From 4c64895e381e7338a6d18d1d1eccdbb66ec0c9e3 Mon Sep 17 00:00:00 2001 From: serverericoza Date: Sat, 5 Jun 2010 22:54:07 +0200 Subject: [PATCH 001/788] Mijn eerste commit, omdat ik het waard ben... --- blaat | 1 - 1 file changed, 1 deletion(-) delete mode 100644 blaat diff --git a/blaat b/blaat deleted file mode 100644 index ed8d5eb7..00000000 --- a/blaat +++ /dev/null @@ -1 +0,0 @@ -Blaat From cdfa38513103f0c4c8b43c3361e41e40028a134d Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Tue, 20 Jul 2010 15:32:17 +0200 Subject: [PATCH 002/788] Merge branch 'master' of projectlivestream.com:pls From 268fd2c37de6c618c8d200d308ff03750f7be785 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 30 Jul 2010 02:45:10 +0200 Subject: [PATCH 003/788] Refactoring naar common bases - also, werkende RTMP streaming! Jammer dat er nog maar 1 frame wordt verwerkt... maar het werkt! --- sockets/SocketW.h | 22 ++ sockets/sw_base.cpp | 764 ++++++++++++++++++++++++++++++++++++++++++ sockets/sw_base.h | 205 ++++++++++++ sockets/sw_inet.cpp | 249 ++++++++++++++ sockets/sw_inet.h | 48 +++ sockets/sw_internal.h | 75 +++++ sockets/sw_unix.cpp | 99 ++++++ sockets/sw_unix.h | 41 +++ util/flv.cpp | 69 ++++ 9 files changed, 1572 insertions(+) create mode 100644 sockets/SocketW.h create mode 100644 sockets/sw_base.cpp create mode 100644 sockets/sw_base.h create mode 100644 sockets/sw_inet.cpp create mode 100644 sockets/sw_inet.h create mode 100644 sockets/sw_internal.h create mode 100644 sockets/sw_unix.cpp create mode 100644 sockets/sw_unix.h create mode 100644 util/flv.cpp diff --git a/sockets/SocketW.h b/sockets/SocketW.h new file mode 100644 index 00000000..b7096c0c --- /dev/null +++ b/sockets/SocketW.h @@ -0,0 +1,22 @@ +// C++ Socket Wrapper +// +// Started 020316 +// +// License: LGPL v2.1+ (see the file LICENSE) +// (c)2002-2003 Anders Lindstr�m + +/*********************************************************************** + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + ***********************************************************************/ + +#ifndef SocketW_H +#define SocketW_H + +#include "sw_base.h" +#include "sw_unix.h" +#include "sw_inet.h" + +#endif //SocketW_H diff --git a/sockets/sw_base.cpp b/sockets/sw_base.cpp new file mode 100644 index 00000000..23ca1eb6 --- /dev/null +++ b/sockets/sw_base.cpp @@ -0,0 +1,764 @@ +// C++ Socket Wrapper +// SocketW base class +// +// Started 020316 +// +// License: LGPL v2.1+ (see the file LICENSE) +// (c)2002-2003 Anders Lindstr�m + +/*********************************************************************** + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + ***********************************************************************/ + +#include "sw_base.h" +#include +#include +#include +#include +#include + +#ifndef __WIN32__ + #include + #include + #include + #include + #include + + #define INVALID_SOCKET -1 //avoid M$ braindamage +#else + //why use POSIX standards when we can make our own + //and frustrate people? (well known M$ policy) + + #ifndef EBADF + #define EBADF WSAEBADF + #endif + + #define ENOTSOCK WSAENOTSOCK + #define EOPNOTSUPP WSAEOPNOTSUPP + #define EADDRINUSE WSAEADDRINUSE + #define EWOULDBLOCK WSAEWOULDBLOCK + #define EMSGSIZE WSAEMSGSIZE + #define EINPROGRESS WSAEINPROGRESS + #define EALREADY WSAEALREADY + #define ECONNREFUSED WSAECONNREFUSED + #define ETIMEDOUT WSAETIMEDOUT + #define ENOTCONN WSAENOTCONN + + #ifndef EINTR + #define EINTR WSAEINTR + #endif +#endif + +#ifndef MSG_NOSIGNAL + #define MSG_NOSIGNAL 0 +#endif + +// Socklen hack +#if defined(__linux__) || defined(__FreeBSD__) // || defined(__bsdi__) || defined(__NetBSD__) too, perhaps? Bugreports, please! + #define sw_socklen_t socklen_t +#elif defined(__WIN32__) || defined(__osf__) + #define sw_socklen_t int +#else + #define sw_socklen_t unsigned int +#endif + + +using namespace std; + +#ifdef __WIN32__ +//Win32 braindamage +int close(int fd) +{ + return closesocket(fd); +} + +int fcntl(int fd, int cmd, long arg) +{ + unsigned long mode = arg; + + return WSAIoctl(fd, cmd, &mode, sizeof(unsigned long), NULL, 0, NULL, NULL, NULL); +} + +void WSA_exit(void) +{ + WSACleanup(); +} +#endif + + +//==================================================================== +//== Error handling mode +//==================================================================== +bool sw_DoThrow = false; +bool sw_Verbose = true; + +void sw_setThrowMode(bool throw_errors) +{ + sw_DoThrow = throw_errors; +} + +void sw_setVerboseMode(bool verbose) +{ + sw_Verbose = verbose; +} + +bool sw_getThrowMode(void) +{ + return sw_DoThrow; +} + +bool sw_getVerboseMode(void) +{ + return sw_Verbose; +} + + +//==================================================================== +//== Base error class +//==================================================================== +SWBaseSocket::SWBaseError::SWBaseError() +{ + be = ok; + error_string = ""; + failed_class = NULL; +} + +SWBaseSocket::SWBaseError::SWBaseError(base_error e) +{ + be = e; + error_string = ""; + failed_class = NULL; +} + +string SWBaseSocket::SWBaseError::get_error() +{ + return error_string; +} + +SWBaseSocket* SWBaseSocket::SWBaseError::get_failedClass(void) +{ + return failed_class; +} + +void SWBaseSocket::SWBaseError::set_errorString(string msg) +{ + error_string = msg; +} + +void SWBaseSocket::SWBaseError::set_failedClass(SWBaseSocket *pnt) +{ + failed_class = pnt; +} + +bool SWBaseSocket::SWBaseError::operator==(SWBaseError e) +{ + return be == e.be; +} + +bool SWBaseSocket::SWBaseError::operator!=(SWBaseError e) +{ + return be != e.be; +} + + +//==================================================================== +//== SWBaseSocket +//== Base class for sockets +//==================================================================== +SWBaseSocket::SWBaseSocket() +{ + //indicate nonopen + myfd = -1; + recv_close = false; + + //init values + error_string = ""; + block_mode = blocking; + fsend_ready = true; + frecv_ready = true; + tsec = 0; + tusec = 0; + + #ifdef __WIN32__ + //kick winsock awake + static bool firstuse = true; + if( firstuse == true ){ + WSAData wsaData; + int nCode; + if( (nCode = WSAStartup(MAKEWORD(1, 1), &wsaData)) != 0 ){ + handle_errno(NULL, "SWBaseSocket - WSAStartup() failed: "); + exit(-1); // Should never happend + } + + //cleanup at exit + atexit(WSA_exit); + firstuse = false; + } + #endif /* __WIN32__ */ +} + +SWBaseSocket::~SWBaseSocket() +{ + if(myfd > 0) + close(myfd); +} + +bool SWBaseSocket::listen(int qLimit, SWBaseError *error) +{ + get_socket(); + + //Avoid "Address already in use" thingie + char yes=1; + setsockopt(myfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); + + if(::listen(myfd, qLimit) == -1){ + handle_errno(error, "SWBaseSocket::listen() error: "); + return false; + } + + no_error(error); + return true; +} + +SWBaseSocket* SWBaseSocket::accept(SWBaseError *error) +{ + int remotefd = -1; + sockaddr remoteAdr; + + if( !waitRead(error) ) + return NULL; + + sw_socklen_t ssize = sizeof(sockaddr); + + if((remotefd = ::accept(myfd, &remoteAdr, &ssize)) == int(INVALID_SOCKET)){ + handle_errno(error, "SWBaseSocket::accept() error: "); + return NULL; + } + + //nonblocking? + if( block_mode == nonblocking ) + fcntl(remotefd, F_SETFL, O_NONBLOCK); + + /* Create new class*/ + SWBaseSocket* remoteClass = create(remotefd, error); + if( remoteClass == NULL ) + return NULL; + + no_error(error); + return remoteClass; +} + +bool SWBaseSocket::disconnect(SWBaseError *error) +{ + int n = 0; + char buf[256]; + + if(myfd < 0){ + set_error(error, notConnected, "SWBaseSocket::disconnect() - No connection"); + return false; + } + + //close WR (this signals the peer) + if( shutdown(myfd, 1) != 0 ){ + handle_errno(error, "SWBaseSocket::disconnect() error: "); + return false; + } + + + SWBaseError err; + + //wait for close signal from peer + if( recv_close == false ){ + while(true){ + if( !waitRead(error) ) + return false; + + n = recv(buf, 256, &err); + + if( n <= 0 ) + break; + if(block_mode == noWait){ + //we don't want to block + set_error(error, notReady, "SWBaseSocket::disconnect() - Need more time, call again"); + return false; + } + } + } + + if( n != 0 ){ + set_error(error, err, error_string); + return false; //error + } + + //reset state + reset(); + + close(myfd); + myfd = -1; + + no_error(error); + return true; +} + +bool SWBaseSocket::close_fd() +{ + if( myfd > 0 ){ + close(myfd); + myfd = -1; + + //reset state + reset(); + + return true; + } + return false; +} + +int SWBaseSocket::send(const char *buf, int bytes, SWBaseError *error) +{ + int ret; + + if(myfd < 0){ + set_error(error, notConnected, "SWBaseSocket::send() - No connection"); + return -1; + } + + if( !waitWrite(error) ) + return -1; + + ret = ::send(myfd, buf, bytes, MSG_NOSIGNAL); + + if( ret < 0 ) + handle_errno(error, "SWBaseSocket::send() error: "); + else + no_error(error); + + return ret; +} + +int SWBaseSocket::fsend(const char *buf, int bytes, SWBaseError *error) +{ + int n; + int bytessent; + + if(fsend_ready){ + //First call + fsend_bytesleft = bytes; + fsend_total = fsend_bytesleft; //global var needed for resume + bytessent = 0; + fsend_ready = false; //point of no return + } + else{ + //resume + bytessent = fsend_total - fsend_bytesleft; + } + + //send package + while( fsend_bytesleft > 0 ){ + n = send( buf + bytessent , fsend_bytesleft, error ); + + //return on error, wouldblock or nowait + if( n < 0 ) + return ( (bytessent > 0 )? -bytessent : -1 ); + + bytessent += n; + fsend_bytesleft -= n; + + if ( block_mode == noWait && fsend_bytesleft > 0 ){ + set_error(error, notReady, "SWBaseSocket::fsend() - Need more time, call again"); + return -bytessent; + } + } + + fsend_ready = true; + + no_error(error); + return fsend_total; +} + +int SWBaseSocket::sendmsg(const string msg, SWBaseError *error) +{ + return send(msg.c_str(), msg.size(), error); +} + +int SWBaseSocket::fsendmsg(const string msg, SWBaseError *error) +{ + return fsend(msg.c_str(), msg.size(), error); +} + +int SWBaseSocket::recv(char *buf, int bytes, SWBaseError *error) +{ + int ret; + + if(myfd < 0){ + set_error(error, notConnected, "SWBaseSocket::recv() - No connection"); + return -1; + } + + if( !waitRead(error) ) + return -1; + + ret = ::recv(myfd, buf, bytes, MSG_NOSIGNAL); + + if( ret < 0 ) + handle_errno(error, "SWBaseSocket::recv() error: "); + else if( ret == 0 ){ + recv_close = true; //we recived a close signal from peer + set_error(error, terminated, "SWBaseSocket::recv() - Connection terminated by peer"); + }else + no_error(error); + + return ret; +} + +int SWBaseSocket::frecv(char *buf, int bytes, SWBaseError *error) +{ + int n; + int bytesrecv; + + if(frecv_ready){ + //First call + frecv_bytesleft = bytes; + frecv_total = frecv_bytesleft; //global var needed for resume + bytesrecv = 0; + frecv_ready = false; //point of no return + } + else{ + //resume + bytesrecv = frecv_total - frecv_bytesleft; + } + + + //recv package + while( frecv_bytesleft > 0 ){ + n = recv( buf + bytesrecv , frecv_bytesleft, error ); + + //return on error, wouldblock, nowait or timeout + if( n < 0 ) + return ( (bytesrecv > 0 )? -bytesrecv : -1 ); + if( n == 0 ) + return 0; // terminated + + bytesrecv += n; + frecv_bytesleft -= n; + + if ( block_mode == noWait && frecv_bytesleft > 0 ){ + set_error(error, notReady, "SWBaseSocket::frecv() - Need more time, call again"); + return -bytesrecv; + } + } + + frecv_ready = true; + + no_error(error); + return frecv_total; +} + +string SWBaseSocket::recvmsg(int bytes, SWBaseError *error) +{ + char *buf = new char[bytes+1]; + + SWBaseError err; + string msg = ""; + int ret = recv(buf, bytes, &err); + + if( ret > 0 ){ + buf[ret]='\0'; // Make sure the string is null terminated + msg = buf; + no_error(error); + } + delete[] buf; + + if( ret < 1 ) + set_error(error, err, err.get_error()); + + return msg; +} + +int SWBaseSocket::get_fd(SWBaseError *error) +{ + if( myfd > 0 ){ + no_error(error); + return myfd; + } + + set_error(error, notConnected, "SWBaseSocket::get_fd() - No descriptor"); + return -1; +} + +bool SWBaseSocket::get_host(sockaddr *host, SWBaseError *error) +{ + if( host == NULL){ + set_error(error, fatal, "SWBaseSocket::get_host() - Got NULL pointer"); + return false; + } + + if(myfd < 0){ + set_error(error, notConnected, "SWBaseSocket::get_host() - No socket"); + return false; + } + + sw_socklen_t tmp = sizeof(sockaddr); + if( getsockname(myfd, host, &tmp) != 0 ){ + handle_errno(error, "SWBaseSocket::get_host() error: "); + return false; + } + + no_error(error); + return true; +} + +bool SWBaseSocket::get_peer(sockaddr *peer, SWBaseError *error) +{ + if( peer == NULL){ + set_error(error, fatal, "SWBaseSocket::get_peer() - Got NULL pointer"); + return false; + } + + if(myfd > 0){ + sw_socklen_t tmp = sizeof(sockaddr); + if( getpeername(myfd, peer, &tmp) != 0 ){ + handle_errno(error, "SWBaseSocket::get_peer() error: "); + return false; + } + }else{ + set_error(error, notConnected, "SWBaseSocket::get_peer() - No connection"); + return false; + } + + no_error(error); + return true; +} + +void SWBaseSocket::reset() +{ + // Reset flags + recv_close = false; + + fsend_ready = true; + frecv_ready = true; +} + +bool SWBaseSocket::waitIO(io_type &type, SWBaseError *error) +{ + if( block_mode != blocking ){ + no_error(error); + return true; + } + + // We prefere to wait with select() even if no timeout is set + // as select() behaves more predictable + + timeval t; + timeval *to = NULL; // Indicate "wait for ever" + t.tv_sec = tsec; + t.tv_usec = tusec; + + if( tsec > 0 || tusec > 0 ) + to = &t; + + fd_set readfds, writefds, exceptfds; + + FD_ZERO(&readfds); + FD_ZERO(&writefds); + FD_ZERO(&exceptfds); + FD_SET(myfd, &readfds); + FD_SET(myfd, &writefds); + FD_SET(myfd, &exceptfds); + + int ret = 0; + + switch (type){ + case read: + ret = select(myfd+1, &readfds, NULL, NULL, to); + break; + case write: + ret = select(myfd+1, NULL, &writefds, NULL, to); + break; + case except: + ret = select(myfd+1, NULL, NULL, &exceptfds, to); + break; + case rw: + ret = select(myfd+1, &readfds, &writefds, NULL, to); + break; + case all: + ret = select(myfd+1, &readfds, &writefds, &exceptfds, to); + break; + } + + if( ret < 0 ){ + handle_errno(error, "SWBaseSocket::waitIO() error: "); + return false; + } + if( ret == 0 ){ + set_error(error, timeout, "SWBaseSocket::waitIO() timeout"); + return false; + } + + if( FD_ISSET(myfd, &readfds) ){ + no_error(error); + type = read; + return true; + } + if( FD_ISSET(myfd, &writefds) ){ + no_error(error); + type = write; + return true; + } + if( FD_ISSET(myfd, &exceptfds) ){ + no_error(error); + type = except; + return true; + } + + set_error(error, fatal, "SWBaseSocket::waitIO() failed on select()"); + return false; +} + +bool SWBaseSocket::waitRead(SWBaseError *error) +{ + io_type tmp = read; + return waitIO(tmp, error); +} + +bool SWBaseSocket::waitWrite(SWBaseError *error) +{ + io_type tmp = write; + return waitIO(tmp, error); +} + +void SWBaseSocket::print_error() +{ + if( error_string.size() > 0 ) + fprintf(stderr, "%s!\n", error_string.c_str()); +} + +void SWBaseSocket::handle_errno(SWBaseError *error, string msg) +{ + #ifndef __WIN32__ + msg += strerror(errno); + #else + //stupid stupid stupid stupid M$ + switch (WSAGetLastError()){ + case 0: msg += "No error"; break; + case WSAEINTR: msg += "Interrupted system call"; break; + case WSAEBADF: msg += "Bad file number"; break; + case WSAEACCES: msg += "Permission denied"; break; + case WSAEFAULT: msg += "Bad address"; break; + case WSAEINVAL: msg += "Invalid argument"; break; + case WSAEMFILE: msg += "Too many open sockets"; break; + case WSAEWOULDBLOCK: msg += "Operation would block"; break; + case WSAEINPROGRESS: msg += "Operation now in progress"; break; + case WSAEALREADY: msg += "Operation already in progress"; break; + case WSAENOTSOCK: msg += "Socket operation on non-socket"; break; + case WSAEDESTADDRREQ: msg += "Destination address required"; break; + case WSAEMSGSIZE: msg += "Message too long"; break; + case WSAEPROTOTYPE: msg += "Protocol wrong type for socket"; break; + case WSAENOPROTOOPT: msg += "Bad protocol option"; break; + case WSAEPROTONOSUPPORT: msg += "Protocol not supported"; break; + case WSAESOCKTNOSUPPORT: msg += "Socket type not supported"; break; + case WSAEOPNOTSUPP: msg += "Operation not supported on socket"; break; + case WSAEPFNOSUPPORT: msg += "Protocol family not supported"; break; + case WSAEAFNOSUPPORT: msg += "Address family not supported"; break; + case WSAEADDRINUSE: msg += "Address already in use"; break; + case WSAEADDRNOTAVAIL: msg += "Can't assign requested address"; break; + case WSAENETDOWN: msg += "Network is down"; break; + case WSAENETUNREACH: msg += "Network is unreachable"; break; + case WSAENETRESET: msg += "Net connection reset"; break; + case WSAECONNABORTED: msg += "Software caused connection abort"; break; + case WSAECONNRESET: msg += "Connection reset by peer"; break; + case WSAENOBUFS: msg += "No buffer space available"; break; + case WSAEISCONN: msg += "Socket is already connected"; break; + case WSAENOTCONN: msg += "Socket is not connected"; break; + case WSAESHUTDOWN: msg += "Can't send after socket shutdown"; break; + case WSAETOOMANYREFS: msg += "Too many references"; break; + case WSAETIMEDOUT: msg += "Connection timed out"; break; + case WSAECONNREFUSED: msg += "Connection refused"; break; + case WSAELOOP: msg += "Too many levels of symbolic links"; break; + case WSAENAMETOOLONG: msg += "File name too long"; break; + case WSAEHOSTDOWN: msg += "Host is down"; break; + case WSAEHOSTUNREACH: msg += "No route to host"; break; + case WSAENOTEMPTY: msg += "Directory not empty"; break; + case WSAEPROCLIM: msg += "Too many processes"; break; + case WSAEUSERS: msg += "Too many users"; break; + case WSAEDQUOT: msg += "Disc quota exceeded"; break; + case WSAESTALE: msg += "Stale NFS file handle"; break; + case WSAEREMOTE: msg += "Too many levels of remote in path"; break; + case WSASYSNOTREADY: msg += "Network system is unavailable"; break; + case WSAVERNOTSUPPORTED: msg += "Winsock version out of range"; break; + case WSANOTINITIALISED: msg += "WSAStartup not yet called"; break; + case WSAEDISCON: msg += "Graceful shutdown in progress"; break; + case WSAHOST_NOT_FOUND: msg += "Host not found"; break; + case WSANO_DATA: msg += "No host data of that type was found"; break; + default: msg += "Unknown Winsock error: " + WSAGetLastError(); break; + } + #endif + + int errorno; + + //Win32 braindamage + #ifdef __WIN32__ + errorno = WSAGetLastError(); + #else + errorno = errno; + #endif + + SWBaseError e; + + if( errorno == EADDRINUSE ) + e = portInUse; + else if( errorno == EAGAIN || errorno == EWOULDBLOCK ) + e = notReady; + else if( errorno == EMSGSIZE ) + e = msgTooLong; + else if( errorno == EINPROGRESS || errorno == EALREADY ) + e = notReady; + else if( errorno == ECONNREFUSED || errorno == ETIMEDOUT ) + e = noResponse; + else if( errorno == ENOTCONN || errorno == EBADF || errorno == ENOTSOCK ) + e = notConnected; + else if( errorno == EPIPE ){ + e = terminated; + recv_close = true; + }else if( errorno == EINTR ) + e = interrupted; + else + e = fatal; //default + + set_error(error, e, msg); +} + +void SWBaseSocket::no_error(SWBaseError *error) +{ + if(error != NULL){ + *error = ok; + error->error_string = ""; + error->failed_class = NULL; + } +} + +void SWBaseSocket::set_error(SWBaseError *error, SWBaseError name, string msg) +{ + error_string = msg; + + if(error != NULL){ + *error = name; + error->error_string = msg; + error->failed_class = this; + }else{ + if( sw_Verbose ) + print_error(); + + if( sw_DoThrow ){ + SWBaseError e; + e = name; + e.error_string = msg; + e.failed_class = this; + throw e; + } + } +} + diff --git a/sockets/sw_base.h b/sockets/sw_base.h new file mode 100644 index 00000000..a6a191f1 --- /dev/null +++ b/sockets/sw_base.h @@ -0,0 +1,205 @@ +// C++ Socket Wrapper +// SocketW base socket header +// +// Started 020316 +// +// License: LGPL v2.1+ (see the file LICENSE) +// (c)2002-2003 Anders Lindström + +/*********************************************************************** + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + ***********************************************************************/ + +#ifndef sw_base_H +#define sw_base_H + +#include "sw_internal.h" + +#include +#include + +// Set error handling mode +// throw_errors == true : Throws the error class on unhandled errors +// throw_errors == false : Exit on unhandled errors +// verbose == true : Prints the error message to stderr on unhandled errors +// +// Default is throw_errors == false and verbose == true +void sw_setThrowMode(bool throw_errors); +void sw_setVerboseMode(bool verbose); +bool sw_getThrowMode(void); +bool sw_getVerboseMode(void); + + +// Abstract base class for streaming sockets +class DECLSPEC SWBaseSocket +{ +public: + SWBaseSocket(); + virtual ~SWBaseSocket(); + + // Error types + // ok - operation succesful + // fatal - unspecified error + // notReady - you should call the function again + // indicates that the function would block (if nowait/nonblocking) + // portInUse - this port is used by another socket ( on listen() ) + // notConnected - socket not connected (or valid) + // msgTooLong - the message size it too big for send() + // terminated - connection terminated (by peer) + // noResponse - can't connect() to peer + // timeout - a read/write operation timed out (only if a timeout value is set and if in blocking mode) + // interrupted - operation was interrupted by a nonblocked signal + enum base_error{ok, fatal, notReady, portInUse, notConnected, msgTooLong, terminated, noResponse, timeout, interrupted}; + + class DECLSPEC SWBaseError + { + public: + SWBaseError(); + SWBaseError(base_error e); + + virtual ~SWBaseError(){;} + + virtual std::string get_error(); + virtual SWBaseSocket* get_failedClass(void); + + virtual bool operator==(SWBaseError e); + virtual bool operator!=(SWBaseError e); + + virtual void set_errorString(std::string msg); + virtual void set_failedClass(SWBaseSocket *pnt); + protected: + friend class SWBaseSocket; + + // The base error type + base_error be; + + // Human readable error string + std::string error_string; + + // A pointer to the class causing the error + SWBaseSocket *failed_class; + }; + + + // Note: If no SWBaseError class is provided with a method call (==NULL), + // SocketW will print the error to stderr and exit or throw on errors. + + // Note: All bool functions returns true on success. + + // Block mode + // blocking - everythings blocks until completly done + // noWait - operations block but only once + // useful with blocking w. select() + // nonblocking - don't block (you should use select()) + enum block_type{blocking, noWait, nonblocking}; + + + // Connection methods + // qLimit - the maximum length the queue of pending connections. + // Accept returns a new socket class connected with peer (should be + // freed with delete) or NULL on failure. You can cast the class to + // the correct type if you need to ( eg. (SWInetSocket *)mysocket ). + virtual bool listen(int qLimit = 5, SWBaseError *error = NULL); + virtual SWBaseSocket* accept(SWBaseError *error = NULL); + // bind() and connect() are implemented in child classes + + // do the disconnect ritual (signal peer, wait for close singal and close socket) + virtual bool disconnect(SWBaseError *error = NULL); + + // force close socket + virtual bool close_fd(); //use with care, disconnect() is cleaner + + // Direct I/O (raw) + // Can send/recv less bytes than specified! + // Returns the actual amount of bytes sent/recv on sucess + // and an negative integer on failure. + virtual int send(const char *buf, int bytes, SWBaseError *error = NULL); + virtual int sendmsg(const std::string msg, SWBaseError *error = NULL); + virtual int recv(char *buf, int bytes, SWBaseError *error = NULL); + virtual std::string recvmsg(int bytes = 256, SWBaseError *error = NULL); + + // Forced I/O + // Force system to send/recv the specified amount of bytes. + // On nowait/nonblocking: might return with notReady and then you + // MUST call the same method again (eg. wait with select() to know when) + // with the same parameters until the operation is finished. + // Returns 'bytes' when finished, negative integer on failure and + // 'notReady'. In the 'notReady' case, -(return value) is the amount of + // bytes sent/recv so far. + virtual int fsend(const char *buf, int bytes, SWBaseError *error = NULL); + virtual int fsendmsg(const std::string msg, SWBaseError *error = NULL); + virtual int frecv(char *buf, int bytes, SWBaseError *error = NULL); + + // Tools + // get_fd() - get socket descriptor, can be used with select() + // returns -1 on failure. + // get_host/peer fills the provided structures with info about the + // host/peer (see man unix & ip). + // SWInetSocket has some more tools for TCP/IP sockets. + virtual int get_fd(SWBaseError *error); + virtual bool get_host(sockaddr *host, SWBaseError *error = NULL); + virtual bool get_peer(sockaddr *peer, SWBaseError *error = NULL); + + // Set recv timeout (only in blocking mode). + // set_timeout(0,0) means wait forever (default). + // This affects the functions recv(), send(), accept() and disconnect() + // and others that use those, i.e. all frecvmsg(). + void set_timeout(Uint32 sec, Uint32 usec){ tsec = sec, tusec = usec; } + + // Error handling + virtual void print_error(); //prints the last error if any to stderr + virtual std::string get_error(){return error_string;} //returns a human readable error string + +protected: + // get a new socket if myfd < 0 + virtual void get_socket()=0; + + // create a new class for accept() using socketdescriptor + virtual SWBaseSocket* create(int socketdescriptor, SWBaseError *error)=0; + + // reset state + virtual void reset(); + + // wait for I/O (with timeout) + enum io_type{read, write, except, rw, all}; + virtual bool waitIO(io_type &type, SWBaseError *error); + bool waitRead(SWBaseError *error); + bool waitWrite(SWBaseError *error); + + // internal error handling + virtual void handle_errno(SWBaseError *error, std::string msg); + virtual void no_error(SWBaseError *error); + virtual void set_error(SWBaseError *error, SWBaseError name, std::string msg); + + // our socket descriptor + int myfd; + + // last error + std::string error_string; + + // data for fsend + bool fsend_ready; + int fsend_total; + int fsend_bytesleft; + + // data for frecv + bool frecv_ready; + int frecv_total; + int frecv_bytesleft; + + // have we recived a shutdown signal? + bool recv_close; + + //blocking mode (set by child classes) + block_type block_mode; + + //timeout for waitIO() + int tsec; + int tusec; +}; + + +#endif /* sw_base_H */ diff --git a/sockets/sw_inet.cpp b/sockets/sw_inet.cpp new file mode 100644 index 00000000..c056722e --- /dev/null +++ b/sockets/sw_inet.cpp @@ -0,0 +1,249 @@ +// C++ Socket Wrapper +// SocketW Inet socket +// +// Started 020316 +// +// License: LGPL v2.1+ (see the file LICENSE) +// (c)2002-2003 Anders Lindström + +/*********************************************************************** + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + ***********************************************************************/ + +#include "sw_inet.h" + +#ifndef __WIN32__ + #include + #include + #include + #include + #include +#else + #define F_SETFL FIONBIO + #define O_NONBLOCK 1 + + //Defined in sw_base.cxx + extern int close(int fd); + extern int fcntl(int fd, int cmd, long arg); +#endif + +using namespace std; + +//==================================================================== +//== SWInetSocket +//== Inet (TCP/IP) streaming sockets +//==================================================================== +SWInetSocket::SWInetSocket(block_type block) +{ + block_mode = block; +} + +SWInetSocket::~SWInetSocket() +{ + +} + +void SWInetSocket::get_socket() +{ + if( myfd < 0 ){ + myfd = socket(PF_INET, SOCK_STREAM, 0); + + if( block_mode == nonblocking ) + fcntl(myfd, F_SETFL, O_NONBLOCK); + + //reset state + reset(); + } +} + +SWBaseSocket* SWInetSocket::create(int socketdescriptor, SWBaseError *error) +{ + SWInetSocket* remoteClass; + + /* Create new class*/ + remoteClass = new SWInetSocket(block_mode); + remoteClass->myfd = socketdescriptor; + + no_error(error); + return remoteClass; +} + +bool SWInetSocket::bind(int port, SWBaseError *error) +{ + return bind(port, "", error); +} + +bool SWInetSocket::bind(int port, string host, SWBaseError *error) +{ + hostent *h; + in_addr inp; + + if( host.size() > 0 ){ + // Bind to a specific address + + if( (h = gethostbyname(host.c_str())) == NULL ){ + set_error(error, fatal, "SWInetSocket::bind() - Can't get host by name"); + return false; + } + + inp = *((in_addr *)h->h_addr); + }else{ + // Bind to any + inp.s_addr = INADDR_ANY; + } + + + get_socket(); + + sockaddr_in myAdr; + + memset(&myAdr, 0, sizeof(myAdr)); + myAdr.sin_family = AF_INET; + myAdr.sin_port = htons(port); + myAdr.sin_addr.s_addr = inp.s_addr; + + if(::bind(myfd, (sockaddr *)&myAdr, sizeof(myAdr)) == -1){ + handle_errno(error, "SWInetSocket::bind() error: "); + return false; + } + + no_error(error); + return true; +} + +bool SWInetSocket::connect(int port, string hostname, SWBaseError *error) +{ + get_socket(); + + hostent *host; + + if( (host = gethostbyname(hostname.c_str())) == NULL ){ + set_error(error, fatal, "SWInetSocket::connect() - Can't get host by name"); + return false; + } + + sockaddr_in remoteAdr; + + memset(&remoteAdr, 0, sizeof(remoteAdr)); + remoteAdr.sin_family = AF_INET; + remoteAdr.sin_port = htons(port); + remoteAdr.sin_addr = *((in_addr *)host->h_addr); + + if(::connect(myfd, (sockaddr *)&remoteAdr, sizeof(remoteAdr)) == -1){ + handle_errno(error, "SWInetSocket::connect() error: "); + return false; + } + + no_error(error); + return true; +} + + +string SWInetSocket::get_peerAddr(SWBaseError *error) +{ + sockaddr_in adr; + + if( !get_peer((sockaddr *)&adr, error) ) + return ""; + + char *pnt; + + if( (pnt = inet_ntoa(adr.sin_addr)) == NULL ){ + set_error(error, fatal, "SWInetSocket::get_peerName() - Can't get peer address"); + return ""; + } + string name(pnt); + + no_error(error); + return name; +} + +int SWInetSocket::get_peerPort(SWBaseError *error) +{ + sockaddr_in adr; + + if( !get_peer((sockaddr *)&adr, error) ) + return -1; + + no_error(error); + + return ntohs(adr.sin_port); +} + +string SWInetSocket::get_peerName(SWBaseError *error) +{ + string name = get_peerAddr(error); + if(name.size() < 1) + return ""; + + + hostent *h; + + if( (h = gethostbyname(name.c_str())) == NULL ){ + set_error(error, fatal, "SWInetSocket::get_peerName() - Can't get peer by address"); + return ""; + } + string host_name(h->h_name); + + no_error(error); + return host_name; +} + +string SWInetSocket::get_hostAddr(SWBaseError *error) +{ + //We need to get the real address, so we must + //first get this computers host name and then + //translate that into an address! + + string name = get_hostName(error); + if( name.size() < 1 ) + return ""; + + hostent *host; + + if( (host = gethostbyname(name.c_str())) == NULL ){ + set_error(error, fatal, "SWInetSocket::get_hostAddr() - Can't get host by name"); + return ""; + } + + char *pnt; + + if( (pnt = inet_ntoa(*((in_addr *)host->h_addr))) == NULL){ + set_error(error, fatal, "SWInetSocket::get_hostAddr() - Can't get host address"); + return ""; + } + + string adr(pnt); + + return adr; +} + +int SWInetSocket::get_hostPort(SWBaseError *error) +{ + sockaddr_in adr; + + if( !get_host((sockaddr *)&adr, error) ) + return -1; + + no_error(error); + + return ntohs(adr.sin_port); +} + +string SWInetSocket::get_hostName(SWBaseError *error) +{ + char buf[256]; + + if( gethostname(buf, 256) != 0 ){ + handle_errno(error, "SWInetSocket::gethostname() error: "); + return ""; + } + + string msg(buf); + + no_error(error); + return msg; +} diff --git a/sockets/sw_inet.h b/sockets/sw_inet.h new file mode 100644 index 00000000..26050845 --- /dev/null +++ b/sockets/sw_inet.h @@ -0,0 +1,48 @@ +// C++ Socket Wrapper +// SocketW Inet socket header +// +// Started 020316 +// +// License: LGPL v2.1+ (see the file LICENSE) +// (c)2002-2003 Anders Lindström + +/*********************************************************************** + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + ***********************************************************************/ + +#ifndef sw_inet_H +#define sw_inet_H + +#include "sw_internal.h" +#include "sw_base.h" +#include + +// Simple streaming TCP/IP class +class DECLSPEC SWInetSocket : public SWBaseSocket +{ +public: + SWInetSocket(block_type block=blocking); + virtual ~SWInetSocket(); + + virtual bool bind(int port, SWBaseError *error = NULL); //use port=0 to get any free port + virtual bool bind(int port, std::string host, SWBaseError *error = NULL); //you can also specify the host interface to use + virtual bool connect(int port, std::string hostname, SWBaseError *error = NULL); + + // Tools + // Gets IP addr, name or port. + virtual std::string get_peerAddr(SWBaseError *error = NULL); + virtual int get_peerPort(SWBaseError *error = NULL); + virtual std::string get_peerName(SWBaseError *error = NULL); + virtual std::string get_hostAddr(SWBaseError *error = NULL); + virtual int get_hostPort(SWBaseError *error = NULL); + virtual std::string get_hostName(SWBaseError *error = NULL); + +protected: + virtual void get_socket(); + virtual SWBaseSocket* create(int socketdescriptor, SWBaseError *error); +}; + +#endif /* sw_inet_H */ diff --git a/sockets/sw_internal.h b/sockets/sw_internal.h new file mode 100644 index 00000000..94313bcd --- /dev/null +++ b/sockets/sw_internal.h @@ -0,0 +1,75 @@ +// C++ Socket Wrapper +// SocketW internal header +// +// Started 030823 +// +// License: LGPL v2.1+ (see the file LICENSE) +// (c)2002-2003 Anders Lindstr�m + +/*********************************************************************** + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + ***********************************************************************/ + +#ifndef sw_internal_H +#define sw_internal_H + +// This header is included in all *.h files + +#ifndef __WIN32__ + #include + #include + #include + #include +#else + #include + + #define F_SETFL FIONBIO + #define O_NONBLOCK 1 +#endif + +#ifndef _SDL_H + +// Define general types +typedef unsigned char Uint8; +typedef signed char Sint8; +typedef unsigned short Uint16; +typedef signed short Sint16; +typedef unsigned int Uint32; +typedef signed int Sint32; + +// It's VERY important that these types really have the right sizes! +// This black magic is from SDL +#define COMPILE_TIME_ASSERT(name, x) \ + typedef int _dummy_ ## name[(x) * 2 - 1] +COMPILE_TIME_ASSERT(uint8, sizeof(Uint8) == 1); +COMPILE_TIME_ASSERT(sint8, sizeof(Sint8) == 1); +COMPILE_TIME_ASSERT(uint16, sizeof(Uint16) == 2); +COMPILE_TIME_ASSERT(sint16, sizeof(Sint16) == 2); +COMPILE_TIME_ASSERT(uint32, sizeof(Uint32) == 4); +COMPILE_TIME_ASSERT(sint32, sizeof(Sint32) == 4); +#undef COMPILE_TIME_ASSERT + +#endif /* _SDL_H */ + +// Some compilers use a special export keyword +#ifndef DECLSPEC + #ifdef __BEOS__ + #if defined(__GNUC__) + #define DECLSPEC __declspec(dllexport) + #else + #define DECLSPEC __declspec(export) + #endif + #else + #ifdef WIN32 + #define DECLSPEC __declspec(dllexport) + #else + #define DECLSPEC + #endif + #endif +#endif + + +#endif /* sw_internal_H */ diff --git a/sockets/sw_unix.cpp b/sockets/sw_unix.cpp new file mode 100644 index 00000000..07e2dd10 --- /dev/null +++ b/sockets/sw_unix.cpp @@ -0,0 +1,99 @@ +// C++ Socket Wrapper +// SocketW Unix socket +// +// Started 020316 +// +// License: LGPL v2.1+ (see the file LICENSE) +// (c)2002-2003 Anders Lindström + +/*********************************************************************** + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + ***********************************************************************/ + +#include "sw_unix.h" +#include + +using namespace std; + +//==================================================================== +//== SWUnixSocket +//== Unix streaming sockets +//==================================================================== +#ifndef __WIN32__ + +SWUnixSocket::SWUnixSocket(block_type block) +{ + block_mode = block; +} + +SWUnixSocket::~SWUnixSocket() +{ + //nothing here +} + +void SWUnixSocket::get_socket() +{ + if( myfd < 0 ){ + myfd = socket(PF_UNIX, SOCK_STREAM, 0); + + if( block_mode == nonblocking ) + fcntl(myfd, F_SETFL, O_NONBLOCK); + + //reset state + reset(); + } +} + + +SWBaseSocket* SWUnixSocket::create(int socketdescriptor, SWBaseError *error) +{ + SWUnixSocket* remoteClass; + + /* Create new class*/ + remoteClass = new SWUnixSocket(block_mode); + remoteClass->myfd = socketdescriptor; + + no_error(error); + return remoteClass; +} + +bool SWUnixSocket::bind(string path, SWBaseError *error) +{ + get_socket(); + + sockaddr_un myAdr; + + myAdr.sun_family = AF_UNIX; + strncpy(myAdr.sun_path, path.c_str(), path.size()+1); + + if(::bind(myfd, (sockaddr *)&myAdr, sizeof(myAdr)) == -1){ + handle_errno(error, "SWUnixSocket::bind() error: "); + return false; + } + + no_error(error); + return true; +} + +bool SWUnixSocket::connect(string path, SWBaseError *error) +{ + get_socket(); + + sockaddr_un remoteAdr; + + remoteAdr.sun_family = AF_UNIX; + strncpy(remoteAdr.sun_path, path.c_str(), path.size()+1); + + if(::connect(myfd, (sockaddr *)&remoteAdr, sizeof(remoteAdr)) == -1){ + handle_errno(error, "SWUnixSocket::connect() error: "); + return false; + } + + no_error(error); + return true; +} + +#endif /* __WIN32__ */ diff --git a/sockets/sw_unix.h b/sockets/sw_unix.h new file mode 100644 index 00000000..576f14c0 --- /dev/null +++ b/sockets/sw_unix.h @@ -0,0 +1,41 @@ +// C++ Socket Wrapper +// SocketW Unix socket header +// +// Started 020316 +// +// License: LGPL v2.1+ (see the file LICENSE) +// (c)2002-2003 Anders Lindström + +/*********************************************************************** + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + ***********************************************************************/ + +#ifndef sw_unix_H +#define sw_unix_H +#ifndef __WIN32__ + +#include "sw_internal.h" +#include "sw_base.h" +#include + +// Simple streaming Unix class +class DECLSPEC SWUnixSocket : public SWBaseSocket +{ +public: + SWUnixSocket(block_type block=blocking); + ~SWUnixSocket(); + + // bind and connect to the socket file "path" + virtual bool bind(std::string path, SWBaseError *error = NULL); + virtual bool connect(std::string path, SWBaseError *error = NULL); + +protected: + virtual void get_socket(); + virtual SWBaseSocket* create(int socketdescriptor, SWBaseError *error); +}; + +#endif /* __WIN32__ */ +#endif /* sw_unix_H */ diff --git a/util/flv.cpp b/util/flv.cpp new file mode 100644 index 00000000..f374c8d5 --- /dev/null +++ b/util/flv.cpp @@ -0,0 +1,69 @@ +#include //for read() + +struct FLV_Pack { + int len; + int buf; + char * data; +};//FLV_Pack + +char FLVHeader[13]; + +//reads full length from a file descriptor +void Magic_Read(char * buf, int len, int file){ + int i = 0; + while (i < len) i += read(file, buf, len-i); +} + + +//reads a FLV header and checks for correctness +//returns true if everything is alright, false otherwise +bool FLV_Readheader(int file){ + Magic_Read(FLVHeader,13,file); + if (FLVHeader[0] != 'F') return false; + if (FLVHeader[1] != 'L') return false; + if (FLVHeader[2] != 'V') return false; + if (FLVHeader[8] != 0x09) return false; + if (FLVHeader[9] != 0) return false; + if (FLVHeader[10] != 0) return false; + if (FLVHeader[11] != 0) return false; + if (FLVHeader[12] != 0) return false; + return true; +}//FLV_Readheader +bool FLV_Readheader(){ + fread(FLVHeader,1,13,stdin); + if (FLVHeader[0] != 'F') return false; + if (FLVHeader[1] != 'L') return false; + if (FLVHeader[2] != 'V') return false; + if (FLVHeader[8] != 0x09) return false; + if (FLVHeader[9] != 0) return false; + if (FLVHeader[10] != 0) return false; + if (FLVHeader[11] != 0) return false; + if (FLVHeader[12] != 0) return false; + return true; +}//FLV_Readheader + +//gets a packet, storing in given FLV_Pack pointer. +//will assign pointer if null +//resizes FLV_Pack data field bigger if data doesn't fit +// (does not auto-shrink for speed!) +void FLV_GetPacket(FLV_Pack *& p, int file){ + if (!p){p = (FLV_Pack*)calloc(1, sizeof(FLV_Pack));} + if (p->buf < 15){p->data = (char*)realloc(p->data, 15); p->buf = 15;} + Magic_Read(p->data,11,file); + p->len = p->data[3] + 15; + p->len += (p->data[2] << 8); + p->len += (p->data[1] << 16); + if (p->buf < p->len){p->data = (char*)realloc(p->data, p->len);} + Magic_Read(p->data+11,p->len-11,file); +}//FLV_GetPacket + +void FLV_GetPacket(FLV_Pack *& p){ + if (!p){p = (FLV_Pack*)calloc(1, sizeof(FLV_Pack));} + if (p->buf < 15){p->data = (char*)realloc(p->data, 15); p->buf = 15;} + fread(p->data,1,11,stdin); + p->len = p->data[3] + 15; + p->len += (p->data[2] << 8); + p->len += (p->data[1] << 16); + if (p->buf < p->len){p->data = (char*)realloc(p->data, p->len);} + fread(p->data+11,1,p->len-11,stdin); +}//FLV_GetPacket From 6f34364989b753644d857de1f1c0aeb6e97474e5 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 30 Jul 2010 21:32:08 +0200 Subject: [PATCH 004/788] Alles werkend - maar echte flash clients zijn het daar niet meer eens, raar genoeg... --- sockets/sw_base.h | 2 +- util/flv.cpp | 25 +------------------------ 2 files changed, 2 insertions(+), 25 deletions(-) diff --git a/sockets/sw_base.h b/sockets/sw_base.h index a6a191f1..2c4e33ef 100644 --- a/sockets/sw_base.h +++ b/sockets/sw_base.h @@ -94,7 +94,7 @@ public: // noWait - operations block but only once // useful with blocking w. select() // nonblocking - don't block (you should use select()) - enum block_type{blocking, noWait, nonblocking}; + enum block_type{nonblocking, noWait, blocking}; // Connection methods diff --git a/util/flv.cpp b/util/flv.cpp index f374c8d5..4dc4235f 100644 --- a/util/flv.cpp +++ b/util/flv.cpp @@ -17,18 +17,6 @@ void Magic_Read(char * buf, int len, int file){ //reads a FLV header and checks for correctness //returns true if everything is alright, false otherwise -bool FLV_Readheader(int file){ - Magic_Read(FLVHeader,13,file); - if (FLVHeader[0] != 'F') return false; - if (FLVHeader[1] != 'L') return false; - if (FLVHeader[2] != 'V') return false; - if (FLVHeader[8] != 0x09) return false; - if (FLVHeader[9] != 0) return false; - if (FLVHeader[10] != 0) return false; - if (FLVHeader[11] != 0) return false; - if (FLVHeader[12] != 0) return false; - return true; -}//FLV_Readheader bool FLV_Readheader(){ fread(FLVHeader,1,13,stdin); if (FLVHeader[0] != 'F') return false; @@ -46,17 +34,6 @@ bool FLV_Readheader(){ //will assign pointer if null //resizes FLV_Pack data field bigger if data doesn't fit // (does not auto-shrink for speed!) -void FLV_GetPacket(FLV_Pack *& p, int file){ - if (!p){p = (FLV_Pack*)calloc(1, sizeof(FLV_Pack));} - if (p->buf < 15){p->data = (char*)realloc(p->data, 15); p->buf = 15;} - Magic_Read(p->data,11,file); - p->len = p->data[3] + 15; - p->len += (p->data[2] << 8); - p->len += (p->data[1] << 16); - if (p->buf < p->len){p->data = (char*)realloc(p->data, p->len);} - Magic_Read(p->data+11,p->len-11,file); -}//FLV_GetPacket - void FLV_GetPacket(FLV_Pack *& p){ if (!p){p = (FLV_Pack*)calloc(1, sizeof(FLV_Pack));} if (p->buf < 15){p->data = (char*)realloc(p->data, 15); p->buf = 15;} @@ -64,6 +41,6 @@ void FLV_GetPacket(FLV_Pack *& p){ p->len = p->data[3] + 15; p->len += (p->data[2] << 8); p->len += (p->data[1] << 16); - if (p->buf < p->len){p->data = (char*)realloc(p->data, p->len);} + if (p->buf < p->len){p->data = (char*)realloc(p->data, p->len);p->buf = p->len;} fread(p->data+11,1,p->len-11,stdin); }//FLV_GetPacket From 6e41963e6abca756530bf03c8ff448aeda55e275 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 24 Aug 2010 00:13:52 +0200 Subject: [PATCH 005/788] Stabiliteitsfixes en in theorie werkende H.264 en AAC, maar je weet maar nooit... --- util/flv.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/util/flv.cpp b/util/flv.cpp index 4dc4235f..0e119a52 100644 --- a/util/flv.cpp +++ b/util/flv.cpp @@ -3,6 +3,7 @@ struct FLV_Pack { int len; int buf; + bool isKeyframe; char * data; };//FLV_Pack @@ -43,4 +44,6 @@ void FLV_GetPacket(FLV_Pack *& p){ p->len += (p->data[1] << 16); if (p->buf < p->len){p->data = (char*)realloc(p->data, p->len);p->buf = p->len;} fread(p->data+11,1,p->len-11,stdin); + p->isKeyframe = false; + if ((p->data[0] == 0x09) && (((p->data[11] & 0xf0) >> 4) == 1)){p->isKeyframe = true;} }//FLV_GetPacket From 9a7420e5b2ca04123643d090637cd31d02decc6f Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 30 Aug 2010 14:40:07 +0200 Subject: [PATCH 006/788] Begin RTSP connector gemaakt --- util/flv_sock.cpp | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 util/flv_sock.cpp diff --git a/util/flv_sock.cpp b/util/flv_sock.cpp new file mode 100644 index 00000000..8d6c55a4 --- /dev/null +++ b/util/flv_sock.cpp @@ -0,0 +1,30 @@ +SWBaseSocket::SWBaseError SWBerr; +char * FLVbuffer; +int FLV_len; +int FLVbs = 0; + +void FLV_Readheader(SWUnixSocket & ss){ + static char header[13]; + while (ss.frecv(header, 13, &SWBerr) != 13){ + //wait + } +}//FLV_Readheader + +void FLV_Dump(){FLV_len = 0;} + +bool FLV_GetPacket(SWUnixSocket & ss){ + if (FLVbs < 15){FLVbuffer = (char*)realloc(FLVbuffer, 15); FLVbs = 15;} + //if received a whole header, receive a whole packet + //if not, retry header next pass + if (FLV_len == 0){ + if (ss.frecv(FLVbuffer, 11, &SWBerr) == 11){ + FLV_len = FLVbuffer[3] + 15; + FLV_len += (FLVbuffer[2] << 8); + FLV_len += (FLVbuffer[1] << 16); + if (FLVbs < FLV_len){FLVbuffer = (char*)realloc(FLVbuffer, FLV_len);FLVbs = FLV_len;} + } + }else{ + if (ss.frecv(FLVbuffer+11, FLV_len-11, &SWBerr) == FLV_len-11){return true;} + } + return false; +}//FLV_GetPacket From 3fb3d79ba04d99771200deaf7aa3380e16cf4ecc Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sat, 6 Nov 2010 16:38:19 +0100 Subject: [PATCH 007/788] Buffer conn timeout fix --- util/flv.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/util/flv.cpp b/util/flv.cpp index 0e119a52..0c922506 100644 --- a/util/flv.cpp +++ b/util/flv.cpp @@ -35,15 +35,16 @@ bool FLV_Readheader(){ //will assign pointer if null //resizes FLV_Pack data field bigger if data doesn't fit // (does not auto-shrink for speed!) -void FLV_GetPacket(FLV_Pack *& p){ +bool FLV_GetPacket(FLV_Pack *& p){ if (!p){p = (FLV_Pack*)calloc(1, sizeof(FLV_Pack));} if (p->buf < 15){p->data = (char*)realloc(p->data, 15); p->buf = 15;} - fread(p->data,1,11,stdin); + if (fread(p->data,1,11,stdin) != 11){return false;} p->len = p->data[3] + 15; p->len += (p->data[2] << 8); p->len += (p->data[1] << 16); if (p->buf < p->len){p->data = (char*)realloc(p->data, p->len);p->buf = p->len;} - fread(p->data+11,1,p->len-11,stdin); + if (fread(p->data+11,1,p->len-11,stdin) != (unsigned int)(p->len-11)){return false;} p->isKeyframe = false; if ((p->data[0] == 0x09) && (((p->data[11] & 0xf0) >> 4) == 1)){p->isKeyframe = true;} + return true; }//FLV_GetPacket From 731a212bb05c3f77a232d004c3b3e1d016c21150 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 7 Nov 2010 14:49:45 +0100 Subject: [PATCH 008/788] Gearbox updates, FLV parser rewrite, buffer fixes... --- util/flv.cpp | 93 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 65 insertions(+), 28 deletions(-) diff --git a/util/flv.cpp b/util/flv.cpp index 0c922506..e7048dcb 100644 --- a/util/flv.cpp +++ b/util/flv.cpp @@ -8,43 +8,80 @@ struct FLV_Pack { };//FLV_Pack char FLVHeader[13]; +bool All_Hell_Broke_Loose = false; -//reads full length from a file descriptor -void Magic_Read(char * buf, int len, int file){ - int i = 0; - while (i < len) i += read(file, buf, len-i); -} - - -//reads a FLV header and checks for correctness +//checks FLV Header for correctness //returns true if everything is alright, false otherwise -bool FLV_Readheader(){ - fread(FLVHeader,1,13,stdin); - if (FLVHeader[0] != 'F') return false; - if (FLVHeader[1] != 'L') return false; - if (FLVHeader[2] != 'V') return false; - if (FLVHeader[8] != 0x09) return false; - if (FLVHeader[9] != 0) return false; - if (FLVHeader[10] != 0) return false; - if (FLVHeader[11] != 0) return false; - if (FLVHeader[12] != 0) return false; +bool FLV_Checkheader(char * header){ + if (header[0] != 'F') return false; + if (header[1] != 'L') return false; + if (header[2] != 'V') return false; + if (header[8] != 0x09) return false; + if (header[9] != 0) return false; + if (header[10] != 0) return false; + if (header[11] != 0) return false; + if (header[12] != 0) return false; return true; -}//FLV_Readheader +}//FLV_Checkheader + +//returns true if header is an FLV header +bool FLV_Isheader(char * header){ + if (header[0] != 'F') return false; + if (header[1] != 'L') return false; + if (header[2] != 'V') return false; + return true; +}//FLV_Isheader + +bool ReadUntil(char * buffer, unsigned int count, unsigned int & sofar){ + if (sofar >= count){return true;} + int r = 0; + r = fread(buffer + sofar,1,count-sofar,stdin); + if (r < 0){All_Hell_Broke_Loose = true; return false;} + sofar += r; + if (sofar >= count){return true;} + return false; +} //gets a packet, storing in given FLV_Pack pointer. //will assign pointer if null //resizes FLV_Pack data field bigger if data doesn't fit // (does not auto-shrink for speed!) bool FLV_GetPacket(FLV_Pack *& p){ + static bool done = true; + static unsigned int sofar = 0; if (!p){p = (FLV_Pack*)calloc(1, sizeof(FLV_Pack));} if (p->buf < 15){p->data = (char*)realloc(p->data, 15); p->buf = 15;} - if (fread(p->data,1,11,stdin) != 11){return false;} - p->len = p->data[3] + 15; - p->len += (p->data[2] << 8); - p->len += (p->data[1] << 16); - if (p->buf < p->len){p->data = (char*)realloc(p->data, p->len);p->buf = p->len;} - if (fread(p->data+11,1,p->len-11,stdin) != (unsigned int)(p->len-11)){return false;} - p->isKeyframe = false; - if ((p->data[0] == 0x09) && (((p->data[11] & 0xf0) >> 4) == 1)){p->isKeyframe = true;} - return true; + + if (done){ + //read a header + if (ReadUntil(p->data, 11, sofar)){ + //if its a correct FLV header, throw away and read tag header + if (FLV_Isheader(p->data)){ + if (ReadUntil(p->data, 13, sofar)){ + if (FLV_Checkheader(p->data)){ + sofar = 0; + memcpy(FLVHeader, p->data, 13); + }else{All_Hell_Broke_Loose = true;} + } + }else{ + //if a tag header, calculate length and read tag body + p->len = p->data[3] + 15; + p->len += (p->data[2] << 8); + p->len += (p->data[1] << 16); + if (p->buf < p->len){p->data = (char*)realloc(p->data, p->len);p->buf = p->len;} + done = false; + } + } + }else{ + //read tag body + if (ReadUntil(p->data, p->len, sofar)){ + //calculate keyframeness, next time read header again, return true + p->isKeyframe = false; + if ((p->data[0] == 0x09) && (((p->data[11] & 0xf0) >> 4) == 1)){p->isKeyframe = true;} + done = true; + return true; + } + } + return false; }//FLV_GetPacket + From 06e63e438c24835d37e683498d37975f47442079 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 7 Nov 2010 15:03:46 +0100 Subject: [PATCH 009/788] Oeps --- util/flv.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/util/flv.cpp b/util/flv.cpp index e7048dcb..44185ffd 100644 --- a/util/flv.cpp +++ b/util/flv.cpp @@ -79,6 +79,7 @@ bool FLV_GetPacket(FLV_Pack *& p){ p->isKeyframe = false; if ((p->data[0] == 0x09) && (((p->data[11] & 0xf0) >> 4) == 1)){p->isKeyframe = true;} done = true; + sofar = 0; return true; } } From 5af9fd2674e4f4f601ddf493d0bdeeb5f4b22592 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 7 Nov 2010 22:48:05 +0100 Subject: [PATCH 010/788] First version of standalone RTMP connector --- util/ddv_socket.cpp | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 util/ddv_socket.cpp diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp new file mode 100644 index 00000000..82bf1b7e --- /dev/null +++ b/util/ddv_socket.cpp @@ -0,0 +1,41 @@ +#include +#include +#include +#include +#include +#include +#include + + +int DDV_Listen(int port){ + int s = socket(AF_INET, SOCK_STREAM, 0); + + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(port);//port 8888 + inet_pton(AF_INET, "0.0.0.0", &addr.sin_addr);//listen on all interfaces + ret = bind(sock, (sockaddr*)&addr, sizeof(addr));//bind to all interfaces, chosen port + if (ret == 0){ + ret = listen(sock, 100);//start listening, backlog of 100 allowed + if (ret == 0){ + return s; + }else{ + printf("Listen failed! Error: %s\n", strerror(errno)); + close(s); + return 0; + } + }else{ + printf("Binding failed! Error: %s\n", strerror(errno)); + close(s); + return 0; + } +} + +int DDV_Accept(int sock){ + int r = accept(sock, 0, 0); + if (r != -1){ + return fdopen(r, "r+"); + }else{ + return -1; + } +} From b210630fcc63846dda1b6908a529745089e34858 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 7 Nov 2010 22:52:00 +0100 Subject: [PATCH 011/788] Compilende versie RTMP superconnector --- util/ddv_socket.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index 82bf1b7e..17f06366 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -14,9 +14,9 @@ int DDV_Listen(int port){ addr.sin_family = AF_INET; addr.sin_port = htons(port);//port 8888 inet_pton(AF_INET, "0.0.0.0", &addr.sin_addr);//listen on all interfaces - ret = bind(sock, (sockaddr*)&addr, sizeof(addr));//bind to all interfaces, chosen port + int ret = bind(s, (sockaddr*)&addr, sizeof(addr));//bind to all interfaces, chosen port if (ret == 0){ - ret = listen(sock, 100);//start listening, backlog of 100 allowed + ret = listen(s, 100);//start listening, backlog of 100 allowed if (ret == 0){ return s; }else{ @@ -31,11 +31,11 @@ int DDV_Listen(int port){ } } -int DDV_Accept(int sock){ +FILE * DDV_Accept(int sock){ int r = accept(sock, 0, 0); if (r != -1){ return fdopen(r, "r+"); }else{ - return -1; + return (FILE*)0; } } From 0bfafcda2b675b57298bcc6aaa5b3e96b92408db Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 7 Nov 2010 23:50:20 +0100 Subject: [PATCH 012/788] Nog een poging... --- util/ddv_socket.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index 17f06366..beb23d41 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -31,11 +31,6 @@ int DDV_Listen(int port){ } } -FILE * DDV_Accept(int sock){ - int r = accept(sock, 0, 0); - if (r != -1){ - return fdopen(r, "r+"); - }else{ - return (FILE*)0; - } +int DDV_Accept(int sock){ + return accept(sock, 0, 0); } From 2ad46257e73629b8d8726b63aafab362647c38df Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 00:56:49 +0100 Subject: [PATCH 013/788] Nog een poging... --- util/ddv_socket.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index beb23d41..bc6fe670 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -34,3 +34,11 @@ int DDV_Listen(int port){ int DDV_Accept(int sock){ return accept(sock, 0, 0); } + +bool DDV_write(char * buffer, int width, int count, int sock){ + return (send(sock, buffer, width*count, 0) == width*count); +} + +bool DDV_read(char * buffer, int width, int count, int sock){ + return (recv(sock, buffer, width*count, 0) == width*count); +} From c980c4fd45f6d716d4cba0673a9658da344c1291 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 01:00:22 +0100 Subject: [PATCH 014/788] Nog een poging... --- util/ddv_socket.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index bc6fe670..595ab2bb 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -6,6 +6,7 @@ #include #include +bool socketError = false; int DDV_Listen(int port){ int s = socket(AF_INET, SOCK_STREAM, 0); @@ -36,9 +37,13 @@ int DDV_Accept(int sock){ } bool DDV_write(char * buffer, int width, int count, int sock){ - return (send(sock, buffer, width*count, 0) == width*count); + bool r = (send(sock, buffer, width*count, 0) == width*count); + if (!r){socketError = true} + return r; } bool DDV_read(char * buffer, int width, int count, int sock){ - return (recv(sock, buffer, width*count, 0) == width*count); + bool r = (recv(sock, buffer, width*count, 0) == width*count); + if (!r){socketError = true} + return r; } From cd4e2f696caa50b1a7e61482fee6aa3768776056 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 01:00:52 +0100 Subject: [PATCH 015/788] Nog een poging... --- util/ddv_socket.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index 595ab2bb..72201cf0 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -36,13 +36,13 @@ int DDV_Accept(int sock){ return accept(sock, 0, 0); } -bool DDV_write(char * buffer, int width, int count, int sock){ +bool DDV_write(void * buffer, int width, int count, int sock){ bool r = (send(sock, buffer, width*count, 0) == width*count); if (!r){socketError = true} return r; } -bool DDV_read(char * buffer, int width, int count, int sock){ +bool DDV_read(void * buffer, int width, int count, int sock){ bool r = (recv(sock, buffer, width*count, 0) == width*count); if (!r){socketError = true} return r; From b3214427a1d1ee6df263d8e785ba6ca0a60cce44 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 01:01:59 +0100 Subject: [PATCH 016/788] Nog een poging... --- util/ddv_socket.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index 72201cf0..6ab42140 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -38,12 +38,12 @@ int DDV_Accept(int sock){ bool DDV_write(void * buffer, int width, int count, int sock){ bool r = (send(sock, buffer, width*count, 0) == width*count); - if (!r){socketError = true} + if (!r){socketError = true;} return r; } bool DDV_read(void * buffer, int width, int count, int sock){ bool r = (recv(sock, buffer, width*count, 0) == width*count); - if (!r){socketError = true} + if (!r){socketError = true;} return r; } From ed8008f956d8554e92f6e61578feca786e3fac4f Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 01:04:11 +0100 Subject: [PATCH 017/788] Nog een poging... --- util/ddv_socket.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index 6ab42140..1217293a 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -38,12 +38,18 @@ int DDV_Accept(int sock){ bool DDV_write(void * buffer, int width, int count, int sock){ bool r = (send(sock, buffer, width*count, 0) == width*count); - if (!r){socketError = true;} + if (!r){ + socketError = true; + printf("Could not write! %s\n", strerror(errno)); + } return r; } bool DDV_read(void * buffer, int width, int count, int sock){ bool r = (recv(sock, buffer, width*count, 0) == width*count); - if (!r){socketError = true;} + if (!r){ + socketError = true; + printf("Could not read! %s\n", strerror(errno)); + } return r; } From 28373a9f6ec671d04bd42f3ae4e635155e734c7d Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 01:14:23 +0100 Subject: [PATCH 018/788] Nog een poging... --- util/ddv_socket.cpp | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index 1217293a..56e298de 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -37,19 +37,31 @@ int DDV_Accept(int sock){ } bool DDV_write(void * buffer, int width, int count, int sock){ - bool r = (send(sock, buffer, width*count, 0) == width*count); - if (!r){ - socketError = true; - printf("Could not write! %s\n", strerror(errno)); + int sofar = 0; + int todo = width*count; + while (sofar != todo){ + int r = send(sock, (char*)buffer + sofar, todo-sofar, 0); + if (r < 0){ + socketError = true; + printf("Could not write! %s\n", strerror(errno)); + return false; + } + sofar += r; } - return r; + return true; } bool DDV_read(void * buffer, int width, int count, int sock){ - bool r = (recv(sock, buffer, width*count, 0) == width*count); - if (!r){ - socketError = true; - printf("Could not read! %s\n", strerror(errno)); + int sofar = 0; + int todo = width*count; + while (sofar != todo){ + int r = recv(sock, (char*)buffer + sofar, todo-sofar, 0); + if (r < 0){ + socketError = true; + printf("Could not read! %s\n", strerror(errno)); + return false; + } + sofar += r; } - return r; + return true; } From cd0ed3be751bce01f909ec04ec46ad224981262f Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 02:45:46 +0100 Subject: [PATCH 019/788] Fix voor afsluiten verbinding... --- util/ddv_socket.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index 56e298de..4e1341d2 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -41,7 +41,7 @@ bool DDV_write(void * buffer, int width, int count, int sock){ int todo = width*count; while (sofar != todo){ int r = send(sock, (char*)buffer + sofar, todo-sofar, 0); - if (r < 0){ + if (r <= 0){ socketError = true; printf("Could not write! %s\n", strerror(errno)); return false; @@ -56,7 +56,7 @@ bool DDV_read(void * buffer, int width, int count, int sock){ int todo = width*count; while (sofar != todo){ int r = recv(sock, (char*)buffer + sofar, todo-sofar, 0); - if (r < 0){ + if (r <= 0){ socketError = true; printf("Could not read! %s\n", strerror(errno)); return false; From 84c7c9c275ddb895348d72a85fc1ae5c53fde80c Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 03:12:24 +0100 Subject: [PATCH 020/788] Buffer crash fix --- util/flv.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/util/flv.cpp b/util/flv.cpp index 44185ffd..c04d977a 100644 --- a/util/flv.cpp +++ b/util/flv.cpp @@ -1,4 +1,5 @@ #include //for read() +#include struct FLV_Pack { int len; @@ -47,6 +48,9 @@ bool ReadUntil(char * buffer, unsigned int count, unsigned int & sofar){ //resizes FLV_Pack data field bigger if data doesn't fit // (does not auto-shrink for speed!) bool FLV_GetPacket(FLV_Pack *& p){ + int preflags = fcntl(fileno(stdin), F_GETFL, 0); + int postflags = preflags | O_NONBLOCK; + fcntl(fileno(stdin), F_SETFL, postflags); static bool done = true; static unsigned int sofar = 0; if (!p){p = (FLV_Pack*)calloc(1, sizeof(FLV_Pack));} @@ -80,9 +84,11 @@ bool FLV_GetPacket(FLV_Pack *& p){ if ((p->data[0] == 0x09) && (((p->data[11] & 0xf0) >> 4) == 1)){p->isKeyframe = true;} done = true; sofar = 0; + fcntl(fileno(stdin), F_SETFL, preflags); return true; } } + fcntl(fileno(stdin), F_SETFL, preflags); return false; }//FLV_GetPacket From 5f3d64ba0b215a7d8c2e5ec8034a2f73d2f8e897 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 03:19:17 +0100 Subject: [PATCH 021/788] FLV Socket lezer fix --- util/flv_sock.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/util/flv_sock.cpp b/util/flv_sock.cpp index 8d6c55a4..60bc3abe 100644 --- a/util/flv_sock.cpp +++ b/util/flv_sock.cpp @@ -2,17 +2,21 @@ SWBaseSocket::SWBaseError SWBerr; char * FLVbuffer; int FLV_len; int FLVbs = 0; +bool HeaderDone = false; +static char FLVheader[13]; void FLV_Readheader(SWUnixSocket & ss){ - static char header[13]; - while (ss.frecv(header, 13, &SWBerr) != 13){ - //wait - } }//FLV_Readheader void FLV_Dump(){FLV_len = 0;} bool FLV_GetPacket(SWUnixSocket & ss){ + if (!HeaderDone){ + if (ss.frecv(FLVheader, 13, &SWBerr) == 13){HeaderDone = true;} + return false; + } + + if (FLVbs < 15){FLVbuffer = (char*)realloc(FLVbuffer, 15); FLVbs = 15;} //if received a whole header, receive a whole packet //if not, retry header next pass From 6949269ccc03a794d6c0c93acfda137b333943b7 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 15:28:56 +0100 Subject: [PATCH 022/788] Nieuwe flv parser, nieuwe unix sockets support... --- util/ddv_socket.cpp | 52 +++++++++++++++++++-- util/flv_sock.cpp | 108 ++++++++++++++++++++++++++++++++++---------- 2 files changed, 131 insertions(+), 29 deletions(-) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index 4e1341d2..caece508 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -1,13 +1,34 @@ #include #include +#include #include #include #include #include #include +#include bool socketError = false; +int DDV_OpenUnix(const char adres[], bool nonblock = false){ + int s = socket(AF_UNIX, SOCK_STREAM, 0); + sockaddr_un addr; + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, adres); + int r = connect(s, (sockaddr*)&adres, sizeof(addr)); + if (r == 0){ + if (nonblock){ + int flags = fcntl(s, F_GETFL, 0); + flags |= O_NONBLOCK; + fcntl(s, F_SETFL, flags); + } + return s; + }else{ + close(s); + return 0; + } +} + int DDV_Listen(int port){ int s = socket(AF_INET, SOCK_STREAM, 0); @@ -36,9 +57,8 @@ int DDV_Accept(int sock){ return accept(sock, 0, 0); } -bool DDV_write(void * buffer, int width, int count, int sock){ +bool DDV_write(void * buffer, int todo, int sock){ int sofar = 0; - int todo = width*count; while (sofar != todo){ int r = send(sock, (char*)buffer + sofar, todo-sofar, 0); if (r <= 0){ @@ -51,9 +71,8 @@ bool DDV_write(void * buffer, int width, int count, int sock){ return true; } -bool DDV_read(void * buffer, int width, int count, int sock){ +bool DDV_read(void * buffer, int todo, int sock){ int sofar = 0; - int todo = width*count; while (sofar != todo){ int r = recv(sock, (char*)buffer + sofar, todo-sofar, 0); if (r <= 0){ @@ -65,3 +84,28 @@ bool DDV_read(void * buffer, int width, int count, int sock){ } return true; } + + +bool DDV_read(void * buffer, int width, int count, int sock){return DDV_read(buffer, width*count, sock);} +bool DDV_write(void * buffer, int width, int count, int sock){return DDV_write(buffer, width*count, sock);} + + +int DDV_iwrite(void * buffer, int todo, int sock){ + int r = send(sock, buffer, todo, 0); + if (r < 0){ + socketError = true; + printf("Could not write! %s\n", strerror(errno)); + } + return r; +} + +int DDV_iread(void * buffer, int todo, int sock){ + int r = recv(sock, buffer, todo, 0); + if (r < 0){ + socketError = true; + printf("Could not write! %s\n", strerror(errno)); + } + return r; +} + + diff --git a/util/flv_sock.cpp b/util/flv_sock.cpp index 60bc3abe..ac520b43 100644 --- a/util/flv_sock.cpp +++ b/util/flv_sock.cpp @@ -1,34 +1,92 @@ -SWBaseSocket::SWBaseError SWBerr; -char * FLVbuffer; -int FLV_len; -int FLVbs = 0; -bool HeaderDone = false; -static char FLVheader[13]; -void FLV_Readheader(SWUnixSocket & ss){ -}//FLV_Readheader +struct FLV_Pack { + int len; + int buf; + bool isKeyframe; + char * data; +};//FLV_Pack -void FLV_Dump(){FLV_len = 0;} +char FLVHeader[13]; +bool All_Hell_Broke_Loose = false; -bool FLV_GetPacket(SWUnixSocket & ss){ - if (!HeaderDone){ - if (ss.frecv(FLVheader, 13, &SWBerr) == 13){HeaderDone = true;} - return false; - } +//checks FLV Header for correctness +//returns true if everything is alright, false otherwise +bool FLV_Checkheader(char * header){ + if (header[0] != 'F') return false; + if (header[1] != 'L') return false; + if (header[2] != 'V') return false; + if (header[8] != 0x09) return false; + if (header[9] != 0) return false; + if (header[10] != 0) return false; + if (header[11] != 0) return false; + if (header[12] != 0) return false; + return true; +}//FLV_Checkheader - - if (FLVbs < 15){FLVbuffer = (char*)realloc(FLVbuffer, 15); FLVbs = 15;} - //if received a whole header, receive a whole packet - //if not, retry header next pass - if (FLV_len == 0){ - if (ss.frecv(FLVbuffer, 11, &SWBerr) == 11){ - FLV_len = FLVbuffer[3] + 15; - FLV_len += (FLVbuffer[2] << 8); - FLV_len += (FLVbuffer[1] << 16); - if (FLVbs < FLV_len){FLVbuffer = (char*)realloc(FLVbuffer, FLV_len);FLVbs = FLV_len;} +//returns true if header is an FLV header +bool FLV_Isheader(char * header){ + if (header[0] != 'F') return false; + if (header[1] != 'L') return false; + if (header[2] != 'V') return false; + return true; +}//FLV_Isheader + +bool ReadUntil(char * buffer, unsigned int count, unsigned int & sofar, int sock){ + if (sofar >= count){return true;} + int r = 0; + r = DDV_iread(buffer + sofar,count-sofar,sock); + if (r < 0){All_Hell_Broke_Loose = true; return false;} + sofar += r; + if (sofar >= count){return true;} + return false; +} + +//gets a packet, storing in given FLV_Pack pointer. +//will assign pointer if null +//resizes FLV_Pack data field bigger if data doesn't fit +// (does not auto-shrink for speed!) +bool FLV_GetPacket(FLV_Pack *& p, int sock){ + int preflags = fcntl(sock, F_GETFL, 0); + int postflags = preflags | O_NONBLOCK; + fcntl(sock, F_SETFL, postflags); + static bool done = true; + static unsigned int sofar = 0; + if (!p){p = (FLV_Pack*)calloc(1, sizeof(FLV_Pack));} + if (p->buf < 15){p->data = (char*)realloc(p->data, 15); p->buf = 15;} + + if (done){ + //read a header + if (ReadUntil(p->data, 11, sofar, sock)){ + //if its a correct FLV header, throw away and read tag header + if (FLV_Isheader(p->data)){ + if (ReadUntil(p->data, 13, sofar, sock)){ + if (FLV_Checkheader(p->data)){ + sofar = 0; + memcpy(FLVHeader, p->data, 13); + }else{All_Hell_Broke_Loose = true;} + } + }else{ + //if a tag header, calculate length and read tag body + p->len = p->data[3] + 15; + p->len += (p->data[2] << 8); + p->len += (p->data[1] << 16); + if (p->buf < p->len){p->data = (char*)realloc(p->data, p->len);p->buf = p->len;} + done = false; + } } }else{ - if (ss.frecv(FLVbuffer+11, FLV_len-11, &SWBerr) == FLV_len-11){return true;} + //read tag body + if (ReadUntil(p->data, p->len, sofar, sock)){ + //calculate keyframeness, next time read header again, return true + p->isKeyframe = false; + if ((p->data[0] == 0x09) && (((p->data[11] & 0xf0) >> 4) == 1)){p->isKeyframe = true;} + done = true; + sofar = 0; + fcntl(sock, F_SETFL, preflags); + return true; + } } + fcntl(sock, F_SETFL, preflags); return false; }//FLV_GetPacket + From a0f1d494ac1c36cafcc97a6232def981e4ddda5c Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 15:32:13 +0100 Subject: [PATCH 023/788] Allow socket reuse --- util/ddv_socket.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index caece508..49f34806 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -12,6 +12,8 @@ bool socketError = false; int DDV_OpenUnix(const char adres[], bool nonblock = false){ int s = socket(AF_UNIX, SOCK_STREAM, 0); + int on = 1; + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); sockaddr_un addr; addr.sun_family = AF_UNIX; strcpy(addr.sun_path, adres); From da133096702d874f1063d5438ee139ac0f8018ad Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 15:43:01 +0100 Subject: [PATCH 024/788] Connect fux --- util/ddv_socket.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index 49f34806..7f7000aa 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -17,7 +17,7 @@ int DDV_OpenUnix(const char adres[], bool nonblock = false){ sockaddr_un addr; addr.sun_family = AF_UNIX; strcpy(addr.sun_path, adres); - int r = connect(s, (sockaddr*)&adres, sizeof(addr)); + int r = connect(s, (sockaddr*)&addr, sizeof(addr)); if (r == 0){ if (nonblock){ int flags = fcntl(s, F_GETFL, 0); From 3a856002a6c41c18e8f79475ca95c01d9f9b7872 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 15:51:06 +0100 Subject: [PATCH 025/788] DDVSocket edits --- util/ddv_socket.cpp | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index 7f7000aa..1aa0cad9 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -64,9 +64,15 @@ bool DDV_write(void * buffer, int todo, int sock){ while (sofar != todo){ int r = send(sock, (char*)buffer + sofar, todo-sofar, 0); if (r <= 0){ - socketError = true; - printf("Could not write! %s\n", strerror(errno)); - return false; + switch (errno){ + case EWOULDBLOCK: printf("Would block\n"); break; + case EAGAIN: printf("Again\n"); break; + default: + socketError = true; + printf("Could not write! %s\n", strerror(errno)); + return false; + break; + } } sofar += r; } @@ -78,9 +84,15 @@ bool DDV_read(void * buffer, int todo, int sock){ while (sofar != todo){ int r = recv(sock, (char*)buffer + sofar, todo-sofar, 0); if (r <= 0){ - socketError = true; - printf("Could not read! %s\n", strerror(errno)); - return false; + switch (errno){ + case EWOULDBLOCK: printf("Read: Would block\n"); break; + case EAGAIN: printf("Read: Again\n"); break; + default: + socketError = true; + printf("Could not read! %s\n", strerror(errno)); + return false; + break; + } } sofar += r; } @@ -96,7 +108,7 @@ int DDV_iwrite(void * buffer, int todo, int sock){ int r = send(sock, buffer, todo, 0); if (r < 0){ socketError = true; - printf("Could not write! %s\n", strerror(errno)); + printf("Could not iwrite! %s\n", strerror(errno)); } return r; } @@ -105,7 +117,7 @@ int DDV_iread(void * buffer, int todo, int sock){ int r = recv(sock, buffer, todo, 0); if (r < 0){ socketError = true; - printf("Could not write! %s\n", strerror(errno)); + printf("Could not iread! %s\n", strerror(errno)); } return r; } From 0d04ea2ace154dabe7ad1dac4a817f9311323118 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 15:51:34 +0100 Subject: [PATCH 026/788] DDVSocket edits --- util/ddv_socket.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index 1aa0cad9..19bcab59 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -66,7 +66,6 @@ bool DDV_write(void * buffer, int todo, int sock){ if (r <= 0){ switch (errno){ case EWOULDBLOCK: printf("Would block\n"); break; - case EAGAIN: printf("Again\n"); break; default: socketError = true; printf("Could not write! %s\n", strerror(errno)); @@ -86,7 +85,6 @@ bool DDV_read(void * buffer, int todo, int sock){ if (r <= 0){ switch (errno){ case EWOULDBLOCK: printf("Read: Would block\n"); break; - case EAGAIN: printf("Read: Again\n"); break; default: socketError = true; printf("Could not read! %s\n", strerror(errno)); From 71a5bf4ea69d0094ad47d548a445615b3580d9b2 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 15:52:53 +0100 Subject: [PATCH 027/788] DDVSocket edits --- util/ddv_socket.cpp | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index 19bcab59..23f2de8b 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -105,8 +105,14 @@ bool DDV_write(void * buffer, int width, int count, int sock){return DDV_write(b int DDV_iwrite(void * buffer, int todo, int sock){ int r = send(sock, buffer, todo, 0); if (r < 0){ - socketError = true; - printf("Could not iwrite! %s\n", strerror(errno)); + switch (errno){ + case EWOULDBLOCK: printf("Write: Would block\n"); break; + default: + socketError = true; + printf("Could not write! %s\n", strerror(errno)); + return false; + break; + } } return r; } @@ -114,8 +120,14 @@ int DDV_iwrite(void * buffer, int todo, int sock){ int DDV_iread(void * buffer, int todo, int sock){ int r = recv(sock, buffer, todo, 0); if (r < 0){ - socketError = true; - printf("Could not iread! %s\n", strerror(errno)); + switch (errno){ + case EWOULDBLOCK: printf("Read: Would block\n"); break; + default: + socketError = true; + printf("Could not read! %s\n", strerror(errno)); + return false; + break; + } } return r; } From 8c6a315dfb8b3cc12f7f150bf7c3df11e9ee948d Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 15:56:43 +0100 Subject: [PATCH 028/788] DDVSocket edits --- util/ddv_socket.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index 23f2de8b..0c14a371 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -55,8 +55,14 @@ int DDV_Listen(int port){ } } -int DDV_Accept(int sock){ - return accept(sock, 0, 0); +int DDV_Accept(int sock, bool nonblock = false){ + int r = accept(sock, 0, 0); + if ((r > 0) && nonblock){ + int flags = fcntl(r, F_GETFL, 0); + flags |= O_NONBLOCK; + fcntl(r, F_SETFL, flags); + } + return r; } bool DDV_write(void * buffer, int todo, int sock){ From a6311fe5358082612234463949ac628fb3815d56 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 16:09:42 +0100 Subject: [PATCH 029/788] DDVSocket edits --- util/ddv_socket.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index 0c14a371..1d3efd69 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -9,6 +9,7 @@ #include bool socketError = false; +bool socketBlocking = false; int DDV_OpenUnix(const char adres[], bool nonblock = false){ int s = socket(AF_UNIX, SOCK_STREAM, 0); @@ -67,11 +68,12 @@ int DDV_Accept(int sock, bool nonblock = false){ bool DDV_write(void * buffer, int todo, int sock){ int sofar = 0; + socketBlocking = false; while (sofar != todo){ int r = send(sock, (char*)buffer + sofar, todo-sofar, 0); if (r <= 0){ switch (errno){ - case EWOULDBLOCK: printf("Would block\n"); break; + case EWOULDBLOCK: printf("Would block\n"); socketBlocking = true; break; default: socketError = true; printf("Could not write! %s\n", strerror(errno)); @@ -86,11 +88,12 @@ bool DDV_write(void * buffer, int todo, int sock){ bool DDV_read(void * buffer, int todo, int sock){ int sofar = 0; + socketBlocking = false; while (sofar != todo){ int r = recv(sock, (char*)buffer + sofar, todo-sofar, 0); if (r <= 0){ switch (errno){ - case EWOULDBLOCK: printf("Read: Would block\n"); break; + case EWOULDBLOCK: printf("Read: Would block\n"); socketBlocking = true; break; default: socketError = true; printf("Could not read! %s\n", strerror(errno)); From a23c9696b6735ca311e86db855b9ed39484b433b Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 16:12:53 +0100 Subject: [PATCH 030/788] DDVSocket edits --- util/ddv_socket.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index 1d3efd69..19a6b80e 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -73,7 +73,7 @@ bool DDV_write(void * buffer, int todo, int sock){ int r = send(sock, (char*)buffer + sofar, todo-sofar, 0); if (r <= 0){ switch (errno){ - case EWOULDBLOCK: printf("Would block\n"); socketBlocking = true; break; + case EWOULDBLOCK: socketBlocking = true; return false; break; default: socketError = true; printf("Could not write! %s\n", strerror(errno)); @@ -93,7 +93,7 @@ bool DDV_read(void * buffer, int todo, int sock){ int r = recv(sock, (char*)buffer + sofar, todo-sofar, 0); if (r <= 0){ switch (errno){ - case EWOULDBLOCK: printf("Read: Would block\n"); socketBlocking = true; break; + case EWOULDBLOCK: socketBlocking = true; return false; break; default: socketError = true; printf("Could not read! %s\n", strerror(errno)); @@ -115,7 +115,7 @@ int DDV_iwrite(void * buffer, int todo, int sock){ int r = send(sock, buffer, todo, 0); if (r < 0){ switch (errno){ - case EWOULDBLOCK: printf("Write: Would block\n"); break; + case EWOULDBLOCK: break; default: socketError = true; printf("Could not write! %s\n", strerror(errno)); @@ -130,7 +130,7 @@ int DDV_iread(void * buffer, int todo, int sock){ int r = recv(sock, buffer, todo, 0); if (r < 0){ switch (errno){ - case EWOULDBLOCK: printf("Read: Would block\n"); break; + case EWOULDBLOCK: break; default: socketError = true; printf("Could not read! %s\n", strerror(errno)); From 83f59d4213b6f13bdb6a1d00824c29a09cef54a1 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 16:25:35 +0100 Subject: [PATCH 031/788] DDVSocket edits --- util/ddv_socket.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index 19a6b80e..43406e4d 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -73,7 +73,7 @@ bool DDV_write(void * buffer, int todo, int sock){ int r = send(sock, (char*)buffer + sofar, todo-sofar, 0); if (r <= 0){ switch (errno){ - case EWOULDBLOCK: socketBlocking = true; return false; break; + case EWOULDBLOCK: socketBlocking = true; break; default: socketError = true; printf("Could not write! %s\n", strerror(errno)); @@ -86,6 +86,12 @@ bool DDV_write(void * buffer, int todo, int sock){ return true; } +bool DDV_ready(int sock){ + char tmp; + int r = recv(sock, &tmp, 1, MSG_PEEK); + return (r == 1); +} + bool DDV_read(void * buffer, int todo, int sock){ int sofar = 0; socketBlocking = false; @@ -93,7 +99,7 @@ bool DDV_read(void * buffer, int todo, int sock){ int r = recv(sock, (char*)buffer + sofar, todo-sofar, 0); if (r <= 0){ switch (errno){ - case EWOULDBLOCK: socketBlocking = true; return false; break; + case EWOULDBLOCK: socketBlocking = true; break; default: socketError = true; printf("Could not read! %s\n", strerror(errno)); From 8081e4cb66195cfec40c13d1e358145bf6c6d528 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 16:29:06 +0100 Subject: [PATCH 032/788] DDVSocket edits --- util/ddv_socket.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index 43406e4d..a1cecf6e 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -88,7 +88,11 @@ bool DDV_write(void * buffer, int todo, int sock){ bool DDV_ready(int sock){ char tmp; + int preflags = fcntl(sock, F_GETFL, 0); + int postflags = preflags | O_NONBLOCK; + fcntl(sock, F_SETFL, postflags); int r = recv(sock, &tmp, 1, MSG_PEEK); + fcntl(sock, F_SETFL, preflags); return (r == 1); } From 8420db87ca0bfb631ee26121ddf0ef8735d5ec93 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 16:33:30 +0100 Subject: [PATCH 033/788] DDVSocket edits --- util/ddv_socket.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index a1cecf6e..cedcc76c 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -13,8 +13,6 @@ bool socketBlocking = false; int DDV_OpenUnix(const char adres[], bool nonblock = false){ int s = socket(AF_UNIX, SOCK_STREAM, 0); - int on = 1; - setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); sockaddr_un addr; addr.sun_family = AF_UNIX; strcpy(addr.sun_path, adres); @@ -35,6 +33,8 @@ int DDV_OpenUnix(const char adres[], bool nonblock = false){ int DDV_Listen(int port){ int s = socket(AF_INET, SOCK_STREAM, 0); + int on = 1; + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(port);//port 8888 From 0d7882f68b6ab746096b2fe8371971bdc4acbbc9 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 19:13:35 +0100 Subject: [PATCH 034/788] Meer debugging --- util/flv_sock.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/util/flv_sock.cpp b/util/flv_sock.cpp index ac520b43..8dd8fbc9 100644 --- a/util/flv_sock.cpp +++ b/util/flv_sock.cpp @@ -35,7 +35,11 @@ bool ReadUntil(char * buffer, unsigned int count, unsigned int & sofar, int sock if (sofar >= count){return true;} int r = 0; r = DDV_iread(buffer + sofar,count-sofar,sock); - if (r < 0){All_Hell_Broke_Loose = true; return false;} + if (r < 0){ + All_Hell_Broke_Loose = true; + fprintf(stderr, "ReadUntil fail: %s. All Hell Broke Loose!\n", strerror(errno)); + return false; + } sofar += r; if (sofar >= count){return true;} return false; @@ -63,7 +67,10 @@ bool FLV_GetPacket(FLV_Pack *& p, int sock){ if (FLV_Checkheader(p->data)){ sofar = 0; memcpy(FLVHeader, p->data, 13); - }else{All_Hell_Broke_Loose = true;} + }else{ + All_Hell_Broke_Loose = true; + fprintf(stderr, "Invalid FLV header. All Hell Broke Loose!\n"); + } } }else{ //if a tag header, calculate length and read tag body From 4c6f17cdbabdf03a990b2b924a5b0866c75afcc5 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 19:15:38 +0100 Subject: [PATCH 035/788] Meer debugging --- util/flv_sock.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/util/flv_sock.cpp b/util/flv_sock.cpp index 8dd8fbc9..a63bf42a 100644 --- a/util/flv_sock.cpp +++ b/util/flv_sock.cpp @@ -36,8 +36,10 @@ bool ReadUntil(char * buffer, unsigned int count, unsigned int & sofar, int sock int r = 0; r = DDV_iread(buffer + sofar,count-sofar,sock); if (r < 0){ - All_Hell_Broke_Loose = true; - fprintf(stderr, "ReadUntil fail: %s. All Hell Broke Loose!\n", strerror(errno)); + if (errno != EWOULDBLOCK){ + All_Hell_Broke_Loose = true; + fprintf(stderr, "ReadUntil fail: %s. All Hell Broke Loose!\n", strerror(errno)); + } return false; } sofar += r; From 15f1570d625a9d98a6028fcd262606d02a7eb074 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 19:26:38 +0100 Subject: [PATCH 036/788] Meer debugging --- util/ddv_socket.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index 56bfd210..610c1cc8 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -11,12 +12,12 @@ bool socketError = false; bool socketBlocking = false; -int DDV_OpenUnix(const char adres[], bool nonblock = false){ +int DDV_OpenUnix(std::string adres, bool nonblock = false){ int s = socket(AF_UNIX, SOCK_STREAM, 0); - struct sockaddr_un addr; + sockaddr_un addr; addr.sun_family = AF_UNIX; - strcpy(addr.sun_path, adres); - int r = connect(s, (sockaddr*)&addr, sizeof(struct sockaddr_un)); + strncpy(addr.sun_path, adres.c_str(), adres.size()+1); + int r = connect(s, (sockaddr*)&addr, sizeof(addr)); if (r == 0){ if (nonblock){ int flags = fcntl(s, F_GETFL, 0); From 6b80d489874c9829e4fde5d83c01e5d260b622fa Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 19:41:06 +0100 Subject: [PATCH 037/788] Meer debugging --- util/ddv_socket.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index 610c1cc8..70274e29 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -13,7 +13,7 @@ bool socketError = false; bool socketBlocking = false; int DDV_OpenUnix(std::string adres, bool nonblock = false){ - int s = socket(AF_UNIX, SOCK_STREAM, 0); + int s = socket(PF_UNIX, SOCK_STREAM, 0); sockaddr_un addr; addr.sun_family = AF_UNIX; strncpy(addr.sun_path, adres.c_str(), adres.size()+1); @@ -24,6 +24,7 @@ int DDV_OpenUnix(std::string adres, bool nonblock = false){ flags |= O_NONBLOCK; fcntl(s, F_SETFL, flags); } + fprintf(stderr, "Connected to %s\n", addr.sun_path); return s; }else{ close(s); From ad5f98fa34cdf37506eda000706c71153468ac24 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 19:44:18 +0100 Subject: [PATCH 038/788] Meer debugging --- util/ddv_socket.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index 70274e29..d8babf7a 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -24,7 +24,6 @@ int DDV_OpenUnix(std::string adres, bool nonblock = false){ flags |= O_NONBLOCK; fcntl(s, F_SETFL, flags); } - fprintf(stderr, "Connected to %s\n", addr.sun_path); return s; }else{ close(s); From 0bda534d2b5253f638b0bcb18e9afb441ca4f1e4 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 19:51:51 +0100 Subject: [PATCH 039/788] Meer debugging --- util/flv_sock.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/util/flv_sock.cpp b/util/flv_sock.cpp index a63bf42a..58f3a14d 100644 --- a/util/flv_sock.cpp +++ b/util/flv_sock.cpp @@ -79,6 +79,7 @@ bool FLV_GetPacket(FLV_Pack *& p, int sock){ p->len = p->data[3] + 15; p->len += (p->data[2] << 8); p->len += (p->data[1] << 16); + fprintf(stderr, "Tag of len %i\n", p->len); if (p->buf < p->len){p->data = (char*)realloc(p->data, p->len);p->buf = p->len;} done = false; } From 451a2563c402ef10299577db7fd77fb9aaacf176 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 20:15:47 +0100 Subject: [PATCH 040/788] Minder debugging --- util/flv_sock.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/util/flv_sock.cpp b/util/flv_sock.cpp index 58f3a14d..8de52131 100644 --- a/util/flv_sock.cpp +++ b/util/flv_sock.cpp @@ -52,9 +52,6 @@ bool ReadUntil(char * buffer, unsigned int count, unsigned int & sofar, int sock //resizes FLV_Pack data field bigger if data doesn't fit // (does not auto-shrink for speed!) bool FLV_GetPacket(FLV_Pack *& p, int sock){ - int preflags = fcntl(sock, F_GETFL, 0); - int postflags = preflags | O_NONBLOCK; - fcntl(sock, F_SETFL, postflags); static bool done = true; static unsigned int sofar = 0; if (!p){p = (FLV_Pack*)calloc(1, sizeof(FLV_Pack));} @@ -79,7 +76,7 @@ bool FLV_GetPacket(FLV_Pack *& p, int sock){ p->len = p->data[3] + 15; p->len += (p->data[2] << 8); p->len += (p->data[1] << 16); - fprintf(stderr, "Tag of len %i\n", p->len); + //fprintf(stderr, "Tag of len %i\n", p->len); if (p->buf < p->len){p->data = (char*)realloc(p->data, p->len);p->buf = p->len;} done = false; } @@ -92,11 +89,9 @@ bool FLV_GetPacket(FLV_Pack *& p, int sock){ if ((p->data[0] == 0x09) && (((p->data[11] & 0xf0) >> 4) == 1)){p->isKeyframe = true;} done = true; sofar = 0; - fcntl(sock, F_SETFL, preflags); return true; } } - fcntl(sock, F_SETFL, preflags); return false; }//FLV_GetPacket From 2ee4b17811c4558592ed6c1294e1387dd120bed0 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 20:28:37 +0100 Subject: [PATCH 041/788] Minder debugging --- util/flv_sock.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/util/flv_sock.cpp b/util/flv_sock.cpp index 8de52131..d04e3283 100644 --- a/util/flv_sock.cpp +++ b/util/flv_sock.cpp @@ -32,6 +32,11 @@ bool FLV_Isheader(char * header){ }//FLV_Isheader bool ReadUntil(char * buffer, unsigned int count, unsigned int & sofar, int sock){ + if (count > 500000){ + All_Hell_Broke_Loose = true; + fprintf(stderr, "ReadUntil fail: > 500kb tag? All Hell Broke Loose!\n", strerror(errno)); + return false; + } if (sofar >= count){return true;} int r = 0; r = DDV_iread(buffer + sofar,count-sofar,sock); From 4e2afeacda8636bbec6ed1401e06945c95c33769 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 20:37:02 +0100 Subject: [PATCH 042/788] Minder debugging --- util/flv_sock.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/util/flv_sock.cpp b/util/flv_sock.cpp index d04e3283..dce181d6 100644 --- a/util/flv_sock.cpp +++ b/util/flv_sock.cpp @@ -48,7 +48,11 @@ bool ReadUntil(char * buffer, unsigned int count, unsigned int & sofar, int sock return false; } sofar += r; - if (sofar >= count){return true;} + if (sofar == count){return true;} + if (sofar > count){ + All_Hell_Broke_Loose = true; + fprintf(stderr, "ReadUntil fail: %s. Read too much. All Hell Broke Loose!\n", strerror(errno)); + } return false; } From b589933e069937b9d9422369ec35b59c0dc4ebbb Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 20:51:14 +0100 Subject: [PATCH 043/788] Minder debugging --- util/flv_sock.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/util/flv_sock.cpp b/util/flv_sock.cpp index dce181d6..f9bd4b05 100644 --- a/util/flv_sock.cpp +++ b/util/flv_sock.cpp @@ -96,6 +96,10 @@ bool FLV_GetPacket(FLV_Pack *& p, int sock){ //calculate keyframeness, next time read header again, return true p->isKeyframe = false; if ((p->data[0] == 0x09) && (((p->data[11] & 0xf0) >> 4) == 1)){p->isKeyframe = true;} + int testlen = p->data[p->len-1] + 15; + testlen += (p->data[p->len-2] << 8); + testlen += (p->data[p->len-3] << 16); + fprintf(stderr, "Len: %i, testlen: %i\n", p->len, testlen); done = true; sofar = 0; return true; From 5f9dcf0c330bb88c4b8ea6207c832e7fcedc411c Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 20:56:14 +0100 Subject: [PATCH 044/788] Tag lengths... --- util/flv_sock.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/util/flv_sock.cpp b/util/flv_sock.cpp index f9bd4b05..81a1cf74 100644 --- a/util/flv_sock.cpp +++ b/util/flv_sock.cpp @@ -96,9 +96,10 @@ bool FLV_GetPacket(FLV_Pack *& p, int sock){ //calculate keyframeness, next time read header again, return true p->isKeyframe = false; if ((p->data[0] == 0x09) && (((p->data[11] & 0xf0) >> 4) == 1)){p->isKeyframe = true;} - int testlen = p->data[p->len-1] + 15; + int testlen = p->data[p->len-1] + 3; testlen += (p->data[p->len-2] << 8); testlen += (p->data[p->len-3] << 16); + testlen += (p->data[p->len-4] << 24); fprintf(stderr, "Len: %i, testlen: %i\n", p->len, testlen); done = true; sofar = 0; From ab50f37c4ed89144a2774f9bed923381fe6a5503 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 20:58:16 +0100 Subject: [PATCH 045/788] Tag length check --- util/flv_sock.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/util/flv_sock.cpp b/util/flv_sock.cpp index 81a1cf74..3f6d32a9 100644 --- a/util/flv_sock.cpp +++ b/util/flv_sock.cpp @@ -32,11 +32,6 @@ bool FLV_Isheader(char * header){ }//FLV_Isheader bool ReadUntil(char * buffer, unsigned int count, unsigned int & sofar, int sock){ - if (count > 500000){ - All_Hell_Broke_Loose = true; - fprintf(stderr, "ReadUntil fail: > 500kb tag? All Hell Broke Loose!\n", strerror(errno)); - return false; - } if (sofar >= count){return true;} int r = 0; r = DDV_iread(buffer + sofar,count-sofar,sock); @@ -96,11 +91,18 @@ bool FLV_GetPacket(FLV_Pack *& p, int sock){ //calculate keyframeness, next time read header again, return true p->isKeyframe = false; if ((p->data[0] == 0x09) && (((p->data[11] & 0xf0) >> 4) == 1)){p->isKeyframe = true;} - int testlen = p->data[p->len-1] + 3; + int testlen = p->data[p->len-1] + 4; testlen += (p->data[p->len-2] << 8); testlen += (p->data[p->len-3] << 16); testlen += (p->data[p->len-4] << 24); - fprintf(stderr, "Len: %i, testlen: %i\n", p->len, testlen); + if (p->len == testlen){ + fprintf(stderr, "Correct length tag...\n"); + }else{ + fprintf(stderr, "Len: %i, testlen: %i\n", p->len, testlen); + All_Hell_Broke_Loose = true; + fprintf(stderr, "ReadUntil fail: > 500kb tag? All Hell Broke Loose!\n", strerror(errno)); + return false; + } done = true; sofar = 0; return true; From 4dec919890cc0f8a36a457ebe90a41de0c1528de Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 21:23:20 +0100 Subject: [PATCH 046/788] Gadver --- util/flv_sock.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/util/flv_sock.cpp b/util/flv_sock.cpp index 3f6d32a9..ebffa7b5 100644 --- a/util/flv_sock.cpp +++ b/util/flv_sock.cpp @@ -35,6 +35,7 @@ bool ReadUntil(char * buffer, unsigned int count, unsigned int & sofar, int sock if (sofar >= count){return true;} int r = 0; r = DDV_iread(buffer + sofar,count-sofar,sock); + fprintf(stderr, "Reading %i/%i, read %i, at %i\n", count, sofar, r, buffer+sofar); if (r < 0){ if (errno != EWOULDBLOCK){ All_Hell_Broke_Loose = true; From 6064dedb8a7d0776710fef99b86272e34da9c5ce Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 21:30:54 +0100 Subject: [PATCH 047/788] Gadver --- util/flv_sock.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/flv_sock.cpp b/util/flv_sock.cpp index ebffa7b5..944ec2d3 100644 --- a/util/flv_sock.cpp +++ b/util/flv_sock.cpp @@ -60,7 +60,7 @@ bool FLV_GetPacket(FLV_Pack *& p, int sock){ static bool done = true; static unsigned int sofar = 0; if (!p){p = (FLV_Pack*)calloc(1, sizeof(FLV_Pack));} - if (p->buf < 15){p->data = (char*)realloc(p->data, 15); p->buf = 15;} + if (p->buf < 15){p->data = (char*)realloc(p->data, 5000); p->buf = 5000;} if (done){ //read a header From 520bc57ddbae8555e18eaa236091f449d0e80a24 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 21:33:24 +0100 Subject: [PATCH 048/788] Gadver --- util/flv_sock.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/flv_sock.cpp b/util/flv_sock.cpp index 944ec2d3..bc91c0d0 100644 --- a/util/flv_sock.cpp +++ b/util/flv_sock.cpp @@ -34,7 +34,7 @@ bool FLV_Isheader(char * header){ bool ReadUntil(char * buffer, unsigned int count, unsigned int & sofar, int sock){ if (sofar >= count){return true;} int r = 0; - r = DDV_iread(buffer + sofar,count-sofar,sock); + r = DDV_read(buffer + sofar,count-sofar,sock); fprintf(stderr, "Reading %i/%i, read %i, at %i\n", count, sofar, r, buffer+sofar); if (r < 0){ if (errno != EWOULDBLOCK){ From 63c83a4d88fc1b60008d6e06e9ecff0e165bad6c Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 21:35:18 +0100 Subject: [PATCH 049/788] Gadver --- util/flv_sock.cpp | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/util/flv_sock.cpp b/util/flv_sock.cpp index bc91c0d0..dbc9c419 100644 --- a/util/flv_sock.cpp +++ b/util/flv_sock.cpp @@ -33,23 +33,13 @@ bool FLV_Isheader(char * header){ bool ReadUntil(char * buffer, unsigned int count, unsigned int & sofar, int sock){ if (sofar >= count){return true;} - int r = 0; - r = DDV_read(buffer + sofar,count-sofar,sock); - fprintf(stderr, "Reading %i/%i, read %i, at %i\n", count, sofar, r, buffer+sofar); - if (r < 0){ - if (errno != EWOULDBLOCK){ - All_Hell_Broke_Loose = true; - fprintf(stderr, "ReadUntil fail: %s. All Hell Broke Loose!\n", strerror(errno)); - } - return false; - } - sofar += r; - if (sofar == count){return true;} - if (sofar > count){ + bool r = DDV_read(buffer + sofar,count-sofar,sock); + fprintf(stderr, "Reading %i/%i\n", sofar, count); + if (!r){ All_Hell_Broke_Loose = true; - fprintf(stderr, "ReadUntil fail: %s. Read too much. All Hell Broke Loose!\n", strerror(errno)); + fprintf(stderr, "ReadUntil fail: %s. All Hell Broke Loose!\n", strerror(errno)); } - return false; + return r; } //gets a packet, storing in given FLV_Pack pointer. From eae7cd906c94e83ef6bbfc38d6b91d8022d4469d Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 21:36:14 +0100 Subject: [PATCH 050/788] Gadver --- util/flv_sock.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/util/flv_sock.cpp b/util/flv_sock.cpp index dbc9c419..7ea1985f 100644 --- a/util/flv_sock.cpp +++ b/util/flv_sock.cpp @@ -34,6 +34,7 @@ bool FLV_Isheader(char * header){ bool ReadUntil(char * buffer, unsigned int count, unsigned int & sofar, int sock){ if (sofar >= count){return true;} bool r = DDV_read(buffer + sofar,count-sofar,sock); + sofar = count; fprintf(stderr, "Reading %i/%i\n", sofar, count); if (!r){ All_Hell_Broke_Loose = true; From 058096f5d573d9656e1463b01c99d3fc017dca12 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 21:37:27 +0100 Subject: [PATCH 051/788] Gadver --- util/flv_sock.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/flv_sock.cpp b/util/flv_sock.cpp index 7ea1985f..4725b8d6 100644 --- a/util/flv_sock.cpp +++ b/util/flv_sock.cpp @@ -33,9 +33,9 @@ bool FLV_Isheader(char * header){ bool ReadUntil(char * buffer, unsigned int count, unsigned int & sofar, int sock){ if (sofar >= count){return true;} + fprintf(stderr, "Reading %i/%i\n", sofar, count); bool r = DDV_read(buffer + sofar,count-sofar,sock); sofar = count; - fprintf(stderr, "Reading %i/%i\n", sofar, count); if (!r){ All_Hell_Broke_Loose = true; fprintf(stderr, "ReadUntil fail: %s. All Hell Broke Loose!\n", strerror(errno)); From f3d73b3df4600bb39ce417f46ca1d176e519aed7 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 21:40:38 +0100 Subject: [PATCH 052/788] Gadver --- util/ddv_socket.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index d8babf7a..eaff2fc2 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -94,6 +94,9 @@ bool DDV_ready(int sock){ fcntl(sock, F_SETFL, postflags); int r = recv(sock, &tmp, 1, MSG_PEEK); fcntl(sock, F_SETFL, preflags); + if (r != 1){ + fprintf(stderr, "Er ging iets mis... %i\n", r); + } return (r == 1); } From e80b413381ce6d2c5c9f746f84c4e0aba37e049b Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 21:45:39 +0100 Subject: [PATCH 053/788] Gadver --- util/ddv_socket.cpp | 17 ++++++++++++++--- util/flv_sock.cpp | 16 ++++++++++------ 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index eaff2fc2..f3da0b2f 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -94,12 +94,23 @@ bool DDV_ready(int sock){ fcntl(sock, F_SETFL, postflags); int r = recv(sock, &tmp, 1, MSG_PEEK); fcntl(sock, F_SETFL, preflags); - if (r != 1){ - fprintf(stderr, "Er ging iets mis... %i\n", r); - } return (r == 1); } +int DDV_readycount(int sock){ + static tmp[1048576]; + int preflags = fcntl(sock, F_GETFL, 0); + int postflags = preflags | O_NONBLOCK; + fcntl(sock, F_SETFL, postflags); + int r = recv(sock, tmp, 1048576, MSG_PEEK); + fcntl(sock, F_SETFL, preflags); + if (r > 0){ + return r; + }else{ + return 0; + } +} + bool DDV_read(void * buffer, int todo, int sock){ int sofar = 0; socketBlocking = false; diff --git a/util/flv_sock.cpp b/util/flv_sock.cpp index 4725b8d6..fe4912f1 100644 --- a/util/flv_sock.cpp +++ b/util/flv_sock.cpp @@ -34,13 +34,17 @@ bool FLV_Isheader(char * header){ bool ReadUntil(char * buffer, unsigned int count, unsigned int & sofar, int sock){ if (sofar >= count){return true;} fprintf(stderr, "Reading %i/%i\n", sofar, count); - bool r = DDV_read(buffer + sofar,count-sofar,sock); - sofar = count; - if (!r){ - All_Hell_Broke_Loose = true; - fprintf(stderr, "ReadUntil fail: %s. All Hell Broke Loose!\n", strerror(errno)); + if (DDV_readycount(sock) >= count-sofar){ + bool r = DDV_read(buffer + sofar,count-sofar,sock); + sofar = count; + if (!r){ + All_Hell_Broke_Loose = true; + fprintf(stderr, "ReadUntil fail: %s. All Hell Broke Loose!\n", strerror(errno)); + } + return r; + }else{ + return false; } - return r; } //gets a packet, storing in given FLV_Pack pointer. From f21ec7e2f2f03363156fa9d7eecd3232fb4aebc4 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 21:46:38 +0100 Subject: [PATCH 054/788] Gadver --- util/ddv_socket.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index f3da0b2f..e81e7445 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -98,7 +98,7 @@ bool DDV_ready(int sock){ } int DDV_readycount(int sock){ - static tmp[1048576]; + static char tmp[1048576]; int preflags = fcntl(sock, F_GETFL, 0); int postflags = preflags | O_NONBLOCK; fcntl(sock, F_SETFL, postflags); From 2585eebbcf8b69d73bd95536bec7ee2665696e13 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 21:48:48 +0100 Subject: [PATCH 055/788] Revert --- util/flv_sock.cpp | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/util/flv_sock.cpp b/util/flv_sock.cpp index fe4912f1..426aa7bd 100644 --- a/util/flv_sock.cpp +++ b/util/flv_sock.cpp @@ -32,19 +32,22 @@ bool FLV_Isheader(char * header){ }//FLV_Isheader bool ReadUntil(char * buffer, unsigned int count, unsigned int & sofar, int sock){ - if (sofar >= count){return true;} - fprintf(stderr, "Reading %i/%i\n", sofar, count); - if (DDV_readycount(sock) >= count-sofar){ - bool r = DDV_read(buffer + sofar,count-sofar,sock); - sofar = count; - if (!r){ + if (sofar == count){return true;} + int r = DDV_iread(buffer + sofar,count-sofar,sock); + if (r < 0){ + if (errno != EWOULDBLOCK){ All_Hell_Broke_Loose = true; fprintf(stderr, "ReadUntil fail: %s. All Hell Broke Loose!\n", strerror(errno)); } - return r; - }else{ return false; } + sofar += r; + if (sofar == count){return true;} + if (sofar > count){ + All_Hell_Broke_Loose = true; + fprintf(stderr, "ReadUntil fail: %s. Read too much. All Hell Broke Loose!\n", strerror(errno)); + } + return false; } //gets a packet, storing in given FLV_Pack pointer. @@ -55,7 +58,7 @@ bool FLV_GetPacket(FLV_Pack *& p, int sock){ static bool done = true; static unsigned int sofar = 0; if (!p){p = (FLV_Pack*)calloc(1, sizeof(FLV_Pack));} - if (p->buf < 15){p->data = (char*)realloc(p->data, 5000); p->buf = 5000;} + if (p->buf < 15){p->data = (char*)realloc(p->data, 15); p->buf = 15;} if (done){ //read a header From c1c4f3049892d2e155c6fc02e3388a0a9c8888a0 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Nov 2010 22:49:15 +0100 Subject: [PATCH 056/788] Oude connector naast de nieuwe... poort nummer nieuwe is nu 1936! --- util/ddv_socket.cpp | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index e81e7445..d8babf7a 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -97,20 +97,6 @@ bool DDV_ready(int sock){ return (r == 1); } -int DDV_readycount(int sock){ - static char tmp[1048576]; - int preflags = fcntl(sock, F_GETFL, 0); - int postflags = preflags | O_NONBLOCK; - fcntl(sock, F_SETFL, postflags); - int r = recv(sock, tmp, 1048576, MSG_PEEK); - fcntl(sock, F_SETFL, preflags); - if (r > 0){ - return r; - }else{ - return 0; - } -} - bool DDV_read(void * buffer, int todo, int sock){ int sofar = 0; socketBlocking = false; From be1b8068a4e93b774b05f0704fb81c4d4c1afdbf Mon Sep 17 00:00:00 2001 From: Thulinma Date: Wed, 17 Nov 2010 01:05:36 +0100 Subject: [PATCH 057/788] Werkende RTMP connector! WHEEEE! Also, RTMPf weggegooit. Er is geen ruimte voor faal in dit bedrijf! --- util/ddv_socket.cpp | 16 ++++++++-------- util/flv_sock.cpp | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index d8babf7a..dc4002c3 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -46,12 +46,12 @@ int DDV_Listen(int port){ if (ret == 0){ return s; }else{ - printf("Listen failed! Error: %s\n", strerror(errno)); + fprintf(stderr, "Listen failed! Error: %s\n", strerror(errno)); close(s); return 0; } }else{ - printf("Binding failed! Error: %s\n", strerror(errno)); + fprintf(stderr, "Binding failed! Error: %s\n", strerror(errno)); close(s); return 0; } @@ -77,7 +77,7 @@ bool DDV_write(void * buffer, int todo, int sock){ case EWOULDBLOCK: socketBlocking = true; break; default: socketError = true; - printf("Could not write! %s\n", strerror(errno)); + fprintf(stderr, "Could not write! %s\n", strerror(errno)); return false; break; } @@ -87,14 +87,14 @@ bool DDV_write(void * buffer, int todo, int sock){ return true; } -bool DDV_ready(int sock){ +signed int DDV_ready(int sock){ char tmp; int preflags = fcntl(sock, F_GETFL, 0); int postflags = preflags | O_NONBLOCK; fcntl(sock, F_SETFL, postflags); int r = recv(sock, &tmp, 1, MSG_PEEK); fcntl(sock, F_SETFL, preflags); - return (r == 1); + return r; } bool DDV_read(void * buffer, int todo, int sock){ @@ -107,7 +107,7 @@ bool DDV_read(void * buffer, int todo, int sock){ case EWOULDBLOCK: socketBlocking = true; break; default: socketError = true; - printf("Could not read! %s\n", strerror(errno)); + fprintf(stderr, "Could not read! %s\n", strerror(errno)); return false; break; } @@ -129,7 +129,7 @@ int DDV_iwrite(void * buffer, int todo, int sock){ case EWOULDBLOCK: break; default: socketError = true; - printf("Could not write! %s\n", strerror(errno)); + fprintf(stderr, "Could not write! %s\n", strerror(errno)); return false; break; } @@ -144,7 +144,7 @@ int DDV_iread(void * buffer, int todo, int sock){ case EWOULDBLOCK: break; default: socketError = true; - printf("Could not read! %s\n", strerror(errno)); + fprintf(stderr, "Could not read! %s\n", strerror(errno)); return false; break; } diff --git a/util/flv_sock.cpp b/util/flv_sock.cpp index 426aa7bd..be752d73 100644 --- a/util/flv_sock.cpp +++ b/util/flv_sock.cpp @@ -69,6 +69,7 @@ bool FLV_GetPacket(FLV_Pack *& p, int sock){ if (FLV_Checkheader(p->data)){ sofar = 0; memcpy(FLVHeader, p->data, 13); + //fwrite(p->data, 13, 1, stdout);//output raw stream }else{ All_Hell_Broke_Loose = true; fprintf(stderr, "Invalid FLV header. All Hell Broke Loose!\n"); @@ -94,12 +95,11 @@ bool FLV_GetPacket(FLV_Pack *& p, int sock){ testlen += (p->data[p->len-2] << 8); testlen += (p->data[p->len-3] << 16); testlen += (p->data[p->len-4] << 24); - if (p->len == testlen){ - fprintf(stderr, "Correct length tag...\n"); - }else{ + //fwrite(p->data, p->len, 1, stdout);//output raw stream + if (p->len != testlen){ fprintf(stderr, "Len: %i, testlen: %i\n", p->len, testlen); All_Hell_Broke_Loose = true; - fprintf(stderr, "ReadUntil fail: > 500kb tag? All Hell Broke Loose!\n", strerror(errno)); + fprintf(stderr, "ReadUntil fail: Wrong size tag? All Hell Broke Loose!\n"); return false; } done = true; From 3978783f2811edbd794d1338021dff9a74340831 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sat, 20 Nov 2010 19:54:44 +0100 Subject: [PATCH 058/788] Rewrite Buffer programma --- util/ddv_socket.cpp | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index dc4002c3..e7b6c099 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -33,7 +33,6 @@ int DDV_OpenUnix(std::string adres, bool nonblock = false){ int DDV_Listen(int port){ int s = socket(AF_INET, SOCK_STREAM, 0); - int on = 1; setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); struct sockaddr_in addr; @@ -57,9 +56,37 @@ int DDV_Listen(int port){ } } +int DDV_UnixListen(std::string adres, bool nonblock = false){ + unlink(adres.c_str()); + int s = socket(AF_UNIX, SOCK_STREAM, 0); + if (nonblock){ + int flags = fcntl(s, F_GETFL, 0); + flags |= O_NONBLOCK; + fcntl(s, F_SETFL, flags); + } + sockaddr_un addr; + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, adres.c_str(), adres.size()+1); + int ret = bind(s, (sockaddr*)&addr, sizeof(addr)); + if (ret == 0){ + ret = listen(s, 100);//start listening, backlog of 100 allowed + if (ret == 0){ + return s; + }else{ + fprintf(stderr, "Listen failed! Error: %s\n", strerror(errno)); + close(s); + return 0; + } + }else{ + fprintf(stderr, "Binding failed! Error: %s\n", strerror(errno)); + close(s); + return 0; + } +} + int DDV_Accept(int sock, bool nonblock = false){ int r = accept(sock, 0, 0); - if ((r > 0) && nonblock){ + if ((r >= 0) && nonblock){ int flags = fcntl(r, F_GETFL, 0); flags |= O_NONBLOCK; fcntl(r, F_SETFL, flags); @@ -126,11 +153,11 @@ int DDV_iwrite(void * buffer, int todo, int sock){ int r = send(sock, buffer, todo, 0); if (r < 0){ switch (errno){ - case EWOULDBLOCK: break; + case EWOULDBLOCK: return 0; break; default: socketError = true; fprintf(stderr, "Could not write! %s\n", strerror(errno)); - return false; + return 0; break; } } @@ -145,7 +172,7 @@ int DDV_iread(void * buffer, int todo, int sock){ default: socketError = true; fprintf(stderr, "Could not read! %s\n", strerror(errno)); - return false; + return 0; break; } } From 43e6c0d35f46760eb515e34a05b2945f81247f66 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 17 Jan 2011 12:17:04 +0100 Subject: [PATCH 059/788] Ik ben een file vergeten --- util/server_setup.cpp | 94 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 util/server_setup.cpp diff --git a/util/server_setup.cpp b/util/server_setup.cpp new file mode 100644 index 00000000..d6be7c15 --- /dev/null +++ b/util/server_setup.cpp @@ -0,0 +1,94 @@ +int mainHandler(int CONN_fd);//define this function in your own code! +#include +#include "ddv_socket.cpp" //DDVTech Socket wrapper +#include "flv_sock.cpp" //FLV parsing with DDVTech Socket wrapper +int server_socket = 0; + +void termination_handler (int signum){ + if (server_socket == 0) return; + switch (signum){ + case SIGINT: break; + case SIGHUP: break; + case SIGTERM: break; + default: return; break; + } + close(server_socket); + server_socket = 0; +} + +int main(int argc, char ** argv){ + int CONN_fd = 0; + + //setup signal handler + struct sigaction new_action; + new_action.sa_handler = termination_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); + + int listen_port = DEFAULT_PORT; + bool daemon_mode = true; + + int opt = 0; + static const char *optString = "np:h?"; + static const struct option longOpts[] = { + {"help",0,0,'h'}, + {"port",1,0,'p'}, + {"no-daemon",0,0,'n'} + }; + while ((opt = getopt_long(argc, argv, optString, longOpts, 0)) != -1){ + switch (opt){ + case 'p': + listen_port = atoi(optarg); + break; + case 'n': + daemon_mode = false; + break; + case 'h': + case '?': + printf("Options: -h[elp], -?, -n[o-daemon], -p[ort] #\n"); + return 1; + break; + } + } + + server_socket = DDV_Listen(listen_port); + #if DEBUG >= 3 + fprintf(stderr, "Made a listening socket on port %i...\n", listen_port); + #endif + if (server_socket > 0){ + if (daemon_mode){ + daemon(1, 0); + #if DEBUG >= 3 + fprintf(stderr, "Going into background mode..."); + #endif + } + }else{ + #if DEBUG >= 1 + fprintf(stderr, "Error: could not make listening socket"); + #endif + return 1; + } + int status; + while (server_socket > 0){ + waitpid((pid_t)-1, &status, WNOHANG); + CONN_fd = DDV_Accept(server_socket); + if (CONN_fd > 0){ + pid_t myid = fork(); + if (myid == 0){ + break; + }else{ + #if DEBUG >= 3 + fprintf(stderr, "Spawned new process %i for handling socket %i\n", (int)myid, CONN_fd); + #endif + } + } + } + if (server_socket <= 0){ + return 0; + } + return mainHandler(CONN_fd); +} From 31fcdfb965de411dba45e228a54c074134b8ff28 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Mon, 17 Jan 2011 16:22:17 +0100 Subject: [PATCH 060/788] Merge branch 'master' of projectlivestream.com:pls From fb4f3f0a3e4f44124630a18c4ff3d62a3ffe02cf Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 24 Jan 2011 01:41:01 +0100 Subject: [PATCH 061/788] HTTP parser, plus wat kleine fixes. Erik, ik heb comments voor je achtergelaten in de vorm '//ERIK: tekst'. Enjoy. --- util/http_parser.cpp | 173 ++++++++++++++++++++++++++++++++++++++++++ util/server_setup.cpp | 4 +- 2 files changed, 175 insertions(+), 2 deletions(-) create mode 100644 util/http_parser.cpp diff --git a/util/http_parser.cpp b/util/http_parser.cpp new file mode 100644 index 00000000..b6bf2beb --- /dev/null +++ b/util/http_parser.cpp @@ -0,0 +1,173 @@ +#include +#include + +class HTTPReader{ + public: + HTTPReader(); + bool ReadSocket(int CONN_fd); + std::string GetHeader(std::string i); + std::string GetVar(std::string i); + void SetHeader(std::string i, std::string v); + void SetHeader(std::string i, int v); + void SetVar(std::string i, std::string v); + void SetBody(std::string s); + void SetBody(char * buffer, int len); + std::string BuildRequest(); + std::string BuildResponse(std::string code, std::string message); + void Clean(); + std::string method; + std::string url; + std::string protocol; + unsigned int length; + private: + bool seenHeaders; + bool seenReq; + bool parse(); + std::string HTTPbuffer; + std::map headers; + std::map vars; + void Trim(std::string & s); +};//HTTPReader + +HTTPReader::HTTPReader(){Clean();} +void HTTPReader::Clean(){ + seenHeaders = false; + seenReq = false; + method = "GET"; + url = "/"; + protocol = "HTTP/1.0"; + length = 0; + HTTPbuffer = ""; + headers.erase(headers.begin(), headers.end()); + vars.erase(vars.begin(), vars.end()); +} + +std::string HTTPReader::BuildRequest(){ + std::map::iterator it; + std::string tmp = method+" "+url+" "+protocol+"\n"; + for (it=headers.begin(); it != headers.end(); it++){ + tmp += (*it).first + ": " + (*it).second + "\n"; + } + tmp += "\n"; + tmp += HTTPbuffer; + return tmp; +} + +std::string HTTPReader::BuildResponse(std::string code, std::string message){ + std::map::iterator it; + std::string tmp = protocol+" "+code+" "+message+"\n"; + for (it=headers.begin(); it != headers.end(); it++){ + tmp += (*it).first + ": " + (*it).second + "\n"; + } + tmp += "\n"; + tmp += HTTPbuffer; + return tmp; +} + +void HTTPReader::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);} +} + +void HTTPReader::SetBody(std::string s){ + HTTPbuffer = s; + SetHeader("Content-Length", s.length()); +} + +void HTTPReader::SetBody(char * buffer, int len){ + HTTPbuffer = ""; + HTTPbuffer.append(buffer, len); + SetHeader("Content-Length", len); +} + + +std::string HTTPReader::GetHeader(std::string i){return headers[i];} +std::string HTTPReader::GetVar(std::string i){return vars[i];} + +void HTTPReader::SetHeader(std::string i, std::string v){ + Trim(i); + Trim(v); + headers[i] = v; +} + +void HTTPReader::SetHeader(std::string i, int v){ + Trim(i); + char val[128]; + sprintf(val, "%i", v); + headers[i] = val; +} + +void HTTPReader::SetVar(std::string i, std::string v){ + Trim(i); + Trim(v); + vars[i] = v; +} + +bool HTTPReader::ReadSocket(int CONN_fd){ + //returned true als hele http packet gelezen is + int r = 0; + int b = 0; + char buffer[500]; + while (true){ + r = DDV_ready(CONN_fd); + if (r < 1){ + if (r == 0){ + socketError = true; + #if DEBUG >= 1 + fprintf(stderr, "User socket is disconnected.\n"); + #endif + } + return parse(); + } + b = DDV_iread(buffer, 500, CONN_fd); + HTTPbuffer.append(buffer, b); + } + return false; +}//HTTPReader::ReadSocket + + +bool HTTPReader::parse(){ + size_t f; + std::string tmpA, tmpB, tmpC; + while (HTTPbuffer != ""){ + if (!seenHeaders){ + f = HTTPbuffer.find('\n'); + if (f == std::string::npos) return false; + tmpA = HTTPbuffer.substr(0, f); + HTTPbuffer.erase(0, f+1); + if (!seenReq){ + seenReq = true; + f = tmpA.find(' '); + if (f != std::string::npos){method = tmpA.substr(0, f); tmpA.erase(0, f+1);} + f = tmpA.find(' '); + if (f != std::string::npos){url = tmpA.substr(0, f); tmpA.erase(0, f+1);} + f = tmpA.find(' '); + if (f != std::string::npos){protocol = tmpA.substr(0, f); tmpA.erase(0, f+1);} + //TODO: GET variable parsing + }else{ + if (tmpA[0] == '\n'){ + seenHeaders = true; + if (GetHeader("Content-Length") != ""){length = atoi(GetHeader("Content-Length").c_str());} + }else{ + f = tmpA.find(':'); + if (f == std::string::npos) continue; + tmpB = tmpA.substr(0, f); + tmpC = tmpA.substr(f+1); + SetHeader(tmpB, tmpC); + } + } + } + if (seenHeaders){ + if (length > 0){ + //TODO: POST variable parsing + return (HTTPbuffer.length() >= length); + }else{ + return true; + } + } + } + return false; //we should never get here... +}//HTTPReader::parse + + diff --git a/util/server_setup.cpp b/util/server_setup.cpp index d6be7c15..fbd93aa0 100644 --- a/util/server_setup.cpp +++ b/util/server_setup.cpp @@ -63,12 +63,12 @@ int main(int argc, char ** argv){ if (daemon_mode){ daemon(1, 0); #if DEBUG >= 3 - fprintf(stderr, "Going into background mode..."); + fprintf(stderr, "Going into background mode...\n"); #endif } }else{ #if DEBUG >= 1 - fprintf(stderr, "Error: could not make listening socket"); + fprintf(stderr, "Error: could not make listening socket\n"); #endif return 1; } From 0ab03a5e2ade2f2c387d662732a02454729a400e Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sat, 12 Feb 2011 15:52:36 +0100 Subject: [PATCH 062/788] Nu met extra puddingbroodjes! --- util/http_parser.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/util/http_parser.cpp b/util/http_parser.cpp index b6bf2beb..f00cb2dd 100644 --- a/util/http_parser.cpp +++ b/util/http_parser.cpp @@ -136,6 +136,7 @@ bool HTTPReader::parse(){ if (f == std::string::npos) return false; tmpA = HTTPbuffer.substr(0, f); HTTPbuffer.erase(0, f+1); + while (tmpA.find('\r') != std::string::npos){tmpA.erase(tmpA.find('\r'));} if (!seenReq){ seenReq = true; f = tmpA.find(' '); @@ -146,7 +147,7 @@ bool HTTPReader::parse(){ if (f != std::string::npos){protocol = tmpA.substr(0, f); tmpA.erase(0, f+1);} //TODO: GET variable parsing }else{ - if (tmpA[0] == '\n'){ + if (tmpA.size() == 0){ seenHeaders = true; if (GetHeader("Content-Length") != ""){length = atoi(GetHeader("Content-Length").c_str());} }else{ From b8c1b6863316bf6e9f59ac7d4dfd8aa368f02233 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 14 Feb 2011 17:17:48 +0100 Subject: [PATCH 063/788] HTTP Progressive mode af en tested working --- util/ddv_socket.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index e7b6c099..0f26d36c 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -94,7 +94,7 @@ int DDV_Accept(int sock, bool nonblock = false){ return r; } -bool DDV_write(void * buffer, int todo, int sock){ +bool DDV_write(const void * buffer, int todo, int sock){ int sofar = 0; socketBlocking = false; while (sofar != todo){ From a30f63ab5a824885878f6977cd1950728b1086c8 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Wed, 16 Feb 2011 17:09:54 +0100 Subject: [PATCH 064/788] Merge branch 'master' of projectlivestream.com:pls From 183827ae9f76a1b7cdb44487f19eeeebc3066615 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Wed, 16 Feb 2011 21:42:40 +0100 Subject: [PATCH 065/788] Bijna klaar! --- util/MP4/Makefile | 18 ++ util/MP4/box.cpp | 150 +++++++++++ util/MP4/box_abst.cpp | 218 +++++++++++++++ util/MP4/box_afra.cpp | 75 ++++++ util/MP4/box_afrt.cpp | 100 +++++++ util/MP4/box_amhp.cpp | 63 +++++ util/MP4/box_asrt.cpp | 81 ++++++ util/MP4/box_avcC.cpp | 86 ++++++ util/MP4/box_dinf.cpp | 43 +++ util/MP4/box_dref.cpp | 58 ++++ util/MP4/box_esds.cpp | 55 ++++ util/MP4/box_ftyp.cpp | 39 +++ util/MP4/box_hdlr.cpp | 59 ++++ util/MP4/box_hmhd.cpp | 57 ++++ util/MP4/box_includes.h | 37 +++ util/MP4/box_mdat.cpp | 29 ++ util/MP4/box_mdhd.cpp | 88 ++++++ util/MP4/box_mdia.cpp | 51 ++++ util/MP4/box_mfhd.cpp | 39 +++ util/MP4/box_minf.cpp | 51 ++++ util/MP4/box_moof.cpp | 51 ++++ util/MP4/box_moov.cpp | 51 ++++ util/MP4/box_mvex.cpp | 51 ++++ util/MP4/box_mvhd.cpp | 113 ++++++++ util/MP4/box_nmhd.cpp | 28 ++ util/MP4/box_rtmp.cpp | 89 ++++++ util/MP4/box_smhd.cpp | 29 ++ util/MP4/box_stbl.cpp | 51 ++++ util/MP4/box_stco.cpp | 58 ++++ util/MP4/box_stsc.cpp | 63 +++++ util/MP4/box_stsd.cpp | 59 ++++ util/MP4/box_stts.cpp | 60 +++++ util/MP4/box_tfhd.cpp | 88 ++++++ util/MP4/box_tkhd.cpp | 117 ++++++++ util/MP4/box_traf.cpp | 51 ++++ util/MP4/box_trak.cpp | 51 ++++ util/MP4/box_trex.cpp | 59 ++++ util/MP4/box_trun.cpp | 68 +++++ util/MP4/box_url.cpp | 23 ++ util/MP4/box_vmhd.cpp | 45 ++++ util/MP4/interface.cpp | 583 ++++++++++++++++++++++++++++++++++++++++ util/MP4/main.cpp | 13 + 42 files changed, 3198 insertions(+) create mode 100644 util/MP4/Makefile create mode 100644 util/MP4/box.cpp create mode 100644 util/MP4/box_abst.cpp create mode 100644 util/MP4/box_afra.cpp create mode 100644 util/MP4/box_afrt.cpp create mode 100644 util/MP4/box_amhp.cpp create mode 100644 util/MP4/box_asrt.cpp create mode 100644 util/MP4/box_avcC.cpp create mode 100644 util/MP4/box_dinf.cpp create mode 100644 util/MP4/box_dref.cpp create mode 100644 util/MP4/box_esds.cpp create mode 100644 util/MP4/box_ftyp.cpp create mode 100644 util/MP4/box_hdlr.cpp create mode 100644 util/MP4/box_hmhd.cpp create mode 100644 util/MP4/box_includes.h create mode 100644 util/MP4/box_mdat.cpp create mode 100644 util/MP4/box_mdhd.cpp create mode 100644 util/MP4/box_mdia.cpp create mode 100644 util/MP4/box_mfhd.cpp create mode 100644 util/MP4/box_minf.cpp create mode 100644 util/MP4/box_moof.cpp create mode 100644 util/MP4/box_moov.cpp create mode 100644 util/MP4/box_mvex.cpp create mode 100644 util/MP4/box_mvhd.cpp create mode 100644 util/MP4/box_nmhd.cpp create mode 100644 util/MP4/box_rtmp.cpp create mode 100644 util/MP4/box_smhd.cpp create mode 100644 util/MP4/box_stbl.cpp create mode 100644 util/MP4/box_stco.cpp create mode 100644 util/MP4/box_stsc.cpp create mode 100644 util/MP4/box_stsd.cpp create mode 100644 util/MP4/box_stts.cpp create mode 100644 util/MP4/box_tfhd.cpp create mode 100644 util/MP4/box_tkhd.cpp create mode 100644 util/MP4/box_traf.cpp create mode 100644 util/MP4/box_trak.cpp create mode 100644 util/MP4/box_trex.cpp create mode 100644 util/MP4/box_trun.cpp create mode 100644 util/MP4/box_url.cpp create mode 100644 util/MP4/box_vmhd.cpp create mode 100644 util/MP4/interface.cpp create mode 100644 util/MP4/main.cpp diff --git a/util/MP4/Makefile b/util/MP4/Makefile new file mode 100644 index 00000000..4ff1d185 --- /dev/null +++ b/util/MP4/Makefile @@ -0,0 +1,18 @@ +SRC = box_abst.cpp box_afra.cpp box_afrt.cpp box_amhp.cpp box_asrt.cpp box_avcC.cpp box.cpp box_dinf.cpp box_dref.cpp box_esds.cpp box_ftyp.cpp box_hdlr.cpp box_hmhd.cpp box_mdat.cpp box_mdhd.cpp box_mdia.cpp box_mfhd.cpp box_minf.cpp box_moof.cpp box_moov.cpp box_mvex.cpp box_mvhd.cpp box_nmhd.cpp box_rtmp.cpp box_smhd.cpp box_stbl.cpp box_stco.cpp box_stsc.cpp box_stsd.cpp box_stts.cpp box_tfhd.cpp box_tkhd.cpp box_traf.cpp box_trak.cpp box_trex.cpp box_trun.cpp box_url.cpp box_vmhd.cpp interface.cpp main.cpp +OBJ = $(SRC:.cpp=.o) +OUT = Boxtest +INCLUDES = +CCFLAGS = -Wall -Wextra -funsigned-char -g +CC = $(CROSS)g++ +LD = $(CROSS)ld +AR = $(CROSS)ar +LIBS = +.SUFFIXES: .cpp +.PHONY: clean default +default: $(OUT) +.cpp.o: + $(CC) $(INCLUDES) $(CCFLAGS) $(LIBS) -c $< -o $@ +$(OUT): $(OBJ) + $(CC) $(LIBS) -o $(OUT) $(OBJ) +clean: + rm -rf $(OBJ) $(OUT) Makefile.bak *~ diff --git a/util/MP4/box.cpp b/util/MP4/box.cpp new file mode 100644 index 00000000..458ba15d --- /dev/null +++ b/util/MP4/box.cpp @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +struct BoxHeader { + uint32_t TotalSize; + uint32_t BoxType; +};//BoxHeader struct + +class Box { + public: + Box(); + Box(uint32_t BoxType); + ~Box(); + void SetBoxType(uint32_t BoxType); + uint32_t GetBoxType(); + void SetPayload(uint32_t Size, uint8_t * Data, uint32_t Index = 0); + uint32_t GetPayloadSize(); + uint8_t * GetPayload(); + uint8_t * GetPayload(uint32_t Index, uint32_t & Size); + uint32_t GetBoxedDataSize(); + uint8_t * GetBoxedData( ); + static uint8_t * uint32_to_uint8( uint32_t data ); + static uint8_t * uint16_to_uint8( uint16_t data ); + static uint8_t * uint8_to_uint8( uint8_t data ); + BoxHeader GetHeader( ); + void ResetPayload( ); + private: + BoxHeader header; + uint8_t * Payload; + uint32_t PayloadSize; +};//Box Class + +Box::Box() { + Payload = NULL; + PayloadSize = 0; +} + +Box::Box(uint32_t BoxType) { + header.BoxType = BoxType; + Payload = NULL; + PayloadSize = 0; +} + +Box::~Box() { +} + +void Box::SetBoxType(uint32_t BoxType) { + header.BoxType = BoxType; +} + +uint32_t Box::GetBoxType() { + return header.BoxType; +} + +void Box::SetPayload(uint32_t Size, uint8_t * Data, uint32_t Index) { + uint8_t * tempchar = NULL; + if ( Index + Size > PayloadSize ) { + if ( Payload ) { + tempchar = new uint8_t[PayloadSize]; + memcpy( tempchar, Payload, PayloadSize ); + delete Payload; + } + PayloadSize = Index + Size; + Payload = new uint8_t[PayloadSize]; + if( tempchar ) { + memcpy( Payload, tempchar, Index ); + } else { + for(uint32_t i = 0; i < Index; i++) { Payload[i] = 0; } + } + memcpy( &Payload[Index], Data, Size ); + header.TotalSize = PayloadSize + 8; + if( tempchar ) { + delete tempchar; + } + } else { + memcpy( &Payload[Index], Data, Size ); + } +} + +uint32_t Box::GetPayloadSize() { + return PayloadSize; +} + +uint8_t * Box::GetPayload() { + uint8_t * temp = new uint8_t[PayloadSize]; + memcpy( temp, Payload, PayloadSize ); + return temp; +} + +uint8_t * Box::GetPayload(uint32_t Index, uint32_t & Size) { + if(Index > PayloadSize) { return NULL; } + if(Index + Size > PayloadSize) { Size = PayloadSize - Index; } + uint8_t * temp = new uint8_t[Size - Index]; + memcpy( temp, &Payload[Index], Size - Index ); + return temp; +} + +uint32_t Box::GetBoxedDataSize() { + return header.TotalSize; +} + +uint8_t * Box::GetBoxedData( ) { + uint8_t * temp = new uint8_t[header.TotalSize]; + memcpy( temp, uint32_to_uint8(header.TotalSize), 4 ); + memcpy( &temp[4], uint32_to_uint8(header.BoxType), 4 ); + memcpy( &temp[8], Payload, PayloadSize ); + return temp; +} + + +uint8_t * Box::uint32_to_uint8( uint32_t data ) { + uint8_t * temp = new uint8_t[4]; + temp[0] = (data >> 24) & 0x000000FF; + temp[1] = (data >> 16 ) & 0x000000FF; + temp[2] = (data >> 8 ) & 0x000000FF; + temp[3] = (data ) & 0x000000FF; + return temp; +} + +uint8_t * Box::uint16_to_uint8( uint16_t data ) { + uint8_t * temp = new uint8_t[2]; + temp[0] = (data >> 8) & 0x00FF; + temp[1] = (data ) & 0x00FF; + return temp; +} + +uint8_t * Box::uint8_to_uint8( uint8_t data ) { + uint8_t * temp = new uint8_t[1]; + temp[0] = data; + return temp; +} + +BoxHeader Box::GetHeader( ) { + return header; +} + +void Box::ResetPayload( ) { + header.TotalSize -= PayloadSize; + PayloadSize = 0; + if(Payload) { + delete Payload; + Payload = NULL; + } +} diff --git a/util/MP4/box_abst.cpp b/util/MP4/box_abst.cpp new file mode 100644 index 00000000..be97f99a --- /dev/null +++ b/util/MP4/box_abst.cpp @@ -0,0 +1,218 @@ +#include "box.cpp" +#include +#include + +struct abst_serverentry { + std::string ServerBaseUrl; +};//abst_serverentry + +struct abst_qualityentry { + std::string QualityModifier; +};//abst_qualityentry + +class Box_abst { + public: + Box_abst( ); + ~Box_abst(); + Box * GetBox(); + void SetBootstrapVersion( uint32_t Version = 1 ); + void SetProfile( uint8_t Profile = 0 ); + void SetLive( bool Live = true ); + void SetUpdate( bool Update = false ); + void SetTimeScale( uint32_t Scale = 1000 ); + void SetMediaTime( uint32_t Time = 0 ); + void SetSMPTE( uint32_t Smpte = 0 ); + void SetMovieIdentifier( std::string Identifier = "" ); + void SetDRM( std::string Drm = "" ); + void SetMetaData( std::string MetaData = "" ); + void AddServerEntry( std::string Url = "", uint32_t Offset = 0 ); + void AddQualityEntry( std::string Quality = "", uint32_t Offset = 0 ); + void AddSegmentRunTable( Box * newSegment, uint32_t Offset = 0 ); + void AddFragmentRunTable( Box * newFragment, uint32_t Offset = 0 ); + void WriteContent( ); + private: + void SetDefaults( ); + void SetReserved( ); + uint32_t curBootstrapInfoVersion; + uint8_t curProfile; + bool isLive; + bool isUpdate; + uint32_t curTimeScale; + uint32_t curMediatime;//write as uint64_t + uint32_t curSMPTE;//write as uint64_t + std::string curMovieIdentifier; + std::string curDRM; + std::string curMetaData; + std::vector Servers; + std::vector Qualities; + std::vector SegmentRunTables; + std::vector FragmentRunTables; + Box * Container; +};//Box_ftyp Class + +Box_abst::Box_abst( ) { + Container = new Box( 0x61627374 ); +} + +Box_abst::~Box_abst() { + delete Container; +} + +Box * Box_abst::GetBox() { + return Container; +} + +void Box_abst::SetBootstrapVersion( uint32_t Version ) { + curBootstrapInfoVersion = Version; +} + +void Box_abst::SetProfile( uint8_t Profile ) { + curProfile = Profile; +} + +void Box_abst::SetLive( bool Live ) { + isLive = Live; +} + +void Box_abst::SetUpdate( bool Update ) { + isUpdate = Update; +} + +void Box_abst::SetTimeScale( uint32_t Scale ) { + curTimeScale = Scale; +} + +void Box_abst::SetMediaTime( uint32_t Time ) { + curMediatime = Time; +} + +void Box_abst::SetSMPTE( uint32_t Smpte ) { + curSMPTE = Smpte; +} + +void Box_abst::SetMovieIdentifier( std::string Identifier ) { + curMovieIdentifier = Identifier; +} + +void Box_abst::SetDRM( std::string Drm ) { + curDRM = Drm; +} + +void Box_abst::SetMetaData( std::string MetaData ) { + curMetaData = MetaData; +} + +void Box_abst::AddServerEntry( std::string Url, uint32_t Offset ) { + if(Offset >= Servers.size()) { + Servers.resize(Offset+1); + } + Servers[Offset].ServerBaseUrl = Url; +} + +void Box_abst::AddQualityEntry( std::string Quality, uint32_t Offset ) { + if(Offset >= Qualities.size()) { + Qualities.resize(Offset+1); + } + Qualities[Offset].QualityModifier = Quality; +} + +void Box_abst::AddSegmentRunTable( Box * newSegment, uint32_t Offset ) { + if( Offset >= SegmentRunTables.size() ) { + SegmentRunTables.resize(Offset+1); + } + if( SegmentRunTables[Offset] ) { + delete SegmentRunTables[Offset]; + } + SegmentRunTables[Offset] = newSegment; +} + +void Box_abst::AddFragmentRunTable( Box * newFragment, uint32_t Offset ) { + if( Offset >= FragmentRunTables.size() ) { + FragmentRunTables.resize(Offset+1); + } + if( FragmentRunTables[Offset] ) { + delete FragmentRunTables[Offset]; + } + FragmentRunTables[Offset] = newFragment; +} + + +void Box_abst::SetDefaults( ) { + SetProfile( ); + SetLive( ); + SetUpdate( ); + SetTimeScale( ); + SetMediaTime( ); + SetSMPTE( ); + SetMovieIdentifier( ); + SetDRM( ); + SetMetaData( ); +} + +void Box_abst::SetReserved( ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0)); +} + +void Box_abst::WriteContent( ) { + Box * current; + std::string serializedServers = ""; + std::string serializedQualities = ""; + std::string serializedSegments = ""; + std::string serializedFragments = ""; + int SegmentAmount = 0; + int FragmentAmount = 0; + uint8_t * temp = new uint8_t[1]; + + Container->ResetPayload( ); + SetReserved( ); + + for( uint32_t i = 0; i < Servers.size(); i++ ) { + serializedServers.append(Servers[i].ServerBaseUrl.c_str()); + serializedServers += '\0'; + } + for( uint32_t i = 0; i < Qualities.size(); i++ ) { + serializedQualities.append(Qualities[i].QualityModifier.c_str()); + serializedQualities += '\0'; + } + for( uint32_t i = 0; i < SegmentRunTables.size(); i++ ) { + current=SegmentRunTables[i]; + if( current ) { + SegmentAmount ++; + serializedSegments.append((char*)current->GetBoxedData(),current->GetBoxedDataSize()); + } + } + for( uint32_t i = 0; i < FragmentRunTables.size(); i++ ) { + current=FragmentRunTables[i]; + if( current ) { + FragmentAmount ++; + serializedFragments.append((char*)current->GetBoxedData(),current->GetBoxedDataSize()); + } + } + uint32_t OffsetServerEntryCount = 29 + curMovieIdentifier.size() + 1; + uint32_t OffsetQualityEntryCount = OffsetServerEntryCount + 4 + serializedServers.size(); + uint32_t OffsetDrmData = OffsetQualityEntryCount + 4 + serializedQualities.size(); + uint32_t OffsetMetaData = OffsetDrmData + curDRM.size() + 1; + uint32_t OffsetSegmentRuntableCount = OffsetMetaData + curMetaData.size() + 1; + uint32_t OffsetFragmentRuntableCount = OffsetSegmentRuntableCount + 4 + serializedSegments.size(); + + temp[0] = 0 & ( curProfile << 6 ) & ( (uint8_t)isLive << 7 ) & ( (uint8_t)isUpdate << 7 ); + + Container->SetPayload((uint32_t)serializedFragments.size(),(uint8_t*)serializedFragments.c_str(),OffsetFragmentRuntableCount+4); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(FragmentAmount),OffsetFragmentRuntableCount); + Container->SetPayload((uint32_t)serializedSegments.size(),(uint8_t*)serializedSegments.c_str(),OffsetSegmentRuntableCount+4); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(SegmentAmount),OffsetSegmentRuntableCount); + Container->SetPayload((uint32_t)curMetaData.size()+1,(uint8_t*)curMetaData.c_str(),OffsetMetaData); + Container->SetPayload((uint32_t)curDRM.size()+1,(uint8_t*)curDRM.c_str(),OffsetDrmData); + Container->SetPayload((uint32_t)serializedQualities.size(),(uint8_t*)serializedQualities.c_str(),OffsetQualityEntryCount+4); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Qualities.size()),OffsetQualityEntryCount); + Container->SetPayload((uint32_t)serializedServers.size(),(uint8_t*)serializedServers.c_str(),OffsetServerEntryCount+4); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Servers.size()),OffsetServerEntryCount); + Container->SetPayload((uint32_t)curMovieIdentifier.size()+1,(uint8_t*)curMovieIdentifier.c_str(),29);//+1 for \0-terminated string... + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(curSMPTE),25); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),21); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(curMediatime),17); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),13); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(curTimeScale),9); + Container->SetPayload((uint32_t)1,temp,8); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(curBootstrapInfoVersion),4); +} diff --git a/util/MP4/box_afra.cpp b/util/MP4/box_afra.cpp new file mode 100644 index 00000000..103c2c40 --- /dev/null +++ b/util/MP4/box_afra.cpp @@ -0,0 +1,75 @@ +#include "box.cpp" +#include + +struct afra_record { + uint32_t Time; + uint32_t Offset; +};//afra_record + +class Box_afra { + public: + Box_afra( ); + ~Box_afra(); + Box * GetBox(); + void SetTimeScale( uint32_t Scale = 1 ); + void AddEntry( uint32_t Time = 0, uint32_t SampleOffset = 0, uint32_t Offset = 0 ); + void WriteContent( ); + private: + void SetReserved( ); + void SetDefaults( ); + + Box * Container; + uint32_t CurrentTimeScale; + std::vector Entries; +};//Box_ftyp Class + +Box_afra::Box_afra( ) { + Container = new Box( 0x61667261 ); + SetReserved( ); + SetDefaults( ); +} + +Box_afra::~Box_afra() { + delete Container; +} + +Box * Box_afra::GetBox() { + return Container; +} + +void Box_afra::SetDefaults( ) { + SetTimeScale( ); +} + +void Box_afra::SetReserved( ) { + uint8_t * temp = new uint8_t[1]; + temp[0] = 0; + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),1); + Container->SetPayload((uint32_t)1,temp); +} + +void Box_afra::SetTimeScale( uint32_t Scale ) { + CurrentTimeScale = Scale; + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Scale),5); +} + +void Box_afra::AddEntry( uint32_t Time, uint32_t SampleOffset, uint32_t Offset ) { + if(Offset >= Entries.size()) { + Entries.resize(Offset+1); + } + Entries[Offset].Time = Time; + Entries[Offset].Offset = SampleOffset; +} + +void Box_afra::WriteContent( ) { + Container->ResetPayload(); + SetReserved( ); + if(!Entries.empty()) { + for(int32_t i = Entries.size() -1; i >= 0; i--) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Entries[i].Offset),(i*12)+21); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Entries[i].Time),(i*12)+17); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),(i*12)+13); + } + } + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Entries.size()),9); +} diff --git a/util/MP4/box_afrt.cpp b/util/MP4/box_afrt.cpp new file mode 100644 index 00000000..67365d7e --- /dev/null +++ b/util/MP4/box_afrt.cpp @@ -0,0 +1,100 @@ +#include "box.cpp" +#include +#include + +struct afrt_fragmentrunentry { + uint32_t FirstFragment; + uint32_t FirstFragmentTimestamp; //write as uint64_t + uint32_t FragmentDuration; + uint8_t DiscontinuityIndicator;//if FragmentDuration == 0 +};//afrt_fragmentrunentry + +class Box_afrt { + public: + Box_afrt( ); + ~Box_afrt(); + Box * GetBox(); + void SetUpdate( bool Update = false ); + void SetTimeScale( uint32_t Scale = 1000 ); + void AddQualityEntry( std::string Quality = "", uint32_t Offset = 0 ); + void AddFragmentRunEntry( uint32_t FirstFragment = 0, uint32_t FirstFragmentTimestamp = 0, uint32_t FragmentsDuration = 1, uint8_t Discontinuity = 0, uint32_t Offset = 0 ); + void WriteContent( ); + private: + void SetDefaults( ); + bool isUpdate; + uint32_t curTimeScale; + std::vector QualitySegmentUrlModifiers; + std::vector FragmentRunEntryTable; + Box * Container; +};//Box_ftyp Class + +Box_afrt::Box_afrt( ) { + Container = new Box( 0x61667274 ); +} + +Box_afrt::~Box_afrt() { + delete Container; +} + +Box * Box_afrt::GetBox() { + return Container; +} + +void Box_afrt::SetUpdate( bool Update ) { + isUpdate = Update; +} + +void Box_afrt::AddQualityEntry( std::string Quality, uint32_t Offset ) { + if(Offset >= QualitySegmentUrlModifiers.size()) { + QualitySegmentUrlModifiers.resize(Offset+1); + } + QualitySegmentUrlModifiers[Offset] = Quality; +} + +void Box_afrt::AddFragmentRunEntry( uint32_t FirstFragment, uint32_t FirstFragmentTimestamp, uint32_t FragmentsDuration, uint8_t Discontinuity, uint32_t Offset ) { + if( Offset >= FragmentRunEntryTable.size() ) { + FragmentRunEntryTable.resize(Offset+1); + } + FragmentRunEntryTable[Offset].FirstFragment = FirstFragment; + FragmentRunEntryTable[Offset].FirstFragmentTimestamp = FirstFragmentTimestamp; + FragmentRunEntryTable[Offset].FragmentDuration = FragmentsDuration; + FragmentRunEntryTable[Offset].DiscontinuityIndicator = Discontinuity; +} + +void Box_afrt::SetDefaults( ) { + SetUpdate( ); + SetTimeScale( ); +} + +void Box_afrt::SetTimeScale( uint32_t Scale ) { + curTimeScale = Scale; +} + +void Box_afrt::WriteContent( ) { + std::string serializedQualities = ""; + std::string serializedFragmentEntries = ""; + Container->ResetPayload( ); + + for( uint32_t i = 0; i < QualitySegmentUrlModifiers.size(); i++ ) { + serializedQualities.append(QualitySegmentUrlModifiers[i].c_str()); + serializedQualities += '\0'; + } + for( uint32_t i = 0; i < FragmentRunEntryTable.size(); i ++ ) { + serializedFragmentEntries.append((char*)Box::uint32_to_uint8(FragmentRunEntryTable[i].FirstFragment)); + serializedFragmentEntries.append((char*)Box::uint32_to_uint8(0)); + serializedFragmentEntries.append((char*)Box::uint32_to_uint8(FragmentRunEntryTable[i].FirstFragmentTimestamp)); + serializedFragmentEntries.append((char*)Box::uint32_to_uint8(FragmentRunEntryTable[i].FragmentDuration)); + if(FragmentRunEntryTable[i].FragmentDuration == 0) { + serializedFragmentEntries.append((char*)Box::uint8_to_uint8(FragmentRunEntryTable[i].DiscontinuityIndicator)); + } + } + + uint32_t OffsetFragmentRunEntryCount = 9 + serializedQualities.size(); + + Container->SetPayload((uint32_t)serializedFragmentEntries.size(),(uint8_t*)serializedFragmentEntries.c_str(),OffsetFragmentRunEntryCount+4); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(FragmentRunEntryTable.size()),OffsetFragmentRunEntryCount); + Container->SetPayload((uint32_t)serializedQualities.size(),(uint8_t*)serializedQualities.c_str(),9); + Container->SetPayload((uint32_t)1,Box::uint8_to_uint8(QualitySegmentUrlModifiers.size()),8); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(curTimeScale)); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8((isUpdate ? 1 : 0))); +} diff --git a/util/MP4/box_amhp.cpp b/util/MP4/box_amhp.cpp new file mode 100644 index 00000000..761da82a --- /dev/null +++ b/util/MP4/box_amhp.cpp @@ -0,0 +1,63 @@ +#include "box.cpp" +#include +#include + +struct amhp_record { + uint8_t HintTrackMode; + uint8_t Settings; + uint8_t TrailerDefaultSize; +};//stsc_record + +class Box_amhp { + public: + Box_amhp( ); + ~Box_amhp(); + Box * GetBox(); + void SetReserved( ); + void AddEntry( uint8_t HintTrackMode, uint8_t Settings, uint8_t TrailerDefaultSize, uint32_t Offset = 0 ); + void WriteContent( ); + private: + Box * Container; + + std::vector Entries; +};//Box_ftyp Class + +Box_amhp::Box_amhp( ) { + Container = new Box( 0x616D6870 ); + SetReserved(); +} + +Box_amhp::~Box_amhp() { + delete Container; +} + +Box * Box_amhp::GetBox() { + return Container; +} + +void Box_amhp::SetReserved( ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0)); +} + +void Box_amhp::AddEntry( uint8_t HintTrackMode, uint8_t Settings, uint8_t TrailerDefaultSize, uint32_t Offset ) { + if(Offset >= Entries.size()) { + Entries.resize(Offset+1); + } + Entries[Offset].HintTrackMode = HintTrackMode; + Entries[Offset].Settings = Settings; + Entries[Offset].TrailerDefaultSize = TrailerDefaultSize; +} + + +void Box_amhp::WriteContent( ) { + Container->ResetPayload(); + SetReserved( ); + if(!Entries.empty()) { + for(int32_t i = Entries.size() -1; i >= 0; i--) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Entries[i].TrailerDefaultSize),(i*12)+16); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Entries[i].Settings),(i*12)+12); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Entries[i].HintTrackMode),(i*12)+8); + } + } + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Entries.size()),4); +} diff --git a/util/MP4/box_asrt.cpp b/util/MP4/box_asrt.cpp new file mode 100644 index 00000000..a6ba2fbb --- /dev/null +++ b/util/MP4/box_asrt.cpp @@ -0,0 +1,81 @@ +#include "box.cpp" +#include +#include + +struct asrt_segmentrunentry { + uint32_t FirstSegment; + uint32_t FragmentsPerSegment; +};//abst_qualityentry + +class Box_asrt { + public: + Box_asrt( ); + ~Box_asrt(); + Box * GetBox(); + void SetUpdate( bool Update = false ); + void AddQualityEntry( std::string Quality = "", uint32_t Offset = 0 ); + void AddSegmentRunEntry( uint32_t FirstSegment = 0, uint32_t FragmentsPerSegment = 100, uint32_t Offset = 0 ); + void WriteContent( ); + private: + void SetDefaults( ); + bool isUpdate; + std::vector QualitySegmentUrlModifiers; + std::vector SegmentRunEntryTable; + Box * Container; +};//Box_ftyp Class + +Box_asrt::Box_asrt( ) { + Container = new Box( 0x61737274 ); +} + +Box_asrt::~Box_asrt() { + delete Container; +} + +Box * Box_asrt::GetBox() { + return Container; +} + +void Box_asrt::SetUpdate( bool Update ) { + isUpdate = Update; +} + +void Box_asrt::AddQualityEntry( std::string Quality, uint32_t Offset ) { + if(Offset >= QualitySegmentUrlModifiers.size()) { + QualitySegmentUrlModifiers.resize(Offset+1); + } + QualitySegmentUrlModifiers[Offset] = Quality; +} + +void Box_asrt::AddSegmentRunEntry( uint32_t FirstSegment, uint32_t FragmentsPerSegment, uint32_t Offset ) { + if( Offset >= SegmentRunEntryTable.size() ) { + SegmentRunEntryTable.resize(Offset+1); + } + SegmentRunEntryTable[Offset].FirstSegment = FirstSegment; + SegmentRunEntryTable[Offset].FragmentsPerSegment = FragmentsPerSegment; +} + +void Box_asrt::SetDefaults( ) { + SetUpdate( ); +} + +void Box_asrt::WriteContent( ) { + std::string serializedQualities = ""; + Container->ResetPayload( ); + + for( uint32_t i = 0; i < QualitySegmentUrlModifiers.size(); i++ ) { + serializedQualities.append(QualitySegmentUrlModifiers[i].c_str()); + serializedQualities += '\0'; + } + + uint32_t OffsetSegmentRunEntryCount = 5 + serializedQualities.size(); + + for( uint32_t i = 0; i < SegmentRunEntryTable.size(); i ++ ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(SegmentRunEntryTable[i].FragmentsPerSegment),(8*i)+OffsetSegmentRunEntryCount+8); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(SegmentRunEntryTable[i].FirstSegment),(8*i)+OffsetSegmentRunEntryCount+4); + } + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(SegmentRunEntryTable.size()),OffsetSegmentRunEntryCount); + Container->SetPayload((uint32_t)serializedQualities.size(),(uint8_t*)serializedQualities.c_str(),5); + Container->SetPayload((uint32_t)1,Box::uint8_to_uint8(QualitySegmentUrlModifiers.size()),4); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8((isUpdate ? 1 : 0))); +} diff --git a/util/MP4/box_avcC.cpp b/util/MP4/box_avcC.cpp new file mode 100644 index 00000000..90bc6fe0 --- /dev/null +++ b/util/MP4/box_avcC.cpp @@ -0,0 +1,86 @@ +#include "box.cpp" +#include + +class Box_avcC { + public: + Box_avcC( ); + ~Box_avcC(); + Box * GetBox(); + void SetDataReferenceIndex( uint16_t DataReferenceIndex = 1 ); + void SetWidth( uint16_t Width = 0 ); + void SetHeight( uint16_t Height = 0 ); + void SetResolution ( uint32_t Horizontal = 0x00480000, uint32_t Vertical = 0x00480000 ); + void SetFrameCount ( uint16_t FrameCount = 1 ); + void SetCompressorName ( std::string CompressorName = ""); + void SetDepth ( uint16_t Depth = 0x0018 ); + private: + Box * Container; + + void SetReserved( ); + void SetDefaults( ); +};//Box_ftyp Class + +Box_avcC::Box_avcC( ) { + Container = new Box( 0x61766343 ); + SetReserved(); + SetDefaults(); +} + +Box_avcC::~Box_avcC() { + delete Container; +} + +Box * Box_avcC::GetBox() { + return Container; +} + +void Box_avcC::SetDataReferenceIndex( uint16_t DataReferenceIndex ) { + Container->SetPayload((uint32_t)2,Box::uint16_to_uint8( DataReferenceIndex ),6); +} + +void Box_avcC::SetWidth( uint16_t Width ) { + Container->SetPayload((uint32_t)2,Box::uint16_to_uint8( Width ),24); +} + +void Box_avcC::SetHeight( uint16_t Height ) { + Container->SetPayload((uint32_t)2,Box::uint16_to_uint8( Height ),26); +} + +void Box_avcC::SetResolution ( uint32_t Horizontal, uint32_t Vertical ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8( Vertical ),32); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8( Horizontal ),28); +} + +void Box_avcC::SetFrameCount ( uint16_t FrameCount ) { + Container->SetPayload((uint32_t)2,Box::uint16_to_uint8( FrameCount ),40); +} + +void Box_avcC::SetCompressorName ( std::string CompressorName ) { + uint8_t * Printable = new uint8_t[1]; + Printable[0] = std::min( (unsigned int)31, CompressorName.size() ); + Container->SetPayload((uint32_t)Printable[0],(uint8_t*)CompressorName.c_str(),43); + Container->SetPayload((uint32_t)1, Printable ,42); +} + +void Box_avcC::SetDepth ( uint16_t Depth ) { + Container->SetPayload((uint32_t)2,Box::uint16_to_uint8( Depth ),74); +} + +void Box_avcC::SetReserved( ) { + Container->SetPayload((uint32_t)2,Box::uint16_to_uint8( (uint16_t)-1 ),76); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),36); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),20); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),16); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),12); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),8); + Container->SetPayload((uint32_t)4,Box::uint16_to_uint8(0),4); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0)); +} + +void Box_avcC::SetDefaults( ) { + SetWidth( ); + SetHeight( ); + SetDepth ( ); + SetFrameCount ( ); + SetResolution ( ); +} diff --git a/util/MP4/box_dinf.cpp b/util/MP4/box_dinf.cpp new file mode 100644 index 00000000..d8d4d243 --- /dev/null +++ b/util/MP4/box_dinf.cpp @@ -0,0 +1,43 @@ +#include "box.cpp" +#include +#include + +class Box_dinf { + public: + Box_dinf(); + ~Box_dinf(); + Box * GetBox(); + void AddContent( Box * newcontent ); + void WriteContent( ); + private: + Box * Container; + + Box * Content; +};//Box_ftyp Class + +Box_dinf::Box_dinf( ) { + Container = new Box( 0x64696E66 ); +} + +Box_dinf::~Box_dinf() { + delete Container; +} + +Box * Box_dinf::GetBox() { + return Container; +} + +void Box_dinf::AddContent( Box * newcontent ) { + if(Content) { + delete Content; + Content = NULL; + } + Content = newcontent; +} + +void Box_dinf::WriteContent( ) { + Container->ResetPayload( ); + std::string serializedbox = ""; + serializedbox.append((char*)Content->GetBoxedData(),Content->GetBoxedDataSize()); + Container->SetPayload((uint32_t)serializedbox.size(),(uint8_t*)serializedbox.c_str()); +} diff --git a/util/MP4/box_dref.cpp b/util/MP4/box_dref.cpp new file mode 100644 index 00000000..cc6a2f5a --- /dev/null +++ b/util/MP4/box_dref.cpp @@ -0,0 +1,58 @@ +#include "box.cpp" +#include +#include + +class Box_dref { + public: + Box_dref(); + ~Box_dref(); + Box * GetBox(); + void AddContent( Box * newcontent, uint32_t offset = 0 ); + void WriteContent( ); + private: + Box * Container; + + void SetReserved( ); + std::vector Content; +};//Box_ftyp Class + +Box_dref::Box_dref( ) { + Container = new Box( 0x64726566 ); + SetReserved( ); +} + +Box_dref::~Box_dref() { + delete Container; +} + +Box * Box_dref::GetBox() { + return Container; +} + +void Box_dref::AddContent( Box * newcontent, uint32_t offset ) { + if( offset >= Content.size() ) { + Content.resize(offset+1); + } + if( Content[offset] ) { + delete Content[offset]; + } + Content[offset] = newcontent; +} + +void Box_dref::WriteContent( ) { + Container->ResetPayload( ); + Box * current; + std::string serializedbox = ""; + for( uint32_t i = 0; i < Content.size(); i++ ) { + current=Content[i]; + if( current ) { + serializedbox.append((char*)current->GetBoxedData(),current->GetBoxedDataSize()); + } + } + Container->SetPayload((uint32_t)serializedbox.size(),(uint8_t*)serializedbox.c_str(),4); + SetReserved( ); +} + +void Box_dref::SetReserved( ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0)); +} diff --git a/util/MP4/box_esds.cpp b/util/MP4/box_esds.cpp new file mode 100644 index 00000000..7253f07d --- /dev/null +++ b/util/MP4/box_esds.cpp @@ -0,0 +1,55 @@ +#include "box.cpp" +#include + +class Box_esds { + public: + Box_esds( ); + ~Box_esds(); + Box * GetBox(); + void SetDataReferenceIndex( uint16_t DataReferenceIndex = 1); + void SetChannelCount( uint16_t Count = 2 ); + void SetSampleSize( uint16_t Size = 16 ); + private: + Box * Container; + + void SetReserved( ); + void SetDefaults( ); +};//Box_ftyp Class + +Box_esds::Box_esds( ) { + Container = new Box( 0x65736473 ); + SetReserved(); + SetDefaults(); +} + +Box_esds::~Box_esds() { + delete Container; +} + +Box * Box_esds::GetBox() { + return Container; +} + +void Box_esds::SetDataReferenceIndex( uint16_t DataReferenceIndex ) { + Container->SetPayload((uint32_t)2,Box::uint16_to_uint8( DataReferenceIndex ),6); +} + +void Box_esds::SetChannelCount( uint16_t Count ) { + Container->SetPayload((uint32_t)2,Box::uint16_to_uint8( Count ),16); +} + +void Box_esds::SetSampleSize( uint16_t Size ) { + Container->SetPayload((uint32_t)2,Box::uint16_to_uint8( Size ),18); +} + +void Box_esds::SetReserved( ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8( 0 ),20); + Container->SetPayload((uint32_t)2,Box::uint16_to_uint8( 0 ),4); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8( 0 )); +} + +void Box_esds::SetDefaults( ) { + SetSampleSize( ); + SetChannelCount( ); + SetDataReferenceIndex( ); +} diff --git a/util/MP4/box_ftyp.cpp b/util/MP4/box_ftyp.cpp new file mode 100644 index 00000000..97ba9c3a --- /dev/null +++ b/util/MP4/box_ftyp.cpp @@ -0,0 +1,39 @@ +#include "box.cpp" + +class Box_ftyp { + public: + Box_ftyp( ); + ~Box_ftyp(); + Box * GetBox(); + void SetMajorBrand( uint32_t MajorBrand = 0x66347620 ); + void SetMinorBrand( uint32_t MinorBrand = 0x1 ); + private: + void SetDefaults( ); + Box * Container; +};//Box_ftyp Class + +Box_ftyp::Box_ftyp( ) { + Container = new Box( 0x66747970 ); + SetDefaults( ); +} + +Box_ftyp::~Box_ftyp() { + delete Container; +} + +Box * Box_ftyp::GetBox() { + return Container; +} + +void Box_ftyp::SetMajorBrand( uint32_t MajorBrand ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(MajorBrand)); +} + +void Box_ftyp::SetMinorBrand( uint32_t MinorBrand ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(MinorBrand),4); +} + +void Box_ftyp::SetDefaults( ) { + SetMinorBrand( ); + SetMajorBrand( ); +} diff --git a/util/MP4/box_hdlr.cpp b/util/MP4/box_hdlr.cpp new file mode 100644 index 00000000..5926740c --- /dev/null +++ b/util/MP4/box_hdlr.cpp @@ -0,0 +1,59 @@ +#include "box.cpp" +#include + +class Box_hdlr { + public: + Box_hdlr( ); + ~Box_hdlr(); + Box * GetBox(); + void SetHandlerType( uint32_t HandlerType = 0 ); + void SetName ( std::string Name = "" ); + private: + Box * Container; + void SetReserved( ); + void SetDefaults( ); + uint32_t CurrentHandlerType; +};//Box_ftyp Class + +Box_hdlr::Box_hdlr( ) { + Container = new Box( 0x68646C72 ); + CurrentHandlerType = 0; + SetReserved(); +} + +Box_hdlr::~Box_hdlr() { + delete Container; +} + +Box * Box_hdlr::GetBox() { + return Container; +} + +void Box_hdlr::SetReserved( ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),20); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),16); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),12); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),4); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0)); +} + +void Box_hdlr::SetHandlerType( uint32_t HandlerType ) { + if( HandlerType != 0 ) { + CurrentHandlerType = HandlerType; + } + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(CurrentHandlerType),8); +} + +void Box_hdlr::SetName ( std::string Name ) { + char * tmp = new char[Name.size()+1]; + strcpy(tmp,Name.c_str()); + Container->ResetPayload(); + SetReserved(); + SetHandlerType(0); + Container->SetPayload((uint32_t)strlen(tmp)+1,(uint8_t*)tmp,24); +} + +void Box_hdlr::SetDefaults( ) { + SetName( ); + SetHandlerType( ); +} diff --git a/util/MP4/box_hmhd.cpp b/util/MP4/box_hmhd.cpp new file mode 100644 index 00000000..8733b822 --- /dev/null +++ b/util/MP4/box_hmhd.cpp @@ -0,0 +1,57 @@ +#include "box.cpp" + +class Box_hmhd { + public: + Box_hmhd( ); + ~Box_hmhd(); + Box * GetBox(); + void SetMaxPDUSize( uint16_t Size = 0 ); + void SetAvgPDUSize( uint16_t Size = 0 ); + void SetMaxBitRate( uint32_t Rate = 0 ); + void SetAvgBitRate( uint32_t Rate = 0 ); + private: + Box * Container; + void SetReserved( ); + void SetDefaults( ); +};//Box_ftyp Class + +Box_hmhd::Box_hmhd( ) { + Container = new Box( 0x686D6864 ); + SetDefaults(); + SetReserved(); +} + +Box_hmhd::~Box_hmhd() { + delete Container; +} + +Box * Box_hmhd::GetBox() { + return Container; +} + +void Box_hmhd::SetMaxPDUSize( uint16_t Size ) { + Container->SetPayload((uint32_t)2,Box::uint16_to_uint8(Size),4); +} + +void Box_hmhd::SetAvgPDUSize( uint16_t Size ) { + Container->SetPayload((uint32_t)2,Box::uint16_to_uint8(Size),6); +} + +void Box_hmhd::SetMaxBitRate( uint32_t Rate ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Rate),8); +} + +void Box_hmhd::SetAvgBitRate( uint32_t Rate ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Rate),12); +} + +void Box_hmhd::SetReserved( ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),16); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0)); +} +void Box_hmhd::SetDefaults( ) { + SetAvgBitRate( ); + SetMaxBitRate( ); + SetAvgPDUSize( ); + SetMaxPDUSize( ); +} diff --git a/util/MP4/box_includes.h b/util/MP4/box_includes.h new file mode 100644 index 00000000..2b796702 --- /dev/null +++ b/util/MP4/box_includes.h @@ -0,0 +1,37 @@ +#include "box_abst.cpp" +#include "box_afra.cpp" +#include "box_afrt.cpp" +#include "box_amhp.cpp" +#include "box_asrt.cpp" +#include "box_avcC.cpp" +#include "box_dinf.cpp" +#include "box_dref.cpp" +#include "box_esds.cpp" +#include "box_ftyp.cpp" +#include "box_hdlr.cpp" +#include "box_hmhd.cpp" +#include "box_mdat.cpp" +#include "box_mdhd.cpp" +#include "box_mdia.cpp" +#include "box_mfhd.cpp" +#include "box_minf.cpp" +#include "box_moof.cpp" +#include "box_moov.cpp" +#include "box_mvex.cpp" +#include "box_mvhd.cpp" +#include "box_nmhd.cpp" +#include "box_rtmp.cpp" +#include "box_smhd.cpp" +#include "box_stbl.cpp" +#include "box_stco.cpp" +#include "box_stsc.cpp" +#include "box_stsd.cpp" +#include "box_stts.cpp" +#include "box_tfhd.cpp" +#include "box_tkhd.cpp" +#include "box_traf.cpp" +#include "box_trak.cpp" +#include "box_trex.cpp" +#include "box_trun.cpp" +#include "box_url.cpp" +#include "box_vmhd.cpp" diff --git a/util/MP4/box_mdat.cpp b/util/MP4/box_mdat.cpp new file mode 100644 index 00000000..258585aa --- /dev/null +++ b/util/MP4/box_mdat.cpp @@ -0,0 +1,29 @@ +#include "box.cpp" +#include +#include + +class Box_mdat { + public: + Box_mdat(); + ~Box_mdat(); + Box * GetBox(); + void SetContent( uint8_t * NewData, uint32_t DataLength , uint32_t offset = 0 ); + private: + Box * Container; +};//Box_ftyp Class + +Box_mdat::Box_mdat( ) { + Container = new Box( 0x6D646174 ); +} + +Box_mdat::~Box_mdat() { + delete Container; +} + +Box * Box_mdat::GetBox() { + return Container; +} + +void Box_mdat::SetContent( uint8_t * NewData, uint32_t DataLength , uint32_t Offset ) { + Container->SetPayload(DataLength,NewData,Offset); +} diff --git a/util/MP4/box_mdhd.cpp b/util/MP4/box_mdhd.cpp new file mode 100644 index 00000000..ed143270 --- /dev/null +++ b/util/MP4/box_mdhd.cpp @@ -0,0 +1,88 @@ +#include "box.cpp" +#include + +#define SECONDS_DIFFERENCE 2082844800 + +class Box_mdhd { + public: + Box_mdhd( ); + ~Box_mdhd(); + Box * GetBox(); + void SetCreationTime( uint32_t TimeStamp = 0 ); + void SetModificationTime( uint32_t TimeStamp = 0 ); + void SetTimeScale( uint32_t TimeUnits = 0 ); + void SetDurationTime( uint32_t TimeUnits = 0 ); + void SetLanguage( uint8_t Firstchar = 'n', uint8_t Secondchar = 'l', uint8_t Thirdchar = 'd' ); + private: + void SetReserved(); + void SetDefaults(); + Box * Container; +};//Box_ftyp Class + +Box_mdhd::Box_mdhd( ) { + Container = new Box( 0x6D646864 ); +} + +Box_mdhd::~Box_mdhd() { + delete Container; +} + +Box * Box_mdhd::GetBox() { + return Container; +} + +void Box_mdhd::SetCreationTime( uint32_t TimeStamp ) { + uint32_t CreationTime; + if(!TimeStamp) { + CreationTime = time(NULL) + SECONDS_DIFFERENCE; + } else { + CreationTime = TimeStamp; + } + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(CreationTime),4); +} + +void Box_mdhd::SetModificationTime( uint32_t TimeStamp ) { + uint32_t ModificationTime; + if(!TimeStamp) { + ModificationTime = time(NULL) + SECONDS_DIFFERENCE; + } else { + ModificationTime = TimeStamp; + } + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(ModificationTime),8); +} + +void Box_mdhd::SetTimeScale( uint32_t TimeUnits ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(TimeUnits),12); +} + +void Box_mdhd::SetDurationTime( uint32_t TimeUnits ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(TimeUnits),16); +} + +void Box_mdhd::SetLanguage( uint8_t Firstchar, uint8_t Secondchar, uint8_t Thirdchar ) { + uint8_t FirstByte = 0; + uint8_t SecondByte = 0; + Firstchar -= 0x60; + Secondchar -= 0x60; + Thirdchar -= 0x60; + FirstByte += (Firstchar << 2); + FirstByte += (Secondchar >> 3); + SecondByte += (Secondchar << 5); + SecondByte += Thirdchar; + + Container->SetPayload((uint32_t)1,&SecondByte,21); + Container->SetPayload((uint32_t)1,&FirstByte,20); +} + +void Box_mdhd::SetReserved() { + Container->SetPayload((uint32_t)2,Box::uint16_to_uint8(0),22); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0)); +} + +void Box_mdhd::SetDefaults() { + SetLanguage(); + SetDurationTime(); + SetTimeScale(); + SetModificationTime(); + SetCreationTime(); +} diff --git a/util/MP4/box_mdia.cpp b/util/MP4/box_mdia.cpp new file mode 100644 index 00000000..553137a7 --- /dev/null +++ b/util/MP4/box_mdia.cpp @@ -0,0 +1,51 @@ +#include "box.cpp" +#include +#include + +class Box_mdia { + public: + Box_mdia(); + ~Box_mdia(); + Box * GetBox(); + void AddContent( Box * newcontent, uint32_t offset = 0 ); + void WriteContent( ); + private: + Box * Container; + + std::vector Content; +};//Box_ftyp Class + +Box_mdia::Box_mdia( ) { + Container = new Box( 0x6D646961 ); +} + +Box_mdia::~Box_mdia() { + delete Container; +} + +Box * Box_mdia::GetBox() { + return Container; +} + +void Box_mdia::AddContent( Box * newcontent, uint32_t offset ) { + if( offset >= Content.size() ) { + Content.resize(offset+1); + } + if( Content[offset] ) { + delete Content[offset]; + } + Content[offset] = newcontent; +} + +void Box_mdia::WriteContent( ) { + Container->ResetPayload( ); + Box * current; + std::string serializedbox = ""; + for( uint32_t i = 0; i < Content.size(); i++ ) { + current=Content[i]; + if( current ) { + serializedbox.append((char*)current->GetBoxedData(),current->GetBoxedDataSize()); + } + } + Container->SetPayload((uint32_t)serializedbox.size(),(uint8_t*)serializedbox.c_str()); +} diff --git a/util/MP4/box_mfhd.cpp b/util/MP4/box_mfhd.cpp new file mode 100644 index 00000000..2680ab0a --- /dev/null +++ b/util/MP4/box_mfhd.cpp @@ -0,0 +1,39 @@ +#include "box.cpp" + +class Box_mfhd { + public: + Box_mfhd( ); + ~Box_mfhd(); + Box * GetBox(); + void SetSequenceNumber( uint32_t SequenceNumber = 1 ); + private: + void SetDefaults( ); + void SetReserved( ); + Box * Container; +};//Box_ftyp Class + +Box_mfhd::Box_mfhd( ) { + Container = new Box( 0x6D666864 ); + SetDefaults( ); + SetReserved( ); +} + +Box_mfhd::~Box_mfhd() { + delete Container; +} + +Box * Box_mfhd::GetBox() { + return Container; +} + +void Box_mfhd::SetDefaults( ) { + SetSequenceNumber( ); +} + +void Box_mfhd::SetReserved( ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0)); +} + +void Box_mfhd::SetSequenceNumber( uint32_t SequenceNumber ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(SequenceNumber),4); +} diff --git a/util/MP4/box_minf.cpp b/util/MP4/box_minf.cpp new file mode 100644 index 00000000..ebde6c56 --- /dev/null +++ b/util/MP4/box_minf.cpp @@ -0,0 +1,51 @@ +#include "box.cpp" +#include +#include + +class Box_minf { + public: + Box_minf(); + ~Box_minf(); + Box * GetBox(); + void AddContent( Box * newcontent, uint32_t offset = 0 ); + void WriteContent( ); + private: + Box * Container; + + std::vector Content; +};//Box_ftyp Class + +Box_minf::Box_minf( ) { + Container = new Box( 0x6D696E66 ); +} + +Box_minf::~Box_minf() { + delete Container; +} + +Box * Box_minf::GetBox() { + return Container; +} + +void Box_minf::AddContent( Box * newcontent, uint32_t offset ) { + if( offset >= Content.size() ) { + Content.resize(offset+1); + } + if( Content[offset] ) { + delete Content[offset]; + } + Content[offset] = newcontent; +} + +void Box_minf::WriteContent( ) { + Container->ResetPayload( ); + Box * current; + std::string serializedbox = ""; + for( uint32_t i = 0; i < Content.size(); i++ ) { + current=Content[i]; + if( current ) { + serializedbox.append((char*)current->GetBoxedData(),current->GetBoxedDataSize()); + } + } + Container->SetPayload((uint32_t)serializedbox.size(),(uint8_t*)serializedbox.c_str()); +} diff --git a/util/MP4/box_moof.cpp b/util/MP4/box_moof.cpp new file mode 100644 index 00000000..1b18a9e5 --- /dev/null +++ b/util/MP4/box_moof.cpp @@ -0,0 +1,51 @@ +#include "box.cpp" +#include +#include + +class Box_moof { + public: + Box_moof(); + ~Box_moof(); + Box * GetBox(); + void AddContent( Box * newcontent, uint32_t offset = 0 ); + void WriteContent( ); + private: + Box * Container; + + std::vector Content; +};//Box_ftyp Class + +Box_moof::Box_moof( ) { + Container = new Box( 0x6D6F6F66 ); +} + +Box_moof::~Box_moof() { + delete Container; +} + +Box * Box_moof::GetBox() { + return Container; +} + +void Box_moof::AddContent( Box * newcontent, uint32_t offset ) { + if( offset >= Content.size() ) { + Content.resize(offset+1); + } + if( Content[offset] ) { + delete Content[offset]; + } + Content[offset] = newcontent; +} + +void Box_moof::WriteContent( ) { + Container->ResetPayload( ); + Box * current; + std::string serializedbox = ""; + for( uint32_t i = 0; i < Content.size(); i++ ) { + current=Content[i]; + if( current ) { + serializedbox.append((char*)current->GetBoxedData(),current->GetBoxedDataSize()); + } + } + Container->SetPayload((uint32_t)serializedbox.size(),(uint8_t*)serializedbox.c_str()); +} diff --git a/util/MP4/box_moov.cpp b/util/MP4/box_moov.cpp new file mode 100644 index 00000000..84542977 --- /dev/null +++ b/util/MP4/box_moov.cpp @@ -0,0 +1,51 @@ +#include "box.cpp" +#include +#include + +class Box_moov { + public: + Box_moov(); + ~Box_moov(); + Box * GetBox(); + void AddContent( Box * newcontent, uint32_t offset = 0 ); + void WriteContent( ); + private: + Box * Container; + + std::vector Content; +};//Box_ftyp Class + +Box_moov::Box_moov( ) { + Container = new Box( 0x6D6F6F76 ); +} + +Box_moov::~Box_moov() { + delete Container; +} + +Box * Box_moov::GetBox() { + return Container; +} + +void Box_moov::AddContent( Box * newcontent, uint32_t offset ) { + if( offset >= Content.size() ) { + Content.resize(offset+1); + } + if( Content[offset] ) { + delete Content[offset]; + } + Content[offset] = newcontent; +} + +void Box_moov::WriteContent( ) { + Container->ResetPayload( ); + Box * current; + std::string serializedbox = ""; + for( uint32_t i = 0; i < Content.size(); i++ ) { + current=Content[i]; + if( current ) { + serializedbox.append((char*)current->GetBoxedData(),current->GetBoxedDataSize()); + } + } + Container->SetPayload((uint32_t)serializedbox.size(),(uint8_t*)serializedbox.c_str()); +} diff --git a/util/MP4/box_mvex.cpp b/util/MP4/box_mvex.cpp new file mode 100644 index 00000000..8d6725ac --- /dev/null +++ b/util/MP4/box_mvex.cpp @@ -0,0 +1,51 @@ +#include "box.cpp" +#include +#include + +class Box_mvex { + public: + Box_mvex(); + ~Box_mvex(); + Box * GetBox(); + void AddContent( Box * newcontent, uint32_t offset = 0 ); + void WriteContent( ); + private: + Box * Container; + + std::vector Content; +};//Box_ftyp Class + +Box_mvex::Box_mvex( ) { + Container = new Box( 0x6D866578 ); +} + +Box_mvex::~Box_mvex() { + delete Container; +} + +Box * Box_mvex::GetBox() { + return Container; +} + +void Box_mvex::AddContent( Box * newcontent, uint32_t offset ) { + if( offset >= Content.size() ) { + Content.resize(offset+1); + } + if( Content[offset] ) { + delete Content[offset]; + } + Content[offset] = newcontent; +} + +void Box_mvex::WriteContent( ) { + Container->ResetPayload( ); + Box * current; + std::string serializedbox = ""; + for( uint32_t i = 0; i < Content.size(); i++ ) { + current=Content[i]; + if( current ) { + serializedbox.append((char*)current->GetBoxedData(),current->GetBoxedDataSize()); + } + } + Container->SetPayload((uint32_t)serializedbox.size(),(uint8_t*)serializedbox.c_str()); +} diff --git a/util/MP4/box_mvhd.cpp b/util/MP4/box_mvhd.cpp new file mode 100644 index 00000000..8055abe5 --- /dev/null +++ b/util/MP4/box_mvhd.cpp @@ -0,0 +1,113 @@ +#include "box.cpp" +#include + +#define SECONDS_DIFFERENCE 2082844800 + +class Box_mvhd { + public: + Box_mvhd( ); + ~Box_mvhd(); + Box * GetBox(); + void SetCreationTime( uint32_t TimeStamp = 0 ); + void SetModificationTime( uint32_t TimeStamp = 0 ); + void SetTimeScale( uint32_t TimeUnits = 1 ); + void SetDurationTime( uint32_t TimeUnits = 0 ); + void SetRate( uint32_t Rate = 0x00010000 ); + void SetVolume( uint16_t Volume = 0x0100 ); + void SetNextTrackID( uint32_t TrackID = 0xFFFFFFFF ); + private: + void SetReserved(); + void SetDefaults(); + Box * Container; + +};//Box_ftyp Class + +Box_mvhd::Box_mvhd( ) { + Container = new Box( 0x6D766864 ); + SetDefaults(); + SetReserved(); +} + +Box_mvhd::~Box_mvhd() { + delete Container; +} + +Box * Box_mvhd::GetBox() { + return Container; +} + +void Box_mvhd::SetCreationTime( uint32_t TimeStamp ) { + uint32_t CreationTime; + if(!TimeStamp) { + CreationTime = time(NULL) + SECONDS_DIFFERENCE; + } else { + CreationTime = TimeStamp; + } + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(CreationTime),4); +} + +void Box_mvhd::SetModificationTime( uint32_t TimeStamp ) { + uint32_t ModificationTime; + if(!TimeStamp) { + ModificationTime = time(NULL) + SECONDS_DIFFERENCE; + } else { + ModificationTime = TimeStamp; + } + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(ModificationTime),8); +} + +void Box_mvhd::SetTimeScale( uint32_t TimeUnits ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(TimeUnits),12); +} + +void Box_mvhd::SetDurationTime( uint32_t TimeUnits ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(TimeUnits),16); +} + +void Box_mvhd::SetRate( uint32_t Rate ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Rate),20); +} + +void Box_mvhd::SetVolume( uint16_t Volume ) { + Container->SetPayload((uint32_t)4,Box::uint16_to_uint8(Volume),24); +} + +void Box_mvhd::SetNextTrackID( uint32_t TrackID ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(TrackID),92); +} + +void Box_mvhd::SetReserved() { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),88); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),84); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),80); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),76); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),72); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),68); + + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0x40000000),64); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),60); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),56); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),52); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0x00010000),48); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),44); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),40); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),36); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0x00010000),32); + + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),28); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),24); + Container->SetPayload((uint32_t)4,Box::uint16_to_uint8(0),22); + + Container->SetPayload((uint32_t)4,Box::uint16_to_uint8(0)); +} + +void Box_mvhd::SetDefaults() { + SetCreationTime(); + SetModificationTime(); + SetDurationTime(); + SetNextTrackID(); + SetRate(); + SetVolume(); + SetTimeScale(); +} + diff --git a/util/MP4/box_nmhd.cpp b/util/MP4/box_nmhd.cpp new file mode 100644 index 00000000..d874d1ed --- /dev/null +++ b/util/MP4/box_nmhd.cpp @@ -0,0 +1,28 @@ +#include "box.cpp" + +class Box_nmhd { + public: + Box_nmhd( ); + ~Box_nmhd(); + Box * GetBox(); + private: + Box * Container; + void SetReserved( ); +};//Box_ftyp Class + +Box_nmhd::Box_nmhd( ) { + Container = new Box( 0x6E6D6864 ); + SetReserved(); +} + +Box_nmhd::~Box_nmhd() { + delete Container; +} + +Box * Box_nmhd::GetBox() { + return Container; +} + +void Box_nmhd::SetReserved( ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0)); +} diff --git a/util/MP4/box_rtmp.cpp b/util/MP4/box_rtmp.cpp new file mode 100644 index 00000000..980c7ef3 --- /dev/null +++ b/util/MP4/box_rtmp.cpp @@ -0,0 +1,89 @@ +#include "box.cpp" +#include + +class Box_rtmp { + public: + Box_rtmp( ); + ~Box_rtmp(); + Box * GetBox(); + void SetDataReferenceIndex( uint16_t NewIndex = 0 ); + void SetHintTrackVersion( uint16_t NewVersion = 1 ); + void SetHighestCompatibleVersion( uint16_t NewVersion = 1 ); + void SetMaxPacketSize( uint16_t NewSize = 0xFFFF ); + void AddContent( Box * newcontent ); + void WriteContent( ); + private: + void SetReserved( ); + void SetDefaults( ); + uint16_t CurrentReferenceIndex; + uint16_t CurrentHintTrackVersion; + uint16_t CurrentHighestCompatibleVersion; + uint16_t CurrentMaxPacketSize; + + Box * Container; + Box * Content; +};//Box_ftyp Class + +Box_rtmp::Box_rtmp( ) { + Container = new Box( 0x72746D70 ); + SetReserved(); + SetDefaults(); +} + +Box_rtmp::~Box_rtmp() { + delete Container; +} + +Box * Box_rtmp::GetBox() { + return Container; +} + +void Box_rtmp::SetReserved( ) { + Container->SetPayload((uint32_t)2,Box::uint16_to_uint8(CurrentMaxPacketSize),12); + Container->SetPayload((uint32_t)2,Box::uint16_to_uint8(CurrentHighestCompatibleVersion),10); + Container->SetPayload((uint32_t)2,Box::uint16_to_uint8(CurrentHintTrackVersion),8); + Container->SetPayload((uint32_t)2,Box::uint16_to_uint8(CurrentReferenceIndex),6); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),2); + Container->SetPayload((uint32_t)2,Box::uint16_to_uint8(0)); +} + +void Box_rtmp::SetDefaults( ) { + SetDataReferenceIndex( ); + SetHintTrackVersion( ); + SetHighestCompatibleVersion( ); + SetMaxPacketSize( ); +} + +void Box_rtmp::SetDataReferenceIndex( uint16_t NewIndex ) { + CurrentReferenceIndex = NewIndex; + Container->SetPayload((uint32_t)2,Box::uint16_to_uint8(NewIndex),6); +} +void Box_rtmp::SetHintTrackVersion( uint16_t NewVersion ) { + CurrentHintTrackVersion = NewVersion; + Container->SetPayload((uint32_t)2,Box::uint16_to_uint8(NewVersion),8); +} +void Box_rtmp::SetHighestCompatibleVersion( uint16_t NewVersion ) { + CurrentHighestCompatibleVersion = NewVersion; + Container->SetPayload((uint32_t)2,Box::uint16_to_uint8(NewVersion),10); +} + +void Box_rtmp::SetMaxPacketSize( uint16_t NewSize ) { + CurrentMaxPacketSize = NewSize; + Container->SetPayload((uint32_t)2,Box::uint16_to_uint8(NewSize),12); +} + +void Box_rtmp::AddContent( Box * newcontent ) { + if(Content) { + delete Content; + Content = NULL; + } + Content = newcontent; +} + +void Box_rtmp::WriteContent( ) { + Container->ResetPayload( ); + SetReserved( ); + std::string serializedbox = ""; + serializedbox.append((char*)Content->GetBoxedData(),Content->GetBoxedDataSize()); + Container->SetPayload((uint32_t)serializedbox.size(),(uint8_t*)serializedbox.c_str(),14); +} diff --git a/util/MP4/box_smhd.cpp b/util/MP4/box_smhd.cpp new file mode 100644 index 00000000..6b6f9d9f --- /dev/null +++ b/util/MP4/box_smhd.cpp @@ -0,0 +1,29 @@ +#include "box.cpp" + +class Box_smhd { + public: + Box_smhd( ); + ~Box_smhd(); + Box * GetBox(); + private: + Box * Container; + void SetReserved( ); +};//Box_ftyp Class + +Box_smhd::Box_smhd( ) { + Container = new Box( 0x736D6864 ); + SetReserved(); +} + +Box_smhd::~Box_smhd() { + delete Container; +} + +Box * Box_smhd::GetBox() { + return Container; +} + +void Box_smhd::SetReserved( ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),4); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0)); +} diff --git a/util/MP4/box_stbl.cpp b/util/MP4/box_stbl.cpp new file mode 100644 index 00000000..44a8d129 --- /dev/null +++ b/util/MP4/box_stbl.cpp @@ -0,0 +1,51 @@ +#include "box.cpp" +#include +#include + +class Box_stbl { + public: + Box_stbl(); + ~Box_stbl(); + Box * GetBox(); + void AddContent( Box * newcontent, uint32_t offset = 0 ); + void WriteContent( ); + private: + Box * Container; + + std::vector Content; +};//Box_ftyp Class + +Box_stbl::Box_stbl( ) { + Container = new Box( 0x7374626C ); +} + +Box_stbl::~Box_stbl() { + delete Container; +} + +Box * Box_stbl::GetBox() { + return Container; +} + +void Box_stbl::AddContent( Box * newcontent, uint32_t offset ) { + if( offset >= Content.size() ) { + Content.resize(offset+1); + } + if( Content[offset] ) { + delete Content[offset]; + } + Content[offset] = newcontent; +} + +void Box_stbl::WriteContent( ) { + Container->ResetPayload( ); + Box * current; + std::string serializedbox = ""; + for( uint32_t i = 0; i < Content.size(); i++ ) { + current=Content[i]; + if( current ) { + serializedbox.append((char*)current->GetBoxedData(),current->GetBoxedDataSize()); + } + } + Container->SetPayload((uint32_t)serializedbox.size(),(uint8_t*)serializedbox.c_str()); +} diff --git a/util/MP4/box_stco.cpp b/util/MP4/box_stco.cpp new file mode 100644 index 00000000..434cede8 --- /dev/null +++ b/util/MP4/box_stco.cpp @@ -0,0 +1,58 @@ +#include "box.cpp" +#include +#include + +class Box_stco { + public: + Box_stco( ); + ~Box_stco(); + Box * GetBox(); + void AddOffset( uint32_t DataOffset, uint32_t Offset = 0 ); + void SetOffsets( std::vector NewOffsets ); + void WriteContent( ); + private: + Box * Container; + + void SetReserved( ); + std::vector Offsets; +};//Box_ftyp Class + +Box_stco::Box_stco( ) { + Container = new Box( 0x7374636F ); + SetReserved(); +} + +Box_stco::~Box_stco() { + delete Container; +} + +Box * Box_stco::GetBox() { + return Container; +} + +void Box_stco::SetReserved( ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0)); +} + +void Box_stco::AddOffset( uint32_t DataOffset, uint32_t Offset ) { + if(Offset >= Offsets.size()) { + Offsets.resize(Offset+1); + } + Offsets[Offset] = DataOffset; +} + + +void Box_stco::WriteContent( ) { + Container->ResetPayload(); + SetReserved( ); + if(!Offsets.empty()) { + for(int32_t i = Offsets.size() -1; i >= 0; i--) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Offsets[i]),(i*4)+8); + } + } + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Offsets.size()),4); +} + +void Box_stco::SetOffsets( std::vector NewOffsets ) { + Offsets = NewOffsets; +} diff --git a/util/MP4/box_stsc.cpp b/util/MP4/box_stsc.cpp new file mode 100644 index 00000000..ea8edd33 --- /dev/null +++ b/util/MP4/box_stsc.cpp @@ -0,0 +1,63 @@ +#include "box.cpp" +#include +#include + +struct stsc_record { + uint32_t FirstChunk; + uint32_t SamplesPerChunk; + uint32_t SampleDescIndex; +};//stsc_record + +class Box_stsc { + public: + Box_stsc( ); + ~Box_stsc(); + Box * GetBox(); + void SetReserved( ); + void AddEntry( uint32_t FirstChunk = 0, uint32_t SamplesPerChunk = 0, uint32_t SampleDescIndex = 0, uint32_t Offset = 0 ); + void WriteContent( ); + private: + Box * Container; + + std::vector Entries; +};//Box_ftyp Class + +Box_stsc::Box_stsc( ) { + Container = new Box( 0x73747363 ); + SetReserved(); +} + +Box_stsc::~Box_stsc() { + delete Container; +} + +Box * Box_stsc::GetBox() { + return Container; +} + +void Box_stsc::SetReserved( ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0)); +} + +void Box_stsc::AddEntry( uint32_t FirstChunk, uint32_t SamplesPerChunk, uint32_t SampleDescIndex, uint32_t Offset ) { + if(Offset >= Entries.size()) { + Entries.resize(Offset+1); + } + Entries[Offset].FirstChunk = FirstChunk; + Entries[Offset].SamplesPerChunk = SamplesPerChunk; + Entries[Offset].SampleDescIndex = SampleDescIndex; +} + + +void Box_stsc::WriteContent( ) { + Container->ResetPayload(); + SetReserved( ); + if(!Entries.empty()) { + for(int32_t i = Entries.size() -1; i >= 0; i--) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Entries[i].SampleDescIndex),(i*12)+16); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Entries[i].SamplesPerChunk),(i*12)+12); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Entries[i].FirstChunk),(i*12)+8); + } + } + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Entries.size()),4); +} diff --git a/util/MP4/box_stsd.cpp b/util/MP4/box_stsd.cpp new file mode 100644 index 00000000..233fa584 --- /dev/null +++ b/util/MP4/box_stsd.cpp @@ -0,0 +1,59 @@ +#include "box.cpp" +#include +#include + +class Box_stsd { + public: + Box_stsd( ); + ~Box_stsd(); + Box * GetBox(); + void AddContent( Box * newcontent, uint32_t offset = 0 ); + void WriteContent(); + private: + Box * Container; + + void SetReserved(); + std::vector Content; +};//Box_ftyp Class + +Box_stsd::Box_stsd( ) { + Container = new Box( 0x73747364 ); + SetReserved(); +} + +Box_stsd::~Box_stsd() { + delete Container; +} + +Box * Box_stsd::GetBox() { + return Container; +} + +void Box_stsd::AddContent( Box * newcontent, uint32_t offset ) { + if( offset >= Content.size() ) { + Content.resize(offset+1); + } + if( Content[offset] ) { + delete Content[offset]; + } + Content[offset] = newcontent; +} + +void Box_stsd::SetReserved( ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8( 1 ),4); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8( 0 ),0); +} + +void Box_stsd::WriteContent( ) { + Container->ResetPayload( ); + SetReserved( ); + Box * current; + std::string serializedbox = ""; + for( uint32_t i = 0; i < Content.size(); i++ ) { + current=Content[i]; + if( current ) { + serializedbox.append((char*)current->GetBoxedData(),current->GetBoxedDataSize()); + } + } + Container->SetPayload((uint32_t)serializedbox.size(),(uint8_t*)serializedbox.c_str(),8); +} diff --git a/util/MP4/box_stts.cpp b/util/MP4/box_stts.cpp new file mode 100644 index 00000000..e16c42c0 --- /dev/null +++ b/util/MP4/box_stts.cpp @@ -0,0 +1,60 @@ +#include "box.cpp" +#include +#include + +struct stts_record { + uint32_t SampleCount; + uint32_t SampleDelta; +};//stsc_record + +class Box_stts { + public: + Box_stts( ); + ~Box_stts(); + Box * GetBox(); + void SetReserved( ); + void AddEntry( uint32_t SampleCount, uint32_t SampleDelta, uint32_t Offset = 0 ); + void WriteContent( ); + private: + Box * Container; + + std::vector Entries; +};//Box_ftyp Class + +Box_stts::Box_stts( ) { + Container = new Box( 0x73747473 ); + SetReserved(); +} + +Box_stts::~Box_stts() { + delete Container; +} + +Box * Box_stts::GetBox() { + return Container; +} + +void Box_stts::SetReserved( ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0)); +} + +void Box_stts::AddEntry( uint32_t SampleCount, uint32_t SampleDelta, uint32_t Offset ) { + if(Offset >= Entries.size()) { + Entries.resize(Offset+1); + } + Entries[Offset].SampleCount = SampleCount; + Entries[Offset].SampleDelta = SampleDelta; +} + + +void Box_stts::WriteContent( ) { + Container->ResetPayload(); + SetReserved( ); + if(!Entries.empty()) { + for(int32_t i = Entries.size() -1; i >= 0; i--) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Entries[i].SampleDelta),(i*8)+12); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Entries[i].SampleCount),(i*8)+8); + } + } + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Entries.size()),4); +} diff --git a/util/MP4/box_tfhd.cpp b/util/MP4/box_tfhd.cpp new file mode 100644 index 00000000..57ed10dc --- /dev/null +++ b/util/MP4/box_tfhd.cpp @@ -0,0 +1,88 @@ +#include "box.cpp" + +class Box_tfhd { + public: + Box_tfhd( ); + ~Box_tfhd(); + Box * GetBox(); + void SetTrackID( uint32_t TrackID = 0 ); + void SetBaseDataOffset( uint32_t Offset = 0 );//write as uint64_t + void SetSampleDescriptionIndex( uint32_t Index = 0 ); + void SetDefaultSampleDuration( uint32_t Duration = 0 ); + void SetDefaultSampleSize( uint32_t Size = 0 ); + void WriteContent( ); + private: + void SetDefaults( ); + uint32_t curTrackID; + uint32_t curBaseDataOffset; + uint32_t curSampleDescriptionIndex; + uint32_t curDefaultSampleDuration; + uint32_t curDefaultSampleSize; + Box * Container; +};//Box_ftyp Class + +Box_tfhd::Box_tfhd( ) { + Container = new Box( 0x74666864 ); + SetDefaults( ); +} + +Box_tfhd::~Box_tfhd() { + delete Container; +} + +Box * Box_tfhd::GetBox() { + return Container; +} + +void Box_tfhd::SetTrackID( uint32_t TrackID ) { + curTrackID = TrackID; +} + +void Box_tfhd::SetBaseDataOffset( uint32_t Offset ) { + curBaseDataOffset = Offset; +} + +void Box_tfhd::SetSampleDescriptionIndex( uint32_t Index ) { + curSampleDescriptionIndex = Index; +} + +void Box_tfhd::SetDefaultSampleDuration( uint32_t Duration ) { + curDefaultSampleDuration = Duration; +} + +void Box_tfhd::SetDefaultSampleSize( uint32_t Size ) { + curDefaultSampleSize = Size; +} + +void Box_tfhd::WriteContent( ) { + uint32_t curoffset; + uint32_t flags = 0 & ( curBaseDataOffset ? 0x1 : 0 ) & ( curSampleDescriptionIndex ? 0x2 : 0 ) & ( curDefaultSampleDuration ? 0x8 : 0 ) & ( curDefaultSampleSize ? 0x10 : 0 ); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(flags)); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(curTrackID),4); + curoffset = 8; + if( curBaseDataOffset ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),curoffset); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(curBaseDataOffset),curoffset+4); + curoffset += 8; + } + if( curSampleDescriptionIndex ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(curSampleDescriptionIndex),curoffset); + curoffset += 8; + } + if( curDefaultSampleDuration ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(curDefaultSampleDuration),curoffset); + curoffset += 8; + } + if( curDefaultSampleSize ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(curDefaultSampleSize),curoffset); + curoffset += 8; + } +} + +void Box_tfhd::SetDefaults( ) { + SetTrackID( ); + SetBaseDataOffset( ); + SetSampleDescriptionIndex( ); + SetDefaultSampleDuration( ); + SetDefaultSampleSize( ); +} diff --git a/util/MP4/box_tkhd.cpp b/util/MP4/box_tkhd.cpp new file mode 100644 index 00000000..c8f5c1e7 --- /dev/null +++ b/util/MP4/box_tkhd.cpp @@ -0,0 +1,117 @@ +#include "box.cpp" +#include + +#define SECONDS_DIFFERENCE 2082844800 + +class Box_tkhd { + public: + Box_tkhd( ); + ~Box_tkhd(); + Box * GetBox(); + void SetCreationTime( uint32_t TimeStamp = 0 ); + void SetModificationTime( uint32_t TimeStamp = 0 ); + void SetDurationTime( uint32_t TimeUnits = 0 ); + void SetWidth( uint16_t Width = 0 ); + void SetHeight( uint16_t Height = 0 ); + void SetFlags( bool Bit0 = true, bool Bit1 = true, bool Bit2 = true ); + void SetVersion( uint32_t Version = 0 ); + void SetTrackID( uint32_t TrackID = 0 ); + private: + void SetReserved(); + void SetDefaults(); + Box * Container; + + uint32_t CurrentFlags; + uint32_t CurrentVersion; +};//Box_ftyp Class + +Box_tkhd::Box_tkhd( ) { + Container = new Box( 0x746B6864 ); + CurrentVersion = 0; + CurrentFlags = 0; +} + +Box_tkhd::~Box_tkhd() { + delete Container; +} + +Box * Box_tkhd::GetBox() { + return Container; +} + +void Box_tkhd::SetCreationTime( uint32_t TimeStamp ) { + uint32_t CreationTime; + if(!TimeStamp) { + CreationTime = time(NULL) + SECONDS_DIFFERENCE; + } else { + CreationTime = TimeStamp; + } + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(CreationTime),4); +} + +void Box_tkhd::SetModificationTime( uint32_t TimeStamp ) { + uint32_t ModificationTime; + if(!TimeStamp) { + ModificationTime = time(NULL) + SECONDS_DIFFERENCE; + } else { + ModificationTime = TimeStamp; + } + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(ModificationTime),8); +} + +void Box_tkhd::SetDurationTime( uint32_t TimeUnits ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(TimeUnits),16); +} + +void Box_tkhd::SetReserved() { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0x40000000),68); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),64); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),60); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),56); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0x10000),52); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),48); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),44); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),40); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0x10000),36); + Container->SetPayload((uint32_t)4,Box::uint16_to_uint8(0),34); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),28); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),24); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),20); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),12); +} + +void Box_tkhd::SetVersion( uint32_t Version ) { + if ( Version >= 2 ) { return; } + CurrentVersion = Version; + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8((CurrentVersion<<24)&(CurrentFlags))); +} + +void Box_tkhd::SetFlags( bool Bit0, bool Bit1, bool Bit2 ) { + CurrentFlags = (( Bit0 ? 0x80 : 0 ) + ( Bit1 ? 0x40 : 0 ) + ( Bit2 ? 0x20 : 0 )) << 16 ; + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8((CurrentVersion<<24)&(CurrentFlags))); +} + +void Box_tkhd::SetTrackID( uint32_t TrackID ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(TrackID),12); +} + +void Box_tkhd::SetWidth( uint16_t Width ) { + uint32_t ResultWidth = ( Width << 16 ); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(ResultWidth),72); +} + +void Box_tkhd::SetHeight( uint16_t Height ) { + uint32_t ResultHeight = ( Height << 16 ); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(ResultHeight),76); +} + +void Box_tkhd::SetDefaults() { + SetHeight(); + SetWidth(); + SetCreationTime(); + SetModificationTime(); + SetDurationTime(); + SetFlags(); + SetVersion(); + SetTrackID(); +} diff --git a/util/MP4/box_traf.cpp b/util/MP4/box_traf.cpp new file mode 100644 index 00000000..fb794578 --- /dev/null +++ b/util/MP4/box_traf.cpp @@ -0,0 +1,51 @@ +#include "box.cpp" +#include +#include + +class Box_traf { + public: + Box_traf(); + ~Box_traf(); + Box * GetBox(); + void AddContent( Box * newcontent, uint32_t offset = 0 ); + void WriteContent( ); + private: + Box * Container; + + std::vector Content; +};//Box_ftyp Class + +Box_traf::Box_traf( ) { + Container = new Box( 0x74726166 ); +} + +Box_traf::~Box_traf() { + delete Container; +} + +Box * Box_traf::GetBox() { + return Container; +} + +void Box_traf::AddContent( Box * newcontent, uint32_t offset ) { + if( offset >= Content.size() ) { + Content.resize(offset+1); + } + if( Content[offset] ) { + delete Content[offset]; + } + Content[offset] = newcontent; +} + +void Box_traf::WriteContent( ) { + Container->ResetPayload( ); + Box * current; + std::string serializedbox = ""; + for( uint32_t i = 0; i < Content.size(); i++ ) { + current=Content[i]; + if( current ) { + serializedbox.append((char*)current->GetBoxedData(),current->GetBoxedDataSize()); + } + } + Container->SetPayload((uint32_t)serializedbox.size(),(uint8_t*)serializedbox.c_str()); +} diff --git a/util/MP4/box_trak.cpp b/util/MP4/box_trak.cpp new file mode 100644 index 00000000..b33bb5bd --- /dev/null +++ b/util/MP4/box_trak.cpp @@ -0,0 +1,51 @@ +#include "box.cpp" +#include +#include + +class Box_trak { + public: + Box_trak(); + ~Box_trak(); + Box * GetBox(); + void AddContent( Box * newcontent, uint32_t offset = 0 ); + void WriteContent( ); + private: + Box * Container; + + std::vector Content; +};//Box_ftyp Class + +Box_trak::Box_trak( ) { + Container = new Box( 0x7472616B ); +} + +Box_trak::~Box_trak() { + delete Container; +} + +Box * Box_trak::GetBox() { + return Container; +} + +void Box_trak::AddContent( Box * newcontent, uint32_t offset ) { + if( offset >= Content.size() ) { + Content.resize(offset+1); + } + if( Content[offset] ) { + delete Content[offset]; + } + Content[offset] = newcontent; +} + +void Box_trak::WriteContent( ) { + Container->ResetPayload( ); + Box * current; + std::string serializedbox = ""; + for( uint32_t i = 0; i < Content.size(); i++ ) { + current=Content[i]; + if( current ) { + serializedbox.append((char*)current->GetBoxedData(),current->GetBoxedDataSize()); + } + } + Container->SetPayload((uint32_t)serializedbox.size(),(uint8_t*)serializedbox.c_str()); +} diff --git a/util/MP4/box_trex.cpp b/util/MP4/box_trex.cpp new file mode 100644 index 00000000..16369795 --- /dev/null +++ b/util/MP4/box_trex.cpp @@ -0,0 +1,59 @@ +#include "box.cpp" + +class Box_trex { + public: + Box_trex( ); + ~Box_trex(); + Box * GetBox(); + void SetTrackID( uint32_t Id = 0 ); + void SetSampleDescriptionIndex( uint32_t Index = 0 ); + void SetSampleDuration( uint32_t Duration = 0 ); + void SetSampleSize( uint32_t Size = 0 ); + private: + void SetReserved( ); + void SetDefaults( ); + Box * Container; +};//Box_ftyp Class + +Box_trex::Box_trex( ) { + Container = new Box( 0x74726578 ); + SetReserved( ); + SetDefaults( ); +} + +Box_trex::~Box_trex() { + delete Container; +} + +Box * Box_trex::GetBox() { + return Container; +} + +void Box_trex::SetDefaults( ) { + SetTrackID( ); + SetSampleDescriptionIndex( ); + SetSampleDuration( ); + SetSampleSize( ); +} + +void Box_trex::SetReserved( ) { + Container->SetPayload((uint32_t)2,Box::uint16_to_uint8(1),22); + Container->SetPayload((uint32_t)2,Box::uint16_to_uint8(0),20); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0)); +} + +void Box_trex::SetTrackID( uint32_t Id ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Id),4); +} + +void Box_trex::SetSampleDescriptionIndex( uint32_t Index ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Index),8); +} + +void Box_trex::SetSampleDuration( uint32_t Duration ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Duration),12); +} + +void Box_trex::SetSampleSize( uint32_t Size ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Size),16); +} diff --git a/util/MP4/box_trun.cpp b/util/MP4/box_trun.cpp new file mode 100644 index 00000000..1010f2da --- /dev/null +++ b/util/MP4/box_trun.cpp @@ -0,0 +1,68 @@ +#include "box.cpp" +#include + +struct trun_sampleinformationstructure { + uint32_t SampleDuration; + uint32_t SampleSize; +}; + +class Box_trun { + public: + Box_trun( ); + ~Box_trun(); + Box * GetBox(); + void SetDataOffset( uint32_t Offset = 0 ); + void AddSampleInformation( uint32_t SampleDuration = 0, uint32_t SampleSize = 0, uint32_t Offset = 0 ); + void WriteContent( ); + private: + void SetDefaults( ); + bool setSampleDuration; + bool setSampleSize; + uint32_t curDataOffset; + std::vector SampleInfo; + Box * Container; +};//Box_ftyp Class + +Box_trun::Box_trun( ) { + Container = new Box( 0x74666864 ); + SetDefaults( ); +} + +Box_trun::~Box_trun() { + delete Container; +} + +Box * Box_trun::GetBox() { + return Container; +} + +void Box_trun::SetDataOffset( uint32_t Offset ) { + curDataOffset = Offset; +} + +void Box_trun::WriteContent( ) { + uint32_t curoffset; + uint32_t flags = 0 & ( curDataOffset ? 0x1 : 0 ) & ( setSampleDuration ? 0x100 : 0 ) & ( setSampleSize ? 0x200 : 0 ); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(flags)); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(SampleInfo.size()),4); + curoffset = 8; + if( curDataOffset ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(curDataOffset),curoffset); + curoffset += 4; + } + for( uint32_t i = 0; i < SampleInfo.size(); i++ ) { + if( setSampleDuration ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(SampleInfo[i].SampleDuration),curoffset); + curoffset += 4; + } + if( setSampleSize ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(SampleInfo[i].SampleSize),curoffset); + curoffset += 4; + } + } +} + +void Box_trun::SetDefaults( ) { + setSampleDuration = false; + setSampleSize = false; +} diff --git a/util/MP4/box_url.cpp b/util/MP4/box_url.cpp new file mode 100644 index 00000000..8287f7c3 --- /dev/null +++ b/util/MP4/box_url.cpp @@ -0,0 +1,23 @@ +#include "box.cpp" + +class Box_url { + public: + Box_url( ); + ~Box_url(); + Box * GetBox(); + private: + Box * Container; +};//Box_ftyp Class + +Box_url::Box_url( ) { + Container = new Box( 0x75726C20 ); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(1)); +} + +Box_url::~Box_url() { + delete Container; +} + +Box * Box_url::GetBox() { + return Container; +} diff --git a/util/MP4/box_vmhd.cpp b/util/MP4/box_vmhd.cpp new file mode 100644 index 00000000..6dd5fb8e --- /dev/null +++ b/util/MP4/box_vmhd.cpp @@ -0,0 +1,45 @@ +#include "box.cpp" + +class Box_vmhd { + public: + Box_vmhd( ); + ~Box_vmhd(); + Box * GetBox(); + void SetGraphicsMode( uint16_t GraphicsMode = 0 ); + void SetOpColor( uint16_t Red = 0, uint16_t Green = 0, uint16_t Blue = 0); + private: + Box * Container; + void SetReserved( ); + void SetDefaults( ); +};//Box_ftyp Class + +Box_vmhd::Box_vmhd( ) { + Container = new Box( 0x766D6864 ); + SetDefaults(); + SetReserved(); +} + +Box_vmhd::~Box_vmhd() { + delete Container; +} + +Box * Box_vmhd::GetBox() { + return Container; +} + +void Box_vmhd::SetGraphicsMode( uint16_t GraphicsMode ) { + Container->SetPayload((uint32_t)2,Box::uint16_to_uint8(GraphicsMode),8); +} +void Box_vmhd::SetOpColor( uint16_t Red, uint16_t Green, uint16_t Blue ) { + Container->SetPayload((uint32_t)2,Box::uint16_to_uint8(Blue),14); + Container->SetPayload((uint32_t)2,Box::uint16_to_uint8(Green),12); + Container->SetPayload((uint32_t)2,Box::uint16_to_uint8(Red),10); +} + +void Box_vmhd::SetReserved( ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(1)); +} +void Box_vmhd::SetDefaults( ) { + SetOpColor(); + SetGraphicsMode(); +} diff --git a/util/MP4/interface.cpp b/util/MP4/interface.cpp new file mode 100644 index 00000000..6e81a6d6 --- /dev/null +++ b/util/MP4/interface.cpp @@ -0,0 +1,583 @@ +#include "box_includes.h" + +#include + +class Interface { + public: + Interface(); + ~Interface(); + void link(); + uint32_t GetContentSize(); + uint8_t * GetContents(); + void SetWidth( uint16_t NewWidth ); + void SetHeight( uint16_t NewHeight ); + void SetDurationTime( uint32_t NewDuration, uint32_t Track ); + void SetTimeScale( uint32_t NewUnitsPerSecond, uint32_t Track ); + void AddSTTSEntry( uint32_t SampleCount, uint32_t SampleDelta, uint32_t Track ); + void EmptySTTS( uint32_t Track ); + void AddSTSCEntry( uint32_t FirstChunk, uint32_t SamplesPerChunk, uint32_t Track ); + void EmptySTSC( uint32_t Track ); + void SetOffsets( std::vector NewOffsets, uint32_t Track ); + void SetData( std::string data ); + std::string GenerateLiveBootstrap( uint32_t CurMediaTime ); + static std::string mdatFold(std::string data); + private: + void SetStaticDefaults(); + void UpdateContents(); + void WriteSTTS( uint32_t Track ); + void WriteSTSC( uint32_t Track ); + bool AllBoxesExist(); + uint16_t Width; + uint16_t Height; + std::vector Duration; + std::vector UnitsPerSecond; + std::vector sttsvide; + std::vector sttssoun; + std::vector stscvide; + std::vector stscsoun; + Box_ftyp * ftyp; + Box_moov * moov; + Box_mvhd * mvhd; + Box_trak * trak_vide; + Box_tkhd * tkhd_vide; + Box_mdia * mdia_vide; + Box_mdhd * mdhd_vide; + Box_hdlr * hdlr_vide; + Box_minf * minf_vide; + Box_vmhd * vmhd_vide; + Box_dinf * dinf_vide; + Box_dref * dref_vide; + Box_url * url_vide; + Box_stbl * stbl_vide; + Box_stts * stts_vide; + Box_stsc * stsc_vide; + Box_stco * stco_vide; + Box_stsd * stsd_vide; + Box_avcC * avcC_vide; + Box_trak * trak_soun; + Box_tkhd * tkhd_soun; + Box_mdia * mdia_soun; + Box_mdhd * mdhd_soun; + Box_hdlr * hdlr_soun; + Box_minf * minf_soun; + Box_smhd * smhd_soun; + Box_dinf * dinf_soun; + Box_dref * dref_soun; + Box_url * url_soun; + Box_stbl * stbl_soun; + Box_stts * stts_soun; + Box_stsc * stsc_soun; + Box_stco * stco_soun; + Box_stsd * stsd_soun; + Box_esds * esds_soun; + Box_rtmp * rtmp; + Box_amhp * amhp; + Box_mvex * mvex; + Box_trex * trex_vide; + Box_trex * trex_soun; + Box_afra * afra; + Box_abst * abst; + Box_asrt * asrt; + Box_afrt * afrt; + Box_moof * moof; + Box_mfhd * mfhd; + Box_traf * traf_vide; + Box_tfhd * tfhd_vide; + Box_trun * trun_vide; + Box_traf * traf_soun; + Box_tfhd * tfhd_soun; + Box_trun * trun_soun; +};//Interface class + +Interface::Interface() { + //Initializing local data + Width = 0; + Height = 0; + //Creating the boxes + ftyp = new Box_ftyp(); + moov = new Box_moov(); + mvhd = new Box_mvhd(); + trak_vide = new Box_trak(); + tkhd_vide = new Box_tkhd(); + mdia_vide = new Box_mdia(); + mdhd_vide = new Box_mdhd(); + hdlr_vide = new Box_hdlr(); + minf_vide = new Box_minf(); + vmhd_vide = new Box_vmhd(); + dinf_vide = new Box_dinf(); + dref_vide = new Box_dref(); + url_vide = new Box_url(); + stbl_vide = new Box_stbl(); + stts_vide = new Box_stts(); + stsc_vide = new Box_stsc(); + stco_vide = new Box_stco(); + stsd_vide = new Box_stsd(); + avcC_vide = new Box_avcC(); + trak_soun = new Box_trak(); + tkhd_soun = new Box_tkhd(); + mdia_soun = new Box_mdia(); + mdhd_soun = new Box_mdhd(); + hdlr_soun = new Box_hdlr(); + minf_soun = new Box_minf(); + smhd_soun = new Box_smhd(); + dinf_soun = new Box_dinf(); + dref_soun = new Box_dref(); + url_soun = new Box_url(); + stbl_soun = new Box_stbl(); + stts_soun = new Box_stts(); + stsc_soun = new Box_stsc(); + stco_soun = new Box_stco(); + stsd_soun = new Box_stsd(); + esds_soun = new Box_esds(); + rtmp = new Box_rtmp(); + amhp = new Box_amhp(); + mvex = new Box_mvex(); + trex_vide = new Box_trex(); + trex_soun = new Box_trex(); + afra = new Box_afra(); + abst = new Box_abst(); + asrt = new Box_asrt(); + afrt = new Box_afrt(); + moof = new Box_moof(); + mfhd = new Box_mfhd(); + traf_vide = new Box_traf(); + tfhd_vide = new Box_tfhd(); + trun_vide = new Box_trun(); + traf_soun = new Box_traf(); + tfhd_soun = new Box_tfhd(); + trun_soun = new Box_trun(); + //Set some values we already know won't change once the boxes have been created + SetStaticDefaults(); +} + +Interface::~Interface() { + //Deleting the boxes if they still exist. + if( trun_soun ) { delete trun_soun; trun_soun = NULL; } + if( tfhd_soun ) { delete tfhd_soun; tfhd_soun = NULL; } + if( traf_soun ) { delete traf_soun; traf_soun = NULL; } + if( trun_vide ) { delete trun_vide; trun_vide = NULL; } + if( tfhd_vide ) { delete tfhd_vide; tfhd_vide = NULL; } + if( traf_vide ) { delete traf_vide; traf_vide = NULL; } + if( mfhd ) { delete mfhd; mfhd = NULL; } + if( moof ) { delete moof; moof = NULL; } + if( afrt ) { delete afrt; afrt = NULL; } + if( asrt ) { delete asrt; asrt = NULL; } + if( abst ) { delete abst; abst = NULL; } + if( afra ) { delete afra; afra = NULL; } + if( trex_vide ) { delete trex_vide; trex_vide = NULL; } + if( trex_soun ) { delete trex_soun; trex_soun = NULL; } + if( mvex ) { delete mvex; mvex = NULL; } + if( amhp ) { delete amhp; amhp = NULL; } + if( rtmp ) { delete rtmp; rtmp = NULL; } + if( esds_soun ) { delete esds_soun; esds_soun = NULL; } + if( stsd_soun ) { delete stsd_soun; stsd_soun = NULL; } + if( stco_soun ) { delete stco_soun; stco_soun = NULL; } + if( stsc_soun ) { delete stsc_soun; stsc_soun = NULL; } + if( stts_soun ) { delete stts_soun; stts_soun = NULL; } + if( stbl_soun ) { delete stbl_soun; stbl_soun = NULL; } + if( url_soun ) { delete url_soun; url_soun = NULL; } + if( dref_soun ) { delete dref_soun; dref_soun = NULL; } + if( dinf_soun ) { delete dinf_soun; dinf_soun = NULL; } + if( minf_soun ) { delete minf_soun; minf_soun = NULL; } + if( hdlr_soun ) { delete hdlr_soun; hdlr_soun = NULL; } + if( mdhd_soun ) { delete mdhd_soun; mdhd_soun = NULL; } + if( mdia_soun ) { delete mdia_soun; mdia_soun = NULL; } + if( tkhd_soun ) { delete tkhd_soun; tkhd_soun = NULL; } + if( trak_soun ) { delete trak_soun; trak_soun = NULL; } + if( avcC_vide ) { delete avcC_vide; avcC_vide = NULL; } + if( stsd_vide ) { delete stsd_vide; stsd_vide = NULL; } + if( stco_vide ) { delete stco_vide; stco_vide = NULL; } + if( stsc_vide ) { delete stsc_vide; stsc_vide = NULL; } + if( stts_vide ) { delete stts_vide; stts_vide = NULL; } + if( stbl_vide ) { delete stbl_vide; stbl_vide = NULL; } + if( url_vide ) { delete url_vide; url_vide = NULL; } + if( dref_vide ) { delete dref_vide; dref_vide = NULL; } + if( dinf_vide ) { delete dinf_vide; dinf_vide = NULL; } + if( minf_vide ) { delete minf_vide; minf_vide = NULL; } + if( hdlr_vide ) { delete hdlr_vide; hdlr_vide = NULL; } + if( mdhd_vide ) { delete mdhd_vide; mdhd_vide = NULL; } + if( mdia_vide ) { delete mdia_vide; mdia_vide = NULL; } + if( tkhd_vide ) { delete tkhd_vide; tkhd_vide = NULL; } + if( trak_vide ) { delete trak_vide; trak_vide = NULL; } + if( mvhd ) { delete mvhd; mvhd = NULL; } + if( moov ) { delete moov; moov = NULL; } + if( ftyp ) { delete ftyp; ftyp = NULL; } +} + +void Interface::link( ) { + //Linking Video Track + stsd_vide->AddContent(avcC_vide->GetBox()); + stbl_vide->AddContent(stsd_vide->GetBox(),3); + stbl_vide->AddContent(stco_vide->GetBox(),2); + stbl_vide->AddContent(stsc_vide->GetBox(),1); + stbl_vide->AddContent(stts_vide->GetBox()); + dref_vide->AddContent(url_vide->GetBox()); + dinf_vide->AddContent(dref_vide->GetBox()); + minf_vide->AddContent(stbl_vide->GetBox(),2); + minf_vide->AddContent(dinf_vide->GetBox(),1); + minf_vide->AddContent(vmhd_vide->GetBox()); + mdia_vide->AddContent(minf_vide->GetBox(),2); + mdia_vide->AddContent(hdlr_vide->GetBox(),1); + mdia_vide->AddContent(mdhd_vide->GetBox()); + trak_vide->AddContent(mdia_vide->GetBox(),1); + trak_vide->AddContent(tkhd_vide->GetBox()); + + //Linking Sound Track + stsd_soun->AddContent(esds_soun->GetBox()); + stbl_soun->AddContent(stsd_soun->GetBox(),3); + stbl_soun->AddContent(stco_soun->GetBox(),2); + stbl_soun->AddContent(stsc_soun->GetBox(),1); + stbl_soun->AddContent(stts_soun->GetBox()); + dref_soun->AddContent(url_soun->GetBox()); + dinf_soun->AddContent(dref_soun->GetBox()); + minf_soun->AddContent(stbl_soun->GetBox(),2); + minf_soun->AddContent(dinf_soun->GetBox(),1); + minf_soun->AddContent(smhd_soun->GetBox()); + mdia_soun->AddContent(minf_soun->GetBox(),2); + mdia_soun->AddContent(hdlr_soun->GetBox(),1); + mdia_soun->AddContent(mdhd_soun->GetBox()); + trak_soun->AddContent(mdia_soun->GetBox(),1); + trak_soun->AddContent(tkhd_soun->GetBox()); + + //Linking mvex + mvex->AddContent(trex_soun->GetBox(),2); + mvex->AddContent(trex_vide->GetBox(),1); + + //Linking total file + moov->AddContent(mvex->GetBox(),3); + moov->AddContent(trak_soun->GetBox(),2); + moov->AddContent(trak_vide->GetBox(),1); + moov->AddContent(mvhd->GetBox()); + + rtmp->AddContent(amhp->GetBox()); + + //Linking ABST + abst->AddFragmentRunTable(afrt->GetBox()); + abst->AddSegmentRunTable(asrt->GetBox()); + + //Linking TRAF_SOUN + traf_soun->AddContent( trun_soun->GetBox(),1); + traf_soun->AddContent( tfhd_soun->GetBox() ); + + //Linking TRAF_vide + traf_vide->AddContent( trun_vide->GetBox(),1); + traf_vide->AddContent( tfhd_vide->GetBox() ); + + //Linking MOOF + moof->AddContent(traf_soun->GetBox(),2); + moof->AddContent(traf_vide->GetBox(),1); + moof->AddContent(mfhd->GetBox()); +} + +uint32_t Interface::GetContentSize( ) { + return ftyp->GetBox( )->GetBoxedDataSize( ) + moov->GetBox( )->GetBoxedDataSize( ) + rtmp->GetBox( )->GetBoxedDataSize( ); +} + +uint8_t * Interface::GetContents( ) { + uint8_t * Result = new uint8_t[GetContentSize( )]; + uint32_t Ftyp_Size = ftyp->GetBox( )->GetBoxedDataSize( ); + uint32_t Moov_Size = moov->GetBox( )->GetBoxedDataSize( ); + uint32_t Rtmp_Size = rtmp->GetBox( )->GetBoxedDataSize( ); + memcpy(Result,ftyp->GetBox( )->GetBoxedData( ),Ftyp_Size); + memcpy(&Result[Ftyp_Size],moov->GetBox( )->GetBoxedData( ),Moov_Size); + memcpy(&Result[Ftyp_Size+Moov_Size],rtmp->GetBox( )->GetBoxedData( ),Rtmp_Size); + return Result; +} + +void Interface::UpdateContents( ) { + if( !Width ) { fprintf(stderr,"WARNING: Width not set!\n"); } + if( !Height ) { fprintf(stderr,"WARNING: Height not set!\n"); } + if( !Duration.size() ) { fprintf(stderr,"WARNING: Duration not set!\n"); } + if( !UnitsPerSecond.size() ) { fprintf(stderr,"WARNING: Timescale not set!\n"); } + if( sttsvide.size() == 0 ) { + fprintf(stderr,"WARNING: No video stts available!\n"); + } else { WriteSTTS( 1 ); } + if( sttssoun.size() == 0 ) { + fprintf(stderr,"WARNING: No sound stts available!\n"); + } else { WriteSTTS( 2 ); } + if( stscvide.size() == 0 ) { + fprintf(stderr,"WARNING: No video stsc available!\n"); + } else { WriteSTSC( 1 ); } + if( stscsoun.size() == 0 ) { + fprintf(stderr,"WARNING: No sound stsc available!\n"); + } else { WriteSTSC( 2 ); } + stsd_vide->WriteContent( ); + stco_vide->WriteContent( ); + stsc_vide->WriteContent( ); + stts_vide->WriteContent( ); + stbl_vide->WriteContent( ); + dref_vide->WriteContent( ); + dinf_vide->WriteContent( ); + minf_vide->WriteContent( ); + mdia_vide->WriteContent( ); + + stsd_soun->WriteContent( ); + stco_soun->WriteContent( ); + stsc_soun->WriteContent( ); + stts_soun->WriteContent( ); + stbl_soun->WriteContent( ); + dref_soun->WriteContent( ); + dinf_soun->WriteContent( ); + minf_soun->WriteContent( ); + mdia_soun->WriteContent( ); + + trak_vide->WriteContent( ); + trak_soun->WriteContent( ); + + mvex->WriteContent( ); + moov->WriteContent( ); + + amhp->WriteContent( ); + rtmp->WriteContent( ); + + afrt->WriteContent( ); + asrt->WriteContent( ); + abst->WriteContent( ); + + trun_soun->WriteContent( ); + traf_soun->WriteContent( ); + + trun_vide->WriteContent( ); + traf_vide->WriteContent( ); + + moof->WriteContent( ); +} + +bool Interface::AllBoxesExist() { + return ( ftyp && moov && mvhd && trak_vide && tkhd_vide && mdia_vide && mdhd_vide && hdlr_vide && + minf_vide && vmhd_vide && dinf_vide && dref_vide && url_vide && stbl_vide && stts_vide && stsc_vide && + stco_vide && stsd_vide && avcC_vide && trak_soun && tkhd_soun && mdia_soun && mdhd_soun && hdlr_soun && + minf_soun && smhd_soun && dinf_soun && dref_soun && url_soun && stbl_soun && stts_soun && stsc_soun && + stco_soun && stsd_soun && esds_soun && rtmp && amhp && mvex && trex_vide && trex_soun && afrt && asrt + && abst && moof && mfhd && traf_vide && tfhd_vide && trun_vide && traf_soun && tfhd_soun && trun_soun ); +} + +void Interface::SetWidth( uint16_t NewWidth ) { + if( Width != NewWidth ) { + Width = NewWidth; + avcC_vide->SetWidth( Width ); + tkhd_vide->SetWidth( Width ); + } +} + +void Interface::SetHeight( uint16_t NewHeight ) { + if( Height != NewHeight ) { + Height = NewHeight; + avcC_vide->SetHeight( Height ); + tkhd_vide->SetHeight( Height ); + } +} + +void Interface::SetDurationTime( uint32_t NewDuration, uint32_t Track ) { + if( Duration.size() < Track ) { Duration.resize(Track+1); } + if( Duration[Track] != NewDuration ) { + Duration[Track] = NewDuration; + switch( Track ) { + case 0: + mvhd->SetDurationTime( Duration[Track] ); + break; + case 1: + mdhd_vide->SetDurationTime( Duration[Track] ); + tkhd_vide->SetDurationTime( Duration[Track] ); + break; + case 2: + mdhd_soun->SetDurationTime( Duration[Track] ); + tkhd_soun->SetDurationTime( Duration[Track] ); + break; + default: + fprintf( stderr, "WARNING, Setting Duration for track %d does have any effect\n", Track ); + break; + } + } +} +void Interface::SetTimeScale( uint32_t NewUnitsPerSecond, uint32_t Track ) { + if( UnitsPerSecond.size() < Track ) { UnitsPerSecond.resize(Track+1); } + if( UnitsPerSecond[Track] != NewUnitsPerSecond ) { + UnitsPerSecond[Track] = NewUnitsPerSecond; + switch(Track) { + case 0: + mvhd->SetTimeScale( UnitsPerSecond[Track] ); + break; + case 1: + mdhd_vide->SetTimeScale( UnitsPerSecond[Track] ); + break; + case 2: + mdhd_soun->SetTimeScale( UnitsPerSecond[Track] ); + break; + default: + fprintf( stderr, "WARNING, Setting Timescale for track %d does have any effect\n", Track ); + break; + } + } +} + +void Interface::SetStaticDefaults() { +// 'vide' = 0x76696465 + hdlr_vide->SetHandlerType( 0x76696465 ); + hdlr_vide->SetName( "Video Track" ); +// 'soun' = 0x736F756E + hdlr_soun->SetHandlerType( 0x736F756E ); + hdlr_vide->SetName( "Audio Track" ); +// Set Track ID's + tkhd_vide->SetTrackID( 1 ); + tkhd_soun->SetTrackID( 2 ); + trex_vide->SetTrackID( 1 ); + trex_soun->SetTrackID( 2 ); +// Set amhp entry + amhp->AddEntry( 1, 0, 0 ); +} + +void Interface::AddSTTSEntry( uint32_t SampleCount, uint32_t SampleDelta, uint32_t Track ) { + stts_record temp; + temp.SampleCount = SampleCount; + temp.SampleDelta = SampleDelta; + switch(Track) { + case 1: + sttsvide.push_back(temp); + break; + case 2: + sttssoun.push_back(temp); + break; + default: + fprintf( stderr, "WARNING: Track %d does not exist, STTS not added\n", Track ); + break; + } +} + +void Interface::EmptySTTS( uint32_t Track ) { + switch(Track) { + case 1: + sttsvide.clear(); + break; + case 2: + sttssoun.clear(); + break; + default: + fprintf( stderr, "WARNING: Track %d does not exist, STTS not cleared\n", Track ); + break; + } +} + +void Interface::WriteSTTS( uint32_t Track ) { + switch( Track ) { + case 1: + for( int i = sttsvide.size() -1; i > 0; i -- ) { + stts_vide->AddEntry(sttsvide[i].SampleCount,sttsvide[i].SampleDelta,i); + } + break; + case 2: + for( int i = sttssoun.size() -1; i > 0; i -- ) { + stts_soun->AddEntry(sttssoun[i].SampleCount,sttssoun[i].SampleDelta,i); + } + break; + default: + fprintf( stderr, "WARNING: Track %d does not exist, STTS not written\n", Track ); + break; + } +} + +void Interface::AddSTSCEntry( uint32_t FirstChunk, uint32_t SamplesPerChunk, uint32_t Track ) { + stsc_record temp; + temp.FirstChunk = FirstChunk; + temp.SamplesPerChunk = SamplesPerChunk; + temp.SampleDescIndex = 1; + switch(Track) { + case 1: + stscvide.push_back(temp); + break; + case 2: + stscsoun.push_back(temp); + break; + default: + fprintf( stderr, "WARNING: Track %d does not exist, STSC not added\n", Track ); + break; + } +} + +void Interface::EmptySTSC( uint32_t Track ) { + switch(Track) { + case 1: + stscvide.clear(); + break; + case 2: + stscsoun.clear(); + break; + default: + fprintf( stderr, "WARNING: Track %d does not exist, STSC not cleared\n", Track ); + break; + } +} + +void Interface::WriteSTSC( uint32_t Track ) { + switch( Track ) { + case 1: + for( int i = stscvide.size() -1; i > 0; i -- ) { + stsc_vide->AddEntry(stscvide[i].FirstChunk,stscvide[i].SamplesPerChunk,1,i); + } + break; + case 2: + for( int i = stscsoun.size() -1; i > 0; i -- ) { + stsc_soun->AddEntry(stscsoun[i].FirstChunk,stscsoun[i].SamplesPerChunk,1,i); + } + break; + default: + fprintf( stderr, "WARNING: Track %d does not exist, STSC not written\n", Track ); + break; + } +} + +void Interface::SetOffsets( std::vector NewOffsets, uint32_t Track ) { + switch( Track ) { + case 1: + stco_vide->SetOffsets( NewOffsets ); + break; + case 2: + stco_soun->SetOffsets( NewOffsets ); + break; + default: + fprintf( stderr, "WARNING: Track %d does not exist, Offsets not written\n", Track ); + break; + } +} + +std::string Interface::GenerateLiveBootstrap( uint32_t CurMediaTime ) { + //SetUpAFRT + afrt->SetUpdate(false); + afrt->SetTimeScale( 1000 ); + afrt->AddQualityEntry( "" ); + afrt->AddFragmentRunEntry( 1, 0, 0, 2 ); + afrt->WriteContent( ); + + //SetUpASRT + asrt->SetUpdate(false); + asrt->AddQualityEntry( "" ); + asrt->AddSegmentRunEntry( 1, 0 ); + asrt->WriteContent( ); + + //SetUpABST + abst->SetBootstrapVersion( 1 ); + abst->SetProfile( 0 ); + abst->SetLive( true ); + abst->SetUpdate( false ); + abst->SetTimeScale( 1000 ); + abst->SetMediaTime( CurMediaTime ); + abst->SetSMPTE( 0 ); + abst->SetMovieIdentifier( "" ); + abst->SetDRM( "" ); + abst->SetMetaData( "" ); + abst->AddServerEntry( "" ); + abst->AddQualityEntry( "" ); + abst->WriteContent( ); + + std::string Result; + Result.append( (char*)abst->GetBox( )->GetBoxedData( ), (int)abst->GetBox( )->GetBoxedDataSize( ) ); + return Result; +} + +std::string Interface::mdatFold(std::string data){ + static Box_mdat * mdat = new Box_mdat; + std::string Result; + mdat->SetContent((uint8_t*)data.c_str(), data.size()); + Result.append((char*)mdat->GetBox()->GetBoxedData(), (int)mdat->GetBox()->GetBoxedDataSize()); + return Result; +} diff --git a/util/MP4/main.cpp b/util/MP4/main.cpp new file mode 100644 index 00000000..027ddb8d --- /dev/null +++ b/util/MP4/main.cpp @@ -0,0 +1,13 @@ +#include +#include "interface.h" + +int main( ) { + std::cout << "Creating Interface\n"; + Interface * file = new Interface(); + std::cout << "Interface created, start linking them\n"; + file->link(); + std::cout << "Linking finished, deleting boxes\n"; + delete file; + std::cout << "Interface deleted\n"; + return 0; +} From bb6f3868ec0c8dd50d5f5b7fae5d13b62ba197ac Mon Sep 17 00:00:00 2001 From: Thulinma Date: Wed, 16 Feb 2011 21:51:54 +0100 Subject: [PATCH 066/788] Connector_HTTP compilend - klaar voor test! --- util/MP4/box_avcC.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/util/MP4/box_avcC.cpp b/util/MP4/box_avcC.cpp index 90bc6fe0..2f021957 100644 --- a/util/MP4/box_avcC.cpp +++ b/util/MP4/box_avcC.cpp @@ -1,5 +1,6 @@ #include "box.cpp" #include +#include class Box_avcC { public: @@ -57,7 +58,7 @@ void Box_avcC::SetFrameCount ( uint16_t FrameCount ) { void Box_avcC::SetCompressorName ( std::string CompressorName ) { uint8_t * Printable = new uint8_t[1]; - Printable[0] = std::min( (unsigned int)31, CompressorName.size() ); + Printable[0] = std::min( (unsigned int)31, (unsigned int)CompressorName.size() ); Container->SetPayload((uint32_t)Printable[0],(uint8_t*)CompressorName.c_str(),43); Container->SetPayload((uint32_t)1, Printable ,42); } From 91c445fbf0ab53f61664fc5c32ce3950c634289d Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Wed, 16 Feb 2011 23:27:43 +0100 Subject: [PATCH 067/788] Bugfix, wss --- util/MP4/interface.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/util/MP4/interface.cpp b/util/MP4/interface.cpp index 6e81a6d6..e724f05b 100644 --- a/util/MP4/interface.cpp +++ b/util/MP4/interface.cpp @@ -148,6 +148,8 @@ Interface::Interface() { trun_soun = new Box_trun(); //Set some values we already know won't change once the boxes have been created SetStaticDefaults(); + //Linking all boxes + link( ); } Interface::~Interface() { From f8bee94f32769f271267ec4e90db5dc80ad443a5 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Wed, 16 Feb 2011 23:47:51 +0100 Subject: [PATCH 068/788] Metadata fix, protocol dix --- util/http_parser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/http_parser.cpp b/util/http_parser.cpp index f00cb2dd..df866b1a 100644 --- a/util/http_parser.cpp +++ b/util/http_parser.cpp @@ -35,7 +35,7 @@ void HTTPReader::Clean(){ seenReq = false; method = "GET"; url = "/"; - protocol = "HTTP/1.0"; + protocol = "HTTP/1.1"; length = 0; HTTPbuffer = ""; headers.erase(headers.begin(), headers.end()); From a8c4463144be92cb5eb6e308ae001a7d14e1ffa6 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 17 Feb 2011 02:26:18 +0100 Subject: [PATCH 069/788] Progressive mode edits, RTMP edits. Also, TODO: AMF3 implementen. *barf* --- util/http_parser.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/util/http_parser.cpp b/util/http_parser.cpp index df866b1a..6f7d619f 100644 --- a/util/http_parser.cpp +++ b/util/http_parser.cpp @@ -14,6 +14,9 @@ class HTTPReader{ void SetBody(char * buffer, int len); std::string BuildRequest(); std::string BuildResponse(std::string code, std::string message); + void SendResponse(int conn, std::string code, std::string message); + void SendBodyPart(int conn, char * buffer, int len); + void SendBodyPart(int conn, std::string bodypart); void Clean(); std::string method; std::string url; @@ -171,4 +174,23 @@ bool HTTPReader::parse(){ return false; //we should never get here... }//HTTPReader::parse +void HTTPReader::SendResponse(int conn, std::string code, std::string message){ + std::string tmp = BuildResponse(code, message); + DDV_write(tmp.c_str(), tmp.size(), conn); +} + +void HTTPReader::SendBodyPart(int conn, char * buffer, int len){ + std::string tmp; + tmp.append(buffer, len); + SendBodyPart(conn, tmp); +} + +void HTTPReader::SendBodyPart(int conn, std::string bodypart){ + static char len[10]; + int sizelen; + sizelen = snprintf(len, 10, "%x\r\n", (unsigned int)bodypart.size()); + DDV_write(len, sizelen, conn); + DDV_write(bodypart.c_str(), bodypart.size(), conn); + DDV_write(len+sizelen-2, 2, conn); +} From 08bc6d3c4d21503713e2e4abb5e7c2b4b1e277d1 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Thu, 17 Feb 2011 07:28:07 +0100 Subject: [PATCH 070/788] Bugfix afrt timesscale --- util/MP4/box_afrt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/MP4/box_afrt.cpp b/util/MP4/box_afrt.cpp index 67365d7e..0df7065f 100644 --- a/util/MP4/box_afrt.cpp +++ b/util/MP4/box_afrt.cpp @@ -95,6 +95,6 @@ void Box_afrt::WriteContent( ) { Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(FragmentRunEntryTable.size()),OffsetFragmentRunEntryCount); Container->SetPayload((uint32_t)serializedQualities.size(),(uint8_t*)serializedQualities.c_str(),9); Container->SetPayload((uint32_t)1,Box::uint8_to_uint8(QualitySegmentUrlModifiers.size()),8); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(curTimeScale)); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(curTimeScale),4); Container->SetPayload((uint32_t)4,Box::uint32_to_uint8((isUpdate ? 1 : 0))); } From d99372f9196ec131bdce0fee7420e3b1627089b3 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Thu, 17 Feb 2011 08:01:03 +0100 Subject: [PATCH 071/788] Bugfix afrt fragmentrunentry --- util/MP4/box_afrt.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/util/MP4/box_afrt.cpp b/util/MP4/box_afrt.cpp index 0df7065f..66c18b43 100644 --- a/util/MP4/box_afrt.cpp +++ b/util/MP4/box_afrt.cpp @@ -80,12 +80,12 @@ void Box_afrt::WriteContent( ) { serializedQualities += '\0'; } for( uint32_t i = 0; i < FragmentRunEntryTable.size(); i ++ ) { - serializedFragmentEntries.append((char*)Box::uint32_to_uint8(FragmentRunEntryTable[i].FirstFragment)); - serializedFragmentEntries.append((char*)Box::uint32_to_uint8(0)); - serializedFragmentEntries.append((char*)Box::uint32_to_uint8(FragmentRunEntryTable[i].FirstFragmentTimestamp)); - serializedFragmentEntries.append((char*)Box::uint32_to_uint8(FragmentRunEntryTable[i].FragmentDuration)); + serializedFragmentEntries.append((char*)Box::uint32_to_uint8(FragmentRunEntryTable[i].FirstFragment),4); + serializedFragmentEntries.append((char*)Box::uint32_to_uint8(0),4); + serializedFragmentEntries.append((char*)Box::uint32_to_uint8(FragmentRunEntryTable[i].FirstFragmentTimestamp),4); + serializedFragmentEntries.append((char*)Box::uint32_to_uint8(FragmentRunEntryTable[i].FragmentDuration)),4; if(FragmentRunEntryTable[i].FragmentDuration == 0) { - serializedFragmentEntries.append((char*)Box::uint8_to_uint8(FragmentRunEntryTable[i].DiscontinuityIndicator)); + serializedFragmentEntries.append((char*)Box::uint8_to_uint8(FragmentRunEntryTable[i].DiscontinuityIndicator),1); } } From 968455e316cb268734ecd89aad7297f86c6466f4 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Thu, 17 Feb 2011 08:09:25 +0100 Subject: [PATCH 072/788] Bugfix abst --- util/MP4/box_abst.cpp | 2 +- util/MP4/box_asrt.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/util/MP4/box_abst.cpp b/util/MP4/box_abst.cpp index be97f99a..7043f0f4 100644 --- a/util/MP4/box_abst.cpp +++ b/util/MP4/box_abst.cpp @@ -195,7 +195,7 @@ void Box_abst::WriteContent( ) { uint32_t OffsetSegmentRuntableCount = OffsetMetaData + curMetaData.size() + 1; uint32_t OffsetFragmentRuntableCount = OffsetSegmentRuntableCount + 4 + serializedSegments.size(); - temp[0] = 0 & ( curProfile << 6 ) & ( (uint8_t)isLive << 7 ) & ( (uint8_t)isUpdate << 7 ); + temp[0] = 0 + ( curProfile << 6 ) + ( (uint8_t)isLive << 7 ) + ( (uint8_t)isUpdate << 7 ); Container->SetPayload((uint32_t)serializedFragments.size(),(uint8_t*)serializedFragments.c_str(),OffsetFragmentRuntableCount+4); Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(FragmentAmount),OffsetFragmentRuntableCount); diff --git a/util/MP4/box_asrt.cpp b/util/MP4/box_asrt.cpp index a6ba2fbb..23553037 100644 --- a/util/MP4/box_asrt.cpp +++ b/util/MP4/box_asrt.cpp @@ -69,12 +69,12 @@ void Box_asrt::WriteContent( ) { } uint32_t OffsetSegmentRunEntryCount = 5 + serializedQualities.size(); - + for( uint32_t i = 0; i < SegmentRunEntryTable.size(); i ++ ) { Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(SegmentRunEntryTable[i].FragmentsPerSegment),(8*i)+OffsetSegmentRunEntryCount+8); Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(SegmentRunEntryTable[i].FirstSegment),(8*i)+OffsetSegmentRunEntryCount+4); } - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(SegmentRunEntryTable.size()),OffsetSegmentRunEntryCount); + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(SegmentRunEntryTable.size()),OffsetSegmentRunEntryCount); Container->SetPayload((uint32_t)serializedQualities.size(),(uint8_t*)serializedQualities.c_str(),5); Container->SetPayload((uint32_t)1,Box::uint8_to_uint8(QualitySegmentUrlModifiers.size()),4); Container->SetPayload((uint32_t)4,Box::uint32_to_uint8((isUpdate ? 1 : 0))); From 3b58555f8ddf8ad096f6ec510dce6fa809b4910f Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Thu, 17 Feb 2011 08:21:14 +0100 Subject: [PATCH 073/788] Bugfix abst --- util/MP4/box_abst.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/util/MP4/box_abst.cpp b/util/MP4/box_abst.cpp index 7043f0f4..e3211ed8 100644 --- a/util/MP4/box_abst.cpp +++ b/util/MP4/box_abst.cpp @@ -189,24 +189,24 @@ void Box_abst::WriteContent( ) { } } uint32_t OffsetServerEntryCount = 29 + curMovieIdentifier.size() + 1; - uint32_t OffsetQualityEntryCount = OffsetServerEntryCount + 4 + serializedServers.size(); - uint32_t OffsetDrmData = OffsetQualityEntryCount + 4 + serializedQualities.size(); + uint32_t OffsetQualityEntryCount = OffsetServerEntryCount + 1 + serializedServers.size(); + uint32_t OffsetDrmData = OffsetQualityEntryCount + 1 + serializedQualities.size(); uint32_t OffsetMetaData = OffsetDrmData + curDRM.size() + 1; uint32_t OffsetSegmentRuntableCount = OffsetMetaData + curMetaData.size() + 1; - uint32_t OffsetFragmentRuntableCount = OffsetSegmentRuntableCount + 4 + serializedSegments.size(); + uint32_t OffsetFragmentRuntableCount = OffsetSegmentRuntableCount + 1 + serializedSegments.size(); temp[0] = 0 + ( curProfile << 6 ) + ( (uint8_t)isLive << 7 ) + ( (uint8_t)isUpdate << 7 ); - Container->SetPayload((uint32_t)serializedFragments.size(),(uint8_t*)serializedFragments.c_str(),OffsetFragmentRuntableCount+4); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(FragmentAmount),OffsetFragmentRuntableCount); - Container->SetPayload((uint32_t)serializedSegments.size(),(uint8_t*)serializedSegments.c_str(),OffsetSegmentRuntableCount+4); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(SegmentAmount),OffsetSegmentRuntableCount); + Container->SetPayload((uint32_t)serializedFragments.size(),(uint8_t*)serializedFragments.c_str(),OffsetFragmentRuntableCount+1); + Container->SetPayload((uint32_t)1,Box::uint8_to_uint8(FragmentAmount),OffsetFragmentRuntableCount); + Container->SetPayload((uint32_t)serializedSegments.size(),(uint8_t*)serializedSegments.c_str(),OffsetSegmentRuntableCount+1); + Container->SetPayload((uint32_t)1,Box::uint8_to_uint8(SegmentAmount),OffsetSegmentRuntableCount); Container->SetPayload((uint32_t)curMetaData.size()+1,(uint8_t*)curMetaData.c_str(),OffsetMetaData); Container->SetPayload((uint32_t)curDRM.size()+1,(uint8_t*)curDRM.c_str(),OffsetDrmData); - Container->SetPayload((uint32_t)serializedQualities.size(),(uint8_t*)serializedQualities.c_str(),OffsetQualityEntryCount+4); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Qualities.size()),OffsetQualityEntryCount); - Container->SetPayload((uint32_t)serializedServers.size(),(uint8_t*)serializedServers.c_str(),OffsetServerEntryCount+4); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Servers.size()),OffsetServerEntryCount); + Container->SetPayload((uint32_t)serializedQualities.size(),(uint8_t*)serializedQualities.c_str(),OffsetQualityEntryCount+1); + Container->SetPayload((uint32_t)1,Box::uint8_to_uint8(Qualities.size()),OffsetQualityEntryCount); + Container->SetPayload((uint32_t)serializedServers.size(),(uint8_t*)serializedServers.c_str(),OffsetServerEntryCount+1); + Container->SetPayload((uint32_t)1,Box::uint8_to_uint8(Servers.size()),OffsetServerEntryCount); Container->SetPayload((uint32_t)curMovieIdentifier.size()+1,(uint8_t*)curMovieIdentifier.c_str(),29);//+1 for \0-terminated string... Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(curSMPTE),25); Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),21); From 893fb56454bf361215907e14ccf35d7906952d7c Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Thu, 17 Feb 2011 08:22:09 +0100 Subject: [PATCH 074/788] Bugfix abst --- util/MP4/box_afrt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/MP4/box_afrt.cpp b/util/MP4/box_afrt.cpp index 66c18b43..979f35be 100644 --- a/util/MP4/box_afrt.cpp +++ b/util/MP4/box_afrt.cpp @@ -83,7 +83,7 @@ void Box_afrt::WriteContent( ) { serializedFragmentEntries.append((char*)Box::uint32_to_uint8(FragmentRunEntryTable[i].FirstFragment),4); serializedFragmentEntries.append((char*)Box::uint32_to_uint8(0),4); serializedFragmentEntries.append((char*)Box::uint32_to_uint8(FragmentRunEntryTable[i].FirstFragmentTimestamp),4); - serializedFragmentEntries.append((char*)Box::uint32_to_uint8(FragmentRunEntryTable[i].FragmentDuration)),4; + serializedFragmentEntries.append((char*)Box::uint32_to_uint8(FragmentRunEntryTable[i].FragmentDuration),4); if(FragmentRunEntryTable[i].FragmentDuration == 0) { serializedFragmentEntries.append((char*)Box::uint8_to_uint8(FragmentRunEntryTable[i].DiscontinuityIndicator),1); } From 8144c34769be5eb1ecb333dfe6d4bf943b5cf5ce Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Thu, 17 Feb 2011 08:35:58 +0100 Subject: [PATCH 075/788] Bugfix abst --- util/MP4/interface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/MP4/interface.cpp b/util/MP4/interface.cpp index e724f05b..30133913 100644 --- a/util/MP4/interface.cpp +++ b/util/MP4/interface.cpp @@ -553,7 +553,7 @@ std::string Interface::GenerateLiveBootstrap( uint32_t CurMediaTime ) { //SetUpASRT asrt->SetUpdate(false); asrt->AddQualityEntry( "" ); - asrt->AddSegmentRunEntry( 1, 0 ); + asrt->AddSegmentRunEntry( 1, 0xFFFFFFFF ); asrt->WriteContent( ); //SetUpABST From a1ebdbaa7fb01a70a815c02006ce85497d280f51 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Sun, 20 Feb 2011 18:29:52 +0100 Subject: [PATCH 076/788] AFRT test --- util/MP4/interface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/MP4/interface.cpp b/util/MP4/interface.cpp index 30133913..1475a768 100644 --- a/util/MP4/interface.cpp +++ b/util/MP4/interface.cpp @@ -547,7 +547,7 @@ std::string Interface::GenerateLiveBootstrap( uint32_t CurMediaTime ) { afrt->SetUpdate(false); afrt->SetTimeScale( 1000 ); afrt->AddQualityEntry( "" ); - afrt->AddFragmentRunEntry( 1, 0, 0, 2 ); + afrt->AddFragmentRunEntry( 1, 0, 10000 ); afrt->WriteContent( ); //SetUpASRT From f4aafcef954753f0f635e66b7cca543d74e9dc12 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 13 Mar 2011 13:20:43 +0100 Subject: [PATCH 077/788] AMF parser sturdyfied en zo, also nieuw testprogramma FTW --- util/amf.cpp | 399 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 399 insertions(+) create mode 100644 util/amf.cpp diff --git a/util/amf.cpp b/util/amf.cpp new file mode 100644 index 00000000..2ef99d68 --- /dev/null +++ b/util/amf.cpp @@ -0,0 +1,399 @@ +#include +#include +#include + +#define AMF0_NUMBER 0x00 +#define AMF0_BOOL 0x01 +#define AMF0_STRING 0x02 +#define AMF0_OBJECT 0x03 +#define AMF0_MOVIECLIP 0x04 +#define AMF0_NULL 0x05 +#define AMF0_UNDEFINED 0x06 +#define AMF0_REFERENCE 0x07 +#define AMF0_ECMA_ARRAY 0x08 +#define AMF0_OBJ_END 0x09 +#define AMF0_STRICT_ARRAY 0x0A +#define AMF0_DATE 0x0B +#define AMF0_LONGSTRING 0x0C +#define AMF0_UNSUPPORTED 0x0D +#define AMF0_RECORDSET 0x0E +#define AMF0_XMLDOC 0x0F +#define AMF0_TYPED_OBJ 0x10 +#define AMF0_UPGRADE 0x11 +#define AMF0_DDV_CONTAINER 0xFF + +class AMFType { + public: + std::string Indice(){return myIndice;}; + unsigned char GetType(){return myType;}; + double NumValue(){return numval;}; + std::string StrValue(){return strval;}; + const char * Str(){return strval.c_str();}; + int hasContent(){ + if (!contents){return 0;} + return contents->size(); + }; + void addContent(AMFType c){if (contents != 0){contents->push_back(c);}}; + AMFType* getContentP(int i){if (contents != 0){return &contents->at(i);}else{return 0;}}; + AMFType getContent(int i){if (contents != 0){return contents->at(i);}else{return AMFType("error");}}; + AMFType* getContentP(std::string s){ + if (contents != 0){ + for (std::vector::iterator it = contents->begin(); it != contents->end(); it++){ + if (it->Indice() == s){ + return &(*it); + } + } + } + return this; + }; + AMFType getContent(std::string s){ + if (contents != 0){ + for (std::vector::iterator it = contents->begin(); it != contents->end(); it++){ + if (it->Indice() == s){ + return *it; + } + } + } + return AMFType("error"); + }; + AMFType(std::string indice, double val, unsigned char setType = AMF0_NUMBER){//num type initializer + myIndice = indice; + myType = setType; + strval = ""; + numval = val; + contents = 0; + }; + AMFType(std::string indice, std::string val, unsigned char setType = AMF0_STRING){//str type initializer + myIndice = indice; + myType = setType; + strval = val; + numval = 0; + contents = 0; + }; + AMFType(std::string indice, unsigned char setType = AMF0_OBJECT){//object type initializer + myIndice = indice; + myType = setType; + strval = ""; + numval = 0; + contents = new std::vector; + }; + ~AMFType(){if (contents != 0){delete contents;contents=0;}}; + AMFType& operator=(const AMFType &a) { + myIndice = a.myIndice; + myType = a.myType; + strval = a.strval; + numval = a.numval; + if (contents){ + if (a.contents != contents){ + delete contents; + if (a.contents){ + contents = new std::vector; + for (std::vector::iterator it = a.contents->begin(); it < a.contents->end(); it++){ + contents->push_back(*it); + } + }else{ + contents = 0; + } + } + }else{ + if (a.contents){ + contents = new std::vector; + for (std::vector::iterator it = a.contents->begin(); it < a.contents->end(); it++){ + contents->push_back(*it); + } + } + } + return *this; + };//= operator + AMFType(const AMFType &a){ + myIndice = a.myIndice; + myType = a.myType; + strval = a.strval; + numval = a.numval; + if (a.contents){ + contents = new std::vector; + for (std::vector::iterator it = a.contents->begin(); it < a.contents->end(); it++){ + contents->push_back(*it); + } + }else{contents = 0;} + };//copy constructor + void Print(std::string indent = ""){ + std::cerr << indent; + switch (myType){ + case AMF0_NUMBER: std::cerr << "Number"; break; + case AMF0_BOOL: std::cerr << "Bool"; break; + case AMF0_STRING://short string + case AMF0_LONGSTRING: std::cerr << "String"; break; + case AMF0_OBJECT: std::cerr << "Object"; break; + case AMF0_MOVIECLIP: std::cerr << "MovieClip"; break; + case AMF0_NULL: std::cerr << "Null"; break; + case AMF0_UNDEFINED: std::cerr << "Undefined"; break; + case AMF0_REFERENCE: std::cerr << "Reference"; break; + case AMF0_ECMA_ARRAY: std::cerr << "ECMA Array"; break; + case AMF0_OBJ_END: std::cerr << "Object end"; break; + case AMF0_STRICT_ARRAY: std::cerr << "Strict Array"; break; + case AMF0_DATE: std::cerr << "Date"; break; + case AMF0_UNSUPPORTED: std::cerr << "Unsupported"; break; + case AMF0_RECORDSET: std::cerr << "Recordset"; break; + case AMF0_XMLDOC: std::cerr << "XML Document"; break; + case AMF0_TYPED_OBJ: std::cerr << "Typed Object"; break; + case AMF0_UPGRADE: std::cerr << "Upgrade to AMF3"; break; + case AMF0_DDV_CONTAINER: std::cerr << "DDVTech Container"; break; + } + std::cerr << " " << myIndice << " "; + switch (myType){ + case AMF0_NUMBER: case AMF0_BOOL: case AMF0_REFERENCE: case AMF0_DATE: std::cerr << numval; break; + case AMF0_STRING: case AMF0_LONGSTRING: case AMF0_XMLDOC: case AMF0_TYPED_OBJ: std::cerr << strval; break; + } + std::cerr << std::endl; + if (contents){ + for (std::vector::iterator it = contents->begin(); it != contents->end(); it++){it->Print(indent+" ");} + } + };//print + std::string Pack(){ + std::string r = ""; + if ((myType == AMF0_STRING) && (strval.size() > 0xFFFF)){myType = AMF0_LONGSTRING;} + if (myType != AMF0_DDV_CONTAINER){r += myType;} + switch (myType){ + case AMF0_NUMBER: + r += *(((char*)&numval)+7); r += *(((char*)&numval)+6); + r += *(((char*)&numval)+5); r += *(((char*)&numval)+4); + r += *(((char*)&numval)+3); r += *(((char*)&numval)+2); + r += *(((char*)&numval)+1); r += *(((char*)&numval)); + break; + case AMF0_DATE: + r += *(((char*)&numval)+7); r += *(((char*)&numval)+6); + r += *(((char*)&numval)+5); r += *(((char*)&numval)+4); + r += *(((char*)&numval)+3); r += *(((char*)&numval)+2); + r += *(((char*)&numval)+1); r += *(((char*)&numval)); + r += (char)0;//timezone always 0 + r += (char)0;//timezone always 0 + break; + case AMF0_BOOL: + r += (char)numval; + break; + case AMF0_STRING: + r += strval.size() / 256; + r += strval.size() % 256; + r += strval; + break; + case AMF0_LONGSTRING: + case AMF0_XMLDOC://is always a longstring + r += strval.size() / (256*256*256); + r += strval.size() / (256*256); + r += strval.size() / 256; + r += strval.size() % 256; + r += strval; + break; + case AMF0_TYPED_OBJ: + r += Indice().size() / 256; + r += Indice().size() % 256; + r += Indice(); + //is an object, with the classname first + case AMF0_OBJECT: + if (contents){ + for (std::vector::iterator it = contents->begin(); it != contents->end(); it++){ + r += it->Indice().size() / 256; + r += it->Indice().size() % 256; + r += it->Indice(); + r += it->Pack(); + } + } + r += (char)0; r += (char)0; r += (char)9; + break; + case AMF0_MOVIECLIP: + case AMF0_NULL: + case AMF0_UNDEFINED: + case AMF0_RECORDSET: + case AMF0_UNSUPPORTED: + //no data to add + break; + case AMF0_REFERENCE: + r += (char)((int)numval / 256); + r += (char)((int)numval % 256); + break; + case AMF0_ECMA_ARRAY:{ + int arrlen = 0; + if (contents){ + arrlen = getContentP("length")->NumValue(); + r += arrlen / (256*256*256); r += arrlen / (256*256); r += arrlen / 256; r += arrlen % 256; + for (std::vector::iterator it = contents->begin(); it != contents->end(); it++){ + r += it->Indice().size() / 256; + r += it->Indice().size() % 256; + r += it->Indice(); + r += it->Pack(); + } + }else{ + r += (char)0; r += (char)0; r += (char)0; r += (char)0; + } + r += (char)0; r += (char)0; r += (char)9; + } break; + case AMF0_STRICT_ARRAY:{ + int arrlen = 0; + if (contents){ + arrlen = getContentP("length")->NumValue(); + r += arrlen / (256*256*256); r += arrlen / (256*256); r += arrlen / 256; r += arrlen % 256; + for (std::vector::iterator it = contents->begin(); it != contents->end(); it++){ + r += it->Pack(); + } + }else{ + r += (char)0; r += (char)0; r += (char)0; r += (char)0; + } + } break; + case AMF0_DDV_CONTAINER://only send contents + if (contents){ + for (std::vector::iterator it = contents->begin(); it != contents->end(); it++){ + r += it->Pack(); + } + } + break; + } + return r; + };//pack + protected: + std::string myIndice; + unsigned char myType; + std::string strval; + double numval; + std::vector * contents; +};//AMFType + +AMFType parseOneAMF(const unsigned char *& data, unsigned int &len, unsigned int &i, std::string name){ + std::string tmpstr; + unsigned int tmpi = 0; + unsigned char tmpdbl[8]; + switch (data[i]){ + case AMF0_NUMBER: + tmpdbl[7] = data[i+1]; + tmpdbl[6] = data[i+2]; + tmpdbl[5] = data[i+3]; + tmpdbl[4] = data[i+4]; + tmpdbl[3] = data[i+5]; + tmpdbl[2] = data[i+6]; + tmpdbl[1] = data[i+7]; + tmpdbl[0] = data[i+8]; + i+=9;//skip 8(a double)+1 forwards + return AMFType(name, *(double*)tmpdbl, AMF0_NUMBER); + break; + case AMF0_DATE: + tmpdbl[7] = data[i+1]; + tmpdbl[6] = data[i+2]; + tmpdbl[5] = data[i+3]; + tmpdbl[4] = data[i+4]; + tmpdbl[3] = data[i+5]; + tmpdbl[2] = data[i+6]; + tmpdbl[1] = data[i+7]; + tmpdbl[0] = data[i+8]; + i+=11;//skip 8(a double)+1+timezone(2) forwards + return AMFType(name, *(double*)tmpdbl, AMF0_DATE); + break; + case AMF0_BOOL: + i+=2;//skip bool+1 forwards + if (data[i-1] == 0){ + return AMFType(name, (double)0, AMF0_BOOL); + }else{ + return AMFType(name, (double)1, AMF0_BOOL); + } + break; + case AMF0_REFERENCE: + tmpi = data[i+1]*256+data[i+2];//get the ref number value as a double + i+=3;//skip ref+1 forwards + return AMFType(name, (double)tmpi, AMF0_REFERENCE); + break; + case AMF0_XMLDOC: + tmpi = data[i+1]*256*256*256+data[i+2]*256*256+data[i+3]*256+data[i+4];//set tmpi to UTF-8-long length + tmpstr.clear();//clean tmpstr, just to be sure + tmpstr.append((const char *)data+i+5, (size_t)tmpi);//add the string data + i += tmpi + 5;//skip length+size+1 forwards + return AMFType(name, tmpstr, AMF0_XMLDOC); + break; + case AMF0_LONGSTRING: + tmpi = data[i+1]*256*256*256+data[i+2]*256*256+data[i+3]*256+data[i+4];//set tmpi to UTF-8-long length + tmpstr.clear();//clean tmpstr, just to be sure + tmpstr.append((const char *)data+i+5, (size_t)tmpi);//add the string data + i += tmpi + 5;//skip length+size+1 forwards + return AMFType(name, tmpstr, AMF0_LONGSTRING); + break; + case AMF0_STRING: + tmpi = data[i+1]*256+data[i+2];//set tmpi to UTF-8 length + tmpstr.clear();//clean tmpstr, just to be sure + tmpstr.append((const char *)data+i+3, (size_t)tmpi);//add the string data + i += tmpi + 3;//skip length+size+1 forwards + return AMFType(name, tmpstr, AMF0_STRING); + break; + case AMF0_NULL: + case AMF0_UNDEFINED: + case AMF0_UNSUPPORTED: + ++i; + return AMFType(name, (double)0, data[i-1]); + break; + case AMF0_OBJECT:{ + ++i; + AMFType ret = AMFType(name, (unsigned char)AMF0_OBJECT); + while (data[i] + data[i+1] != 0){//while not encountering 0x0000 (we assume 0x000009) + tmpi = data[i]*256+data[i+1];//set tmpi to the UTF-8 length + tmpstr.clear();//clean tmpstr, just to be sure + tmpstr.append((const char*)data+i+2, (size_t)tmpi);//add the string data + i += tmpi + 2;//skip length+size forwards + ret.addContent(parseOneAMF(data, len, i, tmpstr));//add content, recursively parsed, updating i, setting indice to tmpstr + } + i += 3;//skip 0x000009 + return ret; + } break; + case AMF0_TYPED_OBJ:{ + ++i; + tmpi = data[i]*256+data[i+1];//set tmpi to the UTF-8 length + tmpstr.clear();//clean tmpstr, just to be sure + tmpstr.append((const char*)data+i+2, (size_t)tmpi);//add the string data + AMFType ret = AMFType(tmpstr, (unsigned char)AMF0_TYPED_OBJ);//the object is not named "name" but tmpstr + while (data[i] + data[i+1] != 0){//while not encountering 0x0000 (we assume 0x000009) + tmpi = data[i]*256+data[i+1];//set tmpi to the UTF-8 length + tmpstr.clear();//clean tmpstr, just to be sure + tmpstr.append((const char*)data+i+2, (size_t)tmpi);//add the string data + i += tmpi + 2;//skip length+size forwards + ret.addContent(parseOneAMF(data, len, i, tmpstr));//add content, recursively parsed, updating i, setting indice to tmpstr + } + i += 3;//skip 0x000009 + return ret; + } break; + case AMF0_ECMA_ARRAY:{ + ++i; + AMFType ret = AMFType(name, (unsigned char)AMF0_ECMA_ARRAY); + i += 4;//ignore the array length, we re-calculate it + while (data[i] + data[i+1] != 0){//while not encountering 0x0000 (we assume 0x000009) + tmpi = data[i]*256+data[i+1];//set tmpi to the UTF-8 length + tmpstr.clear();//clean tmpstr, just to be sure + tmpstr.append((const char*)data+i+2, (size_t)tmpi);//add the string data + i += tmpi + 2;//skip length+size forwards + ret.addContent(parseOneAMF(data, len, i, tmpstr));//add content, recursively parsed, updating i, setting indice to tmpstr + } + i += 3;//skip 0x000009 + return ret; + } break; + case AMF0_STRICT_ARRAY:{ + AMFType ret = AMFType(name, (unsigned char)AMF0_STRICT_ARRAY); + tmpi = data[i+1]*256*256*256+data[i+2]*256*256+data[i+3]*256+data[i+4];//set tmpi to array length + i += 5;//skip size+1 forwards + while (tmpi > 0){//while not done parsing array + ret.addContent(parseOneAMF(data, len, i, "arrVal"));//add content, recursively parsed, updating i + --tmpi; + } + return ret; + } break; + } + #if DEBUG >= 2 + fprintf(stderr, "Error: Unimplemented AMF type %hhx - returning.\n", data[i]); + #endif + return AMFType("error", (unsigned char)0xFF); +}//parseOneAMF + +AMFType parseAMF(const unsigned char * data, unsigned int len){ + AMFType ret("returned", (unsigned char)0xFF);//container type + unsigned int i = 0, j = 0; + while (i < len){ + ret.addContent(parseOneAMF(data, len, i, "")); + if (i > j){j = i;}else{return ret;} + } + return ret; +}//parseAMF +AMFType parseAMF(std::string data){return parseAMF((const unsigned char*)data.c_str(), data.size());} From ca04c4d2ee18b6ba57415dbc1affe7939fdb15a9 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 13 Mar 2011 13:31:55 +0100 Subject: [PATCH 078/788] Mystery solved --- util/amf.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/util/amf.cpp b/util/amf.cpp index 2ef99d68..5fa61863 100644 --- a/util/amf.cpp +++ b/util/amf.cpp @@ -262,6 +262,9 @@ AMFType parseOneAMF(const unsigned char *& data, unsigned int &len, unsigned int std::string tmpstr; unsigned int tmpi = 0; unsigned char tmpdbl[8]; + #if DEBUG >= 10 + fprintf(stderr, "Note: AMF type %hhx found. %i bytes left\n", data[i], len-i); + #endif switch (data[i]){ case AMF0_NUMBER: tmpdbl[7] = data[i+1]; @@ -397,3 +400,4 @@ AMFType parseAMF(const unsigned char * data, unsigned int len){ return ret; }//parseAMF AMFType parseAMF(std::string data){return parseAMF((const unsigned char*)data.c_str(), data.size());} + From 126037b06ac527bde671aa1d4e2c4decad989a7b Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 13 Mar 2011 15:52:00 +0100 Subject: [PATCH 079/788] Fixed array sizes --- util/amf.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/amf.cpp b/util/amf.cpp index 5fa61863..a600d12a 100644 --- a/util/amf.cpp +++ b/util/amf.cpp @@ -215,7 +215,7 @@ class AMFType { case AMF0_ECMA_ARRAY:{ int arrlen = 0; if (contents){ - arrlen = getContentP("length")->NumValue(); + arrlen = contents->size(); r += arrlen / (256*256*256); r += arrlen / (256*256); r += arrlen / 256; r += arrlen % 256; for (std::vector::iterator it = contents->begin(); it != contents->end(); it++){ r += it->Indice().size() / 256; @@ -231,7 +231,7 @@ class AMFType { case AMF0_STRICT_ARRAY:{ int arrlen = 0; if (contents){ - arrlen = getContentP("length")->NumValue(); + arrlen = contents->size(); r += arrlen / (256*256*256); r += arrlen / (256*256); r += arrlen / 256; r += arrlen % 256; for (std::vector::iterator it = contents->begin(); it != contents->end(); it++){ r += it->Pack(); From bfdc49afb60e3282b649a481e1b7dbaa8652d8de Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Sun, 13 Mar 2011 22:20:17 +0100 Subject: [PATCH 080/788] Fixed FPS Frames/Keyframes --- util/MP4/interface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/MP4/interface.cpp b/util/MP4/interface.cpp index 1475a768..f9ec94a7 100644 --- a/util/MP4/interface.cpp +++ b/util/MP4/interface.cpp @@ -547,7 +547,7 @@ std::string Interface::GenerateLiveBootstrap( uint32_t CurMediaTime ) { afrt->SetUpdate(false); afrt->SetTimeScale( 1000 ); afrt->AddQualityEntry( "" ); - afrt->AddFragmentRunEntry( 1, 0, 10000 ); + afrt->AddFragmentRunEntry( 1, CurMediaTime, 4000 ); afrt->WriteContent( ); //SetUpASRT From 02c7ea516bdbd70d459066a77944fc44647a40b9 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Sun, 13 Mar 2011 22:28:22 +0100 Subject: [PATCH 081/788] Test --- util/MP4/interface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/MP4/interface.cpp b/util/MP4/interface.cpp index f9ec94a7..b6d412da 100644 --- a/util/MP4/interface.cpp +++ b/util/MP4/interface.cpp @@ -559,7 +559,7 @@ std::string Interface::GenerateLiveBootstrap( uint32_t CurMediaTime ) { //SetUpABST abst->SetBootstrapVersion( 1 ); abst->SetProfile( 0 ); - abst->SetLive( true ); +// abst->SetLive( true ); abst->SetUpdate( false ); abst->SetTimeScale( 1000 ); abst->SetMediaTime( CurMediaTime ); From d261bca44af6ba980b267605924a05a1ee95108f Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Wed, 16 Mar 2011 10:56:17 +0100 Subject: [PATCH 082/788] ABST Box Edit --- util/MP4/box_abst.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/util/MP4/box_abst.cpp b/util/MP4/box_abst.cpp index e3211ed8..61fcb5a0 100644 --- a/util/MP4/box_abst.cpp +++ b/util/MP4/box_abst.cpp @@ -29,6 +29,7 @@ class Box_abst { void AddQualityEntry( std::string Quality = "", uint32_t Offset = 0 ); void AddSegmentRunTable( Box * newSegment, uint32_t Offset = 0 ); void AddFragmentRunTable( Box * newFragment, uint32_t Offset = 0 ); + void SetVersion( bool NewVersion = 0 ); void WriteContent( ); private: void SetDefaults( ); @@ -37,6 +38,7 @@ class Box_abst { uint8_t curProfile; bool isLive; bool isUpdate; + bool Version; uint32_t curTimeScale; uint32_t curMediatime;//write as uint64_t uint32_t curSMPTE;//write as uint64_t @@ -52,6 +54,7 @@ class Box_abst { Box_abst::Box_abst( ) { Container = new Box( 0x61627374 ); + SetDefaults( ); } Box_abst::~Box_abst() { @@ -136,7 +139,6 @@ void Box_abst::AddFragmentRunTable( Box * newFragment, uint32_t Offset ) { FragmentRunTables[Offset] = newFragment; } - void Box_abst::SetDefaults( ) { SetProfile( ); SetLive( ); @@ -147,8 +149,14 @@ void Box_abst::SetDefaults( ) { SetMovieIdentifier( ); SetDRM( ); SetMetaData( ); + SetVersion( ); } +void SetVersion( bool NewVersion) { + Version = NewVersion; +} + + void Box_abst::SetReserved( ) { Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0)); } From bc6748bf3cf09d1fc1ecfd623825aaf9626c57fd Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Sat, 19 Mar 2011 14:36:09 +0100 Subject: [PATCH 083/788] Box edits for parser --- util/MP4/box.cpp | 180 ++++++++++++++++++++++++++++++++++++++++++ util/MP4/box_abst.cpp | 2 +- 2 files changed, 181 insertions(+), 1 deletion(-) diff --git a/util/MP4/box.cpp b/util/MP4/box.cpp index 458ba15d..f9b8279e 100644 --- a/util/MP4/box.cpp +++ b/util/MP4/box.cpp @@ -6,6 +6,8 @@ #include #include #include +#include +#include struct BoxHeader { uint32_t TotalSize; @@ -16,6 +18,7 @@ class Box { public: Box(); Box(uint32_t BoxType); + Box(uint8_t * Content, uint32_t length); ~Box(); void SetBoxType(uint32_t BoxType); uint32_t GetBoxType(); @@ -30,6 +33,7 @@ class Box { static uint8_t * uint8_to_uint8( uint8_t data ); BoxHeader GetHeader( ); void ResetPayload( ); + void Parse( std::string PrintOffset = "" ); private: BoxHeader header; uint8_t * Payload; @@ -47,6 +51,21 @@ Box::Box(uint32_t BoxType) { PayloadSize = 0; } +Box::Box(uint8_t * Content, uint32_t length) { + header.TotalSize = (Content[0] << 24) + (Content[1] << 16) + (Content[2] << 8) + (Content[3]); + if(header.TotalSize != length) { std::cerr << "Warning: length sizes differ\n"; } + header.BoxType = (Content[4] << 24) + (Content[5] << 16) + (Content[6] << 8) + (Content[7]); + std::cerr << "Created new box with type \"" + << (char)(header.BoxType >> 24) + << (char)((header.BoxType << 8) >> 24) + << (char)((header.BoxType << 16) >> 24) + << (char)((header.BoxType << 24) >> 24) + << "\"\n"; + PayloadSize = length-8; + Payload = new uint8_t[PayloadSize]; + memcpy( Payload, &Content[8], PayloadSize ); +} + Box::~Box() { } @@ -148,3 +167,164 @@ void Box::ResetPayload( ) { Payload = NULL; } } + +void Box::Parse( std::string PrintOffset ) { + if( header.BoxType == 0x61627374 ) { + uint8_t Version = Payload[0]; + uint32_t Flags = (Payload[1] << 16) + (Payload[2] << 8) + (Payload[3]); //uint24_t + uint32_t BootstrapInfoVersion = (Payload[4] << 24) + (Payload[5] << 16) +(Payload[6] << 8) + (Payload[7]); + uint8_t Profile = (Payload[8] >> 6); //uint2_t + uint8_t Live = (( Payload[8] >> 5 ) & 0x1); //uint1_t + uint8_t Update = (( Payload[8] >> 4 ) & 0x1); //uint1_t + uint8_t Reserved = ( Payload[8] & 0x4); //uint4_t + uint32_t Timescale = (Payload[9] << 24) + (Payload[10] << 16) +(Payload[11] << 8) + (Payload[12]); + uint32_t CurrentMediaTime_Upperhalf = (Payload[13] << 24) + (Payload[14] << 16) +(Payload[15] << 8) + (Payload[16]); + uint32_t CurrentMediaTime_Lowerhalf = (Payload[17] << 24) + (Payload[18] << 16) +(Payload[19] << 8) + (Payload[20]); + uint32_t SmpteTimeCodeOffset_Upperhalf = (Payload[21] << 24) + (Payload[22] << 16) +(Payload[23] << 8) + (Payload[24]); + uint32_t SmpteTimeCodeOffset_Lowerhalf = (Payload[25] << 24) + (Payload[26] << 16) +(Payload[27] << 8) + (Payload[28]); + + std::string MovieIdentifier; + uint8_t ServerEntryCount = -1; + std::vector ServerEntryTable; + uint8_t QualityEntryCount = -1; + std::vector QualityEntryTable; + std::string DrmData; + std::string MetaData; + uint8_t SegmentRunTableCount = -1; + std::vector SegmentRunTableEntries; + uint8_t FragmentRunTableCount = -1; + std::vector FragmentRunTableEntries; + + uint32_t CurrentOffset = 29; + uint32_t TempSize; + Box* TempBox; + std::string temp; + while( Payload[CurrentOffset] != '\0' ) { MovieIdentifier += Payload[CurrentOffset]; CurrentOffset ++; } + CurrentOffset ++; + ServerEntryCount = Payload[CurrentOffset]; + CurrentOffset ++; + for( uint8_t i = 0; i < ServerEntryCount; i++ ) { + temp = ""; + while( Payload[CurrentOffset] != '\0' ) { temp += Payload[CurrentOffset]; CurrentOffset ++; } + ServerEntryTable.push_back(temp); + CurrentOffset++; + } + QualityEntryCount = Payload[CurrentOffset]; + CurrentOffset ++; + for( uint8_t i = 0; i < QualityEntryCount; i++ ) { + temp = ""; + while( Payload[CurrentOffset] != '\0' ) { temp += Payload[CurrentOffset]; CurrentOffset ++; } + QualityEntryTable.push_back(temp); + CurrentOffset++; + } + while( Payload[CurrentOffset] != '\0' ) { DrmData += Payload[CurrentOffset]; CurrentOffset ++; } + CurrentOffset ++; + while( Payload[CurrentOffset] != '\0' ) { MetaData += Payload[CurrentOffset]; CurrentOffset ++; } + CurrentOffset ++; + SegmentRunTableCount = Payload[CurrentOffset]; + CurrentOffset ++; + for( uint8_t i = 0; i < SegmentRunTableCount; i++ ) { + TempSize = (Payload[CurrentOffset] << 24) + (Payload[CurrentOffset+1]<< 16) + (Payload[CurrentOffset+2]<< 8) + (Payload[CurrentOffset+3]); + TempBox = new Box( &Payload[CurrentOffset], TempSize ); + SegmentRunTableEntries.push_back(TempBox); + CurrentOffset += TempSize; + } + FragmentRunTableCount = Payload[CurrentOffset]; + CurrentOffset ++; + for( uint8_t i = 0; i < FragmentRunTableCount; i++ ) { + TempSize = (Payload[CurrentOffset] << 24) + (Payload[CurrentOffset+1]<< 16) + (Payload[CurrentOffset+2]<< 8) + (Payload[CurrentOffset+3]); + TempBox = new Box( &Payload[CurrentOffset], TempSize ); + FragmentRunTableEntries.push_back(TempBox); + CurrentOffset += TempSize; + } + + std::cerr << "Box_ABST:\n"; + std::cerr << PrintOffset << " Version: " << (int)Version << "\n"; + std::cerr << PrintOffset << " Flags: " << (int)Flags << "\n"; + std::cerr << PrintOffset << " BootstrapInfoVersion: " << (int)BootstrapInfoVersion << "\n"; + std::cerr << PrintOffset << " Profile: " << (int)Profile << "\n"; + std::cerr << PrintOffset << " Live: " << (int)Live << "\n"; + std::cerr << PrintOffset << " Update: " << (int)Update << "\n"; + std::cerr << PrintOffset << " Reserved: " << (int)Reserved << "\n"; + std::cerr << PrintOffset << " Timescale: " << (int)Timescale << "\n"; + std::cerr << PrintOffset << " CurrentMediaTime: " << (int)CurrentMediaTime_Upperhalf << " " << CurrentMediaTime_Lowerhalf << "\n"; + std::cerr << PrintOffset << " SmpteTimeCodeOffset: " << (int)SmpteTimeCodeOffset_Upperhalf << " " << SmpteTimeCodeOffset_Lowerhalf << "\n"; + std::cerr << PrintOffset << " MovieIdentifier: " << MovieIdentifier << "\n"; + std::cerr << PrintOffset << " ServerEntryCount: " << (int)ServerEntryCount << "\n"; + std::cerr << PrintOffset << " ServerEntryTable:\n"; + for( uint32_t i = 0; i < ServerEntryTable.size( ); i++ ) { + std::cerr << PrintOffset << " " << i+1 << ": " << ServerEntryTable[i] << "\n"; + } + std::cerr << PrintOffset << " QualityEntryCount: " << (int)QualityEntryCount << "\n"; + std::cerr << PrintOffset << " QualityEntryTable:\n"; + for( uint32_t i = 0; i < QualityEntryTable.size( ); i++ ) { + std::cerr << PrintOffset << " " << i+1 << ": " << QualityEntryTable[i] << "\n"; + } + std::cerr << PrintOffset << " DrmData: " << DrmData << "\n"; + std::cerr << PrintOffset << " MetaData: " << MetaData << "\n"; + std::cerr << PrintOffset << " SegmentRunTableCount: " << (int)SegmentRunTableCount << "\n"; + std::cerr << PrintOffset << " SegmentRunTableEntries:\n"; + for( uint32_t i = 0; i < SegmentRunTableEntries.size( ); i++ ) { + std::cerr << PrintOffset << " " << i+1 << ": "; + SegmentRunTableEntries[i]->Parse( PrintOffset+" "); + } + std::cerr << PrintOffset << " FragmentRunTableCount: " << (int)FragmentRunTableCount << "\n"; + std::cerr << PrintOffset << " FragmentRunTableEntries:\n"; + for( uint32_t i = 0; i < FragmentRunTableEntries.size( ); i++ ) { + std::cerr << PrintOffset << " " << i+1 << ": "; + FragmentRunTableEntries[i]->Parse( PrintOffset+" "); + } + + } else if ( header.BoxType == 0x61737274 ) { + uint8_t Version = Payload[0]; + uint32_t Flags = (Payload[1] << 16) + (Payload[2] << 8) + (Payload[3]); //uint24_t + uint8_t QualityEntryCount; + std::vector QualitySegmentUrlModifiers; + uint32_t SegmentRunEntryCount; + std::vector< std::pair > SegmentRunEntryTable; + + uint32_t CurrentOffset = 4; + std::string temp; + std::pair TempPair; + QualityEntryCount = Payload[CurrentOffset]; + CurrentOffset ++; + for( uint8_t i = 0; i < QualityEntryCount; i++ ) { + temp = ""; + while( Payload[CurrentOffset] != '\0' ) { temp += Payload[CurrentOffset]; CurrentOffset ++; } + QualitySegmentUrlModifiers.push_back(temp); + CurrentOffset++; + } + SegmentRunEntryCount = Payload[CurrentOffset]; + CurrentOffset ++; + for( uint8_t i = 0; i < SegmentRunEntryCount; i++ ) { + TempPair.first = (Payload[CurrentOffset] << 24) + (Payload[CurrentOffset+1] << 16) + (Payload[CurrentOffset+2] << 8) + (Payload[CurrentOffset+3]); + CurrentOffset+=4; + TempPair.second = (Payload[CurrentOffset] << 24) + (Payload[CurrentOffset+1] << 16) + (Payload[CurrentOffset+2] << 8) + (Payload[CurrentOffset+3]); + CurrentOffset+=4; + SegmentRunEntryTable.push_back(TempPair); + } + + std::cerr << "Box_ASRT:\n"; + std::cerr << PrintOffset << " Version: " << (int)Version << "\n"; + std::cerr << PrintOffset << " Flags: " << (int)Flags << "\n"; + std::cerr << PrintOffset << " QualityEntryCount: " << (int)QualityEntryCount << "\n"; + std::cerr << PrintOffset << " QualitySegmentUrlModifiers:\n"; + for( uint32_t i = 0; i < QualitySegmentUrlModifiers.size( ); i++ ) { + std::cerr << PrintOffset << " " << i+1 << ": " << QualitySegmentUrlModifiers[i] << "\n"; + } + std::cerr << PrintOffset << " SegmentRunEntryCount: " << (int)QualityEntryCount << "\n"; + std::cerr << PrintOffset << " SegmentRunEntryTable:\n"; + for( uint32_t i = 0; i < SegmentRunEntryTable.size( ); i++ ) { + std::cerr << PrintOffset << " " << i+1 << ":\n"; + std::cerr << PrintOffset << " FirstSegment: " << SegmentRunEntryTable[i].first << "\n"; + std::cerr << PrintOffset << " FragmentsPerSegment: " << SegmentRunEntryTable[i].second << "\n"; + } + } else { + std::cerr << "BoxType '" + << (char)(header.BoxType >> 24) + << (char)((header.BoxType << 8) >> 24) + << (char)((header.BoxType << 16) >> 24) + << (char)((header.BoxType << 24) >> 24) + << "' not yet implemented!\n"; + } +} diff --git a/util/MP4/box_abst.cpp b/util/MP4/box_abst.cpp index 61fcb5a0..129a423c 100644 --- a/util/MP4/box_abst.cpp +++ b/util/MP4/box_abst.cpp @@ -152,7 +152,7 @@ void Box_abst::SetDefaults( ) { SetVersion( ); } -void SetVersion( bool NewVersion) { +void Box_abst::SetVersion( bool NewVersion) { Version = NewVersion; } From 645a1d09108f3ec3af292c0c48951b477c91fd91 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Sat, 19 Mar 2011 15:15:58 +0100 Subject: [PATCH 084/788] Box_Parser finished --- util/MP4/box.cpp | 72 +++++++++++++++++++++++++++++++++++++++++-- util/MP4/box_afrt.cpp | 2 ++ 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/util/MP4/box.cpp b/util/MP4/box.cpp index f9b8279e..2e948bf6 100644 --- a/util/MP4/box.cpp +++ b/util/MP4/box.cpp @@ -9,6 +9,14 @@ #include #include +struct afrt_fragmentrunentry { + uint32_t FirstFragment; + uint32_t FirstFragmentTimestamp_Upperhalf; //write as uint64_t + uint32_t FirstFragmentTimestamp; //write as uint64_t + uint32_t FragmentDuration; + uint8_t DiscontinuityIndicator;//if FragmentDuration == 0 +};//afrt_fragmentrunentry + struct BoxHeader { uint32_t TotalSize; uint32_t BoxType; @@ -294,8 +302,8 @@ void Box::Parse( std::string PrintOffset ) { QualitySegmentUrlModifiers.push_back(temp); CurrentOffset++; } - SegmentRunEntryCount = Payload[CurrentOffset]; - CurrentOffset ++; + SegmentRunEntryCount = (Payload[CurrentOffset] << 24) + (Payload[CurrentOffset+1] << 16) + (Payload[CurrentOffset+2]) + (Payload[CurrentOffset+3]); + CurrentOffset +=4; for( uint8_t i = 0; i < SegmentRunEntryCount; i++ ) { TempPair.first = (Payload[CurrentOffset] << 24) + (Payload[CurrentOffset+1] << 16) + (Payload[CurrentOffset+2] << 8) + (Payload[CurrentOffset+3]); CurrentOffset+=4; @@ -312,13 +320,71 @@ void Box::Parse( std::string PrintOffset ) { for( uint32_t i = 0; i < QualitySegmentUrlModifiers.size( ); i++ ) { std::cerr << PrintOffset << " " << i+1 << ": " << QualitySegmentUrlModifiers[i] << "\n"; } - std::cerr << PrintOffset << " SegmentRunEntryCount: " << (int)QualityEntryCount << "\n"; + std::cerr << PrintOffset << " SegmentRunEntryCount: " << (int)SegmentRunEntryCount << "\n"; std::cerr << PrintOffset << " SegmentRunEntryTable:\n"; for( uint32_t i = 0; i < SegmentRunEntryTable.size( ); i++ ) { std::cerr << PrintOffset << " " << i+1 << ":\n"; std::cerr << PrintOffset << " FirstSegment: " << SegmentRunEntryTable[i].first << "\n"; std::cerr << PrintOffset << " FragmentsPerSegment: " << SegmentRunEntryTable[i].second << "\n"; } + } else if ( header.BoxType == 0x61667274 ) { + uint8_t Version = Payload[0]; + uint32_t Flags = (Payload[1] << 16) + (Payload[2] << 8) + (Payload[3]); //uint24_t + uint32_t TimeScale = (Payload[4] << 24) + (Payload[5] << 16) + (Payload[6] << 8) + (Payload[7]); + uint8_t QualityEntryCount; + std::vector QualitySegmentUrlModifiers; + uint32_t FragmentRunEntryCount; + std::vector FragmentRunEntryTable; + + uint32_t CurrentOffset = 8; + std::string temp; + afrt_fragmentrunentry TempEntry; + QualityEntryCount = Payload[CurrentOffset]; + CurrentOffset ++; + for( uint8_t i = 0; i < QualityEntryCount; i++ ) { + temp = ""; + while( Payload[CurrentOffset] != '\0' ) { temp += Payload[CurrentOffset]; CurrentOffset ++; } + QualitySegmentUrlModifiers.push_back(temp); + CurrentOffset++; + } + FragmentRunEntryCount = (Payload[CurrentOffset] << 24) + (Payload[CurrentOffset+1] << 16) + (Payload[CurrentOffset+2]) + (Payload[CurrentOffset+3]); + CurrentOffset +=4; + for( uint8_t i = 0; i < FragmentRunEntryCount; i ++ ) { + TempEntry.FirstFragment = (Payload[CurrentOffset] << 24) + (Payload[CurrentOffset+1] << 16) + (Payload[CurrentOffset+2]) + (Payload[CurrentOffset+3]); + CurrentOffset +=4; + TempEntry.FirstFragmentTimestamp_Upperhalf = (Payload[CurrentOffset] << 24) + (Payload[CurrentOffset+1] << 16) + (Payload[CurrentOffset+2]) + (Payload[CurrentOffset+3]); + CurrentOffset +=4; + TempEntry.FirstFragmentTimestamp = (Payload[CurrentOffset] << 24) + (Payload[CurrentOffset+1] << 16) + (Payload[CurrentOffset+2]) + (Payload[CurrentOffset+3]); + CurrentOffset +=4; + TempEntry.FragmentDuration = (Payload[CurrentOffset] << 24) + (Payload[CurrentOffset+1] << 16) + (Payload[CurrentOffset+2]) + (Payload[CurrentOffset+3]); + CurrentOffset +=4; + if( TempEntry.FragmentDuration == 0 ) { + TempEntry.DiscontinuityIndicator = Payload[CurrentOffset]; + CurrentOffset++; + } + FragmentRunEntryTable.push_back(TempEntry); + } + + std::cerr << "Box_AFRT:\n"; + std::cerr << PrintOffset << " Version: " << (int)Version << "\n"; + std::cerr << PrintOffset << " Flags: " << (int)Flags << "\n"; + std::cerr << PrintOffset << " Timescale: " << (int)TimeScale << "\n"; + std::cerr << PrintOffset << " QualityEntryCount: " << (int)QualityEntryCount << "\n"; + std::cerr << PrintOffset << " QualitySegmentUrlModifiers:\n"; + for( uint32_t i = 0; i < QualitySegmentUrlModifiers.size( ); i++ ) { + std::cerr << PrintOffset << " " << i+1 << ": " << QualitySegmentUrlModifiers[i] << "\n"; + } + std::cerr << PrintOffset << " FragmentRunEntryCount: " << (int)FragmentRunEntryCount << "\n"; + std::cerr << PrintOffset << " FragmentRunEntryTable:\n"; + for( uint32_t i = 0; i < FragmentRunEntryTable.size( ); i++ ) { + std::cerr << PrintOffset << " " << i+1 << ":\n"; + std::cerr << PrintOffset << " FirstFragment: " << FragmentRunEntryTable[i].FirstFragment << "\n"; + std::cerr << PrintOffset << " FirstFragmentTimestamp: " << FragmentRunEntryTable[i].FirstFragmentTimestamp_Upperhalf << FragmentRunEntryTable[i].FirstFragmentTimestamp << "\n"; + std::cerr << PrintOffset << " FragmentDuration: " << FragmentRunEntryTable[i].FragmentDuration << "\n"; + if( FragmentRunEntryTable[i].FragmentDuration == 0 ) { + std::cerr << PrintOffset << " DiscontinuityIndicator: " << (int)FragmentRunEntryTable[i].DiscontinuityIndicator << "\n"; + } + } } else { std::cerr << "BoxType '" << (char)(header.BoxType >> 24) diff --git a/util/MP4/box_afrt.cpp b/util/MP4/box_afrt.cpp index 979f35be..dd1aad62 100644 --- a/util/MP4/box_afrt.cpp +++ b/util/MP4/box_afrt.cpp @@ -2,12 +2,14 @@ #include #include +/* struct afrt_fragmentrunentry { uint32_t FirstFragment; uint32_t FirstFragmentTimestamp; //write as uint64_t uint32_t FragmentDuration; uint8_t DiscontinuityIndicator;//if FragmentDuration == 0 };//afrt_fragmentrunentry +*/ class Box_afrt { public: From 9daf3e37be77250908656cedfe3f74891e85cf9a Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Sat, 19 Mar 2011 19:09:40 +0100 Subject: [PATCH 085/788] Tussentijdse commit --- util/MP4/box.cpp | 226 +----------------------------------------- util/MP4/box_abst.cpp | 3 +- util/MP4/box_asrt.cpp | 6 ++ 3 files changed, 12 insertions(+), 223 deletions(-) diff --git a/util/MP4/box.cpp b/util/MP4/box.cpp index 2e948bf6..be18b31e 100644 --- a/util/MP4/box.cpp +++ b/util/MP4/box.cpp @@ -41,11 +41,12 @@ class Box { static uint8_t * uint8_to_uint8( uint8_t data ); BoxHeader GetHeader( ); void ResetPayload( ); - void Parse( std::string PrintOffset = "" ); - private: - BoxHeader header; + void Parse( std::string PrintOffset ); + void * Parse( ); uint8_t * Payload; + BoxHeader header; uint32_t PayloadSize; + private: };//Box Class Box::Box() { @@ -175,222 +176,3 @@ void Box::ResetPayload( ) { Payload = NULL; } } - -void Box::Parse( std::string PrintOffset ) { - if( header.BoxType == 0x61627374 ) { - uint8_t Version = Payload[0]; - uint32_t Flags = (Payload[1] << 16) + (Payload[2] << 8) + (Payload[3]); //uint24_t - uint32_t BootstrapInfoVersion = (Payload[4] << 24) + (Payload[5] << 16) +(Payload[6] << 8) + (Payload[7]); - uint8_t Profile = (Payload[8] >> 6); //uint2_t - uint8_t Live = (( Payload[8] >> 5 ) & 0x1); //uint1_t - uint8_t Update = (( Payload[8] >> 4 ) & 0x1); //uint1_t - uint8_t Reserved = ( Payload[8] & 0x4); //uint4_t - uint32_t Timescale = (Payload[9] << 24) + (Payload[10] << 16) +(Payload[11] << 8) + (Payload[12]); - uint32_t CurrentMediaTime_Upperhalf = (Payload[13] << 24) + (Payload[14] << 16) +(Payload[15] << 8) + (Payload[16]); - uint32_t CurrentMediaTime_Lowerhalf = (Payload[17] << 24) + (Payload[18] << 16) +(Payload[19] << 8) + (Payload[20]); - uint32_t SmpteTimeCodeOffset_Upperhalf = (Payload[21] << 24) + (Payload[22] << 16) +(Payload[23] << 8) + (Payload[24]); - uint32_t SmpteTimeCodeOffset_Lowerhalf = (Payload[25] << 24) + (Payload[26] << 16) +(Payload[27] << 8) + (Payload[28]); - - std::string MovieIdentifier; - uint8_t ServerEntryCount = -1; - std::vector ServerEntryTable; - uint8_t QualityEntryCount = -1; - std::vector QualityEntryTable; - std::string DrmData; - std::string MetaData; - uint8_t SegmentRunTableCount = -1; - std::vector SegmentRunTableEntries; - uint8_t FragmentRunTableCount = -1; - std::vector FragmentRunTableEntries; - - uint32_t CurrentOffset = 29; - uint32_t TempSize; - Box* TempBox; - std::string temp; - while( Payload[CurrentOffset] != '\0' ) { MovieIdentifier += Payload[CurrentOffset]; CurrentOffset ++; } - CurrentOffset ++; - ServerEntryCount = Payload[CurrentOffset]; - CurrentOffset ++; - for( uint8_t i = 0; i < ServerEntryCount; i++ ) { - temp = ""; - while( Payload[CurrentOffset] != '\0' ) { temp += Payload[CurrentOffset]; CurrentOffset ++; } - ServerEntryTable.push_back(temp); - CurrentOffset++; - } - QualityEntryCount = Payload[CurrentOffset]; - CurrentOffset ++; - for( uint8_t i = 0; i < QualityEntryCount; i++ ) { - temp = ""; - while( Payload[CurrentOffset] != '\0' ) { temp += Payload[CurrentOffset]; CurrentOffset ++; } - QualityEntryTable.push_back(temp); - CurrentOffset++; - } - while( Payload[CurrentOffset] != '\0' ) { DrmData += Payload[CurrentOffset]; CurrentOffset ++; } - CurrentOffset ++; - while( Payload[CurrentOffset] != '\0' ) { MetaData += Payload[CurrentOffset]; CurrentOffset ++; } - CurrentOffset ++; - SegmentRunTableCount = Payload[CurrentOffset]; - CurrentOffset ++; - for( uint8_t i = 0; i < SegmentRunTableCount; i++ ) { - TempSize = (Payload[CurrentOffset] << 24) + (Payload[CurrentOffset+1]<< 16) + (Payload[CurrentOffset+2]<< 8) + (Payload[CurrentOffset+3]); - TempBox = new Box( &Payload[CurrentOffset], TempSize ); - SegmentRunTableEntries.push_back(TempBox); - CurrentOffset += TempSize; - } - FragmentRunTableCount = Payload[CurrentOffset]; - CurrentOffset ++; - for( uint8_t i = 0; i < FragmentRunTableCount; i++ ) { - TempSize = (Payload[CurrentOffset] << 24) + (Payload[CurrentOffset+1]<< 16) + (Payload[CurrentOffset+2]<< 8) + (Payload[CurrentOffset+3]); - TempBox = new Box( &Payload[CurrentOffset], TempSize ); - FragmentRunTableEntries.push_back(TempBox); - CurrentOffset += TempSize; - } - - std::cerr << "Box_ABST:\n"; - std::cerr << PrintOffset << " Version: " << (int)Version << "\n"; - std::cerr << PrintOffset << " Flags: " << (int)Flags << "\n"; - std::cerr << PrintOffset << " BootstrapInfoVersion: " << (int)BootstrapInfoVersion << "\n"; - std::cerr << PrintOffset << " Profile: " << (int)Profile << "\n"; - std::cerr << PrintOffset << " Live: " << (int)Live << "\n"; - std::cerr << PrintOffset << " Update: " << (int)Update << "\n"; - std::cerr << PrintOffset << " Reserved: " << (int)Reserved << "\n"; - std::cerr << PrintOffset << " Timescale: " << (int)Timescale << "\n"; - std::cerr << PrintOffset << " CurrentMediaTime: " << (int)CurrentMediaTime_Upperhalf << " " << CurrentMediaTime_Lowerhalf << "\n"; - std::cerr << PrintOffset << " SmpteTimeCodeOffset: " << (int)SmpteTimeCodeOffset_Upperhalf << " " << SmpteTimeCodeOffset_Lowerhalf << "\n"; - std::cerr << PrintOffset << " MovieIdentifier: " << MovieIdentifier << "\n"; - std::cerr << PrintOffset << " ServerEntryCount: " << (int)ServerEntryCount << "\n"; - std::cerr << PrintOffset << " ServerEntryTable:\n"; - for( uint32_t i = 0; i < ServerEntryTable.size( ); i++ ) { - std::cerr << PrintOffset << " " << i+1 << ": " << ServerEntryTable[i] << "\n"; - } - std::cerr << PrintOffset << " QualityEntryCount: " << (int)QualityEntryCount << "\n"; - std::cerr << PrintOffset << " QualityEntryTable:\n"; - for( uint32_t i = 0; i < QualityEntryTable.size( ); i++ ) { - std::cerr << PrintOffset << " " << i+1 << ": " << QualityEntryTable[i] << "\n"; - } - std::cerr << PrintOffset << " DrmData: " << DrmData << "\n"; - std::cerr << PrintOffset << " MetaData: " << MetaData << "\n"; - std::cerr << PrintOffset << " SegmentRunTableCount: " << (int)SegmentRunTableCount << "\n"; - std::cerr << PrintOffset << " SegmentRunTableEntries:\n"; - for( uint32_t i = 0; i < SegmentRunTableEntries.size( ); i++ ) { - std::cerr << PrintOffset << " " << i+1 << ": "; - SegmentRunTableEntries[i]->Parse( PrintOffset+" "); - } - std::cerr << PrintOffset << " FragmentRunTableCount: " << (int)FragmentRunTableCount << "\n"; - std::cerr << PrintOffset << " FragmentRunTableEntries:\n"; - for( uint32_t i = 0; i < FragmentRunTableEntries.size( ); i++ ) { - std::cerr << PrintOffset << " " << i+1 << ": "; - FragmentRunTableEntries[i]->Parse( PrintOffset+" "); - } - - } else if ( header.BoxType == 0x61737274 ) { - uint8_t Version = Payload[0]; - uint32_t Flags = (Payload[1] << 16) + (Payload[2] << 8) + (Payload[3]); //uint24_t - uint8_t QualityEntryCount; - std::vector QualitySegmentUrlModifiers; - uint32_t SegmentRunEntryCount; - std::vector< std::pair > SegmentRunEntryTable; - - uint32_t CurrentOffset = 4; - std::string temp; - std::pair TempPair; - QualityEntryCount = Payload[CurrentOffset]; - CurrentOffset ++; - for( uint8_t i = 0; i < QualityEntryCount; i++ ) { - temp = ""; - while( Payload[CurrentOffset] != '\0' ) { temp += Payload[CurrentOffset]; CurrentOffset ++; } - QualitySegmentUrlModifiers.push_back(temp); - CurrentOffset++; - } - SegmentRunEntryCount = (Payload[CurrentOffset] << 24) + (Payload[CurrentOffset+1] << 16) + (Payload[CurrentOffset+2]) + (Payload[CurrentOffset+3]); - CurrentOffset +=4; - for( uint8_t i = 0; i < SegmentRunEntryCount; i++ ) { - TempPair.first = (Payload[CurrentOffset] << 24) + (Payload[CurrentOffset+1] << 16) + (Payload[CurrentOffset+2] << 8) + (Payload[CurrentOffset+3]); - CurrentOffset+=4; - TempPair.second = (Payload[CurrentOffset] << 24) + (Payload[CurrentOffset+1] << 16) + (Payload[CurrentOffset+2] << 8) + (Payload[CurrentOffset+3]); - CurrentOffset+=4; - SegmentRunEntryTable.push_back(TempPair); - } - - std::cerr << "Box_ASRT:\n"; - std::cerr << PrintOffset << " Version: " << (int)Version << "\n"; - std::cerr << PrintOffset << " Flags: " << (int)Flags << "\n"; - std::cerr << PrintOffset << " QualityEntryCount: " << (int)QualityEntryCount << "\n"; - std::cerr << PrintOffset << " QualitySegmentUrlModifiers:\n"; - for( uint32_t i = 0; i < QualitySegmentUrlModifiers.size( ); i++ ) { - std::cerr << PrintOffset << " " << i+1 << ": " << QualitySegmentUrlModifiers[i] << "\n"; - } - std::cerr << PrintOffset << " SegmentRunEntryCount: " << (int)SegmentRunEntryCount << "\n"; - std::cerr << PrintOffset << " SegmentRunEntryTable:\n"; - for( uint32_t i = 0; i < SegmentRunEntryTable.size( ); i++ ) { - std::cerr << PrintOffset << " " << i+1 << ":\n"; - std::cerr << PrintOffset << " FirstSegment: " << SegmentRunEntryTable[i].first << "\n"; - std::cerr << PrintOffset << " FragmentsPerSegment: " << SegmentRunEntryTable[i].second << "\n"; - } - } else if ( header.BoxType == 0x61667274 ) { - uint8_t Version = Payload[0]; - uint32_t Flags = (Payload[1] << 16) + (Payload[2] << 8) + (Payload[3]); //uint24_t - uint32_t TimeScale = (Payload[4] << 24) + (Payload[5] << 16) + (Payload[6] << 8) + (Payload[7]); - uint8_t QualityEntryCount; - std::vector QualitySegmentUrlModifiers; - uint32_t FragmentRunEntryCount; - std::vector FragmentRunEntryTable; - - uint32_t CurrentOffset = 8; - std::string temp; - afrt_fragmentrunentry TempEntry; - QualityEntryCount = Payload[CurrentOffset]; - CurrentOffset ++; - for( uint8_t i = 0; i < QualityEntryCount; i++ ) { - temp = ""; - while( Payload[CurrentOffset] != '\0' ) { temp += Payload[CurrentOffset]; CurrentOffset ++; } - QualitySegmentUrlModifiers.push_back(temp); - CurrentOffset++; - } - FragmentRunEntryCount = (Payload[CurrentOffset] << 24) + (Payload[CurrentOffset+1] << 16) + (Payload[CurrentOffset+2]) + (Payload[CurrentOffset+3]); - CurrentOffset +=4; - for( uint8_t i = 0; i < FragmentRunEntryCount; i ++ ) { - TempEntry.FirstFragment = (Payload[CurrentOffset] << 24) + (Payload[CurrentOffset+1] << 16) + (Payload[CurrentOffset+2]) + (Payload[CurrentOffset+3]); - CurrentOffset +=4; - TempEntry.FirstFragmentTimestamp_Upperhalf = (Payload[CurrentOffset] << 24) + (Payload[CurrentOffset+1] << 16) + (Payload[CurrentOffset+2]) + (Payload[CurrentOffset+3]); - CurrentOffset +=4; - TempEntry.FirstFragmentTimestamp = (Payload[CurrentOffset] << 24) + (Payload[CurrentOffset+1] << 16) + (Payload[CurrentOffset+2]) + (Payload[CurrentOffset+3]); - CurrentOffset +=4; - TempEntry.FragmentDuration = (Payload[CurrentOffset] << 24) + (Payload[CurrentOffset+1] << 16) + (Payload[CurrentOffset+2]) + (Payload[CurrentOffset+3]); - CurrentOffset +=4; - if( TempEntry.FragmentDuration == 0 ) { - TempEntry.DiscontinuityIndicator = Payload[CurrentOffset]; - CurrentOffset++; - } - FragmentRunEntryTable.push_back(TempEntry); - } - - std::cerr << "Box_AFRT:\n"; - std::cerr << PrintOffset << " Version: " << (int)Version << "\n"; - std::cerr << PrintOffset << " Flags: " << (int)Flags << "\n"; - std::cerr << PrintOffset << " Timescale: " << (int)TimeScale << "\n"; - std::cerr << PrintOffset << " QualityEntryCount: " << (int)QualityEntryCount << "\n"; - std::cerr << PrintOffset << " QualitySegmentUrlModifiers:\n"; - for( uint32_t i = 0; i < QualitySegmentUrlModifiers.size( ); i++ ) { - std::cerr << PrintOffset << " " << i+1 << ": " << QualitySegmentUrlModifiers[i] << "\n"; - } - std::cerr << PrintOffset << " FragmentRunEntryCount: " << (int)FragmentRunEntryCount << "\n"; - std::cerr << PrintOffset << " FragmentRunEntryTable:\n"; - for( uint32_t i = 0; i < FragmentRunEntryTable.size( ); i++ ) { - std::cerr << PrintOffset << " " << i+1 << ":\n"; - std::cerr << PrintOffset << " FirstFragment: " << FragmentRunEntryTable[i].FirstFragment << "\n"; - std::cerr << PrintOffset << " FirstFragmentTimestamp: " << FragmentRunEntryTable[i].FirstFragmentTimestamp_Upperhalf << FragmentRunEntryTable[i].FirstFragmentTimestamp << "\n"; - std::cerr << PrintOffset << " FragmentDuration: " << FragmentRunEntryTable[i].FragmentDuration << "\n"; - if( FragmentRunEntryTable[i].FragmentDuration == 0 ) { - std::cerr << PrintOffset << " DiscontinuityIndicator: " << (int)FragmentRunEntryTable[i].DiscontinuityIndicator << "\n"; - } - } - } else { - std::cerr << "BoxType '" - << (char)(header.BoxType >> 24) - << (char)((header.BoxType << 8) >> 24) - << (char)((header.BoxType << 16) >> 24) - << (char)((header.BoxType << 24) >> 24) - << "' not yet implemented!\n"; - } -} diff --git a/util/MP4/box_abst.cpp b/util/MP4/box_abst.cpp index 129a423c..238679ee 100644 --- a/util/MP4/box_abst.cpp +++ b/util/MP4/box_abst.cpp @@ -1,7 +1,9 @@ +#pragma once #include "box.cpp" #include #include + struct abst_serverentry { std::string ServerBaseUrl; };//abst_serverentry @@ -156,7 +158,6 @@ void Box_abst::SetVersion( bool NewVersion) { Version = NewVersion; } - void Box_abst::SetReserved( ) { Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0)); } diff --git a/util/MP4/box_asrt.cpp b/util/MP4/box_asrt.cpp index 23553037..cbc5ba31 100644 --- a/util/MP4/box_asrt.cpp +++ b/util/MP4/box_asrt.cpp @@ -16,9 +16,11 @@ class Box_asrt { void AddQualityEntry( std::string Quality = "", uint32_t Offset = 0 ); void AddSegmentRunEntry( uint32_t FirstSegment = 0, uint32_t FragmentsPerSegment = 100, uint32_t Offset = 0 ); void WriteContent( ); + void SetVersion( bool NewVersion = 0 ); private: void SetDefaults( ); bool isUpdate; + bool Version; std::vector QualitySegmentUrlModifiers; std::vector SegmentRunEntryTable; Box * Container; @@ -55,6 +57,10 @@ void Box_asrt::AddSegmentRunEntry( uint32_t FirstSegment, uint32_t FragmentsPerS SegmentRunEntryTable[Offset].FragmentsPerSegment = FragmentsPerSegment; } +void Box_asrt::SetVersion( bool NewVersion ) { + Version = NewVersion; +} + void Box_asrt::SetDefaults( ) { SetUpdate( ); } From f8f03315107e953ab562623b01c60e03f08667af Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Sat, 19 Mar 2011 19:49:40 +0100 Subject: [PATCH 086/788] Test1 --- util/MP4/interface.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/util/MP4/interface.cpp b/util/MP4/interface.cpp index b6d412da..8292fa83 100644 --- a/util/MP4/interface.cpp +++ b/util/MP4/interface.cpp @@ -547,22 +547,25 @@ std::string Interface::GenerateLiveBootstrap( uint32_t CurMediaTime ) { afrt->SetUpdate(false); afrt->SetTimeScale( 1000 ); afrt->AddQualityEntry( "" ); - afrt->AddFragmentRunEntry( 1, CurMediaTime, 4000 ); + for(int i = 0; i < 198 ; i++ ) { + afrt->AddFragmentRunEntry( i+1, (195*(i+1)) , 195, i ); + } + afrt->AddFragmentRunEntry( 199, 590083 , 163, 198 ); afrt->WriteContent( ); //SetUpASRT asrt->SetUpdate(false); asrt->AddQualityEntry( "" ); - asrt->AddSegmentRunEntry( 1, 0xFFFFFFFF ); + asrt->AddSegmentRunEntry( 1, 199 ); asrt->WriteContent( ); //SetUpABST abst->SetBootstrapVersion( 1 ); abst->SetProfile( 0 ); -// abst->SetLive( true ); + abst->SetLive( false ); abst->SetUpdate( false ); abst->SetTimeScale( 1000 ); - abst->SetMediaTime( CurMediaTime ); + abst->SetMediaTime( 596458 ); abst->SetSMPTE( 0 ); abst->SetMovieIdentifier( "" ); abst->SetDRM( "" ); From c474af784821762546faf62f14168aa89c895125 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Sun, 20 Mar 2011 14:18:23 +0100 Subject: [PATCH 087/788] Fixed framerate --- util/MP4/interface.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/util/MP4/interface.cpp b/util/MP4/interface.cpp index 8292fa83..9f812de0 100644 --- a/util/MP4/interface.cpp +++ b/util/MP4/interface.cpp @@ -547,10 +547,7 @@ std::string Interface::GenerateLiveBootstrap( uint32_t CurMediaTime ) { afrt->SetUpdate(false); afrt->SetTimeScale( 1000 ); afrt->AddQualityEntry( "" ); - for(int i = 0; i < 198 ; i++ ) { - afrt->AddFragmentRunEntry( i+1, (195*(i+1)) , 195, i ); - } - afrt->AddFragmentRunEntry( 199, 590083 , 163, 198 ); + afrt->AddFragmentRunEntry( 1, 4000 , 4000 ); afrt->WriteContent( ); //SetUpASRT From 9e121bbc070a8020d1b62b46e54001041788c10e Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Sun, 20 Mar 2011 14:46:23 +0100 Subject: [PATCH 088/788] Timestamps added --- util/MP4/interface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/MP4/interface.cpp b/util/MP4/interface.cpp index 9f812de0..00f0eb1c 100644 --- a/util/MP4/interface.cpp +++ b/util/MP4/interface.cpp @@ -547,7 +547,7 @@ std::string Interface::GenerateLiveBootstrap( uint32_t CurMediaTime ) { afrt->SetUpdate(false); afrt->SetTimeScale( 1000 ); afrt->AddQualityEntry( "" ); - afrt->AddFragmentRunEntry( 1, 4000 , 4000 ); + afrt->AddFragmentRunEntry( 1, 1 , 4000 ); afrt->WriteContent( ); //SetUpASRT From 819fd2f848b59083d588d3e9aecaf29e31fd2052 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Sun, 20 Mar 2011 15:11:47 +0100 Subject: [PATCH 089/788] CurMediaTime edit --- util/MP4/interface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/MP4/interface.cpp b/util/MP4/interface.cpp index 00f0eb1c..f50b41d5 100644 --- a/util/MP4/interface.cpp +++ b/util/MP4/interface.cpp @@ -562,7 +562,7 @@ std::string Interface::GenerateLiveBootstrap( uint32_t CurMediaTime ) { abst->SetLive( false ); abst->SetUpdate( false ); abst->SetTimeScale( 1000 ); - abst->SetMediaTime( 596458 ); + abst->SetMediaTime( 1 ); abst->SetSMPTE( 0 ); abst->SetMovieIdentifier( "" ); abst->SetDRM( "" ); From 5647bf8fde65f89836af0baa098227e7a0778570 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Sun, 20 Mar 2011 15:14:18 +0100 Subject: [PATCH 090/788] Live edit --- util/MP4/interface.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/util/MP4/interface.cpp b/util/MP4/interface.cpp index f50b41d5..cac5581f 100644 --- a/util/MP4/interface.cpp +++ b/util/MP4/interface.cpp @@ -559,7 +559,8 @@ std::string Interface::GenerateLiveBootstrap( uint32_t CurMediaTime ) { //SetUpABST abst->SetBootstrapVersion( 1 ); abst->SetProfile( 0 ); - abst->SetLive( false ); +// abst->SetLive( false ); + abst->SetLive( true ); abst->SetUpdate( false ); abst->SetTimeScale( 1000 ); abst->SetMediaTime( 1 ); From 28aa3ca4056ce5a212403485e018115d4d355eb9 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Sun, 20 Mar 2011 15:18:50 +0100 Subject: [PATCH 091/788] Live edit --- util/MP4/interface.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/util/MP4/interface.cpp b/util/MP4/interface.cpp index cac5581f..8437913b 100644 --- a/util/MP4/interface.cpp +++ b/util/MP4/interface.cpp @@ -559,11 +559,10 @@ std::string Interface::GenerateLiveBootstrap( uint32_t CurMediaTime ) { //SetUpABST abst->SetBootstrapVersion( 1 ); abst->SetProfile( 0 ); -// abst->SetLive( false ); abst->SetLive( true ); abst->SetUpdate( false ); abst->SetTimeScale( 1000 ); - abst->SetMediaTime( 1 ); + abst->SetMediaTime( 596458 ); abst->SetSMPTE( 0 ); abst->SetMovieIdentifier( "" ); abst->SetDRM( "" ); From 239ec766704a586f1d950bd3b1d87a62ee03613c Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Sun, 20 Mar 2011 15:25:11 +0100 Subject: [PATCH 092/788] Live edit --- util/MP4/interface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/MP4/interface.cpp b/util/MP4/interface.cpp index 8437913b..5175acbe 100644 --- a/util/MP4/interface.cpp +++ b/util/MP4/interface.cpp @@ -547,7 +547,7 @@ std::string Interface::GenerateLiveBootstrap( uint32_t CurMediaTime ) { afrt->SetUpdate(false); afrt->SetTimeScale( 1000 ); afrt->AddQualityEntry( "" ); - afrt->AddFragmentRunEntry( 1, 1 , 4000 ); + afrt->AddFragmentRunEntry( 1, 596458 , 4000 ); afrt->WriteContent( ); //SetUpASRT From fe6d0fdccf957ed15d9c637adca797c3636c8116 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 21 Mar 2011 00:40:49 +0100 Subject: [PATCH 093/788] Nieuwe awesome parser des doods voor RAW tcpflow logs van F4M streams, en hopelijk fix voor F4M streams zelf, nieuwe dataparser voor FLV streams, en awesomeheid in het algemeen --- util/MP4/box.cpp | 6 ++- util/ddv_socket.cpp | 1 + util/flv_data.cpp | 90 ++++++++++++++++++++++++++++++++++++++++++++ util/http_parser.cpp | 38 ++++++++++++++++++- 4 files changed, 132 insertions(+), 3 deletions(-) create mode 100644 util/flv_data.cpp diff --git a/util/MP4/box.cpp b/util/MP4/box.cpp index 2e948bf6..e4c221cf 100644 --- a/util/MP4/box.cpp +++ b/util/MP4/box.cpp @@ -42,10 +42,10 @@ class Box { BoxHeader GetHeader( ); void ResetPayload( ); void Parse( std::string PrintOffset = "" ); - private: - BoxHeader header; uint8_t * Payload; uint32_t PayloadSize; + private: + BoxHeader header; };//Box Class Box::Box() { @@ -385,6 +385,8 @@ void Box::Parse( std::string PrintOffset ) { std::cerr << PrintOffset << " DiscontinuityIndicator: " << (int)FragmentRunEntryTable[i].DiscontinuityIndicator << "\n"; } } + } else if ( header.BoxType == 0x6D646174 ) { + std::cerr << "mdat box containing " << PayloadSize << " bytes of payload" << std::endl; } else { std::cerr << "BoxType '" << (char)(header.BoxType >> 24) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index 0f26d36c..b1bd03e0 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -1,3 +1,4 @@ +#pragma once #include #include #include diff --git a/util/flv_data.cpp b/util/flv_data.cpp new file mode 100644 index 00000000..28a18da4 --- /dev/null +++ b/util/flv_data.cpp @@ -0,0 +1,90 @@ +#include //for read() +#include + +struct FLV_Pack { + int len; + int buf; + bool isKeyframe; + char * data; +};//FLV_Pack + +char FLVHeader[13]; +bool All_Hell_Broke_Loose = false; + +//checks FLV Header for correctness +//returns true if everything is alright, false otherwise +bool FLV_Checkheader(char * header){ + if (header[0] != 'F') return false; + if (header[1] != 'L') return false; + if (header[2] != 'V') return false; + if (header[8] != 0x09) return false; + if (header[9] != 0) return false; + if (header[10] != 0) return false; + if (header[11] != 0) return false; + if (header[12] != 0) return false; + return true; +}//FLV_Checkheader + +//returns true if header is an FLV header +bool FLV_Isheader(char * header){ + if (header[0] != 'F') return false; + if (header[1] != 'L') return false; + if (header[2] != 'V') return false; + return true; +}//FLV_Isheader + +bool ReadUntil(char * buffer, unsigned int count, unsigned int & sofar, char * D, unsigned int S, unsigned int & P){ + if (sofar >= count){return true;} + int r = 0; + if (P+(count-sofar) > S){r = S-P;}else{r = count-sofar;} + memcpy(buffer+sofar, D+P, r); + P += r; + sofar += r; + if (sofar >= count){return true;} + return false; +} + +//gets a packet, storing in given FLV_Pack pointer. +//will assign pointer if null +//resizes FLV_Pack data field bigger if data doesn't fit +// (does not auto-shrink for speed!) +bool FLV_GetPacket(FLV_Pack *& p, char * D, unsigned int S, unsigned int & P){ + static bool done = true; + static unsigned int sofar = 0; + if (!p){p = (FLV_Pack*)calloc(1, sizeof(FLV_Pack));} + if (p->buf < 15){p->data = (char*)realloc(p->data, 15000000); p->buf = 15000000;} + + if (done){ + //read a header + if (ReadUntil(p->data, 11, sofar, D, S, P)){ + //if its a correct FLV header, throw away and read tag header + if (FLV_Isheader(p->data)){ + if (ReadUntil(p->data, 13, sofar, D, S, P)){ + if (FLV_Checkheader(p->data)){ + sofar = 0; + memcpy(FLVHeader, p->data, 13); + }else{All_Hell_Broke_Loose = true;} + } + }else{ + //if a tag header, calculate length and read tag body + p->len = p->data[3] + 15; + p->len += (p->data[2] << 8); + p->len += (p->data[1] << 16); + //if (p->buf < p->len){p->data = (char*)realloc(p->data, p->len);p->buf = p->len;} + done = false; + } + } + }else{ + //read tag body + if (ReadUntil(p->data, p->len, sofar, D, S, P)){ + //calculate keyframeness, next time read header again, return true + p->isKeyframe = false; + if ((p->data[0] == 0x09) && (((p->data[11] & 0xf0) >> 4) == 1)){p->isKeyframe = true;} + done = true; + sofar = 0; + return true; + } + } + return false; +}//FLV_GetPacket + diff --git a/util/http_parser.cpp b/util/http_parser.cpp index 6f7d619f..ff32eda3 100644 --- a/util/http_parser.cpp +++ b/util/http_parser.cpp @@ -1,10 +1,14 @@ +#pragma once +#include "ddv_socket.cpp" #include #include +#include class HTTPReader{ public: HTTPReader(); bool ReadSocket(int CONN_fd); + bool ReadSocket(FILE * F); std::string GetHeader(std::string i); std::string GetVar(std::string i); void SetHeader(std::string i, std::string v); @@ -18,6 +22,8 @@ class HTTPReader{ void SendBodyPart(int conn, char * buffer, int len); void SendBodyPart(int conn, std::string bodypart); void Clean(); + bool CleanForNext(); + std::string body; std::string method; std::string url; std::string protocol; @@ -39,12 +45,26 @@ void HTTPReader::Clean(){ method = "GET"; url = "/"; protocol = "HTTP/1.1"; + body = ""; length = 0; HTTPbuffer = ""; headers.erase(headers.begin(), headers.end()); vars.erase(vars.begin(), vars.end()); } +bool HTTPReader::CleanForNext(){ + seenHeaders = false; + seenReq = false; + method = "GET"; + url = "/"; + protocol = "HTTP/1.1"; + body = ""; + length = 0; + headers.erase(headers.begin(), headers.end()); + vars.erase(vars.begin(), vars.end()); + return parse(); +} + std::string HTTPReader::BuildRequest(){ std::map::iterator it; std::string tmp = method+" "+url+" "+protocol+"\n"; @@ -129,6 +149,16 @@ bool HTTPReader::ReadSocket(int CONN_fd){ return false; }//HTTPReader::ReadSocket +bool HTTPReader::ReadSocket(FILE * F){ + //returned true als hele http packet gelezen is + int b = 1; + char buffer[500]; + while (b > 0){ + b = fread(buffer, 1, 500, F); + HTTPbuffer.append(buffer, b); + } + return false; +}//HTTPReader::ReadSocket bool HTTPReader::parse(){ size_t f; @@ -165,7 +195,13 @@ bool HTTPReader::parse(){ if (seenHeaders){ if (length > 0){ //TODO: POST variable parsing - return (HTTPbuffer.length() >= length); + if (HTTPbuffer.length() >= length){ + body = HTTPbuffer.substr(0, length); + HTTPbuffer.erase(0, length); + return true; + }else{ + return false; + } }else{ return true; } From 10ab707e8ade3ecea6c139b02cd720ba513fae1e Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 21 Mar 2011 00:44:44 +0100 Subject: [PATCH 094/788] oeps. --- util/MP4/box.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/util/MP4/box.cpp b/util/MP4/box.cpp index 1e5a5424..46412680 100644 --- a/util/MP4/box.cpp +++ b/util/MP4/box.cpp @@ -46,8 +46,6 @@ class Box { uint8_t * Payload; BoxHeader header; uint32_t PayloadSize; - private: - BoxHeader header; };//Box Class Box::Box() { @@ -177,7 +175,6 @@ void Box::ResetPayload( ) { Payload = NULL; } } -<<<<<<< HEAD void Box::Parse( std::string PrintOffset ) { if( header.BoxType == 0x61627374 ) { @@ -399,5 +396,4 @@ void Box::Parse( std::string PrintOffset ) { << "' not yet implemented!\n"; } } -======= ->>>>>>> 7520f5799f3da3c1a89a28fd4d62358b0028d8d2 + From c2552e88fd355c2e081ffa2cf34b5630103ae86e Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 21 Mar 2011 00:57:48 +0100 Subject: [PATCH 095/788] Random edits --- util/http_parser.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/http_parser.cpp b/util/http_parser.cpp index ff32eda3..36557fbd 100644 --- a/util/http_parser.cpp +++ b/util/http_parser.cpp @@ -178,7 +178,7 @@ bool HTTPReader::parse(){ if (f != std::string::npos){url = tmpA.substr(0, f); tmpA.erase(0, f+1);} f = tmpA.find(' '); if (f != std::string::npos){protocol = tmpA.substr(0, f); tmpA.erase(0, f+1);} - //TODO: GET variable parsing + //TODO: GET variable parsing? }else{ if (tmpA.size() == 0){ seenHeaders = true; @@ -194,7 +194,7 @@ bool HTTPReader::parse(){ } if (seenHeaders){ if (length > 0){ - //TODO: POST variable parsing + //TODO: POST variable parsing? if (HTTPbuffer.length() >= length){ body = HTTPbuffer.substr(0, length); HTTPbuffer.erase(0, length); From 352a1f00e176f34d1ba4973f9ae6cc3a7116afc6 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 21 Mar 2011 01:12:48 +0100 Subject: [PATCH 096/788] Back to the future 2 --- util/MP4/interface.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/util/MP4/interface.cpp b/util/MP4/interface.cpp index 5175acbe..111f9b33 100644 --- a/util/MP4/interface.cpp +++ b/util/MP4/interface.cpp @@ -547,7 +547,7 @@ std::string Interface::GenerateLiveBootstrap( uint32_t CurMediaTime ) { afrt->SetUpdate(false); afrt->SetTimeScale( 1000 ); afrt->AddQualityEntry( "" ); - afrt->AddFragmentRunEntry( 1, 596458 , 4000 ); + afrt->AddFragmentRunEntry( 1, 1 , 4000 ); afrt->WriteContent( ); //SetUpASRT @@ -562,9 +562,9 @@ std::string Interface::GenerateLiveBootstrap( uint32_t CurMediaTime ) { abst->SetLive( true ); abst->SetUpdate( false ); abst->SetTimeScale( 1000 ); - abst->SetMediaTime( 596458 ); + abst->SetMediaTime( CurMediaTime ); abst->SetSMPTE( 0 ); - abst->SetMovieIdentifier( "" ); + abst->SetMovieIdentifier( "fifa" ); abst->SetDRM( "" ); abst->SetMetaData( "" ); abst->AddServerEntry( "" ); From e69ef556177d8eca3097cfc593779c67c4ce4705 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 21 Mar 2011 01:15:54 +0100 Subject: [PATCH 097/788] En toen wist ik het ook niet meer --- util/MP4/interface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/MP4/interface.cpp b/util/MP4/interface.cpp index 111f9b33..d6331389 100644 --- a/util/MP4/interface.cpp +++ b/util/MP4/interface.cpp @@ -547,7 +547,7 @@ std::string Interface::GenerateLiveBootstrap( uint32_t CurMediaTime ) { afrt->SetUpdate(false); afrt->SetTimeScale( 1000 ); afrt->AddQualityEntry( "" ); - afrt->AddFragmentRunEntry( 1, 1 , 4000 ); + afrt->AddFragmentRunEntry( 1, 4000, 4000 ); afrt->WriteContent( ); //SetUpASRT From 705c033e1e8ce7e8168d732ff19254d9cf36e781 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Mon, 21 Mar 2011 13:49:42 +0100 Subject: [PATCH 098/788] Interface --- util/MP4/interface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/MP4/interface.cpp b/util/MP4/interface.cpp index 5175acbe..8437913b 100644 --- a/util/MP4/interface.cpp +++ b/util/MP4/interface.cpp @@ -547,7 +547,7 @@ std::string Interface::GenerateLiveBootstrap( uint32_t CurMediaTime ) { afrt->SetUpdate(false); afrt->SetTimeScale( 1000 ); afrt->AddQualityEntry( "" ); - afrt->AddFragmentRunEntry( 1, 596458 , 4000 ); + afrt->AddFragmentRunEntry( 1, 1 , 4000 ); afrt->WriteContent( ); //SetUpASRT From 1d39fae70353df7c813738059f2a85033456c6e2 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Mon, 21 Mar 2011 14:01:33 +0100 Subject: [PATCH 099/788] Test1 --- util/MP4/interface.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/util/MP4/interface.cpp b/util/MP4/interface.cpp index 56b63040..fd177cd8 100644 --- a/util/MP4/interface.cpp +++ b/util/MP4/interface.cpp @@ -545,9 +545,10 @@ void Interface::SetOffsets( std::vector NewOffsets, uint32_t Track ) { std::string Interface::GenerateLiveBootstrap( uint32_t CurMediaTime ) { //SetUpAFRT afrt->SetUpdate(false); - afrt->SetTimeScale( 1000 ); + afrt->SetTimeScale( 1 ); afrt->AddQualityEntry( "" ); - afrt->AddFragmentRunEntry( 1, 1 , 4000 ); //FirstFragment, FirstFragmentTimestamp,Fragment Duration in milliseconds + std::cerr << "Setting RunEntry on 4000 ms\n"; + afrt->AddFragmentRunEntry( 1, 1 , 4 ); //FirstFragment, FirstFragmentTimestamp,Fragment Duration in milliseconds afrt->WriteContent( ); //SetUpASRT From 91a5fb01b13c9334f8b4c42ff0245aadd526d77e Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Mon, 21 Mar 2011 14:04:49 +0100 Subject: [PATCH 100/788] Test2 --- util/MP4/interface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/MP4/interface.cpp b/util/MP4/interface.cpp index fd177cd8..0129b36c 100644 --- a/util/MP4/interface.cpp +++ b/util/MP4/interface.cpp @@ -563,7 +563,7 @@ std::string Interface::GenerateLiveBootstrap( uint32_t CurMediaTime ) { abst->SetLive( true ); abst->SetUpdate( false ); abst->SetTimeScale( 1000 ); - abst->SetMediaTime( CurMediaTime ); + abst->SetMediaTime( 40000 ); abst->SetSMPTE( 0 ); abst->SetMovieIdentifier( "fifa" ); abst->SetDRM( "" ); From 22b62c794afe1813921403526a73bac70ed8b7bf Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Mon, 21 Mar 2011 14:08:32 +0100 Subject: [PATCH 101/788] Test3 --- util/MP4/interface.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/MP4/interface.cpp b/util/MP4/interface.cpp index 0129b36c..73147b6a 100644 --- a/util/MP4/interface.cpp +++ b/util/MP4/interface.cpp @@ -562,8 +562,8 @@ std::string Interface::GenerateLiveBootstrap( uint32_t CurMediaTime ) { abst->SetProfile( 0 ); abst->SetLive( true ); abst->SetUpdate( false ); - abst->SetTimeScale( 1000 ); - abst->SetMediaTime( 40000 ); + abst->SetTimeScale( 1 ); + abst->SetMediaTime( 0xFFFFFFFF ); abst->SetSMPTE( 0 ); abst->SetMovieIdentifier( "fifa" ); abst->SetDRM( "" ); From 3f6647eb6e56f5e07d4462fea3799a6f4e3dbac5 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Mon, 21 Mar 2011 14:13:46 +0100 Subject: [PATCH 102/788] Test4 --- util/MP4/interface.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/util/MP4/interface.cpp b/util/MP4/interface.cpp index 73147b6a..cf05e419 100644 --- a/util/MP4/interface.cpp +++ b/util/MP4/interface.cpp @@ -545,10 +545,10 @@ void Interface::SetOffsets( std::vector NewOffsets, uint32_t Track ) { std::string Interface::GenerateLiveBootstrap( uint32_t CurMediaTime ) { //SetUpAFRT afrt->SetUpdate(false); - afrt->SetTimeScale( 1 ); + afrt->SetTimeScale( 1000 ); afrt->AddQualityEntry( "" ); std::cerr << "Setting RunEntry on 4000 ms\n"; - afrt->AddFragmentRunEntry( 1, 1 , 4 ); //FirstFragment, FirstFragmentTimestamp,Fragment Duration in milliseconds + afrt->AddFragmentRunEntry( 1, 1 , 400 ); //FirstFragment, FirstFragmentTimestamp,Fragment Duration in milliseconds afrt->WriteContent( ); //SetUpASRT @@ -562,7 +562,7 @@ std::string Interface::GenerateLiveBootstrap( uint32_t CurMediaTime ) { abst->SetProfile( 0 ); abst->SetLive( true ); abst->SetUpdate( false ); - abst->SetTimeScale( 1 ); + abst->SetTimeScale( 1000 ); abst->SetMediaTime( 0xFFFFFFFF ); abst->SetSMPTE( 0 ); abst->SetMovieIdentifier( "fifa" ); From 75634c65122dff9cdf334ac3b3e415399751b069 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Mon, 21 Mar 2011 14:24:56 +0100 Subject: [PATCH 103/788] Test5 --- util/MP4/box.cpp | 232 ------------------------------------------ util/MP4/box_afrt.cpp | 10 +- 2 files changed, 6 insertions(+), 236 deletions(-) diff --git a/util/MP4/box.cpp b/util/MP4/box.cpp index 46412680..7b2d3361 100644 --- a/util/MP4/box.cpp +++ b/util/MP4/box.cpp @@ -9,14 +9,6 @@ #include #include -struct afrt_fragmentrunentry { - uint32_t FirstFragment; - uint32_t FirstFragmentTimestamp_Upperhalf; //write as uint64_t - uint32_t FirstFragmentTimestamp; //write as uint64_t - uint32_t FragmentDuration; - uint8_t DiscontinuityIndicator;//if FragmentDuration == 0 -};//afrt_fragmentrunentry - struct BoxHeader { uint32_t TotalSize; uint32_t BoxType; @@ -41,8 +33,6 @@ class Box { static uint8_t * uint8_to_uint8( uint8_t data ); BoxHeader GetHeader( ); void ResetPayload( ); - void Parse( std::string PrintOffset ); - void * Parse( ); uint8_t * Payload; BoxHeader header; uint32_t PayloadSize; @@ -175,225 +165,3 @@ void Box::ResetPayload( ) { Payload = NULL; } } - -void Box::Parse( std::string PrintOffset ) { - if( header.BoxType == 0x61627374 ) { - uint8_t Version = Payload[0]; - uint32_t Flags = (Payload[1] << 16) + (Payload[2] << 8) + (Payload[3]); //uint24_t - uint32_t BootstrapInfoVersion = (Payload[4] << 24) + (Payload[5] << 16) +(Payload[6] << 8) + (Payload[7]); - uint8_t Profile = (Payload[8] >> 6); //uint2_t - uint8_t Live = (( Payload[8] >> 5 ) & 0x1); //uint1_t - uint8_t Update = (( Payload[8] >> 4 ) & 0x1); //uint1_t - uint8_t Reserved = ( Payload[8] & 0x4); //uint4_t - uint32_t Timescale = (Payload[9] << 24) + (Payload[10] << 16) +(Payload[11] << 8) + (Payload[12]); - uint32_t CurrentMediaTime_Upperhalf = (Payload[13] << 24) + (Payload[14] << 16) +(Payload[15] << 8) + (Payload[16]); - uint32_t CurrentMediaTime_Lowerhalf = (Payload[17] << 24) + (Payload[18] << 16) +(Payload[19] << 8) + (Payload[20]); - uint32_t SmpteTimeCodeOffset_Upperhalf = (Payload[21] << 24) + (Payload[22] << 16) +(Payload[23] << 8) + (Payload[24]); - uint32_t SmpteTimeCodeOffset_Lowerhalf = (Payload[25] << 24) + (Payload[26] << 16) +(Payload[27] << 8) + (Payload[28]); - - std::string MovieIdentifier; - uint8_t ServerEntryCount = -1; - std::vector ServerEntryTable; - uint8_t QualityEntryCount = -1; - std::vector QualityEntryTable; - std::string DrmData; - std::string MetaData; - uint8_t SegmentRunTableCount = -1; - std::vector SegmentRunTableEntries; - uint8_t FragmentRunTableCount = -1; - std::vector FragmentRunTableEntries; - - uint32_t CurrentOffset = 29; - uint32_t TempSize; - Box* TempBox; - std::string temp; - while( Payload[CurrentOffset] != '\0' ) { MovieIdentifier += Payload[CurrentOffset]; CurrentOffset ++; } - CurrentOffset ++; - ServerEntryCount = Payload[CurrentOffset]; - CurrentOffset ++; - for( uint8_t i = 0; i < ServerEntryCount; i++ ) { - temp = ""; - while( Payload[CurrentOffset] != '\0' ) { temp += Payload[CurrentOffset]; CurrentOffset ++; } - ServerEntryTable.push_back(temp); - CurrentOffset++; - } - QualityEntryCount = Payload[CurrentOffset]; - CurrentOffset ++; - for( uint8_t i = 0; i < QualityEntryCount; i++ ) { - temp = ""; - while( Payload[CurrentOffset] != '\0' ) { temp += Payload[CurrentOffset]; CurrentOffset ++; } - QualityEntryTable.push_back(temp); - CurrentOffset++; - } - while( Payload[CurrentOffset] != '\0' ) { DrmData += Payload[CurrentOffset]; CurrentOffset ++; } - CurrentOffset ++; - while( Payload[CurrentOffset] != '\0' ) { MetaData += Payload[CurrentOffset]; CurrentOffset ++; } - CurrentOffset ++; - SegmentRunTableCount = Payload[CurrentOffset]; - CurrentOffset ++; - for( uint8_t i = 0; i < SegmentRunTableCount; i++ ) { - TempSize = (Payload[CurrentOffset] << 24) + (Payload[CurrentOffset+1]<< 16) + (Payload[CurrentOffset+2]<< 8) + (Payload[CurrentOffset+3]); - TempBox = new Box( &Payload[CurrentOffset], TempSize ); - SegmentRunTableEntries.push_back(TempBox); - CurrentOffset += TempSize; - } - FragmentRunTableCount = Payload[CurrentOffset]; - CurrentOffset ++; - for( uint8_t i = 0; i < FragmentRunTableCount; i++ ) { - TempSize = (Payload[CurrentOffset] << 24) + (Payload[CurrentOffset+1]<< 16) + (Payload[CurrentOffset+2]<< 8) + (Payload[CurrentOffset+3]); - TempBox = new Box( &Payload[CurrentOffset], TempSize ); - FragmentRunTableEntries.push_back(TempBox); - CurrentOffset += TempSize; - } - - std::cerr << "Box_ABST:\n"; - std::cerr << PrintOffset << " Version: " << (int)Version << "\n"; - std::cerr << PrintOffset << " Flags: " << (int)Flags << "\n"; - std::cerr << PrintOffset << " BootstrapInfoVersion: " << (int)BootstrapInfoVersion << "\n"; - std::cerr << PrintOffset << " Profile: " << (int)Profile << "\n"; - std::cerr << PrintOffset << " Live: " << (int)Live << "\n"; - std::cerr << PrintOffset << " Update: " << (int)Update << "\n"; - std::cerr << PrintOffset << " Reserved: " << (int)Reserved << "\n"; - std::cerr << PrintOffset << " Timescale: " << (int)Timescale << "\n"; - std::cerr << PrintOffset << " CurrentMediaTime: " << (int)CurrentMediaTime_Upperhalf << " " << CurrentMediaTime_Lowerhalf << "\n"; - std::cerr << PrintOffset << " SmpteTimeCodeOffset: " << (int)SmpteTimeCodeOffset_Upperhalf << " " << SmpteTimeCodeOffset_Lowerhalf << "\n"; - std::cerr << PrintOffset << " MovieIdentifier: " << MovieIdentifier << "\n"; - std::cerr << PrintOffset << " ServerEntryCount: " << (int)ServerEntryCount << "\n"; - std::cerr << PrintOffset << " ServerEntryTable:\n"; - for( uint32_t i = 0; i < ServerEntryTable.size( ); i++ ) { - std::cerr << PrintOffset << " " << i+1 << ": " << ServerEntryTable[i] << "\n"; - } - std::cerr << PrintOffset << " QualityEntryCount: " << (int)QualityEntryCount << "\n"; - std::cerr << PrintOffset << " QualityEntryTable:\n"; - for( uint32_t i = 0; i < QualityEntryTable.size( ); i++ ) { - std::cerr << PrintOffset << " " << i+1 << ": " << QualityEntryTable[i] << "\n"; - } - std::cerr << PrintOffset << " DrmData: " << DrmData << "\n"; - std::cerr << PrintOffset << " MetaData: " << MetaData << "\n"; - std::cerr << PrintOffset << " SegmentRunTableCount: " << (int)SegmentRunTableCount << "\n"; - std::cerr << PrintOffset << " SegmentRunTableEntries:\n"; - for( uint32_t i = 0; i < SegmentRunTableEntries.size( ); i++ ) { - std::cerr << PrintOffset << " " << i+1 << ": "; - SegmentRunTableEntries[i]->Parse( PrintOffset+" "); - } - std::cerr << PrintOffset << " FragmentRunTableCount: " << (int)FragmentRunTableCount << "\n"; - std::cerr << PrintOffset << " FragmentRunTableEntries:\n"; - for( uint32_t i = 0; i < FragmentRunTableEntries.size( ); i++ ) { - std::cerr << PrintOffset << " " << i+1 << ": "; - FragmentRunTableEntries[i]->Parse( PrintOffset+" "); - } - - } else if ( header.BoxType == 0x61737274 ) { - uint8_t Version = Payload[0]; - uint32_t Flags = (Payload[1] << 16) + (Payload[2] << 8) + (Payload[3]); //uint24_t - uint8_t QualityEntryCount; - std::vector QualitySegmentUrlModifiers; - uint32_t SegmentRunEntryCount; - std::vector< std::pair > SegmentRunEntryTable; - - uint32_t CurrentOffset = 4; - std::string temp; - std::pair TempPair; - QualityEntryCount = Payload[CurrentOffset]; - CurrentOffset ++; - for( uint8_t i = 0; i < QualityEntryCount; i++ ) { - temp = ""; - while( Payload[CurrentOffset] != '\0' ) { temp += Payload[CurrentOffset]; CurrentOffset ++; } - QualitySegmentUrlModifiers.push_back(temp); - CurrentOffset++; - } - SegmentRunEntryCount = (Payload[CurrentOffset] << 24) + (Payload[CurrentOffset+1] << 16) + (Payload[CurrentOffset+2]) + (Payload[CurrentOffset+3]); - CurrentOffset +=4; - for( uint8_t i = 0; i < SegmentRunEntryCount; i++ ) { - TempPair.first = (Payload[CurrentOffset] << 24) + (Payload[CurrentOffset+1] << 16) + (Payload[CurrentOffset+2] << 8) + (Payload[CurrentOffset+3]); - CurrentOffset+=4; - TempPair.second = (Payload[CurrentOffset] << 24) + (Payload[CurrentOffset+1] << 16) + (Payload[CurrentOffset+2] << 8) + (Payload[CurrentOffset+3]); - CurrentOffset+=4; - SegmentRunEntryTable.push_back(TempPair); - } - - std::cerr << "Box_ASRT:\n"; - std::cerr << PrintOffset << " Version: " << (int)Version << "\n"; - std::cerr << PrintOffset << " Flags: " << (int)Flags << "\n"; - std::cerr << PrintOffset << " QualityEntryCount: " << (int)QualityEntryCount << "\n"; - std::cerr << PrintOffset << " QualitySegmentUrlModifiers:\n"; - for( uint32_t i = 0; i < QualitySegmentUrlModifiers.size( ); i++ ) { - std::cerr << PrintOffset << " " << i+1 << ": " << QualitySegmentUrlModifiers[i] << "\n"; - } - std::cerr << PrintOffset << " SegmentRunEntryCount: " << (int)SegmentRunEntryCount << "\n"; - std::cerr << PrintOffset << " SegmentRunEntryTable:\n"; - for( uint32_t i = 0; i < SegmentRunEntryTable.size( ); i++ ) { - std::cerr << PrintOffset << " " << i+1 << ":\n"; - std::cerr << PrintOffset << " FirstSegment: " << SegmentRunEntryTable[i].first << "\n"; - std::cerr << PrintOffset << " FragmentsPerSegment: " << SegmentRunEntryTable[i].second << "\n"; - } - } else if ( header.BoxType == 0x61667274 ) { - uint8_t Version = Payload[0]; - uint32_t Flags = (Payload[1] << 16) + (Payload[2] << 8) + (Payload[3]); //uint24_t - uint32_t TimeScale = (Payload[4] << 24) + (Payload[5] << 16) + (Payload[6] << 8) + (Payload[7]); - uint8_t QualityEntryCount; - std::vector QualitySegmentUrlModifiers; - uint32_t FragmentRunEntryCount; - std::vector FragmentRunEntryTable; - - uint32_t CurrentOffset = 8; - std::string temp; - afrt_fragmentrunentry TempEntry; - QualityEntryCount = Payload[CurrentOffset]; - CurrentOffset ++; - for( uint8_t i = 0; i < QualityEntryCount; i++ ) { - temp = ""; - while( Payload[CurrentOffset] != '\0' ) { temp += Payload[CurrentOffset]; CurrentOffset ++; } - QualitySegmentUrlModifiers.push_back(temp); - CurrentOffset++; - } - FragmentRunEntryCount = (Payload[CurrentOffset] << 24) + (Payload[CurrentOffset+1] << 16) + (Payload[CurrentOffset+2]) + (Payload[CurrentOffset+3]); - CurrentOffset +=4; - for( uint8_t i = 0; i < FragmentRunEntryCount; i ++ ) { - TempEntry.FirstFragment = (Payload[CurrentOffset] << 24) + (Payload[CurrentOffset+1] << 16) + (Payload[CurrentOffset+2]) + (Payload[CurrentOffset+3]); - CurrentOffset +=4; - TempEntry.FirstFragmentTimestamp_Upperhalf = (Payload[CurrentOffset] << 24) + (Payload[CurrentOffset+1] << 16) + (Payload[CurrentOffset+2]) + (Payload[CurrentOffset+3]); - CurrentOffset +=4; - TempEntry.FirstFragmentTimestamp = (Payload[CurrentOffset] << 24) + (Payload[CurrentOffset+1] << 16) + (Payload[CurrentOffset+2]) + (Payload[CurrentOffset+3]); - CurrentOffset +=4; - TempEntry.FragmentDuration = (Payload[CurrentOffset] << 24) + (Payload[CurrentOffset+1] << 16) + (Payload[CurrentOffset+2]) + (Payload[CurrentOffset+3]); - CurrentOffset +=4; - if( TempEntry.FragmentDuration == 0 ) { - TempEntry.DiscontinuityIndicator = Payload[CurrentOffset]; - CurrentOffset++; - } - FragmentRunEntryTable.push_back(TempEntry); - } - - std::cerr << "Box_AFRT:\n"; - std::cerr << PrintOffset << " Version: " << (int)Version << "\n"; - std::cerr << PrintOffset << " Flags: " << (int)Flags << "\n"; - std::cerr << PrintOffset << " Timescale: " << (int)TimeScale << "\n"; - std::cerr << PrintOffset << " QualityEntryCount: " << (int)QualityEntryCount << "\n"; - std::cerr << PrintOffset << " QualitySegmentUrlModifiers:\n"; - for( uint32_t i = 0; i < QualitySegmentUrlModifiers.size( ); i++ ) { - std::cerr << PrintOffset << " " << i+1 << ": " << QualitySegmentUrlModifiers[i] << "\n"; - } - std::cerr << PrintOffset << " FragmentRunEntryCount: " << (int)FragmentRunEntryCount << "\n"; - std::cerr << PrintOffset << " FragmentRunEntryTable:\n"; - for( uint32_t i = 0; i < FragmentRunEntryTable.size( ); i++ ) { - std::cerr << PrintOffset << " " << i+1 << ":\n"; - std::cerr << PrintOffset << " FirstFragment: " << FragmentRunEntryTable[i].FirstFragment << "\n"; - std::cerr << PrintOffset << " FirstFragmentTimestamp: " << FragmentRunEntryTable[i].FirstFragmentTimestamp_Upperhalf << FragmentRunEntryTable[i].FirstFragmentTimestamp << "\n"; - std::cerr << PrintOffset << " FragmentDuration: " << FragmentRunEntryTable[i].FragmentDuration << "\n"; - if( FragmentRunEntryTable[i].FragmentDuration == 0 ) { - std::cerr << PrintOffset << " DiscontinuityIndicator: " << (int)FragmentRunEntryTable[i].DiscontinuityIndicator << "\n"; - } - } - } else if ( header.BoxType == 0x6D646174 ) { - std::cerr << "mdat box containing " << PayloadSize << " bytes of payload" << std::endl; - } else { - std::cerr << "BoxType '" - << (char)(header.BoxType >> 24) - << (char)((header.BoxType << 8) >> 24) - << (char)((header.BoxType << 16) >> 24) - << (char)((header.BoxType << 24) >> 24) - << "' not yet implemented!\n"; - } -} - diff --git a/util/MP4/box_afrt.cpp b/util/MP4/box_afrt.cpp index dd1aad62..06ddece2 100644 --- a/util/MP4/box_afrt.cpp +++ b/util/MP4/box_afrt.cpp @@ -2,14 +2,14 @@ #include #include -/* + struct afrt_fragmentrunentry { uint32_t FirstFragment; uint32_t FirstFragmentTimestamp; //write as uint64_t uint32_t FragmentDuration; uint8_t DiscontinuityIndicator;//if FragmentDuration == 0 };//afrt_fragmentrunentry -*/ + class Box_afrt { public: @@ -60,7 +60,9 @@ void Box_afrt::AddFragmentRunEntry( uint32_t FirstFragment, uint32_t FirstFragme FragmentRunEntryTable[Offset].FirstFragment = FirstFragment; FragmentRunEntryTable[Offset].FirstFragmentTimestamp = FirstFragmentTimestamp; FragmentRunEntryTable[Offset].FragmentDuration = FragmentsDuration; - FragmentRunEntryTable[Offset].DiscontinuityIndicator = Discontinuity; + if( FragmentsDuration == 0) { + FragmentRunEntryTable[Offset].DiscontinuityIndicator = Discontinuity; + } } void Box_afrt::SetDefaults( ) { @@ -87,7 +89,7 @@ void Box_afrt::WriteContent( ) { serializedFragmentEntries.append((char*)Box::uint32_to_uint8(FragmentRunEntryTable[i].FirstFragmentTimestamp),4); serializedFragmentEntries.append((char*)Box::uint32_to_uint8(FragmentRunEntryTable[i].FragmentDuration),4); if(FragmentRunEntryTable[i].FragmentDuration == 0) { - serializedFragmentEntries.append((char*)Box::uint8_to_uint8(FragmentRunEntryTable[i].DiscontinuityIndicator),1); + serializedFragmentEntries.append((char*)Box::uint8_to_uint8(FragmentRunEntryTable[i].DiscontinuityIndicator),1); } } From 501c997eebd8df21a6c9d91becd503bd3572be27 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Mon, 21 Mar 2011 15:41:35 +0100 Subject: [PATCH 104/788] Test6 --- util/MP4/interface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/MP4/interface.cpp b/util/MP4/interface.cpp index cf05e419..edbb04b3 100644 --- a/util/MP4/interface.cpp +++ b/util/MP4/interface.cpp @@ -548,7 +548,7 @@ std::string Interface::GenerateLiveBootstrap( uint32_t CurMediaTime ) { afrt->SetTimeScale( 1000 ); afrt->AddQualityEntry( "" ); std::cerr << "Setting RunEntry on 4000 ms\n"; - afrt->AddFragmentRunEntry( 1, 1 , 400 ); //FirstFragment, FirstFragmentTimestamp,Fragment Duration in milliseconds + afrt->AddFragmentRunEntry( 1, 1 , 4000 ); //FirstFragment, FirstFragmentTimestamp,Fragment Duration in milliseconds afrt->WriteContent( ); //SetUpASRT From 28037d0c506ae60277a992b74fba094f93835be5 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Mon, 21 Mar 2011 15:47:32 +0100 Subject: [PATCH 105/788] Test7 --- util/MP4/interface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/MP4/interface.cpp b/util/MP4/interface.cpp index edbb04b3..c94c23bb 100644 --- a/util/MP4/interface.cpp +++ b/util/MP4/interface.cpp @@ -548,7 +548,7 @@ std::string Interface::GenerateLiveBootstrap( uint32_t CurMediaTime ) { afrt->SetTimeScale( 1000 ); afrt->AddQualityEntry( "" ); std::cerr << "Setting RunEntry on 4000 ms\n"; - afrt->AddFragmentRunEntry( 1, 1 , 4000 ); //FirstFragment, FirstFragmentTimestamp,Fragment Duration in milliseconds + afrt->AddFragmentRunEntry( 1, 0 , 4000 ); //FirstFragment, FirstFragmentTimestamp,Fragment Duration in milliseconds afrt->WriteContent( ); //SetUpASRT From fc31bd989629907a42b99ecacc06ece084f6947c Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 21 Mar 2011 18:23:32 +0100 Subject: [PATCH 106/788] New FLV_Pack style, hopefully some fixes for HTTP dynamic --- util/flv.cpp | 8 +-- util/flv_data.cpp | 8 +-- util/flv_pack.cpp | 123 ++++++++++++++++++++++++++++++++++++++++++++++ util/flv_sock.cpp | 8 +-- 4 files changed, 126 insertions(+), 21 deletions(-) create mode 100644 util/flv_pack.cpp diff --git a/util/flv.cpp b/util/flv.cpp index c04d977a..8780077d 100644 --- a/util/flv.cpp +++ b/util/flv.cpp @@ -1,12 +1,6 @@ #include //for read() #include - -struct FLV_Pack { - int len; - int buf; - bool isKeyframe; - char * data; -};//FLV_Pack +#include "flv_pack.cpp" char FLVHeader[13]; bool All_Hell_Broke_Loose = false; diff --git a/util/flv_data.cpp b/util/flv_data.cpp index 28a18da4..ac176ee5 100644 --- a/util/flv_data.cpp +++ b/util/flv_data.cpp @@ -1,12 +1,6 @@ #include //for read() #include - -struct FLV_Pack { - int len; - int buf; - bool isKeyframe; - char * data; -};//FLV_Pack +#include "flv_pack.cpp" char FLVHeader[13]; bool All_Hell_Broke_Loose = false; diff --git a/util/flv_pack.cpp b/util/flv_pack.cpp new file mode 100644 index 00000000..a60bf6a5 --- /dev/null +++ b/util/flv_pack.cpp @@ -0,0 +1,123 @@ +#pragma once + +class FLV_Pack { + public: + int len; + int buf; + bool isKeyframe; + char * data; + std::string tagType(){ + std::string R = ""; + switch (data[0]){ + case 0x09: + switch (data[11] & 0x0F){ + case 1: R += "JPEG"; break; + case 2: R += "H263"; break; + case 3: R += "ScreenVideo1"; break; + case 4: R += "VP6"; break; + case 5: R += "VP6Alpha"; break; + case 6: R += "ScreenVideo2"; break; + case 7: R += "AVC"; break; + default: R += "unknown"; break; + } + R += " video "; + switch (data[11] & 0xF0){ + case 0x10: R += "keyframe"; break; + case 0x20: R += "iframe"; break; + case 0x30: R += "disposableiframe"; break; + case 0x40: R += "generatedkeyframe"; break; + case 0x50: R += "videoinfo"; break; + } + if ((data[11] & 0x0F) == 7){ + switch (data[12]){ + case 0: R += " header"; break; + case 1: R += " NALU"; break; + case 2: R += " endofsequence"; break; + } + } + break; + case 0x08: + switch (data[11] & 0xF0){ + case 0x00: R += "linear PCM PE"; break; + case 0x10: R += "ADPCM"; break; + case 0x20: R += "MP3"; break; + case 0x30: R += "linear PCM LE"; break; + case 0x40: R += "Nelly16kHz"; break; + case 0x50: R += "Nelly8kHz"; break; + case 0x60: R += "Nelly"; break; + case 0x70: R += "G711A-law"; break; + case 0x80: R += "G711mu-law"; break; + case 0x90: R += "reserved"; break; + case 0xA0: R += "AAC"; break; + case 0xB0: R += "Speex"; break; + case 0xE0: R += "MP38kHz"; break; + case 0xF0: R += "DeviceSpecific"; break; + default: R += "unknown"; break; + } + switch (data[11] & 0x0C){ + case 0x0: R += " 5.5kHz"; break; + case 0x4: R += " 11kHz"; break; + case 0x8: R += " 22kHz"; break; + case 0xC: R += " 44kHz"; break; + } + switch (data[11] & 0x02){ + case 0: R += " 8bit"; break; + case 2: R += " 16bit"; break; + } + switch (data[11] & 0x01){ + case 0: R += " mono"; break; + case 1: R += " stereo"; break; + } + R += " audio"; + if ((data[12] == 0) && ((data[11] & 0xF0) == 0xA0)){ + R += " initdata"; + } + break; + case 0x12: + R += "(meta)data"; + break; + default: + R += "unknown"; + break; + } + return R; + };//tagtype + unsigned int tagTime(){ + return (data[4] << 16) + (data[5] << 8) + data[6] + (data[7] << 24); + }//tagTime getter + void tagTime(unsigned int T){ + data[4] = ((T >> 16) & 0xFF); + data[5] = ((T >> 8) & 0xFF); + data[6] = (T & 0xFF); + data[7] = ((T >> 24) & 0xFF); + }//tagTime setter + FLV_Pack(){ + len = 0; buf = 0; data = 0; isKeyframe = false; + }//empty constructor + FLV_Pack(const FLV_Pack& O){ + buf = O.len; + len = buf; + if (len > 0){ + data = (char*)malloc(len); + memcpy(data, O.data, len); + }else{ + data = 0; + } + isKeyframe = O.isKeyframe; + }//copy constructor + FLV_Pack & operator= (const FLV_Pack& O){ + if (this != &O){//no self-assignment + if (data != 0){free(data);} + buf = O.len; + len = buf; + if (len > 0){ + data = (char*)malloc(len); + memcpy(data, O.data, len); + }else{ + data = 0; + } + isKeyframe = O.isKeyframe; + } + return *this; + }//assignment operator +};//FLV_Pack diff --git a/util/flv_sock.cpp b/util/flv_sock.cpp index be752d73..8b74c9aa 100644 --- a/util/flv_sock.cpp +++ b/util/flv_sock.cpp @@ -1,10 +1,4 @@ - -struct FLV_Pack { - int len; - int buf; - bool isKeyframe; - char * data; -};//FLV_Pack +#include "flv_pack.cpp" char FLVHeader[13]; bool All_Hell_Broke_Loose = false; From a21f471dcf3d64cfff930333b052492fff037367 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 21 Mar 2011 19:30:00 +0100 Subject: [PATCH 107/788] Logging ftw --- util/flv_data.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/util/flv_data.cpp b/util/flv_data.cpp index ac176ee5..fde4d2c5 100644 --- a/util/flv_data.cpp +++ b/util/flv_data.cpp @@ -65,6 +65,9 @@ bool FLV_GetPacket(FLV_Pack *& p, char * D, unsigned int S, unsigned int & P){ p->len += (p->data[2] << 8); p->len += (p->data[1] << 16); //if (p->buf < p->len){p->data = (char*)realloc(p->data, p->len);p->buf = p->len;} + if (p->data[0] > 0x12){ + printf("Invalid data: %2hhx %2hhx %2hhx %2hhx %2hhx %2hhx %2hhx\n", p->data[0], p->data[1], p->data[2], p->data[3], p->data[4], p->data[5], p->data[6]); + } done = false; } } From d911707ae7a761cac7bd9f31cfa1d6fdd2100cdb Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 21 Mar 2011 20:28:02 +0100 Subject: [PATCH 108/788] box.cpp herschreven voor snelheid en bugloosheid. Plus, coole edits in het algemeen. --- util/MP4/box.cpp | 83 ++++++++++-------------------------------- util/MP4/interface.cpp | 1 + 2 files changed, 20 insertions(+), 64 deletions(-) diff --git a/util/MP4/box.cpp b/util/MP4/box.cpp index 7b2d3361..57ccbb93 100644 --- a/util/MP4/box.cpp +++ b/util/MP4/box.cpp @@ -9,11 +9,6 @@ #include #include -struct BoxHeader { - uint32_t TotalSize; - uint32_t BoxType; -};//BoxHeader struct - class Box { public: Box(); @@ -31,73 +26,48 @@ class Box { static uint8_t * uint32_to_uint8( uint32_t data ); static uint8_t * uint16_to_uint8( uint16_t data ); static uint8_t * uint8_to_uint8( uint8_t data ); - BoxHeader GetHeader( ); void ResetPayload( ); + private: uint8_t * Payload; - BoxHeader header; uint32_t PayloadSize; };//Box Class Box::Box() { - Payload = NULL; + Payload = (uint8_t *)malloc(8); PayloadSize = 0; } Box::Box(uint32_t BoxType) { - header.BoxType = BoxType; - Payload = NULL; + Payload = (uint8_t *)malloc(8); + SetBoxType(BoxType); PayloadSize = 0; } Box::Box(uint8_t * Content, uint32_t length) { - header.TotalSize = (Content[0] << 24) + (Content[1] << 16) + (Content[2] << 8) + (Content[3]); - if(header.TotalSize != length) { std::cerr << "Warning: length sizes differ\n"; } - header.BoxType = (Content[4] << 24) + (Content[5] << 16) + (Content[6] << 8) + (Content[7]); - std::cerr << "Created new box with type \"" - << (char)(header.BoxType >> 24) - << (char)((header.BoxType << 8) >> 24) - << (char)((header.BoxType << 16) >> 24) - << (char)((header.BoxType << 24) >> 24) - << "\"\n"; PayloadSize = length-8; - Payload = new uint8_t[PayloadSize]; - memcpy( Payload, &Content[8], PayloadSize ); + Payload = (uint8_t *)malloc(length); + memcpy(Payload, Content, length); } Box::~Box() { + if (Payload) free(Payload); } void Box::SetBoxType(uint32_t BoxType) { - header.BoxType = BoxType; + ((unsigned int*)Payload)[1] = htonl(BoxType); } uint32_t Box::GetBoxType() { - return header.BoxType; + return ntohl(((unsigned int*)Payload)[1]); } void Box::SetPayload(uint32_t Size, uint8_t * Data, uint32_t Index) { - uint8_t * tempchar = NULL; if ( Index + Size > PayloadSize ) { - if ( Payload ) { - tempchar = new uint8_t[PayloadSize]; - memcpy( tempchar, Payload, PayloadSize ); - delete Payload; - } PayloadSize = Index + Size; - Payload = new uint8_t[PayloadSize]; - if( tempchar ) { - memcpy( Payload, tempchar, Index ); - } else { - for(uint32_t i = 0; i < Index; i++) { Payload[i] = 0; } - } - memcpy( &Payload[Index], Data, Size ); - header.TotalSize = PayloadSize + 8; - if( tempchar ) { - delete tempchar; - } - } else { - memcpy( &Payload[Index], Data, Size ); + ((unsigned int*)Payload)[0] = htonl(PayloadSize+8); + Payload = (uint8_t *)realloc(Payload, PayloadSize + 8); } + memcpy(Payload + 8 + Index, Data, Size); } uint32_t Box::GetPayloadSize() { @@ -105,29 +75,21 @@ uint32_t Box::GetPayloadSize() { } uint8_t * Box::GetPayload() { - uint8_t * temp = new uint8_t[PayloadSize]; - memcpy( temp, Payload, PayloadSize ); - return temp; + return Payload+8; } uint8_t * Box::GetPayload(uint32_t Index, uint32_t & Size) { - if(Index > PayloadSize) { return NULL; } + if(Index > PayloadSize) {Size = 0;} if(Index + Size > PayloadSize) { Size = PayloadSize - Index; } - uint8_t * temp = new uint8_t[Size - Index]; - memcpy( temp, &Payload[Index], Size - Index ); - return temp; + return Payload + 8 + Index; } uint32_t Box::GetBoxedDataSize() { - return header.TotalSize; + return ntohl(((unsigned int*)Payload)[0]); } uint8_t * Box::GetBoxedData( ) { - uint8_t * temp = new uint8_t[header.TotalSize]; - memcpy( temp, uint32_to_uint8(header.TotalSize), 4 ); - memcpy( &temp[4], uint32_to_uint8(header.BoxType), 4 ); - memcpy( &temp[8], Payload, PayloadSize ); - return temp; + return Payload; } @@ -153,15 +115,8 @@ uint8_t * Box::uint8_to_uint8( uint8_t data ) { return temp; } -BoxHeader Box::GetHeader( ) { - return header; -} - void Box::ResetPayload( ) { - header.TotalSize -= PayloadSize; PayloadSize = 0; - if(Payload) { - delete Payload; - Payload = NULL; - } + Payload = (uint8_t *)realloc(Payload, PayloadSize + 8); + ((unsigned int*)Payload)[0] = htonl(0); } diff --git a/util/MP4/interface.cpp b/util/MP4/interface.cpp index edbb04b3..a7bbd58f 100644 --- a/util/MP4/interface.cpp +++ b/util/MP4/interface.cpp @@ -582,5 +582,6 @@ std::string Interface::mdatFold(std::string data){ std::string Result; mdat->SetContent((uint8_t*)data.c_str(), data.size()); Result.append((char*)mdat->GetBox()->GetBoxedData(), (int)mdat->GetBox()->GetBoxedDataSize()); + delete mdat; return Result; } From f1373d836e3a7e65c8109d914d1ed6976721cdfd Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 21 Mar 2011 20:50:35 +0100 Subject: [PATCH 109/788] Fix --- util/MP4/interface.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/util/MP4/interface.cpp b/util/MP4/interface.cpp index a7bbd58f..666e9632 100644 --- a/util/MP4/interface.cpp +++ b/util/MP4/interface.cpp @@ -578,10 +578,9 @@ std::string Interface::GenerateLiveBootstrap( uint32_t CurMediaTime ) { } std::string Interface::mdatFold(std::string data){ - static Box_mdat * mdat = new Box_mdat; + static Box * mdat = new Box(0x6D646174); std::string Result; - mdat->SetContent((uint8_t*)data.c_str(), data.size()); - Result.append((char*)mdat->GetBox()->GetBoxedData(), (int)mdat->GetBox()->GetBoxedDataSize()); - delete mdat; + mdat->SetPayload((uint8_t*)data.c_str(), data.size()); + Result.append((char*)mdat->GetBoxedData(), (int)mdat->GetBoxedDataSize()); return Result; } From 414405f99fecda6f03cb9195afc87d5065a32e2a Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 21 Mar 2011 20:52:25 +0100 Subject: [PATCH 110/788] Fix --- util/MP4/interface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/MP4/interface.cpp b/util/MP4/interface.cpp index 666e9632..971279af 100644 --- a/util/MP4/interface.cpp +++ b/util/MP4/interface.cpp @@ -580,7 +580,7 @@ std::string Interface::GenerateLiveBootstrap( uint32_t CurMediaTime ) { std::string Interface::mdatFold(std::string data){ static Box * mdat = new Box(0x6D646174); std::string Result; - mdat->SetPayload((uint8_t*)data.c_str(), data.size()); + mdat->SetPayload(data.size(), (uint8_t*)data.c_str()); Result.append((char*)mdat->GetBoxedData(), (int)mdat->GetBoxedDataSize()); return Result; } From 3997453751134e5e96cea1c9623d356d212d0986 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 21 Mar 2011 21:07:00 +0100 Subject: [PATCH 111/788] Fix --- util/MP4/interface.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/util/MP4/interface.cpp b/util/MP4/interface.cpp index 971279af..69f6aac8 100644 --- a/util/MP4/interface.cpp +++ b/util/MP4/interface.cpp @@ -578,9 +578,12 @@ std::string Interface::GenerateLiveBootstrap( uint32_t CurMediaTime ) { } std::string Interface::mdatFold(std::string data){ - static Box * mdat = new Box(0x6D646174); std::string Result; - mdat->SetPayload(data.size(), (uint8_t*)data.c_str()); - Result.append((char*)mdat->GetBoxedData(), (int)mdat->GetBoxedDataSize()); + unsigned int t_int; + t_int = htonl(data.size()+8); + Result.append(t_int, 4); + t_int = htonl(0x6D646174); + Result.append(t_int, 4); + Result.append(data); return Result; } From e782cc3dc2e570cf1d006566ec88afcea3d38d24 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 21 Mar 2011 21:09:01 +0100 Subject: [PATCH 112/788] Fix --- util/MP4/interface.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/MP4/interface.cpp b/util/MP4/interface.cpp index 69f6aac8..3f8f96a6 100644 --- a/util/MP4/interface.cpp +++ b/util/MP4/interface.cpp @@ -581,9 +581,9 @@ std::string Interface::mdatFold(std::string data){ std::string Result; unsigned int t_int; t_int = htonl(data.size()+8); - Result.append(t_int, 4); + Result.append((char*)&t_int, 4); t_int = htonl(0x6D646174); - Result.append(t_int, 4); + Result.append((char*)&t_int, 4); Result.append(data); return Result; } From 62ccca1427a9fe3787cf002bff6102d62d683a0a Mon Sep 17 00:00:00 2001 From: Thulinma Date: Wed, 6 Apr 2011 03:53:30 +0200 Subject: [PATCH 113/788] make docs now works (needs doxygen installed), added documentation to a LOT of stuff, refactored most utility functions, complete rewrites of DDV sockets and the FLV parsers, updated HTTP_Box_Parser and Connector_HTTP to be able to cope with these new changes - Connector_RTMP, Buffer and Connector_RAW are currently broken. Need to be fixed ASAP. --- Doxyfile | 296 +++++++++++++++++++++++++++++++ util/MP4/box.cpp | 1 + util/ddv_socket.cpp | 402 +++++++++++++++++++++++++++++------------- util/ddv_socket.h | 53 ++++++ util/flv.cpp | 88 --------- util/flv_data.cpp | 87 --------- util/flv_pack.cpp | 123 ------------- util/flv_sock.cpp | 106 ----------- util/flv_tag.cpp | 391 ++++++++++++++++++++++++++++++++++++++++ util/flv_tag.h | 40 +++++ util/http_parser.cpp | 66 ++----- util/http_parser.h | 41 +++++ util/server_setup.cpp | 39 ++-- 13 files changed, 1132 insertions(+), 601 deletions(-) create mode 100644 Doxyfile create mode 100644 util/ddv_socket.h delete mode 100644 util/flv.cpp delete mode 100644 util/flv_data.cpp delete mode 100644 util/flv_pack.cpp delete mode 100644 util/flv_sock.cpp create mode 100644 util/flv_tag.cpp create mode 100644 util/flv_tag.h create mode 100644 util/http_parser.h diff --git a/Doxyfile b/Doxyfile new file mode 100644 index 00000000..dd8ba205 --- /dev/null +++ b/Doxyfile @@ -0,0 +1,296 @@ +# Doxyfile 1.6.3 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = DDVTECH Streaming Server +PROJECT_NUMBER = 1 +OUTPUT_DIRECTORY = ./docs +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = YES +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = YES +QT_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 2 +ALIASES = +OPTIMIZE_OUTPUT_FOR_C = YES +OPTIMIZE_OUTPUT_JAVA = NO +OPTIMIZE_FOR_FORTRAN = NO +OPTIMIZE_OUTPUT_VHDL = NO +EXTENSION_MAPPING = +BUILTIN_STL_SUPPORT = YES +CPP_CLI_SUPPORT = NO +SIP_SUPPORT = NO +IDL_PROPERTY_SUPPORT = NO +DISTRIBUTE_GROUP_DOC = NO +SUBGROUPING = YES +TYPEDEF_HIDES_STRUCT = NO +SYMBOL_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = YES +EXTRACT_PRIVATE = NO +EXTRACT_STATIC = YES +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +EXTRACT_ANON_NSPACES = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = YES +FORCE_LOCAL_INCLUDES = NO +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_MEMBERS_CTORS_1ST = NO +SORT_GROUP_NAMES = NO +SORT_BY_SCOPE_NAME = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_DIRECTORIES = YES +SHOW_FILES = YES +SHOW_NAMESPACES = YES +FILE_VERSION_FILTER = +LAYOUT_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = NO +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +INPUT = . +INPUT_ENCODING = UTF-8 +FILE_PATTERNS = +RECURSIVE = YES +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = */.git/* +EXCLUDE_SYMBOLS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +SOURCE_BROWSER = NO +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +REFERENCES_LINK_SOURCE = YES +USE_HTAGS = NO +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +ALPHABETICAL_INDEX = NO +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_TIMESTAMP = YES +HTML_ALIGN_MEMBERS = YES +HTML_DYNAMIC_SECTIONS = NO + +GENERATE_DOCSET = NO +DOCSET_FEEDNAME = "Doxygen generated docs" +DOCSET_BUNDLE_ID = org.doxygen.Project + +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +CHM_INDEX_ENCODING = +BINARY_TOC = NO +TOC_EXPAND = NO + +GENERATE_QHP = NO +QCH_FILE = +QHP_NAMESPACE = org.doxygen.Project +QHP_VIRTUAL_FOLDER = doc +QHP_CUST_FILTER_NAME = +QHP_CUST_FILTER_ATTRS = +QHP_SECT_FILTER_ATTRS = +QHG_LOCATION = + +GENERATE_ECLIPSEHELP = NO +ECLIPSE_DOC_ID = org.doxygen.Project +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 4 +GENERATE_TREEVIEW = NO +USE_INLINE_TREES = NO +TREEVIEW_WIDTH = 250 +FORMULA_FONTSIZE = 10 +SEARCHENGINE = YES +SERVER_BASED_SEARCH = NO + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +GENERATE_LATEX = YES +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4wide +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = YES +USE_PDFLATEX = YES +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +LATEX_SOURCE_CODE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +GENERATE_XML = NO +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +CLASS_DIAGRAMS = YES +MSCGEN_PATH = +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = NO +DOT_FONTNAME = FreeSans +DOT_FONTSIZE = 10 +DOT_FONTPATH = +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = NO +CALLER_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = png +DOT_PATH = +DOTFILE_DIRS = +DOT_GRAPH_MAX_NODES = 50 +MAX_DOT_GRAPH_DEPTH = 0 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES + diff --git a/util/MP4/box.cpp b/util/MP4/box.cpp index 57ccbb93..9f790b7f 100644 --- a/util/MP4/box.cpp +++ b/util/MP4/box.cpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index b1bd03e0..2835a5cf 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -1,141 +1,112 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "ddv_socket.h" -bool socketError = false; -bool socketBlocking = false; +/// Create a new base socket. This is a basic constructor for converting any valid socket to a DDV::Socket. +/// \param sockNo Integer representing the socket to convert. +DDV::Socket::Socket(int sockNo){ + sock = sockNo; + Error = false; + Blocking = false; +}//DDV::Socket basic constructor -int DDV_OpenUnix(std::string adres, bool nonblock = false){ - int s = socket(PF_UNIX, SOCK_STREAM, 0); +/// Close connection. The internal socket is closed and then set to -1. +void DDV::Socket::close(){ + #if DEBUG >= 3 + fprintf(stderr, "Socket closed.\n"); + #endif + ::close(sock); + sock = -1; +}//DDV::Socket::close + +/// Returns internal socket number. +int DDV::Socket::getSocket(){return sock;} + +/// Create a new Unix Socket. This socket will (try to) connect to the given address right away. +/// \param address String containing the location of the Unix socket to connect to. +/// \param nonblock Whether the socket should be nonblocking. False by default. +DDV::Socket::Socket(std::string address, bool nonblock){ + sock = socket(PF_UNIX, SOCK_STREAM, 0); + if (sock < 0){ + #if DEBUG >= 1 + fprintf(stderr, "Could not create socket! Error: %s\n", strerror(errno)); + #endif + return; + } + Error = false; + Blocking = false; sockaddr_un addr; addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, adres.c_str(), adres.size()+1); - int r = connect(s, (sockaddr*)&addr, sizeof(addr)); + strncpy(addr.sun_path, address.c_str(), address.size()+1); + int r = connect(sock, (sockaddr*)&addr, sizeof(addr)); if (r == 0){ if (nonblock){ - int flags = fcntl(s, F_GETFL, 0); + int flags = fcntl(sock, F_GETFL, 0); flags |= O_NONBLOCK; - fcntl(s, F_SETFL, flags); - } - return s; - }else{ - close(s); - return 0; - } -} - -int DDV_Listen(int port){ - int s = socket(AF_INET, SOCK_STREAM, 0); - int on = 1; - setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - struct sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_port = htons(port);//port 8888 - inet_pton(AF_INET, "0.0.0.0", &addr.sin_addr);//listen on all interfaces - int ret = bind(s, (sockaddr*)&addr, sizeof(addr));//bind to all interfaces, chosen port - if (ret == 0){ - ret = listen(s, 100);//start listening, backlog of 100 allowed - if (ret == 0){ - return s; - }else{ - fprintf(stderr, "Listen failed! Error: %s\n", strerror(errno)); - close(s); - return 0; + fcntl(sock, F_SETFL, flags); } }else{ - fprintf(stderr, "Binding failed! Error: %s\n", strerror(errno)); - close(s); - return 0; + #if DEBUG >= 1 + fprintf(stderr, "Could not connect to %s! Error: %s\n", address.c_str(), strerror(errno)); + #endif + close(); } -} +}//DDV::Socket Unix Contructor -int DDV_UnixListen(std::string adres, bool nonblock = false){ - unlink(adres.c_str()); - int s = socket(AF_UNIX, SOCK_STREAM, 0); - if (nonblock){ - int flags = fcntl(s, F_GETFL, 0); - flags |= O_NONBLOCK; - fcntl(s, F_SETFL, flags); - } - sockaddr_un addr; - addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, adres.c_str(), adres.size()+1); - int ret = bind(s, (sockaddr*)&addr, sizeof(addr)); - if (ret == 0){ - ret = listen(s, 100);//start listening, backlog of 100 allowed - if (ret == 0){ - return s; - }else{ - fprintf(stderr, "Listen failed! Error: %s\n", strerror(errno)); - close(s); - return 0; - } - }else{ - fprintf(stderr, "Binding failed! Error: %s\n", strerror(errno)); - close(s); - return 0; - } -} - -int DDV_Accept(int sock, bool nonblock = false){ - int r = accept(sock, 0, 0); - if ((r >= 0) && nonblock){ - int flags = fcntl(r, F_GETFL, 0); - flags |= O_NONBLOCK; - fcntl(r, F_SETFL, flags); - } - return r; -} - -bool DDV_write(const void * buffer, int todo, int sock){ - int sofar = 0; - socketBlocking = false; - while (sofar != todo){ - int r = send(sock, (char*)buffer + sofar, todo-sofar, 0); - if (r <= 0){ - switch (errno){ - case EWOULDBLOCK: socketBlocking = true; break; - default: - socketError = true; - fprintf(stderr, "Could not write! %s\n", strerror(errno)); - return false; - break; - } - } - sofar += r; - } - return true; -} - -signed int DDV_ready(int sock){ +/// Returns the ready-state for this socket. +/// \returns 1 if data is waiting to be read, -1 if not connected, 0 otherwise. +signed int DDV::Socket::ready(){ + if (sock < 0) return -1; char tmp; int preflags = fcntl(sock, F_GETFL, 0); int postflags = preflags | O_NONBLOCK; fcntl(sock, F_SETFL, postflags); int r = recv(sock, &tmp, 1, MSG_PEEK); fcntl(sock, F_SETFL, preflags); + if (r < 0){ + if (errno == EAGAIN || errno == EWOULDBLOCK){ + return 0; + }else{ + #if DEBUG >= 2 + fprintf(stderr, "Socket ready error! Error: %s\n", strerror(errno)); + #endif + close(); + return -1; + } + } + if (r == 0){ + close(); return -1; + } return r; } -bool DDV_read(void * buffer, int todo, int sock){ +/// Returns the connected-state for this socket. +/// Note that this function might be slightly behind the real situation. +/// The connection status is updated after every read/write attempt, when errors occur +/// and when the socket is closed manually. +/// \returns True if socket is connected, false otherwise. +bool DDV::Socket::connected(){ + return (sock >= 0); +} + +/// Writes data to socket. This function blocks if the socket is blocking and all data cannot be written right away. +/// If the socket is nonblocking and not all data can be written, this function sets internal variable Blocking to true +/// and returns false. +/// \param buffer Location of the buffer to write from. +/// \param len Amount of bytes to write. +/// \returns True if the whole write was succesfull, false otherwise. +bool DDV::Socket::write(const void * buffer, int len){ int sofar = 0; - socketBlocking = false; - while (sofar != todo){ - int r = recv(sock, (char*)buffer + sofar, todo-sofar, 0); + Blocking = false; + while (sofar != len){ + int r = send(sock, (char*)buffer + sofar, len-sofar, 0); if (r <= 0){ switch (errno){ - case EWOULDBLOCK: socketBlocking = true; break; + case EWOULDBLOCK: Blocking = true; break; default: - socketError = true; - fprintf(stderr, "Could not read! %s\n", strerror(errno)); + Error = true; + #if DEBUG >= 2 + fprintf(stderr, "Could not write data! Error: %s\n", strerror(errno)); + #endif + close(); return false; break; } @@ -143,41 +114,218 @@ bool DDV_read(void * buffer, int todo, int sock){ sofar += r; } return true; -} +}//DDv::Socket::write -bool DDV_read(void * buffer, int width, int count, int sock){return DDV_read(buffer, width*count, sock);} -bool DDV_write(void * buffer, int width, int count, int sock){return DDV_write(buffer, width*count, sock);} +/// Reads data from socket. This function blocks if the socket is blocking and all data cannot be read right away. +/// If the socket is nonblocking and not all data can be read, this function sets internal variable Blocking to true +/// and returns false. +/// \param buffer Location of the buffer to read to. +/// \param len Amount of bytes to read. +/// \returns True if the whole read was succesfull, false otherwise. +bool DDV::Socket::read(void * buffer, int len){ + int sofar = 0; + Blocking = false; + while (sofar != len){ + int r = recv(sock, (char*)buffer + sofar, len-sofar, 0); + if (r <= 0){ + switch (errno){ + case EWOULDBLOCK: Blocking = true; break; + default: + Error = true; + #if DEBUG >= 2 + fprintf(stderr, "Could not read data! Error: %s\n", strerror(errno)); + #endif + close(); + return false; + break; + } + } + sofar += r; + } + return true; +}//DDV::Socket::read +/// Read call that is compatible with file access syntax. This function simply calls the other read function. +bool DDV::Socket::read(void * buffer, int width, int count){return read(buffer, width*count);} +/// Write call that is compatible with file access syntax. This function simply calls the other write function. +bool DDV::Socket::write(void * buffer, int width, int count){return write(buffer, width*count);} +/// Write call that is compatible with std::string. This function simply calls the other write function. +bool DDV::Socket::write(const std::string data){return write(data.c_str(), data.size());} -int DDV_iwrite(void * buffer, int todo, int sock){ - int r = send(sock, buffer, todo, 0); +/// Incremental write call. This function tries to write len bytes to the socket from the buffer, +/// returning the amount of bytes it actually wrote. +/// \param buffer Location of the buffer to write from. +/// \param len Amount of bytes to write. +/// \returns The amount of bytes actually written. +int DDV::Socket::iwrite(void * buffer, int len){ + int r = send(sock, buffer, len, 0); if (r < 0){ switch (errno){ case EWOULDBLOCK: return 0; break; default: - socketError = true; - fprintf(stderr, "Could not write! %s\n", strerror(errno)); + Error = true; + #if DEBUG >= 2 + fprintf(stderr, "Could not iwrite data! Error: %s\n", strerror(errno)); + #endif + close(); return 0; break; } } return r; -} +}//DDV::Socket::iwrite -int DDV_iread(void * buffer, int todo, int sock){ - int r = recv(sock, buffer, todo, 0); +/// Incremental read call. This function tries to read len bytes to the buffer from the socket, +/// returning the amount of bytes it actually read. +/// \param buffer Location of the buffer to read to. +/// \param len Amount of bytes to read. +/// \returns The amount of bytes actually read. +int DDV::Socket::iread(void * buffer, int len){ + int r = recv(sock, buffer, len, 0); if (r < 0){ switch (errno){ - case EWOULDBLOCK: break; + case EWOULDBLOCK: return 0; break; default: - socketError = true; - fprintf(stderr, "Could not read! %s\n", strerror(errno)); + Error = true; + #if DEBUG >= 2 + fprintf(stderr, "Could not iread data! Error: %s\n", strerror(errno)); + #endif + close(); return 0; break; } } return r; +}//DDV::Socket::iread + +/// Create a new base ServerSocket. The socket is never connected, and a placeholder for later connections. +DDV::ServerSocket::ServerSocket(){ + sock = -1; +}//DDV::ServerSocket base Constructor + +/// Create a new TCP ServerSocket. The socket is immediately bound and set to listen. +/// A maximum of 100 connections will be accepted between accept() calls. +/// Any further connections coming in will be dropped. +/// \param port The TCP port to listen on +/// \param hostname (optional) The interface to bind to. The default is 0.0.0.0 (all interfaces). +/// \param nonblock (optional) Whether accept() calls will be nonblocking. Default is false (blocking). +DDV::ServerSocket::ServerSocket(int port, std::string hostname, bool nonblock){ + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0){ + #if DEBUG >= 1 + fprintf(stderr, "Could not create socket! Error: %s\n", strerror(errno)); + #endif + return; + } + int on = 1; + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + if (nonblock){ + int flags = fcntl(sock, F_GETFL, 0); + flags |= O_NONBLOCK; + fcntl(sock, F_SETFL, flags); + } + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(port);//set port + inet_pton(AF_INET, hostname.c_str(), &addr.sin_addr);//set interface, 0.0.0.0 (default) is all + int ret = bind(sock, (sockaddr*)&addr, sizeof(addr));//do the actual bind + if (ret == 0){ + ret = listen(sock, 100);//start listening, backlog of 100 allowed + if (ret == 0){ + return; + }else{ + #if DEBUG >= 1 + fprintf(stderr, "Listen failed! Error: %s\n", strerror(errno)); + #endif + close(); + return; + } + }else{ + #if DEBUG >= 1 + fprintf(stderr, "Binding failed! Error: %s\n", strerror(errno)); + #endif + close(); + return; + } +}//DDV::ServerSocket TCP Constructor + +/// Create a new Unix ServerSocket. The socket is immediately bound and set to listen. +/// A maximum of 100 connections will be accepted between accept() calls. +/// Any further connections coming in will be dropped. +/// The address used will first be unlinked - so it succeeds if the Unix socket already existed. Watch out for this behaviour - it will delete any file located at address! +/// \param address The location of the Unix socket to bind to. +/// \param nonblock (optional) Whether accept() calls will be nonblocking. Default is false (blocking). +DDV::ServerSocket::ServerSocket(std::string address, bool nonblock){ + unlink(address.c_str()); + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock < 0){ + #if DEBUG >= 1 + fprintf(stderr, "Could not create socket! Error: %s\n", strerror(errno)); + #endif + return; + } + if (nonblock){ + int flags = fcntl(sock, F_GETFL, 0); + flags |= O_NONBLOCK; + fcntl(sock, F_SETFL, flags); + } + sockaddr_un addr; + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, address.c_str(), address.size()+1); + int ret = bind(sock, (sockaddr*)&addr, sizeof(addr)); + if (ret == 0){ + ret = listen(sock, 100);//start listening, backlog of 100 allowed + if (ret == 0){ + return; + }else{ + #if DEBUG >= 1 + fprintf(stderr, "Listen failed! Error: %s\n", strerror(errno)); + #endif + close(); + return; + } + }else{ + #if DEBUG >= 1 + fprintf(stderr, "Binding failed! Error: %s\n", strerror(errno)); + #endif + close(); + return; + } +}//DDV::ServerSocket Unix Constructor + +/// Accept any waiting connections. If the DDV::ServerSocket is blocking, this function will block until there is an incoming connection. +/// If the DDV::ServerSocket is nonblocking, it might return a DDV::Socket that is not connected, so check for this. +/// \param nonblock (optional) Whether the newly connected socket should be nonblocking. Default is false (blocking). +/// \returns A DDV::Socket, which may or may not be connected, depending on settings and circumstances. +DDV::Socket DDV::ServerSocket::accept(bool nonblock){ + if (sock < 0){return DDV::Socket(-1);} + int r = ::accept(sock, 0, 0); + if ((r >= 0) && nonblock){ + int flags = fcntl(r, F_GETFL, 0); + flags |= O_NONBLOCK; + fcntl(r, F_SETFL, flags); + } + if (r < 0){ + if (errno != EWOULDBLOCK && errno != EAGAIN){close();} + } + return DDV::Socket(r); } +/// Close connection. The internal socket is closed and then set to -1. +void DDV::ServerSocket::close(){ + ::close(sock); + sock = -1; +}//DDV::ServerSocket::close +/// Returns the connected-state for this socket. +/// Note that this function might be slightly behind the real situation. +/// The connection status is updated after every accept attempt, when errors occur +/// and when the socket is closed manually. +/// \returns True if socket is connected, false otherwise. +bool DDV::ServerSocket::connected(){ + return (sock >= 0); +}//DDV::ServerSocket::connected + +/// Returns internal socket number. +int DDV::ServerSocket::getSocket(){return sock;} diff --git a/util/ddv_socket.h b/util/ddv_socket.h new file mode 100644 index 00000000..cc9421ae --- /dev/null +++ b/util/ddv_socket.h @@ -0,0 +1,53 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +///Holds DDV Socket tools. +namespace DDV{ + + /// This class is for easy communicating through sockets, either TCP or Unix. + class Socket{ + private: + int sock; ///< Internally saved socket number. + public: + Socket(int sockNo); ///< Create a new base socket. + Socket(std::string adres, bool nonblock = false); ///< Create a new Unix Socket. + 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. + signed int ready(); ///< Returns the ready-state for this socket. + bool connected(); ///< Returns the connected-state for this socket. + bool read(void * buffer, int len); ///< Reads data from socket. + bool read(void * buffer, int width, int count); ///< Read call that is compatible with file access syntax. + bool write(const void * buffer, int len); ///< Writes data to socket. + bool write(void * buffer, int width, int count); ///< Write call that is compatible with file access syntax. + bool write(const std::string data); ///< Write call that is compatible with std::string. + int iwrite(void * buffer, int len); ///< Incremental write call. + int iread(void * buffer, int len); ///< Incremental read call. + void close(); ///< Close connection. + int getSocket(); ///< Returns internal socket number. + }; + + /// This class is for easily setting up listening socket, either TCP or Unix. + class ServerSocket{ + private: + int sock; ///< Internally saved socket number. + public: + ServerSocket(); ///< Create a new base ServerSocket. + ServerSocket(int port, std::string hostname = "0.0.0.0", bool nonblock = false); ///< Create a new TCP ServerSocket. + ServerSocket(std::string adres, bool nonblock = false); ///< Create a new Unix ServerSocket. + Socket accept(bool nonblock = false); ///< Accept any waiting connections. + bool connected(); ///< Returns the connected-state for this socket. + void close(); ///< Close connection. + int getSocket(); ///< Returns internal socket number. + }; + +}; diff --git a/util/flv.cpp b/util/flv.cpp deleted file mode 100644 index 8780077d..00000000 --- a/util/flv.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include //for read() -#include -#include "flv_pack.cpp" - -char FLVHeader[13]; -bool All_Hell_Broke_Loose = false; - -//checks FLV Header for correctness -//returns true if everything is alright, false otherwise -bool FLV_Checkheader(char * header){ - if (header[0] != 'F') return false; - if (header[1] != 'L') return false; - if (header[2] != 'V') return false; - if (header[8] != 0x09) return false; - if (header[9] != 0) return false; - if (header[10] != 0) return false; - if (header[11] != 0) return false; - if (header[12] != 0) return false; - return true; -}//FLV_Checkheader - -//returns true if header is an FLV header -bool FLV_Isheader(char * header){ - if (header[0] != 'F') return false; - if (header[1] != 'L') return false; - if (header[2] != 'V') return false; - return true; -}//FLV_Isheader - -bool ReadUntil(char * buffer, unsigned int count, unsigned int & sofar){ - if (sofar >= count){return true;} - int r = 0; - r = fread(buffer + sofar,1,count-sofar,stdin); - if (r < 0){All_Hell_Broke_Loose = true; return false;} - sofar += r; - if (sofar >= count){return true;} - return false; -} - -//gets a packet, storing in given FLV_Pack pointer. -//will assign pointer if null -//resizes FLV_Pack data field bigger if data doesn't fit -// (does not auto-shrink for speed!) -bool FLV_GetPacket(FLV_Pack *& p){ - int preflags = fcntl(fileno(stdin), F_GETFL, 0); - int postflags = preflags | O_NONBLOCK; - fcntl(fileno(stdin), F_SETFL, postflags); - static bool done = true; - static unsigned int sofar = 0; - if (!p){p = (FLV_Pack*)calloc(1, sizeof(FLV_Pack));} - if (p->buf < 15){p->data = (char*)realloc(p->data, 15); p->buf = 15;} - - if (done){ - //read a header - if (ReadUntil(p->data, 11, sofar)){ - //if its a correct FLV header, throw away and read tag header - if (FLV_Isheader(p->data)){ - if (ReadUntil(p->data, 13, sofar)){ - if (FLV_Checkheader(p->data)){ - sofar = 0; - memcpy(FLVHeader, p->data, 13); - }else{All_Hell_Broke_Loose = true;} - } - }else{ - //if a tag header, calculate length and read tag body - p->len = p->data[3] + 15; - p->len += (p->data[2] << 8); - p->len += (p->data[1] << 16); - if (p->buf < p->len){p->data = (char*)realloc(p->data, p->len);p->buf = p->len;} - done = false; - } - } - }else{ - //read tag body - if (ReadUntil(p->data, p->len, sofar)){ - //calculate keyframeness, next time read header again, return true - p->isKeyframe = false; - if ((p->data[0] == 0x09) && (((p->data[11] & 0xf0) >> 4) == 1)){p->isKeyframe = true;} - done = true; - sofar = 0; - fcntl(fileno(stdin), F_SETFL, preflags); - return true; - } - } - fcntl(fileno(stdin), F_SETFL, preflags); - return false; -}//FLV_GetPacket - diff --git a/util/flv_data.cpp b/util/flv_data.cpp deleted file mode 100644 index fde4d2c5..00000000 --- a/util/flv_data.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include //for read() -#include -#include "flv_pack.cpp" - -char FLVHeader[13]; -bool All_Hell_Broke_Loose = false; - -//checks FLV Header for correctness -//returns true if everything is alright, false otherwise -bool FLV_Checkheader(char * header){ - if (header[0] != 'F') return false; - if (header[1] != 'L') return false; - if (header[2] != 'V') return false; - if (header[8] != 0x09) return false; - if (header[9] != 0) return false; - if (header[10] != 0) return false; - if (header[11] != 0) return false; - if (header[12] != 0) return false; - return true; -}//FLV_Checkheader - -//returns true if header is an FLV header -bool FLV_Isheader(char * header){ - if (header[0] != 'F') return false; - if (header[1] != 'L') return false; - if (header[2] != 'V') return false; - return true; -}//FLV_Isheader - -bool ReadUntil(char * buffer, unsigned int count, unsigned int & sofar, char * D, unsigned int S, unsigned int & P){ - if (sofar >= count){return true;} - int r = 0; - if (P+(count-sofar) > S){r = S-P;}else{r = count-sofar;} - memcpy(buffer+sofar, D+P, r); - P += r; - sofar += r; - if (sofar >= count){return true;} - return false; -} - -//gets a packet, storing in given FLV_Pack pointer. -//will assign pointer if null -//resizes FLV_Pack data field bigger if data doesn't fit -// (does not auto-shrink for speed!) -bool FLV_GetPacket(FLV_Pack *& p, char * D, unsigned int S, unsigned int & P){ - static bool done = true; - static unsigned int sofar = 0; - if (!p){p = (FLV_Pack*)calloc(1, sizeof(FLV_Pack));} - if (p->buf < 15){p->data = (char*)realloc(p->data, 15000000); p->buf = 15000000;} - - if (done){ - //read a header - if (ReadUntil(p->data, 11, sofar, D, S, P)){ - //if its a correct FLV header, throw away and read tag header - if (FLV_Isheader(p->data)){ - if (ReadUntil(p->data, 13, sofar, D, S, P)){ - if (FLV_Checkheader(p->data)){ - sofar = 0; - memcpy(FLVHeader, p->data, 13); - }else{All_Hell_Broke_Loose = true;} - } - }else{ - //if a tag header, calculate length and read tag body - p->len = p->data[3] + 15; - p->len += (p->data[2] << 8); - p->len += (p->data[1] << 16); - //if (p->buf < p->len){p->data = (char*)realloc(p->data, p->len);p->buf = p->len;} - if (p->data[0] > 0x12){ - printf("Invalid data: %2hhx %2hhx %2hhx %2hhx %2hhx %2hhx %2hhx\n", p->data[0], p->data[1], p->data[2], p->data[3], p->data[4], p->data[5], p->data[6]); - } - done = false; - } - } - }else{ - //read tag body - if (ReadUntil(p->data, p->len, sofar, D, S, P)){ - //calculate keyframeness, next time read header again, return true - p->isKeyframe = false; - if ((p->data[0] == 0x09) && (((p->data[11] & 0xf0) >> 4) == 1)){p->isKeyframe = true;} - done = true; - sofar = 0; - return true; - } - } - return false; -}//FLV_GetPacket - diff --git a/util/flv_pack.cpp b/util/flv_pack.cpp deleted file mode 100644 index a60bf6a5..00000000 --- a/util/flv_pack.cpp +++ /dev/null @@ -1,123 +0,0 @@ -#pragma once - -class FLV_Pack { - public: - int len; - int buf; - bool isKeyframe; - char * data; - std::string tagType(){ - std::string R = ""; - switch (data[0]){ - case 0x09: - switch (data[11] & 0x0F){ - case 1: R += "JPEG"; break; - case 2: R += "H263"; break; - case 3: R += "ScreenVideo1"; break; - case 4: R += "VP6"; break; - case 5: R += "VP6Alpha"; break; - case 6: R += "ScreenVideo2"; break; - case 7: R += "AVC"; break; - default: R += "unknown"; break; - } - R += " video "; - switch (data[11] & 0xF0){ - case 0x10: R += "keyframe"; break; - case 0x20: R += "iframe"; break; - case 0x30: R += "disposableiframe"; break; - case 0x40: R += "generatedkeyframe"; break; - case 0x50: R += "videoinfo"; break; - } - if ((data[11] & 0x0F) == 7){ - switch (data[12]){ - case 0: R += " header"; break; - case 1: R += " NALU"; break; - case 2: R += " endofsequence"; break; - } - } - break; - case 0x08: - switch (data[11] & 0xF0){ - case 0x00: R += "linear PCM PE"; break; - case 0x10: R += "ADPCM"; break; - case 0x20: R += "MP3"; break; - case 0x30: R += "linear PCM LE"; break; - case 0x40: R += "Nelly16kHz"; break; - case 0x50: R += "Nelly8kHz"; break; - case 0x60: R += "Nelly"; break; - case 0x70: R += "G711A-law"; break; - case 0x80: R += "G711mu-law"; break; - case 0x90: R += "reserved"; break; - case 0xA0: R += "AAC"; break; - case 0xB0: R += "Speex"; break; - case 0xE0: R += "MP38kHz"; break; - case 0xF0: R += "DeviceSpecific"; break; - default: R += "unknown"; break; - } - switch (data[11] & 0x0C){ - case 0x0: R += " 5.5kHz"; break; - case 0x4: R += " 11kHz"; break; - case 0x8: R += " 22kHz"; break; - case 0xC: R += " 44kHz"; break; - } - switch (data[11] & 0x02){ - case 0: R += " 8bit"; break; - case 2: R += " 16bit"; break; - } - switch (data[11] & 0x01){ - case 0: R += " mono"; break; - case 1: R += " stereo"; break; - } - R += " audio"; - if ((data[12] == 0) && ((data[11] & 0xF0) == 0xA0)){ - R += " initdata"; - } - break; - case 0x12: - R += "(meta)data"; - break; - default: - R += "unknown"; - break; - } - return R; - };//tagtype - unsigned int tagTime(){ - return (data[4] << 16) + (data[5] << 8) + data[6] + (data[7] << 24); - }//tagTime getter - void tagTime(unsigned int T){ - data[4] = ((T >> 16) & 0xFF); - data[5] = ((T >> 8) & 0xFF); - data[6] = (T & 0xFF); - data[7] = ((T >> 24) & 0xFF); - }//tagTime setter - FLV_Pack(){ - len = 0; buf = 0; data = 0; isKeyframe = false; - }//empty constructor - FLV_Pack(const FLV_Pack& O){ - buf = O.len; - len = buf; - if (len > 0){ - data = (char*)malloc(len); - memcpy(data, O.data, len); - }else{ - data = 0; - } - isKeyframe = O.isKeyframe; - }//copy constructor - FLV_Pack & operator= (const FLV_Pack& O){ - if (this != &O){//no self-assignment - if (data != 0){free(data);} - buf = O.len; - len = buf; - if (len > 0){ - data = (char*)malloc(len); - memcpy(data, O.data, len); - }else{ - data = 0; - } - isKeyframe = O.isKeyframe; - } - return *this; - }//assignment operator -};//FLV_Pack diff --git a/util/flv_sock.cpp b/util/flv_sock.cpp deleted file mode 100644 index 8b74c9aa..00000000 --- a/util/flv_sock.cpp +++ /dev/null @@ -1,106 +0,0 @@ -#include "flv_pack.cpp" - -char FLVHeader[13]; -bool All_Hell_Broke_Loose = false; - -//checks FLV Header for correctness -//returns true if everything is alright, false otherwise -bool FLV_Checkheader(char * header){ - if (header[0] != 'F') return false; - if (header[1] != 'L') return false; - if (header[2] != 'V') return false; - if (header[8] != 0x09) return false; - if (header[9] != 0) return false; - if (header[10] != 0) return false; - if (header[11] != 0) return false; - if (header[12] != 0) return false; - return true; -}//FLV_Checkheader - -//returns true if header is an FLV header -bool FLV_Isheader(char * header){ - if (header[0] != 'F') return false; - if (header[1] != 'L') return false; - if (header[2] != 'V') return false; - return true; -}//FLV_Isheader - -bool ReadUntil(char * buffer, unsigned int count, unsigned int & sofar, int sock){ - if (sofar == count){return true;} - int r = DDV_iread(buffer + sofar,count-sofar,sock); - if (r < 0){ - if (errno != EWOULDBLOCK){ - All_Hell_Broke_Loose = true; - fprintf(stderr, "ReadUntil fail: %s. All Hell Broke Loose!\n", strerror(errno)); - } - return false; - } - sofar += r; - if (sofar == count){return true;} - if (sofar > count){ - All_Hell_Broke_Loose = true; - fprintf(stderr, "ReadUntil fail: %s. Read too much. All Hell Broke Loose!\n", strerror(errno)); - } - return false; -} - -//gets a packet, storing in given FLV_Pack pointer. -//will assign pointer if null -//resizes FLV_Pack data field bigger if data doesn't fit -// (does not auto-shrink for speed!) -bool FLV_GetPacket(FLV_Pack *& p, int sock){ - static bool done = true; - static unsigned int sofar = 0; - if (!p){p = (FLV_Pack*)calloc(1, sizeof(FLV_Pack));} - if (p->buf < 15){p->data = (char*)realloc(p->data, 15); p->buf = 15;} - - if (done){ - //read a header - if (ReadUntil(p->data, 11, sofar, sock)){ - //if its a correct FLV header, throw away and read tag header - if (FLV_Isheader(p->data)){ - if (ReadUntil(p->data, 13, sofar, sock)){ - if (FLV_Checkheader(p->data)){ - sofar = 0; - memcpy(FLVHeader, p->data, 13); - //fwrite(p->data, 13, 1, stdout);//output raw stream - }else{ - All_Hell_Broke_Loose = true; - fprintf(stderr, "Invalid FLV header. All Hell Broke Loose!\n"); - } - } - }else{ - //if a tag header, calculate length and read tag body - p->len = p->data[3] + 15; - p->len += (p->data[2] << 8); - p->len += (p->data[1] << 16); - //fprintf(stderr, "Tag of len %i\n", p->len); - if (p->buf < p->len){p->data = (char*)realloc(p->data, p->len);p->buf = p->len;} - done = false; - } - } - }else{ - //read tag body - if (ReadUntil(p->data, p->len, sofar, sock)){ - //calculate keyframeness, next time read header again, return true - p->isKeyframe = false; - if ((p->data[0] == 0x09) && (((p->data[11] & 0xf0) >> 4) == 1)){p->isKeyframe = true;} - int testlen = p->data[p->len-1] + 4; - testlen += (p->data[p->len-2] << 8); - testlen += (p->data[p->len-3] << 16); - testlen += (p->data[p->len-4] << 24); - //fwrite(p->data, p->len, 1, stdout);//output raw stream - if (p->len != testlen){ - fprintf(stderr, "Len: %i, testlen: %i\n", p->len, testlen); - All_Hell_Broke_Loose = true; - fprintf(stderr, "ReadUntil fail: Wrong size tag? All Hell Broke Loose!\n"); - return false; - } - done = true; - sofar = 0; - return true; - } - } - return false; -}//FLV_GetPacket - diff --git a/util/flv_tag.cpp b/util/flv_tag.cpp new file mode 100644 index 00000000..70321b17 --- /dev/null +++ b/util/flv_tag.cpp @@ -0,0 +1,391 @@ +#include "flv_tag.h" +#include //for Tag::FileLoader +#include //for Tag::FileLoader +#include //for Tag::FileLoader +#include //malloc +#include //memcpy +#include "ddv_socket.h" //socket functions + +char FLV::Header[13]; ///< Holds the last FLV header parsed. +bool FLV::Parse_Error = false; ///< This variable is set to true if a problem is encountered while parsing the FLV. + +/// Checks a FLV Header for validness. Returns true if the header is valid, false +/// if the header is not. Not valid can mean: +/// - Not starting with the string "FLV". +/// - The DataOffset is not 9 bytes. +/// - The PreviousTagSize is not 0 bytes. +/// +/// Note that we see PreviousTagSize as part of the FLV header, not part of the tag header! +bool FLV::check_header(char * header){ + if (header[0] != 'F') return false; + if (header[1] != 'L') return false; + if (header[2] != 'V') return false; + if (header[5] != 0) return false; + if (header[6] != 0) return false; + if (header[7] != 0) return false; + if (header[8] != 0x09) return false; + if (header[9] != 0) return false; + if (header[10] != 0) return false; + if (header[11] != 0) return false; + if (header[12] != 0) return false; + return true; +}//FLV::check_header + +/// Checks the first 3 bytes for the string "FLV". Implementing a basic FLV header check, +/// returning true if it is, false if not. +bool FLV::is_header(char * header){ + if (header[0] != 'F') return false; + if (header[1] != 'L') return false; + if (header[2] != 'V') return false; + return true; +}//FLV::is_header + + +/// Returns a std::string describing the tag in detail. +/// The string includes information about whether the tag is +/// audio, video or metadata, what encoding is used, and the details +/// of the encoding itself. +std::string FLV::Tag::tagType(){ + std::string R = ""; + switch (data[0]){ + case 0x09: + switch (data[11] & 0x0F){ + case 1: R += "JPEG"; break; + case 2: R += "H263"; break; + case 3: R += "ScreenVideo1"; break; + case 4: R += "VP6"; break; + case 5: R += "VP6Alpha"; break; + case 6: R += "ScreenVideo2"; break; + case 7: R += "AVC"; break; + default: R += "unknown"; break; + } + R += " video "; + switch (data[11] & 0xF0){ + case 0x10: R += "keyframe"; break; + case 0x20: R += "iframe"; break; + case 0x30: R += "disposableiframe"; break; + case 0x40: R += "generatedkeyframe"; break; + case 0x50: R += "videoinfo"; break; + } + if ((data[11] & 0x0F) == 7){ + switch (data[12]){ + case 0: R += " header"; break; + case 1: R += " NALU"; break; + case 2: R += " endofsequence"; break; + } + } + break; + case 0x08: + switch (data[11] & 0xF0){ + case 0x00: R += "linear PCM PE"; break; + case 0x10: R += "ADPCM"; break; + case 0x20: R += "MP3"; break; + case 0x30: R += "linear PCM LE"; break; + case 0x40: R += "Nelly16kHz"; break; + case 0x50: R += "Nelly8kHz"; break; + case 0x60: R += "Nelly"; break; + case 0x70: R += "G711A-law"; break; + case 0x80: R += "G711mu-law"; break; + case 0x90: R += "reserved"; break; + case 0xA0: R += "AAC"; break; + case 0xB0: R += "Speex"; break; + case 0xE0: R += "MP38kHz"; break; + case 0xF0: R += "DeviceSpecific"; break; + default: R += "unknown"; break; + } + switch (data[11] & 0x0C){ + case 0x0: R += " 5.5kHz"; break; + case 0x4: R += " 11kHz"; break; + case 0x8: R += " 22kHz"; break; + case 0xC: R += " 44kHz"; break; + } + switch (data[11] & 0x02){ + case 0: R += " 8bit"; break; + case 2: R += " 16bit"; break; + } + switch (data[11] & 0x01){ + case 0: R += " mono"; break; + case 1: R += " stereo"; break; + } + R += " audio"; + if ((data[12] == 0) && ((data[11] & 0xF0) == 0xA0)){ + R += " initdata"; + } + break; + case 0x12: + R += "(meta)data"; + break; + default: + R += "unknown"; + break; + } + return R; +}//FLV::Tag::tagtype + +/// Returns the 32-bit timestamp of this tag. +unsigned int FLV::Tag::tagTime(){ + return (data[4] << 16) + (data[5] << 8) + data[6] + (data[7] << 24); +}//tagTime getter + +/// Sets the 32-bit timestamp of this tag. +void FLV::Tag::tagTime(unsigned int T){ + data[4] = ((T >> 16) & 0xFF); + data[5] = ((T >> 8) & 0xFF); + data[6] = (T & 0xFF); + data[7] = ((T >> 24) & 0xFF); +}//tagTime setter + +/// Constructor for a new, empty, tag. +/// The buffer length is initialized to 0, and later automatically +/// increased if neccesary. +FLV::Tag::Tag(){ + len = 0; buf = 0; data = 0; isKeyframe = false; +}//empty constructor + +/// Copy constructor, copies the contents of an existing tag. +/// The buffer length is initialized to the actual size of the tag +/// that is being copied, and later automaticallt increased if +/// neccesary. +FLV::Tag::Tag(const Tag& O){ + buf = O.len; + len = buf; + if (len > 0){ + data = (char*)malloc(len); + memcpy(data, O.data, len); + }else{ + data = 0; + } + isKeyframe = O.isKeyframe; +}//copy constructor + +/// Assignment operator - works exactly like the copy constructor. +/// This operator checks for self-assignment. +FLV::Tag & FLV::Tag::operator= (const FLV::Tag& O){ + if (this != &O){//no self-assignment + if (data != 0){free(data);} + buf = O.len; + len = buf; + if (len > 0){ + data = (char*)malloc(len); + memcpy(data, O.data, len); + }else{ + data = 0; + } + isKeyframe = O.isKeyframe; + } + return *this; +}//assignment operator + +/// Helper function for FLV::MemLoader. +/// This function will try to read count bytes from data buffer D into buffer. +/// This function should be called repeatedly until true. +/// P and sofar are not the same value, because D may not start with the current tag. +/// \param buffer The target buffer. +/// \param count Amount of bytes to read. +/// \param sofar Current amount read. +/// \param D The location of the data buffer. +/// \param S The size of the data buffer. +/// \param P The current position in the data buffer. Will be updated to reflect new position. +/// \return True if count bytes are read succesfully, false otherwise. +bool FLV::Tag::MemReadUntil(char * buffer, unsigned int count, unsigned int & sofar, char * D, unsigned int S, unsigned int & P){ + if (sofar >= count){return true;} + int r = 0; + if (P+(count-sofar) > S){r = S-P;}else{r = count-sofar;} + memcpy(buffer+sofar, D+P, r); + P += r; + sofar += r; + if (sofar >= count){return true;} + return false; +}//Tag::MemReadUntil + + +/// Try to load a tag from a data buffer in memory. +/// This is a stateful function - if fed incorrect data, it will most likely never return true again! +/// While this function returns false, the Tag might not contain valid data. +/// \param D The location of the data buffer. +/// \param S The size of the data buffer. +/// \param P The current position in the data buffer. Will be updated to reflect new position. +/// \return True if a whole tag is succesfully read, false otherwise. +bool FLV::Tag::MemLoader(char * D, unsigned int S, unsigned int & P){ + static bool done = true; + static unsigned int sofar = 0; + if (buf < 15){data = (char*)realloc(data, 15); buf = 15;} + if (done){ + //read a header + if (MemReadUntil(data, 11, sofar, D, S, P)){ + //if its a correct FLV header, throw away and read tag header + if (FLV::is_header(data)){ + if (MemReadUntil(data, 13, sofar, D, S, P)){ + if (FLV::check_header(data)){ + sofar = 0; + memcpy(FLV::Header, data, 13); + }else{FLV::Parse_Error = true; return false;} + } + }else{ + //if a tag header, calculate length and read tag body + len = data[3] + 15; + len += (data[2] << 8); + len += (data[1] << 16); + if (buf < len){data = (char*)realloc(data, len); buf = len;} + if (data[0] > 0x12){FLV::Parse_Error = true; return false;} + done = false; + } + } + }else{ + //read tag body + if (MemReadUntil(data, len, sofar, D, S, P)){ + //calculate keyframeness, next time read header again, return true + if ((data[0] == 0x09) && (((data[11] & 0xf0) >> 4) == 1)){isKeyframe = true;}else{isKeyframe = false;} + done = true; + sofar = 0; + return true; + } + } + return false; +}//Tag::MemLoader + + +/// Helper function for FLV::SockLoader. +/// This function will try to read count bytes from socket sock into buffer. +/// This function should be called repeatedly until true. +/// \param buffer The target buffer. +/// \param count Amount of bytes to read. +/// \param sofar Current amount read. +/// \param sock Socket to read from. +/// \return True if count bytes are read succesfully, false otherwise. +bool FLV::Tag::SockReadUntil(char * buffer, unsigned int count, unsigned int & sofar, DDV::Socket & sock){ + if (sofar == count){return true;} + int r = sock.read(buffer + sofar,count-sofar); + if (r < 0){ + if (errno != EWOULDBLOCK){ + FLV::Parse_Error = true; + fprintf(stderr, "ReadUntil fail: %s. All Hell Broke Loose!\n", strerror(errno)); + } + return false; + } + sofar += r; + if (sofar == count){return true;} + if (sofar > count){ + FLV::Parse_Error = true; + fprintf(stderr, "ReadUntil fail: %s. Read too much. All Hell Broke Loose!\n", strerror(errno)); + } + return false; +}//Tag::SockReadUntil + +/// Try to load a tag from a socket. +/// This is a stateful function - if fed incorrect data, it will most likely never return true again! +/// While this function returns false, the Tag might not contain valid data. +/// \param sock The socket to read from. +/// \return True if a whole tag is succesfully read, false otherwise. +bool FLV::Tag::SockLoader(DDV::Socket sock){ + static bool done = true; + static unsigned int sofar = 0; + if (buf < 15){data = (char*)realloc(data, 15); buf = 15;} + if (done){ + if (SockReadUntil(data, 11, sofar, sock)){ + //if its a correct FLV header, throw away and read tag header + if (FLV::is_header(data)){ + if (SockReadUntil(data, 13, sofar, sock)){ + if (FLV::check_header(data)){ + sofar = 0; + memcpy(FLV::Header, data, 13); + }else{FLV::Parse_Error = true; return false;} + } + }else{ + //if a tag header, calculate length and read tag body + len = data[3] + 15; + len += (data[2] << 8); + len += (data[1] << 16); + if (buf < len){data = (char*)realloc(data, len); buf = len;} + if (data[0] > 0x12){FLV::Parse_Error = true; return false;} + done = false; + } + } + }else{ + //read tag body + if (SockReadUntil(data, len, sofar, sock)){ + //calculate keyframeness, next time read header again, return true + if ((data[0] == 0x09) && (((data[11] & 0xf0) >> 4) == 1)){isKeyframe = true;}else{isKeyframe = false;} + done = true; + sofar = 0; + return true; + } + } + return false; +}//Tag::SockLoader + +/// Try to load a tag from a socket. +/// This is a stateful function - if fed incorrect data, it will most likely never return true again! +/// While this function returns false, the Tag might not contain valid data. +/// \param sock The socket to read from. +/// \return True if a whole tag is succesfully read, false otherwise. +bool FLV::Tag::SockLoader(int sock){ + return SockLoader(DDV::Socket(sock)); +}//Tag::SockLoader + +/// Helper function for FLV::FileLoader. +/// This function will try to read count bytes from file f into buffer. +/// This function should be called repeatedly until true. +/// \param buffer The target buffer. +/// \param count Amount of bytes to read. +/// \param sofar Current amount read. +/// \param f File to read from. +/// \return True if count bytes are read succesfully, false otherwise. +bool FLV::Tag::FileReadUntil(char * buffer, unsigned int count, unsigned int & sofar, FILE * f){ + if (sofar >= count){return true;} + int r = 0; + r = fread(buffer + sofar,1,count-sofar,f); + if (r < 0){FLV::Parse_Error = true; return false;} + sofar += r; + if (sofar >= count){return true;} + return false; +} + +/// Try to load a tag from a file. +/// This is a stateful function - if fed incorrect data, it will most likely never return true again! +/// While this function returns false, the Tag might not contain valid data. +/// \param f The file to read from. +/// \return True if a whole tag is succesfully read, false otherwise. +bool FLV::Tag::FileLoader(FILE * f){ + int preflags = fcntl(fileno(f), F_GETFL, 0); + int postflags = preflags | O_NONBLOCK; + fcntl(fileno(f), F_SETFL, postflags); + static bool done = true; + static unsigned int sofar = 0; + if (buf < 15){data = (char*)realloc(data, 15); buf = 15;} + + if (done){ + //read a header + if (FileReadUntil(data, 11, sofar, f)){ + //if its a correct FLV header, throw away and read tag header + if (FLV::is_header(data)){ + if (FileReadUntil(data, 13, sofar, f)){ + if (FLV::check_header(data)){ + sofar = 0; + memcpy(FLV::Header, data, 13); + }else{FLV::Parse_Error = true;} + } + }else{ + //if a tag header, calculate length and read tag body + len = data[3] + 15; + len += (data[2] << 8); + len += (data[1] << 16); + if (buf < len){data = (char*)realloc(data, len); buf = len;} + if (data[0] > 0x12){FLV::Parse_Error = true;} + done = false; + } + } + }else{ + //read tag body + if (FileReadUntil(data, len, sofar, f)){ + //calculate keyframeness, next time read header again, return true + if ((data[0] == 0x09) && (((data[11] & 0xf0) >> 4) == 1)){isKeyframe = true;}else{isKeyframe = false;} + done = true; + sofar = 0; + fcntl(fileno(f), F_SETFL, preflags); + return true; + } + } + fcntl(fileno(f), F_SETFL, preflags); + return false; +}//FLV_GetPacket diff --git a/util/flv_tag.h b/util/flv_tag.h new file mode 100644 index 00000000..d91cabb5 --- /dev/null +++ b/util/flv_tag.h @@ -0,0 +1,40 @@ +#pragma once +#include "ddv_socket.h" +#include + +/// 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. + + //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. + std::string tagType(); ///< Returns a std::string describing the tag in detail. + unsigned int tagTime(); ///< Returns the 32-bit timestamp of this tag. + void tagTime(unsigned int T); ///< Sets the 32-bit timestamp of this tag. + Tag(); ///< Constructor for a new, empty, tag. + Tag(const Tag& O); ///< Copy constructor, copies the contents of an existing tag. + Tag & operator= (const Tag& O); ///< Assignment operator - works exactly like the copy constructor. + //loader functions + bool MemLoader(char * D, unsigned int S, unsigned int & P); + bool SockLoader(int sock); + bool SockLoader(DDV::Socket sock); + bool FileLoader(FILE * f); + protected: + int buf; ///< Maximum length of buffer space. + //loader helper functions + bool MemReadUntil(char * buffer, unsigned int count, unsigned int & sofar, char * D, unsigned int S, unsigned int & P); + bool SockReadUntil(char * buffer, unsigned int count, unsigned int & sofar, DDV::Socket & sock); + bool FileReadUntil(char * buffer, unsigned int count, unsigned int & sofar, FILE * f); + };//Tag + +};//FLV namespace diff --git a/util/http_parser.cpp b/util/http_parser.cpp index 36557fbd..dd01e9b2 100644 --- a/util/http_parser.cpp +++ b/util/http_parser.cpp @@ -1,42 +1,5 @@ -#pragma once -#include "ddv_socket.cpp" -#include -#include -#include - -class HTTPReader{ - public: - HTTPReader(); - bool ReadSocket(int CONN_fd); - bool ReadSocket(FILE * F); - std::string GetHeader(std::string i); - std::string GetVar(std::string i); - void SetHeader(std::string i, std::string v); - void SetHeader(std::string i, int v); - void SetVar(std::string i, std::string v); - void SetBody(std::string s); - void SetBody(char * buffer, int len); - std::string BuildRequest(); - std::string BuildResponse(std::string code, std::string message); - void SendResponse(int conn, std::string code, std::string message); - void SendBodyPart(int conn, char * buffer, int len); - void SendBodyPart(int conn, std::string bodypart); - void Clean(); - bool CleanForNext(); - std::string body; - std::string method; - std::string url; - std::string protocol; - unsigned int length; - private: - bool seenHeaders; - bool seenReq; - bool parse(); - std::string HTTPbuffer; - std::map headers; - std::map vars; - void Trim(std::string & s); -};//HTTPReader +#include "http_parser.h" +#include "ddv_socket.h" HTTPReader::HTTPReader(){Clean();} void HTTPReader::Clean(){ @@ -127,29 +90,28 @@ void HTTPReader::SetVar(std::string i, std::string v){ vars[i] = v; } -bool HTTPReader::ReadSocket(int CONN_fd){ +bool HTTPReader::Read(DDV::Socket & sock){ //returned true als hele http packet gelezen is int r = 0; int b = 0; char buffer[500]; while (true){ - r = DDV_ready(CONN_fd); + r = sock.ready(); if (r < 1){ - if (r == 0){ - socketError = true; + if (r == -1){ #if DEBUG >= 1 fprintf(stderr, "User socket is disconnected.\n"); #endif } return parse(); } - b = DDV_iread(buffer, 500, CONN_fd); + b = sock.iread(buffer, 500); HTTPbuffer.append(buffer, b); } return false; }//HTTPReader::ReadSocket -bool HTTPReader::ReadSocket(FILE * F){ +bool HTTPReader::Read(FILE * F){ //returned true als hele http packet gelezen is int b = 1; char buffer[500]; @@ -210,23 +172,23 @@ bool HTTPReader::parse(){ return false; //we should never get here... }//HTTPReader::parse -void HTTPReader::SendResponse(int conn, std::string code, std::string message){ +void HTTPReader::SendResponse(DDV::Socket & conn, std::string code, std::string message){ std::string tmp = BuildResponse(code, message); - DDV_write(tmp.c_str(), tmp.size(), conn); + conn.write(tmp); } -void HTTPReader::SendBodyPart(int conn, char * buffer, int len){ +void HTTPReader::SendBodyPart(DDV::Socket & conn, char * buffer, int len){ std::string tmp; tmp.append(buffer, len); SendBodyPart(conn, tmp); } -void HTTPReader::SendBodyPart(int conn, std::string bodypart){ +void HTTPReader::SendBodyPart(DDV::Socket & conn, std::string bodypart){ static char len[10]; int sizelen; sizelen = snprintf(len, 10, "%x\r\n", (unsigned int)bodypart.size()); - DDV_write(len, sizelen, conn); - DDV_write(bodypart.c_str(), bodypart.size(), conn); - DDV_write(len+sizelen-2, 2, conn); + conn.write(len, sizelen); + conn.write(bodypart); + conn.write(len+sizelen-2, 2); } diff --git a/util/http_parser.h b/util/http_parser.h new file mode 100644 index 00000000..2741f0c4 --- /dev/null +++ b/util/http_parser.h @@ -0,0 +1,41 @@ +#pragma once +#include +#include +#include +#include +#include "ddv_socket.h" + +class HTTPReader{ + public: + HTTPReader(); + bool Read(DDV::Socket & sock); + bool Read(FILE * F); + std::string GetHeader(std::string i); + std::string GetVar(std::string i); + void SetHeader(std::string i, std::string v); + void SetHeader(std::string i, int v); + void SetVar(std::string i, std::string v); + void SetBody(std::string s); + void SetBody(char * buffer, int len); + std::string BuildRequest(); + std::string BuildResponse(std::string code, std::string message); + void SendResponse(DDV::Socket & conn, std::string code, std::string message); + void SendBodyPart(DDV::Socket & conn, char * buffer, int len); + void SendBodyPart(DDV::Socket & conn, std::string bodypart); + void Clean(); + bool CleanForNext(); + std::string body; + std::string method; + std::string url; + std::string protocol; + unsigned int length; + private: + bool seenHeaders; + bool seenReq; + bool parse(); + std::string HTTPbuffer; + std::map headers; + std::map vars; + void Trim(std::string & s); +};//HTTPReader + diff --git a/util/server_setup.cpp b/util/server_setup.cpp index fbd93aa0..bd9e9390 100644 --- a/util/server_setup.cpp +++ b/util/server_setup.cpp @@ -1,23 +1,21 @@ -int mainHandler(int CONN_fd);//define this function in your own code! #include -#include "ddv_socket.cpp" //DDVTech Socket wrapper -#include "flv_sock.cpp" //FLV parsing with DDVTech Socket wrapper -int server_socket = 0; +#include "ddv_socket.h" //DDVTech Socket wrapper +#include "flv_tag.h" //FLV parsing with DDVTech Socket wrapper +DDV::ServerSocket server_socket(-1); void termination_handler (int signum){ - if (server_socket == 0) return; + if (!server_socket.connected()) return; switch (signum){ case SIGINT: break; case SIGHUP: break; case SIGTERM: break; default: return; break; } - close(server_socket); - server_socket = 0; + server_socket.close(); } int main(int argc, char ** argv){ - int CONN_fd = 0; + DDV::Socket CONN_fd(-1); //setup signal handler struct sigaction new_action; @@ -31,12 +29,14 @@ int main(int argc, char ** argv){ int listen_port = DEFAULT_PORT; bool daemon_mode = true; + std::string interface = "0.0.0.0"; int opt = 0; - static const char *optString = "np:h?"; + static const char *optString = "np:i:h?"; static const struct option longOpts[] = { {"help",0,0,'h'}, {"port",1,0,'p'}, + {"interface",1,0,'i'}, {"no-daemon",0,0,'n'} }; while ((opt = getopt_long(argc, argv, optString, longOpts, 0)) != -1){ @@ -44,6 +44,9 @@ int main(int argc, char ** argv){ case 'p': listen_port = atoi(optarg); break; + case 'i': + interface = optarg; + break; case 'n': daemon_mode = false; break; @@ -55,11 +58,11 @@ int main(int argc, char ** argv){ } } - server_socket = DDV_Listen(listen_port); + server_socket = DDV::ServerSocket(listen_port, interface); #if DEBUG >= 3 - fprintf(stderr, "Made a listening socket on port %i...\n", listen_port); + fprintf(stderr, "Made a listening socket on %s:%i...\n", interface.c_str(), listen_port); #endif - if (server_socket > 0){ + if (server_socket.connected()){ if (daemon_mode){ daemon(1, 0); #if DEBUG >= 3 @@ -73,22 +76,22 @@ int main(int argc, char ** argv){ return 1; } int status; - while (server_socket > 0){ + while (server_socket.connected()){ waitpid((pid_t)-1, &status, WNOHANG); - CONN_fd = DDV_Accept(server_socket); - if (CONN_fd > 0){ + CONN_fd = server_socket.accept(); + if (CONN_fd.connected()){ pid_t myid = fork(); if (myid == 0){ break; }else{ #if DEBUG >= 3 - fprintf(stderr, "Spawned new process %i for handling socket %i\n", (int)myid, CONN_fd); + fprintf(stderr, "Spawned new process %i for socket %i\n", (int)myid, CONN_fd.getSocket()); #endif } } } - if (server_socket <= 0){ + if (!server_socket.connected()){ return 0; } - return mainHandler(CONN_fd); + return MAINHANDLER(CONN_fd); } From 1599894b6eddf54b44b3b39a39e8102aabfef75b Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sat, 9 Apr 2011 03:24:29 +0200 Subject: [PATCH 114/788] Renamed all binaries with DDV_ prefix, updated Buffer for new systems, updated RAW connector, partly updated RTMP connector - ready for recode. All connectors now have host binding options. --- util/ddv_socket.cpp | 8 ++++++++ util/ddv_socket.h | 1 + 2 files changed, 9 insertions(+) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index 2835a5cf..6c5027b7 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -8,6 +8,14 @@ DDV::Socket::Socket(int sockNo){ Blocking = false; }//DDV::Socket basic constructor +/// Create a new disconnected base socket. This is a basic constructor for placeholder purposes. +/// A socket created like this is always disconnected and should/could be overwritten at some point. +DDV::Socket::Socket(){ + sock = -1; + Error = false; + Blocking = false; +}//DDV::Socket basic constructor + /// Close connection. The internal socket is closed and then set to -1. void DDV::Socket::close(){ #if DEBUG >= 3 diff --git a/util/ddv_socket.h b/util/ddv_socket.h index cc9421ae..298b5954 100644 --- a/util/ddv_socket.h +++ b/util/ddv_socket.h @@ -19,6 +19,7 @@ namespace DDV{ private: int sock; ///< Internally saved socket number. public: + Socket(); ///< Create a new disconnected base socket. Socket(int sockNo); ///< Create a new base socket. Socket(std::string adres, bool nonblock = false); ///< Create a new Unix Socket. bool Error; ///< Set to true if a socket error happened. From 69b1f859721e4002394bc7945dd3f189e36c78db Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 10 Apr 2011 20:02:04 +0200 Subject: [PATCH 115/788] RAW connector cleanup, added configfile support, added setuid support, added some more comments other places --- util/ddv_socket.cpp | 2 + util/server_setup.cpp | 142 ++++++++++++++++++++++++++++++++---------- 2 files changed, 110 insertions(+), 34 deletions(-) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index 6c5027b7..76c2ee8a 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -309,6 +309,8 @@ DDV::ServerSocket::ServerSocket(std::string address, bool nonblock){ DDV::Socket DDV::ServerSocket::accept(bool nonblock){ if (sock < 0){return DDV::Socket(-1);} int r = ::accept(sock, 0, 0); + //set the socket to be nonblocking, if requested. + //we could do this through accept4 with a flag, but that call is non-standard... if ((r >= 0) && nonblock){ int flags = fcntl(r, F_GETFL, 0); flags |= O_NONBLOCK; diff --git a/util/server_setup.cpp b/util/server_setup.cpp index bd9e9390..daa9e6c6 100644 --- a/util/server_setup.cpp +++ b/util/server_setup.cpp @@ -1,9 +1,18 @@ #include #include "ddv_socket.h" //DDVTech Socket wrapper #include "flv_tag.h" //FLV parsing with DDVTech Socket wrapper +#include +#include +#include +#define defstr(x) #x //converts a define name to string +#define defstrh(x) "[" defstr(x) "]" //converts define name to [string] DDV::ServerSocket server_socket(-1); -void termination_handler (int signum){ +/// Basic signal handler. Disconnects the server_socket if it receives +/// a SIGINT, SIGHUP or SIGTERM signal, but does nothing for SIGPIPE. +/// Disconnecting the server_socket will terminate the main listening loop +/// and cleanly shut down the process. +void signal_handler (int signum){ if (!server_socket.connected()) return; switch (signum){ case SIGINT: break; @@ -12,57 +21,104 @@ void termination_handler (int signum){ default: return; break; } server_socket.close(); -} +}//signal_handler +/// Generic main entry point and loop for DDV Connectors. +/// This sets up the proper termination handler, checks commandline options, +/// parses config files and opens a listening socket on the requested port. +/// Any incoming connections will be accepted and start up the function MAINHANDLER, +/// which should be #defined before including server_setup.cpp. +/// The default port is set by #define DEFAULT_PORT. +/// The configuration file section is set by #define CONFIGSECT. int main(int argc, char ** argv){ - DDV::Socket CONN_fd(-1); - + DDV::Socket S;//placeholder for incoming connections + //setup signal handler struct sigaction new_action; - new_action.sa_handler = termination_handler; + 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); - + + //default values int listen_port = DEFAULT_PORT; bool daemon_mode = true; std::string interface = "0.0.0.0"; + std::string configfile = "/etc/ddvtech.conf"; + std::string username = "root"; + bool ignore_daemon = false; + bool ignore_interface = false; + bool ignore_port = false; + bool ignore_user = false; int opt = 0; - static const char *optString = "np:i:h?"; + static const char *optString = "ndp:i:u:c:h?"; static const struct option longOpts[] = { {"help",0,0,'h'}, {"port",1,0,'p'}, {"interface",1,0,'i'}, - {"no-daemon",0,0,'n'} + {"username",1,0,'u'}, + {"no-daemon",0,0,'n'}, + {"daemon",0,0,'d'}, + {"configfile",1,0,'c'} }; while ((opt = getopt_long(argc, argv, optString, longOpts, 0)) != -1){ switch (opt){ - case 'p': - listen_port = atoi(optarg); - break; - case 'i': - interface = optarg; - break; - case 'n': - daemon_mode = false; - break; + case 'p': listen_port = atoi(optarg); ignore_port = true; break; + case 'i': interface = optarg; ignore_interface = true; break; + case 'n': daemon_mode = false; ignore_daemon = true; break; + case 'd': daemon_mode = true; ignore_daemon = true; break; + case 'c': configfile = optarg; break; + case 'u': username = optarg; ignore_user = true; break; case 'h': case '?': - printf("Options: -h[elp], -?, -n[o-daemon], -p[ort] #\n"); + printf("Options: -h[elp], -?, -n[odaemon], -d[aemon], -p[ort] VAL, -i[nterface] VAL, -c[onfigfile] VAL, -u[sername] VAL\n"); + printf("Defaults:\n interface: 0.0.0.0\n port: %i\n daemon mode: true\n configfile: /etc/ddvtech.conf\n username: root\n", listen_port); + printf("Username root means no change to UID, no matter what the UID is.\n"); + printf("If the configfile exists, it is always loaded first. Commandline settings then overwrite the config file.\n"); + printf("\nThis process takes it directives from the %s section of the configfile.\n", defstrh(CONFIGSECT)); return 1; break; } - } - + }//commandline options parser + + std::ifstream conf(configfile.c_str(), std::ifstream::in); + std::string tmpstr; + bool acc_comm = false; + size_t foundeq; + if (conf.fail()){ + #if DEBUG >= 3 + fprintf(stderr, "Configuration file %s not found - using build-in defaults...\n", configfile.c_str()); + #endif + }else{ + while (conf.good()){ + getline(conf, tmpstr); + if (tmpstr[0] == '['){//new section? check if we care. + if (tmpstr == defstrh(CONFIGSECT)){acc_comm = true;}else{acc_comm = false;} + }else{ + if (!acc_comm){break;}//skip all lines in this section if we do not care about it + foundeq = tmpstr.find('='); + if (foundeq != std::string::npos){ + if ((tmpstr.substr(0, foundeq) == "port") && !ignore_port){listen_port = atoi(tmpstr.substr(foundeq+1).c_str());} + if ((tmpstr.substr(0, foundeq) == "interface") && !ignore_interface){interface = tmpstr.substr(foundeq+1);} + if ((tmpstr.substr(0, foundeq) == "username") && !ignore_user){username = tmpstr.substr(foundeq+1);} + if ((tmpstr.substr(0, foundeq) == "daemon") && !ignore_daemon){daemon_mode = true;} + if ((tmpstr.substr(0, foundeq) == "nodaemon") && !ignore_daemon){daemon_mode = false;} + }//found equals sign + }//section contents + }//configfile line loop + }//configuration + + //setup a new server socket, for the correct interface and port server_socket = DDV::ServerSocket(listen_port, interface); #if DEBUG >= 3 fprintf(stderr, "Made a listening socket on %s:%i...\n", interface.c_str(), listen_port); #endif if (server_socket.connected()){ + //if setup success, enter daemon mode if requested if (daemon_mode){ daemon(1, 0); #if DEBUG >= 3 @@ -75,23 +131,41 @@ int main(int argc, char ** argv){ #endif return 1; } - int status; - while (server_socket.connected()){ - waitpid((pid_t)-1, &status, WNOHANG); - CONN_fd = server_socket.accept(); - if (CONN_fd.connected()){ - pid_t myid = fork(); - if (myid == 0){ - break; + + if (username != "root"){ + struct passwd * user_info = getpwnam(username.c_str()); + if (!user_info){ + #if DEBUG >= 1 + fprintf(stderr, "Error: could not setuid %s: could not get PID\n", username.c_str()); + #endif + return 1; + }else{ + if (setuid(user_info->pw_uid) != 0){ + #if DEBUG >= 1 + fprintf(stderr, "Error: could not setuid %s: not allowed\n", username.c_str()); + #endif }else{ #if DEBUG >= 3 - fprintf(stderr, "Spawned new process %i for socket %i\n", (int)myid, CONN_fd.getSocket()); + fprintf(stderr, "Changed user to %s\n", username.c_str()); #endif } } } - if (!server_socket.connected()){ - return 0; - } - return MAINHANDLER(CONN_fd); -} + + int status; + while (server_socket.connected()){ + while (waitpid((pid_t)-1, &status, WNOHANG) > 0){}//clean up all child processes + S = server_socket.accept(); + if (S.connected()){//check if the new connection is valid + pid_t myid = fork(); + if (myid == 0){//if new child, start MAINHANDLER + return MAINHANDLER(S); + }else{//otherwise, do nothing or output debugging text + #if DEBUG >= 3 + fprintf(stderr, "Spawned new process %i for socket %i\n", (int)myid, S.getSocket()); + #endif + } + } + }//while connected + return 0; +}//main From e416612a3337ef5b2e74d6764ba03c34815b8e9f Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 10 Apr 2011 22:25:51 +0200 Subject: [PATCH 116/788] Documented and nicified AMF parsing. Updated all existing code for this change, except for RTMP connector (which gets a rewrite, like, today). --- util/amf.cpp | 580 +++++++++++++++++++++++++-------------------------- util/amf.h | 68 ++++++ 2 files changed, 358 insertions(+), 290 deletions(-) create mode 100644 util/amf.h diff --git a/util/amf.cpp b/util/amf.cpp index a600d12a..fdff660f 100644 --- a/util/amf.cpp +++ b/util/amf.cpp @@ -1,264 +1,256 @@ -#include -#include -#include +#include "amf.h" -#define AMF0_NUMBER 0x00 -#define AMF0_BOOL 0x01 -#define AMF0_STRING 0x02 -#define AMF0_OBJECT 0x03 -#define AMF0_MOVIECLIP 0x04 -#define AMF0_NULL 0x05 -#define AMF0_UNDEFINED 0x06 -#define AMF0_REFERENCE 0x07 -#define AMF0_ECMA_ARRAY 0x08 -#define AMF0_OBJ_END 0x09 -#define AMF0_STRICT_ARRAY 0x0A -#define AMF0_DATE 0x0B -#define AMF0_LONGSTRING 0x0C -#define AMF0_UNSUPPORTED 0x0D -#define AMF0_RECORDSET 0x0E -#define AMF0_XMLDOC 0x0F -#define AMF0_TYPED_OBJ 0x10 -#define AMF0_UPGRADE 0x11 -#define AMF0_DDV_CONTAINER 0xFF +/// Returns the std::string Indice for the current object, if available. +/// Returns an empty string if no indice exists. +std::string AMF::Object::Indice(){return myIndice;}; -class AMFType { - public: - std::string Indice(){return myIndice;}; - unsigned char GetType(){return myType;}; - double NumValue(){return numval;}; - std::string StrValue(){return strval;}; - const char * Str(){return strval.c_str();}; - int hasContent(){ - if (!contents){return 0;} - return contents->size(); - }; - void addContent(AMFType c){if (contents != 0){contents->push_back(c);}}; - AMFType* getContentP(int i){if (contents != 0){return &contents->at(i);}else{return 0;}}; - AMFType getContent(int i){if (contents != 0){return contents->at(i);}else{return AMFType("error");}}; - AMFType* getContentP(std::string s){ - if (contents != 0){ - for (std::vector::iterator it = contents->begin(); it != contents->end(); it++){ - if (it->Indice() == s){ - return &(*it); - } +/// Returns the AMF::obj0type AMF0 object type for this object. +AMF::obj0type AMF::Object::GetType(){return myType;}; + +/// Returns the numeric value of this object, if available. +/// If this object holds no numeric value, 0 is returned. +double AMF::Object::NumValue(){return numval;}; + +/// Returns the std::string value of this object, if available. +/// If this object holds no string value, an empty string is returned. +std::string AMF::Object::StrValue(){return strval;}; + +/// Returns the C-string value of this object, if available. +/// If this object holds no string value, an empty C-string is returned. +const char * AMF::Object::Str(){return strval.c_str();}; + +/// Returns a count of the amount of objects this object currently holds. +/// If this object is not a container type, this function will always return 0. +int AMF::Object::hasContent(){return contents.size();}; + +/// Adds an AMF::Object to this object. Works for all types, but only makes sense for container types. +void AMF::Object::addContent(AMF::Object c){contents.push_back(c);}; + +/// Returns a pointer to the object held at indice i. +/// Returns AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice. +/// \param i The indice of the object in this container. +AMF::Object* AMF::Object::getContentP(int i){return &contents.at(i);}; + +/// Returns a copy of the object held at indice i. +/// Returns a AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice. +/// \param i The indice of the object in this container. +AMF::Object AMF::Object::getContent(int i){return contents.at(i);}; + +/// Returns a pointer to the object held at indice s. +/// Returns NULL if no object is held at this indice. +/// \param s The indice of the object in this container. +AMF::Object* AMF::Object::getContentP(std::string s){ + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ + if (it->Indice() == s){return &(*it);} + } + return this; +}; + +/// Returns a copy of the object held at indice s. +/// Returns a AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice. +/// \param s The indice of the object in this container. +AMF::Object AMF::Object::getContent(std::string s){ + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ + if (it->Indice() == s){return *it;} + } + return AMF::Object("error", AMF0_DDV_CONTAINER); +}; + +/// Constructor for numeric objects. +/// The object type is by default AMF::AMF0_NUMBER, but this can be forced to a different value. +/// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. +/// \param val The numeric value of this object. Numeric AMF0 objects only support double-type values. +/// \param setType The object type to force this object to. +AMF::Object::Object(std::string indice, double val, AMF::obj0type setType){//num type initializer + myIndice = indice; + myType = setType; + strval = ""; + numval = val; +}; + +/// Constructor for string objects. +/// The object type is by default AMF::AMF0_STRING, but this can be forced to a different value. +/// There is no need to manually change the type to AMF::AMF0_LONGSTRING, this will be done automatically. +/// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. +/// \param val The string value of this object. +/// \param setType The object type to force this object to. +AMF::Object::Object(std::string indice, std::string val, AMF::obj0type setType){//str type initializer + myIndice = indice; + myType = setType; + strval = val; + numval = 0; +}; + +/// Constructor for container objects. +/// The object type is by default AMF::AMF0_OBJECT, but this can be forced to a different value. +/// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. +/// \param setType The object type to force this object to. +AMF::Object::Object(std::string indice, AMF::obj0type setType){//object type initializer + myIndice = indice; + myType = setType; + strval = ""; + numval = 0; +}; + +/// Prints the contents of this object to std::cerr. +/// If this object contains other objects, it will call itself recursively +/// and print all nested content in a nice human-readable format. +void AMF::Object::Print(std::string indent){ + std::cerr << indent; + // print my type + switch (myType){ + case AMF::AMF0_NUMBER: std::cerr << "Number"; break; + case AMF::AMF0_BOOL: std::cerr << "Bool"; break; + case AMF::AMF0_STRING://short string + case AMF::AMF0_LONGSTRING: std::cerr << "String"; break; + case AMF::AMF0_OBJECT: std::cerr << "Object"; break; + case AMF::AMF0_MOVIECLIP: std::cerr << "MovieClip"; break; + case AMF::AMF0_NULL: std::cerr << "Null"; break; + case AMF::AMF0_UNDEFINED: std::cerr << "Undefined"; break; + case AMF::AMF0_REFERENCE: std::cerr << "Reference"; break; + case AMF::AMF0_ECMA_ARRAY: std::cerr << "ECMA Array"; break; + case AMF::AMF0_OBJ_END: std::cerr << "Object end"; break; + case AMF::AMF0_STRICT_ARRAY: std::cerr << "Strict Array"; break; + case AMF::AMF0_DATE: std::cerr << "Date"; break; + case AMF::AMF0_UNSUPPORTED: std::cerr << "Unsupported"; break; + case AMF::AMF0_RECORDSET: std::cerr << "Recordset"; break; + case AMF::AMF0_XMLDOC: std::cerr << "XML Document"; break; + case AMF::AMF0_TYPED_OBJ: std::cerr << "Typed Object"; break; + case AMF::AMF0_UPGRADE: std::cerr << "Upgrade to AMF3"; break; + case AMF::AMF0_DDV_CONTAINER: std::cerr << "DDVTech Container"; break; + } + // print my string indice, if available + std::cerr << " " << myIndice << " "; + // print my numeric or string contents + switch (myType){ + case AMF::AMF0_NUMBER: case AMF::AMF0_BOOL: case AMF::AMF0_REFERENCE: case AMF::AMF0_DATE: std::cerr << numval; break; + case AMF::AMF0_STRING: case AMF::AMF0_LONGSTRING: case AMF::AMF0_XMLDOC: case AMF::AMF0_TYPED_OBJ: std::cerr << strval; break; + default: break;//we don't care about the rest, and don't want a compiler warning... + } + std::cerr << std::endl; + // if I hold other objects, print those too, recursively. + if (contents.size() > 0){ + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){it->Print(indent+" ");} + } +};//print + +/// Packs the AMF object to a std::string for transfer over the network. +/// If the object is a container type, this function will call itself recursively and contain all contents. +/// Tip: When sending multiple AMF objects in one go, put them in a single AMF::AMF0_DDV_CONTAINER for easy transfer. +std::string AMF::Object::Pack(){ + std::string r = ""; + //check for string/longstring conversion + if ((myType == AMF::AMF0_STRING) && (strval.size() > 0xFFFF)){myType = AMF::AMF0_LONGSTRING;} + //skip output of DDV container types, they do not exist. Only output their contents. + if (myType != AMF::AMF0_DDV_CONTAINER){r += myType;} + //output the properly formatted AMF0 data stream for this object's contents. + switch (myType){ + case AMF::AMF0_NUMBER: + r += *(((char*)&numval)+7); r += *(((char*)&numval)+6); + r += *(((char*)&numval)+5); r += *(((char*)&numval)+4); + r += *(((char*)&numval)+3); r += *(((char*)&numval)+2); + r += *(((char*)&numval)+1); r += *(((char*)&numval)); + break; + case AMF::AMF0_DATE: + r += *(((char*)&numval)+7); r += *(((char*)&numval)+6); + r += *(((char*)&numval)+5); r += *(((char*)&numval)+4); + r += *(((char*)&numval)+3); r += *(((char*)&numval)+2); + r += *(((char*)&numval)+1); r += *(((char*)&numval)); + r += (char)0;//timezone always 0 + r += (char)0;//timezone always 0 + break; + case AMF::AMF0_BOOL: + r += (char)numval; + break; + case AMF::AMF0_STRING: + r += strval.size() / 256; + r += strval.size() % 256; + r += strval; + break; + case AMF::AMF0_LONGSTRING: + case AMF::AMF0_XMLDOC://is always a longstring + r += strval.size() / (256*256*256); + r += strval.size() / (256*256); + r += strval.size() / 256; + r += strval.size() % 256; + r += strval; + break; + case AMF::AMF0_TYPED_OBJ: + r += Indice().size() / 256; + r += Indice().size() % 256; + r += Indice(); + //is an object, with the classname first + case AMF::AMF0_OBJECT: + if (contents.size() > 0){ + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ + r += it->Indice().size() / 256; + r += it->Indice().size() % 256; + r += it->Indice(); + r += it->Pack(); } } - return this; - }; - AMFType getContent(std::string s){ - if (contents != 0){ - for (std::vector::iterator it = contents->begin(); it != contents->end(); it++){ - if (it->Indice() == s){ - return *it; - } - } - } - return AMFType("error"); - }; - AMFType(std::string indice, double val, unsigned char setType = AMF0_NUMBER){//num type initializer - myIndice = indice; - myType = setType; - strval = ""; - numval = val; - contents = 0; - }; - AMFType(std::string indice, std::string val, unsigned char setType = AMF0_STRING){//str type initializer - myIndice = indice; - myType = setType; - strval = val; - numval = 0; - contents = 0; - }; - AMFType(std::string indice, unsigned char setType = AMF0_OBJECT){//object type initializer - myIndice = indice; - myType = setType; - strval = ""; - numval = 0; - contents = new std::vector; - }; - ~AMFType(){if (contents != 0){delete contents;contents=0;}}; - AMFType& operator=(const AMFType &a) { - myIndice = a.myIndice; - myType = a.myType; - strval = a.strval; - numval = a.numval; - if (contents){ - if (a.contents != contents){ - delete contents; - if (a.contents){ - contents = new std::vector; - for (std::vector::iterator it = a.contents->begin(); it < a.contents->end(); it++){ - contents->push_back(*it); - } - }else{ - contents = 0; - } + r += (char)0; r += (char)0; r += (char)9; + break; + case AMF::AMF0_MOVIECLIP: + case AMF::AMF0_OBJ_END: + case AMF::AMF0_UPGRADE: + case AMF::AMF0_NULL: + case AMF::AMF0_UNDEFINED: + case AMF::AMF0_RECORDSET: + case AMF::AMF0_UNSUPPORTED: + //no data to add + break; + case AMF::AMF0_REFERENCE: + r += (char)((int)numval / 256); + r += (char)((int)numval % 256); + break; + case AMF::AMF0_ECMA_ARRAY:{ + int arrlen = 0; + if (contents.size() > 0){ + arrlen = contents.size(); + r += arrlen / (256*256*256); r += arrlen / (256*256); r += arrlen / 256; r += arrlen % 256; + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ + r += it->Indice().size() / 256; + r += it->Indice().size() % 256; + r += it->Indice(); + r += it->Pack(); } }else{ - if (a.contents){ - contents = new std::vector; - for (std::vector::iterator it = a.contents->begin(); it < a.contents->end(); it++){ - contents->push_back(*it); - } + r += (char)0; r += (char)0; r += (char)0; r += (char)0; + } + r += (char)0; r += (char)0; r += (char)9; + } break; + case AMF::AMF0_STRICT_ARRAY:{ + int arrlen = 0; + if (contents.size() > 0){ + arrlen = contents.size(); + r += arrlen / (256*256*256); r += arrlen / (256*256); r += arrlen / 256; r += arrlen % 256; + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ + r += it->Pack(); + } + }else{ + r += (char)0; r += (char)0; r += (char)0; r += (char)0; + } + } break; + case AMF::AMF0_DDV_CONTAINER://only send contents + if (contents.size() > 0){ + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ + r += it->Pack(); } } - return *this; - };//= operator - AMFType(const AMFType &a){ - myIndice = a.myIndice; - myType = a.myType; - strval = a.strval; - numval = a.numval; - if (a.contents){ - contents = new std::vector; - for (std::vector::iterator it = a.contents->begin(); it < a.contents->end(); it++){ - contents->push_back(*it); - } - }else{contents = 0;} - };//copy constructor - void Print(std::string indent = ""){ - std::cerr << indent; - switch (myType){ - case AMF0_NUMBER: std::cerr << "Number"; break; - case AMF0_BOOL: std::cerr << "Bool"; break; - case AMF0_STRING://short string - case AMF0_LONGSTRING: std::cerr << "String"; break; - case AMF0_OBJECT: std::cerr << "Object"; break; - case AMF0_MOVIECLIP: std::cerr << "MovieClip"; break; - case AMF0_NULL: std::cerr << "Null"; break; - case AMF0_UNDEFINED: std::cerr << "Undefined"; break; - case AMF0_REFERENCE: std::cerr << "Reference"; break; - case AMF0_ECMA_ARRAY: std::cerr << "ECMA Array"; break; - case AMF0_OBJ_END: std::cerr << "Object end"; break; - case AMF0_STRICT_ARRAY: std::cerr << "Strict Array"; break; - case AMF0_DATE: std::cerr << "Date"; break; - case AMF0_UNSUPPORTED: std::cerr << "Unsupported"; break; - case AMF0_RECORDSET: std::cerr << "Recordset"; break; - case AMF0_XMLDOC: std::cerr << "XML Document"; break; - case AMF0_TYPED_OBJ: std::cerr << "Typed Object"; break; - case AMF0_UPGRADE: std::cerr << "Upgrade to AMF3"; break; - case AMF0_DDV_CONTAINER: std::cerr << "DDVTech Container"; break; - } - std::cerr << " " << myIndice << " "; - switch (myType){ - case AMF0_NUMBER: case AMF0_BOOL: case AMF0_REFERENCE: case AMF0_DATE: std::cerr << numval; break; - case AMF0_STRING: case AMF0_LONGSTRING: case AMF0_XMLDOC: case AMF0_TYPED_OBJ: std::cerr << strval; break; - } - std::cerr << std::endl; - if (contents){ - for (std::vector::iterator it = contents->begin(); it != contents->end(); it++){it->Print(indent+" ");} - } - };//print - std::string Pack(){ - std::string r = ""; - if ((myType == AMF0_STRING) && (strval.size() > 0xFFFF)){myType = AMF0_LONGSTRING;} - if (myType != AMF0_DDV_CONTAINER){r += myType;} - switch (myType){ - case AMF0_NUMBER: - r += *(((char*)&numval)+7); r += *(((char*)&numval)+6); - r += *(((char*)&numval)+5); r += *(((char*)&numval)+4); - r += *(((char*)&numval)+3); r += *(((char*)&numval)+2); - r += *(((char*)&numval)+1); r += *(((char*)&numval)); - break; - case AMF0_DATE: - r += *(((char*)&numval)+7); r += *(((char*)&numval)+6); - r += *(((char*)&numval)+5); r += *(((char*)&numval)+4); - r += *(((char*)&numval)+3); r += *(((char*)&numval)+2); - r += *(((char*)&numval)+1); r += *(((char*)&numval)); - r += (char)0;//timezone always 0 - r += (char)0;//timezone always 0 - break; - case AMF0_BOOL: - r += (char)numval; - break; - case AMF0_STRING: - r += strval.size() / 256; - r += strval.size() % 256; - r += strval; - break; - case AMF0_LONGSTRING: - case AMF0_XMLDOC://is always a longstring - r += strval.size() / (256*256*256); - r += strval.size() / (256*256); - r += strval.size() / 256; - r += strval.size() % 256; - r += strval; - break; - case AMF0_TYPED_OBJ: - r += Indice().size() / 256; - r += Indice().size() % 256; - r += Indice(); - //is an object, with the classname first - case AMF0_OBJECT: - if (contents){ - for (std::vector::iterator it = contents->begin(); it != contents->end(); it++){ - r += it->Indice().size() / 256; - r += it->Indice().size() % 256; - r += it->Indice(); - r += it->Pack(); - } - } - r += (char)0; r += (char)0; r += (char)9; - break; - case AMF0_MOVIECLIP: - case AMF0_NULL: - case AMF0_UNDEFINED: - case AMF0_RECORDSET: - case AMF0_UNSUPPORTED: - //no data to add - break; - case AMF0_REFERENCE: - r += (char)((int)numval / 256); - r += (char)((int)numval % 256); - break; - case AMF0_ECMA_ARRAY:{ - int arrlen = 0; - if (contents){ - arrlen = contents->size(); - r += arrlen / (256*256*256); r += arrlen / (256*256); r += arrlen / 256; r += arrlen % 256; - for (std::vector::iterator it = contents->begin(); it != contents->end(); it++){ - r += it->Indice().size() / 256; - r += it->Indice().size() % 256; - r += it->Indice(); - r += it->Pack(); - } - }else{ - r += (char)0; r += (char)0; r += (char)0; r += (char)0; - } - r += (char)0; r += (char)0; r += (char)9; - } break; - case AMF0_STRICT_ARRAY:{ - int arrlen = 0; - if (contents){ - arrlen = contents->size(); - r += arrlen / (256*256*256); r += arrlen / (256*256); r += arrlen / 256; r += arrlen % 256; - for (std::vector::iterator it = contents->begin(); it != contents->end(); it++){ - r += it->Pack(); - } - }else{ - r += (char)0; r += (char)0; r += (char)0; r += (char)0; - } - } break; - case AMF0_DDV_CONTAINER://only send contents - if (contents){ - for (std::vector::iterator it = contents->begin(); it != contents->end(); it++){ - r += it->Pack(); - } - } - break; - } - return r; - };//pack - protected: - std::string myIndice; - unsigned char myType; - std::string strval; - double numval; - std::vector * contents; -};//AMFType + break; + } + return r; +};//pack -AMFType parseOneAMF(const unsigned char *& data, unsigned int &len, unsigned int &i, std::string name){ +/// Parses a single AMF0 type - used recursively by the AMF::parse() functions. +/// This function updates i every call with the new position in the data. +/// \param data The raw data to parse. +/// \param len The size of the raw data. +/// \param i Current parsing position in the raw data. +/// \param name Indice name for any new object created. +/// \returns A single AMF::Object, parsed from the raw data. +AMF::Object AMF::parseOne(const unsigned char *& data, unsigned int &len, unsigned int &i, std::string name){ std::string tmpstr; unsigned int tmpi = 0; unsigned char tmpdbl[8]; @@ -266,7 +258,7 @@ AMFType parseOneAMF(const unsigned char *& data, unsigned int &len, unsigned int fprintf(stderr, "Note: AMF type %hhx found. %i bytes left\n", data[i], len-i); #endif switch (data[i]){ - case AMF0_NUMBER: + case AMF::AMF0_NUMBER: tmpdbl[7] = data[i+1]; tmpdbl[6] = data[i+2]; tmpdbl[5] = data[i+3]; @@ -276,9 +268,9 @@ AMFType parseOneAMF(const unsigned char *& data, unsigned int &len, unsigned int tmpdbl[1] = data[i+7]; tmpdbl[0] = data[i+8]; i+=9;//skip 8(a double)+1 forwards - return AMFType(name, *(double*)tmpdbl, AMF0_NUMBER); + return AMF::Object(name, *(double*)tmpdbl, AMF::AMF0_NUMBER); break; - case AMF0_DATE: + case AMF::AMF0_DATE: tmpdbl[7] = data[i+1]; tmpdbl[6] = data[i+2]; tmpdbl[5] = data[i+3]; @@ -288,97 +280,97 @@ AMFType parseOneAMF(const unsigned char *& data, unsigned int &len, unsigned int tmpdbl[1] = data[i+7]; tmpdbl[0] = data[i+8]; i+=11;//skip 8(a double)+1+timezone(2) forwards - return AMFType(name, *(double*)tmpdbl, AMF0_DATE); + return AMF::Object(name, *(double*)tmpdbl, AMF::AMF0_DATE); break; - case AMF0_BOOL: + case AMF::AMF0_BOOL: i+=2;//skip bool+1 forwards if (data[i-1] == 0){ - return AMFType(name, (double)0, AMF0_BOOL); + return AMF::Object(name, (double)0, AMF::AMF0_BOOL); }else{ - return AMFType(name, (double)1, AMF0_BOOL); + return AMF::Object(name, (double)1, AMF::AMF0_BOOL); } break; - case AMF0_REFERENCE: + case AMF::AMF0_REFERENCE: tmpi = data[i+1]*256+data[i+2];//get the ref number value as a double i+=3;//skip ref+1 forwards - return AMFType(name, (double)tmpi, AMF0_REFERENCE); + return AMF::Object(name, (double)tmpi, AMF::AMF0_REFERENCE); break; - case AMF0_XMLDOC: + case AMF::AMF0_XMLDOC: tmpi = data[i+1]*256*256*256+data[i+2]*256*256+data[i+3]*256+data[i+4];//set tmpi to UTF-8-long length tmpstr.clear();//clean tmpstr, just to be sure tmpstr.append((const char *)data+i+5, (size_t)tmpi);//add the string data i += tmpi + 5;//skip length+size+1 forwards - return AMFType(name, tmpstr, AMF0_XMLDOC); + return AMF::Object(name, tmpstr, AMF::AMF0_XMLDOC); break; - case AMF0_LONGSTRING: + case AMF::AMF0_LONGSTRING: tmpi = data[i+1]*256*256*256+data[i+2]*256*256+data[i+3]*256+data[i+4];//set tmpi to UTF-8-long length tmpstr.clear();//clean tmpstr, just to be sure tmpstr.append((const char *)data+i+5, (size_t)tmpi);//add the string data i += tmpi + 5;//skip length+size+1 forwards - return AMFType(name, tmpstr, AMF0_LONGSTRING); + return AMF::Object(name, tmpstr, AMF::AMF0_LONGSTRING); break; - case AMF0_STRING: + case AMF::AMF0_STRING: tmpi = data[i+1]*256+data[i+2];//set tmpi to UTF-8 length tmpstr.clear();//clean tmpstr, just to be sure tmpstr.append((const char *)data+i+3, (size_t)tmpi);//add the string data i += tmpi + 3;//skip length+size+1 forwards - return AMFType(name, tmpstr, AMF0_STRING); + return AMF::Object(name, tmpstr, AMF::AMF0_STRING); break; - case AMF0_NULL: - case AMF0_UNDEFINED: - case AMF0_UNSUPPORTED: + case AMF::AMF0_NULL: + case AMF::AMF0_UNDEFINED: + case AMF::AMF0_UNSUPPORTED: ++i; - return AMFType(name, (double)0, data[i-1]); + return AMF::Object(name, (double)0, (AMF::obj0type)data[i-1]); break; - case AMF0_OBJECT:{ + case AMF::AMF0_OBJECT:{ ++i; - AMFType ret = AMFType(name, (unsigned char)AMF0_OBJECT); + AMF::Object ret(name, AMF::AMF0_OBJECT); while (data[i] + data[i+1] != 0){//while not encountering 0x0000 (we assume 0x000009) tmpi = data[i]*256+data[i+1];//set tmpi to the UTF-8 length tmpstr.clear();//clean tmpstr, just to be sure tmpstr.append((const char*)data+i+2, (size_t)tmpi);//add the string data i += tmpi + 2;//skip length+size forwards - ret.addContent(parseOneAMF(data, len, i, tmpstr));//add content, recursively parsed, updating i, setting indice to tmpstr + ret.addContent(AMF::parseOne(data, len, i, tmpstr));//add content, recursively parsed, updating i, setting indice to tmpstr } i += 3;//skip 0x000009 return ret; } break; - case AMF0_TYPED_OBJ:{ + case AMF::AMF0_TYPED_OBJ:{ ++i; tmpi = data[i]*256+data[i+1];//set tmpi to the UTF-8 length tmpstr.clear();//clean tmpstr, just to be sure tmpstr.append((const char*)data+i+2, (size_t)tmpi);//add the string data - AMFType ret = AMFType(tmpstr, (unsigned char)AMF0_TYPED_OBJ);//the object is not named "name" but tmpstr + AMF::Object ret(tmpstr, AMF::AMF0_TYPED_OBJ);//the object is not named "name" but tmpstr while (data[i] + data[i+1] != 0){//while not encountering 0x0000 (we assume 0x000009) tmpi = data[i]*256+data[i+1];//set tmpi to the UTF-8 length tmpstr.clear();//clean tmpstr, just to be sure tmpstr.append((const char*)data+i+2, (size_t)tmpi);//add the string data i += tmpi + 2;//skip length+size forwards - ret.addContent(parseOneAMF(data, len, i, tmpstr));//add content, recursively parsed, updating i, setting indice to tmpstr + ret.addContent(AMF::parseOne(data, len, i, tmpstr));//add content, recursively parsed, updating i, setting indice to tmpstr } i += 3;//skip 0x000009 return ret; } break; - case AMF0_ECMA_ARRAY:{ + case AMF::AMF0_ECMA_ARRAY:{ ++i; - AMFType ret = AMFType(name, (unsigned char)AMF0_ECMA_ARRAY); + AMF::Object ret(name, AMF::AMF0_ECMA_ARRAY); i += 4;//ignore the array length, we re-calculate it while (data[i] + data[i+1] != 0){//while not encountering 0x0000 (we assume 0x000009) tmpi = data[i]*256+data[i+1];//set tmpi to the UTF-8 length tmpstr.clear();//clean tmpstr, just to be sure tmpstr.append((const char*)data+i+2, (size_t)tmpi);//add the string data i += tmpi + 2;//skip length+size forwards - ret.addContent(parseOneAMF(data, len, i, tmpstr));//add content, recursively parsed, updating i, setting indice to tmpstr + ret.addContent(AMF::parseOne(data, len, i, tmpstr));//add content, recursively parsed, updating i, setting indice to tmpstr } i += 3;//skip 0x000009 return ret; } break; - case AMF0_STRICT_ARRAY:{ - AMFType ret = AMFType(name, (unsigned char)AMF0_STRICT_ARRAY); + case AMF::AMF0_STRICT_ARRAY:{ + AMF::Object ret(name, AMF::AMF0_STRICT_ARRAY); tmpi = data[i+1]*256*256*256+data[i+2]*256*256+data[i+3]*256+data[i+4];//set tmpi to array length i += 5;//skip size+1 forwards while (tmpi > 0){//while not done parsing array - ret.addContent(parseOneAMF(data, len, i, "arrVal"));//add content, recursively parsed, updating i + ret.addContent(AMF::parseOne(data, len, i, "arrVal"));//add content, recursively parsed, updating i --tmpi; } return ret; @@ -387,17 +379,25 @@ AMFType parseOneAMF(const unsigned char *& data, unsigned int &len, unsigned int #if DEBUG >= 2 fprintf(stderr, "Error: Unimplemented AMF type %hhx - returning.\n", data[i]); #endif - return AMFType("error", (unsigned char)0xFF); -}//parseOneAMF + return AMF::Object("error", AMF::AMF0_DDV_CONTAINER); +}//parseOne -AMFType parseAMF(const unsigned char * data, unsigned int len){ - AMFType ret("returned", (unsigned char)0xFF);//container type +/// Parses a C-string to a valid AMF::Object. +/// This function will find all AMF objects in the string and return +/// them all packed in a single AMF::AMF0_DDV_CONTAINER AMF::Object. +AMF::Object AMF::parse(const unsigned char * data, unsigned int len){ + AMF::Object ret("returned", AMF::AMF0_DDV_CONTAINER);//container type unsigned int i = 0, j = 0; while (i < len){ - ret.addContent(parseOneAMF(data, len, i, "")); + ret.addContent(AMF::parseOne(data, len, i, "")); if (i > j){j = i;}else{return ret;} } return ret; -}//parseAMF -AMFType parseAMF(std::string data){return parseAMF((const unsigned char*)data.c_str(), data.size());} +}//parse +/// Parses a std::string to a valid AMF::Object. +/// This function will find all AMF objects in the string and return +/// them all packed in a single AMF::AMF0_DDV_CONTAINER AMF::Object. +AMF::Object AMF::parse(std::string data){ + return AMF::parse((const unsigned char*)data.c_str(), data.size()); +}//parse diff --git a/util/amf.h b/util/amf.h new file mode 100644 index 00000000..81ab9881 --- /dev/null +++ b/util/amf.h @@ -0,0 +1,68 @@ +#pragma once +#include +#include +//#include +#include + +/// Holds all AMF parsing and creation related functions and classes. +namespace AMF{ + + /// Enumerates all possible AMF0 types, adding a special DDVTECH container type for ease of use. + enum obj0type { + AMF0_NUMBER = 0x00, + AMF0_BOOL = 0x01, + AMF0_STRING = 0x02, + AMF0_OBJECT = 0x03, + AMF0_MOVIECLIP = 0x04, + AMF0_NULL = 0x05, + AMF0_UNDEFINED = 0x06, + AMF0_REFERENCE = 0x07, + AMF0_ECMA_ARRAY = 0x08, + AMF0_OBJ_END = 0x09, + AMF0_STRICT_ARRAY = 0x0A, + AMF0_DATE = 0x0B, + AMF0_LONGSTRING = 0x0C, + AMF0_UNSUPPORTED = 0x0D, + AMF0_RECORDSET = 0x0E, + AMF0_XMLDOC = 0x0F, + AMF0_TYPED_OBJ = 0x10, + AMF0_UPGRADE = 0x11, + AMF0_DDV_CONTAINER = 0xFF + }; + + /// Recursive class that holds AMF0 objects. + /// It supports all AMF0 types (defined in AMF::obj0type), adding support for a special DDVTECH container type. + class Object { + public: + std::string Indice(); + obj0type GetType(); + double NumValue(); + std::string StrValue(); + const char * Str(); + int hasContent(); + void addContent(AMF::Object c); + Object* getContentP(int i); + Object getContent(int i); + Object* getContentP(std::string s); + Object getContent(std::string s); + Object(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); + void Print(std::string indent = ""); + std::string Pack(); + protected: + std::string myIndice; ///< Holds this objects indice, if any. + obj0type myType; ///< Holds this objects AMF0 type. + std::string strval; ///< Holds this objects string value, if any. + double numval; ///< Holds this objects numeric value, if any. + std::vector contents; ///< Holds this objects contents, if any (for container types). + };//AMFType + + /// Parses a C-string to a valid AMF::Object. + Object parse(const unsigned char * data, unsigned int len); + /// Parses a std::string to a valid AMF::Object. + Object parse(std::string data); + /// Parses a single AMF0 type - used recursively by the AMF::parse() functions. + Object parseOne(const unsigned char *& data, unsigned int &len, unsigned int &i, std::string name); + +};//AMF namespace From 23e1cf43356e429eaf3b669b115f86ef332430ef Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 10 Apr 2011 22:32:40 +0200 Subject: [PATCH 117/788] SWSocket library niet meer nodig! Jeej! --- sockets/SocketW.h | 22 -- sockets/sw_base.cpp | 764 ------------------------------------------ sockets/sw_base.h | 205 ------------ sockets/sw_inet.cpp | 249 -------------- sockets/sw_inet.h | 48 --- sockets/sw_internal.h | 75 ----- sockets/sw_unix.cpp | 99 ------ sockets/sw_unix.h | 41 --- 8 files changed, 1503 deletions(-) delete mode 100644 sockets/SocketW.h delete mode 100644 sockets/sw_base.cpp delete mode 100644 sockets/sw_base.h delete mode 100644 sockets/sw_inet.cpp delete mode 100644 sockets/sw_inet.h delete mode 100644 sockets/sw_internal.h delete mode 100644 sockets/sw_unix.cpp delete mode 100644 sockets/sw_unix.h diff --git a/sockets/SocketW.h b/sockets/SocketW.h deleted file mode 100644 index b7096c0c..00000000 --- a/sockets/SocketW.h +++ /dev/null @@ -1,22 +0,0 @@ -// C++ Socket Wrapper -// -// Started 020316 -// -// License: LGPL v2.1+ (see the file LICENSE) -// (c)2002-2003 Anders Lindstr�m - -/*********************************************************************** - * This library is free software; you can redistribute it and/or * - * modify it under the terms of the GNU Lesser General Public * - * License as published by the Free Software Foundation; either * - * version 2.1 of the License, or (at your option) any later version. * - ***********************************************************************/ - -#ifndef SocketW_H -#define SocketW_H - -#include "sw_base.h" -#include "sw_unix.h" -#include "sw_inet.h" - -#endif //SocketW_H diff --git a/sockets/sw_base.cpp b/sockets/sw_base.cpp deleted file mode 100644 index 23ca1eb6..00000000 --- a/sockets/sw_base.cpp +++ /dev/null @@ -1,764 +0,0 @@ -// C++ Socket Wrapper -// SocketW base class -// -// Started 020316 -// -// License: LGPL v2.1+ (see the file LICENSE) -// (c)2002-2003 Anders Lindstr�m - -/*********************************************************************** - * This library is free software; you can redistribute it and/or * - * modify it under the terms of the GNU Lesser General Public * - * License as published by the Free Software Foundation; either * - * version 2.1 of the License, or (at your option) any later version. * - ***********************************************************************/ - -#include "sw_base.h" -#include -#include -#include -#include -#include - -#ifndef __WIN32__ - #include - #include - #include - #include - #include - - #define INVALID_SOCKET -1 //avoid M$ braindamage -#else - //why use POSIX standards when we can make our own - //and frustrate people? (well known M$ policy) - - #ifndef EBADF - #define EBADF WSAEBADF - #endif - - #define ENOTSOCK WSAENOTSOCK - #define EOPNOTSUPP WSAEOPNOTSUPP - #define EADDRINUSE WSAEADDRINUSE - #define EWOULDBLOCK WSAEWOULDBLOCK - #define EMSGSIZE WSAEMSGSIZE - #define EINPROGRESS WSAEINPROGRESS - #define EALREADY WSAEALREADY - #define ECONNREFUSED WSAECONNREFUSED - #define ETIMEDOUT WSAETIMEDOUT - #define ENOTCONN WSAENOTCONN - - #ifndef EINTR - #define EINTR WSAEINTR - #endif -#endif - -#ifndef MSG_NOSIGNAL - #define MSG_NOSIGNAL 0 -#endif - -// Socklen hack -#if defined(__linux__) || defined(__FreeBSD__) // || defined(__bsdi__) || defined(__NetBSD__) too, perhaps? Bugreports, please! - #define sw_socklen_t socklen_t -#elif defined(__WIN32__) || defined(__osf__) - #define sw_socklen_t int -#else - #define sw_socklen_t unsigned int -#endif - - -using namespace std; - -#ifdef __WIN32__ -//Win32 braindamage -int close(int fd) -{ - return closesocket(fd); -} - -int fcntl(int fd, int cmd, long arg) -{ - unsigned long mode = arg; - - return WSAIoctl(fd, cmd, &mode, sizeof(unsigned long), NULL, 0, NULL, NULL, NULL); -} - -void WSA_exit(void) -{ - WSACleanup(); -} -#endif - - -//==================================================================== -//== Error handling mode -//==================================================================== -bool sw_DoThrow = false; -bool sw_Verbose = true; - -void sw_setThrowMode(bool throw_errors) -{ - sw_DoThrow = throw_errors; -} - -void sw_setVerboseMode(bool verbose) -{ - sw_Verbose = verbose; -} - -bool sw_getThrowMode(void) -{ - return sw_DoThrow; -} - -bool sw_getVerboseMode(void) -{ - return sw_Verbose; -} - - -//==================================================================== -//== Base error class -//==================================================================== -SWBaseSocket::SWBaseError::SWBaseError() -{ - be = ok; - error_string = ""; - failed_class = NULL; -} - -SWBaseSocket::SWBaseError::SWBaseError(base_error e) -{ - be = e; - error_string = ""; - failed_class = NULL; -} - -string SWBaseSocket::SWBaseError::get_error() -{ - return error_string; -} - -SWBaseSocket* SWBaseSocket::SWBaseError::get_failedClass(void) -{ - return failed_class; -} - -void SWBaseSocket::SWBaseError::set_errorString(string msg) -{ - error_string = msg; -} - -void SWBaseSocket::SWBaseError::set_failedClass(SWBaseSocket *pnt) -{ - failed_class = pnt; -} - -bool SWBaseSocket::SWBaseError::operator==(SWBaseError e) -{ - return be == e.be; -} - -bool SWBaseSocket::SWBaseError::operator!=(SWBaseError e) -{ - return be != e.be; -} - - -//==================================================================== -//== SWBaseSocket -//== Base class for sockets -//==================================================================== -SWBaseSocket::SWBaseSocket() -{ - //indicate nonopen - myfd = -1; - recv_close = false; - - //init values - error_string = ""; - block_mode = blocking; - fsend_ready = true; - frecv_ready = true; - tsec = 0; - tusec = 0; - - #ifdef __WIN32__ - //kick winsock awake - static bool firstuse = true; - if( firstuse == true ){ - WSAData wsaData; - int nCode; - if( (nCode = WSAStartup(MAKEWORD(1, 1), &wsaData)) != 0 ){ - handle_errno(NULL, "SWBaseSocket - WSAStartup() failed: "); - exit(-1); // Should never happend - } - - //cleanup at exit - atexit(WSA_exit); - firstuse = false; - } - #endif /* __WIN32__ */ -} - -SWBaseSocket::~SWBaseSocket() -{ - if(myfd > 0) - close(myfd); -} - -bool SWBaseSocket::listen(int qLimit, SWBaseError *error) -{ - get_socket(); - - //Avoid "Address already in use" thingie - char yes=1; - setsockopt(myfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); - - if(::listen(myfd, qLimit) == -1){ - handle_errno(error, "SWBaseSocket::listen() error: "); - return false; - } - - no_error(error); - return true; -} - -SWBaseSocket* SWBaseSocket::accept(SWBaseError *error) -{ - int remotefd = -1; - sockaddr remoteAdr; - - if( !waitRead(error) ) - return NULL; - - sw_socklen_t ssize = sizeof(sockaddr); - - if((remotefd = ::accept(myfd, &remoteAdr, &ssize)) == int(INVALID_SOCKET)){ - handle_errno(error, "SWBaseSocket::accept() error: "); - return NULL; - } - - //nonblocking? - if( block_mode == nonblocking ) - fcntl(remotefd, F_SETFL, O_NONBLOCK); - - /* Create new class*/ - SWBaseSocket* remoteClass = create(remotefd, error); - if( remoteClass == NULL ) - return NULL; - - no_error(error); - return remoteClass; -} - -bool SWBaseSocket::disconnect(SWBaseError *error) -{ - int n = 0; - char buf[256]; - - if(myfd < 0){ - set_error(error, notConnected, "SWBaseSocket::disconnect() - No connection"); - return false; - } - - //close WR (this signals the peer) - if( shutdown(myfd, 1) != 0 ){ - handle_errno(error, "SWBaseSocket::disconnect() error: "); - return false; - } - - - SWBaseError err; - - //wait for close signal from peer - if( recv_close == false ){ - while(true){ - if( !waitRead(error) ) - return false; - - n = recv(buf, 256, &err); - - if( n <= 0 ) - break; - if(block_mode == noWait){ - //we don't want to block - set_error(error, notReady, "SWBaseSocket::disconnect() - Need more time, call again"); - return false; - } - } - } - - if( n != 0 ){ - set_error(error, err, error_string); - return false; //error - } - - //reset state - reset(); - - close(myfd); - myfd = -1; - - no_error(error); - return true; -} - -bool SWBaseSocket::close_fd() -{ - if( myfd > 0 ){ - close(myfd); - myfd = -1; - - //reset state - reset(); - - return true; - } - return false; -} - -int SWBaseSocket::send(const char *buf, int bytes, SWBaseError *error) -{ - int ret; - - if(myfd < 0){ - set_error(error, notConnected, "SWBaseSocket::send() - No connection"); - return -1; - } - - if( !waitWrite(error) ) - return -1; - - ret = ::send(myfd, buf, bytes, MSG_NOSIGNAL); - - if( ret < 0 ) - handle_errno(error, "SWBaseSocket::send() error: "); - else - no_error(error); - - return ret; -} - -int SWBaseSocket::fsend(const char *buf, int bytes, SWBaseError *error) -{ - int n; - int bytessent; - - if(fsend_ready){ - //First call - fsend_bytesleft = bytes; - fsend_total = fsend_bytesleft; //global var needed for resume - bytessent = 0; - fsend_ready = false; //point of no return - } - else{ - //resume - bytessent = fsend_total - fsend_bytesleft; - } - - //send package - while( fsend_bytesleft > 0 ){ - n = send( buf + bytessent , fsend_bytesleft, error ); - - //return on error, wouldblock or nowait - if( n < 0 ) - return ( (bytessent > 0 )? -bytessent : -1 ); - - bytessent += n; - fsend_bytesleft -= n; - - if ( block_mode == noWait && fsend_bytesleft > 0 ){ - set_error(error, notReady, "SWBaseSocket::fsend() - Need more time, call again"); - return -bytessent; - } - } - - fsend_ready = true; - - no_error(error); - return fsend_total; -} - -int SWBaseSocket::sendmsg(const string msg, SWBaseError *error) -{ - return send(msg.c_str(), msg.size(), error); -} - -int SWBaseSocket::fsendmsg(const string msg, SWBaseError *error) -{ - return fsend(msg.c_str(), msg.size(), error); -} - -int SWBaseSocket::recv(char *buf, int bytes, SWBaseError *error) -{ - int ret; - - if(myfd < 0){ - set_error(error, notConnected, "SWBaseSocket::recv() - No connection"); - return -1; - } - - if( !waitRead(error) ) - return -1; - - ret = ::recv(myfd, buf, bytes, MSG_NOSIGNAL); - - if( ret < 0 ) - handle_errno(error, "SWBaseSocket::recv() error: "); - else if( ret == 0 ){ - recv_close = true; //we recived a close signal from peer - set_error(error, terminated, "SWBaseSocket::recv() - Connection terminated by peer"); - }else - no_error(error); - - return ret; -} - -int SWBaseSocket::frecv(char *buf, int bytes, SWBaseError *error) -{ - int n; - int bytesrecv; - - if(frecv_ready){ - //First call - frecv_bytesleft = bytes; - frecv_total = frecv_bytesleft; //global var needed for resume - bytesrecv = 0; - frecv_ready = false; //point of no return - } - else{ - //resume - bytesrecv = frecv_total - frecv_bytesleft; - } - - - //recv package - while( frecv_bytesleft > 0 ){ - n = recv( buf + bytesrecv , frecv_bytesleft, error ); - - //return on error, wouldblock, nowait or timeout - if( n < 0 ) - return ( (bytesrecv > 0 )? -bytesrecv : -1 ); - if( n == 0 ) - return 0; // terminated - - bytesrecv += n; - frecv_bytesleft -= n; - - if ( block_mode == noWait && frecv_bytesleft > 0 ){ - set_error(error, notReady, "SWBaseSocket::frecv() - Need more time, call again"); - return -bytesrecv; - } - } - - frecv_ready = true; - - no_error(error); - return frecv_total; -} - -string SWBaseSocket::recvmsg(int bytes, SWBaseError *error) -{ - char *buf = new char[bytes+1]; - - SWBaseError err; - string msg = ""; - int ret = recv(buf, bytes, &err); - - if( ret > 0 ){ - buf[ret]='\0'; // Make sure the string is null terminated - msg = buf; - no_error(error); - } - delete[] buf; - - if( ret < 1 ) - set_error(error, err, err.get_error()); - - return msg; -} - -int SWBaseSocket::get_fd(SWBaseError *error) -{ - if( myfd > 0 ){ - no_error(error); - return myfd; - } - - set_error(error, notConnected, "SWBaseSocket::get_fd() - No descriptor"); - return -1; -} - -bool SWBaseSocket::get_host(sockaddr *host, SWBaseError *error) -{ - if( host == NULL){ - set_error(error, fatal, "SWBaseSocket::get_host() - Got NULL pointer"); - return false; - } - - if(myfd < 0){ - set_error(error, notConnected, "SWBaseSocket::get_host() - No socket"); - return false; - } - - sw_socklen_t tmp = sizeof(sockaddr); - if( getsockname(myfd, host, &tmp) != 0 ){ - handle_errno(error, "SWBaseSocket::get_host() error: "); - return false; - } - - no_error(error); - return true; -} - -bool SWBaseSocket::get_peer(sockaddr *peer, SWBaseError *error) -{ - if( peer == NULL){ - set_error(error, fatal, "SWBaseSocket::get_peer() - Got NULL pointer"); - return false; - } - - if(myfd > 0){ - sw_socklen_t tmp = sizeof(sockaddr); - if( getpeername(myfd, peer, &tmp) != 0 ){ - handle_errno(error, "SWBaseSocket::get_peer() error: "); - return false; - } - }else{ - set_error(error, notConnected, "SWBaseSocket::get_peer() - No connection"); - return false; - } - - no_error(error); - return true; -} - -void SWBaseSocket::reset() -{ - // Reset flags - recv_close = false; - - fsend_ready = true; - frecv_ready = true; -} - -bool SWBaseSocket::waitIO(io_type &type, SWBaseError *error) -{ - if( block_mode != blocking ){ - no_error(error); - return true; - } - - // We prefere to wait with select() even if no timeout is set - // as select() behaves more predictable - - timeval t; - timeval *to = NULL; // Indicate "wait for ever" - t.tv_sec = tsec; - t.tv_usec = tusec; - - if( tsec > 0 || tusec > 0 ) - to = &t; - - fd_set readfds, writefds, exceptfds; - - FD_ZERO(&readfds); - FD_ZERO(&writefds); - FD_ZERO(&exceptfds); - FD_SET(myfd, &readfds); - FD_SET(myfd, &writefds); - FD_SET(myfd, &exceptfds); - - int ret = 0; - - switch (type){ - case read: - ret = select(myfd+1, &readfds, NULL, NULL, to); - break; - case write: - ret = select(myfd+1, NULL, &writefds, NULL, to); - break; - case except: - ret = select(myfd+1, NULL, NULL, &exceptfds, to); - break; - case rw: - ret = select(myfd+1, &readfds, &writefds, NULL, to); - break; - case all: - ret = select(myfd+1, &readfds, &writefds, &exceptfds, to); - break; - } - - if( ret < 0 ){ - handle_errno(error, "SWBaseSocket::waitIO() error: "); - return false; - } - if( ret == 0 ){ - set_error(error, timeout, "SWBaseSocket::waitIO() timeout"); - return false; - } - - if( FD_ISSET(myfd, &readfds) ){ - no_error(error); - type = read; - return true; - } - if( FD_ISSET(myfd, &writefds) ){ - no_error(error); - type = write; - return true; - } - if( FD_ISSET(myfd, &exceptfds) ){ - no_error(error); - type = except; - return true; - } - - set_error(error, fatal, "SWBaseSocket::waitIO() failed on select()"); - return false; -} - -bool SWBaseSocket::waitRead(SWBaseError *error) -{ - io_type tmp = read; - return waitIO(tmp, error); -} - -bool SWBaseSocket::waitWrite(SWBaseError *error) -{ - io_type tmp = write; - return waitIO(tmp, error); -} - -void SWBaseSocket::print_error() -{ - if( error_string.size() > 0 ) - fprintf(stderr, "%s!\n", error_string.c_str()); -} - -void SWBaseSocket::handle_errno(SWBaseError *error, string msg) -{ - #ifndef __WIN32__ - msg += strerror(errno); - #else - //stupid stupid stupid stupid M$ - switch (WSAGetLastError()){ - case 0: msg += "No error"; break; - case WSAEINTR: msg += "Interrupted system call"; break; - case WSAEBADF: msg += "Bad file number"; break; - case WSAEACCES: msg += "Permission denied"; break; - case WSAEFAULT: msg += "Bad address"; break; - case WSAEINVAL: msg += "Invalid argument"; break; - case WSAEMFILE: msg += "Too many open sockets"; break; - case WSAEWOULDBLOCK: msg += "Operation would block"; break; - case WSAEINPROGRESS: msg += "Operation now in progress"; break; - case WSAEALREADY: msg += "Operation already in progress"; break; - case WSAENOTSOCK: msg += "Socket operation on non-socket"; break; - case WSAEDESTADDRREQ: msg += "Destination address required"; break; - case WSAEMSGSIZE: msg += "Message too long"; break; - case WSAEPROTOTYPE: msg += "Protocol wrong type for socket"; break; - case WSAENOPROTOOPT: msg += "Bad protocol option"; break; - case WSAEPROTONOSUPPORT: msg += "Protocol not supported"; break; - case WSAESOCKTNOSUPPORT: msg += "Socket type not supported"; break; - case WSAEOPNOTSUPP: msg += "Operation not supported on socket"; break; - case WSAEPFNOSUPPORT: msg += "Protocol family not supported"; break; - case WSAEAFNOSUPPORT: msg += "Address family not supported"; break; - case WSAEADDRINUSE: msg += "Address already in use"; break; - case WSAEADDRNOTAVAIL: msg += "Can't assign requested address"; break; - case WSAENETDOWN: msg += "Network is down"; break; - case WSAENETUNREACH: msg += "Network is unreachable"; break; - case WSAENETRESET: msg += "Net connection reset"; break; - case WSAECONNABORTED: msg += "Software caused connection abort"; break; - case WSAECONNRESET: msg += "Connection reset by peer"; break; - case WSAENOBUFS: msg += "No buffer space available"; break; - case WSAEISCONN: msg += "Socket is already connected"; break; - case WSAENOTCONN: msg += "Socket is not connected"; break; - case WSAESHUTDOWN: msg += "Can't send after socket shutdown"; break; - case WSAETOOMANYREFS: msg += "Too many references"; break; - case WSAETIMEDOUT: msg += "Connection timed out"; break; - case WSAECONNREFUSED: msg += "Connection refused"; break; - case WSAELOOP: msg += "Too many levels of symbolic links"; break; - case WSAENAMETOOLONG: msg += "File name too long"; break; - case WSAEHOSTDOWN: msg += "Host is down"; break; - case WSAEHOSTUNREACH: msg += "No route to host"; break; - case WSAENOTEMPTY: msg += "Directory not empty"; break; - case WSAEPROCLIM: msg += "Too many processes"; break; - case WSAEUSERS: msg += "Too many users"; break; - case WSAEDQUOT: msg += "Disc quota exceeded"; break; - case WSAESTALE: msg += "Stale NFS file handle"; break; - case WSAEREMOTE: msg += "Too many levels of remote in path"; break; - case WSASYSNOTREADY: msg += "Network system is unavailable"; break; - case WSAVERNOTSUPPORTED: msg += "Winsock version out of range"; break; - case WSANOTINITIALISED: msg += "WSAStartup not yet called"; break; - case WSAEDISCON: msg += "Graceful shutdown in progress"; break; - case WSAHOST_NOT_FOUND: msg += "Host not found"; break; - case WSANO_DATA: msg += "No host data of that type was found"; break; - default: msg += "Unknown Winsock error: " + WSAGetLastError(); break; - } - #endif - - int errorno; - - //Win32 braindamage - #ifdef __WIN32__ - errorno = WSAGetLastError(); - #else - errorno = errno; - #endif - - SWBaseError e; - - if( errorno == EADDRINUSE ) - e = portInUse; - else if( errorno == EAGAIN || errorno == EWOULDBLOCK ) - e = notReady; - else if( errorno == EMSGSIZE ) - e = msgTooLong; - else if( errorno == EINPROGRESS || errorno == EALREADY ) - e = notReady; - else if( errorno == ECONNREFUSED || errorno == ETIMEDOUT ) - e = noResponse; - else if( errorno == ENOTCONN || errorno == EBADF || errorno == ENOTSOCK ) - e = notConnected; - else if( errorno == EPIPE ){ - e = terminated; - recv_close = true; - }else if( errorno == EINTR ) - e = interrupted; - else - e = fatal; //default - - set_error(error, e, msg); -} - -void SWBaseSocket::no_error(SWBaseError *error) -{ - if(error != NULL){ - *error = ok; - error->error_string = ""; - error->failed_class = NULL; - } -} - -void SWBaseSocket::set_error(SWBaseError *error, SWBaseError name, string msg) -{ - error_string = msg; - - if(error != NULL){ - *error = name; - error->error_string = msg; - error->failed_class = this; - }else{ - if( sw_Verbose ) - print_error(); - - if( sw_DoThrow ){ - SWBaseError e; - e = name; - e.error_string = msg; - e.failed_class = this; - throw e; - } - } -} - diff --git a/sockets/sw_base.h b/sockets/sw_base.h deleted file mode 100644 index 2c4e33ef..00000000 --- a/sockets/sw_base.h +++ /dev/null @@ -1,205 +0,0 @@ -// C++ Socket Wrapper -// SocketW base socket header -// -// Started 020316 -// -// License: LGPL v2.1+ (see the file LICENSE) -// (c)2002-2003 Anders Lindström - -/*********************************************************************** - * This library is free software; you can redistribute it and/or * - * modify it under the terms of the GNU Lesser General Public * - * License as published by the Free Software Foundation; either * - * version 2.1 of the License, or (at your option) any later version. * - ***********************************************************************/ - -#ifndef sw_base_H -#define sw_base_H - -#include "sw_internal.h" - -#include -#include - -// Set error handling mode -// throw_errors == true : Throws the error class on unhandled errors -// throw_errors == false : Exit on unhandled errors -// verbose == true : Prints the error message to stderr on unhandled errors -// -// Default is throw_errors == false and verbose == true -void sw_setThrowMode(bool throw_errors); -void sw_setVerboseMode(bool verbose); -bool sw_getThrowMode(void); -bool sw_getVerboseMode(void); - - -// Abstract base class for streaming sockets -class DECLSPEC SWBaseSocket -{ -public: - SWBaseSocket(); - virtual ~SWBaseSocket(); - - // Error types - // ok - operation succesful - // fatal - unspecified error - // notReady - you should call the function again - // indicates that the function would block (if nowait/nonblocking) - // portInUse - this port is used by another socket ( on listen() ) - // notConnected - socket not connected (or valid) - // msgTooLong - the message size it too big for send() - // terminated - connection terminated (by peer) - // noResponse - can't connect() to peer - // timeout - a read/write operation timed out (only if a timeout value is set and if in blocking mode) - // interrupted - operation was interrupted by a nonblocked signal - enum base_error{ok, fatal, notReady, portInUse, notConnected, msgTooLong, terminated, noResponse, timeout, interrupted}; - - class DECLSPEC SWBaseError - { - public: - SWBaseError(); - SWBaseError(base_error e); - - virtual ~SWBaseError(){;} - - virtual std::string get_error(); - virtual SWBaseSocket* get_failedClass(void); - - virtual bool operator==(SWBaseError e); - virtual bool operator!=(SWBaseError e); - - virtual void set_errorString(std::string msg); - virtual void set_failedClass(SWBaseSocket *pnt); - protected: - friend class SWBaseSocket; - - // The base error type - base_error be; - - // Human readable error string - std::string error_string; - - // A pointer to the class causing the error - SWBaseSocket *failed_class; - }; - - - // Note: If no SWBaseError class is provided with a method call (==NULL), - // SocketW will print the error to stderr and exit or throw on errors. - - // Note: All bool functions returns true on success. - - // Block mode - // blocking - everythings blocks until completly done - // noWait - operations block but only once - // useful with blocking w. select() - // nonblocking - don't block (you should use select()) - enum block_type{nonblocking, noWait, blocking}; - - - // Connection methods - // qLimit - the maximum length the queue of pending connections. - // Accept returns a new socket class connected with peer (should be - // freed with delete) or NULL on failure. You can cast the class to - // the correct type if you need to ( eg. (SWInetSocket *)mysocket ). - virtual bool listen(int qLimit = 5, SWBaseError *error = NULL); - virtual SWBaseSocket* accept(SWBaseError *error = NULL); - // bind() and connect() are implemented in child classes - - // do the disconnect ritual (signal peer, wait for close singal and close socket) - virtual bool disconnect(SWBaseError *error = NULL); - - // force close socket - virtual bool close_fd(); //use with care, disconnect() is cleaner - - // Direct I/O (raw) - // Can send/recv less bytes than specified! - // Returns the actual amount of bytes sent/recv on sucess - // and an negative integer on failure. - virtual int send(const char *buf, int bytes, SWBaseError *error = NULL); - virtual int sendmsg(const std::string msg, SWBaseError *error = NULL); - virtual int recv(char *buf, int bytes, SWBaseError *error = NULL); - virtual std::string recvmsg(int bytes = 256, SWBaseError *error = NULL); - - // Forced I/O - // Force system to send/recv the specified amount of bytes. - // On nowait/nonblocking: might return with notReady and then you - // MUST call the same method again (eg. wait with select() to know when) - // with the same parameters until the operation is finished. - // Returns 'bytes' when finished, negative integer on failure and - // 'notReady'. In the 'notReady' case, -(return value) is the amount of - // bytes sent/recv so far. - virtual int fsend(const char *buf, int bytes, SWBaseError *error = NULL); - virtual int fsendmsg(const std::string msg, SWBaseError *error = NULL); - virtual int frecv(char *buf, int bytes, SWBaseError *error = NULL); - - // Tools - // get_fd() - get socket descriptor, can be used with select() - // returns -1 on failure. - // get_host/peer fills the provided structures with info about the - // host/peer (see man unix & ip). - // SWInetSocket has some more tools for TCP/IP sockets. - virtual int get_fd(SWBaseError *error); - virtual bool get_host(sockaddr *host, SWBaseError *error = NULL); - virtual bool get_peer(sockaddr *peer, SWBaseError *error = NULL); - - // Set recv timeout (only in blocking mode). - // set_timeout(0,0) means wait forever (default). - // This affects the functions recv(), send(), accept() and disconnect() - // and others that use those, i.e. all frecvmsg(). - void set_timeout(Uint32 sec, Uint32 usec){ tsec = sec, tusec = usec; } - - // Error handling - virtual void print_error(); //prints the last error if any to stderr - virtual std::string get_error(){return error_string;} //returns a human readable error string - -protected: - // get a new socket if myfd < 0 - virtual void get_socket()=0; - - // create a new class for accept() using socketdescriptor - virtual SWBaseSocket* create(int socketdescriptor, SWBaseError *error)=0; - - // reset state - virtual void reset(); - - // wait for I/O (with timeout) - enum io_type{read, write, except, rw, all}; - virtual bool waitIO(io_type &type, SWBaseError *error); - bool waitRead(SWBaseError *error); - bool waitWrite(SWBaseError *error); - - // internal error handling - virtual void handle_errno(SWBaseError *error, std::string msg); - virtual void no_error(SWBaseError *error); - virtual void set_error(SWBaseError *error, SWBaseError name, std::string msg); - - // our socket descriptor - int myfd; - - // last error - std::string error_string; - - // data for fsend - bool fsend_ready; - int fsend_total; - int fsend_bytesleft; - - // data for frecv - bool frecv_ready; - int frecv_total; - int frecv_bytesleft; - - // have we recived a shutdown signal? - bool recv_close; - - //blocking mode (set by child classes) - block_type block_mode; - - //timeout for waitIO() - int tsec; - int tusec; -}; - - -#endif /* sw_base_H */ diff --git a/sockets/sw_inet.cpp b/sockets/sw_inet.cpp deleted file mode 100644 index c056722e..00000000 --- a/sockets/sw_inet.cpp +++ /dev/null @@ -1,249 +0,0 @@ -// C++ Socket Wrapper -// SocketW Inet socket -// -// Started 020316 -// -// License: LGPL v2.1+ (see the file LICENSE) -// (c)2002-2003 Anders Lindström - -/*********************************************************************** - * This library is free software; you can redistribute it and/or * - * modify it under the terms of the GNU Lesser General Public * - * License as published by the Free Software Foundation; either * - * version 2.1 of the License, or (at your option) any later version. * - ***********************************************************************/ - -#include "sw_inet.h" - -#ifndef __WIN32__ - #include - #include - #include - #include - #include -#else - #define F_SETFL FIONBIO - #define O_NONBLOCK 1 - - //Defined in sw_base.cxx - extern int close(int fd); - extern int fcntl(int fd, int cmd, long arg); -#endif - -using namespace std; - -//==================================================================== -//== SWInetSocket -//== Inet (TCP/IP) streaming sockets -//==================================================================== -SWInetSocket::SWInetSocket(block_type block) -{ - block_mode = block; -} - -SWInetSocket::~SWInetSocket() -{ - -} - -void SWInetSocket::get_socket() -{ - if( myfd < 0 ){ - myfd = socket(PF_INET, SOCK_STREAM, 0); - - if( block_mode == nonblocking ) - fcntl(myfd, F_SETFL, O_NONBLOCK); - - //reset state - reset(); - } -} - -SWBaseSocket* SWInetSocket::create(int socketdescriptor, SWBaseError *error) -{ - SWInetSocket* remoteClass; - - /* Create new class*/ - remoteClass = new SWInetSocket(block_mode); - remoteClass->myfd = socketdescriptor; - - no_error(error); - return remoteClass; -} - -bool SWInetSocket::bind(int port, SWBaseError *error) -{ - return bind(port, "", error); -} - -bool SWInetSocket::bind(int port, string host, SWBaseError *error) -{ - hostent *h; - in_addr inp; - - if( host.size() > 0 ){ - // Bind to a specific address - - if( (h = gethostbyname(host.c_str())) == NULL ){ - set_error(error, fatal, "SWInetSocket::bind() - Can't get host by name"); - return false; - } - - inp = *((in_addr *)h->h_addr); - }else{ - // Bind to any - inp.s_addr = INADDR_ANY; - } - - - get_socket(); - - sockaddr_in myAdr; - - memset(&myAdr, 0, sizeof(myAdr)); - myAdr.sin_family = AF_INET; - myAdr.sin_port = htons(port); - myAdr.sin_addr.s_addr = inp.s_addr; - - if(::bind(myfd, (sockaddr *)&myAdr, sizeof(myAdr)) == -1){ - handle_errno(error, "SWInetSocket::bind() error: "); - return false; - } - - no_error(error); - return true; -} - -bool SWInetSocket::connect(int port, string hostname, SWBaseError *error) -{ - get_socket(); - - hostent *host; - - if( (host = gethostbyname(hostname.c_str())) == NULL ){ - set_error(error, fatal, "SWInetSocket::connect() - Can't get host by name"); - return false; - } - - sockaddr_in remoteAdr; - - memset(&remoteAdr, 0, sizeof(remoteAdr)); - remoteAdr.sin_family = AF_INET; - remoteAdr.sin_port = htons(port); - remoteAdr.sin_addr = *((in_addr *)host->h_addr); - - if(::connect(myfd, (sockaddr *)&remoteAdr, sizeof(remoteAdr)) == -1){ - handle_errno(error, "SWInetSocket::connect() error: "); - return false; - } - - no_error(error); - return true; -} - - -string SWInetSocket::get_peerAddr(SWBaseError *error) -{ - sockaddr_in adr; - - if( !get_peer((sockaddr *)&adr, error) ) - return ""; - - char *pnt; - - if( (pnt = inet_ntoa(adr.sin_addr)) == NULL ){ - set_error(error, fatal, "SWInetSocket::get_peerName() - Can't get peer address"); - return ""; - } - string name(pnt); - - no_error(error); - return name; -} - -int SWInetSocket::get_peerPort(SWBaseError *error) -{ - sockaddr_in adr; - - if( !get_peer((sockaddr *)&adr, error) ) - return -1; - - no_error(error); - - return ntohs(adr.sin_port); -} - -string SWInetSocket::get_peerName(SWBaseError *error) -{ - string name = get_peerAddr(error); - if(name.size() < 1) - return ""; - - - hostent *h; - - if( (h = gethostbyname(name.c_str())) == NULL ){ - set_error(error, fatal, "SWInetSocket::get_peerName() - Can't get peer by address"); - return ""; - } - string host_name(h->h_name); - - no_error(error); - return host_name; -} - -string SWInetSocket::get_hostAddr(SWBaseError *error) -{ - //We need to get the real address, so we must - //first get this computers host name and then - //translate that into an address! - - string name = get_hostName(error); - if( name.size() < 1 ) - return ""; - - hostent *host; - - if( (host = gethostbyname(name.c_str())) == NULL ){ - set_error(error, fatal, "SWInetSocket::get_hostAddr() - Can't get host by name"); - return ""; - } - - char *pnt; - - if( (pnt = inet_ntoa(*((in_addr *)host->h_addr))) == NULL){ - set_error(error, fatal, "SWInetSocket::get_hostAddr() - Can't get host address"); - return ""; - } - - string adr(pnt); - - return adr; -} - -int SWInetSocket::get_hostPort(SWBaseError *error) -{ - sockaddr_in adr; - - if( !get_host((sockaddr *)&adr, error) ) - return -1; - - no_error(error); - - return ntohs(adr.sin_port); -} - -string SWInetSocket::get_hostName(SWBaseError *error) -{ - char buf[256]; - - if( gethostname(buf, 256) != 0 ){ - handle_errno(error, "SWInetSocket::gethostname() error: "); - return ""; - } - - string msg(buf); - - no_error(error); - return msg; -} diff --git a/sockets/sw_inet.h b/sockets/sw_inet.h deleted file mode 100644 index 26050845..00000000 --- a/sockets/sw_inet.h +++ /dev/null @@ -1,48 +0,0 @@ -// C++ Socket Wrapper -// SocketW Inet socket header -// -// Started 020316 -// -// License: LGPL v2.1+ (see the file LICENSE) -// (c)2002-2003 Anders Lindström - -/*********************************************************************** - * This library is free software; you can redistribute it and/or * - * modify it under the terms of the GNU Lesser General Public * - * License as published by the Free Software Foundation; either * - * version 2.1 of the License, or (at your option) any later version. * - ***********************************************************************/ - -#ifndef sw_inet_H -#define sw_inet_H - -#include "sw_internal.h" -#include "sw_base.h" -#include - -// Simple streaming TCP/IP class -class DECLSPEC SWInetSocket : public SWBaseSocket -{ -public: - SWInetSocket(block_type block=blocking); - virtual ~SWInetSocket(); - - virtual bool bind(int port, SWBaseError *error = NULL); //use port=0 to get any free port - virtual bool bind(int port, std::string host, SWBaseError *error = NULL); //you can also specify the host interface to use - virtual bool connect(int port, std::string hostname, SWBaseError *error = NULL); - - // Tools - // Gets IP addr, name or port. - virtual std::string get_peerAddr(SWBaseError *error = NULL); - virtual int get_peerPort(SWBaseError *error = NULL); - virtual std::string get_peerName(SWBaseError *error = NULL); - virtual std::string get_hostAddr(SWBaseError *error = NULL); - virtual int get_hostPort(SWBaseError *error = NULL); - virtual std::string get_hostName(SWBaseError *error = NULL); - -protected: - virtual void get_socket(); - virtual SWBaseSocket* create(int socketdescriptor, SWBaseError *error); -}; - -#endif /* sw_inet_H */ diff --git a/sockets/sw_internal.h b/sockets/sw_internal.h deleted file mode 100644 index 94313bcd..00000000 --- a/sockets/sw_internal.h +++ /dev/null @@ -1,75 +0,0 @@ -// C++ Socket Wrapper -// SocketW internal header -// -// Started 030823 -// -// License: LGPL v2.1+ (see the file LICENSE) -// (c)2002-2003 Anders Lindstr�m - -/*********************************************************************** - * This library is free software; you can redistribute it and/or * - * modify it under the terms of the GNU Lesser General Public * - * License as published by the Free Software Foundation; either * - * version 2.1 of the License, or (at your option) any later version. * - ***********************************************************************/ - -#ifndef sw_internal_H -#define sw_internal_H - -// This header is included in all *.h files - -#ifndef __WIN32__ - #include - #include - #include - #include -#else - #include - - #define F_SETFL FIONBIO - #define O_NONBLOCK 1 -#endif - -#ifndef _SDL_H - -// Define general types -typedef unsigned char Uint8; -typedef signed char Sint8; -typedef unsigned short Uint16; -typedef signed short Sint16; -typedef unsigned int Uint32; -typedef signed int Sint32; - -// It's VERY important that these types really have the right sizes! -// This black magic is from SDL -#define COMPILE_TIME_ASSERT(name, x) \ - typedef int _dummy_ ## name[(x) * 2 - 1] -COMPILE_TIME_ASSERT(uint8, sizeof(Uint8) == 1); -COMPILE_TIME_ASSERT(sint8, sizeof(Sint8) == 1); -COMPILE_TIME_ASSERT(uint16, sizeof(Uint16) == 2); -COMPILE_TIME_ASSERT(sint16, sizeof(Sint16) == 2); -COMPILE_TIME_ASSERT(uint32, sizeof(Uint32) == 4); -COMPILE_TIME_ASSERT(sint32, sizeof(Sint32) == 4); -#undef COMPILE_TIME_ASSERT - -#endif /* _SDL_H */ - -// Some compilers use a special export keyword -#ifndef DECLSPEC - #ifdef __BEOS__ - #if defined(__GNUC__) - #define DECLSPEC __declspec(dllexport) - #else - #define DECLSPEC __declspec(export) - #endif - #else - #ifdef WIN32 - #define DECLSPEC __declspec(dllexport) - #else - #define DECLSPEC - #endif - #endif -#endif - - -#endif /* sw_internal_H */ diff --git a/sockets/sw_unix.cpp b/sockets/sw_unix.cpp deleted file mode 100644 index 07e2dd10..00000000 --- a/sockets/sw_unix.cpp +++ /dev/null @@ -1,99 +0,0 @@ -// C++ Socket Wrapper -// SocketW Unix socket -// -// Started 020316 -// -// License: LGPL v2.1+ (see the file LICENSE) -// (c)2002-2003 Anders Lindström - -/*********************************************************************** - * This library is free software; you can redistribute it and/or * - * modify it under the terms of the GNU Lesser General Public * - * License as published by the Free Software Foundation; either * - * version 2.1 of the License, or (at your option) any later version. * - ***********************************************************************/ - -#include "sw_unix.h" -#include - -using namespace std; - -//==================================================================== -//== SWUnixSocket -//== Unix streaming sockets -//==================================================================== -#ifndef __WIN32__ - -SWUnixSocket::SWUnixSocket(block_type block) -{ - block_mode = block; -} - -SWUnixSocket::~SWUnixSocket() -{ - //nothing here -} - -void SWUnixSocket::get_socket() -{ - if( myfd < 0 ){ - myfd = socket(PF_UNIX, SOCK_STREAM, 0); - - if( block_mode == nonblocking ) - fcntl(myfd, F_SETFL, O_NONBLOCK); - - //reset state - reset(); - } -} - - -SWBaseSocket* SWUnixSocket::create(int socketdescriptor, SWBaseError *error) -{ - SWUnixSocket* remoteClass; - - /* Create new class*/ - remoteClass = new SWUnixSocket(block_mode); - remoteClass->myfd = socketdescriptor; - - no_error(error); - return remoteClass; -} - -bool SWUnixSocket::bind(string path, SWBaseError *error) -{ - get_socket(); - - sockaddr_un myAdr; - - myAdr.sun_family = AF_UNIX; - strncpy(myAdr.sun_path, path.c_str(), path.size()+1); - - if(::bind(myfd, (sockaddr *)&myAdr, sizeof(myAdr)) == -1){ - handle_errno(error, "SWUnixSocket::bind() error: "); - return false; - } - - no_error(error); - return true; -} - -bool SWUnixSocket::connect(string path, SWBaseError *error) -{ - get_socket(); - - sockaddr_un remoteAdr; - - remoteAdr.sun_family = AF_UNIX; - strncpy(remoteAdr.sun_path, path.c_str(), path.size()+1); - - if(::connect(myfd, (sockaddr *)&remoteAdr, sizeof(remoteAdr)) == -1){ - handle_errno(error, "SWUnixSocket::connect() error: "); - return false; - } - - no_error(error); - return true; -} - -#endif /* __WIN32__ */ diff --git a/sockets/sw_unix.h b/sockets/sw_unix.h deleted file mode 100644 index 576f14c0..00000000 --- a/sockets/sw_unix.h +++ /dev/null @@ -1,41 +0,0 @@ -// C++ Socket Wrapper -// SocketW Unix socket header -// -// Started 020316 -// -// License: LGPL v2.1+ (see the file LICENSE) -// (c)2002-2003 Anders Lindström - -/*********************************************************************** - * This library is free software; you can redistribute it and/or * - * modify it under the terms of the GNU Lesser General Public * - * License as published by the Free Software Foundation; either * - * version 2.1 of the License, or (at your option) any later version. * - ***********************************************************************/ - -#ifndef sw_unix_H -#define sw_unix_H -#ifndef __WIN32__ - -#include "sw_internal.h" -#include "sw_base.h" -#include - -// Simple streaming Unix class -class DECLSPEC SWUnixSocket : public SWBaseSocket -{ -public: - SWUnixSocket(block_type block=blocking); - ~SWUnixSocket(); - - // bind and connect to the socket file "path" - virtual bool bind(std::string path, SWBaseError *error = NULL); - virtual bool connect(std::string path, SWBaseError *error = NULL); - -protected: - virtual void get_socket(); - virtual SWBaseSocket* create(int socketdescriptor, SWBaseError *error); -}; - -#endif /* __WIN32__ */ -#endif /* sw_unix_H */ From f801beab75c25ceb408acceb7b183a63e23b269f Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 11 Apr 2011 17:05:36 +0200 Subject: [PATCH 118/788] Even more documentation, RTMP Connector compiles again, but still extremely buggy. Will create RTMP debugging tool soon. Something wrong with RTMP Connector reading FLV::Tags... needs more investigation. --- Doxyfile | 2 +- util/amf.cpp | 3 + util/amf.h | 3 + util/crypto.cpp | 509 ++++++++++++++++++++++++++++++++++++++++++ util/crypto.h | 56 +++++ util/ddv_socket.cpp | 18 ++ util/ddv_socket.h | 4 + util/flv_tag.cpp | 22 +- util/flv_tag.h | 6 +- util/http_parser.cpp | 132 ++++++++--- util/http_parser.h | 74 +++--- util/rtmpchunks.cpp | 442 ++++++++++++++++++++++++++++++++++++ util/rtmpchunks.h | 65 ++++++ util/server_setup.cpp | 40 +++- 14 files changed, 1287 insertions(+), 89 deletions(-) create mode 100644 util/crypto.cpp create mode 100644 util/crypto.h create mode 100644 util/rtmpchunks.cpp create mode 100644 util/rtmpchunks.h diff --git a/Doxyfile b/Doxyfile index dd8ba205..b6edff54 100644 --- a/Doxyfile +++ b/Doxyfile @@ -43,7 +43,7 @@ SYMBOL_CACHE_SIZE = 0 # Build related configuration options #--------------------------------------------------------------------------- EXTRACT_ALL = YES -EXTRACT_PRIVATE = NO +EXTRACT_PRIVATE = YES EXTRACT_STATIC = YES EXTRACT_LOCAL_CLASSES = YES EXTRACT_LOCAL_METHODS = NO diff --git a/util/amf.cpp b/util/amf.cpp index fdff660f..97d97ef5 100644 --- a/util/amf.cpp +++ b/util/amf.cpp @@ -1,3 +1,6 @@ +/// \file amf.cpp +/// Holds all code for the AMF namespace. + #include "amf.h" /// Returns the std::string Indice for the current object, if available. diff --git a/util/amf.h b/util/amf.h index 81ab9881..8ae95cd4 100644 --- a/util/amf.h +++ b/util/amf.h @@ -1,3 +1,6 @@ +/// \file amf.h +/// Holds all headers for the AMF namespace. + #pragma once #include #include diff --git a/util/crypto.cpp b/util/crypto.cpp new file mode 100644 index 00000000..c523c680 --- /dev/null +++ b/util/crypto.cpp @@ -0,0 +1,509 @@ +/// \file crypto.cpp +/// Holds all code needed for RTMP cryptography. + +#define STR(x) (((std::string)(x)).c_str()) + +#include "crypto.h" + +#define P768 \ +"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ +"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ +"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ +"E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF" + +#define P1024 \ +"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ +"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ +"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ +"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ +"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381" \ +"FFFFFFFFFFFFFFFF" + +#define Q1024 \ +"7FFFFFFFFFFFFFFFE487ED5110B4611A62633145C06E0E68" \ +"948127044533E63A0105DF531D89CD9128A5043CC71A026E" \ +"F7CA8CD9E69D218D98158536F92F8A1BA7F09AB6B6A8E122" \ +"F242DABB312F3F637A262174D31BF6B585FFAE5B7A035BF6" \ +"F71C35FDAD44CFD2D74F9208BE258FF324943328F67329C0" \ +"FFFFFFFFFFFFFFFF" + +#define P1536 \ +"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ +"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ +"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ +"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ +"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ +"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ +"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ +"670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF" + +#define P2048 \ +"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ +"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ +"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ +"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ +"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ +"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ +"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ +"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \ +"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \ +"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \ +"15728E5A8AACAA68FFFFFFFFFFFFFFFF" + +#define P3072 \ +"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ +"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ +"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ +"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ +"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ +"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ +"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ +"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \ +"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \ +"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \ +"15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \ +"ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \ +"ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \ +"F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \ +"BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \ +"43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF" + +#define P4096 \ +"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ +"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ +"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ +"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ +"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ +"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ +"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ +"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \ +"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \ +"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \ +"15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \ +"ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \ +"ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \ +"F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \ +"BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \ +"43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" \ +"88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" \ +"2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" \ +"287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" \ +"1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" \ +"93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199" \ +"FFFFFFFFFFFFFFFF" + +#define P6144 \ +"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ +"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ +"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ +"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ +"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ +"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ +"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ +"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \ +"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \ +"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \ +"15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \ +"ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \ +"ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \ +"F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \ +"BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \ +"43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" \ +"88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" \ +"2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" \ +"287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" \ +"1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" \ +"93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" \ +"36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD" \ +"F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831" \ +"179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B" \ +"DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF" \ +"5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6" \ +"D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3" \ +"23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" \ +"CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328" \ +"06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C" \ +"DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE" \ +"12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF" + +#define P8192 \ +"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ +"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ +"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ +"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ +"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ +"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ +"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ +"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \ +"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \ +"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \ +"15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \ +"ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \ +"ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \ +"F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \ +"BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \ +"43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" \ +"88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" \ +"2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" \ +"287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" \ +"1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" \ +"93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" \ +"36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD" \ +"F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831" \ +"179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B" \ +"DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF" \ +"5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6" \ +"D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3" \ +"23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" \ +"CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328" \ +"06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C" \ +"DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE" \ +"12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E4" \ +"38777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300" \ +"741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F568" \ +"3423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9" \ +"22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B" \ +"4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A" \ +"062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A36" \ +"4597E899A0255DC164F31CC50846851DF9AB48195DED7EA1" \ +"B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F92" \ +"4009438B481C6CD7889A002ED5EE382BC9190DA6FC026E47" \ +"9558E4475677E9AA9E3050E2765694DFC81F56E880B96E71" \ +"60C980DD98EDD3DFFFFFFFFFFFFFFFFF" + + +uint8_t genuineFMSKey[] = { + 0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20, + 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, 0x6c, + 0x61, 0x73, 0x68, 0x20, 0x4d, 0x65, 0x64, 0x69, + 0x61, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x20, 0x30, 0x30, 0x31, // Genuine Adobe Flash Media Server 001 + 0xf0, 0xee, 0xc2, 0x4a, 0x80, 0x68, 0xbe, 0xe8, + 0x2e, 0x00, 0xd0, 0xd1, 0x02, 0x9e, 0x7e, 0x57, + 0x6e, 0xec, 0x5d, 0x2d, 0x29, 0x80, 0x6f, 0xab, + 0x93, 0xb8, 0xe6, 0x36, 0xcf, 0xeb, 0x31, 0xae +}; // 68 + +uint8_t genuineFPKey[] = { + 0x47, 0x65, 0x6E, 0x75, 0x69, 0x6E, 0x65, 0x20, + 0x41, 0x64, 0x6F, 0x62, 0x65, 0x20, 0x46, 0x6C, + 0x61, 0x73, 0x68, 0x20, 0x50, 0x6C, 0x61, 0x79, + 0x65, 0x72, 0x20, 0x30, 0x30, 0x31, // Genuine Adobe Flash Player 001 + 0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, + 0x2E, 0x00, 0xD0, 0xD1, 0x02, 0x9E, 0x7E, 0x57, + 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB, + 0x93, 0xB8, 0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE +}; // 62 + + +void replace(std::string &target, std::string search, std::string replacement) { + if (search == replacement) + return; + if (search == "") + return; + std::string::size_type i = std::string::npos; + while ((i = target.find(search)) != std::string::npos) { + target.replace(i, search.length(), replacement); + } +} + + +DHWrapper::DHWrapper(int32_t bitsCount) { + _bitsCount = bitsCount; + _pDH = NULL; + _pSharedKey = NULL; + _sharedKeyLength = 0; + _peerPublickey = NULL; +} + +DHWrapper::~DHWrapper() { + Cleanup(); +} + +bool DHWrapper::Initialize() { + Cleanup(); + + //1. Create the DH + _pDH = DH_new(); + if (_pDH == NULL) { + Cleanup(); + return false; + } + + //2. Create his internal p and g + _pDH->p = BN_new(); + if (_pDH->p == NULL) { + Cleanup(); + return false; + } + _pDH->g = BN_new(); + if (_pDH->g == NULL) { + Cleanup(); + return false; + } + + //3. initialize p, g and key length + if (BN_hex2bn(&_pDH->p, P1024) == 0) { + Cleanup(); + return false; + } + if (BN_set_word(_pDH->g, 2) != 1) { + Cleanup(); + return false; + } + + //4. Set the key length + _pDH->length = _bitsCount; + + //5. Generate private and public key + if (DH_generate_key(_pDH) != 1) { + Cleanup(); + return false; + } + + return true; +} + +bool DHWrapper::CopyPublicKey(uint8_t *pDst, int32_t dstLength) { + if (_pDH == NULL) { + return false; + } + + return CopyKey(_pDH->pub_key, pDst, dstLength); +} + +bool DHWrapper::CopyPrivateKey(uint8_t *pDst, int32_t dstLength) { + if (_pDH == NULL) { + return false; + } + + return CopyKey(_pDH->priv_key, pDst, dstLength); +} + +bool DHWrapper::CreateSharedKey(uint8_t *pPeerPublicKey, int32_t length) { + if (_pDH == NULL) { + return false; + } + + if (_sharedKeyLength != 0 || _pSharedKey != NULL) { + return false; + } + + _sharedKeyLength = DH_size(_pDH); + if (_sharedKeyLength <= 0 || _sharedKeyLength > 1024) { + return false; + } + _pSharedKey = new uint8_t[_sharedKeyLength]; + + _peerPublickey = BN_bin2bn(pPeerPublicKey, length, 0); + if (_peerPublickey == NULL) { + return false; + } + + if (DH_compute_key(_pSharedKey, _peerPublickey, _pDH) != _sharedKeyLength) { + return false; + } + + return true; +} + +bool DHWrapper::CopySharedKey(uint8_t *pDst, int32_t dstLength) { + if (_pDH == NULL) { + return false; + } + + if (dstLength != _sharedKeyLength) { + return false; + } + + memcpy(pDst, _pSharedKey, _sharedKeyLength); + + return true; +} + +void DHWrapper::Cleanup() { + if (_pDH != NULL) { + if (_pDH->p != NULL) { + BN_free(_pDH->p); + _pDH->p = NULL; + } + if (_pDH->g != NULL) { + BN_free(_pDH->g); + _pDH->g = NULL; + } + DH_free(_pDH); + _pDH = NULL; + } + + if (_pSharedKey != NULL) { + delete[] _pSharedKey; + _pSharedKey = NULL; + } + _sharedKeyLength = 0; + + if (_peerPublickey != NULL) { + BN_free(_peerPublickey); + _peerPublickey = NULL; + } +} + +bool DHWrapper::CopyKey(BIGNUM *pNum, uint8_t *pDst, int32_t dstLength) { + int32_t keySize = BN_num_bytes(pNum); + if ((keySize <= 0) || (dstLength <= 0) || (keySize > dstLength)) { + return false; + } + + if (BN_bn2bin(pNum, pDst) != keySize) { + return false; + } + + return true; +} + +void InitRC4Encryption(uint8_t *secretKey, uint8_t *pubKeyIn, uint8_t *pubKeyOut, RC4_KEY *rc4keyIn, RC4_KEY *rc4keyOut) { + uint8_t digest[SHA256_DIGEST_LENGTH]; + unsigned int digestLen = 0; + + HMAC_CTX ctx; + HMAC_CTX_init(&ctx); + HMAC_Init_ex(&ctx, secretKey, 128, EVP_sha256(), 0); + HMAC_Update(&ctx, pubKeyIn, 128); + HMAC_Final(&ctx, digest, &digestLen); + HMAC_CTX_cleanup(&ctx); + + RC4_set_key(rc4keyOut, 16, digest); + + HMAC_CTX_init(&ctx); + HMAC_Init_ex(&ctx, secretKey, 128, EVP_sha256(), 0); + HMAC_Update(&ctx, pubKeyOut, 128); + HMAC_Final(&ctx, digest, &digestLen); + HMAC_CTX_cleanup(&ctx); + + RC4_set_key(rc4keyIn, 16, digest); +} + +std::string md5(std::string source, bool textResult) { + EVP_MD_CTX mdctx; + unsigned char md_value[EVP_MAX_MD_SIZE]; + unsigned int md_len; + + EVP_DigestInit(&mdctx, EVP_md5()); + EVP_DigestUpdate(&mdctx, STR(source), source.length()); + EVP_DigestFinal_ex(&mdctx, md_value, &md_len); + EVP_MD_CTX_cleanup(&mdctx); + + if (textResult) { + std::string result = ""; + char tmp[3]; + for (uint32_t i = 0; i < md_len; i++) { + sprintf(tmp, "%02x", md_value[i]); + result += tmp; + } + return result; + } else { + return std::string((char *) md_value, md_len); + } +} + +std::string b64(std::string source) { + return b64((uint8_t *) STR(source), source.size()); +} + +std::string b64(uint8_t *pBuffer, uint32_t length) { + BIO *bmem; + BIO *b64; + BUF_MEM *bptr; + + b64 = BIO_new(BIO_f_base64()); + bmem = BIO_new(BIO_s_mem()); + + b64 = BIO_push(b64, bmem); + BIO_write(b64, pBuffer, length); + std::string result = ""; + if (BIO_flush(b64) == 1) { + BIO_get_mem_ptr(b64, &bptr); + result = std::string(bptr->data, bptr->length); + } + + BIO_free_all(b64); + + + replace(result, "\n", ""); + replace(result, "\r", ""); + + return result; +} + +std::string unb64(std::string source) { + return unb64((uint8_t *)STR(source),source.length()); +} + +std::string unb64(uint8_t *pBuffer, uint32_t length){ + // create a memory buffer containing base64 encoded data + //BIO* bmem = BIO_new_mem_buf((void*) STR(source), source.length()); + BIO* bmem = BIO_new_mem_buf((void *)pBuffer, length); + + // push a Base64 filter so that reading from buffer decodes it + BIO *bioCmd = BIO_new(BIO_f_base64()); + // we don't want newlines + BIO_set_flags(bioCmd, BIO_FLAGS_BASE64_NO_NL); + bmem = BIO_push(bioCmd, bmem); + + char *pOut = new char[length]; + + int finalLen = BIO_read(bmem, (void*) pOut, length); + BIO_free_all(bmem); + std::string result(pOut, finalLen); + delete[] pOut; + return result; +} + +void HMACsha256(const void *pData, uint32_t dataLength, const void *pKey, uint32_t keyLength, void *pResult) { + unsigned int digestLen; + HMAC_CTX ctx; + HMAC_CTX_init(&ctx); + HMAC_Init_ex(&ctx, (unsigned char*) pKey, keyLength, EVP_sha256(), NULL); + HMAC_Update(&ctx, (unsigned char *) pData, dataLength); + HMAC_Final(&ctx, (unsigned char *) pResult, &digestLen); + HMAC_CTX_cleanup(&ctx); +} + +uint32_t GetDigestOffset0(uint8_t *pBuffer) { + uint32_t offset = pBuffer[8] + pBuffer[9] + pBuffer[10] + pBuffer[11]; + return (offset % 728) + 12; +} +uint32_t GetDigestOffset1(uint8_t *pBuffer) { + uint32_t offset = pBuffer[772] + pBuffer[773] + pBuffer[774] + pBuffer[775]; + return (offset % 728) + 776; +} +uint32_t GetDigestOffset(uint8_t *pBuffer, uint8_t scheme){ + if (scheme == 0){return GetDigestOffset0(pBuffer);}else{return GetDigestOffset1(pBuffer);} +} +uint32_t GetDHOffset0(uint8_t *pBuffer) { + uint32_t offset = pBuffer[1532] + pBuffer[1533] + pBuffer[1534] + pBuffer[1535]; + return (offset % 632) + 772; +} +uint32_t GetDHOffset1(uint8_t *pBuffer) { + uint32_t offset = pBuffer[768] + pBuffer[769] + pBuffer[770] + pBuffer[771]; + return (offset % 632) + 8; +} +uint32_t GetDHOffset(uint8_t *pBuffer, uint8_t scheme){ + if (scheme == 0){return GetDHOffset0(pBuffer);}else{return GetDHOffset1(pBuffer);} +} + + +bool ValidateClientScheme(uint8_t * pBuffer, uint8_t scheme) { + uint32_t clientDigestOffset = GetDigestOffset(pBuffer, scheme); + uint8_t *pTempBuffer = new uint8_t[1536 - 32]; + memcpy(pTempBuffer, pBuffer, clientDigestOffset); + memcpy(pTempBuffer + clientDigestOffset, pBuffer + clientDigestOffset + 32, 1536 - clientDigestOffset - 32); + uint8_t *pTempHash = new uint8_t[512]; + HMACsha256(pTempBuffer, 1536 - 32, genuineFPKey, 30, pTempHash); + bool result = (memcmp(pBuffer+clientDigestOffset, pTempHash, 32) == 0); + #if DEBUG >= 4 + fprintf(stderr, "Client scheme validation %hhi %s\n", scheme, result?"success":"failed"); + #endif + delete[] pTempBuffer; + delete[] pTempHash; + return result; +} diff --git a/util/crypto.h b/util/crypto.h new file mode 100644 index 00000000..f4daa4bb --- /dev/null +++ b/util/crypto.h @@ -0,0 +1,56 @@ +/// \file crypto.h +/// Holds all headers needed for RTMP cryptography functions. + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class DHWrapper { +private: + int32_t _bitsCount; + DH *_pDH; + uint8_t *_pSharedKey; + int32_t _sharedKeyLength; + BIGNUM *_peerPublickey; +public: + DHWrapper(int32_t bitsCount); + virtual ~DHWrapper(); + + bool Initialize(); + bool CopyPublicKey(uint8_t *pDst, int32_t dstLength); + bool CopyPrivateKey(uint8_t *pDst, int32_t dstLength); + bool CreateSharedKey(uint8_t *pPeerPublicKey, int32_t length); + bool CopySharedKey(uint8_t *pDst, int32_t dstLength); +private: + void Cleanup(); + bool CopyKey(BIGNUM *pNum, uint8_t *pDst, int32_t dstLength); +}; + + +void InitRC4Encryption(uint8_t *secretKey, uint8_t *pubKeyIn, uint8_t *pubKeyOut, RC4_KEY *rc4keyIn, RC4_KEY *rc4keyOut); +std::string md5(std::string source, bool textResult); +std::string b64(std::string source); +std::string b64(uint8_t *pBuffer, uint32_t length); +std::string unb64(std::string source); +std::string unb64(uint8_t *pBuffer, uint32_t length); + +void HMACsha256(const void *pData, uint32_t dataLength, const void *pKey, uint32_t keyLength, void *pResult); + +uint32_t GetDigestOffset0(uint8_t *pBuffer); +uint32_t GetDigestOffset1(uint8_t *pBuffer); +uint32_t GetDigestOffset(uint8_t *pBuffer, uint8_t scheme); +uint32_t GetDHOffset0(uint8_t *pBuffer); +uint32_t GetDHOffset1(uint8_t *pBuffer); +uint32_t GetDHOffset(uint8_t *pBuffer, uint8_t scheme); + +extern uint8_t genuineFMSKey[]; + +bool ValidateClientScheme(uint8_t * pBuffer, uint8_t scheme); diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index 76c2ee8a..e82ea8ae 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -1,3 +1,6 @@ +/// \file ddv_socket.cpp +/// Holds all code for the DDV namespace. + #include "ddv_socket.h" /// Create a new base socket. This is a basic constructor for converting any valid socket to a DDV::Socket. @@ -207,6 +210,21 @@ int DDV::Socket::iread(void * buffer, int len){ return r; }//DDV::Socket::iread +/// Read call that is compatible with std::string. +/// Data is read using iread (which is nonblocking if the DDV::Socket itself is), +/// then appended to end of buffer. +/// \param buffer std::string to append data to. +/// \return True if new data arrived, false otherwise. +bool DDV::Socket::read(std::string & buffer){ + char cbuffer[5000]; + int num = iread(cbuffer, 5000); + if (num > 0){ + buffer.append(cbuffer, num); + return true; + } + return false; +}//read + /// Create a new base ServerSocket. The socket is never connected, and a placeholder for later connections. DDV::ServerSocket::ServerSocket(){ sock = -1; diff --git a/util/ddv_socket.h b/util/ddv_socket.h index 298b5954..5d58e0bd 100644 --- a/util/ddv_socket.h +++ b/util/ddv_socket.h @@ -1,3 +1,6 @@ +/// \file ddv_socket.h +/// Holds all headers for the DDV namespace. + #pragma once #include #include @@ -33,6 +36,7 @@ namespace DDV{ bool write(const std::string data); ///< Write call that is compatible with std::string. int iwrite(void * buffer, int len); ///< Incremental write call. int iread(void * buffer, int len); ///< Incremental read call. + bool read(std::string & buffer); ///< Read call that is compatible with std::string. void close(); ///< Close connection. int getSocket(); ///< Returns internal socket number. }; diff --git a/util/flv_tag.cpp b/util/flv_tag.cpp index 70321b17..08fed05b 100644 --- a/util/flv_tag.cpp +++ b/util/flv_tag.cpp @@ -1,3 +1,6 @@ +/// \file flv_tag.cpp +/// Holds all code for the FLV namespace. + #include "flv_tag.h" #include //for Tag::FileLoader #include //for Tag::FileLoader @@ -8,6 +11,7 @@ char FLV::Header[13]; ///< Holds the last FLV header parsed. bool FLV::Parse_Error = false; ///< This variable is set to true if a problem is encountered while parsing the FLV. +std::string FLV::Error_Str = ""; /// Checks a FLV Header for validness. Returns true if the header is valid, false /// if the header is not. Not valid can mean: @@ -219,7 +223,7 @@ bool FLV::Tag::MemLoader(char * D, unsigned int S, unsigned int & P){ if (FLV::check_header(data)){ sofar = 0; memcpy(FLV::Header, data, 13); - }else{FLV::Parse_Error = true; return false;} + }else{FLV::Parse_Error = true; Error_Str = "Invalid header received."; return false;} } }else{ //if a tag header, calculate length and read tag body @@ -227,7 +231,7 @@ bool FLV::Tag::MemLoader(char * D, unsigned int S, unsigned int & P){ len += (data[2] << 8); len += (data[1] << 16); if (buf < len){data = (char*)realloc(data, len); buf = len;} - if (data[0] > 0x12){FLV::Parse_Error = true; return false;} + if (data[0] > 0x12){FLV::Parse_Error = true; Error_Str = "Invalid Tag received."; return false;} done = false; } } @@ -259,7 +263,7 @@ bool FLV::Tag::SockReadUntil(char * buffer, unsigned int count, unsigned int & s if (r < 0){ if (errno != EWOULDBLOCK){ FLV::Parse_Error = true; - fprintf(stderr, "ReadUntil fail: %s. All Hell Broke Loose!\n", strerror(errno)); + Error_Str = "Error reading from socket."; } return false; } @@ -267,7 +271,7 @@ bool FLV::Tag::SockReadUntil(char * buffer, unsigned int count, unsigned int & s if (sofar == count){return true;} if (sofar > count){ FLV::Parse_Error = true; - fprintf(stderr, "ReadUntil fail: %s. Read too much. All Hell Broke Loose!\n", strerror(errno)); + Error_Str = "Socket buffer overflow."; } return false; }//Tag::SockReadUntil @@ -289,7 +293,7 @@ bool FLV::Tag::SockLoader(DDV::Socket sock){ if (FLV::check_header(data)){ sofar = 0; memcpy(FLV::Header, data, 13); - }else{FLV::Parse_Error = true; return false;} + }else{FLV::Parse_Error = true; Error_Str = "Invalid header received."; return false;} } }else{ //if a tag header, calculate length and read tag body @@ -297,7 +301,7 @@ bool FLV::Tag::SockLoader(DDV::Socket sock){ len += (data[2] << 8); len += (data[1] << 16); if (buf < len){data = (char*)realloc(data, len); buf = len;} - if (data[0] > 0x12){FLV::Parse_Error = true; return false;} + if (data[0] > 0x12){FLV::Parse_Error = true; Error_Str = "Invalid Tag received."; return false;} done = false; } } @@ -335,7 +339,7 @@ bool FLV::Tag::FileReadUntil(char * buffer, unsigned int count, unsigned int & s if (sofar >= count){return true;} int r = 0; r = fread(buffer + sofar,1,count-sofar,f); - if (r < 0){FLV::Parse_Error = true; return false;} + if (r < 0){FLV::Parse_Error = true; Error_Str = "File reading error."; return false;} sofar += r; if (sofar >= count){return true;} return false; @@ -363,7 +367,7 @@ bool FLV::Tag::FileLoader(FILE * f){ if (FLV::check_header(data)){ sofar = 0; memcpy(FLV::Header, data, 13); - }else{FLV::Parse_Error = true;} + }else{FLV::Parse_Error = true; Error_Str = "Invalid header received."; return false;} } }else{ //if a tag header, calculate length and read tag body @@ -371,7 +375,7 @@ bool FLV::Tag::FileLoader(FILE * f){ len += (data[2] << 8); len += (data[1] << 16); if (buf < len){data = (char*)realloc(data, len); buf = len;} - if (data[0] > 0x12){FLV::Parse_Error = true;} + if (data[0] > 0x12){FLV::Parse_Error = true; Error_Str = "Invalid Tag received."; return false;} done = false; } } diff --git a/util/flv_tag.h b/util/flv_tag.h index d91cabb5..e824228c 100644 --- a/util/flv_tag.h +++ b/util/flv_tag.h @@ -1,3 +1,6 @@ +/// \file flv_tag.h +/// Holds all headers for the FLV namespace. + #pragma once #include "ddv_socket.h" #include @@ -7,7 +10,8 @@ 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". diff --git a/util/http_parser.cpp b/util/http_parser.cpp index dd01e9b2..60d370e7 100644 --- a/util/http_parser.cpp +++ b/util/http_parser.cpp @@ -1,8 +1,14 @@ -#include "http_parser.h" -#include "ddv_socket.h" +/// \file http_parser.cpp +/// Holds all code for the HTTP namespace. -HTTPReader::HTTPReader(){Clean();} -void HTTPReader::Clean(){ +#include "http_parser.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(){Clean();} + +/// Completely re-initializes the HTTP::Parser, leaving it ready for either reading or writing usage. +void HTTP::Parser::Clean(){ seenHeaders = false; seenReq = false; method = "GET"; @@ -11,11 +17,14 @@ void HTTPReader::Clean(){ body = ""; length = 0; HTTPbuffer = ""; - headers.erase(headers.begin(), headers.end()); - vars.erase(vars.begin(), vars.end()); + headers.clear(); + vars.clear(); } -bool HTTPReader::CleanForNext(){ +/// Re-initializes the HTTP::Parser, leaving the internal data buffer alone, then tries to parse a new request or response. +/// First does the same as HTTP::Parser::Clean(), but does not clear the internal data buffer. +/// This function then calls the HTTP::Parser::parse() function, and returns that functions return value. +bool HTTP::Parser::CleanForNext(){ seenHeaders = false; seenReq = false; method = "GET"; @@ -23,12 +32,19 @@ bool HTTPReader::CleanForNext(){ protocol = "HTTP/1.1"; body = ""; length = 0; - headers.erase(headers.begin(), headers.end()); - vars.erase(vars.begin(), vars.end()); + headers.clear(); + vars.clear(); return parse(); } -std::string HTTPReader::BuildRequest(){ +/// 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 the internal data buffer are used, +/// where the internal data buffer is used as the body of the request. +/// This means you cannot mix receiving and sending, because the body would get corrupted. +/// \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::iterator it; std::string tmp = method+" "+url+" "+protocol+"\n"; for (it=headers.begin(); it != headers.end(); it++){ @@ -39,7 +55,16 @@ std::string HTTPReader::BuildRequest(){ return tmp; } -std::string HTTPReader::BuildResponse(std::string code, std::string message){ +/// 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 the internal data buffer are used, +/// where the internal data buffer is used as the body of the response. +/// This means you cannot mix receiving and sending, because the body would get corrupted. +/// \param code The HTTP response code. Usually you want 200. +/// \param message The HTTP response message. Usually you want "OK". +/// \return A string containing a valid HTTP 1.0 or 1.1 response, ready for sending. +std::string HTTP::Parser::BuildResponse(std::string code, std::string message){ + /// \todo Include GET/POST variable parsing? std::map::iterator it; std::string tmp = protocol+" "+code+" "+message+"\n"; for (it=headers.begin(); it != headers.end(); it++){ @@ -50,47 +75,61 @@ std::string HTTPReader::BuildResponse(std::string code, std::string message){ return tmp; } -void HTTPReader::Trim(std::string & s){ +/// 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);} } -void HTTPReader::SetBody(std::string s){ +/// 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){ HTTPbuffer = s; SetHeader("Content-Length", s.length()); } -void HTTPReader::SetBody(char * buffer, int len){ +/// 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){ HTTPbuffer = ""; HTTPbuffer.append(buffer, len); SetHeader("Content-Length", len); } +/// Returns header i, if set. +std::string HTTP::Parser::GetHeader(std::string i){return headers[i];} +/// Returns POST variable i, if set. +std::string HTTP::Parser::GetVar(std::string i){return vars[i];} -std::string HTTPReader::GetHeader(std::string i){return headers[i];} -std::string HTTPReader::GetVar(std::string i){return vars[i];} - -void HTTPReader::SetHeader(std::string i, std::string v){ +/// Sets header i to string value v. +void HTTP::Parser::SetHeader(std::string i, std::string v){ Trim(i); Trim(v); headers[i] = v; } -void HTTPReader::SetHeader(std::string i, int v){ +/// Sets header i to integer value v. +void HTTP::Parser::SetHeader(std::string i, int v){ Trim(i); char val[128]; sprintf(val, "%i", v); headers[i] = val; } -void HTTPReader::SetVar(std::string i, std::string v){ +/// Sets POST variable i to string value v. +void HTTP::Parser::SetVar(std::string i, std::string v){ Trim(i); Trim(v); vars[i] = v; } -bool HTTPReader::Read(DDV::Socket & sock){ +/// Attempt to read a whole HTTP request or response from DDV::Socket sock. +/// \return True of a whole request or response was read, false otherwise. +bool HTTP::Parser::Read(DDV::Socket & sock){ //returned true als hele http packet gelezen is int r = 0; int b = 0; @@ -111,7 +150,9 @@ bool HTTPReader::Read(DDV::Socket & sock){ return false; }//HTTPReader::ReadSocket -bool HTTPReader::Read(FILE * F){ +/// Reads a full set of HTTP responses/requests from file F. +/// \return Always false. Use HTTP::Parser::CleanForNext() to parse the contents of the file. +bool HTTP::Parser::Read(FILE * F){ //returned true als hele http packet gelezen is int b = 1; char buffer[500]; @@ -122,7 +163,11 @@ bool HTTPReader::Read(FILE * F){ return false; }//HTTPReader::ReadSocket -bool HTTPReader::parse(){ +/// Attempt to read a whole HTTP response or request from the internal data buffer. +/// If succesful, fills its own fields with the proper data and removes the response/request +/// from the internal data buffer. +/// \return True on success, false otherwise. +bool HTTP::Parser::parse(){ size_t f; std::string tmpA, tmpB, tmpC; while (HTTPbuffer != ""){ @@ -140,7 +185,7 @@ bool HTTPReader::parse(){ if (f != std::string::npos){url = tmpA.substr(0, f); tmpA.erase(0, f+1);} f = tmpA.find(' '); if (f != std::string::npos){protocol = tmpA.substr(0, f); tmpA.erase(0, f+1);} - //TODO: GET variable parsing? + /// \todo Include GET variable parsing? }else{ if (tmpA.size() == 0){ seenHeaders = true; @@ -156,7 +201,7 @@ bool HTTPReader::parse(){ } if (seenHeaders){ if (length > 0){ - //TODO: POST variable parsing? + /// \todo Include POST variable parsing? if (HTTPbuffer.length() >= length){ body = HTTPbuffer.substr(0, length); HTTPbuffer.erase(0, length); @@ -172,23 +217,40 @@ bool HTTPReader::parse(){ return false; //we should never get here... }//HTTPReader::parse -void HTTPReader::SendResponse(DDV::Socket & conn, std::string code, std::string message){ +/// Sends data as response to conn. +/// The response is automatically first build using HTTP::Parser::BuildResponse(). +/// \param conn The DDV::Socket to send the response over. +/// \param code The HTTP response code. Usually you want 200. +/// \param message The HTTP response message. Usually you want "OK". +void HTTP::Parser::SendResponse(DDV::Socket & conn, std::string code, std::string message){ std::string tmp = BuildResponse(code, message); conn.write(tmp); } -void HTTPReader::SendBodyPart(DDV::Socket & conn, char * buffer, int len){ +/// Sends data as HTTP/1.1 bodypart to conn. +/// HTTP/1.1 chunked encoding is automatically applied if needed. +/// \param conn The DDV::Socket to send the part over. +/// \param buffer The buffer to send. +/// \param len The length of the buffer. +void HTTP::Parser::SendBodyPart(DDV::Socket & conn, char * buffer, int len){ std::string tmp; tmp.append(buffer, len); SendBodyPart(conn, tmp); } -void HTTPReader::SendBodyPart(DDV::Socket & conn, std::string bodypart){ - static char len[10]; - int sizelen; - sizelen = snprintf(len, 10, "%x\r\n", (unsigned int)bodypart.size()); - conn.write(len, sizelen); - conn.write(bodypart); - conn.write(len+sizelen-2, 2); +/// Sends data as HTTP/1.1 bodypart to conn. +/// HTTP/1.1 chunked encoding is automatically applied if needed. +/// \param conn The DDV::Socket to send the part over. +/// \param bodypart The data to send. +void HTTP::Parser::SendBodyPart(DDV::Socket & conn, std::string bodypart){ + if (protocol == "HTTP/1.1"){ + static char len[10]; + int sizelen; + sizelen = snprintf(len, 10, "%x\r\n", (unsigned int)bodypart.size()); + conn.write(len, sizelen); + conn.write(bodypart); + conn.write(len+sizelen-2, 2); + }else{ + conn.write(bodypart); + } } - diff --git a/util/http_parser.h b/util/http_parser.h index 2741f0c4..d08cadef 100644 --- a/util/http_parser.h +++ b/util/http_parser.h @@ -1,3 +1,6 @@ +/// \file http_parser.h +/// Holds all headers for the HTTP namespace. + #pragma once #include #include @@ -5,37 +8,40 @@ #include #include "ddv_socket.h" -class HTTPReader{ - public: - HTTPReader(); - bool Read(DDV::Socket & sock); - bool Read(FILE * F); - std::string GetHeader(std::string i); - std::string GetVar(std::string i); - void SetHeader(std::string i, std::string v); - void SetHeader(std::string i, int v); - void SetVar(std::string i, std::string v); - void SetBody(std::string s); - void SetBody(char * buffer, int len); - std::string BuildRequest(); - std::string BuildResponse(std::string code, std::string message); - void SendResponse(DDV::Socket & conn, std::string code, std::string message); - void SendBodyPart(DDV::Socket & conn, char * buffer, int len); - void SendBodyPart(DDV::Socket & conn, std::string bodypart); - void Clean(); - bool CleanForNext(); - std::string body; - std::string method; - std::string url; - std::string protocol; - unsigned int length; - private: - bool seenHeaders; - bool seenReq; - bool parse(); - std::string HTTPbuffer; - std::map headers; - std::map vars; - void Trim(std::string & s); -};//HTTPReader - +/// 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(DDV::Socket & sock); + bool Read(FILE * F); + std::string GetHeader(std::string i); + std::string GetVar(std::string i); + void SetHeader(std::string i, std::string v); + void SetHeader(std::string i, int v); + void SetVar(std::string i, std::string v); + void SetBody(std::string s); + void SetBody(char * buffer, int len); + std::string BuildRequest(); + std::string BuildResponse(std::string code, std::string message); + void SendResponse(DDV::Socket & conn, std::string code, std::string message); + void SendBodyPart(DDV::Socket & conn, char * buffer, int len); + void SendBodyPart(DDV::Socket & conn, std::string bodypart); + void Clean(); + bool CleanForNext(); + std::string body; + std::string method; + std::string url; + std::string protocol; + unsigned int length; + private: + bool seenHeaders; + bool seenReq; + bool parse(); + std::string HTTPbuffer; + std::map headers; + std::map vars; + void Trim(std::string & s); + };//HTTP::Parser class +};//HTTP namespace diff --git a/util/rtmpchunks.cpp b/util/rtmpchunks.cpp new file mode 100644 index 00000000..d297b5e5 --- /dev/null +++ b/util/rtmpchunks.cpp @@ -0,0 +1,442 @@ +/// \file rtmpchunks.cpp +/// Holds all code for the RTMPStream namespace. + +#include "rtmpchunks.h" +#include "crypto.h" + +char versionstring[] = "WWW.DDVTECH.COM "; ///< String that is repeated in the RTMP handshake +std::string RTMPStream::handshake_in; ///< Input for the handshake. +std::string RTMPStream::handshake_out;///< Output for the handshake. + +/// Gets the current system time in milliseconds. +unsigned int RTMPStream::getNowMS(){ + timeval t; + gettimeofday(&t, 0); + return t.tv_sec + t.tv_usec/1000; +}//RTMPStream::getNowMS + + +unsigned int RTMPStream::chunk_rec_max = 128; +unsigned int RTMPStream::chunk_snd_max = 128; +unsigned int RTMPStream::rec_window_size = 0xFA00; +unsigned int RTMPStream::snd_window_size = 1024*500; +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; +unsigned int RTMPStream::firsttime; + +/// Holds the last sent chunk for every msg_id. +std::map RTMPStream::Chunk::lastsend; +/// Holds the last received chunk for every msg_id. +std::map RTMPStream::Chunk::lastrecv; + +/// Packs up the chunk for sending over the network. +/// \warning Do not call if you are not actually sending the resulting data! +/// \returns A std::string ready to be sent. +std::string RTMPStream::Chunk::Pack(){ + std::string output = ""; + RTMPStream::Chunk prev = lastsend[cs_id]; + unsigned int tmpi; + unsigned char chtype = 0x00; + timestamp -= firsttime; + if (prev.cs_id == cs_id){ + if (msg_stream_id == prev.msg_stream_id){ + chtype = 0x40;//do not send msg_stream_id + if (len == prev.len){ + if (msg_type_id == prev.msg_type_id){ + chtype = 0x80;//do not send len and msg_type_id + if (timestamp == prev.timestamp){ + chtype = 0xC0;//do not send timestamp + } + } + } + } + } + if (cs_id <= 63){ + output += (unsigned char)(chtype | cs_id); + }else{ + if (cs_id <= 255+64){ + output += (unsigned char)(chtype | 0); + output += (unsigned char)(cs_id - 64); + }else{ + output += (unsigned char)(chtype | 1); + output += (unsigned char)((cs_id - 64) % 256); + output += (unsigned char)((cs_id - 64) / 256); + } + } + unsigned int ntime = 0; + if (chtype != 0xC0){ + //timestamp or timestamp diff + if (chtype == 0x00){ + tmpi = timestamp; + }else{ + tmpi = timestamp - prev.timestamp; + } + if (tmpi >= 0x00ffffff){ntime = tmpi; tmpi = 0x00ffffff;} + output += (unsigned char)(tmpi / (256*256)); + output += (unsigned char)(tmpi / 256); + output += (unsigned char)(tmpi % 256); + if (chtype != 0x80){ + //len + tmpi = len; + output += (unsigned char)(tmpi / (256*256)); + output += (unsigned char)(tmpi / 256); + output += (unsigned char)(tmpi % 256); + //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 % 256); + output += (unsigned char)(ntime / 256); + output += (unsigned char)(ntime / (256*256)); + output += (unsigned char)(ntime / (256*256*256)); + } + len_left = 0; + while (len_left < len){ + tmpi = len - len_left; + if (tmpi > RTMPStream::chunk_snd_max){tmpi = RTMPStream::chunk_snd_max;} + output.append(data, len_left, tmpi); + len_left += tmpi; + if (len_left < len){ + if (cs_id <= 63){ + output += (unsigned char)(0xC0 + cs_id); + }else{ + if (cs_id <= 255+64){ + output += (unsigned char)(0xC0); + output += (unsigned char)(cs_id - 64); + }else{ + output += (unsigned char)(0xC1); + output += (unsigned char)((cs_id - 64) % 256); + output += (unsigned char)((cs_id - 64) / 256); + } + } + } + } + lastsend[cs_id] = *this; + RTMPStream::snd_cnt += output.size(); + return output; +}//SendChunk + +/// Default contructor, creates an empty chunk with all values initialized to zero. +RTMPStream::Chunk::Chunk(){ + cs_id = 0; + timestamp = 0; + len = 0; + real_len = 0; + len_left = 0; + msg_type_id = 0; + msg_stream_id = 0; + data = ""; +}//constructor + +/// Packs up a chunk with the given arguments as properties. +std::string RTMPStream::SendChunk(unsigned int cs_id, unsigned char msg_type_id, unsigned int msg_stream_id, std::string data){ + RTMPStream::Chunk ch; + ch.cs_id = cs_id; + ch.timestamp = RTMPStream::getNowMS(); + ch.len = data.size(); + ch.real_len = data.size(); + ch.len_left = 0; + ch.msg_type_id = msg_type_id; + ch.msg_stream_id = msg_stream_id; + ch.data = data; + return ch.Pack(); +}//constructor + +/// Packs up a chunk with media contents. +/// \param msg_type_id Type number of the media, as per FLV standard. +/// \param data Contents of the media data. +/// \param len Length of the media data, in bytes. +/// \param ts Timestamp of the media data, relative to current system time. +std::string RTMPStream::SendMedia(unsigned char msg_type_id, unsigned char * data, int len, unsigned int ts){ + RTMPStream::Chunk ch; + ch.cs_id = msg_type_id; + ch.timestamp = ts; + ch.len = len; + ch.real_len = len; + ch.len_left = 0; + ch.msg_type_id = msg_type_id; + ch.msg_stream_id = 1; + ch.data.append((char*)data, (size_t)len); + return ch.Pack(); +}//SendMedia + +/// Packs up a chunk for a control message with 1 argument. +std::string RTMPStream::SendCTL(unsigned char type, unsigned int data){ + RTMPStream::Chunk ch; + ch.cs_id = 2; + ch.timestamp = RTMPStream::getNowMS(); + ch.len = 4; + ch.real_len = 4; + ch.len_left = 0; + ch.msg_type_id = type; + ch.msg_stream_id = 0; + ch.data.resize(4); + *(int*)((char*)ch.data.c_str()) = htonl(data); + return ch.Pack(); +}//SendCTL + +/// Packs up a chunk for a control message with 2 arguments. +std::string RTMPStream::SendCTL(unsigned char type, unsigned int data, unsigned char data2){ + RTMPStream::Chunk ch; + ch.cs_id = 2; + ch.timestamp = RTMPStream::getNowMS(); + ch.len = 5; + ch.real_len = 5; + ch.len_left = 0; + ch.msg_type_id = type; + ch.msg_stream_id = 0; + ch.data.resize(5); + *(int*)((char*)ch.data.c_str()) = htonl(data); + ch.data[4] = data2; + return ch.Pack(); +}//SendCTL + +/// Packs up a chunk for a user control message with 1 argument. +std::string RTMPStream::SendUSR(unsigned char type, unsigned int data){ + RTMPStream::Chunk ch; + ch.cs_id = 2; + ch.timestamp = RTMPStream::getNowMS(); + ch.len = 6; + ch.real_len = 6; + ch.len_left = 0; + ch.msg_type_id = 4; + ch.msg_stream_id = 0; + ch.data.resize(6); + *(int*)((char*)ch.data.c_str()+2) = htonl(data); + ch.data[0] = 0; + ch.data[1] = type; + return ch.Pack(); +}//SendUSR + +/// Packs up a chunk for a user control message with 2 arguments. +std::string RTMPStream::SendUSR(unsigned char type, unsigned int data, unsigned int data2){ + RTMPStream::Chunk ch; + ch.cs_id = 2; + ch.timestamp = RTMPStream::getNowMS(); + ch.len = 10; + ch.real_len = 10; + ch.len_left = 0; + ch.msg_type_id = 4; + ch.msg_stream_id = 0; + ch.data.resize(10); + *(int*)((char*)ch.data.c_str()+2) = htonl(data); + *(int*)((char*)ch.data.c_str()+6) = htonl(data2); + ch.data[0] = 0; + ch.data[1] = type; + return ch.Pack(); +}//SendUSR + + +/// Parses the argument string into the current chunk. +/// Tries to read a whole chunk, if successful it will remove +/// the corresponding data from the input string. +/// \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() < 3) return false;//need at least 3 bytes to continue + + 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 + switch (chunktype & 0xC0){ + case 0x00: + if (indata.size() < i+11) return false; //can't read whole header + timestamp = indata[i++]*256*256; + timestamp += indata[i++]*256; + timestamp += indata[i++]; + len = indata[i++]*256*256; + len += indata[i++]*256; + len += indata[i++]; + len_left = 0; + msg_type_id = indata[i++]; + msg_stream_id = indata[i++]; + msg_stream_id += indata[i++]*256; + msg_stream_id += indata[i++]*256*256; + msg_stream_id += indata[i++]*256*256*256; + break; + case 0x40: + if (indata.size() < i+7) return false; //can't read whole header + timestamp = indata[i++]*256*256; + timestamp += indata[i++]*256; + timestamp += indata[i++]; + timestamp += prev.timestamp; + len = indata[i++]*256*256; + len += indata[i++]*256; + len += indata[i++]; + len_left = 0; + msg_type_id = indata[i++]; + msg_stream_id = prev.msg_stream_id; + break; + case 0x80: + if (indata.size() < i+3) return false; //can't read whole header + timestamp = indata[i++]*256*256; + timestamp += indata[i++]*256; + timestamp += indata[i++]; + timestamp += prev.timestamp; + len = prev.len; + len_left = prev.len_left; + msg_type_id = prev.msg_type_id; + msg_stream_id = prev.msg_stream_id; + break; + case 0xC0: + timestamp = prev.timestamp; + len = prev.len; + len_left = prev.len_left; + msg_type_id = prev.msg_type_id; + msg_stream_id = prev.msg_stream_id; + break; + } + //calculate chunk length, real length, and length left till complete + if (len_left > 0){ + real_len = len_left; + len_left -= real_len; + }else{ + real_len = len; + } + if (real_len > RTMPStream::chunk_rec_max){ + len_left += real_len - RTMPStream::chunk_rec_max; + real_len = RTMPStream::chunk_rec_max; + } + //read extended timestamp, if neccesary + if (timestamp == 0x00ffffff){ + if (indata.size() < i+4) return false; //can't read whole header + timestamp = indata[i++]*256*256*256; + timestamp += indata[i++]*256*256; + timestamp += indata[i++]*256; + timestamp += indata[i++]; + } + + //read data if length > 0, and allocate it + if (real_len > 0){ + if (prev.len_left > 0){ + data = prev.data; + }else{ + data = ""; + } + if (indata.size() < i+real_len) return false;//can't read all data (yet) + data.append(indata, i, real_len); + indata = indata.substr(i+real_len); + lastrecv[cs_id] = *this; + RTMPStream::rec_cnt += i+real_len; + if (len_left == 0){ + return true; + }else{ + return false; + } + }else{ + data = ""; + indata = indata.substr(i+real_len); + lastrecv[cs_id] = *this; + RTMPStream::rec_cnt += i+real_len; + return true; + } +}//Parse + + +/// Does the handshake. Expects handshake_in to be filled, and fills handshake_out. +/// After calling this function, don't forget to read and ignore 1536 extra bytes, +/// this is the handshake response and not interesting for us because we don't do client +/// verification. +bool RTMPStream::doHandshake(){ + char Version; + //Read C0 + Version = RTMPStream::handshake_in[0]; + uint8_t * Client = (uint8_t *)RTMPStream::handshake_in.c_str() + 1; + RTMPStream::handshake_out.resize(3073); + uint8_t * Server = (uint8_t *)RTMPStream::handshake_out.c_str() + 1; + RTMPStream::rec_cnt += 1537; + + //Build S1 Packet + *((uint32_t*)Server) = 0;//time zero + *(((uint32_t*)(Server+4))) = htonl(0x01020304);//version 1 2 3 4 + for (int i = 8; i < 3072; ++i){Server[i] = versionstring[i%16];}//"random" data + + bool encrypted = (Version == 6); + #if DEBUG >= 4 + fprintf(stderr, "Handshake version is %hhi\n", Version); + #endif + uint8_t _validationScheme = 5; + if (ValidateClientScheme(Client, 0)) _validationScheme = 0; + if (ValidateClientScheme(Client, 1)) _validationScheme = 1; + + #if DEBUG >= 4 + fprintf(stderr, "Handshake type is %hhi, encryption is %s\n", _validationScheme, encrypted?"on":"off"); + #endif + + //FIRST 1536 bytes from server response + //compute DH key position + uint32_t serverDHOffset = GetDHOffset(Server, _validationScheme); + uint32_t clientDHOffset = GetDHOffset(Client, _validationScheme); + + //generate DH key + DHWrapper dhWrapper(1024); + if (!dhWrapper.Initialize()) return false; + if (!dhWrapper.CreateSharedKey(Client + clientDHOffset, 128)) return false; + if (!dhWrapper.CopyPublicKey(Server + serverDHOffset, 128)) return false; + + if (encrypted) { + uint8_t secretKey[128]; + if (!dhWrapper.CopySharedKey(secretKey, sizeof (secretKey))) return false; + RC4_KEY _pKeyIn; + RC4_KEY _pKeyOut; + InitRC4Encryption(secretKey, (uint8_t*) & Client[clientDHOffset], (uint8_t*) & Server[serverDHOffset], &_pKeyIn, &_pKeyOut); + uint8_t data[1536]; + RC4(&_pKeyIn, 1536, data, data); + RC4(&_pKeyOut, 1536, data, data); + } + //generate the digest + uint32_t serverDigestOffset = GetDigestOffset(Server, _validationScheme); + uint8_t *pTempBuffer = new uint8_t[1536 - 32]; + memcpy(pTempBuffer, Server, serverDigestOffset); + memcpy(pTempBuffer + serverDigestOffset, Server + serverDigestOffset + 32, 1536 - serverDigestOffset - 32); + uint8_t *pTempHash = new uint8_t[512]; + HMACsha256(pTempBuffer, 1536 - 32, genuineFMSKey, 36, pTempHash); + memcpy(Server + serverDigestOffset, pTempHash, 32); + delete[] pTempBuffer; + delete[] pTempHash; + + //SECOND 1536 bytes from server response + uint32_t keyChallengeIndex = GetDigestOffset(Client, _validationScheme); + pTempHash = new uint8_t[512]; + HMACsha256(Client + keyChallengeIndex, 32, genuineFMSKey, 68, pTempHash); + uint8_t *pLastHash = new uint8_t[512]; + HMACsha256(Server + 1536, 1536 - 32, pTempHash, 32, pLastHash); + memcpy(Server + 1536 * 2 - 32, pLastHash, 32); + delete[] pTempHash; + delete[] pLastHash; + //DONE BUILDING THE RESPONSE ***// + Server[-1] = Version; + RTMPStream::snd_cnt += 3073; + return true; +} diff --git a/util/rtmpchunks.h b/util/rtmpchunks.h new file mode 100644 index 00000000..b11142c8 --- /dev/null +++ b/util/rtmpchunks.h @@ -0,0 +1,65 @@ +/// \file rtmpchunks.h +/// Holds all headers for the RTMPStream namespace. + +#pragma once +#include +#include +#include +#include +#include +#include +#define DEBUG 4 + +/// Contains all functions and classes needed for RTMP connections. +namespace RTMPStream{ + + /// Gets the current system time in milliseconds. + unsigned int getNowMS(); + + extern unsigned int chunk_rec_max; ///< Maximum size for a received chunk. + extern unsigned int chunk_snd_max; ///< Maximum size for a sent chunk. + extern unsigned int rec_window_size; ///< Window size for receiving. + extern unsigned int snd_window_size; ///< Window size for sending. + extern unsigned int rec_window_at; ///< Current position of the receiving window. + extern unsigned int snd_window_at; ///< Current position of the sending window. + extern unsigned int rec_cnt; ///< Counter for total data received, in bytes. + extern unsigned int snd_cnt; ///< Counter for total data sent, in bytes. + + extern timeval lastrec; ///< Timestamp of last time data was received. + extern unsigned int firsttime; ///< Timestamp of first time a chunk was sent. + + /// Holds a single RTMP chunk, either send or receive direction. + class Chunk{ + public: + unsigned int cs_id; ///< ContentStream ID + unsigned int timestamp; ///< Timestamp of this chunk. + unsigned int len; ///< Length of the complete chunk. + unsigned int real_len; ///< Length of this particular part of it. + unsigned int len_left; ///< Length not yet received, out of complete chunk. + unsigned char msg_type_id; ///< Message Type ID + unsigned int msg_stream_id; ///< Message Stream ID + std::string data; ///< Payload of chunk. + + Chunk(); + bool Parse(std::string & data); + std::string Pack(); + + private: + static std::map lastsend; + static std::map lastrecv; + };//RTMPStream::Chunk + + std::string SendChunk(unsigned int cs_id, unsigned char msg_type_id, unsigned int msg_stream_id, std::string data); + std::string SendMedia(unsigned char msg_type_id, unsigned char * data, int len, unsigned int ts); + std::string SendCTL(unsigned char type, unsigned int data); + std::string SendCTL(unsigned char type, unsigned int data, unsigned char data2); + std::string SendUSR(unsigned char type, unsigned int data); + std::string SendUSR(unsigned char type, unsigned int data, unsigned int data2); + + /// This value should be set to the first 1537 bytes received. + extern std::string handshake_in; + /// This value is the handshake response that is to be sent out. + extern std::string handshake_out; + /// Does the handshake. Expects handshake_in to be filled, and fills handshake_out. + bool doHandshake(); +};//RTMPStream namespace diff --git a/util/server_setup.cpp b/util/server_setup.cpp index daa9e6c6..36e09c47 100644 --- a/util/server_setup.cpp +++ b/util/server_setup.cpp @@ -1,12 +1,34 @@ -#include +/// \file server_setup.cpp +/// Contains generic functions for setting up a DDVTECH Connector. + +#ifndef MAINHANDLER + /// Handler that is called for accepted incoming connections. + #define MAINHANDLER NoHandler + #error "No handler was set!" +#endif + + +#ifndef DEFAULT_PORT + /// Default port for this server. + #define DEFAULT_PORT 0 + #error "No default port was set!" +#endif + + +#ifndef CONFIGSECT + /// Configuration file section for this server. + #define CONFIGSECT None + #error "No configuration file section was set!" +#endif + #include "ddv_socket.h" //DDVTech Socket wrapper -#include "flv_tag.h" //FLV parsing with DDVTech Socket wrapper +#include #include #include #include -#define defstr(x) #x //converts a define name to string -#define defstrh(x) "[" defstr(x) "]" //converts define name to [string] -DDV::ServerSocket server_socket(-1); +#define defstr(x) #x ///< converts a define name to string +#define defstrh(x) "[" defstr(x) "]" ///< converts define name to [string] +DDV::ServerSocket server_socket(-1); ///< Placeholder for the server socket /// Basic signal handler. Disconnects the server_socket if it receives /// a SIGINT, SIGHUP or SIGTERM signal, but does nothing for SIGPIPE. @@ -26,10 +48,10 @@ void signal_handler (int signum){ /// Generic main entry point and loop for DDV Connectors. /// This sets up the proper termination handler, checks commandline options, /// parses config files and opens a listening socket on the requested port. -/// Any incoming connections will be accepted and start up the function MAINHANDLER, -/// which should be #defined before including server_setup.cpp. -/// The default port is set by #define DEFAULT_PORT. -/// The configuration file section is set by #define CONFIGSECT. +/// Any incoming connections will be accepted and start up the function #MAINHANDLER, +/// which should be defined before including server_setup.cpp. +/// The default port is set by define #DEFAULT_PORT. +/// The configuration file section is set by define #CONFIGSECT. int main(int argc, char ** argv){ DDV::Socket S;//placeholder for incoming connections From 4bdb7eb4e43d7171ea2c34dbed348f053e2ef2ec Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sat, 16 Apr 2011 03:44:48 +0200 Subject: [PATCH 119/788] *evil scientist laughter* ITS ALIVE! ITS ALIVE! --- util/ddv_socket.cpp | 6 ++++-- util/flv_tag.cpp | 31 +++++++++++++++---------------- util/flv_tag.h | 2 ++ 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index e82ea8ae..3b3567ac 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -121,8 +121,9 @@ bool DDV::Socket::write(const void * buffer, int len){ return false; break; } + }else{ + sofar += r; } - sofar += r; } return true; }//DDv::Socket::write @@ -151,8 +152,9 @@ bool DDV::Socket::read(void * buffer, int len){ return false; break; } + }else{ + sofar += r; } - sofar += r; } return true; }//DDV::Socket::read diff --git a/util/flv_tag.cpp b/util/flv_tag.cpp index 08fed05b..02494b56 100644 --- a/util/flv_tag.cpp +++ b/util/flv_tag.cpp @@ -143,7 +143,7 @@ void FLV::Tag::tagTime(unsigned int T){ /// The buffer length is initialized to 0, and later automatically /// increased if neccesary. FLV::Tag::Tag(){ - len = 0; buf = 0; data = 0; isKeyframe = false; + len = 0; buf = 0; data = 0; isKeyframe = false; done = true; sofar = 0; }//empty constructor /// Copy constructor, copies the contents of an existing tag. @@ -151,6 +151,8 @@ FLV::Tag::Tag(){ /// that is being copied, and later automaticallt increased if /// neccesary. FLV::Tag::Tag(const Tag& O){ + done = true; + sofar = 0; buf = O.len; len = buf; if (len > 0){ @@ -166,14 +168,18 @@ FLV::Tag::Tag(const Tag& O){ /// This operator checks for self-assignment. FLV::Tag & FLV::Tag::operator= (const FLV::Tag& O){ if (this != &O){//no self-assignment - if (data != 0){free(data);} - buf = O.len; - len = buf; + len = O.len; if (len > 0){ - data = (char*)malloc(len); + if (!data){ + data = (char*)malloc(len); + buf = len; + }else{ + if (buf < len){ + data = (char*)realloc(data, len); + buf = len; + } + } memcpy(data, O.data, len); - }else{ - data = 0; } isKeyframe = O.isKeyframe; } @@ -211,8 +217,6 @@ bool FLV::Tag::MemReadUntil(char * buffer, unsigned int count, unsigned int & so /// \param P The current position in the data buffer. Will be updated to reflect new position. /// \return True if a whole tag is succesfully read, false otherwise. bool FLV::Tag::MemLoader(char * D, unsigned int S, unsigned int & P){ - static bool done = true; - static unsigned int sofar = 0; if (buf < 15){data = (char*)realloc(data, 15); buf = 15;} if (done){ //read a header @@ -259,15 +263,14 @@ bool FLV::Tag::MemLoader(char * D, unsigned int S, unsigned int & P){ /// \return True if count bytes are read succesfully, false otherwise. bool FLV::Tag::SockReadUntil(char * buffer, unsigned int count, unsigned int & sofar, DDV::Socket & sock){ if (sofar == count){return true;} - int r = sock.read(buffer + sofar,count-sofar); - if (r < 0){ + if (!sock.read(buffer + sofar,count-sofar)){ if (errno != EWOULDBLOCK){ FLV::Parse_Error = true; Error_Str = "Error reading from socket."; } return false; } - sofar += r; + sofar += count-sofar; if (sofar == count){return true;} if (sofar > count){ FLV::Parse_Error = true; @@ -282,8 +285,6 @@ bool FLV::Tag::SockReadUntil(char * buffer, unsigned int count, unsigned int & s /// \param sock The socket to read from. /// \return True if a whole tag is succesfully read, false otherwise. bool FLV::Tag::SockLoader(DDV::Socket sock){ - static bool done = true; - static unsigned int sofar = 0; if (buf < 15){data = (char*)realloc(data, 15); buf = 15;} if (done){ if (SockReadUntil(data, 11, sofar, sock)){ @@ -354,8 +355,6 @@ bool FLV::Tag::FileLoader(FILE * f){ int preflags = fcntl(fileno(f), F_GETFL, 0); int postflags = preflags | O_NONBLOCK; fcntl(fileno(f), F_SETFL, postflags); - static bool done = true; - static unsigned int sofar = 0; if (buf < 15){data = (char*)realloc(data, 15); buf = 15;} if (done){ diff --git a/util/flv_tag.h b/util/flv_tag.h index e824228c..0ba13ea5 100644 --- a/util/flv_tag.h +++ b/util/flv_tag.h @@ -35,6 +35,8 @@ namespace FLV { 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? //loader helper functions bool MemReadUntil(char * buffer, unsigned int count, unsigned int & sofar, char * D, unsigned int S, unsigned int & P); bool SockReadUntil(char * buffer, unsigned int count, unsigned int & sofar, DDV::Socket & sock); From 4f36bc0aa9360d330a27aa5ae72115a3afc57a82 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 17 Apr 2011 00:38:04 +0200 Subject: [PATCH 120/788] RTMP edits --- util/rtmpchunks.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/util/rtmpchunks.cpp b/util/rtmpchunks.cpp index eca2f9fb..a63a2380 100644 --- a/util/rtmpchunks.cpp +++ b/util/rtmpchunks.cpp @@ -18,8 +18,8 @@ unsigned int RTMPStream::getNowMS(){ unsigned int RTMPStream::chunk_rec_max = 128; unsigned int RTMPStream::chunk_snd_max = 128; -unsigned int RTMPStream::rec_window_size = 0xFA00; -unsigned int RTMPStream::snd_window_size = 1024*500; +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; @@ -243,6 +243,9 @@ std::string RTMPStream::SendUSR(unsigned char type, unsigned int data, unsigned /// Parses the argument string into the current chunk. /// Tries to read a whole chunk, if successful it will remove /// the corresponding data from the input string. +/// If only part of a chunk is read, it will remove the part and call itself again. +/// This has the effect of only causing a "true" reponse in the case a *whole* chunk +/// is read, not just part of a chunk. /// \param indata The input string to parse and update. /// \warning This function will destroy the current data in this chunk! /// \returns True if a whole chunk could be read, false otherwise. @@ -368,7 +371,7 @@ bool RTMPStream::Chunk::Parse(std::string & indata){ /// 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, -/// this is the handshake response and not interesting for us because we don't do client +/// these are the handshake response and not interesting for us because we don't do client /// verification. bool RTMPStream::doHandshake(){ char Version; From 93528dfc1dfc3655e2c17f1624a5e40de20b50a0 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 17 Apr 2011 00:38:04 +0200 Subject: [PATCH 121/788] RTMP edits --- util/rtmpchunks.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/util/rtmpchunks.cpp b/util/rtmpchunks.cpp index eca2f9fb..a63a2380 100644 --- a/util/rtmpchunks.cpp +++ b/util/rtmpchunks.cpp @@ -18,8 +18,8 @@ unsigned int RTMPStream::getNowMS(){ unsigned int RTMPStream::chunk_rec_max = 128; unsigned int RTMPStream::chunk_snd_max = 128; -unsigned int RTMPStream::rec_window_size = 0xFA00; -unsigned int RTMPStream::snd_window_size = 1024*500; +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; @@ -243,6 +243,9 @@ std::string RTMPStream::SendUSR(unsigned char type, unsigned int data, unsigned /// Parses the argument string into the current chunk. /// Tries to read a whole chunk, if successful it will remove /// the corresponding data from the input string. +/// If only part of a chunk is read, it will remove the part and call itself again. +/// This has the effect of only causing a "true" reponse in the case a *whole* chunk +/// is read, not just part of a chunk. /// \param indata The input string to parse and update. /// \warning This function will destroy the current data in this chunk! /// \returns True if a whole chunk could be read, false otherwise. @@ -368,7 +371,7 @@ bool RTMPStream::Chunk::Parse(std::string & indata){ /// 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, -/// this is the handshake response and not interesting for us because we don't do client +/// these are the handshake response and not interesting for us because we don't do client /// verification. bool RTMPStream::doHandshake(){ char Version; From 4608d55d34a96cd8514acb673066d09eaf7148ae Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 17 Apr 2011 04:04:00 +0200 Subject: [PATCH 122/788] AMF3 eerste poging --- util/amf.cpp | 561 ++++++++++++++++++++++++++++++++++++++++++++++++++- util/amf.h | 58 ++++++ 2 files changed, 618 insertions(+), 1 deletion(-) diff --git a/util/amf.cpp b/util/amf.cpp index 97d97ef5..938770a9 100644 --- a/util/amf.cpp +++ b/util/amf.cpp @@ -46,7 +46,7 @@ AMF::Object* AMF::Object::getContentP(std::string s){ for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ if (it->Indice() == s){return &(*it);} } - return this; + return 0; }; /// Returns a copy of the object held at indice s. @@ -59,6 +59,12 @@ AMF::Object AMF::Object::getContent(std::string s){ return AMF::Object("error", AMF0_DDV_CONTAINER); }; +/// Default constructor. +/// Simply fills the data with AMF::Object("error", AMF0_DDV_CONTAINER) +AMF::Object::Object(){ + *this = AMF::Object("error", AMF0_DDV_CONTAINER); +};//default constructor + /// Constructor for numeric objects. /// The object type is by default AMF::AMF0_NUMBER, but this can be forced to a different value. /// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. @@ -404,3 +410,556 @@ AMF::Object AMF::parse(const unsigned char * data, unsigned int len){ AMF::Object AMF::parse(std::string data){ return AMF::parse((const unsigned char*)data.c_str(), data.size()); }//parse + +/// Returns the std::string Indice for the current object, if available. +/// Returns an empty string if no indice exists. +std::string AMF::Object3::Indice(){return myIndice;}; + +/// Returns the AMF::obj0type AMF0 object type for this object. +AMF::obj3type AMF::Object3::GetType(){return myType;}; + +/// Returns the double value of this object, if available. +/// If this object holds no double value, 0 is returned. +double AMF::Object3::DblValue(){return dblval;}; + +/// Returns the integer value of this object, if available. +/// If this object holds no integer value, 0 is returned. +int AMF::Object3::IntValue(){return intval;}; + +/// Returns the std::string value of this object, if available. +/// If this object holds no string value, an empty string is returned. +std::string AMF::Object3::StrValue(){return strval;}; + +/// Returns the C-string value of this object, if available. +/// If this object holds no string value, an empty C-string is returned. +const char * AMF::Object3::Str(){return strval.c_str();}; + +/// Returns a count of the amount of objects this object currently holds. +/// If this object is not a container type, this function will always return 0. +int AMF::Object3::hasContent(){return contents.size();}; + +/// Adds an AMF::Object to this object. Works for all types, but only makes sense for container types. +void AMF::Object3::addContent(AMF::Object3 c){contents.push_back(c);}; + +/// Returns a pointer to the object held at indice i. +/// Returns AMF::AMF3_DDV_CONTAINER of indice "error" if no object is held at this indice. +/// \param i The indice of the object in this container. +AMF::Object3* AMF::Object3::getContentP(int i){return &contents.at(i);}; + +/// Returns a copy of the object held at indice i. +/// Returns a AMF::AMF3_DDV_CONTAINER of indice "error" if no object is held at this indice. +/// \param i The indice of the object in this container. +AMF::Object3 AMF::Object3::getContent(int i){return contents.at(i);}; + +/// Returns a pointer to the object held at indice s. +/// Returns NULL if no object is held at this indice. +/// \param s The indice of the object in this container. +AMF::Object3* AMF::Object3::getContentP(std::string s){ + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ + if (it->Indice() == s){return &(*it);} + } + return 0; +}; + +/// Returns a copy of the object held at indice s. +/// Returns a AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice. +/// \param s The indice of the object in this container. +AMF::Object3 AMF::Object3::getContent(std::string s){ + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ + if (it->Indice() == s){return *it;} + } + return AMF::Object3("error", AMF3_DDV_CONTAINER); +}; + +/// Default constructor. +/// Simply fills the data with AMF::Object3("error", AMF3_DDV_CONTAINER) +AMF::Object3::Object3(){ + *this = AMF::Object3("error", AMF3_DDV_CONTAINER); +};//default constructor + +/// Constructor for double objects. +/// The object type is by default AMF::AMF3_DOUBLE, but this can be forced to a different value. +/// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. +/// \param val The numeric value of this object. Double AMF3 objects only support double-type values. +/// \param setType The object type to force this object to. +AMF::Object3::Object3(std::string indice, double val, AMF::obj3type setType){//num type initializer + myIndice = indice; + myType = setType; + strval = ""; + dblval = val; + intval = 0; +}; + +/// Constructor for integer objects. +/// The object type is by default AMF::AMF3_INTEGER, but this can be forced to a different value. +/// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. +/// \param val The numeric value of this object. Integer AMF3 objects only support integer-type values. +/// \param setType The object type to force this object to. +AMF::Object3::Object3(std::string indice, int val, AMF::obj3type setType){//num type initializer +myIndice = indice; +myType = setType; +strval = ""; +dblval = val; +intval = 0; +}; + +/// Constructor for string objects. +/// The object type is by default AMF::AMF0_STRING, but this can be forced to a different value. +/// There is no need to manually change the type to AMF::AMF0_LONGSTRING, this will be done automatically. +/// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. +/// \param val The string value of this object. +/// \param setType The object type to force this object to. +AMF::Object3::Object3(std::string indice, std::string val, AMF::obj3type setType){//str type initializer + myIndice = indice; + myType = setType; + strval = val; + dblval = 0; + intval = 0; +}; + +/// Constructor for container objects. +/// The object type is by default AMF::AMF0_OBJECT, but this can be forced to a different value. +/// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. +/// \param setType The object type to force this object to. +AMF::Object3::Object3(std::string indice, AMF::obj3type setType){//object type initializer + myIndice = indice; + myType = setType; + strval = ""; + dblval = 0; + intval = 0; +}; + +/// Prints the contents of this object to std::cerr. +/// If this object contains other objects, it will call itself recursively +/// and print all nested content in a nice human-readable format. +void AMF::Object3::Print(std::string indent){ + std::cerr << indent; + // print my type + switch (myType){ + case AMF::AMF3_UNDEFINED: std::cerr << "Undefined"; break; + case AMF::AMF3_NULL: std::cerr << "Null"; break; + case AMF::AMF3_FALSE: std::cerr << "False"; break; + case AMF::AMF3_TRUE: std::cerr << "True"; break; + case AMF::AMF3_INTEGER: std::cerr << "Integer"; break; + case AMF::AMF3_DOUBLE: std::cerr << "Double"; break; + case AMF::AMF3_STRING: std::cerr << "String"; break; + case AMF::AMF3_XMLDOC: std::cerr << "XML Doc"; break; + case AMF::AMF3_DATE: std::cerr << "Date"; break; + case AMF::AMF3_ARRAY: std::cerr << "Array"; break; + case AMF::AMF3_OBJECT: std::cerr << "Object"; break; + case AMF::AMF3_XML: std::cerr << "XML"; break; + case AMF::AMF3_BYTES: std::cerr << "ByteArray"; break; + case AMF::AMF3_DDV_CONTAINER: std::cerr << "DDVTech Container"; break; + } + // print my string indice, if available + std::cerr << " " << myIndice << " "; + // print my numeric or string contents + switch (myType){ + case AMF::AMF3_INTEGER: std::cerr << intval; break; + case AMF::AMF3_DOUBLE: std::cerr << dblval; break; + case AMF::AMF3_STRING: case AMF::AMF3_XMLDOC: case AMF::AMF3_XML: case AMF::AMF3_BYTES: + if (intval > 0){ + std::cerr << "REF" << intval; + }else{ + std::cerr << strval; + } + break; + case AMF::AMF3_DATE: + if (intval > 0){ + std::cerr << "REF" << intval; + }else{ + std::cerr << dblval; + } + break; + case AMF::AMF3_ARRAY: case AMF::AMF3_OBJECT: + if (intval > 0){ + std::cerr << "REF" << intval; + } + break; + default: break;//we don't care about the rest, and don't want a compiler warning... + } + std::cerr << std::endl; + // if I hold other objects, print those too, recursively. + if (contents.size() > 0){ + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){it->Print(indent+" ");} + } +};//print + +/// Packs the AMF object to a std::string for transfer over the network. +/// If the object is a container type, this function will call itself recursively and contain all contents. +/// Tip: When sending multiple AMF objects in one go, put them in a single AMF::AMF0_DDV_CONTAINER for easy transfer. +std::string AMF::Object3::Pack(){ + std::string r = ""; + return r; +};//pack + +/// Parses a single AMF3 type - used recursively by the AMF::parse3() functions. +/// This function updates i every call with the new position in the data. +/// \param data The raw data to parse. +/// \param len The size of the raw data. +/// \param i Current parsing position in the raw data. +/// \param name Indice name for any new object created. +/// \returns A single AMF::Object3, parsed from the raw data. +AMF::Object3 AMF::parseOne3(const unsigned char *& data, unsigned int &len, unsigned int &i, std::string name){ + std::string tmpstr; + unsigned int tmpi = 0; + unsigned int arrsize = 0; + unsigned char tmpdbl[8]; + #if DEBUG >= 10 + fprintf(stderr, "Note: AMF type %hhx found. %i bytes left\n", data[i], len-i); + #endif + switch (data[i]){ + case AMF::AMF3_UNDEFINED: + case AMF::AMF3_NULL: + case AMF::AMF3_FALSE: + case AMF::AMF3_TRUE: + ++i; + return AMF::Object3(name, (AMF::obj3type)data[i-1]); + break; + case AMF::AMF3_INTEGER: + if (data[i+1] < 0x80){ + tmpi = data[i+1]; + i+=2; + }else{ + tmpi = (data[i+1] & 0x7F) << 7;//strip the upper bit, shift 7 up. + if (data[i+2] < 0x80){ + tmpi |= data[i+2]; + i+=3; + }else{ + tmpi = (tmpi | (data[i+2] & 0x7F)) << 7;//strip the upper bit, shift 7 up. + if (data[i+3] < 0x80){ + tmpi |= data[i+3]; + i+=4; + }else{ + tmpi = (tmpi | (data[i+3] & 0x7F)) << 8;//strip the upper bit, shift 7 up. + tmpi |= data[i+4]; + i+=5; + } + } + } + tmpi = (tmpi << 3) >> 3;//fix sign bit + return AMF::Object3(name, (int)tmpi, AMF::AMF3_INTEGER); + break; + case AMF::AMF3_DOUBLE: + tmpdbl[7] = data[i+1]; + tmpdbl[6] = data[i+2]; + tmpdbl[5] = data[i+3]; + tmpdbl[4] = data[i+4]; + tmpdbl[3] = data[i+5]; + tmpdbl[2] = data[i+6]; + tmpdbl[1] = data[i+7]; + tmpdbl[0] = data[i+8]; + i+=9;//skip 8(a double)+1 forwards + return AMF::Object3(name, *(double*)tmpdbl, AMF::AMF3_DOUBLE); + break; + case AMF::AMF3_STRING: + if (data[i+1] < 0x80){ + tmpi = data[i+1]; + i+=2; + }else{ + tmpi = (data[i+1] & 0x7F) << 7;//strip the upper bit, shift 7 up. + if (data[i+2] < 0x80){ + tmpi |= data[i+2]; + i+=3; + }else{ + tmpi = (tmpi | (data[i+2] & 0x7F)) << 7;//strip the upper bit, shift 7 up. + if (data[i+3] < 0x80){ + tmpi |= data[i+3]; + i+=4; + }else{ + tmpi = (tmpi | (data[i+3] & 0x7F)) << 8;//strip the upper bit, shift 7 up. + tmpi |= data[i+4]; + i+=5; + } + } + } + tmpi = (tmpi << 3) >> 3;//fix sign bit + if ((tmpi & 1) == 0){ + return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_STRING);//reference type + } + tmpstr.clear();//clean tmpstr, just to be sure + tmpstr.append((const char *)data+i, (size_t)(tmpi >> 1));//add the string data + i += (tmpi >> 1);//skip length+size+1 forwards + return AMF::Object3(name, tmpstr, AMF::AMF3_STRING);//normal type + break; + case AMF::AMF3_XMLDOC: + if (data[i+1] < 0x80){ + tmpi = data[i+1]; + i+=2; + }else{ + tmpi = (data[i+1] & 0x7F) << 7;//strip the upper bit, shift 7 up. + if (data[i+2] < 0x80){ + tmpi |= data[i+2]; + i+=3; + }else{ + tmpi = (tmpi | (data[i+2] & 0x7F)) << 7;//strip the upper bit, shift 7 up. + if (data[i+3] < 0x80){ + tmpi |= data[i+3]; + i+=4; + }else{ + tmpi = (tmpi | (data[i+3] & 0x7F)) << 8;//strip the upper bit, shift 7 up. + tmpi |= data[i+4]; + i+=5; + } + } + } + tmpi = (tmpi << 3) >> 3;//fix sign bit + if ((tmpi & 1) == 0){ + return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_XMLDOC);//reference type + } + tmpstr.clear();//clean tmpstr, just to be sure + tmpstr.append((const char *)data+i, (size_t)(tmpi >> 1));//add the string data + i += (tmpi >> 1);//skip length+size+1 forwards + return AMF::Object3(name, tmpstr, AMF::AMF3_XMLDOC);//normal type + break; + case AMF::AMF3_XML: + if (data[i+1] < 0x80){ + tmpi = data[i+1]; + i+=2; + }else{ + tmpi = (data[i+1] & 0x7F) << 7;//strip the upper bit, shift 7 up. + if (data[i+2] < 0x80){ + tmpi |= data[i+2]; + i+=3; + }else{ + tmpi = (tmpi | (data[i+2] & 0x7F)) << 7;//strip the upper bit, shift 7 up. + if (data[i+3] < 0x80){ + tmpi |= data[i+3]; + i+=4; + }else{ + tmpi = (tmpi | (data[i+3] & 0x7F)) << 8;//strip the upper bit, shift 7 up. + tmpi |= data[i+4]; + i+=5; + } + } + } + tmpi = (tmpi << 3) >> 3;//fix sign bit + if ((tmpi & 1) == 0){ + return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_XML);//reference type + } + tmpstr.clear();//clean tmpstr, just to be sure + tmpstr.append((const char *)data+i, (size_t)(tmpi >> 1));//add the string data + i += (tmpi >> 1);//skip length+size+1 forwards + return AMF::Object3(name, tmpstr, AMF::AMF3_XML);//normal type + break; + case AMF::AMF3_BYTES: + if (data[i+1] < 0x80){ + tmpi = data[i+1]; + i+=2; + }else{ + tmpi = (data[i+1] & 0x7F) << 7;//strip the upper bit, shift 7 up. + if (data[i+2] < 0x80){ + tmpi |= data[i+2]; + i+=3; + }else{ + tmpi = (tmpi | (data[i+2] & 0x7F)) << 7;//strip the upper bit, shift 7 up. + if (data[i+3] < 0x80){ + tmpi |= data[i+3]; + i+=4; + }else{ + tmpi = (tmpi | (data[i+3] & 0x7F)) << 8;//strip the upper bit, shift 7 up. + tmpi |= data[i+4]; + i+=5; + } + } + } + tmpi = (tmpi << 3) >> 3;//fix sign bit + if ((tmpi & 1) == 0){ + return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_BYTES);//reference type + } + tmpstr.clear();//clean tmpstr, just to be sure + tmpstr.append((const char *)data+i, (size_t)(tmpi >> 1));//add the string data + i += (tmpi >> 1);//skip length+size+1 forwards + return AMF::Object3(name, tmpstr, AMF::AMF3_BYTES);//normal type + break; + case AMF::AMF3_DATE: + if (data[i+1] < 0x80){ + tmpi = data[i+1]; + i+=2; + }else{ + tmpi = (data[i+1] & 0x7F) << 7;//strip the upper bit, shift 7 up. + if (data[i+2] < 0x80){ + tmpi |= data[i+2]; + i+=3; + }else{ + tmpi = (tmpi | (data[i+2] & 0x7F)) << 7;//strip the upper bit, shift 7 up. + if (data[i+3] < 0x80){ + tmpi |= data[i+3]; + i+=4; + }else{ + tmpi = (tmpi | (data[i+3] & 0x7F)) << 8;//strip the upper bit, shift 7 up. + tmpi |= data[i+4]; + i+=5; + } + } + } + tmpi = (tmpi << 3) >> 3;//fix sign bit + if ((tmpi & 1) == 0){ + return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_DATE);//reference type + } + tmpdbl[7] = data[i]; + tmpdbl[6] = data[i+1]; + tmpdbl[5] = data[i+2]; + tmpdbl[4] = data[i+3]; + tmpdbl[3] = data[i+4]; + tmpdbl[2] = data[i+5]; + tmpdbl[1] = data[i+6]; + tmpdbl[0] = data[i+7]; + i += 8;//skip a double forwards + return AMF::Object3(name, *(double*)tmpdbl, AMF::AMF3_DATE); + break; + case AMF::AMF3_ARRAY:{ + if (data[i+1] < 0x80){ + tmpi = data[i+1]; + i+=2; + }else{ + tmpi = (data[i+1] & 0x7F) << 7;//strip the upper bit, shift 7 up. + if (data[i+2] < 0x80){ + tmpi |= data[i+2]; + i+=3; + }else{ + tmpi = (tmpi | (data[i+2] & 0x7F)) << 7;//strip the upper bit, shift 7 up. + if (data[i+3] < 0x80){ + tmpi |= data[i+3]; + i+=4; + }else{ + tmpi = (tmpi | (data[i+3] & 0x7F)) << 8;//strip the upper bit, shift 7 up. + tmpi |= data[i+4]; + i+=5; + } + } + } + tmpi = (tmpi << 3) >> 3;//fix sign bit + if ((tmpi & 1) == 0){ + return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_ARRAY);//reference type + } + AMF::Object3 ret(name, AMF::AMF3_ARRAY); + arrsize = tmpi >> 1; + do{ + if (data[i+1] < 0x80){ + tmpi = data[i+1]; + i+=2; + }else{ + tmpi = (data[i+1] & 0x7F) << 7;//strip the upper bit, shift 7 up. + if (data[i+2] < 0x80){ + tmpi |= data[i+2]; + i+=3; + }else{ + tmpi = (tmpi | (data[i+2] & 0x7F)) << 7;//strip the upper bit, shift 7 up. + if (data[i+3] < 0x80){ + tmpi |= data[i+3]; + i+=4; + }else{ + tmpi = (tmpi | (data[i+3] & 0x7F)) << 8;//strip the upper bit, shift 7 up. + tmpi |= data[i+4]; + i+=5; + } + } + } + tmpi = (tmpi << 3) >> 4;//fix sign bit, ignore references for now... + /// \todo Fix references? + if (tmpi > 0){ + tmpstr.clear();//clean tmpstr, just to be sure + tmpstr.append((const char*)data+i, (size_t)tmpi);//add the string data + ret.addContent(AMF::parseOne3(data, len, i, tmpstr));//add content, recursively parsed, updating i + } + }while(tmpi > 0); + while (arrsize > 0){//while not done parsing array + ret.addContent(AMF::parseOne3(data, len, i, "arrVal"));//add content, recursively parsed, updating i + --arrsize; + } + return ret; + } break; + case AMF::AMF3_OBJECT:{ + if (data[i+1] < 0x80){ + tmpi = data[i+1]; + i+=2; + }else{ + tmpi = (data[i+1] & 0x7F) << 7;//strip the upper bit, shift 7 up. + if (data[i+2] < 0x80){ + tmpi |= data[i+2]; + i+=3; + }else{ + tmpi = (tmpi | (data[i+2] & 0x7F)) << 7;//strip the upper bit, shift 7 up. + if (data[i+3] < 0x80){ + tmpi |= data[i+3]; + i+=4; + }else{ + tmpi = (tmpi | (data[i+3] & 0x7F)) << 8;//strip the upper bit, shift 7 up. + tmpi |= data[i+4]; + i+=5; + } + } + } + tmpi = (tmpi << 3) >> 3;//fix sign bit + if ((tmpi & 1) == 0){ + return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_OBJECT);//reference type + } + AMF::Object3 ret(name, AMF::AMF3_OBJECT); + bool isdynamic = false; + if ((tmpi & 2) == 0){//traits by reference, skip for now + /// \todo Implement traits by reference. Or references in general, of course... + }else{ + isdynamic = ((tmpi & 8) == 8); + arrsize = tmpi >> 4;//count of sealed members + /// \todo Read in arrsize sealed member names, then arrsize sealed members. + } + if (isdynamic){ + do{ + if (data[i+1] < 0x80){ + tmpi = data[i+1]; + i+=2; + }else{ + tmpi = (data[i+1] & 0x7F) << 7;//strip the upper bit, shift 7 up. + if (data[i+2] < 0x80){ + tmpi |= data[i+2]; + i+=3; + }else{ + tmpi = (tmpi | (data[i+2] & 0x7F)) << 7;//strip the upper bit, shift 7 up. + if (data[i+3] < 0x80){ + tmpi |= data[i+3]; + i+=4; + }else{ + tmpi = (tmpi | (data[i+3] & 0x7F)) << 8;//strip the upper bit, shift 7 up. + tmpi |= data[i+4]; + i+=5; + } + } + } + tmpi = (tmpi << 3) >> 4;//fix sign bit, ignore references for now... + /// \todo Fix references? + if (tmpi > 0){ + tmpstr.clear();//clean tmpstr, just to be sure + tmpstr.append((const char*)data+i, (size_t)tmpi);//add the string data + ret.addContent(AMF::parseOne3(data, len, i, tmpstr));//add content, recursively parsed, updating i + } + }while(tmpi > 0);//keep reading dynamic values until empty string + }//dynamic types + return ret; + } break; + } + #if DEBUG >= 2 + fprintf(stderr, "Error: Unimplemented AMF3 type %hhx - returning.\n", data[i]); + #endif + return AMF::Object3("error", AMF::AMF3_DDV_CONTAINER); +}//parseOne + +/// Parses a C-string to a valid AMF::Object3. +/// This function will find all AMF3 objects in the string and return +/// them all packed in a single AMF::AMF3_DDV_CONTAINER AMF::Object3. +AMF::Object3 AMF::parse3(const unsigned char * data, unsigned int len){ + AMF::Object3 ret("returned", AMF::AMF3_DDV_CONTAINER);//container type + unsigned int i = 0, j = 0; + while (i < len){ + ret.addContent(AMF::parseOne3(data, len, i, "")); + if (i > j){j = i;}else{return ret;} + } + return ret; +}//parse + +/// Parses a std::string to a valid AMF::Object3. +/// This function will find all AMF3 objects in the string and return +/// them all packed in a single AMF::AMF3_DDV_CONTAINER AMF::Object3. +AMF::Object3 AMF::parse3(std::string data){ + return AMF::parse3((const unsigned char*)data.c_str(), data.size()); +}//parse diff --git a/util/amf.h b/util/amf.h index 8ae95cd4..61f95a8a 100644 --- a/util/amf.h +++ b/util/amf.h @@ -33,6 +33,24 @@ namespace AMF{ 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 { @@ -48,6 +66,7 @@ namespace AMF{ Object getContent(int i); Object* getContentP(std::string s); Object getContent(std::string s); + Object(); Object(std::string indice, double val, obj0type setType = AMF0_NUMBER); Object(std::string indice, std::string val, obj0type setType = AMF0_STRING); Object(std::string indice, obj0type setType = AMF0_OBJECT); @@ -67,5 +86,44 @@ namespace AMF{ Object parse(std::string data); /// Parses a single AMF0 type - used recursively by the AMF::parse() functions. Object parseOne(const unsigned char *& data, unsigned int &len, unsigned int &i, std::string name); + + /// Recursive class that holds AMF3 objects. + /// It supports all AMF3 types (defined in AMF::obj3type), adding support for a special DDVTECH container type. + class Object3 { + public: + std::string Indice(); + obj3type GetType(); + double DblValue(); + int IntValue(); + std::string StrValue(); + const char * Str(); + int hasContent(); + void addContent(AMF::Object3 c); + Object3* getContentP(int i); + Object3 getContent(int i); + Object3* getContentP(std::string s); + Object3 getContent(std::string s); + Object3(); + Object3(std::string indice, int val, obj3type setType = AMF3_INTEGER); + Object3(std::string indice, double val, obj3type setType = AMF3_DOUBLE); + Object3(std::string indice, std::string val, obj3type setType = AMF3_STRING); + Object3(std::string indice, obj3type setType = AMF3_OBJECT); + void Print(std::string indent = ""); + std::string Pack(); + protected: + std::string myIndice; ///< Holds this objects indice, if any. + obj3type myType; ///< Holds this objects AMF0 type. + std::string strval; ///< Holds this objects string value, if any. + double dblval; ///< Holds this objects double value, if any. + int intval; ///< Holds this objects int value, if any. + std::vector contents; ///< Holds this objects contents, if any (for container types). + };//AMFType + + /// Parses a C-string to a valid AMF::Object3. + Object3 parse3(const unsigned char * data, unsigned int len); + /// Parses a std::string to a valid AMF::Object3. + Object3 parse3(std::string data); + /// Parses a single AMF3 type - used recursively by the AMF::parse3() functions. + Object3 parseOne3(const unsigned char *& data, unsigned int &len, unsigned int &i, std::string name); };//AMF namespace From 06e0aa40abe3ac96665f933edb4358dabaa72fc2 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 17 Apr 2011 23:59:49 +0200 Subject: [PATCH 123/788] AMF3 support, RTMP werkendheid? --- util/amf.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/amf.cpp b/util/amf.cpp index 938770a9..b82aceeb 100644 --- a/util/amf.cpp +++ b/util/amf.cpp @@ -606,7 +606,7 @@ AMF::Object3 AMF::parseOne3(const unsigned char *& data, unsigned int &len, unsi unsigned int arrsize = 0; unsigned char tmpdbl[8]; #if DEBUG >= 10 - fprintf(stderr, "Note: AMF type %hhx found. %i bytes left\n", data[i], len-i); + fprintf(stderr, "Note: AMF3 type %hhx found. %i bytes left\n", data[i], len-i); #endif switch (data[i]){ case AMF::AMF3_UNDEFINED: @@ -759,11 +759,11 @@ AMF::Object3 AMF::parseOne3(const unsigned char *& data, unsigned int &len, unsi }else{ tmpi = (tmpi | (data[i+3] & 0x7F)) << 8;//strip the upper bit, shift 7 up. tmpi |= data[i+4]; + tmpi = (tmpi << 3) >> 3;//fix sign bit i+=5; } } } - tmpi = (tmpi << 3) >> 3;//fix sign bit if ((tmpi & 1) == 0){ return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_BYTES);//reference type } From 0f125ca0e93a74807f707f6a63bea202a24ed859 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 18 Apr 2011 15:09:31 +0200 Subject: [PATCH 124/788] Socket bugfix --- util/ddv_socket.cpp | 38 ++++++++++++++------------------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index 3b3567ac..c54cee42 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -106,21 +106,16 @@ bool DDV::Socket::connected(){ /// \returns True if the whole write was succesfull, false otherwise. bool DDV::Socket::write(const void * buffer, int len){ int sofar = 0; - Blocking = false; + if (sock < 0){return false;} while (sofar != len){ int r = send(sock, (char*)buffer + sofar, len-sofar, 0); if (r <= 0){ - switch (errno){ - case EWOULDBLOCK: Blocking = true; break; - default: - Error = true; - #if DEBUG >= 2 - fprintf(stderr, "Could not write data! Error: %s\n", strerror(errno)); - #endif - close(); - return false; - break; - } + Error = true; + #if DEBUG >= 2 + fprintf(stderr, "Could not write data! Error: %s\n", strerror(errno)); + #endif + close(); + return false; }else{ sofar += r; } @@ -137,21 +132,16 @@ bool DDV::Socket::write(const void * buffer, int len){ /// \returns True if the whole read was succesfull, false otherwise. bool DDV::Socket::read(void * buffer, int len){ int sofar = 0; - Blocking = false; + if (sock < 0){return false;} while (sofar != len){ int r = recv(sock, (char*)buffer + sofar, len-sofar, 0); if (r <= 0){ - switch (errno){ - case EWOULDBLOCK: Blocking = true; break; - default: - Error = true; - #if DEBUG >= 2 - fprintf(stderr, "Could not read data! Error: %s\n", strerror(errno)); - #endif - close(); - return false; - break; - } + Error = true; + #if DEBUG >= 2 + fprintf(stderr, "Could not read data! Error: %s\n", strerror(errno)); + #endif + close(); + return false; }else{ sofar += r; } From 08f78a8bedc06a3b81675bd6c34cc7d1d8651f8c Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 18 Apr 2011 15:19:26 +0200 Subject: [PATCH 125/788] Socket fixes deel 2 --- util/ddv_socket.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index c54cee42..08bfda65 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -176,6 +176,7 @@ int DDV::Socket::iwrite(void * buffer, int len){ break; } } + if (r == 0){close();} return r; }//DDV::Socket::iwrite @@ -199,6 +200,7 @@ int DDV::Socket::iread(void * buffer, int len){ break; } } + if (r == 0){close();} return r; }//DDV::Socket::iread From 8f3466ea7864831698810f3e2d2a629ccf6d12bb Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 18 Apr 2011 15:42:14 +0200 Subject: [PATCH 126/788] Socket fixes 3 --- util/ddv_socket.cpp | 10 ++++++---- util/http_parser.cpp | 16 ++-------------- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index 08bfda65..72ed286e 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -211,12 +211,14 @@ int DDV::Socket::iread(void * buffer, int len){ /// \return True if new data arrived, false otherwise. bool DDV::Socket::read(std::string & buffer){ char cbuffer[5000]; - int num = iread(cbuffer, 5000); + if (!read(cbuffer, 1)){return false;} + int num = iread(cbuffer+1, 4999); if (num > 0){ - buffer.append(cbuffer, num); - return true; + buffer.append(cbuffer, num+1); + }else{ + buffer.append(cbuffer, 1); } - return false; + return true; }//read /// Create a new base ServerSocket. The socket is never connected, and a placeholder for later connections. diff --git a/util/http_parser.cpp b/util/http_parser.cpp index 60d370e7..8c9ed2db 100644 --- a/util/http_parser.cpp +++ b/util/http_parser.cpp @@ -134,20 +134,8 @@ bool HTTP::Parser::Read(DDV::Socket & sock){ int r = 0; int b = 0; char buffer[500]; - while (true){ - r = sock.ready(); - if (r < 1){ - if (r == -1){ - #if DEBUG >= 1 - fprintf(stderr, "User socket is disconnected.\n"); - #endif - } - return parse(); - } - b = sock.iread(buffer, 500); - HTTPbuffer.append(buffer, b); - } - return false; + sock.read(HTTPbuffer); + return parse(); }//HTTPReader::ReadSocket /// Reads a full set of HTTP responses/requests from file F. From 9ff67b96fd0aa2a7a72bc45e006d56b395a31a7a Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 18 Apr 2011 16:08:55 +0200 Subject: [PATCH 127/788] Hopelijk laatste socket fix --- util/http_parser.cpp | 9 ++++----- util/http_parser.h | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/util/http_parser.cpp b/util/http_parser.cpp index 8c9ed2db..4fe7b94f 100644 --- a/util/http_parser.cpp +++ b/util/http_parser.cpp @@ -128,12 +128,11 @@ void HTTP::Parser::SetVar(std::string i, std::string v){ } /// Attempt to read a whole HTTP request or response from DDV::Socket sock. +/// \param sock The socket to use. +/// \param nonblock When true, will not block even if the socket is blocking. /// \return True of a whole request or response was read, false otherwise. -bool HTTP::Parser::Read(DDV::Socket & sock){ - //returned true als hele http packet gelezen is - int r = 0; - int b = 0; - char buffer[500]; +bool HTTP::Parser::Read(DDV::Socket & sock, bool nonblock){ + if (nonblock && (sock.ready() < 1)){return parse();} sock.read(HTTPbuffer); return parse(); }//HTTPReader::ReadSocket diff --git a/util/http_parser.h b/util/http_parser.h index d08cadef..ed8f64fe 100644 --- a/util/http_parser.h +++ b/util/http_parser.h @@ -14,7 +14,7 @@ namespace HTTP{ class Parser{ public: Parser(); - bool Read(DDV::Socket & sock); + bool Read(DDV::Socket & sock, bool nonblock = true); bool Read(FILE * F); std::string GetHeader(std::string i); std::string GetVar(std::string i); From 9aeb827b0fc0d794cbf1c8b3c9651303c07bc7f6 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 18 Apr 2011 16:29:48 +0200 Subject: [PATCH 128/788] New compilation method --- util/rtmpchunks.h | 1 - 1 file changed, 1 deletion(-) diff --git a/util/rtmpchunks.h b/util/rtmpchunks.h index b11142c8..8feeefb0 100644 --- a/util/rtmpchunks.h +++ b/util/rtmpchunks.h @@ -8,7 +8,6 @@ #include #include #include -#define DEBUG 4 /// Contains all functions and classes needed for RTMP connections. namespace RTMPStream{ From 1f41edb949171077ad25ac2c12490a708dfbe7cd Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 20 Jun 2011 19:27:59 +0200 Subject: [PATCH 129/788] Fix missing socket.h/cpp --- util/socket.cpp | 444 ++++++++++++++++++++++++++++++++++++++++++++++++ util/socket.h | 65 +++++++ 2 files changed, 509 insertions(+) create mode 100644 util/socket.cpp create mode 100644 util/socket.h diff --git a/util/socket.cpp b/util/socket.cpp new file mode 100644 index 00000000..60c3c69e --- /dev/null +++ b/util/socket.cpp @@ -0,0 +1,444 @@ +/// \file socket.cpp +/// A handy Socket wrapper library. +/// Written by Jaron Vietor in 2010 for DDVTech + +#include "socket.h" + +/// Create a new base socket. This is a basic constructor for converting any valid socket to a Socket::Connection. +/// \param sockNo Integer representing the socket to convert. +Socket::Connection::Connection(int sockNo){ + sock = sockNo; + Error = false; + Blocking = false; +}//Socket::Connection basic constructor + +/// Create a new disconnected base socket. This is a basic constructor for placeholder purposes. +/// A socket created like this is always disconnected and should/could be overwritten at some point. +Socket::Connection::Connection(){ + sock = -1; + Error = false; + Blocking = false; +}//Socket::Connection basic constructor + +/// Close connection. The internal socket is closed and then set to -1. +void Socket::Connection::close(){ + #if DEBUG >= 4 + fprintf(stderr, "Socket closed.\n"); + #endif + shutdown(sock, SHUT_RDWR); + ::close(sock); + sock = -1; +}//Socket::Connection::close + +/// Returns internal socket number. +int Socket::Connection::getSocket(){return sock;} + +/// Create a new Unix Socket. This socket will (try to) connect to the given address right away. +/// \param address String containing the location of the Unix socket to connect to. +/// \param nonblock Whether the socket should be nonblocking. False by default. +Socket::Connection::Connection(std::string address, bool nonblock){ + sock = socket(PF_UNIX, SOCK_STREAM, 0); + if (sock < 0){ + #if DEBUG >= 1 + fprintf(stderr, "Could not create socket! Error: %s\n", strerror(errno)); + #endif + return; + } + Error = false; + Blocking = false; + sockaddr_un addr; + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, address.c_str(), address.size()+1); + int r = connect(sock, (sockaddr*)&addr, sizeof(addr)); + if (r == 0){ + if (nonblock){ + int flags = fcntl(sock, F_GETFL, 0); + flags |= O_NONBLOCK; + fcntl(sock, F_SETFL, flags); + } + }else{ + #if DEBUG >= 1 + fprintf(stderr, "Could not connect to %s! Error: %s\n", address.c_str(), strerror(errno)); + #endif + close(); + } +}//Socket::Connection Unix Contructor + +/// Returns the ready-state for this socket. +/// \returns 1 if data is waiting to be read, -1 if not connected, 0 otherwise. +signed int Socket::Connection::ready(){ + if (sock < 0) return -1; + char tmp; + int preflags = fcntl(sock, F_GETFL, 0); + int postflags = preflags | O_NONBLOCK; + fcntl(sock, F_SETFL, postflags); + int r = recv(sock, &tmp, 1, MSG_PEEK); + fcntl(sock, F_SETFL, preflags); + if (r < 0){ + if (errno == EAGAIN || errno == EWOULDBLOCK){ + return 0; + }else{ + #if DEBUG >= 2 + fprintf(stderr, "Socket ready error! Error: %s\n", strerror(errno)); + #endif + close(); + return -1; + } + } + if (r == 0){ + close(); return -1; + } + return r; +} + +/// Returns the connected-state for this socket. +/// Note that this function might be slightly behind the real situation. +/// The connection status is updated after every read/write attempt, when errors occur +/// and when the socket is closed manually. +/// \returns True if socket is connected, false otherwise. +bool Socket::Connection::connected(){ + return (sock >= 0); +} + +/// Writes data to socket. This function blocks if the socket is blocking and all data cannot be written right away. +/// If the socket is nonblocking and not all data can be written, this function sets internal variable Blocking to true +/// and returns false. +/// \param buffer Location of the buffer to write from. +/// \param len Amount of bytes to write. +/// \returns True if the whole write was succesfull, false otherwise. +bool Socket::Connection::write(const void * buffer, int len){ + int sofar = 0; + if (sock < 0){return false;} + while (sofar != len){ + int r = send(sock, (char*)buffer + sofar, len-sofar, 0); + if (r <= 0){ + Error = true; + #if DEBUG >= 2 + fprintf(stderr, "Could not write data! Error: %s\n", strerror(errno)); + #endif + close(); + return false; + }else{ + sofar += r; + } + } + return true; +}//DDv::Socket::write + + +/// Reads data from socket. This function blocks if the socket is blocking and all data cannot be read right away. +/// If the socket is nonblocking and not all data can be read, this function sets internal variable Blocking to true +/// and returns false. +/// \param buffer Location of the buffer to read to. +/// \param len Amount of bytes to read. +/// \returns True if the whole read was succesfull, false otherwise. +bool Socket::Connection::read(void * buffer, int len){ + int sofar = 0; + if (sock < 0){return false;} + while (sofar != len){ + int r = recv(sock, (char*)buffer + sofar, len-sofar, 0); + if (r < 0){ + switch (errno){ + case EWOULDBLOCK: return 0; break; + default: + Error = true; + #if DEBUG >= 2 + fprintf(stderr, "Could not read data! Error %i: %s\n", r, strerror(errno)); + #endif + close(); + break; + } + return false; + }else{ + if (r == 0){ + Error = true; + #if DEBUG >= 2 + fprintf(stderr, "Could not read data! Socket is closed.\n"); + #endif + close(); + return false; + } + sofar += r; + } + } + return true; +}//Socket::Connection::read + +/// Read call that is compatible with file access syntax. This function simply calls the other read function. +bool Socket::Connection::read(void * buffer, int width, int count){return read(buffer, width*count);} +/// Write call that is compatible with file access syntax. This function simply calls the other write function. +bool Socket::Connection::write(void * buffer, int width, int count){return write(buffer, width*count);} +/// Write call that is compatible with std::string. This function simply calls the other write function. +bool Socket::Connection::write(const std::string data){return write(data.c_str(), data.size());} + +/// Incremental write call. This function tries to write len bytes to the socket from the buffer, +/// returning the amount of bytes it actually wrote. +/// \param buffer Location of the buffer to write from. +/// \param len Amount of bytes to write. +/// \returns The amount of bytes actually written. +int Socket::Connection::iwrite(void * buffer, int len){ + if (sock < 0){return 0;} + int r = send(sock, buffer, len, 0); + if (r < 0){ + switch (errno){ + case EWOULDBLOCK: return 0; break; + default: + Error = true; + #if DEBUG >= 2 + fprintf(stderr, "Could not iwrite data! Error: %s\n", strerror(errno)); + #endif + close(); + return 0; + break; + } + } + if (r == 0){close();} + return r; +}//Socket::Connection::iwrite + +/// Incremental read call. This function tries to read len bytes to the buffer from the socket, +/// returning the amount of bytes it actually read. +/// \param buffer Location of the buffer to read to. +/// \param len Amount of bytes to read. +/// \returns The amount of bytes actually read. +int Socket::Connection::iread(void * buffer, int len){ + if (sock < 0){return 0;} + int r = recv(sock, buffer, len, 0); + if (r < 0){ + switch (errno){ + case EWOULDBLOCK: return 0; break; + default: + Error = true; + #if DEBUG >= 2 + fprintf(stderr, "Could not iread data! Error: %s\n", strerror(errno)); + #endif + close(); + return 0; + break; + } + } + if (r == 0){close();} + return r; +}//Socket::Connection::iread + +/// Read call that is compatible with std::string. +/// Data is read using iread (which is nonblocking if the Socket::Connection itself is), +/// then appended to end of buffer. This functions reads at least one byte before returning. +/// \param buffer std::string to append data to. +/// \return True if new data arrived, false otherwise. +bool Socket::Connection::read(std::string & buffer){ + char cbuffer[5000]; + if (!read(cbuffer, 1)){return false;} + int num = iread(cbuffer+1, 4999); + if (num > 0){ + buffer.append(cbuffer, num+1); + }else{ + buffer.append(cbuffer, 1); + } + return true; +}//read + +/// Read call that is compatible with std::string. +/// Data is read using iread (which is nonblocking if the Socket::Connection itself is), +/// then appended to end of buffer. +/// \param buffer std::string to append data to. +/// \return True if new data arrived, false otherwise. +bool Socket::Connection::iread(std::string & buffer){ + char cbuffer[5000]; + int num = iread(cbuffer, 5000); + if (num < 1){return false;} + buffer.append(cbuffer, num); + return true; +}//iread + +/// Incremental write call that is compatible with std::string. +/// Data is written using iwrite (which is nonblocking if the Socket::Connection itself is), +/// then removed from front of buffer. +/// \param buffer std::string to remove data from. +/// \return True if more data was sent, false otherwise. +bool Socket::Connection::iwrite(std::string & buffer){ + if (buffer.size() < 1){return false;} + int tmp = iwrite((void*)buffer.c_str(), buffer.size()); + if (tmp < 1){return false;} + buffer = buffer.substr(tmp); + return true; +}//iwrite + +/// Write call that is compatible with std::string. +/// Data is written using write (which is always blocking), +/// then removed from front of buffer. +/// \param buffer std::string to remove data from. +/// \return True if more data was sent, false otherwise. +bool Socket::Connection::swrite(std::string & buffer){ + if (buffer.size() < 1){return false;} + bool tmp = write((void*)buffer.c_str(), buffer.size()); + if (tmp){buffer = "";} + return tmp; +}//write + +/// Gets hostname for connection, if available. +std::string Socket::Connection::getHost(){ + return remotehost; +} + +/// Create a new base Server. The socket is never connected, and a placeholder for later connections. +Socket::Server::Server(){ + sock = -1; +}//Socket::Server base Constructor + +/// Create a new TCP Server. The socket is immediately bound and set to listen. +/// A maximum of 100 connections will be accepted between accept() calls. +/// Any further connections coming in will be dropped. +/// \param port The TCP port to listen on +/// \param hostname (optional) The interface to bind to. The default is 0.0.0.0 (all interfaces). +/// \param nonblock (optional) Whether accept() calls will be nonblocking. Default is false (blocking). +Socket::Server::Server(int port, std::string hostname, bool nonblock){ + sock = socket(AF_INET6, SOCK_STREAM, 0); + if (sock < 0){ + #if DEBUG >= 1 + fprintf(stderr, "Could not create socket! Error: %s\n", strerror(errno)); + #endif + return; + } + int on = 1; + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + if (nonblock){ + int flags = fcntl(sock, F_GETFL, 0); + flags |= O_NONBLOCK; + fcntl(sock, F_SETFL, flags); + } + struct sockaddr_in6 addr; + addr.sin6_family = AF_INET6; + addr.sin6_port = htons(port);//set port + if (hostname == "0.0.0.0"){ + addr.sin6_addr = in6addr_any; + }else{ + inet_pton(AF_INET6, hostname.c_str(), &addr.sin6_addr);//set interface, 0.0.0.0 (default) is all + } + int ret = bind(sock, (sockaddr*)&addr, sizeof(addr));//do the actual bind + if (ret == 0){ + ret = listen(sock, 100);//start listening, backlog of 100 allowed + if (ret == 0){ + return; + }else{ + #if DEBUG >= 1 + fprintf(stderr, "Listen failed! Error: %s\n", strerror(errno)); + #endif + close(); + return; + } + }else{ + #if DEBUG >= 1 + fprintf(stderr, "Binding failed! Error: %s\n", strerror(errno)); + #endif + close(); + return; + } +}//Socket::Server TCP Constructor + +/// Create a new Unix Server. The socket is immediately bound and set to listen. +/// A maximum of 100 connections will be accepted between accept() calls. +/// Any further connections coming in will be dropped. +/// The address used will first be unlinked - so it succeeds if the Unix socket already existed. Watch out for this behaviour - it will delete any file located at address! +/// \param address The location of the Unix socket to bind to. +/// \param nonblock (optional) Whether accept() calls will be nonblocking. Default is false (blocking). +Socket::Server::Server(std::string address, bool nonblock){ + unlink(address.c_str()); + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock < 0){ + #if DEBUG >= 1 + fprintf(stderr, "Could not create socket! Error: %s\n", strerror(errno)); + #endif + return; + } + if (nonblock){ + int flags = fcntl(sock, F_GETFL, 0); + flags |= O_NONBLOCK; + fcntl(sock, F_SETFL, flags); + } + sockaddr_un addr; + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, address.c_str(), address.size()+1); + int ret = bind(sock, (sockaddr*)&addr, sizeof(addr)); + if (ret == 0){ + ret = listen(sock, 100);//start listening, backlog of 100 allowed + if (ret == 0){ + return; + }else{ + #if DEBUG >= 1 + fprintf(stderr, "Listen failed! Error: %s\n", strerror(errno)); + #endif + close(); + return; + } + }else{ + #if DEBUG >= 1 + fprintf(stderr, "Binding failed! Error: %s\n", strerror(errno)); + #endif + close(); + return; + } +}//Socket::Server Unix Constructor + +/// Accept any waiting connections. If the Socket::Server is blocking, this function will block until there is an incoming connection. +/// If the Socket::Server is nonblocking, it might return a Socket::Connection that is not connected, so check for this. +/// \param nonblock (optional) Whether the newly connected socket should be nonblocking. Default is false (blocking). +/// \returns A Socket::Connection, which may or may not be connected, depending on settings and circumstances. +Socket::Connection Socket::Server::accept(bool nonblock){ + if (sock < 0){return Socket::Connection(-1);} + struct sockaddr_in6 addrinfo; + socklen_t len = sizeof(addrinfo); + static char addrconv[INET6_ADDRSTRLEN]; + int r = ::accept(sock, (sockaddr*)&addrinfo, &len); + //set the socket to be nonblocking, if requested. + //we could do this through accept4 with a flag, but that call is non-standard... + if ((r >= 0) && nonblock){ + int flags = fcntl(r, F_GETFL, 0); + flags |= O_NONBLOCK; + fcntl(r, F_SETFL, flags); + } + Socket::Connection tmp(r); + if (r < 0){ + if (errno != EWOULDBLOCK && errno != EAGAIN){close();} + }else{ + if (addrinfo.sin6_family == AF_INET6){ + tmp.remotehost = inet_ntop(AF_INET6, &(addrinfo.sin6_addr), addrconv, INET6_ADDRSTRLEN); + #if DEBUG >= 4 + printf("IPv6 addr: %s\n", tmp.remotehost.c_str()); + #endif + } + if (addrinfo.sin6_family == AF_INET){ + tmp.remotehost = inet_ntop(AF_INET, &(((sockaddr_in*)&addrinfo)->sin_addr), addrconv, INET6_ADDRSTRLEN); + #if DEBUG >= 4 + printf("IPv4 addr: %s\n", tmp.remotehost.c_str()); + #endif + } + if (addrinfo.sin6_family == AF_UNIX){ + #if DEBUG >= 4 + tmp.remotehost = ((sockaddr_un*)&addrinfo)->sun_path; + printf("Unix addr: %s\n", tmp.remotehost.c_str()); + #endif + tmp.remotehost = "UNIX_SOCKET"; + } + } + return tmp; +} + +/// Close connection. The internal socket is closed and then set to -1. +void Socket::Server::close(){ + shutdown(sock, SHUT_RDWR); + ::close(sock); + sock = -1; +}//Socket::Server::close + +/// Returns the connected-state for this socket. +/// Note that this function might be slightly behind the real situation. +/// The connection status is updated after every accept attempt, when errors occur +/// and when the socket is closed manually. +/// \returns True if socket is connected, false otherwise. +bool Socket::Server::connected(){ + return (sock >= 0); +}//Socket::Server::connected + +/// Returns internal socket number. +int Socket::Server::getSocket(){return sock;} diff --git a/util/socket.h b/util/socket.h new file mode 100644 index 00000000..2b982563 --- /dev/null +++ b/util/socket.h @@ -0,0 +1,65 @@ +/// \file socket.h +/// A handy Socket wrapper library. +/// Written by Jaron Vietor in 2010 for DDVTech + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +///Holds Socket tools. +namespace Socket{ + + /// This class is for easy communicating through sockets, either TCP or Unix. + class Connection{ + private: + int sock; ///< Internally saved socket number. + std::string remotehost; ///< Stores remote host address. + public: + Connection(); ///< Create a new disconnected base socket. + Connection(int sockNo); ///< Create a new base socket. + Connection(std::string adres, bool nonblock = false); ///< Create a new Unix Socket. + 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. + signed int ready(); ///< Returns the ready-state for this socket. + bool connected(); ///< Returns the connected-state for this socket. + bool read(void * buffer, int len); ///< Reads data from socket. + bool read(void * buffer, int width, int count); ///< Read call that is compatible with file access syntax. + bool write(const void * buffer, int len); ///< Writes data to socket. + bool write(void * buffer, int width, int count); ///< Write call that is compatible with file access syntax. + bool write(const std::string data); ///< Write call that is compatible with std::string. + int iwrite(void * buffer, int len); ///< Incremental write call. + int iread(void * buffer, int len); ///< Incremental read call. + bool read(std::string & buffer); ///< Read call that is compatible with std::string. + bool swrite(std::string & buffer); ///< Read call that is compatible with std::string. + bool iread(std::string & buffer); ///< Incremental write call that is compatible with std::string. + bool iwrite(std::string & buffer); ///< Write call that is compatible with std::string. + void close(); ///< Close connection. + std::string getHost(); ///< Gets hostname for connection, if available. + int getSocket(); ///< Returns internal socket number. + friend class Server; + }; + + /// This class is for easily setting up listening socket, either TCP or Unix. + class Server{ + private: + int sock; ///< Internally saved socket number. + public: + Server(); ///< Create a new base Server. + Server(int port, std::string hostname = "0.0.0.0", bool nonblock = false); ///< Create a new TCP Server. + Server(std::string adres, bool nonblock = false); ///< Create a new Unix Server. + Connection accept(bool nonblock = false); ///< Accept any waiting connections. + bool connected(); ///< Returns the connected-state for this socket. + void close(); ///< Close connection. + int getSocket(); ///< Returns internal socket number. + }; + +}; From 0cb87218d9d96eb0165fb0ce3c2cfad4b5da4e58 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Wed, 22 Jun 2011 02:18:58 +0200 Subject: [PATCH 130/788] Attempted RTMP fix 2 --- util/rtmpchunks.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/util/rtmpchunks.cpp b/util/rtmpchunks.cpp index a63a2380..9ab9aaa7 100644 --- a/util/rtmpchunks.cpp +++ b/util/rtmpchunks.cpp @@ -42,7 +42,7 @@ std::string RTMPStream::Chunk::Pack(){ unsigned int tmpi; unsigned char chtype = 0x00; timestamp -= firsttime; - if (prev.cs_id == cs_id){ + if ((prev.msg_type_id > 0) && (prev.cs_id == cs_id)){ if (msg_stream_id == prev.msg_stream_id){ chtype = 0x40;//do not send msg_stream_id if (len == prev.len){ @@ -292,6 +292,7 @@ bool RTMPStream::Chunk::Parse(std::string & indata){ break; case 0x40: if (indata.size() < i+7) return false; //can't read whole header + if (prev.msg_type_id == 0){fprintf(stderr, "Warning: Header type 0x40 with no valid previous chunk!\n");} timestamp = indata[i++]*256*256; timestamp += indata[i++]*256; timestamp += indata[i++]; @@ -305,6 +306,7 @@ bool RTMPStream::Chunk::Parse(std::string & indata){ break; case 0x80: if (indata.size() < i+3) return false; //can't read whole header + if (prev.msg_type_id == 0){fprintf(stderr, "Warning: Header type 0x80 with no valid previous chunk!\n");} timestamp = indata[i++]*256*256; timestamp += indata[i++]*256; timestamp += indata[i++]; @@ -315,6 +317,7 @@ bool RTMPStream::Chunk::Parse(std::string & indata){ msg_stream_id = prev.msg_stream_id; break; case 0xC0: + if (prev.msg_type_id == 0){fprintf(stderr, "Warning: Header type 0xC0 with no valid previous chunk!\n");} timestamp = prev.timestamp; len = prev.len; len_left = prev.len_left; From 72636e60e64736d49a7ea129b72b3965c07fadc2 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Mon, 1 Aug 2011 02:32:48 +0200 Subject: [PATCH 131/788] Socket lib output changed to stderr --- util/socket.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/util/socket.cpp b/util/socket.cpp index 60c3c69e..e3d9fb20 100644 --- a/util/socket.cpp +++ b/util/socket.cpp @@ -404,19 +404,19 @@ Socket::Connection Socket::Server::accept(bool nonblock){ if (addrinfo.sin6_family == AF_INET6){ tmp.remotehost = inet_ntop(AF_INET6, &(addrinfo.sin6_addr), addrconv, INET6_ADDRSTRLEN); #if DEBUG >= 4 - printf("IPv6 addr: %s\n", tmp.remotehost.c_str()); + fprintf(stderr,"IPv6 addr: %s\n", tmp.remotehost.c_str()); #endif } if (addrinfo.sin6_family == AF_INET){ tmp.remotehost = inet_ntop(AF_INET, &(((sockaddr_in*)&addrinfo)->sin_addr), addrconv, INET6_ADDRSTRLEN); #if DEBUG >= 4 - printf("IPv4 addr: %s\n", tmp.remotehost.c_str()); + fprintf(stderr,"IPv4 addr: %s\n", tmp.remotehost.c_str()); #endif } if (addrinfo.sin6_family == AF_UNIX){ #if DEBUG >= 4 tmp.remotehost = ((sockaddr_un*)&addrinfo)->sun_path; - printf("Unix addr: %s\n", tmp.remotehost.c_str()); + fprintf(stderr,"Unix addr: %s\n", tmp.remotehost.c_str()); #endif tmp.remotehost = "UNIX_SOCKET"; } From 7d3acba7378c96b78529c9bdfa154dcc3800da00 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 12 Aug 2011 02:03:07 +0200 Subject: [PATCH 132/788] Cleanup in TS dir (Erik! No binaries in the git! ), added getError() to socket lib, fixed Buffer problems when a Connector is being slow. --- util/socket.cpp | 37 ++++++++++++++++++++++++++++++++----- util/socket.h | 3 ++- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/util/socket.cpp b/util/socket.cpp index e3d9fb20..5bb23f9c 100644 --- a/util/socket.cpp +++ b/util/socket.cpp @@ -33,6 +33,11 @@ void Socket::Connection::close(){ /// Returns internal socket number. int Socket::Connection::getSocket(){return sock;} +/// Returns a string describing the last error that occured. +/// Simply calls strerror(errno) - not very reliable! +/// \todo Improve getError at some point to be more reliable and only report socket errors. +std::string Socket::Connection::getError(){return strerror(errno);} + /// Create a new Unix Socket. This socket will (try to) connect to the given address right away. /// \param address String containing the location of the Unix socket to connect to. /// \param nonblock Whether the socket should be nonblocking. False by default. @@ -86,7 +91,11 @@ signed int Socket::Connection::ready(){ } } if (r == 0){ - close(); return -1; + #if DEBUG >= 4 + fprintf(stderr, "Socket ready error - socket is closed.\n"); + #endif + close(); + return -1; } return r; } @@ -192,7 +201,12 @@ int Socket::Connection::iwrite(void * buffer, int len){ break; } } - if (r == 0){close();} + if (r == 0){ + #if DEBUG >= 4 + fprintf(stderr, "Could not iwrite data! Socket is closed.\n"); + #endif + close(); + } return r; }//Socket::Connection::iwrite @@ -217,7 +231,12 @@ int Socket::Connection::iread(void * buffer, int len){ break; } } - if (r == 0){close();} + if (r == 0){ + #if DEBUG >= 4 + fprintf(stderr, "Could not iread data! Socket is closed.\n"); + #endif + close(); + } return r; }//Socket::Connection::iread @@ -285,7 +304,7 @@ std::string Socket::Connection::getHost(){ Socket::Server::Server(){ sock = -1; }//Socket::Server base Constructor - + /// Create a new TCP Server. The socket is immediately bound and set to listen. /// A maximum of 100 connections will be accepted between accept() calls. /// Any further connections coming in will be dropped. @@ -399,7 +418,12 @@ Socket::Connection Socket::Server::accept(bool nonblock){ } Socket::Connection tmp(r); if (r < 0){ - if (errno != EWOULDBLOCK && errno != EAGAIN){close();} + if (errno != EWOULDBLOCK && errno != EAGAIN){ + #if DEBUG >= 1 + fprintf(stderr, "Error during accept - closing server socket.\n"); + #endif + close(); + } }else{ if (addrinfo.sin6_family == AF_INET6){ tmp.remotehost = inet_ntop(AF_INET6, &(addrinfo.sin6_addr), addrconv, INET6_ADDRSTRLEN); @@ -426,6 +450,9 @@ Socket::Connection Socket::Server::accept(bool nonblock){ /// Close connection. The internal socket is closed and then set to -1. void Socket::Server::close(){ + #if DEBUG >= 4 + fprintf(stderr, "ServerSocket closed.\n"); + #endif shutdown(sock, SHUT_RDWR); ::close(sock); sock = -1; diff --git a/util/socket.h b/util/socket.h index 2b982563..9aa754be 100644 --- a/util/socket.h +++ b/util/socket.h @@ -45,6 +45,7 @@ namespace Socket{ void close(); ///< Close connection. std::string getHost(); ///< Gets hostname for connection, if available. int getSocket(); ///< Returns internal socket number. + std::string getError(); ///< Returns a string describing the last error that occured. friend class Server; }; @@ -61,5 +62,5 @@ namespace Socket{ void close(); ///< Close connection. int getSocket(); ///< Returns internal socket number. }; - + }; From d76f7f5c46e2fefcaa8bcfdc51104d1094da8b93 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 12 Aug 2011 02:18:44 +0200 Subject: [PATCH 133/788] Removed Unix address information from Socket lib - useless --- util/socket.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/socket.cpp b/util/socket.cpp index 5bb23f9c..ecb1dab5 100644 --- a/util/socket.cpp +++ b/util/socket.cpp @@ -440,7 +440,7 @@ Socket::Connection Socket::Server::accept(bool nonblock){ if (addrinfo.sin6_family == AF_UNIX){ #if DEBUG >= 4 tmp.remotehost = ((sockaddr_un*)&addrinfo)->sun_path; - fprintf(stderr,"Unix addr: %s\n", tmp.remotehost.c_str()); + fprintf(stderr,"Unix socket, no address\n"); #endif tmp.remotehost = "UNIX_SOCKET"; } From 353bc8aa66732c477053049a3a9144c1f71e2224 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sat, 13 Aug 2011 03:01:33 +0200 Subject: [PATCH 134/788] Some edits to FLV parsing and RTMP fixes... not quite working yet (in Flash player, that is...) --- util/flv_tag.cpp | 25 +++++++++++++++++++++++-- util/flv_tag.h | 3 ++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/util/flv_tag.cpp b/util/flv_tag.cpp index 140183f3..f595bbff 100644 --- a/util/flv_tag.cpp +++ b/util/flv_tag.cpp @@ -17,7 +17,7 @@ std::string FLV::Error_Str = ""; /// - Not starting with the string "FLV". /// - The DataOffset is not 9 bytes. /// - The PreviousTagSize is not 0 bytes. -/// +/// /// Note that we see PreviousTagSize as part of the FLV header, not part of the tag header! bool FLV::check_header(char * header){ if (header[0] != 'F') return false; @@ -43,6 +43,27 @@ bool FLV::is_header(char * header){ return true; }//FLV::is_header +/// True if current tag is init data for this media type. +bool FLV::Tag::isInitData(){ + switch (data[0]){ + case 0x09: + switch (data[11] & 0xF0){ + case 0x50: return true; break; + } + if ((data[11] & 0x0F) == 7){ + switch (data[12]){ + case 0: return true; break; + } + } + break; + case 0x08: + if ((data[12] == 0) && ((data[11] & 0xF0) == 0xA0)){ + return true; + } + break; + } + return false; +} /// Returns a std::string describing the tag in detail. /// The string includes information about whether the tag is @@ -355,7 +376,7 @@ bool FLV::Tag::FileLoader(FILE * f){ int postflags = preflags | O_NONBLOCK; fcntl(fileno(f), F_SETFL, postflags); if (buf < 15){data = (char*)realloc(data, 15); buf = 15;} - + if (done){ //read a header if (FileReadUntil(data, 11, sofar, f)){ diff --git a/util/flv_tag.h b/util/flv_tag.h index 74d0ffa7..2ad39673 100644 --- a/util/flv_tag.h +++ b/util/flv_tag.h @@ -11,7 +11,7 @@ namespace FLV { 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". @@ -22,6 +22,7 @@ namespace FLV { int len; ///< Actual length of tag. bool isKeyframe; ///< True if current tag is a video keyframe. char * data; ///< Pointer to tag buffer. + bool isInitData(); ///< True if current tag is init data for this media type. std::string tagType(); ///< Returns a std::string describing the tag in detail. unsigned int tagTime(); ///< Returns the 32-bit timestamp of this tag. void tagTime(unsigned int T); ///< Sets the 32-bit timestamp of this tag. From 1f43dff92fdb0aab4b35a5725481380f38dce79a Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 14 Aug 2011 18:07:03 +0200 Subject: [PATCH 135/788] Attempt to make RTMP properly compatible with all codecs --- util/flv_tag.cpp | 25 +++++++++++++++++++++++++ util/flv_tag.h | 1 + 2 files changed, 26 insertions(+) diff --git a/util/flv_tag.cpp b/util/flv_tag.cpp index f595bbff..3be1f4e1 100644 --- a/util/flv_tag.cpp +++ b/util/flv_tag.cpp @@ -43,6 +43,31 @@ bool FLV::is_header(char * header){ return true; }//FLV::is_header +/// True if this media type requires init data. +/// Will always return false if the tag type is not 0x08 or 0x09. +/// Returns true for H263, AVC (H264), AAC. +/// \todo Check if MP3 does or does not require init data... +bool FLV::Tag::needsInitData(){ + switch (data[0]){ + case 0x09: + switch (data[11] & 0x0F){ + case 2: return true; break;//H263 requires init data + case 7: return true; break;//AVC requires init data + default: return false; break;//other formats do not + } + break; + case 0x08: + switch (data[11] & 0xF0){ + case 0x20: return false; break;//MP3 does not...? Unsure. + case 0xA0: return true; break;//AAC requires init data + case 0xE0: return false; break;//MP38kHz does not...? + default: return false; break;//other formats do not + } + break; + } + return false;//only audio/video can require init data +} + /// True if current tag is init data for this media type. bool FLV::Tag::isInitData(){ switch (data[0]){ diff --git a/util/flv_tag.h b/util/flv_tag.h index 2ad39673..1d7bbc41 100644 --- a/util/flv_tag.h +++ b/util/flv_tag.h @@ -22,6 +22,7 @@ namespace FLV { int len; ///< Actual length of tag. bool isKeyframe; ///< True if current tag is a video keyframe. char * data; ///< Pointer to tag buffer. + bool needsInitData(); ///< True if this media type requires init data. bool isInitData(); ///< True if current tag is init data for this media type. std::string tagType(); ///< Returns a std::string describing the tag in detail. unsigned int tagTime(); ///< Returns the 32-bit timestamp of this tag. From 85a4ed5d8ca05fc102ce199018e7528037fff7ec Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 21 Aug 2011 00:35:01 +0200 Subject: [PATCH 136/788] Added RTMPChunk to FLV converter to FLV lib, improved RTMP debugger to allow for dumping RTMP streams to FLV, improved RTMP debugger to show and work with more details --- util/flv_tag.cpp | 41 ++++++++++++++++++++++++++++++++++++++++- util/flv_tag.h | 3 +++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/util/flv_tag.cpp b/util/flv_tag.cpp index 3be1f4e1..cc44c24d 100644 --- a/util/flv_tag.cpp +++ b/util/flv_tag.cpp @@ -8,7 +8,10 @@ #include //malloc #include //memcpy -char FLV::Header[13]; ///< Holds the last FLV header parsed. +/// Holds the last FLV header parsed. +/// Defaults to a audio+video header on FLV version 0x01 if no header received yet. +char FLV::Header[13] = {'F', 'L', 'V', 0x01, 0x05, 0, 0, 0, 0x09, 0, 0, 0, 0}; + bool FLV::Parse_Error = false; ///< This variable is set to true if a problem is encountered while parsing the FLV. std::string FLV::Error_Str = ""; @@ -209,6 +212,16 @@ FLV::Tag::Tag(const Tag& O){ isKeyframe = O.isKeyframe; }//copy constructor + +/// Copy constructor from a RTMP chunk. +/// Copies the contents of a RTMP chunk into a valid FLV tag. +/// Exactly the same as making a chunk by through the default (empty) constructor +/// and then calling FLV::Tag::ChunkLoader with the chunk as argument. +FLV::Tag::Tag(const RTMPStream::Chunk& O){ + len = 0; buf = 0; data = 0; isKeyframe = false; done = true; sofar = 0; + ChunkLoader(O); +} + /// Assignment operator - works exactly like the copy constructor. /// This operator checks for self-assignment. FLV::Tag & FLV::Tag::operator= (const FLV::Tag& O){ @@ -231,6 +244,32 @@ FLV::Tag & FLV::Tag::operator= (const FLV::Tag& O){ return *this; }//assignment operator +/// FLV loader function from chunk. +/// Copies the contents and wraps it in a FLV header. +bool FLV::Tag::ChunkLoader(const RTMPStream::Chunk& O){ + len = O.len + 15; + if (len > 0){ + if (!data){ + data = (char*)malloc(len); + buf = len; + }else{ + if (buf < len){ + data = (char*)realloc(data, len); + buf = len; + } + } + memcpy(data+11, &(O.data[0]), O.len); + } + ((unsigned int *)(data+len-4))[0] = O.len; + data[0] = O.msg_type_id; + data[3] = O.len & 0xFF; + data[2] = (O.len >> 8) & 0xFF; + data[1] = (O.len >> 16) & 0xFF; + tagTime(O.timestamp); + return true; +} + + /// Helper function for FLV::MemLoader. /// This function will try to read count bytes from data buffer D into buffer. /// This function should be called repeatedly until true. diff --git a/util/flv_tag.h b/util/flv_tag.h index 1d7bbc41..862cdd68 100644 --- a/util/flv_tag.h +++ b/util/flv_tag.h @@ -4,6 +4,7 @@ #pragma once #include "socket.h" #include +#include "rtmpchunks.h" /// This namespace holds all FLV-parsing related functionality. namespace FLV { @@ -30,7 +31,9 @@ namespace FLV { 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); /// Date: Sun, 21 Aug 2011 17:27:38 +0200 Subject: [PATCH 137/788] Remove second streambegin from RTMP server, attempted fixes in SendUSR/SendCTL RTMP chunking functions --- util/rtmpchunks.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/util/rtmpchunks.cpp b/util/rtmpchunks.cpp index 9ab9aaa7..574bcf53 100644 --- a/util/rtmpchunks.cpp +++ b/util/rtmpchunks.cpp @@ -162,7 +162,7 @@ std::string RTMPStream::SendChunk(unsigned int cs_id, unsigned char msg_type_id, /// \param ts Timestamp of the media data, relative to current system time. std::string RTMPStream::SendMedia(unsigned char msg_type_id, unsigned char * data, int len, unsigned int ts){ RTMPStream::Chunk ch; - ch.cs_id = msg_type_id; + ch.cs_id = msg_type_id+42; ch.timestamp = ts; ch.len = len; ch.real_len = len; @@ -199,7 +199,7 @@ std::string RTMPStream::SendCTL(unsigned char type, unsigned int data, unsigned ch.msg_type_id = type; ch.msg_stream_id = 0; ch.data.resize(5); - *(int*)((char*)ch.data.c_str()) = htonl(data); + *(unsigned int*)((char*)ch.data.c_str()) = htonl(data); ch.data[4] = data2; return ch.Pack(); }//SendCTL @@ -215,7 +215,7 @@ std::string RTMPStream::SendUSR(unsigned char type, unsigned int data){ ch.msg_type_id = 4; ch.msg_stream_id = 0; ch.data.resize(6); - *(int*)((char*)ch.data.c_str()+2) = htonl(data); + *(unsigned int*)((char*)ch.data.c_str()+2) = htonl(data); ch.data[0] = 0; ch.data[1] = type; return ch.Pack(); @@ -232,8 +232,8 @@ std::string RTMPStream::SendUSR(unsigned char type, unsigned int data, unsigned ch.msg_type_id = 4; ch.msg_stream_id = 0; ch.data.resize(10); - *(int*)((char*)ch.data.c_str()+2) = htonl(data); - *(int*)((char*)ch.data.c_str()+6) = htonl(data2); + *(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(); @@ -270,7 +270,7 @@ bool RTMPStream::Chunk::Parse(std::string & indata){ cs_id = chunktype & 0x3F; break; } - + RTMPStream::Chunk prev = lastrecv[cs_id]; //process the rest of the header, for each chunk type @@ -344,7 +344,7 @@ bool RTMPStream::Chunk::Parse(std::string & indata){ timestamp += indata[i++]*256; timestamp += indata[i++]; } - + //read data if length > 0, and allocate it if (real_len > 0){ if (prev.len_left > 0){ @@ -397,22 +397,22 @@ bool RTMPStream::doHandshake(){ uint8_t _validationScheme = 5; if (ValidateClientScheme(Client, 0)) _validationScheme = 0; if (ValidateClientScheme(Client, 1)) _validationScheme = 1; - + #if DEBUG >= 4 fprintf(stderr, "Handshake type is %hhi, encryption is %s\n", _validationScheme, encrypted?"on":"off"); #endif - + //FIRST 1536 bytes from server response //compute DH key position uint32_t serverDHOffset = GetDHOffset(Server, _validationScheme); uint32_t clientDHOffset = GetDHOffset(Client, _validationScheme); - + //generate DH key DHWrapper dhWrapper(1024); if (!dhWrapper.Initialize()) return false; if (!dhWrapper.CreateSharedKey(Client + clientDHOffset, 128)) return false; if (!dhWrapper.CopyPublicKey(Server + serverDHOffset, 128)) return false; - + if (encrypted) { uint8_t secretKey[128]; if (!dhWrapper.CopySharedKey(secretKey, sizeof (secretKey))) return false; @@ -433,7 +433,7 @@ bool RTMPStream::doHandshake(){ memcpy(Server + serverDigestOffset, pTempHash, 32); delete[] pTempBuffer; delete[] pTempHash; - + //SECOND 1536 bytes from server response uint32_t keyChallengeIndex = GetDigestOffset(Client, _validationScheme); pTempHash = new uint8_t[512]; From 693b9e47e5d47a1ef94f16400a968ada51acbcbe Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 21 Aug 2011 18:45:59 +0200 Subject: [PATCH 138/788] Some RTMP timestamp issues found and fixed, as well as some RTMP Parser fixes (but now segfaults? Needs more testing...) --- util/rtmpchunks.cpp | 14 ++++++++------ util/rtmpchunks.h | 1 + 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/util/rtmpchunks.cpp b/util/rtmpchunks.cpp index 574bcf53..f6f11076 100644 --- a/util/rtmpchunks.cpp +++ b/util/rtmpchunks.cpp @@ -42,6 +42,7 @@ std::string RTMPStream::Chunk::Pack(){ unsigned int tmpi; unsigned char chtype = 0x00; timestamp -= firsttime; + if (timestamp < prev.timestamp){timestamp = prev.timestamp;} if ((prev.msg_type_id > 0) && (prev.cs_id == cs_id)){ if (msg_stream_id == prev.msg_stream_id){ chtype = 0x40;//do not send msg_stream_id @@ -215,7 +216,7 @@ std::string RTMPStream::SendUSR(unsigned char type, unsigned int data){ ch.msg_type_id = 4; ch.msg_stream_id = 0; ch.data.resize(6); - *(unsigned int*)((char*)ch.data.c_str()+2) = htonl(data); + *(unsigned int*)(((char*)ch.data.c_str())+2) = htonl(data); ch.data[0] = 0; ch.data[1] = type; return ch.Pack(); @@ -232,8 +233,8 @@ std::string RTMPStream::SendUSR(unsigned char type, unsigned int data, unsigned 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); + *(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(); @@ -274,7 +275,8 @@ bool RTMPStream::Chunk::Parse(std::string & indata){ RTMPStream::Chunk prev = lastrecv[cs_id]; //process the rest of the header, for each chunk type - switch (chunktype & 0xC0){ + headertype = chunktype & 0xC0; + switch (headertype){ case 0x00: if (indata.size() < i+11) return false; //can't read whole header timestamp = indata[i++]*256*256; @@ -296,7 +298,7 @@ bool RTMPStream::Chunk::Parse(std::string & indata){ timestamp = indata[i++]*256*256; timestamp += indata[i++]*256; timestamp += indata[i++]; - timestamp += prev.timestamp; + if (timestamp != 0x00ffffff){timestamp += prev.timestamp;} len = indata[i++]*256*256; len += indata[i++]*256; len += indata[i++]; @@ -310,7 +312,7 @@ bool RTMPStream::Chunk::Parse(std::string & indata){ timestamp = indata[i++]*256*256; timestamp += indata[i++]*256; timestamp += indata[i++]; - timestamp += prev.timestamp; + if (timestamp != 0x00ffffff){timestamp += prev.timestamp;} len = prev.len; len_left = prev.len_left; msg_type_id = prev.msg_type_id; diff --git a/util/rtmpchunks.h b/util/rtmpchunks.h index 8feeefb0..b114f440 100644 --- a/util/rtmpchunks.h +++ b/util/rtmpchunks.h @@ -30,6 +30,7 @@ namespace RTMPStream{ /// Holds a single RTMP chunk, either send or receive direction. class Chunk{ public: + unsigned char headertype; ///< For input chunks, the type of header. This is calculated automatically for output chunks. unsigned int cs_id; ///< ContentStream ID unsigned int timestamp; ///< Timestamp of this chunk. unsigned int len; ///< Length of the complete chunk. From d526616cd32824fe791ce1a5390fe81397b83bba Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 21 Aug 2011 20:49:42 +0200 Subject: [PATCH 139/788] Attempts at fixing timestamp issues, improved FLV/RTMP cooperatibility, fixed several bugs --- util/flv_tag.cpp | 1 + util/flv_tag.h | 6 +++++- util/rtmpchunks.cpp | 40 ++++++++++++++++++++++++++++------------ util/rtmpchunks.h | 6 ++++++ 4 files changed, 40 insertions(+), 13 deletions(-) diff --git a/util/flv_tag.cpp b/util/flv_tag.cpp index cc44c24d..526de7d0 100644 --- a/util/flv_tag.cpp +++ b/util/flv_tag.cpp @@ -2,6 +2,7 @@ /// Holds all code for the FLV namespace. #include "flv_tag.h" +#include "rtmpchunks.h" #include //for Tag::FileLoader #include //for Tag::FileLoader #include //for Tag::FileLoader diff --git a/util/flv_tag.h b/util/flv_tag.h index 862cdd68..1350c870 100644 --- a/util/flv_tag.h +++ b/util/flv_tag.h @@ -4,7 +4,11 @@ #pragma once #include "socket.h" #include -#include "rtmpchunks.h" + +//forward declaration of RTMPStream::Chunk to avoid circular dependencies. +namespace RTMPStream{ + class Chunk; +}; /// This namespace holds all FLV-parsing related functionality. namespace FLV { diff --git a/util/rtmpchunks.cpp b/util/rtmpchunks.cpp index f6f11076..ad0e6c57 100644 --- a/util/rtmpchunks.cpp +++ b/util/rtmpchunks.cpp @@ -2,6 +2,7 @@ /// Holds all code for the RTMPStream namespace. #include "rtmpchunks.h" +#include "flv_tag.h" #include "crypto.h" char versionstring[] = "WWW.DDVTECH.COM "; ///< String that is repeated in the RTMP handshake @@ -41,8 +42,8 @@ std::string RTMPStream::Chunk::Pack(){ RTMPStream::Chunk prev = lastsend[cs_id]; unsigned int tmpi; unsigned char chtype = 0x00; - timestamp -= firsttime; - if (timestamp < prev.timestamp){timestamp = prev.timestamp;} + //timestamp -= firsttime; + //if (timestamp < prev.timestamp){timestamp = prev.timestamp;} if ((prev.msg_type_id > 0) && (prev.cs_id == cs_id)){ if (msg_stream_id == prev.msg_stream_id){ chtype = 0x40;//do not send msg_stream_id @@ -77,15 +78,15 @@ std::string RTMPStream::Chunk::Pack(){ tmpi = timestamp - prev.timestamp; } if (tmpi >= 0x00ffffff){ntime = tmpi; tmpi = 0x00ffffff;} - output += (unsigned char)(tmpi / (256*256)); - output += (unsigned char)(tmpi / 256); - output += (unsigned char)(tmpi % 256); + 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 / (256*256)); - output += (unsigned char)(tmpi / 256); - output += (unsigned char)(tmpi % 256); + 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){ @@ -99,10 +100,10 @@ std::string RTMPStream::Chunk::Pack(){ } //support for 0x00ffffff timestamps if (ntime){ - output += (unsigned char)(ntime % 256); - output += (unsigned char)(ntime / 256); - output += (unsigned char)(ntime / (256*256)); - output += (unsigned char)(ntime / (256*256*256)); + 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){ @@ -174,6 +175,21 @@ std::string RTMPStream::SendMedia(unsigned char msg_type_id, unsigned char * dat return ch.Pack(); }//SendMedia +/// Packs up a chunk with media contents. +/// \param tag FLV::Tag with media to send. +std::string RTMPStream::SendMedia(FLV::Tag & tag){ + RTMPStream::Chunk ch; + ch.cs_id = ((unsigned char)tag.data[0]); + ch.timestamp = tag.tagTime(); + ch.len = tag.len-15; + ch.real_len = tag.len-15; + ch.len_left = 0; + ch.msg_type_id = (unsigned char)tag.data[0]; + ch.msg_stream_id = 1; + ch.data.append(tag.data+11, (size_t)(tag.len-15)); + return ch.Pack(); +}//SendMedia + /// Packs up a chunk for a control message with 1 argument. std::string RTMPStream::SendCTL(unsigned char type, unsigned int data){ RTMPStream::Chunk ch; diff --git a/util/rtmpchunks.h b/util/rtmpchunks.h index b114f440..ff4eee4a 100644 --- a/util/rtmpchunks.h +++ b/util/rtmpchunks.h @@ -9,6 +9,11 @@ #include #include +//forward declaration of FLV::Tag to avoid circular dependencies. +namespace FLV{ + class Tag; +}; + /// Contains all functions and classes needed for RTMP connections. namespace RTMPStream{ @@ -51,6 +56,7 @@ namespace RTMPStream{ 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); From 005f52a6423a9ca6bf71cadd7d0eedee8f3f1a39 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 21 Aug 2011 21:03:21 +0200 Subject: [PATCH 140/788] More timestamp attempts... --- util/rtmpchunks.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/util/rtmpchunks.cpp b/util/rtmpchunks.cpp index ad0e6c57..6aa5f242 100644 --- a/util/rtmpchunks.cpp +++ b/util/rtmpchunks.cpp @@ -43,7 +43,6 @@ std::string RTMPStream::Chunk::Pack(){ unsigned int tmpi; unsigned char chtype = 0x00; //timestamp -= firsttime; - //if (timestamp < prev.timestamp){timestamp = prev.timestamp;} if ((prev.msg_type_id > 0) && (prev.cs_id == cs_id)){ if (msg_stream_id == prev.msg_stream_id){ chtype = 0x40;//do not send msg_stream_id @@ -56,6 +55,8 @@ std::string RTMPStream::Chunk::Pack(){ } } } + //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); From fee658cf1d5e388ee8d9f12e173978120dd27410 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 12 Sep 2011 16:48:18 +0200 Subject: [PATCH 141/788] First version DTSC support (still untested), fixed /TODO to /todo in RTMP main.cpp --- util/dtmi.cpp | 248 ++++++++++++++++++++++++++++++++++++++++++++++++++ util/dtmi.h | 58 ++++++++++++ util/dtsc.cpp | 58 ++++++++++++ util/dtsc.h | 47 ++++++++++ 4 files changed, 411 insertions(+) create mode 100644 util/dtmi.cpp create mode 100644 util/dtmi.h create mode 100644 util/dtsc.cpp create mode 100644 util/dtsc.h diff --git a/util/dtmi.cpp b/util/dtmi.cpp new file mode 100644 index 00000000..a9d38299 --- /dev/null +++ b/util/dtmi.cpp @@ -0,0 +1,248 @@ +/// \file dtmi.cpp +/// Holds all code for DDVTECH MediaInfo parsing/generation. + +#include "dtmi.h" +#include //needed for stderr only + +/// Returns the std::string Indice for the current object, if available. +/// Returns an empty string if no indice exists. +std::string DTSC::DTMI::Indice(){return myIndice;}; + +/// Returns the DTSC::DTMItype AMF0 object type for this object. +DTSC::DTMItype DTSC::DTMI::GetType(){return myType;}; + +/// Returns the numeric value of this object, if available. +/// If this object holds no numeric value, 0 is returned. +uint64_t DTSC::DTMI::NumValue(){return numval;}; + +/// Returns the std::string value of this object, if available. +/// If this object holds no string value, an empty string is returned. +std::string DTSC::DTMI::StrValue(){return strval;}; + +/// Returns the C-string value of this object, if available. +/// If this object holds no string value, an empty C-string is returned. +const char * DTSC::DTMI::Str(){return strval.c_str();}; + +/// Returns a count of the amount of objects this object currently holds. +/// If this object is not a container type, this function will always return 0. +int DTSC::DTMI::hasContent(){return contents.size();}; + +/// Adds an DTSC::DTMI to this object. Works for all types, but only makes sense for container types. +void DTSC::DTMI::addContent(DTSC::DTMI c){contents.push_back(c);}; + +/// Returns a pointer to the object held at indice i. +/// Returns AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice. +/// \param i The indice of the object in this container. +DTSC::DTMI* DTSC::DTMI::getContentP(int i){return &contents.at(i);}; + +/// Returns a copy of the object held at indice i. +/// Returns a AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice. +/// \param i The indice of the object in this container. +DTSC::DTMI DTSC::DTMI::getContent(int i){return contents.at(i);}; + +/// Returns a pointer to the object held at indice s. +/// Returns NULL if no object is held at this indice. +/// \param s The indice of the object in this container. +DTSC::DTMI* DTSC::DTMI::getContentP(std::string s){ + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ + if (it->Indice() == s){return &(*it);} + } + return 0; +}; + +/// Returns a copy of the object held at indice s. +/// Returns a AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice. +/// \param s The indice of the object in this container. +DTSC::DTMI DTSC::DTMI::getContent(std::string s){ + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ + if (it->Indice() == s){return *it;} + } + return DTSC::DTMI("error", DTMI::DTMI_ROOT); +}; + +/// Default constructor. +/// Simply fills the data with DTSC::DTMI("error", AMF0_DDV_CONTAINER) +DTSC::DTMI::DTMI(){ + *this = DTSC::DTMI("error", DTMI::DTMI_ROOT); +};//default constructor + +/// Constructor for numeric objects. +/// The object type is by default AMF::AMF0_NUMBER, but this can be forced to a different value. +/// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. +/// \param val The numeric value of this object. Numeric AMF0 objects only support double-type values. +/// \param setType The object type to force this object to. +DTSC::DTMI::DTMI(std::string indice, double val, DTSC::DTMItype setType){//num type initializer + myIndice = indice; + myType = setType; + strval = ""; + numval = val; +}; + +/// Constructor for string objects. +/// The object type is by default AMF::AMF0_STRING, but this can be forced to a different value. +/// There is no need to manually change the type to AMF::AMF0_LONGSTRING, this will be done automatically. +/// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. +/// \param val The string value of this object. +/// \param setType The object type to force this object to. +DTSC::DTMI::DTMI(std::string indice, std::string val, DTSC::DTMItype setType){//str type initializer + myIndice = indice; + myType = setType; + strval = val; + numval = 0; +}; + +/// Constructor for container objects. +/// The object type is by default AMF::AMF0_OBJECT, but this can be forced to a different value. +/// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. +/// \param setType The object type to force this object to. +DTSC::DTMI::DTMI(std::string indice, DTSC::DTMItype setType){//object type initializer + myIndice = indice; + myType = setType; + strval = ""; + numval = 0; +}; + +/// Prints the contents of this object to std::cerr. +/// If this object contains other objects, it will call itself recursively +/// and print all nested content in a nice human-readable format. +void DTSC::DTMI::Print(std::string indent){ + std::cerr << indent; + // print my type + switch (myType){ + case DTMItype::DTMI_INT: std::cerr << "Integer"; break; + case DTMItype::DTMI_STRING: std::cerr << "String"; break; + case DTMItype::DTMI_OBJECT: std::cerr << "Object"; break; + case DTMItype::DTMI_OBJ_END: std::cerr << "Object end"; break; + case DTMItype::DTMI_ROOT: std::cerr << "Root Node"; break; + } + // print my string indice, if available + std::cerr << " " << myIndice << " "; + // print my numeric or string contents + switch (myType){ + case DTMItype::DTMI_INT: std::cerr << numval; break; + case DTMItype::DTMI_STRING: std::cerr << strval; break; + default: break;//we don't care about the rest, and don't want a compiler warning... + } + std::cerr << std::endl; + // if I hold other objects, print those too, recursively. + if (contents.size() > 0){ + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){it->Print(indent+" ");} + } +};//print + +/// Packs the AMF object to a std::string for transfer over the network. +/// If the object is a container type, this function will call itself recursively and contain all contents. +std::string DTSC::DTMI::Pack(){ + std::string r = ""; + //skip output of DDV container types, they do not exist. Only output their contents. + if (myType != DTMItype::DTMI_ROOT){r += myType;} + //output the properly formatted data stream for this object's contents. + switch (myType){ + case DTMItype::DTMI_INT: + r += *(((char*)&numval)+7); r += *(((char*)&numval)+6); + r += *(((char*)&numval)+5); r += *(((char*)&numval)+4); + r += *(((char*)&numval)+3); r += *(((char*)&numval)+2); + r += *(((char*)&numval)+1); r += *(((char*)&numval)); + break; + case DTMItype::DTMI_STRING: + r += strval.size() / (256*256*256); + r += strval.size() / (256*256); + r += strval.size() / 256; + r += strval.size() % 256; + r += strval; + break; + case DTMItype::DTMI_OBJECT: + if (contents.size() > 0){ + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ + r += it->Indice().size() / 256; + r += it->Indice().size() % 256; + r += it->Indice(); + r += it->Pack(); + } + } + r += (char)0; r += (char)0; r += (char)9; + break; + case DTMItype::DTMI_ROOT://only send contents + if (contents.size() > 0){ + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ + r += it->Pack(); + } + } + break; + } + return r; +};//pack + +/// Parses a single AMF0 type - used recursively by the AMF::parse() functions. +/// This function updates i every call with the new position in the data. +/// \param data The raw data to parse. +/// \param len The size of the raw data. +/// \param i Current parsing position in the raw data. +/// \param name Indice name for any new object created. +/// \returns A single DTSC::DTMI, parsed from the raw data. +DTSC::DTMI DTSC::parseOneDTMI(const unsigned char *& data, unsigned int &len, unsigned int &i, std::string name){ + std::string tmpstr; + unsigned int tmpi = 0; + unsigned char tmpdbl[8]; + #if DEBUG >= 10 + fprintf(stderr, "Note: AMF type %hhx found. %i bytes left\n", data[i], len-i); + #endif + switch (data[i]){ + case DTMI::DTMI_INT: + tmpdbl[7] = data[i+1]; + tmpdbl[6] = data[i+2]; + tmpdbl[5] = data[i+3]; + tmpdbl[4] = data[i+4]; + tmpdbl[3] = data[i+5]; + tmpdbl[2] = data[i+6]; + tmpdbl[1] = data[i+7]; + tmpdbl[0] = data[i+8]; + i+=9;//skip 8(a double)+1 forwards + return DTSC::DTMI(name, *(uint64_t*)tmpdbl, AMF::AMF0_NUMBER); + break; + case DTMI::DTMI_STRING: + tmpi = data[i+1]*256*256*256+data[i+2]*256*256+data[i+3]*256+data[i+4];//set tmpi to UTF-8-long length + tmpstr.clear();//clean tmpstr, just to be sure + tmpstr.append((const char *)data+i+5, (size_t)tmpi);//add the string data + i += tmpi + 5;//skip length+size+1 forwards + return DTSC::DTMI(name, tmpstr, AMF::AMF0_LONGSTRING); + break; + case DTMI::DTMI_OBJECT:{ + ++i; + DTSC::DTMI ret(name, DTMI::DTMI_OBJECT); + while (data[i] + data[i+1] != 0){//while not encountering 0x0000 (we assume 0x000009) + tmpi = data[i]*256+data[i+1];//set tmpi to the UTF-8 length + tmpstr.clear();//clean tmpstr, just to be sure + tmpstr.append((const char*)data+i+2, (size_t)tmpi);//add the string data + i += tmpi + 2;//skip length+size forwards + ret.addContent(AMF::parseOne(data, len, i, tmpstr));//add content, recursively parsed, updating i, setting indice to tmpstr + } + i += 3;//skip 0x000009 + return ret; + } break; + } + #if DEBUG >= 2 + fprintf(stderr, "Error: Unimplemented DTMI type %hhx - returning.\n", data[i]); + #endif + return DTSC::DTMI("error", DTMI::DTMI_ROOT); +}//parseOne + +/// Parses a C-string to a valid DTSC::DTMI. +/// This function will find all AMF objects in the string and return +/// them all packed in a single AMF::AMF0_DDV_CONTAINER DTSC::DTMI. +DTSC::DTMI DTSC::parseDTMI(const unsigned char * data, unsigned int len){ + DTSC::DTMI ret("returned", DTMI::DTMI_ROOT);//container type + unsigned int i = 0, j = 0; + while (i < len){ + ret.addContent(AMF::parseOne(data, len, i, "")); + if (i > j){j = i;}else{return ret;} + } + return ret; +}//parse + +/// Parses a std::string to a valid DTSC::DTMI. +/// This function will find all AMF objects in the string and return +/// them all packed in a single AMF::AMF0_DDV_CONTAINER DTSC::DTMI. +DTSC::DTMI DTSC::parseDTMI(std::string data){ + return parse((const unsigned char*)data.c_str(), data.size()); +}//parse diff --git a/util/dtmi.h b/util/dtmi.h new file mode 100644 index 00000000..410df97d --- /dev/null +++ b/util/dtmi.h @@ -0,0 +1,58 @@ +/// \file dtmi.h +/// Holds all headers for DDVTECH MediaInfo parsing/generation. + +#pragma once +#include +#include +//#include +#include + +/// Holds all DDVTECH Stream Container classes and parsers. +namespace DTSC{ + + /// Enumerates all possible DTMI types. + enum DTMItype { + DTMI_INT = 0x01, ///< Unsigned 64-bit integer. + DTMI_STRING = 0x02, ///< String, equivalent to the AMF longstring type. + DTMI_OBJECT = 0xE0, ///< Object, equivalent to the AMF object type. + DTMI_OBJ_END = 0xEE, ///< End of object marker. + DTMI_ROOT = 0xFF ///< Root node for all DTMI data. + }; + + /// Recursive class that holds DDVTECH MediaInfo. + class DTMI { + public: + std::string Indice(); + DTMItype GetType(); + uint64_t NumValue(); + std::string StrValue(); + const char * Str(); + int hasContent(); + void addContent(DTMI c); + DTMI* getContentP(int i); + DTMI getContent(int i); + DTMI* getContentP(std::string s); + DTMI getContent(std::string s); + DTMI(); + DTMI(std::string indice, double val, DTMItype setType = DTMI_INT); + DTMI(std::string indice, std::string val, DTMItype setType = DTMI_STRING); + DTMI(std::string indice, DTMItype setType = DTMI_OBJECT); + void Print(std::string indent = ""); + std::string Pack(); + protected: + std::string myIndice; ///< Holds this objects indice, if any. + DTMItype myType; ///< Holds this objects AMF0 type. + std::string strval; ///< Holds this objects string value, if any. + uint64_t numval; ///< Holds this objects numeric value, if any. + std::vector contents; ///< Holds this objects contents, if any (for container types). + };//AMFType + + /// Parses a C-string to a valid DTSC::DTMI. + DTMI parseDTMI(const unsigned char * data, unsigned int len); + /// Parses a std::string to a valid DTSC::DTMI. + DTMI parseDTMI(std::string data); + /// Parses a single DTMI type - used recursively by the DTSC::parseDTMI() functions. + DTMI parseOneDTMI(const unsigned char *& data, unsigned int &len, unsigned int &i, std::string name); + +};//DTSC namespace + diff --git a/util/dtsc.cpp b/util/dtsc.cpp new file mode 100644 index 00000000..bc722ecf --- /dev/null +++ b/util/dtsc.cpp @@ -0,0 +1,58 @@ +/// \file dtsc.cpp +/// Holds all code for DDVTECH Stream Container parsing/generation. + +#include "dtsc.h" +#include "string.h" //for memcmp +#include "arpa/inet.h" //for htonl/ntohl + +char * DTSC::Magic_Header = "DTSC"; +char * DTSC::Magic_Packet = "DTPD"; + +DTSC::Stream::Stream(){ + datapointer = 0; +} + +bool DTSC::Stream::parsePacket(std::string & buffer){ + uint32_t len; + if (buffer.length() > 8){ + if (memcmp(buffer.c_str(), DTSC::Magic_Header, 4) == 0){ + len = ntohl(((uint32_t *)buffer.c_str())[1]); + if (buffer.length() < len+8){return false;} + metadata = DTSC::parseDTMI(buffer.c_str() + 8, len); + } + if (memcmp(buffer.c_str(), DTSC::Magic_Packet, 4) == 0){ + len = ntohl(((uint32_t *)buffer.c_str())[1]); + if (buffer.length() < len+8){return false;} + lastPacket = DTSC::parseDTMI(buffer.c_str() + 8, len); + datapointertype = INVALID; + if (lastPacket.getContentP("data")){ + datapointer = lastPacket.getContentP("data")->StrValue.c_str(); + if (lastPacket.getContentP("datatype")){ + std::string tmp = lastPacket.getContentP("datatype")->StrValue(); + if (tmp == "video"){datapointertype = VIDEO;} + if (tmp == "audio"){datapointertype = AUDIO;} + if (tmp == "meta"){datapointertype = META;} + } + }else{ + datapointer = 0; + } + } + } + return false; +} + +char * DTSC::Stream::lastData(){ + return datapointer; +} + +DTSC::datatype DTSC::Stream::lastType(){ + return datapointertype; +} + +bool DTSC::Stream::hasVideo(){ + return (metadata.getContentP("video") != 0); +} + +bool DTSC::Stream::hasAudio(){ + return (metadata.getContentP("audio") != 0); +} diff --git a/util/dtsc.h b/util/dtsc.h new file mode 100644 index 00000000..6b918f30 --- /dev/null +++ b/util/dtsc.h @@ -0,0 +1,47 @@ +/// \file dtsc.h +/// Holds all headers for DDVTECH Stream Container parsing/generation. + +#pragma once +#include "dtmi.h" + +// Video: +// Codec (string) + +// Audio: +// Codec (string) +// Samping rate (int, Hz) +// Sample Size (int, bytesize) +// Channels (int, channelcount) + +namespace DTSC{ + + /// This enum holds all possible datatypes for DTSC packets. + enum datatype { + AUDIO, ///< Stream Audio data + VIDEO, ///< Stream Video data + META, ///< Stream Metadata + INVALID ///< Anything else or no data available. + } + + char * Magic_Header; ///< The magic bytes for a DTSC header + char * Magic_Packet; ///< The magic bytes for a DTSC packet + + /// Holds temporary data for a DTSC stream and provides functions to access/store it. + class Stream { + public: + Stream(); + DTSC::DTMI metadata; + DRSC::DTMI lastPacket; + datatype lastType(); + char * lastData(); + bool hasVideo(); + bool hasAudio(); + bool parsePacket(std::string & buffer); + private: + char * datapointer; + datatype datapointertype; + } + + + +}; From 3aa7e4d114af9f28190f2e964080a01ee4da5b68 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 22 Sep 2011 06:56:39 +0200 Subject: [PATCH 142/788] Removing epoll in favor of more cross-platform poll - also adding RTMP push support and Buffer push support with IP security --- util/socket.cpp | 26 ++++++++++++++++++++++++++ util/socket.h | 2 ++ 2 files changed, 28 insertions(+) diff --git a/util/socket.cpp b/util/socket.cpp index ecb1dab5..b0046d02 100644 --- a/util/socket.cpp +++ b/util/socket.cpp @@ -3,6 +3,11 @@ /// Written by Jaron Vietor in 2010 for DDVTech #include "socket.h" +#include + +#ifdef __FreeBSD__ +#include +#endif /// Create a new base socket. This is a basic constructor for converting any valid socket to a Socket::Connection. /// \param sockNo Integer representing the socket to convert. @@ -69,6 +74,27 @@ Socket::Connection::Connection(std::string address, bool nonblock){ } }//Socket::Connection Unix Contructor +/// Calls poll() on the socket, checking if data is available. +/// This function may return true even if there is no data, but never returns false when there is. +bool Socket::Connection::canRead(){ + struct pollfd PFD; + PFD.fd = sock; + PFD.events = POLLIN; + PFD.revents = 0; + poll(&PFD, 1, 5); + return (PFD.revents & POLLIN) == POLLIN; +} +/// Calls poll() on the socket, checking if data can be written. +bool Socket::Connection::canWrite(){ + struct pollfd PFD; + PFD.fd = sock; + PFD.events = POLLOUT; + PFD.revents = 0; + poll(&PFD, 1, 5); + return (PFD.revents & POLLOUT) == POLLOUT; +} + + /// Returns the ready-state for this socket. /// \returns 1 if data is waiting to be read, -1 if not connected, 0 otherwise. signed int Socket::Connection::ready(){ diff --git a/util/socket.h b/util/socket.h index 9aa754be..57aca183 100644 --- a/util/socket.h +++ b/util/socket.h @@ -27,6 +27,8 @@ namespace Socket{ Connection(); ///< Create a new disconnected base socket. Connection(int sockNo); ///< Create a new base socket. Connection(std::string adres, bool nonblock = false); ///< Create a new Unix Socket. + bool canRead(); ///< Calls poll() on the socket, checking if data is available. + bool canWrite(); ///< Calls poll() on the socket, checking if data can be written. 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. signed int ready(); ///< Returns the ready-state for this socket. From 86e7d69e09fdab53f67a3dd53bdf692e099cfd5b Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 25 Sep 2011 22:44:55 +0200 Subject: [PATCH 143/788] Epoll removal tested and found working! :D --- util/socket.cpp | 24 ++++++++++++++++++++++++ util/socket.h | 2 ++ 2 files changed, 26 insertions(+) diff --git a/util/socket.cpp b/util/socket.cpp index b0046d02..425d2a70 100644 --- a/util/socket.cpp +++ b/util/socket.cpp @@ -495,3 +495,27 @@ bool Socket::Server::connected(){ /// Returns internal socket number. int Socket::Server::getSocket(){return sock;} + +/// Unescapes URLencoded C-strings to a std::string. +/// This function *will* destroy the incoming data! +std::string Socket::Connection::urlunescape(char *s){ + char *p; + for (p = s; *s != '\0'; ++s){ + if (*s == '%'){ + if (*++s != '\0'){ + *p = unhex(*s) << 4; + } + if (*++s != '\0'){ + *p++ += unhex(*s); + } + } else { + if (*s == '+'){*p++ = ' ';}else{*p++ = *s;} + } + } + *p = '\0'; + return std::string(s); +} + +int Socket::Connection::unhex(char c){ + return( c >= '0' && c <= '9' ? c - '0' : c >= 'A' && c <= 'F' ? c - 'A' + 10 : c - 'a' + 10 ); +} diff --git a/util/socket.h b/util/socket.h index 57aca183..456a162f 100644 --- a/util/socket.h +++ b/util/socket.h @@ -23,6 +23,7 @@ namespace Socket{ private: int sock; ///< Internally saved socket number. std::string remotehost; ///< Stores remote host address. + int unhex(char c); ///< Helper function for urlunescape. public: Connection(); ///< Create a new disconnected base socket. Connection(int sockNo); ///< Create a new base socket. @@ -44,6 +45,7 @@ namespace Socket{ bool swrite(std::string & buffer); ///< Read call that is compatible with std::string. bool iread(std::string & buffer); ///< Incremental write call that is compatible with std::string. bool iwrite(std::string & buffer); ///< Write call that is compatible with std::string. + std::string urlunescape(char *s); ///< Unescapes URLencoded C-strings to a std::string. void close(); ///< Close connection. std::string getHost(); ///< Gets hostname for connection, if available. int getSocket(); ///< Returns internal socket number. From 57b9866b4b1d2236cc0c6eccdcf940774560930d Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 26 Sep 2011 02:16:12 +0200 Subject: [PATCH 144/788] Added POST var parsing to HTTP lib, added WriteFile to JSON gearbox version, added real parsing of input to JSON gearbox version --- util/http_parser.cpp | 43 ++++++++++++++++++++++++++++++++++++++++++- util/http_parser.h | 2 ++ util/socket.cpp | 24 ------------------------ util/socket.h | 2 -- 4 files changed, 44 insertions(+), 27 deletions(-) diff --git a/util/http_parser.cpp b/util/http_parser.cpp index ec648a39..9a81c404 100644 --- a/util/http_parser.cpp +++ b/util/http_parser.cpp @@ -188,10 +188,25 @@ bool HTTP::Parser::parse(){ } if (seenHeaders){ if (length > 0){ - /// \todo Include POST variable parsing? if (HTTPbuffer.length() >= length){ body = HTTPbuffer.substr(0, length); HTTPbuffer.erase(0, length); + std::string tmppost = body; + std::string varname; + std::string varval; + while (tmppost.find('=') != std::string::npos){ + size_t found = tmppost.find('='); + varname = urlunescape(tmppost.substr(0, found).c_str()); + tmppost.erase(0, found+1); + size_t found = tmppost.find('&'); + varval = urlunescape(tmppost.substr(0, found).c_str()); + SetVar(varname, varval); + if (found == std::string::npos){ + tmppost.clear(); + }else{ + tmppost.erase(0, found+1); + } + } return true; }else{ return false; @@ -241,3 +256,29 @@ void HTTP::Parser::SendBodyPart(Socket::Connection & conn, std::string bodypart) conn.write(bodypart); } } + +/// Unescapes URLencoded C-strings to a std::string. +/// This function *will* destroy the input data! +std::string HTTP::Parser::urlunescape(char *s){ + char *p; + for (p = s; *s != '\0'; ++s){ + if (*s == '%'){ + if (*++s != '\0'){ + *p = unhex(*s) << 4; + } + if (*++s != '\0'){ + *p++ += unhex(*s); + } + } else { + if (*s == '+'){*p++ = ' ';}else{*p++ = *s;} + } + } + *p = '\0'; + return std::string(s); +} + +/// 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 ); +} diff --git a/util/http_parser.h b/util/http_parser.h index d7048eae..542c06e6 100644 --- a/util/http_parser.h +++ b/util/http_parser.h @@ -30,6 +30,7 @@ namespace HTTP{ void SendBodyPart(Socket::Connection & conn, std::string bodypart); void Clean(); bool CleanForNext(); + std::string urlunescape(char *s); ///< Unescapes URLencoded C-strings to a std::string. std::string body; std::string method; std::string url; @@ -43,5 +44,6 @@ namespace HTTP{ std::map headers; std::map vars; void Trim(std::string & s); + int unhex(char c); ///< Helper function for urlunescape. };//HTTP::Parser class };//HTTP namespace diff --git a/util/socket.cpp b/util/socket.cpp index 425d2a70..b0046d02 100644 --- a/util/socket.cpp +++ b/util/socket.cpp @@ -495,27 +495,3 @@ bool Socket::Server::connected(){ /// Returns internal socket number. int Socket::Server::getSocket(){return sock;} - -/// Unescapes URLencoded C-strings to a std::string. -/// This function *will* destroy the incoming data! -std::string Socket::Connection::urlunescape(char *s){ - char *p; - for (p = s; *s != '\0'; ++s){ - if (*s == '%'){ - if (*++s != '\0'){ - *p = unhex(*s) << 4; - } - if (*++s != '\0'){ - *p++ += unhex(*s); - } - } else { - if (*s == '+'){*p++ = ' ';}else{*p++ = *s;} - } - } - *p = '\0'; - return std::string(s); -} - -int Socket::Connection::unhex(char c){ - return( c >= '0' && c <= '9' ? c - '0' : c >= 'A' && c <= 'F' ? c - 'A' + 10 : c - 'a' + 10 ); -} diff --git a/util/socket.h b/util/socket.h index 456a162f..57aca183 100644 --- a/util/socket.h +++ b/util/socket.h @@ -23,7 +23,6 @@ namespace Socket{ private: int sock; ///< Internally saved socket number. std::string remotehost; ///< Stores remote host address. - int unhex(char c); ///< Helper function for urlunescape. public: Connection(); ///< Create a new disconnected base socket. Connection(int sockNo); ///< Create a new base socket. @@ -45,7 +44,6 @@ namespace Socket{ bool swrite(std::string & buffer); ///< Read call that is compatible with std::string. bool iread(std::string & buffer); ///< Incremental write call that is compatible with std::string. bool iwrite(std::string & buffer); ///< Write call that is compatible with std::string. - std::string urlunescape(char *s); ///< Unescapes URLencoded C-strings to a std::string. void close(); ///< Close connection. std::string getHost(); ///< Gets hostname for connection, if available. int getSocket(); ///< Returns internal socket number. From 592c1cfbd0285d0240c310d825e2ffa9d73bbf1c Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 26 Sep 2011 12:44:15 +0200 Subject: [PATCH 145/788] Added full IPv4 support --- util/http_parser.cpp | 6 +++--- util/socket.cpp | 42 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/util/http_parser.cpp b/util/http_parser.cpp index 9a81c404..274cb3c3 100644 --- a/util/http_parser.cpp +++ b/util/http_parser.cpp @@ -196,10 +196,10 @@ bool HTTP::Parser::parse(){ std::string varval; while (tmppost.find('=') != std::string::npos){ size_t found = tmppost.find('='); - varname = urlunescape(tmppost.substr(0, found).c_str()); + varname = urlunescape((char*)tmppost.substr(0, found).c_str()); tmppost.erase(0, found+1); - size_t found = tmppost.find('&'); - varval = urlunescape(tmppost.substr(0, found).c_str()); + found = tmppost.find('&'); + varval = urlunescape((char*)tmppost.substr(0, found).c_str()); SetVar(varname, varval); if (found == std::string::npos){ tmppost.clear(); diff --git a/util/socket.cpp b/util/socket.cpp index b0046d02..c585f7be 100644 --- a/util/socket.cpp +++ b/util/socket.cpp @@ -374,7 +374,47 @@ Socket::Server::Server(int port, std::string hostname, bool nonblock){ } }else{ #if DEBUG >= 1 - fprintf(stderr, "Binding failed! Error: %s\n", strerror(errno)); + fprintf(stderr, "Binding failed, retrying as IPv4... (%s)\n", strerror(errno)); + #endif + close(); + } + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0){ + #if DEBUG >= 1 + fprintf(stderr, "Could not create socket! Error: %s\n", strerror(errno)); + #endif + return; + } + on = 1; + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + if (nonblock){ + int flags = fcntl(sock, F_GETFL, 0); + flags |= O_NONBLOCK; + fcntl(sock, F_SETFL, flags); + } + struct sockaddr_in addr4; + addr4.sin_family = AF_INET; + addr4.sin_port = htons(port);//set port + if (hostname == "0.0.0.0"){ + addr4.sin_addr.s_addr = INADDR_ANY; + }else{ + inet_pton(AF_INET, hostname.c_str(), &addr4.sin_addr);//set interface, 0.0.0.0 (default) is all + } + ret = bind(sock, (sockaddr*)&addr4, sizeof(addr4));//do the actual bind + if (ret == 0){ + ret = listen(sock, 100);//start listening, backlog of 100 allowed + if (ret == 0){ + return; + }else{ + #if DEBUG >= 1 + fprintf(stderr, "Listen failed! Error: %s\n", strerror(errno)); + #endif + close(); + return; + } + }else{ + #if DEBUG >= 1 + fprintf(stderr, "IPv4 binding also failed, giving up. (%s)\n", strerror(errno)); #endif close(); return; From b4bafce129f218abe0840502a57886d46b492901 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 7 Oct 2011 02:06:41 +0200 Subject: [PATCH 146/788] Added utility for spawning/monitoring/killing processes, fixed reaping of child processes for all Connectors --- util/proc.cpp | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++ util/proc.h | 28 ++++++++++ 2 files changed, 166 insertions(+) create mode 100644 util/proc.cpp create mode 100644 util/proc.h diff --git a/util/proc.cpp b/util/proc.cpp new file mode 100644 index 00000000..0e891be5 --- /dev/null +++ b/util/proc.cpp @@ -0,0 +1,138 @@ +/// \file proc.cpp +/// Contains generic functions for managing processes. + +#include "proc.h" +#include +#include +#include +#include +#include +#if DEBUG >= 1 +#include +#endif + +std::map Util::Procs::plist; +bool Util::Procs::handler_set = false; + +/// Used internally to capture child signals and update plist. +void Util::Procs::childsig_handler(int signum){ + if (signum != SIGCHLD){return;} + pid_t ret = wait(0); + #if DEBUG >= 1 + std::cerr << "Process " << plist[ret] << " terminated." << std::endl; + #endif + plist.erase(ret); +} + +/// Starts a new process if the name is not already active. +/// \return 0 if process was not started, process PID otherwise. +/// \arg name Name for this process - only used internally. +/// \arg cmd Commandline for this process. +pid_t Util::Procs::Start(std::string name, std::string cmd){ + if (isActive(name)){return getPid(name);} + if (!handler_set){ + struct sigaction new_action; + new_action.sa_handler = Util::Procs::childsig_handler; + sigemptyset(&new_action.sa_mask); + new_action.sa_flags = 0; + sigaction(SIGCHLD, &new_action, NULL); + handler_set = true; + } + pid_t ret = fork(); + if (ret == 0){ + //split cmd into arguments + //supports a maximum of 20 arguments + char * tmp = (char*)cmd.c_str(); + char * tmp2 = 0; + char * args[21]; + int i = 0; + tmp2 = strtok(tmp, " "); + args[0] = tmp2; + while (tmp2 != 0 && (i < 20)){ + tmp2 = strtok(0, " "); + ++i; + args[i] = tmp2; + } + if (i == 20){args[20] = 0;} + //execute the command + execvp(args[0], args); + #if DEBUG >= 1 + std::cerr << "Error: " << strerror(errno) << std::endl; + #endif + _exit(42); + }else{ + if (ret > 0){ + #if DEBUG >= 1 + std::cerr << "Process " << name << " started, PID " << ret << ": " << cmd << std::endl; + #endif + plist.insert(std::pair(ret, name)); + }else{ + #if DEBUG >= 1 + std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl; + #endif + return 0; + } + } + return ret; +} + +/// Stops the named process, if running. +/// \arg name (Internal) name of process to stop +void Util::Procs::Stop(std::string name){ + if (!isActive(name)){return;} + Stop(getPid(name)); +} + +/// Stops the process with this pid, if running. +/// \arg name The PID of the process to stop. +void Util::Procs::Stop(pid_t name){ + if (isActive(name)){ + kill(name, SIGTERM); + } +} + +/// (Attempts to) stop all running child processes. +void Util::Procs::StopAll(){ + std::map::iterator it; + for (it = plist.begin(); it != plist.end(); it++){ + Stop((*it).first); + } +} + +/// Returns the number of active child processes. +int Util::Procs::Count(){ + return plist.size(); +} + +/// Returns true if a process by this name is currently active. +bool Util::Procs::isActive(std::string name){ + std::map::iterator it; + for (it = plist.begin(); it != plist.end(); it++){ + if ((*it).second == name){return true;} + } + return false; +} + +/// Returns true if a process with this PID is currently active. +bool Util::Procs::isActive(pid_t name){ + return (plist.count(name) == 1); +} + +/// Gets PID for this named process, if active. +/// \return NULL if not active, process PID otherwise. +pid_t Util::Procs::getPid(std::string name){ + std::map::iterator it; + for (it = plist.begin(); it != plist.end(); it++){ + if ((*it).second == name){return (*it).first;} + } + return 0; +} + +/// Gets name for this process PID, if active. +/// \return Empty string if not active, name otherwise. +std::string Util::Procs::getName(pid_t name){ + if (plist.count(name) == 1){ + return plist[name]; + } + return ""; +} diff --git a/util/proc.h b/util/proc.h new file mode 100644 index 00000000..a2c10ec0 --- /dev/null +++ b/util/proc.h @@ -0,0 +1,28 @@ +/// \file proc.h +/// Contains generic function headers for managing processes. + +#include +#include +#include + +/// Contains utility code, not directly related to streaming media +namespace Util{ + + /// Deals with spawning, monitoring and stopping child processes + class Procs{ + private: + static std::map plist; ///< Holds active processes + static bool handler_set; ///< If true, the sigchld handler has been setup. + static void childsig_handler(int signum); + public: + static pid_t Start(std::string name, std::string cmd); + static void Stop(std::string name); + static void Stop(pid_t name); + static void StopAll(); + static int Count(); + static bool isActive(std::string name); + static bool isActive(pid_t name); + static pid_t getPid(std::string name); + static std::string getName(pid_t name); + }; +}; From 097188ba2f349206a62add5dea431e99ba25b8d6 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 7 Oct 2011 03:08:07 +0200 Subject: [PATCH 147/788] Fixed some issues with buffers, fixed child reaping for all connectors --- util/server_setup.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/util/server_setup.cpp b/util/server_setup.cpp index 20d8c4f5..c28d2040 100644 --- a/util/server_setup.cpp +++ b/util/server_setup.cpp @@ -35,13 +35,17 @@ Socket::Server server_socket(-1); ///< Placeholder for the server socket /// Disconnecting the server_socket will terminate the main listening loop /// and cleanly shut down the process. void signal_handler (int signum){ - if (!server_socket.connected()) return; switch (signum){ case SIGINT: break; case SIGHUP: break; case SIGTERM: break; + case SIGCHLD: + wait(0); + return; + break; default: return; break; } + if (!server_socket.connected()) return; server_socket.close(); }//signal_handler @@ -64,7 +68,8 @@ int main(int argc, char ** argv){ sigaction(SIGHUP, &new_action, NULL); sigaction(SIGTERM, &new_action, NULL); sigaction(SIGPIPE, &new_action, NULL); - + sigaction(SIGCHLD, &new_action, NULL); + //default values int listen_port = DEFAULT_PORT; bool daemon_mode = true; @@ -174,9 +179,7 @@ int main(int argc, char ** argv){ } } - int status; while (server_socket.connected()){ - while (waitpid((pid_t)-1, &status, WNOHANG) > 0){}//clean up all child processes S = server_socket.accept(); if (S.connected()){//check if the new connection is valid pid_t myid = fork(); From 9849a0302a8f4749904175cb31ad6fb8fca9170d Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 7 Oct 2011 03:22:26 +0200 Subject: [PATCH 148/788] Some testing for child reaping issues... --- util/server_setup.cpp | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/util/server_setup.cpp b/util/server_setup.cpp index c28d2040..23a5422e 100644 --- a/util/server_setup.cpp +++ b/util/server_setup.cpp @@ -36,9 +36,21 @@ Socket::Server server_socket(-1); ///< Placeholder for the server socket /// and cleanly shut down the process. void signal_handler (int signum){ switch (signum){ - case SIGINT: break; - case SIGHUP: break; - case SIGTERM: break; + case SIGINT: + #if DEBUG >= 1 + fprintf(stderr, "Received SIGINT - closing server socket.\n"); + #endif + break; + case SIGHUP: + #if DEBUG >= 1 + fprintf(stderr, "Received SIGHUP - closing server socket.\n"); + #endif + break; + case SIGTERM: + #if DEBUG >= 1 + fprintf(stderr, "Received SIGTERM - closing server socket.\n"); + #endif + break; case SIGCHLD: wait(0); return; @@ -192,5 +204,8 @@ int main(int argc, char ** argv){ } } }//while connected + #if DEBUG >= 1 + fprintf(stderr, "Server socket closed, exiting.\n"); + #endif return 0; }//main From e65a391aa141cd7c1a9857fa2fb86a2fd6c08b52 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 7 Oct 2011 03:27:21 +0200 Subject: [PATCH 149/788] Fixed accept call getting interrupted by child reaping --- util/socket.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/socket.cpp b/util/socket.cpp index c585f7be..6753d7ce 100644 --- a/util/socket.cpp +++ b/util/socket.cpp @@ -484,7 +484,7 @@ Socket::Connection Socket::Server::accept(bool nonblock){ } Socket::Connection tmp(r); if (r < 0){ - if (errno != EWOULDBLOCK && errno != EAGAIN){ + if ((errno != EWOULDBLOCK) && (errno != EAGAIN) && (errno != EINTR)){ #if DEBUG >= 1 fprintf(stderr, "Error during accept - closing server socket.\n"); #endif From cf3ca533e6f703fa6db1aaf0d249dd04feaa0f54 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 11 Oct 2011 22:06:10 +0200 Subject: [PATCH 150/788] Cleanup JSON code - now in util dir --- util/json/autolink.h | 19 + util/json/config.h | 43 + util/json/features.h | 42 + util/json/forwards.h | 39 + util/json/json.h | 10 + util/json/json_batchallocator.h | 125 +++ util/json/json_internalarray.inl | 448 ++++++++ util/json/json_internalmap.inl | 607 +++++++++++ util/json/json_reader.cpp | 885 +++++++++++++++ util/json/json_value.cpp | 1718 ++++++++++++++++++++++++++++++ util/json/json_valueiterator.inl | 292 +++++ util/json/json_writer.cpp | 829 ++++++++++++++ util/json/reader.h | 196 ++++ util/json/value.h | 1069 +++++++++++++++++++ util/json/writer.h | 174 +++ 15 files changed, 6496 insertions(+) create mode 100644 util/json/autolink.h create mode 100644 util/json/config.h create mode 100644 util/json/features.h create mode 100644 util/json/forwards.h create mode 100644 util/json/json.h create mode 100644 util/json/json_batchallocator.h create mode 100644 util/json/json_internalarray.inl create mode 100644 util/json/json_internalmap.inl create mode 100644 util/json/json_reader.cpp create mode 100644 util/json/json_value.cpp create mode 100644 util/json/json_valueiterator.inl create mode 100644 util/json/json_writer.cpp create mode 100644 util/json/reader.h create mode 100644 util/json/value.h create mode 100644 util/json/writer.h diff --git a/util/json/autolink.h b/util/json/autolink.h new file mode 100644 index 00000000..37c9258e --- /dev/null +++ b/util/json/autolink.h @@ -0,0 +1,19 @@ +#ifndef JSON_AUTOLINK_H_INCLUDED +# define JSON_AUTOLINK_H_INCLUDED + +# include "config.h" + +# ifdef JSON_IN_CPPTL +# include +# endif + +# if !defined(JSON_NO_AUTOLINK) && !defined(JSON_DLL_BUILD) && !defined(JSON_IN_CPPTL) +# define CPPTL_AUTOLINK_NAME "json" +# undef CPPTL_AUTOLINK_DLL +# ifdef JSON_DLL +# define CPPTL_AUTOLINK_DLL +# endif +# include "autolink.h" +# endif + +#endif // JSON_AUTOLINK_H_INCLUDED diff --git a/util/json/config.h b/util/json/config.h new file mode 100644 index 00000000..5d334cbc --- /dev/null +++ b/util/json/config.h @@ -0,0 +1,43 @@ +#ifndef JSON_CONFIG_H_INCLUDED +# define JSON_CONFIG_H_INCLUDED + +/// If defined, indicates that json library is embedded in CppTL library. +//# define JSON_IN_CPPTL 1 + +/// If defined, indicates that json may leverage CppTL library +//# define JSON_USE_CPPTL 1 +/// If defined, indicates that cpptl vector based map should be used instead of std::map +/// as Value container. +//# define JSON_USE_CPPTL_SMALLMAP 1 +/// If defined, indicates that Json specific container should be used +/// (hash table & simple deque container with customizable allocator). +/// THIS FEATURE IS STILL EXPERIMENTAL! +//# define JSON_VALUE_USE_INTERNAL_MAP 1 +/// Force usage of standard new/malloc based allocator instead of memory pool based allocator. +/// The memory pools allocator used optimization (initializing Value and ValueInternalLink +/// as if it was a POD) that may cause some validation tool to report errors. +/// Only has effects if JSON_VALUE_USE_INTERNAL_MAP is defined. +//# define JSON_USE_SIMPLE_INTERNAL_ALLOCATOR 1 + +/// If defined, indicates that Json use exception to report invalid type manipulation +/// instead of C assert macro. +# define JSON_USE_EXCEPTION 1 + +# ifdef JSON_IN_CPPTL +# include +# ifndef JSON_USE_CPPTL +# define JSON_USE_CPPTL 1 +# endif +# endif + +# ifdef JSON_IN_CPPTL +# define JSON_API CPPTL_API +# elif defined(JSON_DLL_BUILD) +# define JSON_API __declspec(dllexport) +# elif defined(JSON_DLL) +# define JSON_API __declspec(dllimport) +# else +# define JSON_API +# endif + +#endif // JSON_CONFIG_H_INCLUDED diff --git a/util/json/features.h b/util/json/features.h new file mode 100644 index 00000000..5a9adec1 --- /dev/null +++ b/util/json/features.h @@ -0,0 +1,42 @@ +#ifndef CPPTL_JSON_FEATURES_H_INCLUDED +# define CPPTL_JSON_FEATURES_H_INCLUDED + +# include "forwards.h" + +namespace Json { + + /** \brief Configuration passed to reader and writer. + * This configuration object can be used to force the Reader or Writer + * to behave in a standard conforming way. + */ + class JSON_API Features + { + public: + /** \brief A configuration that allows all features and assumes all strings are UTF-8. + * - C & C++ comments are allowed + * - Root object can be any JSON value + * - Assumes Value strings are encoded in UTF-8 + */ + static Features all(); + + /** \brief A configuration that is strictly compatible with the JSON specification. + * - Comments are forbidden. + * - Root object must be either an array or an object value. + * - Assumes Value strings are encoded in UTF-8 + */ + static Features strictMode(); + + /** \brief Initialize the configuration like JsonConfig::allFeatures; + */ + Features(); + + /// \c true if comments are allowed. Default: \c true. + bool allowComments_; + + /// \c true if root must be either an array or an object value. Default: \c false. + bool strictRoot_; + }; + +} // namespace Json + +#endif // CPPTL_JSON_FEATURES_H_INCLUDED diff --git a/util/json/forwards.h b/util/json/forwards.h new file mode 100644 index 00000000..d0ce8300 --- /dev/null +++ b/util/json/forwards.h @@ -0,0 +1,39 @@ +#ifndef JSON_FORWARDS_H_INCLUDED +# define JSON_FORWARDS_H_INCLUDED + +# include "config.h" + +namespace Json { + + // writer.h + class FastWriter; + class StyledWriter; + + // reader.h + class Reader; + + // features.h + class Features; + + // value.h + typedef int Int; + typedef unsigned int UInt; + class StaticString; + class Path; + class PathArgument; + class Value; + class ValueIteratorBase; + class ValueIterator; + class ValueConstIterator; +#ifdef JSON_VALUE_USE_INTERNAL_MAP + class ValueAllocator; + class ValueMapAllocator; + class ValueInternalLink; + class ValueInternalArray; + class ValueInternalMap; +#endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP + +} // namespace Json + + +#endif // JSON_FORWARDS_H_INCLUDED diff --git a/util/json/json.h b/util/json/json.h new file mode 100644 index 00000000..c71ed65a --- /dev/null +++ b/util/json/json.h @@ -0,0 +1,10 @@ +#ifndef JSON_JSON_H_INCLUDED +# define JSON_JSON_H_INCLUDED + +# include "autolink.h" +# include "value.h" +# include "reader.h" +# include "writer.h" +# include "features.h" + +#endif // JSON_JSON_H_INCLUDED diff --git a/util/json/json_batchallocator.h b/util/json/json_batchallocator.h new file mode 100644 index 00000000..87ea5ed8 --- /dev/null +++ b/util/json/json_batchallocator.h @@ -0,0 +1,125 @@ +#ifndef JSONCPP_BATCHALLOCATOR_H_INCLUDED +# define JSONCPP_BATCHALLOCATOR_H_INCLUDED + +# include +# include + +# ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + +namespace Json { + +/* Fast memory allocator. + * + * This memory allocator allocates memory for a batch of object (specified by + * the page size, the number of object in each page). + * + * It does not allow the destruction of a single object. All the allocated objects + * can be destroyed at once. The memory can be either released or reused for future + * allocation. + * + * The in-place new operator must be used to construct the object using the pointer + * returned by allocate. + */ +template +class BatchAllocator +{ +public: + typedef AllocatedType Type; + + BatchAllocator( unsigned int objectsPerPage = 255 ) + : freeHead_( 0 ) + , objectsPerPage_( objectsPerPage ) + { +// printf( "Size: %d => %s\n", sizeof(AllocatedType), typeid(AllocatedType).name() ); + assert( sizeof(AllocatedType) * objectPerAllocation >= sizeof(AllocatedType *) ); // We must be able to store a slist in the object free space. + assert( objectsPerPage >= 16 ); + batches_ = allocateBatch( 0 ); // allocated a dummy page + currentBatch_ = batches_; + } + + ~BatchAllocator() + { + for ( BatchInfo *batch = batches_; batch; ) + { + BatchInfo *nextBatch = batch->next_; + free( batch ); + batch = nextBatch; + } + } + + /// allocate space for an array of objectPerAllocation object. + /// @warning it is the responsability of the caller to call objects constructors. + AllocatedType *allocate() + { + if ( freeHead_ ) // returns node from free list. + { + AllocatedType *object = freeHead_; + freeHead_ = *(AllocatedType **)object; + return object; + } + if ( currentBatch_->used_ == currentBatch_->end_ ) + { + currentBatch_ = currentBatch_->next_; + while ( currentBatch_ && currentBatch_->used_ == currentBatch_->end_ ) + currentBatch_ = currentBatch_->next_; + + if ( !currentBatch_ ) // no free batch found, allocate a new one + { + currentBatch_ = allocateBatch( objectsPerPage_ ); + currentBatch_->next_ = batches_; // insert at the head of the list + batches_ = currentBatch_; + } + } + AllocatedType *allocated = currentBatch_->used_; + currentBatch_->used_ += objectPerAllocation; + return allocated; + } + + /// Release the object. + /// @warning it is the responsability of the caller to actually destruct the object. + void release( AllocatedType *object ) + { + assert( object != 0 ); + *(AllocatedType **)object = freeHead_; + freeHead_ = object; + } + +private: + struct BatchInfo + { + BatchInfo *next_; + AllocatedType *used_; + AllocatedType *end_; + AllocatedType buffer_[objectPerAllocation]; + }; + + // disabled copy constructor and assignement operator. + BatchAllocator( const BatchAllocator & ); + void operator =( const BatchAllocator &); + + static BatchInfo *allocateBatch( unsigned int objectsPerPage ) + { + const unsigned int mallocSize = sizeof(BatchInfo) - sizeof(AllocatedType)* objectPerAllocation + + sizeof(AllocatedType) * objectPerAllocation * objectsPerPage; + BatchInfo *batch = static_cast( malloc( mallocSize ) ); + batch->next_ = 0; + batch->used_ = batch->buffer_; + batch->end_ = batch->buffer_ + objectsPerPage; + return batch; + } + + BatchInfo *batches_; + BatchInfo *currentBatch_; + /// Head of a single linked list within the allocated space of freeed object + AllocatedType *freeHead_; + unsigned int objectsPerPage_; +}; + + +} // namespace Json + +# endif // ifndef JSONCPP_DOC_INCLUDE_IMPLEMENTATION + +#endif // JSONCPP_BATCHALLOCATOR_H_INCLUDED + diff --git a/util/json/json_internalarray.inl b/util/json/json_internalarray.inl new file mode 100644 index 00000000..9b985d25 --- /dev/null +++ b/util/json/json_internalarray.inl @@ -0,0 +1,448 @@ +// included by json_value.cpp +// everything is within Json namespace + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueInternalArray +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueArrayAllocator::~ValueArrayAllocator() +{ +} + +// ////////////////////////////////////////////////////////////////// +// class DefaultValueArrayAllocator +// ////////////////////////////////////////////////////////////////// +#ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR +class DefaultValueArrayAllocator : public ValueArrayAllocator +{ +public: // overridden from ValueArrayAllocator + virtual ~DefaultValueArrayAllocator() + { + } + + virtual ValueInternalArray *newArray() + { + return new ValueInternalArray(); + } + + virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) + { + return new ValueInternalArray( other ); + } + + virtual void destructArray( ValueInternalArray *array ) + { + delete array; + } + + virtual void reallocateArrayPageIndex( Value **&indexes, + ValueInternalArray::PageIndex &indexCount, + ValueInternalArray::PageIndex minNewIndexCount ) + { + ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1; + if ( minNewIndexCount > newIndexCount ) + newIndexCount = minNewIndexCount; + void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount ); + if ( !newIndexes ) + throw std::bad_alloc(); + indexCount = newIndexCount; + indexes = static_cast( newIndexes ); + } + virtual void releaseArrayPageIndex( Value **indexes, + ValueInternalArray::PageIndex indexCount ) + { + if ( indexes ) + free( indexes ); + } + + virtual Value *allocateArrayPage() + { + return static_cast( malloc( sizeof(Value) * ValueInternalArray::itemsPerPage ) ); + } + + virtual void releaseArrayPage( Value *value ) + { + if ( value ) + free( value ); + } +}; + +#else // #ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR +/// @todo make this thread-safe (lock when accessign batch allocator) +class DefaultValueArrayAllocator : public ValueArrayAllocator +{ +public: // overridden from ValueArrayAllocator + virtual ~DefaultValueArrayAllocator() + { + } + + virtual ValueInternalArray *newArray() + { + ValueInternalArray *array = arraysAllocator_.allocate(); + new (array) ValueInternalArray(); // placement new + return array; + } + + virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) + { + ValueInternalArray *array = arraysAllocator_.allocate(); + new (array) ValueInternalArray( other ); // placement new + return array; + } + + virtual void destructArray( ValueInternalArray *array ) + { + if ( array ) + { + array->~ValueInternalArray(); + arraysAllocator_.release( array ); + } + } + + virtual void reallocateArrayPageIndex( Value **&indexes, + ValueInternalArray::PageIndex &indexCount, + ValueInternalArray::PageIndex minNewIndexCount ) + { + ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1; + if ( minNewIndexCount > newIndexCount ) + newIndexCount = minNewIndexCount; + void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount ); + if ( !newIndexes ) + throw std::bad_alloc(); + indexCount = newIndexCount; + indexes = static_cast( newIndexes ); + } + virtual void releaseArrayPageIndex( Value **indexes, + ValueInternalArray::PageIndex indexCount ) + { + if ( indexes ) + free( indexes ); + } + + virtual Value *allocateArrayPage() + { + return static_cast( pagesAllocator_.allocate() ); + } + + virtual void releaseArrayPage( Value *value ) + { + if ( value ) + pagesAllocator_.release( value ); + } +private: + BatchAllocator arraysAllocator_; + BatchAllocator pagesAllocator_; +}; +#endif // #ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR + +static ValueArrayAllocator *&arrayAllocator() +{ + static DefaultValueArrayAllocator defaultAllocator; + static ValueArrayAllocator *arrayAllocator = &defaultAllocator; + return arrayAllocator; +} + +static struct DummyArrayAllocatorInitializer { + DummyArrayAllocatorInitializer() + { + arrayAllocator(); // ensure arrayAllocator() statics are initialized before main(). + } +} dummyArrayAllocatorInitializer; + +// ////////////////////////////////////////////////////////////////// +// class ValueInternalArray +// ////////////////////////////////////////////////////////////////// +bool +ValueInternalArray::equals( const IteratorState &x, + const IteratorState &other ) +{ + return x.array_ == other.array_ + && x.currentItemIndex_ == other.currentItemIndex_ + && x.currentPageIndex_ == other.currentPageIndex_; +} + + +void +ValueInternalArray::increment( IteratorState &it ) +{ + JSON_ASSERT_MESSAGE( it.array_ && + (it.currentPageIndex_ - it.array_->pages_)*itemsPerPage + it.currentItemIndex_ + != it.array_->size_, + "ValueInternalArray::increment(): moving iterator beyond end" ); + ++(it.currentItemIndex_); + if ( it.currentItemIndex_ == itemsPerPage ) + { + it.currentItemIndex_ = 0; + ++(it.currentPageIndex_); + } +} + + +void +ValueInternalArray::decrement( IteratorState &it ) +{ + JSON_ASSERT_MESSAGE( it.array_ && it.currentPageIndex_ == it.array_->pages_ + && it.currentItemIndex_ == 0, + "ValueInternalArray::decrement(): moving iterator beyond end" ); + if ( it.currentItemIndex_ == 0 ) + { + it.currentItemIndex_ = itemsPerPage-1; + --(it.currentPageIndex_); + } + else + { + --(it.currentItemIndex_); + } +} + + +Value & +ValueInternalArray::unsafeDereference( const IteratorState &it ) +{ + return (*(it.currentPageIndex_))[it.currentItemIndex_]; +} + + +Value & +ValueInternalArray::dereference( const IteratorState &it ) +{ + JSON_ASSERT_MESSAGE( it.array_ && + (it.currentPageIndex_ - it.array_->pages_)*itemsPerPage + it.currentItemIndex_ + < it.array_->size_, + "ValueInternalArray::dereference(): dereferencing invalid iterator" ); + return unsafeDereference( it ); +} + +void +ValueInternalArray::makeBeginIterator( IteratorState &it ) const +{ + it.array_ = const_cast( this ); + it.currentItemIndex_ = 0; + it.currentPageIndex_ = pages_; +} + + +void +ValueInternalArray::makeIterator( IteratorState &it, ArrayIndex index ) const +{ + it.array_ = const_cast( this ); + it.currentItemIndex_ = index % itemsPerPage; + it.currentPageIndex_ = pages_ + index / itemsPerPage; +} + + +void +ValueInternalArray::makeEndIterator( IteratorState &it ) const +{ + makeIterator( it, size_ ); +} + + +ValueInternalArray::ValueInternalArray() + : pages_( 0 ) + , size_( 0 ) + , pageCount_( 0 ) +{ +} + + +ValueInternalArray::ValueInternalArray( const ValueInternalArray &other ) + : pages_( 0 ) + , pageCount_( 0 ) + , size_( other.size_ ) +{ + PageIndex minNewPages = other.size_ / itemsPerPage; + arrayAllocator()->reallocateArrayPageIndex( pages_, pageCount_, minNewPages ); + JSON_ASSERT_MESSAGE( pageCount_ >= minNewPages, + "ValueInternalArray::reserve(): bad reallocation" ); + IteratorState itOther; + other.makeBeginIterator( itOther ); + Value *value; + for ( ArrayIndex index = 0; index < size_; ++index, increment(itOther) ) + { + if ( index % itemsPerPage == 0 ) + { + PageIndex pageIndex = index / itemsPerPage; + value = arrayAllocator()->allocateArrayPage(); + pages_[pageIndex] = value; + } + new (value) Value( dereference( itOther ) ); + } +} + + +ValueInternalArray & +ValueInternalArray::operator =( const ValueInternalArray &other ) +{ + ValueInternalArray temp( other ); + swap( temp ); + return *this; +} + + +ValueInternalArray::~ValueInternalArray() +{ + // destroy all constructed items + IteratorState it; + IteratorState itEnd; + makeBeginIterator( it); + makeEndIterator( itEnd ); + for ( ; !equals(it,itEnd); increment(it) ) + { + Value *value = &dereference(it); + value->~Value(); + } + // release all pages + PageIndex lastPageIndex = size_ / itemsPerPage; + for ( PageIndex pageIndex = 0; pageIndex < lastPageIndex; ++pageIndex ) + arrayAllocator()->releaseArrayPage( pages_[pageIndex] ); + // release pages index + arrayAllocator()->releaseArrayPageIndex( pages_, pageCount_ ); +} + + +void +ValueInternalArray::swap( ValueInternalArray &other ) +{ + Value **tempPages = pages_; + pages_ = other.pages_; + other.pages_ = tempPages; + ArrayIndex tempSize = size_; + size_ = other.size_; + other.size_ = tempSize; + PageIndex tempPageCount = pageCount_; + pageCount_ = other.pageCount_; + other.pageCount_ = tempPageCount; +} + +void +ValueInternalArray::clear() +{ + ValueInternalArray dummy; + swap( dummy ); +} + + +void +ValueInternalArray::resize( ArrayIndex newSize ) +{ + if ( newSize == 0 ) + clear(); + else if ( newSize < size_ ) + { + IteratorState it; + IteratorState itEnd; + makeIterator( it, newSize ); + makeIterator( itEnd, size_ ); + for ( ; !equals(it,itEnd); increment(it) ) + { + Value *value = &dereference(it); + value->~Value(); + } + PageIndex pageIndex = (newSize + itemsPerPage - 1) / itemsPerPage; + PageIndex lastPageIndex = size_ / itemsPerPage; + for ( ; pageIndex < lastPageIndex; ++pageIndex ) + arrayAllocator()->releaseArrayPage( pages_[pageIndex] ); + size_ = newSize; + } + else if ( newSize > size_ ) + resolveReference( newSize ); +} + + +void +ValueInternalArray::makeIndexValid( ArrayIndex index ) +{ + // Need to enlarge page index ? + if ( index >= pageCount_ * itemsPerPage ) + { + PageIndex minNewPages = (index + 1) / itemsPerPage; + arrayAllocator()->reallocateArrayPageIndex( pages_, pageCount_, minNewPages ); + JSON_ASSERT_MESSAGE( pageCount_ >= minNewPages, "ValueInternalArray::reserve(): bad reallocation" ); + } + + // Need to allocate new pages ? + ArrayIndex nextPageIndex = + (size_ % itemsPerPage) != 0 ? size_ - (size_%itemsPerPage) + itemsPerPage + : size_; + if ( nextPageIndex <= index ) + { + PageIndex pageIndex = nextPageIndex / itemsPerPage; + PageIndex pageToAllocate = (index - nextPageIndex) / itemsPerPage + 1; + for ( ; pageToAllocate-- > 0; ++pageIndex ) + pages_[pageIndex] = arrayAllocator()->allocateArrayPage(); + } + + // Initialize all new entries + IteratorState it; + IteratorState itEnd; + makeIterator( it, size_ ); + size_ = index + 1; + makeIterator( itEnd, size_ ); + for ( ; !equals(it,itEnd); increment(it) ) + { + Value *value = &dereference(it); + new (value) Value(); // Construct a default value using placement new + } +} + +Value & +ValueInternalArray::resolveReference( ArrayIndex index ) +{ + if ( index >= size_ ) + makeIndexValid( index ); + return pages_[index/itemsPerPage][index%itemsPerPage]; +} + +Value * +ValueInternalArray::find( ArrayIndex index ) const +{ + if ( index >= size_ ) + return 0; + return &(pages_[index/itemsPerPage][index%itemsPerPage]); +} + +ValueInternalArray::ArrayIndex +ValueInternalArray::size() const +{ + return size_; +} + +int +ValueInternalArray::distance( const IteratorState &x, const IteratorState &y ) +{ + return indexOf(y) - indexOf(x); +} + + +ValueInternalArray::ArrayIndex +ValueInternalArray::indexOf( const IteratorState &iterator ) +{ + if ( !iterator.array_ ) + return ArrayIndex(-1); + return ArrayIndex( + (iterator.currentPageIndex_ - iterator.array_->pages_) * itemsPerPage + + iterator.currentItemIndex_ ); +} + + +int +ValueInternalArray::compare( const ValueInternalArray &other ) const +{ + int sizeDiff( size_ - other.size_ ); + if ( sizeDiff != 0 ) + return sizeDiff; + + for ( ArrayIndex index =0; index < size_; ++index ) + { + int diff = pages_[index/itemsPerPage][index%itemsPerPage].compare( + other.pages_[index/itemsPerPage][index%itemsPerPage] ); + if ( diff != 0 ) + return diff; + } + return 0; +} diff --git a/util/json/json_internalmap.inl b/util/json/json_internalmap.inl new file mode 100644 index 00000000..19771488 --- /dev/null +++ b/util/json/json_internalmap.inl @@ -0,0 +1,607 @@ +// included by json_value.cpp +// everything is within Json namespace + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueInternalMap +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +/** \internal MUST be safely initialized using memset( this, 0, sizeof(ValueInternalLink) ); + * This optimization is used by the fast allocator. + */ +ValueInternalLink::ValueInternalLink() + : previous_( 0 ) + , next_( 0 ) +{ +} + +ValueInternalLink::~ValueInternalLink() +{ + for ( int index =0; index < itemPerLink; ++index ) + { + if ( !items_[index].isItemAvailable() ) + { + if ( !items_[index].isMemberNameStatic() ) + free( keys_[index] ); + } + else + break; + } +} + + + +ValueMapAllocator::~ValueMapAllocator() +{ +} + +#ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR +class DefaultValueMapAllocator : public ValueMapAllocator +{ +public: // overridden from ValueMapAllocator + virtual ValueInternalMap *newMap() + { + return new ValueInternalMap(); + } + + virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) + { + return new ValueInternalMap( other ); + } + + virtual void destructMap( ValueInternalMap *map ) + { + delete map; + } + + virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) + { + return new ValueInternalLink[size]; + } + + virtual void releaseMapBuckets( ValueInternalLink *links ) + { + delete [] links; + } + + virtual ValueInternalLink *allocateMapLink() + { + return new ValueInternalLink(); + } + + virtual void releaseMapLink( ValueInternalLink *link ) + { + delete link; + } +}; +#else +/// @todo make this thread-safe (lock when accessign batch allocator) +class DefaultValueMapAllocator : public ValueMapAllocator +{ +public: // overridden from ValueMapAllocator + virtual ValueInternalMap *newMap() + { + ValueInternalMap *map = mapsAllocator_.allocate(); + new (map) ValueInternalMap(); // placement new + return map; + } + + virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) + { + ValueInternalMap *map = mapsAllocator_.allocate(); + new (map) ValueInternalMap( other ); // placement new + return map; + } + + virtual void destructMap( ValueInternalMap *map ) + { + if ( map ) + { + map->~ValueInternalMap(); + mapsAllocator_.release( map ); + } + } + + virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) + { + return new ValueInternalLink[size]; + } + + virtual void releaseMapBuckets( ValueInternalLink *links ) + { + delete [] links; + } + + virtual ValueInternalLink *allocateMapLink() + { + ValueInternalLink *link = linksAllocator_.allocate(); + memset( link, 0, sizeof(ValueInternalLink) ); + return link; + } + + virtual void releaseMapLink( ValueInternalLink *link ) + { + link->~ValueInternalLink(); + linksAllocator_.release( link ); + } +private: + BatchAllocator mapsAllocator_; + BatchAllocator linksAllocator_; +}; +#endif + +static ValueMapAllocator *&mapAllocator() +{ + static DefaultValueMapAllocator defaultAllocator; + static ValueMapAllocator *mapAllocator = &defaultAllocator; + return mapAllocator; +} + +static struct DummyMapAllocatorInitializer { + DummyMapAllocatorInitializer() + { + mapAllocator(); // ensure mapAllocator() statics are initialized before main(). + } +} dummyMapAllocatorInitializer; + + + +// h(K) = value * K >> w ; with w = 32 & K prime w.r.t. 2^32. + +/* +use linked list hash map. +buckets array is a container. +linked list element contains 6 key/values. (memory = (16+4) * 6 + 4 = 124) +value have extra state: valid, available, deleted +*/ + + +ValueInternalMap::ValueInternalMap() + : buckets_( 0 ) + , tailLink_( 0 ) + , bucketsSize_( 0 ) + , itemCount_( 0 ) +{ +} + + +ValueInternalMap::ValueInternalMap( const ValueInternalMap &other ) + : buckets_( 0 ) + , tailLink_( 0 ) + , bucketsSize_( 0 ) + , itemCount_( 0 ) +{ + reserve( other.itemCount_ ); + IteratorState it; + IteratorState itEnd; + other.makeBeginIterator( it ); + other.makeEndIterator( itEnd ); + for ( ; !equals(it,itEnd); increment(it) ) + { + bool isStatic; + const char *memberName = key( it, isStatic ); + const Value &aValue = value( it ); + resolveReference(memberName, isStatic) = aValue; + } +} + + +ValueInternalMap & +ValueInternalMap::operator =( const ValueInternalMap &other ) +{ + ValueInternalMap dummy( other ); + swap( dummy ); + return *this; +} + + +ValueInternalMap::~ValueInternalMap() +{ + if ( buckets_ ) + { + for ( BucketIndex bucketIndex =0; bucketIndex < bucketsSize_; ++bucketIndex ) + { + ValueInternalLink *link = buckets_[bucketIndex].next_; + while ( link ) + { + ValueInternalLink *linkToRelease = link; + link = link->next_; + mapAllocator()->releaseMapLink( linkToRelease ); + } + } + mapAllocator()->releaseMapBuckets( buckets_ ); + } +} + + +void +ValueInternalMap::swap( ValueInternalMap &other ) +{ + ValueInternalLink *tempBuckets = buckets_; + buckets_ = other.buckets_; + other.buckets_ = tempBuckets; + ValueInternalLink *tempTailLink = tailLink_; + tailLink_ = other.tailLink_; + other.tailLink_ = tempTailLink; + BucketIndex tempBucketsSize = bucketsSize_; + bucketsSize_ = other.bucketsSize_; + other.bucketsSize_ = tempBucketsSize; + BucketIndex tempItemCount = itemCount_; + itemCount_ = other.itemCount_; + other.itemCount_ = tempItemCount; +} + + +void +ValueInternalMap::clear() +{ + ValueInternalMap dummy; + swap( dummy ); +} + + +ValueInternalMap::BucketIndex +ValueInternalMap::size() const +{ + return itemCount_; +} + +bool +ValueInternalMap::reserveDelta( BucketIndex growth ) +{ + return reserve( itemCount_ + growth ); +} + +bool +ValueInternalMap::reserve( BucketIndex newItemCount ) +{ + if ( !buckets_ && newItemCount > 0 ) + { + buckets_ = mapAllocator()->allocateMapBuckets( 1 ); + bucketsSize_ = 1; + tailLink_ = &buckets_[0]; + } +// BucketIndex idealBucketCount = (newItemCount + ValueInternalLink::itemPerLink) / ValueInternalLink::itemPerLink; + return true; +} + + +const Value * +ValueInternalMap::find( const char *key ) const +{ + if ( !bucketsSize_ ) + return 0; + HashKey hashedKey = hash( key ); + BucketIndex bucketIndex = hashedKey % bucketsSize_; + for ( const ValueInternalLink *current = &buckets_[bucketIndex]; + current != 0; + current = current->next_ ) + { + for ( BucketIndex index=0; index < ValueInternalLink::itemPerLink; ++index ) + { + if ( current->items_[index].isItemAvailable() ) + return 0; + if ( strcmp( key, current->keys_[index] ) == 0 ) + return ¤t->items_[index]; + } + } + return 0; +} + + +Value * +ValueInternalMap::find( const char *key ) +{ + const ValueInternalMap *constThis = this; + return const_cast( constThis->find( key ) ); +} + + +Value & +ValueInternalMap::resolveReference( const char *key, + bool isStatic ) +{ + HashKey hashedKey = hash( key ); + if ( bucketsSize_ ) + { + BucketIndex bucketIndex = hashedKey % bucketsSize_; + ValueInternalLink **previous = 0; + BucketIndex index; + for ( ValueInternalLink *current = &buckets_[bucketIndex]; + current != 0; + previous = ¤t->next_, current = current->next_ ) + { + for ( index=0; index < ValueInternalLink::itemPerLink; ++index ) + { + if ( current->items_[index].isItemAvailable() ) + return setNewItem( key, isStatic, current, index ); + if ( strcmp( key, current->keys_[index] ) == 0 ) + return current->items_[index]; + } + } + } + + reserveDelta( 1 ); + return unsafeAdd( key, isStatic, hashedKey ); +} + + +void +ValueInternalMap::remove( const char *key ) +{ + HashKey hashedKey = hash( key ); + if ( !bucketsSize_ ) + return; + BucketIndex bucketIndex = hashedKey % bucketsSize_; + for ( ValueInternalLink *link = &buckets_[bucketIndex]; + link != 0; + link = link->next_ ) + { + BucketIndex index; + for ( index =0; index < ValueInternalLink::itemPerLink; ++index ) + { + if ( link->items_[index].isItemAvailable() ) + return; + if ( strcmp( key, link->keys_[index] ) == 0 ) + { + doActualRemove( link, index, bucketIndex ); + return; + } + } + } +} + +void +ValueInternalMap::doActualRemove( ValueInternalLink *link, + BucketIndex index, + BucketIndex bucketIndex ) +{ + // find last item of the bucket and swap it with the 'removed' one. + // set removed items flags to 'available'. + // if last page only contains 'available' items, then desallocate it (it's empty) + ValueInternalLink *&lastLink = getLastLinkInBucket( index ); + BucketIndex lastItemIndex = 1; // a link can never be empty, so start at 1 + for ( ; + lastItemIndex < ValueInternalLink::itemPerLink; + ++lastItemIndex ) // may be optimized with dicotomic search + { + if ( lastLink->items_[lastItemIndex].isItemAvailable() ) + break; + } + + BucketIndex lastUsedIndex = lastItemIndex - 1; + Value *valueToDelete = &link->items_[index]; + Value *valueToPreserve = &lastLink->items_[lastUsedIndex]; + if ( valueToDelete != valueToPreserve ) + valueToDelete->swap( *valueToPreserve ); + if ( lastUsedIndex == 0 ) // page is now empty + { // remove it from bucket linked list and delete it. + ValueInternalLink *linkPreviousToLast = lastLink->previous_; + if ( linkPreviousToLast != 0 ) // can not deleted bucket link. + { + mapAllocator()->releaseMapLink( lastLink ); + linkPreviousToLast->next_ = 0; + lastLink = linkPreviousToLast; + } + } + else + { + Value dummy; + valueToPreserve->swap( dummy ); // restore deleted to default Value. + valueToPreserve->setItemUsed( false ); + } + --itemCount_; +} + + +ValueInternalLink *& +ValueInternalMap::getLastLinkInBucket( BucketIndex bucketIndex ) +{ + if ( bucketIndex == bucketsSize_ - 1 ) + return tailLink_; + ValueInternalLink *&previous = buckets_[bucketIndex+1].previous_; + if ( !previous ) + previous = &buckets_[bucketIndex]; + return previous; +} + + +Value & +ValueInternalMap::setNewItem( const char *key, + bool isStatic, + ValueInternalLink *link, + BucketIndex index ) +{ + char *duplicatedKey = valueAllocator()->makeMemberName( key ); + ++itemCount_; + link->keys_[index] = duplicatedKey; + link->items_[index].setItemUsed(); + link->items_[index].setMemberNameIsStatic( isStatic ); + return link->items_[index]; // items already default constructed. +} + + +Value & +ValueInternalMap::unsafeAdd( const char *key, + bool isStatic, + HashKey hashedKey ) +{ + JSON_ASSERT_MESSAGE( bucketsSize_ > 0, "ValueInternalMap::unsafeAdd(): internal logic error." ); + BucketIndex bucketIndex = hashedKey % bucketsSize_; + ValueInternalLink *&previousLink = getLastLinkInBucket( bucketIndex ); + ValueInternalLink *link = previousLink; + BucketIndex index; + for ( index =0; index < ValueInternalLink::itemPerLink; ++index ) + { + if ( link->items_[index].isItemAvailable() ) + break; + } + if ( index == ValueInternalLink::itemPerLink ) // need to add a new page + { + ValueInternalLink *newLink = mapAllocator()->allocateMapLink(); + index = 0; + link->next_ = newLink; + previousLink = newLink; + link = newLink; + } + return setNewItem( key, isStatic, link, index ); +} + + +ValueInternalMap::HashKey +ValueInternalMap::hash( const char *key ) const +{ + HashKey hash = 0; + while ( *key ) + hash += *key++ * 37; + return hash; +} + + +int +ValueInternalMap::compare( const ValueInternalMap &other ) const +{ + int sizeDiff( itemCount_ - other.itemCount_ ); + if ( sizeDiff != 0 ) + return sizeDiff; + // Strict order guaranty is required. Compare all keys FIRST, then compare values. + IteratorState it; + IteratorState itEnd; + makeBeginIterator( it ); + makeEndIterator( itEnd ); + for ( ; !equals(it,itEnd); increment(it) ) + { + if ( !other.find( key( it ) ) ) + return 1; + } + + // All keys are equals, let's compare values + makeBeginIterator( it ); + for ( ; !equals(it,itEnd); increment(it) ) + { + const Value *otherValue = other.find( key( it ) ); + int valueDiff = value(it).compare( *otherValue ); + if ( valueDiff != 0 ) + return valueDiff; + } + return 0; +} + + +void +ValueInternalMap::makeBeginIterator( IteratorState &it ) const +{ + it.map_ = const_cast( this ); + it.bucketIndex_ = 0; + it.itemIndex_ = 0; + it.link_ = buckets_; +} + + +void +ValueInternalMap::makeEndIterator( IteratorState &it ) const +{ + it.map_ = const_cast( this ); + it.bucketIndex_ = bucketsSize_; + it.itemIndex_ = 0; + it.link_ = 0; +} + + +bool +ValueInternalMap::equals( const IteratorState &x, const IteratorState &other ) +{ + return x.map_ == other.map_ + && x.bucketIndex_ == other.bucketIndex_ + && x.link_ == other.link_ + && x.itemIndex_ == other.itemIndex_; +} + + +void +ValueInternalMap::incrementBucket( IteratorState &iterator ) +{ + ++iterator.bucketIndex_; + JSON_ASSERT_MESSAGE( iterator.bucketIndex_ <= iterator.map_->bucketsSize_, + "ValueInternalMap::increment(): attempting to iterate beyond end." ); + if ( iterator.bucketIndex_ == iterator.map_->bucketsSize_ ) + iterator.link_ = 0; + else + iterator.link_ = &(iterator.map_->buckets_[iterator.bucketIndex_]); + iterator.itemIndex_ = 0; +} + + +void +ValueInternalMap::increment( IteratorState &iterator ) +{ + JSON_ASSERT_MESSAGE( iterator.map_, "Attempting to iterator using invalid iterator." ); + ++iterator.itemIndex_; + if ( iterator.itemIndex_ == ValueInternalLink::itemPerLink ) + { + JSON_ASSERT_MESSAGE( iterator.link_ != 0, + "ValueInternalMap::increment(): attempting to iterate beyond end." ); + iterator.link_ = iterator.link_->next_; + if ( iterator.link_ == 0 ) + incrementBucket( iterator ); + } + else if ( iterator.link_->items_[iterator.itemIndex_].isItemAvailable() ) + { + incrementBucket( iterator ); + } +} + + +void +ValueInternalMap::decrement( IteratorState &iterator ) +{ + if ( iterator.itemIndex_ == 0 ) + { + JSON_ASSERT_MESSAGE( iterator.map_, "Attempting to iterate using invalid iterator." ); + if ( iterator.link_ == &iterator.map_->buckets_[iterator.bucketIndex_] ) + { + JSON_ASSERT_MESSAGE( iterator.bucketIndex_ > 0, "Attempting to iterate beyond beginning." ); + --(iterator.bucketIndex_); + } + iterator.link_ = iterator.link_->previous_; + iterator.itemIndex_ = ValueInternalLink::itemPerLink - 1; + } +} + + +const char * +ValueInternalMap::key( const IteratorState &iterator ) +{ + JSON_ASSERT_MESSAGE( iterator.link_, "Attempting to iterate using invalid iterator." ); + return iterator.link_->keys_[iterator.itemIndex_]; +} + +const char * +ValueInternalMap::key( const IteratorState &iterator, bool &isStatic ) +{ + JSON_ASSERT_MESSAGE( iterator.link_, "Attempting to iterate using invalid iterator." ); + isStatic = iterator.link_->items_[iterator.itemIndex_].isMemberNameStatic(); + return iterator.link_->keys_[iterator.itemIndex_]; +} + + +Value & +ValueInternalMap::value( const IteratorState &iterator ) +{ + JSON_ASSERT_MESSAGE( iterator.link_, "Attempting to iterate using invalid iterator." ); + return iterator.link_->items_[iterator.itemIndex_]; +} + + +int +ValueInternalMap::distance( const IteratorState &x, const IteratorState &y ) +{ + int offset = 0; + IteratorState it = x; + while ( !equals( it, y ) ) + increment( it ); + return offset; +} diff --git a/util/json/json_reader.cpp b/util/json/json_reader.cpp new file mode 100644 index 00000000..3623e71d --- /dev/null +++ b/util/json/json_reader.cpp @@ -0,0 +1,885 @@ +#include "reader.h" +#include "value.h" +#include +#include +#include +#include +#include +#include + +#if _MSC_VER >= 1400 // VC++ 8.0 +#pragma warning( disable : 4996 ) // disable warning about strdup being deprecated. +#endif + +namespace Json { + +// Implementation of class Features +// //////////////////////////////// + +Features::Features() + : allowComments_( true ) + , strictRoot_( false ) +{ +} + + +Features +Features::all() +{ + return Features(); +} + + +Features +Features::strictMode() +{ + Features features; + features.allowComments_ = false; + features.strictRoot_ = true; + return features; +} + +// Implementation of class Reader +// //////////////////////////////// + + +static inline bool +in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4 ) +{ + return c == c1 || c == c2 || c == c3 || c == c4; +} + +static inline bool +in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4, Reader::Char c5 ) +{ + return c == c1 || c == c2 || c == c3 || c == c4 || c == c5; +} + + +static bool +containsNewLine( Reader::Location begin, + Reader::Location end ) +{ + for ( ;begin < end; ++begin ) + if ( *begin == '\n' || *begin == '\r' ) + return true; + return false; +} + +static std::string codePointToUTF8(unsigned int cp) +{ + std::string result; + + // based on description from http://en.wikipedia.org/wiki/UTF-8 + + if (cp <= 0x7f) + { + result.resize(1); + result[0] = static_cast(cp); + } + else if (cp <= 0x7FF) + { + result.resize(2); + result[1] = static_cast(0x80 | (0x3f & cp)); + result[0] = static_cast(0xC0 | (0x1f & (cp >> 6))); + } + else if (cp <= 0xFFFF) + { + result.resize(3); + result[2] = static_cast(0x80 | (0x3f & cp)); + result[1] = 0x80 | static_cast((0x3f & (cp >> 6))); + result[0] = 0xE0 | static_cast((0xf & (cp >> 12))); + } + else if (cp <= 0x10FFFF) + { + result.resize(4); + result[3] = static_cast(0x80 | (0x3f & cp)); + result[2] = static_cast(0x80 | (0x3f & (cp >> 6))); + result[1] = static_cast(0x80 | (0x3f & (cp >> 12))); + result[0] = static_cast(0xF0 | (0x7 & (cp >> 18))); + } + + return result; +} + + +// Class Reader +// ////////////////////////////////////////////////////////////////// + +Reader::Reader() + : features_( Features::all() ) +{ +} + + +Reader::Reader( const Features &features ) + : features_( features ) +{ +} + + +bool +Reader::parse( const std::string &document, + Value &root, + bool collectComments ) +{ + document_ = document; + const char *begin = document_.c_str(); + const char *end = begin + document_.length(); + return parse( begin, end, root, collectComments ); +} + + +bool +Reader::parse( std::istream& sin, + Value &root, + bool collectComments ) +{ + //std::istream_iterator begin(sin); + //std::istream_iterator end; + // Those would allow streamed input from a file, if parse() were a + // template function. + + // Since std::string is reference-counted, this at least does not + // create an extra copy. + std::string doc; + std::getline(sin, doc, (char)EOF); + return parse( doc, root, collectComments ); +} + +bool +Reader::parse( const char *beginDoc, const char *endDoc, + Value &root, + bool collectComments ) +{ + if ( !features_.allowComments_ ) + { + collectComments = false; + } + + begin_ = beginDoc; + end_ = endDoc; + collectComments_ = collectComments; + current_ = begin_; + lastValueEnd_ = 0; + lastValue_ = 0; + commentsBefore_ = ""; + errors_.clear(); + while ( !nodes_.empty() ) + nodes_.pop(); + nodes_.push( &root ); + + bool successful = readValue(); + Token token; + skipCommentTokens( token ); + if ( collectComments_ && !commentsBefore_.empty() ) + root.setComment( commentsBefore_, commentAfter ); + if ( features_.strictRoot_ ) + { + if ( !root.isArray() && !root.isObject() ) + { + // Set error location to start of doc, ideally should be first token found in doc + token.type_ = tokenError; + token.start_ = beginDoc; + token.end_ = endDoc; + addError( "A valid JSON document must be either an array or an object value.", + token ); + return false; + } + } + return successful; +} + + +bool +Reader::readValue() +{ + Token token; + skipCommentTokens( token ); + bool successful = true; + + if ( collectComments_ && !commentsBefore_.empty() ) + { + currentValue().setComment( commentsBefore_, commentBefore ); + commentsBefore_ = ""; + } + + + switch ( token.type_ ) + { + case tokenObjectBegin: + successful = readObject( token ); + break; + case tokenArrayBegin: + successful = readArray( token ); + break; + case tokenNumber: + successful = decodeNumber( token ); + break; + case tokenString: + successful = decodeString( token ); + break; + case tokenTrue: + currentValue() = true; + break; + case tokenFalse: + currentValue() = false; + break; + case tokenNull: + currentValue() = Value(); + break; + default: + return addError( "Syntax error: value, object or array expected.", token ); + } + + if ( collectComments_ ) + { + lastValueEnd_ = current_; + lastValue_ = ¤tValue(); + } + + return successful; +} + + +void +Reader::skipCommentTokens( Token &token ) +{ + if ( features_.allowComments_ ) + { + do + { + readToken( token ); + } + while ( token.type_ == tokenComment ); + } + else + { + readToken( token ); + } +} + + +bool +Reader::expectToken( TokenType type, Token &token, const char *message ) +{ + readToken( token ); + if ( token.type_ != type ) + return addError( message, token ); + return true; +} + + +bool +Reader::readToken( Token &token ) +{ + skipSpaces(); + token.start_ = current_; + Char c = getNextChar(); + bool ok = true; + switch ( c ) + { + case '{': + token.type_ = tokenObjectBegin; + break; + case '}': + token.type_ = tokenObjectEnd; + break; + case '[': + token.type_ = tokenArrayBegin; + break; + case ']': + token.type_ = tokenArrayEnd; + break; + case '"': + token.type_ = tokenString; + ok = readString(); + break; + case '/': + token.type_ = tokenComment; + ok = readComment(); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + token.type_ = tokenNumber; + readNumber(); + break; + case 't': + token.type_ = tokenTrue; + ok = match( "rue", 3 ); + break; + case 'f': + token.type_ = tokenFalse; + ok = match( "alse", 4 ); + break; + case 'n': + token.type_ = tokenNull; + ok = match( "ull", 3 ); + break; + case ',': + token.type_ = tokenArraySeparator; + break; + case ':': + token.type_ = tokenMemberSeparator; + break; + case 0: + token.type_ = tokenEndOfStream; + break; + default: + ok = false; + break; + } + if ( !ok ) + token.type_ = tokenError; + token.end_ = current_; + return true; +} + + +void +Reader::skipSpaces() +{ + while ( current_ != end_ ) + { + Char c = *current_; + if ( c == ' ' || c == '\t' || c == '\r' || c == '\n' ) + ++current_; + else + break; + } +} + + +bool +Reader::match( Location pattern, + int patternLength ) +{ + if ( end_ - current_ < patternLength ) + return false; + int index = patternLength; + while ( index-- ) + if ( current_[index] != pattern[index] ) + return false; + current_ += patternLength; + return true; +} + + +bool +Reader::readComment() +{ + Location commentBegin = current_ - 1; + Char c = getNextChar(); + bool successful = false; + if ( c == '*' ) + successful = readCStyleComment(); + else if ( c == '/' ) + successful = readCppStyleComment(); + if ( !successful ) + return false; + + if ( collectComments_ ) + { + CommentPlacement placement = commentBefore; + if ( lastValueEnd_ && !containsNewLine( lastValueEnd_, commentBegin ) ) + { + if ( c != '*' || !containsNewLine( commentBegin, current_ ) ) + placement = commentAfterOnSameLine; + } + + addComment( commentBegin, current_, placement ); + } + return true; +} + + +void +Reader::addComment( Location begin, + Location end, + CommentPlacement placement ) +{ + assert( collectComments_ ); + if ( placement == commentAfterOnSameLine ) + { + assert( lastValue_ != 0 ); + lastValue_->setComment( std::string( begin, end ), placement ); + } + else + { + if ( !commentsBefore_.empty() ) + commentsBefore_ += "\n"; + commentsBefore_ += std::string( begin, end ); + } +} + + +bool +Reader::readCStyleComment() +{ + while ( current_ != end_ ) + { + Char c = getNextChar(); + if ( c == '*' && *current_ == '/' ) + break; + } + return getNextChar() == '/'; +} + + +bool +Reader::readCppStyleComment() +{ + while ( current_ != end_ ) + { + Char c = getNextChar(); + if ( c == '\r' || c == '\n' ) + break; + } + return true; +} + + +void +Reader::readNumber() +{ + while ( current_ != end_ ) + { + if ( !(*current_ >= '0' && *current_ <= '9') && + !in( *current_, '.', 'e', 'E', '+', '-' ) ) + break; + ++current_; + } +} + +bool +Reader::readString() +{ + Char c = 0; + while ( current_ != end_ ) + { + c = getNextChar(); + if ( c == '\\' ) + getNextChar(); + else if ( c == '"' ) + break; + } + return c == '"'; +} + + +bool +Reader::readObject( Token &tokenStart ) +{ + Token tokenName; + std::string name; + currentValue() = Value( objectValue ); + while ( readToken( tokenName ) ) + { + bool initialTokenOk = true; + while ( tokenName.type_ == tokenComment && initialTokenOk ) + initialTokenOk = readToken( tokenName ); + if ( !initialTokenOk ) + break; + if ( tokenName.type_ == tokenObjectEnd && name.empty() ) // empty object + return true; + if ( tokenName.type_ != tokenString ) + break; + + name = ""; + if ( !decodeString( tokenName, name ) ) + return recoverFromError( tokenObjectEnd ); + + Token colon; + if ( !readToken( colon ) || colon.type_ != tokenMemberSeparator ) + { + return addErrorAndRecover( "Missing ':' after object member name", + colon, + tokenObjectEnd ); + } + Value &value = currentValue()[ name ]; + nodes_.push( &value ); + bool ok = readValue(); + nodes_.pop(); + if ( !ok ) // error already set + return recoverFromError( tokenObjectEnd ); + + Token comma; + if ( !readToken( comma ) + || ( comma.type_ != tokenObjectEnd && + comma.type_ != tokenArraySeparator && + comma.type_ != tokenComment ) ) + { + return addErrorAndRecover( "Missing ',' or '}' in object declaration", + comma, + tokenObjectEnd ); + } + bool finalizeTokenOk = true; + while ( comma.type_ == tokenComment && + finalizeTokenOk ) + finalizeTokenOk = readToken( comma ); + if ( comma.type_ == tokenObjectEnd ) + return true; + } + return addErrorAndRecover( "Missing '}' or object member name", + tokenName, + tokenObjectEnd ); +} + + +bool +Reader::readArray( Token &tokenStart ) +{ + currentValue() = Value( arrayValue ); + skipSpaces(); + if ( *current_ == ']' ) // empty array + { + Token endArray; + readToken( endArray ); + return true; + } + int index = 0; + while ( true ) + { + Value &value = currentValue()[ index++ ]; + nodes_.push( &value ); + bool ok = readValue(); + nodes_.pop(); + if ( !ok ) // error already set + return recoverFromError( tokenArrayEnd ); + + Token token; + // Accept Comment after last item in the array. + ok = readToken( token ); + while ( token.type_ == tokenComment && ok ) + { + ok = readToken( token ); + } + bool badTokenType = ( token.type_ == tokenArraySeparator && + token.type_ == tokenArrayEnd ); + if ( !ok || badTokenType ) + { + return addErrorAndRecover( "Missing ',' or ']' in array declaration", + token, + tokenArrayEnd ); + } + if ( token.type_ == tokenArrayEnd ) + break; + } + return true; +} + + +bool +Reader::decodeNumber( Token &token ) +{ + bool isDouble = false; + for ( Location inspect = token.start_; inspect != token.end_; ++inspect ) + { + isDouble = isDouble + || in( *inspect, '.', 'e', 'E', '+' ) + || ( *inspect == '-' && inspect != token.start_ ); + } + if ( isDouble ) + return decodeDouble( token ); + Location current = token.start_; + bool isNegative = *current == '-'; + if ( isNegative ) + ++current; + Value::UInt threshold = (isNegative ? Value::UInt(-Value::minInt) + : Value::maxUInt) / 10; + Value::UInt value = 0; + while ( current < token.end_ ) + { + Char c = *current++; + if ( c < '0' || c > '9' ) + return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token ); + if ( value >= threshold ) + return decodeDouble( token ); + value = value * 10 + Value::UInt(c - '0'); + } + if ( isNegative ) + currentValue() = -Value::Int( value ); + else if ( value <= Value::UInt(Value::maxInt) ) + currentValue() = Value::Int( value ); + else + currentValue() = value; + return true; +} + + +bool +Reader::decodeDouble( Token &token ) +{ + double value = 0; + const int bufferSize = 32; + int count; + int length = int(token.end_ - token.start_); + if ( length <= bufferSize ) + { + Char buffer[bufferSize]; + memcpy( buffer, token.start_, length ); + buffer[length] = 0; + count = sscanf( buffer, "%lf", &value ); + } + else + { + std::string buffer( token.start_, token.end_ ); + count = sscanf( buffer.c_str(), "%lf", &value ); + } + + if ( count != 1 ) + return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token ); + currentValue() = value; + return true; +} + + +bool +Reader::decodeString( Token &token ) +{ + std::string decoded; + if ( !decodeString( token, decoded ) ) + return false; + currentValue() = decoded; + return true; +} + + +bool +Reader::decodeString( Token &token, std::string &decoded ) +{ + decoded.reserve( token.end_ - token.start_ - 2 ); + Location current = token.start_ + 1; // skip '"' + Location end = token.end_ - 1; // do not include '"' + while ( current != end ) + { + Char c = *current++; + if ( c == '"' ) + break; + else if ( c == '\\' ) + { + if ( current == end ) + return addError( "Empty escape sequence in string", token, current ); + Char escape = *current++; + switch ( escape ) + { + case '"': decoded += '"'; break; + case '/': decoded += '/'; break; + case '\\': decoded += '\\'; break; + case 'b': decoded += '\b'; break; + case 'f': decoded += '\f'; break; + case 'n': decoded += '\n'; break; + case 'r': decoded += '\r'; break; + case 't': decoded += '\t'; break; + case 'u': + { + unsigned int unicode; + if ( !decodeUnicodeCodePoint( token, current, end, unicode ) ) + return false; + decoded += codePointToUTF8(unicode); + } + break; + default: + return addError( "Bad escape sequence in string", token, current ); + } + } + else + { + decoded += c; + } + } + return true; +} + +bool +Reader::decodeUnicodeCodePoint( Token &token, + Location ¤t, + Location end, + unsigned int &unicode ) +{ + + if ( !decodeUnicodeEscapeSequence( token, current, end, unicode ) ) + return false; + if (unicode >= 0xD800 && unicode <= 0xDBFF) + { + // surrogate pairs + if (end - current < 6) + return addError( "additional six characters expected to parse unicode surrogate pair.", token, current ); + unsigned int surrogatePair; + if (*(current++) == '\\' && *(current++)== 'u') + { + if (decodeUnicodeEscapeSequence( token, current, end, surrogatePair )) + { + unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); + } + else + return false; + } + else + return addError( "expecting another \\u token to begin the second half of a unicode surrogate pair", token, current ); + } + return true; +} + +bool +Reader::decodeUnicodeEscapeSequence( Token &token, + Location ¤t, + Location end, + unsigned int &unicode ) +{ + if ( end - current < 4 ) + return addError( "Bad unicode escape sequence in string: four digits expected.", token, current ); + unicode = 0; + for ( int index =0; index < 4; ++index ) + { + Char c = *current++; + unicode *= 16; + if ( c >= '0' && c <= '9' ) + unicode += c - '0'; + else if ( c >= 'a' && c <= 'f' ) + unicode += c - 'a' + 10; + else if ( c >= 'A' && c <= 'F' ) + unicode += c - 'A' + 10; + else + return addError( "Bad unicode escape sequence in string: hexadecimal digit expected.", token, current ); + } + return true; +} + + +bool +Reader::addError( const std::string &message, + Token &token, + Location extra ) +{ + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = extra; + errors_.push_back( info ); + return false; +} + + +bool +Reader::recoverFromError( TokenType skipUntilToken ) +{ + int errorCount = int(errors_.size()); + Token skip; + while ( true ) + { + if ( !readToken(skip) ) + errors_.resize( errorCount ); // discard errors caused by recovery + if ( skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream ) + break; + } + errors_.resize( errorCount ); + return false; +} + + +bool +Reader::addErrorAndRecover( const std::string &message, + Token &token, + TokenType skipUntilToken ) +{ + addError( message, token ); + return recoverFromError( skipUntilToken ); +} + + +Value & +Reader::currentValue() +{ + return *(nodes_.top()); +} + + +Reader::Char +Reader::getNextChar() +{ + if ( current_ == end_ ) + return 0; + return *current_++; +} + + +void +Reader::getLocationLineAndColumn( Location location, + int &line, + int &column ) const +{ + Location current = begin_; + Location lastLineStart = current; + line = 0; + while ( current < location && current != end_ ) + { + Char c = *current++; + if ( c == '\r' ) + { + if ( *current == '\n' ) + ++current; + lastLineStart = current; + ++line; + } + else if ( c == '\n' ) + { + lastLineStart = current; + ++line; + } + } + // column & line start at 1 + column = int(location - lastLineStart) + 1; + ++line; +} + + +std::string +Reader::getLocationLineAndColumn( Location location ) const +{ + int line, column; + getLocationLineAndColumn( location, line, column ); + char buffer[18+16+16+1]; + sprintf( buffer, "Line %d, Column %d", line, column ); + return buffer; +} + + +std::string +Reader::getFormatedErrorMessages() const +{ + std::string formattedMessage; + for ( Errors::const_iterator itError = errors_.begin(); + itError != errors_.end(); + ++itError ) + { + const ErrorInfo &error = *itError; + formattedMessage += "* " + getLocationLineAndColumn( error.token_.start_ ) + "\n"; + formattedMessage += " " + error.message_ + "\n"; + if ( error.extra_ ) + formattedMessage += "See " + getLocationLineAndColumn( error.extra_ ) + " for detail.\n"; + } + return formattedMessage; +} + + +std::istream& operator>>( std::istream &sin, Value &root ) +{ + Json::Reader reader; + bool ok = reader.parse(sin, root, true); + //JSON_ASSERT( ok ); + if (!ok) throw std::runtime_error(reader.getFormatedErrorMessages()); + return sin; +} + + +} // namespace Json diff --git a/util/json/json_value.cpp b/util/json/json_value.cpp new file mode 100644 index 00000000..f418af26 --- /dev/null +++ b/util/json/json_value.cpp @@ -0,0 +1,1718 @@ +#include +#include "value.h" +#include "writer.h" +#include +#include +#include +#include +#ifdef JSON_USE_CPPTL +# include +#endif +#include // size_t +#ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR +# include "json_batchallocator.h" +#endif // #ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR + +#define JSON_ASSERT_UNREACHABLE assert( false ) +#define JSON_ASSERT( condition ) assert( condition ); // @todo <= change this into an exception throw +#define JSON_ASSERT_MESSAGE( condition, message ) if (!( condition )) throw std::runtime_error( message ); + +namespace Json { + +const Value Value::null; +const Int Value::minInt = Int( ~(UInt(-1)/2) ); +const Int Value::maxInt = Int( UInt(-1)/2 ); +const UInt Value::maxUInt = UInt(-1); + +// A "safe" implementation of strdup. Allow null pointer to be passed. +// Also avoid warning on msvc80. +// +//inline char *safeStringDup( const char *czstring ) +//{ +// if ( czstring ) +// { +// const size_t length = (unsigned int)( strlen(czstring) + 1 ); +// char *newString = static_cast( malloc( length ) ); +// memcpy( newString, czstring, length ); +// return newString; +// } +// return 0; +//} +// +//inline char *safeStringDup( const std::string &str ) +//{ +// if ( !str.empty() ) +// { +// const size_t length = str.length(); +// char *newString = static_cast( malloc( length + 1 ) ); +// memcpy( newString, str.c_str(), length ); +// newString[length] = 0; +// return newString; +// } +// return 0; +//} + +ValueAllocator::~ValueAllocator() +{ +} + +class DefaultValueAllocator : public ValueAllocator +{ +public: + virtual ~DefaultValueAllocator() + { + } + + virtual char *makeMemberName( const char *memberName ) + { + return duplicateStringValue( memberName ); + } + + virtual void releaseMemberName( char *memberName ) + { + releaseStringValue( memberName ); + } + + virtual char *duplicateStringValue( const char *value, + unsigned int length = unknown ) + { + //@todo invesgate this old optimization + //if ( !value || value[0] == 0 ) + // return 0; + + if ( length == unknown ) + length = (unsigned int)strlen(value); + char *newString = static_cast( malloc( length + 1 ) ); + memcpy( newString, value, length ); + newString[length] = 0; + return newString; + } + + virtual void releaseStringValue( char *value ) + { + if ( value ) + free( value ); + } +}; + +static ValueAllocator *&valueAllocator() +{ + static DefaultValueAllocator defaultAllocator; + static ValueAllocator *valueAllocator = &defaultAllocator; + return valueAllocator; +} + +static struct DummyValueAllocatorInitializer { + DummyValueAllocatorInitializer() + { + valueAllocator(); // ensure valueAllocator() statics are initialized before main(). + } +} dummyValueAllocatorInitializer; + + + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ValueInternals... +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +#ifdef JSON_VALUE_USE_INTERNAL_MAP +# include "json_internalarray.inl" +# include "json_internalmap.inl" +#endif // JSON_VALUE_USE_INTERNAL_MAP + +# include "json_valueiterator.inl" + + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::CommentInfo +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + + +Value::CommentInfo::CommentInfo() + : comment_( 0 ) +{ +} + +Value::CommentInfo::~CommentInfo() +{ + if ( comment_ ) + valueAllocator()->releaseStringValue( comment_ ); +} + + +void +Value::CommentInfo::setComment( const char *text ) +{ + if ( comment_ ) + valueAllocator()->releaseStringValue( comment_ ); + JSON_ASSERT( text ); + JSON_ASSERT_MESSAGE( text[0]=='\0' || text[0]=='/', "Comments must start with /"); + // It seems that /**/ style comments are acceptable as well. + comment_ = valueAllocator()->duplicateStringValue( text ); +} + + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::CZString +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +# ifndef JSON_VALUE_USE_INTERNAL_MAP + +// Notes: index_ indicates if the string was allocated when +// a string is stored. + +Value::CZString::CZString( int index ) + : cstr_( 0 ) + , index_( index ) +{ +} + +Value::CZString::CZString( const char *cstr, DuplicationPolicy allocate ) + : cstr_( allocate == duplicate ? valueAllocator()->makeMemberName(cstr) + : cstr ) + , index_( allocate ) +{ +} + +Value::CZString::CZString( const CZString &other ) +: cstr_( other.index_ != noDuplication && other.cstr_ != 0 + ? valueAllocator()->makeMemberName( other.cstr_ ) + : other.cstr_ ) + , index_( other.cstr_ ? (other.index_ == noDuplication ? noDuplication : duplicate) + : other.index_ ) +{ +} + +Value::CZString::~CZString() +{ + if ( cstr_ && index_ == duplicate ) + valueAllocator()->releaseMemberName( const_cast( cstr_ ) ); +} + +void +Value::CZString::swap( CZString &other ) +{ + std::swap( cstr_, other.cstr_ ); + std::swap( index_, other.index_ ); +} + +Value::CZString & +Value::CZString::operator =( const CZString &other ) +{ + CZString temp( other ); + swap( temp ); + return *this; +} + +bool +Value::CZString::operator<( const CZString &other ) const +{ + if ( cstr_ ) + return strcmp( cstr_, other.cstr_ ) < 0; + return index_ < other.index_; +} + +bool +Value::CZString::operator==( const CZString &other ) const +{ + if ( cstr_ ) + return strcmp( cstr_, other.cstr_ ) == 0; + return index_ == other.index_; +} + + +int +Value::CZString::index() const +{ + return index_; +} + + +const char * +Value::CZString::c_str() const +{ + return cstr_; +} + +bool +Value::CZString::isStaticString() const +{ + return index_ == noDuplication; +} + +#endif // ifndef JSON_VALUE_USE_INTERNAL_MAP + + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::Value +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +/*! \internal Default constructor initialization must be equivalent to: + * memset( this, 0, sizeof(Value) ) + * This optimization is used in ValueInternalMap fast allocator. + */ +Value::Value( ValueType type ) + : type_( type ) + , allocated_( 0 ) + , comments_( 0 ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif +{ + switch ( type ) + { + case nullValue: + break; + case intValue: + case uintValue: + value_.int_ = 0; + break; + case realValue: + value_.real_ = 0.0; + break; + case stringValue: + value_.string_ = 0; + break; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + value_.map_ = new ObjectValues(); + break; +#else + case arrayValue: + value_.array_ = arrayAllocator()->newArray(); + break; + case objectValue: + value_.map_ = mapAllocator()->newMap(); + break; +#endif + case booleanValue: + value_.bool_ = false; + break; + default: + JSON_ASSERT_UNREACHABLE; + } +} + + +Value::Value( Int value ) + : type_( intValue ) + , comments_( 0 ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif +{ + value_.int_ = value; +} + + +Value::Value( UInt value ) + : type_( uintValue ) + , comments_( 0 ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif +{ + value_.uint_ = value; +} + +Value::Value( double value ) + : type_( realValue ) + , comments_( 0 ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif +{ + value_.real_ = value; +} + +Value::Value( const char *value ) + : type_( stringValue ) + , allocated_( true ) + , comments_( 0 ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif +{ + value_.string_ = valueAllocator()->duplicateStringValue( value ); +} + + +Value::Value( const char *beginValue, + const char *endValue ) + : type_( stringValue ) + , allocated_( true ) + , comments_( 0 ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif +{ + value_.string_ = valueAllocator()->duplicateStringValue( beginValue, + UInt(endValue - beginValue) ); +} + + +Value::Value( const std::string &value ) + : type_( stringValue ) + , allocated_( true ) + , comments_( 0 ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif +{ + value_.string_ = valueAllocator()->duplicateStringValue( value.c_str(), + (unsigned int)value.length() ); + +} + +Value::Value( const StaticString &value ) + : type_( stringValue ) + , allocated_( false ) + , comments_( 0 ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif +{ + value_.string_ = const_cast( value.c_str() ); +} + + +# ifdef JSON_USE_CPPTL +Value::Value( const CppTL::ConstString &value ) + : type_( stringValue ) + , allocated_( true ) + , comments_( 0 ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif +{ + value_.string_ = valueAllocator()->duplicateStringValue( value, value.length() ); +} +# endif + +Value::Value( bool value ) + : type_( booleanValue ) + , comments_( 0 ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif +{ + value_.bool_ = value; +} + + +Value::Value( const Value &other ) + : type_( other.type_ ) + , comments_( 0 ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif +{ + switch ( type_ ) + { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + value_ = other.value_; + break; + case stringValue: + if ( other.value_.string_ ) + { + value_.string_ = valueAllocator()->duplicateStringValue( other.value_.string_ ); + allocated_ = true; + } + else + value_.string_ = 0; + break; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + value_.map_ = new ObjectValues( *other.value_.map_ ); + break; +#else + case arrayValue: + value_.array_ = arrayAllocator()->newArrayCopy( *other.value_.array_ ); + break; + case objectValue: + value_.map_ = mapAllocator()->newMapCopy( *other.value_.map_ ); + break; +#endif + default: + JSON_ASSERT_UNREACHABLE; + } + if ( other.comments_ ) + { + comments_ = new CommentInfo[numberOfCommentPlacement]; + for ( int comment =0; comment < numberOfCommentPlacement; ++comment ) + { + const CommentInfo &otherComment = other.comments_[comment]; + if ( otherComment.comment_ ) + comments_[comment].setComment( otherComment.comment_ ); + } + } +} + + +Value::~Value() +{ + switch ( type_ ) + { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + break; + case stringValue: + if ( allocated_ ) + valueAllocator()->releaseStringValue( value_.string_ ); + break; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + delete value_.map_; + break; +#else + case arrayValue: + arrayAllocator()->destructArray( value_.array_ ); + break; + case objectValue: + mapAllocator()->destructMap( value_.map_ ); + break; +#endif + default: + JSON_ASSERT_UNREACHABLE; + } + + if ( comments_ ) + delete[] comments_; +} + +Value & +Value::operator=( const Value &other ) +{ + Value temp( other ); + swap( temp ); + return *this; +} + +void +Value::swap( Value &other ) +{ + ValueType temp = type_; + type_ = other.type_; + other.type_ = temp; + std::swap( value_, other.value_ ); + int temp2 = allocated_; + allocated_ = other.allocated_; + other.allocated_ = temp2; +} + +ValueType +Value::type() const +{ + return type_; +} + + +int +Value::compare( const Value &other ) +{ + /* + int typeDelta = other.type_ - type_; + switch ( type_ ) + { + case nullValue: + + return other.type_ == type_; + case intValue: + if ( other.type_.isNumeric() + case uintValue: + case realValue: + case booleanValue: + break; + case stringValue, + break; + case arrayValue: + delete value_.array_; + break; + case objectValue: + delete value_.map_; + default: + JSON_ASSERT_UNREACHABLE; + } + */ + return 0; // unreachable +} + +bool +Value::operator <( const Value &other ) const +{ + int typeDelta = type_ - other.type_; + if ( typeDelta ) + return typeDelta < 0 ? true : false; + switch ( type_ ) + { + case nullValue: + return false; + case intValue: + return value_.int_ < other.value_.int_; + case uintValue: + return value_.uint_ < other.value_.uint_; + case realValue: + return value_.real_ < other.value_.real_; + case booleanValue: + return value_.bool_ < other.value_.bool_; + case stringValue: + return ( value_.string_ == 0 && other.value_.string_ ) + || ( other.value_.string_ + && value_.string_ + && strcmp( value_.string_, other.value_.string_ ) < 0 ); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + { + int delta = int( value_.map_->size() - other.value_.map_->size() ); + if ( delta ) + return delta < 0; + return (*value_.map_) < (*other.value_.map_); + } +#else + case arrayValue: + return value_.array_->compare( *(other.value_.array_) ) < 0; + case objectValue: + return value_.map_->compare( *(other.value_.map_) ) < 0; +#endif + default: + JSON_ASSERT_UNREACHABLE; + } + return 0; // unreachable +} + +bool +Value::operator <=( const Value &other ) const +{ + return !(other > *this); +} + +bool +Value::operator >=( const Value &other ) const +{ + return !(*this < other); +} + +bool +Value::operator >( const Value &other ) const +{ + return other < *this; +} + +bool +Value::operator ==( const Value &other ) const +{ + //if ( type_ != other.type_ ) + // GCC 2.95.3 says: + // attempt to take address of bit-field structure member `Json::Value::type_' + // Beats me, but a temp solves the problem. + int temp = other.type_; + if ( type_ != temp ) + return false; + switch ( type_ ) + { + case nullValue: + return true; + case intValue: + return value_.int_ == other.value_.int_; + case uintValue: + return value_.uint_ == other.value_.uint_; + case realValue: + return value_.real_ == other.value_.real_; + case booleanValue: + return value_.bool_ == other.value_.bool_; + case stringValue: + return ( value_.string_ == other.value_.string_ ) + || ( other.value_.string_ + && value_.string_ + && strcmp( value_.string_, other.value_.string_ ) == 0 ); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + return value_.map_->size() == other.value_.map_->size() + && (*value_.map_) == (*other.value_.map_); +#else + case arrayValue: + return value_.array_->compare( *(other.value_.array_) ) == 0; + case objectValue: + return value_.map_->compare( *(other.value_.map_) ) == 0; +#endif + default: + JSON_ASSERT_UNREACHABLE; + } + return 0; // unreachable +} + +bool +Value::operator !=( const Value &other ) const +{ + return !( *this == other ); +} + +const char * +Value::asCString() const +{ + JSON_ASSERT( type_ == stringValue ); + return value_.string_; +} + + +std::string +Value::asString() const +{ + switch ( type_ ) + { + case nullValue: + return ""; + case stringValue: + return value_.string_ ? value_.string_ : ""; + case booleanValue: + return value_.bool_ ? "true" : "false"; + case intValue: + case uintValue: + case realValue: + case arrayValue: + case objectValue: + JSON_ASSERT_MESSAGE( false, "Type is not convertible to string" ); + default: + JSON_ASSERT_UNREACHABLE; + } + return ""; // unreachable +} + +# ifdef JSON_USE_CPPTL +CppTL::ConstString +Value::asConstString() const +{ + return CppTL::ConstString( asString().c_str() ); +} +# endif + +Value::Int +Value::asInt() const +{ + switch ( type_ ) + { + case nullValue: + return 0; + case intValue: + return value_.int_; + case uintValue: + JSON_ASSERT_MESSAGE( value_.uint_ < (unsigned)maxInt, "integer out of signed integer range" ); + return value_.uint_; + case realValue: + JSON_ASSERT_MESSAGE( value_.real_ >= minInt && value_.real_ <= maxInt, "Real out of signed integer range" ); + return Int( value_.real_ ); + case booleanValue: + return value_.bool_ ? 1 : 0; + case stringValue: + case arrayValue: + case objectValue: + JSON_ASSERT_MESSAGE( false, "Type is not convertible to int" ); + default: + JSON_ASSERT_UNREACHABLE; + } + return 0; // unreachable; +} + +Value::UInt +Value::asUInt() const +{ + switch ( type_ ) + { + case nullValue: + return 0; + case intValue: + JSON_ASSERT_MESSAGE( value_.int_ >= 0, "Negative integer can not be converted to unsigned integer" ); + return value_.int_; + case uintValue: + return value_.uint_; + case realValue: + JSON_ASSERT_MESSAGE( value_.real_ >= 0 && value_.real_ <= maxUInt, "Real out of unsigned integer range" ); + return UInt( value_.real_ ); + case booleanValue: + return value_.bool_ ? 1 : 0; + case stringValue: + case arrayValue: + case objectValue: + JSON_ASSERT_MESSAGE( false, "Type is not convertible to uint" ); + default: + JSON_ASSERT_UNREACHABLE; + } + return 0; // unreachable; +} + +double +Value::asDouble() const +{ + switch ( type_ ) + { + case nullValue: + return 0.0; + case intValue: + return value_.int_; + case uintValue: + return value_.uint_; + case realValue: + return value_.real_; + case booleanValue: + return value_.bool_ ? 1.0 : 0.0; + case stringValue: + case arrayValue: + case objectValue: + JSON_ASSERT_MESSAGE( false, "Type is not convertible to double" ); + default: + JSON_ASSERT_UNREACHABLE; + } + return 0; // unreachable; +} + +bool +Value::asBool() const +{ + switch ( type_ ) + { + case nullValue: + return false; + case intValue: + case uintValue: + return value_.int_ != 0; + case realValue: + return value_.real_ != 0.0; + case booleanValue: + return value_.bool_; + case stringValue: + return value_.string_ && value_.string_[0] != 0; + case arrayValue: + case objectValue: + return value_.map_->size() != 0; + default: + JSON_ASSERT_UNREACHABLE; + } + return false; // unreachable; +} + + +bool +Value::isConvertibleTo( ValueType other ) const +{ + switch ( type_ ) + { + case nullValue: + return true; + case intValue: + return ( other == nullValue && value_.int_ == 0 ) + || other == intValue + || ( other == uintValue && value_.int_ >= 0 ) + || other == realValue + || other == stringValue + || other == booleanValue; + case uintValue: + return ( other == nullValue && value_.uint_ == 0 ) + || ( other == intValue && value_.uint_ <= (unsigned)maxInt ) + || other == uintValue + || other == realValue + || other == stringValue + || other == booleanValue; + case realValue: + return ( other == nullValue && value_.real_ == 0.0 ) + || ( other == intValue && value_.real_ >= minInt && value_.real_ <= maxInt ) + || ( other == uintValue && value_.real_ >= 0 && value_.real_ <= maxUInt ) + || other == realValue + || other == stringValue + || other == booleanValue; + case booleanValue: + return ( other == nullValue && value_.bool_ == false ) + || other == intValue + || other == uintValue + || other == realValue + || other == stringValue + || other == booleanValue; + case stringValue: + return other == stringValue + || ( other == nullValue && (!value_.string_ || value_.string_[0] == 0) ); + case arrayValue: + return other == arrayValue + || ( other == nullValue && value_.map_->size() == 0 ); + case objectValue: + return other == objectValue + || ( other == nullValue && value_.map_->size() == 0 ); + default: + JSON_ASSERT_UNREACHABLE; + } + return false; // unreachable; +} + + +/// Number of values in array or object +Value::UInt +Value::size() const +{ + switch ( type_ ) + { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + case stringValue: + return 0; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: // size of the array is highest index + 1 + if ( !value_.map_->empty() ) + { + ObjectValues::const_iterator itLast = value_.map_->end(); + --itLast; + return (*itLast).first.index()+1; + } + return 0; + case objectValue: + return Int( value_.map_->size() ); +#else + case arrayValue: + return Int( value_.array_->size() ); + case objectValue: + return Int( value_.map_->size() ); +#endif + default: + JSON_ASSERT_UNREACHABLE; + } + return 0; // unreachable; +} + + +bool +Value::empty() const +{ + if ( isNull() || isArray() || isObject() ) + return size() == 0u; + else + return false; +} + + +bool +Value::operator!() const +{ + return isNull(); +} + + +void +Value::clear() +{ + JSON_ASSERT( type_ == nullValue || type_ == arrayValue || type_ == objectValue ); + + switch ( type_ ) + { +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + value_.map_->clear(); + break; +#else + case arrayValue: + value_.array_->clear(); + break; + case objectValue: + value_.map_->clear(); + break; +#endif + default: + break; + } +} + +void +Value::resize( UInt newSize ) +{ + JSON_ASSERT( type_ == nullValue || type_ == arrayValue ); + if ( type_ == nullValue ) + *this = Value( arrayValue ); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + UInt oldSize = size(); + if ( newSize == 0 ) + clear(); + else if ( newSize > oldSize ) + (*this)[ newSize - 1 ]; + else + { + for ( UInt index = newSize; index < oldSize; ++index ) + value_.map_->erase( index ); + assert( size() == newSize ); + } +#else + value_.array_->resize( newSize ); +#endif +} + + +Value & +Value::operator[]( UInt index ) +{ + JSON_ASSERT( type_ == nullValue || type_ == arrayValue ); + if ( type_ == nullValue ) + *this = Value( arrayValue ); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString key( index ); + ObjectValues::iterator it = value_.map_->lower_bound( key ); + if ( it != value_.map_->end() && (*it).first == key ) + return (*it).second; + + ObjectValues::value_type defaultValue( key, null ); + it = value_.map_->insert( it, defaultValue ); + return (*it).second; +#else + return value_.array_->resolveReference( index ); +#endif +} + + +const Value & +Value::operator[]( UInt index ) const +{ + JSON_ASSERT( type_ == nullValue || type_ == arrayValue ); + if ( type_ == nullValue ) + return null; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString key( index ); + ObjectValues::const_iterator it = value_.map_->find( key ); + if ( it == value_.map_->end() ) + return null; + return (*it).second; +#else + Value *value = value_.array_->find( index ); + return value ? *value : null; +#endif +} + + +Value & +Value::operator[]( const char *key ) +{ + return resolveReference( key, false ); +} + + +Value & +Value::resolveReference( const char *key, + bool isStatic ) +{ + JSON_ASSERT( type_ == nullValue || type_ == objectValue ); + if ( type_ == nullValue ) + *this = Value( objectValue ); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString actualKey( key, isStatic ? CZString::noDuplication + : CZString::duplicateOnCopy ); + ObjectValues::iterator it = value_.map_->lower_bound( actualKey ); + if ( it != value_.map_->end() && (*it).first == actualKey ) + return (*it).second; + + ObjectValues::value_type defaultValue( actualKey, null ); + it = value_.map_->insert( it, defaultValue ); + Value &value = (*it).second; + return value; +#else + return value_.map_->resolveReference( key, isStatic ); +#endif +} + + +Value +Value::get( UInt index, + const Value &defaultValue ) const +{ + const Value *value = &((*this)[index]); + return value == &null ? defaultValue : *value; +} + + +bool +Value::isValidIndex( UInt index ) const +{ + return index < size(); +} + + + +const Value & +Value::operator[]( const char *key ) const +{ + JSON_ASSERT( type_ == nullValue || type_ == objectValue ); + if ( type_ == nullValue ) + return null; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString actualKey( key, CZString::noDuplication ); + ObjectValues::const_iterator it = value_.map_->find( actualKey ); + if ( it == value_.map_->end() ) + return null; + return (*it).second; +#else + const Value *value = value_.map_->find( key ); + return value ? *value : null; +#endif +} + + +Value & +Value::operator[]( const std::string &key ) +{ + return (*this)[ key.c_str() ]; +} + + +const Value & +Value::operator[]( const std::string &key ) const +{ + return (*this)[ key.c_str() ]; +} + +Value & +Value::operator[]( const StaticString &key ) +{ + return resolveReference( key, true ); +} + + +# ifdef JSON_USE_CPPTL +Value & +Value::operator[]( const CppTL::ConstString &key ) +{ + return (*this)[ key.c_str() ]; +} + + +const Value & +Value::operator[]( const CppTL::ConstString &key ) const +{ + return (*this)[ key.c_str() ]; +} +# endif + + +Value & +Value::append( const Value &value ) +{ + return (*this)[size()] = value; +} + + +Value +Value::get( const char *key, + const Value &defaultValue ) const +{ + const Value *value = &((*this)[key]); + return value == &null ? defaultValue : *value; +} + + +Value +Value::get( const std::string &key, + const Value &defaultValue ) const +{ + return get( key.c_str(), defaultValue ); +} + +Value +Value::removeMember( const char* key ) +{ + JSON_ASSERT( type_ == nullValue || type_ == objectValue ); + if ( type_ == nullValue ) + return null; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString actualKey( key, CZString::noDuplication ); + ObjectValues::iterator it = value_.map_->find( actualKey ); + if ( it == value_.map_->end() ) + return null; + Value old(it->second); + value_.map_->erase(it); + return old; +#else + Value *value = value_.map_->find( key ); + if (value){ + Value old(*value); + value_.map_.remove( key ); + return old; + } else { + return null; + } +#endif +} + +Value +Value::removeMember( const std::string &key ) +{ + return removeMember( key.c_str() ); +} + +# ifdef JSON_USE_CPPTL +Value +Value::get( const CppTL::ConstString &key, + const Value &defaultValue ) const +{ + return get( key.c_str(), defaultValue ); +} +# endif + +bool +Value::isMember( const char *key ) const +{ + const Value *value = &((*this)[key]); + return value != &null; +} + + +bool +Value::isMember( const std::string &key ) const +{ + return isMember( key.c_str() ); +} + + +# ifdef JSON_USE_CPPTL +bool +Value::isMember( const CppTL::ConstString &key ) const +{ + return isMember( key.c_str() ); +} +#endif + +Value::Members +Value::getMemberNames() const +{ + JSON_ASSERT( type_ == nullValue || type_ == objectValue ); + if ( type_ == nullValue ) + return Value::Members(); + Members members; + members.reserve( value_.map_->size() ); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + ObjectValues::const_iterator it = value_.map_->begin(); + ObjectValues::const_iterator itEnd = value_.map_->end(); + for ( ; it != itEnd; ++it ) + members.push_back( std::string( (*it).first.c_str() ) ); +#else + ValueInternalMap::IteratorState it; + ValueInternalMap::IteratorState itEnd; + value_.map_->makeBeginIterator( it ); + value_.map_->makeEndIterator( itEnd ); + for ( ; !ValueInternalMap::equals( it, itEnd ); ValueInternalMap::increment(it) ) + members.push_back( std::string( ValueInternalMap::key( it ) ) ); +#endif + return members; +} +// +//# ifdef JSON_USE_CPPTL +//EnumMemberNames +//Value::enumMemberNames() const +//{ +// if ( type_ == objectValue ) +// { +// return CppTL::Enum::any( CppTL::Enum::transform( +// CppTL::Enum::keys( *(value_.map_), CppTL::Type() ), +// MemberNamesTransform() ) ); +// } +// return EnumMemberNames(); +//} +// +// +//EnumValues +//Value::enumValues() const +//{ +// if ( type_ == objectValue || type_ == arrayValue ) +// return CppTL::Enum::anyValues( *(value_.map_), +// CppTL::Type() ); +// return EnumValues(); +//} +// +//# endif + + +bool +Value::isNull() const +{ + return type_ == nullValue; +} + + +bool +Value::isBool() const +{ + return type_ == booleanValue; +} + + +bool +Value::isInt() const +{ + return type_ == intValue; +} + + +bool +Value::isUInt() const +{ + return type_ == uintValue; +} + + +bool +Value::isIntegral() const +{ + return type_ == intValue + || type_ == uintValue + || type_ == booleanValue; +} + + +bool +Value::isDouble() const +{ + return type_ == realValue; +} + + +bool +Value::isNumeric() const +{ + return isIntegral() || isDouble(); +} + + +bool +Value::isString() const +{ + return type_ == stringValue; +} + + +bool +Value::isArray() const +{ + return type_ == nullValue || type_ == arrayValue; +} + + +bool +Value::isObject() const +{ + return type_ == nullValue || type_ == objectValue; +} + + +void +Value::setComment( const char *comment, + CommentPlacement placement ) +{ + if ( !comments_ ) + comments_ = new CommentInfo[numberOfCommentPlacement]; + comments_[placement].setComment( comment ); +} + + +void +Value::setComment( const std::string &comment, + CommentPlacement placement ) +{ + setComment( comment.c_str(), placement ); +} + + +bool +Value::hasComment( CommentPlacement placement ) const +{ + return comments_ != 0 && comments_[placement].comment_ != 0; +} + +std::string +Value::getComment( CommentPlacement placement ) const +{ + if ( hasComment(placement) ) + return comments_[placement].comment_; + return ""; +} + + +std::string +Value::toStyledString() const +{ + StyledWriter writer; + return writer.write( *this ); +} + + +Value::const_iterator +Value::begin() const +{ + switch ( type_ ) + { +#ifdef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + if ( value_.array_ ) + { + ValueInternalArray::IteratorState it; + value_.array_->makeBeginIterator( it ); + return const_iterator( it ); + } + break; + case objectValue: + if ( value_.map_ ) + { + ValueInternalMap::IteratorState it; + value_.map_->makeBeginIterator( it ); + return const_iterator( it ); + } + break; +#else + case arrayValue: + case objectValue: + if ( value_.map_ ) + return const_iterator( value_.map_->begin() ); + break; +#endif + default: + break; + } + return const_iterator(); +} + +Value::const_iterator +Value::end() const +{ + switch ( type_ ) + { +#ifdef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + if ( value_.array_ ) + { + ValueInternalArray::IteratorState it; + value_.array_->makeEndIterator( it ); + return const_iterator( it ); + } + break; + case objectValue: + if ( value_.map_ ) + { + ValueInternalMap::IteratorState it; + value_.map_->makeEndIterator( it ); + return const_iterator( it ); + } + break; +#else + case arrayValue: + case objectValue: + if ( value_.map_ ) + return const_iterator( value_.map_->end() ); + break; +#endif + default: + break; + } + return const_iterator(); +} + + +Value::iterator +Value::begin() +{ + switch ( type_ ) + { +#ifdef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + if ( value_.array_ ) + { + ValueInternalArray::IteratorState it; + value_.array_->makeBeginIterator( it ); + return iterator( it ); + } + break; + case objectValue: + if ( value_.map_ ) + { + ValueInternalMap::IteratorState it; + value_.map_->makeBeginIterator( it ); + return iterator( it ); + } + break; +#else + case arrayValue: + case objectValue: + if ( value_.map_ ) + return iterator( value_.map_->begin() ); + break; +#endif + default: + break; + } + return iterator(); +} + +Value::iterator +Value::end() +{ + switch ( type_ ) + { +#ifdef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + if ( value_.array_ ) + { + ValueInternalArray::IteratorState it; + value_.array_->makeEndIterator( it ); + return iterator( it ); + } + break; + case objectValue: + if ( value_.map_ ) + { + ValueInternalMap::IteratorState it; + value_.map_->makeEndIterator( it ); + return iterator( it ); + } + break; +#else + case arrayValue: + case objectValue: + if ( value_.map_ ) + return iterator( value_.map_->end() ); + break; +#endif + default: + break; + } + return iterator(); +} + + +// class PathArgument +// ////////////////////////////////////////////////////////////////// + +PathArgument::PathArgument() + : kind_( kindNone ) +{ +} + + +PathArgument::PathArgument( Value::UInt index ) + : index_( index ) + , kind_( kindIndex ) +{ +} + + +PathArgument::PathArgument( const char *key ) + : key_( key ) + , kind_( kindKey ) +{ +} + + +PathArgument::PathArgument( const std::string &key ) + : key_( key.c_str() ) + , kind_( kindKey ) +{ +} + +// class Path +// ////////////////////////////////////////////////////////////////// + +Path::Path( const std::string &path, + const PathArgument &a1, + const PathArgument &a2, + const PathArgument &a3, + const PathArgument &a4, + const PathArgument &a5 ) +{ + InArgs in; + in.push_back( &a1 ); + in.push_back( &a2 ); + in.push_back( &a3 ); + in.push_back( &a4 ); + in.push_back( &a5 ); + makePath( path, in ); +} + + +void +Path::makePath( const std::string &path, + const InArgs &in ) +{ + const char *current = path.c_str(); + const char *end = current + path.length(); + InArgs::const_iterator itInArg = in.begin(); + while ( current != end ) + { + if ( *current == '[' ) + { + ++current; + if ( *current == '%' ) + addPathInArg( path, in, itInArg, PathArgument::kindIndex ); + else + { + Value::UInt index = 0; + for ( ; current != end && *current >= '0' && *current <= '9'; ++current ) + index = index * 10 + Value::UInt(*current - '0'); + args_.push_back( index ); + } + if ( current == end || *current++ != ']' ) + invalidPath( path, int(current - path.c_str()) ); + } + else if ( *current == '%' ) + { + addPathInArg( path, in, itInArg, PathArgument::kindKey ); + ++current; + } + else if ( *current == '.' ) + { + ++current; + } + else + { + const char *beginName = current; + while ( current != end && !strchr( "[.", *current ) ) + ++current; + args_.push_back( std::string( beginName, current ) ); + } + } +} + + +void +Path::addPathInArg( const std::string &path, + const InArgs &in, + InArgs::const_iterator &itInArg, + PathArgument::Kind kind ) +{ + if ( itInArg == in.end() ) + { + // Error: missing argument %d + } + else if ( (*itInArg)->kind_ != kind ) + { + // Error: bad argument type + } + else + { + args_.push_back( **itInArg ); + } +} + + +void +Path::invalidPath( const std::string &path, + int location ) +{ + // Error: invalid path. +} + + +const Value & +Path::resolve( const Value &root ) const +{ + const Value *node = &root; + for ( Args::const_iterator it = args_.begin(); it != args_.end(); ++it ) + { + const PathArgument &arg = *it; + if ( arg.kind_ == PathArgument::kindIndex ) + { + if ( !node->isArray() || node->isValidIndex( arg.index_ ) ) + { + // Error: unable to resolve path (array value expected at position... + } + node = &((*node)[arg.index_]); + } + else if ( arg.kind_ == PathArgument::kindKey ) + { + if ( !node->isObject() ) + { + // Error: unable to resolve path (object value expected at position...) + } + node = &((*node)[arg.key_]); + if ( node == &Value::null ) + { + // Error: unable to resolve path (object has no member named '' at position...) + } + } + } + return *node; +} + + +Value +Path::resolve( const Value &root, + const Value &defaultValue ) const +{ + const Value *node = &root; + for ( Args::const_iterator it = args_.begin(); it != args_.end(); ++it ) + { + const PathArgument &arg = *it; + if ( arg.kind_ == PathArgument::kindIndex ) + { + if ( !node->isArray() || node->isValidIndex( arg.index_ ) ) + return defaultValue; + node = &((*node)[arg.index_]); + } + else if ( arg.kind_ == PathArgument::kindKey ) + { + if ( !node->isObject() ) + return defaultValue; + node = &((*node)[arg.key_]); + if ( node == &Value::null ) + return defaultValue; + } + } + return *node; +} + + +Value & +Path::make( Value &root ) const +{ + Value *node = &root; + for ( Args::const_iterator it = args_.begin(); it != args_.end(); ++it ) + { + const PathArgument &arg = *it; + if ( arg.kind_ == PathArgument::kindIndex ) + { + if ( !node->isArray() ) + { + // Error: node is not an array at position ... + } + node = &((*node)[arg.index_]); + } + else if ( arg.kind_ == PathArgument::kindKey ) + { + if ( !node->isObject() ) + { + // Error: node is not an object at position... + } + node = &((*node)[arg.key_]); + } + } + return *node; +} + + +} // namespace Json diff --git a/util/json/json_valueiterator.inl b/util/json/json_valueiterator.inl new file mode 100644 index 00000000..736e260e --- /dev/null +++ b/util/json/json_valueiterator.inl @@ -0,0 +1,292 @@ +// included by json_value.cpp +// everything is within Json namespace + + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueIteratorBase +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueIteratorBase::ValueIteratorBase() +#ifndef JSON_VALUE_USE_INTERNAL_MAP + : current_() + , isNull_( true ) +{ +} +#else + : isArray_( true ) + , isNull_( true ) +{ + iterator_.array_ = ValueInternalArray::IteratorState(); +} +#endif + + +#ifndef JSON_VALUE_USE_INTERNAL_MAP +ValueIteratorBase::ValueIteratorBase( const Value::ObjectValues::iterator ¤t ) + : current_( current ) + , isNull_( false ) +{ +} +#else +ValueIteratorBase::ValueIteratorBase( const ValueInternalArray::IteratorState &state ) + : isArray_( true ) +{ + iterator_.array_ = state; +} + + +ValueIteratorBase::ValueIteratorBase( const ValueInternalMap::IteratorState &state ) + : isArray_( false ) +{ + iterator_.map_ = state; +} +#endif + +Value & +ValueIteratorBase::deref() const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + return current_->second; +#else + if ( isArray_ ) + return ValueInternalArray::dereference( iterator_.array_ ); + return ValueInternalMap::value( iterator_.map_ ); +#endif +} + + +void +ValueIteratorBase::increment() +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + ++current_; +#else + if ( isArray_ ) + ValueInternalArray::increment( iterator_.array_ ); + ValueInternalMap::increment( iterator_.map_ ); +#endif +} + + +void +ValueIteratorBase::decrement() +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + --current_; +#else + if ( isArray_ ) + ValueInternalArray::decrement( iterator_.array_ ); + ValueInternalMap::decrement( iterator_.map_ ); +#endif +} + + +ValueIteratorBase::difference_type +ValueIteratorBase::computeDistance( const SelfType &other ) const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP +# ifdef JSON_USE_CPPTL_SMALLMAP + return current_ - other.current_; +# else + // Iterator for null value are initialized using the default + // constructor, which initialize current_ to the default + // std::map::iterator. As begin() and end() are two instance + // of the default std::map::iterator, they can not be compared. + // To allow this, we handle this comparison specifically. + if ( isNull_ && other.isNull_ ) + { + return 0; + } + + + // Usage of std::distance is not portable (does not compile with Sun Studio 12 RogueWave STL, + // which is the one used by default). + // Using a portable hand-made version for non random iterator instead: + // return difference_type( std::distance( current_, other.current_ ) ); + difference_type myDistance = 0; + for ( Value::ObjectValues::iterator it = current_; it != other.current_; ++it ) + { + ++myDistance; + } + return myDistance; +# endif +#else + if ( isArray_ ) + return ValueInternalArray::distance( iterator_.array_, other.iterator_.array_ ); + return ValueInternalMap::distance( iterator_.map_, other.iterator_.map_ ); +#endif +} + + +bool +ValueIteratorBase::isEqual( const SelfType &other ) const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + if ( isNull_ ) + { + return other.isNull_; + } + return current_ == other.current_; +#else + if ( isArray_ ) + return ValueInternalArray::equals( iterator_.array_, other.iterator_.array_ ); + return ValueInternalMap::equals( iterator_.map_, other.iterator_.map_ ); +#endif +} + + +void +ValueIteratorBase::copy( const SelfType &other ) +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + current_ = other.current_; +#else + if ( isArray_ ) + iterator_.array_ = other.iterator_.array_; + iterator_.map_ = other.iterator_.map_; +#endif +} + + +Value +ValueIteratorBase::key() const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + const Value::CZString czstring = (*current_).first; + if ( czstring.c_str() ) + { + if ( czstring.isStaticString() ) + return Value( StaticString( czstring.c_str() ) ); + return Value( czstring.c_str() ); + } + return Value( czstring.index() ); +#else + if ( isArray_ ) + return Value( ValueInternalArray::indexOf( iterator_.array_ ) ); + bool isStatic; + const char *memberName = ValueInternalMap::key( iterator_.map_, isStatic ); + if ( isStatic ) + return Value( StaticString( memberName ) ); + return Value( memberName ); +#endif +} + + +UInt +ValueIteratorBase::index() const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + const Value::CZString czstring = (*current_).first; + if ( !czstring.c_str() ) + return czstring.index(); + return Value::UInt( -1 ); +#else + if ( isArray_ ) + return Value::UInt( ValueInternalArray::indexOf( iterator_.array_ ) ); + return Value::UInt( -1 ); +#endif +} + + +const char * +ValueIteratorBase::memberName() const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + const char *name = (*current_).first.c_str(); + return name ? name : ""; +#else + if ( !isArray_ ) + return ValueInternalMap::key( iterator_.map_ ); + return ""; +#endif +} + + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueConstIterator +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueConstIterator::ValueConstIterator() +{ +} + + +#ifndef JSON_VALUE_USE_INTERNAL_MAP +ValueConstIterator::ValueConstIterator( const Value::ObjectValues::iterator ¤t ) + : ValueIteratorBase( current ) +{ +} +#else +ValueConstIterator::ValueConstIterator( const ValueInternalArray::IteratorState &state ) + : ValueIteratorBase( state ) +{ +} + +ValueConstIterator::ValueConstIterator( const ValueInternalMap::IteratorState &state ) + : ValueIteratorBase( state ) +{ +} +#endif + +ValueConstIterator & +ValueConstIterator::operator =( const ValueIteratorBase &other ) +{ + copy( other ); + return *this; +} + + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueIterator +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueIterator::ValueIterator() +{ +} + + +#ifndef JSON_VALUE_USE_INTERNAL_MAP +ValueIterator::ValueIterator( const Value::ObjectValues::iterator ¤t ) + : ValueIteratorBase( current ) +{ +} +#else +ValueIterator::ValueIterator( const ValueInternalArray::IteratorState &state ) + : ValueIteratorBase( state ) +{ +} + +ValueIterator::ValueIterator( const ValueInternalMap::IteratorState &state ) + : ValueIteratorBase( state ) +{ +} +#endif + +ValueIterator::ValueIterator( const ValueConstIterator &other ) + : ValueIteratorBase( other ) +{ +} + +ValueIterator::ValueIterator( const ValueIterator &other ) + : ValueIteratorBase( other ) +{ +} + +ValueIterator & +ValueIterator::operator =( const SelfType &other ) +{ + copy( other ); + return *this; +} diff --git a/util/json/json_writer.cpp b/util/json/json_writer.cpp new file mode 100644 index 00000000..2a9f0dba --- /dev/null +++ b/util/json/json_writer.cpp @@ -0,0 +1,829 @@ +#include "writer.h" +#include +#include +#include +#include +#include +#include +#include + +#if _MSC_VER >= 1400 // VC++ 8.0 +#pragma warning( disable : 4996 ) // disable warning about strdup being deprecated. +#endif + +namespace Json { + +static bool isControlCharacter(char ch) +{ + return ch > 0 && ch <= 0x1F; +} + +static bool containsControlCharacter( const char* str ) +{ + while ( *str ) + { + if ( isControlCharacter( *(str++) ) ) + return true; + } + return false; +} +static void uintToString( unsigned int value, + char *¤t ) +{ + *--current = 0; + do + { + *--current = (value % 10) + '0'; + value /= 10; + } + while ( value != 0 ); +} + +std::string valueToString( Int value ) +{ + char buffer[32]; + char *current = buffer + sizeof(buffer); + bool isNegative = value < 0; + if ( isNegative ) + value = -value; + uintToString( UInt(value), current ); + if ( isNegative ) + *--current = '-'; + assert( current >= buffer ); + return current; +} + + +std::string valueToString( UInt value ) +{ + char buffer[32]; + char *current = buffer + sizeof(buffer); + uintToString( value, current ); + assert( current >= buffer ); + return current; +} + +std::string valueToString( double value ) +{ + char buffer[32]; +#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005 to avoid warning. + sprintf_s(buffer, sizeof(buffer), "%#.16g", value); +#else + sprintf(buffer, "%#.16g", value); +#endif + char* ch = buffer + strlen(buffer) - 1; + if (*ch != '0') return buffer; // nothing to truncate, so save time + while(ch > buffer && *ch == '0'){ + --ch; + } + char* last_nonzero = ch; + while(ch >= buffer){ + switch(*ch){ + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + --ch; + continue; + case '.': + // Truncate zeroes to save bytes in output, but keep one. + *(last_nonzero+2) = '\0'; + return buffer; + default: + return buffer; + } + } + return buffer; +} + + +std::string valueToString( bool value ) +{ + return value ? "true" : "false"; +} + +std::string valueToQuotedString( const char *value ) +{ + // Not sure how to handle unicode... + if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && !containsControlCharacter( value )) + return std::string("\"") + value + "\""; + // We have to walk value and escape any special characters. + // Appending to std::string is not efficient, but this should be rare. + // (Note: forward slashes are *not* rare, but I am not escaping them.) + unsigned maxsize = strlen(value)*2 + 3; // allescaped+quotes+NULL + std::string result; + result.reserve(maxsize); // to avoid lots of mallocs + result += "\""; + for (const char* c=value; *c != 0; ++c) + { + switch(*c) + { + case '\"': + result += "\\\""; + break; + case '\\': + result += "\\\\"; + break; + case '\b': + result += "\\b"; + break; + case '\f': + result += "\\f"; + break; + case '\n': + result += "\\n"; + break; + case '\r': + result += "\\r"; + break; + case '\t': + result += "\\t"; + break; + //case '/': + // Even though \/ is considered a legal escape in JSON, a bare + // slash is also legal, so I see no reason to escape it. + // (I hope I am not misunderstanding something. + // blep notes: actually escaping \/ may be useful in javascript to avoid (*c); + result += oss.str(); + } + else + { + result += *c; + } + break; + } + } + result += "\""; + return result; +} + +// Class Writer +// ////////////////////////////////////////////////////////////////// +Writer::~Writer() +{ +} + + +// Class FastWriter +// ////////////////////////////////////////////////////////////////// + +FastWriter::FastWriter() + : yamlCompatiblityEnabled_( false ) +{ +} + + +void +FastWriter::enableYAMLCompatibility() +{ + yamlCompatiblityEnabled_ = true; +} + + +std::string +FastWriter::write( const Value &root ) +{ + document_ = ""; + writeValue( root ); + document_ += "\n"; + return document_; +} + + +void +FastWriter::writeValue( const Value &value ) +{ + switch ( value.type() ) + { + case nullValue: + document_ += "null"; + break; + case intValue: + document_ += valueToString( value.asInt() ); + break; + case uintValue: + document_ += valueToString( value.asUInt() ); + break; + case realValue: + document_ += valueToString( value.asDouble() ); + break; + case stringValue: + document_ += valueToQuotedString( value.asCString() ); + break; + case booleanValue: + document_ += valueToString( value.asBool() ); + break; + case arrayValue: + { + document_ += "["; + int size = value.size(); + for ( int index =0; index < size; ++index ) + { + if ( index > 0 ) + document_ += ","; + writeValue( value[index] ); + } + document_ += "]"; + } + break; + case objectValue: + { + Value::Members members( value.getMemberNames() ); + document_ += "{"; + for ( Value::Members::iterator it = members.begin(); + it != members.end(); + ++it ) + { + const std::string &name = *it; + if ( it != members.begin() ) + document_ += ","; + document_ += valueToQuotedString( name.c_str() ); + document_ += yamlCompatiblityEnabled_ ? ": " + : ":"; + writeValue( value[name] ); + } + document_ += "}"; + } + break; + } +} + + +// Class StyledWriter +// ////////////////////////////////////////////////////////////////// + +StyledWriter::StyledWriter() + : rightMargin_( 74 ) + , indentSize_( 3 ) +{ +} + + +std::string +StyledWriter::write( const Value &root ) +{ + document_ = ""; + addChildValues_ = false; + indentString_ = ""; + writeCommentBeforeValue( root ); + writeValue( root ); + writeCommentAfterValueOnSameLine( root ); + document_ += "\n"; + return document_; +} + + +void +StyledWriter::writeValue( const Value &value ) +{ + switch ( value.type() ) + { + case nullValue: + pushValue( "null" ); + break; + case intValue: + pushValue( valueToString( value.asInt() ) ); + break; + case uintValue: + pushValue( valueToString( value.asUInt() ) ); + break; + case realValue: + pushValue( valueToString( value.asDouble() ) ); + break; + case stringValue: + pushValue( valueToQuotedString( value.asCString() ) ); + break; + case booleanValue: + pushValue( valueToString( value.asBool() ) ); + break; + case arrayValue: + writeArrayValue( value); + break; + case objectValue: + { + Value::Members members( value.getMemberNames() ); + if ( members.empty() ) + pushValue( "{}" ); + else + { + writeWithIndent( "{" ); + indent(); + Value::Members::iterator it = members.begin(); + while ( true ) + { + const std::string &name = *it; + const Value &childValue = value[name]; + writeCommentBeforeValue( childValue ); + writeWithIndent( valueToQuotedString( name.c_str() ) ); + document_ += " : "; + writeValue( childValue ); + if ( ++it == members.end() ) + { + writeCommentAfterValueOnSameLine( childValue ); + break; + } + document_ += ","; + writeCommentAfterValueOnSameLine( childValue ); + } + unindent(); + writeWithIndent( "}" ); + } + } + break; + } +} + + +void +StyledWriter::writeArrayValue( const Value &value ) +{ + unsigned size = value.size(); + if ( size == 0 ) + pushValue( "[]" ); + else + { + bool isArrayMultiLine = isMultineArray( value ); + if ( isArrayMultiLine ) + { + writeWithIndent( "[" ); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index =0; + while ( true ) + { + const Value &childValue = value[index]; + writeCommentBeforeValue( childValue ); + if ( hasChildValue ) + writeWithIndent( childValues_[index] ); + else + { + writeIndent(); + writeValue( childValue ); + } + if ( ++index == size ) + { + writeCommentAfterValueOnSameLine( childValue ); + break; + } + document_ += ","; + writeCommentAfterValueOnSameLine( childValue ); + } + unindent(); + writeWithIndent( "]" ); + } + else // output on a single line + { + assert( childValues_.size() == size ); + document_ += "[ "; + for ( unsigned index =0; index < size; ++index ) + { + if ( index > 0 ) + document_ += ", "; + document_ += childValues_[index]; + } + document_ += " ]"; + } + } +} + + +bool +StyledWriter::isMultineArray( const Value &value ) +{ + int size = value.size(); + bool isMultiLine = size*3 >= rightMargin_ ; + childValues_.clear(); + for ( int index =0; index < size && !isMultiLine; ++index ) + { + const Value &childValue = value[index]; + isMultiLine = isMultiLine || + ( (childValue.isArray() || childValue.isObject()) && + childValue.size() > 0 ); + } + if ( !isMultiLine ) // check if line length > max line length + { + childValues_.reserve( size ); + addChildValues_ = true; + int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]' + for ( int index =0; index < size && !isMultiLine; ++index ) + { + writeValue( value[index] ); + lineLength += int( childValues_[index].length() ); + isMultiLine = isMultiLine && hasCommentForValue( value[index] ); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + + +void +StyledWriter::pushValue( const std::string &value ) +{ + if ( addChildValues_ ) + childValues_.push_back( value ); + else + document_ += value; +} + + +void +StyledWriter::writeIndent() +{ + if ( !document_.empty() ) + { + char last = document_[document_.length()-1]; + if ( last == ' ' ) // already indented + return; + if ( last != '\n' ) // Comments may add new-line + document_ += '\n'; + } + document_ += indentString_; +} + + +void +StyledWriter::writeWithIndent( const std::string &value ) +{ + writeIndent(); + document_ += value; +} + + +void +StyledWriter::indent() +{ + indentString_ += std::string( indentSize_, ' ' ); +} + + +void +StyledWriter::unindent() +{ + assert( int(indentString_.size()) >= indentSize_ ); + indentString_.resize( indentString_.size() - indentSize_ ); +} + + +void +StyledWriter::writeCommentBeforeValue( const Value &root ) +{ + if ( !root.hasComment( commentBefore ) ) + return; + document_ += normalizeEOL( root.getComment( commentBefore ) ); + document_ += "\n"; +} + + +void +StyledWriter::writeCommentAfterValueOnSameLine( const Value &root ) +{ + if ( root.hasComment( commentAfterOnSameLine ) ) + document_ += " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) ); + + if ( root.hasComment( commentAfter ) ) + { + document_ += "\n"; + document_ += normalizeEOL( root.getComment( commentAfter ) ); + document_ += "\n"; + } +} + + +bool +StyledWriter::hasCommentForValue( const Value &value ) +{ + return value.hasComment( commentBefore ) + || value.hasComment( commentAfterOnSameLine ) + || value.hasComment( commentAfter ); +} + + +std::string +StyledWriter::normalizeEOL( const std::string &text ) +{ + std::string normalized; + normalized.reserve( text.length() ); + const char *begin = text.c_str(); + const char *end = begin + text.length(); + const char *current = begin; + while ( current != end ) + { + char c = *current++; + if ( c == '\r' ) // mac or dos EOL + { + if ( *current == '\n' ) // convert dos EOL + ++current; + normalized += '\n'; + } + else // handle unix EOL & other char + normalized += c; + } + return normalized; +} + + +// Class StyledStreamWriter +// ////////////////////////////////////////////////////////////////// + +StyledStreamWriter::StyledStreamWriter( std::string indentation ) + : document_(NULL) + , rightMargin_( 74 ) + , indentation_( indentation ) +{ +} + + +void +StyledStreamWriter::write( std::ostream &out, const Value &root ) +{ + document_ = &out; + addChildValues_ = false; + indentString_ = ""; + writeCommentBeforeValue( root ); + writeValue( root ); + writeCommentAfterValueOnSameLine( root ); + *document_ << "\n"; + document_ = NULL; // Forget the stream, for safety. +} + + +void +StyledStreamWriter::writeValue( const Value &value ) +{ + switch ( value.type() ) + { + case nullValue: + pushValue( "null" ); + break; + case intValue: + pushValue( valueToString( value.asInt() ) ); + break; + case uintValue: + pushValue( valueToString( value.asUInt() ) ); + break; + case realValue: + pushValue( valueToString( value.asDouble() ) ); + break; + case stringValue: + pushValue( valueToQuotedString( value.asCString() ) ); + break; + case booleanValue: + pushValue( valueToString( value.asBool() ) ); + break; + case arrayValue: + writeArrayValue( value); + break; + case objectValue: + { + Value::Members members( value.getMemberNames() ); + if ( members.empty() ) + pushValue( "{}" ); + else + { + writeWithIndent( "{" ); + indent(); + Value::Members::iterator it = members.begin(); + while ( true ) + { + const std::string &name = *it; + const Value &childValue = value[name]; + writeCommentBeforeValue( childValue ); + writeWithIndent( valueToQuotedString( name.c_str() ) ); + *document_ << " : "; + writeValue( childValue ); + if ( ++it == members.end() ) + { + writeCommentAfterValueOnSameLine( childValue ); + break; + } + *document_ << ","; + writeCommentAfterValueOnSameLine( childValue ); + } + unindent(); + writeWithIndent( "}" ); + } + } + break; + } +} + + +void +StyledStreamWriter::writeArrayValue( const Value &value ) +{ + unsigned size = value.size(); + if ( size == 0 ) + pushValue( "[]" ); + else + { + bool isArrayMultiLine = isMultineArray( value ); + if ( isArrayMultiLine ) + { + writeWithIndent( "[" ); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index =0; + while ( true ) + { + const Value &childValue = value[index]; + writeCommentBeforeValue( childValue ); + if ( hasChildValue ) + writeWithIndent( childValues_[index] ); + else + { + writeIndent(); + writeValue( childValue ); + } + if ( ++index == size ) + { + writeCommentAfterValueOnSameLine( childValue ); + break; + } + *document_ << ","; + writeCommentAfterValueOnSameLine( childValue ); + } + unindent(); + writeWithIndent( "]" ); + } + else // output on a single line + { + assert( childValues_.size() == size ); + *document_ << "[ "; + for ( unsigned index =0; index < size; ++index ) + { + if ( index > 0 ) + *document_ << ", "; + *document_ << childValues_[index]; + } + *document_ << " ]"; + } + } +} + + +bool +StyledStreamWriter::isMultineArray( const Value &value ) +{ + int size = value.size(); + bool isMultiLine = size*3 >= rightMargin_ ; + childValues_.clear(); + for ( int index =0; index < size && !isMultiLine; ++index ) + { + const Value &childValue = value[index]; + isMultiLine = isMultiLine || + ( (childValue.isArray() || childValue.isObject()) && + childValue.size() > 0 ); + } + if ( !isMultiLine ) // check if line length > max line length + { + childValues_.reserve( size ); + addChildValues_ = true; + int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]' + for ( int index =0; index < size && !isMultiLine; ++index ) + { + writeValue( value[index] ); + lineLength += int( childValues_[index].length() ); + isMultiLine = isMultiLine && hasCommentForValue( value[index] ); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + + +void +StyledStreamWriter::pushValue( const std::string &value ) +{ + if ( addChildValues_ ) + childValues_.push_back( value ); + else + *document_ << value; +} + + +void +StyledStreamWriter::writeIndent() +{ + /* + Some comments in this method would have been nice. ;-) + + if ( !document_.empty() ) + { + char last = document_[document_.length()-1]; + if ( last == ' ' ) // already indented + return; + if ( last != '\n' ) // Comments may add new-line + *document_ << '\n'; + } + */ + *document_ << '\n' << indentString_; +} + + +void +StyledStreamWriter::writeWithIndent( const std::string &value ) +{ + writeIndent(); + *document_ << value; +} + + +void +StyledStreamWriter::indent() +{ + indentString_ += indentation_; +} + + +void +StyledStreamWriter::unindent() +{ + assert( indentString_.size() >= indentation_.size() ); + indentString_.resize( indentString_.size() - indentation_.size() ); +} + + +void +StyledStreamWriter::writeCommentBeforeValue( const Value &root ) +{ + if ( !root.hasComment( commentBefore ) ) + return; + *document_ << normalizeEOL( root.getComment( commentBefore ) ); + *document_ << "\n"; +} + + +void +StyledStreamWriter::writeCommentAfterValueOnSameLine( const Value &root ) +{ + if ( root.hasComment( commentAfterOnSameLine ) ) + *document_ << " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) ); + + if ( root.hasComment( commentAfter ) ) + { + *document_ << "\n"; + *document_ << normalizeEOL( root.getComment( commentAfter ) ); + *document_ << "\n"; + } +} + + +bool +StyledStreamWriter::hasCommentForValue( const Value &value ) +{ + return value.hasComment( commentBefore ) + || value.hasComment( commentAfterOnSameLine ) + || value.hasComment( commentAfter ); +} + + +std::string +StyledStreamWriter::normalizeEOL( const std::string &text ) +{ + std::string normalized; + normalized.reserve( text.length() ); + const char *begin = text.c_str(); + const char *end = begin + text.length(); + const char *current = begin; + while ( current != end ) + { + char c = *current++; + if ( c == '\r' ) // mac or dos EOL + { + if ( *current == '\n' ) // convert dos EOL + ++current; + normalized += '\n'; + } + else // handle unix EOL & other char + normalized += c; + } + return normalized; +} + + +std::ostream& operator<<( std::ostream &sout, const Value &root ) +{ + Json::StyledStreamWriter writer; + writer.write(sout, root); + return sout; +} + + +} // namespace Json diff --git a/util/json/reader.h b/util/json/reader.h new file mode 100644 index 00000000..ee1d6a24 --- /dev/null +++ b/util/json/reader.h @@ -0,0 +1,196 @@ +#ifndef CPPTL_JSON_READER_H_INCLUDED +# define CPPTL_JSON_READER_H_INCLUDED + +# include "features.h" +# include "value.h" +# include +# include +# include +# include + +namespace Json { + + /** \brief Unserialize a JSON document into a Value. + * + */ + class JSON_API Reader + { + public: + typedef char Char; + typedef const Char *Location; + + /** \brief Constructs a Reader allowing all features + * for parsing. + */ + Reader(); + + /** \brief Constructs a Reader allowing the specified feature set + * for parsing. + */ + Reader( const Features &features ); + + /** \brief Read a Value from a JSON document. + * \param document UTF-8 encoded string containing the document to read. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param collectComments \c true to collect comment and allow writing them back during + * serialization, \c false to discard comments. + * This parameter is ignored if Features::allowComments_ + * is \c false. + * \return \c true if the document was successfully parsed, \c false if an error occurred. + */ + bool parse( const std::string &document, + Value &root, + bool collectComments = true ); + + /** \brief Read a Value from a JSON document. + * \param document UTF-8 encoded string containing the document to read. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param collectComments \c true to collect comment and allow writing them back during + * serialization, \c false to discard comments. + * This parameter is ignored if Features::allowComments_ + * is \c false. + * \return \c true if the document was successfully parsed, \c false if an error occurred. + */ + bool parse( const char *beginDoc, const char *endDoc, + Value &root, + bool collectComments = true ); + + /// \brief Parse from input stream. + /// \see Json::operator>>(std::istream&, Json::Value&). + bool parse( std::istream &is, + Value &root, + bool collectComments = true ); + + /** \brief Returns a user friendly string that list errors in the parsed document. + * \return Formatted error message with the list of errors with their location in + * the parsed document. An empty string is returned if no error occurred + * during parsing. + */ + std::string getFormatedErrorMessages() const; + + private: + enum TokenType + { + tokenEndOfStream = 0, + tokenObjectBegin, + tokenObjectEnd, + tokenArrayBegin, + tokenArrayEnd, + tokenString, + tokenNumber, + tokenTrue, + tokenFalse, + tokenNull, + tokenArraySeparator, + tokenMemberSeparator, + tokenComment, + tokenError + }; + + class Token + { + public: + TokenType type_; + Location start_; + Location end_; + }; + + class ErrorInfo + { + public: + Token token_; + std::string message_; + Location extra_; + }; + + typedef std::deque Errors; + + bool expectToken( TokenType type, Token &token, const char *message ); + bool readToken( Token &token ); + void skipSpaces(); + bool match( Location pattern, + int patternLength ); + bool readComment(); + bool readCStyleComment(); + bool readCppStyleComment(); + bool readString(); + void readNumber(); + bool readValue(); + bool readObject( Token &token ); + bool readArray( Token &token ); + bool decodeNumber( Token &token ); + bool decodeString( Token &token ); + bool decodeString( Token &token, std::string &decoded ); + bool decodeDouble( Token &token ); + bool decodeUnicodeCodePoint( Token &token, + Location ¤t, + Location end, + unsigned int &unicode ); + bool decodeUnicodeEscapeSequence( Token &token, + Location ¤t, + Location end, + unsigned int &unicode ); + bool addError( const std::string &message, + Token &token, + Location extra = 0 ); + bool recoverFromError( TokenType skipUntilToken ); + bool addErrorAndRecover( const std::string &message, + Token &token, + TokenType skipUntilToken ); + void skipUntilSpace(); + Value ¤tValue(); + Char getNextChar(); + void getLocationLineAndColumn( Location location, + int &line, + int &column ) const; + std::string getLocationLineAndColumn( Location location ) const; + void addComment( Location begin, + Location end, + CommentPlacement placement ); + void skipCommentTokens( Token &token ); + + typedef std::stack Nodes; + Nodes nodes_; + Errors errors_; + std::string document_; + Location begin_; + Location end_; + Location current_; + Location lastValueEnd_; + Value *lastValue_; + std::string commentsBefore_; + Features features_; + bool collectComments_; + }; + + /** \brief Read from 'sin' into 'root'. + + Always keep comments from the input JSON. + + This can be used to read a file into a particular sub-object. + For example: + \code + Json::Value root; + cin >> root["dir"]["file"]; + cout << root; + \endcode + Result: + \verbatim + { + "dir": { + "file": { + // The input stream JSON would be nested here. + } + } + } + \endverbatim + \throw std::exception on parse error. + \see Json::operator<<() + */ + std::istream& operator>>( std::istream&, Value& ); + +} // namespace Json + +#endif // CPPTL_JSON_READER_H_INCLUDED diff --git a/util/json/value.h b/util/json/value.h new file mode 100644 index 00000000..58bfd88e --- /dev/null +++ b/util/json/value.h @@ -0,0 +1,1069 @@ +#ifndef CPPTL_JSON_H_INCLUDED +# define CPPTL_JSON_H_INCLUDED + +# include "forwards.h" +# include +# include + +# ifndef JSON_USE_CPPTL_SMALLMAP +# include +# else +# include +# endif +# ifdef JSON_USE_CPPTL +# include +# endif + +/** \brief JSON (JavaScript Object Notation). + */ +namespace Json { + + /** \brief Type of the value held by a Value object. + */ + enum ValueType + { + nullValue = 0, ///< 'null' value + intValue, ///< signed integer value + uintValue, ///< unsigned integer value + realValue, ///< double value + stringValue, ///< UTF-8 string value + booleanValue, ///< bool value + arrayValue, ///< array value (ordered list) + objectValue ///< object value (collection of name/value pairs). + }; + + enum CommentPlacement + { + commentBefore = 0, ///< a comment placed on the line before a value + commentAfterOnSameLine, ///< a comment just after a value on the same line + commentAfter, ///< a comment on the line after a value (only make sense for root value) + numberOfCommentPlacement + }; + +//# ifdef JSON_USE_CPPTL +// typedef CppTL::AnyEnumerator EnumMemberNames; +// typedef CppTL::AnyEnumerator EnumValues; +//# endif + + /** \brief Lightweight wrapper to tag static string. + * + * Value constructor and objectValue member assignement takes advantage of the + * StaticString and avoid the cost of string duplication when storing the + * string or the member name. + * + * Example of usage: + * \code + * Json::Value aValue( StaticString("some text") ); + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode + */ + class JSON_API StaticString + { + public: + explicit StaticString( const char *czstring ) + : str_( czstring ) + { + } + + operator const char *() const + { + return str_; + } + + const char *c_str() const + { + return str_; + } + + private: + const char *str_; + }; + + /** \brief Represents a JSON value. + * + * This class is a discriminated union wrapper that can represents a: + * - signed integer [range: Value::minInt - Value::maxInt] + * - unsigned integer (range: 0 - Value::maxUInt) + * - double + * - UTF-8 string + * - boolean + * - 'null' + * - an ordered list of Value + * - collection of name/value pairs (javascript object) + * + * The type of the held value is represented by a #ValueType and + * can be obtained using type(). + * + * values of an #objectValue or #arrayValue can be accessed using operator[]() methods. + * Non const methods will automatically create the a #nullValue element + * if it does not exist. + * The sequence of an #arrayValue will be automatically resize and initialized + * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue. + * + * The get() methods can be used to obtanis default value in the case the required element + * does not exist. + * + * It is possible to iterate over the list of a #objectValue values using + * the getMemberNames() method. + */ + class JSON_API Value + { + friend class ValueIteratorBase; +# ifdef JSON_VALUE_USE_INTERNAL_MAP + friend class ValueInternalLink; + friend class ValueInternalMap; +# endif + public: + typedef std::vector Members; + typedef ValueIterator iterator; + typedef ValueConstIterator const_iterator; + typedef Json::UInt UInt; + typedef Json::Int Int; + typedef UInt ArrayIndex; + + static const Value null; + static const Int minInt; + static const Int maxInt; + static const UInt maxUInt; + + private: +#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION +# ifndef JSON_VALUE_USE_INTERNAL_MAP + class CZString + { + public: + enum DuplicationPolicy + { + noDuplication = 0, + duplicate, + duplicateOnCopy + }; + CZString( int index ); + CZString( const char *cstr, DuplicationPolicy allocate ); + CZString( const CZString &other ); + ~CZString(); + CZString &operator =( const CZString &other ); + bool operator<( const CZString &other ) const; + bool operator==( const CZString &other ) const; + int index() const; + const char *c_str() const; + bool isStaticString() const; + private: + void swap( CZString &other ); + const char *cstr_; + int index_; + }; + + public: +# ifndef JSON_USE_CPPTL_SMALLMAP + typedef std::map ObjectValues; +# else + typedef CppTL::SmallMap ObjectValues; +# endif // ifndef JSON_USE_CPPTL_SMALLMAP +# endif // ifndef JSON_VALUE_USE_INTERNAL_MAP +#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + + public: + /** \brief Create a default Value of the given type. + + This is a very useful constructor. + To create an empty array, pass arrayValue. + To create an empty object, pass objectValue. + Another Value can then be set to this one by assignment. + This is useful since clear() and resize() will not alter types. + + Examples: + \code + Json::Value null_value; // null + Json::Value arr_value(Json::arrayValue); // [] + Json::Value obj_value(Json::objectValue); // {} + \endcode + */ + Value( ValueType type = nullValue ); + Value( Int value ); + Value( UInt value ); + Value( double value ); + Value( const char *value ); + Value( const char *beginValue, const char *endValue ); + /** \brief Constructs a value from a static string. + + * Like other value string constructor but do not duplicate the string for + * internal storage. The given string must remain alive after the call to this + * constructor. + * Example of usage: + * \code + * Json::Value aValue( StaticString("some text") ); + * \endcode + */ + Value( const StaticString &value ); + Value( const std::string &value ); +# ifdef JSON_USE_CPPTL + Value( const CppTL::ConstString &value ); +# endif + Value( bool value ); + Value( const Value &other ); + ~Value(); + + Value &operator=( const Value &other ); + /// Swap values. + /// \note Currently, comments are intentionally not swapped, for + /// both logic and efficiency. + void swap( Value &other ); + + ValueType type() const; + + bool operator <( const Value &other ) const; + bool operator <=( const Value &other ) const; + bool operator >=( const Value &other ) const; + bool operator >( const Value &other ) const; + + bool operator ==( const Value &other ) const; + bool operator !=( const Value &other ) const; + + int compare( const Value &other ); + + const char *asCString() const; + std::string asString() const; +# ifdef JSON_USE_CPPTL + CppTL::ConstString asConstString() const; +# endif + Int asInt() const; + UInt asUInt() const; + double asDouble() const; + bool asBool() const; + + bool isNull() const; + bool isBool() const; + bool isInt() const; + bool isUInt() const; + bool isIntegral() const; + bool isDouble() const; + bool isNumeric() const; + bool isString() const; + bool isArray() const; + bool isObject() const; + + bool isConvertibleTo( ValueType other ) const; + + /// Number of values in array or object + UInt size() const; + + /// \brief Return true if empty array, empty object, or null; + /// otherwise, false. + bool empty() const; + + /// Return isNull() + bool operator!() const; + + /// Remove all object members and array elements. + /// \pre type() is arrayValue, objectValue, or nullValue + /// \post type() is unchanged + void clear(); + + /// Resize the array to size elements. + /// New elements are initialized to null. + /// May only be called on nullValue or arrayValue. + /// \pre type() is arrayValue or nullValue + /// \post type() is arrayValue + void resize( UInt size ); + + /// Access an array element (zero based index ). + /// If the array contains less than index element, then null value are inserted + /// in the array so that its size is index+1. + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + Value &operator[]( UInt index ); + /// Access an array element (zero based index ) + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + const Value &operator[]( UInt index ) const; + /// If the array contains at least index+1 elements, returns the element value, + /// otherwise returns defaultValue. + Value get( UInt index, + const Value &defaultValue ) const; + /// Return true if index < size(). + bool isValidIndex( UInt index ) const; + /// \brief Append value to array at the end. + /// + /// Equivalent to jsonvalue[jsonvalue.size()] = value; + Value &append( const Value &value ); + + /// Access an object value by name, create a null member if it does not exist. + Value &operator[]( const char *key ); + /// Access an object value by name, returns null if there is no member with that name. + const Value &operator[]( const char *key ) const; + /// Access an object value by name, create a null member if it does not exist. + Value &operator[]( const std::string &key ); + /// Access an object value by name, returns null if there is no member with that name. + const Value &operator[]( const std::string &key ) const; + /** \brief Access an object value by name, create a null member if it does not exist. + + * If the object as no entry for that name, then the member name used to store + * the new entry is not duplicated. + * Example of use: + * \code + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode + */ + Value &operator[]( const StaticString &key ); +# ifdef JSON_USE_CPPTL + /// Access an object value by name, create a null member if it does not exist. + Value &operator[]( const CppTL::ConstString &key ); + /// Access an object value by name, returns null if there is no member with that name. + const Value &operator[]( const CppTL::ConstString &key ) const; +# endif + /// Return the member named key if it exist, defaultValue otherwise. + Value get( const char *key, + const Value &defaultValue ) const; + /// Return the member named key if it exist, defaultValue otherwise. + Value get( const std::string &key, + const Value &defaultValue ) const; +# ifdef JSON_USE_CPPTL + /// Return the member named key if it exist, defaultValue otherwise. + Value get( const CppTL::ConstString &key, + const Value &defaultValue ) const; +# endif + /// \brief Remove and return the named member. + /// + /// Do nothing if it did not exist. + /// \return the removed Value, or null. + /// \pre type() is objectValue or nullValue + /// \post type() is unchanged + Value removeMember( const char* key ); + /// Same as removeMember(const char*) + Value removeMember( const std::string &key ); + + /// Return true if the object has a member named key. + bool isMember( const char *key ) const; + /// Return true if the object has a member named key. + bool isMember( const std::string &key ) const; +# ifdef JSON_USE_CPPTL + /// Return true if the object has a member named key. + bool isMember( const CppTL::ConstString &key ) const; +# endif + + /// \brief Return a list of the member names. + /// + /// If null, return an empty list. + /// \pre type() is objectValue or nullValue + /// \post if type() was nullValue, it remains nullValue + Members getMemberNames() const; + +//# ifdef JSON_USE_CPPTL +// EnumMemberNames enumMemberNames() const; +// EnumValues enumValues() const; +//# endif + + /// Comments must be //... or /* ... */ + void setComment( const char *comment, + CommentPlacement placement ); + /// Comments must be //... or /* ... */ + void setComment( const std::string &comment, + CommentPlacement placement ); + bool hasComment( CommentPlacement placement ) const; + /// Include delimiters and embedded newlines. + std::string getComment( CommentPlacement placement ) const; + + std::string toStyledString() const; + + const_iterator begin() const; + const_iterator end() const; + + iterator begin(); + iterator end(); + + private: + Value &resolveReference( const char *key, + bool isStatic ); + +# ifdef JSON_VALUE_USE_INTERNAL_MAP + inline bool isItemAvailable() const + { + return itemIsUsed_ == 0; + } + + inline void setItemUsed( bool isUsed = true ) + { + itemIsUsed_ = isUsed ? 1 : 0; + } + + inline bool isMemberNameStatic() const + { + return memberNameIsStatic_ == 0; + } + + inline void setMemberNameIsStatic( bool isStatic ) + { + memberNameIsStatic_ = isStatic ? 1 : 0; + } +# endif // # ifdef JSON_VALUE_USE_INTERNAL_MAP + + private: + struct CommentInfo + { + CommentInfo(); + ~CommentInfo(); + + void setComment( const char *text ); + + char *comment_; + }; + + //struct MemberNamesTransform + //{ + // typedef const char *result_type; + // const char *operator()( const CZString &name ) const + // { + // return name.c_str(); + // } + //}; + + union ValueHolder + { + Int int_; + UInt uint_; + double real_; + bool bool_; + char *string_; +# ifdef JSON_VALUE_USE_INTERNAL_MAP + ValueInternalArray *array_; + ValueInternalMap *map_; +#else + ObjectValues *map_; +# endif + } value_; + ValueType type_ : 8; + int allocated_ : 1; // Notes: if declared as bool, bitfield is useless. +# ifdef JSON_VALUE_USE_INTERNAL_MAP + unsigned int itemIsUsed_ : 1; // used by the ValueInternalMap container. + int memberNameIsStatic_ : 1; // used by the ValueInternalMap container. +# endif + CommentInfo *comments_; + }; + + + /** \brief Experimental and untested: represents an element of the "path" to access a node. + */ + class PathArgument + { + public: + friend class Path; + + PathArgument(); + PathArgument( UInt index ); + PathArgument( const char *key ); + PathArgument( const std::string &key ); + + private: + enum Kind + { + kindNone = 0, + kindIndex, + kindKey + }; + std::string key_; + UInt index_; + Kind kind_; + }; + + /** \brief Experimental and untested: represents a "path" to access a node. + * + * Syntax: + * - "." => root node + * - ".[n]" => elements at index 'n' of root node (an array value) + * - ".name" => member named 'name' of root node (an object value) + * - ".name1.name2.name3" + * - ".[0][1][2].name1[3]" + * - ".%" => member name is provided as parameter + * - ".[%]" => index is provied as parameter + */ + class Path + { + public: + Path( const std::string &path, + const PathArgument &a1 = PathArgument(), + const PathArgument &a2 = PathArgument(), + const PathArgument &a3 = PathArgument(), + const PathArgument &a4 = PathArgument(), + const PathArgument &a5 = PathArgument() ); + + const Value &resolve( const Value &root ) const; + Value resolve( const Value &root, + const Value &defaultValue ) const; + /// Creates the "path" to access the specified node and returns a reference on the node. + Value &make( Value &root ) const; + + private: + typedef std::vector InArgs; + typedef std::vector Args; + + void makePath( const std::string &path, + const InArgs &in ); + void addPathInArg( const std::string &path, + const InArgs &in, + InArgs::const_iterator &itInArg, + PathArgument::Kind kind ); + void invalidPath( const std::string &path, + int location ); + + Args args_; + }; + + /** \brief Experimental do not use: Allocator to customize member name and string value memory management done by Value. + * + * - makeMemberName() and releaseMemberName() are called to respectively duplicate and + * free an Json::objectValue member name. + * - duplicateStringValue() and releaseStringValue() are called similarly to + * duplicate and free a Json::stringValue value. + */ + class ValueAllocator + { + public: + enum { unknown = (unsigned)-1 }; + + virtual ~ValueAllocator(); + + virtual char *makeMemberName( const char *memberName ) = 0; + virtual void releaseMemberName( char *memberName ) = 0; + virtual char *duplicateStringValue( const char *value, + unsigned int length = unknown ) = 0; + virtual void releaseStringValue( char *value ) = 0; + }; + +#ifdef JSON_VALUE_USE_INTERNAL_MAP + /** \brief Allocator to customize Value internal map. + * Below is an example of a simple implementation (default implementation actually + * use memory pool for speed). + * \code + class DefaultValueMapAllocator : public ValueMapAllocator + { + public: // overridden from ValueMapAllocator + virtual ValueInternalMap *newMap() + { + return new ValueInternalMap(); + } + + virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) + { + return new ValueInternalMap( other ); + } + + virtual void destructMap( ValueInternalMap *map ) + { + delete map; + } + + virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) + { + return new ValueInternalLink[size]; + } + + virtual void releaseMapBuckets( ValueInternalLink *links ) + { + delete [] links; + } + + virtual ValueInternalLink *allocateMapLink() + { + return new ValueInternalLink(); + } + + virtual void releaseMapLink( ValueInternalLink *link ) + { + delete link; + } + }; + * \endcode + */ + class JSON_API ValueMapAllocator + { + public: + virtual ~ValueMapAllocator(); + virtual ValueInternalMap *newMap() = 0; + virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) = 0; + virtual void destructMap( ValueInternalMap *map ) = 0; + virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) = 0; + virtual void releaseMapBuckets( ValueInternalLink *links ) = 0; + virtual ValueInternalLink *allocateMapLink() = 0; + virtual void releaseMapLink( ValueInternalLink *link ) = 0; + }; + + /** \brief ValueInternalMap hash-map bucket chain link (for internal use only). + * \internal previous_ & next_ allows for bidirectional traversal. + */ + class JSON_API ValueInternalLink + { + public: + enum { itemPerLink = 6 }; // sizeof(ValueInternalLink) = 128 on 32 bits architecture. + enum InternalFlags { + flagAvailable = 0, + flagUsed = 1 + }; + + ValueInternalLink(); + + ~ValueInternalLink(); + + Value items_[itemPerLink]; + char *keys_[itemPerLink]; + ValueInternalLink *previous_; + ValueInternalLink *next_; + }; + + + /** \brief A linked page based hash-table implementation used internally by Value. + * \internal ValueInternalMap is a tradional bucket based hash-table, with a linked + * list in each bucket to handle collision. There is an addional twist in that + * each node of the collision linked list is a page containing a fixed amount of + * value. This provides a better compromise between memory usage and speed. + * + * Each bucket is made up of a chained list of ValueInternalLink. The last + * link of a given bucket can be found in the 'previous_' field of the following bucket. + * The last link of the last bucket is stored in tailLink_ as it has no following bucket. + * Only the last link of a bucket may contains 'available' item. The last link always + * contains at least one element unless is it the bucket one very first link. + */ + class JSON_API ValueInternalMap + { + friend class ValueIteratorBase; + friend class Value; + public: + typedef unsigned int HashKey; + typedef unsigned int BucketIndex; + +# ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + struct IteratorState + { + IteratorState() + : map_(0) + , link_(0) + , itemIndex_(0) + , bucketIndex_(0) + { + } + ValueInternalMap *map_; + ValueInternalLink *link_; + BucketIndex itemIndex_; + BucketIndex bucketIndex_; + }; +# endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + + ValueInternalMap(); + ValueInternalMap( const ValueInternalMap &other ); + ValueInternalMap &operator =( const ValueInternalMap &other ); + ~ValueInternalMap(); + + void swap( ValueInternalMap &other ); + + BucketIndex size() const; + + void clear(); + + bool reserveDelta( BucketIndex growth ); + + bool reserve( BucketIndex newItemCount ); + + const Value *find( const char *key ) const; + + Value *find( const char *key ); + + Value &resolveReference( const char *key, + bool isStatic ); + + void remove( const char *key ); + + void doActualRemove( ValueInternalLink *link, + BucketIndex index, + BucketIndex bucketIndex ); + + ValueInternalLink *&getLastLinkInBucket( BucketIndex bucketIndex ); + + Value &setNewItem( const char *key, + bool isStatic, + ValueInternalLink *link, + BucketIndex index ); + + Value &unsafeAdd( const char *key, + bool isStatic, + HashKey hashedKey ); + + HashKey hash( const char *key ) const; + + int compare( const ValueInternalMap &other ) const; + + private: + void makeBeginIterator( IteratorState &it ) const; + void makeEndIterator( IteratorState &it ) const; + static bool equals( const IteratorState &x, const IteratorState &other ); + static void increment( IteratorState &iterator ); + static void incrementBucket( IteratorState &iterator ); + static void decrement( IteratorState &iterator ); + static const char *key( const IteratorState &iterator ); + static const char *key( const IteratorState &iterator, bool &isStatic ); + static Value &value( const IteratorState &iterator ); + static int distance( const IteratorState &x, const IteratorState &y ); + + private: + ValueInternalLink *buckets_; + ValueInternalLink *tailLink_; + BucketIndex bucketsSize_; + BucketIndex itemCount_; + }; + + /** \brief A simplified deque implementation used internally by Value. + * \internal + * It is based on a list of fixed "page", each page contains a fixed number of items. + * Instead of using a linked-list, a array of pointer is used for fast item look-up. + * Look-up for an element is as follow: + * - compute page index: pageIndex = itemIndex / itemsPerPage + * - look-up item in page: pages_[pageIndex][itemIndex % itemsPerPage] + * + * Insertion is amortized constant time (only the array containing the index of pointers + * need to be reallocated when items are appended). + */ + class JSON_API ValueInternalArray + { + friend class Value; + friend class ValueIteratorBase; + public: + enum { itemsPerPage = 8 }; // should be a power of 2 for fast divide and modulo. + typedef Value::ArrayIndex ArrayIndex; + typedef unsigned int PageIndex; + +# ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + struct IteratorState // Must be a POD + { + IteratorState() + : array_(0) + , currentPageIndex_(0) + , currentItemIndex_(0) + { + } + ValueInternalArray *array_; + Value **currentPageIndex_; + unsigned int currentItemIndex_; + }; +# endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + + ValueInternalArray(); + ValueInternalArray( const ValueInternalArray &other ); + ValueInternalArray &operator =( const ValueInternalArray &other ); + ~ValueInternalArray(); + void swap( ValueInternalArray &other ); + + void clear(); + void resize( ArrayIndex newSize ); + + Value &resolveReference( ArrayIndex index ); + + Value *find( ArrayIndex index ) const; + + ArrayIndex size() const; + + int compare( const ValueInternalArray &other ) const; + + private: + static bool equals( const IteratorState &x, const IteratorState &other ); + static void increment( IteratorState &iterator ); + static void decrement( IteratorState &iterator ); + static Value &dereference( const IteratorState &iterator ); + static Value &unsafeDereference( const IteratorState &iterator ); + static int distance( const IteratorState &x, const IteratorState &y ); + static ArrayIndex indexOf( const IteratorState &iterator ); + void makeBeginIterator( IteratorState &it ) const; + void makeEndIterator( IteratorState &it ) const; + void makeIterator( IteratorState &it, ArrayIndex index ) const; + + void makeIndexValid( ArrayIndex index ); + + Value **pages_; + ArrayIndex size_; + PageIndex pageCount_; + }; + + /** \brief Experimental: do not use. Allocator to customize Value internal array. + * Below is an example of a simple implementation (actual implementation use + * memory pool). + \code +class DefaultValueArrayAllocator : public ValueArrayAllocator +{ +public: // overridden from ValueArrayAllocator + virtual ~DefaultValueArrayAllocator() + { + } + + virtual ValueInternalArray *newArray() + { + return new ValueInternalArray(); + } + + virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) + { + return new ValueInternalArray( other ); + } + + virtual void destruct( ValueInternalArray *array ) + { + delete array; + } + + virtual void reallocateArrayPageIndex( Value **&indexes, + ValueInternalArray::PageIndex &indexCount, + ValueInternalArray::PageIndex minNewIndexCount ) + { + ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1; + if ( minNewIndexCount > newIndexCount ) + newIndexCount = minNewIndexCount; + void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount ); + if ( !newIndexes ) + throw std::bad_alloc(); + indexCount = newIndexCount; + indexes = static_cast( newIndexes ); + } + virtual void releaseArrayPageIndex( Value **indexes, + ValueInternalArray::PageIndex indexCount ) + { + if ( indexes ) + free( indexes ); + } + + virtual Value *allocateArrayPage() + { + return static_cast( malloc( sizeof(Value) * ValueInternalArray::itemsPerPage ) ); + } + + virtual void releaseArrayPage( Value *value ) + { + if ( value ) + free( value ); + } +}; + \endcode + */ + class JSON_API ValueArrayAllocator + { + public: + virtual ~ValueArrayAllocator(); + virtual ValueInternalArray *newArray() = 0; + virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) = 0; + virtual void destructArray( ValueInternalArray *array ) = 0; + /** \brief Reallocate array page index. + * Reallocates an array of pointer on each page. + * \param indexes [input] pointer on the current index. May be \c NULL. + * [output] pointer on the new index of at least + * \a minNewIndexCount pages. + * \param indexCount [input] current number of pages in the index. + * [output] number of page the reallocated index can handle. + * \b MUST be >= \a minNewIndexCount. + * \param minNewIndexCount Minimum number of page the new index must be able to + * handle. + */ + virtual void reallocateArrayPageIndex( Value **&indexes, + ValueInternalArray::PageIndex &indexCount, + ValueInternalArray::PageIndex minNewIndexCount ) = 0; + virtual void releaseArrayPageIndex( Value **indexes, + ValueInternalArray::PageIndex indexCount ) = 0; + virtual Value *allocateArrayPage() = 0; + virtual void releaseArrayPage( Value *value ) = 0; + }; +#endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP + + + /** \brief base class for Value iterators. + * + */ + class ValueIteratorBase + { + public: + typedef unsigned int size_t; + typedef int difference_type; + typedef ValueIteratorBase SelfType; + + ValueIteratorBase(); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + explicit ValueIteratorBase( const Value::ObjectValues::iterator ¤t ); +#else + ValueIteratorBase( const ValueInternalArray::IteratorState &state ); + ValueIteratorBase( const ValueInternalMap::IteratorState &state ); +#endif + + bool operator ==( const SelfType &other ) const + { + return isEqual( other ); + } + + bool operator !=( const SelfType &other ) const + { + return !isEqual( other ); + } + + difference_type operator -( const SelfType &other ) const + { + return computeDistance( other ); + } + + /// Return either the index or the member name of the referenced value as a Value. + Value key() const; + + /// Return the index of the referenced Value. -1 if it is not an arrayValue. + UInt index() const; + + /// Return the member name of the referenced Value. "" if it is not an objectValue. + const char *memberName() const; + + protected: + Value &deref() const; + + void increment(); + + void decrement(); + + difference_type computeDistance( const SelfType &other ) const; + + bool isEqual( const SelfType &other ) const; + + void copy( const SelfType &other ); + + private: +#ifndef JSON_VALUE_USE_INTERNAL_MAP + Value::ObjectValues::iterator current_; + // Indicates that iterator is for a null value. + bool isNull_; +#else + union + { + ValueInternalArray::IteratorState array_; + ValueInternalMap::IteratorState map_; + } iterator_; + bool isArray_; +#endif + }; + + /** \brief const iterator for object and array value. + * + */ + class ValueConstIterator : public ValueIteratorBase + { + friend class Value; + public: + typedef unsigned int size_t; + typedef int difference_type; + typedef const Value &reference; + typedef const Value *pointer; + typedef ValueConstIterator SelfType; + + ValueConstIterator(); + private: + /*! \internal Use by Value to create an iterator. + */ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + explicit ValueConstIterator( const Value::ObjectValues::iterator ¤t ); +#else + ValueConstIterator( const ValueInternalArray::IteratorState &state ); + ValueConstIterator( const ValueInternalMap::IteratorState &state ); +#endif + public: + SelfType &operator =( const ValueIteratorBase &other ); + + SelfType operator++( int ) + { + SelfType temp( *this ); + ++*this; + return temp; + } + + SelfType operator--( int ) + { + SelfType temp( *this ); + --*this; + return temp; + } + + SelfType &operator--() + { + decrement(); + return *this; + } + + SelfType &operator++() + { + increment(); + return *this; + } + + reference operator *() const + { + return deref(); + } + }; + + + /** \brief Iterator for object and array value. + */ + class ValueIterator : public ValueIteratorBase + { + friend class Value; + public: + typedef unsigned int size_t; + typedef int difference_type; + typedef Value &reference; + typedef Value *pointer; + typedef ValueIterator SelfType; + + ValueIterator(); + ValueIterator( const ValueConstIterator &other ); + ValueIterator( const ValueIterator &other ); + private: + /*! \internal Use by Value to create an iterator. + */ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + explicit ValueIterator( const Value::ObjectValues::iterator ¤t ); +#else + ValueIterator( const ValueInternalArray::IteratorState &state ); + ValueIterator( const ValueInternalMap::IteratorState &state ); +#endif + public: + + SelfType &operator =( const SelfType &other ); + + SelfType operator++( int ) + { + SelfType temp( *this ); + ++*this; + return temp; + } + + SelfType operator--( int ) + { + SelfType temp( *this ); + --*this; + return temp; + } + + SelfType &operator--() + { + decrement(); + return *this; + } + + SelfType &operator++() + { + increment(); + return *this; + } + + reference operator *() const + { + return deref(); + } + }; + + +} // namespace Json + + +#endif // CPPTL_JSON_H_INCLUDED diff --git a/util/json/writer.h b/util/json/writer.h new file mode 100644 index 00000000..5f4b83be --- /dev/null +++ b/util/json/writer.h @@ -0,0 +1,174 @@ +#ifndef JSON_WRITER_H_INCLUDED +# define JSON_WRITER_H_INCLUDED + +# include "value.h" +# include +# include +# include + +namespace Json { + + class Value; + + /** \brief Abstract class for writers. + */ + class JSON_API Writer + { + public: + virtual ~Writer(); + + virtual std::string write( const Value &root ) = 0; + }; + + /** \brief Outputs a Value in JSON format without formatting (not human friendly). + * + * The JSON document is written in a single line. It is not intended for 'human' consumption, + * but may be usefull to support feature such as RPC where bandwith is limited. + * \sa Reader, Value + */ + class JSON_API FastWriter : public Writer + { + public: + FastWriter(); + virtual ~FastWriter(){} + + void enableYAMLCompatibility(); + + public: // overridden from Writer + virtual std::string write( const Value &root ); + + private: + void writeValue( const Value &value ); + + std::string document_; + bool yamlCompatiblityEnabled_; + }; + + /** \brief Writes a Value in JSON format in a human friendly way. + * + * The rules for line break and indent are as follow: + * - Object value: + * - if empty then print {} without indent and line break + * - if not empty the print '{', line break & indent, print one value per line + * and then unindent and line break and print '}'. + * - Array value: + * - if empty then print [] without indent and line break + * - if the array contains no object value, empty array or some other value types, + * and all the values fit on one lines, then print the array on a single line. + * - otherwise, it the values do not fit on one line, or the array contains + * object or non empty array, then print one value per line. + * + * If the Value have comments then they are outputed according to their #CommentPlacement. + * + * \sa Reader, Value, Value::setComment() + */ + class JSON_API StyledWriter: public Writer + { + public: + StyledWriter(); + virtual ~StyledWriter(){} + + public: // overridden from Writer + /** \brief Serialize a Value in JSON format. + * \param root Value to serialize. + * \return String containing the JSON document that represents the root value. + */ + virtual std::string write( const Value &root ); + + private: + void writeValue( const Value &value ); + void writeArrayValue( const Value &value ); + bool isMultineArray( const Value &value ); + void pushValue( const std::string &value ); + void writeIndent(); + void writeWithIndent( const std::string &value ); + void indent(); + void unindent(); + void writeCommentBeforeValue( const Value &root ); + void writeCommentAfterValueOnSameLine( const Value &root ); + bool hasCommentForValue( const Value &value ); + static std::string normalizeEOL( const std::string &text ); + + typedef std::vector ChildValues; + + ChildValues childValues_; + std::string document_; + std::string indentString_; + int rightMargin_; + int indentSize_; + bool addChildValues_; + }; + + /** \brief Writes a Value in JSON format in a human friendly way, + to a stream rather than to a string. + * + * The rules for line break and indent are as follow: + * - Object value: + * - if empty then print {} without indent and line break + * - if not empty the print '{', line break & indent, print one value per line + * and then unindent and line break and print '}'. + * - Array value: + * - if empty then print [] without indent and line break + * - if the array contains no object value, empty array or some other value types, + * and all the values fit on one lines, then print the array on a single line. + * - otherwise, it the values do not fit on one line, or the array contains + * object or non empty array, then print one value per line. + * + * If the Value have comments then they are outputed according to their #CommentPlacement. + * + * \param indentation Each level will be indented by this amount extra. + * \sa Reader, Value, Value::setComment() + */ + class JSON_API StyledStreamWriter + { + public: + StyledStreamWriter( std::string indentation="\t" ); + ~StyledStreamWriter(){} + + public: + /** \brief Serialize a Value in JSON format. + * \param out Stream to write to. (Can be ostringstream, e.g.) + * \param root Value to serialize. + * \note There is no point in deriving from Writer, since write() should not return a value. + */ + void write( std::ostream &out, const Value &root ); + + private: + void writeValue( const Value &value ); + void writeArrayValue( const Value &value ); + bool isMultineArray( const Value &value ); + void pushValue( const std::string &value ); + void writeIndent(); + void writeWithIndent( const std::string &value ); + void indent(); + void unindent(); + void writeCommentBeforeValue( const Value &root ); + void writeCommentAfterValueOnSameLine( const Value &root ); + bool hasCommentForValue( const Value &value ); + static std::string normalizeEOL( const std::string &text ); + + typedef std::vector ChildValues; + + ChildValues childValues_; + std::ostream* document_; + std::string indentString_; + int rightMargin_; + std::string indentation_; + bool addChildValues_; + }; + + std::string JSON_API valueToString( Int value ); + std::string JSON_API valueToString( UInt value ); + std::string JSON_API valueToString( double value ); + std::string JSON_API valueToString( bool value ); + std::string JSON_API valueToQuotedString( const char *value ); + + /// \brief Output using the StyledStreamWriter. + /// \see Json::operator>>() + std::ostream& operator<<( std::ostream&, const Value &root ); + +} // namespace Json + + + +#endif // JSON_WRITER_H_INCLUDED From 3634a627f1e94ba36aaad8b633aece8e76f60095 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Wed, 12 Oct 2011 23:30:42 +0200 Subject: [PATCH 151/788] Added piped process support to Util::Procs --- util/proc.cpp | 105 ++++++++++++++++++++++++++++++++++++++++---------- util/proc.h | 2 + 2 files changed, 87 insertions(+), 20 deletions(-) diff --git a/util/proc.cpp b/util/proc.cpp index 0e891be5..db004d0d 100644 --- a/util/proc.cpp +++ b/util/proc.cpp @@ -24,6 +24,33 @@ void Util::Procs::childsig_handler(int signum){ plist.erase(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); + #if DEBUG >= 1 + std::cerr << "Error running \"" << cmd << "\": " << strerror(errno) << std::endl; + #endif + _exit(42); +} + /// Starts a new process if the name is not already active. /// \return 0 if process was not started, process PID otherwise. /// \arg name Name for this process - only used internally. @@ -40,26 +67,7 @@ pid_t Util::Procs::Start(std::string name, std::string cmd){ } pid_t ret = fork(); if (ret == 0){ - //split cmd into arguments - //supports a maximum of 20 arguments - char * tmp = (char*)cmd.c_str(); - char * tmp2 = 0; - char * args[21]; - int i = 0; - tmp2 = strtok(tmp, " "); - args[0] = tmp2; - while (tmp2 != 0 && (i < 20)){ - tmp2 = strtok(0, " "); - ++i; - args[i] = tmp2; - } - if (i == 20){args[20] = 0;} - //execute the command - execvp(args[0], args); - #if DEBUG >= 1 - std::cerr << "Error: " << strerror(errno) << std::endl; - #endif - _exit(42); + runCmd(cmd); }else{ if (ret > 0){ #if DEBUG >= 1 @@ -76,6 +84,63 @@ pid_t Util::Procs::Start(std::string name, std::string cmd){ return ret; } +/// Starts two piped processes if the name is not already active. +/// \return 0 if process was not started, main (receiving) process PID otherwise. +/// \arg name Name for this process - only used internally. +/// \arg cmd Commandline for sub (sending) process. +/// \arg cmd2 Commandline for main (receiving) process. +pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2){ + if (isActive(name)){return getPid(name);} + if (!handler_set){ + struct sigaction new_action; + new_action.sa_handler = Util::Procs::childsig_handler; + sigemptyset(&new_action.sa_mask); + new_action.sa_flags = 0; + sigaction(SIGCHLD, &new_action, NULL); + handler_set = true; + } + pid_t ret = fork(); + if (ret == 0){ + int pfildes[2]; + if (pipe(pfildes) == -1){ + #if DEBUG >= 1 + std::cerr << "Process " << name << " could not be started. Pipe creation failed." << std::endl; + #endif + _exit(41); + } + pid_t pid; + if ((pid = fork()) == -1){ + #if DEBUG >= 1 + std::cerr << "Process " << name << " could not be started. Second fork() failed." << std::endl; + #endif + _exit(41); + }else if (pid == 0){ + close(pfildes[0]); + dup2(pfildes[1],1); + close(pfildes[1]); + runCmd(cmd); + }else{ + close(pfildes[1]); + dup2(pfildes[0],0); + close(pfildes[0]); + runCmd(cmd2); + } + }else{ + if (ret > 0){ + #if DEBUG >= 1 + std::cerr << "Process " << name << " started, PID " << ret << ": " << cmd << " | " << cmd2 << std::endl; + #endif + plist.insert(std::pair(ret, name)); + }else{ + #if DEBUG >= 1 + std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl; + #endif + return 0; + } + } + return ret; +} + /// Stops the named process, if running. /// \arg name (Internal) name of process to stop void Util::Procs::Stop(std::string name){ diff --git a/util/proc.h b/util/proc.h index a2c10ec0..a74d75bd 100644 --- a/util/proc.h +++ b/util/proc.h @@ -14,8 +14,10 @@ namespace Util{ static std::map plist; ///< Holds active processes static bool handler_set; ///< If true, the sigchld handler has been setup. static void childsig_handler(int signum); + static void runCmd(std::string & cmd); public: static pid_t Start(std::string name, std::string cmd); + static pid_t Start(std::string name, std::string cmd, std::string cmd2); static void Stop(std::string name); static void Stop(pid_t name); static void StopAll(); From 0c01d8a5f1c4f2268926899d6f1886eb0dae8ef6 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sat, 15 Oct 2011 05:27:36 +0200 Subject: [PATCH 152/788] Fixed and tested piped process support, first version of main DDV_Controller application --- util/proc.cpp | 80 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 50 insertions(+), 30 deletions(-) diff --git a/util/proc.cpp b/util/proc.cpp index db004d0d..b5257065 100644 --- a/util/proc.cpp +++ b/util/proc.cpp @@ -19,9 +19,16 @@ void Util::Procs::childsig_handler(int signum){ if (signum != SIGCHLD){return;} pid_t ret = wait(0); #if DEBUG >= 1 - std::cerr << "Process " << plist[ret] << " terminated." << std::endl; + std::string pname = plist[ret]; #endif plist.erase(ret); + #if DEBUG >= 1 + if (isActive(pname)){ + std::cerr << "Process " << pname << " half-terminated." << std::endl; + }else{ + std::cerr << "Process " << pname << " fully terminated." << std::endl; + } + #endif } /// Attempts to run the command cmd. @@ -99,53 +106,66 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2){ sigaction(SIGCHLD, &new_action, NULL); handler_set = true; } + int pfildes[2]; + if (pipe(pfildes) == -1){ + #if DEBUG >= 1 + std::cerr << "Process " << name << " could not be started. Pipe creation failed." << std::endl; + #endif + return 0; + } + pid_t ret = fork(); if (ret == 0){ - int pfildes[2]; - if (pipe(pfildes) == -1){ - #if DEBUG >= 1 - std::cerr << "Process " << name << " could not be started. Pipe creation failed." << std::endl; - #endif - _exit(41); - } - pid_t pid; - if ((pid = fork()) == -1){ - #if DEBUG >= 1 - std::cerr << "Process " << name << " could not be started. Second fork() failed." << std::endl; - #endif - _exit(41); - }else if (pid == 0){ - close(pfildes[0]); - dup2(pfildes[1],1); - close(pfildes[1]); - runCmd(cmd); - }else{ - close(pfildes[1]); - dup2(pfildes[0],0); - close(pfildes[0]); - runCmd(cmd2); - } + close(pfildes[0]); + dup2(pfildes[1],1); + close(pfildes[1]); + runCmd(cmd); }else{ if (ret > 0){ - #if DEBUG >= 1 - std::cerr << "Process " << name << " started, PID " << ret << ": " << cmd << " | " << cmd2 << std::endl; - #endif plist.insert(std::pair(ret, name)); }else{ #if DEBUG >= 1 std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl; #endif + close(pfildes[1]); + close(pfildes[0]); return 0; } } + + pid_t ret2 = fork(); + if (ret2 == 0){ + close(pfildes[1]); + dup2(pfildes[0],0); + close(pfildes[0]); + runCmd(cmd2); + }else{ + if (ret2 > 0){ + #if DEBUG >= 1 + std::cerr << "Process " << name << " started, PIDs (" << ret << ", " << ret2 << "): " << cmd << " | " << cmd2 << std::endl; + #endif + plist.insert(std::pair(ret2, name)); + }else{ + #if DEBUG >= 1 + std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl; + #endif + Stop(name); + close(pfildes[1]); + close(pfildes[0]); + return 0; + } + } + close(pfildes[1]); + close(pfildes[0]); return ret; } /// Stops the named process, if running. /// \arg name (Internal) name of process to stop void Util::Procs::Stop(std::string name){ - if (!isActive(name)){return;} - Stop(getPid(name)); + while (isActive(name)){ + Stop(getPid(name)); + } } /// Stops the process with this pid, if running. From fdfe95e7928c9e8aac77c71b5c26f84426dd3971 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 16 Oct 2011 16:45:55 +0200 Subject: [PATCH 153/788] Fixed some issues in HTTP, added GET param support, fixed a bug, etc... lots of maintenaince. --- util/http_parser.cpp | 74 +++++++++++++++++++++++++------------------- util/http_parser.h | 3 +- util/proc.cpp | 25 +++++++++++++++ util/proc.h | 3 ++ 4 files changed, 72 insertions(+), 33 deletions(-) diff --git a/util/http_parser.cpp b/util/http_parser.cpp index 274cb3c3..da800130 100644 --- a/util/http_parser.cpp +++ b/util/http_parser.cpp @@ -14,9 +14,9 @@ void HTTP::Parser::Clean(){ method = "GET"; url = "/"; protocol = "HTTP/1.1"; - body = ""; + body.clear(); length = 0; - HTTPbuffer = ""; + HTTPbuffer.clear(); headers.clear(); vars.clear(); } @@ -172,7 +172,10 @@ bool HTTP::Parser::parse(){ if (f != std::string::npos){url = tmpA.substr(0, f); tmpA.erase(0, f+1);} f = tmpA.find(' '); if (f != std::string::npos){protocol = tmpA.substr(0, f); tmpA.erase(0, f+1);} - /// \todo Include GET variable parsing? + if (url.find('?') != std::string::npos){ + std::string queryvars = url.substr(url.find('?')+1); + parseVars(queryvars); //parse GET variables + } }else{ if (tmpA.size() == 0){ seenHeaders = true; @@ -191,22 +194,7 @@ bool HTTP::Parser::parse(){ if (HTTPbuffer.length() >= length){ body = HTTPbuffer.substr(0, length); HTTPbuffer.erase(0, length); - std::string tmppost = body; - std::string varname; - std::string varval; - while (tmppost.find('=') != std::string::npos){ - size_t found = tmppost.find('='); - varname = urlunescape((char*)tmppost.substr(0, found).c_str()); - tmppost.erase(0, found+1); - found = tmppost.find('&'); - varval = urlunescape((char*)tmppost.substr(0, found).c_str()); - SetVar(varname, varval); - if (found == std::string::npos){ - tmppost.clear(); - }else{ - tmppost.erase(0, found+1); - } - } + parseVars(body); //parse POST variables return true; }else{ return false; @@ -229,6 +217,26 @@ void HTTP::Parser::SendResponse(Socket::Connection & conn, std::string code, std conn.write(tmp); } +/// 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; + while (data.find('=') != std::string::npos){ + size_t found = data.find('='); + varname = urlunescape(data.substr(0, found)); + data.erase(0, found+1); + found = data.find('&'); + varval = urlunescape(data.substr(0, found)); + SetVar(varname, varval); + if (found == std::string::npos){ + data.clear(); + }else{ + data.erase(0, found+1); + } + } +} + /// Sends data as HTTP/1.1 bodypart to conn. /// HTTP/1.1 chunked encoding is automatically applied if needed. /// \param conn The Socket::Connection to send the part over. @@ -257,24 +265,26 @@ void HTTP::Parser::SendBodyPart(Socket::Connection & conn, std::string bodypart) } } -/// Unescapes URLencoded C-strings to a std::string. -/// This function *will* destroy the input data! -std::string HTTP::Parser::urlunescape(char *s){ - char *p; - for (p = s; *s != '\0'; ++s){ - if (*s == '%'){ - if (*++s != '\0'){ - *p = unhex(*s) << 4; +/// Unescapes URLencoded std::strings. +std::string HTTP::Parser::urlunescape(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; } - if (*++s != '\0'){ - *p++ += unhex(*s); + ++i; + if (i < in.length()){ + tmp += unhex(in[i]); } + out += tmp; } else { - if (*s == '+'){*p++ = ' ';}else{*p++ = *s;} + if (in[i] == '+'){out += ' ';}else{out += in[i];} } } - *p = '\0'; - return std::string(s); + return out; } /// Helper function for urlunescape. diff --git a/util/http_parser.h b/util/http_parser.h index 542c06e6..a38b94e1 100644 --- a/util/http_parser.h +++ b/util/http_parser.h @@ -30,7 +30,7 @@ namespace HTTP{ void SendBodyPart(Socket::Connection & conn, std::string bodypart); void Clean(); bool CleanForNext(); - std::string urlunescape(char *s); ///< Unescapes URLencoded C-strings to a std::string. + std::string urlunescape(std::string in); std::string body; std::string method; std::string url; @@ -40,6 +40,7 @@ namespace HTTP{ bool seenHeaders; bool seenReq; bool parse(); + void parseVars(std::string & data); std::string HTTPbuffer; std::map headers; std::map vars; diff --git a/util/proc.cpp b/util/proc.cpp index b5257065..bfcd3210 100644 --- a/util/proc.cpp +++ b/util/proc.cpp @@ -10,10 +10,35 @@ #if DEBUG >= 1 #include #endif +#include +#include std::map Util::Procs::plist; bool Util::Procs::handler_set = false; +/// Sets the current process' running user +void Util::setUser(std::string username){ + if (username != "root"){ + struct passwd * user_info = getpwnam(username.c_str()); + if (!user_info){ + #if DEBUG >= 1 + fprintf(stderr, "Error: could not setuid %s: could not get PID\n", username.c_str()); + #endif + return 1; + }else{ + if (setuid(user_info->pw_uid) != 0){ + #if DEBUG >= 1 + fprintf(stderr, "Error: could not setuid %s: not allowed\n", username.c_str()); + #endif + }else{ + #if DEBUG >= 3 + fprintf(stderr, "Changed user to %s\n", username.c_str()); + #endif + } + } + } +} + /// Used internally to capture child signals and update plist. void Util::Procs::childsig_handler(int signum){ if (signum != SIGCHLD){return;} diff --git a/util/proc.h b/util/proc.h index a74d75bd..4b117f0c 100644 --- a/util/proc.h +++ b/util/proc.h @@ -27,4 +27,7 @@ namespace Util{ static pid_t getPid(std::string name); static std::string getName(pid_t name); }; + + static setUser(std::string user); + }; From 8c29434ef4a768fb91b4488b803903b854eed955 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 17 Oct 2011 08:42:26 +0200 Subject: [PATCH 154/788] Util merge-age --- util/{proc.cpp => util.cpp} | 16 ++++++++++++++-- util/{proc.h => util.h} | 25 +++++++++++++++++++++++-- 2 files changed, 37 insertions(+), 4 deletions(-) rename util/{proc.cpp => util.cpp} (95%) rename util/{proc.h => util.h} (61%) diff --git a/util/proc.cpp b/util/util.cpp similarity index 95% rename from util/proc.cpp rename to util/util.cpp index bfcd3210..4af99705 100644 --- a/util/proc.cpp +++ b/util/util.cpp @@ -1,5 +1,5 @@ -/// \file proc.cpp -/// Contains generic functions for managing processes. +/// \file util.cpp +/// Contains generic functions for managing processes and configuration. #include "proc.h" #include @@ -246,3 +246,15 @@ std::string Util::Procs::getName(pid_t name){ } return ""; } + +Util::Config::Config(){ + listen_port = 4242; + daemon_mode = true; + interface = "0.0.0.0"; + configfile = "/etc/ddvtech.conf"; + username = "root"; + ignore_daemon = false; + ignore_interface = false; + ignore_port = false; + ignore_user = false; +} \ No newline at end of file diff --git a/util/proc.h b/util/util.h similarity index 61% rename from util/proc.h rename to util/util.h index 4b117f0c..4037a9ca 100644 --- a/util/proc.h +++ b/util/util.h @@ -1,5 +1,5 @@ -/// \file proc.h -/// Contains generic function headers for managing processes. +/// \file util.h +/// Contains generic function headers for managing processes and configuration. #include #include @@ -28,6 +28,27 @@ namespace Util{ static std::string getName(pid_t name); }; + /// Will set the active user to the named username. static setUser(std::string user); + + /// Deals with parsing configuration from files or commandline options. + class Config{ + private: + bool ignore_daemon; + bool ignore_interface; + bool ignore_port; + bool ignore_user; + public: + std::string configfile; + bool daemon_mode; + std::string interface; + int listen_port; + std::string username; + Config(); + void parseArgs(int argc, char ** argv); + }; + /// Will turn the current process into a daemon. + void Daemonize(); + }; From ad39aafa893eb5da9b008b291467a3d6c5d0021e Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 17 Oct 2011 21:21:38 +0200 Subject: [PATCH 155/788] Moved new gearbox to better dir, some cleanup --- util/util.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/util/util.cpp b/util/util.cpp index 4af99705..3f5b6495 100644 --- a/util/util.cpp +++ b/util/util.cpp @@ -247,6 +247,7 @@ std::string Util::Procs::getName(pid_t name){ return ""; } +/// Creates a new configuration manager. Util::Config::Config(){ listen_port = 4242; daemon_mode = true; @@ -257,4 +258,8 @@ Util::Config::Config(){ ignore_interface = false; ignore_port = false; ignore_user = false; +} + +void parseArgs(int argc, char ** argv){ + } \ No newline at end of file From 672aeb6670039c705d7a6a3547aa331c05f113f1 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 18 Oct 2011 03:44:41 +0200 Subject: [PATCH 156/788] Fixed a bug --- util/http_parser.cpp | 66 +++++++++++++++++++------------- util/http_parser.h | 6 ++- util/server_setup.cpp | 2 +- util/util.cpp | 87 +++++++++++++++++++++++++++++++++++++++---- util/util.h | 6 ++- 5 files changed, 130 insertions(+), 37 deletions(-) diff --git a/util/http_parser.cpp b/util/http_parser.cpp index da800130..f46e5a2b 100644 --- a/util/http_parser.cpp +++ b/util/http_parser.cpp @@ -16,32 +16,20 @@ void HTTP::Parser::Clean(){ protocol = "HTTP/1.1"; body.clear(); length = 0; - HTTPbuffer.clear(); headers.clear(); vars.clear(); } /// Re-initializes the HTTP::Parser, leaving the internal data buffer alone, then tries to parse a new request or response. -/// First does the same as HTTP::Parser::Clean(), but does not clear the internal data buffer. -/// This function then calls the HTTP::Parser::parse() function, and returns that functions return value. +/// Does the same as HTTP::Parser::Clean(), then returns HTTP::Parser::parse(). bool HTTP::Parser::CleanForNext(){ - seenHeaders = false; - seenReq = false; - method = "GET"; - url = "/"; - protocol = "HTTP/1.1"; - body = ""; - length = 0; - headers.clear(); - vars.clear(); + Clean(); return parse(); } /// 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 the internal data buffer are used, -/// where the internal data buffer is used as the body of the request. -/// This means you cannot mix receiving and sending, because the body would get corrupted. +/// 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? @@ -51,15 +39,13 @@ std::string HTTP::Parser::BuildRequest(){ tmp += (*it).first + ": " + (*it).second + "\n"; } tmp += "\n"; - tmp += HTTPbuffer; + tmp += body; return tmp; } /// Returns a string containing a valid HTTP 1.0 or 1.1 response, ready for sending. /// The response is partly build from internal variables set before this call is made. -/// To be precise, protocol, headers and the internal data buffer are used, -/// where the internal data buffer is used as the body of the response. -/// This means you cannot mix receiving and sending, because the body would get corrupted. +/// 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. @@ -71,7 +57,7 @@ std::string HTTP::Parser::BuildResponse(std::string code, std::string message){ tmp += (*it).first + ": " + (*it).second + "\n"; } tmp += "\n"; - tmp += HTTPbuffer; + tmp += body; return tmp; } @@ -87,7 +73,7 @@ void HTTP::Parser::Trim(std::string & s){ /// 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){ - HTTPbuffer = s; + body = s; SetHeader("Content-Length", s.length()); } @@ -95,8 +81,8 @@ void HTTP::Parser::SetBody(std::string s){ /// \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){ - HTTPbuffer = ""; - HTTPbuffer.append(buffer, len); + body = ""; + body.append(buffer, len); SetHeader("Content-Length", len); } @@ -265,8 +251,8 @@ void HTTP::Parser::SendBodyPart(Socket::Connection & conn, std::string bodypart) } } -/// Unescapes URLencoded std::strings. -std::string HTTP::Parser::urlunescape(std::string in){ +/// 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] == '%'){ @@ -292,3 +278,33 @@ std::string HTTP::Parser::urlunescape(std::string in){ int HTTP::Parser::unhex(char c){ return( c >= '0' && c <= '9' ? c - '0' : c >= 'A' && c <= 'F' ? c - 'A' + 10 : c - 'a' + 10 ); } + +/// URLencodes std::string data. +std::string HTTP::Parser::urlencode(const std::string &c){ + std::string escaped=""; + int max = c.length(); + for(int i=0; i>4; + char dig2 = (dec&0x0F); + if (dig1<= 9) dig1+=48; + if (10<= dig1 && dig1<=15) dig1+=97-10; + if (dig2<= 9) dig2+=48; + if (10<= dig2 && dig2<=15) dig2+=97-10; + std::string r; + r.append(&dig1, 1); + r.append(&dig2, 1); + return r; +} diff --git a/util/http_parser.h b/util/http_parser.h index a38b94e1..6172ac00 100644 --- a/util/http_parser.h +++ b/util/http_parser.h @@ -30,7 +30,8 @@ namespace HTTP{ void SendBodyPart(Socket::Connection & conn, std::string bodypart); void Clean(); bool CleanForNext(); - std::string urlunescape(std::string in); + 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; @@ -45,6 +46,7 @@ namespace HTTP{ std::map headers; std::map vars; void Trim(std::string & s); - int unhex(char c); ///< Helper function for urlunescape. + static int unhex(char c); + static std::string hex(char dec); };//HTTP::Parser class };//HTTP namespace diff --git a/util/server_setup.cpp b/util/server_setup.cpp index 23a5422e..34327442 100644 --- a/util/server_setup.cpp +++ b/util/server_setup.cpp @@ -159,10 +159,10 @@ int main(int argc, char ** argv){ if (server_socket.connected()){ //if setup success, enter daemon mode if requested if (daemon_mode){ - daemon(1, 0); #if DEBUG >= 3 fprintf(stderr, "Going into background mode...\n"); #endif + daemon(1, 0); } }else{ #if DEBUG >= 1 diff --git a/util/util.cpp b/util/util.cpp index 3f5b6495..687dba58 100644 --- a/util/util.cpp +++ b/util/util.cpp @@ -1,17 +1,19 @@ /// \file util.cpp /// Contains generic functions for managing processes and configuration. -#include "proc.h" +#include "util.h" #include #include #include #include #include -#if DEBUG >= 1 #include -#endif #include #include +#include +#include +#include +#include std::map Util::Procs::plist; bool Util::Procs::handler_set = false; @@ -24,7 +26,7 @@ void Util::setUser(std::string username){ #if DEBUG >= 1 fprintf(stderr, "Error: could not setuid %s: could not get PID\n", username.c_str()); #endif - return 1; + return; }else{ if (setuid(user_info->pw_uid) != 0){ #if DEBUG >= 1 @@ -260,6 +262,77 @@ Util::Config::Config(){ ignore_user = false; } -void parseArgs(int argc, char ** argv){ - -} \ No newline at end of file +/// Parses commandline arguments. +/// Calls exit if an unknown option is encountered, printing a help message. +/// Assumes confsection is set. +void Util::Config::parseArgs(int argc, char ** argv){ + int opt = 0; + static const char *optString = "ndp:i:u:c:h?"; + static const struct option longOpts[] = { + {"help",0,0,'h'}, + {"port",1,0,'p'}, + {"interface",1,0,'i'}, + {"username",1,0,'u'}, + {"no-daemon",0,0,'n'}, + {"daemon",0,0,'d'}, + {"configfile",1,0,'c'} + }; + while ((opt = getopt_long(argc, argv, optString, longOpts, 0)) != -1){ + switch (opt){ + case 'p': listen_port = atoi(optarg); ignore_port = true; break; + case 'i': interface = optarg; ignore_interface = true; break; + case 'n': daemon_mode = false; ignore_daemon = true; break; + case 'd': daemon_mode = true; ignore_daemon = true; break; + case 'c': configfile = optarg; break; + case 'u': username = optarg; ignore_user = true; break; + case 'h': + case '?': + printf("Options: -h[elp], -?, -n[odaemon], -d[aemon], -p[ort] VAL, -i[nterface] VAL, -c[onfigfile] VAL, -u[sername] VAL\n"); + printf("Defaults:\n interface: 0.0.0.0\n port: %i\n daemon mode: true\n configfile: /etc/ddvtech.conf\n username: root\n", listen_port); + printf("Username root means no change to UID, no matter what the UID is.\n"); + printf("If the configfile exists, it is always loaded first. Commandline settings then overwrite the config file.\n"); + printf("\nThis process takes it directives from the %s section of the configfile.\n", confsection.c_str()); + exit(1); + break; + } + }//commandline options parser +} + +/// Parses the configuration file at configfile, if it exists. +/// Assumes confsection is set. +void Util::Config::parseFile(){ + std::ifstream conf(configfile.c_str(), std::ifstream::in); + std::string tmpstr; + bool acc_comm = false; + size_t foundeq; + if (conf.fail()){ + #if DEBUG >= 3 + fprintf(stderr, "Configuration file %s not found - using build-in defaults...\n", configfile.c_str()); + #endif + }else{ + while (conf.good()){ + getline(conf, tmpstr); + if (tmpstr[0] == '['){//new section? check if we care. + if (tmpstr == confsection){acc_comm = true;}else{acc_comm = false;} + }else{ + if (!acc_comm){break;}//skip all lines in this section if we do not care about it + foundeq = tmpstr.find('='); + if (foundeq != std::string::npos){ + if ((tmpstr.substr(0, foundeq) == "port") && !ignore_port){listen_port = atoi(tmpstr.substr(foundeq+1).c_str());} + if ((tmpstr.substr(0, foundeq) == "interface") && !ignore_interface){interface = tmpstr.substr(foundeq+1);} + if ((tmpstr.substr(0, foundeq) == "username") && !ignore_user){username = tmpstr.substr(foundeq+1);} + if ((tmpstr.substr(0, foundeq) == "daemon") && !ignore_daemon){daemon_mode = true;} + if ((tmpstr.substr(0, foundeq) == "nodaemon") && !ignore_daemon){daemon_mode = false;} + }//found equals sign + }//section contents + }//configfile line loop + }//configuration +} + +/// 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(){ + daemon(1, 0); +} diff --git a/util/util.h b/util/util.h index 4037a9ca..31c8c3aa 100644 --- a/util/util.h +++ b/util/util.h @@ -29,7 +29,7 @@ namespace Util{ }; /// Will set the active user to the named username. - static setUser(std::string user); + void setUser(std::string user); /// Deals with parsing configuration from files or commandline options. class Config{ @@ -39,6 +39,7 @@ namespace Util{ bool ignore_port; bool ignore_user; public: + std::string confsection; std::string configfile; bool daemon_mode; std::string interface; @@ -46,8 +47,9 @@ namespace Util{ std::string username; Config(); void parseArgs(int argc, char ** argv); + void parseFile(); }; - + /// Will turn the current process into a daemon. void Daemonize(); From 3f3d6c0222793164ec8cf4fc5a8d62266a2aaf6c Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 18 Oct 2011 09:10:19 +0200 Subject: [PATCH 157/788] Fixes --- util/http_parser.cpp | 5 ++++- util/socket.cpp | 31 +++++++++++++++++++++---------- util/util.cpp | 9 +++++++++ 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/util/http_parser.cpp b/util/http_parser.cpp index f46e5a2b..06bc713c 100644 --- a/util/http_parser.cpp +++ b/util/http_parser.cpp @@ -178,9 +178,12 @@ bool HTTP::Parser::parse(){ if (seenHeaders){ if (length > 0){ if (HTTPbuffer.length() >= length){ + if ((method != "HTTP/1.0") && (method != "HTTP/1.1")){ + body = HTTPbuffer.substr(0, length); + parseVars(body); //parse POST variables + } body = HTTPbuffer.substr(0, length); HTTPbuffer.erase(0, length); - parseVars(body); //parse POST variables return true; }else{ return false; diff --git a/util/socket.cpp b/util/socket.cpp index 5463e8de..522db714 100644 --- a/util/socket.cpp +++ b/util/socket.cpp @@ -29,7 +29,7 @@ Socket::Connection::Connection(){ /// Close connection. The internal socket is closed and then set to -1. void Socket::Connection::close(){ - #if DEBUG >= 4 + #if DEBUG >= 6 fprintf(stderr, "Socket closed.\n"); #endif shutdown(sock, SHUT_RDWR); @@ -81,14 +81,24 @@ Socket::Connection::Connection(std::string address, bool nonblock){ /// \param port String containing the port to connect to. /// \param nonblock Whether the socket should be nonblocking. Socket::Connection::Connection(std::string host, int port, bool nonblock){ - struct addrinfo *result, *rp; + struct addrinfo *result, *rp, hints; Error = false; Blocking = false; std::stringstream ss; ss << port; - if (getaddrinfo(host.c_str(), ss.str().c_str(), 0, &result) != 0){ + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_ADDRCONFIG; + hints.ai_protocol = 0; + hints.ai_canonname = NULL; + hints.ai_addr = NULL; + hints.ai_next = NULL; + int s = getaddrinfo(host.c_str(), ss.str().c_str(), &hints, &result); + if (s != 0){ #if DEBUG >= 1 - fprintf(stderr, "Could not connect to %s:%i! Error: %s\n", host.c_str(), port, strerror(errno)); + fprintf(stderr, "Could not connect to %s:%i! Error: %s\n", host.c_str(), port, gai_strerror(s)); #endif close(); return; @@ -96,10 +106,11 @@ Socket::Connection::Connection(std::string host, int port, bool nonblock){ for (rp = result; rp != NULL; rp = rp->ai_next) { sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); - if (sock == -1){continue;} - if (connect(sock, rp->ai_addr, rp->ai_addrlen) != -1){break;} + if (sock < 0){continue;} + if (connect(sock, rp->ai_addr, rp->ai_addrlen) == 0){break;} ::close(sock); } + freeaddrinfo(result); if (rp == 0){ #if DEBUG >= 1 @@ -534,18 +545,18 @@ Socket::Connection Socket::Server::accept(bool nonblock){ }else{ if (addrinfo.sin6_family == AF_INET6){ tmp.remotehost = inet_ntop(AF_INET6, &(addrinfo.sin6_addr), addrconv, INET6_ADDRSTRLEN); - #if DEBUG >= 4 + #if DEBUG >= 6 fprintf(stderr,"IPv6 addr: %s\n", tmp.remotehost.c_str()); #endif } if (addrinfo.sin6_family == AF_INET){ tmp.remotehost = inet_ntop(AF_INET, &(((sockaddr_in*)&addrinfo)->sin_addr), addrconv, INET6_ADDRSTRLEN); - #if DEBUG >= 4 + #if DEBUG >= 6 fprintf(stderr,"IPv4 addr: %s\n", tmp.remotehost.c_str()); #endif } if (addrinfo.sin6_family == AF_UNIX){ - #if DEBUG >= 4 + #if DEBUG >= 6 tmp.remotehost = ((sockaddr_un*)&addrinfo)->sun_path; fprintf(stderr,"Unix socket, no address\n"); #endif @@ -557,7 +568,7 @@ Socket::Connection Socket::Server::accept(bool nonblock){ /// Close connection. The internal socket is closed and then set to -1. void Socket::Server::close(){ - #if DEBUG >= 4 + #if DEBUG >= 6 fprintf(stderr, "ServerSocket closed.\n"); #endif shutdown(sock, SHUT_RDWR); diff --git a/util/util.cpp b/util/util.cpp index 687dba58..06a1ace1 100644 --- a/util/util.cpp +++ b/util/util.cpp @@ -5,7 +5,12 @@ #include #include #include + +#ifdef __FreeBSD__ +#include +#else #include +#endif #include #include #include @@ -52,6 +57,7 @@ void Util::Procs::childsig_handler(int signum){ #if DEBUG >= 1 if (isActive(pname)){ std::cerr << "Process " << pname << " half-terminated." << std::endl; + Stop(pname); }else{ std::cerr << "Process " << pname << " fully terminated." << std::endl; } @@ -190,8 +196,11 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2){ /// 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;} } } From 2468bd288b87e6a482d81a2e1423988749247bca Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 31 Oct 2011 15:23:46 +0100 Subject: [PATCH 158/788] Better automatic version numbering (now for all processes), added -v switch for version display to all processes. --- util/server_setup.cpp | 112 +++++------------------------------------- util/util.cpp | 15 ++++-- util/util.h | 3 ++ 3 files changed, 27 insertions(+), 103 deletions(-) diff --git a/util/server_setup.cpp b/util/server_setup.cpp index 34327442..75ac19fe 100644 --- a/util/server_setup.cpp +++ b/util/server_setup.cpp @@ -22,12 +22,11 @@ #endif #include "socket.h" //Socket library +#include "util.h" //utilities for process spawning and config management #include #include #include #include -#define defstr(x) #x ///< converts a define name to string -#define defstrh(x) "[" defstr(x) "]" ///< converts define name to [string] Socket::Server server_socket(-1); ///< Placeholder for the server socket /// Basic signal handler. Disconnects the server_socket if it receives @@ -81,115 +80,28 @@ int main(int argc, char ** argv){ sigaction(SIGTERM, &new_action, NULL); sigaction(SIGPIPE, &new_action, NULL); sigaction(SIGCHLD, &new_action, NULL); - - //default values - int listen_port = DEFAULT_PORT; - bool daemon_mode = true; - std::string interface = "0.0.0.0"; - std::string configfile = "/etc/ddvtech.conf"; - std::string username = "root"; - bool ignore_daemon = false; - bool ignore_interface = false; - bool ignore_port = false; - bool ignore_user = false; - - int opt = 0; - static const char *optString = "ndp:i:u:c:h?"; - static const struct option longOpts[] = { - {"help",0,0,'h'}, - {"port",1,0,'p'}, - {"interface",1,0,'i'}, - {"username",1,0,'u'}, - {"no-daemon",0,0,'n'}, - {"daemon",0,0,'d'}, - {"configfile",1,0,'c'} - }; - while ((opt = getopt_long(argc, argv, optString, longOpts, 0)) != -1){ - switch (opt){ - case 'p': listen_port = atoi(optarg); ignore_port = true; break; - case 'i': interface = optarg; ignore_interface = true; break; - case 'n': daemon_mode = false; ignore_daemon = true; break; - case 'd': daemon_mode = true; ignore_daemon = true; break; - case 'c': configfile = optarg; break; - case 'u': username = optarg; ignore_user = true; break; - case 'h': - case '?': - printf("Options: -h[elp], -?, -n[odaemon], -d[aemon], -p[ort] VAL, -i[nterface] VAL, -c[onfigfile] VAL, -u[sername] VAL\n"); - printf("Defaults:\n interface: 0.0.0.0\n port: %i\n daemon mode: true\n configfile: /etc/ddvtech.conf\n username: root\n", listen_port); - printf("Username root means no change to UID, no matter what the UID is.\n"); - printf("If the configfile exists, it is always loaded first. Commandline settings then overwrite the config file.\n"); - printf("\nThis process takes it directives from the %s section of the configfile.\n", defstrh(CONFIGSECT)); - return 1; - break; - } - }//commandline options parser - std::ifstream conf(configfile.c_str(), std::ifstream::in); - std::string tmpstr; - bool acc_comm = false; - size_t foundeq; - if (conf.fail()){ - #if DEBUG >= 3 - fprintf(stderr, "Configuration file %s not found - using build-in defaults...\n", configfile.c_str()); - #endif - }else{ - while (conf.good()){ - getline(conf, tmpstr); - if (tmpstr[0] == '['){//new section? check if we care. - if (tmpstr == defstrh(CONFIGSECT)){acc_comm = true;}else{acc_comm = false;} - }else{ - if (!acc_comm){break;}//skip all lines in this section if we do not care about it - foundeq = tmpstr.find('='); - if (foundeq != std::string::npos){ - if ((tmpstr.substr(0, foundeq) == "port") && !ignore_port){listen_port = atoi(tmpstr.substr(foundeq+1).c_str());} - if ((tmpstr.substr(0, foundeq) == "interface") && !ignore_interface){interface = tmpstr.substr(foundeq+1);} - if ((tmpstr.substr(0, foundeq) == "username") && !ignore_user){username = tmpstr.substr(foundeq+1);} - if ((tmpstr.substr(0, foundeq) == "daemon") && !ignore_daemon){daemon_mode = true;} - if ((tmpstr.substr(0, foundeq) == "nodaemon") && !ignore_daemon){daemon_mode = false;} - }//found equals sign - }//section contents - }//configfile line loop - }//configuration + //set and parse configuration + Util::Config C; + C.confsection = TOSTRING(CONFIGSECT); + C.listen_port = DEFAULT_PORT; + C.parseArgs(argc, argv); + C.parseFile(); //setup a new server socket, for the correct interface and port - server_socket = Socket::Server(listen_port, interface); + server_socket = Socket::Server(C.listen_port, C.interface); #if DEBUG >= 3 - fprintf(stderr, "Made a listening socket on %s:%i...\n", interface.c_str(), listen_port); + fprintf(stderr, "Made a listening socket on %s:%i...\n", C.interface.c_str(), C.listen_port); #endif - if (server_socket.connected()){ - //if setup success, enter daemon mode if requested - if (daemon_mode){ - #if DEBUG >= 3 - fprintf(stderr, "Going into background mode...\n"); - #endif - daemon(1, 0); - } - }else{ + if (!server_socket.connected()){ #if DEBUG >= 1 fprintf(stderr, "Error: could not make listening socket\n"); #endif return 1; } - if (username != "root"){ - struct passwd * user_info = getpwnam(username.c_str()); - if (!user_info){ - #if DEBUG >= 1 - fprintf(stderr, "Error: could not setuid %s: could not get PID\n", username.c_str()); - #endif - return 1; - }else{ - if (setuid(user_info->pw_uid) != 0){ - #if DEBUG >= 1 - fprintf(stderr, "Error: could not setuid %s: not allowed\n", username.c_str()); - #endif - }else{ - #if DEBUG >= 3 - fprintf(stderr, "Changed user to %s\n", username.c_str()); - #endif - } - } - } + Util::setUser(C.username); + if (C.daemon_mode){Util::Daemonize();} while (server_socket.connected()){ S = server_socket.accept(); diff --git a/util/util.cpp b/util/util.cpp index 06a1ace1..9ac0997e 100644 --- a/util/util.cpp +++ b/util/util.cpp @@ -276,7 +276,7 @@ Util::Config::Config(){ /// Assumes confsection is set. void Util::Config::parseArgs(int argc, char ** argv){ int opt = 0; - static const char *optString = "ndp:i:u:c:h?"; + static const char *optString = "ndvp:i:u:c:h?"; static const struct option longOpts[] = { {"help",0,0,'h'}, {"port",1,0,'p'}, @@ -284,7 +284,8 @@ void Util::Config::parseArgs(int argc, char ** argv){ {"username",1,0,'u'}, {"no-daemon",0,0,'n'}, {"daemon",0,0,'d'}, - {"configfile",1,0,'c'} + {"configfile",1,0,'c'}, + {"version",0,0,'v'} }; while ((opt = getopt_long(argc, argv, optString, longOpts, 0)) != -1){ switch (opt){ @@ -294,13 +295,18 @@ void Util::Config::parseArgs(int argc, char ** argv){ case 'd': daemon_mode = true; ignore_daemon = true; break; case 'c': configfile = optarg; break; case 'u': username = optarg; ignore_user = true; break; + case 'v': + printf("%s\n", TOSTRING(VERSION)); + exit(1); + break; case 'h': case '?': - printf("Options: -h[elp], -?, -n[odaemon], -d[aemon], -p[ort] VAL, -i[nterface] VAL, -c[onfigfile] VAL, -u[sername] VAL\n"); + printf("Options: -h[elp], -?, -v[ersion], -n[odaemon], -d[aemon], -p[ort] VAL, -i[nterface] VAL, -c[onfigfile] VAL, -u[sername] VAL\n"); printf("Defaults:\n interface: 0.0.0.0\n port: %i\n daemon mode: true\n configfile: /etc/ddvtech.conf\n username: root\n", listen_port); printf("Username root means no change to UID, no matter what the UID is.\n"); printf("If the configfile exists, it is always loaded first. Commandline settings then overwrite the config file.\n"); printf("\nThis process takes it directives from the %s section of the configfile.\n", confsection.c_str()); + printf("This is %s version %s\n", argv[0], TOSTRING(VERSION)); exit(1); break; } @@ -343,5 +349,8 @@ void Util::Config::parseFile(){ /// Does not change directory to root. /// Does redirect output to /dev/null void Util::Daemonize(){ + #if DEBUG >= 3 + fprintf(stderr, "Going into background mode...\n"); + #endif daemon(1, 0); } diff --git a/util/util.h b/util/util.h index 31c8c3aa..a92b67f0 100644 --- a/util/util.h +++ b/util/util.h @@ -5,6 +5,9 @@ #include #include +#define STRINGIFY(x) #x +#define TOSTRING(x) STRINGIFY(x) + /// Contains utility code, not directly related to streaming media namespace Util{ From 2b65a165657a3b68f3971becebbbaaa92acf1b3f Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 31 Oct 2011 15:53:13 +0100 Subject: [PATCH 159/788] Fixed HTTP util parsing of variables. --- util/http_parser.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/util/http_parser.cpp b/util/http_parser.cpp index 06bc713c..2e7f7239 100644 --- a/util/http_parser.cpp +++ b/util/http_parser.cpp @@ -38,8 +38,7 @@ std::string HTTP::Parser::BuildRequest(){ for (it=headers.begin(); it != headers.end(); it++){ tmp += (*it).first + ": " + (*it).second + "\n"; } - tmp += "\n"; - tmp += body; + tmp += "\n" + body + "\n"; return tmp; } @@ -178,11 +177,8 @@ bool HTTP::Parser::parse(){ if (seenHeaders){ if (length > 0){ if (HTTPbuffer.length() >= length){ - if ((method != "HTTP/1.0") && (method != "HTTP/1.1")){ - body = HTTPbuffer.substr(0, length); - parseVars(body); //parse POST variables - } body = HTTPbuffer.substr(0, length); + parseVars(body); //parse POST variables HTTPbuffer.erase(0, length); return true; }else{ From 1301b7a9195571507264dd4c0a0cbd5df6f938ec Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 31 Oct 2011 18:24:01 +0100 Subject: [PATCH 160/788] Fixes config util for nonsection applications, attempt to fix DDV_Controller process spawning. --- util/util.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/util/util.cpp b/util/util.cpp index 9ac0997e..d599c0d0 100644 --- a/util/util.cpp +++ b/util/util.cpp @@ -273,7 +273,8 @@ Util::Config::Config(){ /// Parses commandline arguments. /// Calls exit if an unknown option is encountered, printing a help message. -/// Assumes confsection is set. +/// confsection must be either already set or never be set at all when this function is called. +/// In other words: do not change confsection after calling this function. void Util::Config::parseArgs(int argc, char ** argv){ int opt = 0; static const char *optString = "ndvp:i:u:c:h?"; @@ -301,11 +302,18 @@ void Util::Config::parseArgs(int argc, char ** argv){ break; case 'h': case '?': - printf("Options: -h[elp], -?, -v[ersion], -n[odaemon], -d[aemon], -p[ort] VAL, -i[nterface] VAL, -c[onfigfile] VAL, -u[sername] VAL\n"); - printf("Defaults:\n interface: 0.0.0.0\n port: %i\n daemon mode: true\n configfile: /etc/ddvtech.conf\n username: root\n", listen_port); - printf("Username root means no change to UID, no matter what the UID is.\n"); - printf("If the configfile exists, it is always loaded first. Commandline settings then overwrite the config file.\n"); - printf("\nThis process takes it directives from the %s section of the configfile.\n", confsection.c_str()); + std::string doingdaemon = "true"; + if (!daemon_mode){doingdaemon = "false";} + if (confsection == ""){ + printf("Options: -h[elp], -?, -v[ersion], -n[odaemon], -d[aemon], -p[ort] VAL, -i[nterface] VAL, -u[sername] VAL\n"); + printf("Defaults:\n interface: %s\n port: %i\n daemon mode: %s\n username: %s\n", interface.c_str(), listen_port, doingdaemon.c_str(), username.c_str()); + }else{ + printf("Options: -h[elp], -?, -v[ersion], -n[odaemon], -d[aemon], -p[ort] VAL, -i[nterface] VAL, -c[onfigfile] VAL, -u[sername] VAL\n"); + printf("Defaults:\n interface: %s\n port: %i\n daemon mode: %s\n configfile: %s\n username: %s\n", interface.c_str(), listen_port, doingdaemon.c_str(), configfile.c_str(), username.c_str()); + printf("Username root means no change to UID, no matter what the UID is.\n"); + printf("If the configfile exists, it is always loaded first. Commandline settings then overwrite the config file.\n"); + printf("\nThis process takes it directives from the %s section of the configfile.\n", confsection.c_str()); + } printf("This is %s version %s\n", argv[0], TOSTRING(VERSION)); exit(1); break; From f9dcedb0e1f72e474995e63bd06cc822fd195ec7 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 31 Oct 2011 19:48:29 +0100 Subject: [PATCH 161/788] Re-fix for HTTP lib, fixed typo in Controller. --- util/http_parser.cpp | 5 ++--- util/http_parser.h | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/util/http_parser.cpp b/util/http_parser.cpp index 2e7f7239..d2b2ac89 100644 --- a/util/http_parser.cpp +++ b/util/http_parser.cpp @@ -158,8 +158,7 @@ bool HTTP::Parser::parse(){ f = tmpA.find(' '); if (f != std::string::npos){protocol = tmpA.substr(0, f); tmpA.erase(0, f+1);} if (url.find('?') != std::string::npos){ - std::string queryvars = url.substr(url.find('?')+1); - parseVars(queryvars); //parse GET variables + parseVars(url.substr(url.find('?')+1)); //parse GET variables } }else{ if (tmpA.size() == 0){ @@ -204,7 +203,7 @@ void HTTP::Parser::SendResponse(Socket::Connection & conn, std::string code, std /// Parses GET or POST-style variable data. /// Saves to internal variable structure using HTTP::Parser::SetVar. -void HTTP::Parser::parseVars(std::string & data){ +void HTTP::Parser::parseVars(std::string data){ std::string varname; std::string varval; while (data.find('=') != std::string::npos){ diff --git a/util/http_parser.h b/util/http_parser.h index 6172ac00..124b9686 100644 --- a/util/http_parser.h +++ b/util/http_parser.h @@ -41,7 +41,7 @@ namespace HTTP{ bool seenHeaders; bool seenReq; bool parse(); - void parseVars(std::string & data); + void parseVars(std::string data); std::string HTTPbuffer; std::map headers; std::map vars; From 0fb103a3b43d3796b7d044a6b6b2fe813f22ecee Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sat, 26 Nov 2011 02:31:56 +0100 Subject: [PATCH 162/788] Added byte counters to sockets, needed for issue #1 --- util/socket.cpp | 31 +++++++++++++++++++++++++++++-- util/socket.h | 8 ++++++-- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/util/socket.cpp b/util/socket.cpp index 522db714..6482b0a3 100644 --- a/util/socket.cpp +++ b/util/socket.cpp @@ -15,6 +15,8 @@ /// \param sockNo Integer representing the socket to convert. Socket::Connection::Connection(int sockNo){ sock = sockNo; + up = 0; + down = 0; Error = false; Blocking = false; }//Socket::Connection basic constructor @@ -23,6 +25,8 @@ Socket::Connection::Connection(int sockNo){ /// A socket created like this is always disconnected and should/could be overwritten at some point. Socket::Connection::Connection(){ sock = -1; + up = 0; + down = 0; Error = false; Blocking = false; }//Socket::Connection basic constructor @@ -58,6 +62,8 @@ Socket::Connection::Connection(std::string address, bool nonblock){ } Error = false; Blocking = false; + up = 0; + down = 0; sockaddr_un addr; addr.sun_family = AF_UNIX; strncpy(addr.sun_path, address.c_str(), address.size()+1); @@ -84,6 +90,8 @@ Socket::Connection::Connection(std::string host, int port, bool nonblock){ struct addrinfo *result, *rp, hints; Error = false; Blocking = false; + up = 0; + down = 0; std::stringstream ss; ss << port; @@ -187,6 +195,16 @@ bool Socket::Connection::connected(){ return (sock >= 0); } +/// Returns total amount of bytes sent. +unsigned int Socket::Connection::dataUp(){ + return up; +} + +/// Returns total amount of bytes received. +unsigned int Socket::Connection::dataDown(){ + return down; +} + /// Writes data to socket. This function blocks if the socket is blocking and all data cannot be written right away. /// If the socket is nonblocking and not all data can be written, this function sets internal variable Blocking to true /// and returns false. @@ -204,15 +222,16 @@ bool Socket::Connection::write(const void * buffer, int len){ fprintf(stderr, "Could not write data! Error: %s\n", strerror(errno)); #endif close(); + up += sofar; return false; }else{ sofar += r; } } + up += sofar; return true; }//DDv::Socket::write - /// Reads data from socket. This function blocks if the socket is blocking and all data cannot be read right away. /// If the socket is nonblocking and not all data can be read, this function sets internal variable Blocking to true /// and returns false. @@ -226,13 +245,17 @@ bool Socket::Connection::read(void * buffer, int len){ int r = recv(sock, (char*)buffer + sofar, len-sofar, 0); if (r < 0){ switch (errno){ - case EWOULDBLOCK: return 0; break; + case EWOULDBLOCK: + down += sofar; + return 0; + break; default: Error = true; #if DEBUG >= 2 fprintf(stderr, "Could not read data! Error %i: %s\n", r, strerror(errno)); #endif close(); + down += sofar; break; } return false; @@ -243,11 +266,13 @@ bool Socket::Connection::read(void * buffer, int len){ fprintf(stderr, "Could not read data! Socket is closed.\n"); #endif close(); + down += sofar; return false; } sofar += r; } } + down += sofar; return true; }//Socket::Connection::read @@ -285,6 +310,7 @@ int Socket::Connection::iwrite(void * buffer, int len){ #endif close(); } + up += r; return r; }//Socket::Connection::iwrite @@ -315,6 +341,7 @@ int Socket::Connection::iread(void * buffer, int len){ #endif close(); } + down += r; return r; }//Socket::Connection::iread diff --git a/util/socket.h b/util/socket.h index 5c4570c3..95c28d82 100644 --- a/util/socket.h +++ b/util/socket.h @@ -23,6 +23,8 @@ namespace Socket{ private: int sock; ///< Internally saved socket number. std::string remotehost; ///< Stores remote host address. + unsigned int up; + unsigned int down; public: Connection(); ///< Create a new disconnected base socket. Connection(int sockNo); ///< Create a new base socket. @@ -30,8 +32,6 @@ namespace Socket{ Connection(std::string adres, bool nonblock = false); ///< Create a new Unix Socket. bool canRead(); ///< Calls poll() on the socket, checking if data is available. bool canWrite(); ///< Calls poll() on the socket, checking if data can be written. - 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. signed int ready(); ///< Returns the ready-state for this socket. bool connected(); ///< Returns the connected-state for this socket. bool read(void * buffer, int len); ///< Reads data from socket. @@ -49,7 +49,11 @@ namespace Socket{ std::string getHost(); ///< Gets hostname for connection, if available. int getSocket(); ///< Returns internal socket number. std::string getError(); ///< Returns a string describing the last error that occured. + unsigned int dataUp(); ///< Returns total amount of bytes sent. + unsigned int dataDown(); ///< Returns total amount of bytes received. 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. }; /// This class is for easily setting up listening socket, either TCP or Unix. From ff4e2aa285a8489a34e0c92df58cff676260f121 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sat, 26 Nov 2011 03:16:28 +0100 Subject: [PATCH 163/788] Implemented stats logging on the connector level - closes #1 --- util/socket.cpp | 15 +++++++++++++++ util/socket.h | 2 ++ 2 files changed, 17 insertions(+) diff --git a/util/socket.cpp b/util/socket.cpp index 6482b0a3..c7379470 100644 --- a/util/socket.cpp +++ b/util/socket.cpp @@ -11,12 +11,19 @@ #include #endif +std::string uint2string(unsigned int i){ + std::stringstream st; + st << i; + return st.str(); +} + /// Create a new base socket. This is a basic constructor for converting any valid socket to a Socket::Connection. /// \param sockNo Integer representing the socket to convert. Socket::Connection::Connection(int sockNo){ sock = sockNo; up = 0; down = 0; + conntime = time(0); Error = false; Blocking = false; }//Socket::Connection basic constructor @@ -27,6 +34,7 @@ Socket::Connection::Connection(){ sock = -1; up = 0; down = 0; + conntime = time(0); Error = false; Blocking = false; }//Socket::Connection basic constructor @@ -64,6 +72,7 @@ Socket::Connection::Connection(std::string address, bool nonblock){ Blocking = false; up = 0; down = 0; + conntime = time(0); sockaddr_un addr; addr.sun_family = AF_UNIX; strncpy(addr.sun_path, address.c_str(), address.size()+1); @@ -92,6 +101,7 @@ Socket::Connection::Connection(std::string host, int port, bool nonblock){ Blocking = false; up = 0; down = 0; + conntime = time(0); std::stringstream ss; ss << port; @@ -205,6 +215,11 @@ unsigned int Socket::Connection::dataDown(){ return down; } +/// Returns a std::string of stats, ended by a newline. +std::string Socket::Connection::getStats(){ + return getHost() + uint2string(time(0) - conntime) + " " + uint2string(up) + uint2string(down) + "\n"; +} + /// Writes data to socket. This function blocks if the socket is blocking and all data cannot be written right away. /// If the socket is nonblocking and not all data can be written, this function sets internal variable Blocking to true /// and returns false. diff --git a/util/socket.h b/util/socket.h index 95c28d82..b1238abc 100644 --- a/util/socket.h +++ b/util/socket.h @@ -25,6 +25,7 @@ namespace Socket{ std::string remotehost; ///< Stores remote host address. unsigned int up; unsigned int down; + unsigned int conntime; public: Connection(); ///< Create a new disconnected base socket. Connection(int sockNo); ///< Create a new base socket. @@ -51,6 +52,7 @@ namespace Socket{ std::string getError(); ///< Returns a string describing the last error that occured. unsigned int dataUp(); ///< Returns total amount of bytes sent. unsigned int dataDown(); ///< Returns total amount of bytes received. + std::string getStats(); ///< 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. From 2f5d5fcecb5e29e487c0a8e0099cbbf09ef15335 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sat, 26 Nov 2011 04:27:04 +0100 Subject: [PATCH 164/788] Summerizing of stats in Buffer done - closes #2 --- util/socket.cpp | 5 +++-- util/socket.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/util/socket.cpp b/util/socket.cpp index c7379470..29b02cd1 100644 --- a/util/socket.cpp +++ b/util/socket.cpp @@ -216,8 +216,9 @@ unsigned int Socket::Connection::dataDown(){ } /// Returns a std::string of stats, ended by a newline. -std::string Socket::Connection::getStats(){ - return getHost() + uint2string(time(0) - conntime) + " " + uint2string(up) + uint2string(down) + "\n"; +/// Requires the current connector name as an argument. +std::string Socket::Connection::getStats(std::string C){ + return getHost() + " " + C + " " + uint2string(time(0) - conntime) + " " + uint2string(up) + " " + uint2string(down) + "\n"; } /// Writes data to socket. This function blocks if the socket is blocking and all data cannot be written right away. diff --git a/util/socket.h b/util/socket.h index b1238abc..a7a16dc9 100644 --- a/util/socket.h +++ b/util/socket.h @@ -52,7 +52,7 @@ namespace Socket{ std::string getError(); ///< Returns a string describing the last error that occured. unsigned int dataUp(); ///< Returns total amount of bytes sent. unsigned int dataDown(); ///< Returns total amount of bytes received. - std::string getStats(); ///< Returns a std::string of stats, ended by a newline. + 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. From 67c92852b590e9e204aa796e97b0fd97782d29cd Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 27 Nov 2011 02:22:04 +0100 Subject: [PATCH 165/788] Updated sockets lib with buffers for easy sending/receiving and such. --- util/socket.cpp | 16 ++++++++++++++++ util/socket.h | 5 +++++ 2 files changed, 21 insertions(+) diff --git a/util/socket.cpp b/util/socket.cpp index 29b02cd1..4fdaceec 100644 --- a/util/socket.cpp +++ b/util/socket.cpp @@ -221,6 +221,22 @@ std::string Socket::Connection::getStats(std::string C){ return getHost() + " " + C + " " + uint2string(time(0) - conntime) + " " + uint2string(up) + " " + uint2string(down) + "\n"; } +/// Updates the downbuffer and upbuffer internal variables. +void Socket::Connection::spool(){ + iread(downbuffer); + iwrite(upbuffer); +} + +/// Returns a reference to the download buffer. +std::string & Socket::Connection::Received(){ + return downbuffer; +} + +/// Appends data to the upbuffer. +void Socket::Connection::Send(std::string data){ + upbuffer.append(data); +} + /// Writes data to socket. This function blocks if the socket is blocking and all data cannot be written right away. /// If the socket is nonblocking and not all data can be written, this function sets internal variable Blocking to true /// and returns false. diff --git a/util/socket.h b/util/socket.h index a7a16dc9..bfe824e5 100644 --- a/util/socket.h +++ b/util/socket.h @@ -26,6 +26,8 @@ namespace Socket{ unsigned int up; unsigned int down; unsigned int conntime; + std::string downbuffer; ///< Stores temporary data coming in. + std::string upbuffer; ///< Stores temporary data going out. public: Connection(); ///< Create a new disconnected base socket. Connection(int sockNo); ///< Create a new base socket. @@ -46,6 +48,9 @@ namespace Socket{ bool swrite(std::string & buffer); ///< Read call that is compatible with std::string. bool iread(std::string & buffer); ///< Incremental write call that is compatible with std::string. bool iwrite(std::string & buffer); ///< Write call that is compatible with std::string. + void spool(); ///< Updates the downbuffer and upbuffer internal variables. + std::string & Received(); ///< Returns a reference to the download buffer. + void Send(std::string data); ///< Appends data to the upbuffer. void close(); ///< Close connection. std::string getHost(); ///< Gets hostname for connection, if available. int getSocket(); ///< Returns internal socket number. From 67bf3b8320ce1ac3e68ba991f70f509e5da52efc Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 13 Sep 2011 00:32:35 +0200 Subject: [PATCH 166/788] First compiling version of DTSC-powered Buffer. Merged DTSC and DTMI into DTSC for ease of use. Todo: FLV2DTSC executable, DTSC support in all connectors... --- util/dtmi.cpp | 248 ------------------------------- util/dtmi.h | 58 -------- util/dtsc.cpp | 388 ++++++++++++++++++++++++++++++++++++++++++++++-- util/dtsc.h | 111 +++++++++++--- util/socket.cpp | 8 +- util/socket.h | 8 +- 6 files changed, 476 insertions(+), 345 deletions(-) delete mode 100644 util/dtmi.cpp delete mode 100644 util/dtmi.h diff --git a/util/dtmi.cpp b/util/dtmi.cpp deleted file mode 100644 index a9d38299..00000000 --- a/util/dtmi.cpp +++ /dev/null @@ -1,248 +0,0 @@ -/// \file dtmi.cpp -/// Holds all code for DDVTECH MediaInfo parsing/generation. - -#include "dtmi.h" -#include //needed for stderr only - -/// Returns the std::string Indice for the current object, if available. -/// Returns an empty string if no indice exists. -std::string DTSC::DTMI::Indice(){return myIndice;}; - -/// Returns the DTSC::DTMItype AMF0 object type for this object. -DTSC::DTMItype DTSC::DTMI::GetType(){return myType;}; - -/// Returns the numeric value of this object, if available. -/// If this object holds no numeric value, 0 is returned. -uint64_t DTSC::DTMI::NumValue(){return numval;}; - -/// Returns the std::string value of this object, if available. -/// If this object holds no string value, an empty string is returned. -std::string DTSC::DTMI::StrValue(){return strval;}; - -/// Returns the C-string value of this object, if available. -/// If this object holds no string value, an empty C-string is returned. -const char * DTSC::DTMI::Str(){return strval.c_str();}; - -/// Returns a count of the amount of objects this object currently holds. -/// If this object is not a container type, this function will always return 0. -int DTSC::DTMI::hasContent(){return contents.size();}; - -/// Adds an DTSC::DTMI to this object. Works for all types, but only makes sense for container types. -void DTSC::DTMI::addContent(DTSC::DTMI c){contents.push_back(c);}; - -/// Returns a pointer to the object held at indice i. -/// Returns AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice. -/// \param i The indice of the object in this container. -DTSC::DTMI* DTSC::DTMI::getContentP(int i){return &contents.at(i);}; - -/// Returns a copy of the object held at indice i. -/// Returns a AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice. -/// \param i The indice of the object in this container. -DTSC::DTMI DTSC::DTMI::getContent(int i){return contents.at(i);}; - -/// Returns a pointer to the object held at indice s. -/// Returns NULL if no object is held at this indice. -/// \param s The indice of the object in this container. -DTSC::DTMI* DTSC::DTMI::getContentP(std::string s){ - for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ - if (it->Indice() == s){return &(*it);} - } - return 0; -}; - -/// Returns a copy of the object held at indice s. -/// Returns a AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice. -/// \param s The indice of the object in this container. -DTSC::DTMI DTSC::DTMI::getContent(std::string s){ - for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ - if (it->Indice() == s){return *it;} - } - return DTSC::DTMI("error", DTMI::DTMI_ROOT); -}; - -/// Default constructor. -/// Simply fills the data with DTSC::DTMI("error", AMF0_DDV_CONTAINER) -DTSC::DTMI::DTMI(){ - *this = DTSC::DTMI("error", DTMI::DTMI_ROOT); -};//default constructor - -/// Constructor for numeric objects. -/// The object type is by default AMF::AMF0_NUMBER, but this can be forced to a different value. -/// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. -/// \param val The numeric value of this object. Numeric AMF0 objects only support double-type values. -/// \param setType The object type to force this object to. -DTSC::DTMI::DTMI(std::string indice, double val, DTSC::DTMItype setType){//num type initializer - myIndice = indice; - myType = setType; - strval = ""; - numval = val; -}; - -/// Constructor for string objects. -/// The object type is by default AMF::AMF0_STRING, but this can be forced to a different value. -/// There is no need to manually change the type to AMF::AMF0_LONGSTRING, this will be done automatically. -/// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. -/// \param val The string value of this object. -/// \param setType The object type to force this object to. -DTSC::DTMI::DTMI(std::string indice, std::string val, DTSC::DTMItype setType){//str type initializer - myIndice = indice; - myType = setType; - strval = val; - numval = 0; -}; - -/// Constructor for container objects. -/// The object type is by default AMF::AMF0_OBJECT, but this can be forced to a different value. -/// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. -/// \param setType The object type to force this object to. -DTSC::DTMI::DTMI(std::string indice, DTSC::DTMItype setType){//object type initializer - myIndice = indice; - myType = setType; - strval = ""; - numval = 0; -}; - -/// Prints the contents of this object to std::cerr. -/// If this object contains other objects, it will call itself recursively -/// and print all nested content in a nice human-readable format. -void DTSC::DTMI::Print(std::string indent){ - std::cerr << indent; - // print my type - switch (myType){ - case DTMItype::DTMI_INT: std::cerr << "Integer"; break; - case DTMItype::DTMI_STRING: std::cerr << "String"; break; - case DTMItype::DTMI_OBJECT: std::cerr << "Object"; break; - case DTMItype::DTMI_OBJ_END: std::cerr << "Object end"; break; - case DTMItype::DTMI_ROOT: std::cerr << "Root Node"; break; - } - // print my string indice, if available - std::cerr << " " << myIndice << " "; - // print my numeric or string contents - switch (myType){ - case DTMItype::DTMI_INT: std::cerr << numval; break; - case DTMItype::DTMI_STRING: std::cerr << strval; break; - default: break;//we don't care about the rest, and don't want a compiler warning... - } - std::cerr << std::endl; - // if I hold other objects, print those too, recursively. - if (contents.size() > 0){ - for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){it->Print(indent+" ");} - } -};//print - -/// Packs the AMF object to a std::string for transfer over the network. -/// If the object is a container type, this function will call itself recursively and contain all contents. -std::string DTSC::DTMI::Pack(){ - std::string r = ""; - //skip output of DDV container types, they do not exist. Only output their contents. - if (myType != DTMItype::DTMI_ROOT){r += myType;} - //output the properly formatted data stream for this object's contents. - switch (myType){ - case DTMItype::DTMI_INT: - r += *(((char*)&numval)+7); r += *(((char*)&numval)+6); - r += *(((char*)&numval)+5); r += *(((char*)&numval)+4); - r += *(((char*)&numval)+3); r += *(((char*)&numval)+2); - r += *(((char*)&numval)+1); r += *(((char*)&numval)); - break; - case DTMItype::DTMI_STRING: - r += strval.size() / (256*256*256); - r += strval.size() / (256*256); - r += strval.size() / 256; - r += strval.size() % 256; - r += strval; - break; - case DTMItype::DTMI_OBJECT: - if (contents.size() > 0){ - for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ - r += it->Indice().size() / 256; - r += it->Indice().size() % 256; - r += it->Indice(); - r += it->Pack(); - } - } - r += (char)0; r += (char)0; r += (char)9; - break; - case DTMItype::DTMI_ROOT://only send contents - if (contents.size() > 0){ - for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ - r += it->Pack(); - } - } - break; - } - return r; -};//pack - -/// Parses a single AMF0 type - used recursively by the AMF::parse() functions. -/// This function updates i every call with the new position in the data. -/// \param data The raw data to parse. -/// \param len The size of the raw data. -/// \param i Current parsing position in the raw data. -/// \param name Indice name for any new object created. -/// \returns A single DTSC::DTMI, parsed from the raw data. -DTSC::DTMI DTSC::parseOneDTMI(const unsigned char *& data, unsigned int &len, unsigned int &i, std::string name){ - std::string tmpstr; - unsigned int tmpi = 0; - unsigned char tmpdbl[8]; - #if DEBUG >= 10 - fprintf(stderr, "Note: AMF type %hhx found. %i bytes left\n", data[i], len-i); - #endif - switch (data[i]){ - case DTMI::DTMI_INT: - tmpdbl[7] = data[i+1]; - tmpdbl[6] = data[i+2]; - tmpdbl[5] = data[i+3]; - tmpdbl[4] = data[i+4]; - tmpdbl[3] = data[i+5]; - tmpdbl[2] = data[i+6]; - tmpdbl[1] = data[i+7]; - tmpdbl[0] = data[i+8]; - i+=9;//skip 8(a double)+1 forwards - return DTSC::DTMI(name, *(uint64_t*)tmpdbl, AMF::AMF0_NUMBER); - break; - case DTMI::DTMI_STRING: - tmpi = data[i+1]*256*256*256+data[i+2]*256*256+data[i+3]*256+data[i+4];//set tmpi to UTF-8-long length - tmpstr.clear();//clean tmpstr, just to be sure - tmpstr.append((const char *)data+i+5, (size_t)tmpi);//add the string data - i += tmpi + 5;//skip length+size+1 forwards - return DTSC::DTMI(name, tmpstr, AMF::AMF0_LONGSTRING); - break; - case DTMI::DTMI_OBJECT:{ - ++i; - DTSC::DTMI ret(name, DTMI::DTMI_OBJECT); - while (data[i] + data[i+1] != 0){//while not encountering 0x0000 (we assume 0x000009) - tmpi = data[i]*256+data[i+1];//set tmpi to the UTF-8 length - tmpstr.clear();//clean tmpstr, just to be sure - tmpstr.append((const char*)data+i+2, (size_t)tmpi);//add the string data - i += tmpi + 2;//skip length+size forwards - ret.addContent(AMF::parseOne(data, len, i, tmpstr));//add content, recursively parsed, updating i, setting indice to tmpstr - } - i += 3;//skip 0x000009 - return ret; - } break; - } - #if DEBUG >= 2 - fprintf(stderr, "Error: Unimplemented DTMI type %hhx - returning.\n", data[i]); - #endif - return DTSC::DTMI("error", DTMI::DTMI_ROOT); -}//parseOne - -/// Parses a C-string to a valid DTSC::DTMI. -/// This function will find all AMF objects in the string and return -/// them all packed in a single AMF::AMF0_DDV_CONTAINER DTSC::DTMI. -DTSC::DTMI DTSC::parseDTMI(const unsigned char * data, unsigned int len){ - DTSC::DTMI ret("returned", DTMI::DTMI_ROOT);//container type - unsigned int i = 0, j = 0; - while (i < len){ - ret.addContent(AMF::parseOne(data, len, i, "")); - if (i > j){j = i;}else{return ret;} - } - return ret; -}//parse - -/// Parses a std::string to a valid DTSC::DTMI. -/// This function will find all AMF objects in the string and return -/// them all packed in a single AMF::AMF0_DDV_CONTAINER DTSC::DTMI. -DTSC::DTMI DTSC::parseDTMI(std::string data){ - return parse((const unsigned char*)data.c_str(), data.size()); -}//parse diff --git a/util/dtmi.h b/util/dtmi.h deleted file mode 100644 index 410df97d..00000000 --- a/util/dtmi.h +++ /dev/null @@ -1,58 +0,0 @@ -/// \file dtmi.h -/// Holds all headers for DDVTECH MediaInfo parsing/generation. - -#pragma once -#include -#include -//#include -#include - -/// Holds all DDVTECH Stream Container classes and parsers. -namespace DTSC{ - - /// Enumerates all possible DTMI types. - enum DTMItype { - DTMI_INT = 0x01, ///< Unsigned 64-bit integer. - DTMI_STRING = 0x02, ///< String, equivalent to the AMF longstring type. - DTMI_OBJECT = 0xE0, ///< Object, equivalent to the AMF object type. - DTMI_OBJ_END = 0xEE, ///< End of object marker. - DTMI_ROOT = 0xFF ///< Root node for all DTMI data. - }; - - /// Recursive class that holds DDVTECH MediaInfo. - class DTMI { - public: - std::string Indice(); - DTMItype GetType(); - uint64_t NumValue(); - std::string StrValue(); - const char * Str(); - int hasContent(); - void addContent(DTMI c); - DTMI* getContentP(int i); - DTMI getContent(int i); - DTMI* getContentP(std::string s); - DTMI getContent(std::string s); - DTMI(); - DTMI(std::string indice, double val, DTMItype setType = DTMI_INT); - DTMI(std::string indice, std::string val, DTMItype setType = DTMI_STRING); - DTMI(std::string indice, DTMItype setType = DTMI_OBJECT); - void Print(std::string indent = ""); - std::string Pack(); - protected: - std::string myIndice; ///< Holds this objects indice, if any. - DTMItype myType; ///< Holds this objects AMF0 type. - std::string strval; ///< Holds this objects string value, if any. - uint64_t numval; ///< Holds this objects numeric value, if any. - std::vector contents; ///< Holds this objects contents, if any (for container types). - };//AMFType - - /// Parses a C-string to a valid DTSC::DTMI. - DTMI parseDTMI(const unsigned char * data, unsigned int len); - /// Parses a std::string to a valid DTSC::DTMI. - DTMI parseDTMI(std::string data); - /// Parses a single DTMI type - used recursively by the DTSC::parseDTMI() functions. - DTMI parseOneDTMI(const unsigned char *& data, unsigned int &len, unsigned int &i, std::string name); - -};//DTSC namespace - diff --git a/util/dtsc.cpp b/util/dtsc.cpp index bc722ecf..9d046287 100644 --- a/util/dtsc.cpp +++ b/util/dtsc.cpp @@ -2,33 +2,50 @@ /// Holds all code for DDVTECH Stream Container parsing/generation. #include "dtsc.h" -#include "string.h" //for memcmp -#include "arpa/inet.h" //for htonl/ntohl +#include //for memcmp +#include //for htonl/ntohl +#include //for fprint, stderr -char * DTSC::Magic_Header = "DTSC"; -char * DTSC::Magic_Packet = "DTPD"; +char DTSC::Magic_Header[] = "DTSC"; +char DTSC::Magic_Packet[] = "DTPD"; +/// Initializes a DTSC::Stream with only one packet buffer. DTSC::Stream::Stream(){ datapointer = 0; + buffercount = 1; } +/// Initializes a DTSC::Stream with a minimum of rbuffers packet buffers. +/// The actual buffer count may not at all times be the requested amount. +DTSC::Stream::Stream(unsigned int rbuffers){ + datapointer = 0; + if (rbuffers < 1){rbuffers = 1;} + buffercount = rbuffers; +} + +/// Attempts to parse a packet from the given std::string buffer. +/// Returns true if successful, removing the parsed part from the buffer string. +/// Returns false if invalid or not enough data is in the buffer. +/// \arg buffer The std::string buffer to attempt to parse. bool DTSC::Stream::parsePacket(std::string & buffer){ uint32_t len; if (buffer.length() > 8){ if (memcmp(buffer.c_str(), DTSC::Magic_Header, 4) == 0){ len = ntohl(((uint32_t *)buffer.c_str())[1]); if (buffer.length() < len+8){return false;} - metadata = DTSC::parseDTMI(buffer.c_str() + 8, len); + metadata = DTSC::parseDTMI((unsigned char*)buffer.c_str() + 8, len); + buffer.erase(0, len+8); } if (memcmp(buffer.c_str(), DTSC::Magic_Packet, 4) == 0){ len = ntohl(((uint32_t *)buffer.c_str())[1]); if (buffer.length() < len+8){return false;} - lastPacket = DTSC::parseDTMI(buffer.c_str() + 8, len); + buffers.push_front(DTSC::DTMI("empty", DTMI_ROOT)); + buffers.front() = DTSC::parseDTMI((unsigned char*)buffer.c_str() + 8, len); datapointertype = INVALID; - if (lastPacket.getContentP("data")){ - datapointer = lastPacket.getContentP("data")->StrValue.c_str(); - if (lastPacket.getContentP("datatype")){ - std::string tmp = lastPacket.getContentP("datatype")->StrValue(); + if (buffers.front().getContentP("data")){ + datapointer = buffers.front().getContentP("data")->StrValue().c_str(); + if (buffers.front().getContentP("datatype")){ + std::string tmp = buffers.front().getContentP("datatype")->StrValue(); if (tmp == "video"){datapointertype = VIDEO;} if (tmp == "audio"){datapointertype = AUDIO;} if (tmp == "meta"){datapointertype = META;} @@ -36,23 +53,372 @@ bool DTSC::Stream::parsePacket(std::string & buffer){ }else{ datapointer = 0; } + buffer.erase(0, len+8); + while (buffers.size() > buffercount){buffers.pop_back();} + advanceRings(); } + #if DEBUG >= 2 + std::cerr << "Error: Invalid DTMI data! I *will* get stuck!" << std::endl; + #endif } return false; } -char * DTSC::Stream::lastData(){ +/// Returns a direct pointer to the data attribute of the last received packet, if available. +/// Returns NULL if no valid pointer or packet is available. +const char * DTSC::Stream::lastData(){ return datapointer; } +/// Returns the packed in this buffer number. +/// \arg num Buffer number. +DTSC::DTMI & DTSC::Stream::getPacket(unsigned int num){ + return buffers[num]; +} + +/// Returns the type of the last received packet. DTSC::datatype DTSC::Stream::lastType(){ return datapointertype; } +/// Returns true if the current stream contains at least one video track. bool DTSC::Stream::hasVideo(){ return (metadata.getContentP("video") != 0); } +/// Returns true if the current stream contains at least one audio track. bool DTSC::Stream::hasAudio(){ return (metadata.getContentP("audio") != 0); } + +/// Returns a packed DTSC packet, ready to sent over the network. +std::string DTSC::Stream::outPacket(unsigned int num){ + std::string tmp; + unsigned int size; + tmp = Magic_Packet; + size = htonl(buffers[num].Pack().length()); + tmp.append((char*)&size, 4); + tmp.append(buffers[num].Pack()); + return tmp; +} + +/// Returns a packed DTSC header, ready to sent over the network. +std::string DTSC::Stream::outHeader(){ + std::string tmp; + unsigned int size; + tmp = Magic_Header; + size = htonl(metadata.Pack().length()); + tmp.append((char*)&size, 4); + tmp.append(metadata.Pack()); + return tmp; +} + +/// advances all given out and internal Ring classes to point to the new buffer, after one has been added. +/// Also updates the internal keyframes ring, as well as marking rings as starved if they are. +/// Unsets waiting rings, updating them with their new buffer number. +void DTSC::Stream::advanceRings(){ + std::deque::iterator dit; + std::set::iterator sit; + for (sit = rings.begin(); sit != rings.end(); sit++){ + (*sit)->b++; + if ((*sit)->waiting){(*sit)->waiting = false; (*sit)->b = 0;} + if ((*sit)->b >= buffers.size()){(*sit)->starved = true;} + } + for (dit = keyframes.begin(); dit != keyframes.end(); dit++){ + dit->b++; + if (dit->b >= buffers.size()){keyframes.erase(dit); break;} + } + if ((lastType() == VIDEO) && (buffers.front().getContentP("keyframe"))){ + keyframes.push_front(DTSC::Ring(0)); + } + //increase buffer size if no keyframes available + if ((buffercount > 1) && (keyframes.size() < 1)){buffercount++;} +} + +/// Constructs a new Ring, at the given buffer position. +/// \arg v Position for buffer. +DTSC::Ring::Ring(unsigned int v){ + b = v; + waiting = false; + starved = false; +} + +/// Requests a new Ring, which will be created and added to the internal Ring list. +/// This Ring will be kept updated so it always points to valid data or has the starved boolean set. +/// Don't forget to call dropRing() for all requested Ring classes that are no longer neccessary! +DTSC::Ring * DTSC::Stream::getRing(){ + DTSC::Ring * tmp; + if (keyframes.size() == 0){ + tmp = new DTSC::Ring(0); + }else{ + tmp = new DTSC::Ring(keyframes[0].b); + } + rings.insert(tmp); + return tmp; +} + +/// Deletes a given out Ring class from memory and internal Ring list. +/// Checks for NULL pointers and invalid pointers, silently discarding them. +void DTSC::Stream::dropRing(DTSC::Ring * ptr){ + if (rings.find(ptr) != rings.end()){ + rings.erase(ptr); + delete ptr; + } +} + +/// Properly cleans up the object for erasing. +/// Drops all Ring classes that have been given out. +DTSC::Stream::~Stream(){ + std::set::iterator sit; + for (sit = rings.begin(); sit != rings.end(); sit++){delete (*sit);} +} + +/// Returns the std::string Indice for the current object, if available. +/// Returns an empty string if no indice exists. +std::string DTSC::DTMI::Indice(){return myIndice;}; + +/// Returns the DTSC::DTMItype AMF0 object type for this object. +DTSC::DTMItype DTSC::DTMI::GetType(){return myType;}; + +/// Returns the numeric value of this object, if available. +/// If this object holds no numeric value, 0 is returned. +uint64_t DTSC::DTMI::NumValue(){return numval;}; + +/// Returns the std::string value of this object, if available. +/// If this object holds no string value, an empty string is returned. +std::string DTSC::DTMI::StrValue(){return strval;}; + +/// Returns the C-string value of this object, if available. +/// If this object holds no string value, an empty C-string is returned. +const char * DTSC::DTMI::Str(){return strval.c_str();}; + +/// Returns a count of the amount of objects this object currently holds. +/// If this object is not a container type, this function will always return 0. +int DTSC::DTMI::hasContent(){return contents.size();}; + +/// Adds an DTSC::DTMI to this object. Works for all types, but only makes sense for container types. +/// This function resets DTMI::packed to an empty string, forcing a repack on the next call to DTMI::Pack. +void DTSC::DTMI::addContent(DTSC::DTMI c){contents.push_back(c); packed = "";}; + +/// Returns a pointer to the object held at indice i. +/// Returns AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice. +/// \param i The indice of the object in this container. +DTSC::DTMI* DTSC::DTMI::getContentP(int i){return &contents.at(i);}; + +/// Returns a copy of the object held at indice i. +/// Returns a AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice. +/// \param i The indice of the object in this container. +DTSC::DTMI DTSC::DTMI::getContent(int i){return contents.at(i);}; + +/// Returns a pointer to the object held at indice s. +/// Returns NULL if no object is held at this indice. +/// \param s The indice of the object in this container. +DTSC::DTMI* DTSC::DTMI::getContentP(std::string s){ + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ + if (it->Indice() == s){return &(*it);} + } + return 0; +}; + +/// Returns a copy of the object held at indice s. +/// Returns a AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice. +/// \param s The indice of the object in this container. +DTSC::DTMI DTSC::DTMI::getContent(std::string s){ + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ + if (it->Indice() == s){return *it;} + } + return DTSC::DTMI("error", DTMI_ROOT); +}; + +/// Default constructor. +/// Simply fills the data with DTSC::DTMI("error", AMF0_DDV_CONTAINER) +DTSC::DTMI::DTMI(){ + *this = DTSC::DTMI("error", DTMI_ROOT); +};//default constructor + +/// Constructor for numeric objects. +/// The object type is by default AMF::AMF0_NUMBER, but this can be forced to a different value. +/// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. +/// \param val The numeric value of this object. Numeric AMF0 objects only support double-type values. +/// \param setType The object type to force this object to. +DTSC::DTMI::DTMI(std::string indice, double val, DTSC::DTMItype setType){//num type initializer + myIndice = indice; + myType = setType; + strval = ""; + numval = val; +}; + +/// Constructor for string objects. +/// The object type is by default AMF::AMF0_STRING, but this can be forced to a different value. +/// There is no need to manually change the type to AMF::AMF0_LONGSTRING, this will be done automatically. +/// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. +/// \param val The string value of this object. +/// \param setType The object type to force this object to. +DTSC::DTMI::DTMI(std::string indice, std::string val, DTSC::DTMItype setType){//str type initializer + myIndice = indice; + myType = setType; + strval = val; + numval = 0; +}; + +/// Constructor for container objects. +/// The object type is by default AMF::AMF0_OBJECT, but this can be forced to a different value. +/// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. +/// \param setType The object type to force this object to. +DTSC::DTMI::DTMI(std::string indice, DTSC::DTMItype setType){//object type initializer + myIndice = indice; + myType = setType; + strval = ""; + numval = 0; +}; + +/// Prints the contents of this object to std::cerr. +/// If this object contains other objects, it will call itself recursively +/// and print all nested content in a nice human-readable format. +void DTSC::DTMI::Print(std::string indent){ + std::cerr << indent; + // print my type + switch (myType){ + case DTMI_INT: std::cerr << "Integer"; break; + case DTMI_STRING: std::cerr << "String"; break; + case DTMI_OBJECT: std::cerr << "Object"; break; + case DTMI_OBJ_END: std::cerr << "Object end"; break; + case DTMI_ROOT: std::cerr << "Root Node"; break; + } + // print my string indice, if available + std::cerr << " " << myIndice << " "; + // print my numeric or string contents + switch (myType){ + case DTMI_INT: std::cerr << numval; break; + case DTMI_STRING: std::cerr << strval; break; + default: break;//we don't care about the rest, and don't want a compiler warning... + } + std::cerr << std::endl; + // if I hold other objects, print those too, recursively. + if (contents.size() > 0){ + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){it->Print(indent+" ");} + } +};//print + +/// Packs the DTMI to a std::string for transfer over the network. +/// If a packed version already exists, does not regenerate it. +/// If the object is a container type, this function will call itself recursively and contain all contents. +std::string DTSC::DTMI::Pack(){ + if (packed != ""){return packed;} + std::string r = ""; + //skip output of DDV container types, they do not exist. Only output their contents. + if (myType != DTMI_ROOT){r += myType;} + //output the properly formatted data stream for this object's contents. + switch (myType){ + case DTMI_INT: + r += *(((char*)&numval)+7); r += *(((char*)&numval)+6); + r += *(((char*)&numval)+5); r += *(((char*)&numval)+4); + r += *(((char*)&numval)+3); r += *(((char*)&numval)+2); + r += *(((char*)&numval)+1); r += *(((char*)&numval)); + break; + case DTMI_STRING: + r += strval.size() / (256*256*256); + r += strval.size() / (256*256); + r += strval.size() / 256; + r += strval.size() % 256; + r += strval; + break; + case DTMI_OBJECT: + if (contents.size() > 0){ + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ + r += it->Indice().size() / 256; + r += it->Indice().size() % 256; + r += it->Indice(); + r += it->Pack(); + } + } + r += (char)0; r += (char)0; r += (char)9; + break; + case DTMI_ROOT://only send contents + if (contents.size() > 0){ + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ + r += it->Pack(); + } + } + break; + case DTMI_OBJ_END: + break; + } + packed = r; + return r; +};//pack + +/// Parses a single AMF0 type - used recursively by the AMF::parse() functions. +/// This function updates i every call with the new position in the data. +/// \param data The raw data to parse. +/// \param len The size of the raw data. +/// \param i Current parsing position in the raw data. +/// \param name Indice name for any new object created. +/// \returns A single DTSC::DTMI, parsed from the raw data. +DTSC::DTMI DTSC::parseOneDTMI(const unsigned char *& data, unsigned int &len, unsigned int &i, std::string name){ + std::string tmpstr; + unsigned int tmpi = 0; + unsigned char tmpdbl[8]; + #if DEBUG >= 10 + fprintf(stderr, "Note: AMF type %hhx found. %i bytes left\n", data[i], len-i); + #endif + switch (data[i]){ + case DTMI_INT: + tmpdbl[7] = data[i+1]; + tmpdbl[6] = data[i+2]; + tmpdbl[5] = data[i+3]; + tmpdbl[4] = data[i+4]; + tmpdbl[3] = data[i+5]; + tmpdbl[2] = data[i+6]; + tmpdbl[1] = data[i+7]; + tmpdbl[0] = data[i+8]; + i+=9;//skip 8(a double)+1 forwards + return DTSC::DTMI(name, *(uint64_t*)tmpdbl, DTMI_INT); + break; + case DTMI_STRING: + tmpi = data[i+1]*256*256*256+data[i+2]*256*256+data[i+3]*256+data[i+4];//set tmpi to UTF-8-long length + tmpstr.clear();//clean tmpstr, just to be sure + tmpstr.append((const char *)data+i+5, (size_t)tmpi);//add the string data + i += tmpi + 5;//skip length+size+1 forwards + return DTSC::DTMI(name, tmpstr, DTMI_STRING); + break; + case DTMI_OBJECT:{ + ++i; + DTSC::DTMI ret(name, DTMI_OBJECT); + while (data[i] + data[i+1] != 0){//while not encountering 0x0000 (we assume 0x000009) + tmpi = data[i]*256+data[i+1];//set tmpi to the UTF-8 length + tmpstr.clear();//clean tmpstr, just to be sure + tmpstr.append((const char*)data+i+2, (size_t)tmpi);//add the string data + i += tmpi + 2;//skip length+size forwards + ret.addContent(parseOneDTMI(data, len, i, tmpstr));//add content, recursively parsed, updating i, setting indice to tmpstr + } + i += 3;//skip 0x000009 + return ret; + } break; + } + #if DEBUG >= 2 + fprintf(stderr, "Error: Unimplemented DTMI type %hhx - returning.\n", data[i]); + #endif + return DTSC::DTMI("error", DTMI_ROOT); +}//parseOne + +/// Parses a C-string to a valid DTSC::DTMI. +/// This function will find all AMF objects in the string and return +/// them all packed in a single AMF::AMF0_DDV_CONTAINER DTSC::DTMI. +DTSC::DTMI DTSC::parseDTMI(const unsigned char * data, unsigned int len){ + DTSC::DTMI ret("returned", DTMI_ROOT);//container type + unsigned int i = 0, j = 0; + while (i < len){ + ret.addContent(parseOneDTMI(data, len, i, "")); + if (i > j){j = i;}else{return ret;} + } + ret.packed = std::string((char*)data, (size_t)len); + return ret; +}//parse + +/// Parses a std::string to a valid DTSC::DTMI. +/// This function will find all AMF objects in the string and return +/// them all packed in a single AMF::AMF0_DDV_CONTAINER DTSC::DTMI. +DTSC::DTMI DTSC::parseDTMI(std::string data){ + return parseDTMI((const unsigned char*)data.c_str(), data.size()); +}//parse diff --git a/util/dtsc.h b/util/dtsc.h index 6b918f30..3a025753 100644 --- a/util/dtsc.h +++ b/util/dtsc.h @@ -2,46 +2,117 @@ /// Holds all headers for DDVTECH Stream Container parsing/generation. #pragma once -#include "dtmi.h" +#include +#include +#include //for uint64_t +#include +#include +#include -// Video: -// Codec (string) +// video +// codec (string) -// Audio: -// Codec (string) -// Samping rate (int, Hz) -// Sample Size (int, bytesize) -// Channels (int, channelcount) +// audio +// codec (string) +// sampingrate (int, Hz) +// samplesize (int, bytesize) +// channels (int, channelcount) +/// Holds all DDVTECH Stream Container classes and parsers. namespace DTSC{ + /// Enumerates all possible DTMI types. + enum DTMItype { + DTMI_INT = 0x01, ///< Unsigned 64-bit integer. + DTMI_STRING = 0x02, ///< String, equivalent to the AMF longstring type. + DTMI_OBJECT = 0xE0, ///< Object, equivalent to the AMF object type. + DTMI_OBJ_END = 0xEE, ///< End of object marker. + DTMI_ROOT = 0xFF ///< Root node for all DTMI data. + }; + + /// Recursive class that holds DDVTECH MediaInfo. + class DTMI { + public: + std::string Indice(); + DTMItype GetType(); + uint64_t NumValue(); + std::string StrValue(); + const char * Str(); + int hasContent(); + void addContent(DTMI c); + DTMI* getContentP(int i); + DTMI getContent(int i); + DTMI* getContentP(std::string s); + DTMI getContent(std::string s); + DTMI(); + DTMI(std::string indice, double val, DTMItype setType = DTMI_INT); + DTMI(std::string indice, std::string val, DTMItype setType = DTMI_STRING); + DTMI(std::string indice, DTMItype setType = DTMI_OBJECT); + void Print(std::string indent = ""); + std::string Pack(); + std::string packed; + protected: + std::string myIndice; ///< Holds this objects indice, if any. + DTMItype myType; ///< Holds this objects AMF0 type. + std::string strval; ///< Holds this objects string value, if any. + uint64_t numval; ///< Holds this objects numeric value, if any. + std::vector contents; ///< Holds this objects contents, if any (for container types). + };//AMFType + + /// Parses a C-string to a valid DTSC::DTMI. + DTMI parseDTMI(const unsigned char * data, unsigned int len); + /// Parses a std::string to a valid DTSC::DTMI. + DTMI parseDTMI(std::string data); + /// Parses a single DTMI type - used recursively by the DTSC::parseDTMI() functions. + DTMI parseOneDTMI(const unsigned char *& data, unsigned int &len, unsigned int &i, std::string name); + /// This enum holds all possible datatypes for DTSC packets. enum datatype { AUDIO, ///< Stream Audio data VIDEO, ///< Stream Video data META, ///< Stream Metadata INVALID ///< Anything else or no data available. - } + }; - char * Magic_Header; ///< The magic bytes for a DTSC header - char * Magic_Packet; ///< The magic bytes for a DTSC packet + extern char Magic_Header[]; ///< The magic bytes for a DTSC header + extern char Magic_Packet[]; ///< The magic bytes for a DTSC packet - /// Holds temporary data for a DTSC stream and provides functions to access/store it. + /// A part from the DTSC::Stream ringbuffer. + /// Holds information about a buffer that will stay consistent + class Ring { + public: + Ring(unsigned int v); + unsigned int b; ///< Holds current number of buffer. May and is intended to change unexpectedly! + bool waiting; ///< If true, this Ring is currently waiting for a buffer fill. + bool starved; ///< If true, this Ring can no longer receive valid data. + }; + + /// Holds temporary data for a DTSC stream and provides functions to utilize it. + /// Optionally also acts as a ring buffer of a certain requested size. + /// If ring buffering mode is enabled, it will automatically grow in size to always contain at least one keyframe. class Stream { public: Stream(); + ~Stream(); + Stream(unsigned int buffers); DTSC::DTMI metadata; - DRSC::DTMI lastPacket; + DTSC::DTMI & getPacket(unsigned int num = 0); datatype lastType(); - char * lastData(); + const char * lastData(); bool hasVideo(); bool hasAudio(); bool parsePacket(std::string & buffer); - private: - char * datapointer; + std::string outPacket(unsigned int num); + std::string outHeader(); + Ring * getRing(); + void dropRing(Ring * ptr); + private: + std::deque buffers; + std::set rings; + std::deque keyframes; + void advanceRings(); + const char * datapointer; datatype datapointertype; - } - - - + unsigned int buffercount; + }; }; diff --git a/util/socket.cpp b/util/socket.cpp index 4fdaceec..ed669e75 100644 --- a/util/socket.cpp +++ b/util/socket.cpp @@ -270,7 +270,7 @@ bool Socket::Connection::write(const void * buffer, int len){ /// \param buffer Location of the buffer to read to. /// \param len Amount of bytes to read. /// \returns True if the whole read was succesfull, false otherwise. -bool Socket::Connection::read(void * buffer, int len){ +bool Socket::Connection::read(const void * buffer, int len){ int sofar = 0; if (sock < 0){return false;} while (sofar != len){ @@ -309,9 +309,9 @@ bool Socket::Connection::read(void * buffer, int len){ }//Socket::Connection::read /// Read call that is compatible with file access syntax. This function simply calls the other read function. -bool Socket::Connection::read(void * buffer, int width, int count){return read(buffer, width*count);} +bool Socket::Connection::read(const void * buffer, int width, int count){return read(buffer, width*count);} /// Write call that is compatible with file access syntax. This function simply calls the other write function. -bool Socket::Connection::write(void * buffer, int width, int count){return write(buffer, width*count);} +bool Socket::Connection::write(const void * buffer, int width, int count){return write(buffer, width*count);} /// Write call that is compatible with std::string. This function simply calls the other write function. bool Socket::Connection::write(const std::string data){return write(data.c_str(), data.size());} @@ -320,7 +320,7 @@ bool Socket::Connection::write(const std::string data){return write(data.c_str() /// \param buffer Location of the buffer to write from. /// \param len Amount of bytes to write. /// \returns The amount of bytes actually written. -int Socket::Connection::iwrite(void * buffer, int len){ +int Socket::Connection::iwrite(const void * buffer, int len){ if (sock < 0){return 0;} int r = send(sock, buffer, len, 0); if (r < 0){ diff --git a/util/socket.h b/util/socket.h index bfe824e5..747c861b 100644 --- a/util/socket.h +++ b/util/socket.h @@ -37,12 +37,12 @@ namespace Socket{ bool canWrite(); ///< Calls poll() on the socket, checking if data can be written. signed int ready(); ///< Returns the ready-state for this socket. bool connected(); ///< Returns the connected-state for this socket. - bool read(void * buffer, int len); ///< Reads data from socket. - bool read(void * buffer, int width, int count); ///< Read call that is compatible with file access syntax. + bool read(const void * buffer, int len); ///< Reads data from socket. + bool read(const void * buffer, int width, int count); ///< Read call that is compatible with file access syntax. bool write(const void * buffer, int len); ///< Writes data to socket. - bool write(void * buffer, int width, int count); ///< Write call that is compatible with file access syntax. + bool write(const void * buffer, int width, int count); ///< Write call that is compatible with file access syntax. bool write(const std::string data); ///< Write call that is compatible with std::string. - int iwrite(void * buffer, int len); ///< Incremental write call. + int iwrite(const void * buffer, int len); ///< Incremental write call. int iread(void * buffer, int len); ///< Incremental read call. bool read(std::string & buffer); ///< Read call that is compatible with std::string. bool swrite(std::string & buffer); ///< Read call that is compatible with std::string. From 553596c1e3a5027551bde2c1013e89b80ab8822a Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 13 Sep 2011 17:20:39 +0200 Subject: [PATCH 167/788] Finished FLV2DTSC code, fixed a lot of bugs in new code, added DTSC info tool. --- util/dtsc.cpp | 134 ++++++++++++++++++++++++++++------------------- util/dtsc.h | 9 ++-- util/flv_tag.cpp | 2 +- 3 files changed, 87 insertions(+), 58 deletions(-) diff --git a/util/dtsc.cpp b/util/dtsc.cpp index 9d046287..8301e5b3 100644 --- a/util/dtsc.cpp +++ b/util/dtsc.cpp @@ -35,6 +35,7 @@ bool DTSC::Stream::parsePacket(std::string & buffer){ if (buffer.length() < len+8){return false;} metadata = DTSC::parseDTMI((unsigned char*)buffer.c_str() + 8, len); buffer.erase(0, len+8); + return false; } if (memcmp(buffer.c_str(), DTSC::Magic_Packet, 4) == 0){ len = ntohl(((uint32_t *)buffer.c_str())[1]); @@ -56,6 +57,7 @@ bool DTSC::Stream::parsePacket(std::string & buffer){ buffer.erase(0, len+8); while (buffers.size() > buffercount){buffers.pop_back();} advanceRings(); + return true; } #if DEBUG >= 2 std::cerr << "Error: Invalid DTMI data! I *will* get stuck!" << std::endl; @@ -92,25 +94,18 @@ bool DTSC::Stream::hasAudio(){ } /// Returns a packed DTSC packet, ready to sent over the network. -std::string DTSC::Stream::outPacket(unsigned int num){ - std::string tmp; - unsigned int size; - tmp = Magic_Packet; - size = htonl(buffers[num].Pack().length()); - tmp.append((char*)&size, 4); - tmp.append(buffers[num].Pack()); - return tmp; +std::string & DTSC::Stream::outPacket(unsigned int num){ + buffers[num].Pack(true); + return buffers[num].packed; } /// Returns a packed DTSC header, ready to sent over the network. -std::string DTSC::Stream::outHeader(){ - std::string tmp; - unsigned int size; - tmp = Magic_Header; - size = htonl(metadata.Pack().length()); - tmp.append((char*)&size, 4); - tmp.append(metadata.Pack()); - return tmp; +std::string & DTSC::Stream::outHeader(){ + if ((metadata.packed.length() < 4) || !metadata.netpacked){ + metadata.Pack(true); + metadata.packed.replace(0, 4, Magic_Header); + } + return metadata.packed; } /// advances all given out and internal Ring classes to point to the new buffer, after one has been added. @@ -198,7 +193,17 @@ int DTSC::DTMI::hasContent(){return contents.size();}; /// Adds an DTSC::DTMI to this object. Works for all types, but only makes sense for container types. /// This function resets DTMI::packed to an empty string, forcing a repack on the next call to DTMI::Pack. -void DTSC::DTMI::addContent(DTSC::DTMI c){contents.push_back(c); packed = "";}; +/// If the indice name already exists, replaces the indice. +void DTSC::DTMI::addContent(DTSC::DTMI c){ + std::vector::iterator it; + for (it = contents.begin(); it != contents.end(); it++){ + if (it->Indice() == c.Indice()){ + contents.erase(it); + break; + } + } + contents.push_back(c); packed = ""; +}; /// Returns a pointer to the object held at indice i. /// Returns AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice. @@ -237,11 +242,11 @@ DTSC::DTMI::DTMI(){ };//default constructor /// Constructor for numeric objects. -/// The object type is by default AMF::AMF0_NUMBER, but this can be forced to a different value. +/// The object type is by default DTMItype::DTMI_INT, but this can be forced to a different value. /// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. -/// \param val The numeric value of this object. Numeric AMF0 objects only support double-type values. +/// \param val The numeric value of this object. Numeric objects only support uint64_t values. /// \param setType The object type to force this object to. -DTSC::DTMI::DTMI(std::string indice, double val, DTSC::DTMItype setType){//num type initializer +DTSC::DTMI::DTMI(std::string indice, uint64_t val, DTSC::DTMItype setType){//num type initializer myIndice = indice; myType = setType; strval = ""; @@ -249,8 +254,6 @@ DTSC::DTMI::DTMI(std::string indice, double val, DTSC::DTMItype setType){//num t }; /// Constructor for string objects. -/// The object type is by default AMF::AMF0_STRING, but this can be forced to a different value. -/// There is no need to manually change the type to AMF::AMF0_LONGSTRING, this will be done automatically. /// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. /// \param val The string value of this object. /// \param setType The object type to force this object to. @@ -262,8 +265,7 @@ DTSC::DTMI::DTMI(std::string indice, std::string val, DTSC::DTMItype setType){// }; /// Constructor for container objects. -/// The object type is by default AMF::AMF0_OBJECT, but this can be forced to a different value. -/// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. +/// \param indice The string indice of this object in its container, or empty string if none. /// \param setType The object type to force this object to. DTSC::DTMI::DTMI(std::string indice, DTSC::DTMItype setType){//object type initializer myIndice = indice; @@ -290,7 +292,13 @@ void DTSC::DTMI::Print(std::string indent){ // print my numeric or string contents switch (myType){ case DTMI_INT: std::cerr << numval; break; - case DTMI_STRING: std::cerr << strval; break; + case DTMI_STRING: + if (strval.length() > 200 || ((strval.length() > 1) && ( (strval[0] < 'A') || (strval[0] > 'z') ) )){ + std::cerr << strval.length() << " bytes of data"; + }else{ + std::cerr << strval; + } + break; default: break;//we don't care about the rest, and don't want a compiler warning... } std::cerr << std::endl; @@ -303,11 +311,22 @@ void DTSC::DTMI::Print(std::string indent){ /// Packs the DTMI to a std::string for transfer over the network. /// If a packed version already exists, does not regenerate it. /// If the object is a container type, this function will call itself recursively and contain all contents. -std::string DTSC::DTMI::Pack(){ - if (packed != ""){return packed;} +/// \arg netpack If true, will pack as a full DTMI packet, if false only as the contents without header. +std::string DTSC::DTMI::Pack(bool netpack){ + if (packed != ""){ + if (netpacked == netpack){return packed;} + if (netpacked){ + packed.erase(0, 8); + }else{ + unsigned int size = htonl(packed.length()); + packed.insert(0, (char*)&size, 4); + packed.insert(0, Magic_Packet); + } + netpacked = !netpacked; + return packed; + } std::string r = ""; - //skip output of DDV container types, they do not exist. Only output their contents. - if (myType != DTMI_ROOT){r += myType;} + r += myType; //output the properly formatted data stream for this object's contents. switch (myType){ case DTMI_INT: @@ -324,6 +343,7 @@ std::string DTSC::DTMI::Pack(){ r += strval; break; case DTMI_OBJECT: + case DTMI_ROOT: if (contents.size() > 0){ for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ r += it->Indice().size() / 256; @@ -332,20 +352,19 @@ std::string DTSC::DTMI::Pack(){ r += it->Pack(); } } - r += (char)0; r += (char)0; r += (char)9; - break; - case DTMI_ROOT://only send contents - if (contents.size() > 0){ - for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ - r += it->Pack(); - } - } + r += (char)0x0; r += (char)0x0; r += (char)0xEE; break; case DTMI_OBJ_END: break; } packed = r; - return r; + netpacked = netpack; + if (netpacked){ + unsigned int size = htonl(packed.length()); + packed.insert(0, (char*)&size, 4); + packed.insert(0, Magic_Packet); + } + return packed; };//pack /// Parses a single AMF0 type - used recursively by the AMF::parse() functions. @@ -372,7 +391,7 @@ DTSC::DTMI DTSC::parseOneDTMI(const unsigned char *& data, unsigned int &len, un tmpdbl[2] = data[i+6]; tmpdbl[1] = data[i+7]; tmpdbl[0] = data[i+8]; - i+=9;//skip 8(a double)+1 forwards + i+=9;//skip 8(an uint64_t)+1 forwards return DTSC::DTMI(name, *(uint64_t*)tmpdbl, DTMI_INT); break; case DTMI_STRING: @@ -382,17 +401,30 @@ DTSC::DTMI DTSC::parseOneDTMI(const unsigned char *& data, unsigned int &len, un i += tmpi + 5;//skip length+size+1 forwards return DTSC::DTMI(name, tmpstr, DTMI_STRING); break; - case DTMI_OBJECT:{ + case DTMI_ROOT:{ ++i; - DTSC::DTMI ret(name, DTMI_OBJECT); - while (data[i] + data[i+1] != 0){//while not encountering 0x0000 (we assume 0x000009) + DTSC::DTMI ret(name, DTMI_ROOT); + while (data[i] + data[i+1] != 0){//while not encountering 0x0000 (we assume 0x0000EE) tmpi = data[i]*256+data[i+1];//set tmpi to the UTF-8 length tmpstr.clear();//clean tmpstr, just to be sure tmpstr.append((const char*)data+i+2, (size_t)tmpi);//add the string data i += tmpi + 2;//skip length+size forwards ret.addContent(parseOneDTMI(data, len, i, tmpstr));//add content, recursively parsed, updating i, setting indice to tmpstr } - i += 3;//skip 0x000009 + i += 3;//skip 0x0000EE + return ret; + } break; + case DTMI_OBJECT:{ + ++i; + DTSC::DTMI ret(name, DTMI_OBJECT); + while (data[i] + data[i+1] != 0){//while not encountering 0x0000 (we assume 0x0000EE) + tmpi = data[i]*256+data[i+1];//set tmpi to the UTF-8 length + tmpstr.clear();//clean tmpstr, just to be sure + tmpstr.append((const char*)data+i+2, (size_t)tmpi);//add the string data + i += tmpi + 2;//skip length+size forwards + ret.addContent(parseOneDTMI(data, len, i, tmpstr));//add content, recursively parsed, updating i, setting indice to tmpstr + } + i += 3;//skip 0x0000EE return ret; } break; } @@ -403,22 +435,18 @@ DTSC::DTMI DTSC::parseOneDTMI(const unsigned char *& data, unsigned int &len, un }//parseOne /// Parses a C-string to a valid DTSC::DTMI. -/// This function will find all AMF objects in the string and return -/// them all packed in a single AMF::AMF0_DDV_CONTAINER DTSC::DTMI. +/// This function will find one DTMI object in the string and return it. DTSC::DTMI DTSC::parseDTMI(const unsigned char * data, unsigned int len){ - DTSC::DTMI ret("returned", DTMI_ROOT);//container type - unsigned int i = 0, j = 0; - while (i < len){ - ret.addContent(parseOneDTMI(data, len, i, "")); - if (i > j){j = i;}else{return ret;} - } + DTSC::DTMI ret;//container type + unsigned int i = 0; + ret = parseOneDTMI(data, len, i, ""); ret.packed = std::string((char*)data, (size_t)len); + ret.netpacked = false; return ret; }//parse /// Parses a std::string to a valid DTSC::DTMI. -/// This function will find all AMF objects in the string and return -/// them all packed in a single AMF::AMF0_DDV_CONTAINER DTSC::DTMI. +/// This function will find one DTMI object in the string and return it. DTSC::DTMI DTSC::parseDTMI(std::string data){ return parseDTMI((const unsigned char*)data.c_str(), data.size()); }//parse diff --git a/util/dtsc.h b/util/dtsc.h index 3a025753..7d5827df 100644 --- a/util/dtsc.h +++ b/util/dtsc.h @@ -45,11 +45,12 @@ namespace DTSC{ DTMI* getContentP(std::string s); DTMI getContent(std::string s); DTMI(); - DTMI(std::string indice, double val, DTMItype setType = DTMI_INT); + DTMI(std::string indice, uint64_t val, DTMItype setType = DTMI_INT); DTMI(std::string indice, std::string val, DTMItype setType = DTMI_STRING); DTMI(std::string indice, DTMItype setType = DTMI_OBJECT); void Print(std::string indent = ""); - std::string Pack(); + std::string Pack(bool netpack = false); + bool netpacked; std::string packed; protected: std::string myIndice; ///< Holds this objects indice, if any. @@ -102,8 +103,8 @@ namespace DTSC{ bool hasVideo(); bool hasAudio(); bool parsePacket(std::string & buffer); - std::string outPacket(unsigned int num); - std::string outHeader(); + std::string & outPacket(unsigned int num); + std::string & outHeader(); Ring * getRing(); void dropRing(Ring * ptr); private: diff --git a/util/flv_tag.cpp b/util/flv_tag.cpp index 526de7d0..2a7b671c 100644 --- a/util/flv_tag.cpp +++ b/util/flv_tag.cpp @@ -109,7 +109,7 @@ std::string FLV::Tag::tagType(){ case 4: R += "VP6"; break; case 5: R += "VP6Alpha"; break; case 6: R += "ScreenVideo2"; break; - case 7: R += "AVC"; break; + case 7: R += "H264"; break; default: R += "unknown"; break; } R += " video "; From ae48440f4b366c9687b84efcfc7622463d1cd5a7 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 13 Sep 2011 20:37:16 +0200 Subject: [PATCH 168/788] Buffer stabilized --- util/dtsc.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/util/dtsc.cpp b/util/dtsc.cpp index 8301e5b3..9e2a5ba4 100644 --- a/util/dtsc.cpp +++ b/util/dtsc.cpp @@ -117,7 +117,7 @@ void DTSC::Stream::advanceRings(){ for (sit = rings.begin(); sit != rings.end(); sit++){ (*sit)->b++; if ((*sit)->waiting){(*sit)->waiting = false; (*sit)->b = 0;} - if ((*sit)->b >= buffers.size()){(*sit)->starved = true;} + if ((*sit)->starved || ((*sit)->b >= buffers.size())){(*sit)->starved = true; (*sit)->b = 0;} } for (dit = keyframes.begin(); dit != keyframes.end(); dit++){ dit->b++; @@ -375,7 +375,6 @@ std::string DTSC::DTMI::Pack(bool netpack){ /// \param name Indice name for any new object created. /// \returns A single DTSC::DTMI, parsed from the raw data. DTSC::DTMI DTSC::parseOneDTMI(const unsigned char *& data, unsigned int &len, unsigned int &i, std::string name){ - std::string tmpstr; unsigned int tmpi = 0; unsigned char tmpdbl[8]; #if DEBUG >= 10 @@ -394,20 +393,18 @@ DTSC::DTMI DTSC::parseOneDTMI(const unsigned char *& data, unsigned int &len, un i+=9;//skip 8(an uint64_t)+1 forwards return DTSC::DTMI(name, *(uint64_t*)tmpdbl, DTMI_INT); break; - case DTMI_STRING: + case DTMI_STRING:{ tmpi = data[i+1]*256*256*256+data[i+2]*256*256+data[i+3]*256+data[i+4];//set tmpi to UTF-8-long length - tmpstr.clear();//clean tmpstr, just to be sure - tmpstr.append((const char *)data+i+5, (size_t)tmpi);//add the string data + std::string tmpstr = std::string((const char *)data+i+5, (size_t)tmpi);//set the string data i += tmpi + 5;//skip length+size+1 forwards return DTSC::DTMI(name, tmpstr, DTMI_STRING); - break; + } break; case DTMI_ROOT:{ ++i; DTSC::DTMI ret(name, DTMI_ROOT); while (data[i] + data[i+1] != 0){//while not encountering 0x0000 (we assume 0x0000EE) tmpi = data[i]*256+data[i+1];//set tmpi to the UTF-8 length - tmpstr.clear();//clean tmpstr, just to be sure - tmpstr.append((const char*)data+i+2, (size_t)tmpi);//add the string data + std::string tmpstr = std::string((const char *)data+i+2, (size_t)tmpi);//set the string data i += tmpi + 2;//skip length+size forwards ret.addContent(parseOneDTMI(data, len, i, tmpstr));//add content, recursively parsed, updating i, setting indice to tmpstr } @@ -419,8 +416,7 @@ DTSC::DTMI DTSC::parseOneDTMI(const unsigned char *& data, unsigned int &len, un DTSC::DTMI ret(name, DTMI_OBJECT); while (data[i] + data[i+1] != 0){//while not encountering 0x0000 (we assume 0x0000EE) tmpi = data[i]*256+data[i+1];//set tmpi to the UTF-8 length - tmpstr.clear();//clean tmpstr, just to be sure - tmpstr.append((const char*)data+i+2, (size_t)tmpi);//add the string data + std::string tmpstr = std::string((const char *)data+i+2, (size_t)tmpi);//set the string data i += tmpi + 2;//skip length+size forwards ret.addContent(parseOneDTMI(data, len, i, tmpstr));//add content, recursively parsed, updating i, setting indice to tmpstr } From 63bf65952bc3dace8ed43ce4a56970bf5b3a6c25 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Wed, 14 Sep 2011 00:05:54 +0200 Subject: [PATCH 169/788] Almost working HTTP connector - mid-rewrite, running into some issues, sleepy, going to bed... --- util/dtsc.cpp | 10 +- util/dtsc.h | 45 ++++++--- util/flv_tag.cpp | 251 +++++++++++++++++++++++++++++++++++++++++++++++ util/flv_tag.h | 5 + util/socket.h | 2 +- 5 files changed, 296 insertions(+), 17 deletions(-) diff --git a/util/dtsc.cpp b/util/dtsc.cpp index 9e2a5ba4..85e1ce47 100644 --- a/util/dtsc.cpp +++ b/util/dtsc.cpp @@ -44,7 +44,7 @@ bool DTSC::Stream::parsePacket(std::string & buffer){ buffers.front() = DTSC::parseDTMI((unsigned char*)buffer.c_str() + 8, len); datapointertype = INVALID; if (buffers.front().getContentP("data")){ - datapointer = buffers.front().getContentP("data")->StrValue().c_str(); + datapointer = &(buffers.front().getContentP("data")->StrValue()); if (buffers.front().getContentP("datatype")){ std::string tmp = buffers.front().getContentP("datatype")->StrValue(); if (tmp == "video"){datapointertype = VIDEO;} @@ -68,8 +68,8 @@ bool DTSC::Stream::parsePacket(std::string & buffer){ /// Returns a direct pointer to the data attribute of the last received packet, if available. /// Returns NULL if no valid pointer or packet is available. -const char * DTSC::Stream::lastData(){ - return datapointer; +std::string & DTSC::Stream::lastData(){ + return *datapointer; } /// Returns the packed in this buffer number. @@ -177,11 +177,11 @@ DTSC::DTMItype DTSC::DTMI::GetType(){return myType;}; /// Returns the numeric value of this object, if available. /// If this object holds no numeric value, 0 is returned. -uint64_t DTSC::DTMI::NumValue(){return numval;}; +uint64_t & DTSC::DTMI::NumValue(){return numval;}; /// Returns the std::string value of this object, if available. /// If this object holds no string value, an empty string is returned. -std::string DTSC::DTMI::StrValue(){return strval;}; +std::string & DTSC::DTMI::StrValue(){return strval;}; /// Returns the C-string value of this object, if available. /// If this object holds no string value, an empty C-string is returned. diff --git a/util/dtsc.h b/util/dtsc.h index 7d5827df..30bbc092 100644 --- a/util/dtsc.h +++ b/util/dtsc.h @@ -9,16 +9,39 @@ #include #include -// video -// codec (string) -// audio -// codec (string) -// sampingrate (int, Hz) -// samplesize (int, bytesize) -// channels (int, channelcount) /// Holds all DDVTECH Stream Container classes and parsers. +///Video: +/// - codec (string: AAC, MP3) +/// - width (int, pixels) +/// - height (int, pixels) +/// - fpks (int, frames per kilosecond (FPS * 1000)) +/// - bps (int, bytes per second) +/// - init (string, init data) +/// +///Audio: +/// - codec (string: H264, H263, VP6) +/// - rate (int, Hz) +/// - size (int, bitsize) +/// - bps (int, bytes per second) +/// - channels (int, channelcount) +/// - init (string, init data) +/// +///All packets: +/// - datatype (string: audio, video, meta (unused)) +/// - data (string: data) +/// - time (int: ms into video) +/// +///Video packets: +/// - keyframe (int, if set, is a seekable keyframe) +/// - interframe (int, if set, is a non-seekable interframe) +/// - disposableframe (int, if set, is a disposable interframe) +/// +///H264 video packets: +/// - nalu (int, if set, is a nalu) +/// - nalu_end (int, if set, is a end-of-sequence) +/// - offset (int, unsigned version of signed int! Holds the ms offset between timestamp and proper display time for B-frames) namespace DTSC{ /// Enumerates all possible DTMI types. @@ -35,8 +58,8 @@ namespace DTSC{ public: std::string Indice(); DTMItype GetType(); - uint64_t NumValue(); - std::string StrValue(); + uint64_t & NumValue(); + std::string & StrValue(); const char * Str(); int hasContent(); void addContent(DTMI c); @@ -99,7 +122,7 @@ namespace DTSC{ DTSC::DTMI metadata; DTSC::DTMI & getPacket(unsigned int num = 0); datatype lastType(); - const char * lastData(); + std::string & lastData(); bool hasVideo(); bool hasAudio(); bool parsePacket(std::string & buffer); @@ -112,7 +135,7 @@ namespace DTSC{ std::set rings; std::deque keyframes; void advanceRings(); - const char * datapointer; + std::string * datapointer; datatype datapointertype; unsigned int buffercount; }; diff --git a/util/flv_tag.cpp b/util/flv_tag.cpp index 2a7b671c..3af96eea 100644 --- a/util/flv_tag.cpp +++ b/util/flv_tag.cpp @@ -2,6 +2,7 @@ /// Holds all code for the FLV namespace. #include "flv_tag.h" +#include "amf.h" #include "rtmpchunks.h" #include //for Tag::FileLoader #include //for Tag::FileLoader @@ -245,6 +246,256 @@ FLV::Tag & FLV::Tag::operator= (const FLV::Tag& O){ return *this; }//assignment operator +/// FLV loader function from DTSC. +/// Takes the DTSC data and makes it into FLV. +bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ + switch (S.lastType()){ + case DTSC::VIDEO: + len = S.lastData().length() + 16; + if (S.metadata.getContentP("video") && S.metadata.getContentP("video")->getContentP("codec")){ + if (S.metadata.getContentP("video")->getContentP("codec")->StrValue() == "H264"){len += 4;} + } + break; + case DTSC::AUDIO: + len = S.lastData().length() + 16; + if (S.metadata.getContentP("audio") && S.metadata.getContentP("audio")->getContentP("codec")){ + if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "AAC"){len += 1;} + } + break; + case DTSC::META: + len = S.lastData().length() + 15; + break; + default://ignore all other types (there are currently no other types...) + break; + } + if (len > 0){ + if (!data){ + data = (char*)malloc(len); + buf = len; + }else{ + if (buf < len){ + data = (char*)realloc(data, len); + buf = len; + } + } + switch (S.lastType()){ + case DTSC::VIDEO: + if ((unsigned int)len == S.lastData().length() + 16){ + memcpy(data+12, S.lastData().c_str(), S.lastData().length()); + }else{ + memcpy(data+16, S.lastData().c_str(), S.lastData().length()); + if (S.getPacket().getContentP("nalu")){data[12] = 1;}else{data[12] = 2;} + int offset = S.getPacket().getContentP("offset")->NumValue(); + data[13] = (offset >> 16) & 0xFF; + data[14] = (offset >> 8) & 0XFF; + data[15] = offset & 0xFF; + } + data[11] = 0; + if (S.metadata.getContentP("video")->getContentP("codec")->StrValue() == "H264"){data[11] += 7;} + if (S.metadata.getContentP("video")->getContentP("codec")->StrValue() == "H263"){data[11] += 2;} + if (S.getPacket().getContentP("keyframe")){data[11] += 0x10;} + if (S.getPacket().getContentP("interframe")){data[11] += 0x20;} + if (S.getPacket().getContentP("disposableframe")){data[11] += 0x30;} + break; + case DTSC::AUDIO: + if ((unsigned int)len == S.lastData().length() + 16){ + memcpy(data+12, S.lastData().c_str(), S.lastData().length()); + }else{ + memcpy(data+13, S.lastData().c_str(), S.lastData().length()); + data[12] = 1;//raw AAC data, not sequence header + } + data[11] = 0; + if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "AAC"){data[11] += 0xA0;} + if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "MP3"){data[11] += 0x20;} + if (S.metadata.getContentP("audio")->getContentP("rate")->NumValue() == 11025){data[11] += 0x04;} + if (S.metadata.getContentP("audio")->getContentP("rate")->NumValue() == 22050){data[11] += 0x08;} + if (S.metadata.getContentP("audio")->getContentP("rate")->NumValue() == 44100){data[11] += 0x0C;} + if (S.metadata.getContentP("audio")->getContentP("size")->NumValue() == 16){data[11] += 0x02;} + if (S.metadata.getContentP("audio")->getContentP("channels")->NumValue() > 1){data[11] += 0x01;} + break; + case DTSC::META: + memcpy(data+11, S.lastData().c_str(), S.lastData().length()); + break; + default: break; + } + } + ((unsigned int *)(data+len-4))[0] = len-15; + switch (S.lastType()){ + case DTSC::VIDEO: data[0] = 0x09; break; + case DTSC::AUDIO: data[0] = 0x08; break; + case DTSC::META: data[0] = 0x12; break; + default: break; + } + data[1] = ((len-15) >> 16) & 0xFF; + data[2] = ((len-15) >> 8) & 0xFF; + data[3] = (len-15) & 0xFF; + tagTime(S.getPacket().getContentP("time")->NumValue()); + return true; +} + +/// FLV Video init data loader function from DTSC. +/// Takes the DTSC Video init data and makes it into FLV. +/// Assumes init data is available - so check before calling! +bool FLV::Tag::DTSCVideoInit(DTSC::Stream & S){ + if (S.metadata.getContentP("video")->getContentP("codec")->StrValue() == "H264"){ + len = S.metadata.getContentP("video")->getContentP("init")->StrValue().length() + 20; + } + if (len > 0){ + if (!data){ + data = (char*)malloc(len); + buf = len; + }else{ + if (buf < len){ + data = (char*)realloc(data, len); + buf = len; + } + } + memcpy(data+16, S.metadata.getContentP("video")->getContentP("init")->StrValue().c_str(), len-20); + data[12] = 0;//H264 sequence header + data[13] = 0; + data[14] = 0; + data[15] = 0; + data[11] = 0x57;//H264 init data (0x07 & 0x50) + } + ((unsigned int *)(data+len-4))[0] = len-15; + switch (S.lastType()){ + case DTSC::VIDEO: data[0] = 0x09; break; + case DTSC::AUDIO: data[0] = 0x08; break; + case DTSC::META: data[0] = 0x12; break; + default: break; + } + data[1] = ((len-15) >> 16) & 0xFF; + data[2] = ((len-15) >> 8) & 0xFF; + data[3] = (len-15) & 0xFF; + tagTime(0); + return true; +} + +/// FLV Audio init data loader function from DTSC. +/// Takes the DTSC Audio init data and makes it into FLV. +/// Assumes init data is available - so check before calling! +bool FLV::Tag::DTSCAudioInit(DTSC::Stream & S){ + len = 0; + if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "AAC"){ + len = S.metadata.getContentP("audio")->getContentP("init")->StrValue().length() + 17; + } + if (len > 0){ + if (!data){ + data = (char*)malloc(len); + buf = len; + }else{ + if (buf < len){ + data = (char*)realloc(data, len); + buf = len; + } + } + memcpy(data+13, S.metadata.getContentP("audio")->getContentP("init")->StrValue().c_str(), len-17); + data[12] = 0;//AAC sequence header + data[11] = 0; + if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "AAC"){data[11] += 0xA0;} + if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "MP3"){data[11] += 0x20;} + if (S.metadata.getContentP("audio")->getContentP("rate")->NumValue() == 11000){data[11] += 0x04;} + if (S.metadata.getContentP("audio")->getContentP("rate")->NumValue() == 22000){data[11] += 0x08;} + if (S.metadata.getContentP("audio")->getContentP("rate")->NumValue() == 44000){data[11] += 0x0C;} + if (S.metadata.getContentP("audio")->getContentP("size")->NumValue() == 16){data[11] += 0x02;} + if (S.metadata.getContentP("audio")->getContentP("channels")->NumValue() > 1){data[11] += 0x01;} + } + ((unsigned int *)(data+len-4))[0] = len-15; + switch (S.lastType()){ + case DTSC::VIDEO: data[0] = 0x09; break; + case DTSC::AUDIO: data[0] = 0x08; break; + case DTSC::META: data[0] = 0x12; break; + default: break; + } + data[1] = ((len-15) >> 16) & 0xFF; + data[2] = ((len-15) >> 8) & 0xFF; + data[3] = (len-15) & 0xFF; + tagTime(0); + return true; +} + +/// FLV metadata loader function from DTSC. +/// Takes the DTSC metadata and makes it into FLV. +/// Assumes metadata is available - so check before calling! +bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){ + AMF::Object amfdata("root", AMF::AMF0_DDV_CONTAINER); + + amfdata.addContent(AMF::Object("", "onMetaData")); + amfdata.addContent(AMF::Object("", AMF::AMF0_ECMA_ARRAY)); + if (S.metadata.getContentP("video")){ + amfdata.getContentP(1)->addContent(AMF::Object("hasVideo", 1, AMF::AMF0_BOOL)); + if (S.metadata.getContentP("video")->getContentP("codec")->StrValue() == "H264"){ + amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", 7, AMF::AMF0_NUMBER)); + } + if (S.metadata.getContentP("video")->getContentP("codec")->StrValue() == "VP6"){ + amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", 4, AMF::AMF0_NUMBER)); + } + if (S.metadata.getContentP("video")->getContentP("codec")->StrValue() == "H263"){ + amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", 2, AMF::AMF0_NUMBER)); + } + if (S.metadata.getContentP("video")->getContentP("width")){ + amfdata.getContentP(1)->addContent(AMF::Object("width", S.metadata.getContentP("video")->getContentP("width")->NumValue(), AMF::AMF0_NUMBER)); + } + if (S.metadata.getContentP("video")->getContentP("height")){ + amfdata.getContentP(1)->addContent(AMF::Object("height", S.metadata.getContentP("video")->getContentP("height")->NumValue(), AMF::AMF0_NUMBER)); + } + if (S.metadata.getContentP("video")->getContentP("fpks")){ + amfdata.getContentP(1)->addContent(AMF::Object("framerate", (double)S.metadata.getContentP("video")->getContentP("fpks")->NumValue() / 1000.0, AMF::AMF0_NUMBER)); + } + if (S.metadata.getContentP("video")->getContentP("bps")){ + amfdata.getContentP(1)->addContent(AMF::Object("videodatarate", ((double)S.metadata.getContentP("video")->getContentP("bps")->NumValue() * 8.0) / 1024.0, AMF::AMF0_NUMBER)); + } + } + if (S.metadata.getContentP("audio")){ + amfdata.getContentP(1)->addContent(AMF::Object("hasAudio", 1, AMF::AMF0_BOOL)); + amfdata.getContentP(1)->addContent(AMF::Object("audiodelay", 0, AMF::AMF0_NUMBER)); + if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "AAC"){ + amfdata.getContentP(1)->addContent(AMF::Object("audiocodecid", 10, AMF::AMF0_NUMBER)); + } + if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "MP3"){ + amfdata.getContentP(1)->addContent(AMF::Object("audiocodecid", 2, AMF::AMF0_NUMBER)); + } + if (S.metadata.getContentP("audio")->getContentP("channels")){ + if (S.metadata.getContentP("audio")->getContentP("channels")->NumValue() > 1){ + amfdata.getContentP(1)->addContent(AMF::Object("stereo", 1, AMF::AMF0_BOOL)); + }else{ + amfdata.getContentP(1)->addContent(AMF::Object("stereo", 0, AMF::AMF0_BOOL)); + } + } + if (S.metadata.getContentP("audio")->getContentP("rate")){ + amfdata.getContentP(1)->addContent(AMF::Object("audiosamplerate", S.metadata.getContentP("audio")->getContentP("rate")->NumValue(), AMF::AMF0_NUMBER)); + } + if (S.metadata.getContentP("audio")->getContentP("size")){ + amfdata.getContentP(1)->addContent(AMF::Object("audiosamplesize", S.metadata.getContentP("audio")->getContentP("size")->NumValue(), AMF::AMF0_NUMBER)); + } + if (S.metadata.getContentP("audio")->getContentP("bps")){ + amfdata.getContentP(1)->addContent(AMF::Object("audiodatarate", ((double)S.metadata.getContentP("audio")->getContentP("bps")->NumValue() * 8.0) / 1024.0, AMF::AMF0_NUMBER)); + } + } + + std::string tmp = amfdata.Pack(); + len = tmp.length() + 15; + if (len > 0){ + if (!data){ + data = (char*)malloc(len); + buf = len; + }else{ + if (buf < len){ + data = (char*)realloc(data, len); + buf = len; + } + } + memcpy(data+11, tmp.c_str(), len-15); + } + ((unsigned int *)(data+len-4))[0] = len-15; + data[0] = 0x12; + data[1] = ((len-15) >> 16) & 0xFF; + data[2] = ((len-15) >> 8) & 0xFF; + data[3] = (len-15) & 0xFF; + tagTime(0); + return true; +} + /// FLV loader function from chunk. /// Copies the contents and wraps it in a FLV header. bool FLV::Tag::ChunkLoader(const RTMPStream::Chunk& O){ diff --git a/util/flv_tag.h b/util/flv_tag.h index 1350c870..d349d533 100644 --- a/util/flv_tag.h +++ b/util/flv_tag.h @@ -3,6 +3,7 @@ #pragma once #include "socket.h" +#include "dtsc.h" #include //forward declaration of RTMPStream::Chunk to avoid circular dependencies. @@ -38,6 +39,10 @@ namespace FLV { Tag(const RTMPStream::Chunk& O); /// Date: Thu, 15 Sep 2011 18:48:57 +0200 Subject: [PATCH 170/788] Attempt to fix FLV --- util/flv_tag.cpp | 33 +++++++++++++++++++++------------ util/flv_tag.h | 1 + 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/util/flv_tag.cpp b/util/flv_tag.cpp index 3af96eea..f0fb956f 100644 --- a/util/flv_tag.cpp +++ b/util/flv_tag.cpp @@ -319,7 +319,7 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ default: break; } } - ((unsigned int *)(data+len-4))[0] = len-15; + setLen(); switch (S.lastType()){ case DTSC::VIDEO: data[0] = 0x09; break; case DTSC::AUDIO: data[0] = 0x08; break; @@ -333,6 +333,19 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ return true; } +/// Helper function that properly sets the tag length from the internal len variable. +void FLV::Tag::setLen(){ + int len4 = len - 4; + int i = data+len-1; + data[--i] = (len4) & 0xFF; + len4 >>= 8; + data[--i] = (len4) & 0xFF; + len4 >>= 8; + data[--i] = (len4) & 0xFF; + len4 >>= 8; + data[--i] = (len4) & 0xFF; +} + /// FLV Video init data loader function from DTSC. /// Takes the DTSC Video init data and makes it into FLV. /// Assumes init data is available - so check before calling! @@ -355,15 +368,10 @@ bool FLV::Tag::DTSCVideoInit(DTSC::Stream & S){ data[13] = 0; data[14] = 0; data[15] = 0; - data[11] = 0x57;//H264 init data (0x07 & 0x50) - } - ((unsigned int *)(data+len-4))[0] = len-15; - switch (S.lastType()){ - case DTSC::VIDEO: data[0] = 0x09; break; - case DTSC::AUDIO: data[0] = 0x08; break; - case DTSC::META: data[0] = 0x12; break; - default: break; + data[11] = 0x17;//H264 keyframe (0x07 & 0x10) } + setLen(); + data[0] = 0x09; data[1] = ((len-15) >> 16) & 0xFF; data[2] = ((len-15) >> 8) & 0xFF; data[3] = (len-15) & 0xFF; @@ -400,13 +408,14 @@ bool FLV::Tag::DTSCAudioInit(DTSC::Stream & S){ if (S.metadata.getContentP("audio")->getContentP("size")->NumValue() == 16){data[11] += 0x02;} if (S.metadata.getContentP("audio")->getContentP("channels")->NumValue() > 1){data[11] += 0x01;} } - ((unsigned int *)(data+len-4))[0] = len-15; + setLen(); switch (S.lastType()){ case DTSC::VIDEO: data[0] = 0x09; break; case DTSC::AUDIO: data[0] = 0x08; break; case DTSC::META: data[0] = 0x12; break; default: break; } + data[0] = 0x08; data[1] = ((len-15) >> 16) & 0xFF; data[2] = ((len-15) >> 8) & 0xFF; data[3] = (len-15) & 0xFF; @@ -487,7 +496,7 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){ } memcpy(data+11, tmp.c_str(), len-15); } - ((unsigned int *)(data+len-4))[0] = len-15; + setLen(); data[0] = 0x12; data[1] = ((len-15) >> 16) & 0xFF; data[2] = ((len-15) >> 8) & 0xFF; @@ -512,7 +521,7 @@ bool FLV::Tag::ChunkLoader(const RTMPStream::Chunk& O){ } memcpy(data+11, &(O.data[0]), O.len); } - ((unsigned int *)(data+len-4))[0] = O.len; + setLen(); data[0] = O.msg_type_id; data[3] = O.len & 0xFF; data[2] = (O.len >> 8) & 0xFF; diff --git a/util/flv_tag.h b/util/flv_tag.h index d349d533..6f1f7da7 100644 --- a/util/flv_tag.h +++ b/util/flv_tag.h @@ -51,6 +51,7 @@ namespace FLV { int buf; ///< Maximum length of buffer space. bool done; ///< Body reading done? unsigned int sofar; ///< How many bytes are read sofar? + void setLen(); //loader helper functions bool MemReadUntil(char * buffer, unsigned int count, unsigned int & sofar, char * D, unsigned int S, unsigned int & P); bool SockReadUntil(char * buffer, unsigned int count, unsigned int & sofar, Socket::Connection & sock); From 53c6d582ba12e6db6cd5cfc15573d68cb59ecd77 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 5 Dec 2011 15:35:10 +0100 Subject: [PATCH 171/788] Fix DTSC tools compiling --- util/flv_tag.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/flv_tag.cpp b/util/flv_tag.cpp index f0fb956f..c5b4b964 100644 --- a/util/flv_tag.cpp +++ b/util/flv_tag.cpp @@ -336,7 +336,7 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ /// Helper function that properly sets the tag length from the internal len variable. void FLV::Tag::setLen(){ int len4 = len - 4; - int i = data+len-1; + int i = len-1; data[--i] = (len4) & 0xFF; len4 >>= 8; data[--i] = (len4) & 0xFF; From a491b59fe8fd3a08230ca529dc781e775296d7ae Mon Sep 17 00:00:00 2001 From: Lekensteyn Date: Sat, 25 Feb 2012 18:22:45 +0100 Subject: [PATCH 172/788] According to the JSON spec, null is NOT valid array/object --- util/json/json_value.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/json/json_value.cpp b/util/json/json_value.cpp index f418af26..21996f4a 100644 --- a/util/json/json_value.cpp +++ b/util/json/json_value.cpp @@ -1312,14 +1312,14 @@ Value::isString() const bool Value::isArray() const { - return type_ == nullValue || type_ == arrayValue; + return type_ == arrayValue; } bool Value::isObject() const { - return type_ == nullValue || type_ == objectValue; + return type_ == objectValue; } From a2eae38e6add055de32f6c72a20d58b0484c8d0c Mon Sep 17 00:00:00 2001 From: Lekensteyn Date: Sat, 25 Feb 2012 23:46:39 +0100 Subject: [PATCH 173/788] Improve http argument parsing for special cases --- util/http_parser.cpp | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/util/http_parser.cpp b/util/http_parser.cpp index d2b2ac89..7a087f70 100644 --- a/util/http_parser.cpp +++ b/util/http_parser.cpp @@ -109,7 +109,10 @@ void HTTP::Parser::SetHeader(std::string i, int v){ void HTTP::Parser::SetVar(std::string i, std::string v){ Trim(i); Trim(v); - vars[i] = 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 Socket::Connection. @@ -201,23 +204,36 @@ void HTTP::Parser::SendResponse(Socket::Connection & conn, std::string code, std conn.write(tmp); } +#include /// Parses GET or POST-style variable data. /// Saves to internal variable structure using HTTP::Parser::SetVar. void HTTP::Parser::parseVars(std::string data){ std::string varname; std::string varval; - while (data.find('=') != std::string::npos){ - size_t found = data.find('='); - varname = urlunescape(data.substr(0, found)); - data.erase(0, found+1); - found = data.find('&'); - varval = urlunescape(data.substr(0, found)); - SetVar(varname, varval); - if (found == std::string::npos){ - data.clear(); - }else{ - data.erase(0, found+1); + // 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; } } From 4c3ce2d837865ffd6b45c7c60fdee9379c70c73d Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 2 Mar 2012 17:48:20 +0100 Subject: [PATCH 174/788] Fixed DTSC info comment. --- util/dtsc.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/dtsc.h b/util/dtsc.h index 30bbc092..3d690d53 100644 --- a/util/dtsc.h +++ b/util/dtsc.h @@ -13,7 +13,7 @@ /// Holds all DDVTECH Stream Container classes and parsers. ///Video: -/// - codec (string: AAC, MP3) +/// - codec (string: H264, H263, VP6) /// - width (int, pixels) /// - height (int, pixels) /// - fpks (int, frames per kilosecond (FPS * 1000)) @@ -21,7 +21,7 @@ /// - init (string, init data) /// ///Audio: -/// - codec (string: H264, H263, VP6) +/// - codec (string: AAC, MP3) /// - rate (int, Hz) /// - size (int, bitsize) /// - bps (int, bytes per second) From b08ced33e8b4f6c977e157aaf7ac11cf8f637e8e Mon Sep 17 00:00:00 2001 From: Lekensteyn Date: Thu, 8 Mar 2012 23:11:28 +0100 Subject: [PATCH 175/788] No need for listening on udp port 65535 --- util/server_setup.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/server_setup.cpp b/util/server_setup.cpp index 75ac19fe..ae15ff87 100644 --- a/util/server_setup.cpp +++ b/util/server_setup.cpp @@ -27,7 +27,7 @@ #include #include #include -Socket::Server server_socket(-1); ///< Placeholder for the server socket +Socket::Server server_socket; ///< Placeholder for the server socket /// Basic signal handler. Disconnects the server_socket if it receives /// a SIGINT, SIGHUP or SIGTERM signal, but does nothing for SIGPIPE. From dbd6cbc500e7ec9e86ce00d04119ddd4b440c36e Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 11 Mar 2012 13:48:03 +0100 Subject: [PATCH 176/788] Added closing of file descriptors to piped process starter (gets rid of ffmpeg junk output), added some extra debugging output to DDV Controller. --- util/util.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/util/util.cpp b/util/util.cpp index d599c0d0..773248f7 100644 --- a/util/util.cpp +++ b/util/util.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -147,11 +148,14 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2){ return 0; } + int devnull = open("/dev/null", O_RDWR); pid_t ret = fork(); if (ret == 0){ close(pfildes[0]); - dup2(pfildes[1],1); + dup2(pfildes[1],STDOUT_FILENO); close(pfildes[1]); + dup2(devnull, STDIN_FILENO); + dup2(devnull, STDERR_FILENO); runCmd(cmd); }else{ if (ret > 0){ @@ -165,12 +169,14 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2){ return 0; } } - + pid_t ret2 = fork(); if (ret2 == 0){ close(pfildes[1]); - dup2(pfildes[0],0); + dup2(pfildes[0],STDIN_FILENO); close(pfildes[0]); + dup2(devnull, STDOUT_FILENO); + dup2(devnull, STDERR_FILENO); runCmd(cmd2); }else{ if (ret2 > 0){ From aaebc563e8c8911da62f055b46585809fc163a5f Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 12 Mar 2012 15:49:19 +0100 Subject: [PATCH 177/788] Fixed several FLV-related server bugs. --- util/flv_tag.cpp | 46 +++++++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/util/flv_tag.cpp b/util/flv_tag.cpp index 526de7d0..427b2243 100644 --- a/util/flv_tag.cpp +++ b/util/flv_tag.cpp @@ -320,7 +320,14 @@ bool FLV::Tag::MemLoader(char * D, unsigned int S, unsigned int & P){ len += (data[2] << 8); len += (data[1] << 16); if (buf < len){data = (char*)realloc(data, len); buf = len;} - if (data[0] > 0x12){FLV::Parse_Error = true; Error_Str = "Invalid Tag received."; return false;} + if (data[0] > 0x12){ + data[0] += 32; + FLV::Parse_Error = true; + Error_Str = "Invalid Tag received ("; + Error_Str += data[0]; + Error_Str += ")."; + return false; + } done = false; } } @@ -347,20 +354,11 @@ bool FLV::Tag::MemLoader(char * D, unsigned int S, unsigned int & P){ /// \param sock Socket to read from. /// \return True if count bytes are read succesfully, false otherwise. bool FLV::Tag::SockReadUntil(char * buffer, unsigned int count, unsigned int & sofar, Socket::Connection & sock){ - if (sofar == count){return true;} - if (!sock.read(buffer + sofar,count-sofar)){ - if (errno != EWOULDBLOCK){ - FLV::Parse_Error = true; - Error_Str = "Error reading from socket."; - } - return false; - } - sofar += count-sofar; - if (sofar == count){return true;} - if (sofar > count){ - FLV::Parse_Error = true; - Error_Str = "Socket buffer overflow."; - } + if (sofar >= count){return true;} + int r = 0; + r = sock.iread(buffer + sofar,count-sofar); + sofar += r; + if (sofar >= count){return true;} return false; }//Tag::SockReadUntil @@ -387,7 +385,14 @@ bool FLV::Tag::SockLoader(Socket::Connection sock){ len += (data[2] << 8); len += (data[1] << 16); if (buf < len){data = (char*)realloc(data, len); buf = len;} - if (data[0] > 0x12){FLV::Parse_Error = true; Error_Str = "Invalid Tag received."; return false;} + if (data[0] > 0x12){ + data[0] += 32; + FLV::Parse_Error = true; + Error_Str = "Invalid Tag received ("; + Error_Str += data[0]; + Error_Str += ")."; + return false; + } done = false; } } @@ -459,7 +464,14 @@ bool FLV::Tag::FileLoader(FILE * f){ len += (data[2] << 8); len += (data[1] << 16); if (buf < len){data = (char*)realloc(data, len); buf = len;} - if (data[0] > 0x12){FLV::Parse_Error = true; Error_Str = "Invalid Tag received."; return false;} + if (data[0] > 0x12){ + data[0] += 32; + FLV::Parse_Error = true; + Error_Str = "Invalid Tag received ("; + Error_Str += data[0]; + Error_Str += ")."; + return false; + } done = false; } } From ee41aa7cd07f1e97c4fa5bb9856c1fded37826b2 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 30 Mar 2012 13:20:08 +0200 Subject: [PATCH 178/788] Added DTSC rate limiter. Closes #12 --- util/dtsc.cpp | 6 ++++++ util/dtsc.h | 1 + 2 files changed, 7 insertions(+) diff --git a/util/dtsc.cpp b/util/dtsc.cpp index 85e1ce47..a5eb05e3 100644 --- a/util/dtsc.cpp +++ b/util/dtsc.cpp @@ -23,6 +23,12 @@ DTSC::Stream::Stream(unsigned int rbuffers){ buffercount = rbuffers; } +/// Returns the time in milliseconds of the last received packet. +/// This is _not_ the time this packet was received, only the stored time. +unsigned int DTSC::Stream::getTime(){ + return buffers.front().getContentP("time")->NumValue(); +} + /// Attempts to parse a packet from the given std::string buffer. /// Returns true if successful, removing the parsed part from the buffer string. /// Returns false if invalid or not enough data is in the buffer. diff --git a/util/dtsc.h b/util/dtsc.h index 3d690d53..f721e6c4 100644 --- a/util/dtsc.h +++ b/util/dtsc.h @@ -129,6 +129,7 @@ namespace DTSC{ std::string & outPacket(unsigned int num); std::string & outHeader(); Ring * getRing(); + unsigned int getTime(); void dropRing(Ring * ptr); private: std::deque buffers; From 7f979c7457f2facf1a32bb2d049020e063498e5f Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sat, 7 Apr 2012 23:45:03 +0200 Subject: [PATCH 179/788] Restructured some for clarity, got rid of the crappy JSON library, general awesomeness. --- util/auth.cpp | 45 + util/auth.h | 11 + util/base64.cpp | 64 ++ util/base64.h | 11 + util/config.cpp | 150 +++ util/config.h | 37 + util/json.cpp | 421 ++++++++ util/json.h | 73 ++ util/json/autolink.h | 19 - util/json/config.h | 43 - util/json/features.h | 42 - util/json/forwards.h | 39 - util/json/json.h | 10 - util/json/json_batchallocator.h | 125 --- util/json/json_internalarray.inl | 448 -------- util/json/json_internalmap.inl | 607 ----------- util/json/json_reader.cpp | 885 --------------- util/json/json_value.cpp | 1718 ------------------------------ util/json/json_valueiterator.inl | 292 ----- util/json/json_writer.cpp | 829 -------------- util/json/reader.h | 196 ---- util/json/value.h | 1069 ------------------- util/json/writer.h | 174 --- util/{util.cpp => procs.cpp} | 136 +-- util/{util.h => procs.h} | 32 +- util/server_setup.cpp | 2 +- 26 files changed, 818 insertions(+), 6660 deletions(-) create mode 100644 util/auth.cpp create mode 100644 util/auth.h create mode 100644 util/base64.cpp create mode 100644 util/base64.h create mode 100644 util/config.cpp create mode 100644 util/config.h create mode 100644 util/json.cpp create mode 100644 util/json.h delete mode 100644 util/json/autolink.h delete mode 100644 util/json/config.h delete mode 100644 util/json/features.h delete mode 100644 util/json/forwards.h delete mode 100644 util/json/json.h delete mode 100644 util/json/json_batchallocator.h delete mode 100644 util/json/json_internalarray.inl delete mode 100644 util/json/json_internalmap.inl delete mode 100644 util/json/json_reader.cpp delete mode 100644 util/json/json_value.cpp delete mode 100644 util/json/json_valueiterator.inl delete mode 100644 util/json/json_writer.cpp delete mode 100644 util/json/reader.h delete mode 100644 util/json/value.h delete mode 100644 util/json/writer.h rename util/{util.cpp => procs.cpp} (55%) rename util/{util.h => procs.h} (55%) diff --git a/util/auth.cpp b/util/auth.cpp new file mode 100644 index 00000000..14be28f2 --- /dev/null +++ b/util/auth.cpp @@ -0,0 +1,45 @@ +#include "auth.h" +#include "base64.h" + +static unsigned char __gbv2keypub_der[] = { + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, + 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xe5, 0xd7, 0x9c, + 0x7d, 0x73, 0xc6, 0xe6, 0xfb, 0x35, 0x7e, 0xd7, 0x57, 0x99, 0x07, 0xdb, + 0x99, 0x70, 0xc9, 0xd0, 0x3e, 0x53, 0x57, 0x3c, 0x1e, 0x55, 0xda, 0x0f, + 0x69, 0xbf, 0x26, 0x79, 0xc7, 0xb6, 0xdd, 0x8e, 0x83, 0x32, 0x65, 0x74, + 0x0d, 0x74, 0x48, 0x42, 0x49, 0x22, 0x52, 0x58, 0x56, 0xc3, 0xe4, 0x49, + 0x5d, 0xac, 0x6a, 0x94, 0xb1, 0x64, 0x14, 0xbf, 0x4d, 0xd5, 0xd7, 0x3a, + 0xca, 0x5c, 0x1e, 0x6f, 0x42, 0x30, 0xac, 0x29, 0xaa, 0xa0, 0x85, 0xd2, + 0x16, 0xa2, 0x8e, 0x89, 0x12, 0xc4, 0x92, 0x06, 0xea, 0xed, 0x48, 0xf6, + 0xdb, 0xed, 0x4f, 0x62, 0x6c, 0xfa, 0xcf, 0xc2, 0xb9, 0x8d, 0x04, 0xb2, + 0xba, 0x63, 0xc9, 0xcc, 0xee, 0x23, 0x64, 0x46, 0x14, 0x12, 0xc8, 0x38, + 0x67, 0x69, 0x6b, 0xaf, 0xd1, 0x7c, 0xb1, 0xb5, 0x79, 0xe4, 0x4e, 0x3a, + 0xa7, 0xe8, 0x28, 0x89, 0x25, 0xc0, 0xd0, 0xd8, 0xc7, 0xd2, 0x26, 0xaa, + 0xf5, 0xbf, 0x36, 0x55, 0x01, 0x89, 0x58, 0x1f, 0x1e, 0xf5, 0xa5, 0x42, + 0x8f, 0x60, 0x2e, 0xc2, 0xd8, 0x21, 0x0b, 0x6c, 0x8d, 0xbb, 0x72, 0xf2, + 0x19, 0x30, 0xe3, 0x4c, 0x3e, 0x80, 0xe7, 0xf2, 0xe3, 0x89, 0x4f, 0xd4, + 0xee, 0x96, 0x3e, 0x4a, 0x9b, 0xe5, 0x16, 0x01, 0xf1, 0x98, 0xc9, 0x0b, + 0xd6, 0xdf, 0x8a, 0x64, 0x47, 0xc4, 0x44, 0xcc, 0x92, 0x69, 0x28, 0xee, + 0x7d, 0xac, 0xdc, 0x30, 0x56, 0x3a, 0xe7, 0xbc, 0xba, 0x45, 0x16, 0x2c, + 0x4c, 0x46, 0x6b, 0x2b, 0x20, 0xfb, 0x3d, 0x20, 0x35, 0xbb, 0x48, 0x49, + 0x13, 0x65, 0xc9, 0x9a, 0x38, 0x10, 0x84, 0x1a, 0x8c, 0xc9, 0xd7, 0xde, + 0x07, 0x10, 0x5a, 0xfb, 0xb4, 0x95, 0xae, 0x18, 0xf2, 0xe3, 0x15, 0xe8, + 0xad, 0x7e, 0xe5, 0x3c, 0xa8, 0x47, 0x85, 0xd6, 0x1f, 0x54, 0xb5, 0xa3, + 0x79, 0x02, 0x03, 0x01, 0x00, 0x01 +}; ///< The GBv2 public key file. +static unsigned int __gbv2keypub_der_len = 294; ///< Length of GBv2 public key data + +/// Attempts to load the GBv2 public key. +Auth::Auth(){ + const unsigned char * key = __gbv2keypub_der; + pubkey = d2i_RSAPublicKey(0, &key, __gbv2keypub_der_len); +} + +/// Attempts to verify RSA signature using the public key. +/// Assumes basesign argument is base64 encoded RSA signature for data. +/// Returns true if the data could be verified, false otherwise. +bool Auth::PubKey_Check(std::string & data, std::string basesign){ + std::string sign = Base64::decode(basesign); + return (RSA_verify(NID_md5, (unsigned char*)data.c_str(), data.size(), (unsigned char*)sign.c_str(), sign.size(), pubkey) == 1); +} diff --git a/util/auth.h b/util/auth.h new file mode 100644 index 00000000..5d8da7b6 --- /dev/null +++ b/util/auth.h @@ -0,0 +1,11 @@ +#include +#include +#include + +class Auth{ + private: + RSA * pubkey; ///< Holds the public key. + public: + Auth(); ///< Attempts to load the GBv2 public key. + bool PubKey_Check(std::string & data, std::string basesign); ///< Attempts to verify RSA signature using the public key. +}; diff --git a/util/base64.cpp b/util/base64.cpp new file mode 100644 index 00000000..0111823f --- /dev/null +++ b/util/base64.cpp @@ -0,0 +1,64 @@ +#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 input Base64 encoded data to decode. +/// \returns Plaintext decoded data. +std::string Base64::decode(std::string const& encoded_string) { + int in_len = encoded_string.size(); + int i = 0; + int j = 0; + int in_ = 0; + unsigned char char_array_4[4], char_array_3[3]; + std::string ret; + while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { + char_array_4[i++] = encoded_string[in_]; in_++; + if (i ==4) { + for (i = 0; i <4; i++){char_array_4[i] = chars.find(char_array_4[i]);} + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + for (i = 0; (i < 3); i++){ret += char_array_3[i];} + i = 0; + } + } + if (i) { + for (j = i; j <4; j++){char_array_4[j] = 0;} + for (j = 0; j <4; j++){char_array_4[j] = chars.find(char_array_4[j]);} + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; + } + return ret; +} diff --git a/util/base64.h b/util/base64.h new file mode 100644 index 00000000..2358ae98 --- /dev/null +++ b/util/base64.h @@ -0,0 +1,11 @@ +#include + +/// Holds base64 decoding and encoding functions. +class Base64{ + private: + static const std::string chars; + static inline bool is_base64(unsigned char c); + public: + static std::string encode(std::string const input); + static std::string decode(std::string const& encoded_string); +}; diff --git a/util/config.cpp b/util/config.cpp new file mode 100644 index 00000000..6d56ab7f --- /dev/null +++ b/util/config.cpp @@ -0,0 +1,150 @@ +/// \file config.cpp +/// Contains generic functions for managing configuration. + +#include "config.h" +#include +#include +#include + +#ifdef __FreeBSD__ +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/// Creates a new configuration manager. +Util::Config::Config(){ + listen_port = 4242; + daemon_mode = true; + interface = "0.0.0.0"; + configfile = "/etc/ddvtech.conf"; + username = "root"; + ignore_daemon = false; + ignore_interface = false; + ignore_port = false; + ignore_user = false; +} + +/// Parses commandline arguments. +/// Calls exit if an unknown option is encountered, printing a help message. +/// confsection must be either already set or never be set at all when this function is called. +/// In other words: do not change confsection after calling this function. +void Util::Config::parseArgs(int argc, char ** argv){ + int opt = 0; + static const char *optString = "ndvp:i:u:c:h?"; + static const struct option longOpts[] = { + {"help",0,0,'h'}, + {"port",1,0,'p'}, + {"interface",1,0,'i'}, + {"username",1,0,'u'}, + {"no-daemon",0,0,'n'}, + {"daemon",0,0,'d'}, + {"configfile",1,0,'c'}, + {"version",0,0,'v'} + }; + while ((opt = getopt_long(argc, argv, optString, longOpts, 0)) != -1){ + switch (opt){ + case 'p': listen_port = atoi(optarg); ignore_port = true; break; + case 'i': interface = optarg; ignore_interface = true; break; + case 'n': daemon_mode = false; ignore_daemon = true; break; + case 'd': daemon_mode = true; ignore_daemon = true; break; + case 'c': configfile = optarg; break; + case 'u': username = optarg; ignore_user = true; break; + case 'v': + printf("%s\n", TOSTRING(VERSION)); + exit(1); + break; + case 'h': + case '?': + std::string doingdaemon = "true"; + if (!daemon_mode){doingdaemon = "false";} + if (confsection == ""){ + printf("Options: -h[elp], -?, -v[ersion], -n[odaemon], -d[aemon], -p[ort] VAL, -i[nterface] VAL, -u[sername] VAL\n"); + printf("Defaults:\n interface: %s\n port: %i\n daemon mode: %s\n username: %s\n", interface.c_str(), listen_port, doingdaemon.c_str(), username.c_str()); + }else{ + printf("Options: -h[elp], -?, -v[ersion], -n[odaemon], -d[aemon], -p[ort] VAL, -i[nterface] VAL, -c[onfigfile] VAL, -u[sername] VAL\n"); + printf("Defaults:\n interface: %s\n port: %i\n daemon mode: %s\n configfile: %s\n username: %s\n", interface.c_str(), listen_port, doingdaemon.c_str(), configfile.c_str(), username.c_str()); + printf("Username root means no change to UID, no matter what the UID is.\n"); + printf("If the configfile exists, it is always loaded first. Commandline settings then overwrite the config file.\n"); + printf("\nThis process takes it directives from the %s section of the configfile.\n", confsection.c_str()); + } + printf("This is %s version %s\n", argv[0], TOSTRING(VERSION)); + exit(1); + break; + } + }//commandline options parser +} + +/// Parses the configuration file at configfile, if it exists. +/// Assumes confsection is set. +void Util::Config::parseFile(){ + std::ifstream conf(configfile.c_str(), std::ifstream::in); + std::string tmpstr; + bool acc_comm = false; + size_t foundeq; + if (conf.fail()){ + #if DEBUG >= 3 + fprintf(stderr, "Configuration file %s not found - using build-in defaults...\n", configfile.c_str()); + #endif + }else{ + while (conf.good()){ + getline(conf, tmpstr); + if (tmpstr[0] == '['){//new section? check if we care. + if (tmpstr == confsection){acc_comm = true;}else{acc_comm = false;} + }else{ + if (!acc_comm){break;}//skip all lines in this section if we do not care about it + foundeq = tmpstr.find('='); + if (foundeq != std::string::npos){ + if ((tmpstr.substr(0, foundeq) == "port") && !ignore_port){listen_port = atoi(tmpstr.substr(foundeq+1).c_str());} + if ((tmpstr.substr(0, foundeq) == "interface") && !ignore_interface){interface = tmpstr.substr(foundeq+1);} + if ((tmpstr.substr(0, foundeq) == "username") && !ignore_user){username = tmpstr.substr(foundeq+1);} + if ((tmpstr.substr(0, foundeq) == "daemon") && !ignore_daemon){daemon_mode = true;} + if ((tmpstr.substr(0, foundeq) == "nodaemon") && !ignore_daemon){daemon_mode = false;} + }//found equals sign + }//section contents + }//configfile line loop + }//configuration +} + +/// Sets the current process' running user +void Util::setUser(std::string username){ + if (username != "root"){ + struct passwd * user_info = getpwnam(username.c_str()); + if (!user_info){ + #if DEBUG >= 1 + fprintf(stderr, "Error: could not setuid %s: could not get PID\n", username.c_str()); + #endif + return; + }else{ + if (setuid(user_info->pw_uid) != 0){ + #if DEBUG >= 1 + fprintf(stderr, "Error: could not setuid %s: not allowed\n", username.c_str()); + #endif + }else{ + #if DEBUG >= 3 + fprintf(stderr, "Changed user to %s\n", username.c_str()); + #endif + } + } + } +} + +/// Will turn the current process into a daemon. +/// Works by calling daemon(1,0): +/// Does not change directory to root. +/// Does redirect output to /dev/null +void Util::Daemonize(){ + #if DEBUG >= 3 + fprintf(stderr, "Going into background mode...\n"); + #endif + daemon(1, 0); +} diff --git a/util/config.h b/util/config.h new file mode 100644 index 00000000..5a0b0da9 --- /dev/null +++ b/util/config.h @@ -0,0 +1,37 @@ +/// \file config.h +/// Contains generic function headers for managing configuration. + +#include + +#define STRINGIFY(x) #x +#define TOSTRING(x) STRINGIFY(x) + +/// Contains utility code, not directly related to streaming media +namespace Util{ + + /// Deals with parsing configuration from files or commandline options. + class Config{ + private: + bool ignore_daemon; + bool ignore_interface; + bool ignore_port; + bool ignore_user; + public: + std::string confsection; + std::string configfile; + bool daemon_mode; + std::string interface; + int listen_port; + std::string username; + Config(); + void parseArgs(int argc, char ** argv); + void parseFile(); + }; + + /// Will set the active user to the named username. + void setUser(std::string user); + + /// Will turn the current process into a daemon. + void Daemonize(); + +}; diff --git a/util/json.cpp b/util/json.cpp new file mode 100644 index 00000000..7542ed37 --- /dev/null +++ b/util/json.cpp @@ -0,0 +1,421 @@ +/// \file json.cpp Holds all JSON-related code. + +#include "json.h" +#include + +int JSON::Value::c2hex(int c){ + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'a' && c <= 'f') return c - 'a' + 10; + if (c >= 'A' && c <= 'F') return c - 'A' + 10; + return 0; +} + + +std::string JSON::Value::read_string(int separator, std::istream & fromstream){ + std::string out; + bool escaped = false; + while (fromstream.good()){ + int c = fromstream.get(); + if (c == '\\'){ + escaped = true; + continue; + } + if (escaped){ + switch (c){ + case 'b': out += '\b'; break; + case 'f': out += '\f'; break; + case 'n': out += '\n'; break; + case 'r': out += '\r'; break; + case 't': out += '\t'; break; + case 'u':{ + int d1 = fromstream.get(); + int d2 = fromstream.get(); + int d3 = fromstream.get(); + int d4 = fromstream.get(); + c = c2hex(d4) + (c2hex(d3) << 4) + (c2hex(d2) << 8) + (c2hex(d1) << 16); + } + default: + out += (char)c; + break; + } + }else{ + if (c == separator){ + return out; + }else{ + out += (char)c; + } + } + } + return out; +} + +std::string JSON::Value::string_escape(std::string val){ + std::string out = "\""; + for (unsigned int i = 0; i < val.size(); ++i){ + switch (val[i]){ + case '"': out += "\\\""; break; + case '\\': out += "\\\\"; break; + case '\n': out += "\\n"; break; + case '\b': out += "\\b"; break; + case '\f': out += "\\f"; break; + case '\r': out += "\\r"; break; + case '\t': out += "\\t"; break; + default: out += val[i]; + } + } + out += "\""; + return out; +} + + +/// Sets this JSON::Value to null; +JSON::Value::Value(){ + null(); +} + +/// Sets this JSON::Value to read from this position in the std::istream +JSON::Value::Value(std::istream & fromstream){ + null(); + bool reading_object = false; + bool reading_obj_name = false; + bool reading_array = false; + while (fromstream.good()){ + int c = fromstream.peek(); + switch (c){ + case '{': + reading_object = true; + reading_obj_name = true; + c = fromstream.get(); + myType = OBJECT; + break; + case '[': + reading_array = true; + c = fromstream.get(); + myType = ARRAY; + append(JSON::Value(fromstream)); + break; + case '\'': + case '"': + c = fromstream.get(); + if (!reading_object || !reading_obj_name){ + myType = STRING; + strVal = read_string(c, fromstream); + return; + }else{ + std::string tmpstr = read_string(c, fromstream); + (*this)[tmpstr] = JSON::Value(fromstream); + } + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + c = fromstream.get(); + myType = INTEGER; + intVal *= 10; + intVal += c - '0'; + break; + case ',': + if (!reading_object && !reading_array) return; + c = fromstream.get(); + if (reading_object){ + reading_obj_name = true; + }else{ + append(JSON::Value(fromstream)); + } + break; + case '}': + if (reading_object){c = fromstream.get();} + return; + break; + case ']': + if (reading_array){c = fromstream.get();} + return; + break; + case 't': + case 'T': + myType = BOOL; + intVal = 1; + return; + break; + case 'f': + case 'F': + myType = BOOL; + intVal = 0; + return; + break; + case 'n': + case 'N': + myType = EMPTY; + return; + break; + default: + c = fromstream.get();//ignore this character + continue; + break; + } + } +} + +/// Sets this JSON::Value to the given string. +JSON::Value::Value(const std::string & val){ + myType = STRING; + strVal = val; +} + +/// Sets this JSON::Value to the given string. +JSON::Value::Value(const char * val){ + myType = STRING; + strVal = val; +} + +/// Sets this JSON::Value to the given integer. +JSON::Value::Value(long long int val){ + myType = INTEGER; + intVal = val; +} + +/// Compares a JSON::Value to another for equality. +bool JSON::Value::operator==(const JSON::Value & rhs) const{ + if (myType != rhs.myType) return false; + if (myType == INTEGER || myType == BOOL){return intVal == rhs.intVal;} + if (myType == STRING){return strVal == rhs.strVal;} + if (myType == EMPTY){return true;} + if (myType == OBJECT){ + if (objVal.size() != rhs.objVal.size()) return false; + for (std::map::const_iterator it = objVal.begin(); it != objVal.end(); ++it){ + if (!rhs.isMember(it->first)){return false;} + if (it->second != rhs.objVal.find(it->first)->second){return false;} + } + return true; + } + return true; +} + +/// Compares a JSON::Value to another for equality. +bool JSON::Value::operator!=(const JSON::Value & rhs) const{ + return !((*this) == rhs); +} + +/// Sets this JSON::Value to the given boolean. +JSON::Value & JSON::Value::operator=(const bool &rhs){ + null(); + myType = BOOL; + if (rhs) intVal = 1; + return *this; +} + +/// Sets this JSON::Value to the given string. +JSON::Value & JSON::Value::operator=(const std::string &rhs){ + null(); + myType = STRING; + strVal = rhs; + return *this; +} + +/// Sets this JSON::Value to the given string. +JSON::Value & JSON::Value::operator=(const char * rhs){ + return ((*this) = (std::string)rhs); +} + +/// Sets this JSON::Value to the given integer. +JSON::Value & JSON::Value::operator=(const long long int &rhs){ + null(); + myType = INTEGER; + intVal = rhs; + return *this; +} + +/// Sets this JSON::Value to the given integer. +JSON::Value & JSON::Value::operator=(const int &rhs){ + return ((*this) = (long long int)rhs); +} + +/// Sets this JSON::Value to the given integer. +JSON::Value & JSON::Value::operator=(const unsigned int &rhs){ + return ((*this) = (long long int)rhs); +} + +/// Automatic conversion to long long int - returns 0 if not an integer type. +JSON::Value::operator long long int(){ + return intVal; +} + + +/// Automatic conversion to std::string. +/// Returns the raw string value if available, otherwise calls toString(). +JSON::Value::operator std::string(){ + if (myType == STRING){ + return strVal; + }else{ + return toString(); + } +} + +/// Retrieves or sets the JSON::Value at this position in the object. +/// Converts destructively to object if not already an object. +JSON::Value & JSON::Value::operator[](const std::string i){ + if (myType != OBJECT){ + null(); + myType = OBJECT; + } + return objVal[i]; +} + +/// Retrieves or sets the JSON::Value at this position in the object. +/// Converts destructively to object if not already an object. +JSON::Value & JSON::Value::operator[](const char * i){ + if (myType != OBJECT){ + null(); + myType = OBJECT; + } + return objVal[i]; +} + +/// Retrieves or sets the JSON::Value at this position in the array. +/// Converts destructively to array if not already an array. +JSON::Value & JSON::Value::operator[](unsigned int i){ + if (myType != ARRAY){ + null(); + myType = ARRAY; + } + while (i >= arrVal.size()){ + append(JSON::Value()); + } + return arrVal[i]; +} + +/// Converts this JSON::Value to valid JSON notation and returns it. +/// Makes absolutely no attempts to pretty-print anything. :-) +std::string JSON::Value::toString(){ + switch (myType){ + case INTEGER: { + std::stringstream st; + st << intVal; + return st.str(); + break; + } + case STRING: { + return string_escape(strVal); + break; + } + case ARRAY: { + std::string tmp = "["; + for (ArrIter it = ArrBegin(); it != ArrEnd(); it++){ + tmp += it->toString(); + if (it + 1 != ArrEnd()){tmp += ",";} + } + tmp += "]"; + return tmp; + break; + } + case OBJECT: { + std::string tmp2 = "{"; + ObjIter it3 = ObjEnd(); + --it3; + for (ObjIter it2 = ObjBegin(); it2 != ObjEnd(); it2++){ + tmp2 += "\"" + it2->first + "\":"; + tmp2 += it2->second.toString(); + if (it2 != it3){tmp2 += ",";} + } + tmp2 += "}"; + return tmp2; + break; + } + case EMPTY: + default: + return "null"; + } + return "null";//should never get here... +} + +/// Appends the given value to the end of this JSON::Value array. +/// Turns this value into an array if it is not already one. +void JSON::Value::append(const JSON::Value & rhs){ + if (myType != ARRAY){ + null(); + myType = ARRAY; + } + arrVal.push_back(rhs); +} + +/// Prepends the given value to the beginning of this JSON::Value array. +/// Turns this value into an array if it is not already one. +void JSON::Value::prepend(const JSON::Value & rhs){ + if (myType != ARRAY){ + null(); + myType = ARRAY; + } + arrVal.push_front(rhs); +} + +/// For array and object JSON::Value objects, reduces them +/// so they contain at most size elements, throwing away +/// the first elements and keeping the last ones. +/// Does nothing for other JSON::Value types, nor does it +/// do anything if the size is already lower or equal to the +/// given size. +void JSON::Value::shrink(unsigned int size){ + if (myType == ARRAY){ + while (arrVal.size() > size){arrVal.pop_front();} + return; + } + if (myType == OBJECT){ + while (objVal.size() > size){objVal.erase(objVal.begin());} + return; + } +} + +/// For object JSON::Value objects, removes the member with +/// the given name, if it exists. Has no effect otherwise. +void JSON::Value::removeMember(const std::string & name){ + objVal.erase(name); +} + +/// For object JSON::Value objects, returns true if the +/// given name is a member. Returns false otherwise. +bool JSON::Value::isMember(const std::string & name) const{ + return objVal.count(name) > 0; +} + +/// Returns an iterator to the begin of the object map, if any. +JSON::ObjIter JSON::Value::ObjBegin(){ + return objVal.begin(); +} + +/// Returns an iterator to the end of the object map, if any. +JSON::ObjIter JSON::Value::ObjEnd(){ + return objVal.end(); +} + +/// Returns an iterator to the begin of the array, if any. +JSON::ArrIter JSON::Value::ArrBegin(){ + return arrVal.begin(); +} + +/// Returns an iterator to the end of the array, if any. +JSON::ArrIter JSON::Value::ArrEnd(){ + return arrVal.end(); +} + +/// Completely clears the contents of this value, +/// changing its type to NULL in the process. +void JSON::Value::null(){ + objVal.clear(); + arrVal.clear(); + strVal.clear(); + intVal = 0; + myType = EMPTY; +} + +/// Converts a std::string to a JSON::Value. +JSON::Value JSON::fromString(std::string json){ + std::istringstream is(json); + return JSON::Value(is); +} diff --git a/util/json.h b/util/json.h new file mode 100644 index 00000000..8de01e11 --- /dev/null +++ b/util/json.h @@ -0,0 +1,73 @@ +/// \file json.h Holds all JSON-related headers. + +#include +#include +#include +#include + +/// JSON-related classes and functions +namespace JSON{ + + /// Lists all types of JSON::Value. + enum ValueType{ EMPTY, BOOL, INTEGER, STRING, ARRAY, OBJECT }; + + class Value;//forward declaration for below typedef + + typedef std::map::iterator ObjIter; + typedef std::deque::iterator ArrIter; + + /// A JSON::Value is either a string or an integer, but may also be an object, array or null. + class Value{ + private: + ValueType myType; + long long int intVal; + std::string strVal; + std::deque arrVal; + std::map objVal; + std::string read_string(int separator, std::istream & fromstream); + std::string string_escape(std::string val); + int c2hex(int c); + public: + //constructors + Value(); + Value(std::istream & fromstream); + Value(const std::string & val); + Value(const char * val); + Value(long long int val); + Value(bool val); + //comparison operators + bool operator==(const Value &rhs) const; + bool operator!=(const Value &rhs) const; + //assignment operators + Value & operator=(const std::string &rhs); + Value & operator=(const char * rhs); + Value & operator=(const long long int &rhs); + Value & operator=(const int &rhs); + Value & operator=(const unsigned int &rhs); + Value & operator=(const bool &rhs); + //converts to basic types + operator long long int(); + operator std::string(); + operator bool(); + //array operator for maps and arrays + Value & operator[](const std::string i); + Value & operator[](const char * i); + Value & operator[](unsigned int i); + //handy functions and others + std::string toString(); + void append(const Value & rhs); + void prepend(const Value & rhs); + void shrink(unsigned int size); + void removeMember(const std::string & name); + bool isMember(const std::string & name) const; + ObjIter ObjBegin(); + ObjIter ObjEnd(); + ArrIter ArrBegin(); + ArrIter ArrEnd(); + unsigned int size(); + void null(); + }; + + Value fromString(std::string json); + +}; diff --git a/util/json/autolink.h b/util/json/autolink.h deleted file mode 100644 index 37c9258e..00000000 --- a/util/json/autolink.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef JSON_AUTOLINK_H_INCLUDED -# define JSON_AUTOLINK_H_INCLUDED - -# include "config.h" - -# ifdef JSON_IN_CPPTL -# include -# endif - -# if !defined(JSON_NO_AUTOLINK) && !defined(JSON_DLL_BUILD) && !defined(JSON_IN_CPPTL) -# define CPPTL_AUTOLINK_NAME "json" -# undef CPPTL_AUTOLINK_DLL -# ifdef JSON_DLL -# define CPPTL_AUTOLINK_DLL -# endif -# include "autolink.h" -# endif - -#endif // JSON_AUTOLINK_H_INCLUDED diff --git a/util/json/config.h b/util/json/config.h deleted file mode 100644 index 5d334cbc..00000000 --- a/util/json/config.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef JSON_CONFIG_H_INCLUDED -# define JSON_CONFIG_H_INCLUDED - -/// If defined, indicates that json library is embedded in CppTL library. -//# define JSON_IN_CPPTL 1 - -/// If defined, indicates that json may leverage CppTL library -//# define JSON_USE_CPPTL 1 -/// If defined, indicates that cpptl vector based map should be used instead of std::map -/// as Value container. -//# define JSON_USE_CPPTL_SMALLMAP 1 -/// If defined, indicates that Json specific container should be used -/// (hash table & simple deque container with customizable allocator). -/// THIS FEATURE IS STILL EXPERIMENTAL! -//# define JSON_VALUE_USE_INTERNAL_MAP 1 -/// Force usage of standard new/malloc based allocator instead of memory pool based allocator. -/// The memory pools allocator used optimization (initializing Value and ValueInternalLink -/// as if it was a POD) that may cause some validation tool to report errors. -/// Only has effects if JSON_VALUE_USE_INTERNAL_MAP is defined. -//# define JSON_USE_SIMPLE_INTERNAL_ALLOCATOR 1 - -/// If defined, indicates that Json use exception to report invalid type manipulation -/// instead of C assert macro. -# define JSON_USE_EXCEPTION 1 - -# ifdef JSON_IN_CPPTL -# include -# ifndef JSON_USE_CPPTL -# define JSON_USE_CPPTL 1 -# endif -# endif - -# ifdef JSON_IN_CPPTL -# define JSON_API CPPTL_API -# elif defined(JSON_DLL_BUILD) -# define JSON_API __declspec(dllexport) -# elif defined(JSON_DLL) -# define JSON_API __declspec(dllimport) -# else -# define JSON_API -# endif - -#endif // JSON_CONFIG_H_INCLUDED diff --git a/util/json/features.h b/util/json/features.h deleted file mode 100644 index 5a9adec1..00000000 --- a/util/json/features.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef CPPTL_JSON_FEATURES_H_INCLUDED -# define CPPTL_JSON_FEATURES_H_INCLUDED - -# include "forwards.h" - -namespace Json { - - /** \brief Configuration passed to reader and writer. - * This configuration object can be used to force the Reader or Writer - * to behave in a standard conforming way. - */ - class JSON_API Features - { - public: - /** \brief A configuration that allows all features and assumes all strings are UTF-8. - * - C & C++ comments are allowed - * - Root object can be any JSON value - * - Assumes Value strings are encoded in UTF-8 - */ - static Features all(); - - /** \brief A configuration that is strictly compatible with the JSON specification. - * - Comments are forbidden. - * - Root object must be either an array or an object value. - * - Assumes Value strings are encoded in UTF-8 - */ - static Features strictMode(); - - /** \brief Initialize the configuration like JsonConfig::allFeatures; - */ - Features(); - - /// \c true if comments are allowed. Default: \c true. - bool allowComments_; - - /// \c true if root must be either an array or an object value. Default: \c false. - bool strictRoot_; - }; - -} // namespace Json - -#endif // CPPTL_JSON_FEATURES_H_INCLUDED diff --git a/util/json/forwards.h b/util/json/forwards.h deleted file mode 100644 index d0ce8300..00000000 --- a/util/json/forwards.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef JSON_FORWARDS_H_INCLUDED -# define JSON_FORWARDS_H_INCLUDED - -# include "config.h" - -namespace Json { - - // writer.h - class FastWriter; - class StyledWriter; - - // reader.h - class Reader; - - // features.h - class Features; - - // value.h - typedef int Int; - typedef unsigned int UInt; - class StaticString; - class Path; - class PathArgument; - class Value; - class ValueIteratorBase; - class ValueIterator; - class ValueConstIterator; -#ifdef JSON_VALUE_USE_INTERNAL_MAP - class ValueAllocator; - class ValueMapAllocator; - class ValueInternalLink; - class ValueInternalArray; - class ValueInternalMap; -#endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP - -} // namespace Json - - -#endif // JSON_FORWARDS_H_INCLUDED diff --git a/util/json/json.h b/util/json/json.h deleted file mode 100644 index c71ed65a..00000000 --- a/util/json/json.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef JSON_JSON_H_INCLUDED -# define JSON_JSON_H_INCLUDED - -# include "autolink.h" -# include "value.h" -# include "reader.h" -# include "writer.h" -# include "features.h" - -#endif // JSON_JSON_H_INCLUDED diff --git a/util/json/json_batchallocator.h b/util/json/json_batchallocator.h deleted file mode 100644 index 87ea5ed8..00000000 --- a/util/json/json_batchallocator.h +++ /dev/null @@ -1,125 +0,0 @@ -#ifndef JSONCPP_BATCHALLOCATOR_H_INCLUDED -# define JSONCPP_BATCHALLOCATOR_H_INCLUDED - -# include -# include - -# ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION - -namespace Json { - -/* Fast memory allocator. - * - * This memory allocator allocates memory for a batch of object (specified by - * the page size, the number of object in each page). - * - * It does not allow the destruction of a single object. All the allocated objects - * can be destroyed at once. The memory can be either released or reused for future - * allocation. - * - * The in-place new operator must be used to construct the object using the pointer - * returned by allocate. - */ -template -class BatchAllocator -{ -public: - typedef AllocatedType Type; - - BatchAllocator( unsigned int objectsPerPage = 255 ) - : freeHead_( 0 ) - , objectsPerPage_( objectsPerPage ) - { -// printf( "Size: %d => %s\n", sizeof(AllocatedType), typeid(AllocatedType).name() ); - assert( sizeof(AllocatedType) * objectPerAllocation >= sizeof(AllocatedType *) ); // We must be able to store a slist in the object free space. - assert( objectsPerPage >= 16 ); - batches_ = allocateBatch( 0 ); // allocated a dummy page - currentBatch_ = batches_; - } - - ~BatchAllocator() - { - for ( BatchInfo *batch = batches_; batch; ) - { - BatchInfo *nextBatch = batch->next_; - free( batch ); - batch = nextBatch; - } - } - - /// allocate space for an array of objectPerAllocation object. - /// @warning it is the responsability of the caller to call objects constructors. - AllocatedType *allocate() - { - if ( freeHead_ ) // returns node from free list. - { - AllocatedType *object = freeHead_; - freeHead_ = *(AllocatedType **)object; - return object; - } - if ( currentBatch_->used_ == currentBatch_->end_ ) - { - currentBatch_ = currentBatch_->next_; - while ( currentBatch_ && currentBatch_->used_ == currentBatch_->end_ ) - currentBatch_ = currentBatch_->next_; - - if ( !currentBatch_ ) // no free batch found, allocate a new one - { - currentBatch_ = allocateBatch( objectsPerPage_ ); - currentBatch_->next_ = batches_; // insert at the head of the list - batches_ = currentBatch_; - } - } - AllocatedType *allocated = currentBatch_->used_; - currentBatch_->used_ += objectPerAllocation; - return allocated; - } - - /// Release the object. - /// @warning it is the responsability of the caller to actually destruct the object. - void release( AllocatedType *object ) - { - assert( object != 0 ); - *(AllocatedType **)object = freeHead_; - freeHead_ = object; - } - -private: - struct BatchInfo - { - BatchInfo *next_; - AllocatedType *used_; - AllocatedType *end_; - AllocatedType buffer_[objectPerAllocation]; - }; - - // disabled copy constructor and assignement operator. - BatchAllocator( const BatchAllocator & ); - void operator =( const BatchAllocator &); - - static BatchInfo *allocateBatch( unsigned int objectsPerPage ) - { - const unsigned int mallocSize = sizeof(BatchInfo) - sizeof(AllocatedType)* objectPerAllocation - + sizeof(AllocatedType) * objectPerAllocation * objectsPerPage; - BatchInfo *batch = static_cast( malloc( mallocSize ) ); - batch->next_ = 0; - batch->used_ = batch->buffer_; - batch->end_ = batch->buffer_ + objectsPerPage; - return batch; - } - - BatchInfo *batches_; - BatchInfo *currentBatch_; - /// Head of a single linked list within the allocated space of freeed object - AllocatedType *freeHead_; - unsigned int objectsPerPage_; -}; - - -} // namespace Json - -# endif // ifndef JSONCPP_DOC_INCLUDE_IMPLEMENTATION - -#endif // JSONCPP_BATCHALLOCATOR_H_INCLUDED - diff --git a/util/json/json_internalarray.inl b/util/json/json_internalarray.inl deleted file mode 100644 index 9b985d25..00000000 --- a/util/json/json_internalarray.inl +++ /dev/null @@ -1,448 +0,0 @@ -// included by json_value.cpp -// everything is within Json namespace - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// class ValueInternalArray -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// - -ValueArrayAllocator::~ValueArrayAllocator() -{ -} - -// ////////////////////////////////////////////////////////////////// -// class DefaultValueArrayAllocator -// ////////////////////////////////////////////////////////////////// -#ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR -class DefaultValueArrayAllocator : public ValueArrayAllocator -{ -public: // overridden from ValueArrayAllocator - virtual ~DefaultValueArrayAllocator() - { - } - - virtual ValueInternalArray *newArray() - { - return new ValueInternalArray(); - } - - virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) - { - return new ValueInternalArray( other ); - } - - virtual void destructArray( ValueInternalArray *array ) - { - delete array; - } - - virtual void reallocateArrayPageIndex( Value **&indexes, - ValueInternalArray::PageIndex &indexCount, - ValueInternalArray::PageIndex minNewIndexCount ) - { - ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1; - if ( minNewIndexCount > newIndexCount ) - newIndexCount = minNewIndexCount; - void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount ); - if ( !newIndexes ) - throw std::bad_alloc(); - indexCount = newIndexCount; - indexes = static_cast( newIndexes ); - } - virtual void releaseArrayPageIndex( Value **indexes, - ValueInternalArray::PageIndex indexCount ) - { - if ( indexes ) - free( indexes ); - } - - virtual Value *allocateArrayPage() - { - return static_cast( malloc( sizeof(Value) * ValueInternalArray::itemsPerPage ) ); - } - - virtual void releaseArrayPage( Value *value ) - { - if ( value ) - free( value ); - } -}; - -#else // #ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR -/// @todo make this thread-safe (lock when accessign batch allocator) -class DefaultValueArrayAllocator : public ValueArrayAllocator -{ -public: // overridden from ValueArrayAllocator - virtual ~DefaultValueArrayAllocator() - { - } - - virtual ValueInternalArray *newArray() - { - ValueInternalArray *array = arraysAllocator_.allocate(); - new (array) ValueInternalArray(); // placement new - return array; - } - - virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) - { - ValueInternalArray *array = arraysAllocator_.allocate(); - new (array) ValueInternalArray( other ); // placement new - return array; - } - - virtual void destructArray( ValueInternalArray *array ) - { - if ( array ) - { - array->~ValueInternalArray(); - arraysAllocator_.release( array ); - } - } - - virtual void reallocateArrayPageIndex( Value **&indexes, - ValueInternalArray::PageIndex &indexCount, - ValueInternalArray::PageIndex minNewIndexCount ) - { - ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1; - if ( minNewIndexCount > newIndexCount ) - newIndexCount = minNewIndexCount; - void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount ); - if ( !newIndexes ) - throw std::bad_alloc(); - indexCount = newIndexCount; - indexes = static_cast( newIndexes ); - } - virtual void releaseArrayPageIndex( Value **indexes, - ValueInternalArray::PageIndex indexCount ) - { - if ( indexes ) - free( indexes ); - } - - virtual Value *allocateArrayPage() - { - return static_cast( pagesAllocator_.allocate() ); - } - - virtual void releaseArrayPage( Value *value ) - { - if ( value ) - pagesAllocator_.release( value ); - } -private: - BatchAllocator arraysAllocator_; - BatchAllocator pagesAllocator_; -}; -#endif // #ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR - -static ValueArrayAllocator *&arrayAllocator() -{ - static DefaultValueArrayAllocator defaultAllocator; - static ValueArrayAllocator *arrayAllocator = &defaultAllocator; - return arrayAllocator; -} - -static struct DummyArrayAllocatorInitializer { - DummyArrayAllocatorInitializer() - { - arrayAllocator(); // ensure arrayAllocator() statics are initialized before main(). - } -} dummyArrayAllocatorInitializer; - -// ////////////////////////////////////////////////////////////////// -// class ValueInternalArray -// ////////////////////////////////////////////////////////////////// -bool -ValueInternalArray::equals( const IteratorState &x, - const IteratorState &other ) -{ - return x.array_ == other.array_ - && x.currentItemIndex_ == other.currentItemIndex_ - && x.currentPageIndex_ == other.currentPageIndex_; -} - - -void -ValueInternalArray::increment( IteratorState &it ) -{ - JSON_ASSERT_MESSAGE( it.array_ && - (it.currentPageIndex_ - it.array_->pages_)*itemsPerPage + it.currentItemIndex_ - != it.array_->size_, - "ValueInternalArray::increment(): moving iterator beyond end" ); - ++(it.currentItemIndex_); - if ( it.currentItemIndex_ == itemsPerPage ) - { - it.currentItemIndex_ = 0; - ++(it.currentPageIndex_); - } -} - - -void -ValueInternalArray::decrement( IteratorState &it ) -{ - JSON_ASSERT_MESSAGE( it.array_ && it.currentPageIndex_ == it.array_->pages_ - && it.currentItemIndex_ == 0, - "ValueInternalArray::decrement(): moving iterator beyond end" ); - if ( it.currentItemIndex_ == 0 ) - { - it.currentItemIndex_ = itemsPerPage-1; - --(it.currentPageIndex_); - } - else - { - --(it.currentItemIndex_); - } -} - - -Value & -ValueInternalArray::unsafeDereference( const IteratorState &it ) -{ - return (*(it.currentPageIndex_))[it.currentItemIndex_]; -} - - -Value & -ValueInternalArray::dereference( const IteratorState &it ) -{ - JSON_ASSERT_MESSAGE( it.array_ && - (it.currentPageIndex_ - it.array_->pages_)*itemsPerPage + it.currentItemIndex_ - < it.array_->size_, - "ValueInternalArray::dereference(): dereferencing invalid iterator" ); - return unsafeDereference( it ); -} - -void -ValueInternalArray::makeBeginIterator( IteratorState &it ) const -{ - it.array_ = const_cast( this ); - it.currentItemIndex_ = 0; - it.currentPageIndex_ = pages_; -} - - -void -ValueInternalArray::makeIterator( IteratorState &it, ArrayIndex index ) const -{ - it.array_ = const_cast( this ); - it.currentItemIndex_ = index % itemsPerPage; - it.currentPageIndex_ = pages_ + index / itemsPerPage; -} - - -void -ValueInternalArray::makeEndIterator( IteratorState &it ) const -{ - makeIterator( it, size_ ); -} - - -ValueInternalArray::ValueInternalArray() - : pages_( 0 ) - , size_( 0 ) - , pageCount_( 0 ) -{ -} - - -ValueInternalArray::ValueInternalArray( const ValueInternalArray &other ) - : pages_( 0 ) - , pageCount_( 0 ) - , size_( other.size_ ) -{ - PageIndex minNewPages = other.size_ / itemsPerPage; - arrayAllocator()->reallocateArrayPageIndex( pages_, pageCount_, minNewPages ); - JSON_ASSERT_MESSAGE( pageCount_ >= minNewPages, - "ValueInternalArray::reserve(): bad reallocation" ); - IteratorState itOther; - other.makeBeginIterator( itOther ); - Value *value; - for ( ArrayIndex index = 0; index < size_; ++index, increment(itOther) ) - { - if ( index % itemsPerPage == 0 ) - { - PageIndex pageIndex = index / itemsPerPage; - value = arrayAllocator()->allocateArrayPage(); - pages_[pageIndex] = value; - } - new (value) Value( dereference( itOther ) ); - } -} - - -ValueInternalArray & -ValueInternalArray::operator =( const ValueInternalArray &other ) -{ - ValueInternalArray temp( other ); - swap( temp ); - return *this; -} - - -ValueInternalArray::~ValueInternalArray() -{ - // destroy all constructed items - IteratorState it; - IteratorState itEnd; - makeBeginIterator( it); - makeEndIterator( itEnd ); - for ( ; !equals(it,itEnd); increment(it) ) - { - Value *value = &dereference(it); - value->~Value(); - } - // release all pages - PageIndex lastPageIndex = size_ / itemsPerPage; - for ( PageIndex pageIndex = 0; pageIndex < lastPageIndex; ++pageIndex ) - arrayAllocator()->releaseArrayPage( pages_[pageIndex] ); - // release pages index - arrayAllocator()->releaseArrayPageIndex( pages_, pageCount_ ); -} - - -void -ValueInternalArray::swap( ValueInternalArray &other ) -{ - Value **tempPages = pages_; - pages_ = other.pages_; - other.pages_ = tempPages; - ArrayIndex tempSize = size_; - size_ = other.size_; - other.size_ = tempSize; - PageIndex tempPageCount = pageCount_; - pageCount_ = other.pageCount_; - other.pageCount_ = tempPageCount; -} - -void -ValueInternalArray::clear() -{ - ValueInternalArray dummy; - swap( dummy ); -} - - -void -ValueInternalArray::resize( ArrayIndex newSize ) -{ - if ( newSize == 0 ) - clear(); - else if ( newSize < size_ ) - { - IteratorState it; - IteratorState itEnd; - makeIterator( it, newSize ); - makeIterator( itEnd, size_ ); - for ( ; !equals(it,itEnd); increment(it) ) - { - Value *value = &dereference(it); - value->~Value(); - } - PageIndex pageIndex = (newSize + itemsPerPage - 1) / itemsPerPage; - PageIndex lastPageIndex = size_ / itemsPerPage; - for ( ; pageIndex < lastPageIndex; ++pageIndex ) - arrayAllocator()->releaseArrayPage( pages_[pageIndex] ); - size_ = newSize; - } - else if ( newSize > size_ ) - resolveReference( newSize ); -} - - -void -ValueInternalArray::makeIndexValid( ArrayIndex index ) -{ - // Need to enlarge page index ? - if ( index >= pageCount_ * itemsPerPage ) - { - PageIndex minNewPages = (index + 1) / itemsPerPage; - arrayAllocator()->reallocateArrayPageIndex( pages_, pageCount_, minNewPages ); - JSON_ASSERT_MESSAGE( pageCount_ >= minNewPages, "ValueInternalArray::reserve(): bad reallocation" ); - } - - // Need to allocate new pages ? - ArrayIndex nextPageIndex = - (size_ % itemsPerPage) != 0 ? size_ - (size_%itemsPerPage) + itemsPerPage - : size_; - if ( nextPageIndex <= index ) - { - PageIndex pageIndex = nextPageIndex / itemsPerPage; - PageIndex pageToAllocate = (index - nextPageIndex) / itemsPerPage + 1; - for ( ; pageToAllocate-- > 0; ++pageIndex ) - pages_[pageIndex] = arrayAllocator()->allocateArrayPage(); - } - - // Initialize all new entries - IteratorState it; - IteratorState itEnd; - makeIterator( it, size_ ); - size_ = index + 1; - makeIterator( itEnd, size_ ); - for ( ; !equals(it,itEnd); increment(it) ) - { - Value *value = &dereference(it); - new (value) Value(); // Construct a default value using placement new - } -} - -Value & -ValueInternalArray::resolveReference( ArrayIndex index ) -{ - if ( index >= size_ ) - makeIndexValid( index ); - return pages_[index/itemsPerPage][index%itemsPerPage]; -} - -Value * -ValueInternalArray::find( ArrayIndex index ) const -{ - if ( index >= size_ ) - return 0; - return &(pages_[index/itemsPerPage][index%itemsPerPage]); -} - -ValueInternalArray::ArrayIndex -ValueInternalArray::size() const -{ - return size_; -} - -int -ValueInternalArray::distance( const IteratorState &x, const IteratorState &y ) -{ - return indexOf(y) - indexOf(x); -} - - -ValueInternalArray::ArrayIndex -ValueInternalArray::indexOf( const IteratorState &iterator ) -{ - if ( !iterator.array_ ) - return ArrayIndex(-1); - return ArrayIndex( - (iterator.currentPageIndex_ - iterator.array_->pages_) * itemsPerPage - + iterator.currentItemIndex_ ); -} - - -int -ValueInternalArray::compare( const ValueInternalArray &other ) const -{ - int sizeDiff( size_ - other.size_ ); - if ( sizeDiff != 0 ) - return sizeDiff; - - for ( ArrayIndex index =0; index < size_; ++index ) - { - int diff = pages_[index/itemsPerPage][index%itemsPerPage].compare( - other.pages_[index/itemsPerPage][index%itemsPerPage] ); - if ( diff != 0 ) - return diff; - } - return 0; -} diff --git a/util/json/json_internalmap.inl b/util/json/json_internalmap.inl deleted file mode 100644 index 19771488..00000000 --- a/util/json/json_internalmap.inl +++ /dev/null @@ -1,607 +0,0 @@ -// included by json_value.cpp -// everything is within Json namespace - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// class ValueInternalMap -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// - -/** \internal MUST be safely initialized using memset( this, 0, sizeof(ValueInternalLink) ); - * This optimization is used by the fast allocator. - */ -ValueInternalLink::ValueInternalLink() - : previous_( 0 ) - , next_( 0 ) -{ -} - -ValueInternalLink::~ValueInternalLink() -{ - for ( int index =0; index < itemPerLink; ++index ) - { - if ( !items_[index].isItemAvailable() ) - { - if ( !items_[index].isMemberNameStatic() ) - free( keys_[index] ); - } - else - break; - } -} - - - -ValueMapAllocator::~ValueMapAllocator() -{ -} - -#ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR -class DefaultValueMapAllocator : public ValueMapAllocator -{ -public: // overridden from ValueMapAllocator - virtual ValueInternalMap *newMap() - { - return new ValueInternalMap(); - } - - virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) - { - return new ValueInternalMap( other ); - } - - virtual void destructMap( ValueInternalMap *map ) - { - delete map; - } - - virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) - { - return new ValueInternalLink[size]; - } - - virtual void releaseMapBuckets( ValueInternalLink *links ) - { - delete [] links; - } - - virtual ValueInternalLink *allocateMapLink() - { - return new ValueInternalLink(); - } - - virtual void releaseMapLink( ValueInternalLink *link ) - { - delete link; - } -}; -#else -/// @todo make this thread-safe (lock when accessign batch allocator) -class DefaultValueMapAllocator : public ValueMapAllocator -{ -public: // overridden from ValueMapAllocator - virtual ValueInternalMap *newMap() - { - ValueInternalMap *map = mapsAllocator_.allocate(); - new (map) ValueInternalMap(); // placement new - return map; - } - - virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) - { - ValueInternalMap *map = mapsAllocator_.allocate(); - new (map) ValueInternalMap( other ); // placement new - return map; - } - - virtual void destructMap( ValueInternalMap *map ) - { - if ( map ) - { - map->~ValueInternalMap(); - mapsAllocator_.release( map ); - } - } - - virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) - { - return new ValueInternalLink[size]; - } - - virtual void releaseMapBuckets( ValueInternalLink *links ) - { - delete [] links; - } - - virtual ValueInternalLink *allocateMapLink() - { - ValueInternalLink *link = linksAllocator_.allocate(); - memset( link, 0, sizeof(ValueInternalLink) ); - return link; - } - - virtual void releaseMapLink( ValueInternalLink *link ) - { - link->~ValueInternalLink(); - linksAllocator_.release( link ); - } -private: - BatchAllocator mapsAllocator_; - BatchAllocator linksAllocator_; -}; -#endif - -static ValueMapAllocator *&mapAllocator() -{ - static DefaultValueMapAllocator defaultAllocator; - static ValueMapAllocator *mapAllocator = &defaultAllocator; - return mapAllocator; -} - -static struct DummyMapAllocatorInitializer { - DummyMapAllocatorInitializer() - { - mapAllocator(); // ensure mapAllocator() statics are initialized before main(). - } -} dummyMapAllocatorInitializer; - - - -// h(K) = value * K >> w ; with w = 32 & K prime w.r.t. 2^32. - -/* -use linked list hash map. -buckets array is a container. -linked list element contains 6 key/values. (memory = (16+4) * 6 + 4 = 124) -value have extra state: valid, available, deleted -*/ - - -ValueInternalMap::ValueInternalMap() - : buckets_( 0 ) - , tailLink_( 0 ) - , bucketsSize_( 0 ) - , itemCount_( 0 ) -{ -} - - -ValueInternalMap::ValueInternalMap( const ValueInternalMap &other ) - : buckets_( 0 ) - , tailLink_( 0 ) - , bucketsSize_( 0 ) - , itemCount_( 0 ) -{ - reserve( other.itemCount_ ); - IteratorState it; - IteratorState itEnd; - other.makeBeginIterator( it ); - other.makeEndIterator( itEnd ); - for ( ; !equals(it,itEnd); increment(it) ) - { - bool isStatic; - const char *memberName = key( it, isStatic ); - const Value &aValue = value( it ); - resolveReference(memberName, isStatic) = aValue; - } -} - - -ValueInternalMap & -ValueInternalMap::operator =( const ValueInternalMap &other ) -{ - ValueInternalMap dummy( other ); - swap( dummy ); - return *this; -} - - -ValueInternalMap::~ValueInternalMap() -{ - if ( buckets_ ) - { - for ( BucketIndex bucketIndex =0; bucketIndex < bucketsSize_; ++bucketIndex ) - { - ValueInternalLink *link = buckets_[bucketIndex].next_; - while ( link ) - { - ValueInternalLink *linkToRelease = link; - link = link->next_; - mapAllocator()->releaseMapLink( linkToRelease ); - } - } - mapAllocator()->releaseMapBuckets( buckets_ ); - } -} - - -void -ValueInternalMap::swap( ValueInternalMap &other ) -{ - ValueInternalLink *tempBuckets = buckets_; - buckets_ = other.buckets_; - other.buckets_ = tempBuckets; - ValueInternalLink *tempTailLink = tailLink_; - tailLink_ = other.tailLink_; - other.tailLink_ = tempTailLink; - BucketIndex tempBucketsSize = bucketsSize_; - bucketsSize_ = other.bucketsSize_; - other.bucketsSize_ = tempBucketsSize; - BucketIndex tempItemCount = itemCount_; - itemCount_ = other.itemCount_; - other.itemCount_ = tempItemCount; -} - - -void -ValueInternalMap::clear() -{ - ValueInternalMap dummy; - swap( dummy ); -} - - -ValueInternalMap::BucketIndex -ValueInternalMap::size() const -{ - return itemCount_; -} - -bool -ValueInternalMap::reserveDelta( BucketIndex growth ) -{ - return reserve( itemCount_ + growth ); -} - -bool -ValueInternalMap::reserve( BucketIndex newItemCount ) -{ - if ( !buckets_ && newItemCount > 0 ) - { - buckets_ = mapAllocator()->allocateMapBuckets( 1 ); - bucketsSize_ = 1; - tailLink_ = &buckets_[0]; - } -// BucketIndex idealBucketCount = (newItemCount + ValueInternalLink::itemPerLink) / ValueInternalLink::itemPerLink; - return true; -} - - -const Value * -ValueInternalMap::find( const char *key ) const -{ - if ( !bucketsSize_ ) - return 0; - HashKey hashedKey = hash( key ); - BucketIndex bucketIndex = hashedKey % bucketsSize_; - for ( const ValueInternalLink *current = &buckets_[bucketIndex]; - current != 0; - current = current->next_ ) - { - for ( BucketIndex index=0; index < ValueInternalLink::itemPerLink; ++index ) - { - if ( current->items_[index].isItemAvailable() ) - return 0; - if ( strcmp( key, current->keys_[index] ) == 0 ) - return ¤t->items_[index]; - } - } - return 0; -} - - -Value * -ValueInternalMap::find( const char *key ) -{ - const ValueInternalMap *constThis = this; - return const_cast( constThis->find( key ) ); -} - - -Value & -ValueInternalMap::resolveReference( const char *key, - bool isStatic ) -{ - HashKey hashedKey = hash( key ); - if ( bucketsSize_ ) - { - BucketIndex bucketIndex = hashedKey % bucketsSize_; - ValueInternalLink **previous = 0; - BucketIndex index; - for ( ValueInternalLink *current = &buckets_[bucketIndex]; - current != 0; - previous = ¤t->next_, current = current->next_ ) - { - for ( index=0; index < ValueInternalLink::itemPerLink; ++index ) - { - if ( current->items_[index].isItemAvailable() ) - return setNewItem( key, isStatic, current, index ); - if ( strcmp( key, current->keys_[index] ) == 0 ) - return current->items_[index]; - } - } - } - - reserveDelta( 1 ); - return unsafeAdd( key, isStatic, hashedKey ); -} - - -void -ValueInternalMap::remove( const char *key ) -{ - HashKey hashedKey = hash( key ); - if ( !bucketsSize_ ) - return; - BucketIndex bucketIndex = hashedKey % bucketsSize_; - for ( ValueInternalLink *link = &buckets_[bucketIndex]; - link != 0; - link = link->next_ ) - { - BucketIndex index; - for ( index =0; index < ValueInternalLink::itemPerLink; ++index ) - { - if ( link->items_[index].isItemAvailable() ) - return; - if ( strcmp( key, link->keys_[index] ) == 0 ) - { - doActualRemove( link, index, bucketIndex ); - return; - } - } - } -} - -void -ValueInternalMap::doActualRemove( ValueInternalLink *link, - BucketIndex index, - BucketIndex bucketIndex ) -{ - // find last item of the bucket and swap it with the 'removed' one. - // set removed items flags to 'available'. - // if last page only contains 'available' items, then desallocate it (it's empty) - ValueInternalLink *&lastLink = getLastLinkInBucket( index ); - BucketIndex lastItemIndex = 1; // a link can never be empty, so start at 1 - for ( ; - lastItemIndex < ValueInternalLink::itemPerLink; - ++lastItemIndex ) // may be optimized with dicotomic search - { - if ( lastLink->items_[lastItemIndex].isItemAvailable() ) - break; - } - - BucketIndex lastUsedIndex = lastItemIndex - 1; - Value *valueToDelete = &link->items_[index]; - Value *valueToPreserve = &lastLink->items_[lastUsedIndex]; - if ( valueToDelete != valueToPreserve ) - valueToDelete->swap( *valueToPreserve ); - if ( lastUsedIndex == 0 ) // page is now empty - { // remove it from bucket linked list and delete it. - ValueInternalLink *linkPreviousToLast = lastLink->previous_; - if ( linkPreviousToLast != 0 ) // can not deleted bucket link. - { - mapAllocator()->releaseMapLink( lastLink ); - linkPreviousToLast->next_ = 0; - lastLink = linkPreviousToLast; - } - } - else - { - Value dummy; - valueToPreserve->swap( dummy ); // restore deleted to default Value. - valueToPreserve->setItemUsed( false ); - } - --itemCount_; -} - - -ValueInternalLink *& -ValueInternalMap::getLastLinkInBucket( BucketIndex bucketIndex ) -{ - if ( bucketIndex == bucketsSize_ - 1 ) - return tailLink_; - ValueInternalLink *&previous = buckets_[bucketIndex+1].previous_; - if ( !previous ) - previous = &buckets_[bucketIndex]; - return previous; -} - - -Value & -ValueInternalMap::setNewItem( const char *key, - bool isStatic, - ValueInternalLink *link, - BucketIndex index ) -{ - char *duplicatedKey = valueAllocator()->makeMemberName( key ); - ++itemCount_; - link->keys_[index] = duplicatedKey; - link->items_[index].setItemUsed(); - link->items_[index].setMemberNameIsStatic( isStatic ); - return link->items_[index]; // items already default constructed. -} - - -Value & -ValueInternalMap::unsafeAdd( const char *key, - bool isStatic, - HashKey hashedKey ) -{ - JSON_ASSERT_MESSAGE( bucketsSize_ > 0, "ValueInternalMap::unsafeAdd(): internal logic error." ); - BucketIndex bucketIndex = hashedKey % bucketsSize_; - ValueInternalLink *&previousLink = getLastLinkInBucket( bucketIndex ); - ValueInternalLink *link = previousLink; - BucketIndex index; - for ( index =0; index < ValueInternalLink::itemPerLink; ++index ) - { - if ( link->items_[index].isItemAvailable() ) - break; - } - if ( index == ValueInternalLink::itemPerLink ) // need to add a new page - { - ValueInternalLink *newLink = mapAllocator()->allocateMapLink(); - index = 0; - link->next_ = newLink; - previousLink = newLink; - link = newLink; - } - return setNewItem( key, isStatic, link, index ); -} - - -ValueInternalMap::HashKey -ValueInternalMap::hash( const char *key ) const -{ - HashKey hash = 0; - while ( *key ) - hash += *key++ * 37; - return hash; -} - - -int -ValueInternalMap::compare( const ValueInternalMap &other ) const -{ - int sizeDiff( itemCount_ - other.itemCount_ ); - if ( sizeDiff != 0 ) - return sizeDiff; - // Strict order guaranty is required. Compare all keys FIRST, then compare values. - IteratorState it; - IteratorState itEnd; - makeBeginIterator( it ); - makeEndIterator( itEnd ); - for ( ; !equals(it,itEnd); increment(it) ) - { - if ( !other.find( key( it ) ) ) - return 1; - } - - // All keys are equals, let's compare values - makeBeginIterator( it ); - for ( ; !equals(it,itEnd); increment(it) ) - { - const Value *otherValue = other.find( key( it ) ); - int valueDiff = value(it).compare( *otherValue ); - if ( valueDiff != 0 ) - return valueDiff; - } - return 0; -} - - -void -ValueInternalMap::makeBeginIterator( IteratorState &it ) const -{ - it.map_ = const_cast( this ); - it.bucketIndex_ = 0; - it.itemIndex_ = 0; - it.link_ = buckets_; -} - - -void -ValueInternalMap::makeEndIterator( IteratorState &it ) const -{ - it.map_ = const_cast( this ); - it.bucketIndex_ = bucketsSize_; - it.itemIndex_ = 0; - it.link_ = 0; -} - - -bool -ValueInternalMap::equals( const IteratorState &x, const IteratorState &other ) -{ - return x.map_ == other.map_ - && x.bucketIndex_ == other.bucketIndex_ - && x.link_ == other.link_ - && x.itemIndex_ == other.itemIndex_; -} - - -void -ValueInternalMap::incrementBucket( IteratorState &iterator ) -{ - ++iterator.bucketIndex_; - JSON_ASSERT_MESSAGE( iterator.bucketIndex_ <= iterator.map_->bucketsSize_, - "ValueInternalMap::increment(): attempting to iterate beyond end." ); - if ( iterator.bucketIndex_ == iterator.map_->bucketsSize_ ) - iterator.link_ = 0; - else - iterator.link_ = &(iterator.map_->buckets_[iterator.bucketIndex_]); - iterator.itemIndex_ = 0; -} - - -void -ValueInternalMap::increment( IteratorState &iterator ) -{ - JSON_ASSERT_MESSAGE( iterator.map_, "Attempting to iterator using invalid iterator." ); - ++iterator.itemIndex_; - if ( iterator.itemIndex_ == ValueInternalLink::itemPerLink ) - { - JSON_ASSERT_MESSAGE( iterator.link_ != 0, - "ValueInternalMap::increment(): attempting to iterate beyond end." ); - iterator.link_ = iterator.link_->next_; - if ( iterator.link_ == 0 ) - incrementBucket( iterator ); - } - else if ( iterator.link_->items_[iterator.itemIndex_].isItemAvailable() ) - { - incrementBucket( iterator ); - } -} - - -void -ValueInternalMap::decrement( IteratorState &iterator ) -{ - if ( iterator.itemIndex_ == 0 ) - { - JSON_ASSERT_MESSAGE( iterator.map_, "Attempting to iterate using invalid iterator." ); - if ( iterator.link_ == &iterator.map_->buckets_[iterator.bucketIndex_] ) - { - JSON_ASSERT_MESSAGE( iterator.bucketIndex_ > 0, "Attempting to iterate beyond beginning." ); - --(iterator.bucketIndex_); - } - iterator.link_ = iterator.link_->previous_; - iterator.itemIndex_ = ValueInternalLink::itemPerLink - 1; - } -} - - -const char * -ValueInternalMap::key( const IteratorState &iterator ) -{ - JSON_ASSERT_MESSAGE( iterator.link_, "Attempting to iterate using invalid iterator." ); - return iterator.link_->keys_[iterator.itemIndex_]; -} - -const char * -ValueInternalMap::key( const IteratorState &iterator, bool &isStatic ) -{ - JSON_ASSERT_MESSAGE( iterator.link_, "Attempting to iterate using invalid iterator." ); - isStatic = iterator.link_->items_[iterator.itemIndex_].isMemberNameStatic(); - return iterator.link_->keys_[iterator.itemIndex_]; -} - - -Value & -ValueInternalMap::value( const IteratorState &iterator ) -{ - JSON_ASSERT_MESSAGE( iterator.link_, "Attempting to iterate using invalid iterator." ); - return iterator.link_->items_[iterator.itemIndex_]; -} - - -int -ValueInternalMap::distance( const IteratorState &x, const IteratorState &y ) -{ - int offset = 0; - IteratorState it = x; - while ( !equals( it, y ) ) - increment( it ); - return offset; -} diff --git a/util/json/json_reader.cpp b/util/json/json_reader.cpp deleted file mode 100644 index 3623e71d..00000000 --- a/util/json/json_reader.cpp +++ /dev/null @@ -1,885 +0,0 @@ -#include "reader.h" -#include "value.h" -#include -#include -#include -#include -#include -#include - -#if _MSC_VER >= 1400 // VC++ 8.0 -#pragma warning( disable : 4996 ) // disable warning about strdup being deprecated. -#endif - -namespace Json { - -// Implementation of class Features -// //////////////////////////////// - -Features::Features() - : allowComments_( true ) - , strictRoot_( false ) -{ -} - - -Features -Features::all() -{ - return Features(); -} - - -Features -Features::strictMode() -{ - Features features; - features.allowComments_ = false; - features.strictRoot_ = true; - return features; -} - -// Implementation of class Reader -// //////////////////////////////// - - -static inline bool -in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4 ) -{ - return c == c1 || c == c2 || c == c3 || c == c4; -} - -static inline bool -in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4, Reader::Char c5 ) -{ - return c == c1 || c == c2 || c == c3 || c == c4 || c == c5; -} - - -static bool -containsNewLine( Reader::Location begin, - Reader::Location end ) -{ - for ( ;begin < end; ++begin ) - if ( *begin == '\n' || *begin == '\r' ) - return true; - return false; -} - -static std::string codePointToUTF8(unsigned int cp) -{ - std::string result; - - // based on description from http://en.wikipedia.org/wiki/UTF-8 - - if (cp <= 0x7f) - { - result.resize(1); - result[0] = static_cast(cp); - } - else if (cp <= 0x7FF) - { - result.resize(2); - result[1] = static_cast(0x80 | (0x3f & cp)); - result[0] = static_cast(0xC0 | (0x1f & (cp >> 6))); - } - else if (cp <= 0xFFFF) - { - result.resize(3); - result[2] = static_cast(0x80 | (0x3f & cp)); - result[1] = 0x80 | static_cast((0x3f & (cp >> 6))); - result[0] = 0xE0 | static_cast((0xf & (cp >> 12))); - } - else if (cp <= 0x10FFFF) - { - result.resize(4); - result[3] = static_cast(0x80 | (0x3f & cp)); - result[2] = static_cast(0x80 | (0x3f & (cp >> 6))); - result[1] = static_cast(0x80 | (0x3f & (cp >> 12))); - result[0] = static_cast(0xF0 | (0x7 & (cp >> 18))); - } - - return result; -} - - -// Class Reader -// ////////////////////////////////////////////////////////////////// - -Reader::Reader() - : features_( Features::all() ) -{ -} - - -Reader::Reader( const Features &features ) - : features_( features ) -{ -} - - -bool -Reader::parse( const std::string &document, - Value &root, - bool collectComments ) -{ - document_ = document; - const char *begin = document_.c_str(); - const char *end = begin + document_.length(); - return parse( begin, end, root, collectComments ); -} - - -bool -Reader::parse( std::istream& sin, - Value &root, - bool collectComments ) -{ - //std::istream_iterator begin(sin); - //std::istream_iterator end; - // Those would allow streamed input from a file, if parse() were a - // template function. - - // Since std::string is reference-counted, this at least does not - // create an extra copy. - std::string doc; - std::getline(sin, doc, (char)EOF); - return parse( doc, root, collectComments ); -} - -bool -Reader::parse( const char *beginDoc, const char *endDoc, - Value &root, - bool collectComments ) -{ - if ( !features_.allowComments_ ) - { - collectComments = false; - } - - begin_ = beginDoc; - end_ = endDoc; - collectComments_ = collectComments; - current_ = begin_; - lastValueEnd_ = 0; - lastValue_ = 0; - commentsBefore_ = ""; - errors_.clear(); - while ( !nodes_.empty() ) - nodes_.pop(); - nodes_.push( &root ); - - bool successful = readValue(); - Token token; - skipCommentTokens( token ); - if ( collectComments_ && !commentsBefore_.empty() ) - root.setComment( commentsBefore_, commentAfter ); - if ( features_.strictRoot_ ) - { - if ( !root.isArray() && !root.isObject() ) - { - // Set error location to start of doc, ideally should be first token found in doc - token.type_ = tokenError; - token.start_ = beginDoc; - token.end_ = endDoc; - addError( "A valid JSON document must be either an array or an object value.", - token ); - return false; - } - } - return successful; -} - - -bool -Reader::readValue() -{ - Token token; - skipCommentTokens( token ); - bool successful = true; - - if ( collectComments_ && !commentsBefore_.empty() ) - { - currentValue().setComment( commentsBefore_, commentBefore ); - commentsBefore_ = ""; - } - - - switch ( token.type_ ) - { - case tokenObjectBegin: - successful = readObject( token ); - break; - case tokenArrayBegin: - successful = readArray( token ); - break; - case tokenNumber: - successful = decodeNumber( token ); - break; - case tokenString: - successful = decodeString( token ); - break; - case tokenTrue: - currentValue() = true; - break; - case tokenFalse: - currentValue() = false; - break; - case tokenNull: - currentValue() = Value(); - break; - default: - return addError( "Syntax error: value, object or array expected.", token ); - } - - if ( collectComments_ ) - { - lastValueEnd_ = current_; - lastValue_ = ¤tValue(); - } - - return successful; -} - - -void -Reader::skipCommentTokens( Token &token ) -{ - if ( features_.allowComments_ ) - { - do - { - readToken( token ); - } - while ( token.type_ == tokenComment ); - } - else - { - readToken( token ); - } -} - - -bool -Reader::expectToken( TokenType type, Token &token, const char *message ) -{ - readToken( token ); - if ( token.type_ != type ) - return addError( message, token ); - return true; -} - - -bool -Reader::readToken( Token &token ) -{ - skipSpaces(); - token.start_ = current_; - Char c = getNextChar(); - bool ok = true; - switch ( c ) - { - case '{': - token.type_ = tokenObjectBegin; - break; - case '}': - token.type_ = tokenObjectEnd; - break; - case '[': - token.type_ = tokenArrayBegin; - break; - case ']': - token.type_ = tokenArrayEnd; - break; - case '"': - token.type_ = tokenString; - ok = readString(); - break; - case '/': - token.type_ = tokenComment; - ok = readComment(); - break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '-': - token.type_ = tokenNumber; - readNumber(); - break; - case 't': - token.type_ = tokenTrue; - ok = match( "rue", 3 ); - break; - case 'f': - token.type_ = tokenFalse; - ok = match( "alse", 4 ); - break; - case 'n': - token.type_ = tokenNull; - ok = match( "ull", 3 ); - break; - case ',': - token.type_ = tokenArraySeparator; - break; - case ':': - token.type_ = tokenMemberSeparator; - break; - case 0: - token.type_ = tokenEndOfStream; - break; - default: - ok = false; - break; - } - if ( !ok ) - token.type_ = tokenError; - token.end_ = current_; - return true; -} - - -void -Reader::skipSpaces() -{ - while ( current_ != end_ ) - { - Char c = *current_; - if ( c == ' ' || c == '\t' || c == '\r' || c == '\n' ) - ++current_; - else - break; - } -} - - -bool -Reader::match( Location pattern, - int patternLength ) -{ - if ( end_ - current_ < patternLength ) - return false; - int index = patternLength; - while ( index-- ) - if ( current_[index] != pattern[index] ) - return false; - current_ += patternLength; - return true; -} - - -bool -Reader::readComment() -{ - Location commentBegin = current_ - 1; - Char c = getNextChar(); - bool successful = false; - if ( c == '*' ) - successful = readCStyleComment(); - else if ( c == '/' ) - successful = readCppStyleComment(); - if ( !successful ) - return false; - - if ( collectComments_ ) - { - CommentPlacement placement = commentBefore; - if ( lastValueEnd_ && !containsNewLine( lastValueEnd_, commentBegin ) ) - { - if ( c != '*' || !containsNewLine( commentBegin, current_ ) ) - placement = commentAfterOnSameLine; - } - - addComment( commentBegin, current_, placement ); - } - return true; -} - - -void -Reader::addComment( Location begin, - Location end, - CommentPlacement placement ) -{ - assert( collectComments_ ); - if ( placement == commentAfterOnSameLine ) - { - assert( lastValue_ != 0 ); - lastValue_->setComment( std::string( begin, end ), placement ); - } - else - { - if ( !commentsBefore_.empty() ) - commentsBefore_ += "\n"; - commentsBefore_ += std::string( begin, end ); - } -} - - -bool -Reader::readCStyleComment() -{ - while ( current_ != end_ ) - { - Char c = getNextChar(); - if ( c == '*' && *current_ == '/' ) - break; - } - return getNextChar() == '/'; -} - - -bool -Reader::readCppStyleComment() -{ - while ( current_ != end_ ) - { - Char c = getNextChar(); - if ( c == '\r' || c == '\n' ) - break; - } - return true; -} - - -void -Reader::readNumber() -{ - while ( current_ != end_ ) - { - if ( !(*current_ >= '0' && *current_ <= '9') && - !in( *current_, '.', 'e', 'E', '+', '-' ) ) - break; - ++current_; - } -} - -bool -Reader::readString() -{ - Char c = 0; - while ( current_ != end_ ) - { - c = getNextChar(); - if ( c == '\\' ) - getNextChar(); - else if ( c == '"' ) - break; - } - return c == '"'; -} - - -bool -Reader::readObject( Token &tokenStart ) -{ - Token tokenName; - std::string name; - currentValue() = Value( objectValue ); - while ( readToken( tokenName ) ) - { - bool initialTokenOk = true; - while ( tokenName.type_ == tokenComment && initialTokenOk ) - initialTokenOk = readToken( tokenName ); - if ( !initialTokenOk ) - break; - if ( tokenName.type_ == tokenObjectEnd && name.empty() ) // empty object - return true; - if ( tokenName.type_ != tokenString ) - break; - - name = ""; - if ( !decodeString( tokenName, name ) ) - return recoverFromError( tokenObjectEnd ); - - Token colon; - if ( !readToken( colon ) || colon.type_ != tokenMemberSeparator ) - { - return addErrorAndRecover( "Missing ':' after object member name", - colon, - tokenObjectEnd ); - } - Value &value = currentValue()[ name ]; - nodes_.push( &value ); - bool ok = readValue(); - nodes_.pop(); - if ( !ok ) // error already set - return recoverFromError( tokenObjectEnd ); - - Token comma; - if ( !readToken( comma ) - || ( comma.type_ != tokenObjectEnd && - comma.type_ != tokenArraySeparator && - comma.type_ != tokenComment ) ) - { - return addErrorAndRecover( "Missing ',' or '}' in object declaration", - comma, - tokenObjectEnd ); - } - bool finalizeTokenOk = true; - while ( comma.type_ == tokenComment && - finalizeTokenOk ) - finalizeTokenOk = readToken( comma ); - if ( comma.type_ == tokenObjectEnd ) - return true; - } - return addErrorAndRecover( "Missing '}' or object member name", - tokenName, - tokenObjectEnd ); -} - - -bool -Reader::readArray( Token &tokenStart ) -{ - currentValue() = Value( arrayValue ); - skipSpaces(); - if ( *current_ == ']' ) // empty array - { - Token endArray; - readToken( endArray ); - return true; - } - int index = 0; - while ( true ) - { - Value &value = currentValue()[ index++ ]; - nodes_.push( &value ); - bool ok = readValue(); - nodes_.pop(); - if ( !ok ) // error already set - return recoverFromError( tokenArrayEnd ); - - Token token; - // Accept Comment after last item in the array. - ok = readToken( token ); - while ( token.type_ == tokenComment && ok ) - { - ok = readToken( token ); - } - bool badTokenType = ( token.type_ == tokenArraySeparator && - token.type_ == tokenArrayEnd ); - if ( !ok || badTokenType ) - { - return addErrorAndRecover( "Missing ',' or ']' in array declaration", - token, - tokenArrayEnd ); - } - if ( token.type_ == tokenArrayEnd ) - break; - } - return true; -} - - -bool -Reader::decodeNumber( Token &token ) -{ - bool isDouble = false; - for ( Location inspect = token.start_; inspect != token.end_; ++inspect ) - { - isDouble = isDouble - || in( *inspect, '.', 'e', 'E', '+' ) - || ( *inspect == '-' && inspect != token.start_ ); - } - if ( isDouble ) - return decodeDouble( token ); - Location current = token.start_; - bool isNegative = *current == '-'; - if ( isNegative ) - ++current; - Value::UInt threshold = (isNegative ? Value::UInt(-Value::minInt) - : Value::maxUInt) / 10; - Value::UInt value = 0; - while ( current < token.end_ ) - { - Char c = *current++; - if ( c < '0' || c > '9' ) - return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token ); - if ( value >= threshold ) - return decodeDouble( token ); - value = value * 10 + Value::UInt(c - '0'); - } - if ( isNegative ) - currentValue() = -Value::Int( value ); - else if ( value <= Value::UInt(Value::maxInt) ) - currentValue() = Value::Int( value ); - else - currentValue() = value; - return true; -} - - -bool -Reader::decodeDouble( Token &token ) -{ - double value = 0; - const int bufferSize = 32; - int count; - int length = int(token.end_ - token.start_); - if ( length <= bufferSize ) - { - Char buffer[bufferSize]; - memcpy( buffer, token.start_, length ); - buffer[length] = 0; - count = sscanf( buffer, "%lf", &value ); - } - else - { - std::string buffer( token.start_, token.end_ ); - count = sscanf( buffer.c_str(), "%lf", &value ); - } - - if ( count != 1 ) - return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token ); - currentValue() = value; - return true; -} - - -bool -Reader::decodeString( Token &token ) -{ - std::string decoded; - if ( !decodeString( token, decoded ) ) - return false; - currentValue() = decoded; - return true; -} - - -bool -Reader::decodeString( Token &token, std::string &decoded ) -{ - decoded.reserve( token.end_ - token.start_ - 2 ); - Location current = token.start_ + 1; // skip '"' - Location end = token.end_ - 1; // do not include '"' - while ( current != end ) - { - Char c = *current++; - if ( c == '"' ) - break; - else if ( c == '\\' ) - { - if ( current == end ) - return addError( "Empty escape sequence in string", token, current ); - Char escape = *current++; - switch ( escape ) - { - case '"': decoded += '"'; break; - case '/': decoded += '/'; break; - case '\\': decoded += '\\'; break; - case 'b': decoded += '\b'; break; - case 'f': decoded += '\f'; break; - case 'n': decoded += '\n'; break; - case 'r': decoded += '\r'; break; - case 't': decoded += '\t'; break; - case 'u': - { - unsigned int unicode; - if ( !decodeUnicodeCodePoint( token, current, end, unicode ) ) - return false; - decoded += codePointToUTF8(unicode); - } - break; - default: - return addError( "Bad escape sequence in string", token, current ); - } - } - else - { - decoded += c; - } - } - return true; -} - -bool -Reader::decodeUnicodeCodePoint( Token &token, - Location ¤t, - Location end, - unsigned int &unicode ) -{ - - if ( !decodeUnicodeEscapeSequence( token, current, end, unicode ) ) - return false; - if (unicode >= 0xD800 && unicode <= 0xDBFF) - { - // surrogate pairs - if (end - current < 6) - return addError( "additional six characters expected to parse unicode surrogate pair.", token, current ); - unsigned int surrogatePair; - if (*(current++) == '\\' && *(current++)== 'u') - { - if (decodeUnicodeEscapeSequence( token, current, end, surrogatePair )) - { - unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); - } - else - return false; - } - else - return addError( "expecting another \\u token to begin the second half of a unicode surrogate pair", token, current ); - } - return true; -} - -bool -Reader::decodeUnicodeEscapeSequence( Token &token, - Location ¤t, - Location end, - unsigned int &unicode ) -{ - if ( end - current < 4 ) - return addError( "Bad unicode escape sequence in string: four digits expected.", token, current ); - unicode = 0; - for ( int index =0; index < 4; ++index ) - { - Char c = *current++; - unicode *= 16; - if ( c >= '0' && c <= '9' ) - unicode += c - '0'; - else if ( c >= 'a' && c <= 'f' ) - unicode += c - 'a' + 10; - else if ( c >= 'A' && c <= 'F' ) - unicode += c - 'A' + 10; - else - return addError( "Bad unicode escape sequence in string: hexadecimal digit expected.", token, current ); - } - return true; -} - - -bool -Reader::addError( const std::string &message, - Token &token, - Location extra ) -{ - ErrorInfo info; - info.token_ = token; - info.message_ = message; - info.extra_ = extra; - errors_.push_back( info ); - return false; -} - - -bool -Reader::recoverFromError( TokenType skipUntilToken ) -{ - int errorCount = int(errors_.size()); - Token skip; - while ( true ) - { - if ( !readToken(skip) ) - errors_.resize( errorCount ); // discard errors caused by recovery - if ( skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream ) - break; - } - errors_.resize( errorCount ); - return false; -} - - -bool -Reader::addErrorAndRecover( const std::string &message, - Token &token, - TokenType skipUntilToken ) -{ - addError( message, token ); - return recoverFromError( skipUntilToken ); -} - - -Value & -Reader::currentValue() -{ - return *(nodes_.top()); -} - - -Reader::Char -Reader::getNextChar() -{ - if ( current_ == end_ ) - return 0; - return *current_++; -} - - -void -Reader::getLocationLineAndColumn( Location location, - int &line, - int &column ) const -{ - Location current = begin_; - Location lastLineStart = current; - line = 0; - while ( current < location && current != end_ ) - { - Char c = *current++; - if ( c == '\r' ) - { - if ( *current == '\n' ) - ++current; - lastLineStart = current; - ++line; - } - else if ( c == '\n' ) - { - lastLineStart = current; - ++line; - } - } - // column & line start at 1 - column = int(location - lastLineStart) + 1; - ++line; -} - - -std::string -Reader::getLocationLineAndColumn( Location location ) const -{ - int line, column; - getLocationLineAndColumn( location, line, column ); - char buffer[18+16+16+1]; - sprintf( buffer, "Line %d, Column %d", line, column ); - return buffer; -} - - -std::string -Reader::getFormatedErrorMessages() const -{ - std::string formattedMessage; - for ( Errors::const_iterator itError = errors_.begin(); - itError != errors_.end(); - ++itError ) - { - const ErrorInfo &error = *itError; - formattedMessage += "* " + getLocationLineAndColumn( error.token_.start_ ) + "\n"; - formattedMessage += " " + error.message_ + "\n"; - if ( error.extra_ ) - formattedMessage += "See " + getLocationLineAndColumn( error.extra_ ) + " for detail.\n"; - } - return formattedMessage; -} - - -std::istream& operator>>( std::istream &sin, Value &root ) -{ - Json::Reader reader; - bool ok = reader.parse(sin, root, true); - //JSON_ASSERT( ok ); - if (!ok) throw std::runtime_error(reader.getFormatedErrorMessages()); - return sin; -} - - -} // namespace Json diff --git a/util/json/json_value.cpp b/util/json/json_value.cpp deleted file mode 100644 index 21996f4a..00000000 --- a/util/json/json_value.cpp +++ /dev/null @@ -1,1718 +0,0 @@ -#include -#include "value.h" -#include "writer.h" -#include -#include -#include -#include -#ifdef JSON_USE_CPPTL -# include -#endif -#include // size_t -#ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR -# include "json_batchallocator.h" -#endif // #ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR - -#define JSON_ASSERT_UNREACHABLE assert( false ) -#define JSON_ASSERT( condition ) assert( condition ); // @todo <= change this into an exception throw -#define JSON_ASSERT_MESSAGE( condition, message ) if (!( condition )) throw std::runtime_error( message ); - -namespace Json { - -const Value Value::null; -const Int Value::minInt = Int( ~(UInt(-1)/2) ); -const Int Value::maxInt = Int( UInt(-1)/2 ); -const UInt Value::maxUInt = UInt(-1); - -// A "safe" implementation of strdup. Allow null pointer to be passed. -// Also avoid warning on msvc80. -// -//inline char *safeStringDup( const char *czstring ) -//{ -// if ( czstring ) -// { -// const size_t length = (unsigned int)( strlen(czstring) + 1 ); -// char *newString = static_cast( malloc( length ) ); -// memcpy( newString, czstring, length ); -// return newString; -// } -// return 0; -//} -// -//inline char *safeStringDup( const std::string &str ) -//{ -// if ( !str.empty() ) -// { -// const size_t length = str.length(); -// char *newString = static_cast( malloc( length + 1 ) ); -// memcpy( newString, str.c_str(), length ); -// newString[length] = 0; -// return newString; -// } -// return 0; -//} - -ValueAllocator::~ValueAllocator() -{ -} - -class DefaultValueAllocator : public ValueAllocator -{ -public: - virtual ~DefaultValueAllocator() - { - } - - virtual char *makeMemberName( const char *memberName ) - { - return duplicateStringValue( memberName ); - } - - virtual void releaseMemberName( char *memberName ) - { - releaseStringValue( memberName ); - } - - virtual char *duplicateStringValue( const char *value, - unsigned int length = unknown ) - { - //@todo invesgate this old optimization - //if ( !value || value[0] == 0 ) - // return 0; - - if ( length == unknown ) - length = (unsigned int)strlen(value); - char *newString = static_cast( malloc( length + 1 ) ); - memcpy( newString, value, length ); - newString[length] = 0; - return newString; - } - - virtual void releaseStringValue( char *value ) - { - if ( value ) - free( value ); - } -}; - -static ValueAllocator *&valueAllocator() -{ - static DefaultValueAllocator defaultAllocator; - static ValueAllocator *valueAllocator = &defaultAllocator; - return valueAllocator; -} - -static struct DummyValueAllocatorInitializer { - DummyValueAllocatorInitializer() - { - valueAllocator(); // ensure valueAllocator() statics are initialized before main(). - } -} dummyValueAllocatorInitializer; - - - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ValueInternals... -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -#ifdef JSON_VALUE_USE_INTERNAL_MAP -# include "json_internalarray.inl" -# include "json_internalmap.inl" -#endif // JSON_VALUE_USE_INTERNAL_MAP - -# include "json_valueiterator.inl" - - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// class Value::CommentInfo -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// - - -Value::CommentInfo::CommentInfo() - : comment_( 0 ) -{ -} - -Value::CommentInfo::~CommentInfo() -{ - if ( comment_ ) - valueAllocator()->releaseStringValue( comment_ ); -} - - -void -Value::CommentInfo::setComment( const char *text ) -{ - if ( comment_ ) - valueAllocator()->releaseStringValue( comment_ ); - JSON_ASSERT( text ); - JSON_ASSERT_MESSAGE( text[0]=='\0' || text[0]=='/', "Comments must start with /"); - // It seems that /**/ style comments are acceptable as well. - comment_ = valueAllocator()->duplicateStringValue( text ); -} - - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// class Value::CZString -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -# ifndef JSON_VALUE_USE_INTERNAL_MAP - -// Notes: index_ indicates if the string was allocated when -// a string is stored. - -Value::CZString::CZString( int index ) - : cstr_( 0 ) - , index_( index ) -{ -} - -Value::CZString::CZString( const char *cstr, DuplicationPolicy allocate ) - : cstr_( allocate == duplicate ? valueAllocator()->makeMemberName(cstr) - : cstr ) - , index_( allocate ) -{ -} - -Value::CZString::CZString( const CZString &other ) -: cstr_( other.index_ != noDuplication && other.cstr_ != 0 - ? valueAllocator()->makeMemberName( other.cstr_ ) - : other.cstr_ ) - , index_( other.cstr_ ? (other.index_ == noDuplication ? noDuplication : duplicate) - : other.index_ ) -{ -} - -Value::CZString::~CZString() -{ - if ( cstr_ && index_ == duplicate ) - valueAllocator()->releaseMemberName( const_cast( cstr_ ) ); -} - -void -Value::CZString::swap( CZString &other ) -{ - std::swap( cstr_, other.cstr_ ); - std::swap( index_, other.index_ ); -} - -Value::CZString & -Value::CZString::operator =( const CZString &other ) -{ - CZString temp( other ); - swap( temp ); - return *this; -} - -bool -Value::CZString::operator<( const CZString &other ) const -{ - if ( cstr_ ) - return strcmp( cstr_, other.cstr_ ) < 0; - return index_ < other.index_; -} - -bool -Value::CZString::operator==( const CZString &other ) const -{ - if ( cstr_ ) - return strcmp( cstr_, other.cstr_ ) == 0; - return index_ == other.index_; -} - - -int -Value::CZString::index() const -{ - return index_; -} - - -const char * -Value::CZString::c_str() const -{ - return cstr_; -} - -bool -Value::CZString::isStaticString() const -{ - return index_ == noDuplication; -} - -#endif // ifndef JSON_VALUE_USE_INTERNAL_MAP - - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// class Value::Value -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// - -/*! \internal Default constructor initialization must be equivalent to: - * memset( this, 0, sizeof(Value) ) - * This optimization is used in ValueInternalMap fast allocator. - */ -Value::Value( ValueType type ) - : type_( type ) - , allocated_( 0 ) - , comments_( 0 ) -# ifdef JSON_VALUE_USE_INTERNAL_MAP - , itemIsUsed_( 0 ) -#endif -{ - switch ( type ) - { - case nullValue: - break; - case intValue: - case uintValue: - value_.int_ = 0; - break; - case realValue: - value_.real_ = 0.0; - break; - case stringValue: - value_.string_ = 0; - break; -#ifndef JSON_VALUE_USE_INTERNAL_MAP - case arrayValue: - case objectValue: - value_.map_ = new ObjectValues(); - break; -#else - case arrayValue: - value_.array_ = arrayAllocator()->newArray(); - break; - case objectValue: - value_.map_ = mapAllocator()->newMap(); - break; -#endif - case booleanValue: - value_.bool_ = false; - break; - default: - JSON_ASSERT_UNREACHABLE; - } -} - - -Value::Value( Int value ) - : type_( intValue ) - , comments_( 0 ) -# ifdef JSON_VALUE_USE_INTERNAL_MAP - , itemIsUsed_( 0 ) -#endif -{ - value_.int_ = value; -} - - -Value::Value( UInt value ) - : type_( uintValue ) - , comments_( 0 ) -# ifdef JSON_VALUE_USE_INTERNAL_MAP - , itemIsUsed_( 0 ) -#endif -{ - value_.uint_ = value; -} - -Value::Value( double value ) - : type_( realValue ) - , comments_( 0 ) -# ifdef JSON_VALUE_USE_INTERNAL_MAP - , itemIsUsed_( 0 ) -#endif -{ - value_.real_ = value; -} - -Value::Value( const char *value ) - : type_( stringValue ) - , allocated_( true ) - , comments_( 0 ) -# ifdef JSON_VALUE_USE_INTERNAL_MAP - , itemIsUsed_( 0 ) -#endif -{ - value_.string_ = valueAllocator()->duplicateStringValue( value ); -} - - -Value::Value( const char *beginValue, - const char *endValue ) - : type_( stringValue ) - , allocated_( true ) - , comments_( 0 ) -# ifdef JSON_VALUE_USE_INTERNAL_MAP - , itemIsUsed_( 0 ) -#endif -{ - value_.string_ = valueAllocator()->duplicateStringValue( beginValue, - UInt(endValue - beginValue) ); -} - - -Value::Value( const std::string &value ) - : type_( stringValue ) - , allocated_( true ) - , comments_( 0 ) -# ifdef JSON_VALUE_USE_INTERNAL_MAP - , itemIsUsed_( 0 ) -#endif -{ - value_.string_ = valueAllocator()->duplicateStringValue( value.c_str(), - (unsigned int)value.length() ); - -} - -Value::Value( const StaticString &value ) - : type_( stringValue ) - , allocated_( false ) - , comments_( 0 ) -# ifdef JSON_VALUE_USE_INTERNAL_MAP - , itemIsUsed_( 0 ) -#endif -{ - value_.string_ = const_cast( value.c_str() ); -} - - -# ifdef JSON_USE_CPPTL -Value::Value( const CppTL::ConstString &value ) - : type_( stringValue ) - , allocated_( true ) - , comments_( 0 ) -# ifdef JSON_VALUE_USE_INTERNAL_MAP - , itemIsUsed_( 0 ) -#endif -{ - value_.string_ = valueAllocator()->duplicateStringValue( value, value.length() ); -} -# endif - -Value::Value( bool value ) - : type_( booleanValue ) - , comments_( 0 ) -# ifdef JSON_VALUE_USE_INTERNAL_MAP - , itemIsUsed_( 0 ) -#endif -{ - value_.bool_ = value; -} - - -Value::Value( const Value &other ) - : type_( other.type_ ) - , comments_( 0 ) -# ifdef JSON_VALUE_USE_INTERNAL_MAP - , itemIsUsed_( 0 ) -#endif -{ - switch ( type_ ) - { - case nullValue: - case intValue: - case uintValue: - case realValue: - case booleanValue: - value_ = other.value_; - break; - case stringValue: - if ( other.value_.string_ ) - { - value_.string_ = valueAllocator()->duplicateStringValue( other.value_.string_ ); - allocated_ = true; - } - else - value_.string_ = 0; - break; -#ifndef JSON_VALUE_USE_INTERNAL_MAP - case arrayValue: - case objectValue: - value_.map_ = new ObjectValues( *other.value_.map_ ); - break; -#else - case arrayValue: - value_.array_ = arrayAllocator()->newArrayCopy( *other.value_.array_ ); - break; - case objectValue: - value_.map_ = mapAllocator()->newMapCopy( *other.value_.map_ ); - break; -#endif - default: - JSON_ASSERT_UNREACHABLE; - } - if ( other.comments_ ) - { - comments_ = new CommentInfo[numberOfCommentPlacement]; - for ( int comment =0; comment < numberOfCommentPlacement; ++comment ) - { - const CommentInfo &otherComment = other.comments_[comment]; - if ( otherComment.comment_ ) - comments_[comment].setComment( otherComment.comment_ ); - } - } -} - - -Value::~Value() -{ - switch ( type_ ) - { - case nullValue: - case intValue: - case uintValue: - case realValue: - case booleanValue: - break; - case stringValue: - if ( allocated_ ) - valueAllocator()->releaseStringValue( value_.string_ ); - break; -#ifndef JSON_VALUE_USE_INTERNAL_MAP - case arrayValue: - case objectValue: - delete value_.map_; - break; -#else - case arrayValue: - arrayAllocator()->destructArray( value_.array_ ); - break; - case objectValue: - mapAllocator()->destructMap( value_.map_ ); - break; -#endif - default: - JSON_ASSERT_UNREACHABLE; - } - - if ( comments_ ) - delete[] comments_; -} - -Value & -Value::operator=( const Value &other ) -{ - Value temp( other ); - swap( temp ); - return *this; -} - -void -Value::swap( Value &other ) -{ - ValueType temp = type_; - type_ = other.type_; - other.type_ = temp; - std::swap( value_, other.value_ ); - int temp2 = allocated_; - allocated_ = other.allocated_; - other.allocated_ = temp2; -} - -ValueType -Value::type() const -{ - return type_; -} - - -int -Value::compare( const Value &other ) -{ - /* - int typeDelta = other.type_ - type_; - switch ( type_ ) - { - case nullValue: - - return other.type_ == type_; - case intValue: - if ( other.type_.isNumeric() - case uintValue: - case realValue: - case booleanValue: - break; - case stringValue, - break; - case arrayValue: - delete value_.array_; - break; - case objectValue: - delete value_.map_; - default: - JSON_ASSERT_UNREACHABLE; - } - */ - return 0; // unreachable -} - -bool -Value::operator <( const Value &other ) const -{ - int typeDelta = type_ - other.type_; - if ( typeDelta ) - return typeDelta < 0 ? true : false; - switch ( type_ ) - { - case nullValue: - return false; - case intValue: - return value_.int_ < other.value_.int_; - case uintValue: - return value_.uint_ < other.value_.uint_; - case realValue: - return value_.real_ < other.value_.real_; - case booleanValue: - return value_.bool_ < other.value_.bool_; - case stringValue: - return ( value_.string_ == 0 && other.value_.string_ ) - || ( other.value_.string_ - && value_.string_ - && strcmp( value_.string_, other.value_.string_ ) < 0 ); -#ifndef JSON_VALUE_USE_INTERNAL_MAP - case arrayValue: - case objectValue: - { - int delta = int( value_.map_->size() - other.value_.map_->size() ); - if ( delta ) - return delta < 0; - return (*value_.map_) < (*other.value_.map_); - } -#else - case arrayValue: - return value_.array_->compare( *(other.value_.array_) ) < 0; - case objectValue: - return value_.map_->compare( *(other.value_.map_) ) < 0; -#endif - default: - JSON_ASSERT_UNREACHABLE; - } - return 0; // unreachable -} - -bool -Value::operator <=( const Value &other ) const -{ - return !(other > *this); -} - -bool -Value::operator >=( const Value &other ) const -{ - return !(*this < other); -} - -bool -Value::operator >( const Value &other ) const -{ - return other < *this; -} - -bool -Value::operator ==( const Value &other ) const -{ - //if ( type_ != other.type_ ) - // GCC 2.95.3 says: - // attempt to take address of bit-field structure member `Json::Value::type_' - // Beats me, but a temp solves the problem. - int temp = other.type_; - if ( type_ != temp ) - return false; - switch ( type_ ) - { - case nullValue: - return true; - case intValue: - return value_.int_ == other.value_.int_; - case uintValue: - return value_.uint_ == other.value_.uint_; - case realValue: - return value_.real_ == other.value_.real_; - case booleanValue: - return value_.bool_ == other.value_.bool_; - case stringValue: - return ( value_.string_ == other.value_.string_ ) - || ( other.value_.string_ - && value_.string_ - && strcmp( value_.string_, other.value_.string_ ) == 0 ); -#ifndef JSON_VALUE_USE_INTERNAL_MAP - case arrayValue: - case objectValue: - return value_.map_->size() == other.value_.map_->size() - && (*value_.map_) == (*other.value_.map_); -#else - case arrayValue: - return value_.array_->compare( *(other.value_.array_) ) == 0; - case objectValue: - return value_.map_->compare( *(other.value_.map_) ) == 0; -#endif - default: - JSON_ASSERT_UNREACHABLE; - } - return 0; // unreachable -} - -bool -Value::operator !=( const Value &other ) const -{ - return !( *this == other ); -} - -const char * -Value::asCString() const -{ - JSON_ASSERT( type_ == stringValue ); - return value_.string_; -} - - -std::string -Value::asString() const -{ - switch ( type_ ) - { - case nullValue: - return ""; - case stringValue: - return value_.string_ ? value_.string_ : ""; - case booleanValue: - return value_.bool_ ? "true" : "false"; - case intValue: - case uintValue: - case realValue: - case arrayValue: - case objectValue: - JSON_ASSERT_MESSAGE( false, "Type is not convertible to string" ); - default: - JSON_ASSERT_UNREACHABLE; - } - return ""; // unreachable -} - -# ifdef JSON_USE_CPPTL -CppTL::ConstString -Value::asConstString() const -{ - return CppTL::ConstString( asString().c_str() ); -} -# endif - -Value::Int -Value::asInt() const -{ - switch ( type_ ) - { - case nullValue: - return 0; - case intValue: - return value_.int_; - case uintValue: - JSON_ASSERT_MESSAGE( value_.uint_ < (unsigned)maxInt, "integer out of signed integer range" ); - return value_.uint_; - case realValue: - JSON_ASSERT_MESSAGE( value_.real_ >= minInt && value_.real_ <= maxInt, "Real out of signed integer range" ); - return Int( value_.real_ ); - case booleanValue: - return value_.bool_ ? 1 : 0; - case stringValue: - case arrayValue: - case objectValue: - JSON_ASSERT_MESSAGE( false, "Type is not convertible to int" ); - default: - JSON_ASSERT_UNREACHABLE; - } - return 0; // unreachable; -} - -Value::UInt -Value::asUInt() const -{ - switch ( type_ ) - { - case nullValue: - return 0; - case intValue: - JSON_ASSERT_MESSAGE( value_.int_ >= 0, "Negative integer can not be converted to unsigned integer" ); - return value_.int_; - case uintValue: - return value_.uint_; - case realValue: - JSON_ASSERT_MESSAGE( value_.real_ >= 0 && value_.real_ <= maxUInt, "Real out of unsigned integer range" ); - return UInt( value_.real_ ); - case booleanValue: - return value_.bool_ ? 1 : 0; - case stringValue: - case arrayValue: - case objectValue: - JSON_ASSERT_MESSAGE( false, "Type is not convertible to uint" ); - default: - JSON_ASSERT_UNREACHABLE; - } - return 0; // unreachable; -} - -double -Value::asDouble() const -{ - switch ( type_ ) - { - case nullValue: - return 0.0; - case intValue: - return value_.int_; - case uintValue: - return value_.uint_; - case realValue: - return value_.real_; - case booleanValue: - return value_.bool_ ? 1.0 : 0.0; - case stringValue: - case arrayValue: - case objectValue: - JSON_ASSERT_MESSAGE( false, "Type is not convertible to double" ); - default: - JSON_ASSERT_UNREACHABLE; - } - return 0; // unreachable; -} - -bool -Value::asBool() const -{ - switch ( type_ ) - { - case nullValue: - return false; - case intValue: - case uintValue: - return value_.int_ != 0; - case realValue: - return value_.real_ != 0.0; - case booleanValue: - return value_.bool_; - case stringValue: - return value_.string_ && value_.string_[0] != 0; - case arrayValue: - case objectValue: - return value_.map_->size() != 0; - default: - JSON_ASSERT_UNREACHABLE; - } - return false; // unreachable; -} - - -bool -Value::isConvertibleTo( ValueType other ) const -{ - switch ( type_ ) - { - case nullValue: - return true; - case intValue: - return ( other == nullValue && value_.int_ == 0 ) - || other == intValue - || ( other == uintValue && value_.int_ >= 0 ) - || other == realValue - || other == stringValue - || other == booleanValue; - case uintValue: - return ( other == nullValue && value_.uint_ == 0 ) - || ( other == intValue && value_.uint_ <= (unsigned)maxInt ) - || other == uintValue - || other == realValue - || other == stringValue - || other == booleanValue; - case realValue: - return ( other == nullValue && value_.real_ == 0.0 ) - || ( other == intValue && value_.real_ >= minInt && value_.real_ <= maxInt ) - || ( other == uintValue && value_.real_ >= 0 && value_.real_ <= maxUInt ) - || other == realValue - || other == stringValue - || other == booleanValue; - case booleanValue: - return ( other == nullValue && value_.bool_ == false ) - || other == intValue - || other == uintValue - || other == realValue - || other == stringValue - || other == booleanValue; - case stringValue: - return other == stringValue - || ( other == nullValue && (!value_.string_ || value_.string_[0] == 0) ); - case arrayValue: - return other == arrayValue - || ( other == nullValue && value_.map_->size() == 0 ); - case objectValue: - return other == objectValue - || ( other == nullValue && value_.map_->size() == 0 ); - default: - JSON_ASSERT_UNREACHABLE; - } - return false; // unreachable; -} - - -/// Number of values in array or object -Value::UInt -Value::size() const -{ - switch ( type_ ) - { - case nullValue: - case intValue: - case uintValue: - case realValue: - case booleanValue: - case stringValue: - return 0; -#ifndef JSON_VALUE_USE_INTERNAL_MAP - case arrayValue: // size of the array is highest index + 1 - if ( !value_.map_->empty() ) - { - ObjectValues::const_iterator itLast = value_.map_->end(); - --itLast; - return (*itLast).first.index()+1; - } - return 0; - case objectValue: - return Int( value_.map_->size() ); -#else - case arrayValue: - return Int( value_.array_->size() ); - case objectValue: - return Int( value_.map_->size() ); -#endif - default: - JSON_ASSERT_UNREACHABLE; - } - return 0; // unreachable; -} - - -bool -Value::empty() const -{ - if ( isNull() || isArray() || isObject() ) - return size() == 0u; - else - return false; -} - - -bool -Value::operator!() const -{ - return isNull(); -} - - -void -Value::clear() -{ - JSON_ASSERT( type_ == nullValue || type_ == arrayValue || type_ == objectValue ); - - switch ( type_ ) - { -#ifndef JSON_VALUE_USE_INTERNAL_MAP - case arrayValue: - case objectValue: - value_.map_->clear(); - break; -#else - case arrayValue: - value_.array_->clear(); - break; - case objectValue: - value_.map_->clear(); - break; -#endif - default: - break; - } -} - -void -Value::resize( UInt newSize ) -{ - JSON_ASSERT( type_ == nullValue || type_ == arrayValue ); - if ( type_ == nullValue ) - *this = Value( arrayValue ); -#ifndef JSON_VALUE_USE_INTERNAL_MAP - UInt oldSize = size(); - if ( newSize == 0 ) - clear(); - else if ( newSize > oldSize ) - (*this)[ newSize - 1 ]; - else - { - for ( UInt index = newSize; index < oldSize; ++index ) - value_.map_->erase( index ); - assert( size() == newSize ); - } -#else - value_.array_->resize( newSize ); -#endif -} - - -Value & -Value::operator[]( UInt index ) -{ - JSON_ASSERT( type_ == nullValue || type_ == arrayValue ); - if ( type_ == nullValue ) - *this = Value( arrayValue ); -#ifndef JSON_VALUE_USE_INTERNAL_MAP - CZString key( index ); - ObjectValues::iterator it = value_.map_->lower_bound( key ); - if ( it != value_.map_->end() && (*it).first == key ) - return (*it).second; - - ObjectValues::value_type defaultValue( key, null ); - it = value_.map_->insert( it, defaultValue ); - return (*it).second; -#else - return value_.array_->resolveReference( index ); -#endif -} - - -const Value & -Value::operator[]( UInt index ) const -{ - JSON_ASSERT( type_ == nullValue || type_ == arrayValue ); - if ( type_ == nullValue ) - return null; -#ifndef JSON_VALUE_USE_INTERNAL_MAP - CZString key( index ); - ObjectValues::const_iterator it = value_.map_->find( key ); - if ( it == value_.map_->end() ) - return null; - return (*it).second; -#else - Value *value = value_.array_->find( index ); - return value ? *value : null; -#endif -} - - -Value & -Value::operator[]( const char *key ) -{ - return resolveReference( key, false ); -} - - -Value & -Value::resolveReference( const char *key, - bool isStatic ) -{ - JSON_ASSERT( type_ == nullValue || type_ == objectValue ); - if ( type_ == nullValue ) - *this = Value( objectValue ); -#ifndef JSON_VALUE_USE_INTERNAL_MAP - CZString actualKey( key, isStatic ? CZString::noDuplication - : CZString::duplicateOnCopy ); - ObjectValues::iterator it = value_.map_->lower_bound( actualKey ); - if ( it != value_.map_->end() && (*it).first == actualKey ) - return (*it).second; - - ObjectValues::value_type defaultValue( actualKey, null ); - it = value_.map_->insert( it, defaultValue ); - Value &value = (*it).second; - return value; -#else - return value_.map_->resolveReference( key, isStatic ); -#endif -} - - -Value -Value::get( UInt index, - const Value &defaultValue ) const -{ - const Value *value = &((*this)[index]); - return value == &null ? defaultValue : *value; -} - - -bool -Value::isValidIndex( UInt index ) const -{ - return index < size(); -} - - - -const Value & -Value::operator[]( const char *key ) const -{ - JSON_ASSERT( type_ == nullValue || type_ == objectValue ); - if ( type_ == nullValue ) - return null; -#ifndef JSON_VALUE_USE_INTERNAL_MAP - CZString actualKey( key, CZString::noDuplication ); - ObjectValues::const_iterator it = value_.map_->find( actualKey ); - if ( it == value_.map_->end() ) - return null; - return (*it).second; -#else - const Value *value = value_.map_->find( key ); - return value ? *value : null; -#endif -} - - -Value & -Value::operator[]( const std::string &key ) -{ - return (*this)[ key.c_str() ]; -} - - -const Value & -Value::operator[]( const std::string &key ) const -{ - return (*this)[ key.c_str() ]; -} - -Value & -Value::operator[]( const StaticString &key ) -{ - return resolveReference( key, true ); -} - - -# ifdef JSON_USE_CPPTL -Value & -Value::operator[]( const CppTL::ConstString &key ) -{ - return (*this)[ key.c_str() ]; -} - - -const Value & -Value::operator[]( const CppTL::ConstString &key ) const -{ - return (*this)[ key.c_str() ]; -} -# endif - - -Value & -Value::append( const Value &value ) -{ - return (*this)[size()] = value; -} - - -Value -Value::get( const char *key, - const Value &defaultValue ) const -{ - const Value *value = &((*this)[key]); - return value == &null ? defaultValue : *value; -} - - -Value -Value::get( const std::string &key, - const Value &defaultValue ) const -{ - return get( key.c_str(), defaultValue ); -} - -Value -Value::removeMember( const char* key ) -{ - JSON_ASSERT( type_ == nullValue || type_ == objectValue ); - if ( type_ == nullValue ) - return null; -#ifndef JSON_VALUE_USE_INTERNAL_MAP - CZString actualKey( key, CZString::noDuplication ); - ObjectValues::iterator it = value_.map_->find( actualKey ); - if ( it == value_.map_->end() ) - return null; - Value old(it->second); - value_.map_->erase(it); - return old; -#else - Value *value = value_.map_->find( key ); - if (value){ - Value old(*value); - value_.map_.remove( key ); - return old; - } else { - return null; - } -#endif -} - -Value -Value::removeMember( const std::string &key ) -{ - return removeMember( key.c_str() ); -} - -# ifdef JSON_USE_CPPTL -Value -Value::get( const CppTL::ConstString &key, - const Value &defaultValue ) const -{ - return get( key.c_str(), defaultValue ); -} -# endif - -bool -Value::isMember( const char *key ) const -{ - const Value *value = &((*this)[key]); - return value != &null; -} - - -bool -Value::isMember( const std::string &key ) const -{ - return isMember( key.c_str() ); -} - - -# ifdef JSON_USE_CPPTL -bool -Value::isMember( const CppTL::ConstString &key ) const -{ - return isMember( key.c_str() ); -} -#endif - -Value::Members -Value::getMemberNames() const -{ - JSON_ASSERT( type_ == nullValue || type_ == objectValue ); - if ( type_ == nullValue ) - return Value::Members(); - Members members; - members.reserve( value_.map_->size() ); -#ifndef JSON_VALUE_USE_INTERNAL_MAP - ObjectValues::const_iterator it = value_.map_->begin(); - ObjectValues::const_iterator itEnd = value_.map_->end(); - for ( ; it != itEnd; ++it ) - members.push_back( std::string( (*it).first.c_str() ) ); -#else - ValueInternalMap::IteratorState it; - ValueInternalMap::IteratorState itEnd; - value_.map_->makeBeginIterator( it ); - value_.map_->makeEndIterator( itEnd ); - for ( ; !ValueInternalMap::equals( it, itEnd ); ValueInternalMap::increment(it) ) - members.push_back( std::string( ValueInternalMap::key( it ) ) ); -#endif - return members; -} -// -//# ifdef JSON_USE_CPPTL -//EnumMemberNames -//Value::enumMemberNames() const -//{ -// if ( type_ == objectValue ) -// { -// return CppTL::Enum::any( CppTL::Enum::transform( -// CppTL::Enum::keys( *(value_.map_), CppTL::Type() ), -// MemberNamesTransform() ) ); -// } -// return EnumMemberNames(); -//} -// -// -//EnumValues -//Value::enumValues() const -//{ -// if ( type_ == objectValue || type_ == arrayValue ) -// return CppTL::Enum::anyValues( *(value_.map_), -// CppTL::Type() ); -// return EnumValues(); -//} -// -//# endif - - -bool -Value::isNull() const -{ - return type_ == nullValue; -} - - -bool -Value::isBool() const -{ - return type_ == booleanValue; -} - - -bool -Value::isInt() const -{ - return type_ == intValue; -} - - -bool -Value::isUInt() const -{ - return type_ == uintValue; -} - - -bool -Value::isIntegral() const -{ - return type_ == intValue - || type_ == uintValue - || type_ == booleanValue; -} - - -bool -Value::isDouble() const -{ - return type_ == realValue; -} - - -bool -Value::isNumeric() const -{ - return isIntegral() || isDouble(); -} - - -bool -Value::isString() const -{ - return type_ == stringValue; -} - - -bool -Value::isArray() const -{ - return type_ == arrayValue; -} - - -bool -Value::isObject() const -{ - return type_ == objectValue; -} - - -void -Value::setComment( const char *comment, - CommentPlacement placement ) -{ - if ( !comments_ ) - comments_ = new CommentInfo[numberOfCommentPlacement]; - comments_[placement].setComment( comment ); -} - - -void -Value::setComment( const std::string &comment, - CommentPlacement placement ) -{ - setComment( comment.c_str(), placement ); -} - - -bool -Value::hasComment( CommentPlacement placement ) const -{ - return comments_ != 0 && comments_[placement].comment_ != 0; -} - -std::string -Value::getComment( CommentPlacement placement ) const -{ - if ( hasComment(placement) ) - return comments_[placement].comment_; - return ""; -} - - -std::string -Value::toStyledString() const -{ - StyledWriter writer; - return writer.write( *this ); -} - - -Value::const_iterator -Value::begin() const -{ - switch ( type_ ) - { -#ifdef JSON_VALUE_USE_INTERNAL_MAP - case arrayValue: - if ( value_.array_ ) - { - ValueInternalArray::IteratorState it; - value_.array_->makeBeginIterator( it ); - return const_iterator( it ); - } - break; - case objectValue: - if ( value_.map_ ) - { - ValueInternalMap::IteratorState it; - value_.map_->makeBeginIterator( it ); - return const_iterator( it ); - } - break; -#else - case arrayValue: - case objectValue: - if ( value_.map_ ) - return const_iterator( value_.map_->begin() ); - break; -#endif - default: - break; - } - return const_iterator(); -} - -Value::const_iterator -Value::end() const -{ - switch ( type_ ) - { -#ifdef JSON_VALUE_USE_INTERNAL_MAP - case arrayValue: - if ( value_.array_ ) - { - ValueInternalArray::IteratorState it; - value_.array_->makeEndIterator( it ); - return const_iterator( it ); - } - break; - case objectValue: - if ( value_.map_ ) - { - ValueInternalMap::IteratorState it; - value_.map_->makeEndIterator( it ); - return const_iterator( it ); - } - break; -#else - case arrayValue: - case objectValue: - if ( value_.map_ ) - return const_iterator( value_.map_->end() ); - break; -#endif - default: - break; - } - return const_iterator(); -} - - -Value::iterator -Value::begin() -{ - switch ( type_ ) - { -#ifdef JSON_VALUE_USE_INTERNAL_MAP - case arrayValue: - if ( value_.array_ ) - { - ValueInternalArray::IteratorState it; - value_.array_->makeBeginIterator( it ); - return iterator( it ); - } - break; - case objectValue: - if ( value_.map_ ) - { - ValueInternalMap::IteratorState it; - value_.map_->makeBeginIterator( it ); - return iterator( it ); - } - break; -#else - case arrayValue: - case objectValue: - if ( value_.map_ ) - return iterator( value_.map_->begin() ); - break; -#endif - default: - break; - } - return iterator(); -} - -Value::iterator -Value::end() -{ - switch ( type_ ) - { -#ifdef JSON_VALUE_USE_INTERNAL_MAP - case arrayValue: - if ( value_.array_ ) - { - ValueInternalArray::IteratorState it; - value_.array_->makeEndIterator( it ); - return iterator( it ); - } - break; - case objectValue: - if ( value_.map_ ) - { - ValueInternalMap::IteratorState it; - value_.map_->makeEndIterator( it ); - return iterator( it ); - } - break; -#else - case arrayValue: - case objectValue: - if ( value_.map_ ) - return iterator( value_.map_->end() ); - break; -#endif - default: - break; - } - return iterator(); -} - - -// class PathArgument -// ////////////////////////////////////////////////////////////////// - -PathArgument::PathArgument() - : kind_( kindNone ) -{ -} - - -PathArgument::PathArgument( Value::UInt index ) - : index_( index ) - , kind_( kindIndex ) -{ -} - - -PathArgument::PathArgument( const char *key ) - : key_( key ) - , kind_( kindKey ) -{ -} - - -PathArgument::PathArgument( const std::string &key ) - : key_( key.c_str() ) - , kind_( kindKey ) -{ -} - -// class Path -// ////////////////////////////////////////////////////////////////// - -Path::Path( const std::string &path, - const PathArgument &a1, - const PathArgument &a2, - const PathArgument &a3, - const PathArgument &a4, - const PathArgument &a5 ) -{ - InArgs in; - in.push_back( &a1 ); - in.push_back( &a2 ); - in.push_back( &a3 ); - in.push_back( &a4 ); - in.push_back( &a5 ); - makePath( path, in ); -} - - -void -Path::makePath( const std::string &path, - const InArgs &in ) -{ - const char *current = path.c_str(); - const char *end = current + path.length(); - InArgs::const_iterator itInArg = in.begin(); - while ( current != end ) - { - if ( *current == '[' ) - { - ++current; - if ( *current == '%' ) - addPathInArg( path, in, itInArg, PathArgument::kindIndex ); - else - { - Value::UInt index = 0; - for ( ; current != end && *current >= '0' && *current <= '9'; ++current ) - index = index * 10 + Value::UInt(*current - '0'); - args_.push_back( index ); - } - if ( current == end || *current++ != ']' ) - invalidPath( path, int(current - path.c_str()) ); - } - else if ( *current == '%' ) - { - addPathInArg( path, in, itInArg, PathArgument::kindKey ); - ++current; - } - else if ( *current == '.' ) - { - ++current; - } - else - { - const char *beginName = current; - while ( current != end && !strchr( "[.", *current ) ) - ++current; - args_.push_back( std::string( beginName, current ) ); - } - } -} - - -void -Path::addPathInArg( const std::string &path, - const InArgs &in, - InArgs::const_iterator &itInArg, - PathArgument::Kind kind ) -{ - if ( itInArg == in.end() ) - { - // Error: missing argument %d - } - else if ( (*itInArg)->kind_ != kind ) - { - // Error: bad argument type - } - else - { - args_.push_back( **itInArg ); - } -} - - -void -Path::invalidPath( const std::string &path, - int location ) -{ - // Error: invalid path. -} - - -const Value & -Path::resolve( const Value &root ) const -{ - const Value *node = &root; - for ( Args::const_iterator it = args_.begin(); it != args_.end(); ++it ) - { - const PathArgument &arg = *it; - if ( arg.kind_ == PathArgument::kindIndex ) - { - if ( !node->isArray() || node->isValidIndex( arg.index_ ) ) - { - // Error: unable to resolve path (array value expected at position... - } - node = &((*node)[arg.index_]); - } - else if ( arg.kind_ == PathArgument::kindKey ) - { - if ( !node->isObject() ) - { - // Error: unable to resolve path (object value expected at position...) - } - node = &((*node)[arg.key_]); - if ( node == &Value::null ) - { - // Error: unable to resolve path (object has no member named '' at position...) - } - } - } - return *node; -} - - -Value -Path::resolve( const Value &root, - const Value &defaultValue ) const -{ - const Value *node = &root; - for ( Args::const_iterator it = args_.begin(); it != args_.end(); ++it ) - { - const PathArgument &arg = *it; - if ( arg.kind_ == PathArgument::kindIndex ) - { - if ( !node->isArray() || node->isValidIndex( arg.index_ ) ) - return defaultValue; - node = &((*node)[arg.index_]); - } - else if ( arg.kind_ == PathArgument::kindKey ) - { - if ( !node->isObject() ) - return defaultValue; - node = &((*node)[arg.key_]); - if ( node == &Value::null ) - return defaultValue; - } - } - return *node; -} - - -Value & -Path::make( Value &root ) const -{ - Value *node = &root; - for ( Args::const_iterator it = args_.begin(); it != args_.end(); ++it ) - { - const PathArgument &arg = *it; - if ( arg.kind_ == PathArgument::kindIndex ) - { - if ( !node->isArray() ) - { - // Error: node is not an array at position ... - } - node = &((*node)[arg.index_]); - } - else if ( arg.kind_ == PathArgument::kindKey ) - { - if ( !node->isObject() ) - { - // Error: node is not an object at position... - } - node = &((*node)[arg.key_]); - } - } - return *node; -} - - -} // namespace Json diff --git a/util/json/json_valueiterator.inl b/util/json/json_valueiterator.inl deleted file mode 100644 index 736e260e..00000000 --- a/util/json/json_valueiterator.inl +++ /dev/null @@ -1,292 +0,0 @@ -// included by json_value.cpp -// everything is within Json namespace - - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// class ValueIteratorBase -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// - -ValueIteratorBase::ValueIteratorBase() -#ifndef JSON_VALUE_USE_INTERNAL_MAP - : current_() - , isNull_( true ) -{ -} -#else - : isArray_( true ) - , isNull_( true ) -{ - iterator_.array_ = ValueInternalArray::IteratorState(); -} -#endif - - -#ifndef JSON_VALUE_USE_INTERNAL_MAP -ValueIteratorBase::ValueIteratorBase( const Value::ObjectValues::iterator ¤t ) - : current_( current ) - , isNull_( false ) -{ -} -#else -ValueIteratorBase::ValueIteratorBase( const ValueInternalArray::IteratorState &state ) - : isArray_( true ) -{ - iterator_.array_ = state; -} - - -ValueIteratorBase::ValueIteratorBase( const ValueInternalMap::IteratorState &state ) - : isArray_( false ) -{ - iterator_.map_ = state; -} -#endif - -Value & -ValueIteratorBase::deref() const -{ -#ifndef JSON_VALUE_USE_INTERNAL_MAP - return current_->second; -#else - if ( isArray_ ) - return ValueInternalArray::dereference( iterator_.array_ ); - return ValueInternalMap::value( iterator_.map_ ); -#endif -} - - -void -ValueIteratorBase::increment() -{ -#ifndef JSON_VALUE_USE_INTERNAL_MAP - ++current_; -#else - if ( isArray_ ) - ValueInternalArray::increment( iterator_.array_ ); - ValueInternalMap::increment( iterator_.map_ ); -#endif -} - - -void -ValueIteratorBase::decrement() -{ -#ifndef JSON_VALUE_USE_INTERNAL_MAP - --current_; -#else - if ( isArray_ ) - ValueInternalArray::decrement( iterator_.array_ ); - ValueInternalMap::decrement( iterator_.map_ ); -#endif -} - - -ValueIteratorBase::difference_type -ValueIteratorBase::computeDistance( const SelfType &other ) const -{ -#ifndef JSON_VALUE_USE_INTERNAL_MAP -# ifdef JSON_USE_CPPTL_SMALLMAP - return current_ - other.current_; -# else - // Iterator for null value are initialized using the default - // constructor, which initialize current_ to the default - // std::map::iterator. As begin() and end() are two instance - // of the default std::map::iterator, they can not be compared. - // To allow this, we handle this comparison specifically. - if ( isNull_ && other.isNull_ ) - { - return 0; - } - - - // Usage of std::distance is not portable (does not compile with Sun Studio 12 RogueWave STL, - // which is the one used by default). - // Using a portable hand-made version for non random iterator instead: - // return difference_type( std::distance( current_, other.current_ ) ); - difference_type myDistance = 0; - for ( Value::ObjectValues::iterator it = current_; it != other.current_; ++it ) - { - ++myDistance; - } - return myDistance; -# endif -#else - if ( isArray_ ) - return ValueInternalArray::distance( iterator_.array_, other.iterator_.array_ ); - return ValueInternalMap::distance( iterator_.map_, other.iterator_.map_ ); -#endif -} - - -bool -ValueIteratorBase::isEqual( const SelfType &other ) const -{ -#ifndef JSON_VALUE_USE_INTERNAL_MAP - if ( isNull_ ) - { - return other.isNull_; - } - return current_ == other.current_; -#else - if ( isArray_ ) - return ValueInternalArray::equals( iterator_.array_, other.iterator_.array_ ); - return ValueInternalMap::equals( iterator_.map_, other.iterator_.map_ ); -#endif -} - - -void -ValueIteratorBase::copy( const SelfType &other ) -{ -#ifndef JSON_VALUE_USE_INTERNAL_MAP - current_ = other.current_; -#else - if ( isArray_ ) - iterator_.array_ = other.iterator_.array_; - iterator_.map_ = other.iterator_.map_; -#endif -} - - -Value -ValueIteratorBase::key() const -{ -#ifndef JSON_VALUE_USE_INTERNAL_MAP - const Value::CZString czstring = (*current_).first; - if ( czstring.c_str() ) - { - if ( czstring.isStaticString() ) - return Value( StaticString( czstring.c_str() ) ); - return Value( czstring.c_str() ); - } - return Value( czstring.index() ); -#else - if ( isArray_ ) - return Value( ValueInternalArray::indexOf( iterator_.array_ ) ); - bool isStatic; - const char *memberName = ValueInternalMap::key( iterator_.map_, isStatic ); - if ( isStatic ) - return Value( StaticString( memberName ) ); - return Value( memberName ); -#endif -} - - -UInt -ValueIteratorBase::index() const -{ -#ifndef JSON_VALUE_USE_INTERNAL_MAP - const Value::CZString czstring = (*current_).first; - if ( !czstring.c_str() ) - return czstring.index(); - return Value::UInt( -1 ); -#else - if ( isArray_ ) - return Value::UInt( ValueInternalArray::indexOf( iterator_.array_ ) ); - return Value::UInt( -1 ); -#endif -} - - -const char * -ValueIteratorBase::memberName() const -{ -#ifndef JSON_VALUE_USE_INTERNAL_MAP - const char *name = (*current_).first.c_str(); - return name ? name : ""; -#else - if ( !isArray_ ) - return ValueInternalMap::key( iterator_.map_ ); - return ""; -#endif -} - - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// class ValueConstIterator -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// - -ValueConstIterator::ValueConstIterator() -{ -} - - -#ifndef JSON_VALUE_USE_INTERNAL_MAP -ValueConstIterator::ValueConstIterator( const Value::ObjectValues::iterator ¤t ) - : ValueIteratorBase( current ) -{ -} -#else -ValueConstIterator::ValueConstIterator( const ValueInternalArray::IteratorState &state ) - : ValueIteratorBase( state ) -{ -} - -ValueConstIterator::ValueConstIterator( const ValueInternalMap::IteratorState &state ) - : ValueIteratorBase( state ) -{ -} -#endif - -ValueConstIterator & -ValueConstIterator::operator =( const ValueIteratorBase &other ) -{ - copy( other ); - return *this; -} - - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// class ValueIterator -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// - -ValueIterator::ValueIterator() -{ -} - - -#ifndef JSON_VALUE_USE_INTERNAL_MAP -ValueIterator::ValueIterator( const Value::ObjectValues::iterator ¤t ) - : ValueIteratorBase( current ) -{ -} -#else -ValueIterator::ValueIterator( const ValueInternalArray::IteratorState &state ) - : ValueIteratorBase( state ) -{ -} - -ValueIterator::ValueIterator( const ValueInternalMap::IteratorState &state ) - : ValueIteratorBase( state ) -{ -} -#endif - -ValueIterator::ValueIterator( const ValueConstIterator &other ) - : ValueIteratorBase( other ) -{ -} - -ValueIterator::ValueIterator( const ValueIterator &other ) - : ValueIteratorBase( other ) -{ -} - -ValueIterator & -ValueIterator::operator =( const SelfType &other ) -{ - copy( other ); - return *this; -} diff --git a/util/json/json_writer.cpp b/util/json/json_writer.cpp deleted file mode 100644 index 2a9f0dba..00000000 --- a/util/json/json_writer.cpp +++ /dev/null @@ -1,829 +0,0 @@ -#include "writer.h" -#include -#include -#include -#include -#include -#include -#include - -#if _MSC_VER >= 1400 // VC++ 8.0 -#pragma warning( disable : 4996 ) // disable warning about strdup being deprecated. -#endif - -namespace Json { - -static bool isControlCharacter(char ch) -{ - return ch > 0 && ch <= 0x1F; -} - -static bool containsControlCharacter( const char* str ) -{ - while ( *str ) - { - if ( isControlCharacter( *(str++) ) ) - return true; - } - return false; -} -static void uintToString( unsigned int value, - char *¤t ) -{ - *--current = 0; - do - { - *--current = (value % 10) + '0'; - value /= 10; - } - while ( value != 0 ); -} - -std::string valueToString( Int value ) -{ - char buffer[32]; - char *current = buffer + sizeof(buffer); - bool isNegative = value < 0; - if ( isNegative ) - value = -value; - uintToString( UInt(value), current ); - if ( isNegative ) - *--current = '-'; - assert( current >= buffer ); - return current; -} - - -std::string valueToString( UInt value ) -{ - char buffer[32]; - char *current = buffer + sizeof(buffer); - uintToString( value, current ); - assert( current >= buffer ); - return current; -} - -std::string valueToString( double value ) -{ - char buffer[32]; -#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005 to avoid warning. - sprintf_s(buffer, sizeof(buffer), "%#.16g", value); -#else - sprintf(buffer, "%#.16g", value); -#endif - char* ch = buffer + strlen(buffer) - 1; - if (*ch != '0') return buffer; // nothing to truncate, so save time - while(ch > buffer && *ch == '0'){ - --ch; - } - char* last_nonzero = ch; - while(ch >= buffer){ - switch(*ch){ - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - --ch; - continue; - case '.': - // Truncate zeroes to save bytes in output, but keep one. - *(last_nonzero+2) = '\0'; - return buffer; - default: - return buffer; - } - } - return buffer; -} - - -std::string valueToString( bool value ) -{ - return value ? "true" : "false"; -} - -std::string valueToQuotedString( const char *value ) -{ - // Not sure how to handle unicode... - if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && !containsControlCharacter( value )) - return std::string("\"") + value + "\""; - // We have to walk value and escape any special characters. - // Appending to std::string is not efficient, but this should be rare. - // (Note: forward slashes are *not* rare, but I am not escaping them.) - unsigned maxsize = strlen(value)*2 + 3; // allescaped+quotes+NULL - std::string result; - result.reserve(maxsize); // to avoid lots of mallocs - result += "\""; - for (const char* c=value; *c != 0; ++c) - { - switch(*c) - { - case '\"': - result += "\\\""; - break; - case '\\': - result += "\\\\"; - break; - case '\b': - result += "\\b"; - break; - case '\f': - result += "\\f"; - break; - case '\n': - result += "\\n"; - break; - case '\r': - result += "\\r"; - break; - case '\t': - result += "\\t"; - break; - //case '/': - // Even though \/ is considered a legal escape in JSON, a bare - // slash is also legal, so I see no reason to escape it. - // (I hope I am not misunderstanding something. - // blep notes: actually escaping \/ may be useful in javascript to avoid (*c); - result += oss.str(); - } - else - { - result += *c; - } - break; - } - } - result += "\""; - return result; -} - -// Class Writer -// ////////////////////////////////////////////////////////////////// -Writer::~Writer() -{ -} - - -// Class FastWriter -// ////////////////////////////////////////////////////////////////// - -FastWriter::FastWriter() - : yamlCompatiblityEnabled_( false ) -{ -} - - -void -FastWriter::enableYAMLCompatibility() -{ - yamlCompatiblityEnabled_ = true; -} - - -std::string -FastWriter::write( const Value &root ) -{ - document_ = ""; - writeValue( root ); - document_ += "\n"; - return document_; -} - - -void -FastWriter::writeValue( const Value &value ) -{ - switch ( value.type() ) - { - case nullValue: - document_ += "null"; - break; - case intValue: - document_ += valueToString( value.asInt() ); - break; - case uintValue: - document_ += valueToString( value.asUInt() ); - break; - case realValue: - document_ += valueToString( value.asDouble() ); - break; - case stringValue: - document_ += valueToQuotedString( value.asCString() ); - break; - case booleanValue: - document_ += valueToString( value.asBool() ); - break; - case arrayValue: - { - document_ += "["; - int size = value.size(); - for ( int index =0; index < size; ++index ) - { - if ( index > 0 ) - document_ += ","; - writeValue( value[index] ); - } - document_ += "]"; - } - break; - case objectValue: - { - Value::Members members( value.getMemberNames() ); - document_ += "{"; - for ( Value::Members::iterator it = members.begin(); - it != members.end(); - ++it ) - { - const std::string &name = *it; - if ( it != members.begin() ) - document_ += ","; - document_ += valueToQuotedString( name.c_str() ); - document_ += yamlCompatiblityEnabled_ ? ": " - : ":"; - writeValue( value[name] ); - } - document_ += "}"; - } - break; - } -} - - -// Class StyledWriter -// ////////////////////////////////////////////////////////////////// - -StyledWriter::StyledWriter() - : rightMargin_( 74 ) - , indentSize_( 3 ) -{ -} - - -std::string -StyledWriter::write( const Value &root ) -{ - document_ = ""; - addChildValues_ = false; - indentString_ = ""; - writeCommentBeforeValue( root ); - writeValue( root ); - writeCommentAfterValueOnSameLine( root ); - document_ += "\n"; - return document_; -} - - -void -StyledWriter::writeValue( const Value &value ) -{ - switch ( value.type() ) - { - case nullValue: - pushValue( "null" ); - break; - case intValue: - pushValue( valueToString( value.asInt() ) ); - break; - case uintValue: - pushValue( valueToString( value.asUInt() ) ); - break; - case realValue: - pushValue( valueToString( value.asDouble() ) ); - break; - case stringValue: - pushValue( valueToQuotedString( value.asCString() ) ); - break; - case booleanValue: - pushValue( valueToString( value.asBool() ) ); - break; - case arrayValue: - writeArrayValue( value); - break; - case objectValue: - { - Value::Members members( value.getMemberNames() ); - if ( members.empty() ) - pushValue( "{}" ); - else - { - writeWithIndent( "{" ); - indent(); - Value::Members::iterator it = members.begin(); - while ( true ) - { - const std::string &name = *it; - const Value &childValue = value[name]; - writeCommentBeforeValue( childValue ); - writeWithIndent( valueToQuotedString( name.c_str() ) ); - document_ += " : "; - writeValue( childValue ); - if ( ++it == members.end() ) - { - writeCommentAfterValueOnSameLine( childValue ); - break; - } - document_ += ","; - writeCommentAfterValueOnSameLine( childValue ); - } - unindent(); - writeWithIndent( "}" ); - } - } - break; - } -} - - -void -StyledWriter::writeArrayValue( const Value &value ) -{ - unsigned size = value.size(); - if ( size == 0 ) - pushValue( "[]" ); - else - { - bool isArrayMultiLine = isMultineArray( value ); - if ( isArrayMultiLine ) - { - writeWithIndent( "[" ); - indent(); - bool hasChildValue = !childValues_.empty(); - unsigned index =0; - while ( true ) - { - const Value &childValue = value[index]; - writeCommentBeforeValue( childValue ); - if ( hasChildValue ) - writeWithIndent( childValues_[index] ); - else - { - writeIndent(); - writeValue( childValue ); - } - if ( ++index == size ) - { - writeCommentAfterValueOnSameLine( childValue ); - break; - } - document_ += ","; - writeCommentAfterValueOnSameLine( childValue ); - } - unindent(); - writeWithIndent( "]" ); - } - else // output on a single line - { - assert( childValues_.size() == size ); - document_ += "[ "; - for ( unsigned index =0; index < size; ++index ) - { - if ( index > 0 ) - document_ += ", "; - document_ += childValues_[index]; - } - document_ += " ]"; - } - } -} - - -bool -StyledWriter::isMultineArray( const Value &value ) -{ - int size = value.size(); - bool isMultiLine = size*3 >= rightMargin_ ; - childValues_.clear(); - for ( int index =0; index < size && !isMultiLine; ++index ) - { - const Value &childValue = value[index]; - isMultiLine = isMultiLine || - ( (childValue.isArray() || childValue.isObject()) && - childValue.size() > 0 ); - } - if ( !isMultiLine ) // check if line length > max line length - { - childValues_.reserve( size ); - addChildValues_ = true; - int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]' - for ( int index =0; index < size && !isMultiLine; ++index ) - { - writeValue( value[index] ); - lineLength += int( childValues_[index].length() ); - isMultiLine = isMultiLine && hasCommentForValue( value[index] ); - } - addChildValues_ = false; - isMultiLine = isMultiLine || lineLength >= rightMargin_; - } - return isMultiLine; -} - - -void -StyledWriter::pushValue( const std::string &value ) -{ - if ( addChildValues_ ) - childValues_.push_back( value ); - else - document_ += value; -} - - -void -StyledWriter::writeIndent() -{ - if ( !document_.empty() ) - { - char last = document_[document_.length()-1]; - if ( last == ' ' ) // already indented - return; - if ( last != '\n' ) // Comments may add new-line - document_ += '\n'; - } - document_ += indentString_; -} - - -void -StyledWriter::writeWithIndent( const std::string &value ) -{ - writeIndent(); - document_ += value; -} - - -void -StyledWriter::indent() -{ - indentString_ += std::string( indentSize_, ' ' ); -} - - -void -StyledWriter::unindent() -{ - assert( int(indentString_.size()) >= indentSize_ ); - indentString_.resize( indentString_.size() - indentSize_ ); -} - - -void -StyledWriter::writeCommentBeforeValue( const Value &root ) -{ - if ( !root.hasComment( commentBefore ) ) - return; - document_ += normalizeEOL( root.getComment( commentBefore ) ); - document_ += "\n"; -} - - -void -StyledWriter::writeCommentAfterValueOnSameLine( const Value &root ) -{ - if ( root.hasComment( commentAfterOnSameLine ) ) - document_ += " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) ); - - if ( root.hasComment( commentAfter ) ) - { - document_ += "\n"; - document_ += normalizeEOL( root.getComment( commentAfter ) ); - document_ += "\n"; - } -} - - -bool -StyledWriter::hasCommentForValue( const Value &value ) -{ - return value.hasComment( commentBefore ) - || value.hasComment( commentAfterOnSameLine ) - || value.hasComment( commentAfter ); -} - - -std::string -StyledWriter::normalizeEOL( const std::string &text ) -{ - std::string normalized; - normalized.reserve( text.length() ); - const char *begin = text.c_str(); - const char *end = begin + text.length(); - const char *current = begin; - while ( current != end ) - { - char c = *current++; - if ( c == '\r' ) // mac or dos EOL - { - if ( *current == '\n' ) // convert dos EOL - ++current; - normalized += '\n'; - } - else // handle unix EOL & other char - normalized += c; - } - return normalized; -} - - -// Class StyledStreamWriter -// ////////////////////////////////////////////////////////////////// - -StyledStreamWriter::StyledStreamWriter( std::string indentation ) - : document_(NULL) - , rightMargin_( 74 ) - , indentation_( indentation ) -{ -} - - -void -StyledStreamWriter::write( std::ostream &out, const Value &root ) -{ - document_ = &out; - addChildValues_ = false; - indentString_ = ""; - writeCommentBeforeValue( root ); - writeValue( root ); - writeCommentAfterValueOnSameLine( root ); - *document_ << "\n"; - document_ = NULL; // Forget the stream, for safety. -} - - -void -StyledStreamWriter::writeValue( const Value &value ) -{ - switch ( value.type() ) - { - case nullValue: - pushValue( "null" ); - break; - case intValue: - pushValue( valueToString( value.asInt() ) ); - break; - case uintValue: - pushValue( valueToString( value.asUInt() ) ); - break; - case realValue: - pushValue( valueToString( value.asDouble() ) ); - break; - case stringValue: - pushValue( valueToQuotedString( value.asCString() ) ); - break; - case booleanValue: - pushValue( valueToString( value.asBool() ) ); - break; - case arrayValue: - writeArrayValue( value); - break; - case objectValue: - { - Value::Members members( value.getMemberNames() ); - if ( members.empty() ) - pushValue( "{}" ); - else - { - writeWithIndent( "{" ); - indent(); - Value::Members::iterator it = members.begin(); - while ( true ) - { - const std::string &name = *it; - const Value &childValue = value[name]; - writeCommentBeforeValue( childValue ); - writeWithIndent( valueToQuotedString( name.c_str() ) ); - *document_ << " : "; - writeValue( childValue ); - if ( ++it == members.end() ) - { - writeCommentAfterValueOnSameLine( childValue ); - break; - } - *document_ << ","; - writeCommentAfterValueOnSameLine( childValue ); - } - unindent(); - writeWithIndent( "}" ); - } - } - break; - } -} - - -void -StyledStreamWriter::writeArrayValue( const Value &value ) -{ - unsigned size = value.size(); - if ( size == 0 ) - pushValue( "[]" ); - else - { - bool isArrayMultiLine = isMultineArray( value ); - if ( isArrayMultiLine ) - { - writeWithIndent( "[" ); - indent(); - bool hasChildValue = !childValues_.empty(); - unsigned index =0; - while ( true ) - { - const Value &childValue = value[index]; - writeCommentBeforeValue( childValue ); - if ( hasChildValue ) - writeWithIndent( childValues_[index] ); - else - { - writeIndent(); - writeValue( childValue ); - } - if ( ++index == size ) - { - writeCommentAfterValueOnSameLine( childValue ); - break; - } - *document_ << ","; - writeCommentAfterValueOnSameLine( childValue ); - } - unindent(); - writeWithIndent( "]" ); - } - else // output on a single line - { - assert( childValues_.size() == size ); - *document_ << "[ "; - for ( unsigned index =0; index < size; ++index ) - { - if ( index > 0 ) - *document_ << ", "; - *document_ << childValues_[index]; - } - *document_ << " ]"; - } - } -} - - -bool -StyledStreamWriter::isMultineArray( const Value &value ) -{ - int size = value.size(); - bool isMultiLine = size*3 >= rightMargin_ ; - childValues_.clear(); - for ( int index =0; index < size && !isMultiLine; ++index ) - { - const Value &childValue = value[index]; - isMultiLine = isMultiLine || - ( (childValue.isArray() || childValue.isObject()) && - childValue.size() > 0 ); - } - if ( !isMultiLine ) // check if line length > max line length - { - childValues_.reserve( size ); - addChildValues_ = true; - int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]' - for ( int index =0; index < size && !isMultiLine; ++index ) - { - writeValue( value[index] ); - lineLength += int( childValues_[index].length() ); - isMultiLine = isMultiLine && hasCommentForValue( value[index] ); - } - addChildValues_ = false; - isMultiLine = isMultiLine || lineLength >= rightMargin_; - } - return isMultiLine; -} - - -void -StyledStreamWriter::pushValue( const std::string &value ) -{ - if ( addChildValues_ ) - childValues_.push_back( value ); - else - *document_ << value; -} - - -void -StyledStreamWriter::writeIndent() -{ - /* - Some comments in this method would have been nice. ;-) - - if ( !document_.empty() ) - { - char last = document_[document_.length()-1]; - if ( last == ' ' ) // already indented - return; - if ( last != '\n' ) // Comments may add new-line - *document_ << '\n'; - } - */ - *document_ << '\n' << indentString_; -} - - -void -StyledStreamWriter::writeWithIndent( const std::string &value ) -{ - writeIndent(); - *document_ << value; -} - - -void -StyledStreamWriter::indent() -{ - indentString_ += indentation_; -} - - -void -StyledStreamWriter::unindent() -{ - assert( indentString_.size() >= indentation_.size() ); - indentString_.resize( indentString_.size() - indentation_.size() ); -} - - -void -StyledStreamWriter::writeCommentBeforeValue( const Value &root ) -{ - if ( !root.hasComment( commentBefore ) ) - return; - *document_ << normalizeEOL( root.getComment( commentBefore ) ); - *document_ << "\n"; -} - - -void -StyledStreamWriter::writeCommentAfterValueOnSameLine( const Value &root ) -{ - if ( root.hasComment( commentAfterOnSameLine ) ) - *document_ << " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) ); - - if ( root.hasComment( commentAfter ) ) - { - *document_ << "\n"; - *document_ << normalizeEOL( root.getComment( commentAfter ) ); - *document_ << "\n"; - } -} - - -bool -StyledStreamWriter::hasCommentForValue( const Value &value ) -{ - return value.hasComment( commentBefore ) - || value.hasComment( commentAfterOnSameLine ) - || value.hasComment( commentAfter ); -} - - -std::string -StyledStreamWriter::normalizeEOL( const std::string &text ) -{ - std::string normalized; - normalized.reserve( text.length() ); - const char *begin = text.c_str(); - const char *end = begin + text.length(); - const char *current = begin; - while ( current != end ) - { - char c = *current++; - if ( c == '\r' ) // mac or dos EOL - { - if ( *current == '\n' ) // convert dos EOL - ++current; - normalized += '\n'; - } - else // handle unix EOL & other char - normalized += c; - } - return normalized; -} - - -std::ostream& operator<<( std::ostream &sout, const Value &root ) -{ - Json::StyledStreamWriter writer; - writer.write(sout, root); - return sout; -} - - -} // namespace Json diff --git a/util/json/reader.h b/util/json/reader.h deleted file mode 100644 index ee1d6a24..00000000 --- a/util/json/reader.h +++ /dev/null @@ -1,196 +0,0 @@ -#ifndef CPPTL_JSON_READER_H_INCLUDED -# define CPPTL_JSON_READER_H_INCLUDED - -# include "features.h" -# include "value.h" -# include -# include -# include -# include - -namespace Json { - - /** \brief Unserialize a JSON document into a Value. - * - */ - class JSON_API Reader - { - public: - typedef char Char; - typedef const Char *Location; - - /** \brief Constructs a Reader allowing all features - * for parsing. - */ - Reader(); - - /** \brief Constructs a Reader allowing the specified feature set - * for parsing. - */ - Reader( const Features &features ); - - /** \brief Read a Value from a JSON document. - * \param document UTF-8 encoded string containing the document to read. - * \param root [out] Contains the root value of the document if it was - * successfully parsed. - * \param collectComments \c true to collect comment and allow writing them back during - * serialization, \c false to discard comments. - * This parameter is ignored if Features::allowComments_ - * is \c false. - * \return \c true if the document was successfully parsed, \c false if an error occurred. - */ - bool parse( const std::string &document, - Value &root, - bool collectComments = true ); - - /** \brief Read a Value from a JSON document. - * \param document UTF-8 encoded string containing the document to read. - * \param root [out] Contains the root value of the document if it was - * successfully parsed. - * \param collectComments \c true to collect comment and allow writing them back during - * serialization, \c false to discard comments. - * This parameter is ignored if Features::allowComments_ - * is \c false. - * \return \c true if the document was successfully parsed, \c false if an error occurred. - */ - bool parse( const char *beginDoc, const char *endDoc, - Value &root, - bool collectComments = true ); - - /// \brief Parse from input stream. - /// \see Json::operator>>(std::istream&, Json::Value&). - bool parse( std::istream &is, - Value &root, - bool collectComments = true ); - - /** \brief Returns a user friendly string that list errors in the parsed document. - * \return Formatted error message with the list of errors with their location in - * the parsed document. An empty string is returned if no error occurred - * during parsing. - */ - std::string getFormatedErrorMessages() const; - - private: - enum TokenType - { - tokenEndOfStream = 0, - tokenObjectBegin, - tokenObjectEnd, - tokenArrayBegin, - tokenArrayEnd, - tokenString, - tokenNumber, - tokenTrue, - tokenFalse, - tokenNull, - tokenArraySeparator, - tokenMemberSeparator, - tokenComment, - tokenError - }; - - class Token - { - public: - TokenType type_; - Location start_; - Location end_; - }; - - class ErrorInfo - { - public: - Token token_; - std::string message_; - Location extra_; - }; - - typedef std::deque Errors; - - bool expectToken( TokenType type, Token &token, const char *message ); - bool readToken( Token &token ); - void skipSpaces(); - bool match( Location pattern, - int patternLength ); - bool readComment(); - bool readCStyleComment(); - bool readCppStyleComment(); - bool readString(); - void readNumber(); - bool readValue(); - bool readObject( Token &token ); - bool readArray( Token &token ); - bool decodeNumber( Token &token ); - bool decodeString( Token &token ); - bool decodeString( Token &token, std::string &decoded ); - bool decodeDouble( Token &token ); - bool decodeUnicodeCodePoint( Token &token, - Location ¤t, - Location end, - unsigned int &unicode ); - bool decodeUnicodeEscapeSequence( Token &token, - Location ¤t, - Location end, - unsigned int &unicode ); - bool addError( const std::string &message, - Token &token, - Location extra = 0 ); - bool recoverFromError( TokenType skipUntilToken ); - bool addErrorAndRecover( const std::string &message, - Token &token, - TokenType skipUntilToken ); - void skipUntilSpace(); - Value ¤tValue(); - Char getNextChar(); - void getLocationLineAndColumn( Location location, - int &line, - int &column ) const; - std::string getLocationLineAndColumn( Location location ) const; - void addComment( Location begin, - Location end, - CommentPlacement placement ); - void skipCommentTokens( Token &token ); - - typedef std::stack Nodes; - Nodes nodes_; - Errors errors_; - std::string document_; - Location begin_; - Location end_; - Location current_; - Location lastValueEnd_; - Value *lastValue_; - std::string commentsBefore_; - Features features_; - bool collectComments_; - }; - - /** \brief Read from 'sin' into 'root'. - - Always keep comments from the input JSON. - - This can be used to read a file into a particular sub-object. - For example: - \code - Json::Value root; - cin >> root["dir"]["file"]; - cout << root; - \endcode - Result: - \verbatim - { - "dir": { - "file": { - // The input stream JSON would be nested here. - } - } - } - \endverbatim - \throw std::exception on parse error. - \see Json::operator<<() - */ - std::istream& operator>>( std::istream&, Value& ); - -} // namespace Json - -#endif // CPPTL_JSON_READER_H_INCLUDED diff --git a/util/json/value.h b/util/json/value.h deleted file mode 100644 index 58bfd88e..00000000 --- a/util/json/value.h +++ /dev/null @@ -1,1069 +0,0 @@ -#ifndef CPPTL_JSON_H_INCLUDED -# define CPPTL_JSON_H_INCLUDED - -# include "forwards.h" -# include -# include - -# ifndef JSON_USE_CPPTL_SMALLMAP -# include -# else -# include -# endif -# ifdef JSON_USE_CPPTL -# include -# endif - -/** \brief JSON (JavaScript Object Notation). - */ -namespace Json { - - /** \brief Type of the value held by a Value object. - */ - enum ValueType - { - nullValue = 0, ///< 'null' value - intValue, ///< signed integer value - uintValue, ///< unsigned integer value - realValue, ///< double value - stringValue, ///< UTF-8 string value - booleanValue, ///< bool value - arrayValue, ///< array value (ordered list) - objectValue ///< object value (collection of name/value pairs). - }; - - enum CommentPlacement - { - commentBefore = 0, ///< a comment placed on the line before a value - commentAfterOnSameLine, ///< a comment just after a value on the same line - commentAfter, ///< a comment on the line after a value (only make sense for root value) - numberOfCommentPlacement - }; - -//# ifdef JSON_USE_CPPTL -// typedef CppTL::AnyEnumerator EnumMemberNames; -// typedef CppTL::AnyEnumerator EnumValues; -//# endif - - /** \brief Lightweight wrapper to tag static string. - * - * Value constructor and objectValue member assignement takes advantage of the - * StaticString and avoid the cost of string duplication when storing the - * string or the member name. - * - * Example of usage: - * \code - * Json::Value aValue( StaticString("some text") ); - * Json::Value object; - * static const StaticString code("code"); - * object[code] = 1234; - * \endcode - */ - class JSON_API StaticString - { - public: - explicit StaticString( const char *czstring ) - : str_( czstring ) - { - } - - operator const char *() const - { - return str_; - } - - const char *c_str() const - { - return str_; - } - - private: - const char *str_; - }; - - /** \brief Represents a JSON value. - * - * This class is a discriminated union wrapper that can represents a: - * - signed integer [range: Value::minInt - Value::maxInt] - * - unsigned integer (range: 0 - Value::maxUInt) - * - double - * - UTF-8 string - * - boolean - * - 'null' - * - an ordered list of Value - * - collection of name/value pairs (javascript object) - * - * The type of the held value is represented by a #ValueType and - * can be obtained using type(). - * - * values of an #objectValue or #arrayValue can be accessed using operator[]() methods. - * Non const methods will automatically create the a #nullValue element - * if it does not exist. - * The sequence of an #arrayValue will be automatically resize and initialized - * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue. - * - * The get() methods can be used to obtanis default value in the case the required element - * does not exist. - * - * It is possible to iterate over the list of a #objectValue values using - * the getMemberNames() method. - */ - class JSON_API Value - { - friend class ValueIteratorBase; -# ifdef JSON_VALUE_USE_INTERNAL_MAP - friend class ValueInternalLink; - friend class ValueInternalMap; -# endif - public: - typedef std::vector Members; - typedef ValueIterator iterator; - typedef ValueConstIterator const_iterator; - typedef Json::UInt UInt; - typedef Json::Int Int; - typedef UInt ArrayIndex; - - static const Value null; - static const Int minInt; - static const Int maxInt; - static const UInt maxUInt; - - private: -#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION -# ifndef JSON_VALUE_USE_INTERNAL_MAP - class CZString - { - public: - enum DuplicationPolicy - { - noDuplication = 0, - duplicate, - duplicateOnCopy - }; - CZString( int index ); - CZString( const char *cstr, DuplicationPolicy allocate ); - CZString( const CZString &other ); - ~CZString(); - CZString &operator =( const CZString &other ); - bool operator<( const CZString &other ) const; - bool operator==( const CZString &other ) const; - int index() const; - const char *c_str() const; - bool isStaticString() const; - private: - void swap( CZString &other ); - const char *cstr_; - int index_; - }; - - public: -# ifndef JSON_USE_CPPTL_SMALLMAP - typedef std::map ObjectValues; -# else - typedef CppTL::SmallMap ObjectValues; -# endif // ifndef JSON_USE_CPPTL_SMALLMAP -# endif // ifndef JSON_VALUE_USE_INTERNAL_MAP -#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION - - public: - /** \brief Create a default Value of the given type. - - This is a very useful constructor. - To create an empty array, pass arrayValue. - To create an empty object, pass objectValue. - Another Value can then be set to this one by assignment. - This is useful since clear() and resize() will not alter types. - - Examples: - \code - Json::Value null_value; // null - Json::Value arr_value(Json::arrayValue); // [] - Json::Value obj_value(Json::objectValue); // {} - \endcode - */ - Value( ValueType type = nullValue ); - Value( Int value ); - Value( UInt value ); - Value( double value ); - Value( const char *value ); - Value( const char *beginValue, const char *endValue ); - /** \brief Constructs a value from a static string. - - * Like other value string constructor but do not duplicate the string for - * internal storage. The given string must remain alive after the call to this - * constructor. - * Example of usage: - * \code - * Json::Value aValue( StaticString("some text") ); - * \endcode - */ - Value( const StaticString &value ); - Value( const std::string &value ); -# ifdef JSON_USE_CPPTL - Value( const CppTL::ConstString &value ); -# endif - Value( bool value ); - Value( const Value &other ); - ~Value(); - - Value &operator=( const Value &other ); - /// Swap values. - /// \note Currently, comments are intentionally not swapped, for - /// both logic and efficiency. - void swap( Value &other ); - - ValueType type() const; - - bool operator <( const Value &other ) const; - bool operator <=( const Value &other ) const; - bool operator >=( const Value &other ) const; - bool operator >( const Value &other ) const; - - bool operator ==( const Value &other ) const; - bool operator !=( const Value &other ) const; - - int compare( const Value &other ); - - const char *asCString() const; - std::string asString() const; -# ifdef JSON_USE_CPPTL - CppTL::ConstString asConstString() const; -# endif - Int asInt() const; - UInt asUInt() const; - double asDouble() const; - bool asBool() const; - - bool isNull() const; - bool isBool() const; - bool isInt() const; - bool isUInt() const; - bool isIntegral() const; - bool isDouble() const; - bool isNumeric() const; - bool isString() const; - bool isArray() const; - bool isObject() const; - - bool isConvertibleTo( ValueType other ) const; - - /// Number of values in array or object - UInt size() const; - - /// \brief Return true if empty array, empty object, or null; - /// otherwise, false. - bool empty() const; - - /// Return isNull() - bool operator!() const; - - /// Remove all object members and array elements. - /// \pre type() is arrayValue, objectValue, or nullValue - /// \post type() is unchanged - void clear(); - - /// Resize the array to size elements. - /// New elements are initialized to null. - /// May only be called on nullValue or arrayValue. - /// \pre type() is arrayValue or nullValue - /// \post type() is arrayValue - void resize( UInt size ); - - /// Access an array element (zero based index ). - /// If the array contains less than index element, then null value are inserted - /// in the array so that its size is index+1. - /// (You may need to say 'value[0u]' to get your compiler to distinguish - /// this from the operator[] which takes a string.) - Value &operator[]( UInt index ); - /// Access an array element (zero based index ) - /// (You may need to say 'value[0u]' to get your compiler to distinguish - /// this from the operator[] which takes a string.) - const Value &operator[]( UInt index ) const; - /// If the array contains at least index+1 elements, returns the element value, - /// otherwise returns defaultValue. - Value get( UInt index, - const Value &defaultValue ) const; - /// Return true if index < size(). - bool isValidIndex( UInt index ) const; - /// \brief Append value to array at the end. - /// - /// Equivalent to jsonvalue[jsonvalue.size()] = value; - Value &append( const Value &value ); - - /// Access an object value by name, create a null member if it does not exist. - Value &operator[]( const char *key ); - /// Access an object value by name, returns null if there is no member with that name. - const Value &operator[]( const char *key ) const; - /// Access an object value by name, create a null member if it does not exist. - Value &operator[]( const std::string &key ); - /// Access an object value by name, returns null if there is no member with that name. - const Value &operator[]( const std::string &key ) const; - /** \brief Access an object value by name, create a null member if it does not exist. - - * If the object as no entry for that name, then the member name used to store - * the new entry is not duplicated. - * Example of use: - * \code - * Json::Value object; - * static const StaticString code("code"); - * object[code] = 1234; - * \endcode - */ - Value &operator[]( const StaticString &key ); -# ifdef JSON_USE_CPPTL - /// Access an object value by name, create a null member if it does not exist. - Value &operator[]( const CppTL::ConstString &key ); - /// Access an object value by name, returns null if there is no member with that name. - const Value &operator[]( const CppTL::ConstString &key ) const; -# endif - /// Return the member named key if it exist, defaultValue otherwise. - Value get( const char *key, - const Value &defaultValue ) const; - /// Return the member named key if it exist, defaultValue otherwise. - Value get( const std::string &key, - const Value &defaultValue ) const; -# ifdef JSON_USE_CPPTL - /// Return the member named key if it exist, defaultValue otherwise. - Value get( const CppTL::ConstString &key, - const Value &defaultValue ) const; -# endif - /// \brief Remove and return the named member. - /// - /// Do nothing if it did not exist. - /// \return the removed Value, or null. - /// \pre type() is objectValue or nullValue - /// \post type() is unchanged - Value removeMember( const char* key ); - /// Same as removeMember(const char*) - Value removeMember( const std::string &key ); - - /// Return true if the object has a member named key. - bool isMember( const char *key ) const; - /// Return true if the object has a member named key. - bool isMember( const std::string &key ) const; -# ifdef JSON_USE_CPPTL - /// Return true if the object has a member named key. - bool isMember( const CppTL::ConstString &key ) const; -# endif - - /// \brief Return a list of the member names. - /// - /// If null, return an empty list. - /// \pre type() is objectValue or nullValue - /// \post if type() was nullValue, it remains nullValue - Members getMemberNames() const; - -//# ifdef JSON_USE_CPPTL -// EnumMemberNames enumMemberNames() const; -// EnumValues enumValues() const; -//# endif - - /// Comments must be //... or /* ... */ - void setComment( const char *comment, - CommentPlacement placement ); - /// Comments must be //... or /* ... */ - void setComment( const std::string &comment, - CommentPlacement placement ); - bool hasComment( CommentPlacement placement ) const; - /// Include delimiters and embedded newlines. - std::string getComment( CommentPlacement placement ) const; - - std::string toStyledString() const; - - const_iterator begin() const; - const_iterator end() const; - - iterator begin(); - iterator end(); - - private: - Value &resolveReference( const char *key, - bool isStatic ); - -# ifdef JSON_VALUE_USE_INTERNAL_MAP - inline bool isItemAvailable() const - { - return itemIsUsed_ == 0; - } - - inline void setItemUsed( bool isUsed = true ) - { - itemIsUsed_ = isUsed ? 1 : 0; - } - - inline bool isMemberNameStatic() const - { - return memberNameIsStatic_ == 0; - } - - inline void setMemberNameIsStatic( bool isStatic ) - { - memberNameIsStatic_ = isStatic ? 1 : 0; - } -# endif // # ifdef JSON_VALUE_USE_INTERNAL_MAP - - private: - struct CommentInfo - { - CommentInfo(); - ~CommentInfo(); - - void setComment( const char *text ); - - char *comment_; - }; - - //struct MemberNamesTransform - //{ - // typedef const char *result_type; - // const char *operator()( const CZString &name ) const - // { - // return name.c_str(); - // } - //}; - - union ValueHolder - { - Int int_; - UInt uint_; - double real_; - bool bool_; - char *string_; -# ifdef JSON_VALUE_USE_INTERNAL_MAP - ValueInternalArray *array_; - ValueInternalMap *map_; -#else - ObjectValues *map_; -# endif - } value_; - ValueType type_ : 8; - int allocated_ : 1; // Notes: if declared as bool, bitfield is useless. -# ifdef JSON_VALUE_USE_INTERNAL_MAP - unsigned int itemIsUsed_ : 1; // used by the ValueInternalMap container. - int memberNameIsStatic_ : 1; // used by the ValueInternalMap container. -# endif - CommentInfo *comments_; - }; - - - /** \brief Experimental and untested: represents an element of the "path" to access a node. - */ - class PathArgument - { - public: - friend class Path; - - PathArgument(); - PathArgument( UInt index ); - PathArgument( const char *key ); - PathArgument( const std::string &key ); - - private: - enum Kind - { - kindNone = 0, - kindIndex, - kindKey - }; - std::string key_; - UInt index_; - Kind kind_; - }; - - /** \brief Experimental and untested: represents a "path" to access a node. - * - * Syntax: - * - "." => root node - * - ".[n]" => elements at index 'n' of root node (an array value) - * - ".name" => member named 'name' of root node (an object value) - * - ".name1.name2.name3" - * - ".[0][1][2].name1[3]" - * - ".%" => member name is provided as parameter - * - ".[%]" => index is provied as parameter - */ - class Path - { - public: - Path( const std::string &path, - const PathArgument &a1 = PathArgument(), - const PathArgument &a2 = PathArgument(), - const PathArgument &a3 = PathArgument(), - const PathArgument &a4 = PathArgument(), - const PathArgument &a5 = PathArgument() ); - - const Value &resolve( const Value &root ) const; - Value resolve( const Value &root, - const Value &defaultValue ) const; - /// Creates the "path" to access the specified node and returns a reference on the node. - Value &make( Value &root ) const; - - private: - typedef std::vector InArgs; - typedef std::vector Args; - - void makePath( const std::string &path, - const InArgs &in ); - void addPathInArg( const std::string &path, - const InArgs &in, - InArgs::const_iterator &itInArg, - PathArgument::Kind kind ); - void invalidPath( const std::string &path, - int location ); - - Args args_; - }; - - /** \brief Experimental do not use: Allocator to customize member name and string value memory management done by Value. - * - * - makeMemberName() and releaseMemberName() are called to respectively duplicate and - * free an Json::objectValue member name. - * - duplicateStringValue() and releaseStringValue() are called similarly to - * duplicate and free a Json::stringValue value. - */ - class ValueAllocator - { - public: - enum { unknown = (unsigned)-1 }; - - virtual ~ValueAllocator(); - - virtual char *makeMemberName( const char *memberName ) = 0; - virtual void releaseMemberName( char *memberName ) = 0; - virtual char *duplicateStringValue( const char *value, - unsigned int length = unknown ) = 0; - virtual void releaseStringValue( char *value ) = 0; - }; - -#ifdef JSON_VALUE_USE_INTERNAL_MAP - /** \brief Allocator to customize Value internal map. - * Below is an example of a simple implementation (default implementation actually - * use memory pool for speed). - * \code - class DefaultValueMapAllocator : public ValueMapAllocator - { - public: // overridden from ValueMapAllocator - virtual ValueInternalMap *newMap() - { - return new ValueInternalMap(); - } - - virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) - { - return new ValueInternalMap( other ); - } - - virtual void destructMap( ValueInternalMap *map ) - { - delete map; - } - - virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) - { - return new ValueInternalLink[size]; - } - - virtual void releaseMapBuckets( ValueInternalLink *links ) - { - delete [] links; - } - - virtual ValueInternalLink *allocateMapLink() - { - return new ValueInternalLink(); - } - - virtual void releaseMapLink( ValueInternalLink *link ) - { - delete link; - } - }; - * \endcode - */ - class JSON_API ValueMapAllocator - { - public: - virtual ~ValueMapAllocator(); - virtual ValueInternalMap *newMap() = 0; - virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) = 0; - virtual void destructMap( ValueInternalMap *map ) = 0; - virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) = 0; - virtual void releaseMapBuckets( ValueInternalLink *links ) = 0; - virtual ValueInternalLink *allocateMapLink() = 0; - virtual void releaseMapLink( ValueInternalLink *link ) = 0; - }; - - /** \brief ValueInternalMap hash-map bucket chain link (for internal use only). - * \internal previous_ & next_ allows for bidirectional traversal. - */ - class JSON_API ValueInternalLink - { - public: - enum { itemPerLink = 6 }; // sizeof(ValueInternalLink) = 128 on 32 bits architecture. - enum InternalFlags { - flagAvailable = 0, - flagUsed = 1 - }; - - ValueInternalLink(); - - ~ValueInternalLink(); - - Value items_[itemPerLink]; - char *keys_[itemPerLink]; - ValueInternalLink *previous_; - ValueInternalLink *next_; - }; - - - /** \brief A linked page based hash-table implementation used internally by Value. - * \internal ValueInternalMap is a tradional bucket based hash-table, with a linked - * list in each bucket to handle collision. There is an addional twist in that - * each node of the collision linked list is a page containing a fixed amount of - * value. This provides a better compromise between memory usage and speed. - * - * Each bucket is made up of a chained list of ValueInternalLink. The last - * link of a given bucket can be found in the 'previous_' field of the following bucket. - * The last link of the last bucket is stored in tailLink_ as it has no following bucket. - * Only the last link of a bucket may contains 'available' item. The last link always - * contains at least one element unless is it the bucket one very first link. - */ - class JSON_API ValueInternalMap - { - friend class ValueIteratorBase; - friend class Value; - public: - typedef unsigned int HashKey; - typedef unsigned int BucketIndex; - -# ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION - struct IteratorState - { - IteratorState() - : map_(0) - , link_(0) - , itemIndex_(0) - , bucketIndex_(0) - { - } - ValueInternalMap *map_; - ValueInternalLink *link_; - BucketIndex itemIndex_; - BucketIndex bucketIndex_; - }; -# endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION - - ValueInternalMap(); - ValueInternalMap( const ValueInternalMap &other ); - ValueInternalMap &operator =( const ValueInternalMap &other ); - ~ValueInternalMap(); - - void swap( ValueInternalMap &other ); - - BucketIndex size() const; - - void clear(); - - bool reserveDelta( BucketIndex growth ); - - bool reserve( BucketIndex newItemCount ); - - const Value *find( const char *key ) const; - - Value *find( const char *key ); - - Value &resolveReference( const char *key, - bool isStatic ); - - void remove( const char *key ); - - void doActualRemove( ValueInternalLink *link, - BucketIndex index, - BucketIndex bucketIndex ); - - ValueInternalLink *&getLastLinkInBucket( BucketIndex bucketIndex ); - - Value &setNewItem( const char *key, - bool isStatic, - ValueInternalLink *link, - BucketIndex index ); - - Value &unsafeAdd( const char *key, - bool isStatic, - HashKey hashedKey ); - - HashKey hash( const char *key ) const; - - int compare( const ValueInternalMap &other ) const; - - private: - void makeBeginIterator( IteratorState &it ) const; - void makeEndIterator( IteratorState &it ) const; - static bool equals( const IteratorState &x, const IteratorState &other ); - static void increment( IteratorState &iterator ); - static void incrementBucket( IteratorState &iterator ); - static void decrement( IteratorState &iterator ); - static const char *key( const IteratorState &iterator ); - static const char *key( const IteratorState &iterator, bool &isStatic ); - static Value &value( const IteratorState &iterator ); - static int distance( const IteratorState &x, const IteratorState &y ); - - private: - ValueInternalLink *buckets_; - ValueInternalLink *tailLink_; - BucketIndex bucketsSize_; - BucketIndex itemCount_; - }; - - /** \brief A simplified deque implementation used internally by Value. - * \internal - * It is based on a list of fixed "page", each page contains a fixed number of items. - * Instead of using a linked-list, a array of pointer is used for fast item look-up. - * Look-up for an element is as follow: - * - compute page index: pageIndex = itemIndex / itemsPerPage - * - look-up item in page: pages_[pageIndex][itemIndex % itemsPerPage] - * - * Insertion is amortized constant time (only the array containing the index of pointers - * need to be reallocated when items are appended). - */ - class JSON_API ValueInternalArray - { - friend class Value; - friend class ValueIteratorBase; - public: - enum { itemsPerPage = 8 }; // should be a power of 2 for fast divide and modulo. - typedef Value::ArrayIndex ArrayIndex; - typedef unsigned int PageIndex; - -# ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION - struct IteratorState // Must be a POD - { - IteratorState() - : array_(0) - , currentPageIndex_(0) - , currentItemIndex_(0) - { - } - ValueInternalArray *array_; - Value **currentPageIndex_; - unsigned int currentItemIndex_; - }; -# endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION - - ValueInternalArray(); - ValueInternalArray( const ValueInternalArray &other ); - ValueInternalArray &operator =( const ValueInternalArray &other ); - ~ValueInternalArray(); - void swap( ValueInternalArray &other ); - - void clear(); - void resize( ArrayIndex newSize ); - - Value &resolveReference( ArrayIndex index ); - - Value *find( ArrayIndex index ) const; - - ArrayIndex size() const; - - int compare( const ValueInternalArray &other ) const; - - private: - static bool equals( const IteratorState &x, const IteratorState &other ); - static void increment( IteratorState &iterator ); - static void decrement( IteratorState &iterator ); - static Value &dereference( const IteratorState &iterator ); - static Value &unsafeDereference( const IteratorState &iterator ); - static int distance( const IteratorState &x, const IteratorState &y ); - static ArrayIndex indexOf( const IteratorState &iterator ); - void makeBeginIterator( IteratorState &it ) const; - void makeEndIterator( IteratorState &it ) const; - void makeIterator( IteratorState &it, ArrayIndex index ) const; - - void makeIndexValid( ArrayIndex index ); - - Value **pages_; - ArrayIndex size_; - PageIndex pageCount_; - }; - - /** \brief Experimental: do not use. Allocator to customize Value internal array. - * Below is an example of a simple implementation (actual implementation use - * memory pool). - \code -class DefaultValueArrayAllocator : public ValueArrayAllocator -{ -public: // overridden from ValueArrayAllocator - virtual ~DefaultValueArrayAllocator() - { - } - - virtual ValueInternalArray *newArray() - { - return new ValueInternalArray(); - } - - virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) - { - return new ValueInternalArray( other ); - } - - virtual void destruct( ValueInternalArray *array ) - { - delete array; - } - - virtual void reallocateArrayPageIndex( Value **&indexes, - ValueInternalArray::PageIndex &indexCount, - ValueInternalArray::PageIndex minNewIndexCount ) - { - ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1; - if ( minNewIndexCount > newIndexCount ) - newIndexCount = minNewIndexCount; - void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount ); - if ( !newIndexes ) - throw std::bad_alloc(); - indexCount = newIndexCount; - indexes = static_cast( newIndexes ); - } - virtual void releaseArrayPageIndex( Value **indexes, - ValueInternalArray::PageIndex indexCount ) - { - if ( indexes ) - free( indexes ); - } - - virtual Value *allocateArrayPage() - { - return static_cast( malloc( sizeof(Value) * ValueInternalArray::itemsPerPage ) ); - } - - virtual void releaseArrayPage( Value *value ) - { - if ( value ) - free( value ); - } -}; - \endcode - */ - class JSON_API ValueArrayAllocator - { - public: - virtual ~ValueArrayAllocator(); - virtual ValueInternalArray *newArray() = 0; - virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) = 0; - virtual void destructArray( ValueInternalArray *array ) = 0; - /** \brief Reallocate array page index. - * Reallocates an array of pointer on each page. - * \param indexes [input] pointer on the current index. May be \c NULL. - * [output] pointer on the new index of at least - * \a minNewIndexCount pages. - * \param indexCount [input] current number of pages in the index. - * [output] number of page the reallocated index can handle. - * \b MUST be >= \a minNewIndexCount. - * \param minNewIndexCount Minimum number of page the new index must be able to - * handle. - */ - virtual void reallocateArrayPageIndex( Value **&indexes, - ValueInternalArray::PageIndex &indexCount, - ValueInternalArray::PageIndex minNewIndexCount ) = 0; - virtual void releaseArrayPageIndex( Value **indexes, - ValueInternalArray::PageIndex indexCount ) = 0; - virtual Value *allocateArrayPage() = 0; - virtual void releaseArrayPage( Value *value ) = 0; - }; -#endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP - - - /** \brief base class for Value iterators. - * - */ - class ValueIteratorBase - { - public: - typedef unsigned int size_t; - typedef int difference_type; - typedef ValueIteratorBase SelfType; - - ValueIteratorBase(); -#ifndef JSON_VALUE_USE_INTERNAL_MAP - explicit ValueIteratorBase( const Value::ObjectValues::iterator ¤t ); -#else - ValueIteratorBase( const ValueInternalArray::IteratorState &state ); - ValueIteratorBase( const ValueInternalMap::IteratorState &state ); -#endif - - bool operator ==( const SelfType &other ) const - { - return isEqual( other ); - } - - bool operator !=( const SelfType &other ) const - { - return !isEqual( other ); - } - - difference_type operator -( const SelfType &other ) const - { - return computeDistance( other ); - } - - /// Return either the index or the member name of the referenced value as a Value. - Value key() const; - - /// Return the index of the referenced Value. -1 if it is not an arrayValue. - UInt index() const; - - /// Return the member name of the referenced Value. "" if it is not an objectValue. - const char *memberName() const; - - protected: - Value &deref() const; - - void increment(); - - void decrement(); - - difference_type computeDistance( const SelfType &other ) const; - - bool isEqual( const SelfType &other ) const; - - void copy( const SelfType &other ); - - private: -#ifndef JSON_VALUE_USE_INTERNAL_MAP - Value::ObjectValues::iterator current_; - // Indicates that iterator is for a null value. - bool isNull_; -#else - union - { - ValueInternalArray::IteratorState array_; - ValueInternalMap::IteratorState map_; - } iterator_; - bool isArray_; -#endif - }; - - /** \brief const iterator for object and array value. - * - */ - class ValueConstIterator : public ValueIteratorBase - { - friend class Value; - public: - typedef unsigned int size_t; - typedef int difference_type; - typedef const Value &reference; - typedef const Value *pointer; - typedef ValueConstIterator SelfType; - - ValueConstIterator(); - private: - /*! \internal Use by Value to create an iterator. - */ -#ifndef JSON_VALUE_USE_INTERNAL_MAP - explicit ValueConstIterator( const Value::ObjectValues::iterator ¤t ); -#else - ValueConstIterator( const ValueInternalArray::IteratorState &state ); - ValueConstIterator( const ValueInternalMap::IteratorState &state ); -#endif - public: - SelfType &operator =( const ValueIteratorBase &other ); - - SelfType operator++( int ) - { - SelfType temp( *this ); - ++*this; - return temp; - } - - SelfType operator--( int ) - { - SelfType temp( *this ); - --*this; - return temp; - } - - SelfType &operator--() - { - decrement(); - return *this; - } - - SelfType &operator++() - { - increment(); - return *this; - } - - reference operator *() const - { - return deref(); - } - }; - - - /** \brief Iterator for object and array value. - */ - class ValueIterator : public ValueIteratorBase - { - friend class Value; - public: - typedef unsigned int size_t; - typedef int difference_type; - typedef Value &reference; - typedef Value *pointer; - typedef ValueIterator SelfType; - - ValueIterator(); - ValueIterator( const ValueConstIterator &other ); - ValueIterator( const ValueIterator &other ); - private: - /*! \internal Use by Value to create an iterator. - */ -#ifndef JSON_VALUE_USE_INTERNAL_MAP - explicit ValueIterator( const Value::ObjectValues::iterator ¤t ); -#else - ValueIterator( const ValueInternalArray::IteratorState &state ); - ValueIterator( const ValueInternalMap::IteratorState &state ); -#endif - public: - - SelfType &operator =( const SelfType &other ); - - SelfType operator++( int ) - { - SelfType temp( *this ); - ++*this; - return temp; - } - - SelfType operator--( int ) - { - SelfType temp( *this ); - --*this; - return temp; - } - - SelfType &operator--() - { - decrement(); - return *this; - } - - SelfType &operator++() - { - increment(); - return *this; - } - - reference operator *() const - { - return deref(); - } - }; - - -} // namespace Json - - -#endif // CPPTL_JSON_H_INCLUDED diff --git a/util/json/writer.h b/util/json/writer.h deleted file mode 100644 index 5f4b83be..00000000 --- a/util/json/writer.h +++ /dev/null @@ -1,174 +0,0 @@ -#ifndef JSON_WRITER_H_INCLUDED -# define JSON_WRITER_H_INCLUDED - -# include "value.h" -# include -# include -# include - -namespace Json { - - class Value; - - /** \brief Abstract class for writers. - */ - class JSON_API Writer - { - public: - virtual ~Writer(); - - virtual std::string write( const Value &root ) = 0; - }; - - /** \brief Outputs a Value in JSON format without formatting (not human friendly). - * - * The JSON document is written in a single line. It is not intended for 'human' consumption, - * but may be usefull to support feature such as RPC where bandwith is limited. - * \sa Reader, Value - */ - class JSON_API FastWriter : public Writer - { - public: - FastWriter(); - virtual ~FastWriter(){} - - void enableYAMLCompatibility(); - - public: // overridden from Writer - virtual std::string write( const Value &root ); - - private: - void writeValue( const Value &value ); - - std::string document_; - bool yamlCompatiblityEnabled_; - }; - - /** \brief Writes a Value in JSON format in a human friendly way. - * - * The rules for line break and indent are as follow: - * - Object value: - * - if empty then print {} without indent and line break - * - if not empty the print '{', line break & indent, print one value per line - * and then unindent and line break and print '}'. - * - Array value: - * - if empty then print [] without indent and line break - * - if the array contains no object value, empty array or some other value types, - * and all the values fit on one lines, then print the array on a single line. - * - otherwise, it the values do not fit on one line, or the array contains - * object or non empty array, then print one value per line. - * - * If the Value have comments then they are outputed according to their #CommentPlacement. - * - * \sa Reader, Value, Value::setComment() - */ - class JSON_API StyledWriter: public Writer - { - public: - StyledWriter(); - virtual ~StyledWriter(){} - - public: // overridden from Writer - /** \brief Serialize a Value in JSON format. - * \param root Value to serialize. - * \return String containing the JSON document that represents the root value. - */ - virtual std::string write( const Value &root ); - - private: - void writeValue( const Value &value ); - void writeArrayValue( const Value &value ); - bool isMultineArray( const Value &value ); - void pushValue( const std::string &value ); - void writeIndent(); - void writeWithIndent( const std::string &value ); - void indent(); - void unindent(); - void writeCommentBeforeValue( const Value &root ); - void writeCommentAfterValueOnSameLine( const Value &root ); - bool hasCommentForValue( const Value &value ); - static std::string normalizeEOL( const std::string &text ); - - typedef std::vector ChildValues; - - ChildValues childValues_; - std::string document_; - std::string indentString_; - int rightMargin_; - int indentSize_; - bool addChildValues_; - }; - - /** \brief Writes a Value in JSON format in a human friendly way, - to a stream rather than to a string. - * - * The rules for line break and indent are as follow: - * - Object value: - * - if empty then print {} without indent and line break - * - if not empty the print '{', line break & indent, print one value per line - * and then unindent and line break and print '}'. - * - Array value: - * - if empty then print [] without indent and line break - * - if the array contains no object value, empty array or some other value types, - * and all the values fit on one lines, then print the array on a single line. - * - otherwise, it the values do not fit on one line, or the array contains - * object or non empty array, then print one value per line. - * - * If the Value have comments then they are outputed according to their #CommentPlacement. - * - * \param indentation Each level will be indented by this amount extra. - * \sa Reader, Value, Value::setComment() - */ - class JSON_API StyledStreamWriter - { - public: - StyledStreamWriter( std::string indentation="\t" ); - ~StyledStreamWriter(){} - - public: - /** \brief Serialize a Value in JSON format. - * \param out Stream to write to. (Can be ostringstream, e.g.) - * \param root Value to serialize. - * \note There is no point in deriving from Writer, since write() should not return a value. - */ - void write( std::ostream &out, const Value &root ); - - private: - void writeValue( const Value &value ); - void writeArrayValue( const Value &value ); - bool isMultineArray( const Value &value ); - void pushValue( const std::string &value ); - void writeIndent(); - void writeWithIndent( const std::string &value ); - void indent(); - void unindent(); - void writeCommentBeforeValue( const Value &root ); - void writeCommentAfterValueOnSameLine( const Value &root ); - bool hasCommentForValue( const Value &value ); - static std::string normalizeEOL( const std::string &text ); - - typedef std::vector ChildValues; - - ChildValues childValues_; - std::ostream* document_; - std::string indentString_; - int rightMargin_; - std::string indentation_; - bool addChildValues_; - }; - - std::string JSON_API valueToString( Int value ); - std::string JSON_API valueToString( UInt value ); - std::string JSON_API valueToString( double value ); - std::string JSON_API valueToString( bool value ); - std::string JSON_API valueToQuotedString( const char *value ); - - /// \brief Output using the StyledStreamWriter. - /// \see Json::operator>>() - std::ostream& operator<<( std::ostream&, const Value &root ); - -} // namespace Json - - - -#endif // JSON_WRITER_H_INCLUDED diff --git a/util/util.cpp b/util/procs.cpp similarity index 55% rename from util/util.cpp rename to util/procs.cpp index 773248f7..83e3047d 100644 --- a/util/util.cpp +++ b/util/procs.cpp @@ -1,7 +1,7 @@ -/// \file util.cpp -/// Contains generic functions for managing processes and configuration. +/// \file procs.cpp +/// Contains generic functions for managing processes. -#include "util.h" +#include "procs.h" #include #include #include @@ -16,37 +16,12 @@ #include #include #include -#include #include #include -#include std::map Util::Procs::plist; bool Util::Procs::handler_set = false; -/// Sets the current process' running user -void Util::setUser(std::string username){ - if (username != "root"){ - struct passwd * user_info = getpwnam(username.c_str()); - if (!user_info){ - #if DEBUG >= 1 - fprintf(stderr, "Error: could not setuid %s: could not get PID\n", username.c_str()); - #endif - return; - }else{ - if (setuid(user_info->pw_uid) != 0){ - #if DEBUG >= 1 - fprintf(stderr, "Error: could not setuid %s: not allowed\n", username.c_str()); - #endif - }else{ - #if DEBUG >= 3 - fprintf(stderr, "Changed user to %s\n", username.c_str()); - #endif - } - } - } -} - /// Used internally to capture child signals and update plist. void Util::Procs::childsig_handler(int signum){ if (signum != SIGCHLD){return;} @@ -263,108 +238,3 @@ std::string Util::Procs::getName(pid_t name){ } return ""; } - -/// Creates a new configuration manager. -Util::Config::Config(){ - listen_port = 4242; - daemon_mode = true; - interface = "0.0.0.0"; - configfile = "/etc/ddvtech.conf"; - username = "root"; - ignore_daemon = false; - ignore_interface = false; - ignore_port = false; - ignore_user = false; -} - -/// Parses commandline arguments. -/// Calls exit if an unknown option is encountered, printing a help message. -/// confsection must be either already set or never be set at all when this function is called. -/// In other words: do not change confsection after calling this function. -void Util::Config::parseArgs(int argc, char ** argv){ - int opt = 0; - static const char *optString = "ndvp:i:u:c:h?"; - static const struct option longOpts[] = { - {"help",0,0,'h'}, - {"port",1,0,'p'}, - {"interface",1,0,'i'}, - {"username",1,0,'u'}, - {"no-daemon",0,0,'n'}, - {"daemon",0,0,'d'}, - {"configfile",1,0,'c'}, - {"version",0,0,'v'} - }; - while ((opt = getopt_long(argc, argv, optString, longOpts, 0)) != -1){ - switch (opt){ - case 'p': listen_port = atoi(optarg); ignore_port = true; break; - case 'i': interface = optarg; ignore_interface = true; break; - case 'n': daemon_mode = false; ignore_daemon = true; break; - case 'd': daemon_mode = true; ignore_daemon = true; break; - case 'c': configfile = optarg; break; - case 'u': username = optarg; ignore_user = true; break; - case 'v': - printf("%s\n", TOSTRING(VERSION)); - exit(1); - break; - case 'h': - case '?': - std::string doingdaemon = "true"; - if (!daemon_mode){doingdaemon = "false";} - if (confsection == ""){ - printf("Options: -h[elp], -?, -v[ersion], -n[odaemon], -d[aemon], -p[ort] VAL, -i[nterface] VAL, -u[sername] VAL\n"); - printf("Defaults:\n interface: %s\n port: %i\n daemon mode: %s\n username: %s\n", interface.c_str(), listen_port, doingdaemon.c_str(), username.c_str()); - }else{ - printf("Options: -h[elp], -?, -v[ersion], -n[odaemon], -d[aemon], -p[ort] VAL, -i[nterface] VAL, -c[onfigfile] VAL, -u[sername] VAL\n"); - printf("Defaults:\n interface: %s\n port: %i\n daemon mode: %s\n configfile: %s\n username: %s\n", interface.c_str(), listen_port, doingdaemon.c_str(), configfile.c_str(), username.c_str()); - printf("Username root means no change to UID, no matter what the UID is.\n"); - printf("If the configfile exists, it is always loaded first. Commandline settings then overwrite the config file.\n"); - printf("\nThis process takes it directives from the %s section of the configfile.\n", confsection.c_str()); - } - printf("This is %s version %s\n", argv[0], TOSTRING(VERSION)); - exit(1); - break; - } - }//commandline options parser -} - -/// Parses the configuration file at configfile, if it exists. -/// Assumes confsection is set. -void Util::Config::parseFile(){ - std::ifstream conf(configfile.c_str(), std::ifstream::in); - std::string tmpstr; - bool acc_comm = false; - size_t foundeq; - if (conf.fail()){ - #if DEBUG >= 3 - fprintf(stderr, "Configuration file %s not found - using build-in defaults...\n", configfile.c_str()); - #endif - }else{ - while (conf.good()){ - getline(conf, tmpstr); - if (tmpstr[0] == '['){//new section? check if we care. - if (tmpstr == confsection){acc_comm = true;}else{acc_comm = false;} - }else{ - if (!acc_comm){break;}//skip all lines in this section if we do not care about it - foundeq = tmpstr.find('='); - if (foundeq != std::string::npos){ - if ((tmpstr.substr(0, foundeq) == "port") && !ignore_port){listen_port = atoi(tmpstr.substr(foundeq+1).c_str());} - if ((tmpstr.substr(0, foundeq) == "interface") && !ignore_interface){interface = tmpstr.substr(foundeq+1);} - if ((tmpstr.substr(0, foundeq) == "username") && !ignore_user){username = tmpstr.substr(foundeq+1);} - if ((tmpstr.substr(0, foundeq) == "daemon") && !ignore_daemon){daemon_mode = true;} - if ((tmpstr.substr(0, foundeq) == "nodaemon") && !ignore_daemon){daemon_mode = false;} - }//found equals sign - }//section contents - }//configfile line loop - }//configuration -} - -/// Will turn the current process into a daemon. -/// Works by calling daemon(1,0): -/// Does not change directory to root. -/// Does redirect output to /dev/null -void Util::Daemonize(){ - #if DEBUG >= 3 - fprintf(stderr, "Going into background mode...\n"); - #endif - daemon(1, 0); -} diff --git a/util/util.h b/util/procs.h similarity index 55% rename from util/util.h rename to util/procs.h index a92b67f0..3ec60902 100644 --- a/util/util.h +++ b/util/procs.h @@ -1,13 +1,10 @@ -/// \file util.h -/// Contains generic function headers for managing processes and configuration. +/// \file procs.h +/// Contains generic function headers for managing processes. #include #include #include -#define STRINGIFY(x) #x -#define TOSTRING(x) STRINGIFY(x) - /// Contains utility code, not directly related to streaming media namespace Util{ @@ -31,29 +28,4 @@ namespace Util{ static std::string getName(pid_t name); }; - /// Will set the active user to the named username. - void setUser(std::string user); - - /// Deals with parsing configuration from files or commandline options. - class Config{ - private: - bool ignore_daemon; - bool ignore_interface; - bool ignore_port; - bool ignore_user; - public: - std::string confsection; - std::string configfile; - bool daemon_mode; - std::string interface; - int listen_port; - std::string username; - Config(); - void parseArgs(int argc, char ** argv); - void parseFile(); - }; - - /// Will turn the current process into a daemon. - void Daemonize(); - }; diff --git a/util/server_setup.cpp b/util/server_setup.cpp index ae15ff87..47f015ac 100644 --- a/util/server_setup.cpp +++ b/util/server_setup.cpp @@ -22,7 +22,7 @@ #endif #include "socket.h" //Socket library -#include "util.h" //utilities for process spawning and config management +#include "config.h" //utilities for config management #include #include #include From d20c27d0203f400a357f09bf143d349c0213bc83 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 8 Apr 2012 22:56:51 +0200 Subject: [PATCH 180/788] First multithreaded version of the Buffer process. --- util/dtsc.cpp | 3 +- util/tinythread.cpp | 287 ++++++++++++++++++ util/tinythread.h | 696 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 985 insertions(+), 1 deletion(-) create mode 100644 util/tinythread.cpp create mode 100644 util/tinythread.h diff --git a/util/dtsc.cpp b/util/dtsc.cpp index a5eb05e3..fd0b6a7e 100644 --- a/util/dtsc.cpp +++ b/util/dtsc.cpp @@ -66,8 +66,9 @@ bool DTSC::Stream::parsePacket(std::string & buffer){ return true; } #if DEBUG >= 2 - std::cerr << "Error: Invalid DTMI data! I *will* get stuck!" << std::endl; + std::cerr << "Error: Invalid DTMI data: " << buffer.substr(0, 4) << std::endl; #endif + buffer.erase(0, 1); } return false; } diff --git a/util/tinythread.cpp b/util/tinythread.cpp new file mode 100644 index 00000000..eb2dce0e --- /dev/null +++ b/util/tinythread.cpp @@ -0,0 +1,287 @@ +/* +Copyright (c) 2010 Marcus Geelnard + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ + +#include +#include "tinythread.h" + +#if defined(_TTHREAD_POSIX_) + #include + #include +#elif defined(_TTHREAD_WIN32_) + #include +#endif + + +namespace tthread { + +//------------------------------------------------------------------------------ +// condition_variable +//------------------------------------------------------------------------------ +// NOTE 1: The Win32 implementation of the condition_variable class is based on +// the corresponding implementation in GLFW, which in turn is based on a +// description by Douglas C. Schmidt and Irfan Pyarali: +// http://www.cs.wustl.edu/~schmidt/win32-cv-1.html +// +// NOTE 2: Windows Vista actually has native support for condition variables +// (InitializeConditionVariable, WakeConditionVariable, etc), but we want to +// be portable with pre-Vista Windows versions, so TinyThread++ does not use +// Vista condition variables. +//------------------------------------------------------------------------------ + +#if defined(_TTHREAD_WIN32_) + #define _CONDITION_EVENT_ONE 0 + #define _CONDITION_EVENT_ALL 1 +#endif + +#if defined(_TTHREAD_WIN32_) +condition_variable::condition_variable() : mWaitersCount(0) +{ + mEvents[_CONDITION_EVENT_ONE] = CreateEvent(NULL, FALSE, FALSE, NULL); + mEvents[_CONDITION_EVENT_ALL] = CreateEvent(NULL, TRUE, FALSE, NULL); + InitializeCriticalSection(&mWaitersCountLock); +} +#endif + +#if defined(_TTHREAD_WIN32_) +condition_variable::~condition_variable() +{ + CloseHandle(mEvents[_CONDITION_EVENT_ONE]); + CloseHandle(mEvents[_CONDITION_EVENT_ALL]); + DeleteCriticalSection(&mWaitersCountLock); +} +#endif + +#if defined(_TTHREAD_WIN32_) +void condition_variable::_wait() +{ + // Wait for either event to become signaled due to notify_one() or + // notify_all() being called + int result = WaitForMultipleObjects(2, mEvents, FALSE, INFINITE); + + // Check if we are the last waiter + EnterCriticalSection(&mWaitersCountLock); + -- mWaitersCount; + bool lastWaiter = (result == (WAIT_OBJECT_0 + _CONDITION_EVENT_ALL)) && + (mWaitersCount == 0); + LeaveCriticalSection(&mWaitersCountLock); + + // If we are the last waiter to be notified to stop waiting, reset the event + if(lastWaiter) + ResetEvent(mEvents[_CONDITION_EVENT_ALL]); +} +#endif + +#if defined(_TTHREAD_WIN32_) +void condition_variable::notify_one() +{ + // Are there any waiters? + EnterCriticalSection(&mWaitersCountLock); + bool haveWaiters = (mWaitersCount > 0); + LeaveCriticalSection(&mWaitersCountLock); + + // If we have any waiting threads, send them a signal + if(haveWaiters) + SetEvent(mEvents[_CONDITION_EVENT_ONE]); +} +#endif + +#if defined(_TTHREAD_WIN32_) +void condition_variable::notify_all() +{ + // Are there any waiters? + EnterCriticalSection(&mWaitersCountLock); + bool haveWaiters = (mWaitersCount > 0); + LeaveCriticalSection(&mWaitersCountLock); + + // If we have any waiting threads, send them a signal + if(haveWaiters) + SetEvent(mEvents[_CONDITION_EVENT_ALL]); +} +#endif + + +//------------------------------------------------------------------------------ +// POSIX pthread_t to unique thread::id mapping logic. +// Note: Here we use a global thread safe std::map to convert instances of +// pthread_t to small thread identifier numbers (unique within one process). +// This method should be portable across different POSIX implementations. +//------------------------------------------------------------------------------ + +#if defined(_TTHREAD_POSIX_) +static thread::id _pthread_t_to_ID(const pthread_t &aHandle) +{ + static mutex idMapLock; + static std::map idMap; + static unsigned long int idCount(1); + + lock_guard guard(idMapLock); + if(idMap.find(aHandle) == idMap.end()) + idMap[aHandle] = idCount ++; + return thread::id(idMap[aHandle]); +} +#endif // _TTHREAD_POSIX_ + + +//------------------------------------------------------------------------------ +// thread +//------------------------------------------------------------------------------ + +/// Information to pass to the new thread (what to run). +struct _thread_start_info { + void (*mFunction)(void *); ///< Pointer to the function to be executed. + void * mArg; ///< Function argument for the thread function. + thread * mThread; ///< Pointer to the thread object. +}; + +// Thread wrapper function. +#if defined(_TTHREAD_WIN32_) +unsigned WINAPI thread::wrapper_function(void * aArg) +#elif defined(_TTHREAD_POSIX_) +void * thread::wrapper_function(void * aArg) +#endif +{ + // Get thread startup information + _thread_start_info * ti = (_thread_start_info *) aArg; + + try + { + // Call the actual client thread function + ti->mFunction(ti->mArg); + } + catch(...) + { + // Uncaught exceptions will terminate the application (default behavior + // according to the C++0x draft) + std::terminate(); + } + + // The thread is no longer executing + lock_guard guard(ti->mThread->mDataMutex); + ti->mThread->mNotAThread = true; + + // The thread is responsible for freeing the startup information + delete ti; + + return 0; +} + +thread::thread(void (*aFunction)(void *), void * aArg) +{ + // Serialize access to this thread structure + lock_guard guard(mDataMutex); + + // Fill out the thread startup information (passed to the thread wrapper, + // which will eventually free it) + _thread_start_info * ti = new _thread_start_info; + ti->mFunction = aFunction; + ti->mArg = aArg; + ti->mThread = this; + + // The thread is now alive + mNotAThread = false; + + // Create the thread +#if defined(_TTHREAD_WIN32_) + mHandle = (HANDLE) _beginthreadex(0, 0, wrapper_function, (void *) ti, 0, &mWin32ThreadID); +#elif defined(_TTHREAD_POSIX_) + if(pthread_create(&mHandle, NULL, wrapper_function, (void *) ti) != 0) + mHandle = 0; +#endif + + // Did we fail to create the thread? + if(!mHandle) + { + mNotAThread = true; + delete ti; + } +} + +thread::~thread() +{ + if(joinable()) + std::terminate(); +} + +void thread::join() +{ + if(joinable()) + { +#if defined(_TTHREAD_WIN32_) + WaitForSingleObject(mHandle, INFINITE); +#elif defined(_TTHREAD_POSIX_) + pthread_join(mHandle, NULL); +#endif + } +} + +bool thread::joinable() const +{ + mDataMutex.lock(); + bool result = !mNotAThread; + mDataMutex.unlock(); + return result; +} + +thread::id thread::get_id() const +{ + if(!joinable()) + return id(); +#if defined(_TTHREAD_WIN32_) + return id((unsigned long int) mWin32ThreadID); +#elif defined(_TTHREAD_POSIX_) + return _pthread_t_to_ID(mHandle); +#endif +} + +unsigned thread::hardware_concurrency() +{ +#if defined(_TTHREAD_WIN32_) + SYSTEM_INFO si; + GetSystemInfo(&si); + return (int) si.dwNumberOfProcessors; +#elif defined(_SC_NPROCESSORS_ONLN) + return (int) sysconf(_SC_NPROCESSORS_ONLN); +#elif defined(_SC_NPROC_ONLN) + return (int) sysconf(_SC_NPROC_ONLN); +#else + // The standard requires this function to return zero if the number of + // hardware cores could not be determined. + return 0; +#endif +} + + +//------------------------------------------------------------------------------ +// this_thread +//------------------------------------------------------------------------------ + +thread::id this_thread::get_id() +{ +#if defined(_TTHREAD_WIN32_) + return thread::id((unsigned long int) GetCurrentThreadId()); +#elif defined(_TTHREAD_POSIX_) + return _pthread_t_to_ID(pthread_self()); +#endif +} + +} diff --git a/util/tinythread.h b/util/tinythread.h new file mode 100644 index 00000000..723370cf --- /dev/null +++ b/util/tinythread.h @@ -0,0 +1,696 @@ +/* +Copyright (c) 2010 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++0x 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++0x 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_) + #include +#else + #include + #include + #include + #include +#endif + +// Generic includes +#include + +/// TinyThread++ version (major number). +#define TINYTHREAD_VERSION_MAJOR 1 +/// TinyThread++ version (minor number). +#define TINYTHREAD_VERSION_MINOR 0 +/// TinyThread++ version (full version). +#define TINYTHREAD_VERSION (TINYTHREAD_VERSION_MAJOR * 100 + TINYTHREAD_VERSION_MINOR) + +// Do we have a fully featured C++0x compiler? +#if (__cplusplus > 199711L) || (defined(__STDCXX_VERSION__) && (__STDCXX_VERSION__ >= 201001L)) + #define _TTHREAD_CPP0X_ +#endif + +// ...at least partial C++0x? +#if defined(_TTHREAD_CPP0X_) || defined(__GXX_EXPERIMENTAL_CXX0X__) || defined(__GXX_EXPERIMENTAL_CPP0X__) + #define _TTHREAD_CPP0X_PARTIAL_ +#endif + +// Macro for disabling assignments of objects. +#ifdef _TTHREAD_CPP0X_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++0x 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++0x +/// 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_CPP0X_) && !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++0x thread classes. For instance, the tthread::mutex class corresponds to +/// the std::mutex class. +namespace tthread { + +/// Mutex class. +/// This is a mutual exclusion object for synchronizing access to shared +/// memory areas for several threads. The mutex is non-recursive (i.e. a +/// program may deadlock if the thread that owns a mutex object calls lock() +/// on that object). +/// @see recursive_mutex +class mutex { + public: + /// Constructor. + mutex() +#if defined(_TTHREAD_WIN32_) + : mAlreadyLocked(false) +#endif + { +#if defined(_TTHREAD_WIN32_) + InitializeCriticalSection(&mHandle); +#else + pthread_mutex_init(&mHandle, NULL); +#endif + } + + /// Destructor. + ~mutex() + { +#if defined(_TTHREAD_WIN32_) + DeleteCriticalSection(&mHandle); +#else + pthread_mutex_destroy(&mHandle); +#endif + } + + /// Lock the mutex. + /// The method will block the calling thread until a lock on the mutex can + /// be obtained. The mutex remains locked until \c unlock() is called. + /// @see lock_guard + inline void lock() + { +#if defined(_TTHREAD_WIN32_) + EnterCriticalSection(&mHandle); + while(mAlreadyLocked) Sleep(1000); // Simulate deadlock... + mAlreadyLocked = true; +#else + pthread_mutex_lock(&mHandle); +#endif + } + + /// Try to lock the mutex. + /// The method will try to lock the mutex. If it fails, the function will + /// return immediately (non-blocking). + /// @return \c true if the lock was acquired, or \c false if the lock could + /// not be acquired. + inline bool try_lock() + { +#if defined(_TTHREAD_WIN32_) + bool ret = (TryEnterCriticalSection(&mHandle) ? true : false); + if(ret && mAlreadyLocked) + { + LeaveCriticalSection(&mHandle); + ret = false; + } + return ret; +#else + return (pthread_mutex_trylock(&mHandle) == 0) ? true : false; +#endif + } + + /// Unlock the mutex. + /// If any threads are waiting for the lock on this mutex, one of them will + /// be unblocked. + inline void unlock() + { +#if defined(_TTHREAD_WIN32_) + mAlreadyLocked = false; + LeaveCriticalSection(&mHandle); +#else + pthread_mutex_unlock(&mHandle); +#endif + } + + _TTHREAD_DISABLE_ASSIGNMENT(mutex) + + private: +#if defined(_TTHREAD_WIN32_) + CRITICAL_SECTION mHandle; + bool mAlreadyLocked; +#else + pthread_mutex_t mHandle; +#endif + + friend class condition_variable; +}; + +/// Recursive mutex class. +/// This is a mutual exclusion object for synchronizing access to shared +/// memory areas for several threads. The mutex is recursive (i.e. a thread +/// may lock the mutex several times, as long as it unlocks the mutex the same +/// number of times). +/// @see mutex +class recursive_mutex { + public: + /// Constructor. + recursive_mutex() + { +#if defined(_TTHREAD_WIN32_) + InitializeCriticalSection(&mHandle); +#else + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&mHandle, &attr); +#endif + } + + /// Destructor. + ~recursive_mutex() + { +#if defined(_TTHREAD_WIN32_) + DeleteCriticalSection(&mHandle); +#else + pthread_mutex_destroy(&mHandle); +#endif + } + + /// Lock the mutex. + /// The method will block the calling thread until a lock on the mutex can + /// be obtained. The mutex remains locked until \c unlock() is called. + /// @see lock_guard + inline void lock() + { +#if defined(_TTHREAD_WIN32_) + EnterCriticalSection(&mHandle); +#else + pthread_mutex_lock(&mHandle); +#endif + } + + /// Try to lock the mutex. + /// The method will try to lock the mutex. If it fails, the function will + /// return immediately (non-blocking). + /// @return \c true if the lock was acquired, or \c false if the lock could + /// not be acquired. + inline bool try_lock() + { +#if defined(_TTHREAD_WIN32_) + return TryEnterCriticalSection(&mHandle) ? true : false; +#else + return (pthread_mutex_trylock(&mHandle) == 0) ? true : false; +#endif + } + + /// Unlock the mutex. + /// If any threads are waiting for the lock on this mutex, one of them will + /// be unblocked. + inline void unlock() + { +#if defined(_TTHREAD_WIN32_) + LeaveCriticalSection(&mHandle); +#else + pthread_mutex_unlock(&mHandle); +#endif + } + + _TTHREAD_DISABLE_ASSIGNMENT(recursive_mutex) + + private: +#if defined(_TTHREAD_WIN32_) + CRITICAL_SECTION mHandle; +#else + pthread_mutex_t mHandle; +#endif + + friend class condition_variable; +}; + +/// Lock guard class. +/// The constructor locks the mutex, and the destructor unlocks the mutex, so +/// the mutex will automatically be unlocked when the lock guard goes out of +/// scope. Example usage: +/// @code +/// mutex m; +/// int counter; +/// +/// void increment() +/// { +/// lock_guard guard(m); +/// ++ counter; +/// } +/// @endcode + +template +class lock_guard { + public: + typedef T mutex_type; + + lock_guard() : mMutex(0) {} + + /// The constructor locks the mutex. + explicit lock_guard(mutex_type &aMutex) + { + mMutex = &aMutex; + mMutex->lock(); + } + + /// The destructor unlocks the mutex. + ~lock_guard() + { + if(mMutex) + mMutex->unlock(); + } + + private: + mutex_type * mMutex; +}; + +/// Condition variable class. +/// This is a signalling object for synchronizing the execution flow for +/// several threads. Example usage: +/// @code +/// // Shared data and associated mutex and condition variable objects +/// int count; +/// mutex m; +/// condition_variable cond; +/// +/// // Wait for the counter to reach a certain number +/// void wait_counter(int targetCount) +/// { +/// lock_guard guard(m); +/// while(count < targetCount) +/// cond.wait(m); +/// } +/// +/// // Increment the counter, and notify waiting threads +/// void increment() +/// { +/// lock_guard guard(m); +/// ++ count; +/// cond.notify_all(); +/// } +/// @endcode +class condition_variable { + public: + /// Constructor. +#if defined(_TTHREAD_WIN32_) + condition_variable(); +#else + condition_variable() + { + pthread_cond_init(&mHandle, NULL); + } +#endif + + /// Destructor. +#if defined(_TTHREAD_WIN32_) + ~condition_variable(); +#else + ~condition_variable() + { + pthread_cond_destroy(&mHandle); + } +#endif + + /// Wait for the condition. + /// The function will block the calling thread until the condition variable + /// is woken by \c notify_one(), \c notify_all() or a spurious wake up. + /// @param[in] aMutex A mutex that will be unlocked when the wait operation + /// starts, an locked again as soon as the wait operation is finished. + template + inline void wait(_mutexT &aMutex) + { +#if defined(_TTHREAD_WIN32_) + // Increment number of waiters + EnterCriticalSection(&mWaitersCountLock); + ++ mWaitersCount; + LeaveCriticalSection(&mWaitersCountLock); + + // Release the mutex while waiting for the condition (will decrease + // the number of waiters when done)... + aMutex.unlock(); + _wait(); + aMutex.lock(); +#else + pthread_cond_wait(&mHandle, &aMutex.mHandle); +#endif + } + + /// Notify one thread that is waiting for the condition. + /// If at least one thread is blocked waiting for this condition variable, + /// one will be woken up. + /// @note Only threads that started waiting prior to this call will be + /// woken up. +#if defined(_TTHREAD_WIN32_) + void notify_one(); +#else + inline void notify_one() + { + pthread_cond_signal(&mHandle); + } +#endif + + /// Notify all threads that are waiting for the condition. + /// All threads that are blocked waiting for this condition variable will + /// be woken up. + /// @note Only threads that started waiting prior to this call will be + /// woken up. +#if defined(_TTHREAD_WIN32_) + void notify_all(); +#else + inline void notify_all() + { + pthread_cond_broadcast(&mHandle); + } +#endif + + _TTHREAD_DISABLE_ASSIGNMENT(condition_variable) + + private: +#if defined(_TTHREAD_WIN32_) + void _wait(); + HANDLE mEvents[2]; ///< Signal and broadcast event HANDLEs. + unsigned int mWaitersCount; ///< Count of the number of waiters. + CRITICAL_SECTION mWaitersCountLock; ///< Serialize access to mWaitersCount. +#else + pthread_cond_t mHandle; +#endif +}; + + +/// Thread class. +class thread { + public: +#if defined(_TTHREAD_WIN32_) + typedef HANDLE native_handle_type; +#else + typedef pthread_t native_handle_type; +#endif + + class id; + + /// Default constructor. + /// Construct a \c thread object without an associated thread of execution + /// (i.e. non-joinable). + thread() : mHandle(0), mNotAThread(true) +#if defined(_TTHREAD_WIN32_) + , mWin32ThreadID(0) +#endif + {} + + /// Thread starting constructor. + /// Construct a \c thread object with a new thread of execution. + /// @param[in] aFunction A function pointer to a function of type: + /// void fun(void * arg) + /// @param[in] aArg Argument to the thread function. + /// @note This constructor is not fully compatible with the standard C++ + /// thread class. It is more similar to the pthread_create() (POSIX) and + /// CreateThread() (Windows) functions. + thread(void (*aFunction)(void *), void * aArg); + + /// Destructor. + /// @note If the thread is joinable upon destruction, \c std::terminate() + /// will be called, which terminates the process. It is always wise to do + /// \c join() before deleting a thread object. + ~thread(); + + /// Wait for the thread to finish (join execution flows). + void join(); + + /// Check if the thread is joinable. + /// A thread object is joinable if it has an associated thread of execution. + bool joinable() const; + + /// Return the thread ID of a thread object. + id get_id() const; + + /// Get the native handle for this thread. + /// @note Under Windows, this is a \c HANDLE, and under POSIX systems, this + /// is a \c pthread_t. + inline native_handle_type native_handle() + { + return mHandle; + } + + /// Determine the number of threads which can possibly execute concurrently. + /// This function is useful for determining the optimal number of threads to + /// use for a task. + /// @return The number of hardware thread contexts in the system. + /// @note If this value is not defined, the function returns zero (0). + static unsigned hardware_concurrency(); + + _TTHREAD_DISABLE_ASSIGNMENT(thread) + + private: + native_handle_type mHandle; ///< Thread handle. + mutable mutex mDataMutex; ///< Serializer for access to the thread private data. + bool mNotAThread; ///< True if this object is not a thread of execution. +#if defined(_TTHREAD_WIN32_) + unsigned int mWin32ThreadID; ///< Unique thread ID (filled out by _beginthreadex). +#endif + + // This is the internal thread wrapper function. +#if defined(_TTHREAD_WIN32_) + static unsigned WINAPI wrapper_function(void * aArg); +#else + static void * wrapper_function(void * aArg); +#endif +}; + +/// Thread ID. +/// The thread ID is a unique identifier for each thread. +/// @see thread::get_id() +class thread::id { + public: + /// Default constructor. + /// The default constructed ID is that of thread without a thread of + /// execution. + id() : mId(0) {}; + + id(unsigned long int aId) : mId(aId) {}; + + id(const id& aId) : mId(aId.mId) {}; + + inline id & operator=(const id &aId) + { + mId = aId.mId; + return *this; + } + + inline friend bool operator==(const id &aId1, const id &aId2) + { + return (aId1.mId == aId2.mId); + } + + inline friend bool operator!=(const id &aId1, const id &aId2) + { + return (aId1.mId != aId2.mId); + } + + inline friend bool operator<=(const id &aId1, const id &aId2) + { + return (aId1.mId <= aId2.mId); + } + + inline friend bool operator<(const id &aId1, const id &aId2) + { + return (aId1.mId < aId2.mId); + } + + inline friend bool operator>=(const id &aId1, const id &aId2) + { + return (aId1.mId >= aId2.mId); + } + + inline friend bool operator>(const id &aId1, const id &aId2) + { + return (aId1.mId > aId2.mId); + } + + inline friend std::ostream& operator <<(std::ostream &os, const id &obj) + { + os << obj.mId; + return os; + } + + private: + unsigned long int mId; +}; + + +// Related to - minimal to be able to support chrono. +typedef long long __intmax_t; + +/// Minimal implementation of the \c ratio class. This class provides enough +/// functionality to implement some basic \c chrono classes. +template <__intmax_t N, __intmax_t D = 1> class ratio { + public: + static double _as_double() { return double(N) / double(D); } +}; + +/// Minimal implementation of the \c chrono namespace. +/// The \c chrono namespace provides types for specifying time intervals. +namespace chrono { + /// Duration template class. This class provides enough functionality to + /// implement \c this_thread::sleep_for(). + template > class duration { + private: + _Rep rep_; + public: + typedef _Rep rep; + typedef _Period period; + + /// Construct a duration object with the given duration. + template + explicit duration(const _Rep2& r) : rep_(r) {}; + + /// Return the value of the duration object. + rep count() const + { + return rep_; + } + }; + + // Standard duration types. + typedef duration<__intmax_t, ratio<1, 1000000000> > nanoseconds; ///< Duration with the unit nanoseconds. + typedef duration<__intmax_t, ratio<1, 1000000> > microseconds; ///< Duration with the unit microseconds. + typedef duration<__intmax_t, ratio<1, 1000> > milliseconds; ///< Duration with the unit milliseconds. + typedef duration<__intmax_t> seconds; ///< Duration with the unit seconds. + typedef duration<__intmax_t, ratio<60> > minutes; ///< Duration with the unit minutes. + typedef duration<__intmax_t, ratio<3600> > hours; ///< Duration with the unit hours. +} + +/// The namespace \c this_thread provides methods for dealing with the +/// calling thread. +namespace this_thread { + /// Return the thread ID of the calling thread. + thread::id get_id(); + + /// Yield execution to another thread. + /// Offers the operating system the opportunity to schedule another thread + /// that is ready to run on the current processor. + inline void yield() + { +#if defined(_TTHREAD_WIN32_) + Sleep(0); +#else + sched_yield(); +#endif + } + + /// Blocks the calling thread for a period of time. + /// @param[in] aTime Minimum time to put the thread to sleep. + /// Example usage: + /// @code + /// // Sleep for 100 milliseconds + /// this_thread::sleep_for(chrono::milliseconds(100)); + /// @endcode + /// @note Supported duration types are: nanoseconds, microseconds, + /// milliseconds, seconds, minutes and hours. + template void sleep_for(const chrono::duration<_Rep, _Period>& aTime) + { +#if defined(_TTHREAD_WIN32_) + Sleep(int(double(aTime.count()) * (1000.0 * _Period::_as_double()) + 0.5)); +#else + usleep(int(double(aTime.count()) * (1000000.0 * _Period::_as_double()) + 0.5)); +#endif + } +} + +} + +// Define/macro cleanup +#undef _TTHREAD_DISABLE_ASSIGNMENT + +#endif // _TINYTHREAD_H_ From e7bcbc4f9fbb6d7c057c866c09555e9c08f58401 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 10 Apr 2012 16:26:30 +0200 Subject: [PATCH 181/788] Several major bugfixes in FLV handling, multithreaded buffer now actually works (still crashes when multiple users connect, though... needs further "tweaking"), updated toolset. --- util/amf.cpp | 53 +++++++++-------- util/amf.h | 2 +- util/dtsc.h | 6 +- util/flv_tag.cpp | 144 +++++++++++++++++++++++++++-------------------- 4 files changed, 115 insertions(+), 90 deletions(-) diff --git a/util/amf.cpp b/util/amf.cpp index b5f51fad..a11788fb 100644 --- a/util/amf.cpp +++ b/util/amf.cpp @@ -2,6 +2,7 @@ /// Holds all code for the AMF namespace. #include "amf.h" +#include #include //needed for stderr only /// Returns the std::string Indice for the current object, if available. @@ -105,43 +106,45 @@ AMF::Object::Object(std::string indice, AMF::obj0type setType){//object type ini /// Prints the contents of this object to std::cerr. /// If this object contains other objects, it will call itself recursively /// and print all nested content in a nice human-readable format. -void AMF::Object::Print(std::string indent){ - std::cerr << indent; +std::string AMF::Object::Print(std::string indent){ + std::stringstream st; + st << indent; // print my type switch (myType){ - case AMF::AMF0_NUMBER: std::cerr << "Number"; break; - case AMF::AMF0_BOOL: std::cerr << "Bool"; break; + case AMF::AMF0_NUMBER: st << "Number"; break; + case AMF::AMF0_BOOL: st << "Bool"; break; case AMF::AMF0_STRING://short string - case AMF::AMF0_LONGSTRING: std::cerr << "String"; break; - case AMF::AMF0_OBJECT: std::cerr << "Object"; break; - case AMF::AMF0_MOVIECLIP: std::cerr << "MovieClip"; break; - case AMF::AMF0_NULL: std::cerr << "Null"; break; - case AMF::AMF0_UNDEFINED: std::cerr << "Undefined"; break; - case AMF::AMF0_REFERENCE: std::cerr << "Reference"; break; - case AMF::AMF0_ECMA_ARRAY: std::cerr << "ECMA Array"; break; - case AMF::AMF0_OBJ_END: std::cerr << "Object end"; break; - case AMF::AMF0_STRICT_ARRAY: std::cerr << "Strict Array"; break; - case AMF::AMF0_DATE: std::cerr << "Date"; break; - case AMF::AMF0_UNSUPPORTED: std::cerr << "Unsupported"; break; - case AMF::AMF0_RECORDSET: std::cerr << "Recordset"; break; - case AMF::AMF0_XMLDOC: std::cerr << "XML Document"; break; - case AMF::AMF0_TYPED_OBJ: std::cerr << "Typed Object"; break; - case AMF::AMF0_UPGRADE: std::cerr << "Upgrade to AMF3"; break; - case AMF::AMF0_DDV_CONTAINER: std::cerr << "DDVTech Container"; break; + case AMF::AMF0_LONGSTRING: st << "String"; break; + case AMF::AMF0_OBJECT: st << "Object"; break; + case AMF::AMF0_MOVIECLIP: st << "MovieClip"; break; + case AMF::AMF0_NULL: st << "Null"; break; + case AMF::AMF0_UNDEFINED: st << "Undefined"; break; + case AMF::AMF0_REFERENCE: st << "Reference"; break; + case AMF::AMF0_ECMA_ARRAY: st << "ECMA Array"; break; + case AMF::AMF0_OBJ_END: st << "Object end"; break; + case AMF::AMF0_STRICT_ARRAY: st << "Strict Array"; break; + case AMF::AMF0_DATE: st << "Date"; break; + case AMF::AMF0_UNSUPPORTED: st << "Unsupported"; break; + case AMF::AMF0_RECORDSET: st << "Recordset"; break; + case AMF::AMF0_XMLDOC: st << "XML Document"; break; + case AMF::AMF0_TYPED_OBJ: st << "Typed Object"; break; + case AMF::AMF0_UPGRADE: st << "Upgrade to AMF3"; break; + case AMF::AMF0_DDV_CONTAINER: st << "DDVTech Container"; break; } // print my string indice, if available - std::cerr << " " << myIndice << " "; + st << " " << myIndice << " "; // print my numeric or string contents switch (myType){ - case AMF::AMF0_NUMBER: case AMF::AMF0_BOOL: case AMF::AMF0_REFERENCE: case AMF::AMF0_DATE: std::cerr << numval; break; - case AMF::AMF0_STRING: case AMF::AMF0_LONGSTRING: case AMF::AMF0_XMLDOC: case AMF::AMF0_TYPED_OBJ: std::cerr << strval; break; + case AMF::AMF0_NUMBER: case AMF::AMF0_BOOL: case AMF::AMF0_REFERENCE: case AMF::AMF0_DATE: st << numval; break; + case AMF::AMF0_STRING: case AMF::AMF0_LONGSTRING: case AMF::AMF0_XMLDOC: case AMF::AMF0_TYPED_OBJ: st << strval; break; default: break;//we don't care about the rest, and don't want a compiler warning... } - std::cerr << std::endl; + st << std::endl; // if I hold other objects, print those too, recursively. if (contents.size() > 0){ - for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){it->Print(indent+" ");} + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){st << it->Print(indent+" ");} } + return st.str(); };//print /// Packs the AMF object to a std::string for transfer over the network. diff --git a/util/amf.h b/util/amf.h index 61f95a8a..836cfdb2 100644 --- a/util/amf.h +++ b/util/amf.h @@ -70,7 +70,7 @@ namespace AMF{ 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); - void Print(std::string indent = ""); + std::string Print(std::string indent = ""); std::string Pack(); protected: std::string myIndice; ///< Holds this objects indice, if any. diff --git a/util/dtsc.h b/util/dtsc.h index f721e6c4..138e3ab4 100644 --- a/util/dtsc.h +++ b/util/dtsc.h @@ -106,9 +106,9 @@ namespace DTSC{ class Ring { public: Ring(unsigned int v); - unsigned int b; ///< Holds current number of buffer. May and is intended to change unexpectedly! - bool waiting; ///< If true, this Ring is currently waiting for a buffer fill. - bool starved; ///< If true, this Ring can no longer receive valid data. + volatile unsigned int b; ///< Holds current number of buffer. May and is intended to change unexpectedly! + volatile bool waiting; ///< If true, this Ring is currently waiting for a buffer fill. + volatile bool starved; ///< If true, this Ring can no longer receive valid data. }; /// Holds temporary data for a DTSC stream and provides functions to utilize it. diff --git a/util/flv_tag.cpp b/util/flv_tag.cpp index cf75e549..bba87754 100644 --- a/util/flv_tag.cpp +++ b/util/flv_tag.cpp @@ -9,6 +9,7 @@ #include //for Tag::FileLoader #include //malloc #include //memcpy +#include /// Holds the last FLV header parsed. /// Defaults to a audio+video header on FLV version 0x01 if no header received yet. @@ -100,80 +101,84 @@ bool FLV::Tag::isInitData(){ /// audio, video or metadata, what encoding is used, and the details /// of the encoding itself. std::string FLV::Tag::tagType(){ - std::string R = ""; + std::stringstream R; + R << len << " bytes of "; switch (data[0]){ case 0x09: switch (data[11] & 0x0F){ - case 1: R += "JPEG"; break; - case 2: R += "H263"; break; - case 3: R += "ScreenVideo1"; break; - case 4: R += "VP6"; break; - case 5: R += "VP6Alpha"; break; - case 6: R += "ScreenVideo2"; break; - case 7: R += "H264"; break; - default: R += "unknown"; break; + case 1: R << "JPEG"; break; + case 2: R << "H263"; break; + case 3: R << "ScreenVideo1"; break; + case 4: R << "VP6"; break; + case 5: R << "VP6Alpha"; break; + case 6: R << "ScreenVideo2"; break; + case 7: R << "H264"; break; + default: R << "unknown"; break; } - R += " video "; + R << " video "; switch (data[11] & 0xF0){ - case 0x10: R += "keyframe"; break; - case 0x20: R += "iframe"; break; - case 0x30: R += "disposableiframe"; break; - case 0x40: R += "generatedkeyframe"; break; - case 0x50: R += "videoinfo"; break; + case 0x10: R << "keyframe"; break; + case 0x20: R << "iframe"; break; + case 0x30: R << "disposableiframe"; break; + case 0x40: R << "generatedkeyframe"; break; + case 0x50: R << "videoinfo"; break; } if ((data[11] & 0x0F) == 7){ switch (data[12]){ - case 0: R += " header"; break; - case 1: R += " NALU"; break; - case 2: R += " endofsequence"; break; + case 0: R << " header"; break; + case 1: R << " NALU"; break; + case 2: R << " endofsequence"; break; } } break; case 0x08: switch (data[11] & 0xF0){ - case 0x00: R += "linear PCM PE"; break; - case 0x10: R += "ADPCM"; break; - case 0x20: R += "MP3"; break; - case 0x30: R += "linear PCM LE"; break; - case 0x40: R += "Nelly16kHz"; break; - case 0x50: R += "Nelly8kHz"; break; - case 0x60: R += "Nelly"; break; - case 0x70: R += "G711A-law"; break; - case 0x80: R += "G711mu-law"; break; - case 0x90: R += "reserved"; break; - case 0xA0: R += "AAC"; break; - case 0xB0: R += "Speex"; break; - case 0xE0: R += "MP38kHz"; break; - case 0xF0: R += "DeviceSpecific"; break; - default: R += "unknown"; break; + case 0x00: R << "linear PCM PE"; break; + case 0x10: R << "ADPCM"; break; + case 0x20: R << "MP3"; break; + case 0x30: R << "linear PCM LE"; break; + case 0x40: R << "Nelly16kHz"; break; + case 0x50: R << "Nelly8kHz"; break; + case 0x60: R << "Nelly"; break; + case 0x70: R << "G711A-law"; break; + case 0x80: R << "G711mu-law"; break; + case 0x90: R << "reserved"; break; + case 0xA0: R << "AAC"; break; + case 0xB0: R << "Speex"; break; + case 0xE0: R << "MP38kHz"; break; + case 0xF0: R << "DeviceSpecific"; break; + default: R << "unknown"; break; } switch (data[11] & 0x0C){ - case 0x0: R += " 5.5kHz"; break; - case 0x4: R += " 11kHz"; break; - case 0x8: R += " 22kHz"; break; - case 0xC: R += " 44kHz"; break; + case 0x0: R << " 5.5kHz"; break; + case 0x4: R << " 11kHz"; break; + case 0x8: R << " 22kHz"; break; + case 0xC: R << " 44kHz"; break; } switch (data[11] & 0x02){ - case 0: R += " 8bit"; break; - case 2: R += " 16bit"; break; + case 0: R << " 8bit"; break; + case 2: R << " 16bit"; break; } switch (data[11] & 0x01){ - case 0: R += " mono"; break; - case 1: R += " stereo"; break; + case 0: R << " mono"; break; + case 1: R << " stereo"; break; } - R += " audio"; + R << " audio"; if ((data[12] == 0) && ((data[11] & 0xF0) == 0xA0)){ - R += " initdata"; + R << " initdata"; } break; - case 0x12: - R += "(meta)data"; + case 0x12:{ + R << "(meta)data: "; + AMF::Object metadata = AMF::parse((unsigned char*)data+11, len-15); + R << metadata.Print(); break; + } default: - R += "unknown"; + R << "unknown"; break; } - return R; + return R.str(); }//FLV::Tag::tagtype /// Returns the 32-bit timestamp of this tag. @@ -297,7 +302,7 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ if (S.getPacket().getContentP("interframe")){data[11] += 0x20;} if (S.getPacket().getContentP("disposableframe")){data[11] += 0x30;} break; - case DTSC::AUDIO: + case DTSC::AUDIO:{ if ((unsigned int)len == S.lastData().length() + 16){ memcpy(data+12, S.lastData().c_str(), S.lastData().length()); }else{ @@ -307,12 +312,18 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ data[11] = 0; if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "AAC"){data[11] += 0xA0;} if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "MP3"){data[11] += 0x20;} - if (S.metadata.getContentP("audio")->getContentP("rate")->NumValue() == 11025){data[11] += 0x04;} - if (S.metadata.getContentP("audio")->getContentP("rate")->NumValue() == 22050){data[11] += 0x08;} - if (S.metadata.getContentP("audio")->getContentP("rate")->NumValue() == 44100){data[11] += 0x0C;} + unsigned int datarate = S.metadata.getContentP("audio")->getContentP("rate")->NumValue(); + if (datarate >= 44100){ + data[11] += 0x0C; + }else if(datarate >= 22050){ + data[11] += 0x08; + }else if(datarate >= 11025){ + data[11] += 0x04; + } if (S.metadata.getContentP("audio")->getContentP("size")->NumValue() == 16){data[11] += 0x02;} if (S.metadata.getContentP("audio")->getContentP("channels")->NumValue() > 1){data[11] += 0x01;} break; + } case DTSC::META: memcpy(data+11, S.lastData().c_str(), S.lastData().length()); break; @@ -329,6 +340,9 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ data[1] = ((len-15) >> 16) & 0xFF; data[2] = ((len-15) >> 8) & 0xFF; data[3] = (len-15) & 0xFF; + data[8] = 0; + data[9] = 0; + data[10] = 0; tagTime(S.getPacket().getContentP("time")->NumValue()); return true; } @@ -336,7 +350,7 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ /// Helper function that properly sets the tag length from the internal len variable. void FLV::Tag::setLen(){ int len4 = len - 4; - int i = len-1; + int i = len; data[--i] = (len4) & 0xFF; len4 >>= 8; data[--i] = (len4) & 0xFF; @@ -375,6 +389,9 @@ bool FLV::Tag::DTSCVideoInit(DTSC::Stream & S){ data[1] = ((len-15) >> 16) & 0xFF; data[2] = ((len-15) >> 8) & 0xFF; data[3] = (len-15) & 0xFF; + data[8] = 0; + data[9] = 0; + data[10] = 0; tagTime(0); return true; } @@ -402,23 +419,25 @@ bool FLV::Tag::DTSCAudioInit(DTSC::Stream & S){ data[11] = 0; if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "AAC"){data[11] += 0xA0;} if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "MP3"){data[11] += 0x20;} - if (S.metadata.getContentP("audio")->getContentP("rate")->NumValue() == 11000){data[11] += 0x04;} - if (S.metadata.getContentP("audio")->getContentP("rate")->NumValue() == 22000){data[11] += 0x08;} - if (S.metadata.getContentP("audio")->getContentP("rate")->NumValue() == 44000){data[11] += 0x0C;} + unsigned int datarate = S.metadata.getContentP("audio")->getContentP("rate")->NumValue(); + if (datarate >= 44100){ + data[11] += 0x0C; + }else if(datarate >= 22050){ + data[11] += 0x08; + }else if(datarate >= 11025){ + data[11] += 0x04; + } if (S.metadata.getContentP("audio")->getContentP("size")->NumValue() == 16){data[11] += 0x02;} if (S.metadata.getContentP("audio")->getContentP("channels")->NumValue() > 1){data[11] += 0x01;} } setLen(); - switch (S.lastType()){ - case DTSC::VIDEO: data[0] = 0x09; break; - case DTSC::AUDIO: data[0] = 0x08; break; - case DTSC::META: data[0] = 0x12; break; - default: break; - } data[0] = 0x08; data[1] = ((len-15) >> 16) & 0xFF; data[2] = ((len-15) >> 8) & 0xFF; data[3] = (len-15) & 0xFF; + data[8] = 0; + data[9] = 0; + data[10] = 0; tagTime(0); return true; } @@ -501,6 +520,9 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){ data[1] = ((len-15) >> 16) & 0xFF; data[2] = ((len-15) >> 8) & 0xFF; data[3] = (len-15) & 0xFF; + data[8] = 0; + data[9] = 0; + data[10] = 0; tagTime(0); return true; } From 106adcffb820aadec74bc9a289decfaa2604f7aa Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 15 Apr 2012 01:54:39 +0200 Subject: [PATCH 182/788] RTMP Connector upgrade to DTSC - push mode doesn't convert to DTSC yet (it will tomorrow) and it is still untested - but should work. --- util/socket.cpp | 5 +++-- util/socket.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/util/socket.cpp b/util/socket.cpp index ed669e75..e751b097 100644 --- a/util/socket.cpp +++ b/util/socket.cpp @@ -222,9 +222,10 @@ std::string Socket::Connection::getStats(std::string C){ } /// Updates the downbuffer and upbuffer internal variables. -void Socket::Connection::spool(){ - iread(downbuffer); +/// Returns true if new data was received, false otherwise. +bool Socket::Connection::spool(){ iwrite(upbuffer); + return iread(downbuffer); } /// Returns a reference to the download buffer. diff --git a/util/socket.h b/util/socket.h index 988b96c2..3a538286 100644 --- a/util/socket.h +++ b/util/socket.h @@ -48,7 +48,7 @@ namespace Socket{ bool swrite(std::string & buffer); ///< Write call that is compatible with std::string. bool iread(std::string & buffer); ///< Incremental write call that is compatible with std::string. bool iwrite(std::string & buffer); ///< Write call that is compatible with std::string. - void spool(); ///< Updates the downbuffer and upbuffer internal variables. + bool spool(); ///< Updates the downbuffer and upbuffer internal variables. std::string & Received(); ///< Returns a reference to the download buffer. void Send(std::string data); ///< Appends data to the upbuffer. void close(); ///< Close connection. From 82946565150304c3ae632a856e722f88a0048e27 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 17 Apr 2012 11:11:01 +0200 Subject: [PATCH 183/788] Minor fixes in config.cpp --- util/config.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/config.cpp b/util/config.cpp index 6d56ab7f..b426766c 100644 --- a/util/config.cpp +++ b/util/config.cpp @@ -3,7 +3,6 @@ #include "config.h" #include -#include #include #ifdef __FreeBSD__ @@ -14,6 +13,7 @@ #include #include #include +#include #include #include #include From f615427c9521675f7075ed31da551b0e916986f1 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 17 Apr 2012 21:33:43 +0200 Subject: [PATCH 184/788] A few bugfixes and improvements in the FLV2DTSC code. Also, moved conversion logic to flv_tag.cpp/h instead of keeping it inside the conversion tool itself. --- util/dtsc.cpp | 14 +++- util/dtsc.h | 1 + util/flv_tag.cpp | 175 +++++++++++++++++++++++++++++++++++++++++++++++ util/flv_tag.h | 5 ++ 4 files changed, 193 insertions(+), 2 deletions(-) diff --git a/util/dtsc.cpp b/util/dtsc.cpp index fd0b6a7e..26601a5d 100644 --- a/util/dtsc.cpp +++ b/util/dtsc.cpp @@ -198,6 +198,13 @@ const char * DTSC::DTMI::Str(){return strval.c_str();}; /// If this object is not a container type, this function will always return 0. int DTSC::DTMI::hasContent(){return contents.size();}; +/// Returns true if this DTSC::DTMI value is non-default. +/// Non-default means it is either not a root element or has content. +bool DTSC::DTMI::isEmpty(){ + if (myType != DTMI_ROOT){return false;} + return (hasContent() == 0); +}; + /// Adds an DTSC::DTMI to this object. Works for all types, but only makes sense for container types. /// This function resets DTMI::packed to an empty string, forcing a repack on the next call to DTMI::Pack. /// If the indice name already exists, replaces the indice. @@ -213,9 +220,12 @@ void DTSC::DTMI::addContent(DTSC::DTMI c){ }; /// Returns a pointer to the object held at indice i. -/// Returns AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice. +/// Returns null pointer if no object is held at this indice. /// \param i The indice of the object in this container. -DTSC::DTMI* DTSC::DTMI::getContentP(int i){return &contents.at(i);}; +DTSC::DTMI* DTSC::DTMI::getContentP(int i){ + if (contents.size() <= (unsigned int)i){return 0;} + return &contents.at(i); +}; /// Returns a copy of the object held at indice i. /// Returns a AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice. diff --git a/util/dtsc.h b/util/dtsc.h index 138e3ab4..428a445e 100644 --- a/util/dtsc.h +++ b/util/dtsc.h @@ -62,6 +62,7 @@ namespace DTSC{ std::string & StrValue(); const char * Str(); int hasContent(); + bool isEmpty(); void addContent(DTMI c); DTMI* getContentP(int i); DTMI getContent(int i); diff --git a/util/flv_tag.cpp b/util/flv_tag.cpp index bba87754..919772f0 100644 --- a/util/flv_tag.cpp +++ b/util/flv_tag.cpp @@ -771,3 +771,178 @@ bool FLV::Tag::FileLoader(FILE * f){ fcntl(fileno(f), F_SETFL, preflags); return false; }//FLV_GetPacket + +DTSC::DTMI FLV::Tag::toDTSC(DTSC::DTMI & metadata){ + DTSC::DTMI pack_out; // Storage for outgoing DTMI data. + + if (data[0] == 0x12){ + AMF::Object meta_in = AMF::parse((unsigned char*)data+11, len-15); + if (meta_in.getContentP(0) && (meta_in.getContentP(0)->StrValue() == "onMetaData") && meta_in.getContentP(1)){ + AMF::Object * tmp = meta_in.getContentP(1); + if (tmp->getContentP("videocodecid")){ + switch ((unsigned int)tmp->getContentP("videocodecid")->NumValue()){ + case 2: Meta_Put(metadata, "video", "codec", "H263"); break; + case 4: Meta_Put(metadata, "video", "codec", "VP6"); break; + case 7: Meta_Put(metadata, "video", "codec", "H264"); break; + default: Meta_Put(metadata, "video", "codec", "?"); break; + } + } + if (tmp->getContentP("audiocodecid")){ + switch ((unsigned int)tmp->getContentP("audiocodecid")->NumValue()){ + case 2: Meta_Put(metadata, "audio", "codec", "MP3"); break; + case 10: Meta_Put(metadata, "audio", "codec", "AAC"); break; + default: Meta_Put(metadata, "audio", "codec", "?"); break; + } + } + if (tmp->getContentP("width")){ + Meta_Put(metadata, "video", "width", tmp->getContentP("width")->NumValue()); + } + if (tmp->getContentP("height")){ + Meta_Put(metadata, "video", "height", tmp->getContentP("height")->NumValue()); + } + if (tmp->getContentP("framerate")){ + Meta_Put(metadata, "video", "fpks", tmp->getContentP("framerate")->NumValue()*1000); + } + if (tmp->getContentP("videodatarate")){ + Meta_Put(metadata, "video", "bps", (tmp->getContentP("videodatarate")->NumValue()*1024)/8); + } + if (tmp->getContentP("audiodatarate")){ + Meta_Put(metadata, "audio", "bps", (tmp->getContentP("audiodatarate")->NumValue()*1024)/8); + } + if (tmp->getContentP("audiosamplerate")){ + Meta_Put(metadata, "audio", "rate", tmp->getContentP("audiosamplerate")->NumValue()); + } + if (tmp->getContentP("audiosamplesize")){ + Meta_Put(metadata, "audio", "size", tmp->getContentP("audiosamplesize")->NumValue()); + } + if (tmp->getContentP("stereo")){ + if (tmp->getContentP("stereo")->NumValue() == 1){ + Meta_Put(metadata, "audio", "channels", 2); + }else{ + Meta_Put(metadata, "audio", "channels", 1); + } + } + } + return pack_out;//empty + } + if (data[0] == 0x08){ + char audiodata = data[11]; + if (needsInitData() && isInitData()){ + if ((audiodata & 0xF0) == 0xA0){ + Meta_Put(metadata, "audio", "init", std::string((char*)data+13, (size_t)len-17)); + }else{ + Meta_Put(metadata, "audio", "init", std::string((char*)data+12, (size_t)len-16)); + } + return pack_out;//skip rest of parsing, get next tag. + } + pack_out = DTSC::DTMI("audio", DTSC::DTMI_ROOT); + pack_out.addContent(DTSC::DTMI("datatype", "audio")); + pack_out.addContent(DTSC::DTMI("time", tagTime())); + if (!Meta_Has(metadata, "audio", "codec")){ + switch (audiodata & 0xF0){ + case 0x20: Meta_Put(metadata, "audio", "codec", "MP3"); break; + case 0xA0: Meta_Put(metadata, "audio", "codec", "AAC"); break; + default: Meta_Put(metadata, "audio", "codec", "?"); break; + } + } + if (!Meta_Has(metadata, "audio", "rate")){ + switch (audiodata & 0x0C){ + case 0x0: Meta_Put(metadata, "audio", "rate", 5512); break; + case 0x4: Meta_Put(metadata, "audio", "rate", 11025); break; + case 0x8: Meta_Put(metadata, "audio", "rate", 22050); break; + case 0xC: Meta_Put(metadata, "audio", "rate", 44100); break; + } + } + if (!Meta_Has(metadata, "audio", "size")){ + switch (audiodata & 0x02){ + case 0x0: Meta_Put(metadata, "audio", "size", 8); break; + case 0x2: Meta_Put(metadata, "audio", "size", 16); break; + } + } + if (!Meta_Has(metadata, "audio", "channels")){ + switch (audiodata & 0x01){ + case 0x0: Meta_Put(metadata, "audio", "channels", 1); break; + case 0x1: Meta_Put(metadata, "audio", "channels", 2); break; + } + } + if ((audiodata & 0xF0) == 0xA0){ + pack_out.addContent(DTSC::DTMI("data", std::string((char*)data+13, (size_t)len-17))); + }else{ + pack_out.addContent(DTSC::DTMI("data", std::string((char*)data+12, (size_t)len-16))); + } + return pack_out; + } + if (data[0] == 0x09){ + char videodata = data[11]; + if (needsInitData() && isInitData()){ + if ((videodata & 0x0F) == 7){ + Meta_Put(metadata, "video", "init", std::string((char*)data+16, (size_t)len-20)); + }else{ + Meta_Put(metadata, "video", "init", std::string((char*)data+12, (size_t)len-16)); + } + return pack_out;//skip rest of parsing, get next tag. + } + if (!Meta_Has(metadata, "video", "codec")){ + switch (videodata & 0x0F){ + case 2: Meta_Put(metadata, "video", "codec", "H263"); break; + case 4: Meta_Put(metadata, "video", "codec", "VP6"); break; + case 7: Meta_Put(metadata, "video", "codec", "H264"); break; + default: Meta_Put(metadata, "video", "codec", "?"); break; + } + } + pack_out = DTSC::DTMI("video", DTSC::DTMI_ROOT); + pack_out.addContent(DTSC::DTMI("datatype", "video")); + switch (videodata & 0xF0){ + case 0x10: pack_out.addContent(DTSC::DTMI("keyframe", 1)); break; + case 0x20: pack_out.addContent(DTSC::DTMI("interframe", 1)); break; + case 0x30: pack_out.addContent(DTSC::DTMI("disposableframe", 1)); break; + case 0x40: pack_out.addContent(DTSC::DTMI("keyframe", 1)); break; + case 0x50: return DTSC::DTMI(); break;//the video info byte we just throw away - useless to us... + } + pack_out.addContent(DTSC::DTMI("time", tagTime())); + if ((videodata & 0x0F) == 7){ + switch (data[12]){ + case 1: pack_out.addContent(DTSC::DTMI("nalu", 1)); break; + case 2: pack_out.addContent(DTSC::DTMI("nalu_end", 1)); break; + } + int offset = (data[13] << 16) + (data[14] << 8) + data[15]; + offset = (offset << 8) >> 8; + pack_out.addContent(DTSC::DTMI("offset", offset)); + pack_out.addContent(DTSC::DTMI("data", std::string((char*)data+16, (size_t)len-20))); + }else{ + pack_out.addContent(DTSC::DTMI("data", std::string((char*)data+12, (size_t)len-16))); + } + return pack_out; + } + return pack_out;//should never get here +}//FLV::Tag::toDTSC + +/// Inserts std::string type metadata into the passed DTMI object. +/// \arg meta The DTMI object to put the metadata into. +/// \arg cat Metadata category to insert into. +/// \arg elem Element name to put into the category. +/// \arg val Value to put into the element name. +void FLV::Tag::Meta_Put(DTSC::DTMI & meta, std::string cat, std::string elem, std::string val){ + if (meta.getContentP(cat) == 0){meta.addContent(DTSC::DTMI(cat));} + meta.getContentP(cat)->addContent(DTSC::DTMI(elem, val)); +} + +/// Inserts uint64_t type metadata into the passed DTMI object. +/// \arg meta The DTMI object to put the metadata into. +/// \arg cat Metadata category to insert into. +/// \arg elem Element name to put into the category. +/// \arg val Value to put into the element name. +void FLV::Tag::Meta_Put(DTSC::DTMI & meta, std::string cat, std::string elem, uint64_t val){ + if (meta.getContentP(cat) == 0){meta.addContent(DTSC::DTMI(cat));} + meta.getContentP(cat)->addContent(DTSC::DTMI(elem, val)); +} + +/// Returns true if the named category and elementname are available in the metadata. +/// \arg meta The DTMI object to check. +/// \arg cat Metadata category to check. +/// \arg elem Element name to check. +bool FLV::Tag::Meta_Has(DTSC::DTMI & meta, std::string cat, std::string elem){ + if (meta.getContentP(cat) == 0){return false;} + if (meta.getContentP(cat)->getContentP(elem) == 0){return false;} + return true; +} diff --git a/util/flv_tag.h b/util/flv_tag.h index 6f1f7da7..6848995c 100644 --- a/util/flv_tag.h +++ b/util/flv_tag.h @@ -43,6 +43,7 @@ namespace FLV { bool DTSCVideoInit(DTSC::Stream & S); bool DTSCAudioInit(DTSC::Stream & S); bool DTSCMetaInit(DTSC::Stream & S); + DTSC::DTMI toDTSC(DTSC::DTMI & metadata); bool MemLoader(char * D, unsigned int S, unsigned int & P); bool SockLoader(int sock); bool SockLoader(Socket::Connection sock); @@ -56,6 +57,10 @@ namespace FLV { bool MemReadUntil(char * buffer, unsigned int count, unsigned int & sofar, char * D, unsigned int S, unsigned int & P); bool SockReadUntil(char * buffer, unsigned int count, unsigned int & sofar, Socket::Connection & sock); bool FileReadUntil(char * buffer, unsigned int count, unsigned int & sofar, FILE * f); + //DTSC writer helpers + void Meta_Put(DTSC::DTMI & meta, std::string cat, std::string elem, std::string val); + void Meta_Put(DTSC::DTMI & meta, std::string cat, std::string elem, uint64_t val); + bool Meta_Has(DTSC::DTMI & meta, std::string cat, std::string elem); };//Tag };//FLV namespace From 2701a0be46d4371ecff4d6a4dafeac86778eecf1 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 27 Apr 2012 21:07:26 +0200 Subject: [PATCH 185/788] Stable multi-user buffer and re-enabled push support. Renamed DDV_->Mist. Closes #25 --- util/config.cpp | 61 ++++++------------------------------------- util/config.h | 24 ++++++----------- util/server_setup.cpp | 8 ------ util/socket.cpp | 41 +++++++++++++++++++++++++++++ util/socket.h | 6 +++++ 5 files changed, 63 insertions(+), 77 deletions(-) diff --git a/util/config.cpp b/util/config.cpp index b426766c..b7533fc3 100644 --- a/util/config.cpp +++ b/util/config.cpp @@ -26,12 +26,7 @@ Util::Config::Config(){ listen_port = 4242; daemon_mode = true; interface = "0.0.0.0"; - configfile = "/etc/ddvtech.conf"; username = "root"; - ignore_daemon = false; - ignore_interface = false; - ignore_port = false; - ignore_user = false; } /// Parses commandline arguments. @@ -48,17 +43,15 @@ void Util::Config::parseArgs(int argc, char ** argv){ {"username",1,0,'u'}, {"no-daemon",0,0,'n'}, {"daemon",0,0,'d'}, - {"configfile",1,0,'c'}, {"version",0,0,'v'} }; while ((opt = getopt_long(argc, argv, optString, longOpts, 0)) != -1){ switch (opt){ - case 'p': listen_port = atoi(optarg); ignore_port = true; break; - case 'i': interface = optarg; ignore_interface = true; break; - case 'n': daemon_mode = false; ignore_daemon = true; break; - case 'd': daemon_mode = true; ignore_daemon = true; break; - case 'c': configfile = optarg; break; - case 'u': username = optarg; ignore_user = true; break; + case 'p': listen_port = atoi(optarg); break; + case 'i': interface = optarg; break; + case 'n': daemon_mode = false; break; + case 'd': daemon_mode = true; break; + case 'u': username = optarg; break; case 'v': printf("%s\n", TOSTRING(VERSION)); exit(1); @@ -67,16 +60,9 @@ void Util::Config::parseArgs(int argc, char ** argv){ case '?': std::string doingdaemon = "true"; if (!daemon_mode){doingdaemon = "false";} - if (confsection == ""){ - printf("Options: -h[elp], -?, -v[ersion], -n[odaemon], -d[aemon], -p[ort] VAL, -i[nterface] VAL, -u[sername] VAL\n"); - printf("Defaults:\n interface: %s\n port: %i\n daemon mode: %s\n username: %s\n", interface.c_str(), listen_port, doingdaemon.c_str(), username.c_str()); - }else{ - printf("Options: -h[elp], -?, -v[ersion], -n[odaemon], -d[aemon], -p[ort] VAL, -i[nterface] VAL, -c[onfigfile] VAL, -u[sername] VAL\n"); - printf("Defaults:\n interface: %s\n port: %i\n daemon mode: %s\n configfile: %s\n username: %s\n", interface.c_str(), listen_port, doingdaemon.c_str(), configfile.c_str(), username.c_str()); - printf("Username root means no change to UID, no matter what the UID is.\n"); - printf("If the configfile exists, it is always loaded first. Commandline settings then overwrite the config file.\n"); - printf("\nThis process takes it directives from the %s section of the configfile.\n", confsection.c_str()); - } + printf("Options: -h[elp], -?, -v[ersion], -n[odaemon], -d[aemon], -p[ort] VAL, -i[nterface] VAL, -u[sername] VAL\n"); + printf("Defaults:\n interface: %s\n port: %i\n daemon mode: %s\n username: %s\n", interface.c_str(), listen_port, doingdaemon.c_str(), username.c_str()); + printf("Username root means no change to UID, no matter what the UID is.\n"); printf("This is %s version %s\n", argv[0], TOSTRING(VERSION)); exit(1); break; @@ -84,37 +70,6 @@ void Util::Config::parseArgs(int argc, char ** argv){ }//commandline options parser } -/// Parses the configuration file at configfile, if it exists. -/// Assumes confsection is set. -void Util::Config::parseFile(){ - std::ifstream conf(configfile.c_str(), std::ifstream::in); - std::string tmpstr; - bool acc_comm = false; - size_t foundeq; - if (conf.fail()){ - #if DEBUG >= 3 - fprintf(stderr, "Configuration file %s not found - using build-in defaults...\n", configfile.c_str()); - #endif - }else{ - while (conf.good()){ - getline(conf, tmpstr); - if (tmpstr[0] == '['){//new section? check if we care. - if (tmpstr == confsection){acc_comm = true;}else{acc_comm = false;} - }else{ - if (!acc_comm){break;}//skip all lines in this section if we do not care about it - foundeq = tmpstr.find('='); - if (foundeq != std::string::npos){ - if ((tmpstr.substr(0, foundeq) == "port") && !ignore_port){listen_port = atoi(tmpstr.substr(foundeq+1).c_str());} - if ((tmpstr.substr(0, foundeq) == "interface") && !ignore_interface){interface = tmpstr.substr(foundeq+1);} - if ((tmpstr.substr(0, foundeq) == "username") && !ignore_user){username = tmpstr.substr(foundeq+1);} - if ((tmpstr.substr(0, foundeq) == "daemon") && !ignore_daemon){daemon_mode = true;} - if ((tmpstr.substr(0, foundeq) == "nodaemon") && !ignore_daemon){daemon_mode = false;} - }//found equals sign - }//section contents - }//configfile line loop - }//configuration -} - /// Sets the current process' running user void Util::setUser(std::string username){ if (username != "root"){ diff --git a/util/config.h b/util/config.h index 5a0b0da9..acf3fb17 100644 --- a/util/config.h +++ b/util/config.h @@ -9,23 +9,15 @@ /// Contains utility code, not directly related to streaming media namespace Util{ - /// Deals with parsing configuration from files or commandline options. + /// Deals with parsing configuration from commandline options. class Config{ - private: - bool ignore_daemon; - bool ignore_interface; - bool ignore_port; - bool ignore_user; - public: - std::string confsection; - std::string configfile; - bool daemon_mode; - std::string interface; - int listen_port; - std::string username; - Config(); - void parseArgs(int argc, char ** argv); - void parseFile(); + public: + bool daemon_mode; + std::string interface; + int listen_port; + std::string username; + Config(); + void parseArgs(int argc, char ** argv); }; /// Will set the active user to the named username. diff --git a/util/server_setup.cpp b/util/server_setup.cpp index 47f015ac..8f8cfad4 100644 --- a/util/server_setup.cpp +++ b/util/server_setup.cpp @@ -15,12 +15,6 @@ #endif -#ifndef CONFIGSECT - /// Configuration file section for this server. - #define CONFIGSECT None - #error "No configuration file section was set!" -#endif - #include "socket.h" //Socket library #include "config.h" //utilities for config management #include @@ -83,10 +77,8 @@ int main(int argc, char ** argv){ //set and parse configuration Util::Config C; - C.confsection = TOSTRING(CONFIGSECT); C.listen_port = DEFAULT_PORT; C.parseArgs(argc, argv); - C.parseFile(); //setup a new server socket, for the correct interface and port server_socket = Socket::Server(C.listen_port, C.interface); diff --git a/util/socket.cpp b/util/socket.cpp index e751b097..603075ee 100644 --- a/util/socket.cpp +++ b/util/socket.cpp @@ -3,6 +3,7 @@ /// Written by Jaron Vietor in 2010 for DDVTech #include "socket.h" +#include #include #include #include @@ -647,3 +648,43 @@ bool Socket::Server::connected(){ /// Returns internal socket number. int Socket::Server::getSocket(){return sock;} + +/// Connect to a stream on the system. +/// Filters the streamname, removing invalid characters and +/// converting all letters to lowercase. +/// If a '?' character is found, everything following that character is deleted. +Socket::Connection Socket::getStream(std::string streamname){ + //strip anything that isn't numbers, digits or underscores + for (std::string::iterator i=streamname.end()-1; i>=streamname.begin(); --i){ + if (*i == '?'){streamname.erase(i, streamname.end()); break;} + if (!isalpha(*i) && !isdigit(*i) && *i != '_'){ + streamname.erase(i); + }else{ + *i=tolower(*i); + } + } + return Socket::Connection("/tmp/mist/stream_"+streamname); +} + +/// Create a stream on the system. +/// Filters the streamname, removing invalid characters and +/// converting all letters to lowercase. +/// If a '?' character is found, everything following that character is deleted. +/// If the /tmp/ddvtech directory doesn't exist yet, this will create it. +Socket::Server Socket::makeStream(std::string streamname){ + //strip anything that isn't numbers, digits or underscores + for (std::string::iterator i=streamname.end()-1; i>=streamname.begin(); --i){ + if (*i == '?'){streamname.erase(i, streamname.end()); break;} + if (!isalpha(*i) && !isdigit(*i) && *i != '_'){ + streamname.erase(i); + }else{ + *i=tolower(*i); + } + } + std::string loc = "/tmp/mist/stream_"+streamname; + //attempt to create the /tmp/mist directory if it doesn't exist already. + //ignore errors - we catch all problems in the Socket::Server creation already + mkdir("/tmp/mist", S_IRWXU | S_IRWXG | S_IRWXO); + //create and return the Socket::Server + return Socket::Server(loc); +} diff --git a/util/socket.h b/util/socket.h index 3a538286..503ec4e9 100644 --- a/util/socket.h +++ b/util/socket.h @@ -77,4 +77,10 @@ namespace Socket{ int getSocket(); ///< Returns internal socket number. }; + /// Connect to a stream on the system. + Connection getStream(std::string streamname); + + /// Create a stream on the system. + Server makeStream(std::string streamname); + }; From 8b15fd52c452bbd8857ef5c9a89cb5a301ed6c51 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sat, 28 Apr 2012 00:51:21 +0200 Subject: [PATCH 186/788] Fixed Controller compiling, upped socket lib verbosity, fixed JSON::Value null -> string convert, fixed Makefile install command. --- util/json.cpp | 6 +++++- util/socket.cpp | 12 ++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/util/json.cpp b/util/json.cpp index 7542ed37..d4b3efc4 100644 --- a/util/json.cpp +++ b/util/json.cpp @@ -254,7 +254,11 @@ JSON::Value::operator std::string(){ if (myType == STRING){ return strVal; }else{ - return toString(); + if (myType == EMPTY){ + return ""; + }else{ + return toString(); + } } } diff --git a/util/socket.cpp b/util/socket.cpp index 603075ee..4ddcfb81 100644 --- a/util/socket.cpp +++ b/util/socket.cpp @@ -454,7 +454,7 @@ Socket::Server::Server(int port, std::string hostname, bool nonblock){ sock = socket(AF_INET6, SOCK_STREAM, 0); if (sock < 0){ #if DEBUG >= 1 - fprintf(stderr, "Could not create socket! Error: %s\n", strerror(errno)); + fprintf(stderr, "Could not create socket %s:%i! Error: %s\n", hostname.c_str(), port, strerror(errno)); #endif return; } @@ -468,7 +468,7 @@ Socket::Server::Server(int port, std::string hostname, bool nonblock){ struct sockaddr_in6 addr; addr.sin6_family = AF_INET6; addr.sin6_port = htons(port);//set port - if (hostname == "0.0.0.0"){ + if (hostname == "0.0.0.0" || hostname.length() == 0){ addr.sin6_addr = in6addr_any; }else{ inet_pton(AF_INET6, hostname.c_str(), &addr.sin6_addr);//set interface, 0.0.0.0 (default) is all @@ -487,14 +487,14 @@ Socket::Server::Server(int port, std::string hostname, bool nonblock){ } }else{ #if DEBUG >= 1 - fprintf(stderr, "Binding failed, retrying as IPv4... (%s)\n", strerror(errno)); + fprintf(stderr, "Binding %s:%i failed, retrying as IPv4... (%s)\n", hostname.c_str(), port, strerror(errno)); #endif close(); } sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0){ #if DEBUG >= 1 - fprintf(stderr, "Could not create socket! Error: %s\n", strerror(errno)); + fprintf(stderr, "Could not create socket %s:%i! Error: %s\n", hostname.c_str(), port, strerror(errno)); #endif return; } @@ -508,7 +508,7 @@ Socket::Server::Server(int port, std::string hostname, bool nonblock){ struct sockaddr_in addr4; addr4.sin_family = AF_INET; addr4.sin_port = htons(port);//set port - if (hostname == "0.0.0.0"){ + if (hostname == "0.0.0.0" || hostname.length() == 0){ addr4.sin_addr.s_addr = INADDR_ANY; }else{ inet_pton(AF_INET, hostname.c_str(), &addr4.sin_addr);//set interface, 0.0.0.0 (default) is all @@ -527,7 +527,7 @@ Socket::Server::Server(int port, std::string hostname, bool nonblock){ } }else{ #if DEBUG >= 1 - fprintf(stderr, "IPv4 binding also failed, giving up. (%s)\n", strerror(errno)); + fprintf(stderr, "IPv4 binding %s:%i also failed, giving up. (%s)\n", hostname.c_str(), port, strerror(errno)); #endif close(); return; From 505ed38a501ac121d5692fed0be5d8fa64f9d3f4 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sat, 28 Apr 2012 14:58:49 +0200 Subject: [PATCH 187/788] Include basic embed code generation in the HTTP connector. Closes #23, #24 and #25. --- util/json.cpp | 11 +++++++++++ util/json.h | 1 + 2 files changed, 12 insertions(+) diff --git a/util/json.cpp b/util/json.cpp index d4b3efc4..e8e12641 100644 --- a/util/json.cpp +++ b/util/json.cpp @@ -2,6 +2,7 @@ #include "json.h" #include +#include int JSON::Value::c2hex(int c){ if (c >= '0' && c <= '9') return c - '0'; @@ -423,3 +424,13 @@ JSON::Value JSON::fromString(std::string json){ std::istringstream is(json); return JSON::Value(is); } + +/// Converts a file to a JSON::Value. +JSON::Value JSON::fromFile(std::string filename){ + std::string Result; + std::ifstream File; + File.open(filename.c_str()); + while (File.good()){Result += File.get();} + File.close( ); + return fromString(Result); +} diff --git a/util/json.h b/util/json.h index 8de01e11..047178cd 100644 --- a/util/json.h +++ b/util/json.h @@ -69,5 +69,6 @@ namespace JSON{ }; Value fromString(std::string json); + Value fromFile(std::string filename); }; From 663ffb74ebd01b78e16a5ba5d18331ee258c66ca Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 1 May 2012 14:28:31 +0200 Subject: [PATCH 188/788] JSON reading fixed. I don't know why this wasn't noticed before. :-) --- util/json.cpp | 28 ++++++++++++++++++---------- util/json.h | 1 + 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/util/json.cpp b/util/json.cpp index e8e12641..37ce3279 100644 --- a/util/json.cpp +++ b/util/json.cpp @@ -68,6 +68,16 @@ std::string JSON::Value::string_escape(std::string val){ return out; } +/// Skips an std::istream forward until any of the following characters is seen: ,]} +void JSON::Value::skipToEnd(std::istream & fromstream){ + while (fromstream.good()){ + char peek = fromstream.peek(); + if (peek == ','){return;} + if (peek == ']'){return;} + if (peek == '}'){return;} + peek = fromstream.get(); + } +} /// Sets this JSON::Value to null; JSON::Value::Value(){ @@ -78,14 +88,12 @@ JSON::Value::Value(){ JSON::Value::Value(std::istream & fromstream){ null(); bool reading_object = false; - bool reading_obj_name = false; bool reading_array = false; while (fromstream.good()){ int c = fromstream.peek(); switch (c){ case '{': reading_object = true; - reading_obj_name = true; c = fromstream.get(); myType = OBJECT; break; @@ -98,7 +106,7 @@ JSON::Value::Value(std::istream & fromstream){ case '\'': case '"': c = fromstream.get(); - if (!reading_object || !reading_obj_name){ + if (!reading_object){ myType = STRING; strVal = read_string(c, fromstream); return; @@ -125,9 +133,7 @@ JSON::Value::Value(std::istream & fromstream){ case ',': if (!reading_object && !reading_array) return; c = fromstream.get(); - if (reading_object){ - reading_obj_name = true; - }else{ + if (reading_array){ append(JSON::Value(fromstream)); } break; @@ -141,18 +147,21 @@ JSON::Value::Value(std::istream & fromstream){ break; case 't': case 'T': + skipToEnd(fromstream); myType = BOOL; intVal = 1; return; break; case 'f': case 'F': + skipToEnd(fromstream); myType = BOOL; intVal = 0; return; break; case 'n': case 'N': + skipToEnd(fromstream); myType = EMPTY; return; break; @@ -427,10 +436,9 @@ JSON::Value JSON::fromString(std::string json){ /// Converts a file to a JSON::Value. JSON::Value JSON::fromFile(std::string filename){ - std::string Result; std::ifstream File; File.open(filename.c_str()); - while (File.good()){Result += File.get();} - File.close( ); - return fromString(Result); + JSON::Value ret(File); + File.close(); + return ret; } diff --git a/util/json.h b/util/json.h index 047178cd..f8d47aec 100644 --- a/util/json.h +++ b/util/json.h @@ -27,6 +27,7 @@ namespace JSON{ std::string read_string(int separator, std::istream & fromstream); std::string string_escape(std::string val); int c2hex(int c); + static void skipToEnd(std::istream & fromstream); public: //constructors Value(); From 332f3990679998ad84847fcc5cd76d101fc06c62 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 1 May 2012 14:42:52 +0200 Subject: [PATCH 189/788] Fixed some issues with empty values in JSON::Value::toString() --- util/json.cpp | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/util/json.cpp b/util/json.cpp index 37ce3279..2799c9f1 100644 --- a/util/json.cpp +++ b/util/json.cpp @@ -321,9 +321,11 @@ std::string JSON::Value::toString(){ } case ARRAY: { std::string tmp = "["; - for (ArrIter it = ArrBegin(); it != ArrEnd(); it++){ - tmp += it->toString(); - if (it + 1 != ArrEnd()){tmp += ",";} + if (arrVal.size() > 0){ + for (ArrIter it = ArrBegin(); it != ArrEnd(); it++){ + tmp += it->toString(); + if (it + 1 != ArrEnd()){tmp += ",";} + } } tmp += "]"; return tmp; @@ -331,12 +333,14 @@ std::string JSON::Value::toString(){ } case OBJECT: { std::string tmp2 = "{"; - ObjIter it3 = ObjEnd(); - --it3; - for (ObjIter it2 = ObjBegin(); it2 != ObjEnd(); it2++){ - tmp2 += "\"" + it2->first + "\":"; - tmp2 += it2->second.toString(); - if (it2 != it3){tmp2 += ",";} + if (objVal.size() > 0){ + ObjIter it3 = ObjEnd(); + --it3; + for (ObjIter it2 = ObjBegin(); it2 != ObjEnd(); it2++){ + tmp2 += "\"" + it2->first + "\":"; + tmp2 += it2->second.toString(); + if (it2 != it3){tmp2 += ",";} + } } tmp2 += "}"; return tmp2; From 76d7acfbb77ec16a3ea203ab625fc7c37bcf3691 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 1 May 2012 15:10:59 +0200 Subject: [PATCH 190/788] Fixed JSON lib automatic conversion from string to long long int. --- util/json.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/util/json.cpp b/util/json.cpp index 2799c9f1..8d4b319f 100644 --- a/util/json.cpp +++ b/util/json.cpp @@ -3,6 +3,7 @@ #include "json.h" #include #include +#include int JSON::Value::c2hex(int c){ if (c >= '0' && c <= '9') return c - '0'; @@ -252,9 +253,15 @@ JSON::Value & JSON::Value::operator=(const unsigned int &rhs){ return ((*this) = (long long int)rhs); } -/// Automatic conversion to long long int - returns 0 if not an integer type. +/// Automatic conversion to long long int - returns 0 if not convertable. JSON::Value::operator long long int(){ - return intVal; + if (myType == INTEGER){ + return intVal; + } + if (myType == STRING){ + return atoll(strVal.c_str()); + } + return 0; } From 1db7ead3c0d37a2c76eb997e0fb1e6603a173318 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 1 May 2012 23:56:13 +0200 Subject: [PATCH 191/788] Added support for pulling media files through ffmpeg, added MistFLV2DTSC converter to binaries. --- util/procs.cpp | 125 ++++++++++++++++++++++++++++++++++++++++++++++++- util/procs.h | 1 + 2 files changed, 124 insertions(+), 2 deletions(-) diff --git a/util/procs.cpp b/util/procs.cpp index 83e3047d..1512849d 100644 --- a/util/procs.cpp +++ b/util/procs.cpp @@ -32,7 +32,7 @@ void Util::Procs::childsig_handler(int signum){ plist.erase(ret); #if DEBUG >= 1 if (isActive(pname)){ - std::cerr << "Process " << pname << " half-terminated." << std::endl; + std::cerr << "Process " << pname << " part-terminated." << std::endl; Stop(pname); }else{ std::cerr << "Process " << pname << " fully terminated." << std::endl; @@ -101,7 +101,7 @@ pid_t Util::Procs::Start(std::string name, std::string cmd){ } /// Starts two piped processes if the name is not already active. -/// \return 0 if process was not started, main (receiving) process PID otherwise. +/// \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. @@ -174,6 +174,127 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2){ return ret; } +/// Starts three piped processes if the name is not already active. +/// \return 0 if process was not started, sub (sending) process PID otherwise. +/// \arg name Name for this process - only used internally. +/// \arg cmd Commandline for sub (sending) process. +/// \arg cmd2 Commandline for sub (middle) process. +/// \arg cmd3 Commandline for main (receiving) process. +pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2, std::string cmd3){ + if (isActive(name)){return getPid(name);} + if (!handler_set){ + struct sigaction new_action; + new_action.sa_handler = Util::Procs::childsig_handler; + sigemptyset(&new_action.sa_mask); + new_action.sa_flags = 0; + sigaction(SIGCHLD, &new_action, NULL); + handler_set = true; + } + + int pfildes[2]; + int pfildes2[2]; + if (pipe(pfildes) == -1){ + #if DEBUG >= 1 + std::cerr << "Process " << name << " could not be started. Pipe creation failed." << std::endl; + #endif + return 0; + } + if (pipe(pfildes2) == -1){ + #if DEBUG >= 1 + std::cerr << "Process " << name << " could not be started. Pipe creation failed." << std::endl; + #endif + return 0; + } + + int devnull = open("/dev/null", O_RDWR); + pid_t ret = fork(); + if (ret == 0){ + close(pfildes[0]); + dup2(pfildes[1],STDOUT_FILENO); + close(pfildes[1]); + dup2(devnull, STDIN_FILENO); + dup2(devnull, STDERR_FILENO); + close(pfildes2[1]); + close(pfildes2[0]); + runCmd(cmd); + }else{ + if (ret > 0){ + plist.insert(std::pair(ret, name)); + }else{ + #if DEBUG >= 1 + std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl; + #endif + close(pfildes[1]); + close(pfildes[0]); + close(pfildes2[1]); + close(pfildes2[0]); + return 0; + } + } + + pid_t ret2 = fork(); + if (ret2 == 0){ + close(pfildes[1]); + close(pfildes2[0]); + dup2(pfildes[0],STDIN_FILENO); + close(pfildes[0]); + dup2(pfildes2[1],STDOUT_FILENO); + close(pfildes2[1]); + dup2(devnull, STDERR_FILENO); + runCmd(cmd2); + }else{ + if (ret2 > 0){ + #if DEBUG >= 1 + std::cerr << "Process " << name << " started, PIDs (" << ret << ", " << ret2 << "): " << cmd << " | " << cmd2 << std::endl; + #endif + plist.insert(std::pair(ret2, name)); + }else{ + #if DEBUG >= 1 + std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl; + #endif + Stop(name); + close(pfildes[1]); + close(pfildes[0]); + close(pfildes2[1]); + close(pfildes2[0]); + return 0; + } + } + close(pfildes[1]); + close(pfildes[0]); + + pid_t ret3 = fork(); + if (ret3 == 0){ + close(pfildes[1]); + close(pfildes[0]); + close(pfildes2[1]); + dup2(pfildes2[0],STDIN_FILENO); + close(pfildes2[0]); + dup2(devnull, STDOUT_FILENO); + dup2(devnull, STDERR_FILENO); + runCmd(cmd3); + }else{ + if (ret3 > 0){ + #if DEBUG >= 1 + std::cerr << "Process " << name << " started, PIDs (" << ret << ", " << ret2 << ", " << ret3 << "): " << cmd << " | " << cmd2 << " | " << cmd3 << std::endl; + #endif + plist.insert(std::pair(ret3, name)); + }else{ + #if DEBUG >= 1 + std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl; + #endif + Stop(name); + close(pfildes[1]); + close(pfildes[0]); + close(pfildes2[1]); + close(pfildes2[0]); + return 0; + } + } + + return ret3; +} + /// Stops the named process, if running. /// \arg name (Internal) name of process to stop void Util::Procs::Stop(std::string name){ diff --git a/util/procs.h b/util/procs.h index 3ec60902..c10620b8 100644 --- a/util/procs.h +++ b/util/procs.h @@ -18,6 +18,7 @@ namespace Util{ public: static pid_t Start(std::string name, std::string cmd); static pid_t Start(std::string name, std::string cmd, std::string cmd2); + static pid_t Start(std::string name, std::string cmd, std::string cmd2, std::string cmd3); static void Stop(std::string name); static void Stop(pid_t name); static void StopAll(); From c123111e702041c4847ec6b0fdbda4b3d733310b Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 4 May 2012 14:28:01 +0200 Subject: [PATCH 192/788] Fixed bugs for debug session may 4 2012. --- util/dtsc.cpp | 2 ++ util/socket.cpp | 12 ++++++++++++ util/socket.h | 1 + 3 files changed, 15 insertions(+) diff --git a/util/dtsc.cpp b/util/dtsc.cpp index 26601a5d..9e937d8d 100644 --- a/util/dtsc.cpp +++ b/util/dtsc.cpp @@ -102,6 +102,8 @@ bool DTSC::Stream::hasAudio(){ /// Returns a packed DTSC packet, ready to sent over the network. std::string & DTSC::Stream::outPacket(unsigned int num){ + static std::string emptystring; + if (num >= buffers.size()) return emptystring; buffers[num].Pack(true); return buffers[num].packed; } diff --git a/util/socket.cpp b/util/socket.cpp index 4ddcfb81..82c52dce 100644 --- a/util/socket.cpp +++ b/util/socket.cpp @@ -40,6 +40,18 @@ Socket::Connection::Connection(){ Blocking = false; }//Socket::Connection basic constructor + +/// Set this socket to be blocking (true) or nonblocking (false). +void Socket::Connection::setBlocking(bool blocking){ + int flags = fcntl(sock, F_GETFL, 0); + if (!blocking){ + flags |= O_NONBLOCK; + }else{ + flags &= !O_NONBLOCK; + } + fcntl(sock, F_SETFL, flags); +} + /// Close connection. The internal socket is closed and then set to -1. void Socket::Connection::close(){ #if DEBUG >= 6 diff --git a/util/socket.h b/util/socket.h index 503ec4e9..03750b2a 100644 --- a/util/socket.h +++ b/util/socket.h @@ -33,6 +33,7 @@ namespace Socket{ Connection(int sockNo); ///< Create a new base socket. Connection(std::string hostname, int port, bool nonblock); ///< Create a new TCP socket. Connection(std::string adres, bool nonblock = false); ///< Create a new Unix Socket. + void setBlocking(bool blocking); ///< Set this socket to be blocking (true) or nonblocking (false). bool canRead(); ///< Calls poll() on the socket, checking if data is available. bool canWrite(); ///< Calls poll() on the socket, checking if data can be written. signed int ready(); ///< Returns the ready-state for this socket. From ad410a2e799a646f42f8f59a2a570731f330833b Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 8 May 2012 19:19:42 +0200 Subject: [PATCH 193/788] Convert to autotools build system for cleanness (part1). --- Makefile.am | 2 + configure.ac | 39 +++ lib/Makefile.am | 15 + {util => lib}/amf.cpp | 0 {util => lib}/amf.h | 0 {util => lib}/auth.cpp | 0 {util => lib}/auth.h | 0 {util => lib}/base64.cpp | 0 {util => lib}/base64.h | 0 {util => lib}/config.cpp | 4 +- {util => lib}/config.h | 0 {util => lib}/crypto.cpp | 0 {util => lib}/crypto.h | 0 {util => lib}/dtsc.cpp | 0 {util => lib}/dtsc.h | 0 {util => lib}/flv_tag.cpp | 0 {util => lib}/flv_tag.h | 0 {util => lib}/http_parser.cpp | 0 {util => lib}/http_parser.h | 0 {util => lib}/json.cpp | 0 {util => lib}/json.h | 0 {util => lib}/md5.cpp | 0 {util => lib}/md5.h | 0 {util => lib}/procs.cpp | 0 {util => lib}/procs.h | 0 {util => lib}/rtmpchunks.cpp | 0 {util => lib}/rtmpchunks.h | 0 {util => lib}/socket.cpp | 0 {util => lib}/socket.h | 0 {util => lib}/tinythread.cpp | 0 {util => lib}/tinythread.h | 0 util/MP4/Makefile | 18 -- util/MP4/box.cpp | 123 ------- util/MP4/box_abst.cpp | 227 ------------- util/MP4/box_afra.cpp | 75 ----- util/MP4/box_afrt.cpp | 104 ------ util/MP4/box_amhp.cpp | 63 ---- util/MP4/box_asrt.cpp | 87 ----- util/MP4/box_avcC.cpp | 87 ----- util/MP4/box_dinf.cpp | 43 --- util/MP4/box_dref.cpp | 58 ---- util/MP4/box_esds.cpp | 55 ---- util/MP4/box_ftyp.cpp | 39 --- util/MP4/box_hdlr.cpp | 59 ---- util/MP4/box_hmhd.cpp | 57 ---- util/MP4/box_includes.h | 37 --- util/MP4/box_mdat.cpp | 29 -- util/MP4/box_mdhd.cpp | 88 ----- util/MP4/box_mdia.cpp | 51 --- util/MP4/box_mfhd.cpp | 39 --- util/MP4/box_minf.cpp | 51 --- util/MP4/box_moof.cpp | 51 --- util/MP4/box_moov.cpp | 51 --- util/MP4/box_mvex.cpp | 51 --- util/MP4/box_mvhd.cpp | 113 ------- util/MP4/box_nmhd.cpp | 28 -- util/MP4/box_rtmp.cpp | 89 ----- util/MP4/box_smhd.cpp | 29 -- util/MP4/box_stbl.cpp | 51 --- util/MP4/box_stco.cpp | 58 ---- util/MP4/box_stsc.cpp | 63 ---- util/MP4/box_stsd.cpp | 59 ---- util/MP4/box_stts.cpp | 60 ---- util/MP4/box_tfhd.cpp | 88 ----- util/MP4/box_tkhd.cpp | 117 ------- util/MP4/box_traf.cpp | 51 --- util/MP4/box_trak.cpp | 51 --- util/MP4/box_trex.cpp | 59 ---- util/MP4/box_trun.cpp | 68 ---- util/MP4/box_url.cpp | 23 -- util/MP4/box_vmhd.cpp | 45 --- util/MP4/interface.cpp | 589 ---------------------------------- util/MP4/main.cpp | 13 - util/server_setup.cpp | 115 ------- version.sh | 5 + 75 files changed, 63 insertions(+), 3314 deletions(-) create mode 100644 Makefile.am create mode 100644 configure.ac create mode 100644 lib/Makefile.am rename {util => lib}/amf.cpp (100%) rename {util => lib}/amf.h (100%) rename {util => lib}/auth.cpp (100%) rename {util => lib}/auth.h (100%) rename {util => lib}/base64.cpp (100%) rename {util => lib}/base64.h (100%) rename {util => lib}/config.cpp (95%) rename {util => lib}/config.h (100%) rename {util => lib}/crypto.cpp (100%) rename {util => lib}/crypto.h (100%) rename {util => lib}/dtsc.cpp (100%) rename {util => lib}/dtsc.h (100%) rename {util => lib}/flv_tag.cpp (100%) rename {util => lib}/flv_tag.h (100%) rename {util => lib}/http_parser.cpp (100%) rename {util => lib}/http_parser.h (100%) rename {util => lib}/json.cpp (100%) rename {util => lib}/json.h (100%) rename {util => lib}/md5.cpp (100%) rename {util => lib}/md5.h (100%) rename {util => lib}/procs.cpp (100%) rename {util => lib}/procs.h (100%) rename {util => lib}/rtmpchunks.cpp (100%) rename {util => lib}/rtmpchunks.h (100%) rename {util => lib}/socket.cpp (100%) rename {util => lib}/socket.h (100%) rename {util => lib}/tinythread.cpp (100%) rename {util => lib}/tinythread.h (100%) delete mode 100644 util/MP4/Makefile delete mode 100644 util/MP4/box.cpp delete mode 100644 util/MP4/box_abst.cpp delete mode 100644 util/MP4/box_afra.cpp delete mode 100644 util/MP4/box_afrt.cpp delete mode 100644 util/MP4/box_amhp.cpp delete mode 100644 util/MP4/box_asrt.cpp delete mode 100644 util/MP4/box_avcC.cpp delete mode 100644 util/MP4/box_dinf.cpp delete mode 100644 util/MP4/box_dref.cpp delete mode 100644 util/MP4/box_esds.cpp delete mode 100644 util/MP4/box_ftyp.cpp delete mode 100644 util/MP4/box_hdlr.cpp delete mode 100644 util/MP4/box_hmhd.cpp delete mode 100644 util/MP4/box_includes.h delete mode 100644 util/MP4/box_mdat.cpp delete mode 100644 util/MP4/box_mdhd.cpp delete mode 100644 util/MP4/box_mdia.cpp delete mode 100644 util/MP4/box_mfhd.cpp delete mode 100644 util/MP4/box_minf.cpp delete mode 100644 util/MP4/box_moof.cpp delete mode 100644 util/MP4/box_moov.cpp delete mode 100644 util/MP4/box_mvex.cpp delete mode 100644 util/MP4/box_mvhd.cpp delete mode 100644 util/MP4/box_nmhd.cpp delete mode 100644 util/MP4/box_rtmp.cpp delete mode 100644 util/MP4/box_smhd.cpp delete mode 100644 util/MP4/box_stbl.cpp delete mode 100644 util/MP4/box_stco.cpp delete mode 100644 util/MP4/box_stsc.cpp delete mode 100644 util/MP4/box_stsd.cpp delete mode 100644 util/MP4/box_stts.cpp delete mode 100644 util/MP4/box_tfhd.cpp delete mode 100644 util/MP4/box_tkhd.cpp delete mode 100644 util/MP4/box_traf.cpp delete mode 100644 util/MP4/box_trak.cpp delete mode 100644 util/MP4/box_trex.cpp delete mode 100644 util/MP4/box_trun.cpp delete mode 100644 util/MP4/box_url.cpp delete mode 100644 util/MP4/box_vmhd.cpp delete mode 100644 util/MP4/interface.cpp delete mode 100644 util/MP4/main.cpp delete mode 100644 util/server_setup.cpp create mode 100755 version.sh diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 00000000..6cf1b795 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,2 @@ +SUBDIRS=lib src +EXTRA_DIST=server.html diff --git a/configure.ac b/configure.ac new file mode 100644 index 00000000..1bce87ef --- /dev/null +++ b/configure.ac @@ -0,0 +1,39 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ([2.60]) +m4_include([version.m4]) +AC_INIT(MistServer, VERSION_NUMBER, contact@ddvtech.com) +AC_CONFIG_SRCDIR([src/buffer.cpp]) +AM_INIT_AUTOMAKE + +# Checks for programs. +AC_PROG_CXX +AC_PROG_CC + +# Checks for libraries. +AC_PROG_RANLIB + +# Checks for header files. +AC_CHECK_HEADERS([arpa/inet.h fcntl.h netdb.h netinet/in.h stdint.h stdlib.h string.h sys/socket.h sys/time.h unistd.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_HEADER_STDBOOL +AC_C_INLINE +AC_TYPE_INT32_T +AC_TYPE_PID_T +AC_TYPE_SIZE_T +AC_TYPE_UINT32_T +AC_TYPE_UINT64_T +AC_TYPE_UINT8_T + +# Checks for library functions. +AC_FUNC_FORK +AC_FUNC_MALLOC +AC_FUNC_REALLOC +AC_CHECK_FUNCS([dup2 gettimeofday memset mkdir socket strerror]) + +AC_CONFIG_FILES([Makefile + lib/Makefile + src/Makefile]) +AC_OUTPUT diff --git a/lib/Makefile.am b/lib/Makefile.am new file mode 100644 index 00000000..76d5de1c --- /dev/null +++ b/lib/Makefile.am @@ -0,0 +1,15 @@ +noinst_LIBRARIES=libamf.a libauth.a libbase64.a libconfig.a libcrypto.a libdtsc.a libflv_tag.a libhttp_parser.a libjson.a libmd5.a libprocs.a librtmpchunks.a libsocket.a libtinythread.a +libamf_a_SOURCES=amf.h amf.cpp +libauth_a_SOURCES=auth.h auth.cpp +libbase64_a_SOURCES=base64.h base64.cpp +libconfig_a_SOURCES=config.h config.cpp +libcrypto_a_SOURCES=crypto.h crypto.cpp +libdtsc_a_SOURCES=dtsc.h dtsc.cpp +libflv_tag_a_SOURCES=flv_tag.h flv_tag.cpp +libhttp_parser_a_SOURCES=http_parser.h http_parser.cpp +libjson_a_SOURCES=json.h json.cpp +libmd5_a_SOURCES=md5.h md5.cpp +libprocs_a_SOURCES=procs.h procs.cpp +librtmpchunks_a_SOURCES=rtmpchunks.h rtmpchunks.cpp +libsocket_a_SOURCES=socket.h socket.cpp +libtinythread_a_SOURCES=tinythread.h tinythread.cpp diff --git a/util/amf.cpp b/lib/amf.cpp similarity index 100% rename from util/amf.cpp rename to lib/amf.cpp diff --git a/util/amf.h b/lib/amf.h similarity index 100% rename from util/amf.h rename to lib/amf.h diff --git a/util/auth.cpp b/lib/auth.cpp similarity index 100% rename from util/auth.cpp rename to lib/auth.cpp diff --git a/util/auth.h b/lib/auth.h similarity index 100% rename from util/auth.h rename to lib/auth.h diff --git a/util/base64.cpp b/lib/base64.cpp similarity index 100% rename from util/base64.cpp rename to lib/base64.cpp diff --git a/util/base64.h b/lib/base64.h similarity index 100% rename from util/base64.h rename to lib/base64.h diff --git a/util/config.cpp b/lib/config.cpp similarity index 95% rename from util/config.cpp rename to lib/config.cpp index b7533fc3..f2d9fe17 100644 --- a/util/config.cpp +++ b/lib/config.cpp @@ -53,7 +53,7 @@ void Util::Config::parseArgs(int argc, char ** argv){ case 'd': daemon_mode = true; break; case 'u': username = optarg; break; case 'v': - printf("%s\n", TOSTRING(VERSION)); + printf("%s\n", TOSTRING(PACKAGE_VERSION)); exit(1); break; case 'h': @@ -63,7 +63,7 @@ void Util::Config::parseArgs(int argc, char ** argv){ printf("Options: -h[elp], -?, -v[ersion], -n[odaemon], -d[aemon], -p[ort] VAL, -i[nterface] VAL, -u[sername] VAL\n"); printf("Defaults:\n interface: %s\n port: %i\n daemon mode: %s\n username: %s\n", interface.c_str(), listen_port, doingdaemon.c_str(), username.c_str()); printf("Username root means no change to UID, no matter what the UID is.\n"); - printf("This is %s version %s\n", argv[0], TOSTRING(VERSION)); + printf("This is %s version %s\n", argv[0], TOSTRING(PACKAGE_VERSION)); exit(1); break; } diff --git a/util/config.h b/lib/config.h similarity index 100% rename from util/config.h rename to lib/config.h diff --git a/util/crypto.cpp b/lib/crypto.cpp similarity index 100% rename from util/crypto.cpp rename to lib/crypto.cpp diff --git a/util/crypto.h b/lib/crypto.h similarity index 100% rename from util/crypto.h rename to lib/crypto.h diff --git a/util/dtsc.cpp b/lib/dtsc.cpp similarity index 100% rename from util/dtsc.cpp rename to lib/dtsc.cpp diff --git a/util/dtsc.h b/lib/dtsc.h similarity index 100% rename from util/dtsc.h rename to lib/dtsc.h diff --git a/util/flv_tag.cpp b/lib/flv_tag.cpp similarity index 100% rename from util/flv_tag.cpp rename to lib/flv_tag.cpp diff --git a/util/flv_tag.h b/lib/flv_tag.h similarity index 100% rename from util/flv_tag.h rename to lib/flv_tag.h diff --git a/util/http_parser.cpp b/lib/http_parser.cpp similarity index 100% rename from util/http_parser.cpp rename to lib/http_parser.cpp diff --git a/util/http_parser.h b/lib/http_parser.h similarity index 100% rename from util/http_parser.h rename to lib/http_parser.h diff --git a/util/json.cpp b/lib/json.cpp similarity index 100% rename from util/json.cpp rename to lib/json.cpp diff --git a/util/json.h b/lib/json.h similarity index 100% rename from util/json.h rename to lib/json.h diff --git a/util/md5.cpp b/lib/md5.cpp similarity index 100% rename from util/md5.cpp rename to lib/md5.cpp diff --git a/util/md5.h b/lib/md5.h similarity index 100% rename from util/md5.h rename to lib/md5.h diff --git a/util/procs.cpp b/lib/procs.cpp similarity index 100% rename from util/procs.cpp rename to lib/procs.cpp diff --git a/util/procs.h b/lib/procs.h similarity index 100% rename from util/procs.h rename to lib/procs.h diff --git a/util/rtmpchunks.cpp b/lib/rtmpchunks.cpp similarity index 100% rename from util/rtmpchunks.cpp rename to lib/rtmpchunks.cpp diff --git a/util/rtmpchunks.h b/lib/rtmpchunks.h similarity index 100% rename from util/rtmpchunks.h rename to lib/rtmpchunks.h diff --git a/util/socket.cpp b/lib/socket.cpp similarity index 100% rename from util/socket.cpp rename to lib/socket.cpp diff --git a/util/socket.h b/lib/socket.h similarity index 100% rename from util/socket.h rename to lib/socket.h diff --git a/util/tinythread.cpp b/lib/tinythread.cpp similarity index 100% rename from util/tinythread.cpp rename to lib/tinythread.cpp diff --git a/util/tinythread.h b/lib/tinythread.h similarity index 100% rename from util/tinythread.h rename to lib/tinythread.h diff --git a/util/MP4/Makefile b/util/MP4/Makefile deleted file mode 100644 index 4ff1d185..00000000 --- a/util/MP4/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -SRC = box_abst.cpp box_afra.cpp box_afrt.cpp box_amhp.cpp box_asrt.cpp box_avcC.cpp box.cpp box_dinf.cpp box_dref.cpp box_esds.cpp box_ftyp.cpp box_hdlr.cpp box_hmhd.cpp box_mdat.cpp box_mdhd.cpp box_mdia.cpp box_mfhd.cpp box_minf.cpp box_moof.cpp box_moov.cpp box_mvex.cpp box_mvhd.cpp box_nmhd.cpp box_rtmp.cpp box_smhd.cpp box_stbl.cpp box_stco.cpp box_stsc.cpp box_stsd.cpp box_stts.cpp box_tfhd.cpp box_tkhd.cpp box_traf.cpp box_trak.cpp box_trex.cpp box_trun.cpp box_url.cpp box_vmhd.cpp interface.cpp main.cpp -OBJ = $(SRC:.cpp=.o) -OUT = Boxtest -INCLUDES = -CCFLAGS = -Wall -Wextra -funsigned-char -g -CC = $(CROSS)g++ -LD = $(CROSS)ld -AR = $(CROSS)ar -LIBS = -.SUFFIXES: .cpp -.PHONY: clean default -default: $(OUT) -.cpp.o: - $(CC) $(INCLUDES) $(CCFLAGS) $(LIBS) -c $< -o $@ -$(OUT): $(OBJ) - $(CC) $(LIBS) -o $(OUT) $(OBJ) -clean: - rm -rf $(OBJ) $(OUT) Makefile.bak *~ diff --git a/util/MP4/box.cpp b/util/MP4/box.cpp deleted file mode 100644 index 9f790b7f..00000000 --- a/util/MP4/box.cpp +++ /dev/null @@ -1,123 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -class Box { - public: - Box(); - Box(uint32_t BoxType); - Box(uint8_t * Content, uint32_t length); - ~Box(); - void SetBoxType(uint32_t BoxType); - uint32_t GetBoxType(); - void SetPayload(uint32_t Size, uint8_t * Data, uint32_t Index = 0); - uint32_t GetPayloadSize(); - uint8_t * GetPayload(); - uint8_t * GetPayload(uint32_t Index, uint32_t & Size); - uint32_t GetBoxedDataSize(); - uint8_t * GetBoxedData( ); - static uint8_t * uint32_to_uint8( uint32_t data ); - static uint8_t * uint16_to_uint8( uint16_t data ); - static uint8_t * uint8_to_uint8( uint8_t data ); - void ResetPayload( ); - private: - uint8_t * Payload; - uint32_t PayloadSize; -};//Box Class - -Box::Box() { - Payload = (uint8_t *)malloc(8); - PayloadSize = 0; -} - -Box::Box(uint32_t BoxType) { - Payload = (uint8_t *)malloc(8); - SetBoxType(BoxType); - PayloadSize = 0; -} - -Box::Box(uint8_t * Content, uint32_t length) { - PayloadSize = length-8; - Payload = (uint8_t *)malloc(length); - memcpy(Payload, Content, length); -} - -Box::~Box() { - if (Payload) free(Payload); -} - -void Box::SetBoxType(uint32_t BoxType) { - ((unsigned int*)Payload)[1] = htonl(BoxType); -} - -uint32_t Box::GetBoxType() { - return ntohl(((unsigned int*)Payload)[1]); -} - -void Box::SetPayload(uint32_t Size, uint8_t * Data, uint32_t Index) { - if ( Index + Size > PayloadSize ) { - PayloadSize = Index + Size; - ((unsigned int*)Payload)[0] = htonl(PayloadSize+8); - Payload = (uint8_t *)realloc(Payload, PayloadSize + 8); - } - memcpy(Payload + 8 + Index, Data, Size); -} - -uint32_t Box::GetPayloadSize() { - return PayloadSize; -} - -uint8_t * Box::GetPayload() { - return Payload+8; -} - -uint8_t * Box::GetPayload(uint32_t Index, uint32_t & Size) { - if(Index > PayloadSize) {Size = 0;} - if(Index + Size > PayloadSize) { Size = PayloadSize - Index; } - return Payload + 8 + Index; -} - -uint32_t Box::GetBoxedDataSize() { - return ntohl(((unsigned int*)Payload)[0]); -} - -uint8_t * Box::GetBoxedData( ) { - return Payload; -} - - -uint8_t * Box::uint32_to_uint8( uint32_t data ) { - uint8_t * temp = new uint8_t[4]; - temp[0] = (data >> 24) & 0x000000FF; - temp[1] = (data >> 16 ) & 0x000000FF; - temp[2] = (data >> 8 ) & 0x000000FF; - temp[3] = (data ) & 0x000000FF; - return temp; -} - -uint8_t * Box::uint16_to_uint8( uint16_t data ) { - uint8_t * temp = new uint8_t[2]; - temp[0] = (data >> 8) & 0x00FF; - temp[1] = (data ) & 0x00FF; - return temp; -} - -uint8_t * Box::uint8_to_uint8( uint8_t data ) { - uint8_t * temp = new uint8_t[1]; - temp[0] = data; - return temp; -} - -void Box::ResetPayload( ) { - PayloadSize = 0; - Payload = (uint8_t *)realloc(Payload, PayloadSize + 8); - ((unsigned int*)Payload)[0] = htonl(0); -} diff --git a/util/MP4/box_abst.cpp b/util/MP4/box_abst.cpp deleted file mode 100644 index 238679ee..00000000 --- a/util/MP4/box_abst.cpp +++ /dev/null @@ -1,227 +0,0 @@ -#pragma once -#include "box.cpp" -#include -#include - - -struct abst_serverentry { - std::string ServerBaseUrl; -};//abst_serverentry - -struct abst_qualityentry { - std::string QualityModifier; -};//abst_qualityentry - -class Box_abst { - public: - Box_abst( ); - ~Box_abst(); - Box * GetBox(); - void SetBootstrapVersion( uint32_t Version = 1 ); - void SetProfile( uint8_t Profile = 0 ); - void SetLive( bool Live = true ); - void SetUpdate( bool Update = false ); - void SetTimeScale( uint32_t Scale = 1000 ); - void SetMediaTime( uint32_t Time = 0 ); - void SetSMPTE( uint32_t Smpte = 0 ); - void SetMovieIdentifier( std::string Identifier = "" ); - void SetDRM( std::string Drm = "" ); - void SetMetaData( std::string MetaData = "" ); - void AddServerEntry( std::string Url = "", uint32_t Offset = 0 ); - void AddQualityEntry( std::string Quality = "", uint32_t Offset = 0 ); - void AddSegmentRunTable( Box * newSegment, uint32_t Offset = 0 ); - void AddFragmentRunTable( Box * newFragment, uint32_t Offset = 0 ); - void SetVersion( bool NewVersion = 0 ); - void WriteContent( ); - private: - void SetDefaults( ); - void SetReserved( ); - uint32_t curBootstrapInfoVersion; - uint8_t curProfile; - bool isLive; - bool isUpdate; - bool Version; - uint32_t curTimeScale; - uint32_t curMediatime;//write as uint64_t - uint32_t curSMPTE;//write as uint64_t - std::string curMovieIdentifier; - std::string curDRM; - std::string curMetaData; - std::vector Servers; - std::vector Qualities; - std::vector SegmentRunTables; - std::vector FragmentRunTables; - Box * Container; -};//Box_ftyp Class - -Box_abst::Box_abst( ) { - Container = new Box( 0x61627374 ); - SetDefaults( ); -} - -Box_abst::~Box_abst() { - delete Container; -} - -Box * Box_abst::GetBox() { - return Container; -} - -void Box_abst::SetBootstrapVersion( uint32_t Version ) { - curBootstrapInfoVersion = Version; -} - -void Box_abst::SetProfile( uint8_t Profile ) { - curProfile = Profile; -} - -void Box_abst::SetLive( bool Live ) { - isLive = Live; -} - -void Box_abst::SetUpdate( bool Update ) { - isUpdate = Update; -} - -void Box_abst::SetTimeScale( uint32_t Scale ) { - curTimeScale = Scale; -} - -void Box_abst::SetMediaTime( uint32_t Time ) { - curMediatime = Time; -} - -void Box_abst::SetSMPTE( uint32_t Smpte ) { - curSMPTE = Smpte; -} - -void Box_abst::SetMovieIdentifier( std::string Identifier ) { - curMovieIdentifier = Identifier; -} - -void Box_abst::SetDRM( std::string Drm ) { - curDRM = Drm; -} - -void Box_abst::SetMetaData( std::string MetaData ) { - curMetaData = MetaData; -} - -void Box_abst::AddServerEntry( std::string Url, uint32_t Offset ) { - if(Offset >= Servers.size()) { - Servers.resize(Offset+1); - } - Servers[Offset].ServerBaseUrl = Url; -} - -void Box_abst::AddQualityEntry( std::string Quality, uint32_t Offset ) { - if(Offset >= Qualities.size()) { - Qualities.resize(Offset+1); - } - Qualities[Offset].QualityModifier = Quality; -} - -void Box_abst::AddSegmentRunTable( Box * newSegment, uint32_t Offset ) { - if( Offset >= SegmentRunTables.size() ) { - SegmentRunTables.resize(Offset+1); - } - if( SegmentRunTables[Offset] ) { - delete SegmentRunTables[Offset]; - } - SegmentRunTables[Offset] = newSegment; -} - -void Box_abst::AddFragmentRunTable( Box * newFragment, uint32_t Offset ) { - if( Offset >= FragmentRunTables.size() ) { - FragmentRunTables.resize(Offset+1); - } - if( FragmentRunTables[Offset] ) { - delete FragmentRunTables[Offset]; - } - FragmentRunTables[Offset] = newFragment; -} - -void Box_abst::SetDefaults( ) { - SetProfile( ); - SetLive( ); - SetUpdate( ); - SetTimeScale( ); - SetMediaTime( ); - SetSMPTE( ); - SetMovieIdentifier( ); - SetDRM( ); - SetMetaData( ); - SetVersion( ); -} - -void Box_abst::SetVersion( bool NewVersion) { - Version = NewVersion; -} - -void Box_abst::SetReserved( ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0)); -} - -void Box_abst::WriteContent( ) { - Box * current; - std::string serializedServers = ""; - std::string serializedQualities = ""; - std::string serializedSegments = ""; - std::string serializedFragments = ""; - int SegmentAmount = 0; - int FragmentAmount = 0; - uint8_t * temp = new uint8_t[1]; - - Container->ResetPayload( ); - SetReserved( ); - - for( uint32_t i = 0; i < Servers.size(); i++ ) { - serializedServers.append(Servers[i].ServerBaseUrl.c_str()); - serializedServers += '\0'; - } - for( uint32_t i = 0; i < Qualities.size(); i++ ) { - serializedQualities.append(Qualities[i].QualityModifier.c_str()); - serializedQualities += '\0'; - } - for( uint32_t i = 0; i < SegmentRunTables.size(); i++ ) { - current=SegmentRunTables[i]; - if( current ) { - SegmentAmount ++; - serializedSegments.append((char*)current->GetBoxedData(),current->GetBoxedDataSize()); - } - } - for( uint32_t i = 0; i < FragmentRunTables.size(); i++ ) { - current=FragmentRunTables[i]; - if( current ) { - FragmentAmount ++; - serializedFragments.append((char*)current->GetBoxedData(),current->GetBoxedDataSize()); - } - } - uint32_t OffsetServerEntryCount = 29 + curMovieIdentifier.size() + 1; - uint32_t OffsetQualityEntryCount = OffsetServerEntryCount + 1 + serializedServers.size(); - uint32_t OffsetDrmData = OffsetQualityEntryCount + 1 + serializedQualities.size(); - uint32_t OffsetMetaData = OffsetDrmData + curDRM.size() + 1; - uint32_t OffsetSegmentRuntableCount = OffsetMetaData + curMetaData.size() + 1; - uint32_t OffsetFragmentRuntableCount = OffsetSegmentRuntableCount + 1 + serializedSegments.size(); - - temp[0] = 0 + ( curProfile << 6 ) + ( (uint8_t)isLive << 7 ) + ( (uint8_t)isUpdate << 7 ); - - Container->SetPayload((uint32_t)serializedFragments.size(),(uint8_t*)serializedFragments.c_str(),OffsetFragmentRuntableCount+1); - Container->SetPayload((uint32_t)1,Box::uint8_to_uint8(FragmentAmount),OffsetFragmentRuntableCount); - Container->SetPayload((uint32_t)serializedSegments.size(),(uint8_t*)serializedSegments.c_str(),OffsetSegmentRuntableCount+1); - Container->SetPayload((uint32_t)1,Box::uint8_to_uint8(SegmentAmount),OffsetSegmentRuntableCount); - Container->SetPayload((uint32_t)curMetaData.size()+1,(uint8_t*)curMetaData.c_str(),OffsetMetaData); - Container->SetPayload((uint32_t)curDRM.size()+1,(uint8_t*)curDRM.c_str(),OffsetDrmData); - Container->SetPayload((uint32_t)serializedQualities.size(),(uint8_t*)serializedQualities.c_str(),OffsetQualityEntryCount+1); - Container->SetPayload((uint32_t)1,Box::uint8_to_uint8(Qualities.size()),OffsetQualityEntryCount); - Container->SetPayload((uint32_t)serializedServers.size(),(uint8_t*)serializedServers.c_str(),OffsetServerEntryCount+1); - Container->SetPayload((uint32_t)1,Box::uint8_to_uint8(Servers.size()),OffsetServerEntryCount); - Container->SetPayload((uint32_t)curMovieIdentifier.size()+1,(uint8_t*)curMovieIdentifier.c_str(),29);//+1 for \0-terminated string... - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(curSMPTE),25); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),21); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(curMediatime),17); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),13); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(curTimeScale),9); - Container->SetPayload((uint32_t)1,temp,8); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(curBootstrapInfoVersion),4); -} diff --git a/util/MP4/box_afra.cpp b/util/MP4/box_afra.cpp deleted file mode 100644 index 103c2c40..00000000 --- a/util/MP4/box_afra.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include "box.cpp" -#include - -struct afra_record { - uint32_t Time; - uint32_t Offset; -};//afra_record - -class Box_afra { - public: - Box_afra( ); - ~Box_afra(); - Box * GetBox(); - void SetTimeScale( uint32_t Scale = 1 ); - void AddEntry( uint32_t Time = 0, uint32_t SampleOffset = 0, uint32_t Offset = 0 ); - void WriteContent( ); - private: - void SetReserved( ); - void SetDefaults( ); - - Box * Container; - uint32_t CurrentTimeScale; - std::vector Entries; -};//Box_ftyp Class - -Box_afra::Box_afra( ) { - Container = new Box( 0x61667261 ); - SetReserved( ); - SetDefaults( ); -} - -Box_afra::~Box_afra() { - delete Container; -} - -Box * Box_afra::GetBox() { - return Container; -} - -void Box_afra::SetDefaults( ) { - SetTimeScale( ); -} - -void Box_afra::SetReserved( ) { - uint8_t * temp = new uint8_t[1]; - temp[0] = 0; - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),1); - Container->SetPayload((uint32_t)1,temp); -} - -void Box_afra::SetTimeScale( uint32_t Scale ) { - CurrentTimeScale = Scale; - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Scale),5); -} - -void Box_afra::AddEntry( uint32_t Time, uint32_t SampleOffset, uint32_t Offset ) { - if(Offset >= Entries.size()) { - Entries.resize(Offset+1); - } - Entries[Offset].Time = Time; - Entries[Offset].Offset = SampleOffset; -} - -void Box_afra::WriteContent( ) { - Container->ResetPayload(); - SetReserved( ); - if(!Entries.empty()) { - for(int32_t i = Entries.size() -1; i >= 0; i--) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Entries[i].Offset),(i*12)+21); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Entries[i].Time),(i*12)+17); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),(i*12)+13); - } - } - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Entries.size()),9); -} diff --git a/util/MP4/box_afrt.cpp b/util/MP4/box_afrt.cpp deleted file mode 100644 index 06ddece2..00000000 --- a/util/MP4/box_afrt.cpp +++ /dev/null @@ -1,104 +0,0 @@ -#include "box.cpp" -#include -#include - - -struct afrt_fragmentrunentry { - uint32_t FirstFragment; - uint32_t FirstFragmentTimestamp; //write as uint64_t - uint32_t FragmentDuration; - uint8_t DiscontinuityIndicator;//if FragmentDuration == 0 -};//afrt_fragmentrunentry - - -class Box_afrt { - public: - Box_afrt( ); - ~Box_afrt(); - Box * GetBox(); - void SetUpdate( bool Update = false ); - void SetTimeScale( uint32_t Scale = 1000 ); - void AddQualityEntry( std::string Quality = "", uint32_t Offset = 0 ); - void AddFragmentRunEntry( uint32_t FirstFragment = 0, uint32_t FirstFragmentTimestamp = 0, uint32_t FragmentsDuration = 1, uint8_t Discontinuity = 0, uint32_t Offset = 0 ); - void WriteContent( ); - private: - void SetDefaults( ); - bool isUpdate; - uint32_t curTimeScale; - std::vector QualitySegmentUrlModifiers; - std::vector FragmentRunEntryTable; - Box * Container; -};//Box_ftyp Class - -Box_afrt::Box_afrt( ) { - Container = new Box( 0x61667274 ); -} - -Box_afrt::~Box_afrt() { - delete Container; -} - -Box * Box_afrt::GetBox() { - return Container; -} - -void Box_afrt::SetUpdate( bool Update ) { - isUpdate = Update; -} - -void Box_afrt::AddQualityEntry( std::string Quality, uint32_t Offset ) { - if(Offset >= QualitySegmentUrlModifiers.size()) { - QualitySegmentUrlModifiers.resize(Offset+1); - } - QualitySegmentUrlModifiers[Offset] = Quality; -} - -void Box_afrt::AddFragmentRunEntry( uint32_t FirstFragment, uint32_t FirstFragmentTimestamp, uint32_t FragmentsDuration, uint8_t Discontinuity, uint32_t Offset ) { - if( Offset >= FragmentRunEntryTable.size() ) { - FragmentRunEntryTable.resize(Offset+1); - } - FragmentRunEntryTable[Offset].FirstFragment = FirstFragment; - FragmentRunEntryTable[Offset].FirstFragmentTimestamp = FirstFragmentTimestamp; - FragmentRunEntryTable[Offset].FragmentDuration = FragmentsDuration; - if( FragmentsDuration == 0) { - FragmentRunEntryTable[Offset].DiscontinuityIndicator = Discontinuity; - } -} - -void Box_afrt::SetDefaults( ) { - SetUpdate( ); - SetTimeScale( ); -} - -void Box_afrt::SetTimeScale( uint32_t Scale ) { - curTimeScale = Scale; -} - -void Box_afrt::WriteContent( ) { - std::string serializedQualities = ""; - std::string serializedFragmentEntries = ""; - Container->ResetPayload( ); - - for( uint32_t i = 0; i < QualitySegmentUrlModifiers.size(); i++ ) { - serializedQualities.append(QualitySegmentUrlModifiers[i].c_str()); - serializedQualities += '\0'; - } - for( uint32_t i = 0; i < FragmentRunEntryTable.size(); i ++ ) { - serializedFragmentEntries.append((char*)Box::uint32_to_uint8(FragmentRunEntryTable[i].FirstFragment),4); - serializedFragmentEntries.append((char*)Box::uint32_to_uint8(0),4); - serializedFragmentEntries.append((char*)Box::uint32_to_uint8(FragmentRunEntryTable[i].FirstFragmentTimestamp),4); - serializedFragmentEntries.append((char*)Box::uint32_to_uint8(FragmentRunEntryTable[i].FragmentDuration),4); - if(FragmentRunEntryTable[i].FragmentDuration == 0) { - serializedFragmentEntries.append((char*)Box::uint8_to_uint8(FragmentRunEntryTable[i].DiscontinuityIndicator),1); - } - } - - uint32_t OffsetFragmentRunEntryCount = 9 + serializedQualities.size(); - - Container->SetPayload((uint32_t)serializedFragmentEntries.size(),(uint8_t*)serializedFragmentEntries.c_str(),OffsetFragmentRunEntryCount+4); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(FragmentRunEntryTable.size()),OffsetFragmentRunEntryCount); - Container->SetPayload((uint32_t)serializedQualities.size(),(uint8_t*)serializedQualities.c_str(),9); - Container->SetPayload((uint32_t)1,Box::uint8_to_uint8(QualitySegmentUrlModifiers.size()),8); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(curTimeScale),4); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8((isUpdate ? 1 : 0))); -} diff --git a/util/MP4/box_amhp.cpp b/util/MP4/box_amhp.cpp deleted file mode 100644 index 761da82a..00000000 --- a/util/MP4/box_amhp.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#include "box.cpp" -#include -#include - -struct amhp_record { - uint8_t HintTrackMode; - uint8_t Settings; - uint8_t TrailerDefaultSize; -};//stsc_record - -class Box_amhp { - public: - Box_amhp( ); - ~Box_amhp(); - Box * GetBox(); - void SetReserved( ); - void AddEntry( uint8_t HintTrackMode, uint8_t Settings, uint8_t TrailerDefaultSize, uint32_t Offset = 0 ); - void WriteContent( ); - private: - Box * Container; - - std::vector Entries; -};//Box_ftyp Class - -Box_amhp::Box_amhp( ) { - Container = new Box( 0x616D6870 ); - SetReserved(); -} - -Box_amhp::~Box_amhp() { - delete Container; -} - -Box * Box_amhp::GetBox() { - return Container; -} - -void Box_amhp::SetReserved( ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0)); -} - -void Box_amhp::AddEntry( uint8_t HintTrackMode, uint8_t Settings, uint8_t TrailerDefaultSize, uint32_t Offset ) { - if(Offset >= Entries.size()) { - Entries.resize(Offset+1); - } - Entries[Offset].HintTrackMode = HintTrackMode; - Entries[Offset].Settings = Settings; - Entries[Offset].TrailerDefaultSize = TrailerDefaultSize; -} - - -void Box_amhp::WriteContent( ) { - Container->ResetPayload(); - SetReserved( ); - if(!Entries.empty()) { - for(int32_t i = Entries.size() -1; i >= 0; i--) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Entries[i].TrailerDefaultSize),(i*12)+16); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Entries[i].Settings),(i*12)+12); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Entries[i].HintTrackMode),(i*12)+8); - } - } - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Entries.size()),4); -} diff --git a/util/MP4/box_asrt.cpp b/util/MP4/box_asrt.cpp deleted file mode 100644 index cbc5ba31..00000000 --- a/util/MP4/box_asrt.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include "box.cpp" -#include -#include - -struct asrt_segmentrunentry { - uint32_t FirstSegment; - uint32_t FragmentsPerSegment; -};//abst_qualityentry - -class Box_asrt { - public: - Box_asrt( ); - ~Box_asrt(); - Box * GetBox(); - void SetUpdate( bool Update = false ); - void AddQualityEntry( std::string Quality = "", uint32_t Offset = 0 ); - void AddSegmentRunEntry( uint32_t FirstSegment = 0, uint32_t FragmentsPerSegment = 100, uint32_t Offset = 0 ); - void WriteContent( ); - void SetVersion( bool NewVersion = 0 ); - private: - void SetDefaults( ); - bool isUpdate; - bool Version; - std::vector QualitySegmentUrlModifiers; - std::vector SegmentRunEntryTable; - Box * Container; -};//Box_ftyp Class - -Box_asrt::Box_asrt( ) { - Container = new Box( 0x61737274 ); -} - -Box_asrt::~Box_asrt() { - delete Container; -} - -Box * Box_asrt::GetBox() { - return Container; -} - -void Box_asrt::SetUpdate( bool Update ) { - isUpdate = Update; -} - -void Box_asrt::AddQualityEntry( std::string Quality, uint32_t Offset ) { - if(Offset >= QualitySegmentUrlModifiers.size()) { - QualitySegmentUrlModifiers.resize(Offset+1); - } - QualitySegmentUrlModifiers[Offset] = Quality; -} - -void Box_asrt::AddSegmentRunEntry( uint32_t FirstSegment, uint32_t FragmentsPerSegment, uint32_t Offset ) { - if( Offset >= SegmentRunEntryTable.size() ) { - SegmentRunEntryTable.resize(Offset+1); - } - SegmentRunEntryTable[Offset].FirstSegment = FirstSegment; - SegmentRunEntryTable[Offset].FragmentsPerSegment = FragmentsPerSegment; -} - -void Box_asrt::SetVersion( bool NewVersion ) { - Version = NewVersion; -} - -void Box_asrt::SetDefaults( ) { - SetUpdate( ); -} - -void Box_asrt::WriteContent( ) { - std::string serializedQualities = ""; - Container->ResetPayload( ); - - for( uint32_t i = 0; i < QualitySegmentUrlModifiers.size(); i++ ) { - serializedQualities.append(QualitySegmentUrlModifiers[i].c_str()); - serializedQualities += '\0'; - } - - uint32_t OffsetSegmentRunEntryCount = 5 + serializedQualities.size(); - - for( uint32_t i = 0; i < SegmentRunEntryTable.size(); i ++ ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(SegmentRunEntryTable[i].FragmentsPerSegment),(8*i)+OffsetSegmentRunEntryCount+8); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(SegmentRunEntryTable[i].FirstSegment),(8*i)+OffsetSegmentRunEntryCount+4); - } - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(SegmentRunEntryTable.size()),OffsetSegmentRunEntryCount); - Container->SetPayload((uint32_t)serializedQualities.size(),(uint8_t*)serializedQualities.c_str(),5); - Container->SetPayload((uint32_t)1,Box::uint8_to_uint8(QualitySegmentUrlModifiers.size()),4); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8((isUpdate ? 1 : 0))); -} diff --git a/util/MP4/box_avcC.cpp b/util/MP4/box_avcC.cpp deleted file mode 100644 index 2f021957..00000000 --- a/util/MP4/box_avcC.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include "box.cpp" -#include -#include - -class Box_avcC { - public: - Box_avcC( ); - ~Box_avcC(); - Box * GetBox(); - void SetDataReferenceIndex( uint16_t DataReferenceIndex = 1 ); - void SetWidth( uint16_t Width = 0 ); - void SetHeight( uint16_t Height = 0 ); - void SetResolution ( uint32_t Horizontal = 0x00480000, uint32_t Vertical = 0x00480000 ); - void SetFrameCount ( uint16_t FrameCount = 1 ); - void SetCompressorName ( std::string CompressorName = ""); - void SetDepth ( uint16_t Depth = 0x0018 ); - private: - Box * Container; - - void SetReserved( ); - void SetDefaults( ); -};//Box_ftyp Class - -Box_avcC::Box_avcC( ) { - Container = new Box( 0x61766343 ); - SetReserved(); - SetDefaults(); -} - -Box_avcC::~Box_avcC() { - delete Container; -} - -Box * Box_avcC::GetBox() { - return Container; -} - -void Box_avcC::SetDataReferenceIndex( uint16_t DataReferenceIndex ) { - Container->SetPayload((uint32_t)2,Box::uint16_to_uint8( DataReferenceIndex ),6); -} - -void Box_avcC::SetWidth( uint16_t Width ) { - Container->SetPayload((uint32_t)2,Box::uint16_to_uint8( Width ),24); -} - -void Box_avcC::SetHeight( uint16_t Height ) { - Container->SetPayload((uint32_t)2,Box::uint16_to_uint8( Height ),26); -} - -void Box_avcC::SetResolution ( uint32_t Horizontal, uint32_t Vertical ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8( Vertical ),32); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8( Horizontal ),28); -} - -void Box_avcC::SetFrameCount ( uint16_t FrameCount ) { - Container->SetPayload((uint32_t)2,Box::uint16_to_uint8( FrameCount ),40); -} - -void Box_avcC::SetCompressorName ( std::string CompressorName ) { - uint8_t * Printable = new uint8_t[1]; - Printable[0] = std::min( (unsigned int)31, (unsigned int)CompressorName.size() ); - Container->SetPayload((uint32_t)Printable[0],(uint8_t*)CompressorName.c_str(),43); - Container->SetPayload((uint32_t)1, Printable ,42); -} - -void Box_avcC::SetDepth ( uint16_t Depth ) { - Container->SetPayload((uint32_t)2,Box::uint16_to_uint8( Depth ),74); -} - -void Box_avcC::SetReserved( ) { - Container->SetPayload((uint32_t)2,Box::uint16_to_uint8( (uint16_t)-1 ),76); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),36); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),20); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),16); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),12); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),8); - Container->SetPayload((uint32_t)4,Box::uint16_to_uint8(0),4); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0)); -} - -void Box_avcC::SetDefaults( ) { - SetWidth( ); - SetHeight( ); - SetDepth ( ); - SetFrameCount ( ); - SetResolution ( ); -} diff --git a/util/MP4/box_dinf.cpp b/util/MP4/box_dinf.cpp deleted file mode 100644 index d8d4d243..00000000 --- a/util/MP4/box_dinf.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include "box.cpp" -#include -#include - -class Box_dinf { - public: - Box_dinf(); - ~Box_dinf(); - Box * GetBox(); - void AddContent( Box * newcontent ); - void WriteContent( ); - private: - Box * Container; - - Box * Content; -};//Box_ftyp Class - -Box_dinf::Box_dinf( ) { - Container = new Box( 0x64696E66 ); -} - -Box_dinf::~Box_dinf() { - delete Container; -} - -Box * Box_dinf::GetBox() { - return Container; -} - -void Box_dinf::AddContent( Box * newcontent ) { - if(Content) { - delete Content; - Content = NULL; - } - Content = newcontent; -} - -void Box_dinf::WriteContent( ) { - Container->ResetPayload( ); - std::string serializedbox = ""; - serializedbox.append((char*)Content->GetBoxedData(),Content->GetBoxedDataSize()); - Container->SetPayload((uint32_t)serializedbox.size(),(uint8_t*)serializedbox.c_str()); -} diff --git a/util/MP4/box_dref.cpp b/util/MP4/box_dref.cpp deleted file mode 100644 index cc6a2f5a..00000000 --- a/util/MP4/box_dref.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "box.cpp" -#include -#include - -class Box_dref { - public: - Box_dref(); - ~Box_dref(); - Box * GetBox(); - void AddContent( Box * newcontent, uint32_t offset = 0 ); - void WriteContent( ); - private: - Box * Container; - - void SetReserved( ); - std::vector Content; -};//Box_ftyp Class - -Box_dref::Box_dref( ) { - Container = new Box( 0x64726566 ); - SetReserved( ); -} - -Box_dref::~Box_dref() { - delete Container; -} - -Box * Box_dref::GetBox() { - return Container; -} - -void Box_dref::AddContent( Box * newcontent, uint32_t offset ) { - if( offset >= Content.size() ) { - Content.resize(offset+1); - } - if( Content[offset] ) { - delete Content[offset]; - } - Content[offset] = newcontent; -} - -void Box_dref::WriteContent( ) { - Container->ResetPayload( ); - Box * current; - std::string serializedbox = ""; - for( uint32_t i = 0; i < Content.size(); i++ ) { - current=Content[i]; - if( current ) { - serializedbox.append((char*)current->GetBoxedData(),current->GetBoxedDataSize()); - } - } - Container->SetPayload((uint32_t)serializedbox.size(),(uint8_t*)serializedbox.c_str(),4); - SetReserved( ); -} - -void Box_dref::SetReserved( ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0)); -} diff --git a/util/MP4/box_esds.cpp b/util/MP4/box_esds.cpp deleted file mode 100644 index 7253f07d..00000000 --- a/util/MP4/box_esds.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include "box.cpp" -#include - -class Box_esds { - public: - Box_esds( ); - ~Box_esds(); - Box * GetBox(); - void SetDataReferenceIndex( uint16_t DataReferenceIndex = 1); - void SetChannelCount( uint16_t Count = 2 ); - void SetSampleSize( uint16_t Size = 16 ); - private: - Box * Container; - - void SetReserved( ); - void SetDefaults( ); -};//Box_ftyp Class - -Box_esds::Box_esds( ) { - Container = new Box( 0x65736473 ); - SetReserved(); - SetDefaults(); -} - -Box_esds::~Box_esds() { - delete Container; -} - -Box * Box_esds::GetBox() { - return Container; -} - -void Box_esds::SetDataReferenceIndex( uint16_t DataReferenceIndex ) { - Container->SetPayload((uint32_t)2,Box::uint16_to_uint8( DataReferenceIndex ),6); -} - -void Box_esds::SetChannelCount( uint16_t Count ) { - Container->SetPayload((uint32_t)2,Box::uint16_to_uint8( Count ),16); -} - -void Box_esds::SetSampleSize( uint16_t Size ) { - Container->SetPayload((uint32_t)2,Box::uint16_to_uint8( Size ),18); -} - -void Box_esds::SetReserved( ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8( 0 ),20); - Container->SetPayload((uint32_t)2,Box::uint16_to_uint8( 0 ),4); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8( 0 )); -} - -void Box_esds::SetDefaults( ) { - SetSampleSize( ); - SetChannelCount( ); - SetDataReferenceIndex( ); -} diff --git a/util/MP4/box_ftyp.cpp b/util/MP4/box_ftyp.cpp deleted file mode 100644 index 97ba9c3a..00000000 --- a/util/MP4/box_ftyp.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "box.cpp" - -class Box_ftyp { - public: - Box_ftyp( ); - ~Box_ftyp(); - Box * GetBox(); - void SetMajorBrand( uint32_t MajorBrand = 0x66347620 ); - void SetMinorBrand( uint32_t MinorBrand = 0x1 ); - private: - void SetDefaults( ); - Box * Container; -};//Box_ftyp Class - -Box_ftyp::Box_ftyp( ) { - Container = new Box( 0x66747970 ); - SetDefaults( ); -} - -Box_ftyp::~Box_ftyp() { - delete Container; -} - -Box * Box_ftyp::GetBox() { - return Container; -} - -void Box_ftyp::SetMajorBrand( uint32_t MajorBrand ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(MajorBrand)); -} - -void Box_ftyp::SetMinorBrand( uint32_t MinorBrand ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(MinorBrand),4); -} - -void Box_ftyp::SetDefaults( ) { - SetMinorBrand( ); - SetMajorBrand( ); -} diff --git a/util/MP4/box_hdlr.cpp b/util/MP4/box_hdlr.cpp deleted file mode 100644 index 5926740c..00000000 --- a/util/MP4/box_hdlr.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include "box.cpp" -#include - -class Box_hdlr { - public: - Box_hdlr( ); - ~Box_hdlr(); - Box * GetBox(); - void SetHandlerType( uint32_t HandlerType = 0 ); - void SetName ( std::string Name = "" ); - private: - Box * Container; - void SetReserved( ); - void SetDefaults( ); - uint32_t CurrentHandlerType; -};//Box_ftyp Class - -Box_hdlr::Box_hdlr( ) { - Container = new Box( 0x68646C72 ); - CurrentHandlerType = 0; - SetReserved(); -} - -Box_hdlr::~Box_hdlr() { - delete Container; -} - -Box * Box_hdlr::GetBox() { - return Container; -} - -void Box_hdlr::SetReserved( ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),20); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),16); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),12); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),4); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0)); -} - -void Box_hdlr::SetHandlerType( uint32_t HandlerType ) { - if( HandlerType != 0 ) { - CurrentHandlerType = HandlerType; - } - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(CurrentHandlerType),8); -} - -void Box_hdlr::SetName ( std::string Name ) { - char * tmp = new char[Name.size()+1]; - strcpy(tmp,Name.c_str()); - Container->ResetPayload(); - SetReserved(); - SetHandlerType(0); - Container->SetPayload((uint32_t)strlen(tmp)+1,(uint8_t*)tmp,24); -} - -void Box_hdlr::SetDefaults( ) { - SetName( ); - SetHandlerType( ); -} diff --git a/util/MP4/box_hmhd.cpp b/util/MP4/box_hmhd.cpp deleted file mode 100644 index 8733b822..00000000 --- a/util/MP4/box_hmhd.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "box.cpp" - -class Box_hmhd { - public: - Box_hmhd( ); - ~Box_hmhd(); - Box * GetBox(); - void SetMaxPDUSize( uint16_t Size = 0 ); - void SetAvgPDUSize( uint16_t Size = 0 ); - void SetMaxBitRate( uint32_t Rate = 0 ); - void SetAvgBitRate( uint32_t Rate = 0 ); - private: - Box * Container; - void SetReserved( ); - void SetDefaults( ); -};//Box_ftyp Class - -Box_hmhd::Box_hmhd( ) { - Container = new Box( 0x686D6864 ); - SetDefaults(); - SetReserved(); -} - -Box_hmhd::~Box_hmhd() { - delete Container; -} - -Box * Box_hmhd::GetBox() { - return Container; -} - -void Box_hmhd::SetMaxPDUSize( uint16_t Size ) { - Container->SetPayload((uint32_t)2,Box::uint16_to_uint8(Size),4); -} - -void Box_hmhd::SetAvgPDUSize( uint16_t Size ) { - Container->SetPayload((uint32_t)2,Box::uint16_to_uint8(Size),6); -} - -void Box_hmhd::SetMaxBitRate( uint32_t Rate ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Rate),8); -} - -void Box_hmhd::SetAvgBitRate( uint32_t Rate ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Rate),12); -} - -void Box_hmhd::SetReserved( ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),16); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0)); -} -void Box_hmhd::SetDefaults( ) { - SetAvgBitRate( ); - SetMaxBitRate( ); - SetAvgPDUSize( ); - SetMaxPDUSize( ); -} diff --git a/util/MP4/box_includes.h b/util/MP4/box_includes.h deleted file mode 100644 index 2b796702..00000000 --- a/util/MP4/box_includes.h +++ /dev/null @@ -1,37 +0,0 @@ -#include "box_abst.cpp" -#include "box_afra.cpp" -#include "box_afrt.cpp" -#include "box_amhp.cpp" -#include "box_asrt.cpp" -#include "box_avcC.cpp" -#include "box_dinf.cpp" -#include "box_dref.cpp" -#include "box_esds.cpp" -#include "box_ftyp.cpp" -#include "box_hdlr.cpp" -#include "box_hmhd.cpp" -#include "box_mdat.cpp" -#include "box_mdhd.cpp" -#include "box_mdia.cpp" -#include "box_mfhd.cpp" -#include "box_minf.cpp" -#include "box_moof.cpp" -#include "box_moov.cpp" -#include "box_mvex.cpp" -#include "box_mvhd.cpp" -#include "box_nmhd.cpp" -#include "box_rtmp.cpp" -#include "box_smhd.cpp" -#include "box_stbl.cpp" -#include "box_stco.cpp" -#include "box_stsc.cpp" -#include "box_stsd.cpp" -#include "box_stts.cpp" -#include "box_tfhd.cpp" -#include "box_tkhd.cpp" -#include "box_traf.cpp" -#include "box_trak.cpp" -#include "box_trex.cpp" -#include "box_trun.cpp" -#include "box_url.cpp" -#include "box_vmhd.cpp" diff --git a/util/MP4/box_mdat.cpp b/util/MP4/box_mdat.cpp deleted file mode 100644 index 258585aa..00000000 --- a/util/MP4/box_mdat.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "box.cpp" -#include -#include - -class Box_mdat { - public: - Box_mdat(); - ~Box_mdat(); - Box * GetBox(); - void SetContent( uint8_t * NewData, uint32_t DataLength , uint32_t offset = 0 ); - private: - Box * Container; -};//Box_ftyp Class - -Box_mdat::Box_mdat( ) { - Container = new Box( 0x6D646174 ); -} - -Box_mdat::~Box_mdat() { - delete Container; -} - -Box * Box_mdat::GetBox() { - return Container; -} - -void Box_mdat::SetContent( uint8_t * NewData, uint32_t DataLength , uint32_t Offset ) { - Container->SetPayload(DataLength,NewData,Offset); -} diff --git a/util/MP4/box_mdhd.cpp b/util/MP4/box_mdhd.cpp deleted file mode 100644 index ed143270..00000000 --- a/util/MP4/box_mdhd.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include "box.cpp" -#include - -#define SECONDS_DIFFERENCE 2082844800 - -class Box_mdhd { - public: - Box_mdhd( ); - ~Box_mdhd(); - Box * GetBox(); - void SetCreationTime( uint32_t TimeStamp = 0 ); - void SetModificationTime( uint32_t TimeStamp = 0 ); - void SetTimeScale( uint32_t TimeUnits = 0 ); - void SetDurationTime( uint32_t TimeUnits = 0 ); - void SetLanguage( uint8_t Firstchar = 'n', uint8_t Secondchar = 'l', uint8_t Thirdchar = 'd' ); - private: - void SetReserved(); - void SetDefaults(); - Box * Container; -};//Box_ftyp Class - -Box_mdhd::Box_mdhd( ) { - Container = new Box( 0x6D646864 ); -} - -Box_mdhd::~Box_mdhd() { - delete Container; -} - -Box * Box_mdhd::GetBox() { - return Container; -} - -void Box_mdhd::SetCreationTime( uint32_t TimeStamp ) { - uint32_t CreationTime; - if(!TimeStamp) { - CreationTime = time(NULL) + SECONDS_DIFFERENCE; - } else { - CreationTime = TimeStamp; - } - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(CreationTime),4); -} - -void Box_mdhd::SetModificationTime( uint32_t TimeStamp ) { - uint32_t ModificationTime; - if(!TimeStamp) { - ModificationTime = time(NULL) + SECONDS_DIFFERENCE; - } else { - ModificationTime = TimeStamp; - } - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(ModificationTime),8); -} - -void Box_mdhd::SetTimeScale( uint32_t TimeUnits ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(TimeUnits),12); -} - -void Box_mdhd::SetDurationTime( uint32_t TimeUnits ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(TimeUnits),16); -} - -void Box_mdhd::SetLanguage( uint8_t Firstchar, uint8_t Secondchar, uint8_t Thirdchar ) { - uint8_t FirstByte = 0; - uint8_t SecondByte = 0; - Firstchar -= 0x60; - Secondchar -= 0x60; - Thirdchar -= 0x60; - FirstByte += (Firstchar << 2); - FirstByte += (Secondchar >> 3); - SecondByte += (Secondchar << 5); - SecondByte += Thirdchar; - - Container->SetPayload((uint32_t)1,&SecondByte,21); - Container->SetPayload((uint32_t)1,&FirstByte,20); -} - -void Box_mdhd::SetReserved() { - Container->SetPayload((uint32_t)2,Box::uint16_to_uint8(0),22); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0)); -} - -void Box_mdhd::SetDefaults() { - SetLanguage(); - SetDurationTime(); - SetTimeScale(); - SetModificationTime(); - SetCreationTime(); -} diff --git a/util/MP4/box_mdia.cpp b/util/MP4/box_mdia.cpp deleted file mode 100644 index 553137a7..00000000 --- a/util/MP4/box_mdia.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "box.cpp" -#include -#include - -class Box_mdia { - public: - Box_mdia(); - ~Box_mdia(); - Box * GetBox(); - void AddContent( Box * newcontent, uint32_t offset = 0 ); - void WriteContent( ); - private: - Box * Container; - - std::vector Content; -};//Box_ftyp Class - -Box_mdia::Box_mdia( ) { - Container = new Box( 0x6D646961 ); -} - -Box_mdia::~Box_mdia() { - delete Container; -} - -Box * Box_mdia::GetBox() { - return Container; -} - -void Box_mdia::AddContent( Box * newcontent, uint32_t offset ) { - if( offset >= Content.size() ) { - Content.resize(offset+1); - } - if( Content[offset] ) { - delete Content[offset]; - } - Content[offset] = newcontent; -} - -void Box_mdia::WriteContent( ) { - Container->ResetPayload( ); - Box * current; - std::string serializedbox = ""; - for( uint32_t i = 0; i < Content.size(); i++ ) { - current=Content[i]; - if( current ) { - serializedbox.append((char*)current->GetBoxedData(),current->GetBoxedDataSize()); - } - } - Container->SetPayload((uint32_t)serializedbox.size(),(uint8_t*)serializedbox.c_str()); -} diff --git a/util/MP4/box_mfhd.cpp b/util/MP4/box_mfhd.cpp deleted file mode 100644 index 2680ab0a..00000000 --- a/util/MP4/box_mfhd.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "box.cpp" - -class Box_mfhd { - public: - Box_mfhd( ); - ~Box_mfhd(); - Box * GetBox(); - void SetSequenceNumber( uint32_t SequenceNumber = 1 ); - private: - void SetDefaults( ); - void SetReserved( ); - Box * Container; -};//Box_ftyp Class - -Box_mfhd::Box_mfhd( ) { - Container = new Box( 0x6D666864 ); - SetDefaults( ); - SetReserved( ); -} - -Box_mfhd::~Box_mfhd() { - delete Container; -} - -Box * Box_mfhd::GetBox() { - return Container; -} - -void Box_mfhd::SetDefaults( ) { - SetSequenceNumber( ); -} - -void Box_mfhd::SetReserved( ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0)); -} - -void Box_mfhd::SetSequenceNumber( uint32_t SequenceNumber ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(SequenceNumber),4); -} diff --git a/util/MP4/box_minf.cpp b/util/MP4/box_minf.cpp deleted file mode 100644 index ebde6c56..00000000 --- a/util/MP4/box_minf.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "box.cpp" -#include -#include - -class Box_minf { - public: - Box_minf(); - ~Box_minf(); - Box * GetBox(); - void AddContent( Box * newcontent, uint32_t offset = 0 ); - void WriteContent( ); - private: - Box * Container; - - std::vector Content; -};//Box_ftyp Class - -Box_minf::Box_minf( ) { - Container = new Box( 0x6D696E66 ); -} - -Box_minf::~Box_minf() { - delete Container; -} - -Box * Box_minf::GetBox() { - return Container; -} - -void Box_minf::AddContent( Box * newcontent, uint32_t offset ) { - if( offset >= Content.size() ) { - Content.resize(offset+1); - } - if( Content[offset] ) { - delete Content[offset]; - } - Content[offset] = newcontent; -} - -void Box_minf::WriteContent( ) { - Container->ResetPayload( ); - Box * current; - std::string serializedbox = ""; - for( uint32_t i = 0; i < Content.size(); i++ ) { - current=Content[i]; - if( current ) { - serializedbox.append((char*)current->GetBoxedData(),current->GetBoxedDataSize()); - } - } - Container->SetPayload((uint32_t)serializedbox.size(),(uint8_t*)serializedbox.c_str()); -} diff --git a/util/MP4/box_moof.cpp b/util/MP4/box_moof.cpp deleted file mode 100644 index 1b18a9e5..00000000 --- a/util/MP4/box_moof.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "box.cpp" -#include -#include - -class Box_moof { - public: - Box_moof(); - ~Box_moof(); - Box * GetBox(); - void AddContent( Box * newcontent, uint32_t offset = 0 ); - void WriteContent( ); - private: - Box * Container; - - std::vector Content; -};//Box_ftyp Class - -Box_moof::Box_moof( ) { - Container = new Box( 0x6D6F6F66 ); -} - -Box_moof::~Box_moof() { - delete Container; -} - -Box * Box_moof::GetBox() { - return Container; -} - -void Box_moof::AddContent( Box * newcontent, uint32_t offset ) { - if( offset >= Content.size() ) { - Content.resize(offset+1); - } - if( Content[offset] ) { - delete Content[offset]; - } - Content[offset] = newcontent; -} - -void Box_moof::WriteContent( ) { - Container->ResetPayload( ); - Box * current; - std::string serializedbox = ""; - for( uint32_t i = 0; i < Content.size(); i++ ) { - current=Content[i]; - if( current ) { - serializedbox.append((char*)current->GetBoxedData(),current->GetBoxedDataSize()); - } - } - Container->SetPayload((uint32_t)serializedbox.size(),(uint8_t*)serializedbox.c_str()); -} diff --git a/util/MP4/box_moov.cpp b/util/MP4/box_moov.cpp deleted file mode 100644 index 84542977..00000000 --- a/util/MP4/box_moov.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "box.cpp" -#include -#include - -class Box_moov { - public: - Box_moov(); - ~Box_moov(); - Box * GetBox(); - void AddContent( Box * newcontent, uint32_t offset = 0 ); - void WriteContent( ); - private: - Box * Container; - - std::vector Content; -};//Box_ftyp Class - -Box_moov::Box_moov( ) { - Container = new Box( 0x6D6F6F76 ); -} - -Box_moov::~Box_moov() { - delete Container; -} - -Box * Box_moov::GetBox() { - return Container; -} - -void Box_moov::AddContent( Box * newcontent, uint32_t offset ) { - if( offset >= Content.size() ) { - Content.resize(offset+1); - } - if( Content[offset] ) { - delete Content[offset]; - } - Content[offset] = newcontent; -} - -void Box_moov::WriteContent( ) { - Container->ResetPayload( ); - Box * current; - std::string serializedbox = ""; - for( uint32_t i = 0; i < Content.size(); i++ ) { - current=Content[i]; - if( current ) { - serializedbox.append((char*)current->GetBoxedData(),current->GetBoxedDataSize()); - } - } - Container->SetPayload((uint32_t)serializedbox.size(),(uint8_t*)serializedbox.c_str()); -} diff --git a/util/MP4/box_mvex.cpp b/util/MP4/box_mvex.cpp deleted file mode 100644 index 8d6725ac..00000000 --- a/util/MP4/box_mvex.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "box.cpp" -#include -#include - -class Box_mvex { - public: - Box_mvex(); - ~Box_mvex(); - Box * GetBox(); - void AddContent( Box * newcontent, uint32_t offset = 0 ); - void WriteContent( ); - private: - Box * Container; - - std::vector Content; -};//Box_ftyp Class - -Box_mvex::Box_mvex( ) { - Container = new Box( 0x6D866578 ); -} - -Box_mvex::~Box_mvex() { - delete Container; -} - -Box * Box_mvex::GetBox() { - return Container; -} - -void Box_mvex::AddContent( Box * newcontent, uint32_t offset ) { - if( offset >= Content.size() ) { - Content.resize(offset+1); - } - if( Content[offset] ) { - delete Content[offset]; - } - Content[offset] = newcontent; -} - -void Box_mvex::WriteContent( ) { - Container->ResetPayload( ); - Box * current; - std::string serializedbox = ""; - for( uint32_t i = 0; i < Content.size(); i++ ) { - current=Content[i]; - if( current ) { - serializedbox.append((char*)current->GetBoxedData(),current->GetBoxedDataSize()); - } - } - Container->SetPayload((uint32_t)serializedbox.size(),(uint8_t*)serializedbox.c_str()); -} diff --git a/util/MP4/box_mvhd.cpp b/util/MP4/box_mvhd.cpp deleted file mode 100644 index 8055abe5..00000000 --- a/util/MP4/box_mvhd.cpp +++ /dev/null @@ -1,113 +0,0 @@ -#include "box.cpp" -#include - -#define SECONDS_DIFFERENCE 2082844800 - -class Box_mvhd { - public: - Box_mvhd( ); - ~Box_mvhd(); - Box * GetBox(); - void SetCreationTime( uint32_t TimeStamp = 0 ); - void SetModificationTime( uint32_t TimeStamp = 0 ); - void SetTimeScale( uint32_t TimeUnits = 1 ); - void SetDurationTime( uint32_t TimeUnits = 0 ); - void SetRate( uint32_t Rate = 0x00010000 ); - void SetVolume( uint16_t Volume = 0x0100 ); - void SetNextTrackID( uint32_t TrackID = 0xFFFFFFFF ); - private: - void SetReserved(); - void SetDefaults(); - Box * Container; - -};//Box_ftyp Class - -Box_mvhd::Box_mvhd( ) { - Container = new Box( 0x6D766864 ); - SetDefaults(); - SetReserved(); -} - -Box_mvhd::~Box_mvhd() { - delete Container; -} - -Box * Box_mvhd::GetBox() { - return Container; -} - -void Box_mvhd::SetCreationTime( uint32_t TimeStamp ) { - uint32_t CreationTime; - if(!TimeStamp) { - CreationTime = time(NULL) + SECONDS_DIFFERENCE; - } else { - CreationTime = TimeStamp; - } - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(CreationTime),4); -} - -void Box_mvhd::SetModificationTime( uint32_t TimeStamp ) { - uint32_t ModificationTime; - if(!TimeStamp) { - ModificationTime = time(NULL) + SECONDS_DIFFERENCE; - } else { - ModificationTime = TimeStamp; - } - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(ModificationTime),8); -} - -void Box_mvhd::SetTimeScale( uint32_t TimeUnits ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(TimeUnits),12); -} - -void Box_mvhd::SetDurationTime( uint32_t TimeUnits ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(TimeUnits),16); -} - -void Box_mvhd::SetRate( uint32_t Rate ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Rate),20); -} - -void Box_mvhd::SetVolume( uint16_t Volume ) { - Container->SetPayload((uint32_t)4,Box::uint16_to_uint8(Volume),24); -} - -void Box_mvhd::SetNextTrackID( uint32_t TrackID ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(TrackID),92); -} - -void Box_mvhd::SetReserved() { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),88); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),84); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),80); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),76); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),72); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),68); - - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0x40000000),64); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),60); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),56); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),52); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0x00010000),48); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),44); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),40); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),36); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0x00010000),32); - - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),28); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),24); - Container->SetPayload((uint32_t)4,Box::uint16_to_uint8(0),22); - - Container->SetPayload((uint32_t)4,Box::uint16_to_uint8(0)); -} - -void Box_mvhd::SetDefaults() { - SetCreationTime(); - SetModificationTime(); - SetDurationTime(); - SetNextTrackID(); - SetRate(); - SetVolume(); - SetTimeScale(); -} - diff --git a/util/MP4/box_nmhd.cpp b/util/MP4/box_nmhd.cpp deleted file mode 100644 index d874d1ed..00000000 --- a/util/MP4/box_nmhd.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "box.cpp" - -class Box_nmhd { - public: - Box_nmhd( ); - ~Box_nmhd(); - Box * GetBox(); - private: - Box * Container; - void SetReserved( ); -};//Box_ftyp Class - -Box_nmhd::Box_nmhd( ) { - Container = new Box( 0x6E6D6864 ); - SetReserved(); -} - -Box_nmhd::~Box_nmhd() { - delete Container; -} - -Box * Box_nmhd::GetBox() { - return Container; -} - -void Box_nmhd::SetReserved( ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0)); -} diff --git a/util/MP4/box_rtmp.cpp b/util/MP4/box_rtmp.cpp deleted file mode 100644 index 980c7ef3..00000000 --- a/util/MP4/box_rtmp.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include "box.cpp" -#include - -class Box_rtmp { - public: - Box_rtmp( ); - ~Box_rtmp(); - Box * GetBox(); - void SetDataReferenceIndex( uint16_t NewIndex = 0 ); - void SetHintTrackVersion( uint16_t NewVersion = 1 ); - void SetHighestCompatibleVersion( uint16_t NewVersion = 1 ); - void SetMaxPacketSize( uint16_t NewSize = 0xFFFF ); - void AddContent( Box * newcontent ); - void WriteContent( ); - private: - void SetReserved( ); - void SetDefaults( ); - uint16_t CurrentReferenceIndex; - uint16_t CurrentHintTrackVersion; - uint16_t CurrentHighestCompatibleVersion; - uint16_t CurrentMaxPacketSize; - - Box * Container; - Box * Content; -};//Box_ftyp Class - -Box_rtmp::Box_rtmp( ) { - Container = new Box( 0x72746D70 ); - SetReserved(); - SetDefaults(); -} - -Box_rtmp::~Box_rtmp() { - delete Container; -} - -Box * Box_rtmp::GetBox() { - return Container; -} - -void Box_rtmp::SetReserved( ) { - Container->SetPayload((uint32_t)2,Box::uint16_to_uint8(CurrentMaxPacketSize),12); - Container->SetPayload((uint32_t)2,Box::uint16_to_uint8(CurrentHighestCompatibleVersion),10); - Container->SetPayload((uint32_t)2,Box::uint16_to_uint8(CurrentHintTrackVersion),8); - Container->SetPayload((uint32_t)2,Box::uint16_to_uint8(CurrentReferenceIndex),6); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),2); - Container->SetPayload((uint32_t)2,Box::uint16_to_uint8(0)); -} - -void Box_rtmp::SetDefaults( ) { - SetDataReferenceIndex( ); - SetHintTrackVersion( ); - SetHighestCompatibleVersion( ); - SetMaxPacketSize( ); -} - -void Box_rtmp::SetDataReferenceIndex( uint16_t NewIndex ) { - CurrentReferenceIndex = NewIndex; - Container->SetPayload((uint32_t)2,Box::uint16_to_uint8(NewIndex),6); -} -void Box_rtmp::SetHintTrackVersion( uint16_t NewVersion ) { - CurrentHintTrackVersion = NewVersion; - Container->SetPayload((uint32_t)2,Box::uint16_to_uint8(NewVersion),8); -} -void Box_rtmp::SetHighestCompatibleVersion( uint16_t NewVersion ) { - CurrentHighestCompatibleVersion = NewVersion; - Container->SetPayload((uint32_t)2,Box::uint16_to_uint8(NewVersion),10); -} - -void Box_rtmp::SetMaxPacketSize( uint16_t NewSize ) { - CurrentMaxPacketSize = NewSize; - Container->SetPayload((uint32_t)2,Box::uint16_to_uint8(NewSize),12); -} - -void Box_rtmp::AddContent( Box * newcontent ) { - if(Content) { - delete Content; - Content = NULL; - } - Content = newcontent; -} - -void Box_rtmp::WriteContent( ) { - Container->ResetPayload( ); - SetReserved( ); - std::string serializedbox = ""; - serializedbox.append((char*)Content->GetBoxedData(),Content->GetBoxedDataSize()); - Container->SetPayload((uint32_t)serializedbox.size(),(uint8_t*)serializedbox.c_str(),14); -} diff --git a/util/MP4/box_smhd.cpp b/util/MP4/box_smhd.cpp deleted file mode 100644 index 6b6f9d9f..00000000 --- a/util/MP4/box_smhd.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "box.cpp" - -class Box_smhd { - public: - Box_smhd( ); - ~Box_smhd(); - Box * GetBox(); - private: - Box * Container; - void SetReserved( ); -};//Box_ftyp Class - -Box_smhd::Box_smhd( ) { - Container = new Box( 0x736D6864 ); - SetReserved(); -} - -Box_smhd::~Box_smhd() { - delete Container; -} - -Box * Box_smhd::GetBox() { - return Container; -} - -void Box_smhd::SetReserved( ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),4); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0)); -} diff --git a/util/MP4/box_stbl.cpp b/util/MP4/box_stbl.cpp deleted file mode 100644 index 44a8d129..00000000 --- a/util/MP4/box_stbl.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "box.cpp" -#include -#include - -class Box_stbl { - public: - Box_stbl(); - ~Box_stbl(); - Box * GetBox(); - void AddContent( Box * newcontent, uint32_t offset = 0 ); - void WriteContent( ); - private: - Box * Container; - - std::vector Content; -};//Box_ftyp Class - -Box_stbl::Box_stbl( ) { - Container = new Box( 0x7374626C ); -} - -Box_stbl::~Box_stbl() { - delete Container; -} - -Box * Box_stbl::GetBox() { - return Container; -} - -void Box_stbl::AddContent( Box * newcontent, uint32_t offset ) { - if( offset >= Content.size() ) { - Content.resize(offset+1); - } - if( Content[offset] ) { - delete Content[offset]; - } - Content[offset] = newcontent; -} - -void Box_stbl::WriteContent( ) { - Container->ResetPayload( ); - Box * current; - std::string serializedbox = ""; - for( uint32_t i = 0; i < Content.size(); i++ ) { - current=Content[i]; - if( current ) { - serializedbox.append((char*)current->GetBoxedData(),current->GetBoxedDataSize()); - } - } - Container->SetPayload((uint32_t)serializedbox.size(),(uint8_t*)serializedbox.c_str()); -} diff --git a/util/MP4/box_stco.cpp b/util/MP4/box_stco.cpp deleted file mode 100644 index 434cede8..00000000 --- a/util/MP4/box_stco.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "box.cpp" -#include -#include - -class Box_stco { - public: - Box_stco( ); - ~Box_stco(); - Box * GetBox(); - void AddOffset( uint32_t DataOffset, uint32_t Offset = 0 ); - void SetOffsets( std::vector NewOffsets ); - void WriteContent( ); - private: - Box * Container; - - void SetReserved( ); - std::vector Offsets; -};//Box_ftyp Class - -Box_stco::Box_stco( ) { - Container = new Box( 0x7374636F ); - SetReserved(); -} - -Box_stco::~Box_stco() { - delete Container; -} - -Box * Box_stco::GetBox() { - return Container; -} - -void Box_stco::SetReserved( ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0)); -} - -void Box_stco::AddOffset( uint32_t DataOffset, uint32_t Offset ) { - if(Offset >= Offsets.size()) { - Offsets.resize(Offset+1); - } - Offsets[Offset] = DataOffset; -} - - -void Box_stco::WriteContent( ) { - Container->ResetPayload(); - SetReserved( ); - if(!Offsets.empty()) { - for(int32_t i = Offsets.size() -1; i >= 0; i--) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Offsets[i]),(i*4)+8); - } - } - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Offsets.size()),4); -} - -void Box_stco::SetOffsets( std::vector NewOffsets ) { - Offsets = NewOffsets; -} diff --git a/util/MP4/box_stsc.cpp b/util/MP4/box_stsc.cpp deleted file mode 100644 index ea8edd33..00000000 --- a/util/MP4/box_stsc.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#include "box.cpp" -#include -#include - -struct stsc_record { - uint32_t FirstChunk; - uint32_t SamplesPerChunk; - uint32_t SampleDescIndex; -};//stsc_record - -class Box_stsc { - public: - Box_stsc( ); - ~Box_stsc(); - Box * GetBox(); - void SetReserved( ); - void AddEntry( uint32_t FirstChunk = 0, uint32_t SamplesPerChunk = 0, uint32_t SampleDescIndex = 0, uint32_t Offset = 0 ); - void WriteContent( ); - private: - Box * Container; - - std::vector Entries; -};//Box_ftyp Class - -Box_stsc::Box_stsc( ) { - Container = new Box( 0x73747363 ); - SetReserved(); -} - -Box_stsc::~Box_stsc() { - delete Container; -} - -Box * Box_stsc::GetBox() { - return Container; -} - -void Box_stsc::SetReserved( ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0)); -} - -void Box_stsc::AddEntry( uint32_t FirstChunk, uint32_t SamplesPerChunk, uint32_t SampleDescIndex, uint32_t Offset ) { - if(Offset >= Entries.size()) { - Entries.resize(Offset+1); - } - Entries[Offset].FirstChunk = FirstChunk; - Entries[Offset].SamplesPerChunk = SamplesPerChunk; - Entries[Offset].SampleDescIndex = SampleDescIndex; -} - - -void Box_stsc::WriteContent( ) { - Container->ResetPayload(); - SetReserved( ); - if(!Entries.empty()) { - for(int32_t i = Entries.size() -1; i >= 0; i--) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Entries[i].SampleDescIndex),(i*12)+16); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Entries[i].SamplesPerChunk),(i*12)+12); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Entries[i].FirstChunk),(i*12)+8); - } - } - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Entries.size()),4); -} diff --git a/util/MP4/box_stsd.cpp b/util/MP4/box_stsd.cpp deleted file mode 100644 index 233fa584..00000000 --- a/util/MP4/box_stsd.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include "box.cpp" -#include -#include - -class Box_stsd { - public: - Box_stsd( ); - ~Box_stsd(); - Box * GetBox(); - void AddContent( Box * newcontent, uint32_t offset = 0 ); - void WriteContent(); - private: - Box * Container; - - void SetReserved(); - std::vector Content; -};//Box_ftyp Class - -Box_stsd::Box_stsd( ) { - Container = new Box( 0x73747364 ); - SetReserved(); -} - -Box_stsd::~Box_stsd() { - delete Container; -} - -Box * Box_stsd::GetBox() { - return Container; -} - -void Box_stsd::AddContent( Box * newcontent, uint32_t offset ) { - if( offset >= Content.size() ) { - Content.resize(offset+1); - } - if( Content[offset] ) { - delete Content[offset]; - } - Content[offset] = newcontent; -} - -void Box_stsd::SetReserved( ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8( 1 ),4); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8( 0 ),0); -} - -void Box_stsd::WriteContent( ) { - Container->ResetPayload( ); - SetReserved( ); - Box * current; - std::string serializedbox = ""; - for( uint32_t i = 0; i < Content.size(); i++ ) { - current=Content[i]; - if( current ) { - serializedbox.append((char*)current->GetBoxedData(),current->GetBoxedDataSize()); - } - } - Container->SetPayload((uint32_t)serializedbox.size(),(uint8_t*)serializedbox.c_str(),8); -} diff --git a/util/MP4/box_stts.cpp b/util/MP4/box_stts.cpp deleted file mode 100644 index e16c42c0..00000000 --- a/util/MP4/box_stts.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include "box.cpp" -#include -#include - -struct stts_record { - uint32_t SampleCount; - uint32_t SampleDelta; -};//stsc_record - -class Box_stts { - public: - Box_stts( ); - ~Box_stts(); - Box * GetBox(); - void SetReserved( ); - void AddEntry( uint32_t SampleCount, uint32_t SampleDelta, uint32_t Offset = 0 ); - void WriteContent( ); - private: - Box * Container; - - std::vector Entries; -};//Box_ftyp Class - -Box_stts::Box_stts( ) { - Container = new Box( 0x73747473 ); - SetReserved(); -} - -Box_stts::~Box_stts() { - delete Container; -} - -Box * Box_stts::GetBox() { - return Container; -} - -void Box_stts::SetReserved( ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0)); -} - -void Box_stts::AddEntry( uint32_t SampleCount, uint32_t SampleDelta, uint32_t Offset ) { - if(Offset >= Entries.size()) { - Entries.resize(Offset+1); - } - Entries[Offset].SampleCount = SampleCount; - Entries[Offset].SampleDelta = SampleDelta; -} - - -void Box_stts::WriteContent( ) { - Container->ResetPayload(); - SetReserved( ); - if(!Entries.empty()) { - for(int32_t i = Entries.size() -1; i >= 0; i--) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Entries[i].SampleDelta),(i*8)+12); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Entries[i].SampleCount),(i*8)+8); - } - } - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Entries.size()),4); -} diff --git a/util/MP4/box_tfhd.cpp b/util/MP4/box_tfhd.cpp deleted file mode 100644 index 57ed10dc..00000000 --- a/util/MP4/box_tfhd.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include "box.cpp" - -class Box_tfhd { - public: - Box_tfhd( ); - ~Box_tfhd(); - Box * GetBox(); - void SetTrackID( uint32_t TrackID = 0 ); - void SetBaseDataOffset( uint32_t Offset = 0 );//write as uint64_t - void SetSampleDescriptionIndex( uint32_t Index = 0 ); - void SetDefaultSampleDuration( uint32_t Duration = 0 ); - void SetDefaultSampleSize( uint32_t Size = 0 ); - void WriteContent( ); - private: - void SetDefaults( ); - uint32_t curTrackID; - uint32_t curBaseDataOffset; - uint32_t curSampleDescriptionIndex; - uint32_t curDefaultSampleDuration; - uint32_t curDefaultSampleSize; - Box * Container; -};//Box_ftyp Class - -Box_tfhd::Box_tfhd( ) { - Container = new Box( 0x74666864 ); - SetDefaults( ); -} - -Box_tfhd::~Box_tfhd() { - delete Container; -} - -Box * Box_tfhd::GetBox() { - return Container; -} - -void Box_tfhd::SetTrackID( uint32_t TrackID ) { - curTrackID = TrackID; -} - -void Box_tfhd::SetBaseDataOffset( uint32_t Offset ) { - curBaseDataOffset = Offset; -} - -void Box_tfhd::SetSampleDescriptionIndex( uint32_t Index ) { - curSampleDescriptionIndex = Index; -} - -void Box_tfhd::SetDefaultSampleDuration( uint32_t Duration ) { - curDefaultSampleDuration = Duration; -} - -void Box_tfhd::SetDefaultSampleSize( uint32_t Size ) { - curDefaultSampleSize = Size; -} - -void Box_tfhd::WriteContent( ) { - uint32_t curoffset; - uint32_t flags = 0 & ( curBaseDataOffset ? 0x1 : 0 ) & ( curSampleDescriptionIndex ? 0x2 : 0 ) & ( curDefaultSampleDuration ? 0x8 : 0 ) & ( curDefaultSampleSize ? 0x10 : 0 ); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(flags)); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(curTrackID),4); - curoffset = 8; - if( curBaseDataOffset ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),curoffset); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(curBaseDataOffset),curoffset+4); - curoffset += 8; - } - if( curSampleDescriptionIndex ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(curSampleDescriptionIndex),curoffset); - curoffset += 8; - } - if( curDefaultSampleDuration ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(curDefaultSampleDuration),curoffset); - curoffset += 8; - } - if( curDefaultSampleSize ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(curDefaultSampleSize),curoffset); - curoffset += 8; - } -} - -void Box_tfhd::SetDefaults( ) { - SetTrackID( ); - SetBaseDataOffset( ); - SetSampleDescriptionIndex( ); - SetDefaultSampleDuration( ); - SetDefaultSampleSize( ); -} diff --git a/util/MP4/box_tkhd.cpp b/util/MP4/box_tkhd.cpp deleted file mode 100644 index c8f5c1e7..00000000 --- a/util/MP4/box_tkhd.cpp +++ /dev/null @@ -1,117 +0,0 @@ -#include "box.cpp" -#include - -#define SECONDS_DIFFERENCE 2082844800 - -class Box_tkhd { - public: - Box_tkhd( ); - ~Box_tkhd(); - Box * GetBox(); - void SetCreationTime( uint32_t TimeStamp = 0 ); - void SetModificationTime( uint32_t TimeStamp = 0 ); - void SetDurationTime( uint32_t TimeUnits = 0 ); - void SetWidth( uint16_t Width = 0 ); - void SetHeight( uint16_t Height = 0 ); - void SetFlags( bool Bit0 = true, bool Bit1 = true, bool Bit2 = true ); - void SetVersion( uint32_t Version = 0 ); - void SetTrackID( uint32_t TrackID = 0 ); - private: - void SetReserved(); - void SetDefaults(); - Box * Container; - - uint32_t CurrentFlags; - uint32_t CurrentVersion; -};//Box_ftyp Class - -Box_tkhd::Box_tkhd( ) { - Container = new Box( 0x746B6864 ); - CurrentVersion = 0; - CurrentFlags = 0; -} - -Box_tkhd::~Box_tkhd() { - delete Container; -} - -Box * Box_tkhd::GetBox() { - return Container; -} - -void Box_tkhd::SetCreationTime( uint32_t TimeStamp ) { - uint32_t CreationTime; - if(!TimeStamp) { - CreationTime = time(NULL) + SECONDS_DIFFERENCE; - } else { - CreationTime = TimeStamp; - } - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(CreationTime),4); -} - -void Box_tkhd::SetModificationTime( uint32_t TimeStamp ) { - uint32_t ModificationTime; - if(!TimeStamp) { - ModificationTime = time(NULL) + SECONDS_DIFFERENCE; - } else { - ModificationTime = TimeStamp; - } - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(ModificationTime),8); -} - -void Box_tkhd::SetDurationTime( uint32_t TimeUnits ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(TimeUnits),16); -} - -void Box_tkhd::SetReserved() { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0x40000000),68); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),64); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),60); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),56); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0x10000),52); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),48); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),44); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),40); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0x10000),36); - Container->SetPayload((uint32_t)4,Box::uint16_to_uint8(0),34); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),28); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),24); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),20); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0),12); -} - -void Box_tkhd::SetVersion( uint32_t Version ) { - if ( Version >= 2 ) { return; } - CurrentVersion = Version; - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8((CurrentVersion<<24)&(CurrentFlags))); -} - -void Box_tkhd::SetFlags( bool Bit0, bool Bit1, bool Bit2 ) { - CurrentFlags = (( Bit0 ? 0x80 : 0 ) + ( Bit1 ? 0x40 : 0 ) + ( Bit2 ? 0x20 : 0 )) << 16 ; - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8((CurrentVersion<<24)&(CurrentFlags))); -} - -void Box_tkhd::SetTrackID( uint32_t TrackID ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(TrackID),12); -} - -void Box_tkhd::SetWidth( uint16_t Width ) { - uint32_t ResultWidth = ( Width << 16 ); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(ResultWidth),72); -} - -void Box_tkhd::SetHeight( uint16_t Height ) { - uint32_t ResultHeight = ( Height << 16 ); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(ResultHeight),76); -} - -void Box_tkhd::SetDefaults() { - SetHeight(); - SetWidth(); - SetCreationTime(); - SetModificationTime(); - SetDurationTime(); - SetFlags(); - SetVersion(); - SetTrackID(); -} diff --git a/util/MP4/box_traf.cpp b/util/MP4/box_traf.cpp deleted file mode 100644 index fb794578..00000000 --- a/util/MP4/box_traf.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "box.cpp" -#include -#include - -class Box_traf { - public: - Box_traf(); - ~Box_traf(); - Box * GetBox(); - void AddContent( Box * newcontent, uint32_t offset = 0 ); - void WriteContent( ); - private: - Box * Container; - - std::vector Content; -};//Box_ftyp Class - -Box_traf::Box_traf( ) { - Container = new Box( 0x74726166 ); -} - -Box_traf::~Box_traf() { - delete Container; -} - -Box * Box_traf::GetBox() { - return Container; -} - -void Box_traf::AddContent( Box * newcontent, uint32_t offset ) { - if( offset >= Content.size() ) { - Content.resize(offset+1); - } - if( Content[offset] ) { - delete Content[offset]; - } - Content[offset] = newcontent; -} - -void Box_traf::WriteContent( ) { - Container->ResetPayload( ); - Box * current; - std::string serializedbox = ""; - for( uint32_t i = 0; i < Content.size(); i++ ) { - current=Content[i]; - if( current ) { - serializedbox.append((char*)current->GetBoxedData(),current->GetBoxedDataSize()); - } - } - Container->SetPayload((uint32_t)serializedbox.size(),(uint8_t*)serializedbox.c_str()); -} diff --git a/util/MP4/box_trak.cpp b/util/MP4/box_trak.cpp deleted file mode 100644 index b33bb5bd..00000000 --- a/util/MP4/box_trak.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "box.cpp" -#include -#include - -class Box_trak { - public: - Box_trak(); - ~Box_trak(); - Box * GetBox(); - void AddContent( Box * newcontent, uint32_t offset = 0 ); - void WriteContent( ); - private: - Box * Container; - - std::vector Content; -};//Box_ftyp Class - -Box_trak::Box_trak( ) { - Container = new Box( 0x7472616B ); -} - -Box_trak::~Box_trak() { - delete Container; -} - -Box * Box_trak::GetBox() { - return Container; -} - -void Box_trak::AddContent( Box * newcontent, uint32_t offset ) { - if( offset >= Content.size() ) { - Content.resize(offset+1); - } - if( Content[offset] ) { - delete Content[offset]; - } - Content[offset] = newcontent; -} - -void Box_trak::WriteContent( ) { - Container->ResetPayload( ); - Box * current; - std::string serializedbox = ""; - for( uint32_t i = 0; i < Content.size(); i++ ) { - current=Content[i]; - if( current ) { - serializedbox.append((char*)current->GetBoxedData(),current->GetBoxedDataSize()); - } - } - Container->SetPayload((uint32_t)serializedbox.size(),(uint8_t*)serializedbox.c_str()); -} diff --git a/util/MP4/box_trex.cpp b/util/MP4/box_trex.cpp deleted file mode 100644 index 16369795..00000000 --- a/util/MP4/box_trex.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include "box.cpp" - -class Box_trex { - public: - Box_trex( ); - ~Box_trex(); - Box * GetBox(); - void SetTrackID( uint32_t Id = 0 ); - void SetSampleDescriptionIndex( uint32_t Index = 0 ); - void SetSampleDuration( uint32_t Duration = 0 ); - void SetSampleSize( uint32_t Size = 0 ); - private: - void SetReserved( ); - void SetDefaults( ); - Box * Container; -};//Box_ftyp Class - -Box_trex::Box_trex( ) { - Container = new Box( 0x74726578 ); - SetReserved( ); - SetDefaults( ); -} - -Box_trex::~Box_trex() { - delete Container; -} - -Box * Box_trex::GetBox() { - return Container; -} - -void Box_trex::SetDefaults( ) { - SetTrackID( ); - SetSampleDescriptionIndex( ); - SetSampleDuration( ); - SetSampleSize( ); -} - -void Box_trex::SetReserved( ) { - Container->SetPayload((uint32_t)2,Box::uint16_to_uint8(1),22); - Container->SetPayload((uint32_t)2,Box::uint16_to_uint8(0),20); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0)); -} - -void Box_trex::SetTrackID( uint32_t Id ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Id),4); -} - -void Box_trex::SetSampleDescriptionIndex( uint32_t Index ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Index),8); -} - -void Box_trex::SetSampleDuration( uint32_t Duration ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Duration),12); -} - -void Box_trex::SetSampleSize( uint32_t Size ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(Size),16); -} diff --git a/util/MP4/box_trun.cpp b/util/MP4/box_trun.cpp deleted file mode 100644 index 1010f2da..00000000 --- a/util/MP4/box_trun.cpp +++ /dev/null @@ -1,68 +0,0 @@ -#include "box.cpp" -#include - -struct trun_sampleinformationstructure { - uint32_t SampleDuration; - uint32_t SampleSize; -}; - -class Box_trun { - public: - Box_trun( ); - ~Box_trun(); - Box * GetBox(); - void SetDataOffset( uint32_t Offset = 0 ); - void AddSampleInformation( uint32_t SampleDuration = 0, uint32_t SampleSize = 0, uint32_t Offset = 0 ); - void WriteContent( ); - private: - void SetDefaults( ); - bool setSampleDuration; - bool setSampleSize; - uint32_t curDataOffset; - std::vector SampleInfo; - Box * Container; -};//Box_ftyp Class - -Box_trun::Box_trun( ) { - Container = new Box( 0x74666864 ); - SetDefaults( ); -} - -Box_trun::~Box_trun() { - delete Container; -} - -Box * Box_trun::GetBox() { - return Container; -} - -void Box_trun::SetDataOffset( uint32_t Offset ) { - curDataOffset = Offset; -} - -void Box_trun::WriteContent( ) { - uint32_t curoffset; - uint32_t flags = 0 & ( curDataOffset ? 0x1 : 0 ) & ( setSampleDuration ? 0x100 : 0 ) & ( setSampleSize ? 0x200 : 0 ); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(flags)); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(SampleInfo.size()),4); - curoffset = 8; - if( curDataOffset ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(curDataOffset),curoffset); - curoffset += 4; - } - for( uint32_t i = 0; i < SampleInfo.size(); i++ ) { - if( setSampleDuration ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(SampleInfo[i].SampleDuration),curoffset); - curoffset += 4; - } - if( setSampleSize ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(SampleInfo[i].SampleSize),curoffset); - curoffset += 4; - } - } -} - -void Box_trun::SetDefaults( ) { - setSampleDuration = false; - setSampleSize = false; -} diff --git a/util/MP4/box_url.cpp b/util/MP4/box_url.cpp deleted file mode 100644 index 8287f7c3..00000000 --- a/util/MP4/box_url.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "box.cpp" - -class Box_url { - public: - Box_url( ); - ~Box_url(); - Box * GetBox(); - private: - Box * Container; -};//Box_ftyp Class - -Box_url::Box_url( ) { - Container = new Box( 0x75726C20 ); - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(1)); -} - -Box_url::~Box_url() { - delete Container; -} - -Box * Box_url::GetBox() { - return Container; -} diff --git a/util/MP4/box_vmhd.cpp b/util/MP4/box_vmhd.cpp deleted file mode 100644 index 6dd5fb8e..00000000 --- a/util/MP4/box_vmhd.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include "box.cpp" - -class Box_vmhd { - public: - Box_vmhd( ); - ~Box_vmhd(); - Box * GetBox(); - void SetGraphicsMode( uint16_t GraphicsMode = 0 ); - void SetOpColor( uint16_t Red = 0, uint16_t Green = 0, uint16_t Blue = 0); - private: - Box * Container; - void SetReserved( ); - void SetDefaults( ); -};//Box_ftyp Class - -Box_vmhd::Box_vmhd( ) { - Container = new Box( 0x766D6864 ); - SetDefaults(); - SetReserved(); -} - -Box_vmhd::~Box_vmhd() { - delete Container; -} - -Box * Box_vmhd::GetBox() { - return Container; -} - -void Box_vmhd::SetGraphicsMode( uint16_t GraphicsMode ) { - Container->SetPayload((uint32_t)2,Box::uint16_to_uint8(GraphicsMode),8); -} -void Box_vmhd::SetOpColor( uint16_t Red, uint16_t Green, uint16_t Blue ) { - Container->SetPayload((uint32_t)2,Box::uint16_to_uint8(Blue),14); - Container->SetPayload((uint32_t)2,Box::uint16_to_uint8(Green),12); - Container->SetPayload((uint32_t)2,Box::uint16_to_uint8(Red),10); -} - -void Box_vmhd::SetReserved( ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(1)); -} -void Box_vmhd::SetDefaults( ) { - SetOpColor(); - SetGraphicsMode(); -} diff --git a/util/MP4/interface.cpp b/util/MP4/interface.cpp deleted file mode 100644 index e567fb86..00000000 --- a/util/MP4/interface.cpp +++ /dev/null @@ -1,589 +0,0 @@ -#include "box_includes.h" - -#include - -class Interface { - public: - Interface(); - ~Interface(); - void link(); - uint32_t GetContentSize(); - uint8_t * GetContents(); - void SetWidth( uint16_t NewWidth ); - void SetHeight( uint16_t NewHeight ); - void SetDurationTime( uint32_t NewDuration, uint32_t Track ); - void SetTimeScale( uint32_t NewUnitsPerSecond, uint32_t Track ); - void AddSTTSEntry( uint32_t SampleCount, uint32_t SampleDelta, uint32_t Track ); - void EmptySTTS( uint32_t Track ); - void AddSTSCEntry( uint32_t FirstChunk, uint32_t SamplesPerChunk, uint32_t Track ); - void EmptySTSC( uint32_t Track ); - void SetOffsets( std::vector NewOffsets, uint32_t Track ); - void SetData( std::string data ); - std::string GenerateLiveBootstrap( uint32_t CurMediaTime ); - static std::string mdatFold(std::string data); - private: - void SetStaticDefaults(); - void UpdateContents(); - void WriteSTTS( uint32_t Track ); - void WriteSTSC( uint32_t Track ); - bool AllBoxesExist(); - uint16_t Width; - uint16_t Height; - std::vector Duration; - std::vector UnitsPerSecond; - std::vector sttsvide; - std::vector sttssoun; - std::vector stscvide; - std::vector stscsoun; - Box_ftyp * ftyp; - Box_moov * moov; - Box_mvhd * mvhd; - Box_trak * trak_vide; - Box_tkhd * tkhd_vide; - Box_mdia * mdia_vide; - Box_mdhd * mdhd_vide; - Box_hdlr * hdlr_vide; - Box_minf * minf_vide; - Box_vmhd * vmhd_vide; - Box_dinf * dinf_vide; - Box_dref * dref_vide; - Box_url * url_vide; - Box_stbl * stbl_vide; - Box_stts * stts_vide; - Box_stsc * stsc_vide; - Box_stco * stco_vide; - Box_stsd * stsd_vide; - Box_avcC * avcC_vide; - Box_trak * trak_soun; - Box_tkhd * tkhd_soun; - Box_mdia * mdia_soun; - Box_mdhd * mdhd_soun; - Box_hdlr * hdlr_soun; - Box_minf * minf_soun; - Box_smhd * smhd_soun; - Box_dinf * dinf_soun; - Box_dref * dref_soun; - Box_url * url_soun; - Box_stbl * stbl_soun; - Box_stts * stts_soun; - Box_stsc * stsc_soun; - Box_stco * stco_soun; - Box_stsd * stsd_soun; - Box_esds * esds_soun; - Box_rtmp * rtmp; - Box_amhp * amhp; - Box_mvex * mvex; - Box_trex * trex_vide; - Box_trex * trex_soun; - Box_afra * afra; - Box_abst * abst; - Box_asrt * asrt; - Box_afrt * afrt; - Box_moof * moof; - Box_mfhd * mfhd; - Box_traf * traf_vide; - Box_tfhd * tfhd_vide; - Box_trun * trun_vide; - Box_traf * traf_soun; - Box_tfhd * tfhd_soun; - Box_trun * trun_soun; -};//Interface class - -Interface::Interface() { - //Initializing local data - Width = 0; - Height = 0; - //Creating the boxes - ftyp = new Box_ftyp(); - moov = new Box_moov(); - mvhd = new Box_mvhd(); - trak_vide = new Box_trak(); - tkhd_vide = new Box_tkhd(); - mdia_vide = new Box_mdia(); - mdhd_vide = new Box_mdhd(); - hdlr_vide = new Box_hdlr(); - minf_vide = new Box_minf(); - vmhd_vide = new Box_vmhd(); - dinf_vide = new Box_dinf(); - dref_vide = new Box_dref(); - url_vide = new Box_url(); - stbl_vide = new Box_stbl(); - stts_vide = new Box_stts(); - stsc_vide = new Box_stsc(); - stco_vide = new Box_stco(); - stsd_vide = new Box_stsd(); - avcC_vide = new Box_avcC(); - trak_soun = new Box_trak(); - tkhd_soun = new Box_tkhd(); - mdia_soun = new Box_mdia(); - mdhd_soun = new Box_mdhd(); - hdlr_soun = new Box_hdlr(); - minf_soun = new Box_minf(); - smhd_soun = new Box_smhd(); - dinf_soun = new Box_dinf(); - dref_soun = new Box_dref(); - url_soun = new Box_url(); - stbl_soun = new Box_stbl(); - stts_soun = new Box_stts(); - stsc_soun = new Box_stsc(); - stco_soun = new Box_stco(); - stsd_soun = new Box_stsd(); - esds_soun = new Box_esds(); - rtmp = new Box_rtmp(); - amhp = new Box_amhp(); - mvex = new Box_mvex(); - trex_vide = new Box_trex(); - trex_soun = new Box_trex(); - afra = new Box_afra(); - abst = new Box_abst(); - asrt = new Box_asrt(); - afrt = new Box_afrt(); - moof = new Box_moof(); - mfhd = new Box_mfhd(); - traf_vide = new Box_traf(); - tfhd_vide = new Box_tfhd(); - trun_vide = new Box_trun(); - traf_soun = new Box_traf(); - tfhd_soun = new Box_tfhd(); - trun_soun = new Box_trun(); - //Set some values we already know won't change once the boxes have been created - SetStaticDefaults(); - //Linking all boxes - link( ); -} - -Interface::~Interface() { - //Deleting the boxes if they still exist. - if( trun_soun ) { delete trun_soun; trun_soun = NULL; } - if( tfhd_soun ) { delete tfhd_soun; tfhd_soun = NULL; } - if( traf_soun ) { delete traf_soun; traf_soun = NULL; } - if( trun_vide ) { delete trun_vide; trun_vide = NULL; } - if( tfhd_vide ) { delete tfhd_vide; tfhd_vide = NULL; } - if( traf_vide ) { delete traf_vide; traf_vide = NULL; } - if( mfhd ) { delete mfhd; mfhd = NULL; } - if( moof ) { delete moof; moof = NULL; } - if( afrt ) { delete afrt; afrt = NULL; } - if( asrt ) { delete asrt; asrt = NULL; } - if( abst ) { delete abst; abst = NULL; } - if( afra ) { delete afra; afra = NULL; } - if( trex_vide ) { delete trex_vide; trex_vide = NULL; } - if( trex_soun ) { delete trex_soun; trex_soun = NULL; } - if( mvex ) { delete mvex; mvex = NULL; } - if( amhp ) { delete amhp; amhp = NULL; } - if( rtmp ) { delete rtmp; rtmp = NULL; } - if( esds_soun ) { delete esds_soun; esds_soun = NULL; } - if( stsd_soun ) { delete stsd_soun; stsd_soun = NULL; } - if( stco_soun ) { delete stco_soun; stco_soun = NULL; } - if( stsc_soun ) { delete stsc_soun; stsc_soun = NULL; } - if( stts_soun ) { delete stts_soun; stts_soun = NULL; } - if( stbl_soun ) { delete stbl_soun; stbl_soun = NULL; } - if( url_soun ) { delete url_soun; url_soun = NULL; } - if( dref_soun ) { delete dref_soun; dref_soun = NULL; } - if( dinf_soun ) { delete dinf_soun; dinf_soun = NULL; } - if( minf_soun ) { delete minf_soun; minf_soun = NULL; } - if( hdlr_soun ) { delete hdlr_soun; hdlr_soun = NULL; } - if( mdhd_soun ) { delete mdhd_soun; mdhd_soun = NULL; } - if( mdia_soun ) { delete mdia_soun; mdia_soun = NULL; } - if( tkhd_soun ) { delete tkhd_soun; tkhd_soun = NULL; } - if( trak_soun ) { delete trak_soun; trak_soun = NULL; } - if( avcC_vide ) { delete avcC_vide; avcC_vide = NULL; } - if( stsd_vide ) { delete stsd_vide; stsd_vide = NULL; } - if( stco_vide ) { delete stco_vide; stco_vide = NULL; } - if( stsc_vide ) { delete stsc_vide; stsc_vide = NULL; } - if( stts_vide ) { delete stts_vide; stts_vide = NULL; } - if( stbl_vide ) { delete stbl_vide; stbl_vide = NULL; } - if( url_vide ) { delete url_vide; url_vide = NULL; } - if( dref_vide ) { delete dref_vide; dref_vide = NULL; } - if( dinf_vide ) { delete dinf_vide; dinf_vide = NULL; } - if( minf_vide ) { delete minf_vide; minf_vide = NULL; } - if( hdlr_vide ) { delete hdlr_vide; hdlr_vide = NULL; } - if( mdhd_vide ) { delete mdhd_vide; mdhd_vide = NULL; } - if( mdia_vide ) { delete mdia_vide; mdia_vide = NULL; } - if( tkhd_vide ) { delete tkhd_vide; tkhd_vide = NULL; } - if( trak_vide ) { delete trak_vide; trak_vide = NULL; } - if( mvhd ) { delete mvhd; mvhd = NULL; } - if( moov ) { delete moov; moov = NULL; } - if( ftyp ) { delete ftyp; ftyp = NULL; } -} - -void Interface::link( ) { - //Linking Video Track - stsd_vide->AddContent(avcC_vide->GetBox()); - stbl_vide->AddContent(stsd_vide->GetBox(),3); - stbl_vide->AddContent(stco_vide->GetBox(),2); - stbl_vide->AddContent(stsc_vide->GetBox(),1); - stbl_vide->AddContent(stts_vide->GetBox()); - dref_vide->AddContent(url_vide->GetBox()); - dinf_vide->AddContent(dref_vide->GetBox()); - minf_vide->AddContent(stbl_vide->GetBox(),2); - minf_vide->AddContent(dinf_vide->GetBox(),1); - minf_vide->AddContent(vmhd_vide->GetBox()); - mdia_vide->AddContent(minf_vide->GetBox(),2); - mdia_vide->AddContent(hdlr_vide->GetBox(),1); - mdia_vide->AddContent(mdhd_vide->GetBox()); - trak_vide->AddContent(mdia_vide->GetBox(),1); - trak_vide->AddContent(tkhd_vide->GetBox()); - - //Linking Sound Track - stsd_soun->AddContent(esds_soun->GetBox()); - stbl_soun->AddContent(stsd_soun->GetBox(),3); - stbl_soun->AddContent(stco_soun->GetBox(),2); - stbl_soun->AddContent(stsc_soun->GetBox(),1); - stbl_soun->AddContent(stts_soun->GetBox()); - dref_soun->AddContent(url_soun->GetBox()); - dinf_soun->AddContent(dref_soun->GetBox()); - minf_soun->AddContent(stbl_soun->GetBox(),2); - minf_soun->AddContent(dinf_soun->GetBox(),1); - minf_soun->AddContent(smhd_soun->GetBox()); - mdia_soun->AddContent(minf_soun->GetBox(),2); - mdia_soun->AddContent(hdlr_soun->GetBox(),1); - mdia_soun->AddContent(mdhd_soun->GetBox()); - trak_soun->AddContent(mdia_soun->GetBox(),1); - trak_soun->AddContent(tkhd_soun->GetBox()); - - //Linking mvex - mvex->AddContent(trex_soun->GetBox(),2); - mvex->AddContent(trex_vide->GetBox(),1); - - //Linking total file - moov->AddContent(mvex->GetBox(),3); - moov->AddContent(trak_soun->GetBox(),2); - moov->AddContent(trak_vide->GetBox(),1); - moov->AddContent(mvhd->GetBox()); - - rtmp->AddContent(amhp->GetBox()); - - //Linking ABST - abst->AddFragmentRunTable(afrt->GetBox()); - abst->AddSegmentRunTable(asrt->GetBox()); - - //Linking TRAF_SOUN - traf_soun->AddContent( trun_soun->GetBox(),1); - traf_soun->AddContent( tfhd_soun->GetBox() ); - - //Linking TRAF_vide - traf_vide->AddContent( trun_vide->GetBox(),1); - traf_vide->AddContent( tfhd_vide->GetBox() ); - - //Linking MOOF - moof->AddContent(traf_soun->GetBox(),2); - moof->AddContent(traf_vide->GetBox(),1); - moof->AddContent(mfhd->GetBox()); -} - -uint32_t Interface::GetContentSize( ) { - return ftyp->GetBox( )->GetBoxedDataSize( ) + moov->GetBox( )->GetBoxedDataSize( ) + rtmp->GetBox( )->GetBoxedDataSize( ); -} - -uint8_t * Interface::GetContents( ) { - uint8_t * Result = new uint8_t[GetContentSize( )]; - uint32_t Ftyp_Size = ftyp->GetBox( )->GetBoxedDataSize( ); - uint32_t Moov_Size = moov->GetBox( )->GetBoxedDataSize( ); - uint32_t Rtmp_Size = rtmp->GetBox( )->GetBoxedDataSize( ); - memcpy(Result,ftyp->GetBox( )->GetBoxedData( ),Ftyp_Size); - memcpy(&Result[Ftyp_Size],moov->GetBox( )->GetBoxedData( ),Moov_Size); - memcpy(&Result[Ftyp_Size+Moov_Size],rtmp->GetBox( )->GetBoxedData( ),Rtmp_Size); - return Result; -} - -void Interface::UpdateContents( ) { - if( !Width ) { fprintf(stderr,"WARNING: Width not set!\n"); } - if( !Height ) { fprintf(stderr,"WARNING: Height not set!\n"); } - if( !Duration.size() ) { fprintf(stderr,"WARNING: Duration not set!\n"); } - if( !UnitsPerSecond.size() ) { fprintf(stderr,"WARNING: Timescale not set!\n"); } - if( sttsvide.size() == 0 ) { - fprintf(stderr,"WARNING: No video stts available!\n"); - } else { WriteSTTS( 1 ); } - if( sttssoun.size() == 0 ) { - fprintf(stderr,"WARNING: No sound stts available!\n"); - } else { WriteSTTS( 2 ); } - if( stscvide.size() == 0 ) { - fprintf(stderr,"WARNING: No video stsc available!\n"); - } else { WriteSTSC( 1 ); } - if( stscsoun.size() == 0 ) { - fprintf(stderr,"WARNING: No sound stsc available!\n"); - } else { WriteSTSC( 2 ); } - stsd_vide->WriteContent( ); - stco_vide->WriteContent( ); - stsc_vide->WriteContent( ); - stts_vide->WriteContent( ); - stbl_vide->WriteContent( ); - dref_vide->WriteContent( ); - dinf_vide->WriteContent( ); - minf_vide->WriteContent( ); - mdia_vide->WriteContent( ); - - stsd_soun->WriteContent( ); - stco_soun->WriteContent( ); - stsc_soun->WriteContent( ); - stts_soun->WriteContent( ); - stbl_soun->WriteContent( ); - dref_soun->WriteContent( ); - dinf_soun->WriteContent( ); - minf_soun->WriteContent( ); - mdia_soun->WriteContent( ); - - trak_vide->WriteContent( ); - trak_soun->WriteContent( ); - - mvex->WriteContent( ); - moov->WriteContent( ); - - amhp->WriteContent( ); - rtmp->WriteContent( ); - - afrt->WriteContent( ); - asrt->WriteContent( ); - abst->WriteContent( ); - - trun_soun->WriteContent( ); - traf_soun->WriteContent( ); - - trun_vide->WriteContent( ); - traf_vide->WriteContent( ); - - moof->WriteContent( ); -} - -bool Interface::AllBoxesExist() { - return ( ftyp && moov && mvhd && trak_vide && tkhd_vide && mdia_vide && mdhd_vide && hdlr_vide && - minf_vide && vmhd_vide && dinf_vide && dref_vide && url_vide && stbl_vide && stts_vide && stsc_vide && - stco_vide && stsd_vide && avcC_vide && trak_soun && tkhd_soun && mdia_soun && mdhd_soun && hdlr_soun && - minf_soun && smhd_soun && dinf_soun && dref_soun && url_soun && stbl_soun && stts_soun && stsc_soun && - stco_soun && stsd_soun && esds_soun && rtmp && amhp && mvex && trex_vide && trex_soun && afrt && asrt - && abst && moof && mfhd && traf_vide && tfhd_vide && trun_vide && traf_soun && tfhd_soun && trun_soun ); -} - -void Interface::SetWidth( uint16_t NewWidth ) { - if( Width != NewWidth ) { - Width = NewWidth; - avcC_vide->SetWidth( Width ); - tkhd_vide->SetWidth( Width ); - } -} - -void Interface::SetHeight( uint16_t NewHeight ) { - if( Height != NewHeight ) { - Height = NewHeight; - avcC_vide->SetHeight( Height ); - tkhd_vide->SetHeight( Height ); - } -} - -void Interface::SetDurationTime( uint32_t NewDuration, uint32_t Track ) { - if( Duration.size() < Track ) { Duration.resize(Track+1); } - if( Duration[Track] != NewDuration ) { - Duration[Track] = NewDuration; - switch( Track ) { - case 0: - mvhd->SetDurationTime( Duration[Track] ); - break; - case 1: - mdhd_vide->SetDurationTime( Duration[Track] ); - tkhd_vide->SetDurationTime( Duration[Track] ); - break; - case 2: - mdhd_soun->SetDurationTime( Duration[Track] ); - tkhd_soun->SetDurationTime( Duration[Track] ); - break; - default: - fprintf( stderr, "WARNING, Setting Duration for track %d does have any effect\n", Track ); - break; - } - } -} -void Interface::SetTimeScale( uint32_t NewUnitsPerSecond, uint32_t Track ) { - if( UnitsPerSecond.size() < Track ) { UnitsPerSecond.resize(Track+1); } - if( UnitsPerSecond[Track] != NewUnitsPerSecond ) { - UnitsPerSecond[Track] = NewUnitsPerSecond; - switch(Track) { - case 0: - mvhd->SetTimeScale( UnitsPerSecond[Track] ); - break; - case 1: - mdhd_vide->SetTimeScale( UnitsPerSecond[Track] ); - break; - case 2: - mdhd_soun->SetTimeScale( UnitsPerSecond[Track] ); - break; - default: - fprintf( stderr, "WARNING, Setting Timescale for track %d does have any effect\n", Track ); - break; - } - } -} - -void Interface::SetStaticDefaults() { -// 'vide' = 0x76696465 - hdlr_vide->SetHandlerType( 0x76696465 ); - hdlr_vide->SetName( "Video Track" ); -// 'soun' = 0x736F756E - hdlr_soun->SetHandlerType( 0x736F756E ); - hdlr_vide->SetName( "Audio Track" ); -// Set Track ID's - tkhd_vide->SetTrackID( 1 ); - tkhd_soun->SetTrackID( 2 ); - trex_vide->SetTrackID( 1 ); - trex_soun->SetTrackID( 2 ); -// Set amhp entry - amhp->AddEntry( 1, 0, 0 ); -} - -void Interface::AddSTTSEntry( uint32_t SampleCount, uint32_t SampleDelta, uint32_t Track ) { - stts_record temp; - temp.SampleCount = SampleCount; - temp.SampleDelta = SampleDelta; - switch(Track) { - case 1: - sttsvide.push_back(temp); - break; - case 2: - sttssoun.push_back(temp); - break; - default: - fprintf( stderr, "WARNING: Track %d does not exist, STTS not added\n", Track ); - break; - } -} - -void Interface::EmptySTTS( uint32_t Track ) { - switch(Track) { - case 1: - sttsvide.clear(); - break; - case 2: - sttssoun.clear(); - break; - default: - fprintf( stderr, "WARNING: Track %d does not exist, STTS not cleared\n", Track ); - break; - } -} - -void Interface::WriteSTTS( uint32_t Track ) { - switch( Track ) { - case 1: - for( int i = sttsvide.size() -1; i > 0; i -- ) { - stts_vide->AddEntry(sttsvide[i].SampleCount,sttsvide[i].SampleDelta,i); - } - break; - case 2: - for( int i = sttssoun.size() -1; i > 0; i -- ) { - stts_soun->AddEntry(sttssoun[i].SampleCount,sttssoun[i].SampleDelta,i); - } - break; - default: - fprintf( stderr, "WARNING: Track %d does not exist, STTS not written\n", Track ); - break; - } -} - -void Interface::AddSTSCEntry( uint32_t FirstChunk, uint32_t SamplesPerChunk, uint32_t Track ) { - stsc_record temp; - temp.FirstChunk = FirstChunk; - temp.SamplesPerChunk = SamplesPerChunk; - temp.SampleDescIndex = 1; - switch(Track) { - case 1: - stscvide.push_back(temp); - break; - case 2: - stscsoun.push_back(temp); - break; - default: - fprintf( stderr, "WARNING: Track %d does not exist, STSC not added\n", Track ); - break; - } -} - -void Interface::EmptySTSC( uint32_t Track ) { - switch(Track) { - case 1: - stscvide.clear(); - break; - case 2: - stscsoun.clear(); - break; - default: - fprintf( stderr, "WARNING: Track %d does not exist, STSC not cleared\n", Track ); - break; - } -} - -void Interface::WriteSTSC( uint32_t Track ) { - switch( Track ) { - case 1: - for( int i = stscvide.size() -1; i > 0; i -- ) { - stsc_vide->AddEntry(stscvide[i].FirstChunk,stscvide[i].SamplesPerChunk,1,i); - } - break; - case 2: - for( int i = stscsoun.size() -1; i > 0; i -- ) { - stsc_soun->AddEntry(stscsoun[i].FirstChunk,stscsoun[i].SamplesPerChunk,1,i); - } - break; - default: - fprintf( stderr, "WARNING: Track %d does not exist, STSC not written\n", Track ); - break; - } -} - -void Interface::SetOffsets( std::vector NewOffsets, uint32_t Track ) { - switch( Track ) { - case 1: - stco_vide->SetOffsets( NewOffsets ); - break; - case 2: - stco_soun->SetOffsets( NewOffsets ); - break; - default: - fprintf( stderr, "WARNING: Track %d does not exist, Offsets not written\n", Track ); - break; - } -} - -std::string Interface::GenerateLiveBootstrap( uint32_t CurMediaTime ) { - //SetUpAFRT - afrt->SetUpdate(false); - afrt->SetTimeScale( 1000 ); - afrt->AddQualityEntry( "" ); - std::cerr << "Setting RunEntry on 4000 ms\n"; - afrt->AddFragmentRunEntry( 1, 0 , 4000 ); //FirstFragment, FirstFragmentTimestamp,Fragment Duration in milliseconds - afrt->WriteContent( ); - - //SetUpASRT - asrt->SetUpdate(false); - asrt->AddQualityEntry( "" ); - asrt->AddSegmentRunEntry( 1, 199 );//1 Segment, 199 Fragments - asrt->WriteContent( ); - - //SetUpABST - abst->SetBootstrapVersion( 1 ); - abst->SetProfile( 0 ); - abst->SetLive( true ); - abst->SetUpdate( false ); - abst->SetTimeScale( 1000 ); - abst->SetMediaTime( 0xFFFFFFFF ); - abst->SetSMPTE( 0 ); - abst->SetMovieIdentifier( "fifa" ); - abst->SetDRM( "" ); - abst->SetMetaData( "" ); - abst->AddServerEntry( "" ); - abst->AddQualityEntry( "" ); - abst->WriteContent( ); - - std::string Result; - Result.append( (char*)abst->GetBox( )->GetBoxedData( ), (int)abst->GetBox( )->GetBoxedDataSize( ) ); - return Result; -} - -std::string Interface::mdatFold(std::string data){ - std::string Result; - unsigned int t_int; - t_int = htonl(data.size()+8); - Result.append((char*)&t_int, 4); - t_int = htonl(0x6D646174); - Result.append((char*)&t_int, 4); - Result.append(data); - return Result; -} diff --git a/util/MP4/main.cpp b/util/MP4/main.cpp deleted file mode 100644 index 027ddb8d..00000000 --- a/util/MP4/main.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include -#include "interface.h" - -int main( ) { - std::cout << "Creating Interface\n"; - Interface * file = new Interface(); - std::cout << "Interface created, start linking them\n"; - file->link(); - std::cout << "Linking finished, deleting boxes\n"; - delete file; - std::cout << "Interface deleted\n"; - return 0; -} diff --git a/util/server_setup.cpp b/util/server_setup.cpp deleted file mode 100644 index 8f8cfad4..00000000 --- a/util/server_setup.cpp +++ /dev/null @@ -1,115 +0,0 @@ -/// \file server_setup.cpp -/// Contains generic functions for setting up a DDVTECH Connector. - -#ifndef MAINHANDLER - /// Handler that is called for accepted incoming connections. - #define MAINHANDLER NoHandler - #error "No handler was set!" -#endif - - -#ifndef DEFAULT_PORT - /// Default port for this server. - #define DEFAULT_PORT 0 - #error "No default port was set!" -#endif - - -#include "socket.h" //Socket library -#include "config.h" //utilities for config management -#include -#include -#include -#include -Socket::Server server_socket; ///< Placeholder for the server socket - -/// Basic signal handler. Disconnects the server_socket if it receives -/// a SIGINT, SIGHUP or SIGTERM signal, but does nothing for SIGPIPE. -/// Disconnecting the server_socket will terminate the main listening loop -/// and cleanly shut down the process. -void signal_handler (int signum){ - switch (signum){ - case SIGINT: - #if DEBUG >= 1 - fprintf(stderr, "Received SIGINT - closing server socket.\n"); - #endif - break; - case SIGHUP: - #if DEBUG >= 1 - fprintf(stderr, "Received SIGHUP - closing server socket.\n"); - #endif - break; - case SIGTERM: - #if DEBUG >= 1 - fprintf(stderr, "Received SIGTERM - closing server socket.\n"); - #endif - break; - case SIGCHLD: - wait(0); - return; - break; - default: return; break; - } - if (!server_socket.connected()) return; - server_socket.close(); -}//signal_handler - -/// Generic main entry point and loop for DDV Connectors. -/// This sets up the proper termination handler, checks commandline options, -/// parses config files and opens a listening socket on the requested port. -/// Any incoming connections will be accepted and start up the function #MAINHANDLER, -/// which should be defined before including server_setup.cpp. -/// The default port is set by define #DEFAULT_PORT. -/// The configuration file section is set by define #CONFIGSECT. -int main(int argc, char ** argv){ - Socket::Connection S;//placeholder for incoming connections - - //setup signal handler - struct sigaction new_action; - new_action.sa_handler = signal_handler; - sigemptyset (&new_action.sa_mask); - new_action.sa_flags = 0; - sigaction(SIGINT, &new_action, NULL); - sigaction(SIGHUP, &new_action, NULL); - sigaction(SIGTERM, &new_action, NULL); - sigaction(SIGPIPE, &new_action, NULL); - sigaction(SIGCHLD, &new_action, NULL); - - //set and parse configuration - Util::Config C; - C.listen_port = DEFAULT_PORT; - C.parseArgs(argc, argv); - - //setup a new server socket, for the correct interface and port - server_socket = Socket::Server(C.listen_port, C.interface); - #if DEBUG >= 3 - fprintf(stderr, "Made a listening socket on %s:%i...\n", C.interface.c_str(), C.listen_port); - #endif - if (!server_socket.connected()){ - #if DEBUG >= 1 - fprintf(stderr, "Error: could not make listening socket\n"); - #endif - return 1; - } - - Util::setUser(C.username); - if (C.daemon_mode){Util::Daemonize();} - - while (server_socket.connected()){ - S = server_socket.accept(); - if (S.connected()){//check if the new connection is valid - pid_t myid = fork(); - if (myid == 0){//if new child, start MAINHANDLER - return MAINHANDLER(S); - }else{//otherwise, do nothing or output debugging text - #if DEBUG >= 3 - fprintf(stderr, "Spawned new process %i for socket %i\n", (int)myid, S.getSocket()); - #endif - } - } - }//while connected - #if DEBUG >= 1 - fprintf(stderr, "Server socket closed, exiting.\n"); - #endif - return 0; -}//main diff --git a/version.sh b/version.sh new file mode 100755 index 00000000..671da00b --- /dev/null +++ b/version.sh @@ -0,0 +1,5 @@ +#!/bin/sh +if git describe --tags > version.tmp; then + echo "m4_define([VERSION_NUMBER], [`tr -d '\n' < version.tmp`])" > version.m4 +fi +rm version.tmp From 6bfddff13192998c826546f39a9dadb305c7f049 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 8 May 2012 22:10:22 +0200 Subject: [PATCH 194/788] Now working fully with autotools --- lib/Makefile.am | 3 +- lib/mp4.cpp | 413 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/mp4.h | 132 ++++++++++++++++ 3 files changed, 547 insertions(+), 1 deletion(-) create mode 100644 lib/mp4.cpp create mode 100644 lib/mp4.h diff --git a/lib/Makefile.am b/lib/Makefile.am index 76d5de1c..d7a351a3 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,4 +1,4 @@ -noinst_LIBRARIES=libamf.a libauth.a libbase64.a libconfig.a libcrypto.a libdtsc.a libflv_tag.a libhttp_parser.a libjson.a libmd5.a libprocs.a librtmpchunks.a libsocket.a libtinythread.a +noinst_LIBRARIES=libamf.a libauth.a libbase64.a libconfig.a libcrypto.a libdtsc.a libflv_tag.a libhttp_parser.a libjson.a libmd5.a libprocs.a librtmpchunks.a libsocket.a libtinythread.a libmp4.a libamf_a_SOURCES=amf.h amf.cpp libauth_a_SOURCES=auth.h auth.cpp libbase64_a_SOURCES=base64.h base64.cpp @@ -13,3 +13,4 @@ libprocs_a_SOURCES=procs.h procs.cpp librtmpchunks_a_SOURCES=rtmpchunks.h rtmpchunks.cpp libsocket_a_SOURCES=socket.h socket.cpp libtinythread_a_SOURCES=tinythread.h tinythread.cpp +libmp4_a_SOURCES=mp4.h mp4.cpp diff --git a/lib/mp4.cpp b/lib/mp4.cpp new file mode 100644 index 00000000..ee0aa703 --- /dev/null +++ b/lib/mp4.cpp @@ -0,0 +1,413 @@ +#include "mp4.h" +#include //for malloc and free +#include //for memcpy +#include //for htonl and friends + +/// Contains all MP4 format related code. +namespace MP4{ + +Box::Box() { + Payload = (uint8_t *)malloc(8); + PayloadSize = 0; +} + +Box::Box(uint32_t BoxType) { + Payload = (uint8_t *)malloc(8); + SetBoxType(BoxType); + PayloadSize = 0; +} + +Box::Box(uint8_t * Content, uint32_t length) { + PayloadSize = length-8; + Payload = (uint8_t *)malloc(length); + memcpy(Payload, Content, length); +} + +Box::~Box() { + if (Payload) free(Payload); +} + +void Box::SetBoxType(uint32_t BoxType) { + ((unsigned int*)Payload)[1] = htonl(BoxType); +} + +uint32_t Box::GetBoxType() { + return ntohl(((unsigned int*)Payload)[1]); +} + +void Box::SetPayload(uint32_t Size, uint8_t * Data, uint32_t Index) { + if ( Index + Size > PayloadSize ) { + PayloadSize = Index + Size; + ((unsigned int*)Payload)[0] = htonl(PayloadSize+8); + Payload = (uint8_t *)realloc(Payload, PayloadSize + 8); + } + memcpy(Payload + 8 + Index, Data, Size); +} + +uint32_t Box::GetPayloadSize() { + return PayloadSize; +} + +uint8_t * Box::GetPayload() { + return Payload+8; +} + +uint8_t * Box::GetPayload(uint32_t Index, uint32_t & Size) { + if(Index > PayloadSize) {Size = 0;} + if(Index + Size > PayloadSize) { Size = PayloadSize - Index; } + return Payload + 8 + Index; +} + +uint32_t Box::GetBoxedDataSize() { + return ntohl(((unsigned int*)Payload)[0]); +} + +uint8_t * Box::GetBoxedData( ) { + return Payload; +} + + +uint8_t * Box::uint32_to_uint8( uint32_t data ) { + uint8_t * temp = new uint8_t[4]; + temp[0] = (data >> 24) & 0x000000FF; + temp[1] = (data >> 16 ) & 0x000000FF; + temp[2] = (data >> 8 ) & 0x000000FF; + temp[3] = (data ) & 0x000000FF; + return temp; +} + +uint8_t * Box::uint16_to_uint8( uint16_t data ) { + uint8_t * temp = new uint8_t[2]; + temp[0] = (data >> 8) & 0x00FF; + temp[1] = (data ) & 0x00FF; + return temp; +} + +uint8_t * Box::uint8_to_uint8( uint8_t data ) { + uint8_t * temp = new uint8_t[1]; + temp[0] = data; + return temp; +} + +void Box::ResetPayload( ) { + PayloadSize = 0; + Payload = (uint8_t *)realloc(Payload, PayloadSize + 8); + ((unsigned int*)Payload)[0] = htonl(0); +} + +void ABST::SetBootstrapVersion( uint32_t Version ) { + curBootstrapInfoVersion = Version; +} + +void ABST::SetProfile( uint8_t Profile ) { + curProfile = Profile; +} + +void ABST::SetLive( bool Live ) { + isLive = Live; +} + +void ABST::SetUpdate( bool Update ) { + isUpdate = Update; +} + +void ABST::SetTimeScale( uint32_t Scale ) { + curTimeScale = Scale; +} + +void ABST::SetMediaTime( uint32_t Time ) { + curMediatime = Time; +} + +void ABST::SetSMPTE( uint32_t Smpte ) { + curSMPTE = Smpte; +} + +void ABST::SetMovieIdentifier( std::string Identifier ) { + curMovieIdentifier = Identifier; +} + +void ABST::SetDRM( std::string Drm ) { + curDRM = Drm; +} + +void ABST::SetMetaData( std::string MetaData ) { + curMetaData = MetaData; +} + +void ABST::AddServerEntry( std::string Url, uint32_t Offset ) { + if(Offset >= Servers.size()) { + Servers.resize(Offset+1); + } + Servers[Offset].ServerBaseUrl = Url; +} + +void ABST::AddQualityEntry( std::string Quality, uint32_t Offset ) { + if(Offset >= Qualities.size()) { + Qualities.resize(Offset+1); + } + Qualities[Offset].QualityModifier = Quality; +} + +void ABST::AddSegmentRunTable( Box * newSegment, uint32_t Offset ) { + if( Offset >= SegmentRunTables.size() ) { + SegmentRunTables.resize(Offset+1); + } + if( SegmentRunTables[Offset] ) { + delete SegmentRunTables[Offset]; + } + SegmentRunTables[Offset] = newSegment; +} + +void ABST::AddFragmentRunTable( Box * newFragment, uint32_t Offset ) { + if( Offset >= FragmentRunTables.size() ) { + FragmentRunTables.resize(Offset+1); + } + if( FragmentRunTables[Offset] ) { + delete FragmentRunTables[Offset]; + } + FragmentRunTables[Offset] = newFragment; +} + +void ABST::SetDefaults( ) { + SetProfile( ); + SetLive( ); + SetUpdate( ); + SetTimeScale( ); + SetMediaTime( ); + SetSMPTE( ); + SetMovieIdentifier( ); + SetDRM( ); + SetMetaData( ); + SetVersion( ); +} + +void ABST::SetVersion( bool NewVersion) { + Version = NewVersion; +} + +void ABST::SetReserved( ) { + Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0)); +} + +void ABST::WriteContent( ) { + Box * current; + std::string serializedServers = ""; + std::string serializedQualities = ""; + std::string serializedSegments = ""; + std::string serializedFragments = ""; + int SegmentAmount = 0; + int FragmentAmount = 0; + uint8_t * temp = new uint8_t[1]; + + ResetPayload( ); + SetReserved( ); + + for( uint32_t i = 0; i < Servers.size(); i++ ) { + serializedServers.append(Servers[i].ServerBaseUrl.c_str()); + serializedServers += '\0'; + } + for( uint32_t i = 0; i < Qualities.size(); i++ ) { + serializedQualities.append(Qualities[i].QualityModifier.c_str()); + serializedQualities += '\0'; + } + for( uint32_t i = 0; i < SegmentRunTables.size(); i++ ) { + current=SegmentRunTables[i]; + if( current ) { + SegmentAmount ++; + serializedSegments.append((char*)current->GetBoxedData(),current->GetBoxedDataSize()); + } + } + for( uint32_t i = 0; i < FragmentRunTables.size(); i++ ) { + current=FragmentRunTables[i]; + if( current ) { + FragmentAmount ++; + serializedFragments.append((char*)current->GetBoxedData(),current->GetBoxedDataSize()); + } + } + uint32_t OffsetServerEntryCount = 29 + curMovieIdentifier.size() + 1; + uint32_t OffsetQualityEntryCount = OffsetServerEntryCount + 1 + serializedServers.size(); + uint32_t OffsetDrmData = OffsetQualityEntryCount + 1 + serializedQualities.size(); + uint32_t OffsetMetaData = OffsetDrmData + curDRM.size() + 1; + uint32_t OffsetSegmentRuntableCount = OffsetMetaData + curMetaData.size() + 1; + uint32_t OffsetFragmentRuntableCount = OffsetSegmentRuntableCount + 1 + serializedSegments.size(); + + temp[0] = 0 + ( curProfile << 6 ) + ( (uint8_t)isLive << 7 ) + ( (uint8_t)isUpdate << 7 ); + + SetPayload((uint32_t)serializedFragments.size(),(uint8_t*)serializedFragments.c_str(),OffsetFragmentRuntableCount+1); + SetPayload((uint32_t)1,Box::uint8_to_uint8(FragmentAmount),OffsetFragmentRuntableCount); + SetPayload((uint32_t)serializedSegments.size(),(uint8_t*)serializedSegments.c_str(),OffsetSegmentRuntableCount+1); + SetPayload((uint32_t)1,Box::uint8_to_uint8(SegmentAmount),OffsetSegmentRuntableCount); + SetPayload((uint32_t)curMetaData.size()+1,(uint8_t*)curMetaData.c_str(),OffsetMetaData); + SetPayload((uint32_t)curDRM.size()+1,(uint8_t*)curDRM.c_str(),OffsetDrmData); + SetPayload((uint32_t)serializedQualities.size(),(uint8_t*)serializedQualities.c_str(),OffsetQualityEntryCount+1); + SetPayload((uint32_t)1,Box::uint8_to_uint8(Qualities.size()),OffsetQualityEntryCount); + SetPayload((uint32_t)serializedServers.size(),(uint8_t*)serializedServers.c_str(),OffsetServerEntryCount+1); + SetPayload((uint32_t)1,Box::uint8_to_uint8(Servers.size()),OffsetServerEntryCount); + SetPayload((uint32_t)curMovieIdentifier.size()+1,(uint8_t*)curMovieIdentifier.c_str(),29);//+1 for \0-terminated string... + SetPayload((uint32_t)4,Box::uint32_to_uint8(curSMPTE),25); + SetPayload((uint32_t)4,Box::uint32_to_uint8(0),21); + SetPayload((uint32_t)4,Box::uint32_to_uint8(curMediatime),17); + SetPayload((uint32_t)4,Box::uint32_to_uint8(0),13); + SetPayload((uint32_t)4,Box::uint32_to_uint8(curTimeScale),9); + SetPayload((uint32_t)1,temp,8); + SetPayload((uint32_t)4,Box::uint32_to_uint8(curBootstrapInfoVersion),4); +} + +void AFRT::SetUpdate( bool Update ) { + isUpdate = Update; +} + +void AFRT::AddQualityEntry( std::string Quality, uint32_t Offset ) { + if(Offset >= QualitySegmentUrlModifiers.size()) { + QualitySegmentUrlModifiers.resize(Offset+1); + } + QualitySegmentUrlModifiers[Offset] = Quality; +} + +void AFRT::AddFragmentRunEntry( uint32_t FirstFragment, uint32_t FirstFragmentTimestamp, uint32_t FragmentsDuration, uint8_t Discontinuity, uint32_t Offset ) { + if( Offset >= FragmentRunEntryTable.size() ) { + FragmentRunEntryTable.resize(Offset+1); + } + FragmentRunEntryTable[Offset].FirstFragment = FirstFragment; + FragmentRunEntryTable[Offset].FirstFragmentTimestamp = FirstFragmentTimestamp; + FragmentRunEntryTable[Offset].FragmentDuration = FragmentsDuration; + if( FragmentsDuration == 0) { + FragmentRunEntryTable[Offset].DiscontinuityIndicator = Discontinuity; + } +} + +void AFRT::SetDefaults( ) { + SetUpdate( ); + SetTimeScale( ); +} + +void AFRT::SetTimeScale( uint32_t Scale ) { + curTimeScale = Scale; +} + +void AFRT::WriteContent( ) { + std::string serializedQualities = ""; + std::string serializedFragmentEntries = ""; + ResetPayload( ); + + for( uint32_t i = 0; i < QualitySegmentUrlModifiers.size(); i++ ) { + serializedQualities.append(QualitySegmentUrlModifiers[i].c_str()); + serializedQualities += '\0'; + } + for( uint32_t i = 0; i < FragmentRunEntryTable.size(); i ++ ) { + serializedFragmentEntries.append((char*)Box::uint32_to_uint8(FragmentRunEntryTable[i].FirstFragment),4); + serializedFragmentEntries.append((char*)Box::uint32_to_uint8(0),4); + serializedFragmentEntries.append((char*)Box::uint32_to_uint8(FragmentRunEntryTable[i].FirstFragmentTimestamp),4); + serializedFragmentEntries.append((char*)Box::uint32_to_uint8(FragmentRunEntryTable[i].FragmentDuration),4); + if(FragmentRunEntryTable[i].FragmentDuration == 0) { + serializedFragmentEntries.append((char*)Box::uint8_to_uint8(FragmentRunEntryTable[i].DiscontinuityIndicator),1); + } + } + + uint32_t OffsetFragmentRunEntryCount = 9 + serializedQualities.size(); + + SetPayload((uint32_t)serializedFragmentEntries.size(),(uint8_t*)serializedFragmentEntries.c_str(),OffsetFragmentRunEntryCount+4); + SetPayload((uint32_t)4,Box::uint32_to_uint8(FragmentRunEntryTable.size()),OffsetFragmentRunEntryCount); + SetPayload((uint32_t)serializedQualities.size(),(uint8_t*)serializedQualities.c_str(),9); + SetPayload((uint32_t)1,Box::uint8_to_uint8(QualitySegmentUrlModifiers.size()),8); + SetPayload((uint32_t)4,Box::uint32_to_uint8(curTimeScale),4); + SetPayload((uint32_t)4,Box::uint32_to_uint8((isUpdate ? 1 : 0))); +} + +void ASRT::SetUpdate( bool Update ) { + isUpdate = Update; +} + +void ASRT::AddQualityEntry( std::string Quality, uint32_t Offset ) { + if(Offset >= QualitySegmentUrlModifiers.size()) { + QualitySegmentUrlModifiers.resize(Offset+1); + } + QualitySegmentUrlModifiers[Offset] = Quality; +} + +void ASRT::AddSegmentRunEntry( uint32_t FirstSegment, uint32_t FragmentsPerSegment, uint32_t Offset ) { + if( Offset >= SegmentRunEntryTable.size() ) { + SegmentRunEntryTable.resize(Offset+1); + } + SegmentRunEntryTable[Offset].FirstSegment = FirstSegment; + SegmentRunEntryTable[Offset].FragmentsPerSegment = FragmentsPerSegment; +} + +void ASRT::SetVersion( bool NewVersion ) { + Version = NewVersion; +} + +void ASRT::SetDefaults( ) { + SetUpdate( ); +} + +void ASRT::WriteContent( ) { + std::string serializedQualities = ""; + ResetPayload( ); + + for( uint32_t i = 0; i < QualitySegmentUrlModifiers.size(); i++ ) { + serializedQualities.append(QualitySegmentUrlModifiers[i].c_str()); + serializedQualities += '\0'; + } + + uint32_t OffsetSegmentRunEntryCount = 5 + serializedQualities.size(); + + for( uint32_t i = 0; i < SegmentRunEntryTable.size(); i ++ ) { + SetPayload((uint32_t)4,Box::uint32_to_uint8(SegmentRunEntryTable[i].FragmentsPerSegment),(8*i)+OffsetSegmentRunEntryCount+8); + SetPayload((uint32_t)4,Box::uint32_to_uint8(SegmentRunEntryTable[i].FirstSegment),(8*i)+OffsetSegmentRunEntryCount+4); + } + SetPayload((uint32_t)4,Box::uint32_to_uint8(SegmentRunEntryTable.size()),OffsetSegmentRunEntryCount); + SetPayload((uint32_t)serializedQualities.size(),(uint8_t*)serializedQualities.c_str(),5); + SetPayload((uint32_t)1,Box::uint8_to_uint8(QualitySegmentUrlModifiers.size()),4); + SetPayload((uint32_t)4,Box::uint32_to_uint8((isUpdate ? 1 : 0))); +} + +std::string GenerateLiveBootstrap( uint32_t CurMediaTime ) { + AFRT afrt; + afrt.SetUpdate(false); + afrt.SetTimeScale(1000); + afrt.AddQualityEntry(""); + afrt.AddFragmentRunEntry(1, 0 , 4000); //FirstFragment, FirstFragmentTimestamp,Fragment Duration in milliseconds + afrt.WriteContent(); + + ASRT asrt; + asrt.SetUpdate(false); + asrt.AddQualityEntry(""); + asrt.AddSegmentRunEntry(1, 199);//1 Segment, 199 Fragments + asrt.WriteContent(); + + ABST abst; + abst.AddFragmentRunTable(&afrt); + abst.AddSegmentRunTable(&asrt); + abst.SetBootstrapVersion(1); + abst.SetProfile(0); + abst.SetLive(true); + abst.SetUpdate(false); + abst.SetTimeScale(1000); + abst.SetMediaTime(0xFFFFFFFF); + abst.SetSMPTE(0); + abst.SetMovieIdentifier("fifa"); + abst.SetDRM(""); + abst.SetMetaData(""); + abst.AddServerEntry(""); + abst.AddQualityEntry(""); + abst.WriteContent(); + + std::string Result; + Result.append((char*)abst.GetBoxedData(), (int)abst.GetBoxedDataSize()); + return Result; +} + +std::string mdatFold(std::string data){ + std::string Result; + unsigned int t_int; + t_int = htonl(data.size()+8); + Result.append((char*)&t_int, 4); + t_int = htonl(0x6D646174); + Result.append((char*)&t_int, 4); + Result.append(data); + return Result; +} + +}; diff --git a/lib/mp4.h b/lib/mp4.h new file mode 100644 index 00000000..b4ec5417 --- /dev/null +++ b/lib/mp4.h @@ -0,0 +1,132 @@ +#pragma once +#include +#include +#include + +/// Contains all MP4 format related code. +namespace MP4{ + + class Box { + public: + Box(); + Box(uint32_t BoxType); + Box(uint8_t * Content, uint32_t length); + ~Box(); + void SetBoxType(uint32_t BoxType); + uint32_t GetBoxType(); + void SetPayload(uint32_t Size, uint8_t * Data, uint32_t Index = 0); + uint32_t GetPayloadSize(); + uint8_t * GetPayload(); + uint8_t * GetPayload(uint32_t Index, uint32_t & Size); + uint32_t GetBoxedDataSize(); + uint8_t * GetBoxedData( ); + static uint8_t * uint32_to_uint8( uint32_t data ); + static uint8_t * uint16_to_uint8( uint16_t data ); + static uint8_t * uint8_to_uint8( uint8_t data ); + void ResetPayload( ); + private: + uint8_t * Payload; + uint32_t PayloadSize; + };//Box Class + + struct abst_serverentry { + std::string ServerBaseUrl; + };//abst_serverentry + + struct abst_qualityentry { + std::string QualityModifier; + };//abst_qualityentry + + /// ABST Box class + class ABST: public Box { + public: + ABST() : Box(0x61627374){}; + void SetBootstrapVersion( uint32_t Version = 1 ); + void SetProfile( uint8_t Profile = 0 ); + void SetLive( bool Live = true ); + void SetUpdate( bool Update = false ); + void SetTimeScale( uint32_t Scale = 1000 ); + void SetMediaTime( uint32_t Time = 0 ); + void SetSMPTE( uint32_t Smpte = 0 ); + void SetMovieIdentifier( std::string Identifier = "" ); + void SetDRM( std::string Drm = "" ); + void SetMetaData( std::string MetaData = "" ); + void AddServerEntry( std::string Url = "", uint32_t Offset = 0 ); + void AddQualityEntry( std::string Quality = "", uint32_t Offset = 0 ); + void AddSegmentRunTable( Box * newSegment, uint32_t Offset = 0 ); + void AddFragmentRunTable( Box * newFragment, uint32_t Offset = 0 ); + void SetVersion( bool NewVersion = 0 ); + void WriteContent( ); + private: + void SetDefaults( ); + void SetReserved( ); + uint32_t curBootstrapInfoVersion; + uint8_t curProfile; + bool isLive; + bool isUpdate; + bool Version; + uint32_t curTimeScale; + uint32_t curMediatime;//write as uint64_t + uint32_t curSMPTE;//write as uint64_t + std::string curMovieIdentifier; + std::string curDRM; + std::string curMetaData; + std::vector Servers; + std::vector Qualities; + std::vector SegmentRunTables; + std::vector FragmentRunTables; + Box * Container; + };//ABST Box + + struct afrt_fragmentrunentry { + uint32_t FirstFragment; + uint32_t FirstFragmentTimestamp; //write as uint64_t + uint32_t FragmentDuration; + uint8_t DiscontinuityIndicator;//if FragmentDuration == 0 + };//afrt_fragmentrunentry + + + /// AFRT Box class + class AFRT : public Box { + public: + AFRT() : Box(0x61667274){}; + void SetUpdate( bool Update = false ); + void SetTimeScale( uint32_t Scale = 1000 ); + void AddQualityEntry( std::string Quality = "", uint32_t Offset = 0 ); + void AddFragmentRunEntry( uint32_t FirstFragment = 0, uint32_t FirstFragmentTimestamp = 0, uint32_t FragmentsDuration = 1, uint8_t Discontinuity = 0, uint32_t Offset = 0 ); + void WriteContent( ); + private: + void SetDefaults( ); + bool isUpdate; + uint32_t curTimeScale; + std::vector QualitySegmentUrlModifiers; + std::vector FragmentRunEntryTable; + };//AFRT Box + + struct asrt_segmentrunentry { + uint32_t FirstSegment; + uint32_t FragmentsPerSegment; + };//abst_qualityentry + + /// ASRT Box class + class ASRT : public Box { + public: + ASRT() : Box(0x61737274){}; + void SetUpdate( bool Update = false ); + void AddQualityEntry( std::string Quality = "", uint32_t Offset = 0 ); + void AddSegmentRunEntry( uint32_t FirstSegment = 0, uint32_t FragmentsPerSegment = 100, uint32_t Offset = 0 ); + void WriteContent( ); + void SetVersion( bool NewVersion = 0 ); + private: + void SetDefaults( ); + bool isUpdate; + bool Version; + std::vector QualitySegmentUrlModifiers; + std::vector SegmentRunEntryTable; + Box * Container; + };//ASRT Box + + std::string GenerateLiveBootstrap( uint32_t CurMediaTime ); + std::string mdatFold(std::string data); + +}; From a0528c331f44f7a9c4b9ede3e2130aa183db01fc Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 8 May 2012 22:43:37 +0200 Subject: [PATCH 195/788] Final edits to bring all analysers and converters into autotools build system. Done! --- configure.ac | 2 ++ 1 file changed, 2 insertions(+) diff --git a/configure.ac b/configure.ac index 1bce87ef..6bc6a611 100644 --- a/configure.ac +++ b/configure.ac @@ -35,5 +35,7 @@ AC_CHECK_FUNCS([dup2 gettimeofday memset mkdir socket strerror]) AC_CONFIG_FILES([Makefile lib/Makefile + src/converters/Makefile + src/analysers/Makefile src/Makefile]) AC_OUTPUT From b90c4e2623ff5bc5e08325582e4c060fbd412a33 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 11 May 2012 15:33:09 +0200 Subject: [PATCH 196/788] Further tweaks to autotools config. --- AUTHORS | 4 + COPYING | 661 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ ChangeLog | 7 + NEWS | 7 + README | 5 + 5 files changed, 684 insertions(+) create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 NEWS create mode 100644 README diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 00000000..7c10c6cd --- /dev/null +++ b/AUTHORS @@ -0,0 +1,4 @@ +tinythread++ library taken verbatim from: +http://tinythread.sourceforge.net + +All other code so far was written by DDVTECH employees. diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..dba13ed2 --- /dev/null +++ b/COPYING @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 00000000..f1ad0c0f --- /dev/null +++ b/ChangeLog @@ -0,0 +1,7 @@ +This is a build from the Mistserver git repository located at: +https://github.com/DDVTECH/DMS + +For a full changelog please see the repository history. + +The version of this build can be found in the version.m4 file and +is a valid checkout point for the above mentioned repository. diff --git a/NEWS b/NEWS new file mode 100644 index 00000000..f1ad0c0f --- /dev/null +++ b/NEWS @@ -0,0 +1,7 @@ +This is a build from the Mistserver git repository located at: +https://github.com/DDVTECH/DMS + +For a full changelog please see the repository history. + +The version of this build can be found in the version.m4 file and +is a valid checkout point for the above mentioned repository. diff --git a/README b/README new file mode 100644 index 00000000..a876e172 --- /dev/null +++ b/README @@ -0,0 +1,5 @@ +For full documentation as well as background information, visit our wiki at: +http://wiki.mistserver.com/ + +Code contributions and bug reports are welcomed through: +https://github.com/DDVTECH/DMS From cd0d79790bee82003c49920f753e0dbfb4d17081 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 11 May 2012 15:45:29 +0200 Subject: [PATCH 197/788] Fix null-array bug in JSON lib as well as flv_tag compile warnings. --- lib/flv_tag.cpp | 14 +++++++------- lib/json.cpp | 8 ++++++-- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index 919772f0..65f58b10 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -795,25 +795,25 @@ DTSC::DTMI FLV::Tag::toDTSC(DTSC::DTMI & metadata){ } } if (tmp->getContentP("width")){ - Meta_Put(metadata, "video", "width", tmp->getContentP("width")->NumValue()); + Meta_Put(metadata, "video", "width", (unsigned long long int)tmp->getContentP("width")->NumValue()); } if (tmp->getContentP("height")){ - Meta_Put(metadata, "video", "height", tmp->getContentP("height")->NumValue()); + Meta_Put(metadata, "video", "height", (unsigned long long int)tmp->getContentP("height")->NumValue()); } if (tmp->getContentP("framerate")){ - Meta_Put(metadata, "video", "fpks", tmp->getContentP("framerate")->NumValue()*1000); + Meta_Put(metadata, "video", "fpks", (unsigned long long int)tmp->getContentP("framerate")->NumValue()*1000); } if (tmp->getContentP("videodatarate")){ - Meta_Put(metadata, "video", "bps", (tmp->getContentP("videodatarate")->NumValue()*1024)/8); + Meta_Put(metadata, "video", "bps", (unsigned long long int)(tmp->getContentP("videodatarate")->NumValue()*1024)/8); } if (tmp->getContentP("audiodatarate")){ - Meta_Put(metadata, "audio", "bps", (tmp->getContentP("audiodatarate")->NumValue()*1024)/8); + Meta_Put(metadata, "audio", "bps", (unsigned long long int)(tmp->getContentP("audiodatarate")->NumValue()*1024)/8); } if (tmp->getContentP("audiosamplerate")){ - Meta_Put(metadata, "audio", "rate", tmp->getContentP("audiosamplerate")->NumValue()); + Meta_Put(metadata, "audio", "rate", (unsigned long long int)tmp->getContentP("audiosamplerate")->NumValue()); } if (tmp->getContentP("audiosamplesize")){ - Meta_Put(metadata, "audio", "size", tmp->getContentP("audiosamplesize")->NumValue()); + Meta_Put(metadata, "audio", "size", (unsigned long long int)tmp->getContentP("audiosamplesize")->NumValue()); } if (tmp->getContentP("stereo")){ if (tmp->getContentP("stereo")->NumValue() == 1){ diff --git a/lib/json.cpp b/lib/json.cpp index 8d4b319f..6fe60c84 100644 --- a/lib/json.cpp +++ b/lib/json.cpp @@ -98,12 +98,16 @@ JSON::Value::Value(std::istream & fromstream){ c = fromstream.get(); myType = OBJECT; break; - case '[': + case '[':{ reading_array = true; c = fromstream.get(); myType = ARRAY; - append(JSON::Value(fromstream)); + Value tmp = JSON::Value(fromstream); + if (tmp.myType != EMPTY){ + append(tmp); + } break; + } case '\'': case '"': c = fromstream.get(); From a086cff5799fd3748a5fee544dbb1e4427f0285b Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 11 May 2012 16:50:30 +0200 Subject: [PATCH 198/788] Fixed documentation, removed a few useless files, changed MD5 implementation to use openssl. --- AUTHORS | 5 +- Makefile.am | 3 + lib/Makefile.am | 3 +- lib/base64.cpp | 2 +- lib/md5.cpp | 362 ----------------------------------------------- lib/md5.h | 93 ------------ lib/tinythread.h | 52 ------- 7 files changed, 7 insertions(+), 513 deletions(-) delete mode 100644 lib/md5.cpp delete mode 100644 lib/md5.h diff --git a/AUTHORS b/AUTHORS index 7c10c6cd..915355b6 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,4 +1,3 @@ -tinythread++ library taken verbatim from: -http://tinythread.sourceforge.net +All code was written by DDVTECH employees specifically for this project, with the exception of: -All other code so far was written by DDVTECH employees. +* tinythread++ library taken verbatim from: http://tinythread.sourceforge.net diff --git a/Makefile.am b/Makefile.am index 6cf1b795..e1d5774b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,2 +1,5 @@ SUBDIRS=lib src EXTRA_DIST=server.html +docs: + doxygen ./Doxyfile > /dev/null +.PHONY: docs diff --git a/lib/Makefile.am b/lib/Makefile.am index d7a351a3..265f87da 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,4 +1,4 @@ -noinst_LIBRARIES=libamf.a libauth.a libbase64.a libconfig.a libcrypto.a libdtsc.a libflv_tag.a libhttp_parser.a libjson.a libmd5.a libprocs.a librtmpchunks.a libsocket.a libtinythread.a libmp4.a +noinst_LIBRARIES=libamf.a libauth.a libbase64.a libconfig.a libcrypto.a libdtsc.a libflv_tag.a libhttp_parser.a libjson.a libprocs.a librtmpchunks.a libsocket.a libtinythread.a libmp4.a libamf_a_SOURCES=amf.h amf.cpp libauth_a_SOURCES=auth.h auth.cpp libbase64_a_SOURCES=base64.h base64.cpp @@ -8,7 +8,6 @@ libdtsc_a_SOURCES=dtsc.h dtsc.cpp libflv_tag_a_SOURCES=flv_tag.h flv_tag.cpp libhttp_parser_a_SOURCES=http_parser.h http_parser.cpp libjson_a_SOURCES=json.h json.cpp -libmd5_a_SOURCES=md5.h md5.cpp libprocs_a_SOURCES=procs.h procs.cpp librtmpchunks_a_SOURCES=rtmpchunks.h rtmpchunks.cpp libsocket_a_SOURCES=socket.h socket.cpp diff --git a/lib/base64.cpp b/lib/base64.cpp index 0111823f..a2c9dec3 100644 --- a/lib/base64.cpp +++ b/lib/base64.cpp @@ -32,7 +32,7 @@ std::string Base64::encode(std::string const input) { }//base64_encode /// Used to base64 decode data. Input is the encoded data as std::string, output is the plaintext data as std::string. -/// \param input Base64 encoded data to decode. +/// \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(); diff --git a/lib/md5.cpp b/lib/md5.cpp deleted file mode 100644 index cdeb5e33..00000000 --- a/lib/md5.cpp +++ /dev/null @@ -1,362 +0,0 @@ -/* MD5 - converted to C++ class by Frank Thilo (thilo@unix-ag.org) - for bzflag (http://www.bzflag.org) - - based on: - - md5.h and md5.c - reference implemantion of RFC 1321 - - Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All -rights reserved. - -License to copy and use this software is granted provided that it -is identified as the "RSA Data Security, Inc. MD5 Message-Digest -Algorithm" in all material mentioning or referencing this software -or this function. - -License is also granted to make and use derivative works provided -that such works are identified as "derived from the RSA Data -Security, Inc. MD5 Message-Digest Algorithm" in all material -mentioning or referencing the derived work. - -RSA Data Security, Inc. makes no representations concerning either -the merchantability of this software or the suitability of this -software for any particular purpose. It is provided "as is" -without express or implied warranty of any kind. - -These notices must be retained in any copies of any part of this -documentation and/or software. - -*/ - -/* interface header */ -#include "md5.h" - -/* system implementation headers */ -#include - - -// Constants for MD5Transform routine. -#define S11 7 -#define S12 12 -#define S13 17 -#define S14 22 -#define S21 5 -#define S22 9 -#define S23 14 -#define S24 20 -#define S31 4 -#define S32 11 -#define S33 16 -#define S34 23 -#define S41 6 -#define S42 10 -#define S43 15 -#define S44 21 - -/////////////////////////////////////////////// - -// F, G, H and I are basic MD5 functions. -inline MD5::uint4 MD5::F(uint4 x, uint4 y, uint4 z) { - return x&y | ~x&z; -} - -inline MD5::uint4 MD5::G(uint4 x, uint4 y, uint4 z) { - return x&z | y&~z; -} - -inline MD5::uint4 MD5::H(uint4 x, uint4 y, uint4 z) { - return x^y^z; -} - -inline MD5::uint4 MD5::I(uint4 x, uint4 y, uint4 z) { - return y ^ (x | ~z); -} - -// rotate_left rotates x left n bits. -inline MD5::uint4 MD5::rotate_left(uint4 x, int n) { - return (x << n) | (x >> (32-n)); -} - -// FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. -// Rotation is separate from addition to prevent recomputation. -inline void MD5::FF(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { - a = rotate_left(a+ F(b,c,d) + x + ac, s) + b; -} - -inline void MD5::GG(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { - a = rotate_left(a + G(b,c,d) + x + ac, s) + b; -} - -inline void MD5::HH(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { - a = rotate_left(a + H(b,c,d) + x + ac, s) + b; -} - -inline void MD5::II(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { - a = rotate_left(a + I(b,c,d) + x + ac, s) + b; -} - -////////////////////////////////////////////// - -// default ctor, just initailize -MD5::MD5() -{ - init(); -} - -////////////////////////////////////////////// - -// nifty shortcut ctor, compute MD5 for string and finalize it right away -MD5::MD5(const std::string &text) -{ - init(); - update(text.c_str(), text.length()); - finalize(); -} - -////////////////////////////// - -void MD5::init() -{ - finalized=false; - - count[0] = 0; - count[1] = 0; - - // load magic initialization constants. - state[0] = 0x67452301; - state[1] = 0xefcdab89; - state[2] = 0x98badcfe; - state[3] = 0x10325476; -} - -////////////////////////////// - -// decodes input (unsigned char) into output (uint4). Assumes len is a multiple of 4. -void MD5::decode(uint4 output[], const uint1 input[], size_type len) -{ - for (unsigned int i = 0, j = 0; j < len; i++, j += 4) - output[i] = ((uint4)input[j]) | (((uint4)input[j+1]) << 8) | - (((uint4)input[j+2]) << 16) | (((uint4)input[j+3]) << 24); -} - -////////////////////////////// - -// encodes input (uint4) into output (unsigned char). Assumes len is -// a multiple of 4. -void MD5::encode(uint1 output[], const uint4 input[], size_type len) -{ - for (size_type i = 0, j = 0; j < len; i++, j += 4) { - output[j] = input[i] & 0xff; - output[j+1] = (input[i] >> 8) & 0xff; - output[j+2] = (input[i] >> 16) & 0xff; - output[j+3] = (input[i] >> 24) & 0xff; - } -} - -////////////////////////////// - -// apply MD5 algo on a block -void MD5::transform(const uint1 block[blocksize]) -{ - uint4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; - decode (x, block, blocksize); - - /* Round 1 */ - FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ - FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ - FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ - FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ - FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ - FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ - FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ - FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ - FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ - FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ - FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ - FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ - FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ - FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ - FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ - FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ - - /* Round 2 */ - GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ - GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ - GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ - GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ - GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ - GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ - GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ - GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ - GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ - GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ - GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ - GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ - GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ - GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ - GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ - GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ - - /* Round 3 */ - HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ - HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ - HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ - HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ - HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ - HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ - HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ - HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ - HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ - HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ - HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ - HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ - HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ - HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ - HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ - HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ - - /* Round 4 */ - II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ - II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ - II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ - II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ - II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ - II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ - II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ - II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ - II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ - II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ - II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ - II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ - II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ - II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ - II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ - II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ - - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - - // Zeroize sensitive information. - memset(x, 0, sizeof x); -} - -////////////////////////////// - -// MD5 block update operation. Continues an MD5 message-digest -// operation, processing another message block -void MD5::update(const unsigned char input[], size_type length) -{ - // compute number of bytes mod 64 - size_type index = count[0] / 8 % blocksize; - - // Update number of bits - if ((count[0] += (length << 3)) < (length << 3)) - count[1]++; - count[1] += (length >> 29); - - // number of bytes we need to fill in buffer - size_type firstpart = 64 - index; - - size_type i; - - // transform as many times as possible. - if (length >= firstpart) - { - // fill buffer first, transform - memcpy(&buffer[index], input, firstpart); - transform(buffer); - - // transform chunks of blocksize (64 bytes) - for (i = firstpart; i + blocksize <= length; i += blocksize) - transform(&input[i]); - - index = 0; - } - else - i = 0; - - // buffer remaining input - memcpy(&buffer[index], &input[i], length-i); -} - -////////////////////////////// - -// for convenience provide a verson with signed char -void MD5::update(const char input[], size_type length) -{ - update((const unsigned char*)input, length); -} - -////////////////////////////// - -// MD5 finalization. Ends an MD5 message-digest operation, writing the -// the message digest and zeroizing the context. -MD5& MD5::finalize() -{ - static unsigned char padding[64] = { - 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; - - if (!finalized) { - // Save number of bits - unsigned char bits[8]; - encode(bits, count, 8); - - // pad out to 56 mod 64. - size_type index = count[0] / 8 % 64; - size_type padLen = (index < 56) ? (56 - index) : (120 - index); - update(padding, padLen); - - // Append length (before padding) - update(bits, 8); - - // Store state in digest - encode(digest, state, 16); - - // Zeroize sensitive information. - memset(buffer, 0, sizeof buffer); - memset(count, 0, sizeof count); - - finalized=true; - } - - return *this; -} - -////////////////////////////// - -// return hex representation of digest as string -std::string MD5::hexdigest() const -{ - if (!finalized) - return ""; - - char buf[33]; - for (int i=0; i<16; i++) - sprintf(buf+i*2, "%02x", digest[i]); - buf[32]=0; - - return std::string(buf); -} - -////////////////////////////// - -std::ostream& operator<<(std::ostream& out, MD5 md5) -{ - return out << md5.hexdigest(); -} - -////////////////////////////// - -std::string md5(const std::string str) -{ - MD5 md5 = MD5(str); - - return md5.hexdigest(); -} diff --git a/lib/md5.h b/lib/md5.h deleted file mode 100644 index 3d7cac95..00000000 --- a/lib/md5.h +++ /dev/null @@ -1,93 +0,0 @@ -/* MD5 - converted to C++ class by Frank Thilo (thilo@unix-ag.org) - for bzflag (http://www.bzflag.org) - - based on: - - md5.h and md5.c - reference implementation of RFC 1321 - - Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All -rights reserved. - -License to copy and use this software is granted provided that it -is identified as the "RSA Data Security, Inc. MD5 Message-Digest -Algorithm" in all material mentioning or referencing this software -or this function. - -License is also granted to make and use derivative works provided -that such works are identified as "derived from the RSA Data -Security, Inc. MD5 Message-Digest Algorithm" in all material -mentioning or referencing the derived work. - -RSA Data Security, Inc. makes no representations concerning either -the merchantability of this software or the suitability of this -software for any particular purpose. It is provided "as is" -without express or implied warranty of any kind. - -These notices must be retained in any copies of any part of this -documentation and/or software. - -*/ - -#ifndef BZF_MD5_H -#define BZF_MD5_H - -#include -#include -#include - -// a small class for calculating MD5 hashes of strings or byte arrays -// it is not meant to be fast or secure -// -// usage: 1) feed it blocks of uchars with update() -// 2) finalize() -// 3) get hexdigest() string -// or -// MD5(std::string).hexdigest() -// -// assumes that char is 8 bit and int is 32 bit -class MD5 -{ -public: - typedef unsigned int size_type; // must be 32bit - - MD5(); - MD5(const std::string& text); - void update(const unsigned char *buf, size_type length); - void update(const char *buf, size_type length); - MD5& finalize(); - std::string hexdigest() const; - friend std::ostream& operator<<(std::ostream&, MD5 md5); - -private: - void init(); - typedef unsigned char uint1; // 8bit - typedef unsigned int uint4; // 32bit - enum {blocksize = 64}; // VC6 won't eat a const static int here - - void transform(const uint1 block[blocksize]); - static void decode(uint4 output[], const uint1 input[], size_type len); - static void encode(uint1 output[], const uint4 input[], size_type len); - - bool finalized; - uint1 buffer[blocksize]; // bytes that didn't fit in last 64 byte chunk - uint4 count[2]; // 64bit counter for number of bits (lo, hi) - uint4 state[4]; // digest so far - uint1 digest[16]; // the result - - // low level logic operations - static inline uint4 F(uint4 x, uint4 y, uint4 z); - static inline uint4 G(uint4 x, uint4 y, uint4 z); - static inline uint4 H(uint4 x, uint4 y, uint4 z); - static inline uint4 I(uint4 x, uint4 y, uint4 z); - static inline uint4 rotate_left(uint4 x, int n); - static inline void FF(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); - static inline void GG(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); - static inline void HH(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); - static inline void II(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); -}; - -std::string md5(const std::string str); - -#endif diff --git a/lib/tinythread.h b/lib/tinythread.h index 723370cf..494e1ec4 100644 --- a/lib/tinythread.h +++ b/lib/tinythread.h @@ -24,37 +24,6 @@ freely, subject to the following restrictions: #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++0x 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++0x 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__) @@ -106,27 +75,6 @@ freely, subject to the following restrictions: 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++0x 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++0x -/// 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_CPP0X_) && !defined(thread_local) #if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_CC) || defined(__IBMCPP__) #define thread_local __thread From 995a20b91ea5dcff69e5b43dfb9cf3c342a3f80d Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 11 May 2012 18:08:17 +0200 Subject: [PATCH 199/788] Re-enable debugging option, fix chars back to unsigned. --- configure.ac | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/configure.ac b/configure.ac index 6bc6a611..b76dfb23 100644 --- a/configure.ac +++ b/configure.ac @@ -33,6 +33,12 @@ AC_FUNC_MALLOC AC_FUNC_REALLOC AC_CHECK_FUNCS([dup2 gettimeofday memset mkdir socket strerror]) +# Fix chars to unsigned +CXXFLAGS="$CXXFLAGS -funsigned-char" + +#allow verbose mode compiles +AC_ARG_ENABLE([verbose], AC_HELP_STRING([--enable-verbose], [Compile with verbose messages]), CXXFLAGS="-DDEBUG=4 $CXXFLAGS") + AC_CONFIG_FILES([Makefile lib/Makefile src/converters/Makefile From b19c800f3408e561935deb4c93c40def083d4a9a Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 11 May 2012 18:39:24 +0200 Subject: [PATCH 200/788] Fix FLV2DTSC binary and add some checks in the flv_tag lib. --- lib/flv_tag.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index 65f58b10..6a70cc06 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -866,8 +866,10 @@ DTSC::DTMI FLV::Tag::toDTSC(DTSC::DTMI & metadata){ } } if ((audiodata & 0xF0) == 0xA0){ + if (len < 18){return DTSC::DTMI();} pack_out.addContent(DTSC::DTMI("data", std::string((char*)data+13, (size_t)len-17))); }else{ + if (len < 17){return DTSC::DTMI();} pack_out.addContent(DTSC::DTMI("data", std::string((char*)data+12, (size_t)len-16))); } return pack_out; @@ -876,8 +878,10 @@ DTSC::DTMI FLV::Tag::toDTSC(DTSC::DTMI & metadata){ char videodata = data[11]; if (needsInitData() && isInitData()){ if ((videodata & 0x0F) == 7){ + if (len < 21){return DTSC::DTMI();} Meta_Put(metadata, "video", "init", std::string((char*)data+16, (size_t)len-20)); }else{ + if (len < 17){return DTSC::DTMI();} Meta_Put(metadata, "video", "init", std::string((char*)data+12, (size_t)len-16)); } return pack_out;//skip rest of parsing, get next tag. @@ -908,8 +912,10 @@ DTSC::DTMI FLV::Tag::toDTSC(DTSC::DTMI & metadata){ int offset = (data[13] << 16) + (data[14] << 8) + data[15]; offset = (offset << 8) >> 8; pack_out.addContent(DTSC::DTMI("offset", offset)); + if (len < 21){return DTSC::DTMI();} pack_out.addContent(DTSC::DTMI("data", std::string((char*)data+16, (size_t)len-20))); }else{ + if (len < 17){return DTSC::DTMI();} pack_out.addContent(DTSC::DTMI("data", std::string((char*)data+12, (size_t)len-16))); } return pack_out; From bb9d95880c197c81ca1b4ade203a766eb242f802 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 11 May 2012 19:01:39 +0200 Subject: [PATCH 201/788] Improve notation for version numbers. --- lib/config.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/config.cpp b/lib/config.cpp index f2d9fe17..da541832 100644 --- a/lib/config.cpp +++ b/lib/config.cpp @@ -53,7 +53,7 @@ void Util::Config::parseArgs(int argc, char ** argv){ case 'd': daemon_mode = true; break; case 'u': username = optarg; break; case 'v': - printf("%s\n", TOSTRING(PACKAGE_VERSION)); + printf("%s\n", PACKAGE_VERSION); exit(1); break; case 'h': @@ -63,7 +63,7 @@ void Util::Config::parseArgs(int argc, char ** argv){ printf("Options: -h[elp], -?, -v[ersion], -n[odaemon], -d[aemon], -p[ort] VAL, -i[nterface] VAL, -u[sername] VAL\n"); printf("Defaults:\n interface: %s\n port: %i\n daemon mode: %s\n username: %s\n", interface.c_str(), listen_port, doingdaemon.c_str(), username.c_str()); printf("Username root means no change to UID, no matter what the UID is.\n"); - printf("This is %s version %s\n", argv[0], TOSTRING(PACKAGE_VERSION)); + printf("This is %s version %s\n", argv[0], PACKAGE_VERSION); exit(1); break; } From ab23344a85c07a4e14458d5b196ca24ef5647679 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 14 May 2012 16:17:15 +0200 Subject: [PATCH 202/788] Should fix compile problems. --- Makefile.am | 1 + configure.ac | 4 +++- lib/Makefile.am | 35 ++++++++++++++++++++--------------- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/Makefile.am b/Makefile.am index e1d5774b..6ccf77b6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,3 +1,4 @@ +ACLOCAL_AMFLAGS=-I m4 SUBDIRS=lib src EXTRA_DIST=server.html docs: diff --git a/configure.ac b/configure.ac index b76dfb23..2c2cd76f 100644 --- a/configure.ac +++ b/configure.ac @@ -5,14 +5,16 @@ AC_PREREQ([2.60]) m4_include([version.m4]) AC_INIT(MistServer, VERSION_NUMBER, contact@ddvtech.com) AC_CONFIG_SRCDIR([src/buffer.cpp]) +AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE +LT_INIT # Checks for programs. AC_PROG_CXX AC_PROG_CC # Checks for libraries. -AC_PROG_RANLIB +AC_CHECK_LIB(ssl, RC4) # Checks for header files. AC_CHECK_HEADERS([arpa/inet.h fcntl.h netdb.h netinet/in.h stdint.h stdlib.h string.h sys/socket.h sys/time.h unistd.h]) diff --git a/lib/Makefile.am b/lib/Makefile.am index 265f87da..2a2f0d1f 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,15 +1,20 @@ -noinst_LIBRARIES=libamf.a libauth.a libbase64.a libconfig.a libcrypto.a libdtsc.a libflv_tag.a libhttp_parser.a libjson.a libprocs.a librtmpchunks.a libsocket.a libtinythread.a libmp4.a -libamf_a_SOURCES=amf.h amf.cpp -libauth_a_SOURCES=auth.h auth.cpp -libbase64_a_SOURCES=base64.h base64.cpp -libconfig_a_SOURCES=config.h config.cpp -libcrypto_a_SOURCES=crypto.h crypto.cpp -libdtsc_a_SOURCES=dtsc.h dtsc.cpp -libflv_tag_a_SOURCES=flv_tag.h flv_tag.cpp -libhttp_parser_a_SOURCES=http_parser.h http_parser.cpp -libjson_a_SOURCES=json.h json.cpp -libprocs_a_SOURCES=procs.h procs.cpp -librtmpchunks_a_SOURCES=rtmpchunks.h rtmpchunks.cpp -libsocket_a_SOURCES=socket.h socket.cpp -libtinythread_a_SOURCES=tinythread.h tinythread.cpp -libmp4_a_SOURCES=mp4.h mp4.cpp +noinst_LTLIBRARIES=libamf.la libauth.la libbase64.la libconfig.la libcrypto.la libdtsc.la libflv_tag.la libhttp_parser.la libjson.la libprocs.la librtmpchunks.la libsocket.la libtinythread.la libmp4.la +libamf_la_SOURCES=amf.h amf.cpp +libauth_la_SOURCES=auth.h auth.cpp +libauth_la_LIBADD=-lssl -lcrypto +libbase64_la_SOURCES=base64.h base64.cpp +libconfig_la_SOURCES=config.h config.cpp +libcrypto_la_SOURCES=crypto.h crypto.cpp +libcrypto_la_LIBADD=-lssl -lcrypto +libdtsc_la_SOURCES=dtsc.h dtsc.cpp +libflv_tag_la_SOURCES=flv_tag.h flv_tag.cpp +libflv_tag_la_LIBADD=./libamf.la ./libsocket.la +libhttp_parser_la_SOURCES=http_parser.h http_parser.cpp +libjson_la_SOURCES=json.h json.cpp +libprocs_la_SOURCES=procs.h procs.cpp +librtmpchunks_la_SOURCES=rtmpchunks.h rtmpchunks.cpp +librtmpchunks_la_LIBADD=./libflv_tag.la -lssl -lcrypto +libsocket_la_SOURCES=socket.h socket.cpp +libtinythread_la_SOURCES=tinythread.h tinythread.cpp +libtinythread_la_LIBADD=-lpthread +libmp4_la_SOURCES=mp4.h mp4.cpp From c9aa8c1ed41ef695371668dd3dcf96472c754e49 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 14 May 2012 17:38:31 +0200 Subject: [PATCH 203/788] Fixed initialization issue in Buffer. --- README | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README b/README index a876e172..b39f75ee 100644 --- a/README +++ b/README @@ -3,3 +3,7 @@ http://wiki.mistserver.com/ Code contributions and bug reports are welcomed through: https://github.com/DDVTECH/DMS + +The following configure options are possible: +--enable-verbose = Compiles all applications in verbose mode, printing a lot more information to the screen than normally. +--disable-verbose = The opposite of above (default). From ccfcdeba2988472c9c2b7a5edee0005fc40dc045 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 14 May 2012 18:18:15 +0200 Subject: [PATCH 204/788] Should fix socket binding issues in cygwin. --- lib/socket.cpp | 47 ++++++++++++++++++++++++++++++++++------------- lib/socket.h | 4 +++- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/lib/socket.cpp b/lib/socket.cpp index 82c52dce..bf1eabda 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -463,12 +463,24 @@ Socket::Server::Server(){ /// \param hostname (optional) The interface to bind to. The default is 0.0.0.0 (all interfaces). /// \param nonblock (optional) Whether accept() calls will be nonblocking. Default is false (blocking). Socket::Server::Server(int port, std::string hostname, bool nonblock){ + if (!IPv6bind(port, hostname, nonblock) && !IPv4bind(port, hostname, nonblock)){ + fprintf(stderr, "Could not create socket %s:%i! Error: %s\n", hostname.c_str(), port, strerror(errno)); + sock = -1; + } +}//Socket::Server TCP Constructor + +/// Attempt to bind an IPv6 socket. +/// \param port The TCP port to listen on +/// \param hostname The interface to bind to. The default is 0.0.0.0 (all interfaces). +/// \param nonblock Whether accept() calls will be nonblocking. Default is false (blocking). +/// \return True if successful, false otherwise. +bool Socket::Server::IPv6bind(int port, std::string hostname, bool nonblock){ sock = socket(AF_INET6, SOCK_STREAM, 0); if (sock < 0){ #if DEBUG >= 1 fprintf(stderr, "Could not create socket %s:%i! Error: %s\n", hostname.c_str(), port, strerror(errno)); #endif - return; + return false; } int on = 1; setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); @@ -489,28 +501,37 @@ Socket::Server::Server(int port, std::string hostname, bool nonblock){ if (ret == 0){ ret = listen(sock, 100);//start listening, backlog of 100 allowed if (ret == 0){ - return; + return true; }else{ #if DEBUG >= 1 fprintf(stderr, "Listen failed! Error: %s\n", strerror(errno)); #endif close(); - return; + return false; } }else{ #if DEBUG >= 1 - fprintf(stderr, "Binding %s:%i failed, retrying as IPv4... (%s)\n", hostname.c_str(), port, strerror(errno)); + fprintf(stderr, "Binding %s:%i failed (%s)\n", hostname.c_str(), port, strerror(errno)); #endif close(); + return false; } +} + +/// Attempt to bind an IPv4 socket. +/// \param port The TCP port to listen on +/// \param hostname The interface to bind to. The default is 0.0.0.0 (all interfaces). +/// \param nonblock Whether accept() calls will be nonblocking. Default is false (blocking). +/// \return True if successful, false otherwise. +bool Socket::Server::IPv4bind(int port, std::string hostname, bool nonblock){ sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0){ #if DEBUG >= 1 - fprintf(stderr, "Could not create socket %s:%i! Error: %s\n", hostname.c_str(), port, strerror(errno)); + fprintf(stderr, "Could not create IPv4 socket %s:%i! Error: %s\n", hostname.c_str(), port, strerror(errno)); #endif - return; + return false; } - on = 1; + int on = 1; setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); if (nonblock){ int flags = fcntl(sock, F_GETFL, 0); @@ -525,26 +546,26 @@ Socket::Server::Server(int port, std::string hostname, bool nonblock){ }else{ inet_pton(AF_INET, hostname.c_str(), &addr4.sin_addr);//set interface, 0.0.0.0 (default) is all } - ret = bind(sock, (sockaddr*)&addr4, sizeof(addr4));//do the actual bind + int ret = bind(sock, (sockaddr*)&addr4, sizeof(addr4));//do the actual bind if (ret == 0){ ret = listen(sock, 100);//start listening, backlog of 100 allowed if (ret == 0){ - return; + return true; }else{ #if DEBUG >= 1 fprintf(stderr, "Listen failed! Error: %s\n", strerror(errno)); #endif close(); - return; + return false; } }else{ #if DEBUG >= 1 - fprintf(stderr, "IPv4 binding %s:%i also failed, giving up. (%s)\n", hostname.c_str(), port, strerror(errno)); + fprintf(stderr, "IPv4 binding %s:%i failed (%s)\n", hostname.c_str(), port, strerror(errno)); #endif close(); - return; + return false; } -}//Socket::Server TCP Constructor +} /// Create a new Unix Server. The socket is immediately bound and set to listen. /// A maximum of 100 connections will be accepted between accept() calls. diff --git a/lib/socket.h b/lib/socket.h index 03750b2a..9c88a248 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -68,7 +68,9 @@ namespace Socket{ class Server{ private: int sock; ///< Internally saved socket number. - public: + 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. From 4d9db0d6ee7d21b3e1bcad5f17fae6ab05635f6e Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 22 May 2012 11:41:22 +0200 Subject: [PATCH 205/788] Further improve socket lib errrors. --- lib/socket.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/socket.cpp b/lib/socket.cpp index bf1eabda..63cf624b 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -478,7 +478,7 @@ bool Socket::Server::IPv6bind(int port, std::string hostname, bool nonblock){ sock = socket(AF_INET6, SOCK_STREAM, 0); if (sock < 0){ #if DEBUG >= 1 - fprintf(stderr, "Could not create socket %s:%i! Error: %s\n", hostname.c_str(), port, strerror(errno)); + fprintf(stderr, "Could not create IPv6 socket %s:%i! Error: %s\n", hostname.c_str(), port, strerror(errno)); #endif return false; } @@ -504,14 +504,14 @@ bool Socket::Server::IPv6bind(int port, std::string hostname, bool nonblock){ return true; }else{ #if DEBUG >= 1 - fprintf(stderr, "Listen failed! Error: %s\n", strerror(errno)); + fprintf(stderr, "IPv6 Listen failed! Error: %s\n", strerror(errno)); #endif close(); return false; } }else{ #if DEBUG >= 1 - fprintf(stderr, "Binding %s:%i failed (%s)\n", hostname.c_str(), port, strerror(errno)); + fprintf(stderr, "IPv6 Binding %s:%i failed (%s)\n", hostname.c_str(), port, strerror(errno)); #endif close(); return false; @@ -553,7 +553,7 @@ bool Socket::Server::IPv4bind(int port, std::string hostname, bool nonblock){ return true; }else{ #if DEBUG >= 1 - fprintf(stderr, "Listen failed! Error: %s\n", strerror(errno)); + fprintf(stderr, "IPv4 Listen failed! Error: %s\n", strerror(errno)); #endif close(); return false; From ee9aacc49e6768e3266ff539bccbd8ba16e484c8 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 22 May 2012 15:11:14 +0200 Subject: [PATCH 206/788] Fixed HTTP Dynamic (F4V) processing. --- lib/mp4.cpp | 4 ++-- lib/mp4.h | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index ee0aa703..85ded767 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -187,7 +187,7 @@ void ABST::SetVersion( bool NewVersion) { } void ABST::SetReserved( ) { - Container->SetPayload((uint32_t)4,Box::uint32_to_uint8(0)); + SetPayload((uint32_t)4,Box::uint32_to_uint8(0)); } void ABST::WriteContent( ) { @@ -393,7 +393,7 @@ std::string GenerateLiveBootstrap( uint32_t CurMediaTime ) { abst.AddServerEntry(""); abst.AddQualityEntry(""); abst.WriteContent(); - + std::string Result; Result.append((char*)abst.GetBoxedData(), (int)abst.GetBoxedDataSize()); return Result; diff --git a/lib/mp4.h b/lib/mp4.h index b4ec5417..0ef24659 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -75,7 +75,6 @@ namespace MP4{ std::vector Qualities; std::vector SegmentRunTables; std::vector FragmentRunTables; - Box * Container; };//ABST Box struct afrt_fragmentrunentry { From 90e0c0037e29beff2c78af63586c66bff1ce42cf Mon Sep 17 00:00:00 2001 From: Lekensteyn Date: Sat, 2 Jun 2012 22:12:23 +0200 Subject: [PATCH 207/788] autofoo cleanup, put version detection in configure.ac - Autogenerated files do not belong to the repository, remove those files. - The version is also determined when generating the configure file, so remove the version.sh file as well. - Remove ACLOCAL_AMFLAGS and the related entry in configure.ac to get rid of configuration errors. - Do not create NEWS or INSTALL by using a lower strictness. - Ignore autogenerated autojunk. --- Makefile.am | 1 - configure.ac | 9 +++++---- version.sh | 5 ----- 3 files changed, 5 insertions(+), 10 deletions(-) delete mode 100755 version.sh diff --git a/Makefile.am b/Makefile.am index 6ccf77b6..e1d5774b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,3 @@ -ACLOCAL_AMFLAGS=-I m4 SUBDIRS=lib src EXTRA_DIST=server.html docs: diff --git a/configure.ac b/configure.ac index 2c2cd76f..4837fbcc 100644 --- a/configure.ac +++ b/configure.ac @@ -2,11 +2,12 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.60]) -m4_include([version.m4]) -AC_INIT(MistServer, VERSION_NUMBER, contact@ddvtech.com) +AC_INIT([MistServer], + m4_esyscmd([git describe --tags | tr -d '\n']), + [contact@ddvtech.com]) AC_CONFIG_SRCDIR([src/buffer.cpp]) -AC_CONFIG_MACRO_DIR([m4]) -AM_INIT_AUTOMAKE +# foreign: no need for NEWS or INSTALL files +AM_INIT_AUTOMAKE([foreign]) LT_INIT # Checks for programs. diff --git a/version.sh b/version.sh deleted file mode 100755 index 671da00b..00000000 --- a/version.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh -if git describe --tags > version.tmp; then - echo "m4_define([VERSION_NUMBER], [`tr -d '\n' < version.tmp`])" > version.m4 -fi -rm version.tmp From c49eaca664f075860e0ecbf5cf8097b82eabba00 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 8 Jun 2012 00:11:25 +0200 Subject: [PATCH 208/788] Fixed RTMP desync bug with Windows clients. --- lib/rtmpchunks.cpp | 4 +--- lib/rtmpchunks.h | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/rtmpchunks.cpp b/lib/rtmpchunks.cpp index 6aa5f242..51a2e9dd 100644 --- a/lib/rtmpchunks.cpp +++ b/lib/rtmpchunks.cpp @@ -13,7 +13,7 @@ std::string RTMPStream::handshake_out;///< Output for the handshake. unsigned int RTMPStream::getNowMS(){ timeval t; gettimeofday(&t, 0); - return t.tv_sec + t.tv_usec/1000; + return t.tv_sec * 1000 + t.tv_usec/1000; }//RTMPStream::getNowMS @@ -27,7 +27,6 @@ unsigned int RTMPStream::rec_cnt = 0; unsigned int RTMPStream::snd_cnt = 0; timeval RTMPStream::lastrec; -unsigned int RTMPStream::firsttime; /// Holds the last sent chunk for every msg_id. std::map RTMPStream::Chunk::lastsend; @@ -42,7 +41,6 @@ std::string RTMPStream::Chunk::Pack(){ RTMPStream::Chunk prev = lastsend[cs_id]; unsigned int tmpi; unsigned char chtype = 0x00; - //timestamp -= firsttime; if ((prev.msg_type_id > 0) && (prev.cs_id == cs_id)){ if (msg_stream_id == prev.msg_stream_id){ chtype = 0x40;//do not send msg_stream_id diff --git a/lib/rtmpchunks.h b/lib/rtmpchunks.h index ff4eee4a..009cfd29 100644 --- a/lib/rtmpchunks.h +++ b/lib/rtmpchunks.h @@ -30,7 +30,6 @@ namespace RTMPStream{ extern unsigned int snd_cnt; ///< Counter for total data sent, in bytes. extern timeval lastrec; ///< Timestamp of last time data was received. - extern unsigned int firsttime; ///< Timestamp of first time a chunk was sent. /// Holds a single RTMP chunk, either send or receive direction. class Chunk{ From 5fff8825d9c7b1d7c208b0e6e28c51c41a6c957d Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 8 Jun 2012 18:19:43 +0200 Subject: [PATCH 209/788] Fixed invalid commandline option segfaults, renamed no-daemon to the proper nodaemon as mentioned in all documentation and help messages. --- lib/config.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/config.cpp b/lib/config.cpp index da541832..17cb5d4f 100644 --- a/lib/config.cpp +++ b/lib/config.cpp @@ -41,9 +41,10 @@ void Util::Config::parseArgs(int argc, char ** argv){ {"port",1,0,'p'}, {"interface",1,0,'i'}, {"username",1,0,'u'}, - {"no-daemon",0,0,'n'}, + {"nodaemon",0,0,'n'}, {"daemon",0,0,'d'}, - {"version",0,0,'v'} + {"version",0,0,'v'}, + 0 }; while ((opt = getopt_long(argc, argv, optString, longOpts, 0)) != -1){ switch (opt){ From b88de8e395cb9b281df628c85d2273d6cabb5053 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 8 Jun 2012 22:06:24 +0200 Subject: [PATCH 210/788] Added DTMI desync handling - no longer spams error messages, fixes issues faster. --- lib/dtsc.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 9e937d8d..f73f73c3 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -35,6 +35,7 @@ unsigned int DTSC::Stream::getTime(){ /// \arg buffer The std::string buffer to attempt to parse. bool DTSC::Stream::parsePacket(std::string & buffer){ uint32_t len; + static bool syncing = false; if (buffer.length() > 8){ if (memcmp(buffer.c_str(), DTSC::Magic_Header, 4) == 0){ len = ntohl(((uint32_t *)buffer.c_str())[1]); @@ -63,12 +64,21 @@ bool DTSC::Stream::parsePacket(std::string & buffer){ buffer.erase(0, len+8); while (buffers.size() > buffercount){buffers.pop_back();} advanceRings(); + syncing = false; return true; } #if DEBUG >= 2 - std::cerr << "Error: Invalid DTMI data: " << buffer.substr(0, 4) << std::endl; + if (!syncing){ + std::cerr << "Error: Invalid DTMI data detected - re-syncing" << std::endl; + syncing = true; + } #endif - buffer.erase(0, 1); + size_t magic_search = buffer.find(Magic_Packet); + if (magic_search == std::string::npos){ + buffer.clear(); + }else{ + buffer.erase(0, magic_search); + } } return false; } From e7e37105b62efd92a8f7411d875fcbc69a9c0062 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 12 Jun 2012 10:11:58 +0200 Subject: [PATCH 211/788] Several stability improvements and optimizations for HTTP handling and parsing (also, should fix HTTP Progressive under Windows - again). --- lib/http_parser.cpp | 84 ++++++++++----------------------------------- lib/http_parser.h | 13 +++---- 2 files changed, 23 insertions(+), 74 deletions(-) diff --git a/lib/http_parser.cpp b/lib/http_parser.cpp index 7a087f70..08d11b7e 100644 --- a/lib/http_parser.cpp +++ b/lib/http_parser.cpp @@ -20,13 +20,6 @@ void HTTP::Parser::Clean(){ vars.clear(); } -/// Re-initializes the HTTP::Parser, leaving the internal data buffer alone, then tries to parse a new request or response. -/// Does the same as HTTP::Parser::Clean(), then returns HTTP::Parser::parse(). -bool HTTP::Parser::CleanForNext(){ - Clean(); - return parse(); -} - /// 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. @@ -115,34 +108,20 @@ void HTTP::Parser::SetVar(std::string i, std::string v){ } } -/// Attempt to read a whole HTTP request or response from Socket::Connection. -/// \param sock The socket to use. -/// \param nonblock When true, will not block even if the socket is blocking. -/// \return True of a whole request or response was read, false otherwise. -bool HTTP::Parser::Read(Socket::Connection & sock, bool nonblock){ - if (nonblock && (sock.ready() < 1)){return parse();} - sock.read(HTTPbuffer); - return parse(); -}//HTTPReader::ReadSocket +/// Attempt to read a whole HTTP request or response from a std::string buffer. +/// If a whole request could be read, it is removed from the front of the given buffer. +/// \param strbuf The buffer to read from. +/// \return True if a whole request or response was read, false otherwise. +bool HTTP::Parser::Read(std::string & strbuf){ + return parse(strbuf); +}//HTTPReader::Read -/// Reads a full set of HTTP responses/requests from file F. -/// \return Always false. Use HTTP::Parser::CleanForNext() to parse the contents of the file. -bool HTTP::Parser::Read(FILE * F){ - //returned true als hele http packet gelezen is - int b = 1; - char buffer[500]; - while (b > 0){ - b = fread(buffer, 1, 500, F); - HTTPbuffer.append(buffer, b); - } - return false; -}//HTTPReader::ReadSocket - -/// Attempt to read a whole HTTP response or request from the internal data buffer. +/// 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 internal data buffer. +/// from the data buffer. +/// \param HTTPbuffer The data buffer to read from. /// \return True on success, false otherwise. -bool HTTP::Parser::parse(){ +bool HTTP::Parser::parse(std::string & HTTPbuffer){ size_t f; std::string tmpA, tmpB, tmpC; while (HTTPbuffer != ""){ @@ -194,16 +173,6 @@ bool HTTP::Parser::parse(){ return false; //we should never get here... }//HTTPReader::parse -/// Sends data as response to conn. -/// The response is automatically first build using HTTP::Parser::BuildResponse(). -/// \param conn The Socket::Connection to send the response over. -/// \param code The HTTP response code. Usually you want 200. -/// \param message The HTTP response message. Usually you want "OK". -void HTTP::Parser::SendResponse(Socket::Connection & conn, std::string code, std::string message){ - std::string tmp = BuildResponse(code, message); - conn.write(tmp); -} - #include /// Parses GET or POST-style variable data. /// Saves to internal variable structure using HTTP::Parser::SetVar. @@ -237,31 +206,16 @@ void HTTP::Parser::parseVars(std::string data){ } } -/// Sends data as HTTP/1.1 bodypart to conn. -/// HTTP/1.1 chunked encoding is automatically applied if needed. -/// \param conn The Socket::Connection to send the part over. -/// \param buffer The buffer to send. -/// \param len The length of the buffer. -void HTTP::Parser::SendBodyPart(Socket::Connection & conn, char * buffer, int len){ - std::string tmp; - tmp.append(buffer, len); - SendBodyPart(conn, tmp); -} - -/// Sends data as HTTP/1.1 bodypart to conn. -/// HTTP/1.1 chunked encoding is automatically applied if needed. -/// \param conn The Socket::Connection to send the part over. -/// \param bodypart The data to send. -void HTTP::Parser::SendBodyPart(Socket::Connection & conn, std::string bodypart){ +/// Converts a string to chunked format if protocol is HTTP/1.1 - does nothing otherwise. +/// \param bodypart The data to convert - will be converted in-place. +void HTTP::Parser::Chunkify(std::string & bodypart){ if (protocol == "HTTP/1.1"){ static char len[10]; - int sizelen; - sizelen = snprintf(len, 10, "%x\r\n", (unsigned int)bodypart.size()); - conn.write(len, sizelen); - conn.write(bodypart); - conn.write(len+sizelen-2, 2); - }else{ - conn.write(bodypart); + int sizelen = snprintf(len, 10, "%x\r\n", (unsigned int)bodypart.size()); + //prepend the chunk size and \r\n + bodypart.insert(0, len, sizelen); + //append \r\n + bodypart.append("\r\n", 2); } } diff --git a/lib/http_parser.h b/lib/http_parser.h index 124b9686..609e7d28 100644 --- a/lib/http_parser.h +++ b/lib/http_parser.h @@ -6,7 +6,6 @@ #include #include #include -#include "socket.h" /// Holds all HTTP processing related code. namespace HTTP{ @@ -14,8 +13,7 @@ namespace HTTP{ class Parser{ public: Parser(); - bool Read(Socket::Connection & sock, bool nonblock = true); - bool Read(FILE * F); + bool Read(std::string & strbuf); std::string GetHeader(std::string i); std::string GetVar(std::string i); void SetHeader(std::string i, std::string v); @@ -25,11 +23,8 @@ namespace HTTP{ void SetBody(char * buffer, int len); std::string BuildRequest(); std::string BuildResponse(std::string code, std::string message); - void SendResponse(Socket::Connection & conn, std::string code, std::string message); - void SendBodyPart(Socket::Connection & conn, char * buffer, int len); - void SendBodyPart(Socket::Connection & conn, std::string bodypart); + void Chunkify(std::string & bodypart); void Clean(); - bool CleanForNext(); static std::string urlunescape(const std::string & in); static std::string urlencode(const std::string & in); std::string body; @@ -40,9 +35,9 @@ namespace HTTP{ private: bool seenHeaders; bool seenReq; - bool parse(); + bool parse(std::string & HTTPbuffer); void parseVars(std::string data); - std::string HTTPbuffer; + std::string read_buffer; std::map headers; std::map vars; void Trim(std::string & s); From ec4011a18b999f6023a72aaf39376ab0cfe25327 Mon Sep 17 00:00:00 2001 From: Lekensteyn Date: Sat, 16 Jun 2012 22:43:32 +0200 Subject: [PATCH 212/788] Always update version when configure is executed --- Makefile.am | 2 +- configure.ac | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile.am b/Makefile.am index e1d5774b..644e3eba 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,5 +1,5 @@ SUBDIRS=lib src -EXTRA_DIST=server.html +EXTRA_DIST=server.html VERSION docs: doxygen ./Doxyfile > /dev/null .PHONY: docs diff --git a/configure.ac b/configure.ac index 4837fbcc..289a67fe 100644 --- a/configure.ac +++ b/configure.ac @@ -3,7 +3,7 @@ AC_PREREQ([2.60]) AC_INIT([MistServer], - m4_esyscmd([git describe --tags | tr -d '\n']), + m4_esyscmd([git rev-parse 2>/dev/null&&git describe --tags > VERSION;tr -d '\n' < VERSION]), [contact@ddvtech.com]) AC_CONFIG_SRCDIR([src/buffer.cpp]) # foreign: no need for NEWS or INSTALL files From 942a4136d270531897ab0ac1c4d52321ac1ab069 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 17 Jun 2012 12:56:12 +0200 Subject: [PATCH 213/788] Allow HTTP Connector to reconnect to different buffers - closes #31 --- lib/socket.cpp | 52 ++++++++++++++++++++++++++++++++++++-------------- lib/socket.h | 8 ++++++-- 2 files changed, 44 insertions(+), 16 deletions(-) diff --git a/lib/socket.cpp b/lib/socket.cpp index 63cf624b..62cbc081 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -53,13 +53,16 @@ void Socket::Connection::setBlocking(bool blocking){ } /// Close connection. The internal socket is closed and then set to -1. +/// If the connection is already closed, nothing happens. void Socket::Connection::close(){ - #if DEBUG >= 6 - fprintf(stderr, "Socket closed.\n"); - #endif - shutdown(sock, SHUT_RDWR); - ::close(sock); - sock = -1; + if (connected()){ + #if DEBUG >= 6 + fprintf(stderr, "Socket closed.\n"); + #endif + shutdown(sock, SHUT_RDWR); + ::close(sock); + sock = -1; + } }//Socket::Connection::close /// Returns internal socket number. @@ -214,7 +217,7 @@ signed int Socket::Connection::ready(){ /// The connection status is updated after every read/write attempt, when errors occur /// and when the socket is closed manually. /// \returns True if socket is connected, false otherwise. -bool Socket::Connection::connected(){ +bool Socket::Connection::connected() const{ return (sock >= 0); } @@ -451,6 +454,24 @@ std::string Socket::Connection::getHost(){ return remotehost; } +/// Returns true if these sockets are the same socket. +/// Does not check the internal stats - only the socket itself. +bool Socket::Connection::operator== (const Connection &B) const{ + return sock == B.sock; +} + +/// Returns true if these sockets are not the same socket. +/// Does not check the internal stats - only the socket itself. +bool Socket::Connection::operator!= (const Connection &B) const{ + return sock != B.sock; +} + +/// Returns true if the socket is valid. +/// Aliases for Socket::Connection::connected() +Socket::Connection::operator bool() const{ + return connected(); +} + /// Create a new base Server. The socket is never connected, and a placeholder for later connections. Socket::Server::Server(){ sock = -1; @@ -661,13 +682,16 @@ Socket::Connection Socket::Server::accept(bool nonblock){ } /// Close connection. The internal socket is closed and then set to -1. +/// If the connection is already closed, nothing happens. void Socket::Server::close(){ - #if DEBUG >= 6 - fprintf(stderr, "ServerSocket closed.\n"); - #endif - shutdown(sock, SHUT_RDWR); - ::close(sock); - sock = -1; + if (connected()){ + #if DEBUG >= 6 + fprintf(stderr, "ServerSocket closed.\n"); + #endif + shutdown(sock, SHUT_RDWR); + ::close(sock); + sock = -1; + } }//Socket::Server::close /// Returns the connected-state for this socket. @@ -675,7 +699,7 @@ void Socket::Server::close(){ /// The connection status is updated after every accept attempt, when errors occur /// and when the socket is closed manually. /// \returns True if socket is connected, false otherwise. -bool Socket::Server::connected(){ +bool Socket::Server::connected() const{ return (sock >= 0); }//Socket::Server::connected diff --git a/lib/socket.h b/lib/socket.h index 9c88a248..0d33573b 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -37,7 +37,7 @@ namespace Socket{ bool canRead(); ///< Calls poll() on the socket, checking if data is available. bool canWrite(); ///< Calls poll() on the socket, checking if data can be written. signed int ready(); ///< Returns the ready-state for this socket. - bool connected(); ///< Returns the connected-state for this socket. + bool connected() const; ///< Returns the connected-state for this socket. bool read(const void * buffer, int len); ///< Reads data from socket. bool read(const void * buffer, int width, int count); ///< Read call that is compatible with file access syntax. bool write(const void * buffer, int len); ///< Writes data to socket. @@ -62,6 +62,10 @@ namespace Socket{ 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. @@ -75,7 +79,7 @@ namespace Socket{ Server(int port, std::string hostname = "0.0.0.0", bool nonblock = false); ///< Create a new TCP Server. Server(std::string adres, bool nonblock = false); ///< Create a new Unix Server. Connection accept(bool nonblock = false); ///< Accept any waiting connections. - bool connected(); ///< Returns the connected-state for this socket. + bool connected() const; ///< Returns the connected-state for this socket. void close(); ///< Close connection. int getSocket(); ///< Returns internal socket number. }; From 9b966bbbf73318c305812f55457822acae4f102a Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 17 Jun 2012 14:39:35 +0200 Subject: [PATCH 214/788] Added createhooks shell script that will install git hooks for auto-rebuilding on checkout/commit, make main binaries depend on VERSION file for rebuilding (otherwise version in -v is not updated). --- createhooks.sh | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100755 createhooks.sh diff --git a/createhooks.sh b/createhooks.sh new file mode 100755 index 00000000..067aafa8 --- /dev/null +++ b/createhooks.sh @@ -0,0 +1,8 @@ +#!/bin/bash +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +echo -e "#!/bin/bash\n[ -f configure ] && touch configure\n[ -f configure.ac ] && touch configure.ac" > $DIR/.git/hooks/post-commit +echo -e "#!/bin/bash\n[ -f configure ] && touch configure\n[ -f configure.ac ] && touch configure.ac" > $DIR/.git/hooks/post-checkout +chmod +x $DIR/.git/hooks/post-commit +chmod +x $DIR/.git/hooks/post-checkout +echo "Done! The version number should now auto-update whenever you commit or checkout." + From a71ab0427e75ff8529582e6429d2c57fba629c40 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 18 Jun 2012 20:37:38 +0200 Subject: [PATCH 215/788] Fixed compiling on some systems: renamed libcrypto to libkeycrypto --- lib/Makefile.am | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/Makefile.am b/lib/Makefile.am index 2a2f0d1f..ee478a4a 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,11 +1,11 @@ -noinst_LTLIBRARIES=libamf.la libauth.la libbase64.la libconfig.la libcrypto.la libdtsc.la libflv_tag.la libhttp_parser.la libjson.la libprocs.la librtmpchunks.la libsocket.la libtinythread.la libmp4.la +noinst_LTLIBRARIES=libamf.la libauth.la libbase64.la libconfig.la libkeycrypto.la libdtsc.la libflv_tag.la libhttp_parser.la libjson.la libprocs.la librtmpchunks.la libsocket.la libtinythread.la libmp4.la libamf_la_SOURCES=amf.h amf.cpp libauth_la_SOURCES=auth.h auth.cpp libauth_la_LIBADD=-lssl -lcrypto libbase64_la_SOURCES=base64.h base64.cpp libconfig_la_SOURCES=config.h config.cpp -libcrypto_la_SOURCES=crypto.h crypto.cpp -libcrypto_la_LIBADD=-lssl -lcrypto +libkeycrypto_la_SOURCES=crypto.h crypto.cpp +libkeycrypto_la_LIBADD=-lssl -lcrypto libdtsc_la_SOURCES=dtsc.h dtsc.cpp libflv_tag_la_SOURCES=flv_tag.h flv_tag.cpp libflv_tag_la_LIBADD=./libamf.la ./libsocket.la @@ -13,7 +13,7 @@ libhttp_parser_la_SOURCES=http_parser.h http_parser.cpp libjson_la_SOURCES=json.h json.cpp libprocs_la_SOURCES=procs.h procs.cpp librtmpchunks_la_SOURCES=rtmpchunks.h rtmpchunks.cpp -librtmpchunks_la_LIBADD=./libflv_tag.la -lssl -lcrypto +librtmpchunks_la_LIBADD=./libflv_tag.la -lssl -lcrypto ./libkeycrypto.la libsocket_la_SOURCES=socket.h socket.cpp libtinythread_la_SOURCES=tinythread.h tinythread.cpp libtinythread_la_LIBADD=-lpthread From 49fe8afb842da52c7bf9367ce320a6a9dfc728ef Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 9 Jul 2012 00:15:06 +0200 Subject: [PATCH 216/788] Restructuring so our libraries can go into a separate libmist project. --- .gitignore | 28 ++ Makefile.am | 4 +- README | 4 +- configure.ac | 11 +- lib/Makefile.am | 29 +- lib/mist-1.0.pc.in | 11 + lib/socket.cpp | 2 +- lib/tinythread.cpp | 287 -------------------- lib/tinythread.h | 644 --------------------------------------------- 9 files changed, 57 insertions(+), 963 deletions(-) create mode 100644 .gitignore create mode 100644 lib/mist-1.0.pc.in delete mode 100644 lib/tinythread.cpp delete mode 100644 lib/tinythread.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..bbbb44fb --- /dev/null +++ b/.gitignore @@ -0,0 +1,28 @@ +#ignore object files and nonsense like that +*.[oa] +*.la +*.lo +*~ +.deps +Makefile +Makefile.in +version.m4 +docs +nbproject +autom4te.cache +/configure +/config.* +/aclocal.m4 +/install-sh +/ltmain.sh +/missing +/depcomp +.libs +VERSION +.dep.inc +autoscan.log +libtool +*.dtsc +*.flv +*.json +*.pc diff --git a/Makefile.am b/Makefile.am index 644e3eba..ef0897d4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,5 +1,5 @@ -SUBDIRS=lib src -EXTRA_DIST=server.html VERSION +SUBDIRS=lib +EXTRA_DIST=VERSION docs: doxygen ./Doxyfile > /dev/null .PHONY: docs diff --git a/README b/README index b39f75ee..60358b64 100644 --- a/README +++ b/README @@ -2,8 +2,8 @@ For full documentation as well as background information, visit our wiki at: http://wiki.mistserver.com/ Code contributions and bug reports are welcomed through: -https://github.com/DDVTECH/DMS +https://github.com/DDVTECH/libmist The following configure options are possible: ---enable-verbose = Compiles all applications in verbose mode, printing a lot more information to the screen than normally. +--enable-verbose = Compiles the libraries in verbose mode, printing a lot more information to the screen than normally. --disable-verbose = The opposite of above (default). diff --git a/configure.ac b/configure.ac index 289a67fe..d1c5047a 100644 --- a/configure.ac +++ b/configure.ac @@ -2,10 +2,10 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.60]) -AC_INIT([MistServer], +AC_INIT([libmist], m4_esyscmd([git rev-parse 2>/dev/null&&git describe --tags > VERSION;tr -d '\n' < VERSION]), [contact@ddvtech.com]) -AC_CONFIG_SRCDIR([src/buffer.cpp]) +AC_CONFIG_SRCDIR([lib/dtsc.cpp]) # foreign: no need for NEWS or INSTALL files AM_INIT_AUTOMAKE([foreign]) LT_INIT @@ -20,6 +20,7 @@ AC_CHECK_LIB(ssl, RC4) # Checks for header files. AC_CHECK_HEADERS([arpa/inet.h fcntl.h netdb.h netinet/in.h stdint.h stdlib.h string.h sys/socket.h sys/time.h unistd.h]) + # Checks for typedefs, structures, and compiler characteristics. AC_HEADER_STDBOOL AC_C_INLINE @@ -42,9 +43,5 @@ CXXFLAGS="$CXXFLAGS -funsigned-char" #allow verbose mode compiles AC_ARG_ENABLE([verbose], AC_HELP_STRING([--enable-verbose], [Compile with verbose messages]), CXXFLAGS="-DDEBUG=4 $CXXFLAGS") -AC_CONFIG_FILES([Makefile - lib/Makefile - src/converters/Makefile - src/analysers/Makefile - src/Makefile]) +AC_CONFIG_FILES([Makefile lib/Makefile lib/mist-1.0.pc]) AC_OUTPUT diff --git a/lib/Makefile.am b/lib/Makefile.am index ee478a4a..34b8e646 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,20 +1,9 @@ -noinst_LTLIBRARIES=libamf.la libauth.la libbase64.la libconfig.la libkeycrypto.la libdtsc.la libflv_tag.la libhttp_parser.la libjson.la libprocs.la librtmpchunks.la libsocket.la libtinythread.la libmp4.la -libamf_la_SOURCES=amf.h amf.cpp -libauth_la_SOURCES=auth.h auth.cpp -libauth_la_LIBADD=-lssl -lcrypto -libbase64_la_SOURCES=base64.h base64.cpp -libconfig_la_SOURCES=config.h config.cpp -libkeycrypto_la_SOURCES=crypto.h crypto.cpp -libkeycrypto_la_LIBADD=-lssl -lcrypto -libdtsc_la_SOURCES=dtsc.h dtsc.cpp -libflv_tag_la_SOURCES=flv_tag.h flv_tag.cpp -libflv_tag_la_LIBADD=./libamf.la ./libsocket.la -libhttp_parser_la_SOURCES=http_parser.h http_parser.cpp -libjson_la_SOURCES=json.h json.cpp -libprocs_la_SOURCES=procs.h procs.cpp -librtmpchunks_la_SOURCES=rtmpchunks.h rtmpchunks.cpp -librtmpchunks_la_LIBADD=./libflv_tag.la -lssl -lcrypto ./libkeycrypto.la -libsocket_la_SOURCES=socket.h socket.cpp -libtinythread_la_SOURCES=tinythread.h tinythread.cpp -libtinythread_la_LIBADD=-lpthread -libmp4_la_SOURCES=mp4.h mp4.cpp +lib_LTLIBRARIES=libmist-1.0.la +libmist_1_0_la_SOURCES=amf.h amf.cpp auth.h auth.cpp base64.h base64.cpp config.h config.cpp crypto.h crypto.cpp dtsc.h dtsc.cpp flv_tag.h flv_tag.cpp http_parser.h http_parser.cpp json.h json.cpp procs.h procs.cpp rtmpchunks.h rtmpchunks.cpp socket.h socket.cpp mp4.h mp4.cpp +libmist_1_0_la_LIBADD=-lssl -lcrypto + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = mist-1.0.pc + +library_includedir=$(includedir)/mist-1.0/mist +library_include_HEADERS = amf.h auth.h base64.h config.h crypto.h dtsc.h flv_tag.h http_parser.h json.h procs.h rtmpchunks.h socket.h mp4.h diff --git a/lib/mist-1.0.pc.in b/lib/mist-1.0.pc.in new file mode 100644 index 00000000..79bca3fb --- /dev/null +++ b/lib/mist-1.0.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Mist +Description: Mist Streaming Media Library +Requires: openssl +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lmist-1.0 -lssl -lcrypto +Cflags: -I${includedir}/mist-1.0 -I${libdir}/mist-1.0/include diff --git a/lib/socket.cpp b/lib/socket.cpp index 62cbc081..727f7de0 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -727,7 +727,7 @@ Socket::Connection Socket::getStream(std::string streamname){ /// Filters the streamname, removing invalid characters and /// converting all letters to lowercase. /// If a '?' character is found, everything following that character is deleted. -/// If the /tmp/ddvtech directory doesn't exist yet, this will create it. +/// If the /tmp/mist directory doesn't exist yet, this will create it. Socket::Server Socket::makeStream(std::string streamname){ //strip anything that isn't numbers, digits or underscores for (std::string::iterator i=streamname.end()-1; i>=streamname.begin(); --i){ diff --git a/lib/tinythread.cpp b/lib/tinythread.cpp deleted file mode 100644 index eb2dce0e..00000000 --- a/lib/tinythread.cpp +++ /dev/null @@ -1,287 +0,0 @@ -/* -Copyright (c) 2010 Marcus Geelnard - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source - distribution. -*/ - -#include -#include "tinythread.h" - -#if defined(_TTHREAD_POSIX_) - #include - #include -#elif defined(_TTHREAD_WIN32_) - #include -#endif - - -namespace tthread { - -//------------------------------------------------------------------------------ -// condition_variable -//------------------------------------------------------------------------------ -// NOTE 1: The Win32 implementation of the condition_variable class is based on -// the corresponding implementation in GLFW, which in turn is based on a -// description by Douglas C. Schmidt and Irfan Pyarali: -// http://www.cs.wustl.edu/~schmidt/win32-cv-1.html -// -// NOTE 2: Windows Vista actually has native support for condition variables -// (InitializeConditionVariable, WakeConditionVariable, etc), but we want to -// be portable with pre-Vista Windows versions, so TinyThread++ does not use -// Vista condition variables. -//------------------------------------------------------------------------------ - -#if defined(_TTHREAD_WIN32_) - #define _CONDITION_EVENT_ONE 0 - #define _CONDITION_EVENT_ALL 1 -#endif - -#if defined(_TTHREAD_WIN32_) -condition_variable::condition_variable() : mWaitersCount(0) -{ - mEvents[_CONDITION_EVENT_ONE] = CreateEvent(NULL, FALSE, FALSE, NULL); - mEvents[_CONDITION_EVENT_ALL] = CreateEvent(NULL, TRUE, FALSE, NULL); - InitializeCriticalSection(&mWaitersCountLock); -} -#endif - -#if defined(_TTHREAD_WIN32_) -condition_variable::~condition_variable() -{ - CloseHandle(mEvents[_CONDITION_EVENT_ONE]); - CloseHandle(mEvents[_CONDITION_EVENT_ALL]); - DeleteCriticalSection(&mWaitersCountLock); -} -#endif - -#if defined(_TTHREAD_WIN32_) -void condition_variable::_wait() -{ - // Wait for either event to become signaled due to notify_one() or - // notify_all() being called - int result = WaitForMultipleObjects(2, mEvents, FALSE, INFINITE); - - // Check if we are the last waiter - EnterCriticalSection(&mWaitersCountLock); - -- mWaitersCount; - bool lastWaiter = (result == (WAIT_OBJECT_0 + _CONDITION_EVENT_ALL)) && - (mWaitersCount == 0); - LeaveCriticalSection(&mWaitersCountLock); - - // If we are the last waiter to be notified to stop waiting, reset the event - if(lastWaiter) - ResetEvent(mEvents[_CONDITION_EVENT_ALL]); -} -#endif - -#if defined(_TTHREAD_WIN32_) -void condition_variable::notify_one() -{ - // Are there any waiters? - EnterCriticalSection(&mWaitersCountLock); - bool haveWaiters = (mWaitersCount > 0); - LeaveCriticalSection(&mWaitersCountLock); - - // If we have any waiting threads, send them a signal - if(haveWaiters) - SetEvent(mEvents[_CONDITION_EVENT_ONE]); -} -#endif - -#if defined(_TTHREAD_WIN32_) -void condition_variable::notify_all() -{ - // Are there any waiters? - EnterCriticalSection(&mWaitersCountLock); - bool haveWaiters = (mWaitersCount > 0); - LeaveCriticalSection(&mWaitersCountLock); - - // If we have any waiting threads, send them a signal - if(haveWaiters) - SetEvent(mEvents[_CONDITION_EVENT_ALL]); -} -#endif - - -//------------------------------------------------------------------------------ -// POSIX pthread_t to unique thread::id mapping logic. -// Note: Here we use a global thread safe std::map to convert instances of -// pthread_t to small thread identifier numbers (unique within one process). -// This method should be portable across different POSIX implementations. -//------------------------------------------------------------------------------ - -#if defined(_TTHREAD_POSIX_) -static thread::id _pthread_t_to_ID(const pthread_t &aHandle) -{ - static mutex idMapLock; - static std::map idMap; - static unsigned long int idCount(1); - - lock_guard guard(idMapLock); - if(idMap.find(aHandle) == idMap.end()) - idMap[aHandle] = idCount ++; - return thread::id(idMap[aHandle]); -} -#endif // _TTHREAD_POSIX_ - - -//------------------------------------------------------------------------------ -// thread -//------------------------------------------------------------------------------ - -/// Information to pass to the new thread (what to run). -struct _thread_start_info { - void (*mFunction)(void *); ///< Pointer to the function to be executed. - void * mArg; ///< Function argument for the thread function. - thread * mThread; ///< Pointer to the thread object. -}; - -// Thread wrapper function. -#if defined(_TTHREAD_WIN32_) -unsigned WINAPI thread::wrapper_function(void * aArg) -#elif defined(_TTHREAD_POSIX_) -void * thread::wrapper_function(void * aArg) -#endif -{ - // Get thread startup information - _thread_start_info * ti = (_thread_start_info *) aArg; - - try - { - // Call the actual client thread function - ti->mFunction(ti->mArg); - } - catch(...) - { - // Uncaught exceptions will terminate the application (default behavior - // according to the C++0x draft) - std::terminate(); - } - - // The thread is no longer executing - lock_guard guard(ti->mThread->mDataMutex); - ti->mThread->mNotAThread = true; - - // The thread is responsible for freeing the startup information - delete ti; - - return 0; -} - -thread::thread(void (*aFunction)(void *), void * aArg) -{ - // Serialize access to this thread structure - lock_guard guard(mDataMutex); - - // Fill out the thread startup information (passed to the thread wrapper, - // which will eventually free it) - _thread_start_info * ti = new _thread_start_info; - ti->mFunction = aFunction; - ti->mArg = aArg; - ti->mThread = this; - - // The thread is now alive - mNotAThread = false; - - // Create the thread -#if defined(_TTHREAD_WIN32_) - mHandle = (HANDLE) _beginthreadex(0, 0, wrapper_function, (void *) ti, 0, &mWin32ThreadID); -#elif defined(_TTHREAD_POSIX_) - if(pthread_create(&mHandle, NULL, wrapper_function, (void *) ti) != 0) - mHandle = 0; -#endif - - // Did we fail to create the thread? - if(!mHandle) - { - mNotAThread = true; - delete ti; - } -} - -thread::~thread() -{ - if(joinable()) - std::terminate(); -} - -void thread::join() -{ - if(joinable()) - { -#if defined(_TTHREAD_WIN32_) - WaitForSingleObject(mHandle, INFINITE); -#elif defined(_TTHREAD_POSIX_) - pthread_join(mHandle, NULL); -#endif - } -} - -bool thread::joinable() const -{ - mDataMutex.lock(); - bool result = !mNotAThread; - mDataMutex.unlock(); - return result; -} - -thread::id thread::get_id() const -{ - if(!joinable()) - return id(); -#if defined(_TTHREAD_WIN32_) - return id((unsigned long int) mWin32ThreadID); -#elif defined(_TTHREAD_POSIX_) - return _pthread_t_to_ID(mHandle); -#endif -} - -unsigned thread::hardware_concurrency() -{ -#if defined(_TTHREAD_WIN32_) - SYSTEM_INFO si; - GetSystemInfo(&si); - return (int) si.dwNumberOfProcessors; -#elif defined(_SC_NPROCESSORS_ONLN) - return (int) sysconf(_SC_NPROCESSORS_ONLN); -#elif defined(_SC_NPROC_ONLN) - return (int) sysconf(_SC_NPROC_ONLN); -#else - // The standard requires this function to return zero if the number of - // hardware cores could not be determined. - return 0; -#endif -} - - -//------------------------------------------------------------------------------ -// this_thread -//------------------------------------------------------------------------------ - -thread::id this_thread::get_id() -{ -#if defined(_TTHREAD_WIN32_) - return thread::id((unsigned long int) GetCurrentThreadId()); -#elif defined(_TTHREAD_POSIX_) - return _pthread_t_to_ID(pthread_self()); -#endif -} - -} diff --git a/lib/tinythread.h b/lib/tinythread.h deleted file mode 100644 index 494e1ec4..00000000 --- a/lib/tinythread.h +++ /dev/null @@ -1,644 +0,0 @@ -/* -Copyright (c) 2010 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_ - -// 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_) - #include -#else - #include - #include - #include - #include -#endif - -// Generic includes -#include - -/// TinyThread++ version (major number). -#define TINYTHREAD_VERSION_MAJOR 1 -/// TinyThread++ version (minor number). -#define TINYTHREAD_VERSION_MINOR 0 -/// TinyThread++ version (full version). -#define TINYTHREAD_VERSION (TINYTHREAD_VERSION_MAJOR * 100 + TINYTHREAD_VERSION_MINOR) - -// Do we have a fully featured C++0x compiler? -#if (__cplusplus > 199711L) || (defined(__STDCXX_VERSION__) && (__STDCXX_VERSION__ >= 201001L)) - #define _TTHREAD_CPP0X_ -#endif - -// ...at least partial C++0x? -#if defined(_TTHREAD_CPP0X_) || defined(__GXX_EXPERIMENTAL_CXX0X__) || defined(__GXX_EXPERIMENTAL_CPP0X__) - #define _TTHREAD_CPP0X_PARTIAL_ -#endif - -// Macro for disabling assignments of objects. -#ifdef _TTHREAD_CPP0X_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 - -#if !defined(_TTHREAD_CPP0X_) && !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++0x thread classes. For instance, the tthread::mutex class corresponds to -/// the std::mutex class. -namespace tthread { - -/// Mutex class. -/// This is a mutual exclusion object for synchronizing access to shared -/// memory areas for several threads. The mutex is non-recursive (i.e. a -/// program may deadlock if the thread that owns a mutex object calls lock() -/// on that object). -/// @see recursive_mutex -class mutex { - public: - /// Constructor. - mutex() -#if defined(_TTHREAD_WIN32_) - : mAlreadyLocked(false) -#endif - { -#if defined(_TTHREAD_WIN32_) - InitializeCriticalSection(&mHandle); -#else - pthread_mutex_init(&mHandle, NULL); -#endif - } - - /// Destructor. - ~mutex() - { -#if defined(_TTHREAD_WIN32_) - DeleteCriticalSection(&mHandle); -#else - pthread_mutex_destroy(&mHandle); -#endif - } - - /// Lock the mutex. - /// The method will block the calling thread until a lock on the mutex can - /// be obtained. The mutex remains locked until \c unlock() is called. - /// @see lock_guard - inline void lock() - { -#if defined(_TTHREAD_WIN32_) - EnterCriticalSection(&mHandle); - while(mAlreadyLocked) Sleep(1000); // Simulate deadlock... - mAlreadyLocked = true; -#else - pthread_mutex_lock(&mHandle); -#endif - } - - /// Try to lock the mutex. - /// The method will try to lock the mutex. If it fails, the function will - /// return immediately (non-blocking). - /// @return \c true if the lock was acquired, or \c false if the lock could - /// not be acquired. - inline bool try_lock() - { -#if defined(_TTHREAD_WIN32_) - bool ret = (TryEnterCriticalSection(&mHandle) ? true : false); - if(ret && mAlreadyLocked) - { - LeaveCriticalSection(&mHandle); - ret = false; - } - return ret; -#else - return (pthread_mutex_trylock(&mHandle) == 0) ? true : false; -#endif - } - - /// Unlock the mutex. - /// If any threads are waiting for the lock on this mutex, one of them will - /// be unblocked. - inline void unlock() - { -#if defined(_TTHREAD_WIN32_) - mAlreadyLocked = false; - LeaveCriticalSection(&mHandle); -#else - pthread_mutex_unlock(&mHandle); -#endif - } - - _TTHREAD_DISABLE_ASSIGNMENT(mutex) - - private: -#if defined(_TTHREAD_WIN32_) - CRITICAL_SECTION mHandle; - bool mAlreadyLocked; -#else - pthread_mutex_t mHandle; -#endif - - friend class condition_variable; -}; - -/// Recursive mutex class. -/// This is a mutual exclusion object for synchronizing access to shared -/// memory areas for several threads. The mutex is recursive (i.e. a thread -/// may lock the mutex several times, as long as it unlocks the mutex the same -/// number of times). -/// @see mutex -class recursive_mutex { - public: - /// Constructor. - recursive_mutex() - { -#if defined(_TTHREAD_WIN32_) - InitializeCriticalSection(&mHandle); -#else - pthread_mutexattr_t attr; - pthread_mutexattr_init(&attr); - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&mHandle, &attr); -#endif - } - - /// Destructor. - ~recursive_mutex() - { -#if defined(_TTHREAD_WIN32_) - DeleteCriticalSection(&mHandle); -#else - pthread_mutex_destroy(&mHandle); -#endif - } - - /// Lock the mutex. - /// The method will block the calling thread until a lock on the mutex can - /// be obtained. The mutex remains locked until \c unlock() is called. - /// @see lock_guard - inline void lock() - { -#if defined(_TTHREAD_WIN32_) - EnterCriticalSection(&mHandle); -#else - pthread_mutex_lock(&mHandle); -#endif - } - - /// Try to lock the mutex. - /// The method will try to lock the mutex. If it fails, the function will - /// return immediately (non-blocking). - /// @return \c true if the lock was acquired, or \c false if the lock could - /// not be acquired. - inline bool try_lock() - { -#if defined(_TTHREAD_WIN32_) - return TryEnterCriticalSection(&mHandle) ? true : false; -#else - return (pthread_mutex_trylock(&mHandle) == 0) ? true : false; -#endif - } - - /// Unlock the mutex. - /// If any threads are waiting for the lock on this mutex, one of them will - /// be unblocked. - inline void unlock() - { -#if defined(_TTHREAD_WIN32_) - LeaveCriticalSection(&mHandle); -#else - pthread_mutex_unlock(&mHandle); -#endif - } - - _TTHREAD_DISABLE_ASSIGNMENT(recursive_mutex) - - private: -#if defined(_TTHREAD_WIN32_) - CRITICAL_SECTION mHandle; -#else - pthread_mutex_t mHandle; -#endif - - friend class condition_variable; -}; - -/// Lock guard class. -/// The constructor locks the mutex, and the destructor unlocks the mutex, so -/// the mutex will automatically be unlocked when the lock guard goes out of -/// scope. Example usage: -/// @code -/// mutex m; -/// int counter; -/// -/// void increment() -/// { -/// lock_guard guard(m); -/// ++ counter; -/// } -/// @endcode - -template -class lock_guard { - public: - typedef T mutex_type; - - lock_guard() : mMutex(0) {} - - /// The constructor locks the mutex. - explicit lock_guard(mutex_type &aMutex) - { - mMutex = &aMutex; - mMutex->lock(); - } - - /// The destructor unlocks the mutex. - ~lock_guard() - { - if(mMutex) - mMutex->unlock(); - } - - private: - mutex_type * mMutex; -}; - -/// Condition variable class. -/// This is a signalling object for synchronizing the execution flow for -/// several threads. Example usage: -/// @code -/// // Shared data and associated mutex and condition variable objects -/// int count; -/// mutex m; -/// condition_variable cond; -/// -/// // Wait for the counter to reach a certain number -/// void wait_counter(int targetCount) -/// { -/// lock_guard guard(m); -/// while(count < targetCount) -/// cond.wait(m); -/// } -/// -/// // Increment the counter, and notify waiting threads -/// void increment() -/// { -/// lock_guard guard(m); -/// ++ count; -/// cond.notify_all(); -/// } -/// @endcode -class condition_variable { - public: - /// Constructor. -#if defined(_TTHREAD_WIN32_) - condition_variable(); -#else - condition_variable() - { - pthread_cond_init(&mHandle, NULL); - } -#endif - - /// Destructor. -#if defined(_TTHREAD_WIN32_) - ~condition_variable(); -#else - ~condition_variable() - { - pthread_cond_destroy(&mHandle); - } -#endif - - /// Wait for the condition. - /// The function will block the calling thread until the condition variable - /// is woken by \c notify_one(), \c notify_all() or a spurious wake up. - /// @param[in] aMutex A mutex that will be unlocked when the wait operation - /// starts, an locked again as soon as the wait operation is finished. - template - inline void wait(_mutexT &aMutex) - { -#if defined(_TTHREAD_WIN32_) - // Increment number of waiters - EnterCriticalSection(&mWaitersCountLock); - ++ mWaitersCount; - LeaveCriticalSection(&mWaitersCountLock); - - // Release the mutex while waiting for the condition (will decrease - // the number of waiters when done)... - aMutex.unlock(); - _wait(); - aMutex.lock(); -#else - pthread_cond_wait(&mHandle, &aMutex.mHandle); -#endif - } - - /// Notify one thread that is waiting for the condition. - /// If at least one thread is blocked waiting for this condition variable, - /// one will be woken up. - /// @note Only threads that started waiting prior to this call will be - /// woken up. -#if defined(_TTHREAD_WIN32_) - void notify_one(); -#else - inline void notify_one() - { - pthread_cond_signal(&mHandle); - } -#endif - - /// Notify all threads that are waiting for the condition. - /// All threads that are blocked waiting for this condition variable will - /// be woken up. - /// @note Only threads that started waiting prior to this call will be - /// woken up. -#if defined(_TTHREAD_WIN32_) - void notify_all(); -#else - inline void notify_all() - { - pthread_cond_broadcast(&mHandle); - } -#endif - - _TTHREAD_DISABLE_ASSIGNMENT(condition_variable) - - private: -#if defined(_TTHREAD_WIN32_) - void _wait(); - HANDLE mEvents[2]; ///< Signal and broadcast event HANDLEs. - unsigned int mWaitersCount; ///< Count of the number of waiters. - CRITICAL_SECTION mWaitersCountLock; ///< Serialize access to mWaitersCount. -#else - pthread_cond_t mHandle; -#endif -}; - - -/// Thread class. -class thread { - public: -#if defined(_TTHREAD_WIN32_) - typedef HANDLE native_handle_type; -#else - typedef pthread_t native_handle_type; -#endif - - class id; - - /// Default constructor. - /// Construct a \c thread object without an associated thread of execution - /// (i.e. non-joinable). - thread() : mHandle(0), mNotAThread(true) -#if defined(_TTHREAD_WIN32_) - , mWin32ThreadID(0) -#endif - {} - - /// Thread starting constructor. - /// Construct a \c thread object with a new thread of execution. - /// @param[in] aFunction A function pointer to a function of type: - /// void fun(void * arg) - /// @param[in] aArg Argument to the thread function. - /// @note This constructor is not fully compatible with the standard C++ - /// thread class. It is more similar to the pthread_create() (POSIX) and - /// CreateThread() (Windows) functions. - thread(void (*aFunction)(void *), void * aArg); - - /// Destructor. - /// @note If the thread is joinable upon destruction, \c std::terminate() - /// will be called, which terminates the process. It is always wise to do - /// \c join() before deleting a thread object. - ~thread(); - - /// Wait for the thread to finish (join execution flows). - void join(); - - /// Check if the thread is joinable. - /// A thread object is joinable if it has an associated thread of execution. - bool joinable() const; - - /// Return the thread ID of a thread object. - id get_id() const; - - /// Get the native handle for this thread. - /// @note Under Windows, this is a \c HANDLE, and under POSIX systems, this - /// is a \c pthread_t. - inline native_handle_type native_handle() - { - return mHandle; - } - - /// Determine the number of threads which can possibly execute concurrently. - /// This function is useful for determining the optimal number of threads to - /// use for a task. - /// @return The number of hardware thread contexts in the system. - /// @note If this value is not defined, the function returns zero (0). - static unsigned hardware_concurrency(); - - _TTHREAD_DISABLE_ASSIGNMENT(thread) - - private: - native_handle_type mHandle; ///< Thread handle. - mutable mutex mDataMutex; ///< Serializer for access to the thread private data. - bool mNotAThread; ///< True if this object is not a thread of execution. -#if defined(_TTHREAD_WIN32_) - unsigned int mWin32ThreadID; ///< Unique thread ID (filled out by _beginthreadex). -#endif - - // This is the internal thread wrapper function. -#if defined(_TTHREAD_WIN32_) - static unsigned WINAPI wrapper_function(void * aArg); -#else - static void * wrapper_function(void * aArg); -#endif -}; - -/// Thread ID. -/// The thread ID is a unique identifier for each thread. -/// @see thread::get_id() -class thread::id { - public: - /// Default constructor. - /// The default constructed ID is that of thread without a thread of - /// execution. - id() : mId(0) {}; - - id(unsigned long int aId) : mId(aId) {}; - - id(const id& aId) : mId(aId.mId) {}; - - inline id & operator=(const id &aId) - { - mId = aId.mId; - return *this; - } - - inline friend bool operator==(const id &aId1, const id &aId2) - { - return (aId1.mId == aId2.mId); - } - - inline friend bool operator!=(const id &aId1, const id &aId2) - { - return (aId1.mId != aId2.mId); - } - - inline friend bool operator<=(const id &aId1, const id &aId2) - { - return (aId1.mId <= aId2.mId); - } - - inline friend bool operator<(const id &aId1, const id &aId2) - { - return (aId1.mId < aId2.mId); - } - - inline friend bool operator>=(const id &aId1, const id &aId2) - { - return (aId1.mId >= aId2.mId); - } - - inline friend bool operator>(const id &aId1, const id &aId2) - { - return (aId1.mId > aId2.mId); - } - - inline friend std::ostream& operator <<(std::ostream &os, const id &obj) - { - os << obj.mId; - return os; - } - - private: - unsigned long int mId; -}; - - -// Related to - minimal to be able to support chrono. -typedef long long __intmax_t; - -/// Minimal implementation of the \c ratio class. This class provides enough -/// functionality to implement some basic \c chrono classes. -template <__intmax_t N, __intmax_t D = 1> class ratio { - public: - static double _as_double() { return double(N) / double(D); } -}; - -/// Minimal implementation of the \c chrono namespace. -/// The \c chrono namespace provides types for specifying time intervals. -namespace chrono { - /// Duration template class. This class provides enough functionality to - /// implement \c this_thread::sleep_for(). - template > class duration { - private: - _Rep rep_; - public: - typedef _Rep rep; - typedef _Period period; - - /// Construct a duration object with the given duration. - template - explicit duration(const _Rep2& r) : rep_(r) {}; - - /// Return the value of the duration object. - rep count() const - { - return rep_; - } - }; - - // Standard duration types. - typedef duration<__intmax_t, ratio<1, 1000000000> > nanoseconds; ///< Duration with the unit nanoseconds. - typedef duration<__intmax_t, ratio<1, 1000000> > microseconds; ///< Duration with the unit microseconds. - typedef duration<__intmax_t, ratio<1, 1000> > milliseconds; ///< Duration with the unit milliseconds. - typedef duration<__intmax_t> seconds; ///< Duration with the unit seconds. - typedef duration<__intmax_t, ratio<60> > minutes; ///< Duration with the unit minutes. - typedef duration<__intmax_t, ratio<3600> > hours; ///< Duration with the unit hours. -} - -/// The namespace \c this_thread provides methods for dealing with the -/// calling thread. -namespace this_thread { - /// Return the thread ID of the calling thread. - thread::id get_id(); - - /// Yield execution to another thread. - /// Offers the operating system the opportunity to schedule another thread - /// that is ready to run on the current processor. - inline void yield() - { -#if defined(_TTHREAD_WIN32_) - Sleep(0); -#else - sched_yield(); -#endif - } - - /// Blocks the calling thread for a period of time. - /// @param[in] aTime Minimum time to put the thread to sleep. - /// Example usage: - /// @code - /// // Sleep for 100 milliseconds - /// this_thread::sleep_for(chrono::milliseconds(100)); - /// @endcode - /// @note Supported duration types are: nanoseconds, microseconds, - /// milliseconds, seconds, minutes and hours. - template void sleep_for(const chrono::duration<_Rep, _Period>& aTime) - { -#if defined(_TTHREAD_WIN32_) - Sleep(int(double(aTime.count()) * (1000.0 * _Period::_as_double()) + 0.5)); -#else - usleep(int(double(aTime.count()) * (1000000.0 * _Period::_as_double()) + 0.5)); -#endif - } -} - -} - -// Define/macro cleanup -#undef _TTHREAD_DISABLE_ASSIGNMENT - -#endif // _TINYTHREAD_H_ From b9a75a320fabd2e991c297233ac90c0c1ec8bc6e Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 12 Jul 2012 01:03:40 +0200 Subject: [PATCH 217/788] Added missing JSON "operator bool()" and size() methods. --- AUTHORS | 4 +--- lib/json.cpp | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/AUTHORS b/AUTHORS index 915355b6..a74a17f1 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,3 +1 @@ -All code was written by DDVTECH employees specifically for this project, with the exception of: - -* tinythread++ library taken verbatim from: http://tinythread.sourceforge.net +All code so far was written by DDVTECH employees. diff --git a/lib/json.cpp b/lib/json.cpp index 6fe60c84..14dd789f 100644 --- a/lib/json.cpp +++ b/lib/json.cpp @@ -283,6 +283,18 @@ JSON::Value::operator std::string(){ } } +/// Automatic conversion to bool. +/// Returns true if there is anything meaningful stored into this value. +JSON::Value::operator bool(){ + if (myType == STRING){return strVal == "";} + if (myType == INTEGER){return intVal == 0;} + if (myType == BOOL){return intVal == 0;} + if (myType == OBJECT){return size() > 0;} + if (myType == ARRAY){return size() > 0;} + if (myType == EMPTY){return false;} + return false;//unimplemented? should never happen... +} + /// Retrieves or sets the JSON::Value at this position in the object. /// Converts destructively to object if not already an object. JSON::Value & JSON::Value::operator[](const std::string i){ @@ -433,6 +445,11 @@ JSON::ArrIter JSON::Value::ArrEnd(){ return arrVal.end(); } +/// Returns the total of the objects and array size combined. +unsigned int JSON::Value::size(){ + return objVal.size() + arrVal.size(); +} + /// Completely clears the contents of this value, /// changing its type to NULL in the process. void JSON::Value::null(){ From 935d5635e57cc90eeab7934b1d10f9910c5cf727 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 20 Jul 2012 17:46:22 +0200 Subject: [PATCH 218/788] Fix multiple-including. --- lib/auth.h | 1 + lib/base64.h | 1 + lib/config.h | 1 + lib/json.h | 1 + lib/procs.h | 1 + 5 files changed, 5 insertions(+) diff --git a/lib/auth.h b/lib/auth.h index 5d8da7b6..42d4580d 100644 --- a/lib/auth.h +++ b/lib/auth.h @@ -1,3 +1,4 @@ +#pragma once #include #include #include diff --git a/lib/base64.h b/lib/base64.h index 2358ae98..6adc5123 100644 --- a/lib/base64.h +++ b/lib/base64.h @@ -1,3 +1,4 @@ +#pragma once #include /// Holds base64 decoding and encoding functions. diff --git a/lib/config.h b/lib/config.h index acf3fb17..04b9b732 100644 --- a/lib/config.h +++ b/lib/config.h @@ -1,6 +1,7 @@ /// \file config.h /// Contains generic function headers for managing configuration. +#pragma once #include #define STRINGIFY(x) #x diff --git a/lib/json.h b/lib/json.h index f8d47aec..3c01ae02 100644 --- a/lib/json.h +++ b/lib/json.h @@ -1,5 +1,6 @@ /// \file json.h Holds all JSON-related headers. +#pragma once #include #include #include diff --git a/lib/procs.h b/lib/procs.h index c10620b8..ceabca3d 100644 --- a/lib/procs.h +++ b/lib/procs.h @@ -1,6 +1,7 @@ /// \file procs.h /// Contains generic function headers for managing processes. +#pragma once #include #include #include From a36ce260ac7f24b5868e75314a652c7033335ace Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 20 Jul 2012 17:48:40 +0200 Subject: [PATCH 219/788] Fixed JSON bool evaluations. --- lib/json.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/json.cpp b/lib/json.cpp index 14dd789f..4f60747e 100644 --- a/lib/json.cpp +++ b/lib/json.cpp @@ -286,9 +286,9 @@ JSON::Value::operator std::string(){ /// Automatic conversion to bool. /// Returns true if there is anything meaningful stored into this value. JSON::Value::operator bool(){ - if (myType == STRING){return strVal == "";} - if (myType == INTEGER){return intVal == 0;} - if (myType == BOOL){return intVal == 0;} + if (myType == STRING){return strVal != "";} + if (myType == INTEGER){return intVal != 0;} + if (myType == BOOL){return intVal != 0;} if (myType == OBJECT){return size() > 0;} if (myType == ARRAY){return size() > 0;} if (myType == EMPTY){return false;} From 5562bea8a0e905eb2b70820b3ce1f52d8d934b55 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 20 Jul 2012 17:48:57 +0200 Subject: [PATCH 220/788] Added handy converter functions to JSON. --- lib/json.cpp | 14 ++++++++++++++ lib/json.h | 3 +++ 2 files changed, 17 insertions(+) diff --git a/lib/json.cpp b/lib/json.cpp index 4f60747e..6e454cf4 100644 --- a/lib/json.cpp +++ b/lib/json.cpp @@ -295,6 +295,20 @@ JSON::Value::operator bool(){ return false;//unimplemented? should never happen... } +/// Explicit conversion to std::string. +const std::string JSON::Value::asString(){ + return (std::string)*this; +} +/// Explicit conversion to long long int. +const long long int JSON::Value::asInt(){ + return (long long int)*this; +} +/// Explicit conversion to bool. +const bool JSON::Value::asBool(){ + return (bool)*this; +} + + /// Retrieves or sets the JSON::Value at this position in the object. /// Converts destructively to object if not already an object. JSON::Value & JSON::Value::operator[](const std::string i){ diff --git a/lib/json.h b/lib/json.h index 3c01ae02..dc35dfcb 100644 --- a/lib/json.h +++ b/lib/json.h @@ -51,6 +51,9 @@ namespace JSON{ operator long long int(); operator std::string(); operator bool(); + const std::string asString(); + const long long int asInt(); + const bool asBool(); //array operator for maps and arrays Value & operator[](const std::string i); Value & operator[](const char * i); From d1e21328795665fac5d46c53a0e490b72a6bf885 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 20 Jul 2012 17:49:16 +0200 Subject: [PATCH 221/788] Updated to new config system. --- lib/config.cpp | 292 +++++++++++++++++++++++++++++++++++++++++++------ lib/config.h | 25 +++-- 2 files changed, 275 insertions(+), 42 deletions(-) diff --git a/lib/config.cpp b/lib/config.cpp index 17cb5d4f..dd37ebcb 100644 --- a/lib/config.cpp +++ b/lib/config.cpp @@ -12,6 +12,7 @@ #endif #include #include +#include #include #include #include @@ -21,56 +22,279 @@ #include #include +bool Util::Config::is_active = false; + /// Creates a new configuration manager. -Util::Config::Config(){ - listen_port = 4242; - daemon_mode = true; - interface = "0.0.0.0"; - username = "root"; +Util::Config::Config(std::string cmd, std::string version){ + vals.null(); + long_count = 0; + vals["cmd"]["current"] = 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"]["current"] = version; } +/// 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: +/// { +/// "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. +/// "default":1234, //The default value 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. +/// } +void Util::Config::addOption(std::string optname, JSON::Value option){ + vals[optname] = option; + 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){ + int longest = 0; + std::map args; + for (JSON::ObjIter it = vals.ObjBegin(); it != vals.ObjEnd(); it++){ + 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::iterator i = args.begin(); i != args.end(); i++){ + if (vals[i->second].isMember("default")){ + 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. -/// confsection must be either already set or never be set at all when this function is called. -/// In other words: do not change confsection after calling this function. void Util::Config::parseArgs(int argc, char ** argv){ int opt = 0; - static const char *optString = "ndvp:i:u:c:h?"; - static const struct option longOpts[] = { - {"help",0,0,'h'}, - {"port",1,0,'p'}, - {"interface",1,0,'i'}, - {"username",1,0,'u'}, - {"nodaemon",0,0,'n'}, - {"daemon",0,0,'d'}, - {"version",0,0,'v'}, - 0 - }; - while ((opt = getopt_long(argc, argv, optString, longOpts, 0)) != -1){ + std::string shortopts; + struct option * longOpts = (struct option*)calloc(long_count+1, sizeof(struct option)); + int long_i = 0; + int arg_count = 0; + 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("default")){ + 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 'p': listen_port = atoi(optarg); break; - case 'i': interface = optarg; break; - case 'n': daemon_mode = false; break; - case 'd': daemon_mode = true; break; - case 'u': username = optarg; break; - case 'v': - printf("%s\n", PACKAGE_VERSION); - exit(1); - break; case 'h': case '?': - std::string doingdaemon = "true"; - if (!daemon_mode){doingdaemon = "false";} - printf("Options: -h[elp], -?, -v[ersion], -n[odaemon], -d[aemon], -p[ort] VAL, -i[nterface] VAL, -u[sername] VAL\n"); - printf("Defaults:\n interface: %s\n port: %i\n daemon mode: %s\n username: %s\n", interface.c_str(), listen_port, doingdaemon.c_str(), username.c_str()); - printf("Username root means no change to UID, no matter what the UID is.\n"); - printf("This is %s version %s\n", argv[0], PACKAGE_VERSION); + 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["current"] = (std::string)optarg; + }else{ + it->second["current"] = 1; + } + break; + } + if (it->second.isMember("short_off") && it->second["short_off"].asString()[0] == opt){ + it->second["current"] = 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["current"] = (std::string)argv[optind]; + optind++; + long_i++; + break; + } + } + } + if (long_i <= arg_count){ + std::cerr << "Usage error: missing argument(s)." << std::endl; + printHelp(std::cout); + exit(1); + } } +/// 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){ + if (!vals.isMember(optname)){ + std::cout << "Fatal error: a non-existent option was accessed." << std::endl; + exit(37); + } + if (vals[optname].isMember("current")){ + return vals[optname]["current"]; + }else{ + return vals[optname]["default"]; + } +} + +/// 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(); +} + +/// Activated the stored config. This will: +/// - Drop permissions to the stored "username". +/// - Daemonize the process if "daemonize" 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(){ + setUser(getString("username")); + if (getBool("daemonize")){Daemonize();} + struct sigaction new_action; + new_action.sa_handler = signal_handler; + sigemptyset (&new_action.sa_mask); + new_action.sa_flags = 0; + sigaction(SIGINT, &new_action, NULL); + sigaction(SIGHUP, &new_action, NULL); + sigaction(SIGTERM, &new_action, NULL); + sigaction(SIGPIPE, &new_action, NULL); + sigaction(SIGCHLD, &new_action, NULL); + 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. + wait(0); + break; + default: //other signals are ignored + break; + } +}//signal_handler + +/// Adds the default connector options to this Util::Config object. +void Util::Config::addConnectorOptions(int port){ + JSON::Value stored_port = JSON::fromString("{\"long\":\"port\", \"short\":\"p\", \"arg\":\"integer\", \"help\":\"TCP port to listen on.\"}"); + stored_port["default"] = port; + addOption("listen_port", stored_port); + addOption("listen_interface", JSON::fromString("{\"long\":\"interface\", \"default\":\"0.0.0.0\", \"short\":\"i\", \"arg\":\"string\", \"help\":\"Interface address to listen on, or 0.0.0.0 for all available interfaces.\"}")); + addOption("username", JSON::fromString("{\"long\":\"username\", \"default\":\"root\", \"short\":\"u\", \"arg\":\"string\", \"help\":\"Username to drop privileges to, or root to not drop provileges.\"}")); + addOption("daemonize", JSON::fromString("{\"long\":\"daemon\", \"short\":\"d\", \"default\":1, \"long_off\":\"nodaemon\", \"short_off\":\"n\", \"help\":\"Whether or not to daemonize the process after starting.\"}")); +}//addConnectorOptions + /// Sets the current process' running user void Util::setUser(std::string username){ if (username != "root"){ diff --git a/lib/config.h b/lib/config.h index 04b9b732..5db09351 100644 --- a/lib/config.h +++ b/lib/config.h @@ -3,22 +3,31 @@ #pragma once #include - -#define STRINGIFY(x) #x -#define TOSTRING(x) STRINGIFY(x) +#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: - bool daemon_mode; - std::string interface; - int listen_port; - std::string username; - Config(); + //variables + static bool is_active; ///< Set to true by activate(), set to false by the signal handler. + //functions + Config(std::string cmd, std::string version); + void addOption(std::string optname, JSON::Value option); + void printHelp(std::ostream & output); void parseArgs(int argc, char ** argv); + JSON::Value & getOption(std::string optname); + std::string getString(std::string optname); + long long int getInteger(std::string optname); + bool getBool(std::string optname); + void activate(); + void addConnectorOptions(int port); }; /// Will set the active user to the named username. From 4641efb79d30ef528ded1d0cdea3b4b9ec5a5872 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 6 Aug 2012 05:35:11 +0200 Subject: [PATCH 222/788] Fortify HTTP parser/generator code. --- lib/http_parser.cpp | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/lib/http_parser.cpp b/lib/http_parser.cpp index 08d11b7e..b132a9a1 100644 --- a/lib/http_parser.cpp +++ b/lib/http_parser.cpp @@ -27,11 +27,12 @@ void HTTP::Parser::Clean(){ std::string HTTP::Parser::BuildRequest(){ /// \todo Include GET/POST variable parsing? std::map::iterator it; + if (protocol.size() < 5 || protocol.substr(0, 4) != "HTTP"){protocol = "HTTP/1.0";} std::string tmp = method+" "+url+" "+protocol+"\n"; for (it=headers.begin(); it != headers.end(); it++){ tmp += (*it).first + ": " + (*it).second + "\n"; } - tmp += "\n" + body + "\n"; + tmp += "\n" + body; return tmp; } @@ -44,6 +45,7 @@ std::string HTTP::Parser::BuildRequest(){ std::string HTTP::Parser::BuildResponse(std::string code, std::string message){ /// \todo Include GET/POST variable parsing? std::map::iterator it; + if (protocol.size() < 5 || protocol.substr(0, 4) != "HTTP"){protocol = "HTTP/1.0";} std::string tmp = protocol+" "+code+" "+message+"\n"; for (it=headers.begin(); it != headers.end(); it++){ tmp += (*it).first + ": " + (*it).second + "\n"; @@ -134,14 +136,17 @@ bool HTTP::Parser::parse(std::string & HTTPbuffer){ if (!seenReq){ seenReq = true; f = tmpA.find(' '); - if (f != std::string::npos){method = tmpA.substr(0, f); tmpA.erase(0, f+1);} - f = tmpA.find(' '); - if (f != std::string::npos){url = tmpA.substr(0, f); tmpA.erase(0, f+1);} - f = tmpA.find(' '); - if (f != std::string::npos){protocol = tmpA.substr(0, f); tmpA.erase(0, f+1);} - if (url.find('?') != std::string::npos){ - parseVars(url.substr(url.find('?')+1)); //parse GET variables - } + if (f != std::string::npos){ + method = tmpA.substr(0, f); tmpA.erase(0, f+1); + f = tmpA.find(' '); + if (f != std::string::npos){ + url = tmpA.substr(0, f); tmpA.erase(0, f+1); + protocol = tmpA; + if (url.find('?') != std::string::npos){ + parseVars(url.substr(url.find('?')+1)); //parse GET variables + } + }else{seenReq = false;} + }else{seenReq = false;} }else{ if (tmpA.size() == 0){ seenHeaders = true; From 08f28d134a5c690cc3c0fee1d6825e4695bd838b Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 13 Aug 2012 07:59:24 +0200 Subject: [PATCH 223/788] Report name of non-existant options to help debugging. --- lib/config.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/config.cpp b/lib/config.cpp index dd37ebcb..0a2327f0 100644 --- a/lib/config.cpp +++ b/lib/config.cpp @@ -219,7 +219,7 @@ void Util::Config::parseArgs(int argc, char ** argv){ /// If the option does not exist, this exits the application with a return code of 37. JSON::Value & Util::Config::getOption(std::string optname){ if (!vals.isMember(optname)){ - std::cout << "Fatal error: a non-existent option was accessed." << std::endl; + std::cout << "Fatal error: a non-existent option '" << optname << "' was accessed." << std::endl; exit(37); } if (vals[optname].isMember("current")){ From 977a8617a2d0f5be533f20d75a9ddb0f9e3777e2 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 14 Aug 2012 20:42:35 +0200 Subject: [PATCH 224/788] Fix Config::activate to not check nonexistant options. --- lib/config.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/config.cpp b/lib/config.cpp index 0a2327f0..194d85c2 100644 --- a/lib/config.cpp +++ b/lib/config.cpp @@ -248,13 +248,17 @@ bool Util::Config::getBool(std::string optname){ } /// Activated the stored config. This will: -/// - Drop permissions to the stored "username". -/// - Daemonize the process if "daemonize" is true. +/// - 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(){ - setUser(getString("username")); - if (getBool("daemonize")){Daemonize();} + if (vals.isMember("username")){ + setUser(getString("username")); + } + if (vals.isMember("daemonize") && getBool("daemonize")){ + Daemonize(); + } struct sigaction new_action; new_action.sa_handler = signal_handler; sigemptyset (&new_action.sa_mask); From 28548f35b08d8454f2549794f9c234c59a02141a Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 14 Aug 2012 20:53:05 +0200 Subject: [PATCH 225/788] Do not report part-termination of processes. --- lib/procs.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/procs.cpp b/lib/procs.cpp index 1512849d..78ac3b25 100644 --- a/lib/procs.cpp +++ b/lib/procs.cpp @@ -32,7 +32,6 @@ void Util::Procs::childsig_handler(int signum){ plist.erase(ret); #if DEBUG >= 1 if (isActive(pname)){ - std::cerr << "Process " << pname << " part-terminated." << std::endl; Stop(pname); }else{ std::cerr << "Process " << pname << " fully terminated." << std::endl; From a86051b3193fe455c4515f6a2b3e44b06a0c7464 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 14 Aug 2012 20:53:32 +0200 Subject: [PATCH 226/788] Removed socket non-errors, added success message for opening listening sockets. --- lib/socket.cpp | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/lib/socket.cpp b/lib/socket.cpp index 727f7de0..1e4458f7 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -203,9 +203,6 @@ signed int Socket::Connection::ready(){ } } if (r == 0){ - #if DEBUG >= 4 - fprintf(stderr, "Socket ready error - socket is closed.\n"); - #endif close(); return -1; } @@ -311,9 +308,6 @@ bool Socket::Connection::read(const void * buffer, int len){ }else{ if (r == 0){ Error = true; - #if DEBUG >= 2 - fprintf(stderr, "Could not read data! Socket is closed.\n"); - #endif close(); down += sofar; return false; @@ -354,9 +348,6 @@ int Socket::Connection::iwrite(const void * buffer, int len){ } } if (r == 0){ - #if DEBUG >= 4 - fprintf(stderr, "Could not iwrite data! Socket is closed.\n"); - #endif close(); } up += r; @@ -385,9 +376,6 @@ int Socket::Connection::iread(void * buffer, int len){ } } if (r == 0){ - #if DEBUG >= 4 - fprintf(stderr, "Could not iread data! Socket is closed.\n"); - #endif close(); } down += r; @@ -522,6 +510,9 @@ bool Socket::Server::IPv6bind(int port, std::string hostname, bool nonblock){ if (ret == 0){ ret = listen(sock, 100);//start listening, backlog of 100 allowed if (ret == 0){ + #if DEBUG >= 1 + fprintf(stderr, "IPv6 socket success @ %s:%i\n", hostname.c_str(), port); + #endif return true; }else{ #if DEBUG >= 1 @@ -571,6 +562,9 @@ bool Socket::Server::IPv4bind(int port, std::string hostname, bool nonblock){ if (ret == 0){ ret = listen(sock, 100);//start listening, backlog of 100 allowed if (ret == 0){ + #if DEBUG >= 1 + fprintf(stderr, "IPv4 socket success @ %s:%i\n", hostname.c_str(), port); + #endif return true; }else{ #if DEBUG >= 1 From 3f089117c78e7c4cc7aca081a3896b1c18df10a6 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Wed, 15 Aug 2012 03:03:05 +0200 Subject: [PATCH 227/788] Apply lekensteyn's workarounds from the DMS repository to prevent compiler warnings. --- lib/amf.cpp | 14 ++++++++++---- lib/dtsc.cpp | 4 +++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/lib/amf.cpp b/lib/amf.cpp index a11788fb..1bd81168 100644 --- a/lib/amf.cpp +++ b/lib/amf.cpp @@ -267,6 +267,7 @@ AMF::Object AMF::parseOne(const unsigned char *& data, unsigned int &len, unsign std::string tmpstr; unsigned int tmpi = 0; unsigned char tmpdbl[8]; + double *d;// hack to work around strict aliasing #if DEBUG >= 10 fprintf(stderr, "Note: AMF type %hhx found. %i bytes left\n", data[i], len-i); #endif @@ -281,7 +282,8 @@ AMF::Object AMF::parseOne(const unsigned char *& data, unsigned int &len, unsign tmpdbl[1] = data[i+7]; tmpdbl[0] = data[i+8]; i+=9;//skip 8(a double)+1 forwards - return AMF::Object(name, *(double*)tmpdbl, AMF::AMF0_NUMBER); + d = (double*)tmpdbl; + return AMF::Object(name, *d, AMF::AMF0_NUMBER); break; case AMF::AMF0_DATE: tmpdbl[7] = data[i+1]; @@ -293,7 +295,8 @@ AMF::Object AMF::parseOne(const unsigned char *& data, unsigned int &len, unsign tmpdbl[1] = data[i+7]; tmpdbl[0] = data[i+8]; i+=11;//skip 8(a double)+1+timezone(2) forwards - return AMF::Object(name, *(double*)tmpdbl, AMF::AMF0_DATE); + d = (double*)tmpdbl; + return AMF::Object(name, *d, AMF::AMF0_DATE); break; case AMF::AMF0_BOOL: i+=2;//skip bool+1 forwards @@ -609,6 +612,7 @@ AMF::Object3 AMF::parseOne3(const unsigned char *& data, unsigned int &len, unsi unsigned int tmpi = 0; unsigned int arrsize = 0; unsigned char tmpdbl[8]; + double *d;// hack to work around strict aliasing #if DEBUG >= 10 fprintf(stderr, "Note: AMF3 type %hhx found. %i bytes left\n", data[i], len-i); #endif @@ -654,7 +658,8 @@ AMF::Object3 AMF::parseOne3(const unsigned char *& data, unsigned int &len, unsi tmpdbl[1] = data[i+7]; tmpdbl[0] = data[i+8]; i+=9;//skip 8(a double)+1 forwards - return AMF::Object3(name, *(double*)tmpdbl, AMF::AMF3_DOUBLE); + d = (double*)tmpdbl; + return AMF::Object3(name, *d, AMF::AMF3_DOUBLE); break; case AMF::AMF3_STRING: if (data[i+1] < 0x80){ @@ -809,8 +814,9 @@ AMF::Object3 AMF::parseOne3(const unsigned char *& data, unsigned int &len, unsi tmpdbl[2] = data[i+5]; tmpdbl[1] = data[i+6]; tmpdbl[0] = data[i+7]; + d = (double*)tmpdbl; i += 8;//skip a double forwards - return AMF::Object3(name, *(double*)tmpdbl, AMF::AMF3_DATE); + return AMF::Object3(name, *d, AMF::AMF3_DATE); break; case AMF::AMF3_ARRAY:{ if (data[i+1] < 0x80){ diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index f73f73c3..31018e18 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -406,6 +406,7 @@ std::string DTSC::DTMI::Pack(bool netpack){ DTSC::DTMI DTSC::parseOneDTMI(const unsigned char *& data, unsigned int &len, unsigned int &i, std::string name){ unsigned int tmpi = 0; unsigned char tmpdbl[8]; + uint64_t * d;// hack to work around strict aliasing #if DEBUG >= 10 fprintf(stderr, "Note: AMF type %hhx found. %i bytes left\n", data[i], len-i); #endif @@ -420,7 +421,8 @@ DTSC::DTMI DTSC::parseOneDTMI(const unsigned char *& data, unsigned int &len, un tmpdbl[1] = data[i+7]; tmpdbl[0] = data[i+8]; i+=9;//skip 8(an uint64_t)+1 forwards - return DTSC::DTMI(name, *(uint64_t*)tmpdbl, DTMI_INT); + d = (uint64_t*)tmpdbl; + return DTSC::DTMI(name, *d, DTMI_INT); break; case DTMI_STRING:{ tmpi = data[i+1]*256*256*256+data[i+2]*256*256+data[i+3]*256+data[i+4];//set tmpi to UTF-8-long length From 06167128bff6b280e7b017a38ec70152eaac087b Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Wed, 15 Aug 2012 11:40:53 +0200 Subject: [PATCH 228/788] Add process termination registration Edited patch because of context change due to: commit 28548f35b08d8454f2549794f9c234c59a02141a Author: Thulinma Date: Tue Aug 14 20:53:05 2012 +0200 Do not report part-termination of processes. --- lib/procs.cpp | 39 ++++++++++++++++++++++++++++++++++++++- lib/procs.h | 4 ++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/lib/procs.cpp b/lib/procs.cpp index 78ac3b25..65782810 100644 --- a/lib/procs.cpp +++ b/lib/procs.cpp @@ -20,12 +20,29 @@ #include std::map Util::Procs::plist; +std::map Util::Procs::exitHandlers; bool Util::Procs::handler_set = false; /// Used internally to capture child signals and update plist. void Util::Procs::childsig_handler(int signum){ if (signum != SIGCHLD){return;} - pid_t ret = wait(0); + int status; + pid_t ret = waitpid(-1, &status, WNOHANG); + if (ret == 0){//ignore, would block otherwise + return; + }else if(ret < 0){ +#if DEBUG >= 3 + std::cerr << "SIGCHLD received, but no child died"; +#endif + return; + } + int exitcode; + if (WIFEXITED(status)){ + exitcode = WEXITSTATUS(status); + }else if (WIFSIGNALED(status)){ + exitcode = -WTERMSIG(status); + }else{/* not possible */return;} + #if DEBUG >= 1 std::string pname = plist[ret]; #endif @@ -34,9 +51,19 @@ void Util::Procs::childsig_handler(int signum){ if (isActive(pname)){ Stop(pname); }else{ + //can this ever happen? std::cerr << "Process " << pname << " fully terminated." << std::endl; } #endif + + TerminationNotifier tn = exitHandlers[ret]; + exitHandlers.erase(ret); + if (tn){ +#if DEBUG >= 2 + std::cerr << "Calling termination handler for " << pname << std::endl; +#endif + tn(ret, exitcode); + } } /// Attempts to run the command cmd. @@ -358,3 +385,13 @@ std::string Util::Procs::getName(pid_t 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; +} diff --git a/lib/procs.h b/lib/procs.h index ceabca3d..f3061261 100644 --- a/lib/procs.h +++ b/lib/procs.h @@ -9,10 +9,13 @@ /// 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 plist; ///< Holds active processes + static std::map 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 runCmd(std::string & cmd); @@ -28,6 +31,7 @@ namespace Util{ static bool isActive(pid_t name); static pid_t getPid(std::string name); static std::string getName(pid_t name); + static bool SetTerminationNotifier(pid_t pid, TerminationNotifier notifier); }; }; From b994bab6d228db764ffd8c1019c0e1e6f02b996f Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Tue, 7 Aug 2012 16:59:37 +0200 Subject: [PATCH 229/788] Add new process function including fds Based on BiPipe from MistPlayer branch v2: fix dup2 of wrong fd, fix invalid fd debug print --- lib/procs.cpp | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++ lib/procs.h | 1 + 2 files changed, 100 insertions(+) diff --git a/lib/procs.cpp b/lib/procs.cpp index 65782810..22c9b67a 100644 --- a/lib/procs.cpp +++ b/lib/procs.cpp @@ -321,6 +321,105 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2, st 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 -1, fd is automatically allocated and written. +/// \arg fdout Standard output file descriptor. If -1, fd is automatically allocated and written. +/// \arg fderr Standard error file descriptor. If -1, fd is automatically allocated and written. +pid_t Util::Procs::StartPiped(std::string name, char * argv[], int * fdin, int * fdout, int * fderr){ + if (isActive(name)){return getPid(name);} + pid_t pid; + int pipein[2], pipeout[2], pipeerr[2]; + if (!handler_set){ + struct sigaction new_action; + new_action.sa_handler = Util::Procs::childsig_handler; + sigemptyset(&new_action.sa_mask); + new_action.sa_flags = 0; + sigaction(SIGCHLD, &new_action, NULL); + handler_set = true; + } + if (*fdin == -1 && pipe(pipein) < 0){ + #if DEBUG >= 1 + std::cerr << "Pipe (in) creation failed for " << name << std::endl; + #endif + return 0; + } + if (*fdout == -1 && pipe(pipeout) < 0) { + #if DEBUG >= 1 + std::cerr << "Pipe (out) creation failed for " << name << std::endl; + #endif + if (*fdin == -1){close(pipein[0]);close(pipein[1]);} + return 0; + } + if (*fderr == -1 && pipe(pipeerr) < 0) { + #if DEBUG >= 1 + std::cerr << "Pipe (err) creation failed for " << name << std::endl; + #endif + if (*fdin == -1){close(pipein [0]);close(pipein [1]);} + if (*fdout == -1){close(pipeout[0]);close(pipeout[1]);} + return 0; + } + pid = fork(); + if (pid == 0){//child + if (*fdin == -1){ + close(pipein[1]);// close unused write end + dup2(pipein[0], STDIN_FILENO); + }else{ + dup2(*fdin, STDIN_FILENO); + } + if (*fdout == -1){ + close(pipeout[0]);// close unused read end + dup2(pipeout[1], STDOUT_FILENO); + }else{ + dup2(*fdout, STDOUT_FILENO); + } + if (*fderr == -1){ + close(pipeerr[0]);// close unused read end + dup2(pipeerr[1], STDERR_FILENO); + }else{ + dup2(*fderr, STDERR_FILENO); + } + execvp(argv[0], argv); + #if DEBUG >= 1 + perror("execvp failed"); + #endif + exit(42); + } else if (pid == -1){ + #if DEBUG >= 1 + std::cerr << "Failed to fork for pipe: " << name << std::endl; + #endif + 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; + } else{//parent + #if DEBUG >= 1 + std::cerr << "Piped process " << name << " started"; + std::cerr << " in=" << (*fdin == -1 ? pipein [1] : *fdin ); + std::cerr << " out=" << (*fdout == -1 ? pipeout[0] : *fdout); + std::cerr << " err=" << (*fderr == -1 ? pipeerr[0] : *fderr); + std::cerr << ", PID " << pid << ": " << argv[0] << std::endl; + #endif + if (*fdin == -1){ + close(pipein[0]);// close unused end end + *fdin = pipein[1]; + } + if (*fdout == -1){ + close(pipeout[1]);// close unused write end + *fdout = pipeout[0]; + } + if (*fderr == -1){ + close(pipeerr[1]);// close unused write end + *fderr = pipeerr[0]; + } + plist.insert(std::pair(pid, name)); + } + return pid; +} + + /// Stops the named process, if running. /// \arg name (Internal) name of process to stop void Util::Procs::Stop(std::string name){ diff --git a/lib/procs.h b/lib/procs.h index f3061261..cc939de8 100644 --- a/lib/procs.h +++ b/lib/procs.h @@ -23,6 +23,7 @@ namespace Util{ 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(std::string name, char * argv[], int * fdin, int * fdout, int * fderr); static void Stop(std::string name); static void Stop(pid_t name); static void StopAll(); From fa5d27b0859cd7bb3587370f6c0ccd57cb442b99 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Tue, 7 Aug 2012 17:02:22 +0200 Subject: [PATCH 230/788] procs: add support for easy redirection to dev null Simply set a NULL pointer for a fd, StartPiped will then take care of opening /dev/null, redirecting fds to it and close it when necessary. --- lib/procs.cpp | 58 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/lib/procs.cpp b/lib/procs.cpp index 22c9b67a..e9811fc8 100644 --- a/lib/procs.cpp +++ b/lib/procs.cpp @@ -325,9 +325,9 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2, st /// \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 -1, fd is automatically allocated and written. -/// \arg fdout Standard output file descriptor. If -1, fd is automatically allocated and written. -/// \arg fderr Standard error file descriptor. If -1, fd is automatically allocated and written. +/// \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 * argv[], int * fdin, int * fdout, int * fderr){ if (isActive(name)){return getPid(name);} pid_t pid; @@ -340,20 +340,20 @@ pid_t Util::Procs::StartPiped(std::string name, char * argv[], int * fdin, int * sigaction(SIGCHLD, &new_action, NULL); handler_set = true; } - if (*fdin == -1 && pipe(pipein) < 0){ + if (fdin && *fdin == -1 && pipe(pipein) < 0){ #if DEBUG >= 1 std::cerr << "Pipe (in) creation failed for " << name << std::endl; #endif return 0; } - if (*fdout == -1 && pipe(pipeout) < 0) { + if (fdout && *fdout == -1 && pipe(pipeout) < 0) { #if DEBUG >= 1 std::cerr << "Pipe (out) creation failed for " << name << std::endl; #endif if (*fdin == -1){close(pipein[0]);close(pipein[1]);} return 0; } - if (*fderr == -1 && pipe(pipeerr) < 0) { + if (fderr && *fderr == -1 && pipe(pipeerr) < 0) { #if DEBUG >= 1 std::cerr << "Pipe (err) creation failed for " << name << std::endl; #endif @@ -361,21 +361,40 @@ pid_t Util::Procs::StartPiped(std::string name, char * argv[], int * fdin, int * 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){ + #if DEBUG >= 1 + std::cerr << "Could not open /dev/null for " << name << ": " << strerror(errno) << std::endl; + #endif + 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 == -1){ + if (!fdin){ + dup2(devnull, STDIN_FILENO); + }else if (*fdin == -1){ close(pipein[1]);// close unused write end dup2(pipein[0], STDIN_FILENO); }else{ dup2(*fdin, STDIN_FILENO); } - if (*fdout == -1){ + if (!fdout){ + dup2(devnull, STDOUT_FILENO); + }else if (*fdout == -1){ close(pipeout[0]);// close unused read end dup2(pipeout[1], STDOUT_FILENO); }else{ dup2(*fdout, STDOUT_FILENO); } - if (*fderr == -1){ + if (!fderr){ + dup2(devnull, STDERR_FILENO); + }else if (*fderr == -1){ close(pipeerr[0]);// close unused read end dup2(pipeerr[1], STDERR_FILENO); }else{ @@ -390,27 +409,30 @@ pid_t Util::Procs::StartPiped(std::string name, char * argv[], int * fdin, int * #if DEBUG >= 1 std::cerr << "Failed to fork for pipe: " << name << std::endl; #endif - 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]);} + 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 #if DEBUG >= 1 std::cerr << "Piped process " << name << " started"; - std::cerr << " in=" << (*fdin == -1 ? pipein [1] : *fdin ); - std::cerr << " out=" << (*fdout == -1 ? pipeout[0] : *fdout); - std::cerr << " err=" << (*fderr == -1 ? pipeerr[0] : *fderr); + if (fdin ) std::cerr << " in=" << (*fdin == -1 ? pipein [1] : *fdin ); + if (fdout) std::cerr << " out=" << (*fdout == -1 ? pipeout[0] : *fdout); + if (fderr) std::cerr << " err=" << (*fderr == -1 ? pipeerr[0] : *fderr); + if (devnull != -1) std::cerr << " null=" << devnull; std::cerr << ", PID " << pid << ": " << argv[0] << std::endl; #endif - if (*fdin == -1){ + if (devnull != -1){close(devnull);} + if (fdin && *fdin == -1){ close(pipein[0]);// close unused end end *fdin = pipein[1]; } - if (*fdout == -1){ + if (fdout && *fdout == -1){ close(pipeout[1]);// close unused write end *fdout = pipeout[0]; } - if (*fderr == -1){ + if (fderr && *fderr == -1){ close(pipeerr[1]);// close unused write end *fderr = pipeerr[0]; } From 36c7eed7e33f99c49221e7b58eb36eef5cd6f13c Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Tue, 7 Aug 2012 23:51:24 +0200 Subject: [PATCH 231/788] procs: fix leakage of pids in child process --- lib/procs.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/procs.cpp b/lib/procs.cpp index e9811fc8..95ac9ad4 100644 --- a/lib/procs.cpp +++ b/lib/procs.cpp @@ -381,25 +381,32 @@ pid_t Util::Procs::StartPiped(std::string name, char * argv[], int * fdin, int * }else if (*fdin == -1){ close(pipein[1]);// close unused write end dup2(pipein[0], STDIN_FILENO); - }else{ + close(pipein[0]); + }else if (*fdin != STDIN_FILENO){ dup2(*fdin, STDIN_FILENO); + close(*fdin); } if (!fdout){ dup2(devnull, STDOUT_FILENO); }else if (*fdout == -1){ close(pipeout[0]);// close unused read end dup2(pipeout[1], STDOUT_FILENO); - }else{ + close(pipeout[1]); + }else if (*fdout != STDOUT_FILENO){ dup2(*fdout, STDOUT_FILENO); + close(*fdout); } if (!fderr){ dup2(devnull, STDERR_FILENO); }else if (*fderr == -1){ close(pipeerr[0]);// close unused read end dup2(pipeerr[1], STDERR_FILENO); - }else{ + close(pipeerr[1]); + }else if (*fderr != STDERR_FILENO){ dup2(*fderr, STDERR_FILENO); + close(*fderr); } + if (devnull != -1){close(devnull);} execvp(argv[0], argv); #if DEBUG >= 1 perror("execvp failed"); From 0c5f449963f39beb4ee84b04054229c331faf3f7 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 23 Aug 2012 11:07:49 +0200 Subject: [PATCH 232/788] Added DTMI support to JSON library. --- lib/json.cpp | 198 +++++++++++++++++++++++++++++++++++++++++++++++++++ lib/json.h | 10 +++ 2 files changed, 208 insertions(+) diff --git a/lib/json.cpp b/lib/json.cpp index 6e454cf4..84a244ea 100644 --- a/lib/json.cpp +++ b/lib/json.cpp @@ -4,6 +4,7 @@ #include #include #include +#include //for uint64_t int JSON::Value::c2hex(int c){ if (c >= '0' && c <= '9') return c - '0'; @@ -342,6 +343,49 @@ JSON::Value & JSON::Value::operator[](unsigned int i){ return arrVal[i]; } +/// Packs to a std::string for transfer over the network. +/// If the object is a container type, this function will call itself recursively and contain all contents. +std::string JSON::Value::toPacked(){ + std::string r; + if (isInt() || isNull() || isBool()){ + r += 0x01; + uint64_t numval = intVal; + r += *(((char*)&numval)+7); r += *(((char*)&numval)+6); + r += *(((char*)&numval)+5); r += *(((char*)&numval)+4); + r += *(((char*)&numval)+3); r += *(((char*)&numval)+2); + r += *(((char*)&numval)+1); r += *(((char*)&numval)); + } + if (isString()){ + r += 0x02; + r += strVal.size() / (256*256*256); + r += strVal.size() / (256*256); + r += strVal.size() / 256; + r += strVal.size() % 256; + r += strVal; + } + if (isObject()){ + r += 0xE0; + if (objVal.size() > 0){ + for (JSON::ObjIter it = objVal.begin(); it != objVal.end(); it++){ + r += it->first.size() / 256; + r += it->first.size() % 256; + r += it->first; + r += it->second.toPacked(); + } + } + r += (char)0x0; r += (char)0x0; r += (char)0xEE; + } + if (isArray()){ + r += 0x0A; + for (JSON::ArrIter it = arrVal.begin(); it != arrVal.end(); it++){ + r += it->toPacked(); + } + r += (char)0x0; r += (char)0x0; r += (char)0xEE; + } + return r; +};//toPacked + + /// Converts this JSON::Value to valid JSON notation and returns it. /// Makes absolutely no attempts to pretty-print anything. :-) std::string JSON::Value::toString(){ @@ -390,6 +434,63 @@ std::string JSON::Value::toString(){ return "null";//should never get here... } +/// Converts this JSON::Value to valid JSON notation and returns it. +/// Makes an attempt at pretty-printing. +std::string JSON::Value::toPrettyString(int indentation){ + switch (myType){ + case INTEGER: { + std::stringstream st; + st << intVal; + return st.str(); + break; + } + case STRING: { + for (int i = 0; i < 5 && i < strVal.size(); ++i){ + if (strVal[i] < 32 || strVal[i] > 125){ + return JSON::Value((long long int)strVal.size()).asString()+" bytes of binary data"; + } + } + return string_escape(strVal); + break; + } + case ARRAY: { + if (arrVal.size() > 0){ + std::string tmp = "[\n"; + for (ArrIter it = ArrBegin(); it != ArrEnd(); it++){ + tmp += std::string(indentation+2, ' ')+it->toPrettyString(indentation+2); + if (it + 1 != ArrEnd()){tmp += ",\n";} + } + tmp += "\n"+std::string(indentation, ' ')+"]"; + return tmp; + }else{ + return "[]"; + } + break; + } + case OBJECT: { + if (objVal.size() > 0){ + std::string tmp2 = "{\n"; + ObjIter it3 = ObjEnd(); + --it3; + for (ObjIter it2 = ObjBegin(); it2 != ObjEnd(); it2++){ + tmp2 += std::string(indentation+2, ' ')+"\"" + it2->first + "\":"; + tmp2 += it2->second.toPrettyString(indentation+2); + if (it2 != it3){tmp2 += ",\n";} + } + tmp2 += "\n"+std::string(indentation, ' ')+"}"; + return tmp2; + }else{ + return "{}"; + } + break; + } + case EMPTY: + default: + return "null"; + } + return "null";//should never get here... +} + /// Appends the given value to the end of this JSON::Value array. /// Turns this value into an array if it is not already one. void JSON::Value::append(const JSON::Value & rhs){ @@ -439,6 +540,36 @@ bool JSON::Value::isMember(const std::string & name) const{ return objVal.count(name) > 0; } +/// Returns true if this object is an integer. +bool JSON::Value::isInt() const{ + return (myType == INTEGER); +} + +/// Returns true if this object is a string. +bool JSON::Value::isString() const{ + return (myType == STRING); +} + +/// Returns true if this object is a bool. +bool JSON::Value::isBool() const{ + return (myType == BOOL); +} + +/// Returns true if this object is an object. +bool JSON::Value::isObject() const{ + return (myType == OBJECT); +} + +/// Returns true if this object is an array. +bool JSON::Value::isArray() const{ + return (myType == ARRAY); +} + +/// Returns true if this object is null. +bool JSON::Value::isNull() const{ + return (myType == EMPTY); +} + /// Returns an iterator to the begin of the object map, if any. JSON::ObjIter JSON::Value::ObjBegin(){ return objVal.begin(); @@ -488,3 +619,70 @@ JSON::Value JSON::fromFile(std::string filename){ File.close(); return ret; } + +/// Parses a single DTMI type - used recursively by the JSON::fromDTMI functions. +/// This function updates i every call with the new position in the data. +/// \param data The raw data to parse. +/// \param len The size of the raw data. +/// \param i Current parsing position in the raw data (defaults to 0). +/// \returns A single JSON::Value, parsed from the raw data. +JSON::Value JSON::fromDTMI(const unsigned char * data, unsigned int len, unsigned int &i){ + #if DEBUG >= 10 + fprintf(stderr, "Note: AMF type %hhx found. %i bytes left\n", data[i], len-i); + #endif + switch (data[i]){ + case 0x01:{//integer + unsigned char tmpdbl[8]; + tmpdbl[7] = data[i+1]; + tmpdbl[6] = data[i+2]; + tmpdbl[5] = data[i+3]; + tmpdbl[4] = data[i+4]; + tmpdbl[3] = data[i+5]; + tmpdbl[2] = data[i+6]; + tmpdbl[1] = data[i+7]; + tmpdbl[0] = data[i+8]; + i+=9;//skip 8(an uint64_t)+1 forwards + uint64_t * d = (uint64_t*)tmpdbl; + return JSON::Value((long long int)*d); + } break; + case 0x02:{//string + unsigned int tmpi = data[i+1]*256*256*256+data[i+2]*256*256+data[i+3]*256+data[i+4];//set tmpi to UTF-8-long length + std::string tmpstr = std::string((const char *)data+i+5, (size_t)tmpi);//set the string data + i += tmpi + 5;//skip length+size+1 forwards + return JSON::Value(tmpstr); + } break; + case 0xFF://also object + case 0xE0:{//object + ++i; + JSON::Value ret; + while (data[i] + data[i+1] != 0){//while not encountering 0x0000 (we assume 0x0000EE) + unsigned int tmpi = data[i]*256+data[i+1];//set tmpi to the UTF-8 length + std::string tmpstr = std::string((const char *)data+i+2, (size_t)tmpi);//set the string data + i += tmpi + 2;//skip length+size forwards + ret[tmpstr] = fromDTMI(data, len, i);//add content, recursively parsed, updating i, setting indice to tmpstr + } + i += 3;//skip 0x0000EE + return ret; + } break; + case 0x0A:{//array + JSON::Value ret; + ++i; + while (data[i] + data[i+1] != 0){//while not encountering 0x0000 (we assume 0x0000EE) + ret.append(fromDTMI(data, len, i));//add content, recursively parsed, updating i + } + i += 3;//skip 0x0000EE + return ret; + } break; + } + #if DEBUG >= 2 + fprintf(stderr, "Error: Unimplemented DTMI type %hhx - returning.\n", data[i]); + #endif + return JSON::Value(); +}//fromOneDTMI + +/// Parses a std::string to a valid JSON::Value. +/// This function will find one DTMI object in the string and return it. +JSON::Value JSON::fromDTMI(std::string data){ + unsigned int i = 0; + return fromDTMI((const unsigned char*)data.c_str(), data.size(), i); +}//fromDTMI diff --git a/lib/json.h b/lib/json.h index dc35dfcb..00840ab8 100644 --- a/lib/json.h +++ b/lib/json.h @@ -59,12 +59,20 @@ namespace JSON{ Value & operator[](const char * i); Value & operator[](unsigned int i); //handy functions and others + std::string toPacked(); std::string toString(); + std::string toPrettyString(int indentation = 0); 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(); @@ -73,6 +81,8 @@ namespace JSON{ void null(); }; + 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); From 8a9d4d6ee1a3b24db52f17f6832d23e0777e0ba3 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 23 Aug 2012 11:08:39 +0200 Subject: [PATCH 233/788] First version of DTSC::File for easy management of DTSC files. --- lib/dtsc.cpp | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++- lib/dtsc.h | 17 +++++++++ 2 files changed, 115 insertions(+), 1 deletion(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 31018e18..624e99bf 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -2,9 +2,9 @@ /// Holds all code for DDVTECH Stream Container parsing/generation. #include "dtsc.h" +#include #include //for memcmp #include //for htonl/ntohl -#include //for fprint, stderr char DTSC::Magic_Header[] = "DTSC"; char DTSC::Magic_Packet[] = "DTPD"; @@ -477,3 +477,100 @@ DTSC::DTMI DTSC::parseDTMI(const unsigned char * data, unsigned int len){ DTSC::DTMI DTSC::parseDTMI(std::string data){ return parseDTMI((const unsigned char*)data.c_str(), data.size()); }//parse + +/// Open a filename for DTSC reading/writing. +/// If create is true and file does not exist, attempt to create. +DTSC::File::File(std::string filename, bool create){ + if (create){ + F = fopen(filename.c_str(), "w+b"); + }else{ + F = fopen(filename.c_str(), "r+b"); + } + if (!F){ + fprintf(stderr, "Could not open file %s\n", filename.c_str()); + return; + } + + //if first 4 bytes not available, assume empty file, write header + if (fread(buffer, 4, 1, F) != 1){ + fseek(F, 0, SEEK_SET); + fwrite(DTSC::Magic_Header, 4, 1, F); + }else{ + if (memcmp(buffer, DTSC::Magic_Header, 4) != 0){ + fprintf(stderr, "Not a DTSC file - aborting: %s\n", filename.c_str()); + fclose(F); + F = 0; + return; + } + } + //we now know the first 4 bytes are DTSC::Magic_Header and we have a valid file + fseek(F, 4, SEEK_SET); + if (fread(buffer, 4, 1, F) != 1){ + fseek(F, 4, SEEK_SET); + memset(buffer, 0, 4); + fwrite(buffer, 4, 1, F);//write 4 zero-bytes + headerSize = 0; + }else{ + headerSize = ntohl(((uint32_t *)buffer)[0]); + } + fseek(F, 8+headerSize, SEEK_SET); +} + +/// Returns the header metadata for this file as a std::string. +/// Sets the file pointer to the first packet. +std::string & DTSC::File::getHeader(){ + fseek(F, 8, SEEK_SET); + strbuffer.reserve(headerSize); + fread((void*)strbuffer.c_str(), headerSize, 1, F); + fseek(F, 8+headerSize, SEEK_SET); +} + +/// (Re)writes the given string to the header area if the size is the same as the existing header. +/// Forces a write if force is set to true. +bool DTSC::File::writeHeader(std::string & header, bool force){ + if (headerSize != header.size() && !force){ + fprintf(stderr, "Could not overwrite header - not equal size\n"); + return false; + } + headerSize = header.size() - 8; + fseek(F, 0, SEEK_SET); + int ret = fwrite(header.c_str(), 8+headerSize, 1, F); + fseek(F, 8+headerSize, SEEK_SET); + return (ret == 1); +} + +/// Reads the packet available at the current file position, returning it as a std::string. +/// If the packet could not be read for any reason, the reason is printed to stderr and an empty string returned. +std::string & DTSC::File::getPacket(){ + if (fread(buffer, 4, 1, F) != 1){ + fprintf(stderr, "Could not read header\n"); + strbuffer = ""; + return strbuffer; + } + if (memcmp(buffer, DTSC::Magic_Packet, 4) != 0){ + fprintf(stderr, "Could not overwrite header - not equal size\n"); + strbuffer = ""; + return strbuffer; + } + if (fread(buffer, 4, 1, F) != 1){ + fprintf(stderr, "Could not read size\n"); + strbuffer = ""; + return strbuffer; + } + long packSize = ntohl(((uint32_t *)buffer)[0]); + strbuffer.reserve(packSize); + if (fread((void*)strbuffer.c_str(), packSize, 1, F)){ + fprintf(stderr, "Could not read packet\n"); + strbuffer = ""; + return strbuffer; + } + return strbuffer; +} + +/// Close the file if open +DTSC::File::~File(){ + if (F){ + fclose(F); + F = 0; + } +} diff --git a/lib/dtsc.h b/lib/dtsc.h index 428a445e..c9205952 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -8,6 +8,7 @@ #include #include #include +#include //for FILE @@ -102,6 +103,22 @@ namespace DTSC{ extern char Magic_Header[]; ///< The magic bytes for a DTSC header extern char Magic_Packet[]; ///< The magic bytes for a DTSC packet + /// A simple wrapper class that will open a file and allow easy reading/writing of DTSC data from/to it. + class File{ + public: + File(std::string filename, bool create = false); + ~File(); + std::string & getHeader(); + bool writeHeader(std::string & header, bool force = false); + std::string & getPacket(); + private: + std::string strbuffer; + FILE * F; + long headerSize; + char buffer[4]; + };//FileWriter + + /// A part from the DTSC::Stream ringbuffer. /// Holds information about a buffer that will stay consistent class Ring { From 0ac64dd6cb490ca62374eda9163e4f6e4a71d1ba Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Thu, 23 Aug 2012 12:06:26 +0200 Subject: [PATCH 234/788] FTP Convert to LibMist --- lib/Makefile.am | 4 +- lib/filesystem.cpp | 273 +++++++++++++++++++++++++++++++ lib/filesystem.h | 69 ++++++++ lib/ftp.cpp | 399 +++++++++++++++++++++++++++++++++++++++++++++ lib/ftp.h | 77 +++++++++ 5 files changed, 820 insertions(+), 2 deletions(-) create mode 100644 lib/filesystem.cpp create mode 100644 lib/filesystem.h create mode 100644 lib/ftp.cpp create mode 100644 lib/ftp.h diff --git a/lib/Makefile.am b/lib/Makefile.am index 34b8e646..da653e54 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,9 +1,9 @@ lib_LTLIBRARIES=libmist-1.0.la -libmist_1_0_la_SOURCES=amf.h amf.cpp auth.h auth.cpp base64.h base64.cpp config.h config.cpp crypto.h crypto.cpp dtsc.h dtsc.cpp flv_tag.h flv_tag.cpp http_parser.h http_parser.cpp json.h json.cpp procs.h procs.cpp rtmpchunks.h rtmpchunks.cpp socket.h socket.cpp mp4.h mp4.cpp +libmist_1_0_la_SOURCES=amf.h amf.cpp auth.h auth.cpp base64.h base64.cpp config.h config.cpp crypto.h crypto.cpp dtsc.h dtsc.cpp flv_tag.h flv_tag.cpp http_parser.h http_parser.cpp json.h json.cpp procs.h procs.cpp rtmpchunks.h rtmpchunks.cpp socket.h socket.cpp mp4.h mp4.cpp ftp.h ftp.cpp filesystem.h filesystem.cpp libmist_1_0_la_LIBADD=-lssl -lcrypto pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = mist-1.0.pc library_includedir=$(includedir)/mist-1.0/mist -library_include_HEADERS = amf.h auth.h base64.h config.h crypto.h dtsc.h flv_tag.h http_parser.h json.h procs.h rtmpchunks.h socket.h mp4.h +library_include_HEADERS = amf.h auth.h base64.h config.h crypto.h dtsc.h flv_tag.h http_parser.h json.h procs.h rtmpchunks.h socket.h mp4.h ftp.h filesystem.h diff --git a/lib/filesystem.cpp b/lib/filesystem.cpp new file mode 100644 index 00000000..06f5cbb7 --- /dev/null +++ b/lib/filesystem.cpp @@ -0,0 +1,273 @@ +#include "filesystem.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( ) { + fprintf( stderr, "Filling Entries of %s:\n", (MyBase + MyPath).c_str() ); + 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 ) { + fprintf( stderr, "\tSkipping %s\n\t\tReason: %s\n", entry->d_name, strerror(errno) ); + continue; + } + ///Convert stat to string + Entries[ std::string( entry->d_name ) ] = StatBuf; + } + } + fprintf( stderr, "Valid dir: %d\n", ValidDir ); + fprintf( stderr, "#Entries: %d\n", Entries.size() ); +} + +void Filesystem::Directory::Print( ) { + if( !ValidDir ) { + printf( "%s is not a valid directory\n", (MyBase + MyPath).c_str() ); + return; + } + printf( "%s:\n", (MyBase + MyPath).c_str() ); + for( std::map::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 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::iterator it = Entries.begin(); it != Entries.end(); it++ ) { + + bool Active = ( std::find( ActiveStreams.begin(), ActiveStreams.end(), (*it).first ) != ActiveStreams.end() ); + fprintf( stderr, "%s active?: %d\n", (*it).first.c_str(), Active ); + fprintf( stderr, "\tMyPath: %s\n\tVisible: %d\n", MyPath.c_str(), MyVisible[MyPath] ); + fprintf( stderr, "\t\tBitmask S_ACTIVE: %d\n\t\tBitmask S_INACTIVE: %d\n", MyVisible[MyPath] & S_ACTIVE, MyVisible[MyPath] & S_INACTIVE ); + if( ( Active && ( MyVisible[MyPath] & S_ACTIVE ) ) || ( (!Active) && ( MyVisible[MyPath] & S_INACTIVE ) ) || ( ((*it).second.st_mode / 010000 ) == 4 ) ) { + if( ((*it).second.st_mode / 010000) == 4 ) { Converter << 'd'; } 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 += "/"; + fprintf( stderr, "MyPath: %s\n", MyPath.c_str() ); + std::vector TempPath; + std::string TempString; + for( std::string::iterator it = MyPath.begin(); it != MyPath.end(); it ++ ) { + 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::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() ) ) { + fprintf( stderr, "Removing file %s unsuccesfull\n", 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 ) ) { + fprintf( stderr, "Creating directory %s unsuccesfull\n", 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() ) ) { + fprintf( stderr, "Renaming file %s to %s unsuccesfull\n", 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; +} diff --git a/lib/filesystem.h b/lib/filesystem.h new file mode 100644 index 00000000..f20e91a0 --- /dev/null +++ b/lib/filesystem.h @@ -0,0 +1,69 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 ActiveStreams = std::vector() ); + 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 diff --git a/lib/ftp.cpp b/lib/ftp.cpp new file mode 100644 index 00000000..27d893d8 --- /dev/null +++ b/lib/ftp.cpp @@ -0,0 +1,399 @@ +#include "ftp.h" + +FTP::User::User( Socket::Connection NewConnection ) { + Conn = NewConnection; + USER = ""; + PASS = ""; + MODE = MODE_STREAM; + STRU = STRU_FILE; + TYPE = TYPE_ASCII_NONPRINT; + PORT = 20; + RNFR = ""; + + 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 ); + + JSON::Value MyConfig = JSON::fromFile( "/tmp/mist/streamlist" ); + fprintf( stderr, "Streamamount: %d\n", MyConfig["streams"].size() ); + for( JSON::ObjIter it = MyConfig["streams"].ObjBegin(); it != MyConfig["streams"].ObjEnd(); it++ ) { + std::string ThisStream = (*it).second["channel"]["URL"].toString(); + ThisStream.erase( ThisStream.begin() ); + ThisStream.erase( ThisStream.end() - 1 ); + while( ThisStream.find( '/' ) != std::string::npos ) { + ThisStream.erase(0,ThisStream.find('/')+1); + } + ActiveStreams.push_back( ThisStream ); + fprintf( stderr, "\t%s\n", ThisStream.c_str() ); + } +} + +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 = ThisCmd; + 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 = ThisCmd; + if( !LoggedIn( ) ) { + USER = ""; + PASS =""; + return 530;//Not logged in. + } + return 230; + break; + } + case CMD_LIST: { + std::cout << "Listening on :" << MyPassivePort << "\n"; + Socket::Connection Connected = Passive.accept(); + if( Connected.connected() ) { + Conn.write( "125 Data connection already open; transfer starting.\n" ); + } else { + Conn.write( "150 File status okay; about to open data connection.\n" ); + } + while( !Connected.connected() ) { + Connected = Passive.accept(); + } + fprintf( stderr, "Sending LIST information\n" ); + Connected.write( MyDir.LIST( ActiveStreams ) ); + 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); + std::cout << ":" << MyPassivePort << "\n"; + 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; + std::cout << ":" << MyPassivePort << "\n"; + 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. + std::cout << "Listening on :" << MyPassivePort << "\n"; + Socket::Connection Connected = Passive.accept(); + if( Connected.connected() ) { + Conn.write( "125 Data connection already open; transfer starting.\n" ); + } else { + Conn.write( "150 File status okay; about to open data connection.\n" ); + } + while( !Connected.connected() ) { + Connected = Passive.accept(); + } + fprintf( stderr, "Sending RETR information\n" ); + Connected.write( MyDir.RETR( Command ) ); + 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. + std::cout << "Listening on :" << MyPassivePort << "\n"; + Socket::Connection Connected = Passive.accept(); + if( Connected.connected() ) { + Conn.write( "125 Data connection already open; transfer starting.\n" ); + } else { + Conn.write( "150 File status okay; about to open data connection.\n" ); + } + while( !Connected.connected() ) { + Connected = Passive.accept(); + } + fprintf( stderr, "Reading STOR information\n" ); + std::string Buffer; + while( Connected.spool() ) { } + 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; } + return true; +} + +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; +} diff --git a/lib/ftp.h b/lib/ftp.h new file mode 100644 index 00000000..d8e6f49d --- /dev/null +++ b/lib/ftp.h @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include +#include +#include "./socket.h" +#include "./filesystem.h" +#include + +#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 = Socket::Connection() ); + ~User( ); + int ParseCommand( std::string Command ); + bool LoggedIn( ); + std::string NumToMsg( int MsgNum ); + Socket::Connection Conn; + private: + 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 From 75a420db2a3b052fdb2a2c6f8de3de1a56db27bd Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 23 Aug 2012 17:05:32 +0200 Subject: [PATCH 235/788] Fixed DTSC::File (now actually works) and updated DTSC metadata information in the documentation. --- lib/dtsc.cpp | 13 +++++++------ lib/dtsc.h | 8 ++++++-- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 624e99bf..72042b22 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -520,9 +520,10 @@ DTSC::File::File(std::string filename, bool create){ /// Sets the file pointer to the first packet. std::string & DTSC::File::getHeader(){ fseek(F, 8, SEEK_SET); - strbuffer.reserve(headerSize); + strbuffer.resize(headerSize); fread((void*)strbuffer.c_str(), headerSize, 1, F); fseek(F, 8+headerSize, SEEK_SET); + return strbuffer; } /// (Re)writes the given string to the header area if the size is the same as the existing header. @@ -532,9 +533,9 @@ bool DTSC::File::writeHeader(std::string & header, bool force){ fprintf(stderr, "Could not overwrite header - not equal size\n"); return false; } - headerSize = header.size() - 8; - fseek(F, 0, SEEK_SET); - int ret = fwrite(header.c_str(), 8+headerSize, 1, F); + headerSize = header.size(); + fseek(F, 8, SEEK_SET); + int ret = fwrite(header.c_str(), headerSize, 1, F); fseek(F, 8+headerSize, SEEK_SET); return (ret == 1); } @@ -558,8 +559,8 @@ std::string & DTSC::File::getPacket(){ return strbuffer; } long packSize = ntohl(((uint32_t *)buffer)[0]); - strbuffer.reserve(packSize); - if (fread((void*)strbuffer.c_str(), packSize, 1, F)){ + strbuffer.resize(packSize); + if (fread((void*)strbuffer.c_str(), packSize, 1, F) != 1){ fprintf(stderr, "Could not read packet\n"); strbuffer = ""; return strbuffer; diff --git a/lib/dtsc.h b/lib/dtsc.h index c9205952..8a3815a6 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -13,15 +13,19 @@ /// Holds all DDVTECH Stream Container classes and parsers. -///Video: +///length (int, length in seconds, if available) +///video: /// - codec (string: H264, H263, VP6) /// - width (int, pixels) /// - height (int, pixels) /// - fpks (int, frames per kilosecond (FPS * 1000)) /// - bps (int, bytes per second) /// - init (string, init data) +/// - keyms (int, average ms per keyframe) +/// - keyvar (int, max ms per keyframe variance) +/// - keys (array of byte position ints - first is first keyframe, last is last keyframe, in between have ~equal spacing) /// -///Audio: +///audio: /// - codec (string: AAC, MP3) /// - rate (int, Hz) /// - size (int, bitsize) From 48bf3d189abc53c0a953f3850e7174c5bb1260e1 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 23 Aug 2012 17:05:53 +0200 Subject: [PATCH 236/788] Half-updated flv_tag with JSON metadata support. --- lib/flv_tag.cpp | 176 ++++++++++++++++++++++-------------------------- lib/flv_tag.h | 11 +-- 2 files changed, 85 insertions(+), 102 deletions(-) diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index 6a70cc06..95070b19 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -772,8 +772,8 @@ bool FLV::Tag::FileLoader(FILE * f){ return false; }//FLV_GetPacket -DTSC::DTMI FLV::Tag::toDTSC(DTSC::DTMI & metadata){ - DTSC::DTMI pack_out; // Storage for outgoing DTMI data. +JSON::Value FLV::Tag::toJSON(JSON::Value & metadata){ + JSON::Value pack_out; // Storage for outgoing metadata. if (data[0] == 0x12){ AMF::Object meta_in = AMF::parse((unsigned char*)data+11, len-15); @@ -781,45 +781,59 @@ DTSC::DTMI FLV::Tag::toDTSC(DTSC::DTMI & metadata){ AMF::Object * tmp = meta_in.getContentP(1); if (tmp->getContentP("videocodecid")){ switch ((unsigned int)tmp->getContentP("videocodecid")->NumValue()){ - case 2: Meta_Put(metadata, "video", "codec", "H263"); break; - case 4: Meta_Put(metadata, "video", "codec", "VP6"); break; - case 7: Meta_Put(metadata, "video", "codec", "H264"); break; - default: Meta_Put(metadata, "video", "codec", "?"); break; + case 2: metadata["video"]["codec"] = "H263"; break; + case 4: metadata["video"]["codec"] = "VP6"; break; + case 7: metadata["video"]["codec"] = "H264"; break; + default: metadata["video"]["codec"] = "?"; break; } } if (tmp->getContentP("audiocodecid")){ switch ((unsigned int)tmp->getContentP("audiocodecid")->NumValue()){ - case 2: Meta_Put(metadata, "audio", "codec", "MP3"); break; - case 10: Meta_Put(metadata, "audio", "codec", "AAC"); break; - default: Meta_Put(metadata, "audio", "codec", "?"); break; + case 2: metadata["audio"]["codec"] = "MP3"; break; + case 10: metadata["audio"]["codec"] = "AAC"; break; + default: metadata["audio"]["codec"] = "?"; break; } } if (tmp->getContentP("width")){ - Meta_Put(metadata, "video", "width", (unsigned long long int)tmp->getContentP("width")->NumValue()); + metadata["video"]["width"] = (long long int)tmp->getContentP("width")->NumValue(); } if (tmp->getContentP("height")){ - Meta_Put(metadata, "video", "height", (unsigned long long int)tmp->getContentP("height")->NumValue()); + metadata["video"]["height"] = (long long int)tmp->getContentP("height")->NumValue(); } if (tmp->getContentP("framerate")){ - Meta_Put(metadata, "video", "fpks", (unsigned long long int)tmp->getContentP("framerate")->NumValue()*1000); + metadata["video"]["fpks"] = (long long int)tmp->getContentP("framerate")->NumValue()*1000; } if (tmp->getContentP("videodatarate")){ - Meta_Put(metadata, "video", "bps", (unsigned long long int)(tmp->getContentP("videodatarate")->NumValue()*1024)/8); + metadata["video"]["bps"] = (long long int)(tmp->getContentP("videodatarate")->NumValue()*1024)/8; } if (tmp->getContentP("audiodatarate")){ - Meta_Put(metadata, "audio", "bps", (unsigned long long int)(tmp->getContentP("audiodatarate")->NumValue()*1024)/8); + metadata["audio"]["bps"] = (long long int)(tmp->getContentP("audiodatarate")->NumValue()*1024)/8; } if (tmp->getContentP("audiosamplerate")){ - Meta_Put(metadata, "audio", "rate", (unsigned long long int)tmp->getContentP("audiosamplerate")->NumValue()); + metadata["audio"]["rate"] = (long long int)tmp->getContentP("audiosamplerate")->NumValue(); } if (tmp->getContentP("audiosamplesize")){ - Meta_Put(metadata, "audio", "size", (unsigned long long int)tmp->getContentP("audiosamplesize")->NumValue()); + metadata["audio"]["size"] = (long long int)tmp->getContentP("audiosamplesize")->NumValue(); } if (tmp->getContentP("stereo")){ if (tmp->getContentP("stereo")->NumValue() == 1){ - Meta_Put(metadata, "audio", "channels", 2); + metadata["audio"]["channels"] = 2; }else{ - Meta_Put(metadata, "audio", "channels", 1); + metadata["audio"]["channels"] = 1; + } + } + } + if (!metadata.isMember("length")){metadata["length"] = 0;} + if (metadata.isMember("video")){ + if (!metadata["video"].isMember("width")){metadata["video"]["width"] = 0;} + if (!metadata["video"].isMember("height")){metadata["video"]["height"] = 0;} + if (!metadata["video"].isMember("fpks")){metadata["video"]["fpks"] = 0;} + if (!metadata["video"].isMember("bps")){metadata["video"]["bps"] = 0;} + if (!metadata["video"].isMember("keyms")){metadata["video"]["keyms"] = 0;} + if (!metadata["video"].isMember("keyvar")){metadata["video"]["keyvar"] = 0;} + if (!metadata["video"].isMember("keys")){ + while (metadata["video"]["keys"].size() < 100){ + metadata["video"]["keys"].append(JSON::Value((long long int)0)); } } } @@ -829,48 +843,47 @@ DTSC::DTMI FLV::Tag::toDTSC(DTSC::DTMI & metadata){ char audiodata = data[11]; if (needsInitData() && isInitData()){ if ((audiodata & 0xF0) == 0xA0){ - Meta_Put(metadata, "audio", "init", std::string((char*)data+13, (size_t)len-17)); + metadata["audio"]["init"] = std::string((char*)data+13, (size_t)len-17); }else{ - Meta_Put(metadata, "audio", "init", std::string((char*)data+12, (size_t)len-16)); + metadata["audio"]["init"] = std::string((char*)data+12, (size_t)len-16); } return pack_out;//skip rest of parsing, get next tag. } - pack_out = DTSC::DTMI("audio", DTSC::DTMI_ROOT); - pack_out.addContent(DTSC::DTMI("datatype", "audio")); - pack_out.addContent(DTSC::DTMI("time", tagTime())); - if (!Meta_Has(metadata, "audio", "codec")){ + pack_out["datatype"] = "audio"; + pack_out["time"] = tagTime(); + if (!metadata["audio"].isMember("codec") || metadata["audio"]["size"].asString() == ""){ switch (audiodata & 0xF0){ - case 0x20: Meta_Put(metadata, "audio", "codec", "MP3"); break; - case 0xA0: Meta_Put(metadata, "audio", "codec", "AAC"); break; - default: Meta_Put(metadata, "audio", "codec", "?"); break; + case 0x20: metadata["audio"]["codec"] = "MP3"; break; + case 0xA0: metadata["audio"]["codec"] = "AAC"; break; + default: metadata["audio"]["codec"] = "?"; break; } } - if (!Meta_Has(metadata, "audio", "rate")){ + if (!metadata["audio"].isMember("rate") || metadata["audio"]["rate"].asInt() < 1){ switch (audiodata & 0x0C){ - case 0x0: Meta_Put(metadata, "audio", "rate", 5512); break; - case 0x4: Meta_Put(metadata, "audio", "rate", 11025); break; - case 0x8: Meta_Put(metadata, "audio", "rate", 22050); break; - case 0xC: Meta_Put(metadata, "audio", "rate", 44100); break; + case 0x0: metadata["audio"]["rate"] = 5512; break; + case 0x4: metadata["audio"]["rate"] = 11025; break; + case 0x8: metadata["audio"]["rate"] = 22050; break; + case 0xC: metadata["audio"]["rate"] = 44100; break; } } - if (!Meta_Has(metadata, "audio", "size")){ + if (!metadata["audio"].isMember("size") || metadata["audio"]["size"].asInt() < 1){ switch (audiodata & 0x02){ - case 0x0: Meta_Put(metadata, "audio", "size", 8); break; - case 0x2: Meta_Put(metadata, "audio", "size", 16); break; + case 0x0: metadata["audio"]["size"] = 8; break; + case 0x2: metadata["audio"]["size"] = 16; break; } } - if (!Meta_Has(metadata, "audio", "channels")){ + if (!metadata["audio"].isMember("channels") || metadata["audio"]["channels"].asInt() < 1){ switch (audiodata & 0x01){ - case 0x0: Meta_Put(metadata, "audio", "channels", 1); break; - case 0x1: Meta_Put(metadata, "audio", "channels", 2); break; + case 0x0: metadata["audio"]["channels"] = 1; break; + case 0x1: metadata["audio"]["channels"] = 2; break; } } if ((audiodata & 0xF0) == 0xA0){ - if (len < 18){return DTSC::DTMI();} - pack_out.addContent(DTSC::DTMI("data", std::string((char*)data+13, (size_t)len-17))); + if (len < 18){return JSON::Value();} + pack_out["data"] = std::string((char*)data+13, (size_t)len-17); }else{ - if (len < 17){return DTSC::DTMI();} - pack_out.addContent(DTSC::DTMI("data", std::string((char*)data+12, (size_t)len-16))); + if (len < 17){return JSON::Value();} + pack_out["data"] = std::string((char*)data+12, (size_t)len-16); } return pack_out; } @@ -878,77 +891,46 @@ DTSC::DTMI FLV::Tag::toDTSC(DTSC::DTMI & metadata){ char videodata = data[11]; if (needsInitData() && isInitData()){ if ((videodata & 0x0F) == 7){ - if (len < 21){return DTSC::DTMI();} - Meta_Put(metadata, "video", "init", std::string((char*)data+16, (size_t)len-20)); + if (len < 21){return JSON::Value();} + metadata["video"]["init"] = std::string((char*)data+16, (size_t)len-20); }else{ - if (len < 17){return DTSC::DTMI();} - Meta_Put(metadata, "video", "init", std::string((char*)data+12, (size_t)len-16)); + if (len < 17){return JSON::Value();} + metadata["video"]["init"] = std::string((char*)data+12, (size_t)len-16); } return pack_out;//skip rest of parsing, get next tag. } - if (!Meta_Has(metadata, "video", "codec")){ + if (!metadata["video"].isMember("codec") || metadata["video"]["codec"].asString() == ""){ switch (videodata & 0x0F){ - case 2: Meta_Put(metadata, "video", "codec", "H263"); break; - case 4: Meta_Put(metadata, "video", "codec", "VP6"); break; - case 7: Meta_Put(metadata, "video", "codec", "H264"); break; - default: Meta_Put(metadata, "video", "codec", "?"); break; + case 2: metadata["video"]["codec"] = "H263"; break; + case 4: metadata["video"]["codec"] = "VP6"; break; + case 7: metadata["video"]["codec"] = "H264"; break; + default: metadata["video"]["codec"] = "?"; break; } } - pack_out = DTSC::DTMI("video", DTSC::DTMI_ROOT); - pack_out.addContent(DTSC::DTMI("datatype", "video")); + pack_out["datatype"] = "video"; switch (videodata & 0xF0){ - case 0x10: pack_out.addContent(DTSC::DTMI("keyframe", 1)); break; - case 0x20: pack_out.addContent(DTSC::DTMI("interframe", 1)); break; - case 0x30: pack_out.addContent(DTSC::DTMI("disposableframe", 1)); break; - case 0x40: pack_out.addContent(DTSC::DTMI("keyframe", 1)); break; - case 0x50: return DTSC::DTMI(); break;//the video info byte we just throw away - useless to us... + case 0x10: pack_out["keyframe"] = 1; break; + case 0x20: pack_out["interframe"] = 1; break; + case 0x30: pack_out["disposableframe"] = 1; break; + case 0x40: pack_out["keyframe"] = 1; break; + case 0x50: return JSON::Value(); break;//the video info byte we just throw away - useless to us... } - pack_out.addContent(DTSC::DTMI("time", tagTime())); + pack_out["time"] = tagTime(); if ((videodata & 0x0F) == 7){ switch (data[12]){ - case 1: pack_out.addContent(DTSC::DTMI("nalu", 1)); break; - case 2: pack_out.addContent(DTSC::DTMI("nalu_end", 1)); break; + case 1: pack_out["nalu"] = 1; break; + case 2: pack_out["nalu_end"] = 1; break; } int offset = (data[13] << 16) + (data[14] << 8) + data[15]; offset = (offset << 8) >> 8; - pack_out.addContent(DTSC::DTMI("offset", offset)); - if (len < 21){return DTSC::DTMI();} - pack_out.addContent(DTSC::DTMI("data", std::string((char*)data+16, (size_t)len-20))); + pack_out["offset"] = offset; + if (len < 21){return JSON::Value();} + pack_out["data"] = std::string((char*)data+16, (size_t)len-20); }else{ - if (len < 17){return DTSC::DTMI();} - pack_out.addContent(DTSC::DTMI("data", std::string((char*)data+12, (size_t)len-16))); + if (len < 17){return JSON::Value();} + pack_out["data"] = std::string((char*)data+12, (size_t)len-16); } return pack_out; } return pack_out;//should never get here -}//FLV::Tag::toDTSC - -/// Inserts std::string type metadata into the passed DTMI object. -/// \arg meta The DTMI object to put the metadata into. -/// \arg cat Metadata category to insert into. -/// \arg elem Element name to put into the category. -/// \arg val Value to put into the element name. -void FLV::Tag::Meta_Put(DTSC::DTMI & meta, std::string cat, std::string elem, std::string val){ - if (meta.getContentP(cat) == 0){meta.addContent(DTSC::DTMI(cat));} - meta.getContentP(cat)->addContent(DTSC::DTMI(elem, val)); -} - -/// Inserts uint64_t type metadata into the passed DTMI object. -/// \arg meta The DTMI object to put the metadata into. -/// \arg cat Metadata category to insert into. -/// \arg elem Element name to put into the category. -/// \arg val Value to put into the element name. -void FLV::Tag::Meta_Put(DTSC::DTMI & meta, std::string cat, std::string elem, uint64_t val){ - if (meta.getContentP(cat) == 0){meta.addContent(DTSC::DTMI(cat));} - meta.getContentP(cat)->addContent(DTSC::DTMI(elem, val)); -} - -/// Returns true if the named category and elementname are available in the metadata. -/// \arg meta The DTMI object to check. -/// \arg cat Metadata category to check. -/// \arg elem Element name to check. -bool FLV::Tag::Meta_Has(DTSC::DTMI & meta, std::string cat, std::string elem){ - if (meta.getContentP(cat) == 0){return false;} - if (meta.getContentP(cat)->getContentP(elem) == 0){return false;} - return true; -} +}//FLV::Tag::toJSON diff --git a/lib/flv_tag.h b/lib/flv_tag.h index 6848995c..4e61fc6a 100644 --- a/lib/flv_tag.h +++ b/lib/flv_tag.h @@ -4,6 +4,7 @@ #pragma once #include "socket.h" #include "dtsc.h" +#include "json.h" #include //forward declaration of RTMPStream::Chunk to avoid circular dependencies. @@ -43,7 +44,7 @@ namespace FLV { bool DTSCVideoInit(DTSC::Stream & S); bool DTSCAudioInit(DTSC::Stream & S); bool DTSCMetaInit(DTSC::Stream & S); - DTSC::DTMI toDTSC(DTSC::DTMI & metadata); + JSON::Value toJSON(JSON::Value & metadata); bool MemLoader(char * D, unsigned int S, unsigned int & P); bool SockLoader(int sock); bool SockLoader(Socket::Connection sock); @@ -57,10 +58,10 @@ namespace FLV { bool MemReadUntil(char * buffer, unsigned int count, unsigned int & sofar, char * D, unsigned int S, unsigned int & P); bool SockReadUntil(char * buffer, unsigned int count, unsigned int & sofar, Socket::Connection & sock); bool FileReadUntil(char * buffer, unsigned int count, unsigned int & sofar, FILE * f); - //DTSC writer helpers - void Meta_Put(DTSC::DTMI & meta, std::string cat, std::string elem, std::string val); - void Meta_Put(DTSC::DTMI & meta, std::string cat, std::string elem, uint64_t val); - bool Meta_Has(DTSC::DTMI & meta, std::string cat, std::string elem); + //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 From 4ac7c54698c93def4ad353d3ac68bb6aec5da011 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 23 Aug 2012 17:06:25 +0200 Subject: [PATCH 237/788] Updated mp4 to parse some information from JSON-style metadata, guesses values if not present. --- lib/mp4.cpp | 11 ++++++++--- lib/mp4.h | 3 ++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 85ded767..736acbd4 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -1,7 +1,8 @@ -#include "mp4.h" #include //for malloc and free #include //for memcpy #include //for htonl and friends +#include "mp4.h" +#include "json.h" /// Contains all MP4 format related code. namespace MP4{ @@ -363,12 +364,16 @@ void ASRT::WriteContent( ) { SetPayload((uint32_t)4,Box::uint32_to_uint8((isUpdate ? 1 : 0))); } -std::string GenerateLiveBootstrap( uint32_t CurMediaTime ) { +std::string GenerateLiveBootstrap( JSON::Value & metadata ) { AFRT afrt; afrt.SetUpdate(false); afrt.SetTimeScale(1000); afrt.AddQualityEntry(""); - afrt.AddFragmentRunEntry(1, 0 , 4000); //FirstFragment, FirstFragmentTimestamp,Fragment Duration in milliseconds + if (!metadata.isMember("video") || !metadata["video"].isMember("keyms")){ + afrt.AddFragmentRunEntry(1, 0, 1000); //FirstFragment, FirstFragmentTimestamp,Fragment Duration in milliseconds + }else{ + afrt.AddFragmentRunEntry(1, 0, metadata["video"]["keyms"].asInt()); //FirstFragment, FirstFragmentTimestamp,Fragment Duration in milliseconds + } afrt.WriteContent(); ASRT asrt; diff --git a/lib/mp4.h b/lib/mp4.h index 0ef24659..2543fed0 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -2,6 +2,7 @@ #include #include #include +#include "json.h" /// Contains all MP4 format related code. namespace MP4{ @@ -125,7 +126,7 @@ namespace MP4{ Box * Container; };//ASRT Box - std::string GenerateLiveBootstrap( uint32_t CurMediaTime ); + std::string GenerateLiveBootstrap( JSON::Value & metadata ); std::string mdatFold(std::string data); }; From a6b072988c4b4966add925cf989b950d7503d3d6 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 24 Aug 2012 11:32:02 +0200 Subject: [PATCH 238/788] Added toNetPacked() to JSON, removed DTSC::DTMI completely (now superseded by JSON). --- lib/dtsc.cpp | 326 +++--------------------------------------------- lib/dtsc.h | 55 +------- lib/flv_tag.cpp | 102 +++++++-------- lib/json.cpp | 35 ++++++ lib/json.h | 6 + 5 files changed, 113 insertions(+), 411 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 72042b22..86d36257 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -26,7 +26,7 @@ DTSC::Stream::Stream(unsigned int rbuffers){ /// Returns the time in milliseconds of the last received packet. /// This is _not_ the time this packet was received, only the stored time. unsigned int DTSC::Stream::getTime(){ - return buffers.front().getContentP("time")->NumValue(); + return buffers.front()["time"].asInt(); } /// Attempts to parse a packet from the given std::string buffer. @@ -40,20 +40,22 @@ bool DTSC::Stream::parsePacket(std::string & buffer){ if (memcmp(buffer.c_str(), DTSC::Magic_Header, 4) == 0){ len = ntohl(((uint32_t *)buffer.c_str())[1]); if (buffer.length() < len+8){return false;} - metadata = DTSC::parseDTMI((unsigned char*)buffer.c_str() + 8, len); + unsigned int i = 0; + metadata = JSON::fromDTMI((unsigned char*)buffer.c_str() + 8, len, i); buffer.erase(0, len+8); return false; } if (memcmp(buffer.c_str(), DTSC::Magic_Packet, 4) == 0){ len = ntohl(((uint32_t *)buffer.c_str())[1]); if (buffer.length() < len+8){return false;} - buffers.push_front(DTSC::DTMI("empty", DTMI_ROOT)); - buffers.front() = DTSC::parseDTMI((unsigned char*)buffer.c_str() + 8, len); + buffers.push_front(JSON::Value()); + unsigned int i = 0; + buffers.front() = JSON::fromDTMI((unsigned char*)buffer.c_str() + 8, len, i); datapointertype = INVALID; - if (buffers.front().getContentP("data")){ - datapointer = &(buffers.front().getContentP("data")->StrValue()); - if (buffers.front().getContentP("datatype")){ - std::string tmp = buffers.front().getContentP("datatype")->StrValue(); + if (buffers.front().isMember("data")){ + datapointer = &(buffers.front()["data"].strVal); + if (buffers.front().isMember("datatype")){ + std::string tmp = buffers.front()["datatype"].asString(); if (tmp == "video"){datapointertype = VIDEO;} if (tmp == "audio"){datapointertype = AUDIO;} if (tmp == "meta"){datapointertype = META;} @@ -91,7 +93,7 @@ std::string & DTSC::Stream::lastData(){ /// Returns the packed in this buffer number. /// \arg num Buffer number. -DTSC::DTMI & DTSC::Stream::getPacket(unsigned int num){ +JSON::Value & DTSC::Stream::getPacket(unsigned int num){ return buffers[num]; } @@ -102,29 +104,24 @@ DTSC::datatype DTSC::Stream::lastType(){ /// Returns true if the current stream contains at least one video track. bool DTSC::Stream::hasVideo(){ - return (metadata.getContentP("video") != 0); + return metadata.isMember("video"); } /// Returns true if the current stream contains at least one audio track. bool DTSC::Stream::hasAudio(){ - return (metadata.getContentP("audio") != 0); + return metadata.isMember("audio"); } /// Returns a packed DTSC packet, ready to sent over the network. std::string & DTSC::Stream::outPacket(unsigned int num){ static std::string emptystring; if (num >= buffers.size()) return emptystring; - buffers[num].Pack(true); - return buffers[num].packed; + return buffers[num].toNetPacked(); } /// Returns a packed DTSC header, ready to sent over the network. std::string & DTSC::Stream::outHeader(){ - if ((metadata.packed.length() < 4) || !metadata.netpacked){ - metadata.Pack(true); - metadata.packed.replace(0, 4, Magic_Header); - } - return metadata.packed; + return metadata.toNetPacked(); } /// advances all given out and internal Ring classes to point to the new buffer, after one has been added. @@ -142,7 +139,7 @@ void DTSC::Stream::advanceRings(){ dit->b++; if (dit->b >= buffers.size()){keyframes.erase(dit); break;} } - if ((lastType() == VIDEO) && (buffers.front().getContentP("keyframe"))){ + if ((lastType() == VIDEO) && (buffers.front().isMember("keyframe"))){ keyframes.push_front(DTSC::Ring(0)); } //increase buffer size if no keyframes available @@ -187,297 +184,6 @@ DTSC::Stream::~Stream(){ for (sit = rings.begin(); sit != rings.end(); sit++){delete (*sit);} } -/// Returns the std::string Indice for the current object, if available. -/// Returns an empty string if no indice exists. -std::string DTSC::DTMI::Indice(){return myIndice;}; - -/// Returns the DTSC::DTMItype AMF0 object type for this object. -DTSC::DTMItype DTSC::DTMI::GetType(){return myType;}; - -/// Returns the numeric value of this object, if available. -/// If this object holds no numeric value, 0 is returned. -uint64_t & DTSC::DTMI::NumValue(){return numval;}; - -/// Returns the std::string value of this object, if available. -/// If this object holds no string value, an empty string is returned. -std::string & DTSC::DTMI::StrValue(){return strval;}; - -/// Returns the C-string value of this object, if available. -/// If this object holds no string value, an empty C-string is returned. -const char * DTSC::DTMI::Str(){return strval.c_str();}; - -/// Returns a count of the amount of objects this object currently holds. -/// If this object is not a container type, this function will always return 0. -int DTSC::DTMI::hasContent(){return contents.size();}; - -/// Returns true if this DTSC::DTMI value is non-default. -/// Non-default means it is either not a root element or has content. -bool DTSC::DTMI::isEmpty(){ - if (myType != DTMI_ROOT){return false;} - return (hasContent() == 0); -}; - -/// Adds an DTSC::DTMI to this object. Works for all types, but only makes sense for container types. -/// This function resets DTMI::packed to an empty string, forcing a repack on the next call to DTMI::Pack. -/// If the indice name already exists, replaces the indice. -void DTSC::DTMI::addContent(DTSC::DTMI c){ - std::vector::iterator it; - for (it = contents.begin(); it != contents.end(); it++){ - if (it->Indice() == c.Indice()){ - contents.erase(it); - break; - } - } - contents.push_back(c); packed = ""; -}; - -/// Returns a pointer to the object held at indice i. -/// Returns null pointer if no object is held at this indice. -/// \param i The indice of the object in this container. -DTSC::DTMI* DTSC::DTMI::getContentP(int i){ - if (contents.size() <= (unsigned int)i){return 0;} - return &contents.at(i); -}; - -/// Returns a copy of the object held at indice i. -/// Returns a AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice. -/// \param i The indice of the object in this container. -DTSC::DTMI DTSC::DTMI::getContent(int i){return contents.at(i);}; - -/// Returns a pointer to the object held at indice s. -/// Returns NULL if no object is held at this indice. -/// \param s The indice of the object in this container. -DTSC::DTMI* DTSC::DTMI::getContentP(std::string s){ - for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ - if (it->Indice() == s){return &(*it);} - } - return 0; -}; - -/// Returns a copy of the object held at indice s. -/// Returns a AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice. -/// \param s The indice of the object in this container. -DTSC::DTMI DTSC::DTMI::getContent(std::string s){ - for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ - if (it->Indice() == s){return *it;} - } - return DTSC::DTMI("error", DTMI_ROOT); -}; - -/// Default constructor. -/// Simply fills the data with DTSC::DTMI("error", AMF0_DDV_CONTAINER) -DTSC::DTMI::DTMI(){ - *this = DTSC::DTMI("error", DTMI_ROOT); -};//default constructor - -/// Constructor for numeric objects. -/// The object type is by default DTMItype::DTMI_INT, but this can be forced to a different value. -/// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. -/// \param val The numeric value of this object. Numeric objects only support uint64_t values. -/// \param setType The object type to force this object to. -DTSC::DTMI::DTMI(std::string indice, uint64_t val, DTSC::DTMItype setType){//num type initializer - myIndice = indice; - myType = setType; - strval = ""; - numval = val; -}; - -/// Constructor for string objects. -/// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. -/// \param val The string value of this object. -/// \param setType The object type to force this object to. -DTSC::DTMI::DTMI(std::string indice, std::string val, DTSC::DTMItype setType){//str type initializer - myIndice = indice; - myType = setType; - strval = val; - numval = 0; -}; - -/// Constructor for container objects. -/// \param indice The string indice of this object in its container, or empty string if none. -/// \param setType The object type to force this object to. -DTSC::DTMI::DTMI(std::string indice, DTSC::DTMItype setType){//object type initializer - myIndice = indice; - myType = setType; - strval = ""; - numval = 0; -}; - -/// Prints the contents of this object to std::cerr. -/// If this object contains other objects, it will call itself recursively -/// and print all nested content in a nice human-readable format. -void DTSC::DTMI::Print(std::string indent){ - std::cerr << indent; - // print my type - switch (myType){ - case DTMI_INT: std::cerr << "Integer"; break; - case DTMI_STRING: std::cerr << "String"; break; - case DTMI_OBJECT: std::cerr << "Object"; break; - case DTMI_OBJ_END: std::cerr << "Object end"; break; - case DTMI_ROOT: std::cerr << "Root Node"; break; - } - // print my string indice, if available - std::cerr << " " << myIndice << " "; - // print my numeric or string contents - switch (myType){ - case DTMI_INT: std::cerr << numval; break; - case DTMI_STRING: - if (strval.length() > 200 || ((strval.length() > 1) && ( (strval[0] < 'A') || (strval[0] > 'z') ) )){ - std::cerr << strval.length() << " bytes of data"; - }else{ - std::cerr << strval; - } - break; - default: break;//we don't care about the rest, and don't want a compiler warning... - } - std::cerr << std::endl; - // if I hold other objects, print those too, recursively. - if (contents.size() > 0){ - for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){it->Print(indent+" ");} - } -};//print - -/// Packs the DTMI to a std::string for transfer over the network. -/// If a packed version already exists, does not regenerate it. -/// If the object is a container type, this function will call itself recursively and contain all contents. -/// \arg netpack If true, will pack as a full DTMI packet, if false only as the contents without header. -std::string DTSC::DTMI::Pack(bool netpack){ - if (packed != ""){ - if (netpacked == netpack){return packed;} - if (netpacked){ - packed.erase(0, 8); - }else{ - unsigned int size = htonl(packed.length()); - packed.insert(0, (char*)&size, 4); - packed.insert(0, Magic_Packet); - } - netpacked = !netpacked; - return packed; - } - std::string r = ""; - r += myType; - //output the properly formatted data stream for this object's contents. - switch (myType){ - case DTMI_INT: - r += *(((char*)&numval)+7); r += *(((char*)&numval)+6); - r += *(((char*)&numval)+5); r += *(((char*)&numval)+4); - r += *(((char*)&numval)+3); r += *(((char*)&numval)+2); - r += *(((char*)&numval)+1); r += *(((char*)&numval)); - break; - case DTMI_STRING: - r += strval.size() / (256*256*256); - r += strval.size() / (256*256); - r += strval.size() / 256; - r += strval.size() % 256; - r += strval; - break; - case DTMI_OBJECT: - case DTMI_ROOT: - if (contents.size() > 0){ - for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ - r += it->Indice().size() / 256; - r += it->Indice().size() % 256; - r += it->Indice(); - r += it->Pack(); - } - } - r += (char)0x0; r += (char)0x0; r += (char)0xEE; - break; - case DTMI_OBJ_END: - break; - } - packed = r; - netpacked = netpack; - if (netpacked){ - unsigned int size = htonl(packed.length()); - packed.insert(0, (char*)&size, 4); - packed.insert(0, Magic_Packet); - } - return packed; -};//pack - -/// Parses a single AMF0 type - used recursively by the AMF::parse() functions. -/// This function updates i every call with the new position in the data. -/// \param data The raw data to parse. -/// \param len The size of the raw data. -/// \param i Current parsing position in the raw data. -/// \param name Indice name for any new object created. -/// \returns A single DTSC::DTMI, parsed from the raw data. -DTSC::DTMI DTSC::parseOneDTMI(const unsigned char *& data, unsigned int &len, unsigned int &i, std::string name){ - unsigned int tmpi = 0; - unsigned char tmpdbl[8]; - uint64_t * d;// hack to work around strict aliasing - #if DEBUG >= 10 - fprintf(stderr, "Note: AMF type %hhx found. %i bytes left\n", data[i], len-i); - #endif - switch (data[i]){ - case DTMI_INT: - tmpdbl[7] = data[i+1]; - tmpdbl[6] = data[i+2]; - tmpdbl[5] = data[i+3]; - tmpdbl[4] = data[i+4]; - tmpdbl[3] = data[i+5]; - tmpdbl[2] = data[i+6]; - tmpdbl[1] = data[i+7]; - tmpdbl[0] = data[i+8]; - i+=9;//skip 8(an uint64_t)+1 forwards - d = (uint64_t*)tmpdbl; - return DTSC::DTMI(name, *d, DTMI_INT); - break; - case DTMI_STRING:{ - tmpi = data[i+1]*256*256*256+data[i+2]*256*256+data[i+3]*256+data[i+4];//set tmpi to UTF-8-long length - std::string tmpstr = std::string((const char *)data+i+5, (size_t)tmpi);//set the string data - i += tmpi + 5;//skip length+size+1 forwards - return DTSC::DTMI(name, tmpstr, DTMI_STRING); - } break; - case DTMI_ROOT:{ - ++i; - DTSC::DTMI ret(name, DTMI_ROOT); - while (data[i] + data[i+1] != 0){//while not encountering 0x0000 (we assume 0x0000EE) - tmpi = data[i]*256+data[i+1];//set tmpi to the UTF-8 length - std::string tmpstr = std::string((const char *)data+i+2, (size_t)tmpi);//set the string data - i += tmpi + 2;//skip length+size forwards - ret.addContent(parseOneDTMI(data, len, i, tmpstr));//add content, recursively parsed, updating i, setting indice to tmpstr - } - i += 3;//skip 0x0000EE - return ret; - } break; - case DTMI_OBJECT:{ - ++i; - DTSC::DTMI ret(name, DTMI_OBJECT); - while (data[i] + data[i+1] != 0){//while not encountering 0x0000 (we assume 0x0000EE) - tmpi = data[i]*256+data[i+1];//set tmpi to the UTF-8 length - std::string tmpstr = std::string((const char *)data+i+2, (size_t)tmpi);//set the string data - i += tmpi + 2;//skip length+size forwards - ret.addContent(parseOneDTMI(data, len, i, tmpstr));//add content, recursively parsed, updating i, setting indice to tmpstr - } - i += 3;//skip 0x0000EE - return ret; - } break; - } - #if DEBUG >= 2 - fprintf(stderr, "Error: Unimplemented DTMI type %hhx - returning.\n", data[i]); - #endif - return DTSC::DTMI("error", DTMI_ROOT); -}//parseOne - -/// Parses a C-string to a valid DTSC::DTMI. -/// This function will find one DTMI object in the string and return it. -DTSC::DTMI DTSC::parseDTMI(const unsigned char * data, unsigned int len){ - DTSC::DTMI ret;//container type - unsigned int i = 0; - ret = parseOneDTMI(data, len, i, ""); - ret.packed = std::string((char*)data, (size_t)len); - ret.netpacked = false; - return ret; -}//parse - -/// Parses a std::string to a valid DTSC::DTMI. -/// This function will find one DTMI object in the string and return it. -DTSC::DTMI DTSC::parseDTMI(std::string data){ - return parseDTMI((const unsigned char*)data.c_str(), data.size()); -}//parse - /// Open a filename for DTSC reading/writing. /// If create is true and file does not exist, attempt to create. DTSC::File::File(std::string filename, bool create){ diff --git a/lib/dtsc.h b/lib/dtsc.h index 8a3815a6..bc23d618 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -9,6 +9,7 @@ #include #include #include //for FILE +#include "json.h" @@ -21,6 +22,7 @@ /// - fpks (int, frames per kilosecond (FPS * 1000)) /// - bps (int, bytes per second) /// - init (string, init data) +/// - keycount (int, count of keyframes) /// - keyms (int, average ms per keyframe) /// - keyvar (int, max ms per keyframe variance) /// - keys (array of byte position ints - first is first keyframe, last is last keyframe, in between have ~equal spacing) @@ -49,53 +51,6 @@ /// - offset (int, unsigned version of signed int! Holds the ms offset between timestamp and proper display time for B-frames) namespace DTSC{ - /// Enumerates all possible DTMI types. - enum DTMItype { - DTMI_INT = 0x01, ///< Unsigned 64-bit integer. - DTMI_STRING = 0x02, ///< String, equivalent to the AMF longstring type. - DTMI_OBJECT = 0xE0, ///< Object, equivalent to the AMF object type. - DTMI_OBJ_END = 0xEE, ///< End of object marker. - DTMI_ROOT = 0xFF ///< Root node for all DTMI data. - }; - - /// Recursive class that holds DDVTECH MediaInfo. - class DTMI { - public: - std::string Indice(); - DTMItype GetType(); - uint64_t & NumValue(); - std::string & StrValue(); - const char * Str(); - int hasContent(); - bool isEmpty(); - void addContent(DTMI c); - DTMI* getContentP(int i); - DTMI getContent(int i); - DTMI* getContentP(std::string s); - DTMI getContent(std::string s); - DTMI(); - DTMI(std::string indice, uint64_t val, DTMItype setType = DTMI_INT); - DTMI(std::string indice, std::string val, DTMItype setType = DTMI_STRING); - DTMI(std::string indice, DTMItype setType = DTMI_OBJECT); - void Print(std::string indent = ""); - std::string Pack(bool netpack = false); - bool netpacked; - std::string packed; - protected: - std::string myIndice; ///< Holds this objects indice, if any. - DTMItype myType; ///< Holds this objects AMF0 type. - std::string strval; ///< Holds this objects string value, if any. - uint64_t numval; ///< Holds this objects numeric value, if any. - std::vector contents; ///< Holds this objects contents, if any (for container types). - };//AMFType - - /// Parses a C-string to a valid DTSC::DTMI. - DTMI parseDTMI(const unsigned char * data, unsigned int len); - /// Parses a std::string to a valid DTSC::DTMI. - DTMI parseDTMI(std::string data); - /// Parses a single DTMI type - used recursively by the DTSC::parseDTMI() functions. - DTMI parseOneDTMI(const unsigned char *& data, unsigned int &len, unsigned int &i, std::string name); - /// This enum holds all possible datatypes for DTSC packets. enum datatype { AUDIO, ///< Stream Audio data @@ -141,8 +96,8 @@ namespace DTSC{ Stream(); ~Stream(); Stream(unsigned int buffers); - DTSC::DTMI metadata; - DTSC::DTMI & getPacket(unsigned int num = 0); + JSON::Value metadata; + JSON::Value & getPacket(unsigned int num = 0); datatype lastType(); std::string & lastData(); bool hasVideo(); @@ -154,7 +109,7 @@ namespace DTSC{ unsigned int getTime(); void dropRing(Ring * ptr); private: - std::deque buffers; + std::deque buffers; std::set rings; std::deque keyframes; void advanceRings(); diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index 95070b19..2c005dc1 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -257,14 +257,14 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ switch (S.lastType()){ case DTSC::VIDEO: len = S.lastData().length() + 16; - if (S.metadata.getContentP("video") && S.metadata.getContentP("video")->getContentP("codec")){ - if (S.metadata.getContentP("video")->getContentP("codec")->StrValue() == "H264"){len += 4;} + if (S.metadata.isMember("video") && S.metadata["video"].isMember("codec")){ + if (S.metadata["video"]["codec"].asString() == "H264"){len += 4;} } break; case DTSC::AUDIO: len = S.lastData().length() + 16; - if (S.metadata.getContentP("audio") && S.metadata.getContentP("audio")->getContentP("codec")){ - if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "AAC"){len += 1;} + if (S.metadata.isMember("audio") && S.metadata["audio"].isMember("codec")){ + if (S.metadata["audio"]["codec"].asString() == "AAC"){len += 1;} } break; case DTSC::META: @@ -289,18 +289,18 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ memcpy(data+12, S.lastData().c_str(), S.lastData().length()); }else{ memcpy(data+16, S.lastData().c_str(), S.lastData().length()); - if (S.getPacket().getContentP("nalu")){data[12] = 1;}else{data[12] = 2;} - int offset = S.getPacket().getContentP("offset")->NumValue(); + if (S.getPacket().isMember("nalu")){data[12] = 1;}else{data[12] = 2;} + int offset = S.getPacket()["offset"].asInt(); data[13] = (offset >> 16) & 0xFF; data[14] = (offset >> 8) & 0XFF; data[15] = offset & 0xFF; } data[11] = 0; - if (S.metadata.getContentP("video")->getContentP("codec")->StrValue() == "H264"){data[11] += 7;} - if (S.metadata.getContentP("video")->getContentP("codec")->StrValue() == "H263"){data[11] += 2;} - if (S.getPacket().getContentP("keyframe")){data[11] += 0x10;} - if (S.getPacket().getContentP("interframe")){data[11] += 0x20;} - if (S.getPacket().getContentP("disposableframe")){data[11] += 0x30;} + if (S.metadata["video"]["codec"].asString() == "H264"){data[11] += 7;} + if (S.metadata["video"]["codec"].asString() == "H263"){data[11] += 2;} + if (S.getPacket().isMember("keyframe")){data[11] += 0x10;} + if (S.getPacket().isMember("interframe")){data[11] += 0x20;} + if (S.getPacket().isMember("disposableframe")){data[11] += 0x30;} break; case DTSC::AUDIO:{ if ((unsigned int)len == S.lastData().length() + 16){ @@ -310,9 +310,9 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ data[12] = 1;//raw AAC data, not sequence header } data[11] = 0; - if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "AAC"){data[11] += 0xA0;} - if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "MP3"){data[11] += 0x20;} - unsigned int datarate = S.metadata.getContentP("audio")->getContentP("rate")->NumValue(); + if (S.metadata["audio"]["codec"].asString() == "AAC"){data[11] += 0xA0;} + if (S.metadata["audio"]["codec"].asString() == "MP3"){data[11] += 0x20;} + unsigned int datarate = S.metadata["audio"]["rate"].asInt(); if (datarate >= 44100){ data[11] += 0x0C; }else if(datarate >= 22050){ @@ -320,8 +320,8 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ }else if(datarate >= 11025){ data[11] += 0x04; } - if (S.metadata.getContentP("audio")->getContentP("size")->NumValue() == 16){data[11] += 0x02;} - if (S.metadata.getContentP("audio")->getContentP("channels")->NumValue() > 1){data[11] += 0x01;} + if (S.metadata["audio"]["size"].asInt() == 16){data[11] += 0x02;} + if (S.metadata["audio"]["channels"].asInt() > 1){data[11] += 0x01;} break; } case DTSC::META: @@ -343,7 +343,7 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ data[8] = 0; data[9] = 0; data[10] = 0; - tagTime(S.getPacket().getContentP("time")->NumValue()); + tagTime(S.getPacket()["time"].asInt()); return true; } @@ -364,8 +364,8 @@ void FLV::Tag::setLen(){ /// Takes the DTSC Video init data and makes it into FLV. /// Assumes init data is available - so check before calling! bool FLV::Tag::DTSCVideoInit(DTSC::Stream & S){ - if (S.metadata.getContentP("video")->getContentP("codec")->StrValue() == "H264"){ - len = S.metadata.getContentP("video")->getContentP("init")->StrValue().length() + 20; + if (S.metadata["video"]["codec"].asString() == "H264"){ + len = S.metadata["video"]["init"].asString().length() + 20; } if (len > 0){ if (!data){ @@ -377,7 +377,7 @@ bool FLV::Tag::DTSCVideoInit(DTSC::Stream & S){ buf = len; } } - memcpy(data+16, S.metadata.getContentP("video")->getContentP("init")->StrValue().c_str(), len-20); + memcpy(data+16, S.metadata["video"]["init"].asString().c_str(), len-20); data[12] = 0;//H264 sequence header data[13] = 0; data[14] = 0; @@ -401,8 +401,8 @@ bool FLV::Tag::DTSCVideoInit(DTSC::Stream & S){ /// Assumes init data is available - so check before calling! bool FLV::Tag::DTSCAudioInit(DTSC::Stream & S){ len = 0; - if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "AAC"){ - len = S.metadata.getContentP("audio")->getContentP("init")->StrValue().length() + 17; + if (S.metadata["audio"]["codec"].asString() == "AAC"){ + len = S.metadata["audio"]["init"].asString().length() + 17; } if (len > 0){ if (!data){ @@ -414,12 +414,12 @@ bool FLV::Tag::DTSCAudioInit(DTSC::Stream & S){ buf = len; } } - memcpy(data+13, S.metadata.getContentP("audio")->getContentP("init")->StrValue().c_str(), len-17); + memcpy(data+13, S.metadata["audio"]["init"].asString().c_str(), len-17); data[12] = 0;//AAC sequence header data[11] = 0; - if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "AAC"){data[11] += 0xA0;} - if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "MP3"){data[11] += 0x20;} - unsigned int datarate = S.metadata.getContentP("audio")->getContentP("rate")->NumValue(); + if (S.metadata["audio"]["codec"].asString() == "AAC"){data[11] += 0xA0;} + if (S.metadata["audio"]["codec"].asString() == "MP3"){data[11] += 0x20;} + unsigned int datarate = S.metadata["audio"]["rate"].asInt(); if (datarate >= 44100){ data[11] += 0x0C; }else if(datarate >= 22050){ @@ -427,8 +427,8 @@ bool FLV::Tag::DTSCAudioInit(DTSC::Stream & S){ }else if(datarate >= 11025){ data[11] += 0x04; } - if (S.metadata.getContentP("audio")->getContentP("size")->NumValue() == 16){data[11] += 0x02;} - if (S.metadata.getContentP("audio")->getContentP("channels")->NumValue() > 1){data[11] += 0x01;} + if (S.metadata["audio"]["size"].asInt() == 16){data[11] += 0x02;} + if (S.metadata["audio"]["channels"].asInt() > 1){data[11] += 0x01;} } setLen(); data[0] = 0x08; @@ -450,54 +450,54 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){ amfdata.addContent(AMF::Object("", "onMetaData")); amfdata.addContent(AMF::Object("", AMF::AMF0_ECMA_ARRAY)); - if (S.metadata.getContentP("video")){ + if (S.metadata.isMember("video")){ amfdata.getContentP(1)->addContent(AMF::Object("hasVideo", 1, AMF::AMF0_BOOL)); - if (S.metadata.getContentP("video")->getContentP("codec")->StrValue() == "H264"){ + if (S.metadata["video"]["codec"].asString() == "H264"){ amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", 7, AMF::AMF0_NUMBER)); } - if (S.metadata.getContentP("video")->getContentP("codec")->StrValue() == "VP6"){ + if (S.metadata["video"]["codec"].asString() == "VP6"){ amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", 4, AMF::AMF0_NUMBER)); } - if (S.metadata.getContentP("video")->getContentP("codec")->StrValue() == "H263"){ + if (S.metadata["video"]["codec"].asString() == "H263"){ amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", 2, AMF::AMF0_NUMBER)); } - if (S.metadata.getContentP("video")->getContentP("width")){ - amfdata.getContentP(1)->addContent(AMF::Object("width", S.metadata.getContentP("video")->getContentP("width")->NumValue(), AMF::AMF0_NUMBER)); + if (S.metadata["video"].isMember("width")){ + amfdata.getContentP(1)->addContent(AMF::Object("width", S.metadata["video"]["width"].asInt(), AMF::AMF0_NUMBER)); } - if (S.metadata.getContentP("video")->getContentP("height")){ - amfdata.getContentP(1)->addContent(AMF::Object("height", S.metadata.getContentP("video")->getContentP("height")->NumValue(), AMF::AMF0_NUMBER)); + if (S.metadata["video"].isMember("height")){ + amfdata.getContentP(1)->addContent(AMF::Object("height", S.metadata["video"]["height"].asInt(), AMF::AMF0_NUMBER)); } - if (S.metadata.getContentP("video")->getContentP("fpks")){ - amfdata.getContentP(1)->addContent(AMF::Object("framerate", (double)S.metadata.getContentP("video")->getContentP("fpks")->NumValue() / 1000.0, AMF::AMF0_NUMBER)); + if (S.metadata["video"].isMember("fpks")){ + amfdata.getContentP(1)->addContent(AMF::Object("framerate", (double)S.metadata["video"]["fpks"].asInt() / 1000.0, AMF::AMF0_NUMBER)); } - if (S.metadata.getContentP("video")->getContentP("bps")){ - amfdata.getContentP(1)->addContent(AMF::Object("videodatarate", ((double)S.metadata.getContentP("video")->getContentP("bps")->NumValue() * 8.0) / 1024.0, AMF::AMF0_NUMBER)); + if (S.metadata["video"].isMember("bps")){ + amfdata.getContentP(1)->addContent(AMF::Object("videodatarate", ((double)S.metadata["video"]["bps"].asInt() * 8.0) / 1024.0, AMF::AMF0_NUMBER)); } } - if (S.metadata.getContentP("audio")){ + if (S.metadata.isMember("audio")){ amfdata.getContentP(1)->addContent(AMF::Object("hasAudio", 1, AMF::AMF0_BOOL)); amfdata.getContentP(1)->addContent(AMF::Object("audiodelay", 0, AMF::AMF0_NUMBER)); - if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "AAC"){ + if (S.metadata["audio"]["codec"].asString() == "AAC"){ amfdata.getContentP(1)->addContent(AMF::Object("audiocodecid", 10, AMF::AMF0_NUMBER)); } - if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "MP3"){ + if (S.metadata["audio"]["codec"].asString() == "MP3"){ amfdata.getContentP(1)->addContent(AMF::Object("audiocodecid", 2, AMF::AMF0_NUMBER)); } - if (S.metadata.getContentP("audio")->getContentP("channels")){ - if (S.metadata.getContentP("audio")->getContentP("channels")->NumValue() > 1){ + if (S.metadata["audio"].isMember("channels")){ + if (S.metadata["audio"]["channels"].asInt() > 1){ amfdata.getContentP(1)->addContent(AMF::Object("stereo", 1, AMF::AMF0_BOOL)); }else{ amfdata.getContentP(1)->addContent(AMF::Object("stereo", 0, AMF::AMF0_BOOL)); } } - if (S.metadata.getContentP("audio")->getContentP("rate")){ - amfdata.getContentP(1)->addContent(AMF::Object("audiosamplerate", S.metadata.getContentP("audio")->getContentP("rate")->NumValue(), AMF::AMF0_NUMBER)); + if (S.metadata["audio"].isMember("rate")){ + amfdata.getContentP(1)->addContent(AMF::Object("audiosamplerate", S.metadata["audio"]["rate"].asInt(), AMF::AMF0_NUMBER)); } - if (S.metadata.getContentP("audio")->getContentP("size")){ - amfdata.getContentP(1)->addContent(AMF::Object("audiosamplesize", S.metadata.getContentP("audio")->getContentP("size")->NumValue(), AMF::AMF0_NUMBER)); + if (S.metadata["audio"].isMember("size")){ + amfdata.getContentP(1)->addContent(AMF::Object("audiosamplesize", S.metadata["audio"]["size"].asInt(), AMF::AMF0_NUMBER)); } - if (S.metadata.getContentP("audio")->getContentP("bps")){ - amfdata.getContentP(1)->addContent(AMF::Object("audiodatarate", ((double)S.metadata.getContentP("audio")->getContentP("bps")->NumValue() * 8.0) / 1024.0, AMF::AMF0_NUMBER)); + if (S.metadata["audio"].isMember("bps")){ + amfdata.getContentP(1)->addContent(AMF::Object("audiodatarate", ((double)S.metadata["audio"]["bps"].asInt() * 8.0) / 1024.0, AMF::AMF0_NUMBER)); } } diff --git a/lib/json.cpp b/lib/json.cpp index 84a244ea..7adf2d32 100644 --- a/lib/json.cpp +++ b/lib/json.cpp @@ -5,6 +5,8 @@ #include #include #include //for uint64_t +#include //for memcpy +#include //for htonl int JSON::Value::c2hex(int c){ if (c >= '0' && c <= '9') return c - '0'; @@ -345,6 +347,7 @@ JSON::Value & JSON::Value::operator[](unsigned int i){ /// Packs to a std::string for transfer over the network. /// If the object is a container type, this function will call itself recursively and contain all contents. +/// As a side effect, this function clear the internal buffer of any object-types. std::string JSON::Value::toPacked(){ std::string r; if (isInt() || isNull() || isBool()){ @@ -374,6 +377,7 @@ std::string JSON::Value::toPacked(){ } } r += (char)0x0; r += (char)0x0; r += (char)0xEE; + strVal.clear(); } if (isArray()){ r += 0x0A; @@ -386,6 +390,37 @@ std::string JSON::Value::toPacked(){ };//toPacked +/// Packs any object-type JSON::Value to a std::string for transfer over the network, including proper DTMI header. +/// Non-object-types will print an error to stderr and return an empty string. +/// This function returns a reference to an internal buffer where the prepared data is kept. +/// The internal buffer is *not* made stale if any changes occur inside the object - subsequent calls to toPacked() will clear the buffer. +std::string & JSON::Value::toNetPacked(){ + static std::string emptystring; + //check if this is legal + if (myType != OBJECT){ + fprintf(stderr, "Fatal error: Only objects may be NetPacked! Aborting.\n"); + return emptystring; + } + //if sneaky storage doesn't contain correct data, re-calculate it + if (strVal.size() == 0 || strVal[0] != 'D' || strVal[1] != 'T'){ + std::string packed = toPacked(); + strVal.resize(packed.size() + 8); + //insert proper header for this type of data + if (isMember("data")){ + memcpy((void*)strVal.c_str(), "DTPD", 4); + }else{ + memcpy((void*)strVal.c_str(), "DTSC", 4); + } + //insert the packet length at bytes 4-7 + unsigned int size = htonl(packed.size()); + memcpy((void*)(strVal.c_str() + 4), (void*)&size, 4); + //copy the rest of the string + memcpy((void*)(strVal.c_str() + 8), packed.c_str(), packed.size()); + } + return strVal; +} + + /// Converts this JSON::Value to valid JSON notation and returns it. /// Makes absolutely no attempts to pretty-print anything. :-) std::string JSON::Value::toString(){ diff --git a/lib/json.h b/lib/json.h index 00840ab8..fee192f2 100644 --- a/lib/json.h +++ b/lib/json.h @@ -6,6 +6,9 @@ #include #include +//empty definition of DTSC::Stream so it can be a friend. +namespace DTSC{class Stream;} + /// JSON-related classes and functions namespace JSON{ @@ -30,6 +33,8 @@ namespace JSON{ int c2hex(int c); static void skipToEnd(std::istream & fromstream); public: + //friends + friend class DTSC::Stream;//for access to strVal //constructors Value(); Value(std::istream & fromstream); @@ -60,6 +65,7 @@ namespace JSON{ Value & operator[](unsigned int i); //handy functions and others std::string toPacked(); + std::string & toNetPacked(); std::string toString(); std::string toPrettyString(int indentation = 0); void append(const Value & rhs); From 5e640c09b9a1f71d4ee80053a3467fb1f07a0179 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 24 Aug 2012 12:09:46 +0200 Subject: [PATCH 239/788] Remove bootstrap generator from MP4 - this is now part of DMS. --- lib/mp4.cpp | 40 ---------------------------------------- lib/mp4.h | 1 - 2 files changed, 41 deletions(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 736acbd4..1753b0f8 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -364,46 +364,6 @@ void ASRT::WriteContent( ) { SetPayload((uint32_t)4,Box::uint32_to_uint8((isUpdate ? 1 : 0))); } -std::string GenerateLiveBootstrap( JSON::Value & metadata ) { - AFRT afrt; - afrt.SetUpdate(false); - afrt.SetTimeScale(1000); - afrt.AddQualityEntry(""); - if (!metadata.isMember("video") || !metadata["video"].isMember("keyms")){ - afrt.AddFragmentRunEntry(1, 0, 1000); //FirstFragment, FirstFragmentTimestamp,Fragment Duration in milliseconds - }else{ - afrt.AddFragmentRunEntry(1, 0, metadata["video"]["keyms"].asInt()); //FirstFragment, FirstFragmentTimestamp,Fragment Duration in milliseconds - } - afrt.WriteContent(); - - ASRT asrt; - asrt.SetUpdate(false); - asrt.AddQualityEntry(""); - asrt.AddSegmentRunEntry(1, 199);//1 Segment, 199 Fragments - asrt.WriteContent(); - - ABST abst; - abst.AddFragmentRunTable(&afrt); - abst.AddSegmentRunTable(&asrt); - abst.SetBootstrapVersion(1); - abst.SetProfile(0); - abst.SetLive(true); - abst.SetUpdate(false); - abst.SetTimeScale(1000); - abst.SetMediaTime(0xFFFFFFFF); - abst.SetSMPTE(0); - abst.SetMovieIdentifier("fifa"); - abst.SetDRM(""); - abst.SetMetaData(""); - abst.AddServerEntry(""); - abst.AddQualityEntry(""); - abst.WriteContent(); - - std::string Result; - Result.append((char*)abst.GetBoxedData(), (int)abst.GetBoxedDataSize()); - return Result; -} - std::string mdatFold(std::string data){ std::string Result; unsigned int t_int; diff --git a/lib/mp4.h b/lib/mp4.h index 2543fed0..50cad77f 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -126,7 +126,6 @@ namespace MP4{ Box * Container; };//ASRT Box - std::string GenerateLiveBootstrap( JSON::Value & metadata ); std::string mdatFold(std::string data); }; From ca0fd8a641937cf063848d5f52c34e14aed84636 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 24 Aug 2012 15:09:35 +0200 Subject: [PATCH 240/788] Fix Util::TerminationNotifier deadlock when calling malloc() inside a signal handler. --- lib/procs.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/procs.cpp b/lib/procs.cpp index 95ac9ad4..1e7c7afb 100644 --- a/lib/procs.cpp +++ b/lib/procs.cpp @@ -56,12 +56,12 @@ void Util::Procs::childsig_handler(int signum){ } #endif - TerminationNotifier tn = exitHandlers[ret]; - exitHandlers.erase(ret); - if (tn){ -#if DEBUG >= 2 + if (exitHandlers.count(ret) > 0){ + TerminationNotifier tn = exitHandlers[ret]; + exitHandlers.erase(ret); + #if DEBUG >= 2 std::cerr << "Calling termination handler for " << pname << std::endl; -#endif + #endif tn(ret, exitcode); } } From 9b3037d0e42042525030b195f4573f42fa856a00 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 24 Aug 2012 17:14:27 +0200 Subject: [PATCH 241/788] Added basic pretty-printing to MP4 boxes. --- lib/mp4.cpp | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/mp4.h | 4 +++ 2 files changed, 75 insertions(+) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 1753b0f8..c24c9e90 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -96,6 +96,11 @@ void Box::ResetPayload( ) { ((unsigned int*)Payload)[0] = htonl(0); } +std::string Box::toPrettyString(int indent){ + return std::string(indent, ' ')+"Unimplemented pretty-printing for this box"; +} + + void ABST::SetBootstrapVersion( uint32_t Version ) { curBootstrapInfoVersion = Version; } @@ -255,6 +260,33 @@ void ABST::WriteContent( ) { SetPayload((uint32_t)4,Box::uint32_to_uint8(curBootstrapInfoVersion),4); } +std::string ABST::toPrettyString(int indent){ + std::string r; + r += std::string(indent, ' ')+"Bootstrap Info\n"; + if (isUpdate){ + r += std::string(indent+1, ' ')+"Update\n"; + }else{ + r += std::string(indent+1, ' ')+"Replacement or new table\n"; + } + if (isLive){ + r += std::string(indent+1, ' ')+"Live\n"; + }else{ + r += std::string(indent+1, ' ')+"Recorded\n"; + } + r += std::string(indent+1, ' ')+"Profile "+JSON::Value((long long int)curProfile).asString()+"\n"; + r += std::string(indent+1, ' ')+"Timescale "+JSON::Value((long long int)curTimeScale).asString()+"\n"; + r += std::string(indent+1, ' ')+"CurrMediaTime "+JSON::Value((long long int)curMediatime).asString()+"\n"; + r += std::string(indent+1, ' ')+"Segment Run Tables "+JSON::Value((long long int)SegmentRunTables.size()).asString()+"\n"; + for( uint32_t i = 0; i < SegmentRunTables.size(); i++ ) { + r += ((ASRT*)SegmentRunTables[i])->toPrettyString(indent+2)+"\n"; + } + r += std::string(indent+1, ' ')+"Fragment Run Tables "+JSON::Value((long long int)FragmentRunTables.size()).asString()+"\n"; + for( uint32_t i = 0; i < FragmentRunTables.size(); i++ ) { + r += ((AFRT*)FragmentRunTables[i])->toPrettyString(indent+2)+"\n"; + } + return r; +} + void AFRT::SetUpdate( bool Update ) { isUpdate = Update; } @@ -316,6 +348,26 @@ void AFRT::WriteContent( ) { SetPayload((uint32_t)4,Box::uint32_to_uint8((isUpdate ? 1 : 0))); } +std::string AFRT::toPrettyString(int indent){ + std::string r; + r += std::string(indent, ' ')+"Fragment Run Table\n"; + if (isUpdate){ + r += std::string(indent+1, ' ')+"Update\n"; + }else{ + r += std::string(indent+1, ' ')+"Replacement or new table\n"; + } + r += std::string(indent+1, ' ')+"Timescale "+JSON::Value((long long int)curTimeScale).asString()+"\n"; + r += std::string(indent+1, ' ')+"Qualities "+JSON::Value((long long int)QualitySegmentUrlModifiers.size()).asString()+"\n"; + for( uint32_t i = 0; i < QualitySegmentUrlModifiers.size(); i++ ) { + r += std::string(indent+2, ' ')+"\""+QualitySegmentUrlModifiers[i]+"\"\n"; + } + r += std::string(indent+1, ' ')+"Fragments "+JSON::Value((long long int)FragmentRunEntryTable.size()).asString()+"\n"; + for( uint32_t i = 0; i < FragmentRunEntryTable.size(); i ++ ) { + r += std::string(indent+2, ' ')+"Duration "+JSON::Value((long long int)FragmentRunEntryTable[i].FragmentDuration).asString()+", starting at "+JSON::Value((long long int)FragmentRunEntryTable[i].FirstFragment).asString()+" @ "+JSON::Value((long long int)FragmentRunEntryTable[i].FirstFragmentTimestamp).asString(); + } + return r; +} + void ASRT::SetUpdate( bool Update ) { isUpdate = Update; } @@ -364,6 +416,25 @@ void ASRT::WriteContent( ) { SetPayload((uint32_t)4,Box::uint32_to_uint8((isUpdate ? 1 : 0))); } +std::string ASRT::toPrettyString(int indent){ + std::string r; + r += std::string(indent, ' ')+"Segment Run Table\n"; + if (isUpdate){ + r += std::string(indent+1, ' ')+"Update\n"; + }else{ + r += std::string(indent+1, ' ')+"Replacement or new table\n"; + } + r += std::string(indent+1, ' ')+"Qualities "+JSON::Value((long long int)QualitySegmentUrlModifiers.size()).asString()+"\n"; + for( uint32_t i = 0; i < QualitySegmentUrlModifiers.size(); i++ ) { + r += std::string(indent+2, ' ')+"\""+QualitySegmentUrlModifiers[i]+"\"\n"; + } + r += std::string(indent+1, ' ')+"Segments "+JSON::Value((long long int)SegmentRunEntryTable.size()).asString()+"\n"; + for( uint32_t i = 0; i < SegmentRunEntryTable.size(); i ++ ) { + r += std::string(indent+2, ' ')+JSON::Value((long long int)SegmentRunEntryTable[i].FragmentsPerSegment).asString()+" fragments per, starting at "+JSON::Value((long long int)SegmentRunEntryTable[i].FirstSegment).asString(); + } + return r; +} + std::string mdatFold(std::string data){ std::string Result; unsigned int t_int; diff --git a/lib/mp4.h b/lib/mp4.h index 50cad77f..d7354ad7 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -25,6 +25,7 @@ namespace MP4{ static uint8_t * uint16_to_uint8( uint16_t data ); static uint8_t * uint8_to_uint8( uint8_t data ); void ResetPayload( ); + std::string toPrettyString(int indent = 0); private: uint8_t * Payload; uint32_t PayloadSize; @@ -58,6 +59,7 @@ namespace MP4{ void AddFragmentRunTable( Box * newFragment, uint32_t Offset = 0 ); void SetVersion( bool NewVersion = 0 ); void WriteContent( ); + std::string toPrettyString(int indent = 0); private: void SetDefaults( ); void SetReserved( ); @@ -95,6 +97,7 @@ namespace MP4{ void AddQualityEntry( std::string Quality = "", uint32_t Offset = 0 ); void AddFragmentRunEntry( uint32_t FirstFragment = 0, uint32_t FirstFragmentTimestamp = 0, uint32_t FragmentsDuration = 1, uint8_t Discontinuity = 0, uint32_t Offset = 0 ); void WriteContent( ); + std::string toPrettyString(int indent = 0); private: void SetDefaults( ); bool isUpdate; @@ -117,6 +120,7 @@ namespace MP4{ void AddSegmentRunEntry( uint32_t FirstSegment = 0, uint32_t FragmentsPerSegment = 100, uint32_t Offset = 0 ); void WriteContent( ); void SetVersion( bool NewVersion = 0 ); + std::string toPrettyString(int indent = 0); private: void SetDefaults( ); bool isUpdate; From 689eb749452c50a00ef637c3951679c5e40c4296 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Sat, 25 Aug 2012 19:21:58 +0200 Subject: [PATCH 242/788] Fix compiler warnings (mainly signed/unsigned comparisons) --- lib/config.cpp | 4 ++-- lib/dtsc.cpp | 6 ++++-- lib/dtsc.h | 2 +- lib/json.cpp | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/config.cpp b/lib/config.cpp index 194d85c2..68172c9e 100644 --- a/lib/config.cpp +++ b/lib/config.cpp @@ -61,10 +61,10 @@ void Util::Config::addOption(std::string optname, JSON::Value option){ /// Prints a usage message to the given output. void Util::Config::printHelp(std::ostream & output){ - int longest = 0; + unsigned int longest = 0; std::map args; for (JSON::ObjIter it = vals.ObjBegin(); it != vals.ObjEnd(); it++){ - int current = 0; + 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;} diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 86d36257..e18d7817 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -217,7 +217,8 @@ DTSC::File::File(std::string filename, bool create){ fwrite(buffer, 4, 1, F);//write 4 zero-bytes headerSize = 0; }else{ - headerSize = ntohl(((uint32_t *)buffer)[0]); + uint32_t * ubuffer = (uint32_t *)buffer; + headerSize = ntohl(ubuffer[0]); } fseek(F, 8+headerSize, SEEK_SET); } @@ -264,7 +265,8 @@ std::string & DTSC::File::getPacket(){ strbuffer = ""; return strbuffer; } - long packSize = ntohl(((uint32_t *)buffer)[0]); + uint32_t * ubuffer = (uint32_t *)buffer; + long packSize = ntohl(ubuffer[0]); strbuffer.resize(packSize); if (fread((void*)strbuffer.c_str(), packSize, 1, F) != 1){ fprintf(stderr, "Could not read packet\n"); diff --git a/lib/dtsc.h b/lib/dtsc.h index bc23d618..2a6ca87d 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -73,7 +73,7 @@ namespace DTSC{ private: std::string strbuffer; FILE * F; - long headerSize; + unsigned long headerSize; char buffer[4]; };//FileWriter diff --git a/lib/json.cpp b/lib/json.cpp index 7adf2d32..f5420d01 100644 --- a/lib/json.cpp +++ b/lib/json.cpp @@ -480,7 +480,7 @@ std::string JSON::Value::toPrettyString(int indentation){ break; } case STRING: { - for (int i = 0; i < 5 && i < strVal.size(); ++i){ + for (unsigned int i = 0; i < 5 && i < strVal.size(); ++i){ if (strVal[i] < 32 || strVal[i] > 125){ return JSON::Value((long long int)strVal.size()).asString()+" bytes of binary data"; } From 95a11c88d528be534514ccc843faafeb01718694 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sat, 25 Aug 2012 22:07:05 +0200 Subject: [PATCH 243/788] Add host overwriting functionality to Socket::Connection. --- lib/socket.cpp | 6 ++++++ lib/socket.h | 1 + 2 files changed, 7 insertions(+) diff --git a/lib/socket.cpp b/lib/socket.cpp index 1e4458f7..89bf05d1 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -442,6 +442,12 @@ std::string Socket::Connection::getHost(){ return remotehost; } +/// Sets hostname for connection manually. +/// Overwrites the detected host, thus possibily making it incorrect. +void setHost(std::string host){ + remotehost = host; +} + /// Returns true if these sockets are the same socket. /// Does not check the internal stats - only the socket itself. bool Socket::Connection::operator== (const Connection &B) const{ diff --git a/lib/socket.h b/lib/socket.h index 0d33573b..2a4f0418 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -54,6 +54,7 @@ namespace Socket{ void Send(std::string data); ///< Appends data to the upbuffer. void close(); ///< Close connection. std::string getHost(); ///< Gets hostname for connection, if available. + void setHost(std::string host); ///< Sets hostname for connection manually. int getSocket(); ///< Returns internal socket number. std::string getError(); ///< Returns a string describing the last error that occured. unsigned int dataUp(); ///< Returns total amount of bytes sent. From fd2211f85f9275020600619f34f13e1b93294ae5 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sat, 25 Aug 2012 22:15:23 +0200 Subject: [PATCH 244/788] Drop the parsed query string from url for incoming HTTP data. --- lib/http_parser.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/http_parser.cpp b/lib/http_parser.cpp index b132a9a1..9e8291a5 100644 --- a/lib/http_parser.cpp +++ b/lib/http_parser.cpp @@ -144,6 +144,7 @@ bool HTTP::Parser::parse(std::string & HTTPbuffer){ protocol = tmpA; if (url.find('?') != std::string::npos){ parseVars(url.substr(url.find('?')+1)); //parse GET variables + url.resize(url.find('?')); } }else{seenReq = false;} }else{seenReq = false;} From 9ca532523c5fd10402fba89254e8b89e64da81d9 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sat, 25 Aug 2012 22:16:05 +0200 Subject: [PATCH 245/788] Typo fix. --- lib/socket.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/socket.cpp b/lib/socket.cpp index 89bf05d1..518c77db 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -444,7 +444,7 @@ std::string Socket::Connection::getHost(){ /// Sets hostname for connection manually. /// Overwrites the detected host, thus possibily making it incorrect. -void setHost(std::string host){ +void Socket::Connection::setHost(std::string host){ remotehost = host; } From 0e0ecb1bdf0a19c74662ae086ff303b405c2e489 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 26 Aug 2012 20:08:16 +0200 Subject: [PATCH 246/788] Stripped some unused code from Socket::Connection and FLV::Tag, made some Socket::Connection methods private. --- lib/flv_tag.cpp | 73 ------------------ lib/flv_tag.h | 3 - lib/socket.cpp | 192 ++++++++---------------------------------------- lib/socket.h | 37 +++++----- 4 files changed, 47 insertions(+), 258 deletions(-) diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index 2c005dc1..89694a3f 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -627,79 +627,6 @@ bool FLV::Tag::MemLoader(char * D, unsigned int S, unsigned int & P){ }//Tag::MemLoader -/// Helper function for FLV::SockLoader. -/// This function will try to read count bytes from socket sock into buffer. -/// This function should be called repeatedly until true. -/// \param buffer The target buffer. -/// \param count Amount of bytes to read. -/// \param sofar Current amount read. -/// \param sock Socket to read from. -/// \return True if count bytes are read succesfully, false otherwise. -bool FLV::Tag::SockReadUntil(char * buffer, unsigned int count, unsigned int & sofar, Socket::Connection & sock){ - if (sofar >= count){return true;} - int r = 0; - r = sock.iread(buffer + sofar,count-sofar); - sofar += r; - if (sofar >= count){return true;} - return false; -}//Tag::SockReadUntil - -/// Try to load a tag from a socket. -/// This is a stateful function - if fed incorrect data, it will most likely never return true again! -/// While this function returns false, the Tag might not contain valid data. -/// \param sock The socket to read from. -/// \return True if a whole tag is succesfully read, false otherwise. -bool FLV::Tag::SockLoader(Socket::Connection sock){ - if (buf < 15){data = (char*)realloc(data, 15); buf = 15;} - if (done){ - if (SockReadUntil(data, 11, sofar, sock)){ - //if its a correct FLV header, throw away and read tag header - if (FLV::is_header(data)){ - if (SockReadUntil(data, 13, sofar, sock)){ - if (FLV::check_header(data)){ - sofar = 0; - memcpy(FLV::Header, data, 13); - }else{FLV::Parse_Error = true; Error_Str = "Invalid header received."; return false;} - } - }else{ - //if a tag header, calculate length and read tag body - len = data[3] + 15; - len += (data[2] << 8); - len += (data[1] << 16); - if (buf < len){data = (char*)realloc(data, len); buf = len;} - if (data[0] > 0x12){ - data[0] += 32; - FLV::Parse_Error = true; - Error_Str = "Invalid Tag received ("; - Error_Str += data[0]; - Error_Str += ")."; - return false; - } - done = false; - } - } - }else{ - //read tag body - if (SockReadUntil(data, len, sofar, sock)){ - //calculate keyframeness, next time read header again, return true - if ((data[0] == 0x09) && (((data[11] & 0xf0) >> 4) == 1)){isKeyframe = true;}else{isKeyframe = false;} - done = true; - sofar = 0; - return true; - } - } - return false; -}//Tag::SockLoader - -/// Try to load a tag from a socket. -/// This is a stateful function - if fed incorrect data, it will most likely never return true again! -/// While this function returns false, the Tag might not contain valid data. -/// \param sock The socket to read from. -/// \return True if a whole tag is succesfully read, false otherwise. -bool FLV::Tag::SockLoader(int sock){ - return SockLoader(Socket::Connection(sock)); -}//Tag::SockLoader - /// Helper function for FLV::FileLoader. /// This function will try to read count bytes from file f into buffer. /// This function should be called repeatedly until true. diff --git a/lib/flv_tag.h b/lib/flv_tag.h index 4e61fc6a..8de48eb5 100644 --- a/lib/flv_tag.h +++ b/lib/flv_tag.h @@ -46,8 +46,6 @@ namespace FLV { bool DTSCMetaInit(DTSC::Stream & S); JSON::Value toJSON(JSON::Value & metadata); bool MemLoader(char * D, unsigned int S, unsigned int & P); - bool SockLoader(int sock); - bool SockLoader(Socket::Connection sock); bool FileLoader(FILE * f); protected: int buf; ///< Maximum length of buffer space. @@ -56,7 +54,6 @@ namespace FLV { void setLen(); //loader helper functions bool MemReadUntil(char * buffer, unsigned int count, unsigned int & sofar, char * D, unsigned int S, unsigned int & P); - bool SockReadUntil(char * buffer, unsigned int count, unsigned int & sofar, Socket::Connection & sock); 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); diff --git a/lib/socket.cpp b/lib/socket.cpp index 518c77db..9a230118 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -160,55 +160,6 @@ Socket::Connection::Connection(std::string host, int port, bool nonblock){ } }//Socket::Connection TCP Contructor -/// Calls poll() on the socket, checking if data is available. -/// This function may return true even if there is no data, but never returns false when there is. -bool Socket::Connection::canRead(){ - struct pollfd PFD; - PFD.fd = sock; - PFD.events = POLLIN; - PFD.revents = 0; - poll(&PFD, 1, 5); - return (PFD.revents & POLLIN) == POLLIN; -} -/// Calls poll() on the socket, checking if data can be written. -bool Socket::Connection::canWrite(){ - struct pollfd PFD; - PFD.fd = sock; - PFD.events = POLLOUT; - PFD.revents = 0; - poll(&PFD, 1, 5); - return (PFD.revents & POLLOUT) == POLLOUT; -} - - -/// Returns the ready-state for this socket. -/// \returns 1 if data is waiting to be read, -1 if not connected, 0 otherwise. -signed int Socket::Connection::ready(){ - if (sock < 0) return -1; - char tmp; - int preflags = fcntl(sock, F_GETFL, 0); - int postflags = preflags | O_NONBLOCK; - fcntl(sock, F_SETFL, postflags); - int r = recv(sock, &tmp, 1, MSG_PEEK); - fcntl(sock, F_SETFL, preflags); - if (r < 0){ - if (errno == EAGAIN || errno == EWOULDBLOCK){ - return 0; - }else{ - #if DEBUG >= 2 - fprintf(stderr, "Socket ready error! Error: %s\n", strerror(errno)); - #endif - close(); - return -1; - } - } - if (r == 0){ - close(); - return -1; - } - return r; -} - /// Returns the connected-state for this socket. /// Note that this function might be slightly behind the real situation. /// The connection status is updated after every read/write attempt, when errors occur @@ -241,6 +192,17 @@ bool Socket::Connection::spool(){ return iread(downbuffer); } +/// Updates the downbuffer and upbuffer internal variables until upbuffer is empty. +/// Returns true if new data was received, false otherwise. +bool Socket::Connection::flush(){ + while (upbuffer.size() > 0 && connected()){ + iwrite(upbuffer); + usleep(5000);//sleep 5 ms + } + return iread(downbuffer); +} + + /// Returns a reference to the download buffer. std::string & Socket::Connection::Received(){ return downbuffer; @@ -251,81 +213,6 @@ void Socket::Connection::Send(std::string data){ upbuffer.append(data); } -/// Writes data to socket. This function blocks if the socket is blocking and all data cannot be written right away. -/// If the socket is nonblocking and not all data can be written, this function sets internal variable Blocking to true -/// and returns false. -/// \param buffer Location of the buffer to write from. -/// \param len Amount of bytes to write. -/// \returns True if the whole write was succesfull, false otherwise. -bool Socket::Connection::write(const void * buffer, int len){ - int sofar = 0; - if (sock < 0){return false;} - while (sofar != len){ - int r = send(sock, (char*)buffer + sofar, len-sofar, 0); - if (r <= 0){ - Error = true; - #if DEBUG >= 2 - fprintf(stderr, "Could not write data! Error: %s\n", strerror(errno)); - #endif - close(); - up += sofar; - return false; - }else{ - sofar += r; - } - } - up += sofar; - return true; -}//DDv::Socket::write - -/// Reads data from socket. This function blocks if the socket is blocking and all data cannot be read right away. -/// If the socket is nonblocking and not all data can be read, this function sets internal variable Blocking to true -/// and returns false. -/// \param buffer Location of the buffer to read to. -/// \param len Amount of bytes to read. -/// \returns True if the whole read was succesfull, false otherwise. -bool Socket::Connection::read(const void * buffer, int len){ - int sofar = 0; - if (sock < 0){return false;} - while (sofar != len){ - int r = recv(sock, (char*)buffer + sofar, len-sofar, 0); - if (r < 0){ - switch (errno){ - case EWOULDBLOCK: - down += sofar; - return 0; - break; - default: - Error = true; - #if DEBUG >= 2 - fprintf(stderr, "Could not read data! Error %i: %s\n", r, strerror(errno)); - #endif - close(); - down += sofar; - break; - } - return false; - }else{ - if (r == 0){ - Error = true; - close(); - down += sofar; - return false; - } - sofar += r; - } - } - down += sofar; - return true; -}//Socket::Connection::read - -/// Read call that is compatible with file access syntax. This function simply calls the other read function. -bool Socket::Connection::read(const void * buffer, int width, int count){return read(buffer, width*count);} -/// Write call that is compatible with file access syntax. This function simply calls the other write function. -bool Socket::Connection::write(const void * buffer, int width, int count){return write(buffer, width*count);} -/// Write call that is compatible with std::string. This function simply calls the other write function. -bool Socket::Connection::write(const std::string data){return write(data.c_str(), data.size());} - /// Incremental write call. This function tries to write len bytes to the socket from the buffer, /// returning the amount of bytes it actually wrote. /// \param buffer Location of the buffer to write from. @@ -336,12 +223,16 @@ int Socket::Connection::iwrite(const void * buffer, int len){ int r = send(sock, buffer, len, 0); if (r < 0){ switch (errno){ - case EWOULDBLOCK: return 0; break; + case EWOULDBLOCK: + return 0; + break; default: - Error = true; - #if DEBUG >= 2 - fprintf(stderr, "Could not iwrite data! Error: %s\n", strerror(errno)); - #endif + if (errno != EPIPE){ + Error = true; + #if DEBUG >= 2 + fprintf(stderr, "Could not iwrite data! Error: %s\n", strerror(errno)); + #endif + } close(); return 0; break; @@ -364,12 +255,16 @@ int Socket::Connection::iread(void * buffer, int len){ int r = recv(sock, buffer, len, 0); if (r < 0){ switch (errno){ - case EWOULDBLOCK: return 0; break; + case EWOULDBLOCK: + return 0; + break; default: - Error = true; - #if DEBUG >= 2 - fprintf(stderr, "Could not iread data! Error: %s\n", strerror(errno)); - #endif + if (errno != EPIPE){ + Error = true; + #if DEBUG >= 2 + fprintf(stderr, "Could not iread data! Error: %s\n", strerror(errno)); + #endif + } close(); return 0; break; @@ -382,23 +277,6 @@ int Socket::Connection::iread(void * buffer, int len){ return r; }//Socket::Connection::iread -/// Read call that is compatible with std::string. -/// Data is read using iread (which is nonblocking if the Socket::Connection itself is), -/// then appended to end of buffer. This functions reads at least one byte before returning. -/// \param buffer std::string to append data to. -/// \return True if new data arrived, false otherwise. -bool Socket::Connection::read(std::string & buffer){ - char cbuffer[5000]; - if (!read(cbuffer, 1)){return false;} - int num = iread(cbuffer+1, 4999); - if (num > 0){ - buffer.append(cbuffer, num+1); - }else{ - buffer.append(cbuffer, 1); - } - return true; -}//read - /// Read call that is compatible with std::string. /// Data is read using iread (which is nonblocking if the Socket::Connection itself is), /// then appended to end of buffer. @@ -425,18 +303,6 @@ bool Socket::Connection::iwrite(std::string & buffer){ return true; }//iwrite -/// Write call that is compatible with std::string. -/// Data is written using write (which is always blocking), -/// then removed from front of buffer. -/// \param buffer std::string to remove data from. -/// \return True if more data was sent, false otherwise. -bool Socket::Connection::swrite(std::string & buffer){ - if (buffer.size() < 1){return false;} - bool tmp = write((void*)buffer.c_str(), buffer.size()); - if (tmp){buffer = "";} - return tmp; -}//write - /// Gets hostname for connection, if available. std::string Socket::Connection::getHost(){ return remotehost; diff --git a/lib/socket.h b/lib/socket.h index 2a4f0418..db902c66 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -14,6 +14,8 @@ #include #include +//for being friendly with Socket::Connection down below +namespace Buffer{class user;}; ///Holds Socket tools. namespace Socket{ @@ -28,35 +30,32 @@ namespace Socket{ unsigned int conntime; std::string downbuffer; ///< Stores temporary data coming in. std::string upbuffer; ///< Stores temporary data going out. + int iread(void * buffer, int len); ///< Incremental read call. + int iwrite(const void * buffer, int len); ///< Incremental write call. + bool iread(std::string & buffer); ///< Incremental write call that is compatible with std::string. + bool iwrite(std::string & buffer); ///< Write call that is compatible with std::string. 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. - void setBlocking(bool blocking); ///< Set this socket to be blocking (true) or nonblocking (false). - bool canRead(); ///< Calls poll() on the socket, checking if data is available. - bool canWrite(); ///< Calls poll() on the socket, checking if data can be written. - signed int ready(); ///< Returns the ready-state for this socket. - bool connected() const; ///< Returns the connected-state for this socket. - bool read(const void * buffer, int len); ///< Reads data from socket. - bool read(const void * buffer, int width, int count); ///< Read call that is compatible with file access syntax. - bool write(const void * buffer, int len); ///< Writes data to socket. - bool write(const void * buffer, int width, int count); ///< Write call that is compatible with file access syntax. - bool write(const std::string data); ///< Write call that is compatible with std::string. - int iwrite(const void * buffer, int len); ///< Incremental write call. - int iread(void * buffer, int len); ///< Incremental read call. - bool read(std::string & buffer); ///< Read call that is compatible with std::string. - bool swrite(std::string & buffer); ///< Write call that is compatible with std::string. - bool iread(std::string & buffer); ///< Incremental write call that is compatible with std::string. - bool iwrite(std::string & buffer); ///< Write call that is compatible with std::string. - bool spool(); ///< Updates the downbuffer and upbuffer internal variables. - std::string & Received(); ///< Returns a reference to the download buffer. - void Send(std::string data); ///< Appends data to the upbuffer. + //generic methods void close(); ///< Close connection. + void setBlocking(bool blocking); ///< Set this socket to be blocking (true) or nonblocking (false). std::string getHost(); ///< Gets hostname for connection, if available. void setHost(std::string host); ///< Sets hostname for connection manually. int getSocket(); ///< Returns 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. + //buffered i/o methods + bool spool(); ///< Updates the downbuffer and upbuffer internal variables. + bool flush(); ///< Updates the downbuffer and upbuffer internal variables until upbuffer is empty. + std::string & Received(); ///< Returns a reference to the download buffer. + void Send(std::string data); ///< Appends data to the upbuffer. + //stats related methods 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. From 516c8bf091893246398358d893475fa5038fd729 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 26 Aug 2012 21:18:40 +0200 Subject: [PATCH 247/788] Fixed hook creator to also work when pulling. --- createhooks.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/createhooks.sh b/createhooks.sh index 067aafa8..d03396a2 100755 --- a/createhooks.sh +++ b/createhooks.sh @@ -2,7 +2,9 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" echo -e "#!/bin/bash\n[ -f configure ] && touch configure\n[ -f configure.ac ] && touch configure.ac" > $DIR/.git/hooks/post-commit echo -e "#!/bin/bash\n[ -f configure ] && touch configure\n[ -f configure.ac ] && touch configure.ac" > $DIR/.git/hooks/post-checkout +echo -e "#!/bin/bash\n[ -f configure ] && touch configure\n[ -f configure.ac ] && touch configure.ac" > $DIR/.git/hooks/post-receive chmod +x $DIR/.git/hooks/post-commit chmod +x $DIR/.git/hooks/post-checkout +chmod +x $DIR/.git/hooks/post-receive echo "Done! The version number should now auto-update whenever you commit or checkout." From 84db1484054fdf0f5cf88db7dc4e4fb8ed2f906f Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 26 Aug 2012 21:19:48 +0200 Subject: [PATCH 248/788] Fix HTTP::Parser to no longer send empty headers. --- lib/http_parser.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/http_parser.cpp b/lib/http_parser.cpp index 9e8291a5..4bc1210f 100644 --- a/lib/http_parser.cpp +++ b/lib/http_parser.cpp @@ -30,7 +30,9 @@ std::string HTTP::Parser::BuildRequest(){ if (protocol.size() < 5 || protocol.substr(0, 4) != "HTTP"){protocol = "HTTP/1.0";} std::string tmp = method+" "+url+" "+protocol+"\n"; for (it=headers.begin(); it != headers.end(); it++){ - tmp += (*it).first + ": " + (*it).second + "\n"; + if ((*it).first != "" && (*it).second != ""){ + tmp += (*it).first + ": " + (*it).second + "\n"; + } } tmp += "\n" + body; return tmp; @@ -48,7 +50,9 @@ std::string HTTP::Parser::BuildResponse(std::string code, std::string message){ if (protocol.size() < 5 || protocol.substr(0, 4) != "HTTP"){protocol = "HTTP/1.0";} std::string tmp = protocol+" "+code+" "+message+"\n"; for (it=headers.begin(); it != headers.end(); it++){ - tmp += (*it).first + ": " + (*it).second + "\n"; + if ((*it).first != "" && (*it).second != ""){ + tmp += (*it).first + ": " + (*it).second + "\n"; + } } tmp += "\n"; tmp += body; From 18a1a386df6207d0114220fa853cb9e8e4777570 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 26 Aug 2012 22:18:26 +0200 Subject: [PATCH 249/788] Do not send Content-Length headers when they are zero. --- lib/http_parser.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/http_parser.cpp b/lib/http_parser.cpp index 4bc1210f..2047be6d 100644 --- a/lib/http_parser.cpp +++ b/lib/http_parser.cpp @@ -51,7 +51,9 @@ std::string HTTP::Parser::BuildResponse(std::string code, std::string message){ std::string tmp = protocol+" "+code+" "+message+"\n"; for (it=headers.begin(); it != headers.end(); it++){ if ((*it).first != "" && (*it).second != ""){ - tmp += (*it).first + ": " + (*it).second + "\n"; + if ((*it).first != "Content-Length" || (*it).second != "0"){ + tmp += (*it).first + ": " + (*it).second + "\n"; + } } } tmp += "\n"; From 3d16a1a012e645210561287633581e57f37c3af5 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 26 Aug 2012 23:30:12 +0200 Subject: [PATCH 250/788] Again, fixed hooks. --- createhooks.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/createhooks.sh b/createhooks.sh index d03396a2..346bbe6a 100755 --- a/createhooks.sh +++ b/createhooks.sh @@ -2,9 +2,9 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" echo -e "#!/bin/bash\n[ -f configure ] && touch configure\n[ -f configure.ac ] && touch configure.ac" > $DIR/.git/hooks/post-commit echo -e "#!/bin/bash\n[ -f configure ] && touch configure\n[ -f configure.ac ] && touch configure.ac" > $DIR/.git/hooks/post-checkout -echo -e "#!/bin/bash\n[ -f configure ] && touch configure\n[ -f configure.ac ] && touch configure.ac" > $DIR/.git/hooks/post-receive +echo -e "#!/bin/bash\n[ -f configure ] && touch configure\n[ -f configure.ac ] && touch configure.ac" > $DIR/.git/hooks/post-merge chmod +x $DIR/.git/hooks/post-commit chmod +x $DIR/.git/hooks/post-checkout -chmod +x $DIR/.git/hooks/post-receive +chmod +x $DIR/.git/hooks/post-merge echo "Done! The version number should now auto-update whenever you commit or checkout." From c6e2f6bdd37cdf28dffd1f89849c8343bbaed5b2 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 26 Aug 2012 23:57:25 +0200 Subject: [PATCH 251/788] Changed version to 1.0.0 --- lib/Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Makefile.am b/lib/Makefile.am index 34b8e646..9d29bc18 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,6 +1,7 @@ lib_LTLIBRARIES=libmist-1.0.la libmist_1_0_la_SOURCES=amf.h amf.cpp auth.h auth.cpp base64.h base64.cpp config.h config.cpp crypto.h crypto.cpp dtsc.h dtsc.cpp flv_tag.h flv_tag.cpp http_parser.h http_parser.cpp json.h json.cpp procs.h procs.cpp rtmpchunks.h rtmpchunks.cpp socket.h socket.cpp mp4.h mp4.cpp libmist_1_0_la_LIBADD=-lssl -lcrypto +libmist_1_0_la_LDFLAGS = -version-info 1:0:0 pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = mist-1.0.pc From 3ec56cf6757532d567a6b831f80ebdda3e977556 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 28 Aug 2012 11:01:59 +0200 Subject: [PATCH 252/788] Change Util::Config to be able to return arrays on request. --- lib/config.cpp | 47 +++++++++++++++++++++++++++++------------------ lib/config.h | 2 +- lib/socket.cpp | 2 +- 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/lib/config.cpp b/lib/config.cpp index 68172c9e..99b1503a 100644 --- a/lib/config.cpp +++ b/lib/config.cpp @@ -28,30 +28,37 @@ bool Util::Config::is_active = false; Util::Config::Config(std::string cmd, std::string version){ vals.null(); long_count = 0; - vals["cmd"]["current"] = cmd; + 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"]["current"] = version; + vals["version"]["value"].append((std::string)PACKAGE_VERSION); + vals["version"]["value"].append(version); } /// 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. -/// "default":1234, //The default value for this option if it is not given on the commandline. +/// "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++;} @@ -80,7 +87,7 @@ void Util::Config::printHelp(std::ostream & output){ } output << "Usage: " << getString("cmd") << " [options]"; for (std::map::iterator i = args.begin(); i != args.end(); i++){ - if (vals[i->second].isMember("default")){ + if (vals[i->second].isMember("value") && vals[i->second]["value"].size()){ output << " [" << i->second << "]"; }else{ output << " " << i->second; @@ -163,7 +170,7 @@ void Util::Config::parseArgs(int argc, char ** argv){ if (it->second.isMember("arg")){longOpts[long_i].has_arg = 1;} long_i++; } - if (it->second.isMember("arg_num") && !it->second.isMember("default")){ + 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(); } @@ -183,14 +190,14 @@ void Util::Config::parseArgs(int argc, char ** argv){ 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["current"] = (std::string)optarg; + it->second["value"].append((std::string)optarg); }else{ - it->second["current"] = 1; + it->second["value"].append((long long int)1); } break; } if (it->second.isMember("short_off") && it->second["short_off"].asString()[0] == opt){ - it->second["current"] = 0; + it->second["value"].append((long long int)0); } } break; @@ -201,7 +208,7 @@ void Util::Config::parseArgs(int argc, char ** argv){ 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["current"] = (std::string)argv[optind]; + it->second["value"].append((std::string)argv[optind]); optind++; long_i++; break; @@ -217,15 +224,19 @@ void Util::Config::parseArgs(int argc, char ** argv){ /// 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){ +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("current")){ - return vals[optname]["current"]; + if (!vals[optname].isMember("value") || !vals[optname]["value"].isArray()){ + vals[optname]["value"].append(JSON::Value()); + } + if (asArray){ + return vals[optname]["value"]; }else{ - return vals[optname]["default"]; + int n = vals[optname]["value"].size(); + return vals[optname]["value"][n-1]; } } @@ -292,11 +303,11 @@ void Util::Config::signal_handler(int signum){ /// Adds the default connector options to this Util::Config object. void Util::Config::addConnectorOptions(int port){ JSON::Value stored_port = JSON::fromString("{\"long\":\"port\", \"short\":\"p\", \"arg\":\"integer\", \"help\":\"TCP port to listen on.\"}"); - stored_port["default"] = port; + stored_port["value"].append((long long int)port); addOption("listen_port", stored_port); - addOption("listen_interface", JSON::fromString("{\"long\":\"interface\", \"default\":\"0.0.0.0\", \"short\":\"i\", \"arg\":\"string\", \"help\":\"Interface address to listen on, or 0.0.0.0 for all available interfaces.\"}")); - addOption("username", JSON::fromString("{\"long\":\"username\", \"default\":\"root\", \"short\":\"u\", \"arg\":\"string\", \"help\":\"Username to drop privileges to, or root to not drop provileges.\"}")); - addOption("daemonize", JSON::fromString("{\"long\":\"daemon\", \"short\":\"d\", \"default\":1, \"long_off\":\"nodaemon\", \"short_off\":\"n\", \"help\":\"Whether or not to daemonize the process after starting.\"}")); + addOption("listen_interface", JSON::fromString("{\"long\":\"interface\", \"value\":[\"0.0.0.0\"], \"short\":\"i\", \"arg\":\"string\", \"help\":\"Interface address to listen on, or 0.0.0.0 for all available interfaces.\"}")); + addOption("username", JSON::fromString("{\"long\":\"username\", \"value\":[\"root\"], \"short\":\"u\", \"arg\":\"string\", \"help\":\"Username to drop privileges to, or root to not drop provileges.\"}")); + addOption("daemonize", JSON::fromString("{\"long\":\"daemon\", \"short\":\"d\", \"value\":[1], \"long_off\":\"nodaemon\", \"short_off\":\"n\", \"help\":\"Whether or not to daemonize the process after starting.\"}")); }//addConnectorOptions /// Sets the current process' running user diff --git a/lib/config.h b/lib/config.h index 5db09351..4ae72c40 100644 --- a/lib/config.h +++ b/lib/config.h @@ -22,7 +22,7 @@ namespace Util{ void addOption(std::string optname, JSON::Value option); void printHelp(std::ostream & output); void parseArgs(int argc, char ** argv); - JSON::Value & getOption(std::string optname); + 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); diff --git a/lib/socket.cpp b/lib/socket.cpp index 9a230118..b3eb1f4b 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -577,7 +577,7 @@ int Socket::Server::getSocket(){return sock;} /// converting all letters to lowercase. /// If a '?' character is found, everything following that character is deleted. Socket::Connection Socket::getStream(std::string streamname){ - //strip anything that isn't numbers, digits or underscores + //strip anything that isn't a number, alpha or underscore 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 != '_'){ From b5c4e88b7512083644069de3b1e34e49bc7cf883 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Tue, 28 Aug 2012 13:59:54 +0200 Subject: [PATCH 253/788] Doxygen upgrade --- Doxyfile | 1580 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 1546 insertions(+), 34 deletions(-) diff --git a/Doxyfile b/Doxyfile index b6edff54..3948c111 100644 --- a/Doxyfile +++ b/Doxyfile @@ -1,296 +1,1808 @@ -# Doxyfile 1.6.3 +# Doxyfile 1.8.2 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" "). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + DOXYFILE_ENCODING = UTF-8 -PROJECT_NAME = DDVTECH Streaming Server + +# The PROJECT_NAME tag is a single word (or sequence of words) that should +# identify the project. Note that if you do not use Doxywizard you need +# to put quotes around the project name if it contains spaces. + +PROJECT_NAME = DDVTECHStreamingServer + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + PROJECT_NUMBER = 1 + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer +# a quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + OUTPUT_DIRECTORY = ./docs + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + INLINE_INHERITED_MEMB = YES + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. Note that you specify absolute paths here, but also +# relative paths, which will be relative from the directory where doxygen is +# started. + STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file system +# doesn't support long names like on DOS, Mac, or CD-ROM. + SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + TAB_SIZE = 2 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding +# "class=itcl::class" will allow you to use the command class in the +# itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, +# and language is one of the parsers supported by doxygen: IDL, Java, +# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, +# C++. For instance to make doxygen treat .inc files as Fortran files (default +# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note +# that for custom extensions you also need to set FILE_PATTERNS otherwise the +# files are not read by doxygen. + EXTENSION_MAPPING = + +# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all +# comments according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you +# can mix doxygen, HTML, and XML commands with Markdown formatting. +# Disable only in case of backward compatibilities issues. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented classes, +# or namespaces to their corresponding documentation. Such a link can be +# prevented in individual cases by by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also makes the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + BUILTIN_STL_SUPPORT = YES + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter and setter methods for a property. Setting this option to YES (the default) will make doxygen replace the get and set methods by a property in the documentation. This will only work if the methods are indeed getting or setting a simple type. If this is not the case, or you want to show the methods anyway, you should set this option to NO. + IDL_PROPERTY_SUPPORT = NO + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and +# unions are shown inside the group in which they are included (e.g. using +# @ingroup) instead of on a separate page (for HTML and Man pages) or +# section (for LaTeX and RTF). + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and +# unions with only public data fields will be shown inline in the documentation +# of the scope in which they are defined (i.e. file, namespace, or group +# documentation), provided this scope is documented. If set to NO (the default), +# structs, classes, and unions are shown on a separate page (for HTML and Man +# pages) or section (for LaTeX and RTF). + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + TYPEDEF_HIDES_STRUCT = NO + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penalty. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will roughly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols. + SYMBOL_CACHE_SIZE = 0 +# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be +# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given +# their name and scope. Since this can be an expensive process and often the +# same symbol appear multiple times in the code, doxygen keeps a cache of +# pre-resolved symbols. If the cache is too small doxygen will become slower. +# If the cache is too large, memory is wasted. The cache size is given by this +# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols. + +LOOKUP_CACHE_SIZE = 0 + #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + EXTRACT_PRIVATE = YES + +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# scope will be included in the documentation. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespaces are hidden. + EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to +# do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even +# if there is only one candidate or it is obvious which candidate to choose +# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# will still accept a match between prototype and implementation in such cases. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and macros in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + SHOW_USED_FILES = YES -SHOW_DIRECTORIES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + LAYOUT_FILE = +# The CITE_BIB_FILES tag can be used to specify one or more bib files +# containing the references data. This must be a list of .bib files. The +# .bib extension is automatically appended if omitted. Using this command +# requires the bibtex tool to be installed. See also +# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style +# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this +# feature you need bibtex and perl available in the search path. + +CITE_BIB_FILES = + #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + WARN_IF_DOC_ERROR = YES + +# The WARN_NO_PARAMDOC option can be enabled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + INPUT = . + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# *.f90 *.f *.for *.vhd *.vhdl + FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + RECURSIVE = YES -EXCLUDE = + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. + EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + EXCLUDE_PATTERNS = */.git/* + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be +# ignored. + INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty or if +# non of the patterns match the file name, INPUT_FILTER is applied. + FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + FILTER_SOURCE_FILES = NO +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) +# and it is also possible to disable source filtering for a specific pattern +# using *.ext= (so without naming a filter). This option only has effect when +# FILTER_SOURCE_FILES is enabled. + +FILTER_SOURCE_PATTERNS = + #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C, C++ and Fortran comments will always remain visible. + STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. Note that when using a custom header you are responsible +# for the proper inclusion of any scripts and style sheets that doxygen +# needs, which is dependent on the configuration options used. +# It is advised to generate a default header using "doxygen -w html +# header.html footer.html stylesheet.css YourConfigFile" and then modify +# that header. Note that the header is subject to change so you typically +# have to redo this when upgrading to a newer version of doxygen or when +# changing the value of configuration settings such as GENERATE_TREEVIEW! + HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If left blank doxygen will +# generate a default style sheet. Note that it is recommended to use +# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this +# tag will in the future become obsolete. + HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional +# user-defined cascading style sheet that is included after the standard +# style sheets created by doxygen. Using this option one can overrule +# certain style aspects. This is preferred over using HTML_STYLESHEET +# since it does not replace the standard style sheet and is therefor more +# robust against future updates. Doxygen will copy the style sheet file to +# the output directory. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that +# the files will be copied as-is; there are no commands or markers available. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the style sheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + HTML_TIMESTAMP = YES -HTML_ALIGN_MEMBERS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. + HTML_DYNAMIC_SECTIONS = NO +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of +# entries shown in the various tree structured indices initially; the user +# can expand and collapse entries dynamically later on. Doxygen will expand +# the tree to such a level that at most the specified number of entries are +# visible (unless a fully collapsed tree already exceeds this amount). +# So setting the number of entries 1 will produce a full collapsed tree by +# default. 0 is a special value representing an infinite number of entries +# and will result in a full expanded tree by default. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + DOCSET_BUNDLE_ID = org.doxygen.Project +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely +# identify the documentation publisher. This should be a reverse domain-name +# style string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + TOC_EXPAND = NO +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# +# Qt Help Project / Custom Filters. + QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# +# Qt Help Project / Filter Attributes. + QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + QHG_LOCATION = +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) +# at top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. Since the tabs have the same information as the +# navigation tree you can set this option to NO if you already set +# GENERATE_TREEVIEW to YES. + DISABLE_INDEX = NO -ENUM_VALUES_PER_LINE = 4 + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. +# Since the tree basically has the same information as the tab index you +# could consider to set DISABLE_INDEX to NO when enabling this option. + GENERATE_TREEVIEW = NO -USE_INLINE_TREES = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values +# (range [0,1..20]) that doxygen will group on one line in the generated HTML +# documentation. Note that a value of 0 will completely suppress the enum +# values from appearing in the overview section. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you may also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = NO + +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to +# the MathJax Content Delivery Network so you can quickly see the result without +# installing MathJax. +# However, it is strongly recommended to install a local +# copy of MathJax from http://www.mathjax.org before deployment. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension +# names that should be enabled during MathJax rendering. + +MATHJAX_EXTENSIONS = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a PHP enabled web server instead of at the web client +# using Javascript. Doxygen will generate the search PHP script and index +# file to put on the web server. The advantage of the server +# based approach is that it scales better to large projects and allows +# full text search. The disadvantages are that it is more difficult to setup +# and does not have live searching capabilities. + SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + GENERATE_LATEX = YES + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, letter, legal and +# executive. If left blank a4wide will be used. + PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for +# the generated latex document. The footer should contain everything after +# the last chapter. If it is left blank doxygen will generate a +# standard footer. Notice: only use this tag if you know what you are doing! + +LATEX_FOOTER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + LATEX_SOURCE_CODE = NO +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See +# http://en.wikipedia.org/wiki/BibTeX for more info. + +LATEX_BIB_STYLE = plain + #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + RTF_HYPERLINKS = NO + +# Load style sheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# pointed to by INCLUDE_PATH will be searched when a #include is found. + SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition that +# overrules the definition found in the source code. + EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these will confuse the parser if not removed. + SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- +# The TAGFILES option can be used to specify one or more tagfiles. For each +# tag file the location of the external documentation should be added. The +# format of a tag file without this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths +# or URLs. Note that each tag file must have a unique name (where the name does +# NOT include the path). If a tag file is not located in the directory in which +# doxygen is run, you must also specify the path to the tagfile here. + TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- -CLASS_DIAGRAMS = YES -MSCGEN_PATH = -HIDE_UNDOC_RELATIONS = YES -HAVE_DOT = NO -DOT_FONTNAME = FreeSans -DOT_FONTSIZE = 10 -DOT_FONTPATH = -CLASS_GRAPH = YES -COLLABORATION_GRAPH = YES -GROUP_GRAPHS = YES -UML_LOOK = NO -TEMPLATE_RELATIONS = NO -INCLUDE_GRAPH = YES -INCLUDED_BY_GRAPH = YES -CALL_GRAPH = NO -CALLER_GRAPH = NO -GRAPHICAL_HIERARCHY = YES -DIRECTORY_GRAPH = YES -DOT_IMAGE_FORMAT = png -DOT_PATH = -DOTFILE_DIRS = -DOT_GRAPH_MAX_NODES = 50 -MAX_DOT_GRAPH_DEPTH = 0 -DOT_TRANSPARENT = NO -DOT_MULTI_TARGETS = NO -GENERATE_LEGEND = YES -DOT_CLEANUP = YES +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option also works with HAVE_DOT disabled, but it is recommended to +# install and use dot, since it yields more powerful graphs. +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will use the Helvetica font for all dot files that +# doxygen generates. When you want a differently looking font you can specify +# the font name using DOT_FONTNAME. You need to make sure dot is able to find +# the font, which can be done by putting it in a standard location or by setting +# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. + +DOT_FONTNAME = FreeSans + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the Helvetica font. +# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to +# set the path where dot can find it. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If the UML_LOOK tag is enabled, the fields and methods are shown inside +# the class node. If there are many fields or methods and many nodes the +# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS +# threshold limits the number of items for each type to make the size more +# managable. Set this to 0 for no limit. Note that the threshold may be +# exceeded by 50% before the limit is enforced. + +UML_LIMIT_NUM_FIELDS = 10 + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will generate a graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are svg, png, jpg, or gif. +# If left blank png will be used. If you choose svg you need to set +# HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible in IE 9+ (other browsers do not have this requirement). + +DOT_IMAGE_FORMAT = png + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# Note that this requires a modern browser other than Internet Explorer. +# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you +# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible. Older versions of IE do not have SVG support. + +INTERACTIVE_SVG = NO + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES From c404624774064fb3d6b7dc461f52c1f83369fb1c Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Tue, 28 Aug 2012 17:08:53 +0200 Subject: [PATCH 254/788] flv: add duration metadata --- lib/flv_tag.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index 89694a3f..31ff4fd7 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -450,6 +450,9 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){ amfdata.addContent(AMF::Object("", "onMetaData")); amfdata.addContent(AMF::Object("", AMF::AMF0_ECMA_ARRAY)); + if (S.metadata.isMember("duration")){ + amfdata.getContentP(1)->addContent(AMF::Object("duration", S.metadata["duration"].asInt() / 1000, AMF::AMF0_NUMBER)); + } if (S.metadata.isMember("video")){ amfdata.getContentP(1)->addContent(AMF::Object("hasVideo", 1, AMF::AMF0_BOOL)); if (S.metadata["video"]["codec"].asString() == "H264"){ From a2f37a1bbdff9ac4efa6ffdd368ef8e865355b7e Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 28 Aug 2012 23:39:42 +0200 Subject: [PATCH 255/788] Add version number as a static member (libver) of Util::Config. --- lib/config.cpp | 1 + lib/config.h | 1 + 2 files changed, 2 insertions(+) diff --git a/lib/config.cpp b/lib/config.cpp index 99b1503a..09361779 100644 --- a/lib/config.cpp +++ b/lib/config.cpp @@ -23,6 +23,7 @@ #include bool Util::Config::is_active = false; +std::string Util::Config::libver = PACKAGE_VERSION; /// Creates a new configuration manager. Util::Config::Config(std::string cmd, std::string version){ diff --git a/lib/config.h b/lib/config.h index 4ae72c40..099c9685 100644 --- a/lib/config.h +++ b/lib/config.h @@ -16,6 +16,7 @@ namespace Util{ 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. //functions Config(std::string cmd, std::string version); From dc8e96634bc5cd8bf8a5a496b147a6ccd6614130 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Wed, 29 Aug 2012 11:20:31 +0200 Subject: [PATCH 256/788] Working FTP --- lib/ftp.cpp | 31 +++++++++++++++++++------------ lib/ftp.h | 4 +++- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/lib/ftp.cpp b/lib/ftp.cpp index 27d893d8..c3617848 100644 --- a/lib/ftp.cpp +++ b/lib/ftp.cpp @@ -1,6 +1,6 @@ #include "ftp.h" -FTP::User::User( Socket::Connection NewConnection ) { +FTP::User::User( Socket::Connection NewConnection, std::map Credentials ) { Conn = NewConnection; USER = ""; PASS = ""; @@ -9,6 +9,7 @@ FTP::User::User( Socket::Connection NewConnection ) { TYPE = TYPE_ASCII_NONPRINT; PORT = 20; RNFR = ""; + AllCredentials = Credentials; MyDir = Filesystem::Directory( "", FTPBasePath ); MyDir.SetPermissions( "", Filesystem::P_LIST ); @@ -69,14 +70,14 @@ int FTP::User::ParseCommand( std::string Command ) { USER = ""; PASS = ""; if( Command == "" ) { return 501; }//Syntax error in parameters or arguments. - USER = ThisCmd; + 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 = ThisCmd; + PASS = Command; if( !LoggedIn( ) ) { USER = ""; PASS =""; @@ -89,15 +90,15 @@ int FTP::User::ParseCommand( std::string Command ) { std::cout << "Listening on :" << MyPassivePort << "\n"; Socket::Connection Connected = Passive.accept(); if( Connected.connected() ) { - Conn.write( "125 Data connection already open; transfer starting.\n" ); + Conn.Send( "125 Data connection already open; transfer starting.\n" ); } else { - Conn.write( "150 File status okay; about to open data connection.\n" ); + Conn.Send( "150 File status okay; about to open data connection.\n" ); } while( !Connected.connected() ) { Connected = Passive.accept(); } fprintf( stderr, "Sending LIST information\n" ); - Connected.write( MyDir.LIST( ActiveStreams ) ); + Connected.Send( MyDir.LIST( ActiveStreams ) ); Connected.close( ); return 226; break; @@ -136,15 +137,15 @@ int FTP::User::ParseCommand( std::string Command ) { std::cout << "Listening on :" << MyPassivePort << "\n"; Socket::Connection Connected = Passive.accept(); if( Connected.connected() ) { - Conn.write( "125 Data connection already open; transfer starting.\n" ); + Conn.Send( "125 Data connection already open; transfer starting.\n" ); } else { - Conn.write( "150 File status okay; about to open data connection.\n" ); + Conn.Send( "150 File status okay; about to open data connection.\n" ); } while( !Connected.connected() ) { Connected = Passive.accept(); } fprintf( stderr, "Sending RETR information\n" ); - Connected.write( MyDir.RETR( Command ) ); + Connected.Send( MyDir.RETR( Command ) ); Connected.close(); return 226; break; @@ -156,9 +157,9 @@ int FTP::User::ParseCommand( std::string Command ) { std::cout << "Listening on :" << MyPassivePort << "\n"; Socket::Connection Connected = Passive.accept(); if( Connected.connected() ) { - Conn.write( "125 Data connection already open; transfer starting.\n" ); + Conn.Send( "125 Data connection already open; transfer starting.\n" ); } else { - Conn.write( "150 File status okay; about to open data connection.\n" ); + Conn.Send( "150 File status okay; about to open data connection.\n" ); } while( !Connected.connected() ) { Connected = Passive.accept(); @@ -306,7 +307,13 @@ int FTP::User::ParseCommand( std::string Command ) { bool FTP::User::LoggedIn( ) { if( USER == "" || PASS == "" ) { return false; } - return true; + 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 ) { diff --git a/lib/ftp.h b/lib/ftp.h index d8e6f49d..2ea6b3c2 100644 --- a/lib/ftp.h +++ b/lib/ftp.h @@ -1,3 +1,4 @@ +#include #include #include #include @@ -54,13 +55,14 @@ namespace FTP { class User { public: - User( Socket::Connection NewConnection = Socket::Connection() ); + User( Socket::Connection NewConnection = Socket::Connection(), std::map Credentials = std::map() ); ~User( ); int ParseCommand( std::string Command ); bool LoggedIn( ); std::string NumToMsg( int MsgNum ); Socket::Connection Conn; private: + std::map AllCredentials; std::string USER; std::string PASS; Mode MODE; From f11ce43887e18f14cb9a13275464d45343fd59ab Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Thu, 30 Aug 2012 11:16:49 +0200 Subject: [PATCH 257/788] Fix compile warnings Todo: error checking for DTSC::File::getHeader --- lib/config.cpp | 6 +++++- lib/dtsc.cpp | 7 ++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/config.cpp b/lib/config.cpp index 99b1503a..c974c3ac 100644 --- a/lib/config.cpp +++ b/lib/config.cpp @@ -341,5 +341,9 @@ void Util::Daemonize(){ #if DEBUG >= 3 fprintf(stderr, "Going into background mode...\n"); #endif - daemon(1, 0); + if (daemon(1, 0) < 0) { + #if DEBUG >= 1 + fprintf(stderr, "Failed to daemonize: %s\n", strerror(errno)); + #endif + } } diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index e18d7817..44f00ebe 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -228,7 +228,12 @@ DTSC::File::File(std::string filename, bool create){ std::string & DTSC::File::getHeader(){ fseek(F, 8, SEEK_SET); strbuffer.resize(headerSize); - fread((void*)strbuffer.c_str(), headerSize, 1, F); + if (fread((void*)strbuffer.c_str(), headerSize, 1, F) != headerSize){ + /// \todo check seek as well and do something more sensible... + #if DEBUG >= 10 + fprintf(stderr, "Panic! Invalid DTSC File header\n"); + #endif + } fseek(F, 8+headerSize, SEEK_SET); return strbuffer; } From 870f71333b107aea60f44d12b50ce1c0f4b3294c Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 30 Aug 2012 11:19:46 +0200 Subject: [PATCH 258/788] Updated socket library to include socket simulation for filedescriptors. --- lib/socket.cpp | 64 ++++++++++++++++++++++++++++++++++++++++---------- lib/socket.h | 2 ++ 2 files changed, 54 insertions(+), 12 deletions(-) diff --git a/lib/socket.cpp b/lib/socket.cpp index b3eb1f4b..7e6daf02 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -22,6 +22,22 @@ std::string uint2string(unsigned int i){ /// \param sockNo Integer representing the socket to convert. Socket::Connection::Connection(int sockNo){ sock = sockNo; + pipes[0] = -1; + pipes[1] = -1; + up = 0; + down = 0; + conntime = time(0); + Error = false; + Blocking = false; +}//Socket::Connection basic constructor + +/// Simulate a socket using two file descriptors. +/// \param write The filedescriptor to write to. +/// \param read The filedescriptor to read from. +Socket::Connection::Connection(int write, int read){ + sock = -1; + pipes[0] = write; + pipes[1] = read; up = 0; down = 0; conntime = time(0); @@ -33,6 +49,8 @@ Socket::Connection::Connection(int sockNo){ /// A socket created like this is always disconnected and should/could be overwritten at some point. Socket::Connection::Connection(){ sock = -1; + pipes[0] = -1; + pipes[1] = -1; up = 0; down = 0; conntime = time(0); @@ -59,9 +77,21 @@ void Socket::Connection::close(){ #if DEBUG >= 6 fprintf(stderr, "Socket closed.\n"); #endif - shutdown(sock, SHUT_RDWR); - ::close(sock); - sock = -1; + if (sock != -1){ + shutdown(sock, SHUT_RDWR); + ::close(sock); + sock = -1; + } + if (pipes[0] != -1){ + shutdown(pipes[0], SHUT_RDWR); + ::close(pipes[0]); + pipes[0] = -1; + } + if (pipes[1] != -1){ + shutdown(pipes[1], SHUT_RDWR); + ::close(pipes[1]); + pipes[1] = -1; + } } }//Socket::Connection::close @@ -166,7 +196,7 @@ Socket::Connection::Connection(std::string host, int port, bool nonblock){ /// and when the socket is closed manually. /// \returns True if socket is connected, false otherwise. bool Socket::Connection::connected() const{ - return (sock >= 0); + return (sock >= 0) || ((pipes[0] >= 0) && (pipes[1] >= 0)); } /// Returns total amount of bytes sent. @@ -219,8 +249,13 @@ void Socket::Connection::Send(std::string data){ /// \param len Amount of bytes to write. /// \returns The amount of bytes actually written. int Socket::Connection::iwrite(const void * buffer, int len){ - if (sock < 0){return 0;} - int r = send(sock, buffer, len, 0); + if (!connected()){return 0;} + int r; + if (sock >= 0){ + r = send(sock, buffer, len, 0); + }else{ + r = write(pipes[0], buffer, len); + } if (r < 0){ switch (errno){ case EWOULDBLOCK: @@ -238,7 +273,7 @@ int Socket::Connection::iwrite(const void * buffer, int len){ break; } } - if (r == 0){ + if (r == 0 && (sock >= 0)){ close(); } up += r; @@ -251,8 +286,13 @@ int Socket::Connection::iwrite(const void * buffer, int len){ /// \param len Amount of bytes to read. /// \returns The amount of bytes actually read. int Socket::Connection::iread(void * buffer, int len){ - if (sock < 0){return 0;} - int r = recv(sock, buffer, len, 0); + if (!connected()){return 0;} + int r; + if (sock >= 0){ + r = recv(sock, buffer, len, 0); + }else{ + r = read(pipes[1], buffer, len); + } if (r < 0){ switch (errno){ case EWOULDBLOCK: @@ -270,7 +310,7 @@ int Socket::Connection::iread(void * buffer, int len){ break; } } - if (r == 0){ + if (r == 0 && (sock >= 0)){ close(); } down += r; @@ -317,13 +357,13 @@ void Socket::Connection::setHost(std::string host){ /// Returns true if these sockets are the same socket. /// Does not check the internal stats - only the socket itself. bool Socket::Connection::operator== (const Connection &B) const{ - return sock == B.sock; + return sock == B.sock && pipes[0] == B.pipes[0] && pipes[1] == B.pipes[1]; } /// Returns true if these sockets are not the same socket. /// Does not check the internal stats - only the socket itself. bool Socket::Connection::operator!= (const Connection &B) const{ - return sock != B.sock; + return sock != B.sock || pipes[0] != B.pipes[0] || pipes[1] != B.pipes[1]; } /// Returns true if the socket is valid. diff --git a/lib/socket.h b/lib/socket.h index db902c66..e4f2e69a 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -24,6 +24,7 @@ namespace Socket{ 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; @@ -42,6 +43,7 @@ namespace 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 setBlocking(bool blocking); ///< Set this socket to be blocking (true) or nonblocking (false). From a995e7215dfe89ab5f6d4feb951c5fbc2b616767 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 30 Aug 2012 11:28:36 +0200 Subject: [PATCH 259/788] Only sockets can be shut down. --- lib/socket.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/socket.cpp b/lib/socket.cpp index 7e6daf02..424460d4 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -83,12 +83,10 @@ void Socket::Connection::close(){ sock = -1; } if (pipes[0] != -1){ - shutdown(pipes[0], SHUT_RDWR); ::close(pipes[0]); pipes[0] = -1; } if (pipes[1] != -1){ - shutdown(pipes[1], SHUT_RDWR); ::close(pipes[1]); pipes[1] = -1; } From f9e700c5e163b1d8d4c4a891760cd97a942d86d8 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 30 Aug 2012 11:50:06 +0200 Subject: [PATCH 260/788] Get length instead of duration when converting to FLV metadata. --- lib/flv_tag.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index 31ff4fd7..3761c7b2 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -450,8 +450,8 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){ amfdata.addContent(AMF::Object("", "onMetaData")); amfdata.addContent(AMF::Object("", AMF::AMF0_ECMA_ARRAY)); - if (S.metadata.isMember("duration")){ - amfdata.getContentP(1)->addContent(AMF::Object("duration", S.metadata["duration"].asInt() / 1000, AMF::AMF0_NUMBER)); + if (S.metadata.isMember("length")){ + amfdata.getContentP(1)->addContent(AMF::Object("duration", S.metadata["length"].asInt(), AMF::AMF0_NUMBER)); } if (S.metadata.isMember("video")){ amfdata.getContentP(1)->addContent(AMF::Object("hasVideo", 1, AMF::AMF0_BOOL)); From 9c0aa93bfd16790dc81735eed35d1ad0f5f42a90 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Thu, 30 Aug 2012 12:23:40 +0200 Subject: [PATCH 261/788] Move Socket::*Stream > Util::Stream::* --- lib/Makefile.am | 4 ++-- lib/socket.cpp | 40 ------------------------------- lib/socket.h | 6 ----- lib/stream.cpp | 62 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/stream.h | 23 ++++++++++++++++++ 5 files changed, 87 insertions(+), 48 deletions(-) create mode 100644 lib/stream.cpp create mode 100644 lib/stream.h diff --git a/lib/Makefile.am b/lib/Makefile.am index 9d29bc18..55364d05 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,5 +1,5 @@ lib_LTLIBRARIES=libmist-1.0.la -libmist_1_0_la_SOURCES=amf.h amf.cpp auth.h auth.cpp base64.h base64.cpp config.h config.cpp crypto.h crypto.cpp dtsc.h dtsc.cpp flv_tag.h flv_tag.cpp http_parser.h http_parser.cpp json.h json.cpp procs.h procs.cpp rtmpchunks.h rtmpchunks.cpp socket.h socket.cpp mp4.h mp4.cpp +libmist_1_0_la_SOURCES=amf.h amf.cpp auth.h auth.cpp base64.h base64.cpp config.h config.cpp crypto.h crypto.cpp dtsc.h dtsc.cpp flv_tag.h flv_tag.cpp http_parser.h http_parser.cpp json.h json.cpp procs.h procs.cpp rtmpchunks.h rtmpchunks.cpp socket.h socket.cpp mp4.h mp4.cpp stream.h stream.cpp libmist_1_0_la_LIBADD=-lssl -lcrypto libmist_1_0_la_LDFLAGS = -version-info 1:0:0 @@ -7,4 +7,4 @@ pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = mist-1.0.pc library_includedir=$(includedir)/mist-1.0/mist -library_include_HEADERS = amf.h auth.h base64.h config.h crypto.h dtsc.h flv_tag.h http_parser.h json.h procs.h rtmpchunks.h socket.h mp4.h +library_include_HEADERS = amf.h auth.h base64.h config.h crypto.h dtsc.h flv_tag.h http_parser.h json.h procs.h rtmpchunks.h socket.h mp4.h stream.h diff --git a/lib/socket.cpp b/lib/socket.cpp index 424460d4..55c60d5b 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -609,43 +609,3 @@ bool Socket::Server::connected() const{ /// Returns internal socket number. int Socket::Server::getSocket(){return sock;} - -/// Connect to a stream on the system. -/// Filters the streamname, removing invalid characters and -/// converting all letters to lowercase. -/// If a '?' character is found, everything following that character is deleted. -Socket::Connection Socket::getStream(std::string streamname){ - //strip anything that isn't a number, alpha or underscore - for (std::string::iterator i=streamname.end()-1; i>=streamname.begin(); --i){ - if (*i == '?'){streamname.erase(i, streamname.end()); break;} - if (!isalpha(*i) && !isdigit(*i) && *i != '_'){ - streamname.erase(i); - }else{ - *i=tolower(*i); - } - } - return Socket::Connection("/tmp/mist/stream_"+streamname); -} - -/// Create a stream on the system. -/// Filters the streamname, removing invalid characters and -/// converting all letters to lowercase. -/// If a '?' character is found, everything following that character is deleted. -/// If the /tmp/mist directory doesn't exist yet, this will create it. -Socket::Server Socket::makeStream(std::string streamname){ - //strip anything that isn't numbers, digits or underscores - for (std::string::iterator i=streamname.end()-1; i>=streamname.begin(); --i){ - if (*i == '?'){streamname.erase(i, streamname.end()); break;} - if (!isalpha(*i) && !isdigit(*i) && *i != '_'){ - streamname.erase(i); - }else{ - *i=tolower(*i); - } - } - std::string loc = "/tmp/mist/stream_"+streamname; - //attempt to create the /tmp/mist directory if it doesn't exist already. - //ignore errors - we catch all problems in the Socket::Server creation already - mkdir("/tmp/mist", S_IRWXU | S_IRWXG | S_IRWXO); - //create and return the Socket::Server - return Socket::Server(loc); -} diff --git a/lib/socket.h b/lib/socket.h index e4f2e69a..bef03fca 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -85,11 +85,5 @@ namespace Socket{ void close(); ///< Close connection. int getSocket(); ///< Returns internal socket number. }; - - /// Connect to a stream on the system. - Connection getStream(std::string streamname); - - /// Create a stream on the system. - Server makeStream(std::string streamname); }; diff --git a/lib/stream.cpp b/lib/stream.cpp new file mode 100644 index 00000000..2e5072a3 --- /dev/null +++ b/lib/stream.cpp @@ -0,0 +1,62 @@ +/// \file stream.cpp +/// Utilities for handling streams. + +#include "stream.h" +#include "procs.h" +#include "socket.h" + +/// 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. +void Util::Stream::sanitizeName(std::string & streamname){ + //strip anything that isn't numbers, digits or underscores + for (std::string::iterator i=streamname.end()-1; i>=streamname.begin(); --i){ + if (*i == '?'){streamname.erase(i, streamname.end()); break;} + if (!isalpha(*i) && !isdigit(*i) && *i != '_'){ + streamname.erase(i); + }else{ + *i=tolower(*i); + } + } +} + +Socket::Connection Util::Stream::getLive(std::string streamname){ + sanitizeName(streamname); + return Socket::Connection("/tmp/mist/stream_"+streamname); +} + +/// Starts a process for the VoD stream. +Socket::Connection Util::Stream::getVod(std::string streamname){ + sanitizeName(streamname); + std::string filename = "/tmp/mist/vod_" + streamname; + /// \todo Is the name unique enough? + std::string name = "MistPlayer " + filename; + const char *argv[] = { "MistPlayer", filename.c_str(), NULL }; + int fdin = -1, fdout = -1; + Util::Procs::StartPiped(name, (char **)argv, &fdin, &fdout, 0); + // if StartPiped fails then fdin and fdout will be unmodified (-1) + return Socket::Connection(fdin, fdout); +} + +/// Probe for available streams. Currently first VoD, then Live. +Socket::Connection Util::Stream::getStream(std::string streamname){ + Socket::Connection vod = getVod(streamname); + if (vod.connected()){ + return vod; + } + return getLive(streamname); +} +/// Create a stream on the system. +/// Filters the streamname, removing invalid characters and +/// converting all letters to lowercase. +/// If a '?' character is found, everything following that character is deleted. +/// If the /tmp/mist directory doesn't exist yet, this will create it. +Socket::Server Util::Stream::makeLive(std::string streamname){ + sanitizeName(streamname); + std::string loc = "/tmp/mist/stream_"+streamname; + //attempt to create the /tmp/mist directory if it doesn't exist already. + //ignore errors - we catch all problems in the Socket::Server creation already + mkdir("/tmp/mist", S_IRWXU | S_IRWXG | S_IRWXO); + //create and return the Socket::Server + return Socket::Server(loc); +} diff --git a/lib/stream.h b/lib/stream.h new file mode 100644 index 00000000..e970c848 --- /dev/null +++ b/lib/stream.h @@ -0,0 +1,23 @@ +/// \file stream.h +/// Utilities for handling streams. + +#pragma once +#include +#include + +namespace Util{ + class Stream{ + /// Sanitize a streamname. + void sanitizeName(std::string & streamname); + public: + /// Get a connection to a Live stream. + static Socket::Connection getLive(std::string streamname); + /// Get a connection to a VoD stream. + static Socket::Connection getVod(std::string streamname); + /// Probe for available streams. Currently first VoD, then Live. + static Socket::Connection getStream(std::string streamname); + + /// Create a Live stream on the system. + static Socket::Server makeLive(std::string streamname); + }; +} From 740d46d4be8de246548db5231a795e2cf3d5a4ef Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Thu, 30 Aug 2012 11:16:49 +0200 Subject: [PATCH 262/788] Fix compile warnings Todo: error checking for DTSC::File::getHeader --- lib/config.cpp | 6 +++++- lib/dtsc.cpp | 7 ++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/config.cpp b/lib/config.cpp index 09361779..7f653a81 100644 --- a/lib/config.cpp +++ b/lib/config.cpp @@ -342,5 +342,9 @@ void Util::Daemonize(){ #if DEBUG >= 3 fprintf(stderr, "Going into background mode...\n"); #endif - daemon(1, 0); + if (daemon(1, 0) < 0) { + #if DEBUG >= 1 + fprintf(stderr, "Failed to daemonize: %s\n", strerror(errno)); + #endif + } } diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index e18d7817..44f00ebe 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -228,7 +228,12 @@ DTSC::File::File(std::string filename, bool create){ std::string & DTSC::File::getHeader(){ fseek(F, 8, SEEK_SET); strbuffer.resize(headerSize); - fread((void*)strbuffer.c_str(), headerSize, 1, F); + if (fread((void*)strbuffer.c_str(), headerSize, 1, F) != headerSize){ + /// \todo check seek as well and do something more sensible... + #if DEBUG >= 10 + fprintf(stderr, "Panic! Invalid DTSC File header\n"); + #endif + } fseek(F, 8+headerSize, SEEK_SET); return strbuffer; } From e8a20c146bc354e539df4354570b1c85fabfb17c Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Thu, 30 Aug 2012 12:30:11 +0200 Subject: [PATCH 263/788] stream: fix headers and missing static keyword --- lib/stream.cpp | 2 ++ lib/stream.h | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/stream.cpp b/lib/stream.cpp index 2e5072a3..cd2d563e 100644 --- a/lib/stream.cpp +++ b/lib/stream.cpp @@ -1,6 +1,8 @@ /// \file stream.cpp /// Utilities for handling streams. +#include +#include #include "stream.h" #include "procs.h" #include "socket.h" diff --git a/lib/stream.h b/lib/stream.h index e970c848..56650c3b 100644 --- a/lib/stream.h +++ b/lib/stream.h @@ -3,12 +3,12 @@ #pragma once #include -#include +#include "socket.h" namespace Util{ class Stream{ /// Sanitize a streamname. - void sanitizeName(std::string & streamname); + static void sanitizeName(std::string & streamname); public: /// Get a connection to a Live stream. static Socket::Connection getLive(std::string streamname); From c6407b3aa1bd6fbc8d7f437a76e844eca6466432 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 31 Aug 2012 10:38:53 +0200 Subject: [PATCH 264/788] Made FLV metadata more like most FLV files. --- lib/flv_tag.cpp | 49 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index 3761c7b2..a025563b 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -452,11 +452,12 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){ amfdata.addContent(AMF::Object("", AMF::AMF0_ECMA_ARRAY)); if (S.metadata.isMember("length")){ amfdata.getContentP(1)->addContent(AMF::Object("duration", S.metadata["length"].asInt(), AMF::AMF0_NUMBER)); + amfdata.getContentP(1)->addContent(AMF::Object("moovPosition", 40, AMF::AMF0_NUMBER)); } if (S.metadata.isMember("video")){ amfdata.getContentP(1)->addContent(AMF::Object("hasVideo", 1, AMF::AMF0_BOOL)); if (S.metadata["video"]["codec"].asString() == "H264"){ - amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", 7, AMF::AMF0_NUMBER)); + amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", (std::string)"avc1")); } if (S.metadata["video"]["codec"].asString() == "VP6"){ amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", 4, AMF::AMF0_NUMBER)); @@ -471,7 +472,7 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){ amfdata.getContentP(1)->addContent(AMF::Object("height", S.metadata["video"]["height"].asInt(), AMF::AMF0_NUMBER)); } if (S.metadata["video"].isMember("fpks")){ - amfdata.getContentP(1)->addContent(AMF::Object("framerate", (double)S.metadata["video"]["fpks"].asInt() / 1000.0, AMF::AMF0_NUMBER)); + amfdata.getContentP(1)->addContent(AMF::Object("videoframerate", (double)S.metadata["video"]["fpks"].asInt() / 1000.0, AMF::AMF0_NUMBER)); } if (S.metadata["video"].isMember("bps")){ amfdata.getContentP(1)->addContent(AMF::Object("videodatarate", ((double)S.metadata["video"]["bps"].asInt() * 8.0) / 1024.0, AMF::AMF0_NUMBER)); @@ -481,17 +482,13 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){ amfdata.getContentP(1)->addContent(AMF::Object("hasAudio", 1, AMF::AMF0_BOOL)); amfdata.getContentP(1)->addContent(AMF::Object("audiodelay", 0, AMF::AMF0_NUMBER)); if (S.metadata["audio"]["codec"].asString() == "AAC"){ - amfdata.getContentP(1)->addContent(AMF::Object("audiocodecid", 10, AMF::AMF0_NUMBER)); + amfdata.getContentP(1)->addContent(AMF::Object("audiocodecid", (std::string)"mp4a")); } if (S.metadata["audio"]["codec"].asString() == "MP3"){ - amfdata.getContentP(1)->addContent(AMF::Object("audiocodecid", 2, AMF::AMF0_NUMBER)); + amfdata.getContentP(1)->addContent(AMF::Object("audiocodecid", (std::string)"mp3")); } if (S.metadata["audio"].isMember("channels")){ - if (S.metadata["audio"]["channels"].asInt() > 1){ - amfdata.getContentP(1)->addContent(AMF::Object("stereo", 1, AMF::AMF0_BOOL)); - }else{ - amfdata.getContentP(1)->addContent(AMF::Object("stereo", 0, AMF::AMF0_BOOL)); - } + amfdata.getContentP(1)->addContent(AMF::Object("audiochannels", S.metadata["audio"]["channels"].asInt(), AMF::AMF0_NUMBER)); } if (S.metadata["audio"].isMember("rate")){ amfdata.getContentP(1)->addContent(AMF::Object("audiosamplerate", S.metadata["audio"]["rate"].asInt(), AMF::AMF0_NUMBER)); @@ -503,7 +500,39 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){ amfdata.getContentP(1)->addContent(AMF::Object("audiodatarate", ((double)S.metadata["audio"]["bps"].asInt() * 8.0) / 1024.0, AMF::AMF0_NUMBER)); } } - + AMF::Object trinfo = AMF::Object("trackinfo", AMF::AMF0_STRICT_ARRAY); + int i = 0; + if (S.metadata.isMember("audio")){ + trinfo.addContent(AMF::Object("", AMF::AMF0_OBJECT)); + trinfo.getContentP(i)->addContent(AMF::Object("length", ((double)S.metadata["length"].asInt()) * ((double)S.metadata["audio"]["rate"].asInt()), AMF::AMF0_NUMBER)); + trinfo.getContentP(i)->addContent(AMF::Object("timescale", S.metadata["audio"]["rate"].asInt(), AMF::AMF0_NUMBER)); + trinfo.getContentP(i)->addContent(AMF::Object("sampledescription", AMF::AMF0_STRICT_ARRAY)); + if (S.metadata["audio"]["codec"].asString() == "AAC"){ + trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string)"mp4a")); + } + if (S.metadata["audio"]["codec"].asString() == "MP3"){ + trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string)"mp3")); + } + ++i; + } + if (S.metadata.isMember("video")){ + trinfo.addContent(AMF::Object("", AMF::AMF0_OBJECT)); + trinfo.getContentP(i)->addContent(AMF::Object("length", ((double)S.metadata["length"].asInt()) * ((double)S.metadata["video"]["fkps"].asInt() / 1000.0), AMF::AMF0_NUMBER)); + trinfo.getContentP(i)->addContent(AMF::Object("timescale", ((double)S.metadata["video"]["fkps"].asInt() / 1000.0), AMF::AMF0_NUMBER)); + trinfo.getContentP(i)->addContent(AMF::Object("sampledescription", AMF::AMF0_STRICT_ARRAY)); + if (S.metadata["video"]["codec"].asString() == "H264"){ + trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string)"avc1")); + } + if (S.metadata["video"]["codec"].asString() == "VP6"){ + trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string)"vp6")); + } + if (S.metadata["video"]["codec"].asString() == "H263"){ + trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string)"h263")); + } + ++i; + } + amfdata.getContentP(1)->addContent(trinfo); + std::string tmp = amfdata.Pack(); len = tmp.length() + 15; if (len > 0){ From b8a2993c73141fc59dc14c459ef6b75aad3d836c Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 31 Aug 2012 10:39:24 +0200 Subject: [PATCH 265/788] Implement decent seeking in DTSC::File. --- lib/dtsc.cpp | 48 +++++++++++++++++++++++++++++++++++++++++------- lib/dtsc.h | 3 +++ 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 44f00ebe..bd7755e9 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -221,20 +221,24 @@ DTSC::File::File(std::string filename, bool create){ headerSize = ntohl(ubuffer[0]); } fseek(F, 8+headerSize, SEEK_SET); + currframe = 1; + frames[currframe] = ftell(F); } /// Returns the header metadata for this file as a std::string. /// Sets the file pointer to the first packet. std::string & DTSC::File::getHeader(){ - fseek(F, 8, SEEK_SET); + if (fseek(F, 8, SEEK_SET) != 0){ + strbuffer = ""; + return strbuffer; + } strbuffer.resize(headerSize); - if (fread((void*)strbuffer.c_str(), headerSize, 1, F) != headerSize){ - /// \todo check seek as well and do something more sensible... - #if DEBUG >= 10 - fprintf(stderr, "Panic! Invalid DTSC File header\n"); - #endif + if (fread((void*)strbuffer.c_str(), headerSize, 1, F) != 1){ + strbuffer = ""; + return strbuffer; } fseek(F, 8+headerSize, SEEK_SET); + currframe = 1; return strbuffer; } @@ -254,6 +258,7 @@ bool DTSC::File::writeHeader(std::string & header, bool force){ /// Reads the packet available at the current file position, returning it as a std::string. /// If the packet could not be read for any reason, the reason is printed to stderr and an empty string returned. +/// Reading the packet means the file position is increased to the next packet. std::string & DTSC::File::getPacket(){ if (fread(buffer, 4, 1, F) != 1){ fprintf(stderr, "Could not read header\n"); @@ -261,7 +266,7 @@ std::string & DTSC::File::getPacket(){ return strbuffer; } if (memcmp(buffer, DTSC::Magic_Packet, 4) != 0){ - fprintf(stderr, "Could not overwrite header - not equal size\n"); + fprintf(stderr, "Invalid header\n"); strbuffer = ""; return strbuffer; } @@ -278,9 +283,38 @@ std::string & DTSC::File::getPacket(){ strbuffer = ""; return strbuffer; } + currframe++; + frames[currframe] = ftell(F); return strbuffer; } +/// Attempts to seek to the given frame number within the file. +/// Returns true if successful, false otherwise. +bool DTSC::File::seek_frame(int frameno){ + std::map::iterator it = frames.lower_bound(frameno); + if (it->first == frameno){ + if (fseek(F, it->second, SEEK_SET) == 0){ + currframe = frameno; + return true; + } + }else{ + if (fseek(F, it->second, SEEK_SET) == 0){ + currframe = it->first; + while (currframe < frameno){ + if (fread(buffer, 4, 1, F) != 1){return false;}//read header + if (memcmp(buffer, DTSC::Magic_Packet, 4) != 0){return false;}//check header + if (fread(buffer, 4, 1, F) != 1){return false;}//read size + uint32_t * ubuffer = (uint32_t *)buffer; + long packSize = ntohl(ubuffer[0]); + if (fseek(F, packSize, SEEK_CUR) != 0){return false;}//seek to next packet + currframe++; + } + return true; + } + } + return false; +} + /// Close the file if open DTSC::File::~File(){ if (F){ diff --git a/lib/dtsc.h b/lib/dtsc.h index 2a6ca87d..473f0e59 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -70,8 +70,11 @@ namespace DTSC{ std::string & getHeader(); bool writeHeader(std::string & header, bool force = false); std::string & getPacket(); + bool seek_frame(int frameno); private: std::string strbuffer; + std::map frames; + int currframe; FILE * F; unsigned long headerSize; char buffer[4]; From 8c8bf628b913e5cdbb91c3a6511d3c2dbe83de78 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Fri, 31 Aug 2012 13:10:04 +0200 Subject: [PATCH 266/788] Do not prevent user from overriding CXXFLAGS make CXXFLAGS=-O0 would not work without this. --- configure.ac | 5 +++-- lib/Makefile.am | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index d1c5047a..5a8dd7c9 100644 --- a/configure.ac +++ b/configure.ac @@ -38,10 +38,11 @@ AC_FUNC_REALLOC AC_CHECK_FUNCS([dup2 gettimeofday memset mkdir socket strerror]) # Fix chars to unsigned -CXXFLAGS="$CXXFLAGS -funsigned-char" +AC_SUBST([global_CFLAGS], [-funsigned-char]) #allow verbose mode compiles -AC_ARG_ENABLE([verbose], AC_HELP_STRING([--enable-verbose], [Compile with verbose messages]), CXXFLAGS="-DDEBUG=4 $CXXFLAGS") +AC_ARG_ENABLE([verbose], AC_HELP_STRING([--enable-verbose], [Compile with verbose messages]), + AC_DEFINE([DEBUG], [4])) AC_CONFIG_FILES([Makefile lib/Makefile lib/mist-1.0.pc]) AC_OUTPUT diff --git a/lib/Makefile.am b/lib/Makefile.am index 55364d05..d9e8c4e5 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,3 +1,5 @@ +AM_CPPFLAGS = $(global_CFLAGS) + lib_LTLIBRARIES=libmist-1.0.la libmist_1_0_la_SOURCES=amf.h amf.cpp auth.h auth.cpp base64.h base64.cpp config.h config.cpp crypto.h crypto.cpp dtsc.h dtsc.cpp flv_tag.h flv_tag.cpp http_parser.h http_parser.cpp json.h json.cpp procs.h procs.cpp rtmpchunks.h rtmpchunks.cpp socket.h socket.cpp mp4.h mp4.cpp stream.h stream.cpp libmist_1_0_la_LIBADD=-lssl -lcrypto From 4a940f7514cfad5e99987894d77bf4e2fb12eae3 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 31 Aug 2012 16:52:57 +0200 Subject: [PATCH 267/788] Improved seeking in DTSC::File, also, now tested working. --- lib/dtsc.cpp | 94 ++++++++++++++++++++++++++++++++++++++-------------- lib/dtsc.h | 8 ++++- 2 files changed, 77 insertions(+), 25 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index bd7755e9..d021c68d 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -222,7 +222,8 @@ DTSC::File::File(std::string filename, bool create){ } fseek(F, 8+headerSize, SEEK_SET); currframe = 1; - frames[currframe] = ftell(F); + frames[1] = 8+headerSize; + msframes[1] = 0; } /// Returns the header metadata for this file as a std::string. @@ -256,24 +257,27 @@ bool DTSC::File::writeHeader(std::string & header, bool force){ return (ret == 1); } -/// Reads the packet available at the current file position, returning it as a std::string. -/// If the packet could not be read for any reason, the reason is printed to stderr and an empty string returned. +/// Reads the packet available at the current file position. +/// If the packet could not be read for any reason, the reason is printed to stderr. /// Reading the packet means the file position is increased to the next packet. -std::string & DTSC::File::getPacket(){ +void DTSC::File::seekNext(){ if (fread(buffer, 4, 1, F) != 1){ fprintf(stderr, "Could not read header\n"); strbuffer = ""; - return strbuffer; + jsonbuffer.null(); + return; } if (memcmp(buffer, DTSC::Magic_Packet, 4) != 0){ - fprintf(stderr, "Invalid header\n"); + fprintf(stderr, "Invalid header - %.4s != %.4s\n", buffer, DTSC::Magic_Packet); strbuffer = ""; - return strbuffer; + jsonbuffer.null(); + return; } if (fread(buffer, 4, 1, F) != 1){ fprintf(stderr, "Could not read size\n"); strbuffer = ""; - return strbuffer; + jsonbuffer.null(); + return; } uint32_t * ubuffer = (uint32_t *)buffer; long packSize = ntohl(ubuffer[0]); @@ -281,33 +285,49 @@ std::string & DTSC::File::getPacket(){ if (fread((void*)strbuffer.c_str(), packSize, 1, F) != 1){ fprintf(stderr, "Could not read packet\n"); strbuffer = ""; - return strbuffer; + jsonbuffer.null(); + return; + } + jsonbuffer = JSON::fromDTMI(strbuffer); + if (jsonbuffer.isMember("keyframe")){ + long pos = ftell(F) - (packSize + 8); + if (frames[currframe] != pos){ + currframe++; + currtime = jsonbuffer["time"].asInt(); + #if DEBUG >= 4 + std::cerr << "Found a new frame " << currframe << " @ " << pos << "b/" << currtime << "s" << std::endl; + #endif + frames[currframe] = pos; + msframes[currframe] = currtime; + } } - currframe++; - frames[currframe] = ftell(F); - return strbuffer; } +/// Returns the internal buffer of the last read packet in raw binary format. +std::string & DTSC::File::getPacket(){return strbuffer;} + +/// Returns the internal buffer of the last read packet in JSON format. +JSON::Value & DTSC::File::getJSON(){return jsonbuffer;} + /// Attempts to seek to the given frame number within the file. /// Returns true if successful, false otherwise. bool DTSC::File::seek_frame(int frameno){ - std::map::iterator it = frames.lower_bound(frameno); - if (it->first == frameno){ - if (fseek(F, it->second, SEEK_SET) == 0){ + if (frames.count(frameno) > 0){ + if (fseek(F, frames[frameno], SEEK_SET) == 0){ currframe = frameno; return true; } }else{ - if (fseek(F, it->second, SEEK_SET) == 0){ - currframe = it->first; + for (int i = frameno; i >= 1; --i){ + if (frames.count(i) > 0){currframe = i; break;} + } + if (fseek(F, frames[currframe], SEEK_SET) == 0){ + #if DEBUG >= 4 + std::cerr << "Seeking from frame " << currframe << " @ " << frames[currframe] << " to " << frameno << std::endl; + #endif while (currframe < frameno){ - if (fread(buffer, 4, 1, F) != 1){return false;}//read header - if (memcmp(buffer, DTSC::Magic_Packet, 4) != 0){return false;}//check header - if (fread(buffer, 4, 1, F) != 1){return false;}//read size - uint32_t * ubuffer = (uint32_t *)buffer; - long packSize = ntohl(ubuffer[0]); - if (fseek(F, packSize, SEEK_CUR) != 0){return false;}//seek to next packet - currframe++; + seekNext(); + if (jsonbuffer.isNull()){return false;} } return true; } @@ -315,6 +335,32 @@ bool DTSC::File::seek_frame(int frameno){ return false; } +/// Attempts to seek to the given time in ms within the file. +/// Returns true if successful, false otherwise. +bool DTSC::File::seek_time(int ms){ + std::map::iterator it; + currtime = 0; + currframe = 1; + for (it = msframes.begin(); it != msframes.end(); ++it){ + if (it->second > ms){break;} + if (it->second > currtime){currtime = it->second; currframe = it->first;} + } + if (fseek(F, frames[currframe], SEEK_SET) == 0){ + #if DEBUG >= 4 + std::cerr << "Seeking from frame " << currframe << " @ " << msframes[currframe] << "ms to " << ms << "ms" << std::endl; + #endif + while (currtime < ms){ + seekNext(); + if (jsonbuffer.isNull()){return false;} + } + if (currtime > ms){ + return seek_frame(currframe - 1); + } + return true; + } + return false; +} + /// Close the file if open DTSC::File::~File(){ if (F){ diff --git a/lib/dtsc.h b/lib/dtsc.h index 473f0e59..ff591227 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -69,11 +69,17 @@ namespace DTSC{ ~File(); std::string & getHeader(); bool writeHeader(std::string & header, bool force = false); + void seekNext(); std::string & getPacket(); + JSON::Value & getJSON(); bool seek_frame(int frameno); - private: + bool seek_time(int seconds); + private: std::string strbuffer; + JSON::Value jsonbuffer; std::map frames; + std::map msframes; + long long int currtime; int currframe; FILE * F; unsigned long headerSize; From c6bb67c4b939d73d114523a33d963ec8614b4571 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 31 Aug 2012 16:53:46 +0200 Subject: [PATCH 268/788] Improvements to HTTP::Parser - now builds responses and requests by reference and has getUrl() method to get the URL without URI-parameters. --- lib/http_parser.cpp | 32 ++++++++++++++++++++------------ lib/http_parser.h | 6 ++++-- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/lib/http_parser.cpp b/lib/http_parser.cpp index 2047be6d..526ff014 100644 --- a/lib/http_parser.cpp +++ b/lib/http_parser.cpp @@ -24,18 +24,18 @@ void HTTP::Parser::Clean(){ /// 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(){ +std::string & HTTP::Parser::BuildRequest(){ /// \todo Include GET/POST variable parsing? std::map::iterator it; if (protocol.size() < 5 || protocol.substr(0, 4) != "HTTP"){protocol = "HTTP/1.0";} - std::string tmp = method+" "+url+" "+protocol+"\n"; + builder = method+" "+url+" "+protocol+"\n"; for (it=headers.begin(); it != headers.end(); it++){ if ((*it).first != "" && (*it).second != ""){ - tmp += (*it).first + ": " + (*it).second + "\n"; + builder += (*it).first + ": " + (*it).second + "\n"; } } - tmp += "\n" + body; - return tmp; + builder += "\n" + body; + return builder; } /// Returns a string containing a valid HTTP 1.0 or 1.1 response, ready for sending. @@ -44,21 +44,21 @@ std::string HTTP::Parser::BuildRequest(){ /// \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){ +std::string & HTTP::Parser::BuildResponse(std::string code, std::string message){ /// \todo Include GET/POST variable parsing? std::map::iterator it; if (protocol.size() < 5 || protocol.substr(0, 4) != "HTTP"){protocol = "HTTP/1.0";} - std::string tmp = protocol+" "+code+" "+message+"\n"; + builder = protocol+" "+code+" "+message+"\n"; for (it=headers.begin(); it != headers.end(); it++){ if ((*it).first != "" && (*it).second != ""){ if ((*it).first != "Content-Length" || (*it).second != "0"){ - tmp += (*it).first + ": " + (*it).second + "\n"; + builder += (*it).first + ": " + (*it).second + "\n"; } } } - tmp += "\n"; - tmp += body; - return tmp; + builder += "\n"; + builder += body; + return builder; } /// Trims any whitespace at the front or back of the string. @@ -86,6 +86,15 @@ void HTTP::Parser::SetBody(char * buffer, int 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. @@ -150,7 +159,6 @@ bool HTTP::Parser::parse(std::string & HTTPbuffer){ protocol = tmpA; if (url.find('?') != std::string::npos){ parseVars(url.substr(url.find('?')+1)); //parse GET variables - url.resize(url.find('?')); } }else{seenReq = false;} }else{seenReq = false;} diff --git a/lib/http_parser.h b/lib/http_parser.h index 609e7d28..ae235a2e 100644 --- a/lib/http_parser.h +++ b/lib/http_parser.h @@ -16,13 +16,14 @@ namespace HTTP{ 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 code, std::string message); + std::string & BuildRequest(); + std::string & BuildResponse(std::string code, std::string message); void Chunkify(std::string & bodypart); void Clean(); static std::string urlunescape(const std::string & in); @@ -37,6 +38,7 @@ namespace HTTP{ bool seenReq; bool parse(std::string & HTTPbuffer); void parseVars(std::string data); + std::string builder; std::string read_buffer; std::map headers; std::map vars; From 2afb88c8de54a3e0e88891f07709a742f63307ec Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 31 Aug 2012 16:54:54 +0200 Subject: [PATCH 269/788] Overloaded Socket::Connection::Send with new versions and now requires arguments to be passed by reference. Also fixed a ::close bug and hugely improved performance. --- lib/socket.cpp | 88 +++++++++++++++++++++++++++++++++++++++++++------- lib/socket.h | 4 ++- 2 files changed, 80 insertions(+), 12 deletions(-) diff --git a/lib/socket.cpp b/lib/socket.cpp index 55c60d5b..d94545b6 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -58,16 +58,22 @@ Socket::Connection::Connection(){ Blocking = false; }//Socket::Connection basic constructor - -/// Set this socket to be blocking (true) or nonblocking (false). -void Socket::Connection::setBlocking(bool blocking){ - int flags = fcntl(sock, F_GETFL, 0); +/// Internally used call to make an file descriptor blocking or not. +void setFDBlocking(int FD, bool blocking){ + int flags = fcntl(FD, F_GETFL, 0); if (!blocking){ flags |= O_NONBLOCK; }else{ flags &= !O_NONBLOCK; } - fcntl(sock, F_SETFL, flags); + fcntl(FD, F_SETFL, flags); +} + +/// Set this socket to be blocking (true) or nonblocking (false). +void Socket::Connection::setBlocking(bool blocking){ + if (sock >=0){setFDBlocking(sock, blocking);} + if (pipes[0] >=0){setFDBlocking(pipes[0], blocking);} + if (pipes[1] >=0){setFDBlocking(pipes[1], blocking);} } /// Close connection. The internal socket is closed and then set to -1. @@ -79,15 +85,18 @@ void Socket::Connection::close(){ #endif if (sock != -1){ shutdown(sock, SHUT_RDWR); - ::close(sock); + errno = EINTR; + while (::close(sock) != 0 && errno == EINTR){} sock = -1; } if (pipes[0] != -1){ - ::close(pipes[0]); + errno = EINTR; + while (::close(pipes[0]) != 0 && errno == EINTR){} pipes[0] = -1; } if (pipes[1] != -1){ - ::close(pipes[1]); + errno = EINTR; + while (::close(pipes[1]) != 0 && errno == EINTR){} pipes[1] = -1; } } @@ -237,8 +246,64 @@ std::string & Socket::Connection::Received(){ } /// Appends data to the upbuffer. -void Socket::Connection::Send(std::string data){ - upbuffer.append(data); +/// This will attempt to send the upbuffer (if non-empty) first. +/// If the upbuffer is empty before or after this attempt, it will attempt to send +/// the data right away. Any data that could not be send will be put into the upbuffer. +/// This means this function is blocking if the socket is, but nonblocking otherwise. +void Socket::Connection::Send(std::string & data){ + if (upbuffer.size() > 0){ + iwrite(upbuffer); + if (upbuffer.size() > 0){ + upbuffer.append(data); + } + } + if (upbuffer.size() == 0){ + int i = iwrite(data.c_str(), data.size()); + if (i < data.size()){ + upbuffer.append(data, i, data.size() - i); + } + } +} + +/// Appends data to the upbuffer. +/// This will attempt to send the upbuffer (if non-empty) first. +/// If the upbuffer is empty before or after this attempt, it will attempt to send +/// the data right away. Any data that could not be send will be put into the upbuffer. +/// This means this function is blocking if the socket is, but nonblocking otherwise. +void Socket::Connection::Send(const char * data){ + int len = strlen(data); + if (upbuffer.size() > 0){ + iwrite(upbuffer); + if (upbuffer.size() > 0){ + upbuffer.append(data, (size_t)len); + } + } + if (upbuffer.size() == 0){ + int i = iwrite(data, len); + if (i < len){ + upbuffer.append(data + i, (size_t)(len - i)); + } + } +} + +/// Appends data to the upbuffer. +/// This will attempt to send the upbuffer (if non-empty) first. +/// If the upbuffer is empty before or after this attempt, it will attempt to send +/// the data right away. Any data that could not be send will be put into the upbuffer. +/// This means this function is blocking if the socket is, but nonblocking otherwise. +void Socket::Connection::Send(const char * data, size_t len){ + if (upbuffer.size() > 0){ + iwrite(upbuffer); + if (upbuffer.size() > 0){ + upbuffer.append(data, len); + } + } + if (upbuffer.size() == 0){ + int i = iwrite(data, len); + if (i < len){ + upbuffer.append(data + i, len - i); + } + } } /// Incremental write call. This function tries to write len bytes to the socket from the buffer, @@ -593,7 +658,8 @@ void Socket::Server::close(){ fprintf(stderr, "ServerSocket closed.\n"); #endif shutdown(sock, SHUT_RDWR); - ::close(sock); + errno = EINTR; + while (::close(sock) != 0 && errno == EINTR){} sock = -1; } }//Socket::Server::close diff --git a/lib/socket.h b/lib/socket.h index bef03fca..273a6433 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -56,7 +56,9 @@ namespace Socket{ bool spool(); ///< Updates the downbuffer and upbuffer internal variables. bool flush(); ///< Updates the downbuffer and upbuffer internal variables until upbuffer is empty. std::string & Received(); ///< Returns a reference to the download buffer. - void Send(std::string data); ///< Appends data to the upbuffer. + void Send(std::string & data); ///< Appends data to the upbuffer. + void Send(const char * data); ///< Appends data to the upbuffer. + void Send(const char * data, size_t len); ///< Appends data to the upbuffer. //stats related methods unsigned int dataUp(); ///< Returns total amount of bytes sent. unsigned int dataDown(); ///< Returns total amount of bytes received. From ed2021d01c3bd95e084f414104ffa884c8f5f13a Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 31 Aug 2012 16:55:28 +0200 Subject: [PATCH 270/788] Utils::Stream::getStream now actually works in all cases where it should, the way it should. ^_^ --- lib/stream.cpp | 40 ++++++++++++++++++++++++++++------------ lib/stream.h | 18 ++++++------------ 2 files changed, 34 insertions(+), 24 deletions(-) diff --git a/lib/stream.cpp b/lib/stream.cpp index cd2d563e..a091325c 100644 --- a/lib/stream.cpp +++ b/lib/stream.cpp @@ -1,8 +1,13 @@ /// \file stream.cpp /// Utilities for handling streams. +#if DEBUG >= 4 +#include +#endif + #include #include +#include "json.h" #include "stream.h" #include "procs.h" #include "socket.h" @@ -23,31 +28,42 @@ void Util::Stream::sanitizeName(std::string & streamname){ } Socket::Connection Util::Stream::getLive(std::string streamname){ - sanitizeName(streamname); return Socket::Connection("/tmp/mist/stream_"+streamname); } -/// Starts a process for the VoD stream. -Socket::Connection Util::Stream::getVod(std::string streamname){ - sanitizeName(streamname); - std::string filename = "/tmp/mist/vod_" + streamname; - /// \todo Is the name unique enough? +/// Starts a process for a VoD stream. +Socket::Connection Util::Stream::getVod(std::string filename){ std::string name = "MistPlayer " + filename; const char *argv[] = { "MistPlayer", filename.c_str(), NULL }; - int fdin = -1, fdout = -1; - Util::Procs::StartPiped(name, (char **)argv, &fdin, &fdout, 0); + int fdin = -1, fdout = -1, fderr = fileno(stderr); + Util::Procs::StartPiped(name, (char **)argv, &fdin, &fdout, &fderr); // if StartPiped fails then fdin and fdout will be unmodified (-1) return Socket::Connection(fdin, fdout); } /// Probe for available streams. Currently first VoD, then Live. Socket::Connection Util::Stream::getStream(std::string streamname){ - Socket::Connection vod = getVod(streamname); - if (vod.connected()){ - return vod; + sanitizeName(streamname); + JSON::Value ServConf = JSON::fromFile("/tmp/mist/streamlist"); + if (ServConf["streams"].isMember(streamname)){ + if (ServConf["streams"][streamname]["channel"]["URL"].asString()[0] == '/'){ + #if DEBUG >= 4 + std::cerr << "Opening VoD stream from file " << ServConf["streams"][streamname]["channel"]["URL"].asString() << std::endl; + #endif + return getVod(ServConf["streams"][streamname]["channel"]["URL"].asString()); + }else{ + #if DEBUG >= 4 + std::cerr << "Opening live stream " << streamname << std::endl; + #endif + return Socket::Connection("/tmp/mist/stream_"+streamname); + } } - return getLive(streamname); + #if DEBUG >= 4 + std::cerr << "Could not open stream " << streamname << " - stream not found" << std::endl; + #endif + return Socket::Connection(); } + /// Create a stream on the system. /// Filters the streamname, removing invalid characters and /// converting all letters to lowercase. diff --git a/lib/stream.h b/lib/stream.h index 56650c3b..f08433d9 100644 --- a/lib/stream.h +++ b/lib/stream.h @@ -7,17 +7,11 @@ namespace Util{ class Stream{ - /// Sanitize a streamname. - static void sanitizeName(std::string & streamname); - public: - /// Get a connection to a Live stream. - static Socket::Connection getLive(std::string streamname); - /// Get a connection to a VoD stream. - static Socket::Connection getVod(std::string streamname); - /// Probe for available streams. Currently first VoD, then Live. - static Socket::Connection getStream(std::string streamname); - - /// Create a Live stream on the system. - static Socket::Server makeLive(std::string streamname); + public: + static void sanitizeName(std::string & streamname); + static Socket::Connection getLive(std::string streamname); + static Socket::Connection getVod(std::string streamname); + static Socket::Connection getStream(std::string streamname); + static Socket::Server makeLive(std::string streamname); }; } From 03f3ecab1beb3e2d774fd274dbc9ad57940c9e66 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 31 Aug 2012 16:56:14 +0200 Subject: [PATCH 271/788] RTMP lib upgrades to return strings by reference - note: initial testing indicates raptors. Needs to be looked at ASAP! --- lib/rtmpchunks.cpp | 36 ++++++++++++++++++------------------ lib/rtmpchunks.h | 16 ++++++++-------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/lib/rtmpchunks.cpp b/lib/rtmpchunks.cpp index 51a2e9dd..961176db 100644 --- a/lib/rtmpchunks.cpp +++ b/lib/rtmpchunks.cpp @@ -36,8 +36,8 @@ std::map RTMPStream::Chunk::lastrecv; /// Packs up the chunk for sending over the network. /// \warning Do not call if you are not actually sending the resulting data! /// \returns A std::string ready to be sent. -std::string RTMPStream::Chunk::Pack(){ - std::string output = ""; +std::string & RTMPStream::Chunk::Pack(){ + static std::string output = ""; RTMPStream::Chunk prev = lastsend[cs_id]; unsigned int tmpi; unsigned char chtype = 0x00; @@ -143,8 +143,8 @@ RTMPStream::Chunk::Chunk(){ }//constructor /// Packs up a chunk with the given arguments as properties. -std::string RTMPStream::SendChunk(unsigned int cs_id, unsigned char msg_type_id, unsigned int msg_stream_id, std::string data){ - RTMPStream::Chunk ch; +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 = RTMPStream::getNowMS(); ch.len = data.size(); @@ -161,8 +161,8 @@ std::string RTMPStream::SendChunk(unsigned int cs_id, unsigned char msg_type_id, /// \param data Contents of the media data. /// \param len Length of the media data, in bytes. /// \param ts Timestamp of the media data, relative to current system time. -std::string RTMPStream::SendMedia(unsigned char msg_type_id, unsigned char * data, int len, unsigned int ts){ - RTMPStream::Chunk ch; +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; @@ -170,14 +170,14 @@ std::string RTMPStream::SendMedia(unsigned char msg_type_id, unsigned char * dat ch.len_left = 0; ch.msg_type_id = msg_type_id; ch.msg_stream_id = 1; - ch.data.append((char*)data, (size_t)len); + 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){ - RTMPStream::Chunk ch; +std::string & RTMPStream::SendMedia(FLV::Tag & tag){ + static RTMPStream::Chunk ch; ch.cs_id = ((unsigned char)tag.data[0]); ch.timestamp = tag.tagTime(); ch.len = tag.len-15; @@ -185,13 +185,13 @@ std::string RTMPStream::SendMedia(FLV::Tag & tag){ ch.len_left = 0; ch.msg_type_id = (unsigned char)tag.data[0]; ch.msg_stream_id = 1; - ch.data.append(tag.data+11, (size_t)(tag.len-15)); + ch.data = std::string(tag.data+11, (size_t)(tag.len-15)); return ch.Pack(); }//SendMedia /// Packs up a chunk for a control message with 1 argument. -std::string RTMPStream::SendCTL(unsigned char type, unsigned int data){ - RTMPStream::Chunk ch; +std::string & RTMPStream::SendCTL(unsigned char type, unsigned int data){ + static RTMPStream::Chunk ch; ch.cs_id = 2; ch.timestamp = RTMPStream::getNowMS(); ch.len = 4; @@ -205,8 +205,8 @@ std::string RTMPStream::SendCTL(unsigned char type, unsigned int data){ }//SendCTL /// Packs up a chunk for a control message with 2 arguments. -std::string RTMPStream::SendCTL(unsigned char type, unsigned int data, unsigned char data2){ - RTMPStream::Chunk ch; +std::string & RTMPStream::SendCTL(unsigned char type, unsigned int data, unsigned char data2){ + static RTMPStream::Chunk ch; ch.cs_id = 2; ch.timestamp = RTMPStream::getNowMS(); ch.len = 5; @@ -221,8 +221,8 @@ std::string RTMPStream::SendCTL(unsigned char type, unsigned int data, unsigned }//SendCTL /// Packs up a chunk for a user control message with 1 argument. -std::string RTMPStream::SendUSR(unsigned char type, unsigned int data){ - RTMPStream::Chunk ch; +std::string & RTMPStream::SendUSR(unsigned char type, unsigned int data){ + static RTMPStream::Chunk ch; ch.cs_id = 2; ch.timestamp = RTMPStream::getNowMS(); ch.len = 6; @@ -238,8 +238,8 @@ std::string RTMPStream::SendUSR(unsigned char type, unsigned int data){ }//SendUSR /// Packs up a chunk for a user control message with 2 arguments. -std::string RTMPStream::SendUSR(unsigned char type, unsigned int data, unsigned int data2){ - RTMPStream::Chunk ch; +std::string & RTMPStream::SendUSR(unsigned char type, unsigned int data, unsigned int data2){ + static RTMPStream::Chunk ch; ch.cs_id = 2; ch.timestamp = RTMPStream::getNowMS(); ch.len = 10; diff --git a/lib/rtmpchunks.h b/lib/rtmpchunks.h index 009cfd29..e153f4ca 100644 --- a/lib/rtmpchunks.h +++ b/lib/rtmpchunks.h @@ -46,20 +46,20 @@ namespace RTMPStream{ Chunk(); bool Parse(std::string & data); - std::string Pack(); + std::string & Pack(); private: static std::map lastsend; static std::map lastrecv; };//RTMPStream::Chunk - std::string SendChunk(unsigned int cs_id, unsigned char msg_type_id, unsigned int msg_stream_id, std::string data); - std::string SendMedia(unsigned char msg_type_id, unsigned char * data, int len, unsigned int ts); - std::string SendMedia(FLV::Tag & tag); - std::string SendCTL(unsigned char type, unsigned int data); - std::string SendCTL(unsigned char type, unsigned int data, unsigned char data2); - std::string SendUSR(unsigned char type, unsigned int data); - std::string SendUSR(unsigned char type, unsigned int data, unsigned int data2); + 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; From 86488b9d153cfa9f4d7d79b57eef6fa755277b1a Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Tue, 28 Aug 2012 13:59:54 +0200 Subject: [PATCH 272/788] Doxygen upgrade --- Doxyfile | 1580 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 1546 insertions(+), 34 deletions(-) diff --git a/Doxyfile b/Doxyfile index b6edff54..3948c111 100644 --- a/Doxyfile +++ b/Doxyfile @@ -1,296 +1,1808 @@ -# Doxyfile 1.6.3 +# Doxyfile 1.8.2 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" "). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + DOXYFILE_ENCODING = UTF-8 -PROJECT_NAME = DDVTECH Streaming Server + +# The PROJECT_NAME tag is a single word (or sequence of words) that should +# identify the project. Note that if you do not use Doxywizard you need +# to put quotes around the project name if it contains spaces. + +PROJECT_NAME = DDVTECHStreamingServer + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + PROJECT_NUMBER = 1 + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer +# a quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + OUTPUT_DIRECTORY = ./docs + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + INLINE_INHERITED_MEMB = YES + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. Note that you specify absolute paths here, but also +# relative paths, which will be relative from the directory where doxygen is +# started. + STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file system +# doesn't support long names like on DOS, Mac, or CD-ROM. + SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + TAB_SIZE = 2 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding +# "class=itcl::class" will allow you to use the command class in the +# itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, +# and language is one of the parsers supported by doxygen: IDL, Java, +# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, +# C++. For instance to make doxygen treat .inc files as Fortran files (default +# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note +# that for custom extensions you also need to set FILE_PATTERNS otherwise the +# files are not read by doxygen. + EXTENSION_MAPPING = + +# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all +# comments according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you +# can mix doxygen, HTML, and XML commands with Markdown formatting. +# Disable only in case of backward compatibilities issues. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented classes, +# or namespaces to their corresponding documentation. Such a link can be +# prevented in individual cases by by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also makes the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + BUILTIN_STL_SUPPORT = YES + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter and setter methods for a property. Setting this option to YES (the default) will make doxygen replace the get and set methods by a property in the documentation. This will only work if the methods are indeed getting or setting a simple type. If this is not the case, or you want to show the methods anyway, you should set this option to NO. + IDL_PROPERTY_SUPPORT = NO + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and +# unions are shown inside the group in which they are included (e.g. using +# @ingroup) instead of on a separate page (for HTML and Man pages) or +# section (for LaTeX and RTF). + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and +# unions with only public data fields will be shown inline in the documentation +# of the scope in which they are defined (i.e. file, namespace, or group +# documentation), provided this scope is documented. If set to NO (the default), +# structs, classes, and unions are shown on a separate page (for HTML and Man +# pages) or section (for LaTeX and RTF). + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + TYPEDEF_HIDES_STRUCT = NO + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penalty. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will roughly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols. + SYMBOL_CACHE_SIZE = 0 +# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be +# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given +# their name and scope. Since this can be an expensive process and often the +# same symbol appear multiple times in the code, doxygen keeps a cache of +# pre-resolved symbols. If the cache is too small doxygen will become slower. +# If the cache is too large, memory is wasted. The cache size is given by this +# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols. + +LOOKUP_CACHE_SIZE = 0 + #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + EXTRACT_PRIVATE = YES + +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# scope will be included in the documentation. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespaces are hidden. + EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to +# do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even +# if there is only one candidate or it is obvious which candidate to choose +# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# will still accept a match between prototype and implementation in such cases. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and macros in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + SHOW_USED_FILES = YES -SHOW_DIRECTORIES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + LAYOUT_FILE = +# The CITE_BIB_FILES tag can be used to specify one or more bib files +# containing the references data. This must be a list of .bib files. The +# .bib extension is automatically appended if omitted. Using this command +# requires the bibtex tool to be installed. See also +# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style +# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this +# feature you need bibtex and perl available in the search path. + +CITE_BIB_FILES = + #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + WARN_IF_DOC_ERROR = YES + +# The WARN_NO_PARAMDOC option can be enabled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + INPUT = . + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# *.f90 *.f *.for *.vhd *.vhdl + FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + RECURSIVE = YES -EXCLUDE = + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. + EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + EXCLUDE_PATTERNS = */.git/* + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be +# ignored. + INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty or if +# non of the patterns match the file name, INPUT_FILTER is applied. + FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + FILTER_SOURCE_FILES = NO +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) +# and it is also possible to disable source filtering for a specific pattern +# using *.ext= (so without naming a filter). This option only has effect when +# FILTER_SOURCE_FILES is enabled. + +FILTER_SOURCE_PATTERNS = + #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C, C++ and Fortran comments will always remain visible. + STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. Note that when using a custom header you are responsible +# for the proper inclusion of any scripts and style sheets that doxygen +# needs, which is dependent on the configuration options used. +# It is advised to generate a default header using "doxygen -w html +# header.html footer.html stylesheet.css YourConfigFile" and then modify +# that header. Note that the header is subject to change so you typically +# have to redo this when upgrading to a newer version of doxygen or when +# changing the value of configuration settings such as GENERATE_TREEVIEW! + HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If left blank doxygen will +# generate a default style sheet. Note that it is recommended to use +# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this +# tag will in the future become obsolete. + HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional +# user-defined cascading style sheet that is included after the standard +# style sheets created by doxygen. Using this option one can overrule +# certain style aspects. This is preferred over using HTML_STYLESHEET +# since it does not replace the standard style sheet and is therefor more +# robust against future updates. Doxygen will copy the style sheet file to +# the output directory. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that +# the files will be copied as-is; there are no commands or markers available. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the style sheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + HTML_TIMESTAMP = YES -HTML_ALIGN_MEMBERS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. + HTML_DYNAMIC_SECTIONS = NO +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of +# entries shown in the various tree structured indices initially; the user +# can expand and collapse entries dynamically later on. Doxygen will expand +# the tree to such a level that at most the specified number of entries are +# visible (unless a fully collapsed tree already exceeds this amount). +# So setting the number of entries 1 will produce a full collapsed tree by +# default. 0 is a special value representing an infinite number of entries +# and will result in a full expanded tree by default. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + DOCSET_BUNDLE_ID = org.doxygen.Project +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely +# identify the documentation publisher. This should be a reverse domain-name +# style string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + TOC_EXPAND = NO +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# +# Qt Help Project / Custom Filters. + QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# +# Qt Help Project / Filter Attributes. + QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + QHG_LOCATION = +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) +# at top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. Since the tabs have the same information as the +# navigation tree you can set this option to NO if you already set +# GENERATE_TREEVIEW to YES. + DISABLE_INDEX = NO -ENUM_VALUES_PER_LINE = 4 + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. +# Since the tree basically has the same information as the tab index you +# could consider to set DISABLE_INDEX to NO when enabling this option. + GENERATE_TREEVIEW = NO -USE_INLINE_TREES = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values +# (range [0,1..20]) that doxygen will group on one line in the generated HTML +# documentation. Note that a value of 0 will completely suppress the enum +# values from appearing in the overview section. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you may also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = NO + +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to +# the MathJax Content Delivery Network so you can quickly see the result without +# installing MathJax. +# However, it is strongly recommended to install a local +# copy of MathJax from http://www.mathjax.org before deployment. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension +# names that should be enabled during MathJax rendering. + +MATHJAX_EXTENSIONS = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a PHP enabled web server instead of at the web client +# using Javascript. Doxygen will generate the search PHP script and index +# file to put on the web server. The advantage of the server +# based approach is that it scales better to large projects and allows +# full text search. The disadvantages are that it is more difficult to setup +# and does not have live searching capabilities. + SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + GENERATE_LATEX = YES + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, letter, legal and +# executive. If left blank a4wide will be used. + PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for +# the generated latex document. The footer should contain everything after +# the last chapter. If it is left blank doxygen will generate a +# standard footer. Notice: only use this tag if you know what you are doing! + +LATEX_FOOTER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + LATEX_SOURCE_CODE = NO +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See +# http://en.wikipedia.org/wiki/BibTeX for more info. + +LATEX_BIB_STYLE = plain + #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + RTF_HYPERLINKS = NO + +# Load style sheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# pointed to by INCLUDE_PATH will be searched when a #include is found. + SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition that +# overrules the definition found in the source code. + EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these will confuse the parser if not removed. + SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- +# The TAGFILES option can be used to specify one or more tagfiles. For each +# tag file the location of the external documentation should be added. The +# format of a tag file without this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths +# or URLs. Note that each tag file must have a unique name (where the name does +# NOT include the path). If a tag file is not located in the directory in which +# doxygen is run, you must also specify the path to the tagfile here. + TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- -CLASS_DIAGRAMS = YES -MSCGEN_PATH = -HIDE_UNDOC_RELATIONS = YES -HAVE_DOT = NO -DOT_FONTNAME = FreeSans -DOT_FONTSIZE = 10 -DOT_FONTPATH = -CLASS_GRAPH = YES -COLLABORATION_GRAPH = YES -GROUP_GRAPHS = YES -UML_LOOK = NO -TEMPLATE_RELATIONS = NO -INCLUDE_GRAPH = YES -INCLUDED_BY_GRAPH = YES -CALL_GRAPH = NO -CALLER_GRAPH = NO -GRAPHICAL_HIERARCHY = YES -DIRECTORY_GRAPH = YES -DOT_IMAGE_FORMAT = png -DOT_PATH = -DOTFILE_DIRS = -DOT_GRAPH_MAX_NODES = 50 -MAX_DOT_GRAPH_DEPTH = 0 -DOT_TRANSPARENT = NO -DOT_MULTI_TARGETS = NO -GENERATE_LEGEND = YES -DOT_CLEANUP = YES +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option also works with HAVE_DOT disabled, but it is recommended to +# install and use dot, since it yields more powerful graphs. +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will use the Helvetica font for all dot files that +# doxygen generates. When you want a differently looking font you can specify +# the font name using DOT_FONTNAME. You need to make sure dot is able to find +# the font, which can be done by putting it in a standard location or by setting +# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. + +DOT_FONTNAME = FreeSans + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the Helvetica font. +# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to +# set the path where dot can find it. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If the UML_LOOK tag is enabled, the fields and methods are shown inside +# the class node. If there are many fields or methods and many nodes the +# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS +# threshold limits the number of items for each type to make the size more +# managable. Set this to 0 for no limit. Note that the threshold may be +# exceeded by 50% before the limit is enforced. + +UML_LIMIT_NUM_FIELDS = 10 + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will generate a graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are svg, png, jpg, or gif. +# If left blank png will be used. If you choose svg you need to set +# HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible in IE 9+ (other browsers do not have this requirement). + +DOT_IMAGE_FORMAT = png + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# Note that this requires a modern browser other than Internet Explorer. +# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you +# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible. Older versions of IE do not have SVG support. + +INTERACTIVE_SVG = NO + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES From c35e1bbef6144e62535c0fc0c062dbe3d8144959 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Tue, 28 Aug 2012 17:08:53 +0200 Subject: [PATCH 273/788] flv: add duration metadata --- lib/flv_tag.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index 89694a3f..31ff4fd7 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -450,6 +450,9 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){ amfdata.addContent(AMF::Object("", "onMetaData")); amfdata.addContent(AMF::Object("", AMF::AMF0_ECMA_ARRAY)); + if (S.metadata.isMember("duration")){ + amfdata.getContentP(1)->addContent(AMF::Object("duration", S.metadata["duration"].asInt() / 1000, AMF::AMF0_NUMBER)); + } if (S.metadata.isMember("video")){ amfdata.getContentP(1)->addContent(AMF::Object("hasVideo", 1, AMF::AMF0_BOOL)); if (S.metadata["video"]["codec"].asString() == "H264"){ From 42875cd5442e532aec239123ccf1df7810474bf8 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Thu, 30 Aug 2012 11:16:49 +0200 Subject: [PATCH 274/788] Fix compile warnings Todo: error checking for DTSC::File::getHeader --- lib/config.cpp | 6 +++++- lib/dtsc.cpp | 7 ++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/config.cpp b/lib/config.cpp index 09361779..7f653a81 100644 --- a/lib/config.cpp +++ b/lib/config.cpp @@ -342,5 +342,9 @@ void Util::Daemonize(){ #if DEBUG >= 3 fprintf(stderr, "Going into background mode...\n"); #endif - daemon(1, 0); + if (daemon(1, 0) < 0) { + #if DEBUG >= 1 + fprintf(stderr, "Failed to daemonize: %s\n", strerror(errno)); + #endif + } } diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index e18d7817..44f00ebe 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -228,7 +228,12 @@ DTSC::File::File(std::string filename, bool create){ std::string & DTSC::File::getHeader(){ fseek(F, 8, SEEK_SET); strbuffer.resize(headerSize); - fread((void*)strbuffer.c_str(), headerSize, 1, F); + if (fread((void*)strbuffer.c_str(), headerSize, 1, F) != headerSize){ + /// \todo check seek as well and do something more sensible... + #if DEBUG >= 10 + fprintf(stderr, "Panic! Invalid DTSC File header\n"); + #endif + } fseek(F, 8+headerSize, SEEK_SET); return strbuffer; } From 31b73b9a7446f10b1e77e0754b9f426978585f9c Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 30 Aug 2012 11:19:46 +0200 Subject: [PATCH 275/788] Updated socket library to include socket simulation for filedescriptors. --- lib/socket.cpp | 64 ++++++++++++++++++++++++++++++++++++++++---------- lib/socket.h | 2 ++ 2 files changed, 54 insertions(+), 12 deletions(-) diff --git a/lib/socket.cpp b/lib/socket.cpp index b3eb1f4b..7e6daf02 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -22,6 +22,22 @@ std::string uint2string(unsigned int i){ /// \param sockNo Integer representing the socket to convert. Socket::Connection::Connection(int sockNo){ sock = sockNo; + pipes[0] = -1; + pipes[1] = -1; + up = 0; + down = 0; + conntime = time(0); + Error = false; + Blocking = false; +}//Socket::Connection basic constructor + +/// Simulate a socket using two file descriptors. +/// \param write The filedescriptor to write to. +/// \param read The filedescriptor to read from. +Socket::Connection::Connection(int write, int read){ + sock = -1; + pipes[0] = write; + pipes[1] = read; up = 0; down = 0; conntime = time(0); @@ -33,6 +49,8 @@ Socket::Connection::Connection(int sockNo){ /// A socket created like this is always disconnected and should/could be overwritten at some point. Socket::Connection::Connection(){ sock = -1; + pipes[0] = -1; + pipes[1] = -1; up = 0; down = 0; conntime = time(0); @@ -59,9 +77,21 @@ void Socket::Connection::close(){ #if DEBUG >= 6 fprintf(stderr, "Socket closed.\n"); #endif - shutdown(sock, SHUT_RDWR); - ::close(sock); - sock = -1; + if (sock != -1){ + shutdown(sock, SHUT_RDWR); + ::close(sock); + sock = -1; + } + if (pipes[0] != -1){ + shutdown(pipes[0], SHUT_RDWR); + ::close(pipes[0]); + pipes[0] = -1; + } + if (pipes[1] != -1){ + shutdown(pipes[1], SHUT_RDWR); + ::close(pipes[1]); + pipes[1] = -1; + } } }//Socket::Connection::close @@ -166,7 +196,7 @@ Socket::Connection::Connection(std::string host, int port, bool nonblock){ /// and when the socket is closed manually. /// \returns True if socket is connected, false otherwise. bool Socket::Connection::connected() const{ - return (sock >= 0); + return (sock >= 0) || ((pipes[0] >= 0) && (pipes[1] >= 0)); } /// Returns total amount of bytes sent. @@ -219,8 +249,13 @@ void Socket::Connection::Send(std::string data){ /// \param len Amount of bytes to write. /// \returns The amount of bytes actually written. int Socket::Connection::iwrite(const void * buffer, int len){ - if (sock < 0){return 0;} - int r = send(sock, buffer, len, 0); + if (!connected()){return 0;} + int r; + if (sock >= 0){ + r = send(sock, buffer, len, 0); + }else{ + r = write(pipes[0], buffer, len); + } if (r < 0){ switch (errno){ case EWOULDBLOCK: @@ -238,7 +273,7 @@ int Socket::Connection::iwrite(const void * buffer, int len){ break; } } - if (r == 0){ + if (r == 0 && (sock >= 0)){ close(); } up += r; @@ -251,8 +286,13 @@ int Socket::Connection::iwrite(const void * buffer, int len){ /// \param len Amount of bytes to read. /// \returns The amount of bytes actually read. int Socket::Connection::iread(void * buffer, int len){ - if (sock < 0){return 0;} - int r = recv(sock, buffer, len, 0); + if (!connected()){return 0;} + int r; + if (sock >= 0){ + r = recv(sock, buffer, len, 0); + }else{ + r = read(pipes[1], buffer, len); + } if (r < 0){ switch (errno){ case EWOULDBLOCK: @@ -270,7 +310,7 @@ int Socket::Connection::iread(void * buffer, int len){ break; } } - if (r == 0){ + if (r == 0 && (sock >= 0)){ close(); } down += r; @@ -317,13 +357,13 @@ void Socket::Connection::setHost(std::string host){ /// Returns true if these sockets are the same socket. /// Does not check the internal stats - only the socket itself. bool Socket::Connection::operator== (const Connection &B) const{ - return sock == B.sock; + return sock == B.sock && pipes[0] == B.pipes[0] && pipes[1] == B.pipes[1]; } /// Returns true if these sockets are not the same socket. /// Does not check the internal stats - only the socket itself. bool Socket::Connection::operator!= (const Connection &B) const{ - return sock != B.sock; + return sock != B.sock || pipes[0] != B.pipes[0] || pipes[1] != B.pipes[1]; } /// Returns true if the socket is valid. diff --git a/lib/socket.h b/lib/socket.h index db902c66..e4f2e69a 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -24,6 +24,7 @@ namespace Socket{ 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; @@ -42,6 +43,7 @@ namespace 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 setBlocking(bool blocking); ///< Set this socket to be blocking (true) or nonblocking (false). From 906e3803f75ad95899c5e29173efd9dd36355fcb Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 30 Aug 2012 11:28:36 +0200 Subject: [PATCH 276/788] Only sockets can be shut down. --- lib/socket.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/socket.cpp b/lib/socket.cpp index 7e6daf02..424460d4 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -83,12 +83,10 @@ void Socket::Connection::close(){ sock = -1; } if (pipes[0] != -1){ - shutdown(pipes[0], SHUT_RDWR); ::close(pipes[0]); pipes[0] = -1; } if (pipes[1] != -1){ - shutdown(pipes[1], SHUT_RDWR); ::close(pipes[1]); pipes[1] = -1; } From ba651ebc4c603a062443cefadbe0d3df01b3fdf1 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 30 Aug 2012 11:50:06 +0200 Subject: [PATCH 277/788] Get length instead of duration when converting to FLV metadata. --- lib/flv_tag.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index 31ff4fd7..3761c7b2 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -450,8 +450,8 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){ amfdata.addContent(AMF::Object("", "onMetaData")); amfdata.addContent(AMF::Object("", AMF::AMF0_ECMA_ARRAY)); - if (S.metadata.isMember("duration")){ - amfdata.getContentP(1)->addContent(AMF::Object("duration", S.metadata["duration"].asInt() / 1000, AMF::AMF0_NUMBER)); + if (S.metadata.isMember("length")){ + amfdata.getContentP(1)->addContent(AMF::Object("duration", S.metadata["length"].asInt(), AMF::AMF0_NUMBER)); } if (S.metadata.isMember("video")){ amfdata.getContentP(1)->addContent(AMF::Object("hasVideo", 1, AMF::AMF0_BOOL)); From 7b2deebfb2d0ca21149d105f730d332251b67440 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Thu, 30 Aug 2012 12:23:40 +0200 Subject: [PATCH 278/788] Move Socket::*Stream > Util::Stream::* --- lib/Makefile.am | 4 ++-- lib/socket.cpp | 40 ------------------------------- lib/socket.h | 6 ----- lib/stream.cpp | 62 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/stream.h | 23 ++++++++++++++++++ 5 files changed, 87 insertions(+), 48 deletions(-) create mode 100644 lib/stream.cpp create mode 100644 lib/stream.h diff --git a/lib/Makefile.am b/lib/Makefile.am index 2e9ff296..931fb9ad 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,5 +1,5 @@ lib_LTLIBRARIES=libmist-1.0.la -libmist_1_0_la_SOURCES=amf.h amf.cpp auth.h auth.cpp base64.h base64.cpp config.h config.cpp crypto.h crypto.cpp dtsc.h dtsc.cpp flv_tag.h flv_tag.cpp http_parser.h http_parser.cpp json.h json.cpp procs.h procs.cpp rtmpchunks.h rtmpchunks.cpp socket.h socket.cpp mp4.h mp4.cpp ftp.h ftp.cpp filesystem.h filesystem.cpp +libmist_1_0_la_SOURCES=amf.h amf.cpp auth.h auth.cpp base64.h base64.cpp config.h config.cpp crypto.h crypto.cpp dtsc.h dtsc.cpp flv_tag.h flv_tag.cpp http_parser.h http_parser.cpp json.h json.cpp procs.h procs.cpp rtmpchunks.h rtmpchunks.cpp socket.h socket.cpp mp4.h mp4.cpp ftp.h ftp.cpp filesystem.h filesystem.cpp stream.h stream.cpp libmist_1_0_la_LIBADD=-lssl -lcrypto libmist_1_0_la_LDFLAGS = -version-info 1:0:0 @@ -7,4 +7,4 @@ pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = mist-1.0.pc library_includedir=$(includedir)/mist-1.0/mist -library_include_HEADERS = amf.h auth.h base64.h config.h crypto.h dtsc.h flv_tag.h http_parser.h json.h procs.h rtmpchunks.h socket.h mp4.h ftp.h filesystem.h +library_include_HEADERS = amf.h auth.h base64.h config.h crypto.h dtsc.h flv_tag.h http_parser.h json.h procs.h rtmpchunks.h socket.h mp4.h ftp.h filesystem.h stream.h diff --git a/lib/socket.cpp b/lib/socket.cpp index 424460d4..55c60d5b 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -609,43 +609,3 @@ bool Socket::Server::connected() const{ /// Returns internal socket number. int Socket::Server::getSocket(){return sock;} - -/// Connect to a stream on the system. -/// Filters the streamname, removing invalid characters and -/// converting all letters to lowercase. -/// If a '?' character is found, everything following that character is deleted. -Socket::Connection Socket::getStream(std::string streamname){ - //strip anything that isn't a number, alpha or underscore - for (std::string::iterator i=streamname.end()-1; i>=streamname.begin(); --i){ - if (*i == '?'){streamname.erase(i, streamname.end()); break;} - if (!isalpha(*i) && !isdigit(*i) && *i != '_'){ - streamname.erase(i); - }else{ - *i=tolower(*i); - } - } - return Socket::Connection("/tmp/mist/stream_"+streamname); -} - -/// Create a stream on the system. -/// Filters the streamname, removing invalid characters and -/// converting all letters to lowercase. -/// If a '?' character is found, everything following that character is deleted. -/// If the /tmp/mist directory doesn't exist yet, this will create it. -Socket::Server Socket::makeStream(std::string streamname){ - //strip anything that isn't numbers, digits or underscores - for (std::string::iterator i=streamname.end()-1; i>=streamname.begin(); --i){ - if (*i == '?'){streamname.erase(i, streamname.end()); break;} - if (!isalpha(*i) && !isdigit(*i) && *i != '_'){ - streamname.erase(i); - }else{ - *i=tolower(*i); - } - } - std::string loc = "/tmp/mist/stream_"+streamname; - //attempt to create the /tmp/mist directory if it doesn't exist already. - //ignore errors - we catch all problems in the Socket::Server creation already - mkdir("/tmp/mist", S_IRWXU | S_IRWXG | S_IRWXO); - //create and return the Socket::Server - return Socket::Server(loc); -} diff --git a/lib/socket.h b/lib/socket.h index e4f2e69a..bef03fca 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -85,11 +85,5 @@ namespace Socket{ void close(); ///< Close connection. int getSocket(); ///< Returns internal socket number. }; - - /// Connect to a stream on the system. - Connection getStream(std::string streamname); - - /// Create a stream on the system. - Server makeStream(std::string streamname); }; diff --git a/lib/stream.cpp b/lib/stream.cpp new file mode 100644 index 00000000..2e5072a3 --- /dev/null +++ b/lib/stream.cpp @@ -0,0 +1,62 @@ +/// \file stream.cpp +/// Utilities for handling streams. + +#include "stream.h" +#include "procs.h" +#include "socket.h" + +/// 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. +void Util::Stream::sanitizeName(std::string & streamname){ + //strip anything that isn't numbers, digits or underscores + for (std::string::iterator i=streamname.end()-1; i>=streamname.begin(); --i){ + if (*i == '?'){streamname.erase(i, streamname.end()); break;} + if (!isalpha(*i) && !isdigit(*i) && *i != '_'){ + streamname.erase(i); + }else{ + *i=tolower(*i); + } + } +} + +Socket::Connection Util::Stream::getLive(std::string streamname){ + sanitizeName(streamname); + return Socket::Connection("/tmp/mist/stream_"+streamname); +} + +/// Starts a process for the VoD stream. +Socket::Connection Util::Stream::getVod(std::string streamname){ + sanitizeName(streamname); + std::string filename = "/tmp/mist/vod_" + streamname; + /// \todo Is the name unique enough? + std::string name = "MistPlayer " + filename; + const char *argv[] = { "MistPlayer", filename.c_str(), NULL }; + int fdin = -1, fdout = -1; + Util::Procs::StartPiped(name, (char **)argv, &fdin, &fdout, 0); + // if StartPiped fails then fdin and fdout will be unmodified (-1) + return Socket::Connection(fdin, fdout); +} + +/// Probe for available streams. Currently first VoD, then Live. +Socket::Connection Util::Stream::getStream(std::string streamname){ + Socket::Connection vod = getVod(streamname); + if (vod.connected()){ + return vod; + } + return getLive(streamname); +} +/// Create a stream on the system. +/// Filters the streamname, removing invalid characters and +/// converting all letters to lowercase. +/// If a '?' character is found, everything following that character is deleted. +/// If the /tmp/mist directory doesn't exist yet, this will create it. +Socket::Server Util::Stream::makeLive(std::string streamname){ + sanitizeName(streamname); + std::string loc = "/tmp/mist/stream_"+streamname; + //attempt to create the /tmp/mist directory if it doesn't exist already. + //ignore errors - we catch all problems in the Socket::Server creation already + mkdir("/tmp/mist", S_IRWXU | S_IRWXG | S_IRWXO); + //create and return the Socket::Server + return Socket::Server(loc); +} diff --git a/lib/stream.h b/lib/stream.h new file mode 100644 index 00000000..e970c848 --- /dev/null +++ b/lib/stream.h @@ -0,0 +1,23 @@ +/// \file stream.h +/// Utilities for handling streams. + +#pragma once +#include +#include + +namespace Util{ + class Stream{ + /// Sanitize a streamname. + void sanitizeName(std::string & streamname); + public: + /// Get a connection to a Live stream. + static Socket::Connection getLive(std::string streamname); + /// Get a connection to a VoD stream. + static Socket::Connection getVod(std::string streamname); + /// Probe for available streams. Currently first VoD, then Live. + static Socket::Connection getStream(std::string streamname); + + /// Create a Live stream on the system. + static Socket::Server makeLive(std::string streamname); + }; +} From 0aaa98b73ee71d2a542bcb58d9d8144366b5bf16 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Thu, 30 Aug 2012 12:30:11 +0200 Subject: [PATCH 279/788] stream: fix headers and missing static keyword --- lib/stream.cpp | 2 ++ lib/stream.h | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/stream.cpp b/lib/stream.cpp index 2e5072a3..cd2d563e 100644 --- a/lib/stream.cpp +++ b/lib/stream.cpp @@ -1,6 +1,8 @@ /// \file stream.cpp /// Utilities for handling streams. +#include +#include #include "stream.h" #include "procs.h" #include "socket.h" diff --git a/lib/stream.h b/lib/stream.h index e970c848..56650c3b 100644 --- a/lib/stream.h +++ b/lib/stream.h @@ -3,12 +3,12 @@ #pragma once #include -#include +#include "socket.h" namespace Util{ class Stream{ /// Sanitize a streamname. - void sanitizeName(std::string & streamname); + static void sanitizeName(std::string & streamname); public: /// Get a connection to a Live stream. static Socket::Connection getLive(std::string streamname); From 803e53cb9d99a815e52b046a222cbaade6e3c04f Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 31 Aug 2012 10:38:53 +0200 Subject: [PATCH 280/788] Made FLV metadata more like most FLV files. --- lib/flv_tag.cpp | 49 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index 3761c7b2..a025563b 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -452,11 +452,12 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){ amfdata.addContent(AMF::Object("", AMF::AMF0_ECMA_ARRAY)); if (S.metadata.isMember("length")){ amfdata.getContentP(1)->addContent(AMF::Object("duration", S.metadata["length"].asInt(), AMF::AMF0_NUMBER)); + amfdata.getContentP(1)->addContent(AMF::Object("moovPosition", 40, AMF::AMF0_NUMBER)); } if (S.metadata.isMember("video")){ amfdata.getContentP(1)->addContent(AMF::Object("hasVideo", 1, AMF::AMF0_BOOL)); if (S.metadata["video"]["codec"].asString() == "H264"){ - amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", 7, AMF::AMF0_NUMBER)); + amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", (std::string)"avc1")); } if (S.metadata["video"]["codec"].asString() == "VP6"){ amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", 4, AMF::AMF0_NUMBER)); @@ -471,7 +472,7 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){ amfdata.getContentP(1)->addContent(AMF::Object("height", S.metadata["video"]["height"].asInt(), AMF::AMF0_NUMBER)); } if (S.metadata["video"].isMember("fpks")){ - amfdata.getContentP(1)->addContent(AMF::Object("framerate", (double)S.metadata["video"]["fpks"].asInt() / 1000.0, AMF::AMF0_NUMBER)); + amfdata.getContentP(1)->addContent(AMF::Object("videoframerate", (double)S.metadata["video"]["fpks"].asInt() / 1000.0, AMF::AMF0_NUMBER)); } if (S.metadata["video"].isMember("bps")){ amfdata.getContentP(1)->addContent(AMF::Object("videodatarate", ((double)S.metadata["video"]["bps"].asInt() * 8.0) / 1024.0, AMF::AMF0_NUMBER)); @@ -481,17 +482,13 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){ amfdata.getContentP(1)->addContent(AMF::Object("hasAudio", 1, AMF::AMF0_BOOL)); amfdata.getContentP(1)->addContent(AMF::Object("audiodelay", 0, AMF::AMF0_NUMBER)); if (S.metadata["audio"]["codec"].asString() == "AAC"){ - amfdata.getContentP(1)->addContent(AMF::Object("audiocodecid", 10, AMF::AMF0_NUMBER)); + amfdata.getContentP(1)->addContent(AMF::Object("audiocodecid", (std::string)"mp4a")); } if (S.metadata["audio"]["codec"].asString() == "MP3"){ - amfdata.getContentP(1)->addContent(AMF::Object("audiocodecid", 2, AMF::AMF0_NUMBER)); + amfdata.getContentP(1)->addContent(AMF::Object("audiocodecid", (std::string)"mp3")); } if (S.metadata["audio"].isMember("channels")){ - if (S.metadata["audio"]["channels"].asInt() > 1){ - amfdata.getContentP(1)->addContent(AMF::Object("stereo", 1, AMF::AMF0_BOOL)); - }else{ - amfdata.getContentP(1)->addContent(AMF::Object("stereo", 0, AMF::AMF0_BOOL)); - } + amfdata.getContentP(1)->addContent(AMF::Object("audiochannels", S.metadata["audio"]["channels"].asInt(), AMF::AMF0_NUMBER)); } if (S.metadata["audio"].isMember("rate")){ amfdata.getContentP(1)->addContent(AMF::Object("audiosamplerate", S.metadata["audio"]["rate"].asInt(), AMF::AMF0_NUMBER)); @@ -503,7 +500,39 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){ amfdata.getContentP(1)->addContent(AMF::Object("audiodatarate", ((double)S.metadata["audio"]["bps"].asInt() * 8.0) / 1024.0, AMF::AMF0_NUMBER)); } } - + AMF::Object trinfo = AMF::Object("trackinfo", AMF::AMF0_STRICT_ARRAY); + int i = 0; + if (S.metadata.isMember("audio")){ + trinfo.addContent(AMF::Object("", AMF::AMF0_OBJECT)); + trinfo.getContentP(i)->addContent(AMF::Object("length", ((double)S.metadata["length"].asInt()) * ((double)S.metadata["audio"]["rate"].asInt()), AMF::AMF0_NUMBER)); + trinfo.getContentP(i)->addContent(AMF::Object("timescale", S.metadata["audio"]["rate"].asInt(), AMF::AMF0_NUMBER)); + trinfo.getContentP(i)->addContent(AMF::Object("sampledescription", AMF::AMF0_STRICT_ARRAY)); + if (S.metadata["audio"]["codec"].asString() == "AAC"){ + trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string)"mp4a")); + } + if (S.metadata["audio"]["codec"].asString() == "MP3"){ + trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string)"mp3")); + } + ++i; + } + if (S.metadata.isMember("video")){ + trinfo.addContent(AMF::Object("", AMF::AMF0_OBJECT)); + trinfo.getContentP(i)->addContent(AMF::Object("length", ((double)S.metadata["length"].asInt()) * ((double)S.metadata["video"]["fkps"].asInt() / 1000.0), AMF::AMF0_NUMBER)); + trinfo.getContentP(i)->addContent(AMF::Object("timescale", ((double)S.metadata["video"]["fkps"].asInt() / 1000.0), AMF::AMF0_NUMBER)); + trinfo.getContentP(i)->addContent(AMF::Object("sampledescription", AMF::AMF0_STRICT_ARRAY)); + if (S.metadata["video"]["codec"].asString() == "H264"){ + trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string)"avc1")); + } + if (S.metadata["video"]["codec"].asString() == "VP6"){ + trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string)"vp6")); + } + if (S.metadata["video"]["codec"].asString() == "H263"){ + trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string)"h263")); + } + ++i; + } + amfdata.getContentP(1)->addContent(trinfo); + std::string tmp = amfdata.Pack(); len = tmp.length() + 15; if (len > 0){ From 88e955683fd63885aca46fb60012df199e9f515c Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 31 Aug 2012 10:39:24 +0200 Subject: [PATCH 281/788] Implement decent seeking in DTSC::File. --- lib/dtsc.cpp | 48 +++++++++++++++++++++++++++++++++++++++++------- lib/dtsc.h | 3 +++ 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 44f00ebe..bd7755e9 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -221,20 +221,24 @@ DTSC::File::File(std::string filename, bool create){ headerSize = ntohl(ubuffer[0]); } fseek(F, 8+headerSize, SEEK_SET); + currframe = 1; + frames[currframe] = ftell(F); } /// Returns the header metadata for this file as a std::string. /// Sets the file pointer to the first packet. std::string & DTSC::File::getHeader(){ - fseek(F, 8, SEEK_SET); + if (fseek(F, 8, SEEK_SET) != 0){ + strbuffer = ""; + return strbuffer; + } strbuffer.resize(headerSize); - if (fread((void*)strbuffer.c_str(), headerSize, 1, F) != headerSize){ - /// \todo check seek as well and do something more sensible... - #if DEBUG >= 10 - fprintf(stderr, "Panic! Invalid DTSC File header\n"); - #endif + if (fread((void*)strbuffer.c_str(), headerSize, 1, F) != 1){ + strbuffer = ""; + return strbuffer; } fseek(F, 8+headerSize, SEEK_SET); + currframe = 1; return strbuffer; } @@ -254,6 +258,7 @@ bool DTSC::File::writeHeader(std::string & header, bool force){ /// Reads the packet available at the current file position, returning it as a std::string. /// If the packet could not be read for any reason, the reason is printed to stderr and an empty string returned. +/// Reading the packet means the file position is increased to the next packet. std::string & DTSC::File::getPacket(){ if (fread(buffer, 4, 1, F) != 1){ fprintf(stderr, "Could not read header\n"); @@ -261,7 +266,7 @@ std::string & DTSC::File::getPacket(){ return strbuffer; } if (memcmp(buffer, DTSC::Magic_Packet, 4) != 0){ - fprintf(stderr, "Could not overwrite header - not equal size\n"); + fprintf(stderr, "Invalid header\n"); strbuffer = ""; return strbuffer; } @@ -278,9 +283,38 @@ std::string & DTSC::File::getPacket(){ strbuffer = ""; return strbuffer; } + currframe++; + frames[currframe] = ftell(F); return strbuffer; } +/// Attempts to seek to the given frame number within the file. +/// Returns true if successful, false otherwise. +bool DTSC::File::seek_frame(int frameno){ + std::map::iterator it = frames.lower_bound(frameno); + if (it->first == frameno){ + if (fseek(F, it->second, SEEK_SET) == 0){ + currframe = frameno; + return true; + } + }else{ + if (fseek(F, it->second, SEEK_SET) == 0){ + currframe = it->first; + while (currframe < frameno){ + if (fread(buffer, 4, 1, F) != 1){return false;}//read header + if (memcmp(buffer, DTSC::Magic_Packet, 4) != 0){return false;}//check header + if (fread(buffer, 4, 1, F) != 1){return false;}//read size + uint32_t * ubuffer = (uint32_t *)buffer; + long packSize = ntohl(ubuffer[0]); + if (fseek(F, packSize, SEEK_CUR) != 0){return false;}//seek to next packet + currframe++; + } + return true; + } + } + return false; +} + /// Close the file if open DTSC::File::~File(){ if (F){ diff --git a/lib/dtsc.h b/lib/dtsc.h index 2a6ca87d..473f0e59 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -70,8 +70,11 @@ namespace DTSC{ std::string & getHeader(); bool writeHeader(std::string & header, bool force = false); std::string & getPacket(); + bool seek_frame(int frameno); private: std::string strbuffer; + std::map frames; + int currframe; FILE * F; unsigned long headerSize; char buffer[4]; From 20c460a081e5bda6516a8a9ca56d9142ed014a1f Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Fri, 31 Aug 2012 13:10:04 +0200 Subject: [PATCH 282/788] Do not prevent user from overriding CXXFLAGS make CXXFLAGS=-O0 would not work without this. --- configure.ac | 5 +++-- lib/Makefile.am | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index d1c5047a..5a8dd7c9 100644 --- a/configure.ac +++ b/configure.ac @@ -38,10 +38,11 @@ AC_FUNC_REALLOC AC_CHECK_FUNCS([dup2 gettimeofday memset mkdir socket strerror]) # Fix chars to unsigned -CXXFLAGS="$CXXFLAGS -funsigned-char" +AC_SUBST([global_CFLAGS], [-funsigned-char]) #allow verbose mode compiles -AC_ARG_ENABLE([verbose], AC_HELP_STRING([--enable-verbose], [Compile with verbose messages]), CXXFLAGS="-DDEBUG=4 $CXXFLAGS") +AC_ARG_ENABLE([verbose], AC_HELP_STRING([--enable-verbose], [Compile with verbose messages]), + AC_DEFINE([DEBUG], [4])) AC_CONFIG_FILES([Makefile lib/Makefile lib/mist-1.0.pc]) AC_OUTPUT diff --git a/lib/Makefile.am b/lib/Makefile.am index 931fb9ad..a5dcbcf4 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,3 +1,5 @@ +AM_CPPFLAGS = $(global_CFLAGS) + lib_LTLIBRARIES=libmist-1.0.la libmist_1_0_la_SOURCES=amf.h amf.cpp auth.h auth.cpp base64.h base64.cpp config.h config.cpp crypto.h crypto.cpp dtsc.h dtsc.cpp flv_tag.h flv_tag.cpp http_parser.h http_parser.cpp json.h json.cpp procs.h procs.cpp rtmpchunks.h rtmpchunks.cpp socket.h socket.cpp mp4.h mp4.cpp ftp.h ftp.cpp filesystem.h filesystem.cpp stream.h stream.cpp libmist_1_0_la_LIBADD=-lssl -lcrypto From 63a25cf9ea1958842a7755f56bb4c9203e10acc7 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 31 Aug 2012 16:52:57 +0200 Subject: [PATCH 283/788] Improved seeking in DTSC::File, also, now tested working. --- lib/dtsc.cpp | 94 ++++++++++++++++++++++++++++++++++++++-------------- lib/dtsc.h | 8 ++++- 2 files changed, 77 insertions(+), 25 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index bd7755e9..d021c68d 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -222,7 +222,8 @@ DTSC::File::File(std::string filename, bool create){ } fseek(F, 8+headerSize, SEEK_SET); currframe = 1; - frames[currframe] = ftell(F); + frames[1] = 8+headerSize; + msframes[1] = 0; } /// Returns the header metadata for this file as a std::string. @@ -256,24 +257,27 @@ bool DTSC::File::writeHeader(std::string & header, bool force){ return (ret == 1); } -/// Reads the packet available at the current file position, returning it as a std::string. -/// If the packet could not be read for any reason, the reason is printed to stderr and an empty string returned. +/// Reads the packet available at the current file position. +/// If the packet could not be read for any reason, the reason is printed to stderr. /// Reading the packet means the file position is increased to the next packet. -std::string & DTSC::File::getPacket(){ +void DTSC::File::seekNext(){ if (fread(buffer, 4, 1, F) != 1){ fprintf(stderr, "Could not read header\n"); strbuffer = ""; - return strbuffer; + jsonbuffer.null(); + return; } if (memcmp(buffer, DTSC::Magic_Packet, 4) != 0){ - fprintf(stderr, "Invalid header\n"); + fprintf(stderr, "Invalid header - %.4s != %.4s\n", buffer, DTSC::Magic_Packet); strbuffer = ""; - return strbuffer; + jsonbuffer.null(); + return; } if (fread(buffer, 4, 1, F) != 1){ fprintf(stderr, "Could not read size\n"); strbuffer = ""; - return strbuffer; + jsonbuffer.null(); + return; } uint32_t * ubuffer = (uint32_t *)buffer; long packSize = ntohl(ubuffer[0]); @@ -281,33 +285,49 @@ std::string & DTSC::File::getPacket(){ if (fread((void*)strbuffer.c_str(), packSize, 1, F) != 1){ fprintf(stderr, "Could not read packet\n"); strbuffer = ""; - return strbuffer; + jsonbuffer.null(); + return; + } + jsonbuffer = JSON::fromDTMI(strbuffer); + if (jsonbuffer.isMember("keyframe")){ + long pos = ftell(F) - (packSize + 8); + if (frames[currframe] != pos){ + currframe++; + currtime = jsonbuffer["time"].asInt(); + #if DEBUG >= 4 + std::cerr << "Found a new frame " << currframe << " @ " << pos << "b/" << currtime << "s" << std::endl; + #endif + frames[currframe] = pos; + msframes[currframe] = currtime; + } } - currframe++; - frames[currframe] = ftell(F); - return strbuffer; } +/// Returns the internal buffer of the last read packet in raw binary format. +std::string & DTSC::File::getPacket(){return strbuffer;} + +/// Returns the internal buffer of the last read packet in JSON format. +JSON::Value & DTSC::File::getJSON(){return jsonbuffer;} + /// Attempts to seek to the given frame number within the file. /// Returns true if successful, false otherwise. bool DTSC::File::seek_frame(int frameno){ - std::map::iterator it = frames.lower_bound(frameno); - if (it->first == frameno){ - if (fseek(F, it->second, SEEK_SET) == 0){ + if (frames.count(frameno) > 0){ + if (fseek(F, frames[frameno], SEEK_SET) == 0){ currframe = frameno; return true; } }else{ - if (fseek(F, it->second, SEEK_SET) == 0){ - currframe = it->first; + for (int i = frameno; i >= 1; --i){ + if (frames.count(i) > 0){currframe = i; break;} + } + if (fseek(F, frames[currframe], SEEK_SET) == 0){ + #if DEBUG >= 4 + std::cerr << "Seeking from frame " << currframe << " @ " << frames[currframe] << " to " << frameno << std::endl; + #endif while (currframe < frameno){ - if (fread(buffer, 4, 1, F) != 1){return false;}//read header - if (memcmp(buffer, DTSC::Magic_Packet, 4) != 0){return false;}//check header - if (fread(buffer, 4, 1, F) != 1){return false;}//read size - uint32_t * ubuffer = (uint32_t *)buffer; - long packSize = ntohl(ubuffer[0]); - if (fseek(F, packSize, SEEK_CUR) != 0){return false;}//seek to next packet - currframe++; + seekNext(); + if (jsonbuffer.isNull()){return false;} } return true; } @@ -315,6 +335,32 @@ bool DTSC::File::seek_frame(int frameno){ return false; } +/// Attempts to seek to the given time in ms within the file. +/// Returns true if successful, false otherwise. +bool DTSC::File::seek_time(int ms){ + std::map::iterator it; + currtime = 0; + currframe = 1; + for (it = msframes.begin(); it != msframes.end(); ++it){ + if (it->second > ms){break;} + if (it->second > currtime){currtime = it->second; currframe = it->first;} + } + if (fseek(F, frames[currframe], SEEK_SET) == 0){ + #if DEBUG >= 4 + std::cerr << "Seeking from frame " << currframe << " @ " << msframes[currframe] << "ms to " << ms << "ms" << std::endl; + #endif + while (currtime < ms){ + seekNext(); + if (jsonbuffer.isNull()){return false;} + } + if (currtime > ms){ + return seek_frame(currframe - 1); + } + return true; + } + return false; +} + /// Close the file if open DTSC::File::~File(){ if (F){ diff --git a/lib/dtsc.h b/lib/dtsc.h index 473f0e59..ff591227 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -69,11 +69,17 @@ namespace DTSC{ ~File(); std::string & getHeader(); bool writeHeader(std::string & header, bool force = false); + void seekNext(); std::string & getPacket(); + JSON::Value & getJSON(); bool seek_frame(int frameno); - private: + bool seek_time(int seconds); + private: std::string strbuffer; + JSON::Value jsonbuffer; std::map frames; + std::map msframes; + long long int currtime; int currframe; FILE * F; unsigned long headerSize; From a0bcc4779e7088d59e71a28a346cb3b364489d43 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 31 Aug 2012 16:53:46 +0200 Subject: [PATCH 284/788] Improvements to HTTP::Parser - now builds responses and requests by reference and has getUrl() method to get the URL without URI-parameters. --- lib/http_parser.cpp | 32 ++++++++++++++++++++------------ lib/http_parser.h | 6 ++++-- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/lib/http_parser.cpp b/lib/http_parser.cpp index 2047be6d..526ff014 100644 --- a/lib/http_parser.cpp +++ b/lib/http_parser.cpp @@ -24,18 +24,18 @@ void HTTP::Parser::Clean(){ /// 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(){ +std::string & HTTP::Parser::BuildRequest(){ /// \todo Include GET/POST variable parsing? std::map::iterator it; if (protocol.size() < 5 || protocol.substr(0, 4) != "HTTP"){protocol = "HTTP/1.0";} - std::string tmp = method+" "+url+" "+protocol+"\n"; + builder = method+" "+url+" "+protocol+"\n"; for (it=headers.begin(); it != headers.end(); it++){ if ((*it).first != "" && (*it).second != ""){ - tmp += (*it).first + ": " + (*it).second + "\n"; + builder += (*it).first + ": " + (*it).second + "\n"; } } - tmp += "\n" + body; - return tmp; + builder += "\n" + body; + return builder; } /// Returns a string containing a valid HTTP 1.0 or 1.1 response, ready for sending. @@ -44,21 +44,21 @@ std::string HTTP::Parser::BuildRequest(){ /// \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){ +std::string & HTTP::Parser::BuildResponse(std::string code, std::string message){ /// \todo Include GET/POST variable parsing? std::map::iterator it; if (protocol.size() < 5 || protocol.substr(0, 4) != "HTTP"){protocol = "HTTP/1.0";} - std::string tmp = protocol+" "+code+" "+message+"\n"; + builder = protocol+" "+code+" "+message+"\n"; for (it=headers.begin(); it != headers.end(); it++){ if ((*it).first != "" && (*it).second != ""){ if ((*it).first != "Content-Length" || (*it).second != "0"){ - tmp += (*it).first + ": " + (*it).second + "\n"; + builder += (*it).first + ": " + (*it).second + "\n"; } } } - tmp += "\n"; - tmp += body; - return tmp; + builder += "\n"; + builder += body; + return builder; } /// Trims any whitespace at the front or back of the string. @@ -86,6 +86,15 @@ void HTTP::Parser::SetBody(char * buffer, int 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. @@ -150,7 +159,6 @@ bool HTTP::Parser::parse(std::string & HTTPbuffer){ protocol = tmpA; if (url.find('?') != std::string::npos){ parseVars(url.substr(url.find('?')+1)); //parse GET variables - url.resize(url.find('?')); } }else{seenReq = false;} }else{seenReq = false;} diff --git a/lib/http_parser.h b/lib/http_parser.h index 609e7d28..ae235a2e 100644 --- a/lib/http_parser.h +++ b/lib/http_parser.h @@ -16,13 +16,14 @@ namespace HTTP{ 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 code, std::string message); + std::string & BuildRequest(); + std::string & BuildResponse(std::string code, std::string message); void Chunkify(std::string & bodypart); void Clean(); static std::string urlunescape(const std::string & in); @@ -37,6 +38,7 @@ namespace HTTP{ bool seenReq; bool parse(std::string & HTTPbuffer); void parseVars(std::string data); + std::string builder; std::string read_buffer; std::map headers; std::map vars; From 6d3598eea00563121295ae22099e7fcf27036d1b Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 31 Aug 2012 16:54:54 +0200 Subject: [PATCH 285/788] Overloaded Socket::Connection::Send with new versions and now requires arguments to be passed by reference. Also fixed a ::close bug and hugely improved performance. --- lib/socket.cpp | 88 +++++++++++++++++++++++++++++++++++++++++++------- lib/socket.h | 4 ++- 2 files changed, 80 insertions(+), 12 deletions(-) diff --git a/lib/socket.cpp b/lib/socket.cpp index 55c60d5b..d94545b6 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -58,16 +58,22 @@ Socket::Connection::Connection(){ Blocking = false; }//Socket::Connection basic constructor - -/// Set this socket to be blocking (true) or nonblocking (false). -void Socket::Connection::setBlocking(bool blocking){ - int flags = fcntl(sock, F_GETFL, 0); +/// Internally used call to make an file descriptor blocking or not. +void setFDBlocking(int FD, bool blocking){ + int flags = fcntl(FD, F_GETFL, 0); if (!blocking){ flags |= O_NONBLOCK; }else{ flags &= !O_NONBLOCK; } - fcntl(sock, F_SETFL, flags); + fcntl(FD, F_SETFL, flags); +} + +/// Set this socket to be blocking (true) or nonblocking (false). +void Socket::Connection::setBlocking(bool blocking){ + if (sock >=0){setFDBlocking(sock, blocking);} + if (pipes[0] >=0){setFDBlocking(pipes[0], blocking);} + if (pipes[1] >=0){setFDBlocking(pipes[1], blocking);} } /// Close connection. The internal socket is closed and then set to -1. @@ -79,15 +85,18 @@ void Socket::Connection::close(){ #endif if (sock != -1){ shutdown(sock, SHUT_RDWR); - ::close(sock); + errno = EINTR; + while (::close(sock) != 0 && errno == EINTR){} sock = -1; } if (pipes[0] != -1){ - ::close(pipes[0]); + errno = EINTR; + while (::close(pipes[0]) != 0 && errno == EINTR){} pipes[0] = -1; } if (pipes[1] != -1){ - ::close(pipes[1]); + errno = EINTR; + while (::close(pipes[1]) != 0 && errno == EINTR){} pipes[1] = -1; } } @@ -237,8 +246,64 @@ std::string & Socket::Connection::Received(){ } /// Appends data to the upbuffer. -void Socket::Connection::Send(std::string data){ - upbuffer.append(data); +/// This will attempt to send the upbuffer (if non-empty) first. +/// If the upbuffer is empty before or after this attempt, it will attempt to send +/// the data right away. Any data that could not be send will be put into the upbuffer. +/// This means this function is blocking if the socket is, but nonblocking otherwise. +void Socket::Connection::Send(std::string & data){ + if (upbuffer.size() > 0){ + iwrite(upbuffer); + if (upbuffer.size() > 0){ + upbuffer.append(data); + } + } + if (upbuffer.size() == 0){ + int i = iwrite(data.c_str(), data.size()); + if (i < data.size()){ + upbuffer.append(data, i, data.size() - i); + } + } +} + +/// Appends data to the upbuffer. +/// This will attempt to send the upbuffer (if non-empty) first. +/// If the upbuffer is empty before or after this attempt, it will attempt to send +/// the data right away. Any data that could not be send will be put into the upbuffer. +/// This means this function is blocking if the socket is, but nonblocking otherwise. +void Socket::Connection::Send(const char * data){ + int len = strlen(data); + if (upbuffer.size() > 0){ + iwrite(upbuffer); + if (upbuffer.size() > 0){ + upbuffer.append(data, (size_t)len); + } + } + if (upbuffer.size() == 0){ + int i = iwrite(data, len); + if (i < len){ + upbuffer.append(data + i, (size_t)(len - i)); + } + } +} + +/// Appends data to the upbuffer. +/// This will attempt to send the upbuffer (if non-empty) first. +/// If the upbuffer is empty before or after this attempt, it will attempt to send +/// the data right away. Any data that could not be send will be put into the upbuffer. +/// This means this function is blocking if the socket is, but nonblocking otherwise. +void Socket::Connection::Send(const char * data, size_t len){ + if (upbuffer.size() > 0){ + iwrite(upbuffer); + if (upbuffer.size() > 0){ + upbuffer.append(data, len); + } + } + if (upbuffer.size() == 0){ + int i = iwrite(data, len); + if (i < len){ + upbuffer.append(data + i, len - i); + } + } } /// Incremental write call. This function tries to write len bytes to the socket from the buffer, @@ -593,7 +658,8 @@ void Socket::Server::close(){ fprintf(stderr, "ServerSocket closed.\n"); #endif shutdown(sock, SHUT_RDWR); - ::close(sock); + errno = EINTR; + while (::close(sock) != 0 && errno == EINTR){} sock = -1; } }//Socket::Server::close diff --git a/lib/socket.h b/lib/socket.h index bef03fca..273a6433 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -56,7 +56,9 @@ namespace Socket{ bool spool(); ///< Updates the downbuffer and upbuffer internal variables. bool flush(); ///< Updates the downbuffer and upbuffer internal variables until upbuffer is empty. std::string & Received(); ///< Returns a reference to the download buffer. - void Send(std::string data); ///< Appends data to the upbuffer. + void Send(std::string & data); ///< Appends data to the upbuffer. + void Send(const char * data); ///< Appends data to the upbuffer. + void Send(const char * data, size_t len); ///< Appends data to the upbuffer. //stats related methods unsigned int dataUp(); ///< Returns total amount of bytes sent. unsigned int dataDown(); ///< Returns total amount of bytes received. From 3ef44546a878eb02f4855a46812319fa1da47714 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 31 Aug 2012 16:55:28 +0200 Subject: [PATCH 286/788] Utils::Stream::getStream now actually works in all cases where it should, the way it should. ^_^ --- lib/stream.cpp | 40 ++++++++++++++++++++++++++++------------ lib/stream.h | 18 ++++++------------ 2 files changed, 34 insertions(+), 24 deletions(-) diff --git a/lib/stream.cpp b/lib/stream.cpp index cd2d563e..a091325c 100644 --- a/lib/stream.cpp +++ b/lib/stream.cpp @@ -1,8 +1,13 @@ /// \file stream.cpp /// Utilities for handling streams. +#if DEBUG >= 4 +#include +#endif + #include #include +#include "json.h" #include "stream.h" #include "procs.h" #include "socket.h" @@ -23,31 +28,42 @@ void Util::Stream::sanitizeName(std::string & streamname){ } Socket::Connection Util::Stream::getLive(std::string streamname){ - sanitizeName(streamname); return Socket::Connection("/tmp/mist/stream_"+streamname); } -/// Starts a process for the VoD stream. -Socket::Connection Util::Stream::getVod(std::string streamname){ - sanitizeName(streamname); - std::string filename = "/tmp/mist/vod_" + streamname; - /// \todo Is the name unique enough? +/// Starts a process for a VoD stream. +Socket::Connection Util::Stream::getVod(std::string filename){ std::string name = "MistPlayer " + filename; const char *argv[] = { "MistPlayer", filename.c_str(), NULL }; - int fdin = -1, fdout = -1; - Util::Procs::StartPiped(name, (char **)argv, &fdin, &fdout, 0); + int fdin = -1, fdout = -1, fderr = fileno(stderr); + Util::Procs::StartPiped(name, (char **)argv, &fdin, &fdout, &fderr); // if StartPiped fails then fdin and fdout will be unmodified (-1) return Socket::Connection(fdin, fdout); } /// Probe for available streams. Currently first VoD, then Live. Socket::Connection Util::Stream::getStream(std::string streamname){ - Socket::Connection vod = getVod(streamname); - if (vod.connected()){ - return vod; + sanitizeName(streamname); + JSON::Value ServConf = JSON::fromFile("/tmp/mist/streamlist"); + if (ServConf["streams"].isMember(streamname)){ + if (ServConf["streams"][streamname]["channel"]["URL"].asString()[0] == '/'){ + #if DEBUG >= 4 + std::cerr << "Opening VoD stream from file " << ServConf["streams"][streamname]["channel"]["URL"].asString() << std::endl; + #endif + return getVod(ServConf["streams"][streamname]["channel"]["URL"].asString()); + }else{ + #if DEBUG >= 4 + std::cerr << "Opening live stream " << streamname << std::endl; + #endif + return Socket::Connection("/tmp/mist/stream_"+streamname); + } } - return getLive(streamname); + #if DEBUG >= 4 + std::cerr << "Could not open stream " << streamname << " - stream not found" << std::endl; + #endif + return Socket::Connection(); } + /// Create a stream on the system. /// Filters the streamname, removing invalid characters and /// converting all letters to lowercase. diff --git a/lib/stream.h b/lib/stream.h index 56650c3b..f08433d9 100644 --- a/lib/stream.h +++ b/lib/stream.h @@ -7,17 +7,11 @@ namespace Util{ class Stream{ - /// Sanitize a streamname. - static void sanitizeName(std::string & streamname); - public: - /// Get a connection to a Live stream. - static Socket::Connection getLive(std::string streamname); - /// Get a connection to a VoD stream. - static Socket::Connection getVod(std::string streamname); - /// Probe for available streams. Currently first VoD, then Live. - static Socket::Connection getStream(std::string streamname); - - /// Create a Live stream on the system. - static Socket::Server makeLive(std::string streamname); + public: + static void sanitizeName(std::string & streamname); + static Socket::Connection getLive(std::string streamname); + static Socket::Connection getVod(std::string streamname); + static Socket::Connection getStream(std::string streamname); + static Socket::Server makeLive(std::string streamname); }; } From 6e4483c5fd0b03794d8cf27b25005381459faeb2 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 31 Aug 2012 16:56:14 +0200 Subject: [PATCH 287/788] RTMP lib upgrades to return strings by reference - note: initial testing indicates raptors. Needs to be looked at ASAP! --- lib/rtmpchunks.cpp | 36 ++++++++++++++++++------------------ lib/rtmpchunks.h | 16 ++++++++-------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/lib/rtmpchunks.cpp b/lib/rtmpchunks.cpp index 51a2e9dd..961176db 100644 --- a/lib/rtmpchunks.cpp +++ b/lib/rtmpchunks.cpp @@ -36,8 +36,8 @@ std::map RTMPStream::Chunk::lastrecv; /// Packs up the chunk for sending over the network. /// \warning Do not call if you are not actually sending the resulting data! /// \returns A std::string ready to be sent. -std::string RTMPStream::Chunk::Pack(){ - std::string output = ""; +std::string & RTMPStream::Chunk::Pack(){ + static std::string output = ""; RTMPStream::Chunk prev = lastsend[cs_id]; unsigned int tmpi; unsigned char chtype = 0x00; @@ -143,8 +143,8 @@ RTMPStream::Chunk::Chunk(){ }//constructor /// Packs up a chunk with the given arguments as properties. -std::string RTMPStream::SendChunk(unsigned int cs_id, unsigned char msg_type_id, unsigned int msg_stream_id, std::string data){ - RTMPStream::Chunk ch; +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 = RTMPStream::getNowMS(); ch.len = data.size(); @@ -161,8 +161,8 @@ std::string RTMPStream::SendChunk(unsigned int cs_id, unsigned char msg_type_id, /// \param data Contents of the media data. /// \param len Length of the media data, in bytes. /// \param ts Timestamp of the media data, relative to current system time. -std::string RTMPStream::SendMedia(unsigned char msg_type_id, unsigned char * data, int len, unsigned int ts){ - RTMPStream::Chunk ch; +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; @@ -170,14 +170,14 @@ std::string RTMPStream::SendMedia(unsigned char msg_type_id, unsigned char * dat ch.len_left = 0; ch.msg_type_id = msg_type_id; ch.msg_stream_id = 1; - ch.data.append((char*)data, (size_t)len); + 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){ - RTMPStream::Chunk ch; +std::string & RTMPStream::SendMedia(FLV::Tag & tag){ + static RTMPStream::Chunk ch; ch.cs_id = ((unsigned char)tag.data[0]); ch.timestamp = tag.tagTime(); ch.len = tag.len-15; @@ -185,13 +185,13 @@ std::string RTMPStream::SendMedia(FLV::Tag & tag){ ch.len_left = 0; ch.msg_type_id = (unsigned char)tag.data[0]; ch.msg_stream_id = 1; - ch.data.append(tag.data+11, (size_t)(tag.len-15)); + ch.data = std::string(tag.data+11, (size_t)(tag.len-15)); return ch.Pack(); }//SendMedia /// Packs up a chunk for a control message with 1 argument. -std::string RTMPStream::SendCTL(unsigned char type, unsigned int data){ - RTMPStream::Chunk ch; +std::string & RTMPStream::SendCTL(unsigned char type, unsigned int data){ + static RTMPStream::Chunk ch; ch.cs_id = 2; ch.timestamp = RTMPStream::getNowMS(); ch.len = 4; @@ -205,8 +205,8 @@ std::string RTMPStream::SendCTL(unsigned char type, unsigned int data){ }//SendCTL /// Packs up a chunk for a control message with 2 arguments. -std::string RTMPStream::SendCTL(unsigned char type, unsigned int data, unsigned char data2){ - RTMPStream::Chunk ch; +std::string & RTMPStream::SendCTL(unsigned char type, unsigned int data, unsigned char data2){ + static RTMPStream::Chunk ch; ch.cs_id = 2; ch.timestamp = RTMPStream::getNowMS(); ch.len = 5; @@ -221,8 +221,8 @@ std::string RTMPStream::SendCTL(unsigned char type, unsigned int data, unsigned }//SendCTL /// Packs up a chunk for a user control message with 1 argument. -std::string RTMPStream::SendUSR(unsigned char type, unsigned int data){ - RTMPStream::Chunk ch; +std::string & RTMPStream::SendUSR(unsigned char type, unsigned int data){ + static RTMPStream::Chunk ch; ch.cs_id = 2; ch.timestamp = RTMPStream::getNowMS(); ch.len = 6; @@ -238,8 +238,8 @@ std::string RTMPStream::SendUSR(unsigned char type, unsigned int data){ }//SendUSR /// Packs up a chunk for a user control message with 2 arguments. -std::string RTMPStream::SendUSR(unsigned char type, unsigned int data, unsigned int data2){ - RTMPStream::Chunk ch; +std::string & RTMPStream::SendUSR(unsigned char type, unsigned int data, unsigned int data2){ + static RTMPStream::Chunk ch; ch.cs_id = 2; ch.timestamp = RTMPStream::getNowMS(); ch.len = 10; diff --git a/lib/rtmpchunks.h b/lib/rtmpchunks.h index 009cfd29..e153f4ca 100644 --- a/lib/rtmpchunks.h +++ b/lib/rtmpchunks.h @@ -46,20 +46,20 @@ namespace RTMPStream{ Chunk(); bool Parse(std::string & data); - std::string Pack(); + std::string & Pack(); private: static std::map lastsend; static std::map lastrecv; };//RTMPStream::Chunk - std::string SendChunk(unsigned int cs_id, unsigned char msg_type_id, unsigned int msg_stream_id, std::string data); - std::string SendMedia(unsigned char msg_type_id, unsigned char * data, int len, unsigned int ts); - std::string SendMedia(FLV::Tag & tag); - std::string SendCTL(unsigned char type, unsigned int data); - std::string SendCTL(unsigned char type, unsigned int data, unsigned char data2); - std::string SendUSR(unsigned char type, unsigned int data); - std::string SendUSR(unsigned char type, unsigned int data, unsigned int data2); + 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; From 565e59a1b7a7aac856d1f997e20d07cfcf15c438 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 3 Sep 2012 10:26:04 +0200 Subject: [PATCH 288/788] Allow FTP lib to play nice with updated socket lib. --- lib/ftp.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/ftp.cpp b/lib/ftp.cpp index c3617848..25695dee 100644 --- a/lib/ftp.cpp +++ b/lib/ftp.cpp @@ -98,7 +98,8 @@ int FTP::User::ParseCommand( std::string Command ) { Connected = Passive.accept(); } fprintf( stderr, "Sending LIST information\n" ); - Connected.Send( MyDir.LIST( ActiveStreams ) ); + std::string tmpstr = MyDir.LIST( ActiveStreams ); + Connected.Send( tmpstr ); Connected.close( ); return 226; break; @@ -145,7 +146,8 @@ int FTP::User::ParseCommand( std::string Command ) { Connected = Passive.accept(); } fprintf( stderr, "Sending RETR information\n" ); - Connected.Send( MyDir.RETR( Command ) ); + std::string tmpstr = MyDir.RETR( Command ); + Connected.Send( tmpstr ); Connected.close(); return 226; break; From 9bf92add4fa9882d11b35642845f7ac962a9b252 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 3 Sep 2012 10:43:18 +0200 Subject: [PATCH 289/788] Carefully extracted raptors from RTMP. --- lib/rtmpchunks.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/rtmpchunks.cpp b/lib/rtmpchunks.cpp index 961176db..74a41a2a 100644 --- a/lib/rtmpchunks.cpp +++ b/lib/rtmpchunks.cpp @@ -37,7 +37,8 @@ std::map RTMPStream::Chunk::lastrecv; /// \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 = ""; + static std::string output; + output.clear(); RTMPStream::Chunk prev = lastsend[cs_id]; unsigned int tmpi; unsigned char chtype = 0x00; From 932c0e5b7b0e81d27c0f9f26957440264a5e02d9 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 3 Sep 2012 12:22:40 +0200 Subject: [PATCH 290/788] Added pausemark support to DTSC::Stream and fixes several minor bugs. --- lib/dtsc.cpp | 20 +++++++++++++------- lib/dtsc.h | 1 + lib/json.cpp | 2 +- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index d021c68d..aa82c9b5 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -54,15 +54,16 @@ bool DTSC::Stream::parsePacket(std::string & buffer){ datapointertype = INVALID; if (buffers.front().isMember("data")){ datapointer = &(buffers.front()["data"].strVal); - if (buffers.front().isMember("datatype")){ - std::string tmp = buffers.front()["datatype"].asString(); - if (tmp == "video"){datapointertype = VIDEO;} - if (tmp == "audio"){datapointertype = AUDIO;} - if (tmp == "meta"){datapointertype = META;} - } }else{ datapointer = 0; } + if (buffers.front().isMember("datatype")){ + std::string tmp = buffers.front()["datatype"].asString(); + if (tmp == "video"){datapointertype = VIDEO;} + if (tmp == "audio"){datapointertype = AUDIO;} + if (tmp == "meta"){datapointertype = META;} + if (tmp == "pause_marker"){datapointertype = PAUSEMARK;} + } buffer.erase(0, len+8); while (buffers.size() > buffercount){buffers.pop_back();} advanceRings(); @@ -295,7 +296,11 @@ void DTSC::File::seekNext(){ currframe++; currtime = jsonbuffer["time"].asInt(); #if DEBUG >= 4 - std::cerr << "Found a new frame " << currframe << " @ " << pos << "b/" << currtime << "s" << std::endl; + if (frames[currframe] != pos){ + std::cerr << "Found a new frame " << currframe << " @ " << pos << "b/" << currtime << "ms" << std::endl; + }else{ + std::cerr << "Passing frame " << currframe << " @ " << pos << "b/" << currtime << "ms" << std::endl; + } #endif frames[currframe] = pos; msframes[currframe] = currtime; @@ -329,6 +334,7 @@ bool DTSC::File::seek_frame(int frameno){ seekNext(); if (jsonbuffer.isNull()){return false;} } + seek_frame(frameno); return true; } } diff --git a/lib/dtsc.h b/lib/dtsc.h index ff591227..c4c578ac 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -56,6 +56,7 @@ namespace DTSC{ AUDIO, ///< Stream Audio data VIDEO, ///< Stream Video data META, ///< Stream Metadata + PAUSEMARK, ///< Pause marker INVALID ///< Anything else or no data available. }; diff --git a/lib/json.cpp b/lib/json.cpp index f5420d01..6a783bc4 100644 --- a/lib/json.cpp +++ b/lib/json.cpp @@ -406,7 +406,7 @@ std::string & JSON::Value::toNetPacked(){ std::string packed = toPacked(); strVal.resize(packed.size() + 8); //insert proper header for this type of data - if (isMember("data")){ + if (isMember("datatype")){ memcpy((void*)strVal.c_str(), "DTPD", 4); }else{ memcpy((void*)strVal.c_str(), "DTSC", 4); From c3abf0682d89b103ee3af79a4e4bd489ca9b5536 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 7 Sep 2012 14:18:25 +0200 Subject: [PATCH 291/788] Preventing compile errors by removing defaults in FTP constructor. --- lib/ftp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ftp.h b/lib/ftp.h index 2ea6b3c2..fb5b35d2 100644 --- a/lib/ftp.h +++ b/lib/ftp.h @@ -55,7 +55,7 @@ namespace FTP { class User { public: - User( Socket::Connection NewConnection = Socket::Connection(), std::map Credentials = std::map() ); + User( Socket::Connection NewConnection, std::map Credentials); ~User( ); int ParseCommand( std::string Command ); bool LoggedIn( ); From 24a3bcd8db7fdddc54c18cf3d18a22500c83a1aa Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 9 Sep 2012 00:53:38 +0200 Subject: [PATCH 292/788] Added todo in HTTP::Parser, changed version to 2.0.0 --- lib/Makefile.am | 2 +- lib/http_parser.cpp | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/Makefile.am b/lib/Makefile.am index a5dcbcf4..fdc39b7b 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -3,7 +3,7 @@ AM_CPPFLAGS = $(global_CFLAGS) lib_LTLIBRARIES=libmist-1.0.la libmist_1_0_la_SOURCES=amf.h amf.cpp auth.h auth.cpp base64.h base64.cpp config.h config.cpp crypto.h crypto.cpp dtsc.h dtsc.cpp flv_tag.h flv_tag.cpp http_parser.h http_parser.cpp json.h json.cpp procs.h procs.cpp rtmpchunks.h rtmpchunks.cpp socket.h socket.cpp mp4.h mp4.cpp ftp.h ftp.cpp filesystem.h filesystem.cpp stream.h stream.cpp libmist_1_0_la_LIBADD=-lssl -lcrypto -libmist_1_0_la_LDFLAGS = -version-info 1:0:0 +libmist_1_0_la_LDFLAGS = -version-info 2:0:0 pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = mist-1.0.pc diff --git a/lib/http_parser.cpp b/lib/http_parser.cpp index 526ff014..f8489fdb 100644 --- a/lib/http_parser.cpp +++ b/lib/http_parser.cpp @@ -141,7 +141,8 @@ bool HTTP::Parser::Read(std::string & strbuf){ bool HTTP::Parser::parse(std::string & HTTPbuffer){ size_t f; std::string tmpA, tmpB, tmpC; - while (HTTPbuffer != ""){ + /// \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; @@ -190,7 +191,7 @@ bool HTTP::Parser::parse(std::string & HTTPbuffer){ } } } - return false; //we should never get here... + return false; //empty input }//HTTPReader::parse #include From 4140b04608ed518a0ac6232f527fd01717c3dd60 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 16 Sep 2012 01:17:08 +0200 Subject: [PATCH 293/788] Various optimalisations to improve performance. More coming soon. --- lib/dtsc.cpp | 55 +++++++++++++- lib/dtsc.h | 2 + lib/ftp.cpp | 3 +- lib/http_parser.cpp | 25 +++++-- lib/rtmpchunks.cpp | 10 +-- lib/socket.cpp | 170 ++++++++++++++++++++++++++++++++++++-------- lib/socket.h | 28 ++++++-- 7 files changed, 243 insertions(+), 50 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index aa82c9b5..d7981462 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -86,6 +86,59 @@ bool DTSC::Stream::parsePacket(std::string & buffer){ return false; } +/// Attempts to parse a packet from the given Socket::Buffer. +/// Returns true if successful, removing the parsed part from the buffer. +/// Returns false if invalid or not enough data is in the buffer. +/// \arg buffer The Socket::Buffer to attempt to parse. +bool DTSC::Stream::parsePacket(Socket::Buffer & buffer){ + uint32_t len; + static bool syncing = false; + if (buffer.available(8)){ + std::string header_bytes = buffer.copy(8); + if (memcmp(header_bytes.c_str(), DTSC::Magic_Header, 4) == 0){ + len = ntohl(((uint32_t *)header_bytes.c_str())[1]); + if (!buffer.available(len+8)){return false;} + unsigned int i = 0; + std::string wholepacket = buffer.remove(len+8); + metadata = JSON::fromDTMI((unsigned char*)wholepacket.c_str() + 8, len, i); + return false; + } + if (memcmp(header_bytes.c_str(), DTSC::Magic_Packet, 4) == 0){ + len = ntohl(((uint32_t *)header_bytes.c_str())[1]); + if (!buffer.available(len+8)){return false;} + buffers.push_front(JSON::Value()); + unsigned int i = 0; + std::string wholepacket = buffer.remove(len+8); + buffers.front() = JSON::fromDTMI((unsigned char*)wholepacket.c_str() + 8, len, i); + datapointertype = INVALID; + if (buffers.front().isMember("data")){ + datapointer = &(buffers.front()["data"].strVal); + }else{ + datapointer = 0; + } + if (buffers.front().isMember("datatype")){ + std::string tmp = buffers.front()["datatype"].asString(); + if (tmp == "video"){datapointertype = VIDEO;} + if (tmp == "audio"){datapointertype = AUDIO;} + if (tmp == "meta"){datapointertype = META;} + if (tmp == "pause_marker"){datapointertype = PAUSEMARK;} + } + while (buffers.size() > buffercount){buffers.pop_back();} + advanceRings(); + syncing = false; + return true; + } + #if DEBUG >= 2 + if (!syncing){ + std::cerr << "Error: Invalid DTMI data detected - syncing" << std::endl; + syncing = true; + } + #endif + buffer.get().clear(); + } + return false; +} + /// Returns a direct pointer to the data attribute of the last received packet, if available. /// Returns NULL if no valid pointer or packet is available. std::string & DTSC::Stream::lastData(){ @@ -295,7 +348,7 @@ void DTSC::File::seekNext(){ if (frames[currframe] != pos){ currframe++; currtime = jsonbuffer["time"].asInt(); - #if DEBUG >= 4 + #if DEBUG >= 6 if (frames[currframe] != pos){ std::cerr << "Found a new frame " << currframe << " @ " << pos << "b/" << currtime << "ms" << std::endl; }else{ diff --git a/lib/dtsc.h b/lib/dtsc.h index c4c578ac..668b0e26 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -10,6 +10,7 @@ #include #include //for FILE #include "json.h" +#include "socket.h" @@ -113,6 +114,7 @@ namespace DTSC{ bool hasVideo(); bool hasAudio(); bool parsePacket(std::string & buffer); + bool parsePacket(Socket::Buffer & buffer); std::string & outPacket(unsigned int num); std::string & outHeader(); Ring * getRing(); diff --git a/lib/ftp.cpp b/lib/ftp.cpp index 25695dee..7510e948 100644 --- a/lib/ftp.cpp +++ b/lib/ftp.cpp @@ -169,7 +169,8 @@ int FTP::User::ParseCommand( std::string Command ) { fprintf( stderr, "Reading STOR information\n" ); std::string Buffer; while( Connected.spool() ) { } - Buffer = Connected.Received(); + /// \todo Comment me back in. ^_^ + //Buffer = Connected.Received(); MyDir.STOR( Command, Buffer ); return 250; break; diff --git a/lib/http_parser.cpp b/lib/http_parser.cpp index f8489fdb..b554e5c0 100644 --- a/lib/http_parser.cpp +++ b/lib/http_parser.cpp @@ -133,6 +133,7 @@ bool HTTP::Parser::Read(std::string & strbuf){ return parse(strbuf); }//HTTPReader::Read +#include /// 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. @@ -141,13 +142,17 @@ bool HTTP::Parser::Read(std::string & strbuf){ 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. + /// \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); - HTTPbuffer.erase(0, f+1); + 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; @@ -166,7 +171,11 @@ bool HTTP::Parser::parse(std::string & HTTPbuffer){ }else{ if (tmpA.size() == 0){ seenHeaders = true; - if (GetHeader("Content-Length") != ""){length = atoi(GetHeader("Content-Length").c_str());} + body.clear(); + if (GetHeader("Content-Length") != ""){ + length = atoi(GetHeader("Content-Length").c_str()); + if (body.capacity() < length){body.reserve(length);} + } }else{ f = tmpA.find(':'); if (f == std::string::npos) continue; @@ -178,10 +187,13 @@ bool HTTP::Parser::parse(std::string & HTTPbuffer){ } if (seenHeaders){ if (length > 0){ - if (HTTPbuffer.length() >= length){ - body = HTTPbuffer.substr(0, length); + 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 - HTTPbuffer.erase(0, length); return true; }else{ return false; @@ -194,7 +206,6 @@ bool HTTP::Parser::parse(std::string & HTTPbuffer){ return false; //empty input }//HTTPReader::parse -#include /// Parses GET or POST-style variable data. /// Saves to internal variable structure using HTTP::Parser::SetVar. void HTTP::Parser::parseVars(std::string data){ diff --git a/lib/rtmpchunks.cpp b/lib/rtmpchunks.cpp index 74a41a2a..2e46da32 100644 --- a/lib/rtmpchunks.cpp +++ b/lib/rtmpchunks.cpp @@ -258,15 +258,17 @@ std::string & RTMPStream::SendUSR(unsigned char type, unsigned int data, unsigne /// Parses the argument string into the current chunk. -/// Tries to read a whole chunk, if successful it will remove -/// the corresponding data from the input string. +/// 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){ +bool RTMPStream::Chunk::Parse(std::string & source){ + static std::string indata; + indata.append(source); + source.clear(); gettimeofday(&RTMPStream::lastrec, 0); unsigned int i = 0; if (indata.size() < 1) return false;//need at least a byte @@ -378,7 +380,7 @@ bool RTMPStream::Chunk::Parse(std::string & indata){ if (len_left == 0){ return true; }else{ - return Parse(indata); + return Parse(source); } }else{ data = ""; diff --git a/lib/socket.cpp b/lib/socket.cpp index d94545b6..f6250aa7 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -12,12 +12,110 @@ #include #endif +#define BUFFER_BLOCKSIZE 4096 //set buffer blocksize to 4KiB +#include //temporary for debugging + std::string uint2string(unsigned int i){ std::stringstream st; st << i; return st.str(); } +/// Returns the amount of elements in the internal std::deque of std::string objects. +/// The back is popped as long as it is empty, first - this way this function is +/// guaranteed to return 0 if the buffer is empty. +unsigned int Socket::Buffer::size(){ + while (data.size() > 0 && data.back().empty()){data.pop_back();} + return data.size(); +} + +/// Appends this string to the internal std::deque of std::string objects. +/// It is automatically split every BUFFER_BLOCKSIZE bytes. +void Socket::Buffer::append(const std::string & newdata){ + append(newdata.c_str(), newdata.size()); +} + +/// Appends this data block to the internal std::deque of std::string objects. +/// It is automatically split every BUFFER_BLOCKSIZE bytes. +void Socket::Buffer::append(const char * newdata, const unsigned int newdatasize){ + unsigned int i = 0, j = 0; + while (i < newdatasize){ + j = i; + while (j < newdatasize && j - i <= BUFFER_BLOCKSIZE){ + j++; + if (newdata[j-1] == '\n'){break;} + } + if (i != j){ + data.push_front(std::string(newdata+i, (size_t)(j - i))); + i = j; + }else{ + break; + } + } + if (data.size() > 1000){ + std::cerr << "Warning: After " << newdatasize << " new bytes, buffer has " << data.size() << " parts!" << std::endl; + } +} + +/// Returns true if at least count bytes are available in this buffer. +bool Socket::Buffer::available(unsigned int count){ + unsigned int i = 0; + for (std::deque::iterator it = data.begin(); it != data.end(); ++it){ + i += (*it).size(); + if (i >= count){return true;} + } + return false; +} + +/// Removes count bytes from the buffer, returning them by value. +/// Returns an empty string if not all count bytes are available. +std::string Socket::Buffer::remove(unsigned int count){ + if (!available(count)){return "";} + unsigned int i = 0; + std::string ret; + for (std::deque::reverse_iterator it = data.rbegin(); it != data.rend(); ++it){ + if (i + (*it).size() < count){ + ret.append(*it); + i += (*it).size(); + (*it).clear(); + }else{ + ret.append(*it, 0, count - i); + (*it).erase(0, count - i); + break; + } + } + return ret; +} + +/// Copies count bytes from the buffer, returning them by value. +/// Returns an empty string if not all count bytes are available. +std::string Socket::Buffer::copy(unsigned int count){ + if (!available(count)){return "";} + unsigned int i = 0; + std::string ret; + for (std::deque::reverse_iterator it = data.rbegin(); it != data.rend(); ++it){ + if (i + (*it).size() < count){ + ret.append(*it); + i += (*it).size(); + }else{ + ret.append(*it, 0, count - i); + break; + } + } + return ret; +} + +/// Gets a reference to the back of the internal std::deque of std::string objects. +std::string & Socket::Buffer::get(){ + static std::string empty; + if (data.size() > 0){ + return data.back(); + }else{ + return empty; + } +} + + /// Create a new base socket. This is a basic constructor for converting any valid socket to a Socket::Connection. /// \param sockNo Integer representing the socket to convert. Socket::Connection::Connection(int sockNo){ @@ -225,23 +323,36 @@ std::string Socket::Connection::getStats(std::string C){ /// Updates the downbuffer and upbuffer internal variables. /// Returns true if new data was received, false otherwise. bool Socket::Connection::spool(){ - iwrite(upbuffer); - return iread(downbuffer); + if (upbuffer.size() > 0){ + iwrite(upbuffer.get()); + } + /// \todo Provide better mechanism to prevent overbuffering. + if (downbuffer.size() > 1000){ + return true; + }else{ + return iread(downbuffer); + } } /// Updates the downbuffer and upbuffer internal variables until upbuffer is empty. /// Returns true if new data was received, false otherwise. bool Socket::Connection::flush(){ while (upbuffer.size() > 0 && connected()){ - iwrite(upbuffer); - usleep(5000);//sleep 5 ms + if (!iwrite(upbuffer.get())){ + usleep(10000);//sleep 10ms + } + } + /// \todo Provide better mechanism to prevent overbuffering. + if (downbuffer.size() > 1000){ + return true; + }else{ + return iread(downbuffer); } - return iread(downbuffer); } /// Returns a reference to the download buffer. -std::string & Socket::Connection::Received(){ +Socket::Buffer & Socket::Connection::Received(){ return downbuffer; } @@ -251,16 +362,15 @@ std::string & Socket::Connection::Received(){ /// the data right away. Any data that could not be send will be put into the upbuffer. /// This means this function is blocking if the socket is, but nonblocking otherwise. void Socket::Connection::Send(std::string & data){ - if (upbuffer.size() > 0){ - iwrite(upbuffer); - if (upbuffer.size() > 0){ - upbuffer.append(data); - } + while (upbuffer.size() > 0){ + if (!iwrite(upbuffer.get())){break;} } - if (upbuffer.size() == 0){ + if (upbuffer.size() > 0){ + upbuffer.append(data); + }else{ int i = iwrite(data.c_str(), data.size()); if (i < data.size()){ - upbuffer.append(data, i, data.size() - i); + upbuffer.append(data.c_str()+i, data.size() - i); } } } @@ -272,16 +382,15 @@ void Socket::Connection::Send(std::string & data){ /// This means this function is blocking if the socket is, but nonblocking otherwise. void Socket::Connection::Send(const char * data){ int len = strlen(data); - if (upbuffer.size() > 0){ - iwrite(upbuffer); - if (upbuffer.size() > 0){ - upbuffer.append(data, (size_t)len); - } + while (upbuffer.size() > 0){ + if (!iwrite(upbuffer.get())){break;} } - if (upbuffer.size() == 0){ + if (upbuffer.size() > 0){ + upbuffer.append(data, len); + }else{ int i = iwrite(data, len); if (i < len){ - upbuffer.append(data + i, (size_t)(len - i)); + upbuffer.append(data + i, len - i); } } } @@ -292,13 +401,12 @@ void Socket::Connection::Send(const char * data){ /// the data right away. Any data that could not be send will be put into the upbuffer. /// This means this function is blocking if the socket is, but nonblocking otherwise. void Socket::Connection::Send(const char * data, size_t len){ - if (upbuffer.size() > 0){ - iwrite(upbuffer); - if (upbuffer.size() > 0){ - upbuffer.append(data, len); - } + while (upbuffer.size() > 0){ + if (!iwrite(upbuffer.get())){break;} } - if (upbuffer.size() == 0){ + if (upbuffer.size() > 0){ + upbuffer.append(data, len); + }else{ int i = iwrite(data, len); if (i < len){ upbuffer.append(data + i, len - i); @@ -380,14 +488,14 @@ int Socket::Connection::iread(void * buffer, int len){ return r; }//Socket::Connection::iread -/// Read call that is compatible with std::string. +/// Read call that is compatible with Socket::Buffer. /// Data is read using iread (which is nonblocking if the Socket::Connection itself is), /// then appended to end of buffer. -/// \param buffer std::string to append data to. +/// \param buffer Socket::Buffer to append data to. /// \return True if new data arrived, false otherwise. -bool Socket::Connection::iread(std::string & buffer){ - char cbuffer[5000]; - int num = iread(cbuffer, 5000); +bool Socket::Connection::iread(Buffer & buffer){ + char cbuffer[BUFFER_BLOCKSIZE]; + int num = iread(cbuffer, BUFFER_BLOCKSIZE); if (num < 1){return false;} buffer.append(cbuffer, num); return true; diff --git a/lib/socket.h b/lib/socket.h index 273a6433..3283c99b 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include #include #include @@ -13,6 +14,7 @@ #include #include #include +#include //for being friendly with Socket::Connection down below namespace Buffer{class user;}; @@ -20,6 +22,20 @@ 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 data; + public: + unsigned int size(); + void append(const std::string & newdata); + void append(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); + };//Buffer + /// This class is for easy communicating through sockets, either TCP or Unix. class Connection{ private: @@ -29,15 +45,15 @@ namespace Socket{ unsigned int up; unsigned int down; unsigned int conntime; - std::string downbuffer; ///< Stores temporary data coming in. - std::string upbuffer; ///< Stores temporary data going out. + Buffer downbuffer; ///< Stores temporary data coming in. + Buffer upbuffer; ///< Stores temporary data going out. int iread(void * buffer, int len); ///< Incremental read call. int iwrite(const void * buffer, int len); ///< Incremental write call. - bool iread(std::string & buffer); ///< Incremental write call that is compatible with std::string. + bool iread(Buffer & buffer); ///< Incremental write call that is compatible with Socket::Buffer. bool iwrite(std::string & buffer); ///< Write call that is compatible with std::string. - public: + public: //friends - friend class Buffer::user; + friend class ::Buffer::user; //constructors Connection(); ///< Create a new disconnected base socket. Connection(int sockNo); ///< Create a new base socket. @@ -55,7 +71,7 @@ namespace Socket{ //buffered i/o methods bool spool(); ///< Updates the downbuffer and upbuffer internal variables. bool flush(); ///< Updates the downbuffer and upbuffer internal variables until upbuffer is empty. - std::string & Received(); ///< Returns a reference to the download buffer. + Buffer & Received(); ///< Returns a reference to the download buffer. void Send(std::string & data); ///< Appends data to the upbuffer. void Send(const char * data); ///< Appends data to the upbuffer. void Send(const char * data, size_t len); ///< Appends data to the upbuffer. From c95bf32fae9db9b4b179aab1aa7d9160e63ab4d5 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 17 Sep 2012 16:47:58 +0200 Subject: [PATCH 294/788] Performance improvements and fixes as well as new high-performance sending functions in the socket library. --- lib/dtsc.cpp | 8 +++- lib/http_parser.cpp | 2 +- lib/socket.cpp | 91 ++++++++++++++++++++++++++++----------------- lib/socket.h | 3 ++ 4 files changed, 68 insertions(+), 36 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index d7981462..78d0a2c3 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -316,7 +316,13 @@ bool DTSC::File::writeHeader(std::string & header, bool force){ /// Reading the packet means the file position is increased to the next packet. void DTSC::File::seekNext(){ if (fread(buffer, 4, 1, F) != 1){ - fprintf(stderr, "Could not read header\n"); + if (feof(F)){ + #if DEBUG >= 4 + fprintf(stderr, "End of file reached.\n"); + #endif + }else{ + fprintf(stderr, "Could not read header\n"); + } strbuffer = ""; jsonbuffer.null(); return; diff --git a/lib/http_parser.cpp b/lib/http_parser.cpp index b554e5c0..ac0e1f7b 100644 --- a/lib/http_parser.cpp +++ b/lib/http_parser.cpp @@ -110,7 +110,7 @@ void HTTP::Parser::SetHeader(std::string i, std::string v){ /// Sets header i to integer value v. void HTTP::Parser::SetHeader(std::string i, int v){ Trim(i); - char val[128]; + char val[23];//ints are never bigger than 22 chars as decimal sprintf(val, "%i", v); headers[i] = val; } diff --git a/lib/socket.cpp b/lib/socket.cpp index f6250aa7..10f39465 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -21,6 +21,13 @@ std::string uint2string(unsigned int i){ return st.str(); } +void ms_sleep(int ms){ + struct timespec T; + T.tv_sec = ms/1000; + T.tv_nsec = 1000*(ms%1000); + nanosleep(&T, 0); +} + /// Returns the amount of elements in the internal std::deque of std::string objects. /// The back is popped as long as it is empty, first - this way this function is /// guaranteed to return 0 if the buffer is empty. @@ -73,6 +80,7 @@ std::string Socket::Buffer::remove(unsigned int count){ if (!available(count)){return "";} unsigned int i = 0; std::string ret; + ret.reserve(count); for (std::deque::reverse_iterator it = data.rbegin(); it != data.rend(); ++it){ if (i + (*it).size() < count){ ret.append(*it); @@ -339,7 +347,7 @@ bool Socket::Connection::spool(){ bool Socket::Connection::flush(){ while (upbuffer.size() > 0 && connected()){ if (!iwrite(upbuffer.get())){ - usleep(10000);//sleep 10ms + ms_sleep(10);//sleep 10ms } } /// \todo Provide better mechanism to prevent overbuffering. @@ -356,41 +364,22 @@ Socket::Buffer & Socket::Connection::Received(){ return downbuffer; } -/// Appends data to the upbuffer. -/// This will attempt to send the upbuffer (if non-empty) first. -/// If the upbuffer is empty before or after this attempt, it will attempt to send -/// the data right away. Any data that could not be send will be put into the upbuffer. -/// This means this function is blocking if the socket is, but nonblocking otherwise. -void Socket::Connection::Send(std::string & data){ - while (upbuffer.size() > 0){ - if (!iwrite(upbuffer.get())){break;} - } - if (upbuffer.size() > 0){ - upbuffer.append(data); - }else{ - int i = iwrite(data.c_str(), data.size()); - if (i < data.size()){ - upbuffer.append(data.c_str()+i, data.size() - i); +/// Will not buffer anything but always send right away. Blocks. +/// This will send the upbuffer (if non-empty) first, then the data. +/// Any data that could not be send will block until it can be send or the connection is severed. +void Socket::Connection::SendNow(const char * data, size_t len){ + while (upbuffer.size() > 0 && connected()){ + if (!iwrite(upbuffer.get())){ + ms_sleep(1);//sleep 1ms if buffer full } } -} - -/// Appends data to the upbuffer. -/// This will attempt to send the upbuffer (if non-empty) first. -/// If the upbuffer is empty before or after this attempt, it will attempt to send -/// the data right away. Any data that could not be send will be put into the upbuffer. -/// This means this function is blocking if the socket is, but nonblocking otherwise. -void Socket::Connection::Send(const char * data){ - int len = strlen(data); - while (upbuffer.size() > 0){ - if (!iwrite(upbuffer.get())){break;} - } - if (upbuffer.size() > 0){ - upbuffer.append(data, len); - }else{ - int i = iwrite(data, len); - if (i < len){ - upbuffer.append(data + i, len - i); + int i = iwrite(data, len); + while (i < len && connected()){ + int j = iwrite(data+i, len-i); + if (j > 0){ + i += j; + }else{ + ms_sleep(1);//sleep 1ms and retry } } } @@ -414,6 +403,40 @@ void Socket::Connection::Send(const char * data, size_t len){ } } +/// Will not buffer anything but always send right away. Blocks. +/// This will send the upbuffer (if non-empty) first, then the data. +/// Any data that could not be send will block until it can be send or the connection is severed. +void Socket::Connection::SendNow(const char * data){ + int len = strlen(data); + SendNow(data, len); +} + +/// Appends data to the upbuffer. +/// This will attempt to send the upbuffer (if non-empty) first. +/// If the upbuffer is empty before or after this attempt, it will attempt to send +/// the data right away. Any data that could not be send will be put into the upbuffer. +/// This means this function is blocking if the socket is, but nonblocking otherwise. +void Socket::Connection::Send(const char * data){ + int len = strlen(data); + Send(data, len); +} + +/// Will not buffer anything but always send right away. Blocks. +/// This will send the upbuffer (if non-empty) first, then the data. +/// Any data that could not be send will block until it can be send or the connection is severed. +void Socket::Connection::SendNow(std::string & data){ + SendNow(data.c_str(), data.size()); +} + +/// Appends data to the upbuffer. +/// This will attempt to send the upbuffer (if non-empty) first. +/// If the upbuffer is empty before or after this attempt, it will attempt to send +/// the data right away. Any data that could not be send will be put into the upbuffer. +/// This means this function is blocking if the socket is, but nonblocking otherwise. +void Socket::Connection::Send(std::string & data){ + Send(data.c_str(), data.size()); +} + /// Incremental write call. This function tries to write len bytes to the socket from the buffer, /// returning the amount of bytes it actually wrote. /// \param buffer Location of the buffer to write from. diff --git a/lib/socket.h b/lib/socket.h index 3283c99b..4b4394b8 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -75,6 +75,9 @@ namespace Socket{ void Send(std::string & data); ///< Appends data to the upbuffer. void Send(const char * data); ///< Appends data to the upbuffer. void Send(const char * data, size_t len); ///< Appends data to the upbuffer. + void SendNow(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 dataUp(); ///< Returns total amount of bytes sent. unsigned int dataDown(); ///< Returns total amount of bytes received. From c019dc6e9f98c69e78156f86e4560d13dd35e1aa Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 18 Sep 2012 15:48:44 +0200 Subject: [PATCH 295/788] Added new timing library, added Socket::Buffer support to RTMP library. --- lib/Makefile.am | 4 +- lib/auth.h | 1 + lib/rtmpchunks.cpp | 159 ++++++++++++++++++++++++++++++++++++++++----- lib/rtmpchunks.h | 5 +- lib/socket.cpp | 27 ++++---- lib/socket.h | 2 +- lib/timing.cpp | 27 ++++++++ lib/timing.h | 10 +++ 8 files changed, 195 insertions(+), 40 deletions(-) create mode 100644 lib/timing.cpp create mode 100644 lib/timing.h diff --git a/lib/Makefile.am b/lib/Makefile.am index fdc39b7b..6ad1607d 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,7 +1,7 @@ AM_CPPFLAGS = $(global_CFLAGS) lib_LTLIBRARIES=libmist-1.0.la -libmist_1_0_la_SOURCES=amf.h amf.cpp auth.h auth.cpp base64.h base64.cpp config.h config.cpp crypto.h crypto.cpp dtsc.h dtsc.cpp flv_tag.h flv_tag.cpp http_parser.h http_parser.cpp json.h json.cpp procs.h procs.cpp rtmpchunks.h rtmpchunks.cpp socket.h socket.cpp mp4.h mp4.cpp ftp.h ftp.cpp filesystem.h filesystem.cpp stream.h stream.cpp +libmist_1_0_la_SOURCES=amf.h amf.cpp auth.h auth.cpp base64.h base64.cpp config.h config.cpp crypto.h crypto.cpp dtsc.h dtsc.cpp flv_tag.h flv_tag.cpp http_parser.h http_parser.cpp json.h json.cpp procs.h procs.cpp rtmpchunks.h rtmpchunks.cpp socket.h socket.cpp mp4.h mp4.cpp ftp.h ftp.cpp filesystem.h filesystem.cpp stream.h stream.cpp timing.h timing.cpp libmist_1_0_la_LIBADD=-lssl -lcrypto libmist_1_0_la_LDFLAGS = -version-info 2:0:0 @@ -9,4 +9,4 @@ pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = mist-1.0.pc library_includedir=$(includedir)/mist-1.0/mist -library_include_HEADERS = amf.h auth.h base64.h config.h crypto.h dtsc.h flv_tag.h http_parser.h json.h procs.h rtmpchunks.h socket.h mp4.h ftp.h filesystem.h stream.h +library_include_HEADERS = amf.h auth.h base64.h config.h crypto.h dtsc.h flv_tag.h http_parser.h json.h procs.h rtmpchunks.h socket.h mp4.h ftp.h filesystem.h stream.h timing.h diff --git a/lib/auth.h b/lib/auth.h index 42d4580d..40fce7ec 100644 --- a/lib/auth.h +++ b/lib/auth.h @@ -1,5 +1,6 @@ #pragma once #include +#include #include #include diff --git a/lib/rtmpchunks.cpp b/lib/rtmpchunks.cpp index 2e46da32..8a9e356f 100644 --- a/lib/rtmpchunks.cpp +++ b/lib/rtmpchunks.cpp @@ -4,19 +4,12 @@ #include "rtmpchunks.h" #include "flv_tag.h" #include "crypto.h" +#include "timing.h" char versionstring[] = "WWW.DDVTECH.COM "; ///< String that is repeated in the RTMP handshake std::string RTMPStream::handshake_in; ///< Input for the handshake. std::string RTMPStream::handshake_out;///< Output for the handshake. -/// Gets the current system time in milliseconds. -unsigned int RTMPStream::getNowMS(){ - timeval t; - gettimeofday(&t, 0); - return t.tv_sec * 1000 + t.tv_usec/1000; -}//RTMPStream::getNowMS - - unsigned int RTMPStream::chunk_rec_max = 128; unsigned int RTMPStream::chunk_snd_max = 128; unsigned int RTMPStream::rec_window_size = 2500000; @@ -147,7 +140,7 @@ RTMPStream::Chunk::Chunk(){ 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 = RTMPStream::getNowMS(); + ch.timestamp = Util::epoch(); ch.len = data.size(); ch.real_len = data.size(); ch.len_left = 0; @@ -194,7 +187,7 @@ std::string & RTMPStream::SendMedia(FLV::Tag & tag){ std::string & RTMPStream::SendCTL(unsigned char type, unsigned int data){ static RTMPStream::Chunk ch; ch.cs_id = 2; - ch.timestamp = RTMPStream::getNowMS(); + ch.timestamp = Util::epoch(); ch.len = 4; ch.real_len = 4; ch.len_left = 0; @@ -209,7 +202,7 @@ std::string & RTMPStream::SendCTL(unsigned char type, unsigned int data){ std::string & RTMPStream::SendCTL(unsigned char type, unsigned int data, unsigned char data2){ static RTMPStream::Chunk ch; ch.cs_id = 2; - ch.timestamp = RTMPStream::getNowMS(); + ch.timestamp = Util::epoch(); ch.len = 5; ch.real_len = 5; ch.len_left = 0; @@ -225,7 +218,7 @@ std::string & RTMPStream::SendCTL(unsigned char type, unsigned int data, unsigne std::string & RTMPStream::SendUSR(unsigned char type, unsigned int data){ static RTMPStream::Chunk ch; ch.cs_id = 2; - ch.timestamp = RTMPStream::getNowMS(); + ch.timestamp = Util::epoch(); ch.len = 6; ch.real_len = 6; ch.len_left = 0; @@ -242,7 +235,7 @@ std::string & RTMPStream::SendUSR(unsigned char type, unsigned int data){ std::string & RTMPStream::SendUSR(unsigned char type, unsigned int data, unsigned int data2){ static RTMPStream::Chunk ch; ch.cs_id = 2; - ch.timestamp = RTMPStream::getNowMS(); + ch.timestamp = Util::epoch(); ch.len = 10; ch.real_len = 10; ch.len_left = 0; @@ -265,10 +258,7 @@ std::string & RTMPStream::SendUSR(unsigned char type, unsigned int data, unsigne /// \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 & source){ - static std::string indata; - indata.append(source); - source.clear(); +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 @@ -380,7 +370,7 @@ bool RTMPStream::Chunk::Parse(std::string & source){ if (len_left == 0){ return true; }else{ - return Parse(source); + return Parse(indata); } }else{ data = ""; @@ -391,6 +381,139 @@ bool RTMPStream::Chunk::Parse(std::string & source){ } }//Parse +/// Parses the argument string into the current chunk. +/// Tries to read a whole chunk, removing data from the input 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(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++]; + 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){fprintf(stderr, "Warning: Header type 0x40 with no valid previous chunk!\n");} + timestamp = indata[i++]*256*256; + timestamp += indata[i++]*256; + timestamp += indata[i++]; + if (timestamp != 0x00ffffff){timestamp += prev.timestamp;} + len = indata[i++]*256*256; + len += indata[i++]*256; + len += indata[i++]; + len_left = 0; + msg_type_id = indata[i++]; + msg_stream_id = prev.msg_stream_id; + break; + case 0x80: + if (!buffer.available(i+3)){return false;} //can't read whole header + indata = buffer.copy(i+3); + if (prev.msg_type_id == 0){fprintf(stderr, "Warning: Header type 0x80 with no valid previous chunk!\n");} + timestamp = indata[i++]*256*256; + timestamp += indata[i++]*256; + timestamp += indata[i++]; + if (timestamp != 0x00ffffff){timestamp += prev.timestamp;} + len = prev.len; + len_left = prev.len_left; + msg_type_id = prev.msg_type_id; + msg_stream_id = prev.msg_stream_id; + break; + case 0xC0: + if (prev.msg_type_id == 0){fprintf(stderr, "Warning: Header type 0xC0 with no valid previous chunk!\n");} + timestamp = prev.timestamp; + len = prev.len; + len_left = prev.len_left; + msg_type_id = prev.msg_type_id; + msg_stream_id = prev.msg_stream_id; + break; + } + //calculate chunk length, real length, and length left till complete + if (len_left > 0){ + real_len = len_left; + len_left -= real_len; + }else{ + real_len = len; + } + if (real_len > RTMPStream::chunk_rec_max){ + len_left += real_len - RTMPStream::chunk_rec_max; + real_len = RTMPStream::chunk_rec_max; + } + //read extended timestamp, if neccesary + if (timestamp == 0x00ffffff){ + if (!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++]; + } + + //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 /// 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, diff --git a/lib/rtmpchunks.h b/lib/rtmpchunks.h index e153f4ca..21dd160e 100644 --- a/lib/rtmpchunks.h +++ b/lib/rtmpchunks.h @@ -8,6 +8,7 @@ #include #include #include +#include "socket.h" //forward declaration of FLV::Tag to avoid circular dependencies. namespace FLV{ @@ -17,9 +18,6 @@ namespace FLV{ /// Contains all functions and classes needed for RTMP connections. namespace RTMPStream{ - /// Gets the current system time in milliseconds. - unsigned int getNowMS(); - extern unsigned int chunk_rec_max; ///< Maximum size for a received chunk. extern unsigned int chunk_snd_max; ///< Maximum size for a sent chunk. extern unsigned int rec_window_size; ///< Window size for receiving. @@ -46,6 +44,7 @@ namespace RTMPStream{ Chunk(); bool Parse(std::string & data); + bool Parse(Socket::Buffer & data); std::string & Pack(); private: diff --git a/lib/socket.cpp b/lib/socket.cpp index 10f39465..461904ba 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -3,6 +3,7 @@ /// Written by Jaron Vietor in 2010 for DDVTech #include "socket.h" +#include "timing.h" #include #include #include @@ -15,19 +16,13 @@ #define BUFFER_BLOCKSIZE 4096 //set buffer blocksize to 4KiB #include //temporary for debugging + std::string uint2string(unsigned int i){ std::stringstream st; st << i; return st.str(); } -void ms_sleep(int ms){ - struct timespec T; - T.tv_sec = ms/1000; - T.tv_nsec = 1000*(ms%1000); - nanosleep(&T, 0); -} - /// Returns the amount of elements in the internal std::deque of std::string objects. /// The back is popped as long as it is empty, first - this way this function is /// guaranteed to return 0 if the buffer is empty. @@ -132,7 +127,7 @@ Socket::Connection::Connection(int sockNo){ pipes[1] = -1; up = 0; down = 0; - conntime = time(0); + conntime = Util::epoch(); Error = false; Blocking = false; }//Socket::Connection basic constructor @@ -146,7 +141,7 @@ Socket::Connection::Connection(int write, int read){ pipes[1] = read; up = 0; down = 0; - conntime = time(0); + conntime = Util::epoch(); Error = false; Blocking = false; }//Socket::Connection basic constructor @@ -159,7 +154,7 @@ Socket::Connection::Connection(){ pipes[1] = -1; up = 0; down = 0; - conntime = time(0); + conntime = Util::epoch(); Error = false; Blocking = false; }//Socket::Connection basic constructor @@ -231,7 +226,7 @@ Socket::Connection::Connection(std::string address, bool nonblock){ Blocking = false; up = 0; down = 0; - conntime = time(0); + conntime = Util::epoch(); sockaddr_un addr; addr.sun_family = AF_UNIX; strncpy(addr.sun_path, address.c_str(), address.size()+1); @@ -260,7 +255,7 @@ Socket::Connection::Connection(std::string host, int port, bool nonblock){ Blocking = false; up = 0; down = 0; - conntime = time(0); + conntime = Util::epoch(); std::stringstream ss; ss << port; @@ -325,7 +320,7 @@ unsigned int Socket::Connection::dataDown(){ /// Returns a std::string of stats, ended by a newline. /// Requires the current connector name as an argument. std::string Socket::Connection::getStats(std::string C){ - return getHost() + " " + C + " " + uint2string(time(0) - conntime) + " " + uint2string(up) + " " + uint2string(down) + "\n"; + return "S " + getHost() + " " + C + " " + uint2string(Util::epoch() - conntime) + " " + uint2string(up) + " " + uint2string(down) + "\n"; } /// Updates the downbuffer and upbuffer internal variables. @@ -347,7 +342,7 @@ bool Socket::Connection::spool(){ bool Socket::Connection::flush(){ while (upbuffer.size() > 0 && connected()){ if (!iwrite(upbuffer.get())){ - ms_sleep(10);//sleep 10ms + Util::sleep(10);//sleep 10ms } } /// \todo Provide better mechanism to prevent overbuffering. @@ -370,7 +365,7 @@ Socket::Buffer & Socket::Connection::Received(){ void Socket::Connection::SendNow(const char * data, size_t len){ while (upbuffer.size() > 0 && connected()){ if (!iwrite(upbuffer.get())){ - ms_sleep(1);//sleep 1ms if buffer full + Util::sleep(1);//sleep 1ms if buffer full } } int i = iwrite(data, len); @@ -379,7 +374,7 @@ void Socket::Connection::SendNow(const char * data, size_t len){ if (j > 0){ i += j; }else{ - ms_sleep(1);//sleep 1ms and retry + Util::sleep(1);//sleep 1ms and retry } } } diff --git a/lib/socket.h b/lib/socket.h index 4b4394b8..c5f907f7 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -44,7 +44,7 @@ namespace Socket{ std::string remotehost; ///< Stores remote host address. unsigned int up; unsigned int down; - unsigned int conntime; + long long int conntime; Buffer downbuffer; ///< Stores temporary data coming in. Buffer upbuffer; ///< Stores temporary data going out. int iread(void * buffer, int len); ///< Incremental read call. diff --git a/lib/timing.cpp b/lib/timing.cpp new file mode 100644 index 00000000..dd26db1b --- /dev/null +++ b/lib/timing.cpp @@ -0,0 +1,27 @@ +/// \file time.cpp +/// Utilities for handling time and timestamps. + +#include "timing.h" +#include //for gettimeofday +#include //for time and nanosleep + +/// Sleeps for the indicated amount of milliseconds or longer. +void Util::sleep(int ms){ + struct timespec T; + T.tv_sec = ms/1000; + T.tv_nsec = 1000*(ms%1000); + nanosleep(&T, 0); +} + +/// Gets the current time in milliseconds. +long long int Util::getMS(){ + /// \todo Possibly change to use clock_gettime - needs -lrt though... + timeval t; + gettimeofday(&t, 0); + return t.tv_sec * 1000 + t.tv_usec/1000; +} + +/// Gets the amount of seconds since 01/01/1970. +long long int Util::epoch(){ + return time(0); +} diff --git a/lib/timing.h b/lib/timing.h new file mode 100644 index 00000000..b9c43a79 --- /dev/null +++ b/lib/timing.h @@ -0,0 +1,10 @@ +/// \file time.h +/// Utilities for handling time and timestamps. + +#pragma once + +namespace Util{ + void sleep(int ms); ///< Sleeps for the indicated amount of milliseconds or longer. + long long int getMS(); ///< Gets the current time in milliseconds. + long long int epoch(); ///< Gets the amount of seconds since 01/01/1970. +}; From 74312b2e518f7dbc474ad77ce4b53710bcff03f2 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 18 Sep 2012 16:37:50 +0200 Subject: [PATCH 296/788] Fixed timing on weird systems. --- lib/Makefile.am | 2 +- lib/mist-1.0.pc.in | 2 +- lib/timing.cpp | 8 +++++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/Makefile.am b/lib/Makefile.am index 6ad1607d..d9741da4 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -2,7 +2,7 @@ AM_CPPFLAGS = $(global_CFLAGS) lib_LTLIBRARIES=libmist-1.0.la libmist_1_0_la_SOURCES=amf.h amf.cpp auth.h auth.cpp base64.h base64.cpp config.h config.cpp crypto.h crypto.cpp dtsc.h dtsc.cpp flv_tag.h flv_tag.cpp http_parser.h http_parser.cpp json.h json.cpp procs.h procs.cpp rtmpchunks.h rtmpchunks.cpp socket.h socket.cpp mp4.h mp4.cpp ftp.h ftp.cpp filesystem.h filesystem.cpp stream.h stream.cpp timing.h timing.cpp -libmist_1_0_la_LIBADD=-lssl -lcrypto +libmist_1_0_la_LIBADD=-lssl -lcrypto -lrt libmist_1_0_la_LDFLAGS = -version-info 2:0:0 pkgconfigdir = $(libdir)/pkgconfig diff --git a/lib/mist-1.0.pc.in b/lib/mist-1.0.pc.in index 79bca3fb..e7036838 100644 --- a/lib/mist-1.0.pc.in +++ b/lib/mist-1.0.pc.in @@ -7,5 +7,5 @@ Name: Mist Description: Mist Streaming Media Library Requires: openssl Version: @PACKAGE_VERSION@ -Libs: -L${libdir} -lmist-1.0 -lssl -lcrypto +Libs: -L${libdir} -lmist-1.0 -lssl -lcrypto -lrt Cflags: -I${includedir}/mist-1.0 -I${libdir}/mist-1.0/include diff --git a/lib/timing.cpp b/lib/timing.cpp index dd26db1b..cc03d0ff 100644 --- a/lib/timing.cpp +++ b/lib/timing.cpp @@ -7,6 +7,8 @@ /// Sleeps for the indicated amount of milliseconds or longer. void Util::sleep(int ms){ + if (ms < 0){return;} + if (ms > 10000){return;} struct timespec T; T.tv_sec = ms/1000; T.tv_nsec = 1000*(ms%1000); @@ -16,9 +18,9 @@ void Util::sleep(int ms){ /// Gets the current time in milliseconds. long long int Util::getMS(){ /// \todo Possibly change to use clock_gettime - needs -lrt though... - timeval t; - gettimeofday(&t, 0); - return t.tv_sec * 1000 + t.tv_usec/1000; + struct timespec t; + clock_gettime(CLOCK_REALTIME, &t); + return ((long long int)t.tv_sec) * 1000 + t.tv_nsec/1000; } /// Gets the amount of seconds since 01/01/1970. From ca25787c459677b82479326d3b0554d141586a7c Mon Sep 17 00:00:00 2001 From: Thulinma Date: Wed, 19 Sep 2012 15:31:43 +0200 Subject: [PATCH 297/788] In soviet russia, stupid mistakes fix YOU. --- lib/timing.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/timing.cpp b/lib/timing.cpp index cc03d0ff..8d7a5f97 100644 --- a/lib/timing.cpp +++ b/lib/timing.cpp @@ -11,16 +11,15 @@ void Util::sleep(int ms){ if (ms > 10000){return;} struct timespec T; T.tv_sec = ms/1000; - T.tv_nsec = 1000*(ms%1000); + T.tv_nsec = 1000000*(ms%1000); nanosleep(&T, 0); } /// Gets the current time in milliseconds. long long int Util::getMS(){ - /// \todo Possibly change to use clock_gettime - needs -lrt though... struct timespec t; clock_gettime(CLOCK_REALTIME, &t); - return ((long long int)t.tv_sec) * 1000 + t.tv_nsec/1000; + return ((long long int)t.tv_sec) * 1000 + t.tv_nsec/1000000; } /// Gets the amount of seconds since 01/01/1970. From 785fcb693b2342aa204496a68ffa83a459ae439f Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 24 Sep 2012 11:36:40 +0200 Subject: [PATCH 298/788] Implemented JSON ==/!= comparison for arrays. --- lib/json.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/json.cpp b/lib/json.cpp index 6a783bc4..de0ec66c 100644 --- a/lib/json.cpp +++ b/lib/json.cpp @@ -213,6 +213,15 @@ bool JSON::Value::operator==(const JSON::Value & rhs) const{ } return true; } + if (myType == ARRAY){ + if (arrVal.size() != rhs.arrVal.size()) return false; + int i = 0; + for (std::deque::const_iterator it = arrVal.begin(); it != arrVal.end(); ++it){ + if (*it != rhs.arrVal[i]){return false;} + i++; + } + return true; + } return true; } From 43cd6311ea0eb07d0ea02ed67b27bf94efe3c416 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 24 Sep 2012 15:01:56 +0200 Subject: [PATCH 299/788] Added FLV metadata for keyframe positions. All fake and estimated, but it works. --- lib/flv_tag.cpp | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index a025563b..22ee3b92 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -453,6 +453,21 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){ if (S.metadata.isMember("length")){ amfdata.getContentP(1)->addContent(AMF::Object("duration", S.metadata["length"].asInt(), AMF::AMF0_NUMBER)); amfdata.getContentP(1)->addContent(AMF::Object("moovPosition", 40, AMF::AMF0_NUMBER)); + AMF::Object keys("keyframes", AMF::AMF0_OBJECT); + keys.addContent(AMF::Object("filepositions", AMF::AMF0_STRICT_ARRAY)); + keys.addContent(AMF::Object("times", AMF::AMF0_STRICT_ARRAY)); + int total_byterate = 0; + if (S.metadata.isMember("video")){ + total_byterate += S.metadata["video"]["bps"].asInt(); + } + if (S.metadata.isMember("audio")){ + total_byterate += S.metadata["audio"]["bps"].asInt(); + } + for (int i = 0; i < S.metadata["length"].asInt(); ++i){//for each second in the file + keys.getContentP(0)->addContent(AMF::Object("", i * total_byterate, AMF::AMF0_NUMBER));//multiply by byterate for fake byte positions + keys.getContentP(1)->addContent(AMF::Object("", i, AMF::AMF0_NUMBER));//seconds + } + amfdata.getContentP(1)->addContent(keys); } if (S.metadata.isMember("video")){ amfdata.getContentP(1)->addContent(AMF::Object("hasVideo", 1, AMF::AMF0_BOOL)); @@ -475,7 +490,7 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){ amfdata.getContentP(1)->addContent(AMF::Object("videoframerate", (double)S.metadata["video"]["fpks"].asInt() / 1000.0, AMF::AMF0_NUMBER)); } if (S.metadata["video"].isMember("bps")){ - amfdata.getContentP(1)->addContent(AMF::Object("videodatarate", ((double)S.metadata["video"]["bps"].asInt() * 8.0) / 1024.0, AMF::AMF0_NUMBER)); + amfdata.getContentP(1)->addContent(AMF::Object("videodatarate", (double)S.metadata["video"]["bps"].asInt() * 128.0, AMF::AMF0_NUMBER)); } } if (S.metadata.isMember("audio")){ @@ -497,7 +512,7 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){ amfdata.getContentP(1)->addContent(AMF::Object("audiosamplesize", S.metadata["audio"]["size"].asInt(), AMF::AMF0_NUMBER)); } if (S.metadata["audio"].isMember("bps")){ - amfdata.getContentP(1)->addContent(AMF::Object("audiodatarate", ((double)S.metadata["audio"]["bps"].asInt() * 8.0) / 1024.0, AMF::AMF0_NUMBER)); + amfdata.getContentP(1)->addContent(AMF::Object("audiodatarate", (double)S.metadata["audio"]["bps"].asInt() * 128.0, AMF::AMF0_NUMBER)); } } AMF::Object trinfo = AMF::Object("trackinfo", AMF::AMF0_STRICT_ARRAY); From 2c7edafc42fdf44f76d7b6578de601e6366e0a69 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Tue, 11 Sep 2012 16:26:18 +0200 Subject: [PATCH 300/788] New skeleton for box code --- lib/mp4.h | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/lib/mp4.h b/lib/mp4.h index d7354ad7..f801d51f 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -10,27 +10,29 @@ namespace MP4{ class Box { public: Box(); - Box(uint32_t BoxType); - Box(uint8_t * Content, uint32_t length); + Box(char* boxType); + Box(std::string & newData); ~Box(); - void SetBoxType(uint32_t BoxType); - uint32_t GetBoxType(); - void SetPayload(uint32_t Size, uint8_t * Data, uint32_t Index = 0); - uint32_t GetPayloadSize(); - uint8_t * GetPayload(); - uint8_t * GetPayload(uint32_t Index, uint32_t & Size); - uint32_t GetBoxedDataSize(); - uint8_t * GetBoxedData( ); - static uint8_t * uint32_to_uint8( uint32_t data ); - static uint8_t * uint16_to_uint8( uint16_t data ); - static uint8_t * uint8_to_uint8( uint8_t data ); - void ResetPayload( ); + std::string getType(); + bool isType( char* boxType ); + bool read(std::string & newData); + size_t boxedSize(); + size_t payloadSize(); + std::string & asBox(); + void clear(); std::string toPrettyString(int indent = 0); - private: - uint8_t * Payload; - uint32_t PayloadSize; + protected: + void setInt8( char data, size_t index = 0); + void setInt16( short data, size_t index = 0); + void setInt32( long data, size_t index = 0); + void setInt64( long long int data, size_t index = 0); + void setString(std::string data, size_t index = 0); + void setString(char* data, size_t size, size_t index = 0); + std::string data; };//Box Class +//Tot hier rewritten + struct abst_serverentry { std::string ServerBaseUrl; };//abst_serverentry From 07faa64075177206d9f09df386afc187de086b96 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Mon, 17 Sep 2012 11:53:17 +0200 Subject: [PATCH 301/788] MP4::Box rewritten --- lib/mp4.cpp | 842 +++++++++++++++++++++++++++------------------------- lib/mp4.h | 16 +- 2 files changed, 440 insertions(+), 418 deletions(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index c24c9e90..08fc7b91 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -7,443 +7,465 @@ /// Contains all MP4 format related code. namespace MP4{ -Box::Box() { - Payload = (uint8_t *)malloc(8); - PayloadSize = 0; -} - -Box::Box(uint32_t BoxType) { - Payload = (uint8_t *)malloc(8); - SetBoxType(BoxType); - PayloadSize = 0; -} - -Box::Box(uint8_t * Content, uint32_t length) { - PayloadSize = length-8; - Payload = (uint8_t *)malloc(length); - memcpy(Payload, Content, length); -} - -Box::~Box() { - if (Payload) free(Payload); -} - -void Box::SetBoxType(uint32_t BoxType) { - ((unsigned int*)Payload)[1] = htonl(BoxType); -} - -uint32_t Box::GetBoxType() { - return ntohl(((unsigned int*)Payload)[1]); -} - -void Box::SetPayload(uint32_t Size, uint8_t * Data, uint32_t Index) { - if ( Index + Size > PayloadSize ) { - PayloadSize = Index + Size; - ((unsigned int*)Payload)[0] = htonl(PayloadSize+8); - Payload = (uint8_t *)realloc(Payload, PayloadSize + 8); + Box::Box(size_t size) { + data.resize( size + 8 ); + memset( (char*)data.c_str(), 0, size + 8 ); } - memcpy(Payload + 8 + Index, Data, Size); -} -uint32_t Box::GetPayloadSize() { - return PayloadSize; -} - -uint8_t * Box::GetPayload() { - return Payload+8; -} - -uint8_t * Box::GetPayload(uint32_t Index, uint32_t & Size) { - if(Index > PayloadSize) {Size = 0;} - if(Index + Size > PayloadSize) { Size = PayloadSize - Index; } - return Payload + 8 + Index; -} - -uint32_t Box::GetBoxedDataSize() { - return ntohl(((unsigned int*)Payload)[0]); -} - -uint8_t * Box::GetBoxedData( ) { - return Payload; -} - - -uint8_t * Box::uint32_to_uint8( uint32_t data ) { - uint8_t * temp = new uint8_t[4]; - temp[0] = (data >> 24) & 0x000000FF; - temp[1] = (data >> 16 ) & 0x000000FF; - temp[2] = (data >> 8 ) & 0x000000FF; - temp[3] = (data ) & 0x000000FF; - return temp; -} - -uint8_t * Box::uint16_to_uint8( uint16_t data ) { - uint8_t * temp = new uint8_t[2]; - temp[0] = (data >> 8) & 0x00FF; - temp[1] = (data ) & 0x00FF; - return temp; -} - -uint8_t * Box::uint8_to_uint8( uint8_t data ) { - uint8_t * temp = new uint8_t[1]; - temp[0] = data; - return temp; -} - -void Box::ResetPayload( ) { - PayloadSize = 0; - Payload = (uint8_t *)realloc(Payload, PayloadSize + 8); - ((unsigned int*)Payload)[0] = htonl(0); -} - -std::string Box::toPrettyString(int indent){ - return std::string(indent, ' ')+"Unimplemented pretty-printing for this box"; -} - - -void ABST::SetBootstrapVersion( uint32_t Version ) { - curBootstrapInfoVersion = Version; -} - -void ABST::SetProfile( uint8_t Profile ) { - curProfile = Profile; -} - -void ABST::SetLive( bool Live ) { - isLive = Live; -} - -void ABST::SetUpdate( bool Update ) { - isUpdate = Update; -} - -void ABST::SetTimeScale( uint32_t Scale ) { - curTimeScale = Scale; -} - -void ABST::SetMediaTime( uint32_t Time ) { - curMediatime = Time; -} - -void ABST::SetSMPTE( uint32_t Smpte ) { - curSMPTE = Smpte; -} - -void ABST::SetMovieIdentifier( std::string Identifier ) { - curMovieIdentifier = Identifier; -} - -void ABST::SetDRM( std::string Drm ) { - curDRM = Drm; -} - -void ABST::SetMetaData( std::string MetaData ) { - curMetaData = MetaData; -} - -void ABST::AddServerEntry( std::string Url, uint32_t Offset ) { - if(Offset >= Servers.size()) { - Servers.resize(Offset+1); + Box::Box( char* BoxType, size_t size ) { + data.resize( size + 8 ); + memset( (char*)data.c_str(), 0, size + 8 ); + memcpy( (char*)data.c_str() + 4, BoxType, 4 ); } - Servers[Offset].ServerBaseUrl = Url; -} -void ABST::AddQualityEntry( std::string Quality, uint32_t Offset ) { - if(Offset >= Qualities.size()) { - Qualities.resize(Offset+1); - } - Qualities[Offset].QualityModifier = Quality; -} - -void ABST::AddSegmentRunTable( Box * newSegment, uint32_t Offset ) { - if( Offset >= SegmentRunTables.size() ) { - SegmentRunTables.resize(Offset+1); - } - if( SegmentRunTables[Offset] ) { - delete SegmentRunTables[Offset]; - } - SegmentRunTables[Offset] = newSegment; -} - -void ABST::AddFragmentRunTable( Box * newFragment, uint32_t Offset ) { - if( Offset >= FragmentRunTables.size() ) { - FragmentRunTables.resize(Offset+1); - } - if( FragmentRunTables[Offset] ) { - delete FragmentRunTables[Offset]; - } - FragmentRunTables[Offset] = newFragment; -} - -void ABST::SetDefaults( ) { - SetProfile( ); - SetLive( ); - SetUpdate( ); - SetTimeScale( ); - SetMediaTime( ); - SetSMPTE( ); - SetMovieIdentifier( ); - SetDRM( ); - SetMetaData( ); - SetVersion( ); -} - -void ABST::SetVersion( bool NewVersion) { - Version = NewVersion; -} - -void ABST::SetReserved( ) { - SetPayload((uint32_t)4,Box::uint32_to_uint8(0)); -} - -void ABST::WriteContent( ) { - Box * current; - std::string serializedServers = ""; - std::string serializedQualities = ""; - std::string serializedSegments = ""; - std::string serializedFragments = ""; - int SegmentAmount = 0; - int FragmentAmount = 0; - uint8_t * temp = new uint8_t[1]; - - ResetPayload( ); - SetReserved( ); - - for( uint32_t i = 0; i < Servers.size(); i++ ) { - serializedServers.append(Servers[i].ServerBaseUrl.c_str()); - serializedServers += '\0'; - } - for( uint32_t i = 0; i < Qualities.size(); i++ ) { - serializedQualities.append(Qualities[i].QualityModifier.c_str()); - serializedQualities += '\0'; - } - for( uint32_t i = 0; i < SegmentRunTables.size(); i++ ) { - current=SegmentRunTables[i]; - if( current ) { - SegmentAmount ++; - serializedSegments.append((char*)current->GetBoxedData(),current->GetBoxedDataSize()); + Box::Box( std::string & newData ) { + if( !read( newData ) ) { + clear(); } } - for( uint32_t i = 0; i < FragmentRunTables.size(); i++ ) { - current=FragmentRunTables[i]; - if( current ) { - FragmentAmount ++; - serializedFragments.append((char*)current->GetBoxedData(),current->GetBoxedDataSize()); + + Box::~Box() { } + + std::string Box::getType() { + return data.substr(4,4); + } + + bool Box::isType( char* boxType ) { + return !memcmp( boxType, data.c_str() + 4, 4 ); + } + + bool Box::read(std::string & newData) { + if( newData.size() > 4 ) { + size_t size = ntohl( ((int*)newData.c_str())[0] ); + if( newData.size() > size + 8 ) { + data = newData.substr( 0, size + 8 ); + newData.erase( 0, size + 8 ); + return true; + } + } + return false; + } + + size_t Box::boxedSize() { + return data.size(); + } + + size_t Box::payloadSize() { + return data.size() - 8; + } + + std::string & Box::asBox() { + ((int*)data.c_str())[0] = htonl( data.size() ); + return data; + } + + void Box::clear() { + data.resize( 8 ); + } + + std::string Box::toPrettyString(int indent){ + return std::string(indent, ' ')+"Unimplemented pretty-printing for this box"; + } + + void Box::setInt8( char newData, size_t index ) { + index += 8; + if( index > data.size() ) { + data.resize( index ); + } + data[index] = newData; + } + + void Box::setInt16( short newData, size_t index ) { + index += 8; + if( index + 1 > data.size() ) { + data.resize( index + 1 ); + } + newData = htons( newData ); + memcpy( (char*)data.c_str() + index, (char*)newData, 2 ); + } + + void Box::setInt32( long newData, size_t index ) { + index += 8; + if( index + 3 > data.size() ) { + data.resize( index + 3 ); + } + newData = htonl( newData ); + memcpy( (char*)data.c_str() + index, (char*)newData, 4 ); + } + + void Box::setInt64( long long int newData, size_t index ) { + index += 8; + if( index + 7 > data.size() ) { + data.resize( index + 7 ); + } + data[index] = ( newData * 0xFF00000000000000 ) >> 56; + data[index+1] = ( newData * 0x00FF000000000000 ) >> 48; + data[index+2] = ( newData * 0x0000FF0000000000 ) >> 40; + data[index+3] = ( newData * 0x000000FF00000000 ) >> 32; + data[index+4] = ( newData * 0x00000000FF000000 ) >> 24; + data[index+5] = ( newData * 0x0000000000FF0000 ) >> 16; + data[index+6] = ( newData * 0x000000000000FF00 ) >> 8; + data[index+7] = ( newData * 0x00000000000000FF ); + } + + void Box::setString(std::string newData, size_t index ) { + setString( (char*)newData.c_str(), newData.size(), index ); + } + + void Box::setString(char* newData, size_t size, size_t index ) { + index += 8; + if( index + size > data.size() ) { + data.resize( index + size ); + } + memcpy( (char*)data.c_str() + index, newData, size ); + } + + +/* + void ABST::SetBootstrapVersion( uint32_t Version ) { + curBootstrapInfoVersion = Version; + } + + void ABST::SetProfile( uint8_t Profile ) { + curProfile = Profile; + } + + void ABST::SetLive( bool Live ) { + isLive = Live; + } + + void ABST::SetUpdate( bool Update ) { + isUpdate = Update; + } + + void ABST::SetTimeScale( uint32_t Scale ) { + curTimeScale = Scale; + } + + void ABST::SetMediaTime( uint32_t Time ) { + curMediatime = Time; + } + + void ABST::SetSMPTE( uint32_t Smpte ) { + curSMPTE = Smpte; + } + + void ABST::SetMovieIdentifier( std::string Identifier ) { + curMovieIdentifier = Identifier; + } + + void ABST::SetDRM( std::string Drm ) { + curDRM = Drm; + } + + void ABST::SetMetaData( std::string MetaData ) { + curMetaData = MetaData; + } + + void ABST::AddServerEntry( std::string Url, uint32_t Offset ) { + if(Offset >= Servers.size()) { + Servers.resize(Offset+1); + } + Servers[Offset].ServerBaseUrl = Url; + } + + void ABST::AddQualityEntry( std::string Quality, uint32_t Offset ) { + if(Offset >= Qualities.size()) { + Qualities.resize(Offset+1); + } + Qualities[Offset].QualityModifier = Quality; + } + + void ABST::AddSegmentRunTable( Box * newSegment, uint32_t Offset ) { + if( Offset >= SegmentRunTables.size() ) { + SegmentRunTables.resize(Offset+1); + } + if( SegmentRunTables[Offset] ) { + delete SegmentRunTables[Offset]; + } + SegmentRunTables[Offset] = newSegment; + } + + void ABST::AddFragmentRunTable( Box * newFragment, uint32_t Offset ) { + if( Offset >= FragmentRunTables.size() ) { + FragmentRunTables.resize(Offset+1); + } + if( FragmentRunTables[Offset] ) { + delete FragmentRunTables[Offset]; + } + FragmentRunTables[Offset] = newFragment; + } + + void ABST::SetDefaults( ) { + SetProfile( ); + SetLive( ); + SetUpdate( ); + SetTimeScale( ); + SetMediaTime( ); + SetSMPTE( ); + SetMovieIdentifier( ); + SetDRM( ); + SetMetaData( ); + SetVersion( ); + } + + void ABST::SetVersion( bool NewVersion) { + Version = NewVersion; + } + + void ABST::SetReserved( ) { + SetInt32(0); + } + + void ABST::WriteContent( ) { + Box * current; + std::string serializedServers = ""; + std::string serializedQualities = ""; + std::string serializedSegments = ""; + std::string serializedFragments = ""; + int SegmentAmount = 0; + int FragmentAmount = 0; + uint8_t * temp = new uint8_t[1]; + + clear( ); + SetReserved( ); + + for( uint32_t i = 0; i < Servers.size(); i++ ) { + serializedServers.append(Servers[i].ServerBaseUrl.c_str()); + serializedServers += '\0'; + } + for( uint32_t i = 0; i < Qualities.size(); i++ ) { + serializedQualities.append(Qualities[i].QualityModifier.c_str()); + serializedQualities += '\0'; + } + for( uint32_t i = 0; i < SegmentRunTables.size(); i++ ) { + current=SegmentRunTables[i]; + if( current ) { + SegmentAmount ++; + serializedSegments.append((char*)current->GetBoxedData(),current->GetBoxedDataSize()); + } + } + for( uint32_t i = 0; i < FragmentRunTables.size(); i++ ) { + current=FragmentRunTables[i]; + if( current ) { + FragmentAmount ++; + serializedFragments.append((char*)current->GetBoxedData(),current->GetBoxedDataSize()); + } + } + uint32_t OffsetServerEntryCount = 29 + curMovieIdentifier.size() + 1; + uint32_t OffsetQualityEntryCount = OffsetServerEntryCount + 1 + serializedServers.size(); + uint32_t OffsetDrmData = OffsetQualityEntryCount + 1 + serializedQualities.size(); + uint32_t OffsetMetaData = OffsetDrmData + curDRM.size() + 1; + uint32_t OffsetSegmentRuntableCount = OffsetMetaData + curMetaData.size() + 1; + uint32_t OffsetFragmentRuntableCount = OffsetSegmentRuntableCount + 1 + serializedSegments.size(); + + temp[0] = 0 + ( curProfile << 6 ) + ( (uint8_t)isLive << 7 ) + ( (uint8_t)isUpdate << 7 ); + + SetPayload((uint32_t)serializedFragments.size(),(uint8_t*)serializedFragments.c_str(),OffsetFragmentRuntableCount+1); + SetPayload((uint32_t)1,Box::uint8_to_uint8(FragmentAmount),OffsetFragmentRuntableCount); + SetPayload((uint32_t)serializedSegments.size(),(uint8_t*)serializedSegments.c_str(),OffsetSegmentRuntableCount+1); + SetPayload((uint32_t)1,Box::uint8_to_uint8(SegmentAmount),OffsetSegmentRuntableCount); + SetPayload((uint32_t)curMetaData.size()+1,(uint8_t*)curMetaData.c_str(),OffsetMetaData); + SetPayload((uint32_t)curDRM.size()+1,(uint8_t*)curDRM.c_str(),OffsetDrmData); + SetPayload((uint32_t)serializedQualities.size(),(uint8_t*)serializedQualities.c_str(),OffsetQualityEntryCount+1); + SetPayload((uint32_t)1,Box::uint8_to_uint8(Qualities.size()),OffsetQualityEntryCount); + SetPayload((uint32_t)serializedServers.size(),(uint8_t*)serializedServers.c_str(),OffsetServerEntryCount+1); + SetPayload((uint32_t)1,Box::uint8_to_uint8(Servers.size()),OffsetServerEntryCount); + SetPayload((uint32_t)curMovieIdentifier.size()+1,(uint8_t*)curMovieIdentifier.c_str(),29);//+1 for \0-terminated string... + SetPayload((uint32_t)4,Box::uint32_to_uint8(curSMPTE),25); + SetPayload((uint32_t)4,Box::uint32_to_uint8(0),21); + SetPayload((uint32_t)4,Box::uint32_to_uint8(curMediatime),17); + SetPayload((uint32_t)4,Box::uint32_to_uint8(0),13); + SetPayload((uint32_t)4,Box::uint32_to_uint8(curTimeScale),9); + SetPayload((uint32_t)1,temp,8); + SetPayload((uint32_t)4,Box::uint32_to_uint8(curBootstrapInfoVersion),4); + } + + std::string ABST::toPrettyString(int indent){ + std::string r; + r += std::string(indent, ' ')+"Bootstrap Info\n"; + if (isUpdate){ + r += std::string(indent+1, ' ')+"Update\n"; + }else{ + r += std::string(indent+1, ' ')+"Replacement or new table\n"; + } + if (isLive){ + r += std::string(indent+1, ' ')+"Live\n"; + }else{ + r += std::string(indent+1, ' ')+"Recorded\n"; + } + r += std::string(indent+1, ' ')+"Profile "+JSON::Value((long long int)curProfile).asString()+"\n"; + r += std::string(indent+1, ' ')+"Timescale "+JSON::Value((long long int)curTimeScale).asString()+"\n"; + r += std::string(indent+1, ' ')+"CurrMediaTime "+JSON::Value((long long int)curMediatime).asString()+"\n"; + r += std::string(indent+1, ' ')+"Segment Run Tables "+JSON::Value((long long int)SegmentRunTables.size()).asString()+"\n"; + for( uint32_t i = 0; i < SegmentRunTables.size(); i++ ) { + r += ((ASRT*)SegmentRunTables[i])->toPrettyString(indent+2)+"\n"; + } + r += std::string(indent+1, ' ')+"Fragment Run Tables "+JSON::Value((long long int)FragmentRunTables.size()).asString()+"\n"; + for( uint32_t i = 0; i < FragmentRunTables.size(); i++ ) { + r += ((AFRT*)FragmentRunTables[i])->toPrettyString(indent+2)+"\n"; + } + return r; + } + + void AFRT::SetUpdate( bool Update ) { + isUpdate = Update; + } + + void AFRT::AddQualityEntry( std::string Quality, uint32_t Offset ) { + if(Offset >= QualitySegmentUrlModifiers.size()) { + QualitySegmentUrlModifiers.resize(Offset+1); + } + QualitySegmentUrlModifiers[Offset] = Quality; + } + + void AFRT::AddFragmentRunEntry( uint32_t FirstFragment, uint32_t FirstFragmentTimestamp, uint32_t FragmentsDuration, uint8_t Discontinuity, uint32_t Offset ) { + if( Offset >= FragmentRunEntryTable.size() ) { + FragmentRunEntryTable.resize(Offset+1); + } + FragmentRunEntryTable[Offset].FirstFragment = FirstFragment; + FragmentRunEntryTable[Offset].FirstFragmentTimestamp = FirstFragmentTimestamp; + FragmentRunEntryTable[Offset].FragmentDuration = FragmentsDuration; + if( FragmentsDuration == 0) { + FragmentRunEntryTable[Offset].DiscontinuityIndicator = Discontinuity; } } - uint32_t OffsetServerEntryCount = 29 + curMovieIdentifier.size() + 1; - uint32_t OffsetQualityEntryCount = OffsetServerEntryCount + 1 + serializedServers.size(); - uint32_t OffsetDrmData = OffsetQualityEntryCount + 1 + serializedQualities.size(); - uint32_t OffsetMetaData = OffsetDrmData + curDRM.size() + 1; - uint32_t OffsetSegmentRuntableCount = OffsetMetaData + curMetaData.size() + 1; - uint32_t OffsetFragmentRuntableCount = OffsetSegmentRuntableCount + 1 + serializedSegments.size(); - - temp[0] = 0 + ( curProfile << 6 ) + ( (uint8_t)isLive << 7 ) + ( (uint8_t)isUpdate << 7 ); - - SetPayload((uint32_t)serializedFragments.size(),(uint8_t*)serializedFragments.c_str(),OffsetFragmentRuntableCount+1); - SetPayload((uint32_t)1,Box::uint8_to_uint8(FragmentAmount),OffsetFragmentRuntableCount); - SetPayload((uint32_t)serializedSegments.size(),(uint8_t*)serializedSegments.c_str(),OffsetSegmentRuntableCount+1); - SetPayload((uint32_t)1,Box::uint8_to_uint8(SegmentAmount),OffsetSegmentRuntableCount); - SetPayload((uint32_t)curMetaData.size()+1,(uint8_t*)curMetaData.c_str(),OffsetMetaData); - SetPayload((uint32_t)curDRM.size()+1,(uint8_t*)curDRM.c_str(),OffsetDrmData); - SetPayload((uint32_t)serializedQualities.size(),(uint8_t*)serializedQualities.c_str(),OffsetQualityEntryCount+1); - SetPayload((uint32_t)1,Box::uint8_to_uint8(Qualities.size()),OffsetQualityEntryCount); - SetPayload((uint32_t)serializedServers.size(),(uint8_t*)serializedServers.c_str(),OffsetServerEntryCount+1); - SetPayload((uint32_t)1,Box::uint8_to_uint8(Servers.size()),OffsetServerEntryCount); - SetPayload((uint32_t)curMovieIdentifier.size()+1,(uint8_t*)curMovieIdentifier.c_str(),29);//+1 for \0-terminated string... - SetPayload((uint32_t)4,Box::uint32_to_uint8(curSMPTE),25); - SetPayload((uint32_t)4,Box::uint32_to_uint8(0),21); - SetPayload((uint32_t)4,Box::uint32_to_uint8(curMediatime),17); - SetPayload((uint32_t)4,Box::uint32_to_uint8(0),13); - SetPayload((uint32_t)4,Box::uint32_to_uint8(curTimeScale),9); - SetPayload((uint32_t)1,temp,8); - SetPayload((uint32_t)4,Box::uint32_to_uint8(curBootstrapInfoVersion),4); -} -std::string ABST::toPrettyString(int indent){ - std::string r; - r += std::string(indent, ' ')+"Bootstrap Info\n"; - if (isUpdate){ - r += std::string(indent+1, ' ')+"Update\n"; - }else{ - r += std::string(indent+1, ' ')+"Replacement or new table\n"; + void AFRT::SetDefaults( ) { + SetUpdate( ); + SetTimeScale( ); } - if (isLive){ - r += std::string(indent+1, ' ')+"Live\n"; - }else{ - r += std::string(indent+1, ' ')+"Recorded\n"; - } - r += std::string(indent+1, ' ')+"Profile "+JSON::Value((long long int)curProfile).asString()+"\n"; - r += std::string(indent+1, ' ')+"Timescale "+JSON::Value((long long int)curTimeScale).asString()+"\n"; - r += std::string(indent+1, ' ')+"CurrMediaTime "+JSON::Value((long long int)curMediatime).asString()+"\n"; - r += std::string(indent+1, ' ')+"Segment Run Tables "+JSON::Value((long long int)SegmentRunTables.size()).asString()+"\n"; - for( uint32_t i = 0; i < SegmentRunTables.size(); i++ ) { - r += ((ASRT*)SegmentRunTables[i])->toPrettyString(indent+2)+"\n"; - } - r += std::string(indent+1, ' ')+"Fragment Run Tables "+JSON::Value((long long int)FragmentRunTables.size()).asString()+"\n"; - for( uint32_t i = 0; i < FragmentRunTables.size(); i++ ) { - r += ((AFRT*)FragmentRunTables[i])->toPrettyString(indent+2)+"\n"; - } - return r; -} -void AFRT::SetUpdate( bool Update ) { - isUpdate = Update; -} - -void AFRT::AddQualityEntry( std::string Quality, uint32_t Offset ) { - if(Offset >= QualitySegmentUrlModifiers.size()) { - QualitySegmentUrlModifiers.resize(Offset+1); + void AFRT::SetTimeScale( uint32_t Scale ) { + curTimeScale = Scale; } - QualitySegmentUrlModifiers[Offset] = Quality; -} -void AFRT::AddFragmentRunEntry( uint32_t FirstFragment, uint32_t FirstFragmentTimestamp, uint32_t FragmentsDuration, uint8_t Discontinuity, uint32_t Offset ) { - if( Offset >= FragmentRunEntryTable.size() ) { - FragmentRunEntryTable.resize(Offset+1); - } - FragmentRunEntryTable[Offset].FirstFragment = FirstFragment; - FragmentRunEntryTable[Offset].FirstFragmentTimestamp = FirstFragmentTimestamp; - FragmentRunEntryTable[Offset].FragmentDuration = FragmentsDuration; - if( FragmentsDuration == 0) { - FragmentRunEntryTable[Offset].DiscontinuityIndicator = Discontinuity; - } -} - -void AFRT::SetDefaults( ) { - SetUpdate( ); - SetTimeScale( ); -} - -void AFRT::SetTimeScale( uint32_t Scale ) { - curTimeScale = Scale; -} - -void AFRT::WriteContent( ) { - std::string serializedQualities = ""; - std::string serializedFragmentEntries = ""; - ResetPayload( ); - - for( uint32_t i = 0; i < QualitySegmentUrlModifiers.size(); i++ ) { - serializedQualities.append(QualitySegmentUrlModifiers[i].c_str()); - serializedQualities += '\0'; - } - for( uint32_t i = 0; i < FragmentRunEntryTable.size(); i ++ ) { - serializedFragmentEntries.append((char*)Box::uint32_to_uint8(FragmentRunEntryTable[i].FirstFragment),4); - serializedFragmentEntries.append((char*)Box::uint32_to_uint8(0),4); - serializedFragmentEntries.append((char*)Box::uint32_to_uint8(FragmentRunEntryTable[i].FirstFragmentTimestamp),4); - serializedFragmentEntries.append((char*)Box::uint32_to_uint8(FragmentRunEntryTable[i].FragmentDuration),4); - if(FragmentRunEntryTable[i].FragmentDuration == 0) { - serializedFragmentEntries.append((char*)Box::uint8_to_uint8(FragmentRunEntryTable[i].DiscontinuityIndicator),1); + void AFRT::WriteContent( ) { + std::string serializedQualities = ""; + std::string serializedFragmentEntries = ""; + clear(); + + for( uint32_t i = 0; i < QualitySegmentUrlModifiers.size(); i++ ) { + serializedQualities.append(QualitySegmentUrlModifiers[i].c_str()); + serializedQualities += '\0'; } + for( uint32_t i = 0; i < FragmentRunEntryTable.size(); i ++ ) { + serializedFragmentEntries.append((char*)Box::uint32_to_uint8(FragmentRunEntryTable[i].FirstFragment),4); + serializedFragmentEntries.append((char*)Box::uint32_to_uint8(0),4); + serializedFragmentEntries.append((char*)Box::uint32_to_uint8(FragmentRunEntryTable[i].FirstFragmentTimestamp),4); + serializedFragmentEntries.append((char*)Box::uint32_to_uint8(FragmentRunEntryTable[i].FragmentDuration),4); + if(FragmentRunEntryTable[i].FragmentDuration == 0) { + serializedFragmentEntries.append((char*)Box::uint8_to_uint8(FragmentRunEntryTable[i].DiscontinuityIndicator),1); + } + } + + uint32_t OffsetFragmentRunEntryCount = 9 + serializedQualities.size(); + + SetPayload((uint32_t)serializedFragmentEntries.size(),(uint8_t*)serializedFragmentEntries.c_str(),OffsetFragmentRunEntryCount+4); + SetPayload((uint32_t)4,Box::uint32_to_uint8(FragmentRunEntryTable.size()),OffsetFragmentRunEntryCount); + SetPayload((uint32_t)serializedQualities.size(),(uint8_t*)serializedQualities.c_str(),9); + SetPayload((uint32_t)1,Box::uint8_to_uint8(QualitySegmentUrlModifiers.size()),8); + SetPayload((uint32_t)4,Box::uint32_to_uint8(curTimeScale),4); + SetPayload((uint32_t)4,Box::uint32_to_uint8((isUpdate ? 1 : 0))); } - - uint32_t OffsetFragmentRunEntryCount = 9 + serializedQualities.size(); - - SetPayload((uint32_t)serializedFragmentEntries.size(),(uint8_t*)serializedFragmentEntries.c_str(),OffsetFragmentRunEntryCount+4); - SetPayload((uint32_t)4,Box::uint32_to_uint8(FragmentRunEntryTable.size()),OffsetFragmentRunEntryCount); - SetPayload((uint32_t)serializedQualities.size(),(uint8_t*)serializedQualities.c_str(),9); - SetPayload((uint32_t)1,Box::uint8_to_uint8(QualitySegmentUrlModifiers.size()),8); - SetPayload((uint32_t)4,Box::uint32_to_uint8(curTimeScale),4); - SetPayload((uint32_t)4,Box::uint32_to_uint8((isUpdate ? 1 : 0))); -} -std::string AFRT::toPrettyString(int indent){ - std::string r; - r += std::string(indent, ' ')+"Fragment Run Table\n"; - if (isUpdate){ - r += std::string(indent+1, ' ')+"Update\n"; - }else{ - r += std::string(indent+1, ' ')+"Replacement or new table\n"; + std::string AFRT::toPrettyString(int indent){ + std::string r; + r += std::string(indent, ' ')+"Fragment Run Table\n"; + if (isUpdate){ + r += std::string(indent+1, ' ')+"Update\n"; + }else{ + r += std::string(indent+1, ' ')+"Replacement or new table\n"; + } + r += std::string(indent+1, ' ')+"Timescale "+JSON::Value((long long int)curTimeScale).asString()+"\n"; + r += std::string(indent+1, ' ')+"Qualities "+JSON::Value((long long int)QualitySegmentUrlModifiers.size()).asString()+"\n"; + for( uint32_t i = 0; i < QualitySegmentUrlModifiers.size(); i++ ) { + r += std::string(indent+2, ' ')+"\""+QualitySegmentUrlModifiers[i]+"\"\n"; + } + r += std::string(indent+1, ' ')+"Fragments "+JSON::Value((long long int)FragmentRunEntryTable.size()).asString()+"\n"; + for( uint32_t i = 0; i < FragmentRunEntryTable.size(); i ++ ) { + r += std::string(indent+2, ' ')+"Duration "+JSON::Value((long long int)FragmentRunEntryTable[i].FragmentDuration).asString()+", starting at "+JSON::Value((long long int)FragmentRunEntryTable[i].FirstFragment).asString()+" @ "+JSON::Value((long long int)FragmentRunEntryTable[i].FirstFragmentTimestamp).asString(); + } + return r; } - r += std::string(indent+1, ' ')+"Timescale "+JSON::Value((long long int)curTimeScale).asString()+"\n"; - r += std::string(indent+1, ' ')+"Qualities "+JSON::Value((long long int)QualitySegmentUrlModifiers.size()).asString()+"\n"; - for( uint32_t i = 0; i < QualitySegmentUrlModifiers.size(); i++ ) { - r += std::string(indent+2, ' ')+"\""+QualitySegmentUrlModifiers[i]+"\"\n"; - } - r += std::string(indent+1, ' ')+"Fragments "+JSON::Value((long long int)FragmentRunEntryTable.size()).asString()+"\n"; - for( uint32_t i = 0; i < FragmentRunEntryTable.size(); i ++ ) { - r += std::string(indent+2, ' ')+"Duration "+JSON::Value((long long int)FragmentRunEntryTable[i].FragmentDuration).asString()+", starting at "+JSON::Value((long long int)FragmentRunEntryTable[i].FirstFragment).asString()+" @ "+JSON::Value((long long int)FragmentRunEntryTable[i].FirstFragmentTimestamp).asString(); - } - return r; -} -void ASRT::SetUpdate( bool Update ) { - isUpdate = Update; -} - -void ASRT::AddQualityEntry( std::string Quality, uint32_t Offset ) { - if(Offset >= QualitySegmentUrlModifiers.size()) { - QualitySegmentUrlModifiers.resize(Offset+1); + void ASRT::SetUpdate( bool Update ) { + isUpdate = Update; } - QualitySegmentUrlModifiers[Offset] = Quality; -} -void ASRT::AddSegmentRunEntry( uint32_t FirstSegment, uint32_t FragmentsPerSegment, uint32_t Offset ) { - if( Offset >= SegmentRunEntryTable.size() ) { - SegmentRunEntryTable.resize(Offset+1); + void ASRT::AddQualityEntry( std::string Quality, uint32_t Offset ) { + if(Offset >= QualitySegmentUrlModifiers.size()) { + QualitySegmentUrlModifiers.resize(Offset+1); + } + QualitySegmentUrlModifiers[Offset] = Quality; } - SegmentRunEntryTable[Offset].FirstSegment = FirstSegment; - SegmentRunEntryTable[Offset].FragmentsPerSegment = FragmentsPerSegment; -} -void ASRT::SetVersion( bool NewVersion ) { - Version = NewVersion; -} + void ASRT::AddSegmentRunEntry( uint32_t FirstSegment, uint32_t FragmentsPerSegment, uint32_t Offset ) { + if( Offset >= SegmentRunEntryTable.size() ) { + SegmentRunEntryTable.resize(Offset+1); + } + SegmentRunEntryTable[Offset].FirstSegment = FirstSegment; + SegmentRunEntryTable[Offset].FragmentsPerSegment = FragmentsPerSegment; + } -void ASRT::SetDefaults( ) { - SetUpdate( ); -} + void ASRT::SetVersion( bool NewVersion ) { + Version = NewVersion; + } -void ASRT::WriteContent( ) { - std::string serializedQualities = ""; - ResetPayload( ); - - for( uint32_t i = 0; i < QualitySegmentUrlModifiers.size(); i++ ) { - serializedQualities.append(QualitySegmentUrlModifiers[i].c_str()); - serializedQualities += '\0'; + void ASRT::SetDefaults( ) { + SetUpdate( ); } - - uint32_t OffsetSegmentRunEntryCount = 5 + serializedQualities.size(); - - for( uint32_t i = 0; i < SegmentRunEntryTable.size(); i ++ ) { - SetPayload((uint32_t)4,Box::uint32_to_uint8(SegmentRunEntryTable[i].FragmentsPerSegment),(8*i)+OffsetSegmentRunEntryCount+8); - SetPayload((uint32_t)4,Box::uint32_to_uint8(SegmentRunEntryTable[i].FirstSegment),(8*i)+OffsetSegmentRunEntryCount+4); - } - SetPayload((uint32_t)4,Box::uint32_to_uint8(SegmentRunEntryTable.size()),OffsetSegmentRunEntryCount); - SetPayload((uint32_t)serializedQualities.size(),(uint8_t*)serializedQualities.c_str(),5); - SetPayload((uint32_t)1,Box::uint8_to_uint8(QualitySegmentUrlModifiers.size()),4); - SetPayload((uint32_t)4,Box::uint32_to_uint8((isUpdate ? 1 : 0))); -} -std::string ASRT::toPrettyString(int indent){ - std::string r; - r += std::string(indent, ' ')+"Segment Run Table\n"; - if (isUpdate){ - r += std::string(indent+1, ' ')+"Update\n"; - }else{ - r += std::string(indent+1, ' ')+"Replacement or new table\n"; + void ASRT::WriteContent( ) { + std::string serializedQualities = ""; + ResetPayload( ); + + for( uint32_t i = 0; i < QualitySegmentUrlModifiers.size(); i++ ) { + serializedQualities.append(QualitySegmentUrlModifiers[i].c_str()); + serializedQualities += '\0'; + } + + uint32_t OffsetSegmentRunEntryCount = 5 + serializedQualities.size(); + + for( uint32_t i = 0; i < SegmentRunEntryTable.size(); i ++ ) { + SetPayload((uint32_t)4,Box::uint32_to_uint8(SegmentRunEntryTable[i].FragmentsPerSegment),(8*i)+OffsetSegmentRunEntryCount+8); + SetPayload((uint32_t)4,Box::uint32_to_uint8(SegmentRunEntryTable[i].FirstSegment),(8*i)+OffsetSegmentRunEntryCount+4); + } + SetPayload((uint32_t)4,Box::uint32_to_uint8(SegmentRunEntryTable.size()),OffsetSegmentRunEntryCount); + SetPayload((uint32_t)serializedQualities.size(),(uint8_t*)serializedQualities.c_str(),5); + SetPayload((uint32_t)1,Box::uint8_to_uint8(QualitySegmentUrlModifiers.size()),4); + SetPayload((uint32_t)4,Box::uint32_to_uint8((isUpdate ? 1 : 0))); } - r += std::string(indent+1, ' ')+"Qualities "+JSON::Value((long long int)QualitySegmentUrlModifiers.size()).asString()+"\n"; - for( uint32_t i = 0; i < QualitySegmentUrlModifiers.size(); i++ ) { - r += std::string(indent+2, ' ')+"\""+QualitySegmentUrlModifiers[i]+"\"\n"; - } - r += std::string(indent+1, ' ')+"Segments "+JSON::Value((long long int)SegmentRunEntryTable.size()).asString()+"\n"; - for( uint32_t i = 0; i < SegmentRunEntryTable.size(); i ++ ) { - r += std::string(indent+2, ' ')+JSON::Value((long long int)SegmentRunEntryTable[i].FragmentsPerSegment).asString()+" fragments per, starting at "+JSON::Value((long long int)SegmentRunEntryTable[i].FirstSegment).asString(); - } - return r; -} -std::string mdatFold(std::string data){ - std::string Result; - unsigned int t_int; - t_int = htonl(data.size()+8); - Result.append((char*)&t_int, 4); - t_int = htonl(0x6D646174); - Result.append((char*)&t_int, 4); - Result.append(data); - return Result; -} + std::string ASRT::toPrettyString(int indent){ + std::string r; + r += std::string(indent, ' ')+"Segment Run Table\n"; + if (isUpdate){ + r += std::string(indent+1, ' ')+"Update\n"; + }else{ + r += std::string(indent+1, ' ')+"Replacement or new table\n"; + } + r += std::string(indent+1, ' ')+"Qualities "+JSON::Value((long long int)QualitySegmentUrlModifiers.size()).asString()+"\n"; + for( uint32_t i = 0; i < QualitySegmentUrlModifiers.size(); i++ ) { + r += std::string(indent+2, ' ')+"\""+QualitySegmentUrlModifiers[i]+"\"\n"; + } + r += std::string(indent+1, ' ')+"Segments "+JSON::Value((long long int)SegmentRunEntryTable.size()).asString()+"\n"; + for( uint32_t i = 0; i < SegmentRunEntryTable.size(); i ++ ) { + r += std::string(indent+2, ' ')+JSON::Value((long long int)SegmentRunEntryTable[i].FragmentsPerSegment).asString()+" fragments per, starting at "+JSON::Value((long long int)SegmentRunEntryTable[i].FirstSegment).asString(); + } + return r; + } + + std::string mdatFold(std::string data){ + std::string Result; + unsigned int t_int; + t_int = htonl(data.size()+8); + Result.append((char*)&t_int, 4); + t_int = htonl(0x6D646174); + Result.append((char*)&t_int, 4); + Result.append(data); + return Result; + } +*/ }; diff --git a/lib/mp4.h b/lib/mp4.h index f801d51f..6542b46e 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -9,8 +9,8 @@ namespace MP4{ class Box { public: - Box(); - Box(char* boxType); + Box( size_t size = 0); + Box(char* boxType, size_t size = 0 ); Box(std::string & newData); ~Box(); std::string getType(); @@ -22,12 +22,12 @@ namespace MP4{ void clear(); std::string toPrettyString(int indent = 0); protected: - void setInt8( char data, size_t index = 0); - void setInt16( short data, size_t index = 0); - void setInt32( long data, size_t index = 0); - void setInt64( long long int data, size_t index = 0); - void setString(std::string data, size_t index = 0); - void setString(char* data, size_t size, size_t index = 0); + void setInt8( char newData, size_t index = 0); + void setInt16( short newData, size_t index = 0); + void setInt32( long newData, size_t index = 0); + void setInt64( long long int newData, size_t index = 0); + void setString(std::string newData, size_t index = 0); + void setString(char* newData, size_t size, size_t index = 0); std::string data; };//Box Class From 42ab5653818ebc62f01f23ca229400d4bc277c20 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Mon, 17 Sep 2012 16:44:57 +0200 Subject: [PATCH 302/788] abst box rewritten --- lib/mp4.cpp | 427 +++++++++++++++++++++++++++++++--------------------- lib/mp4.h | 123 +++++++-------- 2 files changed, 316 insertions(+), 234 deletions(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 08fc7b91..16384fe3 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -8,17 +8,18 @@ namespace MP4{ Box::Box(size_t size) { + isUpdated = false; data.resize( size + 8 ); - memset( (char*)data.c_str(), 0, size + 8 ); } - Box::Box( char* BoxType, size_t size ) { + Box::Box( const char* BoxType, size_t size ) { + isUpdated = false; data.resize( size + 8 ); - memset( (char*)data.c_str(), 0, size + 8 ); memcpy( (char*)data.c_str() + 4, BoxType, 4 ); } Box::Box( std::string & newData ) { + isUpdated = false; if( !read( newData ) ) { clear(); } @@ -55,6 +56,9 @@ namespace MP4{ } std::string & Box::asBox() { + if( isUpdated ) { + regenerate( ); + } ((int*)data.c_str())[0] = htonl( data.size() ); return data; } @@ -74,6 +78,14 @@ namespace MP4{ } data[index] = newData; } + + char Box::getInt8( size_t index ) { + index += 8; + if( index > data.size() ) { + data.resize( index ); + } + return data[index]; + } void Box::setInt16( short newData, size_t index ) { index += 8; @@ -83,6 +95,39 @@ namespace MP4{ newData = htons( newData ); memcpy( (char*)data.c_str() + index, (char*)newData, 2 ); } + + short Box::getInt16( size_t index ) { + index += 8; + if( index + 1 > data.size() ) { + data.resize( index + 1 ); + } + short result; + memcpy( (char*)result, (char*)data.c_str() + index, 2 ); + return ntohs(result); + } + + void Box::setInt24( long newData, size_t index ) { + index += 8; + if( index + 2 > data.size() ) { + data.resize( index + 2 ); + } + data[index] = (newData & 0x00FF0000) >> 16; + data[index+1] = (newData & 0x0000FF00) >> 8; + data[index+2] = (newData & 0x000000FF); + } + + long Box::getInt24( size_t index ) { + index += 8; + if( index + 2 > data.size() ) { + data.resize( index + 2 ); + } + long result = data[index]; + result <<= 8; + result += data[index+1]; + result <<= 8; + result += data[index+2]; + return result; + } void Box::setInt32( long newData, size_t index ) { index += 8; @@ -92,6 +137,17 @@ namespace MP4{ newData = htonl( newData ); memcpy( (char*)data.c_str() + index, (char*)newData, 4 ); } + + long Box::getInt32( size_t index ) { + index += 8; + if( index + 3 > data.size() ) { + data.resize( index + 3 ); + } + long result; + + memcpy( (char*)result, (char*)data.c_str() + index, 4 ); + return ntohl(result); + } void Box::setInt64( long long int newData, size_t index ) { index += 8; @@ -107,6 +163,30 @@ namespace MP4{ data[index+6] = ( newData * 0x000000000000FF00 ) >> 8; data[index+7] = ( newData * 0x00000000000000FF ); } + + long long int Box::getInt64( size_t index ) { + index += 8; + if( index + 7 > data.size() ) { + data.resize( index + 7 ); + } + long result = data[index]; + result <<= 8; + result += data[index+1]; + result <<= 8; + result += data[index+2]; + result <<= 8; + result += data[index+3]; + result <<= 8; + result += data[index+4]; + result <<= 8; + result += data[index+5]; + result <<= 8; + result += data[index+6]; + result <<= 8; + result += data[index+7]; + return result; + } + void Box::setString(std::string newData, size_t index ) { setString( (char*)newData.c_str(), newData.size(), index ); @@ -120,197 +200,218 @@ namespace MP4{ memcpy( (char*)data.c_str() + index, newData, size ); } - -/* - void ABST::SetBootstrapVersion( uint32_t Version ) { - curBootstrapInfoVersion = Version; + void Box::regenerate() { + std::cerr << "Regenerate() not implemented for this box type\n"; + } + + ABST::ABST( ) : Box("abst") { + setVersion( 0 ); + setFlags( 0 ); + setBootstrapinfoVersion( 0 ); + setProfile( 0 ); + setLive( 1 ); + setUpdate( 0 ); + setTimeScale( 1000 ); + setCurrentMediaTime( 0 ); + setSmpteTimeCodeOffset( 0 ); + setMovieIdentifier( "" ); + setDrmData( "" ); + setMetaData( "" ); + } + + void ABST::setVersion( char newVersion ) { + setInt8( newVersion, 0 ); + } + + void ABST::setFlags( long newFlags ) { + setInt24( newFlags, 1 ); } - void ABST::SetProfile( uint8_t Profile ) { - curProfile = Profile; + void ABST::setBootstrapinfoVersion( long newVersion ) { + setInt32( newVersion, 4 ); } - void ABST::SetLive( bool Live ) { - isLive = Live; + void ABST::setProfile( char newProfile ) { + setInt8( ( getInt8(5) & 0x3F ) + ( ( newProfile & 0x02 ) << 6 ), 5 ); } - void ABST::SetUpdate( bool Update ) { - isUpdate = Update; + + void ABST::setLive( char newLive ) { + setInt8( ( getInt8(5) & 0xDF ) + ( ( newLive & 0x01 ) << 5 ), 5 ); } - void ABST::SetTimeScale( uint32_t Scale ) { - curTimeScale = Scale; + + void ABST::setUpdate( char newUpdate ) { + setInt8( ( getInt8(5) & 0xEF ) + ( ( newUpdate & 0x01 ) << 4 ), 5 ); } - void ABST::SetMediaTime( uint32_t Time ) { - curMediatime = Time; + void ABST::setTimeScale( long newScale ) { + setInt32( newScale, 6 ); + } + + void ABST::setCurrentMediaTime( long long int newTime ) { + setInt64( newTime, 10 ); } - void ABST::SetSMPTE( uint32_t Smpte ) { - curSMPTE = Smpte; + void ABST::setSmpteTimeCodeOffset( long long int newTime ) { + setInt64( newTime, 18 ); } - void ABST::SetMovieIdentifier( std::string Identifier ) { - curMovieIdentifier = Identifier; + void ABST::setMovieIdentifier( std::string newIdentifier ) { + movieIdentifier = newIdentifier; } - void ABST::SetDRM( std::string Drm ) { - curDRM = Drm; - } - - void ABST::SetMetaData( std::string MetaData ) { - curMetaData = MetaData; - } - - void ABST::AddServerEntry( std::string Url, uint32_t Offset ) { - if(Offset >= Servers.size()) { - Servers.resize(Offset+1); + void ABST::addServerEntry( std::string newEntry ) { + if( std::find( Servers.begin(), Servers.end(), newEntry ) == Servers.end() ) { + Servers.push_back( newEntry ); + isUpdated = true; } - Servers[Offset].ServerBaseUrl = Url; } - - void ABST::AddQualityEntry( std::string Quality, uint32_t Offset ) { - if(Offset >= Qualities.size()) { - Qualities.resize(Offset+1); - } - Qualities[Offset].QualityModifier = Quality; - } - - void ABST::AddSegmentRunTable( Box * newSegment, uint32_t Offset ) { - if( Offset >= SegmentRunTables.size() ) { - SegmentRunTables.resize(Offset+1); - } - if( SegmentRunTables[Offset] ) { - delete SegmentRunTables[Offset]; - } - SegmentRunTables[Offset] = newSegment; - } - - void ABST::AddFragmentRunTable( Box * newFragment, uint32_t Offset ) { - if( Offset >= FragmentRunTables.size() ) { - FragmentRunTables.resize(Offset+1); - } - if( FragmentRunTables[Offset] ) { - delete FragmentRunTables[Offset]; - } - FragmentRunTables[Offset] = newFragment; - } - - void ABST::SetDefaults( ) { - SetProfile( ); - SetLive( ); - SetUpdate( ); - SetTimeScale( ); - SetMediaTime( ); - SetSMPTE( ); - SetMovieIdentifier( ); - SetDRM( ); - SetMetaData( ); - SetVersion( ); - } - - void ABST::SetVersion( bool NewVersion) { - Version = NewVersion; - } - - void ABST::SetReserved( ) { - SetInt32(0); - } - - void ABST::WriteContent( ) { - Box * current; - std::string serializedServers = ""; - std::string serializedQualities = ""; - std::string serializedSegments = ""; - std::string serializedFragments = ""; - int SegmentAmount = 0; - int FragmentAmount = 0; - uint8_t * temp = new uint8_t[1]; - - clear( ); - SetReserved( ); - - for( uint32_t i = 0; i < Servers.size(); i++ ) { - serializedServers.append(Servers[i].ServerBaseUrl.c_str()); - serializedServers += '\0'; - } - for( uint32_t i = 0; i < Qualities.size(); i++ ) { - serializedQualities.append(Qualities[i].QualityModifier.c_str()); - serializedQualities += '\0'; - } - for( uint32_t i = 0; i < SegmentRunTables.size(); i++ ) { - current=SegmentRunTables[i]; - if( current ) { - SegmentAmount ++; - serializedSegments.append((char*)current->GetBoxedData(),current->GetBoxedDataSize()); + + void ABST::delServerEntry( std::string delEntry ) { + for( std::deque::iterator it = Servers.begin(); it != Servers.end(); it++ ) { + if( (*it) == delEntry ) { + Servers.erase( it ); + isUpdated = true; + break; } } - for( uint32_t i = 0; i < FragmentRunTables.size(); i++ ) { - current=FragmentRunTables[i]; - if( current ) { - FragmentAmount ++; - serializedFragments.append((char*)current->GetBoxedData(),current->GetBoxedDataSize()); + } + + void ABST::addQualityEntry( std::string newEntry ) { + if( std::find( Qualities.begin(), Qualities.end(), newEntry ) == Qualities.end() ) { + Servers.push_back( newEntry ); + isUpdated = true; + } + } + + void ABST::delQualityEntry( std::string delEntry ) { + for( std::deque::iterator it = Qualities.begin(); it != Qualities.end(); it++ ) { + if( (*it) == delEntry ) { + Qualities.erase( it ); + isUpdated = true; + break; } } - uint32_t OffsetServerEntryCount = 29 + curMovieIdentifier.size() + 1; - uint32_t OffsetQualityEntryCount = OffsetServerEntryCount + 1 + serializedServers.size(); - uint32_t OffsetDrmData = OffsetQualityEntryCount + 1 + serializedQualities.size(); - uint32_t OffsetMetaData = OffsetDrmData + curDRM.size() + 1; - uint32_t OffsetSegmentRuntableCount = OffsetMetaData + curMetaData.size() + 1; - uint32_t OffsetFragmentRuntableCount = OffsetSegmentRuntableCount + 1 + serializedSegments.size(); - - temp[0] = 0 + ( curProfile << 6 ) + ( (uint8_t)isLive << 7 ) + ( (uint8_t)isUpdate << 7 ); - - SetPayload((uint32_t)serializedFragments.size(),(uint8_t*)serializedFragments.c_str(),OffsetFragmentRuntableCount+1); - SetPayload((uint32_t)1,Box::uint8_to_uint8(FragmentAmount),OffsetFragmentRuntableCount); - SetPayload((uint32_t)serializedSegments.size(),(uint8_t*)serializedSegments.c_str(),OffsetSegmentRuntableCount+1); - SetPayload((uint32_t)1,Box::uint8_to_uint8(SegmentAmount),OffsetSegmentRuntableCount); - SetPayload((uint32_t)curMetaData.size()+1,(uint8_t*)curMetaData.c_str(),OffsetMetaData); - SetPayload((uint32_t)curDRM.size()+1,(uint8_t*)curDRM.c_str(),OffsetDrmData); - SetPayload((uint32_t)serializedQualities.size(),(uint8_t*)serializedQualities.c_str(),OffsetQualityEntryCount+1); - SetPayload((uint32_t)1,Box::uint8_to_uint8(Qualities.size()),OffsetQualityEntryCount); - SetPayload((uint32_t)serializedServers.size(),(uint8_t*)serializedServers.c_str(),OffsetServerEntryCount+1); - SetPayload((uint32_t)1,Box::uint8_to_uint8(Servers.size()),OffsetServerEntryCount); - SetPayload((uint32_t)curMovieIdentifier.size()+1,(uint8_t*)curMovieIdentifier.c_str(),29);//+1 for \0-terminated string... - SetPayload((uint32_t)4,Box::uint32_to_uint8(curSMPTE),25); - SetPayload((uint32_t)4,Box::uint32_to_uint8(0),21); - SetPayload((uint32_t)4,Box::uint32_to_uint8(curMediatime),17); - SetPayload((uint32_t)4,Box::uint32_to_uint8(0),13); - SetPayload((uint32_t)4,Box::uint32_to_uint8(curTimeScale),9); - SetPayload((uint32_t)1,temp,8); - SetPayload((uint32_t)4,Box::uint32_to_uint8(curBootstrapInfoVersion),4); } - std::string ABST::toPrettyString(int indent){ + void ABST::setDrmData( std::string newDrm ) { + drmData = newDrm; + isUpdated = true; + } + + void ABST::setMetaData( std::string newMetaData ) { + metaData = newMetaData; + isUpdated = true; + } + + void ABST::addSegmentRunTable( Box * newSegment ) { + segmentTables.push_back(newSegment); + isUpdated = true; + } + + void ABST::addFragmentRunTable( Box * newFragment ) { + fragmentTables.push_back(newFragment); + isUpdated = true; + } + + void ABST::regenerate( ) { + int myOffset = 26; + //0-terminated movieIdentifier + memcpy( (char*)data.c_str() + myOffset, movieIdentifier.c_str(), movieIdentifier.size() + 1); + myOffset += movieIdentifier.size() + 1; + //8-bit server amount + setInt8( Servers.size(), myOffset ); + myOffset ++; + //0-terminated string for each entry + for( std::deque::iterator it = Servers.begin(); it != Servers.end(); it++ ) { + memcpy( (char*)data.c_str() + myOffset, (*it).c_str(), (*it).size() + 1); + myOffset += (*it).size() + 1; + } + //8-bit quality amount + setInt8( Qualities.size(), myOffset ); + myOffset ++; + //0-terminated string for each entry + for( std::deque::iterator it = Qualities.begin();it != Qualities.end(); it++ ) { + memcpy( (char*)data.c_str() + myOffset, (*it).c_str(), (*it).size() + 1); + myOffset += (*it).size() + 1; + } + //0-terminated DrmData + memcpy( (char*)data.c_str() + myOffset, drmData.c_str(), drmData.size() + 1); + myOffset += drmData.size() + 1; + //0-terminated MetaData + memcpy( (char*)data.c_str() + myOffset, metaData.c_str(), metaData.size() + 1); + myOffset += metaData.size() + 1; + //8-bit segment run amount + setInt8( segmentTables.size(), myOffset ); + myOffset ++; + //retrieve box for each entry + for( std::deque::iterator it = segmentTables.begin(); it != segmentTables.end(); it++ ) { + memcpy( (char*)data.c_str() + myOffset, (*it)->asBox().c_str(), (*it)->boxedSize() ); + myOffset += (*it)->boxedSize(); + } + //8-bit fragment run amount + setInt8( fragmentTables.size(), myOffset ); + myOffset ++; + //retrieve box for each entry + for( std::deque::iterator it = fragmentTables.begin(); it != fragmentTables.end(); it++ ) { + memcpy( (char*)data.c_str() + myOffset, (*it)->asBox().c_str(), (*it)->boxedSize() + 1); + myOffset += (*it)->boxedSize(); + } + isUpdated = false; + } + + std::string ABST::toPrettyString( int indent ) { std::string r; r += std::string(indent, ' ')+"Bootstrap Info\n"; - if (isUpdate){ + if( getInt8(5) & 0x10 ) { r += std::string(indent+1, ' ')+"Update\n"; - }else{ + } else { r += std::string(indent+1, ' ')+"Replacement or new table\n"; } - if (isLive){ - r += std::string(indent+1, ' ')+"Live\n"; + if( getInt8(5) & 0x20 ) { + r += std::string(indent+1, ' ' )+"Live\n"; }else{ - r += std::string(indent+1, ' ')+"Recorded\n"; + r += std::string(indent+1, ' ' )+"Recorded\n"; } - r += std::string(indent+1, ' ')+"Profile "+JSON::Value((long long int)curProfile).asString()+"\n"; - r += std::string(indent+1, ' ')+"Timescale "+JSON::Value((long long int)curTimeScale).asString()+"\n"; - r += std::string(indent+1, ' ')+"CurrMediaTime "+JSON::Value((long long int)curMediatime).asString()+"\n"; - r += std::string(indent+1, ' ')+"Segment Run Tables "+JSON::Value((long long int)SegmentRunTables.size()).asString()+"\n"; - for( uint32_t i = 0; i < SegmentRunTables.size(); i++ ) { - r += ((ASRT*)SegmentRunTables[i])->toPrettyString(indent+2)+"\n"; + r += std::string(indent+1, ' ')+"Profile "+JSON::Value((long long int)( getInt8(5) & 0xC0 ) ).asString()+"\n"; + r += std::string(indent+1, ' ')+"Timescale "+JSON::Value((long long int)getInt64(10)).asString()+"\n"; + r += std::string(indent+1, ' ')+"CurrMediaTime "+JSON::Value((long long int)getInt32(6)).asString()+"\n"; + r += std::string(indent+1, ' ')+"Segment Run Tables "+JSON::Value((long long int)segmentTables.size()).asString()+"\n"; + for( uint32_t i = 0; i < segmentTables.size(); i++ ) { + r += ((ASRT*)segmentTables[i])->toPrettyString(indent+2)+"\n"; } - r += std::string(indent+1, ' ')+"Fragment Run Tables "+JSON::Value((long long int)FragmentRunTables.size()).asString()+"\n"; - for( uint32_t i = 0; i < FragmentRunTables.size(); i++ ) { - r += ((AFRT*)FragmentRunTables[i])->toPrettyString(indent+2)+"\n"; + r += std::string(indent+1, ' ')+"Fragment Run Tables "+JSON::Value((long long int)fragmentTables.size()).asString()+"\n"; + for( uint32_t i = 0; i < fragmentTables.size(); i++ ) { + r += ((AFRT*)fragmentTables[i])->toPrettyString(indent+2)+"\n"; } return r; } - - void AFRT::SetUpdate( bool Update ) { - isUpdate = Update; + + AFTR::AFRT() : Box(0x61667274){ + setVersion( 0 ); + setUpdate( 0 ); + setTimeScale( 1000 ); } + + void AFRT::setVersion( char newVersion ) { + setInt8( newVersion ,0 ) + } + + void AFRT::setUpdate( long newUpdate ) { + setInt24( newUpdate, 1 ); + } + + void AFRT::setTimeScale( long newScale ) { + setInt32( newScale, 4 ); + } + + void AFRT::addQualityEntry( std::string newQuality ) { + qualityModifiers.push_back( newQuality ); + } + + +/* void AFRT::AddQualityEntry( std::string Quality, uint32_t Offset ) { if(Offset >= QualitySegmentUrlModifiers.size()) { @@ -456,16 +557,6 @@ namespace MP4{ return r; } - std::string mdatFold(std::string data){ - std::string Result; - unsigned int t_int; - t_int = htonl(data.size()+8); - Result.append((char*)&t_int, 4); - t_int = htonl(0x6D646174); - Result.append((char*)&t_int, 4); - Result.append(data); - return Result; - } */ }; diff --git a/lib/mp4.h b/lib/mp4.h index 6542b46e..27e44f4e 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -1,7 +1,10 @@ #pragma once #include +#include #include -#include +#include +#include ///\todo remove this include +#include #include "json.h" /// Contains all MP4 format related code. @@ -10,76 +13,67 @@ namespace MP4{ class Box { public: Box( size_t size = 0); - Box(char* boxType, size_t size = 0 ); - Box(std::string & newData); + Box( const char* boxType, size_t size = 0 ); + Box( std::string & newData ); ~Box(); std::string getType(); bool isType( char* boxType ); - bool read(std::string & newData); + bool read( std::string & newData ); size_t boxedSize(); size_t payloadSize(); std::string & asBox(); + void regenerate(); void clear(); - std::string toPrettyString(int indent = 0); + std::string toPrettyString( int indent = 0 ); protected: - void setInt8( char newData, size_t index = 0); - void setInt16( short newData, size_t index = 0); - void setInt32( long newData, size_t index = 0); - void setInt64( long long int newData, size_t index = 0); - void setString(std::string newData, size_t index = 0); - void setString(char* newData, size_t size, size_t index = 0); + 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( long newData, size_t index ); + long getInt24( size_t index ); + void setInt32( long newData, size_t index ); + long getInt32( size_t index ); + void setInt64( long long int newData, size_t index ); + long long int getInt64( size_t index ); + void setString(std::string newData, size_t index ); + void setString(char* newData, size_t size, size_t index ); std::string data; + bool isUpdated; };//Box Class -//Tot hier rewritten - - struct abst_serverentry { - std::string ServerBaseUrl; - };//abst_serverentry - - struct abst_qualityentry { - std::string QualityModifier; - };//abst_qualityentry - /// ABST Box class class ABST: public Box { public: - ABST() : Box(0x61627374){}; - void SetBootstrapVersion( uint32_t Version = 1 ); - void SetProfile( uint8_t Profile = 0 ); - void SetLive( bool Live = true ); - void SetUpdate( bool Update = false ); - void SetTimeScale( uint32_t Scale = 1000 ); - void SetMediaTime( uint32_t Time = 0 ); - void SetSMPTE( uint32_t Smpte = 0 ); - void SetMovieIdentifier( std::string Identifier = "" ); - void SetDRM( std::string Drm = "" ); - void SetMetaData( std::string MetaData = "" ); - void AddServerEntry( std::string Url = "", uint32_t Offset = 0 ); - void AddQualityEntry( std::string Quality = "", uint32_t Offset = 0 ); - void AddSegmentRunTable( Box * newSegment, uint32_t Offset = 0 ); - void AddFragmentRunTable( Box * newFragment, uint32_t Offset = 0 ); - void SetVersion( bool NewVersion = 0 ); - void WriteContent( ); + ABST(); + void setVersion( char newVersion ); + void setFlags( long newFlags ); + void setBootstrapinfoVersion( long newVersion ); + void setProfile( char newProfile ); + void setLive( char newLive ); + void setUpdate( char newUpdate ); + void setTimeScale( long newTimeScale ); + void setCurrentMediaTime( long long int newTime ); + void setSmpteTimeCodeOffset( long long int newTime ); + void setMovieIdentifier( std::string newIdentifier ); + void addServerEntry( std::string newEntry ); + void delServerEntry( std::string delEntry ); + void addQualityEntry( std::string newEntry ); + void delQualityEntry( std::string delEntry ); + void setDrmData( std::string newDrm ); + void setMetaData( std::string newMetaData ); + void addSegmentRunTable( Box * newSegment ); + void addFragmentRunTable( Box * newFragment ); std::string toPrettyString(int indent = 0); private: - void SetDefaults( ); - void SetReserved( ); - uint32_t curBootstrapInfoVersion; - uint8_t curProfile; - bool isLive; - bool isUpdate; - bool Version; - uint32_t curTimeScale; - uint32_t curMediatime;//write as uint64_t - uint32_t curSMPTE;//write as uint64_t - std::string curMovieIdentifier; - std::string curDRM; - std::string curMetaData; - std::vector Servers; - std::vector Qualities; - std::vector SegmentRunTables; - std::vector FragmentRunTables; + void regenerate(); + std::string movieIdentifier; + std::deque Servers; + std::deque Qualities; + std::string drmData; + std::string metaData; + std::deque segmentTables; + std::deque fragmentTables; };//ABST Box struct afrt_fragmentrunentry { @@ -93,19 +87,18 @@ namespace MP4{ /// AFRT Box class class AFRT : public Box { public: - AFRT() : Box(0x61667274){}; - void SetUpdate( bool Update = false ); - void SetTimeScale( uint32_t Scale = 1000 ); - void AddQualityEntry( std::string Quality = "", uint32_t Offset = 0 ); + AFRT(); + void setVersion( char newVersion ); + void setUpdate( long newUpdate ); + void setTimeScale( long newScale ); + void addQualityEntry( std::string newQuality ); + void AddFragmentRunEntry( uint32_t FirstFragment = 0, uint32_t FirstFragmentTimestamp = 0, uint32_t FragmentsDuration = 1, uint8_t Discontinuity = 0, uint32_t Offset = 0 ); void WriteContent( ); std::string toPrettyString(int indent = 0); private: - void SetDefaults( ); - bool isUpdate; - uint32_t curTimeScale; - std::vector QualitySegmentUrlModifiers; - std::vector FragmentRunEntryTable; + std::deque qualityModifiers; + std::deque FragmentRunEntryTable; };//AFRT Box struct asrt_segmentrunentry { @@ -132,6 +125,4 @@ namespace MP4{ Box * Container; };//ASRT Box - std::string mdatFold(std::string data); - }; From 70e2962d0b683ab16035b631975ca20e06e66629 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Tue, 18 Sep 2012 10:51:00 +0200 Subject: [PATCH 303/788] Nieuwe implementatie af, backwards compatibility ruined. --- lib/mp4.cpp | 210 +++++++++++++++++++++++----------------------------- lib/mp4.h | 47 ++++++------ 2 files changed, 112 insertions(+), 145 deletions(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 16384fe3..d72aa0d0 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -388,14 +388,14 @@ namespace MP4{ return r; } - AFTR::AFRT() : Box(0x61667274){ + AFRT::AFRT() : Box("afrt"){ setVersion( 0 ); setUpdate( 0 ); setTimeScale( 1000 ); } void AFRT::setVersion( char newVersion ) { - setInt8( newVersion ,0 ) + setInt8( newVersion ,0 ); } void AFRT::setUpdate( long newUpdate ) { @@ -408,155 +408,127 @@ namespace MP4{ void AFRT::addQualityEntry( std::string newQuality ) { qualityModifiers.push_back( newQuality ); + isUpdated = true; } - -/* - - void AFRT::AddQualityEntry( std::string Quality, uint32_t Offset ) { - if(Offset >= QualitySegmentUrlModifiers.size()) { - QualitySegmentUrlModifiers.resize(Offset+1); - } - QualitySegmentUrlModifiers[Offset] = Quality; + void AFRT::addFragmentRun( long firstFragment, long long int firstTimestamp, long duration, char discontinuity ) { + fragmentRun newRun; + newRun.firstFragment = firstFragment; + newRun.firstTimestamp = firstTimestamp; + newRun.duration = duration; + newRun.discontinuity = discontinuity; + fragmentRunTable.push_back( newRun ); + isUpdated = true; } - - void AFRT::AddFragmentRunEntry( uint32_t FirstFragment, uint32_t FirstFragmentTimestamp, uint32_t FragmentsDuration, uint8_t Discontinuity, uint32_t Offset ) { - if( Offset >= FragmentRunEntryTable.size() ) { - FragmentRunEntryTable.resize(Offset+1); + + void AFRT::regenerate( ) { + int myOffset = 8; + setInt8( qualityModifiers.size(), myOffset ); + myOffset ++; + //0-terminated string for each entry + for( std::deque::iterator it = qualityModifiers.begin();it != qualityModifiers.end(); it++ ) { + memcpy( (char*)data.c_str() + myOffset, (*it).c_str(), (*it).size() + 1); + myOffset += (*it).size() + 1; } - FragmentRunEntryTable[Offset].FirstFragment = FirstFragment; - FragmentRunEntryTable[Offset].FirstFragmentTimestamp = FirstFragmentTimestamp; - FragmentRunEntryTable[Offset].FragmentDuration = FragmentsDuration; - if( FragmentsDuration == 0) { - FragmentRunEntryTable[Offset].DiscontinuityIndicator = Discontinuity; + setInt32( fragmentRunTable.size(), myOffset ); + myOffset += 4; + //table values for each entry + for( std::deque::iterator it = fragmentRunTable.begin();it != fragmentRunTable.end(); it++ ) { + setInt32( (*it).firstFragment, myOffset ); + myOffset += 4; + setInt64( (*it).firstTimestamp, myOffset ); + myOffset += 8; + setInt32( (*it).duration, myOffset ); + myOffset += 4; + setInt8( (*it).discontinuity, myOffset ); + myOffset += 1; } - } - - void AFRT::SetDefaults( ) { - SetUpdate( ); - SetTimeScale( ); - } - - void AFRT::SetTimeScale( uint32_t Scale ) { - curTimeScale = Scale; - } - - void AFRT::WriteContent( ) { - std::string serializedQualities = ""; - std::string serializedFragmentEntries = ""; - clear(); - - for( uint32_t i = 0; i < QualitySegmentUrlModifiers.size(); i++ ) { - serializedQualities.append(QualitySegmentUrlModifiers[i].c_str()); - serializedQualities += '\0'; - } - for( uint32_t i = 0; i < FragmentRunEntryTable.size(); i ++ ) { - serializedFragmentEntries.append((char*)Box::uint32_to_uint8(FragmentRunEntryTable[i].FirstFragment),4); - serializedFragmentEntries.append((char*)Box::uint32_to_uint8(0),4); - serializedFragmentEntries.append((char*)Box::uint32_to_uint8(FragmentRunEntryTable[i].FirstFragmentTimestamp),4); - serializedFragmentEntries.append((char*)Box::uint32_to_uint8(FragmentRunEntryTable[i].FragmentDuration),4); - if(FragmentRunEntryTable[i].FragmentDuration == 0) { - serializedFragmentEntries.append((char*)Box::uint8_to_uint8(FragmentRunEntryTable[i].DiscontinuityIndicator),1); - } - } - - uint32_t OffsetFragmentRunEntryCount = 9 + serializedQualities.size(); - - SetPayload((uint32_t)serializedFragmentEntries.size(),(uint8_t*)serializedFragmentEntries.c_str(),OffsetFragmentRunEntryCount+4); - SetPayload((uint32_t)4,Box::uint32_to_uint8(FragmentRunEntryTable.size()),OffsetFragmentRunEntryCount); - SetPayload((uint32_t)serializedQualities.size(),(uint8_t*)serializedQualities.c_str(),9); - SetPayload((uint32_t)1,Box::uint8_to_uint8(QualitySegmentUrlModifiers.size()),8); - SetPayload((uint32_t)4,Box::uint32_to_uint8(curTimeScale),4); - SetPayload((uint32_t)4,Box::uint32_to_uint8((isUpdate ? 1 : 0))); + isUpdated = false; } std::string AFRT::toPrettyString(int indent){ std::string r; r += std::string(indent, ' ')+"Fragment Run Table\n"; - if (isUpdate){ + if (getInt24(1)){ r += std::string(indent+1, ' ')+"Update\n"; }else{ r += std::string(indent+1, ' ')+"Replacement or new table\n"; } - r += std::string(indent+1, ' ')+"Timescale "+JSON::Value((long long int)curTimeScale).asString()+"\n"; - r += std::string(indent+1, ' ')+"Qualities "+JSON::Value((long long int)QualitySegmentUrlModifiers.size()).asString()+"\n"; - for( uint32_t i = 0; i < QualitySegmentUrlModifiers.size(); i++ ) { - r += std::string(indent+2, ' ')+"\""+QualitySegmentUrlModifiers[i]+"\"\n"; + r += std::string(indent+1, ' ')+"Timescale "+JSON::Value((long long int)getInt32(4)).asString()+"\n"; + r += std::string(indent+1, ' ')+"Qualities "+JSON::Value((long long int)qualityModifiers.size()).asString()+"\n"; + for( uint32_t i = 0; i < qualityModifiers.size(); i++ ) { + r += std::string(indent+2, ' ')+"\""+qualityModifiers[i]+"\"\n"; } - r += std::string(indent+1, ' ')+"Fragments "+JSON::Value((long long int)FragmentRunEntryTable.size()).asString()+"\n"; - for( uint32_t i = 0; i < FragmentRunEntryTable.size(); i ++ ) { - r += std::string(indent+2, ' ')+"Duration "+JSON::Value((long long int)FragmentRunEntryTable[i].FragmentDuration).asString()+", starting at "+JSON::Value((long long int)FragmentRunEntryTable[i].FirstFragment).asString()+" @ "+JSON::Value((long long int)FragmentRunEntryTable[i].FirstFragmentTimestamp).asString(); + r += std::string(indent+1, ' ')+"Fragments "+JSON::Value((long long int)fragmentRunTable.size()).asString()+"\n"; + for( uint32_t i = 0; i < fragmentRunTable.size(); i ++ ) { + r += std::string(indent+2, ' ')+"Duration "+JSON::Value((long long int)fragmentRunTable[i].duration).asString()+", starting at "+JSON::Value((long long int)fragmentRunTable[i].firstFragment).asString()+" @ "+JSON::Value((long long int)fragmentRunTable[i].firstTimestamp).asString(); } return r; } - - void ASRT::SetUpdate( bool Update ) { - isUpdate = Update; + + ASRT::ASRT() : Box("asrt") { + setVersion( 0 ); + setUpdate( 0 ); } - - void ASRT::AddQualityEntry( std::string Quality, uint32_t Offset ) { - if(Offset >= QualitySegmentUrlModifiers.size()) { - QualitySegmentUrlModifiers.resize(Offset+1); + + void ASRT::setVersion( char newVersion ) { + setInt8( newVersion, 0 ); + } + + void ASRT::setUpdate( long newUpdate ) { + setInt24( newUpdate, 1 ); + } + + void ASRT::addQualityEntry( std::string newQuality ) { + qualityModifiers.push_back( newQuality ); + isUpdated = true; + } + + void ASRT::addSegmentRun( long firstSegment, long fragmentsPerSegment ) { + segmentRun newRun; + newRun.firstSegment = firstSegment; + newRun.fragmentsPerSegment = fragmentsPerSegment; + segmentRunTable.push_back( newRun ); + isUpdated = true; + } + + void ASRT::regenerate( ) { + int myOffset = 4; + setInt8( qualityModifiers.size(), myOffset ); + myOffset ++; + //0-terminated string for each entry + for( std::deque::iterator it = qualityModifiers.begin();it != qualityModifiers.end(); it++ ) { + memcpy( (char*)data.c_str() + myOffset, (*it).c_str(), (*it).size() + 1); + myOffset += (*it).size() + 1; } - QualitySegmentUrlModifiers[Offset] = Quality; - } - - void ASRT::AddSegmentRunEntry( uint32_t FirstSegment, uint32_t FragmentsPerSegment, uint32_t Offset ) { - if( Offset >= SegmentRunEntryTable.size() ) { - SegmentRunEntryTable.resize(Offset+1); + setInt32( segmentRunTable.size(), myOffset ); + myOffset += 4; + //table values for each entry + for( std::deque::iterator it = segmentRunTable.begin();it != segmentRunTable.end(); it++ ) { + setInt32( (*it).firstSegment, myOffset ); + myOffset += 4; + setInt32( (*it).fragmentsPerSegment, myOffset ); + myOffset += 4; } - SegmentRunEntryTable[Offset].FirstSegment = FirstSegment; - SegmentRunEntryTable[Offset].FragmentsPerSegment = FragmentsPerSegment; - } - - void ASRT::SetVersion( bool NewVersion ) { - Version = NewVersion; - } - - void ASRT::SetDefaults( ) { - SetUpdate( ); - } - - void ASRT::WriteContent( ) { - std::string serializedQualities = ""; - ResetPayload( ); - - for( uint32_t i = 0; i < QualitySegmentUrlModifiers.size(); i++ ) { - serializedQualities.append(QualitySegmentUrlModifiers[i].c_str()); - serializedQualities += '\0'; - } - - uint32_t OffsetSegmentRunEntryCount = 5 + serializedQualities.size(); - - for( uint32_t i = 0; i < SegmentRunEntryTable.size(); i ++ ) { - SetPayload((uint32_t)4,Box::uint32_to_uint8(SegmentRunEntryTable[i].FragmentsPerSegment),(8*i)+OffsetSegmentRunEntryCount+8); - SetPayload((uint32_t)4,Box::uint32_to_uint8(SegmentRunEntryTable[i].FirstSegment),(8*i)+OffsetSegmentRunEntryCount+4); - } - SetPayload((uint32_t)4,Box::uint32_to_uint8(SegmentRunEntryTable.size()),OffsetSegmentRunEntryCount); - SetPayload((uint32_t)serializedQualities.size(),(uint8_t*)serializedQualities.c_str(),5); - SetPayload((uint32_t)1,Box::uint8_to_uint8(QualitySegmentUrlModifiers.size()),4); - SetPayload((uint32_t)4,Box::uint32_to_uint8((isUpdate ? 1 : 0))); + isUpdated = false; } std::string ASRT::toPrettyString(int indent){ std::string r; r += std::string(indent, ' ')+"Segment Run Table\n"; - if (isUpdate){ + if (getInt24(1)){ r += std::string(indent+1, ' ')+"Update\n"; }else{ r += std::string(indent+1, ' ')+"Replacement or new table\n"; } - r += std::string(indent+1, ' ')+"Qualities "+JSON::Value((long long int)QualitySegmentUrlModifiers.size()).asString()+"\n"; - for( uint32_t i = 0; i < QualitySegmentUrlModifiers.size(); i++ ) { - r += std::string(indent+2, ' ')+"\""+QualitySegmentUrlModifiers[i]+"\"\n"; + r += std::string(indent+1, ' ')+"Qualities "+JSON::Value((long long int)qualityModifiers.size()).asString()+"\n"; + for( uint32_t i = 0; i < qualityModifiers.size(); i++ ) { + r += std::string(indent+2, ' ')+"\""+qualityModifiers[i]+"\"\n"; } - r += std::string(indent+1, ' ')+"Segments "+JSON::Value((long long int)SegmentRunEntryTable.size()).asString()+"\n"; - for( uint32_t i = 0; i < SegmentRunEntryTable.size(); i ++ ) { - r += std::string(indent+2, ' ')+JSON::Value((long long int)SegmentRunEntryTable[i].FragmentsPerSegment).asString()+" fragments per, starting at "+JSON::Value((long long int)SegmentRunEntryTable[i].FirstSegment).asString(); + r += std::string(indent+1, ' ')+"Segments "+JSON::Value((long long int)segmentRunTable.size()).asString()+"\n"; + for( uint32_t i = 0; i < segmentRunTable.size(); i ++ ) { + r += std::string(indent+2, ' ')+JSON::Value((long long int)segmentRunTable[i].fragmentsPerSegment).asString()+" fragments per, starting at "+JSON::Value((long long int)segmentRunTable[i].firstSegment).asString(); } return r; } - -*/ - }; diff --git a/lib/mp4.h b/lib/mp4.h index 27e44f4e..6089b516 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -76,12 +76,12 @@ namespace MP4{ std::deque fragmentTables; };//ABST Box - struct afrt_fragmentrunentry { - uint32_t FirstFragment; - uint32_t FirstFragmentTimestamp; //write as uint64_t - uint32_t FragmentDuration; - uint8_t DiscontinuityIndicator;//if FragmentDuration == 0 - };//afrt_fragmentrunentry + struct fragmentRun { + long firstFragment; + long long int firstTimestamp; + long duration; + char discontinuity; + };//fragmentRun /// AFRT Box class @@ -92,37 +92,32 @@ namespace MP4{ void setUpdate( long newUpdate ); void setTimeScale( long newScale ); void addQualityEntry( std::string newQuality ); - - void AddFragmentRunEntry( uint32_t FirstFragment = 0, uint32_t FirstFragmentTimestamp = 0, uint32_t FragmentsDuration = 1, uint8_t Discontinuity = 0, uint32_t Offset = 0 ); - void WriteContent( ); + void addFragmentRun( long firstFragment, long long int firstTimestamp, long duration, char discontinuity ); + void regenerate( ); std::string toPrettyString(int indent = 0); private: std::deque qualityModifiers; - std::deque FragmentRunEntryTable; + std::deque fragmentRunTable; };//AFRT Box - struct asrt_segmentrunentry { - uint32_t FirstSegment; - uint32_t FragmentsPerSegment; - };//abst_qualityentry + struct segmentRun { + uint32_t firstSegment; + uint32_t fragmentsPerSegment; + };//segmentRun /// ASRT Box class class ASRT : public Box { public: - ASRT() : Box(0x61737274){}; - void SetUpdate( bool Update = false ); - void AddQualityEntry( std::string Quality = "", uint32_t Offset = 0 ); - void AddSegmentRunEntry( uint32_t FirstSegment = 0, uint32_t FragmentsPerSegment = 100, uint32_t Offset = 0 ); - void WriteContent( ); - void SetVersion( bool NewVersion = 0 ); + ASRT(); + void setVersion( char newVersion ); + void setUpdate( long newUpdate ); + void addQualityEntry( std::string newQuality ); + void addSegmentRun( long firstSegment, long fragmentsPerSegment ); + void regenerate(); std::string toPrettyString(int indent = 0); private: - void SetDefaults( ); - bool isUpdate; - bool Version; - std::vector QualitySegmentUrlModifiers; - std::vector SegmentRunEntryTable; - Box * Container; + std::deque qualityModifiers; + std::deque segmentRunTable; };//ASRT Box }; From d5db1debf58d9e6e6460e5e1636c8c114723cfcc Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Tue, 18 Sep 2012 10:59:09 +0200 Subject: [PATCH 304/788] mfhd box added --- lib/mp4.cpp | 8 ++++++++ lib/mp4.h | 7 ++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index d72aa0d0..44069fca 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -531,4 +531,12 @@ namespace MP4{ } return r; } + + MFHD::MFHD() : Box("mfhd") { + setInt32(0,0); + } + + void MFHD::setSequenceNumber( long newSequenceNumber ) { + setInt32( newSequenceNumber, 4 ); + } }; diff --git a/lib/mp4.h b/lib/mp4.h index 6089b516..7c1692a9 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -119,5 +119,10 @@ namespace MP4{ std::deque qualityModifiers; std::deque segmentRunTable; };//ASRT Box - + + class MFHD : public Box { + public: + MFHD(); + void setSequenceNumber( long newSequenceNumber ); + };//MFHD Box }; From b2b8491655831f0131a8640ea10d816e938cb0bb Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Tue, 18 Sep 2012 11:09:47 +0200 Subject: [PATCH 305/788] moof box added --- lib/mp4.cpp | 17 +++++++++++++++++ lib/mp4.h | 9 +++++++++ 2 files changed, 26 insertions(+) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 44069fca..74cc755f 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -539,4 +539,21 @@ namespace MP4{ void MFHD::setSequenceNumber( long newSequenceNumber ) { setInt32( newSequenceNumber, 4 ); } + + MOOF::MOOF() : Box("moof") {} + + void MOOF::addContent( Box* newContent ) { + content.push_back( newContent ); + isUpdated = true; + } + + void MOOF::regenerate() { + int myOffset = 0; + //retrieve box for each entry + for( std::deque::iterator it = content.begin(); it != content.end(); it++ ) { + memcpy( (char*)data.c_str() + myOffset, (*it)->asBox().c_str(), (*it)->boxedSize() + 1); + myOffset += (*it)->boxedSize(); + } + isUpdated = false; + } }; diff --git a/lib/mp4.h b/lib/mp4.h index 7c1692a9..53a2f359 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -125,4 +125,13 @@ namespace MP4{ MFHD(); void setSequenceNumber( long newSequenceNumber ); };//MFHD Box + + class MOOF : public Box { + public: + MOOF(); + void addContent( Box* newContent ); + void regenerate( ); + private: + std::deque content; + };//MOOF Box }; From 837b7068c4aba6b233a0cbe5626574450d774445 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Tue, 18 Sep 2012 14:26:56 +0200 Subject: [PATCH 306/788] added trun box --- lib/mp4.cpp | 112 +++++++++++++++++++++++++++++++++++++++++++++++++--- lib/mp4.h | 24 +++++++++++ 2 files changed, 131 insertions(+), 5 deletions(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 74cc755f..88cfc563 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -68,7 +68,14 @@ namespace MP4{ } std::string Box::toPrettyString(int indent){ - return std::string(indent, ' ')+"Unimplemented pretty-printing for this box"; + switch( ntohl(((int*)data.c_str())[1]) ) { //type is at this address + case 0x6D666864: return ((MFHD*)this)->toPrettyString(indent); + case 0x6D6F6F66: return ((MOOF*)this)->toPrettyString(indent); + case 0x61627374: return ((ABST*)this)->toPrettyString(indent); + case 0x61667274: return ((AFRT*)this)->toPrettyString(indent); + case 0x61737274: return ((ASRT*)this)->toPrettyString(indent); + default: return std::string(indent, ' ')+"Unimplemented pretty-printing for box "+std::string(data,4,4)+"\n"; + } } void Box::setInt8( char newData, size_t index ) { @@ -316,6 +323,7 @@ namespace MP4{ } void ABST::regenerate( ) { + data.resize( 26 ); int myOffset = 26; //0-terminated movieIdentifier memcpy( (char*)data.c_str() + myOffset, movieIdentifier.c_str(), movieIdentifier.size() + 1); @@ -379,11 +387,11 @@ namespace MP4{ r += std::string(indent+1, ' ')+"CurrMediaTime "+JSON::Value((long long int)getInt32(6)).asString()+"\n"; r += std::string(indent+1, ' ')+"Segment Run Tables "+JSON::Value((long long int)segmentTables.size()).asString()+"\n"; for( uint32_t i = 0; i < segmentTables.size(); i++ ) { - r += ((ASRT*)segmentTables[i])->toPrettyString(indent+2)+"\n"; + r += segmentTables[i]->toPrettyString(indent+2); } r += std::string(indent+1, ' ')+"Fragment Run Tables "+JSON::Value((long long int)fragmentTables.size()).asString()+"\n"; for( uint32_t i = 0; i < fragmentTables.size(); i++ ) { - r += ((AFRT*)fragmentTables[i])->toPrettyString(indent+2)+"\n"; + r += fragmentTables[i]->toPrettyString(indent+2); } return r; } @@ -422,6 +430,7 @@ namespace MP4{ } void AFRT::regenerate( ) { + data.resize( 8 ); int myOffset = 8; setInt8( qualityModifiers.size(), myOffset ); myOffset ++; @@ -461,7 +470,7 @@ namespace MP4{ } r += std::string(indent+1, ' ')+"Fragments "+JSON::Value((long long int)fragmentRunTable.size()).asString()+"\n"; for( uint32_t i = 0; i < fragmentRunTable.size(); i ++ ) { - r += std::string(indent+2, ' ')+"Duration "+JSON::Value((long long int)fragmentRunTable[i].duration).asString()+", starting at "+JSON::Value((long long int)fragmentRunTable[i].firstFragment).asString()+" @ "+JSON::Value((long long int)fragmentRunTable[i].firstTimestamp).asString(); + r += std::string(indent+2, ' ')+"Duration "+JSON::Value((long long int)fragmentRunTable[i].duration).asString()+", starting at "+JSON::Value((long long int)fragmentRunTable[i].firstFragment).asString()+" @ "+JSON::Value((long long int)fragmentRunTable[i].firstTimestamp).asString()+"\n"; } return r; } @@ -493,6 +502,7 @@ namespace MP4{ } void ASRT::regenerate( ) { + data.resize( 4 ); int myOffset = 4; setInt8( qualityModifiers.size(), myOffset ); myOffset ++; @@ -527,7 +537,7 @@ namespace MP4{ } r += std::string(indent+1, ' ')+"Segments "+JSON::Value((long long int)segmentRunTable.size()).asString()+"\n"; for( uint32_t i = 0; i < segmentRunTable.size(); i ++ ) { - r += std::string(indent+2, ' ')+JSON::Value((long long int)segmentRunTable[i].fragmentsPerSegment).asString()+" fragments per, starting at "+JSON::Value((long long int)segmentRunTable[i].firstSegment).asString(); + r += std::string(indent+2, ' ')+JSON::Value((long long int)segmentRunTable[i].fragmentsPerSegment).asString()+" fragments per, starting at "+JSON::Value((long long int)segmentRunTable[i].firstSegment).asString()+"\n"; } return r; } @@ -540,6 +550,12 @@ namespace MP4{ setInt32( newSequenceNumber, 4 ); } + std::string MFHD::toPrettyString( int indent ) { + std::string r; + r += std::string(indent, ' ')+"Movie Fragment Header\n"; + r += std::string(indent+1, ' ')+"SequenceNumber: "+JSON::Value((long long int)getInt32(4)).asString()+"\n"; + } + MOOF::MOOF() : Box("moof") {} void MOOF::addContent( Box* newContent ) { @@ -548,6 +564,7 @@ namespace MP4{ } void MOOF::regenerate() { + data.resize( 0 ); int myOffset = 0; //retrieve box for each entry for( std::deque::iterator it = content.begin(); it != content.end(); it++ ) { @@ -556,4 +573,89 @@ namespace MP4{ } isUpdated = false; } + + std::string MOOF::toPrettyString( int indent ) { + std::string r; + r += std::string(indent, ' ')+"Movie Fragment\n"; + + for( uint32_t i = 0; i < content.size(); i++ ) { + r += content[i]->toPrettyString(indent+2); + } + } + + TRUN::TRUN() : Box("trun") { + setInt8(0,0); + } + + void TRUN::setFlags( long newFlags ) { + setInt24(newFlags,1); + isUpdated = true; + } + + void TRUN::setDataOffset( long newOffset ) { + dataOffset = newOffset; + isUpdated = true; + } + + void TRUN::setFirstSampleFlags( char sampleDependsOn, char sampleIsDependedOn, char sampleHasRedundancy, char sampleIsDifferenceSample ) { + firstSampleFlags = getSampleFlags( sampleDependsOn, sampleIsDependedOn, sampleHasRedundancy, sampleIsDifferenceSample ); + isUpdated = true; + } + + void TRUN::addSampleInformation( long newDuration, long newSize, char sampleDependsOn, char sampleIsDependedOn, char sampleHasRedundancy,char sampleIsDifferenceSample, long newCompositionTimeOffset ) { + trunSampleInformation newSample; + newSample.sampleDuration = newDuration; + newSample.sampleSize = newSize; + newSample.sampleFlags = getSampleFlags( sampleDependsOn, sampleIsDependedOn, sampleHasRedundancy, sampleIsDifferenceSample ); + newSample.sampleCompositionTimeOffset = newCompositionTimeOffset; + allSamples.push_back( newSample ); + isUpdated = true; + } + + long TRUN::getSampleFlags( char sampleDependsOn, char sampleIsDependedOn, char sampleHasRedundancy, char sampleIsDifferenceSample ) { + long sampleFlags = ( sampleDependsOn & 0x03 ); + sampleFlags <<= 2; + sampleFlags += ( sampleIsDependedOn & 0x03 ); + sampleFlags <<= 2; + sampleFlags += ( sampleHasRedundancy & 0x03 ); + sampleFlags <<= 5; + sampleFlags += ( sampleIsDifferenceSample & 0x01 ); + sampleFlags <<= 17; + sampleFlags += 0x0000FFFF; + return sampleFlags; + } + + void TRUN::regenerate( ) { + data.resize( 4 ); + int myOffset = 4; + setInt32( allSamples.size(), myOffset ); + myOffset += 4; + if( getInt24( 1 ) & 0x000001 ) { + setInt32( dataOffset, myOffset ); + myOffset += 4; + } + if( getInt24( 1 ) & 0x000004 ) { + setInt32( firstSampleFlags, myOffset ); + myOffset += 4; + } + for( std::deque::iterator it = allSamples.begin(); it != allSamples.end(); it++ ) { + if( getInt24( 1 ) & 0x000100 ) { + setInt32( (*it).sampleDuration, myOffset ); + myOffset += 4; + } + if( getInt24( 1 ) & 0x000200 ) { + setInt32( (*it).sampleSize, myOffset ); + myOffset += 4; + } + if( getInt24( 1 ) & 0x000400 ) { + setInt32( (*it).sampleFlags, myOffset ); + myOffset += 4; + } + if( getInt24( 1 ) & 0x000800 ) { + setInt32( (*it).sampleCompositionTimeOffset, myOffset ); + myOffset += 4; + } + } + isUpdated = false; + } }; diff --git a/lib/mp4.h b/lib/mp4.h index 53a2f359..70f4d0c1 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -124,6 +124,7 @@ namespace MP4{ public: MFHD(); void setSequenceNumber( long newSequenceNumber ); + std::string toPrettyString(int indent = 0); };//MFHD Box class MOOF : public Box { @@ -131,7 +132,30 @@ namespace MP4{ MOOF(); void addContent( Box* newContent ); void regenerate( ); + std::string toPrettyString(int indent = 0); private: std::deque content; };//MOOF Box + + struct trunSampleInformation { + long sampleDuration; + long sampleSize; + long sampleFlags; + long sampleCompositionTimeOffset; + }; + + class TRUN : public Box { + public: + TRUN(); + void setFlags( long newFlags ); + void setDataOffset( long newOffset ); + void setFirstSampleFlags( char sampleDependsOn, char sampleIsDependedOn, char sampleHasRedundancy, char sampleIsDifferenceSample ); + void addSampleInformation( long newDuration, long newSize, char sampleDependsOn, char sampleIsDependedOn, char sampleHasRedundancy,char sampleIsDifferenceSample, long newCompositionTimeOffset ); + void regenerate(); + private: + long getSampleFlags( char sampleDependsOn, char sampleIsDependedOn, char sampleHasRedundancy, char sampleIsDifferenceSample ); + long dataOffset; + long firstSampleFlags; + std::deque allSamples; + }; }; From 50dd32301dfd1d1f7f082641f1bf5324f1743240 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Tue, 18 Sep 2012 15:19:12 +0200 Subject: [PATCH 307/788] pretty printing fixed --- lib/mp4.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 88cfc563..ee3d524d 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -38,9 +38,9 @@ namespace MP4{ bool Box::read(std::string & newData) { if( newData.size() > 4 ) { size_t size = ntohl( ((int*)newData.c_str())[0] ); - if( newData.size() > size + 8 ) { - data = newData.substr( 0, size + 8 ); - newData.erase( 0, size + 8 ); + if( newData.size() > size ) { + data = newData.substr( 0, size ); + newData.erase( 0, size ); return true; } } @@ -69,12 +69,12 @@ namespace MP4{ std::string Box::toPrettyString(int indent){ switch( ntohl(((int*)data.c_str())[1]) ) { //type is at this address - case 0x6D666864: return ((MFHD*)this)->toPrettyString(indent); - case 0x6D6F6F66: return ((MOOF*)this)->toPrettyString(indent); - case 0x61627374: return ((ABST*)this)->toPrettyString(indent); - case 0x61667274: return ((AFRT*)this)->toPrettyString(indent); - case 0x61737274: return ((ASRT*)this)->toPrettyString(indent); - default: return std::string(indent, ' ')+"Unimplemented pretty-printing for box "+std::string(data,4,4)+"\n"; + 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 0x61737274: return ((ASRT*)this)->toPrettyString(indent); break; + default: return std::string(indent, ' ')+"Unimplemented pretty-printing for box "+std::string(data,4,4)+"\n"; break; } } @@ -100,7 +100,7 @@ namespace MP4{ data.resize( index + 1 ); } newData = htons( newData ); - memcpy( (char*)data.c_str() + index, (char*)newData, 2 ); + memcpy( (void*)(data.c_str() + index), (void*)&newData, 2 ); } short Box::getInt16( size_t index ) { @@ -109,7 +109,7 @@ namespace MP4{ data.resize( index + 1 ); } short result; - memcpy( (char*)result, (char*)data.c_str() + index, 2 ); + memcpy( (void*)&result, (void*)(data.c_str() + index), 2 ); return ntohs(result); } @@ -142,7 +142,7 @@ namespace MP4{ data.resize( index + 3 ); } newData = htonl( newData ); - memcpy( (char*)data.c_str() + index, (char*)newData, 4 ); + memcpy( (char*)data.c_str() + index, (char*)&newData, 4 ); } long Box::getInt32( size_t index ) { @@ -152,7 +152,7 @@ namespace MP4{ } long result; - memcpy( (char*)result, (char*)data.c_str() + index, 4 ); + memcpy( (char*)&result, (char*)data.c_str() + index, 4 ); return ntohl(result); } @@ -577,10 +577,10 @@ namespace MP4{ std::string MOOF::toPrettyString( int indent ) { std::string r; r += std::string(indent, ' ')+"Movie Fragment\n"; - for( uint32_t i = 0; i < content.size(); i++ ) { r += content[i]->toPrettyString(indent+2); } + return r; } TRUN::TRUN() : Box("trun") { From 27f2ea931d00aa94555b39ca09426e3e824c469b Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 24 Sep 2012 10:21:15 +0200 Subject: [PATCH 308/788] Updates to newer new style of newness. --- lib/mp4.cpp | 321 +++++++++++++++++++++++++++++++++++----------------- lib/mp4.h | 85 ++++++++------ 2 files changed, 267 insertions(+), 139 deletions(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index ee3d524d..8190762f 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -7,39 +7,50 @@ /// Contains all MP4 format related code. namespace MP4{ - Box::Box(size_t size) { - isUpdated = false; - data.resize( size + 8 ); - } - - Box::Box( const char* BoxType, size_t size ) { - isUpdated = false; - data.resize( size + 8 ); - memcpy( (char*)data.c_str() + 4, BoxType, 4 ); - } - - Box::Box( std::string & newData ) { - isUpdated = false; - if( !read( newData ) ) { + /// 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; + if (data == 0){ clear(); + }else{ + data_size = ntohl(((int*)data)[0]); } } - Box::~Box() { } + /// If managed, this will free the data pointer. + Box::~Box(){ + if (managed && data != 0){ + free(data); + data = 0; + } + } + /// Returns the values at byte positions 4 through 7. std::string Box::getType() { - return data.substr(4,4); + 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( char* boxType ) { - return !memcmp( boxType, data.c_str() + 4, 4 ); + return !memcmp(boxType, data + 4, 4); } - bool Box::read(std::string & newData) { - if( newData.size() > 4 ) { + /// 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){ size_t size = ntohl( ((int*)newData.c_str())[0] ); - if( newData.size() > size ) { - data = newData.substr( 0, size ); + if (newData.size() >= size){ + void * ret = malloc(size); + if (!ret){return false;} + free(data); + data = ret; + memcpy(data, newData.c_str(), size); newData.erase( 0, size ); return true; } @@ -47,26 +58,39 @@ namespace MP4{ return false; } + /// Returns the total boxed size of this box, including the header. size_t Box::boxedSize() { - return data.size(); + return ntohl(((int*)data)[0]); } + /// Retruns the size of the payload of thix box, excluding the header. + /// This value is defined as boxedSize() - 8. size_t Box::payloadSize() { - return data.size() - 8; + return boxedSize() - 8; } - std::string & Box::asBox() { - if( isUpdated ) { - regenerate( ); - } - ((int*)data.c_str())[0] = htonl( data.size() ); + /// Returns a copy of the data pointer. + char * Box::asBox() { return data; } + /// 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() { - data.resize( 8 ); + if (data && managed){free(data);} + managed = true; + data = 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 . std::string Box::toPrettyString(int indent){ switch( ntohl(((int*)data.c_str())[1]) ) { //type is at this address case 0x6D666864: return ((MFHD*)this)->toPrettyString(indent); break; @@ -78,55 +102,73 @@ namespace MP4{ } } + /// 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 += 8; - if( index > data.size() ) { - data.resize( index ); + 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 += 8; - if( index > data.size() ) { - data.resize( index ); + if (index >= boxedSize()){ + if (!reserve(index, 0, 1)){return 0;} } 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 += 8; - if( index + 1 > data.size() ) { - data.resize( index + 1 ); + if (index+1 >= boxedSize()){ + if (!reserve(index, 0, 2)){return;} } newData = htons( newData ); memcpy( (void*)(data.c_str() + index), (void*)&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 += 8; - if( index + 1 > data.size() ) { - data.resize( index + 1 ); + if (index+1 >= boxedSize()){ + if (!reserve(index, 0, 2)){return 0;} } short result; memcpy( (void*)&result, (void*)(data.c_str() + 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( long newData, size_t index ) { index += 8; - if( index + 2 > data.size() ) { - data.resize( index + 2 ); + 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. long Box::getInt24( size_t index ) { index += 8; - if( index + 2 > data.size() ) { - data.resize( index + 2 ); + if (index+2 >= boxedSize()){ + if (!reserve(index, 0, 3)){return 0;} } long result = data[index]; result <<= 8; @@ -136,81 +178,146 @@ namespace MP4{ 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( long newData, size_t index ) { index += 8; - if( index + 3 > data.size() ) { - data.resize( index + 3 ); + if (index+3 >= boxedSize()){ + if (!reserve(index, 0, 4)){return;} } newData = htonl( newData ); memcpy( (char*)data.c_str() + index, (char*)&newData, 4 ); } - + + /// 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. long Box::getInt32( size_t index ) { index += 8; - if( index + 3 > data.size() ) { - data.resize( index + 3 ); + if (index+3 >= boxedSize()){ + if (!reserve(index, 0, 4)){return 0;} } long result; - memcpy( (char*)&result, (char*)data.c_str() + index, 4 ); return ntohl(result); } + /// 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( long long int newData, size_t index ) { index += 8; - if( index + 7 > data.size() ) { - data.resize( index + 7 ); + if (index+7 >= boxedSize()){ + if (!reserve(index, 0, 8)){return;} } - data[index] = ( newData * 0xFF00000000000000 ) >> 56; - data[index+1] = ( newData * 0x00FF000000000000 ) >> 48; - data[index+2] = ( newData * 0x0000FF0000000000 ) >> 40; - data[index+3] = ( newData * 0x000000FF00000000 ) >> 32; - data[index+4] = ( newData * 0x00000000FF000000 ) >> 24; - data[index+5] = ( newData * 0x0000000000FF0000 ) >> 16; - data[index+6] = ( newData * 0x000000000000FF00 ) >> 8; - data[index+7] = ( newData * 0x00000000000000FF ); + data[index] = ( newData & 0xFF00000000000000 ) >> 56; + data[index+1] = ( newData & 0x00FF000000000000 ) >> 48; + data[index+2] = ( newData & 0x0000FF0000000000 ) >> 40; + data[index+3] = ( newData & 0x000000FF00000000 ) >> 32; + data[index+4] = ( newData & 0x00000000FF000000 ) >> 24; + data[index+5] = ( newData & 0x0000000000FF0000 ) >> 16; + data[index+6] = ( newData & 0x000000000000FF00 ) >> 8; + data[index+7] = ( newData & 0x00000000000000FF ); } - + + /// 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. long long int Box::getInt64( size_t index ) { index += 8; - if( index + 7 > data.size() ) { - data.resize( index + 7 ); + if (index+7 >= boxedSize()){ + if (!reserve(index, 0, 8)){return 0;} } long result = data[index]; - result <<= 8; - result += data[index+1]; - result <<= 8; - result += data[index+2]; - result <<= 8; - result += data[index+3]; - result <<= 8; - result += data[index+4]; - result <<= 8; - result += data[index+5]; - result <<= 8; - result += data[index+6]; - result <<= 8; - result += data[index+7]; + result <<= 8; result += data[index+1]; + result <<= 8; result += data[index+2]; + result <<= 8; result += data[index+3]; + result <<= 8; result += data[index+4]; + result <<= 8; result += data[index+5]; + result <<= 8; result += data[index+6]; + result <<= 8; result += data[index+7]; 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 += 8; - if( index + size > data.size() ) { - data.resize( index + size ); + if (index >= boxedSize()){ + if (!reserve(index, 0, 1)){return;} + data[index] = 0; } - memcpy( (char*)data.c_str() + index, newData, size ); + if (getStringLen(index) != size){ + if (!reserve(index, getStringLen(index)+1, size+1)){return;} + } + memcpy(data + index, newData, size+1); } - void Box::regenerate() { - std::cerr << "Regenerate() not implemented for this box type\n"; + /// 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 += 8; + 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 += 8; + if (index >= boxedSize()){return 0;} + return strlen(data+index); + } + + /// 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 reserve(size_t position, size_t current, size_t wanted){ + if (current == wanted){return true;} + 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 = ret; + data_size = boxedSize() + (wanted-current); + } + //move data behind backward, if any + if (boxedSize() - (position+current) > 0){ + memmove(position+wanted, position+current, boxedSize() - (position+current)); + } + //calculate and set new size + int newSize = boxedSize() + (wanted-current); + ((int*)data)[0] = htonl(newSize); + return true; + }else{ + //make smaller + //move data behind forward, if any + if (boxedSize() - (position+current) > 0){ + memmove(position+wanted, position+current, boxedSize() - (position+current)); + } + //calculate and set new size + int newSize = boxedSize() - (current-wanted); + ((int*)data)[0] = htonl(newSize); + return true; + } + } + ABST::ABST( ) : Box("abst") { setVersion( 0 ); setFlags( 0 ); @@ -239,33 +346,34 @@ namespace MP4{ } void ABST::setProfile( char newProfile ) { - setInt8( ( getInt8(5) & 0x3F ) + ( ( newProfile & 0x02 ) << 6 ), 5 ); + setInt8((getInt8(8) & 0x3F) + ((newProfile & 0x02) << 6), 8); } void ABST::setLive( char newLive ) { - setInt8( ( getInt8(5) & 0xDF ) + ( ( newLive & 0x01 ) << 5 ), 5 ); + setInt8((getInt8(8) & 0xDF) + ((newLive & 0x01) << 5), 8); } void ABST::setUpdate( char newUpdate ) { - setInt8( ( getInt8(5) & 0xEF ) + ( ( newUpdate & 0x01 ) << 4 ), 5 ); + setInt8((getInt8(8) & 0xEF) + ((newUpdate & 0x01) << 4), 8); } void ABST::setTimeScale( long newScale ) { - setInt32( newScale, 6 ); + setInt32(newScale, 9); } void ABST::setCurrentMediaTime( long long int newTime ) { - setInt64( newTime, 10 ); + setInt64( newTime, 13); } void ABST::setSmpteTimeCodeOffset( long long int newTime ) { - setInt64( newTime, 18 ); + setInt64( newTime, 21); } void ABST::setMovieIdentifier( std::string newIdentifier ) { movieIdentifier = newIdentifier; + isUpdated = true; } void ABST::addServerEntry( std::string newEntry ) { @@ -323,8 +431,9 @@ namespace MP4{ } void ABST::regenerate( ) { - data.resize( 26 ); - int myOffset = 26; + if (!isUpdated){return;}//skip if already up to date + data.resize(29); + int myOffset = 29; //0-terminated movieIdentifier memcpy( (char*)data.c_str() + myOffset, movieIdentifier.c_str(), movieIdentifier.size() + 1); myOffset += movieIdentifier.size() + 1; @@ -370,22 +479,24 @@ namespace MP4{ } std::string ABST::toPrettyString( int indent ) { - std::string r; - r += std::string(indent, ' ')+"Bootstrap Info\n"; + regenerate(); + std::stringbuffer r; + r << std::string(indent, ' ') << "Bootstrap Info" << std::endl; + r << std::string(indent+1, ' ') << "Version " << getInt8(0) << std::endl; if( getInt8(5) & 0x10 ) { - r += std::string(indent+1, ' ')+"Update\n"; + r << std::string(indent+1, ' ') << "Update" << std::endl; } else { - r += std::string(indent+1, ' ')+"Replacement or new table\n"; + r << std::string(indent+1, ' ') << "Replacement or new table" << std::endl; } if( getInt8(5) & 0x20 ) { - r += std::string(indent+1, ' ' )+"Live\n"; + r << std::string(indent+1, ' ' ) << "Live" << std::endl; }else{ - r += std::string(indent+1, ' ' )+"Recorded\n"; + r << std::string(indent+1, ' ' ) << "Recorded" << std::endl; } - r += std::string(indent+1, ' ')+"Profile "+JSON::Value((long long int)( getInt8(5) & 0xC0 ) ).asString()+"\n"; - r += std::string(indent+1, ' ')+"Timescale "+JSON::Value((long long int)getInt64(10)).asString()+"\n"; - r += std::string(indent+1, ' ')+"CurrMediaTime "+JSON::Value((long long int)getInt32(6)).asString()+"\n"; - r += std::string(indent+1, ' ')+"Segment Run Tables "+JSON::Value((long long int)segmentTables.size()).asString()+"\n"; + r << std::string(indent+1, ' ') << "Profile " << ( getInt8(5) & 0xC0 ) << std::endl; + r << std::string(indent+1, ' ') << "Timescale " << getInt64(10) << std::endl; + r << std::string(indent+1, ' ') << "CurrMediaTime " << getInt32(6) << std::endl; + r << std::string(indent+1, ' ') << "Segment Run Tables " << segmentTables.size() << std::endl; for( uint32_t i = 0; i < segmentTables.size(); i++ ) { r += segmentTables[i]->toPrettyString(indent+2); } @@ -393,7 +504,7 @@ namespace MP4{ for( uint32_t i = 0; i < fragmentTables.size(); i++ ) { r += fragmentTables[i]->toPrettyString(indent+2); } - return r; + return r.str(); } AFRT::AFRT() : Box("afrt"){ @@ -430,6 +541,7 @@ namespace MP4{ } void AFRT::regenerate( ) { + if (!isUpdated){return;}//skip if already up to date data.resize( 8 ); int myOffset = 8; setInt8( qualityModifiers.size(), myOffset ); @@ -502,6 +614,7 @@ namespace MP4{ } void ASRT::regenerate( ) { + if (!isUpdated){return;}//skip if already up to date data.resize( 4 ); int myOffset = 4; setInt8( qualityModifiers.size(), myOffset ); @@ -564,6 +677,7 @@ namespace MP4{ } void MOOF::regenerate() { + if (!isUpdated){return;}//skip if already up to date data.resize( 0 ); int myOffset = 0; //retrieve box for each entry @@ -626,6 +740,7 @@ namespace MP4{ } void TRUN::regenerate( ) { + if (!isUpdated){return;}//skip if already up to date data.resize( 4 ); int myOffset = 4; setInt32( allSamples.size(), myOffset ); diff --git a/lib/mp4.h b/lib/mp4.h index 70f4d0c1..0f1cf56a 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -3,7 +3,6 @@ #include #include #include -#include ///\todo remove this include #include #include "json.h" @@ -12,20 +11,18 @@ namespace MP4{ class Box { public: - Box( size_t size = 0); - Box( const char* boxType, size_t size = 0 ); - Box( std::string & newData ); + Box(char * datapointer = 0, bool manage = true); ~Box(); std::string getType(); bool isType( char* boxType ); - bool read( std::string & newData ); + bool read(std::string & newData); size_t boxedSize(); size_t payloadSize(); - std::string & asBox(); - void regenerate(); + char * asBox(); void clear(); std::string toPrettyString( int indent = 0 ); protected: + //integer functions void setInt8( char newData, size_t index ); char getInt8( size_t index ); void setInt16( short newData, size_t index ); @@ -36,44 +33,60 @@ namespace MP4{ long getInt32( size_t index ); void setInt64( long long int newData, size_t index ); long long int getInt64( size_t index ); + //string functions void setString(std::string newData, size_t index ); void setString(char* newData, size_t size, size_t index ); - std::string data; - bool isUpdated; + char * getString(size_t index); + size_t getStringLen(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 + int data_size; ///< Currently reserved size + bool managed; ///< If false, will not attempt to resize/free the data pointer. };//Box Class /// ABST Box class class ABST: public Box { public: ABST(); - void setVersion( char newVersion ); - void setFlags( long newFlags ); - void setBootstrapinfoVersion( long newVersion ); - void setProfile( char newProfile ); - void setLive( char newLive ); - void setUpdate( char newUpdate ); - void setTimeScale( long newTimeScale ); - void setCurrentMediaTime( long long int newTime ); - void setSmpteTimeCodeOffset( long long int newTime ); - void setMovieIdentifier( std::string newIdentifier ); - void addServerEntry( std::string newEntry ); - void delServerEntry( std::string delEntry ); - void addQualityEntry( std::string newEntry ); - void delQualityEntry( std::string delEntry ); - void setDrmData( std::string newDrm ); - void setMetaData( std::string newMetaData ); - void addSegmentRunTable( Box * newSegment ); - void addFragmentRunTable( Box * newFragment ); + void setVersion(char newVersion); + char getVersion(); + void setFlags(long newFlags); + long getFlags(); + void setBootstrapinfoVersion(long newVersion); + long getBootstrapinfoVersion(); + void setProfile(char newProfile); + char getProfile(); + void setLive(char newLive); + char getLive(); + void setUpdate(char newUpdate); + char getUpdate(); + void setTimeScale(long newTimeScale); + long getTimeScale(); + void setCurrentMediaTime(long long int newTime); + long long int getCurrentMediaTime(); + void setSmpteTimeCodeOffset(long long int newTime); + long long int getSmpteTimeCodeOffset(); + void setMovieIdentifier(std::string & newIdentifier); + char * getMovieIdentifier(); + void setServerEntry(std::string & entry, int no); + char * getServerEntry(int no); + int getServerEntryCount(); + void setQualityEntry(std::string & entry, int no); + char * getQualityEntry(int no); + int getQualityEntryCount(); + void setDrmData(std::string newDrm); + char * getDrmData(); + void setMetaData(std::string newMetaData); + char * getMetaData(); + void setSegmentRunTable(ASRT table, int no); + ASRT & getSegmentRunTable(int no); + int getSegmentRunTableCount(); + void setFragmentRunTables(AFRT table, int no); + AFRT & getFragmentRunTable(int no); + int getFragmentRunTableCount(); std::string toPrettyString(int indent = 0); - private: - void regenerate(); - std::string movieIdentifier; - std::deque Servers; - std::deque Qualities; - std::string drmData; - std::string metaData; - std::deque segmentTables; - std::deque fragmentTables; };//ABST Box struct fragmentRun { From 770b6e35673da9a5f65e4aba0c5fc242b1de3233 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Mon, 24 Sep 2012 15:31:41 +0200 Subject: [PATCH 309/788] rewritten abst and asrt, including pretty-printing --- lib/mp4.cpp | 705 ++++++++++++++++++++++++++++++---------------------- lib/mp4.h | 110 ++++---- 2 files changed, 465 insertions(+), 350 deletions(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 8190762f..d921b3e5 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -4,6 +4,8 @@ #include "mp4.h" #include "json.h" +#define Int64 long long int + /// Contains all MP4 format related code. namespace MP4{ @@ -49,7 +51,7 @@ namespace MP4{ void * ret = malloc(size); if (!ret){return false;} free(data); - data = ret; + data = (char*)ret; memcpy(data, newData.c_str(), size); newData.erase( 0, size ); return true; @@ -80,7 +82,7 @@ namespace MP4{ void Box::clear() { if (data && managed){free(data);} managed = true; - data = malloc(8); + data = (char*)malloc(8); if (data){ data_size = 8; ((int*)data)[0] = htonl(data_size); @@ -92,13 +94,13 @@ namespace MP4{ /// 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 . std::string Box::toPrettyString(int indent){ - switch( ntohl(((int*)data.c_str())[1]) ) { //type is at this address - case 0x6D666864: return ((MFHD*)this)->toPrettyString(indent); break; - case 0x6D6F6F66: return ((MOOF*)this)->toPrettyString(indent); break; + 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 0x61667274: return ((AFRT*)this)->toPrettyString(indent); break; case 0x61737274: return ((ASRT*)this)->toPrettyString(indent); break; - default: return std::string(indent, ' ')+"Unimplemented pretty-printing for box "+std::string(data,4,4)+"\n"; break; + default: return std::string(indent, ' ')+"Unimplemented pretty-printing for box "+std::string(data+4,4)+"\n"; break; } } @@ -133,7 +135,7 @@ namespace MP4{ if (!reserve(index, 0, 2)){return;} } newData = htons( newData ); - memcpy( (void*)(data.c_str() + index), (void*)&newData, 2 ); + memcpy( data + index, (char*)&newData, 2 ); } /// Gets the 16 bits integer at the given index. @@ -145,7 +147,7 @@ namespace MP4{ if (!reserve(index, 0, 2)){return 0;} } short result; - memcpy( (void*)&result, (void*)(data.c_str() + index), 2 ); + memcpy( (char*)&result, data + index, 2 ); return ntohs(result); } @@ -187,7 +189,7 @@ namespace MP4{ if (!reserve(index, 0, 4)){return;} } newData = htonl( newData ); - memcpy( (char*)data.c_str() + index, (char*)&newData, 4 ); + memcpy( data + index, (char*)&newData, 4 ); } /// Gets the 32 bits integer at the given index. @@ -199,14 +201,14 @@ namespace MP4{ if (!reserve(index, 0, 4)){return 0;} } long result; - memcpy( (char*)&result, (char*)data.c_str() + index, 4 ); + memcpy( (char*)&result, data + index, 4 ); return ntohl(result); } /// 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( long long int newData, size_t index ) { + void Box::setInt64( Int64 newData, size_t index ) { index += 8; if (index+7 >= boxedSize()){ if (!reserve(index, 0, 8)){return;} @@ -224,7 +226,7 @@ namespace MP4{ /// 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. - long long int Box::getInt64( size_t index ) { + Int64 Box::getInt64( size_t index ) { index += 8; if (index+7 >= boxedSize()){ if (!reserve(index, 0, 8)){return 0;} @@ -285,7 +287,7 @@ namespace MP4{ /// 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 reserve(size_t position, size_t current, size_t wanted){ + bool Box::reserve(size_t position, size_t current, size_t wanted){ if (current == wanted){return true;} if (current < wanted){ //make bigger @@ -294,12 +296,12 @@ namespace MP4{ if (!managed){return false;} void * ret = realloc(data, boxedSize() + (wanted-current)); if (!ret){return false;} - data = ret; + data = (char*)ret; data_size = boxedSize() + (wanted-current); } //move data behind backward, if any if (boxedSize() - (position+current) > 0){ - memmove(position+wanted, position+current, boxedSize() - (position+current)); + memmove(data+position+wanted, data+position+current, boxedSize() - (position+current)); } //calculate and set new size int newSize = boxedSize() + (wanted-current); @@ -309,7 +311,7 @@ namespace MP4{ //make smaller //move data behind forward, if any if (boxedSize() - (position+current) > 0){ - memmove(position+wanted, position+current, boxedSize() - (position+current)); + memmove(data+position+wanted, data+position+current, boxedSize() - (position+current)); } //calculate and set new size int newSize = boxedSize() - (current-wanted); @@ -318,7 +320,8 @@ namespace MP4{ } } - ABST::ABST( ) : Box("abst") { + ABST::ABST( ) { + memcpy(data + 4, "abst", 4); setVersion( 0 ); setFlags( 0 ); setBootstrapinfoVersion( 0 ); @@ -328,186 +331,340 @@ namespace MP4{ setTimeScale( 1000 ); setCurrentMediaTime( 0 ); setSmpteTimeCodeOffset( 0 ); - setMovieIdentifier( "" ); - setDrmData( "" ); - setMetaData( "" ); + std::string empty; + setMovieIdentifier( empty ); + setDrmData( empty ); + setMetaData( empty ); } - void ABST::setVersion( char newVersion ) { - setInt8( newVersion, 0 ); + void ABST::setVersion(char newVersion){setInt8(newVersion, 0);} + + char ABST::getVersion(){return getInt8(0);} + + void ABST::setFlags(long newFlags){setInt24(newFlags, 1);} + + long ABST::getFlags(){return getInt24(1);} + + void ABST::setBootstrapinfoVersion(long newVersion){setInt32(newVersion, 4);} + + long 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); } - void ABST::setFlags( long newFlags ) { - setInt24( newFlags, 1 ); - } + char ABST::getProfile(){return (getInt8(8) & 0xC0);}; - void ABST::setBootstrapinfoVersion( long newVersion ) { - setInt32( newVersion, 4 ); - } - - void ABST::setProfile( char newProfile ) { - setInt8((getInt8(8) & 0x3F) + ((newProfile & 0x02) << 6), 8); - } - - - void ABST::setLive( char newLive ) { - setInt8((getInt8(8) & 0xDF) + ((newLive & 0x01) << 5), 8); - } - - - void ABST::setUpdate( char newUpdate ) { - setInt8((getInt8(8) & 0xEF) + ((newUpdate & 0x01) << 4), 8); - } - - void ABST::setTimeScale( long newScale ) { - setInt32(newScale, 9); + void ABST::setLive(bool newLive){ + //live = bit 4 of byte 8. + setInt8((getInt8(8) & 0xDF) + (newLive ? 0x10 : 0 ), 8); } - void ABST::setCurrentMediaTime( long long int newTime ) { - setInt64( newTime, 13); - } + bool ABST::getLive(){return (getInt8(8) & 0x10);} - void ABST::setSmpteTimeCodeOffset( long long int newTime ) { - setInt64( newTime, 21); - } - - void ABST::setMovieIdentifier( std::string newIdentifier ) { - movieIdentifier = newIdentifier; - isUpdated = true; - } - - void ABST::addServerEntry( std::string newEntry ) { - if( std::find( Servers.begin(), Servers.end(), newEntry ) == Servers.end() ) { - Servers.push_back( newEntry ); - isUpdated = true; - } + void ABST::setUpdate(bool newUpdate) { + //update = bit 5 of byte 8. + setInt8((getInt8(8) & 0xEF) + (newUpdate ? 0x08 : 0), 8); } - void ABST::delServerEntry( std::string delEntry ) { - for( std::deque::iterator it = Servers.begin(); it != Servers.end(); it++ ) { - if( (*it) == delEntry ) { - Servers.erase( it ); - isUpdated = true; + bool ABST::getUpdate(){return (getInt8(8) & 0x08);} + + void ABST::setTimeScale(long newScale){setInt32(newScale, 9);} + + long ABST::getTimeScale(){return getInt32(9);} + + void ABST::setCurrentMediaTime(Int64 newTime){setInt64(newTime, 13);} + + Int64 ABST::getCurrentMediaTime(){return getInt64(13);} + + void ABST::setSmpteTimeCodeOffset(Int64 newTime){setInt64(newTime, 21);} + + Int64 ABST::getSmpteTimeCodeOffset(){return getInt64(21);} + + void ABST::setMovieIdentifier(std::string & newIdentifier){setString(newIdentifier, 29);} + + char* ABST::getMovieIdentifier(){return getString(29);} + + long ABST::getServerEntryCount(){ + int countLoc = 29 + getStringLen(29)+1; + return getInt8(countLoc); + } + + void ABST::setServerEntry(std::string & newEntry, long no){ + int countLoc = 29 + getStringLen(29)+1; + int tempLoc = countLoc+1; + for (int i = 0; i < no; i++){ + if (i < getServerEntryCount()){ + tempLoc += getStringLen(tempLoc)+1; + } else { + if(!reserve(tempLoc, 0, no - getServerEntryCount())){return;}; + memset(data+tempLoc, 0, no - getServerEntryCount()); + tempLoc += no - getServerEntryCount(); + setInt8(no, countLoc);//set new serverEntryCount break; } } + setString(newEntry, tempLoc); } - void ABST::addQualityEntry( std::string newEntry ) { - if( std::find( Qualities.begin(), Qualities.end(), newEntry ) == Qualities.end() ) { - Servers.push_back( newEntry ); - isUpdated = true; + ///\return Empty string if no > serverEntryCount(), serverEntry[no] otherwise. + const char* ABST::getServerEntry(long no){ + if (no > getServerEntryCount()){return "";} + int tempLoc = 29+getStringLen(29)+1 + 1;//position of entry count; + for (int i = 0; i < no; i++){tempLoc += getStringLen(tempLoc)+1;} + return getString(tempLoc); + } + + long ABST::getQualityEntryCount(){ + int countLoc = 29 + getStringLen(29)+1 + 1; + for( int i = 0; i< getServerEntryCount(); i++ ) { + countLoc += getStringLen(countLoc)+1; } + return getInt8(countLoc); } - void ABST::delQualityEntry( std::string delEntry ) { - for( std::deque::iterator it = Qualities.begin(); it != Qualities.end(); it++ ) { - if( (*it) == delEntry ) { - Qualities.erase( it ); - isUpdated = true; + void ABST::setQualityEntry(std::string & newEntry, long no){ + int countLoc = 29 + getStringLen(29)+1 + 1; + for( int i = 0; i< getServerEntryCount(); i++ ) { + countLoc += getStringLen(countLoc)+1; + } + int tempLoc = countLoc+1; + for (int i = 0; i < no; i++){ + if (i < getQualityEntryCount()){ + tempLoc += getStringLen(tempLoc)+1; + } else { + if(!reserve(tempLoc, 0, no - getQualityEntryCount())){return;}; + memset(data+tempLoc, 0, no - getQualityEntryCount()); + tempLoc += no - getQualityEntryCount(); + setInt8(no, countLoc);//set new qualityEntryCount break; } } + setString(newEntry, tempLoc); } - + + const char* ABST::getQualityEntry(long no){ + if (no > getQualityEntryCount()){return "";} + int tempLoc = 29+getStringLen(29)+1 + 1;//position of serverentry count; + for (int i = 0; i < getServerEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} + tempLoc += 1; + for (int i = 0; i < no; i++){tempLoc += getStringLen(tempLoc)+1;} + return getString(tempLoc); + } + void ABST::setDrmData( std::string newDrm ) { - drmData = newDrm; - isUpdated = true; + long offset = 29 + getStringLen(29)+1 + 1; + for( int i = 0; i< getServerEntryCount(); i++ ) { + offset += getStringLen(offset)+1; + } + offset++; + for( int i = 0; i< getQualityEntryCount(); i++ ) { + offset += getStringLen(offset)+1; + } + setString(newDrm, offset); + } + + char* ABST::getDrmData() { + long offset = 29 + getStringLen(29)+1 + 1; + for( int i = 0; i< getServerEntryCount(); i++ ) { + offset += getStringLen(offset)+1; + } + offset++; + for( int i = 0; i< getQualityEntryCount(); i++ ) { + offset += getStringLen(offset)+1; + } + return getString(offset); } void ABST::setMetaData( std::string newMetaData ) { - metaData = newMetaData; - isUpdated = true; - } - - void ABST::addSegmentRunTable( Box * newSegment ) { - segmentTables.push_back(newSegment); - isUpdated = true; - } - - void ABST::addFragmentRunTable( Box * newFragment ) { - fragmentTables.push_back(newFragment); - isUpdated = true; + long offset = 29 + getStringLen(29)+1 + 1; + for( int i = 0; i< getServerEntryCount(); i++ ) { + offset += getStringLen(offset)+1; + } + offset++; + for( int i = 0; i< getQualityEntryCount(); i++ ) { + offset += getStringLen(offset)+1; + } + offset+=getStringLen(offset)+1; + setString(newMetaData, offset); } - void ABST::regenerate( ) { - if (!isUpdated){return;}//skip if already up to date - data.resize(29); - int myOffset = 29; - //0-terminated movieIdentifier - memcpy( (char*)data.c_str() + myOffset, movieIdentifier.c_str(), movieIdentifier.size() + 1); - myOffset += movieIdentifier.size() + 1; - //8-bit server amount - setInt8( Servers.size(), myOffset ); - myOffset ++; - //0-terminated string for each entry - for( std::deque::iterator it = Servers.begin(); it != Servers.end(); it++ ) { - memcpy( (char*)data.c_str() + myOffset, (*it).c_str(), (*it).size() + 1); - myOffset += (*it).size() + 1; + char* ABST::getMetaData() { + long offset = 29 + getStringLen(29)+1 + 1; + for( int i = 0; i< getServerEntryCount(); i++ ) { + offset += getStringLen(offset)+1; } - //8-bit quality amount - setInt8( Qualities.size(), myOffset ); - myOffset ++; - //0-terminated string for each entry - for( std::deque::iterator it = Qualities.begin();it != Qualities.end(); it++ ) { - memcpy( (char*)data.c_str() + myOffset, (*it).c_str(), (*it).size() + 1); - myOffset += (*it).size() + 1; + offset++; + for( int i = 0; i< getQualityEntryCount(); i++ ) { + offset += getStringLen(offset)+1; } - //0-terminated DrmData - memcpy( (char*)data.c_str() + myOffset, drmData.c_str(), drmData.size() + 1); - myOffset += drmData.size() + 1; - //0-terminated MetaData - memcpy( (char*)data.c_str() + myOffset, metaData.c_str(), metaData.size() + 1); - myOffset += metaData.size() + 1; - //8-bit segment run amount - setInt8( segmentTables.size(), myOffset ); - myOffset ++; - //retrieve box for each entry - for( std::deque::iterator it = segmentTables.begin(); it != segmentTables.end(); it++ ) { - memcpy( (char*)data.c_str() + myOffset, (*it)->asBox().c_str(), (*it)->boxedSize() ); - myOffset += (*it)->boxedSize(); - } - //8-bit fragment run amount - setInt8( fragmentTables.size(), myOffset ); - myOffset ++; - //retrieve box for each entry - for( std::deque::iterator it = fragmentTables.begin(); it != fragmentTables.end(); it++ ) { - memcpy( (char*)data.c_str() + myOffset, (*it)->asBox().c_str(), (*it)->boxedSize() + 1); - myOffset += (*it)->boxedSize(); - } - isUpdated = false; + offset+=getStringLen(offset)+1; + return getString(offset); } - std::string ABST::toPrettyString( int indent ) { - regenerate(); - std::stringbuffer r; - r << std::string(indent, ' ') << "Bootstrap Info" << std::endl; - r << std::string(indent+1, ' ') << "Version " << getInt8(0) << std::endl; - if( getInt8(5) & 0x10 ) { - r << std::string(indent+1, ' ') << "Update" << std::endl; - } else { - r << std::string(indent+1, ' ') << "Replacement or new table" << std::endl; + long ABST::getSegmentRunTableCount(){ + long offset = 29 + getStringLen(29)+1 + 1; + for( int i = 0; i< getServerEntryCount(); i++ ) { + offset += getStringLen(offset)+1; } - if( getInt8(5) & 0x20 ) { + offset++; + for( int i = 0; i< getQualityEntryCount(); i++ ) { + offset += getStringLen(offset)+1; + } + offset+=getStringLen(offset)+1;//DrmData + offset+=getStringLen(offset)+1;//MetaData + return getInt8(offset); + } + + void ABST::setSegmentRunTable( ASRT newSegment, long no ) { + long offset = 29 + getStringLen(29)+1 + 1; + for( int i = 0; i< getServerEntryCount(); i++ ) { + offset += getStringLen(offset)+1; + } + offset++; + for( int i = 0; i< getQualityEntryCount(); i++ ) { + offset += getStringLen(offset)+1; + } + offset+=getStringLen(offset)+1;//DrmData + offset+=getStringLen(offset)+1;//MetaData + int countLoc = offset; + int tempLoc = countLoc + 1;//segmentRuntableCount + for (int i = 0; i < no; i++){ + if (i < getSegmentRunTableCount()){ + tempLoc += Box(data+8+tempLoc,false).boxedSize(); + } else { + if(!reserve(tempLoc, 0, 8 * (no - getSegmentRunTableCount()))){return;}; + for( int j = 0; j < (no - getSegmentRunTableCount())*8; j += 8 ) { + setInt32(8,tempLoc+j); + } + tempLoc += (no - getServerEntryCount() ) * 8; + setInt8(no, countLoc);//set new serverEntryCount + break; + } + } + } + + ASRT & ABST::getSegmentRunTable( long no ) { + static Box result; + if( no > getSegmentRunTableCount() ) { + static Box res; + return (ASRT&)res; + } + long offset = 29 + getStringLen(29)+1 + 1; + for( int i = 0; i< getServerEntryCount(); i++ ) { + offset += getStringLen(offset)+1; + } + offset++; + for( int i = 0; i< getQualityEntryCount(); i++ ) { + offset += getStringLen(offset)+1; + } + offset+=getStringLen(offset)+1;//DrmData + offset+=getStringLen(offset)+1;//MetaData + int countLoc = offset; + int tempLoc = countLoc + 1;//segmentRuntableCount + for (int i = 0; i < no; i++){ + tempLoc += Box(data+8+tempLoc,false).boxedSize(); + } + result = Box(data+8+tempLoc,false); + return (ASRT&)result; + } + + long ABST::getFragmentRunTableCount() { + long offset = 29 + getStringLen(29)+1 + 1; + for( int i = 0; i< getServerEntryCount(); i++ ) { + offset += getStringLen(offset)+1; + } + offset++; + for( int i = 0; i< getQualityEntryCount(); i++ ) { + offset += getStringLen(offset)+1; + } + offset+=getStringLen(offset)+1;//DrmData + offset+=getStringLen(offset)+1;//MetaData + int countLoc = offset; + int tempLoc = countLoc + 1;//segmentRuntableCount + for (int i = 0; i < getSegmentRunTableCount(); i++){ + tempLoc += Box(data+8+tempLoc,false).boxedSize(); + } + return getInt8( tempLoc ); + } + + //tot hier + + AFRT & ABST::getFragmentRunTable( long no ) { + static Box result; + if( no > getFragmentRunTableCount() ) { + static Box res; + return (AFRT&)res; + } + long offset = 29 + getStringLen(29)+1 + 1; + for( int i = 0; i< getServerEntryCount(); i++ ) { + offset += getStringLen(offset)+1; + } + offset++; + for( int i = 0; i< getQualityEntryCount(); i++ ) { + offset += getStringLen(offset)+1; + } + offset+=getStringLen(offset)+1;//DrmData + offset+=getStringLen(offset)+1;//MetaData + int countLoc = offset; + int tempLoc = countLoc + 1;//segmentRuntableCount + for (int i = 0; i < getSegmentRunTableCount(); i++){ + tempLoc += Box(data+8+tempLoc,false).boxedSize(); + } + tempLoc ++;//segmentRuntableCount + for (int i = 0; i < no; i++){ + tempLoc += Box(data+8+tempLoc,false).boxedSize(); + } + result = Box(data+8+tempLoc,false); + return (AFRT&)result; + } + + std::string ABST::toPrettyString( long indent ) { + std::stringstream r; + r << std::string(indent, ' ') << "[abst] Bootstrap Info" << std::endl; + r << std::string(indent+1, ' ') << "Version " << getVersion() << std::endl; + r << std::string(indent+1, ' ') << "BootstrapinfoVersion " << getBootstrapinfoVersion() << std::endl; + r << std::string(indent+1, ' ') << "Profile " << getProfile() << std::endl; + if( getLive() ) { r << std::string(indent+1, ' ' ) << "Live" << std::endl; }else{ r << std::string(indent+1, ' ' ) << "Recorded" << std::endl; } - r << std::string(indent+1, ' ') << "Profile " << ( getInt8(5) & 0xC0 ) << std::endl; - r << std::string(indent+1, ' ') << "Timescale " << getInt64(10) << std::endl; - r << std::string(indent+1, ' ') << "CurrMediaTime " << getInt32(6) << std::endl; - r << std::string(indent+1, ' ') << "Segment Run Tables " << segmentTables.size() << std::endl; - for( uint32_t i = 0; i < segmentTables.size(); i++ ) { - r += segmentTables[i]->toPrettyString(indent+2); + 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, ' ')+"Fragment Run Tables "+JSON::Value((long long int)fragmentTables.size()).asString()+"\n"; - for( uint32_t i = 0; i < fragmentTables.size(); i++ ) { - r += fragmentTables[i]->toPrettyString(indent+2); + 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( int i = 0; i < getServerEntryCount(); i++ ) { + r << std::string(indent+2, ' ') << getServerEntry(i) << std::endl; + } + r << std::string(indent+1, ' ') << "QualityEntryTable (" << getQualityEntryCount() << ")" << std::endl; + for( int i = 0; i < getQualityEntryCount(); i++ ) { + r << std::string(indent+2, ' ') << 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() : Box("afrt"){ + AFRT::AFRT(){ + memcpy(data + 4, "afrt", 4); setVersion( 0 ); setUpdate( 0 ); setTimeScale( 1000 ); @@ -527,46 +684,17 @@ namespace MP4{ void AFRT::addQualityEntry( std::string newQuality ) { qualityModifiers.push_back( newQuality ); - isUpdated = true; } - void AFRT::addFragmentRun( long firstFragment, long long int firstTimestamp, long duration, char discontinuity ) { + void AFRT::addFragmentRun( long firstFragment, Int64 firstTimestamp, long duration, char discontinuity ) { fragmentRun newRun; newRun.firstFragment = firstFragment; newRun.firstTimestamp = firstTimestamp; newRun.duration = duration; newRun.discontinuity = discontinuity; fragmentRunTable.push_back( newRun ); - isUpdated = true; } - void AFRT::regenerate( ) { - if (!isUpdated){return;}//skip if already up to date - data.resize( 8 ); - int myOffset = 8; - setInt8( qualityModifiers.size(), myOffset ); - myOffset ++; - //0-terminated string for each entry - for( std::deque::iterator it = qualityModifiers.begin();it != qualityModifiers.end(); it++ ) { - memcpy( (char*)data.c_str() + myOffset, (*it).c_str(), (*it).size() + 1); - myOffset += (*it).size() + 1; - } - setInt32( fragmentRunTable.size(), myOffset ); - myOffset += 4; - //table values for each entry - for( std::deque::iterator it = fragmentRunTable.begin();it != fragmentRunTable.end(); it++ ) { - setInt32( (*it).firstFragment, myOffset ); - myOffset += 4; - setInt64( (*it).firstTimestamp, myOffset ); - myOffset += 8; - setInt32( (*it).duration, myOffset ); - myOffset += 4; - setInt8( (*it).discontinuity, myOffset ); - myOffset += 1; - } - isUpdated = false; - } - std::string AFRT::toPrettyString(int indent){ std::string r; r += std::string(indent, ' ')+"Fragment Run Table\n"; @@ -575,19 +703,20 @@ namespace MP4{ }else{ r += std::string(indent+1, ' ')+"Replacement or new table\n"; } - r += std::string(indent+1, ' ')+"Timescale "+JSON::Value((long long int)getInt32(4)).asString()+"\n"; - r += std::string(indent+1, ' ')+"Qualities "+JSON::Value((long long int)qualityModifiers.size()).asString()+"\n"; + r += std::string(indent+1, ' ')+"Timescale "+JSON::Value((Int64)getInt32(4)).asString()+"\n"; + r += std::string(indent+1, ' ')+"Qualities "+JSON::Value((Int64)qualityModifiers.size()).asString()+"\n"; for( uint32_t i = 0; i < qualityModifiers.size(); i++ ) { r += std::string(indent+2, ' ')+"\""+qualityModifiers[i]+"\"\n"; } - r += std::string(indent+1, ' ')+"Fragments "+JSON::Value((long long int)fragmentRunTable.size()).asString()+"\n"; + r += std::string(indent+1, ' ')+"Fragments "+JSON::Value((Int64)fragmentRunTable.size()).asString()+"\n"; for( uint32_t i = 0; i < fragmentRunTable.size(); i ++ ) { - r += std::string(indent+2, ' ')+"Duration "+JSON::Value((long long int)fragmentRunTable[i].duration).asString()+", starting at "+JSON::Value((long long int)fragmentRunTable[i].firstFragment).asString()+" @ "+JSON::Value((long long int)fragmentRunTable[i].firstTimestamp).asString()+"\n"; + r += std::string(indent+2, ' ')+"Duration "+JSON::Value((Int64)fragmentRunTable[i].duration).asString()+", starting at "+JSON::Value((Int64)fragmentRunTable[i].firstFragment).asString()+" @ "+JSON::Value((Int64)fragmentRunTable[i].firstTimestamp).asString()+"\n"; } return r; } - ASRT::ASRT() : Box("asrt") { + ASRT::ASRT(){ + memcpy(data + 4, "asrt", 4); setVersion( 0 ); setUpdate( 0 ); } @@ -596,67 +725,104 @@ namespace MP4{ setInt8( newVersion, 0 ); } + long ASRT::getVersion(){return getInt8(0);} + void ASRT::setUpdate( long newUpdate ) { setInt24( newUpdate, 1 ); } - void ASRT::addQualityEntry( std::string newQuality ) { - qualityModifiers.push_back( newQuality ); - isUpdated = true; + long ASRT::getUpdate(){return getInt24(1);} + + long ASRT::getQualityEntryCount(){ + return getInt8(4); } - void ASRT::addSegmentRun( long firstSegment, long fragmentsPerSegment ) { - segmentRun newRun; - newRun.firstSegment = firstSegment; - newRun.fragmentsPerSegment = fragmentsPerSegment; - segmentRunTable.push_back( newRun ); - isUpdated = true; + void ASRT::setQualityEntry(std::string & newEntry, long no){ + int countLoc = 4; + int tempLoc = countLoc+1; + for (int i = 0; i < no; i++){ + if (i < getQualityEntryCount()){ + tempLoc += getStringLen(tempLoc)+1; + } else { + if(!reserve(tempLoc, 0, no - getQualityEntryCount())){return;}; + memset(data+tempLoc, 0, no - getQualityEntryCount()); + tempLoc += no - getQualityEntryCount(); + setInt8(no, countLoc);//set new qualityEntryCount + break; + } + } + setString(newEntry, tempLoc); } - void ASRT::regenerate( ) { - if (!isUpdated){return;}//skip if already up to date - data.resize( 4 ); - int myOffset = 4; - setInt8( qualityModifiers.size(), myOffset ); - myOffset ++; - //0-terminated string for each entry - for( std::deque::iterator it = qualityModifiers.begin();it != qualityModifiers.end(); it++ ) { - memcpy( (char*)data.c_str() + myOffset, (*it).c_str(), (*it).size() + 1); - myOffset += (*it).size() + 1; - } - setInt32( segmentRunTable.size(), myOffset ); - myOffset += 4; - //table values for each entry - for( std::deque::iterator it = segmentRunTable.begin();it != segmentRunTable.end(); it++ ) { - setInt32( (*it).firstSegment, myOffset ); - myOffset += 4; - setInt32( (*it).fragmentsPerSegment, myOffset ); - myOffset += 4; - } - isUpdated = false; + const char* ASRT::getQualityEntry(long no){ + if (no > getQualityEntryCount()){return "";} + int tempLoc = 5;//position of qualityentry count; + for (int i = 0; i < no; i++){tempLoc += getStringLen(tempLoc)+1;} + return getString(tempLoc); } - + + long ASRT::getSegmentRunEntryCount() { + int tempLoc = 5;//position of qualityentry count; + for (int i = 0; i < getQualityEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} + return getInt32(tempLoc); + } + + void ASRT::setSegmentRun( long firstSegment, long fragmentsPerSegment, long no ) { + int tempLoc = 5;//position of qualityentry count; + for (int i = 0; i < getSegmentRunEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} + int countLoc = tempLoc; + tempLoc += 4; + for (int i = 0; i < no; i++){ + if (i < getSegmentRunEntryCount()){ + tempLoc += 8; + } else { + if(!reserve(tempLoc, 0, (no - getQualityEntryCount())*8)){return;}; + memset(data+tempLoc, 0, (no - getQualityEntryCount())*8); + tempLoc += (no - getQualityEntryCount())*8; + setInt32(no, countLoc);//set new qualityEntryCount + break; + } + } + setInt32(firstSegment,tempLoc); + setInt32(fragmentsPerSegment,tempLoc+4); + } + + asrt_runtable ASRT::getSegmentRun( long no ) { + int tempLoc = 5;//position of qualityentry count; + for (int i = 0; i < getSegmentRunEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} + int countLoc = tempLoc; + tempLoc += 4; + for (int i = 0; i < no; i++){tempLoc += 8;} + asrt_runtable res; + res.firstSegment = getInt32(tempLoc); + res.fragmentsPerSegment = getInt32(tempLoc+4); + return res; + } + std::string ASRT::toPrettyString(int indent){ - std::string r; - r += std::string(indent, ' ')+"Segment Run Table\n"; - if (getInt24(1)){ - r += std::string(indent+1, ' ')+"Update\n"; + std::stringstream r; + r << std::string(indent, ' ') << "[asrt] Segment Run Table" << 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\n"; + r << std::string(indent+1, ' ') << "Replacement or new table" << std::endl; } - r += std::string(indent+1, ' ')+"Qualities "+JSON::Value((long long int)qualityModifiers.size()).asString()+"\n"; - for( uint32_t i = 0; i < qualityModifiers.size(); i++ ) { - r += std::string(indent+2, ' ')+"\""+qualityModifiers[i]+"\"\n"; + r << std::string(indent+1, ' ') << "QualityEntryTable (" << getQualityEntryCount() << ")" << std::endl; + for( int i = 0; i < getQualityEntryCount(); i++ ) { + r << std::string(indent+2, ' ') << getQualityEntry(i) << std::endl; } - r += std::string(indent+1, ' ')+"Segments "+JSON::Value((long long int)segmentRunTable.size()).asString()+"\n"; - for( uint32_t i = 0; i < segmentRunTable.size(); i ++ ) { - r += std::string(indent+2, ' ')+JSON::Value((long long int)segmentRunTable[i].fragmentsPerSegment).asString()+" fragments per, starting at "+JSON::Value((long long int)segmentRunTable[i].firstSegment).asString()+"\n"; + r << std::string(indent+1, ' ') << "SegmentRunEntryTable (" << getSegmentRunEntryCount()<< ")" << std::endl; + for( int i = 0; i < getSegmentRunEntryCount(); i ++ ) { + r << std::string(indent+2, ' ') << "FirstSegment " << getSegmentRun(i).firstSegment << std::endl; + r << std::string(indent+2, ' ') << "FragmentsPerSegment " << getSegmentRun(i).fragmentsPerSegment << std::endl; } - return r; + return r.str(); } - MFHD::MFHD() : Box("mfhd") { + MFHD::MFHD(){ setInt32(0,0); + memcpy(data + 4, "mfhd", 4); } void MFHD::setSequenceNumber( long newSequenceNumber ) { @@ -666,54 +832,35 @@ namespace MP4{ std::string MFHD::toPrettyString( int indent ) { std::string r; r += std::string(indent, ' ')+"Movie Fragment Header\n"; - r += std::string(indent+1, ' ')+"SequenceNumber: "+JSON::Value((long long int)getInt32(4)).asString()+"\n"; + r += std::string(indent+1, ' ')+"SequenceNumber: "+JSON::Value((Int64)getInt32(4)).asString()+"\n"; } - MOOF::MOOF() : Box("moof") {} + MOOF::MOOF(){ + memcpy(data + 4, "moof", 4); + } void MOOF::addContent( Box* newContent ) { content.push_back( newContent ); - isUpdated = true; - } - - void MOOF::regenerate() { - if (!isUpdated){return;}//skip if already up to date - data.resize( 0 ); - int myOffset = 0; - //retrieve box for each entry - for( std::deque::iterator it = content.begin(); it != content.end(); it++ ) { - memcpy( (char*)data.c_str() + myOffset, (*it)->asBox().c_str(), (*it)->boxedSize() + 1); - myOffset += (*it)->boxedSize(); - } - isUpdated = false; } std::string MOOF::toPrettyString( int indent ) { - std::string r; - r += std::string(indent, ' ')+"Movie Fragment\n"; - for( uint32_t i = 0; i < content.size(); i++ ) { - r += content[i]->toPrettyString(indent+2); - } - return r; + } - TRUN::TRUN() : Box("trun") { - setInt8(0,0); + TRUN::TRUN(){ + memcpy(data + 4, "trun", 4); } void TRUN::setFlags( long newFlags ) { setInt24(newFlags,1); - isUpdated = true; } void TRUN::setDataOffset( long newOffset ) { dataOffset = newOffset; - isUpdated = true; } void TRUN::setFirstSampleFlags( char sampleDependsOn, char sampleIsDependedOn, char sampleHasRedundancy, char sampleIsDifferenceSample ) { firstSampleFlags = getSampleFlags( sampleDependsOn, sampleIsDependedOn, sampleHasRedundancy, sampleIsDifferenceSample ); - isUpdated = true; } void TRUN::addSampleInformation( long newDuration, long newSize, char sampleDependsOn, char sampleIsDependedOn, char sampleHasRedundancy,char sampleIsDifferenceSample, long newCompositionTimeOffset ) { @@ -723,7 +870,6 @@ namespace MP4{ newSample.sampleFlags = getSampleFlags( sampleDependsOn, sampleIsDependedOn, sampleHasRedundancy, sampleIsDifferenceSample ); newSample.sampleCompositionTimeOffset = newCompositionTimeOffset; allSamples.push_back( newSample ); - isUpdated = true; } long TRUN::getSampleFlags( char sampleDependsOn, char sampleIsDependedOn, char sampleHasRedundancy, char sampleIsDifferenceSample ) { @@ -738,39 +884,4 @@ namespace MP4{ sampleFlags += 0x0000FFFF; return sampleFlags; } - - void TRUN::regenerate( ) { - if (!isUpdated){return;}//skip if already up to date - data.resize( 4 ); - int myOffset = 4; - setInt32( allSamples.size(), myOffset ); - myOffset += 4; - if( getInt24( 1 ) & 0x000001 ) { - setInt32( dataOffset, myOffset ); - myOffset += 4; - } - if( getInt24( 1 ) & 0x000004 ) { - setInt32( firstSampleFlags, myOffset ); - myOffset += 4; - } - for( std::deque::iterator it = allSamples.begin(); it != allSamples.end(); it++ ) { - if( getInt24( 1 ) & 0x000100 ) { - setInt32( (*it).sampleDuration, myOffset ); - myOffset += 4; - } - if( getInt24( 1 ) & 0x000200 ) { - setInt32( (*it).sampleSize, myOffset ); - myOffset += 4; - } - if( getInt24( 1 ) & 0x000400 ) { - setInt32( (*it).sampleFlags, myOffset ); - myOffset += 4; - } - if( getInt24( 1 ) & 0x000800 ) { - setInt32( (*it).sampleCompositionTimeOffset, myOffset ); - myOffset += 4; - } - } - isUpdated = false; - } }; diff --git a/lib/mp4.h b/lib/mp4.h index 0f1cf56a..450b63a9 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -1,7 +1,9 @@ #pragma once #include #include +#include #include +#include #include #include #include "json.h" @@ -46,49 +48,6 @@ namespace MP4{ bool managed; ///< If false, will not attempt to resize/free the data pointer. };//Box Class - /// ABST Box class - class ABST: public Box { - public: - ABST(); - void setVersion(char newVersion); - char getVersion(); - void setFlags(long newFlags); - long getFlags(); - void setBootstrapinfoVersion(long newVersion); - long getBootstrapinfoVersion(); - void setProfile(char newProfile); - char getProfile(); - void setLive(char newLive); - char getLive(); - void setUpdate(char newUpdate); - char getUpdate(); - void setTimeScale(long newTimeScale); - long getTimeScale(); - void setCurrentMediaTime(long long int newTime); - long long int getCurrentMediaTime(); - void setSmpteTimeCodeOffset(long long int newTime); - long long int getSmpteTimeCodeOffset(); - void setMovieIdentifier(std::string & newIdentifier); - char * getMovieIdentifier(); - void setServerEntry(std::string & entry, int no); - char * getServerEntry(int no); - int getServerEntryCount(); - void setQualityEntry(std::string & entry, int no); - char * getQualityEntry(int no); - int getQualityEntryCount(); - void setDrmData(std::string newDrm); - char * getDrmData(); - void setMetaData(std::string newMetaData); - char * getMetaData(); - void setSegmentRunTable(ASRT table, int no); - ASRT & getSegmentRunTable(int no); - int getSegmentRunTableCount(); - void setFragmentRunTables(AFRT table, int no); - AFRT & getFragmentRunTable(int no); - int getFragmentRunTableCount(); - std::string toPrettyString(int indent = 0); - };//ABST Box - struct fragmentRun { long firstFragment; long long int firstTimestamp; @@ -113,24 +72,26 @@ namespace MP4{ std::deque fragmentRunTable; };//AFRT Box - struct segmentRun { - uint32_t firstSegment; - uint32_t fragmentsPerSegment; - };//segmentRun + struct asrt_runtable{ + long firstSegment; + long fragmentsPerSegment; + }; /// ASRT Box class class ASRT : public Box { public: ASRT(); void setVersion( char newVersion ); + long getVersion(); void setUpdate( long newUpdate ); - void addQualityEntry( std::string newQuality ); - void addSegmentRun( long firstSegment, long fragmentsPerSegment ); - void regenerate(); + long getUpdate(); + long getQualityEntryCount(); + void setQualityEntry( std::string & newQuality, long no ); + const char* getQualityEntry( long no ); + long getSegmentRunEntryCount(); + void setSegmentRun( long firstSegment, long fragmentsPerSegment, long no ); + asrt_runtable getSegmentRun( long no ); std::string toPrettyString(int indent = 0); - private: - std::deque qualityModifiers; - std::deque segmentRunTable; };//ASRT Box class MFHD : public Box { @@ -150,6 +111,49 @@ namespace MP4{ std::deque content; };//MOOF Box + /// ABST Box class + class ABST: public Box { + public: + ABST(); + void setVersion(char newVersion); + char getVersion(); + void setFlags(long newFlags); + long getFlags(); + void setBootstrapinfoVersion(long newVersion); + long getBootstrapinfoVersion(); + void setProfile(char newProfile); + char getProfile(); + void setLive(bool newLive); + bool getLive(); + void setUpdate(bool newUpdate); + bool getUpdate(); + void setTimeScale(long newTimeScale); + long getTimeScale(); + void setCurrentMediaTime(long long int newTime); + long long int getCurrentMediaTime(); + void setSmpteTimeCodeOffset(long long int newTime); + long long int getSmpteTimeCodeOffset(); + void setMovieIdentifier(std::string & newIdentifier); + char * getMovieIdentifier(); + long getServerEntryCount(); + void setServerEntry(std::string & entry, long no); + const char * getServerEntry(long no); + long getQualityEntryCount(); + void setQualityEntry(std::string & entry, long no); + const char * getQualityEntry(long no); + void setDrmData(std::string newDrm); + char * getDrmData(); + void setMetaData(std::string newMetaData); + char * getMetaData(); + long getSegmentRunTableCount(); + void setSegmentRunTable(ASRT table, long no); + ASRT & getSegmentRunTable(long no); + long getFragmentRunTableCount(); + void setFragmentRunTable(AFRT table, long no); + AFRT & getFragmentRunTable(long no); + std::string toPrettyString(long indent = 0); + };//ABST Box + struct trunSampleInformation { long sampleDuration; long sampleSize; From 70fe4bbe98fdfcc8a5846e573ecffd081f3af57b Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Tue, 25 Sep 2012 10:00:00 +0200 Subject: [PATCH 310/788] ABST Done --- lib/mp4.cpp | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index d921b3e5..5d34e232 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -538,11 +538,14 @@ namespace MP4{ for( int j = 0; j < (no - getSegmentRunTableCount())*8; j += 8 ) { setInt32(8,tempLoc+j); } - tempLoc += (no - getServerEntryCount() ) * 8; + tempLoc += (no - getSegmentRunTableCount() ) * 8; setInt8(no, countLoc);//set new serverEntryCount break; } } + ASRT oldSegment = Box(data+8+tempLoc,false); + if(!reserve(tempLoc,oldSegment.boxedSize(),newSegment.boxedSize())){return;} + memcpy( data+8+tempLoc, newSegment.asBox(), newSegment.boxedSize() ); } ASRT & ABST::getSegmentRunTable( long no ) { @@ -589,7 +592,41 @@ namespace MP4{ return getInt8( tempLoc ); } - //tot hier + void ABST::setFragmentRunTable( AFRT newFragment, long no ) { + long offset = 29 + getStringLen(29)+1 + 1; + for( int i = 0; i< getServerEntryCount(); i++ ) { + offset += getStringLen(offset)+1; + } + offset++; + for( int i = 0; i< getQualityEntryCount(); i++ ) { + offset += getStringLen(offset)+1; + } + offset+=getStringLen(offset)+1;//DrmData + offset+=getStringLen(offset)+1;//MetaData + + int tempLoc = offset + 1;//segmentRuntableCount + for (int i = 0; i < getSegmentRunTableCount(); i++ ) { + tempLoc += Box(data+8+tempLoc,false).boxedSize();//walk through all segments + } + int countloc = tempLoc; + tempLoc += 1; + for (int i = 0; i < no; i++){ + if (i < getFragmentRunTableCount()){ + tempLoc += Box(data+8+tempLoc,false).boxedSize(); + } else { + if(!reserve(tempLoc, 0, 8 * (no - getFragmentRunTableCount()))){return;}; + for( int j = 0; j < (no - getFragmentRunTableCount())*8; j += 8 ) { + setInt32(8,tempLoc+j); + } + tempLoc += (no - getFragmentRunTableCount() ) * 8; + setInt8(no, countLoc);//set new serverEntryCount + break; + } + } + AFRT oldFragment = Box(data+8+tempLoc,false); + if(!reserve(tempLoc,oldFragment.boxedSize(),newFragment.boxedSize())){return;} + memcpy( data+8+tempLoc, newFragment.asBox(), newFragment.boxedSize() ); + } AFRT & ABST::getFragmentRunTable( long no ) { static Box result; From 7492e99628118ec9440ac9ae8e25b7d10bbd1c69 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 25 Sep 2012 11:25:46 +0200 Subject: [PATCH 311/788] Implemented TRUN box. --- lib/mp4.cpp | 187 ++++++++++++++++++++++++++++++++++++++++++++-------- lib/mp4.h | 43 ++++++++---- 2 files changed, 190 insertions(+), 40 deletions(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 5d34e232..8e78fdf3 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -887,38 +887,169 @@ namespace MP4{ TRUN::TRUN(){ memcpy(data + 4, "trun", 4); } - - void TRUN::setFlags( long newFlags ) { + + void TRUN::setFlags(long newFlags){ setInt24(newFlags,1); } - - void TRUN::setDataOffset( long newOffset ) { - dataOffset = newOffset; + + long TRUN::getFlags(){ + return getInt24(1); } - - void TRUN::setFirstSampleFlags( char sampleDependsOn, char sampleIsDependedOn, char sampleHasRedundancy, char sampleIsDifferenceSample ) { - firstSampleFlags = getSampleFlags( sampleDependsOn, sampleIsDependedOn, sampleHasRedundancy, sampleIsDifferenceSample ); + + void TRUN::setDataOffset(long newOffset){ + if (getFlags() & dataOffset){ + setInt32(newOffset, 8); + } } - - void TRUN::addSampleInformation( long newDuration, long newSize, char sampleDependsOn, char sampleIsDependedOn, char sampleHasRedundancy,char sampleIsDifferenceSample, long newCompositionTimeOffset ) { - trunSampleInformation newSample; - newSample.sampleDuration = newDuration; - newSample.sampleSize = newSize; - newSample.sampleFlags = getSampleFlags( sampleDependsOn, sampleIsDependedOn, sampleHasRedundancy, sampleIsDifferenceSample ); - newSample.sampleCompositionTimeOffset = newCompositionTimeOffset; - allSamples.push_back( newSample ); + + long TRUN::getDataOffset(){ + if (getFlags() & dataOffset){ + return getInt32(8); + }else{ + return 0; + } } - - long TRUN::getSampleFlags( char sampleDependsOn, char sampleIsDependedOn, char sampleHasRedundancy, char sampleIsDifferenceSample ) { - long sampleFlags = ( sampleDependsOn & 0x03 ); - sampleFlags <<= 2; - sampleFlags += ( sampleIsDependedOn & 0x03 ); - sampleFlags <<= 2; - sampleFlags += ( sampleHasRedundancy & 0x03 ); - sampleFlags <<= 5; - sampleFlags += ( sampleIsDifferenceSample & 0x01 ); - sampleFlags <<= 17; - sampleFlags += 0x0000FFFF; - return sampleFlags; + + void TRUN::setFirstSampleFlags(long newSampleFlags){ + if (!(getFlags() & firstSampleFlags)){return;} + if (getFlags() & dataOffset){ + setInt32(newSampleFlags, 12); + }else{ + setInt32(newSampleFlags, 8); + } } + + long TRUN::getFirstSampleFlags(){ + if (!(getFlags() & firstSampleFlags)){return 0;} + if (getFlags() & dataOffset){ + return getInt32(12); + }else{ + return getInt32(8); + } + } + + long TRUN::getSampleInformationCount(){ + return getInt32(4); + } + + void TRUN::setSampleInformation(trunSampleInformation newSample, long no){ + long flags = getFlags(); + long sampInfoSize = 0; + if (flags & sampleDuration){sampInfoSize += 4;} + if (flags & sampleSize){sampInfoSize += 4;} + if (flags & sampleFlags){sampInfoSize += 4;} + if (flags & sampleOffsets){sampInfoSize += 4;} + long offset = 8; + if (flags & dataOffset){offset += 4;} + if (flags & firstSampleFlags){offset += 4;} + long innerOffset = 0; + if (flags & sampleDuration){ + setInt32(newSample.sampleDuration, offset + no*sampInfoSize + innerOffset); + innerOffset += 4; + } + if (flags & sampleSize){ + setInt32(newSample.sampleSize, offset + no*sampInfoSize + innerOffset); + innerOffset += 4; + } + if (flags & sampleFlags){ + setInt32(newSample.sampleFlags, offset + no*sampInfoSize + innerOffset); + innerOffset += 4; + } + if (flags & sampleOffsets){ + setInt32(newSample.sampleOffset, offset + no*sampInfoSize + innerOffset); + innerOffset += 4; + } + if (getSampleInformationCount() < no+1){ + setInt32(no+1,4); + } + } + + trunSampleInformation TRUN::getSampleInformation(long no){ + trunSampleInformation ret; + ret.sampleDuration = 0; + ret.sampleSize = 0; + ret.sampleFlags = 0; + ret.sampleOffset = 0; + if (getSampleInformationCount() < no+1){return ret;} + long flags = getFlags(); + long sampInfoSize = 0; + if (flags & sampleDuration){sampInfoSize += 4;} + if (flags & sampleSize){sampInfoSize += 4;} + if (flags & sampleFlags){sampInfoSize += 4;} + if (flags & sampleOffsets){sampInfoSize += 4;} + long offset = 8; + if (flags & dataOffset){offset += 4;} + if (flags & firstSampleFlags){offset += 4;} + long innerOffset = 0; + if (flags & sampleDuration){ + ret.sampleDuration = getInt32(offset + no*sampInfoSize + innerOffset); + innerOffset += 4; + } + if (flags & sampleSize){ + ret.sampleSize = getInt32(offset + no*sampInfoSize + innerOffset); + innerOffset += 4; + } + if (flags & sampleFlags){ + ret.sampleFlags = getInt32(offset + no*sampInfoSize + innerOffset); + innerOffset += 4; + } + if (flags & sampleOffsets){ + ret.sampleOffset = getInt32(offset + no*sampInfoSize + innerOffset); + innerOffset += 4; + } + return ret; + } + + std::string TRUN::toPrettyString(long indent){ + std::stringstream r; + r << std::string(indent, ' ') << "[trun] Track Fragment Run" << std::endl; + r << std::string(indent+1, ' ') << "Version " << getInt8(0) << std::endl; + + long flags = getFlags(); + r << std::string(indent+1, ' ') << "Flags"; + if (flags & dataOffset){r << " dataOffset";} + if (flags & firstSampleFlags){r << " firstSampleFlags";} + if (flags & sampleDuration){r << " sampleDuration";} + if (flags & sampleSize){r << " sampleSize";} + if (flags & sampleFlags){r << " sampleFlags";} + if (flags & sampleOffsets){r << " sampleOffsets";} + r << std::endl; + + if (flags & dataOffset){r << std::string(indent+1, ' ') << "Data Offset " << getDataOffset() << std::endl;} + if (flags & dataOffset){r << std::string(indent+1, ' ') << "Sample Flags" << prettyFlags(getFirstSampleFlags()) << std::endl;} + + r << std::string(indent+1, ' ') << "SampleInformation (" << getSampleInformationCount() << "):" << std::endl; + for (int i = 0; i < getSampleInformationCount(); ++i){ + r << std::string(indent+2, ' ') << "[" << i << "]" << std::endl; + trunSampleInformation samp = getSampleInformation(i); + if (flags & sampleDuration){ + r << std::string(indent+2, ' ') << "Duration " << samp.sampleDuration << std::endl; + } + if (flags & sampleSize){ + r << std::string(indent+2, ' ') << "Size " << samp.sampleSize << std::endl; + } + if (flags & sampleFlags){ + r << std::string(indent+2, ' ') << "Flags " << prettyFlags(samp.sampleFlags) << std::endl; + } + if (flags & sampleOffsets){ + r << std::string(indent+2, ' ') << "Offset " << samp.sampleOffset << std::endl; + } + } + + return r.str(); + } + + std::string TRUN::prettyFlags(long flag){ + std::stringstream r; + if (flag & noIPicture){r << " noIPicture";} + if (flag & isIPicture){r << " isIPicture";} + if (flag & noDisposable){r << " noDisposable";} + if (flag & isDisposable){r << " isDisposable";} + if (flag & isRedundant){r << " isRedundant";} + if (flag & noRedundant){r << " noRedundant";} + if (flag & noKeySample){r << " noKeySample";}else{r << " isKeySample";} + return r.str(); + } + + }; diff --git a/lib/mp4.h b/lib/mp4.h index 450b63a9..29c8fd56 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -158,21 +158,40 @@ namespace MP4{ long sampleDuration; long sampleSize; long sampleFlags; - long sampleCompositionTimeOffset; + long sampleOffset; + }; + enum trunflags { + dataOffset = 0x000001, + firstSampleFlags = 0x000004, + sampleDuration = 0x000100, + sampleSize = 0x000200, + sampleFlags = 0x000400, + sampleOffsets = 0x000800 + }; + enum sampleflags { + noIPicture = 0x1000000, + isIPicture = 0x2000000, + noDisposable = 0x400000, + isDisposable = 0x800000, + isRedundant = 0x100000, + noRedundant = 0x200000, + noKeySample = 0x10000, + iskeySample = 0x0, + MUST_BE_PRESENT = 0x1 }; - class TRUN : public Box { public: TRUN(); - void setFlags( long newFlags ); - void setDataOffset( long newOffset ); - void setFirstSampleFlags( char sampleDependsOn, char sampleIsDependedOn, char sampleHasRedundancy, char sampleIsDifferenceSample ); - void addSampleInformation( long newDuration, long newSize, char sampleDependsOn, char sampleIsDependedOn, char sampleHasRedundancy,char sampleIsDifferenceSample, long newCompositionTimeOffset ); - void regenerate(); - private: - long getSampleFlags( char sampleDependsOn, char sampleIsDependedOn, char sampleHasRedundancy, char sampleIsDifferenceSample ); - long dataOffset; - long firstSampleFlags; - std::deque allSamples; + void setFlags(long newFlags); + long getFlags(); + void setDataOffset(long newOffset); + long getDataOffset(); + void setFirstSampleFlags(long newSampleFlags); + long getFirstSampleFlags(); + long getSampleInformationCount(); + void setSampleInformation(trunSampleInformation newSample, long no); + trunSampleInformation getSampleInformation(long no); + std::string toPrettyString(long indent = 0); + std::string prettyFlags(long flag); }; }; From d9b8f7c7f05e0721ea4a73217d0a7f16a894e3ed Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 25 Sep 2012 12:42:55 +0200 Subject: [PATCH 312/788] Implemented TFHD box - fixed TRUN namespace pollution. --- lib/mp4.cpp | 207 ++++++++++++++++++++++++++++++++++++++++++---------- lib/mp4.h | 43 +++++++++-- 2 files changed, 203 insertions(+), 47 deletions(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 8e78fdf3..f7a318f3 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -897,13 +897,13 @@ namespace MP4{ } void TRUN::setDataOffset(long newOffset){ - if (getFlags() & dataOffset){ + if (getFlags() & trundataOffset){ setInt32(newOffset, 8); } } long TRUN::getDataOffset(){ - if (getFlags() & dataOffset){ + if (getFlags() & trundataOffset){ return getInt32(8); }else{ return 0; @@ -911,8 +911,8 @@ namespace MP4{ } void TRUN::setFirstSampleFlags(long newSampleFlags){ - if (!(getFlags() & firstSampleFlags)){return;} - if (getFlags() & dataOffset){ + if (!(getFlags() & trunfirstSampleFlags)){return;} + if (getFlags() & trundataOffset){ setInt32(newSampleFlags, 12); }else{ setInt32(newSampleFlags, 8); @@ -920,8 +920,8 @@ namespace MP4{ } long TRUN::getFirstSampleFlags(){ - if (!(getFlags() & firstSampleFlags)){return 0;} - if (getFlags() & dataOffset){ + if (!(getFlags() & trunfirstSampleFlags)){return 0;} + if (getFlags() & trundataOffset){ return getInt32(12); }else{ return getInt32(8); @@ -935,27 +935,27 @@ namespace MP4{ void TRUN::setSampleInformation(trunSampleInformation newSample, long no){ long flags = getFlags(); long sampInfoSize = 0; - if (flags & sampleDuration){sampInfoSize += 4;} - if (flags & sampleSize){sampInfoSize += 4;} - if (flags & sampleFlags){sampInfoSize += 4;} - if (flags & sampleOffsets){sampInfoSize += 4;} + if (flags & trunsampleDuration){sampInfoSize += 4;} + if (flags & trunsampleSize){sampInfoSize += 4;} + if (flags & trunsampleFlags){sampInfoSize += 4;} + if (flags & trunsampleOffsets){sampInfoSize += 4;} long offset = 8; - if (flags & dataOffset){offset += 4;} - if (flags & firstSampleFlags){offset += 4;} + if (flags & trundataOffset){offset += 4;} + if (flags & trunfirstSampleFlags){offset += 4;} long innerOffset = 0; - if (flags & sampleDuration){ + if (flags & trunsampleDuration){ setInt32(newSample.sampleDuration, offset + no*sampInfoSize + innerOffset); innerOffset += 4; } - if (flags & sampleSize){ + if (flags & trunsampleSize){ setInt32(newSample.sampleSize, offset + no*sampInfoSize + innerOffset); innerOffset += 4; } - if (flags & sampleFlags){ + if (flags & trunsampleFlags){ setInt32(newSample.sampleFlags, offset + no*sampInfoSize + innerOffset); innerOffset += 4; } - if (flags & sampleOffsets){ + if (flags & trunsampleOffsets){ setInt32(newSample.sampleOffset, offset + no*sampInfoSize + innerOffset); innerOffset += 4; } @@ -973,27 +973,27 @@ namespace MP4{ if (getSampleInformationCount() < no+1){return ret;} long flags = getFlags(); long sampInfoSize = 0; - if (flags & sampleDuration){sampInfoSize += 4;} - if (flags & sampleSize){sampInfoSize += 4;} - if (flags & sampleFlags){sampInfoSize += 4;} - if (flags & sampleOffsets){sampInfoSize += 4;} + if (flags & trunsampleDuration){sampInfoSize += 4;} + if (flags & trunsampleSize){sampInfoSize += 4;} + if (flags & trunsampleFlags){sampInfoSize += 4;} + if (flags & trunsampleOffsets){sampInfoSize += 4;} long offset = 8; - if (flags & dataOffset){offset += 4;} - if (flags & firstSampleFlags){offset += 4;} + if (flags & trundataOffset){offset += 4;} + if (flags & trunfirstSampleFlags){offset += 4;} long innerOffset = 0; - if (flags & sampleDuration){ + if (flags & trunsampleDuration){ ret.sampleDuration = getInt32(offset + no*sampInfoSize + innerOffset); innerOffset += 4; } - if (flags & sampleSize){ + if (flags & trunsampleSize){ ret.sampleSize = getInt32(offset + no*sampInfoSize + innerOffset); innerOffset += 4; } - if (flags & sampleFlags){ + if (flags & trunsampleFlags){ ret.sampleFlags = getInt32(offset + no*sampInfoSize + innerOffset); innerOffset += 4; } - if (flags & sampleOffsets){ + if (flags & trunsampleOffsets){ ret.sampleOffset = getInt32(offset + no*sampInfoSize + innerOffset); innerOffset += 4; } @@ -1007,31 +1007,31 @@ namespace MP4{ long flags = getFlags(); r << std::string(indent+1, ' ') << "Flags"; - if (flags & dataOffset){r << " dataOffset";} - if (flags & firstSampleFlags){r << " firstSampleFlags";} - if (flags & sampleDuration){r << " sampleDuration";} - if (flags & sampleSize){r << " sampleSize";} - if (flags & sampleFlags){r << " sampleFlags";} - if (flags & sampleOffsets){r << " sampleOffsets";} + if (flags & trundataOffset){r << " dataOffset";} + if (flags & trunfirstSampleFlags){r << " firstSampleFlags";} + if (flags & trunsampleDuration){r << " sampleDuration";} + if (flags & trunsampleSize){r << " sampleSize";} + if (flags & trunsampleFlags){r << " sampleFlags";} + if (flags & trunsampleOffsets){r << " sampleOffsets";} r << std::endl; - if (flags & dataOffset){r << std::string(indent+1, ' ') << "Data Offset " << getDataOffset() << std::endl;} - if (flags & dataOffset){r << std::string(indent+1, ' ') << "Sample Flags" << prettyFlags(getFirstSampleFlags()) << std::endl;} + if (flags & trundataOffset){r << std::string(indent+1, ' ') << "Data Offset " << getDataOffset() << std::endl;} + if (flags & trundataOffset){r << std::string(indent+1, ' ') << "Sample Flags" << prettySampleFlags(getFirstSampleFlags()) << std::endl;} r << std::string(indent+1, ' ') << "SampleInformation (" << getSampleInformationCount() << "):" << std::endl; for (int i = 0; i < getSampleInformationCount(); ++i){ r << std::string(indent+2, ' ') << "[" << i << "]" << std::endl; trunSampleInformation samp = getSampleInformation(i); - if (flags & sampleDuration){ + if (flags & trunsampleDuration){ r << std::string(indent+2, ' ') << "Duration " << samp.sampleDuration << std::endl; } - if (flags & sampleSize){ + if (flags & trunsampleSize){ r << std::string(indent+2, ' ') << "Size " << samp.sampleSize << std::endl; } - if (flags & sampleFlags){ - r << std::string(indent+2, ' ') << "Flags " << prettyFlags(samp.sampleFlags) << std::endl; + if (flags & trunsampleFlags){ + r << std::string(indent+2, ' ') << "Flags " << prettySampleFlags(samp.sampleFlags) << std::endl; } - if (flags & sampleOffsets){ + if (flags & trunsampleOffsets){ r << std::string(indent+2, ' ') << "Offset " << samp.sampleOffset << std::endl; } } @@ -1039,7 +1039,7 @@ namespace MP4{ return r.str(); } - std::string TRUN::prettyFlags(long flag){ + std::string prettySampleFlags(long flag){ std::stringstream r; if (flag & noIPicture){r << " noIPicture";} if (flag & isIPicture){r << " isIPicture";} @@ -1051,5 +1051,132 @@ namespace MP4{ return r.str(); } + TFHD::TFHD(){ + memcpy(data + 4, "tfhd", 4); + } + + void TFHD::setFlags(long newFlags){ + setInt24(newFlags,1); + } + + long TFHD::getFlags(){ + return getInt24(1); + } + + void TFHD::setTrackID(long newID){ + setInt32(newID,4); + } + + long TFHD::getTrackID(){ + return getInt32(4); + } + + void TFHD::setBaseDataOffset(long long newOffset){ + if (getFlags() & tfhdBaseOffset){ + setInt64(newOffset, 8); + } + } + + long long TFHD::getBaseDataOffset(){ + if (getFlags() & tfhdBaseOffset){ + return getInt64(8); + }else{ + return 0; + } + } + + void TFHD::setSampleDescriptionIndex(long newIndex){ + if (!(getFlags() & tfhdSampleDesc)){return;} + int offset = 8; + if (getFlags() & tfhdBaseOffset){offset += 8;} + setInt32(newIndex, offset); + } + + long TFHD::getSampleDescriptionIndex(){ + if (!(getFlags() & tfhdSampleDesc)){return 0;} + int offset = 8; + if (getFlags() & tfhdBaseOffset){offset += 8;} + return getInt32(offset); + } + + void TFHD::setDefaultSampleDuration(long newDuration){ + if (!(getFlags() & tfhdSampleDura)){return;} + int offset = 8; + if (getFlags() & tfhdBaseOffset){offset += 8;} + if (getFlags() & tfhdSampleDesc){offset += 4;} + setInt32(newDuration, offset); + } + + long TFHD::getDefaultSampleDuration(){ + if (!(getFlags() & tfhdSampleDura)){return 0;} + int offset = 8; + if (getFlags() & tfhdBaseOffset){offset += 8;} + if (getFlags() & tfhdSampleDesc){offset += 4;} + return getInt32(offset); + } + + void TFHD::setDefaultSampleSize(long newSize){ + if (!(getFlags() & tfhdSampleSize)){return;} + int offset = 8; + if (getFlags() & tfhdBaseOffset){offset += 8;} + if (getFlags() & tfhdSampleDesc){offset += 4;} + if (getFlags() & tfhdSampleDura){offset += 4;} + setInt32(newSize, offset); + } + + long TFHD::getDefaultSampleSize(){ + if (!(getFlags() & tfhdSampleSize)){return 0;} + int offset = 8; + if (getFlags() & tfhdBaseOffset){offset += 8;} + if (getFlags() & tfhdSampleDesc){offset += 4;} + if (getFlags() & tfhdSampleDura){offset += 4;} + return getInt32(offset); + } + + void TFHD::setDefaultSampleFlags(long newFlags){ + if (!(getFlags() & tfhdSampleFlag)){return;} + int offset = 8; + if (getFlags() & tfhdBaseOffset){offset += 8;} + if (getFlags() & tfhdSampleDesc){offset += 4;} + if (getFlags() & tfhdSampleDura){offset += 4;} + if (getFlags() & tfhdSampleSize){offset += 4;} + setInt32(newFlags, offset); + } + + long TFHD::getDefaultSampleFlags(){ + if (!(getFlags() & tfhdSampleFlag)){return 0;} + int offset = 8; + if (getFlags() & tfhdBaseOffset){offset += 8;} + if (getFlags() & tfhdSampleDesc){offset += 4;} + if (getFlags() & tfhdSampleDura){offset += 4;} + if (getFlags() & tfhdSampleSize){offset += 4;} + return getInt32(offset); + } + + std::string TFHD::toPrettyString(long indent){ + std::stringstream r; + r << std::string(indent, ' ') << "[tfhd] Track Fragment Header" << std::endl; + r << std::string(indent+1, ' ') << "Version " << getInt8(0) << std::endl; + + long flags = getFlags(); + r << std::string(indent+1, ' ') << "Flags"; + if (flags & tfhdBaseOffset){r << " BaseOffset";} + if (flags & tfhdSampleDesc){r << " SampleDesc";} + if (flags & tfhdSampleDura){r << " SampleDura";} + if (flags & tfhdSampleSize){r << " SampleSize";} + if (flags & tfhdSampleFlag){r << " SampleFlag";} + if (flags & tfhdNoDuration){r << " NoDuration";} + r << std::endl; + + r << std::string(indent+1, ' ') << "TrackID " << getTrackID() << std::endl; + + if (flags & tfhdBaseOffset){r << std::string(indent+1, ' ') << "Base Offset " << getBaseDataOffset() << std::endl;} + if (flags & tfhdSampleDesc){r << std::string(indent+1, ' ') << "Sample Description Index " << getSampleDescriptionIndex() << std::endl;} + if (flags & tfhdSampleDura){r << std::string(indent+1, ' ') << "Default Sample Duration " << getDefaultSampleDuration() << std::endl;} + if (flags & tfhdSampleSize){r << std::string(indent+1, ' ') << "Default Same Size " << getDefaultSampleSize() << std::endl;} + if (flags & tfhdSampleFlag){r << std::string(indent+1, ' ') << "Default Sample Flags " << prettySampleFlags(getDefaultSampleFlags()) << std::endl;} + + return r.str(); + } }; diff --git a/lib/mp4.h b/lib/mp4.h index 29c8fd56..23129af1 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -161,12 +161,12 @@ namespace MP4{ long sampleOffset; }; enum trunflags { - dataOffset = 0x000001, - firstSampleFlags = 0x000004, - sampleDuration = 0x000100, - sampleSize = 0x000200, - sampleFlags = 0x000400, - sampleOffsets = 0x000800 + trundataOffset = 0x000001, + trunfirstSampleFlags = 0x000004, + trunsampleDuration = 0x000100, + trunsampleSize = 0x000200, + trunsampleFlags = 0x000400, + trunsampleOffsets = 0x000800 }; enum sampleflags { noIPicture = 0x1000000, @@ -179,6 +179,7 @@ namespace MP4{ iskeySample = 0x0, MUST_BE_PRESENT = 0x1 }; + std::string prettySampleFlags(long flag); class TRUN : public Box { public: TRUN(); @@ -192,6 +193,34 @@ namespace MP4{ void setSampleInformation(trunSampleInformation newSample, long no); trunSampleInformation getSampleInformation(long no); std::string toPrettyString(long indent = 0); - std::string prettyFlags(long flag); }; + + enum tfhdflags { + tfhdBaseOffset = 0x000001, + tfhdSampleDesc = 0x000002, + tfhdSampleDura = 0x000008, + tfhdSampleSize = 0x000010, + tfhdSampleFlag = 0x000020, + tfhdNoDuration = 0x010000, + }; + class TFHD : public Box { + public: + TFHD(); + void setFlags(long newFlags); + long getFlags(); + void setTrackID(long newID); + long getTrackID(); + void setBaseDataOffset(long long newOffset); + long long getBaseDataOffset(); + void setSampleDescriptionIndex(long newIndex); + long getSampleDescriptionIndex(); + void setDefaultSampleDuration(long newDuration); + long getDefaultSampleDuration(); + void setDefaultSampleSize(long newSize); + long getDefaultSampleSize(); + void setDefaultSampleFlags(long newFlags); + long getDefaultSampleFlags(); + std::string toPrettyString(long indent = 0); + }; + }; From 6c7eeec5756c1986e21687e8165babccddb47f56 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Tue, 25 Sep 2012 11:27:46 +0200 Subject: [PATCH 313/788] MOOF & MFHD prettyprinting. MFHD Done in general --- lib/mp4.cpp | 175 +++++++++++++++++++++++++++++++++++++--------------- lib/mp4.h | 55 +++++++++-------- 2 files changed, 155 insertions(+), 75 deletions(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index f7a318f3..b55d9e45 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -95,10 +95,10 @@ namespace MP4{ /// If this failed, it will print out a message saying pretty-printing is not implemented for . std::string Box::toPrettyString(int 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 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 0x61667274: return ((AFRT*)this)->toPrettyString(indent); break; case 0x61737274: return ((ASRT*)this)->toPrettyString(indent); break; default: return std::string(indent, ' ')+"Unimplemented pretty-printing for box "+std::string(data+4,4)+"\n"; break; } @@ -543,7 +543,7 @@ namespace MP4{ break; } } - ASRT oldSegment = Box(data+8+tempLoc,false); + Box oldSegment = Box(data+8+tempLoc,false); if(!reserve(tempLoc,oldSegment.boxedSize(),newSegment.boxedSize())){return;} memcpy( data+8+tempLoc, newSegment.asBox(), newSegment.boxedSize() ); } @@ -608,7 +608,7 @@ namespace MP4{ for (int i = 0; i < getSegmentRunTableCount(); i++ ) { tempLoc += Box(data+8+tempLoc,false).boxedSize();//walk through all segments } - int countloc = tempLoc; + int countLoc = tempLoc; tempLoc += 1; for (int i = 0; i < no; i++){ if (i < getFragmentRunTableCount()){ @@ -623,7 +623,7 @@ namespace MP4{ break; } } - AFRT oldFragment = Box(data+8+tempLoc,false); + Box oldFragment = Box(data+8+tempLoc,false); if(!reserve(tempLoc,oldFragment.boxedSize(),newFragment.boxedSize())){return;} memcpy( data+8+tempLoc, newFragment.asBox(), newFragment.boxedSize() ); } @@ -685,10 +685,8 @@ namespace MP4{ for( int i = 0; i < getQualityEntryCount(); i++ ) { r << std::string(indent+2, ' ') << 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); @@ -707,49 +705,117 @@ namespace MP4{ setTimeScale( 1000 ); } - void AFRT::setVersion( char newVersion ) { - setInt8( newVersion ,0 ); + void AFRT::setVersion(char newVersion){setInt8(newVersion, 0);} + + long AFRT::getVersion(){return getInt8(0);} + + void AFRT::setUpdate(long newUpdate){setInt24(newUpdate, 1);} + + long AFRT::getUpdate(){return getInt24(1);} + + void AFRT::setTimeScale(long newScale){setInt32(newScale, 4);} + + long AFRT::getTimeScale(){return getInt32(4);} + + long AFRT::getQualityEntryCount(){return getInt8(8);} + + void AFRT::setQualityEntry(std::string & newEntry, long no){ + int countLoc = 8; + int tempLoc = countLoc+1; + for (int i = 0; i < no; i++){ + if (i < getQualityEntryCount()){ + tempLoc += getStringLen(tempLoc)+1; + } else { + if(!reserve(tempLoc, 0, no - getQualityEntryCount())){return;}; + memset(data+tempLoc, 0, no - getQualityEntryCount()); + tempLoc += no - getQualityEntryCount(); + setInt8(no, countLoc);//set new qualityEntryCount + break; + } + } + setString(newEntry, tempLoc); } - void AFRT::setUpdate( long newUpdate ) { - setInt24( newUpdate, 1 ); + const char* AFRT::getQualityEntry(long no){ + if (no > getQualityEntryCount()){return "";} + int tempLoc = 9;//position of first quality entry + for (int i = 0; i < no; i++){tempLoc += getStringLen(tempLoc)+1;} + return getString(tempLoc); } - void AFRT::setTimeScale( long newScale ) { - setInt32( newScale, 4 ); + long AFRT::getFragmentRunCount(){ + int tempLoc = 9; + for( int i = 0; i < getQualityEntryCount(); i++ ){ + tempLoc += getStringLen(tempLoc)+1; + } + return getInt32(tempLoc); } - void AFRT::addQualityEntry( std::string newQuality ) { - qualityModifiers.push_back( newQuality ); + void AFRT::setFragmentRun( afrt_runtable newRun, long no ) { + int tempLoc = 9; + for( int i = 0; i < getQualityEntryCount(); i++ ){ + tempLoc += getStringLen(tempLoc)+1; + } + int countLoc = tempLoc; + tempLoc += 4; + for (int i = 0; i < no; i++){ + if (i < getFragmentRunCount()){ + tempLoc += 17; + } else { + if(!reserve(tempLoc, 0, 17 * (no - getQualityEntryCount()))){return;}; + memset(data+tempLoc, 0, 17 * (no - getQualityEntryCount())); + tempLoc += 17 * (no - getQualityEntryCount()); + setInt32(no, countLoc);//set new qualityEntryCount + break; + } + } + setInt32(newRun.firstFragment,tempLoc); + setInt64(newRun.firstTimestamp,tempLoc+4); + setInt32(newRun.duration,tempLoc+12); + setInt8(newRun.discontinuity,tempLoc+16); } - void AFRT::addFragmentRun( long firstFragment, Int64 firstTimestamp, long duration, char discontinuity ) { - fragmentRun newRun; - newRun.firstFragment = firstFragment; - newRun.firstTimestamp = firstTimestamp; - newRun.duration = duration; - newRun.discontinuity = discontinuity; - fragmentRunTable.push_back( newRun ); + afrt_runtable AFRT::getFragmentRun( long no ) { + afrt_runtable res; + if( no > getFragmentRunCount() ){return res;} + int tempLoc = 9; + for( int i = 0; i < getQualityEntryCount(); i++ ){ + tempLoc += getStringLen(tempLoc)+1; + } + int countLoc = tempLoc; + tempLoc += 4; + for (int i = 0; i < no; i++){ + tempLoc += 17; + } + res.firstFragment = getInt32(tempLoc); + res.firstTimestamp = getInt64(tempLoc+4); + res.duration = getInt32(tempLoc+12); + res.discontinuity = getInt8(tempLoc+16); + return res; } std::string AFRT::toPrettyString(int indent){ - std::string r; - r += std::string(indent, ' ')+"Fragment Run Table\n"; - if (getInt24(1)){ - r += std::string(indent+1, ' ')+"Update\n"; + std::stringstream r; + r << std::string(indent, ' ') << "[afrt] Fragment Run Table" << std::endl; + if (getUpdate()){ + r << std::string(indent+1, ' ') << "Update" << std::endl; }else{ - r += std::string(indent+1, ' ')+"Replacement or new table\n"; + r << std::string(indent+1, ' ') << "Replacement or new table" << std::endl; } - r += std::string(indent+1, ' ')+"Timescale "+JSON::Value((Int64)getInt32(4)).asString()+"\n"; - r += std::string(indent+1, ' ')+"Qualities "+JSON::Value((Int64)qualityModifiers.size()).asString()+"\n"; - for( uint32_t i = 0; i < qualityModifiers.size(); i++ ) { - r += std::string(indent+2, ' ')+"\""+qualityModifiers[i]+"\"\n"; + r << std::string(indent+1, ' ') << "Timescale " << getTimeScale() << std::endl; + r << std::string(indent+1, ' ') << "QualitySegmentUrlModifiers (" << getQualityEntryCount() << ")" << std::endl; + for( int i = 0; i < getQualityEntryCount(); i++ ) { + r << std::string(indent+2, ' ') << getQualityEntry(i) << std::endl; } - r += std::string(indent+1, ' ')+"Fragments "+JSON::Value((Int64)fragmentRunTable.size()).asString()+"\n"; - for( uint32_t i = 0; i < fragmentRunTable.size(); i ++ ) { - r += std::string(indent+2, ' ')+"Duration "+JSON::Value((Int64)fragmentRunTable[i].duration).asString()+", starting at "+JSON::Value((Int64)fragmentRunTable[i].firstFragment).asString()+" @ "+JSON::Value((Int64)fragmentRunTable[i].firstTimestamp).asString()+"\n"; + r << std::string(indent+1, ' ') << "FragmentRunEntryTable (" << getFragmentRunCount() << ")" << std::endl; + for( int i = 0; i < getFragmentRunCount(); i ++ ) { + afrt_runtable myRun = getFragmentRun(i); + r << std::string(indent+2, ' ') << "First Fragment " << myRun.firstFragment << std::endl; + r << std::string(indent+2, ' ') << "First Timestamp " << myRun.firstTimestamp << std::endl; + r << std::string(indent+2, ' ') << "Duration " << myRun.duration << std::endl; + r << std::string(indent+2, ' ') << "Discontinuity " << myRun.discontinuity << std::endl; } - return r; + return r.str(); } ASRT::ASRT(){ @@ -806,16 +872,16 @@ namespace MP4{ void ASRT::setSegmentRun( long firstSegment, long fragmentsPerSegment, long no ) { int tempLoc = 5;//position of qualityentry count; - for (int i = 0; i < getSegmentRunEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} + for (int i = 0; i < getQualityEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} int countLoc = tempLoc; tempLoc += 4; for (int i = 0; i < no; i++){ if (i < getSegmentRunEntryCount()){ tempLoc += 8; } else { - if(!reserve(tempLoc, 0, (no - getQualityEntryCount())*8)){return;}; - memset(data+tempLoc, 0, (no - getQualityEntryCount())*8); - tempLoc += (no - getQualityEntryCount())*8; + if(!reserve(tempLoc, 0, (no - getSegmentRunEntryCount())*8)){return;}; + memset(data+tempLoc, 0, (no - getSegmentRunEntryCount())*8); + tempLoc += (no - getSegmentRunEntryCount())*8; setInt32(no, countLoc);//set new qualityEntryCount break; } @@ -825,12 +891,13 @@ namespace MP4{ } asrt_runtable ASRT::getSegmentRun( long no ) { + asrt_runtable res; + if( no > getSegmentRunEntryCount() ) { return res; } int tempLoc = 5;//position of qualityentry count; for (int i = 0; i < getSegmentRunEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} int countLoc = tempLoc; tempLoc += 4; for (int i = 0; i < no; i++){tempLoc += 8;} - asrt_runtable res; res.firstSegment = getInt32(tempLoc); res.fragmentsPerSegment = getInt32(tempLoc+4); return res; @@ -858,18 +925,19 @@ namespace MP4{ } MFHD::MFHD(){ - setInt32(0,0); memcpy(data + 4, "mfhd", 4); + setInt32(0,0); } - void MFHD::setSequenceNumber( long newSequenceNumber ) { - setInt32( newSequenceNumber, 4 ); - } + void MFHD::setSequenceNumber(long newSequenceNumber){setInt32(newSequenceNumber, 4);} + + long MFHD::getSequenceNumber(){return getInt32(4);} std::string MFHD::toPrettyString( int indent ) { - std::string r; - r += std::string(indent, ' ')+"Movie Fragment Header\n"; - r += std::string(indent+1, ' ')+"SequenceNumber: "+JSON::Value((Int64)getInt32(4)).asString()+"\n"; + std::stringstream r; + r << std::string(indent, ' ') << "[mfhd] Movie Fragment Header" << std::endl; + r << std::string(indent+1, ' ') << "SequenceNumber " << getSequenceNumber() << std::endl; + return r.str(); } MOOF::MOOF(){ @@ -881,7 +949,16 @@ namespace MP4{ } std::string MOOF::toPrettyString( int indent ) { - + std::stringstream r; + r << std::string(indent, ' ') << "[moof] Movie Fragment Box" << std::endl; + Box curBox; + int tempLoc = 0; + while( tempLoc < boxedSize()-8 ){ + curBox = Box( data+8+tempLoc, false ); + r << curBox.toPrettyString(indent+1); + tempLoc += curBox.boxedSize(); + } + return r.str(); } TRUN::TRUN(){ diff --git a/lib/mp4.h b/lib/mp4.h index 23129af1..2cf44f27 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -48,28 +48,30 @@ namespace MP4{ bool managed; ///< If false, will not attempt to resize/free the data pointer. };//Box Class - struct fragmentRun { + struct afrt_runtable { long firstFragment; long long int firstTimestamp; long duration; - char discontinuity; + long discontinuity; };//fragmentRun - /// AFRT Box class class AFRT : public Box { public: AFRT(); void setVersion( char newVersion ); + long getVersion(); void setUpdate( long newUpdate ); + long getUpdate(); void setTimeScale( long newScale ); - void addQualityEntry( std::string newQuality ); - void addFragmentRun( long firstFragment, long long int firstTimestamp, long duration, char discontinuity ); - void regenerate( ); + long getTimeScale(); + long getQualityEntryCount(); + void setQualityEntry( std::string & newQuality, long no ); + const char * getQualityEntry( long no ); + long getFragmentRunCount(); + void setFragmentRun( afrt_runtable newRun, long no ); + afrt_runtable getFragmentRun( long no ); std::string toPrettyString(int indent = 0); - private: - std::deque qualityModifiers; - std::deque fragmentRunTable; };//AFRT Box struct asrt_runtable{ @@ -94,23 +96,6 @@ namespace MP4{ std::string toPrettyString(int indent = 0); };//ASRT Box - class MFHD : public Box { - public: - MFHD(); - void setSequenceNumber( long newSequenceNumber ); - std::string toPrettyString(int indent = 0); - };//MFHD Box - - class MOOF : public Box { - public: - MOOF(); - void addContent( Box* newContent ); - void regenerate( ); - std::string toPrettyString(int indent = 0); - private: - std::deque content; - };//MOOF Box - /// ABST Box class class ABST: public Box { public: @@ -154,6 +139,24 @@ namespace MP4{ std::string toPrettyString(long indent = 0); };//ABST Box + class MFHD : public Box { + public: + MFHD(); + void setSequenceNumber( long newSequenceNumber ); + long getSequenceNumber(); + std::string toPrettyString(int indent = 0); + };//MFHD Box + + class MOOF : public Box { + public: + MOOF(); + void addContent( Box* newContent ); + void regenerate( ); + std::string toPrettyString(int indent = 0); + private: + std::deque content; + };//MOOF Box + struct trunSampleInformation { long sampleDuration; long sampleSize; From 5b3a53e9f5804dc320a88c1e676cadb00cda3d6a Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Tue, 25 Sep 2012 12:46:49 +0200 Subject: [PATCH 314/788] Added MOOF and TRAF boxes --- lib/mp4.cpp | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++-- lib/mp4.h | 16 ++++++-- 2 files changed, 114 insertions(+), 8 deletions(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index b55d9e45..1018dcc5 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -100,6 +100,8 @@ namespace MP4{ case 0x61627374: return ((ABST*)this)->toPrettyString(indent); break; case 0x61667274: return ((AFRT*)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; default: return std::string(indent, ' ')+"Unimplemented pretty-printing for box "+std::string(data+4,4)+"\n"; break; } } @@ -944,8 +946,43 @@ namespace MP4{ memcpy(data + 4, "moof", 4); } - void MOOF::addContent( Box* newContent ) { - content.push_back( newContent ); + long MOOF::getContentCount() { + int res = 0; + int tempLoc = 0; + while( tempLoc < boxedSize()-8 ){ + res ++; + tempLoc += Box(data+8+tempLoc, false).boxedSize(); + } + return res; + } + + void MOOF::setContent( Box newContent, long no ) { + int tempLoc = 0; + int contentCount = getContentCount(); + for (int i = 0; i < no; i++){ + if (i < contentCount){ + tempLoc += Box(data+8+tempLoc,false).boxedSize(); + } else { + if(!reserve(tempLoc, 0, (no - contentCount)*8)){return;}; + memset(data+tempLoc, 0, (no - contentCount)*8); + tempLoc += (no - contentCount)*8; + break; + } + } + Box oldContent = Box( data+8+tempLoc, false ); + if( !reserve( tempLoc, oldContent.boxedSize(), newContent.boxedSize() ) ) { return; } + memcpy( data+8+tempLoc, newContent.asBox(), newContent.boxedSize() ); + } + + Box MOOF::getContent( long no ){ + if( no > getContentCount() ) { return Box(); } + int i = 0; + int tempLoc = 0; + while( i < no ) { + tempLoc += Box( data+8+tempLoc, false).boxedSize(); + i++; + } + return Box(data+8+tempLoc, false); } std::string MOOF::toPrettyString( int indent ) { @@ -953,14 +990,75 @@ namespace MP4{ r << std::string(indent, ' ') << "[moof] Movie Fragment Box" << std::endl; Box curBox; int tempLoc = 0; - while( tempLoc < boxedSize()-8 ){ - curBox = Box( data+8+tempLoc, false ); + int contentCount = getContentCount(); + for( int i = 0; i < contentCount; i++ ) { + curBox = getContent(i); r << curBox.toPrettyString(indent+1); tempLoc += curBox.boxedSize(); } return r.str(); } + TRAF::TRAF(){ + memcpy(data + 4, "traf", 4); + } + + long TRAF::getContentCount() { + int res = 0; + int tempLoc = 0; + while( tempLoc < boxedSize()-8 ){ + res ++; + tempLoc += Box(data+8+tempLoc, false).boxedSize(); + } + return res; + } + + void TRAF::setContent( Box newContent, long no ) { + int tempLoc = 0; + int contentCount = getContentCount(); + for (int i = 0; i < no; i++){ + if (i < contentCount){ + tempLoc += Box(data+8+tempLoc,false).boxedSize(); + } else { + if(!reserve(tempLoc, 0, (no - contentCount)*8)){return;}; + memset(data+tempLoc, 0, (no - contentCount)*8); + tempLoc += (no - contentCount)*8; + break; + } + } + Box oldContent = Box( data+8+tempLoc, false ); + if( !reserve( tempLoc, oldContent.boxedSize(), newContent.boxedSize() ) ) { return; } + memcpy( data+8+tempLoc, newContent.asBox(), newContent.boxedSize() ); + } + + Box TRAF::getContent( long no ){ + if( no > getContentCount() ) { return Box(); } + int i = 0; + int tempLoc = 0; + while( i < no ) { + tempLoc += Box( data+8+tempLoc, false).boxedSize(); + i++; + } + return Box(data+8+tempLoc, false); + } + + std::string TRAF::toPrettyString( int indent ) { + std::stringstream r; + r << std::string(indent, ' ') << "[traf] Track Fragment Box" << 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 += curBox.boxedSize(); + } + return r.str(); + } + + + + TRUN::TRUN(){ memcpy(data + 4, "trun", 4); } diff --git a/lib/mp4.h b/lib/mp4.h index 2cf44f27..2cb8b243 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -150,13 +150,21 @@ namespace MP4{ class MOOF : public Box { public: MOOF(); - void addContent( Box* newContent ); - void regenerate( ); + long getContentCount(); + void setContent( Box newContent, long no ); + Box getContent( long no ); std::string toPrettyString(int indent = 0); - private: - std::deque content; };//MOOF Box + class TRAF : public Box { + public: + TRAF(); + long getContentCount(); + void setContent( Box newContent, long no ); + Box getContent( long no ); + std::string toPrettyString(int indent = 0); + };//TRAF Box + struct trunSampleInformation { long sampleDuration; long sampleSize; From 4450fbdd7007346f64be54f684b678a04b4b502b Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 25 Sep 2012 14:09:22 +0200 Subject: [PATCH 315/788] Implemented AFRA box. --- lib/mp4.cpp | 183 ++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/mp4.h | 35 ++++++++++ 2 files changed, 218 insertions(+) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 1018dcc5..332930bb 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -1354,4 +1354,187 @@ namespace MP4{ return r.str(); } + AFRA::AFRA(){ + memcpy(data + 4, "afra", 4); + setInt32(0, 9);//entrycount = 0 + setFlags(0); + } + + void AFRA::setVersion(long newVersion){ + setInt8(newVersion, 0); + } + + long AFRA::getVersion(){ + return getInt8(0); + } + + void AFRA::setFlags(long newFlags){ + setInt24(newFlags, 1); + } + + long 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(long newVal){ + setInt32(newVal, 5); + } + + long AFRA::getTimeScale(){ + return getInt32(5); + } + + long AFRA::getEntryCount(){ + return getInt32(9); + } + + void AFRA::setEntry(afraentry newEntry, long 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(long 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); + } + } + + long AFRA::getGlobalEntryCount(){ + if (!getGlobalEntries()){return 0;} + int entrysize = 12; + if (getLongOffsets()){entrysize = 16;} + return getInt32(13+entrysize*getEntryCount()); + } + + void AFRA::setGlobalEntry(globalafraentry newEntry, long 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(long 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(long indent){ + std::stringstream r; + r << std::string(indent, ' ') << "[afra] Fragment Random Access" << 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; + + long count = getEntryCount(); + r << std::string(indent+1, ' ') << "Entries (" << count << ") " << std::endl; + for (long 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 (long 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(); + } + + }; diff --git a/lib/mp4.h b/lib/mp4.h index 2cb8b243..00cdac1d 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -234,4 +234,39 @@ namespace MP4{ std::string toPrettyString(long indent = 0); }; + struct afraentry { + long long time; + long long offset; + }; + struct globalafraentry { + long long time; + long segment; + long fragment; + long long afraoffset; + long long offsetfromafra; + }; + class AFRA : public Box { + public: + AFRA(); + void setVersion(long newVersion); + long getVersion(); + void setFlags(long newFlags); + long getFlags(); + void setLongIDs(bool newVal); + bool getLongIDs(); + void setLongOffsets(bool newVal); + bool getLongOffsets(); + void setGlobalEntries(bool newVal); + bool getGlobalEntries(); + void setTimeScale(long newVal); + long getTimeScale(); + long getEntryCount(); + void setEntry(afraentry newEntry, long no); + afraentry getEntry(long no); + long getGlobalEntryCount(); + void setGlobalEntry(globalafraentry newEntry, long no); + globalafraentry getGlobalEntry(long no); + std::string toPrettyString(long indent = 0); + }; + }; From 7f9d29c62f098f158f32ae25fa7aabe554838055 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Tue, 25 Sep 2012 14:10:20 +0200 Subject: [PATCH 316/788] Work in progress on payloadOffset --- lib/mp4.cpp | 200 ++++++++++++++++++++++++---------------------------- lib/mp4.h | 7 +- 2 files changed, 98 insertions(+), 109 deletions(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 332930bb..7126adde 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -16,6 +16,7 @@ namespace MP4{ Box::Box(char * datapointer, bool manage){ data = datapointer; managed = manage; + payloadOffset = 8; if (data == 0){ clear(); }else{ @@ -46,7 +47,18 @@ namespace MP4{ bool Box::read(std::string & newData){ if (!managed){return false;} if (newData.size() > 4){ - size_t size = ntohl( ((int*)newData.c_str())[0] ); + payloadOffset = 8; + long long int 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){ void * ret = malloc(size); if (!ret){return false;} @@ -61,14 +73,17 @@ namespace MP4{ } /// Returns the total boxed size of this box, including the header. - size_t Box::boxedSize() { - return ntohl(((int*)data)[0]); + long long int Box::boxedSize() { + if( payloadOffset == 16 ) { + return ((long long int)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. - size_t Box::payloadSize() { - return boxedSize() - 8; + long long int Box::payloadSize() { + return boxedSize() - payloadOffset; } /// Returns a copy of the data pointer. @@ -82,6 +97,7 @@ namespace MP4{ void Box::clear() { if (data && managed){free(data);} managed = true; + payloadOffset = 8; data = (char*)malloc(8); if (data){ data_size = 8; @@ -102,6 +118,7 @@ namespace MP4{ 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; default: return std::string(indent, ' ')+"Unimplemented pretty-printing for box "+std::string(data+4,4)+"\n"; break; } } @@ -110,7 +127,7 @@ namespace MP4{ /// 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 += 8; + index += payloadOffset; if (index >= boxedSize()){ if (!reserve(index, 0, 1)){return;} } @@ -121,7 +138,7 @@ namespace MP4{ /// 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 += 8; + index += payloadOffset; if (index >= boxedSize()){ if (!reserve(index, 0, 1)){return 0;} } @@ -132,7 +149,7 @@ namespace MP4{ /// 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 += 8; + index += payloadOffset; if (index+1 >= boxedSize()){ if (!reserve(index, 0, 2)){return;} } @@ -144,7 +161,7 @@ namespace MP4{ /// 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 += 8; + index += payloadOffset; if (index+1 >= boxedSize()){ if (!reserve(index, 0, 2)){return 0;} } @@ -157,7 +174,7 @@ namespace MP4{ /// Attempts to resize the data pointer if the index is out of range. /// Fails silently if resizing failed. void Box::setInt24( long newData, size_t index ) { - index += 8; + index += payloadOffset; if (index+2 >= boxedSize()){ if (!reserve(index, 0, 3)){return;} } @@ -170,7 +187,7 @@ namespace MP4{ /// Attempts to resize the data pointer if the index is out of range. /// Returns zero if resizing failed. long Box::getInt24( size_t index ) { - index += 8; + index += payloadOffset; if (index+2 >= boxedSize()){ if (!reserve(index, 0, 3)){return 0;} } @@ -186,7 +203,7 @@ namespace MP4{ /// Attempts to resize the data pointer if the index is out of range. /// Fails silently if resizing failed. void Box::setInt32( long newData, size_t index ) { - index += 8; + index += payloadOffset; if (index+3 >= boxedSize()){ if (!reserve(index, 0, 4)){return;} } @@ -198,7 +215,7 @@ namespace MP4{ /// Attempts to resize the data pointer if the index is out of range. /// Returns zero if resizing failed. long Box::getInt32( size_t index ) { - index += 8; + index += payloadOffset; if (index+3 >= boxedSize()){ if (!reserve(index, 0, 4)){return 0;} } @@ -211,10 +228,13 @@ namespace MP4{ /// Attempts to resize the data pointer if the index is out of range. /// Fails silently if resizing failed. void Box::setInt64( Int64 newData, size_t index ) { - index += 8; + index += payloadOffset; if (index+7 >= boxedSize()){ if (!reserve(index, 0, 8)){return;} } +///\todo Fix 64 bit conversion +// *((int*)(data[index])) = htonl((int)(newData>>32)); +// *((int*)(data[index+4])) = htonl((int)newData); data[index] = ( newData & 0xFF00000000000000 ) >> 56; data[index+1] = ( newData & 0x00FF000000000000 ) >> 48; data[index+2] = ( newData & 0x0000FF0000000000 ) >> 40; @@ -229,18 +249,13 @@ namespace MP4{ /// Attempts to resize the data pointer if the index is out of range. /// Returns zero if resizing failed. Int64 Box::getInt64( size_t index ) { - index += 8; + index += payloadOffset; if (index+7 >= boxedSize()){ if (!reserve(index, 0, 8)){return 0;} } - long result = data[index]; - result <<= 8; result += data[index+1]; - result <<= 8; result += data[index+2]; - result <<= 8; result += data[index+3]; - result <<= 8; result += data[index+4]; - result <<= 8; result += data[index+5]; - result <<= 8; result += data[index+6]; - result <<= 8; result += data[index+7]; + Int64 result = ntohl( ((int*)data)[2] ); + result <<= 32; + result += ntohl( ((int*)data)[3] ); return result; } @@ -255,7 +270,7 @@ namespace MP4{ /// 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 += 8; + index += payloadOffset; if (index >= boxedSize()){ if (!reserve(index, 0, 1)){return;} data[index] = 0; @@ -270,7 +285,7 @@ namespace MP4{ /// Will attempt to resize if the string is out of range. /// Returns null if resizing failed. char * Box::getString(size_t index){ - index += 8; + index += payloadOffset; if (index >= boxedSize()){ if (!reserve(index, 0, 1)){return 0;} data[index] = 0; @@ -281,7 +296,7 @@ namespace MP4{ /// 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 += 8; + index += payloadOffset; if (index >= boxedSize()){return 0;} return strlen(data+index); } @@ -301,25 +316,17 @@ namespace MP4{ data = (char*)ret; data_size = boxedSize() + (wanted-current); } - //move data behind backward, if any - if (boxedSize() - (position+current) > 0){ - memmove(data+position+wanted, data+position+current, boxedSize() - (position+current)); - } - //calculate and set new size + } + //move data behind, if any + if (boxedSize() - (position+current) > 0){ + 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; - }else{ - //make smaller - //move data behind forward, if any - if (boxedSize() - (position+current) > 0){ - memmove(data+position+wanted, data+position+current, boxedSize() - (position+current)); - } - //calculate and set new size - int newSize = boxedSize() - (current-wanted); - ((int*)data)[0] = htonl(newSize); - return true; } + return true; } ABST::ABST( ) { @@ -402,8 +409,8 @@ namespace MP4{ } else { if(!reserve(tempLoc, 0, no - getServerEntryCount())){return;}; memset(data+tempLoc, 0, no - getServerEntryCount()); - tempLoc += no - getServerEntryCount(); - setInt8(no, countLoc);//set new serverEntryCount + tempLoc += (no-1) - getServerEntryCount(); + setInt8(no+1, countLoc);//set new serverEntryCount break; } } @@ -438,8 +445,8 @@ namespace MP4{ } else { if(!reserve(tempLoc, 0, no - getQualityEntryCount())){return;}; memset(data+tempLoc, 0, no - getQualityEntryCount()); - tempLoc += no - getQualityEntryCount(); - setInt8(no, countLoc);//set new qualityEntryCount + tempLoc += (no-1) - getQualityEntryCount(); + setInt8(no+1, countLoc);//set new qualityEntryCount break; } } @@ -456,84 +463,63 @@ namespace MP4{ } void ABST::setDrmData( std::string newDrm ) { - long offset = 29 + getStringLen(29)+1 + 1; - for( int i = 0; i< getServerEntryCount(); i++ ) { - offset += getStringLen(offset)+1; - } - offset++; - for( int i = 0; i< getQualityEntryCount(); i++ ) { - offset += getStringLen(offset)+1; - } - setString(newDrm, offset); + long tempLoc = 29 + getStringLen(29)+1 + 1; + for (int i = 0; i< getServerEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} + tempLoc++; + for (int i = 0; i< getQualityEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} + setString(newDrm, tempLoc); } char* ABST::getDrmData() { - long offset = 29 + getStringLen(29)+1 + 1; - for( int i = 0; i< getServerEntryCount(); i++ ) { - offset += getStringLen(offset)+1; - } - offset++; - for( int i = 0; i< getQualityEntryCount(); i++ ) { - offset += getStringLen(offset)+1; - } - return getString(offset); + long tempLoc = 29 + getStringLen(29)+1 + 1; + for (int i = 0; i< getServerEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} + tempLoc++; + for (int i = 0; i< getQualityEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} + return getString(tempLoc); } void ABST::setMetaData( std::string newMetaData ) { - long offset = 29 + getStringLen(29)+1 + 1; - for( int i = 0; i< getServerEntryCount(); i++ ) { - offset += getStringLen(offset)+1; - } - offset++; - for( int i = 0; i< getQualityEntryCount(); i++ ) { - offset += getStringLen(offset)+1; - } - offset+=getStringLen(offset)+1; - setString(newMetaData, offset); + long tempLoc = 29 + getStringLen(29)+1 + 1; + for (int i = 0; i< getServerEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} + tempLoc++; + for (int i = 0; i< getQualityEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} + offset += getStringLen(tempLoc)+1; + setString(newMetaData, tempLoc); } char* ABST::getMetaData() { - long offset = 29 + getStringLen(29)+1 + 1; - for( int i = 0; i< getServerEntryCount(); i++ ) { - offset += getStringLen(offset)+1; - } - offset++; - for( int i = 0; i< getQualityEntryCount(); i++ ) { - offset += getStringLen(offset)+1; - } - offset+=getStringLen(offset)+1; - return getString(offset); + long tempLoc = 29 + getStringLen(29)+1 + 1; + for (int i = 0; i< getServerEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} + tempLoc++; + for (int i = 0; i< getQualityEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} + offset+=getStringLen(tempLoc)+1; + return getString(tempLoc); } long ABST::getSegmentRunTableCount(){ - long offset = 29 + getStringLen(29)+1 + 1; - for( int i = 0; i< getServerEntryCount(); i++ ) { - offset += getStringLen(offset)+1; - } - offset++; - for( int i = 0; i< getQualityEntryCount(); i++ ) { - offset += getStringLen(offset)+1; - } - offset+=getStringLen(offset)+1;//DrmData - offset+=getStringLen(offset)+1;//MetaData - return getInt8(offset); + long tempLoc = 29 + getStringLen(29)+1 + 1; + for (int i = 0; i< getServerEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} + tempLoc++; + for (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, long no ) { - long offset = 29 + getStringLen(29)+1 + 1; - for( int i = 0; i< getServerEntryCount(); i++ ) { - offset += getStringLen(offset)+1; - } - offset++; - for( int i = 0; i< getQualityEntryCount(); i++ ) { - offset += getStringLen(offset)+1; - } - offset+=getStringLen(offset)+1;//DrmData - offset+=getStringLen(offset)+1;//MetaData - int countLoc = offset; - int tempLoc = countLoc + 1;//segmentRuntableCount + long tempLoc = 29 + getStringLen(29)+1 + 1; + for (int i = 0; i< getServerEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} + tempLoc++; + for (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 for (int i = 0; i < no; i++){ if (i < getSegmentRunTableCount()){ + +////Rewritten Tot Hier + tempLoc += Box(data+8+tempLoc,false).boxedSize(); } else { if(!reserve(tempLoc, 0, 8 * (no - getSegmentRunTableCount()))){return;}; diff --git a/lib/mp4.h b/lib/mp4.h index 00cdac1d..dd08c3b9 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -18,8 +18,8 @@ namespace MP4{ std::string getType(); bool isType( char* boxType ); bool read(std::string & newData); - size_t boxedSize(); - size_t payloadSize(); + long long int boxedSize(); + long long int payloadSize(); char * asBox(); void clear(); std::string toPrettyString( int indent = 0 ); @@ -40,12 +40,15 @@ namespace MP4{ 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); //data functions bool reserve(size_t position, size_t current, size_t wanted); //internal variables char * data; ///< Holds the data of this box int data_size; ///< Currently reserved size bool managed; ///< If false, will not attempt to resize/free the data pointer. + int payloadOffset;/// Date: Wed, 26 Sep 2012 14:37:41 +0200 Subject: [PATCH 317/788] Update --- lib/mp4.cpp | 29 +++++++++++++++++++++-------- lib/mp4.h | 1 + 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 7126adde..c25bbfb1 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -90,6 +90,10 @@ namespace MP4{ 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. @@ -115,6 +119,7 @@ namespace MP4{ 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; @@ -253,9 +258,9 @@ namespace MP4{ if (index+7 >= boxedSize()){ if (!reserve(index, 0, 8)){return 0;} } - Int64 result = ntohl( ((int*)data)[2] ); + Int64 result = ntohl( ((int*)(data+index))[0] ); result <<= 32; - result += ntohl( ((int*)data)[3] ); + result += ntohl( ((int*)(data+index))[1] ); return result; } @@ -483,7 +488,7 @@ namespace MP4{ for (int i = 0; i< getServerEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} tempLoc++; for (int i = 0; i< getQualityEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} - offset += getStringLen(tempLoc)+1; + tempLoc += getStringLen(tempLoc)+1; setString(newMetaData, tempLoc); } @@ -492,7 +497,7 @@ namespace MP4{ for (int i = 0; i< getServerEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} tempLoc++; for (int i = 0; i< getQualityEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} - offset+=getStringLen(tempLoc)+1; + tempLoc += getStringLen(tempLoc)+1; return getString(tempLoc); } @@ -691,6 +696,8 @@ namespace MP4{ setVersion( 0 ); setUpdate( 0 ); setTimeScale( 1000 ); + setInt8(0,9); + setInt32(0,10); } void AFRT::setVersion(char newVersion){setInt8(newVersion, 0);} @@ -752,8 +759,8 @@ namespace MP4{ } else { if(!reserve(tempLoc, 0, 17 * (no - getQualityEntryCount()))){return;}; memset(data+tempLoc, 0, 17 * (no - getQualityEntryCount())); - tempLoc += 17 * (no - getQualityEntryCount()); - setInt32(no, countLoc);//set new qualityEntryCount + tempLoc += 17 * ((no-1) - getQualityEntryCount()); + setInt32((no+1), countLoc);//set new qualityEntryCount break; } } @@ -773,12 +780,17 @@ namespace MP4{ int countLoc = tempLoc; tempLoc += 4; for (int i = 0; i < no; i++){ - tempLoc += 17; + if (getInt32(tempLoc+12) == 0 ) { tempLoc++;} + tempLoc += 16; } res.firstFragment = getInt32(tempLoc); res.firstTimestamp = getInt64(tempLoc+4); res.duration = getInt32(tempLoc+12); - res.discontinuity = getInt8(tempLoc+16); + if( res.duration ) { + res.discontinuity = getInt8(tempLoc+16); + } else { + res.discontinuity = 0; + } return res; } @@ -1432,6 +1444,7 @@ namespace MP4{ }else{ ret.offset = getInt32(21+entrysize*no); } + return ret; } long AFRA::getGlobalEntryCount(){ diff --git a/lib/mp4.h b/lib/mp4.h index dd08c3b9..6ee6d2fe 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -21,6 +21,7 @@ namespace MP4{ long long int boxedSize(); long long int payloadSize(); char * asBox(); + char * payload(); void clear(); std::string toPrettyString( int indent = 0 ); protected: From cac5dcc9a16db55b8037347062363160474b7c35 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 28 Sep 2012 15:12:05 +0200 Subject: [PATCH 318/788] Fixes to MP4 library. Now works and includes test program. --- lib/mp4.cpp | 482 ++++++++++++++++++++++----------------------- lib/mp4.h | 16 +- test/abst_test.cpp | 335 +++++++++++++++++++++++++++++++ 3 files changed, 585 insertions(+), 248 deletions(-) create mode 100644 test/abst_test.cpp diff --git a/lib/mp4.cpp b/lib/mp4.cpp index c25bbfb1..53c2819d 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -146,6 +146,7 @@ namespace MP4{ index += payloadOffset; if (index >= boxedSize()){ if (!reserve(index, 0, 1)){return 0;} + setInt8(0, index-payloadOffset); } return data[index]; } @@ -169,6 +170,7 @@ namespace MP4{ 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 ); @@ -195,6 +197,7 @@ namespace MP4{ index += payloadOffset; if (index+2 >= boxedSize()){ if (!reserve(index, 0, 3)){return 0;} + setInt24(0, index-payloadOffset); } long result = data[index]; result <<= 8; @@ -211,6 +214,7 @@ namespace MP4{ index += payloadOffset; if (index+3 >= boxedSize()){ if (!reserve(index, 0, 4)){return;} + setInt32(0, index-payloadOffset); } newData = htonl( newData ); memcpy( data + index, (char*)&newData, 4 ); @@ -236,6 +240,7 @@ namespace MP4{ index += payloadOffset; if (index+7 >= boxedSize()){ if (!reserve(index, 0, 8)){return;} + setInt64(0, index-payloadOffset); } ///\todo Fix 64 bit conversion // *((int*)(data[index])) = htonl((int)(newData>>32)); @@ -306,6 +311,41 @@ namespace MP4{ 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; + 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. @@ -323,7 +363,7 @@ namespace MP4{ } } //move data behind, if any - if (boxedSize() - (position+current) > 0){ + if (boxedSize() > (position+current)){ memmove(data+position+wanted, data+position+current, boxedSize() - (position+current)); } //calculate and set new size @@ -347,6 +387,8 @@ namespace MP4{ 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 ); } @@ -408,61 +450,65 @@ namespace MP4{ void ABST::setServerEntry(std::string & newEntry, long no){ int countLoc = 29 + getStringLen(29)+1; int tempLoc = countLoc+1; - for (int i = 0; i < no; i++){ - if (i < getServerEntryCount()){ - tempLoc += getStringLen(tempLoc)+1; - } else { - if(!reserve(tempLoc, 0, no - getServerEntryCount())){return;}; - memset(data+tempLoc, 0, no - getServerEntryCount()); - tempLoc += (no-1) - getServerEntryCount(); - setInt8(no+1, countLoc);//set new serverEntryCount - break; - } + //attempt to reach the wanted position + 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(long no){ - if (no > getServerEntryCount()){return "";} - int tempLoc = 29+getStringLen(29)+1 + 1;//position of entry count; + if (no+1 > getServerEntryCount()){return "";} + int tempLoc = 29+getStringLen(29)+1 + 1;//position of first entry for (int i = 0; i < no; i++){tempLoc += getStringLen(tempLoc)+1;} return getString(tempLoc); } long ABST::getQualityEntryCount(){ int countLoc = 29 + getStringLen(29)+1 + 1; - for( int i = 0; i< getServerEntryCount(); i++ ) { - countLoc += getStringLen(countLoc)+1; - } + for (int i = 0; i< getServerEntryCount(); i++){countLoc += getStringLen(countLoc)+1;} return getInt8(countLoc); } void ABST::setQualityEntry(std::string & newEntry, long no){ int countLoc = 29 + getStringLen(29)+1 + 1; - for( int i = 0; i< getServerEntryCount(); i++ ) { - countLoc += getStringLen(countLoc)+1; - } + for (int i = 0; i< getServerEntryCount(); i++){countLoc += getStringLen(countLoc)+1;} int tempLoc = countLoc+1; - for (int i = 0; i < no; i++){ - if (i < getQualityEntryCount()){ - tempLoc += getStringLen(tempLoc)+1; - } else { - if(!reserve(tempLoc, 0, no - getQualityEntryCount())){return;}; - memset(data+tempLoc, 0, no - getQualityEntryCount()); - tempLoc += (no-1) - getQualityEntryCount(); - setInt8(no+1, countLoc);//set new qualityEntryCount - break; - } + //attempt to reach the wanted position + 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(long no){ if (no > getQualityEntryCount()){return "";} - int tempLoc = 29+getStringLen(29)+1 + 1;//position of serverentry count; + int tempLoc = 29+getStringLen(29)+1 + 1;//position of serverentries; for (int i = 0; i < getServerEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} - tempLoc += 1; + tempLoc += 1;//first qualityentry for (int i = 0; i < no; i++){tempLoc += getStringLen(tempLoc)+1;} return getString(tempLoc); } @@ -511,7 +557,7 @@ namespace MP4{ return getInt8(tempLoc); } - void ABST::setSegmentRunTable( ASRT newSegment, long no ) { + void ABST::setSegmentRunTable( ASRT & newSegment, long no ) { long tempLoc = 29 + getStringLen(29)+1 + 1; for (int i = 0; i< getServerEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} tempLoc++; @@ -520,25 +566,23 @@ namespace MP4{ tempLoc+=getStringLen(tempLoc)+1;//MetaData int countLoc = tempLoc; tempLoc++;//skip segmentRuntableCount - for (int i = 0; i < no; i++){ - if (i < getSegmentRunTableCount()){ - -////Rewritten Tot Hier - - tempLoc += Box(data+8+tempLoc,false).boxedSize(); - } else { - if(!reserve(tempLoc, 0, 8 * (no - getSegmentRunTableCount()))){return;}; - for( int j = 0; j < (no - getSegmentRunTableCount())*8; j += 8 ) { - setInt32(8,tempLoc+j); - } - tempLoc += (no - getSegmentRunTableCount() ) * 8; - setInt8(no, countLoc);//set new serverEntryCount - break; + //attempt to reach the wanted position + 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; } - Box oldSegment = Box(data+8+tempLoc,false); - if(!reserve(tempLoc,oldSegment.boxedSize(),newSegment.boxedSize())){return;} - memcpy( data+8+tempLoc, newSegment.asBox(), newSegment.boxedSize() ); + //now, tempLoc is at position for string number no, and we have at least an erro box reserved. + setBox(newSegment, tempLoc); } ASRT & ABST::getSegmentRunTable( long no ) { @@ -547,115 +591,83 @@ namespace MP4{ static Box res; return (ASRT&)res; } - long offset = 29 + getStringLen(29)+1 + 1; - for( int i = 0; i< getServerEntryCount(); i++ ) { - offset += getStringLen(offset)+1; - } - offset++; - for( int i = 0; i< getQualityEntryCount(); i++ ) { - offset += getStringLen(offset)+1; - } - offset+=getStringLen(offset)+1;//DrmData - offset+=getStringLen(offset)+1;//MetaData - int countLoc = offset; - int tempLoc = countLoc + 1;//segmentRuntableCount - for (int i = 0; i < no; i++){ - tempLoc += Box(data+8+tempLoc,false).boxedSize(); - } - result = Box(data+8+tempLoc,false); - return (ASRT&)result; + long tempLoc = 29 + getStringLen(29)+1 + 1; + for (int i = 0; i< getServerEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} + tempLoc++; + for (int i = 0; i< getQualityEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} + tempLoc+=getStringLen(tempLoc)+1;//DrmData + tempLoc+=getStringLen(tempLoc)+1;//MetaData + int countLoc = tempLoc; + tempLoc++;//segmentRuntableCount + for (int i = 0; i < no; ++i){tempLoc += getBoxLen(tempLoc);} + return (ASRT&)getBox(tempLoc); } long ABST::getFragmentRunTableCount() { - long offset = 29 + getStringLen(29)+1 + 1; - for( int i = 0; i< getServerEntryCount(); i++ ) { - offset += getStringLen(offset)+1; - } - offset++; - for( int i = 0; i< getQualityEntryCount(); i++ ) { - offset += getStringLen(offset)+1; - } - offset+=getStringLen(offset)+1;//DrmData - offset+=getStringLen(offset)+1;//MetaData - int countLoc = offset; - int tempLoc = countLoc + 1;//segmentRuntableCount - for (int i = 0; i < getSegmentRunTableCount(); i++){ - tempLoc += Box(data+8+tempLoc,false).boxedSize(); - } + long tempLoc = 29 + getStringLen(29)+1 + 1; + for (int i = 0; i< getServerEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} + tempLoc++; + for (int i = 0; i< getQualityEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} + tempLoc+=getStringLen(tempLoc)+1;//DrmData + tempLoc+=getStringLen(tempLoc)+1;//MetaData + for (int i = getInt8(tempLoc++); i != 0; --i){tempLoc += getBoxLen(tempLoc);} return getInt8( tempLoc ); } - void ABST::setFragmentRunTable( AFRT newFragment, long no ) { - long offset = 29 + getStringLen(29)+1 + 1; - for( int i = 0; i< getServerEntryCount(); i++ ) { - offset += getStringLen(offset)+1; - } - offset++; - for( int i = 0; i< getQualityEntryCount(); i++ ) { - offset += getStringLen(offset)+1; - } - offset+=getStringLen(offset)+1;//DrmData - offset+=getStringLen(offset)+1;//MetaData - - int tempLoc = offset + 1;//segmentRuntableCount - for (int i = 0; i < getSegmentRunTableCount(); i++ ) { - tempLoc += Box(data+8+tempLoc,false).boxedSize();//walk through all segments - } + void ABST::setFragmentRunTable( AFRT & newFragment, long no ) { + long tempLoc = 29 + getStringLen(29)+1 + 1; + for (int i = 0; i< getServerEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} + tempLoc++; + for (int i = 0; i< getQualityEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} + tempLoc+=getStringLen(tempLoc)+1;//DrmData + tempLoc+=getStringLen(tempLoc)+1;//MetaData + for (int i = getInt8(tempLoc++); i != 0; --i){tempLoc += getBoxLen(tempLoc);} int countLoc = tempLoc; - tempLoc += 1; - for (int i = 0; i < no; i++){ - if (i < getFragmentRunTableCount()){ - tempLoc += Box(data+8+tempLoc,false).boxedSize(); - } else { - if(!reserve(tempLoc, 0, 8 * (no - getFragmentRunTableCount()))){return;}; - for( int j = 0; j < (no - getFragmentRunTableCount())*8; j += 8 ) { - setInt32(8,tempLoc+j); - } - tempLoc += (no - getFragmentRunTableCount() ) * 8; - setInt8(no, countLoc);//set new serverEntryCount - break; + tempLoc++; + //attempt to reach the wanted position + 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; } - Box oldFragment = Box(data+8+tempLoc,false); - if(!reserve(tempLoc,oldFragment.boxedSize(),newFragment.boxedSize())){return;} - memcpy( data+8+tempLoc, newFragment.asBox(), newFragment.boxedSize() ); + //now, tempLoc is at position for string number no, and we have at least 1 byte reserved. + setBox(newFragment, tempLoc); } AFRT & ABST::getFragmentRunTable( long no ) { static Box result; - if( no > getFragmentRunTableCount() ) { + if (no >= getFragmentRunTableCount()){ static Box res; return (AFRT&)res; } - long offset = 29 + getStringLen(29)+1 + 1; - for( int i = 0; i< getServerEntryCount(); i++ ) { - offset += getStringLen(offset)+1; - } - offset++; - for( int i = 0; i< getQualityEntryCount(); i++ ) { - offset += getStringLen(offset)+1; - } - offset+=getStringLen(offset)+1;//DrmData - offset+=getStringLen(offset)+1;//MetaData - int countLoc = offset; - int tempLoc = countLoc + 1;//segmentRuntableCount - for (int i = 0; i < getSegmentRunTableCount(); i++){ - tempLoc += Box(data+8+tempLoc,false).boxedSize(); - } - tempLoc ++;//segmentRuntableCount - for (int i = 0; i < no; i++){ - tempLoc += Box(data+8+tempLoc,false).boxedSize(); - } - result = Box(data+8+tempLoc,false); - return (AFRT&)result; + long tempLoc = 29 + getStringLen(29)+1 + 1; + for (int i = 0; i< getServerEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} + tempLoc++; + for (int i = 0; i< getQualityEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} + tempLoc+=getStringLen(tempLoc)+1;//DrmData + tempLoc+=getStringLen(tempLoc)+1;//MetaData + for (int i = getInt8(tempLoc++); i != 0; --i){tempLoc += getBoxLen(tempLoc);} + int countLoc = tempLoc; + tempLoc++; + for (int i = 0; i < no; i++){tempLoc += getBoxLen(tempLoc);} + return (AFRT&)getBox(tempLoc); } std::string ABST::toPrettyString( long indent ) { std::stringstream r; - r << std::string(indent, ' ') << "[abst] Bootstrap Info" << std::endl; - r << std::string(indent+1, ' ') << "Version " << getVersion() << std::endl; + 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 " << getProfile() << std::endl; + r << std::string(indent+1, ' ') << "Profile " << (int)getProfile() << std::endl; if( getLive() ) { r << std::string(indent+1, ' ' ) << "Live" << std::endl; }else{ @@ -672,11 +684,11 @@ namespace MP4{ r << std::string(indent+1, ' ') << "MovieIdentifier " << getMovieIdentifier() << std::endl; r << std::string(indent+1, ' ') << "ServerEntryTable (" << getServerEntryCount() << ")" << std::endl; for( int i = 0; i < getServerEntryCount(); i++ ) { - r << std::string(indent+2, ' ') << getServerEntry(i) << std::endl; + r << std::string(indent+2, ' ') << i << ": " << getServerEntry(i) << std::endl; } r << std::string(indent+1, ' ') << "QualityEntryTable (" << getQualityEntryCount() << ")" << std::endl; for( int i = 0; i < getQualityEntryCount(); i++ ) { - r << std::string(indent+2, ' ') << getQualityEntry(i) << std::endl; + 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; @@ -693,11 +705,9 @@ namespace MP4{ AFRT::AFRT(){ memcpy(data + 4, "afrt", 4); - setVersion( 0 ); - setUpdate( 0 ); - setTimeScale( 1000 ); - setInt8(0,9); - setInt32(0,10); + setVersion(0); + setUpdate(0); + setTimeScale(1000); } void AFRT::setVersion(char newVersion){setInt8(newVersion, 0);} @@ -717,22 +727,26 @@ namespace MP4{ void AFRT::setQualityEntry(std::string & newEntry, long no){ int countLoc = 8; int tempLoc = countLoc+1; - for (int i = 0; i < no; i++){ - if (i < getQualityEntryCount()){ - tempLoc += getStringLen(tempLoc)+1; - } else { - if(!reserve(tempLoc, 0, no - getQualityEntryCount())){return;}; - memset(data+tempLoc, 0, no - getQualityEntryCount()); - tempLoc += no - getQualityEntryCount(); - setInt8(no, countLoc);//set new qualityEntryCount - break; - } + //attempt to reach the wanted position + 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(long no){ - if (no > getQualityEntryCount()){return "";} + if (no+1 > getQualityEntryCount()){return "";} int tempLoc = 9;//position of first quality entry for (int i = 0; i < no; i++){tempLoc += getStringLen(tempLoc)+1;} return getString(tempLoc); @@ -740,34 +754,27 @@ namespace MP4{ long AFRT::getFragmentRunCount(){ int tempLoc = 9; - for( int i = 0; i < getQualityEntryCount(); i++ ){ - tempLoc += getStringLen(tempLoc)+1; - } + for (int i = 0; i < getQualityEntryCount(); ++i){tempLoc += getStringLen(tempLoc)+1;} return getInt32(tempLoc); } void AFRT::setFragmentRun( afrt_runtable newRun, long no ) { int tempLoc = 9; - for( int i = 0; i < getQualityEntryCount(); i++ ){ + for (int i = 0; i < getQualityEntryCount(); ++i){ tempLoc += getStringLen(tempLoc)+1; } int countLoc = tempLoc; tempLoc += 4; for (int i = 0; i < no; i++){ - if (i < getFragmentRunCount()){ - tempLoc += 17; - } else { - if(!reserve(tempLoc, 0, 17 * (no - getQualityEntryCount()))){return;}; - memset(data+tempLoc, 0, 17 * (no - getQualityEntryCount())); - tempLoc += 17 * ((no-1) - getQualityEntryCount()); - setInt32((no+1), countLoc);//set new qualityEntryCount - break; - } + if (getInt32(tempLoc+12) == 0){tempLoc += 17;}else{tempLoc += 16;} } setInt32(newRun.firstFragment,tempLoc); setInt64(newRun.firstTimestamp,tempLoc+4); setInt32(newRun.duration,tempLoc+12); - setInt8(newRun.discontinuity,tempLoc+16); + if (newRun.duration == 0){ + setInt8(newRun.discontinuity,tempLoc+16); + } + if (getInt32(countLoc) < no+1){setInt32(no+1, countLoc);} } afrt_runtable AFRT::getFragmentRun( long no ) { @@ -780,8 +787,7 @@ namespace MP4{ int countLoc = tempLoc; tempLoc += 4; for (int i = 0; i < no; i++){ - if (getInt32(tempLoc+12) == 0 ) { tempLoc++;} - tempLoc += 16; + if (getInt32(tempLoc+12) == 0){tempLoc += 17;}else{tempLoc += 16;} } res.firstFragment = getInt32(tempLoc); res.firstTimestamp = getInt64(tempLoc+4); @@ -796,7 +802,8 @@ namespace MP4{ std::string AFRT::toPrettyString(int indent){ std::stringstream r; - r << std::string(indent, ' ') << "[afrt] Fragment Run Table" << std::endl; + 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{ @@ -805,15 +812,16 @@ namespace MP4{ r << std::string(indent+1, ' ') << "Timescale " << getTimeScale() << std::endl; r << std::string(indent+1, ' ') << "QualitySegmentUrlModifiers (" << getQualityEntryCount() << ")" << std::endl; for( int i = 0; i < getQualityEntryCount(); i++ ) { - r << std::string(indent+2, ' ') << getQualityEntry(i) << std::endl; + r << std::string(indent+2, ' ') << i << ": " << getQualityEntry(i) << std::endl; } r << std::string(indent+1, ' ') << "FragmentRunEntryTable (" << getFragmentRunCount() << ")" << std::endl; for( int i = 0; i < getFragmentRunCount(); i ++ ) { afrt_runtable myRun = getFragmentRun(i); - r << std::string(indent+2, ' ') << "First Fragment " << myRun.firstFragment << std::endl; - r << std::string(indent+2, ' ') << "First Timestamp " << myRun.firstTimestamp << std::endl; - r << std::string(indent+2, ' ') << "Duration " << myRun.duration << std::endl; - r << std::string(indent+2, ' ') << "Discontinuity " << myRun.discontinuity << std::endl; + 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(); } @@ -843,17 +851,21 @@ namespace MP4{ void ASRT::setQualityEntry(std::string & newEntry, long no){ int countLoc = 4; int tempLoc = countLoc+1; - for (int i = 0; i < no; i++){ - if (i < getQualityEntryCount()){ - tempLoc += getStringLen(tempLoc)+1; - } else { - if(!reserve(tempLoc, 0, no - getQualityEntryCount())){return;}; - memset(data+tempLoc, 0, no - getQualityEntryCount()); - tempLoc += no - getQualityEntryCount(); - setInt8(no, countLoc);//set new qualityEntryCount - break; - } + //attempt to reach the wanted position + 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); } @@ -874,17 +886,9 @@ namespace MP4{ int tempLoc = 5;//position of qualityentry count; for (int i = 0; i < getQualityEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} int countLoc = tempLoc; - tempLoc += 4; - for (int i = 0; i < no; i++){ - if (i < getSegmentRunEntryCount()){ - tempLoc += 8; - } else { - if(!reserve(tempLoc, 0, (no - getSegmentRunEntryCount())*8)){return;}; - memset(data+tempLoc, 0, (no - getSegmentRunEntryCount())*8); - tempLoc += (no - getSegmentRunEntryCount())*8; - setInt32(no, countLoc);//set new qualityEntryCount - break; - } + tempLoc += 4 + no*8; + if (no+1 < getSegmentRunEntryCount()){ + setInt32(no+1, countLoc);//set new qualityEntryCount } setInt32(firstSegment,tempLoc); setInt32(fragmentsPerSegment,tempLoc+4); @@ -892,12 +896,11 @@ namespace MP4{ asrt_runtable ASRT::getSegmentRun( long no ) { asrt_runtable res; - if( no > getSegmentRunEntryCount() ) { return res; } + if (no >= getSegmentRunEntryCount()){return res;} int tempLoc = 5;//position of qualityentry count; - for (int i = 0; i < getSegmentRunEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} + for (int i = 0; i < getQualityEntryCount(); ++i){tempLoc += getStringLen(tempLoc)+1;} int countLoc = tempLoc; - tempLoc += 4; - for (int i = 0; i < no; i++){tempLoc += 8;} + tempLoc += 4 + 8*no; res.firstSegment = getInt32(tempLoc); res.fragmentsPerSegment = getInt32(tempLoc+4); return res; @@ -905,7 +908,7 @@ namespace MP4{ std::string ASRT::toPrettyString(int indent){ std::stringstream r; - r << std::string(indent, ' ') << "[asrt] Segment Run Table" << std::endl; + 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; @@ -914,12 +917,11 @@ namespace MP4{ } r << std::string(indent+1, ' ') << "QualityEntryTable (" << getQualityEntryCount() << ")" << std::endl; for( int i = 0; i < getQualityEntryCount(); i++ ) { - r << std::string(indent+2, ' ') << getQualityEntry(i) << std::endl; + r << std::string(indent+2, ' ') << i << ": " << getQualityEntry(i) << std::endl; } r << std::string(indent+1, ' ') << "SegmentRunEntryTable (" << getSegmentRunEntryCount()<< ")" << std::endl; for( int i = 0; i < getSegmentRunEntryCount(); i ++ ) { - r << std::string(indent+2, ' ') << "FirstSegment " << getSegmentRun(i).firstSegment << std::endl; - r << std::string(indent+2, ' ') << "FragmentsPerSegment " << getSegmentRun(i).fragmentsPerSegment << std::endl; + r << std::string(indent+2, ' ') << i << ": First=" << getSegmentRun(i).firstSegment << ", FragmentsPerSegment=" << getSegmentRun(i).fragmentsPerSegment << std::endl; } return r.str(); } @@ -935,7 +937,7 @@ namespace MP4{ std::string MFHD::toPrettyString( int indent ) { std::stringstream r; - r << std::string(indent, ' ') << "[mfhd] Movie Fragment Header" << std::endl; + r << std::string(indent, ' ') << "[mfhd] Movie Fragment Header (" << boxedSize() << ")" << std::endl; r << std::string(indent+1, ' ') << "SequenceNumber " << getSequenceNumber() << std::endl; return r.str(); } @@ -947,19 +949,19 @@ namespace MP4{ long MOOF::getContentCount() { int res = 0; int tempLoc = 0; - while( tempLoc < boxedSize()-8 ){ - res ++; - tempLoc += Box(data+8+tempLoc, false).boxedSize(); + while (tempLoc < boxedSize()-8){ + res++; + tempLoc += getBoxLen(tempLoc); } return res; } - void MOOF::setContent( Box newContent, long no ) { + void MOOF::setContent(Box & newContent, long no){ int tempLoc = 0; int contentCount = getContentCount(); for (int i = 0; i < no; i++){ if (i < contentCount){ - tempLoc += Box(data+8+tempLoc,false).boxedSize(); + tempLoc += getBoxLen(tempLoc); } else { if(!reserve(tempLoc, 0, (no - contentCount)*8)){return;}; memset(data+tempLoc, 0, (no - contentCount)*8); @@ -967,32 +969,31 @@ namespace MP4{ break; } } - Box oldContent = Box( data+8+tempLoc, false ); - if( !reserve( tempLoc, oldContent.boxedSize(), newContent.boxedSize() ) ) { return; } - memcpy( data+8+tempLoc, newContent.asBox(), newContent.boxedSize() ); + setBox(newContent, tempLoc); } - Box MOOF::getContent( long no ){ - if( no > getContentCount() ) { return Box(); } + Box & MOOF::getContent(long no){ + static Box ret = Box((char*)"\000\000\000\010erro", false); + if (no > getContentCount()){return ret;} int i = 0; int tempLoc = 0; - while( i < no ) { - tempLoc += Box( data+8+tempLoc, false).boxedSize(); + while (i < no){ + tempLoc += getBoxLen(tempLoc); i++; } - return Box(data+8+tempLoc, false); + return getBox(tempLoc); } std::string MOOF::toPrettyString( int indent ) { std::stringstream r; - r << std::string(indent, ' ') << "[moof] Movie Fragment Box" << std::endl; + r << std::string(indent, ' ') << "[moof] Movie Fragment 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 += curBox.boxedSize(); + tempLoc += getBoxLen(tempLoc); } return r.str(); } @@ -1004,19 +1005,19 @@ namespace MP4{ long TRAF::getContentCount() { int res = 0; int tempLoc = 0; - while( tempLoc < boxedSize()-8 ){ - res ++; - tempLoc += Box(data+8+tempLoc, false).boxedSize(); + while (tempLoc < boxedSize()-8){ + res++; + tempLoc += getBoxLen(tempLoc); } return res; } - void TRAF::setContent( Box newContent, long no ) { + void TRAF::setContent(Box & newContent, long no){ int tempLoc = 0; int contentCount = getContentCount(); for (int i = 0; i < no; i++){ if (i < contentCount){ - tempLoc += Box(data+8+tempLoc,false).boxedSize(); + tempLoc += getBoxLen(tempLoc); } else { if(!reserve(tempLoc, 0, (no - contentCount)*8)){return;}; memset(data+tempLoc, 0, (no - contentCount)*8); @@ -1024,25 +1025,24 @@ namespace MP4{ break; } } - Box oldContent = Box( data+8+tempLoc, false ); - if( !reserve( tempLoc, oldContent.boxedSize(), newContent.boxedSize() ) ) { return; } - memcpy( data+8+tempLoc, newContent.asBox(), newContent.boxedSize() ); + setBox(newContent, tempLoc); } - Box TRAF::getContent( long no ){ - if( no > getContentCount() ) { return Box(); } + Box & TRAF::getContent( long no ){ + static Box ret = Box((char*)"\000\000\000\010erro", false); + if (no > getContentCount()){return ret;} int i = 0; int tempLoc = 0; - while( i < no ) { - tempLoc += Box( data+8+tempLoc, false).boxedSize(); + while (i < no){ + tempLoc += getBoxLen(tempLoc); i++; } - return Box(data+8+tempLoc, false); + return getBox(tempLoc); } std::string TRAF::toPrettyString( int indent ) { std::stringstream r; - r << std::string(indent, ' ') << "[traf] Track Fragment Box" << std::endl; + r << std::string(indent, ' ') << "[traf] Track Fragment Box (" << boxedSize() << ")" << std::endl; Box curBox; int tempLoc = 0; int contentCount = getContentCount(); @@ -1175,8 +1175,8 @@ namespace MP4{ std::string TRUN::toPrettyString(long indent){ std::stringstream r; - r << std::string(indent, ' ') << "[trun] Track Fragment Run" << std::endl; - r << std::string(indent+1, ' ') << "Version " << getInt8(0) << std::endl; + r << std::string(indent, ' ') << "[trun] Track Fragment Run (" << boxedSize() << ")" << std::endl; + r << std::string(indent+1, ' ') << "Version " << (int)getInt8(0) << std::endl; long flags = getFlags(); r << std::string(indent+1, ' ') << "Flags"; @@ -1328,8 +1328,8 @@ namespace MP4{ std::string TFHD::toPrettyString(long indent){ std::stringstream r; - r << std::string(indent, ' ') << "[tfhd] Track Fragment Header" << std::endl; - r << std::string(indent+1, ' ') << "Version " << getInt8(0) << std::endl; + r << std::string(indent, ' ') << "[tfhd] Track Fragment Header (" << boxedSize() << ")" << std::endl; + r << std::string(indent+1, ' ') << "Version " << (int)getInt8(0) << std::endl; long flags = getFlags(); r << std::string(indent+1, ' ') << "Flags"; @@ -1508,7 +1508,7 @@ namespace MP4{ std::string AFRA::toPrettyString(long indent){ std::stringstream r; - r << std::string(indent, ' ') << "[afra] Fragment Random Access" << std::endl; + 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; diff --git a/lib/mp4.h b/lib/mp4.h index 6ee6d2fe..7d67c898 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -42,7 +42,9 @@ namespace MP4{ char * getString(size_t index); size_t getStringLen(size_t index); //box functions - Box getBox(size_t index); + 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 @@ -135,10 +137,10 @@ namespace MP4{ void setMetaData(std::string newMetaData); char * getMetaData(); long getSegmentRunTableCount(); - void setSegmentRunTable(ASRT table, long no); + void setSegmentRunTable(ASRT & table, long no); ASRT & getSegmentRunTable(long no); long getFragmentRunTableCount(); - void setFragmentRunTable(AFRT table, long no); + void setFragmentRunTable(AFRT & table, long no); AFRT & getFragmentRunTable(long no); std::string toPrettyString(long indent = 0); };//ABST Box @@ -155,8 +157,8 @@ namespace MP4{ public: MOOF(); long getContentCount(); - void setContent( Box newContent, long no ); - Box getContent( long no ); + void setContent(Box & newContent, long no); + Box & getContent(long no); std::string toPrettyString(int indent = 0); };//MOOF Box @@ -164,8 +166,8 @@ namespace MP4{ public: TRAF(); long getContentCount(); - void setContent( Box newContent, long no ); - Box getContent( long no ); + void setContent(Box & newContent, long no); + Box & getContent(long no); std::string toPrettyString(int indent = 0); };//TRAF Box diff --git a/test/abst_test.cpp b/test/abst_test.cpp new file mode 100644 index 00000000..147c87e6 --- /dev/null +++ b/test/abst_test.cpp @@ -0,0 +1,335 @@ +/// \file abst_test.cpp +/// Tests the bootstrap generation functions by comparing to a known good bootstrap. + +#include +#include +#include +#include +#include +#include + +/// 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) << std::endl; +} From f3b0b36e2b610dd99451210716408207631a1a1f Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sat, 29 Sep 2012 01:41:18 +0200 Subject: [PATCH 319/788] Several minor bugfixes and improvements throughout. --- lib/dtsc.cpp | 129 +++++++++++++++++++++++++++++++++++------------- lib/dtsc.h | 9 +++- lib/flv_tag.cpp | 24 ++++++--- lib/mp4.cpp | 13 +---- lib/socket.cpp | 4 ++ 5 files changed, 125 insertions(+), 54 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 78d0a2c3..ab9dbdc2 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -243,6 +243,12 @@ DTSC::Stream::~Stream(){ DTSC::File::File(std::string filename, bool create){ if (create){ F = fopen(filename.c_str(), "w+b"); + //write an empty header + fseek(F, 0, SEEK_SET); + fwrite(DTSC::Magic_Header, 4, 1, F); + memset(buffer, 0, 4); + fwrite(buffer, 4, 1, F);//write 4 zero-bytes + headerSize = 0; }else{ F = fopen(filename.c_str(), "r+b"); } @@ -251,50 +257,26 @@ DTSC::File::File(std::string filename, bool create){ return; } - //if first 4 bytes not available, assume empty file, write header - if (fread(buffer, 4, 1, F) != 1){ - fseek(F, 0, SEEK_SET); - fwrite(DTSC::Magic_Header, 4, 1, F); - }else{ - if (memcmp(buffer, DTSC::Magic_Header, 4) != 0){ - fprintf(stderr, "Not a DTSC file - aborting: %s\n", filename.c_str()); - fclose(F); - F = 0; - return; - } - } //we now know the first 4 bytes are DTSC::Magic_Header and we have a valid file fseek(F, 4, SEEK_SET); if (fread(buffer, 4, 1, F) != 1){ fseek(F, 4, SEEK_SET); memset(buffer, 0, 4); fwrite(buffer, 4, 1, F);//write 4 zero-bytes - headerSize = 0; }else{ uint32_t * ubuffer = (uint32_t *)buffer; headerSize = ntohl(ubuffer[0]); } + readHeader(0); fseek(F, 8+headerSize, SEEK_SET); currframe = 1; frames[1] = 8+headerSize; msframes[1] = 0; } -/// Returns the header metadata for this file as a std::string. -/// Sets the file pointer to the first packet. -std::string & DTSC::File::getHeader(){ - if (fseek(F, 8, SEEK_SET) != 0){ - strbuffer = ""; - return strbuffer; - } - strbuffer.resize(headerSize); - if (fread((void*)strbuffer.c_str(), headerSize, 1, F) != 1){ - strbuffer = ""; - return strbuffer; - } - fseek(F, 8+headerSize, SEEK_SET); - currframe = 1; - return strbuffer; +/// Returns the header metadata for this file as JSON::Value. +JSON::Value & DTSC::File::getMeta(){ + return metadata; } /// (Re)writes the given string to the header area if the size is the same as the existing header. @@ -311,10 +293,84 @@ bool DTSC::File::writeHeader(std::string & header, bool force){ return (ret == 1); } +/// Adds the given string as a new header to the end of the file. +/// \returns The positon the header was written at, or 0 on failure. +long long int DTSC::File::addHeader(std::string & header){ + fseek(F, 0, SEEK_END); + long long int writePos = ftell(F); + int hSize = htonl(header.size()); + int ret = fwrite(DTSC::Magic_Header, 4, 1, F);//write header + if (ret != 1){return 0;} + ret = fwrite((void*)(&hSize), 4, 1, F);//write size + if (ret != 1){return 0;} + ret = fwrite(header.c_str(), header.size(), 1, F);//write contents + if (ret != 1){return 0;} + return writePos;//return position written at +} + +/// Reads the header at the given file position. +/// If the packet could not be read for any reason, the reason is printed to stderr. +/// Reading the header means the file position is moved to after the header. +void DTSC::File::readHeader(int pos){ + fseek(F, pos, SEEK_SET); + if (fread(buffer, 4, 1, F) != 1){ + if (feof(F)){ + #if DEBUG >= 4 + fprintf(stderr, "End of file reached (H%i)\n", pos); + #endif + }else{ + fprintf(stderr, "Could not read header (H%i)\n", pos); + } + strbuffer = ""; + metadata.null(); + return; + } + if (memcmp(buffer, DTSC::Magic_Header, 4) != 0){ + fprintf(stderr, "Invalid header - %.4s != %.4s (H%i)\n", buffer, DTSC::Magic_Header, pos); + strbuffer = ""; + metadata.null(); + return; + } + if (fread(buffer, 4, 1, F) != 1){ + fprintf(stderr, "Could not read size (H%i)\n", pos); + strbuffer = ""; + metadata.null(); + return; + } + uint32_t * ubuffer = (uint32_t *)buffer; + long packSize = ntohl(ubuffer[0]); + strbuffer.resize(packSize); + if (fread((void*)strbuffer.c_str(), packSize, 1, F) != 1){ + fprintf(stderr, "Could not read packet (H%i)\n", pos); + strbuffer = ""; + metadata.null(); + return; + } + metadata = JSON::fromDTMI(strbuffer); + //if there is another header, read it and replace metadata with that one. + if (metadata.isMember("moreheader") && metadata["moreheader"].asInt() > 0){ + readHeader(metadata["moreheader"].asInt()); + return; + } + if (metadata.isMember("keytime")){ + msframes.clear(); + for (int i = 0; i < metadata["keytime"].size(); ++i){ + msframes[i+1] = metadata["keytime"][i].asInt(); + } + } + if (metadata.isMember("keybpos")){ + frames.clear(); + for (int i = 0; i < metadata["keybpos"].size(); ++i){ + frames[i+1] = metadata["keybpos"][i].asInt(); + } + } +} + /// Reads the packet available at the current file position. /// If the packet could not be read for any reason, the reason is printed to stderr. /// Reading the packet means the file position is increased to the next packet. void DTSC::File::seekNext(){ + lastreadpos = ftell(F); if (fread(buffer, 4, 1, F) != 1){ if (feof(F)){ #if DEBUG >= 4 @@ -350,23 +406,25 @@ void DTSC::File::seekNext(){ } jsonbuffer = JSON::fromDTMI(strbuffer); if (jsonbuffer.isMember("keyframe")){ - long pos = ftell(F) - (packSize + 8); - if (frames[currframe] != pos){ + if (frames[currframe] != lastreadpos){ currframe++; currtime = jsonbuffer["time"].asInt(); #if DEBUG >= 6 - if (frames[currframe] != pos){ - std::cerr << "Found a new frame " << currframe << " @ " << pos << "b/" << currtime << "ms" << std::endl; + if (frames[currframe] != lastreadpos){ + std::cerr << "Found a new frame " << currframe << " @ " << lastreadpos << "b/" << currtime << "ms" << std::endl; }else{ - std::cerr << "Passing frame " << currframe << " @ " << pos << "b/" << currtime << "ms" << std::endl; + std::cerr << "Passing frame " << currframe << " @ " << lastreadpos << "b/" << currtime << "ms" << std::endl; } #endif - frames[currframe] = pos; + frames[currframe] = lastreadpos; msframes[currframe] = currtime; } } } +/// Returns the byte positon of the start of the last packet that was read. +long long int DTSC::File::getLastReadPos(){return lastreadpos;} + /// Returns the internal buffer of the last read packet in raw binary format. std::string & DTSC::File::getPacket(){return strbuffer;} @@ -378,6 +436,9 @@ JSON::Value & DTSC::File::getJSON(){return jsonbuffer;} bool DTSC::File::seek_frame(int frameno){ if (frames.count(frameno) > 0){ if (fseek(F, frames[frameno], SEEK_SET) == 0){ + #if DEBUG >= 4 + std::cerr << "Seek direct from " << currframe << " @ " << frames[currframe] << " to " << frameno << " @ " << frames[frameno] << std::endl; + #endif currframe = frameno; return true; } diff --git a/lib/dtsc.h b/lib/dtsc.h index 668b0e26..1bced5d1 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -69,19 +69,24 @@ namespace DTSC{ public: File(std::string filename, bool create = false); ~File(); - std::string & getHeader(); + JSON::Value & getMeta(); + long long int getLastReadPos(); bool writeHeader(std::string & header, bool force = false); + long long int addHeader(std::string & header); void seekNext(); std::string & getPacket(); JSON::Value & getJSON(); bool seek_frame(int frameno); bool seek_time(int seconds); - private: + private: + void readHeader(int pos); std::string strbuffer; JSON::Value jsonbuffer; + JSON::Value metadata; std::map frames; std::map msframes; long long int currtime; + long long int lastreadpos; int currframe; FILE * F; unsigned long headerSize; diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index 22ee3b92..20ed3d36 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -364,6 +364,10 @@ void FLV::Tag::setLen(){ /// Takes the DTSC Video init data and makes it into FLV. /// Assumes init data is available - so check before calling! bool FLV::Tag::DTSCVideoInit(DTSC::Stream & S){ + //Unknown? Assume H264. + if (S.metadata["video"]["codec"].asString() == "?"){ + S.metadata["video"]["codec"] = "H264"; + } if (S.metadata["video"]["codec"].asString() == "H264"){ len = S.metadata["video"]["init"].asString().length() + 20; } @@ -401,6 +405,10 @@ bool FLV::Tag::DTSCVideoInit(DTSC::Stream & S){ /// Assumes init data is available - so check before calling! bool FLV::Tag::DTSCAudioInit(DTSC::Stream & S){ len = 0; + //Unknown? Assume AAC. + if (S.metadata["audio"]["codec"].asString() == "?"){ + S.metadata["audio"]["codec"] = "AAC"; + } if (S.metadata["audio"]["codec"].asString() == "AAC"){ len = S.metadata["audio"]["init"].asString().length() + 17; } @@ -446,6 +454,15 @@ bool FLV::Tag::DTSCAudioInit(DTSC::Stream & S){ /// Takes the DTSC metadata and makes it into FLV. /// Assumes metadata is available - so check before calling! bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){ + //Unknown? Assume AAC. + if (S.metadata["audio"]["codec"].asString() == "?"){ + S.metadata["audio"]["codec"] = "AAC"; + } + //Unknown? Assume H264. + if (S.metadata["video"]["codec"].asString() == "?"){ + S.metadata["video"]["codec"] = "H264"; + } + AMF::Object amfdata("root", AMF::AMF0_DDV_CONTAINER); amfdata.addContent(AMF::Object("", "onMetaData")); @@ -805,11 +822,6 @@ JSON::Value FLV::Tag::toJSON(JSON::Value & metadata){ if (!metadata["video"].isMember("bps")){metadata["video"]["bps"] = 0;} if (!metadata["video"].isMember("keyms")){metadata["video"]["keyms"] = 0;} if (!metadata["video"].isMember("keyvar")){metadata["video"]["keyvar"] = 0;} - if (!metadata["video"].isMember("keys")){ - while (metadata["video"]["keys"].size() < 100){ - metadata["video"]["keys"].append(JSON::Value((long long int)0)); - } - } } return pack_out;//empty } @@ -829,7 +841,6 @@ JSON::Value FLV::Tag::toJSON(JSON::Value & metadata){ switch (audiodata & 0xF0){ case 0x20: metadata["audio"]["codec"] = "MP3"; break; case 0xA0: metadata["audio"]["codec"] = "AAC"; break; - default: metadata["audio"]["codec"] = "?"; break; } } if (!metadata["audio"].isMember("rate") || metadata["audio"]["rate"].asInt() < 1){ @@ -878,7 +889,6 @@ JSON::Value FLV::Tag::toJSON(JSON::Value & metadata){ case 2: metadata["video"]["codec"] = "H263"; break; case 4: metadata["video"]["codec"] = "VP6"; break; case 7: metadata["video"]["codec"] = "H264"; break; - default: metadata["video"]["codec"] = "?"; break; } } pack_out["datatype"] = "video"; diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 53c2819d..fbca71b3 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -242,17 +242,8 @@ namespace MP4{ if (!reserve(index, 0, 8)){return;} setInt64(0, index-payloadOffset); } -///\todo Fix 64 bit conversion -// *((int*)(data[index])) = htonl((int)(newData>>32)); -// *((int*)(data[index+4])) = htonl((int)newData); - data[index] = ( newData & 0xFF00000000000000 ) >> 56; - data[index+1] = ( newData & 0x00FF000000000000 ) >> 48; - data[index+2] = ( newData & 0x0000FF0000000000 ) >> 40; - data[index+3] = ( newData & 0x000000FF00000000 ) >> 32; - data[index+4] = ( newData & 0x00000000FF000000 ) >> 24; - data[index+5] = ( newData & 0x0000000000FF0000 ) >> 16; - data[index+6] = ( newData & 0x000000000000FF00 ) >> 8; - data[index+7] = ( newData & 0x00000000000000FF ); + ((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. diff --git a/lib/socket.cpp b/lib/socket.cpp index 461904ba..5dc642f7 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -215,6 +215,8 @@ std::string Socket::Connection::getError(){return strerror(errno);} /// \param address String containing the location of the Unix socket to connect to. /// \param nonblock Whether the socket should be nonblocking. False by default. Socket::Connection::Connection(std::string address, bool nonblock){ + pipes[0] = -1; + pipes[1] = -1; sock = socket(PF_UNIX, SOCK_STREAM, 0); if (sock < 0){ #if DEBUG >= 1 @@ -250,6 +252,8 @@ Socket::Connection::Connection(std::string address, bool nonblock){ /// \param port String containing the port to connect to. /// \param nonblock Whether the socket should be nonblocking. Socket::Connection::Connection(std::string host, int port, bool nonblock){ + pipes[0] = -1; + pipes[1] = -1; struct addrinfo *result, *rp, hints; Error = false; Blocking = false; From d7c2d268e1b15cf780c1e086891bd74b17a23829 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sat, 29 Sep 2012 02:20:11 +0200 Subject: [PATCH 320/788] Fixed a few copypasta errors in MP4 lib. --- lib/mp4.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index fbca71b3..3d2d8165 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -214,7 +214,6 @@ namespace MP4{ index += payloadOffset; if (index+3 >= boxedSize()){ if (!reserve(index, 0, 4)){return;} - setInt32(0, index-payloadOffset); } newData = htonl( newData ); memcpy( data + index, (char*)&newData, 4 ); @@ -227,6 +226,7 @@ namespace MP4{ index += payloadOffset; if (index+3 >= boxedSize()){ if (!reserve(index, 0, 4)){return 0;} + setInt32(0, index-payloadOffset); } long result; memcpy( (char*)&result, data + index, 4 ); @@ -240,7 +240,6 @@ namespace MP4{ index += payloadOffset; if (index+7 >= boxedSize()){ if (!reserve(index, 0, 8)){return;} - setInt64(0, index-payloadOffset); } ((int*)(data+index))[0] = htonl((int)(newData>>32)); ((int*)(data+index))[1] = htonl((int)(newData & 0xFFFFFFFF)); @@ -253,6 +252,7 @@ namespace MP4{ index += payloadOffset; if (index+7 >= boxedSize()){ if (!reserve(index, 0, 8)){return 0;} + setInt64(0, index-payloadOffset); } Int64 result = ntohl( ((int*)(data+index))[0] ); result <<= 32; From 7d13a18c471ce956894069e74f843c4de069cdb9 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sat, 29 Sep 2012 03:12:51 +0200 Subject: [PATCH 321/788] Stability fixes for MP4 lib, fixed MP4 unit test. --- lib/mp4.cpp | 11 ++++++++++- test/abst_test.cpp | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 3d2d8165..50862b73 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -342,6 +342,9 @@ namespace MP4{ /// \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){ @@ -350,6 +353,7 @@ namespace MP4{ 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); } } @@ -757,6 +761,11 @@ namespace MP4{ int countLoc = tempLoc; tempLoc += 4; for (int i = 0; i < no; i++){ + if (i+1 > getInt32(countLoc)){ + 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); @@ -878,7 +887,7 @@ namespace MP4{ for (int i = 0; i < getQualityEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} int countLoc = tempLoc; tempLoc += 4 + no*8; - if (no+1 < getSegmentRunEntryCount()){ + if (no+1 > getInt32(countLoc)){ setInt32(no+1, countLoc);//set new qualityEntryCount } setInt32(firstSegment,tempLoc); diff --git a/test/abst_test.cpp b/test/abst_test.cpp index 147c87e6..5a19dce7 100644 --- a/test/abst_test.cpp +++ b/test/abst_test.cpp @@ -331,5 +331,5 @@ int main(int argc, char ** argv) { abst.setFragmentRunTable(afrt, 0); if (abst.boxedSize() != __data_len){return 42;} - return memcmp(abst.asBox(), __data, __data_len) << std::endl; + return memcmp(abst.asBox(), __data, __data_len); } From 255e664e4c68711c4751642b8e39d12f41c92b58 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 30 Sep 2012 15:04:48 +0200 Subject: [PATCH 322/788] Updated library version to 3.0.0 --- lib/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Makefile.am b/lib/Makefile.am index d9741da4..2210040d 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -3,7 +3,7 @@ AM_CPPFLAGS = $(global_CFLAGS) lib_LTLIBRARIES=libmist-1.0.la libmist_1_0_la_SOURCES=amf.h amf.cpp auth.h auth.cpp base64.h base64.cpp config.h config.cpp crypto.h crypto.cpp dtsc.h dtsc.cpp flv_tag.h flv_tag.cpp http_parser.h http_parser.cpp json.h json.cpp procs.h procs.cpp rtmpchunks.h rtmpchunks.cpp socket.h socket.cpp mp4.h mp4.cpp ftp.h ftp.cpp filesystem.h filesystem.cpp stream.h stream.cpp timing.h timing.cpp libmist_1_0_la_LIBADD=-lssl -lcrypto -lrt -libmist_1_0_la_LDFLAGS = -version-info 2:0:0 +libmist_1_0_la_LDFLAGS = -version-info 3:0:0 pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = mist-1.0.pc From 5674f5de8f127f8501a9c302533bfec903028102 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 23 Oct 2012 11:12:08 +0200 Subject: [PATCH 323/788] Implemented Util::getMyPath() --- lib/config.cpp | 15 +++++++++++++++ lib/config.h | 3 +++ lib/stream.cpp | 4 +++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/lib/config.cpp b/lib/config.cpp index 7f653a81..cc9c6928 100644 --- a/lib/config.cpp +++ b/lib/config.cpp @@ -311,6 +311,21 @@ void Util::Config::addConnectorOptions(int port){ addOption("daemonize", JSON::fromString("{\"long\":\"daemon\", \"short\":\"d\", \"value\":[1], \"long_off\":\"nodaemon\", \"short_off\":\"n\", \"help\":\"Whether or not to daemonize the process after starting.\"}")); }//addConnectorOptions +/// Gets directory the current executable is stored in. +std::string Util::getMyPath(){ + char mypath[500]; + int ret = readlink("/proc/self/exe", mypath, 500); + if (ret != -1){mypath[ret] = 0;}else{mypath[0] = 0;} + 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; +} + /// Sets the current process' running user void Util::setUser(std::string username){ if (username != "root"){ diff --git a/lib/config.h b/lib/config.h index 099c9685..7e91a4b6 100644 --- a/lib/config.h +++ b/lib/config.h @@ -31,6 +31,9 @@ namespace Util{ void addConnectorOptions(int port); }; + /// Gets directory the current executable is stored in. + std::string getMyPath(); + /// Will set the active user to the named username. void setUser(std::string user); diff --git a/lib/stream.cpp b/lib/stream.cpp index a091325c..3b681c09 100644 --- a/lib/stream.cpp +++ b/lib/stream.cpp @@ -10,6 +10,7 @@ #include "json.h" #include "stream.h" #include "procs.h" +#include "config.h" #include "socket.h" /// Filters the streamname, removing invalid characters and converting all @@ -34,7 +35,8 @@ Socket::Connection Util::Stream::getLive(std::string streamname){ /// Starts a process for a VoD stream. Socket::Connection Util::Stream::getVod(std::string filename){ std::string name = "MistPlayer " + filename; - const char *argv[] = { "MistPlayer", filename.c_str(), NULL }; + std::string player_bin = Util::getMyPath() + "MistPlayer"; + const char *argv[] = {player_bin.c_str(), filename.c_str(), NULL}; int fdin = -1, fdout = -1, fderr = fileno(stderr); Util::Procs::StartPiped(name, (char **)argv, &fdin, &fdout, &fderr); // if StartPiped fails then fdin and fdout will be unmodified (-1) From ec5dd47be9a12009e920fa66380f163787484853 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 29 Oct 2012 10:12:42 +0100 Subject: [PATCH 324/788] Fixed typo in MP4::sampleflags enum. --- lib/mp4.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mp4.h b/lib/mp4.h index 7d67c898..6a6ffb24 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -193,7 +193,7 @@ namespace MP4{ isRedundant = 0x100000, noRedundant = 0x200000, noKeySample = 0x10000, - iskeySample = 0x0, + isKeySample = 0x0, MUST_BE_PRESENT = 0x1 }; std::string prettySampleFlags(long flag); From 503f63af2b2eae083d962c6f39112fb68ce75ec3 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Mon, 29 Oct 2012 12:14:11 +0100 Subject: [PATCH 325/788] added the avcC box --- lib/mp4.cpp | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++- lib/mp4.h | 25 ++++++++++++ 2 files changed, 136 insertions(+), 1 deletion(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 50862b73..6c8dc234 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -124,6 +124,7 @@ namespace MP4{ 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; default: return std::string(indent, ' ')+"Unimplemented pretty-printing for box "+std::string(data+4,4)+"\n"; break; } } @@ -1535,5 +1536,114 @@ namespace MP4{ return r.str(); } - + AVCC::AVCC() { + memcpy(data + 4, "avcC", 4); + setInt8( 0xFF, 4 );//reserved + 4-bytes NAL length + } + + void AVCC::setVersion( long newVersion ) { + setInt8( newVersion, 0 ); + } + + long AVCC::getVersion( ) { + return getInt8( 0 ); + } + + void AVCC::setProfile( long newProfile ) { + setInt8( newProfile, 1 ); + } + + long AVCC::getProfile( ) { + return getInt8( 1 ); + } + + void AVCC::setCompatibleProfiles( long newCompatibleProfiles ) { + setInt8( newCompatibleProfiles, 2 ); + } + + long AVCC::getCompatibleProfiles( ) { + return getInt8( 2 ); + } + + void AVCC::setLevel( long newLevel ) { + setInt8( newLevel, 3 ); + } + + long AVCC::getLevel( ) { + return getInt8( 3 ); + } + + void AVCC::setSPSNumber( long newSPSNumber ) { + setInt8( newSPSNumber, 5 ); + } + + long AVCC::getSPSNumber( ) { + return getInt8( 5 ); + } + + void AVCC::setSPS( std::string newSPS ) { + setInt16( newSPS.size(), 6 ); + for( int i = 0; i < newSPS.size(); i++ ) { + setInt8( newSPS[i], 8+i ); + }//not null-terminated + } + + long AVCC::getSPSLen( ) { + return getInt16( 6 ); + } + + char* AVCC::getSPS( ) { + return payload() + 8; + } + + void AVCC::setPPSNumber( long newPPSNumber ) { + int offset = 8 + getSPSLen( ); + setInt8( newPPSNumber, offset ); + } + + long AVCC::getPPSNumber( ) { + int offset = 8 + getSPSLen( ); + return getInt8( offset ); + } + + void AVCC::setPPS( std::string newPPS ) { + int offset = 8 + getSPSLen( ) + 1; + setInt16( newPPS.size(), offset ); + for( int i = 0; i < newPPS.size(); i++ ) { + setInt8( newPPS[i], offset+2+i ); + }//not null-terminated + } + + long AVCC::getPPSLen( ) { + int offset = 8 + getSPSLen( ) + 1; + return getInt16( offset ); + } + + char* AVCC::getPPS( ) { + int offset = 8 + getSPSLen( ) + 3; + return payload() + offset; + } + + std::string AVCC::toPrettyString(long indent) { + std::stringstream r; + r << std::string(indent, ' ') << "[avcC] H.264 Init Data (" << boxedSize() << ")" << std::endl; + r << std::string(indent+1, ' ') << "Version: " << getVersion( ) << std::endl; + r << std::string(indent+1, ' ') << "Profile: " << getProfile( ) << std::endl; + r << std::string(indent+1, ' ') << "Compatible Profiles: " << getCompatibleProfiles( ) << std::endl; + r << std::string(indent+1, ' ') << "Level: " << getLevel( ) << std::endl; + r << std::string(indent+1, ' ') << "SPS Number: " << getSPSNumber( ) << std::endl; + r << std::string(indent+2, ' ') << getSPSLen( ) << " of SPS data" << std::endl; + r << std::string(indent+1, ' ') << "PPS Number: " << getPPSNumber( ) << std::endl; + r << std::string(indent+2, ' ') << getPPSLen( ) << " of PPS data" << std::endl; + return r.str(); + } + + std::string AVCC::asAnnexB( ) { + std::stringstream r; + r << (char)0x00 << (char)0x00 << (char)0x00 << (char)0x01; + r << getSPS( ); + r << (char)0x00 << (char)0x00 << (char)0x00 << (char)0x01; + r << getPPS( ); + return r.str(); + } }; diff --git a/lib/mp4.h b/lib/mp4.h index 6a6ffb24..6ff69bca 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -275,4 +275,29 @@ namespace MP4{ std::string toPrettyString(long indent = 0); }; + + class AVCC : public Box { + public: + AVCC(); + void setVersion( long newVersion ); + long getVersion( ); + void setProfile( long newProfile ); + long getProfile( ); + void setCompatibleProfiles( long newCompatibleProfiles ); + long getCompatibleProfiles( ); + void setLevel( long newLevel ); + long getLevel( ); + void setSPSNumber( long newSPSNumber ); + long getSPSNumber( ); + void setSPS( std::string newSPS ); + long getSPSLen( ); + char* getSPS( ); + void setPPSNumber( long newPPSNumber ); + long getPPSNumber( ); + void setPPS( std::string newPPS ); + long getPPSLen( ); + char* getPPS( ); + std::string asAnnexB( ); + std::string toPrettyString(long indent = 0); + }; }; From fbc59ba664a71cea8d8c720a9e946a6cbb84a00a Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Mon, 29 Oct 2012 14:34:16 +0100 Subject: [PATCH 326/788] Added SDTP box and AVCC::setPayload function --- lib/mp4.cpp | 29 +++++++++++++++++++++++++++-- lib/mp4.h | 11 ++++++++++- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 6c8dc234..36d9d986 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -1641,9 +1641,34 @@ namespace MP4{ std::string AVCC::asAnnexB( ) { std::stringstream r; r << (char)0x00 << (char)0x00 << (char)0x00 << (char)0x01; - r << getSPS( ); + r.write( getSPS( ), getSPSLen() ); r << (char)0x00 << (char)0x00 << (char)0x00 << (char)0x01; - r << getPPS( ); + r.write( getPPS( ), getPPSLen() ); return r.str(); } + + void AVCC::setPayload( std::string newPayload ) { + if( ! reserve( 0, payloadSize(), newPayload.size() ) ) { return; } + memcpy( (char*)payload(), (char*)newPayload.c_str(), newPayload.size() ); + } + + SDTP::SDTP() { + memcpy(data + 4, "sdtp", 4); + } + + void SDTP::setVersion( long newVersion ) { + setInt8( newVersion, 0 ); + } + + long SDTP::getVersion( ) { + return getInt8( 0 ); + } + + void SDTP::setValue( long newValue, size_t index ) { + setInt8( newValue, index ); + } + + long SDTP::getValue( size_t index ) { + getInt8( index ); + } }; diff --git a/lib/mp4.h b/lib/mp4.h index 6ff69bca..30fbd30d 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -275,7 +275,6 @@ namespace MP4{ std::string toPrettyString(long indent = 0); }; - class AVCC : public Box { public: AVCC(); @@ -298,6 +297,16 @@ namespace MP4{ long getPPSLen( ); char* getPPS( ); std::string asAnnexB( ); + void setPayload( std::string newPayload ); std::string toPrettyString(long indent = 0); }; + + class SDTP : public Box { + public: + SDTP(); + void setVersion( long newVersion ); + long getVersion( ); + void setValue( long newValue, size_t index ); + long getValue( size_t index ); + }; }; From 2ad970f5dfc961cefb00f18aa9c6a27e7a4e9d8c Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Tue, 30 Oct 2012 15:45:28 +0100 Subject: [PATCH 327/788] Readability fixes --- lib/mp4.h | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/mp4.h b/lib/mp4.h index 30fbd30d..2ac0c2d1 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -178,22 +178,22 @@ namespace MP4{ long sampleOffset; }; enum trunflags { - trundataOffset = 0x000001, - trunfirstSampleFlags = 0x000004, - trunsampleDuration = 0x000100, - trunsampleSize = 0x000200, - trunsampleFlags = 0x000400, - trunsampleOffsets = 0x000800 + trundataOffset = 0x00000001, + trunfirstSampleFlags = 0x00000004, + trunsampleDuration = 0x00000100, + trunsampleSize = 0x00000200, + trunsampleFlags = 0x00000400, + trunsampleOffsets = 0x00000800 }; enum sampleflags { - noIPicture = 0x1000000, - isIPicture = 0x2000000, - noDisposable = 0x400000, - isDisposable = 0x800000, - isRedundant = 0x100000, - noRedundant = 0x200000, - noKeySample = 0x10000, - isKeySample = 0x0, + noIPicture = 0x01000000, + isIPicture = 0x02000000, + noDisposable = 0x00400000, + isDisposable = 0x00800000, + isRedundant = 0x00100000, + noRedundant = 0x00200000, + noKeySample = 0x00010000, + isKeySample = 0x00000000, MUST_BE_PRESENT = 0x1 }; std::string prettySampleFlags(long flag); From 3ae5ac5be962983e5e9d17de6b242033fbf669be Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 13 Nov 2012 23:20:17 +0100 Subject: [PATCH 328/788] Fixed a typo and minor speedup in dtsc lib, fixed a bug when sending or receiving empty strings in socket lib. --- lib/dtsc.cpp | 4 ++-- lib/socket.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index ab9dbdc2..7ee542e2 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -145,7 +145,7 @@ std::string & DTSC::Stream::lastData(){ return *datapointer; } -/// Returns the packed in this buffer number. +/// Returns the packet in this buffer number. /// \arg num Buffer number. JSON::Value & DTSC::Stream::getPacket(unsigned int num){ return buffers[num]; @@ -169,7 +169,7 @@ bool DTSC::Stream::hasAudio(){ /// Returns a packed DTSC packet, ready to sent over the network. std::string & DTSC::Stream::outPacket(unsigned int num){ static std::string emptystring; - if (num >= buffers.size()) return emptystring; + if (num >= buffers.size() || !buffers[num].isObject()) return emptystring; return buffers[num].toNetPacked(); } diff --git a/lib/socket.cpp b/lib/socket.cpp index 5dc642f7..b2d70084 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -442,7 +442,7 @@ void Socket::Connection::Send(std::string & data){ /// \param len Amount of bytes to write. /// \returns The amount of bytes actually written. int Socket::Connection::iwrite(const void * buffer, int len){ - if (!connected()){return 0;} + if (!connected() || len < 1){return 0;} int r; if (sock >= 0){ r = send(sock, buffer, len, 0); @@ -479,7 +479,7 @@ int Socket::Connection::iwrite(const void * buffer, int len){ /// \param len Amount of bytes to read. /// \returns The amount of bytes actually read. int Socket::Connection::iread(void * buffer, int len){ - if (!connected()){return 0;} + if (!connected() || len < 1){return 0;} int r; if (sock >= 0){ r = recv(sock, buffer, len, 0); From 21d686fb876ecbfffbcb66071d3afcaeedc6f2cf Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 23 Nov 2012 21:31:50 +0100 Subject: [PATCH 329/788] Bring HTTP lib up to spec (wasn't sending carriage returns properly - now does) --- lib/http_parser.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/http_parser.cpp b/lib/http_parser.cpp index ac0e1f7b..a3144b10 100644 --- a/lib/http_parser.cpp +++ b/lib/http_parser.cpp @@ -28,13 +28,13 @@ std::string & HTTP::Parser::BuildRequest(){ /// \todo Include GET/POST variable parsing? std::map::iterator it; if (protocol.size() < 5 || protocol.substr(0, 4) != "HTTP"){protocol = "HTTP/1.0";} - builder = method+" "+url+" "+protocol+"\n"; + builder = method+" "+url+" "+protocol+"\r\n"; for (it=headers.begin(); it != headers.end(); it++){ if ((*it).first != "" && (*it).second != ""){ - builder += (*it).first + ": " + (*it).second + "\n"; + builder += (*it).first + ": " + (*it).second + "\r\n"; } } - builder += "\n" + body; + builder += "\r\n" + body; return builder; } @@ -48,15 +48,15 @@ std::string & HTTP::Parser::BuildResponse(std::string code, std::string message) /// \todo Include GET/POST variable parsing? std::map::iterator it; if (protocol.size() < 5 || protocol.substr(0, 4) != "HTTP"){protocol = "HTTP/1.0";} - builder = protocol+" "+code+" "+message+"\n"; + 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 + "\n"; + builder += (*it).first + ": " + (*it).second + "\r\n"; } } } - builder += "\n"; + builder += "\r\n"; builder += body; return builder; } From 57004919aaac3462687378d52616844cfdae2656 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 26 Nov 2012 14:42:05 +0100 Subject: [PATCH 330/788] Removed SSL requirement, added GNU_SOURCE in configure.ac --- configure.ac | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 5a8dd7c9..63630ca1 100644 --- a/configure.ac +++ b/configure.ac @@ -15,7 +15,8 @@ AC_PROG_CXX AC_PROG_CC # Checks for libraries. -AC_CHECK_LIB(ssl, RC4) +AC_DEFINE(_GNU_SOURCE) +#AC_CHECK_LIB(ssl, RC4) # Checks for header files. AC_CHECK_HEADERS([arpa/inet.h fcntl.h netdb.h netinet/in.h stdint.h stdlib.h string.h sys/socket.h sys/time.h unistd.h]) From 51a9b4162cf2394e249da0763c3d7d47f4eb28e4 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 7 Dec 2012 17:48:58 +0100 Subject: [PATCH 331/788] Merged RTMP crypto library into rtmpchunks library. Added md5 function to auth library, removed -lcrypto and -lssl from the required linking options of libmist (libmist itself already depends on them, and it is now no longer needed to link to it). --- lib/Makefile.am | 4 +- lib/auth.cpp | 99 +++++---- lib/auth.h | 22 +- lib/crypto.cpp | 509 --------------------------------------------- lib/crypto.h | 31 --- lib/json.cpp | 2 +- lib/mist-1.0.pc.in | 3 +- lib/rtmpchunks.cpp | 191 ++++++++++++++++- 8 files changed, 266 insertions(+), 595 deletions(-) diff --git a/lib/Makefile.am b/lib/Makefile.am index 2210040d..d3c0bfc6 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,7 +1,7 @@ AM_CPPFLAGS = $(global_CFLAGS) lib_LTLIBRARIES=libmist-1.0.la -libmist_1_0_la_SOURCES=amf.h amf.cpp auth.h auth.cpp base64.h base64.cpp config.h config.cpp crypto.h crypto.cpp dtsc.h dtsc.cpp flv_tag.h flv_tag.cpp http_parser.h http_parser.cpp json.h json.cpp procs.h procs.cpp rtmpchunks.h rtmpchunks.cpp socket.h socket.cpp mp4.h mp4.cpp ftp.h ftp.cpp filesystem.h filesystem.cpp stream.h stream.cpp timing.h timing.cpp +libmist_1_0_la_SOURCES=amf.h amf.cpp auth.h auth.cpp base64.h base64.cpp config.h config.cpp dtsc.h dtsc.cpp flv_tag.h flv_tag.cpp http_parser.h http_parser.cpp json.h json.cpp procs.h procs.cpp rtmpchunks.h rtmpchunks.cpp socket.h socket.cpp mp4.h mp4.cpp ftp.h ftp.cpp filesystem.h filesystem.cpp stream.h stream.cpp timing.h timing.cpp libmist_1_0_la_LIBADD=-lssl -lcrypto -lrt libmist_1_0_la_LDFLAGS = -version-info 3:0:0 @@ -9,4 +9,4 @@ pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = mist-1.0.pc library_includedir=$(includedir)/mist-1.0/mist -library_include_HEADERS = amf.h auth.h base64.h config.h crypto.h dtsc.h flv_tag.h http_parser.h json.h procs.h rtmpchunks.h socket.h mp4.h ftp.h filesystem.h stream.h timing.h +library_include_HEADERS = amf.h auth.h base64.h config.h dtsc.h flv_tag.h http_parser.h json.h procs.h rtmpchunks.h socket.h mp4.h ftp.h filesystem.h stream.h timing.h diff --git a/lib/auth.cpp b/lib/auth.cpp index 14be28f2..13c3e1d3 100644 --- a/lib/auth.cpp +++ b/lib/auth.cpp @@ -1,45 +1,66 @@ +#include +#include +#include +#include + #include "auth.h" #include "base64.h" -static unsigned char __gbv2keypub_der[] = { - 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, - 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, - 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xe5, 0xd7, 0x9c, - 0x7d, 0x73, 0xc6, 0xe6, 0xfb, 0x35, 0x7e, 0xd7, 0x57, 0x99, 0x07, 0xdb, - 0x99, 0x70, 0xc9, 0xd0, 0x3e, 0x53, 0x57, 0x3c, 0x1e, 0x55, 0xda, 0x0f, - 0x69, 0xbf, 0x26, 0x79, 0xc7, 0xb6, 0xdd, 0x8e, 0x83, 0x32, 0x65, 0x74, - 0x0d, 0x74, 0x48, 0x42, 0x49, 0x22, 0x52, 0x58, 0x56, 0xc3, 0xe4, 0x49, - 0x5d, 0xac, 0x6a, 0x94, 0xb1, 0x64, 0x14, 0xbf, 0x4d, 0xd5, 0xd7, 0x3a, - 0xca, 0x5c, 0x1e, 0x6f, 0x42, 0x30, 0xac, 0x29, 0xaa, 0xa0, 0x85, 0xd2, - 0x16, 0xa2, 0x8e, 0x89, 0x12, 0xc4, 0x92, 0x06, 0xea, 0xed, 0x48, 0xf6, - 0xdb, 0xed, 0x4f, 0x62, 0x6c, 0xfa, 0xcf, 0xc2, 0xb9, 0x8d, 0x04, 0xb2, - 0xba, 0x63, 0xc9, 0xcc, 0xee, 0x23, 0x64, 0x46, 0x14, 0x12, 0xc8, 0x38, - 0x67, 0x69, 0x6b, 0xaf, 0xd1, 0x7c, 0xb1, 0xb5, 0x79, 0xe4, 0x4e, 0x3a, - 0xa7, 0xe8, 0x28, 0x89, 0x25, 0xc0, 0xd0, 0xd8, 0xc7, 0xd2, 0x26, 0xaa, - 0xf5, 0xbf, 0x36, 0x55, 0x01, 0x89, 0x58, 0x1f, 0x1e, 0xf5, 0xa5, 0x42, - 0x8f, 0x60, 0x2e, 0xc2, 0xd8, 0x21, 0x0b, 0x6c, 0x8d, 0xbb, 0x72, 0xf2, - 0x19, 0x30, 0xe3, 0x4c, 0x3e, 0x80, 0xe7, 0xf2, 0xe3, 0x89, 0x4f, 0xd4, - 0xee, 0x96, 0x3e, 0x4a, 0x9b, 0xe5, 0x16, 0x01, 0xf1, 0x98, 0xc9, 0x0b, - 0xd6, 0xdf, 0x8a, 0x64, 0x47, 0xc4, 0x44, 0xcc, 0x92, 0x69, 0x28, 0xee, - 0x7d, 0xac, 0xdc, 0x30, 0x56, 0x3a, 0xe7, 0xbc, 0xba, 0x45, 0x16, 0x2c, - 0x4c, 0x46, 0x6b, 0x2b, 0x20, 0xfb, 0x3d, 0x20, 0x35, 0xbb, 0x48, 0x49, - 0x13, 0x65, 0xc9, 0x9a, 0x38, 0x10, 0x84, 0x1a, 0x8c, 0xc9, 0xd7, 0xde, - 0x07, 0x10, 0x5a, 0xfb, 0xb4, 0x95, 0xae, 0x18, 0xf2, 0xe3, 0x15, 0xe8, - 0xad, 0x7e, 0xe5, 0x3c, 0xa8, 0x47, 0x85, 0xd6, 0x1f, 0x54, 0xb5, 0xa3, - 0x79, 0x02, 0x03, 0x01, 0x00, 0x01 -}; ///< The GBv2 public key file. -static unsigned int __gbv2keypub_der_len = 294; ///< Length of GBv2 public key data +namespace Secure{ -/// Attempts to load the GBv2 public key. -Auth::Auth(){ - const unsigned char * key = __gbv2keypub_der; - pubkey = d2i_RSAPublicKey(0, &key, __gbv2keypub_der_len); -} + static unsigned char __gbv2keypub_der[] = { + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, + 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xe5, 0xd7, 0x9c, + 0x7d, 0x73, 0xc6, 0xe6, 0xfb, 0x35, 0x7e, 0xd7, 0x57, 0x99, 0x07, 0xdb, + 0x99, 0x70, 0xc9, 0xd0, 0x3e, 0x53, 0x57, 0x3c, 0x1e, 0x55, 0xda, 0x0f, + 0x69, 0xbf, 0x26, 0x79, 0xc7, 0xb6, 0xdd, 0x8e, 0x83, 0x32, 0x65, 0x74, + 0x0d, 0x74, 0x48, 0x42, 0x49, 0x22, 0x52, 0x58, 0x56, 0xc3, 0xe4, 0x49, + 0x5d, 0xac, 0x6a, 0x94, 0xb1, 0x64, 0x14, 0xbf, 0x4d, 0xd5, 0xd7, 0x3a, + 0xca, 0x5c, 0x1e, 0x6f, 0x42, 0x30, 0xac, 0x29, 0xaa, 0xa0, 0x85, 0xd2, + 0x16, 0xa2, 0x8e, 0x89, 0x12, 0xc4, 0x92, 0x06, 0xea, 0xed, 0x48, 0xf6, + 0xdb, 0xed, 0x4f, 0x62, 0x6c, 0xfa, 0xcf, 0xc2, 0xb9, 0x8d, 0x04, 0xb2, + 0xba, 0x63, 0xc9, 0xcc, 0xee, 0x23, 0x64, 0x46, 0x14, 0x12, 0xc8, 0x38, + 0x67, 0x69, 0x6b, 0xaf, 0xd1, 0x7c, 0xb1, 0xb5, 0x79, 0xe4, 0x4e, 0x3a, + 0xa7, 0xe8, 0x28, 0x89, 0x25, 0xc0, 0xd0, 0xd8, 0xc7, 0xd2, 0x26, 0xaa, + 0xf5, 0xbf, 0x36, 0x55, 0x01, 0x89, 0x58, 0x1f, 0x1e, 0xf5, 0xa5, 0x42, + 0x8f, 0x60, 0x2e, 0xc2, 0xd8, 0x21, 0x0b, 0x6c, 0x8d, 0xbb, 0x72, 0xf2, + 0x19, 0x30, 0xe3, 0x4c, 0x3e, 0x80, 0xe7, 0xf2, 0xe3, 0x89, 0x4f, 0xd4, + 0xee, 0x96, 0x3e, 0x4a, 0x9b, 0xe5, 0x16, 0x01, 0xf1, 0x98, 0xc9, 0x0b, + 0xd6, 0xdf, 0x8a, 0x64, 0x47, 0xc4, 0x44, 0xcc, 0x92, 0x69, 0x28, 0xee, + 0x7d, 0xac, 0xdc, 0x30, 0x56, 0x3a, 0xe7, 0xbc, 0xba, 0x45, 0x16, 0x2c, + 0x4c, 0x46, 0x6b, 0x2b, 0x20, 0xfb, 0x3d, 0x20, 0x35, 0xbb, 0x48, 0x49, + 0x13, 0x65, 0xc9, 0x9a, 0x38, 0x10, 0x84, 0x1a, 0x8c, 0xc9, 0xd7, 0xde, + 0x07, 0x10, 0x5a, 0xfb, 0xb4, 0x95, 0xae, 0x18, 0xf2, 0xe3, 0x15, 0xe8, + 0xad, 0x7e, 0xe5, 0x3c, 0xa8, 0x47, 0x85, 0xd6, 0x1f, 0x54, 0xb5, 0xa3, + 0x79, 0x02, 0x03, 0x01, 0x00, 0x01 + }; ///< The GBv2 public key file. + static unsigned int __gbv2keypub_der_len = 294; ///< Length of GBv2 public key data + + /// Attempts to load the GBv2 public key. + Auth::Auth(){ + const unsigned char * key = __gbv2keypub_der; + pubkey = (void*)d2i_RSAPublicKey(0, &key, __gbv2keypub_der_len); + } + + /// Attempts to verify RSA signature using the public key. + /// Assumes basesign argument is base64 encoded RSA signature for data. + /// Returns true if the data could be verified, false otherwise. + bool Auth::PubKey_Check(std::string & data, std::string basesign){ + std::string sign = Base64::decode(basesign); + return (RSA_verify(NID_md5, (unsigned char*)data.c_str(), data.size(), (unsigned char*)sign.c_str(), sign.size(), (RSA*)pubkey) == 1); + } + + /// Wrapper function for openssl MD5 implementation + std::string md5(std::string input){ + char tmp[3]; + std::string ret; + const unsigned char * res = MD5((const unsigned char*)input.c_str(), input.length(), 0); + for (int i = 0; i < 16; ++i){ + snprintf(tmp, 3, "%02x", res[i]); + ret += tmp; + } + return ret; + } -/// Attempts to verify RSA signature using the public key. -/// Assumes basesign argument is base64 encoded RSA signature for data. -/// Returns true if the data could be verified, false otherwise. -bool Auth::PubKey_Check(std::string & data, std::string basesign){ - std::string sign = Base64::decode(basesign); - return (RSA_verify(NID_md5, (unsigned char*)data.c_str(), data.size(), (unsigned char*)sign.c_str(), sign.size(), pubkey) == 1); } diff --git a/lib/auth.h b/lib/auth.h index 40fce7ec..9b299232 100644 --- a/lib/auth.h +++ b/lib/auth.h @@ -1,13 +1,15 @@ #pragma once #include -#include -#include -#include -class Auth{ - private: - RSA * pubkey; ///< Holds the public key. - public: - Auth(); ///< Attempts to load the GBv2 public key. - bool PubKey_Check(std::string & data, std::string basesign); ///< Attempts to verify RSA signature using the public key. -}; +namespace Secure{ + class Auth{ + private: + void * pubkey; ///< Holds the public key. + public: + Auth(); ///< Attempts to load the GBv2 public key. + bool PubKey_Check(std::string & data, std::string basesign); ///< Attempts to verify RSA signature using the public key. + }; + + std::string md5(std::string input); ///< Wrapper function for openssl MD5 implementation + +} diff --git a/lib/crypto.cpp b/lib/crypto.cpp index c523c680..e69de29b 100644 --- a/lib/crypto.cpp +++ b/lib/crypto.cpp @@ -1,509 +0,0 @@ -/// \file crypto.cpp -/// Holds all code needed for RTMP cryptography. - -#define STR(x) (((std::string)(x)).c_str()) - -#include "crypto.h" - -#define P768 \ -"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ -"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ -"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ -"E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF" - -#define P1024 \ -"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ -"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ -"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ -"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ -"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381" \ -"FFFFFFFFFFFFFFFF" - -#define Q1024 \ -"7FFFFFFFFFFFFFFFE487ED5110B4611A62633145C06E0E68" \ -"948127044533E63A0105DF531D89CD9128A5043CC71A026E" \ -"F7CA8CD9E69D218D98158536F92F8A1BA7F09AB6B6A8E122" \ -"F242DABB312F3F637A262174D31BF6B585FFAE5B7A035BF6" \ -"F71C35FDAD44CFD2D74F9208BE258FF324943328F67329C0" \ -"FFFFFFFFFFFFFFFF" - -#define P1536 \ -"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ -"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ -"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ -"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ -"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ -"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ -"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ -"670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF" - -#define P2048 \ -"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ -"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ -"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ -"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ -"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ -"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ -"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ -"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \ -"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \ -"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \ -"15728E5A8AACAA68FFFFFFFFFFFFFFFF" - -#define P3072 \ -"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ -"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ -"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ -"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ -"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ -"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ -"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ -"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \ -"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \ -"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \ -"15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \ -"ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \ -"ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \ -"F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \ -"BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \ -"43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF" - -#define P4096 \ -"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ -"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ -"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ -"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ -"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ -"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ -"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ -"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \ -"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \ -"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \ -"15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \ -"ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \ -"ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \ -"F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \ -"BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \ -"43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" \ -"88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" \ -"2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" \ -"287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" \ -"1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" \ -"93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199" \ -"FFFFFFFFFFFFFFFF" - -#define P6144 \ -"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ -"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ -"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ -"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ -"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ -"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ -"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ -"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \ -"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \ -"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \ -"15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \ -"ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \ -"ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \ -"F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \ -"BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \ -"43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" \ -"88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" \ -"2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" \ -"287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" \ -"1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" \ -"93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" \ -"36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD" \ -"F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831" \ -"179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B" \ -"DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF" \ -"5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6" \ -"D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3" \ -"23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" \ -"CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328" \ -"06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C" \ -"DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE" \ -"12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF" - -#define P8192 \ -"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ -"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ -"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ -"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ -"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ -"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ -"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ -"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \ -"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \ -"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \ -"15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \ -"ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \ -"ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \ -"F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \ -"BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \ -"43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" \ -"88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" \ -"2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" \ -"287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" \ -"1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" \ -"93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" \ -"36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD" \ -"F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831" \ -"179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B" \ -"DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF" \ -"5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6" \ -"D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3" \ -"23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" \ -"CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328" \ -"06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C" \ -"DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE" \ -"12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E4" \ -"38777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300" \ -"741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F568" \ -"3423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9" \ -"22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B" \ -"4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A" \ -"062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A36" \ -"4597E899A0255DC164F31CC50846851DF9AB48195DED7EA1" \ -"B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F92" \ -"4009438B481C6CD7889A002ED5EE382BC9190DA6FC026E47" \ -"9558E4475677E9AA9E3050E2765694DFC81F56E880B96E71" \ -"60C980DD98EDD3DFFFFFFFFFFFFFFFFF" - - -uint8_t genuineFMSKey[] = { - 0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20, - 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, 0x6c, - 0x61, 0x73, 0x68, 0x20, 0x4d, 0x65, 0x64, 0x69, - 0x61, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x20, 0x30, 0x30, 0x31, // Genuine Adobe Flash Media Server 001 - 0xf0, 0xee, 0xc2, 0x4a, 0x80, 0x68, 0xbe, 0xe8, - 0x2e, 0x00, 0xd0, 0xd1, 0x02, 0x9e, 0x7e, 0x57, - 0x6e, 0xec, 0x5d, 0x2d, 0x29, 0x80, 0x6f, 0xab, - 0x93, 0xb8, 0xe6, 0x36, 0xcf, 0xeb, 0x31, 0xae -}; // 68 - -uint8_t genuineFPKey[] = { - 0x47, 0x65, 0x6E, 0x75, 0x69, 0x6E, 0x65, 0x20, - 0x41, 0x64, 0x6F, 0x62, 0x65, 0x20, 0x46, 0x6C, - 0x61, 0x73, 0x68, 0x20, 0x50, 0x6C, 0x61, 0x79, - 0x65, 0x72, 0x20, 0x30, 0x30, 0x31, // Genuine Adobe Flash Player 001 - 0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, - 0x2E, 0x00, 0xD0, 0xD1, 0x02, 0x9E, 0x7E, 0x57, - 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB, - 0x93, 0xB8, 0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE -}; // 62 - - -void replace(std::string &target, std::string search, std::string replacement) { - if (search == replacement) - return; - if (search == "") - return; - std::string::size_type i = std::string::npos; - while ((i = target.find(search)) != std::string::npos) { - target.replace(i, search.length(), replacement); - } -} - - -DHWrapper::DHWrapper(int32_t bitsCount) { - _bitsCount = bitsCount; - _pDH = NULL; - _pSharedKey = NULL; - _sharedKeyLength = 0; - _peerPublickey = NULL; -} - -DHWrapper::~DHWrapper() { - Cleanup(); -} - -bool DHWrapper::Initialize() { - Cleanup(); - - //1. Create the DH - _pDH = DH_new(); - if (_pDH == NULL) { - Cleanup(); - return false; - } - - //2. Create his internal p and g - _pDH->p = BN_new(); - if (_pDH->p == NULL) { - Cleanup(); - return false; - } - _pDH->g = BN_new(); - if (_pDH->g == NULL) { - Cleanup(); - return false; - } - - //3. initialize p, g and key length - if (BN_hex2bn(&_pDH->p, P1024) == 0) { - Cleanup(); - return false; - } - if (BN_set_word(_pDH->g, 2) != 1) { - Cleanup(); - return false; - } - - //4. Set the key length - _pDH->length = _bitsCount; - - //5. Generate private and public key - if (DH_generate_key(_pDH) != 1) { - Cleanup(); - return false; - } - - return true; -} - -bool DHWrapper::CopyPublicKey(uint8_t *pDst, int32_t dstLength) { - if (_pDH == NULL) { - return false; - } - - return CopyKey(_pDH->pub_key, pDst, dstLength); -} - -bool DHWrapper::CopyPrivateKey(uint8_t *pDst, int32_t dstLength) { - if (_pDH == NULL) { - return false; - } - - return CopyKey(_pDH->priv_key, pDst, dstLength); -} - -bool DHWrapper::CreateSharedKey(uint8_t *pPeerPublicKey, int32_t length) { - if (_pDH == NULL) { - return false; - } - - if (_sharedKeyLength != 0 || _pSharedKey != NULL) { - return false; - } - - _sharedKeyLength = DH_size(_pDH); - if (_sharedKeyLength <= 0 || _sharedKeyLength > 1024) { - return false; - } - _pSharedKey = new uint8_t[_sharedKeyLength]; - - _peerPublickey = BN_bin2bn(pPeerPublicKey, length, 0); - if (_peerPublickey == NULL) { - return false; - } - - if (DH_compute_key(_pSharedKey, _peerPublickey, _pDH) != _sharedKeyLength) { - return false; - } - - return true; -} - -bool DHWrapper::CopySharedKey(uint8_t *pDst, int32_t dstLength) { - if (_pDH == NULL) { - return false; - } - - if (dstLength != _sharedKeyLength) { - return false; - } - - memcpy(pDst, _pSharedKey, _sharedKeyLength); - - return true; -} - -void DHWrapper::Cleanup() { - if (_pDH != NULL) { - if (_pDH->p != NULL) { - BN_free(_pDH->p); - _pDH->p = NULL; - } - if (_pDH->g != NULL) { - BN_free(_pDH->g); - _pDH->g = NULL; - } - DH_free(_pDH); - _pDH = NULL; - } - - if (_pSharedKey != NULL) { - delete[] _pSharedKey; - _pSharedKey = NULL; - } - _sharedKeyLength = 0; - - if (_peerPublickey != NULL) { - BN_free(_peerPublickey); - _peerPublickey = NULL; - } -} - -bool DHWrapper::CopyKey(BIGNUM *pNum, uint8_t *pDst, int32_t dstLength) { - int32_t keySize = BN_num_bytes(pNum); - if ((keySize <= 0) || (dstLength <= 0) || (keySize > dstLength)) { - return false; - } - - if (BN_bn2bin(pNum, pDst) != keySize) { - return false; - } - - return true; -} - -void InitRC4Encryption(uint8_t *secretKey, uint8_t *pubKeyIn, uint8_t *pubKeyOut, RC4_KEY *rc4keyIn, RC4_KEY *rc4keyOut) { - uint8_t digest[SHA256_DIGEST_LENGTH]; - unsigned int digestLen = 0; - - HMAC_CTX ctx; - HMAC_CTX_init(&ctx); - HMAC_Init_ex(&ctx, secretKey, 128, EVP_sha256(), 0); - HMAC_Update(&ctx, pubKeyIn, 128); - HMAC_Final(&ctx, digest, &digestLen); - HMAC_CTX_cleanup(&ctx); - - RC4_set_key(rc4keyOut, 16, digest); - - HMAC_CTX_init(&ctx); - HMAC_Init_ex(&ctx, secretKey, 128, EVP_sha256(), 0); - HMAC_Update(&ctx, pubKeyOut, 128); - HMAC_Final(&ctx, digest, &digestLen); - HMAC_CTX_cleanup(&ctx); - - RC4_set_key(rc4keyIn, 16, digest); -} - -std::string md5(std::string source, bool textResult) { - EVP_MD_CTX mdctx; - unsigned char md_value[EVP_MAX_MD_SIZE]; - unsigned int md_len; - - EVP_DigestInit(&mdctx, EVP_md5()); - EVP_DigestUpdate(&mdctx, STR(source), source.length()); - EVP_DigestFinal_ex(&mdctx, md_value, &md_len); - EVP_MD_CTX_cleanup(&mdctx); - - if (textResult) { - std::string result = ""; - char tmp[3]; - for (uint32_t i = 0; i < md_len; i++) { - sprintf(tmp, "%02x", md_value[i]); - result += tmp; - } - return result; - } else { - return std::string((char *) md_value, md_len); - } -} - -std::string b64(std::string source) { - return b64((uint8_t *) STR(source), source.size()); -} - -std::string b64(uint8_t *pBuffer, uint32_t length) { - BIO *bmem; - BIO *b64; - BUF_MEM *bptr; - - b64 = BIO_new(BIO_f_base64()); - bmem = BIO_new(BIO_s_mem()); - - b64 = BIO_push(b64, bmem); - BIO_write(b64, pBuffer, length); - std::string result = ""; - if (BIO_flush(b64) == 1) { - BIO_get_mem_ptr(b64, &bptr); - result = std::string(bptr->data, bptr->length); - } - - BIO_free_all(b64); - - - replace(result, "\n", ""); - replace(result, "\r", ""); - - return result; -} - -std::string unb64(std::string source) { - return unb64((uint8_t *)STR(source),source.length()); -} - -std::string unb64(uint8_t *pBuffer, uint32_t length){ - // create a memory buffer containing base64 encoded data - //BIO* bmem = BIO_new_mem_buf((void*) STR(source), source.length()); - BIO* bmem = BIO_new_mem_buf((void *)pBuffer, length); - - // push a Base64 filter so that reading from buffer decodes it - BIO *bioCmd = BIO_new(BIO_f_base64()); - // we don't want newlines - BIO_set_flags(bioCmd, BIO_FLAGS_BASE64_NO_NL); - bmem = BIO_push(bioCmd, bmem); - - char *pOut = new char[length]; - - int finalLen = BIO_read(bmem, (void*) pOut, length); - BIO_free_all(bmem); - std::string result(pOut, finalLen); - delete[] pOut; - return result; -} - -void HMACsha256(const void *pData, uint32_t dataLength, const void *pKey, uint32_t keyLength, void *pResult) { - unsigned int digestLen; - HMAC_CTX ctx; - HMAC_CTX_init(&ctx); - HMAC_Init_ex(&ctx, (unsigned char*) pKey, keyLength, EVP_sha256(), NULL); - HMAC_Update(&ctx, (unsigned char *) pData, dataLength); - HMAC_Final(&ctx, (unsigned char *) pResult, &digestLen); - HMAC_CTX_cleanup(&ctx); -} - -uint32_t GetDigestOffset0(uint8_t *pBuffer) { - uint32_t offset = pBuffer[8] + pBuffer[9] + pBuffer[10] + pBuffer[11]; - return (offset % 728) + 12; -} -uint32_t GetDigestOffset1(uint8_t *pBuffer) { - uint32_t offset = pBuffer[772] + pBuffer[773] + pBuffer[774] + pBuffer[775]; - return (offset % 728) + 776; -} -uint32_t GetDigestOffset(uint8_t *pBuffer, uint8_t scheme){ - if (scheme == 0){return GetDigestOffset0(pBuffer);}else{return GetDigestOffset1(pBuffer);} -} -uint32_t GetDHOffset0(uint8_t *pBuffer) { - uint32_t offset = pBuffer[1532] + pBuffer[1533] + pBuffer[1534] + pBuffer[1535]; - return (offset % 632) + 772; -} -uint32_t GetDHOffset1(uint8_t *pBuffer) { - uint32_t offset = pBuffer[768] + pBuffer[769] + pBuffer[770] + pBuffer[771]; - return (offset % 632) + 8; -} -uint32_t GetDHOffset(uint8_t *pBuffer, uint8_t scheme){ - if (scheme == 0){return GetDHOffset0(pBuffer);}else{return GetDHOffset1(pBuffer);} -} - - -bool ValidateClientScheme(uint8_t * pBuffer, uint8_t scheme) { - uint32_t clientDigestOffset = GetDigestOffset(pBuffer, scheme); - uint8_t *pTempBuffer = new uint8_t[1536 - 32]; - memcpy(pTempBuffer, pBuffer, clientDigestOffset); - memcpy(pTempBuffer + clientDigestOffset, pBuffer + clientDigestOffset + 32, 1536 - clientDigestOffset - 32); - uint8_t *pTempHash = new uint8_t[512]; - HMACsha256(pTempBuffer, 1536 - 32, genuineFPKey, 30, pTempHash); - bool result = (memcmp(pBuffer+clientDigestOffset, pTempHash, 32) == 0); - #if DEBUG >= 4 - fprintf(stderr, "Client scheme validation %hhi %s\n", scheme, result?"success":"failed"); - #endif - delete[] pTempBuffer; - delete[] pTempHash; - return result; -} diff --git a/lib/crypto.h b/lib/crypto.h index f4daa4bb..2187c9d4 100644 --- a/lib/crypto.h +++ b/lib/crypto.h @@ -13,43 +13,12 @@ #include #include -class DHWrapper { -private: - int32_t _bitsCount; - DH *_pDH; - uint8_t *_pSharedKey; - int32_t _sharedKeyLength; - BIGNUM *_peerPublickey; -public: - DHWrapper(int32_t bitsCount); - virtual ~DHWrapper(); - - bool Initialize(); - bool CopyPublicKey(uint8_t *pDst, int32_t dstLength); - bool CopyPrivateKey(uint8_t *pDst, int32_t dstLength); - bool CreateSharedKey(uint8_t *pPeerPublicKey, int32_t length); - bool CopySharedKey(uint8_t *pDst, int32_t dstLength); -private: - void Cleanup(); - bool CopyKey(BIGNUM *pNum, uint8_t *pDst, int32_t dstLength); -}; void InitRC4Encryption(uint8_t *secretKey, uint8_t *pubKeyIn, uint8_t *pubKeyOut, RC4_KEY *rc4keyIn, RC4_KEY *rc4keyOut); -std::string md5(std::string source, bool textResult); -std::string b64(std::string source); -std::string b64(uint8_t *pBuffer, uint32_t length); -std::string unb64(std::string source); -std::string unb64(uint8_t *pBuffer, uint32_t length); void HMACsha256(const void *pData, uint32_t dataLength, const void *pKey, uint32_t keyLength, void *pResult); -uint32_t GetDigestOffset0(uint8_t *pBuffer); -uint32_t GetDigestOffset1(uint8_t *pBuffer); -uint32_t GetDigestOffset(uint8_t *pBuffer, uint8_t scheme); -uint32_t GetDHOffset0(uint8_t *pBuffer); -uint32_t GetDHOffset1(uint8_t *pBuffer); -uint32_t GetDHOffset(uint8_t *pBuffer, uint8_t scheme); extern uint8_t genuineFMSKey[]; diff --git a/lib/json.cpp b/lib/json.cpp index de0ec66c..c7a0557f 100644 --- a/lib/json.cpp +++ b/lib/json.cpp @@ -407,7 +407,7 @@ std::string & JSON::Value::toNetPacked(){ static std::string emptystring; //check if this is legal if (myType != OBJECT){ - fprintf(stderr, "Fatal error: Only objects may be NetPacked! Aborting.\n"); + fprintf(stderr, "Error: Only objects may be NetPacked!\n"); return emptystring; } //if sneaky storage doesn't contain correct data, re-calculate it diff --git a/lib/mist-1.0.pc.in b/lib/mist-1.0.pc.in index e7036838..dbbd603f 100644 --- a/lib/mist-1.0.pc.in +++ b/lib/mist-1.0.pc.in @@ -5,7 +5,6 @@ includedir=@includedir@ Name: Mist Description: Mist Streaming Media Library -Requires: openssl Version: @PACKAGE_VERSION@ -Libs: -L${libdir} -lmist-1.0 -lssl -lcrypto -lrt +Libs: -L${libdir} -lmist-1.0 Cflags: -I${includedir}/mist-1.0 -I${libdir}/mist-1.0/include diff --git a/lib/rtmpchunks.cpp b/lib/rtmpchunks.cpp index 8a9e356f..1084e004 100644 --- a/lib/rtmpchunks.cpp +++ b/lib/rtmpchunks.cpp @@ -3,7 +3,6 @@ #include "rtmpchunks.h" #include "flv_tag.h" -#include "crypto.h" #include "timing.h" char versionstring[] = "WWW.DDVTECH.COM "; ///< String that is repeated in the RTMP handshake @@ -26,6 +25,196 @@ std::map RTMPStream::Chunk::lastsend; /// Holds the last received chunk for every msg_id. std::map RTMPStream::Chunk::lastrecv; +#include +#include +#include +#include +#include +#include +#include +#include + +#define P1024 \ +"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ +"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ +"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF" + +uint8_t genuineFMSKey[] = { + 0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, 0x6c, 0x61, 0x73, 0x68, 0x20, + 0x4d, 0x65, 0x64, 0x69, 0x61, 0x20, 0x53, 0x65, 0x72, 0x76, // 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 + +uint8_t genuineFPKey[] = { + 0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, 0x6c, 0x61, 0x73, 0x68, 0x20, + 0x50, 0x6c, 0x61, 0x79, // 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; + } +} + +inline uint32_t GetDHOffset(uint8_t *pBuffer, uint8_t scheme){ + if (scheme == 0){ + return ((pBuffer[1532] + pBuffer[1533] + pBuffer[1534] + pBuffer[1535]) % 632) + 772; + }else{ + return ((pBuffer[768] + pBuffer[769] + pBuffer[770] + pBuffer[771]) % 632) + 8; + } +} + +class DHWrapper { + private: + int32_t _bitsCount; + DH *_pDH; + uint8_t *_pSharedKey; + int32_t _sharedKeyLength; + BIGNUM *_peerPublickey; + public: + DHWrapper(int32_t bitsCount); + virtual ~DHWrapper(); + bool Initialize(); + bool CopyPublicKey(uint8_t *pDst, int32_t dstLength); + bool CopyPrivateKey(uint8_t *pDst, int32_t dstLength); + bool CreateSharedKey(uint8_t *pPeerPublicKey, int32_t length); + bool CopySharedKey(uint8_t *pDst, int32_t dstLength); + private: + void Cleanup(); + bool CopyKey(BIGNUM *pNum, uint8_t *pDst, int32_t dstLength); +}; + +DHWrapper::DHWrapper(int32_t bitsCount) { + _bitsCount = bitsCount; + _pDH = 0; + _pSharedKey = 0; + _sharedKeyLength = 0; + _peerPublickey = 0; +} + +DHWrapper::~DHWrapper() { + Cleanup(); +} + +bool DHWrapper::Initialize() { + Cleanup(); + _pDH = DH_new(); + if (!_pDH){Cleanup(); return false;} + _pDH->p = BN_new(); + if (!_pDH->p){Cleanup(); return false;} + _pDH->g = BN_new(); + if (!_pDH->g){Cleanup(); return false;} + if (BN_hex2bn(&_pDH->p, P1024) == 0){Cleanup(); return false;} + if (BN_set_word(_pDH->g, 2) != 1){Cleanup(); return false;} + _pDH->length = _bitsCount; + if (DH_generate_key(_pDH) != 1){Cleanup(); return false;} + return true; +} + +bool DHWrapper::CopyPublicKey(uint8_t *pDst, int32_t dstLength) { + if (!_pDH){return false;} + return CopyKey(_pDH->pub_key, pDst, dstLength); +} + +bool DHWrapper::CopyPrivateKey(uint8_t *pDst, int32_t dstLength) { + if (!_pDH){return false;} + return CopyKey(_pDH->priv_key, pDst, dstLength); +} + +bool DHWrapper::CreateSharedKey(uint8_t *pPeerPublicKey, int32_t length) { + if (!_pDH){return false;} + if (_sharedKeyLength != 0 || _pSharedKey){return false;} + + _sharedKeyLength = DH_size(_pDH); + if (_sharedKeyLength <= 0 || _sharedKeyLength > 1024){return false;} + + _pSharedKey = new uint8_t[_sharedKeyLength]; + _peerPublickey = BN_bin2bn(pPeerPublicKey, length, 0); + if (!_peerPublickey){return false;} + + if (DH_compute_key(_pSharedKey, _peerPublickey, _pDH) != _sharedKeyLength){return false;} + + return true; +} + +bool DHWrapper::CopySharedKey(uint8_t *pDst, int32_t dstLength) { + if (!_pDH){return false;} + if (dstLength != _sharedKeyLength){return false;} + memcpy(pDst, _pSharedKey, _sharedKeyLength); + return true; +} + +void DHWrapper::Cleanup() { + if (_pDH){ + if (_pDH->p){BN_free(_pDH->p); _pDH->p = 0;} + if (_pDH->g){BN_free(_pDH->g); _pDH->g = 0;} + DH_free(_pDH); _pDH = 0; + } + if (_pSharedKey){delete[] _pSharedKey; _pSharedKey = 0;} + _sharedKeyLength = 0; + if (_peerPublickey){BN_free(_peerPublickey); _peerPublickey = 0;} +} + +bool DHWrapper::CopyKey(BIGNUM *pNum, uint8_t *pDst, int32_t dstLength) { + int32_t keySize = BN_num_bytes(pNum); + if ((keySize <= 0) || (dstLength <= 0) || (keySize > dstLength)){return false;} + if (BN_bn2bin(pNum, pDst) != keySize){return false;} + return true; +} + +void InitRC4Encryption(uint8_t *secretKey, uint8_t *pubKeyIn, uint8_t *pubKeyOut, RC4_KEY *rc4keyIn, RC4_KEY *rc4keyOut) { + uint8_t digest[SHA256_DIGEST_LENGTH]; + unsigned int digestLen = 0; + + HMAC_CTX ctx; + HMAC_CTX_init(&ctx); + HMAC_Init_ex(&ctx, secretKey, 128, EVP_sha256(), 0); + HMAC_Update(&ctx, pubKeyIn, 128); + HMAC_Final(&ctx, digest, &digestLen); + HMAC_CTX_cleanup(&ctx); + + RC4_set_key(rc4keyOut, 16, digest); + + HMAC_CTX_init(&ctx); + HMAC_Init_ex(&ctx, secretKey, 128, EVP_sha256(), 0); + HMAC_Update(&ctx, pubKeyOut, 128); + HMAC_Final(&ctx, digest, &digestLen); + HMAC_CTX_cleanup(&ctx); + + RC4_set_key(rc4keyIn, 16, digest); +} + +void HMACsha256(const void *pData, uint32_t dataLength, const void *pKey, uint32_t keyLength, void *pResult) { + unsigned int digestLen; + HMAC_CTX ctx; + HMAC_CTX_init(&ctx); + HMAC_Init_ex(&ctx, (unsigned char*) pKey, keyLength, EVP_sha256(), 0); + HMAC_Update(&ctx, (unsigned char *) pData, dataLength); + HMAC_Final(&ctx, (unsigned char *) pResult, &digestLen); + HMAC_CTX_cleanup(&ctx); +} + +bool ValidateClientScheme(uint8_t * pBuffer, uint8_t scheme) { + uint32_t clientDigestOffset = GetDigestOffset(pBuffer, scheme); + uint8_t *pTempBuffer = new uint8_t[1536 - 32]; + memcpy(pTempBuffer, pBuffer, clientDigestOffset); + memcpy(pTempBuffer + clientDigestOffset, pBuffer + clientDigestOffset + 32, 1536 - clientDigestOffset - 32); + uint8_t *pTempHash = new uint8_t[512]; + HMACsha256(pTempBuffer, 1536 - 32, genuineFPKey, 30, pTempHash); + bool result = (memcmp(pBuffer+clientDigestOffset, pTempHash, 32) == 0); + #if DEBUG >= 4 + fprintf(stderr, "Client scheme validation %hhi %s\n", scheme, result?"success":"failed"); + #endif + delete[] pTempBuffer; + delete[] pTempHash; + return result; +} + /// 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. From 38ef8704f8cd5c00f712361f4f732354c08c24c8 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 11 Dec 2012 11:03:33 +0100 Subject: [PATCH 332/788] Global cleanups and standardization of code style. --- lib/amf.cpp | 1027 ++++++++++++---------- lib/amf.h | 25 +- lib/auth.cpp | 42 +- lib/auth.h | 2 +- lib/base64.cpp | 62 +- lib/config.cpp | 141 ++-- lib/config.h | 10 +- lib/crypto.cpp | 0 lib/crypto.h | 25 - lib/dtsc.cpp | 200 +++-- lib/dtsc.h | 18 +- lib/filesystem.cpp | 308 ++++--- lib/filesystem.h | 76 +- lib/flv_tag.cpp | 710 +++++++++++----- lib/flv_tag.h | 14 +- lib/ftp.cpp | 488 +++++++---- lib/ftp.h | 59 +- lib/http_parser.cpp | 121 ++- lib/http_parser.h | 8 +- lib/json.cpp | 315 ++++--- lib/json.h | 21 +- lib/mp4.cpp | 1962 ++++++++++++++++++++++++++----------------- lib/mp4.h | 289 ++++--- lib/procs.cpp | 302 ++++--- lib/procs.h | 4 +- lib/rtmpchunks.cpp | 538 +++++++----- lib/rtmpchunks.h | 11 +- lib/socket.cpp | 295 ++++--- lib/socket.h | 21 +- lib/stream.cpp | 29 +- lib/stream.h | 2 +- lib/timing.cpp | 17 +- lib/timing.h | 4 +- 33 files changed, 4322 insertions(+), 2824 deletions(-) delete mode 100644 lib/crypto.cpp delete mode 100644 lib/crypto.h diff --git a/lib/amf.cpp b/lib/amf.cpp index 1bd81168..dcd163e5 100644 --- a/lib/amf.cpp +++ b/lib/amf.cpp @@ -4,80 +4,101 @@ #include "amf.h" #include #include //needed for stderr only - /// Returns the std::string Indice for the current object, if available. /// Returns an empty string if no indice exists. -std::string AMF::Object::Indice(){return myIndice;}; +std::string AMF::Object::Indice(){ + return myIndice; +} /// Returns the AMF::obj0type AMF0 object type for this object. -AMF::obj0type AMF::Object::GetType(){return myType;}; +AMF::obj0type AMF::Object::GetType(){ + return myType; +} /// Returns the numeric value of this object, if available. /// If this object holds no numeric value, 0 is returned. -double AMF::Object::NumValue(){return numval;}; +double AMF::Object::NumValue(){ + return numval; +} /// Returns the std::string value of this object, if available. /// If this object holds no string value, an empty string is returned. -std::string AMF::Object::StrValue(){return strval;}; +std::string AMF::Object::StrValue(){ + return strval; +} /// Returns the C-string value of this object, if available. /// If this object holds no string value, an empty C-string is returned. -const char * AMF::Object::Str(){return strval.c_str();}; +const char * AMF::Object::Str(){ + return strval.c_str(); +} /// Returns a count of the amount of objects this object currently holds. /// If this object is not a container type, this function will always return 0. -int AMF::Object::hasContent(){return contents.size();}; +int AMF::Object::hasContent(){ + return contents.size(); +} /// Adds an AMF::Object to this object. Works for all types, but only makes sense for container types. -void AMF::Object::addContent(AMF::Object c){contents.push_back(c);}; +void AMF::Object::addContent(AMF::Object c){ + contents.push_back(c); +} /// Returns a pointer to the object held at indice i. /// Returns AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice. /// \param i The indice of the object in this container. -AMF::Object* AMF::Object::getContentP(int i){return &contents.at(i);}; +AMF::Object* AMF::Object::getContentP(int i){ + return &contents.at(i); +} /// Returns a copy of the object held at indice i. /// Returns a AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice. /// \param i The indice of the object in this container. -AMF::Object AMF::Object::getContent(int i){return contents.at(i);}; +AMF::Object AMF::Object::getContent(int i){ + return contents.at(i); +} /// Returns a pointer to the object held at indice s. /// Returns NULL if no object is held at this indice. /// \param s The indice of the object in this container. AMF::Object* AMF::Object::getContentP(std::string s){ for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ - if (it->Indice() == s){return &(*it);} + if (it->Indice() == s){ + return &( *it); + } } return 0; -}; +} /// Returns a copy of the object held at indice s. /// Returns a AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice. /// \param s The indice of the object in this container. AMF::Object AMF::Object::getContent(std::string s){ for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ - if (it->Indice() == s){return *it;} + if (it->Indice() == s){ + return *it; + } } return AMF::Object("error", AMF0_DDV_CONTAINER); -}; +} /// Default constructor. /// Simply fills the data with AMF::Object("error", AMF0_DDV_CONTAINER) AMF::Object::Object(){ *this = AMF::Object("error", AMF0_DDV_CONTAINER); -};//default constructor +} //default constructor /// Constructor for numeric objects. /// The object type is by default AMF::AMF0_NUMBER, but this can be forced to a different value. /// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. /// \param val The numeric value of this object. Numeric AMF0 objects only support double-type values. /// \param setType The object type to force this object to. -AMF::Object::Object(std::string indice, double val, AMF::obj0type setType){//num type initializer +AMF::Object::Object(std::string indice, double val, AMF::obj0type setType){ //num type initializer myIndice = indice; myType = setType; strval = ""; numval = val; -}; +} /// Constructor for string objects. /// The object type is by default AMF::AMF0_STRING, but this can be forced to a different value. @@ -85,23 +106,23 @@ AMF::Object::Object(std::string indice, double val, AMF::obj0type setType){//num /// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. /// \param val The string value of this object. /// \param setType The object type to force this object to. -AMF::Object::Object(std::string indice, std::string val, AMF::obj0type setType){//str type initializer +AMF::Object::Object(std::string indice, std::string val, AMF::obj0type setType){ //str type initializer myIndice = indice; myType = setType; strval = val; numval = 0; -}; +} /// Constructor for container objects. /// The object type is by default AMF::AMF0_OBJECT, but this can be forced to a different value. /// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. /// \param setType The object type to force this object to. -AMF::Object::Object(std::string indice, AMF::obj0type setType){//object type initializer +AMF::Object::Object(std::string indice, AMF::obj0type setType){ //object type initializer myIndice = indice; myType = setType; strval = ""; numval = 0; -}; +} /// Prints the contents of this object to std::cerr. /// If this object contains other objects, it will call itself recursively @@ -111,41 +132,90 @@ std::string AMF::Object::Print(std::string indent){ st << indent; // print my type switch (myType){ - case AMF::AMF0_NUMBER: st << "Number"; break; - case AMF::AMF0_BOOL: st << "Bool"; break; - case AMF::AMF0_STRING://short string - case AMF::AMF0_LONGSTRING: st << "String"; break; - case AMF::AMF0_OBJECT: st << "Object"; break; - case AMF::AMF0_MOVIECLIP: st << "MovieClip"; break; - case AMF::AMF0_NULL: st << "Null"; break; - case AMF::AMF0_UNDEFINED: st << "Undefined"; break; - case AMF::AMF0_REFERENCE: st << "Reference"; break; - case AMF::AMF0_ECMA_ARRAY: st << "ECMA Array"; break; - case AMF::AMF0_OBJ_END: st << "Object end"; break; - case AMF::AMF0_STRICT_ARRAY: st << "Strict Array"; break; - case AMF::AMF0_DATE: st << "Date"; break; - case AMF::AMF0_UNSUPPORTED: st << "Unsupported"; break; - case AMF::AMF0_RECORDSET: st << "Recordset"; break; - case AMF::AMF0_XMLDOC: st << "XML Document"; break; - case AMF::AMF0_TYPED_OBJ: st << "Typed Object"; break; - case AMF::AMF0_UPGRADE: st << "Upgrade to AMF3"; break; - case AMF::AMF0_DDV_CONTAINER: st << "DDVTech Container"; break; + case AMF::AMF0_NUMBER: + st << "Number"; + break; + case AMF::AMF0_BOOL: + st << "Bool"; + break; + case AMF::AMF0_STRING: //short string + case AMF::AMF0_LONGSTRING: + st << "String"; + break; + case AMF::AMF0_OBJECT: + st << "Object"; + break; + case AMF::AMF0_MOVIECLIP: + st << "MovieClip"; + break; + case AMF::AMF0_NULL: + st << "Null"; + break; + case AMF::AMF0_UNDEFINED: + st << "Undefined"; + break; + case AMF::AMF0_REFERENCE: + st << "Reference"; + break; + case AMF::AMF0_ECMA_ARRAY: + st << "ECMA Array"; + break; + case AMF::AMF0_OBJ_END: + st << "Object end"; + break; + case AMF::AMF0_STRICT_ARRAY: + st << "Strict Array"; + break; + case AMF::AMF0_DATE: + st << "Date"; + break; + case AMF::AMF0_UNSUPPORTED: + st << "Unsupported"; + break; + case AMF::AMF0_RECORDSET: + st << "Recordset"; + break; + case AMF::AMF0_XMLDOC: + st << "XML Document"; + break; + case AMF::AMF0_TYPED_OBJ: + st << "Typed Object"; + break; + case AMF::AMF0_UPGRADE: + st << "Upgrade to AMF3"; + break; + case AMF::AMF0_DDV_CONTAINER: + st << "DDVTech Container"; + break; } // print my string indice, if available st << " " << myIndice << " "; // print my numeric or string contents switch (myType){ - case AMF::AMF0_NUMBER: case AMF::AMF0_BOOL: case AMF::AMF0_REFERENCE: case AMF::AMF0_DATE: st << numval; break; - case AMF::AMF0_STRING: case AMF::AMF0_LONGSTRING: case AMF::AMF0_XMLDOC: case AMF::AMF0_TYPED_OBJ: st << strval; break; - default: break;//we don't care about the rest, and don't want a compiler warning... + case AMF::AMF0_NUMBER: + case AMF::AMF0_BOOL: + case AMF::AMF0_REFERENCE: + case AMF::AMF0_DATE: + st << numval; + break; + case AMF::AMF0_STRING: + case AMF::AMF0_LONGSTRING: + case AMF::AMF0_XMLDOC: + case AMF::AMF0_TYPED_OBJ: + st << strval; + break; + default: + break; //we don't care about the rest, and don't want a compiler warning... } st << std::endl; // if I hold other objects, print those too, recursively. if (contents.size() > 0){ - for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){st << it->Print(indent+" ");} + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ + st << it->Print(indent + " "); + } } return st.str(); -};//print +} //print /// Packs the AMF object to a std::string for transfer over the network. /// If the object is a container type, this function will call itself recursively and contain all contents. @@ -153,24 +223,36 @@ std::string AMF::Object::Print(std::string indent){ std::string AMF::Object::Pack(){ std::string r = ""; //check for string/longstring conversion - if ((myType == AMF::AMF0_STRING) && (strval.size() > 0xFFFF)){myType = AMF::AMF0_LONGSTRING;} + if ((myType == AMF::AMF0_STRING) && (strval.size() > 0xFFFF)){ + myType = AMF::AMF0_LONGSTRING; + } //skip output of DDV container types, they do not exist. Only output their contents. - if (myType != AMF::AMF0_DDV_CONTAINER){r += myType;} + if (myType != AMF::AMF0_DDV_CONTAINER){ + r += myType; + } //output the properly formatted AMF0 data stream for this object's contents. switch (myType){ case AMF::AMF0_NUMBER: - r += *(((char*)&numval)+7); r += *(((char*)&numval)+6); - r += *(((char*)&numval)+5); r += *(((char*)&numval)+4); - r += *(((char*)&numval)+3); r += *(((char*)&numval)+2); - r += *(((char*)&numval)+1); r += *(((char*)&numval)); + r += *(((char*) &numval) + 7); + r += *(((char*) &numval) + 6); + r += *(((char*) &numval) + 5); + r += *(((char*) &numval) + 4); + r += *(((char*) &numval) + 3); + r += *(((char*) &numval) + 2); + r += *(((char*) &numval) + 1); + r += *(((char*) &numval)); break; case AMF::AMF0_DATE: - r += *(((char*)&numval)+7); r += *(((char*)&numval)+6); - r += *(((char*)&numval)+5); r += *(((char*)&numval)+4); - r += *(((char*)&numval)+3); r += *(((char*)&numval)+2); - r += *(((char*)&numval)+1); r += *(((char*)&numval)); - r += (char)0;//timezone always 0 - r += (char)0;//timezone always 0 + r += *(((char*) &numval) + 7); + r += *(((char*) &numval) + 6); + r += *(((char*) &numval) + 5); + r += *(((char*) &numval) + 4); + r += *(((char*) &numval) + 3); + r += *(((char*) &numval) + 2); + r += *(((char*) &numval) + 1); + r += *(((char*) &numval)); + r += (char)0; //timezone always 0 + r += (char)0; //timezone always 0 break; case AMF::AMF0_BOOL: r += (char)numval; @@ -181,18 +263,18 @@ std::string AMF::Object::Pack(){ r += strval; break; case AMF::AMF0_LONGSTRING: - case AMF::AMF0_XMLDOC://is always a longstring - r += strval.size() / (256*256*256); - r += strval.size() / (256*256); + case AMF::AMF0_XMLDOC: //is always a longstring + r += strval.size() / (256 * 256 * 256); + r += strval.size() / (256 * 256); r += strval.size() / 256; r += strval.size() % 256; r += strval; break; - case AMF::AMF0_TYPED_OBJ: + case AMF::AMF0_TYPED_OBJ: //is an object, with the classname first r += Indice().size() / 256; r += Indice().size() % 256; r += Indice(); - //is an object, with the classname first + /* no break */ case AMF::AMF0_OBJECT: if (contents.size() > 0){ for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ @@ -202,7 +284,9 @@ std::string AMF::Object::Pack(){ r += it->Pack(); } } - r += (char)0; r += (char)0; r += (char)9; + r += (char)0; + r += (char)0; + r += (char)9; break; case AMF::AMF0_MOVIECLIP: case AMF::AMF0_OBJ_END: @@ -217,11 +301,14 @@ std::string AMF::Object::Pack(){ r += (char)((int)numval / 256); r += (char)((int)numval % 256); break; - case AMF::AMF0_ECMA_ARRAY:{ + case AMF::AMF0_ECMA_ARRAY: { int arrlen = 0; if (contents.size() > 0){ arrlen = contents.size(); - r += arrlen / (256*256*256); r += arrlen / (256*256); r += arrlen / 256; r += arrlen % 256; + r += arrlen / (256 * 256 * 256); + r += arrlen / (256 * 256); + r += arrlen / 256; + r += arrlen % 256; for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ r += it->Indice().size() / 256; r += it->Indice().size() % 256; @@ -229,23 +316,36 @@ std::string AMF::Object::Pack(){ r += it->Pack(); } }else{ - r += (char)0; r += (char)0; r += (char)0; r += (char)0; + r += (char)0; + r += (char)0; + r += (char)0; + r += (char)0; } - r += (char)0; r += (char)0; r += (char)9; - } break; - case AMF::AMF0_STRICT_ARRAY:{ + r += (char)0; + r += (char)0; + r += (char)9; + } + break; + case AMF::AMF0_STRICT_ARRAY: { int arrlen = 0; if (contents.size() > 0){ arrlen = contents.size(); - r += arrlen / (256*256*256); r += arrlen / (256*256); r += arrlen / 256; r += arrlen % 256; + r += arrlen / (256 * 256 * 256); + r += arrlen / (256 * 256); + r += arrlen / 256; + r += arrlen % 256; for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ r += it->Pack(); } }else{ - r += (char)0; r += (char)0; r += (char)0; r += (char)0; + r += (char)0; + r += (char)0; + r += (char)0; + r += (char)0; } - } break; - case AMF::AMF0_DDV_CONTAINER://only send contents + } + break; + case AMF::AMF0_DDV_CONTAINER: //only send contents if (contents.size() > 0){ for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ r += it->Pack(); @@ -254,7 +354,7 @@ std::string AMF::Object::Pack(){ break; } return r; -};//pack +} //pack /// Parses a single AMF0 type - used recursively by the AMF::parse() functions. /// This function updates i every call with the new position in the data. @@ -267,248 +367,280 @@ AMF::Object AMF::parseOne(const unsigned char *& data, unsigned int &len, unsign std::string tmpstr; unsigned int tmpi = 0; unsigned char tmpdbl[8]; - double *d;// hack to work around strict aliasing - #if DEBUG >= 10 + double *d; // hack to work around strict aliasing +#if DEBUG >= 10 fprintf(stderr, "Note: AMF type %hhx found. %i bytes left\n", data[i], len-i); - #endif +#endif switch (data[i]){ case AMF::AMF0_NUMBER: - tmpdbl[7] = data[i+1]; - tmpdbl[6] = data[i+2]; - tmpdbl[5] = data[i+3]; - tmpdbl[4] = data[i+4]; - tmpdbl[3] = data[i+5]; - tmpdbl[2] = data[i+6]; - tmpdbl[1] = data[i+7]; - tmpdbl[0] = data[i+8]; - i+=9;//skip 8(a double)+1 forwards + tmpdbl[7] = data[i + 1]; + tmpdbl[6] = data[i + 2]; + tmpdbl[5] = data[i + 3]; + tmpdbl[4] = data[i + 4]; + tmpdbl[3] = data[i + 5]; + tmpdbl[2] = data[i + 6]; + tmpdbl[1] = data[i + 7]; + tmpdbl[0] = data[i + 8]; + i += 9; //skip 8(a double)+1 forwards d = (double*)tmpdbl; return AMF::Object(name, *d, AMF::AMF0_NUMBER); break; case AMF::AMF0_DATE: - tmpdbl[7] = data[i+1]; - tmpdbl[6] = data[i+2]; - tmpdbl[5] = data[i+3]; - tmpdbl[4] = data[i+4]; - tmpdbl[3] = data[i+5]; - tmpdbl[2] = data[i+6]; - tmpdbl[1] = data[i+7]; - tmpdbl[0] = data[i+8]; - i+=11;//skip 8(a double)+1+timezone(2) forwards + tmpdbl[7] = data[i + 1]; + tmpdbl[6] = data[i + 2]; + tmpdbl[5] = data[i + 3]; + tmpdbl[4] = data[i + 4]; + tmpdbl[3] = data[i + 5]; + tmpdbl[2] = data[i + 6]; + tmpdbl[1] = data[i + 7]; + tmpdbl[0] = data[i + 8]; + i += 11; //skip 8(a double)+1+timezone(2) forwards d = (double*)tmpdbl; return AMF::Object(name, *d, AMF::AMF0_DATE); break; case AMF::AMF0_BOOL: - i+=2;//skip bool+1 forwards - if (data[i-1] == 0){ + i += 2; //skip bool+1 forwards + if (data[i - 1] == 0){ return AMF::Object(name, (double)0, AMF::AMF0_BOOL); }else{ return AMF::Object(name, (double)1, AMF::AMF0_BOOL); } break; case AMF::AMF0_REFERENCE: - tmpi = data[i+1]*256+data[i+2];//get the ref number value as a double - i+=3;//skip ref+1 forwards + tmpi = data[i + 1] * 256 + data[i + 2]; //get the ref number value as a double + i += 3; //skip ref+1 forwards return AMF::Object(name, (double)tmpi, AMF::AMF0_REFERENCE); break; case AMF::AMF0_XMLDOC: - tmpi = data[i+1]*256*256*256+data[i+2]*256*256+data[i+3]*256+data[i+4];//set tmpi to UTF-8-long length - tmpstr.clear();//clean tmpstr, just to be sure - tmpstr.append((const char *)data+i+5, (size_t)tmpi);//add the string data - i += tmpi + 5;//skip length+size+1 forwards + tmpi = data[i + 1] * 256 * 256 * 256 + data[i + 2] * 256 * 256 + data[i + 3] * 256 + data[i + 4]; //set tmpi to UTF-8-long length + tmpstr.clear(); //clean tmpstr, just to be sure + tmpstr.append((const char *)data + i + 5, (size_t)tmpi); //add the string data + i += tmpi + 5; //skip length+size+1 forwards return AMF::Object(name, tmpstr, AMF::AMF0_XMLDOC); break; case AMF::AMF0_LONGSTRING: - tmpi = data[i+1]*256*256*256+data[i+2]*256*256+data[i+3]*256+data[i+4];//set tmpi to UTF-8-long length - tmpstr.clear();//clean tmpstr, just to be sure - tmpstr.append((const char *)data+i+5, (size_t)tmpi);//add the string data - i += tmpi + 5;//skip length+size+1 forwards + tmpi = data[i + 1] * 256 * 256 * 256 + data[i + 2] * 256 * 256 + data[i + 3] * 256 + data[i + 4]; //set tmpi to UTF-8-long length + tmpstr.clear(); //clean tmpstr, just to be sure + tmpstr.append((const char *)data + i + 5, (size_t)tmpi); //add the string data + i += tmpi + 5; //skip length+size+1 forwards return AMF::Object(name, tmpstr, AMF::AMF0_LONGSTRING); break; case AMF::AMF0_STRING: - tmpi = data[i+1]*256+data[i+2];//set tmpi to UTF-8 length - tmpstr.clear();//clean tmpstr, just to be sure - tmpstr.append((const char *)data+i+3, (size_t)tmpi);//add the string data - i += tmpi + 3;//skip length+size+1 forwards + tmpi = data[i + 1] * 256 + data[i + 2]; //set tmpi to UTF-8 length + tmpstr.clear(); //clean tmpstr, just to be sure + tmpstr.append((const char *)data + i + 3, (size_t)tmpi); //add the string data + i += tmpi + 3; //skip length+size+1 forwards return AMF::Object(name, tmpstr, AMF::AMF0_STRING); break; case AMF::AMF0_NULL: case AMF::AMF0_UNDEFINED: case AMF::AMF0_UNSUPPORTED: ++i; - return AMF::Object(name, (double)0, (AMF::obj0type)data[i-1]); + return AMF::Object(name, (double)0, (AMF::obj0type)data[i - 1]); break; - case AMF::AMF0_OBJECT:{ + case AMF::AMF0_OBJECT: { ++i; AMF::Object ret(name, AMF::AMF0_OBJECT); - while (data[i] + data[i+1] != 0){//while not encountering 0x0000 (we assume 0x000009) - tmpi = data[i]*256+data[i+1];//set tmpi to the UTF-8 length - tmpstr.clear();//clean tmpstr, just to be sure - tmpstr.append((const char*)data+i+2, (size_t)tmpi);//add the string data - i += tmpi + 2;//skip length+size forwards - ret.addContent(AMF::parseOne(data, len, i, tmpstr));//add content, recursively parsed, updating i, setting indice to tmpstr + while (data[i] + data[i + 1] != 0){ //while not encountering 0x0000 (we assume 0x000009) + tmpi = data[i] * 256 + data[i + 1]; //set tmpi to the UTF-8 length + tmpstr.clear(); //clean tmpstr, just to be sure + tmpstr.append((const char*)data + i + 2, (size_t)tmpi); //add the string data + i += tmpi + 2; //skip length+size forwards + ret.addContent(AMF::parseOne(data, len, i, tmpstr)); //add content, recursively parsed, updating i, setting indice to tmpstr } - i += 3;//skip 0x000009 + i += 3; //skip 0x000009 return ret; - } break; - case AMF::AMF0_TYPED_OBJ:{ + } + break; + case AMF::AMF0_TYPED_OBJ: { ++i; - tmpi = data[i]*256+data[i+1];//set tmpi to the UTF-8 length - tmpstr.clear();//clean tmpstr, just to be sure - tmpstr.append((const char*)data+i+2, (size_t)tmpi);//add the string data - AMF::Object ret(tmpstr, AMF::AMF0_TYPED_OBJ);//the object is not named "name" but tmpstr - while (data[i] + data[i+1] != 0){//while not encountering 0x0000 (we assume 0x000009) - tmpi = data[i]*256+data[i+1];//set tmpi to the UTF-8 length - tmpstr.clear();//clean tmpstr, just to be sure - tmpstr.append((const char*)data+i+2, (size_t)tmpi);//add the string data - i += tmpi + 2;//skip length+size forwards - ret.addContent(AMF::parseOne(data, len, i, tmpstr));//add content, recursively parsed, updating i, setting indice to tmpstr + tmpi = data[i] * 256 + data[i + 1]; //set tmpi to the UTF-8 length + tmpstr.clear(); //clean tmpstr, just to be sure + tmpstr.append((const char*)data + i + 2, (size_t)tmpi); //add the string data + AMF::Object ret(tmpstr, AMF::AMF0_TYPED_OBJ); //the object is not named "name" but tmpstr + while (data[i] + data[i + 1] != 0){ //while not encountering 0x0000 (we assume 0x000009) + tmpi = data[i] * 256 + data[i + 1]; //set tmpi to the UTF-8 length + tmpstr.clear(); //clean tmpstr, just to be sure + tmpstr.append((const char*)data + i + 2, (size_t)tmpi); //add the string data + i += tmpi + 2; //skip length+size forwards + ret.addContent(AMF::parseOne(data, len, i, tmpstr)); //add content, recursively parsed, updating i, setting indice to tmpstr } - i += 3;//skip 0x000009 + i += 3; //skip 0x000009 return ret; - } break; - case AMF::AMF0_ECMA_ARRAY:{ + } + break; + case AMF::AMF0_ECMA_ARRAY: { ++i; AMF::Object ret(name, AMF::AMF0_ECMA_ARRAY); - i += 4;//ignore the array length, we re-calculate it - while (data[i] + data[i+1] != 0){//while not encountering 0x0000 (we assume 0x000009) - tmpi = data[i]*256+data[i+1];//set tmpi to the UTF-8 length - tmpstr.clear();//clean tmpstr, just to be sure - tmpstr.append((const char*)data+i+2, (size_t)tmpi);//add the string data - i += tmpi + 2;//skip length+size forwards - ret.addContent(AMF::parseOne(data, len, i, tmpstr));//add content, recursively parsed, updating i, setting indice to tmpstr + i += 4; //ignore the array length, we re-calculate it + while (data[i] + data[i + 1] != 0){ //while not encountering 0x0000 (we assume 0x000009) + tmpi = data[i] * 256 + data[i + 1]; //set tmpi to the UTF-8 length + tmpstr.clear(); //clean tmpstr, just to be sure + tmpstr.append((const char*)data + i + 2, (size_t)tmpi); //add the string data + i += tmpi + 2; //skip length+size forwards + ret.addContent(AMF::parseOne(data, len, i, tmpstr)); //add content, recursively parsed, updating i, setting indice to tmpstr } - i += 3;//skip 0x000009 + i += 3; //skip 0x000009 return ret; - } break; - case AMF::AMF0_STRICT_ARRAY:{ + } + break; + case AMF::AMF0_STRICT_ARRAY: { AMF::Object ret(name, AMF::AMF0_STRICT_ARRAY); - tmpi = data[i+1]*256*256*256+data[i+2]*256*256+data[i+3]*256+data[i+4];//set tmpi to array length - i += 5;//skip size+1 forwards - while (tmpi > 0){//while not done parsing array - ret.addContent(AMF::parseOne(data, len, i, "arrVal"));//add content, recursively parsed, updating i + tmpi = data[i + 1] * 256 * 256 * 256 + data[i + 2] * 256 * 256 + data[i + 3] * 256 + data[i + 4]; //set tmpi to array length + i += 5; //skip size+1 forwards + while (tmpi > 0){ //while not done parsing array + ret.addContent(AMF::parseOne(data, len, i, "arrVal")); //add content, recursively parsed, updating i --tmpi; } return ret; - } break; + } + break; } - #if DEBUG >= 2 +#if DEBUG >= 2 fprintf(stderr, "Error: Unimplemented AMF type %hhx - returning.\n", data[i]); - #endif +#endif return AMF::Object("error", AMF::AMF0_DDV_CONTAINER); -}//parseOne +} //parseOne /// Parses a C-string to a valid AMF::Object. /// This function will find all AMF objects in the string and return /// them all packed in a single AMF::AMF0_DDV_CONTAINER AMF::Object. AMF::Object AMF::parse(const unsigned char * data, unsigned int len){ - AMF::Object ret("returned", AMF::AMF0_DDV_CONTAINER);//container type + AMF::Object ret("returned", AMF::AMF0_DDV_CONTAINER); //container type unsigned int i = 0, j = 0; while (i < len){ ret.addContent(AMF::parseOne(data, len, i, "")); - if (i > j){j = i;}else{return ret;} + if (i > j){ + j = i; + }else{ + return ret; + } } return ret; -}//parse +} //parse /// Parses a std::string to a valid AMF::Object. /// This function will find all AMF objects in the string and return /// them all packed in a single AMF::AMF0_DDV_CONTAINER AMF::Object. AMF::Object AMF::parse(std::string data){ return AMF::parse((const unsigned char*)data.c_str(), data.size()); -}//parse +} //parse /// Returns the std::string Indice for the current object, if available. /// Returns an empty string if no indice exists. -std::string AMF::Object3::Indice(){return myIndice;}; +std::string AMF::Object3::Indice(){ + return myIndice; +} /// Returns the AMF::obj0type AMF0 object type for this object. -AMF::obj3type AMF::Object3::GetType(){return myType;}; +AMF::obj3type AMF::Object3::GetType(){ + return myType; +} /// Returns the double value of this object, if available. /// If this object holds no double value, 0 is returned. -double AMF::Object3::DblValue(){return dblval;}; +double AMF::Object3::DblValue(){ + return dblval; +} /// Returns the integer value of this object, if available. /// If this object holds no integer value, 0 is returned. -int AMF::Object3::IntValue(){return intval;}; +int AMF::Object3::IntValue(){ + return intval; +} /// Returns the std::string value of this object, if available. /// If this object holds no string value, an empty string is returned. -std::string AMF::Object3::StrValue(){return strval;}; +std::string AMF::Object3::StrValue(){ + return strval; +} /// Returns the C-string value of this object, if available. /// If this object holds no string value, an empty C-string is returned. -const char * AMF::Object3::Str(){return strval.c_str();}; +const char * AMF::Object3::Str(){ + return strval.c_str(); +} /// Returns a count of the amount of objects this object currently holds. /// If this object is not a container type, this function will always return 0. -int AMF::Object3::hasContent(){return contents.size();}; +int AMF::Object3::hasContent(){ + return contents.size(); +} /// Adds an AMF::Object to this object. Works for all types, but only makes sense for container types. -void AMF::Object3::addContent(AMF::Object3 c){contents.push_back(c);}; +void AMF::Object3::addContent(AMF::Object3 c){ + contents.push_back(c); +} /// Returns a pointer to the object held at indice i. /// Returns AMF::AMF3_DDV_CONTAINER of indice "error" if no object is held at this indice. /// \param i The indice of the object in this container. -AMF::Object3* AMF::Object3::getContentP(int i){return &contents.at(i);}; +AMF::Object3* AMF::Object3::getContentP(int i){ + return &contents.at(i); +} /// Returns a copy of the object held at indice i. /// Returns a AMF::AMF3_DDV_CONTAINER of indice "error" if no object is held at this indice. /// \param i The indice of the object in this container. -AMF::Object3 AMF::Object3::getContent(int i){return contents.at(i);}; +AMF::Object3 AMF::Object3::getContent(int i){ + return contents.at(i); +} /// Returns a pointer to the object held at indice s. /// Returns NULL if no object is held at this indice. /// \param s The indice of the object in this container. AMF::Object3* AMF::Object3::getContentP(std::string s){ for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ - if (it->Indice() == s){return &(*it);} + if (it->Indice() == s){ + return &( *it); + } } return 0; -}; +} /// Returns a copy of the object held at indice s. /// Returns a AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice. /// \param s The indice of the object in this container. AMF::Object3 AMF::Object3::getContent(std::string s){ for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ - if (it->Indice() == s){return *it;} + if (it->Indice() == s){ + return *it; + } } return AMF::Object3("error", AMF3_DDV_CONTAINER); -}; +} /// Default constructor. /// Simply fills the data with AMF::Object3("error", AMF3_DDV_CONTAINER) AMF::Object3::Object3(){ *this = AMF::Object3("error", AMF3_DDV_CONTAINER); -};//default constructor +} //default constructor /// Constructor for double objects. /// The object type is by default AMF::AMF3_DOUBLE, but this can be forced to a different value. /// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. /// \param val The numeric value of this object. Double AMF3 objects only support double-type values. /// \param setType The object type to force this object to. -AMF::Object3::Object3(std::string indice, double val, AMF::obj3type setType){//num type initializer +AMF::Object3::Object3(std::string indice, double val, AMF::obj3type setType){ //num type initializer myIndice = indice; myType = setType; strval = ""; dblval = val; intval = 0; -}; +} /// Constructor for integer objects. /// The object type is by default AMF::AMF3_INTEGER, but this can be forced to a different value. /// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. /// \param val The numeric value of this object. Integer AMF3 objects only support integer-type values. /// \param setType The object type to force this object to. -AMF::Object3::Object3(std::string indice, int val, AMF::obj3type setType){//num type initializer -myIndice = indice; -myType = setType; -strval = ""; -dblval = val; -intval = 0; -}; +AMF::Object3::Object3(std::string indice, int val, AMF::obj3type setType){ //num type initializer + myIndice = indice; + myType = setType; + strval = ""; + dblval = val; + intval = 0; +} /// Constructor for string objects. /// The object type is by default AMF::AMF0_STRING, but this can be forced to a different value. @@ -516,25 +648,25 @@ intval = 0; /// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. /// \param val The string value of this object. /// \param setType The object type to force this object to. -AMF::Object3::Object3(std::string indice, std::string val, AMF::obj3type setType){//str type initializer +AMF::Object3::Object3(std::string indice, std::string val, AMF::obj3type setType){ //str type initializer myIndice = indice; myType = setType; strval = val; dblval = 0; intval = 0; -}; +} /// Constructor for container objects. /// The object type is by default AMF::AMF0_OBJECT, but this can be forced to a different value. /// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. /// \param setType The object type to force this object to. -AMF::Object3::Object3(std::string indice, AMF::obj3type setType){//object type initializer +AMF::Object3::Object3(std::string indice, AMF::obj3type setType){ //object type initializer myIndice = indice; myType = setType; strval = ""; dblval = 0; intval = 0; -}; +} /// Prints the contents of this object to std::cerr. /// If this object contains other objects, it will call itself recursively @@ -543,28 +675,63 @@ void AMF::Object3::Print(std::string indent){ std::cerr << indent; // print my type switch (myType){ - case AMF::AMF3_UNDEFINED: std::cerr << "Undefined"; break; - case AMF::AMF3_NULL: std::cerr << "Null"; break; - case AMF::AMF3_FALSE: std::cerr << "False"; break; - case AMF::AMF3_TRUE: std::cerr << "True"; break; - case AMF::AMF3_INTEGER: std::cerr << "Integer"; break; - case AMF::AMF3_DOUBLE: std::cerr << "Double"; break; - case AMF::AMF3_STRING: std::cerr << "String"; break; - case AMF::AMF3_XMLDOC: std::cerr << "XML Doc"; break; - case AMF::AMF3_DATE: std::cerr << "Date"; break; - case AMF::AMF3_ARRAY: std::cerr << "Array"; break; - case AMF::AMF3_OBJECT: std::cerr << "Object"; break; - case AMF::AMF3_XML: std::cerr << "XML"; break; - case AMF::AMF3_BYTES: std::cerr << "ByteArray"; break; - case AMF::AMF3_DDV_CONTAINER: std::cerr << "DDVTech Container"; break; + case AMF::AMF3_UNDEFINED: + std::cerr << "Undefined"; + break; + case AMF::AMF3_NULL: + std::cerr << "Null"; + break; + case AMF::AMF3_FALSE: + std::cerr << "False"; + break; + case AMF::AMF3_TRUE: + std::cerr << "True"; + break; + case AMF::AMF3_INTEGER: + std::cerr << "Integer"; + break; + case AMF::AMF3_DOUBLE: + std::cerr << "Double"; + break; + case AMF::AMF3_STRING: + std::cerr << "String"; + break; + case AMF::AMF3_XMLDOC: + std::cerr << "XML Doc"; + break; + case AMF::AMF3_DATE: + std::cerr << "Date"; + break; + case AMF::AMF3_ARRAY: + std::cerr << "Array"; + break; + case AMF::AMF3_OBJECT: + std::cerr << "Object"; + break; + case AMF::AMF3_XML: + std::cerr << "XML"; + break; + case AMF::AMF3_BYTES: + std::cerr << "ByteArray"; + break; + case AMF::AMF3_DDV_CONTAINER: + std::cerr << "DDVTech Container"; + break; } // print my string indice, if available std::cerr << " " << myIndice << " "; // print my numeric or string contents switch (myType){ - case AMF::AMF3_INTEGER: std::cerr << intval; break; - case AMF::AMF3_DOUBLE: std::cerr << dblval; break; - case AMF::AMF3_STRING: case AMF::AMF3_XMLDOC: case AMF::AMF3_XML: case AMF::AMF3_BYTES: + case AMF::AMF3_INTEGER: + std::cerr << intval; + break; + case AMF::AMF3_DOUBLE: + std::cerr << dblval; + break; + case AMF::AMF3_STRING: + case AMF::AMF3_XMLDOC: + case AMF::AMF3_XML: + case AMF::AMF3_BYTES: if (intval > 0){ std::cerr << "REF" << intval; }else{ @@ -578,19 +745,23 @@ void AMF::Object3::Print(std::string indent){ std::cerr << dblval; } break; - case AMF::AMF3_ARRAY: case AMF::AMF3_OBJECT: + case AMF::AMF3_ARRAY: + case AMF::AMF3_OBJECT: if (intval > 0){ std::cerr << "REF" << intval; } break; - default: break;//we don't care about the rest, and don't want a compiler warning... + default: + break; //we don't care about the rest, and don't want a compiler warning... } std::cerr << std::endl; // if I hold other objects, print those too, recursively. if (contents.size() > 0){ - for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){it->Print(indent+" ");} + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ + it->Print(indent + " "); + } } -};//print +} //print /// Packs the AMF object to a std::string for transfer over the network. /// If the object is a container type, this function will call itself recursively and contain all contents. @@ -598,7 +769,7 @@ void AMF::Object3::Print(std::string indent){ std::string AMF::Object3::Pack(){ std::string r = ""; return r; -};//pack +} //pack /// Parses a single AMF3 type - used recursively by the AMF::parse3() functions. /// This function updates i every call with the new position in the data. @@ -612,364 +783,370 @@ AMF::Object3 AMF::parseOne3(const unsigned char *& data, unsigned int &len, unsi unsigned int tmpi = 0; unsigned int arrsize = 0; unsigned char tmpdbl[8]; - double *d;// hack to work around strict aliasing - #if DEBUG >= 10 + double *d; // hack to work around strict aliasing +#if DEBUG >= 10 fprintf(stderr, "Note: AMF3 type %hhx found. %i bytes left\n", data[i], len-i); - #endif +#endif switch (data[i]){ case AMF::AMF3_UNDEFINED: case AMF::AMF3_NULL: case AMF::AMF3_FALSE: case AMF::AMF3_TRUE: ++i; - return AMF::Object3(name, (AMF::obj3type)data[i-1]); + return AMF::Object3(name, (AMF::obj3type)data[i - 1]); break; case AMF::AMF3_INTEGER: - if (data[i+1] < 0x80){ - tmpi = data[i+1]; - i+=2; + if (data[i + 1] < 0x80){ + tmpi = data[i + 1]; + i += 2; }else{ - tmpi = (data[i+1] & 0x7F) << 7;//strip the upper bit, shift 7 up. - if (data[i+2] < 0x80){ - tmpi |= data[i+2]; - i+=3; + tmpi = (data[i + 1] & 0x7F) << 7; //strip the upper bit, shift 7 up. + if (data[i + 2] < 0x80){ + tmpi |= data[i + 2]; + i += 3; }else{ - tmpi = (tmpi | (data[i+2] & 0x7F)) << 7;//strip the upper bit, shift 7 up. - if (data[i+3] < 0x80){ - tmpi |= data[i+3]; - i+=4; + tmpi = (tmpi | (data[i + 2] & 0x7F)) << 7; //strip the upper bit, shift 7 up. + if (data[i + 3] < 0x80){ + tmpi |= data[i + 3]; + i += 4; }else{ - tmpi = (tmpi | (data[i+3] & 0x7F)) << 8;//strip the upper bit, shift 7 up. - tmpi |= data[i+4]; - i+=5; + tmpi = (tmpi | (data[i + 3] & 0x7F)) << 8; //strip the upper bit, shift 7 up. + tmpi |= data[i + 4]; + i += 5; } } } - tmpi = (tmpi << 3) >> 3;//fix sign bit + tmpi = (tmpi << 3) >> 3; //fix sign bit return AMF::Object3(name, (int)tmpi, AMF::AMF3_INTEGER); break; case AMF::AMF3_DOUBLE: - tmpdbl[7] = data[i+1]; - tmpdbl[6] = data[i+2]; - tmpdbl[5] = data[i+3]; - tmpdbl[4] = data[i+4]; - tmpdbl[3] = data[i+5]; - tmpdbl[2] = data[i+6]; - tmpdbl[1] = data[i+7]; - tmpdbl[0] = data[i+8]; - i+=9;//skip 8(a double)+1 forwards + tmpdbl[7] = data[i + 1]; + tmpdbl[6] = data[i + 2]; + tmpdbl[5] = data[i + 3]; + tmpdbl[4] = data[i + 4]; + tmpdbl[3] = data[i + 5]; + tmpdbl[2] = data[i + 6]; + tmpdbl[1] = data[i + 7]; + tmpdbl[0] = data[i + 8]; + i += 9; //skip 8(a double)+1 forwards d = (double*)tmpdbl; return AMF::Object3(name, *d, AMF::AMF3_DOUBLE); break; case AMF::AMF3_STRING: - if (data[i+1] < 0x80){ - tmpi = data[i+1]; - i+=2; + if (data[i + 1] < 0x80){ + tmpi = data[i + 1]; + i += 2; }else{ - tmpi = (data[i+1] & 0x7F) << 7;//strip the upper bit, shift 7 up. - if (data[i+2] < 0x80){ - tmpi |= data[i+2]; - i+=3; + tmpi = (data[i + 1] & 0x7F) << 7; //strip the upper bit, shift 7 up. + if (data[i + 2] < 0x80){ + tmpi |= data[i + 2]; + i += 3; }else{ - tmpi = (tmpi | (data[i+2] & 0x7F)) << 7;//strip the upper bit, shift 7 up. - if (data[i+3] < 0x80){ - tmpi |= data[i+3]; - i+=4; + tmpi = (tmpi | (data[i + 2] & 0x7F)) << 7; //strip the upper bit, shift 7 up. + if (data[i + 3] < 0x80){ + tmpi |= data[i + 3]; + i += 4; }else{ - tmpi = (tmpi | (data[i+3] & 0x7F)) << 8;//strip the upper bit, shift 7 up. - tmpi |= data[i+4]; - i+=5; + tmpi = (tmpi | (data[i + 3] & 0x7F)) << 8; //strip the upper bit, shift 7 up. + tmpi |= data[i + 4]; + i += 5; } } } - tmpi = (tmpi << 3) >> 3;//fix sign bit + tmpi = (tmpi << 3) >> 3; //fix sign bit if ((tmpi & 1) == 0){ - return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_STRING);//reference type + return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_STRING); //reference type } - tmpstr.clear();//clean tmpstr, just to be sure - tmpstr.append((const char *)data+i, (size_t)(tmpi >> 1));//add the string data - i += (tmpi >> 1);//skip length+size+1 forwards - return AMF::Object3(name, tmpstr, AMF::AMF3_STRING);//normal type + tmpstr.clear(); //clean tmpstr, just to be sure + tmpstr.append((const char *)data + i, (size_t)(tmpi >> 1)); //add the string data + i += (tmpi >> 1); //skip length+size+1 forwards + return AMF::Object3(name, tmpstr, AMF::AMF3_STRING); //normal type break; case AMF::AMF3_XMLDOC: - if (data[i+1] < 0x80){ - tmpi = data[i+1]; - i+=2; + if (data[i + 1] < 0x80){ + tmpi = data[i + 1]; + i += 2; }else{ - tmpi = (data[i+1] & 0x7F) << 7;//strip the upper bit, shift 7 up. - if (data[i+2] < 0x80){ - tmpi |= data[i+2]; - i+=3; + tmpi = (data[i + 1] & 0x7F) << 7; //strip the upper bit, shift 7 up. + if (data[i + 2] < 0x80){ + tmpi |= data[i + 2]; + i += 3; }else{ - tmpi = (tmpi | (data[i+2] & 0x7F)) << 7;//strip the upper bit, shift 7 up. - if (data[i+3] < 0x80){ - tmpi |= data[i+3]; - i+=4; + tmpi = (tmpi | (data[i + 2] & 0x7F)) << 7; //strip the upper bit, shift 7 up. + if (data[i + 3] < 0x80){ + tmpi |= data[i + 3]; + i += 4; }else{ - tmpi = (tmpi | (data[i+3] & 0x7F)) << 8;//strip the upper bit, shift 7 up. - tmpi |= data[i+4]; - i+=5; + tmpi = (tmpi | (data[i + 3] & 0x7F)) << 8; //strip the upper bit, shift 7 up. + tmpi |= data[i + 4]; + i += 5; } } } - tmpi = (tmpi << 3) >> 3;//fix sign bit + tmpi = (tmpi << 3) >> 3; //fix sign bit if ((tmpi & 1) == 0){ - return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_XMLDOC);//reference type + return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_XMLDOC); //reference type } - tmpstr.clear();//clean tmpstr, just to be sure - tmpstr.append((const char *)data+i, (size_t)(tmpi >> 1));//add the string data - i += (tmpi >> 1);//skip length+size+1 forwards - return AMF::Object3(name, tmpstr, AMF::AMF3_XMLDOC);//normal type + tmpstr.clear(); //clean tmpstr, just to be sure + tmpstr.append((const char *)data + i, (size_t)(tmpi >> 1)); //add the string data + i += (tmpi >> 1); //skip length+size+1 forwards + return AMF::Object3(name, tmpstr, AMF::AMF3_XMLDOC); //normal type break; case AMF::AMF3_XML: - if (data[i+1] < 0x80){ - tmpi = data[i+1]; - i+=2; + if (data[i + 1] < 0x80){ + tmpi = data[i + 1]; + i += 2; }else{ - tmpi = (data[i+1] & 0x7F) << 7;//strip the upper bit, shift 7 up. - if (data[i+2] < 0x80){ - tmpi |= data[i+2]; - i+=3; + tmpi = (data[i + 1] & 0x7F) << 7; //strip the upper bit, shift 7 up. + if (data[i + 2] < 0x80){ + tmpi |= data[i + 2]; + i += 3; }else{ - tmpi = (tmpi | (data[i+2] & 0x7F)) << 7;//strip the upper bit, shift 7 up. - if (data[i+3] < 0x80){ - tmpi |= data[i+3]; - i+=4; + tmpi = (tmpi | (data[i + 2] & 0x7F)) << 7; //strip the upper bit, shift 7 up. + if (data[i + 3] < 0x80){ + tmpi |= data[i + 3]; + i += 4; }else{ - tmpi = (tmpi | (data[i+3] & 0x7F)) << 8;//strip the upper bit, shift 7 up. - tmpi |= data[i+4]; - i+=5; + tmpi = (tmpi | (data[i + 3] & 0x7F)) << 8; //strip the upper bit, shift 7 up. + tmpi |= data[i + 4]; + i += 5; } } } - tmpi = (tmpi << 3) >> 3;//fix sign bit + tmpi = (tmpi << 3) >> 3; //fix sign bit if ((tmpi & 1) == 0){ - return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_XML);//reference type + return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_XML); //reference type } - tmpstr.clear();//clean tmpstr, just to be sure - tmpstr.append((const char *)data+i, (size_t)(tmpi >> 1));//add the string data - i += (tmpi >> 1);//skip length+size+1 forwards - return AMF::Object3(name, tmpstr, AMF::AMF3_XML);//normal type + tmpstr.clear(); //clean tmpstr, just to be sure + tmpstr.append((const char *)data + i, (size_t)(tmpi >> 1)); //add the string data + i += (tmpi >> 1); //skip length+size+1 forwards + return AMF::Object3(name, tmpstr, AMF::AMF3_XML); //normal type break; case AMF::AMF3_BYTES: - if (data[i+1] < 0x80){ - tmpi = data[i+1]; - i+=2; + if (data[i + 1] < 0x80){ + tmpi = data[i + 1]; + i += 2; }else{ - tmpi = (data[i+1] & 0x7F) << 7;//strip the upper bit, shift 7 up. - if (data[i+2] < 0x80){ - tmpi |= data[i+2]; - i+=3; + tmpi = (data[i + 1] & 0x7F) << 7; //strip the upper bit, shift 7 up. + if (data[i + 2] < 0x80){ + tmpi |= data[i + 2]; + i += 3; }else{ - tmpi = (tmpi | (data[i+2] & 0x7F)) << 7;//strip the upper bit, shift 7 up. - if (data[i+3] < 0x80){ - tmpi |= data[i+3]; - i+=4; + tmpi = (tmpi | (data[i + 2] & 0x7F)) << 7; //strip the upper bit, shift 7 up. + if (data[i + 3] < 0x80){ + tmpi |= data[i + 3]; + i += 4; }else{ - tmpi = (tmpi | (data[i+3] & 0x7F)) << 8;//strip the upper bit, shift 7 up. - tmpi |= data[i+4]; - tmpi = (tmpi << 3) >> 3;//fix sign bit - i+=5; + tmpi = (tmpi | (data[i + 3] & 0x7F)) << 8; //strip the upper bit, shift 7 up. + tmpi |= data[i + 4]; + tmpi = (tmpi << 3) >> 3; //fix sign bit + i += 5; } } } if ((tmpi & 1) == 0){ - return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_BYTES);//reference type + return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_BYTES); //reference type } - tmpstr.clear();//clean tmpstr, just to be sure - tmpstr.append((const char *)data+i, (size_t)(tmpi >> 1));//add the string data - i += (tmpi >> 1);//skip length+size+1 forwards - return AMF::Object3(name, tmpstr, AMF::AMF3_BYTES);//normal type + tmpstr.clear(); //clean tmpstr, just to be sure + tmpstr.append((const char *)data + i, (size_t)(tmpi >> 1)); //add the string data + i += (tmpi >> 1); //skip length+size+1 forwards + return AMF::Object3(name, tmpstr, AMF::AMF3_BYTES); //normal type break; case AMF::AMF3_DATE: - if (data[i+1] < 0x80){ - tmpi = data[i+1]; - i+=2; + if (data[i + 1] < 0x80){ + tmpi = data[i + 1]; + i += 2; }else{ - tmpi = (data[i+1] & 0x7F) << 7;//strip the upper bit, shift 7 up. - if (data[i+2] < 0x80){ - tmpi |= data[i+2]; - i+=3; + tmpi = (data[i + 1] & 0x7F) << 7; //strip the upper bit, shift 7 up. + if (data[i + 2] < 0x80){ + tmpi |= data[i + 2]; + i += 3; }else{ - tmpi = (tmpi | (data[i+2] & 0x7F)) << 7;//strip the upper bit, shift 7 up. - if (data[i+3] < 0x80){ - tmpi |= data[i+3]; - i+=4; + tmpi = (tmpi | (data[i + 2] & 0x7F)) << 7; //strip the upper bit, shift 7 up. + if (data[i + 3] < 0x80){ + tmpi |= data[i + 3]; + i += 4; }else{ - tmpi = (tmpi | (data[i+3] & 0x7F)) << 8;//strip the upper bit, shift 7 up. - tmpi |= data[i+4]; - i+=5; + tmpi = (tmpi | (data[i + 3] & 0x7F)) << 8; //strip the upper bit, shift 7 up. + tmpi |= data[i + 4]; + i += 5; } } } - tmpi = (tmpi << 3) >> 3;//fix sign bit + tmpi = (tmpi << 3) >> 3; //fix sign bit if ((tmpi & 1) == 0){ - return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_DATE);//reference type + return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_DATE); //reference type } tmpdbl[7] = data[i]; - tmpdbl[6] = data[i+1]; - tmpdbl[5] = data[i+2]; - tmpdbl[4] = data[i+3]; - tmpdbl[3] = data[i+4]; - tmpdbl[2] = data[i+5]; - tmpdbl[1] = data[i+6]; - tmpdbl[0] = data[i+7]; + tmpdbl[6] = data[i + 1]; + tmpdbl[5] = data[i + 2]; + tmpdbl[4] = data[i + 3]; + tmpdbl[3] = data[i + 4]; + tmpdbl[2] = data[i + 5]; + tmpdbl[1] = data[i + 6]; + tmpdbl[0] = data[i + 7]; d = (double*)tmpdbl; - i += 8;//skip a double forwards + i += 8; //skip a double forwards return AMF::Object3(name, *d, AMF::AMF3_DATE); break; - case AMF::AMF3_ARRAY:{ - if (data[i+1] < 0x80){ - tmpi = data[i+1]; - i+=2; + case AMF::AMF3_ARRAY: { + if (data[i + 1] < 0x80){ + tmpi = data[i + 1]; + i += 2; }else{ - tmpi = (data[i+1] & 0x7F) << 7;//strip the upper bit, shift 7 up. - if (data[i+2] < 0x80){ - tmpi |= data[i+2]; - i+=3; + tmpi = (data[i + 1] & 0x7F) << 7; //strip the upper bit, shift 7 up. + if (data[i + 2] < 0x80){ + tmpi |= data[i + 2]; + i += 3; }else{ - tmpi = (tmpi | (data[i+2] & 0x7F)) << 7;//strip the upper bit, shift 7 up. - if (data[i+3] < 0x80){ - tmpi |= data[i+3]; - i+=4; + tmpi = (tmpi | (data[i + 2] & 0x7F)) << 7; //strip the upper bit, shift 7 up. + if (data[i + 3] < 0x80){ + tmpi |= data[i + 3]; + i += 4; }else{ - tmpi = (tmpi | (data[i+3] & 0x7F)) << 8;//strip the upper bit, shift 7 up. - tmpi |= data[i+4]; - i+=5; + tmpi = (tmpi | (data[i + 3] & 0x7F)) << 8; //strip the upper bit, shift 7 up. + tmpi |= data[i + 4]; + i += 5; } } } - tmpi = (tmpi << 3) >> 3;//fix sign bit + tmpi = (tmpi << 3) >> 3; //fix sign bit if ((tmpi & 1) == 0){ - return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_ARRAY);//reference type + return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_ARRAY); //reference type } AMF::Object3 ret(name, AMF::AMF3_ARRAY); arrsize = tmpi >> 1; do{ - if (data[i+1] < 0x80){ - tmpi = data[i+1]; - i+=2; + if (data[i + 1] < 0x80){ + tmpi = data[i + 1]; + i += 2; }else{ - tmpi = (data[i+1] & 0x7F) << 7;//strip the upper bit, shift 7 up. - if (data[i+2] < 0x80){ - tmpi |= data[i+2]; - i+=3; + tmpi = (data[i + 1] & 0x7F) << 7; //strip the upper bit, shift 7 up. + if (data[i + 2] < 0x80){ + tmpi |= data[i + 2]; + i += 3; }else{ - tmpi = (tmpi | (data[i+2] & 0x7F)) << 7;//strip the upper bit, shift 7 up. - if (data[i+3] < 0x80){ - tmpi |= data[i+3]; - i+=4; + tmpi = (tmpi | (data[i + 2] & 0x7F)) << 7; //strip the upper bit, shift 7 up. + if (data[i + 3] < 0x80){ + tmpi |= data[i + 3]; + i += 4; }else{ - tmpi = (tmpi | (data[i+3] & 0x7F)) << 8;//strip the upper bit, shift 7 up. - tmpi |= data[i+4]; - i+=5; + tmpi = (tmpi | (data[i + 3] & 0x7F)) << 8; //strip the upper bit, shift 7 up. + tmpi |= data[i + 4]; + i += 5; } } } - tmpi = (tmpi << 3) >> 4;//fix sign bit, ignore references for now... + tmpi = (tmpi << 3) >> 4; //fix sign bit, ignore references for now... /// \todo Fix references? if (tmpi > 0){ - tmpstr.clear();//clean tmpstr, just to be sure - tmpstr.append((const char*)data+i, (size_t)tmpi);//add the string data - ret.addContent(AMF::parseOne3(data, len, i, tmpstr));//add content, recursively parsed, updating i + tmpstr.clear(); //clean tmpstr, just to be sure + tmpstr.append((const char*)data + i, (size_t)tmpi); //add the string data + ret.addContent(AMF::parseOne3(data, len, i, tmpstr)); //add content, recursively parsed, updating i } - }while(tmpi > 0); - while (arrsize > 0){//while not done parsing array - ret.addContent(AMF::parseOne3(data, len, i, "arrVal"));//add content, recursively parsed, updating i + }while (tmpi > 0); + while (arrsize > 0){ //while not done parsing array + ret.addContent(AMF::parseOne3(data, len, i, "arrVal")); //add content, recursively parsed, updating i --arrsize; } return ret; - } break; - case AMF::AMF3_OBJECT:{ - if (data[i+1] < 0x80){ - tmpi = data[i+1]; - i+=2; + } + break; + case AMF::AMF3_OBJECT: { + if (data[i + 1] < 0x80){ + tmpi = data[i + 1]; + i += 2; }else{ - tmpi = (data[i+1] & 0x7F) << 7;//strip the upper bit, shift 7 up. - if (data[i+2] < 0x80){ - tmpi |= data[i+2]; - i+=3; + tmpi = (data[i + 1] & 0x7F) << 7; //strip the upper bit, shift 7 up. + if (data[i + 2] < 0x80){ + tmpi |= data[i + 2]; + i += 3; }else{ - tmpi = (tmpi | (data[i+2] & 0x7F)) << 7;//strip the upper bit, shift 7 up. - if (data[i+3] < 0x80){ - tmpi |= data[i+3]; - i+=4; + tmpi = (tmpi | (data[i + 2] & 0x7F)) << 7; //strip the upper bit, shift 7 up. + if (data[i + 3] < 0x80){ + tmpi |= data[i + 3]; + i += 4; }else{ - tmpi = (tmpi | (data[i+3] & 0x7F)) << 8;//strip the upper bit, shift 7 up. - tmpi |= data[i+4]; - i+=5; + tmpi = (tmpi | (data[i + 3] & 0x7F)) << 8; //strip the upper bit, shift 7 up. + tmpi |= data[i + 4]; + i += 5; } } } - tmpi = (tmpi << 3) >> 3;//fix sign bit + tmpi = (tmpi << 3) >> 3; //fix sign bit if ((tmpi & 1) == 0){ - return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_OBJECT);//reference type + return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_OBJECT); //reference type } AMF::Object3 ret(name, AMF::AMF3_OBJECT); bool isdynamic = false; - if ((tmpi & 2) == 0){//traits by reference, skip for now + if ((tmpi & 2) == 0){ //traits by reference, skip for now /// \todo Implement traits by reference. Or references in general, of course... }else{ isdynamic = ((tmpi & 8) == 8); - arrsize = tmpi >> 4;//count of sealed members + arrsize = tmpi >> 4; //count of sealed members /// \todo Read in arrsize sealed member names, then arrsize sealed members. } if (isdynamic){ do{ - if (data[i+1] < 0x80){ - tmpi = data[i+1]; - i+=2; + if (data[i + 1] < 0x80){ + tmpi = data[i + 1]; + i += 2; }else{ - tmpi = (data[i+1] & 0x7F) << 7;//strip the upper bit, shift 7 up. - if (data[i+2] < 0x80){ - tmpi |= data[i+2]; - i+=3; + tmpi = (data[i + 1] & 0x7F) << 7; //strip the upper bit, shift 7 up. + if (data[i + 2] < 0x80){ + tmpi |= data[i + 2]; + i += 3; }else{ - tmpi = (tmpi | (data[i+2] & 0x7F)) << 7;//strip the upper bit, shift 7 up. - if (data[i+3] < 0x80){ - tmpi |= data[i+3]; - i+=4; + tmpi = (tmpi | (data[i + 2] & 0x7F)) << 7; //strip the upper bit, shift 7 up. + if (data[i + 3] < 0x80){ + tmpi |= data[i + 3]; + i += 4; }else{ - tmpi = (tmpi | (data[i+3] & 0x7F)) << 8;//strip the upper bit, shift 7 up. - tmpi |= data[i+4]; - i+=5; + tmpi = (tmpi | (data[i + 3] & 0x7F)) << 8; //strip the upper bit, shift 7 up. + tmpi |= data[i + 4]; + i += 5; } } } - tmpi = (tmpi << 3) >> 4;//fix sign bit, ignore references for now... + tmpi = (tmpi << 3) >> 4; //fix sign bit, ignore references for now... /// \todo Fix references? if (tmpi > 0){ - tmpstr.clear();//clean tmpstr, just to be sure - tmpstr.append((const char*)data+i, (size_t)tmpi);//add the string data - ret.addContent(AMF::parseOne3(data, len, i, tmpstr));//add content, recursively parsed, updating i + tmpstr.clear(); //clean tmpstr, just to be sure + tmpstr.append((const char*)data + i, (size_t)tmpi); //add the string data + ret.addContent(AMF::parseOne3(data, len, i, tmpstr)); //add content, recursively parsed, updating i } - }while(tmpi > 0);//keep reading dynamic values until empty string - }//dynamic types + }while (tmpi > 0); //keep reading dynamic values until empty string + } //dynamic types return ret; - } break; + } + break; } - #if DEBUG >= 2 +#if DEBUG >= 2 fprintf(stderr, "Error: Unimplemented AMF3 type %hhx - returning.\n", data[i]); - #endif +#endif return AMF::Object3("error", AMF::AMF3_DDV_CONTAINER); -}//parseOne +} //parseOne /// Parses a C-string to a valid AMF::Object3. /// This function will find all AMF3 objects in the string and return /// them all packed in a single AMF::AMF3_DDV_CONTAINER AMF::Object3. AMF::Object3 AMF::parse3(const unsigned char * data, unsigned int len){ - AMF::Object3 ret("returned", AMF::AMF3_DDV_CONTAINER);//container type + AMF::Object3 ret("returned", AMF::AMF3_DDV_CONTAINER); //container type unsigned int i = 0, j = 0; while (i < len){ ret.addContent(AMF::parseOne3(data, len, i, "")); - if (i > j){j = i;}else{return ret;} + if (i > j){ + j = i; + }else{ + return ret; + } } return ret; -}//parse +} //parse /// Parses a std::string to a valid AMF::Object3. /// This function will find all AMF3 objects in the string and return /// them all packed in a single AMF::AMF3_DDV_CONTAINER AMF::Object3. AMF::Object3 AMF::parse3(std::string data){ return AMF::parse3((const unsigned char*)data.c_str(), data.size()); -}//parse +} //parse diff --git a/lib/amf.h b/lib/amf.h index 836cfdb2..f797a6ba 100644 --- a/lib/amf.h +++ b/lib/amf.h @@ -4,14 +4,13 @@ #pragma once #include #include -//#include #include /// Holds all AMF parsing and creation related functions and classes. -namespace AMF{ +namespace AMF { /// Enumerates all possible AMF0 types, adding a special DDVTECH container type for ease of use. - enum obj0type { + enum obj0type{ AMF0_NUMBER = 0x00, AMF0_BOOL = 0x01, AMF0_STRING = 0x02, @@ -34,7 +33,7 @@ namespace AMF{ }; /// Enumerates all possible AMF3 types, adding a special DDVTECH container type for ease of use. - enum obj3type { + enum obj3type{ AMF3_UNDEFINED = 0x00, AMF3_NULL = 0x01, AMF3_FALSE = 0x02, @@ -50,10 +49,10 @@ namespace AMF{ 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 { + class Object{ public: std::string Indice(); obj0type GetType(); @@ -78,7 +77,8 @@ namespace AMF{ std::string strval; ///< Holds this objects string value, if any. double numval; ///< Holds this objects numeric value, if any. std::vector contents; ///< Holds this objects contents, if any (for container types). - };//AMFType + }; + //AMFType /// Parses a C-string to a valid AMF::Object. Object parse(const unsigned char * data, unsigned int len); @@ -89,7 +89,7 @@ namespace AMF{ /// 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 { + class Object3{ public: std::string Indice(); obj3type GetType(); @@ -117,13 +117,14 @@ namespace AMF{ double dblval; ///< Holds this objects double value, if any. int intval; ///< Holds this objects int value, if any. std::vector contents; ///< Holds this objects contents, if any (for container types). - };//AMFType - + }; + //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 + +} //AMF namespace diff --git a/lib/auth.cpp b/lib/auth.cpp index 13c3e1d3..df649766 100644 --- a/lib/auth.cpp +++ b/lib/auth.cpp @@ -6,35 +6,21 @@ #include "auth.h" #include "base64.h" -namespace Secure{ +namespace Secure { - static unsigned char __gbv2keypub_der[] = { - 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, - 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, - 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xe5, 0xd7, 0x9c, - 0x7d, 0x73, 0xc6, 0xe6, 0xfb, 0x35, 0x7e, 0xd7, 0x57, 0x99, 0x07, 0xdb, - 0x99, 0x70, 0xc9, 0xd0, 0x3e, 0x53, 0x57, 0x3c, 0x1e, 0x55, 0xda, 0x0f, - 0x69, 0xbf, 0x26, 0x79, 0xc7, 0xb6, 0xdd, 0x8e, 0x83, 0x32, 0x65, 0x74, - 0x0d, 0x74, 0x48, 0x42, 0x49, 0x22, 0x52, 0x58, 0x56, 0xc3, 0xe4, 0x49, - 0x5d, 0xac, 0x6a, 0x94, 0xb1, 0x64, 0x14, 0xbf, 0x4d, 0xd5, 0xd7, 0x3a, - 0xca, 0x5c, 0x1e, 0x6f, 0x42, 0x30, 0xac, 0x29, 0xaa, 0xa0, 0x85, 0xd2, - 0x16, 0xa2, 0x8e, 0x89, 0x12, 0xc4, 0x92, 0x06, 0xea, 0xed, 0x48, 0xf6, - 0xdb, 0xed, 0x4f, 0x62, 0x6c, 0xfa, 0xcf, 0xc2, 0xb9, 0x8d, 0x04, 0xb2, - 0xba, 0x63, 0xc9, 0xcc, 0xee, 0x23, 0x64, 0x46, 0x14, 0x12, 0xc8, 0x38, - 0x67, 0x69, 0x6b, 0xaf, 0xd1, 0x7c, 0xb1, 0xb5, 0x79, 0xe4, 0x4e, 0x3a, - 0xa7, 0xe8, 0x28, 0x89, 0x25, 0xc0, 0xd0, 0xd8, 0xc7, 0xd2, 0x26, 0xaa, - 0xf5, 0xbf, 0x36, 0x55, 0x01, 0x89, 0x58, 0x1f, 0x1e, 0xf5, 0xa5, 0x42, - 0x8f, 0x60, 0x2e, 0xc2, 0xd8, 0x21, 0x0b, 0x6c, 0x8d, 0xbb, 0x72, 0xf2, - 0x19, 0x30, 0xe3, 0x4c, 0x3e, 0x80, 0xe7, 0xf2, 0xe3, 0x89, 0x4f, 0xd4, - 0xee, 0x96, 0x3e, 0x4a, 0x9b, 0xe5, 0x16, 0x01, 0xf1, 0x98, 0xc9, 0x0b, - 0xd6, 0xdf, 0x8a, 0x64, 0x47, 0xc4, 0x44, 0xcc, 0x92, 0x69, 0x28, 0xee, - 0x7d, 0xac, 0xdc, 0x30, 0x56, 0x3a, 0xe7, 0xbc, 0xba, 0x45, 0x16, 0x2c, - 0x4c, 0x46, 0x6b, 0x2b, 0x20, 0xfb, 0x3d, 0x20, 0x35, 0xbb, 0x48, 0x49, - 0x13, 0x65, 0xc9, 0x9a, 0x38, 0x10, 0x84, 0x1a, 0x8c, 0xc9, 0xd7, 0xde, - 0x07, 0x10, 0x5a, 0xfb, 0xb4, 0x95, 0xae, 0x18, 0xf2, 0xe3, 0x15, 0xe8, - 0xad, 0x7e, 0xe5, 0x3c, 0xa8, 0x47, 0x85, 0xd6, 0x1f, 0x54, 0xb5, 0xa3, - 0x79, 0x02, 0x03, 0x01, 0x00, 0x01 - }; ///< The GBv2 public key file. + static unsigned char __gbv2keypub_der[] = {0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, + 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xe5, 0xd7, 0x9c, 0x7d, 0x73, 0xc6, 0xe6, 0xfb, + 0x35, 0x7e, 0xd7, 0x57, 0x99, 0x07, 0xdb, 0x99, 0x70, 0xc9, 0xd0, 0x3e, 0x53, 0x57, 0x3c, 0x1e, 0x55, 0xda, 0x0f, 0x69, 0xbf, 0x26, 0x79, 0xc7, + 0xb6, 0xdd, 0x8e, 0x83, 0x32, 0x65, 0x74, 0x0d, 0x74, 0x48, 0x42, 0x49, 0x22, 0x52, 0x58, 0x56, 0xc3, 0xe4, 0x49, 0x5d, 0xac, 0x6a, 0x94, 0xb1, + 0x64, 0x14, 0xbf, 0x4d, 0xd5, 0xd7, 0x3a, 0xca, 0x5c, 0x1e, 0x6f, 0x42, 0x30, 0xac, 0x29, 0xaa, 0xa0, 0x85, 0xd2, 0x16, 0xa2, 0x8e, 0x89, 0x12, + 0xc4, 0x92, 0x06, 0xea, 0xed, 0x48, 0xf6, 0xdb, 0xed, 0x4f, 0x62, 0x6c, 0xfa, 0xcf, 0xc2, 0xb9, 0x8d, 0x04, 0xb2, 0xba, 0x63, 0xc9, 0xcc, 0xee, + 0x23, 0x64, 0x46, 0x14, 0x12, 0xc8, 0x38, 0x67, 0x69, 0x6b, 0xaf, 0xd1, 0x7c, 0xb1, 0xb5, 0x79, 0xe4, 0x4e, 0x3a, 0xa7, 0xe8, 0x28, 0x89, 0x25, + 0xc0, 0xd0, 0xd8, 0xc7, 0xd2, 0x26, 0xaa, 0xf5, 0xbf, 0x36, 0x55, 0x01, 0x89, 0x58, 0x1f, 0x1e, 0xf5, 0xa5, 0x42, 0x8f, 0x60, 0x2e, 0xc2, 0xd8, + 0x21, 0x0b, 0x6c, 0x8d, 0xbb, 0x72, 0xf2, 0x19, 0x30, 0xe3, 0x4c, 0x3e, 0x80, 0xe7, 0xf2, 0xe3, 0x89, 0x4f, 0xd4, 0xee, 0x96, 0x3e, 0x4a, 0x9b, + 0xe5, 0x16, 0x01, 0xf1, 0x98, 0xc9, 0x0b, 0xd6, 0xdf, 0x8a, 0x64, 0x47, 0xc4, 0x44, 0xcc, 0x92, 0x69, 0x28, 0xee, 0x7d, 0xac, 0xdc, 0x30, 0x56, + 0x3a, 0xe7, 0xbc, 0xba, 0x45, 0x16, 0x2c, 0x4c, 0x46, 0x6b, 0x2b, 0x20, 0xfb, 0x3d, 0x20, 0x35, 0xbb, 0x48, 0x49, 0x13, 0x65, 0xc9, 0x9a, 0x38, + 0x10, 0x84, 0x1a, 0x8c, 0xc9, 0xd7, 0xde, 0x07, 0x10, 0x5a, 0xfb, 0xb4, 0x95, 0xae, 0x18, 0xf2, 0xe3, 0x15, 0xe8, 0xad, 0x7e, 0xe5, 0x3c, 0xa8, + 0x47, 0x85, 0xd6, 0x1f, 0x54, 0xb5, 0xa3, 0x79, 0x02, 0x03, 0x01, 0x00, 0x01}; ///< The GBv2 public key file. static unsigned int __gbv2keypub_der_len = 294; ///< Length of GBv2 public key data /// Attempts to load the GBv2 public key. diff --git a/lib/auth.h b/lib/auth.h index 9b299232..7024e36f 100644 --- a/lib/auth.h +++ b/lib/auth.h @@ -1,7 +1,7 @@ #pragma once #include -namespace Secure{ +namespace Secure { class Auth{ private: void * pubkey; ///< Holds the public key. diff --git a/lib/base64.cpp b/lib/base64.cpp index a2c9dec3..8a15611f 100644 --- a/lib/base64.cpp +++ b/lib/base64.cpp @@ -3,62 +3,84 @@ /// Needed for base64_encode function const std::string Base64::chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - /// Helper for base64_decode function -inline bool Base64::is_base64(unsigned char c) { +/// 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 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];} + 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];} + if (n < 3){ + quad[3] = '='; + } + if (n < 2){ + quad[2] = '='; + } + for (i = 0; i < 4; i++){ + ret += quad[i]; + } } return ret; -}//base64_encode +} //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) { +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]);} + 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];} + 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]);} + 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]; + for (j = 0; (j < i - 1); j++) + ret += char_array_3[j]; } return ret; } diff --git a/lib/config.cpp b/lib/config.cpp index cc9c6928..9a771823 100644 --- a/lib/config.cpp +++ b/lib/config.cpp @@ -56,14 +56,18 @@ Util::Config::Config(std::string cmd, std::string version){ ///\endcode void Util::Config::addOption(std::string optname, JSON::Value option){ vals[optname] = option; - if (!vals[optname].isMember("value") && vals[optname].isMember("default")){ + 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++;} + if (it->second.isMember("long")){ + long_count++; + } + if (it->second.isMember("long_off")){ + long_count++; + } } } @@ -73,16 +77,30 @@ void Util::Config::printHelp(std::ostream & output){ std::map 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;} + 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("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;} + if (current > longest){ + longest = current; + } args[it->second["arg_num"].asInt()] = it->first; } } @@ -108,7 +126,9 @@ void Util::Config::printHelp(std::ostream & output){ f = "-" + it->second["short"].asString(); } } - while (f.size() < longest){f.append(" ");} + while (f.size() < longest){ + f.append(" "); + } if (it->second.isMember("arg")){ output << f << "(" << it->second["arg"].asString() << ") " << it->second["help"].asString() << std::endl; }else{ @@ -126,7 +146,9 @@ void Util::Config::printHelp(std::ostream & output){ f = "-" + it->second["short_off"].asString(); } } - while (f.size() < longest){f.append(" ");} + while (f.size() < longest){ + f.append(" "); + } if (it->second.isMember("arg")){ output << f << "(" << it->second["arg"].asString() << ") " << it->second["help"].asString() << std::endl; }else{ @@ -135,40 +157,49 @@ void Util::Config::printHelp(std::ostream & output){ } if (it->second.isMember("arg_num")){ f = it->first; - while (f.size() < longest){f.append(" ");} + 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. void 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)); + struct option * longOpts = (struct option*)calloc(long_count + 1, sizeof(struct option)); int long_i = 0; int arg_count = 0; 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("arg")){ + shortopts += ":"; + } } if (it->second.isMember("short_off")){ shortopts += it->second["short_off"].asString(); - if (it->second.isMember("arg")){shortopts += ":";} + 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;} + 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;} + 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())){ @@ -203,10 +234,10 @@ void Util::Config::parseArgs(int argc, char ** argv){ } 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. + } //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]); @@ -226,18 +257,18 @@ void Util::Config::parseArgs(int argc, char ** argv){ /// 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)){ + 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()){ + 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]; + return vals[optname]["value"][n - 1]; } } @@ -273,7 +304,7 @@ void Util::Config::activate(){ } struct sigaction new_action; new_action.sa_handler = signal_handler; - sigemptyset (&new_action.sa_mask); + sigemptyset( &new_action.sa_mask); new_action.sa_flags = 0; sigaction(SIGINT, &new_action, NULL); sigaction(SIGHUP, &new_action, NULL); @@ -288,41 +319,53 @@ void Util::Config::activate(){ /// 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 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. + case SIGCHLD: //when a child dies, reap it. wait(0); break; default: //other signals are ignored break; } -}//signal_handler +} //signal_handler /// Adds the default connector options to this Util::Config object. void Util::Config::addConnectorOptions(int port){ JSON::Value stored_port = JSON::fromString("{\"long\":\"port\", \"short\":\"p\", \"arg\":\"integer\", \"help\":\"TCP port to listen on.\"}"); stored_port["value"].append((long long int)port); addOption("listen_port", stored_port); - addOption("listen_interface", JSON::fromString("{\"long\":\"interface\", \"value\":[\"0.0.0.0\"], \"short\":\"i\", \"arg\":\"string\", \"help\":\"Interface address to listen on, or 0.0.0.0 for all available interfaces.\"}")); - addOption("username", JSON::fromString("{\"long\":\"username\", \"value\":[\"root\"], \"short\":\"u\", \"arg\":\"string\", \"help\":\"Username to drop privileges to, or root to not drop provileges.\"}")); - addOption("daemonize", JSON::fromString("{\"long\":\"daemon\", \"short\":\"d\", \"value\":[1], \"long_off\":\"nodaemon\", \"short_off\":\"n\", \"help\":\"Whether or not to daemonize the process after starting.\"}")); -}//addConnectorOptions + addOption("listen_interface", + JSON::fromString( + "{\"long\":\"interface\", \"value\":[\"0.0.0.0\"], \"short\":\"i\", \"arg\":\"string\", \"help\":\"Interface address to listen on, or 0.0.0.0 for all available interfaces.\"}")); + addOption("username", + JSON::fromString( + "{\"long\":\"username\", \"value\":[\"root\"], \"short\":\"u\", \"arg\":\"string\", \"help\":\"Username to drop privileges to, or root to not drop provileges.\"}")); + addOption("daemonize", + JSON::fromString( + "{\"long\":\"daemon\", \"short\":\"d\", \"value\":[1], \"long_off\":\"nodaemon\", \"short_off\":\"n\", \"help\":\"Whether or not to daemonize the process after starting.\"}")); +} //addConnectorOptions /// Gets directory the current executable is stored in. std::string Util::getMyPath(){ char mypath[500]; int ret = readlink("/proc/self/exe", mypath, 500); - if (ret != -1){mypath[ret] = 0;}else{mypath[0] = 0;} + if (ret != -1){ + mypath[ret] = 0; + }else{ + mypath[0] = 0; + } std::string tPath = mypath; size_t slash = tPath.rfind('/'); if (slash == std::string::npos){ slash = tPath.rfind('\\'); - if (slash == std::string::npos){return "";} + if (slash == std::string::npos){ + return ""; + } } - tPath.resize(slash+1); + tPath.resize(slash + 1); return tPath; } @@ -330,20 +373,20 @@ std::string Util::getMyPath(){ void Util::setUser(std::string username){ if (username != "root"){ struct passwd * user_info = getpwnam(username.c_str()); - if (!user_info){ - #if DEBUG >= 1 + if ( !user_info){ +#if DEBUG >= 1 fprintf(stderr, "Error: could not setuid %s: could not get PID\n", username.c_str()); - #endif +#endif return; }else{ if (setuid(user_info->pw_uid) != 0){ - #if DEBUG >= 1 +#if DEBUG >= 1 fprintf(stderr, "Error: could not setuid %s: not allowed\n", username.c_str()); - #endif +#endif }else{ - #if DEBUG >= 3 +#if DEBUG >= 3 fprintf(stderr, "Changed user to %s\n", username.c_str()); - #endif +#endif } } } @@ -354,12 +397,12 @@ void Util::setUser(std::string username){ /// Does not change directory to root. /// Does redirect output to /dev/null void Util::Daemonize(){ - #if DEBUG >= 3 +#if DEBUG >= 3 fprintf(stderr, "Going into background mode...\n"); - #endif - if (daemon(1, 0) < 0) { - #if DEBUG >= 1 +#endif + if (daemon(1, 0) < 0){ +#if DEBUG >= 1 fprintf(stderr, "Failed to daemonize: %s\n", strerror(errno)); - #endif +#endif } } diff --git a/lib/config.h b/lib/config.h index 7e91a4b6..c776c21b 100644 --- a/lib/config.h +++ b/lib/config.h @@ -2,11 +2,16 @@ /// Contains generic function headers for managing configuration. #pragma once + +#ifndef PACKAGE_VERSION +#define PACKAGE_VERSION "unknown" +#endif + #include #include "json.h" /// Contains utility code, not directly related to streaming media -namespace Util{ +namespace Util { /// Deals with parsing configuration from commandline options. class Config{ @@ -40,4 +45,5 @@ namespace Util{ /// Will turn the current process into a daemon. void Daemonize(); -}; +} +; diff --git a/lib/crypto.cpp b/lib/crypto.cpp deleted file mode 100644 index e69de29b..00000000 diff --git a/lib/crypto.h b/lib/crypto.h deleted file mode 100644 index 2187c9d4..00000000 --- a/lib/crypto.h +++ /dev/null @@ -1,25 +0,0 @@ -/// \file crypto.h -/// Holds all headers needed for RTMP cryptography functions. - -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - - -void InitRC4Encryption(uint8_t *secretKey, uint8_t *pubKeyIn, uint8_t *pubKeyOut, RC4_KEY *rc4keyIn, RC4_KEY *rc4keyOut); - -void HMACsha256(const void *pData, uint32_t dataLength, const void *pKey, uint32_t keyLength, void *pResult); - - -extern uint8_t genuineFMSKey[]; - -bool ValidateClientScheme(uint8_t * pBuffer, uint8_t scheme); diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 7ee542e2..1742e493 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -5,12 +5,12 @@ #include #include //for memcmp #include //for htonl/ntohl - char DTSC::Magic_Header[] = "DTSC"; char DTSC::Magic_Packet[] = "DTPD"; /// Initializes a DTSC::Stream with only one packet buffer. DTSC::Stream::Stream(){ + datapointertype = DTSC::INVALID; datapointer = 0; buffercount = 1; } @@ -18,8 +18,11 @@ DTSC::Stream::Stream(){ /// Initializes a DTSC::Stream with a minimum of rbuffers packet buffers. /// The actual buffer count may not at all times be the requested amount. DTSC::Stream::Stream(unsigned int rbuffers){ + datapointertype = DTSC::INVALID; datapointer = 0; - if (rbuffers < 1){rbuffers = 1;} + if (rbuffers < 1){ + rbuffers = 1; + } buffercount = rbuffers; } @@ -39,15 +42,19 @@ bool DTSC::Stream::parsePacket(std::string & buffer){ if (buffer.length() > 8){ if (memcmp(buffer.c_str(), DTSC::Magic_Header, 4) == 0){ len = ntohl(((uint32_t *)buffer.c_str())[1]); - if (buffer.length() < len+8){return false;} + if (buffer.length() < len + 8){ + return false; + } unsigned int i = 0; metadata = JSON::fromDTMI((unsigned char*)buffer.c_str() + 8, len, i); - buffer.erase(0, len+8); + buffer.erase(0, len + 8); return false; } if (memcmp(buffer.c_str(), DTSC::Magic_Packet, 4) == 0){ len = ntohl(((uint32_t *)buffer.c_str())[1]); - if (buffer.length() < len+8){return false;} + if (buffer.length() < len + 8){ + return false; + } buffers.push_front(JSON::Value()); unsigned int i = 0; buffers.front() = JSON::fromDTMI((unsigned char*)buffer.c_str() + 8, len, i); @@ -59,23 +66,33 @@ bool DTSC::Stream::parsePacket(std::string & buffer){ } if (buffers.front().isMember("datatype")){ std::string tmp = buffers.front()["datatype"].asString(); - if (tmp == "video"){datapointertype = VIDEO;} - if (tmp == "audio"){datapointertype = AUDIO;} - if (tmp == "meta"){datapointertype = META;} - if (tmp == "pause_marker"){datapointertype = PAUSEMARK;} + if (tmp == "video"){ + datapointertype = VIDEO; + } + if (tmp == "audio"){ + datapointertype = AUDIO; + } + if (tmp == "meta"){ + datapointertype = META; + } + if (tmp == "pause_marker"){ + datapointertype = PAUSEMARK; + } + } + buffer.erase(0, len + 8); + while (buffers.size() > buffercount){ + buffers.pop_back(); } - buffer.erase(0, len+8); - while (buffers.size() > buffercount){buffers.pop_back();} advanceRings(); syncing = false; return true; } - #if DEBUG >= 2 +#if DEBUG >= 2 if (!syncing){ std::cerr << "Error: Invalid DTMI data detected - re-syncing" << std::endl; syncing = true; } - #endif +#endif size_t magic_search = buffer.find(Magic_Packet); if (magic_search == std::string::npos){ buffer.clear(); @@ -97,18 +114,22 @@ bool DTSC::Stream::parsePacket(Socket::Buffer & buffer){ std::string header_bytes = buffer.copy(8); if (memcmp(header_bytes.c_str(), DTSC::Magic_Header, 4) == 0){ len = ntohl(((uint32_t *)header_bytes.c_str())[1]); - if (!buffer.available(len+8)){return false;} + if ( !buffer.available(len + 8)){ + return false; + } unsigned int i = 0; - std::string wholepacket = buffer.remove(len+8); + std::string wholepacket = buffer.remove(len + 8); metadata = JSON::fromDTMI((unsigned char*)wholepacket.c_str() + 8, len, i); return false; } if (memcmp(header_bytes.c_str(), DTSC::Magic_Packet, 4) == 0){ len = ntohl(((uint32_t *)header_bytes.c_str())[1]); - if (!buffer.available(len+8)){return false;} + if ( !buffer.available(len + 8)){ + return false; + } buffers.push_front(JSON::Value()); unsigned int i = 0; - std::string wholepacket = buffer.remove(len+8); + std::string wholepacket = buffer.remove(len + 8); buffers.front() = JSON::fromDTMI((unsigned char*)wholepacket.c_str() + 8, len, i); datapointertype = INVALID; if (buffers.front().isMember("data")){ @@ -118,22 +139,32 @@ bool DTSC::Stream::parsePacket(Socket::Buffer & buffer){ } if (buffers.front().isMember("datatype")){ std::string tmp = buffers.front()["datatype"].asString(); - if (tmp == "video"){datapointertype = VIDEO;} - if (tmp == "audio"){datapointertype = AUDIO;} - if (tmp == "meta"){datapointertype = META;} - if (tmp == "pause_marker"){datapointertype = PAUSEMARK;} + if (tmp == "video"){ + datapointertype = VIDEO; + } + if (tmp == "audio"){ + datapointertype = AUDIO; + } + if (tmp == "meta"){ + datapointertype = META; + } + if (tmp == "pause_marker"){ + datapointertype = PAUSEMARK; + } + } + while (buffers.size() > buffercount){ + buffers.pop_back(); } - while (buffers.size() > buffercount){buffers.pop_back();} advanceRings(); syncing = false; return true; } - #if DEBUG >= 2 +#if DEBUG >= 2 if (!syncing){ std::cerr << "Error: Invalid DTMI data detected - syncing" << std::endl; syncing = true; } - #endif +#endif buffer.get().clear(); } return false; @@ -185,19 +216,30 @@ void DTSC::Stream::advanceRings(){ std::deque::iterator dit; std::set::iterator sit; for (sit = rings.begin(); sit != rings.end(); sit++){ - (*sit)->b++; - if ((*sit)->waiting){(*sit)->waiting = false; (*sit)->b = 0;} - if ((*sit)->starved || ((*sit)->b >= buffers.size())){(*sit)->starved = true; (*sit)->b = 0;} + ( *sit)->b++; + if (( *sit)->waiting){ + ( *sit)->waiting = false; + ( *sit)->b = 0; + } + if (( *sit)->starved || (( *sit)->b >= buffers.size())){ + ( *sit)->starved = true; + ( *sit)->b = 0; + } } for (dit = keyframes.begin(); dit != keyframes.end(); dit++){ dit->b++; - if (dit->b >= buffers.size()){keyframes.erase(dit); break;} + if (dit->b >= buffers.size()){ + keyframes.erase(dit); + break; + } } if ((lastType() == VIDEO) && (buffers.front().isMember("keyframe"))){ keyframes.push_front(DTSC::Ring(0)); } //increase buffer size if no keyframes available - if ((buffercount > 1) && (keyframes.size() < 1)){buffercount++;} + if ((buffercount > 1) && (keyframes.size() < 1)){ + buffercount++; + } } /// Constructs a new Ring, at the given buffer position. @@ -235,7 +277,9 @@ void DTSC::Stream::dropRing(DTSC::Ring * ptr){ /// Drops all Ring classes that have been given out. DTSC::Stream::~Stream(){ std::set::iterator sit; - for (sit = rings.begin(); sit != rings.end(); sit++){delete (*sit);} + for (sit = rings.begin(); sit != rings.end(); sit++){ + delete ( *sit); + } } /// Open a filename for DTSC reading/writing. @@ -247,12 +291,12 @@ DTSC::File::File(std::string filename, bool create){ fseek(F, 0, SEEK_SET); fwrite(DTSC::Magic_Header, 4, 1, F); memset(buffer, 0, 4); - fwrite(buffer, 4, 1, F);//write 4 zero-bytes + fwrite(buffer, 4, 1, F); //write 4 zero-bytes headerSize = 0; }else{ F = fopen(filename.c_str(), "r+b"); } - if (!F){ + if ( !F){ fprintf(stderr, "Could not open file %s\n", filename.c_str()); return; } @@ -262,15 +306,15 @@ DTSC::File::File(std::string filename, bool create){ if (fread(buffer, 4, 1, F) != 1){ fseek(F, 4, SEEK_SET); memset(buffer, 0, 4); - fwrite(buffer, 4, 1, F);//write 4 zero-bytes + fwrite(buffer, 4, 1, F); //write 4 zero-bytes }else{ uint32_t * ubuffer = (uint32_t *)buffer; headerSize = ntohl(ubuffer[0]); } readHeader(0); - fseek(F, 8+headerSize, SEEK_SET); + fseek(F, 8 + headerSize, SEEK_SET); currframe = 1; - frames[1] = 8+headerSize; + frames[1] = 8 + headerSize; msframes[1] = 0; } @@ -289,7 +333,7 @@ bool DTSC::File::writeHeader(std::string & header, bool force){ headerSize = header.size(); fseek(F, 8, SEEK_SET); int ret = fwrite(header.c_str(), headerSize, 1, F); - fseek(F, 8+headerSize, SEEK_SET); + fseek(F, 8 + headerSize, SEEK_SET); return (ret == 1); } @@ -299,13 +343,19 @@ long long int DTSC::File::addHeader(std::string & header){ fseek(F, 0, SEEK_END); long long int writePos = ftell(F); int hSize = htonl(header.size()); - int ret = fwrite(DTSC::Magic_Header, 4, 1, F);//write header - if (ret != 1){return 0;} - ret = fwrite((void*)(&hSize), 4, 1, F);//write size - if (ret != 1){return 0;} - ret = fwrite(header.c_str(), header.size(), 1, F);//write contents - if (ret != 1){return 0;} - return writePos;//return position written at + int ret = fwrite(DTSC::Magic_Header, 4, 1, F); //write header + if (ret != 1){ + return 0; + } + ret = fwrite((void*)( &hSize), 4, 1, F); //write size + if (ret != 1){ + return 0; + } + ret = fwrite(header.c_str(), header.size(), 1, F); //write contents + if (ret != 1){ + return 0; + } + return writePos; //return position written at } /// Reads the header at the given file position. @@ -315,9 +365,9 @@ void DTSC::File::readHeader(int pos){ fseek(F, pos, SEEK_SET); if (fread(buffer, 4, 1, F) != 1){ if (feof(F)){ - #if DEBUG >= 4 +#if DEBUG >= 4 fprintf(stderr, "End of file reached (H%i)\n", pos); - #endif +#endif }else{ fprintf(stderr, "Could not read header (H%i)\n", pos); } @@ -355,13 +405,13 @@ void DTSC::File::readHeader(int pos){ if (metadata.isMember("keytime")){ msframes.clear(); for (int i = 0; i < metadata["keytime"].size(); ++i){ - msframes[i+1] = metadata["keytime"][i].asInt(); + msframes[i + 1] = metadata["keytime"][i].asInt(); } } if (metadata.isMember("keybpos")){ frames.clear(); for (int i = 0; i < metadata["keybpos"].size(); ++i){ - frames[i+1] = metadata["keybpos"][i].asInt(); + frames[i + 1] = metadata["keybpos"][i].asInt(); } } } @@ -373,9 +423,9 @@ void DTSC::File::seekNext(){ lastreadpos = ftell(F); if (fread(buffer, 4, 1, F) != 1){ if (feof(F)){ - #if DEBUG >= 4 +#if DEBUG >= 4 fprintf(stderr, "End of file reached.\n"); - #endif +#endif }else{ fprintf(stderr, "Could not read header\n"); } @@ -409,13 +459,13 @@ void DTSC::File::seekNext(){ if (frames[currframe] != lastreadpos){ currframe++; currtime = jsonbuffer["time"].asInt(); - #if DEBUG >= 6 +#if DEBUG >= 6 if (frames[currframe] != lastreadpos){ std::cerr << "Found a new frame " << currframe << " @ " << lastreadpos << "b/" << currtime << "ms" << std::endl; - }else{ + } else{ std::cerr << "Passing frame " << currframe << " @ " << lastreadpos << "b/" << currtime << "ms" << std::endl; } - #endif +#endif frames[currframe] = lastreadpos; msframes[currframe] = currtime; } @@ -423,36 +473,47 @@ void DTSC::File::seekNext(){ } /// Returns the byte positon of the start of the last packet that was read. -long long int DTSC::File::getLastReadPos(){return lastreadpos;} +long long int DTSC::File::getLastReadPos(){ + return lastreadpos; +} /// Returns the internal buffer of the last read packet in raw binary format. -std::string & DTSC::File::getPacket(){return strbuffer;} +std::string & DTSC::File::getPacket(){ + return strbuffer; +} /// Returns the internal buffer of the last read packet in JSON format. -JSON::Value & DTSC::File::getJSON(){return jsonbuffer;} +JSON::Value & DTSC::File::getJSON(){ + return jsonbuffer; +} /// Attempts to seek to the given frame number within the file. /// Returns true if successful, false otherwise. bool DTSC::File::seek_frame(int frameno){ if (frames.count(frameno) > 0){ if (fseek(F, frames[frameno], SEEK_SET) == 0){ - #if DEBUG >= 4 +#if DEBUG >= 4 std::cerr << "Seek direct from " << currframe << " @ " << frames[currframe] << " to " << frameno << " @ " << frames[frameno] << std::endl; - #endif +#endif currframe = frameno; return true; } }else{ for (int i = frameno; i >= 1; --i){ - if (frames.count(i) > 0){currframe = i; break;} + if (frames.count(i) > 0){ + currframe = i; + break; + } } if (fseek(F, frames[currframe], SEEK_SET) == 0){ - #if DEBUG >= 4 +#if DEBUG >= 4 std::cerr << "Seeking from frame " << currframe << " @ " << frames[currframe] << " to " << frameno << std::endl; - #endif +#endif while (currframe < frameno){ seekNext(); - if (jsonbuffer.isNull()){return false;} + if (jsonbuffer.isNull()){ + return false; + } } seek_frame(frameno); return true; @@ -468,16 +529,23 @@ bool DTSC::File::seek_time(int ms){ currtime = 0; currframe = 1; for (it = msframes.begin(); it != msframes.end(); ++it){ - if (it->second > ms){break;} - if (it->second > currtime){currtime = it->second; currframe = it->first;} + if (it->second > ms){ + break; + } + if (it->second > currtime){ + currtime = it->second; + currframe = it->first; + } } if (fseek(F, frames[currframe], SEEK_SET) == 0){ - #if DEBUG >= 4 +#if DEBUG >= 4 std::cerr << "Seeking from frame " << currframe << " @ " << msframes[currframe] << "ms to " << ms << "ms" << std::endl; - #endif +#endif while (currtime < ms){ seekNext(); - if (jsonbuffer.isNull()){return false;} + if (jsonbuffer.isNull()){ + return false; + } } if (currtime > ms){ return seek_frame(currframe - 1); diff --git a/lib/dtsc.h b/lib/dtsc.h index 1bced5d1..f3dd687d 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -12,8 +12,6 @@ #include "json.h" #include "socket.h" - - /// Holds all DDVTECH Stream Container classes and parsers. ///length (int, length in seconds, if available) ///video: @@ -50,10 +48,10 @@ /// - nalu (int, if set, is a nalu) /// - nalu_end (int, if set, is a end-of-sequence) /// - offset (int, unsigned version of signed int! Holds the ms offset between timestamp and proper display time for B-frames) -namespace DTSC{ +namespace DTSC { /// This enum holds all possible datatypes for DTSC packets. - enum datatype { + enum datatype{ AUDIO, ///< Stream Audio data VIDEO, ///< Stream Video data META, ///< Stream Metadata @@ -91,12 +89,12 @@ namespace DTSC{ FILE * F; unsigned long headerSize; char buffer[4]; - };//FileWriter - + }; + //FileWriter /// A part from the DTSC::Stream ringbuffer. /// Holds information about a buffer that will stay consistent - class Ring { + class Ring{ public: Ring(unsigned int v); volatile unsigned int b; ///< Holds current number of buffer. May and is intended to change unexpectedly! @@ -107,7 +105,7 @@ namespace DTSC{ /// 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 { + class Stream{ public: Stream(); ~Stream(); @@ -125,7 +123,7 @@ namespace DTSC{ Ring * getRing(); unsigned int getTime(); void dropRing(Ring * ptr); - private: + private: std::deque buffers; std::set rings; std::deque keyframes; @@ -134,4 +132,4 @@ namespace DTSC{ datatype datapointertype; unsigned int buffercount; }; -}; +} diff --git a/lib/filesystem.cpp b/lib/filesystem.cpp index 06f5cbb7..791d529a 100644 --- a/lib/filesystem.cpp +++ b/lib/filesystem.cpp @@ -1,215 +1,265 @@ #include "filesystem.h" - -Filesystem::Directory::Directory( std::string PathName, std::string BasePath ) { +Filesystem::Directory::Directory(std::string PathName, std::string BasePath){ MyBase = BasePath; - if( PathName[0] == '/' ) { PathName.erase(0,1); } - if( BasePath[BasePath.size()-1] != '/' ) { BasePath += "/"; } + if (PathName[0] == '/'){ + PathName.erase(0, 1); + } + if (BasePath[BasePath.size() - 1] != '/'){ + BasePath += "/"; + } MyPath = PathName; - FillEntries( ); + FillEntries(); } -Filesystem::Directory::~Directory( ) { } +Filesystem::Directory::~Directory(){ +} -void Filesystem::Directory::FillEntries( ) { - fprintf( stderr, "Filling Entries of %s:\n", (MyBase + MyPath).c_str() ); +void Filesystem::Directory::FillEntries(){ + fprintf(stderr, "Filling Entries of %s:\n", (MyBase + MyPath).c_str()); ValidDir = true; struct stat StatBuf; Entries.clear(); - DIR * Dirp = opendir( (MyBase + MyPath).c_str() ); - if( !Dirp ) { + DIR * Dirp = opendir((MyBase + MyPath).c_str()); + if ( !Dirp){ ValidDir = false; - } else { + }else{ dirent * entry; - while( entry = readdir( Dirp ) ) { - if( stat((MyBase + MyPath + "/" + entry->d_name).c_str(), &StatBuf) == -1 ) { - fprintf( stderr, "\tSkipping %s\n\t\tReason: %s\n", entry->d_name, strerror(errno) ); + while ((entry = readdir(Dirp))){ + if (stat((MyBase + MyPath + "/" + entry->d_name).c_str(), &StatBuf) == -1){ + fprintf(stderr, "\tSkipping %s\n\t\tReason: %s\n", entry->d_name, strerror(errno)); continue; } ///Convert stat to string - Entries[ std::string( entry->d_name ) ] = StatBuf; + Entries[std::string(entry->d_name)] = StatBuf; } } - fprintf( stderr, "Valid dir: %d\n", ValidDir ); - fprintf( stderr, "#Entries: %d\n", Entries.size() ); + fprintf(stderr, "Valid dir: %d\n", ValidDir); + fprintf(stderr, "#Entries: %d\n", Entries.size()); } -void Filesystem::Directory::Print( ) { - if( !ValidDir ) { - printf( "%s is not a valid directory\n", (MyBase + MyPath).c_str() ); +void Filesystem::Directory::Print(){ + if ( !ValidDir){ + printf("%s is not a valid directory\n", (MyBase + MyPath).c_str()); return; } - printf( "%s:\n", (MyBase + MyPath).c_str() ); - for( std::map::iterator it = Entries.begin(); it != Entries.end(); it++ ) { - printf( "\t%s\n", (*it).first.c_str() ); + printf("%s:\n", (MyBase + MyPath).c_str()); + for (std::map::iterator it = Entries.begin(); it != Entries.end(); it++){ + printf("\t%s\n", ( *it).first.c_str()); } - printf( "\n" ); + printf("\n"); } -bool Filesystem::Directory::IsDir( ) { +bool Filesystem::Directory::IsDir(){ return ValidDir; } -std::string Filesystem::Directory::PWD( ) { +std::string Filesystem::Directory::PWD(){ return "/" + MyPath; } -std::string Filesystem::Directory::LIST( std::vector ActiveStreams ) { - FillEntries( ); +std::string Filesystem::Directory::LIST(std::vector 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 - + 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::iterator it = Entries.begin(); it != Entries.end(); it++ ) { - - bool Active = ( std::find( ActiveStreams.begin(), ActiveStreams.end(), (*it).first ) != ActiveStreams.end() ); - fprintf( stderr, "%s active?: %d\n", (*it).first.c_str(), Active ); - fprintf( stderr, "\tMyPath: %s\n\tVisible: %d\n", MyPath.c_str(), MyVisible[MyPath] ); - fprintf( stderr, "\t\tBitmask S_ACTIVE: %d\n\t\tBitmask S_INACTIVE: %d\n", MyVisible[MyPath] & S_ACTIVE, MyVisible[MyPath] & S_INACTIVE ); - if( ( Active && ( MyVisible[MyPath] & S_ACTIVE ) ) || ( (!Active) && ( MyVisible[MyPath] & S_INACTIVE ) ) || ( ((*it).second.st_mode / 010000 ) == 4 ) ) { - if( ((*it).second.st_mode / 010000) == 4 ) { Converter << 'd'; } 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 << '-'; } + if (MyLoc[MyLoc.size() - 1] != '/'){ + MyLoc += "/"; + } + + for (std::map::iterator it = Entries.begin(); it != Entries.end(); it++){ + + bool Active = (std::find(ActiveStreams.begin(), ActiveStreams.end(), ( *it).first) != ActiveStreams.end()); + fprintf(stderr, "%s active?: %d\n", ( *it).first.c_str(), Active); + fprintf(stderr, "\tMyPath: %s\n\tVisible: %d\n", MyPath.c_str(), MyVisible[MyPath]); + fprintf(stderr, "\t\tBitmask S_ACTIVE: %d\n\t\tBitmask S_INACTIVE: %d\n", MyVisible[MyPath] & S_ACTIVE, MyVisible[MyPath] & S_INACTIVE); + if ((Active && (MyVisible[MyPath] & S_ACTIVE)) || (( !Active) && (MyVisible[MyPath] & S_INACTIVE)) || ((( *it).second.st_mode / 010000) == 4)){ + if ((( *it).second.st_mode / 010000) == 4){ + Converter << 'd'; + }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 << ( *it).second.st_nlink; Converter << ' '; - if( (pwd = getpwuid((*it).second.st_uid)) ) { + if ((pwd = getpwuid(( *it).second.st_uid))){ Converter << pwd->pw_name; - } else { - Converter << (*it).second.st_uid; + }else{ + Converter << ( *it).second.st_uid; } Converter << ' '; - if( (grp = getgrgid((*it).second.st_gid) ) ) { + if ((grp = getgrgid(( *it).second.st_gid))){ Converter << grp->gr_name; - } else { - Converter << (*it).second.st_gid; + }else{ + Converter << ( *it).second.st_gid; } Converter << ' '; - Converter << (*it).second.st_size; + Converter << ( *it).second.st_size; Converter << ' '; - tm = localtime(&((*it).second.st_mtime)); + tm = localtime( &(( *it).second.st_mtime)); strftime(datestring, sizeof(datestring), "%b %d %H:%M", tm); Converter << datestring; Converter << ' '; - Converter << (*it).first; + Converter << ( *it).first; Converter << '\n'; } } return Converter.str(); } -bool Filesystem::Directory::CWD( std::string Path ) { - if( Path[0] == '/' ) { - Path.erase(0,1); +bool Filesystem::Directory::CWD(std::string Path){ + if (Path[0] == '/'){ + Path.erase(0, 1); MyPath = Path; - } else { - if( MyPath != "" ) { + }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] ); + printf("New Path: %s\n", MyPath.c_str()); + if (MyPermissions.find(MyPath) != MyPermissions.end()){ + printf("\tPermissions: %d\n", MyPermissions[MyPath]); } - return SimplifyPath( ); + return SimplifyPath(); } -bool Filesystem::Directory::CDUP( ) { - return CWD( ".." ); +bool Filesystem::Directory::CDUP(){ + return CWD(".."); } -std::string Filesystem::Directory::RETR( std::string Path ) { +std::string Filesystem::Directory::RETR(std::string Path){ std::string Result; std::string FileName; - if( Path[0] == '/' ) { - Path.erase(0,1); + if (Path[0] == '/'){ + Path.erase(0, 1); FileName = MyBase + Path; - } else { + }else{ FileName = MyBase + MyPath + "/" + Path; } std::ifstream File; - File.open( FileName.c_str() ); - while( File.good() ) { Result += File.get(); } + 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 ) ) { +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); + if (Path[0] == '/'){ + Path.erase(0, 1); FileName = MyBase + Path; - } else { + }else{ FileName = MyBase + MyPath + "/" + Path; } std::ofstream File; - File.open( FileName.c_str() ); + File.open(FileName.c_str()); File << Data; File.close(); } } -bool Filesystem::Directory::SimplifyPath( ) { +bool Filesystem::Directory::SimplifyPath(){ MyPath += "/"; - fprintf( stderr, "MyPath: %s\n", MyPath.c_str() ); + fprintf(stderr, "MyPath: %s\n", MyPath.c_str()); std::vector TempPath; std::string TempString; - for( std::string::iterator it = MyPath.begin(); it != MyPath.end(); it ++ ) { - if( (*it) == '/' ) { - if( TempString == ".." ) { - if( !TempPath.size() ) { + 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 ); + TempPath.erase((TempPath.end() - 1)); + }else if (TempString != "." && TempString != ""){ + TempPath.push_back(TempString); } TempString = ""; - } else { - TempString += (*it); + }else{ + TempString += ( *it); } } MyPath = ""; - for( std::vector::iterator it = TempPath.begin(); it != TempPath.end(); it++ ) { - MyPath += (*it); - if( it != ( TempPath.end() - 1 ) ) { MyPath += "/"; } + for (std::vector::iterator it = TempPath.begin(); it != TempPath.end(); it++){ + MyPath += ( *it); + if (it != (TempPath.end() - 1)){ + MyPath += "/"; + } } - if( MyVisible.find( MyPath ) == MyVisible.end() ) { + 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 ) ) { +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); + if (Path[0] == '/'){ + Path.erase(0, 1); FileName = MyBase + Path; - } else { + }else{ FileName = MyBase + MyPath + "/" + Path; } - if( std::remove( FileName.c_str() ) ) { - fprintf( stderr, "Removing file %s unsuccesfull\n", FileName.c_str() ); + if (std::remove(FileName.c_str())){ + fprintf(stderr, "Removing file %s unsuccesfull\n", FileName.c_str()); return false; } return true; @@ -217,39 +267,39 @@ bool Filesystem::Directory::DELE( std::string Path ) { return false; } -bool Filesystem::Directory::MKD( std::string Path ) { +bool Filesystem::Directory::MKD(std::string Path){ std::string FileName; - if( Path[0] == '/' ) { - Path.erase(0,1); + if (Path[0] == '/'){ + Path.erase(0, 1); FileName = MyBase + Path; - } else { + }else{ FileName = MyBase + MyPath + "/" + Path; } - if( mkdir( FileName.c_str(), S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH ) ) { - fprintf( stderr, "Creating directory %s unsuccesfull\n", FileName.c_str() ); + if (mkdir(FileName.c_str(), S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)){ + fprintf(stderr, "Creating directory %s unsuccesfull\n", FileName.c_str()); 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 ) ) { +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); + if (From[0] == '/'){ + From.erase(0, 1); FileFrom = MyBase + From; - } else { + }else{ FileFrom = MyBase + MyPath + "/" + From; } std::string FileTo; - if( To[0] == '/' ) { + if (To[0] == '/'){ FileTo = MyBase + To; - } else { + }else{ FileTo = MyBase + MyPath + "/" + To; } - if( std::rename( FileFrom.c_str(), FileTo.c_str() ) ) { - fprintf( stderr, "Renaming file %s to %s unsuccesfull\n", FileFrom.c_str(), FileTo.c_str() ); + if (std::rename(FileFrom.c_str(), FileTo.c_str())){ + fprintf(stderr, "Renaming file %s to %s unsuccesfull\n", FileFrom.c_str(), FileTo.c_str()); return false; } return true; @@ -257,17 +307,17 @@ bool Filesystem::Directory::Rename( std::string From, std::string To ) { return false; } -void Filesystem::Directory::SetPermissions( std::string Path, char Permissions ) { +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 ) ) { +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 ) { +void Filesystem::Directory::SetVisibility(std::string Pathname, char Visible){ MyVisible[Pathname] = Visible; } diff --git a/lib/filesystem.h b/lib/filesystem.h index f20e91a0..204449d3 100644 --- a/lib/filesystem.h +++ b/lib/filesystem.h @@ -21,49 +21,47 @@ #include 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_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, + + enum DIR_Show{ + S_NONE = 0x00, S_ACTIVE = 0x01, S_INACTIVE = 0x02, S_ALL = 0x03, }; - - class Directory { + + class Directory{ public: - Directory( std::string PathName = "", std::string BasePath = "."); - ~Directory( ); - void Print( ); - bool IsDir( ); - std::string PWD( ); - std::string LIST( std::vector ActiveStreams = std::vector() ); - 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 ); + Directory(std::string PathName = "", std::string BasePath = "."); + ~Directory(); + void Print(); + bool IsDir(); + std::string PWD(); + std::string LIST(std::vector ActiveStreams = std::vector()); + 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( ); + 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 + std::map Entries; + std::map MyPermissions; + std::map MyVisible; + }; +//Directory Class +}//Filesystem namespace diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index 20ed3d36..e8204076 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -1,9 +1,9 @@ /// \file flv_tag.cpp /// Holds all code for the FLV namespace. -#include "flv_tag.h" #include "amf.h" #include "rtmpchunks.h" +#include "flv_tag.h" #include //for Tag::FileLoader #include //for Tag::FileLoader #include //for Tag::FileLoader @@ -38,7 +38,7 @@ bool FLV::check_header(char * header){ if (header[11] != 0) return false; if (header[12] != 0) return false; return true; -}//FLV::check_header +} //FLV::check_header /// Checks the first 3 bytes for the string "FLV". Implementing a basic FLV header check, /// returning true if it is, false if not. @@ -47,7 +47,7 @@ bool FLV::is_header(char * header){ if (header[1] != 'L') return false; if (header[2] != 'V') return false; return true; -}//FLV::is_header +} //FLV::is_header /// True if this media type requires init data. /// Will always return false if the tag type is not 0x08 or 0x09. @@ -57,21 +57,35 @@ bool FLV::Tag::needsInitData(){ switch (data[0]){ case 0x09: switch (data[11] & 0x0F){ - case 2: return true; break;//H263 requires init data - case 7: return true; break;//AVC requires init data - default: return false; break;//other formats do not + case 2: + return true; + break; //H263 requires init data + case 7: + return true; + break; //AVC requires init data + default: + return false; + break; //other formats do not } break; case 0x08: switch (data[11] & 0xF0){ - case 0x20: return false; break;//MP3 does not...? Unsure. - case 0xA0: return true; break;//AAC requires init data - case 0xE0: return false; break;//MP38kHz does not...? - default: return false; break;//other formats do not + case 0x20: + return false; + break; //MP3 does not...? Unsure. + case 0xA0: + return true; + break; //AAC requires init data + case 0xE0: + return false; + break; //MP38kHz does not...? + default: + return false; + break; //other formats do not } break; } - return false;//only audio/video can require init data + return false; //only audio/video can require init data } /// True if current tag is init data for this media type. @@ -79,11 +93,15 @@ bool FLV::Tag::isInitData(){ switch (data[0]){ case 0x09: switch (data[11] & 0xF0){ - case 0x50: return true; break; + case 0x50: + return true; + break; } if ((data[11] & 0x0F) == 7){ switch (data[12]){ - case 0: return true; break; + case 0: + return true; + break; } } break; @@ -106,71 +124,149 @@ std::string FLV::Tag::tagType(){ switch (data[0]){ case 0x09: switch (data[11] & 0x0F){ - case 1: R << "JPEG"; break; - case 2: R << "H263"; break; - case 3: R << "ScreenVideo1"; break; - case 4: R << "VP6"; break; - case 5: R << "VP6Alpha"; break; - case 6: R << "ScreenVideo2"; break; - case 7: R << "H264"; break; - default: R << "unknown"; break; + case 1: + R << "JPEG"; + break; + case 2: + R << "H263"; + break; + case 3: + R << "ScreenVideo1"; + break; + case 4: + R << "VP6"; + break; + case 5: + R << "VP6Alpha"; + break; + case 6: + R << "ScreenVideo2"; + break; + case 7: + R << "H264"; + break; + default: + R << "unknown"; + break; } - R << " video "; - switch (data[11] & 0xF0){ - case 0x10: R << "keyframe"; break; - case 0x20: R << "iframe"; break; - case 0x30: R << "disposableiframe"; break; - case 0x40: R << "generatedkeyframe"; break; - case 0x50: R << "videoinfo"; break; + R << " video "; + switch (data[11] & 0xF0){ + case 0x10: + R << "keyframe"; + break; + case 0x20: + R << "iframe"; + break; + case 0x30: + R << "disposableiframe"; + break; + case 0x40: + R << "generatedkeyframe"; + break; + case 0x50: + R << "videoinfo"; + break; } if ((data[11] & 0x0F) == 7){ switch (data[12]){ - case 0: R << " header"; break; - case 1: R << " NALU"; break; - case 2: R << " endofsequence"; break; + case 0: + R << " header"; + break; + case 1: + R << " NALU"; + break; + case 2: + R << " endofsequence"; + break; } } break; case 0x08: switch (data[11] & 0xF0){ - case 0x00: R << "linear PCM PE"; break; - case 0x10: R << "ADPCM"; break; - case 0x20: R << "MP3"; break; - case 0x30: R << "linear PCM LE"; break; - case 0x40: R << "Nelly16kHz"; break; - case 0x50: R << "Nelly8kHz"; break; - case 0x60: R << "Nelly"; break; - case 0x70: R << "G711A-law"; break; - case 0x80: R << "G711mu-law"; break; - case 0x90: R << "reserved"; break; - case 0xA0: R << "AAC"; break; - case 0xB0: R << "Speex"; break; - case 0xE0: R << "MP38kHz"; break; - case 0xF0: R << "DeviceSpecific"; break; - default: R << "unknown"; break; + case 0x00: + R << "linear PCM PE"; + break; + case 0x10: + R << "ADPCM"; + break; + case 0x20: + R << "MP3"; + break; + case 0x30: + R << "linear PCM LE"; + break; + case 0x40: + R << "Nelly16kHz"; + break; + case 0x50: + R << "Nelly8kHz"; + break; + case 0x60: + R << "Nelly"; + break; + case 0x70: + R << "G711A-law"; + break; + case 0x80: + R << "G711mu-law"; + break; + case 0x90: + R << "reserved"; + break; + case 0xA0: + R << "AAC"; + break; + case 0xB0: + R << "Speex"; + break; + case 0xE0: + R << "MP38kHz"; + break; + case 0xF0: + R << "DeviceSpecific"; + break; + default: + R << "unknown"; + break; } switch (data[11] & 0x0C){ - case 0x0: R << " 5.5kHz"; break; - case 0x4: R << " 11kHz"; break; - case 0x8: R << " 22kHz"; break; - case 0xC: R << " 44kHz"; break; + case 0x0: + R << " 5.5kHz"; + break; + case 0x4: + R << " 11kHz"; + break; + case 0x8: + R << " 22kHz"; + break; + case 0xC: + R << " 44kHz"; + break; } switch (data[11] & 0x02){ - case 0: R << " 8bit"; break; - case 2: R << " 16bit"; break; + case 0: + R << " 8bit"; + break; + case 2: + R << " 16bit"; + break; } switch (data[11] & 0x01){ - case 0: R << " mono"; break; - case 1: R << " stereo"; break; + case 0: + R << " mono"; + break; + case 1: + R << " stereo"; + break; } R << " audio"; if ((data[12] == 0) && ((data[11] & 0xF0) == 0xA0)){ R << " initdata"; } break; - case 0x12:{ + case 0x12: { R << "(meta)data: "; - AMF::Object metadata = AMF::parse((unsigned char*)data+11, len-15); + AMF::Object metadata = AMF::parse((unsigned char*)data + 11, len - 15); R << metadata.Print(); break; } @@ -179,12 +275,12 @@ std::string FLV::Tag::tagType(){ break; } return R.str(); -}//FLV::Tag::tagtype +} //FLV::Tag::tagtype /// Returns the 32-bit timestamp of this tag. unsigned int FLV::Tag::tagTime(){ return (data[4] << 16) + (data[5] << 8) + data[6] + (data[7] << 24); -}//tagTime getter +} //tagTime getter /// Sets the 32-bit timestamp of this tag. void FLV::Tag::tagTime(unsigned int T){ @@ -192,14 +288,19 @@ void FLV::Tag::tagTime(unsigned int T){ data[5] = ((T >> 8) & 0xFF); data[6] = (T & 0xFF); data[7] = ((T >> 24) & 0xFF); -}//tagTime setter +} //tagTime setter /// Constructor for a new, empty, tag. /// The buffer length is initialized to 0, and later automatically /// increased if neccesary. FLV::Tag::Tag(){ - len = 0; buf = 0; data = 0; isKeyframe = false; done = true; sofar = 0; -}//empty constructor + len = 0; + buf = 0; + data = 0; + isKeyframe = false; + done = true; + sofar = 0; +} //empty constructor /// Copy constructor, copies the contents of an existing tag. /// The buffer length is initialized to the actual size of the tag @@ -217,25 +318,29 @@ FLV::Tag::Tag(const Tag& O){ data = 0; } isKeyframe = O.isKeyframe; -}//copy constructor - +} //copy constructor /// Copy constructor from a RTMP chunk. /// Copies the contents of a RTMP chunk into a valid FLV tag. /// Exactly the same as making a chunk by through the default (empty) constructor /// and then calling FLV::Tag::ChunkLoader with the chunk as argument. FLV::Tag::Tag(const RTMPStream::Chunk& O){ - len = 0; buf = 0; data = 0; isKeyframe = false; done = true; sofar = 0; + len = 0; + buf = 0; + data = 0; + isKeyframe = false; + done = true; + sofar = 0; ChunkLoader(O); } /// Assignment operator - works exactly like the copy constructor. /// This operator checks for self-assignment. -FLV::Tag & FLV::Tag::operator= (const FLV::Tag& O){ - if (this != &O){//no self-assignment +FLV::Tag & FLV::Tag::operator=(const FLV::Tag& O){ + if (this != &O){ //no self-assignment len = O.len; if (len > 0){ - if (!data){ + if ( !data){ data = (char*)malloc(len); buf = len; }else{ @@ -249,7 +354,7 @@ FLV::Tag & FLV::Tag::operator= (const FLV::Tag& O){ isKeyframe = O.isKeyframe; } return *this; -}//assignment operator +} //assignment operator /// FLV loader function from DTSC. /// Takes the DTSC data and makes it into FLV. @@ -258,23 +363,27 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ case DTSC::VIDEO: len = S.lastData().length() + 16; if (S.metadata.isMember("video") && S.metadata["video"].isMember("codec")){ - if (S.metadata["video"]["codec"].asString() == "H264"){len += 4;} + if (S.metadata["video"]["codec"].asString() == "H264"){ + len += 4; + } } break; case DTSC::AUDIO: len = S.lastData().length() + 16; if (S.metadata.isMember("audio") && S.metadata["audio"].isMember("codec")){ - if (S.metadata["audio"]["codec"].asString() == "AAC"){len += 1;} + if (S.metadata["audio"]["codec"].asString() == "AAC"){ + len += 1; + } } break; case DTSC::META: len = S.lastData().length() + 15; break; - default://ignore all other types (there are currently no other types...) + default: //ignore all other types (there are currently no other types...) break; } if (len > 0){ - if (!data){ + if ( !data){ data = (char*)malloc(len); buf = len; }else{ @@ -286,60 +395,90 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ switch (S.lastType()){ case DTSC::VIDEO: if ((unsigned int)len == S.lastData().length() + 16){ - memcpy(data+12, S.lastData().c_str(), S.lastData().length()); + memcpy(data + 12, S.lastData().c_str(), S.lastData().length()); }else{ - memcpy(data+16, S.lastData().c_str(), S.lastData().length()); - if (S.getPacket().isMember("nalu")){data[12] = 1;}else{data[12] = 2;} + memcpy(data + 16, S.lastData().c_str(), S.lastData().length()); + if (S.getPacket().isMember("nalu")){ + data[12] = 1; + }else{ + data[12] = 2; + } int offset = S.getPacket()["offset"].asInt(); data[13] = (offset >> 16) & 0xFF; data[14] = (offset >> 8) & 0XFF; data[15] = offset & 0xFF; } data[11] = 0; - if (S.metadata["video"]["codec"].asString() == "H264"){data[11] += 7;} - if (S.metadata["video"]["codec"].asString() == "H263"){data[11] += 2;} - if (S.getPacket().isMember("keyframe")){data[11] += 0x10;} - if (S.getPacket().isMember("interframe")){data[11] += 0x20;} - if (S.getPacket().isMember("disposableframe")){data[11] += 0x30;} + if (S.metadata["video"]["codec"].asString() == "H264"){ + data[11] += 7; + } + if (S.metadata["video"]["codec"].asString() == "H263"){ + data[11] += 2; + } + if (S.getPacket().isMember("keyframe")){ + data[11] += 0x10; + } + if (S.getPacket().isMember("interframe")){ + data[11] += 0x20; + } + if (S.getPacket().isMember("disposableframe")){ + data[11] += 0x30; + } break; - case DTSC::AUDIO:{ + case DTSC::AUDIO: { if ((unsigned int)len == S.lastData().length() + 16){ - memcpy(data+12, S.lastData().c_str(), S.lastData().length()); + memcpy(data + 12, S.lastData().c_str(), S.lastData().length()); }else{ - memcpy(data+13, S.lastData().c_str(), S.lastData().length()); - data[12] = 1;//raw AAC data, not sequence header + memcpy(data + 13, S.lastData().c_str(), S.lastData().length()); + data[12] = 1; //raw AAC data, not sequence header } data[11] = 0; - if (S.metadata["audio"]["codec"].asString() == "AAC"){data[11] += 0xA0;} - if (S.metadata["audio"]["codec"].asString() == "MP3"){data[11] += 0x20;} + if (S.metadata["audio"]["codec"].asString() == "AAC"){ + data[11] += 0xA0; + } + if (S.metadata["audio"]["codec"].asString() == "MP3"){ + data[11] += 0x20; + } unsigned int datarate = S.metadata["audio"]["rate"].asInt(); if (datarate >= 44100){ data[11] += 0x0C; - }else if(datarate >= 22050){ + }else if (datarate >= 22050){ data[11] += 0x08; - }else if(datarate >= 11025){ + }else if (datarate >= 11025){ data[11] += 0x04; } - if (S.metadata["audio"]["size"].asInt() == 16){data[11] += 0x02;} - if (S.metadata["audio"]["channels"].asInt() > 1){data[11] += 0x01;} + if (S.metadata["audio"]["size"].asInt() == 16){ + data[11] += 0x02; + } + if (S.metadata["audio"]["channels"].asInt() > 1){ + data[11] += 0x01; + } break; } case DTSC::META: - memcpy(data+11, S.lastData().c_str(), S.lastData().length()); + memcpy(data + 11, S.lastData().c_str(), S.lastData().length()); + break; + default: break; - default: break; } } setLen(); switch (S.lastType()){ - case DTSC::VIDEO: data[0] = 0x09; break; - case DTSC::AUDIO: data[0] = 0x08; break; - case DTSC::META: data[0] = 0x12; break; - default: break; + case DTSC::VIDEO: + data[0] = 0x09; + break; + case DTSC::AUDIO: + data[0] = 0x08; + break; + case DTSC::META: + data[0] = 0x12; + break; + default: + break; } - data[1] = ((len-15) >> 16) & 0xFF; - data[2] = ((len-15) >> 8) & 0xFF; - data[3] = (len-15) & 0xFF; + data[1] = ((len - 15) >> 16) & 0xFF; + data[2] = ((len - 15) >> 8) & 0xFF; + data[3] = (len - 15) & 0xFF; data[8] = 0; data[9] = 0; data[10] = 0; @@ -351,13 +490,13 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ void FLV::Tag::setLen(){ int len4 = len - 4; int i = len; - data[--i] = (len4) & 0xFF; + data[ --i] = (len4) & 0xFF; len4 >>= 8; - data[--i] = (len4) & 0xFF; + data[ --i] = (len4) & 0xFF; len4 >>= 8; - data[--i] = (len4) & 0xFF; + data[ --i] = (len4) & 0xFF; len4 >>= 8; - data[--i] = (len4) & 0xFF; + data[ --i] = (len4) & 0xFF; } /// FLV Video init data loader function from DTSC. @@ -372,7 +511,7 @@ bool FLV::Tag::DTSCVideoInit(DTSC::Stream & S){ len = S.metadata["video"]["init"].asString().length() + 20; } if (len > 0){ - if (!data){ + if ( !data){ data = (char*)malloc(len); buf = len; }else{ @@ -381,18 +520,18 @@ bool FLV::Tag::DTSCVideoInit(DTSC::Stream & S){ buf = len; } } - memcpy(data+16, S.metadata["video"]["init"].asString().c_str(), len-20); - data[12] = 0;//H264 sequence header + memcpy(data + 16, S.metadata["video"]["init"].asString().c_str(), len - 20); + data[12] = 0; //H264 sequence header data[13] = 0; data[14] = 0; data[15] = 0; - data[11] = 0x17;//H264 keyframe (0x07 & 0x10) + data[11] = 0x17; //H264 keyframe (0x07 & 0x10) } setLen(); data[0] = 0x09; - data[1] = ((len-15) >> 16) & 0xFF; - data[2] = ((len-15) >> 8) & 0xFF; - data[3] = (len-15) & 0xFF; + data[1] = ((len - 15) >> 16) & 0xFF; + data[2] = ((len - 15) >> 8) & 0xFF; + data[3] = (len - 15) & 0xFF; data[8] = 0; data[9] = 0; data[10] = 0; @@ -413,7 +552,7 @@ bool FLV::Tag::DTSCAudioInit(DTSC::Stream & S){ len = S.metadata["audio"]["init"].asString().length() + 17; } if (len > 0){ - if (!data){ + if ( !data){ data = (char*)malloc(len); buf = len; }else{ @@ -422,27 +561,35 @@ bool FLV::Tag::DTSCAudioInit(DTSC::Stream & S){ buf = len; } } - memcpy(data+13, S.metadata["audio"]["init"].asString().c_str(), len-17); - data[12] = 0;//AAC sequence header + memcpy(data + 13, S.metadata["audio"]["init"].asString().c_str(), len - 17); + data[12] = 0; //AAC sequence header data[11] = 0; - if (S.metadata["audio"]["codec"].asString() == "AAC"){data[11] += 0xA0;} - if (S.metadata["audio"]["codec"].asString() == "MP3"){data[11] += 0x20;} + if (S.metadata["audio"]["codec"].asString() == "AAC"){ + data[11] += 0xA0; + } + if (S.metadata["audio"]["codec"].asString() == "MP3"){ + data[11] += 0x20; + } unsigned int datarate = S.metadata["audio"]["rate"].asInt(); if (datarate >= 44100){ data[11] += 0x0C; - }else if(datarate >= 22050){ + }else if (datarate >= 22050){ data[11] += 0x08; - }else if(datarate >= 11025){ + }else if (datarate >= 11025){ data[11] += 0x04; } - if (S.metadata["audio"]["size"].asInt() == 16){data[11] += 0x02;} - if (S.metadata["audio"]["channels"].asInt() > 1){data[11] += 0x01;} + if (S.metadata["audio"]["size"].asInt() == 16){ + data[11] += 0x02; + } + if (S.metadata["audio"]["channels"].asInt() > 1){ + data[11] += 0x01; + } } setLen(); data[0] = 0x08; - data[1] = ((len-15) >> 16) & 0xFF; - data[2] = ((len-15) >> 8) & 0xFF; - data[3] = (len-15) & 0xFF; + data[1] = ((len - 15) >> 16) & 0xFF; + data[2] = ((len - 15) >> 8) & 0xFF; + data[3] = (len - 15) & 0xFF; data[8] = 0; data[9] = 0; data[10] = 0; @@ -462,7 +609,7 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){ if (S.metadata["video"]["codec"].asString() == "?"){ S.metadata["video"]["codec"] = "H264"; } - + AMF::Object amfdata("root", AMF::AMF0_DDV_CONTAINER); amfdata.addContent(AMF::Object("", "onMetaData")); @@ -480,9 +627,9 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){ if (S.metadata.isMember("audio")){ total_byterate += S.metadata["audio"]["bps"].asInt(); } - for (int i = 0; i < S.metadata["length"].asInt(); ++i){//for each second in the file - keys.getContentP(0)->addContent(AMF::Object("", i * total_byterate, AMF::AMF0_NUMBER));//multiply by byterate for fake byte positions - keys.getContentP(1)->addContent(AMF::Object("", i, AMF::AMF0_NUMBER));//seconds + for (int i = 0; i < S.metadata["length"].asInt(); ++i){ //for each second in the file + keys.getContentP(0)->addContent(AMF::Object("", i * total_byterate, AMF::AMF0_NUMBER)); //multiply by byterate for fake byte positions + keys.getContentP(1)->addContent(AMF::Object("", i, AMF::AMF0_NUMBER)); //seconds } amfdata.getContentP(1)->addContent(keys); } @@ -536,7 +683,8 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){ int i = 0; if (S.metadata.isMember("audio")){ trinfo.addContent(AMF::Object("", AMF::AMF0_OBJECT)); - trinfo.getContentP(i)->addContent(AMF::Object("length", ((double)S.metadata["length"].asInt()) * ((double)S.metadata["audio"]["rate"].asInt()), AMF::AMF0_NUMBER)); + trinfo.getContentP(i)->addContent( + AMF::Object("length", ((double)S.metadata["length"].asInt()) * ((double)S.metadata["audio"]["rate"].asInt()), AMF::AMF0_NUMBER)); trinfo.getContentP(i)->addContent(AMF::Object("timescale", S.metadata["audio"]["rate"].asInt(), AMF::AMF0_NUMBER)); trinfo.getContentP(i)->addContent(AMF::Object("sampledescription", AMF::AMF0_STRICT_ARRAY)); if (S.metadata["audio"]["codec"].asString() == "AAC"){ @@ -549,7 +697,8 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){ } if (S.metadata.isMember("video")){ trinfo.addContent(AMF::Object("", AMF::AMF0_OBJECT)); - trinfo.getContentP(i)->addContent(AMF::Object("length", ((double)S.metadata["length"].asInt()) * ((double)S.metadata["video"]["fkps"].asInt() / 1000.0), AMF::AMF0_NUMBER)); + trinfo.getContentP(i)->addContent( + AMF::Object("length", ((double)S.metadata["length"].asInt()) * ((double)S.metadata["video"]["fkps"].asInt() / 1000.0), AMF::AMF0_NUMBER)); trinfo.getContentP(i)->addContent(AMF::Object("timescale", ((double)S.metadata["video"]["fkps"].asInt() / 1000.0), AMF::AMF0_NUMBER)); trinfo.getContentP(i)->addContent(AMF::Object("sampledescription", AMF::AMF0_STRICT_ARRAY)); if (S.metadata["video"]["codec"].asString() == "H264"){ @@ -568,7 +717,7 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){ std::string tmp = amfdata.Pack(); len = tmp.length() + 15; if (len > 0){ - if (!data){ + if ( !data){ data = (char*)malloc(len); buf = len; }else{ @@ -577,13 +726,13 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){ buf = len; } } - memcpy(data+11, tmp.c_str(), len-15); + memcpy(data + 11, tmp.c_str(), len - 15); } setLen(); data[0] = 0x12; - data[1] = ((len-15) >> 16) & 0xFF; - data[2] = ((len-15) >> 8) & 0xFF; - data[3] = (len-15) & 0xFF; + data[1] = ((len - 15) >> 16) & 0xFF; + data[2] = ((len - 15) >> 8) & 0xFF; + data[3] = (len - 15) & 0xFF; data[8] = 0; data[9] = 0; data[10] = 0; @@ -596,7 +745,7 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){ bool FLV::Tag::ChunkLoader(const RTMPStream::Chunk& O){ len = O.len + 15; if (len > 0){ - if (!data){ + if ( !data){ data = (char*)malloc(len); buf = len; }else{ @@ -605,7 +754,7 @@ bool FLV::Tag::ChunkLoader(const RTMPStream::Chunk& O){ buf = len; } } - memcpy(data+11, &(O.data[0]), O.len); + memcpy(data + 11, &(O.data[0]), O.len); } setLen(); data[0] = O.msg_type_id; @@ -616,7 +765,6 @@ bool FLV::Tag::ChunkLoader(const RTMPStream::Chunk& O){ return true; } - /// Helper function for FLV::MemLoader. /// This function will try to read count bytes from data buffer D into buffer. /// This function should be called repeatedly until true. @@ -629,16 +777,23 @@ bool FLV::Tag::ChunkLoader(const RTMPStream::Chunk& O){ /// \param P The current position in the data buffer. Will be updated to reflect new position. /// \return True if count bytes are read succesfully, false otherwise. bool FLV::Tag::MemReadUntil(char * buffer, unsigned int count, unsigned int & sofar, char * D, unsigned int S, unsigned int & P){ - if (sofar >= count){return true;} + if (sofar >= count){ + return true; + } int r = 0; - if (P+(count-sofar) > S){r = S-P;}else{r = count-sofar;} - memcpy(buffer+sofar, D+P, r); + if (P + (count - sofar) > S){ + r = S - P; + }else{ + r = count - sofar; + } + memcpy(buffer + sofar, D + P, r); P += r; sofar += r; - if (sofar >= count){return true;} + if (sofar >= count){ + return true; + } return false; -}//Tag::MemReadUntil - +} //Tag::MemReadUntil /// Try to load a tag from a data buffer in memory. /// This is a stateful function - if fed incorrect data, it will most likely never return true again! @@ -648,7 +803,10 @@ bool FLV::Tag::MemReadUntil(char * buffer, unsigned int count, unsigned int & so /// \param P The current position in the data buffer. Will be updated to reflect new position. /// \return True if a whole tag is succesfully read, false otherwise. bool FLV::Tag::MemLoader(char * D, unsigned int S, unsigned int & P){ - if (buf < 15){data = (char*)realloc(data, 15); buf = 15;} + if (buf < 15){ + data = (char*)realloc(data, 15); + buf = 15; + } if (done){ //read a header if (MemReadUntil(data, 11, sofar, D, S, P)){ @@ -658,14 +816,21 @@ bool FLV::Tag::MemLoader(char * D, unsigned int S, unsigned int & P){ if (FLV::check_header(data)){ sofar = 0; memcpy(FLV::Header, data, 13); - }else{FLV::Parse_Error = true; Error_Str = "Invalid header received."; return false;} + }else{ + FLV::Parse_Error = true; + Error_Str = "Invalid header received."; + return false; + } } }else{ //if a tag header, calculate length and read tag body len = data[3] + 15; len += (data[2] << 8); len += (data[1] << 16); - if (buf < len){data = (char*)realloc(data, len); buf = len;} + if (buf < len){ + data = (char*)realloc(data, len); + buf = len; + } if (data[0] > 0x12){ data[0] += 32; FLV::Parse_Error = true; @@ -681,15 +846,18 @@ bool FLV::Tag::MemLoader(char * D, unsigned int S, unsigned int & P){ //read tag body if (MemReadUntil(data, len, sofar, D, S, P)){ //calculate keyframeness, next time read header again, return true - if ((data[0] == 0x09) && (((data[11] & 0xf0) >> 4) == 1)){isKeyframe = true;}else{isKeyframe = false;} + if ((data[0] == 0x09) && (((data[11] & 0xf0) >> 4) == 1)){ + isKeyframe = true; + }else{ + isKeyframe = false; + } done = true; sofar = 0; return true; } } return false; -}//Tag::MemLoader - +} //Tag::MemLoader /// Helper function for FLV::FileLoader. /// This function will try to read count bytes from file f into buffer. @@ -700,12 +868,20 @@ bool FLV::Tag::MemLoader(char * D, unsigned int S, unsigned int & P){ /// \param f File to read from. /// \return True if count bytes are read succesfully, false otherwise. bool FLV::Tag::FileReadUntil(char * buffer, unsigned int count, unsigned int & sofar, FILE * f){ - if (sofar >= count){return true;} + if (sofar >= count){ + return true; + } int r = 0; - r = fread(buffer + sofar,1,count-sofar,f); - if (r < 0){FLV::Parse_Error = true; Error_Str = "File reading error."; return false;} + r = fread(buffer + sofar, 1, count - sofar, f); + if (r < 0){ + FLV::Parse_Error = true; + Error_Str = "File reading error."; + return false; + } sofar += r; - if (sofar >= count){return true;} + if (sofar >= count){ + return true; + } return false; } @@ -718,7 +894,10 @@ bool FLV::Tag::FileLoader(FILE * f){ int preflags = fcntl(fileno(f), F_GETFL, 0); int postflags = preflags | O_NONBLOCK; fcntl(fileno(f), F_SETFL, postflags); - if (buf < 15){data = (char*)realloc(data, 15); buf = 15;} + if (buf < 15){ + data = (char*)realloc(data, 15); + buf = 15; + } if (done){ //read a header @@ -729,14 +908,21 @@ bool FLV::Tag::FileLoader(FILE * f){ if (FLV::check_header(data)){ sofar = 0; memcpy(FLV::Header, data, 13); - }else{FLV::Parse_Error = true; Error_Str = "Invalid header received."; return false;} + }else{ + FLV::Parse_Error = true; + Error_Str = "Invalid header received."; + return false; + } } }else{ //if a tag header, calculate length and read tag body len = data[3] + 15; len += (data[2] << 8); len += (data[1] << 16); - if (buf < len){data = (char*)realloc(data, len); buf = len;} + if (buf < len){ + data = (char*)realloc(data, len); + buf = len; + } if (data[0] > 0x12){ data[0] += 32; FLV::Parse_Error = true; @@ -752,7 +938,11 @@ bool FLV::Tag::FileLoader(FILE * f){ //read tag body if (FileReadUntil(data, len, sofar, f)){ //calculate keyframeness, next time read header again, return true - if ((data[0] == 0x09) && (((data[11] & 0xf0) >> 4) == 1)){isKeyframe = true;}else{isKeyframe = false;} + if ((data[0] == 0x09) && (((data[11] & 0xf0) >> 4) == 1)){ + isKeyframe = true; + }else{ + isKeyframe = false; + } done = true; sofar = 0; fcntl(fileno(f), F_SETFL, preflags); @@ -761,28 +951,42 @@ bool FLV::Tag::FileLoader(FILE * f){ } fcntl(fileno(f), F_SETFL, preflags); return false; -}//FLV_GetPacket +} //FLV_GetPacket JSON::Value FLV::Tag::toJSON(JSON::Value & metadata){ JSON::Value pack_out; // Storage for outgoing metadata. if (data[0] == 0x12){ - AMF::Object meta_in = AMF::parse((unsigned char*)data+11, len-15); + AMF::Object meta_in = AMF::parse((unsigned char*)data + 11, len - 15); if (meta_in.getContentP(0) && (meta_in.getContentP(0)->StrValue() == "onMetaData") && meta_in.getContentP(1)){ AMF::Object * tmp = meta_in.getContentP(1); if (tmp->getContentP("videocodecid")){ switch ((unsigned int)tmp->getContentP("videocodecid")->NumValue()){ - case 2: metadata["video"]["codec"] = "H263"; break; - case 4: metadata["video"]["codec"] = "VP6"; break; - case 7: metadata["video"]["codec"] = "H264"; break; - default: metadata["video"]["codec"] = "?"; break; + case 2: + metadata["video"]["codec"] = "H263"; + break; + case 4: + metadata["video"]["codec"] = "VP6"; + break; + case 7: + metadata["video"]["codec"] = "H264"; + break; + default: + metadata["video"]["codec"] = "?"; + break; } } if (tmp->getContentP("audiocodecid")){ switch ((unsigned int)tmp->getContentP("audiocodecid")->NumValue()){ - case 2: metadata["audio"]["codec"] = "MP3"; break; - case 10: metadata["audio"]["codec"] = "AAC"; break; - default: metadata["audio"]["codec"] = "?"; break; + case 2: + metadata["audio"]["codec"] = "MP3"; + break; + case 10: + metadata["audio"]["codec"] = "AAC"; + break; + default: + metadata["audio"]["codec"] = "?"; + break; } } if (tmp->getContentP("width")){ @@ -792,13 +996,13 @@ JSON::Value FLV::Tag::toJSON(JSON::Value & metadata){ metadata["video"]["height"] = (long long int)tmp->getContentP("height")->NumValue(); } if (tmp->getContentP("framerate")){ - metadata["video"]["fpks"] = (long long int)tmp->getContentP("framerate")->NumValue()*1000; + metadata["video"]["fpks"] = (long long int)tmp->getContentP("framerate")->NumValue() * 1000; } if (tmp->getContentP("videodatarate")){ - metadata["video"]["bps"] = (long long int)(tmp->getContentP("videodatarate")->NumValue()*1024)/8; + metadata["video"]["bps"] = (long long int)(tmp->getContentP("videodatarate")->NumValue() * 1024) / 8; } if (tmp->getContentP("audiodatarate")){ - metadata["audio"]["bps"] = (long long int)(tmp->getContentP("audiodatarate")->NumValue()*1024)/8; + metadata["audio"]["bps"] = (long long int)(tmp->getContentP("audiodatarate")->NumValue() * 1024) / 8; } if (tmp->getContentP("audiosamplerate")){ metadata["audio"]["rate"] = (long long int)tmp->getContentP("audiosamplerate")->NumValue(); @@ -814,61 +1018,99 @@ JSON::Value FLV::Tag::toJSON(JSON::Value & metadata){ } } } - if (!metadata.isMember("length")){metadata["length"] = 0;} - if (metadata.isMember("video")){ - if (!metadata["video"].isMember("width")){metadata["video"]["width"] = 0;} - if (!metadata["video"].isMember("height")){metadata["video"]["height"] = 0;} - if (!metadata["video"].isMember("fpks")){metadata["video"]["fpks"] = 0;} - if (!metadata["video"].isMember("bps")){metadata["video"]["bps"] = 0;} - if (!metadata["video"].isMember("keyms")){metadata["video"]["keyms"] = 0;} - if (!metadata["video"].isMember("keyvar")){metadata["video"]["keyvar"] = 0;} + if ( !metadata.isMember("length")){ + metadata["length"] = 0; } - return pack_out;//empty + if (metadata.isMember("video")){ + if ( !metadata["video"].isMember("width")){ + metadata["video"]["width"] = 0; + } + if ( !metadata["video"].isMember("height")){ + metadata["video"]["height"] = 0; + } + if ( !metadata["video"].isMember("fpks")){ + metadata["video"]["fpks"] = 0; + } + if ( !metadata["video"].isMember("bps")){ + metadata["video"]["bps"] = 0; + } + if ( !metadata["video"].isMember("keyms")){ + metadata["video"]["keyms"] = 0; + } + if ( !metadata["video"].isMember("keyvar")){ + metadata["video"]["keyvar"] = 0; + } + } + return pack_out; //empty } if (data[0] == 0x08){ char audiodata = data[11]; if (needsInitData() && isInitData()){ if ((audiodata & 0xF0) == 0xA0){ - metadata["audio"]["init"] = std::string((char*)data+13, (size_t)len-17); + metadata["audio"]["init"] = std::string((char*)data + 13, (size_t)len - 17); }else{ - metadata["audio"]["init"] = std::string((char*)data+12, (size_t)len-16); + metadata["audio"]["init"] = std::string((char*)data + 12, (size_t)len - 16); } - return pack_out;//skip rest of parsing, get next tag. + return pack_out; //skip rest of parsing, get next tag. } pack_out["datatype"] = "audio"; pack_out["time"] = tagTime(); - if (!metadata["audio"].isMember("codec") || metadata["audio"]["size"].asString() == ""){ + if ( !metadata["audio"].isMember("codec") || metadata["audio"]["size"].asString() == ""){ switch (audiodata & 0xF0){ - case 0x20: metadata["audio"]["codec"] = "MP3"; break; - case 0xA0: metadata["audio"]["codec"] = "AAC"; break; + case 0x20: + metadata["audio"]["codec"] = "MP3"; + break; + case 0xA0: + metadata["audio"]["codec"] = "AAC"; + break; } } - if (!metadata["audio"].isMember("rate") || metadata["audio"]["rate"].asInt() < 1){ + if ( !metadata["audio"].isMember("rate") || metadata["audio"]["rate"].asInt() < 1){ switch (audiodata & 0x0C){ - case 0x0: metadata["audio"]["rate"] = 5512; break; - case 0x4: metadata["audio"]["rate"] = 11025; break; - case 0x8: metadata["audio"]["rate"] = 22050; break; - case 0xC: metadata["audio"]["rate"] = 44100; break; + case 0x0: + metadata["audio"]["rate"] = 5512; + break; + case 0x4: + metadata["audio"]["rate"] = 11025; + break; + case 0x8: + metadata["audio"]["rate"] = 22050; + break; + case 0xC: + metadata["audio"]["rate"] = 44100; + break; } } - if (!metadata["audio"].isMember("size") || metadata["audio"]["size"].asInt() < 1){ + if ( !metadata["audio"].isMember("size") || metadata["audio"]["size"].asInt() < 1){ switch (audiodata & 0x02){ - case 0x0: metadata["audio"]["size"] = 8; break; - case 0x2: metadata["audio"]["size"] = 16; break; + case 0x0: + metadata["audio"]["size"] = 8; + break; + case 0x2: + metadata["audio"]["size"] = 16; + break; } } - if (!metadata["audio"].isMember("channels") || metadata["audio"]["channels"].asInt() < 1){ + if ( !metadata["audio"].isMember("channels") || metadata["audio"]["channels"].asInt() < 1){ switch (audiodata & 0x01){ - case 0x0: metadata["audio"]["channels"] = 1; break; - case 0x1: metadata["audio"]["channels"] = 2; break; + case 0x0: + metadata["audio"]["channels"] = 1; + break; + case 0x1: + metadata["audio"]["channels"] = 2; + break; } } if ((audiodata & 0xF0) == 0xA0){ - if (len < 18){return JSON::Value();} - pack_out["data"] = std::string((char*)data+13, (size_t)len-17); + if (len < 18){ + return JSON::Value(); + } + pack_out["data"] = std::string((char*)data + 13, (size_t)len - 17); }else{ - if (len < 17){return JSON::Value();} - pack_out["data"] = std::string((char*)data+12, (size_t)len-16); + if (len < 17){ + return JSON::Value(); + } + pack_out["data"] = std::string((char*)data + 12, (size_t)len - 16); } return pack_out; } @@ -876,45 +1118,73 @@ JSON::Value FLV::Tag::toJSON(JSON::Value & metadata){ char videodata = data[11]; if (needsInitData() && isInitData()){ if ((videodata & 0x0F) == 7){ - if (len < 21){return JSON::Value();} - metadata["video"]["init"] = std::string((char*)data+16, (size_t)len-20); + if (len < 21){ + return JSON::Value(); + } + metadata["video"]["init"] = std::string((char*)data + 16, (size_t)len - 20); }else{ - if (len < 17){return JSON::Value();} - metadata["video"]["init"] = std::string((char*)data+12, (size_t)len-16); + if (len < 17){ + return JSON::Value(); + } + metadata["video"]["init"] = std::string((char*)data + 12, (size_t)len - 16); } - return pack_out;//skip rest of parsing, get next tag. + return pack_out; //skip rest of parsing, get next tag. } - if (!metadata["video"].isMember("codec") || metadata["video"]["codec"].asString() == ""){ + if ( !metadata["video"].isMember("codec") || metadata["video"]["codec"].asString() == ""){ switch (videodata & 0x0F){ - case 2: metadata["video"]["codec"] = "H263"; break; - case 4: metadata["video"]["codec"] = "VP6"; break; - case 7: metadata["video"]["codec"] = "H264"; break; + case 2: + metadata["video"]["codec"] = "H263"; + break; + case 4: + metadata["video"]["codec"] = "VP6"; + break; + case 7: + metadata["video"]["codec"] = "H264"; + break; } } pack_out["datatype"] = "video"; switch (videodata & 0xF0){ - case 0x10: pack_out["keyframe"] = 1; break; - case 0x20: pack_out["interframe"] = 1; break; - case 0x30: pack_out["disposableframe"] = 1; break; - case 0x40: pack_out["keyframe"] = 1; break; - case 0x50: return JSON::Value(); break;//the video info byte we just throw away - useless to us... + case 0x10: + pack_out["keyframe"] = 1; + break; + case 0x20: + pack_out["interframe"] = 1; + break; + case 0x30: + pack_out["disposableframe"] = 1; + break; + case 0x40: + pack_out["keyframe"] = 1; + break; + case 0x50: + return JSON::Value(); + break; //the video info byte we just throw away - useless to us... } pack_out["time"] = tagTime(); if ((videodata & 0x0F) == 7){ switch (data[12]){ - case 1: pack_out["nalu"] = 1; break; - case 2: pack_out["nalu_end"] = 1; break; + case 1: + pack_out["nalu"] = 1; + break; + case 2: + pack_out["nalu_end"] = 1; + break; } int offset = (data[13] << 16) + (data[14] << 8) + data[15]; offset = (offset << 8) >> 8; pack_out["offset"] = offset; - if (len < 21){return JSON::Value();} - pack_out["data"] = std::string((char*)data+16, (size_t)len-20); + if (len < 21){ + return JSON::Value(); + } + pack_out["data"] = std::string((char*)data + 16, (size_t)len - 20); }else{ - if (len < 17){return JSON::Value();} - pack_out["data"] = std::string((char*)data+12, (size_t)len-16); + if (len < 17){ + return JSON::Value(); + } + pack_out["data"] = std::string((char*)data + 12, (size_t)len - 16); } return pack_out; } - return pack_out;//should never get here -}//FLV::Tag::toJSON + return pack_out; //should never get here +} //FLV::Tag::toJSON diff --git a/lib/flv_tag.h b/lib/flv_tag.h index 8de48eb5..b4bb5198 100644 --- a/lib/flv_tag.h +++ b/lib/flv_tag.h @@ -7,10 +7,11 @@ #include "json.h" #include + //forward declaration of RTMPStream::Chunk to avoid circular dependencies. -namespace RTMPStream{ +namespace RTMPStream { class Chunk; -}; +} /// This namespace holds all FLV-parsing related functionality. namespace FLV { @@ -24,7 +25,7 @@ namespace FLV { 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 { + class Tag{ public: int len; ///< Actual length of tag. bool isKeyframe; ///< True if current tag is a video keyframe. @@ -36,7 +37,7 @@ namespace FLV { void tagTime(unsigned int T); ///< Sets the 32-bit timestamp of this tag. Tag(); ///< Constructor for a new, empty, tag. Tag(const Tag& O); ///< Copy constructor, copies the contents of an existing tag. - Tag & operator= (const Tag& O); ///< Assignment operator - works exactly like the copy constructor. + Tag & operator=(const Tag& O); ///< Assignment operator - works exactly like the copy constructor. Tag(const RTMPStream::Chunk& O); /// Credentials ) { +FTP::User::User(Socket::Connection NewConnection, std::map Credentials){ Conn = NewConnection; + MyPassivePort = 0; USER = ""; PASS = ""; MODE = MODE_STREAM; @@ -10,78 +11,150 @@ FTP::User::User( Socket::Connection NewConnection, std::map 1 ) { - if( Command[1] != ' ' ) { return 501; }//Syntax error in parameters or arguments. - if( Command[2] != 'N' ) { return 504; }//Command not implemented for that parameter. + 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. + 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. + return 504; //Command not implemented for that parameter. break; } } - return 200;//Command okay. + 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. + 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. + 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] ) { + 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; @@ -227,24 +349,30 @@ int FTP::User::ParseCommand( std::string Command ) { break; } default: { - return 504;//Command not implemented for that parameter. + return 504; //Command not implemented for that parameter. break; } } - return 200;//Command okay. + 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 + 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. + if ( !LoggedIn()){ + return 530; + } //Not logged in. Filesystem::Directory TmpDir = MyDir; - if( TmpDir.CWD( Command ) ) { - if( TmpDir.IsDir( ) ) { + if (TmpDir.CWD(Command)){ + if (TmpDir.IsDir()){ MyDir = TmpDir; return 250; } @@ -253,11 +381,15 @@ int FTP::User::ParseCommand( std::string Command ) { break; } case CMD_CDUP: { - if( !LoggedIn( ) ) { return 530; }//Not logged in. - if( Command != "" ) { return 501; }//Syntax error in parameters or arguments. + 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( ) ) { + if (TmpDir.CDUP()){ + if (TmpDir.IsDir()){ MyDir = TmpDir; return 250; } @@ -266,62 +398,98 @@ int FTP::User::ParseCommand( std::string Command ) { 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; } + 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; } + 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; } + 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. + if ( !LoggedIn()){ + return 530; + } //Not logged in. + if (Command == ""){ + return 501; + } //Syntax error in parameters or arguments. RNFR = Command; - return 350;//Awaiting further information + 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; } + 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. + return 502; //Command not implemented. break; } } } -bool FTP::User::LoggedIn( ) { - if( USER == "" || PASS == "" ) { return false; } - if( !AllCredentials.size() ) { +bool FTP::User::LoggedIn(){ + if (USER == "" || PASS == ""){ + return false; + } + if ( !AllCredentials.size()){ return true; } - if( ( AllCredentials.find( USER ) != AllCredentials.end() ) && AllCredentials[USER] == PASS ) { + if ((AllCredentials.find(USER) != AllCredentials.end()) && AllCredentials[USER] == PASS){ return true; } return false; } -std::string FTP::User::NumToMsg( int MsgNum ) { +std::string FTP::User::NumToMsg(int MsgNum){ std::string Result; - switch( MsgNum ) { + switch (MsgNum){ case 200: { Result = "200 Message okay.\n"; break; @@ -335,15 +503,15 @@ std::string FTP::User::NumToMsg( int MsgNum ) { break; } case 227: { - std::stringstream sstr; + 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; - } + break; + } case 229: { std::stringstream sstr; sstr << "229 Entering extended passive mode (|||"; @@ -360,12 +528,12 @@ std::string FTP::User::NumToMsg( int MsgNum ) { Result = "250 Requested file action okay, completed.\n"; break; } - case 2570: {//PWD - Result = "257 \"" + MyDir.PWD( ) + "\" selected as PWD\n"; + case 2570: { //PWD + Result = "257 \"" + MyDir.PWD() + "\" selected as PWD\n"; break; } - case 2571: {//MKD - Result = "257 \"" + MyDir.PWD( ) + "\" created\n"; + case 2571: { //MKD + Result = "257 \"" + MyDir.PWD() + "\" created\n"; break; } case 331: { diff --git a/lib/ftp.h b/lib/ftp.h index fb5b35d2..2c2e59bd 100644 --- a/lib/ftp.h +++ b/lib/ftp.h @@ -13,22 +13,23 @@ namespace FTP { static std::string FTPBasePath = "/tmp/mist/OnDemand/"; - - enum Mode { + + 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 { + }; + //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, @@ -51,18 +52,19 @@ namespace FTP { CMD_MKD, CMD_RNFR, CMD_RNTO, - };//FTP::Commands enumeration - - class User { + }; + //FTP::Commands enumeration + + class User{ public: - User( Socket::Connection NewConnection, std::map Credentials); - ~User( ); - int ParseCommand( std::string Command ); - bool LoggedIn( ); - std::string NumToMsg( int MsgNum ); + User(Socket::Connection NewConnection, std::map Credentials); + ~User(); + int ParseCommand(std::string Command); + bool LoggedIn(); + std::string NumToMsg(int MsgNum); Socket::Connection Conn; private: - std::map AllCredentials; + std::map AllCredentials; std::string USER; std::string PASS; Mode MODE; @@ -73,7 +75,8 @@ namespace FTP { int MyPassivePort; Filesystem::Directory MyDir; std::string RNFR; - std::vector< std::string > ActiveStreams; - };//FTP::User class - -};//FTP Namespace + std::vector ActiveStreams; + }; +//FTP::User class + +}//FTP Namespace diff --git a/lib/http_parser.cpp b/lib/http_parser.cpp index a3144b10..2f8ebb72 100644 --- a/lib/http_parser.cpp +++ b/lib/http_parser.cpp @@ -5,7 +5,9 @@ /// 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(){Clean();} +HTTP::Parser::Parser(){ + Clean(); +} /// Completely re-initializes the HTTP::Parser, leaving it ready for either reading or writing usage. void HTTP::Parser::Clean(){ @@ -27,11 +29,13 @@ void HTTP::Parser::Clean(){ std::string & HTTP::Parser::BuildRequest(){ /// \todo Include GET/POST variable parsing? std::map::iterator it; - if (protocol.size() < 5 || protocol.substr(0, 4) != "HTTP"){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"; + if (protocol.size() < 5 || protocol.substr(0, 4) != "HTTP"){ + 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; @@ -47,12 +51,14 @@ std::string & HTTP::Parser::BuildRequest(){ std::string & HTTP::Parser::BuildResponse(std::string code, std::string message){ /// \todo Include GET/POST variable parsing? std::map::iterator it; - if (protocol.size() < 5 || protocol.substr(0, 4) != "HTTP"){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"; + if (protocol.size() < 5 || protocol.substr(0, 4) != "HTTP"){ + 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"; } } } @@ -67,7 +73,11 @@ std::string & HTTP::Parser::BuildResponse(std::string code, std::string message) 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);} + 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. @@ -96,9 +106,13 @@ std::string HTTP::Parser::getUrl(){ } /// Returns header i, if set. -std::string HTTP::Parser::GetHeader(std::string i){return headers[i];} +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];} +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){ @@ -110,7 +124,7 @@ void HTTP::Parser::SetHeader(std::string i, std::string 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 + char val[23]; //ints are never bigger than 22 chars as decimal sprintf(val, "%i", v); headers[i] = val; } @@ -120,7 +134,7 @@ 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()){ + if ( !i.empty()){ vars[i] = v; } } @@ -131,7 +145,7 @@ void HTTP::Parser::SetVar(std::string i, std::string v){ /// \return True if a whole request or response was read, false otherwise. bool HTTP::Parser::Read(std::string & strbuf){ return parse(strbuf); -}//HTTPReader::Read +} //HTTPReader::Read #include /// Attempt to read a whole HTTP response or request from a data buffer. @@ -143,44 +157,54 @@ 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){ + 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()){ + if (f + 1 == HTTPbuffer.size()){ HTTPbuffer.clear(); }else{ - HTTPbuffer.erase(0, f+1); + HTTPbuffer.erase(0, f + 1); } - while (tmpA.find('\r') != std::string::npos){tmpA.erase(tmpA.find('\r'));} - if (!seenReq){ + while (tmpA.find('\r') != std::string::npos){ + tmpA.erase(tmpA.find('\r')); + } + if ( !seenReq){ seenReq = true; f = tmpA.find(' '); if (f != std::string::npos){ - method = tmpA.substr(0, f); tmpA.erase(0, f+1); + 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); + 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 + parseVars(url.substr(url.find('?') + 1)); //parse GET variables } - }else{seenReq = false;} - }else{seenReq = false;} + }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 (body.capacity() < length){ + body.reserve(length); + } } }else{ f = tmpA.find(':'); if (f == std::string::npos) continue; tmpB = tmpA.substr(0, f); - tmpC = tmpA.substr(f+1); + tmpC = tmpA.substr(f + 1); SetHeader(tmpB, tmpC); } } @@ -204,7 +228,7 @@ bool HTTP::Parser::parse(std::string & HTTPbuffer){ } } return false; //empty input -}//HTTPReader::parse +} //HTTPReader::parse /// Parses GET or POST-style variable data. /// Saves to internal variable structure using HTTP::Parser::SetVar. @@ -266,8 +290,12 @@ std::string HTTP::Parser::urlunescape(const std::string & in){ tmp += unhex(in[i]); } out += tmp; - } else { - if (in[i] == '+'){out += ' ';}else{out += in[i];} + }else{ + if (in[i] == '+'){ + out += ' '; + }else{ + out += in[i]; + } } } return out; @@ -276,15 +304,16 @@ std::string HTTP::Parser::urlunescape(const std::string & in){ /// 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 ); + 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=""; + std::string escaped = ""; int max = c.length(); - for(int i=0; i>4; - char dig2 = (dec&0x0F); - if (dig1<= 9) dig1+=48; - if (10<= dig1 && dig1<=15) dig1+=97-10; - if (dig2<= 9) dig2+=48; - if (10<= dig2 && dig2<=15) dig2+=97-10; + 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); + r.append( &dig1, 1); + r.append( &dig2, 1); return r; } diff --git a/lib/http_parser.h b/lib/http_parser.h index ae235a2e..77b12aed 100644 --- a/lib/http_parser.h +++ b/lib/http_parser.h @@ -8,7 +8,7 @@ #include /// Holds all HTTP processing related code. -namespace HTTP{ +namespace HTTP { /// Simple class for reading and writing HTTP 1.0 and 1.1. class Parser{ public: @@ -45,5 +45,7 @@ namespace HTTP{ void Trim(std::string & s); static int unhex(char c); static std::string hex(char dec); - };//HTTP::Parser class -};//HTTP namespace + }; +//HTTP::Parser class + +}//HTTP namespace diff --git a/lib/json.cpp b/lib/json.cpp index c7a0557f..68596e90 100644 --- a/lib/json.cpp +++ b/lib/json.cpp @@ -7,7 +7,6 @@ #include //for uint64_t #include //for memcpy #include //for htonl - int JSON::Value::c2hex(int c){ if (c >= '0' && c <= '9') return c - '0'; if (c >= 'a' && c <= 'f') return c - 'a' + 10; @@ -15,7 +14,6 @@ int JSON::Value::c2hex(int c){ return 0; } - std::string JSON::Value::read_string(int separator, std::istream & fromstream){ std::string out; bool escaped = false; @@ -27,17 +25,28 @@ std::string JSON::Value::read_string(int separator, std::istream & fromstream){ } if (escaped){ switch (c){ - case 'b': out += '\b'; break; - case 'f': out += '\f'; break; - case 'n': out += '\n'; break; - case 'r': out += '\r'; break; - case 't': out += '\t'; break; - case 'u':{ - int d1 = fromstream.get(); - int d2 = fromstream.get(); - int d3 = fromstream.get(); - int d4 = fromstream.get(); - c = c2hex(d4) + (c2hex(d3) << 4) + (c2hex(d2) << 8) + (c2hex(d1) << 16); + case 'b': + out += '\b'; + break; + case 'f': + out += '\f'; + break; + case 'n': + out += '\n'; + break; + case 'r': + out += '\r'; + break; + case 't': + out += '\t'; + break; + case 'u': { + int d1 = fromstream.get(); + int d2 = fromstream.get(); + int d3 = fromstream.get(); + int d4 = fromstream.get(); + c = c2hex(d4) + (c2hex(d3) << 4) + (c2hex(d2) << 8) + (c2hex(d1) << 16); + break; } default: out += (char)c; @@ -58,14 +67,30 @@ std::string JSON::Value::string_escape(std::string val){ std::string out = "\""; for (unsigned int i = 0; i < val.size(); ++i){ switch (val[i]){ - case '"': out += "\\\""; break; - case '\\': out += "\\\\"; break; - case '\n': out += "\\n"; break; - case '\b': out += "\\b"; break; - case '\f': out += "\\f"; break; - case '\r': out += "\\r"; break; - case '\t': out += "\\t"; break; - default: out += val[i]; + case '"': + out += "\\\""; + break; + case '\\': + out += "\\\\"; + break; + case '\n': + out += "\\n"; + break; + case '\b': + out += "\\b"; + break; + case '\f': + out += "\\f"; + break; + case '\r': + out += "\\r"; + break; + case '\t': + out += "\\t"; + break; + default: + out += val[i]; + break; } } out += "\""; @@ -76,9 +101,15 @@ std::string JSON::Value::string_escape(std::string val){ void JSON::Value::skipToEnd(std::istream & fromstream){ while (fromstream.good()){ char peek = fromstream.peek(); - if (peek == ','){return;} - if (peek == ']'){return;} - if (peek == '}'){return;} + if (peek == ','){ + return; + } + if (peek == ']'){ + return; + } + if (peek == '}'){ + return; + } peek = fromstream.get(); } } @@ -101,7 +132,7 @@ JSON::Value::Value(std::istream & fromstream){ c = fromstream.get(); myType = OBJECT; break; - case '[':{ + case '[': { reading_array = true; c = fromstream.get(); myType = ARRAY; @@ -114,13 +145,13 @@ JSON::Value::Value(std::istream & fromstream){ case '\'': case '"': c = fromstream.get(); - if (!reading_object){ + if ( !reading_object){ myType = STRING; strVal = read_string(c, fromstream); return; }else{ std::string tmpstr = read_string(c, fromstream); - (*this)[tmpstr] = JSON::Value(fromstream); + ( *this)[tmpstr] = JSON::Value(fromstream); } break; case '0': @@ -139,18 +170,22 @@ JSON::Value::Value(std::istream & fromstream){ intVal += c - '0'; break; case ',': - if (!reading_object && !reading_array) return; + if ( !reading_object && !reading_array) return; c = fromstream.get(); if (reading_array){ append(JSON::Value(fromstream)); } break; case '}': - if (reading_object){c = fromstream.get();} + if (reading_object){ + c = fromstream.get(); + } return; break; case ']': - if (reading_array){c = fromstream.get();} + if (reading_array){ + c = fromstream.get(); + } return; break; case 't': @@ -174,7 +209,7 @@ JSON::Value::Value(std::istream & fromstream){ return; break; default: - c = fromstream.get();//ignore this character + c = fromstream.get(); //ignore this character continue; break; } @@ -185,12 +220,14 @@ JSON::Value::Value(std::istream & fromstream){ JSON::Value::Value(const std::string & val){ myType = STRING; strVal = val; + intVal = 0; } /// Sets this JSON::Value to the given string. JSON::Value::Value(const char * val){ myType = STRING; strVal = val; + intVal = 0; } /// Sets this JSON::Value to the given integer. @@ -202,14 +239,24 @@ JSON::Value::Value(long long int val){ /// Compares a JSON::Value to another for equality. bool JSON::Value::operator==(const JSON::Value & rhs) const{ if (myType != rhs.myType) return false; - if (myType == INTEGER || myType == BOOL){return intVal == rhs.intVal;} - if (myType == STRING){return strVal == rhs.strVal;} - if (myType == EMPTY){return true;} + if (myType == INTEGER || myType == BOOL){ + return intVal == rhs.intVal; + } + if (myType == STRING){ + return strVal == rhs.strVal; + } + if (myType == EMPTY){ + return true; + } if (myType == OBJECT){ if (objVal.size() != rhs.objVal.size()) return false; for (std::map::const_iterator it = objVal.begin(); it != objVal.end(); ++it){ - if (!rhs.isMember(it->first)){return false;} - if (it->second != rhs.objVal.find(it->first)->second){return false;} + if ( !rhs.isMember(it->first)){ + return false; + } + if (it->second != rhs.objVal.find(it->first)->second){ + return false; + } } return true; } @@ -217,7 +264,9 @@ bool JSON::Value::operator==(const JSON::Value & rhs) const{ if (arrVal.size() != rhs.arrVal.size()) return false; int i = 0; for (std::deque::const_iterator it = arrVal.begin(); it != arrVal.end(); ++it){ - if (*it != rhs.arrVal[i]){return false;} + if ( *it != rhs.arrVal[i]){ + return false; + } i++; } return true; @@ -227,7 +276,7 @@ bool JSON::Value::operator==(const JSON::Value & rhs) const{ /// Compares a JSON::Value to another for equality. bool JSON::Value::operator!=(const JSON::Value & rhs) const{ - return !((*this) == rhs); + return !(( *this) == rhs); } /// Sets this JSON::Value to the given boolean. @@ -248,7 +297,7 @@ JSON::Value & JSON::Value::operator=(const std::string &rhs){ /// Sets this JSON::Value to the given string. JSON::Value & JSON::Value::operator=(const char * rhs){ - return ((*this) = (std::string)rhs); + return (( *this) = (std::string)rhs); } /// Sets this JSON::Value to the given integer. @@ -261,12 +310,12 @@ JSON::Value & JSON::Value::operator=(const long long int &rhs){ /// Sets this JSON::Value to the given integer. JSON::Value & JSON::Value::operator=(const int &rhs){ - return ((*this) = (long long int)rhs); + return (( *this) = (long long int)rhs); } /// Sets this JSON::Value to the given integer. JSON::Value & JSON::Value::operator=(const unsigned int &rhs){ - return ((*this) = (long long int)rhs); + return (( *this) = (long long int)rhs); } /// Automatic conversion to long long int - returns 0 if not convertable. @@ -280,7 +329,6 @@ JSON::Value::operator long long int(){ return 0; } - /// Automatic conversion to std::string. /// Returns the raw string value if available, otherwise calls toString(). JSON::Value::operator std::string(){ @@ -298,29 +346,40 @@ JSON::Value::operator std::string(){ /// Automatic conversion to bool. /// Returns true if there is anything meaningful stored into this value. JSON::Value::operator bool(){ - if (myType == STRING){return strVal != "";} - if (myType == INTEGER){return intVal != 0;} - if (myType == BOOL){return intVal != 0;} - if (myType == OBJECT){return size() > 0;} - if (myType == ARRAY){return size() > 0;} - if (myType == EMPTY){return false;} - return false;//unimplemented? should never happen... + if (myType == STRING){ + return strVal != ""; + } + if (myType == INTEGER){ + return intVal != 0; + } + if (myType == BOOL){ + return intVal != 0; + } + if (myType == OBJECT){ + return size() > 0; + } + if (myType == ARRAY){ + return size() > 0; + } + if (myType == EMPTY){ + return false; + } + return false; //unimplemented? should never happen... } /// Explicit conversion to std::string. const std::string JSON::Value::asString(){ - return (std::string)*this; + return (std::string) *this; } /// Explicit conversion to long long int. const long long int JSON::Value::asInt(){ - return (long long int)*this; + return (long long int) *this; } /// Explicit conversion to bool. const bool JSON::Value::asBool(){ - return (bool)*this; + return (bool) *this; } - /// Retrieves or sets the JSON::Value at this position in the object. /// Converts destructively to object if not already an object. JSON::Value & JSON::Value::operator[](const std::string i){ @@ -362,15 +421,19 @@ std::string JSON::Value::toPacked(){ if (isInt() || isNull() || isBool()){ r += 0x01; uint64_t numval = intVal; - r += *(((char*)&numval)+7); r += *(((char*)&numval)+6); - r += *(((char*)&numval)+5); r += *(((char*)&numval)+4); - r += *(((char*)&numval)+3); r += *(((char*)&numval)+2); - r += *(((char*)&numval)+1); r += *(((char*)&numval)); + r += *(((char*) &numval) + 7); + r += *(((char*) &numval) + 6); + r += *(((char*) &numval) + 5); + r += *(((char*) &numval) + 4); + r += *(((char*) &numval) + 3); + r += *(((char*) &numval) + 2); + r += *(((char*) &numval) + 1); + r += *(((char*) &numval)); } if (isString()){ r += 0x02; - r += strVal.size() / (256*256*256); - r += strVal.size() / (256*256); + r += strVal.size() / (256 * 256 * 256); + r += strVal.size() / (256 * 256); r += strVal.size() / 256; r += strVal.size() % 256; r += strVal; @@ -385,7 +448,9 @@ std::string JSON::Value::toPacked(){ r += it->second.toPacked(); } } - r += (char)0x0; r += (char)0x0; r += (char)0xEE; + r += (char)0x0; + r += (char)0x0; + r += (char)0xEE; strVal.clear(); } if (isArray()){ @@ -393,11 +458,14 @@ std::string JSON::Value::toPacked(){ for (JSON::ArrIter it = arrVal.begin(); it != arrVal.end(); it++){ r += it->toPacked(); } - r += (char)0x0; r += (char)0x0; r += (char)0xEE; + r += (char)0x0; + r += (char)0x0; + r += (char)0xEE; } return r; -};//toPacked - +} +; +//toPacked /// Packs any object-type JSON::Value to a std::string for transfer over the network, including proper DTMI header. /// Non-object-types will print an error to stderr and return an empty string. @@ -422,14 +490,13 @@ std::string & JSON::Value::toNetPacked(){ } //insert the packet length at bytes 4-7 unsigned int size = htonl(packed.size()); - memcpy((void*)(strVal.c_str() + 4), (void*)&size, 4); + memcpy((void*)(strVal.c_str() + 4), (void*) &size, 4); //copy the rest of the string memcpy((void*)(strVal.c_str() + 8), packed.c_str(), packed.size()); } return strVal; } - /// Converts this JSON::Value to valid JSON notation and returns it. /// Makes absolutely no attempts to pretty-print anything. :-) std::string JSON::Value::toString(){ @@ -449,7 +516,9 @@ std::string JSON::Value::toString(){ if (arrVal.size() > 0){ for (ArrIter it = ArrBegin(); it != ArrEnd(); it++){ tmp += it->toString(); - if (it + 1 != ArrEnd()){tmp += ",";} + if (it + 1 != ArrEnd()){ + tmp += ","; + } } } tmp += "]"; @@ -464,7 +533,9 @@ std::string JSON::Value::toString(){ for (ObjIter it2 = ObjBegin(); it2 != ObjEnd(); it2++){ tmp2 += "\"" + it2->first + "\":"; tmp2 += it2->second.toString(); - if (it2 != it3){tmp2 += ",";} + if (it2 != it3){ + tmp2 += ","; + } } } tmp2 += "}"; @@ -475,7 +546,7 @@ std::string JSON::Value::toString(){ default: return "null"; } - return "null";//should never get here... + return "null"; //should never get here... } /// Converts this JSON::Value to valid JSON notation and returns it. @@ -491,7 +562,7 @@ std::string JSON::Value::toPrettyString(int indentation){ case STRING: { for (unsigned int i = 0; i < 5 && i < strVal.size(); ++i){ if (strVal[i] < 32 || strVal[i] > 125){ - return JSON::Value((long long int)strVal.size()).asString()+" bytes of binary data"; + return JSON::Value((long long int)strVal.size()).asString() + " bytes of binary data"; } } return string_escape(strVal); @@ -501,10 +572,12 @@ std::string JSON::Value::toPrettyString(int indentation){ if (arrVal.size() > 0){ std::string tmp = "[\n"; for (ArrIter it = ArrBegin(); it != ArrEnd(); it++){ - tmp += std::string(indentation+2, ' ')+it->toPrettyString(indentation+2); - if (it + 1 != ArrEnd()){tmp += ",\n";} + tmp += std::string(indentation + 2, ' ') + it->toPrettyString(indentation + 2); + if (it + 1 != ArrEnd()){ + tmp += ",\n"; + } } - tmp += "\n"+std::string(indentation, ' ')+"]"; + tmp += "\n" + std::string(indentation, ' ') + "]"; return tmp; }else{ return "[]"; @@ -517,11 +590,13 @@ std::string JSON::Value::toPrettyString(int indentation){ ObjIter it3 = ObjEnd(); --it3; for (ObjIter it2 = ObjBegin(); it2 != ObjEnd(); it2++){ - tmp2 += std::string(indentation+2, ' ')+"\"" + it2->first + "\":"; - tmp2 += it2->second.toPrettyString(indentation+2); - if (it2 != it3){tmp2 += ",\n";} + tmp2 += std::string(indentation + 2, ' ') + "\"" + it2->first + "\":"; + tmp2 += it2->second.toPrettyString(indentation + 2); + if (it2 != it3){ + tmp2 += ",\n"; + } } - tmp2 += "\n"+std::string(indentation, ' ')+"}"; + tmp2 += "\n" + std::string(indentation, ' ') + "}"; return tmp2; }else{ return "{}"; @@ -532,7 +607,7 @@ std::string JSON::Value::toPrettyString(int indentation){ default: return "null"; } - return "null";//should never get here... + return "null"; //should never get here... } /// Appends the given value to the end of this JSON::Value array. @@ -563,11 +638,15 @@ void JSON::Value::prepend(const JSON::Value & rhs){ /// given size. void JSON::Value::shrink(unsigned int size){ if (myType == ARRAY){ - while (arrVal.size() > size){arrVal.pop_front();} + while (arrVal.size() > size){ + arrVal.pop_front(); + } return; } if (myType == OBJECT){ - while (objVal.size() > size){objVal.erase(objVal.begin());} + while (objVal.size() > size){ + objVal.erase(objVal.begin()); + } return; } } @@ -671,62 +750,66 @@ JSON::Value JSON::fromFile(std::string filename){ /// \param i Current parsing position in the raw data (defaults to 0). /// \returns A single JSON::Value, parsed from the raw data. JSON::Value JSON::fromDTMI(const unsigned char * data, unsigned int len, unsigned int &i){ - #if DEBUG >= 10 +#if DEBUG >= 10 fprintf(stderr, "Note: AMF type %hhx found. %i bytes left\n", data[i], len-i); - #endif +#endif switch (data[i]){ - case 0x01:{//integer + case 0x01: { //integer unsigned char tmpdbl[8]; - tmpdbl[7] = data[i+1]; - tmpdbl[6] = data[i+2]; - tmpdbl[5] = data[i+3]; - tmpdbl[4] = data[i+4]; - tmpdbl[3] = data[i+5]; - tmpdbl[2] = data[i+6]; - tmpdbl[1] = data[i+7]; - tmpdbl[0] = data[i+8]; - i+=9;//skip 8(an uint64_t)+1 forwards + tmpdbl[7] = data[i + 1]; + tmpdbl[6] = data[i + 2]; + tmpdbl[5] = data[i + 3]; + tmpdbl[4] = data[i + 4]; + tmpdbl[3] = data[i + 5]; + tmpdbl[2] = data[i + 6]; + tmpdbl[1] = data[i + 7]; + tmpdbl[0] = data[i + 8]; + i += 9; //skip 8(an uint64_t)+1 forwards uint64_t * d = (uint64_t*)tmpdbl; - return JSON::Value((long long int)*d); - } break; - case 0x02:{//string - unsigned int tmpi = data[i+1]*256*256*256+data[i+2]*256*256+data[i+3]*256+data[i+4];//set tmpi to UTF-8-long length - std::string tmpstr = std::string((const char *)data+i+5, (size_t)tmpi);//set the string data - i += tmpi + 5;//skip length+size+1 forwards + return JSON::Value((long long int) *d); + } + break; + case 0x02: { //string + unsigned int tmpi = data[i + 1] * 256 * 256 * 256 + data[i + 2] * 256 * 256 + data[i + 3] * 256 + data[i + 4]; //set tmpi to UTF-8-long length + std::string tmpstr = std::string((const char *)data + i + 5, (size_t)tmpi); //set the string data + i += tmpi + 5; //skip length+size+1 forwards return JSON::Value(tmpstr); - } break; - case 0xFF://also object - case 0xE0:{//object + } + break; + case 0xFF: //also object + case 0xE0: { //object ++i; JSON::Value ret; - while (data[i] + data[i+1] != 0){//while not encountering 0x0000 (we assume 0x0000EE) - unsigned int tmpi = data[i]*256+data[i+1];//set tmpi to the UTF-8 length - std::string tmpstr = std::string((const char *)data+i+2, (size_t)tmpi);//set the string data - i += tmpi + 2;//skip length+size forwards - ret[tmpstr] = fromDTMI(data, len, i);//add content, recursively parsed, updating i, setting indice to tmpstr + while (data[i] + data[i + 1] != 0){ //while not encountering 0x0000 (we assume 0x0000EE) + unsigned int tmpi = data[i] * 256 + data[i + 1]; //set tmpi to the UTF-8 length + std::string tmpstr = std::string((const char *)data + i + 2, (size_t)tmpi); //set the string data + i += tmpi + 2; //skip length+size forwards + ret[tmpstr] = fromDTMI(data, len, i); //add content, recursively parsed, updating i, setting indice to tmpstr } - i += 3;//skip 0x0000EE + i += 3; //skip 0x0000EE return ret; - } break; - case 0x0A:{//array + } + break; + case 0x0A: { //array JSON::Value ret; ++i; - while (data[i] + data[i+1] != 0){//while not encountering 0x0000 (we assume 0x0000EE) - ret.append(fromDTMI(data, len, i));//add content, recursively parsed, updating i + while (data[i] + data[i + 1] != 0){ //while not encountering 0x0000 (we assume 0x0000EE) + ret.append(fromDTMI(data, len, i)); //add content, recursively parsed, updating i } - i += 3;//skip 0x0000EE + i += 3; //skip 0x0000EE return ret; - } break; + } + break; } - #if DEBUG >= 2 +#if DEBUG >= 2 fprintf(stderr, "Error: Unimplemented DTMI type %hhx - returning.\n", data[i]); - #endif +#endif return JSON::Value(); -}//fromOneDTMI +} //fromOneDTMI /// Parses a std::string to a valid JSON::Value. /// This function will find one DTMI object in the string and return it. JSON::Value JSON::fromDTMI(std::string data){ unsigned int i = 0; return fromDTMI((const unsigned char*)data.c_str(), data.size(), i); -}//fromDTMI +} //fromDTMI diff --git a/lib/json.h b/lib/json.h index fee192f2..a2db4845 100644 --- a/lib/json.h +++ b/lib/json.h @@ -7,19 +7,24 @@ #include //empty definition of DTSC::Stream so it can be a friend. -namespace DTSC{class Stream;} +namespace DTSC { + class Stream; +} /// JSON-related classes and functions -namespace JSON{ +namespace JSON { /// Lists all types of JSON::Value. - enum ValueType{ EMPTY, BOOL, INTEGER, STRING, ARRAY, OBJECT }; + enum ValueType{ + EMPTY, BOOL, INTEGER, STRING, ARRAY, OBJECT + }; - class Value;//forward declaration for below typedef + class Value; + //forward declaration for below typedef typedef std::map::iterator ObjIter; typedef std::deque::iterator ArrIter; - + /// A JSON::Value is either a string or an integer, but may also be an object, array or null. class Value{ private: @@ -34,7 +39,7 @@ namespace JSON{ static void skipToEnd(std::istream & fromstream); public: //friends - friend class DTSC::Stream;//for access to strVal + friend class DTSC::Stream; //for access to strVal //constructors Value(); Value(std::istream & fromstream); @@ -91,5 +96,5 @@ namespace JSON{ Value fromDTMI(const unsigned char * data, unsigned int len, unsigned int &i); Value fromString(std::string json); Value fromFile(std::string filename); - -}; + +} diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 36d9d986..e2681cac 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -7,7 +7,7 @@ #define Int64 long long int /// Contains all MP4 format related code. -namespace MP4{ +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. @@ -33,39 +33,43 @@ namespace MP4{ } /// Returns the values at byte positions 4 through 7. - std::string Box::getType() { - return std::string(data+4,4); + 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( char* boxType ) { + bool Box::isType(const char* boxType){ return !memcmp(boxType, data + 4, 4); } /// 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 ( !managed){ + return false; + } if (newData.size() > 4){ payloadOffset = 8; - long long int size = ntohl( ((int*)newData.c_str())[0] ); - if( size == 1) { - if( newData.size() > 16) { - size = 0 + ntohl( ((int*)newData.c_str())[2] ); + long long int 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] ); + size += ntohl(((int*)newData.c_str())[3]); payloadOffset = 16; - } else { + }else{ return false; } } if (newData.size() >= size){ void * ret = malloc(size); - if (!ret){return false;} + if ( !ret){ + return false; + } free(data); data = (char*)ret; memcpy(data, newData.c_str(), size); - newData.erase( 0, size ); + newData.erase(0, size); return true; } } @@ -73,33 +77,35 @@ namespace MP4{ } /// Returns the total boxed size of this box, including the header. - long long int Box::boxedSize() { - if( payloadOffset == 16 ) { - return ((long long int)ntohl( ((int*)data)[2] ) << 32) + ntohl( ((int*)data)[3] ); + long long int Box::boxedSize(){ + if (payloadOffset == 16){ + return ((long long int)ntohl(((int*)data)[2]) << 32) + ntohl(((int*)data)[3]); } - return ntohl( ((int*)data)[0] ); + return ntohl(((int*)data)[0]); } /// Retruns the size of the payload of thix box, excluding the header. /// This value is defined as boxedSize() - 8. - long long int Box::payloadSize() { + long long int Box::payloadSize(){ return boxedSize() - payloadOffset; } /// Returns a copy of the data pointer. - char * Box::asBox() { + char * Box::asBox(){ return data; } - - char * Box::payload() { + + 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);} + void Box::clear(){ + if (data && managed){ + free(data); + } managed = true; payloadOffset = 8; data = (char*)malloc(8); @@ -114,28 +120,52 @@ namespace MP4{ /// 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 . std::string Box::toPrettyString(int 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; - default: return std::string(indent, ' ')+"Unimplemented pretty-printing for box "+std::string(data+4,4)+"\n"; break; + 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; + default: + break; } + return std::string(indent, ' ') + "Unimplemented pretty-printing for box " + std::string(data + 4, 4) + "\n"; } /// 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 ) { + void Box::setInt8(char newData, size_t index){ index += payloadOffset; if (index >= boxedSize()){ - if (!reserve(index, 0, 1)){return;} + if ( !reserve(index, 0, 1)){ + return; + } } data[index] = newData; } @@ -143,11 +173,13 @@ namespace MP4{ /// 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 ) { + char Box::getInt8(size_t index){ index += payloadOffset; if (index >= boxedSize()){ - if (!reserve(index, 0, 1)){return 0;} - setInt8(0, index-payloadOffset); + if ( !reserve(index, 0, 1)){ + return 0; + } + setInt8(0, index - payloadOffset); } return data[index]; } @@ -155,132 +187,152 @@ namespace MP4{ /// 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 ) { + void Box::setInt16(short newData, size_t index){ index += payloadOffset; - if (index+1 >= boxedSize()){ - if (!reserve(index, 0, 2)){return;} + if (index + 1 >= boxedSize()){ + if ( !reserve(index, 0, 2)){ + return; + } } - newData = htons( newData ); - memcpy( data + index, (char*)&newData, 2 ); + 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 ) { + short Box::getInt16(size_t index){ index += payloadOffset; - if (index+1 >= boxedSize()){ - if (!reserve(index, 0, 2)){return 0;} - setInt16(0, 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 ); + 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( long newData, size_t index ) { + void Box::setInt24(long newData, size_t index){ index += payloadOffset; - if (index+2 >= boxedSize()){ - if (!reserve(index, 0, 3)){return;} + 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); + 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. - long Box::getInt24( size_t index ) { + long Box::getInt24(size_t index){ index += payloadOffset; - if (index+2 >= boxedSize()){ - if (!reserve(index, 0, 3)){return 0;} - setInt24(0, index-payloadOffset); + if (index + 2 >= boxedSize()){ + if ( !reserve(index, 0, 3)){ + return 0; + } + setInt24(0, index - payloadOffset); } long result = data[index]; result <<= 8; - result += data[index+1]; + result += data[index + 1]; result <<= 8; - result += data[index+2]; + 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( long newData, size_t index ) { + void Box::setInt32(long newData, size_t index){ index += payloadOffset; - if (index+3 >= boxedSize()){ - if (!reserve(index, 0, 4)){return;} + if (index + 3 >= boxedSize()){ + if ( !reserve(index, 0, 4)){ + return; + } } - newData = htonl( newData ); - memcpy( data + index, (char*)&newData, 4 ); + newData = htonl(newData); + memcpy(data + index, (char*) &newData, 4); } /// 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. - long Box::getInt32( size_t index ) { + long Box::getInt32(size_t index){ index += payloadOffset; - if (index+3 >= boxedSize()){ - if (!reserve(index, 0, 4)){return 0;} - setInt32(0, index-payloadOffset); + if (index + 3 >= boxedSize()){ + if ( !reserve(index, 0, 4)){ + return 0; + } + setInt32(0, index - payloadOffset); } long result; - memcpy( (char*)&result, data + index, 4 ); + memcpy((char*) &result, data + index, 4); return ntohl(result); } /// 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( Int64 newData, size_t index ) { + void Box::setInt64(Int64 newData, size_t index){ index += payloadOffset; - if (index+7 >= boxedSize()){ - if (!reserve(index, 0, 8)){return;} + 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)); + ((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. - Int64 Box::getInt64( size_t index ) { + Int64 Box::getInt64(size_t index){ index += payloadOffset; - if (index+7 >= boxedSize()){ - if (!reserve(index, 0, 8)){return 0;} - setInt64(0, index-payloadOffset); + if (index + 7 >= boxedSize()){ + if ( !reserve(index, 0, 8)){ + return 0; + } + setInt64(0, index - payloadOffset); } - Int64 result = ntohl( ((int*)(data+index))[0] ); + Int64 result = ntohl(((int*)(data + index))[0]); result <<= 32; - result += ntohl( ((int*)(data+index))[1] ); + 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 ); + 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 ) { + void Box::setString(char* newData, size_t size, size_t index){ index += payloadOffset; if (index >= boxedSize()){ - if (!reserve(index, 0, 1)){return;} + if ( !reserve(index, 0, 1)){ + return; + } data[index] = 0; } if (getStringLen(index) != size){ - if (!reserve(index, getStringLen(index)+1, size+1)){return;} + if ( !reserve(index, getStringLen(index) + 1, size + 1)){ + return; + } } - memcpy(data + index, newData, size+1); + memcpy(data + index, newData, size + 1); } /// Gets the NULL-terminated string at the given index. @@ -289,18 +341,22 @@ namespace MP4{ char * Box::getString(size_t index){ index += payloadOffset; if (index >= boxedSize()){ - if (!reserve(index, 0, 1)){return 0;} + if ( !reserve(index, 0, 1)){ + return 0; + } data[index] = 0; } - return data+index; + 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); + if (index >= boxedSize()){ + return 0; + } + return strlen(data + index); } /// Gets a reference to the box at the given index. @@ -310,14 +366,14 @@ namespace MP4{ Box & Box::getBox(size_t index){ static Box retbox; index += payloadOffset; - if (index+8 > boxedSize()){ - if (!reserve(index, 0, 8)){ + 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); + memcpy(data + index, "\000\000\000\010erro", 8); } - retbox = Box(data+index, false); + retbox = Box(data + index, false); return retbox; } @@ -325,7 +381,9 @@ namespace MP4{ /// 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;} + if (index + payloadOffset + 8 > boxedSize()){ + return 0; + } return getBox(index).boxedSize(); } @@ -334,648 +392,830 @@ namespace MP4{ 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); + 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 (current == wanted){ + return true; + } if (position > boxedSize()){ wanted += position - boxedSize(); } if (current < wanted){ //make bigger - if (boxedSize() + (wanted-current) > data_size){ + 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;} + 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); + 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)); + 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); + if (payloadOffset != 16){ + int newSize = boxedSize() + (wanted - current); ((int*)data)[0] = htonl(newSize); } return true; } - ABST::ABST( ) { + 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 ); + 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 ); + 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(long newFlags){setInt24(newFlags, 1);} - - long ABST::getFlags(){return getInt24(1);} - void ABST::setBootstrapinfoVersion(long newVersion){setInt32(newVersion, 4);} - - long ABST::getBootstrapinfoVersion(){return getInt32(4);} + void ABST::setVersion(char newVersion){ + setInt8(newVersion, 0); + } + + char ABST::getVersion(){ + return getInt8(0); + } + + void ABST::setFlags(long newFlags){ + setInt24(newFlags, 1); + } + + long ABST::getFlags(){ + return getInt24(1); + } + + void ABST::setBootstrapinfoVersion(long newVersion){ + setInt32(newVersion, 4); + } + + long 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);}; + + 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); + setInt8((getInt8(8) & 0xDF) + (newLive ? 0x10 : 0), 8); } - - bool ABST::getLive(){return (getInt8(8) & 0x10);} - void ABST::setUpdate(bool newUpdate) { + 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(long newScale){setInt32(newScale, 9);} - - long ABST::getTimeScale(){return getInt32(9);} - - void ABST::setCurrentMediaTime(Int64 newTime){setInt64(newTime, 13);} - - Int64 ABST::getCurrentMediaTime(){return getInt64(13);} + bool ABST::getUpdate(){ + return (getInt8(8) & 0x08); + } - void ABST::setSmpteTimeCodeOffset(Int64 newTime){setInt64(newTime, 21);} - - Int64 ABST::getSmpteTimeCodeOffset(){return getInt64(21);} + void ABST::setTimeScale(long newScale){ + setInt32(newScale, 9); + } - void ABST::setMovieIdentifier(std::string & newIdentifier){setString(newIdentifier, 29);} - - char* ABST::getMovieIdentifier(){return getString(29);} + long ABST::getTimeScale(){ + return getInt32(9); + } + + void ABST::setCurrentMediaTime(Int64 newTime){ + setInt64(newTime, 13); + } + + Int64 ABST::getCurrentMediaTime(){ + return getInt64(13); + } + + void ABST::setSmpteTimeCodeOffset(Int64 newTime){ + setInt64(newTime, 21); + } + + Int64 ABST::getSmpteTimeCodeOffset(){ + return getInt64(21); + } + + void ABST::setMovieIdentifier(std::string & newIdentifier){ + setString(newIdentifier, 29); + } + + char* ABST::getMovieIdentifier(){ + return getString(29); + } long ABST::getServerEntryCount(){ - int countLoc = 29 + getStringLen(29)+1; + int countLoc = 29 + getStringLen(29) + 1; return getInt8(countLoc); } void ABST::setServerEntry(std::string & newEntry, long no){ - int countLoc = 29 + getStringLen(29)+1; - int tempLoc = countLoc+1; + int countLoc = 29 + getStringLen(29) + 1; + int tempLoc = countLoc + 1; //attempt to reach the wanted position int i; for (i = 0; i < getInt8(countLoc) && i < no; ++i){ - tempLoc += getStringLen(tempLoc)+1; + 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; + 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(long no){ - if (no+1 > getServerEntryCount()){return "";} - int tempLoc = 29+getStringLen(29)+1 + 1;//position of first entry - for (int i = 0; i < no; i++){tempLoc += getStringLen(tempLoc)+1;} - return getString(tempLoc); - } - - long ABST::getQualityEntryCount(){ - int countLoc = 29 + getStringLen(29)+1 + 1; - for (int i = 0; i< getServerEntryCount(); i++){countLoc += getStringLen(countLoc)+1;} - return getInt8(countLoc); - } - - void ABST::setQualityEntry(std::string & newEntry, long no){ - int countLoc = 29 + getStringLen(29)+1 + 1; - for (int i = 0; i< getServerEntryCount(); i++){countLoc += getStringLen(countLoc)+1;} - int tempLoc = countLoc+1; - //attempt to reach the wanted position - int i; - for (i = 0; i < getInt8(countLoc) && i < no; ++i){ - tempLoc += getStringLen(tempLoc)+1; + if (no + 1 > getServerEntryCount()){ + return ""; } - //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; + int tempLoc = 29 + getStringLen(29) + 1 + 1; //position of first entry + for (int i = 0; i < no; i++){ + tempLoc += getStringLen(tempLoc) + 1; } - //now, tempLoc is at position for string number no, and we have at least 1 byte reserved. - setString(newEntry, tempLoc); - } - - const char* ABST::getQualityEntry(long no){ - if (no > getQualityEntryCount()){return "";} - int tempLoc = 29+getStringLen(29)+1 + 1;//position of serverentries; - for (int i = 0; i < getServerEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} - tempLoc += 1;//first qualityentry - for (int i = 0; i < no; i++){tempLoc += getStringLen(tempLoc)+1;} - return getString(tempLoc); - } - - void ABST::setDrmData( std::string newDrm ) { - long tempLoc = 29 + getStringLen(29)+1 + 1; - for (int i = 0; i< getServerEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} - tempLoc++; - for (int i = 0; i< getQualityEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} - setString(newDrm, tempLoc); - } - - char* ABST::getDrmData() { - long tempLoc = 29 + getStringLen(29)+1 + 1; - for (int i = 0; i< getServerEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} - tempLoc++; - for (int i = 0; i< getQualityEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} return getString(tempLoc); } - void ABST::setMetaData( std::string newMetaData ) { - long tempLoc = 29 + getStringLen(29)+1 + 1; - for (int i = 0; i< getServerEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} + long ABST::getQualityEntryCount(){ + int countLoc = 29 + getStringLen(29) + 1 + 1; + for (int i = 0; i < getServerEntryCount(); i++){ + countLoc += getStringLen(countLoc) + 1; + } + return getInt8(countLoc); + } + + void ABST::setQualityEntry(std::string & newEntry, long no){ + int countLoc = 29 + getStringLen(29) + 1 + 1; + for (int i = 0; i < getServerEntryCount(); i++){ + countLoc += getStringLen(countLoc) + 1; + } + int tempLoc = countLoc + 1; + //attempt to reach the wanted position + 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(long no){ + if (no > getQualityEntryCount()){ + return ""; + } + int tempLoc = 29 + getStringLen(29) + 1 + 1; //position of serverentries; + for (int i = 0; i < getServerEntryCount(); i++){ + tempLoc += getStringLen(tempLoc) + 1; + } + tempLoc += 1; //first qualityentry + for (int i = 0; i < no; i++){ + tempLoc += getStringLen(tempLoc) + 1; + } + return getString(tempLoc); + } + + void ABST::setDrmData(std::string newDrm){ + long tempLoc = 29 + getStringLen(29) + 1 + 1; + for (int i = 0; i < getServerEntryCount(); i++){ + tempLoc += getStringLen(tempLoc) + 1; + } tempLoc++; - for (int i = 0; i< getQualityEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} - tempLoc += getStringLen(tempLoc)+1; + for (int i = 0; i < getQualityEntryCount(); i++){ + tempLoc += getStringLen(tempLoc) + 1; + } + setString(newDrm, tempLoc); + } + + char* ABST::getDrmData(){ + long tempLoc = 29 + getStringLen(29) + 1 + 1; + for (int i = 0; i < getServerEntryCount(); i++){ + tempLoc += getStringLen(tempLoc) + 1; + } + tempLoc++; + for (int i = 0; i < getQualityEntryCount(); i++){ + tempLoc += getStringLen(tempLoc) + 1; + } + return getString(tempLoc); + } + + void ABST::setMetaData(std::string newMetaData){ + long tempLoc = 29 + getStringLen(29) + 1 + 1; + for (int i = 0; i < getServerEntryCount(); i++){ + tempLoc += getStringLen(tempLoc) + 1; + } + tempLoc++; + for (int i = 0; i < getQualityEntryCount(); i++){ + tempLoc += getStringLen(tempLoc) + 1; + } + tempLoc += getStringLen(tempLoc) + 1; setString(newMetaData, tempLoc); } - - char* ABST::getMetaData() { - long tempLoc = 29 + getStringLen(29)+1 + 1; - for (int i = 0; i< getServerEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} + + char* ABST::getMetaData(){ + long tempLoc = 29 + getStringLen(29) + 1 + 1; + for (int i = 0; i < getServerEntryCount(); i++){ + tempLoc += getStringLen(tempLoc) + 1; + } tempLoc++; - for (int i = 0; i< getQualityEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} - tempLoc += getStringLen(tempLoc)+1; + for (int i = 0; i < getQualityEntryCount(); i++){ + tempLoc += getStringLen(tempLoc) + 1; + } + tempLoc += getStringLen(tempLoc) + 1; return getString(tempLoc); } long ABST::getSegmentRunTableCount(){ - long tempLoc = 29 + getStringLen(29)+1 + 1; - for (int i = 0; i< getServerEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} + long tempLoc = 29 + getStringLen(29) + 1 + 1; + for (int i = 0; i < getServerEntryCount(); i++){ + tempLoc += getStringLen(tempLoc) + 1; + } tempLoc++; - for (int i = 0; i< getQualityEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} - tempLoc+=getStringLen(tempLoc)+1;//DrmData - tempLoc+=getStringLen(tempLoc)+1;//MetaData + for (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, long no ) { - long tempLoc = 29 + getStringLen(29)+1 + 1; - for (int i = 0; i< getServerEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} + + void ABST::setSegmentRunTable(ASRT & newSegment, long no){ + long tempLoc = 29 + getStringLen(29) + 1 + 1; + for (int i = 0; i < getServerEntryCount(); i++){ + tempLoc += getStringLen(tempLoc) + 1; + } tempLoc++; - for (int i = 0; i< getQualityEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} - tempLoc+=getStringLen(tempLoc)+1;//DrmData - tempLoc+=getStringLen(tempLoc)+1;//MetaData + for (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 + tempLoc++; //skip segmentRuntableCount //attempt to reach the wanted position int i; - for (i = 0; i < getInt8(countLoc) && i < no; ++i){tempLoc += getBoxLen(tempLoc);} + 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;}; + 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); + memcpy(data + payloadOffset + tempLoc + j * 8, "\000\000\000\010erro", 8); } - setInt8(no+1, countLoc);//set new count - tempLoc += (no-i)*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( long no ) { + + ASRT & ABST::getSegmentRunTable(long no){ static Box result; - if( no > getSegmentRunTableCount() ) { + if (no > getSegmentRunTableCount()){ static Box res; return (ASRT&)res; } - long tempLoc = 29 + getStringLen(29)+1 + 1; - for (int i = 0; i< getServerEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} + long tempLoc = 29 + getStringLen(29) + 1 + 1; + for (int i = 0; i < getServerEntryCount(); i++){ + tempLoc += getStringLen(tempLoc) + 1; + } tempLoc++; - for (int i = 0; i< getQualityEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} - tempLoc+=getStringLen(tempLoc)+1;//DrmData - tempLoc+=getStringLen(tempLoc)+1;//MetaData + for (int i = 0; i < getQualityEntryCount(); i++){ + tempLoc += getStringLen(tempLoc) + 1; + } + tempLoc += getStringLen(tempLoc) + 1; //DrmData + tempLoc += getStringLen(tempLoc) + 1; //MetaData int countLoc = tempLoc; - tempLoc++;//segmentRuntableCount - for (int i = 0; i < no; ++i){tempLoc += getBoxLen(tempLoc);} + tempLoc++; //segmentRuntableCount + for (int i = 0; i < no; ++i){ + tempLoc += getBoxLen(tempLoc); + } return (ASRT&)getBox(tempLoc); } - - long ABST::getFragmentRunTableCount() { - long tempLoc = 29 + getStringLen(29)+1 + 1; - for (int i = 0; i< getServerEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} + + long ABST::getFragmentRunTableCount(){ + long tempLoc = 29 + getStringLen(29) + 1 + 1; + for (int i = 0; i < getServerEntryCount(); i++){ + tempLoc += getStringLen(tempLoc) + 1; + } tempLoc++; - for (int i = 0; i< getQualityEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} - tempLoc+=getStringLen(tempLoc)+1;//DrmData - tempLoc+=getStringLen(tempLoc)+1;//MetaData - for (int i = getInt8(tempLoc++); i != 0; --i){tempLoc += getBoxLen(tempLoc);} - return getInt8( tempLoc ); + for (int i = 0; i < getQualityEntryCount(); i++){ + tempLoc += getStringLen(tempLoc) + 1; + } + tempLoc += getStringLen(tempLoc) + 1; //DrmData + tempLoc += getStringLen(tempLoc) + 1; //MetaData + for (int i = getInt8(tempLoc++); i != 0; --i){ + tempLoc += getBoxLen(tempLoc); + } + return getInt8(tempLoc); } - - void ABST::setFragmentRunTable( AFRT & newFragment, long no ) { - long tempLoc = 29 + getStringLen(29)+1 + 1; - for (int i = 0; i< getServerEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} + + void ABST::setFragmentRunTable(AFRT & newFragment, long no){ + long tempLoc = 29 + getStringLen(29) + 1 + 1; + for (int i = 0; i < getServerEntryCount(); i++){ + tempLoc += getStringLen(tempLoc) + 1; + } tempLoc++; - for (int i = 0; i< getQualityEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} - tempLoc+=getStringLen(tempLoc)+1;//DrmData - tempLoc+=getStringLen(tempLoc)+1;//MetaData - for (int i = getInt8(tempLoc++); i != 0; --i){tempLoc += getBoxLen(tempLoc);} + for (int i = 0; i < getQualityEntryCount(); i++){ + tempLoc += getStringLen(tempLoc) + 1; + } + tempLoc += getStringLen(tempLoc) + 1; //DrmData + tempLoc += getStringLen(tempLoc) + 1; //MetaData + for (int i = getInt8(tempLoc++); i != 0; --i){ + tempLoc += getBoxLen(tempLoc); + } int countLoc = tempLoc; tempLoc++; //attempt to reach the wanted position int i; - for (i = 0; i < getInt8(countLoc) && i < no; ++i){tempLoc += getBoxLen(tempLoc);} + 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;}; + 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); + memcpy(data + payloadOffset + tempLoc + j * 8, "\000\000\000\010erro", 8); } - setInt8(no+1, countLoc);//set new count - tempLoc += (no-i)*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( long no ) { + AFRT & ABST::getFragmentRunTable(long no){ static Box result; if (no >= getFragmentRunTableCount()){ static Box res; return (AFRT&)res; } - long tempLoc = 29 + getStringLen(29)+1 + 1; - for (int i = 0; i< getServerEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} + long tempLoc = 29 + getStringLen(29) + 1 + 1; + for (int i = 0; i < getServerEntryCount(); i++){ + tempLoc += getStringLen(tempLoc) + 1; + } tempLoc++; - for (int i = 0; i< getQualityEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} - tempLoc+=getStringLen(tempLoc)+1;//DrmData - tempLoc+=getStringLen(tempLoc)+1;//MetaData - for (int i = getInt8(tempLoc++); i != 0; --i){tempLoc += getBoxLen(tempLoc);} + for (int i = 0; i < getQualityEntryCount(); i++){ + tempLoc += getStringLen(tempLoc) + 1; + } + tempLoc += getStringLen(tempLoc) + 1; //DrmData + tempLoc += getStringLen(tempLoc) + 1; //MetaData + for (int i = getInt8(tempLoc++); i != 0; --i){ + tempLoc += getBoxLen(tempLoc); + } int countLoc = tempLoc; tempLoc++; - for (int i = 0; i < no; i++){tempLoc += getBoxLen(tempLoc);} + for (int i = 0; i < no; i++){ + tempLoc += getBoxLen(tempLoc); + } return (AFRT&)getBox(tempLoc); } - - std::string ABST::toPrettyString( long indent ) { + + std::string ABST::toPrettyString(long 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; + 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; + 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; + 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( int i = 0; i < getServerEntryCount(); i++ ) { - r << std::string(indent+2, ' ') << i << ": " << getServerEntry(i) << 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 (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( int i = 0; i < getQualityEntryCount(); i++ ) { - r << std::string(indent+2, ' ') << i << ": " << getQualityEntry(i) << std::endl; + r << std::string(indent + 1, ' ') << "QualityEntryTable (" << getQualityEntryCount() << ")" << std::endl; + for (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, ' ') << "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); + 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);} - - long AFRT::getVersion(){return getInt8(0);} - - void AFRT::setUpdate(long newUpdate){setInt24(newUpdate, 1);} - - long AFRT::getUpdate(){return getInt24(1);} - - void AFRT::setTimeScale(long newScale){setInt32(newScale, 4);} - - long AFRT::getTimeScale(){return getInt32(4);} - - long AFRT::getQualityEntryCount(){return getInt8(8);} - + + void AFRT::setVersion(char newVersion){ + setInt8(newVersion, 0); + } + + long AFRT::getVersion(){ + return getInt8(0); + } + + void AFRT::setUpdate(long newUpdate){ + setInt24(newUpdate, 1); + } + + long AFRT::getUpdate(){ + return getInt24(1); + } + + void AFRT::setTimeScale(long newScale){ + setInt32(newScale, 4); + } + + long AFRT::getTimeScale(){ + return getInt32(4); + } + + long AFRT::getQualityEntryCount(){ + return getInt8(8); + } + void AFRT::setQualityEntry(std::string & newEntry, long no){ int countLoc = 8; - int tempLoc = countLoc+1; + int tempLoc = countLoc + 1; //attempt to reach the wanted position int i; for (i = 0; i < getQualityEntryCount() && i < no; ++i){ - tempLoc += getStringLen(tempLoc)+1; + 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; + 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(long no){ - if (no+1 > getQualityEntryCount()){return "";} - int tempLoc = 9;//position of first quality entry - for (int i = 0; i < no; i++){tempLoc += getStringLen(tempLoc)+1;} + if (no + 1 > getQualityEntryCount()){ + return ""; + } + int tempLoc = 9; //position of first quality entry + for (int i = 0; i < no; i++){ + tempLoc += getStringLen(tempLoc) + 1; + } return getString(tempLoc); } - + long AFRT::getFragmentRunCount(){ int tempLoc = 9; - for (int i = 0; i < getQualityEntryCount(); ++i){tempLoc += getStringLen(tempLoc)+1;} + for (int i = 0; i < getQualityEntryCount(); ++i){ + tempLoc += getStringLen(tempLoc) + 1; + } return getInt32(tempLoc); } - - void AFRT::setFragmentRun( afrt_runtable newRun, long no ) { + + void AFRT::setFragmentRun(afrt_runtable newRun, long no){ int tempLoc = 9; for (int i = 0; i < getQualityEntryCount(); ++i){ - tempLoc += getStringLen(tempLoc)+1; + tempLoc += getStringLen(tempLoc) + 1; } int countLoc = tempLoc; tempLoc += 4; for (int i = 0; i < no; i++){ - if (i+1 > getInt32(countLoc)){ - setInt32(0,tempLoc); - setInt64(0,tempLoc+4); - setInt32(1,tempLoc+12); + if (i + 1 > getInt32(countLoc)){ + setInt32(0, tempLoc); + setInt64(0, tempLoc + 4); + setInt32(1, tempLoc + 12); + } + if (getInt32(tempLoc + 12) == 0){ + tempLoc += 17; + }else{ + tempLoc += 16; } - if (getInt32(tempLoc+12) == 0){tempLoc += 17;}else{tempLoc += 16;} } - setInt32(newRun.firstFragment,tempLoc); - setInt64(newRun.firstTimestamp,tempLoc+4); - setInt32(newRun.duration,tempLoc+12); + setInt32(newRun.firstFragment, tempLoc); + setInt64(newRun.firstTimestamp, tempLoc + 4); + setInt32(newRun.duration, tempLoc + 12); if (newRun.duration == 0){ - setInt8(newRun.discontinuity,tempLoc+16); + setInt8(newRun.discontinuity, tempLoc + 16); + } + if (getInt32(countLoc) < no + 1){ + setInt32(no + 1, countLoc); } - if (getInt32(countLoc) < no+1){setInt32(no+1, countLoc);} } - - afrt_runtable AFRT::getFragmentRun( long no ) { + + afrt_runtable AFRT::getFragmentRun(long no){ afrt_runtable res; - if( no > getFragmentRunCount() ){return res;} + if (no > getFragmentRunCount()){ + return res; + } int tempLoc = 9; - for( int i = 0; i < getQualityEntryCount(); i++ ){ - tempLoc += getStringLen(tempLoc)+1; + for (int i = 0; i < getQualityEntryCount(); i++){ + tempLoc += getStringLen(tempLoc) + 1; } int countLoc = tempLoc; tempLoc += 4; for (int i = 0; i < no; i++){ - if (getInt32(tempLoc+12) == 0){tempLoc += 17;}else{tempLoc += 16;} + 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.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(int 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; + r << std::string(indent + 1, ' ') << "Version " << (int)getVersion() << std::endl; if (getUpdate()){ - r << std::string(indent+1, ' ') << "Update" << std::endl; + 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, ' ') << "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( int i = 0; i < getQualityEntryCount(); i++ ) { - r << std::string(indent+2, ' ') << i << ": " << getQualityEntry(i) << std::endl; + r << std::string(indent + 1, ' ') << "Timescale " << getTimeScale() << std::endl; + r << std::string(indent + 1, ' ') << "QualitySegmentUrlModifiers (" << getQualityEntryCount() << ")" << std::endl; + for (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( int i = 0; i < getFragmentRunCount(); i ++ ) { + r << std::string(indent + 1, ' ') << "FragmentRunEntryTable (" << getFragmentRunCount() << ")" << std::endl; + for (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; + 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; + 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 ); + setVersion(0); + setUpdate(0); } - - void ASRT::setVersion( char newVersion ) { - setInt8( newVersion, 0 ); + + void ASRT::setVersion(char newVersion){ + setInt8(newVersion, 0); } - - long ASRT::getVersion(){return getInt8(0);} - - void ASRT::setUpdate( long newUpdate ) { - setInt24( newUpdate, 1 ); + + long ASRT::getVersion(){ + return getInt8(0); } - - long ASRT::getUpdate(){return getInt24(1);} - + + void ASRT::setUpdate(long newUpdate){ + setInt24(newUpdate, 1); + } + + long ASRT::getUpdate(){ + return getInt24(1); + } + long ASRT::getQualityEntryCount(){ return getInt8(4); } - + void ASRT::setQualityEntry(std::string & newEntry, long no){ int countLoc = 4; - int tempLoc = countLoc+1; + int tempLoc = countLoc + 1; //attempt to reach the wanted position int i; for (i = 0; i < getQualityEntryCount() && i < no; ++i){ - tempLoc += getStringLen(tempLoc)+1; + 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; + 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(long no){ - if (no > getQualityEntryCount()){return "";} - int tempLoc = 5;//position of qualityentry count; - for (int i = 0; i < no; i++){tempLoc += getStringLen(tempLoc)+1;} + if (no > getQualityEntryCount()){ + return ""; + } + int tempLoc = 5; //position of qualityentry count; + for (int i = 0; i < no; i++){ + tempLoc += getStringLen(tempLoc) + 1; + } return getString(tempLoc); } - - long ASRT::getSegmentRunEntryCount() { - int tempLoc = 5;//position of qualityentry count; - for (int i = 0; i < getQualityEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} + + long ASRT::getSegmentRunEntryCount(){ + int tempLoc = 5; //position of qualityentry count; + for (int i = 0; i < getQualityEntryCount(); i++){ + tempLoc += getStringLen(tempLoc) + 1; + } return getInt32(tempLoc); } - - void ASRT::setSegmentRun( long firstSegment, long fragmentsPerSegment, long no ) { - int tempLoc = 5;//position of qualityentry count; - for (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 + + void ASRT::setSegmentRun(long firstSegment, long fragmentsPerSegment, long no){ + int tempLoc = 5; //position of qualityentry count; + for (int i = 0; i < getQualityEntryCount(); i++){ + tempLoc += getStringLen(tempLoc) + 1; } - setInt32(firstSegment,tempLoc); - setInt32(fragmentsPerSegment,tempLoc+4); - } - - asrt_runtable ASRT::getSegmentRun( long no ) { - asrt_runtable res; - if (no >= getSegmentRunEntryCount()){return res;} - int tempLoc = 5;//position of qualityentry count; - for (int i = 0; i < getQualityEntryCount(); ++i){tempLoc += getStringLen(tempLoc)+1;} int countLoc = tempLoc; - tempLoc += 4 + 8*no; + 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(long no){ + asrt_runtable res; + if (no >= getSegmentRunEntryCount()){ + return res; + } + int tempLoc = 5; //position of qualityentry count; + for (int i = 0; i < getQualityEntryCount(); ++i){ + tempLoc += getStringLen(tempLoc) + 1; + } + int countLoc = tempLoc; + tempLoc += 4 + 8 * no; res.firstSegment = getInt32(tempLoc); - res.fragmentsPerSegment = getInt32(tempLoc+4); + res.fragmentsPerSegment = getInt32(tempLoc + 4); return res; } - + std::string ASRT::toPrettyString(int indent){ std::stringstream r; r << std::string(indent, ' ') << "[asrt] Segment Run Table (" << boxedSize() << ")" << std::endl; - r << std::string(indent+1, ' ') << "Version " << getVersion() << std::endl; + r << std::string(indent + 1, ' ') << "Version " << getVersion() << std::endl; if (getUpdate()){ - r << std::string(indent+1, ' ') << "Update" << std::endl; + 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, ' ') << "Replacement or new table" << std::endl; } - r << std::string(indent+1, ' ') << "QualityEntryTable (" << getQualityEntryCount() << ")" << std::endl; - for( int i = 0; i < getQualityEntryCount(); i++ ) { - r << std::string(indent+2, ' ') << i << ": " << getQualityEntry(i) << std::endl; + r << std::string(indent + 1, ' ') << "QualityEntryTable (" << getQualityEntryCount() << ")" << std::endl; + for (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( int i = 0; i < getSegmentRunEntryCount(); i ++ ) { - r << std::string(indent+2, ' ') << i << ": First=" << getSegmentRun(i).firstSegment << ", FragmentsPerSegment=" << getSegmentRun(i).fragmentsPerSegment << std::endl; + r << std::string(indent + 1, ' ') << "SegmentRunEntryTable (" << getSegmentRunEntryCount() << ")" << std::endl; + for (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(); } - + MFHD::MFHD(){ memcpy(data + 4, "mfhd", 4); - setInt32(0,0); + setInt32(0, 0); } - - void MFHD::setSequenceNumber(long newSequenceNumber){setInt32(newSequenceNumber, 4);} - - long MFHD::getSequenceNumber(){return getInt32(4);} - - std::string MFHD::toPrettyString( int indent ) { + + void MFHD::setSequenceNumber(long newSequenceNumber){ + setInt32(newSequenceNumber, 4); + } + + long MFHD::getSequenceNumber(){ + return getInt32(4); + } + + std::string MFHD::toPrettyString(int indent){ std::stringstream r; r << std::string(indent, ' ') << "[mfhd] Movie Fragment Header (" << boxedSize() << ")" << std::endl; - r << std::string(indent+1, ' ') << "SequenceNumber " << getSequenceNumber() << std::endl; + r << std::string(indent + 1, ' ') << "SequenceNumber " << getSequenceNumber() << std::endl; return r.str(); } - + MOOF::MOOF(){ memcpy(data + 4, "moof", 4); } - - long MOOF::getContentCount() { + + long MOOF::getContentCount(){ int res = 0; int tempLoc = 0; - while (tempLoc < boxedSize()-8){ + while (tempLoc < boxedSize() - 8){ res++; tempLoc += getBoxLen(tempLoc); } return res; } - + void MOOF::setContent(Box & newContent, long no){ int tempLoc = 0; int contentCount = getContentCount(); for (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; + }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 & MOOF::getContent(long no){ static Box ret = Box((char*)"\000\000\000\010erro", false); - if (no > getContentCount()){return ret;} + if (no > getContentCount()){ + return ret; + } int i = 0; int tempLoc = 0; while (i < no){ @@ -984,54 +1224,58 @@ namespace MP4{ } return getBox(tempLoc); } - - std::string MOOF::toPrettyString( int indent ) { + + std::string MOOF::toPrettyString(int indent){ std::stringstream r; r << std::string(indent, ' ') << "[moof] Movie Fragment Box (" << boxedSize() << ")" << std::endl; Box curBox; int tempLoc = 0; int contentCount = getContentCount(); - for( int i = 0; i < contentCount; i++ ) { + for (int i = 0; i < contentCount; i++){ curBox = getContent(i); - r << curBox.toPrettyString(indent+1); + r << curBox.toPrettyString(indent + 1); tempLoc += getBoxLen(tempLoc); } return r.str(); } - + TRAF::TRAF(){ memcpy(data + 4, "traf", 4); } - long TRAF::getContentCount() { + long TRAF::getContentCount(){ int res = 0; int tempLoc = 0; - while (tempLoc < boxedSize()-8){ + while (tempLoc < boxedSize() - 8){ res++; tempLoc += getBoxLen(tempLoc); } return res; } - + void TRAF::setContent(Box & newContent, long no){ int tempLoc = 0; int contentCount = getContentCount(); for (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; + }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 & TRAF::getContent( long no ){ + + Box & TRAF::getContent(long no){ static Box ret = Box((char*)"\000\000\000\010erro", false); - if (no > getContentCount()){return ret;} + if (no > getContentCount()){ + return ret; + } int i = 0; int tempLoc = 0; while (i < no){ @@ -1040,30 +1284,27 @@ namespace MP4{ } return getBox(tempLoc); } - - std::string TRAF::toPrettyString( int indent ) { + + std::string TRAF::toPrettyString(int indent){ std::stringstream r; r << std::string(indent, ' ') << "[traf] Track Fragment Box (" << boxedSize() << ")" << std::endl; Box curBox; int tempLoc = 0; int contentCount = getContentCount(); - for( int i = 0; i < contentCount; i++ ) { + for (int i = 0; i < contentCount; i++){ curBox = getContent(i); - r << curBox.toPrettyString(indent+1); + r << curBox.toPrettyString(indent + 1); tempLoc += curBox.boxedSize(); } return r.str(); } - - - TRUN::TRUN(){ memcpy(data + 4, "trun", 4); } void TRUN::setFlags(long newFlags){ - setInt24(newFlags,1); + setInt24(newFlags, 1); } long TRUN::getFlags(){ @@ -1085,7 +1326,9 @@ namespace MP4{ } void TRUN::setFirstSampleFlags(long newSampleFlags){ - if (!(getFlags() & trunfirstSampleFlags)){return;} + if ( !(getFlags() & trunfirstSampleFlags)){ + return; + } if (getFlags() & trundataOffset){ setInt32(newSampleFlags, 12); }else{ @@ -1094,7 +1337,9 @@ namespace MP4{ } long TRUN::getFirstSampleFlags(){ - if (!(getFlags() & trunfirstSampleFlags)){return 0;} + if ( !(getFlags() & trunfirstSampleFlags)){ + return 0; + } if (getFlags() & trundataOffset){ return getInt32(12); }else{ @@ -1109,32 +1354,44 @@ namespace MP4{ void TRUN::setSampleInformation(trunSampleInformation newSample, long no){ long flags = getFlags(); long sampInfoSize = 0; - if (flags & trunsampleDuration){sampInfoSize += 4;} - if (flags & trunsampleSize){sampInfoSize += 4;} - if (flags & trunsampleFlags){sampInfoSize += 4;} - if (flags & trunsampleOffsets){sampInfoSize += 4;} + if (flags & trunsampleDuration){ + sampInfoSize += 4; + } + if (flags & trunsampleSize){ + sampInfoSize += 4; + } + if (flags & trunsampleFlags){ + sampInfoSize += 4; + } + if (flags & trunsampleOffsets){ + sampInfoSize += 4; + } long offset = 8; - if (flags & trundataOffset){offset += 4;} - if (flags & trunfirstSampleFlags){offset += 4;} + if (flags & trundataOffset){ + offset += 4; + } + if (flags & trunfirstSampleFlags){ + offset += 4; + } long innerOffset = 0; if (flags & trunsampleDuration){ - setInt32(newSample.sampleDuration, offset + no*sampInfoSize + innerOffset); + setInt32(newSample.sampleDuration, offset + no * sampInfoSize + innerOffset); innerOffset += 4; } if (flags & trunsampleSize){ - setInt32(newSample.sampleSize, offset + no*sampInfoSize + innerOffset); + setInt32(newSample.sampleSize, offset + no * sampInfoSize + innerOffset); innerOffset += 4; } if (flags & trunsampleFlags){ - setInt32(newSample.sampleFlags, offset + no*sampInfoSize + innerOffset); + setInt32(newSample.sampleFlags, offset + no * sampInfoSize + innerOffset); innerOffset += 4; } if (flags & trunsampleOffsets){ - setInt32(newSample.sampleOffset, offset + no*sampInfoSize + innerOffset); + setInt32(newSample.sampleOffset, offset + no * sampInfoSize + innerOffset); innerOffset += 4; } - if (getSampleInformationCount() < no+1){ - setInt32(no+1,4); + if (getSampleInformationCount() < no + 1){ + setInt32(no + 1, 4); } } @@ -1144,31 +1401,45 @@ namespace MP4{ ret.sampleSize = 0; ret.sampleFlags = 0; ret.sampleOffset = 0; - if (getSampleInformationCount() < no+1){return ret;} + if (getSampleInformationCount() < no + 1){ + return ret; + } long flags = getFlags(); long sampInfoSize = 0; - if (flags & trunsampleDuration){sampInfoSize += 4;} - if (flags & trunsampleSize){sampInfoSize += 4;} - if (flags & trunsampleFlags){sampInfoSize += 4;} - if (flags & trunsampleOffsets){sampInfoSize += 4;} + if (flags & trunsampleDuration){ + sampInfoSize += 4; + } + if (flags & trunsampleSize){ + sampInfoSize += 4; + } + if (flags & trunsampleFlags){ + sampInfoSize += 4; + } + if (flags & trunsampleOffsets){ + sampInfoSize += 4; + } long offset = 8; - if (flags & trundataOffset){offset += 4;} - if (flags & trunfirstSampleFlags){offset += 4;} + if (flags & trundataOffset){ + offset += 4; + } + if (flags & trunfirstSampleFlags){ + offset += 4; + } long innerOffset = 0; if (flags & trunsampleDuration){ - ret.sampleDuration = getInt32(offset + no*sampInfoSize + innerOffset); + ret.sampleDuration = getInt32(offset + no * sampInfoSize + innerOffset); innerOffset += 4; } if (flags & trunsampleSize){ - ret.sampleSize = getInt32(offset + no*sampInfoSize + innerOffset); + ret.sampleSize = getInt32(offset + no * sampInfoSize + innerOffset); innerOffset += 4; } if (flags & trunsampleFlags){ - ret.sampleFlags = getInt32(offset + no*sampInfoSize + innerOffset); + ret.sampleFlags = getInt32(offset + no * sampInfoSize + innerOffset); innerOffset += 4; } if (flags & trunsampleOffsets){ - ret.sampleOffset = getInt32(offset + no*sampInfoSize + innerOffset); + ret.sampleOffset = getInt32(offset + no * sampInfoSize + innerOffset); innerOffset += 4; } return ret; @@ -1177,36 +1448,52 @@ namespace MP4{ std::string TRUN::toPrettyString(long indent){ std::stringstream r; r << std::string(indent, ' ') << "[trun] Track Fragment Run (" << boxedSize() << ")" << std::endl; - r << std::string(indent+1, ' ') << "Version " << (int)getInt8(0) << std::endl; - - long flags = getFlags(); - r << std::string(indent+1, ' ') << "Flags"; - if (flags & trundataOffset){r << " dataOffset";} - if (flags & trunfirstSampleFlags){r << " firstSampleFlags";} - if (flags & trunsampleDuration){r << " sampleDuration";} - if (flags & trunsampleSize){r << " sampleSize";} - if (flags & trunsampleFlags){r << " sampleFlags";} - if (flags & trunsampleOffsets){r << " sampleOffsets";} - r << std::endl; - - if (flags & trundataOffset){r << std::string(indent+1, ' ') << "Data Offset " << getDataOffset() << std::endl;} - if (flags & trundataOffset){r << std::string(indent+1, ' ') << "Sample Flags" << prettySampleFlags(getFirstSampleFlags()) << std::endl;} + r << std::string(indent + 1, ' ') << "Version " << (int)getInt8(0) << std::endl; - r << std::string(indent+1, ' ') << "SampleInformation (" << getSampleInformationCount() << "):" << std::endl; + long flags = getFlags(); + r << std::string(indent + 1, ' ') << "Flags"; + if (flags & trundataOffset){ + r << " dataOffset"; + } + if (flags & trunfirstSampleFlags){ + r << " firstSampleFlags"; + } + if (flags & trunsampleDuration){ + r << " sampleDuration"; + } + if (flags & trunsampleSize){ + r << " sampleSize"; + } + if (flags & trunsampleFlags){ + r << " sampleFlags"; + } + if (flags & trunsampleOffsets){ + r << " sampleOffsets"; + } + r << std::endl; + + if (flags & trundataOffset){ + r << std::string(indent + 1, ' ') << "Data Offset " << getDataOffset() << std::endl; + } + if (flags & trundataOffset){ + r << std::string(indent + 1, ' ') << "Sample Flags" << prettySampleFlags(getFirstSampleFlags()) << std::endl; + } + + r << std::string(indent + 1, ' ') << "SampleInformation (" << getSampleInformationCount() << "):" << std::endl; for (int i = 0; i < getSampleInformationCount(); ++i){ - r << std::string(indent+2, ' ') << "[" << i << "]" << std::endl; + r << std::string(indent + 2, ' ') << "[" << i << "]" << std::endl; trunSampleInformation samp = getSampleInformation(i); if (flags & trunsampleDuration){ - r << std::string(indent+2, ' ') << "Duration " << samp.sampleDuration << std::endl; + r << std::string(indent + 2, ' ') << "Duration " << samp.sampleDuration << std::endl; } if (flags & trunsampleSize){ - r << std::string(indent+2, ' ') << "Size " << samp.sampleSize << std::endl; + r << std::string(indent + 2, ' ') << "Size " << samp.sampleSize << std::endl; } if (flags & trunsampleFlags){ - r << std::string(indent+2, ' ') << "Flags " << prettySampleFlags(samp.sampleFlags) << std::endl; + r << std::string(indent + 2, ' ') << "Flags " << prettySampleFlags(samp.sampleFlags) << std::endl; } if (flags & trunsampleOffsets){ - r << std::string(indent+2, ' ') << "Offset " << samp.sampleOffset << std::endl; + r << std::string(indent + 2, ' ') << "Offset " << samp.sampleOffset << std::endl; } } @@ -1215,13 +1502,29 @@ namespace MP4{ std::string prettySampleFlags(long flag){ std::stringstream r; - if (flag & noIPicture){r << " noIPicture";} - if (flag & isIPicture){r << " isIPicture";} - if (flag & noDisposable){r << " noDisposable";} - if (flag & isDisposable){r << " isDisposable";} - if (flag & isRedundant){r << " isRedundant";} - if (flag & noRedundant){r << " noRedundant";} - if (flag & noKeySample){r << " noKeySample";}else{r << " isKeySample";} + if (flag & noIPicture){ + r << " noIPicture"; + } + if (flag & isIPicture){ + r << " isIPicture"; + } + if (flag & noDisposable){ + r << " noDisposable"; + } + if (flag & isDisposable){ + r << " isDisposable"; + } + if (flag & isRedundant){ + r << " isRedundant"; + } + if (flag & noRedundant){ + r << " noRedundant"; + } + if (flag & noKeySample){ + r << " noKeySample"; + }else{ + r << " isKeySample"; + } return r.str(); } @@ -1230,7 +1533,7 @@ namespace MP4{ } void TFHD::setFlags(long newFlags){ - setInt24(newFlags,1); + setInt24(newFlags, 1); } long TFHD::getFlags(){ @@ -1238,7 +1541,7 @@ namespace MP4{ } void TFHD::setTrackID(long newID){ - setInt32(newID,4); + setInt32(newID, 4); } long TFHD::getTrackID(){ @@ -1250,7 +1553,7 @@ namespace MP4{ setInt64(newOffset, 8); } } - + long long TFHD::getBaseDataOffset(){ if (getFlags() & tfhdBaseOffset){ return getInt64(8); @@ -1258,123 +1561,201 @@ namespace MP4{ return 0; } } - + void TFHD::setSampleDescriptionIndex(long newIndex){ - if (!(getFlags() & tfhdSampleDesc)){return;} + if ( !(getFlags() & tfhdSampleDesc)){ + return; + } int offset = 8; - if (getFlags() & tfhdBaseOffset){offset += 8;} + if (getFlags() & tfhdBaseOffset){ + offset += 8; + } setInt32(newIndex, offset); } - + long TFHD::getSampleDescriptionIndex(){ - if (!(getFlags() & tfhdSampleDesc)){return 0;} + if ( !(getFlags() & tfhdSampleDesc)){ + return 0; + } int offset = 8; - if (getFlags() & tfhdBaseOffset){offset += 8;} + if (getFlags() & tfhdBaseOffset){ + offset += 8; + } return getInt32(offset); } - + void TFHD::setDefaultSampleDuration(long newDuration){ - if (!(getFlags() & tfhdSampleDura)){return;} + if ( !(getFlags() & tfhdSampleDura)){ + return; + } int offset = 8; - if (getFlags() & tfhdBaseOffset){offset += 8;} - if (getFlags() & tfhdSampleDesc){offset += 4;} + if (getFlags() & tfhdBaseOffset){ + offset += 8; + } + if (getFlags() & tfhdSampleDesc){ + offset += 4; + } setInt32(newDuration, offset); } - + long TFHD::getDefaultSampleDuration(){ - if (!(getFlags() & tfhdSampleDura)){return 0;} + if ( !(getFlags() & tfhdSampleDura)){ + return 0; + } int offset = 8; - if (getFlags() & tfhdBaseOffset){offset += 8;} - if (getFlags() & tfhdSampleDesc){offset += 4;} + if (getFlags() & tfhdBaseOffset){ + offset += 8; + } + if (getFlags() & tfhdSampleDesc){ + offset += 4; + } return getInt32(offset); } - + void TFHD::setDefaultSampleSize(long newSize){ - if (!(getFlags() & tfhdSampleSize)){return;} + if ( !(getFlags() & tfhdSampleSize)){ + return; + } int offset = 8; - if (getFlags() & tfhdBaseOffset){offset += 8;} - if (getFlags() & tfhdSampleDesc){offset += 4;} - if (getFlags() & tfhdSampleDura){offset += 4;} + if (getFlags() & tfhdBaseOffset){ + offset += 8; + } + if (getFlags() & tfhdSampleDesc){ + offset += 4; + } + if (getFlags() & tfhdSampleDura){ + offset += 4; + } setInt32(newSize, offset); } - + long TFHD::getDefaultSampleSize(){ - if (!(getFlags() & tfhdSampleSize)){return 0;} + if ( !(getFlags() & tfhdSampleSize)){ + return 0; + } int offset = 8; - if (getFlags() & tfhdBaseOffset){offset += 8;} - if (getFlags() & tfhdSampleDesc){offset += 4;} - if (getFlags() & tfhdSampleDura){offset += 4;} + if (getFlags() & tfhdBaseOffset){ + offset += 8; + } + if (getFlags() & tfhdSampleDesc){ + offset += 4; + } + if (getFlags() & tfhdSampleDura){ + offset += 4; + } return getInt32(offset); } - + void TFHD::setDefaultSampleFlags(long newFlags){ - if (!(getFlags() & tfhdSampleFlag)){return;} + if ( !(getFlags() & tfhdSampleFlag)){ + return; + } int offset = 8; - if (getFlags() & tfhdBaseOffset){offset += 8;} - if (getFlags() & tfhdSampleDesc){offset += 4;} - if (getFlags() & tfhdSampleDura){offset += 4;} - if (getFlags() & tfhdSampleSize){offset += 4;} + if (getFlags() & tfhdBaseOffset){ + offset += 8; + } + if (getFlags() & tfhdSampleDesc){ + offset += 4; + } + if (getFlags() & tfhdSampleDura){ + offset += 4; + } + if (getFlags() & tfhdSampleSize){ + offset += 4; + } setInt32(newFlags, offset); } - + long TFHD::getDefaultSampleFlags(){ - if (!(getFlags() & tfhdSampleFlag)){return 0;} + if ( !(getFlags() & tfhdSampleFlag)){ + return 0; + } int offset = 8; - if (getFlags() & tfhdBaseOffset){offset += 8;} - if (getFlags() & tfhdSampleDesc){offset += 4;} - if (getFlags() & tfhdSampleDura){offset += 4;} - if (getFlags() & tfhdSampleSize){offset += 4;} + if (getFlags() & tfhdBaseOffset){ + offset += 8; + } + if (getFlags() & tfhdSampleDesc){ + offset += 4; + } + if (getFlags() & tfhdSampleDura){ + offset += 4; + } + if (getFlags() & tfhdSampleSize){ + offset += 4; + } return getInt32(offset); } - + std::string TFHD::toPrettyString(long indent){ std::stringstream r; r << std::string(indent, ' ') << "[tfhd] Track Fragment Header (" << boxedSize() << ")" << std::endl; - r << std::string(indent+1, ' ') << "Version " << (int)getInt8(0) << std::endl; - - long flags = getFlags(); - r << std::string(indent+1, ' ') << "Flags"; - if (flags & tfhdBaseOffset){r << " BaseOffset";} - if (flags & tfhdSampleDesc){r << " SampleDesc";} - if (flags & tfhdSampleDura){r << " SampleDura";} - if (flags & tfhdSampleSize){r << " SampleSize";} - if (flags & tfhdSampleFlag){r << " SampleFlag";} - if (flags & tfhdNoDuration){r << " NoDuration";} - r << std::endl; - - r << std::string(indent+1, ' ') << "TrackID " << getTrackID() << std::endl; + r << std::string(indent + 1, ' ') << "Version " << (int)getInt8(0) << std::endl; + + long flags = getFlags(); + r << std::string(indent + 1, ' ') << "Flags"; + if (flags & tfhdBaseOffset){ + r << " BaseOffset"; + } + if (flags & tfhdSampleDesc){ + r << " SampleDesc"; + } + if (flags & tfhdSampleDura){ + r << " SampleDura"; + } + if (flags & tfhdSampleSize){ + r << " SampleSize"; + } + if (flags & tfhdSampleFlag){ + r << " SampleFlag"; + } + if (flags & tfhdNoDuration){ + r << " NoDuration"; + } + r << std::endl; + + r << std::string(indent + 1, ' ') << "TrackID " << getTrackID() << std::endl; + + if (flags & tfhdBaseOffset){ + r << std::string(indent + 1, ' ') << "Base Offset " << getBaseDataOffset() << std::endl; + } + if (flags & tfhdSampleDesc){ + r << std::string(indent + 1, ' ') << "Sample Description Index " << getSampleDescriptionIndex() << std::endl; + } + if (flags & tfhdSampleDura){ + r << std::string(indent + 1, ' ') << "Default Sample Duration " << getDefaultSampleDuration() << std::endl; + } + if (flags & tfhdSampleSize){ + r << std::string(indent + 1, ' ') << "Default Same Size " << getDefaultSampleSize() << std::endl; + } + if (flags & tfhdSampleFlag){ + r << std::string(indent + 1, ' ') << "Default Sample Flags " << prettySampleFlags(getDefaultSampleFlags()) << std::endl; + } - if (flags & tfhdBaseOffset){r << std::string(indent+1, ' ') << "Base Offset " << getBaseDataOffset() << std::endl;} - if (flags & tfhdSampleDesc){r << std::string(indent+1, ' ') << "Sample Description Index " << getSampleDescriptionIndex() << std::endl;} - if (flags & tfhdSampleDura){r << std::string(indent+1, ' ') << "Default Sample Duration " << getDefaultSampleDuration() << std::endl;} - if (flags & tfhdSampleSize){r << std::string(indent+1, ' ') << "Default Same Size " << getDefaultSampleSize() << std::endl;} - if (flags & tfhdSampleFlag){r << std::string(indent+1, ' ') << "Default Sample Flags " << prettySampleFlags(getDefaultSampleFlags()) << std::endl;} - return r.str(); } AFRA::AFRA(){ memcpy(data + 4, "afra", 4); - setInt32(0, 9);//entrycount = 0 + setInt32(0, 9); //entrycount = 0 setFlags(0); } - + void AFRA::setVersion(long newVersion){ setInt8(newVersion, 0); } - + long AFRA::getVersion(){ return getInt8(0); } - + void AFRA::setFlags(long newFlags){ setInt24(newFlags, 1); } - + long AFRA::getFlags(){ return getInt24(1); } - + void AFRA::setLongIDs(bool newVal){ if (newVal){ setInt8((getInt8(4) & 0x7F) + 0x80, 4); @@ -1382,7 +1763,7 @@ namespace MP4{ setInt8((getInt8(4) & 0x7F), 4); } } - + bool AFRA::getLongIDs(){ return getInt8(4) & 0x80; } @@ -1394,11 +1775,11 @@ namespace MP4{ 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); @@ -1406,103 +1787,127 @@ namespace MP4{ setInt8((getInt8(4) & 0xDF), 4); } } - + bool AFRA::getGlobalEntries(){ return getInt8(4) & 0x20; } - + void AFRA::setTimeScale(long newVal){ setInt32(newVal, 5); } - + long AFRA::getTimeScale(){ return getInt32(5); } - + long AFRA::getEntryCount(){ return getInt32(9); } - + void AFRA::setEntry(afraentry newEntry, long 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); + 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); } - if (no+1 > getEntryCount()){setInt32(no+1, 9);} } - + afraentry AFRA::getEntry(long no){ afraentry ret; int entrysize = 12; - if (getLongOffsets()){entrysize = 16;} - ret.time = getInt64(13+entrysize*no); if (getLongOffsets()){ - ret.offset = getInt64(21+entrysize*no); + entrysize = 16; + } + ret.time = getInt64(13 + entrysize * no); + if (getLongOffsets()){ + ret.offset = getInt64(21 + entrysize * no); }else{ - ret.offset = getInt32(21+entrysize*no); + ret.offset = getInt32(21 + entrysize * no); } return ret; } - + long AFRA::getGlobalEntryCount(){ - if (!getGlobalEntries()){return 0;} + if ( !getGlobalEntries()){ + return 0; + } int entrysize = 12; - if (getLongOffsets()){entrysize = 16;} - return getInt32(13+entrysize*getEntryCount()); + if (getLongOffsets()){ + entrysize = 16; + } + return getInt32(13 + entrysize * getEntryCount()); } void AFRA::setGlobalEntry(globalafraentry newEntry, long no){ - int offset = 13+12*getEntryCount()+4; - if (getLongOffsets()){offset = 13+16*getEntryCount()+4;} + 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); + entrysize += 4; } 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); + 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); } - - if (getInt32(offset-4) < no+1){setInt32(no+1, offset-4);} } globalafraentry AFRA::getGlobalEntry(long no){ globalafraentry ret; - int offset = 13+12*getEntryCount()+4; - if (getLongOffsets()){offset = 13+16*getEntryCount()+4;} + 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); + entrysize += 4; } if (getLongOffsets()){ - ret.afraoffset = getInt64(offset+entrysize*no+entrysize-16); - ret.offsetfromafra = getInt64(offset+entrysize*no+entrysize-8); + 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.afraoffset = getInt32(offset+entrysize*no+entrysize-8); - ret.offsetfromafra = getInt32(offset+entrysize*no+entrysize-4); + 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; } @@ -1510,165 +1915,168 @@ namespace MP4{ std::string AFRA::toPrettyString(long 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; + 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; long count = getEntryCount(); - r << std::string(indent+1, ' ') << "Entries (" << count << ") " << std::endl; + r << std::string(indent + 1, ' ') << "Entries (" << count << ") " << std::endl; for (long i = 0; i < count; ++i){ afraentry tmpent = getEntry(i); - r << std::string(indent+1, ' ') << i << ": Time " << tmpent.time << ", Offset " << tmpent.offset << std::endl; + 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; + r << std::string(indent + 1, ' ') << "Global Entries (" << count << ") " << std::endl; for (long 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; + r << std::string(indent + 1, ' ') << i << ": T " << tmpent.time << ", S" << tmpent.segment << "F" << tmpent.fragment << ", " + << tmpent.afraoffset << "/" << tmpent.offsetfromafra << std::endl; } } - + return r.str(); } - AVCC::AVCC() { + AVCC::AVCC(){ memcpy(data + 4, "avcC", 4); - setInt8( 0xFF, 4 );//reserved + 4-bytes NAL length + setInt8(0xFF, 4); //reserved + 4-bytes NAL length } - - void AVCC::setVersion( long newVersion ) { - setInt8( newVersion, 0 ); + + void AVCC::setVersion(long newVersion){ + setInt8(newVersion, 0); } - - long AVCC::getVersion( ) { - return getInt8( 0 ); + + long AVCC::getVersion(){ + return getInt8(0); } - - void AVCC::setProfile( long newProfile ) { - setInt8( newProfile, 1 ); + + void AVCC::setProfile(long newProfile){ + setInt8(newProfile, 1); } - - long AVCC::getProfile( ) { - return getInt8( 1 ); + + long AVCC::getProfile(){ + return getInt8(1); } - - void AVCC::setCompatibleProfiles( long newCompatibleProfiles ) { - setInt8( newCompatibleProfiles, 2 ); + + void AVCC::setCompatibleProfiles(long newCompatibleProfiles){ + setInt8(newCompatibleProfiles, 2); } - - long AVCC::getCompatibleProfiles( ) { - return getInt8( 2 ); + + long AVCC::getCompatibleProfiles(){ + return getInt8(2); } - - void AVCC::setLevel( long newLevel ) { - setInt8( newLevel, 3 ); + + void AVCC::setLevel(long newLevel){ + setInt8(newLevel, 3); } - - long AVCC::getLevel( ) { - return getInt8( 3 ); + + long AVCC::getLevel(){ + return getInt8(3); } - - void AVCC::setSPSNumber( long newSPSNumber ) { - setInt8( newSPSNumber, 5 ); + + void AVCC::setSPSNumber(long newSPSNumber){ + setInt8(newSPSNumber, 5); } - - long AVCC::getSPSNumber( ) { - return getInt8( 5 ); + + long AVCC::getSPSNumber(){ + return getInt8(5); } - - void AVCC::setSPS( std::string newSPS ) { - setInt16( newSPS.size(), 6 ); - for( int i = 0; i < newSPS.size(); i++ ) { - setInt8( newSPS[i], 8+i ); - }//not null-terminated + + void AVCC::setSPS(std::string newSPS){ + setInt16(newSPS.size(), 6); + for (int i = 0; i < newSPS.size(); i++){ + setInt8(newSPS[i], 8 + i); + } //not null-terminated } - - long AVCC::getSPSLen( ) { - return getInt16( 6 ); + + long AVCC::getSPSLen(){ + return getInt16(6); } - - char* AVCC::getSPS( ) { + + char* AVCC::getSPS(){ return payload() + 8; } - - void AVCC::setPPSNumber( long newPPSNumber ) { - int offset = 8 + getSPSLen( ); - setInt8( newPPSNumber, offset ); + + void AVCC::setPPSNumber(long newPPSNumber){ + int offset = 8 + getSPSLen(); + setInt8(newPPSNumber, offset); } - - long AVCC::getPPSNumber( ) { - int offset = 8 + getSPSLen( ); - return getInt8( offset ); + + long AVCC::getPPSNumber(){ + int offset = 8 + getSPSLen(); + return getInt8(offset); } - - void AVCC::setPPS( std::string newPPS ) { - int offset = 8 + getSPSLen( ) + 1; - setInt16( newPPS.size(), offset ); - for( int i = 0; i < newPPS.size(); i++ ) { - setInt8( newPPS[i], offset+2+i ); - }//not null-terminated + + void AVCC::setPPS(std::string newPPS){ + int offset = 8 + getSPSLen() + 1; + setInt16(newPPS.size(), offset); + for (int i = 0; i < newPPS.size(); i++){ + setInt8(newPPS[i], offset + 2 + i); + } //not null-terminated } - - long AVCC::getPPSLen( ) { - int offset = 8 + getSPSLen( ) + 1; - return getInt16( offset ); + + long AVCC::getPPSLen(){ + int offset = 8 + getSPSLen() + 1; + return getInt16(offset); } - - char* AVCC::getPPS( ) { - int offset = 8 + getSPSLen( ) + 3; + + char* AVCC::getPPS(){ + int offset = 8 + getSPSLen() + 3; return payload() + offset; } - - std::string AVCC::toPrettyString(long indent) { + + std::string AVCC::toPrettyString(long indent){ std::stringstream r; r << std::string(indent, ' ') << "[avcC] H.264 Init Data (" << boxedSize() << ")" << std::endl; - r << std::string(indent+1, ' ') << "Version: " << getVersion( ) << std::endl; - r << std::string(indent+1, ' ') << "Profile: " << getProfile( ) << std::endl; - r << std::string(indent+1, ' ') << "Compatible Profiles: " << getCompatibleProfiles( ) << std::endl; - r << std::string(indent+1, ' ') << "Level: " << getLevel( ) << std::endl; - r << std::string(indent+1, ' ') << "SPS Number: " << getSPSNumber( ) << std::endl; - r << std::string(indent+2, ' ') << getSPSLen( ) << " of SPS data" << std::endl; - r << std::string(indent+1, ' ') << "PPS Number: " << getPPSNumber( ) << std::endl; - r << std::string(indent+2, ' ') << getPPSLen( ) << " of PPS data" << std::endl; + r << std::string(indent + 1, ' ') << "Version: " << getVersion() << std::endl; + r << std::string(indent + 1, ' ') << "Profile: " << getProfile() << std::endl; + r << std::string(indent + 1, ' ') << "Compatible Profiles: " << getCompatibleProfiles() << std::endl; + r << std::string(indent + 1, ' ') << "Level: " << getLevel() << std::endl; + r << std::string(indent + 1, ' ') << "SPS Number: " << getSPSNumber() << std::endl; + r << std::string(indent + 2, ' ') << getSPSLen() << " of SPS data" << std::endl; + r << std::string(indent + 1, ' ') << "PPS Number: " << getPPSNumber() << std::endl; + r << std::string(indent + 2, ' ') << getPPSLen() << " of PPS data" << std::endl; return r.str(); } - - std::string AVCC::asAnnexB( ) { + + std::string AVCC::asAnnexB(){ std::stringstream r; r << (char)0x00 << (char)0x00 << (char)0x00 << (char)0x01; - r.write( getSPS( ), getSPSLen() ); + r.write(getSPS(), getSPSLen()); r << (char)0x00 << (char)0x00 << (char)0x00 << (char)0x01; - r.write( getPPS( ), getPPSLen() ); + r.write(getPPS(), getPPSLen()); return r.str(); } - - void AVCC::setPayload( std::string newPayload ) { - if( ! reserve( 0, payloadSize(), newPayload.size() ) ) { return; } - memcpy( (char*)payload(), (char*)newPayload.c_str(), newPayload.size() ); + + void AVCC::setPayload(std::string newPayload){ + if ( !reserve(0, payloadSize(), newPayload.size())){ + return; + } + memcpy((char*)payload(), (char*)newPayload.c_str(), newPayload.size()); } - - SDTP::SDTP() { + + SDTP::SDTP(){ memcpy(data + 4, "sdtp", 4); } - - void SDTP::setVersion( long newVersion ) { - setInt8( newVersion, 0 ); + + void SDTP::setVersion(long newVersion){ + setInt8(newVersion, 0); } - - long SDTP::getVersion( ) { - return getInt8( 0 ); + + long SDTP::getVersion(){ + return getInt8(0); } - - void SDTP::setValue( long newValue, size_t index ) { - setInt8( newValue, index ); + + void SDTP::setValue(long newValue, size_t index){ + setInt8(newValue, index); } - - long SDTP::getValue( size_t index ) { - getInt8( index ); + + long SDTP::getValue(size_t index){ + getInt8(index); } -}; +} diff --git a/lib/mp4.h b/lib/mp4.h index 2ac0c2d1..032462ef 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -9,36 +9,36 @@ #include "json.h" /// Contains all MP4 format related code. -namespace MP4{ +namespace MP4 { - class Box { + class Box{ public: Box(char * datapointer = 0, bool manage = true); ~Box(); std::string getType(); - bool isType( char* boxType ); + bool isType(const char* boxType); bool read(std::string & newData); long long int boxedSize(); long long int payloadSize(); char * asBox(); char * payload(); void clear(); - std::string toPrettyString( int indent = 0 ); + std::string toPrettyString(int 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( long newData, size_t index ); - long getInt24( size_t index ); - void setInt32( long newData, size_t index ); - long getInt32( size_t index ); - void setInt64( long long int newData, size_t index ); - long long int getInt64( size_t index ); + 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(long newData, size_t index); + long getInt24(size_t index); + void setInt32(long newData, size_t index); + long getInt32(size_t index); + void setInt64(long long int newData, size_t index); + long long int getInt64(size_t index); //string functions - void setString(std::string newData, size_t index ); - void setString(char* newData, size_t size, size_t index ); + 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 @@ -51,59 +51,63 @@ namespace MP4{ char * data; ///< Holds the data of this box int data_size; ///< Currently reserved size bool managed; ///< If false, will not attempt to resize/free the data pointer. - int payloadOffset;///= 3 std::cerr << "SIGCHLD received, but no child died"; #endif @@ -41,27 +43,29 @@ void Util::Procs::childsig_handler(int signum){ exitcode = WEXITSTATUS(status); }else if (WIFSIGNALED(status)){ exitcode = -WTERMSIG(status); - }else{/* not possible */return;} + }else{/* not possible */ + return; + } - #if DEBUG >= 1 +#if DEBUG >= 1 std::string pname = plist[ret]; - #endif +#endif plist.erase(ret); - #if DEBUG >= 1 +#if DEBUG >= 1 if (isActive(pname)){ Stop(pname); - }else{ + } else{ //can this ever happen? std::cerr << "Process " << pname << " fully terminated." << std::endl; } - #endif +#endif if (exitHandlers.count(ret) > 0){ TerminationNotifier tn = exitHandlers[ret]; exitHandlers.erase(ret); - #if DEBUG >= 2 +#if DEBUG >= 2 std::cerr << "Calling termination handler for " << pname << std::endl; - #endif +#endif tn(ret, exitcode); } } @@ -84,12 +88,14 @@ void Util::Procs::runCmd(std::string & cmd){ ++i; args[i] = tmp2; } - if (i == 20){args[20] = 0;} + if (i == 20){ + args[20] = 0; + } //execute the command execvp(args[0], args); - #if DEBUG >= 1 +#if DEBUG >= 1 std::cerr << "Error running \"" << cmd << "\": " << strerror(errno) << std::endl; - #endif +#endif _exit(42); } @@ -98,11 +104,13 @@ void Util::Procs::runCmd(std::string & cmd){ /// \arg name Name for this process - only used internally. /// \arg cmd Commandline for this process. pid_t Util::Procs::Start(std::string name, std::string cmd){ - if (isActive(name)){return getPid(name);} - if (!handler_set){ + if (isActive(name)){ + return getPid(name); + } + if ( !handler_set){ struct sigaction new_action; new_action.sa_handler = Util::Procs::childsig_handler; - sigemptyset(&new_action.sa_mask); + sigemptyset( &new_action.sa_mask); new_action.sa_flags = 0; sigaction(SIGCHLD, &new_action, NULL); handler_set = true; @@ -112,14 +120,14 @@ pid_t Util::Procs::Start(std::string name, std::string cmd){ runCmd(cmd); }else{ if (ret > 0){ - #if DEBUG >= 1 +#if DEBUG >= 1 std::cerr << "Process " << name << " started, PID " << ret << ": " << cmd << std::endl; - #endif +#endif plist.insert(std::pair(ret, name)); }else{ - #if DEBUG >= 1 +#if DEBUG >= 1 std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl; - #endif +#endif return 0; } } @@ -132,20 +140,22 @@ pid_t Util::Procs::Start(std::string name, std::string cmd){ /// \arg cmd Commandline for sub (sending) process. /// \arg cmd2 Commandline for main (receiving) process. pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2){ - if (isActive(name)){return getPid(name);} - if (!handler_set){ + if (isActive(name)){ + return getPid(name); + } + if ( !handler_set){ struct sigaction new_action; new_action.sa_handler = Util::Procs::childsig_handler; - sigemptyset(&new_action.sa_mask); + sigemptyset( &new_action.sa_mask); new_action.sa_flags = 0; sigaction(SIGCHLD, &new_action, NULL); handler_set = true; } int pfildes[2]; if (pipe(pfildes) == -1){ - #if DEBUG >= 1 +#if DEBUG >= 1 std::cerr << "Process " << name << " could not be started. Pipe creation failed." << std::endl; - #endif +#endif return 0; } @@ -153,7 +163,7 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2){ pid_t ret = fork(); if (ret == 0){ close(pfildes[0]); - dup2(pfildes[1],STDOUT_FILENO); + dup2(pfildes[1], STDOUT_FILENO); close(pfildes[1]); dup2(devnull, STDIN_FILENO); dup2(devnull, STDERR_FILENO); @@ -162,9 +172,9 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2){ if (ret > 0){ plist.insert(std::pair(ret, name)); }else{ - #if DEBUG >= 1 +#if DEBUG >= 1 std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl; - #endif +#endif close(pfildes[1]); close(pfildes[0]); return 0; @@ -174,21 +184,21 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2){ pid_t ret2 = fork(); if (ret2 == 0){ close(pfildes[1]); - dup2(pfildes[0],STDIN_FILENO); + dup2(pfildes[0], STDIN_FILENO); close(pfildes[0]); dup2(devnull, STDOUT_FILENO); dup2(devnull, STDERR_FILENO); runCmd(cmd2); }else{ if (ret2 > 0){ - #if DEBUG >= 1 - std::cerr << "Process " << name << " started, PIDs (" << ret << ", " << ret2 << "): " << cmd << " | " << cmd2 << std::endl; - #endif +#if DEBUG >= 1 + std::cerr << "Process " << name << " started, PIDs (" << ret << ", " << ret2 << "): " << cmd << " | " << cmd2 << std::endl; +#endif plist.insert(std::pair(ret2, name)); }else{ - #if DEBUG >= 1 +#if DEBUG >= 1 std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl; - #endif +#endif Stop(name); close(pfildes[1]); close(pfildes[0]); @@ -207,11 +217,13 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2){ /// \arg cmd2 Commandline for sub (middle) process. /// \arg cmd3 Commandline for main (receiving) process. pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2, std::string cmd3){ - if (isActive(name)){return getPid(name);} - if (!handler_set){ + if (isActive(name)){ + return getPid(name); + } + if ( !handler_set){ struct sigaction new_action; new_action.sa_handler = Util::Procs::childsig_handler; - sigemptyset(&new_action.sa_mask); + sigemptyset( &new_action.sa_mask); new_action.sa_flags = 0; sigaction(SIGCHLD, &new_action, NULL); handler_set = true; @@ -220,23 +232,23 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2, st int pfildes[2]; int pfildes2[2]; if (pipe(pfildes) == -1){ - #if DEBUG >= 1 +#if DEBUG >= 1 std::cerr << "Process " << name << " could not be started. Pipe creation failed." << std::endl; - #endif +#endif return 0; } if (pipe(pfildes2) == -1){ - #if DEBUG >= 1 +#if DEBUG >= 1 std::cerr << "Process " << name << " could not be started. Pipe creation failed." << std::endl; - #endif +#endif return 0; } - + int devnull = open("/dev/null", O_RDWR); pid_t ret = fork(); if (ret == 0){ close(pfildes[0]); - dup2(pfildes[1],STDOUT_FILENO); + dup2(pfildes[1], STDOUT_FILENO); close(pfildes[1]); dup2(devnull, STDIN_FILENO); dup2(devnull, STDERR_FILENO); @@ -247,9 +259,9 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2, st if (ret > 0){ plist.insert(std::pair(ret, name)); }else{ - #if DEBUG >= 1 +#if DEBUG >= 1 std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl; - #endif +#endif close(pfildes[1]); close(pfildes[0]); close(pfildes2[1]); @@ -257,27 +269,27 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2, st return 0; } } - + pid_t ret2 = fork(); if (ret2 == 0){ close(pfildes[1]); close(pfildes2[0]); - dup2(pfildes[0],STDIN_FILENO); + dup2(pfildes[0], STDIN_FILENO); close(pfildes[0]); - dup2(pfildes2[1],STDOUT_FILENO); + dup2(pfildes2[1], STDOUT_FILENO); close(pfildes2[1]); dup2(devnull, STDERR_FILENO); runCmd(cmd2); }else{ if (ret2 > 0){ - #if DEBUG >= 1 +#if DEBUG >= 1 std::cerr << "Process " << name << " started, PIDs (" << ret << ", " << ret2 << "): " << cmd << " | " << cmd2 << std::endl; - #endif +#endif plist.insert(std::pair(ret2, name)); }else{ - #if DEBUG >= 1 +#if DEBUG >= 1 std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl; - #endif +#endif Stop(name); close(pfildes[1]); close(pfildes[0]); @@ -294,21 +306,21 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2, st close(pfildes[1]); close(pfildes[0]); close(pfildes2[1]); - dup2(pfildes2[0],STDIN_FILENO); + dup2(pfildes2[0], STDIN_FILENO); close(pfildes2[0]); dup2(devnull, STDOUT_FILENO); dup2(devnull, STDERR_FILENO); runCmd(cmd3); }else{ if (ret3 > 0){ - #if DEBUG >= 1 +#if DEBUG >= 1 std::cerr << "Process " << name << " started, PIDs (" << ret << ", " << ret2 << ", " << ret3 << "): " << cmd << " | " << cmd2 << " | " << cmd3 << std::endl; - #endif +#endif plist.insert(std::pair(ret3, name)); }else{ - #if DEBUG >= 1 +#if DEBUG >= 1 std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl; - #endif +#endif Stop(name); close(pfildes[1]); close(pfildes[0]); @@ -317,7 +329,7 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2, st return 0; } } - + return ret3; } @@ -329,118 +341,153 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2, st /// \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 * argv[], int * fdin, int * fdout, int * fderr){ - if (isActive(name)){return getPid(name);} + if (isActive(name)){ + return getPid(name); + } pid_t pid; int pipein[2], pipeout[2], pipeerr[2]; - if (!handler_set){ + if ( !handler_set){ struct sigaction new_action; new_action.sa_handler = Util::Procs::childsig_handler; - sigemptyset(&new_action.sa_mask); + sigemptyset( &new_action.sa_mask); new_action.sa_flags = 0; sigaction(SIGCHLD, &new_action, NULL); handler_set = true; } if (fdin && *fdin == -1 && pipe(pipein) < 0){ - #if DEBUG >= 1 +#if DEBUG >= 1 std::cerr << "Pipe (in) creation failed for " << name << std::endl; - #endif +#endif return 0; } - if (fdout && *fdout == -1 && pipe(pipeout) < 0) { - #if DEBUG >= 1 + if (fdout && *fdout == -1 && pipe(pipeout) < 0){ +#if DEBUG >= 1 std::cerr << "Pipe (out) creation failed for " << name << std::endl; - #endif - if (*fdin == -1){close(pipein[0]);close(pipein[1]);} +#endif + if ( *fdin == -1){ + close(pipein[0]); + close(pipein[1]); + } return 0; } - if (fderr && *fderr == -1 && pipe(pipeerr) < 0) { - #if DEBUG >= 1 + if (fderr && *fderr == -1 && pipe(pipeerr) < 0){ +#if DEBUG >= 1 std::cerr << "Pipe (err) creation failed for " << name << std::endl; - #endif - if (*fdin == -1){close(pipein [0]);close(pipein [1]);} - if (*fdout == -1){close(pipeout[0]);close(pipeout[1]);} +#endif + 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){ + if ( !fdin || !fdout || !fderr){ devnull = open("/dev/null", O_RDWR); if (devnull == -1){ - #if DEBUG >= 1 +#if DEBUG >= 1 std::cerr << "Could not open /dev/null for " << name << ": " << strerror(errno) << std::endl; - #endif - 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]);} +#endif + 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){ + if (pid == 0){ //child + if ( !fdin){ dup2(devnull, STDIN_FILENO); - }else if (*fdin == -1){ - close(pipein[1]);// close unused write end + }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); - close(*fdin); + }else if ( *fdin != STDIN_FILENO){ + dup2( *fdin, STDIN_FILENO); + close( *fdin); } - if (!fdout){ + if ( !fdout){ dup2(devnull, STDOUT_FILENO); - }else if (*fdout == -1){ - close(pipeout[0]);// close unused read end + }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); - close(*fdout); + }else if ( *fdout != STDOUT_FILENO){ + dup2( *fdout, STDOUT_FILENO); + close( *fdout); } - if (!fderr){ + if ( !fderr){ dup2(devnull, STDERR_FILENO); - }else if (*fderr == -1){ - close(pipeerr[0]);// close unused read end + }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); - close(*fderr); + }else if ( *fderr != STDERR_FILENO){ + dup2( *fderr, STDERR_FILENO); + close( *fderr); + } + if (devnull != -1){ + close(devnull); } - if (devnull != -1){close(devnull);} execvp(argv[0], argv); - #if DEBUG >= 1 +#if DEBUG >= 1 perror("execvp failed"); - #endif +#endif exit(42); - } else if (pid == -1){ - #if DEBUG >= 1 + }else if (pid == -1){ +#if DEBUG >= 1 std::cerr << "Failed to fork for pipe: " << name << std::endl; - #endif - 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);} +#endif + 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 - #if DEBUG >= 1 + }else{ //parent +#if DEBUG >= 1 std::cerr << "Piped process " << name << " started"; - if (fdin ) std::cerr << " in=" << (*fdin == -1 ? pipein [1] : *fdin ); + if (fdin ) std::cerr << " in=" << (*fdin == -1 ? pipein [1] : *fdin ); if (fdout) std::cerr << " out=" << (*fdout == -1 ? pipeout[0] : *fdout); if (fderr) std::cerr << " err=" << (*fderr == -1 ? pipeerr[0] : *fderr); if (devnull != -1) std::cerr << " null=" << devnull; std::cerr << ", PID " << pid << ": " << argv[0] << std::endl; - #endif - if (devnull != -1){close(devnull);} - if (fdin && *fdin == -1){ - close(pipein[0]);// close unused end end +#endif + 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 + close(pipeout[1]); // close unused write end *fdout = pipeout[0]; } if (fderr && *fderr == -1){ - close(pipeerr[1]);// close unused write end + close(pipeerr[1]); // close unused write end *fderr = pipeerr[0]; } plist.insert(std::pair(pid, name)); @@ -448,7 +495,6 @@ pid_t Util::Procs::StartPiped(std::string name, char * argv[], int * fdin, int * return pid; } - /// Stops the named process, if running. /// \arg name (Internal) name of process to stop void Util::Procs::Stop(std::string name){ @@ -456,7 +502,9 @@ void Util::Procs::Stop(std::string name){ while (isActive(name)){ Stop(getPid(name)); max--; - if (max <= 0){return;} + if (max <= 0){ + return; + } } } @@ -472,20 +520,22 @@ void Util::Procs::Stop(pid_t name){ void Util::Procs::StopAll(){ std::map::iterator it; for (it = plist.begin(); it != plist.end(); it++){ - Stop((*it).first); + Stop(( *it).first); } } /// Returns the number of active child processes. int Util::Procs::Count(){ - return plist.size(); + return plist.size(); } /// Returns true if a process by this name is currently active. bool Util::Procs::isActive(std::string name){ std::map::iterator it; for (it = plist.begin(); it != plist.end(); it++){ - if ((*it).second == name){return true;} + if (( *it).second == name){ + return true; + } } return false; } @@ -500,7 +550,9 @@ bool Util::Procs::isActive(pid_t name){ pid_t Util::Procs::getPid(std::string name){ std::map::iterator it; for (it = plist.begin(); it != plist.end(); it++){ - if ((*it).second == name){return (*it).first;} + if (( *it).second == name){ + return ( *it).first; + } } return 0; } diff --git a/lib/procs.h b/lib/procs.h index cc939de8..6f0166e5 100644 --- a/lib/procs.h +++ b/lib/procs.h @@ -7,7 +7,7 @@ #include /// Contains utility code, not directly related to streaming media -namespace Util{ +namespace Util { typedef void (*TerminationNotifier)(pid_t pid, int exitCode); @@ -35,4 +35,4 @@ namespace Util{ static bool SetTerminationNotifier(pid_t pid, TerminationNotifier notifier); }; -}; +} diff --git a/lib/rtmpchunks.cpp b/lib/rtmpchunks.cpp index 1084e004..b150ab97 100644 --- a/lib/rtmpchunks.cpp +++ b/lib/rtmpchunks.cpp @@ -7,7 +7,7 @@ char versionstring[] = "WWW.DDVTECH.COM "; ///< String that is repeated in the RTMP handshake std::string RTMPStream::handshake_in; ///< Input for the handshake. -std::string RTMPStream::handshake_out;///< Output for the handshake. +std::string RTMPStream::handshake_out; ///< Output for the handshake. unsigned int RTMPStream::chunk_rec_max = 128; unsigned int RTMPStream::chunk_snd_max = 128; @@ -39,19 +39,17 @@ std::map RTMPStream::Chunk::lastrecv; "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF" -uint8_t genuineFMSKey[] = { - 0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, 0x6c, 0x61, 0x73, 0x68, 0x20, - 0x4d, 0x65, 0x64, 0x69, 0x61, 0x20, 0x53, 0x65, 0x72, 0x76, // 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 +uint8_t genuineFMSKey[] = {0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, 0x6c, 0x61, 0x73, 0x68, 0x20, + 0x4d, 0x65, 0x64, 0x69, 0x61, 0x20, 0x53, 0x65, 0x72, + 0x76, // 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 -uint8_t genuineFPKey[] = { - 0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, 0x6c, 0x61, 0x73, 0x68, 0x20, - 0x50, 0x6c, 0x61, 0x79, // 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 +uint8_t genuineFPKey[] = {0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, 0x6c, 0x61, 0x73, 0x68, 0x20, + 0x50, 0x6c, 0x61, + 0x79, // 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){ @@ -69,7 +67,7 @@ inline uint32_t GetDHOffset(uint8_t *pBuffer, uint8_t scheme){ } } -class DHWrapper { +class DHWrapper{ private: int32_t _bitsCount; DH *_pDH; @@ -89,7 +87,7 @@ class DHWrapper { bool CopyKey(BIGNUM *pNum, uint8_t *pDst, int32_t dstLength); }; -DHWrapper::DHWrapper(int32_t bitsCount) { +DHWrapper::DHWrapper(int32_t bitsCount){ _bitsCount = bitsCount; _pDH = 0; _pSharedKey = 0; @@ -97,119 +95,172 @@ DHWrapper::DHWrapper(int32_t bitsCount) { _peerPublickey = 0; } -DHWrapper::~DHWrapper() { +DHWrapper::~DHWrapper(){ Cleanup(); } -bool DHWrapper::Initialize() { +bool DHWrapper::Initialize(){ Cleanup(); _pDH = DH_new(); - if (!_pDH){Cleanup(); return false;} + if ( !_pDH){ + Cleanup(); + return false; + } _pDH->p = BN_new(); - if (!_pDH->p){Cleanup(); return false;} + if ( !_pDH->p){ + Cleanup(); + return false; + } _pDH->g = BN_new(); - if (!_pDH->g){Cleanup(); return false;} - if (BN_hex2bn(&_pDH->p, P1024) == 0){Cleanup(); return false;} - if (BN_set_word(_pDH->g, 2) != 1){Cleanup(); return false;} + if ( !_pDH->g){ + Cleanup(); + return false; + } + if (BN_hex2bn( &_pDH->p, P1024) == 0){ + Cleanup(); + return false; + } + if (BN_set_word(_pDH->g, 2) != 1){ + Cleanup(); + return false; + } _pDH->length = _bitsCount; - if (DH_generate_key(_pDH) != 1){Cleanup(); return false;} + if (DH_generate_key(_pDH) != 1){ + Cleanup(); + return false; + } return true; } -bool DHWrapper::CopyPublicKey(uint8_t *pDst, int32_t dstLength) { - if (!_pDH){return false;} +bool DHWrapper::CopyPublicKey(uint8_t *pDst, int32_t dstLength){ + if ( !_pDH){ + return false; + } return CopyKey(_pDH->pub_key, pDst, dstLength); } -bool DHWrapper::CopyPrivateKey(uint8_t *pDst, int32_t dstLength) { - if (!_pDH){return false;} +bool DHWrapper::CopyPrivateKey(uint8_t *pDst, int32_t dstLength){ + if ( !_pDH){ + return false; + } return CopyKey(_pDH->priv_key, pDst, dstLength); } -bool DHWrapper::CreateSharedKey(uint8_t *pPeerPublicKey, int32_t length) { - if (!_pDH){return false;} - if (_sharedKeyLength != 0 || _pSharedKey){return false;} - +bool DHWrapper::CreateSharedKey(uint8_t *pPeerPublicKey, int32_t length){ + if ( !_pDH){ + return false; + } + if (_sharedKeyLength != 0 || _pSharedKey){ + return false; + } + _sharedKeyLength = DH_size(_pDH); - if (_sharedKeyLength <= 0 || _sharedKeyLength > 1024){return false;} - + if (_sharedKeyLength <= 0 || _sharedKeyLength > 1024){ + return false; + } + _pSharedKey = new uint8_t[_sharedKeyLength]; _peerPublickey = BN_bin2bn(pPeerPublicKey, length, 0); - if (!_peerPublickey){return false;} - - if (DH_compute_key(_pSharedKey, _peerPublickey, _pDH) != _sharedKeyLength){return false;} - + if ( !_peerPublickey){ + return false; + } + + if (DH_compute_key(_pSharedKey, _peerPublickey, _pDH) != _sharedKeyLength){ + return false; + } + return true; } -bool DHWrapper::CopySharedKey(uint8_t *pDst, int32_t dstLength) { - if (!_pDH){return false;} - if (dstLength != _sharedKeyLength){return false;} +bool DHWrapper::CopySharedKey(uint8_t *pDst, int32_t dstLength){ + if ( !_pDH){ + return false; + } + if (dstLength != _sharedKeyLength){ + return false; + } memcpy(pDst, _pSharedKey, _sharedKeyLength); return true; } -void DHWrapper::Cleanup() { +void DHWrapper::Cleanup(){ if (_pDH){ - if (_pDH->p){BN_free(_pDH->p); _pDH->p = 0;} - if (_pDH->g){BN_free(_pDH->g); _pDH->g = 0;} - DH_free(_pDH); _pDH = 0; + if (_pDH->p){ + BN_free(_pDH->p); + _pDH->p = 0; + } + if (_pDH->g){ + BN_free(_pDH->g); + _pDH->g = 0; + } + DH_free(_pDH); + _pDH = 0; + } + if (_pSharedKey){ + delete[] _pSharedKey; + _pSharedKey = 0; } - if (_pSharedKey){delete[] _pSharedKey; _pSharedKey = 0;} _sharedKeyLength = 0; - if (_peerPublickey){BN_free(_peerPublickey); _peerPublickey = 0;} + if (_peerPublickey){ + BN_free(_peerPublickey); + _peerPublickey = 0; + } } -bool DHWrapper::CopyKey(BIGNUM *pNum, uint8_t *pDst, int32_t dstLength) { +bool DHWrapper::CopyKey(BIGNUM *pNum, uint8_t *pDst, int32_t dstLength){ int32_t keySize = BN_num_bytes(pNum); - if ((keySize <= 0) || (dstLength <= 0) || (keySize > dstLength)){return false;} - if (BN_bn2bin(pNum, pDst) != keySize){return false;} + if ((keySize <= 0) || (dstLength <= 0) || (keySize > dstLength)){ + return false; + } + if (BN_bn2bin(pNum, pDst) != keySize){ + return false; + } return true; } -void InitRC4Encryption(uint8_t *secretKey, uint8_t *pubKeyIn, uint8_t *pubKeyOut, RC4_KEY *rc4keyIn, RC4_KEY *rc4keyOut) { +void InitRC4Encryption(uint8_t *secretKey, uint8_t *pubKeyIn, uint8_t *pubKeyOut, RC4_KEY *rc4keyIn, RC4_KEY *rc4keyOut){ uint8_t digest[SHA256_DIGEST_LENGTH]; unsigned int digestLen = 0; - + HMAC_CTX ctx; - HMAC_CTX_init(&ctx); - HMAC_Init_ex(&ctx, secretKey, 128, EVP_sha256(), 0); - HMAC_Update(&ctx, pubKeyIn, 128); - HMAC_Final(&ctx, digest, &digestLen); - HMAC_CTX_cleanup(&ctx); - + HMAC_CTX_init( &ctx); + HMAC_Init_ex( &ctx, secretKey, 128, EVP_sha256(), 0); + HMAC_Update( &ctx, pubKeyIn, 128); + HMAC_Final( &ctx, digest, &digestLen); + HMAC_CTX_cleanup( &ctx); + RC4_set_key(rc4keyOut, 16, digest); - - HMAC_CTX_init(&ctx); - HMAC_Init_ex(&ctx, secretKey, 128, EVP_sha256(), 0); - HMAC_Update(&ctx, pubKeyOut, 128); - HMAC_Final(&ctx, digest, &digestLen); - HMAC_CTX_cleanup(&ctx); - + + HMAC_CTX_init( &ctx); + HMAC_Init_ex( &ctx, secretKey, 128, EVP_sha256(), 0); + HMAC_Update( &ctx, pubKeyOut, 128); + HMAC_Final( &ctx, digest, &digestLen); + HMAC_CTX_cleanup( &ctx); + RC4_set_key(rc4keyIn, 16, digest); } -void HMACsha256(const void *pData, uint32_t dataLength, const void *pKey, uint32_t keyLength, void *pResult) { +void HMACsha256(const void *pData, uint32_t dataLength, const void *pKey, uint32_t keyLength, void *pResult){ unsigned int digestLen; HMAC_CTX ctx; - HMAC_CTX_init(&ctx); - HMAC_Init_ex(&ctx, (unsigned char*) pKey, keyLength, EVP_sha256(), 0); - HMAC_Update(&ctx, (unsigned char *) pData, dataLength); - HMAC_Final(&ctx, (unsigned char *) pResult, &digestLen); - HMAC_CTX_cleanup(&ctx); + HMAC_CTX_init( &ctx); + HMAC_Init_ex( &ctx, (unsigned char*)pKey, keyLength, EVP_sha256(), 0); + HMAC_Update( &ctx, (unsigned char *)pData, dataLength); + HMAC_Final( &ctx, (unsigned char *)pResult, &digestLen); + HMAC_CTX_cleanup( &ctx); } -bool ValidateClientScheme(uint8_t * pBuffer, uint8_t scheme) { +bool ValidateClientScheme(uint8_t * pBuffer, uint8_t scheme){ uint32_t clientDigestOffset = GetDigestOffset(pBuffer, scheme); uint8_t *pTempBuffer = new uint8_t[1536 - 32]; memcpy(pTempBuffer, pBuffer, clientDigestOffset); memcpy(pTempBuffer + clientDigestOffset, pBuffer + clientDigestOffset + 32, 1536 - clientDigestOffset - 32); uint8_t *pTempHash = new uint8_t[512]; HMACsha256(pTempBuffer, 1536 - 32, genuineFPKey, 30, pTempHash); - bool result = (memcmp(pBuffer+clientDigestOffset, pTempHash, 32) == 0); - #if DEBUG >= 4 + bool result = (memcmp(pBuffer + clientDigestOffset, pTempHash, 32) == 0); +#if DEBUG >= 4 fprintf(stderr, "Client scheme validation %hhi %s\n", scheme, result?"success":"failed"); - #endif +#endif delete[] pTempBuffer; delete[] pTempHash; return result; @@ -226,23 +277,25 @@ std::string & RTMPStream::Chunk::Pack(){ unsigned char chtype = 0x00; if ((prev.msg_type_id > 0) && (prev.cs_id == cs_id)){ if (msg_stream_id == prev.msg_stream_id){ - chtype = 0x40;//do not send msg_stream_id + 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 + chtype = 0x80; //do not send len and msg_type_id if (timestamp == prev.timestamp){ - chtype = 0xC0;//do not send timestamp + chtype = 0xC0; //do not send timestamp } } } } //override - we always sent type 0x00 if the timestamp has decreased since last chunk in this channel - if (timestamp < prev.timestamp){chtype = 0x00;} + if (timestamp < prev.timestamp){ + chtype = 0x00; + } } if (cs_id <= 63){ output += (unsigned char)(chtype | cs_id); }else{ - if (cs_id <= 255+64){ + if (cs_id <= 255 + 64){ output += (unsigned char)(chtype | 0); output += (unsigned char)(cs_id - 64); }else{ @@ -259,7 +312,10 @@ std::string & RTMPStream::Chunk::Pack(){ }else{ tmpi = timestamp - prev.timestamp; } - if (tmpi >= 0x00ffffff){ntime = tmpi; tmpi = 0x00ffffff;} + if (tmpi >= 0x00ffffff){ + ntime = tmpi; + tmpi = 0x00ffffff; + } output += (unsigned char)((tmpi >> 16) & 0xff); output += (unsigned char)((tmpi >> 8) & 0xff); output += (unsigned char)(tmpi & 0xff); @@ -275,8 +331,8 @@ std::string & RTMPStream::Chunk::Pack(){ //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)); + output += (unsigned char)(msg_stream_id / (256 * 256)); + output += (unsigned char)(msg_stream_id / (256 * 256 * 256)); } } } @@ -290,14 +346,16 @@ std::string & RTMPStream::Chunk::Pack(){ len_left = 0; while (len_left < len){ tmpi = len - len_left; - if (tmpi > RTMPStream::chunk_snd_max){tmpi = RTMPStream::chunk_snd_max;} + 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){ + if (cs_id <= 255 + 64){ output += (unsigned char)(0xC0); output += (unsigned char)(cs_id - 64); }else{ @@ -311,10 +369,11 @@ std::string & RTMPStream::Chunk::Pack(){ lastsend[cs_id] = *this; RTMPStream::snd_cnt += output.size(); return output; -}//SendChunk +} //SendChunk -/// Default contructor, creates an empty chunk with all values initialized to zero. +/// Default constructor, creates an empty chunk with all values initialized to zero. RTMPStream::Chunk::Chunk(){ + headertype = 0; cs_id = 0; timestamp = 0; len = 0; @@ -323,7 +382,7 @@ RTMPStream::Chunk::Chunk(){ msg_type_id = 0; msg_stream_id = 0; data = ""; -}//constructor +} //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){ @@ -337,7 +396,7 @@ std::string & RTMPStream::SendChunk(unsigned int cs_id, unsigned char msg_type_i ch.msg_stream_id = msg_stream_id; ch.data = data; return ch.Pack(); -}//constructor +} //constructor /// Packs up a chunk with media contents. /// \param msg_type_id Type number of the media, as per FLV standard. @@ -346,7 +405,7 @@ std::string & RTMPStream::SendChunk(unsigned int cs_id, unsigned char msg_type_i /// \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.cs_id = msg_type_id + 42; ch.timestamp = ts; ch.len = len; ch.real_len = len; @@ -355,7 +414,7 @@ std::string & RTMPStream::SendMedia(unsigned char msg_type_id, unsigned char * d ch.msg_stream_id = 1; ch.data = std::string((char*)data, (size_t)len); return ch.Pack(); -}//SendMedia +} //SendMedia /// Packs up a chunk with media contents. /// \param tag FLV::Tag with media to send. @@ -363,14 +422,14 @@ std::string & RTMPStream::SendMedia(FLV::Tag & tag){ static RTMPStream::Chunk ch; ch.cs_id = ((unsigned char)tag.data[0]); ch.timestamp = tag.tagTime(); - ch.len = tag.len-15; - ch.real_len = tag.len-15; + ch.len = tag.len - 15; + ch.real_len = tag.len - 15; ch.len_left = 0; ch.msg_type_id = (unsigned char)tag.data[0]; ch.msg_stream_id = 1; - ch.data = std::string(tag.data+11, (size_t)(tag.len-15)); + ch.data = std::string(tag.data + 11, (size_t)(tag.len - 15)); return ch.Pack(); -}//SendMedia +} //SendMedia /// Packs up a chunk for a control message with 1 argument. std::string & RTMPStream::SendCTL(unsigned char type, unsigned int data){ @@ -385,7 +444,7 @@ std::string & RTMPStream::SendCTL(unsigned char type, unsigned int data){ ch.data.resize(4); *(int*)((char*)ch.data.c_str()) = htonl(data); return ch.Pack(); -}//SendCTL +} //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){ @@ -401,7 +460,7 @@ std::string & RTMPStream::SendCTL(unsigned char type, unsigned int data, unsigne *(unsigned int*)((char*)ch.data.c_str()) = htonl(data); ch.data[4] = data2; return ch.Pack(); -}//SendCTL +} //SendCTL /// Packs up a chunk for a user control message with 1 argument. std::string & RTMPStream::SendUSR(unsigned char type, unsigned int data){ @@ -414,11 +473,11 @@ std::string & RTMPStream::SendUSR(unsigned char type, unsigned int data){ ch.msg_type_id = 4; ch.msg_stream_id = 0; ch.data.resize(6); - *(unsigned int*)(((char*)ch.data.c_str())+2) = htonl(data); + *(unsigned int*)(((char*)ch.data.c_str()) + 2) = htonl(data); ch.data[0] = 0; ch.data[1] = type; return ch.Pack(); -}//SendUSR +} //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){ @@ -431,13 +490,12 @@ std::string & RTMPStream::SendUSR(unsigned char type, unsigned int data, unsigne 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); + *(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 - +} //SendUSR /// Parses the argument string into the current chunk. /// Tries to read a whole chunk, removing data from the input string as it reads. @@ -448,21 +506,21 @@ std::string & RTMPStream::SendUSR(unsigned char type, unsigned int data, unsigne /// \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); + gettimeofday( &RTMPStream::lastrec, 0); unsigned int i = 0; - if (indata.size() < 1) return false;//need at least a byte + if (indata.size() < 1) return false; //need at least a byte - unsigned char chunktype = indata[i++]; + 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; + 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; + 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; @@ -475,48 +533,58 @@ bool RTMPStream::Chunk::Parse(std::string & indata){ headertype = chunktype & 0xC0; switch (headertype){ case 0x00: - if (indata.size() < i+11) return false; //can't read whole header - timestamp = indata[i++]*256*256; - timestamp += indata[i++]*256; - timestamp += indata[i++]; - len = indata[i++]*256*256; - len += indata[i++]*256; - len += indata[i++]; + if (indata.size() < i + 11) return false; //can't read whole header + timestamp = indata[i++ ] * 256 * 256; + timestamp += indata[i++ ] * 256; + timestamp += indata[i++ ]; + len = indata[i++ ] * 256 * 256; + len += indata[i++ ] * 256; + len += indata[i++ ]; len_left = 0; - msg_type_id = indata[i++]; - msg_stream_id = indata[i++]; - msg_stream_id += indata[i++]*256; - msg_stream_id += indata[i++]*256*256; - msg_stream_id += indata[i++]*256*256*256; + msg_type_id = indata[i++ ]; + msg_stream_id = indata[i++ ]; + msg_stream_id += indata[i++ ] * 256; + msg_stream_id += indata[i++ ] * 256 * 256; + msg_stream_id += indata[i++ ] * 256 * 256 * 256; break; case 0x40: - if (indata.size() < i+7) return false; //can't read whole header - if (prev.msg_type_id == 0){fprintf(stderr, "Warning: Header type 0x40 with no valid previous chunk!\n");} - timestamp = indata[i++]*256*256; - timestamp += indata[i++]*256; - timestamp += indata[i++]; - if (timestamp != 0x00ffffff){timestamp += prev.timestamp;} - len = indata[i++]*256*256; - len += indata[i++]*256; - len += indata[i++]; + if (indata.size() < i + 7) return false; //can't read whole header + if (prev.msg_type_id == 0){ + fprintf(stderr, "Warning: Header type 0x40 with no valid previous chunk!\n"); + } + timestamp = indata[i++ ] * 256 * 256; + timestamp += indata[i++ ] * 256; + timestamp += indata[i++ ]; + if (timestamp != 0x00ffffff){ + timestamp += prev.timestamp; + } + len = indata[i++ ] * 256 * 256; + len += indata[i++ ] * 256; + len += indata[i++ ]; len_left = 0; - msg_type_id = indata[i++]; + msg_type_id = indata[i++ ]; msg_stream_id = prev.msg_stream_id; break; case 0x80: - if (indata.size() < i+3) return false; //can't read whole header - if (prev.msg_type_id == 0){fprintf(stderr, "Warning: Header type 0x80 with no valid previous chunk!\n");} - timestamp = indata[i++]*256*256; - timestamp += indata[i++]*256; - timestamp += indata[i++]; - if (timestamp != 0x00ffffff){timestamp += prev.timestamp;} + if (indata.size() < i + 3) return false; //can't read whole header + if (prev.msg_type_id == 0){ + fprintf(stderr, "Warning: Header type 0x80 with no valid previous chunk!\n"); + } + timestamp = indata[i++ ] * 256 * 256; + timestamp += indata[i++ ] * 256; + timestamp += indata[i++ ]; + if (timestamp != 0x00ffffff){ + timestamp += prev.timestamp; + } len = prev.len; len_left = prev.len_left; msg_type_id = prev.msg_type_id; msg_stream_id = prev.msg_stream_id; break; case 0xC0: - if (prev.msg_type_id == 0){fprintf(stderr, "Warning: Header type 0xC0 with no valid previous chunk!\n");} + if (prev.msg_type_id == 0){ + fprintf(stderr, "Warning: Header type 0xC0 with no valid previous chunk!\n"); + } timestamp = prev.timestamp; len = prev.len; len_left = prev.len_left; @@ -537,11 +605,11 @@ bool RTMPStream::Chunk::Parse(std::string & indata){ } //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++]; + if (indata.size() < i + 4) return false; //can't read whole header + timestamp = indata[i++ ] * 256 * 256 * 256; + timestamp += indata[i++ ] * 256 * 256; + timestamp += indata[i++ ] * 256; + timestamp += indata[i++ ]; } //read data if length > 0, and allocate it @@ -551,11 +619,11 @@ bool RTMPStream::Chunk::Parse(std::string & indata){ }else{ data = ""; } - if (indata.size() < i+real_len) return false;//can't read all data (yet) + 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); + indata = indata.substr(i + real_len); lastrecv[cs_id] = *this; - RTMPStream::rec_cnt += i+real_len; + RTMPStream::rec_cnt += i + real_len; if (len_left == 0){ return true; }else{ @@ -563,12 +631,12 @@ bool RTMPStream::Chunk::Parse(std::string & indata){ } }else{ data = ""; - indata = indata.substr(i+real_len); + indata = indata.substr(i + real_len); lastrecv[cs_id] = *this; - RTMPStream::rec_cnt += i+real_len; + RTMPStream::rec_cnt += i + real_len; return true; } -}//Parse +} //Parse /// Parses the argument string into the current chunk. /// Tries to read a whole chunk, removing data from the input as it reads. @@ -579,77 +647,95 @@ bool RTMPStream::Chunk::Parse(std::string & indata){ /// \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); + gettimeofday( &RTMPStream::lastrec, 0); unsigned int i = 0; - if (!buffer.available(3)){return false;}//we want at least 3 bytes + if ( !buffer.available(3)){ + return false; + } //we want at least 3 bytes std::string indata = buffer.copy(3); - - unsigned char chunktype = indata[i++]; + + unsigned char chunktype = indata[i++ ]; //read the chunkstream ID properly switch (chunktype & 0x3F){ case 0: - cs_id = indata[i++] + 64; + cs_id = indata[i++ ] + 64; break; case 1: - cs_id = indata[i++] + 64; - cs_id += indata[i++] * 256; + 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++]; - len = indata[i++]*256*256; - len += indata[i++]*256; - len += indata[i++]; + 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++ ]; + 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; + 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){fprintf(stderr, "Warning: Header type 0x40 with no valid previous chunk!\n");} - timestamp = indata[i++]*256*256; - timestamp += indata[i++]*256; - timestamp += indata[i++]; - if (timestamp != 0x00ffffff){timestamp += prev.timestamp;} - len = indata[i++]*256*256; - len += indata[i++]*256; - len += indata[i++]; + if ( !buffer.available(i + 7)){ + return false; + } //can't read whole header + indata = buffer.copy(i + 7); + if (prev.msg_type_id == 0){ + fprintf(stderr, "Warning: Header type 0x40 with no valid previous chunk!\n"); + } + timestamp = indata[i++ ] * 256 * 256; + timestamp += indata[i++ ] * 256; + timestamp += indata[i++ ]; + if (timestamp != 0x00ffffff){ + timestamp += prev.timestamp; + } + len = indata[i++ ] * 256 * 256; + len += indata[i++ ] * 256; + len += indata[i++ ]; len_left = 0; - msg_type_id = indata[i++]; + msg_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){fprintf(stderr, "Warning: Header type 0x80 with no valid previous chunk!\n");} - timestamp = indata[i++]*256*256; - timestamp += indata[i++]*256; - timestamp += indata[i++]; - if (timestamp != 0x00ffffff){timestamp += prev.timestamp;} + if ( !buffer.available(i + 3)){ + return false; + } //can't read whole header + indata = buffer.copy(i + 3); + if (prev.msg_type_id == 0){ + fprintf(stderr, "Warning: Header type 0x80 with no valid previous chunk!\n"); + } + timestamp = indata[i++ ] * 256 * 256; + timestamp += indata[i++ ] * 256; + timestamp += indata[i++ ]; + if (timestamp != 0x00ffffff){ + timestamp += prev.timestamp; + } len = prev.len; len_left = prev.len_left; msg_type_id = prev.msg_type_id; msg_stream_id = prev.msg_stream_id; break; case 0xC0: - if (prev.msg_type_id == 0){fprintf(stderr, "Warning: Header type 0xC0 with no valid previous chunk!\n");} + if (prev.msg_type_id == 0){ + fprintf(stderr, "Warning: Header type 0xC0 with no valid previous chunk!\n"); + } timestamp = prev.timestamp; len = prev.len; len_left = prev.len_left; @@ -670,39 +756,43 @@ bool RTMPStream::Chunk::Parse(Socket::Buffer & buffer){ } //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++]; + 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++ ]; } - + //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 ( !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 + 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 + data = buffer.remove(real_len); //append the data and remove from buffer } lastrecv[cs_id] = *this; - RTMPStream::rec_cnt += i+real_len; + RTMPStream::rec_cnt += i + real_len; if (len_left == 0){ return true; }else{ return Parse(buffer); } }else{ - buffer.remove(i);//remove the header + buffer.remove(i); //remove the header data = ""; - indata = indata.substr(i+real_len); + indata = indata.substr(i + real_len); lastrecv[cs_id] = *this; - RTMPStream::rec_cnt += i+real_len; + RTMPStream::rec_cnt += i + real_len; return true; } -}//Parse +} //Parse /// Does the handshake. Expects handshake_in to be filled, and fills handshake_out. /// After calling this function, don't forget to read and ignore 1536 extra bytes, @@ -718,21 +808,23 @@ bool RTMPStream::doHandshake(){ RTMPStream::rec_cnt += 1537; //Build S1 Packet - *((uint32_t*)Server) = 0;//time zero - *(((uint32_t*)(Server+4))) = htonl(0x01020304);//version 1 2 3 4 - for (int i = 8; i < 3072; ++i){Server[i] = versionstring[i%16];}//"random" data + *((uint32_t*)Server) = 0; //time zero + *(((uint32_t*)(Server + 4))) = htonl(0x01020304); //version 1 2 3 4 + for (int i = 8; i < 3072; ++i){ + Server[i] = versionstring[i % 16]; + } //"random" data bool encrypted = (Version == 6); - #if DEBUG >= 4 +#if DEBUG >= 4 fprintf(stderr, "Handshake version is %hhi\n", Version); - #endif +#endif uint8_t _validationScheme = 5; if (ValidateClientScheme(Client, 0)) _validationScheme = 0; if (ValidateClientScheme(Client, 1)) _validationScheme = 1; - #if DEBUG >= 4 +#if DEBUG >= 4 fprintf(stderr, "Handshake type is %hhi, encryption is %s\n", _validationScheme, encrypted?"on":"off"); - #endif +#endif //FIRST 1536 bytes from server response //compute DH key position @@ -741,19 +833,19 @@ bool RTMPStream::doHandshake(){ //generate DH key DHWrapper dhWrapper(1024); - if (!dhWrapper.Initialize()) return false; - if (!dhWrapper.CreateSharedKey(Client + clientDHOffset, 128)) return false; - if (!dhWrapper.CopyPublicKey(Server + serverDHOffset, 128)) return false; + if ( !dhWrapper.Initialize()) return false; + if ( !dhWrapper.CreateSharedKey(Client + clientDHOffset, 128)) return false; + if ( !dhWrapper.CopyPublicKey(Server + serverDHOffset, 128)) return false; - if (encrypted) { + if (encrypted){ uint8_t secretKey[128]; - if (!dhWrapper.CopySharedKey(secretKey, sizeof (secretKey))) return false; + if ( !dhWrapper.CopySharedKey(secretKey, sizeof(secretKey))) return false; RC4_KEY _pKeyIn; RC4_KEY _pKeyOut; - InitRC4Encryption(secretKey, (uint8_t*) & Client[clientDHOffset], (uint8_t*) & Server[serverDHOffset], &_pKeyIn, &_pKeyOut); + InitRC4Encryption(secretKey, (uint8_t*) &Client[clientDHOffset], (uint8_t*) &Server[serverDHOffset], &_pKeyIn, &_pKeyOut); uint8_t data[1536]; - RC4(&_pKeyIn, 1536, data, data); - RC4(&_pKeyOut, 1536, data, data); + RC4( &_pKeyIn, 1536, data, data); + RC4( &_pKeyOut, 1536, data, data); } //generate the digest uint32_t serverDigestOffset = GetDigestOffset(Server, _validationScheme); @@ -776,7 +868,7 @@ bool RTMPStream::doHandshake(){ delete[] pTempHash; delete[] pLastHash; //DONE BUILDING THE RESPONSE ***// - Server[-1] = Version; + Server[ -1] = Version; RTMPStream::snd_cnt += 3073; return true; } diff --git a/lib/rtmpchunks.h b/lib/rtmpchunks.h index 21dd160e..b4ec4d4e 100644 --- a/lib/rtmpchunks.h +++ b/lib/rtmpchunks.h @@ -11,12 +11,12 @@ #include "socket.h" //forward declaration of FLV::Tag to avoid circular dependencies. -namespace FLV{ +namespace FLV { class Tag; -}; +} /// Contains all functions and classes needed for RTMP connections. -namespace RTMPStream{ +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. @@ -50,7 +50,8 @@ namespace RTMPStream{ private: static std::map lastsend; static std::map lastrecv; - };//RTMPStream::Chunk + }; + //RTMPStream::Chunk std::string & SendChunk(unsigned int cs_id, unsigned char msg_type_id, unsigned int msg_stream_id, std::string data); std::string & SendMedia(unsigned char msg_type_id, unsigned char * data, int len, unsigned int ts); @@ -66,4 +67,4 @@ namespace RTMPStream{ extern std::string handshake_out; /// Does the handshake. Expects handshake_in to be filled, and fills handshake_out. bool doHandshake(); -};//RTMPStream namespace +} //RTMPStream namespace diff --git a/lib/socket.cpp b/lib/socket.cpp index b2d70084..bad2a703 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -16,7 +16,6 @@ #define BUFFER_BLOCKSIZE 4096 //set buffer blocksize to 4KiB #include //temporary for debugging - std::string uint2string(unsigned int i){ std::stringstream st; st << i; @@ -27,7 +26,9 @@ std::string uint2string(unsigned int i){ /// The back is popped as long as it is empty, first - this way this function is /// guaranteed to return 0 if the buffer is empty. unsigned int Socket::Buffer::size(){ - while (data.size() > 0 && data.back().empty()){data.pop_back();} + while (data.size() > 0 && data.back().empty()){ + data.pop_back(); + } return data.size(); } @@ -45,10 +46,12 @@ void Socket::Buffer::append(const char * newdata, const unsigned int newdatasize j = i; while (j < newdatasize && j - i <= BUFFER_BLOCKSIZE){ j++; - if (newdata[j-1] == '\n'){break;} + if (newdata[j - 1] == '\n'){ + break; + } } if (i != j){ - data.push_front(std::string(newdata+i, (size_t)(j - i))); + data.push_front(std::string(newdata + i, (size_t)(j - i))); i = j; }else{ break; @@ -63,8 +66,10 @@ void Socket::Buffer::append(const char * newdata, const unsigned int newdatasize bool Socket::Buffer::available(unsigned int count){ unsigned int i = 0; for (std::deque::iterator it = data.begin(); it != data.end(); ++it){ - i += (*it).size(); - if (i >= count){return true;} + i += ( *it).size(); + if (i >= count){ + return true; + } } return false; } @@ -72,18 +77,20 @@ bool Socket::Buffer::available(unsigned int count){ /// Removes count bytes from the buffer, returning them by value. /// Returns an empty string if not all count bytes are available. std::string Socket::Buffer::remove(unsigned int count){ - if (!available(count)){return "";} + if ( !available(count)){ + return ""; + } unsigned int i = 0; std::string ret; ret.reserve(count); for (std::deque::reverse_iterator it = data.rbegin(); it != data.rend(); ++it){ - if (i + (*it).size() < count){ - ret.append(*it); - i += (*it).size(); - (*it).clear(); + if (i + ( *it).size() < count){ + ret.append( *it); + i += ( *it).size(); + ( *it).clear(); }else{ - ret.append(*it, 0, count - i); - (*it).erase(0, count - i); + ret.append( *it, 0, count - i); + ( *it).erase(0, count - i); break; } } @@ -93,15 +100,17 @@ std::string Socket::Buffer::remove(unsigned int count){ /// Copies count bytes from the buffer, returning them by value. /// Returns an empty string if not all count bytes are available. std::string Socket::Buffer::copy(unsigned int count){ - if (!available(count)){return "";} + if ( !available(count)){ + return ""; + } unsigned int i = 0; std::string ret; for (std::deque::reverse_iterator it = data.rbegin(); it != data.rend(); ++it){ - if (i + (*it).size() < count){ - ret.append(*it); - i += (*it).size(); + if (i + ( *it).size() < count){ + ret.append( *it); + i += ( *it).size(); }else{ - ret.append(*it, 0, count - i); + ret.append( *it, 0, count - i); break; } } @@ -118,7 +127,6 @@ std::string & Socket::Buffer::get(){ } } - /// Create a new base socket. This is a basic constructor for converting any valid socket to a Socket::Connection. /// \param sockNo Integer representing the socket to convert. Socket::Connection::Connection(int sockNo){ @@ -130,7 +138,7 @@ Socket::Connection::Connection(int sockNo){ conntime = Util::epoch(); Error = false; Blocking = false; -}//Socket::Connection basic constructor +} //Socket::Connection basic constructor /// Simulate a socket using two file descriptors. /// \param write The filedescriptor to write to. @@ -144,7 +152,7 @@ Socket::Connection::Connection(int write, int read){ conntime = Util::epoch(); Error = false; Blocking = false; -}//Socket::Connection basic constructor +} //Socket::Connection basic constructor /// Create a new disconnected base socket. This is a basic constructor for placeholder purposes. /// A socket created like this is always disconnected and should/could be overwritten at some point. @@ -157,12 +165,12 @@ Socket::Connection::Connection(){ conntime = Util::epoch(); Error = false; Blocking = false; -}//Socket::Connection basic constructor +} //Socket::Connection basic constructor /// Internally used call to make an file descriptor blocking or not. void setFDBlocking(int FD, bool blocking){ int flags = fcntl(FD, F_GETFL, 0); - if (!blocking){ + if ( !blocking){ flags |= O_NONBLOCK; }else{ flags &= !O_NONBLOCK; @@ -172,44 +180,57 @@ void setFDBlocking(int FD, bool blocking){ /// Set this socket to be blocking (true) or nonblocking (false). void Socket::Connection::setBlocking(bool blocking){ - if (sock >=0){setFDBlocking(sock, blocking);} - if (pipes[0] >=0){setFDBlocking(pipes[0], blocking);} - if (pipes[1] >=0){setFDBlocking(pipes[1], blocking);} + if (sock >= 0){ + setFDBlocking(sock, blocking); + } + if (pipes[0] >= 0){ + setFDBlocking(pipes[0], blocking); + } + if (pipes[1] >= 0){ + setFDBlocking(pipes[1], blocking); + } } /// Close connection. The internal socket is closed and then set to -1. /// If the connection is already closed, nothing happens. void Socket::Connection::close(){ if (connected()){ - #if DEBUG >= 6 +#if DEBUG >= 6 fprintf(stderr, "Socket closed.\n"); - #endif +#endif if (sock != -1){ shutdown(sock, SHUT_RDWR); errno = EINTR; - while (::close(sock) != 0 && errno == EINTR){} + while (::close(sock) != 0 && errno == EINTR){ + } sock = -1; } if (pipes[0] != -1){ errno = EINTR; - while (::close(pipes[0]) != 0 && errno == EINTR){} + while (::close(pipes[0]) != 0 && errno == EINTR){ + } pipes[0] = -1; } if (pipes[1] != -1){ errno = EINTR; - while (::close(pipes[1]) != 0 && errno == EINTR){} + while (::close(pipes[1]) != 0 && errno == EINTR){ + } pipes[1] = -1; } } -}//Socket::Connection::close +} //Socket::Connection::close /// Returns internal socket number. -int Socket::Connection::getSocket(){return sock;} +int Socket::Connection::getSocket(){ + return sock; +} /// Returns a string describing the last error that occured. /// Simply calls strerror(errno) - not very reliable! /// \todo Improve getError at some point to be more reliable and only report socket errors. -std::string Socket::Connection::getError(){return strerror(errno);} +std::string Socket::Connection::getError(){ + return strerror(errno); +} /// Create a new Unix Socket. This socket will (try to) connect to the given address right away. /// \param address String containing the location of the Unix socket to connect to. @@ -219,9 +240,9 @@ Socket::Connection::Connection(std::string address, bool nonblock){ pipes[1] = -1; sock = socket(PF_UNIX, SOCK_STREAM, 0); if (sock < 0){ - #if DEBUG >= 1 +#if DEBUG >= 1 fprintf(stderr, "Could not create socket! Error: %s\n", strerror(errno)); - #endif +#endif return; } Error = false; @@ -231,8 +252,8 @@ Socket::Connection::Connection(std::string address, bool nonblock){ conntime = Util::epoch(); sockaddr_un addr; addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, address.c_str(), address.size()+1); - int r = connect(sock, (sockaddr*)&addr, sizeof(addr)); + strncpy(addr.sun_path, address.c_str(), address.size() + 1); + int r = connect(sock, (sockaddr*) &addr, sizeof(addr)); if (r == 0){ if (nonblock){ int flags = fcntl(sock, F_GETFL, 0); @@ -240,12 +261,12 @@ Socket::Connection::Connection(std::string address, bool nonblock){ fcntl(sock, F_SETFL, flags); } }else{ - #if DEBUG >= 1 +#if DEBUG >= 1 fprintf(stderr, "Could not connect to %s! Error: %s\n", address.c_str(), strerror(errno)); - #endif +#endif close(); } -}//Socket::Connection Unix Contructor +} //Socket::Connection Unix Contructor /// Create a new TCP Socket. This socket will (try to) connect to the given host/port right away. /// \param host String containing the hostname to connect to. @@ -263,7 +284,7 @@ Socket::Connection::Connection(std::string host, int port, bool nonblock){ std::stringstream ss; ss << port; - memset(&hints, 0, sizeof(struct addrinfo)); + memset( &hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_ADDRCONFIG; @@ -273,25 +294,29 @@ Socket::Connection::Connection(std::string host, int port, bool nonblock){ hints.ai_next = NULL; int s = getaddrinfo(host.c_str(), ss.str().c_str(), &hints, &result); if (s != 0){ - #if DEBUG >= 1 +#if DEBUG >= 1 fprintf(stderr, "Could not connect to %s:%i! Error: %s\n", host.c_str(), port, gai_strerror(s)); - #endif +#endif close(); return; } - for (rp = result; rp != NULL; rp = rp->ai_next) { + for (rp = result; rp != NULL; rp = rp->ai_next){ sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); - if (sock < 0){continue;} - if (connect(sock, rp->ai_addr, rp->ai_addrlen) == 0){break;} + if (sock < 0){ + continue; + } + if (connect(sock, rp->ai_addr, rp->ai_addrlen) == 0){ + break; + } ::close(sock); } freeaddrinfo(result); if (rp == 0){ - #if DEBUG >= 1 +#if DEBUG >= 1 fprintf(stderr, "Could not connect to %s! Error: %s\n", host.c_str(), strerror(errno)); - #endif +#endif close(); }else{ if (nonblock){ @@ -300,7 +325,7 @@ Socket::Connection::Connection(std::string host, int port, bool nonblock){ fcntl(sock, F_SETFL, flags); } } -}//Socket::Connection TCP Contructor +} //Socket::Connection TCP Contructor /// Returns the connected-state for this socket. /// Note that this function might be slightly behind the real situation. @@ -345,8 +370,8 @@ bool Socket::Connection::spool(){ /// Returns true if new data was received, false otherwise. bool Socket::Connection::flush(){ while (upbuffer.size() > 0 && connected()){ - if (!iwrite(upbuffer.get())){ - Util::sleep(10);//sleep 10ms + if ( !iwrite(upbuffer.get())){ + Util::sleep(10); //sleep 10ms } } /// \todo Provide better mechanism to prevent overbuffering. @@ -357,7 +382,6 @@ bool Socket::Connection::flush(){ } } - /// Returns a reference to the download buffer. Socket::Buffer & Socket::Connection::Received(){ return downbuffer; @@ -368,17 +392,17 @@ Socket::Buffer & Socket::Connection::Received(){ /// Any data that could not be send will block until it can be send or the connection is severed. void Socket::Connection::SendNow(const char * data, size_t len){ while (upbuffer.size() > 0 && connected()){ - if (!iwrite(upbuffer.get())){ - Util::sleep(1);//sleep 1ms if buffer full + if ( !iwrite(upbuffer.get())){ + Util::sleep(1); //sleep 1ms if buffer full } } int i = iwrite(data, len); while (i < len && connected()){ - int j = iwrite(data+i, len-i); + int j = iwrite(data + i, len - i); if (j > 0){ i += j; }else{ - Util::sleep(1);//sleep 1ms and retry + Util::sleep(1); //sleep 1ms and retry } } } @@ -390,7 +414,9 @@ void Socket::Connection::SendNow(const char * data, size_t len){ /// This means this function is blocking if the socket is, but nonblocking otherwise. void Socket::Connection::Send(const char * data, size_t len){ while (upbuffer.size() > 0){ - if (!iwrite(upbuffer.get())){break;} + if ( !iwrite(upbuffer.get())){ + break; + } } if (upbuffer.size() > 0){ upbuffer.append(data, len); @@ -442,7 +468,9 @@ void Socket::Connection::Send(std::string & data){ /// \param len Amount of bytes to write. /// \returns The amount of bytes actually written. int Socket::Connection::iwrite(const void * buffer, int len){ - if (!connected() || len < 1){return 0;} + if ( !connected() || len < 1){ + return 0; + } int r; if (sock >= 0){ r = send(sock, buffer, len, 0); @@ -457,9 +485,9 @@ int Socket::Connection::iwrite(const void * buffer, int len){ default: if (errno != EPIPE){ Error = true; - #if DEBUG >= 2 +#if DEBUG >= 2 fprintf(stderr, "Could not iwrite data! Error: %s\n", strerror(errno)); - #endif +#endif } close(); return 0; @@ -471,7 +499,7 @@ int Socket::Connection::iwrite(const void * buffer, int len){ } up += r; return r; -}//Socket::Connection::iwrite +} //Socket::Connection::iwrite /// Incremental read call. This function tries to read len bytes to the buffer from the socket, /// returning the amount of bytes it actually read. @@ -479,7 +507,9 @@ int Socket::Connection::iwrite(const void * buffer, int len){ /// \param len Amount of bytes to read. /// \returns The amount of bytes actually read. int Socket::Connection::iread(void * buffer, int len){ - if (!connected() || len < 1){return 0;} + if ( !connected() || len < 1){ + return 0; + } int r; if (sock >= 0){ r = recv(sock, buffer, len, 0); @@ -494,9 +524,9 @@ int Socket::Connection::iread(void * buffer, int len){ default: if (errno != EPIPE){ Error = true; - #if DEBUG >= 2 +#if DEBUG >= 2 fprintf(stderr, "Could not iread data! Error: %s\n", strerror(errno)); - #endif +#endif } close(); return 0; @@ -508,7 +538,7 @@ int Socket::Connection::iread(void * buffer, int len){ } down += r; return r; -}//Socket::Connection::iread +} //Socket::Connection::iread /// Read call that is compatible with Socket::Buffer. /// Data is read using iread (which is nonblocking if the Socket::Connection itself is), @@ -518,10 +548,12 @@ int Socket::Connection::iread(void * buffer, int len){ bool Socket::Connection::iread(Buffer & buffer){ char cbuffer[BUFFER_BLOCKSIZE]; int num = iread(cbuffer, BUFFER_BLOCKSIZE); - if (num < 1){return false;} + if (num < 1){ + return false; + } buffer.append(cbuffer, num); return true; -}//iread +} //iread /// Incremental write call that is compatible with std::string. /// Data is written using iwrite (which is nonblocking if the Socket::Connection itself is), @@ -529,12 +561,16 @@ bool Socket::Connection::iread(Buffer & buffer){ /// \param buffer std::string to remove data from. /// \return True if more data was sent, false otherwise. bool Socket::Connection::iwrite(std::string & buffer){ - if (buffer.size() < 1){return false;} + if (buffer.size() < 1){ + return false; + } int tmp = iwrite((void*)buffer.c_str(), buffer.size()); - if (tmp < 1){return false;} + if (tmp < 1){ + return false; + } buffer = buffer.substr(tmp); return true; -}//iwrite +} //iwrite /// Gets hostname for connection, if available. std::string Socket::Connection::getHost(){ @@ -549,13 +585,13 @@ void Socket::Connection::setHost(std::string host){ /// Returns true if these sockets are the same socket. /// Does not check the internal stats - only the socket itself. -bool Socket::Connection::operator== (const Connection &B) const{ +bool Socket::Connection::operator==(const Connection &B) const{ return sock == B.sock && pipes[0] == B.pipes[0] && pipes[1] == B.pipes[1]; } /// Returns true if these sockets are not the same socket. /// Does not check the internal stats - only the socket itself. -bool Socket::Connection::operator!= (const Connection &B) const{ +bool Socket::Connection::operator!=(const Connection &B) const{ return sock != B.sock || pipes[0] != B.pipes[0] || pipes[1] != B.pipes[1]; } @@ -568,7 +604,7 @@ Socket::Connection::operator bool() const{ /// Create a new base Server. The socket is never connected, and a placeholder for later connections. Socket::Server::Server(){ sock = -1; -}//Socket::Server base Constructor +} //Socket::Server base Constructor /// Create a new TCP Server. The socket is immediately bound and set to listen. /// A maximum of 100 connections will be accepted between accept() calls. @@ -577,11 +613,11 @@ Socket::Server::Server(){ /// \param hostname (optional) The interface to bind to. The default is 0.0.0.0 (all interfaces). /// \param nonblock (optional) Whether accept() calls will be nonblocking. Default is false (blocking). Socket::Server::Server(int port, std::string hostname, bool nonblock){ - if (!IPv6bind(port, hostname, nonblock) && !IPv4bind(port, hostname, nonblock)){ + if ( !IPv6bind(port, hostname, nonblock) && !IPv4bind(port, hostname, nonblock)){ fprintf(stderr, "Could not create socket %s:%i! Error: %s\n", hostname.c_str(), port, strerror(errno)); sock = -1; } -}//Socket::Server TCP Constructor +} //Socket::Server TCP Constructor /// Attempt to bind an IPv6 socket. /// \param port The TCP port to listen on @@ -591,9 +627,9 @@ Socket::Server::Server(int port, std::string hostname, bool nonblock){ bool Socket::Server::IPv6bind(int port, std::string hostname, bool nonblock){ sock = socket(AF_INET6, SOCK_STREAM, 0); if (sock < 0){ - #if DEBUG >= 1 +#if DEBUG >= 1 fprintf(stderr, "Could not create IPv6 socket %s:%i! Error: %s\n", hostname.c_str(), port, strerror(errno)); - #endif +#endif return false; } int on = 1; @@ -605,31 +641,31 @@ bool Socket::Server::IPv6bind(int port, std::string hostname, bool nonblock){ } struct sockaddr_in6 addr; addr.sin6_family = AF_INET6; - addr.sin6_port = htons(port);//set port + addr.sin6_port = htons(port); //set port if (hostname == "0.0.0.0" || hostname.length() == 0){ addr.sin6_addr = in6addr_any; }else{ - inet_pton(AF_INET6, hostname.c_str(), &addr.sin6_addr);//set interface, 0.0.0.0 (default) is all + inet_pton(AF_INET6, hostname.c_str(), &addr.sin6_addr); //set interface, 0.0.0.0 (default) is all } - int ret = bind(sock, (sockaddr*)&addr, sizeof(addr));//do the actual bind + int ret = bind(sock, (sockaddr*) &addr, sizeof(addr)); //do the actual bind if (ret == 0){ - ret = listen(sock, 100);//start listening, backlog of 100 allowed + ret = listen(sock, 100); //start listening, backlog of 100 allowed if (ret == 0){ - #if DEBUG >= 1 +#if DEBUG >= 1 fprintf(stderr, "IPv6 socket success @ %s:%i\n", hostname.c_str(), port); - #endif +#endif return true; }else{ - #if DEBUG >= 1 +#if DEBUG >= 1 fprintf(stderr, "IPv6 Listen failed! Error: %s\n", strerror(errno)); - #endif +#endif close(); return false; } }else{ - #if DEBUG >= 1 +#if DEBUG >= 1 fprintf(stderr, "IPv6 Binding %s:%i failed (%s)\n", hostname.c_str(), port, strerror(errno)); - #endif +#endif close(); return false; } @@ -643,9 +679,9 @@ bool Socket::Server::IPv6bind(int port, std::string hostname, bool nonblock){ bool Socket::Server::IPv4bind(int port, std::string hostname, bool nonblock){ sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0){ - #if DEBUG >= 1 +#if DEBUG >= 1 fprintf(stderr, "Could not create IPv4 socket %s:%i! Error: %s\n", hostname.c_str(), port, strerror(errno)); - #endif +#endif return false; } int on = 1; @@ -657,31 +693,31 @@ bool Socket::Server::IPv4bind(int port, std::string hostname, bool nonblock){ } struct sockaddr_in addr4; addr4.sin_family = AF_INET; - addr4.sin_port = htons(port);//set port + addr4.sin_port = htons(port); //set port if (hostname == "0.0.0.0" || hostname.length() == 0){ addr4.sin_addr.s_addr = INADDR_ANY; }else{ - inet_pton(AF_INET, hostname.c_str(), &addr4.sin_addr);//set interface, 0.0.0.0 (default) is all + inet_pton(AF_INET, hostname.c_str(), &addr4.sin_addr); //set interface, 0.0.0.0 (default) is all } - int ret = bind(sock, (sockaddr*)&addr4, sizeof(addr4));//do the actual bind + int ret = bind(sock, (sockaddr*) &addr4, sizeof(addr4)); //do the actual bind if (ret == 0){ - ret = listen(sock, 100);//start listening, backlog of 100 allowed + ret = listen(sock, 100); //start listening, backlog of 100 allowed if (ret == 0){ - #if DEBUG >= 1 +#if DEBUG >= 1 fprintf(stderr, "IPv4 socket success @ %s:%i\n", hostname.c_str(), port); - #endif +#endif return true; }else{ - #if DEBUG >= 1 +#if DEBUG >= 1 fprintf(stderr, "IPv4 Listen failed! Error: %s\n", strerror(errno)); - #endif +#endif close(); return false; } }else{ - #if DEBUG >= 1 +#if DEBUG >= 1 fprintf(stderr, "IPv4 binding %s:%i failed (%s)\n", hostname.c_str(), port, strerror(errno)); - #endif +#endif close(); return false; } @@ -697,9 +733,9 @@ Socket::Server::Server(std::string address, bool nonblock){ unlink(address.c_str()); sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock < 0){ - #if DEBUG >= 1 +#if DEBUG >= 1 fprintf(stderr, "Could not create socket! Error: %s\n", strerror(errno)); - #endif +#endif return; } if (nonblock){ @@ -709,38 +745,40 @@ Socket::Server::Server(std::string address, bool nonblock){ } sockaddr_un addr; addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, address.c_str(), address.size()+1); - int ret = bind(sock, (sockaddr*)&addr, sizeof(addr)); + strncpy(addr.sun_path, address.c_str(), address.size() + 1); + int ret = bind(sock, (sockaddr*) &addr, sizeof(addr)); if (ret == 0){ - ret = listen(sock, 100);//start listening, backlog of 100 allowed + ret = listen(sock, 100); //start listening, backlog of 100 allowed if (ret == 0){ return; }else{ - #if DEBUG >= 1 +#if DEBUG >= 1 fprintf(stderr, "Listen failed! Error: %s\n", strerror(errno)); - #endif +#endif close(); return; } }else{ - #if DEBUG >= 1 +#if DEBUG >= 1 fprintf(stderr, "Binding failed! Error: %s\n", strerror(errno)); - #endif +#endif close(); return; } -}//Socket::Server Unix Constructor +} //Socket::Server Unix Constructor /// Accept any waiting connections. If the Socket::Server is blocking, this function will block until there is an incoming connection. /// If the Socket::Server is nonblocking, it might return a Socket::Connection that is not connected, so check for this. /// \param nonblock (optional) Whether the newly connected socket should be nonblocking. Default is false (blocking). /// \returns A Socket::Connection, which may or may not be connected, depending on settings and circumstances. Socket::Connection Socket::Server::accept(bool nonblock){ - if (sock < 0){return Socket::Connection(-1);} + if (sock < 0){ + return Socket::Connection( -1); + } struct sockaddr_in6 addrinfo; socklen_t len = sizeof(addrinfo); static char addrconv[INET6_ADDRSTRLEN]; - int r = ::accept(sock, (sockaddr*)&addrinfo, &len); + int r = ::accept(sock, (sockaddr*) &addrinfo, &len); //set the socket to be nonblocking, if requested. //we could do this through accept4 with a flag, but that call is non-standard... if ((r >= 0) && nonblock){ @@ -751,29 +789,29 @@ Socket::Connection Socket::Server::accept(bool nonblock){ Socket::Connection tmp(r); if (r < 0){ if ((errno != EWOULDBLOCK) && (errno != EAGAIN) && (errno != EINTR)){ - #if DEBUG >= 1 +#if DEBUG >= 1 fprintf(stderr, "Error during accept - closing server socket.\n"); - #endif +#endif close(); } }else{ if (addrinfo.sin6_family == AF_INET6){ tmp.remotehost = inet_ntop(AF_INET6, &(addrinfo.sin6_addr), addrconv, INET6_ADDRSTRLEN); - #if DEBUG >= 6 +#if DEBUG >= 6 fprintf(stderr,"IPv6 addr: %s\n", tmp.remotehost.c_str()); - #endif +#endif } if (addrinfo.sin6_family == AF_INET){ - tmp.remotehost = inet_ntop(AF_INET, &(((sockaddr_in*)&addrinfo)->sin_addr), addrconv, INET6_ADDRSTRLEN); - #if DEBUG >= 6 + tmp.remotehost = inet_ntop(AF_INET, &(((sockaddr_in*) &addrinfo)->sin_addr), addrconv, INET6_ADDRSTRLEN); +#if DEBUG >= 6 fprintf(stderr,"IPv4 addr: %s\n", tmp.remotehost.c_str()); - #endif +#endif } if (addrinfo.sin6_family == AF_UNIX){ - #if DEBUG >= 6 +#if DEBUG >= 6 tmp.remotehost = ((sockaddr_un*)&addrinfo)->sun_path; fprintf(stderr,"Unix socket, no address\n"); - #endif +#endif tmp.remotehost = "UNIX_SOCKET"; } } @@ -784,15 +822,16 @@ Socket::Connection Socket::Server::accept(bool nonblock){ /// If the connection is already closed, nothing happens. void Socket::Server::close(){ if (connected()){ - #if DEBUG >= 6 +#if DEBUG >= 6 fprintf(stderr, "ServerSocket closed.\n"); - #endif +#endif shutdown(sock, SHUT_RDWR); errno = EINTR; - while (::close(sock) != 0 && errno == EINTR){} + while (::close(sock) != 0 && errno == EINTR){ + } sock = -1; } -}//Socket::Server::close +} //Socket::Server::close /// Returns the connected-state for this socket. /// Note that this function might be slightly behind the real situation. @@ -801,7 +840,9 @@ void Socket::Server::close(){ /// \returns True if socket is connected, false otherwise. bool Socket::Server::connected() const{ return (sock >= 0); -}//Socket::Server::connected +} //Socket::Server::connected /// Returns internal socket number. -int Socket::Server::getSocket(){return sock;} +int Socket::Server::getSocket(){ + return sock; +} diff --git a/lib/socket.h b/lib/socket.h index c5f907f7..d7f347ad 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -17,10 +17,12 @@ #include //for being friendly with Socket::Connection down below -namespace Buffer{class user;}; +namespace Buffer { + class user; +} ///Holds Socket tools. -namespace Socket{ +namespace Socket { /// A buffer made out of std::string objects that can be efficiently read from and written to. class Buffer{ @@ -34,7 +36,8 @@ namespace Socket{ bool available(unsigned int count); std::string remove(unsigned int count); std::string copy(unsigned int count); - };//Buffer + }; + //Buffer /// This class is for easy communicating through sockets, either TCP or Unix. class Connection{ @@ -51,7 +54,7 @@ namespace Socket{ int iwrite(const void * buffer, int len); ///< Incremental write call. bool iread(Buffer & buffer); ///< Incremental write call that is compatible with Socket::Buffer. bool iwrite(std::string & buffer); ///< Write call that is compatible with std::string. - public: + public: //friends friend class ::Buffer::user; //constructors @@ -86,8 +89,8 @@ namespace Socket{ 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; + bool operator==(const Connection &B) const; + bool operator!=(const Connection &B) const; operator bool() const; }; @@ -97,7 +100,7 @@ namespace Socket{ 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: + 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. @@ -106,5 +109,5 @@ namespace Socket{ void close(); ///< Close connection. int getSocket(); ///< Returns internal socket number. }; - -}; + +} diff --git a/lib/stream.cpp b/lib/stream.cpp index 3b681c09..476c4402 100644 --- a/lib/stream.cpp +++ b/lib/stream.cpp @@ -18,18 +18,21 @@ /// that character is deleted. The original string is modified. void Util::Stream::sanitizeName(std::string & streamname){ //strip anything that isn't numbers, digits or underscores - for (std::string::iterator i=streamname.end()-1; i>=streamname.begin(); --i){ - if (*i == '?'){streamname.erase(i, streamname.end()); break;} - if (!isalpha(*i) && !isdigit(*i) && *i != '_'){ + for (std::string::iterator i = streamname.end() - 1; i >= streamname.begin(); --i){ + if ( *i == '?'){ + streamname.erase(i, streamname.end()); + break; + } + if ( !isalpha( *i) && !isdigit( *i) && *i != '_'){ streamname.erase(i); }else{ - *i=tolower(*i); + *i = tolower( *i); } } } Socket::Connection Util::Stream::getLive(std::string streamname){ - return Socket::Connection("/tmp/mist/stream_"+streamname); + return Socket::Connection("/tmp/mist/stream_" + streamname); } /// Starts a process for a VoD stream. @@ -49,20 +52,20 @@ Socket::Connection Util::Stream::getStream(std::string streamname){ JSON::Value ServConf = JSON::fromFile("/tmp/mist/streamlist"); if (ServConf["streams"].isMember(streamname)){ if (ServConf["streams"][streamname]["channel"]["URL"].asString()[0] == '/'){ - #if DEBUG >= 4 +#if DEBUG >= 4 std::cerr << "Opening VoD stream from file " << ServConf["streams"][streamname]["channel"]["URL"].asString() << std::endl; - #endif +#endif return getVod(ServConf["streams"][streamname]["channel"]["URL"].asString()); }else{ - #if DEBUG >= 4 +#if DEBUG >= 4 std::cerr << "Opening live stream " << streamname << std::endl; - #endif - return Socket::Connection("/tmp/mist/stream_"+streamname); +#endif + return Socket::Connection("/tmp/mist/stream_" + streamname); } } - #if DEBUG >= 4 +#if DEBUG >= 4 std::cerr << "Could not open stream " << streamname << " - stream not found" << std::endl; - #endif +#endif return Socket::Connection(); } @@ -73,7 +76,7 @@ Socket::Connection Util::Stream::getStream(std::string streamname){ /// If the /tmp/mist directory doesn't exist yet, this will create it. Socket::Server Util::Stream::makeLive(std::string streamname){ sanitizeName(streamname); - std::string loc = "/tmp/mist/stream_"+streamname; + std::string loc = "/tmp/mist/stream_" + streamname; //attempt to create the /tmp/mist directory if it doesn't exist already. //ignore errors - we catch all problems in the Socket::Server creation already mkdir("/tmp/mist", S_IRWXU | S_IRWXG | S_IRWXO); diff --git a/lib/stream.h b/lib/stream.h index f08433d9..2c285997 100644 --- a/lib/stream.h +++ b/lib/stream.h @@ -5,7 +5,7 @@ #include #include "socket.h" -namespace Util{ +namespace Util { class Stream{ public: static void sanitizeName(std::string & streamname); diff --git a/lib/timing.cpp b/lib/timing.cpp index 8d7a5f97..f89523fa 100644 --- a/lib/timing.cpp +++ b/lib/timing.cpp @@ -4,22 +4,25 @@ #include "timing.h" #include //for gettimeofday #include //for time and nanosleep - /// Sleeps for the indicated amount of milliseconds or longer. void Util::sleep(int ms){ - if (ms < 0){return;} - if (ms > 10000){return;} + if (ms < 0){ + return; + } + if (ms > 10000){ + return; + } struct timespec T; - T.tv_sec = ms/1000; - T.tv_nsec = 1000000*(ms%1000); - nanosleep(&T, 0); + T.tv_sec = ms / 1000; + T.tv_nsec = 1000000 * (ms % 1000); + nanosleep( &T, 0); } /// 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; + return ((long long int)t.tv_sec) * 1000 + t.tv_nsec / 1000000; } /// Gets the amount of seconds since 01/01/1970. diff --git a/lib/timing.h b/lib/timing.h index b9c43a79..3cbe1e6b 100644 --- a/lib/timing.h +++ b/lib/timing.h @@ -3,8 +3,8 @@ #pragma once -namespace Util{ +namespace Util { void sleep(int ms); ///< Sleeps for the indicated amount of milliseconds or longer. long long int getMS(); ///< Gets the current time in milliseconds. long long int epoch(); ///< Gets the amount of seconds since 01/01/1970. -}; +} From 6813f00b093aa563226045131611778d4bab231c Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Mon, 22 Oct 2012 15:09:24 +0200 Subject: [PATCH 333/788] Initial commit --- lib/Makefile.am | 2 +- lib/nal.cpp | 71 ++++++++++ lib/nal.h | 14 ++ lib/ts_packet.cpp | 349 ++++++++++++++++++++++++++++++++++++++++++++++ lib/ts_packet.h | 147 +++++++++++++++++++ 5 files changed, 582 insertions(+), 1 deletion(-) create mode 100644 lib/nal.cpp create mode 100644 lib/nal.h create mode 100644 lib/ts_packet.cpp create mode 100644 lib/ts_packet.h diff --git a/lib/Makefile.am b/lib/Makefile.am index d3c0bfc6..9d92098f 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -9,4 +9,4 @@ pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = mist-1.0.pc library_includedir=$(includedir)/mist-1.0/mist -library_include_HEADERS = amf.h auth.h base64.h config.h dtsc.h flv_tag.h http_parser.h json.h procs.h rtmpchunks.h socket.h mp4.h ftp.h filesystem.h stream.h timing.h +library_include_HEADERS = amf.h auth.h base64.h config.h dtsc.h flv_tag.h http_parser.h json.h procs.h rtmpchunks.h socket.h mp4.h ftp.h filesystem.h stream.h timing.h nal.h ts_packet.h diff --git a/lib/nal.cpp b/lib/nal.cpp new file mode 100644 index 00000000..dfbc34b4 --- /dev/null +++ b/lib/nal.cpp @@ -0,0 +1,71 @@ +#include "nal.h" + +NAL_Unit::NAL_Unit( ) { + +} + +NAL_Unit::NAL_Unit( std::string & InputData ) { + ReadData( InputData ); +} + +bool NAL_Unit::ReadData( std::string & InputData ) { + 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; +// fprintf( stderr, "NAL_Unit::ReadData --- DataSize: %d\n", InputData.size() ); + if( InputData.size() < 3 ) { 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 + bool FinalByteRead = false; + int Location = std::min( InputData.find( ShortAnnexB ), InputData.find( FullAnnexB ) ); + MyData = InputData.substr(0,Location); + InputData.erase(0,Location); + } else { + if( InputData.size() < 4 ) { return false; } + int UnitLen = (InputData[0] << 24) + (InputData[1] << 16) + (InputData[2] << 8) + InputData[3]; + if( InputData.size() < 4+UnitLen ) { return false; } + InputData.erase(0,4);//Remove Length + MyData = InputData.substr(0,UnitLen); + InputData.erase(0,UnitLen);//Remove this unit from the string + } + return true; +} + +std::string NAL_Unit::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; +} + +std::string NAL_Unit::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; +} + +int NAL_Unit::Type( ) { + return ( MyData[0] & 0x1F ); +} diff --git a/lib/nal.h b/lib/nal.h new file mode 100644 index 00000000..b129f479 --- /dev/null +++ b/lib/nal.h @@ -0,0 +1,14 @@ +#include +#include + +class NAL_Unit { + public: + NAL_Unit( ); + NAL_Unit( std::string & InputData ); + bool ReadData( std::string & InputData ); + std::string AnnexB( bool LongIntro = false ); + std::string SizePrepended( ); + int Type( ); + private: + std::string MyData; +};//NAL_Unit class diff --git a/lib/ts_packet.cpp b/lib/ts_packet.cpp new file mode 100644 index 00000000..bdec17ce --- /dev/null +++ b/lib/ts_packet.cpp @@ -0,0 +1,349 @@ +/// \file ts_packet.cpp +/// Holds all code for the TS namespace. + +#include "ts_packet.h" + +/// This constructor creates an empty TS::Packet, ready for use for either reading or writing. +/// All this constructor does is call TS::Packet::Clear(). +TS::Packet::Packet() { Clear( ); } + +/// This function fills a TS::Packet from provided Data. +/// It fills the content with the first 188 bytes of Data. +/// \param Data The data to be read into the packet. +/// \return true if it was possible to read in a full packet, false otherwise. +bool TS::Packet::FromString( std::string & Data ) { + if( Data.size() < 188 ) { + return false; + } else { + for( int i = 0; i < 188; i++ ) { Buffer[i] = Data[i]; } + Data.erase(0,188); + Free = 0; + } + return true; +} + +/// The deconstructor deletes all space that may be occupied by a TS::Packet. +TS::Packet::~Packet() { } + +/// Sets the PID of a single TS::Packet. +/// \param NewPID The new PID of the packet. +void TS::Packet::PID( int NewPID ) { + Buffer[1] = (Buffer[1] & 0xE0) + ((NewPID & 0x1F00) >> 8 ); + Buffer[2] = (NewPID & 0x00FF); + Free = std::min( Free, 184 ); +} + +/// Gets the PID of a single TS::Packet. +/// \return The value of the PID. +int TS::Packet::PID() { + return (( Buffer[1] & 0x1F ) << 8 ) + Buffer[2]; +} + +/// Sets the Continuity Counter of a single TS::Packet. +/// \param NewContinuity The new Continuity Counter of the packet. +void TS::Packet::ContinuityCounter( int NewContinuity ) { + Buffer[3] = ( Buffer[3] & 0xF0 ) + ( NewContinuity & 0x0F ); + Free = std::min( Free, 184 ); +} + +/// Gets the Continuity Counter of a single TS::Packet. +/// \return The value of the Continuity Counter. +int TS::Packet::ContinuityCounter() { + return ( Buffer[3] & 0x0F ); +} + +/// Gets the amount of bytes that are not written yet in a TS::Packet. +/// \return The amount of bytes that can still be written to this packet. +int TS::Packet::BytesFree( ) { + return Free; +} + +/// Clears a TS::Packet. +void TS::Packet::Clear( ) { + Free = 184; + Buffer[0] = 0x47; + for( int i = 1; i < 188; i++ ) { Buffer[i] = 0x00; } + AdaptationField( 1 ); +} + +/// Sets the selection value for an adaptationfield of a TS::Packet. +/// \param NewSelector The new value of the selection bits. +/// - 1: No AdaptationField. +/// - 2: AdaptationField Only. +/// - 3: AdaptationField followed by Data. +void TS::Packet::AdaptationField( int NewSelector ) { + Buffer[3] = ( Buffer[3] & 0xCF ) + ((NewSelector & 0x03) << 4); + Buffer[4] = 0; + Free = std::min( Free, 184 ); +} + +/// Gets whether a TS::Packet contains an adaptationfield. +/// \return The existence of an adaptationfield. +/// - 0: No adaptationfield present. +/// - 1: Adaptationfield is present. +int TS::Packet::AdaptationField( ) { + return ((Buffer[3] & 0x30) >> 4 ); +} + +/// Sets the PCR (Program Clock Reference) of a TS::Packet. +/// \param NewVal The new PCR Value. +void TS::Packet::PCR( int64_t NewVal ) { + AdaptationField( 3 ); + Buffer[4] = 7; + Buffer[5] = (Buffer[5] | 0x10 ); + int64_t TmpVal = NewVal / 300; + fprintf( stderr, "\tSetting PCR_Base: %d\n", TmpVal ); + Buffer[6] = (((TmpVal>>1)>>24) & 0xFF); + Buffer[7] = (((TmpVal>>1)>>16) & 0xFF); + Buffer[8] = (((TmpVal>>1)>>8) & 0xFF); + Buffer[9] = ((TmpVal>>1) & 0xFF); + int Remainder = NewVal % 300; + Buffer[10] = 0x7E + ((TmpVal & 0x01)<<7) + ((Remainder & 0x0100) >> 8 ); + Buffer[11] = (Remainder & 0x00FF); + Free = std::min( Free, 176 ); +}; + +/// Gets the PCR (Program Clock Reference) of a TS::Packet. +/// \return The value of the PCR. +int64_t TS::Packet::PCR( ) { + if( !AdaptationField() ) { + return -1; + } + if( !(Buffer[5] & 0x10 ) ) { + return -1; + } + int64_t Result = 0; + Result = (((((((Buffer[6] << 8) + Buffer[7]) << 8) + Buffer[8]) << 8) + Buffer[9]) << 1) + ( Buffer[10] & 0x80 ); + Result = Result * 300; + Result += ((Buffer[10] & 0x01) << 8 + Buffer[11]); + return Result; +} + +/// Gets the current length of the adaptationfield. +/// \return The length of the adaptationfield. +int TS::Packet::AdaptationFieldLen( ) { + if( !AdaptationField() ) { + return -1; + } + return (int)Buffer[4]; +} + +/// Prints a packet to stdout, for analyser purposes. +void TS::Packet::Print( ) { + std::cout << "TS Packet: " << (Buffer[0] == 0x47) + << "\n\tNewUnit: " << UnitStart() + << "\n\tPID: " << PID() + << "\n\tContinuity Counter: " << ContinuityCounter() + << "\n\tAdaption Field: " << AdaptationField() << "\n"; + if( AdaptationField() ) { + std::cout << "\t\tAdaption Field Length: " << AdaptationFieldLen() << "\n"; + if( AdaptationFieldLen() ) { + std::cout << "\t\tRandom Access: " << RandomAccess() << "\n"; + } + if( PCR() != -1 ) { + std::cout << "\t\tPCR: " << PCR() << "( " << (double)PCR() / 27000000 << " s )\n"; + } + } +} + +/// Gets whether a new unit starts in this TS::Packet. +/// \return The start of a new unit. +int TS::Packet::UnitStart( ) { + return ( Buffer[1] & 0x40) >> 6; +} + +/// Sets the start of a new unit in this TS::Packet. +/// \param NewVal The new value for the start of a unit. +void TS::Packet::UnitStart( int NewVal ) { + if( NewVal ) { + Buffer[1] = (Buffer[1] | 0x40); + } else { + Buffer[1] = (Buffer[1] & 0xBF); + } +} + +/// Gets whether this TS::Packet can be accessed at random (indicates keyframe). +/// \return Whether or not this TS::Packet contains a keyframe. +int TS::Packet::RandomAccess( ) { + if( AdaptationField() < 2 ) { + return -1; + } + return ( Buffer[5] & 0x40) >> 6; +} + +/// Sets whether this TS::Packet contains a keyframe +/// \param NewVal Whether or not this TS::Packet contains a keyframe. +void TS::Packet::RandomAccess( int NewVal ) { + if( AdaptationField() ) { + if( Buffer[4] == 0 ) { + Buffer[4] = 1; + } + if( NewVal ) { + Buffer[5] = (Buffer[5] | 0x40); + } else { + Buffer[5] = (Buffer[5] & 0xBF); + } + } else { + AdaptationField( 3 ); + Buffer[4] = 1; + if( NewVal ) { + Buffer[5] = 0x40; + } else { + Buffer[5] = 0x00; + } + + } + Free = std::min( Free, 182 ); +} + +/// Transforms the TS::Packet into a standard Program Association Table +void TS::Packet::DefaultPAT( ) { + static int MyCntr = 0; + std::copy( TS::PAT, TS::PAT + 188, Buffer ); + ContinuityCounter( MyCntr ); + Free = 0; + MyCntr = ( (MyCntr + 1) % 0x10); +} + +/// Transforms the TS::Packet into a standard Program Mapping Table +void TS::Packet::DefaultPMT( ) { + static int MyCntr = 0; + std::copy( TS::PMT, TS::PMT + 188, Buffer ); + ContinuityCounter( MyCntr ); + Free = 0; + MyCntr = ( (MyCntr + 1) % 0x10); +} + +/// Generates a string from the contents of the TS::Packet +/// \return A string representation of the packet. +char* TS::Packet::ToString( ) { + return Buffer; +} + +/// Generates a PES Lead-in for a video frame. +/// Starts at the first Free byte. +/// \param NewLen The length of this video frame. +void TS::Packet::PESVideoLeadIn( int NewLen ) { + static int PTS = 126000; + NewLen += 14; + int Offset = ( 188 - Free ); + Buffer[Offset] = 0x00;//PacketStartCodePrefix + Buffer[Offset+1] = 0x00;//PacketStartCodePrefix (Cont) + Buffer[Offset+2] = 0x01;//PacketStartCodePrefix (Cont) + Buffer[Offset+3] = 0xe0;//StreamType Video + Buffer[Offset+4] = (NewLen & 0xFF00) >> 8;//PES PacketLength + Buffer[Offset+5] = (NewLen & 0x00FF);//PES PacketLength (Cont) + Buffer[Offset+6] = 0x80;//Reserved + Flags + Buffer[Offset+7] = 0x80;//PTSOnlyFlag + Flags + Buffer[Offset+8] = 0x05;//PESHeaderDataLength + Buffer[Offset+9] = 0x20 + ((PTS & 0x1C0000000) >> 29 ) + 1;//PTS + Buffer[Offset+10] = 0x00 + ((PTS & 0x03FC00000) >> 22 );//PTS (Cont) + Buffer[Offset+11] = 0x00 + ((PTS & 0x0003F8000) >> 14 ) + 1;//PTS (Cont) + Buffer[Offset+12] = 0x00 + ((PTS & 0x000007F80) >> 7 );//PTS (Cont) + Buffer[Offset+13] = 0x00 + ((PTS & 0x00000007F) << 1) + 1;//PTS (Cont) + + //PesPacket-Wise Prepended Data + + Buffer[Offset+14] = 0x00;//NALU StartCode + Buffer[Offset+15] = 0x00;//NALU StartCode (Cont) + Buffer[Offset+16] = 0x00;//NALU StartCode (Cont) + Buffer[Offset+17] = 0x01;//NALU StartCode (Cont) + Buffer[Offset+18] = 0x09;//NALU EndOfPacket (Einde Vorige Packet) + Buffer[Offset+19] = 0xF0;//NALU EndOfPacket (Cont) + Free = Free - 20; + PTS += 3003; +} + +/// Generates a PES Lead-in for an audio frame. +/// Starts at the first Free byte. +/// \param NewLen The length of this audio frame. +/// \param PTS The timestamp of the audio frame. +void TS::Packet::PESAudioLeadIn( int NewLen, uint64_t PTS ) { + NewLen += 8; + int Offset = ( 188 - Free ) - 2; + Buffer[Offset] = 0x00;//PacketStartCodePrefix + Buffer[Offset+1] = 0x00;//PacketStartCodePrefix (Cont) + Buffer[Offset+2] = 0x01;//PacketStartCodePrefix (Cont) + Buffer[Offset+3] = 0xc0;//StreamType Video + + Buffer[Offset+4] = (NewLen & 0xFF00) >> 8;//PES PacketLength + Buffer[Offset+5] = (NewLen & 0x00FF);//PES PacketLength (Cont) + Buffer[Offset+6] = 0x80;//Reserved + Flags + Buffer[Offset+7] = 0x80;//PTSOnlyFlag + Flags + Buffer[Offset+8] = 0x05;//PESHeaderDataLength + Buffer[Offset+9] = 0x20 + ((PTS & 0x1C0000000) >> 29 ) + 1;//PTS + Buffer[Offset+10] = 0x00 + ((PTS & 0x03FC00000) >> 22 );//PTS (Cont) + Buffer[Offset+11] = 0x00 + ((PTS & 0x0003F8000) >> 14 ) + 1;//PTS (Cont) + Buffer[Offset+12] = 0x00 + ((PTS & 0x000007F80) >> 7 );//PTS (Cont) + Buffer[Offset+13] = 0x00 + ((PTS & 0x00000007F) << 1) + 1;//PTS (Cont) + Free = Free - 12; +} + +/// Fills the free bytes of the TS::Packet. +/// Stores as many bytes from NewVal as possible in the packet. +/// \param NewVal The data to store in the packet. +void TS::Packet::FillFree( std::string & NewVal ) { + int Offset = 188 - Free; + std::copy( NewVal.begin(), NewVal.begin() + Free, Buffer + Offset ); + NewVal.erase(0,Free); + Free = 0; +} + +/// Adds NumBytes of stuffing to the TS::Packet. +/// \param NumBytes the amount of stuffing bytes. +void TS::Packet::AddStuffing( int NumBytes ) { + if( NumBytes <= 0 ) { return; } + if( AdaptationField( ) == 3 ) { + int Offset = Buffer[4]; + Buffer[4] = Offset + NumBytes - 1; + for( int i = 0; i < ( NumBytes -2 ); i ++ ) { + Buffer[6+Offset+i] = 0xFF; + } + Free -= NumBytes; + } else { + AdaptationField( 3 ); + Buffer[4] = NumBytes - 1; + Buffer[5] = 0x00; + for( int i = 0; i < ( NumBytes -2 ); i ++ ) { + Buffer[6+i] = 0xFF; + } + Free -= NumBytes; + } +} + + +/// Transforms this TS::Packet into a standard Service Description Table +void TS::Packet::FFMpegHeader( ) { + static int MyCntr = 0; + std::copy( TS::SDT, TS::SDT + 188, Buffer ); + ContinuityCounter( MyCntr ); + Free = 0; + MyCntr = ( (MyCntr + 1) % 0x10); +} + +int TS::Packet::PESTimeStamp( ) { + if( !UnitStart( ) ) { return -1; } + int PesOffset = 4; + if( AdaptationField( ) >= 2 ) { PesOffset += 1 + AdaptationFieldLen( ); } + fprintf( stderr, "PES Offset: %d\n", PesOffset ); + fprintf( stderr, "PES StartCode: %0.2X %0.2X %0.2X\n", Buffer[PesOffset], Buffer[PesOffset+1], Buffer[PesOffset+2] ); + int MyTimestamp = (Buffer[PesOffset+9] & 0x0F) >> 1; + MyTimestamp = (MyTimestamp << 8) + Buffer[PesOffset+10]; + MyTimestamp = (MyTimestamp << 7) + ((Buffer[PesOffset+11]) >> 1); + MyTimestamp = (MyTimestamp << 8) + Buffer[PesOffset+12]; + MyTimestamp = (MyTimestamp << 7) + ((Buffer[PesOffset+13]) >> 1); + fprintf( stderr, "PES Timestamp: %d\n", MyTimestamp ); + return MyTimestamp; +} + +int TS::Packet::GetDataOffset( ) { + int Offset = 4; + if( AdaptationField( ) >= 2 ) { + Offset += 1 + AdaptationFieldLen( ); + } + if( UnitStart() ) { + Offset += 8;//Default Header + Flag Bytes + Offset += 1 + Buffer[Offset];//HeaderLengthByte + HeaderLength + } + return Offset; +} diff --git a/lib/ts_packet.h b/lib/ts_packet.h new file mode 100644 index 00000000..0fdc42f7 --- /dev/null +++ b/lib/ts_packet.h @@ -0,0 +1,147 @@ +/// \file ts_packet.h +/// Holds all headers for the TS Namespace. + +#pragma once +#include +#include +#include //for uint64_t +#include +#include +#include +#include + +#include "dtsc.h" + +/// Holds all TS processing related code. +namespace TS { + /// Class for reading and writing TS Streams + class Packet { + public: + Packet(); + ~Packet(); + bool FromString( std::string & Data ); + void PID( int NewPID ); + int PID(); + void ContinuityCounter( int NewContinuity ); + int ContinuityCounter(); + void Clear(); + void PCR( int64_t NewVal ); + int64_t PCR(); + void AdaptationField( int NewVal ); + int AdaptationField( ); + int AdaptationFieldLen( ); + void DefaultPAT(); + void DefaultPMT(); + int UnitStart( ); + void UnitStart( int NewVal ); + int RandomAccess( ); + void RandomAccess( int NewVal ); + int BytesFree(); + + void Print(); + char* ToString(); + void PESVideoLeadIn( int NewLen ); + void PESAudioLeadIn( int NewLen, uint64_t PTS = 0 ); + void FillFree( std::string & PackageData ); + void AddStuffing( int NumBytes ); + void FFMpegHeader( ); + + int PESTimeStamp( ); + int GetDataOffset( ); + private: + int Free; + char Buffer[188];///< The actual data + };//TS::Packet class + + /// 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. + static inline std::string GetAudioHeader( int FrameLen ) { + char StandardHeader[7] = {0xFF,0xF1,0x4C,0x80,0x00,0x1F,0xFC}; + FrameLen += 7; + StandardHeader[3] = ( StandardHeader[3] & 0xFC ) + ( ( FrameLen & 0x00001800 ) >> 11 ); + StandardHeader[4] = ( ( FrameLen & 0x000007F8 ) >> 3 ); + StandardHeader[5] = ( StandardHeader[5] & 0x3F ) + ( ( FrameLen & 0x00000007 ) << 5 ); + return std::string(StandardHeader,7); + } + + /// A standard Program Association Table, as generated by FFMPEG. + /// Seems to be independent of the stream. + 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 + }; + + /// A standard Program Mapping Table, as generated by FFMPEG. + /// Contains both Audio and Video mappings, works also on video- or audio-only streams. + static char PMT[188] = { + 0x47,0x50,0x00,0x10, 0x00,0x02,0xB0,0x1D, 0x00,0x01,0xC1,0x00, 0x00,0xE1,0x00,0xF0, + 0x00,0x1B,0xE1,0x00, 0xF0,0x00,0x0F,0xE1, 0x01,0xF0,0x06,0x0A, 0x04,0x65,0x6E,0x67, + 0x00,0x8D,0x82,0x9A, 0x07,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 + }; + + /// A standard Sevice Description Table, as generated by FFMPEG. + /// Not used in our connector, provided for compatibility means + static char SDT[188] = { + 0x47,0x40,0x11,0x10, 0x00,0x42,0xF0,0x25, 0x00,0x01,0xC1,0x00, 0x00,0x00,0x01,0xFF, + 0x00,0x01,0xFC,0x80, 0x14,0x48,0x12,0x01, 0x06,0x46,0x46,0x6D, 0x70,0x65,0x67,0x09, + 0x53,0x65,0x72,0x76, 0x69,0x63,0x65,0x30, 0x31,0xA7,0x79,0xA0, 0x03,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 + }; + + /// A standard Picture Parameter Set, as generated by FFMPEG. + /// Seems to be stream-independent. + static char PPS[24] = { + 0x00,0x00,0x00,0x01, + 0x27,0x4D,0x40,0x1F, + 0xA9,0x18,0x0A,0x00, + 0xB7,0x60,0x0D,0x40, + 0x40,0x40,0x4C,0x2B, + 0x5E,0xF7,0xC0,0x40 + }; + + /// A standard Sequence Parameter Set, as generated by FFMPEG. + /// Seems to be stream-independent. + static char SPS[8] = { + 0x00,0x00,0x00,0x01, + 0x28,0xCE,0x09,0xC8 + }; + + /// The full Bytesteam Nal-Header. + static char NalHeader[4] = { + 0x00,0x00,0x00,0x01 + }; + + /// The shortened Bytesteam Nal-Header. + static char ShortNalHeader[3] = { + 0x00,0x00,0x01 + }; +};//TS namespace From c43fba4275b0753e046f53d0d2daf999900d9db0 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Tue, 4 Dec 2012 11:04:33 +0100 Subject: [PATCH 334/788] Working converter --- lib/ts_packet.cpp | 42 ++++++++++++++++++++++++------------------ lib/ts_packet.h | 2 +- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/lib/ts_packet.cpp b/lib/ts_packet.cpp index bdec17ce..4e0d320a 100644 --- a/lib/ts_packet.cpp +++ b/lib/ts_packet.cpp @@ -223,8 +223,8 @@ char* TS::Packet::ToString( ) { /// Generates a PES Lead-in for a video frame. /// Starts at the first Free byte. /// \param NewLen The length of this video frame. -void TS::Packet::PESVideoLeadIn( int NewLen ) { - static int PTS = 126000; +void TS::Packet::PESVideoLeadIn( int NewLen, long long unsigned int PTS ) { + //static long long unsigned int PTS = 0; NewLen += 14; int Offset = ( 188 - Free ); Buffer[Offset] = 0x00;//PacketStartCodePrefix @@ -234,24 +234,30 @@ void TS::Packet::PESVideoLeadIn( int NewLen ) { Buffer[Offset+4] = (NewLen & 0xFF00) >> 8;//PES PacketLength Buffer[Offset+5] = (NewLen & 0x00FF);//PES PacketLength (Cont) Buffer[Offset+6] = 0x80;//Reserved + Flags - Buffer[Offset+7] = 0x80;//PTSOnlyFlag + Flags - Buffer[Offset+8] = 0x05;//PESHeaderDataLength - Buffer[Offset+9] = 0x20 + ((PTS & 0x1C0000000) >> 29 ) + 1;//PTS - Buffer[Offset+10] = 0x00 + ((PTS & 0x03FC00000) >> 22 );//PTS (Cont) - Buffer[Offset+11] = 0x00 + ((PTS & 0x0003F8000) >> 14 ) + 1;//PTS (Cont) - Buffer[Offset+12] = 0x00 + ((PTS & 0x000007F80) >> 7 );//PTS (Cont) - Buffer[Offset+13] = 0x00 + ((PTS & 0x00000007F) << 1) + 1;//PTS (Cont) - + if( PTS != 1 ) { + Buffer[Offset+7] = 0x80;//PTSOnlyFlag + Flags + Buffer[Offset+8] = 0x05;//PESHeaderDataLength + Buffer[Offset+9] = 0x20 + ((PTS & 0x1C0000000) >> 29 ) + 1;//PTS + Buffer[Offset+10] = 0x00 + ((PTS & 0x03FC00000) >> 22 );//PTS (Cont) + Buffer[Offset+11] = 0x00 + ((PTS & 0x0003F8000) >> 14 ) + 1;//PTS (Cont) + Buffer[Offset+12] = 0x00 + ((PTS & 0x000007F80) >> 7 );//PTS (Cont) + Buffer[Offset+13] = 0x00 + ((PTS & 0x00000007F) << 1) + 1;//PTS (Cont) + Offset += 14; + } else { + Buffer[Offset+7] = 0x00;//PTSOnlyFlag + Flags + Buffer[Offset+8] = 0x00;//PESHeaderDataLength + Offset += 9; + } //PesPacket-Wise Prepended Data - Buffer[Offset+14] = 0x00;//NALU StartCode - Buffer[Offset+15] = 0x00;//NALU StartCode (Cont) - Buffer[Offset+16] = 0x00;//NALU StartCode (Cont) - Buffer[Offset+17] = 0x01;//NALU StartCode (Cont) - Buffer[Offset+18] = 0x09;//NALU EndOfPacket (Einde Vorige Packet) - Buffer[Offset+19] = 0xF0;//NALU EndOfPacket (Cont) - Free = Free - 20; - PTS += 3003; + Buffer[Offset] = 0x00;//NALU StartCode + Buffer[Offset+1] = 0x00;//NALU StartCode (Cont) + Buffer[Offset+2] = 0x00;//NALU StartCode (Cont) + Buffer[Offset+3] = 0x01;//NALU StartCode (Cont) + Buffer[Offset+4] = 0x09;//NALU EndOfPacket (Einde Vorige Packet) + Buffer[Offset+5] = 0xF0;//NALU EndOfPacket (Cont) + Free = Free - (Offset+6); + //PTS += 3003; } /// Generates a PES Lead-in for an audio frame. diff --git a/lib/ts_packet.h b/lib/ts_packet.h index 0fdc42f7..67b62107 100644 --- a/lib/ts_packet.h +++ b/lib/ts_packet.h @@ -40,7 +40,7 @@ namespace TS { void Print(); char* ToString(); - void PESVideoLeadIn( int NewLen ); + void PESVideoLeadIn( int NewLen, long long unsigned int PTS = 1 ); void PESAudioLeadIn( int NewLen, uint64_t PTS = 0 ); void FillFree( std::string & PackageData ); void AddStuffing( int NumBytes ); From 611d4db3d76ddeb05d0d107169880e82f05cbf32 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Tue, 11 Dec 2012 15:25:59 +0100 Subject: [PATCH 335/788] Fixed a lot of bugs in TS, updated to std-string implementation --- lib/ts_packet.cpp | 271 ++++++++++++++++++++-------------------------- lib/ts_packet.h | 46 +------- 2 files changed, 122 insertions(+), 195 deletions(-) diff --git a/lib/ts_packet.cpp b/lib/ts_packet.cpp index 4e0d320a..e6079f1a 100644 --- a/lib/ts_packet.cpp +++ b/lib/ts_packet.cpp @@ -5,7 +5,10 @@ /// This constructor creates an empty TS::Packet, ready for use for either reading or writing. /// All this constructor does is call TS::Packet::Clear(). -TS::Packet::Packet() { Clear( ); } +TS::Packet::Packet() { + strBuf.reserve( 188 ); + Clear( ); +} /// This function fills a TS::Packet from provided Data. /// It fills the content with the first 188 bytes of Data. @@ -15,9 +18,8 @@ bool TS::Packet::FromString( std::string & Data ) { if( Data.size() < 188 ) { return false; } else { - for( int i = 0; i < 188; i++ ) { Buffer[i] = Data[i]; } + strBuf = Data.substr(0,188); Data.erase(0,188); - Free = 0; } return true; } @@ -28,42 +30,41 @@ TS::Packet::~Packet() { } /// Sets the PID of a single TS::Packet. /// \param NewPID The new PID of the packet. void TS::Packet::PID( int NewPID ) { - Buffer[1] = (Buffer[1] & 0xE0) + ((NewPID & 0x1F00) >> 8 ); - Buffer[2] = (NewPID & 0x00FF); - Free = std::min( Free, 184 ); + strBuf[1] = (strBuf[1] & 0xE0) + ((NewPID & 0x1F00) >> 8 ); + strBuf[2] = (NewPID & 0x00FF); } /// Gets the PID of a single TS::Packet. /// \return The value of the PID. int TS::Packet::PID() { - return (( Buffer[1] & 0x1F ) << 8 ) + Buffer[2]; + return (( strBuf[1] & 0x1F ) << 8 ) + strBuf[2]; } /// Sets the Continuity Counter of a single TS::Packet. /// \param NewContinuity The new Continuity Counter of the packet. void TS::Packet::ContinuityCounter( int NewContinuity ) { - Buffer[3] = ( Buffer[3] & 0xF0 ) + ( NewContinuity & 0x0F ); - Free = std::min( Free, 184 ); + strBuf[3] = ( strBuf[3] & 0xF0 ) + ( NewContinuity & 0x0F ); } /// Gets the Continuity Counter of a single TS::Packet. /// \return The value of the Continuity Counter. int TS::Packet::ContinuityCounter() { - return ( Buffer[3] & 0x0F ); + return ( strBuf[3] & 0x0F ); } /// Gets the amount of bytes that are not written yet in a TS::Packet. /// \return The amount of bytes that can still be written to this packet. int TS::Packet::BytesFree( ) { - return Free; + return 188 - strBuf.size(); } /// Clears a TS::Packet. void TS::Packet::Clear( ) { - Free = 184; - Buffer[0] = 0x47; - for( int i = 1; i < 188; i++ ) { Buffer[i] = 0x00; } - AdaptationField( 1 ); + strBuf.resize(4); + strBuf[0] = 0x47; + strBuf[1] = 0x00; + strBuf[2] = 0x00; + strBuf[3] = 0x10; } /// Sets the selection value for an adaptationfield of a TS::Packet. @@ -72,9 +73,12 @@ void TS::Packet::Clear( ) { /// - 2: AdaptationField Only. /// - 3: AdaptationField followed by Data. void TS::Packet::AdaptationField( int NewSelector ) { - Buffer[3] = ( Buffer[3] & 0xCF ) + ((NewSelector & 0x03) << 4); - Buffer[4] = 0; - Free = std::min( Free, 184 ); + strBuf[3] = ( strBuf[3] & 0xCF ) + ((NewSelector & 0x03) << 4); + if( NewSelector & 0x02 ) { + strBuf[4] = 0x00; + } else { + strBuf.resize(4); + } } /// Gets whether a TS::Packet contains an adaptationfield. @@ -82,26 +86,28 @@ void TS::Packet::AdaptationField( int NewSelector ) { /// - 0: No adaptationfield present. /// - 1: Adaptationfield is present. int TS::Packet::AdaptationField( ) { - return ((Buffer[3] & 0x30) >> 4 ); + return ((strBuf[3] & 0x30) >> 4 ); } /// Sets the PCR (Program Clock Reference) of a TS::Packet. /// \param NewVal The new PCR Value. void TS::Packet::PCR( int64_t NewVal ) { + if( strBuf.size() < 12 ) { + strBuf.resize( 12 ); + } AdaptationField( 3 ); - Buffer[4] = 7; - Buffer[5] = (Buffer[5] | 0x10 ); + strBuf[4] = 0x07; + strBuf[5] = (strBuf[5] | 0x10 ); int64_t TmpVal = NewVal / 300; fprintf( stderr, "\tSetting PCR_Base: %d\n", TmpVal ); - Buffer[6] = (((TmpVal>>1)>>24) & 0xFF); - Buffer[7] = (((TmpVal>>1)>>16) & 0xFF); - Buffer[8] = (((TmpVal>>1)>>8) & 0xFF); - Buffer[9] = ((TmpVal>>1) & 0xFF); + strBuf[6] = (((TmpVal>>1)>>24) & 0xFF); + strBuf[7] = (((TmpVal>>1)>>16) & 0xFF); + strBuf[8] = (((TmpVal>>1)>>8) & 0xFF); + strBuf[9] = ((TmpVal>>1) & 0xFF); int Remainder = NewVal % 300; - Buffer[10] = 0x7E + ((TmpVal & 0x01)<<7) + ((Remainder & 0x0100) >> 8 ); - Buffer[11] = (Remainder & 0x00FF); - Free = std::min( Free, 176 ); -}; + strBuf[10] = 0x7E + ((TmpVal & 0x01)<<7) + ((Remainder & 0x0100) >> 8 ); + strBuf[11] = (Remainder & 0x00FF); +} /// Gets the PCR (Program Clock Reference) of a TS::Packet. /// \return The value of the PCR. @@ -109,13 +115,13 @@ int64_t TS::Packet::PCR( ) { if( !AdaptationField() ) { return -1; } - if( !(Buffer[5] & 0x10 ) ) { + if( !(strBuf[5] & 0x10 ) ) { return -1; } int64_t Result = 0; - Result = (((((((Buffer[6] << 8) + Buffer[7]) << 8) + Buffer[8]) << 8) + Buffer[9]) << 1) + ( Buffer[10] & 0x80 ); + Result = (((strBuf[6] << 24) + (strBuf[7] << 16) + (strBuf[8] << 8) + strBuf[9]) << 1) + ( strBuf[10] & 0x80 >> 7 ); Result = Result * 300; - Result += ((Buffer[10] & 0x01) << 8 + Buffer[11]); + Result += ((strBuf[10] & 0x01) << 8 + strBuf[11]); return Result; } @@ -125,12 +131,12 @@ int TS::Packet::AdaptationFieldLen( ) { if( !AdaptationField() ) { return -1; } - return (int)Buffer[4]; + return (int)strBuf[4]; } /// Prints a packet to stdout, for analyser purposes. void TS::Packet::Print( ) { - std::cout << "TS Packet: " << (Buffer[0] == 0x47) + std::cout << "TS Packet: " << (strBuf[0] == 0x47) << "\n\tNewUnit: " << UnitStart() << "\n\tPID: " << PID() << "\n\tContinuity Counter: " << ContinuityCounter() @@ -149,16 +155,16 @@ void TS::Packet::Print( ) { /// Gets whether a new unit starts in this TS::Packet. /// \return The start of a new unit. int TS::Packet::UnitStart( ) { - return ( Buffer[1] & 0x40) >> 6; + return ( strBuf[1] & 0x40) >> 6; } /// Sets the start of a new unit in this TS::Packet. /// \param NewVal The new value for the start of a unit. void TS::Packet::UnitStart( int NewVal ) { if( NewVal ) { - Buffer[1] = (Buffer[1] | 0x40); + strBuf[1] |= 0x40; } else { - Buffer[1] = (Buffer[1] & 0xBF); + strBuf[1] &= 0xBF; } } @@ -168,96 +174,95 @@ int TS::Packet::RandomAccess( ) { if( AdaptationField() < 2 ) { return -1; } - return ( Buffer[5] & 0x40) >> 6; + return ( strBuf[5] & 0x40) >> 6; } /// Sets whether this TS::Packet contains a keyframe /// \param NewVal Whether or not this TS::Packet contains a keyframe. void TS::Packet::RandomAccess( int NewVal ) { - if( AdaptationField() ) { - if( Buffer[4] == 0 ) { - Buffer[4] = 1; - } + if( AdaptationField() == 3 ) { + if( strBuf.size() < 6 ) { + strBuf.resize(6); + } + if( !strBuf[4] ) { + strBuf[4] = 1; + } if( NewVal ) { - Buffer[5] = (Buffer[5] | 0x40); + strBuf[5] |= 0x40; } else { - Buffer[5] = (Buffer[5] & 0xBF); + strBuf[5] &= 0xBF; } } else { - AdaptationField( 3 ); - Buffer[4] = 1; - if( NewVal ) { - Buffer[5] = 0x40; - } else { - Buffer[5] = 0x00; + if( strBuf.size() < 6 ) { + strBuf.resize(6); + } + AdaptationField( 3 ); + strBuf[4] = 1; + if( NewVal ) { + strBuf[5] = 0x40; + } else { + strBuf[5] = 0x00; } - } - Free = std::min( Free, 182 ); } /// Transforms the TS::Packet into a standard Program Association Table void TS::Packet::DefaultPAT( ) { static int MyCntr = 0; - std::copy( TS::PAT, TS::PAT + 188, Buffer ); + strBuf = std::string( TS::PAT, 188 ); ContinuityCounter( MyCntr ); - Free = 0; MyCntr = ( (MyCntr + 1) % 0x10); } /// Transforms the TS::Packet into a standard Program Mapping Table void TS::Packet::DefaultPMT( ) { static int MyCntr = 0; - std::copy( TS::PMT, TS::PMT + 188, Buffer ); + strBuf = std::string( TS::PMT, 188 ); ContinuityCounter( MyCntr ); - Free = 0; MyCntr = ( (MyCntr + 1) % 0x10); } /// Generates a string from the contents of the TS::Packet /// \return A string representation of the packet. -char* TS::Packet::ToString( ) { - return Buffer; +const char* TS::Packet::ToString( ) { + if( strBuf.size() != 188 ) { + std::cerr << "Error: Size invalid (" << strBuf.size() << ") Invalid data from this point on." << std::endl; + } + return strBuf.c_str( ); } /// Generates a PES Lead-in for a video frame. /// Starts at the first Free byte. /// \param NewLen The length of this video frame. void TS::Packet::PESVideoLeadIn( int NewLen, long long unsigned int PTS ) { - //static long long unsigned int PTS = 0; - NewLen += 14; - int Offset = ( 188 - Free ); - Buffer[Offset] = 0x00;//PacketStartCodePrefix - Buffer[Offset+1] = 0x00;//PacketStartCodePrefix (Cont) - Buffer[Offset+2] = 0x01;//PacketStartCodePrefix (Cont) - Buffer[Offset+3] = 0xe0;//StreamType Video - Buffer[Offset+4] = (NewLen & 0xFF00) >> 8;//PES PacketLength - Buffer[Offset+5] = (NewLen & 0x00FF);//PES PacketLength (Cont) - Buffer[Offset+6] = 0x80;//Reserved + Flags + NewLen += ( PTS == 1 ? 9 : 14 ); + strBuf += (char)0x00;//PacketStartCodePrefix + strBuf += (char)0x00;//PacketStartCodePrefix (Cont) + strBuf += (char)0x01;//PacketStartCodePrefix (Cont) + strBuf += (char)0xe0;//StreamType Video + strBuf += (char)((NewLen & 0xFF00) >> 8);//PES PacketLength + strBuf += (char)(NewLen & 0x00FF);//PES PacketLength (Cont) + strBuf += (char)0x80;//Reserved + Flags if( PTS != 1 ) { - Buffer[Offset+7] = 0x80;//PTSOnlyFlag + Flags - Buffer[Offset+8] = 0x05;//PESHeaderDataLength - Buffer[Offset+9] = 0x20 + ((PTS & 0x1C0000000) >> 29 ) + 1;//PTS - Buffer[Offset+10] = 0x00 + ((PTS & 0x03FC00000) >> 22 );//PTS (Cont) - Buffer[Offset+11] = 0x00 + ((PTS & 0x0003F8000) >> 14 ) + 1;//PTS (Cont) - Buffer[Offset+12] = 0x00 + ((PTS & 0x000007F80) >> 7 );//PTS (Cont) - Buffer[Offset+13] = 0x00 + ((PTS & 0x00000007F) << 1) + 1;//PTS (Cont) - Offset += 14; + strBuf += (char)0x80;//PTSOnlyFlag + Flags + strBuf += (char)0x05;//PESHeaderDataLength + strBuf += (char)(0x20 + ((PTS & 0x1C0000000) >> 29 ) + 1);//PTS + strBuf += (char)((PTS & 0x03FC00000) >> 22 );//PTS (Cont) + strBuf += (char)(((PTS & 0x0003F8000) >> 14 ) + 1);//PTS (Cont) + strBuf += (char)((PTS & 0x000007F80) >> 7 );//PTS (Cont) + strBuf += (char)(((PTS & 0x00000007F) << 1) + 1);//PTS (Cont) } else { - Buffer[Offset+7] = 0x00;//PTSOnlyFlag + Flags - Buffer[Offset+8] = 0x00;//PESHeaderDataLength - Offset += 9; + strBuf += (char)0x00;//PTSOnlyFlag + Flags + strBuf += (char)0x00;//PESHeaderDataLength } //PesPacket-Wise Prepended Data - Buffer[Offset] = 0x00;//NALU StartCode - Buffer[Offset+1] = 0x00;//NALU StartCode (Cont) - Buffer[Offset+2] = 0x00;//NALU StartCode (Cont) - Buffer[Offset+3] = 0x01;//NALU StartCode (Cont) - Buffer[Offset+4] = 0x09;//NALU EndOfPacket (Einde Vorige Packet) - Buffer[Offset+5] = 0xF0;//NALU EndOfPacket (Cont) - Free = Free - (Offset+6); - //PTS += 3003; + strBuf += (char)0x00;//NALU StartCode + strBuf += (char)0x00;//NALU StartCode (Cont) + strBuf += (char)0x00;//NALU StartCode (Cont) + strBuf += (char)0x01;//NALU StartCode (Cont) + strBuf += (char)0x09;//NALU EndOfPacket (Einde Vorige Packet) + strBuf += (char)0xF0;//NALU EndOfPacket (Cont) } /// Generates a PES Lead-in for an audio frame. @@ -266,33 +271,30 @@ void TS::Packet::PESVideoLeadIn( int NewLen, long long unsigned int PTS ) { /// \param PTS The timestamp of the audio frame. void TS::Packet::PESAudioLeadIn( int NewLen, uint64_t PTS ) { NewLen += 8; - int Offset = ( 188 - Free ) - 2; - Buffer[Offset] = 0x00;//PacketStartCodePrefix - Buffer[Offset+1] = 0x00;//PacketStartCodePrefix (Cont) - Buffer[Offset+2] = 0x01;//PacketStartCodePrefix (Cont) - Buffer[Offset+3] = 0xc0;//StreamType Video + strBuf += (char)0x00;//PacketStartCodePrefix + strBuf += (char)0x00;//PacketStartCodePrefix (Cont) + strBuf += (char)0x01;//PacketStartCodePrefix (Cont) + strBuf += (char)0xc0;//StreamType Audio - Buffer[Offset+4] = (NewLen & 0xFF00) >> 8;//PES PacketLength - Buffer[Offset+5] = (NewLen & 0x00FF);//PES PacketLength (Cont) - Buffer[Offset+6] = 0x80;//Reserved + Flags - Buffer[Offset+7] = 0x80;//PTSOnlyFlag + Flags - Buffer[Offset+8] = 0x05;//PESHeaderDataLength - Buffer[Offset+9] = 0x20 + ((PTS & 0x1C0000000) >> 29 ) + 1;//PTS - Buffer[Offset+10] = 0x00 + ((PTS & 0x03FC00000) >> 22 );//PTS (Cont) - Buffer[Offset+11] = 0x00 + ((PTS & 0x0003F8000) >> 14 ) + 1;//PTS (Cont) - Buffer[Offset+12] = 0x00 + ((PTS & 0x000007F80) >> 7 );//PTS (Cont) - Buffer[Offset+13] = 0x00 + ((PTS & 0x00000007F) << 1) + 1;//PTS (Cont) - Free = Free - 12; + strBuf += (char)((NewLen & 0xFF00) >> 8);//PES PacketLength + strBuf += (char)(NewLen & 0x00FF);//PES PacketLength (Cont) + strBuf += (char)0x80;//Reserved + Flags + strBuf += (char)0x80;//PTSOnlyFlag + Flags + strBuf += (char)0x05;//PESHeaderDataLength + strBuf += (char)(0x20 + ((PTS & 0x1C0000000) >> 29 ) + 1);//PTS + strBuf += (char)((PTS & 0x03FC00000) >> 22 );//PTS (Cont) + strBuf += (char)(((PTS & 0x0003F8000) >> 14 ) + 1);//PTS (Cont) + strBuf += (char)((PTS & 0x000007F80) >> 7 );//PTS (Cont) + strBuf += (char)(((PTS & 0x00000007F) << 1) + 1);//PTS (Cont) } /// Fills the free bytes of the TS::Packet. /// Stores as many bytes from NewVal as possible in the packet. /// \param NewVal The data to store in the packet. void TS::Packet::FillFree( std::string & NewVal ) { - int Offset = 188 - Free; - std::copy( NewVal.begin(), NewVal.begin() + Free, Buffer + Offset ); - NewVal.erase(0,Free); - Free = 0; + int toWrite = 188-strBuf.size(); + strBuf += NewVal.substr(0,toWrite); + NewVal.erase(0,toWrite); } /// Adds NumBytes of stuffing to the TS::Packet. @@ -300,56 +302,19 @@ void TS::Packet::FillFree( std::string & NewVal ) { void TS::Packet::AddStuffing( int NumBytes ) { if( NumBytes <= 0 ) { return; } if( AdaptationField( ) == 3 ) { - int Offset = Buffer[4]; - Buffer[4] = Offset + NumBytes - 1; + int Offset = strBuf[4]; + strBuf[4] = Offset + NumBytes - 1; + strBuf.resize(7+Offset+NumBytes-2); for( int i = 0; i < ( NumBytes -2 ); i ++ ) { - Buffer[6+Offset+i] = 0xFF; + strBuf[6+Offset+i] = 0xFF; } - Free -= NumBytes; } else { AdaptationField( 3 ); - Buffer[4] = NumBytes - 1; - Buffer[5] = 0x00; - for( int i = 0; i < ( NumBytes -2 ); i ++ ) { - Buffer[6+i] = 0xFF; + strBuf.resize(6); + strBuf[4] = (char)(NumBytes - 1); + strBuf[5] = (char)0x00; + for( int i = 0; i < ( NumBytes - 2 ); i ++ ) { + strBuf += (char)0xFF; } - Free -= NumBytes; } } - - -/// Transforms this TS::Packet into a standard Service Description Table -void TS::Packet::FFMpegHeader( ) { - static int MyCntr = 0; - std::copy( TS::SDT, TS::SDT + 188, Buffer ); - ContinuityCounter( MyCntr ); - Free = 0; - MyCntr = ( (MyCntr + 1) % 0x10); -} - -int TS::Packet::PESTimeStamp( ) { - if( !UnitStart( ) ) { return -1; } - int PesOffset = 4; - if( AdaptationField( ) >= 2 ) { PesOffset += 1 + AdaptationFieldLen( ); } - fprintf( stderr, "PES Offset: %d\n", PesOffset ); - fprintf( stderr, "PES StartCode: %0.2X %0.2X %0.2X\n", Buffer[PesOffset], Buffer[PesOffset+1], Buffer[PesOffset+2] ); - int MyTimestamp = (Buffer[PesOffset+9] & 0x0F) >> 1; - MyTimestamp = (MyTimestamp << 8) + Buffer[PesOffset+10]; - MyTimestamp = (MyTimestamp << 7) + ((Buffer[PesOffset+11]) >> 1); - MyTimestamp = (MyTimestamp << 8) + Buffer[PesOffset+12]; - MyTimestamp = (MyTimestamp << 7) + ((Buffer[PesOffset+13]) >> 1); - fprintf( stderr, "PES Timestamp: %d\n", MyTimestamp ); - return MyTimestamp; -} - -int TS::Packet::GetDataOffset( ) { - int Offset = 4; - if( AdaptationField( ) >= 2 ) { - Offset += 1 + AdaptationFieldLen( ); - } - if( UnitStart() ) { - Offset += 8;//Default Header + Flag Bytes - Offset += 1 + Buffer[Offset];//HeaderLengthByte + HeaderLength - } - return Offset; -} diff --git a/lib/ts_packet.h b/lib/ts_packet.h index 67b62107..a8336743 100644 --- a/lib/ts_packet.h +++ b/lib/ts_packet.h @@ -39,18 +39,15 @@ namespace TS { int BytesFree(); void Print(); - char* ToString(); + const char* ToString(); void PESVideoLeadIn( int NewLen, long long unsigned int PTS = 1 ); void PESAudioLeadIn( int NewLen, uint64_t PTS = 0 ); void FillFree( std::string & PackageData ); void AddStuffing( int NumBytes ); - void FFMpegHeader( ); - - int PESTimeStamp( ); - int GetDataOffset( ); private: - int Free; - char Buffer[188];///< The actual data + //int Free; + std::string strBuf; + //char Buffer[188];///< The actual data };//TS::Packet class /// Constructs an audio header to be used on each audio frame. @@ -100,41 +97,6 @@ namespace TS { 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF }; - /// A standard Sevice Description Table, as generated by FFMPEG. - /// Not used in our connector, provided for compatibility means - static char SDT[188] = { - 0x47,0x40,0x11,0x10, 0x00,0x42,0xF0,0x25, 0x00,0x01,0xC1,0x00, 0x00,0x00,0x01,0xFF, - 0x00,0x01,0xFC,0x80, 0x14,0x48,0x12,0x01, 0x06,0x46,0x46,0x6D, 0x70,0x65,0x67,0x09, - 0x53,0x65,0x72,0x76, 0x69,0x63,0x65,0x30, 0x31,0xA7,0x79,0xA0, 0x03,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 - }; - - /// A standard Picture Parameter Set, as generated by FFMPEG. - /// Seems to be stream-independent. - static char PPS[24] = { - 0x00,0x00,0x00,0x01, - 0x27,0x4D,0x40,0x1F, - 0xA9,0x18,0x0A,0x00, - 0xB7,0x60,0x0D,0x40, - 0x40,0x40,0x4C,0x2B, - 0x5E,0xF7,0xC0,0x40 - }; - - /// A standard Sequence Parameter Set, as generated by FFMPEG. - /// Seems to be stream-independent. - static char SPS[8] = { - 0x00,0x00,0x00,0x01, - 0x28,0xCE,0x09,0xC8 - }; - /// The full Bytesteam Nal-Header. static char NalHeader[4] = { 0x00,0x00,0x00,0x01 From 140fd4febf046fe5380d27f2f124f0f660474a30 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Mon, 17 Dec 2012 13:52:31 +0100 Subject: [PATCH 336/788] Bugfix in TS concerning audio init data, Makefile fix --- lib/Makefile.am | 2 +- lib/ts_packet.cpp | 1 - lib/ts_packet.h | 12 ++++++++---- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/Makefile.am b/lib/Makefile.am index 9d92098f..4de85fa8 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,7 +1,7 @@ AM_CPPFLAGS = $(global_CFLAGS) lib_LTLIBRARIES=libmist-1.0.la -libmist_1_0_la_SOURCES=amf.h amf.cpp auth.h auth.cpp base64.h base64.cpp config.h config.cpp dtsc.h dtsc.cpp flv_tag.h flv_tag.cpp http_parser.h http_parser.cpp json.h json.cpp procs.h procs.cpp rtmpchunks.h rtmpchunks.cpp socket.h socket.cpp mp4.h mp4.cpp ftp.h ftp.cpp filesystem.h filesystem.cpp stream.h stream.cpp timing.h timing.cpp +libmist_1_0_la_SOURCES=amf.h amf.cpp auth.h auth.cpp base64.h base64.cpp config.h config.cpp dtsc.h dtsc.cpp flv_tag.h flv_tag.cpp http_parser.h http_parser.cpp json.h json.cpp procs.h procs.cpp rtmpchunks.h rtmpchunks.cpp socket.h socket.cpp mp4.h mp4.cpp ftp.h ftp.cpp filesystem.h filesystem.cpp stream.h stream.cpp timing.h timing.cpp ts_packet.cpp ts_packet.h libmist_1_0_la_LIBADD=-lssl -lcrypto -lrt libmist_1_0_la_LDFLAGS = -version-info 3:0:0 diff --git a/lib/ts_packet.cpp b/lib/ts_packet.cpp index e6079f1a..07e7f54c 100644 --- a/lib/ts_packet.cpp +++ b/lib/ts_packet.cpp @@ -99,7 +99,6 @@ void TS::Packet::PCR( int64_t NewVal ) { strBuf[4] = 0x07; strBuf[5] = (strBuf[5] | 0x10 ); int64_t TmpVal = NewVal / 300; - fprintf( stderr, "\tSetting PCR_Base: %d\n", TmpVal ); strBuf[6] = (((TmpVal>>1)>>24) & 0xFF); strBuf[7] = (((TmpVal>>1)>>16) & 0xFF); strBuf[8] = (((TmpVal>>1)>>8) & 0xFF); diff --git a/lib/ts_packet.h b/lib/ts_packet.h index a8336743..c85bd39d 100644 --- a/lib/ts_packet.h +++ b/lib/ts_packet.h @@ -54,12 +54,16 @@ namespace TS { /// 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. - static inline std::string GetAudioHeader( int FrameLen ) { - char StandardHeader[7] = {0xFF,0xF1,0x4C,0x80,0x00,0x1F,0xFC}; + static inline std::string GetAudioHeader( int FrameLen, std::string initData ) { + char StandardHeader[7] = {0xFF,0xF1,0x00,0x00,0x00,0x1F,0xFC}; FrameLen += 7; - StandardHeader[3] = ( StandardHeader[3] & 0xFC ) + ( ( FrameLen & 0x00001800 ) >> 11 ); + 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); + StandardHeader[3] = ((initData[1] & 0x18 ) << 3 ); + StandardHeader[3] |= ( ( FrameLen & 0x00001800 ) >> 11 ); StandardHeader[4] = ( ( FrameLen & 0x000007F8 ) >> 3 ); - StandardHeader[5] = ( StandardHeader[5] & 0x3F ) + ( ( FrameLen & 0x00000007 ) << 5 ); + StandardHeader[5] |= ( ( FrameLen & 0x00000007 ) << 5 ); return std::string(StandardHeader,7); } From 439d5bf98cfb9b8927b949a9787b121fe20616a7 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Thu, 27 Dec 2012 22:16:13 +0100 Subject: [PATCH 337/788] Changes for bipbop on ts. Audio now synchronises properly --- lib/ts_packet.cpp | 4 ++-- lib/ts_packet.h | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/ts_packet.cpp b/lib/ts_packet.cpp index 07e7f54c..41040fa7 100644 --- a/lib/ts_packet.cpp +++ b/lib/ts_packet.cpp @@ -303,9 +303,9 @@ void TS::Packet::AddStuffing( int NumBytes ) { if( AdaptationField( ) == 3 ) { int Offset = strBuf[4]; strBuf[4] = Offset + NumBytes - 1; - strBuf.resize(7+Offset+NumBytes-2); + strBuf.resize(5+Offset+NumBytes-2); for( int i = 0; i < ( NumBytes -2 ); i ++ ) { - strBuf[6+Offset+i] = 0xFF; + strBuf[5+Offset+i] = 0xFF; } } else { AdaptationField( 3 ); diff --git a/lib/ts_packet.h b/lib/ts_packet.h index c85bd39d..4fb6a129 100644 --- a/lib/ts_packet.h +++ b/lib/ts_packet.h @@ -59,8 +59,8 @@ namespace TS { 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); - StandardHeader[3] = ((initData[1] & 0x18 ) << 3 ); + 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 ); @@ -87,9 +87,9 @@ namespace TS { /// A standard Program Mapping Table, as generated by FFMPEG. /// Contains both Audio and Video mappings, works also on video- or audio-only streams. static char PMT[188] = { - 0x47,0x50,0x00,0x10, 0x00,0x02,0xB0,0x1D, 0x00,0x01,0xC1,0x00, 0x00,0xE1,0x00,0xF0, - 0x00,0x1B,0xE1,0x00, 0xF0,0x00,0x0F,0xE1, 0x01,0xF0,0x06,0x0A, 0x04,0x65,0x6E,0x67, - 0x00,0x8D,0x82,0x9A, 0x07,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, + 0x47,0x50,0x00,0x10, 0x00,0x02,0xB0,0x17, 0x00,0x01,0xC1,0x00, 0x00,0xE1,0x00,0xF0, + 0x00,0x1B,0xE1,0x00, 0xF0,0x00,0x0F,0xE1, 0x01,0xF0,0x00,0x2F, 0x44,0xB9,0x9B,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, From 05416da269ae0a8f42bfeac8b474b058055e9530 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 31 Dec 2012 16:33:45 +0100 Subject: [PATCH 338/788] Standardized coding style for TS-related code. --- lib/nal.cpp | 75 +++++++------ lib/nal.h | 17 +-- lib/ts_packet.cpp | 276 +++++++++++++++++++++++----------------------- lib/ts_packet.h | 123 +++++++++------------ 4 files changed, 246 insertions(+), 245 deletions(-) diff --git a/lib/nal.cpp b/lib/nal.cpp index dfbc34b4..41a7034d 100644 --- a/lib/nal.cpp +++ b/lib/nal.cpp @@ -1,14 +1,14 @@ #include "nal.h" -NAL_Unit::NAL_Unit( ) { - +NAL_Unit::NAL_Unit(){ + } -NAL_Unit::NAL_Unit( std::string & InputData ) { - ReadData( InputData ); +NAL_Unit::NAL_Unit(std::string & InputData){ + ReadData(InputData); } -bool NAL_Unit::ReadData( std::string & InputData ) { +bool NAL_Unit::ReadData(std::string & InputData){ std::string FullAnnexB; FullAnnexB += (char)0x00; FullAnnexB += (char)0x00; @@ -19,53 +19,66 @@ bool NAL_Unit::ReadData( std::string & InputData ) { ShortAnnexB += (char)0x00; ShortAnnexB += (char)0x01; // fprintf( stderr, "NAL_Unit::ReadData --- DataSize: %d\n", InputData.size() ); - if( InputData.size() < 3 ) { return false; } + if (InputData.size() < 3){ + 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 ) { + 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 + InputData.erase(0, 3); //Intro Bytes bool FinalByteRead = false; - int Location = std::min( InputData.find( ShortAnnexB ), InputData.find( FullAnnexB ) ); - MyData = InputData.substr(0,Location); - InputData.erase(0,Location); - } else { - if( InputData.size() < 4 ) { return false; } + int Location = std::min(InputData.find(ShortAnnexB), InputData.find(FullAnnexB)); + MyData = InputData.substr(0, Location); + InputData.erase(0, Location); + }else{ + if (InputData.size() < 4){ + return false; + } int UnitLen = (InputData[0] << 24) + (InputData[1] << 16) + (InputData[2] << 8) + InputData[3]; - if( InputData.size() < 4+UnitLen ) { return false; } - InputData.erase(0,4);//Remove Length - MyData = InputData.substr(0,UnitLen); - InputData.erase(0,UnitLen);//Remove this unit from the string + if (InputData.size() < 4 + UnitLen){ + return false; + } + InputData.erase(0, 4); //Remove Length + MyData = InputData.substr(0, UnitLen); + InputData.erase(0, UnitLen); //Remove this unit from the string } return true; } -std::string NAL_Unit::AnnexB( bool LongIntro ) { +std::string NAL_Unit::AnnexB(bool LongIntro){ std::string Result; - if( MyData.size() ) { - if( LongIntro ) { Result += (char)0x00; } + if (MyData.size()){ + if (LongIntro){ + Result += (char)0x00; + } Result += (char)0x00; Result += (char)0x00; - Result += (char)0x01;//Annex B Lead-In + Result += (char)0x01; //Annex B Lead-In Result += MyData; } return Result; } -std::string NAL_Unit::SizePrepended( ) { +std::string NAL_Unit::SizePrepended(){ std::string Result; - if( MyData.size() ) { + 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 += (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; } -int NAL_Unit::Type( ) { - return ( MyData[0] & 0x1F ); +int NAL_Unit::Type(){ + return (MyData[0] & 0x1F); } diff --git a/lib/nal.h b/lib/nal.h index b129f479..c8307eaf 100644 --- a/lib/nal.h +++ b/lib/nal.h @@ -1,14 +1,15 @@ #include #include -class NAL_Unit { +class NAL_Unit{ public: - NAL_Unit( ); - NAL_Unit( std::string & InputData ); - bool ReadData( std::string & InputData ); - std::string AnnexB( bool LongIntro = false ); - std::string SizePrepended( ); - int Type( ); + NAL_Unit(); + NAL_Unit(std::string & InputData); + bool ReadData(std::string & InputData); + std::string AnnexB(bool LongIntro = false); + std::string SizePrepended(); + int Type(); private: std::string MyData; -};//NAL_Unit class +}; +//NAL_Unit class diff --git a/lib/ts_packet.cpp b/lib/ts_packet.cpp index 41040fa7..f8d580cb 100644 --- a/lib/ts_packet.cpp +++ b/lib/ts_packet.cpp @@ -5,61 +5,62 @@ /// This constructor creates an empty TS::Packet, ready for use for either reading or writing. /// All this constructor does is call TS::Packet::Clear(). -TS::Packet::Packet() { - strBuf.reserve( 188 ); - Clear( ); +TS::Packet::Packet(){ + strBuf.reserve(188); + Clear(); } /// This function fills a TS::Packet from provided Data. /// It fills the content with the first 188 bytes of Data. /// \param Data The data to be read into the packet. /// \return true if it was possible to read in a full packet, false otherwise. -bool TS::Packet::FromString( std::string & Data ) { - if( Data.size() < 188 ) { - return false; - } else { - strBuf = Data.substr(0,188); - Data.erase(0,188); +bool TS::Packet::FromString(std::string & Data){ + if (Data.size() < 188){ + return false; + }else{ + strBuf = Data.substr(0, 188); + Data.erase(0, 188); } return true; } /// The deconstructor deletes all space that may be occupied by a TS::Packet. -TS::Packet::~Packet() { } +TS::Packet::~Packet(){ +} /// Sets the PID of a single TS::Packet. /// \param NewPID The new PID of the packet. -void TS::Packet::PID( int NewPID ) { - strBuf[1] = (strBuf[1] & 0xE0) + ((NewPID & 0x1F00) >> 8 ); +void TS::Packet::PID(int NewPID){ + strBuf[1] = (strBuf[1] & 0xE0) + ((NewPID & 0x1F00) >> 8); strBuf[2] = (NewPID & 0x00FF); } /// Gets the PID of a single TS::Packet. /// \return The value of the PID. -int TS::Packet::PID() { - return (( strBuf[1] & 0x1F ) << 8 ) + strBuf[2]; +int TS::Packet::PID(){ + return ((strBuf[1] & 0x1F) << 8) + strBuf[2]; } /// Sets the Continuity Counter of a single TS::Packet. /// \param NewContinuity The new Continuity Counter of the packet. -void TS::Packet::ContinuityCounter( int NewContinuity ) { - strBuf[3] = ( strBuf[3] & 0xF0 ) + ( NewContinuity & 0x0F ); +void TS::Packet::ContinuityCounter(int NewContinuity){ + strBuf[3] = (strBuf[3] & 0xF0) + (NewContinuity & 0x0F); } /// Gets the Continuity Counter of a single TS::Packet. /// \return The value of the Continuity Counter. -int TS::Packet::ContinuityCounter() { - return ( strBuf[3] & 0x0F ); +int TS::Packet::ContinuityCounter(){ + return (strBuf[3] & 0x0F); } /// Gets the amount of bytes that are not written yet in a TS::Packet. /// \return The amount of bytes that can still be written to this packet. -int TS::Packet::BytesFree( ) { +int TS::Packet::BytesFree(){ return 188 - strBuf.size(); } /// Clears a TS::Packet. -void TS::Packet::Clear( ) { +void TS::Packet::Clear(){ strBuf.resize(4); strBuf[0] = 0x47; strBuf[1] = 0x00; @@ -72,11 +73,11 @@ void TS::Packet::Clear( ) { /// - 1: No AdaptationField. /// - 2: AdaptationField Only. /// - 3: AdaptationField followed by Data. -void TS::Packet::AdaptationField( int NewSelector ) { - strBuf[3] = ( strBuf[3] & 0xCF ) + ((NewSelector & 0x03) << 4); - if( NewSelector & 0x02 ) { +void TS::Packet::AdaptationField(int NewSelector){ + strBuf[3] = (strBuf[3] & 0xCF) + ((NewSelector & 0x03) << 4); + if (NewSelector & 0x02){ strBuf[4] = 0x00; - } else { + }else{ strBuf.resize(4); } } @@ -85,40 +86,40 @@ void TS::Packet::AdaptationField( int NewSelector ) { /// \return The existence of an adaptationfield. /// - 0: No adaptationfield present. /// - 1: Adaptationfield is present. -int TS::Packet::AdaptationField( ) { - return ((strBuf[3] & 0x30) >> 4 ); +int TS::Packet::AdaptationField(){ + return ((strBuf[3] & 0x30) >> 4); } /// Sets the PCR (Program Clock Reference) of a TS::Packet. /// \param NewVal The new PCR Value. -void TS::Packet::PCR( int64_t NewVal ) { - if( strBuf.size() < 12 ) { - strBuf.resize( 12 ); +void TS::Packet::PCR(int64_t NewVal){ + if (strBuf.size() < 12){ + strBuf.resize(12); } - AdaptationField( 3 ); + AdaptationField(3); strBuf[4] = 0x07; - strBuf[5] = (strBuf[5] | 0x10 ); + strBuf[5] = (strBuf[5] | 0x10); int64_t TmpVal = NewVal / 300; - strBuf[6] = (((TmpVal>>1)>>24) & 0xFF); - strBuf[7] = (((TmpVal>>1)>>16) & 0xFF); - strBuf[8] = (((TmpVal>>1)>>8) & 0xFF); - strBuf[9] = ((TmpVal>>1) & 0xFF); + strBuf[6] = (((TmpVal >> 1) >> 24) & 0xFF); + strBuf[7] = (((TmpVal >> 1) >> 16) & 0xFF); + strBuf[8] = (((TmpVal >> 1) >> 8) & 0xFF); + strBuf[9] = ((TmpVal >> 1) & 0xFF); int Remainder = NewVal % 300; - strBuf[10] = 0x7E + ((TmpVal & 0x01)<<7) + ((Remainder & 0x0100) >> 8 ); + strBuf[10] = 0x7E + ((TmpVal & 0x01) << 7) + ((Remainder & 0x0100) >> 8); strBuf[11] = (Remainder & 0x00FF); } /// Gets the PCR (Program Clock Reference) of a TS::Packet. /// \return The value of the PCR. -int64_t TS::Packet::PCR( ) { - if( !AdaptationField() ) { +int64_t TS::Packet::PCR(){ + if ( !AdaptationField()){ return -1; } - if( !(strBuf[5] & 0x10 ) ) { + if ( !(strBuf[5] & 0x10)){ return -1; } int64_t Result = 0; - Result = (((strBuf[6] << 24) + (strBuf[7] << 16) + (strBuf[8] << 8) + strBuf[9]) << 1) + ( strBuf[10] & 0x80 >> 7 ); + Result = (((strBuf[6] << 24) + (strBuf[7] << 16) + (strBuf[8] << 8) + strBuf[9]) << 1) + (strBuf[10] & 0x80 >> 7); Result = Result * 300; Result += ((strBuf[10] & 0x01) << 8 + strBuf[11]); return Result; @@ -126,26 +127,23 @@ int64_t TS::Packet::PCR( ) { /// Gets the current length of the adaptationfield. /// \return The length of the adaptationfield. -int TS::Packet::AdaptationFieldLen( ) { - if( !AdaptationField() ) { +int TS::Packet::AdaptationFieldLen(){ + if ( !AdaptationField()){ return -1; } return (int)strBuf[4]; } /// Prints a packet to stdout, for analyser purposes. -void TS::Packet::Print( ) { - std::cout << "TS Packet: " << (strBuf[0] == 0x47) - << "\n\tNewUnit: " << UnitStart() - << "\n\tPID: " << PID() - << "\n\tContinuity Counter: " << ContinuityCounter() - << "\n\tAdaption Field: " << AdaptationField() << "\n"; - if( AdaptationField() ) { +void TS::Packet::Print(){ + std::cout << "TS Packet: " << (strBuf[0] == 0x47) << "\n\tNewUnit: " << UnitStart() << "\n\tPID: " << PID() << "\n\tContinuity Counter: " + << ContinuityCounter() << "\n\tAdaption Field: " << AdaptationField() << "\n"; + if (AdaptationField()){ std::cout << "\t\tAdaption Field Length: " << AdaptationFieldLen() << "\n"; - if( AdaptationFieldLen() ) { + if (AdaptationFieldLen()){ std::cout << "\t\tRandom Access: " << RandomAccess() << "\n"; } - if( PCR() != -1 ) { + if (PCR() != -1){ std::cout << "\t\tPCR: " << PCR() << "( " << (double)PCR() / 27000000 << " s )\n"; } } @@ -153,166 +151,168 @@ void TS::Packet::Print( ) { /// Gets whether a new unit starts in this TS::Packet. /// \return The start of a new unit. -int TS::Packet::UnitStart( ) { - return ( strBuf[1] & 0x40) >> 6; +int TS::Packet::UnitStart(){ + return (strBuf[1] & 0x40) >> 6; } /// Sets the start of a new unit in this TS::Packet. /// \param NewVal The new value for the start of a unit. -void TS::Packet::UnitStart( int NewVal ) { - if( NewVal ) { +void TS::Packet::UnitStart(int NewVal){ + if (NewVal){ strBuf[1] |= 0x40; - } else { + }else{ strBuf[1] &= 0xBF; } } /// Gets whether this TS::Packet can be accessed at random (indicates keyframe). /// \return Whether or not this TS::Packet contains a keyframe. -int TS::Packet::RandomAccess( ) { - if( AdaptationField() < 2 ) { +int TS::Packet::RandomAccess(){ + if (AdaptationField() < 2){ return -1; } - return ( strBuf[5] & 0x40) >> 6; + return (strBuf[5] & 0x40) >> 6; } /// Sets whether this TS::Packet contains a keyframe /// \param NewVal Whether or not this TS::Packet contains a keyframe. -void TS::Packet::RandomAccess( int NewVal ) { - if( AdaptationField() == 3 ) { - if( strBuf.size() < 6 ) { +void TS::Packet::RandomAccess(int NewVal){ + if (AdaptationField() == 3){ + if (strBuf.size() < 6){ strBuf.resize(6); } - if( !strBuf[4] ) { - strBuf[4] = 1; + if ( !strBuf[4]){ + strBuf[4] = 1; } - if( NewVal ) { + if (NewVal){ strBuf[5] |= 0x40; - } else { + }else{ strBuf[5] &= 0xBF; } - } else { - if( strBuf.size() < 6 ) { + }else{ + if (strBuf.size() < 6){ strBuf.resize(6); } - AdaptationField( 3 ); + AdaptationField(3); strBuf[4] = 1; - if( NewVal ) { + if (NewVal){ strBuf[5] = 0x40; - } else { + }else{ strBuf[5] = 0x00; } } } /// Transforms the TS::Packet into a standard Program Association Table -void TS::Packet::DefaultPAT( ) { +void TS::Packet::DefaultPAT(){ static int MyCntr = 0; - strBuf = std::string( TS::PAT, 188 ); - ContinuityCounter( MyCntr ); - MyCntr = ( (MyCntr + 1) % 0x10); + strBuf = std::string(TS::PAT, 188); + ContinuityCounter(MyCntr); + MyCntr = ((MyCntr + 1) % 0x10); } /// Transforms the TS::Packet into a standard Program Mapping Table -void TS::Packet::DefaultPMT( ) { +void TS::Packet::DefaultPMT(){ static int MyCntr = 0; - strBuf = std::string( TS::PMT, 188 ); - ContinuityCounter( MyCntr ); - MyCntr = ( (MyCntr + 1) % 0x10); + strBuf = std::string(TS::PMT, 188); + ContinuityCounter(MyCntr); + MyCntr = ((MyCntr + 1) % 0x10); } /// Generates a string from the contents of the TS::Packet /// \return A string representation of the packet. -const char* TS::Packet::ToString( ) { - if( strBuf.size() != 188 ) { +const char* TS::Packet::ToString(){ + if (strBuf.size() != 188){ std::cerr << "Error: Size invalid (" << strBuf.size() << ") Invalid data from this point on." << std::endl; } - return strBuf.c_str( ); + return strBuf.c_str(); } /// Generates a PES Lead-in for a video frame. /// Starts at the first Free byte. /// \param NewLen The length of this video frame. -void TS::Packet::PESVideoLeadIn( int NewLen, long long unsigned int PTS ) { - NewLen += ( PTS == 1 ? 9 : 14 ); - strBuf += (char)0x00;//PacketStartCodePrefix - strBuf += (char)0x00;//PacketStartCodePrefix (Cont) - strBuf += (char)0x01;//PacketStartCodePrefix (Cont) - strBuf += (char)0xe0;//StreamType Video - strBuf += (char)((NewLen & 0xFF00) >> 8);//PES PacketLength - strBuf += (char)(NewLen & 0x00FF);//PES PacketLength (Cont) - strBuf += (char)0x80;//Reserved + Flags - if( PTS != 1 ) { - strBuf += (char)0x80;//PTSOnlyFlag + Flags - strBuf += (char)0x05;//PESHeaderDataLength - strBuf += (char)(0x20 + ((PTS & 0x1C0000000) >> 29 ) + 1);//PTS - strBuf += (char)((PTS & 0x03FC00000) >> 22 );//PTS (Cont) - strBuf += (char)(((PTS & 0x0003F8000) >> 14 ) + 1);//PTS (Cont) - strBuf += (char)((PTS & 0x000007F80) >> 7 );//PTS (Cont) - strBuf += (char)(((PTS & 0x00000007F) << 1) + 1);//PTS (Cont) - } else { - strBuf += (char)0x00;//PTSOnlyFlag + Flags - strBuf += (char)0x00;//PESHeaderDataLength +void TS::Packet::PESVideoLeadIn(int NewLen, long long unsigned int PTS){ + NewLen += (PTS == 1 ? 9 : 14); + strBuf += (char)0x00; //PacketStartCodePrefix + strBuf += (char)0x00; //PacketStartCodePrefix (Cont) + strBuf += (char)0x01; //PacketStartCodePrefix (Cont) + strBuf += (char)0xe0; //StreamType Video + strBuf += (char)((NewLen & 0xFF00) >> 8); //PES PacketLength + strBuf += (char)(NewLen & 0x00FF); //PES PacketLength (Cont) + strBuf += (char)0x80; //Reserved + Flags + if (PTS != 1){ + strBuf += (char)0x80; //PTSOnlyFlag + Flags + strBuf += (char)0x05; //PESHeaderDataLength + strBuf += (char)(0x20 + ((PTS & 0x1C0000000) >> 29) + 1); //PTS + strBuf += (char)((PTS & 0x03FC00000) >> 22); //PTS (Cont) + strBuf += (char)(((PTS & 0x0003F8000) >> 14) + 1); //PTS (Cont) + strBuf += (char)((PTS & 0x000007F80) >> 7); //PTS (Cont) + strBuf += (char)(((PTS & 0x00000007F) << 1) + 1); //PTS (Cont) + }else{ + strBuf += (char)0x00; //PTSOnlyFlag + Flags + strBuf += (char)0x00; //PESHeaderDataLength } //PesPacket-Wise Prepended Data - - strBuf += (char)0x00;//NALU StartCode - strBuf += (char)0x00;//NALU StartCode (Cont) - strBuf += (char)0x00;//NALU StartCode (Cont) - strBuf += (char)0x01;//NALU StartCode (Cont) - strBuf += (char)0x09;//NALU EndOfPacket (Einde Vorige Packet) - strBuf += (char)0xF0;//NALU EndOfPacket (Cont) + + strBuf += (char)0x00; //NALU StartCode + strBuf += (char)0x00; //NALU StartCode (Cont) + strBuf += (char)0x00; //NALU StartCode (Cont) + strBuf += (char)0x01; //NALU StartCode (Cont) + strBuf += (char)0x09; //NALU EndOfPacket (Einde Vorige Packet) + strBuf += (char)0xF0; //NALU EndOfPacket (Cont) } /// Generates a PES Lead-in for an audio frame. /// Starts at the first Free byte. /// \param NewLen The length of this audio frame. /// \param PTS The timestamp of the audio frame. -void TS::Packet::PESAudioLeadIn( int NewLen, uint64_t PTS ) { +void TS::Packet::PESAudioLeadIn(int NewLen, uint64_t PTS){ NewLen += 8; - strBuf += (char)0x00;//PacketStartCodePrefix - strBuf += (char)0x00;//PacketStartCodePrefix (Cont) - strBuf += (char)0x01;//PacketStartCodePrefix (Cont) - strBuf += (char)0xc0;//StreamType Audio - - strBuf += (char)((NewLen & 0xFF00) >> 8);//PES PacketLength - strBuf += (char)(NewLen & 0x00FF);//PES PacketLength (Cont) - strBuf += (char)0x80;//Reserved + Flags - strBuf += (char)0x80;//PTSOnlyFlag + Flags - strBuf += (char)0x05;//PESHeaderDataLength - strBuf += (char)(0x20 + ((PTS & 0x1C0000000) >> 29 ) + 1);//PTS - strBuf += (char)((PTS & 0x03FC00000) >> 22 );//PTS (Cont) - strBuf += (char)(((PTS & 0x0003F8000) >> 14 ) + 1);//PTS (Cont) - strBuf += (char)((PTS & 0x000007F80) >> 7 );//PTS (Cont) - strBuf += (char)(((PTS & 0x00000007F) << 1) + 1);//PTS (Cont) + strBuf += (char)0x00; //PacketStartCodePrefix + strBuf += (char)0x00; //PacketStartCodePrefix (Cont) + strBuf += (char)0x01; //PacketStartCodePrefix (Cont) + strBuf += (char)0xc0; //StreamType Audio + + strBuf += (char)((NewLen & 0xFF00) >> 8); //PES PacketLength + strBuf += (char)(NewLen & 0x00FF); //PES PacketLength (Cont) + strBuf += (char)0x80; //Reserved + Flags + strBuf += (char)0x80; //PTSOnlyFlag + Flags + strBuf += (char)0x05; //PESHeaderDataLength + strBuf += (char)(0x20 + ((PTS & 0x1C0000000) >> 29) + 1); //PTS + strBuf += (char)((PTS & 0x03FC00000) >> 22); //PTS (Cont) + strBuf += (char)(((PTS & 0x0003F8000) >> 14) + 1); //PTS (Cont) + strBuf += (char)((PTS & 0x000007F80) >> 7); //PTS (Cont) + strBuf += (char)(((PTS & 0x00000007F) << 1) + 1); //PTS (Cont) } /// Fills the free bytes of the TS::Packet. /// Stores as many bytes from NewVal as possible in the packet. /// \param NewVal The data to store in the packet. -void TS::Packet::FillFree( std::string & NewVal ) { - int toWrite = 188-strBuf.size(); - strBuf += NewVal.substr(0,toWrite); - NewVal.erase(0,toWrite); +void TS::Packet::FillFree(std::string & NewVal){ + int toWrite = 188 - strBuf.size(); + strBuf += NewVal.substr(0, toWrite); + NewVal.erase(0, toWrite); } /// Adds NumBytes of stuffing to the TS::Packet. /// \param NumBytes the amount of stuffing bytes. -void TS::Packet::AddStuffing( int NumBytes ) { - if( NumBytes <= 0 ) { return; } - if( AdaptationField( ) == 3 ) { +void TS::Packet::AddStuffing(int NumBytes){ + if (NumBytes <= 0){ + return; + } + if (AdaptationField() == 3){ int Offset = strBuf[4]; strBuf[4] = Offset + NumBytes - 1; - strBuf.resize(5+Offset+NumBytes-2); - for( int i = 0; i < ( NumBytes -2 ); i ++ ) { - strBuf[5+Offset+i] = 0xFF; + strBuf.resize(5 + Offset + NumBytes - 2); + for (int i = 0; i < (NumBytes - 2); i++){ + strBuf[5 + Offset + i] = 0xFF; } - } else { - AdaptationField( 3 ); + }else{ + AdaptationField(3); strBuf.resize(6); strBuf[4] = (char)(NumBytes - 1); strBuf[5] = (char)0x00; - for( int i = 0; i < ( NumBytes - 2 ); i ++ ) { + for (int i = 0; i < (NumBytes - 2); i++){ strBuf += (char)0xFF; } } diff --git a/lib/ts_packet.h b/lib/ts_packet.h index 4fb6a129..5525958b 100644 --- a/lib/ts_packet.h +++ b/lib/ts_packet.h @@ -15,99 +15,86 @@ /// Holds all TS processing related code. namespace TS { /// Class for reading and writing TS Streams - class Packet { + class Packet{ public: Packet(); ~Packet(); - bool FromString( std::string & Data ); - void PID( int NewPID ); + bool FromString(std::string & Data); + void PID(int NewPID); int PID(); - void ContinuityCounter( int NewContinuity ); + void ContinuityCounter(int NewContinuity); int ContinuityCounter(); void Clear(); - void PCR( int64_t NewVal ); + void PCR(int64_t NewVal); int64_t PCR(); - void AdaptationField( int NewVal ); - int AdaptationField( ); - int AdaptationFieldLen( ); + void AdaptationField(int NewVal); + int AdaptationField(); + int AdaptationFieldLen(); void DefaultPAT(); void DefaultPMT(); - int UnitStart( ); - void UnitStart( int NewVal ); - int RandomAccess( ); - void RandomAccess( int NewVal ); + int UnitStart(); + void UnitStart(int NewVal); + int RandomAccess(); + void RandomAccess(int NewVal); int BytesFree(); - + void Print(); const char* ToString(); - void PESVideoLeadIn( int NewLen, long long unsigned int PTS = 1 ); - void PESAudioLeadIn( int NewLen, uint64_t PTS = 0 ); - void FillFree( std::string & PackageData ); - void AddStuffing( int NumBytes ); + void PESVideoLeadIn(int NewLen, long long unsigned int PTS = 1); + void PESAudioLeadIn(int NewLen, uint64_t PTS = 0); + void FillFree(std::string & PackageData); + void AddStuffing(int NumBytes); private: //int Free; std::string strBuf; //char Buffer[188];///< The actual data - };//TS::Packet class - + }; + //TS::Packet class + /// 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. - static inline std::string GetAudioHeader( int FrameLen, std::string initData ) { - char StandardHeader[7] = {0xFF,0xF1,0x00,0x00,0x00,0x1F,0xFC}; + 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); + 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. - 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 - }; - + 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}; + /// A standard Program Mapping Table, as generated by FFMPEG. /// Contains both Audio and Video mappings, works also on video- or audio-only streams. - static char PMT[188] = { - 0x47,0x50,0x00,0x10, 0x00,0x02,0xB0,0x17, 0x00,0x01,0xC1,0x00, 0x00,0xE1,0x00,0xF0, - 0x00,0x1B,0xE1,0x00, 0xF0,0x00,0x0F,0xE1, 0x01,0xF0,0x00,0x2F, 0x44,0xB9,0x9B,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 - }; - + static char PMT[188] = {0x47, 0x50, 0x00, 0x10, 0x00, 0x02, 0xB0, 0x17, 0x00, 0x01, 0xC1, 0x00, 0x00, 0xE1, 0x00, 0xF0, 0x00, 0x1B, 0xE1, 0x00, + 0xF0, 0x00, 0x0F, 0xE1, 0x01, 0xF0, 0x00, 0x2F, 0x44, 0xB9, 0x9B, 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}; + /// The full Bytesteam Nal-Header. - static char NalHeader[4] = { - 0x00,0x00,0x00,0x01 - }; - + static char NalHeader[4] = {0x00, 0x00, 0x00, 0x01}; + /// The shortened Bytesteam Nal-Header. - static char ShortNalHeader[3] = { - 0x00,0x00,0x01 - }; -};//TS namespace + static char ShortNalHeader[3] = {0x00, 0x00, 0x01}; +} +; +//TS namespace From 7dbbc6c892e209d9a89db5254e513166b70725b3 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 31 Dec 2012 19:13:04 +0100 Subject: [PATCH 339/788] Implemented chunked transfer encoding support. --- lib/http_parser.cpp | 44 +++++++++++++++++++++++++++++++++++++++++++- lib/http_parser.h | 2 ++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/lib/http_parser.cpp b/lib/http_parser.cpp index 2f8ebb72..1dea052f 100644 --- a/lib/http_parser.cpp +++ b/lib/http_parser.cpp @@ -13,6 +13,8 @@ HTTP::Parser::Parser(){ void HTTP::Parser::Clean(){ seenHeaders = false; seenReq = false; + getChunks = false; + doingChunk = 0; method = "GET"; url = "/"; protocol = "HTTP/1.1"; @@ -200,6 +202,10 @@ bool HTTP::Parser::parse(std::string & HTTPbuffer){ body.reserve(length); } } + if (GetHeader("Transfer-Encoding") == "chunked"){ + getChunks = true; + doingChunk = 0; + } }else{ f = tmpA.find(':'); if (f == std::string::npos) continue; @@ -223,7 +229,43 @@ bool HTTP::Parser::parse(std::string & HTTPbuffer){ return false; } }else{ - return true; + if (getChunks){ + 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 (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; + } } } } diff --git a/lib/http_parser.h b/lib/http_parser.h index 77b12aed..300edf56 100644 --- a/lib/http_parser.h +++ b/lib/http_parser.h @@ -36,6 +36,8 @@ namespace HTTP { private: bool seenHeaders; bool seenReq; + bool getChunks; + unsigned int doingChunk; bool parse(std::string & HTTPbuffer); void parseVars(std::string data); std::string builder; From 9b6f63671cc09ba13593ef018f0dac8e59e6e73e Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 31 Dec 2012 20:06:30 +0100 Subject: [PATCH 340/788] Fix compiling on 32-bit systems. --- lib/ts_packet.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/ts_packet.cpp b/lib/ts_packet.cpp index f8d580cb..67533043 100644 --- a/lib/ts_packet.cpp +++ b/lib/ts_packet.cpp @@ -243,11 +243,11 @@ void TS::Packet::PESVideoLeadIn(int NewLen, long long unsigned int PTS){ if (PTS != 1){ strBuf += (char)0x80; //PTSOnlyFlag + Flags strBuf += (char)0x05; //PESHeaderDataLength - strBuf += (char)(0x20 + ((PTS & 0x1C0000000) >> 29) + 1); //PTS - strBuf += (char)((PTS & 0x03FC00000) >> 22); //PTS (Cont) - strBuf += (char)(((PTS & 0x0003F8000) >> 14) + 1); //PTS (Cont) - strBuf += (char)((PTS & 0x000007F80) >> 7); //PTS (Cont) - strBuf += (char)(((PTS & 0x00000007F) << 1) + 1); //PTS (Cont) + strBuf += (char)(0x20 + ((PTS & 0x1C0000000LL) >> 29) + 1); //PTS + strBuf += (char)((PTS & 0x03FC00000LL) >> 22); //PTS (Cont) + strBuf += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //PTS (Cont) + strBuf += (char)((PTS & 0x000007F80LL) >> 7); //PTS (Cont) + strBuf += (char)(((PTS & 0x00000007FLL) << 1) + 1); //PTS (Cont) }else{ strBuf += (char)0x00; //PTSOnlyFlag + Flags strBuf += (char)0x00; //PESHeaderDataLength @@ -278,11 +278,11 @@ void TS::Packet::PESAudioLeadIn(int NewLen, uint64_t PTS){ strBuf += (char)0x80; //Reserved + Flags strBuf += (char)0x80; //PTSOnlyFlag + Flags strBuf += (char)0x05; //PESHeaderDataLength - strBuf += (char)(0x20 + ((PTS & 0x1C0000000) >> 29) + 1); //PTS - strBuf += (char)((PTS & 0x03FC00000) >> 22); //PTS (Cont) - strBuf += (char)(((PTS & 0x0003F8000) >> 14) + 1); //PTS (Cont) - strBuf += (char)((PTS & 0x000007F80) >> 7); //PTS (Cont) - strBuf += (char)(((PTS & 0x00000007F) << 1) + 1); //PTS (Cont) + strBuf += (char)(0x20 + ((PTS & 0x1C0000000LL) >> 29) + 1); //PTS + strBuf += (char)((PTS & 0x03FC00000LL) >> 22); //PTS (Cont) + strBuf += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //PTS (Cont) + strBuf += (char)((PTS & 0x000007F80LL) >> 7); //PTS (Cont) + strBuf += (char)(((PTS & 0x00000007FLL) << 1) + 1); //PTS (Cont) } /// Fills the free bytes of the TS::Packet. From 3c2f3728844242df14f5c65b488dc7abf2a059d6 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 8 Jan 2013 14:43:54 +0100 Subject: [PATCH 341/788] Fixed FPS conversion from FLV files. --- lib/flv_tag.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index e8204076..5ce1006d 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -996,7 +996,7 @@ JSON::Value FLV::Tag::toJSON(JSON::Value & metadata){ metadata["video"]["height"] = (long long int)tmp->getContentP("height")->NumValue(); } if (tmp->getContentP("framerate")){ - metadata["video"]["fpks"] = (long long int)tmp->getContentP("framerate")->NumValue() * 1000; + metadata["video"]["fpks"] = (long long int)(tmp->getContentP("framerate")->NumValue() * 1000.0); } if (tmp->getContentP("videodatarate")){ metadata["video"]["bps"] = (long long int)(tmp->getContentP("videodatarate")->NumValue() * 1024) / 8; From 942c7d69ba01a8880585cc95932eaaa6b6231847 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 8 Jan 2013 16:19:38 +0100 Subject: [PATCH 342/788] Replaced useless data with infinitely more useful data. --- lib/rtmpchunks.cpp | 7 +++++-- lib/ts_packet.cpp | 8 ++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/rtmpchunks.cpp b/lib/rtmpchunks.cpp index b150ab97..4f0a203a 100644 --- a/lib/rtmpchunks.cpp +++ b/lib/rtmpchunks.cpp @@ -5,7 +5,10 @@ #include "flv_tag.h" #include "timing.h" -char versionstring[] = "WWW.DDVTECH.COM "; ///< String that is repeated in the RTMP handshake +#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. @@ -811,7 +814,7 @@ bool RTMPStream::doHandshake(){ *((uint32_t*)Server) = 0; //time zero *(((uint32_t*)(Server + 4))) = htonl(0x01020304); //version 1 2 3 4 for (int i = 8; i < 3072; ++i){ - Server[i] = versionstring[i % 16]; + Server[i] = FILLER_DATA[i % sizeof(FILLER_DATA)]; } //"random" data bool encrypted = (Version == 6); diff --git a/lib/ts_packet.cpp b/lib/ts_packet.cpp index 67533043..07792124 100644 --- a/lib/ts_packet.cpp +++ b/lib/ts_packet.cpp @@ -3,6 +3,10 @@ #include "ts_packet.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 + /// This constructor creates an empty TS::Packet, ready for use for either reading or writing. /// All this constructor does is call TS::Packet::Clear(). TS::Packet::Packet(){ @@ -305,7 +309,7 @@ void TS::Packet::AddStuffing(int NumBytes){ strBuf[4] = Offset + NumBytes - 1; strBuf.resize(5 + Offset + NumBytes - 2); for (int i = 0; i < (NumBytes - 2); i++){ - strBuf[5 + Offset + i] = 0xFF; + strBuf[5 + Offset + i] = FILLER_DATA[i % sizeof(FILLER_DATA)]; } }else{ AdaptationField(3); @@ -313,7 +317,7 @@ void TS::Packet::AddStuffing(int NumBytes){ strBuf[4] = (char)(NumBytes - 1); strBuf[5] = (char)0x00; for (int i = 0; i < (NumBytes - 2); i++){ - strBuf += (char)0xFF; + strBuf += FILLER_DATA[i % sizeof(FILLER_DATA)]; } } } From cddf0192f6a8fb985a700eb335b696444472c2eb Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Tue, 8 Jan 2013 14:27:13 +0100 Subject: [PATCH 343/788] Working HLS video (not audio) for bipbop video --- lib/mp4.cpp | 1 + lib/mp4.h | 1 + lib/ts_packet.cpp | 20 ++++++++++++++++---- lib/ts_packet.h | 1 + 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index e2681cac..94575003 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -2055,6 +2055,7 @@ namespace MP4 { void AVCC::setPayload(std::string newPayload){ if ( !reserve(0, payloadSize(), newPayload.size())){ + std::cerr << "Cannot allocate enough memory for payload" << std::endl; return; } memcpy((char*)payload(), (char*)newPayload.c_str(), newPayload.size()); diff --git a/lib/mp4.h b/lib/mp4.h index 032462ef..7f631dae 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -1,6 +1,7 @@ #pragma once #include #include +#include #include #include #include diff --git a/lib/ts_packet.cpp b/lib/ts_packet.cpp index 07792124..74b4e9b4 100644 --- a/lib/ts_packet.cpp +++ b/lib/ts_packet.cpp @@ -48,6 +48,9 @@ int TS::Packet::PID(){ /// Sets the Continuity Counter of a single TS::Packet. /// \param NewContinuity The new Continuity Counter of the packet. void TS::Packet::ContinuityCounter(int NewContinuity){ + if (strBuf.size() < 4){ + strBuf.resize(4); + } strBuf[3] = (strBuf[3] & 0xF0) + (NewContinuity & 0x0F); } @@ -211,16 +214,16 @@ void TS::Packet::RandomAccess(int NewVal){ void TS::Packet::DefaultPAT(){ static int MyCntr = 0; strBuf = std::string(TS::PAT, 188); - ContinuityCounter(MyCntr); - MyCntr = ((MyCntr + 1) % 0x10); + ContinuityCounter(MyCntr++); + MyCntr %= 0x10; } /// Transforms the TS::Packet into a standard Program Mapping Table void TS::Packet::DefaultPMT(){ static int MyCntr = 0; strBuf = std::string(TS::PMT, 188); - ContinuityCounter(MyCntr); - MyCntr = ((MyCntr + 1) % 0x10); + ContinuityCounter(MyCntr++); + MyCntr %= 0x10; } /// Generates a string from the contents of the TS::Packet @@ -298,6 +301,15 @@ void TS::Packet::FillFree(std::string & NewVal){ NewVal.erase(0, toWrite); } +/// Fills the free bytes of the TS::Packet. +/// Stores as many bytes from NewVal as possible in the packet. +/// \param NewVal The data to store in the packet. +int TS::Packet::FillFree(const char* NewVal, int maxLen){ + int toWrite = std::min((int)(188 - strBuf.size()), maxLen); + strBuf += std::string(NewVal, toWrite); + return toWrite; +} + /// Adds NumBytes of stuffing to the TS::Packet. /// \param NumBytes the amount of stuffing bytes. void TS::Packet::AddStuffing(int NumBytes){ diff --git a/lib/ts_packet.h b/lib/ts_packet.h index 5525958b..7ea77feb 100644 --- a/lib/ts_packet.h +++ b/lib/ts_packet.h @@ -43,6 +43,7 @@ namespace TS { void PESVideoLeadIn(int NewLen, long long unsigned int PTS = 1); void PESAudioLeadIn(int NewLen, uint64_t PTS = 0); void FillFree(std::string & PackageData); + int FillFree(const char* PackageData, int maxLen); void AddStuffing(int NumBytes); private: //int Free; From eb6c4ecbcb48451ba26bf4816f44d1096a4b68b8 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 14 Jan 2013 14:52:49 +0100 Subject: [PATCH 344/788] Fixed possible segmentation faults in StopAll() --- lib/procs.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/procs.cpp b/lib/procs.cpp index 0871276d..ab1bbbf1 100644 --- a/lib/procs.cpp +++ b/lib/procs.cpp @@ -468,12 +468,12 @@ pid_t Util::Procs::StartPiped(std::string name, char * argv[], int * fdin, int * return 0; }else{ //parent #if DEBUG >= 1 - std::cerr << "Piped process " << name << " started"; - if (fdin ) std::cerr << " in=" << (*fdin == -1 ? pipein [1] : *fdin ); - if (fdout) std::cerr << " out=" << (*fdout == -1 ? pipeout[0] : *fdout); - if (fderr) std::cerr << " err=" << (*fderr == -1 ? pipeerr[0] : *fderr); - if (devnull != -1) std::cerr << " null=" << devnull; - std::cerr << ", PID " << pid << ": " << argv[0] << std::endl; + std::cerr << "Piped process " << name << " started"; + if (fdin ) std::cerr << " in=" << (*fdin == -1 ? pipein [1] : *fdin ); + if (fdout) std::cerr << " out=" << (*fdout == -1 ? pipeout[0] : *fdout); + if (fderr) std::cerr << " err=" << (*fderr == -1 ? pipeerr[0] : *fderr); + if (devnull != -1) std::cerr << " null=" << devnull; + std::cerr << ", PID " << pid << ": " << argv[0] << std::endl; #endif if (devnull != -1){ close(devnull); @@ -518,8 +518,9 @@ void Util::Procs::Stop(pid_t name){ /// (Attempts to) stop all running child processes. void Util::Procs::StopAll(){ + std::map listcopy = plist; std::map::iterator it; - for (it = plist.begin(); it != plist.end(); it++){ + for (it = listcopy.begin(); it != listcopy.end(); it++){ Stop(( *it).first); } } From 828d1688690b53b3867d56a9dabd94a97c50ca12 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 27 Jan 2013 16:54:33 +0100 Subject: [PATCH 345/788] Updated library version to 4.0.1 --- lib/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Makefile.am b/lib/Makefile.am index 4de85fa8..4daf7ce7 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -3,7 +3,7 @@ AM_CPPFLAGS = $(global_CFLAGS) lib_LTLIBRARIES=libmist-1.0.la libmist_1_0_la_SOURCES=amf.h amf.cpp auth.h auth.cpp base64.h base64.cpp config.h config.cpp dtsc.h dtsc.cpp flv_tag.h flv_tag.cpp http_parser.h http_parser.cpp json.h json.cpp procs.h procs.cpp rtmpchunks.h rtmpchunks.cpp socket.h socket.cpp mp4.h mp4.cpp ftp.h ftp.cpp filesystem.h filesystem.cpp stream.h stream.cpp timing.h timing.cpp ts_packet.cpp ts_packet.h libmist_1_0_la_LIBADD=-lssl -lcrypto -lrt -libmist_1_0_la_LDFLAGS = -version-info 3:0:0 +libmist_1_0_la_LDFLAGS = -version-info 4:0:1 pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = mist-1.0.pc From 4f4c1784b89b701d73de0d9ae971c30785de63eb Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Tue, 5 Feb 2013 16:07:48 +0100 Subject: [PATCH 346/788] Buffer overflow fixes --- lib/ts_packet.cpp | 20 +++++++++++++------- lib/ts_packet.h | 8 ++++---- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/lib/ts_packet.cpp b/lib/ts_packet.cpp index 74b4e9b4..83289d95 100644 --- a/lib/ts_packet.cpp +++ b/lib/ts_packet.cpp @@ -238,23 +238,29 @@ const char* TS::Packet::ToString(){ /// Generates a PES Lead-in for a video frame. /// Starts at the first Free byte. /// \param NewLen The length of this video frame. -void TS::Packet::PESVideoLeadIn(int NewLen, long long unsigned int PTS){ - NewLen += (PTS == 1 ? 9 : 14); +void TS::Packet::PESVideoLeadIn(unsigned int NewLen, long long unsigned int PTS){ + //NewLen += (PTS == 1 ? 9 : 19); + NewLen = 0; strBuf += (char)0x00; //PacketStartCodePrefix strBuf += (char)0x00; //PacketStartCodePrefix (Cont) strBuf += (char)0x01; //PacketStartCodePrefix (Cont) strBuf += (char)0xe0; //StreamType Video strBuf += (char)((NewLen & 0xFF00) >> 8); //PES PacketLength strBuf += (char)(NewLen & 0x00FF); //PES PacketLength (Cont) - strBuf += (char)0x80; //Reserved + Flags + strBuf += (char)0x84; //Reserved + Flags if (PTS != 1){ - strBuf += (char)0x80; //PTSOnlyFlag + Flags - strBuf += (char)0x05; //PESHeaderDataLength - strBuf += (char)(0x20 + ((PTS & 0x1C0000000LL) >> 29) + 1); //PTS + strBuf += (char)0xC0; //PTSOnlyFlag + Flags + strBuf += (char)0x0A; //PESHeaderDataLength + strBuf += (char)(0x30 + ((PTS & 0x1C0000000LL) >> 29) + 1); //Fixed + PTS strBuf += (char)((PTS & 0x03FC00000LL) >> 22); //PTS (Cont) strBuf += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //PTS (Cont) strBuf += (char)((PTS & 0x000007F80LL) >> 7); //PTS (Cont) strBuf += (char)(((PTS & 0x00000007FLL) << 1) + 1); //PTS (Cont) + strBuf += (char)(0x10 + ((PTS & 0x1C0000000LL) >> 29) + 1); //Fixed + DTS + strBuf += (char)((PTS & 0x03FC00000LL) >> 22); //DTS (Cont) + strBuf += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //DTS (Cont) + strBuf += (char)((PTS & 0x000007F80LL) >> 7); //DTS (Cont) + strBuf += (char)(((PTS & 0x00000007FLL) << 1) + 1); //DTS (Cont) }else{ strBuf += (char)0x00; //PTSOnlyFlag + Flags strBuf += (char)0x00; //PESHeaderDataLength @@ -273,7 +279,7 @@ void TS::Packet::PESVideoLeadIn(int NewLen, long long unsigned int PTS){ /// Starts at the first Free byte. /// \param NewLen The length of this audio frame. /// \param PTS The timestamp of the audio frame. -void TS::Packet::PESAudioLeadIn(int NewLen, uint64_t PTS){ +void TS::Packet::PESAudioLeadIn(unsigned int NewLen, uint64_t PTS){ NewLen += 8; strBuf += (char)0x00; //PacketStartCodePrefix strBuf += (char)0x00; //PacketStartCodePrefix (Cont) diff --git a/lib/ts_packet.h b/lib/ts_packet.h index 7ea77feb..82fb8567 100644 --- a/lib/ts_packet.h +++ b/lib/ts_packet.h @@ -4,7 +4,7 @@ #pragma once #include #include -#include //for uint64_t +#include //for uint64_t #include #include #include @@ -40,8 +40,8 @@ namespace TS { void Print(); const char* ToString(); - void PESVideoLeadIn(int NewLen, long long unsigned int PTS = 1); - void PESAudioLeadIn(int NewLen, uint64_t PTS = 0); + void PESVideoLeadIn(unsigned int NewLen, long long unsigned int PTS = 1); + void PESAudioLeadIn(unsigned int NewLen, uint64_t PTS = 0); void FillFree(std::string & PackageData); int FillFree(const char* PackageData, int maxLen); void AddStuffing(int NumBytes); @@ -62,7 +62,7 @@ namespace TS { 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] = ((initData[1] & 0x18) << 3); //AAC Channel Config (cont.) StandardHeader[3] |= ((FrameLen & 0x00001800) >> 11); StandardHeader[4] = ((FrameLen & 0x000007F8) >> 3); StandardHeader[5] |= ((FrameLen & 0x00000007) << 5); From af4caded70a8e2bc9f355eeb98a848b8e200c95e Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 11 Feb 2013 12:43:02 +0100 Subject: [PATCH 347/788] Fixed handling of stuffing bytes in TS packets. --- lib/ts_packet.cpp | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/lib/ts_packet.cpp b/lib/ts_packet.cpp index 83289d95..24b95d7a 100644 --- a/lib/ts_packet.cpp +++ b/lib/ts_packet.cpp @@ -323,19 +323,23 @@ void TS::Packet::AddStuffing(int NumBytes){ return; } if (AdaptationField() == 3){ - int Offset = strBuf[4]; - strBuf[4] = Offset + NumBytes - 1; - strBuf.resize(5 + Offset + NumBytes - 2); - for (int i = 0; i < (NumBytes - 2); i++){ - strBuf[5 + Offset + i] = FILLER_DATA[i % sizeof(FILLER_DATA)]; + strBuf.resize(5 + strBuf[4]); + strBuf[4] += NumBytes; + for (int i = 0; i < NumBytes; i++){ + strBuf.append(FILLER_DATA + (i % sizeof(FILLER_DATA)), 1); } }else{ AdaptationField(3); - strBuf.resize(6); - strBuf[4] = (char)(NumBytes - 1); - strBuf[5] = (char)0x00; - for (int i = 0; i < (NumBytes - 2); i++){ - strBuf += FILLER_DATA[i % sizeof(FILLER_DATA)]; + if (NumBytes > 1){ + strBuf.resize(6); + strBuf[4] = (char)(NumBytes - 1); + strBuf[5] = (char)0x00; + for (int i = 0; i < (NumBytes - 2); i++){ + strBuf += FILLER_DATA[i % sizeof(FILLER_DATA)]; + } + }else{ + strBuf.resize(5); + strBuf[4] = (char)(NumBytes - 1); } } } From 475fd0f1381dd9967ede51dca0953a7753770486 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 12 Feb 2013 15:24:27 +0100 Subject: [PATCH 348/788] TS optimalisations --- lib/ts_packet.cpp | 82 ++++++++++++++++++++++++++++++++++------------- lib/ts_packet.h | 3 ++ 2 files changed, 62 insertions(+), 23 deletions(-) diff --git a/lib/ts_packet.cpp b/lib/ts_packet.cpp index 24b95d7a..82fbd941 100644 --- a/lib/ts_packet.cpp +++ b/lib/ts_packet.cpp @@ -237,9 +237,10 @@ const char* TS::Packet::ToString(){ /// Generates a PES Lead-in for a video frame. /// Starts at the first Free byte. -/// \param NewLen The length of this video frame. +/// \param NewLen The length of this frame. +/// \param PTS The timestamp of the frame. void TS::Packet::PESVideoLeadIn(unsigned int NewLen, long long unsigned int PTS){ - //NewLen += (PTS == 1 ? 9 : 19); + //NewLen += 19; NewLen = 0; strBuf += (char)0x00; //PacketStartCodePrefix strBuf += (char)0x00; //PacketStartCodePrefix (Cont) @@ -248,25 +249,19 @@ void TS::Packet::PESVideoLeadIn(unsigned int NewLen, long long unsigned int PTS) strBuf += (char)((NewLen & 0xFF00) >> 8); //PES PacketLength strBuf += (char)(NewLen & 0x00FF); //PES PacketLength (Cont) strBuf += (char)0x84; //Reserved + Flags - if (PTS != 1){ - strBuf += (char)0xC0; //PTSOnlyFlag + Flags - strBuf += (char)0x0A; //PESHeaderDataLength - strBuf += (char)(0x30 + ((PTS & 0x1C0000000LL) >> 29) + 1); //Fixed + PTS - strBuf += (char)((PTS & 0x03FC00000LL) >> 22); //PTS (Cont) - strBuf += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //PTS (Cont) - strBuf += (char)((PTS & 0x000007F80LL) >> 7); //PTS (Cont) - strBuf += (char)(((PTS & 0x00000007FLL) << 1) + 1); //PTS (Cont) - strBuf += (char)(0x10 + ((PTS & 0x1C0000000LL) >> 29) + 1); //Fixed + DTS - strBuf += (char)((PTS & 0x03FC00000LL) >> 22); //DTS (Cont) - strBuf += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //DTS (Cont) - strBuf += (char)((PTS & 0x000007F80LL) >> 7); //DTS (Cont) - strBuf += (char)(((PTS & 0x00000007FLL) << 1) + 1); //DTS (Cont) - }else{ - strBuf += (char)0x00; //PTSOnlyFlag + Flags - strBuf += (char)0x00; //PESHeaderDataLength - } + strBuf += (char)0xC0; //PTSOnlyFlag + Flags + strBuf += (char)0x0A; //PESHeaderDataLength + strBuf += (char)(0x30 + ((PTS & 0x1C0000000LL) >> 29) + 1); //Fixed + PTS + strBuf += (char)((PTS & 0x03FC00000LL) >> 22); //PTS (Cont) + strBuf += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //PTS (Cont) + strBuf += (char)((PTS & 0x000007F80LL) >> 7); //PTS (Cont) + strBuf += (char)(((PTS & 0x00000007FLL) << 1) + 1); //PTS (Cont) + strBuf += (char)(0x10 + ((PTS & 0x1C0000000LL) >> 29) + 1); //Fixed + DTS + strBuf += (char)((PTS & 0x03FC00000LL) >> 22); //DTS (Cont) + strBuf += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //DTS (Cont) + strBuf += (char)((PTS & 0x000007F80LL) >> 7); //DTS (Cont) + strBuf += (char)(((PTS & 0x00000007FLL) << 1) + 1); //DTS (Cont) //PesPacket-Wise Prepended Data - strBuf += (char)0x00; //NALU StartCode strBuf += (char)0x00; //NALU StartCode (Cont) strBuf += (char)0x00; //NALU StartCode (Cont) @@ -277,15 +272,14 @@ void TS::Packet::PESVideoLeadIn(unsigned int NewLen, long long unsigned int PTS) /// Generates a PES Lead-in for an audio frame. /// Starts at the first Free byte. -/// \param NewLen The length of this audio frame. -/// \param PTS The timestamp of the audio frame. +/// \param NewLen The length of this frame. +/// \param PTS The timestamp of the frame. void TS::Packet::PESAudioLeadIn(unsigned int NewLen, uint64_t PTS){ NewLen += 8; strBuf += (char)0x00; //PacketStartCodePrefix strBuf += (char)0x00; //PacketStartCodePrefix (Cont) strBuf += (char)0x01; //PacketStartCodePrefix (Cont) strBuf += (char)0xc0; //StreamType Audio - strBuf += (char)((NewLen & 0xFF00) >> 8); //PES PacketLength strBuf += (char)(NewLen & 0x00FF); //PES PacketLength (Cont) strBuf += (char)0x80; //Reserved + Flags @@ -298,6 +292,48 @@ void TS::Packet::PESAudioLeadIn(unsigned int NewLen, uint64_t PTS){ strBuf += (char)(((PTS & 0x00000007FLL) << 1) + 1); //PTS (Cont) } +/// Generates a PES Lead-in for a video frame. +/// Prepends the lead-in to variable toSend, assumes toSend's length is all other data. +/// \param toSend Data that is to be send, will be modified. +/// \param PTS The timestamp of the frame. +void TS::Packet::PESVideoLeadIn(std::string & toSend, long long unsigned int PTS){ + std::string tmpStr; + tmpStr.reserve(25); + tmpStr.append("\000\000\001\340\000\000\204\300\012", 9); + tmpStr += (char)(0x30 + ((PTS & 0x1C0000000LL) >> 29) + 1); //Fixed + PTS + tmpStr += (char)((PTS & 0x03FC00000LL) >> 22); //PTS (Cont) + tmpStr += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //PTS (Cont) + tmpStr += (char)((PTS & 0x000007F80LL) >> 7); //PTS (Cont) + tmpStr += (char)(((PTS & 0x00000007FLL) << 1) + 1); //PTS (Cont) + tmpStr += (char)(0x10 + ((PTS & 0x1C0000000LL) >> 29) + 1); //Fixed + DTS + tmpStr += (char)((PTS & 0x03FC00000LL) >> 22); //DTS (Cont) + tmpStr += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //DTS (Cont) + tmpStr += (char)((PTS & 0x000007F80LL) >> 7); //DTS (Cont) + tmpStr += (char)(((PTS & 0x00000007FLL) << 1) + 1); //DTS (Cont) + tmpStr.append("\000\000\000\001\011\360", 6); + toSend.insert(0, tmpStr); +} + +/// Generates a PES Lead-in for an audio frame. +/// Prepends the lead-in to variable toSend, assumes toSend's length is all other data. +/// \param toSend Data that is to be send, will be modified. +/// \param PTS The timestamp of the frame. +void TS::Packet::PESAudioLeadIn(std::string & toSend, long long unsigned int PTS){ + std::string tmpStr; + tmpStr.reserve(14); + unsigned int NewLen = toSend.size() + 8; + tmpStr.append("\000\000\001\300", 4); + tmpStr += (char)((NewLen & 0xFF00) >> 8); //PES PacketLength + tmpStr += (char)(NewLen & 0x00FF); //PES PacketLength (Cont) + tmpStr.append("\200\200\005", 3); + tmpStr += (char)(0x20 + ((PTS & 0x1C0000000LL) >> 29) + 1); //PTS + tmpStr += (char)((PTS & 0x03FC00000LL) >> 22); //PTS (Cont) + tmpStr += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //PTS (Cont) + tmpStr += (char)((PTS & 0x000007F80LL) >> 7); //PTS (Cont) + tmpStr += (char)(((PTS & 0x00000007FLL) << 1) + 1); //PTS (Cont) + toSend.insert(0, tmpStr); +} + /// Fills the free bytes of the TS::Packet. /// Stores as many bytes from NewVal as possible in the packet. /// \param NewVal The data to store in the packet. diff --git a/lib/ts_packet.h b/lib/ts_packet.h index 82fb8567..1a54afb4 100644 --- a/lib/ts_packet.h +++ b/lib/ts_packet.h @@ -42,6 +42,9 @@ namespace TS { const char* ToString(); void PESVideoLeadIn(unsigned int NewLen, long long unsigned int PTS = 1); void PESAudioLeadIn(unsigned int NewLen, uint64_t PTS = 0); + static void PESAudioLeadIn(std::string & toSend, long long unsigned int PTS); + static void PESVideoLeadIn(std::string & toSend, long long unsigned int PTS); + void FillFree(std::string & PackageData); int FillFree(const char* PackageData, int maxLen); void AddStuffing(int NumBytes); From 603376d15a70b50e4373875136375e6cf90233fa Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 12 Feb 2013 15:24:52 +0100 Subject: [PATCH 349/788] Increased threshold levels for buffer warnings and capping. --- lib/socket.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/socket.cpp b/lib/socket.cpp index bad2a703..5b13f356 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -57,7 +57,7 @@ void Socket::Buffer::append(const char * newdata, const unsigned int newdatasize break; } } - if (data.size() > 1000){ + if (data.size() > 5000){ std::cerr << "Warning: After " << newdatasize << " new bytes, buffer has " << data.size() << " parts!" << std::endl; } } @@ -359,7 +359,7 @@ bool Socket::Connection::spool(){ iwrite(upbuffer.get()); } /// \todo Provide better mechanism to prevent overbuffering. - if (downbuffer.size() > 1000){ + if (downbuffer.size() > 10000){ return true; }else{ return iread(downbuffer); From 1baff448f23b1afb39a854bf1397de659b44f199 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 19 Feb 2013 16:06:39 +0100 Subject: [PATCH 350/788] Assorted bugfixes to DTSC and Socket libraries. --- lib/dtsc.cpp | 9 +++++++-- lib/socket.cpp | 45 +++++++++++++++++++++++++++++---------------- lib/socket.h | 1 + 3 files changed, 37 insertions(+), 18 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 1742e493..5814ede3 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -48,7 +48,9 @@ bool DTSC::Stream::parsePacket(std::string & buffer){ unsigned int i = 0; metadata = JSON::fromDTMI((unsigned char*)buffer.c_str() + 8, len, i); buffer.erase(0, len + 8); - return false; + if (buffer.length() <= 8){ + return false; + } } if (memcmp(buffer.c_str(), DTSC::Magic_Packet, 4) == 0){ len = ntohl(((uint32_t *)buffer.c_str())[1]); @@ -120,7 +122,10 @@ bool DTSC::Stream::parsePacket(Socket::Buffer & buffer){ unsigned int i = 0; std::string wholepacket = buffer.remove(len + 8); metadata = JSON::fromDTMI((unsigned char*)wholepacket.c_str() + 8, len, i); - return false; + if (!buffer.available(8)){ + return false; + } + header_bytes = buffer.copy(8); } if (memcmp(header_bytes.c_str(), DTSC::Magic_Packet, 4) == 0){ len = ntohl(((uint32_t *)header_bytes.c_str())[1]); diff --git a/lib/socket.cpp b/lib/socket.cpp index 5b13f356..a9a7eec3 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -105,6 +105,7 @@ std::string Socket::Buffer::copy(unsigned int count){ } unsigned int i = 0; std::string ret; + ret.reserve(count); for (std::deque::reverse_iterator it = data.rbegin(); it != data.rend(); ++it){ if (i + ( *it).size() < count){ ret.append( *it); @@ -226,10 +227,9 @@ int Socket::Connection::getSocket(){ } /// Returns a string describing the last error that occured. -/// Simply calls strerror(errno) - not very reliable! -/// \todo Improve getError at some point to be more reliable and only report socket errors. +/// Only reports errors if an error actually occured - returns the host address or empty string otherwise. std::string Socket::Connection::getError(){ - return strerror(errno); + return remotehost; } /// Create a new Unix Socket. This socket will (try to) connect to the given address right away. @@ -240,8 +240,9 @@ Socket::Connection::Connection(std::string address, bool nonblock){ pipes[1] = -1; sock = socket(PF_UNIX, SOCK_STREAM, 0); if (sock < 0){ + remotehost = strerror(errno); #if DEBUG >= 1 - fprintf(stderr, "Could not create socket! Error: %s\n", strerror(errno)); + fprintf(stderr, "Could not create socket! Error: %s\n", remotehost.c_str()); #endif return; } @@ -261,8 +262,9 @@ Socket::Connection::Connection(std::string address, bool nonblock){ fcntl(sock, F_SETFL, flags); } }else{ + remotehost = strerror(errno); #if DEBUG >= 1 - fprintf(stderr, "Could not connect to %s! Error: %s\n", address.c_str(), strerror(errno)); + fprintf(stderr, "Could not connect to %s! Error: %s\n", address.c_str(), remotehost.c_str()); #endif close(); } @@ -301,6 +303,7 @@ Socket::Connection::Connection(std::string host, int port, bool nonblock){ return; } + remotehost = ""; for (rp = result; rp != NULL; rp = rp->ai_next){ sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (sock < 0){ @@ -309,13 +312,14 @@ Socket::Connection::Connection(std::string host, int port, bool nonblock){ if (connect(sock, rp->ai_addr, rp->ai_addrlen) == 0){ break; } + remotehost += strerror(errno); ::close(sock); } freeaddrinfo(result); if (rp == 0){ #if DEBUG >= 1 - fprintf(stderr, "Could not connect to %s! Error: %s\n", host.c_str(), strerror(errno)); + fprintf(stderr, "Could not connect to %s! Error: %s\n", host.c_str(), remotehost.c_str()); #endif close(); }else{ @@ -614,7 +618,7 @@ Socket::Server::Server(){ /// \param nonblock (optional) Whether accept() calls will be nonblocking. Default is false (blocking). Socket::Server::Server(int port, std::string hostname, bool nonblock){ if ( !IPv6bind(port, hostname, nonblock) && !IPv4bind(port, hostname, nonblock)){ - fprintf(stderr, "Could not create socket %s:%i! Error: %s\n", hostname.c_str(), port, strerror(errno)); + fprintf(stderr, "Could not create socket %s:%i! Error: %s\n", hostname.c_str(), port, errors.c_str()); sock = -1; } } //Socket::Server TCP Constructor @@ -627,8 +631,9 @@ Socket::Server::Server(int port, std::string hostname, bool nonblock){ bool Socket::Server::IPv6bind(int port, std::string hostname, bool nonblock){ sock = socket(AF_INET6, SOCK_STREAM, 0); if (sock < 0){ + errors = strerror(errno); #if DEBUG >= 1 - fprintf(stderr, "Could not create IPv6 socket %s:%i! Error: %s\n", hostname.c_str(), port, strerror(errno)); + fprintf(stderr, "Could not create IPv6 socket %s:%i! Error: %s\n", hostname.c_str(), port, errors.c_str()); #endif return false; } @@ -656,15 +661,17 @@ bool Socket::Server::IPv6bind(int port, std::string hostname, bool nonblock){ #endif return true; }else{ + errors = strerror(errno); #if DEBUG >= 1 - fprintf(stderr, "IPv6 Listen failed! Error: %s\n", strerror(errno)); + fprintf(stderr, "IPv6 Listen failed! Error: %s\n", errors.c_str()); #endif close(); return false; } }else{ + errors = strerror(errno); #if DEBUG >= 1 - fprintf(stderr, "IPv6 Binding %s:%i failed (%s)\n", hostname.c_str(), port, strerror(errno)); + fprintf(stderr, "IPv6 Binding %s:%i failed (%s)\n", hostname.c_str(), port, errors.c_str()); #endif close(); return false; @@ -679,8 +686,9 @@ bool Socket::Server::IPv6bind(int port, std::string hostname, bool nonblock){ bool Socket::Server::IPv4bind(int port, std::string hostname, bool nonblock){ sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0){ + errors = strerror(errno); #if DEBUG >= 1 - fprintf(stderr, "Could not create IPv4 socket %s:%i! Error: %s\n", hostname.c_str(), port, strerror(errno)); + fprintf(stderr, "Could not create IPv4 socket %s:%i! Error: %s\n", hostname.c_str(), port, errors.c_str()); #endif return false; } @@ -708,15 +716,17 @@ bool Socket::Server::IPv4bind(int port, std::string hostname, bool nonblock){ #endif return true; }else{ + errors = strerror(errno); #if DEBUG >= 1 - fprintf(stderr, "IPv4 Listen failed! Error: %s\n", strerror(errno)); + fprintf(stderr, "IPv4 Listen failed! Error: %s\n", errors.c_str()); #endif close(); return false; } }else{ + errors = strerror(errno); #if DEBUG >= 1 - fprintf(stderr, "IPv4 binding %s:%i failed (%s)\n", hostname.c_str(), port, strerror(errno)); + fprintf(stderr, "IPv4 binding %s:%i failed (%s)\n", hostname.c_str(), port, errors.c_str()); #endif close(); return false; @@ -733,8 +743,9 @@ Socket::Server::Server(std::string address, bool nonblock){ unlink(address.c_str()); sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock < 0){ + errors = strerror(errno); #if DEBUG >= 1 - fprintf(stderr, "Could not create socket! Error: %s\n", strerror(errno)); + fprintf(stderr, "Could not create socket! Error: %s\n", errors.c_str()); #endif return; } @@ -752,15 +763,17 @@ Socket::Server::Server(std::string address, bool nonblock){ if (ret == 0){ return; }else{ + errors = strerror(errno); #if DEBUG >= 1 - fprintf(stderr, "Listen failed! Error: %s\n", strerror(errno)); + fprintf(stderr, "Listen failed! Error: %s\n", errors.c_str()); #endif close(); return; } }else{ + errors = strerror(errno); #if DEBUG >= 1 - fprintf(stderr, "Binding failed! Error: %s\n", strerror(errno)); + fprintf(stderr, "Binding failed! Error: %s\n", errors.c_str()); #endif close(); return; diff --git a/lib/socket.h b/lib/socket.h index d7f347ad..73db0b0b 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -97,6 +97,7 @@ namespace Socket { /// 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 From a69583fd49954b274e2bca2f4942f6e366f41349 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Tue, 19 Feb 2013 10:57:23 +0100 Subject: [PATCH 351/788] Edits to DTSC lib for live buffering --- lib/dtsc.cpp | 86 +++++++++++++++++++++++++++++++++++++++++----------- lib/dtsc.h | 8 ++++- 2 files changed, 76 insertions(+), 18 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 5814ede3..11762845 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -13,17 +13,19 @@ DTSC::Stream::Stream(){ datapointertype = DTSC::INVALID; datapointer = 0; buffercount = 1; + buffertime = 0; } /// Initializes a DTSC::Stream with a minimum of rbuffers packet buffers. /// The actual buffer count may not at all times be the requested amount. -DTSC::Stream::Stream(unsigned int rbuffers){ +DTSC::Stream::Stream(unsigned int rbuffers, unsigned int bufferTime){ datapointertype = DTSC::INVALID; datapointer = 0; if (rbuffers < 1){ rbuffers = 1; } buffercount = rbuffers; + buffertime = bufferTime; } /// Returns the time in milliseconds of the last received packet. @@ -47,6 +49,7 @@ bool DTSC::Stream::parsePacket(std::string & buffer){ } unsigned int i = 0; metadata = JSON::fromDTMI((unsigned char*)buffer.c_str() + 8, len, i); + metadata.removeMember( "moreheader" ); buffer.erase(0, len + 8); if (buffer.length() <= 8){ return false; @@ -122,6 +125,7 @@ bool DTSC::Stream::parsePacket(Socket::Buffer & buffer){ unsigned int i = 0; std::string wholepacket = buffer.remove(len + 8); metadata = JSON::fromDTMI((unsigned char*)wholepacket.c_str() + 8, len, i); + metadata.removeMember( "moreheader" ); if (!buffer.available(8)){ return false; } @@ -220,29 +224,44 @@ std::string & DTSC::Stream::outHeader(){ void DTSC::Stream::advanceRings(){ std::deque::iterator dit; std::set::iterator sit; - for (sit = rings.begin(); sit != rings.end(); sit++){ - ( *sit)->b++; - if (( *sit)->waiting){ - ( *sit)->waiting = false; - ( *sit)->b = 0; - } - if (( *sit)->starved || (( *sit)->b >= buffers.size())){ - ( *sit)->starved = true; - ( *sit)->b = 0; + if (rings.size()) { + for (sit = rings.begin(); sit != rings.end(); sit++){ + ( *sit)->b++; + if (( *sit)->waiting){ + ( *sit)->waiting = false; + ( *sit)->b = 0; + } + if (( *sit)->starved || (( *sit)->b >= buffers.size())){ + ( *sit)->starved = true; + ( *sit)->b = 0; + } } } - for (dit = keyframes.begin(); dit != keyframes.end(); dit++){ - dit->b++; - if (dit->b >= buffers.size()){ - keyframes.erase(dit); - break; + if (keyframes.size()){ + for (dit = keyframes.begin(); dit != keyframes.end(); dit++){ + dit->b++; } + bool repeat; + do { + repeat = false; + for (dit = keyframes.begin(); dit != keyframes.end(); dit++){ + if (dit->b >= buffers.size()){ + keyframes.erase(dit); + repeat = true; + break; + } + } + } while( repeat ); } if ((lastType() == VIDEO) && (buffers.front().isMember("keyframe"))){ keyframes.push_front(DTSC::Ring(0)); } - //increase buffer size if no keyframes available - if ((buffercount > 1) && (keyframes.size() < 1)){ + int timeBuffered = 0; + if (keyframes.size() > 1){ + //increase buffer size if no keyframes available or too little time available + timeBuffered = buffers[keyframes[keyframes.size() - 1].b]["time"].asInt() - buffers[keyframes[0].b]["time"].asInt(); + } + if (((buffercount > 1) && (keyframes.size() < 2)) || (timeBuffered < buffertime)){ buffercount++; } } @@ -278,6 +297,34 @@ void DTSC::Stream::dropRing(DTSC::Ring * ptr){ } } +void DTSC::Stream::updateHeaders(){ + if( keyframes.size() > 1 ) { + metadata["keytime"].shrink(keyframes.size() - 2); + metadata["keytime"].append(buffers[keyframes[1].b]["time"].asInt()); + metadata.toPacked(); + updateRingHeaders(); + } +} + +void DTSC::Stream::updateRingHeaders(){ + std::set::iterator sit; + if (!rings.size()){ + return; + } + for (sit = rings.begin(); sit != rings.end(); sit++){ + ( *sit)->updated = true; + } +} + +unsigned int DSTSC::Stream::msSeek(unsigned int ms) { + for( std::deque::iterator it = keyframes.begin(); it != keyframes.end(); it++ ) { + if( buffers[it->b]["time"].asInt( ) < ms ) { + return it->b; + } + } + ///\todo Send PauseMark +} + /// Properly cleans up the object for erasing. /// Drops all Ring classes that have been given out. DTSC::Stream::~Stream(){ @@ -438,6 +485,11 @@ void DTSC::File::seekNext(){ jsonbuffer.null(); return; } + if (memcmp(buffer, DTSC::Magic_Header, 4) == 0){ + readHeader(lastreadpos); + jsonbuffer = metadata; + return; + } if (memcmp(buffer, DTSC::Magic_Packet, 4) != 0){ fprintf(stderr, "Invalid header - %.4s != %.4s\n", buffer, DTSC::Magic_Packet); strbuffer = ""; diff --git a/lib/dtsc.h b/lib/dtsc.h index f3dd687d..2855809b 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -56,6 +56,7 @@ namespace DTSC { VIDEO, ///< Stream Video data META, ///< Stream Metadata PAUSEMARK, ///< Pause marker + MODIFIEDHEADER, ///< Modified header data. INVALID ///< Anything else or no data available. }; @@ -100,6 +101,7 @@ namespace DTSC { 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. }; /// Holds temporary data for a DTSC stream and provides functions to utilize it. @@ -109,7 +111,7 @@ namespace DTSC { public: Stream(); ~Stream(); - Stream(unsigned int buffers); + Stream(unsigned int buffers, unsigned int bufferTime = 0); JSON::Value metadata; JSON::Value & getPacket(unsigned int num = 0); datatype lastType(); @@ -123,13 +125,17 @@ namespace DTSC { Ring * getRing(); unsigned int getTime(); void dropRing(Ring * ptr); + void updateHeaders(); + unsigned int msSeek(unsigned int ms); private: std::deque buffers; std::set rings; std::deque keyframes; void advanceRings(); + void updateRingHeaders(); std::string * datapointer; datatype datapointertype; unsigned int buffercount; + unsigned int buffertime; }; } From 4dbca1e680dc8142ec1b81f25f7d5b5f4a353463 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Tue, 19 Feb 2013 22:04:38 +0100 Subject: [PATCH 352/788] Progressive now works with continuously updated metadata --- lib/dtsc.cpp | 12 +++++++++--- lib/dtsc.h | 2 ++ lib/json.cpp | 1 + 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 11762845..9999b7cf 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -206,6 +206,10 @@ bool DTSC::Stream::hasAudio(){ return metadata.isMember("audio"); } +void DTSC::Stream::setBufferTime(unsigned int ms){ + buffertime = ms; +} + /// Returns a packed DTSC packet, ready to sent over the network. std::string & DTSC::Stream::outPacket(unsigned int num){ static std::string emptystring; @@ -261,7 +265,7 @@ void DTSC::Stream::advanceRings(){ //increase buffer size if no keyframes available or too little time available timeBuffered = buffers[keyframes[keyframes.size() - 1].b]["time"].asInt() - buffers[keyframes[0].b]["time"].asInt(); } - if (((buffercount > 1) && (keyframes.size() < 2)) || (timeBuffered < buffertime)){ + if (buffercount > 1 && (keyframes.size() < 2 || timeBuffered < buffertime)){ buffercount++; } } @@ -272,6 +276,8 @@ DTSC::Ring::Ring(unsigned int v){ b = v; waiting = false; starved = false; + updated = false; + playCount = 0; } /// Requests a new Ring, which will be created and added to the internal Ring list. @@ -316,13 +322,13 @@ void DTSC::Stream::updateRingHeaders(){ } } -unsigned int DSTSC::Stream::msSeek(unsigned int ms) { +unsigned int DTSC::Stream::msSeek(unsigned int ms) { for( std::deque::iterator it = keyframes.begin(); it != keyframes.end(); it++ ) { if( buffers[it->b]["time"].asInt( ) < ms ) { return it->b; } } - ///\todo Send PauseMark + return keyframes[keyframes.size()-1].b; } /// Properly cleans up the object for erasing. diff --git a/lib/dtsc.h b/lib/dtsc.h index 2855809b..a7329f3e 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -102,6 +102,7 @@ namespace DTSC { 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; }; /// Holds temporary data for a DTSC stream and provides functions to utilize it. @@ -127,6 +128,7 @@ namespace DTSC { void dropRing(Ring * ptr); void updateHeaders(); unsigned int msSeek(unsigned int ms); + void setBufferTime(unsigned int ms); private: std::deque buffers; std::set rings; diff --git a/lib/json.cpp b/lib/json.cpp index 68596e90..9bc421d8 100644 --- a/lib/json.cpp +++ b/lib/json.cpp @@ -476,6 +476,7 @@ std::string & JSON::Value::toNetPacked(){ //check if this is legal if (myType != OBJECT){ fprintf(stderr, "Error: Only objects may be NetPacked!\n"); + abort(); return emptystring; } //if sneaky storage doesn't contain correct data, re-calculate it From 12db927cbf5f77d1763bdebacecb24cccc4f64cf Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Wed, 27 Feb 2013 09:47:50 +0100 Subject: [PATCH 353/788] Changes for propagating fragment numbers if the stream is live --- lib/dtsc.cpp | 28 ++++++++++++++++++++++++---- lib/dtsc.h | 1 + 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 9999b7cf..5be18539 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -257,13 +257,17 @@ void DTSC::Stream::advanceRings(){ } } while( repeat ); } + static int fragNum = 1; if ((lastType() == VIDEO) && (buffers.front().isMember("keyframe"))){ keyframes.push_front(DTSC::Ring(0)); + if ( !buffers.front().isMember("fragnum")){ + buffers.front()["fragnum"] = fragNum++; + } } - int timeBuffered = 0; + unsigned int timeBuffered = 0; if (keyframes.size() > 1){ - //increase buffer size if no keyframes available or too little time available - timeBuffered = buffers[keyframes[keyframes.size() - 1].b]["time"].asInt() - buffers[keyframes[0].b]["time"].asInt(); + //increase buffer size if no keyframes available or too little time available + timeBuffered = buffers[keyframes[0].b]["time"].asInt() - buffers[keyframes[keyframes.size() - 1].b]["time"].asInt(); } if (buffercount > 1 && (keyframes.size() < 2 || timeBuffered < buffertime)){ buffercount++; @@ -304,9 +308,16 @@ void DTSC::Stream::dropRing(DTSC::Ring * ptr){ } void DTSC::Stream::updateHeaders(){ - if( keyframes.size() > 1 ) { + if( keyframes.size() > 2 ) { metadata["keytime"].shrink(keyframes.size() - 2); + metadata["keynum"].shrink(keyframes.size() - 2 ); metadata["keytime"].append(buffers[keyframes[1].b]["time"].asInt()); + if( metadata["keynum"].size() == 0 ) { + metadata["keynum"].append(1ll); + } else { + metadata["keynum"].append( metadata["keynum"][metadata["keynum"].size()-1].asInt() + 1 ); + } + metadata["lastms"] = buffers[keyframes[0].b]["time"].asInt(); metadata.toPacked(); updateRingHeaders(); } @@ -331,6 +342,15 @@ unsigned int DTSC::Stream::msSeek(unsigned int ms) { return keyframes[keyframes.size()-1].b; } +unsigned int DTSC::Stream::frameSeek(unsigned int frameno) { + for( std::deque::iterator it = keyframes.begin(); it != keyframes.end(); it++ ) { + if( buffers[it->b]["fragnum"].asInt( ) == frameno ) { + return it->b; + } + } + return keyframes[keyframes.size()-1].b; +} + /// Properly cleans up the object for erasing. /// Drops all Ring classes that have been given out. DTSC::Stream::~Stream(){ diff --git a/lib/dtsc.h b/lib/dtsc.h index a7329f3e..de199ebb 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -128,6 +128,7 @@ namespace DTSC { void dropRing(Ring * ptr); void updateHeaders(); unsigned int msSeek(unsigned int ms); + unsigned int frameSeek(unsigned int frameno); void setBufferTime(unsigned int ms); private: std::deque buffers; From 23def8aa863f8adb01c21c45c206598cbad27399 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 1 Mar 2013 20:37:53 +0100 Subject: [PATCH 354/788] Fixed coding style on recent commits. --- lib/dtsc.cpp | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 5be18539..a1a9a393 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -49,7 +49,7 @@ bool DTSC::Stream::parsePacket(std::string & buffer){ } unsigned int i = 0; metadata = JSON::fromDTMI((unsigned char*)buffer.c_str() + 8, len, i); - metadata.removeMember( "moreheader" ); + metadata.removeMember("moreheader"); buffer.erase(0, len + 8); if (buffer.length() <= 8){ return false; @@ -125,8 +125,8 @@ bool DTSC::Stream::parsePacket(Socket::Buffer & buffer){ unsigned int i = 0; std::string wholepacket = buffer.remove(len + 8); metadata = JSON::fromDTMI((unsigned char*)wholepacket.c_str() + 8, len, i); - metadata.removeMember( "moreheader" ); - if (!buffer.available(8)){ + metadata.removeMember("moreheader"); + if ( !buffer.available(8)){ return false; } header_bytes = buffer.copy(8); @@ -228,7 +228,7 @@ std::string & DTSC::Stream::outHeader(){ void DTSC::Stream::advanceRings(){ std::deque::iterator dit; std::set::iterator sit; - if (rings.size()) { + if (rings.size()){ for (sit = rings.begin(); sit != rings.end(); sit++){ ( *sit)->b++; if (( *sit)->waiting){ @@ -246,7 +246,7 @@ void DTSC::Stream::advanceRings(){ dit->b++; } bool repeat; - do { + do{ repeat = false; for (dit = keyframes.begin(); dit != keyframes.end(); dit++){ if (dit->b >= buffers.size()){ @@ -255,7 +255,7 @@ void DTSC::Stream::advanceRings(){ break; } } - } while( repeat ); + }while (repeat); } static int fragNum = 1; if ((lastType() == VIDEO) && (buffers.front().isMember("keyframe"))){ @@ -308,16 +308,16 @@ void DTSC::Stream::dropRing(DTSC::Ring * ptr){ } void DTSC::Stream::updateHeaders(){ - if( keyframes.size() > 2 ) { + if (keyframes.size() > 2){ metadata["keytime"].shrink(keyframes.size() - 2); - metadata["keynum"].shrink(keyframes.size() - 2 ); + metadata["keynum"].shrink(keyframes.size() - 2); metadata["keytime"].append(buffers[keyframes[1].b]["time"].asInt()); - if( metadata["keynum"].size() == 0 ) { + if (metadata["keynum"].size() == 0){ metadata["keynum"].append(1ll); - } else { - metadata["keynum"].append( metadata["keynum"][metadata["keynum"].size()-1].asInt() + 1 ); + }else{ + metadata["keynum"].append(metadata["keynum"][metadata["keynum"].size() - 1].asInt() + 1); } - metadata["lastms"] = buffers[keyframes[0].b]["time"].asInt(); + metadata["lastms"] = buffers[keyframes[0].b]["time"].asInt(); metadata.toPacked(); updateRingHeaders(); } @@ -325,7 +325,7 @@ void DTSC::Stream::updateHeaders(){ void DTSC::Stream::updateRingHeaders(){ std::set::iterator sit; - if (!rings.size()){ + if ( !rings.size()){ return; } for (sit = rings.begin(); sit != rings.end(); sit++){ @@ -333,22 +333,22 @@ void DTSC::Stream::updateRingHeaders(){ } } -unsigned int DTSC::Stream::msSeek(unsigned int ms) { - for( std::deque::iterator it = keyframes.begin(); it != keyframes.end(); it++ ) { - if( buffers[it->b]["time"].asInt( ) < ms ) { +unsigned int DTSC::Stream::msSeek(unsigned int ms){ + for (std::deque::iterator it = keyframes.begin(); it != keyframes.end(); it++){ + if (buffers[it->b]["time"].asInt() < ms){ return it->b; } } - return keyframes[keyframes.size()-1].b; + return keyframes[keyframes.size() - 1].b; } -unsigned int DTSC::Stream::frameSeek(unsigned int frameno) { - for( std::deque::iterator it = keyframes.begin(); it != keyframes.end(); it++ ) { - if( buffers[it->b]["fragnum"].asInt( ) == frameno ) { +unsigned int DTSC::Stream::frameSeek(unsigned int frameno){ + for (std::deque::iterator it = keyframes.begin(); it != keyframes.end(); it++){ + if (buffers[it->b]["fragnum"].asInt() == frameno){ return it->b; } } - return keyframes[keyframes.size()-1].b; + return keyframes[keyframes.size() - 1].b; } /// Properly cleans up the object for erasing. From ddb681fb8c8d61f273018238da4b7bf3d72e7e0d Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 4 Mar 2013 22:02:10 +0100 Subject: [PATCH 355/788] MP4 lib signedness fixes. --- lib/mp4.cpp | 278 ++++++++++++++++++++++++++-------------------------- lib/mp4.h | 268 +++++++++++++++++++++++++------------------------- 2 files changed, 273 insertions(+), 273 deletions(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 94575003..0095868a 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -4,7 +4,7 @@ #include "mp4.h" #include "json.h" -#define Int64 long long int +#define Int64 uint64_t /// Contains all MP4 format related code. namespace MP4 { @@ -50,7 +50,7 @@ namespace MP4 { } if (newData.size() > 4){ payloadOffset = 8; - long long int size = ntohl(((int*)newData.c_str())[0]); + uint64_t size = ntohl(((int*)newData.c_str())[0]); if (size == 1){ if (newData.size() > 16){ size = 0 + ntohl(((int*)newData.c_str())[2]); @@ -77,16 +77,16 @@ namespace MP4 { } /// Returns the total boxed size of this box, including the header. - long long int Box::boxedSize(){ + uint64_t Box::boxedSize(){ if (payloadOffset == 16){ - return ((long long int)ntohl(((int*)data)[2]) << 32) + ntohl(((int*)data)[3]); + 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. - long long int Box::payloadSize(){ + uint64_t Box::payloadSize(){ return boxedSize() - payloadOffset; } @@ -217,7 +217,7 @@ namespace MP4 { /// 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(long newData, size_t index){ + void Box::setInt24(uint32_t newData, size_t index){ index += payloadOffset; if (index + 2 >= boxedSize()){ if ( !reserve(index, 0, 3)){ @@ -232,7 +232,7 @@ namespace MP4 { /// 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. - long Box::getInt24(size_t index){ + uint32_t Box::getInt24(size_t index){ index += payloadOffset; if (index + 2 >= boxedSize()){ if ( !reserve(index, 0, 3)){ @@ -240,7 +240,7 @@ namespace MP4 { } setInt24(0, index - payloadOffset); } - long result = data[index]; + uint32_t result = data[index]; result <<= 8; result += data[index + 1]; result <<= 8; @@ -251,7 +251,7 @@ namespace MP4 { /// 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(long newData, size_t index){ + void Box::setInt32(uint32_t newData, size_t index){ index += payloadOffset; if (index + 3 >= boxedSize()){ if ( !reserve(index, 0, 4)){ @@ -265,7 +265,7 @@ namespace MP4 { /// 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. - long Box::getInt32(size_t index){ + uint32_t Box::getInt32(size_t index){ index += payloadOffset; if (index + 3 >= boxedSize()){ if ( !reserve(index, 0, 4)){ @@ -273,7 +273,7 @@ namespace MP4 { } setInt32(0, index - payloadOffset); } - long result; + uint32_t result; memcpy((char*) &result, data + index, 4); return ntohl(result); } @@ -463,19 +463,19 @@ namespace MP4 { return getInt8(0); } - void ABST::setFlags(long newFlags){ + void ABST::setFlags(uint32_t newFlags){ setInt24(newFlags, 1); } - long ABST::getFlags(){ + uint32_t ABST::getFlags(){ return getInt24(1); } - void ABST::setBootstrapinfoVersion(long newVersion){ + void ABST::setBootstrapinfoVersion(uint32_t newVersion){ setInt32(newVersion, 4); } - long ABST::getBootstrapinfoVersion(){ + uint32_t ABST::getBootstrapinfoVersion(){ return getInt32(4); } @@ -507,11 +507,11 @@ namespace MP4 { return (getInt8(8) & 0x08); } - void ABST::setTimeScale(long newScale){ + void ABST::setTimeScale(uint32_t newScale){ setInt32(newScale, 9); } - long ABST::getTimeScale(){ + uint32_t ABST::getTimeScale(){ return getInt32(9); } @@ -539,12 +539,12 @@ namespace MP4 { return getString(29); } - long ABST::getServerEntryCount(){ + uint32_t ABST::getServerEntryCount(){ int countLoc = 29 + getStringLen(29) + 1; return getInt8(countLoc); } - void ABST::setServerEntry(std::string & newEntry, long no){ + 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 @@ -568,7 +568,7 @@ namespace MP4 { } ///\return Empty string if no > serverEntryCount(), serverEntry[no] otherwise. - const char* ABST::getServerEntry(long no){ + const char* ABST::getServerEntry(uint32_t no){ if (no + 1 > getServerEntryCount()){ return ""; } @@ -579,7 +579,7 @@ namespace MP4 { return getString(tempLoc); } - long ABST::getQualityEntryCount(){ + uint32_t ABST::getQualityEntryCount(){ int countLoc = 29 + getStringLen(29) + 1 + 1; for (int i = 0; i < getServerEntryCount(); i++){ countLoc += getStringLen(countLoc) + 1; @@ -587,7 +587,7 @@ namespace MP4 { return getInt8(countLoc); } - void ABST::setQualityEntry(std::string & newEntry, long no){ + void ABST::setQualityEntry(std::string & newEntry, uint32_t no){ int countLoc = 29 + getStringLen(29) + 1 + 1; for (int i = 0; i < getServerEntryCount(); i++){ countLoc += getStringLen(countLoc) + 1; @@ -613,7 +613,7 @@ namespace MP4 { setString(newEntry, tempLoc); } - const char* ABST::getQualityEntry(long no){ + const char* ABST::getQualityEntry(uint32_t no){ if (no > getQualityEntryCount()){ return ""; } @@ -629,7 +629,7 @@ namespace MP4 { } void ABST::setDrmData(std::string newDrm){ - long tempLoc = 29 + getStringLen(29) + 1 + 1; + uint32_t tempLoc = 29 + getStringLen(29) + 1 + 1; for (int i = 0; i < getServerEntryCount(); i++){ tempLoc += getStringLen(tempLoc) + 1; } @@ -641,7 +641,7 @@ namespace MP4 { } char* ABST::getDrmData(){ - long tempLoc = 29 + getStringLen(29) + 1 + 1; + uint32_t tempLoc = 29 + getStringLen(29) + 1 + 1; for (int i = 0; i < getServerEntryCount(); i++){ tempLoc += getStringLen(tempLoc) + 1; } @@ -653,7 +653,7 @@ namespace MP4 { } void ABST::setMetaData(std::string newMetaData){ - long tempLoc = 29 + getStringLen(29) + 1 + 1; + uint32_t tempLoc = 29 + getStringLen(29) + 1 + 1; for (int i = 0; i < getServerEntryCount(); i++){ tempLoc += getStringLen(tempLoc) + 1; } @@ -666,7 +666,7 @@ namespace MP4 { } char* ABST::getMetaData(){ - long tempLoc = 29 + getStringLen(29) + 1 + 1; + uint32_t tempLoc = 29 + getStringLen(29) + 1 + 1; for (int i = 0; i < getServerEntryCount(); i++){ tempLoc += getStringLen(tempLoc) + 1; } @@ -678,8 +678,8 @@ namespace MP4 { return getString(tempLoc); } - long ABST::getSegmentRunTableCount(){ - long tempLoc = 29 + getStringLen(29) + 1 + 1; + uint32_t ABST::getSegmentRunTableCount(){ + uint32_t tempLoc = 29 + getStringLen(29) + 1 + 1; for (int i = 0; i < getServerEntryCount(); i++){ tempLoc += getStringLen(tempLoc) + 1; } @@ -692,8 +692,8 @@ namespace MP4 { return getInt8(tempLoc); } - void ABST::setSegmentRunTable(ASRT & newSegment, long no){ - long tempLoc = 29 + getStringLen(29) + 1 + 1; + void ABST::setSegmentRunTable(ASRT & newSegment, uint32_t no){ + uint32_t tempLoc = 29 + getStringLen(29) + 1 + 1; for (int i = 0; i < getServerEntryCount(); i++){ tempLoc += getStringLen(tempLoc) + 1; } @@ -728,13 +728,13 @@ namespace MP4 { setBox(newSegment, tempLoc); } - ASRT & ABST::getSegmentRunTable(long no){ + ASRT & ABST::getSegmentRunTable(uint32_t no){ static Box result; if (no > getSegmentRunTableCount()){ static Box res; return (ASRT&)res; } - long tempLoc = 29 + getStringLen(29) + 1 + 1; + uint32_t tempLoc = 29 + getStringLen(29) + 1 + 1; for (int i = 0; i < getServerEntryCount(); i++){ tempLoc += getStringLen(tempLoc) + 1; } @@ -752,8 +752,8 @@ namespace MP4 { return (ASRT&)getBox(tempLoc); } - long ABST::getFragmentRunTableCount(){ - long tempLoc = 29 + getStringLen(29) + 1 + 1; + uint32_t ABST::getFragmentRunTableCount(){ + uint32_t tempLoc = 29 + getStringLen(29) + 1 + 1; for (int i = 0; i < getServerEntryCount(); i++){ tempLoc += getStringLen(tempLoc) + 1; } @@ -769,8 +769,8 @@ namespace MP4 { return getInt8(tempLoc); } - void ABST::setFragmentRunTable(AFRT & newFragment, long no){ - long tempLoc = 29 + getStringLen(29) + 1 + 1; + void ABST::setFragmentRunTable(AFRT & newFragment, uint32_t no){ + uint32_t tempLoc = 29 + getStringLen(29) + 1 + 1; for (int i = 0; i < getServerEntryCount(); i++){ tempLoc += getStringLen(tempLoc) + 1; } @@ -808,13 +808,13 @@ namespace MP4 { setBox(newFragment, tempLoc); } - AFRT & ABST::getFragmentRunTable(long no){ + AFRT & ABST::getFragmentRunTable(uint32_t no){ static Box result; if (no >= getFragmentRunTableCount()){ static Box res; return (AFRT&)res; } - long tempLoc = 29 + getStringLen(29) + 1 + 1; + uint32_t tempLoc = 29 + getStringLen(29) + 1 + 1; for (int i = 0; i < getServerEntryCount(); i++){ tempLoc += getStringLen(tempLoc) + 1; } @@ -835,7 +835,7 @@ namespace MP4 { return (AFRT&)getBox(tempLoc); } - std::string ABST::toPrettyString(long indent){ + 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; @@ -887,31 +887,31 @@ namespace MP4 { setInt8(newVersion, 0); } - long AFRT::getVersion(){ + uint32_t AFRT::getVersion(){ return getInt8(0); } - void AFRT::setUpdate(long newUpdate){ + void AFRT::setUpdate(uint32_t newUpdate){ setInt24(newUpdate, 1); } - long AFRT::getUpdate(){ + uint32_t AFRT::getUpdate(){ return getInt24(1); } - void AFRT::setTimeScale(long newScale){ + void AFRT::setTimeScale(uint32_t newScale){ setInt32(newScale, 4); } - long AFRT::getTimeScale(){ + uint32_t AFRT::getTimeScale(){ return getInt32(4); } - long AFRT::getQualityEntryCount(){ + uint32_t AFRT::getQualityEntryCount(){ return getInt8(8); } - void AFRT::setQualityEntry(std::string & newEntry, long no){ + void AFRT::setQualityEntry(std::string & newEntry, uint32_t no){ int countLoc = 8; int tempLoc = countLoc + 1; //attempt to reach the wanted position @@ -934,7 +934,7 @@ namespace MP4 { setString(newEntry, tempLoc); } - const char* AFRT::getQualityEntry(long no){ + const char* AFRT::getQualityEntry(uint32_t no){ if (no + 1 > getQualityEntryCount()){ return ""; } @@ -945,7 +945,7 @@ namespace MP4 { return getString(tempLoc); } - long AFRT::getFragmentRunCount(){ + uint32_t AFRT::getFragmentRunCount(){ int tempLoc = 9; for (int i = 0; i < getQualityEntryCount(); ++i){ tempLoc += getStringLen(tempLoc) + 1; @@ -953,7 +953,7 @@ namespace MP4 { return getInt32(tempLoc); } - void AFRT::setFragmentRun(afrt_runtable newRun, long no){ + void AFRT::setFragmentRun(afrt_runtable newRun, uint32_t no){ int tempLoc = 9; for (int i = 0; i < getQualityEntryCount(); ++i){ tempLoc += getStringLen(tempLoc) + 1; @@ -983,7 +983,7 @@ namespace MP4 { } } - afrt_runtable AFRT::getFragmentRun(long no){ + afrt_runtable AFRT::getFragmentRun(uint32_t no){ afrt_runtable res; if (no > getFragmentRunCount()){ return res; @@ -1050,23 +1050,23 @@ namespace MP4 { setInt8(newVersion, 0); } - long ASRT::getVersion(){ + uint32_t ASRT::getVersion(){ return getInt8(0); } - void ASRT::setUpdate(long newUpdate){ + void ASRT::setUpdate(uint32_t newUpdate){ setInt24(newUpdate, 1); } - long ASRT::getUpdate(){ + uint32_t ASRT::getUpdate(){ return getInt24(1); } - long ASRT::getQualityEntryCount(){ + uint32_t ASRT::getQualityEntryCount(){ return getInt8(4); } - void ASRT::setQualityEntry(std::string & newEntry, long no){ + void ASRT::setQualityEntry(std::string & newEntry, uint32_t no){ int countLoc = 4; int tempLoc = countLoc + 1; //attempt to reach the wanted position @@ -1089,7 +1089,7 @@ namespace MP4 { setString(newEntry, tempLoc); } - const char* ASRT::getQualityEntry(long no){ + const char* ASRT::getQualityEntry(uint32_t no){ if (no > getQualityEntryCount()){ return ""; } @@ -1100,7 +1100,7 @@ namespace MP4 { return getString(tempLoc); } - long ASRT::getSegmentRunEntryCount(){ + uint32_t ASRT::getSegmentRunEntryCount(){ int tempLoc = 5; //position of qualityentry count; for (int i = 0; i < getQualityEntryCount(); i++){ tempLoc += getStringLen(tempLoc) + 1; @@ -1108,7 +1108,7 @@ namespace MP4 { return getInt32(tempLoc); } - void ASRT::setSegmentRun(long firstSegment, long fragmentsPerSegment, long no){ + void ASRT::setSegmentRun(uint32_t firstSegment, uint32_t fragmentsPerSegment, uint32_t no){ int tempLoc = 5; //position of qualityentry count; for (int i = 0; i < getQualityEntryCount(); i++){ tempLoc += getStringLen(tempLoc) + 1; @@ -1122,7 +1122,7 @@ namespace MP4 { setInt32(fragmentsPerSegment, tempLoc + 4); } - asrt_runtable ASRT::getSegmentRun(long no){ + asrt_runtable ASRT::getSegmentRun(uint32_t no){ asrt_runtable res; if (no >= getSegmentRunEntryCount()){ return res; @@ -1164,11 +1164,11 @@ namespace MP4 { setInt32(0, 0); } - void MFHD::setSequenceNumber(long newSequenceNumber){ + void MFHD::setSequenceNumber(uint32_t newSequenceNumber){ setInt32(newSequenceNumber, 4); } - long MFHD::getSequenceNumber(){ + uint32_t MFHD::getSequenceNumber(){ return getInt32(4); } @@ -1183,7 +1183,7 @@ namespace MP4 { memcpy(data + 4, "moof", 4); } - long MOOF::getContentCount(){ + uint32_t MOOF::getContentCount(){ int res = 0; int tempLoc = 0; while (tempLoc < boxedSize() - 8){ @@ -1193,7 +1193,7 @@ namespace MP4 { return res; } - void MOOF::setContent(Box & newContent, long no){ + void MOOF::setContent(Box & newContent, uint32_t no){ int tempLoc = 0; int contentCount = getContentCount(); for (int i = 0; i < no; i++){ @@ -1211,7 +1211,7 @@ namespace MP4 { setBox(newContent, tempLoc); } - Box & MOOF::getContent(long no){ + Box & MOOF::getContent(uint32_t no){ static Box ret = Box((char*)"\000\000\000\010erro", false); if (no > getContentCount()){ return ret; @@ -1243,7 +1243,7 @@ namespace MP4 { memcpy(data + 4, "traf", 4); } - long TRAF::getContentCount(){ + uint32_t TRAF::getContentCount(){ int res = 0; int tempLoc = 0; while (tempLoc < boxedSize() - 8){ @@ -1253,7 +1253,7 @@ namespace MP4 { return res; } - void TRAF::setContent(Box & newContent, long no){ + void TRAF::setContent(Box & newContent, uint32_t no){ int tempLoc = 0; int contentCount = getContentCount(); for (int i = 0; i < no; i++){ @@ -1271,7 +1271,7 @@ namespace MP4 { setBox(newContent, tempLoc); } - Box & TRAF::getContent(long no){ + Box & TRAF::getContent(uint32_t no){ static Box ret = Box((char*)"\000\000\000\010erro", false); if (no > getContentCount()){ return ret; @@ -1303,21 +1303,21 @@ namespace MP4 { memcpy(data + 4, "trun", 4); } - void TRUN::setFlags(long newFlags){ + void TRUN::setFlags(uint32_t newFlags){ setInt24(newFlags, 1); } - long TRUN::getFlags(){ + uint32_t TRUN::getFlags(){ return getInt24(1); } - void TRUN::setDataOffset(long newOffset){ + void TRUN::setDataOffset(uint32_t newOffset){ if (getFlags() & trundataOffset){ setInt32(newOffset, 8); } } - long TRUN::getDataOffset(){ + uint32_t TRUN::getDataOffset(){ if (getFlags() & trundataOffset){ return getInt32(8); }else{ @@ -1325,7 +1325,7 @@ namespace MP4 { } } - void TRUN::setFirstSampleFlags(long newSampleFlags){ + void TRUN::setFirstSampleFlags(uint32_t newSampleFlags){ if ( !(getFlags() & trunfirstSampleFlags)){ return; } @@ -1336,7 +1336,7 @@ namespace MP4 { } } - long TRUN::getFirstSampleFlags(){ + uint32_t TRUN::getFirstSampleFlags(){ if ( !(getFlags() & trunfirstSampleFlags)){ return 0; } @@ -1347,13 +1347,13 @@ namespace MP4 { } } - long TRUN::getSampleInformationCount(){ + uint32_t TRUN::getSampleInformationCount(){ return getInt32(4); } - void TRUN::setSampleInformation(trunSampleInformation newSample, long no){ - long flags = getFlags(); - long sampInfoSize = 0; + void TRUN::setSampleInformation(trunSampleInformation newSample, uint32_t no){ + uint32_t flags = getFlags(); + uint32_t sampInfoSize = 0; if (flags & trunsampleDuration){ sampInfoSize += 4; } @@ -1366,14 +1366,14 @@ namespace MP4 { if (flags & trunsampleOffsets){ sampInfoSize += 4; } - long offset = 8; + uint32_t offset = 8; if (flags & trundataOffset){ offset += 4; } if (flags & trunfirstSampleFlags){ offset += 4; } - long innerOffset = 0; + uint32_t innerOffset = 0; if (flags & trunsampleDuration){ setInt32(newSample.sampleDuration, offset + no * sampInfoSize + innerOffset); innerOffset += 4; @@ -1395,7 +1395,7 @@ namespace MP4 { } } - trunSampleInformation TRUN::getSampleInformation(long no){ + trunSampleInformation TRUN::getSampleInformation(uint32_t no){ trunSampleInformation ret; ret.sampleDuration = 0; ret.sampleSize = 0; @@ -1404,8 +1404,8 @@ namespace MP4 { if (getSampleInformationCount() < no + 1){ return ret; } - long flags = getFlags(); - long sampInfoSize = 0; + uint32_t flags = getFlags(); + uint32_t sampInfoSize = 0; if (flags & trunsampleDuration){ sampInfoSize += 4; } @@ -1418,14 +1418,14 @@ namespace MP4 { if (flags & trunsampleOffsets){ sampInfoSize += 4; } - long offset = 8; + uint32_t offset = 8; if (flags & trundataOffset){ offset += 4; } if (flags & trunfirstSampleFlags){ offset += 4; } - long innerOffset = 0; + uint32_t innerOffset = 0; if (flags & trunsampleDuration){ ret.sampleDuration = getInt32(offset + no * sampInfoSize + innerOffset); innerOffset += 4; @@ -1445,12 +1445,12 @@ namespace MP4 { return ret; } - std::string TRUN::toPrettyString(long indent){ + std::string TRUN::toPrettyString(uint32_t indent){ std::stringstream r; r << std::string(indent, ' ') << "[trun] Track Fragment Run (" << boxedSize() << ")" << std::endl; r << std::string(indent + 1, ' ') << "Version " << (int)getInt8(0) << std::endl; - long flags = getFlags(); + uint32_t flags = getFlags(); r << std::string(indent + 1, ' ') << "Flags"; if (flags & trundataOffset){ r << " dataOffset"; @@ -1500,7 +1500,7 @@ namespace MP4 { return r.str(); } - std::string prettySampleFlags(long flag){ + std::string prettySampleFlags(uint32_t flag){ std::stringstream r; if (flag & noIPicture){ r << " noIPicture"; @@ -1532,29 +1532,29 @@ namespace MP4 { memcpy(data + 4, "tfhd", 4); } - void TFHD::setFlags(long newFlags){ + void TFHD::setFlags(uint32_t newFlags){ setInt24(newFlags, 1); } - long TFHD::getFlags(){ + uint32_t TFHD::getFlags(){ return getInt24(1); } - void TFHD::setTrackID(long newID){ + void TFHD::setTrackID(uint32_t newID){ setInt32(newID, 4); } - long TFHD::getTrackID(){ + uint32_t TFHD::getTrackID(){ return getInt32(4); } - void TFHD::setBaseDataOffset(long long newOffset){ + void TFHD::setBaseDataOffset(uint64_t newOffset){ if (getFlags() & tfhdBaseOffset){ setInt64(newOffset, 8); } } - long long TFHD::getBaseDataOffset(){ + uint64_t TFHD::getBaseDataOffset(){ if (getFlags() & tfhdBaseOffset){ return getInt64(8); }else{ @@ -1562,7 +1562,7 @@ namespace MP4 { } } - void TFHD::setSampleDescriptionIndex(long newIndex){ + void TFHD::setSampleDescriptionIndex(uint32_t newIndex){ if ( !(getFlags() & tfhdSampleDesc)){ return; } @@ -1573,7 +1573,7 @@ namespace MP4 { setInt32(newIndex, offset); } - long TFHD::getSampleDescriptionIndex(){ + uint32_t TFHD::getSampleDescriptionIndex(){ if ( !(getFlags() & tfhdSampleDesc)){ return 0; } @@ -1584,7 +1584,7 @@ namespace MP4 { return getInt32(offset); } - void TFHD::setDefaultSampleDuration(long newDuration){ + void TFHD::setDefaultSampleDuration(uint32_t newDuration){ if ( !(getFlags() & tfhdSampleDura)){ return; } @@ -1598,7 +1598,7 @@ namespace MP4 { setInt32(newDuration, offset); } - long TFHD::getDefaultSampleDuration(){ + uint32_t TFHD::getDefaultSampleDuration(){ if ( !(getFlags() & tfhdSampleDura)){ return 0; } @@ -1612,7 +1612,7 @@ namespace MP4 { return getInt32(offset); } - void TFHD::setDefaultSampleSize(long newSize){ + void TFHD::setDefaultSampleSize(uint32_t newSize){ if ( !(getFlags() & tfhdSampleSize)){ return; } @@ -1629,7 +1629,7 @@ namespace MP4 { setInt32(newSize, offset); } - long TFHD::getDefaultSampleSize(){ + uint32_t TFHD::getDefaultSampleSize(){ if ( !(getFlags() & tfhdSampleSize)){ return 0; } @@ -1646,7 +1646,7 @@ namespace MP4 { return getInt32(offset); } - void TFHD::setDefaultSampleFlags(long newFlags){ + void TFHD::setDefaultSampleFlags(uint32_t newFlags){ if ( !(getFlags() & tfhdSampleFlag)){ return; } @@ -1666,7 +1666,7 @@ namespace MP4 { setInt32(newFlags, offset); } - long TFHD::getDefaultSampleFlags(){ + uint32_t TFHD::getDefaultSampleFlags(){ if ( !(getFlags() & tfhdSampleFlag)){ return 0; } @@ -1686,12 +1686,12 @@ namespace MP4 { return getInt32(offset); } - std::string TFHD::toPrettyString(long indent){ + std::string TFHD::toPrettyString(uint32_t indent){ std::stringstream r; r << std::string(indent, ' ') << "[tfhd] Track Fragment Header (" << boxedSize() << ")" << std::endl; r << std::string(indent + 1, ' ') << "Version " << (int)getInt8(0) << std::endl; - long flags = getFlags(); + uint32_t flags = getFlags(); r << std::string(indent + 1, ' ') << "Flags"; if (flags & tfhdBaseOffset){ r << " BaseOffset"; @@ -1740,19 +1740,19 @@ namespace MP4 { setFlags(0); } - void AFRA::setVersion(long newVersion){ + void AFRA::setVersion(uint32_t newVersion){ setInt8(newVersion, 0); } - long AFRA::getVersion(){ + uint32_t AFRA::getVersion(){ return getInt8(0); } - void AFRA::setFlags(long newFlags){ + void AFRA::setFlags(uint32_t newFlags){ setInt24(newFlags, 1); } - long AFRA::getFlags(){ + uint32_t AFRA::getFlags(){ return getInt24(1); } @@ -1792,19 +1792,19 @@ namespace MP4 { return getInt8(4) & 0x20; } - void AFRA::setTimeScale(long newVal){ + void AFRA::setTimeScale(uint32_t newVal){ setInt32(newVal, 5); } - long AFRA::getTimeScale(){ + uint32_t AFRA::getTimeScale(){ return getInt32(5); } - long AFRA::getEntryCount(){ + uint32_t AFRA::getEntryCount(){ return getInt32(9); } - void AFRA::setEntry(afraentry newEntry, long no){ + void AFRA::setEntry(afraentry newEntry, uint32_t no){ int entrysize = 12; if (getLongOffsets()){ entrysize = 16; @@ -1820,7 +1820,7 @@ namespace MP4 { } } - afraentry AFRA::getEntry(long no){ + afraentry AFRA::getEntry(uint32_t no){ afraentry ret; int entrysize = 12; if (getLongOffsets()){ @@ -1835,7 +1835,7 @@ namespace MP4 { return ret; } - long AFRA::getGlobalEntryCount(){ + uint32_t AFRA::getGlobalEntryCount(){ if ( !getGlobalEntries()){ return 0; } @@ -1846,7 +1846,7 @@ namespace MP4 { return getInt32(13 + entrysize * getEntryCount()); } - void AFRA::setGlobalEntry(globalafraentry newEntry, long no){ + void AFRA::setGlobalEntry(globalafraentry newEntry, uint32_t no){ int offset = 13 + 12 * getEntryCount() + 4; if (getLongOffsets()){ offset = 13 + 16 * getEntryCount() + 4; @@ -1880,7 +1880,7 @@ namespace MP4 { } } - globalafraentry AFRA::getGlobalEntry(long no){ + globalafraentry AFRA::getGlobalEntry(uint32_t no){ globalafraentry ret; int offset = 13 + 12 * getEntryCount() + 4; if (getLongOffsets()){ @@ -1912,7 +1912,7 @@ namespace MP4 { return ret; } - std::string AFRA::toPrettyString(long indent){ + 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; @@ -1922,9 +1922,9 @@ namespace MP4 { r << std::string(indent + 1, ' ') << "Global Entries " << getGlobalEntries() << std::endl; r << std::string(indent + 1, ' ') << "TimeScale " << getTimeScale() << std::endl; - long count = getEntryCount(); + uint32_t count = getEntryCount(); r << std::string(indent + 1, ' ') << "Entries (" << count << ") " << std::endl; - for (long i = 0; i < count; ++i){ + 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; } @@ -1932,7 +1932,7 @@ namespace MP4 { if (getGlobalEntries()){ count = getGlobalEntryCount(); r << std::string(indent + 1, ' ') << "Global Entries (" << count << ") " << std::endl; - for (long i = 0; i < count; ++i){ + 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; @@ -1947,43 +1947,43 @@ namespace MP4 { setInt8(0xFF, 4); //reserved + 4-bytes NAL length } - void AVCC::setVersion(long newVersion){ + void AVCC::setVersion(uint32_t newVersion){ setInt8(newVersion, 0); } - long AVCC::getVersion(){ + uint32_t AVCC::getVersion(){ return getInt8(0); } - void AVCC::setProfile(long newProfile){ + void AVCC::setProfile(uint32_t newProfile){ setInt8(newProfile, 1); } - long AVCC::getProfile(){ + uint32_t AVCC::getProfile(){ return getInt8(1); } - void AVCC::setCompatibleProfiles(long newCompatibleProfiles){ + void AVCC::setCompatibleProfiles(uint32_t newCompatibleProfiles){ setInt8(newCompatibleProfiles, 2); } - long AVCC::getCompatibleProfiles(){ + uint32_t AVCC::getCompatibleProfiles(){ return getInt8(2); } - void AVCC::setLevel(long newLevel){ + void AVCC::setLevel(uint32_t newLevel){ setInt8(newLevel, 3); } - long AVCC::getLevel(){ + uint32_t AVCC::getLevel(){ return getInt8(3); } - void AVCC::setSPSNumber(long newSPSNumber){ + void AVCC::setSPSNumber(uint32_t newSPSNumber){ setInt8(newSPSNumber, 5); } - long AVCC::getSPSNumber(){ + uint32_t AVCC::getSPSNumber(){ return getInt8(5); } @@ -1994,7 +1994,7 @@ namespace MP4 { } //not null-terminated } - long AVCC::getSPSLen(){ + uint32_t AVCC::getSPSLen(){ return getInt16(6); } @@ -2002,12 +2002,12 @@ namespace MP4 { return payload() + 8; } - void AVCC::setPPSNumber(long newPPSNumber){ + void AVCC::setPPSNumber(uint32_t newPPSNumber){ int offset = 8 + getSPSLen(); setInt8(newPPSNumber, offset); } - long AVCC::getPPSNumber(){ + uint32_t AVCC::getPPSNumber(){ int offset = 8 + getSPSLen(); return getInt8(offset); } @@ -2020,7 +2020,7 @@ namespace MP4 { } //not null-terminated } - long AVCC::getPPSLen(){ + uint32_t AVCC::getPPSLen(){ int offset = 8 + getSPSLen() + 1; return getInt16(offset); } @@ -2030,7 +2030,7 @@ namespace MP4 { return payload() + offset; } - std::string AVCC::toPrettyString(long indent){ + std::string AVCC::toPrettyString(uint32_t indent){ std::stringstream r; r << std::string(indent, ' ') << "[avcC] H.264 Init Data (" << boxedSize() << ")" << std::endl; r << std::string(indent + 1, ' ') << "Version: " << getVersion() << std::endl; @@ -2065,19 +2065,19 @@ namespace MP4 { memcpy(data + 4, "sdtp", 4); } - void SDTP::setVersion(long newVersion){ + void SDTP::setVersion(uint32_t newVersion){ setInt8(newVersion, 0); } - long SDTP::getVersion(){ + uint32_t SDTP::getVersion(){ return getInt8(0); } - void SDTP::setValue(long newValue, size_t index){ + void SDTP::setValue(uint32_t newValue, size_t index){ setInt8(newValue, index); } - long SDTP::getValue(size_t index){ + uint32_t SDTP::getValue(size_t index){ getInt8(index); } } diff --git a/lib/mp4.h b/lib/mp4.h index 7f631dae..053b59e3 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -19,8 +19,8 @@ namespace MP4 { std::string getType(); bool isType(const char* boxType); bool read(std::string & newData); - long long int boxedSize(); - long long int payloadSize(); + uint64_t boxedSize(); + uint64_t payloadSize(); char * asBox(); char * payload(); void clear(); @@ -31,12 +31,12 @@ namespace MP4 { char getInt8(size_t index); void setInt16(short newData, size_t index); short getInt16(size_t index); - void setInt24(long newData, size_t index); - long getInt24(size_t index); - void setInt32(long newData, size_t index); - long getInt32(size_t index); - void setInt64(long long int newData, size_t index); - long long int getInt64(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); @@ -57,10 +57,10 @@ namespace MP4 { //Box Class struct afrt_runtable{ - long firstFragment; - long long int firstTimestamp; - long duration; - long discontinuity; + uint32_t firstFragment; + uint64_t firstTimestamp; + uint32_t duration; + uint32_t discontinuity; }; //fragmentRun @@ -69,24 +69,24 @@ namespace MP4 { public: AFRT(); void setVersion(char newVersion); - long getVersion(); - void setUpdate(long newUpdate); - long getUpdate(); - void setTimeScale(long newScale); - long getTimeScale(); - long getQualityEntryCount(); - void setQualityEntry(std::string & newQuality, long no); - const char * getQualityEntry(long no); - long getFragmentRunCount(); - void setFragmentRun(afrt_runtable newRun, long no); - afrt_runtable getFragmentRun(long no); + 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(int indent = 0); }; //AFRT Box struct asrt_runtable{ - long firstSegment; - long fragmentsPerSegment; + uint32_t firstSegment; + uint32_t fragmentsPerSegment; }; /// ASRT Box class @@ -94,15 +94,15 @@ namespace MP4 { public: ASRT(); void setVersion(char newVersion); - long getVersion(); - void setUpdate(long newUpdate); - long getUpdate(); - long getQualityEntryCount(); - void setQualityEntry(std::string & newQuality, long no); - const char* getQualityEntry(long no); - long getSegmentRunEntryCount(); - void setSegmentRun(long firstSegment, long fragmentsPerSegment, long no); - asrt_runtable getSegmentRun(long no); + 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(int indent = 0); }; //ASRT Box @@ -113,49 +113,49 @@ namespace MP4 { ABST(); void setVersion(char newVersion); char getVersion(); - void setFlags(long newFlags); - long getFlags(); - void setBootstrapinfoVersion(long newVersion); - long getBootstrapinfoVersion(); + 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(long newTimeScale); - long getTimeScale(); - void setCurrentMediaTime(long long int newTime); - long long int getCurrentMediaTime(); - void setSmpteTimeCodeOffset(long long int newTime); - long long int getSmpteTimeCodeOffset(); + 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(); - long getServerEntryCount(); - void setServerEntry(std::string & entry, long no); - const char * getServerEntry(long no); - long getQualityEntryCount(); - void setQualityEntry(std::string & entry, long no); - const char * getQualityEntry(long no); + 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(); - long getSegmentRunTableCount(); - void setSegmentRunTable(ASRT & table, long no); - ASRT & getSegmentRunTable(long no); - long getFragmentRunTableCount(); - void setFragmentRunTable(AFRT & table, long no); - AFRT & getFragmentRunTable(long no); - std::string toPrettyString(long indent = 0); + 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 class MFHD: public Box{ public: MFHD(); - void setSequenceNumber(long newSequenceNumber); - long getSequenceNumber(); + void setSequenceNumber(uint32_t newSequenceNumber); + uint32_t getSequenceNumber(); std::string toPrettyString(int indent = 0); }; //MFHD Box @@ -163,9 +163,9 @@ namespace MP4 { class MOOF: public Box{ public: MOOF(); - long getContentCount(); - void setContent(Box & newContent, long no); - Box & getContent(long no); + uint32_t getContentCount(); + void setContent(Box & newContent, uint32_t no); + Box & getContent(uint32_t no); std::string toPrettyString(int indent = 0); }; //MOOF Box @@ -173,18 +173,18 @@ namespace MP4 { class TRAF: public Box{ public: TRAF(); - long getContentCount(); - void setContent(Box & newContent, long no); - Box & getContent(long no); + uint32_t getContentCount(); + void setContent(Box & newContent, uint32_t no); + Box & getContent(uint32_t no); std::string toPrettyString(int indent = 0); }; //TRAF Box struct trunSampleInformation{ - long sampleDuration; - long sampleSize; - long sampleFlags; - long sampleOffset; + uint32_t sampleDuration; + uint32_t sampleSize; + uint32_t sampleFlags; + uint32_t sampleOffset; }; enum trunflags{ trundataOffset = 0x00000001, @@ -205,20 +205,20 @@ namespace MP4 { isKeySample = 0x00000000, MUST_BE_PRESENT = 0x1 }; - std::string prettySampleFlags(long flag); + std::string prettySampleFlags(uint32_t flag); class TRUN: public Box{ public: TRUN(); - void setFlags(long newFlags); - long getFlags(); - void setDataOffset(long newOffset); - long getDataOffset(); - void setFirstSampleFlags(long newSampleFlags); - long getFirstSampleFlags(); - long getSampleInformationCount(); - void setSampleInformation(trunSampleInformation newSample, long no); - trunSampleInformation getSampleInformation(long no); - std::string toPrettyString(long indent = 0); + 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{ @@ -232,91 +232,91 @@ namespace MP4 { class TFHD: public Box{ public: TFHD(); - void setFlags(long newFlags); - long getFlags(); - void setTrackID(long newID); - long getTrackID(); - void setBaseDataOffset(long long newOffset); - long long getBaseDataOffset(); - void setSampleDescriptionIndex(long newIndex); - long getSampleDescriptionIndex(); - void setDefaultSampleDuration(long newDuration); - long getDefaultSampleDuration(); - void setDefaultSampleSize(long newSize); - long getDefaultSampleSize(); - void setDefaultSampleFlags(long newFlags); - long getDefaultSampleFlags(); - std::string toPrettyString(long indent = 0); + 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); }; struct afraentry{ - long long time; - long long offset; + uint64_t time; + uint64_t offset; }; struct globalafraentry{ - long long time; - long segment; - long fragment; - long long afraoffset; - long long offsetfromafra; + uint64_t time; + uint32_t segment; + uint32_t fragment; + uint64_t afraoffset; + uint64_t offsetfromafra; }; class AFRA: public Box{ public: AFRA(); - void setVersion(long newVersion); - long getVersion(); - void setFlags(long newFlags); - long getFlags(); + 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(long newVal); - long getTimeScale(); - long getEntryCount(); - void setEntry(afraentry newEntry, long no); - afraentry getEntry(long no); - long getGlobalEntryCount(); - void setGlobalEntry(globalafraentry newEntry, long no); - globalafraentry getGlobalEntry(long no); - std::string toPrettyString(long indent = 0); + 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); }; class AVCC: public Box{ public: AVCC(); - void setVersion(long newVersion); - long getVersion(); - void setProfile(long newProfile); - long getProfile(); - void setCompatibleProfiles(long newCompatibleProfiles); - long getCompatibleProfiles(); - void setLevel(long newLevel); - long getLevel(); - void setSPSNumber(long newSPSNumber); - long getSPSNumber(); + 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); - long getSPSLen(); + uint32_t getSPSLen(); char* getSPS(); - void setPPSNumber(long newPPSNumber); - long getPPSNumber(); + void setPPSNumber(uint32_t newPPSNumber); + uint32_t getPPSNumber(); void setPPS(std::string newPPS); - long getPPSLen(); + uint32_t getPPSLen(); char* getPPS(); std::string asAnnexB(); void setPayload(std::string newPayload); - std::string toPrettyString(long indent = 0); + std::string toPrettyString(uint32_t indent = 0); }; class SDTP: public Box{ public: SDTP(); - void setVersion(long newVersion); - long getVersion(); - void setValue(long newValue, size_t index); - long getValue(size_t index); + void setVersion(uint32_t newVersion); + uint32_t getVersion(); + void setValue(uint32_t newValue, size_t index); + uint32_t getValue(size_t index); }; } From db735a73c70d8590730b47546b4d7b8334ea49f1 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 4 Mar 2013 22:02:26 +0100 Subject: [PATCH 356/788] New API format support in stream.cpp --- lib/stream.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/stream.cpp b/lib/stream.cpp index 476c4402..2dd2b3b0 100644 --- a/lib/stream.cpp +++ b/lib/stream.cpp @@ -51,11 +51,11 @@ Socket::Connection Util::Stream::getStream(std::string streamname){ sanitizeName(streamname); JSON::Value ServConf = JSON::fromFile("/tmp/mist/streamlist"); if (ServConf["streams"].isMember(streamname)){ - if (ServConf["streams"][streamname]["channel"]["URL"].asString()[0] == '/'){ + if (ServConf["streams"][streamname]["source"].asString()[0] == '/'){ #if DEBUG >= 4 - std::cerr << "Opening VoD stream from file " << ServConf["streams"][streamname]["channel"]["URL"].asString() << std::endl; + std::cerr << "Opening VoD stream from file " << ServConf["streams"][streamname]["source"].asString() << std::endl; #endif - return getVod(ServConf["streams"][streamname]["channel"]["URL"].asString()); + return getVod(ServConf["streams"][streamname]["source"].asString()); }else{ #if DEBUG >= 4 std::cerr << "Opening live stream " << streamname << std::endl; From 027a986481cfcae6c978ba0fe42bd7f5fe93c3d5 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 5 Mar 2013 11:42:22 +0100 Subject: [PATCH 357/788] Improved live metadata support. --- lib/dtsc.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index a1a9a393..2ccf4587 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -307,6 +307,9 @@ void DTSC::Stream::dropRing(DTSC::Ring * ptr){ } } +/// Updates the headers for a live stream, keeping track of all available +/// keyframes and their media times. The function MAY NOT be run at any other +/// time than right after receiving a new keyframe, or there'll be raptors. void DTSC::Stream::updateHeaders(){ if (keyframes.size() > 2){ metadata["keytime"].shrink(keyframes.size() - 2); @@ -317,6 +320,7 @@ void DTSC::Stream::updateHeaders(){ }else{ metadata["keynum"].append(metadata["keynum"][metadata["keynum"].size() - 1].asInt() + 1); } + metadata["keylen"].append(buffers[keyframes[0].b]["time"].asInt() - buffers[keyframes[1].b]["time"].asInt()); metadata["lastms"] = buffers[keyframes[0].b]["time"].asInt(); metadata.toPacked(); updateRingHeaders(); From 23e87450c79242ca2cf4a20dc090f0acb01f0ce0 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 5 Mar 2013 12:25:40 +0100 Subject: [PATCH 358/788] More metadata improvements - now has "vod" and "live" data to indicate stream type, as well as "buffer_window" to give information about the size of the intended buffer length. --- lib/dtsc.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 2ccf4587..f5e90b8c 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -322,6 +322,8 @@ void DTSC::Stream::updateHeaders(){ } metadata["keylen"].append(buffers[keyframes[0].b]["time"].asInt() - buffers[keyframes[1].b]["time"].asInt()); metadata["lastms"] = buffers[keyframes[0].b]["time"].asInt(); + metadata["buffer_window"] = (long long int)buffertime; + metadata["live"] = true; metadata.toPacked(); updateRingHeaders(); } @@ -496,6 +498,8 @@ void DTSC::File::readHeader(int pos){ frames[i + 1] = metadata["keybpos"][i].asInt(); } } + metadata["vod"] = true; + metadata.toPacked(); } /// Reads the packet available at the current file position. From f487e9b4eced2104bc3837298171fc1a7b7abc0e Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 5 Mar 2013 17:22:28 +0100 Subject: [PATCH 359/788] Implemented live fragmenting into ~10-second fragments through new metadata. --- lib/dtsc.cpp | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index f5e90b8c..ee100a0b 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -321,6 +321,51 @@ void DTSC::Stream::updateHeaders(){ metadata["keynum"].append(metadata["keynum"][metadata["keynum"].size() - 1].asInt() + 1); } metadata["keylen"].append(buffers[keyframes[0].b]["time"].asInt() - buffers[keyframes[1].b]["time"].asInt()); + long long int firstFragNo = -1; + metadata["frags"].null(); + long long int currFrag = metadata["keytime"][0u].asInt() / 10000; + if (currFrag == 0){ + long long int fragLen = 1; + long long int fragDur = metadata["keylen"][0u].asInt(); + for (unsigned int j = 1; j < metadata["keytime"].size(); j++){ + if (metadata["keytime"][j].asInt() / 10000 > currFrag){ + if (firstFragNo == -1){ + firstFragNo = currFrag; + } + JSON::Value thisFrag; + thisFrag["num"] = metadata["keynum"][0u]; + thisFrag["len"] = fragLen; + thisFrag["dur"] = fragDur; + metadata["frags"].append(thisFrag); + break; + } + fragLen++; + fragDur += metadata["keylen"][j].asInt(); + } + } + for (unsigned int i = 1; i < metadata["keytime"].size(); i++){ + if (metadata["keytime"][i].asInt() / 10000 > currFrag){ + currFrag = metadata["keytime"][i].asInt() / 10000; + long long int fragLen = 1; + long long int fragDur = metadata["keylen"][i].asInt(); + for (unsigned int j = i + 1; j < metadata["keytime"].size(); j++){ + if (metadata["keytime"][j].asInt() / 10000 > currFrag){ + if (firstFragNo == -1){ + firstFragNo = currFrag; + } + JSON::Value thisFrag; + thisFrag["num"] = metadata["keynum"][i]; + thisFrag["len"] = fragLen; + thisFrag["dur"] = fragDur; + metadata["frags"].append(thisFrag); + break; + } + fragLen++; + fragDur += metadata["keylen"][j].asInt(); + } + } + } + metadata["missed_frags"] = firstFragNo; metadata["lastms"] = buffers[keyframes[0].b]["time"].asInt(); metadata["buffer_window"] = (long long int)buffertime; metadata["live"] = true; From c4544a4d21a89502321af313eb2fab56271c7c5d Mon Sep 17 00:00:00 2001 From: Thulinma Date: Wed, 6 Mar 2013 21:03:26 +0100 Subject: [PATCH 360/788] Limit maximum attempted data writing to 50KiB per block (resolves slowness issues with small network buffers and/or big packets). --- lib/socket.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/socket.cpp b/lib/socket.cpp index a9a7eec3..1c5a02f8 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -402,7 +402,7 @@ void Socket::Connection::SendNow(const char * data, size_t len){ } int i = iwrite(data, len); while (i < len && connected()){ - int j = iwrite(data + i, len - i); + int j = iwrite(data + i, std::min(len - i, (size_t)51200)); if (j > 0){ i += j; }else{ From a1ed3a8465b5e089894b31e6a2a0a0daa9e5781d Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 7 Mar 2013 20:08:22 +0100 Subject: [PATCH 361/788] Implemented SDTP, UUID and UUID_TrackFragmentReference boxes in MP4 lib. --- lib/mp4.cpp | 196 +++++++++++++++++++++++++++++++++++++++++++++++++++- lib/mp4.h | 27 ++++++++ 2 files changed, 222 insertions(+), 1 deletion(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 0095868a..dec687d2 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -151,10 +151,19 @@ namespace MP4 { case 0x61766343: return ((AVCC*)this)->toPrettyString(indent); break; + case 0x73647470: + return ((SDTP*)this)->toPrettyString(indent); + break; + case 0x75756964: + return ((UUID*)this)->toPrettyString(indent); + break; default: break; } - return std::string(indent, ' ') + "Unimplemented pretty-printing for box " + std::string(data + 4, 4) + "\n"; + std::string retval = std::string(indent, ' ') + "Unimplemented pretty-printing for box " + std::string(data + 4, 4) + "\n"; + /// \todo Implement hexdump for unimplemented boxes? + //retval += + return retval; } /// Sets the 8 bits integer at the given index. @@ -2080,4 +2089,189 @@ namespace MP4 { uint32_t SDTP::getValue(size_t index){ 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(); + } + + 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; + } + + 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]) << (4-(4*(i%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(); + } + + } diff --git a/lib/mp4.h b/lib/mp4.h index 053b59e3..a7d8f80e 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -317,6 +317,33 @@ namespace MP4 { 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); + }; + } From 4e169829299ece6387f8d9d4d10e8c1c63840274 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 12 Mar 2013 02:04:28 +0100 Subject: [PATCH 362/788] Fixed wrong count of frame numbers when seeking in files. Improved DTSC lib verbosity. --- lib/dtsc.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index ee100a0b..f9cd821f 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -386,10 +386,11 @@ void DTSC::Stream::updateRingHeaders(){ unsigned int DTSC::Stream::msSeek(unsigned int ms){ for (std::deque::iterator it = keyframes.begin(); it != keyframes.end(); it++){ - if (buffers[it->b]["time"].asInt() < ms){ + if (buffers[it->b]["time"].asInt() <= ms){ return it->b; } } + std::cerr << "Warning: seeking past buffer size! (" << ms << "ms < " << buffers[keyframes[keyframes.size() - 1].b]["time"].asInt() << "ms)" << std::endl; return keyframes[keyframes.size() - 1].b; } @@ -399,6 +400,7 @@ unsigned int DTSC::Stream::frameSeek(unsigned int frameno){ return it->b; } } + std::cerr << "Warning: seeking past buffer size! (F" << frameno << " < F" << buffers[keyframes[keyframes.size() - 1].b]["fragnum"].asInt() << ")" << std::endl; return keyframes[keyframes.size() - 1].b; } @@ -442,9 +444,10 @@ DTSC::File::File(std::string filename, bool create){ } readHeader(0); fseek(F, 8 + headerSize, SEEK_SET); - currframe = 1; - frames[1] = 8 + headerSize; - msframes[1] = 0; + currframe = 0; + //currframe = 1; + //frames[1] = 8 + headerSize; + //msframes[1] = 0; } /// Returns the header metadata for this file as JSON::Value. @@ -628,7 +631,7 @@ JSON::Value & DTSC::File::getJSON(){ bool DTSC::File::seek_frame(int frameno){ if (frames.count(frameno) > 0){ if (fseek(F, frames[frameno], SEEK_SET) == 0){ -#if DEBUG >= 4 +#if DEBUG >= 5 std::cerr << "Seek direct from " << currframe << " @ " << frames[currframe] << " to " << frameno << " @ " << frames[frameno] << std::endl; #endif currframe = frameno; @@ -642,7 +645,7 @@ bool DTSC::File::seek_frame(int frameno){ } } if (fseek(F, frames[currframe], SEEK_SET) == 0){ -#if DEBUG >= 4 +#if DEBUG >= 5 std::cerr << "Seeking from frame " << currframe << " @ " << frames[currframe] << " to " << frameno << std::endl; #endif while (currframe < frameno){ @@ -674,7 +677,7 @@ bool DTSC::File::seek_time(int ms){ } } if (fseek(F, frames[currframe], SEEK_SET) == 0){ -#if DEBUG >= 4 +#if DEBUG >= 5 std::cerr << "Seeking from frame " << currframe << " @ " << msframes[currframe] << "ms to " << ms << "ms" << std::endl; #endif while (currtime < ms){ From 97b51ff39a7a2051de1e04faeaaa3b9380f16da9 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 12 Mar 2013 16:19:46 +0100 Subject: [PATCH 363/788] Augmented Socket::Buffer features. --- lib/socket.cpp | 24 ++++++++++++++++++++++++ lib/socket.h | 3 +++ 2 files changed, 27 insertions(+) diff --git a/lib/socket.cpp b/lib/socket.cpp index 1c5a02f8..34647692 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -32,6 +32,18 @@ unsigned int Socket::Buffer::size(){ return data.size(); } +/// Returns either the amount of total bytes available in the buffer or max, whichever is smaller. +unsigned int Socket::Buffer::bytes(unsigned int max){ + unsigned int i = 0; + for (std::deque::iterator it = data.begin(); it != data.end(); ++it){ + i += ( *it).size(); + if (i >= max){ + return max; + } + } + return i; +} + /// Appends this string to the internal std::deque of std::string objects. /// It is automatically split every BUFFER_BLOCKSIZE bytes. void Socket::Buffer::append(const std::string & newdata){ @@ -62,6 +74,18 @@ void Socket::Buffer::append(const char * newdata, const unsigned int newdatasize } } +/// Prepends this data block to the internal std::deque of std::string objects. +/// It is _not_ automatically split every BUFFER_BLOCKSIZE bytes. +void Socket::Buffer::prepend(const std::string & newdata){ + data.push_back(newdata); +} + +/// Prepends this data block to the internal std::deque of std::string objects. +/// It is _not_ automatically split every BUFFER_BLOCKSIZE bytes. +void Socket::Buffer::prepend(const char * newdata, const unsigned int newdatasize){ + data.push_back(std::string(newdata, (size_t)newdatasize)); +} + /// Returns true if at least count bytes are available in this buffer. bool Socket::Buffer::available(unsigned int count){ unsigned int i = 0; diff --git a/lib/socket.h b/lib/socket.h index 73db0b0b..03aec9c2 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -30,8 +30,11 @@ namespace Socket { std::deque 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); From cc8dfb5257dcd930a74b4db41740eed6d8c28f47 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 12 Mar 2013 16:20:02 +0100 Subject: [PATCH 364/788] Optimized TS library throughput. --- lib/ts_packet.cpp | 70 +++++++++++++++++++++++++++++++++++++++++------ lib/ts_packet.h | 6 ++-- 2 files changed, 65 insertions(+), 11 deletions(-) diff --git a/lib/ts_packet.cpp b/lib/ts_packet.cpp index 82fbd941..97a79364 100644 --- a/lib/ts_packet.cpp +++ b/lib/ts_packet.cpp @@ -334,30 +334,81 @@ void TS::Packet::PESAudioLeadIn(std::string & toSend, long long unsigned int PTS toSend.insert(0, tmpStr); } +/// Generates a PES Lead-in for a video frame. +/// Prepends the lead-in to variable toSend, assumes toSend's length is all other data. +/// \param NewLen The length of this frame. +/// \param PTS The timestamp of the frame. +std::string & TS::Packet::getPESVideoLeadIn(unsigned int NewLen, long long unsigned int PTS){ + static std::string tmpStr; + tmpStr.clear(); + tmpStr.reserve(25); + tmpStr.append("\000\000\001\340\000\000\204\300\012", 9); + tmpStr += (char)(0x30 + ((PTS & 0x1C0000000LL) >> 29) + 1); //Fixed + PTS + tmpStr += (char)((PTS & 0x03FC00000LL) >> 22); //PTS (Cont) + tmpStr += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //PTS (Cont) + tmpStr += (char)((PTS & 0x000007F80LL) >> 7); //PTS (Cont) + tmpStr += (char)(((PTS & 0x00000007FLL) << 1) + 1); //PTS (Cont) + tmpStr += (char)(0x10 + ((PTS & 0x1C0000000LL) >> 29) + 1); //Fixed + DTS + tmpStr += (char)((PTS & 0x03FC00000LL) >> 22); //DTS (Cont) + tmpStr += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //DTS (Cont) + tmpStr += (char)((PTS & 0x000007F80LL) >> 7); //DTS (Cont) + tmpStr += (char)(((PTS & 0x00000007FLL) << 1) + 1); //DTS (Cont) + tmpStr.append("\000\000\000\001\011\360", 6); + return tmpStr; +} + +/// Generates a PES Lead-in for an audio frame. +/// Prepends the lead-in to variable toSend, assumes toSend's length is all other data. +/// \param NewLen The length of this frame. +/// \param PTS The timestamp of the frame. +std::string & TS::Packet::getPESAudioLeadIn(unsigned int NewLen, long long unsigned int PTS){ + static std::string tmpStr; + tmpStr.clear(); + tmpStr.reserve(14); + NewLen = NewLen + 8; + tmpStr.append("\000\000\001\300", 4); + tmpStr += (char)((NewLen & 0xFF00) >> 8); //PES PacketLength + tmpStr += (char)(NewLen & 0x00FF); //PES PacketLength (Cont) + tmpStr.append("\200\200\005", 3); + tmpStr += (char)(0x20 + ((PTS & 0x1C0000000LL) >> 29) + 1); //PTS + tmpStr += (char)((PTS & 0x03FC00000LL) >> 22); //PTS (Cont) + tmpStr += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //PTS (Cont) + tmpStr += (char)((PTS & 0x000007F80LL) >> 7); //PTS (Cont) + tmpStr += (char)(((PTS & 0x00000007FLL) << 1) + 1); //PTS (Cont) + return tmpStr; +} + /// Fills the free bytes of the TS::Packet. /// Stores as many bytes from NewVal as possible in the packet. /// \param NewVal The data to store in the packet. void TS::Packet::FillFree(std::string & NewVal){ - int toWrite = 188 - strBuf.size(); - strBuf += NewVal.substr(0, toWrite); - NewVal.erase(0, toWrite); + int toWrite = BytesFree(); + if (toWrite == NewVal.size()){ + strBuf += NewVal; + NewVal.clear(); + }else{ + strBuf += NewVal.substr(0, toWrite); + NewVal.erase(0, toWrite); + } } /// Fills the free bytes of the TS::Packet. /// Stores as many bytes from NewVal as possible in the packet. /// \param NewVal The data to store in the packet. int TS::Packet::FillFree(const char* NewVal, int maxLen){ - int toWrite = std::min((int)(188 - strBuf.size()), maxLen); + int toWrite = std::min((int)BytesFree(), maxLen); strBuf += std::string(NewVal, toWrite); return toWrite; } -/// Adds NumBytes of stuffing to the TS::Packet. -/// \param NumBytes the amount of stuffing bytes. -void TS::Packet::AddStuffing(int NumBytes){ - if (NumBytes <= 0){ - return; +/// Adds stuffing to the TS::Packet depending on how much content you want to send. +/// \param NumBytes the amount of non-stuffing content bytes you want to send. +/// \return The amount of content bytes that can be send. +unsigned int TS::Packet::AddStuffing(int NumBytes){ + if (BytesFree() <= NumBytes){ + return BytesFree(); } + NumBytes = BytesFree() - NumBytes; if (AdaptationField() == 3){ strBuf.resize(5 + strBuf[4]); strBuf[4] += NumBytes; @@ -378,4 +429,5 @@ void TS::Packet::AddStuffing(int NumBytes){ strBuf[4] = (char)(NumBytes - 1); } } + return BytesFree(); } diff --git a/lib/ts_packet.h b/lib/ts_packet.h index 1a54afb4..d289733d 100644 --- a/lib/ts_packet.h +++ b/lib/ts_packet.h @@ -44,10 +44,12 @@ namespace TS { void PESAudioLeadIn(unsigned int NewLen, uint64_t PTS = 0); static void PESAudioLeadIn(std::string & toSend, long long unsigned int PTS); static void PESVideoLeadIn(std::string & toSend, long long unsigned int PTS); - + static std::string & getPESAudioLeadIn(unsigned int NewLen, long long unsigned int PTS); + static std::string & getPESVideoLeadIn(unsigned int NewLen, long long unsigned int PTS); + void FillFree(std::string & PackageData); int FillFree(const char* PackageData, int maxLen); - void AddStuffing(int NumBytes); + unsigned int AddStuffing(int NumBytes); private: //int Free; std::string strBuf; From aa5056ee3d2c2abe42ea0b06426cfa137f2998fa Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 14 Mar 2013 00:13:07 +0100 Subject: [PATCH 365/788] Fixed compiling on OSX, also fixes DDVTECH/mistlib#1 --- configure.ac | 5 ++++- lib/Makefile.am | 3 ++- lib/config.cpp | 2 +- lib/procs.cpp | 2 +- lib/timing.cpp | 17 +++++++++++++++++ 5 files changed, 25 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index 63630ca1..6103353a 100644 --- a/configure.ac +++ b/configure.ac @@ -16,7 +16,7 @@ AC_PROG_CC # Checks for libraries. AC_DEFINE(_GNU_SOURCE) -#AC_CHECK_LIB(ssl, RC4) +PKG_CHECK_MODULES(DEPS, openssl) # Checks for header files. AC_CHECK_HEADERS([arpa/inet.h fcntl.h netdb.h netinet/in.h stdint.h stdlib.h string.h sys/socket.h sys/time.h unistd.h]) @@ -38,6 +38,9 @@ AC_FUNC_MALLOC AC_FUNC_REALLOC AC_CHECK_FUNCS([dup2 gettimeofday memset mkdir socket strerror]) +AC_CHECK_FUNCS([clock_gettime], [CLOCK_LIB=], [AC_CHECK_LIB([rt], [clock_gettime], [CLOCK_LIB=-lrt], [CLOCK_LIB=])]) +AC_SUBST([CLOCK_LIB]) + # Fix chars to unsigned AC_SUBST([global_CFLAGS], [-funsigned-char]) diff --git a/lib/Makefile.am b/lib/Makefile.am index 4daf7ce7..c93d31c3 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -2,8 +2,9 @@ AM_CPPFLAGS = $(global_CFLAGS) lib_LTLIBRARIES=libmist-1.0.la libmist_1_0_la_SOURCES=amf.h amf.cpp auth.h auth.cpp base64.h base64.cpp config.h config.cpp dtsc.h dtsc.cpp flv_tag.h flv_tag.cpp http_parser.h http_parser.cpp json.h json.cpp procs.h procs.cpp rtmpchunks.h rtmpchunks.cpp socket.h socket.cpp mp4.h mp4.cpp ftp.h ftp.cpp filesystem.h filesystem.cpp stream.h stream.cpp timing.h timing.cpp ts_packet.cpp ts_packet.h -libmist_1_0_la_LIBADD=-lssl -lcrypto -lrt libmist_1_0_la_LDFLAGS = -version-info 4:0:1 +libmist_1_0_la_CPPFLAGS=$(DEPS_CFLAGS) +libmist_1_0_la_LIBADD=$(DEPS_LIBS) $(CLOCK_LIB) pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = mist-1.0.pc diff --git a/lib/config.cpp b/lib/config.cpp index 9a771823..0bfafb79 100644 --- a/lib/config.cpp +++ b/lib/config.cpp @@ -5,7 +5,7 @@ #include #include -#ifdef __FreeBSD__ +#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__MACH__) #include #else #include diff --git a/lib/procs.cpp b/lib/procs.cpp index ab1bbbf1..64022133 100644 --- a/lib/procs.cpp +++ b/lib/procs.cpp @@ -6,7 +6,7 @@ #include #include -#ifdef __FreeBSD__ +#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__MACH__) #include #else #include diff --git a/lib/timing.cpp b/lib/timing.cpp index f89523fa..3f9841c2 100644 --- a/lib/timing.cpp +++ b/lib/timing.cpp @@ -4,6 +4,23 @@ #include "timing.h" #include //for gettimeofday #include //for time and nanosleep + +//emulate clock_gettime() for OSX compatibility +#if defined(__APPLE__) || defined(__MACH__) +#include +#include +#define CLOCK_REALTIME 0 +void clock_gettime(int ign, struct timespec * ts){ + clock_serv_t cclock; + mach_timespec_t mts; + host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &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. void Util::sleep(int ms){ if (ms < 0){ From 680b2625c1a354e47c0194bb5117ebd71551d6d7 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sat, 16 Mar 2013 16:28:00 +0100 Subject: [PATCH 366/788] Fixed chars no longer being unsigned (oops) --- lib/Makefile.am | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/Makefile.am b/lib/Makefile.am index c93d31c3..0549f29f 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,9 +1,7 @@ -AM_CPPFLAGS = $(global_CFLAGS) - lib_LTLIBRARIES=libmist-1.0.la libmist_1_0_la_SOURCES=amf.h amf.cpp auth.h auth.cpp base64.h base64.cpp config.h config.cpp dtsc.h dtsc.cpp flv_tag.h flv_tag.cpp http_parser.h http_parser.cpp json.h json.cpp procs.h procs.cpp rtmpchunks.h rtmpchunks.cpp socket.h socket.cpp mp4.h mp4.cpp ftp.h ftp.cpp filesystem.h filesystem.cpp stream.h stream.cpp timing.h timing.cpp ts_packet.cpp ts_packet.h libmist_1_0_la_LDFLAGS = -version-info 4:0:1 -libmist_1_0_la_CPPFLAGS=$(DEPS_CFLAGS) +libmist_1_0_la_CPPFLAGS=$(DEPS_CFLAGS) $(global_CFLAGS) libmist_1_0_la_LIBADD=$(DEPS_LIBS) $(CLOCK_LIB) pkgconfigdir = $(libdir)/pkgconfig From 00d06fe2931b63dd634adc34b7b732ab43b5b2fe Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 18 Mar 2013 17:36:12 +0100 Subject: [PATCH 367/788] Tweaked Stream and RTMP lib verbosity levels. --- lib/rtmpchunks.cpp | 6 +++--- lib/stream.cpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/rtmpchunks.cpp b/lib/rtmpchunks.cpp index 4f0a203a..887d62f5 100644 --- a/lib/rtmpchunks.cpp +++ b/lib/rtmpchunks.cpp @@ -261,7 +261,7 @@ bool ValidateClientScheme(uint8_t * pBuffer, uint8_t scheme){ uint8_t *pTempHash = new uint8_t[512]; HMACsha256(pTempBuffer, 1536 - 32, genuineFPKey, 30, pTempHash); bool result = (memcmp(pBuffer + clientDigestOffset, pTempHash, 32) == 0); -#if DEBUG >= 4 +#if DEBUG >= 5 fprintf(stderr, "Client scheme validation %hhi %s\n", scheme, result?"success":"failed"); #endif delete[] pTempBuffer; @@ -818,14 +818,14 @@ bool RTMPStream::doHandshake(){ } //"random" data bool encrypted = (Version == 6); -#if DEBUG >= 4 +#if DEBUG >= 8 fprintf(stderr, "Handshake version is %hhi\n", Version); #endif uint8_t _validationScheme = 5; if (ValidateClientScheme(Client, 0)) _validationScheme = 0; if (ValidateClientScheme(Client, 1)) _validationScheme = 1; -#if DEBUG >= 4 +#if DEBUG >= 8 fprintf(stderr, "Handshake type is %hhi, encryption is %s\n", _validationScheme, encrypted?"on":"off"); #endif diff --git a/lib/stream.cpp b/lib/stream.cpp index 2dd2b3b0..1ecd2363 100644 --- a/lib/stream.cpp +++ b/lib/stream.cpp @@ -52,18 +52,18 @@ Socket::Connection Util::Stream::getStream(std::string streamname){ JSON::Value ServConf = JSON::fromFile("/tmp/mist/streamlist"); if (ServConf["streams"].isMember(streamname)){ if (ServConf["streams"][streamname]["source"].asString()[0] == '/'){ -#if DEBUG >= 4 +#if DEBUG >= 5 std::cerr << "Opening VoD stream from file " << ServConf["streams"][streamname]["source"].asString() << std::endl; #endif return getVod(ServConf["streams"][streamname]["source"].asString()); }else{ -#if DEBUG >= 4 +#if DEBUG >= 5 std::cerr << "Opening live stream " << streamname << std::endl; #endif return Socket::Connection("/tmp/mist/stream_" + streamname); } } -#if DEBUG >= 4 +#if DEBUG >= 5 std::cerr << "Could not open stream " << streamname << " - stream not found" << std::endl; #endif return Socket::Connection(); From f2b4e1d1a4a4689e2dc6249059f5b3c1c9abbc61 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 18 Mar 2013 17:36:45 +0100 Subject: [PATCH 368/788] Added canSeek* functionality to DTSC::Stream, minor optimization to MP4 lib. --- lib/dtsc.cpp | 36 ++++++++++++++++++++++++++++++++++++ lib/dtsc.h | 2 ++ lib/mp4.cpp | 2 +- 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index f9cd821f..e3b4a811 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -384,7 +384,39 @@ void DTSC::Stream::updateRingHeaders(){ } } +/// Returns 0 if seeking is possible, -1 if the wanted frame is too old, 1 if the wanted frame is too new. +int DTSC::Stream::canSeekms(unsigned int ms){ + if ( !metadata["keytime"].size()){ + return 1; + } + if (ms > metadata["keytime"][metadata["keytime"].size() - 1].asInt()){ + return 1; + } + if (ms < metadata["keytime"][0u].asInt()){ + return -1; + } + return 0; +} + +/// Returns 0 if seeking is possible, -1 if the wanted frame is too old, 1 if the wanted frame is too new. +int DTSC::Stream::canSeekFrame(unsigned int frameno){ + if ( !metadata["keynum"].size()){ + return 1; + } + if (frameno > metadata["keynum"][metadata["keynum"].size() - 1].asInt()){ + return 1; + } + if (frameno < metadata["keynum"][0u].asInt()){ + return -1; + } + return 0; +} + unsigned int DTSC::Stream::msSeek(unsigned int ms){ + if (ms > buffers[keyframes[0u].b]["time"].asInt()){ + std::cerr << "Warning: seeking past ingest! (" << ms << "ms > " << buffers[keyframes[0u].b]["time"].asInt() << "ms)" << std::endl; + return keyframes[0u].b; + } for (std::deque::iterator it = keyframes.begin(); it != keyframes.end(); it++){ if (buffers[it->b]["time"].asInt() <= ms){ return it->b; @@ -395,6 +427,10 @@ unsigned int DTSC::Stream::msSeek(unsigned int ms){ } unsigned int DTSC::Stream::frameSeek(unsigned int frameno){ + if (frameno > buffers[keyframes[0u].b]["fragnum"].asInt()){ + std::cerr << "Warning: seeking past ingest! (F" << frameno << " > F" << buffers[keyframes[0u].b]["fragnum"].asInt() << ")" << std::endl; + return keyframes[0u].b; + } for (std::deque::iterator it = keyframes.begin(); it != keyframes.end(); it++){ if (buffers[it->b]["fragnum"].asInt() == frameno){ return it->b; diff --git a/lib/dtsc.h b/lib/dtsc.h index de199ebb..64304346 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -127,6 +127,8 @@ namespace DTSC { unsigned int getTime(); void dropRing(Ring * ptr); void updateHeaders(); + int canSeekms(unsigned int ms); + int canSeekFrame(unsigned int frameno); unsigned int msSeek(unsigned int ms); unsigned int frameSeek(unsigned int frameno); void setBufferTime(unsigned int ms); diff --git a/lib/mp4.cpp b/lib/mp4.cpp index dec687d2..c94dacbb 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -2180,7 +2180,7 @@ namespace MP4 { if (uuid_string[j] == '-'){ continue; } - data[8+i/2] |= (c2hex(uuid_string[j]) << (4-(4*(i%2)))); + data[8+i/2] |= (c2hex(uuid_string[j]) << ((~i & 1) << 2)); ++i; } } From bf69dacefd62170262d7e1b665b000b1dca122ce Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 19 Mar 2013 14:41:07 +0100 Subject: [PATCH 369/788] Added automated exit handler in Util::Procs. --- lib/json.cpp | 1 - lib/procs.cpp | 99 ++++++++++++++++++++++++++++++++++----------------- lib/procs.h | 4 ++- 3 files changed, 69 insertions(+), 35 deletions(-) diff --git a/lib/json.cpp b/lib/json.cpp index 9bc421d8..68596e90 100644 --- a/lib/json.cpp +++ b/lib/json.cpp @@ -476,7 +476,6 @@ std::string & JSON::Value::toNetPacked(){ //check if this is legal if (myType != OBJECT){ fprintf(stderr, "Error: Only objects may be NetPacked!\n"); - abort(); return emptystring; } //if sneaky storage doesn't contain correct data, re-calculate it diff --git a/lib/procs.cpp b/lib/procs.cpp index 64022133..a3d77075 100644 --- a/lib/procs.cpp +++ b/lib/procs.cpp @@ -18,11 +18,73 @@ #include #include #include +#include "timing.h" std::map Util::Procs::plist; std::map Util::Procs::exitHandlers; bool Util::Procs::handler_set = false; +/// Called at exit of any program that used a Start* function. +/// Waits up to 2.5 seconds, then sends SIGINT signal to all managed processes. +/// After that waits up to 10 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; + while ( !plist.empty()){ + Util::sleep(500); + if (++waiting > 5){ + break; + } + } + + if ( !plist.empty()){ + std::map listcopy = plist; + std::map::iterator it; + for (it = listcopy.begin(); it != listcopy.end(); it++){ + kill(( *it).first, SIGINT); + } + } + + waiting = 0; + while ( !plist.empty()){ + Util::sleep(500); + if (++waiting > 10){ + break; + } + } + + if ( !plist.empty()){ + std::map listcopy = plist; + std::map::iterator it; + for (it = listcopy.begin(); it != listcopy.end(); it++){ + kill(( *it).first, SIGKILL); + } + } + + waiting = 0; + while ( !plist.empty()){ + Util::sleep(100); + if (++waiting > 10){ + break; + } + } + +} + +/// 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){ @@ -107,14 +169,7 @@ pid_t Util::Procs::Start(std::string name, std::string cmd){ if (isActive(name)){ return getPid(name); } - if ( !handler_set){ - struct sigaction new_action; - new_action.sa_handler = Util::Procs::childsig_handler; - sigemptyset( &new_action.sa_mask); - new_action.sa_flags = 0; - sigaction(SIGCHLD, &new_action, NULL); - handler_set = true; - } + setHandler(); pid_t ret = fork(); if (ret == 0){ runCmd(cmd); @@ -143,14 +198,7 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2){ if (isActive(name)){ return getPid(name); } - if ( !handler_set){ - struct sigaction new_action; - new_action.sa_handler = Util::Procs::childsig_handler; - sigemptyset( &new_action.sa_mask); - new_action.sa_flags = 0; - sigaction(SIGCHLD, &new_action, NULL); - handler_set = true; - } + setHandler(); int pfildes[2]; if (pipe(pfildes) == -1){ #if DEBUG >= 1 @@ -220,15 +268,7 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2, st if (isActive(name)){ return getPid(name); } - if ( !handler_set){ - struct sigaction new_action; - new_action.sa_handler = Util::Procs::childsig_handler; - sigemptyset( &new_action.sa_mask); - new_action.sa_flags = 0; - sigaction(SIGCHLD, &new_action, NULL); - handler_set = true; - } - + setHandler(); int pfildes[2]; int pfildes2[2]; if (pipe(pfildes) == -1){ @@ -346,14 +386,7 @@ pid_t Util::Procs::StartPiped(std::string name, char * argv[], int * fdin, int * } pid_t pid; int pipein[2], pipeout[2], pipeerr[2]; - if ( !handler_set){ - struct sigaction new_action; - new_action.sa_handler = Util::Procs::childsig_handler; - sigemptyset( &new_action.sa_mask); - new_action.sa_flags = 0; - sigaction(SIGCHLD, &new_action, NULL); - handler_set = true; - } + setHandler(); if (fdin && *fdin == -1 && pipe(pipein) < 0){ #if DEBUG >= 1 std::cerr << "Pipe (in) creation failed for " << name << std::endl; diff --git a/lib/procs.h b/lib/procs.h index 6f0166e5..4a0343da 100644 --- a/lib/procs.h +++ b/lib/procs.h @@ -18,8 +18,10 @@ namespace Util { static std::map 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); - public: + static void setHandler(); + public: static pid_t Start(std::string name, std::string cmd); static pid_t Start(std::string name, std::string cmd, std::string cmd2); static pid_t Start(std::string name, std::string cmd, std::string cmd2, std::string cmd3); From 4a79862111cb5d9bd259f92b776f2e35be63b08d Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 21 Mar 2013 14:23:49 +0100 Subject: [PATCH 370/788] Some fixes for metadata rewriting in DTSC::File, optimized live fragmenter somewhat. --- lib/dtsc.cpp | 29 +++++++++-------------------- lib/dtsc.h | 2 ++ 2 files changed, 11 insertions(+), 20 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index e3b4a811..fb2606d0 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -323,26 +323,7 @@ void DTSC::Stream::updateHeaders(){ metadata["keylen"].append(buffers[keyframes[0].b]["time"].asInt() - buffers[keyframes[1].b]["time"].asInt()); long long int firstFragNo = -1; metadata["frags"].null(); - long long int currFrag = metadata["keytime"][0u].asInt() / 10000; - if (currFrag == 0){ - long long int fragLen = 1; - long long int fragDur = metadata["keylen"][0u].asInt(); - for (unsigned int j = 1; j < metadata["keytime"].size(); j++){ - if (metadata["keytime"][j].asInt() / 10000 > currFrag){ - if (firstFragNo == -1){ - firstFragNo = currFrag; - } - JSON::Value thisFrag; - thisFrag["num"] = metadata["keynum"][0u]; - thisFrag["len"] = fragLen; - thisFrag["dur"] = fragDur; - metadata["frags"].append(thisFrag); - break; - } - fragLen++; - fragDur += metadata["keylen"][j].asInt(); - } - } + long long int currFrag = -1; for (unsigned int i = 1; i < metadata["keytime"].size(); i++){ if (metadata["keytime"][i].asInt() / 10000 > currFrag){ currFrag = metadata["keytime"][i].asInt() / 10000; @@ -491,6 +472,11 @@ JSON::Value & DTSC::File::getMeta(){ return metadata; } +/// Returns the header metadata for this file as JSON::Value. +JSON::Value & DTSC::File::getFirstMeta(){ + return firstmetadata; +} + /// (Re)writes the given string to the header area if the size is the same as the existing header. /// Forces a write if force is set to true. bool DTSC::File::writeHeader(std::string & header, bool force){ @@ -565,6 +551,9 @@ void DTSC::File::readHeader(int pos){ return; } metadata = JSON::fromDTMI(strbuffer); + if (pos == 0){ + firstmetadata = metadata; + } //if there is another header, read it and replace metadata with that one. if (metadata.isMember("moreheader") && metadata["moreheader"].asInt() > 0){ readHeader(metadata["moreheader"].asInt()); diff --git a/lib/dtsc.h b/lib/dtsc.h index 64304346..cfa8bc08 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -69,6 +69,7 @@ namespace DTSC { File(std::string filename, bool create = false); ~File(); JSON::Value & getMeta(); + JSON::Value & getFirstMeta(); long long int getLastReadPos(); bool writeHeader(std::string & header, bool force = false); long long int addHeader(std::string & header); @@ -82,6 +83,7 @@ namespace DTSC { std::string strbuffer; JSON::Value jsonbuffer; JSON::Value metadata; + JSON::Value firstmetadata; std::map frames; std::map msframes; long long int currtime; From f61a28ecaa08e61c8413c68bb5ef1b1f2936a68d Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 22 Mar 2013 22:55:34 +0100 Subject: [PATCH 371/788] Fixed pretty major bug in MistBuffer causing frame lengths to go completely out of wack. --- lib/dtsc.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index fb2606d0..23eb9b53 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -314,6 +314,7 @@ void DTSC::Stream::updateHeaders(){ if (keyframes.size() > 2){ metadata["keytime"].shrink(keyframes.size() - 2); metadata["keynum"].shrink(keyframes.size() - 2); + metadata["keylen"].shrink(keyframes.size() - 2); metadata["keytime"].append(buffers[keyframes[1].b]["time"].asInt()); if (metadata["keynum"].size() == 0){ metadata["keynum"].append(1ll); From 2730aee68219861d771c6d000dba8119695d88ef Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 24 Mar 2013 20:18:00 +0100 Subject: [PATCH 372/788] Fixed buffer not properly allowing push source to restart an ongoing broadcast. --- lib/dtsc.cpp | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 23eb9b53..e4eda4db 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -312,15 +312,32 @@ void DTSC::Stream::dropRing(DTSC::Ring * ptr){ /// time than right after receiving a new keyframe, or there'll be raptors. void DTSC::Stream::updateHeaders(){ if (keyframes.size() > 2){ + if (buffers[keyframes[0].b]["time"].asInt() < buffers[keyframes[keyframes.size() - 1].b]["time"].asInt()){ + std::cerr << "Detected new video - resetting all buffers and metadata - hold on, this ride might get bumpy!" << std::endl; + keyframes.clear(); + buffers.clear(); + std::set::iterator sit; + if ( !rings.size()){ + return; + } + for (sit = rings.begin(); sit != rings.end(); sit++){ + ( *sit)->updated = true; + ( *sit)->b = 0; + ( *sit)->starved = true; + } + metadata.removeMember("keytime"); + metadata.removeMember("keynum"); + metadata.removeMember("keylen"); + metadata.removeMember("frags"); + metadata.removeMember("lastms"); + metadata.removeMember("missed_frags"); + return; + } metadata["keytime"].shrink(keyframes.size() - 2); metadata["keynum"].shrink(keyframes.size() - 2); metadata["keylen"].shrink(keyframes.size() - 2); metadata["keytime"].append(buffers[keyframes[1].b]["time"].asInt()); - if (metadata["keynum"].size() == 0){ - metadata["keynum"].append(1ll); - }else{ - metadata["keynum"].append(metadata["keynum"][metadata["keynum"].size() - 1].asInt() + 1); - } + metadata["keynum"].append(buffers[keyframes[1].b]["fragnum"].asInt()); metadata["keylen"].append(buffers[keyframes[0].b]["time"].asInt() - buffers[keyframes[1].b]["time"].asInt()); long long int firstFragNo = -1; metadata["frags"].null(); From 06f023f3df4fa808355f2fedd4b51247ccfa89d2 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 24 Mar 2013 20:18:22 +0100 Subject: [PATCH 373/788] Tweaked Util::Procs exit handler timings. --- lib/procs.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/procs.cpp b/lib/procs.cpp index a3d77075..810161b6 100644 --- a/lib/procs.cpp +++ b/lib/procs.cpp @@ -31,8 +31,8 @@ bool Util::Procs::handler_set = false; void Util::Procs::exit_handler(){ int waiting = 0; while ( !plist.empty()){ - Util::sleep(500); - if (++waiting > 5){ + Util::sleep(100); + if (++waiting > 25){ break; } } @@ -47,8 +47,8 @@ void Util::Procs::exit_handler(){ waiting = 0; while ( !plist.empty()){ - Util::sleep(500); - if (++waiting > 10){ + Util::sleep(200); + if (++waiting > 25){ break; } } From 370ebc49a217a76fbd827759f17e2125172b2480 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 25 Mar 2013 10:48:19 +0100 Subject: [PATCH 374/788] Fixed some potential segfaults in DTSC library. --- lib/dtsc.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index e4eda4db..b26defeb 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -317,13 +317,12 @@ void DTSC::Stream::updateHeaders(){ keyframes.clear(); buffers.clear(); std::set::iterator sit; - if ( !rings.size()){ - return; - } - for (sit = rings.begin(); sit != rings.end(); sit++){ - ( *sit)->updated = true; - ( *sit)->b = 0; - ( *sit)->starved = true; + if (rings.size()){ + for (sit = rings.begin(); sit != rings.end(); sit++){ + ( *sit)->updated = true; + ( *sit)->b = 0; + ( *sit)->starved = true; + } } metadata.removeMember("keytime"); metadata.removeMember("keynum"); @@ -331,6 +330,7 @@ void DTSC::Stream::updateHeaders(){ metadata.removeMember("frags"); metadata.removeMember("lastms"); metadata.removeMember("missed_frags"); + metadata.toPacked(); return; } metadata["keytime"].shrink(keyframes.size() - 2); From de46eee3b9f9ce6fe080335505bf79a582981c04 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 25 Mar 2013 16:24:18 +0100 Subject: [PATCH 375/788] Fixed audio-only livestreams causing unbounded buffers. --- lib/dtsc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index b26defeb..3a7bfa37 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -258,7 +258,7 @@ void DTSC::Stream::advanceRings(){ }while (repeat); } static int fragNum = 1; - if ((lastType() == VIDEO) && (buffers.front().isMember("keyframe"))){ + if ((lastType() == VIDEO && buffers.front().isMember("keyframe")) || (!metadata.isMember("video") && lastType() == AUDIO)){ keyframes.push_front(DTSC::Ring(0)); if ( !buffers.front().isMember("fragnum")){ buffers.front()["fragnum"] = fragNum++; From e1f7d2acea48a951e2b682824955731e3a4dd7cf Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 25 Mar 2013 19:40:21 +0100 Subject: [PATCH 376/788] Fixed reading FLV files taking 100% CPU. --- lib/flv_tag.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index 5ce1006d..d6cf6839 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -4,6 +4,7 @@ #include "amf.h" #include "rtmpchunks.h" #include "flv_tag.h" +#include "timing.h" #include //for Tag::FileLoader #include //for Tag::FileLoader #include //for Tag::FileLoader @@ -913,6 +914,8 @@ bool FLV::Tag::FileLoader(FILE * f){ Error_Str = "Invalid header received."; return false; } + }else{ + Util::sleep(100);//sleep 100ms } }else{ //if a tag header, calculate length and read tag body @@ -933,6 +936,8 @@ bool FLV::Tag::FileLoader(FILE * f){ } done = false; } + }else{ + Util::sleep(100);//sleep 100ms } }else{ //read tag body @@ -947,6 +952,8 @@ bool FLV::Tag::FileLoader(FILE * f){ sofar = 0; fcntl(fileno(f), F_SETFL, preflags); return true; + }else{ + Util::sleep(100);//sleep 100ms } } fcntl(fileno(f), F_SETFL, preflags); From ea9abcceb69b21aae2ab75453e41ae8403ff0a6b Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 25 Mar 2013 19:49:00 +0100 Subject: [PATCH 377/788] When no video channel is present, inject a seek time at most every ~2000ms. --- lib/dtsc.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 3a7bfa37..7d2ad220 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -258,11 +258,13 @@ void DTSC::Stream::advanceRings(){ }while (repeat); } static int fragNum = 1; - if ((lastType() == VIDEO && buffers.front().isMember("keyframe")) || (!metadata.isMember("video") && lastType() == AUDIO)){ + static unsigned int lastkeytime = 4242; + if ((lastType() == VIDEO && buffers.front().isMember("keyframe")) || (!metadata.isMember("video") && buffers.front()["time"].asInt() / 2000 != lastkeytime)){ keyframes.push_front(DTSC::Ring(0)); if ( !buffers.front().isMember("fragnum")){ buffers.front()["fragnum"] = fragNum++; } + lastkeytime = buffers.front()["time"].asInt() / 2000; } unsigned int timeBuffered = 0; if (keyframes.size() > 1){ From 97974409a672acf4fc402fcdda7112f8d0e62149 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 25 Mar 2013 22:30:00 +0100 Subject: [PATCH 378/788] Added FLV-based metadata channel support (Dynamic and HTTP Progressive FLV only, for now), both input and output. --- lib/flv_tag.cpp | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index d6cf6839..39d78cf9 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -360,6 +360,7 @@ FLV::Tag & FLV::Tag::operator=(const FLV::Tag& O){ /// FLV loader function from DTSC. /// Takes the DTSC data and makes it into FLV. bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ + std::string meta_str; switch (S.lastType()){ case DTSC::VIDEO: len = S.lastData().length() + 16; @@ -377,9 +378,21 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ } } break; - case DTSC::META: - len = S.lastData().length() + 15; + case DTSC::META:{ + AMF::Object amfdata("root", AMF::AMF0_DDV_CONTAINER); + amfdata.addContent(AMF::Object("", "onMetaData")); + amfdata.addContent(AMF::Object("", AMF::AMF0_ECMA_ARRAY)); + for (JSON::ObjIter it = S.getPacket()["data"].ObjBegin(); it != S.getPacket()["data"].ObjEnd(); it++){ + if (it->second.asInt()){ + amfdata.getContentP(1)->addContent(AMF::Object(it->first, it->second.asInt(), AMF::AMF0_NUMBER)); + }else{ + amfdata.getContentP(1)->addContent(AMF::Object(it->first, it->second.asString(), AMF::AMF0_STRING)); + } + } + meta_str = amfdata.Pack(); + len = meta_str.length() + 15; break; + } default: //ignore all other types (there are currently no other types...) break; } @@ -457,7 +470,7 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ break; } case DTSC::META: - memcpy(data + 11, S.lastData().c_str(), S.lastData().length()); + memcpy(data + 11, meta_str.c_str(), meta_str.length()); break; default: break; @@ -1024,6 +1037,22 @@ JSON::Value FLV::Tag::toJSON(JSON::Value & metadata){ metadata["audio"]["channels"] = 1; } } + for (int i = 0; i < tmp->hasContent(); ++i){ + if (tmp->getContentP(i)->Indice() == "videocodecid" || tmp->getContentP(i)->Indice() == "audiocodecid" || tmp->getContentP(i)->Indice() == "width" || tmp->getContentP(i)->Indice() == "height" || tmp->getContentP(i)->Indice() == "framerate" || tmp->getContentP(i)->Indice() == "videodatarate" || tmp->getContentP(i)->Indice() == "audiodatarate" || tmp->getContentP(i)->Indice() == "audiosamplerate" || tmp->getContentP(i)->Indice() == "audiosamplesize" || tmp->getContentP(i)->Indice() == "audiochannels"){ + continue; + } + if (tmp->getContentP(i)->NumValue()){ + pack_out["data"][tmp->getContentP(i)->Indice()] = (long long)tmp->getContentP(i)->NumValue(); + }else{ + if (tmp->getContentP(i)->StrValue() != ""){ + pack_out["data"][tmp->getContentP(i)->Indice()] = tmp->getContentP(i)->StrValue(); + } + } + } + if (pack_out){ + pack_out["datatype"] = "meta"; + pack_out["time"] = tagTime(); + } } if ( !metadata.isMember("length")){ metadata["length"] = 0; From 35df73e04251014b903eca5a007c213e895b7440 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 26 Mar 2013 15:57:53 +0100 Subject: [PATCH 379/788] Fixed several buffer segfaults when multiple viewers are connected. --- lib/dtsc.cpp | 7 ++++--- lib/json.cpp | 41 ++++++++++++++++++++++++++--------------- lib/json.h | 1 + 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 7d2ad220..eb88eac1 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -126,6 +126,7 @@ bool DTSC::Stream::parsePacket(Socket::Buffer & buffer){ std::string wholepacket = buffer.remove(len + 8); metadata = JSON::fromDTMI((unsigned char*)wholepacket.c_str() + 8, len, i); metadata.removeMember("moreheader"); + metadata.netPrepare(); if ( !buffer.available(8)){ return false; } @@ -332,7 +333,7 @@ void DTSC::Stream::updateHeaders(){ metadata.removeMember("frags"); metadata.removeMember("lastms"); metadata.removeMember("missed_frags"); - metadata.toPacked(); + metadata.netPrepare(); return; } metadata["keytime"].shrink(keyframes.size() - 2); @@ -370,7 +371,7 @@ void DTSC::Stream::updateHeaders(){ metadata["lastms"] = buffers[keyframes[0].b]["time"].asInt(); metadata["buffer_window"] = (long long int)buffertime; metadata["live"] = true; - metadata.toPacked(); + metadata.netPrepare(); updateRingHeaders(); } } @@ -592,7 +593,7 @@ void DTSC::File::readHeader(int pos){ } } metadata["vod"] = true; - metadata.toPacked(); + metadata.netPrepare(); } /// Reads the packet available at the current file position. diff --git a/lib/json.cpp b/lib/json.cpp index 68596e90..e5f2d72e 100644 --- a/lib/json.cpp +++ b/lib/json.cpp @@ -464,13 +464,36 @@ std::string JSON::Value::toPacked(){ } return r; } -; //toPacked +/// Pre-packs any object-type JSON::Value to a std::string for transfer over the network, including proper DTMI header. +/// Non-object-types will print an error to stderr. +/// The internal buffer is guaranteed to be up-to-date after this function is called. +void JSON::Value::netPrepare(){ + if (myType != OBJECT){ + fprintf(stderr, "Error: Only objects may be NetPacked!\n"); + return; + } + std::string packed = toPacked(); + strVal.resize(packed.size() + 8); + //insert proper header for this type of data + if (isMember("datatype")){ + memcpy((void*)strVal.c_str(), "DTPD", 4); + }else{ + memcpy((void*)strVal.c_str(), "DTSC", 4); + } + //insert the packet length at bytes 4-7 + unsigned int size = htonl(packed.size()); + memcpy((void*)(strVal.c_str() + 4), (void*) &size, 4); + //copy the rest of the string + memcpy((void*)(strVal.c_str() + 8), packed.c_str(), packed.size()); +} + /// Packs any object-type JSON::Value to a std::string for transfer over the network, including proper DTMI header. /// Non-object-types will print an error to stderr and return an empty string. /// This function returns a reference to an internal buffer where the prepared data is kept. -/// The internal buffer is *not* made stale if any changes occur inside the object - subsequent calls to toPacked() will clear the buffer. +/// The internal buffer is *not* made stale if any changes occur inside the object - subsequent calls to toPacked() will clear the buffer, +/// calls to netPrepare will guarantee it is up-to-date. std::string & JSON::Value::toNetPacked(){ static std::string emptystring; //check if this is legal @@ -480,19 +503,7 @@ std::string & JSON::Value::toNetPacked(){ } //if sneaky storage doesn't contain correct data, re-calculate it if (strVal.size() == 0 || strVal[0] != 'D' || strVal[1] != 'T'){ - std::string packed = toPacked(); - strVal.resize(packed.size() + 8); - //insert proper header for this type of data - if (isMember("datatype")){ - memcpy((void*)strVal.c_str(), "DTPD", 4); - }else{ - memcpy((void*)strVal.c_str(), "DTSC", 4); - } - //insert the packet length at bytes 4-7 - unsigned int size = htonl(packed.size()); - memcpy((void*)(strVal.c_str() + 4), (void*) &size, 4); - //copy the rest of the string - memcpy((void*)(strVal.c_str() + 8), packed.c_str(), packed.size()); + netPrepare(); } return strVal; } diff --git a/lib/json.h b/lib/json.h index a2db4845..2f2e4e55 100644 --- a/lib/json.h +++ b/lib/json.h @@ -70,6 +70,7 @@ namespace JSON { Value & operator[](unsigned int i); //handy functions and others std::string toPacked(); + void netPrepare(); std::string & toNetPacked(); std::string toString(); std::string toPrettyString(int indentation = 0); From 3f7c1febc3c58f97e03349d8bae4eb56cac3f783 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sat, 30 Mar 2013 14:08:47 +0100 Subject: [PATCH 380/788] Added support for alternative-style FLV metadata. --- lib/flv_tag.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index 39d78cf9..fc8dada5 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -978,8 +978,15 @@ JSON::Value FLV::Tag::toJSON(JSON::Value & metadata){ if (data[0] == 0x12){ AMF::Object meta_in = AMF::parse((unsigned char*)data + 11, len - 15); - if (meta_in.getContentP(0) && (meta_in.getContentP(0)->StrValue() == "onMetaData") && meta_in.getContentP(1)){ - AMF::Object * tmp = meta_in.getContentP(1); + AMF::Object * tmp = 0; + if (meta_in.getContentP(1) && meta_in.getContentP(0) && (meta_in.getContentP(0)->StrValue() == "onMetaData")){ + tmp = meta_in.getContentP(1); + }else{ + if (meta_in.getContentP(2) && meta_in.getContentP(1) && (meta_in.getContentP(1)->StrValue() == "onMetaData")){ + tmp = meta_in.getContentP(2); + } + } + if (tmp){ if (tmp->getContentP("videocodecid")){ switch ((unsigned int)tmp->getContentP("videocodecid")->NumValue()){ case 2: From 014aa9777017d7c1b999f4c5dd67d9d5bdc29f85 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sat, 30 Mar 2013 22:16:50 +0100 Subject: [PATCH 381/788] Updated libmist version to 5.0.2 --- lib/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Makefile.am b/lib/Makefile.am index 0549f29f..5ef82b82 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,6 +1,6 @@ lib_LTLIBRARIES=libmist-1.0.la libmist_1_0_la_SOURCES=amf.h amf.cpp auth.h auth.cpp base64.h base64.cpp config.h config.cpp dtsc.h dtsc.cpp flv_tag.h flv_tag.cpp http_parser.h http_parser.cpp json.h json.cpp procs.h procs.cpp rtmpchunks.h rtmpchunks.cpp socket.h socket.cpp mp4.h mp4.cpp ftp.h ftp.cpp filesystem.h filesystem.cpp stream.h stream.cpp timing.h timing.cpp ts_packet.cpp ts_packet.h -libmist_1_0_la_LDFLAGS = -version-info 4:0:1 +libmist_1_0_la_LDFLAGS = -version-info 5:0:2 libmist_1_0_la_CPPFLAGS=$(DEPS_CFLAGS) $(global_CFLAGS) libmist_1_0_la_LIBADD=$(DEPS_LIBS) $(CLOCK_LIB) From 7565b705ad10fc67197d24eea6a75ada34553968 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 5 Apr 2013 16:36:29 +0200 Subject: [PATCH 382/788] Fixed memory leak in FLV library. --- lib/flv_tag.cpp | 23 +++++++++++++++++------ lib/flv_tag.h | 1 + 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index fc8dada5..3c8a1994 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -335,6 +335,16 @@ FLV::Tag::Tag(const RTMPStream::Chunk& O){ ChunkLoader(O); } +/// Generic destructor that frees the allocated memory in the internal data variable, if any. +FLV::Tag::~Tag(){ + if (data){ + free(data); + data = 0; + buf = 0; + len = 0; + } +} + /// Assignment operator - works exactly like the copy constructor. /// This operator checks for self-assignment. FLV::Tag & FLV::Tag::operator=(const FLV::Tag& O){ @@ -759,13 +769,14 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){ bool FLV::Tag::ChunkLoader(const RTMPStream::Chunk& O){ len = O.len + 15; if (len > 0){ - if ( !data){ - data = (char*)malloc(len); - buf = len; - }else{ - if (buf < len){ - data = (char*)realloc(data, len); + if (buf < len || !data){ + char * newdata = (char*)realloc(data, len); + // on realloc fail, retain the old data + if (newdata != 0){ + data = newdata; buf = len; + }else{ + return false; } } memcpy(data + 11, &(O.data[0]), O.len); diff --git a/lib/flv_tag.h b/lib/flv_tag.h index b4bb5198..5b4289a5 100644 --- a/lib/flv_tag.h +++ b/lib/flv_tag.h @@ -39,6 +39,7 @@ namespace FLV { 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); /// Date: Mon, 8 Apr 2013 16:25:58 +0200 Subject: [PATCH 383/788] Fixed FMLE RTMP ingest. --- lib/flv_tag.cpp | 157 +++++++++++++++++++----------------------------- lib/flv_tag.h | 2 + 2 files changed, 64 insertions(+), 95 deletions(-) diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index 3c8a1994..d1f1fbfa 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -115,6 +115,62 @@ bool FLV::Tag::isInitData(){ return false; } +const char * FLV::Tag::getVideoCodec(){ + switch (data[11] & 0x0F){ + case 1: + return "JPEG"; + case 2: + return "H263"; + case 3: + return "ScreenVideo1"; + case 4: + return "VP6"; + case 5: + return "VP6Alpha"; + case 6: + return "ScreenVideo2"; + case 7: + return "H264"; + default: + return "unknown"; + } +} + +const char * FLV::Tag::getAudioCodec(){ + switch (data[11] & 0xF0){ + case 0x00: + return "linear PCM PE"; + case 0x10: + return "ADPCM"; + case 0x20: + return "MP3"; + case 0x30: + return "linear PCM LE"; + case 0x40: + return "Nelly16kHz"; + case 0x50: + return "Nelly8kHz"; + case 0x60: + return "Nelly"; + case 0x70: + return "G711A-law"; + case 0x80: + return "G711mu-law"; + case 0x90: + return "reserved"; + case 0xA0: + return "AAC"; + case 0xB0: + return "Speex"; + case 0xE0: + return "MP38kHz"; + case 0xF0: + return "DeviceSpecific"; + default: + return "unknown"; + } +} + /// Returns a std::string describing the tag in detail. /// The string includes information about whether the tag is /// audio, video or metadata, what encoding is used, and the details @@ -124,33 +180,7 @@ std::string FLV::Tag::tagType(){ R << len << " bytes of "; switch (data[0]){ case 0x09: - switch (data[11] & 0x0F){ - case 1: - R << "JPEG"; - break; - case 2: - R << "H263"; - break; - case 3: - R << "ScreenVideo1"; - break; - case 4: - R << "VP6"; - break; - case 5: - R << "VP6Alpha"; - break; - case 6: - R << "ScreenVideo2"; - break; - case 7: - R << "H264"; - break; - default: - R << "unknown"; - break; - } - R << " video "; + R << getVideoCodec() << " video "; switch (data[11] & 0xF0){ case 0x10: R << "keyframe"; @@ -183,53 +213,7 @@ std::string FLV::Tag::tagType(){ } break; case 0x08: - switch (data[11] & 0xF0){ - case 0x00: - R << "linear PCM PE"; - break; - case 0x10: - R << "ADPCM"; - break; - case 0x20: - R << "MP3"; - break; - case 0x30: - R << "linear PCM LE"; - break; - case 0x40: - R << "Nelly16kHz"; - break; - case 0x50: - R << "Nelly8kHz"; - break; - case 0x60: - R << "Nelly"; - break; - case 0x70: - R << "G711A-law"; - break; - case 0x80: - R << "G711mu-law"; - break; - case 0x90: - R << "reserved"; - break; - case 0xA0: - R << "AAC"; - break; - case 0xB0: - R << "Speex"; - break; - case 0xE0: - R << "MP38kHz"; - break; - case 0xF0: - R << "DeviceSpecific"; - break; - default: - R << "unknown"; - break; - } + R << getAudioCodec(); switch (data[11] & 0x0C){ case 0x0: R << " 5.5kHz"; @@ -1109,15 +1093,8 @@ JSON::Value FLV::Tag::toJSON(JSON::Value & metadata){ } pack_out["datatype"] = "audio"; pack_out["time"] = tagTime(); - if ( !metadata["audio"].isMember("codec") || metadata["audio"]["size"].asString() == ""){ - switch (audiodata & 0xF0){ - case 0x20: - metadata["audio"]["codec"] = "MP3"; - break; - case 0xA0: - metadata["audio"]["codec"] = "AAC"; - break; - } + if ( !metadata["audio"].isMember("codec") || metadata["audio"]["codec"].asString() == "?" || metadata["audio"]["codec"].asString() == ""){ + metadata["audio"]["codec"] = getAudioCodec(); } if ( !metadata["audio"].isMember("rate") || metadata["audio"]["rate"].asInt() < 1){ switch (audiodata & 0x0C){ @@ -1184,18 +1161,8 @@ JSON::Value FLV::Tag::toJSON(JSON::Value & metadata){ } return pack_out; //skip rest of parsing, get next tag. } - if ( !metadata["video"].isMember("codec") || metadata["video"]["codec"].asString() == ""){ - switch (videodata & 0x0F){ - case 2: - metadata["video"]["codec"] = "H263"; - break; - case 4: - metadata["video"]["codec"] = "VP6"; - break; - case 7: - metadata["video"]["codec"] = "H264"; - break; - } + if ( !metadata["video"].isMember("codec") || metadata["video"]["codec"].asString() == "?" || metadata["video"]["codec"].asString() == ""){ + metadata["video"]["codec"] = getVideoCodec(); } pack_out["datatype"] = "video"; switch (videodata & 0xF0){ diff --git a/lib/flv_tag.h b/lib/flv_tag.h index 5b4289a5..e8ccb88d 100644 --- a/lib/flv_tag.h +++ b/lib/flv_tag.h @@ -32,6 +32,8 @@ namespace FLV { 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(); ///< Returns the 32-bit timestamp of this tag. void tagTime(unsigned int T); ///< Sets the 32-bit timestamp of this tag. From 54a19d60e6bc5dc79488c0ee9d2ec2f44785670e Mon Sep 17 00:00:00 2001 From: Thulinma Date: Wed, 10 Apr 2013 22:18:12 +0200 Subject: [PATCH 384/788] Added setBlocking to Socket::Server. --- lib/socket.cpp | 7 +++++++ lib/socket.h | 1 + 2 files changed, 8 insertions(+) diff --git a/lib/socket.cpp b/lib/socket.cpp index 34647692..703aed67 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -855,6 +855,13 @@ Socket::Connection Socket::Server::accept(bool nonblock){ return tmp; } +/// Set this socket to be blocking (true) or nonblocking (false). +void Socket::Server::setBlocking(bool blocking){ + if (sock >= 0){ + setFDBlocking(sock, blocking); + } +} + /// Close connection. The internal socket is closed and then set to -1. /// If the connection is already closed, nothing happens. void Socket::Server::close(){ diff --git a/lib/socket.h b/lib/socket.h index 03aec9c2..227cf018 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -109,6 +109,7 @@ namespace Socket { 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. void close(); ///< Close connection. int getSocket(); ///< Returns internal socket number. From 71b918b7aa2144baa31630b98c528a043268c953 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 11 Apr 2013 14:34:30 +0200 Subject: [PATCH 385/788] Make all FLV memory management go through a single function - further robustifies FLV library. --- lib/flv_tag.cpp | 114 +++++++++++++++++++++--------------------------- lib/flv_tag.h | 1 + 2 files changed, 50 insertions(+), 65 deletions(-) diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index d1f1fbfa..096c362e 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -294,11 +294,11 @@ FLV::Tag::Tag(){ FLV::Tag::Tag(const Tag& O){ done = true; sofar = 0; - buf = O.len; - len = buf; + len = O.len; if (len > 0){ - data = (char*)malloc(len); - memcpy(data, O.data, len); + if (checkBufferSize()){ + memcpy(data, O.data, len); + } }else{ data = 0; } @@ -335,16 +335,11 @@ FLV::Tag & FLV::Tag::operator=(const FLV::Tag& O){ if (this != &O){ //no self-assignment len = O.len; if (len > 0){ - if ( !data){ - data = (char*)malloc(len); - buf = len; + if (checkBufferSize()){ + memcpy(data, O.data, len); }else{ - if (buf < len){ - data = (char*)realloc(data, len); - buf = len; - } + len = buf; } - memcpy(data, O.data, len); } isKeyframe = O.isKeyframe; } @@ -391,14 +386,8 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ break; } if (len > 0){ - if ( !data){ - data = (char*)malloc(len); - buf = len; - }else{ - if (buf < len){ - data = (char*)realloc(data, len); - buf = len; - } + if ( !checkBufferSize()){ + return false; } switch (S.lastType()){ case DTSC::VIDEO: @@ -519,14 +508,8 @@ bool FLV::Tag::DTSCVideoInit(DTSC::Stream & S){ len = S.metadata["video"]["init"].asString().length() + 20; } if (len > 0){ - if ( !data){ - data = (char*)malloc(len); - buf = len; - }else{ - if (buf < len){ - data = (char*)realloc(data, len); - buf = len; - } + if ( !checkBufferSize()){ + return false; } memcpy(data + 16, S.metadata["video"]["init"].asString().c_str(), len - 20); data[12] = 0; //H264 sequence header @@ -560,14 +543,8 @@ bool FLV::Tag::DTSCAudioInit(DTSC::Stream & S){ len = S.metadata["audio"]["init"].asString().length() + 17; } if (len > 0){ - if ( !data){ - data = (char*)malloc(len); - buf = len; - }else{ - if (buf < len){ - data = (char*)realloc(data, len); - buf = len; - } + if ( !checkBufferSize()){ + return false; } memcpy(data + 13, S.metadata["audio"]["init"].asString().c_str(), len - 17); data[12] = 0; //AAC sequence header @@ -725,16 +702,11 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){ std::string tmp = amfdata.Pack(); len = tmp.length() + 15; if (len > 0){ - if ( !data){ - data = (char*)malloc(len); - buf = len; + if (checkBufferSize()){ + memcpy(data + 11, tmp.c_str(), len - 15); }else{ - if (buf < len){ - data = (char*)realloc(data, len); - buf = len; - } + return false; } - memcpy(data + 11, tmp.c_str(), len - 15); } setLen(); data[0] = 0x12; @@ -753,15 +725,8 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){ bool FLV::Tag::ChunkLoader(const RTMPStream::Chunk& O){ len = O.len + 15; if (len > 0){ - if (buf < len || !data){ - char * newdata = (char*)realloc(data, len); - // on realloc fail, retain the old data - if (newdata != 0){ - data = newdata; - buf = len; - }else{ - return false; - } + if ( !checkBufferSize()){ + return false; } memcpy(data + 11, &(O.data[0]), O.len); } @@ -812,9 +777,11 @@ bool FLV::Tag::MemReadUntil(char * buffer, unsigned int count, unsigned int & so /// \param P The current position in the data buffer. Will be updated to reflect new position. /// \return True if a whole tag is succesfully read, false otherwise. bool FLV::Tag::MemLoader(char * D, unsigned int S, unsigned int & P){ - if (buf < 15){ - data = (char*)realloc(data, 15); - buf = 15; + if (len < 15){ + len = 15; + } + if ( !checkBufferSize()){ + return false; } if (done){ //read a header @@ -836,9 +803,8 @@ bool FLV::Tag::MemLoader(char * D, unsigned int S, unsigned int & P){ len = data[3] + 15; len += (data[2] << 8); len += (data[1] << 16); - if (buf < len){ - data = (char*)realloc(data, len); - buf = len; + if ( !checkBufferSize()){ + return false; } if (data[0] > 0x12){ data[0] += 32; @@ -903,9 +869,10 @@ bool FLV::Tag::FileLoader(FILE * f){ int preflags = fcntl(fileno(f), F_GETFL, 0); int postflags = preflags | O_NONBLOCK; fcntl(fileno(f), F_SETFL, postflags); - if (buf < 15){ - data = (char*)realloc(data, 15); - buf = 15; + + if (len < 15){len = 15;} + if ( !checkBufferSize()){ + return false; } if (done){ @@ -930,9 +897,8 @@ bool FLV::Tag::FileLoader(FILE * f){ len = data[3] + 15; len += (data[2] << 8); len += (data[1] << 16); - if (buf < len){ - data = (char*)realloc(data, len); - buf = len; + if ( !checkBufferSize()){ + return false; } if (data[0] > 0x12){ data[0] += 32; @@ -1209,3 +1175,21 @@ JSON::Value FLV::Tag::toJSON(JSON::Value & metadata){ } return pack_out; //should never get here } //FLV::Tag::toJSON + +/// Checks if buf is large enough to contain len. +/// Attempts to resize data buffer if not/ +/// \returns True if buffer is large enough, false otherwise. +bool FLV::Tag::checkBufferSize(){ + if (buf < len || !data){ + char * newdata = (char*)realloc(data, len); + // on realloc fail, retain the old data + if (newdata != 0){ + data = newdata; + buf = len; + }else{ + len = buf; + return false; + } + } + return true; +} diff --git a/lib/flv_tag.h b/lib/flv_tag.h index e8ccb88d..5959982c 100644 --- a/lib/flv_tag.h +++ b/lib/flv_tag.h @@ -56,6 +56,7 @@ namespace FLV { 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); From 61246062fb5a40ca5e8645c822d61e5dce99bdf0 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sat, 13 Apr 2013 13:45:42 +0200 Subject: [PATCH 386/788] Fixed / improved fragmenter. --- lib/dtsc.cpp | 44 +++++++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index eb88eac1..1937fd66 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -342,32 +342,54 @@ void DTSC::Stream::updateHeaders(){ metadata["keytime"].append(buffers[keyframes[1].b]["time"].asInt()); metadata["keynum"].append(buffers[keyframes[1].b]["fragnum"].asInt()); metadata["keylen"].append(buffers[keyframes[0].b]["time"].asInt() - buffers[keyframes[1].b]["time"].asInt()); - long long int firstFragNo = -1; - metadata["frags"].null(); - long long int currFrag = -1; - for (unsigned int i = 1; i < metadata["keytime"].size(); i++){ - if (metadata["keytime"][i].asInt() / 10000 > currFrag){ - currFrag = metadata["keytime"][i].asInt() / 10000; + unsigned int fragStart = 0; + if ( !metadata["frags"]){ + // this means that if we have < ~10 seconds in the buffer, fragmenting goes horribly wrong. + if ( !metadata.isMember("missed_frags")){ + metadata["missed_frags"] = 0ll; + } + }else{ + // delete fragments of which the beginning can no longer be reached + while (metadata["frags"][0u]["num"].asInt() < metadata["keynum"][0u].asInt()){ + metadata["frags"].shrink(metadata["frags"].size() - 1); + // increase the missed fragments counter + metadata["missed_frags"] = metadata["missed_frags"].asInt() + 1; + } + if (metadata["frags"].size() > 0){ + // set oldestFrag to the first keynum outside any current fragment + long long unsigned int oldestFrag = metadata["frags"][metadata["frags"].size() - 1]["num"].asInt() + metadata["frags"][metadata["frags"].size() - 1]["len"].asInt(); + // seek fragStart to the first keynum >= oldestFrag + while (metadata["keynum"][fragStart].asInt() < oldestFrag){ + fragStart++; + } + } + } + for (unsigned int i = fragStart; i < metadata["keytime"].size(); i++){ + if (i == fragStart){ + long long int currFrag = metadata["keytime"][i].asInt() / 10000; long long int fragLen = 1; long long int fragDur = metadata["keylen"][i].asInt(); for (unsigned int j = i + 1; j < metadata["keytime"].size(); j++){ - if (metadata["keytime"][j].asInt() / 10000 > currFrag){ - if (firstFragNo == -1){ - firstFragNo = currFrag; - } + // if we are now 10+ seconds, finish the fragment + if (fragDur >= 10000){ + // construct and append the fragment JSON::Value thisFrag; thisFrag["num"] = metadata["keynum"][i]; thisFrag["len"] = fragLen; thisFrag["dur"] = fragDur; metadata["frags"].append(thisFrag); + // next fragment starts fragLen fragments up + fragStart += fragLen; + // skip that many - no unneeded looping + i += fragLen - 1; break; } + // otherwise, +1 the length and add up the duration fragLen++; fragDur += metadata["keylen"][j].asInt(); } } } - metadata["missed_frags"] = firstFragNo; metadata["lastms"] = buffers[keyframes[0].b]["time"].asInt(); metadata["buffer_window"] = (long long int)buffertime; metadata["live"] = true; From 2705af401226a5f62871369e20eb1713a0b05baa Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 15 Apr 2013 10:45:56 +0200 Subject: [PATCH 387/788] Fixed memory corruption in optionless binaries. --- lib/config.cpp | 60 ++++++++++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/lib/config.cpp b/lib/config.cpp index 0bfafb79..c083d5ec 100644 --- a/lib/config.cpp +++ b/lib/config.cpp @@ -28,7 +28,7 @@ std::string Util::Config::libver = PACKAGE_VERSION; /// Creates a new configuration manager. Util::Config::Config(std::string cmd, std::string version){ vals.null(); - long_count = 0; + long_count = 2; vals["cmd"]["value"].append(cmd); vals["version"]["long"] = "version"; vals["version"]["short"] = "v"; @@ -173,38 +173,40 @@ void Util::Config::parseArgs(int argc, char ** argv){ struct option * longOpts = (struct option*)calloc(long_count + 1, sizeof(struct option)); int long_i = 0; int arg_count = 0; - 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 (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("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; + 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++; } - 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; + 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++; } - 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(); + 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(); + } } } } From b9613149e02f98234030dd419e37c55ae25dd43a Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 15 Apr 2013 11:43:55 +0200 Subject: [PATCH 388/788] Updated version to 5.1.2 --- lib/Makefile.am | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Makefile.am b/lib/Makefile.am index 5ef82b82..0c43b95a 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,6 +1,6 @@ lib_LTLIBRARIES=libmist-1.0.la libmist_1_0_la_SOURCES=amf.h amf.cpp auth.h auth.cpp base64.h base64.cpp config.h config.cpp dtsc.h dtsc.cpp flv_tag.h flv_tag.cpp http_parser.h http_parser.cpp json.h json.cpp procs.h procs.cpp rtmpchunks.h rtmpchunks.cpp socket.h socket.cpp mp4.h mp4.cpp ftp.h ftp.cpp filesystem.h filesystem.cpp stream.h stream.cpp timing.h timing.cpp ts_packet.cpp ts_packet.h -libmist_1_0_la_LDFLAGS = -version-info 5:0:2 +libmist_1_0_la_LDFLAGS = -version-info 5:1:2 libmist_1_0_la_CPPFLAGS=$(DEPS_CFLAGS) $(global_CFLAGS) libmist_1_0_la_LIBADD=$(DEPS_LIBS) $(CLOCK_LIB) @@ -9,3 +9,4 @@ pkgconfig_DATA = mist-1.0.pc library_includedir=$(includedir)/mist-1.0/mist library_include_HEADERS = amf.h auth.h base64.h config.h dtsc.h flv_tag.h http_parser.h json.h procs.h rtmpchunks.h socket.h mp4.h ftp.h filesystem.h stream.h timing.h nal.h ts_packet.h + From f5cbe7343da2c79c6381472c6666c5339df76b46 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Tue, 23 Apr 2013 14:39:52 +0200 Subject: [PATCH 389/788] Tweaked pretty printing in several cases. --- lib/json.cpp | 18 +++++++++++------- lib/mp4.cpp | 11 ++++++----- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/lib/json.cpp b/lib/json.cpp index e5f2d72e..dc6b3da2 100644 --- a/lib/json.cpp +++ b/lib/json.cpp @@ -581,11 +581,11 @@ std::string JSON::Value::toPrettyString(int indentation){ } case ARRAY: { if (arrVal.size() > 0){ - std::string tmp = "[\n"; + std::string tmp = "[\n" + std::string(indentation + 2, ' '); for (ArrIter it = ArrBegin(); it != ArrEnd(); it++){ - tmp += std::string(indentation + 2, ' ') + it->toPrettyString(indentation + 2); + tmp += it->toPrettyString(indentation + 2); if (it + 1 != ArrEnd()){ - tmp += ",\n"; + tmp += ", "; } } tmp += "\n" + std::string(indentation, ' ') + "]"; @@ -597,17 +597,21 @@ std::string JSON::Value::toPrettyString(int indentation){ } case OBJECT: { if (objVal.size() > 0){ - std::string tmp2 = "{\n"; + bool shortMode = false; + if (size() <= 3 && isMember("len")){ + shortMode = true; + } + std::string tmp2 = "{" + std::string((shortMode ? "" : "\n")); ObjIter it3 = ObjEnd(); --it3; for (ObjIter it2 = ObjBegin(); it2 != ObjEnd(); it2++){ - tmp2 += std::string(indentation + 2, ' ') + "\"" + it2->first + "\":"; + tmp2 += (shortMode ? std::string("") : std::string(indentation + 2, ' ')) + "\"" + it2->first + "\":"; tmp2 += it2->second.toPrettyString(indentation + 2); if (it2 != it3){ - tmp2 += ",\n"; + tmp2 += "," + std::string((shortMode ? " " : "\n")); } } - tmp2 += "\n" + std::string(indentation, ' ') + "}"; + tmp2 += (shortMode ? std::string("") : "\n" + std::string(indentation, ' ')) + "}"; return tmp2; }else{ return "{}"; diff --git a/lib/mp4.cpp b/lib/mp4.cpp index c94dacbb..3b9cfce3 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -1490,20 +1490,21 @@ namespace MP4 { r << std::string(indent + 1, ' ') << "SampleInformation (" << getSampleInformationCount() << "):" << std::endl; for (int i = 0; i < getSampleInformationCount(); ++i){ - r << std::string(indent + 2, ' ') << "[" << i << "]" << std::endl; + r << std::string(indent + 2, ' ') << "[" << i << "] "; trunSampleInformation samp = getSampleInformation(i); if (flags & trunsampleDuration){ - r << std::string(indent + 2, ' ') << "Duration " << samp.sampleDuration << std::endl; + r << "Duration=" << samp.sampleDuration << " "; } if (flags & trunsampleSize){ - r << std::string(indent + 2, ' ') << "Size " << samp.sampleSize << std::endl; + r << "Size=" << samp.sampleSize << " "; } if (flags & trunsampleFlags){ - r << std::string(indent + 2, ' ') << "Flags " << prettySampleFlags(samp.sampleFlags) << std::endl; + r << "Flags=" << prettySampleFlags(samp.sampleFlags) << " "; } if (flags & trunsampleOffsets){ - r << std::string(indent + 2, ' ') << "Offset " << samp.sampleOffset << std::endl; + r << "Offset=" << samp.sampleOffset << " "; } + r << std::endl; } return r.str(); From 62c4689ae2cc5f1d017edbe790ac800fb54951b2 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 21 May 2013 13:21:07 +0200 Subject: [PATCH 390/788] Fixed a potential threading problem in DTSC::Stream. --- lib/dtsc.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 1937fd66..b2f3455f 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -189,6 +189,10 @@ std::string & DTSC::Stream::lastData(){ /// Returns the packet in this buffer number. /// \arg num Buffer number. JSON::Value & DTSC::Stream::getPacket(unsigned int num){ + static JSON::Value empty; + if (num >= buffers.size()){ + return empty; + } return buffers[num]; } From 9228198097ab6b51bfc0c4bac249ac7587a9feeb Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 3 Jun 2013 15:50:21 +0200 Subject: [PATCH 391/788] Fixed a FLV header bug causing both video and audio to always be mentioned even when not present, added header-only parsing support to HTTP library. --- lib/flv_tag.cpp | 4 ++-- lib/http_parser.cpp | 4 ++++ lib/http_parser.h | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index 096c362e..f7f04c79 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -587,11 +587,11 @@ bool FLV::Tag::DTSCAudioInit(DTSC::Stream & S){ /// Assumes metadata is available - so check before calling! bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){ //Unknown? Assume AAC. - if (S.metadata["audio"]["codec"].asString() == "?"){ + if (S.metadata.isMember("audio") && S.metadata["audio"]["codec"].asString() == "?"){ S.metadata["audio"]["codec"] = "AAC"; } //Unknown? Assume H264. - if (S.metadata["video"]["codec"].asString() == "?"){ + if (S.metadata.isMember("video") && S.metadata["video"]["codec"].asString() == "?"){ S.metadata["video"]["codec"] = "H264"; } diff --git a/lib/http_parser.cpp b/lib/http_parser.cpp index 1dea052f..460cb2b7 100644 --- a/lib/http_parser.cpp +++ b/lib/http_parser.cpp @@ -6,6 +6,7 @@ /// 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(); } @@ -217,6 +218,9 @@ bool HTTP::Parser::parse(std::string & HTTPbuffer){ } if (seenHeaders){ if (length > 0){ + if (headerOnly){ + return true; + } unsigned int toappend = length - body.length(); if (toappend > 0){ body.append(HTTPbuffer, 0, toappend); diff --git a/lib/http_parser.h b/lib/http_parser.h index 300edf56..bf753190 100644 --- a/lib/http_parser.h +++ b/lib/http_parser.h @@ -33,6 +33,7 @@ namespace HTTP { std::string url; std::string protocol; unsigned int length; + bool headerOnly; ///< If true, do not parse body if the length is a known size. private: bool seenHeaders; bool seenReq; From 4db81af9906637198f2d572cecfe734bc91d9186 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Wed, 17 Apr 2013 13:29:30 +0200 Subject: [PATCH 392/788] Added an isFixed member to the output of the header. --- lib/dtsc.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index b2f3455f..bb846123 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -601,8 +601,12 @@ void DTSC::File::readHeader(int pos){ if (pos == 0){ firstmetadata = metadata; } + if (fread(buffer, 4, 1, F) != 1){ + metadata["isFixed"] = true; + } //if there is another header, read it and replace metadata with that one. if (metadata.isMember("moreheader") && metadata["moreheader"].asInt() > 0){ + metadata.removeMember("isFixed"); readHeader(metadata["moreheader"].asInt()); return; } From a7de3bf08f171f72439cfdae151236de1485f4b3 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Wed, 24 Apr 2013 10:25:34 +0200 Subject: [PATCH 393/788] Configuration parser bug fixed. --- lib/config.cpp | 2 +- lib/config.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/config.cpp b/lib/config.cpp index c083d5ec..829aa1b4 100644 --- a/lib/config.cpp +++ b/lib/config.cpp @@ -167,7 +167,7 @@ void Util::Config::printHelp(std::ostream & output){ /// Parses commandline arguments. /// Calls exit if an unknown option is encountered, printing a help message. -void Util::Config::parseArgs(int argc, char ** argv){ +void 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)); diff --git a/lib/config.h b/lib/config.h index c776c21b..8ed0fb5d 100644 --- a/lib/config.h +++ b/lib/config.h @@ -27,7 +27,7 @@ namespace Util { Config(std::string cmd, std::string version); void addOption(std::string optname, JSON::Value option); void printHelp(std::ostream & output); - void parseArgs(int argc, char ** argv); + void 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); From 2c343b9db6a8d3c760eda5c543a03a9cdef4548f Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Thu, 25 Apr 2013 10:52:37 +0200 Subject: [PATCH 394/788] Added initial DTSCv2 support --- lib/dtsc.cpp | 114 ++++++++++++++++++++++++++++++++++++++++++++++++--- lib/dtsc.h | 1 + lib/json.cpp | 41 ++++++++++++++++-- 3 files changed, 147 insertions(+), 9 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index bb846123..3d0a64e0 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -7,6 +7,7 @@ #include //for htonl/ntohl char DTSC::Magic_Header[] = "DTSC"; char DTSC::Magic_Packet[] = "DTPD"; +char DTSC::Magic_Packet2[] = "DTP2"; /// Initializes a DTSC::Stream with only one packet buffer. DTSC::Stream::Stream(){ @@ -92,6 +93,49 @@ bool DTSC::Stream::parsePacket(std::string & buffer){ syncing = false; return true; } + if (memcmp(buffer.c_str(), DTSC::Magic_Packet2, 4) == 0){ + len = ntohl(((uint32_t *)buffer.c_str())[1]); + if (buffer.length() < len + 20){ + return false; + } + buffers.push_front(JSON::Value()); + unsigned int i = 0; + long long int tmpTrackID = ntohl(((int*)(buffer.c_str() + 8))[0]); + long long int tmpTime = ntohl(((int*)(buffer.c_str() + 12))[0]); + tmpTime << 32; + tmpTime += ntohl(((int*)(buffer.c_str() + 16))[0]); + buffers.front() = JSON::fromDTMI((unsigned char*)buffer.c_str() + 20, len, i); + buffers.front()["time"] = tmpTime; + buffers.front()["trackid"] = tmpTrackID; + datapointertype = INVALID; + if (buffers.front().isMember("data")){ + datapointer = &(buffers.front()["data"].strVal); + }else{ + datapointer = 0; + } + if (buffers.front().isMember("datatype")){ + std::string tmp = buffers.front()["datatype"].asString(); + if (tmp == "video"){ + datapointertype = VIDEO; + } + if (tmp == "audio"){ + datapointertype = AUDIO; + } + if (tmp == "meta"){ + datapointertype = META; + } + if (tmp == "pause_marker"){ + datapointertype = PAUSEMARK; + } + } + buffer.erase(0, len + 20); + while (buffers.size() > buffercount){ + buffers.pop_back(); + } + advanceRings(); + syncing = false; + return true; + } #if DEBUG >= 2 if (!syncing){ std::cerr << "Error: Invalid DTMI data detected - re-syncing" << std::endl; @@ -99,10 +143,15 @@ bool DTSC::Stream::parsePacket(std::string & buffer){ } #endif size_t magic_search = buffer.find(Magic_Packet); - if (magic_search == std::string::npos){ - buffer.clear(); + size_t magic_search2 = buffer.find(Magic_Packet2); + if (magic_search2 == std::string::npos){ + if (magic_search == std::string::npos){ + buffer.clear(); + }else{ + buffer.erase(0, magic_search); + } }else{ - buffer.erase(0, magic_search); + buffer.erase(0, magic_search2); } } return false; @@ -169,6 +218,49 @@ bool DTSC::Stream::parsePacket(Socket::Buffer & buffer){ syncing = false; return true; } + if (memcmp(header_bytes.c_str(), DTSC::Magic_Packet2, 4) == 0){ + len = ntohl(((uint32_t *)header_bytes.c_str())[1]); + if ( !buffer.available(len + 20)){ + return false; + } + buffers.push_front(JSON::Value()); + unsigned int i = 0; + std::string wholepacket = buffer.remove(len + 20); + long long int tmpTrackID = ntohl(((int*)(wholepacket.c_str() + 8))[0]); + long long int tmpTime = ntohl(((int*)(wholepacket.c_str() + 12))[0]); + tmpTime << 32; + tmpTime += ntohl(((int*)(wholepacket.c_str() + 16))[0]); + buffers.front() = JSON::fromDTMI((unsigned char*)wholepacket.c_str() + 20, len, i); + buffers.front()["time"] = tmpTime; + buffers.front()["trackid"] = tmpTrackID; + datapointertype = INVALID; + if (buffers.front().isMember("data")){ + datapointer = &(buffers.front()["data"].strVal); + }else{ + datapointer = 0; + } + if (buffers.front().isMember("datatype")){ + std::string tmp = buffers.front()["datatype"].asString(); + if (tmp == "video"){ + datapointertype = VIDEO; + } + if (tmp == "audio"){ + datapointertype = AUDIO; + } + if (tmp == "meta"){ + datapointertype = META; + } + if (tmp == "pause_marker"){ + datapointertype = PAUSEMARK; + } + } + while (buffers.size() > buffercount){ + buffers.pop_back(); + } + advanceRings(); + syncing = false; + return true; + } #if DEBUG >= 2 if (!syncing){ std::cerr << "Error: Invalid DTMI data detected - syncing" << std::endl; @@ -648,8 +740,15 @@ void DTSC::File::seekNext(){ jsonbuffer = metadata; return; } - if (memcmp(buffer, DTSC::Magic_Packet, 4) != 0){ - fprintf(stderr, "Invalid header - %.4s != %.4s\n", buffer, DTSC::Magic_Packet); + long long unsigned int version = 0; + if (memcmp(buffer, DTSC::Magic_Packet, 4) == 0){ + version = 1; + } + if (memcmp(buffer, DTSC::Magic_Packet2, 4) == 0){ + version = 2; + } + if (version == 0){ + fprintf(stderr, "Invalid header - %.4s != %.4s\n", buffer, DTSC::Magic_Packet2); strbuffer = ""; jsonbuffer.null(); return; @@ -661,7 +760,7 @@ void DTSC::File::seekNext(){ return; } uint32_t * ubuffer = (uint32_t *)buffer; - long packSize = ntohl(ubuffer[0]); + long packSize = ntohl(ubuffer[0]) + (version == 2 ? 12 : 0); strbuffer.resize(packSize); if (fread((void*)strbuffer.c_str(), packSize, 1, F) != 1){ fprintf(stderr, "Could not read packet\n"); @@ -669,6 +768,9 @@ void DTSC::File::seekNext(){ jsonbuffer.null(); return; } + if (version == 2){ + strbuffer.erase(0,12); + } jsonbuffer = JSON::fromDTMI(strbuffer); if (jsonbuffer.isMember("keyframe")){ if (frames[currframe] != lastreadpos){ diff --git a/lib/dtsc.h b/lib/dtsc.h index cfa8bc08..cb9d392e 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -62,6 +62,7 @@ namespace DTSC { 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 /// A simple wrapper class that will open a file and allow easy reading/writing of DTSC data from/to it. class File{ diff --git a/lib/json.cpp b/lib/json.cpp index dc6b3da2..0b3db6fc 100644 --- a/lib/json.cpp +++ b/lib/json.cpp @@ -475,18 +475,53 @@ void JSON::Value::netPrepare(){ return; } std::string packed = toPacked(); - strVal.resize(packed.size() + 8); //insert proper header for this type of data + int packID = -1; + long long unsigned int time = objVal["time"].asInt(); + std::string dataType = objVal["datatype"].asString(); if (isMember("datatype")){ - memcpy((void*)strVal.c_str(), "DTPD", 4); + if (isMember("trackid")){ + packID = objVal["trackid"].asInt(); + }else{ + if (objVal["datatype"].asString() == "video"){ + packID = 1; + } + if (objVal["datatype"].asString() == "audio"){ + packID = 2; + } + if (objVal["datatype"].asString() == "meta"){ + packID = 3; + } + if (packID == -1){ + packID = 0; + } + } + removeMember("time"); + removeMember("datatype"); + packed = toPacked(); + objVal["time"] = (long long int)time; + objVal["datatype"] = dataType; + strVal.resize(packed.size() + 20); + memcpy((void*)strVal.c_str(), "DTP2", 4); }else{ + strVal.resize(packed.size() + 8); memcpy((void*)strVal.c_str(), "DTSC", 4); } //insert the packet length at bytes 4-7 unsigned int size = htonl(packed.size()); memcpy((void*)(strVal.c_str() + 4), (void*) &size, 4); //copy the rest of the string - memcpy((void*)(strVal.c_str() + 8), packed.c_str(), packed.size()); + if (packID != -1){ + packID = htonl((int)packID); + memcpy((void*)(strVal.c_str() + 8), (void*) &packID, 4); + int tmpHalf = htonl((int)(time >> 32)); + memcpy((void*)(strVal.c_str() + 12), (void*) &tmpHalf, 4); + tmpHalf = htonl((int)(time & 0xFFFFFFFF)); + memcpy((void*)(strVal.c_str() + 16), (void*) &tmpHalf, 4); + memcpy((void*)(strVal.c_str() + 20), packed.c_str(), packed.size()); + }else{ + memcpy((void*)(strVal.c_str() + 8), packed.c_str(), packed.size()); + } } /// Packs any object-type JSON::Value to a std::string for transfer over the network, including proper DTMI header. From f14a4fb75eeac42bf4843a7de857d43e6d3e602e Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Mon, 29 Apr 2013 10:41:20 +0200 Subject: [PATCH 395/788] Updated flv_tag to output DTSCv2 --- .gitignore | 1 + lib/dtsc.cpp | 5 ++- lib/flv_tag.cpp | 98 ++++++++++++++++++++++++------------------------- lib/json.cpp | 21 +++++++++-- lib/json.h | 1 + 5 files changed, 72 insertions(+), 54 deletions(-) diff --git a/.gitignore b/.gitignore index bbbb44fb..0b3b45e4 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,4 @@ libtool *.flv *.json *.pc +*.swp diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 3d0a64e0..9b209996 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -769,9 +769,10 @@ void DTSC::File::seekNext(){ return; } if (version == 2){ - strbuffer.erase(0,12); + jsonbuffer = JSON::fromDTMI2(strbuffer); + }else{ + jsonbuffer = JSON::fromDTMI(strbuffer); } - jsonbuffer = JSON::fromDTMI(strbuffer); if (jsonbuffer.isMember("keyframe")){ if (frames[currframe] != lastreadpos){ currframe++; diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index f7f04c79..c866da49 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -951,58 +951,58 @@ JSON::Value FLV::Tag::toJSON(JSON::Value & metadata){ if (tmp->getContentP("videocodecid")){ switch ((unsigned int)tmp->getContentP("videocodecid")->NumValue()){ case 2: - metadata["video"]["codec"] = "H263"; + metadata["tracks"]["track1"]["codec"] = "H263"; break; case 4: - metadata["video"]["codec"] = "VP6"; + metadata["tracks"]["track1"]["codec"] = "VP6"; break; case 7: - metadata["video"]["codec"] = "H264"; + metadata["tracks"]["track1"]["codec"] = "H264"; break; default: - metadata["video"]["codec"] = "?"; + metadata["tracks"]["track1"]["codec"] = "?"; break; } } if (tmp->getContentP("audiocodecid")){ switch ((unsigned int)tmp->getContentP("audiocodecid")->NumValue()){ case 2: - metadata["audio"]["codec"] = "MP3"; + metadata["tracks"]["track2"]["codec"] = "MP3"; break; case 10: - metadata["audio"]["codec"] = "AAC"; + metadata["tracks"]["track2"]["codec"] = "AAC"; break; default: - metadata["audio"]["codec"] = "?"; + metadata["tracks"]["track2"]["codec"] = "?"; break; } } if (tmp->getContentP("width")){ - metadata["video"]["width"] = (long long int)tmp->getContentP("width")->NumValue(); + metadata["tracks"]["track1"]["width"] = (long long int)tmp->getContentP("width")->NumValue(); } if (tmp->getContentP("height")){ - metadata["video"]["height"] = (long long int)tmp->getContentP("height")->NumValue(); + metadata["tracks"]["track1"]["height"] = (long long int)tmp->getContentP("height")->NumValue(); } if (tmp->getContentP("framerate")){ - metadata["video"]["fpks"] = (long long int)(tmp->getContentP("framerate")->NumValue() * 1000.0); + metadata["tracks"]["track1"]["fpks"] = (long long int)(tmp->getContentP("framerate")->NumValue() * 1000.0); } if (tmp->getContentP("videodatarate")){ - metadata["video"]["bps"] = (long long int)(tmp->getContentP("videodatarate")->NumValue() * 1024) / 8; + metadata["tracks"]["track1"]["bps"] = (long long int)(tmp->getContentP("videodatarate")->NumValue() * 1024) / 8; } if (tmp->getContentP("audiodatarate")){ - metadata["audio"]["bps"] = (long long int)(tmp->getContentP("audiodatarate")->NumValue() * 1024) / 8; + metadata["tracks"]["track2"]["bps"] = (long long int)(tmp->getContentP("audiodatarate")->NumValue() * 1024) / 8; } if (tmp->getContentP("audiosamplerate")){ - metadata["audio"]["rate"] = (long long int)tmp->getContentP("audiosamplerate")->NumValue(); + metadata["tracks"]["track2"]["rate"] = (long long int)tmp->getContentP("audiosamplerate")->NumValue(); } if (tmp->getContentP("audiosamplesize")){ - metadata["audio"]["size"] = (long long int)tmp->getContentP("audiosamplesize")->NumValue(); + metadata["tracks"]["track2"]["size"] = (long long int)tmp->getContentP("audiosamplesize")->NumValue(); } if (tmp->getContentP("stereo")){ if (tmp->getContentP("stereo")->NumValue() == 1){ - metadata["audio"]["channels"] = 2; + metadata["tracks"]["track2"]["channels"] = 2; }else{ - metadata["audio"]["channels"] = 1; + metadata["tracks"]["track2"]["channels"] = 1; } } for (int i = 0; i < tmp->hasContent(); ++i){ @@ -1022,27 +1022,27 @@ JSON::Value FLV::Tag::toJSON(JSON::Value & metadata){ pack_out["time"] = tagTime(); } } - if ( !metadata.isMember("length")){ - metadata["length"] = 0; + if ( !metadata["tracks"]["track1"].isMember("length")){ + metadata["tracks"]["track1"]["length"] = 0; } - if (metadata.isMember("video")){ - if ( !metadata["video"].isMember("width")){ - metadata["video"]["width"] = 0; + if (metadata["tracks"].isMember("track1")){ + if ( !metadata["tracks"]["track1"].isMember("width")){ + metadata["tracks"]["track1"]["width"] = 0; } - if ( !metadata["video"].isMember("height")){ - metadata["video"]["height"] = 0; + if ( !metadata["tracks"]["track1"].isMember("height")){ + metadata["tracks"]["track1"]["height"] = 0; } - if ( !metadata["video"].isMember("fpks")){ - metadata["video"]["fpks"] = 0; + if ( !metadata["tracks"]["track1"].isMember("fpks")){ + metadata["tracks"]["track1"]["fpks"] = 0; } - if ( !metadata["video"].isMember("bps")){ - metadata["video"]["bps"] = 0; + if ( !metadata["tracks"]["track1"].isMember("bps")){ + metadata["tracks"]["track1"]["bps"] = 0; } - if ( !metadata["video"].isMember("keyms")){ - metadata["video"]["keyms"] = 0; + if ( !metadata["tracks"]["track1"].isMember("keyms")){ + metadata["tracks"]["track1"]["keyms"] = 0; } - if ( !metadata["video"].isMember("keyvar")){ - metadata["video"]["keyvar"] = 0; + if ( !metadata["tracks"]["track1"].isMember("keyvar")){ + metadata["tracks"]["track1"]["keyvar"] = 0; } } return pack_out; //empty @@ -1051,50 +1051,50 @@ JSON::Value FLV::Tag::toJSON(JSON::Value & metadata){ char audiodata = data[11]; if (needsInitData() && isInitData()){ if ((audiodata & 0xF0) == 0xA0){ - metadata["audio"]["init"] = std::string((char*)data + 13, (size_t)len - 17); + metadata["tracks"]["track2"]["init"] = std::string((char*)data + 13, (size_t)len - 17); }else{ - metadata["audio"]["init"] = std::string((char*)data + 12, (size_t)len - 16); + metadata["tracks"]["track2"]["init"] = std::string((char*)data + 12, (size_t)len - 16); } return pack_out; //skip rest of parsing, get next tag. } pack_out["datatype"] = "audio"; pack_out["time"] = tagTime(); - if ( !metadata["audio"].isMember("codec") || metadata["audio"]["codec"].asString() == "?" || metadata["audio"]["codec"].asString() == ""){ + if ( !metadata["tracks"]["track2"].isMember("codec") || metadata["tracks"]["track2"]["codec"].asString() == "?" || metadata["tracks"]["track2"]["codec"].asString() == ""){ metadata["audio"]["codec"] = getAudioCodec(); } - if ( !metadata["audio"].isMember("rate") || metadata["audio"]["rate"].asInt() < 1){ + if ( !metadata["tracks"]["track2"].isMember("rate") || metadata["tracks"]["track2"]["rate"].asInt() < 1){ switch (audiodata & 0x0C){ case 0x0: - metadata["audio"]["rate"] = 5512; + metadata["tracks"]["track2"]["rate"] = 5512; break; case 0x4: - metadata["audio"]["rate"] = 11025; + metadata["tracks"]["track2"]["rate"] = 11025; break; case 0x8: - metadata["audio"]["rate"] = 22050; + metadata["tracks"]["track2"]["rate"] = 22050; break; case 0xC: - metadata["audio"]["rate"] = 44100; + metadata["tracks"]["track2"]["rate"] = 44100; break; } } - if ( !metadata["audio"].isMember("size") || metadata["audio"]["size"].asInt() < 1){ + if ( !metadata["tracks"]["track2"].isMember("size") || metadata["tracks"]["track2"]["size"].asInt() < 1){ switch (audiodata & 0x02){ case 0x0: - metadata["audio"]["size"] = 8; + metadata["tracks"]["track2"]["size"] = 8; break; case 0x2: - metadata["audio"]["size"] = 16; + metadata["tracks"]["track2"]["size"] = 16; break; } } - if ( !metadata["audio"].isMember("channels") || metadata["audio"]["channels"].asInt() < 1){ + if ( !metadata["tracks"]["track2"].isMember("channels") || metadata["tracks"]["track2"]["channels"].asInt() < 1){ switch (audiodata & 0x01){ case 0x0: - metadata["audio"]["channels"] = 1; + metadata["tracks"]["track2"]["channels"] = 1; break; case 0x1: - metadata["audio"]["channels"] = 2; + metadata["tracks"]["track2"]["channels"] = 2; break; } } @@ -1118,17 +1118,17 @@ JSON::Value FLV::Tag::toJSON(JSON::Value & metadata){ if (len < 21){ return JSON::Value(); } - metadata["video"]["init"] = std::string((char*)data + 16, (size_t)len - 20); + metadata["tracks"]["track1"]["init"] = std::string((char*)data + 16, (size_t)len - 20); }else{ if (len < 17){ return JSON::Value(); } - metadata["video"]["init"] = std::string((char*)data + 12, (size_t)len - 16); + metadata["tracks"]["track1"]["init"] = std::string((char*)data + 12, (size_t)len - 16); } return pack_out; //skip rest of parsing, get next tag. } - if ( !metadata["video"].isMember("codec") || metadata["video"]["codec"].asString() == "?" || metadata["video"]["codec"].asString() == ""){ - metadata["video"]["codec"] = getVideoCodec(); + if ( !metadata["tracks"]["track1"].isMember("codec") || metadata["tracks"]["track1"]["codec"].asString() == "?" || metadata["tracks"]["track1"]["codec"].asString() == ""){ + metadata["tracks"]["track1"]["codec"] = getVideoCodec(); } pack_out["datatype"] = "video"; switch (videodata & 0xF0){ diff --git a/lib/json.cpp b/lib/json.cpp index 0b3db6fc..d61111a3 100644 --- a/lib/json.cpp +++ b/lib/json.cpp @@ -478,11 +478,12 @@ void JSON::Value::netPrepare(){ //insert proper header for this type of data int packID = -1; long long unsigned int time = objVal["time"].asInt(); - std::string dataType = objVal["datatype"].asString(); + std::string dataType; if (isMember("datatype")){ + dataType = objVal["datatype"].asString(); if (isMember("trackid")){ packID = objVal["trackid"].asInt(); - }else{ + }else{ if (objVal["datatype"].asString() == "video"){ packID = 1; } @@ -498,12 +499,15 @@ void JSON::Value::netPrepare(){ } removeMember("time"); removeMember("datatype"); + removeMember("trackid"); packed = toPacked(); objVal["time"] = (long long int)time; objVal["datatype"] = dataType; + objVal["trackid"] = packID; strVal.resize(packed.size() + 20); memcpy((void*)strVal.c_str(), "DTP2", 4); }else{ + packID = -1; strVal.resize(packed.size() + 8); memcpy((void*)strVal.c_str(), "DTSC", 4); } @@ -512,7 +516,7 @@ void JSON::Value::netPrepare(){ memcpy((void*)(strVal.c_str() + 4), (void*) &size, 4); //copy the rest of the string if (packID != -1){ - packID = htonl((int)packID); + packID = htonl(packID); memcpy((void*)(strVal.c_str() + 8), (void*) &packID, 4); int tmpHalf = htonl((int)(time >> 32)); memcpy((void*)(strVal.c_str() + 12), (void*) &tmpHalf, 4); @@ -863,3 +867,14 @@ JSON::Value JSON::fromDTMI(std::string data){ unsigned int i = 0; return fromDTMI((const unsigned char*)data.c_str(), data.size(), i); } //fromDTMI + +JSON::Value JSON::fromDTMI2(std::string data){ + JSON::Value tmp = fromDTMI(data.substr(12)); + long long int tmpTrackID = ntohl(((int*)(data.c_str()))[0]); + long long int tmpTime = ntohl(((int*)(data.c_str() + 4))[0]); + tmpTime << 32; + tmpTime += ntohl(((int*)(data.c_str() + 8))[0]); + tmp["time"] = tmpTime; + tmp["trackid"] = tmpTrackID; + return tmp; +} diff --git a/lib/json.h b/lib/json.h index 2f2e4e55..f54e7384 100644 --- a/lib/json.h +++ b/lib/json.h @@ -93,6 +93,7 @@ namespace JSON { void null(); }; + Value fromDTMI2(std::string data); Value fromDTMI(std::string data); Value fromDTMI(const unsigned char * data, unsigned int len, unsigned int &i); Value fromString(std::string json); From d2597101fc4fb9b0e71f45374c1b19bc9bb64930 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Tue, 30 Apr 2013 10:35:36 +0200 Subject: [PATCH 396/788] Optimizations in packet parsing --- lib/dtsc.cpp | 114 +++++++++++---------------------------------------- 1 file changed, 24 insertions(+), 90 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 9b209996..b31ac59f 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -56,14 +56,26 @@ bool DTSC::Stream::parsePacket(std::string & buffer){ return false; } } + int version = 0; if (memcmp(buffer.c_str(), DTSC::Magic_Packet, 4) == 0){ + version = 1; + } + if (memcmp(buffer.c_str(), DTSC::Magic_Packet2, 4) == 0){ + version = 2; + } + if (version){ len = ntohl(((uint32_t *)buffer.c_str())[1]); if (buffer.length() < len + 8){ return false; } buffers.push_front(JSON::Value()); unsigned int i = 0; - buffers.front() = JSON::fromDTMI((unsigned char*)buffer.c_str() + 8, len, i); + if (version == 1){ + buffers.front() = JSON::fromDTMI((unsigned char*)buffer.c_str() + 8, len, i); + } + if (version == 2){ + buffers.front() = JSON::fromDTMI2(buffer.substr(8)); + } datapointertype = INVALID; if (buffers.front().isMember("data")){ datapointer = &(buffers.front()["data"].strVal); @@ -93,49 +105,6 @@ bool DTSC::Stream::parsePacket(std::string & buffer){ syncing = false; return true; } - if (memcmp(buffer.c_str(), DTSC::Magic_Packet2, 4) == 0){ - len = ntohl(((uint32_t *)buffer.c_str())[1]); - if (buffer.length() < len + 20){ - return false; - } - buffers.push_front(JSON::Value()); - unsigned int i = 0; - long long int tmpTrackID = ntohl(((int*)(buffer.c_str() + 8))[0]); - long long int tmpTime = ntohl(((int*)(buffer.c_str() + 12))[0]); - tmpTime << 32; - tmpTime += ntohl(((int*)(buffer.c_str() + 16))[0]); - buffers.front() = JSON::fromDTMI((unsigned char*)buffer.c_str() + 20, len, i); - buffers.front()["time"] = tmpTime; - buffers.front()["trackid"] = tmpTrackID; - datapointertype = INVALID; - if (buffers.front().isMember("data")){ - datapointer = &(buffers.front()["data"].strVal); - }else{ - datapointer = 0; - } - if (buffers.front().isMember("datatype")){ - std::string tmp = buffers.front()["datatype"].asString(); - if (tmp == "video"){ - datapointertype = VIDEO; - } - if (tmp == "audio"){ - datapointertype = AUDIO; - } - if (tmp == "meta"){ - datapointertype = META; - } - if (tmp == "pause_marker"){ - datapointertype = PAUSEMARK; - } - } - buffer.erase(0, len + 20); - while (buffers.size() > buffercount){ - buffers.pop_back(); - } - advanceRings(); - syncing = false; - return true; - } #if DEBUG >= 2 if (!syncing){ std::cerr << "Error: Invalid DTMI data detected - re-syncing" << std::endl; @@ -181,7 +150,14 @@ bool DTSC::Stream::parsePacket(Socket::Buffer & buffer){ } header_bytes = buffer.copy(8); } + int version = 0; if (memcmp(header_bytes.c_str(), DTSC::Magic_Packet, 4) == 0){ + version = 1; + } + if (memcmp(header_bytes.c_str(), DTSC::Magic_Packet2, 4) == 0){ + version = 2; + } + if (version){ len = ntohl(((uint32_t *)header_bytes.c_str())[1]); if ( !buffer.available(len + 8)){ return false; @@ -189,50 +165,12 @@ bool DTSC::Stream::parsePacket(Socket::Buffer & buffer){ buffers.push_front(JSON::Value()); unsigned int i = 0; std::string wholepacket = buffer.remove(len + 8); - buffers.front() = JSON::fromDTMI((unsigned char*)wholepacket.c_str() + 8, len, i); - datapointertype = INVALID; - if (buffers.front().isMember("data")){ - datapointer = &(buffers.front()["data"].strVal); - }else{ - datapointer = 0; + if (version == 1){ + buffers.front() = JSON::fromDTMI((unsigned char*)wholepacket.c_str() + 8, len, i); } - if (buffers.front().isMember("datatype")){ - std::string tmp = buffers.front()["datatype"].asString(); - if (tmp == "video"){ - datapointertype = VIDEO; - } - if (tmp == "audio"){ - datapointertype = AUDIO; - } - if (tmp == "meta"){ - datapointertype = META; - } - if (tmp == "pause_marker"){ - datapointertype = PAUSEMARK; - } + if (version == 2){ + buffers.front() = JSON::fromDTMI2(wholepacket.substr(8)); } - while (buffers.size() > buffercount){ - buffers.pop_back(); - } - advanceRings(); - syncing = false; - return true; - } - if (memcmp(header_bytes.c_str(), DTSC::Magic_Packet2, 4) == 0){ - len = ntohl(((uint32_t *)header_bytes.c_str())[1]); - if ( !buffer.available(len + 20)){ - return false; - } - buffers.push_front(JSON::Value()); - unsigned int i = 0; - std::string wholepacket = buffer.remove(len + 20); - long long int tmpTrackID = ntohl(((int*)(wholepacket.c_str() + 8))[0]); - long long int tmpTime = ntohl(((int*)(wholepacket.c_str() + 12))[0]); - tmpTime << 32; - tmpTime += ntohl(((int*)(wholepacket.c_str() + 16))[0]); - buffers.front() = JSON::fromDTMI((unsigned char*)wholepacket.c_str() + 20, len, i); - buffers.front()["time"] = tmpTime; - buffers.front()["trackid"] = tmpTrackID; datapointertype = INVALID; if (buffers.front().isMember("data")){ datapointer = &(buffers.front()["data"].strVal); @@ -693,12 +631,8 @@ void DTSC::File::readHeader(int pos){ if (pos == 0){ firstmetadata = metadata; } - if (fread(buffer, 4, 1, F) != 1){ - metadata["isFixed"] = true; - } //if there is another header, read it and replace metadata with that one. if (metadata.isMember("moreheader") && metadata["moreheader"].asInt() > 0){ - metadata.removeMember("isFixed"); readHeader(metadata["moreheader"].asInt()); return; } From 404798141a32cd362ff6756bd754f1bc3d2ca008 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Tue, 30 Apr 2013 10:35:59 +0200 Subject: [PATCH 397/788] Updated the flv to json operation. --- lib/flv_tag.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index c866da49..69041804 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -1022,6 +1022,8 @@ JSON::Value FLV::Tag::toJSON(JSON::Value & metadata){ pack_out["time"] = tagTime(); } } + metadata["tracks"]["track1"]["trackid"] = 1; + metadata["tracks"]["track1"]["type"] = "video"; if ( !metadata["tracks"]["track1"].isMember("length")){ metadata["tracks"]["track1"]["length"] = 0; } @@ -1059,6 +1061,8 @@ JSON::Value FLV::Tag::toJSON(JSON::Value & metadata){ } pack_out["datatype"] = "audio"; pack_out["time"] = tagTime(); + metadata["tracks"]["track2"]["trackid"] = 2; + metadata["tracks"]["track2"]["type"] = "audio"; if ( !metadata["tracks"]["track2"].isMember("codec") || metadata["tracks"]["track2"]["codec"].asString() == "?" || metadata["tracks"]["track2"]["codec"].asString() == ""){ metadata["audio"]["codec"] = getAudioCodec(); } From c7a058960e8e2902c239866aaab6a1d31f7bcf78 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Wed, 1 May 2013 09:31:31 +0200 Subject: [PATCH 398/788] Fixed a minor bug in DTP2 Output --- lib/json.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/json.cpp b/lib/json.cpp index d61111a3..1322c739 100644 --- a/lib/json.cpp +++ b/lib/json.cpp @@ -479,7 +479,7 @@ void JSON::Value::netPrepare(){ int packID = -1; long long unsigned int time = objVal["time"].asInt(); std::string dataType; - if (isMember("datatype")){ + if (isMember("datatype") || isMember("trackid")){ dataType = objVal["datatype"].asString(); if (isMember("trackid")){ packID = objVal["trackid"].asInt(); From 59d03dcf48822c955076f3545afa7672fec311a2 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Sat, 4 May 2013 12:20:55 +0200 Subject: [PATCH 399/788] Fixed a bug in parsing configuration values. --- lib/config.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/config.cpp b/lib/config.cpp index 829aa1b4..245cca8d 100644 --- a/lib/config.cpp +++ b/lib/config.cpp @@ -248,6 +248,8 @@ void Util::Config::parseArgs(int & argc, char ** & argv){ break; } } + optind++; + long_i++; } if (long_i <= arg_count){ std::cerr << "Usage error: missing argument(s)." << std::endl; From c4fecd7e3d3e7e2487b7413075ac28dacce31f83 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Sat, 4 May 2013 12:21:59 +0200 Subject: [PATCH 400/788] Updated DTSC::File, added appending new packets, added copy constructor in order to keep valid file descriptors open. --- lib/dtsc.cpp | 81 +++++++++++++++++++++++++++++++++++++++++++++++----- lib/dtsc.h | 10 +++++++ 2 files changed, 84 insertions(+), 7 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index b31ac59f..3c4862e9 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -507,6 +507,35 @@ DTSC::Stream::~Stream(){ } } +DTSC::File::File(){ + F = 0; +} + +DTSC::File::File(const File & rhs){ + *this = rhs; +} + +DTSC::File & DTSC::File::operator =(const File & rhs){ + created = rhs.created; + if (rhs.F){ + int tmpFd = fileno(rhs.F); + int newFd = dup(tmpFd); + F = fdopen( newFd, (created ? "w+b": "r+b")); + }else{ + F = 0; + } + strbuffer = rhs.strbuffer; + jsonbuffer = rhs.jsonbuffer; + metadata = rhs.metadata; + firstmetadata = rhs.firstmetadata; + frames = rhs.frames; + msframes = rhs.msframes; + currtime = rhs.currtime; + lastreadpos = rhs.lastreadpos; + headerSize = rhs.headerSize; + memcpy(buffer, rhs.buffer, 4); +} + /// Open a filename for DTSC reading/writing. /// If create is true and file does not exist, attempt to create. DTSC::File::File(std::string filename, bool create){ @@ -521,6 +550,7 @@ DTSC::File::File(std::string filename, bool create){ }else{ F = fopen(filename.c_str(), "r+b"); } + created = create; if ( !F){ fprintf(stderr, "Could not open file %s\n", filename.c_str()); return; @@ -562,6 +592,12 @@ bool DTSC::File::writeHeader(std::string & header, bool force){ return false; } headerSize = header.size(); + int pSize = htonl(header.size()); + fseek(F, 4, SEEK_SET); + int tmpret = fwrite((void*)( &pSize), 4, 1, F); + if (tmpret != 1){ + return false; + } fseek(F, 8, SEEK_SET); int ret = fwrite(header.c_str(), headerSize, 1, F); fseek(F, 8 + headerSize, SEEK_SET); @@ -621,13 +657,15 @@ void DTSC::File::readHeader(int pos){ uint32_t * ubuffer = (uint32_t *)buffer; long packSize = ntohl(ubuffer[0]); strbuffer.resize(packSize); - if (fread((void*)strbuffer.c_str(), packSize, 1, F) != 1){ - fprintf(stderr, "Could not read packet (H%i)\n", pos); - strbuffer = ""; - metadata.null(); - return; + if (packSize){ + if (fread((void*)strbuffer.c_str(), packSize, 1, F) != 1){ + fprintf(stderr, "Could not read packet (H%i)\n", pos); + strbuffer = ""; + metadata.null(); + return; + } + metadata = JSON::fromDTMI(strbuffer); } - metadata = JSON::fromDTMI(strbuffer); if (pos == 0){ firstmetadata = metadata; } @@ -652,6 +690,19 @@ void DTSC::File::readHeader(int pos){ metadata.netPrepare(); } +long int DTSC::File::getBytePosEOF(){ + fseek(F, 0, SEEK_END); + return ftell(F); +} + +long int DTSC::File::getBytePos(){ + return ftell(F); +} + +bool DTSC::File::reachedEOF(){ + return feof(F); +} + /// Reads the packet available at the current file position. /// If the packet could not be read for any reason, the reason is printed to stderr. /// Reading the packet means the file position is increased to the next packet. @@ -682,7 +733,7 @@ void DTSC::File::seekNext(){ version = 2; } if (version == 0){ - fprintf(stderr, "Invalid header - %.4s != %.4s\n", buffer, DTSC::Magic_Packet2); + fprintf(stderr, "Invalid packet header @ %#x - %.4s != %.4s\n", getBytePos(), buffer, DTSC::Magic_Packet2); strbuffer = ""; jsonbuffer.null(); return; @@ -807,6 +858,22 @@ bool DTSC::File::seek_time(int ms){ return false; } +bool DTSC::File::seek_bpos(int bpos){ + if (fseek(F, bpos, SEEK_SET) == 0){ + return true; + } + return false; +} + +void DTSC::File::writePacket(std::string & newPacket){ + fseek(F, 0, SEEK_END); + fwrite(newPacket.c_str(), newPacket.size(), 1, F); //write contents +} + +void DTSC::File::writePacket(JSON::Value & newPacket){ + writePacket(newPacket.toNetPacked()); +} + /// Close the file if open DTSC::File::~File(){ if (F){ diff --git a/lib/dtsc.h b/lib/dtsc.h index cb9d392e..e60b311e 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -67,18 +67,27 @@ namespace DTSC { /// 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); ~File(); JSON::Value & getMeta(); JSON::Value & getFirstMeta(); 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(); std::string & getPacket(); JSON::Value & getJSON(); bool seek_frame(int frameno); bool seek_time(int seconds); + bool seek_bpos(int bpos); + void writePacket(std::string & newPacket); + void writePacket(JSON::Value & newPacket); private: void readHeader(int pos); std::string strbuffer; @@ -93,6 +102,7 @@ namespace DTSC { FILE * F; unsigned long headerSize; char buffer[4]; + bool created; }; //FileWriter From 23813cc9b28fb569b0dcd80ffbd0a950bf4db90d Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Tue, 7 May 2013 11:23:19 +0200 Subject: [PATCH 401/788] Fixed a few small bugs regarding the output of DTSCv2 --- lib/dtsc.cpp | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 3c4862e9..d623c0f6 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -43,19 +43,6 @@ bool DTSC::Stream::parsePacket(std::string & buffer){ uint32_t len; static bool syncing = false; if (buffer.length() > 8){ - if (memcmp(buffer.c_str(), DTSC::Magic_Header, 4) == 0){ - len = ntohl(((uint32_t *)buffer.c_str())[1]); - if (buffer.length() < len + 8){ - return false; - } - unsigned int i = 0; - metadata = JSON::fromDTMI((unsigned char*)buffer.c_str() + 8, len, i); - metadata.removeMember("moreheader"); - buffer.erase(0, len + 8); - if (buffer.length() <= 8){ - return false; - } - } int version = 0; if (memcmp(buffer.c_str(), DTSC::Magic_Packet, 4) == 0){ version = 1; @@ -63,6 +50,23 @@ bool DTSC::Stream::parsePacket(std::string & buffer){ if (memcmp(buffer.c_str(), DTSC::Magic_Packet2, 4) == 0){ version = 2; } + if (memcmp(buffer.c_str(), DTSC::Magic_Header, 4) == 0){ + len = ntohl(((uint32_t *)buffer.c_str())[1]); + if (buffer.length() < len + 8){ + return false; + } + unsigned int i = 0; + if (version == 1){ + metadata = JSON::fromDTMI((unsigned char*)buffer.c_str() + 8, len, i); + }else{ + metadata = JSON::fromDTMI2(buffer.substr(8)); + } + metadata.removeMember("moreheader"); + buffer.erase(0, len + 8); + if (buffer.length() <= 8){ + return false; + } + } if (version){ len = ntohl(((uint32_t *)buffer.c_str())[1]); if (buffer.length() < len + 8){ From c9f37c57b5d065b497a5eb05e0c25ea51800ea9f Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Mon, 13 May 2013 09:25:49 +0200 Subject: [PATCH 402/788] Upgraded DTSC Lib to do seeking through selected tracks rather than a list of keyframes. --- lib/dtsc.cpp | 91 ++++++++++++++++++---------------------------------- lib/dtsc.h | 6 ++-- 2 files changed, 35 insertions(+), 62 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index d623c0f6..15e07e20 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -532,8 +532,6 @@ DTSC::File & DTSC::File::operator =(const File & rhs){ jsonbuffer = rhs.jsonbuffer; metadata = rhs.metadata; firstmetadata = rhs.firstmetadata; - frames = rhs.frames; - msframes = rhs.msframes; currtime = rhs.currtime; lastreadpos = rhs.lastreadpos; headerSize = rhs.headerSize; @@ -678,18 +676,6 @@ void DTSC::File::readHeader(int pos){ readHeader(metadata["moreheader"].asInt()); return; } - if (metadata.isMember("keytime")){ - msframes.clear(); - for (int i = 0; i < metadata["keytime"].size(); ++i){ - msframes[i + 1] = metadata["keytime"][i].asInt(); - } - } - if (metadata.isMember("keybpos")){ - frames.clear(); - for (int i = 0; i < metadata["keybpos"].size(); ++i){ - frames[i + 1] = metadata["keybpos"][i].asInt(); - } - } metadata["vod"] = true; metadata.netPrepare(); } @@ -763,18 +749,16 @@ void DTSC::File::seekNext(){ jsonbuffer = JSON::fromDTMI(strbuffer); } if (jsonbuffer.isMember("keyframe")){ - if (frames[currframe] != lastreadpos){ + if (metadata["tracks"][selectedTracks[0]]["keybpos"][currframe].asInt() != lastreadpos){ currframe++; currtime = jsonbuffer["time"].asInt(); #if DEBUG >= 6 - if (frames[currframe] != lastreadpos){ + if (metadata["tracks"][selectedTracks[0]]["keybpos"][currframe].asInt() != lastreadpos){ std::cerr << "Found a new frame " << currframe << " @ " << lastreadpos << "b/" << currtime << "ms" << std::endl; } else{ std::cerr << "Passing frame " << currframe << " @ " << lastreadpos << "b/" << currtime << "ms" << std::endl; } #endif - frames[currframe] = lastreadpos; - msframes[currframe] = currtime; } } } @@ -797,34 +781,28 @@ JSON::Value & DTSC::File::getJSON(){ /// Attempts to seek to the given frame number within the file. /// Returns true if successful, false otherwise. bool DTSC::File::seek_frame(int frameno){ - if (frames.count(frameno) > 0){ - if (fseek(F, frames[frameno], SEEK_SET) == 0){ + int bytePos = -1; + int replaceBytePos = -1; + for (int i = 0; i < metadata["tracks"][selectedTracks[0]]["keynum"].size(); i++){ + if (metadata["tracks"][selectedTracks[0]]["keynum"][i].asInt() == frameno){ + bytePos = metadata["tracks"][selectedTracks[0]]["keybpos"][i].asInt(); + break; + } + if (metadata["tracks"][selectedTracks[0]]["keynum"][i].asInt() < frameno){ + replaceBytePos = metadata["tracks"][selectedTracks[0]]["keybpos"][i].asInt(); + } + } + if (bytePos == -1){ + bytePos = replaceBytePos; + } + if (bytePos > -1){ + if (fseek(F, bytePos, SEEK_SET) == 0){ #if DEBUG >= 5 - std::cerr << "Seek direct from " << currframe << " @ " << frames[currframe] << " to " << frameno << " @ " << frames[frameno] << std::endl; + std::cerr << "Seek direct from " << currframe << " @ " << metadata["tracks"][selectedTracks[0]]["keybpos"][currframe].asInt() << " to " << frameno << " @ " << bytePos << std::endl; #endif currframe = frameno; return true; } - }else{ - for (int i = frameno; i >= 1; --i){ - if (frames.count(i) > 0){ - currframe = i; - break; - } - } - if (fseek(F, frames[currframe], SEEK_SET) == 0){ -#if DEBUG >= 5 - std::cerr << "Seeking from frame " << currframe << " @ " << frames[currframe] << " to " << frameno << std::endl; -#endif - while (currframe < frameno){ - seekNext(); - if (jsonbuffer.isNull()){ - return false; - } - } - seek_frame(frameno); - return true; - } } return false; } @@ -832,31 +810,20 @@ bool DTSC::File::seek_frame(int frameno){ /// Attempts to seek to the given time in ms within the file. /// Returns true if successful, false otherwise. bool DTSC::File::seek_time(int ms){ - std::map::iterator it; currtime = 0; currframe = 1; - for (it = msframes.begin(); it != msframes.end(); ++it){ - if (it->second > ms){ + int bytePos = -1; + for (int i = 0; i < metadata["tracks"][selectedTracks[0]]["keynum"].size(); i++){ + if (metadata["tracks"][selectedTracks[0]]["keytime"][i].asInt() > ms){ break; } - if (it->second > currtime){ - currtime = it->second; - currframe = it->first; + if (metadata["tracks"][selectedTracks[0]]["keytime"][i].asInt() > currtime){ + currtime = metadata["tracks"][selectedTracks[0]]["keytime"][i].asInt(); + currframe = i + 1; } + bytePos = metadata["tracks"][selectedTracks[0]]["keybpos"][i].asInt(); } - if (fseek(F, frames[currframe], SEEK_SET) == 0){ -#if DEBUG >= 5 - std::cerr << "Seeking from frame " << currframe << " @ " << msframes[currframe] << "ms to " << ms << "ms" << std::endl; -#endif - while (currtime < ms){ - seekNext(); - if (jsonbuffer.isNull()){ - return false; - } - } - if (currtime > ms){ - return seek_frame(currframe - 1); - } + if (fseek(F, bytePos, SEEK_SET) == 0){ return true; } return false; @@ -878,6 +845,10 @@ void DTSC::File::writePacket(JSON::Value & newPacket){ writePacket(newPacket.toNetPacked()); } +void DTSC::File::selectTracks(std::vector & trackIDs){ + selectedTracks = trackIDs; +} + /// Close the file if open DTSC::File::~File(){ if (F){ diff --git a/lib/dtsc.h b/lib/dtsc.h index e60b311e..ce18679e 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -88,14 +88,15 @@ namespace DTSC { bool seek_bpos(int bpos); void writePacket(std::string & newPacket); void writePacket(JSON::Value & newPacket); + void selectTracks(std::vector & trackIDs); private: void readHeader(int pos); std::string strbuffer; JSON::Value jsonbuffer; JSON::Value metadata; JSON::Value firstmetadata; - std::map frames; - std::map msframes; + //std::map frames; + //std::map msframes; long long int currtime; long long int lastreadpos; int currframe; @@ -103,6 +104,7 @@ namespace DTSC { unsigned long headerSize; char buffer[4]; bool created; + std::vector selectedTracks; }; //FileWriter From 22295d3b5d59e70ee36de3ddfa8ce0bbe39d40e4 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Fri, 17 May 2013 15:15:52 +0200 Subject: [PATCH 403/788] Progress on HTTP Smooth output for DTSCv2 --- lib/dtsc.cpp | 69 ++++++++++++++++++++++++++++++++++++++++++---------- lib/dtsc.h | 5 ++-- lib/json.cpp | 31 ++++++++++++++--------- 3 files changed, 78 insertions(+), 27 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 15e07e20..18cc9369 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -62,6 +62,12 @@ bool DTSC::Stream::parsePacket(std::string & buffer){ metadata = JSON::fromDTMI2(buffer.substr(8)); } metadata.removeMember("moreheader"); + trackMapping.clear(); + if (metadata.isMember("tracks")){ + for (JSON::ObjIter it = metadata["tracks"].ObjBegin(); it != metadata["tracks"].ObjEnd(); it++){ + trackMapping.insert(std::pair(it->second["trackid"].asInt(),it->first)); + } + } buffer.erase(0, len + 8); if (buffer.length() <= 8){ return false; @@ -79,6 +85,9 @@ bool DTSC::Stream::parsePacket(std::string & buffer){ } if (version == 2){ buffers.front() = JSON::fromDTMI2(buffer.substr(8)); + if (!buffers.front().isMember("datatype")){ + buffers.front()["datatype"] = metadata["tracks"][trackMapping[buffers.front()["trackid"].asInt()]]["type"]; + } } datapointertype = INVALID; if (buffers.front().isMember("data")){ @@ -149,6 +158,12 @@ bool DTSC::Stream::parsePacket(Socket::Buffer & buffer){ metadata = JSON::fromDTMI((unsigned char*)wholepacket.c_str() + 8, len, i); metadata.removeMember("moreheader"); metadata.netPrepare(); + trackMapping.clear(); + if (metadata.isMember("tracks")){ + for (JSON::ObjIter it = metadata["tracks"].ObjBegin(); it != metadata["tracks"].ObjEnd(); it++){ + trackMapping.insert(std::pair(it->second["trackid"].asInt(),it->first)); + } + } if ( !buffer.available(8)){ return false; } @@ -174,6 +189,9 @@ bool DTSC::Stream::parsePacket(Socket::Buffer & buffer){ } if (version == 2){ buffers.front() = JSON::fromDTMI2(wholepacket.substr(8)); + if (!buffers.front().isMember("datatype")){ + buffers.front()["datatype"] = metadata["tracks"][trackMapping[buffers.front()["trackid"].asInt()]]["type"]; + } } datapointertype = INVALID; if (buffers.front().isMember("data")){ @@ -535,6 +553,7 @@ DTSC::File & DTSC::File::operator =(const File & rhs){ currtime = rhs.currtime; lastreadpos = rhs.lastreadpos; headerSize = rhs.headerSize; + trackMapping = rhs.trackMapping; memcpy(buffer, rhs.buffer, 4); } @@ -569,11 +588,14 @@ DTSC::File::File(std::string filename, bool create){ headerSize = ntohl(ubuffer[0]); } readHeader(0); + trackMapping.clear(); + if (metadata.isMember("tracks")){ + for (JSON::ObjIter it = metadata["tracks"].ObjBegin(); it != metadata["tracks"].ObjEnd(); it++){ + trackMapping.insert(std::pair(it->second["trackid"].asInt(),it->first)); + } + } fseek(F, 8 + headerSize, SEEK_SET); currframe = 0; - //currframe = 1; - //frames[1] = 8 + headerSize; - //msframes[1] = 0; } /// Returns the header metadata for this file as JSON::Value. @@ -723,7 +745,7 @@ void DTSC::File::seekNext(){ version = 2; } if (version == 0){ - fprintf(stderr, "Invalid packet header @ %#x - %.4s != %.4s\n", getBytePos(), buffer, DTSC::Magic_Packet2); + fprintf(stderr, "Invalid packet header @ %#x - %.4s != %.4s\n", lastreadpos, buffer, DTSC::Magic_Packet2); strbuffer = ""; jsonbuffer.null(); return; @@ -735,7 +757,7 @@ void DTSC::File::seekNext(){ return; } uint32_t * ubuffer = (uint32_t *)buffer; - long packSize = ntohl(ubuffer[0]) + (version == 2 ? 12 : 0); + long packSize = ntohl(ubuffer[0]); strbuffer.resize(packSize); if (fread((void*)strbuffer.c_str(), packSize, 1, F) != 1){ fprintf(stderr, "Could not read packet\n"); @@ -745,20 +767,25 @@ void DTSC::File::seekNext(){ } if (version == 2){ jsonbuffer = JSON::fromDTMI2(strbuffer); + if (!jsonbuffer.isMember("datatype")){ + jsonbuffer["datatype"] = metadata["tracks"][trackMapping[jsonbuffer["trackid"].asInt()]]["type"]; + } }else{ jsonbuffer = JSON::fromDTMI(strbuffer); } if (jsonbuffer.isMember("keyframe")){ - if (metadata["tracks"][selectedTracks[0]]["keybpos"][currframe].asInt() != lastreadpos){ - currframe++; - currtime = jsonbuffer["time"].asInt(); -#if DEBUG >= 6 + if (selectedTracks.size()){ if (metadata["tracks"][selectedTracks[0]]["keybpos"][currframe].asInt() != lastreadpos){ - std::cerr << "Found a new frame " << currframe << " @ " << lastreadpos << "b/" << currtime << "ms" << std::endl; - } else{ - std::cerr << "Passing frame " << currframe << " @ " << lastreadpos << "b/" << currtime << "ms" << std::endl; - } + currframe++; + currtime = jsonbuffer["time"].asInt(); +#if DEBUG >= 6 + if (metadata["tracks"][selectedTracks[0]]["keybpos"][currframe].asInt() != lastreadpos){ + std::cerr << "Found a new frame " << currframe << " @ " << lastreadpos << "b/" << currtime << "ms" << std::endl; + } else{ + std::cerr << "Passing frame " << currframe << " @ " << lastreadpos << "b/" << currtime << "ms" << std::endl; + } #endif + } } } } @@ -849,6 +876,22 @@ void DTSC::File::selectTracks(std::vector & trackIDs){ selectedTracks = trackIDs; } +bool DTSC::File::atKeyframe(){ + if (getJSON().isMember("keyframe")){ + return true; + } + bool inHeader = false; + for (JSON::ObjIter oIt = metadata["tracks"].ObjBegin(); oIt != metadata["tracks"].ObjEnd(); oIt++){ + for (JSON::ArrIter aIt = oIt->second["keynum"].ArrBegin(); aIt != oIt->second["keynum"].ArrEnd(); aIt++){ + if ((*aIt).asInt() == getBytePos()){ + inHeader = true; + break; + } + } + } + return inHeader; +} + /// Close the file if open DTSC::File::~File(){ if (F){ diff --git a/lib/dtsc.h b/lib/dtsc.h index ce18679e..4f772025 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -89,14 +89,14 @@ namespace DTSC { void writePacket(std::string & newPacket); void writePacket(JSON::Value & newPacket); void selectTracks(std::vector & trackIDs); + bool atKeyframe(); private: void readHeader(int pos); std::string strbuffer; JSON::Value jsonbuffer; JSON::Value metadata; JSON::Value firstmetadata; - //std::map frames; - //std::map msframes; + std::map trackMapping; long long int currtime; long long int lastreadpos; int currframe; @@ -157,5 +157,6 @@ namespace DTSC { datatype datapointertype; unsigned int buffercount; unsigned int buffertime; + std::map trackMapping; }; } diff --git a/lib/json.cpp b/lib/json.cpp index 1322c739..d39e2921 100644 --- a/lib/json.cpp +++ b/lib/json.cpp @@ -493,12 +493,15 @@ void JSON::Value::netPrepare(){ if (objVal["datatype"].asString() == "meta"){ packID = 3; } + //endmark and the likes... if (packID == -1){ packID = 0; } } removeMember("time"); - removeMember("datatype"); + if (packID != 0){ + removeMember("datatype"); + } removeMember("trackid"); packed = toPacked(); objVal["time"] = (long long int)time; @@ -512,20 +515,24 @@ void JSON::Value::netPrepare(){ memcpy((void*)strVal.c_str(), "DTSC", 4); } //insert the packet length at bytes 4-7 - unsigned int size = htonl(packed.size()); + unsigned int size = packed.size(); + if (packID != -1){ + size += 12; + } + size = htonl(size); memcpy((void*)(strVal.c_str() + 4), (void*) &size, 4); //copy the rest of the string - if (packID != -1){ - packID = htonl(packID); - memcpy((void*)(strVal.c_str() + 8), (void*) &packID, 4); - int tmpHalf = htonl((int)(time >> 32)); - memcpy((void*)(strVal.c_str() + 12), (void*) &tmpHalf, 4); - tmpHalf = htonl((int)(time & 0xFFFFFFFF)); - memcpy((void*)(strVal.c_str() + 16), (void*) &tmpHalf, 4); - memcpy((void*)(strVal.c_str() + 20), packed.c_str(), packed.size()); - }else{ + if (packID == -1){ memcpy((void*)(strVal.c_str() + 8), packed.c_str(), packed.size()); + return; } + packID = htonl(packID); + memcpy((void*)(strVal.c_str() + 8), (void*) &packID, 4); + int tmpHalf = htonl((int)(time >> 32)); + memcpy((void*)(strVal.c_str() + 12), (void*) &tmpHalf, 4); + tmpHalf = htonl((int)(time & 0xFFFFFFFF)); + memcpy((void*)(strVal.c_str() + 16), (void*) &tmpHalf, 4); + memcpy((void*)(strVal.c_str() + 20), packed.c_str(), packed.size()); } /// Packs any object-type JSON::Value to a std::string for transfer over the network, including proper DTMI header. @@ -869,8 +876,8 @@ JSON::Value JSON::fromDTMI(std::string data){ } //fromDTMI JSON::Value JSON::fromDTMI2(std::string data){ - JSON::Value tmp = fromDTMI(data.substr(12)); long long int tmpTrackID = ntohl(((int*)(data.c_str()))[0]); + JSON::Value tmp = fromDTMI(data.substr(12)); long long int tmpTime = ntohl(((int*)(data.c_str() + 4))[0]); tmpTime << 32; tmpTime += ntohl(((int*)(data.c_str() + 8))[0]); From 6e95dbe10bd9238fe20efd6b92a0cf873bbff1c4 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Wed, 22 May 2013 15:48:26 +0200 Subject: [PATCH 404/788] Multiple updates for DTSCv2 Filtering. --- lib/dtsc.cpp | 104 +++++++++++++++++++++++---------------------------- lib/dtsc.h | 30 +++++++++++++-- 2 files changed, 73 insertions(+), 61 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 18cc9369..534852ce 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -719,6 +719,9 @@ bool DTSC::File::reachedEOF(){ /// If the packet could not be read for any reason, the reason is printed to stderr. /// Reading the packet means the file position is increased to the next packet. void DTSC::File::seekNext(){ + fseek(F,currentPositions.begin()->seekPos, SEEK_SET); + seek_time(currentPositions.begin()->seekTime + 1, currentPositions.begin()->trackID); + currentPositions.erase(currentPositions.begin()); lastreadpos = ftell(F); if (fread(buffer, 4, 1, F) != 1){ if (feof(F)){ @@ -773,21 +776,6 @@ void DTSC::File::seekNext(){ }else{ jsonbuffer = JSON::fromDTMI(strbuffer); } - if (jsonbuffer.isMember("keyframe")){ - if (selectedTracks.size()){ - if (metadata["tracks"][selectedTracks[0]]["keybpos"][currframe].asInt() != lastreadpos){ - currframe++; - currtime = jsonbuffer["time"].asInt(); -#if DEBUG >= 6 - if (metadata["tracks"][selectedTracks[0]]["keybpos"][currframe].asInt() != lastreadpos){ - std::cerr << "Found a new frame " << currframe << " @ " << lastreadpos << "b/" << currtime << "ms" << std::endl; - } else{ - std::cerr << "Passing frame " << currframe << " @ " << lastreadpos << "b/" << currtime << "ms" << std::endl; - } -#endif - } - } - } } /// Returns the byte positon of the start of the last packet that was read. @@ -805,55 +793,54 @@ JSON::Value & DTSC::File::getJSON(){ return jsonbuffer; } -/// Attempts to seek to the given frame number within the file. -/// Returns true if successful, false otherwise. -bool DTSC::File::seek_frame(int frameno){ - int bytePos = -1; - int replaceBytePos = -1; - for (int i = 0; i < metadata["tracks"][selectedTracks[0]]["keynum"].size(); i++){ - if (metadata["tracks"][selectedTracks[0]]["keynum"][i].asInt() == frameno){ - bytePos = metadata["tracks"][selectedTracks[0]]["keybpos"][i].asInt(); +bool DTSC::File::seek_time(int ms, int trackNo){ + seekPos tmpPos; + tmpPos.trackID = trackNo; + for (int i = 0; i < metadata["tracks"][trackMapping[trackNo]]["keynum"].size(); i++){ + if (metadata["tracks"][trackMapping[trackNo]]["keytime"][i].asInt() > ms){ break; } - if (metadata["tracks"][selectedTracks[0]]["keynum"][i].asInt() < frameno){ - replaceBytePos = metadata["tracks"][selectedTracks[0]]["keybpos"][i].asInt(); + tmpPos.seekTime = metadata["tracks"][trackMapping[trackNo]]["keytime"][i].asInt(); + tmpPos.seekPos = metadata["tracks"][trackMapping[trackNo]]["keybpos"][i].asInt(); + } + bool foundPacket = false; + while ( !foundPacket){ + //Seek to first packet after ms. + seek_bpos(tmpPos.seekPos); + //read the header + char header[20]; + fread((void*)header, 20, 1, F); + //check if packetID matches, if not, skip size + 8 bytes. + int packSize = ntohl(((int*)header)[1]); + int packID = ntohl(((int*)header)[2]); + if (packID != trackNo){ + tmpPos.seekPos += 8 + packSize; + continue; + } + //get timestamp of packet, if too large, break, if not, skip size bytes. + long long unsigned int myTime = ((long long unsigned int)ntohl(((int*)header)[3]) << 32); + myTime += ntohl(((int*)header)[4]); + if (myTime >= ms){ + tmpPos.seekTime = myTime; + foundPacket = true; + }else{ + tmpPos.seekPos += 8 + packSize; + continue; } } - if (bytePos == -1){ - bytePos = replaceBytePos; - } - if (bytePos > -1){ - if (fseek(F, bytePos, SEEK_SET) == 0){ -#if DEBUG >= 5 - std::cerr << "Seek direct from " << currframe << " @ " << metadata["tracks"][selectedTracks[0]]["keybpos"][currframe].asInt() << " to " << frameno << " @ " << bytePos << std::endl; -#endif - currframe = frameno; - return true; - } - } - return false; + currentPositions.insert(tmpPos); + fprintf(stderr, "TrackID %d, Time seek %d -- retrieved bytepos %d, timestamp %d\n", trackNo, ms, tmpPos.seekPos, tmpPos.seekTime); } /// Attempts to seek to the given time in ms within the file. /// Returns true if successful, false otherwise. bool DTSC::File::seek_time(int ms){ - currtime = 0; - currframe = 1; - int bytePos = -1; - for (int i = 0; i < metadata["tracks"][selectedTracks[0]]["keynum"].size(); i++){ - if (metadata["tracks"][selectedTracks[0]]["keytime"][i].asInt() > ms){ - break; - } - if (metadata["tracks"][selectedTracks[0]]["keytime"][i].asInt() > currtime){ - currtime = metadata["tracks"][selectedTracks[0]]["keytime"][i].asInt(); - currframe = i + 1; - } - bytePos = metadata["tracks"][selectedTracks[0]]["keybpos"][i].asInt(); + currentPositions.clear(); + seekPos tmpPos; + for (std::set::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){ + seek_time(ms,(*it)); } - if (fseek(F, bytePos, SEEK_SET) == 0){ - return true; - } - return false; + return true; } bool DTSC::File::seek_bpos(int bpos){ @@ -872,10 +859,6 @@ void DTSC::File::writePacket(JSON::Value & newPacket){ writePacket(newPacket.toNetPacked()); } -void DTSC::File::selectTracks(std::vector & trackIDs){ - selectedTracks = trackIDs; -} - bool DTSC::File::atKeyframe(){ if (getJSON().isMember("keyframe")){ return true; @@ -892,6 +875,11 @@ bool DTSC::File::atKeyframe(){ return inHeader; } +void DTSC::File::selectTracks(std::set & tracks){ + currentPositions.clear(); + selectedTracks = tracks; +} + /// Close the file if open DTSC::File::~File(){ if (F){ diff --git a/lib/dtsc.h b/lib/dtsc.h index 4f772025..e83ef8fc 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -64,6 +64,29 @@ namespace DTSC { extern char Magic_Packet[]; ///< The magic bytes for a DTSC packet extern char Magic_Packet2[]; ///< The magic bytes for a DTSC packet version 2 + /// A simple structure used for ordering byte seek positions. + struct seekPos { + bool operator < (const seekPos& rhs) const { + if (seekTime < rhs.seekTime){ + return true; + }else{ + if (seekTime == rhs.seekTime){ + if (seekPos < rhs.seekPos){ + return true; + }else{ + if (trackID < rhs.trackID){ + return true; + } + } + } + } + return false; + } + long long unsigned int seekTime; + long long unsigned int seekPos; + unsigned int trackID; + }; + /// A simple wrapper class that will open a file and allow easy reading/writing of DTSC data from/to it. class File{ public: @@ -83,13 +106,13 @@ namespace DTSC { void seekNext(); std::string & getPacket(); JSON::Value & getJSON(); - bool seek_frame(int frameno); bool seek_time(int seconds); + bool seek_time(int seconds, int trackNo); bool seek_bpos(int bpos); void writePacket(std::string & newPacket); void writePacket(JSON::Value & newPacket); - void selectTracks(std::vector & trackIDs); bool atKeyframe(); + void selectTracks(std::set & tracks); private: void readHeader(int pos); std::string strbuffer; @@ -104,7 +127,8 @@ namespace DTSC { unsigned long headerSize; char buffer[4]; bool created; - std::vector selectedTracks; + std::set currentPositions; + std::set selectedTracks; }; //FileWriter From a3bae914865b273512da234120506afd248093ed Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Mon, 27 May 2013 13:45:42 +0200 Subject: [PATCH 405/788] Upgrade FLV output --- lib/dtsc.cpp | 25 ++++++++-- lib/flv_tag.cpp | 127 ++++++++++++++++++++++++++---------------------- lib/flv_tag.h | 4 +- 3 files changed, 92 insertions(+), 64 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 534852ce..4312c01f 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -703,8 +703,12 @@ void DTSC::File::readHeader(int pos){ } long int DTSC::File::getBytePosEOF(){ - fseek(F, 0, SEEK_END); - return ftell(F); + static long int endPos = 0; + if ( !endPos){ + fseek(F, 0, SEEK_END); + endPos = ftell(F); + } + return endPos; } long int DTSC::File::getBytePos(){ @@ -719,8 +723,13 @@ bool DTSC::File::reachedEOF(){ /// If the packet could not be read for any reason, the reason is printed to stderr. /// Reading the packet means the file position is increased to the next packet. void DTSC::File::seekNext(){ - fseek(F,currentPositions.begin()->seekPos, SEEK_SET); + if ( !currentPositions.size()){ + strbuffer = ""; + jsonbuffer.null(); + return; + } seek_time(currentPositions.begin()->seekTime + 1, currentPositions.begin()->trackID); + fseek(F,currentPositions.begin()->seekPos, SEEK_SET); currentPositions.erase(currentPositions.begin()); lastreadpos = ftell(F); if (fread(buffer, 4, 1, F) != 1){ @@ -796,6 +805,8 @@ JSON::Value & DTSC::File::getJSON(){ bool DTSC::File::seek_time(int ms, int trackNo){ seekPos tmpPos; tmpPos.trackID = trackNo; + tmpPos.seekTime = metadata["tracks"][trackMapping[trackNo]]["keytime"][0u].asInt(); + tmpPos.seekPos = metadata["tracks"][trackMapping[trackNo]]["keybpos"][0u].asInt(); for (int i = 0; i < metadata["tracks"][trackMapping[trackNo]]["keynum"].size(); i++){ if (metadata["tracks"][trackMapping[trackNo]]["keytime"][i].asInt() > ms){ break; @@ -805,6 +816,9 @@ bool DTSC::File::seek_time(int ms, int trackNo){ } bool foundPacket = false; while ( !foundPacket){ + if (tmpPos.seekPos == getBytePosEOF()){ + return false; + } //Seek to first packet after ms. seek_bpos(tmpPos.seekPos); //read the header @@ -829,7 +843,6 @@ bool DTSC::File::seek_time(int ms, int trackNo){ } } currentPositions.insert(tmpPos); - fprintf(stderr, "TrackID %d, Time seek %d -- retrieved bytepos %d, timestamp %d\n", trackNo, ms, tmpPos.seekPos, tmpPos.seekTime); } /// Attempts to seek to the given time in ms within the file. @@ -865,7 +878,7 @@ bool DTSC::File::atKeyframe(){ } bool inHeader = false; for (JSON::ObjIter oIt = metadata["tracks"].ObjBegin(); oIt != metadata["tracks"].ObjEnd(); oIt++){ - for (JSON::ArrIter aIt = oIt->second["keynum"].ArrBegin(); aIt != oIt->second["keynum"].ArrEnd(); aIt++){ + for (JSON::ArrIter aIt = oIt->second["keybpos"].ArrBegin(); aIt != oIt->second["keybpos"].ArrEnd(); aIt++){ if ((*aIt).asInt() == getBytePos()){ inHeader = true; break; @@ -878,6 +891,8 @@ bool DTSC::File::atKeyframe(){ void DTSC::File::selectTracks(std::set & tracks){ currentPositions.clear(); selectedTracks = tracks; + for (std::set::iterator it = tracks.begin(); it != tracks.end(); it++){ + } } /// Close the file if open diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index 69041804..8ca1ae2a 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -500,18 +500,22 @@ void FLV::Tag::setLen(){ /// Takes the DTSC Video init data and makes it into FLV. /// Assumes init data is available - so check before calling! bool FLV::Tag::DTSCVideoInit(DTSC::Stream & S){ + return DTSCVideoInit(S.metadata["video"]); +} + +bool FLV::Tag::DTSCVideoInit(JSON::Value & video){ //Unknown? Assume H264. - if (S.metadata["video"]["codec"].asString() == "?"){ - S.metadata["video"]["codec"] = "H264"; + if (video["codec"].asString() == "?"){ + video["codec"] = "H264"; } - if (S.metadata["video"]["codec"].asString() == "H264"){ - len = S.metadata["video"]["init"].asString().length() + 20; + if (video["codec"].asString() == "H264"){ + len = video["init"].asString().length() + 20; } if (len > 0){ if ( !checkBufferSize()){ return false; } - memcpy(data + 16, S.metadata["video"]["init"].asString().c_str(), len - 20); + memcpy(data + 16, video["init"].asString().c_str(), len - 20); data[12] = 0; //H264 sequence header data[13] = 0; data[14] = 0; @@ -534,28 +538,32 @@ bool FLV::Tag::DTSCVideoInit(DTSC::Stream & S){ /// Takes the DTSC Audio init data and makes it into FLV. /// Assumes init data is available - so check before calling! bool FLV::Tag::DTSCAudioInit(DTSC::Stream & S){ + return DTSCAudioInit(S.metadata["audio"]); +} + +bool FLV::Tag::DTSCAudioInit(JSON::Value & audio){ len = 0; //Unknown? Assume AAC. - if (S.metadata["audio"]["codec"].asString() == "?"){ - S.metadata["audio"]["codec"] = "AAC"; + if (audio["codec"].asString() == "?"){ + audio["codec"] = "AAC"; } - if (S.metadata["audio"]["codec"].asString() == "AAC"){ - len = S.metadata["audio"]["init"].asString().length() + 17; + if (audio["codec"].asString() == "AAC"){ + len = audio["init"].asString().length() + 17; } if (len > 0){ if ( !checkBufferSize()){ return false; } - memcpy(data + 13, S.metadata["audio"]["init"].asString().c_str(), len - 17); + memcpy(data + 13, audio["init"].asString().c_str(), len - 17); data[12] = 0; //AAC sequence header data[11] = 0; - if (S.metadata["audio"]["codec"].asString() == "AAC"){ + if (audio["codec"].asString() == "AAC"){ data[11] += 0xA0; } - if (S.metadata["audio"]["codec"].asString() == "MP3"){ + if (audio["codec"].asString() == "MP3"){ data[11] += 0x20; } - unsigned int datarate = S.metadata["audio"]["rate"].asInt(); + unsigned int datarate = audio["rate"].asInt(); if (datarate >= 44100){ data[11] += 0x0C; }else if (datarate >= 22050){ @@ -563,11 +571,12 @@ bool FLV::Tag::DTSCAudioInit(DTSC::Stream & S){ }else if (datarate >= 11025){ data[11] += 0x04; } - if (S.metadata["audio"]["size"].asInt() == 16){ + if (audio["size"].asInt() == 16){ data[11] += 0x02; } - if (S.metadata["audio"]["channels"].asInt() > 1){ + if (audio["channels"].asInt() > 1){ data[11] += 0x01; + } } setLen(); @@ -585,14 +594,16 @@ bool FLV::Tag::DTSCAudioInit(DTSC::Stream & S){ /// FLV metadata loader function from DTSC. /// Takes the DTSC metadata and makes it into FLV. /// Assumes metadata is available - so check before calling! -bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){ +bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S, std::string vidName, std::string audName){ + JSON::Value & videoRef = S.metadata["tracks"][vidName]; + JSON::Value & audioRef = S.metadata["tracks"][audName]; //Unknown? Assume AAC. - if (S.metadata.isMember("audio") && S.metadata["audio"]["codec"].asString() == "?"){ - S.metadata["audio"]["codec"] = "AAC"; + if (audioRef["codec"].asString() == "?"){ + audioRef["codec"] = "AAC"; } //Unknown? Assume H264. - if (S.metadata.isMember("video") && S.metadata["video"]["codec"].asString() == "?"){ - S.metadata["video"]["codec"] = "H264"; + if (videoRef["codec"].asString() == "?"){ + videoRef["codec"] = "H264"; } AMF::Object amfdata("root", AMF::AMF0_DDV_CONTAINER); @@ -606,11 +617,11 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){ keys.addContent(AMF::Object("filepositions", AMF::AMF0_STRICT_ARRAY)); keys.addContent(AMF::Object("times", AMF::AMF0_STRICT_ARRAY)); int total_byterate = 0; - if (S.metadata.isMember("video")){ - total_byterate += S.metadata["video"]["bps"].asInt(); + if (vidName != ""){ + total_byterate += videoRef["bps"].asInt(); } - if (S.metadata.isMember("audio")){ - total_byterate += S.metadata["audio"]["bps"].asInt(); + if (audName != ""){ + total_byterate += audioRef["bps"].asInt(); } for (int i = 0; i < S.metadata["length"].asInt(); ++i){ //for each second in the file keys.getContentP(0)->addContent(AMF::Object("", i * total_byterate, AMF::AMF0_NUMBER)); //multiply by byterate for fake byte positions @@ -618,81 +629,81 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){ } amfdata.getContentP(1)->addContent(keys); } - if (S.metadata.isMember("video")){ + if (vidName != ""){ amfdata.getContentP(1)->addContent(AMF::Object("hasVideo", 1, AMF::AMF0_BOOL)); - if (S.metadata["video"]["codec"].asString() == "H264"){ + if (videoRef["codec"].asString() == "H264"){ amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", (std::string)"avc1")); } - if (S.metadata["video"]["codec"].asString() == "VP6"){ + if (videoRef["codec"].asString() == "VP6"){ amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", 4, AMF::AMF0_NUMBER)); } - if (S.metadata["video"]["codec"].asString() == "H263"){ + if (videoRef["codec"].asString() == "H263"){ amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", 2, AMF::AMF0_NUMBER)); } - if (S.metadata["video"].isMember("width")){ - amfdata.getContentP(1)->addContent(AMF::Object("width", S.metadata["video"]["width"].asInt(), AMF::AMF0_NUMBER)); + if (videoRef.isMember("width")){ + amfdata.getContentP(1)->addContent(AMF::Object("width", videoRef["width"].asInt(), AMF::AMF0_NUMBER)); } - if (S.metadata["video"].isMember("height")){ - amfdata.getContentP(1)->addContent(AMF::Object("height", S.metadata["video"]["height"].asInt(), AMF::AMF0_NUMBER)); + if (videoRef.isMember("height")){ + amfdata.getContentP(1)->addContent(AMF::Object("height", videoRef["height"].asInt(), AMF::AMF0_NUMBER)); } - if (S.metadata["video"].isMember("fpks")){ - amfdata.getContentP(1)->addContent(AMF::Object("videoframerate", (double)S.metadata["video"]["fpks"].asInt() / 1000.0, AMF::AMF0_NUMBER)); + if (videoRef.isMember("fpks")){ + amfdata.getContentP(1)->addContent(AMF::Object("videoframerate", (double)videoRef["fpks"].asInt() / 1000.0, AMF::AMF0_NUMBER)); } - if (S.metadata["video"].isMember("bps")){ - amfdata.getContentP(1)->addContent(AMF::Object("videodatarate", (double)S.metadata["video"]["bps"].asInt() * 128.0, AMF::AMF0_NUMBER)); + if (videoRef.isMember("bps")){ + amfdata.getContentP(1)->addContent(AMF::Object("videodatarate", (double)videoRef["bps"].asInt() * 128.0, AMF::AMF0_NUMBER)); } } - if (S.metadata.isMember("audio")){ + if (audName != ""){ amfdata.getContentP(1)->addContent(AMF::Object("hasAudio", 1, AMF::AMF0_BOOL)); amfdata.getContentP(1)->addContent(AMF::Object("audiodelay", 0, AMF::AMF0_NUMBER)); - if (S.metadata["audio"]["codec"].asString() == "AAC"){ + if (audioRef["codec"].asString() == "AAC"){ amfdata.getContentP(1)->addContent(AMF::Object("audiocodecid", (std::string)"mp4a")); } - if (S.metadata["audio"]["codec"].asString() == "MP3"){ + if (audioRef["codec"].asString() == "MP3"){ amfdata.getContentP(1)->addContent(AMF::Object("audiocodecid", (std::string)"mp3")); } - if (S.metadata["audio"].isMember("channels")){ - amfdata.getContentP(1)->addContent(AMF::Object("audiochannels", S.metadata["audio"]["channels"].asInt(), AMF::AMF0_NUMBER)); + if (audioRef.isMember("channels")){ + amfdata.getContentP(1)->addContent(AMF::Object("audiochannels", audioRef["channels"].asInt(), AMF::AMF0_NUMBER)); } - if (S.metadata["audio"].isMember("rate")){ - amfdata.getContentP(1)->addContent(AMF::Object("audiosamplerate", S.metadata["audio"]["rate"].asInt(), AMF::AMF0_NUMBER)); + if (audioRef.isMember("rate")){ + amfdata.getContentP(1)->addContent(AMF::Object("audiosamplerate", audioRef["rate"].asInt(), AMF::AMF0_NUMBER)); } - if (S.metadata["audio"].isMember("size")){ - amfdata.getContentP(1)->addContent(AMF::Object("audiosamplesize", S.metadata["audio"]["size"].asInt(), AMF::AMF0_NUMBER)); + if (audioRef.isMember("size")){ + amfdata.getContentP(1)->addContent(AMF::Object("audiosamplesize", audioRef["size"].asInt(), AMF::AMF0_NUMBER)); } - if (S.metadata["audio"].isMember("bps")){ - amfdata.getContentP(1)->addContent(AMF::Object("audiodatarate", (double)S.metadata["audio"]["bps"].asInt() * 128.0, AMF::AMF0_NUMBER)); + if (audioRef.isMember("bps")){ + amfdata.getContentP(1)->addContent(AMF::Object("audiodatarate", (double)audioRef["bps"].asInt() * 128.0, AMF::AMF0_NUMBER)); } } AMF::Object trinfo = AMF::Object("trackinfo", AMF::AMF0_STRICT_ARRAY); int i = 0; - if (S.metadata.isMember("audio")){ + if (audName != ""){ trinfo.addContent(AMF::Object("", AMF::AMF0_OBJECT)); trinfo.getContentP(i)->addContent( - AMF::Object("length", ((double)S.metadata["length"].asInt()) * ((double)S.metadata["audio"]["rate"].asInt()), AMF::AMF0_NUMBER)); - trinfo.getContentP(i)->addContent(AMF::Object("timescale", S.metadata["audio"]["rate"].asInt(), AMF::AMF0_NUMBER)); + AMF::Object("length", ((double)S.metadata["length"].asInt()) * ((double)audioRef["rate"].asInt()), AMF::AMF0_NUMBER)); + trinfo.getContentP(i)->addContent(AMF::Object("timescale", audioRef["rate"].asInt(), AMF::AMF0_NUMBER)); trinfo.getContentP(i)->addContent(AMF::Object("sampledescription", AMF::AMF0_STRICT_ARRAY)); - if (S.metadata["audio"]["codec"].asString() == "AAC"){ + if (audioRef["codec"].asString() == "AAC"){ trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string)"mp4a")); } - if (S.metadata["audio"]["codec"].asString() == "MP3"){ + if (audioRef["codec"].asString() == "MP3"){ trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string)"mp3")); } ++i; } - if (S.metadata.isMember("video")){ + if (vidName != ""){ trinfo.addContent(AMF::Object("", AMF::AMF0_OBJECT)); trinfo.getContentP(i)->addContent( - AMF::Object("length", ((double)S.metadata["length"].asInt()) * ((double)S.metadata["video"]["fkps"].asInt() / 1000.0), AMF::AMF0_NUMBER)); - trinfo.getContentP(i)->addContent(AMF::Object("timescale", ((double)S.metadata["video"]["fkps"].asInt() / 1000.0), AMF::AMF0_NUMBER)); + AMF::Object("length", ((double)S.metadata["length"].asInt()) * ((double)videoRef["fkps"].asInt() / 1000.0), AMF::AMF0_NUMBER)); + trinfo.getContentP(i)->addContent(AMF::Object("timescale", ((double)videoRef["fkps"].asInt() / 1000.0), AMF::AMF0_NUMBER)); trinfo.getContentP(i)->addContent(AMF::Object("sampledescription", AMF::AMF0_STRICT_ARRAY)); - if (S.metadata["video"]["codec"].asString() == "H264"){ + if (videoRef["codec"].asString() == "H264"){ trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string)"avc1")); } - if (S.metadata["video"]["codec"].asString() == "VP6"){ + if (videoRef["codec"].asString() == "VP6"){ trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string)"vp6")); } - if (S.metadata["video"]["codec"].asString() == "H263"){ + if (videoRef["codec"].asString() == "H263"){ trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string)"h263")); } ++i; diff --git a/lib/flv_tag.h b/lib/flv_tag.h index 5959982c..5433068a 100644 --- a/lib/flv_tag.h +++ b/lib/flv_tag.h @@ -46,8 +46,10 @@ namespace FLV { bool ChunkLoader(const RTMPStream::Chunk& O); bool DTSCLoader(DTSC::Stream & S); bool DTSCVideoInit(DTSC::Stream & S); + bool DTSCVideoInit(JSON::Value & video); bool DTSCAudioInit(DTSC::Stream & S); - bool DTSCMetaInit(DTSC::Stream & S); + bool DTSCAudioInit(JSON::Value & audio); + bool DTSCMetaInit(DTSC::Stream & S, std::string vidName = "", std::string audName = ""); JSON::Value toJSON(JSON::Value & metadata); bool MemLoader(char * D, unsigned int S, unsigned int & P); bool FileLoader(FILE * f); From c1560deaa9264a095a3f768629b106f5c2c92b47 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Wed, 29 May 2013 09:20:18 +0200 Subject: [PATCH 406/788] Small fixes in DTSC lib. --- lib/dtsc.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 4312c01f..ad417d4a 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -728,6 +728,7 @@ void DTSC::File::seekNext(){ jsonbuffer.null(); return; } + fseek(F,currentPositions.begin()->seekPos, SEEK_SET); seek_time(currentPositions.begin()->seekTime + 1, currentPositions.begin()->trackID); fseek(F,currentPositions.begin()->seekPos, SEEK_SET); currentPositions.erase(currentPositions.begin()); @@ -805,14 +806,16 @@ JSON::Value & DTSC::File::getJSON(){ bool DTSC::File::seek_time(int ms, int trackNo){ seekPos tmpPos; tmpPos.trackID = trackNo; - tmpPos.seekTime = metadata["tracks"][trackMapping[trackNo]]["keytime"][0u].asInt(); - tmpPos.seekPos = metadata["tracks"][trackMapping[trackNo]]["keybpos"][0u].asInt(); + tmpPos.seekTime = jsonbuffer["time"].asInt(); + tmpPos.seekPos = getBytePos(); for (int i = 0; i < metadata["tracks"][trackMapping[trackNo]]["keynum"].size(); i++){ if (metadata["tracks"][trackMapping[trackNo]]["keytime"][i].asInt() > ms){ break; } - tmpPos.seekTime = metadata["tracks"][trackMapping[trackNo]]["keytime"][i].asInt(); - tmpPos.seekPos = metadata["tracks"][trackMapping[trackNo]]["keybpos"][i].asInt(); + if (metadata["tracks"][trackMapping[trackNo]]["keytime"][i].asInt() > jsonbuffer["time"].asInt()){ + tmpPos.seekTime = metadata["tracks"][trackMapping[trackNo]]["keytime"][i].asInt(); + tmpPos.seekPos = metadata["tracks"][trackMapping[trackNo]]["keybpos"][i].asInt(); + } } bool foundPacket = false; while ( !foundPacket){ @@ -891,8 +894,6 @@ bool DTSC::File::atKeyframe(){ void DTSC::File::selectTracks(std::set & tracks){ currentPositions.clear(); selectedTracks = tracks; - for (std::set::iterator it = tracks.begin(); it != tracks.end(); it++){ - } } /// Close the file if open From e5ab3e36982787754d6887edf8b5c4d4f0f874f7 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Thu, 30 May 2013 11:46:26 +0200 Subject: [PATCH 407/788] Fixed a few minor bugs in Multibitrate seeking. --- lib/dtsc.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index ad417d4a..e83880b3 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -729,6 +729,11 @@ void DTSC::File::seekNext(){ return; } fseek(F,currentPositions.begin()->seekPos, SEEK_SET); + if ( reachedEOF()){ + strbuffer = ""; + jsonbuffer.null(); + return; + } seek_time(currentPositions.begin()->seekTime + 1, currentPositions.begin()->trackID); fseek(F,currentPositions.begin()->seekPos, SEEK_SET); currentPositions.erase(currentPositions.begin()); @@ -819,7 +824,7 @@ bool DTSC::File::seek_time(int ms, int trackNo){ } bool foundPacket = false; while ( !foundPacket){ - if (tmpPos.seekPos == getBytePosEOF()){ + if (reachedEOF()){ return false; } //Seek to first packet after ms. @@ -854,6 +859,7 @@ bool DTSC::File::seek_time(int ms){ currentPositions.clear(); seekPos tmpPos; for (std::set::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){ + seek_bpos(0); seek_time(ms,(*it)); } return true; @@ -881,8 +887,8 @@ bool DTSC::File::atKeyframe(){ } bool inHeader = false; for (JSON::ObjIter oIt = metadata["tracks"].ObjBegin(); oIt != metadata["tracks"].ObjEnd(); oIt++){ - for (JSON::ArrIter aIt = oIt->second["keybpos"].ArrBegin(); aIt != oIt->second["keybpos"].ArrEnd(); aIt++){ - if ((*aIt).asInt() == getBytePos()){ + for (JSON::ArrIter aIt = oIt->second["keytime"].ArrBegin(); aIt != oIt->second["keytime"].ArrEnd(); aIt++){ + if ((*aIt).asInt() == jsonbuffer["time"].asInt()){ inHeader = true; break; } From a2b70298fc454cc3f0708bc623022291f32b5dfd Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Tue, 4 Jun 2013 11:48:03 +0200 Subject: [PATCH 408/788] New keys approach in lib. --- lib/dtsc.cpp | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index e83880b3..b725a20e 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -811,15 +811,21 @@ JSON::Value & DTSC::File::getJSON(){ bool DTSC::File::seek_time(int ms, int trackNo){ seekPos tmpPos; tmpPos.trackID = trackNo; - tmpPos.seekTime = jsonbuffer["time"].asInt(); - tmpPos.seekPos = getBytePos(); - for (int i = 0; i < metadata["tracks"][trackMapping[trackNo]]["keynum"].size(); i++){ - if (metadata["tracks"][trackMapping[trackNo]]["keytime"][i].asInt() > ms){ + //if (jsonbuffer && ms > jsonbuffer["time"].asInt()){ + // tmpPos.seekTime = jsonbuffer["time"].asInt(); + // tmpPos.seekPos = getBytePos(); + //}else{ + tmpPos.seekTime = 0; + tmpPos.seekPos = 0; + // jsonbuffer["time"] = 0u; + //} + for (JSON::ArrIter keyIt = metadata["tracks"][trackMapping[trackNo]]["keys"].ArrBegin(); keyIt != metadata["tracks"][trackMapping[trackNo]]["keys"].ArrEnd(); keyIt++){ + if ((*keyIt)["time"].asInt() > ms){ break; } - if (metadata["tracks"][trackMapping[trackNo]]["keytime"][i].asInt() > jsonbuffer["time"].asInt()){ - tmpPos.seekTime = metadata["tracks"][trackMapping[trackNo]]["keytime"][i].asInt(); - tmpPos.seekPos = metadata["tracks"][trackMapping[trackNo]]["keybpos"][i].asInt(); + if ((*keyIt)["time"].asInt() > jsonbuffer["time"].asInt()){ + tmpPos.seekTime = (*keyIt)["time"].asInt(); + tmpPos.seekPos = (*keyIt)["bpos"].asInt(); } } bool foundPacket = false; @@ -835,7 +841,7 @@ bool DTSC::File::seek_time(int ms, int trackNo){ //check if packetID matches, if not, skip size + 8 bytes. int packSize = ntohl(((int*)header)[1]); int packID = ntohl(((int*)header)[2]); - if (packID != trackNo){ + if (memcmp(header,Magic_Packet2,4) != 0 || packID != trackNo){ tmpPos.seekPos += 8 + packSize; continue; } @@ -886,11 +892,15 @@ bool DTSC::File::atKeyframe(){ return true; } bool inHeader = false; - for (JSON::ObjIter oIt = metadata["tracks"].ObjBegin(); oIt != metadata["tracks"].ObjEnd(); oIt++){ - for (JSON::ArrIter aIt = oIt->second["keytime"].ArrBegin(); aIt != oIt->second["keytime"].ArrEnd(); aIt++){ - if ((*aIt).asInt() == jsonbuffer["time"].asInt()){ - inHeader = true; - break; + for (std::set::iterator selectIt = selectedTracks.begin(); selectIt != selectedTracks.end(); selectIt++){ + for (JSON::ObjIter oIt = metadata["tracks"].ObjBegin(); oIt != metadata["tracks"].ObjEnd(); oIt++){ + if (oIt->second["trackid"].asInt() == (*selectIt)){ + for (JSON::ArrIter aIt = oIt->second["keys"].ArrBegin(); aIt != oIt->second["keys"].ArrEnd(); aIt++){ + if ((*aIt)["time"].asInt() == jsonbuffer["time"].asInt()){ + inHeader = true; + break; + } + } } } } From 0c50862344b63c454220fb970ce9838a0af9b744 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Wed, 5 Jun 2013 09:19:10 +0200 Subject: [PATCH 409/788] Fixed a bug in determining when to seek. --- lib/dtsc.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index b725a20e..03f7888f 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -734,6 +734,7 @@ void DTSC::File::seekNext(){ jsonbuffer.null(); return; } + clearerr(F); seek_time(currentPositions.begin()->seekTime + 1, currentPositions.begin()->trackID); fseek(F,currentPositions.begin()->seekPos, SEEK_SET); currentPositions.erase(currentPositions.begin()); @@ -811,19 +812,18 @@ JSON::Value & DTSC::File::getJSON(){ bool DTSC::File::seek_time(int ms, int trackNo){ seekPos tmpPos; tmpPos.trackID = trackNo; - //if (jsonbuffer && ms > jsonbuffer["time"].asInt()){ - // tmpPos.seekTime = jsonbuffer["time"].asInt(); - // tmpPos.seekPos = getBytePos(); - //}else{ + if (jsonbuffer && ms > jsonbuffer["time"].asInt() && trackNo >= jsonbuffer["trackid"].asInt()){ + tmpPos.seekTime = jsonbuffer["time"].asInt(); + tmpPos.seekPos = getBytePos(); + }else{ tmpPos.seekTime = 0; tmpPos.seekPos = 0; - // jsonbuffer["time"] = 0u; - //} + } for (JSON::ArrIter keyIt = metadata["tracks"][trackMapping[trackNo]]["keys"].ArrBegin(); keyIt != metadata["tracks"][trackMapping[trackNo]]["keys"].ArrEnd(); keyIt++){ if ((*keyIt)["time"].asInt() > ms){ break; } - if ((*keyIt)["time"].asInt() > jsonbuffer["time"].asInt()){ + if ((*keyIt)["time"].asInt() > tmpPos.seekTime){ tmpPos.seekTime = (*keyIt)["time"].asInt(); tmpPos.seekPos = (*keyIt)["bpos"].asInt(); } From 1449e9e73e126abc19c861eac65a999059ba955b Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Thu, 6 Jun 2013 09:59:51 +0200 Subject: [PATCH 410/788] Added getTrackById() --- lib/dtsc.cpp | 24 +++++++++++++++++++++++- lib/dtsc.h | 2 ++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 03f7888f..24c013a8 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -248,6 +248,15 @@ JSON::Value & DTSC::Stream::getPacket(unsigned int num){ return buffers[num]; } +/// Returns a track element by giving the id. +JSON::Value & DTSC::Stream::getTrackById(int trackNo){ + static JSON::Value empty; + if (trackMapping.find(trackNo) != trackMapping.end()){ + return metadata["tracks"][trackMapping[trackNo]]; + } + return empty; +} + /// Returns the type of the last received packet. DTSC::datatype DTSC::Stream::lastType(){ return datapointertype; @@ -809,6 +818,15 @@ JSON::Value & DTSC::File::getJSON(){ return jsonbuffer; } +/// Returns a track element by giving the id. +JSON::Value & DTSC::File::getTrackById(int trackNo){ + static JSON::Value empty; + if (trackMapping.find(trackNo) != trackMapping.end()){ + return metadata["tracks"][trackMapping[trackNo]]; + } + return empty; +} + bool DTSC::File::seek_time(int ms, int trackNo){ seekPos tmpPos; tmpPos.trackID = trackNo; @@ -908,8 +926,12 @@ bool DTSC::File::atKeyframe(){ } void DTSC::File::selectTracks(std::set & tracks){ - currentPositions.clear(); selectedTracks = tracks; + if ( !currentPositions.size()){ + seek_time(0); + }else{ + currentPositions.clear(); + } } /// Close the file if open diff --git a/lib/dtsc.h b/lib/dtsc.h index e83ef8fc..7c903516 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -106,6 +106,7 @@ namespace DTSC { void seekNext(); std::string & getPacket(); JSON::Value & getJSON(); + JSON::Value & getTrackById(int trackNo); bool seek_time(int seconds); bool seek_time(int seconds, int trackNo); bool seek_bpos(int bpos); @@ -154,6 +155,7 @@ namespace DTSC { Stream(unsigned int buffers, unsigned int bufferTime = 0); JSON::Value metadata; JSON::Value & getPacket(unsigned int num = 0); + JSON::Value & getTrackById(int trackNo); datatype lastType(); std::string & lastData(); bool hasVideo(); From 64c97d15a31278994ec595084040046cdc038f46 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 6 Jun 2013 14:23:31 +0200 Subject: [PATCH 411/788] Working DTSC2 -> FLV --- lib/dtsc.cpp | 69 ++++++++++++++++++++++++------------------------- lib/flv_tag.cpp | 39 ++++++++++++++-------------- lib/flv_tag.h | 2 +- 3 files changed, 54 insertions(+), 56 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 24c013a8..fe87788e 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -85,9 +85,6 @@ bool DTSC::Stream::parsePacket(std::string & buffer){ } if (version == 2){ buffers.front() = JSON::fromDTMI2(buffer.substr(8)); - if (!buffers.front().isMember("datatype")){ - buffers.front()["datatype"] = metadata["tracks"][trackMapping[buffers.front()["trackid"].asInt()]]["type"]; - } } datapointertype = INVALID; if (buffers.front().isMember("data")){ @@ -95,20 +92,24 @@ bool DTSC::Stream::parsePacket(std::string & buffer){ }else{ datapointer = 0; } + std::string tmp = ""; + if (buffers.front().isMember("trackid")){ + tmp = getTrackById(buffers.front()["trackid"].asInt())["type"].asString(); + } if (buffers.front().isMember("datatype")){ - std::string tmp = buffers.front()["datatype"].asString(); - if (tmp == "video"){ - datapointertype = VIDEO; - } - if (tmp == "audio"){ - datapointertype = AUDIO; - } - if (tmp == "meta"){ - datapointertype = META; - } - if (tmp == "pause_marker"){ - datapointertype = PAUSEMARK; - } + tmp = buffers.front()["datatype"].asString(); + } + if (tmp == "video"){ + datapointertype = VIDEO; + } + if (tmp == "audio"){ + datapointertype = AUDIO; + } + if (tmp == "meta"){ + datapointertype = META; + } + if (tmp == "pause_marker"){ + datapointertype = PAUSEMARK; } buffer.erase(0, len + 8); while (buffers.size() > buffercount){ @@ -189,9 +190,6 @@ bool DTSC::Stream::parsePacket(Socket::Buffer & buffer){ } if (version == 2){ buffers.front() = JSON::fromDTMI2(wholepacket.substr(8)); - if (!buffers.front().isMember("datatype")){ - buffers.front()["datatype"] = metadata["tracks"][trackMapping[buffers.front()["trackid"].asInt()]]["type"]; - } } datapointertype = INVALID; if (buffers.front().isMember("data")){ @@ -199,20 +197,24 @@ bool DTSC::Stream::parsePacket(Socket::Buffer & buffer){ }else{ datapointer = 0; } + std::string tmp = ""; + if (buffers.front().isMember("trackid")){ + tmp = getTrackById(buffers.front()["trackid"].asInt())["type"].asString(); + } if (buffers.front().isMember("datatype")){ - std::string tmp = buffers.front()["datatype"].asString(); - if (tmp == "video"){ - datapointertype = VIDEO; - } - if (tmp == "audio"){ - datapointertype = AUDIO; - } - if (tmp == "meta"){ - datapointertype = META; - } - if (tmp == "pause_marker"){ - datapointertype = PAUSEMARK; - } + tmp = buffers.front()["datatype"].asString(); + } + if (tmp == "video"){ + datapointertype = VIDEO; + } + if (tmp == "audio"){ + datapointertype = AUDIO; + } + if (tmp == "meta"){ + datapointertype = META; + } + if (tmp == "pause_marker"){ + datapointertype = PAUSEMARK; } while (buffers.size() > buffercount){ buffers.pop_back(); @@ -795,9 +797,6 @@ void DTSC::File::seekNext(){ } if (version == 2){ jsonbuffer = JSON::fromDTMI2(strbuffer); - if (!jsonbuffer.isMember("datatype")){ - jsonbuffer["datatype"] = metadata["tracks"][trackMapping[jsonbuffer["trackid"].asInt()]]["type"]; - } }else{ jsonbuffer = JSON::fromDTMI(strbuffer); } diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index 8ca1ae2a..8ea65b70 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -350,19 +350,20 @@ FLV::Tag & FLV::Tag::operator=(const FLV::Tag& O){ /// Takes the DTSC data and makes it into FLV. bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ std::string meta_str; + JSON::Value & track = S.getTrackById(S.getPacket()["trackid"].asInt()); switch (S.lastType()){ case DTSC::VIDEO: len = S.lastData().length() + 16; - if (S.metadata.isMember("video") && S.metadata["video"].isMember("codec")){ - if (S.metadata["video"]["codec"].asString() == "H264"){ + if (track && track.isMember("codec")){ + if (track["codec"].asString() == "H264"){ len += 4; } } break; case DTSC::AUDIO: len = S.lastData().length() + 16; - if (S.metadata.isMember("audio") && S.metadata["audio"].isMember("codec")){ - if (S.metadata["audio"]["codec"].asString() == "AAC"){ + if (track && track.isMember("codec")){ + if (track["codec"].asString() == "AAC"){ len += 1; } } @@ -406,10 +407,10 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ data[15] = offset & 0xFF; } data[11] = 0; - if (S.metadata["video"]["codec"].asString() == "H264"){ + if (track.isMember("codec") && track["codec"].asString() == "H264"){ data[11] += 7; } - if (S.metadata["video"]["codec"].asString() == "H263"){ + if (track.isMember("codec") && track["codec"].asString() == "H263"){ data[11] += 2; } if (S.getPacket().isMember("keyframe")){ @@ -430,13 +431,13 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ data[12] = 1; //raw AAC data, not sequence header } data[11] = 0; - if (S.metadata["audio"]["codec"].asString() == "AAC"){ + if (track.isMember("codec") && track["codec"].asString() == "AAC"){ data[11] += 0xA0; } - if (S.metadata["audio"]["codec"].asString() == "MP3"){ + if (track.isMember("codec") && track["codec"].asString() == "MP3"){ data[11] += 0x20; } - unsigned int datarate = S.metadata["audio"]["rate"].asInt(); + unsigned int datarate = track["rate"].asInt(); if (datarate >= 44100){ data[11] += 0x0C; }else if (datarate >= 22050){ @@ -444,10 +445,10 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ }else if (datarate >= 11025){ data[11] += 0x04; } - if (S.metadata["audio"]["size"].asInt() == 16){ + if (track["size"].asInt() == 16){ data[11] += 0x02; } - if (S.metadata["audio"]["channels"].asInt() > 1){ + if (track["channels"].asInt() > 1){ data[11] += 0x01; } break; @@ -594,9 +595,7 @@ bool FLV::Tag::DTSCAudioInit(JSON::Value & audio){ /// FLV metadata loader function from DTSC. /// Takes the DTSC metadata and makes it into FLV. /// Assumes metadata is available - so check before calling! -bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S, std::string vidName, std::string audName){ - JSON::Value & videoRef = S.metadata["tracks"][vidName]; - JSON::Value & audioRef = S.metadata["tracks"][audName]; +bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S, JSON::Value & videoRef, JSON::Value & audioRef){ //Unknown? Assume AAC. if (audioRef["codec"].asString() == "?"){ audioRef["codec"] = "AAC"; @@ -617,10 +616,10 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S, std::string vidName, std::string a keys.addContent(AMF::Object("filepositions", AMF::AMF0_STRICT_ARRAY)); keys.addContent(AMF::Object("times", AMF::AMF0_STRICT_ARRAY)); int total_byterate = 0; - if (vidName != ""){ + if (videoRef){ total_byterate += videoRef["bps"].asInt(); } - if (audName != ""){ + if (audioRef){ total_byterate += audioRef["bps"].asInt(); } for (int i = 0; i < S.metadata["length"].asInt(); ++i){ //for each second in the file @@ -629,7 +628,7 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S, std::string vidName, std::string a } amfdata.getContentP(1)->addContent(keys); } - if (vidName != ""){ + if (videoRef){ amfdata.getContentP(1)->addContent(AMF::Object("hasVideo", 1, AMF::AMF0_BOOL)); if (videoRef["codec"].asString() == "H264"){ amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", (std::string)"avc1")); @@ -653,7 +652,7 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S, std::string vidName, std::string a amfdata.getContentP(1)->addContent(AMF::Object("videodatarate", (double)videoRef["bps"].asInt() * 128.0, AMF::AMF0_NUMBER)); } } - if (audName != ""){ + if (audioRef){ amfdata.getContentP(1)->addContent(AMF::Object("hasAudio", 1, AMF::AMF0_BOOL)); amfdata.getContentP(1)->addContent(AMF::Object("audiodelay", 0, AMF::AMF0_NUMBER)); if (audioRef["codec"].asString() == "AAC"){ @@ -677,7 +676,7 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S, std::string vidName, std::string a } AMF::Object trinfo = AMF::Object("trackinfo", AMF::AMF0_STRICT_ARRAY); int i = 0; - if (audName != ""){ + if (audioRef){ trinfo.addContent(AMF::Object("", AMF::AMF0_OBJECT)); trinfo.getContentP(i)->addContent( AMF::Object("length", ((double)S.metadata["length"].asInt()) * ((double)audioRef["rate"].asInt()), AMF::AMF0_NUMBER)); @@ -691,7 +690,7 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S, std::string vidName, std::string a } ++i; } - if (vidName != ""){ + if (videoRef){ trinfo.addContent(AMF::Object("", AMF::AMF0_OBJECT)); trinfo.getContentP(i)->addContent( AMF::Object("length", ((double)S.metadata["length"].asInt()) * ((double)videoRef["fkps"].asInt() / 1000.0), AMF::AMF0_NUMBER)); diff --git a/lib/flv_tag.h b/lib/flv_tag.h index 5433068a..b2815aee 100644 --- a/lib/flv_tag.h +++ b/lib/flv_tag.h @@ -49,7 +49,7 @@ namespace FLV { bool DTSCVideoInit(JSON::Value & video); bool DTSCAudioInit(DTSC::Stream & S); bool DTSCAudioInit(JSON::Value & audio); - bool DTSCMetaInit(DTSC::Stream & S, std::string vidName = "", std::string audName = ""); + bool DTSCMetaInit(DTSC::Stream & S, JSON::Value & videoRef, JSON::Value & audioRef); JSON::Value toJSON(JSON::Value & metadata); bool MemLoader(char * D, unsigned int S, unsigned int & P); bool FileLoader(FILE * f); From 68f44c3d29e9a29aad12448e930117b6bda5aa86 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 6 Jun 2013 14:49:03 +0200 Subject: [PATCH 412/788] Fixed DTSC::Stream::parsePacket with string buffers instead of sockets. --- lib/dtsc.cpp | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index fe87788e..786140a2 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -43,25 +43,15 @@ bool DTSC::Stream::parsePacket(std::string & buffer){ uint32_t len; static bool syncing = false; if (buffer.length() > 8){ - int version = 0; - if (memcmp(buffer.c_str(), DTSC::Magic_Packet, 4) == 0){ - version = 1; - } - if (memcmp(buffer.c_str(), DTSC::Magic_Packet2, 4) == 0){ - version = 2; - } if (memcmp(buffer.c_str(), DTSC::Magic_Header, 4) == 0){ len = ntohl(((uint32_t *)buffer.c_str())[1]); if (buffer.length() < len + 8){ return false; } unsigned int i = 0; - if (version == 1){ - metadata = JSON::fromDTMI((unsigned char*)buffer.c_str() + 8, len, i); - }else{ - metadata = JSON::fromDTMI2(buffer.substr(8)); - } + metadata = JSON::fromDTMI((unsigned char*)buffer.c_str() + 8, len, i); metadata.removeMember("moreheader"); + metadata.netPrepare(); trackMapping.clear(); if (metadata.isMember("tracks")){ for (JSON::ObjIter it = metadata["tracks"].ObjBegin(); it != metadata["tracks"].ObjEnd(); it++){ @@ -73,6 +63,13 @@ bool DTSC::Stream::parsePacket(std::string & buffer){ return false; } } + int version = 0; + if (memcmp(buffer.c_str(), DTSC::Magic_Packet, 4) == 0){ + version = 1; + } + if (memcmp(buffer.c_str(), DTSC::Magic_Packet2, 4) == 0){ + version = 2; + } if (version){ len = ntohl(((uint32_t *)buffer.c_str())[1]); if (buffer.length() < len + 8){ From 5db07ea5ed9b103487499b9b4d257e6e999ab07e Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Tue, 19 Feb 2013 10:56:33 +0100 Subject: [PATCH 413/788] Converter API New Version for mistinfo --- lib/converter.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++++ lib/converter.h | 18 ++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 lib/converter.cpp create mode 100644 lib/converter.h diff --git a/lib/converter.cpp b/lib/converter.cpp new file mode 100644 index 00000000..a0f27485 --- /dev/null +++ b/lib/converter.cpp @@ -0,0 +1,44 @@ +#include +#include + +#include "converter.h" +#include "procs.h" + +namespace Converter { + + Converter::Converter(){ + fillFFMpegEncoders(); + } + + void Converter::fillFFMpegEncoders(){ + std::vector cmd; + cmd.push_back("ffmpeg"); + cmd.push_back("-encoders"); + int outFD = -1; + Util::Procs::StartPiped("FFMpegInfo", cmd, 0, &outFD, 0); + FILE * outFile = fdopen( outFD, "r" ); + char * fileBuf = 0; + size_t fileBufLen = 0; + while ( !(feof(outFile) || ferror(outFile))){ + getline(&fileBuf, &fileBufLen, outFile); + 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 ); + } + + + converterInfo & Converter::getCodecs() { + return allCodecs; + } +} diff --git a/lib/converter.h b/lib/converter.h new file mode 100644 index 00000000..31b18b80 --- /dev/null +++ b/lib/converter.h @@ -0,0 +1,18 @@ +#include +#include + +#include "json.h" + +typedef std::map codecInfo; +typedef std::map converterInfo; + +namespace Converter { + class Converter { + public: + Converter(); + converterInfo & getCodecs(); + private: + void fillFFMpegEncoders(); + converterInfo allCodecs; + }; +} From 21a9aaab98d6fbc9b82521923765b4e44dfcfc58 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Wed, 27 Feb 2013 15:39:01 +0100 Subject: [PATCH 414/788] Working encoder detection --- lib/Makefile.am | 5 ++--- lib/converter.cpp | 28 ++++++++++++++++++++-------- lib/converter.h | 1 + lib/procs.h | 1 + 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/lib/Makefile.am b/lib/Makefile.am index 0c43b95a..9f68f05d 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,5 +1,5 @@ lib_LTLIBRARIES=libmist-1.0.la -libmist_1_0_la_SOURCES=amf.h amf.cpp auth.h auth.cpp base64.h base64.cpp config.h config.cpp dtsc.h dtsc.cpp flv_tag.h flv_tag.cpp http_parser.h http_parser.cpp json.h json.cpp procs.h procs.cpp rtmpchunks.h rtmpchunks.cpp socket.h socket.cpp mp4.h mp4.cpp ftp.h ftp.cpp filesystem.h filesystem.cpp stream.h stream.cpp timing.h timing.cpp ts_packet.cpp ts_packet.h +libmist_1_0_la_SOURCES=amf.h amf.cpp auth.h auth.cpp base64.h base64.cpp config.h config.cpp dtsc.h dtsc.cpp flv_tag.h flv_tag.cpp http_parser.h http_parser.cpp json.h json.cpp procs.h procs.cpp rtmpchunks.h rtmpchunks.cpp socket.h socket.cpp mp4.h mp4.cpp ftp.h ftp.cpp filesystem.h filesystem.cpp stream.h stream.cpp timing.h timing.cpp ts_packet.cpp ts_packet.h converter.cpp converter.h libmist_1_0_la_LDFLAGS = -version-info 5:1:2 libmist_1_0_la_CPPFLAGS=$(DEPS_CFLAGS) $(global_CFLAGS) libmist_1_0_la_LIBADD=$(DEPS_LIBS) $(CLOCK_LIB) @@ -8,5 +8,4 @@ pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = mist-1.0.pc library_includedir=$(includedir)/mist-1.0/mist -library_include_HEADERS = amf.h auth.h base64.h config.h dtsc.h flv_tag.h http_parser.h json.h procs.h rtmpchunks.h socket.h mp4.h ftp.h filesystem.h stream.h timing.h nal.h ts_packet.h - +library_include_HEADERS = amf.h auth.h base64.h config.h dtsc.h flv_tag.h http_parser.h json.h procs.h rtmpchunks.h socket.h mp4.h ftp.h filesystem.h stream.h timing.h nal.h ts_packet.h converter.h diff --git a/lib/converter.cpp b/lib/converter.cpp index a0f27485..65320ab1 100644 --- a/lib/converter.cpp +++ b/lib/converter.cpp @@ -1,6 +1,8 @@ #include +#include #include +#include "timing.h" #include "converter.h" #include "procs.h" @@ -10,17 +12,18 @@ namespace Converter { fillFFMpegEncoders(); } - void Converter::fillFFMpegEncoders(){ - std::vector cmd; - cmd.push_back("ffmpeg"); - cmd.push_back("-encoders"); + void Converter::fillFFMpegEncoders(){ + char ** cmd = (char**)malloc(3*sizeof(char*)); + cmd[0] = "ffmpeg"; + cmd[1] = "-encoders"; + cmd[2] = NULL; int outFD = -1; Util::Procs::StartPiped("FFMpegInfo", cmd, 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); + 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"; @@ -37,8 +40,17 @@ namespace Converter { fclose( outFile ); } - - converterInfo & Converter::getCodecs() { + converterInfo & Converter::getCodecs(){ return allCodecs; } + + 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++){ + Result[convIt->first][codIt->first] = codIt->second; + } + } + return Result; + } } diff --git a/lib/converter.h b/lib/converter.h index 31b18b80..b572e52e 100644 --- a/lib/converter.h +++ b/lib/converter.h @@ -11,6 +11,7 @@ namespace Converter { public: Converter(); converterInfo & getCodecs(); + JSON::Value getEncoders(); private: void fillFFMpegEncoders(); converterInfo allCodecs; diff --git a/lib/procs.h b/lib/procs.h index 4a0343da..32fdd46d 100644 --- a/lib/procs.h +++ b/lib/procs.h @@ -5,6 +5,7 @@ #include #include #include +#include /// Contains utility code, not directly related to streaming media namespace Util { From 4fe4fca0815c49508be613df57948d6d29fcefb0 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Wed, 6 Mar 2013 11:53:31 +0100 Subject: [PATCH 415/788] Updated to allow for basic conversion. --- lib/converter.cpp | 185 +++++++++++++++++++++++++++++++++++++++++++--- lib/converter.h | 7 ++ 2 files changed, 183 insertions(+), 9 deletions(-) diff --git a/lib/converter.cpp b/lib/converter.cpp index 65320ab1..5c6a5974 100644 --- a/lib/converter.cpp +++ b/lib/converter.cpp @@ -1,10 +1,15 @@ #include #include #include +#include +#include +#include +#include #include "timing.h" #include "converter.h" #include "procs.h" +#include "config.h" namespace Converter { @@ -12,13 +17,14 @@ namespace Converter { fillFFMpegEncoders(); } - void Converter::fillFFMpegEncoders(){ - char ** cmd = (char**)malloc(3*sizeof(char*)); - cmd[0] = "ffmpeg"; - cmd[1] = "-encoders"; - cmd[2] = NULL; + void Converter::fillFFMpegEncoders(){ + std::vector 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, &outFD, 0); + 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; @@ -45,12 +51,173 @@ namespace Converter { } JSON::Value Converter::getEncoders(){ - JSON::Value Result; + 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++){ - Result[convIt->first][codIt->first] = codIt->second; + result[convIt->first][codIt->first] = codIt->second; } } - return Result; + return result; + } + + JSON::Value Converter::queryPath(std::string myPath){ + std::vector cmd; + cmd.reserve(3); + cmd.push_back((char*)"MistInfo"); + cmd.push_back(NULL); + cmd.push_back(NULL); + fprintf( stderr, "Querying %s\n", myPath.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 myPath = std::string(myPath + (myPath[myPath.size()-1] == '/' ? "" : "/") + entry->d_name); + cmd[1] = (char*)myPath.c_str(); + int outFD = -1; + Util::Procs::StartPiped("MistInfo", &cmd[0], 0, &outFD, 0); + while( Util::Procs::isActive("MistInfo")){ Util::sleep(10); } + FILE * outFile = fdopen( outFD, "r" ); + char * fileBuf = 0; + size_t fileBufLen = 0; + getline(&fileBuf, &fileBufLen, outFile); + std::string line = fileBuf; + result[fileName] = JSON::fromString(std::string(fileBuf)); + if ( !result[fileName]){ + result.removeMember(fileName); + } + fclose( outFile ); + } + } + return result; + } + + 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; + } + 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"]; + 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"].isMember("kfps")){ + encoderCommand << "-r " << parameters["video"]["kfps"].asInt() / 1000 << " "; + } + ///\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 -"; + } + Util::Procs::Start(name,encoderCommand.str(),Util::getMyPath() + "MistFLV2DTSC -o " + parameters["output"].asString()); + allConversions[name] = parameters; + } + + void Converter::updateStatus(){ + if (allConversions.size()){ + std::map::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)){ + continue; + } + if (cIt->second["output"].asString().find(".dtsc") != std::string::npos){ + statusHistory[cIt->first] = "Conversion succesful, running DTSCFix"; + Util::Procs::Start(cIt->first+"DTSCFix",Util::getMyPath() + "MistDTSCFix " + cIt->second["output"].asString()); + }else{ + statusHistory[cIt->first] = "Conversion succesful"; + } + allConversions.erase(cIt); + hasChanged = true; + break; + } + } + } + if(statusHistory.size()){ + std::map::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 succesful"; + } + } + } + } + + JSON::Value Converter::getStatus(){ + updateStatus(); + JSON::Value result; + if (allConversions.size()){ + for (std::map::iterator cIt = allConversions.begin(); cIt != allConversions.end(); cIt++){ + result[cIt->first] = "Converting"; + } + } + if (statusHistory.size()){ + std::map::iterator sIt; + for (sIt = statusHistory.begin(); sIt != statusHistory.end(); sIt++){ + result[sIt->first] = sIt->second; + } + } + return result; + } + + void Converter::clearStatus(){ + statusHistory.clear(); } } diff --git a/lib/converter.h b/lib/converter.h index b572e52e..9f3d4f28 100644 --- a/lib/converter.h +++ b/lib/converter.h @@ -12,8 +12,15 @@ namespace Converter { 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(); private: void fillFFMpegEncoders(); converterInfo allCodecs; + std::map allConversions; + std::map statusHistory; }; } From dce6d4b0303aadf398d3b8defe8e3c6a281886f0 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Thu, 7 Mar 2013 12:19:15 +0100 Subject: [PATCH 416/788] Updates to StartPiped (added std::string version and a 2-process variant), also updated conversion to allow for status checks (not fully functional yet) --- lib/converter.cpp | 44 +++++++++++++++++++++++++++++++++++++-- lib/procs.cpp | 53 +++++++++++++++++++++++++++++++++++++++++++++++ lib/procs.h | 2 ++ 3 files changed, 97 insertions(+), 2 deletions(-) diff --git a/lib/converter.cpp b/lib/converter.cpp index 5c6a5974..425a4a76 100644 --- a/lib/converter.cpp +++ b/lib/converter.cpp @@ -161,7 +161,9 @@ namespace Converter { } encoderCommand << "-f flv -"; } - Util::Procs::Start(name,encoderCommand.str(),Util::getMyPath() + "MistFLV2DTSC -o " + parameters["output"].asString()); + 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; } @@ -200,12 +202,50 @@ namespace Converter { } } + JSON::Value 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; + } + JSON::Value Converter::getStatus(){ updateStatus(); JSON::Value result; if (allConversions.size()){ for (std::map::iterator cIt = allConversions.begin(); cIt != allConversions.end(); cIt++){ - result[cIt->first] = "Converting"; + 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(line.find("frame") != 0);//"frame" is the fist word on an actual status line of ffmpeg + result[cIt->first] = parseFFMpegStatus( line ); + result[cIt->first]["duration"] = cIt->second["duration"]; + result[cIt->first]["progress"] = (result[cIt->first]["time"].asInt() * 100) / cIt->second["duration"].asInt(); + free(fileBuf); + fclose(statusFile); } } if (statusHistory.size()){ diff --git a/lib/procs.cpp b/lib/procs.cpp index 810161b6..f0a07913 100644 --- a/lib/procs.cpp +++ b/lib/procs.cpp @@ -528,6 +528,59 @@ pid_t Util::Procs::StartPiped(std::string name, char * argv[], int * fdin, int * 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){ +#if DEBUG >= 1 + std::cerr << "Process " << name << " could not be started. Pipe creation failed." << std::endl; +#endif + 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){ diff --git a/lib/procs.h b/lib/procs.h index 32fdd46d..4f24ff5c 100644 --- a/lib/procs.h +++ b/lib/procs.h @@ -27,6 +27,8 @@ namespace Util { 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(std::string name, char * 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 StopAll(); From 9a9b5f8fe6a79e4aacf7f83fec3f2455ac3b6814 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Thu, 7 Mar 2013 16:15:08 +0100 Subject: [PATCH 417/788] Spelling corrections, FFMPEG-Error detection. --- lib/converter.cpp | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/lib/converter.cpp b/lib/converter.cpp index 425a4a76..bc0e224b 100644 --- a/lib/converter.cpp +++ b/lib/converter.cpp @@ -177,11 +177,9 @@ namespace Converter { if (Util::Procs::isActive(cIt->first)){ continue; } - if (cIt->second["output"].asString().find(".dtsc") != std::string::npos){ - statusHistory[cIt->first] = "Conversion succesful, running DTSCFix"; + 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()); - }else{ - statusHistory[cIt->first] = "Conversion succesful"; } allConversions.erase(cIt); hasChanged = true; @@ -196,7 +194,7 @@ namespace Converter { if (Util::Procs::isActive(sIt->first+"DTSCFIX")){ continue; } - statusHistory[sIt->first] = "Conversion succesful"; + statusHistory[sIt->first] = "Conversion successful"; } } } @@ -240,10 +238,17 @@ namespace Converter { totalTime += atoi(line.substr(curOffset+9, 2).c_str()) * 10; cIt->second["duration"] = totalTime; } - }while(line.find("frame") != 0);//"frame" is the fist word on an actual status line of ffmpeg - result[cIt->first] = parseFFMpegStatus( line ); - result[cIt->first]["duration"] = cIt->second["duration"]; - result[cIt->first]["progress"] = (result[cIt->first]["time"].asInt() * 100) / cIt->second["duration"].asInt(); + }while ( !feof(statusFile) && line.find("frame") != 0);//"frame" is the fist word on an actual status line of ffmpeg + if ( !feof(statusFile)){ + result[cIt->first] = parseFFMpegStatus( line ); + result[cIt->first]["duration"] = cIt->second["duration"]; + result[cIt->first]["progress"] = (result[cIt->first]["time"].asInt() * 100) / cIt->second["duration"].asInt(); + }else{ + line.erase(line.end()-1); + line = line.substr( line.rfind("\n") + 1 ); + result[cIt->first] = line; + statusHistory[cIt->first] = line; + } free(fileBuf); fclose(statusFile); } From a6299235d12eec3d87c42aca6e0cf0e656bf7286 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Mon, 11 Mar 2013 14:43:47 +0100 Subject: [PATCH 418/788] Working conversion --- lib/converter.cpp | 93 +++++++++++++++++++++++++++-------------------- lib/converter.h | 1 + 2 files changed, 54 insertions(+), 40 deletions(-) diff --git a/lib/converter.cpp b/lib/converter.cpp index bc0e224b..e31dc65f 100644 --- a/lib/converter.cpp +++ b/lib/converter.cpp @@ -117,6 +117,16 @@ namespace Converter { statusHistory[name] = "Can not find encoder " + parameters["encoder"]; 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 "; @@ -134,6 +144,9 @@ namespace Converter { if (parameters["video"].isMember("kfps")){ encoderCommand << "-r " << parameters["video"]["kfps"].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{ @@ -175,14 +188,45 @@ namespace Converter { hasChanged = false; for (cIt = allConversions.begin(); cIt != allConversions.end(); cIt++){ if (Util::Procs::isActive(cIt->first)){ - continue; + 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; } - 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; } } @@ -200,7 +244,7 @@ namespace Converter { } } - JSON::Value parseFFMpegStatus(std::string statusLine){ + 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() ); @@ -219,38 +263,7 @@ namespace Converter { JSON::Value result; if (allConversions.size()){ for (std::map::iterator cIt = allConversions.begin(); cIt != allConversions.end(); cIt++){ - 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)){ - result[cIt->first] = parseFFMpegStatus( line ); - result[cIt->first]["duration"] = cIt->second["duration"]; - result[cIt->first]["progress"] = (result[cIt->first]["time"].asInt() * 100) / cIt->second["duration"].asInt(); - }else{ - line.erase(line.end()-1); - line = line.substr( line.rfind("\n") + 1 ); - result[cIt->first] = line; - statusHistory[cIt->first] = line; - } - free(fileBuf); - fclose(statusFile); + result[cIt->first] = cIt->second["status"]; } } if (statusHistory.size()){ diff --git a/lib/converter.h b/lib/converter.h index 9f3d4f28..41a89170 100644 --- a/lib/converter.h +++ b/lib/converter.h @@ -17,6 +17,7 @@ namespace Converter { void updateStatus(); JSON::Value getStatus(); void clearStatus(); + JSON::Value parseFFMpegStatus(std::string statusLine); private: void fillFFMpegEncoders(); converterInfo allCodecs; From e2e3f64d899053ec0675ed966587641d9ea93a0e Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 6 Jun 2013 15:33:45 +0200 Subject: [PATCH 419/788] Fixed typo in converter error message. --- lib/converter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/converter.cpp b/lib/converter.cpp index e31dc65f..d2755221 100644 --- a/lib/converter.cpp +++ b/lib/converter.cpp @@ -114,7 +114,7 @@ namespace Converter { return; } if (allCodecs.find(parameters["encoder"]) == allCodecs.end()){ - statusHistory[name] = "Can not find encoder " + parameters["encoder"]; + statusHistory[name] = "Can not find encoder " + parameters["encoder"].asString(); return; } if (parameters.isMember("video")){ From 10dc56c3380971a4961d62c10ef756c8537f7c61 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 10 Jun 2013 15:18:34 +0200 Subject: [PATCH 420/788] Fixed a race condition in process status checking. --- lib/procs.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/procs.cpp b/lib/procs.cpp index f0a07913..5b61ea18 100644 --- a/lib/procs.cpp +++ b/lib/procs.cpp @@ -621,7 +621,9 @@ bool Util::Procs::isActive(std::string name){ std::map::iterator it; for (it = plist.begin(); it != plist.end(); it++){ if (( *it).second == name){ - return true; + if (kill(( *it).first, 0) == 0){ + return true; + } } } return false; @@ -629,7 +631,7 @@ bool Util::Procs::isActive(std::string name){ /// Returns true if a process with this PID is currently active. bool Util::Procs::isActive(pid_t name){ - return (plist.count(name) == 1); + return (plist.count(name) == 1) && (kill(name, 0) == 0); } /// Gets PID for this named process, if active. From 54154871baabf2b374ab07b28f5acab3bbbaf476 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 18 Jun 2013 14:18:57 +0200 Subject: [PATCH 421/788] Fixed RTMP timestamp usage. --- lib/rtmpchunks.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/rtmpchunks.cpp b/lib/rtmpchunks.cpp index 887d62f5..a9dd78cb 100644 --- a/lib/rtmpchunks.cpp +++ b/lib/rtmpchunks.cpp @@ -391,7 +391,7 @@ RTMPStream::Chunk::Chunk(){ 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::epoch(); + ch.timestamp = Util::getMS(); ch.len = data.size(); ch.real_len = data.size(); ch.len_left = 0; @@ -438,7 +438,7 @@ std::string & RTMPStream::SendMedia(FLV::Tag & tag){ std::string & RTMPStream::SendCTL(unsigned char type, unsigned int data){ static RTMPStream::Chunk ch; ch.cs_id = 2; - ch.timestamp = Util::epoch(); + ch.timestamp = Util::getMS(); ch.len = 4; ch.real_len = 4; ch.len_left = 0; @@ -453,7 +453,7 @@ std::string & RTMPStream::SendCTL(unsigned char type, unsigned int data){ std::string & RTMPStream::SendCTL(unsigned char type, unsigned int data, unsigned char data2){ static RTMPStream::Chunk ch; ch.cs_id = 2; - ch.timestamp = Util::epoch(); + ch.timestamp = Util::getMS(); ch.len = 5; ch.real_len = 5; ch.len_left = 0; @@ -469,7 +469,7 @@ std::string & RTMPStream::SendCTL(unsigned char type, unsigned int data, unsigne std::string & RTMPStream::SendUSR(unsigned char type, unsigned int data){ static RTMPStream::Chunk ch; ch.cs_id = 2; - ch.timestamp = Util::epoch(); + ch.timestamp = Util::getMS(); ch.len = 6; ch.real_len = 6; ch.len_left = 0; @@ -486,7 +486,7 @@ std::string & RTMPStream::SendUSR(unsigned char type, unsigned int data){ std::string & RTMPStream::SendUSR(unsigned char type, unsigned int data, unsigned int data2){ static RTMPStream::Chunk ch; ch.cs_id = 2; - ch.timestamp = Util::epoch(); + ch.timestamp = Util::getMS(); ch.len = 10; ch.real_len = 10; ch.len_left = 0; From 0c65ba87df7fe2ba7bece61ae223e4c1bd9db707 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Tue, 11 Jun 2013 09:11:10 +0200 Subject: [PATCH 422/788] Fixed a bug in the conversion API --- lib/converter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/converter.cpp b/lib/converter.cpp index d2755221..1b83d29b 100644 --- a/lib/converter.cpp +++ b/lib/converter.cpp @@ -141,8 +141,8 @@ namespace Converter { return; } encoderCommand << "-vcodec " << vidCodec->first << " "; - if (parameters["video"].isMember("kfps")){ - encoderCommand << "-r " << parameters["video"]["kfps"].asInt() / 1000 << " "; + 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() << " "; From ae0da6955c798b223d48f837ea2c8185a8d28970 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Mon, 17 Jun 2013 13:32:50 +0200 Subject: [PATCH 423/788] initial (broken) commit --- lib/dtsc.cpp | 156 ++++++++++++++++++--------------------------------- lib/dtsc.h | 31 ++++++++-- 2 files changed, 81 insertions(+), 106 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 786140a2..e319cd66 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -32,7 +32,7 @@ DTSC::Stream::Stream(unsigned int rbuffers, unsigned int bufferTime){ /// Returns the time in milliseconds of the last received packet. /// This is _not_ the time this packet was received, only the stored time. unsigned int DTSC::Stream::getTime(){ - return buffers.front()["time"].asInt(); + return buffers.rbegin()->second["time"].asInt(); } /// Attempts to parse a packet from the given std::string buffer. @@ -75,44 +75,16 @@ bool DTSC::Stream::parsePacket(std::string & buffer){ if (buffer.length() < len + 8){ return false; } - buffers.push_front(JSON::Value()); + JSON::Value newPack; + livePos newPos; unsigned int i = 0; if (version == 1){ - buffers.front() = JSON::fromDTMI((unsigned char*)buffer.c_str() + 8, len, i); + newPack = JSON::fromDTMI((unsigned char*)buffer.c_str() + 8, len, i); } if (version == 2){ - buffers.front() = JSON::fromDTMI2(buffer.substr(8)); + newPack = JSON::fromDTMI2(buffer.substr(8)); } - datapointertype = INVALID; - if (buffers.front().isMember("data")){ - datapointer = &(buffers.front()["data"].strVal); - }else{ - datapointer = 0; - } - std::string tmp = ""; - if (buffers.front().isMember("trackid")){ - tmp = getTrackById(buffers.front()["trackid"].asInt())["type"].asString(); - } - if (buffers.front().isMember("datatype")){ - tmp = buffers.front()["datatype"].asString(); - } - if (tmp == "video"){ - datapointertype = VIDEO; - } - if (tmp == "audio"){ - datapointertype = AUDIO; - } - if (tmp == "meta"){ - datapointertype = META; - } - if (tmp == "pause_marker"){ - datapointertype = PAUSEMARK; - } - buffer.erase(0, len + 8); - while (buffers.size() > buffercount){ - buffers.pop_back(); - } - advanceRings(); + addPacket(newPack); syncing = false; return true; } @@ -179,42 +151,15 @@ bool DTSC::Stream::parsePacket(Socket::Buffer & buffer){ if ( !buffer.available(len + 8)){ return false; } - buffers.push_front(JSON::Value()); + JSON::Value newPack; + livePos newPos; unsigned int i = 0; std::string wholepacket = buffer.remove(len + 8); if (version == 1){ - buffers.front() = JSON::fromDTMI((unsigned char*)wholepacket.c_str() + 8, len, i); + newPack = JSON::fromDTMI((unsigned char*)wholepacket.c_str() + 8, len, i); } if (version == 2){ - buffers.front() = JSON::fromDTMI2(wholepacket.substr(8)); - } - datapointertype = INVALID; - if (buffers.front().isMember("data")){ - datapointer = &(buffers.front()["data"].strVal); - }else{ - datapointer = 0; - } - std::string tmp = ""; - if (buffers.front().isMember("trackid")){ - tmp = getTrackById(buffers.front()["trackid"].asInt())["type"].asString(); - } - if (buffers.front().isMember("datatype")){ - tmp = buffers.front()["datatype"].asString(); - } - if (tmp == "video"){ - datapointertype = VIDEO; - } - if (tmp == "audio"){ - datapointertype = AUDIO; - } - if (tmp == "meta"){ - datapointertype = META; - } - if (tmp == "pause_marker"){ - datapointertype = PAUSEMARK; - } - while (buffers.size() > buffercount){ - buffers.pop_back(); + newPack = JSON::fromDTMI2(wholepacket.substr(8)); } advanceRings(); syncing = false; @@ -231,6 +176,48 @@ bool DTSC::Stream::parsePacket(Socket::Buffer & buffer){ return false; } +void DTSC::Stream::addPacket(JSON::Value & newPack){ + livePos newPos; + newPos.trackID = newPack["trackid"].asInt(); + newPos.seekTime = newPack["time"].asInt(); + buffers[newPos] = newPack; + datapointertype = INVALID; + ///\todo Save keyframes when they arrive. + if (newPack.isMember("data")){ + datapointer = &(buffers[newPos]["data"].strVal); + }else{ + datapointer = 0; + } + std::string tmp = ""; + if (newPack.isMember("trackid")){ + tmp = getTrackById(newPack["trackid"].asInt())["type"].asString(); + } + if (newPack.isMember("datatype")){ + tmp = newPack["datatype"].asString(); + } + if (tmp == "video"){ + datapointertype = VIDEO; + } + if (tmp == "audio"){ + datapointertype = AUDIO; + } + if (tmp == "meta"){ + datapointertype = META; + } + if (tmp == "pause_marker"){ + datapointertype = PAUSEMARK; + } + while (buffers.size() > buffercount){ + if (buffers.begin()->second.isMember("keyframe")){ + std::string track = trackMapping[buffers.begin()->first.trackID]; + keyframes[buffers.begin()->first.trackID].erase(buffers.begin()->first); + int keySize = metadata["tracks"][track]["keys"].size(); + metadata["tracks"][track]["keys"].shrink(keySize - 1); + } + buffers.erase(buffers.begin()); + } +} + /// Returns a direct pointer to the data attribute of the last received packet, if available. /// Returns NULL if no valid pointer or packet is available. std::string & DTSC::Stream::lastData(){ @@ -239,9 +226,9 @@ std::string & DTSC::Stream::lastData(){ /// Returns the packet in this buffer number. /// \arg num Buffer number. -JSON::Value & DTSC::Stream::getPacket(unsigned int num){ +JSON::Value & DTSC::Stream::getPacket(livePos num){ static JSON::Value empty; - if (num >= buffers.size()){ + if (buffers.find(num) == buffers.end()){ return empty; } return buffers[num]; @@ -276,9 +263,9 @@ void DTSC::Stream::setBufferTime(unsigned int ms){ } /// Returns a packed DTSC packet, ready to sent over the network. -std::string & DTSC::Stream::outPacket(unsigned int num){ +std::string & DTSC::Stream::outPacket(livePos num){ static std::string emptystring; - if (num >= buffers.size() || !buffers[num].isObject()) return emptystring; + if (buffers.find(num) == buffers.end() || !buffers[num].isObject()) return emptystring; return buffers[num].toNetPacked(); } @@ -291,37 +278,6 @@ std::string & DTSC::Stream::outHeader(){ /// Also updates the internal keyframes ring, as well as marking rings as starved if they are. /// Unsets waiting rings, updating them with their new buffer number. void DTSC::Stream::advanceRings(){ - std::deque::iterator dit; - std::set::iterator sit; - if (rings.size()){ - for (sit = rings.begin(); sit != rings.end(); sit++){ - ( *sit)->b++; - if (( *sit)->waiting){ - ( *sit)->waiting = false; - ( *sit)->b = 0; - } - if (( *sit)->starved || (( *sit)->b >= buffers.size())){ - ( *sit)->starved = true; - ( *sit)->b = 0; - } - } - } - if (keyframes.size()){ - for (dit = keyframes.begin(); dit != keyframes.end(); dit++){ - dit->b++; - } - bool repeat; - do{ - repeat = false; - for (dit = keyframes.begin(); dit != keyframes.end(); dit++){ - if (dit->b >= buffers.size()){ - keyframes.erase(dit); - repeat = true; - break; - } - } - }while (repeat); - } static int fragNum = 1; static unsigned int lastkeytime = 4242; if ((lastType() == VIDEO && buffers.front().isMember("keyframe")) || (!metadata.isMember("video") && buffers.front()["time"].asInt() / 2000 != lastkeytime)){ diff --git a/lib/dtsc.h b/lib/dtsc.h index 7c903516..65763097 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -133,12 +133,31 @@ namespace DTSC { }; //FileWriter + /// A simple structure used for ordering byte seek positions. + struct livePos { + bool operator < (const livePos& 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; + unsigned int trackID; + }; + /// A part from the DTSC::Stream ringbuffer. /// Holds information about a buffer that will stay consistent class Ring{ public: Ring(unsigned int v); - volatile unsigned int b; ///< Holds current number of buffer. May and is intended to change unexpectedly! + volatile 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. @@ -154,7 +173,7 @@ namespace DTSC { ~Stream(); Stream(unsigned int buffers, unsigned int bufferTime = 0); JSON::Value metadata; - JSON::Value & getPacket(unsigned int num = 0); + JSON::Value & getPacket(livePos num); JSON::Value & getTrackById(int trackNo); datatype lastType(); std::string & lastData(); @@ -162,7 +181,7 @@ namespace DTSC { bool hasAudio(); bool parsePacket(std::string & buffer); bool parsePacket(Socket::Buffer & buffer); - std::string & outPacket(unsigned int num); + std::string & outPacket(livePos num); std::string & outHeader(); Ring * getRing(); unsigned int getTime(); @@ -174,11 +193,11 @@ namespace DTSC { unsigned int frameSeek(unsigned int frameno); void setBufferTime(unsigned int ms); private: - std::deque buffers; - std::set rings; - std::deque keyframes; + std::map buffers; + std::map > keyframes; void advanceRings(); void updateRingHeaders(); + void addPacket(JSON::Value & newPack); std::string * datapointer; datatype datapointertype; unsigned int buffercount; From 8ddc1ab884d626ebe79db1ae8e4ef13a3d72e4cc Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Wed, 19 Jun 2013 11:06:56 +0200 Subject: [PATCH 424/788] Edits for live multibitrate --- lib/dtsc.cpp | 164 ++++++++++++++++----------------------------------- lib/dtsc.h | 28 ++++++--- 2 files changed, 71 insertions(+), 121 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index e319cd66..cb212c65 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -76,7 +76,6 @@ bool DTSC::Stream::parsePacket(std::string & buffer){ return false; } JSON::Value newPack; - livePos newPos; unsigned int i = 0; if (version == 1){ newPack = JSON::fromDTMI((unsigned char*)buffer.c_str() + 8, len, i); @@ -152,7 +151,6 @@ bool DTSC::Stream::parsePacket(Socket::Buffer & buffer){ return false; } JSON::Value newPack; - livePos newPos; unsigned int i = 0; std::string wholepacket = buffer.remove(len + 8); if (version == 1){ @@ -161,7 +159,7 @@ bool DTSC::Stream::parsePacket(Socket::Buffer & buffer){ if (version == 2){ newPack = JSON::fromDTMI2(wholepacket.substr(8)); } - advanceRings(); + addPacket(newPack); syncing = false; return true; } @@ -207,6 +205,25 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ if (tmp == "pause_marker"){ datapointertype = PAUSEMARK; } + int keySize = metadata["tracks"][trackMapping[newPos.trackID]]["keys"].size(); + if (newPack.isMember("keyframe") || (keySize && ((newPack["time"].asInt() - 2000) > metadata["tracks"][trackMapping[newPos.trackID]]["keys"][keySize - 1]["time"].asInt())) || (!keySize)){ + keyframes[newPos.trackID].insert(newPos); + JSON::Value key; + key["time"] = newPack["time"]; + if (keySize){ + key["num"] = (*(metadata["tracks"][trackMapping[newPos.trackID]]["keys"].ArrEnd()--))["num"].asInt() + 1; + }else{ + key["num"] = 1; + } + } + unsigned int timeBuffered = 0; + if (keySize > 1){ + //increase buffer size if no keyframes available or too little time available + timeBuffered = (buffers.end()--)->second["time"].asInt() - buffers.begin()->second["time"].asInt(); + } + if (buffercount > 1 && (keyframes.size() < 2 || timeBuffered < buffertime)){ + buffercount++; + } while (buffers.size() > buffercount){ if (buffers.begin()->second.isMember("keyframe")){ std::string track = trackMapping[buffers.begin()->first.trackID]; @@ -234,6 +251,10 @@ JSON::Value & DTSC::Stream::getPacket(livePos num){ return buffers[num]; } +JSON::Value & DTSC::Stream::getPacket(){ + return buffers.begin()->second; +} + /// Returns a track element by giving the id. JSON::Value & DTSC::Stream::getTrackById(int trackNo){ static JSON::Value empty; @@ -262,6 +283,14 @@ void DTSC::Stream::setBufferTime(unsigned int ms){ buffertime = ms; } +std::string & DTSC::Stream::outPacket(){ + static std::string emptystring; + if (!buffers.size() || !buffers.begin()->second.isObject()){ + return emptystring; + } + return buffers.begin()->second.toNetPacked(); +} + /// Returns a packed DTSC packet, ready to sent over the network. std::string & DTSC::Stream::outPacket(livePos num){ static std::string emptystring; @@ -274,32 +303,9 @@ std::string & DTSC::Stream::outHeader(){ return metadata.toNetPacked(); } -/// advances all given out and internal Ring classes to point to the new buffer, after one has been added. -/// Also updates the internal keyframes ring, as well as marking rings as starved if they are. -/// Unsets waiting rings, updating them with their new buffer number. -void DTSC::Stream::advanceRings(){ - static int fragNum = 1; - static unsigned int lastkeytime = 4242; - if ((lastType() == VIDEO && buffers.front().isMember("keyframe")) || (!metadata.isMember("video") && buffers.front()["time"].asInt() / 2000 != lastkeytime)){ - keyframes.push_front(DTSC::Ring(0)); - if ( !buffers.front().isMember("fragnum")){ - buffers.front()["fragnum"] = fragNum++; - } - lastkeytime = buffers.front()["time"].asInt() / 2000; - } - unsigned int timeBuffered = 0; - if (keyframes.size() > 1){ - //increase buffer size if no keyframes available or too little time available - timeBuffered = buffers[keyframes[0].b]["time"].asInt() - buffers[keyframes[keyframes.size() - 1].b]["time"].asInt(); - } - if (buffercount > 1 && (keyframes.size() < 2 || timeBuffered < buffertime)){ - buffercount++; - } -} - /// Constructs a new Ring, at the given buffer position. /// \arg v Position for buffer. -DTSC::Ring::Ring(unsigned int v){ +DTSC::Ring::Ring(livePos v){ b = v; waiting = false; starved = false; @@ -311,42 +317,23 @@ DTSC::Ring::Ring(unsigned int v){ /// This Ring will be kept updated so it always points to valid data or has the starved boolean set. /// Don't forget to call dropRing() for all requested Ring classes that are no longer neccessary! DTSC::Ring * DTSC::Stream::getRing(){ - DTSC::Ring * tmp; - if (keyframes.size() == 0){ - tmp = new DTSC::Ring(0); - }else{ - tmp = new DTSC::Ring(keyframes[0].b); - } - rings.insert(tmp); - return tmp; + return new DTSC::Ring(buffers.begin()->first); } /// Deletes a given out Ring class from memory and internal Ring list. /// Checks for NULL pointers and invalid pointers, silently discarding them. void DTSC::Stream::dropRing(DTSC::Ring * ptr){ - if (rings.find(ptr) != rings.end()){ - rings.erase(ptr); - delete ptr; - } } /// Updates the headers for a live stream, keeping track of all available /// keyframes and their media times. The function MAY NOT be run at any other /// time than right after receiving a new keyframe, or there'll be raptors. void DTSC::Stream::updateHeaders(){ - if (keyframes.size() > 2){ - if (buffers[keyframes[0].b]["time"].asInt() < buffers[keyframes[keyframes.size() - 1].b]["time"].asInt()){ + if (buffers.size() > 2){ + if (buffers.begin()->second["time"].asInt() < (buffers.end()--)->second["time"].asInt()){ std::cerr << "Detected new video - resetting all buffers and metadata - hold on, this ride might get bumpy!" << std::endl; keyframes.clear(); buffers.clear(); - std::set::iterator sit; - if (rings.size()){ - for (sit = rings.begin(); sit != rings.end(); sit++){ - ( *sit)->updated = true; - ( *sit)->b = 0; - ( *sit)->starved = true; - } - } metadata.removeMember("keytime"); metadata.removeMember("keynum"); metadata.removeMember("keylen"); @@ -356,12 +343,9 @@ void DTSC::Stream::updateHeaders(){ metadata.netPrepare(); return; } - metadata["keytime"].shrink(keyframes.size() - 2); - metadata["keynum"].shrink(keyframes.size() - 2); - metadata["keylen"].shrink(keyframes.size() - 2); - metadata["keytime"].append(buffers[keyframes[1].b]["time"].asInt()); - metadata["keynum"].append(buffers[keyframes[1].b]["fragnum"].asInt()); - metadata["keylen"].append(buffers[keyframes[0].b]["time"].asInt() - buffers[keyframes[1].b]["time"].asInt()); + for (JSON::ObjIter trIt = metadata["tracks"].ObjBegin(); trIt != metadata["tracks"].ObjEnd(); trIt++){ + trIt->second["keys"].shrink(keyframes[trIt->second["trackid"].asInt()].size() - 2); + } unsigned int fragStart = 0; if ( !metadata["frags"]){ // this means that if we have < ~10 seconds in the buffer, fragmenting goes horribly wrong. @@ -410,87 +394,43 @@ void DTSC::Stream::updateHeaders(){ } } } - metadata["lastms"] = buffers[keyframes[0].b]["time"].asInt(); + //metadata["lastms"] = buffers[keyframes[0].b]["time"].asInt(); metadata["buffer_window"] = (long long int)buffertime; metadata["live"] = true; metadata.netPrepare(); - updateRingHeaders(); - } -} - -void DTSC::Stream::updateRingHeaders(){ - std::set::iterator sit; - if ( !rings.size()){ - return; - } - for (sit = rings.begin(); sit != rings.end(); sit++){ - ( *sit)->updated = true; } } /// Returns 0 if seeking is possible, -1 if the wanted frame is too old, 1 if the wanted frame is too new. int DTSC::Stream::canSeekms(unsigned int ms){ - if ( !metadata["keytime"].size()){ + if ( !buffers.size()){ return 1; } - if (ms > metadata["keytime"][metadata["keytime"].size() - 1].asInt()){ + if (ms > (buffers.end()--)->second["time"].asInt()){ return 1; } - if (ms < metadata["keytime"][0u].asInt()){ + if (ms < buffers.begin()->second["time"].asInt()){ return -1; } return 0; } -/// Returns 0 if seeking is possible, -1 if the wanted frame is too old, 1 if the wanted frame is too new. -int DTSC::Stream::canSeekFrame(unsigned int frameno){ - if ( !metadata["keynum"].size()){ - return 1; - } - if (frameno > metadata["keynum"][metadata["keynum"].size() - 1].asInt()){ - return 1; - } - if (frameno < metadata["keynum"][0u].asInt()){ - return -1; - } - return 0; -} - -unsigned int DTSC::Stream::msSeek(unsigned int ms){ - if (ms > buffers[keyframes[0u].b]["time"].asInt()){ - std::cerr << "Warning: seeking past ingest! (" << ms << "ms > " << buffers[keyframes[0u].b]["time"].asInt() << "ms)" << std::endl; - return keyframes[0u].b; - } - for (std::deque::iterator it = keyframes.begin(); it != keyframes.end(); it++){ - if (buffers[it->b]["time"].asInt() <= ms){ - return it->b; +DTSC::livePos DTSC::Stream::msSeek(unsigned int ms, std::set allowedTracks){ + livePos result = buffers.begin()->first; + for (std::map::iterator bIt = buffers.begin(); bIt != buffers.end(); bIt++){ + if (allowedTracks.find(bIt->first.trackID) != allowedTracks.end()){ + if (bIt->first.seekTime > ms){ + break; + } + result = bIt->first; } } - std::cerr << "Warning: seeking past buffer size! (" << ms << "ms < " << buffers[keyframes[keyframes.size() - 1].b]["time"].asInt() << "ms)" << std::endl; - return keyframes[keyframes.size() - 1].b; -} - -unsigned int DTSC::Stream::frameSeek(unsigned int frameno){ - if (frameno > buffers[keyframes[0u].b]["fragnum"].asInt()){ - std::cerr << "Warning: seeking past ingest! (F" << frameno << " > F" << buffers[keyframes[0u].b]["fragnum"].asInt() << ")" << std::endl; - return keyframes[0u].b; - } - for (std::deque::iterator it = keyframes.begin(); it != keyframes.end(); it++){ - if (buffers[it->b]["fragnum"].asInt() == frameno){ - return it->b; - } - } - std::cerr << "Warning: seeking past buffer size! (F" << frameno << " < F" << buffers[keyframes[keyframes.size() - 1].b]["fragnum"].asInt() << ")" << std::endl; - return keyframes[keyframes.size() - 1].b; + return result; } /// Properly cleans up the object for erasing. /// Drops all Ring classes that have been given out. DTSC::Stream::~Stream(){ - std::set::iterator sit; - for (sit = rings.begin(); sit != rings.end(); sit++){ - delete ( *sit); - } } DTSC::File::File(){ diff --git a/lib/dtsc.h b/lib/dtsc.h index 65763097..3388a288 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -135,6 +135,18 @@ namespace DTSC { /// 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) const { if (seekTime < rhs.seekTime){ return true; @@ -147,16 +159,16 @@ namespace DTSC { } return false; } - long long unsigned int seekTime; - unsigned int trackID; + volatile long long unsigned int seekTime; + volatile unsigned int trackID; }; /// A part from the DTSC::Stream ringbuffer. /// Holds information about a buffer that will stay consistent class Ring{ public: - Ring(unsigned int v); - volatile livePos b; + 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. @@ -173,6 +185,7 @@ namespace DTSC { ~Stream(); Stream(unsigned int buffers, unsigned int bufferTime = 0); JSON::Value metadata; + JSON::Value & getPacket(); JSON::Value & getPacket(livePos num); JSON::Value & getTrackById(int trackNo); datatype lastType(); @@ -181,6 +194,7 @@ namespace DTSC { 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(); @@ -188,15 +202,11 @@ namespace DTSC { void dropRing(Ring * ptr); void updateHeaders(); int canSeekms(unsigned int ms); - int canSeekFrame(unsigned int frameno); - unsigned int msSeek(unsigned int ms); - unsigned int frameSeek(unsigned int frameno); + livePos msSeek(unsigned int ms, std::set allowedTracks); void setBufferTime(unsigned int ms); private: std::map buffers; std::map > keyframes; - void advanceRings(); - void updateRingHeaders(); void addPacket(JSON::Value & newPack); std::string * datapointer; datatype datapointertype; From 785136f7ccb1d1b18df910890b45682c0c8c60e3 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 25 Jun 2013 15:38:11 +0200 Subject: [PATCH 425/788] Fixed DTSC Stream library multibitrate support. --- lib/dtsc.cpp | 21 +++++++++++++++++---- lib/dtsc.h | 6 ++++-- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index cb212c65..4875c0b6 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -179,6 +179,7 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ newPos.trackID = newPack["trackid"].asInt(); newPos.seekTime = newPack["time"].asInt(); buffers[newPos] = newPack; + buffers[newPos].toNetPacked();//make sure package is packed and ready datapointertype = INVALID; ///\todo Save keyframes when they arrive. if (newPack.isMember("data")){ @@ -285,10 +286,10 @@ void DTSC::Stream::setBufferTime(unsigned int ms){ std::string & DTSC::Stream::outPacket(){ static std::string emptystring; - if (!buffers.size() || !buffers.begin()->second.isObject()){ + if (!buffers.size() || !buffers.rbegin()->second.isObject()){ return emptystring; } - return buffers.begin()->second.toNetPacked(); + return buffers.rbegin()->second.toNetPacked(); } /// Returns a packed DTSC packet, ready to sent over the network. @@ -406,7 +407,7 @@ int DTSC::Stream::canSeekms(unsigned int ms){ if ( !buffers.size()){ return 1; } - if (ms > (buffers.end()--)->second["time"].asInt()){ + if (ms > buffers.rbegin()->second["time"].asInt()){ return 1; } if (ms < buffers.begin()->second["time"].asInt()){ @@ -415,7 +416,7 @@ int DTSC::Stream::canSeekms(unsigned int ms){ return 0; } -DTSC::livePos DTSC::Stream::msSeek(unsigned int ms, std::set allowedTracks){ +DTSC::livePos DTSC::Stream::msSeek(unsigned int ms, std::set & allowedTracks){ livePos result = buffers.begin()->first; for (std::map::iterator bIt = buffers.begin(); bIt != buffers.end(); bIt++){ if (allowedTracks.find(bIt->first.trackID) != allowedTracks.end()){ @@ -428,6 +429,18 @@ DTSC::livePos DTSC::Stream::msSeek(unsigned int ms, std::set allowedTracks) return result; } +bool DTSC::Stream::isNewest(DTSC::livePos & pos){ + return (buffers.upper_bound(pos) == buffers.end()); +} + +DTSC::livePos DTSC::Stream::getNext(DTSC::livePos & pos, std::set & allowedTracks){ + if (!isNewest(pos)){ + return (buffers.upper_bound(pos))->first; + }else{ + return livePos(); + } +} + /// Properly cleans up the object for erasing. /// Drops all Ring classes that have been given out. DTSC::Stream::~Stream(){ diff --git a/lib/dtsc.h b/lib/dtsc.h index 3388a288..fc2e5d2a 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -202,9 +202,11 @@ namespace DTSC { void dropRing(Ring * ptr); void updateHeaders(); int canSeekms(unsigned int ms); - livePos msSeek(unsigned int ms, std::set allowedTracks); + livePos msSeek(unsigned int ms, std::set & allowedTracks); void setBufferTime(unsigned int ms); - private: + bool isNewest(DTSC::livePos & pos); + DTSC::livePos getNext(DTSC::livePos & pos, std::set & allowedTracks); + private: std::map buffers; std::map > keyframes; void addPacket(JSON::Value & newPack); From bec0c12ac67b3b5205c6703ede3468d324a45034 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 25 Jun 2013 15:55:55 +0200 Subject: [PATCH 426/788] Fixed command line argument parsing bug. --- lib/config.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/config.cpp b/lib/config.cpp index 245cca8d..1df00ad8 100644 --- a/lib/config.cpp +++ b/lib/config.cpp @@ -243,8 +243,6 @@ void Util::Config::parseArgs(int & argc, char ** & argv){ 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]); - optind++; - long_i++; break; } } From e3861f9fc6b95ba4bcee80a1573e438f1459c0fd Mon Sep 17 00:00:00 2001 From: Oswald de Bruin Date: Thu, 6 Jun 2013 12:06:29 +0200 Subject: [PATCH 427/788] Started ogg support in lib --- lib/Makefile.am | 4 +- lib/ogg.cpp | 246 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/ogg.h | 40 ++++++++ 3 files changed, 288 insertions(+), 2 deletions(-) create mode 100644 lib/ogg.cpp create mode 100644 lib/ogg.h diff --git a/lib/Makefile.am b/lib/Makefile.am index 9f68f05d..d0fb29e1 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,5 +1,5 @@ lib_LTLIBRARIES=libmist-1.0.la -libmist_1_0_la_SOURCES=amf.h amf.cpp auth.h auth.cpp base64.h base64.cpp config.h config.cpp dtsc.h dtsc.cpp flv_tag.h flv_tag.cpp http_parser.h http_parser.cpp json.h json.cpp procs.h procs.cpp rtmpchunks.h rtmpchunks.cpp socket.h socket.cpp mp4.h mp4.cpp ftp.h ftp.cpp filesystem.h filesystem.cpp stream.h stream.cpp timing.h timing.cpp ts_packet.cpp ts_packet.h converter.cpp converter.h +libmist_1_0_la_SOURCES=amf.h amf.cpp auth.h auth.cpp base64.h base64.cpp config.h config.cpp dtsc.h dtsc.cpp flv_tag.h flv_tag.cpp http_parser.h http_parser.cpp json.h json.cpp procs.h procs.cpp rtmpchunks.h rtmpchunks.cpp socket.h socket.cpp mp4.h mp4.cpp ftp.h ftp.cpp filesystem.h filesystem.cpp stream.h stream.cpp timing.h timing.cpp ts_packet.cpp ts_packet.h converter.cpp converter.h ogg.h ogg.cpp libmist_1_0_la_LDFLAGS = -version-info 5:1:2 libmist_1_0_la_CPPFLAGS=$(DEPS_CFLAGS) $(global_CFLAGS) libmist_1_0_la_LIBADD=$(DEPS_LIBS) $(CLOCK_LIB) @@ -8,4 +8,4 @@ pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = mist-1.0.pc library_includedir=$(includedir)/mist-1.0/mist -library_include_HEADERS = amf.h auth.h base64.h config.h dtsc.h flv_tag.h http_parser.h json.h procs.h rtmpchunks.h socket.h mp4.h ftp.h filesystem.h stream.h timing.h nal.h ts_packet.h converter.h +library_include_HEADERS = amf.h auth.h base64.h config.h dtsc.h flv_tag.h http_parser.h json.h procs.h rtmpchunks.h socket.h mp4.h ftp.h filesystem.h stream.h timing.h nal.h ts_packet.h converter.h ogg.h diff --git a/lib/ogg.cpp b/lib/ogg.cpp new file mode 100644 index 00000000..1a04235f --- /dev/null +++ b/lib/ogg.cpp @@ -0,0 +1,246 @@ +#include "ogg.h" +#include +#include +#include +#include + +#define lsb32(offset) data[offset] | data[offset+1] << 8 | data[offset+2] << 16 | data[offset+3] << 24 + +namespace OGG{ + Page::Page(){ + data = NULL; + datasize = 0; + dataSum = 0; + } + + bool Page::read(std::string & newData){ + dataSum = 0; + datasize = 0; + if (newData.size()<27){ + return false; + } + if (!checkDataSize(27)){ + return false; + } + memcpy(data, newData.c_str(), 27);//copying the header, always 27 bytes + if(!checkDataSize(27 + getPageSegments())){ + return false; + } + memcpy(data + 27, newData.c_str() + 27, getPageSegments()); + //copying the first part of the page into data, which tells the size of the page + + for(unsigned int i = 0; i < getPageSegments(); i++){ + dataSum += getSegmentTable()[i]; + } + if(!checkDataSize(27 + getPageSegments()+dataSum)){ + return false; + } + memcpy(data + 27 + getPageSegments(), newData.c_str() + 27 + getPageSegments(), dataSum); + newData.erase(0, datasize); + return true; + } + + long unsigned int Page::getMagicNumber(){ + return ntohl(((long unsigned int*)(data))[0]); + } + + void Page::setMagicNumber(){ + if(checkDataSize(4)){ + memcpy(data, "OggS", 4); + } + } + + char Page::getVersion(){ + return data[4]; + } + + void Page::setVersion(char newVal){ + if(checkDataSize(5)){ + data[4] = newVal; + } + } + + char Page::getHeaderType(){ + return data[5]; + } + + void Page::setHeaderType(char newVal){ + if(checkDataSize(6)){ + data[5] = newVal; + } + } + + long long unsigned int Page::getGranulePosition(){ + if(checkDataSize(14)){ + //switching bit order upon return + return ntohl(((long unsigned*)(data+6))[1]) & ((long long unsigned)(ntohl(((long unsigned*)(data+6))[0]) << 32)); + } + return 0; + } + + void Page::setGranulePosition(long long unsigned int newVal){ + if(checkDataSize(14)){ + ((long unsigned*)(data+6))[1] = htonl(newVal & 0xFFFFFFFF); + ((long unsigned*)(data+6))[0] = htonl((newVal >> 32) & 0xFFFFFFFF); + } + } + + long unsigned int Page::getBitstreamSerialNumber(){ + //return ntohl(((long unsigned int*)(data+14))[0]); + return lsb32(14); + } + + void Page::setBitstreamSerialNumber(long unsigned int newVal){ + if(checkDataSize(18)){ + ((long unsigned *)(data+14))[0] = htonl(newVal); + } + } + + long unsigned int Page::getPageSequenceNumber(){ + return lsb32(18); + } + + void Page::setPageSequenceNumber(long unsigned int newVal){ + if(checkDataSize(22)){ + ((long unsigned *)(data+18))[0] = htonl(newVal); + } + } + + long unsigned int Page::getCRCChecksum(){ + return ntohl(((long unsigned int*)(data+22))[0]); + //return lsb32(22); + } + + void Page::setCRCChecksum(long unsigned int newVal){ + if(checkDataSize(26)){ + ((long unsigned *)(data+22))[0] = htonl(newVal); + } + } + + char Page::getPageSegments(){ + return data[26]; + } + + inline void Page::setPageSegments(char newVal){ + data[26] = newVal; + } + + char* Page::getSegmentTable(){ + return data+27; + } + + std::deque Page::getSegmentTableDeque(){ + std::deque retVal; + unsigned int temp = 0; + char* segmentTable = getSegmentTable(); + for (unsigned int i = 0; i < getPageSegments(); i++){ + temp += segmentTable[i]; + if (segmentTable[i] < 255){ + retVal.push_back(temp); + temp = 0; + } + } + return retVal; + } + + bool Page::setSegmentTable(std::vector layout){ + unsigned int place = 0; + char table[255]; + for (unsigned int i = 0; i < layout.size(); i++){ + while (layout[i]>=255){ + if (place >= 255) return false; + table[place] = 255; + layout[i] -= 255; + place++; + } + if (place >= 255) return false; + table[place] = layout[i]; + place++; + } + setSegmentTable(table,place); + return true; + } + + void Page::setSegmentTable(char* newVal, unsigned int length){ + if(checkDataSize(27 + length)){ + memcpy(data + 27, newVal, length); + } + } + + char* Page::getFullPayload(){ + return data + 27 + getPageSegments(); + } + + std::string Page::toPrettyString(){ + std::stringstream r; + r << "Size(" << datasize << ")(" << dataSum << ")" << std::endl; + r << "Magic_Number: " << std::string(data, 4) << std::endl; + r << "Version: " << (int)getVersion() << std::endl; + r << "Header_type: " << std::hex << (int)getHeaderType() << std::dec; + if (getHeaderType() & 0x01){ + r << " continued"; + } + if (getHeaderType() & 0x02){ + r << " bos"; + } + if (getHeaderType() & 0x04){ + r << " eos"; + } + r << std::endl; + r << "Granule_position: " << getGranulePosition() << std::endl; + r << "Bitstream_SN: " << getBitstreamSerialNumber() << std::endl; + r << "Page_sequence_number: " << getPageSequenceNumber() << std::endl; + r << "CRC_checksum: " << std::hex << getCRCChecksum()<< std::dec << std::endl; + r << " Calced Checksum: " << std::hex << calcChecksum() << std::dec << std::endl; + r << "CRC_checksum write: " << std::hex << getCRCChecksum()<< std::dec << std::endl; + r << "Page_segments: " << (int)getPageSegments() << std::endl; + r << "SegmentTable: "; + std::deque temp = getSegmentTableDeque(); + for (std::deque::iterator i = temp.begin(); i != temp.end(); i++){ + r << (*i) << " "; + } + r << std::endl; + return r.str(); + } + + long unsigned int Compute(char* buffer, unsigned int count){ + long unsigned int m_crc = ~0u; + //const unsigned char* ptr = (const unsigned char *) buffer; + for (unsigned int i = 0; i < count; i++) { + buffer++; + m_crc ^= ((unsigned long int)buffer << 24); + for (int i = 0; i < 8; i++) { + if (m_crc & 0x80000000) { + m_crc = (m_crc << 1) ^ 0x04C11DB7; + }else { + m_crc <<= 1; + } + } + } + return m_crc; + } + + long unsigned int Page::calcChecksum(){ + long unsigned int retVal = 0; + long unsigned int oldChecksum = getCRCChecksum(); + setCRCChecksum (0); + retVal = Compute(data, datasize); + setCRCChecksum (oldChecksum); + return retVal; + } + + bool Page::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; + } + } +} diff --git a/lib/ogg.h b/lib/ogg.h new file mode 100644 index 00000000..5d2e1d2d --- /dev/null +++ b/lib/ogg.h @@ -0,0 +1,40 @@ +#include +#include +#include + + +namespace OGG{ + class Page{ + public: + Page(); + bool read(std::string & newData); + long unsigned int getMagicNumber(); + 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 getPageSequenceNumber(); + void setPageSequenceNumber(long unsigned int newVal); + long unsigned int getCRCChecksum(); + void setCRCChecksum(long unsigned int newVal); + char getPageSegments(); + inline void setPageSegments(char newVal); + char* getSegmentTable(); + std::deque getSegmentTableDeque(); + bool setSegmentTable(std::vector layout); + void setSegmentTable(char* newVal, unsigned int length); + char* getFullPayload(); + std::string toPrettyString(); + private: + long unsigned int calcChecksum(); + char* data; + unsigned int datasize; + unsigned int dataSum; + bool checkDataSize(unsigned int size); + }; +} From b600d6b6030e81447e5d432107fc72043cd076cc Mon Sep 17 00:00:00 2001 From: Oswald de Bruin Date: Thu, 6 Jun 2013 14:52:52 +0200 Subject: [PATCH 428/788] OGG full readout possible --- lib/ogg.cpp | 12 ++++++++---- lib/ogg.h | 3 +++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/ogg.cpp b/lib/ogg.cpp index 1a04235f..21454aa3 100644 --- a/lib/ogg.cpp +++ b/lib/ogg.cpp @@ -15,7 +15,7 @@ namespace OGG{ bool Page::read(std::string & newData){ dataSum = 0; - datasize = 0; + //datasize = 0; if (newData.size()<27){ return false; } @@ -36,7 +36,7 @@ namespace OGG{ return false; } memcpy(data + 27 + getPageSegments(), newData.c_str() + 27 + getPageSegments(), dataSum); - newData.erase(0, datasize); + newData.erase(0, getPageSize()); return true; } @@ -167,13 +167,17 @@ namespace OGG{ } } + unsigned long int Page::getPageSize(){ + return 27 + getPageSegments()+dataSum; + } + char* Page::getFullPayload(){ return data + 27 + getPageSegments(); } std::string Page::toPrettyString(){ std::stringstream r; - r << "Size(" << datasize << ")(" << dataSum << ")" << std::endl; + r << "Size(" << getPageSize() << ")(" << dataSum << ")" << std::endl; r << "Magic_Number: " << std::string(data, 4) << std::endl; r << "Version: " << (int)getVersion() << std::endl; r << "Header_type: " << std::hex << (int)getHeaderType() << std::dec; @@ -224,7 +228,7 @@ namespace OGG{ long unsigned int retVal = 0; long unsigned int oldChecksum = getCRCChecksum(); setCRCChecksum (0); - retVal = Compute(data, datasize); + retVal = Compute(data, getPageSize()); setCRCChecksum (oldChecksum); return retVal; } diff --git a/lib/ogg.h b/lib/ogg.h index 5d2e1d2d..2f4b9c66 100644 --- a/lib/ogg.h +++ b/lib/ogg.h @@ -28,7 +28,10 @@ namespace OGG{ std::deque getSegmentTableDeque(); bool setSegmentTable(std::vector layout); void setSegmentTable(char* newVal, unsigned int length); + unsigned long int getPageSize(); char* getFullPayload(); + char* getSegment(long unsigned int); + std::string toPrettyString(); private: long unsigned int calcChecksum(); From 9073fe7cfbbf3db5830ccade497fab066d7b5a7f Mon Sep 17 00:00:00 2001 From: Oswald de Bruin Date: Thu, 6 Jun 2013 16:58:05 +0200 Subject: [PATCH 429/788] small commit --- lib/ogg.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ogg.h b/lib/ogg.h index 2f4b9c66..39a6a13b 100644 --- a/lib/ogg.h +++ b/lib/ogg.h @@ -1,7 +1,7 @@ #include #include #include - +#include Date: Mon, 10 Jun 2013 13:42:17 +0200 Subject: [PATCH 430/788] ogg edits --- lib/ogg.cpp | 36 ++++++++++++++++++++++++++++++++---- lib/ogg.h | 7 +++++-- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/lib/ogg.cpp b/lib/ogg.cpp index 21454aa3..bc6dc338 100644 --- a/lib/ogg.cpp +++ b/lib/ogg.cpp @@ -14,11 +14,11 @@ namespace OGG{ } bool Page::read(std::string & newData){ - dataSum = 0; //datasize = 0; if (newData.size()<27){ return false; } + dataSum = 0; if (!checkDataSize(27)){ return false; } @@ -175,19 +175,47 @@ namespace OGG{ return data + 27 + getPageSegments(); } + bool Page::typeBOS(){ + if (getHeaderType() & 0x02){ + return true; + } + return false; + } + + bool Page::typeEOS(){ + if (getHeaderType() & 0x04){ + return true; + } + return false; + } + + bool Page::typeContinue(){ + if (getHeaderType() & 0x01){ + return true; + } + return false; + } + + bool Page::typeNone(){ + if (getHeaderType() & 0x07 == 0x00){ + return true; + } + return false; + } + std::string Page::toPrettyString(){ std::stringstream r; r << "Size(" << getPageSize() << ")(" << dataSum << ")" << std::endl; r << "Magic_Number: " << std::string(data, 4) << std::endl; r << "Version: " << (int)getVersion() << std::endl; r << "Header_type: " << std::hex << (int)getHeaderType() << std::dec; - if (getHeaderType() & 0x01){ + if (typeContinue()){ r << " continued"; } - if (getHeaderType() & 0x02){ + if (typeBOS()){ r << " bos"; } - if (getHeaderType() & 0x04){ + if (typeEOS()){ r << " eos"; } r << std::endl; diff --git a/lib/ogg.h b/lib/ogg.h index 39a6a13b..e9a363d8 100644 --- a/lib/ogg.h +++ b/lib/ogg.h @@ -1,7 +1,7 @@ #include #include #include -#include Date: Wed, 12 Jun 2013 14:56:01 +0200 Subject: [PATCH 431/788] Ogg fix --- lib/ogg.cpp | 167 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 138 insertions(+), 29 deletions(-) diff --git a/lib/ogg.cpp b/lib/ogg.cpp index bc6dc338..e0d4d5b0 100644 --- a/lib/ogg.cpp +++ b/lib/ogg.cpp @@ -4,9 +4,41 @@ #include #include -#define lsb32(offset) data[offset] | data[offset+1] << 8 | data[offset+2] << 16 | data[offset+3] << 24 - namespace OGG{ + 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(){ data = NULL; datasize = 0; @@ -18,12 +50,19 @@ namespace OGG{ if (newData.size()<27){ return false; } + /*if (getMagicNumber() != 0x4f676753){ + return false; + }*/ dataSum = 0; if (!checkDataSize(27)){ return false; } memcpy(data, newData.c_str(), 27);//copying the header, always 27 bytes - if(!checkDataSize(27 + getPageSegments())){ + + if (newData.size() < 27 + getPageSegments()){//check input size + return false; + } + if(!checkDataSize(27 + getPageSegments())){//check if size available in memory return false; } memcpy(data + 27, newData.c_str() + 27, getPageSegments()); @@ -32,6 +71,10 @@ namespace OGG{ for(unsigned int i = 0; i < getPageSegments(); i++){ dataSum += getSegmentTable()[i]; } + + if (newData.size() < 27 + getPageSegments() + dataSum){//check input size + return false; + } if(!checkDataSize(27 + getPageSegments()+dataSum)){ return false; } @@ -73,7 +116,11 @@ namespace OGG{ long long unsigned int Page::getGranulePosition(){ if(checkDataSize(14)){ //switching bit order upon return - return ntohl(((long unsigned*)(data+6))[1]) & ((long long unsigned)(ntohl(((long unsigned*)(data+6))[0]) << 32)); + //return ntohl(((long unsigned*)(data+6))[1]) & ((long long unsigned)((long long unsigned)ntohl(((long unsigned*)(data+6))[0]) << 32)); + //long long unsigned int temp; + //temp = ((long unsigned int)(data+6)[0]); + //temp = temp << 32 + ((long unsigned int)(data+6)[1]); + return get_64(data+6); } return 0; } @@ -87,33 +134,35 @@ namespace OGG{ long unsigned int Page::getBitstreamSerialNumber(){ //return ntohl(((long unsigned int*)(data+14))[0]); - return lsb32(14); + return get_32(data+14); } void Page::setBitstreamSerialNumber(long unsigned int newVal){ if(checkDataSize(18)){ - ((long unsigned *)(data+14))[0] = htonl(newVal); + //((long unsigned *)(data+14))[0] = htonl(newVal); + set_32(data+14, newVal); } } long unsigned int Page::getPageSequenceNumber(){ - return lsb32(18); + return get_32(data+18); } void Page::setPageSequenceNumber(long unsigned int newVal){ if(checkDataSize(22)){ - ((long unsigned *)(data+18))[0] = htonl(newVal); + //((long unsigned *)(data+18))[0] = htonl(newVal); + set_32(data+18, newVal); } } long unsigned int Page::getCRCChecksum(){ - return ntohl(((long unsigned int*)(data+22))[0]); - //return lsb32(22); + //return ntohl(((long unsigned int*)(data+22))[0]); + return get_32(data+22); } void Page::setCRCChecksum(long unsigned int newVal){ if(checkDataSize(26)){ - ((long unsigned *)(data+22))[0] = htonl(newVal); + set_32(data+22,newVal); } } @@ -219,12 +268,12 @@ namespace OGG{ r << " eos"; } r << std::endl; - r << "Granule_position: " << getGranulePosition() << std::endl; + r << "Granule_position: " < temp = getSegmentTableDeque(); @@ -235,28 +284,88 @@ namespace OGG{ return r.str(); } - long unsigned int Compute(char* buffer, unsigned int count){ - long unsigned int m_crc = ~0u; - //const unsigned char* ptr = (const unsigned char *) buffer; - for (unsigned int i = 0; i < count; i++) { - buffer++; - m_crc ^= ((unsigned long int)buffer << 24); - for (int i = 0; i < 8; i++) { - if (m_crc & 0x80000000) { - m_crc = (m_crc << 1) ^ 0x04C11DB7; - }else { - m_crc <<= 1; - } - } + 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 m_crc; + return crc; } long unsigned int Page::calcChecksum(){ long unsigned int retVal = 0; long unsigned int oldChecksum = getCRCChecksum(); setCRCChecksum (0); - retVal = Compute(data, getPageSize()); + retVal = crc32(0, data, getPageSize()); setCRCChecksum (oldChecksum); return retVal; } From 8cae5f1fc6720e33360b1fc5e7ed50c59a63a07c Mon Sep 17 00:00:00 2001 From: Oswald de Bruin Date: Fri, 14 Jun 2013 16:30:55 +0200 Subject: [PATCH 432/788] Merge branch 'oggLib' of github.com:DDVTECH/libmist into oggLib Conflicts: lib/Makefile.am lib/ogg.cpp Ogg completed further --- lib/Makefile.am | 4 +- lib/theora.cpp | 100 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/theora.h | 19 +++++++++ 3 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 lib/theora.cpp create mode 100644 lib/theora.h diff --git a/lib/Makefile.am b/lib/Makefile.am index d0fb29e1..41c0c207 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,5 +1,5 @@ lib_LTLIBRARIES=libmist-1.0.la -libmist_1_0_la_SOURCES=amf.h amf.cpp auth.h auth.cpp base64.h base64.cpp config.h config.cpp dtsc.h dtsc.cpp flv_tag.h flv_tag.cpp http_parser.h http_parser.cpp json.h json.cpp procs.h procs.cpp rtmpchunks.h rtmpchunks.cpp socket.h socket.cpp mp4.h mp4.cpp ftp.h ftp.cpp filesystem.h filesystem.cpp stream.h stream.cpp timing.h timing.cpp ts_packet.cpp ts_packet.h converter.cpp converter.h ogg.h ogg.cpp +libmist_1_0_la_SOURCES=amf.h amf.cpp auth.h auth.cpp base64.h base64.cpp config.h config.cpp dtsc.h dtsc.cpp flv_tag.h flv_tag.cpp http_parser.h http_parser.cpp json.h json.cpp procs.h procs.cpp rtmpchunks.h rtmpchunks.cpp socket.h socket.cpp mp4.h mp4.cpp ftp.h ftp.cpp filesystem.h filesystem.cpp stream.h stream.cpp timing.h timing.cpp ts_packet.cpp ts_packet.h converter.cpp converter.h ogg.h ogg.cpp theora.cpp theora.h libmist_1_0_la_LDFLAGS = -version-info 5:1:2 libmist_1_0_la_CPPFLAGS=$(DEPS_CFLAGS) $(global_CFLAGS) libmist_1_0_la_LIBADD=$(DEPS_LIBS) $(CLOCK_LIB) @@ -8,4 +8,4 @@ pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = mist-1.0.pc library_includedir=$(includedir)/mist-1.0/mist -library_include_HEADERS = amf.h auth.h base64.h config.h dtsc.h flv_tag.h http_parser.h json.h procs.h rtmpchunks.h socket.h mp4.h ftp.h filesystem.h stream.h timing.h nal.h ts_packet.h converter.h ogg.h +library_include_HEADERS = amf.h auth.h base64.h config.h dtsc.h flv_tag.h http_parser.h json.h procs.h rtmpchunks.h socket.h mp4.h ftp.h filesystem.h stream.h timing.h nal.h ts_packet.h converter.h ogg.h theora.h diff --git a/lib/theora.cpp b/lib/theora.cpp new file mode 100644 index 00000000..2640033b --- /dev/null +++ b/lib/theora.cpp @@ -0,0 +1,100 @@ +#include +#include +#include +#include + +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; + } + } + + /// 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 header::getInt32(size_t index){ + /*if (index + 3 >= datasize){ + if ( !reserve(index, 0, 4)){ + return 0; + } + setInt32(0, index); + }*/ + uint32_t result; + memcpy((char*) &result, data + index, 4); + return ntohl(result); + } + + header::header(){ + data = NULL; + datasize = 0; + } + + bool header::read(char* newData, unsigned int length){ + if (length < 7){ + return false; + } + if(memcmp(newData+1, "theora", 6)!=0){ + return false; + } + switch(newData[0]){ + case 0x80: + //if (length != 42) return false; + break; + case 0x81: + break; + case 0x82: + break; + default: + return false; + break; + }; + if (checkDataSize(length)){ + memcpy(data, newData, length); + }else{ + return false; + } + return true; + } + + int header::getHeaderType(){ + switch(data[0]){ + case 0x80: + return 0; + break; + case 0x81: + return 1; + break; + case 0x82: + return 2; + break; + default: + return -1; + break; + }; + } + + long unsigned int header::getFRN(){ + if (getHeaderType() == 0){ + return getInt32(22); + } + return 0; + } + + long unsigned int header::getFRD(){ + if (getHeaderType() == 0){ + return getInt32(26); + } + return 0; + } + +} diff --git a/lib/theora.h b/lib/theora.h new file mode 100644 index 00000000..bd472ece --- /dev/null +++ b/lib/theora.h @@ -0,0 +1,19 @@ +#include +#include + +namespace theora{ + class header{ + public: + header(); + bool read(char* newData, unsigned int length); + int getHeaderType(); + long unsigned int getFRN(); + long unsigned int getFRD(); + protected: + uint32_t getInt32(size_t index); + private: + char* data; + unsigned int datasize; + bool checkDataSize(unsigned int size); + }; +} From 04500e469493f4b2a15247f21f3d34c87c44d8f6 Mon Sep 17 00:00:00 2001 From: Oswald de Bruin Date: Mon, 17 Jun 2013 15:55:29 +0200 Subject: [PATCH 433/788] Lib ogg added new function --- lib/theora.cpp | 20 ++++++++++++++++++++ lib/theora.h | 6 ++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/lib/theora.cpp b/lib/theora.cpp index 2640033b..8170a22c 100644 --- a/lib/theora.cpp +++ b/lib/theora.cpp @@ -33,6 +33,19 @@ namespace theora{ memcpy((char*) &result, data + index, 4); return ntohl(result); } + + uint16_t header::getInt16(size_t index){ + /*if (index + 3 >= datasize){ + if ( !reserve(index, 0, 4)){ + return 0; + } + setInt32(0, index); + }*/ + uint16_t result; + memcpy((char*) &result, data + index, 2); + return ntohs(result); + } + header::header(){ data = NULL; @@ -83,6 +96,13 @@ namespace theora{ }; } + char header::getKFGShift(){ + if (getHeaderType() == 0){ + return (getInt16(40) >> 5) & 0x1F; + } + return 0; + } + long unsigned int header::getFRN(){ if (getHeaderType() == 0){ return getInt32(22); diff --git a/lib/theora.h b/lib/theora.h index bd472ece..825d8fde 100644 --- a/lib/theora.h +++ b/lib/theora.h @@ -7,10 +7,12 @@ namespace theora{ header(); bool read(char* newData, unsigned int length); int getHeaderType(); - long unsigned int getFRN(); - long unsigned int getFRD(); + char getKFGShift(); + long unsigned int getFRN();//frame rate numerator + long unsigned int getFRD();//frame rate denominator protected: uint32_t getInt32(size_t index); + uint16_t getInt16(size_t index); private: char* data; unsigned int datasize; From 4ea8fe03276ccaf21f58024c087cb84e01f4947f Mon Sep 17 00:00:00 2001 From: Oswald de Bruin Date: Tue, 18 Jun 2013 10:29:27 +0200 Subject: [PATCH 434/788] Added new functions to OGG --- lib/theora.cpp | 29 +++++++++++++++++++++++++++++ lib/theora.h | 3 +++ 2 files changed, 32 insertions(+) diff --git a/lib/theora.cpp b/lib/theora.cpp index 8170a22c..0399f0ee 100644 --- a/lib/theora.cpp +++ b/lib/theora.cpp @@ -34,6 +34,21 @@ namespace theora{ return ntohl(result); } + uint32_t header::getInt24(size_t index){ + /*if (index + 3 >= datasize){ + if ( !reserve(index, 0, 4)){ + return 0; + } + setInt32(0, index); + }*/ + uint32_t result = 0; + //memcpy(((char*) &result)+1, data + index, 3); + result += data[index] << 16; + result += data[index+1] << 8; + result += data[index+2]; + return result; + } + uint16_t header::getInt16(size_t index){ /*if (index + 3 >= datasize){ if ( !reserve(index, 0, 4)){ @@ -109,6 +124,20 @@ namespace theora{ } 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){ diff --git a/lib/theora.h b/lib/theora.h index 825d8fde..99a7b6cc 100644 --- a/lib/theora.h +++ b/lib/theora.h @@ -8,10 +8,13 @@ namespace theora{ bool read(char* newData, unsigned int length); int getHeaderType(); char getKFGShift(); + long unsigned int getPICH();//movie height + long unsigned int getPICW();//movie width long unsigned int getFRN();//frame rate numerator long unsigned int getFRD();//frame rate denominator protected: uint32_t getInt32(size_t index); + uint32_t getInt24(size_t index); uint16_t getInt16(size_t index); private: char* data; From a2f088ad80153e71f7a87a823136997220b5992e Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Mon, 24 Jun 2013 09:36:27 +0200 Subject: [PATCH 435/788] Theora headers 1 and 2 working in analyser --- lib/ogg.cpp | 48 +++++-- lib/ogg.h | 8 +- lib/theora.cpp | 336 ++++++++++++++++++++++++++++++++++++------------- lib/theora.h | 39 +++++- 4 files changed, 324 insertions(+), 107 deletions(-) diff --git a/lib/ogg.cpp b/lib/ogg.cpp index e0d4d5b0..20a26c26 100644 --- a/lib/ogg.cpp +++ b/lib/ogg.cpp @@ -251,13 +251,17 @@ namespace OGG{ } return false; } + + void Page::setInternalCodec(std::string myCodec){ + codec = myCodec; + } - std::string Page::toPrettyString(){ + std::string Page::toPrettyString(size_t indent){ std::stringstream r; - r << "Size(" << getPageSize() << ")(" << dataSum << ")" << std::endl; - r << "Magic_Number: " << std::string(data, 4) << std::endl; - r << "Version: " << (int)getVersion() << std::endl; - r << "Header_type: " << std::hex << (int)getHeaderType() << std::dec; + r << std::string(indent,' ') << "OGG Page (" << getPageSize() << ")" << std::endl; + r << std::string(indent + 2,' ') << "Magic Number: " << std::string(data, 4) << std::endl; + r << std::string(indent + 2,' ') << "Version: " << (int)getVersion() << std::endl; + r << std::string(indent + 2,' ') << "Headertype: " << std::hex << (int)getHeaderType() << std::dec; if (typeContinue()){ r << " continued"; } @@ -268,19 +272,33 @@ namespace OGG{ r << " eos"; } r << std::endl; - r << "Granule_position: " < temp = getSegmentTableDeque(); for (std::deque::iterator i = temp.begin(); i != temp.end(); i++){ - r << (*i) << " "; + r << std::string(indent + 4,' ') << (*i) << std::endl; + } + r << std::string(indent + 2,' ') << "Payloadsize: " << dataSum << std::endl; + if (codec == "theora"){ + int offset = 0; + for (int i = 0; i < getSegmentTableDeque().size(); i++){ + theora::header tmpHeader; + int len = getSegmentTableDeque()[i]; + if (tmpHeader.read(getFullPayload()+offset,len)){ + r << tmpHeader.toPrettyString(indent + 4); + } + theora::frame tmpFrame; + if (tmpFrame.read(getFullPayload()+offset,len)){ + r << tmpFrame.toPrettyString(indent + 4); + } + offset += len; + } } - r << std::endl; return r.str(); } @@ -384,4 +402,8 @@ namespace OGG{ return true; } } + + int Page::getPayloadSize(){ + return dataSum; + } } diff --git a/lib/ogg.h b/lib/ogg.h index e9a363d8..8af19687 100644 --- a/lib/ogg.h +++ b/lib/ogg.h @@ -1,7 +1,9 @@ +#pragma once #include #include #include #include"dtsc.h" +#include "theora.h" namespace OGG{ class Page{ @@ -30,17 +32,19 @@ namespace OGG{ void setSegmentTable(char* newVal, unsigned int length); unsigned long int getPageSize(); char* getFullPayload(); - char* getSegment(long unsigned int); + int getPayloadSize(); bool typeBOS(); bool typeEOS(); bool typeContinue(); bool typeNone(); - std::string toPrettyString(); + std::string toPrettyString(size_t indent = 0); + void setInternalCodec(std::string myCodec); private: long unsigned int calcChecksum(); char* data; unsigned int datasize; unsigned int dataSum; bool checkDataSize(unsigned int size); + std::string codec; }; } diff --git a/lib/theora.cpp b/lib/theora.cpp index 0399f0ee..72160062 100644 --- a/lib/theora.cpp +++ b/lib/theora.cpp @@ -2,6 +2,7 @@ #include #include #include +#include namespace theora{ bool header::checkDataSize(unsigned int size){ @@ -19,73 +20,261 @@ namespace theora{ } } - /// 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 header::getInt32(size_t index){ - /*if (index + 3 >= datasize){ - if ( !reserve(index, 0, 4)){ - return 0; - } - setInt32(0, index); - }*/ - uint32_t result; - memcpy((char*) &result, data + index, 4); - return ntohl(result); + 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 (index + 3 >= datasize){ - if ( !reserve(index, 0, 4)){ - return 0; - } - setInt32(0, index); - }*/ - uint32_t result = 0; - //memcpy(((char*) &result)+1, data + index, 3); - result += data[index] << 16; - result += data[index+1] << 8; - result += data[index+2]; - return result; + 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 (index + 3 >= datasize){ - if ( !reserve(index, 0, 4)){ - return 0; - } - setInt32(0, index); - }*/ - uint16_t result; - memcpy((char*) &result, data + index, 2); - return ntohs(result); + 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(){ data = NULL; datasize = 0; } + + bool header::validateIdentificationHeader(){ + if (datasize != 42){return false;} + if (getHeaderType() != 0){return false;} + if (getVMAJ() != 3){return false;} + if (getVMIN() != 2){return false;} + if (getFMBW() == 0){return false;} + if (getFMBH() == 0){return false;} + if (getPICW() > getFMBW() * 16){return false;} + if (getPICH() > getFMBH() * 16){return false;} + if (getPICX() > (getFMBW() * 16) - getPICW()){return false;} + if (getPICY() > (getFMBH() * 16) - getPICH()){return false;} + if (getFRN() == 0){return false;} + if (getFRD() == 0){return false;} + return true; + } bool header::read(char* newData, unsigned int length){ if (length < 7){ return false; } + if (! (newData[0] & 0x80)){ + return false; + } if(memcmp(newData+1, "theora", 6)!=0){ return false; } - switch(newData[0]){ - case 0x80: - //if (length != 42) return false; + if (checkDataSize(length)){ + memcpy(data, newData, length); + }else{ + return false; + } + switch(getHeaderType()){ + case 0: + return validateIdentificationHeader(); break; - case 0x81: + case 1: + ///\todo Read Comment header break; - case 0x82: + case 2: + ///\todo Read Setup Header break; - default: + } + 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); + } + + std::string header::getUserComment(size_t index){ + if (index >= getNComments()){return "";} + int len; + int offset = 11 + commentLen(7) + 4; + for (int i = 0; i < index; i++){ + offset += 4 + commentLen(offset); + } + return std::string(data + offset + 4,commentLen(offset)); + } + + std::string header::toPrettyString(size_t indent){ + std::stringstream result; + 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 (int i = 0; i < getNComments(); i++){ + result << std::string(indent+4,' ') << "[" << i << "] " << getUserComment(i) << std::endl; + } + break; + } + return result.str(); + } + + frame::frame(){ + data = NULL; + datasize = 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; - break; - }; + } + }else{ + return true; + } + } + + bool frame::read(char* newData, unsigned int length){ + if (length < 7){ + return false; + } + if ((newData[0] & 0x80)){ + return false; + } if (checkDataSize(length)){ memcpy(data, newData, length); }else{ @@ -93,57 +282,24 @@ namespace theora{ } return true; } - - int header::getHeaderType(){ - switch(data[0]){ - case 0x80: - return 0; - break; - case 0x81: - return 1; - break; - case 0x82: - return 2; - break; - default: - return -1; - break; - }; + + char frame::getFTYPE(){ + return (data[0] >> 6) & 0x01; } - - 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); - } + + char frame::getNQIS(){ return 0; } + char frame::getQIS(size_t index){ + if (index >= 3){return 0;} + return 0; + } + + 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(); + } } diff --git a/lib/theora.h b/lib/theora.h index 99a7b6cc..6bfdb64f 100644 --- a/lib/theora.h +++ b/lib/theora.h @@ -1,5 +1,7 @@ +#pragma once #include #include +#include namespace theora{ class header{ @@ -7,18 +9,51 @@ namespace theora{ header(); bool read(char* newData, unsigned int length); int getHeaderType(); - char getKFGShift(); - long unsigned int getPICH();//movie height + 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); + std::string toPrettyString(size_t indent = 0); 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; unsigned int datasize; bool checkDataSize(unsigned int size); + bool validateIdentificationHeader(); + }; + + class frame{ + public: + frame(); + bool read(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); }; } From f88bee5baf78fb044e176e894505f94de687f61b Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Tue, 25 Jun 2013 16:03:36 +0200 Subject: [PATCH 436/788] Basic Theora support working, added a function to the dtsc lib. --- lib/dtsc.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++ lib/dtsc.h | 1 + lib/ogg.cpp | 25 ++++++++++++----------- lib/ogg.h | 3 ++- lib/theora.cpp | 17 ++++++++++++++++ lib/theora.h | 3 +++ 6 files changed, 90 insertions(+), 13 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 4875c0b6..f13eff08 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -708,6 +708,60 @@ void DTSC::File::seekNext(){ } } + +void DTSC::File::parseNext(){ + if (fread(buffer, 4, 1, F) != 1){ + if (feof(F)){ +#if DEBUG >= 4 + fprintf(stderr, "End of file reached.\n"); +#endif + }else{ + fprintf(stderr, "Could not read header\n"); + } + strbuffer = ""; + jsonbuffer.null(); + return; + } + if (memcmp(buffer, DTSC::Magic_Header, 4) == 0){ + readHeader(lastreadpos); + jsonbuffer = metadata; + return; + } + long long unsigned int version = 0; + if (memcmp(buffer, DTSC::Magic_Packet, 4) == 0){ + version = 1; + } + if (memcmp(buffer, DTSC::Magic_Packet2, 4) == 0){ + version = 2; + } + if (version == 0){ + fprintf(stderr, "Invalid packet header @ %#x - %.4s != %.4s\n", lastreadpos, buffer, DTSC::Magic_Packet2); + strbuffer = ""; + jsonbuffer.null(); + return; + } + if (fread(buffer, 4, 1, F) != 1){ + fprintf(stderr, "Could not read size\n"); + strbuffer = ""; + jsonbuffer.null(); + return; + } + uint32_t * ubuffer = (uint32_t *)buffer; + long packSize = ntohl(ubuffer[0]); + strbuffer.resize(packSize); + if (fread((void*)strbuffer.c_str(), packSize, 1, F) != 1){ + fprintf(stderr, "Could not read packet\n"); + strbuffer = ""; + jsonbuffer.null(); + return; + } + if (version == 2){ + jsonbuffer = JSON::fromDTMI2(strbuffer); + }else{ + jsonbuffer = JSON::fromDTMI(strbuffer); + } +} + /// Returns the byte positon of the start of the last packet that was read. long long int DTSC::File::getLastReadPos(){ return lastreadpos; diff --git a/lib/dtsc.h b/lib/dtsc.h index fc2e5d2a..362f1bc3 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -104,6 +104,7 @@ namespace DTSC { long int getBytePos(); bool reachedEOF(); void seekNext(); + void parseNext(); std::string & getPacket(); JSON::Value & getJSON(); JSON::Value & getTrackById(int trackNo); diff --git a/lib/ogg.cpp b/lib/ogg.cpp index 20a26c26..debba28c 100644 --- a/lib/ogg.cpp +++ b/lib/ogg.cpp @@ -46,6 +46,7 @@ namespace OGG{ } bool Page::read(std::string & newData){ + segmentTableDeque.clear(); //datasize = 0; if (newData.size()<27){ return false; @@ -178,18 +179,18 @@ namespace OGG{ return data+27; } - std::deque Page::getSegmentTableDeque(){ - std::deque retVal; - unsigned int temp = 0; - char* segmentTable = getSegmentTable(); - for (unsigned int i = 0; i < getPageSegments(); i++){ - temp += segmentTable[i]; - if (segmentTable[i] < 255){ - retVal.push_back(temp); - temp = 0; + std::deque & Page::getSegmentTableDeque(){ + if ( !segmentTableDeque.size()){ + unsigned int temp = 0; + for (unsigned int i = 0; i < getPageSegments(); i++){ + temp += getSegmentTable()[i]; + if (getSegmentTable()[i] < 255){ + segmentTableDeque.push_back(temp); + temp = 0; + } } } - return retVal; + return segmentTableDeque; } bool Page::setSegmentTable(std::vector layout){ @@ -272,10 +273,10 @@ namespace OGG{ r << " eos"; } r << std::endl; - r << std::string(indent + 2,' ') << "Granule Position: " < getSegmentTableDeque(); + std::deque & getSegmentTableDeque(); bool setSegmentTable(std::vector layout); void setSegmentTable(char* newVal, unsigned int length); unsigned long int getPageSize(); @@ -40,6 +40,7 @@ namespace OGG{ std::string toPrettyString(size_t indent = 0); void setInternalCodec(std::string myCodec); private: + std::deque segmentTableDeque; long unsigned int calcChecksum(); char* data; unsigned int datasize; diff --git a/lib/theora.cpp b/lib/theora.cpp index 72160062..45b12f0a 100644 --- a/lib/theora.cpp +++ b/lib/theora.cpp @@ -203,6 +203,13 @@ namespace theora{ 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 len; @@ -244,6 +251,8 @@ namespace theora{ 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(); } @@ -296,6 +305,14 @@ namespace theora{ 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; diff --git a/lib/theora.h b/lib/theora.h index 6bfdb64f..e3ab70b4 100644 --- a/lib/theora.h +++ b/lib/theora.h @@ -30,7 +30,10 @@ namespace theora{ 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); + long long unsigned int parseGranuleUpper(long long unsigned int granPos); + long long unsigned int parseGranuleLower(long long unsigned int granPos); protected: uint32_t getInt32(size_t index); uint32_t getInt24(size_t index); From 526feda14881461eab66b2ae6662b8494315274e Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Thu, 27 Jun 2013 13:06:09 +0200 Subject: [PATCH 437/788] Small updates and fixes to the dtsc library. --- lib/dtsc.cpp | 11 +++++++---- lib/dtsc.h | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index f13eff08..6f4c0a32 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -212,15 +212,16 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ JSON::Value key; key["time"] = newPack["time"]; if (keySize){ - key["num"] = (*(metadata["tracks"][trackMapping[newPos.trackID]]["keys"].ArrEnd()--))["num"].asInt() + 1; + key["num"] = metadata["tracks"][trackMapping[newPos.trackID]]["keys"][keySize -1]["num"].asInt() + 1; }else{ key["num"] = 1; } + metadata["tracks"][trackMapping[newPos.trackID]]["keys"].append(key); } unsigned int timeBuffered = 0; if (keySize > 1){ //increase buffer size if no keyframes available or too little time available - timeBuffered = (buffers.end()--)->second["time"].asInt() - buffers.begin()->second["time"].asInt(); + timeBuffered = buffers.rbegin()->second["time"].asInt() - buffers.begin()->second["time"].asInt(); } if (buffercount > 1 && (keyframes.size() < 2 || timeBuffered < buffertime)){ buffercount++; @@ -612,8 +613,10 @@ void DTSC::File::readHeader(int pos){ } //if there is another header, read it and replace metadata with that one. if (metadata.isMember("moreheader") && metadata["moreheader"].asInt() > 0){ - readHeader(metadata["moreheader"].asInt()); - return; + if (metadata["moreheader"].asInt() < getBytePosEOF()){ + readHeader(metadata["moreheader"].asInt()); + return; + } } metadata["vod"] = true; metadata.netPrepare(); diff --git a/lib/dtsc.h b/lib/dtsc.h index 362f1bc3..cfb26265 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -207,7 +207,7 @@ namespace DTSC { void setBufferTime(unsigned int ms); bool isNewest(DTSC::livePos & pos); DTSC::livePos getNext(DTSC::livePos & pos, std::set & allowedTracks); - private: + private: std::map buffers; std::map > keyframes; void addPacket(JSON::Value & newPack); From 3e559fdffd8c859564964f0c6c76b01d53f7fb9f Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 1 Jul 2013 15:08:15 +0200 Subject: [PATCH 438/788] JSON library fix for empty-named objects. Also, stuff Erik did that I don't fully understand, but he made me amend his commit :'( --- lib/dtsc.cpp | 110 +++++++++++---------------------------------------- lib/dtsc.h | 1 - lib/json.cpp | 10 +++-- 3 files changed, 30 insertions(+), 91 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 6f4c0a32..66801b8f 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -207,16 +207,29 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ datapointertype = PAUSEMARK; } int keySize = metadata["tracks"][trackMapping[newPos.trackID]]["keys"].size(); - if (newPack.isMember("keyframe") || (keySize && ((newPack["time"].asInt() - 2000) > metadata["tracks"][trackMapping[newPos.trackID]]["keys"][keySize - 1]["time"].asInt())) || (!keySize)){ - keyframes[newPos.trackID].insert(newPos); - JSON::Value key; - key["time"] = newPack["time"]; - if (keySize){ - key["num"] = metadata["tracks"][trackMapping[newPos.trackID]]["keys"][keySize -1]["num"].asInt() + 1; - }else{ - key["num"] = 1; + if (buffercount > 1){ + if (newPack.isMember("keyframe") || (keySize && ((newPack["time"].asInt() - 2000) > metadata["tracks"][trackMapping[newPos.trackID]]["keys"][keySize - 1]["time"].asInt())) || (!keySize)){ + keyframes[newPos.trackID].insert(newPos); + JSON::Value key; + key["time"] = newPack["time"]; + if (keySize){ + key["num"] = metadata["tracks"][trackMapping[newPos.trackID]]["keys"][keySize -1]["num"].asInt() + 1; + metadata["tracks"][trackMapping[newPos.trackID]]["keys"][keySize - 1]["len"] = newPack["time"].asInt() - metadata["tracks"][trackMapping[newPos.trackID]]["keys"][keySize - 1]["time"].asInt(); + int size = 0; + for (JSON::ArrIter it = metadata["tracks"][trackMapping[newPos.trackID]]["keys"][keySize - 1]["parts"].ArrBegin(); it != metadata["tracks"][trackMapping[newPos.trackID]]["keys"][keySize - 1]["parts"].ArrEnd(); it++){ + size += it->asInt(); + } + metadata["tracks"][trackMapping[newPos.trackID]]["keys"][keySize -1]["size"] = size; + }else{ + key["num"] = 1; + } + metadata["tracks"][trackMapping[newPos.trackID]]["keys"].append(key); + keySize = metadata["tracks"][trackMapping[newPos.trackID]]["keys"].size(); } - metadata["tracks"][trackMapping[newPos.trackID]]["keys"].append(key); + if (keySize){ + metadata["tracks"][trackMapping[newPos.trackID]]["keys"][keySize - 1]["parts"].append((long long int)newPack["data"].asString().size()); + } + metadata["live"] = true; } unsigned int timeBuffered = 0; if (keySize > 1){ @@ -227,7 +240,7 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ buffercount++; } while (buffers.size() > buffercount){ - if (buffers.begin()->second.isMember("keyframe")){ + if (keyframes[buffers.begin()->first.trackID].count(buffers.begin()->first)){ std::string track = trackMapping[buffers.begin()->first.trackID]; keyframes[buffers.begin()->first.trackID].erase(buffers.begin()->first); int keySize = metadata["tracks"][track]["keys"].size(); @@ -235,6 +248,7 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ } buffers.erase(buffers.begin()); } + metadata.netPrepare(); } /// Returns a direct pointer to the data attribute of the last received packet, if available. @@ -327,82 +341,6 @@ DTSC::Ring * DTSC::Stream::getRing(){ void DTSC::Stream::dropRing(DTSC::Ring * ptr){ } -/// Updates the headers for a live stream, keeping track of all available -/// keyframes and their media times. The function MAY NOT be run at any other -/// time than right after receiving a new keyframe, or there'll be raptors. -void DTSC::Stream::updateHeaders(){ - if (buffers.size() > 2){ - if (buffers.begin()->second["time"].asInt() < (buffers.end()--)->second["time"].asInt()){ - std::cerr << "Detected new video - resetting all buffers and metadata - hold on, this ride might get bumpy!" << std::endl; - keyframes.clear(); - buffers.clear(); - metadata.removeMember("keytime"); - metadata.removeMember("keynum"); - metadata.removeMember("keylen"); - metadata.removeMember("frags"); - metadata.removeMember("lastms"); - metadata.removeMember("missed_frags"); - metadata.netPrepare(); - return; - } - for (JSON::ObjIter trIt = metadata["tracks"].ObjBegin(); trIt != metadata["tracks"].ObjEnd(); trIt++){ - trIt->second["keys"].shrink(keyframes[trIt->second["trackid"].asInt()].size() - 2); - } - unsigned int fragStart = 0; - if ( !metadata["frags"]){ - // this means that if we have < ~10 seconds in the buffer, fragmenting goes horribly wrong. - if ( !metadata.isMember("missed_frags")){ - metadata["missed_frags"] = 0ll; - } - }else{ - // delete fragments of which the beginning can no longer be reached - while (metadata["frags"][0u]["num"].asInt() < metadata["keynum"][0u].asInt()){ - metadata["frags"].shrink(metadata["frags"].size() - 1); - // increase the missed fragments counter - metadata["missed_frags"] = metadata["missed_frags"].asInt() + 1; - } - if (metadata["frags"].size() > 0){ - // set oldestFrag to the first keynum outside any current fragment - long long unsigned int oldestFrag = metadata["frags"][metadata["frags"].size() - 1]["num"].asInt() + metadata["frags"][metadata["frags"].size() - 1]["len"].asInt(); - // seek fragStart to the first keynum >= oldestFrag - while (metadata["keynum"][fragStart].asInt() < oldestFrag){ - fragStart++; - } - } - } - for (unsigned int i = fragStart; i < metadata["keytime"].size(); i++){ - if (i == fragStart){ - long long int currFrag = metadata["keytime"][i].asInt() / 10000; - long long int fragLen = 1; - long long int fragDur = metadata["keylen"][i].asInt(); - for (unsigned int j = i + 1; j < metadata["keytime"].size(); j++){ - // if we are now 10+ seconds, finish the fragment - if (fragDur >= 10000){ - // construct and append the fragment - JSON::Value thisFrag; - thisFrag["num"] = metadata["keynum"][i]; - thisFrag["len"] = fragLen; - thisFrag["dur"] = fragDur; - metadata["frags"].append(thisFrag); - // next fragment starts fragLen fragments up - fragStart += fragLen; - // skip that many - no unneeded looping - i += fragLen - 1; - break; - } - // otherwise, +1 the length and add up the duration - fragLen++; - fragDur += metadata["keylen"][j].asInt(); - } - } - } - //metadata["lastms"] = buffers[keyframes[0].b]["time"].asInt(); - metadata["buffer_window"] = (long long int)buffertime; - metadata["live"] = true; - metadata.netPrepare(); - } -} - /// Returns 0 if seeking is possible, -1 if the wanted frame is too old, 1 if the wanted frame is too new. int DTSC::Stream::canSeekms(unsigned int ms){ if ( !buffers.size()){ diff --git a/lib/dtsc.h b/lib/dtsc.h index cfb26265..ee27aee4 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -201,7 +201,6 @@ namespace DTSC { Ring * getRing(); unsigned int getTime(); void dropRing(Ring * ptr); - void updateHeaders(); int canSeekms(unsigned int ms); livePos msSeek(unsigned int ms, std::set & allowedTracks); void setBufferTime(unsigned int ms); diff --git a/lib/json.cpp b/lib/json.cpp index d39e2921..59bf8b13 100644 --- a/lib/json.cpp +++ b/lib/json.cpp @@ -442,10 +442,12 @@ std::string JSON::Value::toPacked(){ r += 0xE0; if (objVal.size() > 0){ for (JSON::ObjIter it = objVal.begin(); it != objVal.end(); it++){ - r += it->first.size() / 256; - r += it->first.size() % 256; - r += it->first; - r += it->second.toPacked(); + if (it->first.size() > 0){ + r += it->first.size() / 256; + r += it->first.size() % 256; + r += it->first; + r += it->second.toPacked(); + } } } r += (char)0x0; From 5be3774e3cfad6ff8121a2d50411bc9d2b316338 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 2 Jul 2013 13:51:16 +0200 Subject: [PATCH 439/788] Fixed new style lastData() function --- lib/dtsc.cpp | 11 ++--------- lib/dtsc.h | 1 - 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 66801b8f..b6437520 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -12,7 +12,6 @@ char DTSC::Magic_Packet2[] = "DTP2"; /// Initializes a DTSC::Stream with only one packet buffer. DTSC::Stream::Stream(){ datapointertype = DTSC::INVALID; - datapointer = 0; buffercount = 1; buffertime = 0; } @@ -21,7 +20,6 @@ DTSC::Stream::Stream(){ /// The actual buffer count may not at all times be the requested amount. DTSC::Stream::Stream(unsigned int rbuffers, unsigned int bufferTime){ datapointertype = DTSC::INVALID; - datapointer = 0; if (rbuffers < 1){ rbuffers = 1; } @@ -182,11 +180,6 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ buffers[newPos].toNetPacked();//make sure package is packed and ready datapointertype = INVALID; ///\todo Save keyframes when they arrive. - if (newPack.isMember("data")){ - datapointer = &(buffers[newPos]["data"].strVal); - }else{ - datapointer = 0; - } std::string tmp = ""; if (newPack.isMember("trackid")){ tmp = getTrackById(newPack["trackid"].asInt())["type"].asString(); @@ -229,7 +222,7 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ if (keySize){ metadata["tracks"][trackMapping[newPos.trackID]]["keys"][keySize - 1]["parts"].append((long long int)newPack["data"].asString().size()); } - metadata["live"] = true; + metadata["live"] = 1ll; } unsigned int timeBuffered = 0; if (keySize > 1){ @@ -254,7 +247,7 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ /// Returns a direct pointer to the data attribute of the last received packet, if available. /// Returns NULL if no valid pointer or packet is available. std::string & DTSC::Stream::lastData(){ - return *datapointer; + return buffers.rbegin()->second["data"].strVal; } /// Returns the packet in this buffer number. diff --git a/lib/dtsc.h b/lib/dtsc.h index ee27aee4..bf38e4f2 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -210,7 +210,6 @@ namespace DTSC { std::map buffers; std::map > keyframes; void addPacket(JSON::Value & newPack); - std::string * datapointer; datatype datapointertype; unsigned int buffercount; unsigned int buffertime; From 593805f0ef609d040879392a0be986f670e54379 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Wed, 3 Jul 2013 16:06:07 +0200 Subject: [PATCH 440/788] Fixed many small issues in JSON/DTSC libraries. --- lib/dtsc.cpp | 24 +++++++++++++++--------- lib/json.cpp | 36 +++++++++++++++++++++++++++++++++--- lib/json.h | 1 + 3 files changed, 49 insertions(+), 12 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index b6437520..e73a26fe 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -79,7 +79,7 @@ bool DTSC::Stream::parsePacket(std::string & buffer){ newPack = JSON::fromDTMI((unsigned char*)buffer.c_str() + 8, len, i); } if (version == 2){ - newPack = JSON::fromDTMI2(buffer.substr(8)); + newPack = JSON::fromDTMI2((unsigned char*)buffer.c_str() + 8, len, i); } addPacket(newPack); syncing = false; @@ -124,17 +124,17 @@ bool DTSC::Stream::parsePacket(Socket::Buffer & buffer){ std::string wholepacket = buffer.remove(len + 8); metadata = JSON::fromDTMI((unsigned char*)wholepacket.c_str() + 8, len, i); metadata.removeMember("moreheader"); - metadata.netPrepare(); - trackMapping.clear(); + if (buffercount > 1){ + metadata.netPrepare(); + } if (metadata.isMember("tracks")){ + trackMapping.clear(); for (JSON::ObjIter it = metadata["tracks"].ObjBegin(); it != metadata["tracks"].ObjEnd(); it++){ trackMapping.insert(std::pair(it->second["trackid"].asInt(),it->first)); } } - if ( !buffer.available(8)){ - return false; - } - header_bytes = buffer.copy(8); + //recursively calls itself until failure or data packet instead of header + return parsePacket(buffer); } int version = 0; if (memcmp(header_bytes.c_str(), DTSC::Magic_Packet, 4) == 0){ @@ -155,7 +155,7 @@ bool DTSC::Stream::parsePacket(Socket::Buffer & buffer){ newPack = JSON::fromDTMI((unsigned char*)wholepacket.c_str() + 8, len, i); } if (version == 2){ - newPack = JSON::fromDTMI2(wholepacket.substr(8)); + newPack = JSON::fromDTMI2((unsigned char*)wholepacket.c_str() + 8, len, i); } addPacket(newPack); syncing = false; @@ -229,11 +229,17 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ //increase buffer size if no keyframes available or too little time available timeBuffered = buffers.rbegin()->second["time"].asInt() - buffers.begin()->second["time"].asInt(); } - if (buffercount > 1 && (keyframes.size() < 2 || timeBuffered < buffertime)){ + if (buffercount > 1 && timeBuffered < buffertime){ buffercount++; } while (buffers.size() > buffercount){ if (keyframes[buffers.begin()->first.trackID].count(buffers.begin()->first)){ + //if there are < 3 keyframes, throwing one away would mean less than 2 left. + if (keyframes[buffers.begin()->first.trackID].size() < 3){ + //so, we don't throw it away but instead increase the buffer size + buffercount++; + break; + } std::string track = trackMapping[buffers.begin()->first.trackID]; keyframes[buffers.begin()->first.trackID].erase(buffers.begin()->first); int keySize = metadata["tracks"][track]["keys"].size(); diff --git a/lib/json.cpp b/lib/json.cpp index 59bf8b13..84b76de1 100644 --- a/lib/json.cpp +++ b/lib/json.cpp @@ -816,8 +816,14 @@ JSON::Value JSON::fromDTMI(const unsigned char * data, unsigned int len, unsigne #if DEBUG >= 10 fprintf(stderr, "Note: AMF type %hhx found. %i bytes left\n", data[i], len-i); #endif + if (i >= len){ + return JSON::Value(); + } switch (data[i]){ case 0x01: { //integer + if (i+8 >= len){ + return JSON::Value(); + } unsigned char tmpdbl[8]; tmpdbl[7] = data[i + 1]; tmpdbl[6] = data[i + 2]; @@ -833,8 +839,14 @@ JSON::Value JSON::fromDTMI(const unsigned char * data, unsigned int len, unsigne } break; case 0x02: { //string + if (i+4 >= len){ + return JSON::Value(); + } unsigned int tmpi = data[i + 1] * 256 * 256 * 256 + data[i + 2] * 256 * 256 + data[i + 3] * 256 + data[i + 4]; //set tmpi to UTF-8-long length std::string tmpstr = std::string((const char *)data + i + 5, (size_t)tmpi); //set the string data + if (i+4+tmpi >= len){ + return JSON::Value(); + } i += tmpi + 5; //skip length+size+1 forwards return JSON::Value(tmpstr); } @@ -843,7 +855,10 @@ JSON::Value JSON::fromDTMI(const unsigned char * data, unsigned int len, unsigne case 0xE0: { //object ++i; JSON::Value ret; - while (data[i] + data[i + 1] != 0){ //while not encountering 0x0000 (we assume 0x0000EE) + while (data[i] + data[i + 1] != 0 && i < len){ //while not encountering 0x0000 (we assume 0x0000EE) + if (i+2 >= len){ + return JSON::Value(); + } unsigned int tmpi = data[i] * 256 + data[i + 1]; //set tmpi to the UTF-8 length std::string tmpstr = std::string((const char *)data + i + 2, (size_t)tmpi); //set the string data i += tmpi + 2; //skip length+size forwards @@ -856,7 +871,7 @@ JSON::Value JSON::fromDTMI(const unsigned char * data, unsigned int len, unsigne case 0x0A: { //array JSON::Value ret; ++i; - while (data[i] + data[i + 1] != 0){ //while not encountering 0x0000 (we assume 0x0000EE) + while (data[i] + data[i + 1] != 0 && i < len){ //while not encountering 0x0000 (we assume 0x0000EE) ret.append(fromDTMI(data, len, i)); //add content, recursively parsed, updating i } i += 3; //skip 0x0000EE @@ -865,8 +880,9 @@ JSON::Value JSON::fromDTMI(const unsigned char * data, unsigned int len, unsigne break; } #if DEBUG >= 2 - fprintf(stderr, "Error: Unimplemented DTMI type %hhx - returning.\n", data[i]); + fprintf(stderr, "Error: Unimplemented DTMI type %hhx, @ %i / %i - returning.\n", data[i], i, len); #endif + i += 1; return JSON::Value(); } //fromOneDTMI @@ -887,3 +903,17 @@ JSON::Value JSON::fromDTMI2(std::string data){ tmp["trackid"] = tmpTrackID; return tmp; } + +JSON::Value JSON::fromDTMI2(const unsigned char * data, unsigned int len, unsigned int &i){ + JSON::Value tmp; + if (len < 13){return tmp;} + long long int tmpTrackID = ntohl(((int*)data)[0]); + long long int tmpTime = ntohl(((int*)data)[1]); + tmpTime << 32; + tmpTime += ntohl(((int*)data)[2]); + i += 12; + tmp = fromDTMI(data, len, i); + tmp["time"] = tmpTime; + tmp["trackid"] = tmpTrackID; + return tmp; +} diff --git a/lib/json.h b/lib/json.h index f54e7384..65aca525 100644 --- a/lib/json.h +++ b/lib/json.h @@ -94,6 +94,7 @@ namespace JSON { }; 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); From 68b74997fc2020f2e140439ccacb9eb9131a8d4e Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Jul 2013 16:03:23 +0200 Subject: [PATCH 441/788] Working live support once more. --- lib/dtsc.cpp | 46 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index e73a26fe..876da73c 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -176,6 +176,9 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ livePos newPos; newPos.trackID = newPack["trackid"].asInt(); newPos.seekTime = newPack["time"].asInt(); + while (buffers.count(newPos) > 0){ + newPos.seekTime++; + } buffers[newPos] = newPack; buffers[newPos].toNetPacked();//make sure package is packed and ready datapointertype = INVALID; @@ -224,21 +227,18 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ } metadata["live"] = 1ll; } - unsigned int timeBuffered = 0; - if (keySize > 1){ - //increase buffer size if no keyframes available or too little time available - timeBuffered = buffers.rbegin()->second["time"].asInt() - buffers.begin()->second["time"].asInt(); - } + //increase buffer size if too little time available + unsigned int timeBuffered = buffers.rbegin()->second["time"].asInt() - buffers.begin()->second["time"].asInt(); if (buffercount > 1 && timeBuffered < buffertime){ - buffercount++; + buffercount = buffers.size(); + if (buffercount < 2){buffercount = 2;} } + //std::cout << buffers.size() << " - " << buffercount << std::endl; while (buffers.size() > buffercount){ if (keyframes[buffers.begin()->first.trackID].count(buffers.begin()->first)){ //if there are < 3 keyframes, throwing one away would mean less than 2 left. if (keyframes[buffers.begin()->first.trackID].size() < 3){ - //so, we don't throw it away but instead increase the buffer size - buffercount++; - break; + std::cout << "Warning - track " << buffers.begin()->first.trackID << " doesn't have enough keyframes to be reliably served." << std::endl; } std::string track = trackMapping[buffers.begin()->first.trackID]; keyframes[buffers.begin()->first.trackID].erase(buffers.begin()->first); @@ -332,7 +332,14 @@ DTSC::Ring::Ring(livePos v){ /// This Ring will be kept updated so it always points to valid data or has the starved boolean set. /// Don't forget to call dropRing() for all requested Ring classes that are no longer neccessary! DTSC::Ring * DTSC::Stream::getRing(){ - return new DTSC::Ring(buffers.begin()->first); + livePos tmp = buffers.begin()->first; + std::map >::iterator it; + for (it = keyframes.begin(); it != keyframes.end(); it++){ + if ((*it->second.begin()).seekTime > tmp.seekTime){ + tmp = *it->second.begin(); + } + } + return new DTSC::Ring(tmp); } /// Deletes a given out Ring class from memory and internal Ring list. @@ -355,13 +362,24 @@ int DTSC::Stream::canSeekms(unsigned int ms){ } DTSC::livePos DTSC::Stream::msSeek(unsigned int ms, std::set & allowedTracks){ + std::set seekTracks = allowedTracks; livePos result = buffers.begin()->first; + for (std::set::iterator it = allowedTracks.begin(); it != allowedTracks.end(); it++){ + if (getTrackById(*it).isMember("type") && getTrackById(*it)["type"].asString() == "video"){ + int trackNo = *it; + seekTracks.clear(); + seekTracks.insert(trackNo); + break; + } + } for (std::map::iterator bIt = buffers.begin(); bIt != buffers.end(); bIt++){ - if (allowedTracks.find(bIt->first.trackID) != allowedTracks.end()){ - if (bIt->first.seekTime > ms){ - break; + if (seekTracks.find(bIt->first.trackID) != seekTracks.end()){ + if (bIt->second.isMember("keyframe")){ + result = bIt->first; + if (bIt->first.seekTime >= ms){ + return result; + } } - result = bIt->first; } } return result; From 768282866ea3660f5e7608f9868aeaa78a573f0f Mon Sep 17 00:00:00 2001 From: Thulinma Date: Wed, 10 Jul 2013 09:47:06 +0200 Subject: [PATCH 442/788] Fixed several calls to toNetPacked and netPrepare where it wasn't neccesary. --- lib/dtsc.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 876da73c..f9b3da3b 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -180,7 +180,9 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ newPos.seekTime++; } buffers[newPos] = newPack; - buffers[newPos].toNetPacked();//make sure package is packed and ready + if (buffercount > 1){ + buffers[newPos].toNetPacked();//make sure package is packed and ready + } datapointertype = INVALID; ///\todo Save keyframes when they arrive. std::string tmp = ""; @@ -247,7 +249,9 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ } buffers.erase(buffers.begin()); } - metadata.netPrepare(); + if (buffercount > 1){ + metadata.netPrepare(); + } } /// Returns a direct pointer to the data attribute of the last received packet, if available. From db2e630436389ea25a257f6e82530c825ca1b77f Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Wed, 10 Jul 2013 11:27:26 +0200 Subject: [PATCH 443/788] HLS working, dunno why, but it works. --- lib/dtsc.cpp | 29 +++++++++++++++++++++++++++-- lib/dtsc.h | 1 + lib/flv_tag.cpp | 2 ++ lib/ts_packet.cpp | 32 +++++++++++++++++++------------- lib/ts_packet.h | 2 +- 5 files changed, 50 insertions(+), 16 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index f9b3da3b..37ce52c7 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -81,6 +81,7 @@ bool DTSC::Stream::parsePacket(std::string & buffer){ if (version == 2){ newPack = JSON::fromDTMI2((unsigned char*)buffer.c_str() + 8, len, i); } + buffer.erase(0, len + 8); addPacket(newPack); syncing = false; return true; @@ -173,6 +174,7 @@ bool DTSC::Stream::parsePacket(Socket::Buffer & buffer){ } void DTSC::Stream::addPacket(JSON::Value & newPack){ + long long unsigned int now = Util::getMS(); livePos newPos; newPos.trackID = newPack["trackid"].asInt(); newPos.seekTime = newPack["time"].asInt(); @@ -614,7 +616,6 @@ void DTSC::File::seekNext(){ return; } clearerr(F); - seek_time(currentPositions.begin()->seekTime + 1, currentPositions.begin()->trackID); fseek(F,currentPositions.begin()->seekPos, SEEK_SET); currentPositions.erase(currentPositions.begin()); lastreadpos = ftell(F); @@ -668,10 +669,33 @@ void DTSC::File::seekNext(){ }else{ jsonbuffer = JSON::fromDTMI(strbuffer); } + int tempLoc = getBytePos(); + char newHeader[20]; + if (fread((void*)newHeader, 20, 1, F) == 1){ + if (memcmp(newHeader, DTSC::Magic_Packet2, 4) == 0){ + seekPos tmpPos; + tmpPos.seekPos = tempLoc; + tmpPos.trackID = ntohl(((int*)newHeader)[2]); + if (selectedTracks.find(tmpPos.trackID) != selectedTracks.end()){ + tmpPos.seekTime = ((long long unsigned int)ntohl(((int*)newHeader)[3])) << 32; + tmpPos.seekTime += ntohl(((int*)newHeader)[4]); + }else{ + for (JSON::ArrIter it = getTrackById(jsonbuffer["trackid"].asInt())["keys"].ArrBegin(); it != getTrackById(jsonbuffer["trackid"].asInt())["keys"].ArrEnd(); it++){ + if ((*it)["time"].asInt() > jsonbuffer["time"].asInt()){ + tmpPos.seekTime = (*it)["time"].asInt(); + tmpPos.seekPos = (*it)["bpos"].asInt(); + break; + } + } + } + currentPositions.insert(tmpPos); + } + } } void DTSC::File::parseNext(){ + lastreadpos = ftell(F); if (fread(buffer, 4, 1, F) != 1){ if (feof(F)){ #if DEBUG >= 4 @@ -769,6 +793,7 @@ bool DTSC::File::seek_time(int ms, int trackNo){ } bool foundPacket = false; while ( !foundPacket){ + lastreadpos = ftell(F); if (reachedEOF()){ return false; } @@ -787,8 +812,8 @@ bool DTSC::File::seek_time(int ms, int trackNo){ //get timestamp of packet, if too large, break, if not, skip size bytes. long long unsigned int myTime = ((long long unsigned int)ntohl(((int*)header)[3]) << 32); myTime += ntohl(((int*)header)[4]); + tmpPos.seekTime = myTime; if (myTime >= ms){ - tmpPos.seekTime = myTime; foundPacket = true; }else{ tmpPos.seekPos += 8 + packSize; diff --git a/lib/dtsc.h b/lib/dtsc.h index bf38e4f2..dc4bfbfb 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -11,6 +11,7 @@ #include //for FILE #include "json.h" #include "socket.h" +#include "timing.h" /// Holds all DDVTECH Stream Container classes and parsers. ///length (int, length in seconds, if available) diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index 8ea65b70..d093459a 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -1071,6 +1071,7 @@ JSON::Value FLV::Tag::toJSON(JSON::Value & metadata){ } pack_out["datatype"] = "audio"; pack_out["time"] = tagTime(); + pack_out["trackid"] = 2; metadata["tracks"]["track2"]["trackid"] = 2; metadata["tracks"]["track2"]["type"] = "audio"; if ( !metadata["tracks"]["track2"].isMember("codec") || metadata["tracks"]["track2"]["codec"].asString() == "?" || metadata["tracks"]["track2"]["codec"].asString() == ""){ @@ -1145,6 +1146,7 @@ JSON::Value FLV::Tag::toJSON(JSON::Value & metadata){ metadata["tracks"]["track1"]["codec"] = getVideoCodec(); } pack_out["datatype"] = "video"; + pack_out["trackid"] = 1; switch (videodata & 0xF0){ case 0x10: pack_out["keyframe"] = 1; diff --git a/lib/ts_packet.cpp b/lib/ts_packet.cpp index 97a79364..07f15f05 100644 --- a/lib/ts_packet.cpp +++ b/lib/ts_packet.cpp @@ -1,6 +1,7 @@ /// \file ts_packet.cpp /// Holds all code for the TS namespace. +#include #include "ts_packet.h" #ifndef FILLER_DATA @@ -142,18 +143,23 @@ int TS::Packet::AdaptationFieldLen(){ } /// Prints a packet to stdout, for analyser purposes. -void TS::Packet::Print(){ - std::cout << "TS Packet: " << (strBuf[0] == 0x47) << "\n\tNewUnit: " << UnitStart() << "\n\tPID: " << PID() << "\n\tContinuity Counter: " - << ContinuityCounter() << "\n\tAdaption Field: " << AdaptationField() << "\n"; +std::string TS::Packet::toPrettyString(size_t indent){ + std::stringstream output; + output << std::string(indent,' ') << "TS Packet: " << (strBuf[0] == 0x47) << std::endl; + output << std::string(indent+2,' ') << "NewUnit: " << UnitStart() << std::endl; + output << std::string(indent+2,' ') << "PID: " << PID() << std::endl; + output << std::string(indent+2,' ') << "Continuity Counter: " << ContinuityCounter() << std::endl; + output << std::string(indent+2,' ') << "Adaption Field: " << AdaptationField() << std::endl; if (AdaptationField()){ - std::cout << "\t\tAdaption Field Length: " << AdaptationFieldLen() << "\n"; + output << std::string(indent+4,' ') << "Adaptation Length: " << AdaptationFieldLen() << std::endl;; if (AdaptationFieldLen()){ - std::cout << "\t\tRandom Access: " << RandomAccess() << "\n"; + output << std::string(indent+4,' ') << "Random Access: " << RandomAccess() << std::endl; } if (PCR() != -1){ - std::cout << "\t\tPCR: " << PCR() << "( " << (double)PCR() / 27000000 << " s )\n"; + output << std::string(indent+4,' ') << "PCR: " << PCR() << "( " << (double)PCR() / 27000000 << " s )" << std::endl; } } + return output.str(); } /// Gets whether a new unit starts in this TS::Packet. @@ -275,17 +281,17 @@ void TS::Packet::PESVideoLeadIn(unsigned int NewLen, long long unsigned int PTS) /// \param NewLen The length of this frame. /// \param PTS The timestamp of the frame. void TS::Packet::PESAudioLeadIn(unsigned int NewLen, uint64_t PTS){ - NewLen += 8; + NewLen += 5; strBuf += (char)0x00; //PacketStartCodePrefix strBuf += (char)0x00; //PacketStartCodePrefix (Cont) strBuf += (char)0x01; //PacketStartCodePrefix (Cont) strBuf += (char)0xc0; //StreamType Audio strBuf += (char)((NewLen & 0xFF00) >> 8); //PES PacketLength strBuf += (char)(NewLen & 0x00FF); //PES PacketLength (Cont) - strBuf += (char)0x80; //Reserved + Flags + strBuf += (char)0x84; //Reserved + Flags strBuf += (char)0x80; //PTSOnlyFlag + Flags strBuf += (char)0x05; //PESHeaderDataLength - strBuf += (char)(0x20 + ((PTS & 0x1C0000000LL) >> 29) + 1); //PTS + strBuf += (char)(0x30 + ((PTS & 0x1C0000000LL) >> 29) + 1); //PTS strBuf += (char)((PTS & 0x03FC00000LL) >> 22); //PTS (Cont) strBuf += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //PTS (Cont) strBuf += (char)((PTS & 0x000007F80LL) >> 7); //PTS (Cont) @@ -321,12 +327,12 @@ void TS::Packet::PESVideoLeadIn(std::string & toSend, long long unsigned int PTS void TS::Packet::PESAudioLeadIn(std::string & toSend, long long unsigned int PTS){ std::string tmpStr; tmpStr.reserve(14); - unsigned int NewLen = toSend.size() + 8; + unsigned int NewLen = toSend.size() + 5; tmpStr.append("\000\000\001\300", 4); tmpStr += (char)((NewLen & 0xFF00) >> 8); //PES PacketLength tmpStr += (char)(NewLen & 0x00FF); //PES PacketLength (Cont) - tmpStr.append("\200\200\005", 3); - tmpStr += (char)(0x20 + ((PTS & 0x1C0000000LL) >> 29) + 1); //PTS + tmpStr.append("\204\200\005", 3); + tmpStr += (char)(0x30 + ((PTS & 0x1C0000000LL) >> 29) + 1); //PTS tmpStr += (char)((PTS & 0x03FC00000LL) >> 22); //PTS (Cont) tmpStr += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //PTS (Cont) tmpStr += (char)((PTS & 0x000007F80LL) >> 7); //PTS (Cont) @@ -369,7 +375,7 @@ std::string & TS::Packet::getPESAudioLeadIn(unsigned int NewLen, long long unsig tmpStr.append("\000\000\001\300", 4); tmpStr += (char)((NewLen & 0xFF00) >> 8); //PES PacketLength tmpStr += (char)(NewLen & 0x00FF); //PES PacketLength (Cont) - tmpStr.append("\200\200\005", 3); + tmpStr.append("\204\200\005", 3); tmpStr += (char)(0x20 + ((PTS & 0x1C0000000LL) >> 29) + 1); //PTS tmpStr += (char)((PTS & 0x03FC00000LL) >> 22); //PTS (Cont) tmpStr += (char)(((PTS & 0x0003F8000LL) >> 14) + 1); //PTS (Cont) diff --git a/lib/ts_packet.h b/lib/ts_packet.h index d289733d..5a1649b5 100644 --- a/lib/ts_packet.h +++ b/lib/ts_packet.h @@ -38,7 +38,7 @@ namespace TS { void RandomAccess(int NewVal); int BytesFree(); - void Print(); + std::string toPrettyString(size_t indent = 0); const char* ToString(); void PESVideoLeadIn(unsigned int NewLen, long long unsigned int PTS = 1); void PESAudioLeadIn(unsigned int NewLen, uint64_t PTS = 0); From 6a1fee6b5b72fe102f4187e8ed2b8842fdda40ee Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 11 Jul 2013 16:25:28 +0200 Subject: [PATCH 444/788] Added DTSC::Stream live fragment generation, fixed canSeekms function. --- lib/dtsc.cpp | 103 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 82 insertions(+), 21 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 37ce52c7..dc7d7e60 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -178,6 +178,7 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ livePos newPos; newPos.trackID = newPack["trackid"].asInt(); newPos.seekTime = newPack["time"].asInt(); + std::string newTrack = trackMapping[newPos.trackID]; while (buffers.count(newPos) > 0){ newPos.seekTime++; } @@ -186,7 +187,6 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ buffers[newPos].toNetPacked();//make sure package is packed and ready } datapointertype = INVALID; - ///\todo Save keyframes when they arrive. std::string tmp = ""; if (newPack.isMember("trackid")){ tmp = getTrackById(newPack["trackid"].asInt())["type"].asString(); @@ -206,38 +206,82 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ if (tmp == "pause_marker"){ datapointertype = PAUSEMARK; } - int keySize = metadata["tracks"][trackMapping[newPos.trackID]]["keys"].size(); + int keySize = metadata["tracks"][newTrack]["keys"].size(); if (buffercount > 1){ - if (newPack.isMember("keyframe") || (keySize && ((newPack["time"].asInt() - 2000) > metadata["tracks"][trackMapping[newPos.trackID]]["keys"][keySize - 1]["time"].asInt())) || (!keySize)){ + #define prevKey metadata["tracks"][newTrack]["keys"][keySize - 1] + if (newPack.isMember("keyframe") || !keySize || newPack["time"].asInt() - 2000 > prevKey["time"].asInt()){ keyframes[newPos.trackID].insert(newPos); JSON::Value key; key["time"] = newPack["time"]; if (keySize){ - key["num"] = metadata["tracks"][trackMapping[newPos.trackID]]["keys"][keySize -1]["num"].asInt() + 1; - metadata["tracks"][trackMapping[newPos.trackID]]["keys"][keySize - 1]["len"] = newPack["time"].asInt() - metadata["tracks"][trackMapping[newPos.trackID]]["keys"][keySize - 1]["time"].asInt(); + key["num"] = prevKey["num"].asInt() + 1; + prevKey["len"] = newPack["time"].asInt() - prevKey["time"].asInt(); int size = 0; - for (JSON::ArrIter it = metadata["tracks"][trackMapping[newPos.trackID]]["keys"][keySize - 1]["parts"].ArrBegin(); it != metadata["tracks"][trackMapping[newPos.trackID]]["keys"][keySize - 1]["parts"].ArrEnd(); it++){ + for (JSON::ArrIter it = prevKey["parts"].ArrBegin(); it != prevKey["parts"].ArrEnd(); it++){ size += it->asInt(); } - metadata["tracks"][trackMapping[newPos.trackID]]["keys"][keySize -1]["size"] = size; + prevKey["size"] = size; + long long int bps = (double)prevKey["size"].asInt() / ((double)prevKey["len"].asInt() / 1000.0); + if (bps > metadata["tracks"][newTrack]["maxbps"].asInt()){ + metadata["tracks"][newTrack]["maxbps"] = (long long int)(bps * 1.2); + } }else{ key["num"] = 1; } - metadata["tracks"][trackMapping[newPos.trackID]]["keys"].append(key); - keySize = metadata["tracks"][trackMapping[newPos.trackID]]["keys"].size(); + metadata["tracks"][newTrack]["keys"].append(key); + keySize = metadata["tracks"][newTrack]["keys"].size(); + + //find the last fragment + JSON::Value lastFrag; + if (metadata["tracks"][newTrack]["frags"].size() > 0){ + lastFrag = metadata["tracks"][newTrack]["frags"][metadata["tracks"][newTrack]["frags"].size() - 1]; + } + //find the first keyframe past the last fragment + JSON::ArrIter fragIt = metadata["tracks"][newTrack]["keys"].ArrBegin(); + while (fragIt != metadata["tracks"][newTrack]["keys"].ArrEnd() && fragIt != metadata["tracks"][newTrack]["keys"].ArrEnd() - 1 && (*fragIt)["num"].asInt() < lastFrag["num"].asInt() + lastFrag["len"].asInt()){ + fragIt++; + } + //continue only if a keyframe was found + if (fragIt != metadata["tracks"][newTrack]["keys"].ArrEnd() && fragIt != metadata["tracks"][newTrack]["keys"].ArrEnd() - 1){ + //calculate the variables of the new fragment + JSON::Value newFrag; + newFrag["num"] = (*fragIt)["num"]; + newFrag["time"] = (*fragIt)["time"]; + newFrag["len"] = 1ll; + newFrag["dur"] = (*fragIt)["len"]; + fragIt++; + //keep calculating until 10+ seconds or no more keyframes + while (fragIt != metadata["tracks"][newTrack]["keys"].ArrEnd() && fragIt != metadata["tracks"][newTrack]["keys"].ArrEnd() - 1){ + newFrag["len"] = newFrag["len"].asInt() + 1; + newFrag["dur"] = newFrag["dur"].asInt() + (*fragIt)["len"].asInt(); + //more than 10 seconds? store the new fragment + if (newFrag["dur"].asInt() >= 10000){ + /// \todo Make this variable instead of hardcoded 10 seconds? + metadata["tracks"][newTrack]["frags"].append(newFrag); + break; + } + fragIt++; + } + } } if (keySize){ - metadata["tracks"][trackMapping[newPos.trackID]]["keys"][keySize - 1]["parts"].append((long long int)newPack["data"].asString().size()); + metadata["tracks"][newTrack]["keys"][keySize - 1]["parts"].append((long long int)newPack["data"].asString().size()); } metadata["live"] = 1ll; } + //increase buffer size if too little time available unsigned int timeBuffered = buffers.rbegin()->second["time"].asInt() - buffers.begin()->second["time"].asInt(); - if (buffercount > 1 && timeBuffered < buffertime){ - buffercount = buffers.size(); - if (buffercount < 2){buffercount = 2;} + if (buffercount > 1){ + if (timeBuffered < buffertime){ + buffercount = buffers.size(); + if (buffercount < 2){buffercount = 2;} + } + if (metadata["buffer_window"].asInt() < timeBuffered){ + metadata["buffer_window"] = (long long int)timeBuffered; + } } - //std::cout << buffers.size() << " - " << buffercount << std::endl; + while (buffers.size() > buffercount){ if (keyframes[buffers.begin()->first.trackID].count(buffers.begin()->first)){ //if there are < 3 keyframes, throwing one away would mean less than 2 left. @@ -248,6 +292,14 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ keyframes[buffers.begin()->first.trackID].erase(buffers.begin()->first); int keySize = metadata["tracks"][track]["keys"].size(); metadata["tracks"][track]["keys"].shrink(keySize - 1); + if (metadata["tracks"][track]["frags"].size() > 0){ + // delete fragments of which the beginning can no longer be reached + while (metadata["tracks"][track]["frags"][0u]["num"].asInt() < metadata["tracks"][track]["keys"][0u]["num"].asInt()){ + metadata["tracks"][track]["frags"].shrink(metadata["tracks"][track]["frags"].size() - 1); + // increase the missed fragments counter + metadata["tracks"][track]["missed_frags"] = metadata["tracks"][track]["missed_frags"].asInt() + 1; + } + } } buffers.erase(buffers.begin()); } @@ -354,17 +406,26 @@ void DTSC::Stream::dropRing(DTSC::Ring * ptr){ } /// Returns 0 if seeking is possible, -1 if the wanted frame is too old, 1 if the wanted frame is too new. +/// This function looks in the header - not in the buffered data itself. int DTSC::Stream::canSeekms(unsigned int ms){ - if ( !buffers.size()){ + bool too_late = false; + //no tracks? Frame too new by definition. + if ( !metadata.isMember("tracks") || metadata["tracks"].size() < 1){ return 1; } - if (ms > buffers.rbegin()->second["time"].asInt()){ - return 1; + //loop trough all the tracks + for (JSON::ObjIter it = metadata["tracks"].ObjBegin(); it != metadata["tracks"].ObjEnd(); it++){ + if (it->second.isMember("keys") && it->second["keys"].size() > 0){ + if (it->second["keys"][0u]["time"].asInt() <= ms && it->second["keys"][it->second["keys"].size() - 1]["time"].asInt() >= ms){ + return 0; + } + if (it->second["keys"][0u]["time"].asInt() > ms){too_late = true;} + } } - if (ms < buffers.begin()->second["time"].asInt()){ - return -1; - } - return 0; + //did we spot a track already past this point? return too late. + if (too_late){return -1;} + //otherwise, assume not available yet + return 1; } DTSC::livePos DTSC::Stream::msSeek(unsigned int ms, std::set & allowedTracks){ From a906da6caff78377ed5d728a06177a51fbab0aec Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 18 Jul 2013 15:38:21 +0200 Subject: [PATCH 445/788] Fixed FLV MP3 audio, some fixes for live support. --- lib/dtsc.cpp | 23 ++++++++++++++++++++++- lib/dtsc.h | 7 +++++++ lib/flv_tag.cpp | 2 +- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index dc7d7e60..16be9230 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -173,11 +173,31 @@ bool DTSC::Stream::parsePacket(Socket::Buffer & buffer){ return false; } +/// Adds a keyframe packet to all tracks, so the stream can be fully played. +void DTSC::Stream::endStream(){ + if (metadata.isMember("tracks")){ + for (JSON::ObjIter it = metadata["tracks"].ObjBegin(); it != metadata["tracks"].ObjEnd(); it++){ + JSON::Value newPack; + newPack["time"] = it->second["lastms"]; + newPack["trackid"] = it->second["trackid"]; + newPack["keyframe"] = 1ll; + newPack["data"] = ""; + addPacket(newPack); + } + } +} + void DTSC::Stream::addPacket(JSON::Value & newPack){ long long unsigned int now = Util::getMS(); livePos newPos; newPos.trackID = newPack["trackid"].asInt(); newPos.seekTime = newPack["time"].asInt(); + if (buffers.size() > 0){ + livePos lastPos = buffers.rbegin()->first; + if (newPos < lastPos){ + newPos.seekTime = lastPos.seekTime+1; + } + } std::string newTrack = trackMapping[newPos.trackID]; while (buffers.count(newPos) > 0){ newPos.seekTime++; @@ -208,8 +228,9 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ } int keySize = metadata["tracks"][newTrack]["keys"].size(); if (buffercount > 1){ + metadata["tracks"][newTrack]["lastms"] = newPack["time"]; #define prevKey metadata["tracks"][newTrack]["keys"][keySize - 1] - if (newPack.isMember("keyframe") || !keySize || newPack["time"].asInt() - 2000 > prevKey["time"].asInt()){ + if (newPack.isMember("keyframe") || !keySize || (datapointertype != VIDEO && newPack["time"].asInt() - 2000 > prevKey["time"].asInt())){ keyframes[newPos.trackID].insert(newPos); JSON::Value key; key["time"] = newPack["time"]; diff --git a/lib/dtsc.h b/lib/dtsc.h index dc4bfbfb..3a1e9f18 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -149,6 +149,12 @@ namespace DTSC { 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; @@ -207,6 +213,7 @@ namespace DTSC { void setBufferTime(unsigned int ms); bool isNewest(DTSC::livePos & pos); DTSC::livePos getNext(DTSC::livePos & pos, std::set & allowedTracks); + void endStream(); private: std::map buffers; std::map > keyframes; diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index d093459a..c712881d 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -1075,7 +1075,7 @@ JSON::Value FLV::Tag::toJSON(JSON::Value & metadata){ metadata["tracks"]["track2"]["trackid"] = 2; metadata["tracks"]["track2"]["type"] = "audio"; if ( !metadata["tracks"]["track2"].isMember("codec") || metadata["tracks"]["track2"]["codec"].asString() == "?" || metadata["tracks"]["track2"]["codec"].asString() == ""){ - metadata["audio"]["codec"] = getAudioCodec(); + metadata["tracks"]["track2"]["codec"] = getAudioCodec(); } if ( !metadata["tracks"]["track2"].isMember("rate") || metadata["tracks"]["track2"]["rate"].asInt() < 1){ switch (audiodata & 0x0C){ From 3af710e30dee4e29d25d1be4407822a788e0475b Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 18 Jul 2013 16:40:46 +0200 Subject: [PATCH 446/788] Fixed FLV MP3 audio, some fixes for live support. --- lib/dtsc.cpp | 26 ++++++++++++++++++++++++-- lib/dtsc.h | 7 +++++++ lib/flv_tag.cpp | 2 +- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index dc7d7e60..2f191573 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -173,11 +173,32 @@ bool DTSC::Stream::parsePacket(Socket::Buffer & buffer){ return false; } +/// Adds a keyframe packet to all tracks, so the stream can be fully played. +void DTSC::Stream::endStream(){ + if (metadata.isMember("tracks")){ + for (JSON::ObjIter it = metadata["tracks"].ObjBegin(); it != metadata["tracks"].ObjEnd(); it++){ + JSON::Value newPack; + newPack["time"] = it->second["lastms"]; + newPack["trackid"] = it->second["trackid"]; + newPack["keyframe"] = 1ll; + newPack["data"] = ""; + addPacket(newPack); + addPacket(newPack); + } + } +} + void DTSC::Stream::addPacket(JSON::Value & newPack){ long long unsigned int now = Util::getMS(); livePos newPos; newPos.trackID = newPack["trackid"].asInt(); newPos.seekTime = newPack["time"].asInt(); + if (buffers.size() > 0){ + livePos lastPos = buffers.rbegin()->first; + if (newPos < lastPos){ + newPos.seekTime = lastPos.seekTime+1; + } + } std::string newTrack = trackMapping[newPos.trackID]; while (buffers.count(newPos) > 0){ newPos.seekTime++; @@ -208,8 +229,9 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ } int keySize = metadata["tracks"][newTrack]["keys"].size(); if (buffercount > 1){ + metadata["tracks"][newTrack]["lastms"] = newPack["time"]; #define prevKey metadata["tracks"][newTrack]["keys"][keySize - 1] - if (newPack.isMember("keyframe") || !keySize || newPack["time"].asInt() - 2000 > prevKey["time"].asInt()){ + if (newPack.isMember("keyframe") || !keySize || (datapointertype != VIDEO && newPack["time"].asInt() - 2000 > prevKey["time"].asInt())){ keyframes[newPos.trackID].insert(newPos); JSON::Value key; key["time"] = newPack["time"]; @@ -255,7 +277,7 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ newFrag["len"] = newFrag["len"].asInt() + 1; newFrag["dur"] = newFrag["dur"].asInt() + (*fragIt)["len"].asInt(); //more than 10 seconds? store the new fragment - if (newFrag["dur"].asInt() >= 10000){ + if (newFrag["dur"].asInt() >= 10000 || (*fragIt)["len"].asInt() < 2){ /// \todo Make this variable instead of hardcoded 10 seconds? metadata["tracks"][newTrack]["frags"].append(newFrag); break; diff --git a/lib/dtsc.h b/lib/dtsc.h index dc4bfbfb..3a1e9f18 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -149,6 +149,12 @@ namespace DTSC { 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; @@ -207,6 +213,7 @@ namespace DTSC { void setBufferTime(unsigned int ms); bool isNewest(DTSC::livePos & pos); DTSC::livePos getNext(DTSC::livePos & pos, std::set & allowedTracks); + void endStream(); private: std::map buffers; std::map > keyframes; diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index d093459a..c712881d 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -1075,7 +1075,7 @@ JSON::Value FLV::Tag::toJSON(JSON::Value & metadata){ metadata["tracks"]["track2"]["trackid"] = 2; metadata["tracks"]["track2"]["type"] = "audio"; if ( !metadata["tracks"]["track2"].isMember("codec") || metadata["tracks"]["track2"]["codec"].asString() == "?" || metadata["tracks"]["track2"]["codec"].asString() == ""){ - metadata["audio"]["codec"] = getAudioCodec(); + metadata["tracks"]["track2"]["codec"] = getAudioCodec(); } if ( !metadata["tracks"]["track2"].isMember("rate") || metadata["tracks"]["track2"]["rate"].asInt() < 1){ switch (audiodata & 0x0C){ From 6666cf22f0d5493cdd1e14b7a4c012621c3b09f7 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Mon, 22 Jul 2013 11:36:19 +0200 Subject: [PATCH 447/788] Updates for buffer stream resetting. --- lib/dtsc.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 16be9230..d0b4c868 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -195,7 +195,15 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ if (buffers.size() > 0){ livePos lastPos = buffers.rbegin()->first; if (newPos < lastPos){ - newPos.seekTime = lastPos.seekTime+1; + if (newPos.seekTime < lastPos.seekTime - 1000){ + metadata.null(); + metadata["reset"] = 1LL; + buffers.clear; + keyframes.clear(); + trackMapping.clear(); + }else{ + newPos.seekTime = lastPos.seekTime+1; + } } } std::string newTrack = trackMapping[newPos.trackID]; From c394a1a767fb716679e2af6256db83b26a15e78d Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Mon, 22 Jul 2013 11:36:53 +0200 Subject: [PATCH 448/788] Edits in dtsc lib to only use optimized parsing if the file is merged. --- lib/dtsc.cpp | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index d0b4c868..3275465c 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -706,6 +706,9 @@ void DTSC::File::seekNext(){ return; } clearerr(F); + if ( !metadata.isMember("merged") || !metadata["merged"]){ + seek_time(currentPositions.begin()->seekTime + 1, currentPositions.begin()->trackID); + } fseek(F,currentPositions.begin()->seekPos, SEEK_SET); currentPositions.erase(currentPositions.begin()); lastreadpos = ftell(F); @@ -759,26 +762,28 @@ void DTSC::File::seekNext(){ }else{ jsonbuffer = JSON::fromDTMI(strbuffer); } - int tempLoc = getBytePos(); - char newHeader[20]; - if (fread((void*)newHeader, 20, 1, F) == 1){ - if (memcmp(newHeader, DTSC::Magic_Packet2, 4) == 0){ - seekPos tmpPos; - tmpPos.seekPos = tempLoc; - tmpPos.trackID = ntohl(((int*)newHeader)[2]); - if (selectedTracks.find(tmpPos.trackID) != selectedTracks.end()){ - tmpPos.seekTime = ((long long unsigned int)ntohl(((int*)newHeader)[3])) << 32; - tmpPos.seekTime += ntohl(((int*)newHeader)[4]); - }else{ - for (JSON::ArrIter it = getTrackById(jsonbuffer["trackid"].asInt())["keys"].ArrBegin(); it != getTrackById(jsonbuffer["trackid"].asInt())["keys"].ArrEnd(); it++){ - if ((*it)["time"].asInt() > jsonbuffer["time"].asInt()){ - tmpPos.seekTime = (*it)["time"].asInt(); - tmpPos.seekPos = (*it)["bpos"].asInt(); - break; + if (metadata.isMember("merged") && metadata["merged"]){ + int tempLoc = getBytePos(); + char newHeader[20]; + if (fread((void*)newHeader, 20, 1, F) == 1){ + if (memcmp(newHeader, DTSC::Magic_Packet2, 4) == 0){ + seekPos tmpPos; + tmpPos.seekPos = tempLoc; + tmpPos.trackID = ntohl(((int*)newHeader)[2]); + if (selectedTracks.find(tmpPos.trackID) != selectedTracks.end()){ + tmpPos.seekTime = ((long long unsigned int)ntohl(((int*)newHeader)[3])) << 32; + tmpPos.seekTime += ntohl(((int*)newHeader)[4]); + }else{ + for (JSON::ArrIter it = getTrackById(jsonbuffer["trackid"].asInt())["keys"].ArrBegin(); it != getTrackById(jsonbuffer["trackid"].asInt())["keys"].ArrEnd(); it++){ + if ((*it)["time"].asInt() > jsonbuffer["time"].asInt()){ + tmpPos.seekTime = (*it)["time"].asInt(); + tmpPos.seekPos = (*it)["bpos"].asInt(); + break; + } } } + currentPositions.insert(tmpPos); } - currentPositions.insert(tmpPos); } } } From 47baa483c8dc9136d224e94187ae212832fe15b8 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Mon, 22 Jul 2013 11:54:13 +0200 Subject: [PATCH 449/788] Fix'ed a typo. --- lib/dtsc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 14aaf9a7..1a3b4e01 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -198,7 +198,7 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ if (newPos.seekTime < lastPos.seekTime - 1000){ metadata.null(); metadata["reset"] = 1LL; - buffers.clear; + buffers.clear(); keyframes.clear(); trackMapping.clear(); }else{ From 867fa715eb5f9b95024825b552ee22e6451bb4fa Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Mon, 22 Jul 2013 14:38:01 +0200 Subject: [PATCH 450/788] Fixed CPU Usage in the buffer. --- lib/dtsc.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 1a3b4e01..80059206 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -188,6 +188,7 @@ void DTSC::Stream::endStream(){ } void DTSC::Stream::addPacket(JSON::Value & newPack){ + bool updateMeta = false; long long unsigned int now = Util::getMS(); livePos newPos; newPos.trackID = newPack["trackid"].asInt(); @@ -195,7 +196,7 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ if (buffers.size() > 0){ livePos lastPos = buffers.rbegin()->first; if (newPos < lastPos){ - if (newPos.seekTime < lastPos.seekTime - 1000){ + if ((lastPos.seekTime > 1000) && newPos.seekTime < lastPos.seekTime - 1000){ metadata.null(); metadata["reset"] = 1LL; buffers.clear(); @@ -239,6 +240,7 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ metadata["tracks"][newTrack]["lastms"] = newPack["time"]; #define prevKey metadata["tracks"][newTrack]["keys"][keySize - 1] if (newPack.isMember("keyframe") || !keySize || (datapointertype != VIDEO && newPack["time"].asInt() - 2000 > prevKey["time"].asInt())){ + updateMeta = true; keyframes[newPos.trackID].insert(newPos); JSON::Value key; key["time"] = newPack["time"]; @@ -306,13 +308,14 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ buffercount = buffers.size(); if (buffercount < 2){buffercount = 2;} } - if (metadata["buffer_window"].asInt() < timeBuffered){ + if (updateMeta && metadata["buffer_window"].asInt() < timeBuffered){ metadata["buffer_window"] = (long long int)timeBuffered; } } while (buffers.size() > buffercount){ if (keyframes[buffers.begin()->first.trackID].count(buffers.begin()->first)){ + updateMeta = true; //if there are < 3 keyframes, throwing one away would mean less than 2 left. if (keyframes[buffers.begin()->first.trackID].size() < 3){ std::cout << "Warning - track " << buffers.begin()->first.trackID << " doesn't have enough keyframes to be reliably served." << std::endl; @@ -332,7 +335,7 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ } buffers.erase(buffers.begin()); } - if (buffercount > 1){ + if (updateMeta){ metadata.netPrepare(); } } From 6a72fddf7b0fde9bc1ccce265016b1c2bf94ff3f Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 23 Jul 2013 14:28:35 +0200 Subject: [PATCH 451/788] Fixed edge case of interrupt handler for catching ended child processes. --- lib/procs.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/procs.cpp b/lib/procs.cpp index 5b61ea18..7afa1851 100644 --- a/lib/procs.cpp +++ b/lib/procs.cpp @@ -95,6 +95,10 @@ void Util::Procs::childsig_handler(int signum){ if (ret == 0){ //ignore, would block otherwise return; }else if (ret < 0){ + if (errno == EINTR){ + childsig_handler(signum); + return; + } #if DEBUG >= 3 std::cerr << "SIGCHLD received, but no child died"; #endif From 9cdf8d92a1ab6e7dcfde837706e4688bc46b1148 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 23 Jul 2013 15:52:34 +0200 Subject: [PATCH 452/788] Fixed a bug. (DTSC lib mistakenly wiped metadata when seeking backwards, re-re-fixed proc child signal problems) --- lib/dtsc.cpp | 6 +++--- lib/procs.cpp | 14 +++----------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 80059206..02995228 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -193,7 +193,7 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ livePos newPos; newPos.trackID = newPack["trackid"].asInt(); newPos.seekTime = newPack["time"].asInt(); - if (buffers.size() > 0){ + if (buffercount > 1 && buffers.size() > 0){ livePos lastPos = buffers.rbegin()->first; if (newPos < lastPos){ if ((lastPos.seekTime > 1000) && newPos.seekTime < lastPos.seekTime - 1000){ @@ -314,11 +314,11 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ } while (buffers.size() > buffercount){ - if (keyframes[buffers.begin()->first.trackID].count(buffers.begin()->first)){ + if (buffercount > 1 && keyframes[buffers.begin()->first.trackID].count(buffers.begin()->first)){ updateMeta = true; //if there are < 3 keyframes, throwing one away would mean less than 2 left. if (keyframes[buffers.begin()->first.trackID].size() < 3){ - std::cout << "Warning - track " << buffers.begin()->first.trackID << " doesn't have enough keyframes to be reliably served." << std::endl; + std::cerr << "Warning - track " << buffers.begin()->first.trackID << " doesn't have enough keyframes to be reliably served." << std::endl; } std::string track = trackMapping[buffers.begin()->first.trackID]; keyframes[buffers.begin()->first.trackID].erase(buffers.begin()->first); diff --git a/lib/procs.cpp b/lib/procs.cpp index 7afa1851..0fa99625 100644 --- a/lib/procs.cpp +++ b/lib/procs.cpp @@ -91,17 +91,9 @@ void Util::Procs::childsig_handler(int signum){ return; } int status; - pid_t ret = waitpid( -1, &status, WNOHANG); - if (ret == 0){ //ignore, would block otherwise - return; - }else if (ret < 0){ - if (errno == EINTR){ - childsig_handler(signum); - return; - } -#if DEBUG >= 3 - std::cerr << "SIGCHLD received, but no child died"; -#endif + pid_t ret = waitpid( -1, &status, 0); + if (ret <= 0){ + childsig_handler(signum); return; } int exitcode; From 588e1717336d9bc7a9c5be9e81e1fe02298786e0 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 23 Jul 2013 16:23:52 +0200 Subject: [PATCH 453/788] Those last two commit, but slightly better. --- lib/dtsc.cpp | 4 +++- lib/procs.cpp | 56 +++++++++++++++++++++++++++------------------------ 2 files changed, 33 insertions(+), 27 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 02995228..f14c0516 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -206,6 +206,8 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ newPos.seekTime = lastPos.seekTime+1; } } + }else{ + buffers.clear(); } std::string newTrack = trackMapping[newPos.trackID]; while (buffers.count(newPos) > 0){ @@ -237,10 +239,10 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ } int keySize = metadata["tracks"][newTrack]["keys"].size(); if (buffercount > 1){ - metadata["tracks"][newTrack]["lastms"] = newPack["time"]; #define prevKey metadata["tracks"][newTrack]["keys"][keySize - 1] if (newPack.isMember("keyframe") || !keySize || (datapointertype != VIDEO && newPack["time"].asInt() - 2000 > prevKey["time"].asInt())){ updateMeta = true; + metadata["tracks"][newTrack]["lastms"] = newPack["time"]; keyframes[newPos.trackID].insert(newPos); JSON::Value key; key["time"] = newPack["time"]; diff --git a/lib/procs.cpp b/lib/procs.cpp index 0fa99625..abeb09d2 100644 --- a/lib/procs.cpp +++ b/lib/procs.cpp @@ -91,40 +91,44 @@ void Util::Procs::childsig_handler(int signum){ return; } int status; - pid_t ret = waitpid( -1, &status, 0); - if (ret <= 0){ - childsig_handler(signum); - return; - } - int exitcode; - if (WIFEXITED(status)){ - exitcode = WEXITSTATUS(status); - }else if (WIFSIGNALED(status)){ - exitcode = -WTERMSIG(status); - }else{/* not possible */ - return; - } + 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; + } + } + int exitcode; + if (WIFEXITED(status)){ + exitcode = WEXITSTATUS(status); + }else if (WIFSIGNALED(status)){ + exitcode = -WTERMSIG(status); + }else{/* not possible */ + return; + } #if DEBUG >= 1 - std::string pname = plist[ret]; + std::string pname = plist[ret]; #endif - plist.erase(ret); + plist.erase(ret); #if DEBUG >= 1 - if (isActive(pname)){ - Stop(pname); - } else{ - //can this ever happen? - std::cerr << "Process " << pname << " fully terminated." << std::endl; - } + if (isActive(pname)){ + Stop(pname); + } else{ + //can this ever happen? + std::cerr << "Process " << pname << " fully terminated." << std::endl; + } #endif - if (exitHandlers.count(ret) > 0){ - TerminationNotifier tn = exitHandlers[ret]; - exitHandlers.erase(ret); + if (exitHandlers.count(ret) > 0){ + TerminationNotifier tn = exitHandlers[ret]; + exitHandlers.erase(ret); #if DEBUG >= 2 - std::cerr << "Calling termination handler for " << pname << std::endl; + std::cerr << "Calling termination handler for " << pname << std::endl; #endif - tn(ret, exitcode); + tn(ret, exitcode); + } } } From 5b5d2d4f409247581d5c7f5b7c98cdc38a7c4cfa Mon Sep 17 00:00:00 2001 From: Thulinma Date: Wed, 24 Jul 2013 13:26:24 +0200 Subject: [PATCH 454/788] Changed default fragment duration from 10s to 5s. --- lib/dtsc.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index f14c0516..952bba8d 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -287,9 +287,9 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ while (fragIt != metadata["tracks"][newTrack]["keys"].ArrEnd() && fragIt != metadata["tracks"][newTrack]["keys"].ArrEnd() - 1){ newFrag["len"] = newFrag["len"].asInt() + 1; newFrag["dur"] = newFrag["dur"].asInt() + (*fragIt)["len"].asInt(); - //more than 10 seconds? store the new fragment - if (newFrag["dur"].asInt() >= 10000 || (*fragIt)["len"].asInt() < 2){ - /// \todo Make this variable instead of hardcoded 10 seconds? + //more than 5 seconds? store the new fragment + if (newFrag["dur"].asInt() >= 5000 || (*fragIt)["len"].asInt() < 2){ + /// \todo Make this variable instead of hardcoded 5 seconds? metadata["tracks"][newTrack]["frags"].append(newFrag); break; } From ba03efc07db1c635a4402072045f76e6ee13829a Mon Sep 17 00:00:00 2001 From: ThatGuy Date: Mon, 29 Jul 2013 14:12:42 +0200 Subject: [PATCH 455/788] workaround issue with endStream --- lib/dtsc.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 952bba8d..a7a83c88 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -175,14 +175,16 @@ bool DTSC::Stream::parsePacket(Socket::Buffer & buffer){ /// Adds a keyframe packet to all tracks, so the stream can be fully played. void DTSC::Stream::endStream(){ - if (metadata.isMember("tracks")){ + if (metadata.isMember("tracks") && metadata["tracks"].size() > 0){ for (JSON::ObjIter it = metadata["tracks"].ObjBegin(); it != metadata["tracks"].ObjEnd(); it++){ - JSON::Value newPack; - newPack["time"] = it->second["lastms"]; - newPack["trackid"] = it->second["trackid"]; - newPack["keyframe"] = 1ll; - newPack["data"] = ""; - addPacket(newPack); + if(it->second.isMember("lastms") && it->second.isMember("trackid")){ // TODO + JSON::Value newPack; + newPack["time"] = it->second["lastms"]; + newPack["trackid"] = it->second["trackid"]; + newPack["keyframe"] = 1ll; + newPack["data"] = ""; + addPacket(newPack); + } } } } @@ -328,7 +330,7 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ metadata["tracks"][track]["keys"].shrink(keySize - 1); if (metadata["tracks"][track]["frags"].size() > 0){ // delete fragments of which the beginning can no longer be reached - while (metadata["tracks"][track]["frags"][0u]["num"].asInt() < metadata["tracks"][track]["keys"][0u]["num"].asInt()){ + while (metadata["tracks"][track]["frags"].size() > 0 && metadata["tracks"][track]["frags"][0u]["num"].asInt() < metadata["tracks"][track]["keys"][0u]["num"].asInt()){ metadata["tracks"][track]["frags"].shrink(metadata["tracks"][track]["frags"].size() - 1); // increase the missed fragments counter metadata["tracks"][track]["missed_frags"] = metadata["tracks"][track]["missed_frags"].asInt() + 1; @@ -338,7 +340,7 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ buffers.erase(buffers.begin()); } if (updateMeta){ - metadata.netPrepare(); + //metadata.netPrepare(); } } From 0daa757e9292e312df78fda5763d64766ef5bd06 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Wed, 7 Aug 2013 16:04:34 +0200 Subject: [PATCH 456/788] Added getOutputOf functions to easily retrieve output of executed applications. --- lib/procs.cpp | 34 ++++++++++++++++++++++++++++++++++ lib/procs.h | 2 ++ 2 files changed, 36 insertions(+) diff --git a/lib/procs.cpp b/lib/procs.cpp index abeb09d2..f7942272 100644 --- a/lib/procs.cpp +++ b/lib/procs.cpp @@ -132,6 +132,40 @@ void Util::Procs::childsig_handler(int signum){ } } + +/// Runs the given command and returns the stdout output as a string. +std::string Util::Procs::getOutputOf(char * 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); + 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; + } + 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 diff --git a/lib/procs.h b/lib/procs.h index 4f24ff5c..16f75a6d 100644 --- a/lib/procs.h +++ b/lib/procs.h @@ -23,6 +23,8 @@ namespace Util { static void runCmd(std::string & cmd); static void setHandler(); public: + static std::string getOutputOf(char * 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); From 0c059f0ca3536114a05cb2e0a2a5923711834a7b Mon Sep 17 00:00:00 2001 From: Thulinma Date: Wed, 7 Aug 2013 16:04:57 +0200 Subject: [PATCH 457/788] Added better generic support for connector options. --- lib/config.cpp | 98 ++++++++++++++++++++++++++++++++++++++++---------- lib/config.h | 8 +++-- 2 files changed, 86 insertions(+), 20 deletions(-) diff --git a/lib/config.cpp b/lib/config.cpp index 1df00ad8..e7710e7c 100644 --- a/lib/config.cpp +++ b/lib/config.cpp @@ -21,6 +21,7 @@ #include #include #include +#include //for getMyExec bool Util::Config::is_active = false; std::string Util::Config::libver = PACKAGE_VERSION; @@ -167,7 +168,7 @@ void Util::Config::printHelp(std::ostream & output){ /// Parses commandline arguments. /// Calls exit if an unknown option is encountered, printing a help message. -void Util::Config::parseArgs(int & argc, char ** & argv){ +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)); @@ -250,10 +251,9 @@ void Util::Config::parseArgs(int & argc, char ** & argv){ long_i++; } if (long_i <= arg_count){ - std::cerr << "Usage error: missing argument(s)." << std::endl; - printHelp(std::cout); - exit(1); + return false; } + return true; } /// Returns a reference to the current value of an option or default if none was set. @@ -334,22 +334,67 @@ void Util::Config::signal_handler(int signum){ } } //signal_handler -/// Adds the default connector options to this Util::Config object. -void Util::Config::addConnectorOptions(int port){ - JSON::Value stored_port = JSON::fromString("{\"long\":\"port\", \"short\":\"p\", \"arg\":\"integer\", \"help\":\"TCP port to listen on.\"}"); - stored_port["value"].append((long long int)port); - addOption("listen_port", stored_port); - addOption("listen_interface", - JSON::fromString( - "{\"long\":\"interface\", \"value\":[\"0.0.0.0\"], \"short\":\"i\", \"arg\":\"string\", \"help\":\"Interface address to listen on, or 0.0.0.0 for all available interfaces.\"}")); - addOption("username", - JSON::fromString( - "{\"long\":\"username\", \"value\":[\"root\"], \"short\":\"u\", \"arg\":\"string\", \"help\":\"Username to drop privileges to, or root to not drop provileges.\"}")); - addOption("daemonize", - JSON::fromString( - "{\"long\":\"daemon\", \"short\":\"d\", \"value\":[1], \"long_off\":\"nodaemon\", \"short_off\":\"n\", \"help\":\"Whether or not to daemonize the process after starting.\"}")); +/// 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"]["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"]["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"]["type"] = "str"; + + 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(1ll); + 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]; @@ -371,6 +416,23 @@ std::string Util::getMyPath(){ return tPath; } +/// Gets all executables in getMyPath that start with "Mist". +void Util::getMyExec(std::deque & execs){ + std::string path = Util::getMyPath(); + 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); +} + /// Sets the current process' running user void Util::setUser(std::string username){ if (username != "root"){ diff --git a/lib/config.h b/lib/config.h index 8ed0fb5d..4d35fa56 100644 --- a/lib/config.h +++ b/lib/config.h @@ -27,18 +27,22 @@ namespace Util { Config(std::string cmd, std::string version); void addOption(std::string optname, JSON::Value option); void printHelp(std::ostream & output); - void parseArgs(int & argc, char ** & argv); + 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(); - void addConnectorOptions(int port); + 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 & execs); + /// Will set the active user to the named username. void setUser(std::string user); From ea71dd2d5c62597b90f82560444356a175743106 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 12 Aug 2013 13:18:48 +0200 Subject: [PATCH 458/788] Some small optimizations, added reference functions to JSON lib, fixed config commandline options. --- lib/config.cpp | 3 +++ lib/dtsc.cpp | 8 ++++---- lib/flv_tag.cpp | 8 ++++---- lib/json.cpp | 23 +++++++++++++++++++++++ lib/json.h | 2 ++ lib/socket.cpp | 2 +- lib/socket.h | 2 +- 7 files changed, 38 insertions(+), 10 deletions(-) diff --git a/lib/config.cpp b/lib/config.cpp index e7710e7c..b5156308 100644 --- a/lib/config.cpp +++ b/lib/config.cpp @@ -348,6 +348,7 @@ void Util::Config::addConnectorOptions(int port, JSON::Value & capabilities){ 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(); @@ -359,6 +360,7 @@ void Util::Config::addConnectorOptions(int port, JSON::Value & capabilities){ 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); @@ -376,6 +378,7 @@ void Util::Config::addBasicConnectorOptions(JSON::Value & capabilities){ 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"; option.null(); diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index a7a83c88..f5b0a9fa 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -222,10 +222,10 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ datapointertype = INVALID; std::string tmp = ""; if (newPack.isMember("trackid")){ - tmp = getTrackById(newPack["trackid"].asInt())["type"].asString(); + tmp = getTrackById(newPack["trackid"].asInt())["type"].asStringRef(); } if (newPack.isMember("datatype")){ - tmp = newPack["datatype"].asString(); + tmp = newPack["datatype"].asStringRef(); } if (tmp == "video"){ datapointertype = VIDEO; @@ -300,7 +300,7 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ } } if (keySize){ - metadata["tracks"][newTrack]["keys"][keySize - 1]["parts"].append((long long int)newPack["data"].asString().size()); + metadata["tracks"][newTrack]["keys"][keySize - 1]["parts"].append((long long int)newPack["data"].asStringRef().size()); } metadata["live"] = 1ll; } @@ -468,7 +468,7 @@ DTSC::livePos DTSC::Stream::msSeek(unsigned int ms, std::set & allowedTrack std::set seekTracks = allowedTracks; livePos result = buffers.begin()->first; for (std::set::iterator it = allowedTracks.begin(); it != allowedTracks.end(); it++){ - if (getTrackById(*it).isMember("type") && getTrackById(*it)["type"].asString() == "video"){ + if (getTrackById(*it).isMember("type") && getTrackById(*it)["type"].asStringRef() == "video"){ int trackNo = *it; seekTracks.clear(); seekTracks.insert(trackNo); diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index c712881d..6f296941 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -355,7 +355,7 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ case DTSC::VIDEO: len = S.lastData().length() + 16; if (track && track.isMember("codec")){ - if (track["codec"].asString() == "H264"){ + if (track["codec"].asStringRef() == "H264"){ len += 4; } } @@ -363,7 +363,7 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ case DTSC::AUDIO: len = S.lastData().length() + 16; if (track && track.isMember("codec")){ - if (track["codec"].asString() == "AAC"){ + if (track["codec"].asStringRef() == "AAC"){ len += 1; } } @@ -407,10 +407,10 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ data[15] = offset & 0xFF; } data[11] = 0; - if (track.isMember("codec") && track["codec"].asString() == "H264"){ + if (track.isMember("codec") && track["codec"].asStringRef() == "H264"){ data[11] += 7; } - if (track.isMember("codec") && track["codec"].asString() == "H263"){ + if (track.isMember("codec") && track["codec"].asStringRef() == "H263"){ data[11] += 2; } if (S.getPacket().isMember("keyframe")){ diff --git a/lib/json.cpp b/lib/json.cpp index 84b76de1..866ed2fe 100644 --- a/lib/json.cpp +++ b/lib/json.cpp @@ -380,6 +380,29 @@ const bool JSON::Value::asBool(){ return (bool) *this; } +/// Explicit conversion to std::string reference. +/// Returns a direct reference for string type JSON::Value objects, +/// but a reference to a static empty string otherwise. +/// \warning Only save to use when the JSON::Value is a string type! +const std::string & JSON::Value::asStringRef(){ + static std::string ugly_buffer; + if (myType == STRING){ + return strVal; + } + return ugly_buffer; +} + +/// Explicit conversion to c-string. +/// Returns a direct reference for string type JSON::Value objects, +/// a reference to an empty string otherwise. +/// \warning Only save to use when the JSON::Value is a string type! +const char * JSON::Value::c_str(){ + if (myType == STRING){ + return strVal.c_str(); + } + return ""; +} + /// Retrieves or sets the JSON::Value at this position in the object. /// Converts destructively to object if not already an object. JSON::Value & JSON::Value::operator[](const std::string i){ diff --git a/lib/json.h b/lib/json.h index 65aca525..c3ad40eb 100644 --- a/lib/json.h +++ b/lib/json.h @@ -64,6 +64,8 @@ namespace JSON { const std::string asString(); const long long int asInt(); const bool asBool(); + const std::string & asStringRef(); + const char * c_str(); //array operator for maps and arrays Value & operator[](const std::string i); Value & operator[](const char * i); diff --git a/lib/socket.cpp b/lib/socket.cpp index 703aed67..93d53aa6 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -477,7 +477,7 @@ void Socket::Connection::Send(const char * data){ /// Will not buffer anything but always send right away. Blocks. /// This will send the upbuffer (if non-empty) first, then the data. /// Any data that could not be send will block until it can be send or the connection is severed. -void Socket::Connection::SendNow(std::string & data){ +void Socket::Connection::SendNow(const std::string & data){ SendNow(data.c_str(), data.size()); } diff --git a/lib/socket.h b/lib/socket.h index 227cf018..afdf37ff 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -81,7 +81,7 @@ namespace Socket { void Send(std::string & data); ///< Appends data to the upbuffer. void Send(const char * data); ///< Appends data to the upbuffer. void Send(const char * data, size_t len); ///< Appends data to the upbuffer. - void SendNow(std::string & data); ///< Will not buffer anything but always send right away. Blocks. + 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 From b907ade842310cd5a8c15c3aaddf219f2326398c Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 12 Aug 2013 16:24:10 +0200 Subject: [PATCH 459/788] Removed some verbosity from procs library. --- lib/procs.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/procs.cpp b/lib/procs.cpp index f7942272..4be1230b 100644 --- a/lib/procs.cpp +++ b/lib/procs.cpp @@ -112,7 +112,7 @@ void Util::Procs::childsig_handler(int signum){ std::string pname = plist[ret]; #endif plist.erase(ret); -#if DEBUG >= 1 +#if DEBUG >= 5 if (isActive(pname)){ Stop(pname); } else{ @@ -124,7 +124,7 @@ void Util::Procs::childsig_handler(int signum){ if (exitHandlers.count(ret) > 0){ TerminationNotifier tn = exitHandlers[ret]; exitHandlers.erase(ret); -#if DEBUG >= 2 +#if DEBUG >= 5 std::cerr << "Calling termination handler for " << pname << std::endl; #endif tn(ret, exitcode); @@ -209,7 +209,7 @@ pid_t Util::Procs::Start(std::string name, std::string cmd){ runCmd(cmd); }else{ if (ret > 0){ -#if DEBUG >= 1 +#if DEBUG >= 5 std::cerr << "Process " << name << " started, PID " << ret << ": " << cmd << std::endl; #endif plist.insert(std::pair(ret, name)); @@ -273,7 +273,7 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2){ runCmd(cmd2); }else{ if (ret2 > 0){ -#if DEBUG >= 1 +#if DEBUG >= 5 std::cerr << "Process " << name << " started, PIDs (" << ret << ", " << ret2 << "): " << cmd << " | " << cmd2 << std::endl; #endif plist.insert(std::pair(ret2, name)); @@ -356,7 +356,7 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2, st runCmd(cmd2); }else{ if (ret2 > 0){ -#if DEBUG >= 1 +#if DEBUG >= 5 std::cerr << "Process " << name << " started, PIDs (" << ret << ", " << ret2 << "): " << cmd << " | " << cmd2 << std::endl; #endif plist.insert(std::pair(ret2, name)); @@ -387,7 +387,7 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2, st runCmd(cmd3); }else{ if (ret3 > 0){ -#if DEBUG >= 1 +#if DEBUG >= 5 std::cerr << "Process " << name << " started, PIDs (" << ret << ", " << ret2 << ", " << ret3 << "): " << cmd << " | " << cmd2 << " | " << cmd3 << std::endl; #endif plist.insert(std::pair(ret3, name)); @@ -416,6 +416,9 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2, st /// \arg fdout Same as fdin, but for stderr. pid_t Util::Procs::StartPiped(std::string name, char * argv[], int * fdin, int * fdout, int * fderr){ if (isActive(name)){ + #if DEBUG >= 1 + std::cerr << name << " already active - skipping start" << std::endl; + #endif return getPid(name); } pid_t pid; @@ -534,7 +537,7 @@ pid_t Util::Procs::StartPiped(std::string name, char * argv[], int * fdin, int * } return 0; }else{ //parent -#if DEBUG >= 1 +#if DEBUG >= 5 std::cerr << "Piped process " << name << " started"; if (fdin ) std::cerr << " in=" << (*fdin == -1 ? pipein [1] : *fdin ); if (fdout) std::cerr << " out=" << (*fdout == -1 ? pipeout[0] : *fdout); From fe47c9597fde0b3ec6227b600f4b5288bc46a6ff Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Tue, 23 Jul 2013 15:54:11 +0200 Subject: [PATCH 460/788] Fixed a bug with the endpos of multiple files. --- lib/dtsc.cpp | 14 +++++++++----- lib/dtsc.h | 1 + 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index f5b0a9fa..bc7ea9c6 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -507,6 +507,7 @@ DTSC::Stream::~Stream(){ DTSC::File::File(){ F = 0; + endPos = 0; } DTSC::File::File(const File & rhs){ @@ -522,6 +523,7 @@ DTSC::File & DTSC::File::operator =(const File & rhs){ }else{ F = 0; } + endPos = rhs.endPos; strbuffer = rhs.strbuffer; jsonbuffer = rhs.jsonbuffer; metadata = rhs.metadata; @@ -552,6 +554,8 @@ DTSC::File::File(std::string filename, bool create){ fprintf(stderr, "Could not open file %s\n", filename.c_str()); return; } + fseek(F, 0, SEEK_END); + endPos = ftell(F); //we now know the first 4 bytes are DTSC::Magic_Header and we have a valid file fseek(F, 4, SEEK_SET); @@ -622,6 +626,8 @@ long long int DTSC::File::addHeader(std::string & header){ if (ret != 1){ return 0; } + fseek(F, 0, SEEK_END); + endPos = ftell(F); return writePos; //return position written at } @@ -681,11 +687,6 @@ void DTSC::File::readHeader(int pos){ } long int DTSC::File::getBytePosEOF(){ - static long int endPos = 0; - if ( !endPos){ - fseek(F, 0, SEEK_END); - endPos = ftell(F); - } return endPos; } @@ -923,6 +924,7 @@ bool DTSC::File::seek_time(int ms, int trackNo){ } } currentPositions.insert(tmpPos); + fprintf(stderr,"Seek_time to %d on track %d, time %d on track %d found\n", ms, trackNo, tmpPos.seekTime,tmpPos.trackID); } /// Attempts to seek to the given time in ms within the file. @@ -947,6 +949,8 @@ bool DTSC::File::seek_bpos(int bpos){ void DTSC::File::writePacket(std::string & newPacket){ fseek(F, 0, SEEK_END); fwrite(newPacket.c_str(), newPacket.size(), 1, F); //write contents + fseek(F, 0, SEEK_END); + endPos = ftell(F); } void DTSC::File::writePacket(JSON::Value & newPacket){ diff --git a/lib/dtsc.h b/lib/dtsc.h index 3a1e9f18..b61f9801 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -117,6 +117,7 @@ namespace DTSC { bool atKeyframe(); void selectTracks(std::set & tracks); private: + long int endPos; void readHeader(int pos); std::string strbuffer; JSON::Value jsonbuffer; From ed7de501389525340890edcf26c3aa206fe895c1 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 13 Aug 2013 10:31:58 +0200 Subject: [PATCH 461/788] Fixed JSON library string escape issues. --- lib/json.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/json.cpp b/lib/json.cpp index 866ed2fe..59ec0a25 100644 --- a/lib/json.cpp +++ b/lib/json.cpp @@ -52,6 +52,7 @@ std::string JSON::Value::read_string(int separator, std::istream & fromstream){ out += (char)c; break; } + escaped = false; }else{ if (c == separator){ return out; @@ -613,7 +614,7 @@ std::string JSON::Value::toString(){ ObjIter it3 = ObjEnd(); --it3; for (ObjIter it2 = ObjBegin(); it2 != ObjEnd(); it2++){ - tmp2 += "\"" + it2->first + "\":"; + tmp2 += string_escape(it2->first)+":"; tmp2 += it2->second.toString(); if (it2 != it3){ tmp2 += ","; @@ -676,7 +677,7 @@ std::string JSON::Value::toPrettyString(int indentation){ ObjIter it3 = ObjEnd(); --it3; for (ObjIter it2 = ObjBegin(); it2 != ObjEnd(); it2++){ - tmp2 += (shortMode ? std::string("") : std::string(indentation + 2, ' ')) + "\"" + it2->first + "\":"; + tmp2 += (shortMode ? std::string("") : std::string(indentation + 2, ' ')) + string_escape(it2->first) + ":"; tmp2 += it2->second.toPrettyString(indentation + 2); if (it2 != it3){ tmp2 += "," + std::string((shortMode ? " " : "\n")); @@ -859,8 +860,8 @@ JSON::Value JSON::fromDTMI(const unsigned char * data, unsigned int len, unsigne i += 9; //skip 8(an uint64_t)+1 forwards uint64_t * d = (uint64_t*)tmpdbl; return JSON::Value((long long int) *d); - } break; + } case 0x02: { //string if (i+4 >= len){ return JSON::Value(); @@ -872,8 +873,8 @@ JSON::Value JSON::fromDTMI(const unsigned char * data, unsigned int len, unsigne } i += tmpi + 5; //skip length+size+1 forwards return JSON::Value(tmpstr); - } break; + } case 0xFF: //also object case 0xE0: { //object ++i; @@ -889,8 +890,8 @@ JSON::Value JSON::fromDTMI(const unsigned char * data, unsigned int len, unsigne } i += 3; //skip 0x0000EE return ret; - } break; + } case 0x0A: { //array JSON::Value ret; ++i; @@ -899,8 +900,8 @@ JSON::Value JSON::fromDTMI(const unsigned char * data, unsigned int len, unsigne } i += 3; //skip 0x0000EE return ret; - } break; + } } #if DEBUG >= 2 fprintf(stderr, "Error: Unimplemented DTMI type %hhx, @ %i / %i - returning.\n", data[i], i, len); From 37df1716de3727f6ce28a52b5452812f73040e80 Mon Sep 17 00:00:00 2001 From: Oswald Auguste de Bruin Date: Mon, 15 Jul 2013 16:24:43 +0200 Subject: [PATCH 462/788] Added vorbis functionality --- lib/Makefile.am | 4 ++-- lib/vorbis.cpp | 34 ++++++++++++++++++++++++++++++++++ lib/vorbis.h | 16 ++++++++++++++++ 3 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 lib/vorbis.cpp create mode 100644 lib/vorbis.h diff --git a/lib/Makefile.am b/lib/Makefile.am index 41c0c207..22c1f7cd 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,5 +1,5 @@ lib_LTLIBRARIES=libmist-1.0.la -libmist_1_0_la_SOURCES=amf.h amf.cpp auth.h auth.cpp base64.h base64.cpp config.h config.cpp dtsc.h dtsc.cpp flv_tag.h flv_tag.cpp http_parser.h http_parser.cpp json.h json.cpp procs.h procs.cpp rtmpchunks.h rtmpchunks.cpp socket.h socket.cpp mp4.h mp4.cpp ftp.h ftp.cpp filesystem.h filesystem.cpp stream.h stream.cpp timing.h timing.cpp ts_packet.cpp ts_packet.h converter.cpp converter.h ogg.h ogg.cpp theora.cpp theora.h +libmist_1_0_la_SOURCES=amf.h amf.cpp auth.h auth.cpp base64.h base64.cpp config.h config.cpp dtsc.h dtsc.cpp flv_tag.h flv_tag.cpp http_parser.h http_parser.cpp json.h json.cpp procs.h procs.cpp rtmpchunks.h rtmpchunks.cpp socket.h socket.cpp mp4.h mp4.cpp ftp.h ftp.cpp filesystem.h filesystem.cpp stream.h stream.cpp timing.h timing.cpp ts_packet.cpp ts_packet.h converter.cpp converter.h ogg.h ogg.cpp theora.cpp theora.h vorbis.cpp vorbis.h libmist_1_0_la_LDFLAGS = -version-info 5:1:2 libmist_1_0_la_CPPFLAGS=$(DEPS_CFLAGS) $(global_CFLAGS) libmist_1_0_la_LIBADD=$(DEPS_LIBS) $(CLOCK_LIB) @@ -8,4 +8,4 @@ pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = mist-1.0.pc library_includedir=$(includedir)/mist-1.0/mist -library_include_HEADERS = amf.h auth.h base64.h config.h dtsc.h flv_tag.h http_parser.h json.h procs.h rtmpchunks.h socket.h mp4.h ftp.h filesystem.h stream.h timing.h nal.h ts_packet.h converter.h ogg.h theora.h +library_include_HEADERS = amf.h auth.h base64.h config.h dtsc.h flv_tag.h http_parser.h json.h procs.h rtmpchunks.h socket.h mp4.h ftp.h filesystem.h stream.h timing.h nal.h ts_packet.h converter.h ogg.h theora.h vorbis.h diff --git a/lib/vorbis.cpp b/lib/vorbis.cpp new file mode 100644 index 00000000..9b58fb77 --- /dev/null +++ b/lib/vorbis.cpp @@ -0,0 +1,34 @@ +#include"vorbis.h" +#include +#include +#include + +namespace vorbis{ + header::header(){ + data = NULL; + datasize = 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::read(char* newData, unsigned int length){ + if (checkDataSize(length)){ + memcpy(data, newData, length); + }else{ + return false; + } + return true; + } +} diff --git a/lib/vorbis.h b/lib/vorbis.h new file mode 100644 index 00000000..194bc812 --- /dev/null +++ b/lib/vorbis.h @@ -0,0 +1,16 @@ +#pragma once +#include +#include +#include + +namespace vorbis{ + class header{ + public: + header(); + bool read(char* newData, unsigned int length); + private: + char* data; + unsigned int datasize; + bool checkDataSize(unsigned int size); + }; +} From 9ed5a5e28c08039a08f16a9becb3e0f5991aca74 Mon Sep 17 00:00:00 2001 From: Oswald Auguste de Bruin Date: Tue, 16 Jul 2013 16:23:33 +0200 Subject: [PATCH 463/788] Vorbis started on prettyprint --- lib/ogg.cpp | 11 ++++ lib/ogg.h | 1 + lib/vorbis.cpp | 143 ++++++++++++++++++++++++++++++++++++++++++++++++- lib/vorbis.h | 16 ++++++ 4 files changed, 170 insertions(+), 1 deletion(-) diff --git a/lib/ogg.cpp b/lib/ogg.cpp index debba28c..7ccdc474 100644 --- a/lib/ogg.cpp +++ b/lib/ogg.cpp @@ -299,6 +299,17 @@ namespace OGG{ } offset += len; } + }else if(codec == "vorbis"){ + r << "Vorbis Data" << std::endl; + int offset = 0; + for (int i = 0; i < getSegmentTableDeque().size(); i++){ + vorbis::header tmpHeader; + int len = getSegmentTableDeque()[i]; + if (tmpHeader.read(getFullPayload()+offset,len)){ + r << tmpHeader.toPrettyString(indent + 4); + } + offset += len; + } } return r.str(); } diff --git a/lib/ogg.h b/lib/ogg.h index 628487e2..c983e079 100644 --- a/lib/ogg.h +++ b/lib/ogg.h @@ -4,6 +4,7 @@ #include #include"dtsc.h" #include "theora.h" +#include "vorbis.h" namespace OGG{ class Page{ diff --git a/lib/vorbis.cpp b/lib/vorbis.cpp index 9b58fb77..9d95f6a8 100644 --- a/lib/vorbis.cpp +++ b/lib/vorbis.cpp @@ -1,13 +1,93 @@ #include"vorbis.h" #include #include +#include #include namespace vorbis{ + header::header(){ data = NULL; datasize = 0; } + + 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 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]>>4; + }else{ + return 0; + } + } + + char header::getBlockSize1(){ + if (getHeaderType() == 1){ + return data[28] & 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); @@ -23,12 +103,73 @@ namespace vorbis{ } } + bool header::validate(){ + switch(getHeaderType()){ + case 1://ID header + if (datasize!=29) 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 header::read(char* newData, unsigned int length){ + if (length < 7){ + return false; + } + /*if (! (newData[0] & 0x80)){ + return false; + }*/ + if(memcmp(newData+1, "vorbis", 6)!=0){ + return false; + } + if (checkDataSize(length)){ memcpy(data, newData, length); }else{ return false; } - return true; + return validate(); } + + + + 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 << "Testing vorbis Pretty string" << std::endl; + return r.str(); + } + } diff --git a/lib/vorbis.h b/lib/vorbis.h index 194bc812..3ef5c57a 100644 --- a/lib/vorbis.h +++ b/lib/vorbis.h @@ -8,9 +8,25 @@ namespace vorbis{ public: header(); bool read(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); + protected: + uint32_t getInt32(size_t index); + uint32_t getInt24(size_t index); + uint16_t getInt16(size_t index); private: char* data; unsigned int datasize; bool checkDataSize(unsigned int size); + bool validate(); }; } From c3d116d3dbdedeed79885e6fa4b0d282992cb6f5 Mon Sep 17 00:00:00 2001 From: Oswald Auguste de Bruin Date: Wed, 17 Jul 2013 11:13:35 +0200 Subject: [PATCH 464/788] Vorbis does right validation now --- lib/vorbis.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/vorbis.cpp b/lib/vorbis.cpp index 9d95f6a8..28df5637 100644 --- a/lib/vorbis.cpp +++ b/lib/vorbis.cpp @@ -106,12 +106,12 @@ namespace vorbis{ bool header::validate(){ switch(getHeaderType()){ case 1://ID header - if (datasize!=29) 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; + 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; From 5d85548360a7e03a378af389718dfde4a151d624 Mon Sep 17 00:00:00 2001 From: Oswald Auguste de Bruin Date: Wed, 17 Jul 2013 15:36:30 +0200 Subject: [PATCH 465/788] Added extra needed functions to ogg lib --- lib/ogg.cpp | 34 ++++++++++++++++++++++++++++++++-- lib/ogg.h | 13 ++++++++----- 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/lib/ogg.cpp b/lib/ogg.cpp index 7ccdc474..4a678c22 100644 --- a/lib/ogg.cpp +++ b/lib/ogg.cpp @@ -193,21 +193,35 @@ namespace OGG{ return segmentTableDeque; } + static void STerrMSG(){ + std::cerr << "Segments too big, create a continue page" << std::endl; + } + bool Page::setSegmentTable(std::vector layout){ unsigned int place = 0; char table[255]; for (unsigned int i = 0; i < layout.size(); i++){ while (layout[i]>=255){ - if (place >= 255) return false; + if (place >= 255){ + STerrMSG(); + return false; + } table[place] = 255; layout[i] -= 255; place++; } - if (place >= 255) return false; + if (place >= 255){ + STerrMSG(); + return false; + } table[place] = layout[i]; place++; } setSegmentTable(table,place); + dataSum=0; + for (unsigned int i = 0; i < layout.size(); i++){ + dataSum += layout[i]; + } return true; } @@ -418,4 +432,20 @@ namespace OGG{ int Page::getPayloadSize(){ return dataSum; } + + void Page::clear(){ + memset(data,0,27); + datasize = 0; + dataSum = 0; + codec = ""; + segmentTableDeque.clear(); + } + + bool Page::setPayload(char* newData, unsigned int length){ + if(!checkDataSize(27 + getPageSegments() + length)){//check if size available in memory + return false; + } + memcpy(data + 27 + getPageSegments(), newData, length); + return true; + } } diff --git a/lib/ogg.h b/lib/ogg.h index c983e079..59ab4947 100644 --- a/lib/ogg.h +++ b/lib/ogg.h @@ -40,13 +40,16 @@ namespace OGG{ bool typeNone(); std::string toPrettyString(size_t indent = 0); void setInternalCodec(std::string myCodec); + long unsigned int calcChecksum(); + void clear(); + + bool setPayload(char* newData, unsigned int length); private: std::deque segmentTableDeque; - long unsigned int calcChecksum(); - char* data; - unsigned int datasize; - unsigned int dataSum; + char* data;//pointer to the beginning of the Page data + unsigned int datasize;//size of the complete page + unsigned int dataSum;//size of the total segments bool checkDataSize(unsigned int size); - std::string codec; + std::string codec;//codec in the page }; } From 0bf50edaea9f2f4da47a10e77afd9c74caec3008 Mon Sep 17 00:00:00 2001 From: Oswald Auguste de Bruin Date: Thu, 18 Jul 2013 15:58:35 +0200 Subject: [PATCH 466/788] Ogg 2 DTSC working lib --- lib/ogg.cpp | 26 +++++++++++++++++++------- lib/ogg.h | 7 ++++--- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/lib/ogg.cpp b/lib/ogg.cpp index 4a678c22..4c9bbba8 100644 --- a/lib/ogg.cpp +++ b/lib/ogg.cpp @@ -198,6 +198,11 @@ namespace OGG{ } bool Page::setSegmentTable(std::vector layout){ + dataSum=0; + for (unsigned int i = 0; i < layout.size(); i++){ + dataSum += layout[i]; + } + std::cerr << "dataSum size: " << dataSum << std::endl; unsigned int place = 0; char table[255]; for (unsigned int i = 0; i < layout.size(); i++){ @@ -217,11 +222,8 @@ namespace OGG{ table[place] = layout[i]; place++; } + setPageSegments(place); setSegmentTable(table,place); - dataSum=0; - for (unsigned int i = 0; i < layout.size(); i++){ - dataSum += layout[i]; - } return true; } @@ -234,7 +236,11 @@ namespace OGG{ unsigned long int Page::getPageSize(){ return 27 + getPageSegments()+dataSum; } - + + char* Page::getPage(){ + return data; + } + char* Page::getFullPayload(){ return data + 27 + getPageSegments(); } @@ -433,18 +439,24 @@ namespace OGG{ return dataSum; } - void Page::clear(){ + bool Page::clear(){ + if(!checkDataSize(27)){//check if size available in memory + return false; + } memset(data,0,27); datasize = 0; dataSum = 0; codec = ""; + setMagicNumber(); segmentTableDeque.clear(); + return true; } bool Page::setPayload(char* newData, unsigned int length){ - if(!checkDataSize(27 + getPageSegments() + length)){//check if size available in memory + if(!checkDataSize(27 + getPageSegments() + length)){//check if size available in memory return false; } + std::cerr << "Payload length: " << length << std::endl; memcpy(data + 27 + getPageSegments(), newData, length); return true; } diff --git a/lib/ogg.h b/lib/ogg.h index 59ab4947..4987484e 100644 --- a/lib/ogg.h +++ b/lib/ogg.h @@ -31,8 +31,10 @@ namespace OGG{ std::deque & getSegmentTableDeque(); bool setSegmentTable(std::vector layout); void setSegmentTable(char* newVal, unsigned int length); + + char* getPage();//returns complete page with header unsigned long int getPageSize(); - char* getFullPayload(); + char* getFullPayload();//returns all segments in the page int getPayloadSize(); bool typeBOS(); bool typeEOS(); @@ -41,8 +43,7 @@ namespace OGG{ std::string toPrettyString(size_t indent = 0); void setInternalCodec(std::string myCodec); long unsigned int calcChecksum(); - void clear(); - + bool clear(); bool setPayload(char* newData, unsigned int length); private: std::deque segmentTableDeque; From 6c4d9d96aa03c20877506391a648b727f21708a3 Mon Sep 17 00:00:00 2001 From: Oswald Auguste de Bruin Date: Tue, 6 Aug 2013 13:35:19 +0200 Subject: [PATCH 467/788] Ogg Really working --- lib/ogg.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/ogg.cpp b/lib/ogg.cpp index 4c9bbba8..52a112e1 100644 --- a/lib/ogg.cpp +++ b/lib/ogg.cpp @@ -128,8 +128,7 @@ namespace OGG{ void Page::setGranulePosition(long long unsigned int newVal){ if(checkDataSize(14)){ - ((long unsigned*)(data+6))[1] = htonl(newVal & 0xFFFFFFFF); - ((long unsigned*)(data+6))[0] = htonl((newVal >> 32) & 0xFFFFFFFF); + set_64(data+6, newVal); } } @@ -202,7 +201,6 @@ namespace OGG{ for (unsigned int i = 0; i < layout.size(); i++){ dataSum += layout[i]; } - std::cerr << "dataSum size: " << dataSum << std::endl; unsigned int place = 0; char table[255]; for (unsigned int i = 0; i < layout.size(); i++){ @@ -456,7 +454,6 @@ namespace OGG{ if(!checkDataSize(27 + getPageSegments() + length)){//check if size available in memory return false; } - std::cerr << "Payload length: " << length << std::endl; memcpy(data + 27 + getPageSegments(), newData, length); return true; } From b85ceb9fc5132eddeafee83ec7ae12b22681bc8f Mon Sep 17 00:00:00 2001 From: Oswald Auguste de Bruin Date: Fri, 9 Aug 2013 15:29:38 +0200 Subject: [PATCH 468/788] DTSC2OGG page in lib now, not yet header making --- lib/ogg.cpp | 28 +++++++++++++++++++++++++++- lib/ogg.h | 3 ++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/lib/ogg.cpp b/lib/ogg.cpp index 52a112e1..8064b59f 100644 --- a/lib/ogg.cpp +++ b/lib/ogg.cpp @@ -456,5 +456,31 @@ namespace OGG{ } memcpy(data + 27 + getPageSegments(), newData, length); return true; - } + } + + void Page::readDTSCVector(std::vector DTSCVec, unsigned int serial, unsigned int sequence){ + clear(); + setVersion(); + if (DTSCVec[0]["OggCont"]){ + setHeaderType(1);//headertype 1 = Continue Page + }else if (DTSCVec[0]["OggEOS"]){ + setHeaderType(4);//headertype 4 = end of stream + }else{ + setHeaderType(0);//headertype 0 = normal + } + setGranulePosition(DTSCVec[0]["granule"].asInt()); + setBitstreamSerialNumber(serial); + setPageSequenceNumber(sequence); + + std::vector curSegTable; + std::string pageBuffer; + + for (unsigned int i = 0; i < DTSCVec.size(); i++){ + curSegTable.push_back(DTSCVec[i]["data"].asString().size()); + pageBuffer += DTSCVec[i]["data"].asString(); + } + setSegmentTable(curSegTable); + setPayload((char*)pageBuffer.c_str(), pageBuffer.size()); + setCRCChecksum(calcChecksum()); + } } diff --git a/lib/ogg.h b/lib/ogg.h index 4987484e..4797c6f4 100644 --- a/lib/ogg.h +++ b/lib/ogg.h @@ -5,6 +5,7 @@ #include"dtsc.h" #include "theora.h" #include "vorbis.h" +#include "json.h" namespace OGG{ class Page{ @@ -31,7 +32,6 @@ namespace OGG{ std::deque & getSegmentTableDeque(); bool setSegmentTable(std::vector layout); void setSegmentTable(char* newVal, unsigned int length); - char* getPage();//returns complete page with header unsigned long int getPageSize(); char* getFullPayload();//returns all segments in the page @@ -45,6 +45,7 @@ namespace OGG{ long unsigned int calcChecksum(); bool clear(); bool setPayload(char* newData, unsigned int length); + void readDTSCVector(std::vector DTSCVec, unsigned int serial, unsigned int sequence); private: std::deque segmentTableDeque; char* data;//pointer to the beginning of the Page data From f173ad0966687b15725a1adebd975bf9dcb00832 Mon Sep 17 00:00:00 2001 From: Oswald Auguste de Bruin Date: Mon, 12 Aug 2013 11:33:08 +0200 Subject: [PATCH 469/788] Ogg lib has new DTSC features --- lib/ogg.cpp | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ lib/ogg.h | 8 ++++++++ 2 files changed, 58 insertions(+) diff --git a/lib/ogg.cpp b/lib/ogg.cpp index 8064b59f..d15a7600 100644 --- a/lib/ogg.cpp +++ b/lib/ogg.cpp @@ -483,4 +483,54 @@ namespace OGG{ setPayload((char*)pageBuffer.c_str(), pageBuffer.size()); setCRCChecksum(calcChecksum()); } + + void headerPages::readDTSCHeader(JSON::Value meta){ + //pages.clear(); + parsedPages = ""; + Page curOggPage; + srand (Util::getMS());//randomising with milliseconds from boot + std::vector curSegTable; + DTSCID2OGGSerial.clear(); + DTSCID2seqNum.clear(); + //Creating ID headers for theora and vorbis + for ( JSON::ObjIter it = meta["tracks"].ObjBegin(); it != meta["tracks"].ObjEnd(); it ++) { + curOggPage.clear(); + curOggPage.setVersion(); + curOggPage.setHeaderType(2);//headertype 2 = Begin of Stream + curOggPage.setGranulePosition(0); + DTSCID2OGGSerial[it->second["trackid"].asInt()] = rand() % 0xFFFFFFFE +1; //initialising on a random not 0 number + curOggPage.setBitstreamSerialNumber(DTSCID2OGGSerial[it->second["trackid"].asInt()]); + DTSCID2seqNum[it->second["trackid"].asInt()] = 0; + curOggPage.setPageSequenceNumber(DTSCID2seqNum[it->second["trackid"].asInt()]++); + curSegTable.clear(); + curSegTable.push_back(it->second["IDHeader"].asString().size()); + curOggPage.setSegmentTable(curSegTable); + curOggPage.setPayload((char*)it->second["IDHeader"].asString().c_str(), it->second["IDHeader"].asString().size()); + curOggPage.setCRCChecksum(curOggPage.calcChecksum()); + //std::cout << std::string(curOggPage.getPage(), curOggPage.getPageSize()); + //pages.push_back(curOggPage); + parsedPages += std::string(curOggPage.getPage(), curOggPage.getPageSize()); + } + //Creating remaining headers for theora and vorbis + //for tracks in header + //create standard page with comment (empty) en setup header(init) + for ( JSON::ObjIter it = meta["tracks"].ObjBegin(); it != meta["tracks"].ObjEnd(); it ++) { + curOggPage.clear(); + curOggPage.setVersion(); + curOggPage.setHeaderType(0);//headertype 0 = normal + curOggPage.setGranulePosition(0); + curOggPage.setBitstreamSerialNumber(DTSCID2OGGSerial[it->second["trackid"].asInt()]); + curOggPage.setPageSequenceNumber(DTSCID2seqNum[it->second["trackid"].asInt()]++); + curSegTable.clear(); + curSegTable.push_back(it->second["CommentHeader"].asString().size()); + curSegTable.push_back(it->second["init"].asString().size()); + curOggPage.setSegmentTable(curSegTable); + std::string fullHeader = it->second["CommentHeader"].asString() + it->second["init"].asString(); + curOggPage.setPayload((char*)fullHeader.c_str(),fullHeader.size()); + curOggPage.setCRCChecksum(curOggPage.calcChecksum()); + //std::cout << std::string(curOggPage.getPage(), curOggPage.getPageSize()); + //pages.push_back(curOggPage); + parsedPages += std::string(curOggPage.getPage(), curOggPage.getPageSize()); + } + } } diff --git a/lib/ogg.h b/lib/ogg.h index 4797c6f4..b0abcf20 100644 --- a/lib/ogg.h +++ b/lib/ogg.h @@ -54,4 +54,12 @@ namespace OGG{ bool checkDataSize(unsigned int size); std::string codec;//codec in the page }; + + class headerPages{ + public: + void readDTSCHeader(JSON::Value meta); + std::map DTSCID2OGGSerial; + std::map DTSCID2seqNum; + std::string parsedPages; + }; } From 7ea9dd96545afb450995c909fe1cdced66c445bd Mon Sep 17 00:00:00 2001 From: Oswald Auguste de Bruin Date: Mon, 19 Aug 2013 08:45:42 +0200 Subject: [PATCH 470/788] fprintf slowdown removed from dtsc.cpp --- lib/dtsc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index bc7ea9c6..010a5eff 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -924,7 +924,7 @@ bool DTSC::File::seek_time(int ms, int trackNo){ } } currentPositions.insert(tmpPos); - fprintf(stderr,"Seek_time to %d on track %d, time %d on track %d found\n", ms, trackNo, tmpPos.seekTime,tmpPos.trackID); + //fprintf(stderr,"Seek_time to %d on track %d, time %d on track %d found\n", ms, trackNo, tmpPos.seekTime,tmpPos.trackID); } /// Attempts to seek to the given time in ms within the file. From f21ce291aeec8f096c2980626bffbb91a3bfba1a Mon Sep 17 00:00:00 2001 From: Thulinma Date: Wed, 21 Aug 2013 11:59:43 +0200 Subject: [PATCH 471/788] Added many helper functions to HTTP library, optimizing and fixing support for certain cases (among which chunked encoding). --- lib/http_parser.cpp | 210 +++++++++++++++++++++++++++++++++++++++++--- lib/http_parser.h | 7 ++ 2 files changed, 206 insertions(+), 11 deletions(-) diff --git a/lib/http_parser.cpp b/lib/http_parser.cpp index 460cb2b7..56259179 100644 --- a/lib/http_parser.cpp +++ b/lib/http_parser.cpp @@ -2,6 +2,7 @@ /// 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(). @@ -45,6 +46,27 @@ std::string & HTTP::Parser::BuildRequest(){ 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::iterator it; + if (protocol.size() < 5 || protocol.substr(0, 4) != "HTTP"){ + 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. @@ -70,6 +92,109 @@ std::string & HTTP::Parser::BuildResponse(std::string code, std::string message) return builder; } +/// 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. +/// \param code The HTTP response code. Usually you want 200. +/// \param message The HTTP response message. Usually you want "OK". +void HTTP::Parser::SendResponse(std::string code, std::string message, Socket::Connection & conn){ + /// \todo Include GET/POST variable parsing? + std::map::iterator it; + if (protocol.size() < 5 || protocol.substr(0, 4) != "HTTP"){ + 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); +} + +/// 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("200", "OK", to); + if (getChunks){ + int doingChunk = 0; + while (to.connected() && from.connected()){ + if (from.Received().size() || from.spool()){ + if (doingChunk){ + unsigned int toappend = from.Received().get().size(); + if (toappend > doingChunk){ + toappend = doingChunk; + to.SendNow(from.Received().get().c_str(), toappend); + from.Received().get().erase(0, toappend); + }else{ + to.SendNow(from.Received().get()); + from.Received().get().clear(); + } + doingChunk -= toappend; + }else{ + //Make sure the received data ends in a newline (\n). + if ( *(from.Received().get().rbegin()) != '\n'){ + if (from.Received().size() > 1){ + std::string tmp = from.Received().get(); + from.Received().get().clear(); + from.Received().get().insert(0, tmp); + }else{ + Util::sleep(100); + 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 (int i = 0; i < tmpA.size(); ++i){ + chunkLen = (chunkLen << 4) | unhex(tmpA[i]); + } + if (chunkLen == 0){ + getChunks = false; + to.SendNow("\r\n", 2); + return; + } + doingChunk = 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. @@ -142,15 +267,34 @@ void HTTP::Parser::SetVar(std::string i, std::string 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). + if ( *(conn.Received().get().rbegin()) != '\n'){ + if (conn.Received().size() > 1){ + std::string tmp = conn.Received().get(); + conn.Received().get().clear(); + conn.Received().get().insert(0, tmp); + }else{ + return false; + } + } + return parse(conn.Received().get()); +} //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. +/// 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 -#include /// 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. @@ -177,18 +321,34 @@ bool HTTP::Parser::parse(std::string & HTTPbuffer){ seenReq = true; f = tmpA.find(' '); if (f != std::string::npos){ - method = tmpA.substr(0, f); - tmpA.erase(0, f + 1); - f = tmpA.find(' '); - if (f != std::string::npos){ - url = tmpA.substr(0, f); + if (tmpA.substr(0, 4) == "HTTP"){ + protocol = 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 + 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 + } + }else{ + seenReq = false; } }else{ - seenReq = false; + 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 + } + }else{ + seenReq = false; + } } }else{ seenReq = false; @@ -234,6 +394,9 @@ bool HTTP::Parser::parse(std::string & HTTPbuffer){ } }else{ if (getChunks){ + if (headerOnly){ + return true; + } if (doingChunk){ unsigned int toappend = HTTPbuffer.size(); if (toappend > doingChunk){ @@ -321,6 +484,31 @@ void HTTP::Parser::Chunkify(std::string & bodypart){ } } +/// Converts a string to chunked format if protocol is HTTP/1.1 - does nothing otherwise. +/// \param bodypart The data to convert - will be converted in-place. +void HTTP::Parser::Chunkify(std::string & bodypart, Socket::Connection & conn){ + Chunkify(bodypart.c_str(), bodypart.size(), conn); +} + +/// Converts a string to chunked format if protocol is HTTP/1.1 - does nothing otherwise. +/// \param bodypart The data to convert - will be converted in-place. +void HTTP::Parser::Chunkify(const char * data, unsigned int size, Socket::Connection & conn){ + if (protocol == "HTTP/1.1"){ + static char len[10]; + int sizelen = snprintf(len, 10, "%x\r\n", size); + //prepend the chunk size and \r\n + conn.SendNow(len, sizelen); + //send the chunk itself + conn.SendNow(data, size); + //append \r\n + conn.SendNow("\r\n", 2); + if ( !size){ + //append \r\n again! + conn.SendNow("\r\n", 2); + } + } +} + /// Unescapes URLencoded std::string data. std::string HTTP::Parser::urlunescape(const std::string & in){ std::string out; diff --git a/lib/http_parser.h b/lib/http_parser.h index bf753190..bb6ff8a5 100644 --- a/lib/http_parser.h +++ b/lib/http_parser.h @@ -6,6 +6,7 @@ #include #include #include +#include "socket.h" /// Holds all HTTP processing related code. namespace HTTP { @@ -13,6 +14,7 @@ namespace HTTP { 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); @@ -24,7 +26,12 @@ namespace HTTP { void SetBody(char * buffer, int len); std::string & BuildRequest(); 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 Chunkify(std::string & bodypart); + void Chunkify(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(); static std::string urlunescape(const std::string & in); static std::string urlencode(const std::string & in); From 5b38492c8bc66777fbf3c65ef7e05e4597a950ee Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 22 Aug 2013 14:11:45 +0200 Subject: [PATCH 472/788] Fixed proxying chunks problems. --- lib/http_parser.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/http_parser.cpp b/lib/http_parser.cpp index 56259179..f086176c 100644 --- a/lib/http_parser.cpp +++ b/lib/http_parser.cpp @@ -124,20 +124,20 @@ void HTTP::Parser::SendResponse(std::string code, std::string message, Socket::C void HTTP::Parser::Proxy(Socket::Connection & from, Socket::Connection & to){ SendResponse("200", "OK", to); if (getChunks){ - int doingChunk = 0; + int proxyingChunk = 0; while (to.connected() && from.connected()){ - if (from.Received().size() || from.spool()){ - if (doingChunk){ + if ((from.Received().size() && (from.Received().size() > 2 || *(from.Received().get().rbegin()) == '\n')) || from.spool()){ + if (proxyingChunk){ unsigned int toappend = from.Received().get().size(); - if (toappend > doingChunk){ - toappend = doingChunk; + 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(); } - doingChunk -= toappend; + proxyingChunk -= toappend; }else{ //Make sure the received data ends in a newline (\n). if ( *(from.Received().get().rbegin()) != '\n'){ @@ -167,7 +167,7 @@ void HTTP::Parser::Proxy(Socket::Connection & from, Socket::Connection & to){ to.SendNow("\r\n", 2); return; } - doingChunk = chunkLen; + proxyingChunk = chunkLen; } from.Received().get().clear(); } From f0b36a0e36b04d15b69038a6e1f6f03862475fd5 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 22 Aug 2013 15:19:25 +0200 Subject: [PATCH 473/788] Fixed multithreading usage of HTTP chunked encoding. --- lib/http_parser.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/http_parser.cpp b/lib/http_parser.cpp index f086176c..784728d7 100644 --- a/lib/http_parser.cpp +++ b/lib/http_parser.cpp @@ -475,7 +475,7 @@ void HTTP::Parser::parseVars(std::string data){ /// \param bodypart The data to convert - will be converted in-place. void HTTP::Parser::Chunkify(std::string & bodypart){ if (protocol == "HTTP/1.1"){ - static char len[10]; + char len[10]; int sizelen = snprintf(len, 10, "%x\r\n", (unsigned int)bodypart.size()); //prepend the chunk size and \r\n bodypart.insert(0, len, sizelen); @@ -494,7 +494,7 @@ void HTTP::Parser::Chunkify(std::string & bodypart, Socket::Connection & conn){ /// \param bodypart The data to convert - will be converted in-place. void HTTP::Parser::Chunkify(const char * data, unsigned int size, Socket::Connection & conn){ if (protocol == "HTTP/1.1"){ - static char len[10]; + char len[10]; int sizelen = snprintf(len, 10, "%x\r\n", size); //prepend the chunk size and \r\n conn.SendNow(len, sizelen); From baeacc6d9ec754adb715d0c299bd8d3403d706e6 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 22 Aug 2013 22:31:31 +0200 Subject: [PATCH 474/788] More fixes for chunked encoding. --- lib/http_parser.cpp | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/lib/http_parser.cpp b/lib/http_parser.cpp index 784728d7..268b01c3 100644 --- a/lib/http_parser.cpp +++ b/lib/http_parser.cpp @@ -124,29 +124,37 @@ void HTTP::Parser::SendResponse(std::string code, std::string message, Socket::C void HTTP::Parser::Proxy(Socket::Connection & from, Socket::Connection & to){ SendResponse("200", "OK", to); if (getChunks){ - int proxyingChunk = 0; + unsigned int proxyingChunk = 0; while (to.connected() && from.connected()){ - if ((from.Received().size() && (from.Received().size() > 2 || *(from.Received().get().rbegin()) == '\n')) || from.spool()){ + if ((from.Received().size() && (from.Received().size() > 1 || *(from.Received().get().rbegin()) == '\n')) || from.spool()){ if (proxyingChunk){ - 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(); + 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; } - 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; } } @@ -274,10 +282,14 @@ void HTTP::Parser::SetVar(std::string i, std::string v){ /// \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). - if ( *(conn.Received().get().rbegin()) != '\n'){ + while ( *(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; From ca07e477fe9a01df4666e2669b2fb6d96f434eff Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 23 Aug 2013 00:04:38 +0200 Subject: [PATCH 475/788] HTTP library usability improvements. --- lib/http_parser.cpp | 62 ++++++++++++++++++++++++++++++++------------- lib/http_parser.h | 3 ++- 2 files changed, 46 insertions(+), 19 deletions(-) diff --git a/lib/http_parser.cpp b/lib/http_parser.cpp index 268b01c3..35747a89 100644 --- a/lib/http_parser.cpp +++ b/lib/http_parser.cpp @@ -114,6 +114,35 @@ void HTTP::Parser::SendResponse(std::string code, std::string message, Socket::C } } 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){ + protocol = request.protocol; + body = ""; + if (protocol == "HTTP/1.1"){ + SetHeader("Transfer-Encoding", "chunked"); + }else{ + SetBody(""); + } + 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){ + StartResponse("200", "OK", request, conn); } /// After receiving a header with this object, this function call will: @@ -483,27 +512,17 @@ void HTTP::Parser::parseVars(std::string data){ } } -/// Converts a string to chunked format if protocol is HTTP/1.1 - does nothing otherwise. -/// \param bodypart The data to convert - will be converted in-place. -void HTTP::Parser::Chunkify(std::string & bodypart){ - if (protocol == "HTTP/1.1"){ - char len[10]; - int sizelen = snprintf(len, 10, "%x\r\n", (unsigned int)bodypart.size()); - //prepend the chunk size and \r\n - bodypart.insert(0, len, sizelen); - //append \r\n - bodypart.append("\r\n", 2); - } -} - -/// Converts a string to chunked format if protocol is HTTP/1.1 - does nothing otherwise. -/// \param bodypart The data to convert - will be converted in-place. +/// 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(std::string & bodypart, Socket::Connection & conn){ Chunkify(bodypart.c_str(), bodypart.size(), conn); } -/// Converts a string to chunked format if protocol is HTTP/1.1 - does nothing otherwise. -/// \param bodypart The data to convert - will be converted in-place. +/// 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){ if (protocol == "HTTP/1.1"){ char len[10]; @@ -515,9 +534,16 @@ void HTTP::Parser::Chunkify(const char * data, unsigned int size, Socket::Connec //append \r\n conn.SendNow("\r\n", 2); if ( !size){ - //append \r\n again! + //append \r\n again if this was the end of the file (required by chunked transfer encoding according to spec) 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(); + } } } diff --git a/lib/http_parser.h b/lib/http_parser.h index bb6ff8a5..35fdfee8 100644 --- a/lib/http_parser.h +++ b/lib/http_parser.h @@ -28,7 +28,8 @@ namespace HTTP { 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 Chunkify(std::string & bodypart); + void StartResponse(std::string code, std::string message, Parser & request, Socket::Connection & conn); + void StartResponse(Parser & request, Socket::Connection & conn); void Chunkify(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); From 16343ed016d782896bedaed00b641ea626888692 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 23 Aug 2013 10:36:25 +0200 Subject: [PATCH 476/788] Added waitForMeta function to DTSC::Stream object. --- lib/dtsc.cpp | 23 +++++++++++++++++++++++ lib/dtsc.h | 1 + 2 files changed, 24 insertions(+) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 010a5eff..b3c7d195 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -189,6 +189,29 @@ void DTSC::Stream::endStream(){ } } +/// Blocks until either the stream has metadata available or the sourceSocket errors. +/// This function is intended to be run before any commands are sent and thus will not throw away anything important. +void DTSC::Stream::waitForMeta(Socket::Connection & sourceSocket){ + while ( !metadata && sourceSocket.connected()){ + //we have data? attempt to read header + if (sourceSocket.Received().size()){ + //return value is ignore because we're not interested in data packets, just metadata. + parsePacket(sourceSocket.Received()); + } + //still no header? check for more data + if ( !metadata){ + if (sourceSocket.spool()){ + //more received? attempt to read + //return value is ignore because we're not interested in data packets, just metadata. + parsePacket(sourceSocket.Received()); + }else{ + //nothing extra to receive? wait a bit and retry + Util::sleep(5); + } + } + } +} + void DTSC::Stream::addPacket(JSON::Value & newPack){ bool updateMeta = false; long long unsigned int now = Util::getMS(); diff --git a/lib/dtsc.h b/lib/dtsc.h index b61f9801..81675c85 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -215,6 +215,7 @@ namespace DTSC { bool isNewest(DTSC::livePos & pos); DTSC::livePos getNext(DTSC::livePos & pos, std::set & allowedTracks); void endStream(); + void waitForMeta(Socket::Connection & sourceSocket); private: std::map buffers; std::map > keyframes; From 07a4b61142dc91be8b79e46edc270c817bbecc1f Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 25 Aug 2013 02:04:58 +0200 Subject: [PATCH 477/788] Make (most) constant JSON::Value member functions available to constant objects, add constant equivalents where applicable. --- lib/json.cpp | 74 +++++++++++++++++++++++++++++++++++++++------------- lib/json.h | 35 ++++++++++++++++--------- 2 files changed, 78 insertions(+), 31 deletions(-) diff --git a/lib/json.cpp b/lib/json.cpp index 59ec0a25..c9d0545f 100644 --- a/lib/json.cpp +++ b/lib/json.cpp @@ -64,7 +64,7 @@ std::string JSON::Value::read_string(int separator, std::istream & fromstream){ return out; } -std::string JSON::Value::string_escape(std::string val){ +std::string JSON::Value::string_escape(const std::string val){ std::string out = "\""; for (unsigned int i = 0; i < val.size(); ++i){ switch (val[i]){ @@ -320,7 +320,7 @@ JSON::Value & JSON::Value::operator=(const unsigned int &rhs){ } /// Automatic conversion to long long int - returns 0 if not convertable. -JSON::Value::operator long long int(){ +JSON::Value::operator long long int() const{ if (myType == INTEGER){ return intVal; } @@ -332,7 +332,7 @@ JSON::Value::operator long long int(){ /// Automatic conversion to std::string. /// Returns the raw string value if available, otherwise calls toString(). -JSON::Value::operator std::string(){ +JSON::Value::operator std::string() const{ if (myType == STRING){ return strVal; }else{ @@ -346,7 +346,7 @@ JSON::Value::operator std::string(){ /// Automatic conversion to bool. /// Returns true if there is anything meaningful stored into this value. -JSON::Value::operator bool(){ +JSON::Value::operator bool() const{ if (myType == STRING){ return strVal != ""; } @@ -369,15 +369,15 @@ JSON::Value::operator bool(){ } /// Explicit conversion to std::string. -const std::string JSON::Value::asString(){ +const std::string JSON::Value::asString() const{ return (std::string) *this; } /// Explicit conversion to long long int. -const long long int JSON::Value::asInt(){ +const long long int JSON::Value::asInt() const{ return (long long int) *this; } /// Explicit conversion to bool. -const bool JSON::Value::asBool(){ +const bool JSON::Value::asBool() const{ return (bool) *this; } @@ -385,7 +385,7 @@ const bool JSON::Value::asBool(){ /// Returns a direct reference for string type JSON::Value objects, /// but a reference to a static empty string otherwise. /// \warning Only save to use when the JSON::Value is a string type! -const std::string & JSON::Value::asStringRef(){ +const std::string & JSON::Value::asStringRef() const{ static std::string ugly_buffer; if (myType == STRING){ return strVal; @@ -397,7 +397,7 @@ const std::string & JSON::Value::asStringRef(){ /// Returns a direct reference for string type JSON::Value objects, /// a reference to an empty string otherwise. /// \warning Only save to use when the JSON::Value is a string type! -const char * JSON::Value::c_str(){ +const char * JSON::Value::c_str() const{ if (myType == STRING){ return strVal.c_str(); } @@ -437,6 +437,24 @@ JSON::Value & JSON::Value::operator[](unsigned int i){ return arrVal[i]; } +/// Retrieves the JSON::Value at this position in the object. +/// Fails horribly if that values does not exist. +JSON::Value const & JSON::Value::operator[](const std::string i) const{ + return objVal.at(i); +} + +/// Retrieves the JSON::Value at this position in the object. +/// Fails horribly if that values does not exist. +JSON::Value const & JSON::Value::operator[](const char * i) const{ + return objVal.at(i); +} + +/// Retrieves the JSON::Value at this position in the array. +/// Fails horribly if that values does not exist. +JSON::Value const & JSON::Value::operator[](unsigned int i) const{ + return arrVal[i]; +} + /// Packs to a std::string for transfer over the network. /// If the object is a container type, this function will call itself recursively and contain all contents. /// As a side effect, this function clear the internal buffer of any object-types. @@ -582,7 +600,7 @@ std::string & JSON::Value::toNetPacked(){ /// Converts this JSON::Value to valid JSON notation and returns it. /// Makes absolutely no attempts to pretty-print anything. :-) -std::string JSON::Value::toString(){ +std::string JSON::Value::toString() const{ switch (myType){ case INTEGER: { std::stringstream st; @@ -597,7 +615,7 @@ std::string JSON::Value::toString(){ case ARRAY: { std::string tmp = "["; if (arrVal.size() > 0){ - for (ArrIter it = ArrBegin(); it != ArrEnd(); it++){ + for (ArrConstIter it = ArrBegin(); it != ArrEnd(); it++){ tmp += it->toString(); if (it + 1 != ArrEnd()){ tmp += ","; @@ -611,9 +629,9 @@ std::string JSON::Value::toString(){ case OBJECT: { std::string tmp2 = "{"; if (objVal.size() > 0){ - ObjIter it3 = ObjEnd(); + ObjConstIter it3 = ObjEnd(); --it3; - for (ObjIter it2 = ObjBegin(); it2 != ObjEnd(); it2++){ + for (ObjConstIter it2 = ObjBegin(); it2 != ObjEnd(); it2++){ tmp2 += string_escape(it2->first)+":"; tmp2 += it2->second.toString(); if (it2 != it3){ @@ -634,7 +652,7 @@ std::string JSON::Value::toString(){ /// Converts this JSON::Value to valid JSON notation and returns it. /// Makes an attempt at pretty-printing. -std::string JSON::Value::toPrettyString(int indentation){ +std::string JSON::Value::toPrettyString(int indentation) const{ switch (myType){ case INTEGER: { std::stringstream st; @@ -654,7 +672,7 @@ std::string JSON::Value::toPrettyString(int indentation){ case ARRAY: { if (arrVal.size() > 0){ std::string tmp = "[\n" + std::string(indentation + 2, ' '); - for (ArrIter it = ArrBegin(); it != ArrEnd(); it++){ + for (ArrConstIter it = ArrBegin(); it != ArrEnd(); it++){ tmp += it->toPrettyString(indentation + 2); if (it + 1 != ArrEnd()){ tmp += ", "; @@ -674,9 +692,9 @@ std::string JSON::Value::toPrettyString(int indentation){ shortMode = true; } std::string tmp2 = "{" + std::string((shortMode ? "" : "\n")); - ObjIter it3 = ObjEnd(); + ObjConstIter it3 = ObjEnd(); --it3; - for (ObjIter it2 = ObjBegin(); it2 != ObjEnd(); it2++){ + for (ObjConstIter it2 = ObjBegin(); it2 != ObjEnd(); it2++){ tmp2 += (shortMode ? std::string("") : std::string(indentation + 2, ' ')) + string_escape(it2->first) + ":"; tmp2 += it2->second.toPrettyString(indentation + 2); if (it2 != it3){ @@ -800,8 +818,28 @@ JSON::ArrIter JSON::Value::ArrEnd(){ return arrVal.end(); } +/// Returns an iterator to the begin of the object map, if any. +JSON::ObjConstIter JSON::Value::ObjBegin() const{ + return objVal.begin(); +} + +/// Returns an iterator to the end of the object map, if any. +JSON::ObjConstIter JSON::Value::ObjEnd() const{ + return objVal.end(); +} + +/// Returns an iterator to the begin of the array, if any. +JSON::ArrConstIter JSON::Value::ArrBegin() const{ + return arrVal.begin(); +} + +/// Returns an iterator to the end of the array, if any. +JSON::ArrConstIter JSON::Value::ArrEnd() const{ + return arrVal.end(); +} + /// Returns the total of the objects and array size combined. -unsigned int JSON::Value::size(){ +unsigned int JSON::Value::size() const{ return objVal.size() + arrVal.size(); } diff --git a/lib/json.h b/lib/json.h index c3ad40eb..8dbea25f 100644 --- a/lib/json.h +++ b/lib/json.h @@ -24,7 +24,9 @@ namespace JSON { typedef std::map::iterator ObjIter; typedef std::deque::iterator ArrIter; - + typedef std::map::const_iterator ObjConstIter; + typedef std::deque::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: @@ -34,7 +36,7 @@ namespace JSON { std::deque arrVal; std::map objVal; std::string read_string(int separator, std::istream & fromstream); - std::string string_escape(std::string val); + static std::string string_escape(const std::string val); int c2hex(int c); static void skipToEnd(std::istream & fromstream); public: @@ -58,24 +60,27 @@ namespace JSON { Value & operator=(const unsigned int &rhs); Value & operator=(const bool &rhs); //converts to basic types - operator long long int(); - operator std::string(); - operator bool(); - const std::string asString(); - const long long int asInt(); - const bool asBool(); - const std::string & asStringRef(); - const char * c_str(); + 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); + Value const & operator[](const std::string i) const; + Value const & operator[](const char * i) const; + Value const & operator[](unsigned int i) const; //handy functions and others std::string toPacked(); void netPrepare(); std::string & toNetPacked(); - std::string toString(); - std::string toPrettyString(int indentation = 0); + 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); @@ -91,7 +96,11 @@ namespace JSON { ObjIter ObjEnd(); ArrIter ArrBegin(); ArrIter ArrEnd(); - unsigned int size(); + ObjConstIter ObjBegin() const; + ObjConstIter ObjEnd() const; + ArrConstIter ArrBegin() const; + ArrConstIter ArrEnd() const; + unsigned int size() const; void null(); }; From 6f7b300bf5f653e562d33324d5574e30d4932547 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 25 Aug 2013 02:05:20 +0200 Subject: [PATCH 478/788] Fixed slow-working Util::Procs terminate handler. --- lib/procs.cpp | 59 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/lib/procs.cpp b/lib/procs.cpp index 4be1230b..86295771 100644 --- a/lib/procs.cpp +++ b/lib/procs.cpp @@ -25,47 +25,66 @@ std::map Util::Procs::exitHandlers; bool Util::Procs::handler_set = false; /// Called at exit of any program that used a Start* function. -/// Waits up to 2.5 seconds, then sends SIGINT signal to all managed processes. -/// After that waits up to 10 seconds for children to exit, then sends SIGKILL to +/// 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; - while ( !plist.empty()){ - Util::sleep(100); - if (++waiting > 25){ - break; + std::map listcopy = plist; + std::map::iterator it; + + //wait up to 1 second for applications to shut down + while ( !listcopy.empty() && waiting <= 50){ + for (it = listcopy.begin(); it != listcopy.end(); it++){ + if (kill(( *it).first, 0) == 0){ + Util::sleep(20); + ++waiting; + }else{ + listcopy.erase(it); + break; + } } } - if ( !plist.empty()){ - std::map listcopy = plist; - std::map::iterator it; + //send sigint to all remaining + if ( !listcopy.empty()){ for (it = listcopy.begin(); it != listcopy.end(); it++){ kill(( *it).first, SIGINT); } } waiting = 0; - while ( !plist.empty()){ - Util::sleep(200); - if (++waiting > 25){ - break; + //wait up to 5 seconds for applications to shut down + while ( !listcopy.empty() && waiting <= 50){ + for (it = listcopy.begin(); it != listcopy.end(); it++){ + if (kill(( *it).first, 0) == 0){ + Util::sleep(100); + ++waiting; + }else{ + listcopy.erase(it); + break; + } } } - + + //send sigkill to all remaining if ( !plist.empty()){ - std::map listcopy = plist; - std::map::iterator it; for (it = listcopy.begin(); it != listcopy.end(); it++){ kill(( *it).first, SIGKILL); } } waiting = 0; - while ( !plist.empty()){ - Util::sleep(100); - if (++waiting > 10){ - break; + //wait up to 1 second for applications to shut down + while ( !listcopy.empty() && waiting <= 50){ + for (it = listcopy.begin(); it != listcopy.end(); it++){ + if (kill(( *it).first, 0) == 0){ + Util::sleep(20); + ++waiting; + }else{ + listcopy.erase(it); + break; + } } } From c6eca9b9906879604d44013c145a3a58f0e5a807 Mon Sep 17 00:00:00 2001 From: Ozzay Date: Fri, 19 Apr 2013 16:21:40 +0200 Subject: [PATCH 479/788] First container boxes implemented for MP4. Also my first fixes for boxes. --- lib/mp4.cpp | 251 +++++++++++++++++++++++++++++++++++++++++----------- lib/mp4.h | 75 +++++++++++++++- 2 files changed, 270 insertions(+), 56 deletions(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 3b9cfce3..269ffe17 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -154,6 +154,21 @@ namespace MP4 { 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 0x75756964: return ((UUID*)this)->toPrettyString(indent); break; @@ -445,6 +460,62 @@ namespace MP4 { return true; } + uint32_t containerBox::getContentCount(){ + int res = 0; + int tempLoc = 0; + while (tempLoc < boxedSize() - 8){ + res++; + tempLoc += getBoxLen(tempLoc); + } + return res; + } + + void containerBox::setContent(Box & newContent, uint32_t no){ + int tempLoc = 0; + int contentCount = getContentCount(); + for (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; + } + int i = 0; + int tempLoc = 0; + while (i < no){ + tempLoc += getBoxLen(tempLoc); + i++; + } + return getBox(tempLoc); + } + + std::string containerBox::toPrettyContainerString(int indent, std::string boxName){ + std::stringstream r; + r << std::string(indent, ' ') << boxName <<" (" << 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(); + } + ABST::ABST(){ memcpy(data + 4, "abst", 4); setVersion(0); @@ -1192,60 +1263,8 @@ namespace MP4 { memcpy(data + 4, "moof", 4); } - uint32_t MOOF::getContentCount(){ - int res = 0; - int tempLoc = 0; - while (tempLoc < boxedSize() - 8){ - res++; - tempLoc += getBoxLen(tempLoc); - } - return res; - } - - void MOOF::setContent(Box & newContent, uint32_t no){ - int tempLoc = 0; - int contentCount = getContentCount(); - for (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 & MOOF::getContent(uint32_t no){ - static Box ret = Box((char*)"\000\000\000\010erro", false); - if (no > getContentCount()){ - return ret; - } - int i = 0; - int tempLoc = 0; - while (i < no){ - tempLoc += getBoxLen(tempLoc); - i++; - } - return getBox(tempLoc); - } - std::string MOOF::toPrettyString(int indent){ - std::stringstream r; - r << std::string(indent, ' ') << "[moof] Movie Fragment 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(); + return toPrettyContainerString(indent, std::string("[moof] Movie Fragment Box")); } TRAF::TRAF(){ @@ -2145,6 +2164,124 @@ namespace MP4 { return r.str(); } + FTYP::FTYP(){ + memcpy(data + 4, "ftyp", 4); + } + + void FTYP::setMajorBrand(uint32_t newMajorBrand){ + setInt32(newMajorBrand, 0); + } + + uint32_t FTYP::getMajorBrand(){ + return getInt32(0); + } + + void FTYP::setMinorVersion(uint32_t newMinorVersion){ + setInt32(newMinorVersion, 4); + } + + uint32_t FTYP::getMinorVersion(){ + return getInt32(4); + } + + uint32_t FTYP::getCompatibleBrandsCount(){ + return (payloadSize() - 8) / 4; + } + + void FTYP::setCompatibleBrands(uint32_t newCompatibleBrand, size_t index){ + setInt32(newCompatibleBrand, 8 + (index * 4)); + } + + uint32_t FTYP::getCompatibleBrands(size_t index){ + if (index >= getCompatibleBrandsCount()){ + return 0; + } + return getInt32(8 + (index * 4)); + } + + std::string FTYP::toPrettyString(int indent){ + std::stringstream r; + r << std::string(indent, ' ') << "[ftyp] File Type (" << boxedSize() << ")" << std::endl; + r << std::string(indent + 1, ' ') << "MajorBrand: 0x" << std::hex << getMajorBrand() << std::dec << std::endl; + r << std::string(indent + 1, ' ') << "MinorVersion: " << getMinorVersion() << std::endl; + r << std::string(indent + 1, ' ') << "CompatibleBrands (" << getCompatibleBrandsCount() << "):" << std::endl; + for (int i = 0; i < getCompatibleBrandsCount(); i++){ + r << std::string(indent + 2, ' ') << "[" << i << "] CompatibleBrand: 0x" << std::hex << getCompatibleBrands(i) << std::dec << std::endl; + } + return r.str(); + } + + MOOV::MOOV(){ + memcpy(data + 4, "moov", 4); + } + + std::string MOOV::toPrettyString(int indent){ + return toPrettyContainerString(indent, std::string("[moov] Movie Box")); + } + + MVEX::MVEX(){ + memcpy(data + 4, "mvex", 4); + } + + std::string MVEX::toPrettyString(int indent){ + return toPrettyContainerString(indent, std::string("[mvex] Movie Extends Header Box")); + } + + TREX::TREX(){ + memcpy(data + 4, "ftyp", 4); + } + + void TREX::setTrackID(uint32_t newTrackID){ + setInt32(newTrackID, 0); + } + + uint32_t TREX::getTrackID(){ + return getInt32(0); + } + + void TREX::setDefaultSampleDescriptionIndex(uint32_t newDefaultSampleDescriptionIndex){ + setInt32(newDefaultSampleDescriptionIndex,4); + } + + uint32_t TREX::getDefaultSampleDescriptionIndex(){ + return getInt32(4); + } + + void TREX::setDefaultSampleDuration(uint32_t newDefaultSampleDuration){ + setInt32(newDefaultSampleDuration,8); + } + + uint32_t TREX::getDefaultSampleDuration(){ + getInt32(8); + } + + void TREX::setDefaultSampleSize(uint32_t newDefaultSampleSize){ + setInt32(newDefaultSampleSize,12); + } + + uint32_t TREX::getDefaultSampleSize(){ + getInt32(12); + } + + void TREX::setDefaultSampleFlags(uint32_t newDefaultSampleFlags){ + setInt32(newDefaultSampleFlags,16); + } + + uint32_t TREX::getDefaultSampleFlags(){ + getInt32(16); + } + + std::string TREX::toPrettyString(int indent){ + std::stringstream r; + r << std::string(indent, ' ') << "[trex] Track Extends (" << boxedSize() << ")" << std::endl; + r << std::string(indent + 1, ' ') << "TrackID: " << getTrackID() << std::endl; + r << std::string(indent + 1, ' ') << "DefaultSampleDescriptionIndex : " << getDefaultSampleDescriptionIndex() << std::endl; + r << std::string(indent + 1, ' ') << "DefaultSampleDuration : " << getDefaultSampleDuration() << std::endl; + r << std::string(indent + 1, ' ') << "DefaultSampleSize : " << getDefaultSampleSize() << std::endl; + r << std::string(indent + 1, ' ') << "DefaultSampleFlags : " << getDefaultSampleFlags() << std::endl; + return r.str(); + } + static char c2hex(int c){ if (c >= '0' && c <= '9') return c - '0'; if (c >= 'a' && c <= 'f') return c - 'a' + 10; @@ -2152,6 +2289,14 @@ namespace MP4 { return 0; } + MFRA::MFRA(){ + memcpy(data + 4, "mfra", 4); + } + + std::string MFRA::toPrettyString(int indent){ + return toPrettyContainerString(indent, std::string("[mfra] Movie Fragment Random Acces Box")); + } + UUID::UUID(){ memcpy(data + 4, "uuid", 4); setInt64(0, 0); diff --git a/lib/mp4.h b/lib/mp4.h index a7d8f80e..91cd07cf 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -55,7 +55,17 @@ namespace MP4 { int payloadOffset; /// Date: Thu, 25 Apr 2013 08:45:56 +0200 Subject: [PATCH 480/788] Added 5 headers and the new STBL, which introduced us to 6 extra boxes to implement :( --- lib/mp4.cpp | 631 ++++++++++++++++++++++++++++++++++++++++++++++++++-- lib/mp4.h | 189 ++++++++++++++-- 2 files changed, 781 insertions(+), 39 deletions(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 269ffe17..5ebb70d7 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -119,7 +119,7 @@ namespace MP4 { /// 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 . - std::string Box::toPrettyString(int indent){ + std::string Box::toPrettyString(uint32_t indent){ switch (ntohl( *((int*)(data + 4)))){ //type is at this address case 0x6D666864: return ((MFHD*)this)->toPrettyString(indent); @@ -169,6 +169,54 @@ namespace MP4 { 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 0x75756964: return ((UUID*)this)->toPrettyString(indent); break; @@ -459,7 +507,22 @@ namespace MP4 { } return true; } + + 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); + } uint32_t containerBox::getContentCount(){ int res = 0; int tempLoc = 0; @@ -470,6 +533,13 @@ namespace MP4 { return res; } + 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(); + } + void containerBox::setContent(Box & newContent, uint32_t no){ int tempLoc = 0; int contentCount = getContentCount(); @@ -502,7 +572,7 @@ namespace MP4 { return getBox(tempLoc); } - std::string containerBox::toPrettyContainerString(int indent, std::string boxName){ + std::string containerBox::toPrettyContainerString(uint32_t indent, std::string boxName){ std::stringstream r; r << std::string(indent, ' ') << boxName <<" (" << boxedSize() << ")" << std::endl; Box curBox; @@ -1092,7 +1162,7 @@ namespace MP4 { return res; } - std::string AFRT::toPrettyString(int indent){ + 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; @@ -1218,7 +1288,7 @@ namespace MP4 { return res; } - std::string ASRT::toPrettyString(int indent){ + 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; @@ -1252,7 +1322,7 @@ namespace MP4 { return getInt32(4); } - std::string MFHD::toPrettyString(int indent){ + std::string MFHD::toPrettyString(uint32_t indent){ std::stringstream r; r << std::string(indent, ' ') << "[mfhd] Movie Fragment Header (" << boxedSize() << ")" << std::endl; r << std::string(indent + 1, ' ') << "SequenceNumber " << getSequenceNumber() << std::endl; @@ -1263,7 +1333,7 @@ namespace MP4 { memcpy(data + 4, "moof", 4); } - std::string MOOF::toPrettyString(int indent){ + std::string MOOF::toPrettyString(uint32_t indent){ return toPrettyContainerString(indent, std::string("[moof] Movie Fragment Box")); } @@ -1313,7 +1383,7 @@ namespace MP4 { return getBox(tempLoc); } - std::string TRAF::toPrettyString(int indent){ + std::string TRAF::toPrettyString(uint32_t indent){ std::stringstream r; r << std::string(indent, ' ') << "[traf] Track Fragment Box (" << boxedSize() << ")" << std::endl; Box curBox; @@ -2199,7 +2269,7 @@ namespace MP4 { return getInt32(8 + (index * 4)); } - std::string FTYP::toPrettyString(int indent){ + std::string FTYP::toPrettyString(uint32_t indent){ std::stringstream r; r << std::string(indent, ' ') << "[ftyp] File Type (" << boxedSize() << ")" << std::endl; r << std::string(indent + 1, ' ') << "MajorBrand: 0x" << std::hex << getMajorBrand() << std::dec << std::endl; @@ -2215,7 +2285,7 @@ namespace MP4 { memcpy(data + 4, "moov", 4); } - std::string MOOV::toPrettyString(int indent){ + std::string MOOV::toPrettyString(uint32_t indent){ return toPrettyContainerString(indent, std::string("[moov] Movie Box")); } @@ -2223,12 +2293,12 @@ namespace MP4 { memcpy(data + 4, "mvex", 4); } - std::string MVEX::toPrettyString(int indent){ + std::string MVEX::toPrettyString(uint32_t indent){ return toPrettyContainerString(indent, std::string("[mvex] Movie Extends Header Box")); } TREX::TREX(){ - memcpy(data + 4, "ftyp", 4); + memcpy(data + 4, "trex", 4); } void TREX::setTrackID(uint32_t newTrackID){ @@ -2271,7 +2341,7 @@ namespace MP4 { getInt32(16); } - std::string TREX::toPrettyString(int indent){ + std::string TREX::toPrettyString(uint32_t indent){ std::stringstream r; r << std::string(indent, ' ') << "[trex] Track Extends (" << boxedSize() << ")" << std::endl; r << std::string(indent + 1, ' ') << "TrackID: " << getTrackID() << std::endl; @@ -2281,7 +2351,534 @@ namespace MP4 { r << std::string(indent + 1, ' ') << "DefaultSampleFlags : " << getDefaultSampleFlags() << std::endl; return r.str(); } - + + TRAK::TRAK(){ + memcpy(data + 4, "trak", 4); + } + + std::string TRAK::toPrettyString(uint32_t indent){ + return toPrettyContainerString(indent, std::string("[trak] Track Structure")); + } + + MDIA::MDIA(){ + memcpy(data + 4, "mdia", 4); + } + + std::string MDIA::toPrettyString(uint32_t indent){ + return toPrettyContainerString(indent, std::string("[mdia] Track Media Structure")); + } + + MINF::MINF(){ + memcpy(data + 4, "minf", 4); + } + + std::string MINF::toPrettyString(uint32_t indent){ + return toPrettyContainerString(indent, std::string("[minf] Media Information")); + } + + DINF::DINF(){ + memcpy(data + 4, "dinf", 4); + } + + std::string DINF::toPrettyString(uint32_t indent){ + return toPrettyContainerString(indent, std::string("[dinf] Data Information")); + } + + MFRA::MFRA(){ + memcpy(data + 4, "mfra", 4); + } + + std::string MFRA::toPrettyString(uint32_t indent){ + return toPrettyContainerString(indent, std::string("[mfra] Movie Fragment Random Acces Box")); + } + + MFRO::MFRO(){ + memcpy(data + 4, "mfro", 4); + } + + void MFRO::setSize(uint32_t newSize){ + setInt32(newSize,0); + } + + uint32_t MFRO::getSize(){ + getInt32(0); + } + + std::string MFRO::toPrettyString(uint32_t indent){ + std::stringstream r; + r << std::string(indent, ' ') << "[mfro] Movie Fragment Random Access Offset (" << boxedSize() << ")" << std::endl; + r << std::string(indent + 1, ' ') << "Size: " << getSize() << std::endl; + return r.str(); + } + + HDLR::HDLR(){ + memcpy(data + 4, "hdlr", 4); + } + + void HDLR::setSize(uint32_t newSize){ + setInt32(newSize,0); + } + + uint32_t HDLR::getSize(){ + return getInt32(0); + } + + void HDLR::setPreDefined(uint32_t newPreDefined){ + setInt32(newPreDefined,4); + } + + uint32_t HDLR::getPreDefined(){ + return getInt32(4); + } + + void HDLR::setHandlerType(uint32_t newHandlerType){ + setInt32(newHandlerType, 8); + } + + uint32_t HDLR::getHandlerType(){ + return getInt32(8); + } + + void HDLR::setName(std::string newName){ + setString(newName, 24); + } + + std::string HDLR::getName(){ + return getString(24); + } + + std::string HDLR::toPrettyString(uint32_t indent){ + std::stringstream r; + r << std::string(indent, ' ') << "[hdlr] Handler Reference (" << boxedSize() << ")" << std::endl; + r << std::string(indent + 1, ' ') << "PreDefined: " << getPreDefined() << std::endl; + r << std::string(indent + 1, ' ') << "HandlerType: " << getHandlerType() << std::endl; + r << std::string(indent + 1, ' ') << "Name: " << getName() << std::endl; + return r.str(); + } + + //Note: next 4 headers inherit from fullBox, start at byte 4. + VMHD::VMHD(){ + memcpy(data + 4, "vmhd", 4); + } + + void VMHD::setGraphicsMode(uint16_t newGraphicsMode){ + setInt16(newGraphicsMode,4); + } + + uint16_t VMHD::getGraphicsMode(){ + return getInt16(4); + } + + uint32_t VMHD::getOpColorCount(){ + return 3; + } + + void VMHD::setOpColor(uint16_t newOpColor, size_t index){ + if (index <3){ + setInt16(newOpColor, 6 + (2 * index)); + } + } + + uint16_t VMHD::getOpColor(size_t index){ + if (index < 3){ + return getInt16(6 + (index * 2)); + }else{ + return 0; + } + } + + std::string VMHD::toPrettyString(uint32_t indent){ + std::stringstream r; + r << std::string(indent, ' ') << "[vmhd] Video Media Header Box (" << boxedSize() << ")" << std::endl; + r << fullBox::toPrettyString(indent); + r << std::string(indent + 1, ' ') << "GraphicsMode: " << getGraphicsMode() << std::endl; + for (int i = 0; i < getOpColorCount(); i++){ + r << std::string(indent + 1, ' ') << "OpColor["< getEntryCount()){ + int amount = index + 1 - getEntryCount(); + if ( !reserve(payloadOffset + offset, 0, amount * 8)){ + return; + } + for (int j = 0; j < amount; ++j){ + memcpy(data + payloadOffset + offset + j * 8, "\000\000\000\010erro", 8); + } + setInt32(index + 1, 4); + offset += (index - i) * 8; + } + setBox(newDataEntry, offset); + } + + Box & DREF::getDataEntry(size_t index){ + uint32_t offset = 8; + if (index > getEntryCount()){ + static Box res; + return (Box &)res; + } + + for (int i=0; i < index; i++){ + offset += getBoxLen(offset); + } + return (Box &)getBox(offset); + } + + std::string DREF::toPrettyString(uint32_t indent){ + std::stringstream r; + r << std::string(indent, ' ') << "[dref] Data Reference Box (" << boxedSize() << ")" << std::endl; + r << fullBox::toPrettyString(indent); + r << std::string(indent + 1, ' ') << "EntryCount: " << getEntryCount() << std::endl; + for (int32_t i = 0; i< getEntryCount(); i++){ + r << getDataEntry(i).toPrettyString(indent+1); + } + return r.str(); + } + + MVHD::MVHD(){ + memcpy(data + 4, "dref", 4); + } + + void MVHD::setCreationTime(uint64_t newCreationTime){ + if (getVersion() == 0){ + setInt32((uint32_t) newCreationTime, 4); + }else{ + setInt64(newCreationTime, 4); + } + } + + uint64_t MVHD::getCreationTime(){ + if (getVersion() == 0){ + return (uint64_t)getInt32(4); + }else{ + return getInt64(4); + } + } + + void MVHD::setModificationTime(uint64_t newModificationTime){ + if (getVersion() == 0){ + setInt32((uint32_t) newModificationTime, 8); + }else{ + setInt64(newModificationTime, 12); + } + } + + uint64_t MVHD::getModificationTime(){ + if (getVersion() == 0){ + return (uint64_t)getInt32(8); + }else{ + return getInt64(12); + } + } + + void MVHD::setTimeScale(uint32_t newTimeScale){ + if (getVersion() == 0){ + setInt32((uint32_t) newTimeScale, 12); + }else{ + setInt32(newTimeScale, 20); + } + } + + uint32_t MVHD::getTimeScale(){ + if (getVersion() == 0){ + return getInt32(12); + }else{ + return getInt32(20); + } + } + + void MVHD::setDuration(uint64_t newDuration){ + if (getVersion() == 0){ + setInt32((uint32_t) newDuration, 16); + }else{ + setInt64(newDuration, 24); + } + } + + uint64_t MVHD::getDuration(){ + if (getVersion() == 0){ + return (uint64_t)getInt32(16); + }else{ + return getInt64(24); + } + } + + void MVHD::setRate(uint32_t newRate){ + if (getVersion() == 0){ + setInt32( newRate, 20); + }else{ + setInt32(newRate, 32); + } + } + + uint32_t MVHD::getRate(){ + if (getVersion() == 0){ + return getInt32(20); + }else{ + return getInt32(32); + } + } + + void MVHD::setVolume(uint16_t newVolume){ + if (getVersion() == 0){ + setInt16(newVolume, 24); + }else{ + setInt16(newVolume, 36); + } + } + + uint16_t MVHD::getVolume(){ + if (getVersion() == 0){ + return getInt16(24); + }else{ + return getInt16(36); + } + } + //10 bytes reserverd in between + uint32_t MVHD::getMatrixCount(){ + return 9; + } + + void MVHD::setMatrix(int32_t newMatrix, size_t index){ + int offset = 0; + if (getVersion() == 0){ + offset = 24 + 2 + 10; + }else{ + offset = 36 + 2 + 10; + } + setInt32(newMatrix, offset + index * 4); + } + + int32_t MVHD::getMatrix(size_t index){ + int offset = 0; + if (getVersion() == 0){ + offset = 24 + 2 + 10; + }else{ + offset = 36 + 2 + 10; + } + return getInt32(offset + index * 4); + } + + //24 bytes of pre-defined in between + void MVHD::setTrackID(uint32_t newTrackID){ + if (getVersion() == 0){ + setInt32(newTrackID, 86); + }else{ + setInt32(newTrackID, 98); + } + } + + uint32_t MVHD::getTrackID(){ + if (getVersion() == 0){ + return getInt32(86); + }else{ + return getInt32(98); + } + } + + std::string MVHD::toPrettyString(uint32_t indent){ + std::stringstream r; + r << std::string(indent, ' ') << "[mvhd] Movie Header Box (" << boxedSize() << ")" << std::endl; + r << fullBox::toPrettyString(indent); + r << std::string(indent + 1, ' ') << "CreationTime: " << getCreationTime() << std::endl; + r << std::string(indent + 1, ' ') << "ModificationTime: " << getModificationTime() << std::endl; + r << std::string(indent + 1, ' ') << "TimeScale: " << getTimeScale() << std::endl; + r << std::string(indent + 1, ' ') << "Duration: " << getDuration() << std::endl; + r << std::string(indent + 1, ' ') << "Rate: " << getRate() << std::endl; + r << std::string(indent + 1, ' ') << "Volume: " << getVolume() << std::endl; + r << std::string(indent + 1, ' ') << "Matrix: "; + for (int32_t i = 0; i< getMatrixCount(); i++){ + r << getMatrix(i); + if (i!=getMatrixCount()-1){ + r << ", "; + } + } + r << std::endl; + r << std::string(indent + 1, ' ') << "TrackID: " << getTrackID() << std::endl; + return r.str(); + } + static char c2hex(int c){ if (c >= '0' && c <= '9') return c - '0'; if (c >= 'a' && c <= 'f') return c - 'a' + 10; @@ -2289,14 +2886,6 @@ namespace MP4 { return 0; } - MFRA::MFRA(){ - memcpy(data + 4, "mfra", 4); - } - - std::string MFRA::toPrettyString(int indent){ - return toPrettyContainerString(indent, std::string("[mfra] Movie Fragment Random Acces Box")); - } - UUID::UUID(){ memcpy(data + 4, "uuid", 4); setInt64(0, 0); diff --git a/lib/mp4.h b/lib/mp4.h index 91cd07cf..b030eaec 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -24,7 +24,7 @@ namespace MP4 { char * asBox(); char * payload(); void clear(); - std::string toPrettyString(int indent = 0); + std::string toPrettyString(uint32_t indent = 0); protected: //integer functions void setInt8(char newData, size_t index); @@ -56,16 +56,25 @@ namespace MP4 { }; //Box Class + class fullBox: public Box{ + public: + 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(int indent = 0); - std::string toPrettyContainerString(int indent, std::string boxName); + std::string toPrettyString(uint32_t indent = 0); + std::string toPrettyContainerString(uint32_t indent, std::string boxName); }; - + struct afrt_runtable{ uint32_t firstFragment; uint64_t firstTimestamp; @@ -90,7 +99,7 @@ namespace MP4 { uint32_t getFragmentRunCount(); void setFragmentRun(afrt_runtable newRun, uint32_t no); afrt_runtable getFragmentRun(uint32_t no); - std::string toPrettyString(int indent = 0); + std::string toPrettyString(uint32_t indent = 0); }; //AFRT Box @@ -113,7 +122,7 @@ namespace MP4 { uint32_t getSegmentRunEntryCount(); void setSegmentRun(uint32_t firstSegment, uint32_t fragmentsPerSegment, uint32_t no); asrt_runtable getSegmentRun(uint32_t no); - std::string toPrettyString(int indent = 0); + std::string toPrettyString(uint32_t indent = 0); }; //ASRT Box @@ -166,7 +175,7 @@ namespace MP4 { MFHD(); void setSequenceNumber(uint32_t newSequenceNumber); uint32_t getSequenceNumber(); - std::string toPrettyString(int indent = 0); + std::string toPrettyString(uint32_t indent = 0); }; //MFHD Box @@ -176,13 +185,13 @@ namespace MP4 { uint32_t getContentCount(); void setContent(Box & newContent, uint32_t no); Box & getContent(uint32_t no); - std::string toPrettyString(int indent = 0); + std::string toPrettyString(uint32_t indent = 0); };*/ class MOOF: public containerBox{ public: MOOF(); - std::string toPrettyString(int indent = 0); + std::string toPrettyString(uint32_t indent = 0); }; //MOOF Box @@ -192,7 +201,7 @@ namespace MP4 { uint32_t getContentCount(); void setContent(Box & newContent, uint32_t no); Box & getContent(uint32_t no); - std::string toPrettyString(int indent = 0); + std::string toPrettyString(uint32_t indent = 0); }; //TRAF Box @@ -346,19 +355,19 @@ namespace MP4 { uint32_t getCompatibleBrandsCount(); void setCompatibleBrands(uint32_t newCompatibleBrand, size_t index); uint32_t getCompatibleBrands(size_t index); - std::string toPrettyString(int indent = 0); + std::string toPrettyString(uint32_t indent = 0); }; class MOOV: public containerBox{ public: MOOV(); - std::string toPrettyString(int indent = 0); + std::string toPrettyString(uint32_t indent = 0); }; class MVEX: public containerBox{ public: MVEX(); - std::string toPrettyString(int indent = 0); + std::string toPrettyString(uint32_t indent = 0); }; class TREX: public Box{ @@ -374,20 +383,164 @@ namespace MP4 { uint32_t getDefaultSampleSize(); void setDefaultSampleFlags(uint32_t newDefaultSampleFlags); uint32_t getDefaultSampleFlags(); - std::string toPrettyString(int indent = 0); + std::string toPrettyString(uint32_t indent = 0); }; class MFRA: public containerBox{ public: MFRA(); - std::string toPrettyString(int indent = 0); + std::string toPrettyString(uint32_t indent = 0); + }; + + class TRAK: public containerBox{ + public: + TRAK(); + std::string toPrettyString(uint32_t indent = 0); }; - /*class MDAT: public Box{ + class MDIA: public containerBox{ public: - MDAT(); - };*/ + MDIA(); + std::string toPrettyString(uint32_t indent = 0); + }; + + class MINF: public containerBox{ + public: + MINF(); + std::string toPrettyString(uint32_t indent = 0); + }; + + class DINF: public containerBox{ + public: + DINF(); + std::string toPrettyString(uint32_t indent = 0); + }; + + 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(); + void setSize(uint32_t newSize); + uint32_t getSize(); + void setPreDefined(uint32_t newPreDefined); + uint32_t getPreDefined(); + void setHandlerType(uint32_t newHandlerType); + uint32_t 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(); + std::string toPrettyString(uint32_t indent = 0); + }; + + 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(); + 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); + }; class UUID: public Box{ public: From a11db00abbe7716a905726fe3555af4397e5d402 Mon Sep 17 00:00:00 2001 From: Ozzay Date: Thu, 25 Apr 2013 16:00:02 +0200 Subject: [PATCH 481/788] TKHD and MVHD boxes added --- lib/mp4.cpp | 412 +++++++++++++++++++++++++++++++++++++++++++++++++++- lib/mp4.h | 59 ++++++++ 2 files changed, 469 insertions(+), 2 deletions(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 5ebb70d7..a6f48de1 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -217,6 +217,12 @@ namespace MP4 { 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 0x75756964: return ((UUID*)this)->toPrettyString(indent); break; @@ -2717,7 +2723,7 @@ namespace MP4 { } MVHD::MVHD(){ - memcpy(data + 4, "dref", 4); + memcpy(data + 4, "mvhd", 4); } void MVHD::setCreationTime(uint64_t newCreationTime){ @@ -2878,7 +2884,409 @@ namespace MP4 { r << std::string(indent + 1, ' ') << "TrackID: " << getTrackID() << std::endl; return r.str(); } - + + TFRA::TFRA(){ + memcpy(data + 4, "dref", 4); + } + + //note, fullbox starts at byte 4 + void TFRA::setTrackID(uint32_t newTrackID){ + setInt32(newTrackID, 4); + } + + uint32_t TFRA::getTrackID(){ + getInt32(4); + } + + void TFRA::setLengthSizeOfTrafNum(char newVal){ + char part = getInt8(11); + setInt8(((newVal & 0x03)<<4) + (part & 0xCF),11); + } + + char TFRA::getLengthSizeOfTrafNum(){ + return (getInt8(11)>>4) & 0x03; + } + + void TFRA::setLengthSizeOfTrunNum(char newVal){ + char part = getInt8(11); + setInt8(((newVal & 0x03)<<2) + (part & 0xF3),11); + } + + char TFRA::getLengthSizeOfTrunNum(){ + return (getInt8(11)>>2) & 0x03; + } + + void TFRA::setLengthSizeOfSampleNum(char newVal){ + char part = getInt8(11); + setInt8(((newVal & 0x03)) + (part & 0xFC),11); + } + + char TFRA::getLengthSizeOfSampleNum(){ + return (getInt8(11)) & 0x03; + } + + void TFRA::setNumberOfEntry(uint32_t newNumberOfEntry){ + setInt32(newNumberOfEntry,12); + } + + uint32_t TFRA::getNumberOfEntry(){ + return getInt32(12); + } + + uint32_t TFRA::getTFRAEntrySize(){ + int EntrySize= (getVersion()==1 ? 16 : 8); + EntrySize += getLengthSizeOfTrafNum()+1; + EntrySize += getLengthSizeOfTrunNum()+1; + EntrySize += getLengthSizeOfSampleNum()+1; + return EntrySize; + } + + void TFRA::setTFRAEntry(TFRAEntry newTFRAEntry, uint32_t no){ + if (no + 1 > getNumberOfEntry()){//if a new entry is issued + uint32_t offset = 16 + getTFRAEntrySize() * getNumberOfEntry();//start of filler in bytes + uint32_t fillsize = (no + 1 - getNumberOfEntry())*getTFRAEntrySize();//filler in bytes + if ( !reserve(offset, 0, fillsize)){//filling space + return; + } + setNumberOfEntry(no+1); + } + uint32_t loc = 16 + no * getTFRAEntrySize(); + if (getVersion() == 1){ + setInt64(newTFRAEntry.time, loc); + setInt64(newTFRAEntry.moofOffset, loc+8); + loc += 16; + }else{ + setInt32(newTFRAEntry.time, loc); + setInt32(newTFRAEntry.moofOffset, loc+4); + loc += 8; + } + switch (getLengthSizeOfTrafNum()){ + case 0: + setInt8(newTFRAEntry.trafNumber, loc); + break; + case 1: + setInt16(newTFRAEntry.trafNumber, loc); + break; + case 2: + setInt24(newTFRAEntry.trafNumber, loc); + break; + case 3: + setInt32(newTFRAEntry.trafNumber, loc); + break; + } + loc += getLengthSizeOfTrafNum() + 1; + switch (getLengthSizeOfTrunNum()){ + case 0: + setInt8(newTFRAEntry.trunNumber, loc); + break; + case 1: + setInt16(newTFRAEntry.trunNumber, loc); + break; + case 2: + setInt24(newTFRAEntry.trunNumber, loc); + break; + case 3: + setInt32(newTFRAEntry.trunNumber, loc); + break; + } + loc += getLengthSizeOfTrunNum() + 1; + switch (getLengthSizeOfSampleNum()){ + case 0: + setInt8(newTFRAEntry.sampleNumber, loc); + break; + case 1: + setInt16(newTFRAEntry.sampleNumber, loc); + break; + case 2: + setInt24(newTFRAEntry.sampleNumber, loc); + break; + case 3: + setInt32(newTFRAEntry.sampleNumber, loc); + break; + } + } + + TFRAEntry & TFRA::getTFRAEntry(uint32_t no){ + static TFRAEntry retval; + if (no >= getNumberOfEntry()){ + static TFRAEntry inval; + return inval; + } + uint32_t loc = 16 + no * getTFRAEntrySize(); + if (getVersion() == 1){ + retval.time = getInt64(loc); + retval.moofOffset = getInt64(loc + 8); + loc += 16; + }else{ + retval.time = getInt32(loc); + retval.moofOffset = getInt32(loc + 4); + loc += 8; + } + switch (getLengthSizeOfTrafNum()){ + case 0: + retval.trafNumber = getInt8(loc); + break; + case 1: + retval.trafNumber = getInt16(loc); + break; + case 2: + retval.trafNumber = getInt24(loc); + break; + case 3: + retval.trafNumber = getInt32(loc); + break; + } + loc += getLengthSizeOfTrafNum() + 1; + switch (getLengthSizeOfTrunNum()){ + case 0: + retval.trunNumber = getInt8(loc); + break; + case 1: + retval.trunNumber = getInt16(loc); + break; + case 2: + retval.trunNumber = getInt24(loc); + break; + case 3: + retval.trunNumber = getInt32(loc); + break; + } + loc += getLengthSizeOfTrunNum() + 1; + switch (getLengthSizeOfSampleNum()){ + case 0: + retval.sampleNumber = getInt8(loc); + break; + case 1: + retval.sampleNumber = getInt16(loc); + break; + case 2: + retval.sampleNumber = getInt24(loc); + break; + case 3: + retval.sampleNumber = getInt32(loc); + break; + } + return retval; + } + + std::string TFRA::toPrettyString(uint32_t indent){ + std::stringstream r; + r << std::string(indent, ' ') << "[tfra] Track Fragment Random Access Box (" << boxedSize() << ")" << std::endl; + r << fullBox::toPrettyString(indent); + r << std::string(indent + 1, ' ') << "TrackID: " << getTrackID() << std::endl; + r << std::string(indent + 1, ' ') << "lengthSizeOfTrafNum: " << (int)getLengthSizeOfTrafNum() << std::endl; + r << std::string(indent + 1, ' ') << "lengthSizeOfTrunNum: " << (int)getLengthSizeOfTrunNum() << std::endl; + r << std::string(indent + 1, ' ') << "lengthSizeOfSampleNum: " << (int)getLengthSizeOfSampleNum() << std::endl; + r << std::string(indent + 1, ' ') << "NumberOfEntry: " << getNumberOfEntry() << std::endl; + for (int i = 0; i < getNumberOfEntry(); i++){ + static TFRAEntry temp; + temp = getTFRAEntry(i); + r << std::string(indent + 1, ' ') << "Entry[" << i <<"]:"<< std::endl; + r << std::string(indent + 2, ' ') << "Time: " << temp.time << std::endl; + r << std::string(indent + 2, ' ') << "MoofOffset: " << temp.moofOffset << std::endl; + r << std::string(indent + 2, ' ') << "TrafNumber: " << temp.trafNumber << std::endl; + r << std::string(indent + 2, ' ') << "TrunNumber: " << temp.trunNumber << std::endl; + r << std::string(indent + 2, ' ') << "SampleNumber: " << temp.sampleNumber << std::endl; + } + return r.str(); + } + + TKHD::TKHD(){ + memcpy(data + 4, "tkhd", 4); + } + + void TKHD::setCreationTime(uint64_t newCreationTime){ + if (getVersion() == 0){ + setInt32((uint32_t) newCreationTime, 4); + }else{ + setInt64(newCreationTime, 4); + } + } + + uint64_t TKHD::getCreationTime(){ + if (getVersion() == 0){ + return (uint64_t)getInt32(4); + }else{ + return getInt64(4); + } + } + + void TKHD::setModificationTime(uint64_t newModificationTime){ + if (getVersion() == 0){ + setInt32((uint32_t) newModificationTime, 8); + }else{ + setInt64(newModificationTime, 12); + } + } + + uint64_t TKHD::getModificationTime(){ + if (getVersion() == 0){ + return (uint64_t)getInt32(8); + }else{ + return getInt64(12); + } + } + + void TKHD::setTrackID(uint32_t newTrackID){ + if (getVersion() == 0){ + setInt32((uint32_t) newTrackID, 12); + }else{ + setInt32(newTrackID, 20); + } + } + + uint32_t TKHD::getTrackID(){ + if (getVersion() == 0){ + return getInt32(12); + }else{ + return getInt32(20); + } + } + //note 4 bytes reserved in between + void TKHD::setDuration(uint64_t newDuration){ + if (getVersion() == 0){ + setInt32(newDuration, 20); + }else{ + setInt64(newDuration, 28); + } + } + + uint64_t TKHD::getDuration(){ + if (getVersion() == 0){ + return (uint64_t)getInt32(20); + }else{ + return getInt64(28); + } + } + //8 bytes reserved in between + void TKHD::setLayer(uint16_t newLayer){ + if (getVersion() == 0){ + setInt16(newLayer, 32); + }else{ + setInt16(newLayer, 44); + } + } + + uint16_t TKHD::getLayer(){ + if (getVersion() == 0){ + return getInt16(32); + }else{ + return getInt16(44); + } + } + + void TKHD::setAlternateGroup(uint16_t newAlternateGroup){ + if (getVersion() == 0){ + setInt16(newAlternateGroup, 34); + }else{ + setInt16(newAlternateGroup, 46); + } + } + + uint16_t TKHD::getAlternateGroup(){ + if (getVersion() == 0){ + return getInt16(34); + }else{ + return getInt16(46); + } + } + + void TKHD::setVolume(uint16_t newVolume){ + if (getVersion() == 0){ + setInt16(newVolume, 36); + }else{ + setInt16(newVolume, 48); + } + } + + uint16_t TKHD::getVolume(){ + if (getVersion() == 0){ + return getInt16(36); + }else{ + return getInt16(48); + } + } + //2 bytes reserved in between + uint32_t TKHD::getMatrixCount(){ + return 9; + } + + void TKHD::setMatrix(int32_t newMatrix, size_t index){ + int offset = 0; + if (getVersion() == 0){ + offset = 36 + 2 + 2; + }else{ + offset = 48 + 2 + 2; + } + setInt32(newMatrix, offset + index * 4); + } + + int32_t TKHD::getMatrix(size_t index){ + int offset = 0; + if (getVersion() == 0){ + offset = 36 + 2 + 2; + }else{ + offset = 48 + 2 + 2; + } + return getInt32(offset + index * 4); + } + + void TKHD::setWidth(uint32_t newWidth){ + if (getVersion() == 0){ + setInt32(newWidth, 76); + }else{ + setInt32(newWidth, 88); + } + } + + uint32_t TKHD::getWidth(){ + if (getVersion() == 0){ + return getInt32(76); + }else{ + return getInt32(88); + } + } + + void TKHD::setHeight(uint32_t newHeight){ + if (getVersion() == 0){ + setInt32(newHeight, 80); + }else{ + setInt32(newHeight, 92); + } + } + + uint32_t TKHD::getHeight(){ + if (getVersion() == 0){ + return getInt32(80); + }else{ + return getInt32(92); + } + } + + std::string TKHD::toPrettyString(uint32_t indent){ + std::stringstream r; + r << std::string(indent, ' ') << "[tkhd] Track Header Box (" << boxedSize() << ")" << std::endl; + r << fullBox::toPrettyString(indent); + r << std::string(indent + 1, ' ') << "CreationTime: " << getCreationTime() << std::endl; + r << std::string(indent + 1, ' ') << "ModificationTime: " << getModificationTime() << std::endl; + r << std::string(indent + 1, ' ') << "TrackID: " << getTrackID() << std::endl; + r << std::string(indent + 1, ' ') << "Duration: " << getDuration() << std::endl; + r << std::string(indent + 1, ' ') << "Layer: " << getLayer() << std::endl; + r << std::string(indent + 1, ' ') << "AlternateGroup: " << getAlternateGroup() << std::endl; + r << std::string(indent + 1, ' ') << "Volume: " << getVolume() << std::endl; + r << std::string(indent + 1, ' ') << "Matrix: "; + for (int32_t i = 0; i< getMatrixCount(); i++){ + r << getMatrix(i); + if (i!=getMatrixCount()-1){ + r << ", "; + } + } + r << std::endl; + r << std::string(indent + 1, ' ') << "Width: " << (getWidth() >> 16) << "." << (getWidth() & 0xFFFF) << std::endl; + r << std::string(indent + 1, ' ') << "Height: " << (getHeight() >> 16) << "." << (getHeight() & 0xFFFF) << std::endl; + return r.str(); + } + static char c2hex(int c){ if (c >= '0' && c <= '9') return c - '0'; if (c >= 'a' && c <= 'f') return c - 'a' + 10; diff --git a/lib/mp4.h b/lib/mp4.h index b030eaec..5e10b584 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -534,6 +534,7 @@ namespace MP4 { uint32_t getRate(); void setVolume(uint16_t newVolume); uint16_t getVolume(); + ///\todo fix default values uint32_t getMatrixCount(); void setMatrix(int32_t newMatrix, size_t index); int32_t getMatrix(size_t index); @@ -542,6 +543,64 @@ namespace MP4 { 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(); + 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(); + ///\todo fix default values + 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 UUID: public Box{ public: UUID(); From 450fb82a4dddef4e08712af9107107a6d4c9e696 Mon Sep 17 00:00:00 2001 From: Ozzay Date: Sun, 28 Apr 2013 09:41:45 +0200 Subject: [PATCH 482/788] Added more boxes All but the last of the sample table have been implemented --- lib/mp4.cpp | 352 ++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/mp4.h | 85 +++++++++++++ 2 files changed, 437 insertions(+) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index a6f48de1..2c5f714a 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -223,6 +223,24 @@ namespace MP4 { 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 0x7374737A: + return ((STSZ*)this)->toPrettyString(indent); + break; case 0x75756964: return ((UUID*)this)->toPrettyString(indent); break; @@ -3286,7 +3304,341 @@ namespace MP4 { r << std::string(indent + 1, ' ') << "Height: " << (getHeight() >> 16) << "." << (getHeight() & 0xFFFF) << std::endl; return r.str(); } + + MDHD::MDHD(){ + memcpy(data + 4, "mdhd", 4); + } + void MDHD::setCreationTime(uint64_t newCreationTime){ + if (getVersion() == 0){ + setInt32((uint32_t) newCreationTime, 4); + }else{ + setInt64(newCreationTime, 4); + } + } + + uint64_t MDHD::getCreationTime(){ + if (getVersion() == 0){ + return (uint64_t)getInt32(4); + }else{ + return getInt64(4); + } + } + + void MDHD::setModificationTime(uint64_t newModificationTime){ + if (getVersion() == 0){ + setInt32((uint32_t) newModificationTime, 8); + }else{ + setInt64(newModificationTime, 12); + } + } + + uint64_t MDHD::getModificationTime(){ + if (getVersion() == 0){ + return (uint64_t)getInt32(8); + }else{ + return getInt64(12); + } + } + + void MDHD::setTimeScale(uint32_t newTimeScale){ + if (getVersion() == 0){ + setInt32((uint32_t) newTimeScale, 12); + }else{ + setInt32(newTimeScale, 20); + } + } + + uint32_t MDHD::getTimeScale(){ + if (getVersion() == 0){ + return getInt32(12); + }else{ + return getInt32(20); + } + } + + void MDHD::setDuration(uint64_t newDuration){ + if (getVersion() == 0){ + setInt32((uint32_t) newDuration, 16); + }else{ + setInt64(newDuration, 24); + } + } + + uint64_t MDHD::getDuration(){ + if (getVersion() == 0){ + return (uint64_t)getInt32(16); + }else{ + return getInt64(24); + } + } + + void MDHD::setLanguage (uint16_t newLanguage){ + if (getVersion() == 0){ + setInt16(newLanguage & 0x7F, 20); + }else{ + setInt16(newLanguage & 0x7F, 20); + } + } + + uint16_t MDHD::getLanguage(){ + if (getVersion() == 0){ + return getInt16(20) & 0x7F; + }else{ + return getInt16(20) & 0x7F; + } + } + + std::string MDHD::toPrettyString(uint32_t indent){ + std::stringstream r; + r << std::string(indent, ' ') << "[mdhd] Media Header Box (" << boxedSize() << ")" << std::endl; + r << fullBox::toPrettyString(indent); + r << std::string(indent + 1, ' ') << "CreationTime: " << getCreationTime() << std::endl; + r << std::string(indent + 1, ' ') << "ModificationTime: " << getModificationTime() << std::endl; + r << std::string(indent + 1, ' ') << "TimeScale: " << getTimeScale() << std::endl; + r << std::string(indent + 1, ' ') << "Duration: " << getDuration() << std::endl; + r << std::string(indent + 1, ' ') << "Language: 0x" << std::hex << getLanguage() << std::dec<< std::endl; + return r.str(); + } + + STTS::STTS(){ + memcpy(data + 4, "stts", 4); + } + + void STTS::setEntryCount(uint32_t newEntryCount){ + setInt32(newEntryCount, 4); + } + + uint32_t STTS::getEntryCount(){ + return getInt32(4); + } + + void STTS::setSTTSEntry(STTSEntry newSTTSEntry, uint32_t no){ + if(no + 1 > getEntryCount()){ + for (int i = getEntryCount(); i < no; i++){ + setInt64(0, 8 + (i * 8));//filling up undefined entries of 64 bits + } + } + setInt32(newSTTSEntry.sampleCount, 8 + no * 8); + setInt32(newSTTSEntry.sampleDelta, 8 + (no * 8) + 4); + } + + STTSEntry STTS::getSTTSEntry(uint32_t no){ + static STTSEntry retval; + if (no >= getEntryCount()){ + static STTSEntry inval; + return inval; + } + retval.sampleCount = getInt32(8 + (no * 8)); + retval.sampleDelta = getInt32(8 + (no * 8) + 4); + return retval; + } + + std::string STTS::toPrettyString(uint32_t indent){ + std::stringstream r; + r << std::string(indent, ' ') << "[stts] Sample Table Box (" << boxedSize() << ")" << std::endl; + r << fullBox::toPrettyString(indent); + r << std::string(indent + 1, ' ') << "EntryCount: " << getEntryCount() << std::endl; + for (int i = 0; i < getEntryCount(); i++){ + static STTSEntry temp; + temp = getSTTSEntry(i); + r << std::string(indent + 1, ' ') << "Entry[" << i <<"]:"<< std::endl; + r << std::string(indent + 2, ' ') << "SampleCount: " << temp.sampleCount << std::endl; + r << std::string(indent + 2, ' ') << "SampleDelta: " << temp.sampleDelta << std::endl; + } + return r.str(); + + } + + CTTS::CTTS(){ + memcpy(data + 4, "ctts", 4); + } + + void CTTS::setEntryCount(uint32_t newEntryCount){ + setInt32(newEntryCount, 4); + } + + uint32_t CTTS::getEntryCount(){ + return getInt32(4); + } + + void CTTS::setCTTSEntry(CTTSEntry newCTTSEntry, uint32_t no){ + if(no + 1 > getEntryCount()){ + for (int i = getEntryCount(); i < no; i++){ + setInt64(0, 8 + (i * 8));//filling up undefined entries of 64 bits + } + } + setInt32(newCTTSEntry.sampleCount, 8 + no * 8); + setInt32(newCTTSEntry.sampleOffset, 8 + (no * 8) + 4); + } + + CTTSEntry CTTS::getCTTSEntry(uint32_t no){ + static CTTSEntry retval; + if (no >= getEntryCount()){ + static CTTSEntry inval; + return inval; + } + retval.sampleCount = getInt32(8 + (no * 8)); + retval.sampleOffset = getInt32(8 + (no * 8) + 4); + return retval; + } + + std::string CTTS::toPrettyString(uint32_t indent){ + std::stringstream r; + r << std::string(indent, ' ') << "[stts] Sample Table Box (" << boxedSize() << ")" << std::endl; + r << fullBox::toPrettyString(indent); + r << std::string(indent + 1, ' ') << "EntryCount: " << getEntryCount() << std::endl; + for (int i = 0; i < getEntryCount(); i++){ + static CTTSEntry temp; + temp = getCTTSEntry(i); + r << std::string(indent + 1, ' ') << "Entry[" << i <<"]:"<< std::endl; + r << std::string(indent + 2, ' ') << "SampleCount: " << temp.sampleCount << std::endl; + r << std::string(indent + 2, ' ') << "SampleOffset: " << temp.sampleOffset << std::endl; + } + return r.str(); + + } + + STSC::STSC(){ + memcpy(data + 4, "stsc", 4); + } + + void STSC::setEntryCount(uint32_t newEntryCount){ + setInt32(newEntryCount, 4); + } + + uint32_t STSC::getEntryCount(){ + return getInt32(4); + } + + void STSC::setSTSCEntry(STSCEntry newSTSCEntry, uint32_t no){ + if(no + 1 > getEntryCount()){ + for (int i = getEntryCount(); i < no; i++){ + setInt64(0, 8 + (i * 12));//filling up undefined entries of 64 bits + setInt32(0, 8 + (i * 12) + 8); + } + } + setInt32(newSTSCEntry.firstChunk, 8 + no * 12); + setInt32(newSTSCEntry.samplesPerChunk, 8 + (no * 12) + 4); + setInt32(newSTSCEntry.sampleDescriptionIndex, 8 + (no * 12) + 8); + } + + STSCEntry STSC::getSTSCEntry(uint32_t no){ + static STSCEntry retval; + if (no >= getEntryCount()){ + static STSCEntry inval; + return inval; + } + retval.firstChunk = getInt32(8 + (no * 12)); + retval.samplesPerChunk = getInt32(8 + (no * 12) + 4); + retval.sampleDescriptionIndex = getInt32(8 + (no * 12) + 8); + return retval; + } + + std::string STSC::toPrettyString(uint32_t indent){ + std::stringstream r; + r << std::string(indent, ' ') << "[stsc] Sample To Chunk Box (" << boxedSize() << ")" << std::endl; + r << fullBox::toPrettyString(indent); + r << std::string(indent + 1, ' ') << "EntryCount: " << getEntryCount() << std::endl; + for (int i = 0; i < getEntryCount(); i++){ + static STSCEntry temp; + temp = getSTSCEntry(i); + r << std::string(indent + 1, ' ') << "Entry[" << i <<"]:"<< std::endl; + r << std::string(indent + 2, ' ') << "FirstChunk: " << temp.firstChunk << std::endl; + r << std::string(indent + 2, ' ') << "SamplesPerChunk: " << temp.samplesPerChunk << std::endl; + r << std::string(indent + 2, ' ') << "SampleDescriptionIndex: " << temp.sampleDescriptionIndex << std::endl; + } + return r.str(); + } + + STCO::STCO(){ + memcpy(data + 4, "stco", 4); + } + + void STCO::setEntryCount(uint32_t newEntryCount){ + setInt32(newEntryCount, 4); + } + + uint32_t STCO::getEntryCount(){ + return getInt32(4); + } + + void STCO::setChunkOffset(uint32_t newChunkOffset, uint32_t no){ + if (no + 1 > getEntryCount()){ + for (int i = getEntryCount(); i < no; i++){ + setInt32(0, 8 + i * 4);//filling undefined entries + } + } + setInt32(newChunkOffset, 8 + no * 4); + } + + uint32_t STCO::getChunkOffset(uint32_t no){ + if (no + 1 >= getEntryCount()){ + return 0; + } + return getInt32(8 + no * 4); + } + + std::string STCO::toPrettyString(uint32_t indent){ + std::stringstream r; + r << std::string(indent, ' ') << "[stco] Chunk Offset Box (" << boxedSize() << ")" << std::endl; + r << fullBox::toPrettyString(indent); + r << std::string(indent + 1, ' ') << "EntryCount: " << getEntryCount() << std::endl; + for (int i = 0; i < getEntryCount(); i++){ + r << std::string(indent + 1, ' ') << "ChunkOffset[" << i <<"]: " << getChunkOffset(i) << std::endl; + } + return r.str(); + } + + STSZ::STSZ(){ + memcpy(data + 4, "stco", 4); + } + + void STSZ::setSampleSize(uint32_t newSampleSize){ + setInt32(newSampleSize, 4); + } + + uint32_t STSZ::getSampleSize(){ + return getInt32(4); + } + + + void STSZ::setSampleCount(uint32_t newSampleCount){ + setInt32(newSampleCount, 8); + } + + uint32_t STSZ::getSampleCount(){ + return getInt32(8); + } + + void STSZ::setEntrySize(uint32_t newEntrySize, uint32_t no){ + if (no + 1 > getSampleCount()){ + for (int i = getSampleCount(); i < no; i++){ + setInt32(0, 12 + i * 4);//filling undefined entries + } + } + setInt32(newEntrySize, 12 + no * 4); + } + + uint32_t STSZ::getEntrySize(uint32_t no){ + if (no + 1 >= getSampleCount()){ + return 0; + } + return getInt32(12 + no * 4); + } + + std::string STSZ::toPrettyString(uint32_t indent){ + std::stringstream r; + r << std::string(indent, ' ') << "[stsz] Sample Size Box (" << boxedSize() << ")" << std::endl; + r << fullBox::toPrettyString(indent); + r << std::string(indent + 1, ' ') << "SampleSize: " << getSampleSize() << std::endl; + r << std::string(indent + 1, ' ') << "SampleCount: " << getSampleCount() << std::endl; + for (int i = 0; i < getSampleCount(); i++){ + r << std::string(indent + 1, ' ') << "EntrySize[" << i <<"]: " << getEntrySize(i) << std::endl; + } + return r.str(); + } + static char c2hex(int c){ if (c >= '0' && c <= '9') return c - '0'; if (c >= 'a' && c <= 'f') return c - 'a' + 10; diff --git a/lib/mp4.h b/lib/mp4.h index 5e10b584..4a599e3b 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -601,6 +601,91 @@ namespace MP4 { std::string toPrettyString(uint32_t indent = 0); }; + class MDHD: public fullBox{ + public: + MDHD(); + 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(); + 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(); + 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(); + 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 STSZ: public fullBox{ + public: + STSZ(); + 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 UUID: public Box{ public: UUID(); From 6a2145387cf8b2f9d31e078e8c7232075344a951 Mon Sep 17 00:00:00 2001 From: Oswald de Bruin Date: Mon, 29 Apr 2013 15:23:08 +0200 Subject: [PATCH 483/788] STSD added --- lib/mp4.cpp | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/mp4.h | 12 +++++++++- 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 2c5f714a..c4ead1eb 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -241,6 +241,9 @@ namespace MP4 { case 0x7374737A: return ((STSZ*)this)->toPrettyString(indent); break; + case 0x73747364: + return ((STSD*)this)->toPrettyString(indent); + break; case 0x75756964: return ((UUID*)this)->toPrettyString(indent); break; @@ -3639,6 +3642,69 @@ namespace MP4 { return r.str(); } + STSD::STSD(){ + memcpy(data + 4, "stsd", 4); + } + + void STSD::setEntryCount (uint32_t newEntryCount){ + setInt32(newEntryCount, 4); + } + + uint32_t STSD::getEntryCount(){ + return getInt32(4); + } + + void STSD::setEntry(Box & newContent, uint32_t no){ + int tempLoc = 8; + int entryCount = getEntryCount(); + for (int i = 0; i < no; i++){ + if (i < entryCount){ + tempLoc += getBoxLen(tempLoc); + }else{ + if ( !reserve(tempLoc, 0, (no - entryCount) * 8)){ + return; + } + memset(data + tempLoc, 0, (no - entryCount) * 8); + tempLoc += (no - entryCount) * 8; + break; + } + } + setBox(newContent, tempLoc); + if (getEntryCount() < no){ + setEntryCount(no); + } + } + + Box & STSD::getEntry(uint32_t no){ + static Box ret = Box((char*)"\000\000\000\010erro", false); + if (no > getEntryCount()){ + return ret; + } + int i = 0; + int tempLoc = 8; + while (i < no){ + tempLoc += getBoxLen(tempLoc); + i++; + } + return getBox(tempLoc); + } + + std::string STSD::toPrettyString(uint32_t indent){ + std::stringstream r; + r << std::string(indent, ' ') << "[stsd] Sample Description Box (" << boxedSize() << ")" << std::endl; + r << fullBox::toPrettyString(indent); + r << std::string(indent + 1, ' ') << "EntrySize: " << getEntryCount() << std::endl; + Box curBox; + int tempLoc = 0; + int contentCount = getEntryCount(); + for (int i = 0; i < contentCount; i++){ + curBox = getEntry(i); + r << curBox.toPrettyString(indent + 1); + tempLoc += getBoxLen(tempLoc); + } + return r.str(); + } + static char c2hex(int c){ if (c >= '0' && c <= '9') return c - '0'; if (c >= 'a' && c <= 'f') return c - 'a' + 10; diff --git a/lib/mp4.h b/lib/mp4.h index 4a599e3b..1b09870a 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -685,7 +685,17 @@ namespace MP4 { uint32_t getEntrySize(uint32_t no); std::string toPrettyString(uint32_t indent = 0); }; - + + class STSD: public fullBox{ + public: + STSD(); + 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 UUID: public Box{ public: UUID(); From 0e0b162bf7c624a254c27518f085d18722cb888b Mon Sep 17 00:00:00 2001 From: Oswald de Bruin Date: Wed, 1 May 2013 16:26:37 +0200 Subject: [PATCH 484/788] Skeleton for Audio and Visual sample entries. Mostly completed. Almost ready to support super speedway movie --- lib/mp4.cpp | 259 +++++++++++++++++++++++++++++++++++++++++++++++++++- lib/mp4.h | 88 ++++++++++++++++++ 2 files changed, 346 insertions(+), 1 deletion(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index c4ead1eb..7b1ae72f 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -244,6 +244,12 @@ namespace MP4 { case 0x73747364: return ((STSD*)this)->toPrettyString(indent); break; + case 0x6D703461: + return ((MP4A*)this)->toPrettyString(indent); + break; + case 0x61766331: + return ((AVC1*)this)->toPrettyString(indent); + break; case 0x75756964: return ((UUID*)this)->toPrettyString(indent); break; @@ -3594,7 +3600,7 @@ namespace MP4 { } STSZ::STSZ(){ - memcpy(data + 4, "stco", 4); + memcpy(data + 4, "stsz", 4); } void STSZ::setSampleSize(uint32_t newSampleSize){ @@ -3642,6 +3648,257 @@ namespace MP4 { return r.str(); } + SampleEntry::SampleEntry(){ + memcpy(data + 4, "erro", 4); + } + + void SampleEntry::setDataReferenceIndex(uint16_t newDataReferenceIndex){ + setInt16(newDataReferenceIndex, 6); + } + + uint16_t SampleEntry::getDataReferenceIndex(){ + return getInt16(6); + } + + std::string SampleEntry::toPrettySampleString(uint32_t indent){ + std::stringstream r; + r << std::string(indent + 1, ' ') << "DataReferenceIndex: " << getDataReferenceIndex() << std::endl; + return r.str(); + } + + CLAP::CLAP(){ + + } + + void CLAP::setCleanApertureWidthN(uint32_t newVal){ + + } + + uint32_t CLAP::getCleanApertureWidthN(){ + + } + + void CLAP::setCleanApertureWidthD(uint32_t newVal){ + + } + + uint32_t CLAP::getCleanApertureWidthD(){ + + } + + void CLAP::setCleanApertureHeightN(uint32_t newVal){ + + } + + uint32_t CLAP::getCleanApertureHeightN(){ + + } + + void CLAP::setCleanApertureHeightD(uint32_t newVal){ + + } + + uint32_t CLAP::getCleanApertureHeightD(){ + + } + + void CLAP::setHorizOffN(uint32_t newVal){ + + } + + uint32_t CLAP::getHorizOffN(){ + + } + + void CLAP::setHorizOffD(uint32_t newVal){ + + } + + uint32_t CLAP::getHorizOffD(){ + + } + + void CLAP::setVertOffN(uint32_t newVal){ + + } + + uint32_t CLAP::getVertOffN(){ + + } + + void CLAP::setVertOffD(uint32_t newVal){ + + } + + uint32_t CLAP::getVertOffD(){ + + } + + std::string CLAP::toPrettyString(uint32_t indent = 0){ + + } + + PASP::PASP(){ + + } + + void PASP::setHSpacing(uint32_t newVal){ + + } + + uint32_t PASP::getHSpacing(){ + + } + + void PASP::setVSpacing(uint32_t newVal){ + + } + + uint32_t PASP::getVSpacing(){ + + } + + std::string PASP::toPrettyString(uint32_t indent = 0){ + + } + + VisualSampleEntry::VisualSampleEntry(){ + memcpy(data + 4, "erro", 4); + } + + void VisualSampleEntry::setWidth(uint16_t newWidth){ + setInt16(newWidth,24); + } + + uint16_t VisualSampleEntry::getWidth(){ + return getInt16(24); + } + + void VisualSampleEntry::setHeight(uint16_t newHeight){ + setInt16(newHeight, 26); + } + + uint16_t VisualSampleEntry::getHeight(){ + return getInt16(26); + } + + void VisualSampleEntry::setHorizResolution (uint32_t newHorizResolution){ + setInt32(newHorizResolution, 28); + } + + uint32_t VisualSampleEntry::getHorizResolution(){ + return getInt32(28); + } + + void VisualSampleEntry::setVertResolution (uint32_t newVertResolution){ + setInt32(newVertResolution,32); + } + + uint32_t VisualSampleEntry::getVertResolution(){ + return getInt32(32); + } + + void VisualSampleEntry::setFrameCount(uint16_t newFrameCount){ + setInt16(newFrameCount, 40); + } + + uint16_t VisualSampleEntry::getFrameCount(){ + return getInt16(40); + } + + void VisualSampleEntry::setCompressorName(std::string newCompressorName){ + setString(newCompressorName,42); + } + + std::string VisualSampleEntry::getCompressorName(){ + return getString(42); + } + + void VisualSampleEntry::setDepth(uint16_t newDepth){ + setInt16(newDepth, 74); + } + + uint16_t VisualSampleEntry::getDepth(){ + getInt16(74); + } + + std::string VisualSampleEntry::toPrettyVisualString(uint32_t indent, std::string name){ + std::stringstream r; + r << std::string(indent, ' ') << name << " (" << boxedSize() << ")" << std::endl; + r << toPrettySampleString(indent); + r << std::string(indent + 1, ' ') << "Width: " << getWidth() << std::endl; + r << std::string(indent + 1, ' ') << "Height: " << getHeight() << std::endl; + r << std::string(indent + 1, ' ') << "HorizResolution: " << getHorizResolution() << std::endl; + r << std::string(indent + 1, ' ') << "VertResolution: " << getVertResolution() << std::endl; + r << std::string(indent + 1, ' ') << "FrameCount: " << getFrameCount() << std::endl; + r << std::string(indent + 1, ' ') << "CompressorName: " << getCompressorName() << std::endl; + r << std::string(indent + 1, ' ') << "Depth: " << getDepth() << std::endl; + return r.str(); + } + + AudioSampleEntry::AudioSampleEntry(){ + memcpy(data + 4, "erro", 4); + } + + void AudioSampleEntry::setChannelCount(uint16_t newChannelCount){ + setInt16(newChannelCount,16); + } + + uint16_t AudioSampleEntry::getChannelCount(){ + return getInt16(16); + } + + void AudioSampleEntry::setSampleSize(uint16_t newSampleSize){ + setInt16(newSampleSize,18); + } + + uint16_t AudioSampleEntry::getSampleSize(){ + return getInt16(18); + } + + void AudioSampleEntry::setPreDefined(uint16_t newPreDefined){ + setInt16(newPreDefined,20); + } + + uint16_t AudioSampleEntry::getPreDefined(){ + return getInt16(20); + } + + void AudioSampleEntry::setSampleRate(uint32_t newSampleRate){ + setInt32(newSampleRate,24); + } + + uint32_t AudioSampleEntry::getSampleRate(){ + return getInt32(24); + } + + std::string AudioSampleEntry::toPrettyAudioString(uint32_t indent, std::string name){ + std::stringstream r; + r << std::string(indent, ' ') << name << " (" << boxedSize() << ")" << std::endl; + r << toPrettySampleString(indent); + r << std::string(indent + 1, ' ') << "ChannelCount: " << getChannelCount() << std::endl; + r << std::string(indent + 1, ' ') << "SampleSize: " << getSampleSize() << std::endl; + r << std::string(indent + 1, ' ') << "PreDefined: " << getPreDefined() << std::endl; + r << std::string(indent + 1, ' ') << "SampleRate: " << getSampleRate() << std::endl; + return r.str(); + } + + MP4A::MP4A(){ + memcpy(data + 4, "mp4a", 4); + } + + std::string MP4A::toPrettyString(uint32_t indent){ + return toPrettyAudioString(indent, "[mp4a] MPEG 4 Audio"); + } + + AVC1::AVC1(){ + memcpy(data + 4, "avc1", 4); + } + + std::string AVC1::toPrettyString(uint32_t indent){ + return toPrettyVisualString(indent, "[avc1] Advanced Video Codec 1"); + } + STSD::STSD(){ memcpy(data + 4, "stsd", 4); } diff --git a/lib/mp4.h b/lib/mp4.h index 1b09870a..3ac99060 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -686,6 +686,94 @@ namespace MP4 { 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 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(); + std::string toPrettyVisualString(uint32_t index = 0, std::string = ""); + }; + + class AudioSampleEntry: public SampleEntry{ + public: + ///\todo set default values + AudioSampleEntry(); + 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); + uint32_t getSampleRate(); + std::string toPrettyAudioString(uint32_t indent = 0, std::string name = ""); + }; + + class MP4A: public AudioSampleEntry{ + public: + MP4A(); + std::string toPrettyString(uint32_t indent = 0); + }; + + class AVC1: public VisualSampleEntry{ + public: + AVC1(); + std::string toPrettyString(uint32_t indent = 0); + }; + class STSD: public fullBox{ public: STSD(); From c7d1a0c9ad338367dc9acc1a9c07ab48d160bd5a Mon Sep 17 00:00:00 2001 From: Oswald de Bruin Date: Thu, 2 May 2013 10:56:45 +0200 Subject: [PATCH 485/788] MP4 support for ISMV finished. --- lib/mp4.cpp | 108 ++++++++++++++++++++++++++++++++++++++-------------- lib/mp4.h | 4 +- 2 files changed, 83 insertions(+), 29 deletions(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 7b1ae72f..a95eab7e 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -3667,99 +3667,113 @@ namespace MP4 { } CLAP::CLAP(){ - + memcpy(data + 4, "clap", 4); } void CLAP::setCleanApertureWidthN(uint32_t newVal){ - + setInt32(newVal,0); } uint32_t CLAP::getCleanApertureWidthN(){ - + return getInt32(0); } void CLAP::setCleanApertureWidthD(uint32_t newVal){ - + setInt32(newVal,4); } uint32_t CLAP::getCleanApertureWidthD(){ - + return getInt32(4); } void CLAP::setCleanApertureHeightN(uint32_t newVal){ - + setInt32(newVal,8); } uint32_t CLAP::getCleanApertureHeightN(){ - + return getInt32(8); } void CLAP::setCleanApertureHeightD(uint32_t newVal){ - + setInt32(newVal, 12); } uint32_t CLAP::getCleanApertureHeightD(){ - + return getInt32(12); } void CLAP::setHorizOffN(uint32_t newVal){ - + setInt32(newVal, 16); } uint32_t CLAP::getHorizOffN(){ - + return getInt32(16); } void CLAP::setHorizOffD(uint32_t newVal){ - + setInt32(newVal, 20); } uint32_t CLAP::getHorizOffD(){ - + return getInt32(20); } void CLAP::setVertOffN(uint32_t newVal){ - + setInt32(newVal, 24); } uint32_t CLAP::getVertOffN(){ - + return getInt32(24); } void CLAP::setVertOffD(uint32_t newVal){ - + setInt32(newVal, 28); } uint32_t CLAP::getVertOffD(){ - + return getInt32(32); } - std::string CLAP::toPrettyString(uint32_t indent = 0){ - + std::string CLAP::toPrettyString(uint32_t indent){ + std::stringstream r; + r << std::string(indent, ' ') << "[clap] Clean Aperture Box (" << boxedSize() << ")" << std::endl; + r << std::string(indent + 1, ' ') << "CleanApertureWidthN: " << getCleanApertureWidthN() << std::endl; + r << std::string(indent + 1, ' ') << "CleanApertureWidthD: " << getCleanApertureWidthD() << std::endl; + r << std::string(indent + 1, ' ') << "CleanApertureHeightN: " << getCleanApertureHeightN() << std::endl; + r << std::string(indent + 1, ' ') << "CleanApertureHeightD: " << getCleanApertureHeightD() << std::endl; + r << std::string(indent + 1, ' ') << "HorizOffN: " << getHorizOffN() << std::endl; + r << std::string(indent + 1, ' ') << "HorizOffD: " << getHorizOffD() << std::endl; + r << std::string(indent + 1, ' ') << "VertOffN: " << getVertOffN() << std::endl; + r << std::string(indent + 1, ' ') << "VertOffD: " << getVertOffD() << std::endl; + return r.str(); } PASP::PASP(){ - + memcpy(data + 4, "pasp", 4); } void PASP::setHSpacing(uint32_t newVal){ - + setInt32(newVal, 0); } uint32_t PASP::getHSpacing(){ - + return getInt32(0); } void PASP::setVSpacing(uint32_t newVal){ - + setInt32(newVal, 4); } uint32_t PASP::getVSpacing(){ - + return getInt32(4); } - std::string PASP::toPrettyString(uint32_t indent = 0){ - + std::string PASP::toPrettyString(uint32_t indent){ + std::stringstream r; + r << std::string(indent, ' ') << "[pasp] Pixel Aspect Ratio Box (" << boxedSize() << ")" << std::endl; + r << std::string(indent + 1, ' ') << "HSpacing: " << getHSpacing() << std::endl; + r << std::string(indent + 1, ' ') << "VSpacing: " << getVSpacing() << std::endl; + return r.str(); } VisualSampleEntry::VisualSampleEntry(){ @@ -3821,6 +3835,38 @@ namespace MP4 { uint16_t VisualSampleEntry::getDepth(){ getInt16(74); } + + Box & VisualSampleEntry::getCLAP(){ + static Box ret = Box((char*)"\000\000\000\010erro", false); + if(payloadSize() <84){//if the EntryBox is not big enough to hold a CLAP/PASP + return ret; + } + if (getBox(76).isType("clap")){ + return getBox(76); + }else{ + return ret; + } + } + + Box & VisualSampleEntry::getPASP(){ + static Box ret = Box((char*)"\000\000\000\010erro", false); + if(payloadSize() <84){//if the EntryBox is not big enough to hold a CLAP/PASP + return ret; + } + if (getBox(76).isType("pasp")){ + return getBox(76); + }else{ + if (payloadSize() < 76 + getBoxLen(76) + 8){ + return ret; + }else{ + if (getBox(76+getBoxLen(76)).isType("pasp")){ + return getBox(76+getBoxLen(76)); + }else{ + return ret; + } + } + } + } std::string VisualSampleEntry::toPrettyVisualString(uint32_t indent, std::string name){ std::stringstream r; @@ -3833,6 +3879,12 @@ namespace MP4 { r << std::string(indent + 1, ' ') << "FrameCount: " << getFrameCount() << std::endl; r << std::string(indent + 1, ' ') << "CompressorName: " << getCompressorName() << std::endl; r << std::string(indent + 1, ' ') << "Depth: " << getDepth() << std::endl; + if (getCLAP().isType("clap")){ + r << getCLAP().toPrettyString(indent+1); + } + if (getPASP().isType("pasp")){ + r << getPASP().toPrettyString(indent+1); + } return r.str(); } @@ -3865,11 +3917,11 @@ namespace MP4 { } void AudioSampleEntry::setSampleRate(uint32_t newSampleRate){ - setInt32(newSampleRate,24); + setInt32(newSampleRate << 16, 24); } uint32_t AudioSampleEntry::getSampleRate(){ - return getInt32(24); + return getInt32(24) >> 16; } std::string AudioSampleEntry::toPrettyAudioString(uint32_t indent, std::string name){ diff --git a/lib/mp4.h b/lib/mp4.h index 3ac99060..cbecd429 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -724,7 +724,7 @@ namespace MP4 { void setVSpacing(uint32_t newVal); uint32_t getVSpacing(); std::string toPrettyString(uint32_t indent = 0); - } + }; class VisualSampleEntry: public SampleEntry{ ///\todo set default values @@ -744,6 +744,8 @@ namespace MP4 { std::string getCompressorName(); void setDepth(uint16_t newDepth); uint16_t getDepth(); + Box & getCLAP(); + Box & getPASP(); std::string toPrettyVisualString(uint32_t index = 0, std::string = ""); }; From 4cb5d5a5abb9b86e1d5e625bb62e40f1236e8682 Mon Sep 17 00:00:00 2001 From: Oswald de Bruin Date: Thu, 2 May 2013 16:27:15 +0200 Subject: [PATCH 486/788] Added stss box --- lib/mp4.cpp | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/mp4.h | 22 +++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index a95eab7e..b32efba2 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -250,6 +250,15 @@ namespace MP4 { case 0x61766331: return ((AVC1*)this)->toPrettyString(indent); break; + case 0x65647473: + return ((EDTS*)this)->toPrettyString(indent); + break; + case 0x73747373: + return ((STSS*)this)->toPrettyString(indent); + break; + case 0x75647461: + return ((UDTA*)this)->toPrettyString(indent); + break; case 0x75756964: return ((UUID*)this)->toPrettyString(indent); break; @@ -4014,6 +4023,59 @@ namespace MP4 { return r.str(); } + EDTS::EDTS(){ + memcpy(data + 4, "edts", 4); + } + + std::string EDTS::toPrettyString(uint32_t indent){ + return toPrettyContainerString(indent, std::string("[edts] Edit Box")); + } + + UDTA::UDTA(){ + memcpy(data + 4, "udta", 4); + } + + std::string UDTA::toPrettyString(uint32_t indent){ + return toPrettyContainerString(indent, std::string("[udta] User Data Box")); + } + + STSS::STSS(){ + memcpy(data + 4, "stss", 4); + } + + void STSS::setEntryCount(uint32_t newVal){ + setInt32(newVal, 4); + } + + uint32_t STSS::getEntryCount(){ + getInt32(4); + } + + void STSS::setSampleNumber(uint32 newVal, uint32_t index){ + if (index+1 > getEntryCount()){ + setEntryCount(index); + } + setInt32(newVal, 8 + (index * 4)); + } + + uint32_t STSS::getSampleNumber(uint32_t index){ + if (index >= getEntryCount()){ + return 0; + } + return getInt32(8 + (index * 4)); + } + + std::string STSS::toPrettyString(uint32_t indent){ + std::stringstream r; + r << std::string(indent, ' ') << "[stss] Sync Sample Box (" << boxedSize() << ")" << std::endl; + r << fullBox::toPrettyString(indent); + r << std::string(indent + 1, ' ') << "EntryCount: " << getEntryCount() << std::endl; + for (int i = 0; i < getEntryCount(); i++){ + r << std::string(indent + 1, ' ') << "SampleNumber[" << i <<"] : " << getSampleNumber(i) << std::endl; + } + return r.str(); + } + static char c2hex(int c){ if (c >= '0' && c <= '9') return c - '0'; if (c >= 'a' && c <= 'f') return c - 'a' + 10; diff --git a/lib/mp4.h b/lib/mp4.h index cbecd429..094f6d15 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -786,6 +786,28 @@ namespace MP4 { std::string toPrettyString(uint32_t indent = 0); }; + class EDTS: public containerBox{ + public: + EDTS(); + std::string toPrettyString(uint32_t indent = 0); + }; + + class UDTA: public containerBox{ + public: + UDTA(); + std::string toPrettyString(uint32_t indent = 0); + }; + + class STSS: public fullBox{ + public: + STSS(); + void setEntryCount(uint32_t newVal); + uint32_t getEntryCount(); + void setSampleNumber(uint32 newVal, uint32_t index); + uint32_t getSampleNumber(uint32_t index); + std::string toPrettyString(uint32_t indent = 0); + }; + class UUID: public Box{ public: UUID(); From 7b6eb6ad26f909f49b69a1d028605d009aee0fd8 Mon Sep 17 00:00:00 2001 From: Oswald de Bruin Date: Fri, 3 May 2013 13:42:13 +0200 Subject: [PATCH 487/788] Added Meta and Edit list boxes --- lib/mp4.cpp | 167 +++++++++++++++++++++++++++++++++++++++++++++++++--- lib/mp4.h | 30 +++++++++- 2 files changed, 188 insertions(+), 9 deletions(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index b32efba2..6cfef87c 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -256,6 +256,12 @@ namespace MP4 { 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 0x75647461: return ((UDTA*)this)->toPrettyString(indent); break; @@ -565,6 +571,14 @@ namespace MP4 { 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(); + } + uint32_t containerBox::getContentCount(){ int res = 0; int tempLoc = 0; @@ -575,13 +589,6 @@ namespace MP4 { return res; } - 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(); - } - void containerBox::setContent(Box & newContent, uint32_t no){ int tempLoc = 0; int contentCount = getContentCount(); @@ -627,6 +634,63 @@ namespace MP4 { } return r.str(); } + + uint32_t containerFullBox::getContentCount(){ + int res = 0; + int tempLoc = 4; + while (tempLoc < boxedSize() - 8){ + res++; + tempLoc += getBoxLen(tempLoc); + } + return res; + } + + void containerFullBox::setContent(Box & newContent, uint32_t no){ + int tempLoc = 4; + int contentCount = getContentCount(); + for (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; + } + 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(); + } ABST::ABST(){ memcpy(data + 4, "abst", 4); @@ -4051,7 +4115,7 @@ namespace MP4 { getInt32(4); } - void STSS::setSampleNumber(uint32 newVal, uint32_t index){ + void STSS::setSampleNumber(uint32_t newVal, uint32_t index){ if (index+1 > getEntryCount()){ setEntryCount(index); } @@ -4076,6 +4140,93 @@ namespace MP4 { return r.str(); } + META::META(){ + memcpy(data + 4, "meta", 4); + } + + std::string META::toPrettyString(uint32_t indent){ + return toPrettyCFBString(indent, "[meta] Meta Box"); + } + + ELST::ELST(){ + memcpy(data + 4, "elst", 4); + } + + void ELST::setSegmentDuration(uint64_t newVal){ + if (getVersion() == 1){ + setInt64(newVal, 4); + }else{ + setInt32(newVal, 4); + } + } + + uint64_t ELST::getSegmentDuration(){ + if (getVersion() == 1){ + return getInt64(4); + }else{ + return getInt32(4); + } + } + + void ELST::setMediaTime(uint64_t newVal){ + if (getVersion() == 1){ + setInt64(newVal, 12); + }else{ + setInt32(newVal, 8); + } + } + + uint64_t ELST::getMediaTime(){ + if (getVersion() == 1){ + return getInt64(12); + }else{ + return getInt32(8); + } + } + + void ELST::setMediaRateInteger(uint16_t newVal){ + if (getVersion() == 1){ + setInt16(newVal, 20); + }else{ + setInt16(newVal, 12); + } + } + + uint16_t ELST::getMediaRateInteger(){ + if (getVersion() == 1){ + return getInt16(20); + }else{ + return getInt16(12); + } + } + + void ELST::setMediaRateFraction(uint16_t newVal){ + if (getVersion() == 1){ + setInt16(newVal, 22); + }else{ + setInt16(newVal, 14); + } + } + + uint16_t ELST::getMediaRateFraction(){ + if (getVersion() == 1){ + return getInt16(22); + }else{ + return getInt16(14); + } + } + + std::string ELST::toPrettyString(uint32_t indent){ + std::stringstream r; + r << std::string(indent, ' ') << "[elst] Edit List Box (" << boxedSize() << ")" << std::endl; + r << fullBox::toPrettyString(indent); + r << std::string(indent + 1, ' ') << "SegmentDuration: " << getSegmentDuration() << std::endl; + r << std::string(indent + 1, ' ') << "MediaTime: " << getMediaTime() << std::endl; + r << std::string(indent + 1, ' ') << "MediaRateInteger: " << getMediaRateInteger() << std::endl; + r << std::string(indent + 1, ' ') << "MediaRateFraction: " << getMediaRateFraction() << std::endl; + return r.str(); + } + static char c2hex(int c){ if (c >= '0' && c <= '9') return c - '0'; if (c >= 'a' && c <= 'f') return c - 'a' + 10; diff --git a/lib/mp4.h b/lib/mp4.h index 094f6d15..c69150fb 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -74,6 +74,14 @@ namespace MP4 { std::string toPrettyString(uint32_t indent = 0); std::string toPrettyContainerString(uint32_t indent, std::string boxName); }; + + 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); + }; struct afrt_runtable{ uint32_t firstFragment; @@ -803,10 +811,30 @@ namespace MP4 { STSS(); void setEntryCount(uint32_t newVal); uint32_t getEntryCount(); - void setSampleNumber(uint32 newVal, uint32_t index); + 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); + }; class UUID: public Box{ public: From 6d6b001ec952072080e67fbe15e1626b30015526 Mon Sep 17 00:00:00 2001 From: Oswald de Bruin Date: Wed, 22 May 2013 15:59:21 +0200 Subject: [PATCH 488/788] Initialised boxes with default values as depicted in MP4 documentation --- lib/mp4.cpp | 92 +++++++++++++++++++++++++++++++++++++++++++++++------ lib/mp4.h | 19 +++++------ 2 files changed, 92 insertions(+), 19 deletions(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 6cfef87c..e0d4cb8b 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -556,6 +556,9 @@ namespace MP4 { return true; } + fullBox::fullBox(){ + } + void fullBox::setVersion(char newVersion){ setInt8(newVersion, 0); } @@ -579,6 +582,10 @@ namespace MP4 { return r.str(); } + containerBox::containerBox(){ + + } + uint32_t containerBox::getContentCount(){ int res = 0; int tempLoc = 0; @@ -2342,6 +2349,8 @@ namespace MP4 { FTYP::FTYP(){ memcpy(data + 4, "ftyp", 4); + setMajorBrand(0); + setMinorVersion(0); } void FTYP::setMajorBrand(uint32_t newMajorBrand){ @@ -2519,6 +2528,7 @@ namespace MP4 { HDLR::HDLR(){ memcpy(data + 4, "hdlr", 4); + setName(""); } void HDLR::setSize(uint32_t newSize){ @@ -2770,8 +2780,11 @@ namespace MP4 { } - DREF::DREF(){ + DREF::DREF(char v, uint32_t f){ memcpy(data + 4, "dref", 4); + setVersion(v); + setFlags(f); + setInt32(0,4); } uint32_t DREF::getEntryCount(){ @@ -2822,8 +2835,27 @@ namespace MP4 { return r.str(); } - MVHD::MVHD(){ + MVHD::MVHD(char v, uint32_t f){ memcpy(data + 4, "mvhd", 4); + setVersion(v); + setFlags(f); + setCreationTime(0); + setModificationTime(0); + setTimeScale(1000); + setDuration(0); + setRate(0x00010000); + setVolume(0x0100); + setMatrix(0x00010000,0); + /*setMatrix(0,1); + setMatrix(0,2); + setMatrix(0,3);*/ + setMatrix(0x00010000,4); + /*setMatrix(0,5); + setMatrix(0,6); + setMatrix(0,7);*/ + //fills automatically with zero's + setMatrix(0x40000000,8); + setTrackID(0); } void MVHD::setCreationTime(uint64_t newCreationTime){ @@ -3191,8 +3223,23 @@ namespace MP4 { return r.str(); } - TKHD::TKHD(){ + TKHD::TKHD(char v, uint32_t f){ memcpy(data + 4, "tkhd", 4); + setVersion(v); + setFlags(f); + setCreationTime(0); + setModificationTime(0); + setTrackID(0); + setDuration(0); + setLayer(0); + setAlternateGroup(0); + setVolume(0x0100); + setMatrix(0x00010000,0); + setMatrix(0x00010000,4); + //fills automatically with zero's + setMatrix(0x40000000,8); + setWidth(0); + setHeight(0); } void TKHD::setCreationTime(uint64_t newCreationTime){ @@ -3387,8 +3434,20 @@ namespace MP4 { return r.str(); } - MDHD::MDHD(){ + MDHD::MDHD(char v, uint32_t f){ memcpy(data + 4, "mdhd", 4); + setVersion(v); + setFlags(f); + setCreationTime(0); + setModificationTime(0); + setTimeScale(1000); + setDuration(0); + setLanguage(0); + if (v==0){ + setInt16(0,22); + }else{ + setInt16(0,34); + } } void MDHD::setCreationTime(uint64_t newCreationTime){ @@ -3459,7 +3518,7 @@ namespace MP4 { if (getVersion() == 0){ setInt16(newLanguage & 0x7F, 20); }else{ - setInt16(newLanguage & 0x7F, 20); + setInt16(newLanguage & 0x7F, 32); } } @@ -3467,7 +3526,7 @@ namespace MP4 { if (getVersion() == 0){ return getInt16(20) & 0x7F; }else{ - return getInt16(20) & 0x7F; + return getInt16(32) & 0x7F; } } @@ -3485,6 +3544,7 @@ namespace MP4 { STTS::STTS(){ memcpy(data + 4, "stts", 4); + setEntryCount(0); } void STTS::setEntryCount(uint32_t newEntryCount){ @@ -3581,8 +3641,11 @@ namespace MP4 { } - STSC::STSC(){ + STSC::STSC(char v, uint32_t f){ memcpy(data + 4, "stsc", 4); + setVersion(v); + setFlags(f); + setEntryCount(0); } void STSC::setEntryCount(uint32_t newEntryCount){ @@ -3633,8 +3696,11 @@ namespace MP4 { return r.str(); } - STCO::STCO(){ + STCO::STCO(char v, uint32_t f){ memcpy(data + 4, "stco", 4); + setVersion(v); + setFlags(f); + setEntryCount(0); } void STCO::setEntryCount(uint32_t newEntryCount){ @@ -4024,8 +4090,11 @@ namespace MP4 { return toPrettyVisualString(indent, "[avc1] Advanced Video Codec 1"); } - STSD::STSD(){ + STSD::STSD(char v, uint32_t f){ memcpy(data + 4, "stsd", 4); + setVersion(v); + setFlags(f); + setEntryCount(0); } void STSD::setEntryCount (uint32_t newEntryCount){ @@ -4103,8 +4172,11 @@ namespace MP4 { return toPrettyContainerString(indent, std::string("[udta] User Data Box")); } - STSS::STSS(){ + STSS::STSS(char v, uint32_t f){ memcpy(data + 4, "stss", 4); + setVersion(v); + setFlags(f); + setEntryCount(0); } void STSS::setEntryCount(uint32_t newVal){ diff --git a/lib/mp4.h b/lib/mp4.h index c69150fb..4ea91e0b 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -58,6 +58,7 @@ namespace MP4 { class fullBox: public Box{ public: + fullBox(); void setVersion(char newVersion); char getVersion(); void setFlags(uint32_t newFlags); @@ -67,7 +68,7 @@ namespace MP4 { class containerBox: public Box{ public: - //containerBox(); + containerBox(); uint32_t getContentCount(); void setContent(Box & newContent, uint32_t no); Box & getContent(uint32_t no); @@ -520,7 +521,7 @@ namespace MP4 { class DREF: public fullBox{ public: - DREF(); + DREF(char v = 1, uint32_t = 0); uint32_t getEntryCount(); void setDataEntry(fullBox & newDataEntry, size_t index); Box & getDataEntry(size_t index); @@ -529,7 +530,7 @@ namespace MP4 { class MVHD: public fullBox{ public: - MVHD(); + MVHD(char v = 1, uint32_t f = 0); void setCreationTime(uint64_t newCreationTime); uint64_t getCreationTime(); void setModificationTime(uint64_t newModificationTime); @@ -580,7 +581,7 @@ namespace MP4 { class TKHD: public fullBox{ public: - TKHD(); + TKHD(char v = 1, uint32_t f = 0); void setCreationTime(uint64_t newCreationTime); uint64_t getCreationTime(); void setModificationTime(uint64_t newModificationTime); @@ -611,7 +612,7 @@ namespace MP4 { class MDHD: public fullBox{ public: - MDHD(); + MDHD(char v = 1, uint32_t f = 0); void setCreationTime(uint64_t newCreationTime); uint64_t getCreationTime(); void setModificationTime(uint64_t newModificationTime); @@ -664,7 +665,7 @@ namespace MP4 { class STSC: public fullBox{ public: - STSC(); + STSC(char v = 1, uint32_t f = 0); void setEntryCount(uint32_t newEntryCount); uint32_t getEntryCount(); void setSTSCEntry(STSCEntry newSTSCEntry, uint32_t no); @@ -674,7 +675,7 @@ namespace MP4 { class STCO: public fullBox{ public: - STCO(); + STCO(char v = 1, uint32_t f = 0); void setEntryCount(uint32_t newEntryCount); uint32_t getEntryCount(); void setChunkOffset(uint32_t newChunkOffset, uint32_t no); @@ -786,7 +787,7 @@ namespace MP4 { class STSD: public fullBox{ public: - STSD(); + STSD(char v = 1, uint32_t f = 0); void setEntryCount (uint32_t newEntryCount); uint32_t getEntryCount(); void setEntry(Box & newContent, uint32_t no); @@ -808,7 +809,7 @@ namespace MP4 { class STSS: public fullBox{ public: - STSS(); + STSS(char v = 1, uint32_t f = 0); void setEntryCount(uint32_t newVal); uint32_t getEntryCount(); void setSampleNumber(uint32_t newVal, uint32_t index); From 687f477cffd98c58270f9ec345f694f24f9ad6d3 Mon Sep 17 00:00:00 2001 From: Oswald de Bruin Date: Thu, 23 May 2013 16:15:17 +0200 Subject: [PATCH 489/788] Fixed default values and errors --- lib/mp4.cpp | 17 ++++++++++++----- lib/mp4.h | 4 ++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index e0d4cb8b..20e7b7dc 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -3542,8 +3542,10 @@ namespace MP4 { return r.str(); } - STTS::STTS(){ + STTS::STTS(char v, uint32_t f){ memcpy(data + 4, "stts", 4); + setVersion(v); + setFlags(f); setEntryCount(0); } @@ -3557,6 +3559,7 @@ namespace MP4 { void STTS::setSTTSEntry(STTSEntry newSTTSEntry, uint32_t no){ if(no + 1 > getEntryCount()){ + setEntryCount(no + 1); for (int i = getEntryCount(); i < no; i++){ setInt64(0, 8 + (i * 8));//filling up undefined entries of 64 bits } @@ -3738,8 +3741,11 @@ namespace MP4 { return r.str(); } - STSZ::STSZ(){ + STSZ::STSZ(char v, uint32_t f){ memcpy(data + 4, "stsz", 4); + setVersion(v); + setFlags(f); + setSampleCount(0); } void STSZ::setSampleSize(uint32_t newSampleSize){ @@ -3761,11 +3767,12 @@ namespace MP4 { void STSZ::setEntrySize(uint32_t newEntrySize, uint32_t no){ if (no + 1 > getSampleCount()){ + setSampleCount(no + 1); for (int i = getSampleCount(); i < no; i++){ setInt32(0, 12 + i * 4);//filling undefined entries } } - setInt32(newEntrySize, 12 + no * 4); + setInt32(newEntrySize, 12 + no * 4); } uint32_t STSZ::getEntrySize(uint32_t no){ @@ -4121,8 +4128,8 @@ namespace MP4 { } } setBox(newContent, tempLoc); - if (getEntryCount() < no){ - setEntryCount(no); + if (getEntryCount() < no+1){ + setEntryCount(no+1); } } diff --git a/lib/mp4.h b/lib/mp4.h index 4ea91e0b..3bd29efa 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -634,7 +634,7 @@ namespace MP4 { class STTS: public fullBox{ public: - STTS(); + STTS(char v = 1, uint32_t f = 0); void setEntryCount(uint32_t newEntryCount); uint32_t getEntryCount(); void setSTTSEntry(STTSEntry newSTTSEntry, uint32_t no); @@ -685,7 +685,7 @@ namespace MP4 { class STSZ: public fullBox{ public: - STSZ(); + STSZ(char v = 1, uint32_t f = 0); void setSampleSize(uint32_t newSampleSize); uint32_t getSampleSize(); void setSampleCount(uint32_t newSampleCount); From 82e6e07a812ebe2ca65c3ee62c13e56b1be6e4d4 Mon Sep 17 00:00:00 2001 From: Oswald de Bruin Date: Mon, 3 Jun 2013 10:32:44 +0200 Subject: [PATCH 490/788] Last MP4 edits --- lib/mp4.cpp | 43 ++++++++++++++++++++++++++++++++++++++++++- lib/mp4.h | 14 ++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 20e7b7dc..821351fb 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -247,9 +247,15 @@ namespace MP4 { case 0x6D703461: return ((MP4A*)this)->toPrettyString(indent); break; + case 0x61616320: + return ((AAC*)this)->toPrettyString(indent); + break; case 0x61766331: return ((AVC1*)this)->toPrettyString(indent); break; + case 0x68323634: + return ((H264*)this)->toPrettyString(indent); + break; case 0x65647473: return ((EDTS*)this)->toPrettyString(indent); break; @@ -3661,6 +3667,7 @@ namespace MP4 { void STSC::setSTSCEntry(STSCEntry newSTSCEntry, uint32_t no){ if(no + 1 > getEntryCount()){ + setEntryCount(no+1); for (int i = getEntryCount(); i < no; i++){ setInt64(0, 8 + (i * 12));//filling up undefined entries of 64 bits setInt32(0, 8 + (i * 12) + 8); @@ -3716,6 +3723,7 @@ namespace MP4 { void STCO::setChunkOffset(uint32_t newChunkOffset, uint32_t no){ if (no + 1 > getEntryCount()){ + setEntryCount(no+1); for (int i = getEntryCount(); i < no; i++){ setInt32(0, 8 + i * 4);//filling undefined entries } @@ -3924,6 +3932,15 @@ namespace MP4 { VisualSampleEntry::VisualSampleEntry(){ memcpy(data + 4, "erro", 4); + setHorizResolution(0x00480000); + setVertResolution(0x00480000); + setFrameCount(1); + setCompressorName(""); + setDepth(0x0018); + } + + void VisualSampleEntry::setCodec(char* newCodec){ + memcpy(data + 4, newCodec, 4); } void VisualSampleEntry::setWidth(uint16_t newWidth){ @@ -3967,6 +3984,7 @@ namespace MP4 { } void VisualSampleEntry::setCompressorName(std::string newCompressorName){ + newCompressorName.resize(32, ' '); setString(newCompressorName,42); } @@ -4036,6 +4054,13 @@ namespace MP4 { AudioSampleEntry::AudioSampleEntry(){ memcpy(data + 4, "erro", 4); + setChannelCount(2); + setSampleSize(16); + setSampleRate(44100); + } + + void AudioSampleEntry::setCodec(char* newCodec){ + memcpy(data + 4, newCodec, 4); } void AudioSampleEntry::setChannelCount(uint16_t newChannelCount){ @@ -4086,7 +4111,15 @@ namespace MP4 { } std::string MP4A::toPrettyString(uint32_t indent){ - return toPrettyAudioString(indent, "[mp4a] MPEG 4 Audio"); + return toPrettyAudioString(indent, "[mp4a] MPEG-4 Audio"); + } + + AAC::AAC(){ + memcpy(data + 4, "aac ", 4); + } + + std::string AAC::toPrettyString(uint32_t indent){ + return toPrettyAudioString(indent, "[aac ] Advanced Audio Codec"); } AVC1::AVC1(){ @@ -4097,6 +4130,14 @@ namespace MP4 { return toPrettyVisualString(indent, "[avc1] Advanced Video Codec 1"); } + H264::H264(){ + memcpy(data + 4, "h264", 4); + } + + std::string H264::toPrettyString(uint32_t indent){ + return toPrettyVisualString(indent, "[h264] H.264/MPEG-4 AVC"); + } + STSD::STSD(char v, uint32_t f){ memcpy(data + 4, "stsd", 4); setVersion(v); diff --git a/lib/mp4.h b/lib/mp4.h index 3bd29efa..d819e4a6 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -739,6 +739,7 @@ namespace MP4 { ///\todo set default values public: VisualSampleEntry(); + void setCodec(char* newCodec); void setWidth(uint16_t newWidth); uint16_t getWidth(); void setHeight(uint16_t newHeight); @@ -762,6 +763,7 @@ namespace MP4 { public: ///\todo set default values AudioSampleEntry(); + void setCodec(char* newCodec); void setChannelCount(uint16_t newChannelCount); uint16_t getChannelCount(); void setSampleSize(uint16_t newSampleSize); @@ -779,11 +781,23 @@ namespace MP4 { 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: From ae6270faffa675af59423a43aee922825c7b76ca Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Tue, 11 Jun 2013 11:50:58 +0200 Subject: [PATCH 491/788] Prettyfied printing for HDLR box --- lib/mp4.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 821351fb..f83defa0 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -2573,7 +2573,9 @@ namespace MP4 { std::stringstream r; r << std::string(indent, ' ') << "[hdlr] Handler Reference (" << boxedSize() << ")" << std::endl; r << std::string(indent + 1, ' ') << "PreDefined: " << getPreDefined() << std::endl; - r << std::string(indent + 1, ' ') << "HandlerType: " << getHandlerType() << std::endl; + r << std::string(indent + 1, ' ') << "HandlerType: " << + (char)((getHandlerType() & 0xFF000000) >> 24) << (char)((getHandlerType() & 0x00FF0000) >> 16) << + (char)((getHandlerType() & 0x0000FF00) >> 8) << (char)(getHandlerType() & 0x000000FF) << std::endl; r << std::string(indent + 1, ' ') << "Name: " << getName() << std::endl; return r.str(); } From c8782b4d24bf731c64860728875ed6f79924ec59 Mon Sep 17 00:00:00 2001 From: Oswald de Bruin Date: Wed, 26 Jun 2013 16:13:14 +0200 Subject: [PATCH 492/788] Merge branch 'Oswald_MP4_EDITS' of github.com:DDVTECH/libmist into Oswald_MP4_EDITS Conflicts: lib/mp4.cpp No idea what happened --- lib/mp4.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index f83defa0..b50cd7eb 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -2863,7 +2863,7 @@ namespace MP4 { setMatrix(0,7);*/ //fills automatically with zero's setMatrix(0x40000000,8); - setTrackID(0); + setTrackID(1); } void MVHD::setCreationTime(uint64_t newCreationTime){ From 7f47a746fb96ef80b11bb9ef7a4f7d2d5eeaa584 Mon Sep 17 00:00:00 2001 From: Oswald Auguste de Bruin Date: Tue, 2 Jul 2013 14:37:16 +0200 Subject: [PATCH 493/788] MP4 lib added default values --- lib/mp4.cpp | 39 ++++++++++++++++++++------------------- lib/mp4.h | 1 + 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index b50cd7eb..12123549 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -2583,6 +2583,10 @@ namespace MP4 { //Note: next 4 headers inherit from fullBox, start at byte 4. VMHD::VMHD(){ memcpy(data + 4, "vmhd", 4); + setGraphicsMode(0); + setOpColor(0,0); + setOpColor(0,1); + setOpColor(0,2); } void VMHD::setGraphicsMode(uint16_t newGraphicsMode){ @@ -2624,6 +2628,7 @@ namespace MP4 { SMHD::SMHD(){ memcpy(data + 4, "smhd", 4); + setBalance(0); } void SMHD::setBalance(int16_t newBalance){ @@ -3765,7 +3770,6 @@ namespace MP4 { uint32_t STSZ::getSampleSize(){ return getInt32(4); } - void STSZ::setSampleCount(uint32_t newSampleCount){ setInt32(newSampleCount, 8); @@ -3824,6 +3828,10 @@ namespace MP4 { CLAP::CLAP(){ memcpy(data + 4, "clap", 4); + setHorizOffN(0); + setHorizOffD(0); + setVertOffN(0); + setVertOffD(0); } void CLAP::setCleanApertureWidthN(uint32_t newVal){ @@ -4002,16 +4010,16 @@ namespace MP4 { getInt16(74); } + void VisualSampleEntry::setCLAP(Box& clap){ + setBox(clap,78); + } + Box & VisualSampleEntry::getCLAP(){ static Box ret = Box((char*)"\000\000\000\010erro", false); if(payloadSize() <84){//if the EntryBox is not big enough to hold a CLAP/PASP return ret; } - if (getBox(76).isType("clap")){ - return getBox(76); - }else{ - return ret; - } + return getBox(78); } Box & VisualSampleEntry::getPASP(){ @@ -4019,19 +4027,12 @@ namespace MP4 { if(payloadSize() <84){//if the EntryBox is not big enough to hold a CLAP/PASP return ret; } - if (getBox(76).isType("pasp")){ - return getBox(76); + if (payloadSize() < 78 + getBoxLen(78) + 8){ + return ret; }else{ - if (payloadSize() < 76 + getBoxLen(76) + 8){ - return ret; - }else{ - if (getBox(76+getBoxLen(76)).isType("pasp")){ - return getBox(76+getBoxLen(76)); - }else{ - return ret; - } - } + return getBox(78+getBoxLen(78)); } + } std::string VisualSampleEntry::toPrettyVisualString(uint32_t indent, std::string name){ @@ -4045,10 +4046,10 @@ namespace MP4 { r << std::string(indent + 1, ' ') << "FrameCount: " << getFrameCount() << std::endl; r << std::string(indent + 1, ' ') << "CompressorName: " << getCompressorName() << std::endl; r << std::string(indent + 1, ' ') << "Depth: " << getDepth() << std::endl; - if (getCLAP().isType("clap")){ + if (!getCLAP().isType("erro")){ r << getCLAP().toPrettyString(indent+1); } - if (getPASP().isType("pasp")){ + if (!getPASP().isType("erro")){ r << getPASP().toPrettyString(indent+1); } return r.str(); diff --git a/lib/mp4.h b/lib/mp4.h index d819e4a6..cc445548 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -755,6 +755,7 @@ namespace MP4 { 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 = ""); }; From 1c796c8f303032a88e58da239364086b9a1e3c0e Mon Sep 17 00:00:00 2001 From: Oswald Auguste de Bruin Date: Wed, 3 Jul 2013 16:17:32 +0200 Subject: [PATCH 494/788] started on ESDS box --- lib/mp4.cpp | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++- lib/mp4.h | 44 ++++++++++++++- 2 files changed, 196 insertions(+), 2 deletions(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 12123549..8205e162 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -2278,7 +2278,159 @@ namespace MP4 { } memcpy((char*)payload(), (char*)newPayload.c_str(), newPayload.size()); } - + + //fullbox, start at 4 + ESDS::ESDS(){ + memcpy(data + 4, "esds", 4); + setESDescriptorType(0x03); + setExtendedDescriptorType(0x8000FE); + + } + + char ESDS::getESDescriptorType(){ + return getInt8(4); + } + + void ESDS::setESDescriptorType(char newVal){ + setInt8(newVal,4); + } + + uint32_t ESDS::getExtendedDescriptorType(){ + return getInt24(5); + } + //3 bytes + void ESDS::setExtendedDescriptorType(uint32_t newVal){ + setInt24(newVal,5); + } + //3 bytes + char ESDS::getESDescriptorTypeLength(){ + return getInt8(8); + } + + void ESDS::setESDescriptorTypeLength(char newVal){ + setInt8(newVal,8); + } + + char ESDS::getByteObjectTypeID(){ + return getInt8(9); + } + + void ESDS::setByteObjectTypeID(char newVal){ + setInt8(newVal,9); + } + + char ESDS::getStreamType(){ + return getInt8(10) >> 2; + } + //6 bits + void ESDS::setStreamType(char newVal){ + setInt8(newVal<<2 + ((uint8_t)getUpstreamFlag() << 1) + (uint8_t)getReservedFlag() , 10); + } + //6 bits + bool ESDS::getUpstreamFlag(){ + return (((getInt8(10) >> 1) & 0x01) == 1); + } + + void ESDS::setUpstreamFlag(bool newVal){ + setInt8(getStreamType()<<2 + ((uint8_t)newVal << 1) + (uint8_t)getReservedFlag() , 10); + } + + bool ESDS::getReservedFlag(){ + return ((getInt8(10) & 0x01) == 1); + } + + void ESDS::setReservedFlag(bool newVal){ + setInt8((getStreamType()<<2) + ((uint8_t)getUpstreamFlag() << 1) + (uint8_t)newVal , 10); + } + + uint32_t ESDS::getBufferSize(){ + return getInt24(11); + } + //3 bytes + void ESDS::setBufferSize(uint32_t newVal){ + setInt24(newVal,11); + } + //3 bytes + uint32_t ESDS::getMaximumBitRate(){ + return getInt32(14); + } + + void ESDS::getMaximumBitRate(uint32_t newVal){ + setInt32(newVal,14); + } + + uint32_t ESDS::getAverageBitRate(){ + return getInt32(18); + } + + void ESDS::setAverageBitRate(uint32_t newVal){ + setInt32(newVal,18); + } + + char ESDS::getDescriptorTypeTag(){ + return getInt8(22); + } + + void ESDS::setDescriptorTypeTag(char newVal){ + setInt8(newVal,22); + } + + uint32_t ESDS::getExtendedDescriptorTypeTag(){ + return getInt24(23); + } + //3 bytes + void ESDS::setExtendedDescriptorTypeTag(uint32_t newVal){ + setInt24(newVal, 23); + } + //3 bytes + char ESDS::getConfigDescriptorTypeLength(){ + return getInt8(26); + } + + void ESDS::setConfigDescriptorTypeLength(char newVal){ + setInt8(newVal, 26); + } + + uint16_t ESDS::getESHeaderStartCodes(){ + return getInt16(27); + } + + void ESDS::setESHeaderStartCodes(uint16_t newVal){ + setInt16(newVal,27); + } + + char ESDS::getSLConfigDescriptorTypeTag(){ + return getInt16(29); + } + + void ESDS::setSLConfigDescriptorTypeTag(char newVal){ + setInt16(newVal, 29); + } + + uint32_t ESDS::getSLConfigExtendedDescriptorTypeTag(){ + return getInt24(31); + } + //3 bytes + void ESDS::setSLConfigExtendedDescriptorTypeTag(uint32_t newVal){ + setInt24(newVal, 31); + } + //3 bytes + char ESDS::getSLDescriptorTypeLength(){ + return getInt8(34); + } + + void ESDS::setSLDescriptorTypeLength(char newVal){ + setInt8(newVal, 34); + } + + char ESDS::getSLValue(){ + return getInt8(35); + } + + void ESDS::setSLValue(char newVal){ + setInt8(newVal, 35); + } + SDTP::SDTP(){ memcpy(data + 4, "sdtp", 4); } diff --git a/lib/mp4.h b/lib/mp4.h index cc445548..3cc8738e 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -343,7 +343,49 @@ namespace MP4 { void setPayload(std::string newPayload); std::string toPrettyString(uint32_t indent = 0); }; - + + ///\todo : ESDS is filthy implemented, clean up when optimising + class ESDS: public fullBox{ + public: + ESDS(); + char getESDescriptorType(); + void setESDescriptorType(char newVal); + uint32_t getExtendedDescriptorType();//3 bytes + void setExtendedDescriptorType(uint32_t newVal);//3 bytes + char getESDescriptorTypeLength(); + void setESDescriptorTypeLength(char newVal); + char getByteObjectTypeID(); + void setByteObjectTypeID(char newVal); + char getStreamType();//6 bits + void setStreamType(char newVal);//6 bits + bool getUpstreamFlag(); + void setUpstreamFlag(bool newVal); + bool getReservedFlag(); + void setReservedFlag(bool newVal); + uint32_t getBufferSize();//3 bytes + void setBufferSize(uint32_t newVal);//3 bytes + uint32_t getMaximumBitRate(); + void getMaximumBitRate(uint32_t newVal); + uint32_t getAverageBitRate(); + void setAverageBitRate(uint32_t newVal); + char getDescriptorTypeTag(); + void setDescriptorTypeTag(char newVal); + uint32_t getExtendedDescriptorTypeTag();//3 bytes + void setExtendedDescriptorTypeTag(uint32_t newVal);//3 bytes + char getConfigDescriptorTypeLength(); + void setConfigDescriptorTypeLength(char newVal); + uint16_t getESHeaderStartCodes(); + void setESHeaderStartCodes(uint16_t newVal); + char getSLConfigDescriptorTypeTag(); + void setSLConfigDescriptorTypeTag(char newVal); + uint32_t getSLConfigExtendedDescriptorTypeTag();//3 bytes + void setSLConfigExtendedDescriptorTypeTag(uint32_t newVal);//3 bytes + char getSLDescriptorTypeLength(); + void setSLDescriptorTypeLength(char newVal); + char getSLValue(); + void setSLValue(char newVal); + }; + class SDTP: public Box{ public: SDTP(); From 671a452578c5bf54bc4ec38860d4b0447ad7536f Mon Sep 17 00:00:00 2001 From: Oswald Auguste de Bruin Date: Mon, 8 Jul 2013 12:01:59 +0200 Subject: [PATCH 495/788] Latest MP4 commit --- lib/mp4.cpp | 239 ++++++++++++++++++++++++++++++++++++++-------------- lib/mp4.h | 34 ++++++-- 2 files changed, 203 insertions(+), 70 deletions(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 8205e162..f4352a40 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -268,6 +268,9 @@ namespace MP4 { case 0x656C7374: return ((ELST*)this)->toPrettyString(indent); break; + case 0x65736473: + return ((ESDS*)this)->toPrettyString(indent); + break; case 0x75647461: return ((UDTA*)this)->toPrettyString(indent); break; @@ -2283,8 +2286,16 @@ namespace MP4 { ESDS::ESDS(){ memcpy(data + 4, "esds", 4); setESDescriptorType(0x03); - setExtendedDescriptorType(0x8000FE); - + setExtendedESDescriptorType(0x808080); + setStreamPriority(16); + setDecoderConfigDescriptorTag(0x04); + setExtendedDecoderConfigDescriptorTag(0x808080); + setReservedFlag(true); + setDecoderDescriptorTypeTag(0x05); + setExtendedDecoderDescriptorTypeTag(0x808080); + setSLConfigDescriptorTypeTag(0x06); + setSLConfigExtendedDescriptorTypeTag(0x808010); + setSLValue(0x02); } char ESDS::getESDescriptorType(){ @@ -2295,11 +2306,11 @@ namespace MP4 { setInt8(newVal,4); } - uint32_t ESDS::getExtendedDescriptorType(){ + uint32_t ESDS::getExtendedESDescriptorType(){ return getInt24(5); } //3 bytes - void ESDS::setExtendedDescriptorType(uint32_t newVal){ + void ESDS::setExtendedESDescriptorType(uint32_t newVal){ setInt24(newVal,5); } //3 bytes @@ -2310,125 +2321,220 @@ namespace MP4 { void ESDS::setESDescriptorTypeLength(char newVal){ setInt8(newVal,8); } + //ESID 2 bytes + uint16_t ESDS::getESID(){ + return getInt16(9); + } + void ESDS::setESID(uint16_t newVal){ + setInt16(newVal, 9); + } + + //stream priority 1 byte + char ESDS::getStreamPriority(){ + return getInt8(11); + } + + void ESDS::setStreamPriority(char newVal){ + setInt8(newVal, 11); + } + + //decoder config descriptor tag 1byte + char ESDS::getDecoderConfigDescriptorTag(){ + return getInt8(12); + } + + void ESDS::setDecoderConfigDescriptorTag(char newVal){ + setInt8(newVal, 12); + } + + //extended decoder config descriptor tag 3 bytes + uint32_t ESDS::getExtendedDecoderConfigDescriptorTag(){ + return getInt24(13); + } + //3 bytes + void ESDS::setExtendedDecoderConfigDescriptorTag(uint32_t newVal){ + setInt24(newVal, 13); + } + //3 bytes + //decoder config descriptor type length + char ESDS::getDecoderConfigDescriptorTypeLength(){ + return getInt8(16); + } + + void ESDS::setDecoderConfigDescriptorTypeLength(char newVal){ + setInt8(newVal, 16); + } + //Note: tel 8 bytes op bij de volgende functies char ESDS::getByteObjectTypeID(){ - return getInt8(9); + return getInt8(17); } void ESDS::setByteObjectTypeID(char newVal){ - setInt8(newVal,9); + setInt8(newVal,17); } char ESDS::getStreamType(){ - return getInt8(10) >> 2; + return getInt8(18) >> 2; } //6 bits void ESDS::setStreamType(char newVal){ - setInt8(newVal<<2 + ((uint8_t)getUpstreamFlag() << 1) + (uint8_t)getReservedFlag() , 10); + setInt8(newVal<<2 + ((uint8_t)getUpstreamFlag() << 1) + (uint8_t)getReservedFlag() , 18); } //6 bits bool ESDS::getUpstreamFlag(){ - return (((getInt8(10) >> 1) & 0x01) == 1); + return (((getInt8(18) >> 1) & 0x01) == 1); } void ESDS::setUpstreamFlag(bool newVal){ - setInt8(getStreamType()<<2 + ((uint8_t)newVal << 1) + (uint8_t)getReservedFlag() , 10); + setInt8(getStreamType()<<2 + ((uint8_t)newVal << 1) + (uint8_t)getReservedFlag() , 18); } bool ESDS::getReservedFlag(){ - return ((getInt8(10) & 0x01) == 1); + return ((getInt8(18) & 0x01) == 1); } void ESDS::setReservedFlag(bool newVal){ - setInt8((getStreamType()<<2) + ((uint8_t)getUpstreamFlag() << 1) + (uint8_t)newVal , 10); + setInt8((getStreamType()<<2) + ((uint8_t)getUpstreamFlag() << 1) + (uint8_t)newVal , 18); } uint32_t ESDS::getBufferSize(){ - return getInt24(11); + return getInt24(19); } //3 bytes void ESDS::setBufferSize(uint32_t newVal){ - setInt24(newVal,11); + setInt24(newVal,19); } //3 bytes uint32_t ESDS::getMaximumBitRate(){ - return getInt32(14); + return getInt32(22); } - void ESDS::getMaximumBitRate(uint32_t newVal){ - setInt32(newVal,14); + void ESDS::setMaximumBitRate(uint32_t newVal){ + setInt32(newVal,22); } uint32_t ESDS::getAverageBitRate(){ - return getInt32(18); + return getInt32(26); } void ESDS::setAverageBitRate(uint32_t newVal){ - setInt32(newVal,18); + setInt32(newVal,26); } - char ESDS::getDescriptorTypeTag(){ - return getInt8(22); + char ESDS::getDecoderDescriptorTypeTag(){ + return getInt8(30); } - void ESDS::setDescriptorTypeTag(char newVal){ - setInt8(newVal,22); + void ESDS::setDecoderDescriptorTypeTag(char newVal){ + setInt8(newVal,30); } - uint32_t ESDS::getExtendedDescriptorTypeTag(){ - return getInt24(23); + uint32_t ESDS::getExtendedDecoderDescriptorTypeTag(){ + return getInt24(31); } //3 bytes - void ESDS::setExtendedDescriptorTypeTag(uint32_t newVal){ - setInt24(newVal, 23); - } - //3 bytes - char ESDS::getConfigDescriptorTypeLength(){ - return getInt8(26); - } - - void ESDS::setConfigDescriptorTypeLength(char newVal){ - setInt8(newVal, 26); - } - - uint16_t ESDS::getESHeaderStartCodes(){ - return getInt16(27); - } - - void ESDS::setESHeaderStartCodes(uint16_t newVal){ - setInt16(newVal,27); - } - - char ESDS::getSLConfigDescriptorTypeTag(){ - return getInt16(29); - } - - void ESDS::setSLConfigDescriptorTypeTag(char newVal){ - setInt16(newVal, 29); - } - - uint32_t ESDS::getSLConfigExtendedDescriptorTypeTag(){ - return getInt24(31); - } - //3 bytes - void ESDS::setSLConfigExtendedDescriptorTypeTag(uint32_t newVal){ + void ESDS::setExtendedDecoderDescriptorTypeTag(uint32_t newVal){ setInt24(newVal, 31); } //3 bytes - char ESDS::getSLDescriptorTypeLength(){ + char ESDS::getConfigDescriptorTypeLength(){ return getInt8(34); } - void ESDS::setSLDescriptorTypeLength(char newVal){ + void ESDS::setConfigDescriptorTypeLength(char newVal){ setInt8(newVal, 34); } + uint16_t ESDS::getESHeaderStartCodes(){ + return getInt16(35); + } + + void ESDS::setESHeaderStartCodes(uint16_t newVal){ + setInt16(newVal,35); + } + + char ESDS::getSLConfigDescriptorTypeTag(){ + return getInt16(37); + } + + void ESDS::setSLConfigDescriptorTypeTag(char newVal){ + setInt16(newVal, 37); + } + + uint32_t ESDS::getSLConfigExtendedDescriptorTypeTag(){ + return getInt24(39); + } + //3 bytes + void ESDS::setSLConfigExtendedDescriptorTypeTag(uint32_t newVal){ + setInt24(newVal, 39); + } + //3 bytes + char ESDS::getSLDescriptorTypeLength(){ + return getInt8(42); + } + + void ESDS::setSLDescriptorTypeLength(char newVal){ + setInt8(newVal, 42); + } + char ESDS::getSLValue(){ - return getInt8(35); + return getInt8(43); } void ESDS::setSLValue(char newVal){ - setInt8(newVal, 35); + setInt8(newVal, 43); + } + + std::string ESDS::toPrettyString(uint32_t indent){ + std::stringstream r; + r << std::string(indent, ' ') << "[esds] ES Descriptor Box (" << boxedSize() << ")" << std::endl; + r << fullBox::toPrettyString(indent); + //char getESDescriptorType(); + r << std::string(indent + 1, ' ') << "ESDescriptorType: 0x" << std::hex << getESDescriptorType() << std::dec << std::endl; + //uint32_t getExtendedESDescriptorType();//3 bytes + r << std::string(indent + 1, ' ') << "ExtendedESDescriptorType: 0x" << std::hex << getExtendedESDescriptorType() << std::dec << std::endl; + //char getESDescriptorTypeLength(); + r << std::string(indent + 1, ' ') << "ESDescriptorTypeLength:" << getESDescriptorTypeLength() << std::endl; + //uint16_t getESID(); + r << std::string(indent + 1, ' ') << "ESID: 0x" << std::hex << getESID() << std::dec << std::endl; + //char getStreamPriority(); + r << std::string(indent + 1, ' ') << "StreamPriority:" << getStreamPriority() << std::endl; + //char getDecoderConfigDescriptorTag(); + r << std::string(indent + 1, ' ') << "DecoderConfigDescriptorTag: 0x" << std::hex << getDecoderConfigDescriptorTag() << std::dec << std::endl; + //uint32_t getExtendedDecoderConfigDescriptorTag(); + r << std::string(indent + 1, ' ') << "ExtendedDecoderConfigDescriptorTag: 0x" << std::hex << getExtendedDecoderConfigDescriptorTag() << std::dec << std::endl; + //char getDecoderConfigDescriptorTypeLength(); + r << std::string(indent + 1, ' ') << "DecoderConfigDescriptorTag: 0x" << std::hex << getDecoderConfigDescriptorTag() << std::dec << std::endl; + //char getByteObjectTypeID(); + r << std::string(indent + 1, ' ') << "ByteObjectTypeID: 0x" << std::hex << getByteObjectTypeID() << std::dec << std::endl; + //char getStreamType();//6 bits + r << std::string(indent + 1, ' ') << "StreamType: 0x" << std::hex << getStreamType() << std::dec << std::endl; + //bool getUpstreamFlag(); + r << std::string(indent + 1, ' ') << "UpstreamFlag: 0x" << getUpstreamFlag() << std::endl; + //uint32_t getBufferSize();//3 bytes + r << std::string(indent + 1, ' ') << "BufferSize: 0x" << std::hex << getBufferSize() << std::dec << std::endl; + //uint32_t getMaximumBitRate(); + r << std::string(indent + 1, ' ') << "MaximumBitRate: 0x" << std::hex << getMaximumBitRate() << std::dec << std::endl; + //uint32_t getAverageBitRate(); + r << std::string(indent + 1, ' ') << "AverageBitRate: 0x" << std::hex << getAverageBitRate() << std::dec << std::endl; + //char getDecoderDescriptorTypeTag(); + r << std::string(indent + 1, ' ') << "DecoderDescriptorTypeTag: 0x" << std::hex << getDecoderDescriptorTypeTag() << std::dec << std::endl; + //uint32_t getExtendedDecoderDescriptorTypeTag();//3 bytes + r << std::string(indent + 1, ' ') << "ExtendedDecoderDescriptorTypeTag: 0x" << std::hex << getExtendedDecoderDescriptorTypeTag() << std::dec << std::endl; + //char getConfigDescriptorTypeLength(); + r << std::string(indent + 1, ' ') << "ConfigDescriptorTypeLength: 0x" << std::hex << getConfigDescriptorTypeLength() << std::dec << std::endl; + //uint16_t getESHeaderStartCodes(); + r << std::string(indent + 1, ' ') << "ESHeaderStartCodes: 0x" << std::hex << getESHeaderStartCodes() << std::dec << std::endl; + //char getSLConfigDescriptorTypeTag(); + r << std::string(indent + 1, ' ') << "SLConfigDescriptorTypeTag: 0x" << std::hex << getSLConfigDescriptorTypeTag() << std::dec << std::endl; + //uint32_t getSLConfigExtendedDescriptorTypeTag();//3 bytes + r << std::string(indent + 1, ' ') << "SLConfigExtendedDescriptorTypeTag: 0x" << std::hex << getSLConfigExtendedDescriptorTypeTag() << std::dec << std::endl; + //char getSLDescriptorTypeLength(); + r << std::string(indent + 1, ' ') << "SLDescriptorTypeLength: 0x" << std::hex << getSLDescriptorTypeLength() << std::dec << std::endl; + //char getSLValue(); + r << std::string(indent + 1, ' ') << "SLValue: 0x" << std::hex << getSLValue() << std::dec << std::endl; + return r.str(); } SDTP::SDTP(){ @@ -4250,6 +4356,14 @@ namespace MP4 { return getInt32(24) >> 16; } + void AudioSampleEntry::setCodecBox(Box& newBox){ + setBox(newBox, 28); + } + + Box & AudioSampleEntry::getCodecBox(){ + return getBox(28); + } + std::string AudioSampleEntry::toPrettyAudioString(uint32_t indent, std::string name){ std::stringstream r; r << std::string(indent, ' ') << name << " (" << boxedSize() << ")" << std::endl; @@ -4258,6 +4372,7 @@ namespace MP4 { r << std::string(indent + 1, ' ') << "SampleSize: " << getSampleSize() << std::endl; r << std::string(indent + 1, ' ') << "PreDefined: " << getPreDefined() << std::endl; r << std::string(indent + 1, ' ') << "SampleRate: " << getSampleRate() << std::endl; + r << getCodecBox().toPrettyString(indent + 1) << std::endl; return r.str(); } diff --git a/lib/mp4.h b/lib/mp4.h index 3cc8738e..f2a0675d 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -350,10 +350,25 @@ namespace MP4 { ESDS(); char getESDescriptorType(); void setESDescriptorType(char newVal); - uint32_t getExtendedDescriptorType();//3 bytes - void setExtendedDescriptorType(uint32_t newVal);//3 bytes + uint32_t getExtendedESDescriptorType();//3 bytes + void setExtendedESDescriptorType(uint32_t newVal);//3 bytes char getESDescriptorTypeLength(); void setESDescriptorTypeLength(char newVal); + //ESID 2 bytes + uint16_t getESID(); + void setESID(uint16_t newVal); + //stream priority 1 byte + char getStreamPriority(); + void setStreamPriority(char newVal); + //decoder config descriptor tag 1byte + char getDecoderConfigDescriptorTag(); + void setDecoderConfigDescriptorTag(char newVal); + //extended decoder config descriptor tag 3 bytes + uint32_t getExtendedDecoderConfigDescriptorTag(); + void setExtendedDecoderConfigDescriptorTag(uint32_t newVal);//3 bytes + //decoder config descriptor type length + char getDecoderConfigDescriptorTypeLength(); + void setDecoderConfigDescriptorTypeLength(char newVal); char getByteObjectTypeID(); void setByteObjectTypeID(char newVal); char getStreamType();//6 bits @@ -365,13 +380,13 @@ namespace MP4 { uint32_t getBufferSize();//3 bytes void setBufferSize(uint32_t newVal);//3 bytes uint32_t getMaximumBitRate(); - void getMaximumBitRate(uint32_t newVal); + void setMaximumBitRate(uint32_t newVal); uint32_t getAverageBitRate(); void setAverageBitRate(uint32_t newVal); - char getDescriptorTypeTag(); - void setDescriptorTypeTag(char newVal); - uint32_t getExtendedDescriptorTypeTag();//3 bytes - void setExtendedDescriptorTypeTag(uint32_t newVal);//3 bytes + char getDecoderDescriptorTypeTag(); + void setDecoderDescriptorTypeTag(char newVal); + uint32_t getExtendedDecoderDescriptorTypeTag();//3 bytes + void setExtendedDecoderDescriptorTypeTag(uint32_t newVal);//3 bytes char getConfigDescriptorTypeLength(); void setConfigDescriptorTypeLength(char newVal); uint16_t getESHeaderStartCodes(); @@ -384,6 +399,7 @@ namespace MP4 { void setSLDescriptorTypeLength(char newVal); char getSLValue(); void setSLValue(char newVal); + std::string toPrettyString(uint32_t indent = 0); }; class SDTP: public Box{ @@ -814,7 +830,9 @@ namespace MP4 { void setPreDefined(uint16_t newPreDefined); uint16_t getPreDefined(); void setSampleRate(uint32_t newSampleRate); - uint32_t getSampleRate(); + uint32_t getSampleRate(); + void setCodecBox(Box& newBox); + Box & getCodecBox(); std::string toPrettyAudioString(uint32_t indent = 0, std::string name = ""); }; From 84b3d612ca9d70b51cdf7eb33029e6786c36dc7b Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Thu, 18 Jul 2013 16:12:38 +0200 Subject: [PATCH 496/788] Temporary commit for HTTPDynamic Support. --- lib/mp4.cpp | 120 ++++++++++++++++++++++------------------------------ lib/mp4.h | 4 +- 2 files changed, 52 insertions(+), 72 deletions(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index f4352a40..1d526f01 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -2379,7 +2379,7 @@ namespace MP4 { } //6 bits void ESDS::setStreamType(char newVal){ - setInt8(newVal<<2 + ((uint8_t)getUpstreamFlag() << 1) + (uint8_t)getReservedFlag() , 18); + setInt8(((newVal << 2) & 0xFC) + (getInt8(18) & 0x03), 18); } //6 bits bool ESDS::getUpstreamFlag(){ @@ -2395,7 +2395,7 @@ namespace MP4 { } void ESDS::setReservedFlag(bool newVal){ - setInt8((getStreamType()<<2) + ((uint8_t)getUpstreamFlag() << 1) + (uint8_t)newVal , 18); + setInt8((getInt8(18) & 0xFE) + (int)newVal, 18); } uint32_t ESDS::getBufferSize(){ @@ -2433,10 +2433,12 @@ namespace MP4 { uint32_t ESDS::getExtendedDecoderDescriptorTypeTag(){ return getInt24(31); } + //3 bytes void ESDS::setExtendedDecoderDescriptorTypeTag(uint32_t newVal){ setInt24(newVal, 31); } + //3 bytes char ESDS::getConfigDescriptorTypeLength(){ return getInt8(34); @@ -2446,94 +2448,79 @@ namespace MP4 { setInt8(newVal, 34); } - uint16_t ESDS::getESHeaderStartCodes(){ - return getInt16(35); + std::string ESDS::getESHeaderStartCodes(){ + std::string result; + for (int i = 0; i < getInt8(34) - 1; i++){ + result += getInt8(35 + i); + } + return result; } - void ESDS::setESHeaderStartCodes(uint16_t newVal){ - setInt16(newVal,35); + void ESDS::setESHeaderStartCodes(std::string newVal){ + setConfigDescriptorTypeLength(newVal.size()); + for (int i = 0; i < newVal.size(); i++){ + setInt8(newVal[i],35+i); + } } char ESDS::getSLConfigDescriptorTypeTag(){ - return getInt16(37); + return getInt16(34 + getInt8(34)); } void ESDS::setSLConfigDescriptorTypeTag(char newVal){ - setInt16(newVal, 37); + setInt16(newVal, 34 + getInt8(34)); } uint32_t ESDS::getSLConfigExtendedDescriptorTypeTag(){ - return getInt24(39); + return getInt24(36 + getInt8(34)); } //3 bytes void ESDS::setSLConfigExtendedDescriptorTypeTag(uint32_t newVal){ - setInt24(newVal, 39); + setInt24(newVal, 36 + getInt8(34)); } //3 bytes char ESDS::getSLDescriptorTypeLength(){ - return getInt8(42); + return getInt8(39 + getInt8(34)); } void ESDS::setSLDescriptorTypeLength(char newVal){ - setInt8(newVal, 42); + setInt8(newVal, 39 + getInt8(34)); } char ESDS::getSLValue(){ - return getInt8(43); + return getInt8(40 + getInt8(34)); } void ESDS::setSLValue(char newVal){ - setInt8(newVal, 43); + setInt8(newVal, 40 + getInt8(34)); } std::string ESDS::toPrettyString(uint32_t indent){ std::stringstream r; r << std::string(indent, ' ') << "[esds] ES Descriptor Box (" << boxedSize() << ")" << std::endl; r << fullBox::toPrettyString(indent); - //char getESDescriptorType(); - r << std::string(indent + 1, ' ') << "ESDescriptorType: 0x" << std::hex << getESDescriptorType() << std::dec << std::endl; - //uint32_t getExtendedESDescriptorType();//3 bytes - r << std::string(indent + 1, ' ') << "ExtendedESDescriptorType: 0x" << std::hex << getExtendedESDescriptorType() << std::dec << std::endl; - //char getESDescriptorTypeLength(); - r << std::string(indent + 1, ' ') << "ESDescriptorTypeLength:" << getESDescriptorTypeLength() << std::endl; - //uint16_t getESID(); - r << std::string(indent + 1, ' ') << "ESID: 0x" << std::hex << getESID() << std::dec << std::endl; - //char getStreamPriority(); - r << std::string(indent + 1, ' ') << "StreamPriority:" << getStreamPriority() << std::endl; - //char getDecoderConfigDescriptorTag(); - r << std::string(indent + 1, ' ') << "DecoderConfigDescriptorTag: 0x" << std::hex << getDecoderConfigDescriptorTag() << std::dec << std::endl; - //uint32_t getExtendedDecoderConfigDescriptorTag(); - r << std::string(indent + 1, ' ') << "ExtendedDecoderConfigDescriptorTag: 0x" << std::hex << getExtendedDecoderConfigDescriptorTag() << std::dec << std::endl; - //char getDecoderConfigDescriptorTypeLength(); - r << std::string(indent + 1, ' ') << "DecoderConfigDescriptorTag: 0x" << std::hex << getDecoderConfigDescriptorTag() << std::dec << std::endl; - //char getByteObjectTypeID(); - r << std::string(indent + 1, ' ') << "ByteObjectTypeID: 0x" << std::hex << getByteObjectTypeID() << std::dec << std::endl; - //char getStreamType();//6 bits - r << std::string(indent + 1, ' ') << "StreamType: 0x" << std::hex << getStreamType() << std::dec << std::endl; - //bool getUpstreamFlag(); - r << std::string(indent + 1, ' ') << "UpstreamFlag: 0x" << getUpstreamFlag() << std::endl; - //uint32_t getBufferSize();//3 bytes - r << std::string(indent + 1, ' ') << "BufferSize: 0x" << std::hex << getBufferSize() << std::dec << std::endl; - //uint32_t getMaximumBitRate(); - r << std::string(indent + 1, ' ') << "MaximumBitRate: 0x" << std::hex << getMaximumBitRate() << std::dec << std::endl; - //uint32_t getAverageBitRate(); - r << std::string(indent + 1, ' ') << "AverageBitRate: 0x" << std::hex << getAverageBitRate() << std::dec << std::endl; - //char getDecoderDescriptorTypeTag(); - r << std::string(indent + 1, ' ') << "DecoderDescriptorTypeTag: 0x" << std::hex << getDecoderDescriptorTypeTag() << std::dec << std::endl; - //uint32_t getExtendedDecoderDescriptorTypeTag();//3 bytes - r << std::string(indent + 1, ' ') << "ExtendedDecoderDescriptorTypeTag: 0x" << std::hex << getExtendedDecoderDescriptorTypeTag() << std::dec << std::endl; - //char getConfigDescriptorTypeLength(); - r << std::string(indent + 1, ' ') << "ConfigDescriptorTypeLength: 0x" << std::hex << getConfigDescriptorTypeLength() << std::dec << std::endl; - //uint16_t getESHeaderStartCodes(); - r << std::string(indent + 1, ' ') << "ESHeaderStartCodes: 0x" << std::hex << getESHeaderStartCodes() << std::dec << std::endl; - //char getSLConfigDescriptorTypeTag(); - r << std::string(indent + 1, ' ') << "SLConfigDescriptorTypeTag: 0x" << std::hex << getSLConfigDescriptorTypeTag() << std::dec << std::endl; - //uint32_t getSLConfigExtendedDescriptorTypeTag();//3 bytes - r << std::string(indent + 1, ' ') << "SLConfigExtendedDescriptorTypeTag: 0x" << std::hex << getSLConfigExtendedDescriptorTypeTag() << std::dec << std::endl; - //char getSLDescriptorTypeLength(); - r << std::string(indent + 1, ' ') << "SLDescriptorTypeLength: 0x" << std::hex << getSLDescriptorTypeLength() << std::dec << std::endl; - //char getSLValue(); - r << std::string(indent + 1, ' ') << "SLValue: 0x" << std::hex << getSLValue() << std::dec << std::endl; + r << std::string(indent + 1, ' ') << "ESDescriptorType: 0x" << std::hex << (int)getESDescriptorType() << std::dec << std::endl; + r << std::string(indent + 1, ' ') << "ExtendedESDescriptorType: 0x" << std::hex << (int)getExtendedESDescriptorType() << std::dec << std::endl; + r << std::string(indent + 1, ' ') << "ESDescriptorTypeLength:" << (int)getESDescriptorTypeLength() << std::endl; + r << std::string(indent + 1, ' ') << "ESID: 0x" << std::hex << (int)getESID() << std::dec << std::endl; + r << std::string(indent + 1, ' ') << "StreamPriority: 0x" << std::hex << (int)getStreamPriority() << std::dec << std::endl; + r << std::string(indent + 1, ' ') << "DecoderConfigDescriptorTag: 0x" << std::hex << (int)getDecoderConfigDescriptorTag() << std::dec << std::endl; + r << std::string(indent + 1, ' ') << "ExtendedDecoderConfigDescriptorTag: 0x" << std::hex << (int)getExtendedDecoderConfigDescriptorTag() << std::dec << std::endl; + r << std::string(indent + 1, ' ') << "DecoderConfigDescriptorTypeLength: " << (int)getDecoderConfigDescriptorTypeLength() << std::endl; + r << std::string(indent + 1, ' ') << "ByteObjectTypeID: 0x" << std::hex << (int)getByteObjectTypeID() << std::dec << std::endl; + r << std::string(indent + 1, ' ') << "StreamType: 0x" << std::hex << (int)getStreamType() << std::dec << std::endl; + r << std::string(indent + 1, ' ') << "UpstreamFlag: 0x" << std::hex << (int)getUpstreamFlag() << std::dec << std::endl; + r << std::string(indent + 1, ' ') << "BufferSize: 0x" << std::hex << (int)getBufferSize() << std::dec << std::endl; + r << std::string(indent + 1, ' ') << "MaximumBitRate: 0x" << std::hex << (int)getMaximumBitRate() << std::dec << std::endl; + r << std::string(indent + 1, ' ') << "AverageBitRate: 0x" << std::hex << (int)getAverageBitRate() << std::dec << std::endl; + r << std::string(indent + 1, ' ') << "DecoderDescriptorTypeTag: 0x" << std::hex << (int)getDecoderDescriptorTypeTag() << std::dec << std::endl; + r << std::string(indent + 1, ' ') << "ExtendedDecoderDescriptorTypeTag: 0x" << std::hex << (int)getExtendedDecoderDescriptorTypeTag() << std::dec << std::endl; + r << std::string(indent + 1, ' ') << "ConfigDescriptorTypeLength: 0x" << std::hex << (int)getConfigDescriptorTypeLength() << std::dec << std::endl; + r << std::string(indent + 1, ' ') << "ESHeaderStartCodes: " << getESHeaderStartCodes() << std::endl; + r << std::string(indent + 1, ' ') << "SLConfigDescriptorTypeTag: 0x" << std::hex << (int)getSLConfigDescriptorTypeTag() << std::dec << std::endl; + r << std::string(indent + 1, ' ') << "SLConfigExtendedDescriptorTypeTag: 0x" << std::hex << (int)getSLConfigExtendedDescriptorTypeTag() << std::dec << std::endl; + r << std::string(indent + 1, ' ') << "SLDescriptorTypeLength: 0x" << std::hex << (int)getSLDescriptorTypeLength() << std::dec << std::endl; + r << std::string(indent + 1, ' ') << "SLValue: 0x" << std::hex << (int)getSLValue() << std::dec << std::endl; return r.str(); } @@ -2887,6 +2874,7 @@ namespace MP4 { SMHD::SMHD(){ memcpy(data + 4, "smhd", 4); setBalance(0); + setInt16(0,6); } void SMHD::setBalance(int16_t newBalance){ @@ -3116,16 +3104,9 @@ namespace MP4 { setDuration(0); setRate(0x00010000); setVolume(0x0100); - setMatrix(0x00010000,0); - /*setMatrix(0,1); - setMatrix(0,2); - setMatrix(0,3);*/ + setMatrix(0x40000000,0); setMatrix(0x00010000,4); - /*setMatrix(0,5); - setMatrix(0,6); - setMatrix(0,7);*/ - //fills automatically with zero's - setMatrix(0x40000000,8); + setMatrix(0x00010000,8); setTrackID(1); } @@ -4252,7 +4233,6 @@ namespace MP4 { } void VisualSampleEntry::setCompressorName(std::string newCompressorName){ - newCompressorName.resize(32, ' '); setString(newCompressorName,42); } @@ -4506,8 +4486,8 @@ namespace MP4 { } void STSS::setSampleNumber(uint32_t newVal, uint32_t index){ - if (index+1 > getEntryCount()){ - setEntryCount(index); + if (index >= getEntryCount()){ + setEntryCount(index + 1); } setInt32(newVal, 8 + (index * 4)); } diff --git a/lib/mp4.h b/lib/mp4.h index f2a0675d..26b906d0 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -389,8 +389,8 @@ namespace MP4 { void setExtendedDecoderDescriptorTypeTag(uint32_t newVal);//3 bytes char getConfigDescriptorTypeLength(); void setConfigDescriptorTypeLength(char newVal); - uint16_t getESHeaderStartCodes(); - void setESHeaderStartCodes(uint16_t newVal); + std::string getESHeaderStartCodes(); + void setESHeaderStartCodes(std::string newVal); char getSLConfigDescriptorTypeTag(); void setSLConfigDescriptorTypeTag(char newVal); uint32_t getSLConfigExtendedDescriptorTypeTag();//3 bytes From 4b36b85e54ac49aaeff994a56cae406f506dcbf1 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Tue, 23 Jul 2013 13:12:49 +0200 Subject: [PATCH 497/788] Fixed minor bugs in MP4, resulting in working mp4 conversion. --- lib/mp4.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 1d526f01..35604848 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -3969,16 +3969,16 @@ namespace MP4 { void STCO::setChunkOffset(uint32_t newChunkOffset, uint32_t no){ if (no + 1 > getEntryCount()){ - setEntryCount(no+1); for (int i = getEntryCount(); i < no; i++){ setInt32(0, 8 + i * 4);//filling undefined entries } + setEntryCount(no+1); } setInt32(newChunkOffset, 8 + no * 4); } uint32_t STCO::getChunkOffset(uint32_t no){ - if (no + 1 >= getEntryCount()){ + if (no >= getEntryCount()){ return 0; } return getInt32(8 + no * 4); @@ -4029,7 +4029,7 @@ namespace MP4 { } uint32_t STSZ::getEntrySize(uint32_t no){ - if (no + 1 >= getSampleCount()){ + if (no >= getSampleCount()){ return 0; } return getInt32(12 + no * 4); From 5a0c2c001c1569e629de79ca7b57fc789865f49d Mon Sep 17 00:00:00 2001 From: Oswald Auguste de Bruin Date: Thu, 15 Aug 2013 15:31:12 +0200 Subject: [PATCH 498/788] More fixes on MP4 lib --- lib/mp4.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 35604848..598164cf 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -2516,7 +2516,10 @@ namespace MP4 { r << std::string(indent + 1, ' ') << "DecoderDescriptorTypeTag: 0x" << std::hex << (int)getDecoderDescriptorTypeTag() << std::dec << std::endl; r << std::string(indent + 1, ' ') << "ExtendedDecoderDescriptorTypeTag: 0x" << std::hex << (int)getExtendedDecoderDescriptorTypeTag() << std::dec << std::endl; r << std::string(indent + 1, ' ') << "ConfigDescriptorTypeLength: 0x" << std::hex << (int)getConfigDescriptorTypeLength() << std::dec << std::endl; - r << std::string(indent + 1, ' ') << "ESHeaderStartCodes: " << getESHeaderStartCodes() << std::endl; + r << std::string(indent + 1, ' ') << "ESHeaderStartCodes: 0x"; + for (unsigned int i = 0; i Date: Mon, 19 Aug 2013 09:32:51 +0200 Subject: [PATCH 499/788] MP4lib bug that prevented DTSC2mp4 is now fixed. MP4 conversion now works --- lib/mp4.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 598164cf..0681d335 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -2464,37 +2464,37 @@ namespace MP4 { } char ESDS::getSLConfigDescriptorTypeTag(){ - return getInt16(34 + getInt8(34)); + return getInt16(35 + getInt8(34)); } void ESDS::setSLConfigDescriptorTypeTag(char newVal){ - setInt16(newVal, 34 + getInt8(34)); + setInt16(newVal, 35 + getInt8(34)); } uint32_t ESDS::getSLConfigExtendedDescriptorTypeTag(){ - return getInt24(36 + getInt8(34)); + return getInt24(37 + getInt8(34)); } //3 bytes void ESDS::setSLConfigExtendedDescriptorTypeTag(uint32_t newVal){ - setInt24(newVal, 36 + getInt8(34)); + setInt24(newVal, 37 + getInt8(34)); } //3 bytes char ESDS::getSLDescriptorTypeLength(){ - return getInt8(39 + getInt8(34)); - } - - void ESDS::setSLDescriptorTypeLength(char newVal){ - setInt8(newVal, 39 + getInt8(34)); - } - - char ESDS::getSLValue(){ return getInt8(40 + getInt8(34)); } - void ESDS::setSLValue(char newVal){ + void ESDS::setSLDescriptorTypeLength(char newVal){ setInt8(newVal, 40 + getInt8(34)); } + char ESDS::getSLValue(){ + return getInt8(41 + getInt8(34)); + } + + void ESDS::setSLValue(char newVal){ + setInt8(newVal, 41 + getInt8(34)); + } + std::string ESDS::toPrettyString(uint32_t indent){ std::stringstream r; r << std::string(indent, ' ') << "[esds] ES Descriptor Box (" << boxedSize() << ")" << std::endl; From b83203501ce44a89db12bb1facbd5ded2e8d4bef Mon Sep 17 00:00:00 2001 From: Oswald de Bruin Date: Mon, 3 Jun 2013 10:32:44 +0200 Subject: [PATCH 500/788] Last MP4 edits --- lib/mp4.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 0681d335..8712c442 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -3972,6 +3972,7 @@ namespace MP4 { void STCO::setChunkOffset(uint32_t newChunkOffset, uint32_t no){ if (no + 1 > getEntryCount()){ + setEntryCount(no+1); for (int i = getEntryCount(); i < no; i++){ setInt32(0, 8 + i * 4);//filling undefined entries } @@ -4236,6 +4237,7 @@ namespace MP4 { } void VisualSampleEntry::setCompressorName(std::string newCompressorName){ + newCompressorName.resize(32, ' '); setString(newCompressorName,42); } From 9d9709f38b3ffe2e850fdf1e0220f889b2bfbaf6 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Tue, 23 Jul 2013 13:12:49 +0200 Subject: [PATCH 501/788] Fixed minor bugs in MP4, resulting in working mp4 conversion. --- lib/mp4.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 8712c442..5b49447c 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -3972,7 +3972,6 @@ namespace MP4 { void STCO::setChunkOffset(uint32_t newChunkOffset, uint32_t no){ if (no + 1 > getEntryCount()){ - setEntryCount(no+1); for (int i = getEntryCount(); i < no; i++){ setInt32(0, 8 + i * 4);//filling undefined entries } From b10b1df10c0f60767537b9be5d018370479da3bb Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Wed, 21 Aug 2013 15:49:58 +0200 Subject: [PATCH 502/788] Fix'ed a typo in ESDS Box --- lib/mp4.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 5b49447c..9f211a49 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -2450,7 +2450,7 @@ namespace MP4 { std::string ESDS::getESHeaderStartCodes(){ std::string result; - for (int i = 0; i < getInt8(34) - 1; i++){ + for (int i = 0; i < getInt8(34); i++){ result += getInt8(35 + i); } return result; @@ -2464,35 +2464,35 @@ namespace MP4 { } char ESDS::getSLConfigDescriptorTypeTag(){ - return getInt16(35 + getInt8(34)); + return getInt8(35 + getInt8(34)); } void ESDS::setSLConfigDescriptorTypeTag(char newVal){ - setInt16(newVal, 35 + getInt8(34)); + setInt8(newVal, 35 + getInt8(34)); } uint32_t ESDS::getSLConfigExtendedDescriptorTypeTag(){ - return getInt24(37 + getInt8(34)); + return getInt24(36 + getInt8(34)); } //3 bytes void ESDS::setSLConfigExtendedDescriptorTypeTag(uint32_t newVal){ - setInt24(newVal, 37 + getInt8(34)); + setInt24(newVal, 36 + getInt8(34)); } //3 bytes char ESDS::getSLDescriptorTypeLength(){ - return getInt8(40 + getInt8(34)); + return getInt8(39 + getInt8(34)); } void ESDS::setSLDescriptorTypeLength(char newVal){ - setInt8(newVal, 40 + getInt8(34)); + setInt8(newVal, 39 + getInt8(34)); } char ESDS::getSLValue(){ - return getInt8(41 + getInt8(34)); + return getInt8(40 + getInt8(34)); } void ESDS::setSLValue(char newVal){ - setInt8(newVal, 41 + getInt8(34)); + setInt8(newVal, 40 + getInt8(34)); } std::string ESDS::toPrettyString(uint32_t indent){ From 2b3c3609c84e0a5437675cfabd05109603ff04df Mon Sep 17 00:00:00 2001 From: Oswald Auguste de Bruin Date: Thu, 22 Aug 2013 16:18:30 +0200 Subject: [PATCH 503/788] DTSC2MP4 conversion now enabled in lib. Also, cleaned up automakefile. --- lib/Makefile.am | 45 +++++- lib/mp4.h | 23 +++ lib/mp4_conv.cpp | 374 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 440 insertions(+), 2 deletions(-) create mode 100644 lib/mp4_conv.cpp diff --git a/lib/Makefile.am b/lib/Makefile.am index 22c1f7cd..cc86a62f 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,5 +1,25 @@ lib_LTLIBRARIES=libmist-1.0.la -libmist_1_0_la_SOURCES=amf.h amf.cpp auth.h auth.cpp base64.h base64.cpp config.h config.cpp dtsc.h dtsc.cpp flv_tag.h flv_tag.cpp http_parser.h http_parser.cpp json.h json.cpp procs.h procs.cpp rtmpchunks.h rtmpchunks.cpp socket.h socket.cpp mp4.h mp4.cpp ftp.h ftp.cpp filesystem.h filesystem.cpp stream.h stream.cpp timing.h timing.cpp ts_packet.cpp ts_packet.h converter.cpp converter.h ogg.h ogg.cpp theora.cpp theora.h vorbis.cpp vorbis.h +libmist_1_0_la_SOURCES=amf.h amf.cpp +libmist_1_0_la_SOURCES+=auth.h auth.cpp +libmist_1_0_la_SOURCES+=base64.h base64.cpp +libmist_1_0_la_SOURCES+=config.h config.cpp +libmist_1_0_la_SOURCES+=dtsc.h dtsc.cpp +libmist_1_0_la_SOURCES+=flv_tag.h flv_tag.cpp +libmist_1_0_la_SOURCES+=http_parser.h http_parser.cpp +libmist_1_0_la_SOURCES+=json.h json.cpp +libmist_1_0_la_SOURCES+=procs.h procs.cpp +libmist_1_0_la_SOURCES+=rtmpchunks.h rtmpchunks.cpp +libmist_1_0_la_SOURCES+=socket.h socket.cpp +libmist_1_0_la_SOURCES+=mp4.h mp4.cpp mp4_conv.cpp +libmist_1_0_la_SOURCES+=ftp.h ftp.cpp +libmist_1_0_la_SOURCES+=filesystem.h filesystem.cpp +libmist_1_0_la_SOURCES+=stream.h stream.cpp +libmist_1_0_la_SOURCES+=timing.h timing.cpp +libmist_1_0_la_SOURCES+=ts_packet.cpp ts_packet.h +libmist_1_0_la_SOURCES+=converter.cpp converter.h +libmist_1_0_la_SOURCES+=ogg.h ogg.cpp +libmist_1_0_la_SOURCES+=theora.cpp theora.h +libmist_1_0_la_SOURCES+=vorbis.cpp vorbis.h libmist_1_0_la_LDFLAGS = -version-info 5:1:2 libmist_1_0_la_CPPFLAGS=$(DEPS_CFLAGS) $(global_CFLAGS) libmist_1_0_la_LIBADD=$(DEPS_LIBS) $(CLOCK_LIB) @@ -8,4 +28,25 @@ pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = mist-1.0.pc library_includedir=$(includedir)/mist-1.0/mist -library_include_HEADERS = amf.h auth.h base64.h config.h dtsc.h flv_tag.h http_parser.h json.h procs.h rtmpchunks.h socket.h mp4.h ftp.h filesystem.h stream.h timing.h nal.h ts_packet.h converter.h ogg.h theora.h vorbis.h +library_include_HEADERS = amf.h +library_include_HEADERS +=auth.h +library_include_HEADERS +=base64.h +library_include_HEADERS +=config.h +library_include_HEADERS +=dtsc.h +library_include_HEADERS +=flv_tag.h +library_include_HEADERS +=http_parser.h +library_include_HEADERS +=json.h +library_include_HEADERS +=procs.h +library_include_HEADERS +=rtmpchunks.h +library_include_HEADERS +=socket.h +library_include_HEADERS +=mp4.h +library_include_HEADERS +=ftp.h +library_include_HEADERS +=filesystem.h +library_include_HEADERS +=stream.h +library_include_HEADERS +=timing.h +library_include_HEADERS +=nal.h +library_include_HEADERS +=ts_packet.h +library_include_HEADERS +=converter.h +library_include_HEADERS +=ogg.h +library_include_HEADERS +=theora.h +library_include_HEADERS +=vorbis.h diff --git a/lib/mp4.h b/lib/mp4.h index 26b906d0..cce0056d 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -1,5 +1,6 @@ #pragma once #include +#include #include #include #include @@ -11,7 +12,29 @@ /// Contains all MP4 format related code. namespace MP4 { + struct keyPart{ + long long int trackID; + long long int size; + long long int time; + long long int len; + JSON::Value parts; + }; + class DTSC2MP4Converter{ + public: + std::string DTSCMeta2MP4Header(JSON::Value metaData); + void parseDTSC(JSON::Value mediaPart); + bool sendReady(); + std::string sendString(); + private: + std::vector keyParts; + //std::vector keyParts = Conv.keyParts; + std::map > trackBuffer; + long long unsigned int curKey;//the key chunk we are currently searching for in keyParts + long long unsigned int curPart;//current part in current key + std::string stringBuffer; + }; + class Box{ public: Box(char * datapointer = 0, bool manage = true); diff --git a/lib/mp4_conv.cpp b/lib/mp4_conv.cpp new file mode 100644 index 00000000..fd8ac5ea --- /dev/null +++ b/lib/mp4_conv.cpp @@ -0,0 +1,374 @@ +#include "mp4.h" +#include + +namespace MP4{ + bool keyPartSort(keyPart i, keyPart j){ + return (i.time < j.time); + } + + std::string DTSC2MP4Converter::DTSCMeta2MP4Header(JSON::Value metaData){ + std::stringstream header; + + //ftyp box + /// \todo fill ftyp with non hardcoded values from file + MP4::FTYP ftypBox; + ftypBox.setMajorBrand(0x6D703431);//mp41 + ftypBox.setMinorVersion(0); + ftypBox.setCompatibleBrands(0x69736f6d,0); + ftypBox.setCompatibleBrands(0x69736f32,1); + ftypBox.setCompatibleBrands(0x61766331,2); + ftypBox.setCompatibleBrands(0x6D703431,3); + header << std::string(ftypBox.asBox(),ftypBox.boxedSize()); + + uint64_t mdatSize = 0; + //std::vector stcoOffsets; + //moov box + MP4::MOOV moovBox; + MP4::MVHD mvhdBox; + mvhdBox.setVersion(0); + mvhdBox.setCreationTime(0); + mvhdBox.setModificationTime(0); + mvhdBox.setTimeScale(1000); + mvhdBox.setRate(0x10000); + mvhdBox.setDuration(metaData["lastms"].asInt() + metaData["firstms"].asInt()); + mvhdBox.setTrackID(0); + mvhdBox.setVolume(256); + mvhdBox.setMatrix(0x00010000,0); + mvhdBox.setMatrix(0,1); + mvhdBox.setMatrix(0,2); + mvhdBox.setMatrix(0,3); + mvhdBox.setMatrix(0x00010000,4); + mvhdBox.setMatrix(0,5); + mvhdBox.setMatrix(0,6); + mvhdBox.setMatrix(0,7); + mvhdBox.setMatrix(0x40000000,8); + moovBox.setContent(mvhdBox, 0); + + //calculate interleaving + //putting all metadata in a huge vector 'keyParts' + keyParts.clear(); + for (JSON::ObjIter trackIt = metaData["tracks"].ObjBegin(); trackIt != metaData["tracks"].ObjEnd(); trackIt++){ + for (unsigned int keyIt = 0; keyIt != trackIt->second["keys"].size(); keyIt++){ + keyPart temp; + temp.trackID = trackIt->second["trackid"].asInt(); + temp.size = trackIt->second["keys"][keyIt]["size"].asInt(); + temp.time = trackIt->second["keys"][keyIt]["time"].asInt(); + temp.len = trackIt->second["keys"][keyIt]["len"].asInt(); + temp.parts = trackIt->second["keys"][keyIt]["parts"]; + keyParts.push_back(temp); + } + } + //sort by time on keyframes for interleaving + std::sort(keyParts.begin(), keyParts.end(), keyPartSort); + + //start arbitrary track addition for header + int boxOffset = 1; + for (JSON::ObjIter it = metaData["tracks"].ObjBegin(); it != metaData["tracks"].ObjEnd(); it++){ + int timescale = 0; + MP4::TRAK trakBox; + MP4::TKHD tkhdBox; + //std::cerr << it->second["trackid"].asInt() << std::endl; + tkhdBox.setVersion(0); + tkhdBox.setFlags(15); + tkhdBox.setTrackID(it->second["trackid"].asInt()); + tkhdBox.setDuration(it->second["lastms"].asInt() + it->second["firsms"].asInt()); + + if (it->second["type"].asString() == "video"){ + tkhdBox.setWidth(it->second["width"].asInt() << 16); + tkhdBox.setHeight(it->second["height"].asInt() << 16); + tkhdBox.setVolume(0); + }else{ + tkhdBox.setVolume(256); + tkhdBox.setAlternateGroup(1); + } + tkhdBox.setMatrix(0x00010000,0); + tkhdBox.setMatrix(0,1); + tkhdBox.setMatrix(0,2); + tkhdBox.setMatrix(0,3); + tkhdBox.setMatrix(0x00010000,4); + tkhdBox.setMatrix(0,5); + tkhdBox.setMatrix(0,6); + tkhdBox.setMatrix(0,7); + tkhdBox.setMatrix(0x40000000,8); + trakBox.setContent(tkhdBox, 0); + + MP4::MDIA mdiaBox; + MP4::MDHD mdhdBox(0);/// \todo fix constructor mdhd in lib + mdhdBox.setCreationTime(0); + mdhdBox.setModificationTime(0); + //Calculating media time based on sampledelta. Probably cheating, but it works... + int tmpParts = 0; + for (JSON::ArrIter tmpIt = it->second["keys"].ArrBegin(); tmpIt != it->second["keys"].ArrEnd(); tmpIt++){ + tmpParts += (*tmpIt)["parts"].size(); + } + timescale = ((double)(42 * tmpParts) / (it->second["lastms"].asInt() + it->second["firstms"].asInt())) * 1000; + mdhdBox.setTimeScale(timescale); + mdhdBox.setDuration(((it->second["lastms"].asInt() + it->second["firsms"].asInt()) * ((double)timescale / 1000))); + mdiaBox.setContent(mdhdBox, 0); + + std::string tmpStr = it->second["type"].asString(); + MP4::HDLR hdlrBox;/// \todo fix constructor hdlr in lib + if (tmpStr == "video"){ + hdlrBox.setHandlerType(0x76696465);//vide + }else if (tmpStr == "audio"){ + hdlrBox.setHandlerType(0x736F756E);//soun + } + hdlrBox.setName(it->first); + mdiaBox.setContent(hdlrBox, 1); + + MP4::MINF minfBox; + if (tmpStr == "video"){ + MP4::VMHD vmhdBox; + vmhdBox.setFlags(1); + minfBox.setContent(vmhdBox,0); + }else if (tmpStr == "audio"){ + MP4::SMHD smhdBox; + minfBox.setContent(smhdBox,0); + } + MP4::DINF dinfBox; + MP4::DREF drefBox;/// \todo fix constructor dref in lib + drefBox.setVersion(0); + MP4::URL urlBox; + urlBox.setFlags(1); + drefBox.setDataEntry(urlBox,0); + dinfBox.setContent(drefBox,0); + minfBox.setContent(dinfBox,1); + + MP4::STBL stblBox; + MP4::STSD stsdBox; + stsdBox.setVersion(0); + if (tmpStr == "video"){//boxname = codec + MP4::VisualSampleEntry vse; + std::string tmpStr2 = it->second["codec"]; + if (tmpStr2 == "H264"){ + vse.setCodec("avc1"); + } + vse.setDataReferenceIndex(1); + vse.setWidth(it->second["width"].asInt()); + vse.setHeight(it->second["height"].asInt()); + MP4::AVCC avccBox; + avccBox.setPayload(it->second["init"].asString()); + vse.setCLAP(avccBox); + stsdBox.setEntry(vse,0); + }else if(tmpStr == "audio"){//boxname = codec + MP4::AudioSampleEntry ase; + std::string tmpStr2 = it->second["codec"]; + if (tmpStr2 == "AAC"){ + ase.setCodec("mp4a"); + ase.setDataReferenceIndex(1); + } + ase.setSampleRate(it->second["rate"].asInt()); + ase.setChannelCount(it->second["channels"].asInt()); + ase.setSampleSize(it->second["size"].asInt()); + MP4::ESDS esdsBox; + esdsBox.setESDescriptorTypeLength(32+it->second["init"].asString().size()); + esdsBox.setESID(2); + esdsBox.setStreamPriority(0); + esdsBox.setDecoderConfigDescriptorTypeLength(18+it->second["init"].asString().size()); + esdsBox.setByteObjectTypeID(0x40); + esdsBox.setStreamType(5); + esdsBox.setReservedFlag(1); + esdsBox.setBufferSize(1250000); + esdsBox.setMaximumBitRate(10000000); + esdsBox.setAverageBitRate(it->second["bps"].asInt() * 8); + esdsBox.setConfigDescriptorTypeLength(5); + esdsBox.setESHeaderStartCodes(it->second["init"].asString()); + esdsBox.setSLConfigDescriptorTypeTag(0x6); + esdsBox.setSLConfigExtendedDescriptorTypeTag(0x808080); + esdsBox.setSLDescriptorTypeLength(1); + esdsBox.setSLValue(2); + ase.setCodecBox(esdsBox); + stsdBox.setEntry(ase,0); + } + stblBox.setContent(stsdBox,0); + + /// \todo update following stts lines + MP4::STTS sttsBox;//current version probably causes problems + sttsBox.setVersion(0); + MP4::STTSEntry newEntry; + newEntry.sampleCount = tmpParts; + //42, because of reasons. + newEntry.sampleDelta = 42; + sttsBox.setSTTSEntry(newEntry, 0); + stblBox.setContent(sttsBox,1); + + if (it->second["type"] == "video"){ + //STSS Box here + MP4::STSS stssBox; + stssBox.setVersion(0); + int tmpCount = 1; + for (int i = 0; i < it->second["keys"].size(); i++){ + stssBox.setSampleNumber(tmpCount,i); + tmpCount += it->second["keys"][i]["parts"].size(); + } + stblBox.setContent(stssBox,2); + } + + int offset = (it->second["type"] == "video"); + + + MP4::STSC stscBox; + stscBox.setVersion(0); + uint32_t total = 0; + MP4::STSCEntry stscEntry; + stscEntry.firstChunk = 1; + stscEntry.samplesPerChunk = 1; + stscEntry.sampleDescriptionIndex = 1; + stscBox.setSTSCEntry(stscEntry, 0); + stblBox.setContent(stscBox,2 + offset); + + MP4::STSZ stszBox; + stszBox.setVersion(0); + total = 0; + for (int i = 0; i < it->second["keys"].size(); i++){ + for (int o = 0; o < it->second["keys"][i]["parts"].size(); o++){ + stszBox.setEntrySize(it->second["keys"][i]["parts"][o].asInt(), total);//in bytes in file + total++; + } + } + stblBox.setContent(stszBox,3 + offset); + + MP4::STCO stcoBox; + stcoBox.setVersion(1); + total = 0; + uint64_t totalByteOffset = 0; + //Inserting wrong values on purpose here, will be fixed later. + //Current values are actual byte offset without header-sized offset + for (unsigned int i = 0; i < keyParts.size(); i++){//for all keypart size + if(keyParts[i].trackID == it->second["trackid"].asInt()){//if keypart is of current trackID + for (unsigned int o = 0; o < keyParts[i].parts.size(); o++){//add all parts to STCO + stcoBox.setChunkOffset(totalByteOffset, total); + total++; + totalByteOffset += keyParts[i].parts[o].asInt(); + } + }else{ + totalByteOffset += keyParts[i].size; + } + } + //calculating the offset where the STCO box will be in the main MOOV box + //needed for probable optimise + /*stcoOffsets.push_back( + moovBox.payloadSize() + + trakBox.boxedSize() + + mdiaBox.boxedSize() + + minfBox.boxedSize() + + stblBox.boxedSize() + );*/ + mdatSize = totalByteOffset; + stblBox.setContent(stcoBox,4 + offset); + minfBox.setContent(stblBox,2); + mdiaBox.setContent(minfBox, 2); + trakBox.setContent(mdiaBox, 1); + moovBox.setContent(trakBox, boxOffset); + boxOffset++; + } + //end arbitrary + //initial offset length ftyp, length moov + 8 + unsigned long long int byteOffset = ftypBox.boxedSize() + moovBox.boxedSize() + 8; + //update all STCO + //std::map STCOFix; + //for tracks + for (unsigned int i = 1; i < moovBox.getContentCount(); i++){ + //10 lines to get the STCO box. + MP4::TRAK checkTrakBox; + MP4::MDIA checkMdiaBox; + MP4::MINF checkMinfBox; + MP4::STBL checkStblBox; + MP4::STCO checkStcoBox; + checkTrakBox = ((MP4::TRAK&)moovBox.getContent(i)); + for (int j = 0; j < checkTrakBox.getContentCount(); j++){ + if (checkTrakBox.getContent(j).isType("mdia")){ + checkMdiaBox = ((MP4::MDIA&)checkTrakBox.getContent(j)); + break; + } + } + for (int j = 0; j < checkMdiaBox.getContentCount(); j++){ + if (checkMdiaBox.getContent(j).isType("minf")){ + checkMinfBox = ((MP4::MINF&)checkMdiaBox.getContent(j)); + break; + } + } + for (int j = 0; j < checkMinfBox.getContentCount(); j++){ + if (checkMinfBox.getContent(j).isType("stbl")){ + checkStblBox = ((MP4::STBL&)checkMinfBox.getContent(j)); + break; + } + } + for (int j = 0; j < checkStblBox.getContentCount(); j++){ + if (checkStblBox.getContent(j).isType("stco")){ + //STCOFix.insert( std::pair(((MP4::TKHD&)(checkTrakBox.getContent(0))).getTrackID(),(MP4::STCO&)(checkStblBox.getContent(j)))); + checkStcoBox = ((MP4::STCO&)checkStblBox.getContent(j)); + break; + } + } + /*MP4::Box temp = MP4::Box((moovBox.payload()+stcoOffsets[i]),false); + MP4::STCO & checkStcoBox = *((MP4::STCO*)(&temp)); + std::cerr << checkStcoBox.toPrettyString() << std::endl;*/ + //got the STCO box, fixing values with MP4 header offset + for (int j = 0; j < checkStcoBox.getEntryCount(); j++){ + checkStcoBox.setChunkOffset(checkStcoBox.getChunkOffset(j) + byteOffset, j); + } + } + header << std::string(moovBox.asBox(),moovBox.boxedSize()); + + //printf("%c%c%c%cmdat", (mdatSize>>24) & 0x000000FF,(mdatSize>>16) & 0x000000FF,(mdatSize>>8) & 0x000000FF,mdatSize & 0x000000FF); + header << (char)((mdatSize>>24) & 0x000000FF) << (char)((mdatSize>>16) & 0x000000FF) << (char)((mdatSize>>8) & 0x000000FF) << (char)(mdatSize & 0x000000FF) << "mdat"; + std::cerr << "Header Written" << std::endl; + //end of header + std::map > trackBuffer; + long long unsigned int curKey = 0;//the key chunk we are currently searching for in keyParts + long long unsigned int curPart = 0;//current part in current key + + return header.str(); + } + + void DTSC2MP4Converter::parseDTSC(JSON::Value mediaPart){ + //mdat output here + //output cleanout buffer first + //while there are requested packets in the trackBuffer:... + while (!trackBuffer[keyParts[curKey].trackID].empty()){ + //output requested packages + if(keyParts[curKey].parts[curPart].asInt() != trackBuffer[keyParts[curKey].trackID].front()["data"].asString().size()){ + std::cerr << "Size discrepancy in buffer packet. Size: " << mediaPart["data"].asString().size() << " Expected:" << keyParts[curKey].parts[curPart].asInt() << std::endl; + } + stringBuffer += trackBuffer[keyParts[curKey].trackID].front()["data"].asString(); + trackBuffer[keyParts[curKey].trackID].pop_front(); + curPart++; + if(curPart >= keyParts[curKey].parts.size()){ + curPart = 0; + curKey++; + } + } + //after that, try to put out the JSON data directly + if(keyParts[curKey].trackID == mediaPart["trackid"].asInt()){ + //output JSON packet + if(keyParts[curKey].parts[curPart].asInt() != mediaPart["data"].asString().size()){ + std::cerr << "Size discrepancy in JSON packet. Size: " << mediaPart["data"].asString().size() << " Expected:" << keyParts[curKey].parts[curPart].asInt() << std::endl; + } + stringBuffer += mediaPart["data"].asString(); + curPart++; + if(curPart >= keyParts[curKey].parts.size()){ + curPart = 0; + curKey++; + } + }else{ + //buffer for later + trackBuffer[mediaPart["trackid"].asInt()].push_back(mediaPart); + } + } + + bool DTSC2MP4Converter::sendReady(){ + if (stringBuffer.length() > 0){ + return true; + }else{ + return false; + } + } + + std::string DTSC2MP4Converter::sendString(){ + std::string temp = stringBuffer; + stringBuffer = ""; + return temp; + } +} + From 491d85d3b66e035fad08c7cf2729fd33f2f1fab2 Mon Sep 17 00:00:00 2001 From: Oswald Auguste de Bruin Date: Fri, 23 Aug 2013 11:05:25 +0200 Subject: [PATCH 504/788] MP4 further developed --- lib/mp4.h | 5 ++--- lib/mp4_conv.cpp | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/mp4.h b/lib/mp4.h index cce0056d..2c39c91c 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -26,12 +26,11 @@ namespace MP4 { void parseDTSC(JSON::Value mediaPart); bool sendReady(); std::string sendString(); - private: std::vector keyParts; - //std::vector keyParts = Conv.keyParts; - std::map > trackBuffer; + private: long long unsigned int curKey;//the key chunk we are currently searching for in keyParts long long unsigned int curPart;//current part in current key + std::map > trackBuffer; std::string stringBuffer; }; diff --git a/lib/mp4_conv.cpp b/lib/mp4_conv.cpp index fd8ac5ea..ea4fb208 100644 --- a/lib/mp4_conv.cpp +++ b/lib/mp4_conv.cpp @@ -313,7 +313,7 @@ namespace MP4{ //printf("%c%c%c%cmdat", (mdatSize>>24) & 0x000000FF,(mdatSize>>16) & 0x000000FF,(mdatSize>>8) & 0x000000FF,mdatSize & 0x000000FF); header << (char)((mdatSize>>24) & 0x000000FF) << (char)((mdatSize>>16) & 0x000000FF) << (char)((mdatSize>>8) & 0x000000FF) << (char)(mdatSize & 0x000000FF) << "mdat"; - std::cerr << "Header Written" << std::endl; + //std::cerr << "Header Written" << std::endl; //end of header std::map > trackBuffer; long long unsigned int curKey = 0;//the key chunk we are currently searching for in keyParts From f17406edd6ac26ad1c437f1f5b2dacda085d0793 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Mon, 26 Aug 2013 10:52:09 +0200 Subject: [PATCH 505/788] Optimize for generating mp4 header. --- lib/mp4_conv.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/mp4_conv.cpp b/lib/mp4_conv.cpp index ea4fb208..3cdbcba6 100644 --- a/lib/mp4_conv.cpp +++ b/lib/mp4_conv.cpp @@ -48,13 +48,13 @@ namespace MP4{ //putting all metadata in a huge vector 'keyParts' keyParts.clear(); for (JSON::ObjIter trackIt = metaData["tracks"].ObjBegin(); trackIt != metaData["tracks"].ObjEnd(); trackIt++){ - for (unsigned int keyIt = 0; keyIt != trackIt->second["keys"].size(); keyIt++){ + for (JSON::ObjArrIter keyIt = trackIt->second["keys"].ArrBegin(); keyIt != trackIt->second["keys"].ArrEnd(); keyIt++){ keyPart temp; temp.trackID = trackIt->second["trackid"].asInt(); - temp.size = trackIt->second["keys"][keyIt]["size"].asInt(); - temp.time = trackIt->second["keys"][keyIt]["time"].asInt(); - temp.len = trackIt->second["keys"][keyIt]["len"].asInt(); - temp.parts = trackIt->second["keys"][keyIt]["parts"]; + temp.size = (*keyIt)["size"].asInt(); + temp.time = (*keyIt)["time"].asInt(); + temp.len = (*keyIt)["len"].asInt(); + temp.parts = (*keyIt)["parts"]; keyParts.push_back(temp); } } From 203a4382a73044782cbc80814a01cd26516a9657 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 26 Aug 2013 11:21:43 +0200 Subject: [PATCH 506/788] Fixed broken build. Bad Erik! ^_^ --- lib/mp4.cpp | 4 ++-- lib/mp4.h | 4 ++-- lib/mp4_conv.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 9f211a49..1a61349f 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -4191,7 +4191,7 @@ namespace MP4 { setDepth(0x0018); } - void VisualSampleEntry::setCodec(char* newCodec){ + void VisualSampleEntry::setCodec(const char* newCodec){ memcpy(data + 4, newCodec, 4); } @@ -4304,7 +4304,7 @@ namespace MP4 { setSampleRate(44100); } - void AudioSampleEntry::setCodec(char* newCodec){ + void AudioSampleEntry::setCodec(const char* newCodec){ memcpy(data + 4, newCodec, 4); } diff --git a/lib/mp4.h b/lib/mp4.h index 2c39c91c..72320d32 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -819,7 +819,7 @@ namespace MP4 { ///\todo set default values public: VisualSampleEntry(); - void setCodec(char* newCodec); + void setCodec(const char* newCodec); void setWidth(uint16_t newWidth); uint16_t getWidth(); void setHeight(uint16_t newHeight); @@ -844,7 +844,7 @@ namespace MP4 { public: ///\todo set default values AudioSampleEntry(); - void setCodec(char* newCodec); + void setCodec(const char* newCodec); void setChannelCount(uint16_t newChannelCount); uint16_t getChannelCount(); void setSampleSize(uint16_t newSampleSize); diff --git a/lib/mp4_conv.cpp b/lib/mp4_conv.cpp index 3cdbcba6..e9613ef2 100644 --- a/lib/mp4_conv.cpp +++ b/lib/mp4_conv.cpp @@ -48,7 +48,7 @@ namespace MP4{ //putting all metadata in a huge vector 'keyParts' keyParts.clear(); for (JSON::ObjIter trackIt = metaData["tracks"].ObjBegin(); trackIt != metaData["tracks"].ObjEnd(); trackIt++){ - for (JSON::ObjArrIter keyIt = trackIt->second["keys"].ArrBegin(); keyIt != trackIt->second["keys"].ArrEnd(); keyIt++){ + for (JSON::ArrIter keyIt = trackIt->second["keys"].ArrBegin(); keyIt != trackIt->second["keys"].ArrEnd(); keyIt++){ keyPart temp; temp.trackID = trackIt->second["trackid"].asInt(); temp.size = (*keyIt)["size"].asInt(); From dbddd18f1e13bc04ed05e49e7442142d3325f39d Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Mon, 26 Aug 2013 12:46:11 +0200 Subject: [PATCH 507/788] Fixed a bug in endStream, fixed infinite loop. --- lib/dtsc.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index b3c7d195..54a3a821 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -176,7 +176,8 @@ bool DTSC::Stream::parsePacket(Socket::Buffer & buffer){ /// Adds a keyframe packet to all tracks, so the stream can be fully played. void DTSC::Stream::endStream(){ if (metadata.isMember("tracks") && metadata["tracks"].size() > 0){ - for (JSON::ObjIter it = metadata["tracks"].ObjBegin(); it != metadata["tracks"].ObjEnd(); it++){ + JSON::Value trackData = metadata["tracks"]; + for (JSON::ObjIter it = trackData.ObjBegin(); it != trackData.ObjEnd(); it++){ if(it->second.isMember("lastms") && it->second.isMember("trackid")){ // TODO JSON::Value newPack; newPack["time"] = it->second["lastms"]; From d78e91b2bd6687444d019e7be9969f71f716df9d Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Mon, 26 Aug 2013 15:15:44 +0200 Subject: [PATCH 508/788] Small fix in converter api --- lib/converter.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/converter.cpp b/lib/converter.cpp index 1b83d29b..c38da886 100644 --- a/lib/converter.cpp +++ b/lib/converter.cpp @@ -63,7 +63,8 @@ namespace Converter { JSON::Value Converter::queryPath(std::string myPath){ std::vector cmd; cmd.reserve(3); - cmd.push_back((char*)"MistInfo"); + std::string mistPath = Util::getMyPath() + "MistInfo"; + cmd.push_back((char*)mistPath.c_str()); cmd.push_back(NULL); cmd.push_back(NULL); fprintf( stderr, "Querying %s\n", myPath.c_str()); From c0ccde0d566ca0810d63aa1a7dae93068e4417b4 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Mon, 26 Aug 2013 16:01:01 +0200 Subject: [PATCH 509/788] Fixed the optimize in the dtsc lib to be an actual optimize. --- lib/dtsc.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 54a3a821..7e0204b6 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -806,6 +806,7 @@ void DTSC::File::seekNext(){ tmpPos.seekTime = ((long long unsigned int)ntohl(((int*)newHeader)[3])) << 32; tmpPos.seekTime += ntohl(((int*)newHeader)[4]); }else{ + tmpPos.seekTime = -1; for (JSON::ArrIter it = getTrackById(jsonbuffer["trackid"].asInt())["keys"].ArrBegin(); it != getTrackById(jsonbuffer["trackid"].asInt())["keys"].ArrEnd(); it++){ if ((*it)["time"].asInt() > jsonbuffer["time"].asInt()){ tmpPos.seekTime = (*it)["time"].asInt(); @@ -814,7 +815,20 @@ void DTSC::File::seekNext(){ } } } - currentPositions.insert(tmpPos); + if (tmpPos.seekTime != -1){ + bool insert = true; + for (std::set::iterator curPosIter = currentPositions.begin(); curPosIter != currentPositions.end(); curPosIter++){ + if ((*curPosIter).trackID == tmpPos.trackID && (*curPosIter).seekTime >= tmpPos.seekTime){ + insert = false; + break; + } + } + if (insert){ + currentPositions.insert(tmpPos); + }else{ + seek_time(jsonbuffer["time"].asInt() + 1, jsonbuffer["trackid"].asInt()); + } + } } } } From d7e9029609da1563aa72c8c3b4306ffa83b4c316 Mon Sep 17 00:00:00 2001 From: Oswald Auguste de Bruin Date: Mon, 26 Aug 2013 16:18:06 +0200 Subject: [PATCH 510/788] Fixed nasty segfault bug from Local settings page. It does not segfault anymore. --- lib/converter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/converter.cpp b/lib/converter.cpp index c38da886..295b9315 100644 --- a/lib/converter.cpp +++ b/lib/converter.cpp @@ -81,8 +81,8 @@ namespace Converter { continue; } std::string fileName = entry->d_name; - std::string myPath = std::string(myPath + (myPath[myPath.size()-1] == '/' ? "" : "/") + entry->d_name); - cmd[1] = (char*)myPath.c_str(); + std::string mijnPad = std::string(myPath + (myPath[myPath.size()-1] == '/' ? "" : "/") + entry->d_name); + cmd[1] = (char*)mijnPad.c_str(); int outFD = -1; Util::Procs::StartPiped("MistInfo", &cmd[0], 0, &outFD, 0); while( Util::Procs::isActive("MistInfo")){ Util::sleep(10); } From 6675b647b845d1b28e710ec4cf59bc088b345caa Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 27 Aug 2013 00:29:04 +0200 Subject: [PATCH 511/788] Some static/non-static fixes to procs/stream library, updated converter to make use of getOutputOf function instead of re-implementing this. --- lib/converter.cpp | 23 ++++------------------- lib/procs.cpp | 4 ++-- lib/procs.h | 4 ++-- lib/stream.cpp | 4 ++-- 4 files changed, 10 insertions(+), 25 deletions(-) diff --git a/lib/converter.cpp b/lib/converter.cpp index 295b9315..d03411cb 100644 --- a/lib/converter.cpp +++ b/lib/converter.cpp @@ -61,12 +61,9 @@ namespace Converter { } JSON::Value Converter::queryPath(std::string myPath){ - std::vector cmd; - cmd.reserve(3); + char const * cmd[3] = {0, 0, 0}; std::string mistPath = Util::getMyPath() + "MistInfo"; - cmd.push_back((char*)mistPath.c_str()); - cmd.push_back(NULL); - cmd.push_back(NULL); + cmd[0] = mistPath.c_str(); fprintf( stderr, "Querying %s\n", myPath.c_str()); JSON::Value result; DIR * Dirp = opendir(myPath.c_str()); @@ -82,20 +79,8 @@ namespace Converter { } std::string fileName = entry->d_name; std::string mijnPad = std::string(myPath + (myPath[myPath.size()-1] == '/' ? "" : "/") + entry->d_name); - cmd[1] = (char*)mijnPad.c_str(); - int outFD = -1; - Util::Procs::StartPiped("MistInfo", &cmd[0], 0, &outFD, 0); - while( Util::Procs::isActive("MistInfo")){ Util::sleep(10); } - FILE * outFile = fdopen( outFD, "r" ); - char * fileBuf = 0; - size_t fileBufLen = 0; - getline(&fileBuf, &fileBufLen, outFile); - std::string line = fileBuf; - result[fileName] = JSON::fromString(std::string(fileBuf)); - if ( !result[fileName]){ - result.removeMember(fileName); - } - fclose( outFile ); + cmd[1] = mijnPad.c_str(); + result[fileName] = JSON::fromString(Util::Procs::getOutputOf((char* const*)cmd)); } } return result; diff --git a/lib/procs.cpp b/lib/procs.cpp index 86295771..2293417e 100644 --- a/lib/procs.cpp +++ b/lib/procs.cpp @@ -153,7 +153,7 @@ void Util::Procs::childsig_handler(int signum){ /// Runs the given command and returns the stdout output as a string. -std::string Util::Procs::getOutputOf(char * argv[]){ +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); @@ -433,7 +433,7 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2, st /// \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 * argv[], int * fdin, int * fdout, int * fderr){ +pid_t Util::Procs::StartPiped(std::string name, char* const* argv, int * fdin, int * fdout, int * fderr){ if (isActive(name)){ #if DEBUG >= 1 std::cerr << name << " already active - skipping start" << std::endl; diff --git a/lib/procs.h b/lib/procs.h index 16f75a6d..8df712fc 100644 --- a/lib/procs.h +++ b/lib/procs.h @@ -23,12 +23,12 @@ namespace Util { static void runCmd(std::string & cmd); static void setHandler(); public: - static std::string getOutputOf(char * argv[]); + 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(std::string name, char * 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); diff --git a/lib/stream.cpp b/lib/stream.cpp index 1ecd2363..b2bb3e5e 100644 --- a/lib/stream.cpp +++ b/lib/stream.cpp @@ -39,9 +39,9 @@ Socket::Connection Util::Stream::getLive(std::string streamname){ Socket::Connection Util::Stream::getVod(std::string filename){ std::string name = "MistPlayer " + filename; std::string player_bin = Util::getMyPath() + "MistPlayer"; - const char *argv[] = {player_bin.c_str(), filename.c_str(), NULL}; + char* const argv[] = {(char*)player_bin.c_str(), (char*)filename.c_str(), NULL}; int fdin = -1, fdout = -1, fderr = fileno(stderr); - Util::Procs::StartPiped(name, (char **)argv, &fdin, &fdout, &fderr); + Util::Procs::StartPiped(name, argv, &fdin, &fdout, &fderr); // if StartPiped fails then fdin and fdout will be unmodified (-1) return Socket::Connection(fdin, fdout); } From 6cf42561a6fbc4c0d893391a7c374a39408bf7d9 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Tue, 27 Aug 2013 15:01:48 +0200 Subject: [PATCH 512/788] Fixed bugs in dtsc and theora libs. --- lib/dtsc.cpp | 7 ++++--- lib/dtsc.h | 2 +- lib/theora.cpp | 6 +++--- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 7e0204b6..f1463f10 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -811,6 +811,7 @@ void DTSC::File::seekNext(){ if ((*it)["time"].asInt() > jsonbuffer["time"].asInt()){ tmpPos.seekTime = (*it)["time"].asInt(); tmpPos.seekPos = (*it)["bpos"].asInt(); + tmpPos.trackID = jsonbuffer["trackid"].asInt(); break; } } @@ -826,7 +827,7 @@ void DTSC::File::seekNext(){ if (insert){ currentPositions.insert(tmpPos); }else{ - seek_time(jsonbuffer["time"].asInt() + 1, jsonbuffer["trackid"].asInt()); + seek_time(jsonbuffer["time"].asInt() + 1, jsonbuffer["trackid"].asInt(), true); } } } @@ -913,10 +914,10 @@ JSON::Value & DTSC::File::getTrackById(int trackNo){ return empty; } -bool DTSC::File::seek_time(int ms, int trackNo){ +bool DTSC::File::seek_time(int ms, int trackNo, bool forceSeek){ seekPos tmpPos; tmpPos.trackID = trackNo; - if (jsonbuffer && ms > jsonbuffer["time"].asInt() && trackNo >= jsonbuffer["trackid"].asInt()){ + if (!forceSeek && jsonbuffer && ms > jsonbuffer["time"].asInt() && trackNo >= jsonbuffer["trackid"].asInt()){ tmpPos.seekTime = jsonbuffer["time"].asInt(); tmpPos.seekPos = getBytePos(); }else{ diff --git a/lib/dtsc.h b/lib/dtsc.h index 81675c85..0d112722 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -110,7 +110,7 @@ namespace DTSC { JSON::Value & getJSON(); JSON::Value & getTrackById(int trackNo); bool seek_time(int seconds); - bool seek_time(int seconds, int trackNo); + bool seek_time(int seconds, int trackNo, bool forceSeek = false); bool seek_bpos(int bpos); void writePacket(std::string & newPacket); void writePacket(JSON::Value & newPacket); diff --git a/lib/theora.cpp b/lib/theora.cpp index 45b12f0a..1e7a23c2 100644 --- a/lib/theora.cpp +++ b/lib/theora.cpp @@ -1,6 +1,6 @@ -#include -#include -#include +#include "theora.h" +#include +#include #include #include From 86a745dbfbad241d6e9c4961310b2d034e464d6a Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Tue, 27 Aug 2013 16:00:07 +0200 Subject: [PATCH 513/788] Fixed a potential bug in DTSC::seekPos --- lib/dtsc.cpp | 20 ++++++++++---------- lib/dtsc.h | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index f1463f10..c50db20a 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -731,7 +731,7 @@ void DTSC::File::seekNext(){ jsonbuffer.null(); return; } - fseek(F,currentPositions.begin()->seekPos, SEEK_SET); + fseek(F,currentPositions.begin()->bytePos, SEEK_SET); if ( reachedEOF()){ strbuffer = ""; jsonbuffer.null(); @@ -741,7 +741,7 @@ void DTSC::File::seekNext(){ if ( !metadata.isMember("merged") || !metadata["merged"]){ seek_time(currentPositions.begin()->seekTime + 1, currentPositions.begin()->trackID); } - fseek(F,currentPositions.begin()->seekPos, SEEK_SET); + fseek(F,currentPositions.begin()->bytePos, SEEK_SET); currentPositions.erase(currentPositions.begin()); lastreadpos = ftell(F); if (fread(buffer, 4, 1, F) != 1){ @@ -800,7 +800,7 @@ void DTSC::File::seekNext(){ if (fread((void*)newHeader, 20, 1, F) == 1){ if (memcmp(newHeader, DTSC::Magic_Packet2, 4) == 0){ seekPos tmpPos; - tmpPos.seekPos = tempLoc; + tmpPos.bytePos = tempLoc; tmpPos.trackID = ntohl(((int*)newHeader)[2]); if (selectedTracks.find(tmpPos.trackID) != selectedTracks.end()){ tmpPos.seekTime = ((long long unsigned int)ntohl(((int*)newHeader)[3])) << 32; @@ -810,7 +810,7 @@ void DTSC::File::seekNext(){ for (JSON::ArrIter it = getTrackById(jsonbuffer["trackid"].asInt())["keys"].ArrBegin(); it != getTrackById(jsonbuffer["trackid"].asInt())["keys"].ArrEnd(); it++){ if ((*it)["time"].asInt() > jsonbuffer["time"].asInt()){ tmpPos.seekTime = (*it)["time"].asInt(); - tmpPos.seekPos = (*it)["bpos"].asInt(); + tmpPos.bytePos = (*it)["bpos"].asInt(); tmpPos.trackID = jsonbuffer["trackid"].asInt(); break; } @@ -919,10 +919,10 @@ bool DTSC::File::seek_time(int ms, int trackNo, bool forceSeek){ tmpPos.trackID = trackNo; if (!forceSeek && jsonbuffer && ms > jsonbuffer["time"].asInt() && trackNo >= jsonbuffer["trackid"].asInt()){ tmpPos.seekTime = jsonbuffer["time"].asInt(); - tmpPos.seekPos = getBytePos(); + tmpPos.bytePos = getBytePos(); }else{ tmpPos.seekTime = 0; - tmpPos.seekPos = 0; + tmpPos.bytePos = 0; } for (JSON::ArrIter keyIt = metadata["tracks"][trackMapping[trackNo]]["keys"].ArrBegin(); keyIt != metadata["tracks"][trackMapping[trackNo]]["keys"].ArrEnd(); keyIt++){ if ((*keyIt)["time"].asInt() > ms){ @@ -930,7 +930,7 @@ bool DTSC::File::seek_time(int ms, int trackNo, bool forceSeek){ } if ((*keyIt)["time"].asInt() > tmpPos.seekTime){ tmpPos.seekTime = (*keyIt)["time"].asInt(); - tmpPos.seekPos = (*keyIt)["bpos"].asInt(); + tmpPos.bytePos = (*keyIt)["bpos"].asInt(); } } bool foundPacket = false; @@ -940,7 +940,7 @@ bool DTSC::File::seek_time(int ms, int trackNo, bool forceSeek){ return false; } //Seek to first packet after ms. - seek_bpos(tmpPos.seekPos); + seek_bpos(tmpPos.bytePos); //read the header char header[20]; fread((void*)header, 20, 1, F); @@ -948,7 +948,7 @@ bool DTSC::File::seek_time(int ms, int trackNo, bool forceSeek){ int packSize = ntohl(((int*)header)[1]); int packID = ntohl(((int*)header)[2]); if (memcmp(header,Magic_Packet2,4) != 0 || packID != trackNo){ - tmpPos.seekPos += 8 + packSize; + tmpPos.bytePos += 8 + packSize; continue; } //get timestamp of packet, if too large, break, if not, skip size bytes. @@ -958,7 +958,7 @@ bool DTSC::File::seek_time(int ms, int trackNo, bool forceSeek){ if (myTime >= ms){ foundPacket = true; }else{ - tmpPos.seekPos += 8 + packSize; + tmpPos.bytePos += 8 + packSize; continue; } } diff --git a/lib/dtsc.h b/lib/dtsc.h index 0d112722..b92d6fe5 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -72,7 +72,7 @@ namespace DTSC { return true; }else{ if (seekTime == rhs.seekTime){ - if (seekPos < rhs.seekPos){ + if (bytePos < rhs.bytePos){ return true; }else{ if (trackID < rhs.trackID){ @@ -84,7 +84,7 @@ namespace DTSC { return false; } long long unsigned int seekTime; - long long unsigned int seekPos; + long long unsigned int bytePos; unsigned int trackID; }; From f48833343a6cb60eced241d250d4cdf62a61bee4 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 27 Aug 2013 20:30:38 +0200 Subject: [PATCH 514/788] Removed accidental C++11 dependancy. --- lib/json.cpp | 10 +++++----- lib/json.h | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/json.cpp b/lib/json.cpp index c9d0545f..68113298 100644 --- a/lib/json.cpp +++ b/lib/json.cpp @@ -439,19 +439,19 @@ JSON::Value & JSON::Value::operator[](unsigned int i){ /// Retrieves the JSON::Value at this position in the object. /// Fails horribly if that values does not exist. -JSON::Value const & JSON::Value::operator[](const std::string i) const{ - return objVal.at(i); +const JSON::Value & JSON::Value::operator[](const std::string i) const{ + return objVal.find(i)->second; } /// Retrieves the JSON::Value at this position in the object. /// Fails horribly if that values does not exist. -JSON::Value const & JSON::Value::operator[](const char * i) const{ - return objVal.at(i); +const JSON::Value & JSON::Value::operator[](const char * i) const{ + return objVal.find(i)->second; } /// Retrieves the JSON::Value at this position in the array. /// Fails horribly if that values does not exist. -JSON::Value const & JSON::Value::operator[](unsigned int i) const{ +const JSON::Value & JSON::Value::operator[](unsigned int i) const{ return arrVal[i]; } diff --git a/lib/json.h b/lib/json.h index 8dbea25f..63217205 100644 --- a/lib/json.h +++ b/lib/json.h @@ -72,9 +72,9 @@ namespace JSON { Value & operator[](const std::string i); Value & operator[](const char * i); Value & operator[](unsigned int i); - Value const & operator[](const std::string i) const; - Value const & operator[](const char * i) const; - Value const & operator[](unsigned int i) const; + 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(); void netPrepare(); From 2769d3cc6c7197262ae2501c96e4adc50de8027e Mon Sep 17 00:00:00 2001 From: Thulinma Date: Wed, 28 Aug 2013 12:18:22 +0200 Subject: [PATCH 515/788] Fixed JSON string escaping to support non-printable characters, made some JSON lib private functions static in the .cpp file instead. --- lib/json.cpp | 23 ++++++++++++++++++----- lib/json.h | 4 ---- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/lib/json.cpp b/lib/json.cpp index 68113298..2b78934b 100644 --- a/lib/json.cpp +++ b/lib/json.cpp @@ -7,14 +7,21 @@ #include //for uint64_t #include //for memcpy #include //for htonl -int JSON::Value::c2hex(int c){ + +static int c2hex(char c){ if (c >= '0' && c <= '9') return c - '0'; if (c >= 'a' && c <= 'f') return c - 'a' + 10; if (c >= 'A' && c <= 'F') return c - 'A' + 10; return 0; } -std::string JSON::Value::read_string(int separator, std::istream & fromstream){ +static char hex2c(char c){ + if (c < 10){return '0' + c;} + if (c < 16){return 'A' + (c - 10);} + return '0'; +} + +static std::string read_string(int separator, std::istream & fromstream){ std::string out; bool escaped = false; while (fromstream.good()){ @@ -64,7 +71,7 @@ std::string JSON::Value::read_string(int separator, std::istream & fromstream){ return out; } -std::string JSON::Value::string_escape(const std::string val){ +static std::string string_escape(const std::string val){ std::string out = "\""; for (unsigned int i = 0; i < val.size(); ++i){ switch (val[i]){ @@ -90,7 +97,13 @@ std::string JSON::Value::string_escape(const std::string val){ out += "\\t"; break; default: - out += val[i]; + if (val[i] < 32 || val[i] > 126){ + out += "\\u00"; + out += hex2c((val[i] >> 4) & 0xf); + out += hex2c(val[i] & 0xf); + }else{ + out += val[i]; + } break; } } @@ -99,7 +112,7 @@ std::string JSON::Value::string_escape(const std::string val){ } /// Skips an std::istream forward until any of the following characters is seen: ,]} -void JSON::Value::skipToEnd(std::istream & fromstream){ +static void skipToEnd(std::istream & fromstream){ while (fromstream.good()){ char peek = fromstream.peek(); if (peek == ','){ diff --git a/lib/json.h b/lib/json.h index 63217205..e16268c1 100644 --- a/lib/json.h +++ b/lib/json.h @@ -35,10 +35,6 @@ namespace JSON { std::string strVal; std::deque arrVal; std::map objVal; - std::string read_string(int separator, std::istream & fromstream); - static std::string string_escape(const std::string val); - int c2hex(int c); - static void skipToEnd(std::istream & fromstream); public: //friends friend class DTSC::Stream; //for access to strVal From 31bfc8223e0eefcb69ac2e6c565bc8a7ddfbdb84 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Wed, 28 Aug 2013 12:48:29 +0200 Subject: [PATCH 516/788] Made toPrettyString slightly smarter and always output valid JSON, no matter what. (May still output altered contents, though.) --- lib/json.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/json.cpp b/lib/json.cpp index 2b78934b..928c410b 100644 --- a/lib/json.cpp +++ b/lib/json.cpp @@ -674,9 +674,9 @@ std::string JSON::Value::toPrettyString(int indentation) const{ break; } case STRING: { - for (unsigned int i = 0; i < 5 && i < strVal.size(); ++i){ - if (strVal[i] < 32 || strVal[i] > 125){ - return JSON::Value((long long int)strVal.size()).asString() + " bytes of binary data"; + for (unsigned int i = 0; i < 201 && i < strVal.size(); ++i){ + if (strVal[i] < 32 || strVal[i] > 126 || strVal.size() > 200){ + return "\""+JSON::Value((long long int)strVal.size()).asString() + " bytes of data\""; } } return string_escape(strVal); From 632b3ff5948697baa88eb671afc06db7007c5d76 Mon Sep 17 00:00:00 2001 From: Oswald Auguste de Bruin Date: Thu, 29 Aug 2013 13:10:22 +0200 Subject: [PATCH 517/788] Ogg fixed --- lib/ogg.cpp | 14 ++++++++++---- lib/vorbis.cpp | 28 ++++++++++++++++++++-------- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/lib/ogg.cpp b/lib/ogg.cpp index d15a7600..2a6bec50 100644 --- a/lib/ogg.cpp +++ b/lib/ogg.cpp @@ -188,6 +188,9 @@ namespace OGG{ temp = 0; } } + if (temp!=0){ + segmentTableDeque.push_back(temp); + } } return segmentTableDeque; } @@ -196,6 +199,7 @@ namespace OGG{ std::cerr << "Segments too big, create a continue page" << std::endl; } + ///\TODO MAKE FIX HERE bool Page::setSegmentTable(std::vector layout){ dataSum=0; for (unsigned int i = 0; i < layout.size(); i++){ @@ -205,7 +209,7 @@ namespace OGG{ char table[255]; for (unsigned int i = 0; i < layout.size(); i++){ while (layout[i]>=255){ - if (place >= 255){ + if (place > 255){ STerrMSG(); return false; } @@ -213,12 +217,14 @@ namespace OGG{ layout[i] -= 255; place++; } - if (place >= 255){ + if (place > 255){ STerrMSG(); return false; } - table[place] = layout[i]; - place++; + if (layout[i] != 0){ + table[place] = layout[i]; + place++; + } } setPageSegments(place); setSegmentTable(table,place); diff --git a/lib/vorbis.cpp b/lib/vorbis.cpp index 28df5637..4eead952 100644 --- a/lib/vorbis.cpp +++ b/lib/vorbis.cpp @@ -66,7 +66,7 @@ namespace vorbis{ char header::getBlockSize0(){ if (getHeaderType() == 1){ - return data[28]>>4; + return (data[28]>>4) & 0x0F; }else{ return 0; } @@ -106,12 +106,24 @@ namespace vorbis{ 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; + 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; @@ -140,7 +152,7 @@ namespace vorbis{ }else{ return false; } - return validate(); + return true; } From 31ad924b4698dce621a6003c9dd1a910126ce6c3 Mon Sep 17 00:00:00 2001 From: Oswald Auguste de Bruin Date: Tue, 3 Sep 2013 09:42:37 +0200 Subject: [PATCH 518/788] Fixed sagfile (segment fault) in ogg progressive. --- lib/ogg.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/ogg.cpp b/lib/ogg.cpp index 2a6bec50..43d1ded7 100644 --- a/lib/ogg.cpp +++ b/lib/ogg.cpp @@ -448,7 +448,6 @@ namespace OGG{ return false; } memset(data,0,27); - datasize = 0; dataSum = 0; codec = ""; setMagicNumber(); From c373136679859e2614c40ed018458e3a8d51c0f4 Mon Sep 17 00:00:00 2001 From: Oswald Auguste de Bruin Date: Tue, 3 Sep 2013 09:53:27 +0200 Subject: [PATCH 519/788] Mem leak fixed --- lib/ogg.cpp | 6 ++++++ lib/ogg.h | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/ogg.cpp b/lib/ogg.cpp index 43d1ded7..0592d6e9 100644 --- a/lib/ogg.cpp +++ b/lib/ogg.cpp @@ -44,6 +44,12 @@ namespace OGG{ datasize = 0; dataSum = 0; } + + Page::~Page(){ + if (data){ + free(data); + } + } bool Page::read(std::string & newData){ segmentTableDeque.clear(); diff --git a/lib/ogg.h b/lib/ogg.h index b0abcf20..368adf5b 100644 --- a/lib/ogg.h +++ b/lib/ogg.h @@ -11,6 +11,7 @@ namespace OGG{ class Page{ public: Page(); + ~Page(); bool read(std::string & newData); long unsigned int getMagicNumber(); void setMagicNumber(); @@ -49,7 +50,7 @@ namespace OGG{ private: std::deque segmentTableDeque; char* data;//pointer to the beginning of the Page data - unsigned int datasize;//size of the complete page + unsigned int datasize;//size of the allocated memory unsigned int dataSum;//size of the total segments bool checkDataSize(unsigned int size); std::string codec;//codec in the page From de733c31a313ee1812cd067f53c814f0fcf9f04b Mon Sep 17 00:00:00 2001 From: Oswald Auguste de Bruin Date: Wed, 4 Sep 2013 15:55:35 +0200 Subject: [PATCH 520/788] Fix in lib which makes mistserver compilable and usable for mac --- lib/config.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/config.cpp b/lib/config.cpp index b5156308..e856eb83 100644 --- a/lib/config.cpp +++ b/lib/config.cpp @@ -10,6 +10,9 @@ #else #include #endif +#if defined(__APPLE__) +#include +#endif #include #include #include @@ -401,12 +404,18 @@ void Util::Config::addBasicConnectorOptions(JSON::Value & capabilities){ /// Gets directory the current executable is stored in. std::string Util::getMyPath(){ char mypath[500]; +#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 std::string tPath = mypath; size_t slash = tPath.rfind('/'); if (slash == std::string::npos){ From c4e0c399ac6205181151552ff0578c87621b9bca Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 9 Sep 2013 12:05:26 +0200 Subject: [PATCH 521/788] Fixed restreaming bug. --- lib/dtsc.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index c50db20a..62f922f8 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -223,11 +223,9 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ livePos lastPos = buffers.rbegin()->first; if (newPos < lastPos){ if ((lastPos.seekTime > 1000) && newPos.seekTime < lastPos.seekTime - 1000){ - metadata.null(); metadata["reset"] = 1LL; buffers.clear(); keyframes.clear(); - trackMapping.clear(); }else{ newPos.seekTime = lastPos.seekTime+1; } From cc38f1947775c65c6e53d9854946c926188e14e5 Mon Sep 17 00:00:00 2001 From: Oswald Auguste de Bruin Date: Mon, 9 Sep 2013 15:38:35 +0200 Subject: [PATCH 522/788] Fixed Ogg problems --- lib/ogg.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/ogg.cpp b/lib/ogg.cpp index 0592d6e9..37814e52 100644 --- a/lib/ogg.cpp +++ b/lib/ogg.cpp @@ -227,9 +227,11 @@ namespace OGG{ STerrMSG(); return false; } - if (layout[i] != 0){ + if (layout[i] >= 0){//fix somewhere here table[place] = layout[i]; - place++; + if (place<255){//last segment does not need a closing 0 + place++; + } } } setPageSegments(place); @@ -472,7 +474,7 @@ namespace OGG{ void Page::readDTSCVector(std::vector DTSCVec, unsigned int serial, unsigned int sequence){ clear(); setVersion(); - if (DTSCVec[0]["OggCont"]){ + if (DTSCVec[0]["OggCont"] || DTSCVec[0]["granule"].asInt() == -1){//if it is a continue page, also for granule=0xFFFFFFFF setHeaderType(1);//headertype 1 = Continue Page }else if (DTSCVec[0]["OggEOS"]){ setHeaderType(4);//headertype 4 = end of stream From bf485b180384f11aebad705ebeb677cb4f87c545 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Wed, 11 Sep 2013 12:28:31 +0200 Subject: [PATCH 523/788] Added cross-platform getTmpFolder() function in Util. --- lib/stream.cpp | 43 +++++++++++++++++++++++++++++++++++-------- lib/stream.h | 1 + 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/lib/stream.cpp b/lib/stream.cpp index b2bb3e5e..588c3b42 100644 --- a/lib/stream.cpp +++ b/lib/stream.cpp @@ -7,12 +7,43 @@ #include #include +#include +#include #include "json.h" #include "stream.h" #include "procs.h" #include "config.h" #include "socket.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. @@ -32,7 +63,7 @@ void Util::Stream::sanitizeName(std::string & streamname){ } Socket::Connection Util::Stream::getLive(std::string streamname){ - return Socket::Connection("/tmp/mist/stream_" + streamname); + return Socket::Connection(getTmpFolder() + "stream_" + streamname); } /// Starts a process for a VoD stream. @@ -49,7 +80,7 @@ Socket::Connection Util::Stream::getVod(std::string filename){ /// Probe for available streams. Currently first VoD, then Live. Socket::Connection Util::Stream::getStream(std::string streamname){ sanitizeName(streamname); - JSON::Value ServConf = JSON::fromFile("/tmp/mist/streamlist"); + JSON::Value ServConf = JSON::fromFile(getTmpFolder() + "streamlist"); if (ServConf["streams"].isMember(streamname)){ if (ServConf["streams"][streamname]["source"].asString()[0] == '/'){ #if DEBUG >= 5 @@ -60,7 +91,7 @@ Socket::Connection Util::Stream::getStream(std::string streamname){ #if DEBUG >= 5 std::cerr << "Opening live stream " << streamname << std::endl; #endif - return Socket::Connection("/tmp/mist/stream_" + streamname); + return Socket::Connection(getTmpFolder() + "stream_" + streamname); } } #if DEBUG >= 5 @@ -73,13 +104,9 @@ Socket::Connection Util::Stream::getStream(std::string streamname){ /// Filters the streamname, removing invalid characters and /// converting all letters to lowercase. /// If a '?' character is found, everything following that character is deleted. -/// If the /tmp/mist directory doesn't exist yet, this will create it. Socket::Server Util::Stream::makeLive(std::string streamname){ sanitizeName(streamname); - std::string loc = "/tmp/mist/stream_" + streamname; - //attempt to create the /tmp/mist directory if it doesn't exist already. - //ignore errors - we catch all problems in the Socket::Server creation already - mkdir("/tmp/mist", S_IRWXU | S_IRWXG | S_IRWXO); + std::string loc = getTmpFolder() + "stream_" + streamname; //create and return the Socket::Server return Socket::Server(loc); } diff --git a/lib/stream.h b/lib/stream.h index 2c285997..2be8c92b 100644 --- a/lib/stream.h +++ b/lib/stream.h @@ -6,6 +6,7 @@ #include "socket.h" namespace Util { + std::string getTmpFolder(); class Stream{ public: static void sanitizeName(std::string & streamname); From f3bed1ab94e736905a26b9cf68000733fa4af4d5 Mon Sep 17 00:00:00 2001 From: Oswald Auguste de Bruin Date: Thu, 12 Sep 2013 08:52:48 +0200 Subject: [PATCH 524/788] Weird fix that made ogg work. --- lib/ogg.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/ogg.cpp b/lib/ogg.cpp index 37814e52..c6a513f5 100644 --- a/lib/ogg.cpp +++ b/lib/ogg.cpp @@ -474,7 +474,7 @@ namespace OGG{ void Page::readDTSCVector(std::vector DTSCVec, unsigned int serial, unsigned int sequence){ clear(); setVersion(); - if (DTSCVec[0]["OggCont"] || DTSCVec[0]["granule"].asInt() == -1){//if it is a continue page, also for granule=0xFFFFFFFF + if (DTSCVec[0]["OggCont"] ){//if it is a continue page, also for granule=0xFFFFFFFF setHeaderType(1);//headertype 1 = Continue Page }else if (DTSCVec[0]["OggEOS"]){ setHeaderType(4);//headertype 4 = end of stream @@ -482,6 +482,14 @@ namespace OGG{ setHeaderType(0);//headertype 0 = normal } setGranulePosition(DTSCVec[0]["granule"].asInt()); + for (unsigned int i = 1; i < DTSCVec.size(); i++){ + if (DTSCVec[0]["granule"].asInt() != DTSCVec[i]["granule"].asInt()){ + std::cerr << "Granule inconcistency!! " << DTSCVec[0]["granule"].asInt() << " != " << DTSCVec[i]["granule"].asInt() << std::endl; + } + if (DTSCVec[0]["trackid"].asInt() != DTSCVec[i]["trackid"].asInt()){ + std::cerr << "Track ID inconcistency!! " << DTSCVec[0]["trackid"].asInt() << " != " < Date: Thu, 12 Sep 2013 11:32:24 +0200 Subject: [PATCH 525/788] Now providing audio and video in separate elements within the encoders parts. --- lib/converter.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/converter.cpp b/lib/converter.cpp index d03411cb..b1e67e52 100644 --- a/lib/converter.cpp +++ b/lib/converter.cpp @@ -32,15 +32,15 @@ namespace Converter { 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"; + allCodecs["ffmpeg"]["audio"][strtok(NULL, " \t")] = "aac"; } if (strstr(fileBuf, "h264") || strstr(fileBuf, "H264")){ strtok(fileBuf, " \t"); - allCodecs["ffmpeg"][strtok(NULL, " \t")] = "h264"; + allCodecs["ffmpeg"]["video"][strtok(NULL, " \t")] = "h264"; } if (strstr(fileBuf, "mp3") || strstr(fileBuf, "MP3")){ strtok(fileBuf, " \t"); - allCodecs["ffmpeg"][strtok(NULL, " \t")] = "mp3"; + allCodecs["ffmpeg"]["audio"][strtok(NULL, " \t")] = "mp3"; } } fclose( outFile ); From b10185be2ab686ce1bb1b7ebcbe62c3f723a340a Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Thu, 12 Sep 2013 12:42:40 +0200 Subject: [PATCH 526/788] Fixed a bug in the conversion API. --- lib/converter.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/converter.cpp b/lib/converter.cpp index b1e67e52..a85ccbcc 100644 --- a/lib/converter.cpp +++ b/lib/converter.cpp @@ -32,15 +32,15 @@ namespace Converter { while ( !(feof(outFile) || ferror(outFile)) && (getline(&fileBuf, &fileBufLen, outFile) != -1)){ if (strstr(fileBuf, "aac") || strstr(fileBuf, "AAC")){ strtok(fileBuf, " \t"); - allCodecs["ffmpeg"]["audio"][strtok(NULL, " \t")] = "aac"; + allCodecs["ffmpeg"][strtok(NULL, " \t")] = "aac"; } if (strstr(fileBuf, "h264") || strstr(fileBuf, "H264")){ strtok(fileBuf, " \t"); - allCodecs["ffmpeg"]["video"][strtok(NULL, " \t")] = "h264"; + allCodecs["ffmpeg"][strtok(NULL, " \t")] = "h264"; } if (strstr(fileBuf, "mp3") || strstr(fileBuf, "MP3")){ strtok(fileBuf, " \t"); - allCodecs["ffmpeg"]["audio"][strtok(NULL, " \t")] = "mp3"; + allCodecs["ffmpeg"][strtok(NULL, " \t")] = "mp3"; } } fclose( outFile ); @@ -54,7 +54,12 @@ namespace Converter { 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++){ - result[convIt->first][codIt->first] = codIt->second; + if (codIt->second == "h264"){ + result[convIt->first]["video"][codIt->first] = codIt->second; + }else{ + result[convIt->first]["audio"][codIt->first] = codIt->second; + + } } } return result; From 5ea87a37f7217c3269db8ee4053e43227268e467 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Sun, 15 Sep 2013 16:08:30 +0200 Subject: [PATCH 527/788] Update on the JSON lib to support new parts encoding. --- lib/json.cpp | 15 +++++++++++++++ lib/json.h | 2 ++ 2 files changed, 17 insertions(+) diff --git a/lib/json.cpp b/lib/json.cpp index 928c410b..ab716a6b 100644 --- a/lib/json.cpp +++ b/lib/json.cpp @@ -992,3 +992,18 @@ JSON::Value JSON::fromDTMI2(const unsigned char * data, unsigned int len, unsign tmp["trackid"] = tmpTrackID; return tmp; } + +std::string JSON::encodeVector(std::vector & toEncode){ + std::string result; + for(int i = 0; i < toEncode.size(); i++){ + long long int temp = toEncode[i]; + while( temp >= 0xFFFF){ + result += (char)0xFF; + result += (char)0xFF; + temp -= 0xFFFF; + } + result += (char)temp / 255; + result += (char)temp % 255; + } + return result; +} diff --git a/lib/json.h b/lib/json.h index e16268c1..b1f6b6cc 100644 --- a/lib/json.h +++ b/lib/json.h @@ -5,6 +5,7 @@ #include #include #include +#include //empty definition of DTSC::Stream so it can be a friend. namespace DTSC { @@ -107,4 +108,5 @@ namespace JSON { Value fromString(std::string json); Value fromFile(std::string filename); + std::string encodeVector(std::vector & toEncode); } From 974861d993efa329b6dcae2f79d4200f5f34c529 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Thu, 19 Sep 2013 10:03:54 +0200 Subject: [PATCH 528/788] Memory efficiency upgrade. --- lib/dtsc.cpp | 12 +++--------- lib/dtsc.h | 2 -- lib/json.h | 29 +++++++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 62f922f8..3ea91b76 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -277,6 +277,9 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ for (JSON::ArrIter it = prevKey["parts"].ArrBegin(); it != prevKey["parts"].ArrEnd(); it++){ size += it->asInt(); } + prevKey["partsize"] = prevKey["parts"].size(); + std::string tmpParts = JSON::encodeVector(prevKey["parts"].ArrBegin(), prevKey["parts"].ArrEnd()); + prevKey["parts"] = tmpParts; prevKey["size"] = size; long long int bps = (double)prevKey["size"].asInt() / ((double)prevKey["len"].asInt() / 1000.0); if (bps > metadata["tracks"][newTrack]["maxbps"].asInt()){ @@ -549,7 +552,6 @@ DTSC::File & DTSC::File::operator =(const File & rhs){ strbuffer = rhs.strbuffer; jsonbuffer = rhs.jsonbuffer; metadata = rhs.metadata; - firstmetadata = rhs.firstmetadata; currtime = rhs.currtime; lastreadpos = rhs.lastreadpos; headerSize = rhs.headerSize; @@ -605,11 +607,6 @@ JSON::Value & DTSC::File::getMeta(){ return metadata; } -/// Returns the header metadata for this file as JSON::Value. -JSON::Value & DTSC::File::getFirstMeta(){ - return firstmetadata; -} - /// (Re)writes the given string to the header area if the size is the same as the existing header. /// Forces a write if force is set to true. bool DTSC::File::writeHeader(std::string & header, bool force){ @@ -694,9 +691,6 @@ void DTSC::File::readHeader(int pos){ } metadata = JSON::fromDTMI(strbuffer); } - if (pos == 0){ - firstmetadata = metadata; - } //if there is another header, read it and replace metadata with that one. if (metadata.isMember("moreheader") && metadata["moreheader"].asInt() > 0){ if (metadata["moreheader"].asInt() < getBytePosEOF()){ diff --git a/lib/dtsc.h b/lib/dtsc.h index b92d6fe5..5e0c5278 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -97,7 +97,6 @@ namespace DTSC { File & operator = (const File & rhs); ~File(); JSON::Value & getMeta(); - JSON::Value & getFirstMeta(); long long int getLastReadPos(); bool writeHeader(std::string & header, bool force = false); long long int addHeader(std::string & header); @@ -122,7 +121,6 @@ namespace DTSC { std::string strbuffer; JSON::Value jsonbuffer; JSON::Value metadata; - JSON::Value firstmetadata; std::map trackMapping; long long int currtime; long long int lastreadpos; diff --git a/lib/json.h b/lib/json.h index b1f6b6cc..f8b7e27e 100644 --- a/lib/json.h +++ b/lib/json.h @@ -109,4 +109,33 @@ namespace JSON { Value fromFile(std::string filename); std::string encodeVector(std::vector & toEncode); + + template + 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 / 255; + result += (char)tmp % 255; + } + return result; + } + + template + void decodeVector( std::string input, T & result ){ + result.clear(); + int tmp = 0; + for( int i = 0; i < input.size(); i += 2){ + tmp += input[i] + input[i + 1]; + if ((tmp % 0xFFFF) != 0 || (input[i] + input[i+1]) == 0){ + result.push_back(tmp); + tmp = 0; + } + } + } } From a80eeef659d53c8de43b8a4024b87371cd0df0b3 Mon Sep 17 00:00:00 2001 From: Oswald Auguste de Bruin Date: Fri, 20 Sep 2013 14:00:38 +0200 Subject: [PATCH 529/788] MP4 not fixed, but at least does not segfault --- lib/mp4.h | 6 +++--- lib/mp4_conv.cpp | 43 ++++++++++++++++++++++++++----------------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/lib/mp4.h b/lib/mp4.h index 72320d32..6da3d709 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -17,7 +17,7 @@ namespace MP4 { long long int size; long long int time; long long int len; - JSON::Value parts; + std::string parts; }; class DTSC2MP4Converter{ @@ -28,8 +28,8 @@ namespace MP4 { std::string sendString(); std::vector keyParts; private: - long long unsigned int curKey;//the key chunk we are currently searching for in keyParts - long long unsigned int curPart;//current part in current key + //long long unsigned int curKey;//the key chunk we are currently searching for in keyParts + //long long unsigned int curPart;//current part in current key std::map > trackBuffer; std::string stringBuffer; }; diff --git a/lib/mp4_conv.cpp b/lib/mp4_conv.cpp index e9613ef2..89b7c83f 100644 --- a/lib/mp4_conv.cpp +++ b/lib/mp4_conv.cpp @@ -54,7 +54,7 @@ namespace MP4{ temp.size = (*keyIt)["size"].asInt(); temp.time = (*keyIt)["time"].asInt(); temp.len = (*keyIt)["len"].asInt(); - temp.parts = (*keyIt)["parts"]; + temp.parts = (*keyIt)["parts"].asString(); keyParts.push_back(temp); } } @@ -99,7 +99,7 @@ namespace MP4{ //Calculating media time based on sampledelta. Probably cheating, but it works... int tmpParts = 0; for (JSON::ArrIter tmpIt = it->second["keys"].ArrBegin(); tmpIt != it->second["keys"].ArrEnd(); tmpIt++){ - tmpParts += (*tmpIt)["parts"].size(); + tmpParts += (*tmpIt)["partsize"].asInt(); } timescale = ((double)(42 * tmpParts) / (it->second["lastms"].asInt() + it->second["firstms"].asInt())) * 1000; mdhdBox.setTimeScale(timescale); @@ -199,7 +199,7 @@ namespace MP4{ int tmpCount = 1; for (int i = 0; i < it->second["keys"].size(); i++){ stssBox.setSampleNumber(tmpCount,i); - tmpCount += it->second["keys"][i]["parts"].size(); + tmpCount += it->second["keys"][i]["partsize"].asInt(); } stblBox.setContent(stssBox,2); } @@ -221,8 +221,10 @@ namespace MP4{ stszBox.setVersion(0); total = 0; for (int i = 0; i < it->second["keys"].size(); i++){ - for (int o = 0; o < it->second["keys"][i]["parts"].size(); o++){ - stszBox.setEntrySize(it->second["keys"][i]["parts"][o].asInt(), total);//in bytes in file + std::deque parsedParts; + JSON::decodeVector(it->second["keys"][i]["parts"].asString(), parsedParts); + for (unsigned int o = 0; o < parsedParts.size(); o++){ + stszBox.setEntrySize(parsedParts[o], total);//in bytes in file total++; } } @@ -236,10 +238,12 @@ namespace MP4{ //Current values are actual byte offset without header-sized offset for (unsigned int i = 0; i < keyParts.size(); i++){//for all keypart size if(keyParts[i].trackID == it->second["trackid"].asInt()){//if keypart is of current trackID - for (unsigned int o = 0; o < keyParts[i].parts.size(); o++){//add all parts to STCO + std::deque parsedParts; + JSON::decodeVector(keyParts[i].parts, parsedParts); + for (unsigned int o = 0; o < parsedParts.size(); o++){//add all parts to STCO stcoBox.setChunkOffset(totalByteOffset, total); total++; - totalByteOffset += keyParts[i].parts[o].asInt(); + totalByteOffset += parsedParts[o]; } }else{ totalByteOffset += keyParts[i].size; @@ -315,26 +319,29 @@ namespace MP4{ header << (char)((mdatSize>>24) & 0x000000FF) << (char)((mdatSize>>16) & 0x000000FF) << (char)((mdatSize>>8) & 0x000000FF) << (char)(mdatSize & 0x000000FF) << "mdat"; //std::cerr << "Header Written" << std::endl; //end of header - std::map > trackBuffer; - long long unsigned int curKey = 0;//the key chunk we are currently searching for in keyParts - long long unsigned int curPart = 0;//current part in current key return header.str(); } void DTSC2MP4Converter::parseDTSC(JSON::Value mediaPart){ + static long long unsigned int curKey = 0;//the key chunk we are currently searching for in keyParts + static long long unsigned int curPart = 0;//current part in current key //mdat output here //output cleanout buffer first //while there are requested packets in the trackBuffer:... + //std::cerr << curPart << " " << curKey << " " << keyParts.size() << " " << keyParts[curKey].trackID << "|"; + //std::cerr << trackBuffer[keyParts[curKey].trackID].empty() << std::endl; while (!trackBuffer[keyParts[curKey].trackID].empty()){ //output requested packages - if(keyParts[curKey].parts[curPart].asInt() != trackBuffer[keyParts[curKey].trackID].front()["data"].asString().size()){ - std::cerr << "Size discrepancy in buffer packet. Size: " << mediaPart["data"].asString().size() << " Expected:" << keyParts[curKey].parts[curPart].asInt() << std::endl; + std::deque parsedParts; + JSON::decodeVector(keyParts[curKey].parts, parsedParts); + if(parsedParts[curPart] != trackBuffer[keyParts[curKey].trackID].front()["data"].asString().size()){ + std::cerr << "Size discrepancy in buffer packet. Size: " << mediaPart["data"].asString().size() << " Expected:" << parsedParts[curPart] << std::endl; } stringBuffer += trackBuffer[keyParts[curKey].trackID].front()["data"].asString(); trackBuffer[keyParts[curKey].trackID].pop_front(); curPart++; - if(curPart >= keyParts[curKey].parts.size()){ + if(curPart >= parsedParts.size()){ curPart = 0; curKey++; } @@ -342,12 +349,14 @@ namespace MP4{ //after that, try to put out the JSON data directly if(keyParts[curKey].trackID == mediaPart["trackid"].asInt()){ //output JSON packet - if(keyParts[curKey].parts[curPart].asInt() != mediaPart["data"].asString().size()){ - std::cerr << "Size discrepancy in JSON packet. Size: " << mediaPart["data"].asString().size() << " Expected:" << keyParts[curKey].parts[curPart].asInt() << std::endl; + std::deque parsedParts; + JSON::decodeVector(keyParts[curKey].parts, parsedParts); + if(parsedParts[curPart] != mediaPart["data"].asStringRef().size()){ + std::cerr << "Size discrepancy in JSON packet. Size: " << mediaPart["data"].asStringRef().size() << " Expected:" << parsedParts[curPart] << std::endl; } - stringBuffer += mediaPart["data"].asString(); + stringBuffer += mediaPart["data"].asStringRef(); curPart++; - if(curPart >= keyParts[curKey].parts.size()){ + if(curPart >= parsedParts.size()){ curPart = 0; curKey++; } From d77ce7d9e8d958150d359e8f28ec251a15c6dd00 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Mon, 23 Sep 2013 09:36:27 +0200 Subject: [PATCH 530/788] Hotfix for encoding parts. --- lib/json.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/json.h b/lib/json.h index f8b7e27e..a0433e01 100644 --- a/lib/json.h +++ b/lib/json.h @@ -120,8 +120,8 @@ namespace JSON { result += (char)0xFF; tmp -= 0xFFFF; } - result += (char)tmp / 255; - result += (char)tmp % 255; + result += (char)(tmp / 256); + result += (char)(tmp % 256); } return result; } @@ -131,8 +131,9 @@ namespace JSON { result.clear(); int tmp = 0; for( int i = 0; i < input.size(); i += 2){ - tmp += input[i] + input[i + 1]; - if ((tmp % 0xFFFF) != 0 || (input[i] + input[i+1]) == 0){ + int curLen = (input[i] << 8) + input[i + 1]; + tmp += curLen; + if (curLen != 0xFFFF){ result.push_back(tmp); tmp = 0; } From 92f86e0a9cd5a44636056901a498e047ae756a8b Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Mon, 23 Sep 2013 11:02:51 +0200 Subject: [PATCH 531/788] Fixed a bug with reading first metadata through parseNext. --- lib/dtsc.cpp | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 3ea91b76..8a011b67 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -843,8 +843,27 @@ void DTSC::File::parseNext(){ return; } if (memcmp(buffer, DTSC::Magic_Header, 4) == 0){ - readHeader(lastreadpos); - jsonbuffer = metadata; + if (lastreadpos != 0){ + readHeader(lastreadpos); + jsonbuffer = metadata; + }else{ + if (fread(buffer, 4, 1, F) != 1){ + fprintf(stderr, "Could not read size\n"); + strbuffer = ""; + jsonbuffer.null(); + return; + } + uint32_t * ubuffer = (uint32_t *)buffer; + long packSize = ntohl(ubuffer[0]); + strbuffer.resize(packSize); + if (fread((void*)strbuffer.c_str(), packSize, 1, F) != 1){ + fprintf(stderr, "Could not read packet\n"); + strbuffer = ""; + jsonbuffer.null(); + return; + } + jsonbuffer = JSON::fromDTMI(strbuffer); + } return; } long long unsigned int version = 0; From eaeaad39e1274d5cfce03e50d8d9314d8981ca30 Mon Sep 17 00:00:00 2001 From: Oswald Auguste de Bruin Date: Wed, 25 Sep 2013 11:19:44 +0200 Subject: [PATCH 532/788] Midways commit --- lib/json.h | 4 ++-- lib/mp4_conv.cpp | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/json.h b/lib/json.h index a0433e01..9c6d2010 100644 --- a/lib/json.h +++ b/lib/json.h @@ -129,9 +129,9 @@ namespace JSON { template void decodeVector( std::string input, T & result ){ result.clear(); - int tmp = 0; + unsigned int tmp = 0; for( int i = 0; i < input.size(); i += 2){ - int curLen = (input[i] << 8) + input[i + 1]; + unsigned int curLen = (input[i] << 8) + input[i + 1]; tmp += curLen; if (curLen != 0xFFFF){ result.push_back(tmp); diff --git a/lib/mp4_conv.cpp b/lib/mp4_conv.cpp index 89b7c83f..8d051f8a 100644 --- a/lib/mp4_conv.cpp +++ b/lib/mp4_conv.cpp @@ -60,6 +60,16 @@ namespace MP4{ } //sort by time on keyframes for interleaving std::sort(keyParts.begin(), keyParts.end(), keyPartSort); + //next for loop is for debugging, delete when done + for (unsigned int i = 0; i < keyParts.size(); i++){ + std::deque parsedParts; + JSON::decodeVector(keyParts[i].parts, parsedParts); + std::cerr << "Header packet size: " << keyParts[i].size; + for (unsigned int o = 0; o < parsedParts.size(); o++){ + std::cerr << " " << parsedParts[o]; + } + std::cerr << std::endl; + } //start arbitrary track addition for header int boxOffset = 1; @@ -331,10 +341,12 @@ namespace MP4{ //while there are requested packets in the trackBuffer:... //std::cerr << curPart << " " << curKey << " " << keyParts.size() << " " << keyParts[curKey].trackID << "|"; //std::cerr << trackBuffer[keyParts[curKey].trackID].empty() << std::endl; + std::cerr << "Curpart: " << curPart < parsedParts; JSON::decodeVector(keyParts[curKey].parts, parsedParts); + std::cerr << "Buffer packet size: " << mediaPart["data"].asString().size() << " Expected:" << parsedParts[curPart] << std::endl; if(parsedParts[curPart] != trackBuffer[keyParts[curKey].trackID].front()["data"].asString().size()){ std::cerr << "Size discrepancy in buffer packet. Size: " << mediaPart["data"].asString().size() << " Expected:" << parsedParts[curPart] << std::endl; } @@ -351,6 +363,7 @@ namespace MP4{ //output JSON packet std::deque parsedParts; JSON::decodeVector(keyParts[curKey].parts, parsedParts); + std::cerr << "JSON packet size: " << mediaPart["data"].asStringRef().size() << " Expected:" << parsedParts[curPart] << std::endl; if(parsedParts[curPart] != mediaPart["data"].asStringRef().size()){ std::cerr << "Size discrepancy in JSON packet. Size: " << mediaPart["data"].asStringRef().size() << " Expected:" << parsedParts[curPart] << std::endl; } From ca7b7940b50d48a6015a8759f74f459e33702472 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Wed, 25 Sep 2013 11:39:33 +0200 Subject: [PATCH 533/788] Removed non-template encodeVector. --- lib/json.cpp | 15 --------------- lib/json.h | 2 -- 2 files changed, 17 deletions(-) diff --git a/lib/json.cpp b/lib/json.cpp index ab716a6b..928c410b 100644 --- a/lib/json.cpp +++ b/lib/json.cpp @@ -992,18 +992,3 @@ JSON::Value JSON::fromDTMI2(const unsigned char * data, unsigned int len, unsign tmp["trackid"] = tmpTrackID; return tmp; } - -std::string JSON::encodeVector(std::vector & toEncode){ - std::string result; - for(int i = 0; i < toEncode.size(); i++){ - long long int temp = toEncode[i]; - while( temp >= 0xFFFF){ - result += (char)0xFF; - result += (char)0xFF; - temp -= 0xFFFF; - } - result += (char)temp / 255; - result += (char)temp % 255; - } - return result; -} diff --git a/lib/json.h b/lib/json.h index 9c6d2010..fea93476 100644 --- a/lib/json.h +++ b/lib/json.h @@ -108,8 +108,6 @@ namespace JSON { Value fromString(std::string json); Value fromFile(std::string filename); - std::string encodeVector(std::vector & toEncode); - template std::string encodeVector(T begin, T end){ std::string result; From 1cc1ca870753871c5bcc0f57097991bd35fa004e Mon Sep 17 00:00:00 2001 From: Oswald Auguste de Bruin Date: Thu, 26 Sep 2013 10:26:15 +0200 Subject: [PATCH 534/788] MP4 working! --- lib/mp4.h | 1 + lib/mp4_conv.cpp | 66 ++++++++++++++++++------------------------------ 2 files changed, 25 insertions(+), 42 deletions(-) diff --git a/lib/mp4.h b/lib/mp4.h index 6da3d709..cd7b8db6 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -18,6 +18,7 @@ namespace MP4 { long long int time; long long int len; std::string parts; + long long int partsize; }; class DTSC2MP4Converter{ diff --git a/lib/mp4_conv.cpp b/lib/mp4_conv.cpp index 8d051f8a..3c19d5d7 100644 --- a/lib/mp4_conv.cpp +++ b/lib/mp4_conv.cpp @@ -49,27 +49,20 @@ namespace MP4{ keyParts.clear(); for (JSON::ObjIter trackIt = metaData["tracks"].ObjBegin(); trackIt != metaData["tracks"].ObjEnd(); trackIt++){ for (JSON::ArrIter keyIt = trackIt->second["keys"].ArrBegin(); keyIt != trackIt->second["keys"].ArrEnd(); keyIt++){ - keyPart temp; - temp.trackID = trackIt->second["trackid"].asInt(); - temp.size = (*keyIt)["size"].asInt(); - temp.time = (*keyIt)["time"].asInt(); - temp.len = (*keyIt)["len"].asInt(); - temp.parts = (*keyIt)["parts"].asString(); - keyParts.push_back(temp); + if ((*keyIt)["size"].asInt() > 0){ + keyPart temp; + temp.trackID = trackIt->second["trackid"].asInt(); + temp.size = (*keyIt)["size"].asInt(); + temp.time = (*keyIt)["time"].asInt(); + temp.len = (*keyIt)["len"].asInt(); + temp.parts = (*keyIt)["parts"].asString(); + temp.partsize = (*keyIt)["partsize"].asInt(); + keyParts.push_back(temp); + } } } //sort by time on keyframes for interleaving std::sort(keyParts.begin(), keyParts.end(), keyPartSort); - //next for loop is for debugging, delete when done - for (unsigned int i = 0; i < keyParts.size(); i++){ - std::deque parsedParts; - JSON::decodeVector(keyParts[i].parts, parsedParts); - std::cerr << "Header packet size: " << keyParts[i].size; - for (unsigned int o = 0; o < parsedParts.size(); o++){ - std::cerr << " " << parsedParts[o]; - } - std::cerr << std::endl; - } //start arbitrary track addition for header int boxOffset = 1; @@ -77,7 +70,6 @@ namespace MP4{ int timescale = 0; MP4::TRAK trakBox; MP4::TKHD tkhdBox; - //std::cerr << it->second["trackid"].asInt() << std::endl; tkhdBox.setVersion(0); tkhdBox.setFlags(15); tkhdBox.setTrackID(it->second["trackid"].asInt()); @@ -231,7 +223,7 @@ namespace MP4{ stszBox.setVersion(0); total = 0; for (int i = 0; i < it->second["keys"].size(); i++){ - std::deque parsedParts; + std::deque parsedParts; JSON::decodeVector(it->second["keys"][i]["parts"].asString(), parsedParts); for (unsigned int o = 0; o < parsedParts.size(); o++){ stszBox.setEntrySize(parsedParts[o], total);//in bytes in file @@ -248,7 +240,7 @@ namespace MP4{ //Current values are actual byte offset without header-sized offset for (unsigned int i = 0; i < keyParts.size(); i++){//for all keypart size if(keyParts[i].trackID == it->second["trackid"].asInt()){//if keypart is of current trackID - std::deque parsedParts; + std::deque parsedParts; JSON::decodeVector(keyParts[i].parts, parsedParts); for (unsigned int o = 0; o < parsedParts.size(); o++){//add all parts to STCO stcoBox.setChunkOffset(totalByteOffset, total); @@ -315,9 +307,6 @@ namespace MP4{ break; } } - /*MP4::Box temp = MP4::Box((moovBox.payload()+stcoOffsets[i]),false); - MP4::STCO & checkStcoBox = *((MP4::STCO*)(&temp)); - std::cerr << checkStcoBox.toPrettyString() << std::endl;*/ //got the STCO box, fixing values with MP4 header offset for (int j = 0; j < checkStcoBox.getEntryCount(); j++){ checkStcoBox.setChunkOffset(checkStcoBox.getChunkOffset(j) + byteOffset, j); @@ -325,9 +314,7 @@ namespace MP4{ } header << std::string(moovBox.asBox(),moovBox.boxedSize()); - //printf("%c%c%c%cmdat", (mdatSize>>24) & 0x000000FF,(mdatSize>>16) & 0x000000FF,(mdatSize>>8) & 0x000000FF,mdatSize & 0x000000FF); header << (char)((mdatSize>>24) & 0x000000FF) << (char)((mdatSize>>16) & 0x000000FF) << (char)((mdatSize>>8) & 0x000000FF) << (char)(mdatSize & 0x000000FF) << "mdat"; - //std::cerr << "Header Written" << std::endl; //end of header return header.str(); @@ -339,21 +326,17 @@ namespace MP4{ //mdat output here //output cleanout buffer first //while there are requested packets in the trackBuffer:... - //std::cerr << curPart << " " << curKey << " " << keyParts.size() << " " << keyParts[curKey].trackID << "|"; - //std::cerr << trackBuffer[keyParts[curKey].trackID].empty() << std::endl; - std::cerr << "Curpart: " << curPart < parsedParts; - JSON::decodeVector(keyParts[curKey].parts, parsedParts); - std::cerr << "Buffer packet size: " << mediaPart["data"].asString().size() << " Expected:" << parsedParts[curPart] << std::endl; - if(parsedParts[curPart] != trackBuffer[keyParts[curKey].trackID].front()["data"].asString().size()){ - std::cerr << "Size discrepancy in buffer packet. Size: " << mediaPart["data"].asString().size() << " Expected:" << parsedParts[curPart] << std::endl; - } + //std::deque parsedParts; + //JSON::decodeVector(keyParts[curKey].parts, parsedParts); + //if(parsedParts[curPart] != trackBuffer[keyParts[curKey].trackID].front()["data"].asString().size()){ + //std::cerr << "Size discrepancy in buffer packet. Size: " << trackBuffer[keyParts[curKey].trackID].front()["data"].asString().size() << " Expected:" << parsedParts[curPart] << std::endl; + //} stringBuffer += trackBuffer[keyParts[curKey].trackID].front()["data"].asString(); trackBuffer[keyParts[curKey].trackID].pop_front(); curPart++; - if(curPart >= parsedParts.size()){ + if(curPart >= keyParts[curKey].partsize){ curPart = 0; curKey++; } @@ -361,15 +344,14 @@ namespace MP4{ //after that, try to put out the JSON data directly if(keyParts[curKey].trackID == mediaPart["trackid"].asInt()){ //output JSON packet - std::deque parsedParts; - JSON::decodeVector(keyParts[curKey].parts, parsedParts); - std::cerr << "JSON packet size: " << mediaPart["data"].asStringRef().size() << " Expected:" << parsedParts[curPart] << std::endl; - if(parsedParts[curPart] != mediaPart["data"].asStringRef().size()){ - std::cerr << "Size discrepancy in JSON packet. Size: " << mediaPart["data"].asStringRef().size() << " Expected:" << parsedParts[curPart] << std::endl; - } + //std::deque parsedParts; + //JSON::decodeVector(keyParts[curKey].parts, parsedParts); + //if(parsedParts[curPart] != mediaPart["data"].asStringRef().size()){ + //std::cerr << "Size discrepancy in JSON packet. Size: " << mediaPart["data"].asStringRef().size() << " Expected:" << parsedParts[curPart] << std::endl; + //} stringBuffer += mediaPart["data"].asStringRef(); curPart++; - if(curPart >= parsedParts.size()){ + if(curPart >= keyParts[curKey].partsize){ curPart = 0; curKey++; } From 5c9574d7ebd14531493d2e274349202eabdf116f Mon Sep 17 00:00:00 2001 From: Oswald Auguste de Bruin Date: Thu, 26 Sep 2013 10:33:49 +0200 Subject: [PATCH 535/788] cleaned up code --- lib/mp4_conv.cpp | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/lib/mp4_conv.cpp b/lib/mp4_conv.cpp index 3c19d5d7..6c5a91e1 100644 --- a/lib/mp4_conv.cpp +++ b/lib/mp4_conv.cpp @@ -21,7 +21,6 @@ namespace MP4{ header << std::string(ftypBox.asBox(),ftypBox.boxedSize()); uint64_t mdatSize = 0; - //std::vector stcoOffsets; //moov box MP4::MOOV moovBox; MP4::MVHD mvhdBox; @@ -189,7 +188,7 @@ namespace MP4{ sttsBox.setVersion(0); MP4::STTSEntry newEntry; newEntry.sampleCount = tmpParts; - //42, because of reasons. + //42, Used as magic number for timescale calculation newEntry.sampleDelta = 42; sttsBox.setSTTSEntry(newEntry, 0); stblBox.setContent(sttsBox,1); @@ -253,13 +252,6 @@ namespace MP4{ } //calculating the offset where the STCO box will be in the main MOOV box //needed for probable optimise - /*stcoOffsets.push_back( - moovBox.payloadSize() + - trakBox.boxedSize() + - mdiaBox.boxedSize() + - minfBox.boxedSize() + - stblBox.boxedSize() - );*/ mdatSize = totalByteOffset; stblBox.setContent(stcoBox,4 + offset); minfBox.setContent(stblBox,2); @@ -272,7 +264,6 @@ namespace MP4{ //initial offset length ftyp, length moov + 8 unsigned long long int byteOffset = ftypBox.boxedSize() + moovBox.boxedSize() + 8; //update all STCO - //std::map STCOFix; //for tracks for (unsigned int i = 1; i < moovBox.getContentCount(); i++){ //10 lines to get the STCO box. @@ -302,7 +293,6 @@ namespace MP4{ } for (int j = 0; j < checkStblBox.getContentCount(); j++){ if (checkStblBox.getContent(j).isType("stco")){ - //STCOFix.insert( std::pair(((MP4::TKHD&)(checkTrakBox.getContent(0))).getTrackID(),(MP4::STCO&)(checkStblBox.getContent(j)))); checkStcoBox = ((MP4::STCO&)checkStblBox.getContent(j)); break; } @@ -328,11 +318,6 @@ namespace MP4{ //while there are requested packets in the trackBuffer:... while (!trackBuffer[keyParts[curKey].trackID].empty()){ //output requested packages - //std::deque parsedParts; - //JSON::decodeVector(keyParts[curKey].parts, parsedParts); - //if(parsedParts[curPart] != trackBuffer[keyParts[curKey].trackID].front()["data"].asString().size()){ - //std::cerr << "Size discrepancy in buffer packet. Size: " << trackBuffer[keyParts[curKey].trackID].front()["data"].asString().size() << " Expected:" << parsedParts[curPart] << std::endl; - //} stringBuffer += trackBuffer[keyParts[curKey].trackID].front()["data"].asString(); trackBuffer[keyParts[curKey].trackID].pop_front(); curPart++; @@ -344,11 +329,6 @@ namespace MP4{ //after that, try to put out the JSON data directly if(keyParts[curKey].trackID == mediaPart["trackid"].asInt()){ //output JSON packet - //std::deque parsedParts; - //JSON::decodeVector(keyParts[curKey].parts, parsedParts); - //if(parsedParts[curPart] != mediaPart["data"].asStringRef().size()){ - //std::cerr << "Size discrepancy in JSON packet. Size: " << mediaPart["data"].asStringRef().size() << " Expected:" << parsedParts[curPart] << std::endl; - //} stringBuffer += mediaPart["data"].asStringRef(); curPart++; if(curPart >= keyParts[curKey].partsize){ From 31f4509e262ce8e6982ef5f9edecf51dd2f77357 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 30 Sep 2013 13:24:28 +0200 Subject: [PATCH 536/788] Optimized performance of DTSC::File::atKeyFrame --- lib/dtsc.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 8a011b67..9c212d9b 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -1011,20 +1011,16 @@ bool DTSC::File::atKeyframe(){ if (getJSON().isMember("keyframe")){ return true; } - bool inHeader = false; + long long int bTime = jsonbuffer["time"].asInt(); for (std::set::iterator selectIt = selectedTracks.begin(); selectIt != selectedTracks.end(); selectIt++){ - for (JSON::ObjIter oIt = metadata["tracks"].ObjBegin(); oIt != metadata["tracks"].ObjEnd(); oIt++){ - if (oIt->second["trackid"].asInt() == (*selectIt)){ - for (JSON::ArrIter aIt = oIt->second["keys"].ArrBegin(); aIt != oIt->second["keys"].ArrEnd(); aIt++){ - if ((*aIt)["time"].asInt() == jsonbuffer["time"].asInt()){ - inHeader = true; - break; - } - } + JSON::Value & keys = getTrackById((*selectIt))["keys"]; + for (JSON::ArrIter aIt = keys.ArrBegin(); aIt != keys.ArrEnd(); aIt++){ + if ((*aIt)["time"].asInt() == bTime){ + return true; } } } - return inHeader; + return false; } void DTSC::File::selectTracks(std::set & tracks){ From 2d0f25b7be736d697a1bd6bae53ef31eb037aeef Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 30 Sep 2013 13:25:20 +0200 Subject: [PATCH 537/788] Fixed a few bugs in Util::Procs SIGCHILD handling / isActive function. --- lib/procs.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/procs.cpp b/lib/procs.cpp index 2293417e..684ef712 100644 --- a/lib/procs.cpp +++ b/lib/procs.cpp @@ -127,15 +127,12 @@ void Util::Procs::childsig_handler(int signum){ return; } -#if DEBUG >= 1 +#if DEBUG >= 5 std::string pname = plist[ret]; #endif plist.erase(ret); #if DEBUG >= 5 - if (isActive(pname)){ - Stop(pname); - } else{ - //can this ever happen? + if (!isActive(pname)){ std::cerr << "Process " << pname << " fully terminated." << std::endl; } #endif @@ -674,11 +671,14 @@ int Util::Procs::Count(){ /// Returns true if a process by this name is currently active. bool Util::Procs::isActive(std::string name){ + std::map listcopy = plist; std::map::iterator it; - for (it = plist.begin(); it != plist.end(); it++){ + for (it = listcopy.begin(); it != listcopy.end(); it++){ if (( *it).second == name){ if (kill(( *it).first, 0) == 0){ return true; + }else{ + plist.erase(( *it).first); } } } From 7ecf95e399b1a25944e89e5939ab51d9145fce8e Mon Sep 17 00:00:00 2001 From: Oswald Auguste de Bruin Date: Fri, 4 Oct 2013 12:21:45 +0200 Subject: [PATCH 538/788] MP4, converted vectors to set --- lib/mp4.h | 14 +++++++++++++- lib/mp4_conv.cpp | 30 +++++++++++++++--------------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/lib/mp4.h b/lib/mp4.h index cd7b8db6..10a844ca 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -1,6 +1,7 @@ #pragma once #include #include +#include #include #include #include @@ -13,6 +14,17 @@ /// Contains all MP4 format related code. namespace MP4 { struct keyPart{ + bool operator < (const keyPart& rhs) const { + if (time < rhs.time){ + return true; + } + if (time == rhs.time){ + if (trackID < rhs.trackID){ + return true; + } + } + return false; + } long long int trackID; long long int size; long long int time; @@ -27,7 +39,7 @@ namespace MP4 { void parseDTSC(JSON::Value mediaPart); bool sendReady(); std::string sendString(); - std::vector keyParts; + std::set keyParts; private: //long long unsigned int curKey;//the key chunk we are currently searching for in keyParts //long long unsigned int curPart;//current part in current key diff --git a/lib/mp4_conv.cpp b/lib/mp4_conv.cpp index 6c5a91e1..02e3b977 100644 --- a/lib/mp4_conv.cpp +++ b/lib/mp4_conv.cpp @@ -2,9 +2,9 @@ #include namespace MP4{ - bool keyPartSort(keyPart i, keyPart j){ + /*bool keyPartSort(keyPart i, keyPart j){ return (i.time < j.time); - } + }*/ std::string DTSC2MP4Converter::DTSCMeta2MP4Header(JSON::Value metaData){ std::stringstream header; @@ -56,12 +56,12 @@ namespace MP4{ temp.len = (*keyIt)["len"].asInt(); temp.parts = (*keyIt)["parts"].asString(); temp.partsize = (*keyIt)["partsize"].asInt(); - keyParts.push_back(temp); + keyParts.insert(temp); } } } //sort by time on keyframes for interleaving - std::sort(keyParts.begin(), keyParts.end(), keyPartSort); + //std::sort(keyParts.begin(), keyParts.end(), keyPartSort); //start arbitrary track addition for header int boxOffset = 1; @@ -237,17 +237,17 @@ namespace MP4{ uint64_t totalByteOffset = 0; //Inserting wrong values on purpose here, will be fixed later. //Current values are actual byte offset without header-sized offset - for (unsigned int i = 0; i < keyParts.size(); i++){//for all keypart size - if(keyParts[i].trackID == it->second["trackid"].asInt()){//if keypart is of current trackID + for (std::set::iterator i = keyParts.begin(); i != keyParts.end(); i++){//for all keypart size + if(i->trackID == it->second["trackid"].asInt()){//if keypart is of current trackID std::deque parsedParts; - JSON::decodeVector(keyParts[i].parts, parsedParts); + JSON::decodeVector(i->parts, parsedParts); for (unsigned int o = 0; o < parsedParts.size(); o++){//add all parts to STCO stcoBox.setChunkOffset(totalByteOffset, total); total++; totalByteOffset += parsedParts[o]; } }else{ - totalByteOffset += keyParts[i].size; + totalByteOffset += i->size; } } //calculating the offset where the STCO box will be in the main MOOV box @@ -311,27 +311,27 @@ namespace MP4{ } void DTSC2MP4Converter::parseDTSC(JSON::Value mediaPart){ - static long long unsigned int curKey = 0;//the key chunk we are currently searching for in keyParts + static std::set::iterator curKey = keyParts.begin();//the key chunk we are currently searching for in keyParts static long long unsigned int curPart = 0;//current part in current key //mdat output here //output cleanout buffer first //while there are requested packets in the trackBuffer:... - while (!trackBuffer[keyParts[curKey].trackID].empty()){ + while (!trackBuffer[curKey->trackID].empty()){ //output requested packages - stringBuffer += trackBuffer[keyParts[curKey].trackID].front()["data"].asString(); - trackBuffer[keyParts[curKey].trackID].pop_front(); + stringBuffer += trackBuffer[curKey->trackID].front()["data"].asString(); + trackBuffer[curKey->trackID].pop_front(); curPart++; - if(curPart >= keyParts[curKey].partsize){ + if(curPart >= curKey->partsize){ curPart = 0; curKey++; } } //after that, try to put out the JSON data directly - if(keyParts[curKey].trackID == mediaPart["trackid"].asInt()){ + if(curKey->trackID == mediaPart["trackid"].asInt()){ //output JSON packet stringBuffer += mediaPart["data"].asStringRef(); curPart++; - if(curPart >= keyParts[curKey].partsize){ + if(curPart >= curKey->partsize){ curPart = 0; curKey++; } From b1ec75600f488ba3de67ff8b7e10a48e18e6073b Mon Sep 17 00:00:00 2001 From: Thulinma Date: Wed, 16 Oct 2013 10:49:55 +0200 Subject: [PATCH 539/788] Optimized DTSC::File::atKeyFrame() further. --- lib/dtsc.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 9c212d9b..61974c7d 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -1012,12 +1012,10 @@ bool DTSC::File::atKeyframe(){ return true; } long long int bTime = jsonbuffer["time"].asInt(); - for (std::set::iterator selectIt = selectedTracks.begin(); selectIt != selectedTracks.end(); selectIt++){ - JSON::Value & keys = getTrackById((*selectIt))["keys"]; - for (JSON::ArrIter aIt = keys.ArrBegin(); aIt != keys.ArrEnd(); aIt++){ - if ((*aIt)["time"].asInt() == bTime){ - return true; - } + JSON::Value & keys = getTrackById(jsonbuffer["trackid"].asInt())["keys"]; + for (JSON::ArrIter aIt = keys.ArrBegin(); aIt != keys.ArrEnd(); ++aIt){ + if ((*aIt)["time"].asInt() >= bTime){ + return ((*aIt)["time"].asInt() == bTime); } } return false; From 6889e8910bbbe67138d05aa90ee97586a5ce9b08 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Wed, 16 Oct 2013 12:12:40 +0200 Subject: [PATCH 540/788] Optimize for DTSC::File::seek_time --- lib/dtsc.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 61974c7d..90b901a0 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -935,7 +935,8 @@ bool DTSC::File::seek_time(int ms, int trackNo, bool forceSeek){ tmpPos.seekTime = 0; tmpPos.bytePos = 0; } - for (JSON::ArrIter keyIt = metadata["tracks"][trackMapping[trackNo]]["keys"].ArrBegin(); keyIt != metadata["tracks"][trackMapping[trackNo]]["keys"].ArrEnd(); keyIt++){ + JSON::Value & keys = metadata["tracks"][trackMapping[trackNo]]["keys"]; + for (JSON::ArrIter keyIt = keys.ArrBegin(); keyIt != keys.ArrEnd(); keyIt++){ if ((*keyIt)["time"].asInt() > ms){ break; } From da583bedba165cad12a40bce30dc7b72d529b321 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Thu, 10 Oct 2013 09:09:27 +0200 Subject: [PATCH 541/788] Added "isFixed" function. --- lib/dtsc.cpp | 10 ++++++++++ lib/dtsc.h | 1 + 2 files changed, 11 insertions(+) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 90b901a0..9e2e8fd3 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -1038,3 +1038,13 @@ DTSC::File::~File(){ F = 0; } } + + +bool DTSC::isFixed(JSON::Value & metadata){ + for (JSON::ObjIter it = metadata["tracks"].ObjBegin(); it != metadata["tracks"].ObjEnd(); it++){ + if (!(it->second.isMember("keys") && it->second["keys"].isArray() && it->second["keys"][0u].isMember("bpos"))){ + return false; + } + } + return true; +} diff --git a/lib/dtsc.h b/lib/dtsc.h index 5e0c5278..ac07bcf1 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -50,6 +50,7 @@ /// - nalu_end (int, if set, is a end-of-sequence) /// - offset (int, unsigned version of signed int! Holds the ms offset between timestamp and proper display time for B-frames) namespace DTSC { + bool isFixed(JSON::Value & metadata); /// This enum holds all possible datatypes for DTSC packets. enum datatype{ From 7ff374ab697b2d569bbffb4ee14778b114e9974b Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 17 Oct 2013 10:02:28 +0200 Subject: [PATCH 542/788] Fixed Erik's mistakes. Ohai Erik. --- lib/dtsc.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 9e2e8fd3..e8205ca2 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -1041,6 +1041,8 @@ DTSC::File::~File(){ bool DTSC::isFixed(JSON::Value & metadata){ + if (metadata.isMember("is_fixed")){return true;} + if ( !metadata.isMember("tracks")){return false;} for (JSON::ObjIter it = metadata["tracks"].ObjBegin(); it != metadata["tracks"].ObjEnd(); it++){ if (!(it->second.isMember("keys") && it->second["keys"].isArray() && it->second["keys"][0u].isMember("bpos"))){ return false; From a7fa0eedcb3d2f3e802d9aaa491ca31531dacbf4 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 17 Oct 2013 14:25:24 +0200 Subject: [PATCH 543/788] Socket library optimization. --- lib/socket.cpp | 44 ++++++++++++++++++++++++++++++++++++-------- lib/socket.h | 2 ++ 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/lib/socket.cpp b/lib/socket.cpp index 93d53aa6..ead8610e 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -203,6 +203,12 @@ void setFDBlocking(int FD, bool blocking){ fcntl(FD, F_SETFL, flags); } +/// Internally used call to make an file descriptor blocking or not. +bool isFDBlocking(int FD){ + int flags = fcntl(FD, F_GETFL, 0); + return (flags & O_NONBLOCK); +} + /// Set this socket to be blocking (true) or nonblocking (false). void Socket::Connection::setBlocking(bool blocking){ if (sock >= 0){ @@ -216,6 +222,20 @@ void Socket::Connection::setBlocking(bool blocking){ } } +/// Set this socket to be blocking (true) or nonblocking (false). +bool Socket::Connection::isBlocking(){ + if (sock >= 0){ + return isFDBlocking(sock); + } + if (pipes[0] >= 0){ + return isFDBlocking(pipes[0]); + } + if (pipes[1] >= 0){ + return isFDBlocking(pipes[1]); + } + return false; +} + /// Close connection. The internal socket is closed and then set to -1. /// If the connection is already closed, nothing happens. void Socket::Connection::close(){ @@ -383,9 +403,12 @@ std::string Socket::Connection::getStats(std::string C){ /// Updates the downbuffer and upbuffer internal variables. /// Returns true if new data was received, false otherwise. bool Socket::Connection::spool(){ + bool bing = isBlocking(); + if (!bing){setBlocking(true);} if (upbuffer.size() > 0){ iwrite(upbuffer.get()); } + if (!bing){setBlocking(false);} /// \todo Provide better mechanism to prevent overbuffering. if (downbuffer.size() > 10000){ return true; @@ -397,11 +420,12 @@ bool Socket::Connection::spool(){ /// Updates the downbuffer and upbuffer internal variables until upbuffer is empty. /// Returns true if new data was received, false otherwise. bool Socket::Connection::flush(){ + bool bing = isBlocking(); + if (!bing){setBlocking(true);} while (upbuffer.size() > 0 && connected()){ - if ( !iwrite(upbuffer.get())){ - Util::sleep(10); //sleep 10ms - } + iwrite(upbuffer.get()); } + if (!bing){setBlocking(false);} /// \todo Provide better mechanism to prevent overbuffering. if (downbuffer.size() > 1000){ return true; @@ -420,17 +444,13 @@ Socket::Buffer & Socket::Connection::Received(){ /// Any data that could not be send will block until it can be send or the connection is severed. void Socket::Connection::SendNow(const char * data, size_t len){ while (upbuffer.size() > 0 && connected()){ - if ( !iwrite(upbuffer.get())){ - Util::sleep(1); //sleep 1ms if buffer full - } + iwrite(upbuffer.get()); } int i = iwrite(data, len); while (i < len && connected()){ int j = iwrite(data + i, std::min(len - i, (size_t)51200)); if (j > 0){ i += j; - }else{ - Util::sleep(1); //sleep 1ms and retry } } } @@ -862,6 +882,14 @@ void Socket::Server::setBlocking(bool blocking){ } } +/// Set this socket to be blocking (true) or nonblocking (false). +bool Socket::Server::isBlocking(){ + if (sock >= 0){ + return isFDBlocking(sock); + } + return false; +} + /// Close connection. The internal socket is closed and then set to -1. /// If the connection is already closed, nothing happens. void Socket::Server::close(){ diff --git a/lib/socket.h b/lib/socket.h index afdf37ff..f434874a 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -69,6 +69,7 @@ namespace Socket { //generic methods void close(); ///< Close connection. 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. void setHost(std::string host); ///< Sets hostname for connection manually. int getSocket(); ///< Returns internal socket number. @@ -111,6 +112,7 @@ namespace Socket { 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. int getSocket(); ///< Returns internal socket number. }; From e61eba35da2b2bd77fee5002aac1aede86f1e904 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 24 Oct 2013 10:49:12 +0200 Subject: [PATCH 544/788] Remember that socket optimization? This is what I meant to commit, then. Move along, nothing to see here. --- lib/socket.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/socket.cpp b/lib/socket.cpp index ead8610e..27a6e708 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -403,12 +403,9 @@ std::string Socket::Connection::getStats(std::string C){ /// Updates the downbuffer and upbuffer internal variables. /// Returns true if new data was received, false otherwise. bool Socket::Connection::spool(){ - bool bing = isBlocking(); - if (!bing){setBlocking(true);} if (upbuffer.size() > 0){ iwrite(upbuffer.get()); } - if (!bing){setBlocking(false);} /// \todo Provide better mechanism to prevent overbuffering. if (downbuffer.size() > 10000){ return true; @@ -443,6 +440,8 @@ Socket::Buffer & Socket::Connection::Received(){ /// This will send the upbuffer (if non-empty) first, then the data. /// Any data that could not be send will block until it can be send or the connection is severed. void Socket::Connection::SendNow(const char * data, size_t len){ + bool bing = isBlocking(); + if (!bing){setBlocking(true);} while (upbuffer.size() > 0 && connected()){ iwrite(upbuffer.get()); } @@ -453,6 +452,7 @@ void Socket::Connection::SendNow(const char * data, size_t len){ i += j; } } + if (!bing){setBlocking(false);} } /// Appends data to the upbuffer. From bfb2019c3a961e62901221d747c335b7443223e3 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 24 Oct 2013 11:13:54 +0200 Subject: [PATCH 545/788] Ehh.... Yeah. Again, nothing to see here. Boolean logic. Whee. --- lib/socket.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/socket.cpp b/lib/socket.cpp index 27a6e708..ad27fc19 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -206,7 +206,7 @@ void setFDBlocking(int FD, bool blocking){ /// Internally used call to make an file descriptor blocking or not. bool isFDBlocking(int FD){ int flags = fcntl(FD, F_GETFL, 0); - return (flags & O_NONBLOCK); + return !(flags & O_NONBLOCK); } /// Set this socket to be blocking (true) or nonblocking (false). From bd5cce8d6ac45813ec757e9c92ad023fb34c633d Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 29 Oct 2013 16:17:01 +0100 Subject: [PATCH 546/788] Fixed JSON lib errors with characters >= 0x80 --- lib/json.cpp | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/lib/json.cpp b/lib/json.cpp index 928c410b..cfd5b4bd 100644 --- a/lib/json.cpp +++ b/lib/json.cpp @@ -8,14 +8,14 @@ #include //for memcpy #include //for htonl -static int c2hex(char c){ +static inline char c2hex(char 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; } -static char hex2c(char c){ +static inline char hex2c(char c){ if (c < 10){return '0' + c;} if (c < 16){return 'A' + (c - 10);} return '0'; @@ -25,7 +25,8 @@ static std::string read_string(int separator, std::istream & fromstream){ std::string out; bool escaped = false; while (fromstream.good()){ - int c = fromstream.get(); + char c; + fromstream.get(c); if (c == '\\'){ escaped = true; continue; @@ -48,15 +49,18 @@ static std::string read_string(int separator, std::istream & fromstream){ out += '\t'; break; case 'u': { - int d1 = fromstream.get(); - int d2 = fromstream.get(); - int d3 = fromstream.get(); - int d4 = fromstream.get(); - c = c2hex(d4) + (c2hex(d3) << 4) + (c2hex(d2) << 8) + (c2hex(d1) << 16); + char d1, d2, d3, d4; + fromstream.get(d1); + fromstream.get(d2); + fromstream.get(d3); + fromstream.get(d4); + out.append(1, (c2hex(d4) + (c2hex(d3) << 4))); + //We ignore the upper two characters. + // + (c2hex(d2) << 8) + (c2hex(d1) << 16) break; } default: - out += (char)c; + out.append(1, c); break; } escaped = false; @@ -64,7 +68,7 @@ static std::string read_string(int separator, std::istream & fromstream){ if (c == separator){ return out; }else{ - out += (char)c; + out.append(1, c); } } } @@ -74,7 +78,7 @@ static std::string read_string(int separator, std::istream & fromstream){ static std::string string_escape(const std::string val){ std::string out = "\""; for (unsigned int i = 0; i < val.size(); ++i){ - switch (val[i]){ + switch (val.data()[i]){ case '"': out += "\\\""; break; @@ -97,12 +101,12 @@ static std::string string_escape(const std::string val){ out += "\\t"; break; default: - if (val[i] < 32 || val[i] > 126){ + if (val.data()[i] < 32 || val.data()[i] > 126){ out += "\\u00"; - out += hex2c((val[i] >> 4) & 0xf); - out += hex2c(val[i] & 0xf); + out += hex2c((val.data()[i] >> 4) & 0xf); + out += hex2c(val.data()[i] & 0xf); }else{ - out += val[i]; + out += val.data()[i]; } break; } From 73910bd343d898ec33c400ea7b0322d56ab1a249 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 31 Oct 2013 14:03:58 +0100 Subject: [PATCH 547/788] Fixed socket error reporting. --- lib/socket.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/socket.cpp b/lib/socket.cpp index ad27fc19..6b87157f 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -533,8 +533,9 @@ int Socket::Connection::iwrite(const void * buffer, int len){ default: if (errno != EPIPE){ Error = true; + remotehost = strerror(errno); #if DEBUG >= 2 - fprintf(stderr, "Could not iwrite data! Error: %s\n", strerror(errno)); + fprintf(stderr, "Could not iwrite data! Error: %s\n", remotehost.c_str()); #endif } close(); @@ -572,8 +573,9 @@ int Socket::Connection::iread(void * buffer, int len){ default: if (errno != EPIPE){ Error = true; + remotehost = strerror(errno); #if DEBUG >= 2 - fprintf(stderr, "Could not iread data! Error: %s\n", strerror(errno)); + fprintf(stderr, "Could not iread data! Error: %s\n", remotehost.c_str()); #endif } close(); From 1e9128c23dc19530517042f20009e4df21158011 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 31 Oct 2013 14:04:12 +0100 Subject: [PATCH 548/788] Fixed track filtering in live streams. --- lib/dtsc.cpp | 16 ++++++++++------ lib/dtsc.h | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index e8205ca2..c887cfed 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -513,16 +513,20 @@ DTSC::livePos DTSC::Stream::msSeek(unsigned int ms, std::set & allowedTrack return result; } -bool DTSC::Stream::isNewest(DTSC::livePos & pos){ - return (buffers.upper_bound(pos) == buffers.end()); +/// Returns whether the current position is the last currently available position within allowedTracks. +/// Simply returns the result of getNext(pos, allowedTracks) == pos +bool DTSC::Stream::isNewest(DTSC::livePos & pos, std::set & allowedTracks){ + return getNext(pos, allowedTracks) == pos; } +/// Returns the next available position within allowedTracks, or the current position if no next is availble. DTSC::livePos DTSC::Stream::getNext(DTSC::livePos & pos, std::set & allowedTracks){ - if (!isNewest(pos)){ - return (buffers.upper_bound(pos))->first; - }else{ - return livePos(); + std::map::iterator iter = buffers.upper_bound(pos); + while (iter != buffers.end()){ + if (allowedTracks.count(iter->first.trackID)){return iter->first;} + iter++; } + return pos; } /// Properly cleans up the object for erasing. diff --git a/lib/dtsc.h b/lib/dtsc.h index ac07bcf1..24156a25 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -211,7 +211,7 @@ namespace DTSC { int canSeekms(unsigned int ms); livePos msSeek(unsigned int ms, std::set & allowedTracks); void setBufferTime(unsigned int ms); - bool isNewest(DTSC::livePos & pos); + bool isNewest(DTSC::livePos & pos, std::set & allowedTracks); DTSC::livePos getNext(DTSC::livePos & pos, std::set & allowedTracks); void endStream(); void waitForMeta(Socket::Connection & sourceSocket); From 7930d3869ec5fe904ffe8102699e9d80daaf7986 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Mon, 28 Oct 2013 09:16:59 +0100 Subject: [PATCH 549/788] Updates firstms for live. --- lib/dtsc.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index c887cfed..9c8f2c88 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -356,6 +356,7 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ if (metadata["tracks"][track]["frags"].size() > 0){ // delete fragments of which the beginning can no longer be reached while (metadata["tracks"][track]["frags"].size() > 0 && metadata["tracks"][track]["frags"][0u]["num"].asInt() < metadata["tracks"][track]["keys"][0u]["num"].asInt()){ + metadata["tracks"][track]["firstms"] = metadata["tracks"][track]["firstms"].asInt() + metadata["tracks"][track]["frags"][0u]["dur"].asInt(); metadata["tracks"][track]["frags"].shrink(metadata["tracks"][track]["frags"].size() - 1); // increase the missed fragments counter metadata["tracks"][track]["missed_frags"] = metadata["tracks"][track]["missed_frags"].asInt() + 1; From 01d008a56cf8997edcd6c7fc34d354350a0d8bf0 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Fri, 1 Nov 2013 12:22:14 +0100 Subject: [PATCH 550/788] Small fixes on DTSC lib --- lib/dtsc.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 9c8f2c88..73fe5606 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -264,7 +264,7 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ int keySize = metadata["tracks"][newTrack]["keys"].size(); if (buffercount > 1){ #define prevKey metadata["tracks"][newTrack]["keys"][keySize - 1] - if (newPack.isMember("keyframe") || !keySize || (datapointertype != VIDEO && newPack["time"].asInt() - 2000 > prevKey["time"].asInt())){ + if (newPack.isMember("keyframe") || !keySize || (datapointertype != VIDEO && newPack["time"].asInt() - 5000 > prevKey["time"].asInt())){ updateMeta = true; metadata["tracks"][newTrack]["lastms"] = newPack["time"]; keyframes[newPos.trackID].insert(newPos); @@ -503,12 +503,12 @@ DTSC::livePos DTSC::Stream::msSeek(unsigned int ms, std::set & allowedTrack } for (std::map::iterator bIt = buffers.begin(); bIt != buffers.end(); bIt++){ if (seekTracks.find(bIt->first.trackID) != seekTracks.end()){ - if (bIt->second.isMember("keyframe")){ + // if (bIt->second.isMember("keyframe")){ result = bIt->first; if (bIt->first.seekTime >= ms){ return result; } - } + //} } } return result; From b78e9bc5626e663681fc5d58690debe34022d079 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 14 Nov 2013 12:24:11 +0100 Subject: [PATCH 551/788] Finally fixed long standing RTMP sync issue. Also added support for getting/setting FLV tag offsets. --- lib/flv_tag.cpp | 28 +++++++++++++++++++++------- lib/flv_tag.h | 6 ++++-- lib/rtmpchunks.cpp | 5 ++++- 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index 6f296941..49643ba1 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -262,6 +262,25 @@ std::string FLV::Tag::tagType(){ return R.str(); } //FLV::Tag::tagtype +/// Returns the 24-bit offset of this tag. +/// Returns 0 if the tag isn't H264 +int FLV::Tag::offset(){ + if (data[11] & 0x0F == 7){ + return (((data[13] << 16) + (data[14] << 8) + data[15]) << 8) >> 8; + }else{ + return 0; + } +} //offset getter + +/// Sets the 24-bit offset of this tag. +/// Ignored if the tag isn't H264 +void FLV::Tag::offset(int o){ + if (data[11] & 0x0F != 7){return;} + data[13] = (o >> 16) & 0xFF; + data[14] = (o >> 8) & 0XFF; + data[15] = o & 0xFF; +} //offset setter + /// Returns the 32-bit timestamp of this tag. unsigned int FLV::Tag::tagTime(){ return (data[4] << 16) + (data[5] << 8) + data[6] + (data[7] << 24); @@ -401,10 +420,7 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ }else{ data[12] = 2; } - int offset = S.getPacket()["offset"].asInt(); - data[13] = (offset >> 16) & 0xFF; - data[14] = (offset >> 8) & 0XFF; - data[15] = offset & 0xFF; + offset(S.getPacket()["offset"].asInt()); } data[11] = 0; if (track.isMember("codec") && track["codec"].asStringRef() == "H264"){ @@ -1174,9 +1190,7 @@ JSON::Value FLV::Tag::toJSON(JSON::Value & metadata){ pack_out["nalu_end"] = 1; break; } - int offset = (data[13] << 16) + (data[14] << 8) + data[15]; - offset = (offset << 8) >> 8; - pack_out["offset"] = offset; + pack_out["offset"] = offset(); if (len < 21){ return JSON::Value(); } diff --git a/lib/flv_tag.h b/lib/flv_tag.h index b2815aee..1c1e0446 100644 --- a/lib/flv_tag.h +++ b/lib/flv_tag.h @@ -35,8 +35,10 @@ namespace FLV { 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(); ///< Returns the 32-bit timestamp of this tag. - void tagTime(unsigned int T); ///< Sets the 32-bit timestamp of this tag. + 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. diff --git a/lib/rtmpchunks.cpp b/lib/rtmpchunks.cpp index a9dd78cb..25e0dbf8 100644 --- a/lib/rtmpchunks.cpp +++ b/lib/rtmpchunks.cpp @@ -423,7 +423,10 @@ std::string & RTMPStream::SendMedia(unsigned char msg_type_id, unsigned char * d /// \param tag FLV::Tag with media to send. std::string & RTMPStream::SendMedia(FLV::Tag & tag){ static RTMPStream::Chunk ch; - ch.cs_id = ((unsigned char)tag.data[0]); + //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 = tag.len - 15; ch.real_len = tag.len - 15; From 558123e11e024734d2c118d888afd71395eb7bdb Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 15 Nov 2013 15:28:39 +0100 Subject: [PATCH 552/788] Fixes to FLV library. --- lib/flv_tag.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index 49643ba1..231f0ab7 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -265,17 +265,12 @@ std::string FLV::Tag::tagType(){ /// Returns the 24-bit offset of this tag. /// Returns 0 if the tag isn't H264 int FLV::Tag::offset(){ - if (data[11] & 0x0F == 7){ - return (((data[13] << 16) + (data[14] << 8) + data[15]) << 8) >> 8; - }else{ - return 0; - } + return (((data[13] << 16) + (data[14] << 8) + data[15]) << 8) >> 8; } //offset getter /// Sets the 24-bit offset of this tag. /// Ignored if the tag isn't H264 void FLV::Tag::offset(int o){ - if (data[11] & 0x0F != 7){return;} data[13] = (o >> 16) & 0xFF; data[14] = (o >> 8) & 0XFF; data[15] = o & 0xFF; From 7d5942adf1a0220738992e396f96a4ca552ccbb1 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 18 Nov 2013 13:13:21 +0100 Subject: [PATCH 553/788] Added support for sending JSON::Values over a socket in DTMI format without needing to convert in advance. --- lib/json.cpp | 116 +++++++++++++++++++++++++++++++++++++++++++++++++-- lib/json.h | 5 ++- 2 files changed, 116 insertions(+), 5 deletions(-) diff --git a/lib/json.cpp b/lib/json.cpp index cfd5b4bd..a0c19ed9 100644 --- a/lib/json.cpp +++ b/lib/json.cpp @@ -475,7 +475,7 @@ const JSON::Value & JSON::Value::operator[](unsigned int i) const{ /// Packs to a std::string for transfer over the network. /// If the object is a container type, this function will call itself recursively and contain all contents. /// As a side effect, this function clear the internal buffer of any object-types. -std::string JSON::Value::toPacked(){ +std::string JSON::Value::toPacked() const{ std::string r; if (isInt() || isNull() || isBool()){ r += 0x01; @@ -500,7 +500,7 @@ std::string JSON::Value::toPacked(){ if (isObject()){ r += 0xE0; if (objVal.size() > 0){ - for (JSON::ObjIter it = objVal.begin(); it != objVal.end(); it++){ + for (JSON::ObjConstIter it = objVal.begin(); it != objVal.end(); it++){ if (it->first.size() > 0){ r += it->first.size() / 256; r += it->first.size() % 256; @@ -512,11 +512,10 @@ std::string JSON::Value::toPacked(){ r += (char)0x0; r += (char)0x0; r += (char)0xEE; - strVal.clear(); } if (isArray()){ r += 0x0A; - for (JSON::ArrIter it = arrVal.begin(); it != arrVal.end(); it++){ + for (JSON::ArrConstIter it = arrVal.begin(); it != arrVal.end(); it++){ r += it->toPacked(); } r += (char)0x0; @@ -527,6 +526,115 @@ std::string JSON::Value::toPacked(){ } //toPacked +/// Packs and transfers over the network. +/// If the object is a container type, this function will call itself recursively for all contents. +void JSON::Value::sendTo(Socket::Connection & socket) const{ + if (isInt() || isNull() || isBool()){ + socket.SendNow("\001", 1); + int tmpHalf = htonl((int)(intVal >> 32)); + socket.SendNow((char*)&tmpHalf, 4); + tmpHalf = htonl((int)(intVal & 0xFFFFFFFF)); + socket.SendNow((char*)&tmpHalf, 4); + return; + } + if (isString()){ + socket.SendNow("\002", 1); + int tmpVal = htonl((int)strVal.size()); + socket.SendNow((char*)&tmpVal, 4); + socket.SendNow(strVal); + return; + } + if (isObject()){ + if (isMember("trackid") && isMember("time")){ + unsigned int trackid = objVal.find("trackid")->second.asInt(); + long long time = objVal.find("time")->second.asInt(); + unsigned int size = 16; + if (objVal.size() > 0){ + for (JSON::ObjConstIter it = objVal.begin(); it != objVal.end(); it++){ + if (it->first.size() > 0 && it->first != "trackid" && it->first != "time" && it->first != "datatype"){ + size += 2+it->first.size()+it->second.packedSize(); + } + } + } + socket.SendNow("DTP2", 4); + size = htonl(size); + socket.SendNow((char*)&size, 4); + trackid = htonl(trackid); + socket.SendNow((char*)&trackid, 4); + int tmpHalf = htonl((int)(time >> 32)); + socket.SendNow((char*)&tmpHalf, 4); + tmpHalf = htonl((int)(time & 0xFFFFFFFF)); + socket.SendNow((char*)&tmpHalf, 4); + socket.SendNow("\340", 1); + if (objVal.size() > 0){ + for (JSON::ObjConstIter it = objVal.begin(); it != objVal.end(); it++){ + if (it->first.size() > 0 && it->first != "trackid" && it->first != "time" && it->first != "datatype"){ + char sizebuffer[2] = {0, 0}; + sizebuffer[0] = (it->first.size() >> 8) & 0xFF; + sizebuffer[1] = it->first.size() & 0xFF; + socket.SendNow(sizebuffer, 2); + socket.SendNow(it->first); + it->second.sendTo(socket); + } + } + } + socket.SendNow("\000\000\356", 3); + return; + } + socket.SendNow("\340", 1); + if (objVal.size() > 0){ + for (JSON::ObjConstIter it = objVal.begin(); it != objVal.end(); it++){ + if (it->first.size() > 0){ + char sizebuffer[2] = {0, 0}; + sizebuffer[0] = (it->first.size() >> 8) & 0xFF; + sizebuffer[1] = it->first.size() & 0xFF; + socket.SendNow(sizebuffer, 2); + socket.SendNow(it->first); + it->second.sendTo(socket); + } + } + } + socket.SendNow("\000\000\356", 3); + return; + } + if (isArray()){ + socket.SendNow("\012", 1); + for (JSON::ArrConstIter it = arrVal.begin(); it != arrVal.end(); it++){ + it->sendTo(socket); + } + socket.SendNow("\000\000\356", 3); + return; + } +}//sendTo + +/// Returns the packed size of this Value. +unsigned int JSON::Value::packedSize() const{ + if (isInt() || isNull() || isBool()){ + return 9; + } + if (isString()){ + return 5 + strVal.size(); + } + if (isObject()){ + unsigned int ret = 4; + if (objVal.size() > 0){ + for (JSON::ObjConstIter it = objVal.begin(); it != objVal.end(); it++){ + if (it->first.size() > 0){ + ret += 2+it->first.size()+it->second.packedSize(); + } + } + } + return ret; + } + if (isArray()){ + unsigned int ret = 4; + for (JSON::ArrConstIter it = arrVal.begin(); it != arrVal.end(); it++){ + ret += it->packedSize(); + } + return ret; + } +}//packedSize + /// Pre-packs any object-type JSON::Value to a std::string for transfer over the network, including proper DTMI header. /// Non-object-types will print an error to stderr. /// The internal buffer is guaranteed to be up-to-date after this function is called. diff --git a/lib/json.h b/lib/json.h index fea93476..2f4db947 100644 --- a/lib/json.h +++ b/lib/json.h @@ -6,6 +6,7 @@ #include #include #include +#include "socket.h" //empty definition of DTSC::Stream so it can be a friend. namespace DTSC { @@ -73,7 +74,9 @@ namespace JSON { const Value & operator[](const char * i) const; const Value & operator[](unsigned int i) const; //handy functions and others - std::string toPacked(); + std::string toPacked() const; + void sendTo(Socket::Connection & socket) const; + unsigned int packedSize() const; void netPrepare(); std::string & toNetPacked(); std::string toString() const; From 12dd880bc121e1d38c7f6cd51e2df4b5d84b0346 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 18 Nov 2013 13:13:53 +0100 Subject: [PATCH 554/788] Refactored DTSC::Stream to prepare for proper inheritance in the Buffer process. --- lib/dtsc.cpp | 69 ++++++++++++++++++++++++++++++---------------------- lib/dtsc.h | 41 +++---------------------------- 2 files changed, 44 insertions(+), 66 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 73fe5606..b50e7aa4 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -27,6 +27,10 @@ DTSC::Stream::Stream(unsigned int rbuffers, unsigned int bufferTime){ buffertime = bufferTime; } +/// This function does nothing, it's supposed to be overridden. +/// It will be called right before a buffer position is deleted. +void DTSC::Stream::deletionCallback(livePos deleting){} + /// Returns the time in milliseconds of the last received packet. /// This is _not_ the time this packet was received, only the stored time. unsigned int DTSC::Stream::getTime(){ @@ -213,8 +217,16 @@ void DTSC::Stream::waitForMeta(Socket::Connection & sourceSocket){ } } +/// Resets the stream by clearing the buffers and keyframes, making sure to call the deletionCallback first. +void DTSC::Stream::resetStream(){ + for (std::map::iterator it = buffers.begin(); it != buffers.end(); it++){ + deletionCallback(it->first); + } + buffers.clear(); + keyframes.clear(); +} + void DTSC::Stream::addPacket(JSON::Value & newPack){ - bool updateMeta = false; long long unsigned int now = Util::getMS(); livePos newPos; newPos.trackID = newPack["trackid"].asInt(); @@ -223,15 +235,13 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ livePos lastPos = buffers.rbegin()->first; if (newPos < lastPos){ if ((lastPos.seekTime > 1000) && newPos.seekTime < lastPos.seekTime - 1000){ - metadata["reset"] = 1LL; - buffers.clear(); - keyframes.clear(); + resetStream(); }else{ newPos.seekTime = lastPos.seekTime+1; } } }else{ - buffers.clear(); + resetStream(); } std::string newTrack = trackMapping[newPos.trackID]; while (buffers.count(newPos) > 0){ @@ -265,7 +275,6 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ if (buffercount > 1){ #define prevKey metadata["tracks"][newTrack]["keys"][keySize - 1] if (newPack.isMember("keyframe") || !keySize || (datapointertype != VIDEO && newPack["time"].asInt() - 5000 > prevKey["time"].asInt())){ - updateMeta = true; metadata["tracks"][newTrack]["lastms"] = newPack["time"]; keyframes[newPos.trackID].insert(newPos); JSON::Value key; @@ -337,37 +346,39 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ buffercount = buffers.size(); if (buffercount < 2){buffercount = 2;} } - if (updateMeta && metadata["buffer_window"].asInt() < timeBuffered){ + if (metadata["buffer_window"].asInt() < timeBuffered){ metadata["buffer_window"] = (long long int)timeBuffered; } } while (buffers.size() > buffercount){ - if (buffercount > 1 && keyframes[buffers.begin()->first.trackID].count(buffers.begin()->first)){ - updateMeta = true; - //if there are < 3 keyframes, throwing one away would mean less than 2 left. - if (keyframes[buffers.begin()->first.trackID].size() < 3){ - std::cerr << "Warning - track " << buffers.begin()->first.trackID << " doesn't have enough keyframes to be reliably served." << std::endl; - } - std::string track = trackMapping[buffers.begin()->first.trackID]; - keyframes[buffers.begin()->first.trackID].erase(buffers.begin()->first); - int keySize = metadata["tracks"][track]["keys"].size(); - metadata["tracks"][track]["keys"].shrink(keySize - 1); - if (metadata["tracks"][track]["frags"].size() > 0){ - // delete fragments of which the beginning can no longer be reached - while (metadata["tracks"][track]["frags"].size() > 0 && metadata["tracks"][track]["frags"][0u]["num"].asInt() < metadata["tracks"][track]["keys"][0u]["num"].asInt()){ - metadata["tracks"][track]["firstms"] = metadata["tracks"][track]["firstms"].asInt() + metadata["tracks"][track]["frags"][0u]["dur"].asInt(); - metadata["tracks"][track]["frags"].shrink(metadata["tracks"][track]["frags"].size() - 1); - // increase the missed fragments counter - metadata["tracks"][track]["missed_frags"] = metadata["tracks"][track]["missed_frags"].asInt() + 1; - } + cutOneBuffer(); + } +} + +/// Deletes a the first part of the buffer, updating the keyframes list and metadata as required. +/// Will print a warning to std::cerr if a track has less than 2 keyframes left because of this. +void DTSC::Stream::cutOneBuffer(){ + if (buffercount > 1 && keyframes[buffers.begin()->first.trackID].count(buffers.begin()->first)){ + //if there are < 3 keyframes, throwing one away would mean less than 2 left. + if (keyframes[buffers.begin()->first.trackID].size() < 3){ + std::cerr << "Warning - track " << buffers.begin()->first.trackID << " doesn't have enough keyframes to be reliably served." << std::endl; + } + std::string track = trackMapping[buffers.begin()->first.trackID]; + keyframes[buffers.begin()->first.trackID].erase(buffers.begin()->first); + int keySize = metadata["tracks"][track]["keys"].size(); + metadata["tracks"][track]["keys"].shrink(keySize - 1); + if (metadata["tracks"][track]["frags"].size() > 0){ + // delete fragments of which the beginning can no longer be reached + while (metadata["tracks"][track]["frags"].size() > 0 && metadata["tracks"][track]["frags"][0u]["num"].asInt() < metadata["tracks"][track]["keys"][0u]["num"].asInt()){ + metadata["tracks"][track]["firstms"] = metadata["tracks"][track]["firstms"].asInt() + metadata["tracks"][track]["frags"][0u]["dur"].asInt(); + metadata["tracks"][track]["frags"].shrink(metadata["tracks"][track]["frags"].size() - 1); + // increase the missed fragments counter + metadata["tracks"][track]["missed_frags"] = metadata["tracks"][track]["missed_frags"].asInt() + 1; } } - buffers.erase(buffers.begin()); - } - if (updateMeta){ - //metadata.netPrepare(); } + buffers.erase(buffers.begin()); } /// Returns a direct pointer to the data attribute of the last received packet, if available. diff --git a/lib/dtsc.h b/lib/dtsc.h index 24156a25..2b00cc94 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -13,42 +13,6 @@ #include "socket.h" #include "timing.h" -/// Holds all DDVTECH Stream Container classes and parsers. -///length (int, length in seconds, if available) -///video: -/// - codec (string: H264, H263, VP6) -/// - width (int, pixels) -/// - height (int, pixels) -/// - fpks (int, frames per kilosecond (FPS * 1000)) -/// - bps (int, bytes per second) -/// - init (string, init data) -/// - keycount (int, count of keyframes) -/// - keyms (int, average ms per keyframe) -/// - keyvar (int, max ms per keyframe variance) -/// - keys (array of byte position ints - first is first keyframe, last is last keyframe, in between have ~equal spacing) -/// -///audio: -/// - codec (string: AAC, MP3) -/// - rate (int, Hz) -/// - size (int, bitsize) -/// - bps (int, bytes per second) -/// - channels (int, channelcount) -/// - init (string, init data) -/// -///All packets: -/// - datatype (string: audio, video, meta (unused)) -/// - data (string: data) -/// - time (int: ms into video) -/// -///Video packets: -/// - keyframe (int, if set, is a seekable keyframe) -/// - interframe (int, if set, is a non-seekable interframe) -/// - disposableframe (int, if set, is a disposable interframe) -/// -///H264 video packets: -/// - nalu (int, if set, is a nalu) -/// - nalu_end (int, if set, is a end-of-sequence) -/// - offset (int, unsigned version of signed int! Holds the ms offset between timestamp and proper display time for B-frames) namespace DTSC { bool isFixed(JSON::Value & metadata); @@ -215,7 +179,9 @@ namespace DTSC { DTSC::livePos getNext(DTSC::livePos & pos, std::set & allowedTracks); void endStream(); void waitForMeta(Socket::Connection & sourceSocket); - private: + protected: + void cutOneBuffer(); + void resetStream(); std::map buffers; std::map > keyframes; void addPacket(JSON::Value & newPack); @@ -223,5 +189,6 @@ namespace DTSC { unsigned int buffercount; unsigned int buffertime; std::map trackMapping; + void deletionCallback(livePos deleting); }; } From ca1ec8707af645b7fc5b2dc273e49ad5aecf33f1 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 19 Nov 2013 13:29:57 +0100 Subject: [PATCH 555/788] Fixed sending metadata tracks through JSON::Value::sendTo(). --- lib/json.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/json.cpp b/lib/json.cpp index a0c19ed9..183415f9 100644 --- a/lib/json.cpp +++ b/lib/json.cpp @@ -581,6 +581,11 @@ void JSON::Value::sendTo(Socket::Connection & socket) const{ socket.SendNow("\000\000\356", 3); return; } + if (isMember("tracks")){ + socket.SendNow("DTSC", 4); + unsigned int size = htonl(packedSize()); + socket.SendNow((char*)&size, 4); + } socket.SendNow("\340", 1); if (objVal.size() > 0){ for (JSON::ObjConstIter it = objVal.begin(); it != objVal.end(); it++){ From 071cba94f91514f4a142198ffc8985a6061bfcb1 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 21 Nov 2013 13:34:01 +0100 Subject: [PATCH 556/788] Added DTSC::File error checking --- lib/dtsc.cpp | 8 ++++++++ lib/dtsc.h | 1 + 2 files changed, 9 insertions(+) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index b50e7aa4..01063ff6 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -575,11 +575,19 @@ DTSC::File & DTSC::File::operator =(const File & rhs){ memcpy(buffer, rhs.buffer, 4); } +DTSC::File::operator bool() const{ + return F; +} + /// Open a filename for DTSC reading/writing. /// If create is true and file does not exist, attempt to create. DTSC::File::File(std::string filename, bool create){ if (create){ F = fopen(filename.c_str(), "w+b"); + if(!F){ + std::cerr << "Could not create file" << filename << ": " << strerror(errno) << std::endl; + return; + } //write an empty header fseek(F, 0, SEEK_SET); fwrite(DTSC::Magic_Header, 4, 1, F); diff --git a/lib/dtsc.h b/lib/dtsc.h index 2b00cc94..28fe8993 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -60,6 +60,7 @@ namespace DTSC { File(const File & rhs); File(std::string filename, bool create = false); File & operator = (const File & rhs); + operator bool() const; ~File(); JSON::Value & getMeta(); long long int getLastReadPos(); From 79b4772c3dc04d69c3537565c3222e62aa317672 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Wed, 27 Nov 2013 15:29:49 +0100 Subject: [PATCH 557/788] Metadata upgrades. --- lib/Makefile.am | 2 +- lib/dtsc.cpp | 267 +++++----------- lib/dtsc.h | 223 +++++++++++--- lib/dtscmeta.cpp | 786 +++++++++++++++++++++++++++++++++++++++++++++++ lib/flv_tag.cpp | 165 ++++------ lib/flv_tag.h | 8 +- lib/ogg.h | 8 +- 7 files changed, 1116 insertions(+), 343 deletions(-) create mode 100644 lib/dtscmeta.cpp diff --git a/lib/Makefile.am b/lib/Makefile.am index cc86a62f..aaef3331 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -3,7 +3,7 @@ libmist_1_0_la_SOURCES=amf.h amf.cpp libmist_1_0_la_SOURCES+=auth.h auth.cpp libmist_1_0_la_SOURCES+=base64.h base64.cpp libmist_1_0_la_SOURCES+=config.h config.cpp -libmist_1_0_la_SOURCES+=dtsc.h dtsc.cpp +libmist_1_0_la_SOURCES+=dtsc.h dtsc.cpp dtscmeta.cpp libmist_1_0_la_SOURCES+=flv_tag.h flv_tag.cpp libmist_1_0_la_SOURCES+=http_parser.h http_parser.cpp libmist_1_0_la_SOURCES+=json.h json.cpp diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 01063ff6..87a29c94 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -51,15 +51,8 @@ bool DTSC::Stream::parsePacket(std::string & buffer){ return false; } unsigned int i = 0; - metadata = JSON::fromDTMI((unsigned char*)buffer.c_str() + 8, len, i); - metadata.removeMember("moreheader"); - metadata.netPrepare(); - trackMapping.clear(); - if (metadata.isMember("tracks")){ - for (JSON::ObjIter it = metadata["tracks"].ObjBegin(); it != metadata["tracks"].ObjEnd(); it++){ - trackMapping.insert(std::pair(it->second["trackid"].asInt(),it->first)); - } - } + JSON::Value meta = JSON::fromDTMI((unsigned char*)buffer.c_str() + 8, len, i); + metadata = Meta(meta); buffer.erase(0, len + 8); if (buffer.length() <= 8){ return false; @@ -127,17 +120,8 @@ bool DTSC::Stream::parsePacket(Socket::Buffer & buffer){ } unsigned int i = 0; std::string wholepacket = buffer.remove(len + 8); - metadata = JSON::fromDTMI((unsigned char*)wholepacket.c_str() + 8, len, i); - metadata.removeMember("moreheader"); - if (buffercount > 1){ - metadata.netPrepare(); - } - if (metadata.isMember("tracks")){ - trackMapping.clear(); - for (JSON::ObjIter it = metadata["tracks"].ObjBegin(); it != metadata["tracks"].ObjEnd(); it++){ - trackMapping.insert(std::pair(it->second["trackid"].asInt(),it->first)); - } - } + JSON::Value meta = JSON::fromDTMI((unsigned char*)wholepacket.c_str() + 8, len, i); + metadata = Meta(meta); //recursively calls itself until failure or data packet instead of header return parsePacket(buffer); } @@ -179,18 +163,13 @@ bool DTSC::Stream::parsePacket(Socket::Buffer & buffer){ /// Adds a keyframe packet to all tracks, so the stream can be fully played. void DTSC::Stream::endStream(){ - if (metadata.isMember("tracks") && metadata["tracks"].size() > 0){ - JSON::Value trackData = metadata["tracks"]; - for (JSON::ObjIter it = trackData.ObjBegin(); it != trackData.ObjEnd(); it++){ - if(it->second.isMember("lastms") && it->second.isMember("trackid")){ // TODO - JSON::Value newPack; - newPack["time"] = it->second["lastms"]; - newPack["trackid"] = it->second["trackid"]; - newPack["keyframe"] = 1ll; - newPack["data"] = ""; - addPacket(newPack); - } - } + for (std::map::iterator it = metadata.tracks.begin(); it != metadata.tracks.end(); it++){ + JSON::Value newPack; + newPack["time"] = it->second.lastms; + newPack["trackid"] = it->second.trackID; + newPack["keyframe"] = 1ll; + newPack["data"] = ""; + addPacket(newPack); } } @@ -243,7 +222,7 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ }else{ resetStream(); } - std::string newTrack = trackMapping[newPos.trackID]; + std::string newTrack = metadata.tracks[newPos.trackID].getIdentifier(); while (buffers.count(newPos) > 0){ newPos.seekTime++; } @@ -253,8 +232,8 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ } datapointertype = INVALID; std::string tmp = ""; - if (newPack.isMember("trackid")){ - tmp = getTrackById(newPack["trackid"].asInt())["type"].asStringRef(); + if (newPack.isMember("trackid") && newPack["trackid"].asInt() > 0){ + tmp = metadata.tracks[newPack["trackid"].asInt()].type; } if (newPack.isMember("datatype")){ tmp = newPack["datatype"].asStringRef(); @@ -268,75 +247,12 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ if (tmp == "meta"){ datapointertype = META; } - if (tmp == "pause_marker"){ + if (tmp == "pause_marker" || (newPack.isMember("mark") && newPack["mark"].asStringRef() == "pause")){ datapointertype = PAUSEMARK; } - int keySize = metadata["tracks"][newTrack]["keys"].size(); if (buffercount > 1){ - #define prevKey metadata["tracks"][newTrack]["keys"][keySize - 1] - if (newPack.isMember("keyframe") || !keySize || (datapointertype != VIDEO && newPack["time"].asInt() - 5000 > prevKey["time"].asInt())){ - metadata["tracks"][newTrack]["lastms"] = newPack["time"]; - keyframes[newPos.trackID].insert(newPos); - JSON::Value key; - key["time"] = newPack["time"]; - if (keySize){ - key["num"] = prevKey["num"].asInt() + 1; - prevKey["len"] = newPack["time"].asInt() - prevKey["time"].asInt(); - int size = 0; - for (JSON::ArrIter it = prevKey["parts"].ArrBegin(); it != prevKey["parts"].ArrEnd(); it++){ - size += it->asInt(); - } - prevKey["partsize"] = prevKey["parts"].size(); - std::string tmpParts = JSON::encodeVector(prevKey["parts"].ArrBegin(), prevKey["parts"].ArrEnd()); - prevKey["parts"] = tmpParts; - prevKey["size"] = size; - long long int bps = (double)prevKey["size"].asInt() / ((double)prevKey["len"].asInt() / 1000.0); - if (bps > metadata["tracks"][newTrack]["maxbps"].asInt()){ - metadata["tracks"][newTrack]["maxbps"] = (long long int)(bps * 1.2); - } - }else{ - key["num"] = 1; - } - metadata["tracks"][newTrack]["keys"].append(key); - keySize = metadata["tracks"][newTrack]["keys"].size(); - - //find the last fragment - JSON::Value lastFrag; - if (metadata["tracks"][newTrack]["frags"].size() > 0){ - lastFrag = metadata["tracks"][newTrack]["frags"][metadata["tracks"][newTrack]["frags"].size() - 1]; - } - //find the first keyframe past the last fragment - JSON::ArrIter fragIt = metadata["tracks"][newTrack]["keys"].ArrBegin(); - while (fragIt != metadata["tracks"][newTrack]["keys"].ArrEnd() && fragIt != metadata["tracks"][newTrack]["keys"].ArrEnd() - 1 && (*fragIt)["num"].asInt() < lastFrag["num"].asInt() + lastFrag["len"].asInt()){ - fragIt++; - } - //continue only if a keyframe was found - if (fragIt != metadata["tracks"][newTrack]["keys"].ArrEnd() && fragIt != metadata["tracks"][newTrack]["keys"].ArrEnd() - 1){ - //calculate the variables of the new fragment - JSON::Value newFrag; - newFrag["num"] = (*fragIt)["num"]; - newFrag["time"] = (*fragIt)["time"]; - newFrag["len"] = 1ll; - newFrag["dur"] = (*fragIt)["len"]; - fragIt++; - //keep calculating until 10+ seconds or no more keyframes - while (fragIt != metadata["tracks"][newTrack]["keys"].ArrEnd() && fragIt != metadata["tracks"][newTrack]["keys"].ArrEnd() - 1){ - newFrag["len"] = newFrag["len"].asInt() + 1; - newFrag["dur"] = newFrag["dur"].asInt() + (*fragIt)["len"].asInt(); - //more than 5 seconds? store the new fragment - if (newFrag["dur"].asInt() >= 5000 || (*fragIt)["len"].asInt() < 2){ - /// \todo Make this variable instead of hardcoded 5 seconds? - metadata["tracks"][newTrack]["frags"].append(newFrag); - break; - } - fragIt++; - } - } - } - if (keySize){ - metadata["tracks"][newTrack]["keys"][keySize - 1]["parts"].append((long long int)newPack["data"].asStringRef().size()); - } - metadata["live"] = 1ll; + metadata.update(newPack); + metadata.live = true; } //increase buffer size if too little time available @@ -346,8 +262,8 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ buffercount = buffers.size(); if (buffercount < 2){buffercount = 2;} } - if (metadata["buffer_window"].asInt() < timeBuffered){ - metadata["buffer_window"] = (long long int)timeBuffered; + if (metadata.bufferWindow < timeBuffered){ + metadata.bufferWindow = timeBuffered; } } @@ -359,23 +275,24 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ /// Deletes a the first part of the buffer, updating the keyframes list and metadata as required. /// Will print a warning to std::cerr if a track has less than 2 keyframes left because of this. void DTSC::Stream::cutOneBuffer(){ - if (buffercount > 1 && keyframes[buffers.begin()->first.trackID].count(buffers.begin()->first)){ + int trid = buffers.begin()->first.trackID; + if (buffercount > 1 && keyframes[trid].count(buffers.begin()->first)){ //if there are < 3 keyframes, throwing one away would mean less than 2 left. - if (keyframes[buffers.begin()->first.trackID].size() < 3){ - std::cerr << "Warning - track " << buffers.begin()->first.trackID << " doesn't have enough keyframes to be reliably served." << std::endl; + if (keyframes[trid].size() < 3){ + std::cerr << "Warning - track " << trid << " doesn't have enough keyframes to be reliably served." << std::endl; } - std::string track = trackMapping[buffers.begin()->first.trackID]; - keyframes[buffers.begin()->first.trackID].erase(buffers.begin()->first); - int keySize = metadata["tracks"][track]["keys"].size(); - metadata["tracks"][track]["keys"].shrink(keySize - 1); - if (metadata["tracks"][track]["frags"].size() > 0){ - // delete fragments of which the beginning can no longer be reached - while (metadata["tracks"][track]["frags"].size() > 0 && metadata["tracks"][track]["frags"][0u]["num"].asInt() < metadata["tracks"][track]["keys"][0u]["num"].asInt()){ - metadata["tracks"][track]["firstms"] = metadata["tracks"][track]["firstms"].asInt() + metadata["tracks"][track]["frags"][0u]["dur"].asInt(); - metadata["tracks"][track]["frags"].shrink(metadata["tracks"][track]["frags"].size() - 1); - // increase the missed fragments counter - metadata["tracks"][track]["missed_frags"] = metadata["tracks"][track]["missed_frags"].asInt() + 1; - } + keyframes[trid].erase(buffers.begin()->first); + int keySize = metadata.tracks[trid].keys.size(); + for (int i = 0; i < metadata.tracks[trid].keys[0].getParts(); i++){ + metadata.tracks[trid].parts.pop_front(); + } + metadata.tracks[trid].keys.pop_front(); + // delete fragments of which the beginning can no longer be reached + while (metadata.tracks[trid].fragments.size() && metadata.tracks[trid].fragments[0].getNumber() < metadata.tracks[trid].keys[0].getNumber()){ + metadata.tracks[trid].firstms += metadata.tracks[trid].fragments[0].getDuration(); + metadata.tracks[trid].fragments.pop_front(); + // increase the missed fragments counter + metadata.tracks[trid].missedFrags ++; } } buffers.erase(buffers.begin()); @@ -401,15 +318,6 @@ JSON::Value & DTSC::Stream::getPacket(){ return buffers.begin()->second; } -/// Returns a track element by giving the id. -JSON::Value & DTSC::Stream::getTrackById(int trackNo){ - static JSON::Value empty; - if (trackMapping.find(trackNo) != trackMapping.end()){ - return metadata["tracks"][trackMapping[trackNo]]; - } - return empty; -} - /// Returns the type of the last received packet. DTSC::datatype DTSC::Stream::lastType(){ return datapointertype; @@ -417,12 +325,22 @@ DTSC::datatype DTSC::Stream::lastType(){ /// Returns true if the current stream contains at least one video track. bool DTSC::Stream::hasVideo(){ - return metadata.isMember("video"); + for (std::map::iterator it = metadata.tracks.begin(); it != metadata.tracks.end(); it++){ + if (it->second.type == "video"){ + return true; + } + } + return false; } /// Returns true if the current stream contains at least one audio track. bool DTSC::Stream::hasAudio(){ - return metadata.isMember("audio"); + for (std::map::iterator it = metadata.tracks.begin(); it != metadata.tracks.end(); it++){ + if (it->second.type == "audio"){ + return true; + } + } + return false; } void DTSC::Stream::setBufferTime(unsigned int ms){ @@ -446,7 +364,7 @@ std::string & DTSC::Stream::outPacket(livePos num){ /// Returns a packed DTSC header, ready to sent over the network. std::string & DTSC::Stream::outHeader(){ - return metadata.toNetPacked(); + return metadata.toJSON().toNetPacked(); } /// Constructs a new Ring, at the given buffer position. @@ -483,16 +401,16 @@ void DTSC::Stream::dropRing(DTSC::Ring * ptr){ int DTSC::Stream::canSeekms(unsigned int ms){ bool too_late = false; //no tracks? Frame too new by definition. - if ( !metadata.isMember("tracks") || metadata["tracks"].size() < 1){ + if ( !metadata.tracks.size()){ return 1; } //loop trough all the tracks - for (JSON::ObjIter it = metadata["tracks"].ObjBegin(); it != metadata["tracks"].ObjEnd(); it++){ - if (it->second.isMember("keys") && it->second["keys"].size() > 0){ - if (it->second["keys"][0u]["time"].asInt() <= ms && it->second["keys"][it->second["keys"].size() - 1]["time"].asInt() >= ms){ + for (std::map::iterator it = metadata.tracks.begin(); it != metadata.tracks.end(); it++){ + if (it->second.keys.size()){ + if (it->second.keys[0].getTime() <= ms && it->second.keys[it->second.keys.size() - 1].getTime() >= ms){ return 0; } - if (it->second["keys"][0u]["time"].asInt() > ms){too_late = true;} + if (it->second.keys[0].getTime() > ms){too_late = true;} } } //did we spot a track already past this point? return too late. @@ -505,7 +423,7 @@ DTSC::livePos DTSC::Stream::msSeek(unsigned int ms, std::set & allowedTrack std::set seekTracks = allowedTracks; livePos result = buffers.begin()->first; for (std::set::iterator it = allowedTracks.begin(); it != allowedTracks.end(); it++){ - if (getTrackById(*it).isMember("type") && getTrackById(*it)["type"].asStringRef() == "video"){ + if (metadata.tracks[*it].type == "video"){ int trackNo = *it; seekTracks.clear(); seekTracks.insert(trackNo); @@ -616,18 +534,12 @@ DTSC::File::File(std::string filename, bool create){ headerSize = ntohl(ubuffer[0]); } readHeader(0); - trackMapping.clear(); - if (metadata.isMember("tracks")){ - for (JSON::ObjIter it = metadata["tracks"].ObjBegin(); it != metadata["tracks"].ObjEnd(); it++){ - trackMapping.insert(std::pair(it->second["trackid"].asInt(),it->first)); - } - } fseek(F, 8 + headerSize, SEEK_SET); currframe = 0; } /// Returns the header metadata for this file as JSON::Value. -JSON::Value & DTSC::File::getMeta(){ +DTSC::readOnlyMeta & DTSC::File::getMeta(){ return metadata; } @@ -688,19 +600,19 @@ void DTSC::File::readHeader(int pos){ fprintf(stderr, "Could not read header (H%i)\n", pos); } strbuffer = ""; - metadata.null(); + metadata = readOnlyMeta(); return; } if (memcmp(buffer, DTSC::Magic_Header, 4) != 0){ fprintf(stderr, "Invalid header - %.4s != %.4s (H%i)\n", buffer, DTSC::Magic_Header, pos); strbuffer = ""; - metadata.null(); + metadata = readOnlyMeta(); return; } if (fread(buffer, 4, 1, F) != 1){ fprintf(stderr, "Could not read size (H%i)\n", pos); strbuffer = ""; - metadata.null(); + metadata = readOnlyMeta(); return; } uint32_t * ubuffer = (uint32_t *)buffer; @@ -710,20 +622,21 @@ void DTSC::File::readHeader(int pos){ if (fread((void*)strbuffer.c_str(), packSize, 1, F) != 1){ fprintf(stderr, "Could not read packet (H%i)\n", pos); strbuffer = ""; - metadata.null(); + metadata = readOnlyMeta(); return; } - metadata = JSON::fromDTMI(strbuffer); + metaStorage = JSON::fromDTMI(strbuffer); + metadata = readOnlyMeta(metaStorage);//make readonly } //if there is another header, read it and replace metadata with that one. - if (metadata.isMember("moreheader") && metadata["moreheader"].asInt() > 0){ - if (metadata["moreheader"].asInt() < getBytePosEOF()){ - readHeader(metadata["moreheader"].asInt()); + if (metadata.moreheader){ + if (metadata.moreheader < getBytePosEOF()){ + readHeader(metadata.moreheader); return; } } - metadata["vod"] = true; - metadata.netPrepare(); + metadata.vod = true; + metadata.live = false; } long int DTSC::File::getBytePosEOF(){ @@ -754,7 +667,7 @@ void DTSC::File::seekNext(){ return; } clearerr(F); - if ( !metadata.isMember("merged") || !metadata["merged"]){ + if ( !metadata.merged){ seek_time(currentPositions.begin()->seekTime + 1, currentPositions.begin()->trackID); } fseek(F,currentPositions.begin()->bytePos, SEEK_SET); @@ -774,7 +687,7 @@ void DTSC::File::seekNext(){ } if (memcmp(buffer, DTSC::Magic_Header, 4) == 0){ readHeader(lastreadpos); - jsonbuffer = metadata; + jsonbuffer = metadata.toJSON(); return; } long long unsigned int version = 0; @@ -810,7 +723,7 @@ void DTSC::File::seekNext(){ }else{ jsonbuffer = JSON::fromDTMI(strbuffer); } - if (metadata.isMember("merged") && metadata["merged"]){ + if ( metadata.merged){ int tempLoc = getBytePos(); char newHeader[20]; if (fread((void*)newHeader, 20, 1, F) == 1){ @@ -823,11 +736,12 @@ void DTSC::File::seekNext(){ tmpPos.seekTime += ntohl(((int*)newHeader)[4]); }else{ tmpPos.seekTime = -1; - for (JSON::ArrIter it = getTrackById(jsonbuffer["trackid"].asInt())["keys"].ArrBegin(); it != getTrackById(jsonbuffer["trackid"].asInt())["keys"].ArrEnd(); it++){ - if ((*it)["time"].asInt() > jsonbuffer["time"].asInt()){ - tmpPos.seekTime = (*it)["time"].asInt(); - tmpPos.bytePos = (*it)["bpos"].asInt(); - tmpPos.trackID = jsonbuffer["trackid"].asInt(); + long tid = jsonbuffer["trackid"].asInt(); + for (int i = 0; i != metadata.tracks[tid].keyLen; i++){ + if (metadata.tracks[tid].keys[i].getTime() > jsonbuffer["time"].asInt()){ + tmpPos.seekTime = metadata.tracks[tid].keys[i].getTime(); + tmpPos.bytePos = metadata.tracks[tid].keys[i].getBpos(); + tmpPos.trackID = tid; break; } } @@ -869,7 +783,7 @@ void DTSC::File::parseNext(){ if (memcmp(buffer, DTSC::Magic_Header, 4) == 0){ if (lastreadpos != 0){ readHeader(lastreadpos); - jsonbuffer = metadata; + jsonbuffer = metadata.toJSON(); }else{ if (fread(buffer, 4, 1, F) != 1){ fprintf(stderr, "Could not read size\n"); @@ -940,15 +854,6 @@ JSON::Value & DTSC::File::getJSON(){ return jsonbuffer; } -/// Returns a track element by giving the id. -JSON::Value & DTSC::File::getTrackById(int trackNo){ - static JSON::Value empty; - if (trackMapping.find(trackNo) != trackMapping.end()){ - return metadata["tracks"][trackMapping[trackNo]]; - } - return empty; -} - bool DTSC::File::seek_time(int ms, int trackNo, bool forceSeek){ seekPos tmpPos; tmpPos.trackID = trackNo; @@ -959,14 +864,13 @@ bool DTSC::File::seek_time(int ms, int trackNo, bool forceSeek){ tmpPos.seekTime = 0; tmpPos.bytePos = 0; } - JSON::Value & keys = metadata["tracks"][trackMapping[trackNo]]["keys"]; - for (JSON::ArrIter keyIt = keys.ArrBegin(); keyIt != keys.ArrEnd(); keyIt++){ - if ((*keyIt)["time"].asInt() > ms){ + for (int i = 0; i < metadata.tracks[trackNo].keyLen; i++){ + if (metadata.tracks[trackNo].keys[i].getTime() > ms){ break; } - if ((*keyIt)["time"].asInt() > tmpPos.seekTime){ - tmpPos.seekTime = (*keyIt)["time"].asInt(); - tmpPos.bytePos = (*keyIt)["bpos"].asInt(); + if (metadata.tracks[trackNo].keys[i].getTime() > tmpPos.seekTime){ + tmpPos.seekTime = metadata.tracks[trackNo].keys[i].getTime(); + tmpPos.bytePos = metadata.tracks[trackNo].keys[i].getBpos(); } } bool foundPacket = false; @@ -999,7 +903,6 @@ bool DTSC::File::seek_time(int ms, int trackNo, bool forceSeek){ } } currentPositions.insert(tmpPos); - //fprintf(stderr,"Seek_time to %d on track %d, time %d on track %d found\n", ms, trackNo, tmpPos.seekTime,tmpPos.trackID); } /// Attempts to seek to the given time in ms within the file. @@ -1037,10 +940,10 @@ bool DTSC::File::atKeyframe(){ return true; } long long int bTime = jsonbuffer["time"].asInt(); - JSON::Value & keys = getTrackById(jsonbuffer["trackid"].asInt())["keys"]; - for (JSON::ArrIter aIt = keys.ArrBegin(); aIt != keys.ArrEnd(); ++aIt){ - if ((*aIt)["time"].asInt() >= bTime){ - return ((*aIt)["time"].asInt() == bTime); + int trackid = jsonbuffer["trackid"].asInt(); + for (int i = 0; i < metadata.tracks[trackid].keyLen; i++){ + if (metadata.tracks[trackid].keys[i].getTime() >= bTime){ + return (metadata.tracks[trackid].keys[i].getTime() == bTime); } } return false; diff --git a/lib/dtsc.h b/lib/dtsc.h index 28fe8993..a3119105 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -53,53 +53,6 @@ namespace DTSC { unsigned int trackID; }; - /// 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(); - JSON::Value & 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(); - std::string & getPacket(); - JSON::Value & getJSON(); - JSON::Value & getTrackById(int trackNo); - bool seek_time(int seconds); - bool seek_time(int seconds, int trackNo, bool forceSeek = false); - bool seek_bpos(int bpos); - void writePacket(std::string & newPacket); - void writePacket(JSON::Value & newPacket); - bool atKeyframe(); - void selectTracks(std::set & tracks); - private: - long int endPos; - void readHeader(int pos); - std::string strbuffer; - JSON::Value jsonbuffer; - JSON::Value metadata; - std::map trackMapping; - long long int currtime; - long long int lastreadpos; - int currframe; - FILE * F; - unsigned long headerSize; - char buffer[4]; - bool created; - std::set currentPositions; - std::set selectedTracks; - }; - //FileWriter - /// A simple structure used for ordering byte seek positions. struct livePos { livePos(){ @@ -149,6 +102,179 @@ namespace DTSC { volatile int playCount; }; + class Part{ + public: + short getSize(); + void setSize(short newSize); + short getDuration(); + void setDuration(short newDuration); + long getOffset(); + void setOffset(long newOffset); + char* getData(); + private: + char data[8]; + }; + + class Key{ + public: + long long unsigned int getBpos(); + void setBpos(long long unsigned int newBpos); + long getLength(); + void setLength(long newLength); + short getNumber(); + void setNumber(short newNumber); + short getParts(); + void setParts(short newParts); + long getTime(); + void setTime(long newTime); + char* getData(); + private: + char data[16]; + }; + + class Fragment{ + public: + long getDuration(); + void setDuration(long newDuration); + char getLength(); + void setLength(char newLength); + short getNumber(); + void setNumber(short newNumber); + long getSize(); + void setSize(long newSize); + char* getData(); + private: + char data[11]; + }; + + class readOnlyTrack{ + public: + readOnlyTrack(); + readOnlyTrack(JSON::Value & trackRef); + int getSendLen(); + void send(Socket::Connection & conn); + std::string getIdentifier(); + JSON::Value toJSON(); + long long unsigned int fragLen; + Fragment* fragments; + long long unsigned int keyLen; + Key* keys; + long long unsigned int partLen; + Part* parts; + int trackID; + int length; + int firstms; + int 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; + }; + + class Track : public readOnlyTrack { + public: + Track(); + Track(const readOnlyTrack & rhs); + Track(JSON::Value & trackRef); + inline operator bool() const {return parts.size();} + void update(JSON::Value & pack); + int getSendLen(); + void send(Socket::Connection & conn); + JSON::Value toJSON(); + std::deque fragments; + std::deque keys; + std::deque parts; + Key & getKey(int keyNum); + std::string getIdentifier(); + void reset(); + }; + + class readOnlyMeta { + public: + readOnlyMeta(); + readOnlyMeta(JSON::Value & meta); + std::map tracks; + bool vod; + bool live; + bool merged; + long long int moreheader; + long long int length; + long long int bufferWindow; + void send(Socket::Connection & conn); + JSON::Value toJSON(); + bool isFixed(); + }; + + class Meta : public readOnlyMeta { + public: + Meta(); + Meta(const readOnlyMeta & meta); + Meta(JSON::Value & meta); + inline operator bool() const {return vod || live;} + std::map tracks; + void update(JSON::Value & pack); + void send(Socket::Connection & conn); + JSON::Value toJSON(); + void reset(); + bool isFixed(); + }; + + /// 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(); + readOnlyMeta & 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(); + std::string & getPacket(); + JSON::Value & getJSON(); + bool seek_time(int seconds); + bool seek_time(int seconds, int trackNo, bool forceSeek = false); + bool seek_bpos(int bpos); + void writePacket(std::string & newPacket); + void writePacket(JSON::Value & newPacket); + bool atKeyframe(); + void selectTracks(std::set & tracks); + private: + long int endPos; + void readHeader(int pos); + std::string strbuffer; + JSON::Value jsonbuffer; + JSON::Value metaStorage; + readOnlyMeta metadata; + std::map trackMapping; + long long int currtime; + long long int lastreadpos; + int currframe; + FILE * F; + unsigned long headerSize; + char buffer[4]; + bool created; + std::set currentPositions; + std::set 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. @@ -157,10 +283,9 @@ namespace DTSC { Stream(); ~Stream(); Stream(unsigned int buffers, unsigned int bufferTime = 0); - JSON::Value metadata; + Meta metadata; JSON::Value & getPacket(); JSON::Value & getPacket(livePos num); - JSON::Value & getTrackById(int trackNo); datatype lastType(); std::string & lastData(); bool hasVideo(); diff --git a/lib/dtscmeta.cpp b/lib/dtscmeta.cpp new file mode 100644 index 00000000..1172e037 --- /dev/null +++ b/lib/dtscmeta.cpp @@ -0,0 +1,786 @@ +#include "dtsc.h" + +namespace DTSC { + short Part::getSize(){ + return ntohs(((short*)data)[0]); + } + + void Part::setSize(short newSize){ + ((short*)data)[0] = htons(newSize); + } + + short Part::getDuration(){ + return ntohs(((short*)(data+2))[0]); + } + + void Part::setDuration(short newDuration){ + ((short*)(data+2))[0] = htons(newDuration); + } + + long Part::getOffset(){ + return ntohl(((int*)(data+4))[0]); + } + + void Part::setOffset(long newOffset){ + ((int*)(data+4))[0] = htonl(newOffset); + } + + char* Part::getData(){ + return data; + } + + long long unsigned int Key::getBpos(){ + return (((long long unsigned int)data[0] << 32) | (data[1] << 24) | (data[2] << 16) | (data[3] << 8) | data[4]); + } + + void Key::setBpos(long long unsigned int newBpos){ + data[4] = newBpos & 0xFF; + data[3] = (newBpos >> 8) & 0xFF; + data[2] = (newBpos >> 16) & 0xFF; + data[1] = (newBpos >> 24) & 0xFF; + data[0] = (newBpos >> 32) & 0xFF; + } + + long Key::getLength(){ + return ((data[5] << 16) | (data[6] << 8) | data[7]); + } + + void Key::setLength(long newLength){ + data[7] = newLength & 0xFF; + data[6] = (newLength >> 8) & 0xFF; + data[5] = (newLength >> 16) & 0xFF; + } + + short Key::getNumber(){ + return ntohs(((short*)(data+8))[0]); + } + + void Key::setNumber(short newNumber){ + ((short*)(data+8))[0] = htons(newNumber); + } + + short Key::getParts(){ + return ntohs(((short*)(data+10))[0]); + } + + void Key::setParts(short newParts){ + ((short*)(data+10))[0] = htons(newParts); + } + + long Key::getTime(){ + return ntohl(((int*)(data+12))[0]); + } + + void Key::setTime(long newTime){ + ((int*)(data+12))[0] = htonl(newTime); + } + + char* Key::getData(){ + return data; + } + + long Fragment::getDuration(){ + return ntohl(((int*)data)[0]); + } + + void Fragment::setDuration(long newDuration){ + ((int*)data)[0] = htonl(newDuration); + } + + char Fragment::getLength(){ + return data[4]; + } + + void Fragment::setLength(char newLength){ + data[4] = newLength; + } + + short Fragment::getNumber(){ + return ntohs(((short*)(data+5))[0]); + } + + void Fragment::setNumber(short newNumber){ + ((short*)(data+5))[0] = htons(newNumber); + } + + long Fragment::getSize(){ + return ntohl(((int*)(data+7))[0]); + } + + void Fragment::setSize(long newSize){ + ((int*)(data+7))[0] = htonl(newSize); + } + + char* Fragment::getData(){ + return data; + } + + readOnlyTrack::readOnlyTrack(){ + fragments = NULL; + fragLen = 0; + keys = NULL; + keyLen = 0; + parts = NULL; + partLen = 0; + missedFrags = 0; + } + + readOnlyTrack::readOnlyTrack(JSON::Value & trackRef){ + if (trackRef.isMember("fragments")){ + fragments = (Fragment*)trackRef["fragments"].asString().data(); + fragLen = trackRef["fragments"].asString().size() / 11; + }else{ + fragments = 0; + fragLen = 0; + } + if (trackRef.isMember("keys")){ + keys = (Key*)trackRef["keys"].asString().data(); + keyLen = trackRef["keys"].asString().size() / 16; + }else{ + keys = 0; + keyLen = 0; + } + if (trackRef.isMember("parts")){ + parts = (Part*)trackRef["parts"].asString().data(); + partLen = trackRef["parts"].asString().size() / 8; + }else{ + parts = 0; + partLen = 0; + } + trackID = trackRef["trackid"].asInt(); + length = trackRef["length"].asInt(); + firstms = trackRef["firstms"].asInt(); + lastms = trackRef["lastms"].asInt(); + bps = trackRef["bps"].asInt(); + missedFrags = trackRef["missed_fags"].asInt(); + codec = trackRef["codec"].asString(); + type = trackRef["type"].asString(); + init = trackRef["init"].asString(); + if (type == "audio"){ + rate = trackRef["rate"].asInt(); + size = trackRef["size"].asInt(); + channels = trackRef["channels"].asInt(); + } + if (type == "video"){ + width = trackRef["width"].asInt(); + height = trackRef["height"].asInt(); + fpks = trackRef["fpks"].asInt(); + } + } + + Track::Track(){} + + Track::Track(const readOnlyTrack & rhs){ + trackID = rhs.trackID; + length = rhs.length; + firstms = rhs.firstms; + lastms = rhs.lastms; + bps = rhs.bps; + missedFrags = rhs.missedFrags; + init = rhs.init; + codec = rhs.codec; + type = rhs.type; + rate = rhs.rate; + size = rhs.size; + channels = rhs.channels; + width = rhs.width; + height = rhs.height; + fpks = rhs.fpks; + if (rhs.fragments && rhs.fragLen){ + fragments = std::deque(rhs.fragments, rhs.fragments + rhs.fragLen); + } + if (rhs.keys && rhs.keyLen){ + keys = std::deque(rhs.keys, rhs.keys + rhs.keyLen); + } + if (rhs.parts && rhs.partLen){ + parts = std::deque(rhs.parts, rhs.parts + rhs.partLen); + } + } + + Track::Track(JSON::Value & trackRef){ + if (trackRef.isMember("fragments") && trackRef["fragments"].isString()){ + Fragment* tmp = (Fragment*)trackRef["fragments"].asString().data(); + fragments = std::deque(tmp,tmp + (trackRef["fragments"].asString().size() / 11)); + } + if (trackRef.isMember("keys") && trackRef["keys"].isString()){ + Key* tmp = (Key*)trackRef["keys"].asString().data(); + keys = std::deque(tmp,tmp + (trackRef["keys"].asString().size() / 16)); + } + if (trackRef.isMember("parts") && trackRef["parts"].isString()){ + Part* tmp = (Part*)trackRef["parts"].asString().data(); + parts = std::deque(tmp,tmp + (trackRef["parts"].asString().size() / 8)); + } + trackID = trackRef["trackid"].asInt(); + length = trackRef["length"].asInt(); + firstms = trackRef["firstms"].asInt(); + lastms = trackRef["lastms"].asInt(); + bps = trackRef["bps"].asInt(); + missedFrags = trackRef["missed_fags"].asInt(); + codec = trackRef["codec"].asString(); + type = trackRef["type"].asString(); + init = trackRef["init"].asString(); + if (type == "audio"){ + rate = trackRef["rate"].asInt(); + size = trackRef["size"].asInt(); + channels = trackRef["channels"].asInt(); + } + if (type == "video"){ + width = trackRef["width"].asInt(); + height = trackRef["height"].asInt(); + fpks = trackRef["fpks"].asInt(); + } + } + + void Track::update(JSON::Value & pack){ + Part newPart; + newPart.setSize(pack["data"].asString().size()); + newPart.setOffset(pack["offset"].asInt()); + if (parts.size()){ + parts[parts.size()-1].setDuration(pack["time"].asInt() - lastms); + newPart.setDuration(pack["time"].asInt() - lastms); + }else{ + newPart.setDuration(0); + } + if (pack["time"].asInt() > firstms){ + firstms = pack["time"].asInt(); + } + parts.push_back(newPart); + lastms = pack["time"].asInt(); + if (pack.isMember("keyframe") || !keys.size() || (type != "video" && pack["time"].asInt() - 2000 > keys[keys.size() - 1].getTime())){ + Key newKey; + newKey.setTime(pack["time"].asInt()); + newKey.setParts(0); + newKey.setLength(pack["time"].asInt()); + if (keys.size()){ + newKey.setNumber(keys[keys.size() - 1].getNumber() + 1); + keys[keys.size() - 1].setLength(pack["time"].asInt() - keys[keys.size() - 1].getLength()); + }else{ + newKey.setNumber(1); + } + if (pack.isMember("bpos")){//For VoD + newKey.setBpos(pack["bpos"].asInt()); + }else{ + newKey.setBpos(0); + } + keys.push_back(newKey); + if (!fragments.size() || pack["time"].asInt() - 10000 >= getKey(fragments.rbegin()->getNumber()).getTime()){ + //new fragment + Fragment newFrag; + newFrag.setDuration(0); + newFrag.setLength(1); + newFrag.setNumber(keys[keys.size() - 1].getNumber()); + if (fragments.size()){ + fragments[fragments.size() - 1].setDuration(pack["time"].asInt() - fragments[fragments.size() - 1].getDuration()); + } + newFrag.setDuration(pack["time"].asInt()); + newFrag.setSize(0); + fragments.push_back(newFrag); + }else{ + Fragment & lastFrag = fragments[fragments.size() - 1]; + lastFrag.setLength(lastFrag.getLength() + 1); + } + } + keys.rbegin()->setParts(keys.rbegin()->getParts() + 1); + fragments.rbegin()->setSize(fragments.rbegin()->getSize() + pack["data"].asString().size()); + bps += pack["data"].asString().size(); + } + + Key & Track::getKey(int keyNum){ + static Key empty; + if (keyNum < keys[0].getNumber()){ + return empty; + } + if ((keyNum - keys[0].getNumber()) > keys.size()){ + return empty; + } + return keys[keyNum - keys[0].getNumber()]; + } + + std::string readOnlyTrack::getIdentifier(){ + std::stringstream result; + if (type == ""){ + result << "Metadata_" << trackID; + return result.str(); + } + result << type << "_"; + result << codec << "_"; + result << (bps / 1024) << "kbit_"; + if (type == "audio"){ + result << channels << "ch_"; + result << rate << "hz"; + }else if (type == "video"){ + result << width << "x" << height << "_"; + result << (double)fpks / 1000 << "fps"; + } + return result.str(); + } + + std::string Track::getIdentifier(){ + std::stringstream result; + if (type == ""){ + result << "Metadata_" << trackID; + return result.str(); + } + result << type << "_"; + result << codec << "_"; + result << (bps / 1024) << "kbit_"; + if (type == "audio"){ + result << channels << "ch_"; + result << rate << "hz"; + }else if (type == "video"){ + result << width << "x" << height << "_"; + result << (double)fpks / 1000 << "fps"; + } + return result.str(); + } + + void Track::reset(){ + fragments.clear(); + parts.clear(); + keys.clear(); + bps = 0; + firstms = 999; + lastms = 0; + } + + readOnlyMeta::readOnlyMeta(){ + vod = false; + live = false; + moreheader = 0; + length = 0; + merged = false; + bufferWindow = 0; + } + + readOnlyMeta::readOnlyMeta(JSON::Value & meta){ + vod = meta.isMember("vod") && meta["vod"]; + live = meta.isMember("live") && meta["live"]; + length = 0; + merged = meta.isMember("merged") && meta["merged"]; + bufferWindow = 0; + if (meta.isMember("buffer_window")){ + bufferWindow = meta["buffer_window"].asInt(); + } + for (JSON::ObjIter it = meta["tracks"].ObjBegin(); it != meta["tracks"].ObjEnd(); it++){ + if (it->second.isMember("trackid") && it->second["trackid"]){ + tracks[it->second["trackid"].asInt()] = readOnlyTrack(it->second); + if (length < (it->second["lastms"].asInt() - it->second["firstms"].asInt())){ + length = (it->second["lastms"].asInt() - it->second["firstms"].asInt()); + } + } + } + moreheader = meta["moreheader"].asInt(); + } + + Meta::Meta(){ + vod = false; + live = false; + moreheader = 0; + length = 0; + merged = false; + bufferWindow = 0; + } + + Meta::Meta(const readOnlyMeta & rhs){ + vod = rhs.vod; + live = rhs.live; + merged = rhs.merged; + length = rhs.length; + bufferWindow = rhs.bufferWindow; + for (std::map::const_iterator it = rhs.tracks.begin(); it != rhs.tracks.end(); it++){ + tracks[it->first] = it->second; + } + moreheader = rhs.moreheader; + } + + Meta::Meta(JSON::Value & meta){ + vod = meta.isMember("vod") && meta["vod"]; + live = meta.isMember("live") && meta["live"]; + merged = meta.isMember("merged") && meta["merged"]; + bufferWindow = 0; + if (meta.isMember("buffer_window")){ + bufferWindow = meta["buffer_window"].asInt(); + } + for (JSON::ObjIter it = meta["tracks"].ObjBegin(); it != meta["tracks"].ObjEnd(); it++){ + if(it->second["trackid"].asInt()){ + tracks[it->second["trackid"].asInt()] = Track(it->second); + if (length < (it->second["lastms"].asInt() - it->second["firstms"].asInt())){ + length = (it->second["lastms"].asInt() - it->second["firstms"].asInt()); + } + } + } + moreheader = meta["moreheader"].asInt(); + } + + void Meta::update(JSON::Value & pack){ + vod = pack.isMember("bpos"); + live = !vod; + if (pack["trackid"].asInt()){ + tracks[pack["trackid"].asInt()].update(pack); + } + } + + char * convertShort(short input){ + static char result[2]; + result[0] = (input >> 8) & 0xFF; + result[1] = (input) & 0xFF; + return result; + } + + char * convertInt(int input){ + static char result[4]; + result[0] = (input >> 24) & 0xFF; + result[1] = (input >> 16) & 0xFF; + result[2] = (input >> 8) & 0xFF; + result[3] = (input) & 0xFF; + return result; + } + + char * convertLongLong(long long int input){ + static char result[8]; + result[0] = (input >> 56) & 0xFF; + result[1] = (input >> 48) & 0xFF; + result[2] = (input >> 40) & 0xFF; + result[3] = (input >> 32) & 0xFF; + result[4] = (input >> 24) & 0xFF; + result[5] = (input >> 16) & 0xFF; + result[6] = (input >> 8) & 0xFF; + result[7] = (input) & 0xFF; + return result; + } + + int readOnlyTrack::getSendLen(){ + int result = 163 + init.size() + codec.size() + type.size() + getIdentifier().size(); + result += fragLen * 11; + result += keyLen * 16; + result += partLen * 8; + if (type == "audio"){ + result += 49; + }else if (type == "video"){ + result += 48; + } + return result; + } + + int Track::getSendLen(){ + int result = 163 + init.size() + codec.size() + type.size() + getIdentifier().size(); + result += fragments.size() * 11; + result += keys.size() * 16; + result += parts.size() * 8; + if (type == "audio"){ + result += 49; + }else if (type == "video"){ + result += 48; + } + return result; + } + + void readOnlyTrack::send(Socket::Connection & conn){ + conn.SendNow(convertShort(getIdentifier().size()), 2); + conn.SendNow(getIdentifier()); + conn.SendNow("\340", 1);//Begin track object + conn.SendNow("\000\011fragments\002", 12); + conn.SendNow(convertInt(fragLen*11), 4); + conn.SendNow((char*)fragments, fragLen*11); + conn.SendNow("\000\004keys\002", 7); + conn.SendNow(convertInt(keyLen*16), 4); + conn.SendNow((char*)keys, keyLen*16); + conn.SendNow("\000\005parts\002", 8); + conn.SendNow(convertInt(partLen*8), 4); + conn.SendNow((char*)parts, partLen*8); + conn.SendNow("\000\007trackid\001", 10); + conn.SendNow(convertLongLong(trackID), 8); + conn.SendNow("\000\006length\001", 9); + conn.SendNow(convertLongLong(length), 8); + conn.SendNow("\000\007firstms\001", 10); + conn.SendNow(convertLongLong(firstms), 8); + conn.SendNow("\000\006lastms\001", 9); + conn.SendNow(convertLongLong(lastms), 8); + conn.SendNow("\000\003bps\001", 6); + conn.SendNow(convertLongLong(bps), 8); + conn.SendNow("\000\004init\002", 7); + conn.SendNow(convertInt(init.size()), 4); + conn.SendNow(init); + conn.SendNow("\000\005codec\002", 8); + conn.SendNow(convertInt(codec.size()), 4); + conn.SendNow(codec); + conn.SendNow("\000\004type\002", 7); + conn.SendNow(convertInt(type.size()), 4); + conn.SendNow(type); + if (type == "audio"){ + conn.SendNow("\000\004rate\001", 7); + conn.SendNow(convertLongLong(rate), 8); + conn.SendNow("\000\004size\001", 7); + conn.SendNow(convertLongLong(size), 8); + conn.SendNow("\000\010channels\001", 11); + conn.SendNow(convertLongLong(channels), 8); + }else if (type == "video"){ + conn.SendNow("\000\005width\001", 8); + conn.SendNow(convertLongLong(width), 8); + conn.SendNow("\000\006height\001", 9); + conn.SendNow(convertLongLong(height), 8); + conn.SendNow("\000\004fpks\001", 7); + conn.SendNow(convertLongLong(fpks), 8); + } + conn.SendNow("\000\000\356", 3);//End this track Object + } + + void Track::send(Socket::Connection & conn){ + conn.SendNow(convertShort(getIdentifier().size()), 2); + conn.SendNow(getIdentifier()); + conn.SendNow("\340", 1);//Begin track object + conn.SendNow("\000\011fragments\002", 12); + conn.SendNow(convertInt(fragments.size()*11), 4); + for (std::deque::iterator it = fragments.begin(); it != fragments.end(); it++){ + conn.SendNow(it->getData(), 11); + } + conn.SendNow("\000\004keys\002", 7); + conn.SendNow(convertInt(keys.size()*16), 4); + for (std::deque::iterator it = keys.begin(); it != keys.end(); it++){ + conn.SendNow(it->getData(), 16); + } + conn.SendNow("\000\005parts\002", 8); + conn.SendNow(convertInt(parts.size()*8), 4); + for (std::deque::iterator it = parts.begin(); it != parts.end(); it++){ + conn.SendNow(it->getData(), 8); + } + conn.SendNow("\000\007trackid\001", 10); + conn.SendNow(convertLongLong(trackID), 8); + conn.SendNow("\000\006length\001", 9); + conn.SendNow(convertLongLong(length), 8); + conn.SendNow("\000\007firstms\001", 10); + conn.SendNow(convertLongLong(firstms), 8); + conn.SendNow("\000\006lastms\001", 9); + conn.SendNow(convertLongLong(lastms), 8); + conn.SendNow("\000\003bps\001", 6); + conn.SendNow(convertLongLong(bps), 8); + conn.SendNow("\000\004init\002", 7); + conn.SendNow(convertInt(init.size()), 4); + conn.SendNow(init); + conn.SendNow("\000\005codec\002", 8); + conn.SendNow(convertInt(codec.size()), 4); + conn.SendNow(codec); + conn.SendNow("\000\004type\002", 7); + conn.SendNow(convertInt(type.size()), 4); + conn.SendNow(type); + if (type == "audio"){ + conn.SendNow("\000\004rate\001", 7); + conn.SendNow(convertLongLong(rate), 8); + conn.SendNow("\000\004size\001", 7); + conn.SendNow(convertLongLong(size), 8); + conn.SendNow("\000\010channels\001", 11); + conn.SendNow(convertLongLong(channels), 8); + }else if (type == "video"){ + conn.SendNow("\000\005width\001", 8); + conn.SendNow(convertLongLong(width), 8); + conn.SendNow("\000\006height\001", 9); + conn.SendNow(convertLongLong(height), 8); + conn.SendNow("\000\004fpks\001", 7); + conn.SendNow(convertLongLong(fpks), 8); + } + conn.SendNow("\000\000\356", 3);//End this track Object + } + + void readOnlyMeta::send(Socket::Connection & conn){ + int dataLen = 16 + (vod ? 14 : 0) + (live ? 15 : 0) + (merged ? 17 : 0) + 21; + for (std::map::iterator it = tracks.begin(); it != tracks.end(); it++){ + dataLen += it->second.getSendLen(); + } + conn.SendNow(DTSC::Magic_Header, 4); + conn.SendNow(convertInt(dataLen), 4); + conn.SendNow("\340\000\006tracks\340", 10); + for (std::map::iterator it = tracks.begin(); it != tracks.end(); it++){ + it->second.send(conn); + } + conn.SendNow("\000\000\356", 3); + if (vod){ + conn.SendNow("\000\003vod\001", 6); + conn.SendNow(convertLongLong(1), 8); + } + if (live){ + conn.SendNow("\000\004live\001", 7); + conn.SendNow(convertLongLong(1), 8); + } + if (merged){ + conn.SendNow("\000\006merged\001", 9); + conn.SendNow(convertLongLong(1), 8); + } + conn.SendNow("\000\012moreheader\001", 13); + conn.SendNow(convertLongLong(moreheader), 8); + conn.SendNow("\000\000\356", 3);//End global object + } + + void Meta::send(Socket::Connection & conn){ + int dataLen = 16 + (vod ? 14 : 0) + (live ? 15 : 0) + (merged ? 17 : 0) + 21; + for (std::map::iterator it = tracks.begin(); it != tracks.end(); it++){ + dataLen += it->second.getSendLen(); + } + conn.SendNow(DTSC::Magic_Header, 4); + conn.SendNow(convertInt(dataLen), 4); + conn.SendNow("\340\000\006tracks\340", 10); + for (std::map::iterator it = tracks.begin(); it != tracks.end(); it++){ + it->second.send(conn); + } + conn.SendNow("\000\000\356", 3);//End tracks object + if (vod){ + conn.SendNow("\000\003vod\001", 6); + conn.SendNow(convertLongLong(1), 8); + } + if (live){ + conn.SendNow("\000\004live\001", 7); + conn.SendNow(convertLongLong(1), 8); + } + if (merged){ + conn.SendNow("\000\006merged\001", 9); + conn.SendNow(convertLongLong(1), 8); + } + conn.SendNow("\000\012moreheader\001", 13); + conn.SendNow(convertLongLong(moreheader), 8); + conn.SendNow("\000\000\356", 3);//End global object + } + + JSON::Value readOnlyTrack::toJSON(){ + JSON::Value result; + if (fragments){ + result["fragments"] = std::string((char*)fragments, fragLen * 11); + } + if (keys){ + result["keys"] = std::string((char*)keys, keyLen * 16); + } + if (parts){ + result["parts"] = std::string((char*)parts, partLen * 8); + } + result["trackid"] = trackID; + result["length"] = length; + result["firstms"] = firstms; + result["lastms"] = lastms; + result["bps"] = bps; + if (missedFrags){ + result["missed_frags"] = missedFrags; + } + result["codec"] = codec; + result["type"] = type; + result["init"] = init; + if (type == "audio"){ + result["rate"] = rate; + result["size"] = size; + result["channels"] = channels; + }else if (type == "video"){ + result["width"] = width; + result["height"] = height; + result["fpks"] = fpks; + } + return result; + } + + JSON::Value Track::toJSON(){ + JSON::Value result; + std::string tmp; + tmp.reserve(fragments.size() * 11); + for (std::deque::iterator it = fragments.begin(); it != fragments.end(); it++){ + tmp.append(it->getData(), 11); + } + result["fragments"] = tmp; + tmp = ""; + tmp.reserve(keys.size() * 16); + for (std::deque::iterator it = keys.begin(); it != keys.end(); it++){ + tmp.append(it->getData(), 16); + } + result["keys"] = tmp; + tmp = ""; + tmp.reserve(parts.size() * 8); + for (std::deque::iterator it = parts.begin(); it != parts.end(); it++){ + tmp.append(it->getData(), 8); + } + result["parts"] = tmp; + result["trackid"] = trackID; + result["length"] = length; + result["firstms"] = firstms; + result["lastms"] = lastms; + result["bps"] = bps; + if (missedFrags){ + result["missed_frags"] = missedFrags; + } + result["codec"] = codec; + result["type"] = type; + result["init"] = init; + if (type == "audio"){ + result["rate"] = rate; + result["size"] = size; + result["channels"] = channels; + }else if (type == "video"){ + result["width"] = width; + result["height"] = height; + result["fpks"] = fpks; + } + return result; + } + + JSON::Value Meta::toJSON(){ + JSON::Value result; + for (std::map::iterator it = tracks.begin(); it != tracks.end(); it++){ + if (!it->second.trackID){continue;} + result["tracks"][it->second.getIdentifier()] = it->second.toJSON(); + } + if (vod){ + result["vod"] = 1ll; + } + if (live){ + result["live"] = 1ll; + } + if (merged){ + result["merged"] = 1ll; + } + if (bufferWindow){ + result["buffer_window"]; + } + result["moreheader"] = moreheader; + return result; + } + + JSON::Value readOnlyMeta::toJSON(){ + JSON::Value result; + for (std::map::iterator it = tracks.begin(); it != tracks.end(); it++){ + if (!it->second.trackID){continue;} + result["tracks"][it->second.getIdentifier()] = it->second.toJSON(); + } + if (vod){ + result["vod"] = 1ll; + } + if (live){ + result["live"] = 1ll; + } + if (merged){ + result["merged"] = 1ll; + } + result["moreheader"] = moreheader; + if (bufferWindow){ + result["buffer_window"]; + } + return result; + } + + void Meta::reset(){ + for (std::map::iterator it = tracks.begin(); it != tracks.end(); it++){ + it->second.reset(); + } + } + + bool readOnlyMeta::isFixed(){ + for (std::map::iterator it = tracks.begin(); it != tracks.end(); it++){ + if ( !it->second.keyLen || !(it->second.keys[it->second.keyLen - 1].getBpos())){ + return false; + } + } + return true; + } + + bool Meta::isFixed(){ + for (std::map::iterator it = tracks.begin(); it != tracks.end(); it++){ + if (!it->second.keys.size() || !(it->second.keys.rbegin()->getBpos())){ + return false; + } + } + return true; + } +} diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index 231f0ab7..f32271b0 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -364,22 +364,18 @@ FLV::Tag & FLV::Tag::operator=(const FLV::Tag& O){ /// Takes the DTSC data and makes it into FLV. bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ std::string meta_str; - JSON::Value & track = S.getTrackById(S.getPacket()["trackid"].asInt()); + DTSC::Track & track = S.metadata.tracks[S.getPacket()["trackid"].asInt()]; switch (S.lastType()){ case DTSC::VIDEO: len = S.lastData().length() + 16; - if (track && track.isMember("codec")){ - if (track["codec"].asStringRef() == "H264"){ - len += 4; - } + if (track.codec == "H264"){ + len += 4; } break; case DTSC::AUDIO: len = S.lastData().length() + 16; - if (track && track.isMember("codec")){ - if (track["codec"].asStringRef() == "AAC"){ - len += 1; - } + if (track.codec == "AAC"){ + len += 1; } break; case DTSC::META:{ @@ -418,10 +414,10 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ offset(S.getPacket()["offset"].asInt()); } data[11] = 0; - if (track.isMember("codec") && track["codec"].asStringRef() == "H264"){ + if (track.codec == "H264"){ data[11] += 7; } - if (track.isMember("codec") && track["codec"].asStringRef() == "H263"){ + if (track.codec == "H263"){ data[11] += 2; } if (S.getPacket().isMember("keyframe")){ @@ -442,13 +438,13 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ data[12] = 1; //raw AAC data, not sequence header } data[11] = 0; - if (track.isMember("codec") && track["codec"].asString() == "AAC"){ + if (track.codec == "AAC"){ data[11] += 0xA0; } - if (track.isMember("codec") && track["codec"].asString() == "MP3"){ + if (track.codec == "MP3"){ data[11] += 0x20; } - unsigned int datarate = track["rate"].asInt(); + unsigned int datarate = track.rate; if (datarate >= 44100){ data[11] += 0x0C; }else if (datarate >= 22050){ @@ -456,10 +452,10 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ }else if (datarate >= 11025){ data[11] += 0x04; } - if (track["size"].asInt() == 16){ + if (track.size == 16){ data[11] += 0x02; } - if (track["channels"].asInt() > 1){ + if (track.channels > 1){ data[11] += 0x01; } break; @@ -508,26 +504,20 @@ void FLV::Tag::setLen(){ data[ --i] = (len4) & 0xFF; } -/// FLV Video init data loader function from DTSC. -/// Takes the DTSC Video init data and makes it into FLV. -/// Assumes init data is available - so check before calling! -bool FLV::Tag::DTSCVideoInit(DTSC::Stream & S){ - return DTSCVideoInit(S.metadata["video"]); -} - -bool FLV::Tag::DTSCVideoInit(JSON::Value & video){ +/// FLV Video init data loader function from JSON. +bool FLV::Tag::DTSCVideoInit(DTSC::Track & video){ //Unknown? Assume H264. - if (video["codec"].asString() == "?"){ - video["codec"] = "H264"; + if (video.codec == "?"){ + video.codec = "H264"; } - if (video["codec"].asString() == "H264"){ - len = video["init"].asString().length() + 20; + if (video.codec == "H264"){ + len = video.init.size() + 20; } if (len > 0){ if ( !checkBufferSize()){ return false; } - memcpy(data + 16, video["init"].asString().c_str(), len - 20); + memcpy(data + 16, video.init.c_str(), len - 20); data[12] = 0; //H264 sequence header data[13] = 0; data[14] = 0; @@ -546,36 +536,30 @@ bool FLV::Tag::DTSCVideoInit(JSON::Value & video){ return true; } -/// FLV Audio init data loader function from DTSC. -/// Takes the DTSC Audio init data and makes it into FLV. -/// Assumes init data is available - so check before calling! -bool FLV::Tag::DTSCAudioInit(DTSC::Stream & S){ - return DTSCAudioInit(S.metadata["audio"]); -} - -bool FLV::Tag::DTSCAudioInit(JSON::Value & audio){ +/// FLV Audio init data loader function from JSON. +bool FLV::Tag::DTSCAudioInit(DTSC::Track & audio){ len = 0; //Unknown? Assume AAC. - if (audio["codec"].asString() == "?"){ - audio["codec"] = "AAC"; + if (audio.codec == "?"){ + audio.codec = "AAC"; } - if (audio["codec"].asString() == "AAC"){ - len = audio["init"].asString().length() + 17; + if (audio.codec == "AAC"){ + len = audio.init.size() + 17; } if (len > 0){ if ( !checkBufferSize()){ return false; } - memcpy(data + 13, audio["init"].asString().c_str(), len - 17); + memcpy(data + 13, audio.init.c_str(), len - 17); data[12] = 0; //AAC sequence header data[11] = 0; - if (audio["codec"].asString() == "AAC"){ + if (audio.codec == "AAC"){ data[11] += 0xA0; } - if (audio["codec"].asString() == "MP3"){ + if (audio.codec == "MP3"){ data[11] += 0x20; } - unsigned int datarate = audio["rate"].asInt(); + unsigned int datarate = audio.rate; if (datarate >= 44100){ data[11] += 0x0C; }else if (datarate >= 22050){ @@ -583,10 +567,10 @@ bool FLV::Tag::DTSCAudioInit(JSON::Value & audio){ }else if (datarate >= 11025){ data[11] += 0x04; } - if (audio["size"].asInt() == 16){ + if (audio.size == 16){ data[11] += 0x02; } - if (audio["channels"].asInt() > 1){ + if (audio.channels > 1){ data[11] += 0x01; } @@ -606,34 +590,34 @@ bool FLV::Tag::DTSCAudioInit(JSON::Value & audio){ /// FLV metadata loader function from DTSC. /// Takes the DTSC metadata and makes it into FLV. /// Assumes metadata is available - so check before calling! -bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S, JSON::Value & videoRef, JSON::Value & audioRef){ +bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S, DTSC::Track & videoRef, DTSC::Track & audioRef){ //Unknown? Assume AAC. - if (audioRef["codec"].asString() == "?"){ - audioRef["codec"] = "AAC"; + if (audioRef.codec == "?"){ + audioRef.codec = "AAC"; } //Unknown? Assume H264. - if (videoRef["codec"].asString() == "?"){ - videoRef["codec"] = "H264"; + if (videoRef.codec == "?"){ + videoRef.codec = "H264"; } AMF::Object amfdata("root", AMF::AMF0_DDV_CONTAINER); amfdata.addContent(AMF::Object("", "onMetaData")); amfdata.addContent(AMF::Object("", AMF::AMF0_ECMA_ARRAY)); - if (S.metadata.isMember("length")){ - amfdata.getContentP(1)->addContent(AMF::Object("duration", S.metadata["length"].asInt(), AMF::AMF0_NUMBER)); + if (S.metadata.vod){ + amfdata.getContentP(1)->addContent(AMF::Object("duration", videoRef.lastms / 1000, AMF::AMF0_NUMBER)); amfdata.getContentP(1)->addContent(AMF::Object("moovPosition", 40, AMF::AMF0_NUMBER)); AMF::Object keys("keyframes", AMF::AMF0_OBJECT); keys.addContent(AMF::Object("filepositions", AMF::AMF0_STRICT_ARRAY)); keys.addContent(AMF::Object("times", AMF::AMF0_STRICT_ARRAY)); int total_byterate = 0; if (videoRef){ - total_byterate += videoRef["bps"].asInt(); + total_byterate += videoRef.bps; } if (audioRef){ - total_byterate += audioRef["bps"].asInt(); + total_byterate += audioRef.bps; } - for (int i = 0; i < S.metadata["length"].asInt(); ++i){ //for each second in the file + for (int i = 0; i < videoRef.lastms / 1000; ++i){ //for each second in the file keys.getContentP(0)->addContent(AMF::Object("", i * total_byterate, AMF::AMF0_NUMBER)); //multiply by byterate for fake byte positions keys.getContentP(1)->addContent(AMF::Object("", i, AMF::AMF0_NUMBER)); //seconds } @@ -641,62 +625,45 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S, JSON::Value & videoRef, JSON::Valu } if (videoRef){ amfdata.getContentP(1)->addContent(AMF::Object("hasVideo", 1, AMF::AMF0_BOOL)); - if (videoRef["codec"].asString() == "H264"){ + if (videoRef.codec == "H264"){ amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", (std::string)"avc1")); } - if (videoRef["codec"].asString() == "VP6"){ + if (videoRef.codec == "VP6"){ amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", 4, AMF::AMF0_NUMBER)); } - if (videoRef["codec"].asString() == "H263"){ + if (videoRef.codec == "H263"){ amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", 2, AMF::AMF0_NUMBER)); } - if (videoRef.isMember("width")){ - amfdata.getContentP(1)->addContent(AMF::Object("width", videoRef["width"].asInt(), AMF::AMF0_NUMBER)); - } - if (videoRef.isMember("height")){ - amfdata.getContentP(1)->addContent(AMF::Object("height", videoRef["height"].asInt(), AMF::AMF0_NUMBER)); - } - if (videoRef.isMember("fpks")){ - amfdata.getContentP(1)->addContent(AMF::Object("videoframerate", (double)videoRef["fpks"].asInt() / 1000.0, AMF::AMF0_NUMBER)); - } - if (videoRef.isMember("bps")){ - amfdata.getContentP(1)->addContent(AMF::Object("videodatarate", (double)videoRef["bps"].asInt() * 128.0, AMF::AMF0_NUMBER)); - } + amfdata.getContentP(1)->addContent(AMF::Object("width", videoRef.width, AMF::AMF0_NUMBER)); + amfdata.getContentP(1)->addContent(AMF::Object("height", videoRef.height, AMF::AMF0_NUMBER)); + amfdata.getContentP(1)->addContent(AMF::Object("videoframerate", (double)videoRef.fpks / 1000.0, AMF::AMF0_NUMBER)); + amfdata.getContentP(1)->addContent(AMF::Object("videodatarate", (double)videoRef.bps * 128.0, AMF::AMF0_NUMBER)); } if (audioRef){ amfdata.getContentP(1)->addContent(AMF::Object("hasAudio", 1, AMF::AMF0_BOOL)); amfdata.getContentP(1)->addContent(AMF::Object("audiodelay", 0, AMF::AMF0_NUMBER)); - if (audioRef["codec"].asString() == "AAC"){ + if (audioRef.codec == "AAC"){ amfdata.getContentP(1)->addContent(AMF::Object("audiocodecid", (std::string)"mp4a")); } - if (audioRef["codec"].asString() == "MP3"){ + if (audioRef.codec == "MP3"){ amfdata.getContentP(1)->addContent(AMF::Object("audiocodecid", (std::string)"mp3")); } - if (audioRef.isMember("channels")){ - amfdata.getContentP(1)->addContent(AMF::Object("audiochannels", audioRef["channels"].asInt(), AMF::AMF0_NUMBER)); - } - if (audioRef.isMember("rate")){ - amfdata.getContentP(1)->addContent(AMF::Object("audiosamplerate", audioRef["rate"].asInt(), AMF::AMF0_NUMBER)); - } - if (audioRef.isMember("size")){ - amfdata.getContentP(1)->addContent(AMF::Object("audiosamplesize", audioRef["size"].asInt(), AMF::AMF0_NUMBER)); - } - if (audioRef.isMember("bps")){ - amfdata.getContentP(1)->addContent(AMF::Object("audiodatarate", (double)audioRef["bps"].asInt() * 128.0, AMF::AMF0_NUMBER)); - } + amfdata.getContentP(1)->addContent(AMF::Object("audiochannels", audioRef.channels, AMF::AMF0_NUMBER)); + amfdata.getContentP(1)->addContent(AMF::Object("audiosamplerate", audioRef.rate, AMF::AMF0_NUMBER)); + amfdata.getContentP(1)->addContent(AMF::Object("audiosamplesize", audioRef.size, AMF::AMF0_NUMBER)); + amfdata.getContentP(1)->addContent(AMF::Object("audiodatarate", (double)audioRef.bps * 128.0, AMF::AMF0_NUMBER)); } AMF::Object trinfo = AMF::Object("trackinfo", AMF::AMF0_STRICT_ARRAY); int i = 0; if (audioRef){ trinfo.addContent(AMF::Object("", AMF::AMF0_OBJECT)); - trinfo.getContentP(i)->addContent( - AMF::Object("length", ((double)S.metadata["length"].asInt()) * ((double)audioRef["rate"].asInt()), AMF::AMF0_NUMBER)); - trinfo.getContentP(i)->addContent(AMF::Object("timescale", audioRef["rate"].asInt(), AMF::AMF0_NUMBER)); + trinfo.getContentP(i)->addContent(AMF::Object("length", ((double)S.metadata.length) * ((double)audioRef.rate), AMF::AMF0_NUMBER)); + trinfo.getContentP(i)->addContent(AMF::Object("timescale", audioRef.rate, AMF::AMF0_NUMBER)); trinfo.getContentP(i)->addContent(AMF::Object("sampledescription", AMF::AMF0_STRICT_ARRAY)); - if (audioRef["codec"].asString() == "AAC"){ + if (audioRef.codec == "AAC"){ trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string)"mp4a")); } - if (audioRef["codec"].asString() == "MP3"){ + if (audioRef.codec == "MP3"){ trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string)"mp3")); } ++i; @@ -704,16 +671,16 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S, JSON::Value & videoRef, JSON::Valu if (videoRef){ trinfo.addContent(AMF::Object("", AMF::AMF0_OBJECT)); trinfo.getContentP(i)->addContent( - AMF::Object("length", ((double)S.metadata["length"].asInt()) * ((double)videoRef["fkps"].asInt() / 1000.0), AMF::AMF0_NUMBER)); - trinfo.getContentP(i)->addContent(AMF::Object("timescale", ((double)videoRef["fkps"].asInt() / 1000.0), AMF::AMF0_NUMBER)); + AMF::Object("length", ((double)videoRef.lastms / 1000) * ((double)videoRef.fpks / 1000.0), AMF::AMF0_NUMBER)); + trinfo.getContentP(i)->addContent(AMF::Object("timescale", ((double)videoRef.fpks / 1000.0), AMF::AMF0_NUMBER)); trinfo.getContentP(i)->addContent(AMF::Object("sampledescription", AMF::AMF0_STRICT_ARRAY)); - if (videoRef["codec"].asString() == "H264"){ + if (videoRef.codec == "H264"){ trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string)"avc1")); } - if (videoRef["codec"].asString() == "VP6"){ + if (videoRef.codec == "VP6"){ trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string)"vp6")); } - if (videoRef["codec"].asString() == "H263"){ + if (videoRef.codec == "H263"){ trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string)"h263")); } ++i; @@ -1061,12 +1028,6 @@ JSON::Value FLV::Tag::toJSON(JSON::Value & metadata){ if ( !metadata["tracks"]["track1"].isMember("bps")){ metadata["tracks"]["track1"]["bps"] = 0; } - if ( !metadata["tracks"]["track1"].isMember("keyms")){ - metadata["tracks"]["track1"]["keyms"] = 0; - } - if ( !metadata["tracks"]["track1"].isMember("keyvar")){ - metadata["tracks"]["track1"]["keyvar"] = 0; - } } return pack_out; //empty } diff --git a/lib/flv_tag.h b/lib/flv_tag.h index 1c1e0446..591b8337 100644 --- a/lib/flv_tag.h +++ b/lib/flv_tag.h @@ -47,11 +47,9 @@ namespace FLV { //loader functions bool ChunkLoader(const RTMPStream::Chunk& O); bool DTSCLoader(DTSC::Stream & S); - bool DTSCVideoInit(DTSC::Stream & S); - bool DTSCVideoInit(JSON::Value & video); - bool DTSCAudioInit(DTSC::Stream & S); - bool DTSCAudioInit(JSON::Value & audio); - bool DTSCMetaInit(DTSC::Stream & S, JSON::Value & videoRef, JSON::Value & audioRef); + bool DTSCVideoInit(DTSC::Track & video); + bool DTSCAudioInit(DTSC::Track & audio); + bool DTSCMetaInit(DTSC::Stream & S, DTSC::Track & videoRef, DTSC::Track & audioRef); JSON::Value toJSON(JSON::Value & metadata); bool MemLoader(char * D, unsigned int S, unsigned int & P); bool FileLoader(FILE * f); diff --git a/lib/ogg.h b/lib/ogg.h index 368adf5b..6d31edb1 100644 --- a/lib/ogg.h +++ b/lib/ogg.h @@ -1,8 +1,8 @@ #pragma once -#include -#include -#include -#include"dtsc.h" +#include +#include +#include +#include "dtsc.h" #include "theora.h" #include "vorbis.h" #include "json.h" From 12abcea138ff10cf52b45eeec951fcedb198dce2 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Thu, 28 Nov 2013 10:18:50 +0100 Subject: [PATCH 558/788] Updated DTSC::isFixed --- lib/dtsc.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 87a29c94..33948afd 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -968,12 +968,6 @@ DTSC::File::~File(){ bool DTSC::isFixed(JSON::Value & metadata){ - if (metadata.isMember("is_fixed")){return true;} - if ( !metadata.isMember("tracks")){return false;} - for (JSON::ObjIter it = metadata["tracks"].ObjBegin(); it != metadata["tracks"].ObjEnd(); it++){ - if (!(it->second.isMember("keys") && it->second["keys"].isArray() && it->second["keys"][0u].isMember("bpos"))){ - return false; - } - } - return true; + DTSC::Meta testFixed(metadata); + return testFixed.isFixed(); } From 8f8b3694b09a8a6b9d89cdd16f5b8701fcf86966 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 28 Nov 2013 10:26:24 +0100 Subject: [PATCH 559/788] Fixed FLV attempting to load empty DTSC packets. --- lib/flv_tag.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index f32271b0..0e89af35 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -394,6 +394,7 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ break; } default: //ignore all other types (there are currently no other types...) + return false; break; } if (len > 0){ From 35ee722f81499bb1c19f96de728713b35e801598 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Thu, 28 Nov 2013 11:08:46 +0100 Subject: [PATCH 560/788] Added ID and comment header support for theora and vorbis --- lib/dtsc.h | 3 +++ lib/dtscmeta.cpp | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/lib/dtsc.h b/lib/dtsc.h index a3119105..279d3710 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -178,6 +178,9 @@ namespace DTSC { int width; int height; int fpks; + //vorbis and theora only + std::string idHeader; + std::string commentHeader; }; class Track : public readOnlyTrack { diff --git a/lib/dtscmeta.cpp b/lib/dtscmeta.cpp index 1172e037..18ea01ef 100644 --- a/lib/dtscmeta.cpp +++ b/lib/dtscmeta.cpp @@ -166,6 +166,10 @@ namespace DTSC { height = trackRef["height"].asInt(); fpks = trackRef["fpks"].asInt(); } + if (codec == "vorbis" || codec == "theora"){ + idHeader = trackRef["idheader"].asString(); + commentHeader = trackRef["commentheader"].asString(); + } } Track::Track(){} @@ -186,6 +190,8 @@ namespace DTSC { width = rhs.width; height = rhs.height; fpks = rhs.fpks; + idHeader = rhs.idHeader; + commentHeader = rhs.commentHeader; if (rhs.fragments && rhs.fragLen){ fragments = std::deque(rhs.fragments, rhs.fragments + rhs.fragLen); } @@ -229,6 +235,10 @@ namespace DTSC { height = trackRef["height"].asInt(); fpks = trackRef["fpks"].asInt(); } + if (codec == "vorbis" || codec == "theora"){ + idHeader = trackRef["idheader"].asString(); + commentHeader = trackRef["commentheader"].asString(); + } } void Track::update(JSON::Value & pack){ @@ -459,6 +469,10 @@ namespace DTSC { }else if (type == "video"){ result += 48; } + if (codec == "vorbis" || codec == "theora"){ + result += 15 + idHeader.size();//idheader + result += 20 + commentHeader.size();//commentheader + } return result; } @@ -472,6 +486,10 @@ namespace DTSC { }else if (type == "video"){ result += 48; } + if (codec == "vorbis" || codec == "theora"){ + result += 15 + idHeader.size();//idheader + result += 20 + commentHeader.size();//commentheader + } return result; } @@ -522,6 +540,14 @@ namespace DTSC { conn.SendNow("\000\004fpks\001", 7); conn.SendNow(convertLongLong(fpks), 8); } + if (codec == "vorbis" || codec == "theora"){ + conn.SendNow("\000\010idheader\002", 11); + conn.SendNow(convertInt(idHeader.size()), 4); + conn.SendNow(idHeader); + conn.SendNow("\000\015commentheader\002", 16); + conn.SendNow(convertInt(commentHeader.size()), 4); + conn.SendNow(commentHeader); + } conn.SendNow("\000\000\356", 3);//End this track Object } @@ -578,6 +604,14 @@ namespace DTSC { conn.SendNow("\000\004fpks\001", 7); conn.SendNow(convertLongLong(fpks), 8); } + if (codec == "vorbis" || codec == "theora"){ + conn.SendNow("\000\010idheader\002", 11); + conn.SendNow(convertInt(idHeader.size()), 4); + conn.SendNow(idHeader); + conn.SendNow("\000\015commentheader\002", 16); + conn.SendNow(convertInt(commentHeader.size()), 4); + conn.SendNow(commentHeader); + } conn.SendNow("\000\000\356", 3);//End this track Object } @@ -670,6 +704,10 @@ namespace DTSC { result["height"] = height; result["fpks"] = fpks; } + if (codec == "vorbis" || codec == "theora"){ + result["idheader"] = idHeader; + result["commentheader"] = commentHeader; + } return result; } @@ -713,6 +751,10 @@ namespace DTSC { result["height"] = height; result["fpks"] = fpks; } + if (codec == "vorbis" || codec == "theora"){ + result["idheader"] = idHeader; + result["commentheader"] = commentHeader; + } return result; } From 43e49dcba1025c2eb3bd5b9e7905ed881c908312 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Thu, 28 Nov 2013 14:05:45 +0100 Subject: [PATCH 561/788] Fix for the isFixed function --- lib/dtscmeta.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dtscmeta.cpp b/lib/dtscmeta.cpp index 18ea01ef..addbe4d6 100644 --- a/lib/dtscmeta.cpp +++ b/lib/dtscmeta.cpp @@ -819,7 +819,7 @@ namespace DTSC { bool Meta::isFixed(){ for (std::map::iterator it = tracks.begin(); it != tracks.end(); it++){ - if (!it->second.keys.size() || !(it->second.keys.rbegin()->getBpos())){ + if (it->second.keys.size() && !(it->second.keys.rbegin()->getBpos())){ return false; } } From 09e4f597a3c705ca9d744525dd0b79151752761c Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Thu, 28 Nov 2013 14:44:16 +0100 Subject: [PATCH 562/788] Amended fix for isFixed --- lib/dtscmeta.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/dtscmeta.cpp b/lib/dtscmeta.cpp index addbe4d6..2fd7e59f 100644 --- a/lib/dtscmeta.cpp +++ b/lib/dtscmeta.cpp @@ -819,7 +819,10 @@ namespace DTSC { bool Meta::isFixed(){ for (std::map::iterator it = tracks.begin(); it != tracks.end(); it++){ - if (it->second.keys.size() && !(it->second.keys.rbegin()->getBpos())){ + if (it->second.type == "meta" || it->second.type == ""){ + continue; + } + if (!it->second.keys.size() || !(it->second.keys.rbegin()->getBpos())){ return false; } } From 3b3630ea77f158fdd961a1e6df90031d71e5117f Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 28 Nov 2013 15:55:40 +0100 Subject: [PATCH 563/788] Updated FLV library to use DTSC::Meta instead of JSON::Value --- lib/flv_tag.cpp | 114 ++++++++++++++---------------------------------- lib/flv_tag.h | 2 +- 2 files changed, 34 insertions(+), 82 deletions(-) diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index 0e89af35..e0d27024 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -923,7 +923,7 @@ bool FLV::Tag::FileLoader(FILE * f){ return false; } //FLV_GetPacket -JSON::Value FLV::Tag::toJSON(JSON::Value & metadata){ +JSON::Value FLV::Tag::toJSON(DTSC::Meta & metadata){ JSON::Value pack_out; // Storage for outgoing metadata. if (data[0] == 0x12){ @@ -937,61 +937,32 @@ JSON::Value FLV::Tag::toJSON(JSON::Value & metadata){ } } if (tmp){ - if (tmp->getContentP("videocodecid")){ - switch ((unsigned int)tmp->getContentP("videocodecid")->NumValue()){ - case 2: - metadata["tracks"]["track1"]["codec"] = "H263"; - break; - case 4: - metadata["tracks"]["track1"]["codec"] = "VP6"; - break; - case 7: - metadata["tracks"]["track1"]["codec"] = "H264"; - break; - default: - metadata["tracks"]["track1"]["codec"] = "?"; - break; - } - } - if (tmp->getContentP("audiocodecid")){ - switch ((unsigned int)tmp->getContentP("audiocodecid")->NumValue()){ - case 2: - metadata["tracks"]["track2"]["codec"] = "MP3"; - break; - case 10: - metadata["tracks"]["track2"]["codec"] = "AAC"; - break; - default: - metadata["tracks"]["track2"]["codec"] = "?"; - break; - } - } if (tmp->getContentP("width")){ - metadata["tracks"]["track1"]["width"] = (long long int)tmp->getContentP("width")->NumValue(); + metadata.tracks[1].width = (long long int)tmp->getContentP("width")->NumValue(); } if (tmp->getContentP("height")){ - metadata["tracks"]["track1"]["height"] = (long long int)tmp->getContentP("height")->NumValue(); + metadata.tracks[1].height = (long long int)tmp->getContentP("height")->NumValue(); } if (tmp->getContentP("framerate")){ - metadata["tracks"]["track1"]["fpks"] = (long long int)(tmp->getContentP("framerate")->NumValue() * 1000.0); + metadata.tracks[1].fpks = (long long int)(tmp->getContentP("framerate")->NumValue() * 1000.0); } if (tmp->getContentP("videodatarate")){ - metadata["tracks"]["track1"]["bps"] = (long long int)(tmp->getContentP("videodatarate")->NumValue() * 1024) / 8; + metadata.tracks[1].bps = (long long int)(tmp->getContentP("videodatarate")->NumValue() * 1024) / 8; } if (tmp->getContentP("audiodatarate")){ - metadata["tracks"]["track2"]["bps"] = (long long int)(tmp->getContentP("audiodatarate")->NumValue() * 1024) / 8; + metadata.tracks[2].bps = (long long int)(tmp->getContentP("audiodatarate")->NumValue() * 1024) / 8; } if (tmp->getContentP("audiosamplerate")){ - metadata["tracks"]["track2"]["rate"] = (long long int)tmp->getContentP("audiosamplerate")->NumValue(); + metadata.tracks[2].rate = (long long int)tmp->getContentP("audiosamplerate")->NumValue(); } if (tmp->getContentP("audiosamplesize")){ - metadata["tracks"]["track2"]["size"] = (long long int)tmp->getContentP("audiosamplesize")->NumValue(); + metadata.tracks[2].size = (long long int)tmp->getContentP("audiosamplesize")->NumValue(); } if (tmp->getContentP("stereo")){ if (tmp->getContentP("stereo")->NumValue() == 1){ - metadata["tracks"]["track2"]["channels"] = 2; + metadata.tracks[2].channels = 2; }else{ - metadata["tracks"]["track2"]["channels"] = 1; + metadata.tracks[2].channels = 1; } } for (int i = 0; i < tmp->hasContent(); ++i){ @@ -1011,78 +982,58 @@ JSON::Value FLV::Tag::toJSON(JSON::Value & metadata){ pack_out["time"] = tagTime(); } } - metadata["tracks"]["track1"]["trackid"] = 1; - metadata["tracks"]["track1"]["type"] = "video"; - if ( !metadata["tracks"]["track1"].isMember("length")){ - metadata["tracks"]["track1"]["length"] = 0; - } - if (metadata["tracks"].isMember("track1")){ - if ( !metadata["tracks"]["track1"].isMember("width")){ - metadata["tracks"]["track1"]["width"] = 0; - } - if ( !metadata["tracks"]["track1"].isMember("height")){ - metadata["tracks"]["track1"]["height"] = 0; - } - if ( !metadata["tracks"]["track1"].isMember("fpks")){ - metadata["tracks"]["track1"]["fpks"] = 0; - } - if ( !metadata["tracks"]["track1"].isMember("bps")){ - metadata["tracks"]["track1"]["bps"] = 0; - } - } return pack_out; //empty } if (data[0] == 0x08){ char audiodata = data[11]; if (needsInitData() && isInitData()){ if ((audiodata & 0xF0) == 0xA0){ - metadata["tracks"]["track2"]["init"] = std::string((char*)data + 13, (size_t)len - 17); + metadata.tracks[2].init = std::string((char*)data + 13, (size_t)len - 17); }else{ - metadata["tracks"]["track2"]["init"] = std::string((char*)data + 12, (size_t)len - 16); + metadata.tracks[2].init = std::string((char*)data + 12, (size_t)len - 16); } return pack_out; //skip rest of parsing, get next tag. } - pack_out["datatype"] = "audio"; pack_out["time"] = tagTime(); pack_out["trackid"] = 2; - metadata["tracks"]["track2"]["trackid"] = 2; - metadata["tracks"]["track2"]["type"] = "audio"; - if ( !metadata["tracks"]["track2"].isMember("codec") || metadata["tracks"]["track2"]["codec"].asString() == "?" || metadata["tracks"]["track2"]["codec"].asString() == ""){ - metadata["tracks"]["track2"]["codec"] = getAudioCodec(); + metadata.tracks[2].trackID = 2; + metadata.tracks[2].type = "audio"; + if (metadata.tracks[2].codec == ""){ + metadata.tracks[2].codec = getAudioCodec(); } - if ( !metadata["tracks"]["track2"].isMember("rate") || metadata["tracks"]["track2"]["rate"].asInt() < 1){ + if ( !metadata.tracks[2].rate){ switch (audiodata & 0x0C){ case 0x0: - metadata["tracks"]["track2"]["rate"] = 5512; + metadata.tracks[2].rate = 5512; break; case 0x4: - metadata["tracks"]["track2"]["rate"] = 11025; + metadata.tracks[2].rate = 11025; break; case 0x8: - metadata["tracks"]["track2"]["rate"] = 22050; + metadata.tracks[2].rate = 22050; break; case 0xC: - metadata["tracks"]["track2"]["rate"] = 44100; + metadata.tracks[2].rate = 44100; break; } } - if ( !metadata["tracks"]["track2"].isMember("size") || metadata["tracks"]["track2"]["size"].asInt() < 1){ + if ( !metadata.tracks[2].size){ switch (audiodata & 0x02){ case 0x0: - metadata["tracks"]["track2"]["size"] = 8; + metadata.tracks[2].size = 8; break; case 0x2: - metadata["tracks"]["track2"]["size"] = 16; + metadata.tracks[2].size = 16; break; } } - if ( !metadata["tracks"]["track2"].isMember("channels") || metadata["tracks"]["track2"]["channels"].asInt() < 1){ + if ( !metadata.tracks[2].channels){ switch (audiodata & 0x01){ case 0x0: - metadata["tracks"]["track2"]["channels"] = 1; + metadata.tracks[2].channels = 1; break; case 0x1: - metadata["tracks"]["track2"]["channels"] = 2; + metadata.tracks[2].channels = 2; break; } } @@ -1106,19 +1057,20 @@ JSON::Value FLV::Tag::toJSON(JSON::Value & metadata){ if (len < 21){ return JSON::Value(); } - metadata["tracks"]["track1"]["init"] = std::string((char*)data + 16, (size_t)len - 20); + metadata.tracks[1].init = std::string((char*)data + 16, (size_t)len - 20); }else{ if (len < 17){ return JSON::Value(); } - metadata["tracks"]["track1"]["init"] = std::string((char*)data + 12, (size_t)len - 16); + metadata.tracks[1].init = std::string((char*)data + 12, (size_t)len - 16); } return pack_out; //skip rest of parsing, get next tag. } - if ( !metadata["tracks"]["track1"].isMember("codec") || metadata["tracks"]["track1"]["codec"].asString() == "?" || metadata["tracks"]["track1"]["codec"].asString() == ""){ - metadata["tracks"]["track1"]["codec"] = getVideoCodec(); + if (metadata.tracks[1].codec == ""){ + metadata.tracks[1].codec = getVideoCodec(); } - pack_out["datatype"] = "video"; + metadata.tracks[1].type = "video"; + metadata.tracks[1].trackID = 1; pack_out["trackid"] = 1; switch (videodata & 0xF0){ case 0x10: diff --git a/lib/flv_tag.h b/lib/flv_tag.h index 591b8337..201b846f 100644 --- a/lib/flv_tag.h +++ b/lib/flv_tag.h @@ -50,7 +50,7 @@ namespace FLV { bool DTSCVideoInit(DTSC::Track & video); bool DTSCAudioInit(DTSC::Track & audio); bool DTSCMetaInit(DTSC::Stream & S, DTSC::Track & videoRef, DTSC::Track & audioRef); - JSON::Value toJSON(JSON::Value & metadata); + JSON::Value toJSON(DTSC::Meta & metadata); bool MemLoader(char * D, unsigned int S, unsigned int & P); bool FileLoader(FILE * f); protected: From ae17368228e81de8e79c400a3ddeea6d3ec8dc06 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Fri, 29 Nov 2013 14:28:15 +0100 Subject: [PATCH 564/788] Final fix for the DTSC::isFixed function --- lib/dtsc.cpp | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 33948afd..922aef39 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -968,6 +968,27 @@ DTSC::File::~File(){ bool DTSC::isFixed(JSON::Value & metadata){ - DTSC::Meta testFixed(metadata); - return testFixed.isFixed(); + if (metadata.isMember("is_fixed")){return true;} + if ( !metadata.isMember("tracks")){return false;} + for (JSON::ObjIter it = metadata["tracks"].ObjBegin(); it != metadata["tracks"].ObjEnd(); it++){ + if (it->second["type"].asString() == "meta"){ + continue; + } + if (!it->second["keys"].isString()){ + std::cerr << "Not fixed because keys for track " << it->second["trackid"].asInt() << " is not a string" << std::endl; + return false; + } + //Check for bpos: last element bpos != 0 + std::string keyRef = it->second["keys"].asStringRef(); + if (keyRef.size() < 16){ + std::cerr << "No keys in track " << it->second["trackid"].asInt() << std::endl; + return false; + } + int offset = keyRef.size() - 17; + if (!(keyRef[offset] | keyRef[offset+1] | keyRef[offset+2] | keyRef[offset+3] | keyRef[offset+4])){ + std::cerr << "Not fixed because last bpos for track " << it->second["trackid"].asInt() << " it 0" << std::endl; + return false; + } + } + return true; } From eaaa2936b41e822b1de3a1c82f1ab47fc8cd2c81 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Fri, 29 Nov 2013 14:28:38 +0100 Subject: [PATCH 565/788] Fixed an error of thought with regards to maximum size of a part. --- lib/dtsc.h | 6 +++--- lib/dtscmeta.cpp | 43 +++++++++++++++++++++++-------------------- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/lib/dtsc.h b/lib/dtsc.h index 279d3710..632bc062 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -104,15 +104,15 @@ namespace DTSC { class Part{ public: - short getSize(); - void setSize(short newSize); + long getSize(); + void setSize(long newSize); short getDuration(); void setDuration(short newDuration); long getOffset(); void setOffset(long newOffset); char* getData(); private: - char data[8]; + char data[9]; }; class Key{ diff --git a/lib/dtscmeta.cpp b/lib/dtscmeta.cpp index 2fd7e59f..3cb988f5 100644 --- a/lib/dtscmeta.cpp +++ b/lib/dtscmeta.cpp @@ -1,28 +1,30 @@ #include "dtsc.h" namespace DTSC { - short Part::getSize(){ - return ntohs(((short*)data)[0]); + long Part::getSize(){ + return ((long)data[0] << 16) | ((long)data[1] << 8) | data[2]; } - void Part::setSize(short newSize){ - ((short*)data)[0] = htons(newSize); + void Part::setSize(long newSize){ + data[0] = (newSize & 0xFF0000) >> 16; + data[1] = (newSize & 0x00FF00) >> 8; + data[2] = (newSize & 0x0000FF); } short Part::getDuration(){ - return ntohs(((short*)(data+2))[0]); + return ntohs(((short*)(data+3))[0]); } void Part::setDuration(short newDuration){ - ((short*)(data+2))[0] = htons(newDuration); + ((short*)(data+3))[0] = htons(newDuration); } long Part::getOffset(){ - return ntohl(((int*)(data+4))[0]); + return ntohl(((int*)(data+5))[0]); } void Part::setOffset(long newOffset){ - ((int*)(data+4))[0] = htonl(newOffset); + ((int*)(data+5))[0] = htonl(newOffset); } char* Part::getData(){ @@ -142,7 +144,7 @@ namespace DTSC { } if (trackRef.isMember("parts")){ parts = (Part*)trackRef["parts"].asString().data(); - partLen = trackRef["parts"].asString().size() / 8; + partLen = trackRef["parts"].asString().size() / 9; }else{ parts = 0; partLen = 0; @@ -214,7 +216,7 @@ namespace DTSC { } if (trackRef.isMember("parts") && trackRef["parts"].isString()){ Part* tmp = (Part*)trackRef["parts"].asString().data(); - parts = std::deque(tmp,tmp + (trackRef["parts"].asString().size() / 8)); + parts = std::deque(tmp,tmp + (trackRef["parts"].asString().size() / 9)); } trackID = trackRef["trackid"].asInt(); length = trackRef["length"].asInt(); @@ -256,7 +258,7 @@ namespace DTSC { } parts.push_back(newPart); lastms = pack["time"].asInt(); - if (pack.isMember("keyframe") || !keys.size() || (type != "video" && pack["time"].asInt() - 2000 > keys[keys.size() - 1].getTime())){ + if (pack.isMember("keyframe") || !keys.size() || (type != "video" && pack["time"].asInt() - 5000 > keys[keys.size() - 1].getTime())){ Key newKey; newKey.setTime(pack["time"].asInt()); newKey.setParts(0); @@ -463,7 +465,7 @@ namespace DTSC { int result = 163 + init.size() + codec.size() + type.size() + getIdentifier().size(); result += fragLen * 11; result += keyLen * 16; - result += partLen * 8; + result += partLen * 9; if (type == "audio"){ result += 49; }else if (type == "video"){ @@ -480,7 +482,7 @@ namespace DTSC { int result = 163 + init.size() + codec.size() + type.size() + getIdentifier().size(); result += fragments.size() * 11; result += keys.size() * 16; - result += parts.size() * 8; + result += parts.size() * 9; if (type == "audio"){ result += 49; }else if (type == "video"){ @@ -504,8 +506,8 @@ namespace DTSC { conn.SendNow(convertInt(keyLen*16), 4); conn.SendNow((char*)keys, keyLen*16); conn.SendNow("\000\005parts\002", 8); - conn.SendNow(convertInt(partLen*8), 4); - conn.SendNow((char*)parts, partLen*8); + conn.SendNow(convertInt(partLen*9), 4); + conn.SendNow((char*)parts, partLen*9); conn.SendNow("\000\007trackid\001", 10); conn.SendNow(convertLongLong(trackID), 8); conn.SendNow("\000\006length\001", 9); @@ -566,9 +568,9 @@ namespace DTSC { conn.SendNow(it->getData(), 16); } conn.SendNow("\000\005parts\002", 8); - conn.SendNow(convertInt(parts.size()*8), 4); + conn.SendNow(convertInt(parts.size()*9), 4); for (std::deque::iterator it = parts.begin(); it != parts.end(); it++){ - conn.SendNow(it->getData(), 8); + conn.SendNow(it->getData(), 9); } conn.SendNow("\000\007trackid\001", 10); conn.SendNow(convertLongLong(trackID), 8); @@ -682,7 +684,7 @@ namespace DTSC { result["keys"] = std::string((char*)keys, keyLen * 16); } if (parts){ - result["parts"] = std::string((char*)parts, partLen * 8); + result["parts"] = std::string((char*)parts, partLen * 9); } result["trackid"] = trackID; result["length"] = length; @@ -726,9 +728,9 @@ namespace DTSC { } result["keys"] = tmp; tmp = ""; - tmp.reserve(parts.size() * 8); + tmp.reserve(parts.size() * 9); for (std::deque::iterator it = parts.begin(); it != parts.end(); it++){ - tmp.append(it->getData(), 8); + tmp.append(it->getData(), 9); } result["parts"] = tmp; result["trackid"] = trackID; @@ -823,6 +825,7 @@ namespace DTSC { continue; } if (!it->second.keys.size() || !(it->second.keys.rbegin()->getBpos())){ + std::cerr << "Not fixed while track " << it->first << " has " << it->second.keys.size() << "keyframes" << std::endl; return false; } } From f09b2588047fda6b092ee616e303c0086bf80cd6 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 2 Dec 2013 11:05:50 +0100 Subject: [PATCH 566/788] Removed duplicate DTSC::Track::getIdentifier function, added DTSC::Stream::addMeta function. --- lib/dtsc.cpp | 10 +++++++++- lib/dtsc.h | 2 +- lib/dtscmeta.cpp | 22 +--------------------- 3 files changed, 11 insertions(+), 23 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 922aef39..8bcd03db 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -121,7 +121,7 @@ bool DTSC::Stream::parsePacket(Socket::Buffer & buffer){ unsigned int i = 0; std::string wholepacket = buffer.remove(len + 8); JSON::Value meta = JSON::fromDTMI((unsigned char*)wholepacket.c_str() + 8, len, i); - metadata = Meta(meta); + addMeta(meta); //recursively calls itself until failure or data packet instead of header return parsePacket(buffer); } @@ -205,6 +205,14 @@ void DTSC::Stream::resetStream(){ keyframes.clear(); } +/// Adds a set of metadata to the steam. +/// This is implemented by simply replacing the current metadata. +/// This effectively resets the stream. +void DTSC::Stream::addMeta(JSON::Value & newMeta){ + metadata = Meta(newMeta); +} + +/// Adds a single DTSC packet to the stream, updating the internal metadata if needed. void DTSC::Stream::addPacket(JSON::Value & newPack){ long long unsigned int now = Util::getMS(); livePos newPos; diff --git a/lib/dtsc.h b/lib/dtsc.h index 632bc062..caa23c95 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -197,7 +197,6 @@ namespace DTSC { std::deque keys; std::deque parts; Key & getKey(int keyNum); - std::string getIdentifier(); void reset(); }; @@ -314,6 +313,7 @@ namespace DTSC { std::map buffers; std::map > keyframes; void addPacket(JSON::Value & newPack); + void addMeta(JSON::Value & newMeta); datatype datapointertype; unsigned int buffercount; unsigned int buffertime; diff --git a/lib/dtscmeta.cpp b/lib/dtscmeta.cpp index 3cb988f5..bcb58d6a 100644 --- a/lib/dtscmeta.cpp +++ b/lib/dtscmeta.cpp @@ -311,31 +311,11 @@ namespace DTSC { std::string readOnlyTrack::getIdentifier(){ std::stringstream result; if (type == ""){ - result << "Metadata_" << trackID; + result << "metadata_" << trackID; return result.str(); } result << type << "_"; result << codec << "_"; - result << (bps / 1024) << "kbit_"; - if (type == "audio"){ - result << channels << "ch_"; - result << rate << "hz"; - }else if (type == "video"){ - result << width << "x" << height << "_"; - result << (double)fpks / 1000 << "fps"; - } - return result.str(); - } - - std::string Track::getIdentifier(){ - std::stringstream result; - if (type == ""){ - result << "Metadata_" << trackID; - return result.str(); - } - result << type << "_"; - result << codec << "_"; - result << (bps / 1024) << "kbit_"; if (type == "audio"){ result << channels << "ch_"; result << rate << "hz"; From 99353f49a075223298ef2dbf0db0658427a9e148 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 3 Dec 2013 09:50:25 +0100 Subject: [PATCH 567/788] Fixed firstms/lastms in DTSC::Track, fixed stream buffer limitations. --- lib/dtsc.cpp | 8 ++++---- lib/dtsc.h | 6 +++--- lib/dtscmeta.cpp | 8 ++++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 8bcd03db..9b1cdf90 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -235,9 +235,6 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ newPos.seekTime++; } buffers[newPos] = newPack; - if (buffercount > 1){ - buffers[newPos].toNetPacked();//make sure package is packed and ready - } datapointertype = INVALID; std::string tmp = ""; if (newPack.isMember("trackid") && newPack["trackid"].asInt() > 0){ @@ -260,6 +257,9 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ } if (buffercount > 1){ metadata.update(newPack); + if (newPack.isMember("keyframe") || metadata.tracks[newPos.trackID].keys.rbegin()->getTime() == newPos.seekTime){ + keyframes[newPos.trackID].insert(newPos); + } metadata.live = true; } @@ -297,7 +297,7 @@ void DTSC::Stream::cutOneBuffer(){ metadata.tracks[trid].keys.pop_front(); // delete fragments of which the beginning can no longer be reached while (metadata.tracks[trid].fragments.size() && metadata.tracks[trid].fragments[0].getNumber() < metadata.tracks[trid].keys[0].getNumber()){ - metadata.tracks[trid].firstms += metadata.tracks[trid].fragments[0].getDuration(); + metadata.tracks[trid].firstms = metadata.tracks[trid].keys[0].getTime(); metadata.tracks[trid].fragments.pop_front(); // increase the missed fragments counter metadata.tracks[trid].missedFrags ++; diff --git a/lib/dtsc.h b/lib/dtsc.h index caa23c95..1bb850c7 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -312,12 +312,12 @@ namespace DTSC { void resetStream(); std::map buffers; std::map > keyframes; - void addPacket(JSON::Value & newPack); - void addMeta(JSON::Value & newMeta); + virtual void addPacket(JSON::Value & newPack); + virtual void addMeta(JSON::Value & newMeta); datatype datapointertype; unsigned int buffercount; unsigned int buffertime; std::map trackMapping; - void deletionCallback(livePos deleting); + virtual void deletionCallback(livePos deleting); }; } diff --git a/lib/dtscmeta.cpp b/lib/dtscmeta.cpp index bcb58d6a..b0441eaa 100644 --- a/lib/dtscmeta.cpp +++ b/lib/dtscmeta.cpp @@ -125,6 +125,8 @@ namespace DTSC { parts = NULL; partLen = 0; missedFrags = 0; + firstms = 0; + lastms = 0; } readOnlyTrack::readOnlyTrack(JSON::Value & trackRef){ @@ -253,9 +255,6 @@ namespace DTSC { }else{ newPart.setDuration(0); } - if (pack["time"].asInt() > firstms){ - firstms = pack["time"].asInt(); - } parts.push_back(newPart); lastms = pack["time"].asInt(); if (pack.isMember("keyframe") || !keys.size() || (type != "video" && pack["time"].asInt() - 5000 > keys[keys.size() - 1].getTime())){ @@ -275,6 +274,7 @@ namespace DTSC { newKey.setBpos(0); } keys.push_back(newKey); + firstms = keys[0].getTime(); if (!fragments.size() || pack["time"].asInt() - 10000 >= getKey(fragments.rbegin()->getNumber()).getTime()){ //new fragment Fragment newFrag; @@ -331,7 +331,7 @@ namespace DTSC { parts.clear(); keys.clear(); bps = 0; - firstms = 999; + firstms = 0; lastms = 0; } From 2148e9ce2030e79bba848f8f820085e892aa6416 Mon Sep 17 00:00:00 2001 From: Oswald Auguste de Bruin Date: Thu, 28 Nov 2013 11:48:57 +0100 Subject: [PATCH 568/788] Fixed OGG and MP4 --- lib/mp4.cpp | 2 +- lib/mp4.h | 33 +++++---- lib/mp4_conv.cpp | 182 ++++++++++++++++++++++++++++++++--------------- lib/ogg.cpp | 28 ++++---- lib/ogg.h | 2 +- 5 files changed, 157 insertions(+), 90 deletions(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 1a61349f..2b7237ca 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -2518,7 +2518,7 @@ namespace MP4 { r << std::string(indent + 1, ' ') << "ConfigDescriptorTypeLength: 0x" << std::hex << (int)getConfigDescriptorTypeLength() << std::dec << std::endl; r << std::string(indent + 1, ' ') << "ESHeaderStartCodes: 0x"; for (unsigned int i = 0; i #include #include "json.h" +#include "dtsc.h" /// Contains all MP4 format related code. namespace MP4 { struct keyPart{ - bool operator < (const keyPart& rhs) const { - if (time < rhs.time){ - return true; - } - if (time == rhs.time){ - if (trackID < rhs.trackID){ + public: + bool operator < (const keyPart& rhs) const { + if (time < rhs.time){ return true; } + if (time == rhs.time){ + if (trackID < rhs.trackID){ + return true; + } + } + return false; } - return false; - } - long long int trackID; - long long int size; - long long int time; - long long int len; - std::string parts; - long long int partsize; + long long int trackID; + long long int size; + long long int time; + long long int len; + std::deque parts; + long long int partsize; }; class DTSC2MP4Converter{ public: - std::string DTSCMeta2MP4Header(JSON::Value metaData); + std::string DTSCMeta2MP4Header(DTSC::Meta & metaData); void parseDTSC(JSON::Value mediaPart); bool sendReady(); std::string sendString(); + std::string purgeBuffer(); std::set keyParts; private: //long long unsigned int curKey;//the key chunk we are currently searching for in keyParts diff --git a/lib/mp4_conv.cpp b/lib/mp4_conv.cpp index 02e3b977..0be96112 100644 --- a/lib/mp4_conv.cpp +++ b/lib/mp4_conv.cpp @@ -6,9 +6,8 @@ namespace MP4{ return (i.time < j.time); }*/ - std::string DTSC2MP4Converter::DTSCMeta2MP4Header(JSON::Value metaData){ + std::string DTSC2MP4Converter::DTSCMeta2MP4Header(DTSC::Meta & metaData){ std::stringstream header; - //ftyp box /// \todo fill ftyp with non hardcoded values from file MP4::FTYP ftypBox; @@ -29,7 +28,15 @@ namespace MP4{ mvhdBox.setModificationTime(0); mvhdBox.setTimeScale(1000); mvhdBox.setRate(0x10000); - mvhdBox.setDuration(metaData["lastms"].asInt() + metaData["firstms"].asInt()); + //calculating longest duration + int fileDuration = 0; + ///\TODO lastms and firstms fixen + for ( std::map::iterator trackIt = metaData.tracks.begin(); trackIt != metaData.tracks.end(); trackIt ++) { + if (trackIt->second.lastms - trackIt->second.firstms > fileDuration){ + fileDuration = trackIt->second.lastms - trackIt->second.firstms; + } + } + mvhdBox.setDuration(fileDuration); mvhdBox.setTrackID(0); mvhdBox.setVolume(256); mvhdBox.setMatrix(0x00010000,0); @@ -44,19 +51,42 @@ namespace MP4{ moovBox.setContent(mvhdBox, 0); //calculate interleaving - //putting all metadata in a huge vector 'keyParts' + //putting all metadata in a huge, auto-sorting vector 'keyParts' keyParts.clear(); - for (JSON::ObjIter trackIt = metaData["tracks"].ObjBegin(); trackIt != metaData["tracks"].ObjEnd(); trackIt++){ - for (JSON::ArrIter keyIt = trackIt->second["keys"].ArrBegin(); keyIt != trackIt->second["keys"].ArrEnd(); keyIt++){ - if ((*keyIt)["size"].asInt() > 0){ - keyPart temp; - temp.trackID = trackIt->second["trackid"].asInt(); - temp.size = (*keyIt)["size"].asInt(); - temp.time = (*keyIt)["time"].asInt(); - temp.len = (*keyIt)["len"].asInt(); - temp.parts = (*keyIt)["parts"].asString(); - temp.partsize = (*keyIt)["partsize"].asInt(); - keyParts.insert(temp); + for ( std::map::iterator trackIt = metaData.tracks.begin(); trackIt != metaData.tracks.end(); trackIt ++) { + if (trackIt->first>0){ + std::cerr << "preparing track: " << trackIt->first << std::endl; + int partItNumber = 0; + for ( std::deque< DTSC::Key>::iterator keyIt = trackIt->second.keys.begin(); keyIt != trackIt->second.keys.end(); keyIt ++) { + //if ((*keyIt)->size > 0){ + keyPart temp; + temp.trackID = trackIt->second.trackID; + /* + temp.size = (*keyIt)["size"].asInt(); + temp.time = (*keyIt)["time"].asInt(); + temp.len = (*keyIt)["len"].asInt(); + temp.parts = (*keyIt)["parts"].asString(); + temp.partsize = (*keyIt)["partsize"].asInt(); + */ + temp.time = keyIt->getTime();//timeplaats van keyframe + std::cerr << "time: " << temp.time << std::endl; + temp.len = keyIt->getLength();//duration van keyframe + //std::cerr << "totalparts, partItNumber, getparts:"<< trackIt->second.parts.size() << ", " << partItNumber << ", " << keyIt->getParts() << std::endl; + temp.parts = std::deque (trackIt->second.parts.begin() + partItNumber,trackIt->second.parts.begin() + partItNumber + keyIt->getParts() );//array met bytegrootte van elke aparte part + //calculate total size of parts + int tempSize = 0; + //std::cerr << "keyframe parts: "; + for (unsigned int di = 0; di < temp.parts.size(); di++){ + tempSize += temp.parts[di].getSize(); + //std::cerr << temp.parts[di].getSize() << " "; + } + //std::cerr << std::endl; + temp.size = tempSize;//bytegrootte van keyframe (alle parts bij elkaar) + temp.partsize = keyIt->getParts();//amount of parts in this keyframe + + keyParts.insert(temp); + //} + partItNumber += keyIt->getParts(); } } } @@ -65,18 +95,22 @@ namespace MP4{ //start arbitrary track addition for header int boxOffset = 1; - for (JSON::ObjIter it = metaData["tracks"].ObjBegin(); it != metaData["tracks"].ObjEnd(); it++){ + for ( std::map::iterator it = metaData.tracks.begin(); it != metaData.tracks.end(); it ++) { + if (it->first > 0){ + std::cerr << "track " << it->second.trackID << std::endl; + //for (JSON::ObjIter it = metaData["tracks"].ObjBegin(); it != metaData["tracks"].ObjEnd(); it++){ int timescale = 0; MP4::TRAK trakBox; MP4::TKHD tkhdBox; tkhdBox.setVersion(0); tkhdBox.setFlags(15); - tkhdBox.setTrackID(it->second["trackid"].asInt()); - tkhdBox.setDuration(it->second["lastms"].asInt() + it->second["firsms"].asInt()); + tkhdBox.setTrackID(it->second.trackID); + ///\TODO duration firstms and lastms fix + tkhdBox.setDuration(it->second.lastms + it->second.firstms); - if (it->second["type"].asString() == "video"){ - tkhdBox.setWidth(it->second["width"].asInt() << 16); - tkhdBox.setHeight(it->second["height"].asInt() << 16); + if (it->second.type == "video"){ + tkhdBox.setWidth(it->second.width << 16); + tkhdBox.setHeight(it->second.height << 16); tkhdBox.setVolume(0); }else{ tkhdBox.setVolume(256); @@ -99,22 +133,24 @@ namespace MP4{ mdhdBox.setModificationTime(0); //Calculating media time based on sampledelta. Probably cheating, but it works... int tmpParts = 0; - for (JSON::ArrIter tmpIt = it->second["keys"].ArrBegin(); tmpIt != it->second["keys"].ArrEnd(); tmpIt++){ - tmpParts += (*tmpIt)["partsize"].asInt(); + for (std::deque< DTSC::Key>::iterator tmpIt = it->second.keys.begin(); tmpIt != it->second.keys.end(); tmpIt ++) { + //for (JSON::ArrIter tmpIt = it->second["keys"].ArrBegin(); tmpIt != it->second["keys"].ArrEnd(); tmpIt++){ + tmpParts += tmpIt->getParts(); } - timescale = ((double)(42 * tmpParts) / (it->second["lastms"].asInt() + it->second["firstms"].asInt())) * 1000; + timescale = ((double)(42 * tmpParts) / (it->second.lastms + it->second.firstms)) * 1000; mdhdBox.setTimeScale(timescale); - mdhdBox.setDuration(((it->second["lastms"].asInt() + it->second["firsms"].asInt()) * ((double)timescale / 1000))); + ///\TODO fix lastms, firstms + mdhdBox.setDuration((it->second.lastms + it->second.firstms) * ((double)timescale / 1000)); mdiaBox.setContent(mdhdBox, 0); - std::string tmpStr = it->second["type"].asString(); + std::string tmpStr = it->second.type; MP4::HDLR hdlrBox;/// \todo fix constructor hdlr in lib if (tmpStr == "video"){ hdlrBox.setHandlerType(0x76696465);//vide }else if (tmpStr == "audio"){ hdlrBox.setHandlerType(0x736F756E);//soun } - hdlrBox.setName(it->first); + hdlrBox.setName(it->second.getIdentifier()); mdiaBox.setContent(hdlrBox, 1); MP4::MINF minfBox; @@ -140,40 +176,40 @@ namespace MP4{ stsdBox.setVersion(0); if (tmpStr == "video"){//boxname = codec MP4::VisualSampleEntry vse; - std::string tmpStr2 = it->second["codec"]; + std::string tmpStr2 = it->second.codec; if (tmpStr2 == "H264"){ vse.setCodec("avc1"); } vse.setDataReferenceIndex(1); - vse.setWidth(it->second["width"].asInt()); - vse.setHeight(it->second["height"].asInt()); + vse.setWidth(it->second.width); + vse.setHeight(it->second.height); MP4::AVCC avccBox; - avccBox.setPayload(it->second["init"].asString()); + avccBox.setPayload(it->second.init); vse.setCLAP(avccBox); stsdBox.setEntry(vse,0); }else if(tmpStr == "audio"){//boxname = codec MP4::AudioSampleEntry ase; - std::string tmpStr2 = it->second["codec"]; + std::string tmpStr2 = it->second.codec; if (tmpStr2 == "AAC"){ ase.setCodec("mp4a"); ase.setDataReferenceIndex(1); } - ase.setSampleRate(it->second["rate"].asInt()); - ase.setChannelCount(it->second["channels"].asInt()); - ase.setSampleSize(it->second["size"].asInt()); + ase.setSampleRate(it->second.rate); + ase.setChannelCount(it->second.channels); + ase.setSampleSize(it->second.size); MP4::ESDS esdsBox; - esdsBox.setESDescriptorTypeLength(32+it->second["init"].asString().size()); + esdsBox.setESDescriptorTypeLength(32+it->second.init.size()); esdsBox.setESID(2); esdsBox.setStreamPriority(0); - esdsBox.setDecoderConfigDescriptorTypeLength(18+it->second["init"].asString().size()); + esdsBox.setDecoderConfigDescriptorTypeLength(18+it->second.init.size()); esdsBox.setByteObjectTypeID(0x40); esdsBox.setStreamType(5); esdsBox.setReservedFlag(1); esdsBox.setBufferSize(1250000); esdsBox.setMaximumBitRate(10000000); - esdsBox.setAverageBitRate(it->second["bps"].asInt() * 8); + esdsBox.setAverageBitRate(it->second.bps * 8); esdsBox.setConfigDescriptorTypeLength(5); - esdsBox.setESHeaderStartCodes(it->second["init"].asString()); + esdsBox.setESHeaderStartCodes(it->second.init); esdsBox.setSLConfigDescriptorTypeTag(0x6); esdsBox.setSLConfigExtendedDescriptorTypeTag(0x808080); esdsBox.setSLDescriptorTypeLength(1); @@ -193,19 +229,21 @@ namespace MP4{ sttsBox.setSTTSEntry(newEntry, 0); stblBox.setContent(sttsBox,1); - if (it->second["type"] == "video"){ + if (it->second.type == "video"){ //STSS Box here MP4::STSS stssBox; stssBox.setVersion(0); int tmpCount = 1; - for (int i = 0; i < it->second["keys"].size(); i++){ - stssBox.setSampleNumber(tmpCount,i); - tmpCount += it->second["keys"][i]["partsize"].asInt(); + int tmpItCount = 0; + for ( std::deque< DTSC::Key>::iterator tmpIt = it->second.keys.begin(); tmpIt != it->second.keys.end(); tmpIt ++) { + stssBox.setSampleNumber(tmpCount,tmpItCount); + tmpCount += tmpIt->getParts(); + tmpItCount ++; } stblBox.setContent(stssBox,2); } - int offset = (it->second["type"] == "video"); + int offset = (it->second.type == "video"); MP4::STSC stscBox; @@ -221,44 +259,51 @@ namespace MP4{ MP4::STSZ stszBox; stszBox.setVersion(0); total = 0; - for (int i = 0; i < it->second["keys"].size(); i++){ - std::deque parsedParts; - JSON::decodeVector(it->second["keys"][i]["parts"].asString(), parsedParts); - for (unsigned int o = 0; o < parsedParts.size(); o++){ - stszBox.setEntrySize(parsedParts[o], total);//in bytes in file - total++; - } + for (std::deque< DTSC::Part>::iterator partIt = it->second.parts.begin(); partIt != it->second.parts.end(); partIt ++) { + //for (int i = 0; i < it->second["keys"].size(); i++){ + //std::deque parsedParts; + //JSON::decodeVector(it->second["keys"][i]["parts"].asString(), parsedParts); + //for (unsigned int o = 0; o < tmpIt->parts.size(); o++){ + stszBox.setEntrySize(partIt->getSize(), total);//in bytes in file + total++; } stblBox.setContent(stszBox,3 + offset); MP4::STCO stcoBox; stcoBox.setVersion(1); total = 0; - uint64_t totalByteOffset = 0; + long long unsigned int totalByteOffset = 0; //Inserting wrong values on purpose here, will be fixed later. //Current values are actual byte offset without header-sized offset + std::cerr << "pre-totalByteOffset: " << totalByteOffset << std::endl; for (std::set::iterator i = keyParts.begin(); i != keyParts.end(); i++){//for all keypart size - if(i->trackID == it->second["trackid"].asInt()){//if keypart is of current trackID - std::deque parsedParts; - JSON::decodeVector(i->parts, parsedParts); - for (unsigned int o = 0; o < parsedParts.size(); o++){//add all parts to STCO + if(i->trackID == it->second.trackID){//if keypart is of current trackID + //std::deque parsedParts; + //JSON::decodeVector(i->parts, parsedParts); + std::deque onowai = i->parts; + for (unsigned int o = 0; o < onowai.size(); o++){//add all parts to STCO + //for (std::deque::iterator partIt = (*i).parts.begin(); partIt != (*i).parts.end(); partIt++){ stcoBox.setChunkOffset(totalByteOffset, total); total++; - totalByteOffset += parsedParts[o]; + totalByteOffset += onowai[o].getSize(); + std::cerr << "small totalByteOffset: " << totalByteOffset << std::endl; } }else{ totalByteOffset += i->size; + std::cerr << "large totalByteOffset: " << totalByteOffset << std::endl; } } //calculating the offset where the STCO box will be in the main MOOV box //needed for probable optimise mdatSize = totalByteOffset; + stblBox.setContent(stcoBox,4 + offset); minfBox.setContent(stblBox,2); mdiaBox.setContent(minfBox, 2); trakBox.setContent(mdiaBox, 1); moovBox.setContent(trakBox, boxOffset); boxOffset++; + } } //end arbitrary //initial offset length ftyp, length moov + 8 @@ -318,10 +363,12 @@ namespace MP4{ //while there are requested packets in the trackBuffer:... while (!trackBuffer[curKey->trackID].empty()){ //output requested packages + //std::deque onowai = curKey->parts; stringBuffer += trackBuffer[curKey->trackID].front()["data"].asString(); + //std::cerr << "bufDataSize, antDataSize" << trackBuffer[curKey->trackID].front()["data"].asString().size() << ", " << onowai[curPart].getSize() << std::endl; trackBuffer[curKey->trackID].pop_front(); curPart++; - if(curPart >= curKey->partsize){ + if(curPart >= curKey->parts.size()){ curPart = 0; curKey++; } @@ -330,8 +377,10 @@ namespace MP4{ if(curKey->trackID == mediaPart["trackid"].asInt()){ //output JSON packet stringBuffer += mediaPart["data"].asStringRef(); + //std::deque onowai = curKey->parts; + //std::cerr << "dataSize, antDataSize" << mediaPart["data"].asStringRef().size() << ", " << onowai[curPart].getSize() << std::endl; curPart++; - if(curPart >= curKey->partsize){ + if(curPart >= curKey->parts.size()){ curPart = 0; curKey++; } @@ -354,5 +403,20 @@ namespace MP4{ stringBuffer = ""; return temp; } + + std::string DTSC2MP4Converter::purgeBuffer(){ + std::string retval = stringBuffer; + stringBuffer = ""; + for (std::map >::iterator it = trackBuffer.begin(); it !=trackBuffer.end(); it++){ + while (!it->second.empty()){ + //output requested packages + if (it->second.front()["data"].asString() != ""){ + retval += it->second.front()["data"].asString(); + } + it->second.pop_front(); + } + } + return retval; + } } diff --git a/lib/ogg.cpp b/lib/ogg.cpp index c6a513f5..664fbb87 100644 --- a/lib/ogg.cpp +++ b/lib/ogg.cpp @@ -505,7 +505,7 @@ namespace OGG{ setCRCChecksum(calcChecksum()); } - void headerPages::readDTSCHeader(JSON::Value meta){ + void headerPages::readDTSCHeader(DTSC::Meta & meta){ //pages.clear(); parsedPages = ""; Page curOggPage; @@ -514,19 +514,19 @@ namespace OGG{ DTSCID2OGGSerial.clear(); DTSCID2seqNum.clear(); //Creating ID headers for theora and vorbis - for ( JSON::ObjIter it = meta["tracks"].ObjBegin(); it != meta["tracks"].ObjEnd(); it ++) { + for ( std::map::iterator it = meta.tracks.begin(); it != meta.tracks.end(); it ++) { curOggPage.clear(); curOggPage.setVersion(); curOggPage.setHeaderType(2);//headertype 2 = Begin of Stream curOggPage.setGranulePosition(0); - DTSCID2OGGSerial[it->second["trackid"].asInt()] = rand() % 0xFFFFFFFE +1; //initialising on a random not 0 number - curOggPage.setBitstreamSerialNumber(DTSCID2OGGSerial[it->second["trackid"].asInt()]); - DTSCID2seqNum[it->second["trackid"].asInt()] = 0; - curOggPage.setPageSequenceNumber(DTSCID2seqNum[it->second["trackid"].asInt()]++); + DTSCID2OGGSerial[it->second.trackID] = rand() % 0xFFFFFFFE +1; //initialising on a random not 0 number + curOggPage.setBitstreamSerialNumber(DTSCID2OGGSerial[it->second.trackID]); + DTSCID2seqNum[it->second.trackID] = 0; + curOggPage.setPageSequenceNumber(DTSCID2seqNum[it->second.trackID]++); curSegTable.clear(); - curSegTable.push_back(it->second["IDHeader"].asString().size()); + curSegTable.push_back(it->second.idHeader.size()); curOggPage.setSegmentTable(curSegTable); - curOggPage.setPayload((char*)it->second["IDHeader"].asString().c_str(), it->second["IDHeader"].asString().size()); + curOggPage.setPayload((char*)it->second.idHeader.c_str(), it->second.idHeader.size()); curOggPage.setCRCChecksum(curOggPage.calcChecksum()); //std::cout << std::string(curOggPage.getPage(), curOggPage.getPageSize()); //pages.push_back(curOggPage); @@ -535,18 +535,18 @@ namespace OGG{ //Creating remaining headers for theora and vorbis //for tracks in header //create standard page with comment (empty) en setup header(init) - for ( JSON::ObjIter it = meta["tracks"].ObjBegin(); it != meta["tracks"].ObjEnd(); it ++) { + for ( std::map::iterator it = meta.tracks.begin(); it != meta.tracks.end(); it ++) { curOggPage.clear(); curOggPage.setVersion(); curOggPage.setHeaderType(0);//headertype 0 = normal curOggPage.setGranulePosition(0); - curOggPage.setBitstreamSerialNumber(DTSCID2OGGSerial[it->second["trackid"].asInt()]); - curOggPage.setPageSequenceNumber(DTSCID2seqNum[it->second["trackid"].asInt()]++); + curOggPage.setBitstreamSerialNumber(DTSCID2OGGSerial[it->second.trackID]); + curOggPage.setPageSequenceNumber(DTSCID2seqNum[it->second.trackID]++); curSegTable.clear(); - curSegTable.push_back(it->second["CommentHeader"].asString().size()); - curSegTable.push_back(it->second["init"].asString().size()); + curSegTable.push_back(it->second.commentHeader.size()); + curSegTable.push_back(it->second.init.size()); curOggPage.setSegmentTable(curSegTable); - std::string fullHeader = it->second["CommentHeader"].asString() + it->second["init"].asString(); + std::string fullHeader = it->second.commentHeader + it->second.init; curOggPage.setPayload((char*)fullHeader.c_str(),fullHeader.size()); curOggPage.setCRCChecksum(curOggPage.calcChecksum()); //std::cout << std::string(curOggPage.getPage(), curOggPage.getPageSize()); diff --git a/lib/ogg.h b/lib/ogg.h index 6d31edb1..4ac01d48 100644 --- a/lib/ogg.h +++ b/lib/ogg.h @@ -58,7 +58,7 @@ namespace OGG{ class headerPages{ public: - void readDTSCHeader(JSON::Value meta); + void readDTSCHeader(DTSC::Meta & meta); std::map DTSCID2OGGSerial; std::map DTSCID2seqNum; std::string parsedPages; From 559b4cacce453ecdec7eeb01d1d6e7a130c6274d Mon Sep 17 00:00:00 2001 From: Oswald Auguste de Bruin Date: Tue, 3 Dec 2013 14:02:13 +0100 Subject: [PATCH 569/788] Sanitised mp4 converter code --- lib/mp4_conv.cpp | 435 ++++++++++++++++++++++------------------------- 1 file changed, 199 insertions(+), 236 deletions(-) diff --git a/lib/mp4_conv.cpp b/lib/mp4_conv.cpp index 0be96112..20b725d3 100644 --- a/lib/mp4_conv.cpp +++ b/lib/mp4_conv.cpp @@ -2,10 +2,6 @@ #include namespace MP4{ - /*bool keyPartSort(keyPart i, keyPart j){ - return (i.time < j.time); - }*/ - std::string DTSC2MP4Converter::DTSCMeta2MP4Header(DTSC::Meta & metaData){ std::stringstream header; //ftyp box @@ -52,257 +48,228 @@ namespace MP4{ //calculate interleaving //putting all metadata in a huge, auto-sorting vector 'keyParts' + //sort by time on keyframes for interleaving keyParts.clear(); for ( std::map::iterator trackIt = metaData.tracks.begin(); trackIt != metaData.tracks.end(); trackIt ++) { if (trackIt->first>0){ - std::cerr << "preparing track: " << trackIt->first << std::endl; int partItNumber = 0; for ( std::deque< DTSC::Key>::iterator keyIt = trackIt->second.keys.begin(); keyIt != trackIt->second.keys.end(); keyIt ++) { - //if ((*keyIt)->size > 0){ - keyPart temp; - temp.trackID = trackIt->second.trackID; - /* - temp.size = (*keyIt)["size"].asInt(); - temp.time = (*keyIt)["time"].asInt(); - temp.len = (*keyIt)["len"].asInt(); - temp.parts = (*keyIt)["parts"].asString(); - temp.partsize = (*keyIt)["partsize"].asInt(); - */ - temp.time = keyIt->getTime();//timeplaats van keyframe - std::cerr << "time: " << temp.time << std::endl; - temp.len = keyIt->getLength();//duration van keyframe - //std::cerr << "totalparts, partItNumber, getparts:"<< trackIt->second.parts.size() << ", " << partItNumber << ", " << keyIt->getParts() << std::endl; - temp.parts = std::deque (trackIt->second.parts.begin() + partItNumber,trackIt->second.parts.begin() + partItNumber + keyIt->getParts() );//array met bytegrootte van elke aparte part - //calculate total size of parts - int tempSize = 0; - //std::cerr << "keyframe parts: "; - for (unsigned int di = 0; di < temp.parts.size(); di++){ - tempSize += temp.parts[di].getSize(); - //std::cerr << temp.parts[di].getSize() << " "; - } - //std::cerr << std::endl; - temp.size = tempSize;//bytegrootte van keyframe (alle parts bij elkaar) - temp.partsize = keyIt->getParts();//amount of parts in this keyframe - - keyParts.insert(temp); - //} + keyPart temp; + temp.trackID = trackIt->second.trackID; + temp.time = keyIt->getTime();//timeplaats van keyframe + temp.len = keyIt->getLength();//duration van keyframe + temp.parts = std::deque (trackIt->second.parts.begin() + partItNumber,trackIt->second.parts.begin() + partItNumber + keyIt->getParts() );//array met bytegrootte van elke aparte part + //calculate total size of parts + int tempSize = 0; + for (unsigned int di = 0; di < temp.parts.size(); di++){ + tempSize += temp.parts[di].getSize(); + } + temp.size = tempSize;//bytegrootte van keyframe (alle parts bij elkaar) + temp.partsize = keyIt->getParts();//amount of parts in this keyframe + + keyParts.insert(temp); partItNumber += keyIt->getParts(); } } } - //sort by time on keyframes for interleaving - //std::sort(keyParts.begin(), keyParts.end(), keyPartSort); //start arbitrary track addition for header int boxOffset = 1; for ( std::map::iterator it = metaData.tracks.begin(); it != metaData.tracks.end(); it ++) { if (it->first > 0){ - std::cerr << "track " << it->second.trackID << std::endl; - //for (JSON::ObjIter it = metaData["tracks"].ObjBegin(); it != metaData["tracks"].ObjEnd(); it++){ - int timescale = 0; - MP4::TRAK trakBox; - MP4::TKHD tkhdBox; - tkhdBox.setVersion(0); - tkhdBox.setFlags(15); - tkhdBox.setTrackID(it->second.trackID); - ///\TODO duration firstms and lastms fix - tkhdBox.setDuration(it->second.lastms + it->second.firstms); - - if (it->second.type == "video"){ - tkhdBox.setWidth(it->second.width << 16); - tkhdBox.setHeight(it->second.height << 16); - tkhdBox.setVolume(0); - }else{ - tkhdBox.setVolume(256); - tkhdBox.setAlternateGroup(1); - } - tkhdBox.setMatrix(0x00010000,0); - tkhdBox.setMatrix(0,1); - tkhdBox.setMatrix(0,2); - tkhdBox.setMatrix(0,3); - tkhdBox.setMatrix(0x00010000,4); - tkhdBox.setMatrix(0,5); - tkhdBox.setMatrix(0,6); - tkhdBox.setMatrix(0,7); - tkhdBox.setMatrix(0x40000000,8); - trakBox.setContent(tkhdBox, 0); - - MP4::MDIA mdiaBox; - MP4::MDHD mdhdBox(0);/// \todo fix constructor mdhd in lib - mdhdBox.setCreationTime(0); - mdhdBox.setModificationTime(0); - //Calculating media time based on sampledelta. Probably cheating, but it works... - int tmpParts = 0; - for (std::deque< DTSC::Key>::iterator tmpIt = it->second.keys.begin(); tmpIt != it->second.keys.end(); tmpIt ++) { - //for (JSON::ArrIter tmpIt = it->second["keys"].ArrBegin(); tmpIt != it->second["keys"].ArrEnd(); tmpIt++){ - tmpParts += tmpIt->getParts(); - } - timescale = ((double)(42 * tmpParts) / (it->second.lastms + it->second.firstms)) * 1000; - mdhdBox.setTimeScale(timescale); - ///\TODO fix lastms, firstms - mdhdBox.setDuration((it->second.lastms + it->second.firstms) * ((double)timescale / 1000)); - mdiaBox.setContent(mdhdBox, 0); + int timescale = 0; + MP4::TRAK trakBox; + MP4::TKHD tkhdBox; + tkhdBox.setVersion(0); + tkhdBox.setFlags(15); + tkhdBox.setTrackID(it->second.trackID); + ///\TODO duration firstms and lastms fix + tkhdBox.setDuration(it->second.lastms + it->second.firstms); - std::string tmpStr = it->second.type; - MP4::HDLR hdlrBox;/// \todo fix constructor hdlr in lib - if (tmpStr == "video"){ - hdlrBox.setHandlerType(0x76696465);//vide - }else if (tmpStr == "audio"){ - hdlrBox.setHandlerType(0x736F756E);//soun + if (it->second.type == "video"){ + tkhdBox.setWidth(it->second.width << 16); + tkhdBox.setHeight(it->second.height << 16); + tkhdBox.setVolume(0); + }else{ + tkhdBox.setVolume(256); + tkhdBox.setAlternateGroup(1); } - hdlrBox.setName(it->second.getIdentifier()); - mdiaBox.setContent(hdlrBox, 1); + tkhdBox.setMatrix(0x00010000,0); + tkhdBox.setMatrix(0,1); + tkhdBox.setMatrix(0,2); + tkhdBox.setMatrix(0,3); + tkhdBox.setMatrix(0x00010000,4); + tkhdBox.setMatrix(0,5); + tkhdBox.setMatrix(0,6); + tkhdBox.setMatrix(0,7); + tkhdBox.setMatrix(0x40000000,8); + trakBox.setContent(tkhdBox, 0); - MP4::MINF minfBox; - if (tmpStr == "video"){ - MP4::VMHD vmhdBox; - vmhdBox.setFlags(1); - minfBox.setContent(vmhdBox,0); - }else if (tmpStr == "audio"){ - MP4::SMHD smhdBox; - minfBox.setContent(smhdBox,0); + MP4::MDIA mdiaBox; + MP4::MDHD mdhdBox(0);/// \todo fix constructor mdhd in lib + mdhdBox.setCreationTime(0); + mdhdBox.setModificationTime(0); + //Calculating media time based on sampledelta. Probably cheating, but it works... + int tmpParts = 0; + for (std::deque< DTSC::Key>::iterator tmpIt = it->second.keys.begin(); tmpIt != it->second.keys.end(); tmpIt ++) { + tmpParts += tmpIt->getParts(); } - MP4::DINF dinfBox; - MP4::DREF drefBox;/// \todo fix constructor dref in lib - drefBox.setVersion(0); - MP4::URL urlBox; - urlBox.setFlags(1); - drefBox.setDataEntry(urlBox,0); - dinfBox.setContent(drefBox,0); - minfBox.setContent(dinfBox,1); + timescale = ((double)(42 * tmpParts) / (it->second.lastms + it->second.firstms)) * 1000; + mdhdBox.setTimeScale(timescale); + ///\TODO fix lastms, firstms + mdhdBox.setDuration((it->second.lastms + it->second.firstms) * ((double)timescale / 1000)); + mdiaBox.setContent(mdhdBox, 0); - MP4::STBL stblBox; - MP4::STSD stsdBox; - stsdBox.setVersion(0); - if (tmpStr == "video"){//boxname = codec - MP4::VisualSampleEntry vse; - std::string tmpStr2 = it->second.codec; - if (tmpStr2 == "H264"){ - vse.setCodec("avc1"); - } - vse.setDataReferenceIndex(1); - vse.setWidth(it->second.width); - vse.setHeight(it->second.height); - MP4::AVCC avccBox; - avccBox.setPayload(it->second.init); - vse.setCLAP(avccBox); - stsdBox.setEntry(vse,0); - }else if(tmpStr == "audio"){//boxname = codec - MP4::AudioSampleEntry ase; - std::string tmpStr2 = it->second.codec; - if (tmpStr2 == "AAC"){ - ase.setCodec("mp4a"); - ase.setDataReferenceIndex(1); - } - ase.setSampleRate(it->second.rate); - ase.setChannelCount(it->second.channels); - ase.setSampleSize(it->second.size); - MP4::ESDS esdsBox; - esdsBox.setESDescriptorTypeLength(32+it->second.init.size()); - esdsBox.setESID(2); - esdsBox.setStreamPriority(0); - esdsBox.setDecoderConfigDescriptorTypeLength(18+it->second.init.size()); - esdsBox.setByteObjectTypeID(0x40); - esdsBox.setStreamType(5); - esdsBox.setReservedFlag(1); - esdsBox.setBufferSize(1250000); - esdsBox.setMaximumBitRate(10000000); - esdsBox.setAverageBitRate(it->second.bps * 8); - esdsBox.setConfigDescriptorTypeLength(5); - esdsBox.setESHeaderStartCodes(it->second.init); - esdsBox.setSLConfigDescriptorTypeTag(0x6); - esdsBox.setSLConfigExtendedDescriptorTypeTag(0x808080); - esdsBox.setSLDescriptorTypeLength(1); - esdsBox.setSLValue(2); - ase.setCodecBox(esdsBox); - stsdBox.setEntry(ase,0); - } - stblBox.setContent(stsdBox,0); - - /// \todo update following stts lines - MP4::STTS sttsBox;//current version probably causes problems - sttsBox.setVersion(0); - MP4::STTSEntry newEntry; - newEntry.sampleCount = tmpParts; - //42, Used as magic number for timescale calculation - newEntry.sampleDelta = 42; - sttsBox.setSTTSEntry(newEntry, 0); - stblBox.setContent(sttsBox,1); - - if (it->second.type == "video"){ - //STSS Box here - MP4::STSS stssBox; - stssBox.setVersion(0); - int tmpCount = 1; - int tmpItCount = 0; - for ( std::deque< DTSC::Key>::iterator tmpIt = it->second.keys.begin(); tmpIt != it->second.keys.end(); tmpIt ++) { - stssBox.setSampleNumber(tmpCount,tmpItCount); - tmpCount += tmpIt->getParts(); - tmpItCount ++; - } - stblBox.setContent(stssBox,2); + std::string tmpStr = it->second.type; + MP4::HDLR hdlrBox;/// \todo fix constructor hdlr in lib + if (tmpStr == "video"){ + hdlrBox.setHandlerType(0x76696465);//vide + }else if (tmpStr == "audio"){ + hdlrBox.setHandlerType(0x736F756E);//soun + } + hdlrBox.setName(it->second.getIdentifier()); + mdiaBox.setContent(hdlrBox, 1); + + MP4::MINF minfBox; + if (tmpStr == "video"){ + MP4::VMHD vmhdBox; + vmhdBox.setFlags(1); + minfBox.setContent(vmhdBox,0); + }else if (tmpStr == "audio"){ + MP4::SMHD smhdBox; + minfBox.setContent(smhdBox,0); } - - int offset = (it->second.type == "video"); - + MP4::DINF dinfBox; + MP4::DREF drefBox;/// \todo fix constructor dref in lib + drefBox.setVersion(0); + MP4::URL urlBox; + urlBox.setFlags(1); + drefBox.setDataEntry(urlBox,0); + dinfBox.setContent(drefBox,0); + minfBox.setContent(dinfBox,1); - MP4::STSC stscBox; - stscBox.setVersion(0); - uint32_t total = 0; - MP4::STSCEntry stscEntry; - stscEntry.firstChunk = 1; - stscEntry.samplesPerChunk = 1; - stscEntry.sampleDescriptionIndex = 1; - stscBox.setSTSCEntry(stscEntry, 0); - stblBox.setContent(stscBox,2 + offset); - - MP4::STSZ stszBox; - stszBox.setVersion(0); - total = 0; - for (std::deque< DTSC::Part>::iterator partIt = it->second.parts.begin(); partIt != it->second.parts.end(); partIt ++) { - //for (int i = 0; i < it->second["keys"].size(); i++){ - //std::deque parsedParts; - //JSON::decodeVector(it->second["keys"][i]["parts"].asString(), parsedParts); - //for (unsigned int o = 0; o < tmpIt->parts.size(); o++){ - stszBox.setEntrySize(partIt->getSize(), total);//in bytes in file - total++; - } - stblBox.setContent(stszBox,3 + offset); + MP4::STBL stblBox; + MP4::STSD stsdBox; + stsdBox.setVersion(0); + if (tmpStr == "video"){//boxname = codec + MP4::VisualSampleEntry vse; + std::string tmpStr2 = it->second.codec; + if (tmpStr2 == "H264"){ + vse.setCodec("avc1"); + } + vse.setDataReferenceIndex(1); + vse.setWidth(it->second.width); + vse.setHeight(it->second.height); + MP4::AVCC avccBox; + avccBox.setPayload(it->second.init); + vse.setCLAP(avccBox); + stsdBox.setEntry(vse,0); + }else if(tmpStr == "audio"){//boxname = codec + MP4::AudioSampleEntry ase; + std::string tmpStr2 = it->second.codec; + if (tmpStr2 == "AAC"){ + ase.setCodec("mp4a"); + ase.setDataReferenceIndex(1); + } + ase.setSampleRate(it->second.rate); + ase.setChannelCount(it->second.channels); + ase.setSampleSize(it->second.size); + MP4::ESDS esdsBox; + esdsBox.setESDescriptorTypeLength(32+it->second.init.size()); + esdsBox.setESID(2); + esdsBox.setStreamPriority(0); + esdsBox.setDecoderConfigDescriptorTypeLength(18+it->second.init.size()); + esdsBox.setByteObjectTypeID(0x40); + esdsBox.setStreamType(5); + esdsBox.setReservedFlag(1); + esdsBox.setBufferSize(1250000); + esdsBox.setMaximumBitRate(10000000); + esdsBox.setAverageBitRate(it->second.bps * 8); + esdsBox.setConfigDescriptorTypeLength(5); + esdsBox.setESHeaderStartCodes(it->second.init); + esdsBox.setSLConfigDescriptorTypeTag(0x6); + esdsBox.setSLConfigExtendedDescriptorTypeTag(0x808080); + esdsBox.setSLDescriptorTypeLength(1); + esdsBox.setSLValue(2); + ase.setCodecBox(esdsBox); + stsdBox.setEntry(ase,0); + } + stblBox.setContent(stsdBox,0); - MP4::STCO stcoBox; - stcoBox.setVersion(1); - total = 0; - long long unsigned int totalByteOffset = 0; - //Inserting wrong values on purpose here, will be fixed later. - //Current values are actual byte offset without header-sized offset - std::cerr << "pre-totalByteOffset: " << totalByteOffset << std::endl; - for (std::set::iterator i = keyParts.begin(); i != keyParts.end(); i++){//for all keypart size - if(i->trackID == it->second.trackID){//if keypart is of current trackID - //std::deque parsedParts; - //JSON::decodeVector(i->parts, parsedParts); - std::deque onowai = i->parts; - for (unsigned int o = 0; o < onowai.size(); o++){//add all parts to STCO - //for (std::deque::iterator partIt = (*i).parts.begin(); partIt != (*i).parts.end(); partIt++){ - stcoBox.setChunkOffset(totalByteOffset, total); - total++; - totalByteOffset += onowai[o].getSize(); - std::cerr << "small totalByteOffset: " << totalByteOffset << std::endl; - } - }else{ - totalByteOffset += i->size; - std::cerr << "large totalByteOffset: " << totalByteOffset << std::endl; + /// \TODO update following stts lines + MP4::STTS sttsBox;//current version probably causes problems + sttsBox.setVersion(0); + MP4::STTSEntry newEntry; + newEntry.sampleCount = tmpParts; + //42, Used as magic number for timescale calculation + newEntry.sampleDelta = 42; + sttsBox.setSTTSEntry(newEntry, 0); + stblBox.setContent(sttsBox,1); + + if (it->second.type == "video"){ + //STSS Box here + MP4::STSS stssBox; + stssBox.setVersion(0); + int tmpCount = 1; + int tmpItCount = 0; + for ( std::deque< DTSC::Key>::iterator tmpIt = it->second.keys.begin(); tmpIt != it->second.keys.end(); tmpIt ++) { + stssBox.setSampleNumber(tmpCount,tmpItCount); + tmpCount += tmpIt->getParts(); + tmpItCount ++; + } + stblBox.setContent(stssBox,2); } - } - //calculating the offset where the STCO box will be in the main MOOV box - //needed for probable optimise - mdatSize = totalByteOffset; - - stblBox.setContent(stcoBox,4 + offset); - minfBox.setContent(stblBox,2); - mdiaBox.setContent(minfBox, 2); - trakBox.setContent(mdiaBox, 1); - moovBox.setContent(trakBox, boxOffset); - boxOffset++; + + int offset = (it->second.type == "video"); + + + MP4::STSC stscBox; + stscBox.setVersion(0); + uint32_t total = 0; + MP4::STSCEntry stscEntry; + stscEntry.firstChunk = 1; + stscEntry.samplesPerChunk = 1; + stscEntry.sampleDescriptionIndex = 1; + stscBox.setSTSCEntry(stscEntry, 0); + stblBox.setContent(stscBox,2 + offset); + + MP4::STSZ stszBox; + stszBox.setVersion(0); + total = 0; + for (std::deque< DTSC::Part>::iterator partIt = it->second.parts.begin(); partIt != it->second.parts.end(); partIt ++) { + stszBox.setEntrySize(partIt->getSize(), total);//in bytes in file + total++; + } + stblBox.setContent(stszBox,3 + offset); + + MP4::STCO stcoBox; + stcoBox.setVersion(1); + total = 0; + long long unsigned int totalByteOffset = 0; + //Inserting wrong values on purpose here, will be fixed later. + //Current values are actual byte offset without header-sized offset + for (std::set::iterator i = keyParts.begin(); i != keyParts.end(); i++){//for all keypart size + if(i->trackID == it->second.trackID){//if keypart is of current trackID + std::deque tempArr = i->parts; + for (unsigned int o = 0; o < tempArr.size(); o++){//add all parts to STCO + stcoBox.setChunkOffset(totalByteOffset, total); + total++; + totalByteOffset += tempArr[o].getSize(); + } + }else{ + totalByteOffset += i->size; + } + } + //calculating the offset where the STCO box will be in the main MOOV box + //needed for probable optimise + mdatSize = totalByteOffset; + + stblBox.setContent(stcoBox,4 + offset); + minfBox.setContent(stblBox,2); + mdiaBox.setContent(minfBox, 2); + trakBox.setContent(mdiaBox, 1); + moovBox.setContent(trakBox, boxOffset); + boxOffset++; } } //end arbitrary @@ -363,9 +330,7 @@ namespace MP4{ //while there are requested packets in the trackBuffer:... while (!trackBuffer[curKey->trackID].empty()){ //output requested packages - //std::deque onowai = curKey->parts; stringBuffer += trackBuffer[curKey->trackID].front()["data"].asString(); - //std::cerr << "bufDataSize, antDataSize" << trackBuffer[curKey->trackID].front()["data"].asString().size() << ", " << onowai[curPart].getSize() << std::endl; trackBuffer[curKey->trackID].pop_front(); curPart++; if(curPart >= curKey->parts.size()){ @@ -377,8 +342,6 @@ namespace MP4{ if(curKey->trackID == mediaPart["trackid"].asInt()){ //output JSON packet stringBuffer += mediaPart["data"].asStringRef(); - //std::deque onowai = curKey->parts; - //std::cerr << "dataSize, antDataSize" << mediaPart["data"].asStringRef().size() << ", " << onowai[curPart].getSize() << std::endl; curPart++; if(curPart >= curKey->parts.size()){ curPart = 0; From d52e05fbd2be792f229c02ddfde91f43db91c6d1 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 3 Dec 2013 14:46:33 +0100 Subject: [PATCH 570/788] Some random fixes to metadata. Yay random. --- lib/dtsc.cpp | 9 +++------ lib/dtscmeta.cpp | 22 ++++++++++++++-------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 9b1cdf90..9e1a6dc1 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -270,9 +270,7 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ buffercount = buffers.size(); if (buffercount < 2){buffercount = 2;} } - if (metadata.bufferWindow < timeBuffered){ - metadata.bufferWindow = timeBuffered; - } + metadata.bufferWindow = timeBuffered; } while (buffers.size() > buffercount){ @@ -290,17 +288,16 @@ void DTSC::Stream::cutOneBuffer(){ std::cerr << "Warning - track " << trid << " doesn't have enough keyframes to be reliably served." << std::endl; } keyframes[trid].erase(buffers.begin()->first); - int keySize = metadata.tracks[trid].keys.size(); for (int i = 0; i < metadata.tracks[trid].keys[0].getParts(); i++){ metadata.tracks[trid].parts.pop_front(); } metadata.tracks[trid].keys.pop_front(); + metadata.tracks[trid].firstms = metadata.tracks[trid].keys[0].getTime(); // delete fragments of which the beginning can no longer be reached while (metadata.tracks[trid].fragments.size() && metadata.tracks[trid].fragments[0].getNumber() < metadata.tracks[trid].keys[0].getNumber()){ - metadata.tracks[trid].firstms = metadata.tracks[trid].keys[0].getTime(); metadata.tracks[trid].fragments.pop_front(); // increase the missed fragments counter - metadata.tracks[trid].missedFrags ++; + metadata.tracks[trid].missedFrags++; } } buffers.erase(buffers.begin()); diff --git a/lib/dtscmeta.cpp b/lib/dtscmeta.cpp index b0441eaa..b4e25ad4 100644 --- a/lib/dtscmeta.cpp +++ b/lib/dtscmeta.cpp @@ -261,10 +261,10 @@ namespace DTSC { Key newKey; newKey.setTime(pack["time"].asInt()); newKey.setParts(0); - newKey.setLength(pack["time"].asInt()); + newKey.setLength(0); if (keys.size()){ newKey.setNumber(keys[keys.size() - 1].getNumber() + 1); - keys[keys.size() - 1].setLength(pack["time"].asInt() - keys[keys.size() - 1].getLength()); + keys[keys.size() - 1].setLength(pack["time"].asInt() - keys[keys.size() - 1].getTime()); }else{ newKey.setNumber(1); } @@ -598,7 +598,7 @@ namespace DTSC { } void readOnlyMeta::send(Socket::Connection & conn){ - int dataLen = 16 + (vod ? 14 : 0) + (live ? 15 : 0) + (merged ? 17 : 0) + 21; + int dataLen = 16 + (vod ? 14 : 0) + (live ? 15 : 0) + (merged ? 17 : 0) + (bufferWindow ? 24 : 0) + 21; for (std::map::iterator it = tracks.begin(); it != tracks.end(); it++){ dataLen += it->second.getSendLen(); } @@ -621,13 +621,17 @@ namespace DTSC { conn.SendNow("\000\006merged\001", 9); conn.SendNow(convertLongLong(1), 8); } + if (bufferWindow){ + conn.SendNow("\000\015buffer_window\001", 16); + conn.SendNow(convertLongLong(bufferWindow), 8); + } conn.SendNow("\000\012moreheader\001", 13); conn.SendNow(convertLongLong(moreheader), 8); conn.SendNow("\000\000\356", 3);//End global object } void Meta::send(Socket::Connection & conn){ - int dataLen = 16 + (vod ? 14 : 0) + (live ? 15 : 0) + (merged ? 17 : 0) + 21; + int dataLen = 16 + (vod ? 14 : 0) + (live ? 15 : 0) + (merged ? 17 : 0) + (bufferWindow ? 24 : 0) + 21; for (std::map::iterator it = tracks.begin(); it != tracks.end(); it++){ dataLen += it->second.getSendLen(); } @@ -650,6 +654,10 @@ namespace DTSC { conn.SendNow("\000\006merged\001", 9); conn.SendNow(convertLongLong(1), 8); } + if (bufferWindow){ + conn.SendNow("\000\015buffer_window\001", 16); + conn.SendNow(convertLongLong(bufferWindow), 8); + } conn.SendNow("\000\012moreheader\001", 13); conn.SendNow(convertLongLong(moreheader), 8); conn.SendNow("\000\000\356", 3);//End global object @@ -743,7 +751,6 @@ namespace DTSC { JSON::Value Meta::toJSON(){ JSON::Value result; for (std::map::iterator it = tracks.begin(); it != tracks.end(); it++){ - if (!it->second.trackID){continue;} result["tracks"][it->second.getIdentifier()] = it->second.toJSON(); } if (vod){ @@ -756,7 +763,7 @@ namespace DTSC { result["merged"] = 1ll; } if (bufferWindow){ - result["buffer_window"]; + result["buffer_window"] = bufferWindow; } result["moreheader"] = moreheader; return result; @@ -765,7 +772,6 @@ namespace DTSC { JSON::Value readOnlyMeta::toJSON(){ JSON::Value result; for (std::map::iterator it = tracks.begin(); it != tracks.end(); it++){ - if (!it->second.trackID){continue;} result["tracks"][it->second.getIdentifier()] = it->second.toJSON(); } if (vod){ @@ -779,7 +785,7 @@ namespace DTSC { } result["moreheader"] = moreheader; if (bufferWindow){ - result["buffer_window"]; + result["buffer_window"] = bufferWindow; } return result; } From 6adca64680fb9cc2f732d0ecaa40845cfb22fe25 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 3 Dec 2013 16:25:10 +0100 Subject: [PATCH 571/788] More fixes to metadata. --- lib/dtsc.h | 2 -- lib/dtscmeta.cpp | 21 +-------------------- lib/flv_tag.cpp | 2 +- 3 files changed, 2 insertions(+), 23 deletions(-) diff --git a/lib/dtsc.h b/lib/dtsc.h index 1bb850c7..ceb8eebd 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -162,7 +162,6 @@ namespace DTSC { long long unsigned int partLen; Part* parts; int trackID; - int length; int firstms; int lastms; int bps; @@ -209,7 +208,6 @@ namespace DTSC { bool live; bool merged; long long int moreheader; - long long int length; long long int bufferWindow; void send(Socket::Connection & conn); JSON::Value toJSON(); diff --git a/lib/dtscmeta.cpp b/lib/dtscmeta.cpp index b4e25ad4..ea524f10 100644 --- a/lib/dtscmeta.cpp +++ b/lib/dtscmeta.cpp @@ -152,7 +152,6 @@ namespace DTSC { partLen = 0; } trackID = trackRef["trackid"].asInt(); - length = trackRef["length"].asInt(); firstms = trackRef["firstms"].asInt(); lastms = trackRef["lastms"].asInt(); bps = trackRef["bps"].asInt(); @@ -180,7 +179,6 @@ namespace DTSC { Track::Track(const readOnlyTrack & rhs){ trackID = rhs.trackID; - length = rhs.length; firstms = rhs.firstms; lastms = rhs.lastms; bps = rhs.bps; @@ -221,7 +219,6 @@ namespace DTSC { parts = std::deque(tmp,tmp + (trackRef["parts"].asString().size() / 9)); } trackID = trackRef["trackid"].asInt(); - length = trackRef["length"].asInt(); firstms = trackRef["firstms"].asInt(); lastms = trackRef["lastms"].asInt(); bps = trackRef["bps"].asInt(); @@ -339,7 +336,6 @@ namespace DTSC { vod = false; live = false; moreheader = 0; - length = 0; merged = false; bufferWindow = 0; } @@ -347,7 +343,6 @@ namespace DTSC { readOnlyMeta::readOnlyMeta(JSON::Value & meta){ vod = meta.isMember("vod") && meta["vod"]; live = meta.isMember("live") && meta["live"]; - length = 0; merged = meta.isMember("merged") && meta["merged"]; bufferWindow = 0; if (meta.isMember("buffer_window")){ @@ -356,9 +351,6 @@ namespace DTSC { for (JSON::ObjIter it = meta["tracks"].ObjBegin(); it != meta["tracks"].ObjEnd(); it++){ if (it->second.isMember("trackid") && it->second["trackid"]){ tracks[it->second["trackid"].asInt()] = readOnlyTrack(it->second); - if (length < (it->second["lastms"].asInt() - it->second["firstms"].asInt())){ - length = (it->second["lastms"].asInt() - it->second["firstms"].asInt()); - } } } moreheader = meta["moreheader"].asInt(); @@ -368,7 +360,6 @@ namespace DTSC { vod = false; live = false; moreheader = 0; - length = 0; merged = false; bufferWindow = 0; } @@ -377,7 +368,6 @@ namespace DTSC { vod = rhs.vod; live = rhs.live; merged = rhs.merged; - length = rhs.length; bufferWindow = rhs.bufferWindow; for (std::map::const_iterator it = rhs.tracks.begin(); it != rhs.tracks.end(); it++){ tracks[it->first] = it->second; @@ -396,9 +386,6 @@ namespace DTSC { for (JSON::ObjIter it = meta["tracks"].ObjBegin(); it != meta["tracks"].ObjEnd(); it++){ if(it->second["trackid"].asInt()){ tracks[it->second["trackid"].asInt()] = Track(it->second); - if (length < (it->second["lastms"].asInt() - it->second["firstms"].asInt())){ - length = (it->second["lastms"].asInt() - it->second["firstms"].asInt()); - } } } moreheader = meta["moreheader"].asInt(); @@ -459,7 +446,7 @@ namespace DTSC { } int Track::getSendLen(){ - int result = 163 + init.size() + codec.size() + type.size() + getIdentifier().size(); + int result = 146 + init.size() + codec.size() + type.size() + getIdentifier().size(); result += fragments.size() * 11; result += keys.size() * 16; result += parts.size() * 9; @@ -490,8 +477,6 @@ namespace DTSC { conn.SendNow((char*)parts, partLen*9); conn.SendNow("\000\007trackid\001", 10); conn.SendNow(convertLongLong(trackID), 8); - conn.SendNow("\000\006length\001", 9); - conn.SendNow(convertLongLong(length), 8); conn.SendNow("\000\007firstms\001", 10); conn.SendNow(convertLongLong(firstms), 8); conn.SendNow("\000\006lastms\001", 9); @@ -554,8 +539,6 @@ namespace DTSC { } conn.SendNow("\000\007trackid\001", 10); conn.SendNow(convertLongLong(trackID), 8); - conn.SendNow("\000\006length\001", 9); - conn.SendNow(convertLongLong(length), 8); conn.SendNow("\000\007firstms\001", 10); conn.SendNow(convertLongLong(firstms), 8); conn.SendNow("\000\006lastms\001", 9); @@ -675,7 +658,6 @@ namespace DTSC { result["parts"] = std::string((char*)parts, partLen * 9); } result["trackid"] = trackID; - result["length"] = length; result["firstms"] = firstms; result["lastms"] = lastms; result["bps"] = bps; @@ -722,7 +704,6 @@ namespace DTSC { } result["parts"] = tmp; result["trackid"] = trackID; - result["length"] = length; result["firstms"] = firstms; result["lastms"] = lastms; result["bps"] = bps; diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index e0d27024..babc7f35 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -658,7 +658,7 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S, DTSC::Track & videoRef, DTSC::Trac int i = 0; if (audioRef){ trinfo.addContent(AMF::Object("", AMF::AMF0_OBJECT)); - trinfo.getContentP(i)->addContent(AMF::Object("length", ((double)S.metadata.length) * ((double)audioRef.rate), AMF::AMF0_NUMBER)); + trinfo.getContentP(i)->addContent(AMF::Object("length", ((double)audioRef.lastms) * ((double)audioRef.rate), AMF::AMF0_NUMBER)); trinfo.getContentP(i)->addContent(AMF::Object("timescale", audioRef.rate, AMF::AMF0_NUMBER)); trinfo.getContentP(i)->addContent(AMF::Object("sampledescription", AMF::AMF0_STRICT_ARRAY)); if (audioRef.codec == "AAC"){ From 58b540222ac31eb6feb63514861bea19382331f7 Mon Sep 17 00:00:00 2001 From: Oswald Auguste de Bruin Date: Tue, 3 Dec 2013 15:42:41 +0100 Subject: [PATCH 572/788] Fixed ghost metadata track. --- lib/dtscmeta.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dtscmeta.cpp b/lib/dtscmeta.cpp index ea524f10..20ca4592 100644 --- a/lib/dtscmeta.cpp +++ b/lib/dtscmeta.cpp @@ -394,7 +394,7 @@ namespace DTSC { void Meta::update(JSON::Value & pack){ vod = pack.isMember("bpos"); live = !vod; - if (pack["trackid"].asInt()){ + if (pack["trackid"].asInt() && tracks.count(pack["trackid"].asInt()) ){ tracks[pack["trackid"].asInt()].update(pack); } } From 61a035c0b27d07e0630ef6758ece5363ed83cc5d Mon Sep 17 00:00:00 2001 From: Thulinma Date: Wed, 4 Dec 2013 13:52:57 +0100 Subject: [PATCH 573/788] Fixed readOnlyTrack::getSendLen. Oops. --- lib/dtscmeta.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dtscmeta.cpp b/lib/dtscmeta.cpp index 20ca4592..45216fed 100644 --- a/lib/dtscmeta.cpp +++ b/lib/dtscmeta.cpp @@ -429,7 +429,7 @@ namespace DTSC { } int readOnlyTrack::getSendLen(){ - int result = 163 + init.size() + codec.size() + type.size() + getIdentifier().size(); + int result = 146 + init.size() + codec.size() + type.size() + getIdentifier().size(); result += fragLen * 11; result += keyLen * 16; result += partLen * 9; From debce93475181a7864c041412b32ba326ec77a0b Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 5 Dec 2013 11:51:01 +0100 Subject: [PATCH 574/788] Fixed FLV tag copy constructor data address initialization. --- lib/flv_tag.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index babc7f35..5dca1ba8 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -309,12 +309,11 @@ FLV::Tag::Tag(const Tag& O){ done = true; sofar = 0; len = O.len; + data = 0; if (len > 0){ if (checkBufferSize()){ memcpy(data, O.data, len); } - }else{ - data = 0; } isKeyframe = O.isKeyframe; } //copy constructor From a9df633e87a7a33457fa23988e9dc47cd205c4d8 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 5 Dec 2013 12:39:53 +0100 Subject: [PATCH 575/788] More fixes to FLV memory bookkeeping. --- lib/flv_tag.cpp | 80 ++++++++++++++++++++++--------------------------- 1 file changed, 36 insertions(+), 44 deletions(-) diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index 5dca1ba8..05bf1235 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -513,17 +513,15 @@ bool FLV::Tag::DTSCVideoInit(DTSC::Track & video){ if (video.codec == "H264"){ len = video.init.size() + 20; } - if (len > 0){ - if ( !checkBufferSize()){ - return false; - } - memcpy(data + 16, video.init.c_str(), len - 20); - data[12] = 0; //H264 sequence header - data[13] = 0; - data[14] = 0; - data[15] = 0; - data[11] = 0x17; //H264 keyframe (0x07 & 0x10) + if (len <= 0 || !checkBufferSize()){ + return false; } + memcpy(data + 16, video.init.c_str(), len - 20); + data[12] = 0; //H264 sequence header + data[13] = 0; + data[14] = 0; + data[15] = 0; + data[11] = 0x17; //H264 keyframe (0x07 & 0x10) setLen(); data[0] = 0x09; data[1] = ((len - 15) >> 16) & 0xFF; @@ -546,34 +544,31 @@ bool FLV::Tag::DTSCAudioInit(DTSC::Track & audio){ if (audio.codec == "AAC"){ len = audio.init.size() + 17; } - if (len > 0){ - if ( !checkBufferSize()){ - return false; - } - memcpy(data + 13, audio.init.c_str(), len - 17); - data[12] = 0; //AAC sequence header - data[11] = 0; - if (audio.codec == "AAC"){ - data[11] += 0xA0; - } - if (audio.codec == "MP3"){ - data[11] += 0x20; - } - unsigned int datarate = audio.rate; - if (datarate >= 44100){ - data[11] += 0x0C; - }else if (datarate >= 22050){ - data[11] += 0x08; - }else if (datarate >= 11025){ - data[11] += 0x04; - } - if (audio.size == 16){ - data[11] += 0x02; - } - if (audio.channels > 1){ - data[11] += 0x01; - - } + if (len <= 0 || !checkBufferSize()){ + return false; + } + memcpy(data + 13, audio.init.c_str(), len - 17); + data[12] = 0; //AAC sequence header + data[11] = 0; + if (audio.codec == "AAC"){ + data[11] += 0xA0; + } + if (audio.codec == "MP3"){ + data[11] += 0x20; + } + unsigned int datarate = audio.rate; + if (datarate >= 44100){ + data[11] += 0x0C; + }else if (datarate >= 22050){ + data[11] += 0x08; + }else if (datarate >= 11025){ + data[11] += 0x04; + } + if (audio.size == 16){ + data[11] += 0x02; + } + if (audio.channels > 1){ + data[11] += 0x01; } setLen(); data[0] = 0x08; @@ -689,13 +684,10 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S, DTSC::Track & videoRef, DTSC::Trac std::string tmp = amfdata.Pack(); len = tmp.length() + 15; - if (len > 0){ - if (checkBufferSize()){ - memcpy(data + 11, tmp.c_str(), len - 15); - }else{ - return false; - } + if (len <= 0 || checkBufferSize()){ + return false; } + memcpy(data + 11, tmp.c_str(), len - 15); setLen(); data[0] = 0x12; data[1] = ((len - 15) >> 16) & 0xFF; From 88dfb7d5352c1ef4cb334f2615b99dc94c4ea1f8 Mon Sep 17 00:00:00 2001 From: Oswald Auguste de Bruin Date: Fri, 13 Dec 2013 11:41:44 +0100 Subject: [PATCH 576/788] Fixed metadata FLV bug --- lib/flv_tag.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index 05bf1235..c635f767 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -684,7 +684,7 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S, DTSC::Track & videoRef, DTSC::Trac std::string tmp = amfdata.Pack(); len = tmp.length() + 15; - if (len <= 0 || checkBufferSize()){ + if (len <= 0 || !checkBufferSize()){ return false; } memcpy(data + 11, tmp.c_str(), len - 15); From fc12369e5e811b05c987c8208d0213a5da59665b Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 16 Dec 2013 13:07:57 +0100 Subject: [PATCH 577/788] Fixed live support for DTSC metadata, improved track reset and ordering handling. --- lib/dtsc.cpp | 67 ++++++++++++++++++++++-------------------------- lib/dtsc.h | 8 +++--- lib/dtscmeta.cpp | 18 +++++++++++-- 3 files changed, 50 insertions(+), 43 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 9e1a6dc1..55eae8c8 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -218,19 +218,9 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ livePos newPos; newPos.trackID = newPack["trackid"].asInt(); newPos.seekTime = newPack["time"].asInt(); - if (buffercount > 1 && buffers.size() > 0){ - livePos lastPos = buffers.rbegin()->first; - if (newPos < lastPos){ - if ((lastPos.seekTime > 1000) && newPos.seekTime < lastPos.seekTime - 1000){ - resetStream(); - }else{ - newPos.seekTime = lastPos.seekTime+1; - } - } - }else{ + if (buffercount > 1 && metadata.tracks[newPos.trackID].keys.size() > 1 && newPos.seekTime < metadata.tracks[newPos.trackID].keys.rbegin()->getTime()){ resetStream(); } - std::string newTrack = metadata.tracks[newPos.trackID].getIdentifier(); while (buffers.count(newPos) > 0){ newPos.seekTime++; } @@ -261,19 +251,18 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ keyframes[newPos.trackID].insert(newPos); } metadata.live = true; + //throw away buffers if buffer time is met + int trid = buffers.begin()->first.trackID; + int firstTime = buffers.begin()->first.seekTime; + while (metadata.tracks[trid].keys.size() > 2 && metadata.tracks[trid].keys.rbegin()->getTime() - firstTime > buffertime){ + cutOneBuffer(); + trid = buffers.begin()->first.trackID; + firstTime = buffers.begin()->first.seekTime; + } + metadata.bufferWindow = buffertime; } - //increase buffer size if too little time available - unsigned int timeBuffered = buffers.rbegin()->second["time"].asInt() - buffers.begin()->second["time"].asInt(); - if (buffercount > 1){ - if (timeBuffered < buffertime){ - buffercount = buffers.size(); - if (buffercount < 2){buffercount = 2;} - } - metadata.bufferWindow = timeBuffered; - } - - while (buffers.size() > buffercount){ + while (buffercount == 1 && buffers.size() > 1){ cutOneBuffer(); } } @@ -282,24 +271,30 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ /// Will print a warning to std::cerr if a track has less than 2 keyframes left because of this. void DTSC::Stream::cutOneBuffer(){ int trid = buffers.begin()->first.trackID; - if (buffercount > 1 && keyframes[trid].count(buffers.begin()->first)){ - //if there are < 3 keyframes, throwing one away would mean less than 2 left. - if (keyframes[trid].size() < 3){ - std::cerr << "Warning - track " << trid << " doesn't have enough keyframes to be reliably served." << std::endl; + int delTime = buffers.begin()->first.seekTime; + if (buffercount > 1){ + while (keyframes[trid].size() > 0 && keyframes[trid].begin()->seekTime <= delTime){ + keyframes[trid].erase(keyframes[trid].begin()); } - keyframes[trid].erase(buffers.begin()->first); - for (int i = 0; i < metadata.tracks[trid].keys[0].getParts(); i++){ - metadata.tracks[trid].parts.pop_front(); + while (metadata.tracks[trid].keys.size() && metadata.tracks[trid].keys[0].getTime() <= delTime){ + for (int i = 0; i < metadata.tracks[trid].keys[0].getParts(); i++){ + metadata.tracks[trid].parts.pop_front(); + } + metadata.tracks[trid].keys.pop_front(); } - metadata.tracks[trid].keys.pop_front(); - metadata.tracks[trid].firstms = metadata.tracks[trid].keys[0].getTime(); - // delete fragments of which the beginning can no longer be reached - while (metadata.tracks[trid].fragments.size() && metadata.tracks[trid].fragments[0].getNumber() < metadata.tracks[trid].keys[0].getNumber()){ - metadata.tracks[trid].fragments.pop_front(); - // increase the missed fragments counter - metadata.tracks[trid].missedFrags++; + if (metadata.tracks[trid].keys.size()){ + metadata.tracks[trid].firstms = metadata.tracks[trid].keys[0].getTime(); + //delete fragments of which the beginning can no longer be reached + while (metadata.tracks[trid].fragments.size() && metadata.tracks[trid].fragments[0].getNumber() < metadata.tracks[trid].keys[0].getNumber()){ + metadata.tracks[trid].fragments.pop_front(); + //increase the missed fragments counter + metadata.tracks[trid].missedFrags++; + } + }else{ + metadata.tracks[trid].fragments.clear(); } } + deletionCallback(buffers.begin()->first); buffers.erase(buffers.begin()); } diff --git a/lib/dtsc.h b/lib/dtsc.h index ceb8eebd..f604232c 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -77,13 +77,11 @@ namespace DTSC { if (seekTime < rhs.seekTime){ return true; }else{ - if (seekTime == rhs.seekTime){ - if (trackID < rhs.trackID){ - return true; - } + if (seekTime > rhs.seekTime){ + return false; } } - return false; + return (trackID < rhs.trackID); } volatile long long unsigned int seekTime; volatile unsigned int trackID; diff --git a/lib/dtscmeta.cpp b/lib/dtscmeta.cpp index 45216fed..941dcdbe 100644 --- a/lib/dtscmeta.cpp +++ b/lib/dtscmeta.cpp @@ -155,7 +155,7 @@ namespace DTSC { firstms = trackRef["firstms"].asInt(); lastms = trackRef["lastms"].asInt(); bps = trackRef["bps"].asInt(); - missedFrags = trackRef["missed_fags"].asInt(); + missedFrags = trackRef["missed_frags"].asInt(); codec = trackRef["codec"].asString(); type = trackRef["type"].asString(); init = trackRef["init"].asString(); @@ -222,7 +222,7 @@ namespace DTSC { firstms = trackRef["firstms"].asInt(); lastms = trackRef["lastms"].asInt(); bps = trackRef["bps"].asInt(); - missedFrags = trackRef["missed_fags"].asInt(); + missedFrags = trackRef["missed_frags"].asInt(); codec = trackRef["codec"].asString(); type = trackRef["type"].asString(); init = trackRef["init"].asString(); @@ -243,6 +243,10 @@ namespace DTSC { } void Track::update(JSON::Value & pack){ + if (pack["time"].asInt() < lastms){ + std::cerr << "Received packets for track " << trackID << " in wrong order (" << pack["time"].asInt() << " < " << lastms << ") - ignoring!" << std::endl; + return; + } Part newPart; newPart.setSize(pack["data"].asString().size()); newPart.setOffset(pack["offset"].asInt()); @@ -442,6 +446,7 @@ namespace DTSC { result += 15 + idHeader.size();//idheader result += 20 + commentHeader.size();//commentheader } + if (missedFrags){result += 23;} return result; } @@ -459,6 +464,7 @@ namespace DTSC { result += 15 + idHeader.size();//idheader result += 20 + commentHeader.size();//commentheader } + if (missedFrags){result += 23;} return result; } @@ -477,6 +483,10 @@ namespace DTSC { conn.SendNow((char*)parts, partLen*9); conn.SendNow("\000\007trackid\001", 10); conn.SendNow(convertLongLong(trackID), 8); + if (missedFrags){ + conn.SendNow("\000\014missed_frags\001", 15); + conn.SendNow(convertLongLong(missedFrags), 8); + } conn.SendNow("\000\007firstms\001", 10); conn.SendNow(convertLongLong(firstms), 8); conn.SendNow("\000\006lastms\001", 9); @@ -539,6 +549,10 @@ namespace DTSC { } conn.SendNow("\000\007trackid\001", 10); conn.SendNow(convertLongLong(trackID), 8); + if (missedFrags){ + conn.SendNow("\000\014missed_frags\001", 15); + conn.SendNow(convertLongLong(missedFrags), 8); + } conn.SendNow("\000\007firstms\001", 10); conn.SendNow(convertLongLong(firstms), 8); conn.SendNow("\000\006lastms\001", 9); From a9ecac23ed472ab3261958d83f0dc47f9de16480 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 16 Dec 2013 15:00:57 +0100 Subject: [PATCH 578/788] Fixed DTSC bytes per second calculations. --- lib/dtscmeta.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/dtscmeta.cpp b/lib/dtscmeta.cpp index 941dcdbe..26bbf4ad 100644 --- a/lib/dtscmeta.cpp +++ b/lib/dtscmeta.cpp @@ -127,6 +127,7 @@ namespace DTSC { missedFrags = 0; firstms = 0; lastms = 0; + bps = 0; } readOnlyTrack::readOnlyTrack(JSON::Value & trackRef){ @@ -283,9 +284,12 @@ namespace DTSC { newFrag.setLength(1); newFrag.setNumber(keys[keys.size() - 1].getNumber()); if (fragments.size()){ - fragments[fragments.size() - 1].setDuration(pack["time"].asInt() - fragments[fragments.size() - 1].getDuration()); + fragments[fragments.size() - 1].setDuration(pack["time"].asInt() - getKey(fragments[fragments.size() - 1].getNumber()).getTime()); + if ( !bps && fragments[fragments.size() - 1].getDuration() > 1000){ + bps = (fragments[fragments.size() - 1].getSize() * 1000) / fragments[fragments.size() - 1].getDuration(); + } } - newFrag.setDuration(pack["time"].asInt()); + newFrag.setDuration(0); newFrag.setSize(0); fragments.push_back(newFrag); }else{ @@ -295,7 +299,6 @@ namespace DTSC { } keys.rbegin()->setParts(keys.rbegin()->getParts() + 1); fragments.rbegin()->setSize(fragments.rbegin()->getSize() + pack["data"].asString().size()); - bps += pack["data"].asString().size(); } Key & Track::getKey(int keyNum){ From 30e13972da493659e1bfbcc59bfc47c1f6748a9f Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Tue, 17 Dec 2013 14:33:57 +0100 Subject: [PATCH 579/788] Removed debug messages. --- lib/dtsc.cpp | 3 --- lib/dtscmeta.cpp | 1 - 2 files changed, 4 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 55eae8c8..bb35ff21 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -975,18 +975,15 @@ bool DTSC::isFixed(JSON::Value & metadata){ continue; } if (!it->second["keys"].isString()){ - std::cerr << "Not fixed because keys for track " << it->second["trackid"].asInt() << " is not a string" << std::endl; return false; } //Check for bpos: last element bpos != 0 std::string keyRef = it->second["keys"].asStringRef(); if (keyRef.size() < 16){ - std::cerr << "No keys in track " << it->second["trackid"].asInt() << std::endl; return false; } int offset = keyRef.size() - 17; if (!(keyRef[offset] | keyRef[offset+1] | keyRef[offset+2] | keyRef[offset+3] | keyRef[offset+4])){ - std::cerr << "Not fixed because last bpos for track " << it->second["trackid"].asInt() << " it 0" << std::endl; return false; } } diff --git a/lib/dtscmeta.cpp b/lib/dtscmeta.cpp index 26bbf4ad..108ea619 100644 --- a/lib/dtscmeta.cpp +++ b/lib/dtscmeta.cpp @@ -809,7 +809,6 @@ namespace DTSC { continue; } if (!it->second.keys.size() || !(it->second.keys.rbegin()->getBpos())){ - std::cerr << "Not fixed while track " << it->first << " has " << it->second.keys.size() << "keyframes" << std::endl; return false; } } From 1cf6e347459e6e6ccd970b5b94b99882adf69172 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Tue, 17 Dec 2013 14:53:17 +0100 Subject: [PATCH 580/788] Added call to player with streamname. --- lib/stream.cpp | 6 +++--- lib/stream.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/stream.cpp b/lib/stream.cpp index 588c3b42..af15740a 100644 --- a/lib/stream.cpp +++ b/lib/stream.cpp @@ -67,10 +67,10 @@ Socket::Connection Util::Stream::getLive(std::string streamname){ } /// Starts a process for a VoD stream. -Socket::Connection Util::Stream::getVod(std::string filename){ +Socket::Connection Util::Stream::getVod(std::string filename, std::string streamname){ std::string name = "MistPlayer " + filename; std::string player_bin = Util::getMyPath() + "MistPlayer"; - char* const argv[] = {(char*)player_bin.c_str(), (char*)filename.c_str(), NULL}; + char* const argv[] = {(char*)player_bin.c_str(), (char*)filename.c_str(), "-s", (char*)streamname.c_str(), NULL}; int fdin = -1, fdout = -1, fderr = fileno(stderr); Util::Procs::StartPiped(name, argv, &fdin, &fdout, &fderr); // if StartPiped fails then fdin and fdout will be unmodified (-1) @@ -86,7 +86,7 @@ Socket::Connection Util::Stream::getStream(std::string streamname){ #if DEBUG >= 5 std::cerr << "Opening VoD stream from file " << ServConf["streams"][streamname]["source"].asString() << std::endl; #endif - return getVod(ServConf["streams"][streamname]["source"].asString()); + return getVod(ServConf["streams"][streamname]["source"].asString(), streamname); }else{ #if DEBUG >= 5 std::cerr << "Opening live stream " << streamname << std::endl; diff --git a/lib/stream.h b/lib/stream.h index 2be8c92b..8037b367 100644 --- a/lib/stream.h +++ b/lib/stream.h @@ -11,7 +11,7 @@ namespace Util { public: static void sanitizeName(std::string & streamname); static Socket::Connection getLive(std::string streamname); - static Socket::Connection getVod(std::string streamname); + static Socket::Connection getVod(std::string filename, std::string streamname); static Socket::Connection getStream(std::string streamname); static Socket::Server makeLive(std::string streamname); }; From 06b5596b2a9bf34b6e43004213f604d25fe2a353 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Fri, 20 Dec 2013 13:07:38 +0100 Subject: [PATCH 581/788] Working DTSC Merge --- lib/dtsc.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index bb35ff21..8a9951fe 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -476,16 +476,14 @@ DTSC::File::File(const File & rhs){ DTSC::File & DTSC::File::operator =(const File & rhs){ created = rhs.created; if (rhs.F){ - int tmpFd = fileno(rhs.F); - int newFd = dup(tmpFd); - F = fdopen( newFd, (created ? "w+b": "r+b")); + F = fdopen( dup(fileno(rhs.F)), (created ? "w+b": "r+b")); }else{ F = 0; } endPos = rhs.endPos; strbuffer = rhs.strbuffer; - jsonbuffer = rhs.jsonbuffer; - metadata = rhs.metadata; + metaStorage = rhs.metaStorage; + metadata = metaStorage; currtime = rhs.currtime; lastreadpos = rhs.lastreadpos; headerSize = rhs.headerSize; From 87d027817c924bbd7aa7a67cac47a61d74c4c71d Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 19 Dec 2013 13:36:48 +0100 Subject: [PATCH 582/788] Assorted fixes, improvements etc --- lib/dtsc.cpp | 15 ++++++++-- lib/dtscmeta.cpp | 7 +++-- lib/json.h | 34 ++++++++++++++++++++++ lib/mp4.cpp | 76 +++++++++++++++++++++++++++++++----------------- lib/mp4.h | 6 ++-- 5 files changed, 103 insertions(+), 35 deletions(-) diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 8a9951fe..4ad6fa82 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -224,6 +224,9 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ while (buffers.count(newPos) > 0){ newPos.seekTime++; } + while (buffercount == 1 && buffers.size() > 0){ + cutOneBuffer(); + } buffers[newPos] = newPack; datapointertype = INVALID; std::string tmp = ""; @@ -262,9 +265,6 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ metadata.bufferWindow = buffertime; } - while (buffercount == 1 && buffers.size() > 1){ - cutOneBuffer(); - } } /// Deletes a the first part of the buffer, updating the keyframes list and metadata as required. @@ -922,6 +922,15 @@ bool DTSC::File::seek_bpos(int bpos){ return false; } +void DTSC::File::rewritePacket(std::string & newPacket, int bytePos){ + fseek(F, bytePos, SEEK_SET); + fwrite(newPacket.c_str(), newPacket.size(), 1, F); + fseek(F, 0, SEEK_END); + if (ftell(F) > endPos){ + endPos = ftell(F); + } +} + void DTSC::File::writePacket(std::string & newPacket){ fseek(F, 0, SEEK_END); fwrite(newPacket.c_str(), newPacket.size(), 1, F); //write contents diff --git a/lib/dtscmeta.cpp b/lib/dtscmeta.cpp index 108ea619..1c2b2957 100644 --- a/lib/dtscmeta.cpp +++ b/lib/dtscmeta.cpp @@ -209,11 +209,11 @@ namespace DTSC { Track::Track(JSON::Value & trackRef){ if (trackRef.isMember("fragments") && trackRef["fragments"].isString()){ Fragment* tmp = (Fragment*)trackRef["fragments"].asString().data(); - fragments = std::deque(tmp,tmp + (trackRef["fragments"].asString().size() / 11)); + fragments = std::deque(tmp, tmp + (trackRef["fragments"].asString().size() / 11)); } if (trackRef.isMember("keys") && trackRef["keys"].isString()){ Key* tmp = (Key*)trackRef["keys"].asString().data(); - keys = std::deque(tmp,tmp + (trackRef["keys"].asString().size() / 16)); + keys = std::deque(tmp, tmp + (trackRef["keys"].asString().size() / 16)); } if (trackRef.isMember("parts") && trackRef["parts"].isString()){ Part* tmp = (Part*)trackRef["parts"].asString().data(); @@ -277,7 +277,7 @@ namespace DTSC { } keys.push_back(newKey); firstms = keys[0].getTime(); - if (!fragments.size() || pack["time"].asInt() - 10000 >= getKey(fragments.rbegin()->getNumber()).getTime()){ + if (!fragments.size() || pack["time"].asInt() - 5000 >= getKey(fragments.rbegin()->getNumber()).getTime()){ //new fragment Fragment newFrag; newFrag.setDuration(0); @@ -342,6 +342,7 @@ namespace DTSC { readOnlyMeta::readOnlyMeta(){ vod = false; live = false; + merged = false; moreheader = 0; merged = false; bufferWindow = 0; diff --git a/lib/json.h b/lib/json.h index 2f4db947..f8b56eac 100644 --- a/lib/json.h +++ b/lib/json.h @@ -140,4 +140,38 @@ namespace JSON { } } } + + template + 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 + 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; + } + } + } } diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 2b7237ca..5a8cbb5d 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -244,7 +244,8 @@ namespace MP4 { case 0x73747364: return ((STSD*)this)->toPrettyString(indent); break; - case 0x6D703461: + case 0x6D703461://mp4a + case 0x656E6361://enca return ((MP4A*)this)->toPrettyString(indent); break; case 0x61616320: @@ -253,7 +254,8 @@ namespace MP4 { case 0x61766331: return ((AVC1*)this)->toPrettyString(indent); break; - case 0x68323634: + case 0x68323634://h264 + case 0x656E6376://encv return ((H264*)this)->toPrettyString(indent); break; case 0x65647473: @@ -493,7 +495,7 @@ namespace MP4 { /// 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; + static Box retbox = Box((char*)"\000\000\000\010erro", false); index += payloadOffset; if (index + 8 > boxedSize()){ if ( !reserve(index, 0, 8)){ @@ -510,7 +512,7 @@ namespace MP4 { /// 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()){ + if ((index + payloadOffset + 8) > boxedSize()){ return 0; } return getBox(index).boxedSize(); @@ -600,7 +602,7 @@ namespace MP4 { int tempLoc = 0; while (tempLoc < boxedSize() - 8){ res++; - tempLoc += getBoxLen(tempLoc); + tempLoc += Box(getBox(tempLoc).asBox(), false).boxedSize(); } return res; } @@ -640,13 +642,9 @@ namespace MP4 { std::string containerBox::toPrettyContainerString(uint32_t indent, std::string boxName){ std::stringstream r; r << std::string(indent, ' ') << boxName <<" (" << boxedSize() << ")" << std::endl; - Box curBox; - int tempLoc = 0; - int contentCount = getContentCount(); - for (int i = 0; i < contentCount; i++){ - curBox = getContent(i); + for (int i = 0; i < getContentCount(); i++){ + Box curBox = MP4::Box(getContent(i).asBox(), false); r << curBox.toPrettyString(indent + 1); - tempLoc += getBoxLen(tempLoc); } return r.str(); } @@ -1508,13 +1506,10 @@ namespace MP4 { std::string TRAF::toPrettyString(uint32_t indent){ std::stringstream r; r << std::string(indent, ' ') << "[traf] Track Fragment Box (" << boxedSize() << ")" << std::endl; - Box curBox; - int tempLoc = 0; int contentCount = getContentCount(); for (int i = 0; i < contentCount; i++){ - curBox = getContent(i); + Box curBox = Box(getContent(i).asBox(),false); r << curBox.toPrettyString(indent + 1); - tempLoc += curBox.boxedSize(); } return r.str(); } @@ -2274,6 +2269,25 @@ namespace MP4 { return r.str(); } + void AVCC::fromAnnexB(std::string annexBFormatted){ + ///\todo fix correct data :p + setVersion(0x01); + setProfile(0x4D); + setCompatibleProfiles(0x40); + setLevel(0x1F); + setSPSNumber(0xE1); + static char annexBHeader[] = {0x00,0x00,0x00,0x01}; + if (memcmp(annexBFormatted.c_str(), annexBHeader, 4)){ + return; + } + annexBFormatted.erase(0,4); + int separator = annexBFormatted.find(annexBHeader, 0, 4); + setSPS(annexBFormatted.substr(0,separator)); + setPPSNumber(0x01); + annexBFormatted.erase(0,separator+4); + setPPS(annexBFormatted); + } + void AVCC::setPayload(std::string newPayload){ if ( !reserve(0, payloadSize(), newPayload.size())){ std::cerr << "Cannot allocate enough memory for payload" << std::endl; @@ -2518,8 +2532,9 @@ namespace MP4 { r << std::string(indent + 1, ' ') << "ConfigDescriptorTypeLength: 0x" << std::hex << (int)getConfigDescriptorTypeLength() << std::dec << std::endl; r << std::string(indent + 1, ' ') << "ESHeaderStartCodes: 0x"; for (unsigned int i = 0; i> 16; } + + uint16_t AudioSampleEntry::toAACInit(){ + uint16_t result = 0; + result |= (2 & 0x1F) << 11; + result |= (getSampleRate() & 0x0F) << 7; + result |= (getChannelCount() & 0x0F) << 3; + return result; + } void AudioSampleEntry::setCodecBox(Box& newBox){ setBox(newBox, 28); @@ -4356,7 +4382,7 @@ namespace MP4 { r << std::string(indent + 1, ' ') << "SampleSize: " << getSampleSize() << std::endl; r << std::string(indent + 1, ' ') << "PreDefined: " << getPreDefined() << std::endl; r << std::string(indent + 1, ' ') << "SampleRate: " << getSampleRate() << std::endl; - r << getCodecBox().toPrettyString(indent + 1) << std::endl; + r << getCodecBox().toPrettyString(indent + 1); return r.str(); } @@ -4447,13 +4473,9 @@ namespace MP4 { r << std::string(indent, ' ') << "[stsd] Sample Description Box (" << boxedSize() << ")" << std::endl; r << fullBox::toPrettyString(indent); r << std::string(indent + 1, ' ') << "EntrySize: " << getEntryCount() << std::endl; - Box curBox; - int tempLoc = 0; - int contentCount = getEntryCount(); - for (int i = 0; i < contentCount; i++){ - curBox = getEntry(i); + for (int i = 0; i < getEntryCount(); i++){ + Box curBox = Box(getEntry(i).asBox(), false); r << curBox.toPrettyString(indent + 1); - tempLoc += getBoxLen(tempLoc); } return r.str(); } diff --git a/lib/mp4.h b/lib/mp4.h index 68dfeea0..0715171e 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -9,6 +9,7 @@ #include #include #include +#include #include "json.h" #include "dtsc.h" @@ -256,7 +257,7 @@ namespace MP4 { uint32_t sampleDuration; uint32_t sampleSize; uint32_t sampleFlags; - uint32_t sampleOffset; + int32_t sampleOffset; }; enum trunflags{ trundataOffset = 0x00000001, @@ -378,6 +379,7 @@ namespace MP4 { uint32_t getPPSLen(); char* getPPS(); std::string asAnnexB(); + void fromAnnexB(std::string annexBFormatted); void setPayload(std::string newPayload); std::string toPrettyString(uint32_t indent = 0); }; @@ -869,6 +871,7 @@ namespace MP4 { uint16_t getPreDefined(); void setSampleRate(uint32_t newSampleRate); uint32_t getSampleRate(); + uint16_t toAACInit(); void setCodecBox(Box& newBox); Box & getCodecBox(); std::string toPrettyAudioString(uint32_t indent = 0, std::string name = ""); @@ -976,4 +979,3 @@ namespace MP4 { }; } - From c7e67b2d843fb283a860aae03e3c607ac5f1775d Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 20 Dec 2013 13:54:28 +0100 Subject: [PATCH 583/788] Missed a line earlier. Whee. --- lib/dtsc.h | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/dtsc.h b/lib/dtsc.h index f604232c..e3d79d8f 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -249,6 +249,7 @@ namespace DTSC { bool seek_time(int seconds); bool seek_time(int seconds, 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(); From cfb8edf4f318a78616d3212cc5ca6caddb032024 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Fri, 20 Dec 2013 14:26:43 +0100 Subject: [PATCH 584/788] Fixed MP4 multibitrate/multicodec support. Now with extra sanity (checks)! --- lib/mp4_conv.cpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/lib/mp4_conv.cpp b/lib/mp4_conv.cpp index 20b725d3..cc734c10 100644 --- a/lib/mp4_conv.cpp +++ b/lib/mp4_conv.cpp @@ -46,11 +46,23 @@ namespace MP4{ mvhdBox.setMatrix(0x40000000,8); moovBox.setContent(mvhdBox, 0); + bool seenAudio = false; + bool seenVideo = false; + //calculate interleaving //putting all metadata in a huge, auto-sorting vector 'keyParts' //sort by time on keyframes for interleaving keyParts.clear(); for ( std::map::iterator trackIt = metaData.tracks.begin(); trackIt != metaData.tracks.end(); trackIt ++) { + if (trackIt->second.codec != "AAC" && trackIt->second.codec != "H264"){continue;} + if (trackIt->second.type == "audio"){ + if (seenAudio){continue;} + seenAudio = true; + } + if (trackIt->second.type == "video"){ + if (seenVideo){continue;} + seenVideo = true; + } if (trackIt->first>0){ int partItNumber = 0; for ( std::deque< DTSC::Key>::iterator keyIt = trackIt->second.keys.begin(); keyIt != trackIt->second.keys.end(); keyIt ++) { @@ -75,7 +87,18 @@ namespace MP4{ //start arbitrary track addition for header int boxOffset = 1; + seenAudio = false; + seenVideo = false; for ( std::map::iterator it = metaData.tracks.begin(); it != metaData.tracks.end(); it ++) { + if (it->second.codec != "AAC" && it->second.codec != "H264"){continue;} + if (it->second.type == "audio"){ + if (seenAudio){continue;} + seenAudio = true; + } + if (it->second.type == "video"){ + if (seenVideo){continue;} + seenVideo = true; + } if (it->first > 0){ int timescale = 0; MP4::TRAK trakBox; From 33427f1663dc35bb34eb6dfdd82388da618d020c Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 5 Jan 2014 11:57:25 +0100 Subject: [PATCH 585/788] Changed buildsystems from automake to plain make. --- .gitignore | 10 +++++--- AUTHORS | 1 - ChangeLog | 7 ------ Makefile | 62 ++++++++++++++++++++++++++++++++++++++++++++++ Makefile.am | 5 ---- NEWS | 7 ------ README | 42 +++++++++++++++++++++++++------ configure.ac | 52 -------------------------------------- createhooks.sh | 7 +++--- lib/Makefile.am | 52 -------------------------------------- lib/mist-1.0.pc.in | 10 -------- 11 files changed, 106 insertions(+), 149 deletions(-) delete mode 100644 AUTHORS delete mode 100644 ChangeLog create mode 100644 Makefile delete mode 100644 Makefile.am delete mode 100644 NEWS delete mode 100644 configure.ac delete mode 100644 lib/Makefile.am delete mode 100644 lib/mist-1.0.pc.in diff --git a/.gitignore b/.gitignore index 0b3b45e4..e7bec938 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,16 @@ #ignore object files and nonsense like that -*.[oa] -*.la -*.lo +*.o +*.orig +*.bak *~ .deps -Makefile Makefile.in version.m4 docs nbproject autom4te.cache +/libmist.so +/libmist.a /configure /config.* /aclocal.m4 @@ -27,3 +28,4 @@ libtool *.json *.pc *.swp + diff --git a/AUTHORS b/AUTHORS deleted file mode 100644 index a74a17f1..00000000 --- a/AUTHORS +++ /dev/null @@ -1 +0,0 @@ -All code so far was written by DDVTECH employees. diff --git a/ChangeLog b/ChangeLog deleted file mode 100644 index f1ad0c0f..00000000 --- a/ChangeLog +++ /dev/null @@ -1,7 +0,0 @@ -This is a build from the Mistserver git repository located at: -https://github.com/DDVTECH/DMS - -For a full changelog please see the repository history. - -The version of this build can be found in the version.m4 file and -is a valid checkout point for the above mentioned repository. diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..3cf260f2 --- /dev/null +++ b/Makefile @@ -0,0 +1,62 @@ +prefix = /usr +exec_prefix = $(prefix) +includedir = $(prefix)/include +libdir = $(exec_prefix)/lib + +PACKAGE_VERSION := $(shell git describe --tags 2> /dev/null || cat VERSION 2> /dev/null || echo "Unknown") +DEBUG = 4 + +ifeq ($(PACKAGE_VERSION),Unknown) + $(warning Version is unknown - consider creating a VERSION file or fixing your git setup.) +endif + +CPPFLAGS = -Wall -g -O2 -fPIC -DDEBUG="$(DEBUG)" -DPACKAGE_VERSION="\"$(PACKAGE_VERSION)\"" + +LDLIBS = -lcrypto + + +.DEFAULT_GOAL := all + +all: libmist.so libmist.a + +DOXYGEN := $(shell doxygen -v 2> /dev/null) +ifdef DOXYGEN +all: docs +else +$(warning Doxygen not installed - not building source documentation.) +endif + +objects := $(patsubst %.cpp,%.o,$(wildcard lib/*.cpp)) + + +libmist.so: $(objects) + $(CXX) -shared -o $@ $(LDLIBS) $^ + +libmist.a: $(objects) + $(AR) -rcs $@ $^ + +docs: lib/*.h lib/*.cpp Doxyfile + doxygen ./Doxyfile > /dev/null + +clean: + rm -f lib/*.o libmist.so libmist.a + rm -rf ./docs + +install: libmist.so libmist.a lib/*.h + mkdir -p $(DESTDIR)$(includedir)/mist + install lib/*.h $(DESTDIR)$(includedir)/mist/ + install libmist.so $(DESTDIR)$(libdir)/libmist.so + install libmist.a $(DESTDIR)$(libdir)/libmist.a + $(POST_INSTALL) + ldconfig + +uninstall: + 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 + + + diff --git a/Makefile.am b/Makefile.am deleted file mode 100644 index ef0897d4..00000000 --- a/Makefile.am +++ /dev/null @@ -1,5 +0,0 @@ -SUBDIRS=lib -EXTRA_DIST=VERSION -docs: - doxygen ./Doxyfile > /dev/null -.PHONY: docs diff --git a/NEWS b/NEWS deleted file mode 100644 index f1ad0c0f..00000000 --- a/NEWS +++ /dev/null @@ -1,7 +0,0 @@ -This is a build from the Mistserver git repository located at: -https://github.com/DDVTECH/DMS - -For a full changelog please see the repository history. - -The version of this build can be found in the version.m4 file and -is a valid checkout point for the above mentioned repository. diff --git a/README b/README index 60358b64..485e9e4d 100644 --- a/README +++ b/README @@ -1,9 +1,37 @@ -For full documentation as well as background information, visit our wiki at: -http://wiki.mistserver.com/ + _________________________________________________ +| Mist Libraries | +| Copyright 2010-2014 DDVTech BV, The Netherlands | +| | +| Licensed under the aGPLv3 license | +| See COPYING file for full license | +|_________________________________________________| -Code contributions and bug reports are welcomed through: -https://github.com/DDVTECH/libmist -The following configure options are possible: ---enable-verbose = Compiles the libraries in verbose mode, printing a lot more information to the screen than normally. ---disable-verbose = The opposite of above (default). +The latest version of this code can always be found at: + https://github.com/DDVTECH/mistlib + +For full documentation/changelogs/etc visit our wiki at: + http://wiki.mistserver.com/ + +Code contributions and bug reports are welcomed! Please submit at: + https://github.com/DDVTECH/mistlib + +To install using default options, simply run: + make && sudo make install + +Dependencies: + openssl + +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. + PACKAGE_VERSION Overrides the library version number string. You shouldn't need to use this, normally. + prefix Prefix to install files to. Defaults to /usr + 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 + 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 -fPIC -DDEBUG="$(DEBUG)" -DPACKAGE_VERSION="\"$(PACKAGE_VERSION)\"" + LDLIBS Libraries to include. Defaults to -lcrypto + +Use "make var1=val1 var2=val2" to set these. For example: + make install DEBUG=0 prefix=/usr/local diff --git a/configure.ac b/configure.ac deleted file mode 100644 index 6103353a..00000000 --- a/configure.ac +++ /dev/null @@ -1,52 +0,0 @@ -# -*- Autoconf -*- -# Process this file with autoconf to produce a configure script. - -AC_PREREQ([2.60]) -AC_INIT([libmist], - m4_esyscmd([git rev-parse 2>/dev/null&&git describe --tags > VERSION;tr -d '\n' < VERSION]), - [contact@ddvtech.com]) -AC_CONFIG_SRCDIR([lib/dtsc.cpp]) -# foreign: no need for NEWS or INSTALL files -AM_INIT_AUTOMAKE([foreign]) -LT_INIT - -# Checks for programs. -AC_PROG_CXX -AC_PROG_CC - -# Checks for libraries. -AC_DEFINE(_GNU_SOURCE) -PKG_CHECK_MODULES(DEPS, openssl) - -# Checks for header files. -AC_CHECK_HEADERS([arpa/inet.h fcntl.h netdb.h netinet/in.h stdint.h stdlib.h string.h sys/socket.h sys/time.h unistd.h]) - - -# Checks for typedefs, structures, and compiler characteristics. -AC_HEADER_STDBOOL -AC_C_INLINE -AC_TYPE_INT32_T -AC_TYPE_PID_T -AC_TYPE_SIZE_T -AC_TYPE_UINT32_T -AC_TYPE_UINT64_T -AC_TYPE_UINT8_T - -# Checks for library functions. -AC_FUNC_FORK -AC_FUNC_MALLOC -AC_FUNC_REALLOC -AC_CHECK_FUNCS([dup2 gettimeofday memset mkdir socket strerror]) - -AC_CHECK_FUNCS([clock_gettime], [CLOCK_LIB=], [AC_CHECK_LIB([rt], [clock_gettime], [CLOCK_LIB=-lrt], [CLOCK_LIB=])]) -AC_SUBST([CLOCK_LIB]) - -# Fix chars to unsigned -AC_SUBST([global_CFLAGS], [-funsigned-char]) - -#allow verbose mode compiles -AC_ARG_ENABLE([verbose], AC_HELP_STRING([--enable-verbose], [Compile with verbose messages]), - AC_DEFINE([DEBUG], [4])) - -AC_CONFIG_FILES([Makefile lib/Makefile lib/mist-1.0.pc]) -AC_OUTPUT diff --git a/createhooks.sh b/createhooks.sh index 346bbe6a..c1c90b04 100755 --- a/createhooks.sh +++ b/createhooks.sh @@ -1,10 +1,9 @@ #!/bin/bash DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -echo -e "#!/bin/bash\n[ -f configure ] && touch configure\n[ -f configure.ac ] && touch configure.ac" > $DIR/.git/hooks/post-commit -echo -e "#!/bin/bash\n[ -f configure ] && touch configure\n[ -f configure.ac ] && touch configure.ac" > $DIR/.git/hooks/post-checkout -echo -e "#!/bin/bash\n[ -f configure ] && touch configure\n[ -f configure.ac ] && touch configure.ac" > $DIR/.git/hooks/post-merge +echo -e "#!/bin/bash\nmake clean" > $DIR/.git/hooks/post-commit +echo -e "#!/bin/bash\nmake clean" > $DIR/.git/hooks/post-checkout +echo -e "#!/bin/bash\nmake clean" > $DIR/.git/hooks/post-merge chmod +x $DIR/.git/hooks/post-commit chmod +x $DIR/.git/hooks/post-checkout chmod +x $DIR/.git/hooks/post-merge echo "Done! The version number should now auto-update whenever you commit or checkout." - diff --git a/lib/Makefile.am b/lib/Makefile.am deleted file mode 100644 index aaef3331..00000000 --- a/lib/Makefile.am +++ /dev/null @@ -1,52 +0,0 @@ -lib_LTLIBRARIES=libmist-1.0.la -libmist_1_0_la_SOURCES=amf.h amf.cpp -libmist_1_0_la_SOURCES+=auth.h auth.cpp -libmist_1_0_la_SOURCES+=base64.h base64.cpp -libmist_1_0_la_SOURCES+=config.h config.cpp -libmist_1_0_la_SOURCES+=dtsc.h dtsc.cpp dtscmeta.cpp -libmist_1_0_la_SOURCES+=flv_tag.h flv_tag.cpp -libmist_1_0_la_SOURCES+=http_parser.h http_parser.cpp -libmist_1_0_la_SOURCES+=json.h json.cpp -libmist_1_0_la_SOURCES+=procs.h procs.cpp -libmist_1_0_la_SOURCES+=rtmpchunks.h rtmpchunks.cpp -libmist_1_0_la_SOURCES+=socket.h socket.cpp -libmist_1_0_la_SOURCES+=mp4.h mp4.cpp mp4_conv.cpp -libmist_1_0_la_SOURCES+=ftp.h ftp.cpp -libmist_1_0_la_SOURCES+=filesystem.h filesystem.cpp -libmist_1_0_la_SOURCES+=stream.h stream.cpp -libmist_1_0_la_SOURCES+=timing.h timing.cpp -libmist_1_0_la_SOURCES+=ts_packet.cpp ts_packet.h -libmist_1_0_la_SOURCES+=converter.cpp converter.h -libmist_1_0_la_SOURCES+=ogg.h ogg.cpp -libmist_1_0_la_SOURCES+=theora.cpp theora.h -libmist_1_0_la_SOURCES+=vorbis.cpp vorbis.h -libmist_1_0_la_LDFLAGS = -version-info 5:1:2 -libmist_1_0_la_CPPFLAGS=$(DEPS_CFLAGS) $(global_CFLAGS) -libmist_1_0_la_LIBADD=$(DEPS_LIBS) $(CLOCK_LIB) - -pkgconfigdir = $(libdir)/pkgconfig -pkgconfig_DATA = mist-1.0.pc - -library_includedir=$(includedir)/mist-1.0/mist -library_include_HEADERS = amf.h -library_include_HEADERS +=auth.h -library_include_HEADERS +=base64.h -library_include_HEADERS +=config.h -library_include_HEADERS +=dtsc.h -library_include_HEADERS +=flv_tag.h -library_include_HEADERS +=http_parser.h -library_include_HEADERS +=json.h -library_include_HEADERS +=procs.h -library_include_HEADERS +=rtmpchunks.h -library_include_HEADERS +=socket.h -library_include_HEADERS +=mp4.h -library_include_HEADERS +=ftp.h -library_include_HEADERS +=filesystem.h -library_include_HEADERS +=stream.h -library_include_HEADERS +=timing.h -library_include_HEADERS +=nal.h -library_include_HEADERS +=ts_packet.h -library_include_HEADERS +=converter.h -library_include_HEADERS +=ogg.h -library_include_HEADERS +=theora.h -library_include_HEADERS +=vorbis.h diff --git a/lib/mist-1.0.pc.in b/lib/mist-1.0.pc.in deleted file mode 100644 index dbbd603f..00000000 --- a/lib/mist-1.0.pc.in +++ /dev/null @@ -1,10 +0,0 @@ -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: Mist -Description: Mist Streaming Media Library -Version: @PACKAGE_VERSION@ -Libs: -L${libdir} -lmist-1.0 -Cflags: -I${includedir}/mist-1.0 -I${libdir}/mist-1.0/include From b5a4ea1b93b45b0985143a95921fe4ccc57c5344 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sun, 5 Jan 2014 11:57:56 +0100 Subject: [PATCH 586/788] Remove doxygen errors, update Doxyfile to latest version. --- Doxyfile | 2335 ++++++++++++++++++++++++++----------------- lib/http_parser.cpp | 2 + lib/mp4.cpp | 2 +- lib/mp4_conv.cpp | 8 +- lib/ogg.cpp | 2 +- lib/rtmpchunks.cpp | 12 +- lib/timing.cpp | 2 +- lib/timing.h | 2 +- lib/ts_packet.cpp | 2 + lib/ts_packet.h | 1 + 10 files changed, 1429 insertions(+), 939 deletions(-) diff --git a/Doxyfile b/Doxyfile index 3948c111..8cdff0d2 100644 --- a/Doxyfile +++ b/Doxyfile @@ -1,110 +1,121 @@ -# Doxyfile 1.8.2 +# Doxyfile 1.8.6 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # -# All text after a hash (#) is considered a comment and will be ignored. +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. # The format is: -# TAG = value [value, ...] -# For lists items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (" "). +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all -# text before the first occurrence of this tag. Doxygen uses libiconv (or the -# iconv built into libc) for the transcoding. See -# http://www.gnu.org/software/libiconv for the list of possible encodings. +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 -# The PROJECT_NAME tag is a single word (or sequence of words) that should -# identify the project. Note that if you do not use Doxywizard you need -# to put quotes around the project name if it contains spaces. +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. PROJECT_NAME = DDVTECHStreamingServer -# The PROJECT_NUMBER tag can be used to enter a project or revision number. -# This could be handy for archiving the generated documentation or -# if some version control system is used. +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. PROJECT_NUMBER = 1 # Using the PROJECT_BRIEF tag one can provide an optional one line description -# for a project that appears at the top of each page and should give viewer -# a quick idea about the purpose of the project. Keep the description short. +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = -# With the PROJECT_LOGO tag one can specify an logo or icon that is -# included in the documentation. The maximum height of the logo should not -# exceed 55 pixels and the maximum width should not exceed 200 pixels. -# Doxygen will copy the logo to the output directory. +# With the PROJECT_LOGO tag one can specify an logo or icon that is included in +# the documentation. The maximum height of the logo should not exceed 55 pixels +# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo +# to the output directory. PROJECT_LOGO = -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) -# base path where the generated documentation will be put. -# If a relative path is entered, it will be relative to the location -# where doxygen was started. If left blank the current directory will be used. +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. OUTPUT_DIRECTORY = ./docs -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create -# 4096 sub-directories (in 2 levels) under the output directory of each output -# format and will distribute the generated files over these directories. -# Enabling this option can be useful when feeding doxygen a huge amount of -# source files, where putting all generated files in the same directory would -# otherwise cause performance problems for the file system. +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. -# The default language is English, other supported languages are: -# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, -# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, -# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English -# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, -# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, -# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. OUTPUT_LANGUAGE = English -# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will -# include brief member descriptions after the members that are listed in -# the file and class documentation (similar to JavaDoc). -# Set to NO to disable this. +# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. BRIEF_MEMBER_DESC = YES -# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend -# the brief description of a member or function before the detailed description. -# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. +# The default value is: YES. REPEAT_BRIEF = YES -# This tag implements a quasi-intelligent brief description abbreviator -# that is used to form the text in various listings. Each string -# in this list, if found as the leading text of the brief description, will be -# stripped from the text and the result after processing the whole list, is -# used as the annotated text. Otherwise, the brief description is used as-is. -# If left blank, the following values are used ("$name" is automatically -# replaced with the name of the entity): "The $name class" "The $name widget" -# "The $name file" "is" "provides" "specifies" "contains" -# "represents" "a" "an" "the" +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# Doxygen will generate a detailed section even if there is only a brief +# doxygen will generate a detailed section even if there is only a brief # description. +# The default value is: NO. ALWAYS_DETAILED_SEC = NO @@ -112,174 +123,204 @@ ALWAYS_DETAILED_SEC = NO # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. +# The default value is: NO. INLINE_INHERITED_MEMB = YES -# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full -# path before files name in the file list and in the header files. If set -# to NO the shortest path that makes the file name unique will be used. +# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. FULL_PATH_NAMES = YES -# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag -# can be used to strip a user-defined part of the path. Stripping is -# only done if one of the specified strings matches the left-hand part of -# the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the -# path to strip. Note that you specify absolute paths here, but also -# relative paths, which will be relative from the directory where doxygen is -# started. +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of -# the path mentioned in the documentation of a class, which tells -# the reader which header file to include in order to use a class. -# If left blank only the name of the header file containing the class -# definition is used. Otherwise one should specify the include paths that -# are normally passed to the compiler using the -I flag. +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. STRIP_FROM_INC_PATH = -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter -# (but less readable) file names. This can be useful if your file system -# doesn't support long names like on DOS, Mac, or CD-ROM. +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. SHORT_NAMES = NO -# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen -# will interpret the first line (until the first dot) of a JavaDoc-style -# comment as the brief description. If set to NO, the JavaDoc -# comments will behave just like regular Qt-style comments -# (thus requiring an explicit @brief command for a brief description.) +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. JAVADOC_AUTOBRIEF = YES -# If the QT_AUTOBRIEF tag is set to YES then Doxygen will -# interpret the first line (until the first dot) of a Qt-style -# comment as the brief description. If set to NO, the comments -# will behave just like regular Qt-style comments (thus requiring -# an explicit \brief command for a brief description.) +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. QT_AUTOBRIEF = NO -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen -# treat a multi-line C++ special comment block (i.e. a block of //! or /// -# comments) as a brief description. This used to be the default behaviour. -# The new default is to treat a multi-line C++ comment block as a detailed -# description. Set this tag to YES if you prefer the old behaviour instead. +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO -# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented -# member inherits the documentation from any documented member that it -# re-implements. +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. INHERIT_DOCS = YES -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce -# a new page for each member. If set to NO, the documentation of a member will -# be part of the file/class/namespace that contains it. +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a +# new page for each member. If set to NO, the documentation of a member will be +# part of the file/class/namespace that contains it. +# The default value is: NO. SEPARATE_MEMBER_PAGES = NO -# The TAB_SIZE tag can be used to set the number of spaces in a tab. -# Doxygen uses this value to replace tabs by spaces in code fragments. +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 2 -# This tag can be used to specify a number of aliases that acts -# as commands in the documentation. An alias has the form "name=value". -# For example adding "sideeffect=\par Side Effects:\n" will allow you to -# put the command \sideeffect (or @sideeffect) in the documentation, which -# will result in a user-defined paragraph with heading "Side Effects:". -# You can put \n's in the value part of an alias to insert newlines. +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. ALIASES = # This tag can be used to specify a number of word-keyword mappings (TCL only). -# A mapping has the form "name=value". For example adding -# "class=itcl::class" will allow you to use the command class in the -# itcl::class meaning. +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. TCL_SUBST = -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C -# sources only. Doxygen will then generate output that is more tailored for C. -# For instance, some of the names that are used will be different. The list -# of all members will be omitted, etc. +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = YES -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java -# sources only. Doxygen will then generate output that is more tailored for -# Java. For instance, namespaces will be presented as packages, qualified -# scopes will look different, etc. +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran -# sources only. Doxygen will then generate output that is more tailored for -# Fortran. +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL -# sources. Doxygen will then generate output that is tailored for -# VHDL. +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it -# using this tag. The format is ext=language, where ext is a file extension, -# and language is one of the parsers supported by doxygen: IDL, Java, -# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, -# C++. For instance to make doxygen treat .inc files as Fortran files (default -# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note -# that for custom extensions you also need to set FILE_PATTERNS otherwise the -# files are not read by doxygen. +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. +# +# Note For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. EXTENSION_MAPPING = -# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all -# comments according to the Markdown format, which allows for more readable +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable # documentation. See http://daringfireball.net/projects/markdown/ for details. -# The output of markdown processing is further processed by doxygen, so you -# can mix doxygen, HTML, and XML commands with Markdown formatting. -# Disable only in case of backward compatibilities issues. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. MARKDOWN_SUPPORT = YES -# When enabled doxygen tries to link words that correspond to documented classes, -# or namespaces to their corresponding documentation. Such a link can be -# prevented in individual cases by by putting a % sign in front of the word or -# globally by setting AUTOLINK_SUPPORT to NO. +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by by putting a % sign in front of the word +# or globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want -# to include (a tag file for) the STL sources as input, then you should -# set this tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. -# func(std::string) {}). This also makes the inheritance and collaboration +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. +# The default value is: NO. BUILTIN_STL_SUPPORT = YES # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. +# The default value is: NO. CPP_CLI_SUPPORT = NO -# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. -# Doxygen will parse them like normal C++ but will assume all classes use public -# instead of private inheritance when no explicit protection keyword is present. +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. SIP_SUPPORT = NO -# For Microsoft's IDL there are propget and propput attributes to indicate getter and setter methods for a property. Setting this option to YES (the default) will make doxygen replace the get and set methods by a property in the documentation. This will only work if the methods are indeed getting or setting a simple type. If this is not the case, or you want to show the methods anyway, you should set this option to NO. +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. IDL_PROPERTY_SUPPORT = NO @@ -287,67 +328,61 @@ IDL_PROPERTY_SUPPORT = NO # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. +# The default value is: NO. DISTRIBUTE_GROUP_DOC = NO -# Set the SUBGROUPING tag to YES (the default) to allow class member groups of -# the same type (for instance a group of public functions) to be put as a -# subgroup of that type (e.g. under the Public Functions section). Set it to -# NO to prevent subgrouping. Alternatively, this can be done per class using -# the \nosubgrouping command. +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. SUBGROUPING = YES -# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and -# unions are shown inside the group in which they are included (e.g. using -# @ingroup) instead of on a separate page (for HTML and Man pages) or -# section (for LaTeX and RTF). +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. INLINE_GROUPED_CLASSES = NO -# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and -# unions with only public data fields will be shown inline in the documentation -# of the scope in which they are defined (i.e. file, namespace, or group -# documentation), provided this scope is documented. If set to NO (the default), -# structs, classes, and unions are shown on a separate page (for HTML and Man -# pages) or section (for LaTeX and RTF). +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. INLINE_SIMPLE_STRUCTS = NO -# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum -# is documented as struct, union, or enum with the name of the typedef. So +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically -# be useful for C code in case the coding convention dictates that all compound +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. TYPEDEF_HIDES_STRUCT = NO -# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to -# determine which symbols to keep in memory and which to flush to disk. -# When the cache is full, less often used symbols will be written to disk. -# For small to medium size projects (<1000 input files) the default value is -# probably good enough. For larger projects a too small cache size can cause -# doxygen to be busy swapping symbols to and from disk most of the time -# causing a significant performance penalty. -# If the system has enough physical memory increasing the cache will improve the -# performance by keeping more symbols in memory. Note that the value works on -# a logarithmic scale so increasing the size by one will roughly double the -# memory usage. The cache size is given by this formula: -# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, -# corresponding to a cache size of 2^16 = 65536 symbols. - -SYMBOL_CACHE_SIZE = 0 - -# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be -# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given -# their name and scope. Since this can be an expensive process and often the -# same symbol appear multiple times in the code, doxygen keeps a cache of -# pre-resolved symbols. If the cache is too small doxygen will become slower. -# If the cache is too large, memory is wasted. The cache size is given by this -# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, -# corresponding to a cache size of 2^16 = 65536 symbols. +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 @@ -356,340 +391,390 @@ LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. -# Private class members and static file members will be hidden unless -# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. EXTRACT_ALL = YES -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class -# will be included in the documentation. +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will +# be included in the documentation. +# The default value is: NO. EXTRACT_PRIVATE = YES # If the EXTRACT_PACKAGE tag is set to YES all members with package or internal # scope will be included in the documentation. +# The default value is: NO. EXTRACT_PACKAGE = NO -# If the EXTRACT_STATIC tag is set to YES all static members of a file -# will be included in the documentation. +# If the EXTRACT_STATIC tag is set to YES all static members of a file will be +# included in the documentation. +# The default value is: NO. EXTRACT_STATIC = YES -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) -# defined locally in source files will be included in the documentation. -# If set to NO only classes defined in header files are included. +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. EXTRACT_LOCAL_CLASSES = YES -# This flag is only useful for Objective-C code. When set to YES local -# methods, which are defined in the implementation section but not in -# the interface are included in the documentation. -# If set to NO (the default) only methods in the interface are included. +# This flag is only useful for Objective-C code. When set to YES local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO only methods in the interface are +# included. +# The default value is: NO. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base -# name of the file that contains the anonymous namespace. By default -# anonymous namespaces are hidden. +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. EXTRACT_ANON_NSPACES = NO -# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all -# undocumented members of documented classes, files or namespaces. -# If set to NO (the default) these members will be included in the -# various overviews, but no documentation section is generated. -# This option has no effect if EXTRACT_ALL is enabled. +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. HIDE_UNDOC_MEMBERS = NO -# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. -# If set to NO (the default) these classes will be included in the various -# overviews. This option has no effect if EXTRACT_ALL is enabled. +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO these classes will be included in the various overviews. This option has +# no effect if EXTRACT_ALL is enabled. +# The default value is: NO. HIDE_UNDOC_CLASSES = NO -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all -# friend (class|struct|union) declarations. -# If set to NO (the default) these declarations will be included in the -# documentation. +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO these declarations will be +# included in the documentation. +# The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO -# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any -# documentation blocks found inside the body of a function. -# If set to NO (the default) these blocks will be appended to the -# function's detailed documentation block. +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. HIDE_IN_BODY_DOCS = NO -# The INTERNAL_DOCS tag determines if documentation -# that is typed after a \internal command is included. If the tag is set -# to NO (the default) then the documentation will be excluded. -# Set it to YES to include the internal documentation. +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. INTERNAL_DOCS = NO -# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate -# file names in lower-case letters. If set to YES upper-case letters are also +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. +# The default value is: system dependent. CASE_SENSE_NAMES = YES -# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen -# will show members with their full class and namespace scopes in the -# documentation. If set to YES the scope will be hidden. +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES the +# scope will be hidden. +# The default value is: NO. HIDE_SCOPE_NAMES = NO -# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen -# will put a list of the files that are included by a file in the documentation -# of that file. +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. SHOW_INCLUDE_FILES = YES -# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen -# will list include files with double quotes in the documentation -# rather than with sharp brackets. +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. FORCE_LOCAL_INCLUDES = NO -# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] -# is inserted in the documentation for inline members. +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. INLINE_INFO = YES -# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen -# will sort the (detailed) documentation of file and class members -# alphabetically by member name. If set to NO the members will appear in -# declaration order. +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. +# The default value is: YES. SORT_MEMBER_DOCS = YES -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the -# brief documentation of file, namespace and class members alphabetically -# by member name. If set to NO (the default) the members will appear in -# declaration order. +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. SORT_BRIEF_DOCS = NO -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen -# will sort the (brief and detailed) documentation of class members so that -# constructors and destructors are listed first. If set to NO (the default) -# the constructors will appear in the respective orders defined by -# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. -# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO -# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. SORT_MEMBERS_CTORS_1ST = NO -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the -# hierarchy of group names into alphabetical order. If set to NO (the default) -# the group names will appear in their defined order. +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. SORT_GROUP_NAMES = NO -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be -# sorted by fully-qualified names, including namespaces. If set to -# NO (the default), the class list will be sorted only by class name, -# not including the namespace part. +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the -# alphabetical list. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. SORT_BY_SCOPE_NAME = NO -# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to -# do proper type resolution of all parameters of a function it will reject a -# match between the prototype and the implementation of a member function even -# if there is only one candidate or it is obvious which candidate to choose -# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen -# will still accept a match between prototype and implementation in such cases. +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. STRICT_PROTO_MATCHING = NO -# The GENERATE_TODOLIST tag can be used to enable (YES) or -# disable (NO) the todo list. This list is created by putting \todo -# commands in the documentation. +# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the +# todo list. This list is created by putting \todo commands in the +# documentation. +# The default value is: YES. GENERATE_TODOLIST = YES -# The GENERATE_TESTLIST tag can be used to enable (YES) or -# disable (NO) the test list. This list is created by putting \test -# commands in the documentation. +# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the +# test list. This list is created by putting \test commands in the +# documentation. +# The default value is: YES. GENERATE_TESTLIST = YES -# The GENERATE_BUGLIST tag can be used to enable (YES) or -# disable (NO) the bug list. This list is created by putting \bug -# commands in the documentation. +# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. GENERATE_BUGLIST = YES -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or -# disable (NO) the deprecated list. This list is created by putting -# \deprecated commands in the documentation. +# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. GENERATE_DEPRECATEDLIST= YES -# The ENABLED_SECTIONS tag can be used to enable conditional -# documentation sections, marked by \if sectionname ... \endif. +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. ENABLED_SECTIONS = -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines -# the initial value of a variable or macro consists of for it to appear in -# the documentation. If the initializer consists of more lines than specified -# here it will be hidden. Use a value of 0 to hide initializers completely. -# The appearance of the initializer of individual variables and macros in the -# documentation can be controlled using \showinitializer or \hideinitializer -# command in the documentation regardless of this setting. +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 30 -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated -# at the bottom of the documentation of classes and structs. If set to YES the -# list will mention the files that were used to generate the documentation. +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES the list +# will mention the files that were used to generate the documentation. +# The default value is: YES. SHOW_USED_FILES = YES -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. -# This will remove the Files entry from the Quick Index and from the -# Folder Tree View (if specified). The default is YES. +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. SHOW_FILES = YES -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the -# Namespaces page. -# This will remove the Namespaces entry from the Quick Index -# and from the Folder Tree View (if specified). The default is YES. +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via -# popen()) the command , where is the value of -# the FILE_VERSION_FILTER tag, and is the name of an input file -# provided by doxygen. Whatever the program writes to standard output -# is used as the file version. See the manual for examples. +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file -# that represents doxygen's defaults, run doxygen with the -l option. -# You can optionally specify a file name after the option, if omitted -# DoxygenLayout.xml will be used as the name of the layout file. +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. LAYOUT_FILE = -# The CITE_BIB_FILES tag can be used to specify one or more bib files -# containing the references data. This must be a list of .bib files. The -# .bib extension is automatically appended if omitted. Using this command -# requires the bibtex tool to be installed. See also -# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style -# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this -# feature you need bibtex and perl available in the search path. +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. Do not use file names with spaces, bibtex cannot handle them. See +# also \cite for info how to create references. CITE_BIB_FILES = #--------------------------------------------------------------------------- -# configuration options related to warning and progress messages +# Configuration options related to warning and progress messages #--------------------------------------------------------------------------- -# The QUIET tag can be used to turn on/off the messages that are generated -# by doxygen. Possible values are YES and NO. If left blank NO is used. +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are -# generated by doxygen. Possible values are YES and NO. If left blank -# NO is used. +# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. WARNINGS = YES -# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings -# for undocumented members. If EXTRACT_ALL is set to YES then this flag will -# automatically be disabled. +# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. WARN_IF_UNDOCUMENTED = YES -# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some -# parameters in a documented function, or documenting parameters that -# don't exist or using markup commands wrongly. +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. WARN_IF_DOC_ERROR = YES -# The WARN_NO_PARAMDOC option can be enabled to get warnings for -# functions that are documented, but have no documentation for their parameters -# or return value. If set to NO (the default) doxygen will only warn about -# wrong or incomplete parameter documentation, but not about the absence of -# documentation. +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO doxygen will only warn about wrong or incomplete parameter +# documentation, but not about the absence of documentation. +# The default value is: NO. WARN_NO_PARAMDOC = NO -# The WARN_FORMAT tag determines the format of the warning messages that -# doxygen can produce. The string should contain the $file, $line, and $text -# tags, which will be replaced by the file and line number from which the -# warning originated and the warning text. Optionally the format may contain -# $version, which will be replaced by the version of the file (if it could -# be obtained via FILE_VERSION_FILTER) +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" -# The WARN_LOGFILE tag can be used to specify a file to which warning -# and error messages should be written. If left blank the output is written -# to stderr. +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). WARN_LOGFILE = #--------------------------------------------------------------------------- -# configuration options related to the input files +# Configuration options related to the input files #--------------------------------------------------------------------------- -# The INPUT tag can be used to specify the files and/or directories that contain -# documented source files. You may enter file names like "myfile.cpp" or -# directories like "/usr/src/myproject". Separate the files or directories -# with spaces. +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. +# Note: If this tag is empty the current directory is searched. INPUT = . # This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is -# also the default input encoding. Doxygen uses libiconv (or the iconv built -# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for -# the list of possible encodings. +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank the following patterns are tested: -# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh -# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py -# *.f90 *.f *.for *.vhd *.vhdl +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank the +# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, +# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, +# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, +# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, +# *.qsf, *.as and *.js. FILE_PATTERNS = -# The RECURSIVE tag can be used to turn specify whether or not subdirectories -# should be searched for input files as well. Possible values are YES and NO. -# If left blank NO is used. +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. +# # Note that relative paths are relative to the directory from which doxygen is # run. @@ -698,14 +783,16 @@ EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. +# The default value is: NO. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. Note that the wildcards are matched -# against the file with absolute path, so to exclude all test directories -# for example use the pattern */test/* +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* EXCLUDE_PATTERNS = */.git/* @@ -714,765 +801,1080 @@ EXCLUDE_PATTERNS = */.git/* # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* EXCLUDE_SYMBOLS = -# The EXAMPLE_PATH tag can be used to specify one or more files or -# directories that contain example code fragments that are included (see -# the \include command). +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank all files are included. +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude -# commands irrespective of the value of the RECURSIVE tag. -# Possible values are YES and NO. If left blank NO is used. +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. EXAMPLE_RECURSIVE = NO -# The IMAGE_PATH tag can be used to specify one or more files or -# directories that contain image that are included in the documentation (see -# the \image command). +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command , where -# is the value of the INPUT_FILTER tag, and is the name of an -# input file. Doxygen will then use the output that the filter program writes -# to standard output. -# If FILTER_PATTERNS is specified, this tag will be -# ignored. +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. -# Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. -# The filters are a list of the form: -# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further -# info on how filters are used. If FILTER_PATTERNS is empty or if -# non of the patterns match the file name, INPUT_FILTER is applied. +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will be used to filter the input files when producing source -# files to browse (i.e. when SOURCE_BROWSER is set to YES). +# INPUT_FILTER ) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file -# pattern. A pattern will override the setting for FILTER_PATTERN (if any) -# and it is also possible to disable source filtering for a specific pattern -# using *.ext= (so without naming a filter). This option only has effect when -# FILTER_SOURCE_FILES is enabled. +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. FILTER_SOURCE_PATTERNS = +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + #--------------------------------------------------------------------------- -# configuration options related to source browsing +# Configuration options related to source browsing #--------------------------------------------------------------------------- -# If the SOURCE_BROWSER tag is set to YES then a list of source files will -# be generated. Documented entities will be cross-referenced with these sources. -# Note: To get rid of all source code in the generated output, make sure also -# VERBATIM_HEADERS is set to NO. +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. SOURCE_BROWSER = NO -# Setting the INLINE_SOURCES tag to YES will include the body -# of functions and classes directly in the documentation. +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. INLINE_SOURCES = NO -# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct -# doxygen to hide any special comment blocks from generated source code -# fragments. Normal C, C++ and Fortran comments will always remain visible. +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. STRIP_CODE_COMMENTS = YES -# If the REFERENCED_BY_RELATION tag is set to YES -# then for each documented function all documented -# functions referencing it will be listed. +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. REFERENCED_BY_RELATION = NO -# If the REFERENCES_RELATION tag is set to YES -# then for each documented function all documented entities -# called/used by that function will be listed. +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. REFERENCES_RELATION = NO -# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) -# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from -# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will -# link to the source code. -# Otherwise they will link to the documentation. +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES, then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. REFERENCES_LINK_SOURCE = YES -# If the USE_HTAGS tag is set to YES then the references to source code -# will point to the HTML generated by the htags(1) tool instead of doxygen -# built-in source browser. The htags tool is part of GNU's global source -# tagging system (see http://www.gnu.org/software/global/global.html). You -# will need version 4.8.6 or higher. +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO -# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen -# will generate a verbatim copy of the header file for each class for -# which an include is specified. Set to NO to disable this. +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index +# Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index -# of all compounds will be generated. Enable this if the project -# contains a lot of classes, structs, unions or interfaces. +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. ALPHABETICAL_INDEX = NO -# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then -# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns -# in which this list will be split (can be a number in the range [1..20]) +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. COLS_IN_ALPHA_INDEX = 5 -# In case all classes in a project start with a common prefix, all -# classes will be put under the same header in the alphabetical index. -# The IGNORE_PREFIX tag can be used to specify one or more prefixes that -# should be ignored while generating the index headers. +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = #--------------------------------------------------------------------------- -# configuration options related to the HTML output +# Configuration options related to the HTML output #--------------------------------------------------------------------------- -# If the GENERATE_HTML tag is set to YES (the default) Doxygen will -# generate HTML output. +# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output +# The default value is: YES. GENERATE_HTML = YES -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `html' will be used as the default path. +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = html -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for -# each generated HTML page (for example: .htm,.php,.asp). If it is left blank -# doxygen will generate files with .html extension. +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html -# The HTML_HEADER tag can be used to specify a personal HTML header for -# each generated HTML page. If it is left blank doxygen will generate a -# standard header. Note that when using a custom header you are responsible -# for the proper inclusion of any scripts and style sheets that doxygen -# needs, which is dependent on the configuration options used. -# It is advised to generate a default header using "doxygen -w html -# header.html footer.html stylesheet.css YourConfigFile" and then modify -# that header. Note that the header is subject to change so you typically -# have to redo this when upgrading to a newer version of doxygen or when -# changing the value of configuration settings such as GENERATE_TREEVIEW! +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = -# The HTML_FOOTER tag can be used to specify a personal HTML footer for -# each generated HTML page. If it is left blank doxygen will generate a -# standard footer. +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading -# style sheet that is used by each HTML page. It can be used to -# fine-tune the look of the HTML output. If left blank doxygen will -# generate a default style sheet. Note that it is recommended to use -# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this -# tag will in the future become obsolete. +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_STYLESHEET = -# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional -# user-defined cascading style sheet that is included after the standard -# style sheets created by doxygen. Using this option one can overrule -# certain style aspects. This is preferred over using HTML_STYLESHEET -# since it does not replace the standard style sheet and is therefor more -# robust against future updates. Doxygen will copy the style sheet file to -# the output directory. +# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- +# defined cascading style sheet that is included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefor more robust against future updates. +# Doxygen will copy the style sheet file to the output directory. For an example +# see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the -# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these -# files. In the HTML_STYLESHEET file, use the file name only. Also note that -# the files will be copied as-is; there are no commands or markers available. +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_FILES = -# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. -# Doxygen will adjust the colors in the style sheet and background images -# according to this color. Hue is specified as an angle on a colorwheel, -# see http://en.wikipedia.org/wiki/Hue for more information. -# For instance the value 0 represents red, 60 is yellow, 120 is green, -# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. -# The allowed range is 0 to 359. +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the stylesheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_HUE = 220 -# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of -# the colors in the HTML output. For a value of 0 the output will use -# grayscales only. A value of 255 will produce the most vivid colors. +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_SAT = 100 -# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to -# the luminance component of the colors in the HTML output. Values below -# 100 gradually make the output lighter, whereas values above 100 make -# the output darker. The value divided by 100 is the actual gamma applied, -# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, -# and 100 does not change the gamma. +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting -# this to NO can help when comparing the output of multiple runs. +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = NO -# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of -# entries shown in the various tree structured indices initially; the user -# can expand and collapse entries dynamically later on. Doxygen will expand -# the tree to such a level that at most the specified number of entries are -# visible (unless a fully collapsed tree already exceeds this amount). -# So setting the number of entries 1 will produce a full collapsed tree by -# default. 0 is a special value representing an infinite number of entries -# and will result in a full expanded tree by default. +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_INDEX_NUM_ENTRIES = 100 -# If the GENERATE_DOCSET tag is set to YES, additional index files -# will be generated that can be used as input for Apple's Xcode 3 -# integrated development environment, introduced with OSX 10.5 (Leopard). -# To create a documentation set, doxygen will generate a Makefile in the -# HTML output directory. Running make will produce the docset in that -# directory and running "make install" will install the docset in -# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find -# it at startup. -# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO -# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the -# feed. A documentation feed provides an umbrella under which multiple -# documentation sets from a single provider (such as a company or product suite) -# can be grouped. +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "Doxygen generated docs" -# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that -# should uniquely identify the documentation set bundle. This should be a -# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen -# will append .docset to the name. +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.doxygen.Project -# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely -# identify the documentation publisher. This should be a reverse domain-name -# style string, e.g. com.mycompany.MyDocSet.documentation. +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_ID = org.doxygen.Publisher -# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_NAME = Publisher -# If the GENERATE_HTMLHELP tag is set to YES, additional index files -# will be generated that can be used as input for tools like the -# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) -# of the generated HTML documentation. +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_HTMLHELP = NO -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can -# be used to specify the file name of the resulting .chm file. You -# can add a path in front of the file if the result should not be +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be # written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_FILE = -# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can -# be used to specify the location (absolute path including file name) of -# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run -# the HTML help compiler on the generated index.hhp. +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler ( hhc.exe). If non-empty +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = -# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag -# controls if a separate .chi index file is generated (YES) or that -# it should be included in the master .chm file (NO). +# The GENERATE_CHI flag controls if a separate .chi index file is generated ( +# YES) or that it should be included in the master .chm file ( NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING -# is used to encode HtmlHelp index (hhk), content (hhc) and project file -# content. +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = -# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag -# controls whether a binary table of contents is generated (YES) or a -# normal table of contents (NO) in the .chm file. +# The BINARY_TOC flag controls whether a binary table of contents is generated ( +# YES) or a normal table of contents ( NO) in the .chm file. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO -# The TOC_EXPAND flag can be set to YES to add extra items for group members -# to the contents of the HTML help documentation and to the tree view. +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and -# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated -# that can be used as input for Qt's qhelpgenerator to generate a -# Qt Compressed Help (.qch) of the generated HTML documentation. +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = NO -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can -# be used to specify the file name of the resulting .qch file. -# The path specified is relative to the HTML output folder. +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. QCH_FILE = -# The QHP_NAMESPACE tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#namespace +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = org.doxygen.Project -# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#virtual-folders +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = doc -# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to -# add. For more information please see -# http://doc.trolltech.com/qthelpproject.html#custom-filters +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = -# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the -# custom filter to add. For more information please see -# -# Qt Help Project / Custom Filters. +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this -# project's -# filter section matches. -# -# Qt Help Project / Filter Attributes. +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = -# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can -# be used to specify the location of Qt's qhelpgenerator. -# If non-empty doxygen will try to run qhelpgenerator on the generated -# .qhp file. +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = -# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files -# will be generated, which together with the HTML files, form an Eclipse help -# plugin. To install this plugin and make it available under the help contents -# menu in Eclipse, the contents of the directory containing the HTML and XML -# files needs to be copied into the plugins directory of eclipse. The name of -# the directory within the plugins directory should be the same as -# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before -# the help appears. +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = NO -# A unique identifier for the eclipse help plugin. When installing the plugin -# the directory name containing the HTML and XML files should also have -# this name. +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = org.doxygen.Project -# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) -# at top of each HTML page. The value NO (the default) enables the index and -# the value YES disables it. Since the tabs have the same information as the -# navigation tree you can set this option to NO if you already set -# GENERATE_TREEVIEW to YES. +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. -# If the tag value is set to YES, a side panel will be generated -# containing a tree-like index structure (just like the one that -# is generated for HTML Help). For this to work a browser that supports -# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). -# Windows users are probably better off using the HTML help feature. -# Since the tree basically has the same information as the tab index you -# could consider to set DISABLE_INDEX to NO when enabling this option. +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NO -# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values -# (range [0,1..20]) that doxygen will group on one line in the generated HTML -# documentation. Note that a value of 0 will completely suppress the enum -# values from appearing in the overview section. +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. ENUM_VALUES_PER_LINE = 4 -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be -# used to set the initial width (in pixels) of the frame in which the tree -# is shown. +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 250 -# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open -# links to external symbols imported via tag files in a separate window. +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO -# Use this tag to change the font size of Latex formulas included -# as images in the HTML documentation. The default is 10. Note that -# when you change the font size after a successful doxygen run you need -# to manually remove any form_*.png images from the HTML output directory -# to force them to be regenerated. +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are -# not supported properly for IE 6.0, but are supported on all modern browsers. -# Note that when changing this option you need to delete any form_*.png files -# in the HTML output before the changes have effect. +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_TRANSPARENT = YES -# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax -# (see http://www.mathjax.org) which uses client side Javascript for the -# rendering instead of using prerendered bitmaps. Use this if you do not -# have LaTeX installed or if you want to formulas look prettier in the HTML -# output. When enabled you may also need to install MathJax separately and -# configure the path to it using the MATHJAX_RELPATH option. +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using prerendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. USE_MATHJAX = NO -# When MathJax is enabled you need to specify the location relative to the -# HTML output directory using the MATHJAX_RELPATH option. The destination -# directory should contain the MathJax.js script. For instance, if the mathjax -# directory is located at the same level as the HTML output directory, then -# MATHJAX_RELPATH should be ../mathjax. The default value points to -# the MathJax Content Delivery Network so you can quickly see the result without -# installing MathJax. -# However, it is strongly recommended to install a local -# copy of MathJax from http://www.mathjax.org before deployment. +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest -# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension -# names that should be enabled during MathJax rendering. +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = -# When the SEARCHENGINE tag is enabled doxygen will generate a search box -# for the HTML output. The underlying search engine uses javascript -# and DHTML and should work on any modern browser. Note that when using -# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets -# (GENERATE_DOCSET) there is already a search function so this one should -# typically be disabled. For large projects the javascript based search engine -# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /