From 62ccca1427a9fe3787cf002bff6102d62d683a0a Mon Sep 17 00:00:00 2001 From: Thulinma Date: Wed, 6 Apr 2011 03:53:30 +0200 Subject: [PATCH] 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); }