--- /dev/null
+[general]
+upstream_branch = upstream
+upstream_tag = ${upstreamversion}
+++ /dev/null
-/*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2018 Haivision Systems Inc.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- */
-
-#include <cstring>
-#include <chrono>
-#include <iostream>
-#include <iomanip>
-#include <sstream>
-#include <utility>
-#include <memory>
-
-#include "srt.h" // Required for SRT_SYNC_CLOCK_* definitions.
-#include "apputil.hpp"
-#include "netinet_any.h"
-#include "srt_compat.h"
-
-using namespace std;
-using namespace srt;
-
-
-// NOTE: MINGW currently does not include support for inet_pton(). See
-// http://mingw.5.n7.nabble.com/Win32API-request-for-new-functions-td22029.html
-// Even if it did support inet_pton(), it is only available on Windows Vista
-// and later. Since we need to support WindowsXP and later in ORTHRUS. Many
-// customers still use it, we will need to implement using something like
-// WSAStringToAddress() which is available on Windows95 and later.
-// Support for IPv6 was added on WindowsXP SP1.
-// Header: winsock2.h
-// Implementation: ws2_32.dll
-// See:
-// https://msdn.microsoft.com/en-us/library/windows/desktop/ms742214(v=vs.85).aspx
-// http://www.winsocketdotnetworkprogramming.com/winsock2programming/winsock2advancedInternet3b.html
-#if defined(_WIN32) && !defined(HAVE_INET_PTON)
-namespace // Prevent conflict in case when still defined
-{
-int inet_pton(int af, const char * src, void * dst)
-{
- struct sockaddr_storage ss;
- int ssSize = sizeof(ss);
- char srcCopy[INET6_ADDRSTRLEN + 1];
-
- ZeroMemory(&ss, sizeof(ss));
-
- // work around non-const API
- strncpy(srcCopy, src, INET6_ADDRSTRLEN + 1);
- srcCopy[INET6_ADDRSTRLEN] = '\0';
-
- if (WSAStringToAddress(
- srcCopy, af, NULL, (struct sockaddr *)&ss, &ssSize) != 0)
- {
- return 0;
- }
-
- switch (af)
- {
- case AF_INET :
- {
- *(struct in_addr *)dst = ((struct sockaddr_in *)&ss)->sin_addr;
- return 1;
- }
- case AF_INET6 :
- {
- *(struct in6_addr *)dst = ((struct sockaddr_in6 *)&ss)->sin6_addr;
- return 1;
- }
- default :
- {
- // No-Op
- }
- }
-
- return 0;
-}
-}
-#endif // _WIN32 && !HAVE_INET_PTON
-
-sockaddr_any CreateAddr(const string& name, unsigned short port, int pref_family)
-{
- // Handle empty name.
- // If family is specified, empty string resolves to ANY of that family.
- // If not, it resolves to IPv4 ANY (to specify IPv6 any, use [::]).
- if (name == "")
- {
- sockaddr_any result(pref_family == AF_INET6 ? pref_family : AF_INET);
- result.hport(port);
- return result;
- }
-
- bool first6 = pref_family != AF_INET;
- int families[2] = {AF_INET6, AF_INET};
- if (!first6)
- {
- families[0] = AF_INET;
- families[1] = AF_INET6;
- }
-
- for (int i = 0; i < 2; ++i)
- {
- int family = families[i];
- sockaddr_any result (family);
-
- // Try to resolve the name by pton first
- if (inet_pton(family, name.c_str(), result.get_addr()) == 1)
- {
- result.hport(port); // same addr location in ipv4 and ipv6
- return result;
- }
- }
-
- // If not, try to resolve by getaddrinfo
- // This time, use the exact value of pref_family
-
- sockaddr_any result;
- addrinfo fo = {
- 0,
- pref_family,
- 0, 0,
- 0, 0,
- NULL, NULL
- };
-
- addrinfo* val = nullptr;
- int erc = getaddrinfo(name.c_str(), nullptr, &fo, &val);
- if (erc == 0)
- {
- result.set(val->ai_addr);
- result.len = result.size();
- result.hport(port); // same addr location in ipv4 and ipv6
- }
- freeaddrinfo(val);
-
- return result;
-}
-
-string Join(const vector<string>& in, string sep)
-{
- if ( in.empty() )
- return "";
-
- ostringstream os;
-
- os << in[0];
- for (auto i = in.begin()+1; i != in.end(); ++i)
- os << sep << *i;
- return os.str();
-}
-
-// OPTION LIBRARY
-
-OptionScheme::Args OptionName::DetermineTypeFromHelpText(const std::string& helptext)
-{
- if (helptext.empty())
- return OptionScheme::ARG_NONE;
-
- if (helptext[0] == '<')
- {
- // If the argument is <one-argument>, then it's ARG_NONE.
- // If it's <multiple-arguments...>, then it's ARG_VAR.
- // When closing angle bracket isn't found, fallback to ARG_ONE.
- size_t pos = helptext.find('>');
- if (pos == std::string::npos)
- return OptionScheme::ARG_ONE; // mistake, but acceptable
-
- if (pos >= 4 && helptext.substr(pos-3, 4) == "...>")
- return OptionScheme::ARG_VAR;
-
- // We have < and > without ..., simply one argument
- return OptionScheme::ARG_ONE;
- }
-
- if (helptext[0] == '[')
- {
- // Argument in [] means it is optional; in this case
- // you should state that the argument can be given or not.
- return OptionScheme::ARG_VAR;
- }
-
- // Also as fallback
- return OptionScheme::ARG_NONE;
-}
-
-options_t ProcessOptions(char* const* argv, int argc, std::vector<OptionScheme> scheme)
-{
- using namespace std;
-
- string current_key;
- string extra_arg;
- size_t vals = 0;
- OptionScheme::Args type = OptionScheme::ARG_VAR; // This is for no-option-yet or consumed
- map<string, vector<string>> params;
- bool moreoptions = true;
-
- for (char* const* p = argv+1; p != argv+argc; ++p)
- {
- const char* a = *p;
- // cout << "*D ARG: '" << a << "'\n";
- bool isoption = false;
- if (a[0] == '-')
- {
- isoption = true;
- // If a[0] isn't NUL - because it is dash - then
- // we can safely check a[1].
- // An expression starting with a dash is not
- // an option marker if it is a single dash or
- // a negative number.
- if (!a[1] || isdigit(a[1]))
- isoption = false;
- }
-
- if (moreoptions && isoption)
- {
- bool arg_specified = false;
- size_t seppos; // (see goto, it would jump over initialization)
- current_key = a+1;
- if ( current_key == "-" )
- {
- // The -- argument terminates the options.
- // The default key is restored to empty so that
- // it collects now all arguments under the empty key
- // (not-option-assigned argument).
- moreoptions = false;
- goto EndOfArgs;
- }
-
- // Maintain the backward compatibility with argument specified after :
- // or with one string separated by space inside.
- seppos = current_key.find(':');
- if (seppos == string::npos)
- seppos = current_key.find(' ');
- if (seppos != string::npos)
- {
- // Old option specification.
- extra_arg = current_key.substr(seppos + 1);
- current_key = current_key.substr(0, 0 + seppos);
- arg_specified = true; // Prevent eating args from option list
- }
-
- params[current_key].clear();
- vals = 0;
-
- if (extra_arg != "")
- {
- params[current_key].push_back(extra_arg);
- ++vals;
- extra_arg.clear();
- }
-
- // Find the key in the scheme. If not found, treat it as ARG_NONE.
- for (const auto& s: scheme)
- {
- if (s.names().count(current_key))
- {
- // cout << "*D found '" << current_key << "' in scheme type=" << int(s.type) << endl;
- // If argument was specified using the old way, like
- // -v:0 or "-v 0", then consider the argument specified and
- // treat further arguments as either no-option arguments or
- // new options.
- if (s.type == OptionScheme::ARG_NONE || arg_specified)
- {
- // Anyway, consider it already processed.
- break;
- }
- type = s.type;
-
- if ( vals == 1 && type == OptionScheme::ARG_ONE )
- {
- // Argument for one-arg option already consumed,
- // so set to free args.
- goto EndOfArgs;
- }
- goto Found;
- }
-
- }
- // Not found: set ARG_NONE.
- // cout << "*D KEY '" << current_key << "' assumed type NONE\n";
-EndOfArgs:
- type = OptionScheme::ARG_VAR;
- current_key = "";
-Found:
- continue;
- }
-
- // Collected a value - check if full
- // cout << "*D COLLECTING '" << a << "' for key '" << current_key << "' (" << vals << " so far)\n";
- params[current_key].push_back(a);
- ++vals;
- if ( vals == 1 && type == OptionScheme::ARG_ONE )
- {
- // cout << "*D KEY TYPE ONE - resetting to empty key\n";
- // Reset the key to "default one".
- current_key = "";
- vals = 0;
- type = OptionScheme::ARG_VAR;
- }
- else
- {
- // cout << "*D KEY type VAR - still collecting until the end of options or next option.\n";
- }
- }
-
- return params;
-}
-
-string OptionHelpItem(const OptionName& o)
-{
- string out = "\t-" + o.main_name;
- string hlp = o.helptext;
- string prefix;
-
- if (hlp == "")
- {
- hlp = " (Undocumented)";
- }
- else if (hlp[0] != ' ')
- {
- size_t end = string::npos;
- if (hlp[0] == '<')
- {
- end = hlp.find('>');
- }
- else if (hlp[0] == '[')
- {
- end = hlp.find(']');
- }
-
- if (end != string::npos)
- {
- ++end;
- }
- else
- {
- end = hlp.find(' ');
- }
-
- if (end != string::npos)
- {
- prefix = hlp.substr(0, end);
- //while (hlp[end] == ' ')
- // ++end;
- hlp = hlp.substr(end);
- out += " " + prefix;
- }
- }
-
- out += " -" + hlp;
- return out;
-}
-
-const char* SRTClockTypeStr()
-{
- const int clock_type = srt_clock_type();
-
- switch (clock_type)
- {
- case SRT_SYNC_CLOCK_STDCXX_STEADY:
- return "CXX11_STEADY";
- case SRT_SYNC_CLOCK_GETTIME_MONOTONIC:
- return "GETTIME_MONOTONIC";
- case SRT_SYNC_CLOCK_WINQPC:
- return "WIN_QPC";
- case SRT_SYNC_CLOCK_MACH_ABSTIME:
- return "MACH_ABSTIME";
- case SRT_SYNC_CLOCK_POSIX_GETTIMEOFDAY:
- return "POSIX_GETTIMEOFDAY";
- default:
- break;
- }
-
- return "UNKNOWN VALUE";
-}
-
-void PrintLibVersion()
-{
- cerr << "Built with SRT Library version: " << SRT_VERSION << endl;
- const uint32_t srtver = srt_getversion();
- const int major = srtver / 0x10000;
- const int minor = (srtver / 0x100) % 0x100;
- const int patch = srtver % 0x100;
- cerr << "SRT Library version: " << major << "." << minor << "." << patch << ", clock type: " << SRTClockTypeStr() << endl;
-}
+++ /dev/null
-/*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2018 Haivision Systems Inc.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- */
-
-
-#ifndef INC_SRT_APPCOMMON_H
-#define INC_SRT_APPCOMMON_H
-
-#include <string>
-#include <map>
-#include <set>
-#include <vector>
-#include <memory>
-
-#include "netinet_any.h"
-#include "utilities.h"
-
-#if _WIN32
-
-// Keep this below commented out.
-// This is for a case when you need cpp debugging on Windows.
-//#ifdef _WINSOCKAPI_
-//#error "You include <winsock.h> somewhere, remove it. It causes conflicts"
-//#endif
-
-#include <winsock2.h>
-#include <windows.h>
-#include <ws2tcpip.h>
-// WIN32 API does not have sleep() and usleep(), Although MINGW does.
-#ifdef __MINGW32__
-#include <unistd.h>
-#else
-extern "C" inline int sleep(int seconds) { Sleep(seconds * 1000); return 0; }
-#endif
-
-inline bool SysInitializeNetwork()
-{
- WORD wVersionRequested = MAKEWORD(2, 2);
- WSADATA wsaData;
- return WSAStartup(wVersionRequested, &wsaData) == 0;
-}
-
-inline void SysCleanupNetwork()
-{
- WSACleanup();
-}
-
-#else
-#include <netdb.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <unistd.h>
-
-// Fixes Android build on NDK r16b and earlier.
-#if defined(__ANDROID__) && (__ANDROID__ == 1)
- #include <android/ndk-version.h>
- #if !defined(__NDK_MAJOR__) || (__NDK_MAJOR__ <= 16)
- struct ip_mreq_sourceFIXED {
- struct in_addr imr_multiaddr;
- struct in_addr imr_interface;
- struct in_addr imr_sourceaddr;
- };
- #define ip_mreq_source ip_mreq_sourceFIXED
- #endif
-#endif
-
-// Nothing needs to be done on POSIX; this is a Windows problem.
-inline bool SysInitializeNetwork() {return true;}
-inline void SysCleanupNetwork() {}
-
-#endif
-
-#ifdef _WIN32
-inline int SysError() { return ::GetLastError(); }
-const int SysAGAIN = WSAEWOULDBLOCK;
-#else
-inline int SysError() { return errno; }
-const int SysAGAIN = EAGAIN;
-#endif
-
-srt::sockaddr_any CreateAddr(const std::string& name, unsigned short port = 0, int pref_family = AF_UNSPEC);
-std::string Join(const std::vector<std::string>& in, std::string sep);
-
-template <class VarType, class ValType>
-struct OnReturnSetter
-{
- VarType& var;
- ValType value;
-
- OnReturnSetter(VarType& target, ValType v): var(target), value(v) {}
- ~OnReturnSetter() { var = value; }
-};
-
-template <class VarType, class ValType>
-OnReturnSetter<VarType, ValType> OnReturnSet(VarType& target, ValType v)
-{ return OnReturnSetter<VarType, ValType>(target, v); }
-
-// ---- OPTIONS MODULE
-
-inline bool CheckTrue(const std::vector<std::string>& in)
-{
- if (in.empty())
- return true;
-
- const std::set<std::string> false_vals = { "0", "no", "off", "false" };
- if (false_vals.count(in[0]))
- return false;
-
- return true;
-
- //if (in[0] != "false" && in[0] != "off")
- // return true;
-
- //return false;
-}
-
-template<class Number>
-static inline Number StrToNumber(const std::string& )
-{
- typename Number::incorrect_version wrong = Number::incorrect_version;
- return Number();
-}
-
-#define STON(type, function) \
-template<> inline type StrToNumber(const std::string& s) { return function (s, 0, 0); }
-
-STON(int, stoi);
-STON(unsigned long, stoul);
-STON(unsigned int, stoul);
-STON(long long, stoll);
-STON(unsigned long long, stoull);
-
-#undef STON
-
-typedef std::map<std::string, std::vector<std::string>> options_t;
-
-struct OutList
-{
- typedef std::vector<std::string> type;
- static type process(const options_t::mapped_type& i) { return i; }
-};
-
-struct OutString
-{
- typedef std::string type;
- static type process(const options_t::mapped_type& i) { return Join(i, " "); }
-};
-
-struct NumberAutoConvert
-{
- std::string value;
-
- NumberAutoConvert(): NumberAutoConvert("") {}
- NumberAutoConvert(const std::string& arg): NumberAutoConvert(arg.c_str()) {}
- NumberAutoConvert(const char* arg): value(arg)
- {
- if (value.empty())
- value = "0"; // Must convert to a default 0 number
- }
-
- template<class Number>
- operator Number()
- {
- return StrToNumber<Number>(value);
- }
-};
-
-struct OutNumber
-{
- typedef NumberAutoConvert type;
- static type process(const options_t::mapped_type& i)
- {
- // Numbers can't be joined, use the "last overrides" rule.
- if (i.empty())
- return {"0"};
-
- return type { i.back() };
- }
-};
-
-template <class Number>
-struct OutNumberAs
-{
- typedef Number type;
- static type process(const options_t::mapped_type& i)
- {
- return OutNumber::process(i);
- }
-};
-
-
-struct OutBool
-{
- typedef bool type;
- static type process(const options_t::mapped_type& i) { return CheckTrue(i); }
-};
-
-struct OptionName;
-
-struct OptionScheme
-{
- const OptionName* pid;
- enum Args { ARG_NONE, ARG_ONE, ARG_VAR } type;
-
- OptionScheme(const OptionScheme&) = default;
- OptionScheme(OptionScheme&& src)
- : pid(src.pid)
- , type(src.type)
- {
- }
-
- OptionScheme(const OptionName& id, Args tp);
-
- const std::set<std::string>& names() const;
-};
-
-struct OptionName
-{
- std::string helptext;
- std::string main_name;
- std::set<std::string> names;
-
- template <class... Args>
- OptionName(std::string ht, std::string first, Args... rest)
- : helptext(ht), main_name(first),
- names {first, rest...}
- {
- }
-
- template <class... Args>
- OptionName(std::vector<OptionScheme>& sc, OptionScheme::Args type,
- std::string ht, std::string first, Args... rest)
- : helptext(ht), main_name(first),
- names {first, rest...}
- {
- sc.push_back(OptionScheme(*this, type));
- }
-
- template <class... Args>
- OptionName(std::vector<OptionScheme>& sc,
- std::string ht, std::string first, Args... rest)
- : helptext(ht), main_name(first),
- names {first, rest...}
- {
- OptionScheme::Args type = DetermineTypeFromHelpText(ht);
- sc.push_back(OptionScheme(*this, type));
- }
-
- OptionName(std::initializer_list<std::string> args): main_name(*args.begin()), names(args) {}
-
- operator std::set<std::string>() { return names; }
- operator const std::set<std::string>() const { return names; }
-
-private:
- static OptionScheme::Args DetermineTypeFromHelpText(const std::string& helptext);
-};
-
-inline OptionScheme::OptionScheme(const OptionName& id, Args tp): pid(&id), type(tp) {}
-inline const std::set<std::string>& OptionScheme::names() const { return pid->names; }
-
-template <class OutType, class OutValue> inline
-typename OutType::type Option(const options_t&, OutValue deflt=OutValue()) { return deflt; }
-
-template <class OutType, class OutValue, class... Args> inline
-typename OutType::type Option(const options_t& options, OutValue deflt, std::string key, Args... further_keys)
-{
- auto i = options.find(key);
- if ( i == options.end() )
- return Option<OutType>(options, deflt, further_keys...);
- return OutType::process(i->second);
-}
-
-template<typename TrapType>
-struct OptionTrapType
-{
- static TrapType pass(TrapType v) { return v; }
-};
-
-template<>
-struct OptionTrapType<const char*>
-{
- static std::string pass(const char* v) { return v; }
-};
-
-template <class OutType, class OutValue> inline
-typename OutType::type Option(const options_t& options, OutValue deflt, const OptionName& oname)
-{
- (void)OptionTrapType<OutValue>::pass(deflt);
- for (auto key: oname.names)
- {
- auto i = options.find(key);
- if ( i != options.end() )
- {
- return OutType::process(i->second);
- }
- }
- return deflt;
-}
-
-template <class OutType> inline
-typename OutType::type Option(const options_t& options, const OptionName& oname)
-{
- typedef typename OutType::type out_t;
- for (auto key: oname.names)
- {
- auto i = options.find(key);
- if ( i != options.end() )
- {
- return OutType::process(i->second);
- }
- }
- return out_t();
-}
-
-inline bool OptionPresent(const options_t& options, const std::set<std::string>& keys)
-{
- for (auto key: keys)
- {
- auto i = options.find(key);
- if ( i != options.end() )
- return true;
- }
- return false;
-}
-
-options_t ProcessOptions(char* const* argv, int argc, std::vector<OptionScheme> scheme);
-std::string OptionHelpItem(const OptionName& o);
-
-const char* SRTClockTypeStr();
-void PrintLibVersion();
-
-#endif // INC_SRT_APPCOMMON_H
+++ /dev/null
-/*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2018 Haivision Systems Inc.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- */
-
-#include <map>
-#include <vector>
-#include <string>
-#include <iterator>
-#include <algorithm>
-#include <cctype>
-#include "logsupport.hpp"
-#include "../srtcore/srt.h"
-#include "../srtcore/utilities.h"
-
-using namespace std;
-
-// This is based on codes taken from <sys/syslog.h>
-// This is POSIX standard, so it's not going to change.
-// Haivision standard only adds one more severity below
-// DEBUG named DEBUG_TRACE to satisfy all possible needs.
-
-map<string, int> srt_level_names
-{
- { "alert", LOG_ALERT },
- { "crit", LOG_CRIT },
- { "debug", LOG_DEBUG },
- { "emerg", LOG_EMERG },
- { "err", LOG_ERR },
- { "error", LOG_ERR }, /* DEPRECATED */
- { "fatal", LOG_CRIT }, // XXX Added for SRT
- { "info", LOG_INFO },
- // WTF? Undefined symbol? { "none", INTERNAL_NOPRI }, /* INTERNAL */
- { "notice", LOG_NOTICE },
- { "note", LOG_NOTICE }, // XXX Added for SRT
- { "panic", LOG_EMERG }, /* DEPRECATED */
- { "warn", LOG_WARNING }, /* DEPRECATED */
- { "warning", LOG_WARNING },
- //{ "", -1 }
-};
-
-
-
-srt_logging::LogLevel::type SrtParseLogLevel(string level)
-{
- using namespace srt_logging;
-
- if ( level.empty() )
- return LogLevel::fatal;
-
- if ( isdigit(level[0]) )
- {
- long lev = strtol(level.c_str(), 0, 10);
- if ( lev >= SRT_LOG_LEVEL_MIN && lev <= SRT_LOG_LEVEL_MAX )
- return LogLevel::type(lev);
-
- cerr << "ERROR: Invalid loglevel number: " << level << " - fallback to FATAL\n";
- return LogLevel::fatal;
- }
-
- int (*ToLower)(int) = &std::tolower; // manual overload resolution
- transform(level.begin(), level.end(), level.begin(), ToLower);
-
- auto i = srt_level_names.find(level);
- if ( i == srt_level_names.end() )
- {
- cerr << "ERROR: Invalid loglevel spec: " << level << " - fallback to FATAL\n";
- return LogLevel::fatal;
- }
-
- return LogLevel::type(i->second);
-}
-
-struct ToLowerFormat
-{
- char operator()(char in)
- {
- if (islower(in))
- return in;
- if (isupper(in))
- return tolower(in);
- if (in == '_')
- return '-';
-
- throw std::invalid_argument("Wrong FA name - please check the definition in scripts/generate-logging-defs.tcl file");
- }
-};
-
-void LogFANames::Install(string upname, int value)
-{
- string id;
- transform(upname.begin(), upname.end(), back_inserter(id), ToLowerFormat());
- namemap[id] = value;
-}
-
-// See logsupport_appdefs.cpp for log FA definitions
-LogFANames srt_transmit_logfa_names;
-
-const map<string, int> SrtLogFAList()
-{
- return srt_transmit_logfa_names.namemap;
-}
-
-set<srt_logging::LogFA> SrtParseLogFA(string fa, set<string>* punknown)
-{
- using namespace srt_logging;
-
- set<LogFA> fas;
-
- // The split algo won't work on empty string.
- if ( fa == "" )
- return fas;
-
- auto& names = srt_transmit_logfa_names.namemap;
-
- if ( fa == "all" )
- {
- for (auto entry: names)
- {
- // Skip "general", it's always on
- if (entry.first == "general")
- continue;
- fas.insert(entry.second);
- }
- return fas;
- }
-
- int (*ToLower)(int) = &std::tolower;
- transform(fa.begin(), fa.end(), fa.begin(), ToLower);
-
- vector<string> xfas;
- size_t pos = 0, ppos = 0;
- for (;;)
- {
- if ( fa[pos] != ',' )
- {
- ++pos;
- if ( pos < fa.size() )
- continue;
- }
- size_t n = pos - ppos;
- if ( n != 0 )
- xfas.push_back(fa.substr(ppos, n));
- ++pos;
- if ( pos >= fa.size() )
- break;
- ppos = pos;
- }
-
- for (size_t i = 0; i < xfas.size(); ++i)
- {
- fa = xfas[i];
- int* pfa = map_getp(names, fa);
- if (!pfa)
- {
- if (punknown)
- punknown->insert(fa); // If requested, add it back silently
- else
- cerr << "ERROR: Invalid log functional area spec: '" << fa << "' - skipping\n";
- continue;
- }
-
- fas.insert(*pfa);
- }
-
- return fas;
-}
-
-void ParseLogFASpec(const vector<string>& speclist, string& w_on, string& w_off)
-{
- std::ostringstream son, soff;
-
- for (auto& s: speclist)
- {
- string name;
- bool on = true;
- if (s[0] == '+')
- name = s.substr(1);
- else if (s[0] == '~')
- {
- name = s.substr(1);
- on = false;
- }
- else
- name = s;
-
- if (on)
- son << "," << name;
- else
- soff << "," << name;
- }
-
- const string& sons = son.str();
- const string& soffs = soff.str();
-
- w_on = sons.empty() ? string() : sons.substr(1);
- w_off = soffs.empty() ? string() : soffs.substr(1);
-}
-
-
+++ /dev/null
-/*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2018 Haivision Systems Inc.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- */
-
-#ifndef INC_SRT_LOGSUPPORT_HPP
-#define INC_SRT_LOGSUPPORT_HPP
-
-#include <string>
-#include <map>
-#include <vector>
-#include "../srtcore/srt.h"
-#include "../srtcore/logging_api.h"
-
-srt_logging::LogLevel::type SrtParseLogLevel(std::string level);
-std::set<srt_logging::LogFA> SrtParseLogFA(std::string fa, std::set<std::string>* punknown = nullptr);
-void ParseLogFASpec(const std::vector<std::string>& speclist, std::string& w_on, std::string& w_off);
-const std::map<std::string, int> SrtLogFAList();
-
-SRT_API extern std::map<std::string, int> srt_level_names;
-
-struct LogFANames
-{
- std::map<std::string, int> namemap;
- void Install(std::string upname, int value);
- LogFANames();
-};
-
-#endif
+++ /dev/null
-/*
- WARNING: Generated from ../scripts/generate-logging-defs.tcl
-
- DO NOT MODIFY.
-
- Copyright applies as per the generator script.
- */
-
-
-#include "logsupport.hpp"
-
-LogFANames::LogFANames()
-{
- Install("GENERAL", SRT_LOGFA_GENERAL);
- Install("SOCKMGMT", SRT_LOGFA_SOCKMGMT);
- Install("CONN", SRT_LOGFA_CONN);
- Install("XTIMER", SRT_LOGFA_XTIMER);
- Install("TSBPD", SRT_LOGFA_TSBPD);
- Install("RSRC", SRT_LOGFA_RSRC);
-
- Install("CONGEST", SRT_LOGFA_CONGEST);
- Install("PFILTER", SRT_LOGFA_PFILTER);
-
- Install("API_CTRL", SRT_LOGFA_API_CTRL);
-
- Install("QUE_CTRL", SRT_LOGFA_QUE_CTRL);
-
- Install("EPOLL_UPD", SRT_LOGFA_EPOLL_UPD);
-
- Install("API_RECV", SRT_LOGFA_API_RECV);
- Install("BUF_RECV", SRT_LOGFA_BUF_RECV);
- Install("QUE_RECV", SRT_LOGFA_QUE_RECV);
- Install("CHN_RECV", SRT_LOGFA_CHN_RECV);
- Install("GRP_RECV", SRT_LOGFA_GRP_RECV);
-
- Install("API_SEND", SRT_LOGFA_API_SEND);
- Install("BUF_SEND", SRT_LOGFA_BUF_SEND);
- Install("QUE_SEND", SRT_LOGFA_QUE_SEND);
- Install("CHN_SEND", SRT_LOGFA_CHN_SEND);
- Install("GRP_SEND", SRT_LOGFA_GRP_SEND);
-
- Install("INTERNAL", SRT_LOGFA_INTERNAL);
-
- Install("QUE_MGMT", SRT_LOGFA_QUE_MGMT);
- Install("CHN_MGMT", SRT_LOGFA_CHN_MGMT);
- Install("GRP_MGMT", SRT_LOGFA_GRP_MGMT);
- Install("EPOLL_API", SRT_LOGFA_EPOLL_API);
-
- Install("HAICRYPT", SRT_LOGFA_HAICRYPT);
- Install("APPLOG", SRT_LOGFA_APPLOG);
-}
+++ /dev/null
-/*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2018 Haivision Systems Inc.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- */
-
-#include "socketoptions.hpp"
-#include "verbose.hpp"
-
-using namespace std;
-
-
-extern const set<string> true_names = { "1", "yes", "on", "true" };
-extern const set<string> false_names = { "0", "no", "off", "false" };
-
-extern const std::map<std::string, int> enummap_transtype = {
- { "live", SRTT_LIVE },
- { "file", SRTT_FILE }
-};
-
-
-const char* const SocketOption::mode_names[3] = {
- "listener", "caller", "rendezvous"
-};
-
-SocketOption::Mode SrtInterpretMode(const string& modestr, const string& host, const string& adapter)
-{
- SocketOption::Mode mode = SocketOption::FAILURE;
-
- if (modestr == "client" || modestr == "caller")
- {
- mode = SocketOption::CALLER;
- }
- else if (modestr == "server" || modestr == "listener")
- {
- mode = SocketOption::LISTENER;
- }
- else if (modestr == "rendezvous")
- {
- mode = SocketOption::RENDEZVOUS;
- }
- else if (modestr == "default")
- {
- // Use the following convention:
- // 1. Server for source, Client for target
- // 2. If host is empty, then always server.
- if ( host == "" )
- mode = SocketOption::LISTENER;
- //else if ( !dir_output )
- //mode = "server";
- else
- {
- // Host is given, so check also "adapter"
- if (adapter != "")
- mode = SocketOption::RENDEZVOUS;
- else
- mode = SocketOption::CALLER;
- }
- }
- else
- {
- mode = SocketOption::FAILURE;
- }
-
- return mode;
-}
-
-SocketOption::Mode SrtConfigurePre(SRTSOCKET socket, string host, map<string, string> options, vector<string>* failures)
-{
- vector<string> dummy;
- vector<string>& fails = failures ? *failures : dummy;
-
- string modestr = "default", adapter;
-
- if (options.count("mode"))
- {
- modestr = options["mode"];
- }
-
- if (options.count("adapter"))
- {
- adapter = options["adapter"];
- }
-
- SocketOption::Mode mode = SrtInterpretMode(modestr, host, adapter);
- if (mode == SocketOption::FAILURE)
- {
- fails.push_back("mode");
- }
-
- if (options.count("linger"))
- {
- linger lin;
- lin.l_linger = stoi(options["linger"]);
- lin.l_onoff = lin.l_linger > 0 ? 1 : 0;
- srt_setsockopt(socket, SocketOption::PRE, SRTO_LINGER, &lin, sizeof(linger));
- }
-
-
- bool all_clear = true;
- for (const auto &o: srt_options)
- {
- if ( o.binding == SocketOption::PRE && options.count(o.name) )
- {
- string value = options.at(o.name);
- bool ok = o.apply<SocketOption::SRT>(socket, value);
- if ( !ok )
- {
- fails.push_back(o.name);
- all_clear = false;
- }
- }
- }
-
- return all_clear ? mode : SocketOption::FAILURE;
-}
-
-void SrtConfigurePost(SRTSOCKET socket, map<string, string> options, vector<string>* failures)
-{
- vector<string> dummy;
- vector<string>& fails = failures ? *failures : dummy;
-
- for (const auto &o: srt_options)
- {
- if ( o.binding == SocketOption::POST && options.count(o.name) )
- {
- string value = options.at(o.name);
- Verb() << "Setting option: " << o.name << " = " << value;
- bool ok = o.apply<SocketOption::SRT>(socket, value);
- if ( !ok )
- fails.push_back(o.name);
- }
- }
-}
-
+++ /dev/null
-/*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2018 Haivision Systems Inc.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- */
-
-#ifndef INC_SRT_SOCKETOPTIONS_HPP
-#define INC_SRT_SOCKETOPTIONS_HPP
-
-#include <string>
-#include <map>
-#include <set>
-#include <vector>
-#include "../srtcore/srt.h" // Devel path
-
-#ifdef _WIN32
-#include "winsock2.h"
-#endif
-
-struct OptionValue
-{
- std::string s;
- union {
- int i;
- int64_t l;
- bool b;
- };
-
- const void* value = nullptr;
- size_t size = 0;
-};
-
-extern const std::set<std::string> false_names, true_names;
-
-struct SocketOption
-{
- enum Type { STRING = 0, INT, INT64, BOOL, ENUM };
- enum Binding { PRE = 0, POST };
- enum Domain { SYSTEM, SRT };
- enum Mode {FAILURE = -1, LISTENER = 0, CALLER = 1, RENDEZVOUS = 2};
- static const char* const mode_names [3];
-
- std::string name;
- int protocol;
- int symbol;
- Binding binding;
- Type type;
- const std::map<std::string, int>* valmap;
-
- template <Domain D, typename Object = int>
- bool apply(Object socket, std::string value) const;
-
- template <Domain D, Type T, typename Object = int>
- bool applyt(Object socket, std::string value) const;
-
- template <Domain D, typename Object>
- static int setso(Object socket, int protocol, int symbol, const void* data, size_t size);
-
- template<Type T>
- bool extract(std::string value, OptionValue& val) const;
-};
-
-template<>
-inline int SocketOption::setso<SocketOption::SRT, int>(int socket, int /*ignored*/, int sym, const void* data, size_t size)
-{
- return srt_setsockopt(socket, 0, SRT_SOCKOPT(sym), data, (int) size);
-}
-
-#if ENABLE_BONDING
-template<>
-inline int SocketOption::setso<SocketOption::SRT, SRT_SOCKOPT_CONFIG*>(SRT_SOCKOPT_CONFIG* obj, int /*ignored*/, int sym, const void* data, size_t size)
-{
- return srt_config_add(obj, SRT_SOCKOPT(sym), data, (int) size);
-}
-#endif
-
-
-template<>
-inline int SocketOption::setso<SocketOption::SYSTEM, int>(int socket, int proto, int sym, const void* data, size_t size)
-{
- return ::setsockopt(socket, proto, sym, (const char *)data, (int) size);
-}
-
-template<>
-inline bool SocketOption::extract<SocketOption::STRING>(std::string value, OptionValue& o) const
-{
- o.s = value;
- o.value = o.s.data();
- o.size = o.s.size();
- return true;
-}
-
-template<>
-inline bool SocketOption::extract<SocketOption::INT>(std::string value, OptionValue& o) const
-{
- try
- {
- o.i = stoi(value, 0, 0);
- o.value = &o.i;
- o.size = sizeof o.i;
- return true;
- }
- catch (...) // stoi throws
- {
- return false; // do not change o
- }
- return false;
-}
-
-template<>
-inline bool SocketOption::extract<SocketOption::INT64>(std::string value, OptionValue& o) const
-{
- try
- {
- long long vall = stoll(value);
- o.l = vall; // int64_t resolves to either 'long long', or 'long' being 64-bit integer
- o.value = &o.l;
- o.size = sizeof o.l;
- return true;
- }
- catch (...) // stoll throws
- {
- return false;
- }
- return false;
-}
-
-template<>
-inline bool SocketOption::extract<SocketOption::BOOL>(std::string value, OptionValue& o) const
-{
- bool val;
- if ( false_names.count(value) )
- val = false;
- else if ( true_names.count(value) )
- val = true;
- else
- return false;
-
- o.b = val;
- o.value = &o.b;
- o.size = sizeof o.b;
- return true;
-}
-
-template<>
-inline bool SocketOption::extract<SocketOption::ENUM>(std::string value, OptionValue& o) const
-{
- if (valmap)
- {
- // Search value in the map. If found, set to o.
- auto p = valmap->find(value);
- if ( p != valmap->end() )
- {
- o.i = p->second;
- o.value = &o.i;
- o.size = sizeof o.i;
- return true;
- }
- }
-
- // Fallback: try interpreting it as integer.
- try
- {
- o.i = stoi(value, 0, 0);
- o.value = &o.i;
- o.size = sizeof o.i;
- return true;
- }
- catch (...) // stoi throws
- {
- return false; // do not change o
- }
- return false;
-}
-
-template <SocketOption::Domain D, SocketOption::Type T, typename Object>
-inline bool SocketOption::applyt(Object socket, std::string value) const
-{
- OptionValue o; // common meet point
- int result = -1;
- if (extract<T>(value, o))
- result = setso<D>(socket, protocol, symbol, o.value, o.size);
- return result != -1;
-}
-
-
-template<SocketOption::Domain D, typename Object>
-inline bool SocketOption::apply(Object socket, std::string value) const
-{
- switch ( type )
- {
-#define SRT_HANDLE_TYPE(ty) case ty: return applyt<D, ty, Object>(socket, value)
-
- SRT_HANDLE_TYPE(STRING);
- SRT_HANDLE_TYPE(INT);
- SRT_HANDLE_TYPE(INT64);
- SRT_HANDLE_TYPE(BOOL);
- SRT_HANDLE_TYPE(ENUM);
-
-#undef SRT_HANDLE_TYPE
- }
- return false;
-}
-
-extern const std::map<std::string, int> enummap_transtype;
-
-namespace {
-const SocketOption srt_options [] {
- { "transtype", 0, SRTO_TRANSTYPE, SocketOption::PRE, SocketOption::ENUM, &enummap_transtype },
- { "maxbw", 0, SRTO_MAXBW, SocketOption::POST, SocketOption::INT64, nullptr},
- { "pbkeylen", 0, SRTO_PBKEYLEN, SocketOption::PRE, SocketOption::INT, nullptr},
- { "passphrase", 0, SRTO_PASSPHRASE, SocketOption::PRE, SocketOption::STRING, nullptr},
-
- { "mss", 0, SRTO_MSS, SocketOption::PRE, SocketOption::INT, nullptr},
- { "fc", 0, SRTO_FC, SocketOption::PRE, SocketOption::INT, nullptr},
- { "sndbuf", 0, SRTO_SNDBUF, SocketOption::PRE, SocketOption::INT, nullptr},
- { "rcvbuf", 0, SRTO_RCVBUF, SocketOption::PRE, SocketOption::INT, nullptr},
- // linger option is handled outside of the common loop, therefore commented out.
- //{ "linger", 0, SRTO_LINGER, SocketOption::PRE, SocketOption::INT, nullptr},
- { "ipttl", 0, SRTO_IPTTL, SocketOption::PRE, SocketOption::INT, nullptr},
- { "iptos", 0, SRTO_IPTOS, SocketOption::PRE, SocketOption::INT, nullptr},
- { "inputbw", 0, SRTO_INPUTBW, SocketOption::POST, SocketOption::INT64, nullptr},
- { "mininputbw", 0, SRTO_MININPUTBW, SocketOption::POST, SocketOption::INT64, nullptr},
- { "oheadbw", 0, SRTO_OHEADBW, SocketOption::POST, SocketOption::INT, nullptr},
- { "latency", 0, SRTO_LATENCY, SocketOption::PRE, SocketOption::INT, nullptr},
- { "tsbpdmode", 0, SRTO_TSBPDMODE, SocketOption::PRE, SocketOption::BOOL, nullptr},
- { "tlpktdrop", 0, SRTO_TLPKTDROP, SocketOption::PRE, SocketOption::BOOL, nullptr},
- { "snddropdelay", 0, SRTO_SNDDROPDELAY, SocketOption::POST, SocketOption::INT, nullptr},
- { "nakreport", 0, SRTO_NAKREPORT, SocketOption::PRE, SocketOption::BOOL, nullptr},
- { "conntimeo", 0, SRTO_CONNTIMEO, SocketOption::PRE, SocketOption::INT, nullptr},
- { "drifttracer", 0, SRTO_DRIFTTRACER, SocketOption::POST, SocketOption::BOOL, nullptr},
- { "lossmaxttl", 0, SRTO_LOSSMAXTTL, SocketOption::POST, SocketOption::INT, nullptr},
- { "rcvlatency", 0, SRTO_RCVLATENCY, SocketOption::PRE, SocketOption::INT, nullptr},
- { "peerlatency", 0, SRTO_PEERLATENCY, SocketOption::PRE, SocketOption::INT, nullptr},
- { "minversion", 0, SRTO_MINVERSION, SocketOption::PRE, SocketOption::INT, nullptr},
- { "streamid", 0, SRTO_STREAMID, SocketOption::PRE, SocketOption::STRING, nullptr},
- { "congestion", 0, SRTO_CONGESTION, SocketOption::PRE, SocketOption::STRING, nullptr},
- { "messageapi", 0, SRTO_MESSAGEAPI, SocketOption::PRE, SocketOption::BOOL, nullptr},
- { "payloadsize", 0, SRTO_PAYLOADSIZE, SocketOption::PRE, SocketOption::INT, nullptr},
- { "kmrefreshrate", 0, SRTO_KMREFRESHRATE, SocketOption::PRE, SocketOption::INT, nullptr },
- { "kmpreannounce", 0, SRTO_KMPREANNOUNCE, SocketOption::PRE, SocketOption::INT, nullptr },
- { "enforcedencryption", 0, SRTO_ENFORCEDENCRYPTION, SocketOption::PRE, SocketOption::BOOL, nullptr },
- { "ipv6only", 0, SRTO_IPV6ONLY, SocketOption::PRE, SocketOption::INT, nullptr },
- { "peeridletimeo", 0, SRTO_PEERIDLETIMEO, SocketOption::PRE, SocketOption::INT, nullptr },
- { "packetfilter", 0, SRTO_PACKETFILTER, SocketOption::PRE, SocketOption::STRING, nullptr },
-#if ENABLE_BONDING
- { "groupconnect", 0, SRTO_GROUPCONNECT, SocketOption::PRE, SocketOption::INT, nullptr},
- { "groupminstabletimeo", 0, SRTO_GROUPMINSTABLETIMEO, SocketOption::PRE, SocketOption::INT, nullptr},
-#endif
-#ifdef SRT_ENABLE_BINDTODEVICE
- { "bindtodevice", 0, SRTO_BINDTODEVICE, SocketOption::PRE, SocketOption::STRING, nullptr},
-#endif
- { "retransmitalgo", 0, SRTO_RETRANSMITALGO, SocketOption::PRE, SocketOption::INT, nullptr }
-};
-}
-
-SocketOption::Mode SrtInterpretMode(const std::string& modestr, const std::string& host, const std::string& adapter);
-SocketOption::Mode SrtConfigurePre(SRTSOCKET socket, std::string host, std::map<std::string, std::string> options, std::vector<std::string>* failures = 0);
-void SrtConfigurePost(SRTSOCKET socket, std::map<std::string, std::string> options, std::vector<std::string>* failures = 0);
-
-#endif
+++ /dev/null
-/*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2018 Haivision Systems Inc.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- */
-
-/*****************************************************************************
-written by
- Haivision Systems Inc.
- *****************************************************************************/
-#ifdef _WIN32
-#include <direct.h>
-#endif
-#include <iostream>
-#include <iterator>
-#include <vector>
-#include <map>
-#include <stdexcept>
-#include <string>
-#include <csignal>
-#include <thread>
-#include <chrono>
-#include <cassert>
-#include <sys/stat.h>
-#include <srt.h>
-#include <udt.h>
-#include <common.h>
-
-#include "apputil.hpp"
-#include "uriparser.hpp"
-#include "logsupport.hpp"
-#include "socketoptions.hpp"
-#include "transmitmedia.hpp"
-#include "verbose.hpp"
-
-
-#ifndef S_ISDIR
-#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
-#endif
-
-
-using namespace std;
-
-static bool interrupt = false;
-void OnINT_ForceExit(int)
-{
- Verb() << "\n-------- REQUESTED INTERRUPT!\n";
- interrupt = true;
-}
-
-struct FileTransmitConfig
-{
- unsigned long chunk_size;
- bool skip_flushing;
- bool quiet = false;
- srt_logging::LogLevel::type loglevel = srt_logging::LogLevel::error;
- set<srt_logging::LogFA> logfas;
- string logfile;
- int bw_report = 0;
- int stats_report = 0;
- string stats_out;
- SrtStatsPrintFormat stats_pf = SRTSTATS_PROFMAT_2COLS;
- bool full_stats = false;
-
- string source;
- string target;
-};
-
-
-void PrintOptionHelp(const set<string> &opt_names, const string &value, const string &desc)
-{
- cerr << "\t";
- int i = 0;
- for (auto opt : opt_names)
- {
- if (i++) cerr << ", ";
- cerr << "-" << opt;
- }
-
- if (!value.empty())
- cerr << ":" << value;
- cerr << "\t- " << desc << "\n";
-}
-
-
-int parse_args(FileTransmitConfig &cfg, int argc, char** argv)
-{
- const OptionName
- o_chunk = { "c", "chunk" },
- o_no_flush = { "sf", "skipflush" },
- o_bwreport = { "r", "bwreport", "report", "bandwidth-report", "bitrate-report" },
- o_statsrep = { "s", "stats", "stats-report-frequency" },
- o_statsout = { "statsout" },
- o_statspf = { "pf", "statspf" },
- o_statsfull = { "f", "fullstats" },
- o_loglevel = { "ll", "loglevel" },
- o_logfa = { "logfa" },
- o_logfile = { "logfile" },
- o_quiet = { "q", "quiet" },
- o_verbose = { "v", "verbose" },
- o_help = { "h", "help" },
- o_version = { "version" };
-
- const vector<OptionScheme> optargs = {
- { o_chunk, OptionScheme::ARG_ONE },
- { o_no_flush, OptionScheme::ARG_NONE },
- { o_bwreport, OptionScheme::ARG_ONE },
- { o_statsrep, OptionScheme::ARG_ONE },
- { o_statsout, OptionScheme::ARG_ONE },
- { o_statspf, OptionScheme::ARG_ONE },
- { o_statsfull, OptionScheme::ARG_NONE },
- { o_loglevel, OptionScheme::ARG_ONE },
- { o_logfa, OptionScheme::ARG_ONE },
- { o_logfile, OptionScheme::ARG_ONE },
- { o_quiet, OptionScheme::ARG_NONE },
- { o_verbose, OptionScheme::ARG_NONE },
- { o_help, OptionScheme::ARG_NONE },
- { o_version, OptionScheme::ARG_NONE }
- };
-
- options_t params = ProcessOptions(argv, argc, optargs);
-
- bool print_help = Option<OutBool>(params, false, o_help);
- const bool print_version = Option<OutBool>(params, false, o_version);
-
- if (params[""].size() != 2 && !print_help && !print_version)
- {
- cerr << "ERROR. Invalid syntax. Specify source and target URIs.\n";
- if (params[""].size() > 0)
- {
- cerr << "The following options are passed without a key: ";
- copy(params[""].begin(), params[""].end(), ostream_iterator<string>(cerr, ", "));
- cerr << endl;
- }
- print_help = true; // Enable help to print it further
- }
-
-
- if (print_help)
- {
- cout << "SRT sample application to transmit files.\n";
- PrintLibVersion();
- cerr << "Usage: srt-file-transmit [options] <input-uri> <output-uri>\n";
- cerr << "\n";
-
- PrintOptionHelp(o_chunk, "<chunk=1456>", "max size of data read in one step");
- PrintOptionHelp(o_no_flush, "", "skip output file flushing");
- PrintOptionHelp(o_bwreport, "<every_n_packets=0>", "bandwidth report frequency");
- PrintOptionHelp(o_statsrep, "<every_n_packets=0>", "frequency of status report");
- PrintOptionHelp(o_statsout, "<filename>", "output stats to file");
- PrintOptionHelp(o_statspf, "<format=default>", "stats printing format [json|csv|default]");
- PrintOptionHelp(o_statsfull, "", "full counters in stats-report (prints total statistics)");
- PrintOptionHelp(o_loglevel, "<level=error>", "log level [fatal,error,info,note,warning]");
- PrintOptionHelp(o_logfa, "<fas=general,...>", "log functional area [all,general,bstats,control,data,tsbpd,rexmit]");
- PrintOptionHelp(o_logfile, "<filename="">", "write logs to file");
- PrintOptionHelp(o_quiet, "", "quiet mode (default off)");
- PrintOptionHelp(o_verbose, "", "verbose mode (default off)");
- cerr << "\n";
- cerr << "\t-h,-help - show this help\n";
- cerr << "\t-version - print SRT library version\n";
- cerr << "\n";
- cerr << "\t<input-uri> - URI specifying a medium to read from\n";
- cerr << "\t<output-uri> - URI specifying a medium to write to\n";
- cerr << "URI syntax: SCHEME://HOST:PORT/PATH?PARAM1=VALUE&PARAM2=VALUE...\n";
- cerr << "Supported schemes:\n";
- cerr << "\tsrt: use HOST, PORT, and PARAM for setting socket options\n";
- cerr << "\tudp: use HOST, PORT and PARAM for some UDP specific settings\n";
- cerr << "\tfile: file URI or file://con to use stdin or stdout\n";
-
- return 2;
- }
-
- if (Option<OutBool>(params, false, o_version))
- {
- PrintLibVersion();
- return 2;
- }
-
- cfg.chunk_size = stoul(Option<OutString>(params, "1456", o_chunk));
- cfg.skip_flushing = Option<OutBool>(params, false, o_no_flush);
- cfg.bw_report = stoi(Option<OutString>(params, "0", o_bwreport));
- cfg.stats_report = stoi(Option<OutString>(params, "0", o_statsrep));
- cfg.stats_out = Option<OutString>(params, "", o_statsout);
- const string pf = Option<OutString>(params, "default", o_statspf);
- if (pf == "default")
- {
- cfg.stats_pf = SRTSTATS_PROFMAT_2COLS;
- }
- else if (pf == "json")
- {
- cfg.stats_pf = SRTSTATS_PROFMAT_JSON;
- }
- else if (pf == "csv")
- {
- cfg.stats_pf = SRTSTATS_PROFMAT_CSV;
- }
- else
- {
- cfg.stats_pf = SRTSTATS_PROFMAT_2COLS;
- cerr << "ERROR: Unsupported print format: " << pf << endl;
- return 1;
- }
-
- cfg.full_stats = Option<OutBool>(params, false, o_statsfull);
- cfg.loglevel = SrtParseLogLevel(Option<OutString>(params, "error", o_loglevel));
- cfg.logfas = SrtParseLogFA(Option<OutString>(params, "", o_logfa));
- cfg.logfile = Option<OutString>(params, "", o_logfile);
- cfg.quiet = Option<OutBool>(params, false, o_quiet);
-
- if (Option<OutBool>(params, false, o_verbose))
- Verbose::on = !cfg.quiet;
-
- cfg.source = params[""].at(0);
- cfg.target = params[""].at(1);
-
- return 0;
-}
-
-
-void ExtractPath(string path, string& w_dir, string& w_fname)
-{
- string directory = path;
- string filename = "";
-
- struct stat state;
- stat(path.c_str(), &state);
-
- if (!S_ISDIR(state.st_mode))
- {
- // Extract directory as a butlast part of path
- size_t pos = path.find_last_of("/");
- if ( pos == string::npos )
- {
- filename = path;
- directory = ".";
- }
- else
- {
- directory = path.substr(0, pos);
- filename = path.substr(pos+1);
- }
- }
-
- if (directory[0] != '/')
- {
- // Glue in the absolute prefix of the current directory
- // to make it absolute. This is needed to properly interpret
- // the fixed uri.
- static const size_t s_max_path = 4096; // don't care how proper this is
- char tmppath[s_max_path];
-#ifdef _WIN32
- const char* gwd = _getcwd(tmppath, s_max_path);
-#else
- const char* gwd = getcwd(tmppath, s_max_path);
-#endif
- if ( !gwd )
- {
- // Don't bother with that now. We need something better for
- // that anyway.
- throw std::invalid_argument("Path too long");
- }
- const string wd = gwd;
-
- directory = wd + "/" + directory;
- }
-
- w_dir = directory;
- w_fname = filename;
-}
-
-bool DoUpload(UriParser& ut, string path, string filename,
- const FileTransmitConfig &cfg, std::ostream &out_stats)
-{
- bool result = false;
- unique_ptr<Target> tar;
- SRTSOCKET s = SRT_INVALID_SOCK;
- bool connected = false;
- int pollid = -1;
-
- ifstream ifile(path, ios::binary);
- if ( !ifile )
- {
- cerr << "Error opening file: '" << path << "'";
- goto exit;
- }
-
- pollid = srt_epoll_create();
- if ( pollid < 0 )
- {
- cerr << "Can't initialize epoll";
- goto exit;
- }
-
-
- while (!interrupt)
- {
- if (!tar.get())
- {
- tar = Target::Create(ut.makeUri());
- if (!tar.get())
- {
- cerr << "Unsupported target type: " << ut.uri() << endl;
- goto exit;
- }
-
- int events = SRT_EPOLL_OUT | SRT_EPOLL_ERR;
- if (srt_epoll_add_usock(pollid,
- tar->GetSRTSocket(), &events))
- {
- cerr << "Failed to add SRT destination to poll, "
- << tar->GetSRTSocket() << endl;
- goto exit;
- }
- srt::setstreamid(tar->GetSRTSocket(), filename);
- }
-
- s = tar->GetSRTSocket();
- assert(s != SRT_INVALID_SOCK);
-
- SRTSOCKET efd;
- int efdlen = 1;
- if (srt_epoll_wait(pollid,
- 0, 0, &efd, &efdlen,
- 100, nullptr, nullptr, 0, 0) < 0)
- {
- continue;
- }
-
- assert(efd == s);
- assert(efdlen == 1);
-
- SRT_SOCKSTATUS status = srt_getsockstate(s);
-
- switch (status)
- {
- case SRTS_LISTENING:
- {
- if (!tar->AcceptNewClient())
- {
- cerr << "Failed to accept SRT connection" << endl;
- goto exit;
- }
-
- srt_epoll_remove_usock(pollid, s);
-
- s = tar->GetSRTSocket();
- int events = SRT_EPOLL_OUT | SRT_EPOLL_ERR;
- if (srt_epoll_add_usock(pollid, s, &events))
- {
- cerr << "Failed to add SRT client to poll" << endl;
- goto exit;
- }
- cerr << "Target connected (listener)" << endl;
- connected = true;
- }
- break;
- case SRTS_CONNECTED:
- {
- if (!connected)
- {
- cerr << "Target connected (caller)" << endl;
- connected = true;
- }
- }
- break;
- case SRTS_BROKEN:
- case SRTS_NONEXIST:
- case SRTS_CLOSED:
- {
- cerr << "Target disconnected" << endl;
- goto exit;
- }
- default:
- {
- // No-Op
- }
- break;
- }
-
- if (connected)
- {
- vector<char> buf(cfg.chunk_size);
- size_t n = ifile.read(buf.data(), cfg.chunk_size).gcount();
- size_t shift = 0;
- while (n > 0)
- {
- int st = tar->Write(buf.data() + shift, n, 0, out_stats);
- Verb() << "Upload: " << n << " --> " << st
- << (!shift ? string() : "+" + Sprint(shift));
- if (st == SRT_ERROR)
- {
- cerr << "Upload: SRT error: " << srt_getlasterror_str()
- << endl;
- goto exit;
- }
-
- n -= st;
- shift += st;
- }
-
- if (ifile.eof())
- {
- cerr << "File sent" << endl;
- result = true;
- break;
- }
-
- if ( !ifile.good() )
- {
- cerr << "ERROR while reading file\n";
- goto exit;
- }
-
- }
- }
-
- if (result && !cfg.skip_flushing)
- {
- assert(s != SRT_INVALID_SOCK);
-
- // send-flush-loop
- result = false;
- while (!interrupt)
- {
- size_t bytes;
- size_t blocks;
- int st = srt_getsndbuffer(s, &blocks, &bytes);
- if (st == SRT_ERROR)
- {
- cerr << "Error in srt_getsndbuffer: " << srt_getlasterror_str()
- << endl;
- goto exit;
- }
- if (bytes == 0)
- {
- cerr << "Buffers flushed" << endl;
- result = true;
- break;
- }
- Verb() << "Sending buffer still: bytes=" << bytes << " blocks="
- << blocks;
- srt::sync::this_thread::sleep_for(srt::sync::milliseconds_from(250));
- }
- }
-
-exit:
- if (pollid >= 0)
- {
- srt_epoll_release(pollid);
- }
-
- return result;
-}
-
-bool DoDownload(UriParser& us, string directory, string filename,
- const FileTransmitConfig &cfg, std::ostream &out_stats)
-{
- bool result = false;
- unique_ptr<Source> src;
- SRTSOCKET s = SRT_INVALID_SOCK;
- bool connected = false;
- int pollid = -1;
- string id;
- ofstream ofile;
- SRT_SOCKSTATUS status;
- SRTSOCKET efd;
- int efdlen = 1;
-
- pollid = srt_epoll_create();
- if ( pollid < 0 )
- {
- cerr << "Can't initialize epoll";
- goto exit;
- }
-
- while (!interrupt)
- {
- if (!src.get())
- {
- src = Source::Create(us.makeUri());
- if (!src.get())
- {
- cerr << "Unsupported source type: " << us.uri() << endl;
- goto exit;
- }
-
- int events = SRT_EPOLL_IN | SRT_EPOLL_ERR;
- if (srt_epoll_add_usock(pollid,
- src->GetSRTSocket(), &events))
- {
- cerr << "Failed to add SRT source to poll, "
- << src->GetSRTSocket() << endl;
- goto exit;
- }
- }
-
- s = src->GetSRTSocket();
- assert(s != SRT_INVALID_SOCK);
-
- if (srt_epoll_wait(pollid,
- &efd, &efdlen, 0, 0,
- 100, nullptr, nullptr, 0, 0) < 0)
- {
- continue;
- }
-
- assert(efd == s);
- assert(efdlen == 1);
-
- status = srt_getsockstate(s);
- Verb() << "Event with status " << status << "\n";
-
- switch (status)
- {
- case SRTS_LISTENING:
- {
- if (!src->AcceptNewClient())
- {
- cerr << "Failed to accept SRT connection" << endl;
- goto exit;
- }
-
- srt_epoll_remove_usock(pollid, s);
-
- s = src->GetSRTSocket();
- int events = SRT_EPOLL_IN | SRT_EPOLL_ERR;
- if (srt_epoll_add_usock(pollid, s, &events))
- {
- cerr << "Failed to add SRT client to poll" << endl;
- goto exit;
- }
- id = srt::getstreamid(s);
- cerr << "Source connected (listener), id ["
- << id << "]" << endl;
- connected = true;
- continue;
- }
- break;
- case SRTS_CONNECTED:
- {
- if (!connected)
- {
- id = srt::getstreamid(s);
- cerr << "Source connected (caller), id ["
- << id << "]" << endl;
- connected = true;
- }
- }
- break;
-
- // No need to do any special action in case of broken.
- // The app will just try to read and in worst case it will
- // get an error.
- case SRTS_BROKEN:
- cerr << "Connection closed, reading buffer remains\n";
- break;
-
- case SRTS_NONEXIST:
- case SRTS_CLOSED:
- {
- cerr << "Source disconnected" << endl;
- goto exit;
- }
- break;
- default:
- {
- // No-Op
- }
- break;
- }
-
- if (connected)
- {
- MediaPacket packet(cfg.chunk_size);
-
- if (!ofile.is_open())
- {
- const char * fn = id.empty() ? filename.c_str() : id.c_str();
- directory.append("/");
- directory.append(fn);
- ofile.open(directory.c_str(), ios::out | ios::trunc | ios::binary);
-
- if (!ofile.is_open())
- {
- cerr << "Error opening file [" << directory << "]" << endl;
- goto exit;
- }
- cerr << "Writing output to [" << directory << "]" << endl;
- }
-
- int n = src->Read(cfg.chunk_size, packet, out_stats);
- if (n == SRT_ERROR)
- {
- cerr << "Download: SRT error: " << srt_getlasterror_str() << endl;
- goto exit;
- }
-
- if (n == 0)
- {
- result = true;
- cerr << "Download COMPLETE.\n";
- break;
- }
-
- // Write to file any amount of data received
- Verb() << "Download: --> " << n;
- ofile.write(packet.payload.data(), n);
- if (!ofile.good())
- {
- cerr << "Error writing file" << endl;
- goto exit;
- }
-
- }
- }
-
-exit:
- if (pollid >= 0)
- {
- srt_epoll_release(pollid);
- }
-
- return result;
-}
-
-bool Upload(UriParser& srt_target_uri, UriParser& fileuri,
- const FileTransmitConfig &cfg, std::ostream &out_stats)
-{
- if ( fileuri.scheme() != "file" )
- {
- cerr << "Upload: source accepted only as a file\n";
- return false;
- }
- // fileuri is source-reading file
- // srt_target_uri is SRT target
-
- string path = fileuri.path();
- string directory, filename;
- ExtractPath(path, (directory), (filename));
- Verb() << "Extract path '" << path << "': directory=" << directory << " filename=" << filename;
- // Set ID to the filename.
- // Directory will be preserved.
-
- // Add some extra parameters.
- srt_target_uri["transtype"] = "file";
-
- return DoUpload(srt_target_uri, path, filename, cfg, out_stats);
-}
-
-bool Download(UriParser& srt_source_uri, UriParser& fileuri,
- const FileTransmitConfig &cfg, std::ostream &out_stats)
-{
- if (fileuri.scheme() != "file" )
- {
- cerr << "Download: target accepted only as a file\n";
- return false;
- }
-
- string path = fileuri.path(), directory, filename;
- ExtractPath(path, (directory), (filename));
- Verb() << "Extract path '" << path << "': directory=" << directory << " filename=" << filename;
-
- // Add some extra parameters.
- srt_source_uri["transtype"] = "file";
-
- return DoDownload(srt_source_uri, directory, filename, cfg, out_stats);
-}
-
-
-int main(int argc, char** argv)
-{
- FileTransmitConfig cfg;
- const int parse_ret = parse_args(cfg, argc, argv);
- if (parse_ret != 0)
- return parse_ret == 1 ? EXIT_FAILURE : 0;
-
- //
- // Set global config variables
- //
- if (cfg.chunk_size != SRT_LIVE_MAX_PLSIZE)
- transmit_chunk_size = cfg.chunk_size;
- transmit_stats_writer = SrtStatsWriterFactory(cfg.stats_pf);
- transmit_bw_report = cfg.bw_report;
- transmit_stats_report = cfg.stats_report;
- transmit_total_stats = cfg.full_stats;
-
- //
- // Set SRT log levels and functional areas
- //
- srt_setloglevel(cfg.loglevel);
- for (set<srt_logging::LogFA>::iterator i = cfg.logfas.begin(); i != cfg.logfas.end(); ++i)
- srt_addlogfa(*i);
-
- //
- // SRT log handler
- //
- std::ofstream logfile_stream; // leave unused if not set
- if (!cfg.logfile.empty())
- {
- logfile_stream.open(cfg.logfile.c_str());
- if (!logfile_stream)
- {
- cerr << "ERROR: Can't open '" << cfg.logfile.c_str() << "' for writing - fallback to cerr\n";
- }
- else
- {
- srt::setlogstream(logfile_stream);
- }
- }
-
- //
- // SRT stats output
- //
- std::ofstream logfile_stats; // leave unused if not set
- if (cfg.stats_out != "" && cfg.stats_out != "stdout")
- {
- logfile_stats.open(cfg.stats_out.c_str());
- if (!logfile_stats)
- {
- cerr << "ERROR: Can't open '" << cfg.stats_out << "' for writing stats. Fallback to stdout.\n";
- return 1;
- }
- }
- else if (cfg.bw_report != 0 || cfg.stats_report != 0)
- {
- g_stats_are_printed_to_stdout = true;
- }
-
- ostream &out_stats = logfile_stats.is_open() ? logfile_stats : cout;
-
- // File transmission code
-
- UriParser us(cfg.source), ut(cfg.target);
-
- Verb() << "SOURCE type=" << us.scheme() << ", TARGET type=" << ut.scheme();
-
- signal(SIGINT, OnINT_ForceExit);
- signal(SIGTERM, OnINT_ForceExit);
-
- try
- {
- if (us.scheme() == "srt")
- {
- if (ut.scheme() != "file")
- {
- cerr << "SRT to FILE should be specified\n";
- return 1;
- }
- Download(us, ut, cfg, out_stats);
- }
- else if (ut.scheme() == "srt")
- {
- if (us.scheme() != "file")
- {
- cerr << "FILE to SRT should be specified\n";
- return 1;
- }
- Upload(ut, us, cfg, out_stats);
- }
- else
- {
- cerr << "SRT URI must be one of given media.\n";
- return 1;
- }
- }
- catch (std::exception& x)
- {
- cerr << "ERROR: " << x.what() << endl;
- return 1;
- }
-
-
- return 0;
-}
+++ /dev/null
-/*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2018 Haivision Systems Inc.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- */
-
-// NOTE: This application uses C++11.
-
-// This program uses quite a simple architecture, which is mainly related to
-// the way how it's invoked: srt-live-transmit <source> <target> (plus options).
-//
-// The media for <source> and <target> are filled by abstract classes
-// named Source and Target respectively. Most important virtuals to
-// be filled by the derived classes are Source::Read and Target::Write.
-//
-// For SRT please take a look at the SrtCommon class first. This contains
-// everything that is needed for creating an SRT medium, that is, making
-// a connection as listener, as caller, and as rendezvous. The listener
-// and caller modes are built upon the same philosophy as those for
-// BSD/POSIX socket API (bind/listen/accept or connect).
-//
-// The instance class is selected per details in the URI (usually scheme)
-// and then this URI is used to configure the medium object. Medium-specific
-// options are specified in the URI: SCHEME://HOST:PORT?opt1=val1&opt2=val2 etc.
-//
-// Options for connection are set by ConfigurePre and ConfigurePost.
-// This is a philosophy that exists also in BSD/POSIX sockets, just not
-// officially mentioned:
-// - The "PRE" options must be set prior to connecting and can't be altered
-// on a connected socket, however if set on a listening socket, they are
-// derived by accept-ed socket.
-// - The "POST" options can be altered any time on a connected socket.
-// They MAY have also some meaning when set prior to connecting; such
-// option is SRTO_RCVSYN, which makes connect/accept call asynchronous.
-// Because of that this option is treated special way in this app.
-//
-// See 'srt_options' global variable (common/socketoptions.hpp) for a list of
-// all options.
-
-// MSVS likes to complain about lots of standard C functions being unsafe.
-#ifdef _MSC_VER
-#define _CRT_SECURE_NO_WARNINGS 1
-#endif
-
-#define REQUIRE_CXX11 1
-
-#include <cctype>
-#include <iostream>
-#include <fstream>
-#include <string>
-#include <map>
-#include <set>
-#include <vector>
-#include <memory>
-#include <algorithm>
-#include <iterator>
-#include <stdexcept>
-#include <cstring>
-#include <csignal>
-#include <chrono>
-#include <thread>
-#include <list>
-
-
-#include "apputil.hpp" // CreateAddr
-#include "uriparser.hpp" // UriParser
-#include "socketoptions.hpp"
-#include "logsupport.hpp"
-#include "transmitmedia.hpp"
-#include "verbose.hpp"
-
-// NOTE: This is without "haisrt/" because it uses an internal path
-// to the library. Application using the "installed" library should
-// use <srt/srt.h>
-#include <srt.h>
-#include <udt.h> // This TEMPORARILY contains extra C++-only SRT API.
-#include <logging.h>
-
-using namespace std;
-
-
-
-struct ForcedExit: public std::runtime_error
-{
- ForcedExit(const std::string& arg):
- std::runtime_error(arg)
- {
- }
-};
-
-struct AlarmExit: public std::runtime_error
-{
- AlarmExit(const std::string& arg):
- std::runtime_error(arg)
- {
- }
-};
-
-srt::sync::atomic<bool> int_state;
-srt::sync::atomic<bool> timer_state;
-void OnINT_ForceExit(int)
-{
- Verb() << "\n-------- REQUESTED INTERRUPT!\n";
- int_state = true;
-}
-
-void OnAlarm_Interrupt(int)
-{
- Verb() << "\n---------- INTERRUPT ON TIMEOUT!\n";
-
- int_state = false; // JIC
- timer_state = true;
-
- if ((false))
- {
- throw AlarmExit("Watchdog bites hangup");
- }
-}
-
-extern "C" void TestLogHandler(void* opaque, int level, const char* file, int line, const char* area, const char* message);
-
-
-
-struct LiveTransmitConfig
-{
- int timeout = 0;
- int timeout_mode = 0;
- int chunk_size = -1;
- bool quiet = false;
- srt_logging::LogLevel::type loglevel = srt_logging::LogLevel::error;
- set<srt_logging::LogFA> logfas;
- bool log_internal;
- string logfile;
- int bw_report = 0;
- bool srctime = false;
- size_t buffering = 10;
- int stats_report = 0;
- string stats_out;
- SrtStatsPrintFormat stats_pf = SRTSTATS_PROFMAT_2COLS;
- bool auto_reconnect = true;
- bool full_stats = false;
-
- string source;
- string target;
-};
-
-
-void PrintOptionHelp(const OptionName& opt_names, const string &value, const string &desc)
-{
- cerr << "\t";
- int i = 0;
- for (auto opt : opt_names.names)
- {
- if (i++) cerr << ", ";
- cerr << "-" << opt;
- }
-
- if (!value.empty())
- cerr << ":" << value;
- cerr << "\t- " << desc << "\n";
-}
-
-int parse_args(LiveTransmitConfig &cfg, int argc, char** argv)
-{
- const OptionName
- o_timeout = { "t", "to", "timeout" },
- o_timeout_mode = { "tm", "timeout-mode" },
- o_autorecon = { "a", "auto", "autoreconnect" },
- o_chunk = { "c", "chunk" },
- o_bwreport = { "r", "bwreport", "report", "bandwidth-report", "bitrate-report" },
- o_srctime = {"st", "srctime", "sourcetime"},
- o_buffering = {"buffering"},
- o_statsrep = { "s", "stats", "stats-report-frequency" },
- o_statsout = { "statsout" },
- o_statspf = { "pf", "statspf" },
- o_statsfull = { "f", "fullstats" },
- o_loglevel = { "ll", "loglevel" },
- o_logfa = { "lfa", "logfa" },
- o_log_internal = { "loginternal"},
- o_logfile = { "logfile" },
- o_quiet = { "q", "quiet" },
- o_verbose = { "v", "verbose" },
- o_help = { "h", "help" },
- o_version = { "version" };
-
- const vector<OptionScheme> optargs = {
- { o_timeout, OptionScheme::ARG_ONE },
- { o_timeout_mode, OptionScheme::ARG_ONE },
- { o_autorecon, OptionScheme::ARG_ONE },
- { o_chunk, OptionScheme::ARG_ONE },
- { o_bwreport, OptionScheme::ARG_ONE },
- { o_srctime, OptionScheme::ARG_ONE },
- { o_buffering, OptionScheme::ARG_ONE },
- { o_statsrep, OptionScheme::ARG_ONE },
- { o_statsout, OptionScheme::ARG_ONE },
- { o_statspf, OptionScheme::ARG_ONE },
- { o_statsfull, OptionScheme::ARG_NONE },
- { o_loglevel, OptionScheme::ARG_ONE },
- { o_logfa, OptionScheme::ARG_ONE },
- { o_log_internal, OptionScheme::ARG_NONE },
- { o_logfile, OptionScheme::ARG_ONE },
- { o_quiet, OptionScheme::ARG_NONE },
- { o_verbose, OptionScheme::ARG_NONE },
- { o_help, OptionScheme::ARG_VAR },
- { o_version, OptionScheme::ARG_NONE }
- };
-
- options_t params = ProcessOptions(argv, argc, optargs);
-
- bool print_help = OptionPresent(params, o_help);
- const bool print_version = OptionPresent(params, o_version);
-
- if (params[""].size() != 2 && !print_help && !print_version)
- {
- cerr << "ERROR. Invalid syntax. Specify source and target URIs.\n";
- if (params[""].size() > 0)
- {
- cerr << "The following options are passed without a key: ";
- copy(params[""].begin(), params[""].end(), ostream_iterator<string>(cerr, ", "));
- cerr << endl;
- }
- print_help = true; // Enable help to print it further
- }
-
- if (print_help)
- {
- string helpspec = Option<OutString>(params, o_help);
-
- if (helpspec == "logging")
- {
- cerr << "Logging options:\n";
- cerr << " -ll <LEVEL> - specify minimum log level\n";
- cerr << " -lfa <area...> - specify functional areas\n";
- cerr << "Where:\n\n";
- cerr << " <LEVEL>: fatal error note warning debug\n\n";
- cerr << "Turns on logs that are at the given log level or any higher level\n";
- cerr << "(all to the left in the list above from the selected level).\n";
- cerr << "Names from syslog, like alert, crit, emerg, err, info, panic, are also\n";
- cerr << "recognized, but they are aligned to those that lie close in the above hierarchy.\n\n";
- cerr << " <area...> is a coma-separated list of areas to turn on.\n\n";
- cerr << "The list may include 'all' to turn all FAs on.\n";
- cerr << "Example: `-lfa:sockmgmt,chn-recv` enables only `sockmgmt` and `chn-recv` log FAs.\n";
- cerr << "Default: all are on except haicrypt. NOTE: 'general' FA can't be disabled.\n\n";
- cerr << "List of functional areas:\n";
-
- map<int, string> revmap;
- for (auto entry: SrtLogFAList())
- revmap[entry.second] = entry.first;
-
- // Each group on a new line
- int en10 = 0;
- for (auto entry: revmap)
- {
- cerr << " " << entry.second;
- if (entry.first/10 != en10)
- {
- cerr << endl;
- en10 = entry.first/10;
- }
- }
- cerr << endl;
-
- return 1;
- }
-
- cout << "SRT sample application to transmit live streaming.\n";
- PrintLibVersion();
- cerr << "Usage: srt-live-transmit [options] <input-uri> <output-uri>\n";
- cerr << "\n";
-#ifndef _WIN32
- PrintOptionHelp(o_timeout, "<timeout=0>", "exit timer in seconds");
- PrintOptionHelp(o_timeout_mode, "<mode=0>", "timeout mode (0 - since app start; 1 - like 0, but cancel on connect");
-#endif
- PrintOptionHelp(o_autorecon, "<enabled=yes>", "auto-reconnect mode {yes, no}");
- PrintOptionHelp(o_chunk, "<chunk=1456>", "max size of data read in one step, that can fit one SRT packet");
- PrintOptionHelp(o_bwreport, "<every_n_packets=0>", "bandwidth report frequency");
- PrintOptionHelp(o_srctime, "<enabled=yes>", "Pass packet time from source to SRT output {yes, no}");
- PrintOptionHelp(o_buffering, "<packets=n>", "Buffer up to n incoming packets");
- PrintOptionHelp(o_statsrep, "<every_n_packets=0>", "frequency of status report");
- PrintOptionHelp(o_statsout, "<filename>", "output stats to file");
- PrintOptionHelp(o_statspf, "<format=default>", "stats printing format {json, csv, default}");
- PrintOptionHelp(o_statsfull, "", "full counters in stats-report (prints total statistics)");
- PrintOptionHelp(o_loglevel, "<level=warn>", "log level {fatal,error,warn,note,info,debug}");
- PrintOptionHelp(o_logfa, "<fas>", "log functional area (see '-h logging' for more info)");
- //PrintOptionHelp(o_log_internal, "", "use internal logger");
- PrintOptionHelp(o_logfile, "<filename="">", "write logs to file");
- PrintOptionHelp(o_quiet, "", "quiet mode (default off)");
- PrintOptionHelp(o_verbose, "", "verbose mode (default off)");
- cerr << "\n";
- cerr << "\t-h,-help - show this help (use '-h logging' for logging system)\n";
- cerr << "\t-version - print SRT library version\n";
- cerr << "\n";
- cerr << "\t<input-uri> - URI specifying a medium to read from\n";
- cerr << "\t<output-uri> - URI specifying a medium to write to\n";
- cerr << "URI syntax: SCHEME://HOST:PORT/PATH?PARAM1=VALUE&PARAM2=VALUE...\n";
- cerr << "Supported schemes:\n";
- cerr << "\tsrt: use HOST, PORT, and PARAM for setting socket options\n";
- cerr << "\tudp: use HOST, PORT and PARAM for some UDP specific settings\n";
- cerr << "\tfile: only as file://con for using stdin or stdout\n";
-
- return 2;
- }
-
- if (print_version)
- {
- PrintLibVersion();
- return 2;
- }
-
- cfg.timeout = Option<OutNumber>(params, o_timeout);
- cfg.timeout_mode = Option<OutNumber>(params, o_timeout_mode);
- cfg.chunk_size = Option<OutNumber>(params, "-1", o_chunk);
- cfg.srctime = Option<OutBool>(params, cfg.srctime, o_srctime);
- const int buffering = Option<OutNumber>(params, "10", o_buffering);
- if (buffering <= 0)
- {
- cerr << "ERROR: Buffering value should be positive. Value provided: " << buffering << "." << endl;
- return 1;
- }
- else
- {
- cfg.buffering = (size_t) buffering;
- }
- cfg.bw_report = Option<OutNumber>(params, o_bwreport);
- cfg.stats_report = Option<OutNumber>(params, o_statsrep);
- cfg.stats_out = Option<OutString>(params, o_statsout);
- const string pf = Option<OutString>(params, "default", o_statspf);
- string pfext;
- cfg.stats_pf = ParsePrintFormat(pf, (pfext));
- if (cfg.stats_pf == SRTSTATS_PROFMAT_INVALID)
- {
- cfg.stats_pf = SRTSTATS_PROFMAT_2COLS;
- cerr << "ERROR: Unsupported print format: " << pf << " -- fallback to default" << endl;
- return 1;
- }
-
- cfg.full_stats = OptionPresent(params, o_statsfull);
- cfg.loglevel = SrtParseLogLevel(Option<OutString>(params, "warn", o_loglevel));
- cfg.logfas = SrtParseLogFA(Option<OutString>(params, "", o_logfa));
- cfg.log_internal = OptionPresent(params, o_log_internal);
- cfg.logfile = Option<OutString>(params, o_logfile);
- cfg.quiet = OptionPresent(params, o_quiet);
-
- if (OptionPresent(params, o_verbose))
- Verbose::on = !cfg.quiet;
-
- cfg.auto_reconnect = Option<OutBool>(params, true, o_autorecon);
-
- cfg.source = params[""].at(0);
- cfg.target = params[""].at(1);
-
- return 0;
-}
-
-
-
-int main(int argc, char** argv)
-{
- srt_startup();
- // This is mainly required on Windows to initialize the network system,
- // for a case when the instance would use UDP. SRT does it on its own, independently.
- if (!SysInitializeNetwork())
- throw std::runtime_error("Can't initialize network!");
-
- // Symmetrically, this does a cleanup; put into a local destructor to ensure that
- // it's called regardless of how this function returns.
- struct NetworkCleanup
- {
- ~NetworkCleanup()
- {
- srt_cleanup();
- SysCleanupNetwork();
- }
- } cleanupobj;
-
-
- LiveTransmitConfig cfg;
- const int parse_ret = parse_args(cfg, argc, argv);
- if (parse_ret != 0)
- return parse_ret == 1 ? EXIT_FAILURE : 0;
-
- //
- // Set global config variables
- //
- if (cfg.chunk_size > 0)
- transmit_chunk_size = cfg.chunk_size;
- transmit_stats_writer = SrtStatsWriterFactory(cfg.stats_pf);
- transmit_bw_report = cfg.bw_report;
- transmit_stats_report = cfg.stats_report;
- transmit_total_stats = cfg.full_stats;
-
- //
- // Set SRT log levels and functional areas
- //
- srt_setloglevel(cfg.loglevel);
- if (!cfg.logfas.empty())
- {
- srt_resetlogfa(nullptr, 0);
- for (set<srt_logging::LogFA>::iterator i = cfg.logfas.begin(); i != cfg.logfas.end(); ++i)
- srt_addlogfa(*i);
- }
-
- //
- // SRT log handler
- //
- std::ofstream logfile_stream; // leave unused if not set
- char NAME[] = "SRTLIB";
- if (cfg.log_internal)
- {
- srt_setlogflags(0
- | SRT_LOGF_DISABLE_TIME
- | SRT_LOGF_DISABLE_SEVERITY
- | SRT_LOGF_DISABLE_THREADNAME
- | SRT_LOGF_DISABLE_EOL
- );
- srt_setloghandler(NAME, TestLogHandler);
- }
- else if (!cfg.logfile.empty())
- {
- logfile_stream.open(cfg.logfile.c_str());
- if (!logfile_stream)
- {
- cerr << "ERROR: Can't open '" << cfg.logfile.c_str() << "' for writing - fallback to cerr\n";
- }
- else
- {
- srt::setlogstream(logfile_stream);
- }
- }
-
-
- //
- // SRT stats output
- //
- std::ofstream logfile_stats; // leave unused if not set
- if (cfg.stats_out != "")
- {
- logfile_stats.open(cfg.stats_out.c_str());
- if (!logfile_stats)
- {
- cerr << "ERROR: Can't open '" << cfg.stats_out << "' for writing stats. Fallback to stdout.\n";
- logfile_stats.close();
- }
- }
- else if (cfg.bw_report != 0 || cfg.stats_report != 0)
- {
- g_stats_are_printed_to_stdout = true;
- }
-
- ostream &out_stats = logfile_stats.is_open() ? logfile_stats : cout;
-
-#ifdef _WIN32
-
- if (cfg.timeout != 0)
- {
- cerr << "ERROR: The -timeout option (-t) is not implemented on Windows\n";
- return EXIT_FAILURE;
- }
-
-#else
- if (cfg.timeout > 0)
- {
- signal(SIGALRM, OnAlarm_Interrupt);
- if (!cfg.quiet)
- cerr << "TIMEOUT: will interrupt after " << cfg.timeout << "s\n";
- alarm(cfg.timeout);
- }
-#endif
- signal(SIGINT, OnINT_ForceExit);
- signal(SIGTERM, OnINT_ForceExit);
-
-
- if (!cfg.quiet)
- {
- cerr << "Media path: '"
- << cfg.source
- << "' --> '"
- << cfg.target
- << "'\n";
- }
-
- unique_ptr<Source> src;
- bool srcConnected = false;
- unique_ptr<Target> tar;
- bool tarConnected = false;
-
- int pollid = srt_epoll_create();
- if (pollid < 0)
- {
- cerr << "Can't initialize epoll";
- return 1;
- }
-
- size_t receivedBytes = 0;
- size_t wroteBytes = 0;
- size_t lostBytes = 0;
- size_t lastReportedtLostBytes = 0;
- std::time_t writeErrorLogTimer(std::time(nullptr));
-
- try {
- // Now loop until broken
- while (!int_state && !timer_state)
- {
- if (!src.get())
- {
- src = Source::Create(cfg.source);
- if (!src.get())
- {
- cerr << "Unsupported source type" << endl;
- return 1;
- }
- int events = SRT_EPOLL_IN | SRT_EPOLL_ERR;
- switch (src->uri.type())
- {
- case UriParser::SRT:
- if (srt_epoll_add_usock(pollid,
- src->GetSRTSocket(), &events))
- {
- cerr << "Failed to add SRT source to poll, "
- << src->GetSRTSocket() << endl;
- return 1;
- }
- break;
- case UriParser::UDP:
- if (srt_epoll_add_ssock(pollid,
- src->GetSysSocket(), &events))
- {
- cerr << "Failed to add UDP source to poll, "
- << src->GetSysSocket() << endl;
- return 1;
- }
- break;
- case UriParser::FILE:
- if (srt_epoll_add_ssock(pollid,
- src->GetSysSocket(), &events))
- {
- cerr << "Failed to add FILE source to poll, "
- << src->GetSysSocket() << endl;
- return 1;
- }
- break;
- default:
- break;
- }
-
- receivedBytes = 0;
- }
-
- if (!tar.get())
- {
- tar = Target::Create(cfg.target);
- if (!tar.get())
- {
- cerr << "Unsupported target type" << endl;
- return 1;
- }
-
- // IN because we care for state transitions only
- // OUT - to check the connection state changes
- int events = SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR;
- switch(tar->uri.type())
- {
- case UriParser::SRT:
- if (srt_epoll_add_usock(pollid,
- tar->GetSRTSocket(), &events))
- {
- cerr << "Failed to add SRT destination to poll, "
- << tar->GetSRTSocket() << endl;
- return 1;
- }
- break;
- default:
- break;
- }
-
- wroteBytes = 0;
- lostBytes = 0;
- lastReportedtLostBytes = 0;
- }
-
- int srtrfdslen = 2;
- int srtwfdslen = 2;
- SRTSOCKET srtrwfds[4] = {SRT_INVALID_SOCK, SRT_INVALID_SOCK , SRT_INVALID_SOCK , SRT_INVALID_SOCK };
- int sysrfdslen = 2;
- SYSSOCKET sysrfds[2];
- if (srt_epoll_wait(pollid,
- &srtrwfds[0], &srtrfdslen, &srtrwfds[2], &srtwfdslen,
- 100,
- &sysrfds[0], &sysrfdslen, 0, 0) >= 0)
- {
- bool doabort = false;
- for (size_t i = 0; i < sizeof(srtrwfds) / sizeof(SRTSOCKET); i++)
- {
- SRTSOCKET s = srtrwfds[i];
- if (s == SRT_INVALID_SOCK)
- continue;
-
- // Remove duplicated sockets
- for (size_t j = i + 1; j < sizeof(srtrwfds) / sizeof(SRTSOCKET); j++)
- {
- const SRTSOCKET next_s = srtrwfds[j];
- if (next_s == s)
- srtrwfds[j] = SRT_INVALID_SOCK;
- }
-
- bool issource = false;
- if (src && src->GetSRTSocket() == s)
- {
- issource = true;
- }
- else if (tar && tar->GetSRTSocket() != s)
- {
- continue;
- }
-
- const char * dirstring = (issource) ? "source" : "target";
-
- SRT_SOCKSTATUS status = srt_getsockstate(s);
- switch (status)
- {
- case SRTS_LISTENING:
- {
- const bool res = (issource) ?
- src->AcceptNewClient() : tar->AcceptNewClient();
- if (!res)
- {
- cerr << "Failed to accept SRT connection"
- << endl;
- doabort = true;
- break;
- }
-
- srt_epoll_remove_usock(pollid, s);
-
- SRTSOCKET ns = (issource) ?
- src->GetSRTSocket() : tar->GetSRTSocket();
- int events = SRT_EPOLL_IN | SRT_EPOLL_ERR;
- if (srt_epoll_add_usock(pollid, ns, &events))
- {
- cerr << "Failed to add SRT client to poll, "
- << ns << endl;
- doabort = true;
- }
- else
- {
- if (!cfg.quiet)
- {
- cerr << "Accepted SRT "
- << dirstring
- << " connection"
- << endl;
- }
-#ifndef _WIN32
- if (cfg.timeout_mode == 1 && cfg.timeout > 0)
- {
- if (!cfg.quiet)
- cerr << "TIMEOUT: cancel\n";
- alarm(0);
- }
-#endif
- if (issource)
- srcConnected = true;
- else
- tarConnected = true;
- }
- }
- break;
- case SRTS_BROKEN:
- case SRTS_NONEXIST:
- case SRTS_CLOSED:
- {
- if (issource)
- {
- if (srcConnected)
- {
- if (!cfg.quiet)
- {
- cerr << "SRT source disconnected"
- << endl;
- }
- srcConnected = false;
- }
- }
- else if (tarConnected)
- {
- if (!cfg.quiet)
- cerr << "SRT target disconnected" << endl;
- tarConnected = false;
- }
-
- if(!cfg.auto_reconnect)
- {
- doabort = true;
- }
- else
- {
- // force re-connection
- srt_epoll_remove_usock(pollid, s);
- if (issource)
- src.reset();
- else
- tar.reset();
-
-#ifndef _WIN32
- if (cfg.timeout_mode == 1 && cfg.timeout > 0)
- {
- if (!cfg.quiet)
- cerr << "TIMEOUT: will interrupt after " << cfg.timeout << "s\n";
- alarm(cfg.timeout);
- }
-#endif
- }
- }
- break;
- case SRTS_CONNECTED:
- {
- if (issource)
- {
- if (!srcConnected)
- {
- if (!cfg.quiet)
- cerr << "SRT source connected" << endl;
- srcConnected = true;
- }
- }
- else if (!tarConnected)
- {
- if (!cfg.quiet)
- cerr << "SRT target connected" << endl;
- tarConnected = true;
- if (tar->uri.type() == UriParser::SRT)
- {
- const int events = SRT_EPOLL_IN | SRT_EPOLL_ERR;
- // Disable OUT event polling when connected
- if (srt_epoll_update_usock(pollid,
- tar->GetSRTSocket(), &events))
- {
- cerr << "Failed to add SRT destination to poll, "
- << tar->GetSRTSocket() << endl;
- return 1;
- }
- }
-
-#ifndef _WIN32
- if (cfg.timeout_mode == 1 && cfg.timeout > 0)
- {
- if (!cfg.quiet)
- cerr << "TIMEOUT: cancel\n";
- alarm(0);
- }
-#endif
- }
- }
-
- default:
- {
- // No-Op
- }
- break;
- }
- }
-
- if (doabort)
- {
- break;
- }
-
- // read a few chunks at a time in attempt to deplete
- // read buffers as much as possible on each read event
- // note that this implies live streams and does not
- // work for cached/file sources
- std::list<std::shared_ptr<MediaPacket>> dataqueue;
- if (src.get() && src->IsOpen() && (srtrfdslen || sysrfdslen))
- {
- while (dataqueue.size() < cfg.buffering)
- {
- std::shared_ptr<MediaPacket> pkt(new MediaPacket(transmit_chunk_size));
- const int res = src->Read(transmit_chunk_size, *pkt, out_stats);
-
- if (res == SRT_ERROR && src->uri.type() == UriParser::SRT)
- {
- if (srt_getlasterror(NULL) == SRT_EASYNCRCV)
- break;
-
- throw std::runtime_error(
- string("error: recvmsg: ") + string(srt_getlasterror_str())
- );
- }
-
- if (res == 0 || pkt->payload.empty())
- {
- break;
- }
-
- dataqueue.push_back(pkt);
- receivedBytes += pkt->payload.size();
- }
- }
-
- // if there is no target, let the received data be lost
- while (!dataqueue.empty())
- {
- std::shared_ptr<MediaPacket> pkt = dataqueue.front();
- if (!tar.get() || !tar->IsOpen())
- {
- lostBytes += pkt->payload.size();
- }
- else if (!tar->Write(pkt->payload.data(), pkt->payload.size(), cfg.srctime ? pkt->time : 0, out_stats))
- {
- lostBytes += pkt->payload.size();
- }
- else
- {
- wroteBytes += pkt->payload.size();
- }
-
- dataqueue.pop_front();
- }
-
- if (!cfg.quiet && (lastReportedtLostBytes != lostBytes))
- {
- std::time_t now(std::time(nullptr));
- if (std::difftime(now, writeErrorLogTimer) >= 5.0)
- {
- cerr << lostBytes << " bytes lost, "
- << wroteBytes << " bytes sent, "
- << receivedBytes << " bytes received"
- << endl;
- writeErrorLogTimer = now;
- lastReportedtLostBytes = lostBytes;
- }
- }
- }
- }
- }
- catch (std::exception& x)
- {
- cerr << "ERROR: " << x.what() << endl;
- return 255;
- }
-
- return 0;
-}
-
-// Class utilities
-
-
-void TestLogHandler(void* opaque, int level, const char* file, int line, const char* area, const char* message)
-{
- char prefix[100] = "";
- if ( opaque )
- strncpy(prefix, (char*)opaque, 99);
- time_t now;
- time(&now);
- char buf[1024];
- struct tm local = SysLocalTime(now);
- size_t pos = strftime(buf, 1024, "[%c ", &local);
-
-#ifdef _MSC_VER
- // That's something weird that happens on Microsoft Visual Studio 2013
- // Trying to keep portability, while every version of MSVS is a different plaform.
- // On MSVS 2015 there's already a standard-compliant snprintf, whereas _snprintf
- // is available on backward compatibility and it doesn't work exactly the same way.
-#define snprintf _snprintf
-#endif
- snprintf(buf+pos, 1024-pos, "%s:%d(%s)]{%d} %s", file, line, area, level, message);
-
- cerr << buf << endl;
-}
+++ /dev/null
-// MSVS likes to complain about lots of standard C functions being unsafe.
-#ifdef _MSC_VER
-#define _CRT_SECURE_NO_WARNINGS 1
-#include <io.h>
-#endif
-
-#include "platform_sys.h"
-
-#define REQUIRE_CXX11 1
-
-#include <cctype>
-#include <iostream>
-#include <fstream>
-#include <string>
-#include <map>
-#include <set>
-#include <vector>
-#include <deque>
-#include <memory>
-#include <algorithm>
-#include <iterator>
-#include <stdexcept>
-#include <cstring>
-#include <csignal>
-#include <chrono>
-#include <thread>
-#include <mutex>
-#include <condition_variable>
-
-#include "apputil.hpp" // CreateAddr
-#include "uriparser.hpp" // UriParser
-#include "socketoptions.hpp"
-#include "logsupport.hpp"
-#include "transmitbase.hpp" // bytevector typedef to avoid collisions
-#include "verbose.hpp"
-
-// NOTE: This is without "haisrt/" because it uses an internal path
-// to the library. Application using the "installed" library should
-// use <srt/srt.h>
-#include <srt.h>
-#include <udt.h> // This TEMPORARILY contains extra C++-only SRT API.
-#include <logging.h>
-#include <api.h>
-#include <utilities.h>
-
-/*
-# MAF contents for this file. Note that not every file from the support
-# library is used, but to simplify the build definition it links against
-# the whole srtsupport library.
-
-SOURCES
-srt-test-tunnel.cpp
-testmedia.cpp
-../apps/verbose.cpp
-../apps/socketoptions.cpp
-../apps/uriparser.cpp
-../apps/logsupport.cpp
-
-*/
-
-using namespace std;
-using namespace srt;
-
-const srt_logging::LogFA SRT_LOGFA_APP = 10;
-namespace srt_logging
-{
-Logger applog(SRT_LOGFA_APP, srt_logger_config, "TUNNELAPP");
-}
-
-using srt_logging::applog;
-
-class Medium
-{
- static int s_counter;
- int m_counter;
-public:
- enum ReadStatus
- {
- RD_DATA, RD_AGAIN, RD_EOF, RD_ERROR
- };
-
- enum Mode
- {
- LISTENER, CALLER
- };
-
-protected:
- UriParser m_uri;
- size_t m_chunk = 0;
- map<string, string> m_options;
- Mode m_mode;
-
- bool m_listener = false;
- bool m_open = false;
- bool m_eof = false;
- bool m_broken = false;
-
- std::mutex access; // For closing
-
- template <class DerivedMedium, class SocketType>
- static Medium* CreateAcceptor(DerivedMedium* self, const sockaddr_any& sa, SocketType sock, size_t chunk)
- {
- string addr = sockaddr_any(sa.get(), sizeof sa).str();
- DerivedMedium* m = new DerivedMedium(UriParser(self->type() + string("://") + addr), chunk);
- m->m_socket = sock;
- return m;
- }
-
-public:
-
- string uri() { return m_uri.uri(); }
- string id()
- {
- std::ostringstream os;
- os << type() << m_counter;
- return os.str();
- }
-
- Medium(const UriParser& u, size_t ch): m_counter(s_counter++), m_uri(u), m_chunk(ch) {}
- Medium(): m_counter(s_counter++) {}
-
- virtual const char* type() = 0;
- virtual bool IsOpen() = 0;
- virtual void CloseInternal() = 0;
-
- void CloseState()
- {
- m_open = false;
- m_broken = true;
- }
-
- // External API for this class that allows to close
- // the entity on request. The CloseInternal should
- // redirect to a type-specific function, the same that
- // should be also called in destructor.
- void Close()
- {
- CloseState();
- CloseInternal();
- }
- virtual bool End() = 0;
-
- virtual int ReadInternal(char* output, int size) = 0;
- virtual bool IsErrorAgain() = 0;
-
- ReadStatus Read(bytevector& output);
- virtual void Write(bytevector& portion) = 0;
-
- virtual void CreateListener() = 0;
- virtual void CreateCaller() = 0;
- virtual unique_ptr<Medium> Accept() = 0;
- virtual void Connect() = 0;
-
- static std::unique_ptr<Medium> Create(const std::string& url, size_t chunk, Mode);
-
- virtual bool Broken() = 0;
- virtual size_t Still() { return 0; }
-
- class ReadEOF: public std::runtime_error
- {
- public:
- ReadEOF(const std::string& fn): std::runtime_error( "EOF while reading file: " + fn )
- {
- }
- };
-
- class TransmissionError: public std::runtime_error
- {
- public:
- TransmissionError(const std::string& fn): std::runtime_error( fn )
- {
- }
- };
-
- static void Error(const string& text)
- {
- throw TransmissionError("ERROR (internal): " + text);
- }
-
- virtual ~Medium()
- {
- CloseState();
- }
-
-protected:
- void InitMode(Mode m)
- {
- m_mode = m;
- Init();
-
- if (m_mode == LISTENER)
- {
- CreateListener();
- m_listener = true;
- }
- else
- {
- CreateCaller();
- }
-
- m_open = true;
- }
-
- virtual void Init() {}
-
-};
-
-class Engine
-{
- Medium* media[2];
- std::thread thr;
- class Tunnel* parent_tunnel;
- std::string nameid;
-
- int status = 0;
- Medium::ReadStatus rdst = Medium::RD_ERROR;
- UDT::ERRORINFO srtx;
-
-public:
- enum Dir { DIR_IN, DIR_OUT };
-
- int stat() { return status; }
-
- Engine(Tunnel* p, Medium* m1, Medium* m2, const std::string& nid)
- :
-#ifdef HAVE_FULL_CXX11
- media {m1, m2},
-#endif
- parent_tunnel(p), nameid(nid)
- {
-#ifndef HAVE_FULL_CXX11
- // MSVC is not exactly C++11 compliant and complains around
- // initialization of an array.
- // Leaving this method of initialization for clarity and
- // possibly more preferred performance.
- media[0] = m1;
- media[1] = m2;
-#endif
- }
-
- void Start()
- {
- Verb() << "START: " << media[DIR_IN]->uri() << " --> " << media[DIR_OUT]->uri();
- const std::string thrn = media[DIR_IN]->id() + ">" + media[DIR_OUT]->id();
- srt::ThreadName tn(thrn);
-
- thr = thread([this]() { Worker(); });
- }
-
- void Stop()
- {
- // If this thread is already stopped, don't stop.
- if (thr.joinable())
- {
- LOGP(applog.Debug, "Engine::Stop: Closing media:");
- // Close both media as a hanged up reading thread
- // will block joining.
- media[0]->Close();
- media[1]->Close();
-
- LOGP(applog.Debug, "Engine::Stop: media closed, joining engine thread:");
- if (thr.get_id() == std::this_thread::get_id())
- {
- // If this is this thread which called this, no need
- // to stop because this thread will exit by itself afterwards.
- // You must, however, detach yourself, or otherwise the thr's
- // destructor would kill the program.
- thr.detach();
- LOGP(applog.Debug, "DETACHED.");
- }
- else
- {
- thr.join();
- LOGP(applog.Debug, "Joined.");
- }
- }
- }
-
- void Worker();
-};
-
-
-struct Tunnelbox;
-
-class Tunnel
-{
- Tunnelbox* parent_box;
- std::unique_ptr<Medium> med_acp, med_clr;
- Engine acp_to_clr, clr_to_acp;
- srt::sync::atomic<bool> running{true};
- std::mutex access;
-
-public:
-
- string show()
- {
- return med_acp->uri() + " <-> " + med_clr->uri();
- }
-
- Tunnel(Tunnelbox* m, std::unique_ptr<Medium>&& acp, std::unique_ptr<Medium>&& clr):
- parent_box(m),
- med_acp(move(acp)), med_clr(move(clr)),
- acp_to_clr(this, med_acp.get(), med_clr.get(), med_acp->id() + ">" + med_clr->id()),
- clr_to_acp(this, med_clr.get(), med_acp.get(), med_clr->id() + ">" + med_acp->id())
- {
- }
-
- void Start()
- {
- acp_to_clr.Start();
- clr_to_acp.Start();
- }
-
- // This is to be called by an Engine from Engine::Worker
- // thread.
- // [[affinity = acp_to_clr.thr || clr_to_acp.thr]];
- void decommission_engine(Medium* which_medium)
- {
- // which_medium is the medium that failed.
- // Upon breaking of one medium from the pair,
- // the other needs to be closed as well.
- Verb() << "Medium broken: " << which_medium->uri();
-
- bool stop = true;
-
- /*
- {
- lock_guard<std::mutex> lk(access);
- if (acp_to_clr.stat() == -1 && clr_to_acp.stat() == -1)
- {
- Verb() << "Tunnel: Both engine decommissioned, will stop the tunnel.";
- // Both engines are down, decommission the tunnel.
- // Note that the status -1 means that particular engine
- // is not currently running and you can safely
- // join its thread.
- stop = true;
- }
- else
- {
- Verb() << "Tunnel: Decommissioned one engine, waiting for the other one to report";
- }
- }
- */
-
- if (stop)
- {
- // First, stop all media.
- med_acp->Close();
- med_clr->Close();
-
- // Then stop the tunnel (this is only a signal
- // to a cleanup thread to delete it).
- Stop();
- }
- }
-
- void Stop();
-
- bool decommission_if_dead(bool forced); // [[affinity = g_tunnels.thr]]
-};
-
-void Engine::Worker()
-{
- bytevector outbuf;
-
- Medium* which_medium = media[DIR_IN];
-
- for (;;)
- {
- try
- {
- which_medium = media[DIR_IN];
- rdst = media[DIR_IN]->Read((outbuf));
- switch (rdst)
- {
- case Medium::RD_DATA:
- {
- which_medium = media[DIR_OUT];
- // We get the data, write them to the output
- media[DIR_OUT]->Write((outbuf));
- }
- break;
-
- case Medium::RD_EOF:
- status = -1;
- throw Medium::ReadEOF("");
-
- case Medium::RD_AGAIN:
- // Theoreticall RD_AGAIN should not be reported
- // because it should be taken care of internally by
- // repeated sending - unless we get m_broken set.
- // If it is, however, it should be handled just like error.
- case Medium::RD_ERROR:
- status = -1;
- Medium::Error("Error while reading");
- }
- }
- catch (Medium::ReadEOF&)
- {
- Verb() << "EOF. Exiting engine.";
- break;
- }
- catch (Medium::TransmissionError& er)
- {
- Verb() << er.what() << " - interrupting engine: " << nameid;
- break;
- }
- }
-
- // This is an engine thread and it should simply
- // tell the parent_box Tunnel that it is no longer
- // operative. It's not necessary to inform it which
- // of two engines is decommissioned - it should only
- // know that one of them got down. It will then check
- // if both are down here and decommission the whole
- // tunnel if so.
- parent_tunnel->decommission_engine(which_medium);
-}
-
-class SrtMedium: public Medium
-{
- SRTSOCKET m_socket = SRT_ERROR;
- friend class Medium;
-public:
-
-#ifdef HAVE_FULL_CXX11
- using Medium::Medium;
-
-#else // MSVC and gcc 4.7 not exactly support C++11
-
- SrtMedium(UriParser u, size_t ch): Medium(u, ch) {}
-
-#endif
-
- bool IsOpen() override { return m_open; }
- bool End() override { return m_eof; }
- bool Broken() override { return m_broken; }
-
- void CloseSrt()
- {
- Verb() << "Closing SRT socket for " << uri();
- lock_guard<std::mutex> lk(access);
- if (m_socket == SRT_ERROR)
- return;
- srt_close(m_socket);
- m_socket = SRT_ERROR;
- }
-
- // Forwarded in order to separate the implementation from
- // the virtual function so that virtual function is not
- // being called in destructor.
- void CloseInternal() override { return CloseSrt(); }
-
- const char* type() override { return "srt"; }
- int ReadInternal(char* output, int size) override;
- bool IsErrorAgain() override;
-
- void Write(bytevector& portion) override;
- void CreateListener() override;
- void CreateCaller() override;
- unique_ptr<Medium> Accept() override;
- void Connect() override;
-
-protected:
- void Init() override;
-
- void ConfigurePre();
- void ConfigurePost(SRTSOCKET socket);
-
- using Medium::Error;
-
- static void Error(UDT::ERRORINFO& ri, const string& text)
- {
- throw TransmissionError("ERROR: " + text + ": " + ri.getErrorMessage());
- }
-
- ~SrtMedium() override
- {
- CloseState();
- CloseSrt();
- }
-};
-
-class TcpMedium: public Medium
-{
- int m_socket = -1;
- friend class Medium;
-public:
-
-#ifdef HAVE_FULL_CXX11
- using Medium::Medium;
-
-#else // MSVC not exactly supports C++11
-
- TcpMedium(UriParser u, size_t ch): Medium(u, ch) {}
-
-#endif
-
-#ifdef _WIN32
- static int tcp_close(int socket)
- {
- return ::closesocket(socket);
- }
-
- enum { DEF_SEND_FLAG = 0 };
-
-#elif defined(LINUX) || defined(GNU) || defined(CYGWIN)
- static int tcp_close(int socket)
- {
- return ::close(socket);
- }
-
- enum { DEF_SEND_FLAG = MSG_NOSIGNAL };
-
-#else
- static int tcp_close(int socket)
- {
- return ::close(socket);
- }
-
- enum { DEF_SEND_FLAG = 0 };
-
-#endif
-
- bool IsOpen() override { return m_open; }
- bool End() override { return m_eof; }
- bool Broken() override { return m_broken; }
-
- void CloseTcp()
- {
- Verb() << "Closing TCP socket for " << uri();
- lock_guard<std::mutex> lk(access);
- if (m_socket == -1)
- return;
- tcp_close(m_socket);
- m_socket = -1;
- }
- void CloseInternal() override { return CloseTcp(); }
-
- const char* type() override { return "tcp"; }
- int ReadInternal(char* output, int size) override;
- bool IsErrorAgain() override;
- void Write(bytevector& portion) override;
- void CreateListener() override;
- void CreateCaller() override;
- unique_ptr<Medium> Accept() override;
- void Connect() override;
-
-protected:
-
- void ConfigurePre()
- {
-#if defined(__APPLE__)
- int optval = 1;
- setsockopt(m_socket, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval));
-#endif
- }
-
- void ConfigurePost(int)
- {
- }
-
- using Medium::Error;
-
- static void Error(int verrno, const string& text)
- {
- char rbuf[1024];
- throw TransmissionError("ERROR: " + text + ": " + SysStrError(verrno, rbuf, 1024));
- }
-
- virtual ~TcpMedium()
- {
- CloseState();
- CloseTcp();
- }
-};
-
-void SrtMedium::Init()
-{
- // This function is required due to extra option
- // check need
-
- if (m_options.count("mode"))
- Error("No option 'mode' is required, it defaults to position of the argument");
-
- if (m_options.count("blocking"))
- Error("Blocking is not configurable here.");
-
- // XXX
- // Look also for other options that should not be here.
-
- // Enforce the transtype = file
- m_options["transtype"] = "file";
-}
-
-void SrtMedium::ConfigurePre()
-{
- vector<string> fails;
- m_options["mode"] = "caller";
- SrtConfigurePre(m_socket, "", m_options, &fails);
- if (!fails.empty())
- {
- cerr << "Failed options: " << Printable(fails) << endl;
- }
-}
-
-void SrtMedium::ConfigurePost(SRTSOCKET so)
-{
- vector<string> fails;
- SrtConfigurePost(so, m_options, &fails);
- if (!fails.empty())
- {
- cerr << "Failed options: " << Printable(fails) << endl;
- }
-}
-
-void SrtMedium::CreateListener()
-{
- int backlog = 5; // hardcoded!
-
- m_socket = srt_create_socket();
-
- ConfigurePre();
-
- sockaddr_any sa = CreateAddr(m_uri.host(), m_uri.portno());
-
- int stat = srt_bind(m_socket, sa.get(), sizeof sa);
-
- if ( stat == SRT_ERROR )
- {
- srt_close(m_socket);
- Error(UDT::getlasterror(), "srt_bind");
- }
-
- stat = srt_listen(m_socket, backlog);
- if ( stat == SRT_ERROR )
- {
- srt_close(m_socket);
- Error(UDT::getlasterror(), "srt_listen");
- }
-
- m_listener = true;
-};
-
-void TcpMedium::CreateListener()
-{
- int backlog = 5; // hardcoded!
-
-
- sockaddr_any sa = CreateAddr(m_uri.host(), m_uri.portno());
-
- m_socket = socket(sa.get()->sa_family, SOCK_STREAM, IPPROTO_TCP);
- ConfigurePre();
-
- int stat = ::bind(m_socket, sa.get(), sa.size());
-
- if (stat == -1)
- {
- tcp_close(m_socket);
- Error(errno, "bind");
- }
-
- stat = listen(m_socket, backlog);
- if ( stat == -1 )
- {
- tcp_close(m_socket);
- Error(errno, "listen");
- }
-
- m_listener = true;
-}
-
-unique_ptr<Medium> SrtMedium::Accept()
-{
- sockaddr_any sa;
- SRTSOCKET s = srt_accept(m_socket, (sa.get()), (&sa.len));
- if (s == SRT_ERROR)
- {
- Error(UDT::getlasterror(), "srt_accept");
- }
-
- ConfigurePost(s);
-
- // Configure 1s timeout
- int timeout_1s = 1000;
- srt_setsockflag(m_socket, SRTO_RCVTIMEO, &timeout_1s, sizeof timeout_1s);
-
- unique_ptr<Medium> med(CreateAcceptor(this, sa, s, m_chunk));
- Verb() << "accepted a connection from " << med->uri();
-
- return med;
-}
-
-unique_ptr<Medium> TcpMedium::Accept()
-{
- sockaddr_any sa;
- int s = ::accept(m_socket, (sa.get()), (&sa.syslen()));
- if (s == -1)
- {
- Error(errno, "accept");
- }
-
- // Configure 1s timeout
- timeval timeout_1s { 1, 0 };
- int st SRT_ATR_UNUSED = setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout_1s, sizeof timeout_1s);
- timeval re;
- socklen_t size = sizeof re;
- int st2 SRT_ATR_UNUSED = getsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*)&re, &size);
-
- LOGP(applog.Debug, "Setting SO_RCVTIMEO to @", m_socket, ": ", st == -1 ? "FAILED" : "SUCCEEDED",
- ", read-back value: ", st2 == -1 ? int64_t(-1) : (int64_t(re.tv_sec)*1000000 + re.tv_usec)/1000, "ms");
-
- unique_ptr<Medium> med(CreateAcceptor(this, sa, s, m_chunk));
- Verb() << "accepted a connection from " << med->uri();
-
- return med;
-}
-
-void SrtMedium::CreateCaller()
-{
- m_socket = srt_create_socket();
- ConfigurePre();
-
- // XXX setting up outgoing port not supported
-}
-
-void TcpMedium::CreateCaller()
-{
- m_socket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- ConfigurePre();
-}
-
-void SrtMedium::Connect()
-{
- sockaddr_any sa = CreateAddr(m_uri.host(), m_uri.portno());
-
- int st = srt_connect(m_socket, sa.get(), sizeof sa);
- if (st == SRT_ERROR)
- Error(UDT::getlasterror(), "srt_connect");
-
- ConfigurePost(m_socket);
-
- // Configure 1s timeout
- int timeout_1s = 1000;
- srt_setsockflag(m_socket, SRTO_RCVTIMEO, &timeout_1s, sizeof timeout_1s);
-}
-
-void TcpMedium::Connect()
-{
- sockaddr_any sa = CreateAddr(m_uri.host(), m_uri.portno());
-
- int st = ::connect(m_socket, sa.get(), sa.size());
- if (st == -1)
- Error(errno, "connect");
-
- ConfigurePost(m_socket);
-
- // Configure 1s timeout
- timeval timeout_1s { 1, 0 };
- setsockopt(m_socket, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout_1s, sizeof timeout_1s);
-}
-
-int SrtMedium::ReadInternal(char* w_buffer, int size)
-{
- int st = -1;
- do
- {
- st = srt_recv(m_socket, (w_buffer), size);
- if (st == SRT_ERROR)
- {
- int syserr;
- if (srt_getlasterror(&syserr) == SRT_EASYNCRCV && !m_broken)
- continue;
- }
- break;
-
- } while (true);
-
- return st;
-}
-
-int TcpMedium::ReadInternal(char* w_buffer, int size)
-{
- int st = -1;
- LOGP(applog.Debug, "TcpMedium:recv @", m_socket, " - begin");
- do
- {
- st = ::recv(m_socket, (w_buffer), size, 0);
- if (st == -1)
- {
- if ((errno == EAGAIN || errno == EWOULDBLOCK))
- {
- if (!m_broken)
- {
- LOGP(applog.Debug, "TcpMedium: read:AGAIN, repeating");
- continue;
- }
- LOGP(applog.Debug, "TcpMedium: read:AGAIN, not repeating - already broken");
- }
- else
- {
- LOGP(applog.Debug, "TcpMedium: read:ERROR: ", errno);
- }
- }
- break;
- } while (true);
- LOGP(applog.Debug, "TcpMedium:recv @", m_socket, " - result: ", st);
- return st;
-}
-
-bool SrtMedium::IsErrorAgain()
-{
- return srt_getlasterror(NULL) == SRT_EASYNCRCV;
-}
-
-bool TcpMedium::IsErrorAgain()
-{
- return errno == EAGAIN;
-}
-
-// The idea of Read function is to get the buffer that
-// possibly contains some data not written to the output yet,
-// but the time has come to read. We can't let the buffer expand
-// more than the size of the chunk, so if the buffer size already
-// exceeds it, don't return any data, but behave as if they were read.
-// This will cause the worker loop to redirect to Write immediately
-// thereafter and possibly will flush out the remains of the buffer.
-// It's still possible that the buffer won't be completely purged
-Medium::ReadStatus Medium::Read(bytevector& w_output)
-{
- // Don't read, but fake that you read
- if (w_output.size() > m_chunk)
- {
- Verb() << "BUFFER EXCEEDED";
- return RD_DATA;
- }
-
- // Resize to maximum first
- size_t shift = w_output.size();
- if (shift && m_eof)
- {
- // You have nonempty buffer, but eof was already
- // encountered. Report as if something was read.
- //
- // Don't read anything because this will surely
- // result in error since now.
- return RD_DATA;
- }
-
- size_t pred_size = shift + m_chunk;
-
- w_output.resize(pred_size);
- int st = ReadInternal((w_output.data() + shift), m_chunk);
- if (st == -1)
- {
- if (IsErrorAgain())
- return RD_AGAIN;
-
- return RD_ERROR;
- }
-
- if (st == 0)
- {
- m_eof = true;
- if (shift)
- {
- // If there's 0 (eof), but you still have data
- // in the buffer, fake that they were read. Only
- // when the buffer was empty at entrance should this
- // result with EOF.
- //
- // Set back the size this buffer had before we attempted
- // to read into it.
- w_output.resize(shift);
- return RD_DATA;
- }
- w_output.clear();
- return RD_EOF;
- }
-
- w_output.resize(shift+st);
- return RD_DATA;
-}
-
-void SrtMedium::Write(bytevector& w_buffer)
-{
- int st = srt_send(m_socket, w_buffer.data(), w_buffer.size());
- if (st == SRT_ERROR)
- {
- Error(UDT::getlasterror(), "srt_send");
- }
-
- // This should be ==, whereas > is not possible, but
- // this should simply embrace this case as a sanity check.
- if (st >= int(w_buffer.size()))
- w_buffer.clear();
- else if (st == 0)
- {
- Error("Unexpected EOF on Write");
- }
- else
- {
- // Remove only those bytes that were sent
- w_buffer.erase(w_buffer.begin(), w_buffer.begin()+st);
- }
-}
-
-void TcpMedium::Write(bytevector& w_buffer)
-{
- int st = ::send(m_socket, w_buffer.data(), w_buffer.size(), DEF_SEND_FLAG);
- if (st == -1)
- {
- Error(errno, "send");
- }
-
- // This should be ==, whereas > is not possible, but
- // this should simply embrace this case as a sanity check.
- if (st >= int(w_buffer.size()))
- w_buffer.clear();
- else if (st == 0)
- {
- Error("Unexpected EOF on Write");
- }
- else
- {
- // Remove only those bytes that were sent
- w_buffer.erase(w_buffer.begin(), w_buffer.begin()+st);
- }
-}
-
-std::unique_ptr<Medium> Medium::Create(const std::string& url, size_t chunk, Medium::Mode mode)
-{
- UriParser uri(url);
- std::unique_ptr<Medium> out;
-
- // Might be something smarter, but there are only 2 types.
- if (uri.scheme() == "srt")
- {
- out.reset(new SrtMedium(uri, chunk));
- }
- else if (uri.scheme() == "tcp")
- {
- out.reset(new TcpMedium(uri, chunk));
- }
- else
- {
- Error("Medium not supported");
- }
-
- out->InitMode(mode);
-
- return out;
-}
-
-struct Tunnelbox
-{
- list<unique_ptr<Tunnel>> tunnels;
- std::mutex access;
- condition_variable decom_ready;
- bool main_running = true;
- thread thr;
-
- void signal_decommission()
- {
- lock_guard<std::mutex> lk(access);
- decom_ready.notify_one();
- }
-
- void install(std::unique_ptr<Medium>&& acp, std::unique_ptr<Medium>&& clr)
- {
- lock_guard<std::mutex> lk(access);
- Verb() << "Tunnelbox: Starting tunnel: " << acp->uri() << " <-> " << clr->uri();
-
- tunnels.emplace_back(new Tunnel(this, move(acp), move(clr)));
- // Note: after this instruction, acp and clr are no longer valid!
- auto& it = tunnels.back();
-
- it->Start();
- }
-
- void start_cleaner()
- {
- thr = thread( [this]() { CleanupWorker(); } );
- }
-
- void stop_cleaner()
- {
- if (thr.joinable())
- thr.join();
- }
-
-private:
-
- void CleanupWorker()
- {
- unique_lock<std::mutex> lk(access);
-
- while (main_running)
- {
- decom_ready.wait(lk);
-
- // Got a signal, find a tunnel ready to cleanup.
- // We just get the signal, but we don't know which
- // tunnel has generated it.
- for (auto i = tunnels.begin(), i_next = i; i != tunnels.end(); i = i_next)
- {
- ++i_next;
- // Bound in one call the check if the tunnel is dead
- // and decommissioning because this must be done in
- // the one critical section - make sure no other thread
- // is accessing it at the same time and also make join all
- // threads that might have been accessing it. After
- // exiting as true (meaning that it was decommissioned
- // as expected) it can be safely deleted.
- if ((*i)->decommission_if_dead(main_running))
- {
- tunnels.erase(i);
- }
- }
- }
- }
-};
-
-void Tunnel::Stop()
-{
- // Check for running must be done without locking
- // because if the tunnel isn't running
- if (!running)
- return; // already stopped
-
- lock_guard<std::mutex> lk(access);
-
- // Ok, you are the first to make the tunnel
- // not running and inform the tunnelbox.
- running = false;
- parent_box->signal_decommission();
-}
-
-bool Tunnel::decommission_if_dead(bool forced)
-{
- lock_guard<std::mutex> lk(access);
- if (running && !forced)
- return false; // working, not to be decommissioned
-
- // Join the engine threads, make sure nothing
- // is running that could use the data.
- acp_to_clr.Stop();
- clr_to_acp.Stop();
-
-
- // Done. The tunnelbox after calling this can
- // safely delete the decommissioned tunnel.
- return true;
-}
-
-int Medium::s_counter = 1;
-
-Tunnelbox g_tunnels;
-std::unique_ptr<Medium> main_listener;
-
-size_t default_chunk = 4096;
-
-int OnINT_StopService(int)
-{
- g_tunnels.main_running = false;
- g_tunnels.signal_decommission();
-
- // Will cause the Accept() block to exit.
- main_listener->Close();
-
- return 0;
-}
-
-int main( int argc, char** argv )
-{
- if (!SysInitializeNetwork())
- {
- cerr << "Fail to initialize network module.";
- return 1;
- }
-
- size_t chunk = default_chunk;
-
- OptionName
- o_loglevel = { "ll", "loglevel" },
- o_logfa = { "lf", "logfa" },
- o_chunk = {"c", "chunk" },
- o_verbose = {"v", "verbose" },
- o_noflush = {"s", "skipflush" };
-
- // Options that expect no arguments (ARG_NONE) need not be mentioned.
- vector<OptionScheme> optargs = {
- { o_loglevel, OptionScheme::ARG_ONE },
- { o_logfa, OptionScheme::ARG_ONE },
- { o_chunk, OptionScheme::ARG_ONE }
- };
- options_t params = ProcessOptions(argv, argc, optargs);
-
- /*
- cerr << "OPTIONS (DEBUG)\n";
- for (auto o: params)
- {
- cerr << "[" << o.first << "] ";
- copy(o.second.begin(), o.second.end(), ostream_iterator<string>(cerr, " "));
- cerr << endl;
- }
- */
-
- vector<string> args = params[""];
- if ( args.size() < 2 )
- {
- cerr << "Usage: " << argv[0] << " <listen-uri> <call-uri>\n";
- return 1;
- }
-
- string loglevel = Option<OutString>(params, "error", o_loglevel);
- string logfa = Option<OutString>(params, "", o_logfa);
- srt_logging::LogLevel::type lev = SrtParseLogLevel(loglevel);
- srt::setloglevel(lev);
- if (logfa == "")
- {
- srt::addlogfa(SRT_LOGFA_APP);
- }
- else
- {
- // Add only selected FAs
- set<string> unknown_fas;
- set<srt_logging::LogFA> fas = SrtParseLogFA(logfa, &unknown_fas);
- srt::resetlogfa(fas);
-
- // The general parser doesn't recognize the "app" FA, we check it here.
- if (unknown_fas.count("app"))
- srt::addlogfa(SRT_LOGFA_APP);
- }
-
- string verbo = Option<OutString>(params, "no", o_verbose);
- if ( verbo == "" || !false_names.count(verbo) )
- {
- Verbose::on = true;
- Verbose::cverb = &std::cout;
- }
-
- string chunks = Option<OutString>(params, "", o_chunk);
- if ( chunks!= "" )
- {
- chunk = stoi(chunks);
- }
-
- string listen_node = args[0];
- string call_node = args[1];
-
- UriParser ul(listen_node), uc(call_node);
-
- // It is allowed to use both media of the same type,
- // but only srt and tcp are allowed.
-
- set<string> allowed = {"srt", "tcp"};
- if (!allowed.count(ul.scheme())|| !allowed.count(uc.scheme()))
- {
- cerr << "ERROR: only tcp and srt schemes supported";
- return -1;
- }
-
- Verb() << "LISTEN type=" << ul.scheme() << ", CALL type=" << uc.scheme();
-
- g_tunnels.start_cleaner();
-
- main_listener = Medium::Create(listen_node, chunk, Medium::LISTENER);
-
- // The main program loop is only to catch
- // new connections and manage them. Also takes care
- // of the broken connections.
-
- for (;;)
- {
- try
- {
- Verb() << "Waiting for connection...";
- std::unique_ptr<Medium> accepted = main_listener->Accept();
- if (!g_tunnels.main_running)
- {
- Verb() << "Service stopped. Exiting.";
- break;
- }
- Verb() << "Connection accepted. Connecting to the relay...";
-
- // Now call the target address.
- std::unique_ptr<Medium> caller = Medium::Create(call_node, chunk, Medium::CALLER);
- caller->Connect();
-
- Verb() << "Connected. Establishing pipe.";
-
- // No exception, we are free to pass :)
- g_tunnels.install(move(accepted), move(caller));
- }
- catch (...)
- {
- Verb() << "Connection reported, but failed";
- }
- }
-
- g_tunnels.stop_cleaner();
-
- return 0;
-}
-
-
+++ /dev/null
-/*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2018 Haivision Systems Inc.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- */
-
-#include <string>
-#include <chrono>
-#include <iostream>
-#include <iomanip>
-#include <sstream>
-#include <utility>
-#include <memory>
-
-#include "statswriter.hpp"
-#include "netinet_any.h"
-#include "srt_compat.h"
-
-// Note: std::put_time is supported only in GCC 5 and higher
-#if !defined(__GNUC__) || defined(__clang__) || (__GNUC__ >= 5)
-#define HAS_PUT_TIME
-#endif
-
-using namespace std;
-
-
-template <class TYPE>
-inline SrtStatData* make_stat(SrtStatCat cat, const string& name, const string& longname,
- TYPE CBytePerfMon::*field)
-{
- return new SrtStatDataType<TYPE>(cat, name, longname, field);
-}
-
-#define STATX(catsuf, sname, lname, field) s.emplace_back(make_stat(SSC_##catsuf, #sname, #lname, &CBytePerfMon:: field))
-#define STAT(catsuf, sname, field) STATX(catsuf, sname, field, field)
-
-vector<unique_ptr<SrtStatData>> g_SrtStatsTable;
-
-struct SrtStatsTableInit
-{
- SrtStatsTableInit(vector<unique_ptr<SrtStatData>>& s)
- {
- STATX(GEN, time, Time, msTimeStamp);
-
- STAT(WINDOW, flow, pktFlowWindow);
- STAT(WINDOW, congestion, pktCongestionWindow);
- STAT(WINDOW, flight, pktFlightSize);
-
- STAT(LINK, rtt, msRTT);
- STAT(LINK, bandwidth, mbpsBandwidth);
- STAT(LINK, maxBandwidth, mbpsMaxBW);
-
- STAT(SEND, packets, pktSent);
- STAT(SEND, packetsUnique, pktSentUnique);
- STAT(SEND, packetsLost, pktSndLoss);
- STAT(SEND, packetsDropped, pktSndDrop);
- STAT(SEND, packetsRetransmitted, pktRetrans);
- STAT(SEND, packetsFilterExtra, pktSndFilterExtra);
- STAT(SEND, bytes, byteSent);
- STAT(SEND, bytesUnique, byteSentUnique);
- STAT(SEND, bytesDropped, byteSndDrop);
- STAT(SEND, byteAvailBuf, byteAvailSndBuf);
- STAT(SEND, msBuf, msSndBuf);
- STAT(SEND, mbitRate, mbpsSendRate);
- STAT(SEND, sendPeriod, usPktSndPeriod);
-
- STAT(RECV, packets, pktRecv);
- STAT(RECV, packetsUnique, pktRecvUnique);
- STAT(RECV, packetsLost, pktRcvLoss);
- STAT(RECV, packetsDropped, pktRcvDrop);
- STAT(RECV, packetsRetransmitted, pktRcvRetrans);
- STAT(RECV, packetsBelated, pktRcvBelated);
- STAT(RECV, packetsFilterExtra, pktRcvFilterExtra);
- STAT(RECV, packetsFilterSupply, pktRcvFilterSupply);
- STAT(RECV, packetsFilterLoss, pktRcvFilterLoss);
- STAT(RECV, bytes, byteRecv);
- STAT(RECV, bytesUnique, byteRecvUnique);
- STAT(RECV, bytesLost, byteRcvLoss);
- STAT(RECV, bytesDropped, byteRcvDrop);
- STAT(RECV, byteAvailBuf, byteAvailRcvBuf);
- STAT(RECV, msBuf, msRcvBuf);
- STAT(RECV, mbitRate, mbpsRecvRate);
- STAT(RECV, msTsbPdDelay, msRcvTsbPdDelay);
- }
-} g_SrtStatsTableInit (g_SrtStatsTable);
-
-
-#undef STAT
-#undef STATX
-
-string srt_json_cat_names [] = {
- "",
- "window",
- "link",
- "send",
- "recv"
-};
-
-#ifdef HAS_PUT_TIME
-// Follows ISO 8601
-std::string SrtStatsWriter::print_timestamp()
-{
- using namespace std;
- using namespace std::chrono;
-
- const auto systime_now = system_clock::now();
- const time_t time_now = system_clock::to_time_t(systime_now);
-
- std::ostringstream output;
-
- // SysLocalTime returns zeroed tm_now on failure, which is ok for put_time.
- const tm tm_now = SysLocalTime(time_now);
- output << std::put_time(&tm_now, "%FT%T.") << std::setfill('0') << std::setw(6);
- const auto since_epoch = systime_now.time_since_epoch();
- const seconds s = duration_cast<seconds>(since_epoch);
- output << duration_cast<microseconds>(since_epoch - s).count();
- output << std::put_time(&tm_now, "%z");
- return output.str();
-}
-#else
-
-// This is a stub. The error when not defining it would be too
-// misleading, so this stub will work if someone mistakenly adds
-// the item to the output format without checking that HAS_PUT_TIME.
-string SrtStatsWriter::print_timestamp()
-{ return "<NOT IMPLEMENTED>"; }
-#endif // HAS_PUT_TIME
-
-
-class SrtStatsJson : public SrtStatsWriter
-{
- static string quotekey(const string& name)
- {
- if (name == "")
- return "";
-
- return R"(")" + name + R"(":)";
- }
-
- static string quote(const string& name)
- {
- if (name == "")
- return "";
-
- return R"(")" + name + R"(")";
- }
-
-public:
- string WriteStats(int sid, const CBytePerfMon& mon) override
- {
- std::ostringstream output;
-
- string pretty_cr, pretty_tab;
- if (Option("pretty"))
- {
- pretty_cr = "\n";
- pretty_tab = "\t";
- }
-
- SrtStatCat cat = SSC_GEN;
-
- // Do general manually
- output << quotekey(srt_json_cat_names[cat]) << "{" << pretty_cr;
-
- // SID is displayed manually
- output << pretty_tab << quotekey("sid") << sid;
-
- // Extra Timepoint is also displayed manually
-#ifdef HAS_PUT_TIME
- // NOTE: still assumed SSC_GEN category
- output << "," << pretty_cr << pretty_tab
- << quotekey("timepoint") << quote(print_timestamp());
-#endif
-
- // Now continue with fields as specified in the table
- for (auto& i: g_SrtStatsTable)
- {
- if (i->category == cat)
- {
- output << ","; // next item in same cat
- output << pretty_cr;
- output << pretty_tab;
- if (cat != SSC_GEN)
- output << pretty_tab;
- }
- else
- {
- if (cat != SSC_GEN)
- {
- // DO NOT close if general category, just
- // enter the depth.
- output << pretty_cr << pretty_tab << "}";
- }
- cat = i->category;
- output << ",";
- output << pretty_cr;
- if (cat != SSC_GEN)
- output << pretty_tab;
-
- output << quotekey(srt_json_cat_names[cat]) << "{" << pretty_cr << pretty_tab;
- if (cat != SSC_GEN)
- output << pretty_tab;
- }
-
- // Print the current field
- output << quotekey(i->name);
- i->PrintValue(output, mon);
- }
-
- // Close the previous subcategory
- if (cat != SSC_GEN)
- {
- output << pretty_cr << pretty_tab << "}" << pretty_cr;
- }
-
- // Close the general category entity
- output << "}" << pretty_cr << endl;
-
- return output.str();
- }
-
- string WriteBandwidth(double mbpsBandwidth) override
- {
- std::ostringstream output;
- output << "{\"bandwidth\":" << mbpsBandwidth << '}' << endl;
- return output.str();
- }
-};
-
-class SrtStatsCsv : public SrtStatsWriter
-{
-private:
- bool first_line_printed;
-
-public:
- SrtStatsCsv() : first_line_printed(false) {}
-
- string WriteStats(int sid, const CBytePerfMon& mon) override
- {
- std::ostringstream output;
-
- // Header
- if (!first_line_printed)
- {
-#ifdef HAS_PUT_TIME
- output << "Timepoint,";
-#endif
- output << "Time,SocketID";
-
- for (auto& i: g_SrtStatsTable)
- {
- output << "," << i->longname;
- }
- output << endl;
- first_line_printed = true;
- }
-
- // Values
-#ifdef HAS_PUT_TIME
- // HDR: Timepoint
- output << print_timestamp() << ",";
-#endif // HAS_PUT_TIME
-
- // HDR: Time,SocketID
- output << mon.msTimeStamp << "," << sid;
-
- // HDR: the loop of all values in g_SrtStatsTable
- for (auto& i: g_SrtStatsTable)
- {
- output << ",";
- i->PrintValue(output, mon);
- }
-
- output << endl;
- return output.str();
- }
-
- string WriteBandwidth(double mbpsBandwidth) override
- {
- std::ostringstream output;
- output << "+++/+++SRT BANDWIDTH: " << mbpsBandwidth << endl;
- return output.str();
- }
-};
-
-class SrtStatsCols : public SrtStatsWriter
-{
-public:
- string WriteStats(int sid, const CBytePerfMon& mon) override
- {
- std::ostringstream output;
- output << "======= SRT STATS: sid=" << sid << endl;
- output << "PACKETS SENT: " << setw(11) << mon.pktSent << " RECEIVED: " << setw(11) << mon.pktRecv << endl;
- output << "LOST PKT SENT: " << setw(11) << mon.pktSndLoss << " RECEIVED: " << setw(11) << mon.pktRcvLoss << endl;
- output << "REXMIT SENT: " << setw(11) << mon.pktRetrans << " RECEIVED: " << setw(11) << mon.pktRcvRetrans << endl;
- output << "DROP PKT SENT: " << setw(11) << mon.pktSndDrop << " RECEIVED: " << setw(11) << mon.pktRcvDrop << endl;
- output << "FILTER EXTRA TX: " << setw(11) << mon.pktSndFilterExtra << " RX: " << setw(11) << mon.pktRcvFilterExtra << endl;
- output << "FILTER RX SUPPL: " << setw(11) << mon.pktRcvFilterSupply << " RX LOSS: " << setw(11) << mon.pktRcvFilterLoss << endl;
- output << "RATE SENDING: " << setw(11) << mon.mbpsSendRate << " RECEIVING: " << setw(11) << mon.mbpsRecvRate << endl;
- output << "BELATED RECEIVED: " << setw(11) << mon.pktRcvBelated << " AVG TIME: " << setw(11) << mon.pktRcvAvgBelatedTime << endl;
- output << "REORDER DISTANCE: " << setw(11) << mon.pktReorderDistance << endl;
- output << "WINDOW FLOW: " << setw(11) << mon.pktFlowWindow << " CONGESTION: " << setw(11) << mon.pktCongestionWindow << " FLIGHT: " << setw(11) << mon.pktFlightSize << endl;
- output << "LINK RTT: " << setw(9) << mon.msRTT << "ms BANDWIDTH: " << setw(7) << mon.mbpsBandwidth << "Mb/s " << endl;
- output << "BUFFERLEFT: SND: " << setw(11) << mon.byteAvailSndBuf << " RCV: " << setw(11) << mon.byteAvailRcvBuf << endl;
- return output.str();
- }
-
- string WriteBandwidth(double mbpsBandwidth) override
- {
- std::ostringstream output;
- output << "+++/+++SRT BANDWIDTH: " << mbpsBandwidth << endl;
- return output.str();
- }
-};
-
-shared_ptr<SrtStatsWriter> SrtStatsWriterFactory(SrtStatsPrintFormat printformat)
-{
- switch (printformat)
- {
- case SRTSTATS_PROFMAT_JSON:
- return make_shared<SrtStatsJson>();
- case SRTSTATS_PROFMAT_CSV:
- return make_shared<SrtStatsCsv>();
- case SRTSTATS_PROFMAT_2COLS:
- return make_shared<SrtStatsCols>();
- default:
- break;
- }
- return nullptr;
-}
-
-SrtStatsPrintFormat ParsePrintFormat(string pf, string& w_extras)
-{
- size_t havecomma = pf.find(',');
- if (havecomma != string::npos)
- {
- w_extras = pf.substr(havecomma+1);
- pf = pf.substr(0, havecomma);
- }
-
- if (pf == "default")
- return SRTSTATS_PROFMAT_2COLS;
-
- if (pf == "json")
- return SRTSTATS_PROFMAT_JSON;
-
- if (pf == "csv")
- return SRTSTATS_PROFMAT_CSV;
-
- return SRTSTATS_PROFMAT_INVALID;
-}
+++ /dev/null
-/*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2018 Haivision Systems Inc.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- */
-
-
-#ifndef INC_SRT_APPS_STATSWRITER_H
-#define INC_SRT_APPS_STATSWRITER_H
-
-#include <string>
-#include <map>
-#include <vector>
-#include <memory>
-
-#include "srt.h"
-#include "utilities.h"
-
-enum SrtStatsPrintFormat
-{
- SRTSTATS_PROFMAT_INVALID = -1,
- SRTSTATS_PROFMAT_2COLS = 0,
- SRTSTATS_PROFMAT_JSON,
- SRTSTATS_PROFMAT_CSV
-};
-
-SrtStatsPrintFormat ParsePrintFormat(std::string pf, std::string& w_extras);
-
-enum SrtStatCat
-{
- SSC_GEN, //< General
- SSC_WINDOW, // flow/congestion window
- SSC_LINK, //< Link data
- SSC_SEND, //< Sending
- SSC_RECV //< Receiving
-};
-
-struct SrtStatData
-{
- SrtStatCat category;
- std::string name;
- std::string longname;
-
- SrtStatData(SrtStatCat cat, std::string n, std::string l): category(cat), name(n), longname(l) {}
- virtual ~SrtStatData() {}
-
- virtual void PrintValue(std::ostream& str, const CBytePerfMon& mon) = 0;
-};
-
-template <class TYPE>
-struct SrtStatDataType: public SrtStatData
-{
- typedef TYPE CBytePerfMon::*pfield_t;
- pfield_t pfield;
-
- SrtStatDataType(SrtStatCat cat, const std::string& name, const std::string& longname, pfield_t field)
- : SrtStatData (cat, name, longname), pfield(field)
- {
- }
-
- void PrintValue(std::ostream& str, const CBytePerfMon& mon) override
- {
- str << mon.*pfield;
- }
-};
-
-class SrtStatsWriter
-{
-public:
- virtual std::string WriteStats(int sid, const CBytePerfMon& mon) = 0;
- virtual std::string WriteBandwidth(double mbpsBandwidth) = 0;
- virtual ~SrtStatsWriter() {}
-
- // Only if HAS_PUT_TIME. Specified in the imp file.
- std::string print_timestamp();
-
- void Option(const std::string& key, const std::string& val)
- {
- options[key] = val;
- }
-
- bool Option(const std::string& key, std::string* rval = nullptr)
- {
- const std::string* out = map_getp(options, key);
- if (!out)
- return false;
-
- if (rval)
- *rval = *out;
- return true;
- }
-
-protected:
- std::map<std::string, std::string> options;
-};
-
-extern std::vector<std::unique_ptr<SrtStatData>> g_SrtStatsTable;
-
-std::shared_ptr<SrtStatsWriter> SrtStatsWriterFactory(SrtStatsPrintFormat printformat);
-
-
-
-#endif
+++ /dev/null
-# IMPORTANT!
-#
-# This file contains information about ALL files existing in this directory
-# and belonging to the shared file between official applications
-# so that the build definition file can take them all to link against the app.
-# Applications in the 'apps' directory will use them all.
-# Appliecaions in the 'testing' directory may use some of them and they will
-# take selectively whichever parts they need.
-
-SOURCES
-apputil.cpp
-statswriter.cpp
-logsupport.cpp
-logsupport_appdefs.cpp
-socketoptions.cpp
-transmitmedia.cpp
-uriparser.cpp
-verbose.cpp
-
-PRIVATE HEADERS
-apputil.hpp
-logsupport.hpp
-socketoptions.hpp
-transmitbase.hpp
-transmitmedia.hpp
-uriparser.hpp
-verbose.hpp
+++ /dev/null
-/*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2018 Haivision Systems Inc.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- */
-
-#ifndef INC_SRT_COMMON_TRANMITBASE_HPP
-#define INC_SRT_COMMON_TRANMITBASE_HPP
-
-#include <string>
-#include <memory>
-#include <vector>
-#include <iostream>
-#include <stdexcept>
-#include "srt.h"
-#include "uriparser.hpp"
-#include "apputil.hpp"
-#include "statswriter.hpp"
-
-typedef std::vector<char> bytevector;
-extern bool transmit_total_stats;
-extern bool g_stats_are_printed_to_stdout;
-extern unsigned long transmit_bw_report;
-extern unsigned long transmit_stats_report;
-extern unsigned long transmit_chunk_size;
-
-struct MediaPacket
-{
- bytevector payload;
- int64_t time = 0;
-
- MediaPacket(bytevector&& src) : payload(std::move(src)) {}
- MediaPacket(bytevector&& src, int64_t stime) : payload(std::move(src)), time(stime) {}
-
- MediaPacket(size_t payload_size) : payload(payload_size), time(0) {}
- MediaPacket(const bytevector& src) : payload(src) {}
- MediaPacket(const bytevector& src, int64_t stime) : payload(src), time(stime) {}
- MediaPacket() {}
-};
-
-extern std::shared_ptr<SrtStatsWriter> transmit_stats_writer;
-
-class Location
-{
-public:
- UriParser uri;
- Location() {}
-};
-
-class Source: public Location
-{
-public:
- virtual int Read(size_t chunk, MediaPacket& pkt, std::ostream &out_stats = std::cout) = 0;
- virtual bool IsOpen() = 0;
- virtual bool End() = 0;
- static std::unique_ptr<Source> Create(const std::string& url);
- virtual void Close() {}
- virtual ~Source() {}
-
- class ReadEOF: public std::runtime_error
- {
- public:
- ReadEOF(const std::string& fn): std::runtime_error( "EOF while reading file: " + fn )
- {
- }
- };
-
- virtual SRTSOCKET GetSRTSocket() const { return SRT_INVALID_SOCK; }
- virtual int GetSysSocket() const { return -1; }
- virtual bool AcceptNewClient() { return false; }
-};
-
-class Target: public Location
-{
-public:
- virtual int Write(const char* data, size_t size, int64_t src_time, std::ostream &out_stats = std::cout) = 0;
- virtual bool IsOpen() = 0;
- virtual bool Broken() = 0;
- virtual void Close() {}
- virtual size_t Still() { return 0; }
- static std::unique_ptr<Target> Create(const std::string& url);
- virtual ~Target() {}
-
- virtual SRTSOCKET GetSRTSocket() const { return SRT_INVALID_SOCK; }
- virtual int GetSysSocket() const { return -1; }
- virtual bool AcceptNewClient() { return false; }
-};
-
-#endif
+++ /dev/null
-/*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2018 Haivision Systems Inc.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- */
-
-// Just for formality. This file should be used
-#include <iostream>
-#include <iomanip>
-#include <fstream>
-#include <sstream>
-#include <memory>
-#include <string>
-#include <stdexcept>
-#include <iterator>
-#include <map>
-#include <srt.h>
-#if !defined(_WIN32)
-#include <sys/ioctl.h>
-#else
-#include <fcntl.h>
-#include <io.h>
-#endif
-#if defined(SUNOS)
-#include <sys/filio.h>
-#endif
-
-#include "netinet_any.h"
-#include "apputil.hpp"
-#include "socketoptions.hpp"
-#include "uriparser.hpp"
-#include "transmitmedia.hpp"
-#include "srt_compat.h"
-#include "verbose.hpp"
-
-using namespace std;
-using namespace srt;
-
-bool g_stats_are_printed_to_stdout = false;
-bool transmit_total_stats = false;
-unsigned long transmit_bw_report = 0;
-unsigned long transmit_stats_report = 0;
-unsigned long transmit_chunk_size = SRT_LIVE_MAX_PLSIZE;
-
-class FileSource: public Source
-{
- ifstream ifile;
- string filename_copy;
-public:
-
- FileSource(const string& path): ifile(path, ios::in | ios::binary), filename_copy(path)
- {
- if ( !ifile )
- throw std::runtime_error(path + ": Can't open file for reading");
- }
-
- int Read(size_t chunk, MediaPacket& pkt, ostream & ignored SRT_ATR_UNUSED = cout) override
- {
- if (pkt.payload.size() < chunk)
- pkt.payload.resize(chunk);
-
- pkt.time = 0;
- ifile.read(pkt.payload.data(), chunk);
- size_t nread = ifile.gcount();
- if (nread < pkt.payload.size())
- pkt.payload.resize(nread);
-
- if (pkt.payload.empty())
- {
- return 0;
- }
-
- return (int) nread;
- }
-
- bool IsOpen() override { return bool(ifile); }
- bool End() override { return ifile.eof(); }
-};
-
-class FileTarget: public Target
-{
- ofstream ofile;
-public:
-
- FileTarget(const string& path): ofile(path, ios::out | ios::trunc | ios::binary) {}
-
- int Write(const char* data, size_t size, int64_t time SRT_ATR_UNUSED, ostream & ignored SRT_ATR_UNUSED = cout) override
- {
- ofile.write(data, size);
- return !(ofile.bad()) ? (int) size : 0;
- }
-
- bool IsOpen() override { return !!ofile; }
- bool Broken() override { return !ofile.good(); }
- //~FileTarget() { ofile.close(); }
- void Close() override { ofile.close(); }
-};
-
-template <class Iface> struct File;
-template <> struct File<Source> { typedef FileSource type; };
-template <> struct File<Target> { typedef FileTarget type; };
-
-template <class Iface>
-Iface* CreateFile(const string& name) { return new typename File<Iface>::type (name); }
-
-shared_ptr<SrtStatsWriter> transmit_stats_writer;
-
-void SrtCommon::InitParameters(string host, map<string,string> par)
-{
- // Application-specific options: mode, blocking, timeout, adapter
- if (Verbose::on && !par.empty())
- {
- Verb() << "SRT parameters specified:\n";
- for (map<string,string>::iterator i = par.begin(); i != par.end(); ++i)
- {
- cerr << "\t" << i->first << " = '" << i->second << "'\n";
- }
- }
-
- string adapter;
- if (par.count("adapter"))
- {
- adapter = par.at("adapter");
- }
-
- m_mode = "default";
- if (par.count("mode"))
- {
- m_mode = par.at("mode");
- }
- SocketOption::Mode mode = SrtInterpretMode(m_mode, host, adapter);
- if (mode == SocketOption::FAILURE)
- {
- Error("Invalid mode");
- }
-
- // Fix the mode name after successful interpretation
- m_mode = SocketOption::mode_names[mode];
-
- par.erase("mode");
-
- if (par.count("timeout"))
- {
- m_timeout = stoi(par.at("timeout"), 0, 0);
- par.erase("timeout");
- }
-
- if (par.count("adapter"))
- {
- m_adapter = par.at("adapter");
- par.erase("adapter");
- }
- else if (m_mode == "listener")
- {
- // For listener mode, adapter is taken from host,
- // if 'adapter' parameter is not given
- m_adapter = host;
- }
-
- if (par.count("tsbpd") && false_names.count(par.at("tsbpd")))
- {
- m_tsbpdmode = false;
- }
-
- if (par.count("port"))
- {
- m_outgoing_port = stoi(par.at("port"), 0, 0);
- par.erase("port");
- }
-
- // That's kinda clumsy, but it must rely on the defaults.
- // Default mode is live, so check if the file mode was enforced
- if ((par.count("transtype") == 0 || par["transtype"] != "file")
- && transmit_chunk_size > SRT_LIVE_DEF_PLSIZE)
- {
- if (transmit_chunk_size > SRT_LIVE_MAX_PLSIZE)
- throw std::runtime_error("Chunk size in live mode exceeds 1456 bytes; this is not supported");
-
- par["payloadsize"] = Sprint(transmit_chunk_size);
- }
-
- // Assign the others here.
- m_options = par;
-}
-
-void SrtCommon::PrepareListener(string host, int port, int backlog)
-{
- m_bindsock = srt_create_socket();
- if ( m_bindsock == SRT_ERROR )
- Error("srt_create_socket");
-
- int stat = ConfigurePre(m_bindsock);
- if ( stat == SRT_ERROR )
- Error("ConfigurePre");
-
- sockaddr_any sa = CreateAddr(host, port);
- sockaddr* psa = sa.get();
- Verb() << "Binding a server on " << host << ":" << port << " ...";
-
- stat = srt_bind(m_bindsock, psa, sizeof sa);
- if ( stat == SRT_ERROR )
- {
- srt_close(m_bindsock);
- Error("srt_bind");
- }
-
- Verb() << " listen...";
-
- stat = srt_listen(m_bindsock, backlog);
- if ( stat == SRT_ERROR )
- {
- srt_close(m_bindsock);
- Error("srt_listen");
- }
-}
-
-void SrtCommon::StealFrom(SrtCommon& src)
-{
- // This is used when SrtCommon class designates a listener
- // object that is doing Accept in appropriate direction class.
- // The new object should get the accepted socket.
- m_output_direction = src.m_output_direction;
- m_timeout = src.m_timeout;
- m_tsbpdmode = src.m_tsbpdmode;
- m_options = src.m_options;
- m_bindsock = SRT_INVALID_SOCK; // no listener
- m_sock = src.m_sock;
- src.m_sock = SRT_INVALID_SOCK; // STEALING
-}
-
-bool SrtCommon::AcceptNewClient()
-{
- sockaddr_any scl;
- Verb() << " accept... ";
-
- m_sock = srt_accept(m_bindsock, scl.get(), &scl.len);
- if ( m_sock == SRT_INVALID_SOCK )
- {
- srt_close(m_bindsock);
- m_bindsock = SRT_INVALID_SOCK;
- Error("srt_accept");
- }
-
- // we do one client connection at a time,
- // so close the listener.
- srt_close(m_bindsock);
- m_bindsock = SRT_INVALID_SOCK;
-
- Verb() << " connected.";
-
- // ConfigurePre is done on bindsock, so any possible Pre flags
- // are DERIVED by sock. ConfigurePost is done exclusively on sock.
- int stat = ConfigurePost(m_sock);
- if ( stat == SRT_ERROR )
- Error("ConfigurePost");
-
- return true;
-}
-
-void SrtCommon::Init(string host, int port, map<string,string> par, bool dir_output)
-{
- m_output_direction = dir_output;
- InitParameters(host, par);
-
- Verb() << "Opening SRT " << (dir_output ? "target" : "source") << " " << m_mode
- << " on " << host << ":" << port;
-
- if ( m_mode == "caller" )
- OpenClient(host, port);
- else if ( m_mode == "listener" )
- OpenServer(m_adapter, port);
- else if ( m_mode == "rendezvous" )
- OpenRendezvous(m_adapter, host, port);
- else
- {
- throw std::invalid_argument("Invalid 'mode'. Use 'client' or 'server'");
- }
-}
-
-int SrtCommon::ConfigurePost(SRTSOCKET sock)
-{
- bool no = false;
- int result = 0;
- if ( m_output_direction )
- {
- result = srt_setsockopt(sock, 0, SRTO_SNDSYN, &no, sizeof no);
- if ( result == -1 )
- return result;
-
- if ( m_timeout )
- return srt_setsockopt(sock, 0, SRTO_SNDTIMEO, &m_timeout, sizeof m_timeout);
- }
- else
- {
- result = srt_setsockopt(sock, 0, SRTO_RCVSYN, &no, sizeof no);
- if ( result == -1 )
- return result;
-
- if ( m_timeout )
- return srt_setsockopt(sock, 0, SRTO_RCVTIMEO, &m_timeout, sizeof m_timeout);
- }
-
- SrtConfigurePost(sock, m_options);
-
- for (const auto &o: srt_options)
- {
- if ( o.binding == SocketOption::POST && m_options.count(o.name) )
- {
- string value = m_options.at(o.name);
- bool ok = o.apply<SocketOption::SRT>(sock, value);
- if ( !ok )
- Verb() << "WARNING: failed to set '" << o.name << "' (post, "
- << (m_output_direction? "target":"source") << ") to "
- << value;
- else
- Verb() << "NOTE: SRT/post::" << o.name << "=" << value;
- }
- }
-
- return 0;
-}
-
-int SrtCommon::ConfigurePre(SRTSOCKET sock)
-{
- int result = 0;
-
- bool no = false;
- if ( !m_tsbpdmode )
- {
- result = srt_setsockopt(sock, 0, SRTO_TSBPDMODE, &no, sizeof no);
- if ( result == -1 )
- return result;
- }
-
- result = srt_setsockopt(sock, 0, SRTO_RCVSYN, &no, sizeof no);
- if ( result == -1 )
- return result;
-
-
- // host is only checked for emptiness and depending on that the connection mode is selected.
- // Here we are not exactly interested with that information.
- vector<string> failures;
-
- // NOTE: here host = "", so the 'connmode' will be returned as LISTENER always,
- // but it doesn't matter here. We don't use 'connmode' for anything else than
- // checking for failures.
- SocketOption::Mode conmode = SrtConfigurePre(sock, "", m_options, &failures);
-
- if ( conmode == SocketOption::FAILURE )
- {
- if ( Verbose::on )
- {
- cerr << "WARNING: failed to set options: ";
- copy(failures.begin(), failures.end(), ostream_iterator<string>(cerr, ", "));
- cerr << endl;
- }
-
- return SRT_ERROR;
- }
-
- return 0;
-}
-
-void SrtCommon::SetupAdapter(const string& host, int port)
-{
- sockaddr_any localsa = CreateAddr(host, port);
- sockaddr* psa = localsa.get();
- int stat = srt_bind(m_sock, psa, sizeof localsa);
- if ( stat == SRT_ERROR )
- Error("srt_bind");
-}
-
-void SrtCommon::OpenClient(string host, int port)
-{
- PrepareClient();
-
- if (m_outgoing_port || m_adapter != "")
- {
- SetupAdapter(m_adapter, m_outgoing_port);
- }
-
- ConnectClient(host, port);
-}
-
-void SrtCommon::PrepareClient()
-{
- m_sock = srt_create_socket();
- if ( m_sock == SRT_ERROR )
- Error("srt_create_socket");
-
- int stat = ConfigurePre(m_sock);
- if ( stat == SRT_ERROR )
- Error("ConfigurePre");
-}
-
-
-void SrtCommon::ConnectClient(string host, int port)
-{
-
- sockaddr_any sa = CreateAddr(host, port);
- sockaddr* psa = sa.get();
-
- Verb() << "Connecting to " << host << ":" << port;
-
- int stat = srt_connect(m_sock, psa, sizeof sa);
- if ( stat == SRT_ERROR )
- {
- srt_close(m_sock);
- Error("srt_connect");
- }
-
- stat = ConfigurePost(m_sock);
- if ( stat == SRT_ERROR )
- Error("ConfigurePost");
-}
-
-void SrtCommon::Error(string src)
-{
- int errnov = 0;
- int result = srt_getlasterror(&errnov);
- string message = srt_getlasterror_str();
- Verb() << "\nERROR #" << result << "." << errnov << ": " << message;
-
- throw TransmissionError("error: " + src + ": " + message);
-}
-
-void SrtCommon::OpenRendezvous(string adapter, string host, int port)
-{
- m_sock = srt_create_socket();
- if ( m_sock == SRT_ERROR )
- Error("srt_create_socket");
-
- bool yes = true;
- srt_setsockopt(m_sock, 0, SRTO_RENDEZVOUS, &yes, sizeof yes);
-
- int stat = ConfigurePre(m_sock);
- if ( stat == SRT_ERROR )
- Error("ConfigurePre");
-
- sockaddr_any sa = CreateAddr(host, port);
- if (sa.family() == AF_UNSPEC)
- {
- Error("OpenRendezvous: invalid target host specification: " + host);
- }
-
- const int outport = m_outgoing_port ? m_outgoing_port : port;
-
- sockaddr_any localsa = CreateAddr(adapter, outport, sa.family());
-
- Verb() << "Binding a server on " << adapter << ":" << outport;
-
- stat = srt_bind(m_sock, localsa.get(), sizeof localsa);
- if ( stat == SRT_ERROR )
- {
- srt_close(m_sock);
- Error("srt_bind");
- }
-
- Verb() << "Connecting to " << host << ":" << port;
-
- stat = srt_connect(m_sock, sa.get(), sizeof sa);
- if ( stat == SRT_ERROR )
- {
- srt_close(m_sock);
- Error("srt_connect");
- }
-
- stat = ConfigurePost(m_sock);
- if ( stat == SRT_ERROR )
- Error("ConfigurePost");
-}
-
-void SrtCommon::Close()
-{
- Verb() << "SrtCommon: DESTROYING CONNECTION, closing sockets (rt%" << m_sock << " ls%" << m_bindsock << ")...";
-
- if ( m_sock != SRT_INVALID_SOCK )
- {
- srt_close(m_sock);
- m_sock = SRT_INVALID_SOCK;
- }
-
- if ( m_bindsock != SRT_INVALID_SOCK )
- {
- srt_close(m_bindsock);
- m_bindsock = SRT_INVALID_SOCK ;
- }
-
- Verb() << "SrtCommon: ... done.";
-}
-
-SrtCommon::~SrtCommon()
-{
- Close();
-}
-
-SrtSource::SrtSource(string host, int port, const map<string,string>& par)
-{
- Init(host, port, par, false);
-
- ostringstream os;
- os << host << ":" << port;
- hostport_copy = os.str();
-}
-
-int SrtSource::Read(size_t chunk, MediaPacket& pkt, ostream &out_stats)
-{
- static unsigned long counter = 1;
-
- if (pkt.payload.size() < chunk)
- pkt.payload.resize(chunk);
-
- SRT_MSGCTRL ctrl;
- const int stat = srt_recvmsg2(m_sock, pkt.payload.data(), (int) chunk, &ctrl);
- if (stat <= 0)
- {
- pkt.payload.clear();
- return stat;
- }
-
- pkt.time = ctrl.srctime;
-
- chunk = size_t(stat);
- if (chunk < pkt.payload.size())
- pkt.payload.resize(chunk);
-
- const bool need_bw_report = transmit_bw_report && (counter % transmit_bw_report) == transmit_bw_report - 1;
- const bool need_stats_report = transmit_stats_report && (counter % transmit_stats_report) == transmit_stats_report - 1;
-
- if (need_bw_report || need_stats_report)
- {
- CBytePerfMon perf;
- srt_bstats(m_sock, &perf, need_stats_report && !transmit_total_stats);
- if (transmit_stats_writer != nullptr)
- {
- if (need_bw_report)
- cerr << transmit_stats_writer->WriteBandwidth(perf.mbpsBandwidth) << std::flush;
- if (need_stats_report)
- out_stats << transmit_stats_writer->WriteStats(m_sock, perf) << std::flush;
- }
- }
- ++counter;
- return stat;
-}
-
-int SrtTarget::ConfigurePre(SRTSOCKET sock)
-{
- int result = SrtCommon::ConfigurePre(sock);
- if ( result == -1 )
- return result;
-
- int yes = 1;
- // This is for the HSv4 compatibility; if both parties are HSv5
- // (min. version 1.2.1), then this setting simply does nothing.
- // In HSv4 this setting is obligatory; otherwise the SRT handshake
- // extension will not be done at all.
- result = srt_setsockopt(sock, 0, SRTO_SENDER, &yes, sizeof yes);
- if ( result == -1 )
- return result;
-
- return 0;
-}
-
-int SrtTarget::Write(const char* data, size_t size, int64_t src_time, ostream &out_stats)
-{
- static unsigned long counter = 1;
-
- SRT_MSGCTRL ctrl = srt_msgctrl_default;
- ctrl.srctime = src_time;
- int stat = srt_sendmsg2(m_sock, data, (int) size, &ctrl);
- if (stat == SRT_ERROR)
- {
- return stat;
- }
-
- const bool need_bw_report = transmit_bw_report && (counter % transmit_bw_report) == transmit_bw_report - 1;
- const bool need_stats_report = transmit_stats_report && (counter % transmit_stats_report) == transmit_stats_report - 1;
-
- if (need_bw_report || need_stats_report)
- {
- CBytePerfMon perf;
- srt_bstats(m_sock, &perf, need_stats_report && !transmit_total_stats);
- if (transmit_stats_writer != nullptr)
- {
- if (need_bw_report)
- cerr << transmit_stats_writer->WriteBandwidth(perf.mbpsBandwidth) << std::flush;
- if (need_stats_report)
- out_stats << transmit_stats_writer->WriteStats(m_sock, perf) << std::flush;
- }
- }
- ++counter;
- return stat;
-}
-
-
-SrtModel::SrtModel(string host, int port, map<string,string> par)
-{
- InitParameters(host, par);
- if (m_mode == "caller")
- is_caller = true;
- else if (m_mode != "listener")
- throw std::invalid_argument("Only caller and listener modes supported");
-
- m_host = host;
- m_port = port;
-}
-
-void SrtModel::Establish(std::string& w_name)
-{
- // This does connect or accept.
- // When this returned true, the caller should create
- // a new SrtSource or SrtTaget then call StealFrom(*this) on it.
-
- // If this is a connector and the peer doesn't have a corresponding
- // medium, it should send back a single byte with value 0. This means
- // that agent should stop connecting.
-
- if (is_caller)
- {
- // Establish a connection
-
- PrepareClient();
-
- if (w_name != "")
- {
- Verb() << "Connect with requesting stream [" << w_name << "]";
- srt::setstreamid(m_sock, w_name);
- }
- else
- {
- Verb() << "NO STREAM ID for SRT connection";
- }
-
- if (m_outgoing_port)
- {
- Verb() << "Setting outgoing port: " << m_outgoing_port;
- SetupAdapter("", m_outgoing_port);
- }
-
- ConnectClient(m_host, m_port);
-
- if (m_outgoing_port == 0)
- {
- // Must rely on a randomly selected one. Extract the port
- // so that it will be reused next time.
- sockaddr_any s(AF_INET);
- int namelen = s.size();
- if ( srt_getsockname(Socket(), s.get(), &namelen) == SRT_ERROR )
- {
- Error("srt_getsockname");
- }
-
- m_outgoing_port = s.hport();
- Verb() << "Extracted outgoing port: " << m_outgoing_port;
- }
- }
- else
- {
- // Listener - get a socket by accepting.
- // Check if the listener is already created first
- if (Listener() == SRT_INVALID_SOCK)
- {
- Verb() << "Setting up listener: port=" << m_port << " backlog=5";
- PrepareListener(m_adapter, m_port, 5);
- }
-
- Verb() << "Accepting a client...";
- AcceptNewClient();
- // This rewrites m_sock with a new SRT socket ("accepted" socket)
- w_name = srt::getstreamid(m_sock);
- Verb() << "... GOT CLIENT for stream [" << w_name << "]";
- }
-}
-
-
-template <class Iface> struct Srt;
-template <> struct Srt<Source> { typedef SrtSource type; };
-template <> struct Srt<Target> { typedef SrtTarget type; };
-
-template <class Iface>
-Iface* CreateSrt(const string& host, int port, const map<string,string>& par) { return new typename Srt<Iface>::type (host, port, par); }
-
-class ConsoleSource: public Source
-{
-public:
-
- ConsoleSource()
- {
-#ifdef _WIN32
- // The default stdin mode on windows is text.
- // We have to set it to the binary mode
- _setmode(_fileno(stdin), _O_BINARY);
-#endif
- }
-
- int Read(size_t chunk, MediaPacket& pkt, ostream & ignored SRT_ATR_UNUSED = cout) override
- {
- if (pkt.payload.size() < chunk)
- pkt.payload.resize(chunk);
-
- bool st = cin.read(pkt.payload.data(), chunk).good();
- chunk = cin.gcount();
- if (chunk == 0 || !st)
- {
- pkt.payload.clear();
- return 0;
- }
-
- // Save this time to potentially use it for SRT target.
- pkt.time = srt_time_now();
- if (chunk < pkt.payload.size())
- pkt.payload.resize(chunk);
-
- return (int) chunk;
- }
-
- bool IsOpen() override { return cin.good(); }
- bool End() override { return cin.eof(); }
- int GetSysSocket() const override { return 0; };
-};
-
-class ConsoleTarget: public Target
-{
-public:
-
- ConsoleTarget()
- {
-#ifdef _WIN32
- // The default stdout mode on windows is text.
- // We have to set it to the binary mode
- _setmode(_fileno(stdout), _O_BINARY);
-#endif
- }
-
- virtual ~ConsoleTarget()
- {
- cout.flush();
- }
-
- int Write(const char* data, size_t len, int64_t src_time SRT_ATR_UNUSED, ostream & ignored SRT_ATR_UNUSED = cout) override
- {
- cout.write(data, len);
- return (int) len;
- }
-
- bool IsOpen() override { return cout.good(); }
- bool Broken() override { return cout.eof(); }
- int GetSysSocket() const override { return 0; };
-};
-
-template <class Iface> struct Console;
-template <> struct Console<Source> { typedef ConsoleSource type; };
-template <> struct Console<Target> { typedef ConsoleTarget type; };
-
-template <class Iface>
-Iface* CreateConsole() { return new typename Console<Iface>::type (); }
-
-
-// More options can be added in future.
-SocketOption udp_options [] {
- { "iptos", IPPROTO_IP, IP_TOS, SocketOption::PRE, SocketOption::INT, nullptr },
- // IP_TTL and IP_MULTICAST_TTL are handled separately by a common option, "ttl".
- { "mcloop", IPPROTO_IP, IP_MULTICAST_LOOP, SocketOption::PRE, SocketOption::INT, nullptr },
- { "sndbuf", SOL_SOCKET, SO_SNDBUF, SocketOption::PRE, SocketOption::INT, nullptr},
- { "rcvbuf", SOL_SOCKET, SO_RCVBUF, SocketOption::PRE, SocketOption::INT, nullptr}
-};
-
-static inline bool IsMulticast(in_addr adr)
-{
- unsigned char* abytes = (unsigned char*)&adr.s_addr;
- unsigned char c = abytes[0];
- return c >= 224 && c <= 239;
-}
-
-
-class UdpCommon
-{
-protected:
- int m_sock = -1;
- sockaddr_any sadr;
- string adapter;
- map<string, string> m_options;
-
- void Setup(string host, int port, map<string,string> attr)
- {
- m_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
- if (m_sock == -1)
- Error(SysError(), "UdpCommon::Setup: socket");
-
- int yes = 1;
- ::setsockopt(m_sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&yes, sizeof yes);
-
- // set non-blocking mode
-#if defined(_WIN32)
- unsigned long ulyes = 1;
- if (ioctlsocket(m_sock, FIONBIO, &ulyes) == SOCKET_ERROR)
-#else
- if (ioctl(m_sock, FIONBIO, (const char *)&yes) < 0)
-#endif
- {
- Error(SysError(), "UdpCommon::Setup: ioctl FIONBIO");
- }
-
- sadr = CreateAddr(host, port);
-
- bool is_multicast = false;
-
- if (attr.count("multicast"))
- {
- // XXX: Here provide support for IPv6 multicast #1479
- if (sadr.family() != AF_INET)
- {
- throw std::runtime_error("UdpCommon: Multicast on IPv6 is not yet supported");
- }
-
- if (!IsMulticast(sadr.sin.sin_addr))
- {
- throw std::runtime_error("UdpCommon: requested multicast for a non-multicast-type IP address");
- }
- is_multicast = true;
- }
- else if (sadr.family() == AF_INET && IsMulticast(sadr.sin.sin_addr))
- {
- is_multicast = true;
- }
-
- if (is_multicast)
- {
- ip_mreq_source mreq_ssm;
- ip_mreq mreq;
- sockaddr_any maddr (AF_INET);
- int opt_name;
- void* mreq_arg_ptr;
- socklen_t mreq_arg_size;
-
- adapter = attr.count("adapter") ? attr.at("adapter") : string();
- if ( adapter == "" )
- {
- Verb() << "Multicast: home address: INADDR_ANY:" << port;
- maddr.sin.sin_family = AF_INET;
- maddr.sin.sin_addr.s_addr = htonl(INADDR_ANY);
- maddr.sin.sin_port = htons(port); // necessary for temporary use
- }
- else
- {
- Verb() << "Multicast: home address: " << adapter << ":" << port;
- maddr = CreateAddr(adapter, port);
- }
-
- if (attr.count("source"))
- {
-#ifdef IP_ADD_SOURCE_MEMBERSHIP
- /* this is an ssm. we need to use the right struct and opt */
- opt_name = IP_ADD_SOURCE_MEMBERSHIP;
- mreq_ssm.imr_multiaddr.s_addr = sadr.sin.sin_addr.s_addr;
- mreq_ssm.imr_interface.s_addr = maddr.sin.sin_addr.s_addr;
- inet_pton(AF_INET, attr.at("source").c_str(), &mreq_ssm.imr_sourceaddr);
- mreq_arg_size = sizeof(mreq_ssm);
- mreq_arg_ptr = &mreq_ssm;
-#else
- throw std::runtime_error("UdpCommon: source-filter multicast not supported by OS");
-#endif
- }
- else
- {
- opt_name = IP_ADD_MEMBERSHIP;
- mreq.imr_multiaddr.s_addr = sadr.sin.sin_addr.s_addr;
- mreq.imr_interface.s_addr = maddr.sin.sin_addr.s_addr;
- mreq_arg_size = sizeof(mreq);
- mreq_arg_ptr = &mreq;
- }
-
-#ifdef _WIN32
- const char* mreq_arg = (const char*)mreq_arg_ptr;
- const auto status_error = SOCKET_ERROR;
-#else
- const void* mreq_arg = mreq_arg_ptr;
- const auto status_error = -1;
-#endif
-
-#if defined(_WIN32) || defined(__CYGWIN__)
- // On Windows it somehow doesn't work when bind()
- // is called with multicast address. Write the address
- // that designates the network device here.
- // Also, sets port sharing when working with multicast
- sadr = maddr;
- int reuse = 1;
- int shareAddrRes = setsockopt(m_sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<const char*>(&reuse), sizeof(reuse));
- if (shareAddrRes == status_error)
- {
- throw runtime_error("marking socket for shared use failed");
- }
- Verb() << "Multicast(Windows): will bind to home address";
-#else
- Verb() << "Multicast(POSIX): will bind to IGMP address: " << host;
-#endif
- int res = setsockopt(m_sock, IPPROTO_IP, opt_name, mreq_arg, mreq_arg_size);
-
- if ( res == status_error )
- {
- Error(errno, "adding to multicast membership failed");
- }
-
- attr.erase("multicast");
- attr.erase("adapter");
- }
-
- // The "ttl" options is handled separately, it maps to both IP_TTL
- // and IP_MULTICAST_TTL so that TTL setting works for both uni- and multicast.
- if (attr.count("ttl"))
- {
- int ttl = stoi(attr.at("ttl"));
- int res = setsockopt(m_sock, IPPROTO_IP, IP_TTL, (const char*)&ttl, sizeof ttl);
- if (res == -1)
- Verb() << "WARNING: failed to set 'ttl' (IP_TTL) to " << ttl;
- res = setsockopt(m_sock, IPPROTO_IP, IP_MULTICAST_TTL, (const char*)&ttl, sizeof ttl);
- if (res == -1)
- Verb() << "WARNING: failed to set 'ttl' (IP_MULTICAST_TTL) to " << ttl;
-
- attr.erase("ttl");
- }
-
- m_options = attr;
-
- for (auto o: udp_options)
- {
- // Ignore "binding" - for UDP there are no post options.
- if ( m_options.count(o.name) )
- {
- string value = m_options.at(o.name);
- bool ok = o.apply<SocketOption::SYSTEM>(m_sock, value);
- if ( !ok )
- Verb() << "WARNING: failed to set '" << o.name << "' to " << value;
- }
- }
- }
-
- void Error(int err, string src)
- {
- char buf[512];
- string message = SysStrError(err, buf, 512u);
-
- cerr << "\nERROR #" << err << ": " << message << endl;
-
- throw TransmissionError("error: " + src + ": " + message);
- }
-
- ~UdpCommon()
- {
-#ifdef _WIN32
- if (m_sock != -1)
- {
- shutdown(m_sock, SD_BOTH);
- closesocket(m_sock);
- m_sock = -1;
- }
-#else
- close(m_sock);
-#endif
- }
-};
-
-
-class UdpSource: public Source, public UdpCommon
-{
- bool eof = true;
-public:
-
- UdpSource(string host, int port, const map<string,string>& attr)
- {
- Setup(host, port, attr);
- int stat = ::bind(m_sock, sadr.get(), sadr.size());
- if ( stat == -1 )
- Error(SysError(), "Binding address for UDP");
- eof = false;
- }
-
- int Read(size_t chunk, MediaPacket& pkt, ostream & ignored SRT_ATR_UNUSED = cout) override
- {
- if (pkt.payload.size() < chunk)
- pkt.payload.resize(chunk);
-
- sockaddr_any sa(sadr.family());
- socklen_t si = sa.size();
- int stat = recvfrom(m_sock, pkt.payload.data(), (int) chunk, 0, sa.get(), &si);
- if (stat < 1)
- {
- if (SysError() != EWOULDBLOCK)
- eof = true;
- pkt.payload.clear();
- return stat;
- }
- sa.len = si;
-
- // Save this time to potentially use it for SRT target.
- pkt.time = srt_time_now();
- chunk = size_t(stat);
- if (chunk < pkt.payload.size())
- pkt.payload.resize(chunk);
-
- return stat;
- }
-
- bool IsOpen() override { return m_sock != -1; }
- bool End() override { return eof; }
-
- int GetSysSocket() const override { return m_sock; };
-};
-
-class UdpTarget: public Target, public UdpCommon
-{
-public:
- UdpTarget(string host, int port, const map<string,string>& attr )
- {
- if (host.empty())
- cerr << "\nWARN Host for UDP target is not provided. Will send to localhost:" << port << ".\n";
-
- Setup(host, port, attr);
- if (adapter != "")
- {
- sockaddr_any maddr = CreateAddr(adapter, 0);
- if (maddr.family() != AF_INET)
- {
- Error(0, "UDP/target: IPv6 multicast not supported in the application");
- }
-
- in_addr addr = maddr.sin.sin_addr;
-
- int res = setsockopt(m_sock, IPPROTO_IP, IP_MULTICAST_IF, reinterpret_cast<const char*>(&addr), sizeof(addr));
- if (res == -1)
- {
- Error(SysError(), "setsockopt/IP_MULTICAST_IF: " + adapter);
- }
- }
-
- }
-
- int Write(const char* data, size_t len, int64_t src_time SRT_ATR_UNUSED, ostream & ignored SRT_ATR_UNUSED = cout) override
- {
- int stat = sendto(m_sock, data, (int) len, 0, sadr.get(), sadr.size());
- if ( stat == -1 )
- {
- if ((false))
- Error(SysError(), "UDP Write/sendto");
- return stat;
- }
- return stat;
- }
-
- bool IsOpen() override { return m_sock != -1; }
- bool Broken() override { return false; }
-
- int GetSysSocket() const override { return m_sock; };
-};
-
-template <class Iface> struct Udp;
-template <> struct Udp<Source> { typedef UdpSource type; };
-template <> struct Udp<Target> { typedef UdpTarget type; };
-
-template <class Iface>
-Iface* CreateUdp(const string& host, int port, const map<string,string>& par) { return new typename Udp<Iface>::type (host, port, par); }
-
-template<class Base>
-inline bool IsOutput() { return false; }
-
-template<>
-inline bool IsOutput<Target>() { return true; }
-
-template <class Base>
-extern unique_ptr<Base> CreateMedium(const string& uri)
-{
- unique_ptr<Base> ptr;
-
- UriParser u(uri);
-
- int iport = 0;
- switch ( u.type() )
- {
- default:
- break; // do nothing, return nullptr
- case UriParser::FILE:
- if (u.host() == "con" || u.host() == "console")
- {
- if (IsOutput<Base>() && (
- (Verbose::on && Verbose::cverb == &cout)
- || g_stats_are_printed_to_stdout))
- {
- cerr << "ERROR: file://con with -v or -r or -s would result in mixing the data and text info.\n";
- cerr << "ERROR: HINT: you can stream through a FIFO (named pipe)\n";
- throw invalid_argument("incorrect parameter combination");
- }
- ptr.reset(CreateConsole<Base>());
- }
-// Disable regular file support for the moment
-#if 0
- else
- ptr.reset( CreateFile<Base>(u.path()));
-#endif
- break;
-
- case UriParser::SRT:
- iport = atoi(u.port().c_str());
- if ( iport < 1024 )
- {
- cerr << "Port value invalid: " << iport << " - must be >=1024\n";
- throw invalid_argument("Invalid port number");
- }
- ptr.reset( CreateSrt<Base>(u.host(), iport, u.parameters()) );
- break;
-
-
- case UriParser::UDP:
- iport = atoi(u.port().c_str());
- if ( iport < 1024 )
- {
- cerr << "Port value invalid: " << iport << " - must be >=1024\n";
- throw invalid_argument("Invalid port number");
- }
- ptr.reset( CreateUdp<Base>(u.host(), iport, u.parameters()) );
- break;
-
- }
-
- if (ptr.get())
- ptr->uri = move(u);
-
- return ptr;
-}
-
-
-std::unique_ptr<Source> Source::Create(const std::string& url)
-{
- return CreateMedium<Source>(url);
-}
-
-std::unique_ptr<Target> Target::Create(const std::string& url)
-{
- return CreateMedium<Target>(url);
-}
+++ /dev/null
-/*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2018 Haivision Systems Inc.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- */
-
-#ifndef INC_SRT_COMMON_TRANSMITMEDIA_HPP
-#define INC_SRT_COMMON_TRANSMITMEDIA_HPP
-
-#include <string>
-#include <map>
-#include <stdexcept>
-
-#include "transmitbase.hpp"
-#include <udt.h> // Needs access to CUDTException
-
-using namespace std;
-
-// Trial version of an exception. Try to implement later an official
-// interruption mechanism in SRT using this.
-
-struct TransmissionError: public std::runtime_error
-{
- TransmissionError(const std::string& arg):
- std::runtime_error(arg)
- {
- }
-};
-
-class SrtCommon
-{
-protected:
-
- bool m_output_direction = false; //< Defines which of SND or RCV option variant should be used, also to set SRT_SENDER for output
- int m_timeout = 0; //< enforces using SRTO_SNDTIMEO or SRTO_RCVTIMEO, depending on @a m_output_direction
- bool m_tsbpdmode = true;
- int m_outgoing_port = 0;
- string m_mode;
- string m_adapter;
- map<string, string> m_options; // All other options, as provided in the URI
- SRTSOCKET m_sock = SRT_INVALID_SOCK;
- SRTSOCKET m_bindsock = SRT_INVALID_SOCK;
- bool IsUsable() { SRT_SOCKSTATUS st = srt_getsockstate(m_sock); return st > SRTS_INIT && st < SRTS_BROKEN; }
- bool IsBroken() { return srt_getsockstate(m_sock) > SRTS_CONNECTED; }
-
-public:
- void InitParameters(string host, map<string,string> par);
- void PrepareListener(string host, int port, int backlog);
- void StealFrom(SrtCommon& src);
- bool AcceptNewClient();
-
- SRTSOCKET Socket() const { return m_sock; }
- SRTSOCKET Listener() const { return m_bindsock; }
-
- void Close();
-
-protected:
-
- void Error(string src);
- void Init(string host, int port, map<string,string> par, bool dir_output);
-
- virtual int ConfigurePost(SRTSOCKET sock);
- virtual int ConfigurePre(SRTSOCKET sock);
-
- void OpenClient(string host, int port);
- void PrepareClient();
- void SetupAdapter(const std::string& host, int port);
- void ConnectClient(string host, int port);
-
- void OpenServer(string host, int port)
- {
- PrepareListener(host, port, 1);
- }
-
- void OpenRendezvous(string adapter, string host, int port);
-
- virtual ~SrtCommon();
-};
-
-
-class SrtSource: public Source, public SrtCommon
-{
- std::string hostport_copy;
-public:
-
- SrtSource(std::string host, int port, const std::map<std::string,std::string>& par);
- SrtSource()
- {
- // Do nothing - create just to prepare for use
- }
-
- int Read(size_t chunk, MediaPacket& pkt, ostream& out_stats = cout) override;
-
- /*
- In this form this isn't needed.
- Unblock if any extra settings have to be made.
- virtual int ConfigurePre(UDTSOCKET sock) override
- {
- int result = SrtCommon::ConfigurePre(sock);
- if ( result == -1 )
- return result;
- return 0;
- }
- */
-
- bool IsOpen() override { return IsUsable(); }
- bool End() override { return IsBroken(); }
-
- SRTSOCKET GetSRTSocket() const override
- {
- SRTSOCKET socket = SrtCommon::Socket();
- if (socket == SRT_INVALID_SOCK)
- socket = SrtCommon::Listener();
- return socket;
- }
-
- bool AcceptNewClient() override { return SrtCommon::AcceptNewClient(); }
-};
-
-class SrtTarget: public Target, public SrtCommon
-{
-public:
-
- SrtTarget(std::string host, int port, const std::map<std::string,std::string>& par)
- {
- Init(host, port, par, true);
- }
-
- SrtTarget() {}
-
- int ConfigurePre(SRTSOCKET sock) override;
- int Write(const char* data, size_t size, int64_t src_time, ostream &out_stats = cout) override;
- bool IsOpen() override { return IsUsable(); }
- bool Broken() override { return IsBroken(); }
-
- size_t Still() override
- {
- size_t bytes;
- int st = srt_getsndbuffer(m_sock, nullptr, &bytes);
- if (st == -1)
- return 0;
- return bytes;
- }
-
- SRTSOCKET GetSRTSocket() const override
- {
- SRTSOCKET socket = SrtCommon::Socket();
- if (socket == SRT_INVALID_SOCK)
- socket = SrtCommon::Listener();
- return socket;
- }
- bool AcceptNewClient() override { return SrtCommon::AcceptNewClient(); }
-};
-
-
-// This class is used when we don't know yet whether the given URI
-// designates an effective listener or caller. So we create it, initialize,
-// then we know what mode we'll be using.
-//
-// When caller, then we will do connect() using this object, then clone out
-// a new object - of a direction specific class - which will steal the socket
-// from this one and then roll the data. After this, this object is ready
-// to connect again, and will create its own socket for that occasion, and
-// the whole procedure repeats.
-//
-// When listener, then this object will be doing accept() and with every
-// successful acceptation it will clone out a new object - of a direction
-// specific class - which will steal just the connection socket from this
-// object. This object will still live on and accept new connections and
-// so on.
-class SrtModel: public SrtCommon
-{
-public:
- bool is_caller = false;
- string m_host;
- int m_port = 0;
-
- SrtModel(string host, int port, map<string,string> par);
- void Establish(std::string& name);
-};
-
-
-
-#endif
+++ /dev/null
-/*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2018 Haivision Systems Inc.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- */
-
-// STL includes
-#include <algorithm>
-#include <map>
-#include <string>
-
-#include "uriparser.hpp"
-
-#ifdef TEST
-#define TEST1 1
-#endif
-
-#ifdef TEST1
-#include <iostream>
-#endif
-
-using namespace std;
-
-map<string, UriParser::Type> types;
-
-struct UriParserInit
-{
- UriParserInit()
- {
- types["file"] = UriParser::FILE;
- types["udp"] = UriParser::UDP;
- types["tcp"] = UriParser::TCP;
- types["srt"] = UriParser::SRT;
- types["rtmp"] = UriParser::RTMP;
- types["http"] = UriParser::HTTP;
- types["rtp"] = UriParser::RTP;
- types[""] = UriParser::UNKNOWN;
- }
-} g_uriparser_init;
-
-UriParser::UriParser(const string& strUrl, DefaultExpect exp)
-{
- m_expect = exp;
- Parse(strUrl, exp);
-}
-
-UriParser::~UriParser(void)
-{
-}
-
-string UriParser::makeUri()
-{
- // Reassemble parts into the URI
- string prefix = "";
- if (m_proto != "")
- {
- prefix = m_proto + "://";
- }
-
- std::ostringstream out;
-
- out << prefix << m_host;
- if ((m_port == "" || m_port == "0") && m_expect == EXPECT_FILE)
- {
- // Do not add port
- }
- else
- {
- out << ":" << m_port;
- }
-
- if (m_path != "")
- {
- if (m_path[0] != '/')
- out << "/";
- out << m_path;
- }
-
- if (!m_mapQuery.empty())
- {
- out << "?";
-
- query_it i = m_mapQuery.begin();
- for (;;)
- {
- out << i->first << "=" << i->second;
- ++i;
- if (i == m_mapQuery.end())
- break;
- out << "&";
- }
- }
-
- m_origUri = out.str();
- return m_origUri;
-}
-
-string UriParser::proto(void) const
-{
- return m_proto;
-}
-
-UriParser::Type UriParser::type() const
-{
- return m_uriType;
-}
-
-string UriParser::host(void) const
-{
- return m_host;
-}
-
-string UriParser::port(void) const
-{
- return m_port;
-}
-
-unsigned short int UriParser::portno(void) const
-{
- // This returns port in numeric version. Fallback to 0.
- try
- {
- int i = atoi(m_port.c_str());
- if ( i <= 0 || i > 65535 )
- return 0;
- return i;
- }
- catch (...)
- {
- return 0;
- }
-}
-
-string UriParser::path(void) const
-{
- return m_path;
-}
-
-string UriParser::queryValue(const string& strKey) const
-{
- return m_mapQuery.at(strKey);
-}
-
-void UriParser::Parse(const string& strUrl, DefaultExpect exp)
-{
- int iQueryStart = -1;
-
- size_t idx = strUrl.find("?");
- if (idx != string::npos)
- {
- m_host = strUrl.substr(0, idx);
- iQueryStart = idx + 1;
- }
- else
- {
- m_host = strUrl;
- }
-
- idx = m_host.find("://");
- if (idx != string::npos)
- {
- m_proto = m_host.substr(0, idx);
- transform(m_proto.begin(), m_proto.end(), m_proto.begin(), [](char c){ return tolower(c); });
- m_host = m_host.substr(idx + 3, m_host.size() - (idx + 3));
- }
-
- // Handle the IPv6 specification in square brackets.
- // This actually handles anything specified in [] so potentially
- // you can also specify the usual hostname here as well. If the
- // whole host results to have [] at edge positions, they are stripped,
- // otherwise they remain. In both cases the search for the colon
- // separating the port specification starts only after ].
- const size_t i6pos = m_host.find("[");
- size_t i6end = string::npos;
-
- // Search for the "path" part only behind the closed bracket,
- // if both open and close brackets were found
- size_t path_since = 0;
- if (i6pos != string::npos)
- {
- i6end = m_host.find("]", i6pos);
- if (i6end != string::npos)
- path_since = i6end;
- }
-
- idx = m_host.find("/", path_since);
- if (idx != string::npos)
- {
- m_path = m_host.substr(idx, m_host.size() - idx);
- m_host = m_host.substr(0, idx);
- }
-
- // Check special things in the HOST entry.
- size_t atp = m_host.find('@');
- if ( atp != string::npos )
- {
- string realhost = m_host.substr(atp+1);
- string prehost;
- if ( atp > 0 )
- {
- prehost = m_host.substr(0, atp-0);
- size_t colon = prehost.find(':');
- if ( colon != string::npos )
- {
- string pw = prehost.substr(colon+1);
- string user;
- if ( colon > 0 )
- user = prehost.substr(0, colon-0);
- m_mapQuery["user"] = user;
- m_mapQuery["password"] = pw;
- }
- else
- {
- m_mapQuery["user"] = prehost;
- }
- }
- else
- {
- m_mapQuery["multicast"] = "1";
- }
- m_host = realhost;
- }
-
- bool stripbrackets = false;
- size_t hostend = 0;
- if (i6pos != string::npos)
- {
- // IPv6 IP address. Find the terminating ]
- hostend = m_host.find("]", i6pos);
- idx = m_host.rfind(":");
- if (hostend != string::npos)
- {
- // Found the end. But not necessarily it was
- // at the beginning. If it was at the beginning,
- // strip them from the host name.
-
- size_t lasthost = idx;
- if (idx != string::npos && idx < hostend)
- {
- idx = string::npos;
- lasthost = m_host.size();
- }
-
- if (i6pos == 0 && hostend == lasthost - 1)
- {
- stripbrackets = true;
- }
- }
- }
- else
- {
- idx = m_host.rfind(":");
- }
-
- if (idx != string::npos)
- {
- m_port = m_host.substr(idx + 1, m_host.size() - (idx + 1));
-
- // Extract host WITHOUT stripping brackets
- m_host = m_host.substr(0, idx);
- }
-
- if (stripbrackets)
- {
- if (!hostend)
- hostend = m_host.size() - 1;
- m_host = m_host.substr(1, hostend - 1);
- }
-
- if ( m_port == "" && m_host != "" )
- {
- // Check if the host-but-no-port has specified
- // a single integer number. If so
- // We need to use C86 strtol, cannot use C++11
- const char* beg = m_host.c_str();
- const char* end = m_host.c_str() + m_host.size();
- char* eos = 0;
- long val = strtol(beg, &eos, 10);
- if ( val > 0 && eos == end )
- {
- m_port = m_host;
- m_host = "";
- }
- }
-
- string strQueryPair;
- while (iQueryStart > -1)
- {
- idx = strUrl.find("&", iQueryStart);
- if (idx != string::npos)
- {
- strQueryPair = strUrl.substr(iQueryStart, idx - iQueryStart);
- iQueryStart = idx + 1;
- }
- else
- {
- strQueryPair = strUrl.substr(iQueryStart, strUrl.size() - iQueryStart);
- iQueryStart = idx;
- }
-
- idx = strQueryPair.find("=");
- if (idx != string::npos)
- {
- m_mapQuery[strQueryPair.substr(0, idx)] = strQueryPair.substr(idx + 1, strQueryPair.size() - (idx + 1));
- }
- }
-
- if ( m_proto == "file" )
- {
- if ( m_path.size() > 3 && m_path.substr(0, 3) == "/./" )
- m_path = m_path.substr(3);
- }
-
- // Post-parse fixes
- // Treat empty protocol as a file. In this case, merge the host and path.
- if ( exp == EXPECT_FILE && m_proto == "" && m_port == "" )
- {
- m_proto = "file";
- m_path = m_host + m_path;
- m_host = "";
- }
-
- m_uriType = types[m_proto]; // default-constructed UNKNOWN will be used if not found (although also inserted)
- m_origUri = strUrl;
-}
-
-#ifdef TEST
-
-#include <vector>
-
-using namespace std;
-
-int main( int argc, char** argv )
-{
- if ( argc < 2 )
- {
- return 0;
- }
- UriParser parser (argv[1], UriParser::EXPECT_HOST);
- std::vector<std::string> args;
-
- if (argc > 2)
- {
- copy(argv+2, argv+argc, back_inserter(args));
- }
-
-
- (void)argc;
-
- cout << "PARSING URL: " << argv[1] << endl;
- cout << "SCHEME INDEX: " << int(parser.type()) << endl;
- cout << "PROTOCOL: " << parser.proto() << endl;
- cout << "HOST: " << parser.host() << endl;
- cout << "PORT (string): " << parser.port() << endl;
- cout << "PORT (numeric): " << parser.portno() << endl;
- cout << "PATH: " << parser.path() << endl;
- cout << "PARAMETERS:\n";
- for (auto& p: parser.parameters())
- {
- cout << "\t" << p.first << " = " << p.second << endl;
- }
-
- if (!args.empty())
- {
- for (string& s: args)
- {
- vector<string> keyval;
- Split(s, '=', back_inserter(keyval));
- if (keyval.size() < 2)
- keyval.push_back("");
- parser[keyval[0]] = keyval[1];
- }
-
- cout << "REASSEMBLED: " << parser.makeUri() << endl;
- }
- return 0;
-}
-
-#endif
+++ /dev/null
-/*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2018 Haivision Systems Inc.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- */
-
-#ifndef INC_SRT_URL_PARSER_H
-#define INC_SRT_URL_PARSER_H
-
-#include <string>
-#include <map>
-#include <cstdlib>
-#include "utilities.h"
-
-
-//++
-// UriParser
-//--
-
-class UriParser
-{
-// Construction
-public:
-
- enum DefaultExpect { EXPECT_FILE, EXPECT_HOST };
- enum Type
- {
- UNKNOWN, FILE, UDP, TCP, SRT, RTMP, HTTP, RTP
- };
-
- UriParser(const std::string& strUrl, DefaultExpect exp = EXPECT_FILE);
- UriParser(): m_uriType(UNKNOWN) {}
- virtual ~UriParser(void);
-
- // Some predefined types
- Type type() const;
-
- typedef MapProxy<std::string, std::string> ParamProxy;
-
-// Operations
-public:
- std::string uri() const { return m_origUri; }
- std::string proto() const;
- std::string scheme() const { return proto(); }
- std::string host() const;
- std::string port() const;
- unsigned short int portno() const;
- std::string hostport() const { return host() + ":" + port(); }
- std::string path() const;
- std::string queryValue(const std::string& strKey) const;
- std::string makeUri();
- ParamProxy operator[](const std::string& key) { return ParamProxy(m_mapQuery, key); }
- const std::map<std::string, std::string>& parameters() const { return m_mapQuery; }
- typedef std::map<std::string, std::string>::const_iterator query_it;
-
-private:
- void Parse(const std::string& strUrl, DefaultExpect);
-
-// Overridables
-public:
-
-// Overrides
-public:
-
-// Data
-private:
- std::string m_origUri;
- std::string m_proto;
- std::string m_host;
- std::string m_port;
- std::string m_path;
- Type m_uriType;
- DefaultExpect m_expect;
-
- std::map<std::string, std::string> m_mapQuery;
-};
-
-//#define TEST1 1
-
-#endif // INC_SRT_URL_PARSER_H
+++ /dev/null
-/*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2018 Haivision Systems Inc.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- */
-
-#include "verbose.hpp"
-
-namespace Verbose
-{
- bool on = false;
- std::ostream* cverb = &std::cerr;
-#if SRT_ENABLE_VERBOSE_LOCK
- std::mutex vlock;
-#endif
-
- Log& Log::operator<<(LogNoEol)
- {
- noeol = true;
- if (on)
- {
- (*cverb) << std::flush;
- }
- return *this;
- }
-
-#if SRT_ENABLE_VERBOSE_LOCK
- Log& Log::operator<<(LogLock)
- {
- lockline = true;
- return *this;
- }
-#endif
-
- Log::~Log()
- {
- if (on && !noeol)
- {
-#if SRT_ENABLE_VERBOSE_LOCK
- if (lockline)
- {
- // Lock explicitly, as requested, and wait for the opportunity.
- vlock.lock();
- }
- else if (vlock.try_lock())
- {
- // Successfully locked, so unlock immediately, locking wasn't requeted.
- vlock.unlock();
- }
- else
- {
- // Failed to lock, which means that some other thread has locked it first.
- // This means that some other thread wants to print the whole line and doesn't
- // want to be disturbed during this process. Lock the thread then as this is
- // the only way to wait until it's unlocked. However, do not block your printing
- // with locking, because you were not requested to lock (treat this mutex as
- // an entry semaphore, which may only occasionally block the whole line).
- vlock.lock();
- vlock.unlock();
- }
-#endif
- (*cverb) << std::endl;
-#if SRT_ENABLE_VERBOSE_LOCK
-
- // If lockline is set, the lock was requested and WAS DONE, so unlock.
- // Otherwise locking WAS NOT DONE.
- if (lockline)
- vlock.unlock();
-#endif
- }
- }
-}
+++ /dev/null
-/*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2018 Haivision Systems Inc.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- */
-
-#ifndef INC_SRT_VERBOSE_HPP
-#define INC_SRT_VERBOSE_HPP
-
-#include <iostream>
-#if SRT_ENABLE_VERBOSE_LOCK
-#include <mutex>
-#endif
-
-namespace Verbose
-{
-
-extern bool on;
-extern std::ostream* cverb;
-
-struct LogNoEol { LogNoEol() {} };
-#if SRT_ENABLE_VERBOSE_LOCK
-struct LogLock { LogLock() {} };
-#endif
-
-class Log
-{
- bool noeol = false;
-#if SRT_ENABLE_VERBOSE_LOCK
- bool lockline = false;
-#endif
-
- // Disallow creating dynamic objects
- void* operator new(size_t);
-
-public:
-
- template <class V>
- Log& operator<<(const V& arg)
- {
- // Template - must be here; extern template requires
- // predefined specializations.
- if (on)
- (*cverb) << arg;
- return *this;
- }
-
- Log& operator<<(LogNoEol);
-#if SRT_ENABLE_VERBOSE_LOCK
- Log& operator<<(LogLock);
-#endif
- ~Log();
-};
-
-
-class ErrLog: public Log
-{
-public:
-
- template <class V>
- ErrLog& operator<<(const V& arg)
- {
- // Template - must be here; extern template requires
- // predefined specializations.
- if (on)
- (*cverb) << arg;
- else
- std::cerr << arg;
- return *this;
- }
-};
-
-// terminal
-inline void Print(Log& ) {}
-
-template <typename Arg1, typename... Args>
-inline void Print(Log& out, Arg1&& arg1, Args&&... args)
-{
- out << arg1;
- Print(out, args...);
-}
-
-}
-
-inline Verbose::Log Verb() { return Verbose::Log(); }
-inline Verbose::ErrLog Verror() { return Verbose::ErrLog(); }
-
-template <typename... Args>
-inline void Verb(Args&&... args)
-{
- Verbose::Log log;
- Verbose::Print(log, args...);
-}
-
-
-// Manipulator tags
-static const Verbose::LogNoEol VerbNoEOL;
-#if SRT_ENABLE_VERBOSE_LOCK
-static const Verbose::LogLock VerbLock;
-#endif
-
-#endif
+++ /dev/null
-
-PUBLIC HEADERS
-win/syslog_defs.h
-#
-# These are included by platform_sys.h header contained in ../srtcore/filelist.maf
-#
-win/unistd.h
-
-SOURCES
-win_time.cpp
+++ /dev/null
-#ifndef INC_SRT_WINDOWS_SYSLOG_DEFS_H
-#define INC_SRT_WINDOWS_SYSLOG_DEFS_H
-
-#define LOG_EMERG 0
-#define LOG_ALERT 1
-#define LOG_CRIT 2
-#define LOG_ERR 3
-#define LOG_WARNING 4
-#define LOG_NOTICE 5
-#define LOG_INFO 6
-#define LOG_DEBUG 7
-
-#define LOG_PRIMASK 0x07
-
-#define LOG_PRI(p) ((p) & LOG_PRIMASK)
-#define LOG_MAKEPRI(fac, pri) (((fac) << 3) | (pri))
-
-#define LOG_KERN (0<<3)
-#define LOG_USER (1<<3)
-#define LOG_MAIL (2<<3)
-#define LOG_DAEMON (3<<3)
-#define LOG_AUTH (4<<3)
-#define LOG_SYSLOG (5<<3)
-#define LOG_LPR (6<<3)
-#define LOG_NEWS (7<<3)
-#define LOG_UUCP (8<<3)
-#define LOG_CRON (9<<3)
-#define LOG_AUTHPRIV (10<<3)
-#define LOG_FTP (11<<3)
-
-/* Codes through 15 are reserved for system use */
-#define LOG_LOCAL0 (16<<3)
-#define LOG_LOCAL1 (17<<3)
-#define LOG_LOCAL2 (18<<3)
-#define LOG_LOCAL3 (19<<3)
-#define LOG_LOCAL4 (20<<3)
-#define LOG_LOCAL5 (21<<3)
-#define LOG_LOCAL6 (22<<3)
-#define LOG_LOCAL7 (23<<3)
-
-#define LOG_NFACILITIES 24
-#define LOG_FACMASK 0x03f8
-#define LOG_FAC(p) (((p) & LOG_FACMASK) >> 3)
-
-#endif
+++ /dev/null
-#ifndef _UNISTD_H
-#define _UNISTD_H 1
-
-/* This file intended to serve as a drop-in replacement for
- * unistd.h on Windows
- * Please add functionality as neeeded
- */
-
-#include <stdlib.h>
-#include <io.h>
-//#include <getopt.h> /* getopt at: https://gist.github.com/ashelly/7776712*/
-#include <process.h> /* for getpid() and the exec..() family */
-#include <direct.h> /* for _getcwd() and _chdir() */
-
-#define srandom srand
-#define random rand
-
-/* Values for the second argument to access.
- These may be OR'd together. */
-#define R_OK 4 /* Test for read permission. */
-#define W_OK 2 /* Test for write permission. */
-//#define X_OK 1 /* execute permission - unsupported in windows*/
-#define F_OK 0 /* Test for existence. */
-
-#define access _access
-#define dup2 _dup2
-#define execve _execve
-#define ftruncate _chsize
-#define unlink _unlink
-#define fileno _fileno
-#define getcwd _getcwd
-#define chdir _chdir
-#define isatty _isatty
-#define lseek _lseek
-/* read, write, and close are NOT being #defined here, because while there are file handle specific versions for Windows, they probably don't work for sockets. You need to look at your app and consider whether to call e.g. closesocket(). */
-
-#define ssize_t int
-
-#define STDIN_FILENO 0
-#define STDOUT_FILENO 1
-#define STDERR_FILENO 2
-/* should be in some equivalent to <sys/types.h> */
-typedef __int8 int8_t;
-typedef __int16 int16_t;
-typedef __int32 int32_t;
-typedef __int64 int64_t;
-typedef unsigned __int8 uint8_t;
-typedef unsigned __int16 uint16_t;
-typedef unsigned __int32 uint32_t;
-typedef unsigned __int64 uint64_t;
-
-#endif /* unistd.h */
+++ /dev/null
-#ifndef INC_SRT_WIN_WINTIME
-#define INC_SRT_WIN_WINTIME
-
-#include <winsock2.h>
-#include <windows.h>
-// HACK: This include is a workaround for a bug in the MinGW headers
-// where pthread.h, which defines _POSIX_THREAD_SAFE_FUNCTIONS,
-// has to be included before time.h so that time.h defines
-// localtime_r correctly
-#include <time.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#if !defined(_MSC_VER)
- #define SRTCOMPAT_WINTIME_STATIC_INLINE_DECL static inline
-#else
- // NOTE: MVC Does not like static inline for C functions in some versions.
- // so just use static for MVC.
- #define SRTCOMPAT_WINTIME_STATIC_INLINE_DECL static
-#endif
-
-#ifndef _TIMEZONE_DEFINED /* also in sys/time.h */
-#define _TIMEZONE_DEFINED
-struct timezone
-{
- int tz_minuteswest; /* minutes W of Greenwich */
- int tz_dsttime; /* type of dst correction */
-};
-#endif
-
-void SRTCompat_timeradd(
- struct timeval *a, struct timeval *b, struct timeval *result);
-SRTCOMPAT_WINTIME_STATIC_INLINE_DECL void timeradd(
- struct timeval *a, struct timeval *b, struct timeval *result)
-{
- SRTCompat_timeradd(a, b, result);
-}
-
-int SRTCompat_gettimeofday(
- struct timeval* tp, struct timezone* tz);
-SRTCOMPAT_WINTIME_STATIC_INLINE_DECL int gettimeofday(
- struct timeval* tp, struct timezone* tz)
-{
- return SRTCompat_gettimeofday(tp, tz);
-}
-
-#undef SRTCOMPAT_WINTIME_STATIC_INLINE_DECL
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // INC_SRT_WIN_WINTIME
+++ /dev/null
-/*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2018 Haivision Systems Inc.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- */
-
-/*****************************************************************************
-written by
- Haivision Systems Inc.
- *****************************************************************************/
-
-#include "win/wintime.h"
-#include <sys/timeb.h>
-
-void SRTCompat_timeradd(struct timeval *a, struct timeval *b, struct timeval *result)
-{
- result->tv_sec = a->tv_sec + b->tv_sec;
- result->tv_usec = a->tv_usec + b->tv_usec;
- if (result->tv_usec >= 1000000)
- {
- result->tv_sec++;
- result->tv_usec -= 1000000;
- }
-}
-
-int SRTCompat_gettimeofday(struct timeval* tp, struct timezone*)
-{
- struct timeb tb;
- ftime(&tb);
- tp->tv_sec = (long)tb.time;
- tp->tv_usec = 1000*tb.millitm;
- return 0;
-}
+++ /dev/null
-/*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2021 Haivision Systems Inc.
- *
- * 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.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; If not, see <http://www.gnu.org/licenses/>
- */
-
-
-#include <stdio.h>
-#include <stdlib.h>
-#ifdef _WIN32
-#define usleep(x) Sleep(x / 1000)
-#else
-#include <unistd.h>
-#endif
-
-#include "srt.h"
-
-int main(int argc, char** argv)
-{
- int ss, st;
- struct sockaddr_in sa;
- const int no = 0;
- const char message [] = "This message should be sent to the other side";
-
- if (argc != 3) {
- fprintf(stderr, "Usage: %s <host> <port>\n", argv[0]);
- return 1;
- }
-
- printf("SRT startup\n");
- srt_startup();
-
- printf("Creating SRT socket\n");
- ss = srt_create_socket();
- if (ss == SRT_ERROR)
- {
- fprintf(stderr, "srt_socket: %s\n", srt_getlasterror_str());
- return 1;
- }
-
- printf("Creating remote address\n");
- sa.sin_family = AF_INET;
- sa.sin_port = htons(atoi(argv[2]));
- if (inet_pton(AF_INET, argv[1], &sa.sin_addr) != 1)
- {
- return 1;
- }
-
- int epollid = srt_epoll_create();
- if (epollid == -1)
- {
- fprintf(stderr, "srt_epoll_create: %s\n", srt_getlasterror_str());
- return 1;
- }
-
- printf("srt setsockflag\n");
- if (SRT_ERROR == srt_setsockflag(ss, SRTO_RCVSYN, &no, sizeof no)
- || SRT_ERROR == srt_setsockflag(ss, SRTO_SNDSYN, &no, sizeof no))
- {
- fprintf(stderr, "SRTO_SNDSYN or SRTO_RCVSYN: %s\n", srt_getlasterror_str());
- return 1;
- }
-
- // When a caller is connected, a write-readiness event is triggered.
- int modes = SRT_EPOLL_OUT | SRT_EPOLL_ERR;
- if (SRT_ERROR == srt_epoll_add_usock(epollid, ss, &modes))
- {
- fprintf(stderr, "srt_epoll_add_usock: %s\n", srt_getlasterror_str());
- return 1;
- }
-
- printf("srt connect\n");
- st = srt_connect(ss, (struct sockaddr*)&sa, sizeof sa);
- if (st == SRT_ERROR)
- {
- fprintf(stderr, "srt_connect: %s\n", srt_getlasterror_str());
- return 1;
- }
-
- // We had subscribed for write-readiness or error.
- // Write readiness comes in wready array,
- // error is notified via rready in this case.
- int rlen = 1;
- SRTSOCKET rready;
- int wlen = 1;
- SRTSOCKET wready;
- if (srt_epoll_wait(epollid, &rready, &rlen, &wready, &wlen, -1, 0, 0, 0, 0) != -1)
- {
- SRT_SOCKSTATUS state = srt_getsockstate(ss);
- if (state != SRTS_CONNECTED || rlen > 0) // rlen > 0 - an error notification
- {
- fprintf(stderr, "srt_epoll_wait: reject reason %s\n", srt_rejectreason_str(srt_getrejectreason(rready)));
- return 1;
- }
-
- if (wlen != 1 || wready != ss)
- {
- fprintf(stderr, "srt_epoll_wait: wlen %d, wready %d, socket %d\n", wlen, wready, ss);
- return 1;
- }
- }
- else
- {
- fprintf(stderr, "srt_connect: %s\n", srt_getlasterror_str());
- return 1;
- }
-
- int i;
- for (i = 0; i < 100; i++)
- {
- rready = SRT_INVALID_SOCK;
- rlen = 1;
- wready = SRT_INVALID_SOCK;
- wlen = 1;
-
- // As we have subscribed only for write-readiness or error events,
- // but have not subscribed for read-readiness,
- // through readfds we are notified about an error.
- int timeout_ms = 5000; // ms
- int res = srt_epoll_wait(epollid, &rready, &rlen, &wready, &wlen, timeout_ms, 0, 0, 0, 0);
- if (res == SRT_ERROR || rlen > 0)
- {
- fprintf(stderr, "srt_epoll_wait: %s\n", srt_getlasterror_str());
- return 1;
- }
-
- printf("srt sendmsg2 #%d >> %s\n", i, message);
- st = srt_sendmsg2(ss, message, sizeof message, NULL);
- if (st == SRT_ERROR)
- {
- fprintf(stderr, "srt_sendmsg: %s\n", srt_getlasterror_str());
- return 1;
- }
-
- usleep(1000); // 1 ms
- }
-
- // Let's wait a bit so that all packets reach destination
- usleep(100000); // 100 ms
-
- // In live mode the connection will be closed even if some packets were not yet acknowledged.
- printf("srt close\n");
- st = srt_close(ss);
- if (st == SRT_ERROR)
- {
- fprintf(stderr, "srt_close: %s\n", srt_getlasterror_str());
- return 1;
- }
-
- printf("srt cleanup\n");
- srt_cleanup();
- return 0;
-}
+++ /dev/null
-#ifndef _WIN32
- #include <arpa/inet.h>
- #include <netdb.h>
-#else
- #include <winsock2.h>
- #include <ws2tcpip.h>
-#endif
-#include <fstream>
-#include <iostream>
-#include <cstdlib>
-#include <cstring>
-#include <srt.h>
-
-using namespace std;
-
-int main(int argc, char* argv[])
-{
- if ((argc != 5) || (0 == atoi(argv[2])))
- {
- cout << "usage: recvfile server_ip server_port remote_filename local_filename" << endl;
- return -1;
- }
-
- // Use this function to initialize the UDT library
- srt_startup();
-
- srt_setloglevel(srt_logging::LogLevel::debug);
-
- struct addrinfo hints, *peer;
-
- memset(&hints, 0, sizeof(struct addrinfo));
- hints.ai_flags = AI_PASSIVE;
- hints.ai_family = AF_INET;
- hints.ai_socktype = SOCK_DGRAM;
-
- SRTSOCKET fhandle = srt_create_socket();
- // SRT requires that third argument is always SOCK_DGRAM. The Stream API is set by an option,
- // although there's also lots of other options to be set, for which there's a convenience option,
- // SRTO_TRANSTYPE.
- SRT_TRANSTYPE tt = SRTT_FILE;
- srt_setsockopt(fhandle, 0, SRTO_TRANSTYPE, &tt, sizeof tt);
-
- if (0 != getaddrinfo(argv[1], argv[2], &hints, &peer))
- {
- cout << "incorrect server/peer address. " << argv[1] << ":" << argv[2] << endl;
- return -1;
- }
-
- // Connect to the server, implicit bind.
- if (SRT_ERROR == srt_connect(fhandle, peer->ai_addr, peer->ai_addrlen))
- {
- cout << "connect: " << srt_getlasterror_str() << endl;
- return -1;
- }
-
- freeaddrinfo(peer);
-
- // Send name information of the requested file.
- int len = strlen(argv[3]);
-
- if (SRT_ERROR == srt_send(fhandle, (char*)&len, sizeof(int)))
- {
- cout << "send: " << srt_getlasterror_str() << endl;
- return -1;
- }
-
- if (SRT_ERROR == srt_send(fhandle, argv[3], len))
- {
- cout << "send: " << srt_getlasterror_str() << endl;
- return -1;
- }
-
- // Get size information.
- int64_t size;
-
- if (SRT_ERROR == srt_recv(fhandle, (char*)&size, sizeof(int64_t)))
- {
- cout << "send: " << srt_getlasterror_str() << endl;
- return -1;
- }
-
- if (size < 0)
- {
- cout << "no such file " << argv[3] << " on the server\n";
- return -1;
- }
-
- // Receive the file.
- int64_t recvsize;
- int64_t offset = 0;
-
- SRT_TRACEBSTATS trace;
- srt_bstats(fhandle, &trace, true);
-
- if (SRT_ERROR == (recvsize = srt_recvfile(fhandle, argv[4], &offset, size, SRT_DEFAULT_RECVFILE_BLOCK)))
- {
- cout << "recvfile: " << srt_getlasterror_str() << endl;
- return -1;
- }
-
- srt_bstats(fhandle, &trace, true);
-
- cout << "speed = " << trace.mbpsRecvRate << "Mbits/sec" << endl;
- int losspercent = 100*trace.pktRcvLossTotal/trace.pktRecv;
- cout << "loss = " << trace.pktRcvLossTotal << "pkt (" << losspercent << "%)\n";
-
- srt_close(fhandle);
-
- // Signal to the SRT library to clean up all allocated sockets and resources.
- srt_cleanup();
-
- return 0;
-}
+++ /dev/null
-#ifndef WIN32
- #include <cstdlib>
- #include <netdb.h>
-#else
- #include <winsock2.h>
- #include <ws2tcpip.h>
-#endif
-#include <fstream>
-#include <iostream>
-#include <string>
-#include <cstring>
-#include <srt.h>
-#include <assert.h> // assert
-
-using namespace std;
-
-int main(int argc, char* argv[])
-{
- // usage: recvlive [server_port]
- if ((2 < argc) || ((2 == argc) && (0 == atoi(argv[1]))))
- {
- cout << "usage: recvlive [server_port]" << endl;
- return 0;
- }
-
- // use this function to initialize the UDT library
- srt_startup();
-
- srt_setloglevel(srt_logging::LogLevel::debug);
-
- addrinfo hints;
- addrinfo* res;
-
- memset(&hints, 0, sizeof(struct addrinfo));
- hints.ai_flags = AI_PASSIVE;
- hints.ai_family = AF_INET;
- hints.ai_socktype = SOCK_DGRAM;
-
- string service("9000");
- if (2 == argc)
- service = argv[1];
-
- if (0 != getaddrinfo(NULL, service.c_str(), &hints, &res))
- {
- cout << "illegal port number or port is busy.\n" << endl;
- return 0;
- }
-
- SRTSOCKET sfd = srt_create_socket();
- if (SRT_INVALID_SOCK == sfd)
- {
- cout << "srt_socket: " << srt_getlasterror_str() << endl;
- return 0;
- }
-
- // SRT requires that third argument is always SOCK_DGRAM. The Stream API is set by an option,
- // although there's also lots of other options to be set, for which there's a convenience option,
- // SRTO_TRANSTYPE.
- // SRT_TRANSTYPE tt = SRTT_LIVE;
- // if (SRT_ERROR == srt_setsockopt(sfd, 0, SRTO_TRANSTYPE, &tt, sizeof tt))
- // {
- // cout << "srt_setsockopt: " << srt_getlasterror_str() << endl;
- // return 0;
- // }
-
- bool no = false;
- if (SRT_ERROR == srt_setsockopt(sfd, 0, SRTO_RCVSYN, &no, sizeof no))
- {
- cout << "srt_setsockopt: " << srt_getlasterror_str() << endl;
- return 0;
- }
-
- // Test the deprecated option feature here:
- //srt_setsockopt(sfd, 0, SRTO_STRICTENC, &no, sizeof no);
-
- // Windows UDP issue
- // For better performance, modify HKLM\System\CurrentControlSet\Services\Afd\Parameters\FastSendDatagramThreshold
-#ifdef WIN32
- int mss = 1052;
- srt_setsockopt(sfd, 0, SRTO_MSS, &mss, sizeof(int));
-#endif
-
- // int64_t maxbw = 5000000;
- // srt_setsockopt(sfd, 0, SRTO_MAXBW, &maxbw, sizeof maxbw);
-
- if (SRT_ERROR == srt_bind(sfd, res->ai_addr, res->ai_addrlen))
- {
- cout << "srt_bind: " << srt_getlasterror_str() << endl;
- return 0;
- }
-
- freeaddrinfo(res);
-
- cout << "server is ready at port: " << service << endl;
-
- if (SRT_ERROR == srt_listen(sfd, 10))
- {
- cout << "srt_listen: " << srt_getlasterror_str() << endl;
- return 0;
- }
-
- int epid = srt_epoll_create();
- if (epid < 0)
- {
- cout << "srt_epoll_create: " << srt_getlasterror_str() << endl;
- return 0;
- }
-
- int events = SRT_EPOLL_IN | SRT_EPOLL_ERR;
- if (SRT_ERROR == srt_epoll_add_usock(epid, sfd, &events))
- {
- cout << "srt_epoll_add_usock: " << srt_getlasterror_str() << endl;
- return 0;
- }
-
- const int srtrfdslenmax = 100;
- SRTSOCKET srtrfds[srtrfdslenmax];
- char data[1500];
-
- // the event loop
- while (true)
- {
- int srtrfdslen = srtrfdslenmax;
- int n = srt_epoll_wait(epid, &srtrfds[0], &srtrfdslen, 0, 0, 100, 0, 0, 0, 0);
- assert(n <= srtrfdslen);
- for (int i = 0; i < n; i++)
- {
- SRTSOCKET s = srtrfds[i];
- SRT_SOCKSTATUS status = srt_getsockstate(s);
- if ((status == SRTS_BROKEN) ||
- (status == SRTS_NONEXIST) ||
- (status == SRTS_CLOSED))
- {
- cout << "source disconnected. status=" << status << endl;
- srt_close(s);
- continue;
- }
- else if (s == sfd)
- {
- assert(status == SRTS_LISTENING);
-
- SRTSOCKET fhandle;
- sockaddr_storage clientaddr;
- int addrlen = sizeof(clientaddr);
-
- fhandle = srt_accept(sfd, (sockaddr*)&clientaddr, &addrlen);
- if (SRT_INVALID_SOCK == fhandle)
- {
- cout << "srt_accept: " << srt_getlasterror_str() << endl;
- return 0;
- }
-
- char clienthost[NI_MAXHOST];
- char clientservice[NI_MAXSERV];
- getnameinfo((sockaddr *)&clientaddr, addrlen,
- clienthost, sizeof(clienthost),
- clientservice, sizeof(clientservice), NI_NUMERICHOST|NI_NUMERICSERV);
- cout << "new connection: " << clienthost << ":" << clientservice << endl;
-
- int events = SRT_EPOLL_IN | SRT_EPOLL_ERR;
- if (SRT_ERROR == srt_epoll_add_usock(epid, fhandle, &events))
- {
- cout << "srt_epoll_add_usock: " << srt_getlasterror_str() << endl;
- return 0;
- }
- }
- else
- {
- while (true)
- {
- int ret = srt_recvmsg(s, data, sizeof(data));
- if (SRT_ERROR == ret)
- {
- // EAGAIN for SRT READING
- if (SRT_EASYNCRCV != srt_getlasterror(NULL))
- {
- cout << "srt_recvmsg: " << srt_getlasterror_str() << endl;
- return 0;
- }
- break;
- }
- // cout << ret << " bytes received" << endl;
- }
- }
- }
- }
-
- srt_close(sfd);
-
- srt_epoll_release(epid);
-
- // use this function to release the UDT library
- srt_cleanup();
-
- return 0;
-}
-
-// Local Variables:
-// c-file-style: "ellemtel"
-// c-basic-offset: 3
-// compile-command: "g++ -Wall -O2 -std=c++11 -I.. -I../srtcore -o recvlive recvlive.cpp -L.. -lsrt -lpthread -L/usr/local/opt/openssl/lib -lssl -lcrypto"
-// End:
+++ /dev/null
-#ifndef _WIN32
- #include <cstdlib>
- #include <netdb.h>
-#else
- #include <winsock2.h>
- #include <ws2tcpip.h>
-#endif
-#include <fstream>
-#include <iostream>
-#include <string>
-#include <cstring>
-#include <srt.h>
-
-using namespace std;
-
-#ifndef _WIN32
-void* sendfile(void*);
-#else
-DWORD WINAPI sendfile(LPVOID);
-#endif
-
-int main(int argc, char* argv[])
-{
- if ((2 < argc) || ((2 == argc) && (0 == atoi(argv[1]))))
- {
- cout << "usage: sendfile [server_port]" << endl;
- return 0;
- }
-
- // Initialize the SRT library.
- srt_startup();
-
- addrinfo hints;
- addrinfo* res;
-
- memset(&hints, 0, sizeof(struct addrinfo));
- hints.ai_flags = AI_PASSIVE;
- hints.ai_family = AF_INET;
- hints.ai_socktype = SOCK_DGRAM;
-
- string service("9000");
- if (2 == argc)
- service = argv[1];
-
- if (0 != getaddrinfo(NULL, service.c_str(), &hints, &res))
- {
- cout << "illegal port number or port is busy.\n" << endl;
- return 0;
- }
-
- SRTSOCKET serv = srt_create_socket();
-
- // SRT requires that third argument is always SOCK_DGRAM. The Stream API is set by an option,
- // although there's also lots of other options to be set, for which there's a convenience option,
- // SRTO_TRANSTYPE.
- SRT_TRANSTYPE tt = SRTT_FILE;
- srt_setsockopt(serv, 0, SRTO_TRANSTYPE, &tt, sizeof tt);
-
- // Windows UDP issue
- // For better performance, modify HKLM\System\CurrentControlSet\Services\Afd\Parameters\FastSendDatagramThreshold
-#ifdef _WIN32
- int mss = 1052;
- srt_setsockopt(serv, 0, SRTO_MSS, &mss, sizeof(int));
-#endif
-
- if (SRT_ERROR == srt_bind(serv, res->ai_addr, res->ai_addrlen))
- {
- cout << "bind: " << srt_getlasterror_str() << endl;
- return 0;
- }
-
- freeaddrinfo(res);
-
- cout << "server is ready at port: " << service << endl;
- srt_listen(serv, 10);
-
- sockaddr_storage clientaddr;
- int addrlen = sizeof(clientaddr);
-
- SRTSOCKET fhandle;
-
- // Accept multiple client connections.
- while (true)
- {
- if (SRT_INVALID_SOCK == (fhandle = srt_accept(serv, (sockaddr*)&clientaddr, &addrlen)))
- {
- cout << "accept: " << srt_getlasterror_str() << endl;
- return 0;
- }
-
- char clienthost[NI_MAXHOST];
- char clientservice[NI_MAXSERV];
- getnameinfo((sockaddr *)&clientaddr, addrlen, clienthost, sizeof(clienthost), clientservice, sizeof(clientservice), NI_NUMERICHOST|NI_NUMERICSERV);
- cout << "new connection: " << clienthost << ":" << clientservice << endl;
-
-#ifndef _WIN32
- pthread_t filethread;
- pthread_create(&filethread, NULL, sendfile, new SRTSOCKET(fhandle));
- pthread_detach(filethread);
-#else
- CreateThread(NULL, 0, sendfile, new SRTSOCKET(fhandle), 0, NULL);
-#endif
- }
-
- srt_close(serv);
-
- // Signal to the SRT library to clean up all allocated sockets and resources.
- srt_cleanup();
-
- return 0;
-}
-
-#ifndef _WIN32
-void* sendfile(void* usocket)
-#else
-DWORD WINAPI sendfile(LPVOID usocket)
-#endif
-{
- SRTSOCKET fhandle = *(SRTSOCKET*)usocket;
- delete (SRTSOCKET*)usocket;
-
- // Acquiring file name information from client.
- char file[1024];
- int len;
-
- if (SRT_ERROR == srt_recv(fhandle, (char*)&len, sizeof(int)))
- {
- cout << "recv: " << srt_getlasterror_str() << endl;
- return 0;
- }
-
- if (SRT_ERROR == srt_recv(fhandle, file, len))
- {
- cout << "recv: " << srt_getlasterror_str() << endl;
- return 0;
- }
- file[len] = '\0';
-
- // Open the file only to know its size.
- fstream ifs(file, ios::in | ios::binary);
- ifs.seekg(0, ios::end);
- const int64_t size = ifs.tellg();
- ifs.close();
-
- // Send file size.
- if (SRT_ERROR == srt_send(fhandle, (char*)&size, sizeof(int64_t)))
- {
- cout << "send: " << srt_getlasterror_str() << endl;
- return 0;
- }
-
- SRT_TRACEBSTATS trace;
- srt_bstats(fhandle, &trace, true);
-
- // Send the file itself.
- int64_t offset = 0;
- if (SRT_ERROR == srt_sendfile(fhandle, file, &offset, size, SRT_DEFAULT_SENDFILE_BLOCK))
- {
- cout << "sendfile: " << srt_getlasterror_str() << endl;
- return 0;
- }
-
- srt_bstats(fhandle, &trace, true);
- cout << "speed = " << trace.mbpsSendRate << "Mbits/sec" << endl;
- const int64_t losspercent = 100 * trace.pktSndLossTotal / trace.pktSent;
- cout << "network loss = " << trace.pktSndLossTotal << "pkts (" << losspercent << "%)\n";
-
- srt_close(fhandle);
-
- #ifndef _WIN32
- return NULL;
- #else
- return 0;
- #endif
-}
+++ /dev/null
-/*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2017 Haivision Systems Inc.
- *
- * 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.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; If not, see <http://www.gnu.org/licenses/>
- */
-
-
-#include <stdio.h>
-#include <stdlib.h>
-#ifdef _WIN32
-#define usleep(x) Sleep(x / 1000)
-#else
-#include <unistd.h>
-#endif
-
-#include "srt.h"
-
-struct
-{
- const char* name;
- int gtype;
-} group_types [] = {
- {
- "broadcast", SRT_GTYPE_BROADCAST
- }
- // Others will follow
-};
-
-#define SIZE(array) (sizeof array/sizeof(array[0]))
-
-// Note that in this example application there's a socket
-// used first to connect to the service and then it will be
-// used for writing. Therefore the same function will be used
-// for waiting for the socket to be connected and then to wait
-// for write-ready on the socket used for transmission. For a
-// model of waiting for read-ready see test-c-server-bonding.c file.
-int WaitForWriteReady(int eid, SRTSOCKET ss)
-{
- int ready_err[2];
- int ready_err_len = 2;
- int ready_out[2];
- int ready_out_len = 2;
-
- int st = srt_epoll_wait(eid, ready_err, &ready_err_len, ready_out, &ready_out_len, -1,
- 0, 0, 0, 0);
-
- // Note: with indefinite wait time we can either have a connection reported
- // or possibly error. Also srt_epoll_wait never returns 0 - at least the number
- // of ready connections is reported or -1 is returned for error, including timeout.
- if (st < 1)
- {
- fprintf(stderr, "srt_epoll_wait: %s\n", srt_getlasterror_str());
- return 0;
- }
-
- // Check if this was reported as error-ready, in which case it doesn't
- // matter if read-ready.
- if (ready_err[0] == ss)
- {
- int reason = srt_getrejectreason(ss);
- fprintf(stderr, "srt_epoll_wait: socket @%d reported error reason=%d: %s\n", ss, reason, srt_rejectreason_str(reason));
- return 0;
- }
-
- return 1;
-}
-
-int main(int argc, char** argv)
-{
- int ss, st;
- struct sockaddr_in sa;
- //int yes = 1; // for options, none needed so far
- const char message [] = "This message should be sent to the other side";
-
- if (argc < 3)
- {
- fprintf(stderr, "Usage: %s <type> {<host> <port>}... \n", argv[0]);
- return 1;
- }
-
- int gtype = SRT_GTYPE_BROADCAST;
- size_t i;
- for (i = 0; i < SIZE(group_types); ++i)
- if (0 == strcmp(group_types[i].name, argv[1]))
- {
- gtype = group_types[i].gtype;
- break;
- }
-
- int is_nonblocking = 0;
- size_t nmemb = argc - 2;
- if (nmemb < 2)
- {
- fprintf(stderr, "Usage error: no members specified\n");
- return 1;
- }
-
- if (nmemb % 2)
- {
- // Last argument is then optionset
- --nmemb;
- const char* opt = argv[argc-1];
- if (strchr(opt, 'n'))
- is_nonblocking = 1;
- }
-
- nmemb /= 2;
-
- printf("srt startup\n");
- srt_startup();
-
- // Declare all variables before any destructive goto.
- // In C++ such a code that jumps over initialization would be illegal,
- // in C it causes an uninitialized value to be used.
- int eid = -1;
- int write_modes = SRT_EPOLL_OUT | SRT_EPOLL_ERR;
- SRT_SOCKGROUPDATA* grpdata = NULL;
- SRT_SOCKGROUPCONFIG* grpconfig = calloc(nmemb, sizeof (SRT_SOCKGROUPCONFIG));
-
- printf("srt group\n");
- ss = srt_create_group(gtype);
- if (ss == SRT_ERROR)
- {
- fprintf(stderr, "srt_create_group: %s\n", srt_getlasterror_str());
- goto end;
- }
-
- const int B = 2;
-
- for (i = 0; i < nmemb; ++i)
- {
- printf("srt remote address #%zi\n", i);
-
- sa.sin_family = AF_INET;
- sa.sin_port = htons(atoi(argv[B + 2*i + 1]));
- if (inet_pton(AF_INET, argv[B + 2*i], &sa.sin_addr) != 1)
- {
- fprintf(stderr, "inet_pton: can't resolve address: %s\n", argv[B + 2*i]);
- goto end;
- }
-
- grpconfig[i] = srt_prepare_endpoint(NULL, (struct sockaddr*)&sa, sizeof sa);
- }
-
- if (is_nonblocking)
- {
- int blockingmode = 0;
- srt_setsockflag(ss, SRTO_RCVSYN, &blockingmode, sizeof (blockingmode));
- eid = srt_epoll_create();
- srt_epoll_add_usock(eid, ss, &write_modes);
- }
-
- printf("srt connect (group)\n");
-
- // Note: this function unblocks at the moment when at least one connection
- // from the array is established (no matter which one); the others will
- // continue in background.
- st = srt_connect_group(ss, grpconfig, nmemb);
- if (st == SRT_ERROR)
- {
- fprintf(stderr, "srt_connect: %s\n", srt_getlasterror_str());
- goto end;
- }
-
- // In non-blocking mode the srt_connect function returns immediately
- // and displays only errors of the initial usage, not runtime errors.
- // These could be reported by epoll.
- if (is_nonblocking)
- {
- // WRITE-ready means connected
-
- printf("srt wait for socket reporting connection success\n");
- if (!WaitForWriteReady(eid, ss))
- goto end;
- }
-
- // In non-blocking mode now is the time to possibly change the epoll.
- // As this socket will be used for writing, it is in the right mode already.
- // Just set the right flag, as for non-blocking connect it needs RCVSYN.
- if (is_nonblocking)
- {
- int blockingmode = 0;
- srt_setsockflag(ss, SRTO_SNDSYN, &blockingmode, sizeof (blockingmode));
- }
-
- // Important: Normally you need that at least one link is ready for
- // the group link to be ready. All but first are done actually in
- // background, so this sleep only makes it more probable. If you'd like
- // to make sure that ALL links are established - by some reason - then
- // you'd have to subscribe for epoll event SRT_EPOLL_UPDATE and after the
- // connect function exits do checks by srt_group_data to see if all links
- // are established, and if not, repeat it after srt_epoll_wait for the
- // SRT_EPOLL_UPDATE signal.
- printf("sleeping 1s to make it probable all links are established\n");
- sleep(1);
-
- grpdata = calloc(nmemb, sizeof (SRT_SOCKGROUPDATA));
-
- for (i = 0; i < 100; i++)
- {
- printf("srt sendmsg2 #%zd >> %s\n",i,message);
-
- SRT_MSGCTRL mc = srt_msgctrl_default;
- mc.grpdata = grpdata;
- mc.grpdata_size = nmemb; // Set maximum known
-
- if (is_nonblocking)
- {
- // Block in epoll as srt_recvmsg2 will not block.
- if (!WaitForWriteReady(eid, ss))
- goto end;
- }
-
- st = srt_sendmsg2(ss, message, sizeof message, &mc);
- if (st == SRT_ERROR)
- {
- fprintf(stderr, "srt_sendmsg: %s\n", srt_getlasterror_str());
- goto end;
- }
-
- // Perform the group check. This can be used to recognize broken connections
- // and probably reestablish them by calling `srt_connect` for them. Here they
- // are only shown.
- printf(" ++ Group status [%zi]:", mc.grpdata_size);
- if (!mc.grpdata)
- {
- printf(" (ERROR: array too small!)\n");
- }
- else
- {
- for (i = 0; i < mc.grpdata_size; ++i)
- {
- printf( "[%zd] result=%d state=%s ", i, mc.grpdata[i].result,
- mc.grpdata[i].sockstate <= SRTS_CONNECTING ? "pending" :
- mc.grpdata[i].sockstate == SRTS_CONNECTED ? "connected" : "broken");
- }
- printf("\n");
- }
-
- usleep(1000); // 1 ms
- }
-
-end:
- if (eid != -1)
- {
- srt_epoll_release(eid);
- }
- printf("srt close\n");
- st = srt_close(ss);
- if (st == SRT_ERROR)
- {
- fprintf(stderr, "srt_close: %s\n", srt_getlasterror_str());
- return 1;
- }
-
- free(grpdata);
- free(grpconfig);
-
-//cleanup:
- printf("srt cleanup\n");
- srt_cleanup();
- return 0;
-}
+++ /dev/null
-/*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2017 Haivision Systems Inc.
- *
- * 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.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; If not, see <http://www.gnu.org/licenses/>
- */
-
-
-#include <stdio.h>
-#include <stdlib.h>
-#ifdef _WIN32
-#define usleep(x) Sleep(x / 1000)
-#else
-#include <unistd.h>
-#endif
-
-#include "srt.h"
-
-int main(int argc, char** argv)
-{
- int ss, st;
- struct sockaddr_in sa;
- int yes = 1;
- const char message [] = "This message should be sent to the other side";
-
- if (argc != 3) {
- fprintf(stderr, "Usage: %s <host> <port>\n", argv[0]);
- return 1;
- }
-
- printf("srt startup\n");
- srt_startup();
-
- printf("srt socket\n");
- ss = srt_create_socket();
- if (ss == SRT_ERROR)
- {
- fprintf(stderr, "srt_socket: %s\n", srt_getlasterror_str());
- return 1;
- }
-
- printf("srt remote address\n");
- sa.sin_family = AF_INET;
- sa.sin_port = htons(atoi(argv[2]));
- if (inet_pton(AF_INET, argv[1], &sa.sin_addr) != 1)
- {
- return 1;
- }
-
- printf("srt setsockflag\n");
- srt_setsockflag(ss, SRTO_SENDER, &yes, sizeof yes);
-
- // Test deprecated
- //srt_setsockflag(ss, SRTO_STRICTENC, &yes, sizeof yes);
-
- printf("srt connect\n");
- st = srt_connect(ss, (struct sockaddr*)&sa, sizeof sa);
- if (st == SRT_ERROR)
- {
- fprintf(stderr, "srt_connect: %s\n", srt_getlasterror_str());
- return 1;
- }
-
- int i;
- for (i = 0; i < 100; i++)
- {
- printf("srt sendmsg2 #%d >> %s\n",i,message);
- st = srt_sendmsg2(ss, message, sizeof message, NULL);
- if (st == SRT_ERROR)
- {
- fprintf(stderr, "srt_sendmsg: %s\n", srt_getlasterror_str());
- return 1;
- }
-
- usleep(1000); // 1 ms
- }
-
-
- printf("srt close\n");
- st = srt_close(ss);
- if (st == SRT_ERROR)
- {
- fprintf(stderr, "srt_close: %s\n", srt_getlasterror_str());
- return 1;
- }
-
- printf("srt cleanup\n");
- srt_cleanup();
- return 0;
-}
+++ /dev/null
-/*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2017 Haivision Systems Inc.
- *
- * 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.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; If not, see <http://www.gnu.org/licenses/>
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-
-
-#include "srt.h"
-
-// Note that in this example application there's a listening
-// socket, off which then a transmission socket is accepted,
-// then this socket will be used for reading. Therefore the same
-// function will be used for waiting for the listener to get the
-// accepted socket ready and then to wait for read-readiness on
-// the transmission socket. For a model of waiting for write-ready
-// see test-c-client-bonding.c file.
-int WaitForReadReady(int eid, SRTSOCKET ss)
-{
- int ready_in[2];
- int ready_in_len = 2;
- int ready_err[2];
- int ready_err_len = 2;
-
- int st = srt_epoll_wait(eid, ready_in, &ready_in_len, ready_err, &ready_err_len, -1,
- 0, 0, 0, 0);
-
- // Note: with indefinite wait time we can either have a connection reported
- // or possibly error. Also srt_epoll_wait never returns 0 - at least the number
- // of ready connections is reported or -1 is returned for error, including timeout.
- if (st < 1)
- {
- fprintf(stderr, "srt_epoll_wait: %s\n", srt_getlasterror_str());
- return 0;
- }
-
- // Check if this was reported as error-ready, in which case it doesn't
- // matter if read-ready.
- if (ready_err[0] == ss)
- {
- fprintf(stderr, "srt_epoll_wait: socket @%d reported error\n", ss);
- return 0;
- }
-
- if (ready_in[0] != ss)
- {
- fprintf(stderr, "srt_epoll_wait: socket @%d not reported ready\n", ss);
- return 0;
- }
-
- return 1;
-}
-
-int main(int argc, char** argv)
-{
- int globstatus = 0;
-
- int ss, st;
- struct sockaddr_in sa;
- int yes = 1;
- struct sockaddr_storage their_addr;
- SRT_SOCKGROUPDATA* grpdata = NULL;
-
- if (argc < 3 || argc > 4)
- {
- fprintf(stderr, "Usage: %s <host> <port> [options]\n", argv[0]);
- return 1;
- }
-
- printf("srt startup\n");
- srt_startup();
- // Since now, srt_cleanup() must be done before exiting.
-
- printf("srt socket\n");
- ss = srt_create_socket();
- if (ss == SRT_ERROR)
- {
- fprintf(stderr, "srt_socket: %s\n", srt_getlasterror_str());
- globstatus = 1;
- goto cleanup;
- }
- // Now that the socket is created, jump to 'end' on error.
-
- // Check options
- int is_nonblocking = 0;
- SRTSOCKET their_fd = SRT_INVALID_SOCK; // declared early because of gotos
- int eid = -1;
- int lsn_modes = SRT_EPOLL_IN | SRT_EPOLL_ERR;
- int read_modes = lsn_modes;
- if (argc > 3)
- {
- const char* opt = argv[3];
- if (strchr(opt, 'n'))
- is_nonblocking = 1;
- }
-
- printf("srt bind address\n");
- if (0 == strcmp(argv[1], "0"))
- {
- memset(&sa, 0, sizeof sa);
- }
- else if (inet_pton(AF_INET, argv[1], &sa.sin_addr) != 1)
- {
- fprintf(stderr, "srt_bind: Can't resolve address: %s\n", argv[1]);
- globstatus = 1;
- goto end;
- }
- sa.sin_family = AF_INET;
- sa.sin_port = htons(atoi(argv[2]));
-
- printf("srt setsockflag: groupconnect\n");
- srt_setsockflag(ss, SRTO_GROUPCONNECT, &yes, sizeof yes);
-
- printf("srt bind\n");
- st = srt_bind(ss, (struct sockaddr*)&sa, sizeof sa);
- if (st == SRT_ERROR)
- {
- fprintf(stderr, "srt_bind: %s\n", srt_getlasterror_str());
- globstatus = 1;
- goto end;
- }
-
- if (is_nonblocking)
- {
- int blockingmode = 0;
- srt_setsockflag(ss, SRTO_RCVSYN, &blockingmode, sizeof (blockingmode));
- eid = srt_epoll_create();
- srt_epoll_add_usock(eid, ss, &lsn_modes);
- }
-
- printf("srt listen\n");
-
- // We set here 10, just for a case. Every unit in this number
- // defines a maximum number of connections that can be pending
- // simultaneously - it doesn't matter here if particular connection
- // will belong to a bonding group or will be a single-socket connection.
- st = srt_listen(ss, 10);
- if (st == SRT_ERROR)
- {
- fprintf(stderr, "srt_listen: %s\n", srt_getlasterror_str());
- globstatus = 1;
- goto end;
- }
-
-
- // In this example, there will be prepared an array of 10 items.
- // The listener, however, doesn't know how many member connections
- // one bonded connection will contain, so a real application should be
- // prepared for dynamically adjusting the array size.
- const size_t N = 10;
- grpdata = calloc(N, sizeof (SRT_SOCKGROUPDATA));
-
- // In non-blocking mode you can't call srt_accept immediately.
- // You must first wait for readiness on the listener socket.
- if (is_nonblocking)
- {
- printf("srt wait for listener socket reporting in a new connection\n");
- if (!WaitForReadReady(eid, ss))
- goto end;
- }
-
- printf("srt accept\n");
- int addr_size = sizeof their_addr;
- their_fd = srt_accept(ss, (struct sockaddr *)&their_addr, &addr_size);
-
- if (their_fd == -1)
- {
- fprintf(stderr, "srt_accept: %s\n", srt_getlasterror_str());
- goto end;
- }
-
- printf("accepted socket: @%d\n", their_fd);
-
- // You never know if `srt_accept` is going to give you a socket or a group.
- // You have to check it on your own. The SRTO_GROUPCONNECT flag doesn't disallow
- // single socket connections.
- int isgroup = their_fd & SRTGROUP_MASK;
-
- if (!isgroup)
- {
- fprintf(stderr, "srt_accept: Accepted @%d is not a group???\n", their_fd);
- goto end;
- }
-
- if (is_nonblocking)
- {
- // NOTE: The SRTO_RCVSYN = false flag will be derived from
- // the listener socket and we are going to read, so it matches
- // the need. In case when you'd like to write to the accepted
- // socket, you'd have to set also SRTO_SNDSYN = false.
- srt_epoll_add_usock(eid, their_fd, &read_modes);
-
- // The listener socket is no longer important.
- srt_epoll_remove_usock(eid, ss);
- }
-
- // Still, use the same procedure for receiving, no matter if
- // this is a bonded or single connection.
- int i;
- for (i = 0; i < 100; i++)
- {
- printf("srt recvmsg #%d... ",i);
- char msg[2048];
- SRT_MSGCTRL mc = srt_msgctrl_default;
- mc.grpdata = grpdata;
- mc.grpdata_size = N;
-
- if (is_nonblocking)
- {
- // Block in epoll as srt_recvmsg2 will not block.
- if (!WaitForReadReady(eid, their_fd))
- goto end;
- }
-
- st = srt_recvmsg2(their_fd, msg, sizeof msg, &mc);
- if (st == SRT_ERROR)
- {
- fprintf(stderr, "srt_recvmsg: %s\n", srt_getlasterror_str());
- goto end;
- }
-
- printf("Got msg of len %d << %s (%s)\n", st, msg, isgroup ? "group" : "single");
-
- if (!isgroup)
- continue;
-
- if (!mc.grpdata)
- printf("Group status: [%zi] members > %zi, can't handle.\n", mc.grpdata_size, N);
- else
- {
- printf(" ++ Group status [%zi]: ", mc.grpdata_size);
- size_t z;
- for (z = 0; z < mc.grpdata_size; ++z)
- {
- printf( "[%zd] result=%d state=%s ", z, mc.grpdata[z].result,
- mc.grpdata[z].sockstate <= SRTS_CONNECTING ? "pending" :
- mc.grpdata[z].sockstate == SRTS_CONNECTED ? "connected" : "broken");
- }
- printf("\n");
- }
- }
-
-end:
- if (eid != -1)
- {
- srt_epoll_release(eid);
- }
- free(grpdata);
- printf("srt close\n");
- st = srt_close(their_fd); // just for a case; broken socket should be wiped out anyway
- if (st == SRT_ERROR)
- {
- fprintf(stderr, "srt_close: %s\n", srt_getlasterror_str());
- // But not matter, we're finishing here.
- }
- st = srt_close(ss);
- if (st == SRT_ERROR)
- {
- fprintf(stderr, "srt_close: %s\n", srt_getlasterror_str());
- // But not matter, we're finishing here.
- }
-
-cleanup:
- printf("srt cleanup\n");
- srt_cleanup();
- return globstatus;
-}
+++ /dev/null
-/*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2017 Haivision Systems Inc.
- *
- * 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.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; If not, see <http://www.gnu.org/licenses/>
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-
-
-#include "srt.h"
-
-int main(int argc, char** argv)
-{
- int ss, st;
- struct sockaddr_in sa;
- int yes = 1;
- struct sockaddr_storage their_addr;
-
- if (argc != 3) {
- fprintf(stderr, "Usage: %s <host> <port>\n", argv[0]);
- return 1;
- }
-
- printf("srt startup\n");
- srt_startup();
-
- printf("srt socket\n");
- ss = srt_create_socket();
- if (ss == SRT_ERROR)
- {
- fprintf(stderr, "srt_socket: %s\n", srt_getlasterror_str());
- return 1;
- }
-
- printf("srt bind address\n");
- sa.sin_family = AF_INET;
- sa.sin_port = htons(atoi(argv[2]));
- if (inet_pton(AF_INET, argv[1], &sa.sin_addr) != 1)
- {
- return 1;
- }
-
- printf("srt setsockflag\n");
- srt_setsockflag(ss, SRTO_RCVSYN, &yes, sizeof yes);
-
- printf("srt bind\n");
- st = srt_bind(ss, (struct sockaddr*)&sa, sizeof sa);
- if (st == SRT_ERROR)
- {
- fprintf(stderr, "srt_bind: %s\n", srt_getlasterror_str());
- return 1;
- }
-
- printf("srt listen\n");
- st = srt_listen(ss, 2);
- if (st == SRT_ERROR)
- {
- fprintf(stderr, "srt_listen: %s\n", srt_getlasterror_str());
- return 1;
- }
-
- printf("srt accept\n");
- int addr_size = sizeof their_addr;
- int their_fd = srt_accept(ss, (struct sockaddr *)&their_addr, &addr_size);
-
- int i;
- for (i = 0; i < 100; i++)
- {
- printf("srt recvmsg #%d... ",i);
- char msg[2048];
- st = srt_recvmsg(their_fd, msg, sizeof msg);
- if (st == SRT_ERROR)
- {
- fprintf(stderr, "srt_recvmsg: %s\n", srt_getlasterror_str());
- goto end;
- }
-
- printf("Got msg of len %d << %s\n", st, msg);
- }
-
-end:
- printf("srt close\n");
- st = srt_close(ss);
- if (st == SRT_ERROR)
- {
- fprintf(stderr, "srt_close: %s\n", srt_getlasterror_str());
- return 1;
- }
-
- printf("srt cleanup\n");
- srt_cleanup();
- return 0;
-}
+++ /dev/null
-/*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2018 Haivision Systems Inc.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "srt.h"
-
-int main( int argc, char** argv )
-{
- if (argc < 3)
- {
- fprintf(stderr, "Usage: %s <remote host> <remote port>\n", argv[0]);
- return 1;
- }
-
- int ss, st;
- struct sockaddr_in sa;
- int yes = 1;
- const char message [] = "This message should be sent to the other side";
-
- srt_startup();
-
- ss = srt_create_socket();
- if ( ss == SRT_ERROR )
- {
- fprintf(stderr, "srt_socket: %s\n", srt_getlasterror_str());
- return 1;
- }
-
- sa.sin_port = htons(atoi(argv[2]));
- if ( inet_pton(AF_INET, argv[1], &sa.sin_addr) != 1)
- {
- return 1;
- }
-
- // This is obligatory only in live mode, if you predict to connect
- // to a peer with SRT version 1.2.0 or older. Not required since
- // 1.3.0, and all older versions support only live mode.
- //srt_setsockflag(ss, SRTO_SENDER, &yes, sizeof yes);
- //
- // In order to make sure that the client supports non-live message
- // mode, let's require this.
- int minversion = SRT_VERSION_FEAT_HSv5;
- srt_setsockflag(ss, SRTO_MINVERSION, &minversion, sizeof minversion);
-
- // Require also non-live message mode.
- int file_mode = SRTT_FILE;
- srt_setsockflag(ss, SRTO_TRANSTYPE, &file_mode, sizeof file_mode);
- srt_setsockflag(ss, SRTO_MESSAGEAPI, &yes, sizeof yes);
-
- // Note that the other side will reject the connection if the
- // listener didn't set the same mode.
-
- st = srt_connect(ss, (struct sockaddr*)&sa, sizeof sa);
- if ( st == SRT_ERROR )
- {
- fprintf(stderr, "srt_connect: %s\n", srt_getlasterror_str());
- return 1;
- }
-
- st = srt_send(ss, message, sizeof message);
- if ( st == SRT_ERROR )
- {
- fprintf(stderr, "srt_sendmsg: %s\n", srt_getlasterror_str());
- return 1;
- }
-
- st = srt_close(ss);
- if ( st == SRT_ERROR )
- {
- fprintf(stderr, "srt_close: %s\n", srt_getlasterror_str());
- return 1;
- }
-
- srt_cleanup();
- return 0;
-}
+++ /dev/null
-<configuration>
- <config>
- <add key="dependencyVersion" value="Highest" />
- <add key="globalPackagesFolder" value="c:\packages" />
- <add key="repositoryPath" value="packages" />
- </config>
- <packageSources>
- <add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
- <!--<add key="Local Source" value="D:\Data\Dev\LocalNuget" />-->
- </packageSources>
-</configuration>
\ No newline at end of file
Summary: SRT (Secure Reliable Transport) library
Url: https://github.com/Haivision/srt
Group: Multimedia/Libraries
-Source0: %{name}-%{version}.tar.bz2
+Source0: %{name}-%{version}.tar.gz
Source99: baselibs.conf
Source1001: srt.manifest
BuildRequires: cmake
--with-pic
%build
-export CFLAGS="$CFLAGS -DCMAKE_BUILD_TYPE=Debug"
make
%install
+++ /dev/null
-## Scripts for building SRT for Android
-
-See [Building SRT for Android](../../docs/build/build-android.md) for the instructions.
+++ /dev/null
-#!/bin/sh
-
-echo_help()
-{
- echo "Usage: $0 [options...]"
- echo " -n NDK root path for the build"
- echo " -a Target API level"
- echo " -t Space-separated list of target architectures"
- echo " Android supports the following architectures: armeabi-v7a arm64-v8a x86 x86_64"
- echo " -e Encryption library to be used. Possible options: openssl (default) mbedtls"
- echo " -o OpenSSL version. E.g. 1.1.1l"
- echo " -m Mbed TLS version. E.g. v2.26.0"
- echo
- echo "Example: ./build-android -n /home/username/Android/Sdk/ndk/23.0.7599858 -a 28 -t \"arm64-v8a x86_64\""
- echo
-}
-
-# Init optional command line vars
-NDK_ROOT=""
-API_LEVEL=28
-BUILD_TARGETS="armeabi-v7a arm64-v8a x86 x86_64"
-OPENSSL_VERSION=1.1.1l
-ENC_LIB=openssl
-MBEDTLS_VERSION=v2.26.0
-
-while getopts n:a:t:o:s:e:m: option
-do
- case "${option}"
- in
- n) NDK_ROOT=${OPTARG};;
- a) API_LEVEL=${OPTARG};;
- t) BUILD_TARGETS=${OPTARG};;
- o) OPENSSL_VERSION=${OPTARG};;
- s) SRT_VERSION=${OPTARG};;
- e) ENC_LIB=${OPTARG};;
- m) MBEDTLS_VERSION=${OPTARG};;
- *) twentytwo=${OPTARG};;
- esac
-done
-
-echo_help
-
-if [ -z "$NDK_ROOT" ] ; then
- echo "NDK directory not set."
- exit 128
-else
- if [ ! -d "$NDK_ROOT" ]; then
- echo "NDK directory does not exist: $NDK_ROOT"
- exit 128
- fi
-fi
-
-SCRIPT_DIR=$(pwd)
-HOST_TAG='unknown'
-unamestr=$(uname -s)
-if [ "$unamestr" = 'Linux' ]; then
- HOST_TAG='linux-x86_64'
-elif [ "$unamestr" = 'Darwin' ]; then
- if [ $(uname -p) = 'arm' ]; then
- echo "NDK does not currently support ARM64"
- exit 128
- else
- HOST_TAG='darwin-x86_64'
- fi
-fi
-
-# Write files relative to current location
-BASE_DIR=$(pwd)
-case "${BASE_DIR}" in
- *\ * )
- echo "Your path contains whitespaces, which is not supported by 'make install'."
- exit 128
- ;;
-esac
-cd "${BASE_DIR}"
-
-if [ $ENC_LIB = 'openssl' ]; then
- echo "Building OpenSSL $OPENSSL_VERSION"
- $SCRIPT_DIR/mkssl -n $NDK_ROOT -a $API_LEVEL -t "$BUILD_TARGETS" -o $OPENSSL_VERSION -d $BASE_DIR -h $HOST_TAG
-elif [ $ENC_LIB = 'mbedtls' ]; then
- if [ ! -d $BASE_DIR/mbedtls ]; then
- git clone https://github.com/ARMmbed/mbedtls mbedtls
- if [ ! -z "$MBEDTLS_VERSION" ]; then
- git -C $BASE_DIR/mbedtls checkout $MBEDTLS_VERSION
- fi
- fi
-else
- echo "Unknown encryption library. Possible options: openssl mbedtls"
- exit 128
-fi
-
-# Build working copy of srt repository
-REPO_DIR="../.."
-
-for build_target in $BUILD_TARGETS; do
- LIB_DIR=$BASE_DIR/$build_target/lib
- JNI_DIR=$BASE_DIR/prebuilt/$build_target
-
- mkdir -p $JNI_DIR
-
- if [ $ENC_LIB = 'mbedtls' ]; then
- $SCRIPT_DIR/mkmbedtls -n $NDK_ROOT -a $API_LEVEL -t $build_target -s $BASE_DIR/mbedtls -i $BASE_DIR/$build_target
- cp $LIB_DIR/libmbedcrypto.so $JNI_DIR/libmbedcrypto.so
- cp $LIB_DIR/libmbedtls.so $JNI_DIR/libmbedtls.so
- cp $LIB_DIR/libmbedx509.so $JNI_DIR/libmbedx509.so
- fi
-
- git -C $REPO_DIR clean -fd -e scripts
- $SCRIPT_DIR/mksrt -n $NDK_ROOT -a $API_LEVEL -t $build_target -e $ENC_LIB -s $REPO_DIR -i $BASE_DIR/$build_target
- cp $LIB_DIR/libsrt.so $JNI_DIR/libsrt.so
-done
+++ /dev/null
-#!/bin/sh
-
-while getopts s:i:t:n:a: option
-do
- case "${option}"
- in
- s) SRC_DIR=${OPTARG};;
- i) INSTALL_DIR=${OPTARG};;
- t) ARCH_ABI=${OPTARG};;
- n) NDK_ROOT=${OPTARG};;
- a) API_LEVEL=${OPTARG};;
- *) twentytwo=${OPTARG};;
- esac
-done
-
-
-BUILD_DIR=/tmp/mbedtls_android_build
-rm -rf $BUILD_DIR
-mkdir $BUILD_DIR
-cd $BUILD_DIR
-cmake -DENABLE_TESTING=Off -DUSE_SHARED_MBEDTLS_LIBRARY=On \
--DCMAKE_PREFIX_PATH=$INSTALL_DIR -DCMAKE_INSTALL_PREFIX=$INSTALL_DIR -DCMAKE_ANDROID_NDK=$NDK_ROOT \
--DCMAKE_SYSTEM_NAME=Android -DCMAKE_SYSTEM_VERSION=$API_LEVEL -DCMAKE_ANDROID_ARCH_ABI=$ARCH_ABI \
--DCMAKE_C_FLAGS="-fPIC" -DCMAKE_SHARED_LINKER_FLAGS="-Wl,--build-id" \
--DCMAKE_BUILD_TYPE=RelWithDebInfo $SRC_DIR
-cmake --build .
-cmake --install .
+++ /dev/null
-#!/bin/sh
-
-while getopts s:i:t:n:a:e: option
-do
- case "${option}"
- in
- s) SRC_DIR=${OPTARG};;
- i) INSTALL_DIR=${OPTARG};;
- t) ARCH_ABI=${OPTARG};;
- n) NDK_ROOT=${OPTARG};;
- a) API_LEVEL=${OPTARG};;
- e) ENC_LIB=${OPTARG};;
- *) twentytwo=${OPTARG};;
- esac
-done
-
-
-cd $SRC_DIR
-./configure --use-enclib=$ENC_LIB \
---use-openssl-pc=OFF \
---OPENSSL_INCLUDE_DIR=$INSTALL_DIR/include \
---OPENSSL_CRYPTO_LIBRARY=$INSTALL_DIR/lib/libcrypto.a --OPENSSL_SSL_LIBRARY=$INSTALL_DIR/lib/libssl.a \
---STATIC_MBEDTLS=FALSE \
---MBEDTLS_INCLUDE_DIR=$INSTALL_DIR/include --MBEDTLS_INCLUDE_DIRS=$INSTALL_DIR/include \
---MBEDTLS_LIBRARIES=$INSTALL_DIR/lib/libmbedtls.so \
---CMAKE_PREFIX_PATH=$INSTALL_DIR --CMAKE_INSTALL_PREFIX=$INSTALL_DIR --CMAKE_ANDROID_NDK=$NDK_ROOT \
---CMAKE_SYSTEM_NAME=Android --CMAKE_SYSTEM_VERSION=$API_LEVEL --CMAKE_ANDROID_ARCH_ABI=$ARCH_ABI \
---CMAKE_C_FLAGS="-fPIC" --CMAKE_SHARED_LINKER_FLAGS="-Wl,--build-id" \
---enable-c++11 --enable-stdcxx-sync \
---enable-debug=2 --enable-logging=0 --enable-heavy-logging=0 --enable-apps=0
-make
-make install
+++ /dev/null
-#!/bin/sh
-
-while getopts n:o:a:t:d:h: option
-do
- case "${option}"
- in
- n) ANDROID_NDK=${OPTARG};;
- o) OPENSSL_VERSION=${OPTARG};;
- a) API_LEVEL=${OPTARG};;
- t) BUILD_TARGETS=${OPTARG};;
- d) OUT_DIR=${OPTARG};;
- h) HOST_TAG=${OPTARG};;
- *) twentytwo=${OPTARG};;
- esac
-done
-
-
-BUILD_DIR=/tmp/openssl_android_build
-
-if [ ! -d openssl-${OPENSSL_VERSION} ]
-then
- if [ ! -f openssl-${OPENSSL_VERSION}.tar.gz ]
- then
- wget https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz || exit 128
- fi
- tar xzf openssl-${OPENSSL_VERSION}.tar.gz || exit 128
-fi
-
-cd openssl-${OPENSSL_VERSION} || exit 128
-
-
-##### export ndk directory. Required by openssl-build-scripts #####
-case ${OPENSSL_VERSION} in
- 1.1.1*)
- export ANDROID_NDK_HOME=$ANDROID_NDK
- ;;
- *)
- export ANDROID_NDK_ROOT=$ANDROID_NDK
- ;;
-esac
-
-export PATH=$ANDROID_NDK/toolchains/llvm/prebuilt/$HOST_TAG/bin:$PATH
-
-##### build-function #####
-build_the_thing() {
- make clean
- ./Configure $SSL_TARGET -D__ANDROID_API__=$API_LEVEL && \
- make SHLIB_EXT=.so && \
- make install SHLIB_EXT=.so DESTDIR=$DESTDIR || exit 128
-}
-
-##### set variables according to build-tagret #####
-for build_target in $BUILD_TARGETS
-do
- case $build_target in
- armeabi-v7a)
- DESTDIR="$BUILD_DIR/armeabi-v7a"
- SSL_TARGET="android-arm"
- ;;
- x86)
- DESTDIR="$BUILD_DIR/x86"
- SSL_TARGET="android-x86"
- ;;
- x86_64)
- DESTDIR="$BUILD_DIR/x86_64"
- SSL_TARGET="android-x86_64"
- ;;
- arm64-v8a)
- DESTDIR="$BUILD_DIR/arm64-v8a"
- SSL_TARGET="android-arm64"
- ;;
- esac
-
- rm -rf $DESTDIR
- build_the_thing
-#### copy libraries and includes to output-directory #####
- mkdir -p $OUT_DIR/$build_target/include
- cp -R $DESTDIR/usr/local/include/* $OUT_DIR/$build_target/include
- cp -R $DESTDIR/usr/local/ssl/* $OUT_DIR/$build_target/
- mkdir -p $OUT_DIR/$build_target/lib
- cp -R $DESTDIR/usr/local/lib/*.so $OUT_DIR/$build_target/lib
- cp -R $DESTDIR/usr/local/lib/*.a $OUT_DIR/$build_target/lib
-done
-
-echo Success
+++ /dev/null
-@ECHO OFF
-%SYSTEMROOT%\System32\WindowsPowerShell\v1.0\PowerShell.exe -Command "& '%~dpn0.ps1'"
-pause
+++ /dev/null
-################################################################################
-# Windows SRT Build Script
-#============================
-# Usable on a Windows PC with Powershell and Visual studio,
-# or called by CI systems like AppVeyor
-#
-# By default produces a VS2019 64-bit Release binary using C++11 threads, without
-# encryption or unit tests enabled, but including test apps.
-# Before enabling any encryption options, install OpenSSL or set VCKPG flag to build
-################################################################################
-
-param (
- [Parameter()][String]$VS_VERSION = "2019",
- [Parameter()][String]$CONFIGURATION = "Release",
- [Parameter()][String]$DEVENV_PLATFORM = "x64",
- [Parameter()][String]$ENABLE_ENCRYPTION = "OFF",
- [Parameter()][String]$STATIC_LINK_SSL = "OFF",
- [Parameter()][String]$CXX11 = "ON",
- [Parameter()][String]$BUILD_APPS = "ON",
- [Parameter()][String]$UNIT_TESTS = "OFF",
- [Parameter()][String]$BUILD_DIR = "_build",
- [Parameter()][String]$VCPKG_OPENSSL = "OFF"
-)
-
-# cmake can be optionally installed (useful when running interactively on a developer station).
-# The URL for automatic download is defined later in the script, but it should be possible to just vary the
-# specific version set below and the URL should be stable enough to still work - you have been warned.
-$cmakeVersion = "3.17.3"
-
-# make all errors trigger a script stop, rather than just carry on
-$ErrorActionPreference = "Stop"
-
-$projectRoot = Join-Path $PSScriptRoot "/.." -Resolve
-
-# if running within AppVeyor, use environment variables to set params instead of passed-in values
-if ( $Env:APPVEYOR ) {
- if ( $Env:PLATFORM -eq 'x86' ) { $DEVENV_PLATFORM = 'Win32' } else { $DEVENV_PLATFORM = 'x64' }
- if ( $Env:APPVEYOR_BUILD_WORKER_IMAGE -eq 'Visual Studio 2019' ) { $VS_VERSION='2019' }
- if ( $Env:APPVEYOR_BUILD_WORKER_IMAGE -eq 'Visual Studio 2015' ) { $VS_VERSION='2015' }
- if ( $Env:APPVEYOR_BUILD_WORKER_IMAGE -eq 'Visual Studio 2013' ) { $VS_VERSION='2013' }
-
- #if not statically linking OpenSSL, set flag to gather the specific openssl package from the build server into package
- if ( $STATIC_LINK_SSL -eq 'OFF' ) { $Env:GATHER_SSL_INTO_PACKAGE = $true }
-
- #if unit tests are on, set flag to actually execute ctest step
- if ( $UNIT_TESTS -eq 'ON' ) { $Env:RUN_UNIT_TESTS = $true }
-
- $CONFIGURATION = $Env:CONFIGURATION
-
- #appveyor has many openssl installations - place the latest one in the default location unless VS2013
- if( $VS_VERSION -ne '2013' ) {
- Remove-Item -Path "C:\OpenSSL-Win32" -Recurse -Force -ErrorAction SilentlyContinue | Out-Null
- Remove-Item -Path "C:\OpenSSL-Win64" -Recurse -Force -ErrorAction SilentlyContinue | Out-Null
- Copy-Item -Path "C:\OpenSSL-v111-Win32" "C:\OpenSSL-Win32" -Recurse | Out-Null
- Copy-Item -Path "C:\OpenSSL-v111-Win64" "C:\OpenSSL-Win64" -Recurse | Out-Null
- }
-}
-
-# persist VS_VERSION so it can be used in an artifact name later
-$Env:VS_VERSION = $VS_VERSION
-
-# select the appropriate cmake generator string given the environment
-if ( $VS_VERSION -eq '2019' ) { $CMAKE_GENERATOR = 'Visual Studio 16 2019'; $MSBUILDVER = "16.0"; }
-if ( $VS_VERSION -eq '2015' -and $DEVENV_PLATFORM -eq 'Win32' ) { $CMAKE_GENERATOR = 'Visual Studio 14 2015'; $MSBUILDVER = "14.0"; }
-if ( $VS_VERSION -eq '2015' -and $DEVENV_PLATFORM -eq 'x64' ) { $CMAKE_GENERATOR = 'Visual Studio 14 2015 Win64'; $MSBUILDVER = "14.0"; }
-if ( $VS_VERSION -eq '2013' -and $DEVENV_PLATFORM -eq 'Win32' ) { $CMAKE_GENERATOR = 'Visual Studio 12 2013'; $MSBUILDVER = "12.0"; }
-if ( $VS_VERSION -eq '2013' -and $DEVENV_PLATFORM -eq 'x64' ) { $CMAKE_GENERATOR = 'Visual Studio 12 2013 Win64'; $MSBUILDVER = "12.0"; }
-
-# clear any previous build and create & enter the build directory
-$buildDir = Join-Path "$projectRoot" "$BUILD_DIR"
-Write-Output "Creating (or cleaning if already existing) the folder $buildDir for project files and outputs"
-Remove-Item -Path $buildDir -Recurse -Force -ErrorAction SilentlyContinue | Out-Null
-New-Item -ItemType Directory -Path $buildDir -ErrorAction SilentlyContinue | Out-Null
-Push-Location $buildDir
-
-# check cmake is installed
-if ( $null -eq (Get-Command "cmake.exe" -ErrorAction SilentlyContinue) ) {
- $installCmake = Read-Host "Unable to find cmake in your PATH - would you like to download and install automatically? [yes/no]"
-
- if ( $installCmake -eq "y" -or $installCmake -eq "yes" ) {
- # download cmake and run MSI for user
- $client = New-Object System.Net.WebClient
- $tempDownloadFile = New-TemporaryFile
-
- $cmakeUrl = "https://github.com/Kitware/CMake/releases/download/v$cmakeVersion/cmake-$cmakeVersion-win64-x64.msi"
- $cmakeMsiFile = "$tempDownloadFile.cmake-$cmakeVersion-win64-x64.msi"
- Write-Output "Downloading cmake from $cmakeUrl (temporary file location $cmakeMsiFile)"
- Write-Output "Note: select the option to add cmake to path for this script to operate"
- $client.DownloadFile("$cmakeUrl", "$cmakeMsiFile")
- Start-Process $cmakeMsiFile -Wait
- Remove-Item $cmakeMsiFile
- Write-Output "Cmake should have installed, this script will now exit because of path updates - please now re-run this script"
- throw
- }
- else{
- Write-Output "Quitting because cmake is required"
- throw
- }
-}
-
-# get pthreads from nuget if CXX11 is not enabled
-if ( $CXX11 -eq "OFF" ) {
- # get pthreads (this is legacy, and is only availble in nuget for VS2015 and VS2013)
- if ( $VS_VERSION -gt 2015 ) {
- Write-Output "Pthreads is not recommended for use beyond VS2015 and is not supported by this build script - aborting build"
- throw
- }
- if ( $DEVENV_PLATFORM -eq 'Win32' ) {
- nuget install cinegy.pthreads-win32-$VS_VERSION -version 2.9.1.24 -OutputDirectory ../_packages
- }
- else {
- nuget install cinegy.pthreads-win64-$VS_VERSION -version 2.9.1.24 -OutputDirectory ../_packages
- }
-}
-
-# check to see if static SSL linking was requested, and enable encryption if not already ON
-if ( $STATIC_LINK_SSL -eq "ON" ) {
- if ( $ENABLE_ENCRYPTION -eq "OFF" ) {
- # requesting a static link implicitly requires encryption support
- Write-Output "Static linking to OpenSSL requested, will force encryption feature ON"
- $ENABLE_ENCRYPTION = "ON"
- }
-}
-
-# check to see if VCPKG is marked to provide OpenSSL, and enable encryption if not already ON
-if ( $VCPKG_OPENSSL -eq "ON" ) {
- if ( $ENABLE_ENCRYPTION -eq "OFF" ) {
- # requesting VCPKG to provide OpenSSL requires encryption support
- Write-Output "VCPKG compilation of OpenSSL requested, will force encryption feature ON"
- $ENABLE_ENCRYPTION = "ON"
- }
-}
-
-# build the cmake command flags from arguments
-$cmakeFlags = "-DCMAKE_BUILD_TYPE=$CONFIGURATION " +
- "-DENABLE_STDCXX_SYNC=$CXX11 " +
- "-DENABLE_APPS=$BUILD_APPS " +
- "-DENABLE_ENCRYPTION=$ENABLE_ENCRYPTION " +
- "-DENABLE_UNITTESTS=$UNIT_TESTS"
-
-# if VCPKG is flagged to provide OpenSSL, checkout VCPKG and install package
-if ( $VCPKG_OPENSSL -eq 'ON' ) {
- Push-Location $projectRoot
- Write-Output "Cloning VCPKG into: $(Get-Location)"
- if (Test-Path -Path ".\vcpkg") {
- Set-Location .\vcpkg
- git pull
- } else {
- git clone https://github.com/microsoft/vcpkg
- Set-Location .\vcpkg
- }
-
- .\bootstrap-vcpkg.bat
-
- if($DEVENV_PLATFORM -EQ "x64"){
- if($STATIC_LINK_SSL -EQ "ON"){
- .\vcpkg install openssl:x64-windows-static
- $cmakeFlags += " -DVCPKG_TARGET_TRIPLET=x64-windows-static"
- }
- else{
- .\vcpkg install openssl:x64-windows
- }
- }
- else{
- if($STATIC_LINK_SSL -EQ "ON"){
- .\vcpkg install openssl:x86-windows-static
- $cmakeFlags += " -DVCPKG_TARGET_TRIPLET=x86-windows-static"
- }
- else{
- .\vcpkg install openssl:x86-windows
- }
- }
-
- .\vcpkg integrate install
- Pop-Location
- $cmakeFlags += " -DCMAKE_TOOLCHAIN_FILE=$projectRoot\vcpkg\scripts\buildsystems\vcpkg.cmake"
-}
-else {
- $cmakeFlags += " -DOPENSSL_USE_STATIC_LIBS=$STATIC_LINK_SSL "
-}
-
-# cmake uses a flag for architecture from vs2019, so add that as a suffix
-if ( $VS_VERSION -eq '2019' ) {
- $cmakeFlags += " -A `"$DEVENV_PLATFORM`""
-}
-
-# fire cmake to build project files
-$execVar = "cmake ../ -G`"$CMAKE_GENERATOR`" $cmakeFlags"
-Write-Output $execVar
-
-# Reset reaction to Continue for cmake as it sometimes tends to print
-# things on stderr, which is understood by PowerShell as error. The
-# exit code from cmake will be checked anyway.
-$ErrorActionPreference = "Continue"
-Invoke-Expression "& $execVar"
-
-# check build ran OK, exit if cmake failed
-if( $LASTEXITCODE -ne 0 ) {
- Write-Output "Non-zero exit code from cmake: $LASTEXITCODE"
- throw
-}
-
-$ErrorActionPreference = "Stop"
-
-# run the set-version-metadata script to inject build numbers into appveyors console and the resulting DLL
-. $PSScriptRoot/set-version-metadata.ps1
-
-# look for msbuild
-$msBuildPath = Get-Command "msbuild.exe" -ErrorAction SilentlyContinue
-if ( $null -eq $msBuildPath ) {
- # no mbsuild in the path, so try to locate with 'vswhere'
- $vsWherePath = Get-Command "vswhere.exe" -ErrorAction SilentlyContinue
- if ( $null -eq $vsWherePath ) {
- # no vswhere in the path, so check the Microsoft published location (true since VS2017 Update 2)
- $vsWherePath = Get-Command "${Env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -ErrorAction SilentlyContinue
- if ( $null -eq $vsWherePath ) {
- Write-Output "Cannot find vswhere (used to locate msbuild). Please install VS2017 update 2 (or later) or add vswhere to your path and try again"
- throw
- }
- }
- $msBuildPath = & $vsWherePath -products * -version $MSBUILDVER -requires Microsoft.Component.MSBuild -find MSBuild\**\Bin\MSBuild.exe | select-object -first 1
- if ( $null -eq $msBuildPath ) {
- Write-Output "vswhere.exe cannot find msbuild for the specified Visual Studio version - please check the installation"
- throw
- }
-}
-
-& $msBuildPath SRT.sln -m /p:Configuration=$CONFIGURATION /p:Platform=$DEVENV_PLATFORM
-
-# return to the directory previously occupied before running the script
-Pop-Location
-
-# if msbuild returned non-zero, throw to cause failure in CI
-if( $LASTEXITCODE -ne 0 ) {
- throw
-}
+++ /dev/null
-# code copied from https://crascit.com/2015/07/25/cmake-gtest/
-cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR)
-
-project(googletest-download NONE)
-
-include(ExternalProject)
-
-ExternalProject_Add(
- googletest
- SOURCE_DIR "@GOOGLETEST_DOWNLOAD_ROOT@/googletest-src"
- BINARY_DIR "@GOOGLETEST_DOWNLOAD_ROOT@/googletest-build"
- GIT_REPOSITORY
- https://github.com/google/googletest.git
- GIT_TAG release-1.8.1
- CONFIGURE_COMMAND ""
- BUILD_COMMAND ""
- INSTALL_COMMAND ""
- TEST_COMMAND ""
-)
+++ /dev/null
-# the following code to fetch googletest
-# is inspired by and adapted after https://crascit.com/2015/07/25/cmake-gtest/
-# download and unpack googletest at configure time
-
-macro(fetch_googletest _download_module_path _download_root)
- set(GOOGLETEST_DOWNLOAD_ROOT ${_download_root})
- configure_file(
- ${_download_module_path}/googletest-download.cmake
- ${_download_root}/CMakeLists.txt
- @ONLY
- )
- unset(GOOGLETEST_DOWNLOAD_ROOT)
-
- execute_process(
- COMMAND
- "${CMAKE_COMMAND}" -G "${CMAKE_GENERATOR}" .
- WORKING_DIRECTORY
- ${_download_root}
- )
- execute_process(
- COMMAND
- "${CMAKE_COMMAND}" --build .
- WORKING_DIRECTORY
- ${_download_root}
- )
-
- # adds the targers: gtest, gtest_main, gmock, gmock_main
- add_subdirectory(
- ${_download_root}/googletest-src
- ${_download_root}/googletest-build
- )
-endmacro()
+++ /dev/null
-# This file is based off of the Platform/Darwin.cmake and Platform/UnixPaths.cmake
-# files which are included with CMake 2.8.4
-# It has been altered for iOS development
-
-# Options:
-#
-# IOS_PLATFORM = OS (default) or SIMULATOR or SIMULATOR64
-# This decides if SDKS will be selected from the iPhoneOS.platform or iPhoneSimulator.platform folders
-# OS - the default, used to build for iPhone and iPad physical devices, which have an arm arch.
-# SIMULATOR - used to build for the Simulator platforms, which have an x86 arch.
-#
-# IOS_ARCH = arm64 (default for OS), armv7, armv7s, i386 (default for SIMULATOR), x86_64 (default for SIMULATOR64)
-#
-# CMAKE_IOS_DEVELOPER_ROOT = automatic(default) or /path/to/platform/Developer folder
-# By default this location is automatcially chosen based on the IOS_PLATFORM value above.
-# If set manually, it will override the default location and force the user of a particular Developer Platform
-#
-# CMAKE_IOS_SDK_ROOT = automatic(default) or /path/to/platform/Developer/SDKs/SDK folder
-# By default this location is automatcially chosen based on the CMAKE_IOS_DEVELOPER_ROOT value.
-# In this case it will always be the most up-to-date SDK found in the CMAKE_IOS_DEVELOPER_ROOT path.
-# If set manually, this will force the use of a specific SDK version
-#
-# IOS_DISABLE_BITCODE - set to 1 if you want to disable bitcode generation
-
-# Standard settings
-set (CMAKE_SYSTEM_NAME Darwin)
-set (CMAKE_SYSTEM_VERSION 1)
-set (UNIX True)
-set (APPLE True)
-set (IOS True)
-
-# Required as of cmake 2.8.10
-set (CMAKE_OSX_DEPLOYMENT_TARGET "" CACHE STRING "Force unset of the deployment target for iOS" FORCE)
-
-# Determine the cmake host system version so we know where to find the iOS SDKs
-find_program (CMAKE_UNAME uname /bin /usr/bin /usr/local/bin)
-if (CMAKE_UNAME)
- exec_program(uname ARGS -r OUTPUT_VARIABLE CMAKE_HOST_SYSTEM_VERSION)
- string (REGEX REPLACE "^([0-9]+)\\.([0-9]+).*$" "\\1" DARWIN_MAJOR_VERSION "${CMAKE_HOST_SYSTEM_VERSION}")
-endif (CMAKE_UNAME)
-
-
-set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
-set(CMAKE_AR ar CACHE FILEPATH "" FORCE)
-
-set (CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG "-compatibility_version ")
-set (CMAKE_C_OSX_CURRENT_VERSION_FLAG "-current_version ")
-set (CMAKE_CXX_OSX_COMPATIBILITY_VERSION_FLAG "${CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG}")
-set (CMAKE_CXX_OSX_CURRENT_VERSION_FLAG "${CMAKE_C_OSX_CURRENT_VERSION_FLAG}")
-
-if (NOT DEFINED IOS_DISABLE_BITCODE)
- set (EMBED_OPTIONS "-fembed-bitcode")
-endif(NOT DEFINED IOS_DISABLE_BITCODE)
-
-if (CMAKE_BUILD_TYPE STREQUAL "Debug" OR ENABLE_DEBUG)
- set(IOS_DEBUG_OPTIONS "-glldb -gmodules")
-else()
- set(IOS_DEBUG_OPTIONS "-fvisibility=hidden -fvisibility-inlines-hidden")
-endif()
-
-set (CMAKE_C_FLAGS_INIT "${IOS_DEBUG_OPTIONS} ${EMBED_OPTIONS}")
-set (CMAKE_CXX_FLAGS_INIT "${IOS_DEBUG_OPTIONS} ${EMBED_OPTIONS}")
-
-set (CMAKE_C_LINK_FLAGS "-Wl,-search_paths_first ${EMBED_OPTIONS} ${CMAKE_C_LINK_FLAGS}")
-set (CMAKE_CXX_LINK_FLAGS "-Wl,-search_paths_first ${EMBED_OPTIONS} ${CMAKE_CXX_LINK_FLAGS}")
-
-
-set (CMAKE_PLATFORM_HAS_INSTALLNAME 1)
-set (CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-dynamiclib")
-set (CMAKE_SHARED_MODULE_CREATE_C_FLAGS "-bundle")
-set (CMAKE_SHARED_MODULE_LOADER_C_FLAG "-Wl,-bundle_loader,")
-set (CMAKE_SHARED_MODULE_LOADER_CXX_FLAG "-Wl,-bundle_loader,")
-set (CMAKE_FIND_LIBRARY_SUFFIXES ".dylib" ".so" ".a")
-
-# Specify install_name_tool and pkg-config since it outside of SDK path and therefore can't be found by CMake
-if (NOT DEFINED CMAKE_INSTALL_NAME_TOOL)
- find_program(CMAKE_INSTALL_NAME_TOOL install_name_tool)
-endif (NOT DEFINED CMAKE_INSTALL_NAME_TOOL)
-
-if (NOT DEFINED PKG_CONFIG_EXECUTABLE)
- find_program(PKG_CONFIG_EXECUTABLE NAMES pkg-config)
- if (DEFINED PKG_CONFIG_EXECUTABLE)
- execute_process(COMMAND pkg-config --version OUTPUT_VARIABLE PKG_CONFIG_VERSION_STRING)
- endif(DEFINED PKG_CONFIG_EXECUTABLE)
-endif(NOT DEFINED PKG_CONFIG_EXECUTABLE)
-
-
-# fffio Specify path to install shared library on device
-set (CMAKE_INSTALL_NAME_DIR "@executable_path/Frameworks")
-set (CMAKE_BUILD_WITH_INSTALL_NAME_DIR TRUE)
-
-# Setup iOS platform unless specified manually with IOS_PLATFORM
-if (NOT DEFINED IOS_PLATFORM)
- set (IOS_PLATFORM "OS")
-endif (NOT DEFINED IOS_PLATFORM)
-set (IOS_PLATFORM ${IOS_PLATFORM} CACHE STRING "Type of iOS Platform")
-
-# Check the platform selection and setup for developer root
-if (${IOS_PLATFORM} STREQUAL OS)
- set (IOS_PLATFORM_LOCATION "iPhoneOS.platform")
-
- # This causes the installers to properly locate the output libraries
- set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-iphoneos")
-elseif (${IOS_PLATFORM} STREQUAL SIMULATOR)
- set (SIMULATOR true)
- set (IOS_PLATFORM_LOCATION "iPhoneSimulator.platform")
-
- # This causes the installers to properly locate the output libraries
- set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-iphonesimulator")
-elseif (${IOS_PLATFORM} STREQUAL SIMULATOR64)
- set (SIMULATOR true)
- set (IOS_PLATFORM_LOCATION "iPhoneSimulator.platform")
-
- # This causes the installers to properly locate the output libraries
- set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-iphonesimulator")
-else (${IOS_PLATFORM} STREQUAL OS)
- message (FATAL_ERROR "Unsupported IOS_PLATFORM value selected. Please choose OS or SIMULATOR")
-endif (${IOS_PLATFORM} STREQUAL OS)
-
-# Setup iOS developer location unless specified manually with CMAKE_IOS_DEVELOPER_ROOT
-if (NOT DEFINED CMAKE_IOS_DEVELOPER_ROOT)
- exec_program(/usr/bin/xcode-select ARGS -print-path OUTPUT_VARIABLE CMAKE_XCODE_DEVELOPER_DIR)
- set (CMAKE_IOS_DEVELOPER_ROOT "${CMAKE_XCODE_DEVELOPER_DIR}/Platforms/${IOS_PLATFORM_LOCATION}/Developer")
-endif (NOT DEFINED CMAKE_IOS_DEVELOPER_ROOT)
-set (CMAKE_IOS_DEVELOPER_ROOT ${CMAKE_IOS_DEVELOPER_ROOT} CACHE PATH "Location of iOS Platform")
-
-# Find and use the most recent iOS sdk unless specified manually with CMAKE_IOS_SDK_ROOT
-if (NOT DEFINED CMAKE_IOS_SDK_ROOT)
- file (GLOB _CMAKE_IOS_SDKS "${CMAKE_IOS_DEVELOPER_ROOT}/SDKs/*")
- if (_CMAKE_IOS_SDKS)
- list (SORT _CMAKE_IOS_SDKS)
- list (REVERSE _CMAKE_IOS_SDKS)
- list (GET _CMAKE_IOS_SDKS 0 CMAKE_IOS_SDK_ROOT)
- else (_CMAKE_IOS_SDKS)
- message (FATAL_ERROR "No iOS SDK's found in default search path ${CMAKE_IOS_DEVELOPER_ROOT}. Manually set CMAKE_IOS_SDK_ROOT or install the iOS SDK.")
- endif (_CMAKE_IOS_SDKS)
- message (STATUS "Toolchain using default iOS SDK: ${CMAKE_IOS_SDK_ROOT}")
-endif (NOT DEFINED CMAKE_IOS_SDK_ROOT)
-set (CMAKE_IOS_SDK_ROOT ${CMAKE_IOS_SDK_ROOT} CACHE PATH "Location of the selected iOS SDK")
-
-# Set the sysroot default to the most recent SDK
-set (CMAKE_OSX_SYSROOT ${CMAKE_IOS_SDK_ROOT} CACHE PATH "Sysroot used for iOS support")
-
-# set the architecture for iOS
-if (NOT DEFINED IOS_ARCH)
- if (${IOS_PLATFORM} STREQUAL OS)
- set (IOS_ARCH arm64)
- elseif (${IOS_PLATFORM} STREQUAL SIMULATOR)
- set (IOS_ARCH i386)
- elseif (${IOS_PLATFORM} STREQUAL SIMULATOR64)
- set (IOS_ARCH x86_64)
- endif (${IOS_PLATFORM} STREQUAL OS)
-endif(NOT DEFINED IOS_ARCH)
-set (CMAKE_OSX_ARCHITECTURES ${IOS_ARCH} CACHE STRING "Build architecture for iOS")
-
-# Set the find root to the iOS developer roots and to user defined paths
-set (CMAKE_FIND_ROOT_PATH ${CMAKE_IOS_DEVELOPER_ROOT} ${CMAKE_IOS_SDK_ROOT} ${CMAKE_PREFIX_PATH} CACHE STRING "iOS find search path root")
-
-# default to searching for frameworks first
-set (CMAKE_FIND_FRAMEWORK FIRST)
-
-# set up the default search directories for frameworks
-set (CMAKE_SYSTEM_FRAMEWORK_PATH
- ${CMAKE_IOS_SDK_ROOT}/System/Library/Frameworks
- ${CMAKE_IOS_SDK_ROOT}/System/Library/PrivateFrameworks
- ${CMAKE_IOS_SDK_ROOT}/Developer/Library/Frameworks
-)
-
-# only search the iOS sdks, not the remainder of the host filesystem
-set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY)
-set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
-set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
-
+++ /dev/null
-#!/bin/bash
-
-#
-# SRT - Secure, Reliable, Transport
-# Copyright (c) 2018 Haivision Systems Inc.
-#
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-#
-
-FFPLAY=`type -p ffplay || echo none`
-if [[ $FFPLAY == "none" ]]; then
- echo >&2 "ERROR: ffplay not available to call. Please install ffplay first."
- exit 1
-fi
-
-DIRNAME=`dirname $0`
-if [[ ! -x $DIRNAME/srt-live-transmit ]]; then
- echo >&2 "ERROR: you need 'srt-live-transmit' tool from SRT package in the same directory as this script."
- exit 1
-fi
-
-SRCLOC=$1
-if [[ -z $SRCLOC ]]; then
- echo >&2 "Usage: `basename $0` <source URL>"
- exit 1
-fi
-
-$DIRNAME/srt-live-transmit "$1" file://con/ | ffplay -
-
+++ /dev/null
-#!/usr/bin/tclsh
-
-set read_running 0
-set write_running 0
-set read_eof 0
-set theend 0
-
-set nread 0
-set nwritten 0
-
-proc ReadBack {fd} {
-
- if { !$::write_running } {
- puts stderr "ERROR: connection closed unexpectedly!"
- set ::theend 1
- return
- }
-
- set r [read $fd 4096]
- if {$r == ""} {
-
- if {[eof $fd]} {
- puts stderr "EOF on socket"
- set ::read_running 0
- return
- }
-
- # --- puts stderr "SPURIOUS, not reading"
- return
- }
-
- # --- puts stderr "REPRINTING [string bytelength $r] bytes"
- puts -nonewline stdout $r
- incr ::nwritten [string bytelength $r]
- # --- puts stderr "DONE"
-
- set remain [expr {$::nread - $::nwritten}]
- if { $::read_eof } {
- puts stderr "Finishing... read=$::nread written=$::nwritten diff=[expr {$::nwritten - $::nread}] - [expr {100.0*$remain/$::nread}]%"
- }
-
- # Nothing more to read
- if {$remain == 0} {
- puts stderr "NOTHING MORE TO BE WRITTEN - exiting"
- set ::theend 1
- return
- }
-
- after idle "ReadBack $fd"
-}
-
-proc SendToSocket {fd} {
- global theend
-
- if { !$::write_running } {
- # --- puts stderr "SERVER DOWN, not reading"
- fileevent stdin readable {}
- return
- }
-
- if { $::read_eof } {
- # Don't read, already EOF.
-
- }
- # --- puts stderr "READING cin"
- set r [read stdin 4096]
- if {$r == ""} {
- if {[eof stdin]} {
- if {!$::read_eof} {
- puts stderr "EOF, setting server off"
- set ::read_eof 1
- }
- # Just enough when the next SendToSocket will
- # not be scheduled.
- return
- }
- # --- puts stderr "SPURIOUS, not reading"
- return
- }
-
- # --- puts stderr "SENDING [string bytelength $r] bytes"
- # Set blocking for a short moment of sending
- # in order to prevent losing data that must wait
- fconfigure $fd -blocking yes
- puts -nonewline $fd $r
- incr ::nread [string bytelength $r]
- fconfigure $fd -blocking no
-
- # --- if {[fblocked stdin]} {
- # --- # Nothing more to read
- # --- return
- # --- }
- after idle "SendToSocket $fd"
-}
-
-set fd [socket {*}$argv]
-fconfigure $fd -encoding binary -translation binary -blocking no -buffering none
-fileevent $fd readable "ReadBack $fd"
-
-fconfigure stdin -encoding binary -translation binary -blocking no
-fconfigure stdout -encoding binary -translation binary
-fileevent stdin readable "SendToSocket $fd"
-
-# --- puts stderr "READY, sending"
-set read_running 1
-set write_running 1
-
-vwait theend
-
-close $fd
+++ /dev/null
-#!/usr/bin/tclsh
-
-proc SpawnEchoServer {fd host port} {
- fconfigure $fd -encoding binary -translation binary -blocking no -buffering none
- fileevent $fd readable "EchoBack $fd"
- # --- puts stderr "Connected: [fconfigure $fd -peername]"
-}
-
-proc EchoBack {fd} {
-
- # --- puts stderr "READ-READY"
-
- while 1 {
-
- # --- puts stderr "READING 4096"
- set r [read $fd 4096]
- if {$r == ""} {
- if {[eof $fd]} {
- # --- puts stderr "EOF. Closing"
- close $fd
- return
- }
-
- # --- puts stderr "SPURIOUS, giving up read"
- return
- }
-
- # Set blocking for a short moment of sending
- # in order to prevent losing data that must wait
-
- # --- puts stderr "SENDING [string bytelength $r] bytes"
- fconfigure $fd -blocking yes
- puts -nonewline $fd $r
- fconfigure $fd -blocking no
-
- if {[fblocked $fd]} {
- # --- puts stderr "NO MORE DATA"
- # Nothing more to read
- return
- }
-
- # --- puts stderr "AGAIN"
-
- }
-
-}
-
-socket -server SpawnEchoServer $argv
-puts stderr "SERVER READY"
-
-vwait tk
+++ /dev/null
-tmp
-installers
-*.exe
-*~
-~*
-.#*
-*.bak
-*.autosave
-.DS_Store
-._*
+++ /dev/null
-# SRT Static Libraries Installer for Windows
-
-This directory contains scripts to build a binary installer for
-libsrt on Windows systems for Visual Studio applications using SRT.
-
-## SRT developer: Building the libsrt installer
-
-### Prerequisites
-
-These first two steps need to be executed once only.
-
-- Prerequisite 1: Install OpenSSL for Windows, both 64 and 32 bits.
- This can be done automatically by running the PowerShell script `install-openssl.ps1`.
-
-- Prerequisite 2: Install NSIS, the NullSoft Installation Scripting system.
- This can be done automatically by running the PowerShell script `install-nsis.ps1`.
-
-### Building the libsrt installer
-
-To build the libsrt installer, simply run the PowerShell script `build-win-installer.ps1`.
-Running it without parameters, for instance launching it from the Windows Explorer, is
-sufficient to build the installer.
-
-Optional parameters:
-
-- `-Version name` :
- Use the specified string as version number for libsrt. By default, if the
- current commit has a tag, use that tag (initial "v" removed, for instance
- `1.4.3`). Otherwise, the defaut version is a detailed version number (most
- recent version, number of commits since then, short commit SHA, for instance
- `1.4.3-32-g22cc924`). Use that option if necessary to specify some other
- non-standard form of version string.
-
-- `-NoPause` :
- Do not wait for the user to press `<enter>` at end of execution. By default,
- execute a `pause` instruction at the end of execution, which is useful
- when the script was launched from Windows Explorer. Use that option when the
- script is invoked from another PowerShell script.
-
-The installer is then available in the directory `installers`.
-
-The name of the installer is `libsrt-VERS.exe` where `VERS` is the SRT version number
-(see the `-Version` option).
-
-The installer shall then be published as a release asset in the `srt` repository
-on GitHub, either as `libsrt-VERS.exe` or `libsrt-VERS-win-installer.zip`.
-In the latter case, the archive shall contain `libsrt-VERS.exe`.
-
-## SRT user: Using the libsrt installer
-
-### Installing the SRT libraries
-
-To install the SRT libraries, simply run the `libsrt-VERS.exe` installer which is
-available in the [SRT release area](https://github.com/Haivision/srt/releases).
-
-After installing the libsrt binaries, an environment variable named `LIBSRT` is
-defined with the installation root (typically `C:\Program Files (x86)\libsrt`).
-
-If there is a need for automation, in a CI/CD pipeline for instance, the download
-of the latest `libsrt-VERS.exe` and its installation can be automated using the
-sample PowerShell script `install-libsrt.ps1` which is available in this directory.
-This script may be freely copied in the user's build environment.
-
-When run without parameters (for instance from the Windows explorer), this
-script downloads and installs the latest version of libsrt.
-
-Optional parameters:
-
-- `-Destination path` :
- Specify a local directory where the libsrt package will be downloaded.
- By default, use the `tmp` subdirectory from this script's directory.
-
-- `-ForceDownload` :
- Force a download even if the package is already downloaded in the
- destination path. Note that the latest version is always checked.
- If a older package is already present but a newer one is available
- online, the newer one is always downloaded, even without this option.
-
-- `-GitHubActions` :
- When used in a GitHub Actions workflow, make sure that the `LIBSRT`
- environment variable is propagated to subsequent jobs. In your GitHub
- workflow, in the initial setup phase, use
- `script-dir\install-libsrt.ps1 -GitHubActions -NoPause`.
-
-- `-NoInstall` :
- Do not install the package, only download it. By default, libsrt is installed.
-
-- `-NoPause` :
- Do not wait for the user to press `<enter>` at end of execution. By default,
- execute a `pause` instruction at the end of execution, which is useful
- when the script was launched from Windows Explorer. Use that option when the
- script is invoked from another PowerShell script.
-
-### Building Windows applications with libsrt
-
-In the SRT installation root directory (specified in environment variable `LIBSRT`),
-there is a Visual Studio property file named `libsrt.props`. Simply reference this
-property file in your Visual Studio project to use libsrt.
-
-You can also do that manually by editing the application project file (the XML
-file named with a `.vcxproj` extension). Add the following line just before
-the end of the file:
-
-~~~
-<Import Project="$(LIBSRT)\libsrt.props"/>
-~~~
-
-With this setup, just compile your application normally, either using the
-Visual Studio IDE or the MSBuild command line tool.
-
-## Files reference
-
-This directory contains the following files:
-
-| File name | Usage
-| ----------------------- | -----
-| build-win-installer.ps1 | PowerShell script to build the libsrt installer.
-| install-libsrt.ps1 | Sample PowerShell script to automatically install libsrt (for user's projects).
-| install-openssl.ps1 | PowerShell script to install OpenSSL (prerequisite to build the installer).
-| install-nsis.ps1 | PowerShell script to install NSIS (prerequisite to build the installer).
-| libsrt.nsi | NSIS installation script (used to build the installer).
-| libsrt.props | Visual Studio property files to use libsrt (embedded in the installer).
-| README.md | This text file.
+++ /dev/null
-#-----------------------------------------------------------------------------
-#
-# SRT - Secure, Reliable, Transport
-# Copyright (c) 2021, Thierry Lelegard
-#
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-#
-#-----------------------------------------------------------------------------
-
-<#
- .SYNOPSIS
-
- Build the SRT static libraries installer for Windows.
-
- .PARAMETER Version
-
- Use the specified string as version number from libsrt. By default, if
- the current commit has a tag, use that tag (initial 'v' removed). Otherwise,
- the defaut version is a detailed version number (most recent version, number
- of commits since then, short commit SHA).
-
- .PARAMETER NoPause
-
- Do not wait for the user to press <enter> at end of execution. By default,
- execute a "pause" instruction at the end of execution, which is useful
- when the script was run from Windows Explorer.
-
-#>
-[CmdletBinding()]
-param(
- [string]$Version = "",
- [switch]$NoPause = $false
-)
-Write-Output "Building the SRT static libraries installer for Windows"
-
-# Directory containing this script:
-$ScriptDir = $PSScriptRoot
-
-# The root of the srt repository is two levels up.
-$RepoDir = (Split-Path -Parent (Split-Path -Parent $ScriptDir))
-
-# Output directory for final installers:
-$OutDir = "$ScriptDir\installers"
-
-# Temporary directory for build operations:
-$TmpDir = "$ScriptDir\tmp"
-
-
-#-----------------------------------------------------------------------------
-# A function to exit this script with optional error message, using -NoPause
-#-----------------------------------------------------------------------------
-
-function Exit-Script([string]$Message = "")
-{
- $Code = 0
- if ($Message -ne "") {
- Write-Output "ERROR: $Message"
- $Code = 1
- }
- if (-not $NoPause) {
- pause
- }
- exit $Code
-}
-
-
-#-----------------------------------------------------------------------------
-# Build SRT version strings
-#-----------------------------------------------------------------------------
-
-# By default, let git format a decent version number.
-if (-not $Version) {
- $Version = (git describe --tags ) -replace '-g','-'
-}
-$Version = $Version -replace '^v',''
-
-# Split version string in pieces and make sure to get at least four elements.
-$VField = ($Version -split "[-\. ]") + @("0", "0", "0", "0") | Select-String -Pattern '^\d*$'
-$VersionInfo = "$($VField[0]).$($VField[1]).$($VField[2]).$($VField[3])"
-
-Write-Output "SRT version: $Version"
-Write-Output "Windows version info: $VersionInfo"
-
-
-#-----------------------------------------------------------------------------
-# Initialization phase, verify prerequisites
-#-----------------------------------------------------------------------------
-
-# Locate OpenSSL root from local installation.
-$SslRoot = @{
- "x64" = "C:\Program Files\OpenSSL-Win64";
- "Win32" = "C:\Program Files (x86)\OpenSSL-Win32"
-}
-
-# Verify OpenSSL directories.
-$Missing = 0
-foreach ($file in @($SslRoot["x64"], $SslRoot["Win32"])) {
- if (-not (Test-Path $file)) {
- Write-Output "**** Missing $file"
- $Missing = $Missing + 1
- }
-}
-if ($Missing -gt 0) {
- Exit-Script "Missing $Missing OpenSSL files, use install-openssl.ps1 to install OpenSSL"
-}
-
-# Locate MSBuild and CMake, regardless of Visual Studio version.
-Write-Output "Searching MSBuild ..."
-$MSRoots = @("C:\Program Files*\MSBuild", "C:\Program Files*\Microsoft Visual Studio", "C:\Program Files*\CMake*")
-$MSBuild = Get-ChildItem -Recurse -Path $MSRoots -Include MSBuild.exe -ErrorAction Ignore |
- ForEach-Object { (Get-Command $_).FileVersionInfo } |
- Sort-Object -Unique -Property FileVersion |
- ForEach-Object { $_.FileName} |
- Select-Object -Last 1
-if (-not $MSBuild) {
- Exit-Script "MSBuild not found"
-}
-
-Write-Output "Searching CMake ..."
-$CMake = Get-ChildItem -Recurse -Path $MSRoots -Include cmake.exe -ErrorAction Ignore |
- ForEach-Object { (Get-Command $_).FileVersionInfo } |
- Sort-Object -Unique -Property FileVersion |
- ForEach-Object { $_.FileName} |
- Select-Object -Last 1
-if (-not $CMake) {
- Exit-Script "CMake not found, check option 'C++ CMake tools for Windows' in Visual Studio installer"
-}
-
-# Locate NSIS, the Nullsoft Scriptable Installation System.
-Write-Output "Searching NSIS ..."
-$NSIS = Get-Item "C:\Program Files*\NSIS\makensis.exe" | ForEach-Object { $_.FullName} | Select-Object -Last 1
-if (-not $NSIS) {
- Exit-Script "NSIS not found, use install-nsis.ps1 to install NSIS"
-}
-
-Write-Output "MSBuild: $MSBuild"
-Write-Output "CMake: $CMake"
-Write-Output "NSIS: $NSIS"
-
-# Create the directories for builds when necessary.
-[void](New-Item -Path $TmpDir -ItemType Directory -Force)
-[void](New-Item -Path $OutDir -ItemType Directory -Force)
-
-
-#-----------------------------------------------------------------------------
-# Configure and build SRT library using CMake on two architectures.
-#-----------------------------------------------------------------------------
-
-foreach ($Platform in @("x64", "Win32")) {
-
- # Build directory. Cleanup to force a fresh cmake config.
- $BuildDir = "$TmpDir\build.$Platform"
- Remove-Item -Recurse -Force -ErrorAction SilentlyContinue $BuildDir
- [void](New-Item -Path $BuildDir -ItemType Directory -Force)
-
- # Run CMake.
- Write-Output "Configuring build for platform $Platform ..."
- $SRoot = $SslRoot[$Platform]
- & $CMake -S $RepoDir -B $BuildDir -A $Platform `
- -DENABLE_STDCXX_SYNC=ON `
- -DOPENSSL_ROOT_DIR="$SRoot" `
- -DOPENSSL_LIBRARIES="$SRoot\lib\libssl_static.lib;$SRoot\lib\libcrypto_static.lib" `
- -DOPENSSL_INCLUDE_DIR="$SRoot\include"
-
- # Patch version string in version.h
- Get-Content "$BuildDir\version.h" |
- ForEach-Object {
- $_ -replace "#define *SRT_VERSION_STRING .*","#define SRT_VERSION_STRING `"$Version`""
- } |
- Out-File "$BuildDir\version.new" -Encoding ascii
- Move-Item "$BuildDir\version.new" "$BuildDir\version.h" -Force
-
- # Compile SRT.
- Write-Output "Building for platform $Platform ..."
- foreach ($Conf in @("Release", "Debug")) {
- & $MSBuild "$BuildDir\SRT.sln" /nologo /maxcpucount /property:Configuration=$Conf /property:Platform=$Platform /target:srt_static
- }
-}
-
-# Verify the presence of compiled libraries.
-Write-Output "Checking compiled libraries ..."
-$Missing = 0
-foreach ($Conf in @("Release", "Debug")) {
- foreach ($Platform in @("x64", "Win32")) {
- $Path = "$TmpDir\build.$Platform\$Conf\srt_static.lib"
- if (-not (Test-Path $Path)) {
- Write-Output "**** Missing $Path"
- $Missing = $Missing + 1
- }
- }
-}
-if ($Missing -gt 0) {
- Exit-Script "Missing $Missing files"
-}
-
-
-#-----------------------------------------------------------------------------
-# Build the binary installer.
-#-----------------------------------------------------------------------------
-
-$InstallExe = "$OutDir\libsrt-$Version.exe"
-$InstallZip = "$OutDir\libsrt-$Version-win-installer.zip"
-
-Write-Output "Building installer ..."
-& $NSIS /V2 `
- /DVersion="$Version" `
- /DVersionInfo="$VersionInfo" `
- /DOutDir="$OutDir" `
- /DBuildRoot="$TmpDir" `
- /DRepoDir="$RepoDir" `
- "$ScriptDir\libsrt.nsi"
-
-if (-not (Test-Path $InstallExe)) {
- Exit-Script "**** Missing $InstallExe"
-}
-
-Write-Output "Building installer archive ..."
-Remove-Item -Force -ErrorAction SilentlyContinue $InstallZip
-Compress-Archive -Path $InstallExe -DestinationPath $InstallZip -CompressionLevel Optimal
-
-if (-not (Test-Path $InstallZip)) {
- Exit-Script "**** Missing $InstallZip"
-}
-
-Exit-Script
+++ /dev/null
-# SRT library download and install for Windows.
-# Copyright (c) 2021, Thierry Lelegard
-# All rights reserved.
-
-<#
- .SYNOPSIS
-
- Download and install the libsrt library for Windows. This script is
- provided to automate the build of Windows applications using libsrt.
-
- .PARAMETER Destination
-
- Specify a local directory where the libsrt package will be downloaded.
- By default, use "tmp" subdirectory from this script.
-
- .PARAMETER ForceDownload
-
- Force a download even if the package is already downloaded.
-
- .PARAMETER GitHubActions
-
- When used in a GitHub Actions workflow, make sure that the LIBSRT
- environment variable is propagated to subsequent jobs.
-
- .PARAMETER NoInstall
-
- Do not install the package. By default, libsrt is installed.
-
- .PARAMETER NoPause
-
- Do not wait for the user to press <enter> at end of execution. By default,
- execute a "pause" instruction at the end of execution, which is useful
- when the script was run from Windows Explorer.
-#>
-[CmdletBinding(SupportsShouldProcess=$true)]
-param(
- [string]$Destination = "",
- [switch]$ForceDownload = $false,
- [switch]$GitHubActions = $false,
- [switch]$NoInstall = $false,
- [switch]$NoPause = $false
-)
-
-Write-Output "libsrt download and installation procedure"
-
-# Default directory for downloaded products.
-if (-not $Destination) {
- $Destination = "$PSScriptRoot\tmp"
-}
-
-# A function to exit this script.
-function Exit-Script([string]$Message = "")
-{
- $Code = 0
- if ($Message -ne "") {
- Write-Output "ERROR: $Message"
- $Code = 1
- }
- if (-not $NoPause) {
- pause
- }
- exit $Code
-}
-
-# Without this, Invoke-WebRequest is awfully slow.
-$ProgressPreference = 'SilentlyContinue'
-
-# Get the URL of the latest libsrt installer.
-$URL = (Invoke-RestMethod "https://api.github.com/repos/Haivision/srt/releases?per_page=20" |
- ForEach-Object { $_.assets } |
- ForEach-Object { $_.browser_download_url } |
- Select-String @("/libsrt-.*\.exe$", "/libsrt-.*-win-installer\.zip$") |
- Select-Object -First 1)
-
-if (-not $URL) {
- Exit-Script "Could not find a libsrt installer on GitHub"
-}
-if (-not ($URL -match "\.zip$") -and -not ($URL -match "\.exe$")) {
- Exit-Script "Unexpected URL, not .exe, not .zip: $URL"
-}
-
-# Installer name and path.
-$InstName = (Split-Path -Leaf $URL)
-$InstPath = "$Destination\$InstName"
-
-# Create the directory for downloaded products when necessary.
-[void](New-Item -Path $Destination -ItemType Directory -Force)
-
-# Download installer
-if (-not $ForceDownload -and (Test-Path $InstPath)) {
- Write-Output "$InstName already downloaded, use -ForceDownload to download again"
-}
-else {
- Write-Output "Downloading $URL ..."
- Invoke-WebRequest $URL.ToString() -UseBasicParsing -UserAgent Download -OutFile $InstPath
- if (-not (Test-Path $InstPath)) {
- Exit-Script "$URL download failed"
- }
-}
-
-# If installer is an archive, expect an exe with same name inside.
-if ($InstName -match "\.zip$") {
-
- # Expected installer name in archive.
- $ZipName = $InstName
- $ZipPath = $InstPath
- $InstName = $ZipName -replace '-win-installer.zip','.exe'
- $InstPath = "$Destination\$InstName"
-
- # Extract the installer.
- Remove-Item -Force $InstPath -ErrorAction SilentlyContinue
- Write-Output "Expanding $ZipName ..."
- Expand-Archive $ZipPath -DestinationPath $Destination
- if (-not (Test-Path $InstPath)) {
- Exit-Script "$InstName not found in $ZipName"
- }
-}
-
-# Install libsrt
-if (-not $NoInstall) {
- Write-Output "Installing $InstName"
- Start-Process -FilePath $InstPath -ArgumentList @("/S") -Wait
-}
-
-# Propagate LIBSRT in next jobs for GitHub Actions.
-if ($GitHubActions -and (-not -not $env:GITHUB_ENV) -and (Test-Path $env:GITHUB_ENV)) {
- $libsrt = [System.Environment]::GetEnvironmentVariable("LIBSRT","Machine")
- Write-Output "LIBSRT=$libsrt" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
-}
-
-Exit-Script
+++ /dev/null
-#-----------------------------------------------------------------------------
-#
-# SRT - Secure, Reliable, Transport
-# Copyright (c) 2021, Thierry Lelegard
-#
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-#
-#-----------------------------------------------------------------------------
-
-<#
- .SYNOPSIS
-
- Download, expand and install NSIS, the NullSoft Installer Scripting.
-
- .PARAMETER ForceDownload
-
- Force a download even if NSIS is already downloaded.
-
- .PARAMETER NoInstall
-
- Do not install the NSIS package. By default, NSIS is installed.
-
- .PARAMETER NoPause
-
- Do not wait for the user to press <enter> at end of execution. By default,
- execute a "pause" instruction at the end of execution, which is useful
- when the script was run from Windows Explorer.
-#>
-[CmdletBinding(SupportsShouldProcess=$true)]
-param(
- [switch]$ForceDownload = $false,
- [switch]$NoInstall = $false,
- [switch]$NoPause = $false
-)
-
-Write-Output "NSIS download and installation procedure"
-$NSISPage = "https://nsis.sourceforge.io/Download"
-$FallbackURL = "http://prdownloads.sourceforge.net/nsis/nsis-3.05-setup.exe?download"
-
-# A function to exit this script.
-function Exit-Script([string]$Message = "")
-{
- $Code = 0
- if ($Message -ne "") {
- Write-Output "ERROR: $Message"
- $Code = 1
- }
- if (-not $NoPause) {
- pause
- }
- exit $Code
-}
-
-# Local file names.
-$RootDir = $PSScriptRoot
-$TmpDir = "$RootDir\tmp"
-
-# Create the directory for external products when necessary.
-[void] (New-Item -Path $TmpDir -ItemType Directory -Force)
-
-# Without this, Invoke-WebRequest is awfully slow.
-$ProgressPreference = 'SilentlyContinue'
-
-# Get the HTML page for NSIS downloads.
-$status = 0
-$message = ""
-$Ref = $null
-try {
- $response = Invoke-WebRequest -UseBasicParsing -UserAgent Download -Uri $NSISPage
- $status = [int] [Math]::Floor($response.StatusCode / 100)
-}
-catch {
- $message = $_.Exception.Message
-}
-
-if ($status -ne 1 -and $status -ne 2) {
- # Error fetch NSIS download page.
- if ($message -eq "" -and (Test-Path variable:response)) {
- Write-Output "Status code $($response.StatusCode), $($response.StatusDescription)"
- }
- else {
- Write-Output "#### Error accessing ${NSISPage}: $message"
- }
-}
-else {
- # Parse HTML page to locate the latest installer.
- $Ref = $response.Links.href | Where-Object { $_ -like "*/nsis-*-setup.exe?download" } | Select-Object -First 1
-}
-
-if (-not $Ref) {
- # Could not find a reference to NSIS installer.
- $Url = [System.Uri]$FallbackURL
-}
-else {
- # Build the absolute URL's from base URL (the download page) and href links.
- $Url = New-Object -TypeName 'System.Uri' -ArgumentList ([System.Uri]$NSISPage, $Ref)
-}
-
-$InstallerName = (Split-Path -Leaf $Url.LocalPath)
-$InstallerPath = "$TmpDir\$InstallerName"
-
-# Download installer
-if (-not $ForceDownload -and (Test-Path $InstallerPath)) {
- Write-Output "$InstallerName already downloaded, use -ForceDownload to download again"
-}
-else {
- Write-Output "Downloading $Url ..."
- Invoke-WebRequest -UseBasicParsing -UserAgent Download -Uri $Url -OutFile $InstallerPath
- if (-not (Test-Path $InstallerPath)) {
- Exit-Script "$Url download failed"
- }
-}
-
-# Install NSIS
-if (-not $NoInstall) {
- Write-Output "Installing $InstallerName"
- Start-Process -FilePath $InstallerPath -ArgumentList @("/S") -Wait
-}
-
-Exit-Script
+++ /dev/null
-#-----------------------------------------------------------------------------
-#
-# SRT - Secure, Reliable, Transport
-# Copyright (c) 2021, Thierry Lelegard
-#
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-#
-#-----------------------------------------------------------------------------
-
-<#
- .SYNOPSIS
-
- Download, expand and install OpenSSL for Windows.
-
- .PARAMETER ForceDownload
-
- Force a download even if the OpenSSL installers are already downloaded.
-
- .PARAMETER NoInstall
-
- Do not install the OpenSSL packages. By default, OpenSSL is installed.
-
- .PARAMETER NoPause
-
- Do not wait for the user to press <enter> at end of execution. By default,
- execute a "pause" instruction at the end of execution, which is useful
- when the script was run from Windows Explorer.
-#>
-[CmdletBinding(SupportsShouldProcess=$true)]
-param(
- [switch]$ForceDownload = $false,
- [switch]$NoInstall = $false,
- [switch]$NoPause = $false
-)
-
-Write-Output "OpenSSL download and installation procedure"
-$OpenSSLHomePage = "http://slproweb.com/products/Win32OpenSSL.html"
-
-# A function to exit this script.
-function Exit-Script([string]$Message = "")
-{
- $Code = 0
- if ($Message -ne "") {
- Write-Output "ERROR: $Message"
- $Code = 1
- }
- if (-not $NoPause) {
- pause
- }
- exit $Code
-}
-
-# Local file names.
-$RootDir = $PSScriptRoot
-$TmpDir = "$RootDir\tmp"
-
-# Create the directory for external products when necessary.
-[void] (New-Item -Path $TmpDir -ItemType Directory -Force)
-
-# Without this, Invoke-WebRequest is awfully slow.
-$ProgressPreference = 'SilentlyContinue'
-
-# Get the HTML page for OpenSSL downloads.
-$status = 0
-$message = ""
-try {
- $response = Invoke-WebRequest -UseBasicParsing -UserAgent Download -Uri $OpenSSLHomePage
- $status = [int] [Math]::Floor($response.StatusCode / 100)
-}
-catch {
- $message = $_.Exception.Message
-}
-if ($status -ne 1 -and $status -ne 2) {
- if ($message -eq "" -and (Test-Path variable:response)) {
- Exit-Script "Status code $($response.StatusCode), $($response.StatusDescription)"
- }
- else {
- Exit-Script "#### Error accessing ${OpenSSLHomePage}: $message"
- }
-}
-
-# Parse HTML page to locate the latest MSI files.
-$Ref32 = $response.Links.href | Where-Object { $_ -like "*/Win32OpenSSL-*.msi" } | Select-Object -First 1
-$Ref64 = $response.Links.href | Where-Object { $_ -like "*/Win64OpenSSL-*.msi" } | Select-Object -First 1
-
-# Build the absolute URL's from base URL (the download page) and href links.
-$Url32 = New-Object -TypeName 'System.Uri' -ArgumentList ([System.Uri]$OpenSSLHomePage, $Ref32)
-$Url64 = New-Object -TypeName 'System.Uri' -ArgumentList ([System.Uri]$OpenSSLHomePage, $Ref64)
-
-# Download and install one MSI package.
-function Download-Install([string]$Url)
-{
- $MsiName = (Split-Path -Leaf $Url.toString())
- $MsiPath = "$TmpDir\$MsiName"
-
- if (-not $ForceDownload -and (Test-Path $MsiPath)) {
- Write-Output "$MsiName already downloaded, use -ForceDownload to download again"
- }
- else {
- Write-Output "Downloading $Url ..."
- Invoke-WebRequest -UseBasicParsing -UserAgent Download -Uri $Url -OutFile $MsiPath
- }
-
- if (-not (Test-Path $MsiPath)) {
- Exit-Script "$Url download failed"
- }
-
- if (-not $NoInstall) {
- Write-Output "Installing $MsiName"
- Start-Process msiexec.exe -ArgumentList @("/i", $MsiPath, "/qn", "/norestart") -Wait
- }
-}
-
-# Download and install the two MSI packages.
-Download-Install $Url32
-Download-Install $Url64
-Exit-Script
+++ /dev/null
-;-----------------------------------------------------------------------------
-;
-; SRT - Secure, Reliable, Transport
-; Copyright (c) 2021, Thierry Lelegard
-;
-; This Source Code Form is subject to the terms of the Mozilla Public
-; License, v. 2.0. If a copy of the MPL was not distributed with this
-; file, You can obtain one at http://mozilla.org/MPL/2.0/.
-;
-;-----------------------------------------------------------------------------
-;
-; NSIS script to build the SRT binary installer for Windows.
-; Do not invoke NSIS directly, use PowerShell script build-win-installer.ps1
-; to ensure that all parameters are properly passed.
-;
-;-----------------------------------------------------------------------------
-
-Name "SRT"
-Caption "SRT Libraries Installer"
-
-!verbose push
-!verbose 0
-!include "MUI2.nsh"
-!include "Sections.nsh"
-!include "TextFunc.nsh"
-!include "FileFunc.nsh"
-!include "WinMessages.nsh"
-!include "x64.nsh"
-!verbose pop
-
-!define ProductName "libsrt"
-!define Build32Dir "${BuildRoot}\build.Win32"
-!define Build64Dir "${BuildRoot}\build.x64"
-!define SSL32Dir "C:\Program Files (x86)\OpenSSL-Win32"
-!define SSL64Dir "C:\Program Files\OpenSSL-Win64"
-
-; Installer file information.
-VIProductVersion ${VersionInfo}
-VIAddVersionKey ProductName "${ProductName}"
-VIAddVersionKey ProductVersion "${Version}"
-VIAddVersionKey Comments "The SRT static libraries for Visual C++ on Windows"
-VIAddVersionKey CompanyName "Haivision"
-VIAddVersionKey LegalCopyright "Copyright (c) 2021 Haivision Systems Inc."
-VIAddVersionKey FileVersion "${VersionInfo}"
-VIAddVersionKey FileDescription "SRT Installer"
-
-; Name of binary installer file.
-OutFile "${OutDir}\${ProductName}-${Version}.exe"
-
-; Generate a Unicode installer (default is ANSI).
-Unicode true
-
-; Registry key for environment variables
-!define EnvironmentKey '"SYSTEM\CurrentControlSet\Control\Session Manager\Environment"'
-
-; Registry entry for product info and uninstallation info.
-!define ProductKey "Software\${ProductName}"
-!define UninstallKey "Software\Microsoft\Windows\CurrentVersion\Uninstall\${ProductName}"
-
-; Use XP manifest.
-XPStyle on
-
-; Request administrator privileges for Windows Vista and higher.
-RequestExecutionLevel admin
-
-; "Modern User Interface" (MUI) settings.
-!define MUI_ABORTWARNING
-
-; Default installation folder.
-InstallDir "$PROGRAMFILES\${ProductName}"
-
-; Get installation folder from registry if available from a previous installation.
-InstallDirRegKey HKLM "${ProductKey}" "InstallDir"
-
-; Installer pages.
-!insertmacro MUI_PAGE_DIRECTORY
-!insertmacro MUI_PAGE_INSTFILES
-
-; Uninstaller pages.
-!insertmacro MUI_UNPAGE_CONFIRM
-!insertmacro MUI_UNPAGE_INSTFILES
-
-; Languages.
-!insertmacro MUI_LANGUAGE "English"
-
-; Installation initialization.
-function .onInit
- ; In 64-bit installers, don't use registry redirection.
- ${If} ${RunningX64}
- SetRegView 64
- ${EndIf}
-functionEnd
-
-; Uninstallation initialization.
-function un.onInit
- ; In 64-bit installers, don't use registry redirection.
- ${If} ${RunningX64}
- SetRegView 64
- ${EndIf}
-functionEnd
-
-; Installation section
-Section "Install"
-
- ; Work on "all users" context, not current user.
- SetShellVarContext all
-
- ; Delete obsolete files from previous versions.
- Delete "$INSTDIR\LICENSE.pthread.txt"
- Delete "$INSTDIR\include\srt\srt4udt.h"
- Delete "$INSTDIR\include\srt\udt.h"
- Delete "$INSTDIR\lib\Release-x64\pthread.lib"
- Delete "$INSTDIR\lib\Release-Win32\pthread.lib"
- Delete "$INSTDIR\lib\Debug-x64\srt.pdb"
- Delete "$INSTDIR\lib\Debug-x64\pthread.pdb"
- Delete "$INSTDIR\lib\Debug-x64\pthread.lib"
- Delete "$INSTDIR\lib\Debug-Win32\srt.pdb"
- Delete "$INSTDIR\lib\Debug-Win32\pthread.pdb"
- Delete "$INSTDIR\lib\Debug-Win32\pthread.lib"
-
- SetOutPath "$INSTDIR"
- File /oname=LICENSE.txt "${RepoDir}\LICENSE"
- File "libsrt.props"
-
- ; Header files.
- CreateDirectory "$INSTDIR\include\srt"
- SetOutPath "$INSTDIR\include\srt"
- File "${RepoDir}\srtcore\logging_api.h"
- File "${RepoDir}\srtcore\platform_sys.h"
- File "${RepoDir}\srtcore\srt.h"
- File "${Build64Dir}\version.h"
-
- CreateDirectory "$INSTDIR\include\win"
- SetOutPath "$INSTDIR\include\win"
- File "${RepoDir}\common\win\syslog_defs.h"
-
- ; Libraries.
- CreateDirectory "$INSTDIR\lib"
-
- CreateDirectory "$INSTDIR\lib\Release-x64"
- SetOutPath "$INSTDIR\lib\Release-x64"
- File /oname=srt.lib "${Build64Dir}\Release\srt_static.lib"
- File /oname=libcrypto.lib "${SSL64Dir}\lib\VC\static\libcrypto64MD.lib"
- File /oname=libssl.lib "${SSL64Dir}\lib\VC\static\libssl64MD.lib"
-
- CreateDirectory "$INSTDIR\lib\Debug-x64"
- SetOutPath "$INSTDIR\lib\Debug-x64"
- File /oname=srt.lib "${Build64Dir}\Debug\srt_static.lib"
- File /oname=libcrypto.lib "${SSL64Dir}\lib\VC\static\libcrypto64MDd.lib"
- File /oname=libssl.lib "${SSL64Dir}\lib\VC\static\libssl64MDd.lib"
-
- CreateDirectory "$INSTDIR\lib\Release-Win32"
- SetOutPath "$INSTDIR\lib\Release-Win32"
- File /oname=srt.lib "${Build32Dir}\Release\srt_static.lib"
- File /oname=libcrypto.lib "${SSL32Dir}\lib\VC\static\libcrypto32MD.lib"
- File /oname=libssl.lib "${SSL32Dir}\lib\VC\static\libssl32MD.lib"
-
- CreateDirectory "$INSTDIR\lib\Debug-Win32"
- SetOutPath "$INSTDIR\lib\Debug-Win32"
- File /oname=srt.lib "${Build32Dir}\Debug\srt_static.lib"
- File /oname=libcrypto.lib "${SSL32Dir}\lib\VC\static\libcrypto32MDd.lib"
- File /oname=libssl.lib "${SSL32Dir}\lib\VC\static\libssl32MDd.lib"
-
- ; Add an environment variable to installation root.
- WriteRegStr HKLM ${EnvironmentKey} "LIBSRT" "$INSTDIR"
-
- ; Store installation folder in registry.
- WriteRegStr HKLM "${ProductKey}" "InstallDir" $INSTDIR
-
- ; Create uninstaller
- WriteUninstaller "$INSTDIR\Uninstall.exe"
-
- ; Declare uninstaller in "Add/Remove Software" control panel
- WriteRegStr HKLM "${UninstallKey}" "DisplayName" "${ProductName}"
- WriteRegStr HKLM "${UninstallKey}" "Publisher" "Haivision"
- WriteRegStr HKLM "${UninstallKey}" "URLInfoAbout" "https://github.com/Haivision/srt"
- WriteRegStr HKLM "${UninstallKey}" "DisplayVersion" "${Version}"
- WriteRegStr HKLM "${UninstallKey}" "DisplayIcon" "$INSTDIR\Uninstall.exe"
- WriteRegStr HKLM "${UninstallKey}" "UninstallString" "$INSTDIR\Uninstall.exe"
-
- ; Get estimated size of installed files
- ${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2
- IntFmt $0 "0x%08X" $0
- WriteRegDWORD HKLM "${UninstallKey}" "EstimatedSize" "$0"
-
- ; Notify applications of environment modifications
- SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
-
-SectionEnd
-
-; Uninstallation section
-Section "Uninstall"
-
- ; Work on "all users" context, not current user.
- SetShellVarContext all
-
- ; Get installation folder from registry
- ReadRegStr $0 HKLM "${ProductKey}" "InstallDir"
-
- ; Delete product registry entries
- DeleteRegKey HKCU "${ProductKey}"
- DeleteRegKey HKLM "${ProductKey}"
- DeleteRegKey HKLM "${UninstallKey}"
- DeleteRegValue HKLM ${EnvironmentKey} "LIBSRT"
-
- ; Delete product files.
- RMDir /r "$0\include"
- RMDir /r "$0\lib"
- Delete "$0\libsrt.props"
- Delete "$0\LICENSE*"
- Delete "$0\Uninstall.exe"
- RMDir "$0"
-
- ; Notify applications of environment modifications
- SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
-
-SectionEnd
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-
-<!-- Visual Studio or MSBuild property file to use SRT static library -->
-
-<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-
- <!-- Normalize platform name to x64 and Win32 (some projects use x86 or Win64) -->
- <Choose>
- <When Condition="'$(Platform)' == 'x86'">
- <PropertyGroup Label="UserMacros">
- <SrtPlatform>Win32</SrtPlatform>
- </PropertyGroup>
- </When>
- <When Condition="'$(Platform)' == 'Win64'">
- <PropertyGroup Label="UserMacros">
- <SrtPlatform>x64</SrtPlatform>
- </PropertyGroup>
- </When>
- <Otherwise>
- <PropertyGroup Label="UserMacros">
- <SrtPlatform>$(Platform)</SrtPlatform>
- </PropertyGroup>
- </Otherwise>
- </Choose>
-
- <!-- Compilation and link options -->
- <ItemDefinitionGroup>
- <ClCompile>
- <AdditionalIncludeDirectories>$(LIBSRT)\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
- </ClCompile>
- <Link>
- <AdditionalDependencies>srt.lib;libssl.lib;libcrypto.lib;crypt32.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
- <AdditionalLibraryDirectories>$(LIBSRT)\lib\$(Configuration)-$(SrtPlatform);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
- <AdditionalOptions>/ignore:4099 %(AdditionalOptions)</AdditionalOptions>
- </Link>
- </ItemDefinitionGroup>
-
-</Project>
+++ /dev/null
-# must be unique in a given SonarQube instance
-sonar.projectKey=srt
-sonar.organization=haivision
-
-# --- optional properties ---
-
-# This is the name and version displayed in the SonarCloud UI.
-#sonar.projectName=srt
-#sonar.projectVersion=1.0
-
-# Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows.
-#sonar.sources=.
-
-# Encoding of the source code. Default is default system encoding
-#sonar.sourceEncoding=UTF-8
-
-# =====================================================
-# Meta-data for the project
-# =====================================================
-
-sonar.links.homepage=https://github.com/Haivision/srt
-sonar.links.ci=https://travis-ci.org/Haivision/srt
-sonar.links.scm=https://github.com/Haivision/srt
-sonar.links.issue=https://github.com/Haivision/srt/issues
-
-
-# =====================================================
-# Properties that will be shared amongst all modules
-# =====================================================
-
-# SQ standard properties
-sonar.sources=.
-
-# Properties specific to the C/C++ analyzer:
-sonar.cfamily.build-wrapper-output=bw-output
-sonar.cfamily.gcov.reportsPath=.
+++ /dev/null
-scripts/srt-ffplay
\ No newline at end of file
+++ /dev/null
-//
-// Implementation of N4562 std::experimental::any (merged into C++17) for C++11 compilers.
-//
-// See also:
-// + http://en.cppreference.com/w/cpp/any
-// + http://en.cppreference.com/w/cpp/experimental/any
-// + http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4562.html#any
-// + https://cplusplus.github.io/LWG/lwg-active.html#2509
-//
-//
-// Copyright (c) 2016 Denilson das Mercês Amorim
-//
-// Distributed under the Boost Software License, Version 1.0. (See accompanying
-// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-//
-#ifndef LINB_ANY_HPP
-#define LINB_ANY_HPP
-#pragma once
-#include <typeinfo>
-#include <type_traits>
-#include <stdexcept>
-
-
-#if defined(PARTICLE)
-#if !defined(__cpp_exceptions) && !defined(ANY_IMPL_NO_EXCEPTIONS) && !defined(ANY_IMPL_EXCEPTIONS)
-# define ANY_IMPL_NO_EXCEPTIONS
-# endif
-#else
-// you can opt-out of exceptions by definining ANY_IMPL_NO_EXCEPTIONS,
-// but you must ensure not to cast badly when passing an `any' object to any_cast<T>(any)
-#endif
-
-#if defined(PARTICLE)
-#if !defined(__cpp_rtti) && !defined(ANY_IMPL_NO_RTTI) && !defined(ANY_IMPL_RTTI)
-# define ANY_IMPL_NO_RTTI
-# endif
-#else
-// you can opt-out of RTTI by defining ANY_IMPL_NO_RTTI,
-// in order to disable functions working with the typeid of a type
-#endif
-
-
-namespace linb
-{
-
-class bad_any_cast : public std::bad_cast
-{
-public:
- const char* what() const noexcept override
- {
- return "bad any cast";
- }
-};
-
-class any final
-{
-public:
- /// Constructs an object of type any with an empty state.
- any() :
- vtable(nullptr)
- {
- }
-
- /// Constructs an object of type any with an equivalent state as other.
- any(const any& rhs) :
- vtable(rhs.vtable)
- {
- if(!rhs.empty())
- {
- rhs.vtable->copy(rhs.storage, this->storage);
- }
- }
-
- /// Constructs an object of type any with a state equivalent to the original state of other.
- /// rhs is left in a valid but otherwise unspecified state.
- any(any&& rhs) noexcept :
- vtable(rhs.vtable)
- {
- if(!rhs.empty())
- {
- rhs.vtable->move(rhs.storage, this->storage);
- rhs.vtable = nullptr;
- }
- }
-
- /// Same effect as this->clear().
- ~any()
- {
- this->clear();
- }
-
- /// Constructs an object of type any that contains an object of type T direct-initialized with std::forward<ValueType>(value).
- ///
- /// T shall satisfy the CopyConstructible requirements, otherwise the program is ill-formed.
- /// This is because an `any` may be copy constructed into another `any` at any time, so a copy should always be allowed.
- template<typename ValueType, typename = typename std::enable_if<!std::is_same<typename std::decay<ValueType>::type, any>::value>::type>
- any(ValueType&& value)
- {
- static_assert(std::is_copy_constructible<typename std::decay<ValueType>::type>::value,
- "T shall satisfy the CopyConstructible requirements.");
- this->construct(std::forward<ValueType>(value));
- }
-
- /// Has the same effect as any(rhs).swap(*this). No effects if an exception is thrown.
- any& operator=(const any& rhs)
- {
- any(rhs).swap(*this);
- return *this;
- }
-
- /// Has the same effect as any(std::move(rhs)).swap(*this).
- ///
- /// The state of *this is equivalent to the original state of rhs and rhs is left in a valid
- /// but otherwise unspecified state.
- any& operator=(any&& rhs) noexcept
- {
- any(std::move(rhs)).swap(*this);
- return *this;
- }
-
- /// Has the same effect as any(std::forward<ValueType>(value)).swap(*this). No effect if a exception is thrown.
- ///
- /// T shall satisfy the CopyConstructible requirements, otherwise the program is ill-formed.
- /// This is because an `any` may be copy constructed into another `any` at any time, so a copy should always be allowed.
- template<typename ValueType, typename = typename std::enable_if<!std::is_same<typename std::decay<ValueType>::type, any>::value>::type>
- any& operator=(ValueType&& value)
- {
- static_assert(std::is_copy_constructible<typename std::decay<ValueType>::type>::value,
- "T shall satisfy the CopyConstructible requirements.");
- any(std::forward<ValueType>(value)).swap(*this);
- return *this;
- }
-
- /// If not empty, destroys the contained object.
- void clear() noexcept
- {
- if(!empty())
- {
- this->vtable->destroy(storage);
- this->vtable = nullptr;
- }
- }
-
- /// Returns true if *this has no contained object, otherwise false.
- bool empty() const noexcept
- {
- return this->vtable == nullptr;
- }
-
-#ifndef ANY_IMPL_NO_RTTI
- /// If *this has a contained object of type T, typeid(T); otherwise typeid(void).
- const std::type_info& type() const noexcept
- {
- return empty()? typeid(void) : this->vtable->type();
- }
-#endif
-
- /// Exchange the states of *this and rhs.
- void swap(any& rhs) noexcept
- {
- if(this->vtable != rhs.vtable)
- {
- any tmp(std::move(rhs));
-
- // move from *this to rhs.
- rhs.vtable = this->vtable;
- if(this->vtable != nullptr)
- {
- this->vtable->move(this->storage, rhs.storage);
- //this->vtable = nullptr; -- unneeded, see below
- }
-
- // move from tmp (previously rhs) to *this.
- this->vtable = tmp.vtable;
- if(tmp.vtable != nullptr)
- {
- tmp.vtable->move(tmp.storage, this->storage);
- tmp.vtable = nullptr;
- }
- }
- else // same types
- {
- if(this->vtable != nullptr)
- this->vtable->swap(this->storage, rhs.storage);
- }
- }
-
-private: // Storage and Virtual Method Table
- union storage_union
- {
- using stack_storage_t = typename std::aligned_storage<2 * sizeof(void*), std::alignment_of<void*>::value>::type;
-
- void* dynamic;
- stack_storage_t stack; // 2 words for e.g. shared_ptr
- };
-
- /// Base VTable specification.
- struct vtable_type
- {
- // Note: The caller is responssible for doing .vtable = nullptr after destructful operations
- // such as destroy() and/or move().
-
-#ifndef ANY_IMPL_NO_RTTI
- /// The type of the object this vtable is for.
- const std::type_info& (*type)() noexcept;
-#endif
-
- /// Destroys the object in the union.
- /// The state of the union after this call is unspecified, caller must ensure not to use src anymore.
- void(*destroy)(storage_union&) noexcept;
-
- /// Copies the **inner** content of the src union into the yet unitialized dest union.
- /// As such, both inner objects will have the same state, but on separate memory locations.
- void(*copy)(const storage_union& src, storage_union& dest);
-
- /// Moves the storage from src to the yet unitialized dest union.
- /// The state of src after this call is unspecified, caller must ensure not to use src anymore.
- void(*move)(storage_union& src, storage_union& dest) noexcept;
-
- /// Exchanges the storage between lhs and rhs.
- void(*swap)(storage_union& lhs, storage_union& rhs) noexcept;
- };
-
- /// VTable for dynamically allocated storage.
- template<typename T>
- struct vtable_dynamic
- {
-#ifndef ANY_IMPL_NO_RTTI
- static const std::type_info& type() noexcept
- {
- return typeid(T);
- }
-#endif
-
- static void destroy(storage_union& storage) noexcept
- {
- //assert(reinterpret_cast<T*>(storage.dynamic));
- delete reinterpret_cast<T*>(storage.dynamic);
- }
-
- static void copy(const storage_union& src, storage_union& dest)
- {
- dest.dynamic = new T(*reinterpret_cast<const T*>(src.dynamic));
- }
-
- static void move(storage_union& src, storage_union& dest) noexcept
- {
- dest.dynamic = src.dynamic;
- src.dynamic = nullptr;
- }
-
- static void swap(storage_union& lhs, storage_union& rhs) noexcept
- {
- // just exchage the storage pointers.
- std::swap(lhs.dynamic, rhs.dynamic);
- }
- };
-
- /// VTable for stack allocated storage.
- template<typename T>
- struct vtable_stack
- {
-#ifndef ANY_IMPL_NO_RTTI
- static const std::type_info& type() noexcept
- {
- return typeid(T);
- }
-#endif
-
- static void destroy(storage_union& storage) noexcept
- {
- reinterpret_cast<T*>(&storage.stack)->~T();
- }
-
- static void copy(const storage_union& src, storage_union& dest)
- {
- new (&dest.stack) T(reinterpret_cast<const T&>(src.stack));
- }
-
- static void move(storage_union& src, storage_union& dest) noexcept
- {
- // one of the conditions for using vtable_stack is a nothrow move constructor,
- // so this move constructor will never throw a exception.
- new (&dest.stack) T(std::move(reinterpret_cast<T&>(src.stack)));
- destroy(src);
- }
-
- static void swap(storage_union& lhs, storage_union& rhs) noexcept
- {
- storage_union tmp_storage;
- move(rhs, tmp_storage);
- move(lhs, rhs);
- move(tmp_storage, lhs);
- }
- };
-
- /// Whether the type T must be dynamically allocated or can be stored on the stack.
- template<typename T>
- struct requires_allocation :
- std::integral_constant<bool,
- !(std::is_nothrow_move_constructible<T>::value // N4562 §6.3/3 [any.class]
- && sizeof(T) <= sizeof(storage_union::stack)
- && std::alignment_of<T>::value <= std::alignment_of<storage_union::stack_storage_t>::value)>
- {};
-
- /// Returns the pointer to the vtable of the type T.
- template<typename T>
- static vtable_type* vtable_for_type()
- {
- using VTableType = typename std::conditional<requires_allocation<T>::value, vtable_dynamic<T>, vtable_stack<T>>::type;
- static vtable_type table = {
-#ifndef ANY_IMPL_NO_RTTI
- VTableType::type,
-#endif
- VTableType::destroy,
- VTableType::copy, VTableType::move,
- VTableType::swap,
- };
- return &table;
- }
-
-protected:
- template<typename T>
- friend const T* any_cast(const any* operand) noexcept;
- template<typename T>
- friend T* any_cast(any* operand) noexcept;
-
-#ifndef ANY_IMPL_NO_RTTI
- /// Same effect as is_same(this->type(), t);
- bool is_typed(const std::type_info& t) const
- {
- return is_same(this->type(), t);
- }
-#endif
-
-#ifndef ANY_IMPL_NO_RTTI
- /// Checks if two type infos are the same.
- ///
- /// If ANY_IMPL_FAST_TYPE_INFO_COMPARE is defined, checks only the address of the
- /// type infos, otherwise does an actual comparision. Checking addresses is
- /// only a valid approach when there's no interaction with outside sources
- /// (other shared libraries and such).
- static bool is_same(const std::type_info& a, const std::type_info& b)
- {
-#ifdef ANY_IMPL_FAST_TYPE_INFO_COMPARE
- return &a == &b;
-#else
- return a == b;
-#endif
- }
-#endif
-
- /// Casts (with no type_info checks) the storage pointer as const T*.
- template<typename T>
- const T* cast() const noexcept
- {
- return requires_allocation<typename std::decay<T>::type>::value?
- reinterpret_cast<const T*>(storage.dynamic) :
- reinterpret_cast<const T*>(&storage.stack);
- }
-
- /// Casts (with no type_info checks) the storage pointer as T*.
- template<typename T>
- T* cast() noexcept
- {
- return requires_allocation<typename std::decay<T>::type>::value?
- reinterpret_cast<T*>(storage.dynamic) :
- reinterpret_cast<T*>(&storage.stack);
- }
-
-private:
- storage_union storage; // on offset(0) so no padding for align
- vtable_type* vtable;
-
- template<typename ValueType, typename T>
- typename std::enable_if<requires_allocation<T>::value>::type
- do_construct(ValueType&& value)
- {
- storage.dynamic = new T(std::forward<ValueType>(value));
- }
-
- template<typename ValueType, typename T>
- typename std::enable_if<!requires_allocation<T>::value>::type
- do_construct(ValueType&& value)
- {
- new (&storage.stack) T(std::forward<ValueType>(value));
- }
-
- /// Chooses between stack and dynamic allocation for the type decay_t<ValueType>,
- /// assigns the correct vtable, and constructs the object on our storage.
- template<typename ValueType>
- void construct(ValueType&& value)
- {
- using T = typename std::decay<ValueType>::type;
-
- this->vtable = vtable_for_type<T>();
-
- do_construct<ValueType,T>(std::forward<ValueType>(value));
- }
-};
-
-
-
-namespace detail
-{
- template<typename ValueType>
- inline ValueType any_cast_move_if_true(typename std::remove_reference<ValueType>::type* p, std::true_type)
- {
- return std::move(*p);
- }
-
- template<typename ValueType>
- inline ValueType any_cast_move_if_true(typename std::remove_reference<ValueType>::type* p, std::false_type)
- {
- return *p;
- }
-}
-
-/// Performs *any_cast<add_const_t<remove_reference_t<ValueType>>>(&operand), or throws bad_any_cast on failure.
-template<typename ValueType>
-inline ValueType any_cast(const any& operand)
-{
- auto p = any_cast<typename std::add_const<typename std::remove_reference<ValueType>::type>::type>(&operand);
-#ifndef ANY_IMPL_NO_EXCEPTIONS
- if(p == nullptr) throw bad_any_cast();
-#endif
- return *p;
-}
-
-/// Performs *any_cast<remove_reference_t<ValueType>>(&operand), or throws bad_any_cast on failure.
-template<typename ValueType>
-inline ValueType any_cast(any& operand)
-{
- auto p = any_cast<typename std::remove_reference<ValueType>::type>(&operand);
-#ifndef ANY_IMPL_NO_EXCEPTIONS
- if(p == nullptr) throw bad_any_cast();
-#endif
- return *p;
-}
-
-///
-/// If ValueType is MoveConstructible and isn't a lvalue reference, performs
-/// std::move(*any_cast<remove_reference_t<ValueType>>(&operand)), otherwise
-/// *any_cast<remove_reference_t<ValueType>>(&operand). Throws bad_any_cast on failure.
-///
-template<typename ValueType>
-inline ValueType any_cast(any&& operand)
-{
- using can_move = std::integral_constant<bool,
- std::is_move_constructible<ValueType>::value
- && !std::is_lvalue_reference<ValueType>::value>;
-
- auto p = any_cast<typename std::remove_reference<ValueType>::type>(&operand);
-#ifndef ANY_IMPL_NO_EXCEPTIONS
- if(p == nullptr) throw bad_any_cast();
-#endif
- return detail::any_cast_move_if_true<ValueType>(p, can_move());
-}
-
-/// If operand != nullptr && operand->type() == typeid(ValueType), a pointer to the object
-/// contained by operand, otherwise nullptr.
-template<typename ValueType>
-inline const ValueType* any_cast(const any* operand) noexcept
-{
- using T = typename std::decay<ValueType>::type;
-
-#ifndef ANY_IMPL_NO_RTTI
- if (operand && operand->is_typed(typeid(T)))
-#else
- if (operand && operand->vtable == any::vtable_for_type<T>())
-#endif
- return operand->cast<ValueType>();
- else
- return nullptr;
-}
-
-/// If operand != nullptr && operand->type() == typeid(ValueType), a pointer to the object
-/// contained by operand, otherwise nullptr.
-template<typename ValueType>
-inline ValueType* any_cast(any* operand) noexcept
-{
- using T = typename std::decay<ValueType>::type;
-
-#ifndef ANY_IMPL_NO_RTTI
- if (operand && operand->is_typed(typeid(T)))
-#else
- if (operand && operand->vtable == any::vtable_for_type<T>())
-#endif
- return operand->cast<ValueType>();
- else
- return nullptr;
-}
-
-}
-
-namespace std
-{
- inline void swap(linb::any& lhs, linb::any& rhs) noexcept
- {
- lhs.swap(rhs);
- }
-}
-
-#endif
+++ /dev/null
-HEADERS
-any.hpp
-
-SOURCES
-test_buffer.cpp
-test_bonding.cpp
-test_common.cpp
-test_connection_timeout.cpp
-test_many_connections.cpp
-test_cryspr.cpp
-test_enforced_encryption.cpp
-test_epoll.cpp
-test_fec_rebuilding.cpp
-test_file_transmission.cpp
-test_ipv6.cpp
-test_list.cpp
-test_listen_callback.cpp
-test_muxer.cpp
-test_seqno.cpp
-test_socket_options.cpp
-test_sync.cpp
-test_threadname.cpp
-test_timer.cpp
-test_unitqueue.cpp
-test_utilities.cpp
-test_reuseaddr.cpp
-
-# Tests for bonding only - put here!
-
-SOURCES - ENABLE_BONDING
-
+++ /dev/null
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <future>
-#include <thread>
-#include <chrono>
-#include <vector>
-#include <functional>
-#ifdef _WIN32
-#define usleep(x) Sleep(x / 1000)
-#else
-#include <unistd.h>
-#endif
-
-#if ENABLE_BONDING
-
-#include "gtest/gtest.h"
-
-#include "srt.h"
-#include "netinet_any.h"
-
-TEST(Bonding, SRTConnectGroup)
-{
- struct sockaddr_in sa;
-
- srt_startup();
-
- const int ss = srt_create_group(SRT_GTYPE_BROADCAST);
- ASSERT_NE(ss, SRT_ERROR);
-
- std::vector<SRT_SOCKGROUPCONFIG> targets;
- for (int i = 0; i < 2; ++i)
- {
- sa.sin_family = AF_INET;
- sa.sin_port = htons(4200 + i);
- ASSERT_EQ(inet_pton(AF_INET, "192.168.1.237", &sa.sin_addr), 1);
-
- const SRT_SOCKGROUPCONFIG gd = srt_prepare_endpoint(NULL, (struct sockaddr*)&sa, sizeof sa);
- targets.push_back(gd);
- }
-
- std::future<void> closing_promise = std::async(std::launch::async, [](int ss) {
- std::this_thread::sleep_for(std::chrono::seconds(2));
- std::cerr << "Closing group" << std::endl;
- srt_close(ss);
- }, ss);
-
- std::cout << "srt_connect_group calling " << std::endl;
- const int st = srt_connect_group(ss, targets.data(), targets.size());
- std::cout << "srt_connect_group returned " << st << std::endl;
-
- closing_promise.wait();
- // Delete config objects before prospective exception
- for (auto& gd: targets)
- srt_delete_config(gd.config);
-
- int res = srt_close(ss);
- if (res == SRT_ERROR)
- {
- std::cerr << "srt_close: " << srt_getlasterror_str() << std::endl;
- }
-
- srt_cleanup();
-}
-
-
-void listening_thread(bool should_read)
-{
- const SRTSOCKET server_sock = srt_create_socket();
- sockaddr_in bind_sa;
- memset(&bind_sa, 0, sizeof bind_sa);
- bind_sa.sin_family = AF_INET;
- ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &bind_sa.sin_addr), 1);
- bind_sa.sin_port = htons(4200);
-
- ASSERT_NE(srt_bind(server_sock, (sockaddr*)&bind_sa, sizeof bind_sa), -1);
- const int yes = 1;
- srt_setsockflag(server_sock, SRTO_GROUPCONNECT, &yes, sizeof yes);
-
- const int no = 1;
- srt_setsockflag(server_sock, SRTO_RCVSYN, &no, sizeof no);
-
- const int eid = srt_epoll_create();
- const int listen_event = SRT_EPOLL_IN | SRT_EPOLL_ERR;
- srt_epoll_add_usock(eid, server_sock, &listen_event);
-
- ASSERT_NE(srt_listen(server_sock, 5), -1);
- std::cout << "Listen: wait for acceptability\n";
- int fds[2];
- int fds_len = 2;
- int ers[2];
- int ers_len = 2;
- int wr = srt_epoll_wait(eid, fds, &fds_len, ers, &ers_len, 5000,
- 0, 0, 0, 0);
-
- ASSERT_NE(wr, -1);
- std::cout << "Listen: reported " << fds_len << " acceptable and " << ers_len << " errors\n";
- ASSERT_GT(fds_len, 0);
- ASSERT_EQ(fds[0], server_sock);
-
- srt::sockaddr_any scl;
- int acp = srt_accept(server_sock, (scl.get()), (&scl.len));
- ASSERT_NE(acp & SRTGROUP_MASK, 0);
-
- if (should_read)
- {
- std::cout << "Listener will read packets...\n";
- // Read everything until closed
- int n = 0;
- for (;;)
- {
- char buf[1500];
- int rd = srt_recv(acp, buf, 1500);
- if (rd == -1)
- {
- std::cout << "Listener read " << n << " packets, stopping\n";
- break;
- }
- ++n;
- }
- }
-
- srt_close(acp);
-
- std::cout << "Listen: wait 7 seconds\n";
- std::this_thread::sleep_for(std::chrono::seconds(7));
- // srt_accept..
-}
-
-void ConnectCallback(void* /*opaq*/, SRTSOCKET sock, int error, const sockaddr* /*peer*/, int token)
-{
- std::cout << "Connect callback. Socket: " << sock
- << ", error: " << error
- << ", token: " << token << '\n';
-}
-
-TEST(Bonding, NonBlockingGroupConnect)
-{
- srt_startup();
-
- const int ss = srt_create_group(SRT_GTYPE_BROADCAST);
- ASSERT_NE(ss, SRT_ERROR);
- std::cout << "Created group socket: " << ss << '\n';
-
- int no = 0;
- ASSERT_NE(srt_setsockopt(ss, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // non-blocking mode
- ASSERT_NE(srt_setsockopt(ss, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // non-blocking mode
-
- const int poll_id = srt_epoll_create();
- // Will use this epoll to wait for srt_accept(...)
- const int epoll_out = SRT_EPOLL_OUT | SRT_EPOLL_ERR;
- ASSERT_NE(srt_epoll_add_usock(poll_id, ss, &epoll_out), SRT_ERROR);
-
- srt_connect_callback(ss, &ConnectCallback, this);
-
- sockaddr_in sa;
- sa.sin_family = AF_INET;
- sa.sin_port = htons(4200);
- ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1);
-
- sockaddr_in safail = sa;
- safail.sin_port = htons(4201); // port where we have no listener
-
- std::future<void> listen_promise = std::async(std::launch::async, std::bind(&listening_thread, false));
-
- std::cout << "Connecting two sockets " << std::endl;
- {
- const int sockid = srt_connect(ss, (sockaddr*) &sa, sizeof sa);
- EXPECT_GT(sockid, 0) << "Socket " << 1;
- sa.sin_port = htons(4201); // Changing port so that second connect fails
- std::cout << "Socket created: " << sockid << '\n';
- ASSERT_NE(srt_epoll_add_usock(poll_id, sockid, &epoll_out), SRT_ERROR);
- }
- {
- const int sockid = srt_connect(ss, (sockaddr*) &safail, sizeof safail);
- EXPECT_GT(sockid, 0) << "Socket " << 2;
- safail.sin_port = htons(4201); // Changing port so that second connect fails
- std::cout << "Socket created: " << sockid << '\n';
- ASSERT_NE(srt_epoll_add_usock(poll_id, sockid, &epoll_out), SRT_ERROR);
- }
- std::cout << "Returned from connecting two sockets " << std::endl;
-
- const int default_len = 3;
- int rlen = default_len;
- SRTSOCKET read[default_len];
-
- int wlen = default_len;
- SRTSOCKET write[default_len];
-
- for (int j = 0; j < 2; ++j)
- {
- const int epoll_res = srt_epoll_wait(poll_id, read, &rlen,
- write, &wlen,
- 5000, /* timeout */
- 0, 0, 0, 0);
-
- std::cout << "Epoll result: " << epoll_res << '\n';
- std::cout << "Epoll rlen: " << rlen << ", wlen: " << wlen << '\n';
- for (int i = 0; i < rlen; ++i)
- {
- std::cout << "Epoll read[" << i << "]: " << read[i] << '\n';
- }
- for (int i = 0; i < wlen; ++i)
- {
- std::cout << "Epoll write[" << i << "]: " << write[i] << " (removed from epoll)\n";
- EXPECT_EQ(srt_epoll_remove_usock(poll_id, write[i]), 0);
- }
- }
-
- listen_promise.wait();
-
- EXPECT_EQ(srt_close(ss), 0) << "srt_close: %s\n" << srt_getlasterror_str();
-
- srt_cleanup();
-}
-
-void ConnectCallback_Close(void* /*opaq*/, SRTSOCKET sock, int error, const sockaddr* /*peer*/, int token)
-{
- std::cout << "Connect callback. Socket: " << sock
- << ", error: " << error
- << ", token: " << token << '\n';
-
- if (error == SRT_SUCCESS)
- return;
-
- // XXX WILL CAUSE DEADLOCK!
- srt_close(sock);
-}
-
-TEST(Bonding, CloseGroupAndSocket)
-{
- srt_startup();
-
- const int ss = srt_create_group(SRT_GTYPE_BROADCAST);
- ASSERT_NE(ss, SRT_ERROR);
- std::cout << "Created group socket: " << ss << '\n';
-
- int no = 0;
- ASSERT_NE(srt_setsockopt(ss, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // non-blocking mode
- ASSERT_NE(srt_setsockopt(ss, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // non-blocking mode
-
- const int poll_id = srt_epoll_create();
- // Will use this epoll to wait for srt_accept(...)
- const int epoll_out = SRT_EPOLL_OUT | SRT_EPOLL_ERR;
- ASSERT_NE(srt_epoll_add_usock(poll_id, ss, &epoll_out), SRT_ERROR);
-
- srt_connect_callback(ss, &ConnectCallback_Close, this);
-
- sockaddr_in sa;
- sa.sin_family = AF_INET;
- sa.sin_port = htons(4200);
- ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1);
-
- std::future<void> listen_promise = std::async(std::launch::async, std::bind(listening_thread, true));
-
- std::cout << "Connecting two sockets " << std::endl;
- for (int i = 0; i < 2; ++i)
- {
- const int sockid = srt_connect(ss, (sockaddr*) &sa, sizeof sa);
- EXPECT_GT(sockid, 0) << "Socket " << i;
- sa.sin_port = htons(4201); // Changing port so that second connect fails
- std::cout << "Socket created: " << sockid << '\n';
- ASSERT_NE(srt_epoll_add_usock(poll_id, sockid, &epoll_out), SRT_ERROR);
- }
- std::cout << "Returned from connecting two sockets " << std::endl;
-
- const int default_len = 3;
- int rlen = default_len;
- SRTSOCKET read[default_len];
-
- int wlen = default_len;
- SRTSOCKET write[default_len];
-
- for (int j = 0; j < 2; ++j)
- {
- const int epoll_res = srt_epoll_wait(poll_id, read, &rlen,
- write, &wlen,
- 5000, /* timeout */
- 0, 0, 0, 0);
-
- std::cout << "Epoll result: " << epoll_res << '\n';
- std::cout << "Epoll rlen: " << rlen << ", wlen: " << wlen << '\n';
- for (int i = 0; i < rlen; ++i)
- {
- std::cout << "Epoll read[" << i << "]: " << read[i] << '\n';
- }
- for (int i = 0; i < wlen; ++i)
- {
- std::cout << "Epoll write[" << i << "]: " << write[i] << " (removed from epoll)\n";
- EXPECT_EQ(srt_epoll_remove_usock(poll_id, write[i]), 0);
- }
- }
-
- // Some basic checks for group stats
- SRT_TRACEBSTATS stats;
- EXPECT_EQ(srt_bstats(ss, &stats, true), SRT_SUCCESS);
- EXPECT_EQ(stats.pktSent, 0);
- EXPECT_EQ(stats.pktSentTotal, 0);
- EXPECT_EQ(stats.pktSentUnique, 0);
- EXPECT_EQ(stats.pktSentUniqueTotal, 0);
- EXPECT_EQ(stats.pktRecv, 0);
- EXPECT_EQ(stats.pktRecvTotal, 0);
- EXPECT_EQ(stats.pktRecvUnique, 0);
- EXPECT_EQ(stats.pktRecvUniqueTotal, 0);
- EXPECT_EQ(stats.pktRcvDrop, 0);
- EXPECT_EQ(stats.pktRcvDropTotal, 0);
-
- std::cout << "Starting thread for sending:\n";
- std::thread sender([ss] {
- char buf[1316];
- memset(buf, 1, sizeof(buf));
- int n = 0;
- for (int i = 0; i < 10000; ++i)
- {
- std::this_thread::sleep_for(std::chrono::milliseconds(10));
- if (srt_send(ss, buf, 1316) == -1)
- {
- std::cout << "[Sender] sending failure, exitting after sending " << n << " packets\n";
- break;
- }
-
- ++n;
- }
- });
-
- std::cout << "Will close sending in 300ms...\n";
-
- std::this_thread::sleep_for(std::chrono::milliseconds(300));
-
- EXPECT_EQ(srt_close(ss), 0) << "srt_close: %s\n" << srt_getlasterror_str();
-
- std::cout << "CLOSED GROUP. Now waiting for sender to exit...\n";
- sender.join();
- listen_promise.wait();
-
- srt_cleanup();
-}
-
-#endif // ENABLE_BONDING
+++ /dev/null
-#include <array>
-#include <numeric>
-#include "gtest/gtest.h"
-#include "buffer.h"
-#include "buffer_rcv.h"
-
-using namespace srt;
-using namespace std;
-
-class CRcvBufferReadMsg
- : public ::testing::Test
-{
-protected:
- CRcvBufferReadMsg(bool message_api = true)
- : m_use_message_api(message_api)
- {
- // initialization code here
- }
-
- virtual ~CRcvBufferReadMsg()
- {
- // cleanup any pending stuff, but no exceptions allowed
- }
-
-protected:
- // SetUp() is run immediately before a test starts.
- void SetUp() override
- {
- // make_unique is unfortunatelly C++14
- m_unit_queue = unique_ptr<CUnitQueue>(new CUnitQueue);
- ASSERT_NE(m_unit_queue.get(), nullptr);
- m_unit_queue->init(m_buff_size_pkts, 1500, AF_INET);
-
-#if ENABLE_NEW_RCVBUFFER
- const bool enable_msg_api = m_use_message_api;
- const bool enable_peer_rexmit = true;
- m_rcv_buffer = unique_ptr<CRcvBufferNew>(new CRcvBufferNew(m_init_seqno, m_buff_size_pkts, m_unit_queue.get(), enable_msg_api));
- m_rcv_buffer->setPeerRexmitFlag(enable_peer_rexmit);
-#else
- m_rcv_buffer = unique_ptr<CRcvBuffer>(new CRcvBuffer(m_unit_queue.get(), m_buff_size_pkts));
-#endif
- ASSERT_NE(m_rcv_buffer.get(), nullptr);
- }
-
- void TearDown() override
- {
- // Code here will be called just after the test completes.
- // OK to throw exceptions from here if needed.
- m_rcv_buffer.reset();
- m_unit_queue.reset();
- }
-
-public:
- /// Generate and add one packet to the receiver buffer.
- ///
- /// @returns the result of rcv_buffer::insert(..)
- int addPacket(int seqno, bool pb_first = true, bool pb_last = true, bool out_of_order = false, int ts = 0)
- {
- CUnit* unit = m_unit_queue->getNextAvailUnit();
- EXPECT_NE(unit, nullptr);
-
- CPacket& packet = unit->m_Packet;
- packet.m_iSeqNo = seqno;
- packet.m_iTimeStamp = ts;
-
- packet.setLength(m_payload_sz);
- generatePayload(packet.data(), packet.getLength(), packet.m_iSeqNo);
-
- packet.m_iMsgNo = PacketBoundaryBits(PB_SUBSEQUENT);
- if (pb_first)
- packet.m_iMsgNo |= PacketBoundaryBits(PB_FIRST);
- if (pb_last)
- packet.m_iMsgNo |= PacketBoundaryBits(PB_LAST);
-
- if (!out_of_order)
- {
- packet.m_iMsgNo |= MSGNO_PACKET_INORDER::wrap(1);
- EXPECT_TRUE(packet.getMsgOrderFlag());
- }
-
-#if ENABLE_NEW_RCVBUFFER
- return m_rcv_buffer->insert(unit);
-#else
- const int offset = CSeqNo::seqoff(m_first_unack_seqno, seqno);
- return m_rcv_buffer->addData(unit, offset);
-#endif
- }
-
- /// @returns 0 on success, the result of rcv_buffer::insert(..) once it failed
- int addMessage(size_t msg_len_pkts, int start_seqno, bool out_of_order = false, int ts = 0)
- {
- for (size_t i = 0; i < msg_len_pkts; ++i)
- {
- const bool pb_first = (i == 0);
- const bool pb_last = (i == (msg_len_pkts - 1));
- const int res = addPacket(start_seqno + i, pb_first, pb_last, out_of_order, ts);
-
- if (res != 0)
- return res;
- }
- return 0;
- }
-
- void generatePayload(char* dst, size_t len, int seqno) { std::iota(dst, dst + len, (char)seqno); }
-
- bool verifyPayload(char* dst, size_t len, int seqno)
- {
- // Note. A more consistent way would be to use generatePayload function,
- // but I don't want to create another buffer for the data.
- for (size_t i = 0; i < len; ++i)
- {
- if (dst[i] != static_cast<char>(seqno + i))
- return false;
- }
- return true;
- }
-
- int ackPackets(int num_pkts)
- {
- m_first_unack_seqno = CSeqNo::incseq(m_first_unack_seqno, num_pkts);
-#if ENABLE_NEW_RCVBUFFER
- return 0;
-#else
- return m_rcv_buffer->ackData(num_pkts);
-#endif
- }
-
- int getAvailBufferSize()
- {
-#if ENABLE_NEW_RCVBUFFER
- return m_rcv_buffer->getAvailSize(m_first_unack_seqno);
-#else
- return m_rcv_buffer->getAvailBufSize();
-#endif
- }
-
- int readMessage(char* data, size_t len)
- {
-#if ENABLE_NEW_RCVBUFFER
- return m_rcv_buffer->readMessage(data, len);
-#else
- return m_rcv_buffer->readMsg(data, len);
-#endif
- }
-
- bool hasAvailablePackets()
- {
-#if ENABLE_NEW_RCVBUFFER
- return m_rcv_buffer->hasAvailablePackets();
-#else
- return m_rcv_buffer->isRcvDataAvailable();
-#endif
- }
-
-protected:
- unique_ptr<CUnitQueue> m_unit_queue;
-#if ENABLE_NEW_RCVBUFFER
- unique_ptr<CRcvBufferNew> m_rcv_buffer;
-#else
- unique_ptr<CRcvBuffer> m_rcv_buffer;
-#endif
- const int m_buff_size_pkts = 16;
- const int m_init_seqno = 1000;
- int m_first_unack_seqno = m_init_seqno;
- static const size_t m_payload_sz = 1456;
- const bool m_use_message_api;
-
- const sync::steady_clock::time_point m_tsbpd_base = sync::steady_clock::now(); // now() - HS.timestamp, microseconds
- const sync::steady_clock::duration m_delay = sync::milliseconds_from(200);
-};
-
-// Check the available size of the receiver buffer.
-TEST_F(CRcvBufferReadMsg, Create)
-{
- EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 1);
-}
-
-// Check that destroying the buffer also frees memory units.
-TEST_F(CRcvBufferReadMsg, Destroy)
-{
- EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 1);
- // Add a number of units (packets) to the buffer
- // equal to the buffer size in packets
- for (int i = 0; i < getAvailBufferSize(); ++i)
- EXPECT_EQ(addMessage(1, CSeqNo::incseq(m_init_seqno, i)), 0);
-
- m_rcv_buffer.reset();
- EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity());
-}
-
-// Fill the buffer full, and check adding more data results in an error.
-TEST_F(CRcvBufferReadMsg, FullBuffer)
-{
- auto& rcv_buffer = *m_rcv_buffer.get();
- // Add a number of units (packets) to the buffer
- // equal to the buffer size in packets
- for (int i = 0; i < getAvailBufferSize(); ++i)
- {
- EXPECT_EQ(addMessage(1, CSeqNo::incseq(m_init_seqno, i)), 0);
- }
-
- EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 1); // logic
-
- ackPackets(m_buff_size_pkts - 1);
- EXPECT_EQ(getAvailBufferSize(), 0);
-
- // Try to add more data than the available size of the buffer
- EXPECT_EQ(addPacket(CSeqNo::incseq(m_init_seqno, getAvailBufferSize())), -1);
-
- array<char, m_payload_sz> buff;
- for (int i = 0; i < m_buff_size_pkts - 1; ++i)
- {
- const int res = rcv_buffer.readBuffer(buff.data(), buff.size());
- EXPECT_TRUE(size_t(res) == m_payload_sz);
- EXPECT_TRUE(verifyPayload(buff.data(), res, CSeqNo::incseq(m_init_seqno, i)));
- }
-
- EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity());
-}
-
-// BUG in the old RCV buffer!!!
-// In this test case a packet is added to receiver buffer with offset 1,
-// thus leaving offset 0 with an empty pointer.
-// The buffer says it is not empty, and the data is available
-// to be read, but reading is not possible.
-TEST_F(CRcvBufferReadMsg, OnePacketGap)
-{
- // Add one packet message to to the buffer
- // with a gap of one packet.
- EXPECT_EQ(addMessage(1, CSeqNo::incseq(m_init_seqno)), 0);
-
- auto& rcv_buffer = *m_rcv_buffer.get();
- // Before ACK the available buffer size stays the same.
- EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 1);
- // Not available for reading as not yet acknowledged.
- EXPECT_FALSE(hasAvailablePackets());
- // Confirm reading zero bytes.
- array<char, m_payload_sz> buff;
- int res = readMessage(buff.data(), buff.size());
- EXPECT_EQ(res, 0);
-
- // BUG. Acknowledging an empty position must not result in a read-readiness.
- // TODO: Actually we should not acknowledge, but must drop instead.
- ackPackets(1);
-#if ENABLE_NEW_RCVBUFFER // Expected behavior
- EXPECT_FALSE(hasAvailablePackets());
- EXPECT_FALSE(rcv_buffer.isRcvDataReady());
-
- const auto next_packet = m_rcv_buffer->getFirstValidPacketInfo();
- EXPECT_EQ(next_packet.seqno, m_init_seqno + 1);
-#else // Wrong behavior (BUG)
- EXPECT_TRUE(hasAvailablePackets());
- EXPECT_TRUE(rcv_buffer.isRcvDataReady());
-#endif
-
- EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 2);
-#if ENABLE_NEW_RCVBUFFER
- // The new buffer will return 0 as reading is not available.
- res = rcv_buffer.readBuffer(buff.data(), buff.size());
- EXPECT_EQ(res, 0);
-#else
- cerr << "Expecting IPE from readBuffer(..): \n";
- res = rcv_buffer.readBuffer(buff.data(), buff.size());
- EXPECT_EQ(res, -1);
-#endif
-
- res = readMessage(buff.data(), buff.size());
- EXPECT_EQ(res, 0);
-
-#if ENABLE_NEW_RCVBUFFER
- // Add a missing packet (can't add before an acknowledged position in the old buffer).
- EXPECT_EQ(addMessage(1, m_init_seqno), 0);
-
- for (int pktno = 0; pktno < 2; ++pktno)
- {
- const size_t msg_bytelen = m_payload_sz;
- EXPECT_TRUE(rcv_buffer.isRcvDataReady());
- EXPECT_EQ(readMessage(buff.data(), buff.size()), msg_bytelen);
- EXPECT_TRUE(verifyPayload(buff.data(), msg_bytelen, CSeqNo::incseq(m_init_seqno, pktno)));
- }
- EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity());
-#endif
-
- // Further read is not possible
- EXPECT_FALSE(rcv_buffer.isRcvDataReady());
-}
-
-/// One packet is added to the buffer after 1-packet gap. Should be read only after ACK.
-/// 1. insert (1)
-/// |
-/// +---+---+ ---+---+---+---+ +---+
-/// | 0 | 1 | 0 | 0 | 0 | 0 |...| 0 | m_pUnit[]
-/// +---+---+ ---+---+---+---+ +---+
-/// 2. drop (0)
-/// 2. read (1)
-///
-TEST_F(CRcvBufferReadMsg, OnePacketGapDrop)
-{
- // Add one packet message to to the buffer
- // with a gap of one packet.
- EXPECT_EQ(addMessage(1, CSeqNo::incseq(m_init_seqno)), 0);
- auto& rcv_buffer = *m_rcv_buffer.get();
- EXPECT_FALSE(hasAvailablePackets());
- EXPECT_FALSE(rcv_buffer.isRcvDataReady());
-#if ENABLE_NEW_RCVBUFFER
- rcv_buffer.dropUpTo(CSeqNo::incseq(m_init_seqno));
-#else
- rcv_buffer.dropData(1);
-#endif
-
- EXPECT_TRUE(hasAvailablePackets());
- EXPECT_TRUE(rcv_buffer.isRcvDataReady());
- array<char, m_payload_sz> buff;
- EXPECT_TRUE(readMessage(buff.data(), buff.size()) == m_payload_sz);
- EXPECT_TRUE(verifyPayload(buff.data(), m_payload_sz, CSeqNo::incseq(m_init_seqno)));
- EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity());
-}
-
-// Add one packet to the buffer and read it once it is acknowledged.
-// Confirm the data read is valid.
-// Don't allow to add packet with the same sequence number.
-TEST_F(CRcvBufferReadMsg, OnePacket)
-{
- const size_t msg_pkts = 1;
- // Adding one message without acknowledging
- EXPECT_EQ(addMessage(msg_pkts, m_init_seqno, false), 0);
- // Adding a packet into the same position must return an error.
- EXPECT_EQ(addMessage(msg_pkts, m_init_seqno, false), -1);
-
- const size_t msg_bytelen = msg_pkts * m_payload_sz;
- array<char, 2 * msg_bytelen> buff;
-
- // The new receiver buffer allows reading without ACK.
-#if !ENABLE_NEW_RCVBUFFER
- EXPECT_FALSE(hasAvailablePackets());
-
- const int res1 = readMessage(buff.data(), buff.size());
- EXPECT_EQ(res1, 0);
-
- // Full ACK
- ackPackets(msg_pkts);
-#endif
- EXPECT_TRUE(hasAvailablePackets());
-
- const int res2 = readMessage(buff.data(), buff.size());
- EXPECT_EQ(res2, msg_bytelen);
- EXPECT_TRUE(verifyPayload(buff.data(), res2, m_init_seqno));
- EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity());
-}
-
-// Add ten packets to the buffer, acknowledge and read some of them.
-// Then try to add packets to the position of existing packets.
-// We can't check adding to the position of those packets already read,
-// because a negative offset is not checked by the receiver buffer,
-// but must be handled by the CUDT socket.
-TEST_F(CRcvBufferReadMsg, AddData)
-{
- const int num_pkts = 10;
- ASSERT_LT(num_pkts, m_buff_size_pkts);
- for (int i = 0; i < num_pkts; ++i)
- {
- EXPECT_EQ(addMessage(1, CSeqNo::incseq(m_init_seqno, i)), 0);
- }
-
- // The available buffer size remains the same
- // The value is reported by SRT receiver like this:
- // data[ACKD_BUFFERLEFT] = m_pRcvBuffer->getAvailBufSize();
- EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 1);
-#if ENABLE_NEW_RCVBUFFER
- // The new receiver buffer does not need ACK to allow reading.
- EXPECT_TRUE(hasAvailablePackets());
-#else
- EXPECT_FALSE(hasAvailablePackets());
-#endif
-
- // Now acknowledge two packets
- const int ack_pkts = 2;
- ackPackets(2);
- EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 1 - ack_pkts);
- EXPECT_TRUE(hasAvailablePackets());
-
- std::array<char, m_payload_sz> buff;
- for (int i = 0; i < ack_pkts; ++i)
- {
- const int res = readMessage(buff.data(), buff.size());
- EXPECT_TRUE(size_t(res) == m_payload_sz);
- EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - ack_pkts + i);
- EXPECT_TRUE(verifyPayload(buff.data(), res, CSeqNo::incseq(m_init_seqno, i)));
- }
-
- // Add packet to the position of oackets already read.
- // Can't check the old buffer, as it does not handle a negative offset.
-#if ENABLE_NEW_RCVBUFFER
- EXPECT_EQ(addPacket(m_init_seqno), -2);
-#endif
-
- // Add packet to a non-empty position.
- EXPECT_EQ(addPacket(CSeqNo::incseq(m_init_seqno, ack_pkts)), -1);
-
- const int num_pkts_left = num_pkts - ack_pkts;
- ackPackets(num_pkts_left);
- for (int i = 0; i < num_pkts_left; ++i)
- {
- const int res = readMessage(buff.data(), buff.size());
- EXPECT_TRUE(size_t(res) == m_payload_sz);
- EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - num_pkts_left + i);
- EXPECT_TRUE(verifyPayload(buff.data(), res, CSeqNo::incseq(m_init_seqno, ack_pkts + i)));
- }
- EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity());
-}
-
-// Check reading the whole message (consisting of several packets) from the buffer.
-TEST_F(CRcvBufferReadMsg, MsgAcked)
-{
- const size_t msg_pkts = 4;
- // Adding one message without acknowledging
- addMessage(msg_pkts, m_init_seqno, false);
-
- const size_t msg_bytelen = msg_pkts * m_payload_sz;
- array<char, 2 * msg_bytelen> buff;
-
- // Acknowledge all packets of the message.
- ackPackets(msg_pkts);
- // Now the whole message can be read.
- EXPECT_TRUE(m_rcv_buffer->isRcvDataReady());
- EXPECT_TRUE(hasAvailablePackets());
-
- const int res = readMessage(buff.data(), buff.size());
- EXPECT_EQ(res, msg_bytelen);
- for (size_t i = 0; i < msg_pkts; ++i)
- {
- const ptrdiff_t offset = i * m_payload_sz;
- EXPECT_TRUE(verifyPayload(buff.data() + offset, m_payload_sz, CSeqNo::incseq(m_init_seqno, i)));
- }
- EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity());
-}
-
-// Check reading the whole message (consisting of several packets) into
-// a buffer of an insufficient size.
-TEST_F(CRcvBufferReadMsg, SmallReadBuffer)
-{
- const size_t msg_pkts = 4;
- // Adding one message without acknowledging
- addMessage(msg_pkts, m_init_seqno, false);
-
- const size_t msg_bytelen = msg_pkts * m_payload_sz;
- array<char, 2 * msg_bytelen> buff;
-
- // Acknowledge all packets of the message.
- ackPackets(msg_pkts);
- // Now the whole message can be read.
- EXPECT_TRUE(m_rcv_buffer->isRcvDataReady());
- EXPECT_TRUE(hasAvailablePackets());
-
- // Check reading into an insufficient size buffer.
- // The old buffer extracts the whole message, but copies only
- // the number of bytes provided in the 'len' argument.
- const int res = readMessage(buff.data(), m_payload_sz);
- EXPECT_EQ(res, 1456);
-
- // No more messages to read
- EXPECT_FALSE(m_rcv_buffer->isRcvDataReady());
- EXPECT_FALSE(hasAvailablePackets());
- EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 1);
- EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity());
-}
-
-// BUG!!!
-// Checks signaling of read-readiness of a half-acknowledged message.
-// The RCV buffer implementation has an issue here: when only half of the message is
-// acknowledged, the RCV buffer signals read-readiness, even though
-// the message can't be read, and reading returns 0.
-TEST_F(CRcvBufferReadMsg, MsgHalfAck)
-{
- const size_t msg_pkts = 4;
- // Adding one message without acknowledging
- addMessage(msg_pkts, m_init_seqno, false);
-
- // Nothing to read (0 for zero bytes read).
- const size_t msg_bytelen = msg_pkts * m_payload_sz;
- array<char, 2 * msg_bytelen> buff;
-#if ENABLE_NEW_RCVBUFFER
- // The new receiver buffer does not care about ACK.
- EXPECT_TRUE(m_rcv_buffer->isRcvDataReady());
- EXPECT_TRUE(hasAvailablePackets());
-
- const int res = readMessage(buff.data(), buff.size());
- EXPECT_EQ(res, msg_bytelen);
- for (size_t i = 0; i < msg_pkts; ++i)
- {
- const ptrdiff_t offset = i * m_payload_sz;
- EXPECT_TRUE(verifyPayload(buff.data() + offset, m_payload_sz, CSeqNo::incseq(m_init_seqno, i)));
- }
-#else
- EXPECT_FALSE(m_rcv_buffer->isRcvDataReady());
- EXPECT_FALSE(hasAvailablePackets());
- const int res = readMessage(buff.data(), buff.size());
- EXPECT_EQ(res, 0);
-
- // ACK half of the message and check read-readiness.
- ackPackets(2);
- // FIXME: Sadly RCV buffer says the data is ready to be read.
- EXPECT_TRUE(m_rcv_buffer->isRcvDataReady());
- EXPECT_TRUE(hasAvailablePackets());
-
- // Actually must be nothing to read (can't read half a message).
- const int res2 = readMessage(buff.data(), buff.size());
- EXPECT_EQ(res2, 0);
-
- // ACK the remaining half of the message and check read-readiness.
- ackPackets(2);
- const int res3 = readMessage(buff.data(), buff.size());
- EXPECT_EQ(res3, msg_bytelen);
- for (size_t i = 0; i < msg_pkts; ++i)
- {
- const ptrdiff_t offset = i * m_payload_sz;
- EXPECT_TRUE(verifyPayload(buff.data() + offset, m_payload_sz, CSeqNo::incseq(m_init_seqno, i)));
- }
-#endif
- EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity());
-}
-
-// BUG!!!
-// Adding a message with the out-of-order flag set.
-// RCV buffer does not signal read-readiness, but actually the packet can be read.
-TEST_F(CRcvBufferReadMsg, OutOfOrderMsgNoACK)
-{
- const size_t msg_pkts = 4;
- // Adding one message with the Out-Of-Order flag set, but without acknowledging.
- addMessage(msg_pkts, m_init_seqno, true);
-
-#if ENABLE_NEW_RCVBUFFER
- EXPECT_TRUE(m_rcv_buffer->isRcvDataReady());
- EXPECT_TRUE(hasAvailablePackets());
-#else
- EXPECT_FALSE(m_rcv_buffer->isRcvDataReady());
- EXPECT_FALSE(hasAvailablePackets());
-#endif
- const size_t msg_bytelen = msg_pkts * m_payload_sz;
- array<char, 2 * msg_bytelen> buff;
- const int res = readMessage(buff.data(), buff.size());
- EXPECT_EQ(res, msg_bytelen);
- for (size_t i = 0; i < msg_pkts; ++i)
- {
- const ptrdiff_t offset = i * m_payload_sz;
- EXPECT_TRUE(verifyPayload(buff.data() + offset, m_payload_sz, CSeqNo::incseq(m_init_seqno, i)));
- }
-
- EXPECT_FALSE(m_rcv_buffer->isRcvDataReady());
- EXPECT_FALSE(hasAvailablePackets());
-
-#if ENABLE_NEW_RCVBUFFER
- EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity());
-#else
- ackPackets(msg_pkts);
- // The old buffer still does not free units.
- EXPECT_NE(m_unit_queue->size(), m_unit_queue->capacity());
- // BUG: wrong read-ready state.
- EXPECT_TRUE(m_rcv_buffer->isRcvDataReady());
- EXPECT_TRUE(hasAvailablePackets());
- // Nothing read, but empty units are freed.
- EXPECT_EQ(readMessage(buff.data(), buff.size()), 0);
- EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity());
-#endif
-}
-
-// Adding a message with the out-of-order flag set.
-// The message can be read.
-TEST_F(CRcvBufferReadMsg, OutOfOrderMsgGap)
-{
- const size_t msg_pkts = 4;
- // Adding one message with the Out-Of-Order flag set, but without acknowledging.
- addMessage(msg_pkts, CSeqNo::incseq(m_init_seqno, 1), true);
-
-#if ENABLE_NEW_RCVBUFFER
- EXPECT_TRUE(m_rcv_buffer->isRcvDataReady());
- EXPECT_TRUE(hasAvailablePackets());
-#else
- EXPECT_FALSE(m_rcv_buffer->isRcvDataReady());
- EXPECT_FALSE(hasAvailablePackets());
-#endif
- const size_t msg_bytelen = msg_pkts * m_payload_sz;
- array<char, 2 * msg_bytelen> buff;
- const int res = readMessage(buff.data(), buff.size());
- EXPECT_EQ(res, msg_bytelen);
- for (size_t i = 0; i < msg_pkts; ++i)
- {
- const ptrdiff_t offset = i * m_payload_sz;
- EXPECT_TRUE(verifyPayload(buff.data() + offset, m_payload_sz, CSeqNo::incseq(m_init_seqno, 1 + i)));
- }
-
- EXPECT_FALSE(m_rcv_buffer->isRcvDataReady());
- EXPECT_FALSE(hasAvailablePackets());
- // Adding one message with the Out-Of-Order flag set, but without acknowledging.
- //int seqno, bool pb_first = true, bool pb_last = true, bool out_of_order = false, int ts = 0)
- const int res2 = addPacket(CSeqNo::incseq(m_init_seqno, 1));
- EXPECT_EQ(res2, -1); // already exists
-
- EXPECT_EQ(addPacket(m_init_seqno), 0);
- ackPackets(msg_pkts + 1);
- EXPECT_TRUE(m_rcv_buffer->isRcvDataReady());
- EXPECT_TRUE(hasAvailablePackets());
-
- const int res3 = readMessage(buff.data(), buff.size());
- EXPECT_TRUE(res3 == m_payload_sz);
- EXPECT_TRUE(verifyPayload(buff.data(), m_payload_sz, m_init_seqno));
-
- // Only "passack" or EntryState_Read packets remain in the buffer.
- // They are falsely signalled as read-ready.
-#if ENABLE_NEW_RCVBUFFER
- EXPECT_FALSE(m_rcv_buffer->isRcvDataReady());
- EXPECT_FALSE(hasAvailablePackets());
-#else
- EXPECT_TRUE(m_rcv_buffer->isRcvDataReady()); // BUG: nothing to read.
- EXPECT_TRUE(hasAvailablePackets()); // BUG: nothing to read.
-#endif
-
- // Adding a packet right after the EntryState_Read packets.
- const int seqno = CSeqNo::incseq(m_init_seqno, msg_pkts + 1);
- EXPECT_EQ(addPacket(seqno), 0);
- ackPackets(1);
- EXPECT_TRUE(m_rcv_buffer->isRcvDataReady());
- EXPECT_TRUE(hasAvailablePackets());
- EXPECT_TRUE(readMessage(buff.data(), buff.size()) == m_payload_sz);
- EXPECT_TRUE(verifyPayload(buff.data(), m_payload_sz, seqno));
- EXPECT_FALSE(m_rcv_buffer->isRcvDataReady());
- EXPECT_FALSE(hasAvailablePackets());
- EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity());
-}
-
-// One message (4 packets) are added to the buffer.
-// Check if reading is only possible once the whole message is present in the buffer.
-TEST_F(CRcvBufferReadMsg, LongMsgReadReady)
-{
- const size_t msg_pkts = 4;
- const size_t msg_bytelen = msg_pkts * m_payload_sz;
- array<char, 2 * msg_bytelen> buff;
- for (size_t i = 0; i < msg_pkts; ++i)
- {
- // int addPacket(int seqno, bool pb_first = true, bool pb_last = true, bool out_of_order = false, int ts = 0)
- const bool pb_first = (i == 0);
- const bool pb_last = (i == (msg_pkts - 1));
- EXPECT_EQ(addPacket(CSeqNo::incseq(m_init_seqno, i), pb_first, pb_last), 0);
- ackPackets(1);
- if (!pb_last)
- {
-#if ENABLE_NEW_RCVBUFFER
- EXPECT_FALSE(m_rcv_buffer->isRcvDataReady());
- EXPECT_FALSE(hasAvailablePackets());
-#else
- // BUG: The old buffer returns true (read-readiness).
- EXPECT_TRUE(m_rcv_buffer->isRcvDataReady());
- EXPECT_TRUE(hasAvailablePackets());
-#endif
- EXPECT_EQ(readMessage(buff.data(), buff.size()), 0);
- }
- }
-
- // Read the whole message.
- EXPECT_TRUE(m_rcv_buffer->isRcvDataReady());
- EXPECT_TRUE(hasAvailablePackets());
-
- const int res = readMessage(buff.data(), buff.size());
- EXPECT_EQ(res, msg_bytelen);
- for (size_t i = 0; i < msg_pkts; ++i)
- {
- const ptrdiff_t offset = i * m_payload_sz;
- EXPECT_TRUE(verifyPayload(buff.data() + offset, m_payload_sz, CSeqNo::incseq(m_init_seqno, i)));
- }
- EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity());
-}
-
-#if ENABLE_NEW_RCVBUFFER
-// One message (4 packets) is added to the buffer. Can be read out of order.
-// Reading should be possible even before the missing packet is dropped.
-TEST_F(CRcvBufferReadMsg, MsgOutOfOrderDrop)
-{
- const size_t msg_pkts = 4;
- // 1. Add one message (4 packets) without acknowledging
- const int msg_seqno = m_init_seqno + 1; // seqno of the first packet in the message
- EXPECT_EQ(addMessage(msg_pkts, msg_seqno, true), 0);
- EXPECT_TRUE(m_rcv_buffer->isRcvDataReady());
-
- // 2. Read full message after gap.
- const size_t msg_bytelen = msg_pkts * m_payload_sz;
- array<char, 2 * msg_bytelen> buff;
- int res = m_rcv_buffer->readMessage(buff.data(), buff.size());
- EXPECT_EQ(res, msg_bytelen);
- for (size_t i = 0; i < msg_pkts; ++i)
- {
- EXPECT_TRUE(verifyPayload(buff.data() + i * m_payload_sz, m_payload_sz, msg_seqno + i));
- }
-
- EXPECT_FALSE(m_rcv_buffer->isRcvDataReady());
-
- // Can't add to the same message
- EXPECT_EQ(addMessage(msg_pkts, msg_seqno, true), -1);
-
- const auto pkt_info = m_rcv_buffer->getFirstValidPacketInfo();
- EXPECT_EQ(pkt_info.seqno, -1); // Nothing to read
- EXPECT_TRUE(srt::sync::is_zero(pkt_info.tsbpd_time));
-
- // Drop missing packet
- m_rcv_buffer->dropUpTo(msg_seqno);
- EXPECT_FALSE(m_rcv_buffer->isRcvDataReady());
- // All memory units are expected to be freed.
- EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity());
-}
-
-// One message (4 packets) is added to the buffer after a message with "in order" flag.
-// Read in order
-TEST_F(CRcvBufferReadMsg, MsgOutOfOrderAfterInOrder)
-{
- const size_t msg_pkts = 4;
- // 1. Add one packet with inOrder=true and one message (4 packets) with inOrder=false
- EXPECT_EQ(addMessage(msg_pkts, m_init_seqno + 2 * msg_pkts, true), 0);
- EXPECT_EQ(addMessage(msg_pkts, m_init_seqno, false), 0);
- EXPECT_EQ(addMessage(msg_pkts, m_init_seqno + msg_pkts, true), 0);
- EXPECT_TRUE(m_rcv_buffer->isRcvDataReady());
-
- // 2. Read messages in order
- const size_t msg_bytelen = msg_pkts * m_payload_sz;
- std::array<char, 2 * msg_bytelen> buff;
- for (int msg_i = 0; msg_i < 3; ++msg_i)
- {
- EXPECT_TRUE(m_rcv_buffer->isRcvDataReady());
- EXPECT_EQ(m_rcv_buffer->readMessage(buff.data(), buff.size()), msg_bytelen);
- for (size_t i = 0; i < msg_pkts; ++i)
- {
- EXPECT_TRUE(verifyPayload(buff.data() + i * m_payload_sz, m_payload_sz, m_init_seqno + msg_i * msg_pkts + i));
- }
- }
-
- EXPECT_FALSE(m_rcv_buffer->isRcvDataReady());
-}
-
-/// One packet is added to the buffer. Can be read on TSBPD-readiness.
-///
-/// 1. insert
-/// |
-/// +---+ ---+---+---+---+---+ +---+
-/// | 1 | 0 | 0 | 0 | 0 | 0 |...| 0 | m_pUnit[]
-/// +---+ ---+---+---+---+---+ +---+
-/// |
-/// 2. read
-///
-TEST_F(CRcvBufferReadMsg, OnePacketTSBPD)
-{
- const size_t msg_pkts = 1;
-
- m_rcv_buffer->setTsbPdMode(m_tsbpd_base, false, m_delay);
-
- const int packet_ts = 0;
- // Adding one message. Note that all packets have the out of order flag
- // set to false by default in TSBPD mode, but this flag is ignored.
- EXPECT_EQ(addMessage(msg_pkts, m_init_seqno, true, packet_ts), 0);
-
- const size_t msg_bytelen = msg_pkts * m_payload_sz;
- array<char, 2 * msg_bytelen> buff;
-
- // Confirm adding to the same location returns an error.
- EXPECT_EQ(addMessage(msg_pkts, m_init_seqno, true, packet_ts), -1);
-
- // There is one packet in the buffer, but not ready to read after delay/2
- EXPECT_FALSE(m_rcv_buffer->isRcvDataReady(m_tsbpd_base + (m_delay / 2)));
- EXPECT_FALSE(m_rcv_buffer->isRcvDataReady(m_tsbpd_base + m_delay - sync::microseconds_from(1)));
- // There is one packet in the buffer ready to read after delay
- EXPECT_TRUE(m_rcv_buffer->isRcvDataReady(m_tsbpd_base + m_delay));
- EXPECT_TRUE(m_rcv_buffer->isRcvDataReady(m_tsbpd_base + m_delay + sync::microseconds_from(1)));
-
- // Read out the first message
- const int read_len = m_rcv_buffer->readMessage(buff.data(), buff.size());
- EXPECT_EQ(read_len, msg_bytelen);
- EXPECT_TRUE(verifyPayload(buff.data(), read_len, m_init_seqno));
-
- // Check the state after a packet was read
- EXPECT_FALSE(m_rcv_buffer->isRcvDataReady(m_tsbpd_base + m_delay));
- EXPECT_EQ(addMessage(msg_pkts, m_init_seqno, false), -2);
-
- EXPECT_FALSE(m_rcv_buffer->isRcvDataReady(m_tsbpd_base + m_delay));
-}
-
-/// TSBPD = ON, a ready to play packet is preceeded by a missing packet.
-/// The read-rediness must be signalled, and a packet must be read after the missing
-/// one is dropped.
-/// The TSBPD delay is set to 200 ms. This means, that the packet can be played
-/// not earlier than after 200200 microseconds from the peer start time.
-/// The peer start time is set to 100000 us.
-///
-///
-/// |<m_iMaxPosInc>|
-/// | /
-/// | /
-/// | |
-/// +---+---+---+---+---+---+ +---+
-/// | 0 | 1 | 0 | 0 | 0 | 0 |...| 0 | m_pUnit[]
-/// +---+---+---+---+---+---+ +---+
-/// | |
-/// | \__last pkt received
-/// |
-/// \___ m_iStartPos: first message to read
-/// \___ m_iLastAckPos: last ack sent
-///
-/// m_pUnit[i]->m_iFlag: 0:free, 1:good, 2:passack, 3:dropped
-///
-TEST_F(CRcvBufferReadMsg, TSBPDGapBeforeValid)
-{
- m_rcv_buffer->setTsbPdMode(m_tsbpd_base, false, m_delay);
- // Add a solo packet to position m_init_seqno + 1 with timestamp 200 us
- const int seqno = m_init_seqno + 1;
- const int32_t pkt_ts = 200;
- EXPECT_EQ(addMessage(1, seqno, false, pkt_ts), 0);
-
- const auto readready_timestamp = m_tsbpd_base + sync::microseconds_from(pkt_ts) + m_delay;
- // Check that getFirstValidPacketInfo() returns first valid packet.
- const auto pkt_info = m_rcv_buffer->getFirstValidPacketInfo();
- EXPECT_EQ(pkt_info.tsbpd_time, readready_timestamp);
- EXPECT_EQ(pkt_info.seqno, seqno);
- EXPECT_TRUE(pkt_info.seq_gap);
-
- // The packet can't be read because there is a missing packet preceeding.
- EXPECT_FALSE(m_rcv_buffer->isRcvDataReady(readready_timestamp));
-
- const int seq_gap_len = CSeqNo::seqoff(m_rcv_buffer->getStartSeqNo(), pkt_info.seqno);
- EXPECT_GT(seq_gap_len, 0);
- if (seq_gap_len > 0)
- {
- m_rcv_buffer->dropUpTo(pkt_info.seqno);
- }
-
- EXPECT_TRUE(m_rcv_buffer->isRcvDataReady(readready_timestamp));
-
- const size_t msg_bytelen = m_payload_sz;
- array<char, 2 * msg_bytelen> buff;
- EXPECT_EQ(readMessage(buff.data(), buff.size()), msg_bytelen);
- EXPECT_TRUE(verifyPayload(buff.data(), m_payload_sz, seqno));
- EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity());
-}
-
-
-class CRcvBufferReadStream
- : public CRcvBufferReadMsg
-{
-protected:
- CRcvBufferReadStream()
- : CRcvBufferReadMsg(false)
- {}
-
- virtual ~CRcvBufferReadStream() { }
-};
-
-
-// Add ten packets to the buffer in stream mode, read some of them.
-// Try to add packets to occupied positions.
-TEST_F(CRcvBufferReadStream, ReadSinglePackets)
-{
- const int num_pkts = 10;
- ASSERT_LT(num_pkts, m_buff_size_pkts);
- for (int i = 0; i < num_pkts; ++i)
- {
- EXPECT_EQ(addPacket(CSeqNo::incseq(m_init_seqno, i), false, false), 0);
- }
-
- // The available buffer size remains the same
- // The value is reported by SRT receiver like this:
- // data[ACKD_BUFFERLEFT] = m_pRcvBuffer->getAvailBufSize();
- EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 1);
- EXPECT_TRUE(hasAvailablePackets());
-
- // Now acknowledge two packets
- const int ack_pkts = 2;
- ackPackets(2);
- EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 1 - ack_pkts);
- EXPECT_TRUE(hasAvailablePackets());
-
- std::array<char, m_payload_sz> buff;
- for (int i = 0; i < ack_pkts; ++i)
- {
- const size_t res = m_rcv_buffer->readBuffer(buff.data(), buff.size());
- EXPECT_TRUE(size_t(res) == m_payload_sz);
- EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - ack_pkts + i);
- EXPECT_TRUE(verifyPayload(buff.data(), res, CSeqNo::incseq(m_init_seqno, i)));
- }
-
- // Add packet to the position of oackets already read.
- // Can't check the old buffer, as it does not handle a negative offset.
- EXPECT_EQ(addPacket(m_init_seqno), -2);
-
- // Add packet to a non-empty position.
- EXPECT_EQ(addPacket(CSeqNo::incseq(m_init_seqno, ack_pkts)), -1);
-
- const int num_pkts_left = num_pkts - ack_pkts;
- ackPackets(num_pkts_left);
- for (int i = 0; i < num_pkts_left; ++i)
- {
- const int res = m_rcv_buffer->readBuffer(buff.data(), buff.size());
- EXPECT_TRUE(size_t(res) == m_payload_sz);
- EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - num_pkts_left + i);
- EXPECT_TRUE(verifyPayload(buff.data(), res, CSeqNo::incseq(m_init_seqno, ack_pkts + i)));
- }
- EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity());
-}
-
-
-// Add packets to the buffer in stream mode. Read fractional number of packets
-// to confirm a partially read packet stays in the buffer and is read properly afterwards.
-TEST_F(CRcvBufferReadStream, ReadFractional)
-{
- const int num_pkts = 10;
- ASSERT_LT(num_pkts, m_buff_size_pkts);
- for (int i = 0; i < num_pkts; ++i)
- {
- EXPECT_EQ(addPacket(CSeqNo::incseq(m_init_seqno, i), false, false), 0);
- }
-
- // The available buffer size remains the same
- // The value is reported by SRT receiver like this:
- // data[ACKD_BUFFERLEFT] = m_pRcvBuffer->getAvailBufSize();
- EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 1);
- EXPECT_TRUE(hasAvailablePackets());
-
- array<char, m_payload_sz * num_pkts> buff;
-
- const size_t nfull_pkts = 2;
- const size_t num_bytes1 = nfull_pkts * m_payload_sz + m_payload_sz / 2;
- const int res1 = m_rcv_buffer->readBuffer(buff.data(), num_bytes1);
- EXPECT_TRUE(size_t(res1) == num_bytes1);
- EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - nfull_pkts - 1);
- EXPECT_TRUE(hasAvailablePackets());
-
- const size_t num_bytes2 = m_payload_sz * (num_pkts - nfull_pkts - 1) + m_payload_sz / 2;
-
- const int res2 = m_rcv_buffer->readBuffer(buff.data() + num_bytes1, buff.size() - num_bytes1);
- EXPECT_TRUE(size_t(res2) == num_bytes2);
- EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - num_pkts - 1);
- EXPECT_FALSE(hasAvailablePackets());
- ackPackets(num_pkts); // Move the reference ACK position.
- EXPECT_EQ(getAvailBufferSize(), m_buff_size_pkts - 1);
-
- for (int i = 0; i < num_pkts; ++i)
- {
- EXPECT_TRUE(verifyPayload(buff.data() + i * m_payload_sz, m_payload_sz, CSeqNo::incseq(m_init_seqno, i))) << "i = " << i;
- }
-
- EXPECT_EQ(m_unit_queue->size(), m_unit_queue->capacity());
-}
-
-#endif // ENABEL_NEW_RCVBUFFER
+++ /dev/null
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "gtest/gtest.h"
-#include "utilities.h"
-#include "common.h"
-
-using namespace srt;
-
-void test_cipaddress_pton(const char* peer_ip, int family, const uint32_t (&ip)[4])
-{
- const int port = 4200;
-
- // Peer
- sockaddr_storage ss;
- ss.ss_family = family;
-
- void* sin_addr = nullptr;
- if (family == AF_INET)
- {
- sockaddr_in* const sa = (sockaddr_in*)&ss;
- sa->sin_port = htons(port);
- sin_addr = &sa->sin_addr;
- }
- else // IPv6
- {
- sockaddr_in6* const sa = (sockaddr_in6*)&ss;
- sa->sin6_port = htons(port);
- sin_addr = &sa->sin6_addr;
- }
-
- ASSERT_EQ(inet_pton(family, peer_ip, sin_addr), 1);
- const sockaddr_any peer(ss);
-
- // HOST
- sockaddr_any host(family);
- host.hport(port);
-
- srt::CIPAddress::pton(host, ip, peer);
- EXPECT_EQ(peer, host) << "Peer " << peer.str() << " host " << host.str();
-}
-
-// Example IPv4 address: 192.168.0.1
-TEST(CIPAddress, IPv4_pton)
-{
- const char* peer_ip = "192.168.0.1";
- const uint32_t ip[4] = {htobe32(0xC0A80001), 0, 0, 0};
- test_cipaddress_pton(peer_ip, AF_INET, ip);
-}
-
-// Example IPv6 address: 2001:db8:85a3:8d3:1319:8a2e:370:7348
-TEST(CIPAddress, IPv6_pton)
-{
- const char* peer_ip = "2001:db8:85a3:8d3:1319:8a2e:370:7348";
- const uint32_t ip[4] = {htobe32(0x20010db8), htobe32(0x85a308d3), htobe32(0x13198a2e), htobe32(0x03707348)};
-
- test_cipaddress_pton(peer_ip, AF_INET6, ip);
-}
-
-// Example IPv4 address: 192.168.0.1
-// Maps to IPv6 address: 0:0:0:0:0:FFFF:192.168.0.1
-// Simplified: ::FFFF:192.168.0.1
-TEST(CIPAddress, IPv4_in_IPv6_pton)
-{
- const char* peer_ip = "::ffff:192.168.0.1";
- const uint32_t ip[4] = {0, 0, htobe32(0x0000FFFF), htobe32(0xC0A80001)};
-
- test_cipaddress_pton(peer_ip, AF_INET6, ip);
-}
+++ /dev/null
-#include <gtest/gtest.h>
-#include <chrono>
-
-#ifdef _WIN32
-#define INC_SRT_WIN_WINTIME // exclude gettimeofday from srt headers
-#else
-typedef int SOCKET;
-#define INVALID_SOCKET ((SOCKET)-1)
-#define closesocket close
-#endif
-
-#include"platform_sys.h"
-#include "srt.h"
-
-using namespace std;
-
-
-class TestConnectionTimeout
- : public ::testing::Test
-{
-protected:
- TestConnectionTimeout()
- {
- // initialization code here
- }
-
- ~TestConnectionTimeout()
- {
- // cleanup any pending stuff, but no exceptions allowed
- }
-
-protected:
-
- // SetUp() is run immediately before a test starts.
- void SetUp() override
- {
- ASSERT_EQ(srt_startup(), 0);
-
- m_sa.sin_family = AF_INET;
- m_sa.sin_addr.s_addr = INADDR_ANY;
- m_udp_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
- ASSERT_NE(m_udp_sock, -1);
-
- // Find unused a port not used by any other service.
- // Otherwise srt_connect may actually connect.
- int bind_res = -1;
- const sockaddr* psa = reinterpret_cast<const sockaddr*>(&m_sa);
- for (int port = 5000; port <= 5555; ++port)
- {
- m_sa.sin_port = htons(port);
- bind_res = ::bind(m_udp_sock, psa, sizeof m_sa);
- if (bind_res >= 0)
- {
- cerr << "Running test on port " << port << "\n";
- break;
- }
- }
-
- ASSERT_GE(bind_res, 0);
- ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &m_sa.sin_addr), 1);
- }
-
- void TearDown() override
- {
- // Code here will be called just after the test completes.
- // OK to throw exceptions from here if needed.
- ASSERT_NE(closesocket(m_udp_sock), -1);
- srt_cleanup();
- }
-
-protected:
-
- SOCKET m_udp_sock = INVALID_SOCKET;
- sockaddr_in m_sa = sockaddr_in();
-
-};
-
-
-
-/**
- * The test creates a socket and tries to connect to a localhost port 5555
- * in a non-blocking mode. This means we wait on epoll for a notification
- * about SRT_EPOLL_OUT | SRT_EPOLL_ERR events on the socket calling srt_epoll_wait(...).
- * The test expects a connection timeout to happen within the time,
- * set with SRTO_CONNTIMEO (500 ms).
- * The expected behavior is to return from srt_epoll_wait(...)
- *
- * @remarks Inspired by Max Tomilov (maxtomilov) in issue #468
-*/
-TEST_F(TestConnectionTimeout, Nonblocking) {
-
- const SRTSOCKET client_sock = srt_create_socket();
- ASSERT_GT(client_sock, 0); // socket_id should be > 0
-
- // First let's check the default connection timeout value.
- // It should be 3 seconds (3000 ms)
- int conn_timeout = 0;
- int conn_timeout_len = sizeof conn_timeout;
- EXPECT_EQ(srt_getsockopt(client_sock, 0, SRTO_CONNTIMEO, &conn_timeout, &conn_timeout_len), SRT_SUCCESS);
- EXPECT_EQ(conn_timeout, 3000);
-
- // Set connection timeout to 500 ms to reduce the test execution time
- const int connection_timeout_ms = 300;
- EXPECT_EQ(srt_setsockopt(client_sock, 0, SRTO_CONNTIMEO, &connection_timeout_ms, sizeof connection_timeout_ms), SRT_SUCCESS);
-
- const int yes = 1;
- const int no = 0;
- ASSERT_EQ(srt_setsockopt(client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_SUCCESS); // for async connect
- ASSERT_EQ(srt_setsockopt(client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_SUCCESS); // for async connect
- ASSERT_EQ(srt_setsockopt(client_sock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_SUCCESS);
- ASSERT_EQ(srt_setsockflag(client_sock, SRTO_SENDER, &yes, sizeof yes), SRT_SUCCESS);
-
- const int pollid = srt_epoll_create();
- ASSERT_GE(pollid, 0);
- const int epoll_out = SRT_EPOLL_OUT | SRT_EPOLL_ERR;
- ASSERT_NE(srt_epoll_add_usock(pollid, client_sock, &epoll_out), SRT_ERROR);
-
- const sockaddr* psa = reinterpret_cast<const sockaddr*>(&m_sa);
- ASSERT_NE(srt_connect(client_sock, psa, sizeof m_sa), SRT_ERROR);
-
- // Socket readiness for connection is checked by polling on WRITE allowed sockets.
- {
- int rlen = 2;
- SRTSOCKET read[2];
-
- int wlen = 2;
- SRTSOCKET write[2];
-
- const chrono::steady_clock::time_point chrono_ts_start = chrono::steady_clock::now();
-
- // Here we check the connection timeout.
- // Epoll timeout is set 100 ms greater than socket's TTL
- EXPECT_EQ(srt_epoll_wait(pollid, read, &rlen,
- write, &wlen,
- connection_timeout_ms + 100, // +100 ms
- 0, 0, 0, 0)
- /* Expected return value is 2. We have only 1 socket, but
- * sockets with exceptions are returned to both read and write sets.
- */
- , 2);
- // Check the actual timeout
- const chrono::steady_clock::time_point chrono_ts_end = chrono::steady_clock::now();
- const auto delta_ms = chrono::duration_cast<chrono::milliseconds>(chrono_ts_end - chrono_ts_start).count();
- // Confidence interval border : +/-80 ms
- EXPECT_LE(delta_ms, connection_timeout_ms + 80) << "Timeout was: " << delta_ms;
- EXPECT_GE(delta_ms, connection_timeout_ms - 80) << "Timeout was: " << delta_ms;
-
- EXPECT_EQ(rlen, 1);
- EXPECT_EQ(read[0], client_sock);
- EXPECT_EQ(wlen, 1);
- EXPECT_EQ(write[0], client_sock);
- }
-
- EXPECT_EQ(srt_epoll_remove_usock(pollid, client_sock), SRT_SUCCESS);
- EXPECT_EQ(srt_close(client_sock), SRT_SUCCESS);
- (void)srt_epoll_release(pollid);
-}
-
-/**
- * The test creates a socket and tries to connect to a localhost port 5555
- * in a blocking mode. The srt_connect function is expected to return
- * SRT_ERROR, and the error_code should be SRT_ENOSERVER, meaning a
- * connection timeout.
- * This test is a regression test for an issue described in PR #833.
- * Under certain conditions m_bConnecting variable on a socket
- * might not be reset to false after a connection attempt has failed.
- * In that case any further call to srt_connect will return SRT_ECONNSOCK:
- * Operation not supported: Cannot do this operation on a CONNECTED socket
- *
-*/
-TEST_F(TestConnectionTimeout, BlockingLoop)
-{
- const SRTSOCKET client_sock = srt_create_socket();
- ASSERT_GT(client_sock, 0); // socket_id should be > 0
-
- // Set connection timeout to 999 ms to reduce the test execution time.
- // Also need to hit a time point between two threads:
- // srt_connect will check TTL every second,
- // CRcvQueue::worker will wait on a socket for 10 ms.
- // Need to have a condition, when srt_connect will process the timeout.
- const int connection_timeout_ms = 999;
- EXPECT_EQ(srt_setsockopt(client_sock, 0, SRTO_CONNTIMEO, &connection_timeout_ms, sizeof connection_timeout_ms), SRT_SUCCESS);
-
- const sockaddr* psa = reinterpret_cast<const sockaddr*>(&m_sa);
- for (int i = 0; i < 10; ++i)
- {
- const chrono::steady_clock::time_point chrono_ts_start = chrono::steady_clock::now();
- EXPECT_EQ(srt_connect(client_sock, psa, sizeof m_sa), SRT_ERROR);
-
- const auto delta_ms = chrono::duration_cast<chrono::milliseconds>(chrono::steady_clock::now() - chrono_ts_start).count();
- // Confidence interval border : +/-200 ms
- EXPECT_LE(delta_ms, connection_timeout_ms + 200) << "Timeout was: " << delta_ms;
- EXPECT_GE(delta_ms, connection_timeout_ms - 200) << "Timeout was: " << delta_ms;
-
- const int error_code = srt_getlasterror(nullptr);
- EXPECT_EQ(error_code, SRT_ENOSERVER);
- if (error_code != SRT_ENOSERVER)
- {
- cerr << "Connection attempt no. " << i << " resulted with: "
- << error_code << " " << srt_getlasterror_str() << "\n";
- break;
- }
- }
-
- EXPECT_EQ(srt_close(client_sock), SRT_SUCCESS);
-}
-
-
+++ /dev/null
-#include <stdio.h>
-#include <string.h>
-#include "gtest/gtest.h"
-
-#ifdef SRT_ENABLE_ENCRYPTION
-#include "common.h"
-#include "hcrypt.h"
-#include "version.h"
-
-
-#if (CRYSPR_VERSION_NUMBER >= 0x010100)
-#define WITH_FIPSMODE 1 /* 1: openssl-evp branch */
-#endif
-
-#define UT_PKT_MAXLEN 1500
-
-const void *nullPtr = NULL;
-
-/* TestCRYSPRmethods: Test presense of required cryspr methods */
-
-class TestCRYSPRmethods
- : public ::testing::Test
-{
-protected:
- TestCRYSPRmethods()
- {
- // initialization code here
- cryspr_m = NULL;
- cryspr_fbm = NULL;
- }
-
- ~TestCRYSPRmethods()
- {
- // cleanup any pending stuff, but no exceptions allowed
- }
-
- // SetUp() is run immediately before a test starts.
-#if defined(__GNUC__) && (__GNUC___ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))
-// override only supported for GCC>=4.7
- void SetUp() override {
-#else
- void SetUp() {
-#endif
- cryspr_m = cryspr4SRT();
- cryspr_fbm = crysprInit(&cryspr_fb);
-
- ASSERT_NE(cryspr_m, nullPtr);
- ASSERT_NE(cryspr_fbm, nullPtr);
- ASSERT_EQ(cryspr_fbm, &cryspr_fb);
- }
-
-#if defined(__GNUC__) && (__GNUC___ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))
- void TearDown() override {
-#else
- void TearDown() {
-#endif
- // Code here will be called just after the test completes.
- // OK to throw exceptions from here if needed.
- }
-
-protected:
-
- CRYSPR_methods *cryspr_m; /* methods */
- CRYSPR_methods cryspr_fb, *cryspr_fbm; /* fall back methods */
-// CRYSPR_cb *cryspr_cb; /* Control block */
-};
-
-
-TEST_F(TestCRYSPRmethods, MethodOpen)
-{
- EXPECT_NE(cryspr_m, nullPtr);
- EXPECT_NE(cryspr_m->open, nullPtr);
-}
-
-
-TEST_F(TestCRYSPRmethods, init)
-{
- ASSERT_NE(cryspr_m, nullPtr);
-}
-
-#if WITH_FIPSMODE
-TEST_F(TestCRYSPRmethods, fipsmode)
-{
- if(cryspr_m->fips_mode_set == NULL || cryspr_m->fips_mode_set == cryspr_fbm->fips_mode_set ) {
-#if defined(CRYSPR_FIPSMODE) //undef: not supported, 0: supported and Off by default, 1: enabled by default
- EXPECT_NE(cryspr_m->fips_mode_set, cryspr_fbm->fips_mode_set); //Fallback method cannot set FIPS mode
- EXPECT_EQ(cryspr_m->fips_mode_set(CRYSPR_FIPSMODE ? 0 : 1), CRYSPR_FIPSMODE);
- EXPECT_EQ(cryspr_m->fips_mode_set(CRYSPR_FIPSMODE), (CRYSPR_FIPSMODE? 0 : 1));
-#endif /* CRYSPR_FIPSMODE */
- }
-}
-#endif /* WITH_FIPSMODE */
-
-TEST_F(TestCRYSPRmethods, open)
-{
- EXPECT_NE(cryspr_m->open, nullPtr);
-}
-
-TEST_F(TestCRYSPRmethods, close)
-{
- EXPECT_NE(cryspr_m->close, nullPtr);
-}
-
-TEST_F(TestCRYSPRmethods, prng)
-{
- EXPECT_NE(cryspr_m->prng, nullPtr);
-}
-
-TEST_F(TestCRYSPRmethods, aes_set_key)
-{
- EXPECT_NE(cryspr_m->aes_set_key, nullPtr);
-}
-
-TEST_F(TestCRYSPRmethods, AESecb)
-{
- if(cryspr_m->km_wrap == cryspr_fbm->km_wrap) {
- /* fallback KM_WRAP method used
- * AES-ECB method then required
- */
- EXPECT_NE(cryspr_m->aes_ecb_cipher, nullPtr);
- EXPECT_NE(cryspr_m->aes_ecb_cipher, cryspr_fbm->aes_ecb_cipher);
- }
-}
-TEST_F(TestCRYSPRmethods, AESctr)
-{
- EXPECT_NE(cryspr_m->aes_ctr_cipher, nullPtr);
-}
-
-TEST_F(TestCRYSPRmethods, SHA1)
-{
- if(cryspr_m->sha1_msg_digest == NULL || cryspr_m->km_pbkdf2 == cryspr_fbm->km_pbkdf2 ) {
- /* fallback PBKDF2 used
- * then sha1 method required.
- */
- EXPECT_NE(cryspr_m->sha1_msg_digest, nullPtr);
- EXPECT_NE(cryspr_m->sha1_msg_digest, cryspr_fbm->sha1_msg_digest);
- }
-}
-
-
-/* CRYSPR control block test */
-class TestCRYSPRcypto
- : public ::testing::Test
-{
-protected:
- TestCRYSPRcypto()
- {
- // initialization code here
- cryspr_m = NULL;
- cryspr_fbm = NULL;
- cryspr_cb = NULL;
- }
-
- ~TestCRYSPRcypto()
- {
- // cleanup any pending stuff, but no exceptions allowed
- }
-
- // SetUp() is run immediately before a test starts.
-#if defined(__GNUC__) && (__GNUC___ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))
-// override only supported for GCC>=4.7
- void SetUp() override {
-#else
- void SetUp() {
-#endif
- cryspr_m = cryspr4SRT();
- cryspr_fbm = crysprInit(&cryspr_fb);
-
- ASSERT_NE(cryspr_m, nullPtr);
- ASSERT_NE(cryspr_fbm, nullPtr);
- ASSERT_EQ(cryspr_fbm, &cryspr_fb);
- cryspr_cb = cryspr_m->open(cryspr_m, UT_PKT_MAXLEN);
- ASSERT_NE(cryspr_cb, nullPtr);
- }
-
-#if defined(__GNUC__) && (__GNUC___ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))
- void TearDown() override {
-#else
- void TearDown() {
-#endif
- // Code here will be called just after the test completes.
- // OK to throw exceptions from here if needed.
- if (cryspr_m && cryspr_cb) {
- EXPECT_EQ(cryspr_m->close(cryspr_cb), 0);
- }
- }
-
-protected:
-
- CRYSPR_methods *cryspr_m; /* methods */
- CRYSPR_methods cryspr_fb, *cryspr_fbm; /* fall back methods */
- CRYSPR_cb *cryspr_cb; /* Control block */
-};
-
-TEST_F(TestCRYSPRcypto, CtrlBlock)
-{
- EXPECT_EQ(cryspr_m, cryspr_cb->cryspr); //methods set in control block
-}
-
-/*PBKDF2-----------------------------------------------------------------------------------------*/
-
-/* See https://asecuritysite.com/encryption/PBKDF2z
- to generate "known good" PBKDF2 hash
-*/
-/* Test Vector 1.1 to 1.3 */
-
-struct UTVcryspr_pbkdf2 {
- const char *name;
- const char *passwd;
- const char *salt;
- int itr;
- size_t keklen;
- unsigned char kek[256/8];
-};
-
-/* PBKDF2 test vectors */
-struct UTVcryspr_pbkdf2 pbkdf2_tv[] = {
- {//[0]
- /* testname */ "PBKDF2 tv1.128",
- /* passwd */ "000000000000",
- /* salt */ "00000000",
- /* iteration */ 2048,
- /* keklen */ 128/8,
- /* kek */ {0xb6,0xbf,0x5f,0x0c,0xdd,0x25,0xe8,0x58,0x23,0xfd,0x84,0x7a,0xb2,0xb6,0x7f,0x79}
- },
- {//[1]
- /* testname */ "PBKDF2 tv1.192",
- /* passwd */ "000000000000",
- /* salt */ "00000000",
- /* iteration */ 2048,
- /* keklen */ 192/8,
- /* kek */ {0xb6,0xbf,0x5f,0x0c,0xdd,0x25,0xe8,0x58,0x23,0xfd,0x84,0x7a,0xb2,0xb6,0x7f,0x79,
- 0x90,0xab,0xca,0x6e,0xf0,0x02,0xf1,0xad}
- },
- {//[2]
- /* testname */ "PBKDF2 tv1.256",
- /* passwd */ "000000000000",
- /* salt */ "00000000",
- /* iteration */ 2048,
- /* keklen */ 256/8,
- /* kek */ {0xb6,0xbf,0x5f,0x0c,0xdd,0x25,0xe8,0x58,0x23,0xfd,0x84,0x7a,0xb2,0xb6,0x7f,0x79,
- 0x90,0xab,0xca,0x6e,0xf0,0x02,0xf1,0xad,0x19,0x59,0xcf,0x18,0xac,0x91,0x53,0x3d}
- },
- {//[3]
- /* testname */ "PBKDF2 tv2.1",
- /* passwd */ "password",
- /* salt */ "salt",
- /* iteration */ 1,
- /* keklen */ 20,
- /* kek */ {0x0c,0x60,0xc8,0x0f,0x96,0x1f,0x0e,0x71,0xf3,0xa9,0xb5,0x24,0xaf,0x60,0x12,0x06,
- 0x2f,0xe0,0x37,0xa6}
- },
- {//[4]
- /* testname */ "PBKDF2 tv2.20",
- /* passwd */ "password",
- /* salt */ "salt",
- /* iteration */ 2,
- /* keklen */ 20,
- /* kek */ {0xea,0x6c,0x01,0x4d,0xc7,0x2d,0x6f,0x8c,0xcd,0x1e,0xd9,0x2a,0xce,0x1d,0x41,0xf0,
- 0xd8,0xde,0x89,0x57}
- },
- {//[5]
- /* testname */ "PBKDF2 tv2.4096",
- /* passwd */ "password",
- /* salt */ "salt",
- /* iteration */ 4096,
- /* keklen */ 20,
- /* kek */ {0x4b,0x00,0x79,0x01,0xb7,0x65,0x48,0x9a,0xbe,0xad,0x49,0xd9,0x26,0xf7,0x21,0xd0,
- 0x65,0xa4,0x29,0xc1}
- },
- {//[6]
- /* testname */ "PBKDF2 tv3.0",
- /* passwd */ "passwordPASSWORDpassword",
- /* salt */ "saltSALTsaltSALTsaltSALTsaltSALTsalt",
- /* iteration */ 4096,
- /* keklen */ 25,
- /* kek */ {0x3d,0x2e,0xec,0x4f,0xe4,0x1c,0x84,0x9b,0x80,0xc8,0xd8,0x36,0x62,0xc0,0xe4,0x4a,
- 0x8b,0x29,0x1a,0x96,0x4c,0xf2,0xf0,0x70,0x38}
- },
-};
-
-void test_pbkdf2(
- CRYSPR_methods *cryspr_m,
- CRYSPR_cb *cryspr_cb,
- size_t tvi) //test vector index
-{
- unsigned char kek[256/8];
-
- if(tvi < sizeof(pbkdf2_tv)/sizeof(pbkdf2_tv[0])) {
- struct UTVcryspr_pbkdf2 *tv = &pbkdf2_tv[tvi];
-
- ASSERT_NE(cryspr_m->km_pbkdf2, nullPtr);
-
- cryspr_m->km_pbkdf2(
- cryspr_cb,
- (char *)tv->passwd, /* passphrase */
- strnlen(tv->passwd, 80), /* passphrase len */
- (unsigned char *)tv->salt, /* salt */
- strnlen(tv->salt, 80), /* salt_len */
- tv->itr, /* iterations */
- tv->keklen, /* desired key len {(}16,24,32}*/
- kek); /* derived key */
-
- EXPECT_EQ(memcmp(kek, tv->kek, tv->keklen),0);
- }
-}
-
-
-TEST_F(TestCRYSPRcypto, PBKDF2_tv1_k128)
-{
- test_pbkdf2(cryspr_m, cryspr_cb, 0);
-}
-
-TEST_F(TestCRYSPRcypto, PBKDF2_tv1_k192)
-{
- test_pbkdf2(cryspr_m, cryspr_cb, 1);
-}
-
-TEST_F(TestCRYSPRcypto, PBKDF2_tv1_k256)
-{
- test_pbkdf2(cryspr_m, cryspr_cb, 2);
-}
-
-TEST_F(TestCRYSPRcypto, PBKDF2_tv2_i1)
-{
- test_pbkdf2(cryspr_m, cryspr_cb, 3);
-}
-
-TEST_F(TestCRYSPRcypto, PBKDF2_tv2_i20)
-{
- test_pbkdf2(cryspr_m, cryspr_cb, 4);
-}
-
-TEST_F(TestCRYSPRcypto, PBKDF2_tv2_i4096)
-{
- test_pbkdf2(cryspr_m, cryspr_cb, 5);
-}
-
-TEST_F(TestCRYSPRcypto, PBKDF2_tv3_0)
-{
- test_pbkdf2(cryspr_m, cryspr_cb, 6);
-}
-
-/*AES KeyWrap -----------------------------------------------------------------------------------*/
-
-struct UTVcryspr_km_wrap {
- const char *name;
- unsigned char sek[256/8]; /* key to wrap (unwrap result)*/
- size_t seklen;
- unsigned char kek[256/8];
- unsigned char wrap[8+256/8]; /* wrapped sek (wrap result) */
-};
-
-/* KMWRAP/KMUNWRAP test vectors */
-struct UTVcryspr_km_wrap UTV_cryspr_km_wrap[] = {
- {//[0]
- /*name */ "tv1.128",
- /* sek */ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
- /* seklen */ 128/8,
- /* kek */ {0xb6,0xbf,0x5f,0x0c,0xdd,0x25,0xe8,0x58,0x23,0xfd,0x84,0x7a,0xb2,0xb6,0x7f,0x79},
- /* wrap */ {0xF8,0xB6,0x12,0x1B,0xF2,0x03,0x62,0x40,0x80,0x32,0x60,0x8D,0xED,0x0B,0x8E,0x4B,
- 0x29,0x7E,0x80,0x17,0x4E,0x89,0x68,0xF1}
- },
- {//[1]
- /*name */ "tv1.192",
- /* sek */ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
- /* seklen */ 192/8,
- /* kek */ {0xb6,0xbf,0x5f,0x0c,0xdd,0x25,0xe8,0x58,0x23,0xfd,0x84,0x7a,0xb2,0xb6,0x7f,0x79,
- 0x90,0xab,0xca,0x6e,0xf0,0x02,0xf1,0xad},
- /* wrap */ {0xC1,0xA6,0x58,0x9E,0xC0,0x52,0x6D,0x37,0x84,0x3C,0xBD,0x3B,0x02,0xDD,0x79,0x3F,
- 0xE6,0x14,0x2D,0x81,0x69,0x4B,0x8E,0x07,0x26,0x4F,0xCD,0x86,0xD6,0x6A,0x70,0x62},
- },
- {//[2]
- /*name */ "tv1.256",
- /* sek */ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
- /* seklen */ 256/8,
- /* kek */ {0xb6,0xbf,0x5f,0x0c,0xdd,0x25,0xe8,0x58,0x23,0xfd,0x84,0x7a,0xb2,0xb6,0x7f,0x79,
- 0x90,0xab,0xca,0x6e,0xf0,0x02,0xf1,0xad,0x19,0x59,0xcf,0x18,0xac,0x91,0x53,0x3d},
- /* wrap */ {0x94,0xBE,0x9C,0xA6,0x7A,0x27,0x20,0x56,0xED,0xEA,0xA0,0x8F,0x71,0xB1,0xF1,0x85,
- 0xF6,0xC5,0x67,0xF4,0xA9,0xC2,0x1E,0x78,0x49,0x36,0xA5,0xAE,0x60,0xD0,0x1C,0x30,
- 0x68,0x27,0x4F,0x66,0x56,0x5A,0x55,0xAA},
- },
-};
-
-void test_kmwrap(
- CRYSPR_methods *cryspr_m,
- CRYSPR_cb *cryspr_cb,
- size_t tvi) //Test vector index
-{
- unsigned char wrap[HAICRYPT_WRAPKEY_SIGN_SZ+256/8];
- int rc1,rc2;
-
- if (tvi < sizeof(UTV_cryspr_km_wrap)/sizeof(UTV_cryspr_km_wrap[0]))
- {
- struct UTVcryspr_km_wrap *tv = &UTV_cryspr_km_wrap[tvi];
- size_t wraplen=HAICRYPT_WRAPKEY_SIGN_SZ+tv->seklen;
-
- if(cryspr_m && cryspr_cb) {
- ASSERT_NE(cryspr_m->km_setkey, nullPtr);
- ASSERT_NE(cryspr_m->km_wrap, nullPtr);
-
- rc1 = cryspr_m->km_setkey(
- cryspr_cb,
- true, //Wrap
- tv->kek,
- tv->seklen);
-
- rc2 = cryspr_m->km_wrap(
- cryspr_cb,
- wrap,
- tv->sek,
- tv->seklen);
-
- ASSERT_EQ(rc1, 0);
- ASSERT_EQ(rc2, 0);
- EXPECT_EQ(memcmp(tv->wrap, wrap, wraplen), 0);
- }
- }
-}
-
-void test_kmunwrap(
- CRYSPR_methods *cryspr_m,
- CRYSPR_cb *cryspr_cb,
- size_t tvi) //Test vector index
-{
- unsigned char sek[256/8];
- int rc1,rc2;
-
- if(tvi < sizeof(UTV_cryspr_km_wrap)/sizeof(UTV_cryspr_km_wrap[0]))
- {
- struct UTVcryspr_km_wrap *tv = &UTV_cryspr_km_wrap[tvi];
- size_t wraplen=HAICRYPT_WRAPKEY_SIGN_SZ+tv->seklen;
-
- if(cryspr_m && cryspr_cb) {
- ASSERT_NE(cryspr_m->km_setkey, nullPtr);
- ASSERT_NE(cryspr_m->km_unwrap, nullPtr);
-
- rc1 = cryspr_m->km_setkey(
- cryspr_cb,
- false, //Unwrap
- tv->kek,
- tv->seklen);
-
- rc2 = cryspr_m->km_unwrap(
- cryspr_cb,
- sek,
- tv->wrap,
- wraplen);
-
- ASSERT_EQ(rc1, 0);
- ASSERT_EQ(rc2, 0);
- EXPECT_EQ(memcmp(tv->sek, sek, tv->seklen), 0);
- }
- }
-}
-
-
-TEST_F(TestCRYSPRcypto, KMWRAP_tv1_k128)
-{
- test_kmwrap(cryspr_m, cryspr_cb, 0);
-}
-TEST_F(TestCRYSPRcypto, KMWRAP_tv1_k192)
-{
- test_kmwrap(cryspr_m, cryspr_cb, 1);
-}
-TEST_F(TestCRYSPRcypto, KMWRAP_tv1_k256)
-{
- test_kmwrap(cryspr_m, cryspr_cb, 2);
-}
-
-TEST_F(TestCRYSPRcypto, KMUNWRAP_tv1_k128)
-{
- test_kmunwrap(cryspr_m, cryspr_cb, 0);
-}
-TEST_F(TestCRYSPRcypto, KMUNWRAP_tv1_k192)
-{
- test_kmunwrap(cryspr_m, cryspr_cb, 1);
-}
-TEST_F(TestCRYSPRcypto, KMUNWRAP_tv1_k256)
-{
- test_kmunwrap(cryspr_m, cryspr_cb, 2);
-}
-
-/*AES ECB -----------------------------------------------------------------------------------*/
-#if !(CRYSPR_HAS_AESCTR && CRYSPR_HAS_AESKWRAP)
-/* AES-ECB test vectors */
-struct UTVcryspr_aes_ecb {
- const char *name;
- unsigned char sek[256/8]; /* Stream Encrypting Key*/
- size_t seklen;
- const char *cleartxt; /* clear text (decrypt result0 */
- unsigned char ciphertxt[32]; /* cipher text (encrypt result) */
- size_t outlen;
-};
-
-struct UTVcryspr_aes_ecb UTV_cryspr_aes_ecb[] = {
- {//[0]
- /*name */ "tv1.128",
- /* sek */ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
- /* seklen */ 128/8,
- /* cleartxt */ "0000000000000000",
- /* ciphertxt */ {0xE0,0x86,0x82,0xBE,0x5F,0x2B,0x18,0xA6,0xE8,0x43,0x7A,0x15,0xB1,0x10,0xD4,0x18},
- /* cipherlen */ 16,
- },
- {//[1]
- /*name */ "tv1.192",
- /* sek */ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
- /* seklen */ 192/8,
- /* cleartxt */ "0000000000000000",
- /* ciphertxt */ {0xCC,0xFE,0xD9,0x9E,0x38,0xE9,0x60,0xF5,0xD7,0xE1,0xC5,0x9F,0x56,0x3A,0x49,0x9D},
- /* cipherlen */ 16,
- },
- {//[2]
- /*name */ "tv1.256",
- /* sek */ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
- /* seklen */ 256/8,
- /* cleartxt */ "0000000000000000",
- /* ciphertxt */ {0x94,0xB1,0x3A,0x9F,0x4C,0x09,0xD4,0xD7,0x00,0x2C,0x3F,0x11,0x7D,0xB1,0x7C,0x8B},
- /* cipherlen */ 16,
- },
- {//[3]
- /*name */ "tv2.128",
- /* sek */ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
- /* seklen */ 128/8,
- /* cleartxt */ "00000000000000000000000000000000",
- /* ciphertxt */ {0xE0,0x86,0x82,0xBE,0x5F,0x2B,0x18,0xA6,0xE8,0x43,0x7A,0x15,0xB1,0x10,0xD4,0x18,
- 0xE0,0x86,0x82,0xBE,0x5F,0x2B,0x18,0xA6,0xE8,0x43,0x7A,0x15,0xB1,0x10,0xD4,0x18},
- /* cipherlen */ 32,
- },
- {//[4]
- /*name */ "tv2.192",
- /* sek */ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
- /* seklen */ 192/8,
- /* cleartxt */ "00000000000000000000000000000000",
- /* ciphertxt */ {0xCC,0xFE,0xD9,0x9E,0x38,0xE9,0x60,0xF5,0xD7,0xE1,0xC5,0x9F,0x56,0x3A,0x49,0x9D,
- 0xCC,0xFE,0xD9,0x9E,0x38,0xE9,0x60,0xF5,0xD7,0xE1,0xC5,0x9F,0x56,0x3A,0x49,0x9D},
- /* cipherlen */ 32,
- },
- {//[5]
- /*name */ "tv2.256",
- /* sek */ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
- /* seklen */ 256/8,
- /* cleartxt */ "00000000000000000000000000000000",
- /* ciphertxt */ {0x94,0xB1,0x3A,0x9F,0x4C,0x09,0xD4,0xD7,0x00,0x2C,0x3F,0x11,0x7D,0xB1,0x7C,0x8B,
- 0x94,0xB1,0x3A,0x9F,0x4C,0x09,0xD4,0xD7,0x00,0x2C,0x3F,0x11,0x7D,0xB1,0x7C,0x8B},
- /* cipherlen */ 32,
- },
-};
-
-void test_AESecb(
- CRYSPR_methods *cryspr_m,
- CRYSPR_cb *cryspr_cb,
- size_t tvi,
- bool bEncrypt)
-{
- unsigned char result[128];
- unsigned char *intxt;
- unsigned char *outtxt;
- int rc1,rc2;
-
- if(tvi < sizeof(UTV_cryspr_aes_ecb)/sizeof(UTV_cryspr_aes_ecb[0]))
- {
- struct UTVcryspr_aes_ecb *tv = &UTV_cryspr_aes_ecb[tvi];
- size_t txtlen=strnlen((const char *)tv->cleartxt, 100);
- size_t outlen=sizeof(result);
-
- ASSERT_NE(cryspr_m->aes_set_key, nullPtr);
- ASSERT_NE(cryspr_m->aes_ecb_cipher, nullPtr);
-
- rc1 = cryspr_m->aes_set_key(
- bEncrypt,
- tv->sek, /* Stream encrypting Key */
- tv->seklen,
-#if WITH_FIPSMODE
- cryspr_cb->aes_sek[0]);
-#else
- &cryspr_cb->aes_sek[0]);
-#endif
- if(bEncrypt) {
- intxt=(unsigned char *)tv->cleartxt;
- outtxt=(unsigned char *)tv->ciphertxt;
- }else{
- intxt=(unsigned char *)tv->ciphertxt;
- outtxt=(unsigned char *)tv->cleartxt;
- }
-
- rc2 = cryspr_m->aes_ecb_cipher(
- bEncrypt, /* true:encrypt, false:decrypt */
-#if WITH_FIPSMODE
- cryspr_cb->aes_sek[0], /* CRYpto Service PRovider AES Key context */
-#else
- &cryspr_cb->aes_sek[0], /* CRYpto Service PRovider AES Key context */
-#endif
- intxt, /* src */
- txtlen, /* length */
- result, /* dest */
- &outlen); /* dest length */
-
- ASSERT_EQ(rc1, 0);
- ASSERT_EQ(rc2, 0);
- ASSERT_EQ(outlen, ((txtlen+(CRYSPR_AESBLKSZ-1))/CRYSPR_AESBLKSZ)*CRYSPR_AESBLKSZ);
- EXPECT_EQ(memcmp(outtxt, result, txtlen), 0);
- }
-}
-
-
-#define ENCRYPT true
-#define DECRYPT false
-
-TEST_F(TestCRYSPRcypto, EncryptAESecb_tv1_128)
-{
- test_AESecb(cryspr_m, cryspr_cb, 0, ENCRYPT);
-}
-TEST_F(TestCRYSPRcypto, EncryptAESecb_tv1_192)
-{
- test_AESecb(cryspr_m, cryspr_cb, 1, ENCRYPT);
-}
-TEST_F(TestCRYSPRcypto, EncryptAESecb_tv1_256)
-{
- test_AESecb(cryspr_m, cryspr_cb, 2, ENCRYPT);
-}
-TEST_F(TestCRYSPRcypto, EncryptAESecb_tv2_128)
-{
- test_AESecb(cryspr_m, cryspr_cb, 3, ENCRYPT);
-}
-TEST_F(TestCRYSPRcypto, EncryptAESecb_tv2_192)
-{
- test_AESecb(cryspr_m, cryspr_cb, 4, ENCRYPT);
-}
-TEST_F(TestCRYSPRcypto, EncryptAESecb_tv2_256)
-{
- test_AESecb(cryspr_m, cryspr_cb, 5, ENCRYPT);
-}
-TEST_F(TestCRYSPRcypto, DecryptAESecb_tv1_128)
-{
- test_AESecb(cryspr_m, cryspr_cb, 0, DECRYPT);
-}
-TEST_F(TestCRYSPRcypto, DecryptAESecb_tv1_192)
-{
- test_AESecb(cryspr_m, cryspr_cb, 1, DECRYPT);
-}
-TEST_F(TestCRYSPRcypto, DecryptAESecb_tv1_256)
-{
- test_AESecb(cryspr_m, cryspr_cb, 2, DECRYPT);
-}
-TEST_F(TestCRYSPRcypto, DecryptAESecb_tv2_128)
-{
- test_AESecb(cryspr_m, cryspr_cb, 3, DECRYPT);
-}
-TEST_F(TestCRYSPRcypto, DecryptAESecb_tv2_192)
-{
- test_AESecb(cryspr_m, cryspr_cb, 4, DECRYPT);
-}
-TEST_F(TestCRYSPRcypto, DecryptAESecb_tv2_256)
-{
- test_AESecb(cryspr_m, cryspr_cb, 5, DECRYPT);
-}
-#endif /* !(CRYSPR_HAS_AESCTR && CRYSPR_HAS_AESKWRAP) */
-
-/*AES CTR -----------------------------------------------------------------------------------*/
-#if CRYSPR_HAS_AESCTR
-
-struct UTVcryspr_aes_ctr {
- const char *name;
- unsigned char sek[256/8]; /* Stream Encrypting Key*/
- size_t seklen;
- unsigned char iv[CRYSPR_AESBLKSZ];/* initial vector */
- const char *cleartxt; /* clear text (decrypt result0 */
- unsigned char ciphertxt[24]; /* cipher text (encrypt result) */
-};
-
-/* AES-CTR test vectors */
-struct UTVcryspr_aes_ctr UTV_cryspr_aes_ctr[] = {
- {//[0]
- /*name */ "tv1.128",
- /* sek */ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
- /* seklen */ 128/8,
- /* iv */ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
- /* cleartxt */ "000000000000000000000000",
- /* ciphertxt */ {0x56,0xD9,0x7B,0xE4,0xDF,0xBA,0x1C,0x0B,0xB8,0x7C,0xCA,0x69,0xFA,0x04,0x1B,0x1E,
- 0x68,0xD2,0xCC,0xFE,0xCA,0x4E,0x00,0x51},
- },
- {//[1]
- /*name */ "tv1.192",
- /* sek */ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
- /* seklen */ 192/8,
- /* iv */ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
- /* cleartxt */ "000000000000000000000000",
- /* ciphertxt */ {0x9A,0xD0,0x59,0xA2,0x9C,0x8F,0x62,0x93,0xD8,0xC4,0x99,0x5E,0xF9,0x00,0x3B,0xE7,
- 0xFD,0x03,0x82,0xBA,0xF7,0x43,0xC7,0x7B},
- },
- {//[2]
- /*name */ "tv1.256",
- /* sek */ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
- /* seklen */ 256/8,
- /* iv */ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
- /* cleartxt */ "000000000000000000000000",
- /* ciphertxt */ {0xEC,0xA5,0xF0,0x48,0x92,0x70,0xB9,0xB9,0x9D,0x78,0x92,0x24,0xA2,0xB4,0x10,0xB7,
- 0x63,0x3F,0xBA,0xCB,0xF7,0x75,0x06,0x89}
- },
-};
-
-void test_AESctr(
- CRYSPR_methods *cryspr_m,
- CRYSPR_cb *cryspr_cb,
- size_t tvi,
- bool bEncrypt)
-{
- unsigned char result[100];
- unsigned char ivec[CRYSPR_AESBLKSZ];
- unsigned char *intxt;
- unsigned char *outtxt;
- int rc1,rc2;
-
- if(tvi < sizeof(UTV_cryspr_aes_ctr)/sizeof(UTV_cryspr_aes_ctr[0]))
- {
- struct UTVcryspr_aes_ctr *tv = &UTV_cryspr_aes_ctr[tvi];
- size_t txtlen=strnlen((const char *)tv->cleartxt, 100);
-
- ASSERT_NE(cryspr_m->aes_set_key, nullPtr);
- ASSERT_NE(cryspr_m->aes_ctr_cipher, nullPtr);
-
- rc1 = cryspr_m->aes_set_key(
- true, //For CTR, Encrypt key is used for both encryption and decryption
- tv->sek, /* Stream encrypting Key */
- tv->seklen,
-#if WITH_FIPSMODE
- cryspr_cb->aes_sek[0]);
-#else
- &cryspr_cb->aes_sek[0]);
-#endif
- if(bEncrypt) {
- intxt=(unsigned char *)tv->cleartxt;
- outtxt=(unsigned char *)tv->ciphertxt;
- }else{
- intxt=(unsigned char *)tv->ciphertxt;
- outtxt=(unsigned char *)tv->cleartxt;
- }
-
- memcpy(ivec, tv->iv, sizeof(ivec)); //cipher ivec not const
- rc2 = cryspr_m->aes_ctr_cipher(
- bEncrypt, /* true:encrypt, false:decrypt */
-#if WITH_FIPSMODE
- cryspr_cb->aes_sek[0], /* CRYpto Service PRovider AES Key context */
-#else
- &cryspr_cb->aes_sek[0], /* CRYpto Service PRovider AES Key context */
-#endif
- ivec, /* iv */
- intxt, /* src */
- txtlen, /* length */
- result); /* dest */
-
- ASSERT_EQ(rc1, 0);
- ASSERT_EQ(rc2, 0);
- EXPECT_EQ(memcmp(outtxt, result, txtlen), 0);
- }
-}
-
-
-#define ENCRYPT true
-#define DECRYPT false
-
-TEST_F(TestCRYSPRcypto, EncryptAESctr_tv1_128)
-{
- test_AESctr(cryspr_m, cryspr_cb, 0, ENCRYPT);
-}
-TEST_F(TestCRYSPRcypto, EncryptAESctr_tv1_192)
-{
- test_AESctr(cryspr_m, cryspr_cb, 1, ENCRYPT);
-}
-TEST_F(TestCRYSPRcypto, EncryptAESctr_tv1_256)
-{
- test_AESctr(cryspr_m, cryspr_cb, 2, ENCRYPT);
-}
-TEST_F(TestCRYSPRcypto, DecryptAESctr_tv1_128)
-{
- test_AESctr(cryspr_m, cryspr_cb, 0, DECRYPT);
-}
-TEST_F(TestCRYSPRcypto, DecryptAESctr_tv1_192)
-{
- test_AESctr(cryspr_m, cryspr_cb, 1, DECRYPT);
-}
-TEST_F(TestCRYSPRcypto, DecryptAESctr_tv1_256)
-{
- test_AESctr(cryspr_m, cryspr_cb, 2, DECRYPT);
-}
-#endif /* CRYSPR_HAS_AESCTR */
-
-#endif /* SRT_ENABLE_ENCRYPTION */
+++ /dev/null
-/*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2018 Haivision Systems Inc.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- * Written by:
- * Haivision Systems Inc.
- */
-
-#include <gtest/gtest.h>
-#include <thread>
-#include <condition_variable>
-#include <mutex>
-
-#include "srt.h"
-#include "sync.h"
-
-
-
-enum PEER_TYPE
-{
- PEER_CALLER = 0,
- PEER_LISTENER = 1,
- PEER_COUNT = 2, // Number of peers
-};
-
-
-enum CHECK_SOCKET_TYPE
-{
- CHECK_SOCKET_CALLER = 0,
- CHECK_SOCKET_ACCEPTED = 1,
- CHECK_SOCKET_COUNT = 2, // Number of peers
-};
-
-
-enum TEST_CASE
-{
- TEST_CASE_A_1 = 0,
- TEST_CASE_A_2,
- TEST_CASE_A_3,
- TEST_CASE_A_4,
- TEST_CASE_A_5,
- TEST_CASE_B_1,
- TEST_CASE_B_2,
- TEST_CASE_B_3,
- TEST_CASE_B_4,
- TEST_CASE_B_5,
- TEST_CASE_C_1,
- TEST_CASE_C_2,
- TEST_CASE_C_3,
- TEST_CASE_C_4,
- TEST_CASE_C_5,
- TEST_CASE_D_1,
- TEST_CASE_D_2,
- TEST_CASE_D_3,
- TEST_CASE_D_4,
- TEST_CASE_D_5,
-};
-
-
-struct TestResultNonBlocking
-{
- int connect_ret;
- int accept_ret;
- int epoll_wait_ret;
- int epoll_event;
- int socket_state[CHECK_SOCKET_COUNT];
- int km_state [CHECK_SOCKET_COUNT];
-};
-
-
-struct TestResultBlocking
-{
- int connect_ret;
- int accept_ret;
- int socket_state[CHECK_SOCKET_COUNT];
- int km_state[CHECK_SOCKET_COUNT];
-};
-
-
-template<typename TResult>
-struct TestCase
-{
- bool enforcedenc [PEER_COUNT];
- const std::string (&password)[PEER_COUNT];
- TResult expected_result;
-};
-
-typedef TestCase<TestResultNonBlocking> TestCaseNonBlocking;
-typedef TestCase<TestResultBlocking> TestCaseBlocking;
-
-
-
-static const std::string s_pwd_a ("s!t@r#i$c^t");
-static const std::string s_pwd_b ("s!t@r#i$c^tu");
-static const std::string s_pwd_no("");
-
-
-
-/*
- * TESTING SCENARIO
- * Both peers exchange HandShake v5.
- * Listener is sender in a non-blocking mode
- * Caller is receiver in a non-blocking mode
-
- * Cases B.2-B.4 are specific. Here we have incompatible password settings, but
- * listener accepts it, while caller rejects it. In this case we have a short-living
- * confusion state: The connection is accepted on the listener side, and the listener
- * sends back the conclusion handshake, but caller will reject it.
- *
- * Because of that, we should ignore what will happen in the listener as this is
- * just a matter of luck: if the listener thread is lucky, it will report the socket
- * to accept, so epoll will signal it and accept will report it, and moreover, further
- * good luck on this socket would make the state check return SRTS_CONNECTED. Without
- * this good luck, the caller might be quick enough to reject the handshake and send
- * the UMSG_SHUTDOWN packet to the peer. If it gets with it before acceptance, it will
- * withdraw the socket before it could be reported by accept.
- *
- * Still, we check predictable things here, so we accept two possibilities:
- * - The accepted socket wasn't reported at all
- * - The accepted socket was reported, and after `srt_connect` is done, it should turn to SRTS_BROKEN.
- *
- * This embraces both cases when the accepted socket was broken in the beginning, and when it was CONNECTED
- * in the beginning, but broke soon thereafter.
- *
- * This behavior is predicted and accepted - it's also the reason that setting ENFORCEDENC to false is
- * NOT RECOMMENDED on a listener socket that isn't intended to accept only connections from known callers
- * that are known to have set this flag also to false.
- *
- * In the cases C.2-C.4 it is the listener who rejects the connection, so we don't have an accepted socket
- * and the situation is always the same and clear in the beginning. The caller cannot continue with the
- * connection after listener accepted it, even if it tolerates incompatible password settings.
- */
-
-const int IGNORE_EPOLL = -2;
-const int IGNORE_SRTS = -1;
-
-const TestCaseNonBlocking g_test_matrix_non_blocking[] =
-{
- // ENFORCEDENC | Password | | EPoll wait | socket_state | KM State
- // caller | listener | caller | listener | connect_ret accept_ret | ret | event | caller accepted | caller listener
-/*A.1 */ { {true, true }, {s_pwd_a, s_pwd_a}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}},
-/*A.2 */ { {true, true }, {s_pwd_a, s_pwd_b}, { SRT_SUCCESS, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_SRTS}}},
-/*A.3 */ { {true, true }, {s_pwd_a, s_pwd_no}, { SRT_SUCCESS, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_SRTS}}},
-/*A.4 */ { {true, true }, {s_pwd_no, s_pwd_b}, { SRT_SUCCESS, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_SRTS}}},
-/*A.5 */ { {true, true }, {s_pwd_no, s_pwd_no}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}},
-
-/*B.1 */ { {true, false }, {s_pwd_a, s_pwd_a}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}},
-/*B.2 */ { {true, false }, {s_pwd_a, s_pwd_b}, { SRT_SUCCESS, 0, IGNORE_EPOLL, 0, {SRTS_CONNECTING, SRTS_BROKEN}, {SRT_KM_S_BADSECRET, SRT_KM_S_BADSECRET}}},
-/*B.3 */ { {true, false }, {s_pwd_a, s_pwd_no}, { SRT_SUCCESS, 0, IGNORE_EPOLL, 0, {SRTS_CONNECTING, SRTS_BROKEN}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}},
-/*B.4 */ { {true, false }, {s_pwd_no, s_pwd_b}, { SRT_SUCCESS, 0, IGNORE_EPOLL, 0, {SRTS_CONNECTING, SRTS_BROKEN}, {SRT_KM_S_UNSECURED, SRT_KM_S_NOSECRET}}},
-/*B.5 */ { {true, false }, {s_pwd_no, s_pwd_no}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}},
-
-/*C.1 */ { {false, true }, {s_pwd_a, s_pwd_a}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}},
-/*C.2 */ { {false, true }, {s_pwd_a, s_pwd_b}, { SRT_SUCCESS, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_SRTS}}},
-/*C.3 */ { {false, true }, {s_pwd_a, s_pwd_no}, { SRT_SUCCESS, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_SRTS}}},
-/*C.4 */ { {false, true }, {s_pwd_no, s_pwd_b}, { SRT_SUCCESS, SRT_INVALID_SOCK, 0, 0, {SRTS_BROKEN, IGNORE_SRTS}, {SRT_KM_S_UNSECURED, IGNORE_SRTS}}},
-/*C.5 */ { {false, true }, {s_pwd_no, s_pwd_no}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}},
-
-/*D.1 */ { {false, false }, {s_pwd_a, s_pwd_a}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}},
-/*D.2 */ { {false, false }, {s_pwd_a, s_pwd_b}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_BADSECRET, SRT_KM_S_BADSECRET}}},
-/*D.3 */ { {false, false }, {s_pwd_a, s_pwd_no}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}},
-/*D.4 */ { {false, false }, {s_pwd_no, s_pwd_b}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_NOSECRET, SRT_KM_S_NOSECRET}}},
-/*D.5 */ { {false, false }, {s_pwd_no, s_pwd_no}, { SRT_SUCCESS, 0, 1, SRT_EPOLL_IN, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}},
-};
-
-
-/*
- * TESTING SCENARIO
- * Both peers exchange HandShake v5.
- * Listener is sender in a blocking mode
- * Caller is receiver in a blocking mode
- *
- * In the cases B.2-B.4 the caller will reject the connection due to the enforced encryption check
- * of the HS response from the listener on the stage of the KM response check.
- * While the listener accepts the connection with the connected state. So the caller sends UMSG_SHUTDOWN
- * to notify the listener that it has closed the connection. The accepted socket gets the SRTS_BROKEN states.
- * For these cases a special accept_ret = -2 is used, that allows the accepted socket to be broken or already closed.
- *
- * In the cases C.2-C.4 it is the listener who rejects the connection, so we don't have an accepted socket.
- */
-const TestCaseBlocking g_test_matrix_blocking[] =
-{
- // ENFORCEDENC | Password | | socket_state | KM State
- // caller | listener | caller | listener | connect_ret accept_ret | caller accepted | caller listener
-/*A.1 */ { {true, true }, {s_pwd_a, s_pwd_a}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}},
-/*A.2 */ { {true, true }, {s_pwd_a, s_pwd_b}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, -1}, {SRT_KM_S_UNSECURED, -1}}},
-/*A.3 */ { {true, true }, {s_pwd_a, s_pwd_no}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, -1}, {SRT_KM_S_UNSECURED, -1}}},
-/*A.4 */ { {true, true }, {s_pwd_no, s_pwd_b}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, -1}, {SRT_KM_S_UNSECURED, -1}}},
-/*A.5 */ { {true, true }, {s_pwd_no, s_pwd_no}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}},
-
-/*B.1 */ { {true, false }, {s_pwd_a, s_pwd_a}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}},
-/*B.2 */ { {true, false }, {s_pwd_a, s_pwd_b}, { SRT_INVALID_SOCK, -2, {SRTS_OPENED, SRTS_BROKEN}, {SRT_KM_S_BADSECRET, SRT_KM_S_BADSECRET}}},
-/*B.3 */ { {true, false }, {s_pwd_a, s_pwd_no}, { SRT_INVALID_SOCK, -2, {SRTS_OPENED, SRTS_BROKEN}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}},
-/*B.4 */ { {true, false }, {s_pwd_no, s_pwd_b}, { SRT_INVALID_SOCK, -2, {SRTS_OPENED, SRTS_BROKEN}, {SRT_KM_S_UNSECURED, SRT_KM_S_NOSECRET}}},
-/*B.5 */ { {true, false }, {s_pwd_no, s_pwd_no}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}},
-
-/*C.1 */ { {false, true }, {s_pwd_a, s_pwd_a}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}},
-/*C.2 */ { {false, true }, {s_pwd_a, s_pwd_b}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, -1}, {SRT_KM_S_UNSECURED, -1}}},
-/*C.3 */ { {false, true }, {s_pwd_a, s_pwd_no}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, -1}, {SRT_KM_S_UNSECURED, -1}}},
-/*C.4 */ { {false, true }, {s_pwd_no, s_pwd_b}, { SRT_INVALID_SOCK, SRT_INVALID_SOCK, {SRTS_OPENED, -1}, {SRT_KM_S_UNSECURED, -1}}},
-/*C.5 */ { {false, true }, {s_pwd_no, s_pwd_no}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}},
-
-/*D.1 */ { {false, false }, {s_pwd_a, s_pwd_a}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_SECURED, SRT_KM_S_SECURED}}},
-/*D.2 */ { {false, false }, {s_pwd_a, s_pwd_b}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_BADSECRET, SRT_KM_S_BADSECRET}}},
-/*D.3 */ { {false, false }, {s_pwd_a, s_pwd_no}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}},
-/*D.4 */ { {false, false }, {s_pwd_no, s_pwd_b}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_NOSECRET, SRT_KM_S_NOSECRET}}},
-/*D.5 */ { {false, false }, {s_pwd_no, s_pwd_no}, { SRT_SUCCESS, 0, {SRTS_CONNECTED, SRTS_CONNECTED}, {SRT_KM_S_UNSECURED, SRT_KM_S_UNSECURED}}},
-};
-
-
-
-class TestEnforcedEncryption
- : public ::testing::Test
-{
-protected:
- TestEnforcedEncryption()
- {
- // initialization code here
- }
-
- ~TestEnforcedEncryption()
- {
- // cleanup any pending stuff, but no exceptions allowed
- }
-
-protected:
-
- // SetUp() is run immediately before a test starts.
- void SetUp()
- {
- ASSERT_EQ(srt_startup(), 0);
-
- m_pollid = srt_epoll_create();
- ASSERT_GE(m_pollid, 0);
-
- m_caller_socket = srt_create_socket();
- ASSERT_NE(m_caller_socket, SRT_INVALID_SOCK);
-
- ASSERT_NE(srt_setsockflag(m_caller_socket, SRTO_SENDER, &s_yes, sizeof s_yes), SRT_ERROR);
- ASSERT_NE(srt_setsockopt (m_caller_socket, 0, SRTO_TSBPDMODE, &s_yes, sizeof s_yes), SRT_ERROR);
-
- m_listener_socket = srt_create_socket();
- ASSERT_NE(m_listener_socket, SRT_INVALID_SOCK);
-
- ASSERT_NE(srt_setsockflag(m_listener_socket, SRTO_SENDER, &s_no, sizeof s_no), SRT_ERROR);
- ASSERT_NE(srt_setsockopt (m_listener_socket, 0, SRTO_TSBPDMODE, &s_yes, sizeof s_yes), SRT_ERROR);
-
- // Will use this epoll to wait for srt_accept(...)
- const int epoll_out = SRT_EPOLL_IN | SRT_EPOLL_ERR;
- ASSERT_NE(srt_epoll_add_usock(m_pollid, m_listener_socket, &epoll_out), SRT_ERROR);
- }
-
- void TearDown()
- {
- // Code here will be called just after the test completes.
- // OK to throw exceptions from here if needed.
- ASSERT_NE(srt_close(m_caller_socket), SRT_ERROR);
- ASSERT_NE(srt_close(m_listener_socket), SRT_ERROR);
- srt_cleanup();
- }
-
-
-public:
-
-
- int SetEnforcedEncryption(PEER_TYPE peer, bool value)
- {
- const SRTSOCKET &socket = peer == PEER_CALLER ? m_caller_socket : m_listener_socket;
- return srt_setsockopt(socket, 0, SRTO_ENFORCEDENCRYPTION, value ? &s_yes : &s_no, sizeof s_yes);
- }
-
-
- bool GetEnforcedEncryption(PEER_TYPE peer_type)
- {
- const SRTSOCKET socket = peer_type == PEER_CALLER ? m_caller_socket : m_listener_socket;
- bool optval;
- int optlen = sizeof optval;
- EXPECT_EQ(srt_getsockopt(socket, 0, SRTO_ENFORCEDENCRYPTION, (void*)&optval, &optlen), SRT_SUCCESS);
- return optval ? true : false;
- }
-
-
- int SetPassword(PEER_TYPE peer_type, const std::basic_string<char> &pwd)
- {
- const SRTSOCKET socket = peer_type == PEER_CALLER ? m_caller_socket : m_listener_socket;
- return srt_setsockopt(socket, 0, SRTO_PASSPHRASE, pwd.c_str(), (int) pwd.size());
- }
-
-
- int GetKMState(SRTSOCKET socket)
- {
- int km_state = 0;
- int opt_size = sizeof km_state;
- EXPECT_EQ(srt_getsockopt(socket, 0, SRTO_KMSTATE, reinterpret_cast<void*>(&km_state), &opt_size), SRT_SUCCESS);
-
- return km_state;
- }
-
-
- int GetSocetkOption(SRTSOCKET socket, SRT_SOCKOPT opt)
- {
- int val = 0;
- int size = sizeof val;
- EXPECT_EQ(srt_getsockopt(socket, 0, opt, reinterpret_cast<void*>(&val), &size), SRT_SUCCESS);
-
- return val;
- }
-
-
- template<typename TResult>
- int WaitOnEpoll(const TResult &expect);
-
-
- template<typename TResult>
- const TestCase<TResult>& GetTestMatrix(TEST_CASE test_case) const;
-
- template<typename TResult>
- void TestConnect(TEST_CASE test_case/*, bool is_blocking*/)
- {
- const bool is_blocking = std::is_same<TResult, TestResultBlocking>::value;
- if (is_blocking)
- {
- ASSERT_NE(srt_setsockopt( m_caller_socket, 0, SRTO_RCVSYN, &s_yes, sizeof s_yes), SRT_ERROR);
- ASSERT_NE(srt_setsockopt( m_caller_socket, 0, SRTO_SNDSYN, &s_yes, sizeof s_yes), SRT_ERROR);
- ASSERT_NE(srt_setsockopt(m_listener_socket, 0, SRTO_RCVSYN, &s_yes, sizeof s_yes), SRT_ERROR);
- ASSERT_NE(srt_setsockopt(m_listener_socket, 0, SRTO_SNDSYN, &s_yes, sizeof s_yes), SRT_ERROR);
- }
- else
- {
- ASSERT_NE(srt_setsockopt( m_caller_socket, 0, SRTO_RCVSYN, &s_no, sizeof s_no), SRT_ERROR); // non-blocking mode
- ASSERT_NE(srt_setsockopt( m_caller_socket, 0, SRTO_SNDSYN, &s_no, sizeof s_no), SRT_ERROR); // non-blocking mode
- ASSERT_NE(srt_setsockopt(m_listener_socket, 0, SRTO_RCVSYN, &s_no, sizeof s_no), SRT_ERROR); // non-blocking mode
- ASSERT_NE(srt_setsockopt(m_listener_socket, 0, SRTO_SNDSYN, &s_no, sizeof s_no), SRT_ERROR); // non-blocking mode
- }
-
- // Prepare input state
- const TestCase<TResult> &test = GetTestMatrix<TResult>(test_case);
- ASSERT_EQ(SetEnforcedEncryption(PEER_CALLER, test.enforcedenc[PEER_CALLER]), SRT_SUCCESS);
- ASSERT_EQ(SetEnforcedEncryption(PEER_LISTENER, test.enforcedenc[PEER_LISTENER]), SRT_SUCCESS);
-
- ASSERT_EQ(SetPassword(PEER_CALLER, test.password[PEER_CALLER]), SRT_SUCCESS);
- ASSERT_EQ(SetPassword(PEER_LISTENER, test.password[PEER_LISTENER]), SRT_SUCCESS);
-
- const TResult &expect = test.expected_result;
-
- // Start testing
- srt::sync::atomic<bool> caller_done;
- sockaddr_in sa;
- memset(&sa, 0, sizeof sa);
- sa.sin_family = AF_INET;
- sa.sin_port = htons(5200);
- ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1);
- sockaddr* psa = (sockaddr*)&sa;
- ASSERT_NE(srt_bind(m_listener_socket, psa, sizeof sa), SRT_ERROR);
- ASSERT_NE(srt_listen(m_listener_socket, 4), SRT_ERROR);
-
- auto accepting_thread = std::thread([&] {
- const int epoll_event = WaitOnEpoll(expect);
-
- // In a blocking mode we expect a socket returned from srt_accept() if the srt_connect succeeded.
- // In a non-blocking mode we expect a socket returned from srt_accept() if the srt_connect succeeded,
- // otherwise SRT_INVALID_SOCKET after the listening socket is closed.
- sockaddr_in client_address;
- int length = sizeof(sockaddr_in);
- SRTSOCKET accepted_socket = -1;
- if (epoll_event == SRT_EPOLL_IN)
- {
- accepted_socket = srt_accept(m_listener_socket, (sockaddr*)&client_address, &length);
- std::cout << "ACCEPT: done, result=" << accepted_socket << std::endl;
- }
- else
- {
- std::cout << "ACCEPT: NOT done\n";
- }
-
- if (accepted_socket == SRT_INVALID_SOCK)
- {
- std::cerr << "[T] ACCEPT ERROR: " << srt_getlasterror_str() << std::endl;
- }
- else
- {
- std::cerr << "[T] ACCEPT SUCCEEDED: @" << accepted_socket << "\n";
- }
-
- EXPECT_NE(accepted_socket, 0);
- if (expect.accept_ret == SRT_INVALID_SOCK)
- {
- EXPECT_EQ(accepted_socket, SRT_INVALID_SOCK);
- }
- else if (expect.accept_ret != -2)
- {
- EXPECT_NE(accepted_socket, SRT_INVALID_SOCK);
- }
-
- if (accepted_socket != SRT_INVALID_SOCK && expect.socket_state[CHECK_SOCKET_ACCEPTED] != IGNORE_SRTS)
- {
- if (m_is_tracing)
- {
- std::cerr << "EARLY Socket state accepted: " << m_socket_state[srt_getsockstate(accepted_socket)]
- << " (expected: " << m_socket_state[expect.socket_state[CHECK_SOCKET_ACCEPTED]] << ")\n";
- std::cerr << "KM State accepted: " << m_km_state[GetKMState(accepted_socket)] << '\n';
- std::cerr << "RCV KM State accepted: " << m_km_state[GetSocetkOption(accepted_socket, SRTO_RCVKMSTATE)] << '\n';
- std::cerr << "SND KM State accepted: " << m_km_state[GetSocetkOption(accepted_socket, SRTO_SNDKMSTATE)] << '\n';
- }
-
- // We have to wait some time for the socket to be able to process the HS responce from the caller.
- // In test cases B2 - B4 the socket is expected to change its state from CONNECTED to BROKEN
- // due to KM mismatches
- do
- {
- std::this_thread::sleep_for(std::chrono::milliseconds(50));
- } while (!caller_done);
-
- // Special case when the expected state is "broken": if so, tolerate every possible
- // socket state, just NOT LESS than SRTS_BROKEN, and also don't read any flags on that socket.
-
- if (expect.socket_state[CHECK_SOCKET_ACCEPTED] == SRTS_BROKEN)
- {
- EXPECT_GE(srt_getsockstate(accepted_socket), SRTS_BROKEN);
- }
- else
- {
- EXPECT_EQ(srt_getsockstate(accepted_socket), expect.socket_state[CHECK_SOCKET_ACCEPTED]);
- EXPECT_EQ(GetSocetkOption(accepted_socket, SRTO_SNDKMSTATE), expect.km_state[CHECK_SOCKET_ACCEPTED]);
- }
-
- if (m_is_tracing)
- {
- const SRT_SOCKSTATUS status = srt_getsockstate(accepted_socket);
- std::cerr << "LATE Socket state accepted: " << m_socket_state[status]
- << " (expected: " << m_socket_state[expect.socket_state[CHECK_SOCKET_ACCEPTED]] << ")\n";
- }
- }
- });
-
- const int connect_ret = srt_connect(m_caller_socket, psa, sizeof sa);
- EXPECT_EQ(connect_ret, expect.connect_ret);
-
- if (connect_ret == SRT_ERROR && connect_ret != expect.connect_ret)
- {
- std::cerr << "UNEXPECTED! srt_connect returned error: "
- << srt_getlasterror_str() << " (code " << srt_getlasterror(NULL) << ")\n";
- }
-
- caller_done = true;
-
- if (is_blocking == false)
- accepting_thread.join();
-
- if (m_is_tracing)
- {
- std::cerr << "Socket state caller: " << m_socket_state[srt_getsockstate(m_caller_socket)] << "\n";
- std::cerr << "Socket state listener: " << m_socket_state[srt_getsockstate(m_listener_socket)] << "\n";
- std::cerr << "KM State caller: " << m_km_state[GetKMState(m_caller_socket)] << '\n';
- std::cerr << "RCV KM State caller: " << m_km_state[GetSocetkOption(m_caller_socket, SRTO_RCVKMSTATE)] << '\n';
- std::cerr << "SND KM State caller: " << m_km_state[GetSocetkOption(m_caller_socket, SRTO_SNDKMSTATE)] << '\n';
- std::cerr << "KM State listener: " << m_km_state[GetKMState(m_listener_socket)] << '\n';
- }
-
- // If a blocking call to srt_connect() returned error, then the state is not valid,
- // but we still check it because we know what it should be. This way we may see potential changes in the core behavior.
- if (is_blocking)
- {
- EXPECT_EQ(srt_getsockstate(m_caller_socket), expect.socket_state[CHECK_SOCKET_CALLER]);
- }
- // A caller socket, regardless of the mode, if it's not expected to be connected, check negatively.
- if (expect.socket_state[CHECK_SOCKET_CALLER] == SRTS_CONNECTED)
- {
- EXPECT_EQ(srt_getsockstate(m_caller_socket), SRTS_CONNECTED);
- }
- else
- {
- // If the socket is not expected to be connected (might be CONNECTING),
- // then it is ok if it's CONNECTING or BROKEN.
- EXPECT_NE(srt_getsockstate(m_caller_socket), SRTS_CONNECTED);
- }
-
- EXPECT_EQ(GetSocetkOption(m_caller_socket, SRTO_RCVKMSTATE), expect.km_state[CHECK_SOCKET_CALLER]);
-
- EXPECT_EQ(srt_getsockstate(m_listener_socket), SRTS_LISTENING);
- EXPECT_EQ(GetKMState(m_listener_socket), SRT_KM_S_UNSECURED);
-
- if (is_blocking)
- {
- // srt_accept() has no timeout, so we have to close the socket and wait for the thread to exit.
- // Just give it some time and close the socket.
- std::this_thread::sleep_for(std::chrono::milliseconds(50));
- ASSERT_NE(srt_close(m_listener_socket), SRT_ERROR);
- accepting_thread.join();
- }
- }
-
-
-private:
- // put in any custom data members that you need
-
- SRTSOCKET m_caller_socket = SRT_INVALID_SOCK;
- SRTSOCKET m_listener_socket = SRT_INVALID_SOCK;
-
- int m_pollid = 0;
-
- const bool s_yes = true;
- const bool s_no = false;
-
- const bool m_is_tracing = false;
- static const char* m_km_state[];
- static const char* const* m_socket_state;
-};
-
-
-
-template<>
-int TestEnforcedEncryption::WaitOnEpoll<TestResultBlocking>(const TestResultBlocking &)
-{
- return SRT_EPOLL_IN;
-}
-
-static std::ostream& PrintEpollEvent(std::ostream& os, int events, int et_events)
-{
- using namespace std;
-
- static pair<int, const char*> const namemap [] = {
- make_pair(SRT_EPOLL_IN, "R"),
- make_pair(SRT_EPOLL_OUT, "W"),
- make_pair(SRT_EPOLL_ERR, "E"),
- make_pair(SRT_EPOLL_UPDATE, "U")
- };
-
- int N = Size(namemap);
-
- for (int i = 0; i < N; ++i)
- {
- if (events & namemap[i].first)
- {
- os << "[";
- if (et_events & namemap[i].first)
- os << "^";
- os << namemap[i].second << "]";
- }
- }
-
- return os;
-}
-
-template<>
-int TestEnforcedEncryption::WaitOnEpoll<TestResultNonBlocking>(const TestResultNonBlocking &expect)
-{
- const int default_len = 3;
- SRT_EPOLL_EVENT ready[default_len];
- const int epoll_res = srt_epoll_uwait(m_pollid, ready, default_len, 500);
- std::cerr << "Epoll wait result: " << epoll_res;
- if (epoll_res > 0)
- {
- std::cerr << " FOUND: @" << ready[0].fd << " in ";
- PrintEpollEvent(std::cerr, ready[0].events, 0);
- }
- else
- {
- std::cerr << " NOTHING READY";
- }
- std::cerr << std::endl;
-
- // Expect: -2 means that
- if (expect.epoll_wait_ret != IGNORE_EPOLL)
- {
- EXPECT_EQ(epoll_res, expect.epoll_wait_ret);
- }
-
- if (epoll_res == SRT_ERROR)
- {
- std::cerr << "Epoll returned error: " << srt_getlasterror_str() << " (code " << srt_getlasterror(NULL) << ")\n";
- return 0;
- }
-
- // We have exactly one socket here and we expect to return
- // only this one, or nothing.
- if (epoll_res != 0)
- {
- EXPECT_EQ(epoll_res, 1);
- EXPECT_EQ(ready[0].fd, m_listener_socket);
- }
-
- return epoll_res == 0 ? 0 : int(ready[0].events);
-}
-
-
-template<>
-const TestCase<TestResultBlocking>& TestEnforcedEncryption::GetTestMatrix<TestResultBlocking>(TEST_CASE test_case) const
-{
- return g_test_matrix_blocking[test_case];
-}
-
-template<>
-const TestCase<TestResultNonBlocking>& TestEnforcedEncryption::GetTestMatrix<TestResultNonBlocking>(TEST_CASE test_case) const
-{
- return g_test_matrix_non_blocking[test_case];
-}
-
-
-
-const char* TestEnforcedEncryption::m_km_state[] = {
- "SRT_KM_S_UNSECURED (0)", //No encryption
- "SRT_KM_S_SECURING (1)", //Stream encrypted, exchanging Keying Material
- "SRT_KM_S_SECURED (2)", //Stream encrypted, keying Material exchanged, decrypting ok.
- "SRT_KM_S_NOSECRET (3)", //Stream encrypted and no secret to decrypt Keying Material
- "SRT_KM_S_BADSECRET (4)" //Stream encrypted and wrong secret, cannot decrypt Keying Material
-};
-
-
-static const char* const socket_state_array[] = {
- "IGNORE_SRTS",
- "SRTS_INVALID",
- "SRTS_INIT",
- "SRTS_OPENED",
- "SRTS_LISTENING",
- "SRTS_CONNECTING",
- "SRTS_CONNECTED",
- "SRTS_BROKEN",
- "SRTS_CLOSING",
- "SRTS_CLOSED",
- "SRTS_NONEXIST"
-};
-
-// A trick that allows the array to be indexed by -1
-const char* const* TestEnforcedEncryption::m_socket_state = socket_state_array+1;
-
-/**
- * @fn TestEnforcedEncryption.PasswordLength
- * @brief The password length should belong to the interval of [10; 80]
- */
-TEST_F(TestEnforcedEncryption, PasswordLength)
-{
-#ifdef SRT_ENABLE_ENCRYPTION
- // Empty string sets password to none
- EXPECT_EQ(SetPassword(PEER_CALLER, std::string("")), SRT_SUCCESS);
- EXPECT_EQ(SetPassword(PEER_LISTENER, std::string("")), SRT_SUCCESS);
-
- EXPECT_EQ(SetPassword(PEER_CALLER, std::string("too_short")), SRT_ERROR);
- EXPECT_EQ(SetPassword(PEER_LISTENER, std::string("too_short")), SRT_ERROR);
-
- std::string long_pwd;
- const int pwd_len = 81; // 80 is the maximum password length accepted
- long_pwd.reserve(pwd_len);
- const char start_char = '!';
-
- // Please ensure to be within the valid ASCII symbols!
- ASSERT_LT(pwd_len + start_char, 126);
- for (int i = 0; i < pwd_len; ++i)
- long_pwd.push_back(static_cast<char>(start_char + i));
-
- EXPECT_EQ(SetPassword(PEER_CALLER, long_pwd), SRT_ERROR);
- EXPECT_EQ(SetPassword(PEER_LISTENER, long_pwd), SRT_ERROR);
-
- EXPECT_EQ(SetPassword(PEER_CALLER, std::string("proper_len")), SRT_SUCCESS);
- EXPECT_EQ(SetPassword(PEER_LISTENER, std::string("proper_length")), SRT_SUCCESS);
-#else
- EXPECT_EQ(SetPassword(PEER_CALLER, "whateverpassword"), SRT_ERROR);
-#endif
-}
-
-
-/**
- * @fn TestEnforcedEncryption.SetGetDefault
- * @brief The default value for the enforced encryption should be ON
- */
-TEST_F(TestEnforcedEncryption, SetGetDefault)
-{
- EXPECT_EQ(GetEnforcedEncryption(PEER_CALLER), true);
- EXPECT_EQ(GetEnforcedEncryption(PEER_LISTENER), true);
-
- EXPECT_EQ(SetEnforcedEncryption(PEER_CALLER, false), SRT_SUCCESS);
- EXPECT_EQ(SetEnforcedEncryption(PEER_LISTENER, false), SRT_SUCCESS);
-
- EXPECT_EQ(GetEnforcedEncryption(PEER_CALLER), false);
- EXPECT_EQ(GetEnforcedEncryption(PEER_LISTENER), false);
-}
-
-
-#define CREATE_TEST_CASE_BLOCKING(CASE_NUMBER, DESC) TEST_F(TestEnforcedEncryption, CASE_NUMBER##_Blocking_##DESC)\
-{\
- TestConnect<TestResultBlocking>(TEST_##CASE_NUMBER);\
-}
-
-#define CREATE_TEST_CASE_NONBLOCKING(CASE_NUMBER, DESC) TEST_F(TestEnforcedEncryption, CASE_NUMBER##_NonBlocking_##DESC)\
-{\
- TestConnect<TestResultNonBlocking>(TEST_##CASE_NUMBER);\
-}
-
-
-#define CREATE_TEST_CASES(CASE_NUMBER, DESC) \
- CREATE_TEST_CASE_NONBLOCKING(CASE_NUMBER, DESC) \
- CREATE_TEST_CASE_BLOCKING(CASE_NUMBER, DESC)
-
-#ifdef SRT_ENABLE_ENCRYPTION
-CREATE_TEST_CASES(CASE_A_1, Enforced_On_On_Pwd_Set_Set_Match)
-CREATE_TEST_CASES(CASE_A_2, Enforced_On_On_Pwd_Set_Set_Mismatch)
-CREATE_TEST_CASES(CASE_A_3, Enforced_On_On_Pwd_Set_None)
-CREATE_TEST_CASES(CASE_A_4, Enforced_On_On_Pwd_None_Set)
-#endif
-CREATE_TEST_CASES(CASE_A_5, Enforced_On_On_Pwd_None_None)
-
-#ifdef SRT_ENABLE_ENCRYPTION
-CREATE_TEST_CASES(CASE_B_1, Enforced_On_Off_Pwd_Set_Set_Match)
-CREATE_TEST_CASES(CASE_B_2, Enforced_On_Off_Pwd_Set_Set_Mismatch)
-CREATE_TEST_CASES(CASE_B_3, Enforced_On_Off_Pwd_Set_None)
-CREATE_TEST_CASES(CASE_B_4, Enforced_On_Off_Pwd_None_Set)
-#endif
-CREATE_TEST_CASES(CASE_B_5, Enforced_On_Off_Pwd_None_None)
-
-#ifdef SRT_ENABLE_ENCRYPTION
-CREATE_TEST_CASES(CASE_C_1, Enforced_Off_On_Pwd_Set_Set_Match)
-CREATE_TEST_CASES(CASE_C_2, Enforced_Off_On_Pwd_Set_Set_Mismatch)
-CREATE_TEST_CASES(CASE_C_3, Enforced_Off_On_Pwd_Set_None)
-CREATE_TEST_CASES(CASE_C_4, Enforced_Off_On_Pwd_None_Set)
-#endif
-CREATE_TEST_CASES(CASE_C_5, Enforced_Off_On_Pwd_None_None)
-
-#ifdef SRT_ENABLE_ENCRYPTION
-CREATE_TEST_CASES(CASE_D_1, Enforced_Off_Off_Pwd_Set_Set_Match)
-CREATE_TEST_CASES(CASE_D_2, Enforced_Off_Off_Pwd_Set_Set_Mismatch)
-CREATE_TEST_CASES(CASE_D_3, Enforced_Off_Off_Pwd_Set_None)
-CREATE_TEST_CASES(CASE_D_4, Enforced_Off_Off_Pwd_None_Set)
-#endif
-CREATE_TEST_CASES(CASE_D_5, Enforced_Off_Off_Pwd_None_None)
-
+++ /dev/null
-#include <iostream>
-#include <chrono>
-#include <future>
-#include <thread>
-#include <condition_variable>
-#include "gtest/gtest.h"
-#include "api.h"
-#include "epoll.h"
-
-
-using namespace std;
-using namespace srt;
-
-
-TEST(CEPoll, InfiniteWait)
-{
- ASSERT_EQ(srt_startup(), 0);
-
- const int epoll_id = srt_epoll_create();
- ASSERT_GE(epoll_id, 0);
-
- ASSERT_EQ(srt_epoll_wait(epoll_id, nullptr, nullptr,
- nullptr, nullptr,
- -1,
- 0, 0, 0, 0), SRT_ERROR);
-
- EXPECT_EQ(srt_epoll_release(epoll_id), 0);
-
- EXPECT_EQ(srt_cleanup(), 0);
-}
-
-TEST(CEPoll, WaitNoSocketsInEpoll)
-{
- ASSERT_EQ(srt_startup(), 0);
-
- const int epoll_id = srt_epoll_create();
- ASSERT_GE(epoll_id, 0);
-
- int rlen = 2;
- SRTSOCKET read[2];
-
- int wlen = 2;
- SRTSOCKET write[2];
-
- ASSERT_EQ(srt_epoll_wait(epoll_id, read, &rlen, write, &wlen,
- -1, 0, 0, 0, 0), SRT_ERROR);
-
- EXPECT_EQ(srt_epoll_release(epoll_id), 0);
-
- EXPECT_EQ(srt_cleanup(), 0);
-}
-
-TEST(CEPoll, WaitNoSocketsInEpoll2)
-{
- ASSERT_EQ(srt_startup(), 0);
-
- const int epoll_id = srt_epoll_create();
- ASSERT_GE(epoll_id, 0);
-
- SRT_EPOLL_EVENT events[2];
-
- ASSERT_EQ(srt_epoll_uwait(epoll_id, events, 2, -1), SRT_ERROR);
-
- EXPECT_EQ(srt_epoll_release(epoll_id), 0);
-
- EXPECT_EQ(srt_cleanup(), 0);
-}
-
-TEST(CEPoll, WaitEmptyCall)
-{
- ASSERT_EQ(srt_startup(), 0);
-
- SRTSOCKET client_sock = srt_create_socket();
- ASSERT_NE(client_sock, SRT_ERROR);
-
- const int no = 0;
- ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect
- ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect
-
- const int epoll_id = srt_epoll_create();
- ASSERT_GE(epoll_id, 0);
-
- const int epoll_out = SRT_EPOLL_OUT | SRT_EPOLL_ERR;
- ASSERT_NE(srt_epoll_add_usock(epoll_id, client_sock, &epoll_out), SRT_ERROR);
-
- ASSERT_EQ(srt_epoll_wait(epoll_id, 0, NULL, 0, NULL,
- -1, 0, 0, 0, 0), SRT_ERROR);
-
- EXPECT_EQ(srt_epoll_release(epoll_id), 0);
- EXPECT_EQ(srt_cleanup(), 0);
-}
-
-TEST(CEPoll, UWaitEmptyCall)
-{
- ASSERT_EQ(srt_startup(), 0);
-
- SRTSOCKET client_sock = srt_create_socket();
- ASSERT_NE(client_sock, SRT_ERROR);
-
- const int no = 0;
- ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect
- ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect
-
- const int epoll_id = srt_epoll_create();
- ASSERT_GE(epoll_id, 0);
-
- const int epoll_out = SRT_EPOLL_OUT | SRT_EPOLL_ERR;
- ASSERT_NE(srt_epoll_add_usock(epoll_id, client_sock, &epoll_out), SRT_ERROR);
-
- ASSERT_EQ(srt_epoll_uwait(epoll_id, NULL, 10, -1), SRT_ERROR);
-
- EXPECT_EQ(srt_epoll_release(epoll_id), 0);
-
- EXPECT_EQ(srt_cleanup(), 0);
-}
-
-TEST(CEPoll, WaitAllSocketsInEpollReleased)
-{
- ASSERT_EQ(srt_startup(), 0);
-
- SRTSOCKET client_sock = srt_create_socket();
- ASSERT_NE(client_sock, SRT_ERROR);
-
- const int yes = 1;
- const int no = 0;
- ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect
- ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect
- ASSERT_NE(srt_setsockflag(client_sock, SRTO_SENDER, &yes, sizeof yes), SRT_ERROR);
- ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR);
-
- const int epoll_id = srt_epoll_create();
- ASSERT_GE(epoll_id, 0);
-
- const int epoll_out = SRT_EPOLL_OUT | SRT_EPOLL_ERR;
- ASSERT_NE(srt_epoll_add_usock(epoll_id, client_sock, &epoll_out), SRT_ERROR);
- ASSERT_NE(srt_epoll_remove_usock(epoll_id, client_sock), SRT_ERROR);
-
- int rlen = 2;
- SRTSOCKET read[2];
-
- int wlen = 2;
- SRTSOCKET write[2];
-
- ASSERT_EQ(srt_epoll_wait(epoll_id, read, &rlen, write, &wlen,
- -1, 0, 0, 0, 0), SRT_ERROR);
-
- EXPECT_EQ(srt_epoll_release(epoll_id), 0);
-
- EXPECT_EQ(srt_cleanup(), 0);
-}
-
-TEST(CEPoll, WaitAllSocketsInEpollReleased2)
-{
- ASSERT_EQ(srt_startup(), 0);
-
- SRTSOCKET client_sock = srt_create_socket();
- ASSERT_NE(client_sock, SRT_ERROR);
-
- const int yes = 1;
- const int no = 0;
- ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect
- ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect
- ASSERT_NE(srt_setsockflag(client_sock, SRTO_SENDER, &yes, sizeof yes), SRT_ERROR);
- ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR);
-
- const int epoll_id = srt_epoll_create();
- ASSERT_GE(epoll_id, 0);
-
- const int epoll_out = SRT_EPOLL_OUT | SRT_EPOLL_ERR;
- ASSERT_NE(srt_epoll_add_usock(epoll_id, client_sock, &epoll_out), SRT_ERROR);
- ASSERT_NE(srt_epoll_remove_usock(epoll_id, client_sock), SRT_ERROR);
-
- SRT_EPOLL_EVENT events[2];
-
- ASSERT_EQ(srt_epoll_uwait(epoll_id, events, 2, -1), SRT_ERROR);
-
- EXPECT_EQ(srt_epoll_release(epoll_id), 0);
-
- EXPECT_EQ(srt_cleanup(), 0);
-}
-
-TEST(CEPoll, WrongEpoll_idOnAddUSock)
-{
- ASSERT_EQ(srt_startup(), 0);
-
- SRTSOCKET client_sock = srt_create_socket();
- ASSERT_NE(client_sock, SRT_ERROR);
-
- const int no = 0;
- ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect
- ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect
-
- const int epoll_id = srt_epoll_create();
- ASSERT_GE(epoll_id, 0);
-
- const int epoll_out = SRT_EPOLL_OUT | SRT_EPOLL_ERR;
- /* We intentionally pass the wrong socket ID. The error should be returned.*/
- ASSERT_EQ(srt_epoll_add_usock(epoll_id + 1, client_sock, &epoll_out), SRT_ERROR);
-
- EXPECT_EQ(srt_epoll_release(epoll_id), 0);
-
- EXPECT_EQ(srt_cleanup(), 0);
-}
-
-
-TEST(CEPoll, HandleEpollEvent)
-{
- ASSERT_EQ(srt_startup(), 0);
-
- SRTSOCKET client_sock = srt_create_socket();
- EXPECT_NE(client_sock, SRT_ERROR);
-
- const int yes = 1;
- const int no = 0;
- EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect
- EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect
- EXPECT_NE(srt_setsockflag(client_sock, SRTO_SENDER, &yes, sizeof yes), SRT_ERROR);
- EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR);
-
- CEPoll epoll;
- const int epoll_id = epoll.create();
- ASSERT_GE(epoll_id, 0);
-
- const int epoll_out = SRT_EPOLL_OUT | SRT_EPOLL_ERR;
- ASSERT_NE(epoll.update_usock(epoll_id, client_sock, &epoll_out), SRT_ERROR);
-
- set<int> epoll_ids = { epoll_id };
-
- epoll.update_events(client_sock, epoll_ids, SRT_EPOLL_ERR, true);
-
- set<SRTSOCKET> readset;
- set<SRTSOCKET> writeset;
- set<SRTSOCKET>* rval = &readset;
- set<SRTSOCKET>* wval = &writeset;
-
- ASSERT_NE(epoll.wait(epoll_id, rval, wval, -1, nullptr, nullptr), SRT_ERROR);
-
- try
- {
- int no_events = 0;
- EXPECT_EQ(epoll.update_usock(epoll_id, client_sock, &no_events), 0);
- }
- catch (CUDTException &ex)
- {
- cerr << ex.getErrorMessage() << endl;
- throw;
- }
-
- try
- {
- EXPECT_EQ(epoll.release(epoll_id), 0);
- }
- catch (CUDTException &ex)
- {
- cerr << ex.getErrorMessage() << endl;
- throw;
- }
-
- EXPECT_EQ(srt_cleanup(), 0);
-}
-
-
-
-// In this test case a caller connects to a listener on a localhost.
-// Then the caller closes the connection, and listener is expected to
-// be notified about connection break via polling the accepted socket.
-TEST(CEPoll, NotifyConnectionBreak)
-{
- ASSERT_EQ(srt_startup(), 0);
-
- // 1. Prepare client
- SRTSOCKET client_sock = srt_create_socket();
- ASSERT_NE(client_sock, SRT_ERROR);
-
- const int yes SRT_ATR_UNUSED = 1;
- const int no SRT_ATR_UNUSED = 0;
- ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect
- ASSERT_NE(srt_setsockopt(client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect
-
- const int client_epoll_id = srt_epoll_create();
- ASSERT_GE(client_epoll_id, 0);
-
- const int epoll_out = SRT_EPOLL_OUT | SRT_EPOLL_ERR;
- /* We intentionally pass the wrong socket ID. The error should be returned.*/
- EXPECT_EQ(srt_epoll_add_usock(client_epoll_id, client_sock, &epoll_out), SRT_SUCCESS);
-
- sockaddr_in sa_client;
- memset(&sa_client, 0, sizeof sa_client);
- sa_client.sin_family = AF_INET;
- sa_client.sin_port = htons(5555);
- ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa_client.sin_addr), 1);
-
- // 2. Prepare server
- SRTSOCKET server_sock = srt_create_socket();
- ASSERT_NE(server_sock, SRT_ERROR);
-
- ASSERT_NE(srt_setsockopt(server_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect
- ASSERT_NE(srt_setsockopt(server_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect
-
- const int server_epoll_id = srt_epoll_create();
- ASSERT_GE(server_epoll_id, 0);
-
- int epoll_mode = SRT_EPOLL_IN | SRT_EPOLL_ERR;
- srt_epoll_add_usock(server_epoll_id, server_sock, &epoll_mode);
-
- sockaddr_in sa;
- memset(&sa, 0, sizeof sa);
- sa.sin_family = AF_INET;
- sa.sin_port = htons(5555);
- ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1);
-
- srt_bind(server_sock, (sockaddr*)& sa, sizeof(sa));
- srt_listen(server_sock, 1);
-
-
- auto connect_res = std::async(std::launch::async, [&client_sock, &sa]() {
- return srt_connect(client_sock, (sockaddr*)& sa, sizeof(sa));
- });
-
-
- const int default_len = 3;
- int rlen = default_len;
- SRTSOCKET read[default_len];
- int wlen = default_len;
- SRTSOCKET write[default_len];
- // Wait on epoll for connection
- const int epoll_res = srt_epoll_wait(server_epoll_id, read, &rlen,
- write, &wlen,
- 5000, /* timeout */
- 0, 0, 0, 0);
-
- EXPECT_EQ(epoll_res, 1);
- if (epoll_res == SRT_ERROR)
- {
- std::cerr << "Epoll returned error: " << srt_getlasterror_str() << " (code " << srt_getlasterror(NULL) << ")\n";
- }
-
- // Wait for the caller connection thread to return connection result
- EXPECT_EQ(connect_res.get(), SRT_SUCCESS);
-
- sockaddr_in scl;
- int sclen = sizeof scl;
- SRTSOCKET sock = srt_accept(server_sock, (sockaddr*)& scl, &sclen);
- EXPECT_NE(sock, SRT_INVALID_SOCK);
-
- int epoll_io = srt_epoll_create();
- int modes = SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR;
- EXPECT_NE(srt_epoll_add_usock(epoll_io, sock, &modes), SRT_ERROR);
-
- // The caller will close connection after 1 second
- auto close_res = std::async(std::launch::async, [&client_sock]() {
- cout << "(async call): WILL CLOSE client connection in 3s\n";
- this_thread::sleep_for(chrono::seconds(1));
- cout << "(async call): Closing client connection\n";
- return srt_close(client_sock);
- });
-
- int timeout_ms = -1;
- int ready[2] = { SRT_INVALID_SOCK, SRT_INVALID_SOCK };
- int len = 2;
- cout << "TEST: entering INFINITE WAIT\n";
- const int epoll_wait_res = srt_epoll_wait(epoll_io, ready, &len, nullptr, nullptr, timeout_ms, 0, 0, 0, 0);
- cout << "TEST: return from INFINITE WAIT\n";
- if (epoll_wait_res == SRT_ERROR)
- cerr << "socket::read::epoll " << to_string(srt_getlasterror(nullptr));
- EXPECT_EQ(epoll_wait_res, 1);
- EXPECT_EQ(len, 1);
- EXPECT_EQ(ready[0], sock);
-
- // Wait for the caller to close connection
- // There should be no wait, as epoll should wait untill connection is closed.
- EXPECT_EQ(close_res.get(), SRT_SUCCESS);
- const SRT_SOCKSTATUS state = srt_getsockstate(sock);
- const bool state_valid = state == SRTS_BROKEN || state == SRTS_CLOSING || state == SRTS_CLOSED;
- EXPECT_TRUE(state_valid);
- if (!state_valid)
- cerr << "socket state: " << state << endl;
-
- EXPECT_EQ(srt_cleanup(), 0);
-}
-
-
-TEST(CEPoll, HandleEpollEvent2)
-{
- ASSERT_EQ(srt_startup(), 0);
-
- SRTSOCKET client_sock = srt_create_socket();
- EXPECT_NE(client_sock, SRT_ERROR);
-
- const int yes = 1;
- const int no = 0;
- EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect
- EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect
- EXPECT_NE(srt_setsockflag(client_sock, SRTO_SENDER, &yes, sizeof yes), SRT_ERROR);
- EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR);
-
- CEPoll epoll;
- const int epoll_id = epoll.create();
- ASSERT_GE(epoll_id, 0);
-
- const int epoll_out = SRT_EPOLL_OUT | SRT_EPOLL_ERR | SRT_EPOLL_ET;
- ASSERT_NE(epoll.update_usock(epoll_id, client_sock, &epoll_out), SRT_ERROR);
-
- set<int> epoll_ids = { epoll_id };
-
- epoll.update_events(client_sock, epoll_ids, SRT_EPOLL_ERR, true);
-
- SRT_EPOLL_EVENT fds[1024];
-
- int result = epoll.uwait(epoll_id, fds, 1024, -1);
- ASSERT_EQ(result, 1);
- ASSERT_EQ(fds[0].events, int(SRT_EPOLL_ERR));
-
- // Edge-triggered means that after one wait call was done, the next
- // call to this event should no longer report it. Now use timeout 0
- // to return immediately.
- result = epoll.uwait(epoll_id, fds, 1024, 0);
- ASSERT_EQ(result, 0);
-
- try
- {
- int no_events = 0;
- EXPECT_EQ(epoll.update_usock(epoll_id, client_sock, &no_events), 0);
- }
- catch (CUDTException &ex)
- {
- cerr << ex.getErrorMessage() << endl;
- throw;
- }
-
- try
- {
- EXPECT_EQ(epoll.release(epoll_id), 0);
- }
- catch (CUDTException &ex)
- {
- cerr << ex.getErrorMessage() << endl;
- throw;
- }
-
- EXPECT_EQ(srt_cleanup(), 0);
-}
-
-
-TEST(CEPoll, HandleEpollNoEvent)
-{
- ASSERT_EQ(srt_startup(), 0);
-
- SRTSOCKET client_sock = srt_create_socket();
- EXPECT_NE(client_sock, SRT_ERROR);
-
- const int yes = 1;
- const int no = 0;
- EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect
- EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect
- EXPECT_NE(srt_setsockflag(client_sock, SRTO_SENDER, &yes, sizeof yes), SRT_ERROR);
- EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR);
-
- CEPoll epoll;
- const int epoll_id = epoll.create();
- ASSERT_GE(epoll_id, 0);
-
- const int epoll_out = SRT_EPOLL_OUT | SRT_EPOLL_ERR;
- ASSERT_NE(epoll.update_usock(epoll_id, client_sock, &epoll_out), SRT_ERROR);
-
- SRT_EPOLL_EVENT fds[1024];
-
- // Use timeout 0 because with -1 this call would hang up
- int result = epoll.uwait(epoll_id, fds, 1024, 0);
- ASSERT_EQ(result, 0);
-
- try
- {
- int no_events = 0;
- EXPECT_EQ(epoll.update_usock(epoll_id, client_sock, &no_events), 0);
- }
- catch (CUDTException &ex)
- {
- cerr << ex.getErrorMessage() << endl;
- throw;
- }
-
- try
- {
- EXPECT_EQ(epoll.release(epoll_id), 0);
- }
- catch (CUDTException &ex)
- {
- cerr << ex.getErrorMessage() << endl;
- throw;
- }
-
- EXPECT_EQ(srt_cleanup(), 0);
-}
-
-TEST(CEPoll, ThreadedUpdate)
-{
- ASSERT_EQ(srt_startup(), 0);
-
- SRTSOCKET client_sock = srt_create_socket();
- EXPECT_NE(client_sock, SRT_ERROR);
-
- const int no = 0;
- EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect
- EXPECT_NE(srt_setsockopt (client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect
-
- CEPoll epoll;
- const int epoll_id = epoll.create();
- ASSERT_GE(epoll_id, 0);
- ASSERT_EQ(epoll.setflags(epoll_id, SRT_EPOLL_ENABLE_EMPTY), 0);
-
- thread td = thread( [&epoll, epoll_id, client_sock]()
- {
- cerr << "Spawned thread to add sockets to eid (wait 1s to order execution)\n";
- this_thread::sleep_for(chrono::seconds(1)); // Make sure that uwait will be called as first
- cerr << "ADDING sockets to eid\n";
- const int epoll_out = SRT_EPOLL_OUT | SRT_EPOLL_ERR;
- ASSERT_NE(epoll.update_usock(epoll_id, client_sock, &epoll_out), SRT_ERROR);
-
- set<int> epoll_ids = { epoll_id };
-
- epoll.update_events(client_sock, epoll_ids, SRT_EPOLL_ERR, true);
- cerr << "THREAD END\n";
- });
-
- SRT_EPOLL_EVENT fds[1024];
-
- cerr << "Entering infinite-wait by uwait:\n";
-
- int result = epoll.uwait(epoll_id, fds, 1024, -1);
- cerr << "Exit no longer infinite-wait by uwait, result=" << result << "\n";
- ASSERT_EQ(result, 1);
- ASSERT_EQ(fds[0].events, int(SRT_EPOLL_ERR));
-
- cerr << "THREAD JOIN...\n";
- td.join();
- cerr << "...JOINED\n";
-
- try
- {
- int no_events = 0;
- EXPECT_EQ(epoll.update_usock(epoll_id, client_sock, &no_events), 0);
- }
- catch (CUDTException &ex)
- {
- cerr << ex.getErrorMessage() << endl;
- throw;
- }
-
- try
- {
- EXPECT_EQ(epoll.release(epoll_id), 0);
- }
- catch (CUDTException &ex)
- {
- cerr << ex.getErrorMessage() << endl;
- throw;
- }
-
-
- EXPECT_EQ(srt_cleanup(), 0);
-}
-
-
-class TestEPoll: public testing::Test
-{
-protected:
-
- int m_client_pollid = SRT_ERROR;
- SRTSOCKET m_client_sock = SRT_ERROR;
-
- void clientSocket()
- {
- int yes = 1;
- int no = 0;
-
- m_client_sock = srt_create_socket();
- ASSERT_NE(m_client_sock, SRT_ERROR);
-
- ASSERT_NE(srt_setsockopt(m_client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect
- ASSERT_NE(srt_setsockflag(m_client_sock, SRTO_SENDER, &yes, sizeof yes), SRT_ERROR);
-
- ASSERT_NE(srt_setsockopt(m_client_sock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR);
-
- int epoll_out = SRT_EPOLL_OUT;
- srt_epoll_add_usock(m_client_pollid, m_client_sock, &epoll_out);
-
- sockaddr_in sa;
- memset(&sa, 0, sizeof sa);
- sa.sin_family = AF_INET;
- sa.sin_port = htons(9999);
-
- ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1);
-
- sockaddr* psa = (sockaddr*)&sa;
-
- ASSERT_NE(srt_connect(m_client_sock, psa, sizeof sa), SRT_ERROR);
-
-
- // Socket readiness for connection is checked by polling on WRITE allowed sockets.
-
- {
- int rlen = 2;
- SRTSOCKET read[2];
-
- int wlen = 2;
- SRTSOCKET write[2];
-
- ASSERT_NE(srt_epoll_wait(m_client_pollid, read, &rlen,
- write, &wlen,
- -1, // -1 is set for debuging purpose.
- // in case of production we need to set appropriate value
- 0, 0, 0, 0), SRT_ERROR);
-
- ASSERT_EQ(rlen, 0); // get exactly one write event without reads
- ASSERT_EQ(wlen, 1); // get exactly one write event without reads
- ASSERT_EQ(write[0], m_client_sock); // for our client socket
- }
-
- char buffer[1316] = {1, 2, 3, 4};
- ASSERT_NE(srt_sendmsg(m_client_sock, buffer, sizeof buffer,
- -1, // infinit ttl
- true // in order must be set to true
- ),
- SRT_ERROR);
-
- // disable receiving OUT events
- int epoll_err = SRT_EPOLL_ERR;
- ASSERT_EQ(0, srt_epoll_update_usock(m_client_pollid, m_client_sock, &epoll_err));
- {
- int rlen = 2;
- SRTSOCKET read[2];
-
- int wlen = 2;
- SRTSOCKET write[2];
-
- EXPECT_EQ(SRT_ERROR, srt_epoll_wait(m_client_pollid, read, &rlen,
- write, &wlen,
- 1000,
- 0, 0, 0, 0));
- const int last_error = srt_getlasterror(NULL);
- EXPECT_EQ(SRT_ETIMEOUT, last_error) << last_error;
- }
- }
-
- int m_server_pollid = SRT_ERROR;
-
- void createServerSocket(SRTSOCKET& w_servsock)
- {
- int yes = 1;
- int no = 0;
-
- SRTSOCKET servsock = srt_create_socket();
- ASSERT_NE(servsock, SRT_ERROR);
-
- ASSERT_NE(srt_setsockopt(servsock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect
- ASSERT_NE(srt_setsockopt(servsock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR);
-
- int epoll_in = SRT_EPOLL_IN;
- srt_epoll_add_usock(m_server_pollid, servsock, &epoll_in);
-
- sockaddr_in sa;
- memset(&sa, 0, sizeof sa);
- sa.sin_family = AF_INET;
- sa.sin_port = htons(9999);
- sa.sin_addr.s_addr = INADDR_ANY;
- sockaddr* psa = (sockaddr*)&sa;
-
- ASSERT_NE(srt_bind(servsock, psa, sizeof sa), SRT_ERROR);
- ASSERT_NE(srt_listen(servsock, SOMAXCONN), SRT_ERROR);
-
- w_servsock = servsock;
- }
-
- void runServer(SRTSOCKET servsock)
- {
- int epoll_in = SRT_EPOLL_IN;
-
- { // wait for connection from client
- int rlen = 2;
- SRTSOCKET read[2];
-
- int wlen = 2;
- SRTSOCKET write[2];
-
- ASSERT_NE(srt_epoll_wait(m_server_pollid,
- read, &rlen,
- write, &wlen,
- -1, // -1 is set for debuging purpose.
- // in case of production we need to set appropriate value
- 0, 0, 0, 0), SRT_ERROR );
-
- ASSERT_EQ(rlen, 1); // get exactly one read event without writes
- ASSERT_EQ(wlen, 0); // get exactly one read event without writes
- ASSERT_EQ(read[0], servsock); // read event is for bind socket
- }
-
- sockaddr_in scl;
- int sclen = sizeof scl;
-
- SRTSOCKET acpsock = srt_accept(servsock, (sockaddr*)&scl, &sclen);
- ASSERT_NE(acpsock, SRT_INVALID_SOCK);
-
- srt_epoll_add_usock(m_server_pollid, acpsock, &epoll_in); // wait for input
-
- { // wait for 1316 packet from client
- int rlen = 2;
- SRTSOCKET read[2];
-
- int wlen = 2;
- SRTSOCKET write[2];
-
- ASSERT_NE(srt_epoll_wait(m_server_pollid,
- read, &rlen,
- write, &wlen,
- -1, // -1 is set for debuging purpose.
- // in case of production we need to set appropriate value
- 0, 0, 0, 0), SRT_ERROR );
-
- ASSERT_EQ(rlen, 1); // get exactly one read event without writes
- ASSERT_EQ(wlen, 0); // get exactly one read event without writes
- ASSERT_EQ(read[0], acpsock); // read event is for bind socket
- }
-
- char buffer[1316];
- ASSERT_EQ(srt_recvmsg(acpsock, buffer, sizeof buffer), 1316);
-
- char pattern[4] = {1, 2, 3, 4};
- EXPECT_TRUE(std::mismatch(pattern, pattern+4, buffer).first == pattern+4);
-
- std::cout << "serverSocket waiting..." << std::endl;
- {
- int rlen = 2;
- SRTSOCKET read[2];
-
- int wlen = 2;
- SRTSOCKET write[2];
-
- ASSERT_EQ(-1, srt_epoll_wait(m_server_pollid,
- read, &rlen,
- write, &wlen,
- 2000,
- 0, 0, 0, 0));
- const int last_error = srt_getlasterror(NULL);
- ASSERT_EQ(SRT_ETIMEOUT, last_error) << last_error;
- }
- std::cout << "serverSocket finished waiting" << std::endl;
-
- srt_close(acpsock);
- srt_close(servsock);
- }
-
- void SetUp() override
- {
- ASSERT_EQ(srt_startup(), 0);
-
- m_client_pollid = srt_epoll_create();
- ASSERT_NE(SRT_ERROR, m_client_pollid);
-
- m_server_pollid = srt_epoll_create();
- ASSERT_NE(SRT_ERROR, m_server_pollid);
-
- }
-
- void TearDown() override
- {
- (void)srt_epoll_release(m_client_pollid);
- (void)srt_epoll_release(m_server_pollid);
- srt_cleanup();
- }
-};
-
-
-TEST_F(TestEPoll, SimpleAsync)
-{
- SRTSOCKET ss = SRT_INVALID_SOCK;
- createServerSocket( (ss) );
-
- std::thread client([this] { clientSocket(); });
-
- runServer(ss);
-
- client.join(); // Make sure client has exit before you delete the socket
-
- srt_close(m_client_sock); // cannot close m_client_sock after srt_sendmsg because of issue in api.c:2346
-}
-
+++ /dev/null
-#include <vector>
-#include <algorithm>
-#include <future>
-
-#include "gtest/gtest.h"
-#include "packet.h"
-#include "fec.h"
-#include "core.h"
-#include "packetfilter.h"
-#include "packetfilter_api.h"
-
-// For direct imp access
-#include "api.h"
-
-using namespace std;
-using namespace srt;
-
-class TestFECRebuilding: public testing::Test
-{
-protected:
- FECFilterBuiltin* fec = nullptr;
- vector<SrtPacket> provided;
- vector<unique_ptr<CPacket>> source;
- int sockid = 54321;
- int isn = 123456;
- size_t plsize = 1316;
-
- TestFECRebuilding()
- {
- // Required to make ParseCorrectorConfig work
- PacketFilter::globalInit();
- }
-
- void SetUp() override
- {
- int timestamp = 10;
-
- SrtFilterInitializer init = {
- sockid,
- isn - 1, // It's passed in this form to PacketFilter constructor, it should increase it
- isn - 1, // XXX Probably this better be changed.
- plsize,
- CSrtConfig::DEF_BUFFER_SIZE
- };
-
-
- // Make configuration row-only with size 7
- string conf = "fec,rows:1,cols:7";
-
- provided.clear();
-
- fec = new FECFilterBuiltin(init, provided, conf);
-
- int32_t seq = isn;
-
- for (int i = 0; i < 7; ++i)
- {
- source.emplace_back(new CPacket);
- CPacket& p = *source.back();
-
- p.allocate(SRT_LIVE_MAX_PLSIZE);
-
- uint32_t* hdr = p.getHeader();
-
- // Fill in the values
- hdr[SRT_PH_SEQNO] = seq;
- hdr[SRT_PH_MSGNO] = 1 | MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO);
- hdr[SRT_PH_ID] = sockid;
- hdr[SRT_PH_TIMESTAMP] = timestamp;
-
- // Fill in the contents.
- // Randomly chose the size
-
- int minsize = 732;
- int divergence = plsize - minsize - 1;
- size_t length = minsize + rand() % divergence;
-
- p.setLength(length);
- for (size_t b = 0; b < length; ++b)
- {
- p.data()[b] = rand() % 255;
- }
-
- timestamp += 10;
- seq = CSeqNo::incseq(seq);
- }
- }
-
- void TearDown() override
- {
- delete fec;
- }
-};
-
-namespace srt {
- class TestMockCUDT
- {
- public:
- CUDT* core;
-
- bool checkApplyFilterConfig(const string& s)
- {
- return core->checkApplyFilterConfig(s);
- }
- };
-}
-
-// The expected whole procedure of connection using FEC is
-// expected to:
-//
-// 1. Successfully set the FEC option for correct filter type.
-// - STOP ON FAILURE: unknown filter type (the table below, case D)
-// 2. Perform the connection and integrate configurations.
-// - STOP on failed integration (the table below, cases A and B)
-// 3. Deliver on both sides identical configurations consisting
-// of combined configurations and completed with default values.
-// - Not possible if stopped before.
-//
-// Test coverage for the above cases:
-//
-// Success cases in all of the above: ConfigExchange, Connection, ConnectionReorder
-// Failure cases:
-// 1. ConfigExchangeFaux - setting unknown filter type
-// 2. ConfigExchangeFaux, RejectionConflict, RejectionIncomplete, RejectionIncompleteEmpty
-//
-// For config exchange we have several possibilities here:
-//
-// - any same parameters with different values are rejected (Case A)
-// - resulting configuiration should have the `cols` value set (Cases B)
-//
-// The configuration API rules that control correctness:
-//
-// 1. The first word defines an existing filter type.
-// 2. Parameters are defined in whatever order.
-// 3. Some parameters are optional and have default values. Others are mandatory.
-// 4. A parameter provided twice remains with the last specification.
-// 5. A parameter with empty value is like not provided parameter.
-// 6. Only parameters handled by given filter type are allowed.
-// 7. Every parameter may have limitations on the provided value:
-// a. Numeric values in appropriate range
-// b. String-enumeration with only certain values allowed
-//
-// Additionally there are rules for configuration integration:
-//
-// 8. Configuration consists of parameters provided in both sides.
-// 9. Parameters lacking after integration are set to default values.
-// 10. Parameters specified on both sides (including type) must be equal.
-// 11. Empty configuration blindly accepts the configuration from the peer.
-// 12. The final configuration must provide mandatory parameters
-//
-// Restrictive rules type are: 1, 6, 7, 10
-//
-// Case description:
-// A: Conflicting values on the same parameter (rejection, rule 10 failure)
-// B: Missing a mandatory parameter (rejection, rule 12 failure)
-// C: Successful setting and combining parameters
-// 1: rules (positive): 1, 3, 6, 7(part), 8, 9, 12
-// 2: rules (positive): 1, 2, 3, 6, 7(part), 9, 10, 12
-// 3,4: rules (positive): 1, 2, 3(all), 6, 7(all), 8, 10, 12
-// 5: rules (positive): 1, 3, 4, 5, 6, 7, 8, 9, 12
-// 6: rules (positive): 1, 3, 6, 7, 8, 11, 12
-// D: Unknown filter type (failed option, rule 1)
-// E: Incorrect values of the parameters (failed option, rule 7)
-// F: Unknown excessive parameters (failed option, rule 6)
-//
-// Case |Party A | Party B | Situation | Test coverage
-//------|------------------------|--------------------|---------------------|---------------
-// A |fec,cols:10 | fec,cols:20 | Conflict | ConfigExchangeFaux, RejectionConflict
-// B1 |fec,rows:10 | fec,arq:never | Missing `cols` | RejectionIncomplete
-// B2 |fec,rows:10 | | Missing `cols` | RejectionIncompleteEmpty
-// C1 |fec,cols:10,rows:10 | fec | OK | ConfigExchange, Connection
-// C2 |fec,cols:10,rows:10 | fec,rows:10,cols:10| OK | ConnectionReorder
-// C3 |FULL 1 (see below) | FULL 2 (see below) | OK | ConnectionFull1
-// C4 |FULL 3 (see below) | FULL 4 (see below) | OK | ConnectionFull2
-// C5 |fec,cols:,cols:10 | fec,cols:,rows:10 | OK | ConnectionMess
-// C6 |fec,rows:20,cols:20 | | OK | ConnectionForced
-// D |FEC,Cols:10 | (unimportant) | Option rejected | ConfigExchangeFaux
-// E1 |fec,cols:-10 | (unimportant) | Option rejected | ConfigExchangeFaux
-// E2 |fec,cols:10,rows:0 | (unimportant) | Option rejected | ConfigExchangeFaux
-// E3 |fec,cols:10,rows:-1 | (unimportant) | Option rejected | ConfigExchangeFaux
-// E4 |fec,cols:10,layout:x (*)| (unimportant) | Option rejected | ConfigExchangeFaux
-// E5 |fec,cols:10,arq:x (*) | (unimportant) | Option rejected | ConfigExchangeFaux
-// F |fec,cols:10,weight:2 | (unimportant) | Option rejected | ConfigExchangeFaux
-//
-// (*) Here is just an example of a longer string that surely is wrong for this parameter.
-//
-// The configurations for FULL (cases C3 and C4) are longer and use all possible
-// values in different order:
-// 1. fec,cols:10,rows:20,arq:never,layout:even
-// 1. fec,layout:even,rows:20,cols:10,arq:never
-// 1. fec,cols:10,rows:20,arq:always,layout:even
-// 1. fec,layout:even,rows:20,cols:10,arq:always
-
-
-bool filterConfigSame(const string& config1, const string& config2)
-{
- vector<string> config1_vector;
- Split(config1, ',', back_inserter(config1_vector));
- sort(config1_vector.begin(), config1_vector.end());
-
- vector<string> config2_vector;
- Split(config2, ',', back_inserter(config2_vector));
- sort(config2_vector.begin(), config2_vector.end());
-
- return config1_vector == config2_vector;
-}
-
-TEST(TestFEC, ConfigExchange)
-{
- srt_startup();
-
- CUDTSocket* s1;
-
- SRTSOCKET sid1 = CUDT::uglobal().newSocket(&s1);
-
- TestMockCUDT m1;
- m1.core = &s1->core();
-
- // Can't access the configuration storage without
- // accessing the private fields, so let's use the official API
-
- char fec_config1 [] = "fec,cols:10,rows:10";
-
- srt_setsockflag(sid1, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1);
-
- EXPECT_TRUE(m1.checkApplyFilterConfig("fec,cols:10,arq:never"));
-
- char fec_configback[200];
- int fec_configback_size = 200;
- srt_getsockflag(sid1, SRTO_PACKETFILTER, fec_configback, &fec_configback_size);
-
- // Order of parameters may differ, so store everything in a vector and sort it.
-
- string exp_config = "fec,cols:10,rows:10,arq:never,layout:staircase";
-
- EXPECT_TRUE(filterConfigSame(fec_configback, exp_config));
- srt_cleanup();
-}
-
-TEST(TestFEC, ConfigExchangeFaux)
-{
- srt_startup();
-
- CUDTSocket* s1;
-
- SRTSOCKET sid1 = CUDT::uglobal().newSocket(&s1);
-
- const char* fec_config_wrong [] = {
- "FEC,Cols:20", // D: unknown filter
- "fec,cols:-10", // E1: invalid value for cols
- "fec,cols:10,rows:0", // E2: invalid value for rows
- "fec,cols:10,rows:-1", // E3: invalid value for rows
- "fec,cols:10,layout:stairwars", // E4: invalid value for layout
- "fec,cols:10,arq:sometimes", // E5: invalid value for arq
- "fec,cols:10,weight:2" // F: invalid parameter name
- };
-
- for (auto badconfig: fec_config_wrong)
- {
- ASSERT_EQ(srt_setsockflag(sid1, SRTO_PACKETFILTER, badconfig, strlen(badconfig)), -1);
- }
-
- TestMockCUDT m1;
- m1.core = &s1->core();
-
- // Can't access the configuration storage without
- // accessing the private fields, so let's use the official API
-
- char fec_config1 [] = "fec,cols:20,rows:10";
-
- EXPECT_NE(srt_setsockflag(sid1, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1), -1);
-
- cout << "(NOTE: expecting a failure message)\n";
- EXPECT_FALSE(m1.checkApplyFilterConfig("fec,cols:10,arq:never"));
-
- srt_cleanup();
-}
-
-TEST(TestFEC, Connection)
-{
- srt_startup();
-
- SRTSOCKET s = srt_create_socket();
- SRTSOCKET l = srt_create_socket();
-
- sockaddr_in sa;
- memset(&sa, 0, sizeof sa);
- sa.sin_family = AF_INET;
- sa.sin_port = htons(5555);
- ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1);
-
- srt_bind(l, (sockaddr*)& sa, sizeof(sa));
-
- const char fec_config1 [] = "fec,cols:10,rows:10";
- const char fec_config2 [] = "fec,cols:10,arq:never";
- const char fec_config_final [] = "fec,cols:10,rows:10,arq:never,layout:staircase";
-
- ASSERT_NE(srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1), -1);
- ASSERT_NE(srt_setsockflag(l, SRTO_PACKETFILTER, fec_config2, (sizeof fec_config2)-1), -1);
-
- srt_listen(l, 1);
-
- auto connect_res = std::async(std::launch::async, [&s, &sa]() {
- return srt_connect(s, (sockaddr*)& sa, sizeof(sa));
- });
-
- SRTSOCKET la[] = { l };
- // Given 2s timeout for accepting as it has occasionally happened with Travis
- // that 1s might not be enough.
- SRTSOCKET a = srt_accept_bond(la, 1, 2000);
- ASSERT_NE(a, SRT_ERROR);
- EXPECT_EQ(connect_res.get(), SRT_SUCCESS);
-
- // Now that the connection is established, check negotiated config
-
- char result_config1[200] = "";
- int result_config1_size = 200;
- char result_config2[200] = "";
- int result_config2_size = 200;
-
- EXPECT_NE(srt_getsockflag(s, SRTO_PACKETFILTER, result_config1, &result_config1_size), -1);
- EXPECT_NE(srt_getsockflag(a, SRTO_PACKETFILTER, result_config2, &result_config2_size), -1);
-
- string caller_config = result_config1;
- string accept_config = result_config2;
- EXPECT_EQ(caller_config, accept_config);
-
- EXPECT_TRUE(filterConfigSame(caller_config, fec_config_final));
- EXPECT_TRUE(filterConfigSame(accept_config, fec_config_final));
-
- srt_cleanup();
-}
-
-TEST(TestFEC, ConnectionReorder)
-{
- srt_startup();
-
- SRTSOCKET s = srt_create_socket();
- SRTSOCKET l = srt_create_socket();
-
- sockaddr_in sa;
- memset(&sa, 0, sizeof sa);
- sa.sin_family = AF_INET;
- sa.sin_port = htons(5555);
- ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1);
-
- srt_bind(l, (sockaddr*)& sa, sizeof(sa));
-
- const char fec_config1 [] = "fec,cols:10,rows:10";
- const char fec_config2 [] = "fec,rows:10,cols:10";
- const char fec_config_final [] = "fec,cols:10,rows:10,arq:onreq,layout:staircase";
-
- ASSERT_NE(srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1), -1);
- ASSERT_NE(srt_setsockflag(l, SRTO_PACKETFILTER, fec_config2, (sizeof fec_config2)-1), -1);
-
- srt_listen(l, 1);
-
- auto connect_res = std::async(std::launch::async, [&s, &sa]() {
- return srt_connect(s, (sockaddr*)& sa, sizeof(sa));
- });
-
- SRTSOCKET la[] = { l };
- SRTSOCKET a = srt_accept_bond(la, 1, 2000);
- ASSERT_NE(a, SRT_ERROR);
- EXPECT_EQ(connect_res.get(), SRT_SUCCESS);
-
- // Now that the connection is established, check negotiated config
-
- char result_config1[200] = "";
- int result_config1_size = 200;
- char result_config2[200] = "";
- int result_config2_size = 200;
-
- srt_getsockflag(s, SRTO_PACKETFILTER, result_config1, &result_config1_size);
- srt_getsockflag(a, SRTO_PACKETFILTER, result_config2, &result_config2_size);
-
- string caller_config = result_config1;
- string accept_config = result_config2;
- EXPECT_EQ(caller_config, accept_config);
-
- EXPECT_TRUE(filterConfigSame(caller_config, fec_config_final));
- EXPECT_TRUE(filterConfigSame(accept_config, fec_config_final));
-
- srt_cleanup();
-}
-
-TEST(TestFEC, ConnectionFull1)
-{
- srt_startup();
-
- SRTSOCKET s = srt_create_socket();
- SRTSOCKET l = srt_create_socket();
-
- sockaddr_in sa;
- memset(&sa, 0, sizeof sa);
- sa.sin_family = AF_INET;
- sa.sin_port = htons(5555);
- ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1);
-
- srt_bind(l, (sockaddr*)& sa, sizeof(sa));
-
- const char fec_config1 [] = "fec,cols:10,rows:20,arq:never,layout:even";
- const char fec_config2 [] = "fec,layout:even,rows:20,cols:10,arq:never";
- const char fec_config_final [] = "fec,cols:10,rows:20,arq:never,layout:even";
-
- ASSERT_NE(srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1), -1);
- ASSERT_NE(srt_setsockflag(l, SRTO_PACKETFILTER, fec_config2, (sizeof fec_config2)-1), -1);
-
- srt_listen(l, 1);
-
- auto connect_res = std::async(std::launch::async, [&s, &sa]() {
- return srt_connect(s, (sockaddr*)& sa, sizeof(sa));
- });
-
- SRTSOCKET la[] = { l };
- SRTSOCKET a = srt_accept_bond(la, 1, 2000);
- ASSERT_NE(a, SRT_ERROR);
- EXPECT_EQ(connect_res.get(), SRT_SUCCESS);
-
- // Now that the connection is established, check negotiated config
-
- char result_config1[200] = "";
- int result_config1_size = 200;
- char result_config2[200] = "";
- int result_config2_size = 200;
-
- srt_getsockflag(s, SRTO_PACKETFILTER, result_config1, &result_config1_size);
- srt_getsockflag(a, SRTO_PACKETFILTER, result_config2, &result_config2_size);
-
- string caller_config = result_config1;
- string accept_config = result_config2;
- EXPECT_EQ(caller_config, accept_config);
-
- EXPECT_TRUE(filterConfigSame(caller_config, fec_config_final));
- EXPECT_TRUE(filterConfigSame(accept_config, fec_config_final));
-
- srt_cleanup();
-}
-TEST(TestFEC, ConnectionFull2)
-{
- srt_startup();
-
- SRTSOCKET s = srt_create_socket();
- SRTSOCKET l = srt_create_socket();
-
- sockaddr_in sa;
- memset(&sa, 0, sizeof sa);
- sa.sin_family = AF_INET;
- sa.sin_port = htons(5555);
- ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1);
-
- srt_bind(l, (sockaddr*)& sa, sizeof(sa));
-
- const char fec_config1 [] = "fec,cols:10,rows:20,arq:always,layout:even";
- const char fec_config2 [] = "fec,layout:even,rows:20,cols:10,arq:always";
- const char fec_config_final [] = "fec,cols:10,rows:20,arq:always,layout:even";
-
- ASSERT_NE(srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1), -1);
- ASSERT_NE(srt_setsockflag(l, SRTO_PACKETFILTER, fec_config2, (sizeof fec_config2)-1), -1);
-
- srt_listen(l, 1);
-
- auto connect_res = std::async(std::launch::async, [&s, &sa]() {
- return srt_connect(s, (sockaddr*)& sa, sizeof(sa));
- });
-
- SRTSOCKET la[] = { l };
- SRTSOCKET a = srt_accept_bond(la, 1, 2000);
- ASSERT_NE(a, SRT_ERROR);
- EXPECT_EQ(connect_res.get(), SRT_SUCCESS);
-
- // Now that the connection is established, check negotiated config
-
- char result_config1[200] = "";
- int result_config1_size = 200;
- char result_config2[200] = "";
- int result_config2_size = 200;
-
- srt_getsockflag(s, SRTO_PACKETFILTER, result_config1, &result_config1_size);
- srt_getsockflag(a, SRTO_PACKETFILTER, result_config2, &result_config2_size);
-
- string caller_config = result_config1;
- string accept_config = result_config2;
- EXPECT_EQ(caller_config, accept_config);
-
- EXPECT_TRUE(filterConfigSame(caller_config, fec_config_final));
- EXPECT_TRUE(filterConfigSame(accept_config, fec_config_final));
-
- srt_cleanup();
-}
-
-TEST(TestFEC, ConnectionMess)
-{
- srt_startup();
-
- SRTSOCKET s = srt_create_socket();
- SRTSOCKET l = srt_create_socket();
-
- sockaddr_in sa;
- memset(&sa, 0, sizeof sa);
- sa.sin_family = AF_INET;
- sa.sin_port = htons(5555);
- ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1);
-
- srt_bind(l, (sockaddr*)& sa, sizeof(sa));
-
- const char fec_config1 [] = "fec,cols:,cols:10";
- const char fec_config2 [] = "fec,cols:,rows:10";
- const char fec_config_final [] = "fec,cols:10,rows:10,arq:onreq,layout:staircase";
-
- ASSERT_NE(srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1), -1);
- ASSERT_NE(srt_setsockflag(l, SRTO_PACKETFILTER, fec_config2, (sizeof fec_config2)-1), -1);
-
- srt_listen(l, 1);
-
- auto connect_res = std::async(std::launch::async, [&s, &sa]() {
- return srt_connect(s, (sockaddr*)& sa, sizeof(sa));
- });
-
- SRTSOCKET la[] = { l };
- SRTSOCKET a = srt_accept_bond(la, 1, 2000);
- ASSERT_NE(a, SRT_ERROR);
- EXPECT_EQ(connect_res.get(), SRT_SUCCESS);
-
- // Now that the connection is established, check negotiated config
-
- char result_config1[200] = "";
- int result_config1_size = 200;
- char result_config2[200] = "";
- int result_config2_size = 200;
-
- srt_getsockflag(s, SRTO_PACKETFILTER, result_config1, &result_config1_size);
- srt_getsockflag(a, SRTO_PACKETFILTER, result_config2, &result_config2_size);
-
- string caller_config = result_config1;
- string accept_config = result_config2;
- EXPECT_EQ(caller_config, accept_config);
-
- EXPECT_TRUE(filterConfigSame(caller_config, fec_config_final));
- EXPECT_TRUE(filterConfigSame(accept_config, fec_config_final));
-
- srt_cleanup();
-}
-
-TEST(TestFEC, ConnectionForced)
-{
- srt_startup();
-
- SRTSOCKET s = srt_create_socket();
- SRTSOCKET l = srt_create_socket();
-
- sockaddr_in sa;
- memset(&sa, 0, sizeof sa);
- sa.sin_family = AF_INET;
- sa.sin_port = htons(5555);
- ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1);
-
- srt_bind(l, (sockaddr*)& sa, sizeof(sa));
-
- const char fec_config1 [] = "fec,rows:20,cols:20";
- const char fec_config_final [] = "fec,cols:20,rows:20";
-
- ASSERT_NE(srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1), -1);
-
- srt_listen(l, 1);
-
- auto connect_res = std::async(std::launch::async, [&s, &sa]() {
- return srt_connect(s, (sockaddr*)& sa, sizeof(sa));
- });
-
- SRTSOCKET la[] = { l };
- SRTSOCKET a = srt_accept_bond(la, 1, 2000);
- ASSERT_NE(a, SRT_ERROR);
- EXPECT_EQ(connect_res.get(), SRT_SUCCESS);
-
- // Now that the connection is established, check negotiated config
-
- char result_config1[200] = "";
- int result_config1_size = 200;
- char result_config2[200] = "";
- int result_config2_size = 200;
-
- srt_getsockflag(s, SRTO_PACKETFILTER, result_config1, &result_config1_size);
- srt_getsockflag(a, SRTO_PACKETFILTER, result_config2, &result_config2_size);
-
- EXPECT_TRUE(filterConfigSame(result_config1, fec_config_final));
- EXPECT_TRUE(filterConfigSame(result_config2, fec_config_final));
-
- srt_cleanup();
-}
-
-TEST(TestFEC, RejectionConflict)
-{
- srt_startup();
-
- SRTSOCKET s = srt_create_socket();
- SRTSOCKET l = srt_create_socket();
-
- sockaddr_in sa;
- memset(&sa, 0, sizeof sa);
- sa.sin_family = AF_INET;
- sa.sin_port = htons(5555);
- ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1);
-
- srt_bind(l, (sockaddr*)& sa, sizeof(sa));
-
- const char fec_config1 [] = "fec,cols:10,rows:10";
- const char fec_config2 [] = "fec,cols:20,arq:never";
-
- srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1);
- srt_setsockflag(l, SRTO_PACKETFILTER, fec_config2, (sizeof fec_config2)-1);
-
- srt_listen(l, 1);
-
- auto connect_res = std::async(std::launch::async, [&s, &sa]() {
- return srt_connect(s, (sockaddr*)& sa, sizeof(sa));
- });
-
- EXPECT_EQ(connect_res.get(), SRT_ERROR);
- EXPECT_EQ(srt_getrejectreason(s), SRT_REJ_FILTER);
-
- bool no = false;
- // Set non-blocking so that srt_accept can return
- // immediately with failure. Just to make sure that
- // the connection is not about to be established,
- // also on the listener side.
- srt_setsockflag(l, SRTO_RCVSYN, &no, sizeof no);
- sockaddr_in scl;
- int sclen = sizeof scl;
- EXPECT_EQ(srt_accept(l, (sockaddr*)& scl, &sclen), SRT_ERROR);
-
- srt_cleanup();
-}
-
-TEST(TestFEC, RejectionIncompleteEmpty)
-{
- srt_startup();
-
- SRTSOCKET s = srt_create_socket();
- SRTSOCKET l = srt_create_socket();
-
- sockaddr_in sa;
- memset(&sa, 0, sizeof sa);
- sa.sin_family = AF_INET;
- sa.sin_port = htons(5555);
- ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1);
-
- srt_bind(l, (sockaddr*)& sa, sizeof(sa));
-
- const char fec_config1 [] = "fec,rows:10";
- srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1);
-
- srt_listen(l, 1);
-
- auto connect_res = std::async(std::launch::async, [&s, &sa]() {
- return srt_connect(s, (sockaddr*)& sa, sizeof(sa));
- });
-
- EXPECT_EQ(connect_res.get(), SRT_ERROR);
- EXPECT_EQ(srt_getrejectreason(s), SRT_REJ_FILTER);
-
- bool no = false;
- // Set non-blocking so that srt_accept can return
- // immediately with failure. Just to make sure that
- // the connection is not about to be established,
- // also on the listener side.
- srt_setsockflag(l, SRTO_RCVSYN, &no, sizeof no);
- sockaddr_in scl;
- int sclen = sizeof scl;
- EXPECT_EQ(srt_accept(l, (sockaddr*)& scl, &sclen), SRT_ERROR);
-
- srt_cleanup();
-}
-
-
-TEST(TestFEC, RejectionIncomplete)
-{
- srt_startup();
-
- SRTSOCKET s = srt_create_socket();
- SRTSOCKET l = srt_create_socket();
-
- sockaddr_in sa;
- memset(&sa, 0, sizeof sa);
- sa.sin_family = AF_INET;
- sa.sin_port = htons(5555);
- ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1);
-
- srt_bind(l, (sockaddr*)& sa, sizeof(sa));
-
- const char fec_config1 [] = "fec,rows:10";
- const char fec_config2 [] = "fec,arq:never";
-
- srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1);
- srt_setsockflag(l, SRTO_PACKETFILTER, fec_config2, (sizeof fec_config2)-1);
-
- srt_listen(l, 1);
-
- auto connect_res = std::async(std::launch::async, [&s, &sa]() {
- return srt_connect(s, (sockaddr*)& sa, sizeof(sa));
- });
-
- EXPECT_EQ(connect_res.get(), SRT_ERROR);
- EXPECT_EQ(srt_getrejectreason(s), SRT_REJ_FILTER);
-
- bool no = false;
- // Set non-blocking so that srt_accept can return
- // immediately with failure. Just to make sure that
- // the connection is not about to be established,
- // also on the listener side.
- srt_setsockflag(l, SRTO_RCVSYN, &no, sizeof no);
- sockaddr_in scl;
- int sclen = sizeof scl;
- EXPECT_EQ(srt_accept(l, (sockaddr*)& scl, &sclen), SRT_ERROR);
-
- srt_cleanup();
-}
-
-TEST_F(TestFECRebuilding, Prepare)
-{
- // Stuff in prepared packets into the source fec.
- int32_t seq;
- for (int i = 0; i < 7; ++i)
- {
- CPacket& p = *source[i].get();
-
- // Feed it simultaneously into the sender FEC
- fec->feedSource(p);
- seq = p.getSeqNo();
- }
-
- SrtPacket fec_ctl(SRT_LIVE_MAX_PLSIZE);
-
- // Use the sequence number of the last packet, as usual.
- bool have_fec_ctl = fec->packControlPacket(fec_ctl, seq);
-
- EXPECT_EQ(have_fec_ctl, true);
-}
-
-TEST_F(TestFECRebuilding, NoRebuild)
-{
- // Stuff in prepared packets into the source fec.
- int32_t seq;
- for (int i = 0; i < 7; ++i)
- {
- CPacket& p = *source[i].get();
-
- // Feed it simultaneously into the sender FEC
- fec->feedSource(p);
- seq = p.getSeqNo();
- }
-
- SrtPacket fec_ctl(SRT_LIVE_MAX_PLSIZE);
-
- // Use the sequence number of the last packet, as usual.
- const bool have_fec_ctl = fec->packControlPacket(fec_ctl, seq);
-
- ASSERT_EQ(have_fec_ctl, true);
- // By having all packets and FEC CTL packet, now stuff in
- // these packets into the receiver
-
- FECFilterBuiltin::loss_seqs_t loss; // required as return, ignore
-
- for (int i = 0; i < 7; ++i)
- {
- // SKIP packet 4 to simulate loss
- if (i == 4 || i == 6)
- continue;
-
- // Stuff in the packet into the FEC filter
- bool want_passthru = fec->receive(*source[i], loss);
- EXPECT_EQ(want_passthru, true);
- }
-
- // Prepare a real packet basing on the SrtPacket.
-
- // XXX Consider packing this into a callable function as this
- // is a code directly copied from PacketFilter::packControlPacket.
-
- unique_ptr<CPacket> fecpkt ( new CPacket );
-
- uint32_t* chdr = fecpkt->getHeader();
- memcpy(chdr, fec_ctl.hdr, SRT_PH_E_SIZE * sizeof(*chdr));
-
- // The buffer can be assigned.
- fecpkt->m_pcData = fec_ctl.buffer;
- fecpkt->setLength(fec_ctl.length);
-
- // This sets only the Packet Boundary flags, while all other things:
- // - Order
- // - Rexmit
- // - Crypto
- // - Message Number
- // will be set to 0/false
- fecpkt->m_iMsgNo = MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO);
-
- // ... and then fix only the Crypto flags
- fecpkt->setMsgCryptoFlags(EncryptionKeySpec(0));
-
- // And now receive the FEC control packet
-
- bool want_passthru_fec = fec->receive(*fecpkt, loss);
- EXPECT_EQ(want_passthru_fec, false); // Confirm that it's been eaten up
- EXPECT_EQ(provided.size(), 0U); // Confirm that nothing was rebuilt
-
- /*
- // XXX With such a short sequence, losses will not be reported.
- // You need at least one packet past the row, even in 1-row config.
- // Probably a better way for loss collection should be devised.
-
- ASSERT_EQ(loss.size(), 2);
- EXPECT_EQ(loss[0].first, isn + 4);
- EXPECT_EQ(loss[1].first, isn + 6);
- */
-}
-
-TEST_F(TestFECRebuilding, Rebuild)
-{
- // Stuff in prepared packets into the source fec->
- int32_t seq;
- for (int i = 0; i < 7; ++i)
- {
- CPacket& p = *source[i].get();
-
- // Feed it simultaneously into the sender FEC
- fec->feedSource(p);
- seq = p.getSeqNo();
- }
-
- SrtPacket fec_ctl(SRT_LIVE_MAX_PLSIZE);
-
- // Use the sequence number of the last packet, as usual.
- const bool have_fec_ctl = fec->packControlPacket(fec_ctl, seq);
-
- ASSERT_EQ(have_fec_ctl, true);
- // By having all packets and FEC CTL packet, now stuff in
- // these packets into the receiver
-
- FECFilterBuiltin::loss_seqs_t loss; // required as return, ignore
-
- for (int i = 0; i < 7; ++i)
- {
- // SKIP packet 4 to simulate loss
- if (i == 4)
- continue;
-
- // Stuff in the packet into the FEC filter
- bool want_passthru = fec->receive(*source[i], loss);
- EXPECT_EQ(want_passthru, true);
- }
-
- // Prepare a real packet basing on the SrtPacket.
-
- // XXX Consider packing this into a callable function as this
- // is a code directly copied from PacketFilter::packControlPacket.
-
- unique_ptr<CPacket> fecpkt ( new CPacket );
-
- uint32_t* chdr = fecpkt->getHeader();
- memcpy(chdr, fec_ctl.hdr, SRT_PH_E_SIZE * sizeof(*chdr));
-
- // The buffer can be assigned.
- fecpkt->m_pcData = fec_ctl.buffer;
- fecpkt->setLength(fec_ctl.length);
-
- // This sets only the Packet Boundary flags, while all other things:
- // - Order
- // - Rexmit
- // - Crypto
- // - Message Number
- // will be set to 0/false
- fecpkt->m_iMsgNo = MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO);
-
- // ... and then fix only the Crypto flags
- fecpkt->setMsgCryptoFlags(EncryptionKeySpec(0));
-
- // And now receive the FEC control packet
-
- const bool want_passthru_fec = fec->receive(*fecpkt, loss);
- EXPECT_EQ(want_passthru_fec, false); // Confirm that it's been eaten up
-
- EXPECT_EQ(loss.size(), 0U);
- ASSERT_EQ(provided.size(), 1U);
-
- SrtPacket& rebuilt = provided[0];
- CPacket& skipped = *source[4];
-
- // Set artificially the SN_REXMIT flag in the skipped source packet
- // because the rebuilt packet shall have REXMIT flag set.
- skipped.m_iMsgNo |= MSGNO_REXMIT::wrap(true);
-
- // Compare the header
- EXPECT_EQ(skipped.getHeader()[SRT_PH_SEQNO], rebuilt.hdr[SRT_PH_SEQNO]);
- EXPECT_EQ(skipped.getHeader()[SRT_PH_MSGNO], rebuilt.hdr[SRT_PH_MSGNO]);
- EXPECT_EQ(skipped.getHeader()[SRT_PH_ID], rebuilt.hdr[SRT_PH_ID]);
- EXPECT_EQ(skipped.getHeader()[SRT_PH_TIMESTAMP], rebuilt.hdr[SRT_PH_TIMESTAMP]);
-
- // Compare sizes and contents
- ASSERT_EQ(skipped.size(), rebuilt.size());
-
- EXPECT_EQ(memcmp(skipped.data(), rebuilt.data(), rebuilt.size()), 0);
-}
+++ /dev/null
-/*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2020 Haivision Systems Inc.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- * Based on the proposal by Russell Greene (Issue #440)
- *
- */
-
-#include <gtest/gtest.h>
-
-#ifdef _WIN32
-#define INC_SRT_WIN_WINTIME // exclude gettimeofday from srt headers
-#endif
-
-#include "srt.h"
-
-#include <array>
-#include <thread>
-#include <fstream>
-#include <ctime>
-#include <random>
-#include <vector>
-
-//#pragma comment (lib, "ws2_32.lib")
-
-TEST(Transmission, FileUpload)
-{
- srt_startup();
-
- // Generate the source file
- // We need a file that will contain more data
- // than can be contained in one sender buffer.
-
- SRTSOCKET sock_lsn = srt_create_socket(), sock_clr = srt_create_socket();
-
- const int tt = SRTT_FILE;
- srt_setsockflag(sock_lsn, SRTO_TRANSTYPE, &tt, sizeof tt);
- srt_setsockflag(sock_clr, SRTO_TRANSTYPE, &tt, sizeof tt);
-
- // Configure listener
- sockaddr_in sa_lsn = sockaddr_in();
- sa_lsn.sin_family = AF_INET;
- sa_lsn.sin_addr.s_addr = INADDR_ANY;
- sa_lsn.sin_port = htons(5555);
-
- // Find unused a port not used by any other service.
- // Otherwise srt_connect may actually connect.
- int bind_res = -1;
- for (int port = 5000; port <= 5555; ++port)
- {
- sa_lsn.sin_port = htons(port);
- bind_res = srt_bind(sock_lsn, (sockaddr*)&sa_lsn, sizeof sa_lsn);
- if (bind_res == 0)
- {
- std::cout << "Running test on port " << port << "\n";
- break;
- }
-
- ASSERT_TRUE(bind_res == SRT_EINVOP) << "Bind failed not due to an occupied port. Result " << bind_res;
- }
-
- ASSERT_GE(bind_res, 0);
-
- srt_bind(sock_lsn, (sockaddr*)&sa_lsn, sizeof sa_lsn);
-
- int optval = 0;
- int optlen = sizeof optval;
- ASSERT_EQ(srt_getsockflag(sock_lsn, SRTO_SNDBUF, &optval, &optlen), 0);
- const size_t filesize = 7 * optval;
-
- {
- std::cout << "WILL CREATE source file with size=" << filesize << " (= 7 * " << optval << "[sndbuf])\n";
- std::ofstream outfile("file.source", std::ios::out | std::ios::binary);
- ASSERT_EQ(!!outfile, true) << srt_getlasterror_str();
-
- std::random_device rd;
- std::mt19937 mtrd(rd());
- std::uniform_int_distribution<short> dis(0, UINT8_MAX);
-
- for (size_t i = 0; i < filesize; ++i)
- {
- char outbyte = dis(mtrd);
- outfile.write(&outbyte, 1);
- }
- }
-
- srt_listen(sock_lsn, 1);
-
- // Start listener-receiver thread
-
- bool thread_exit = false;
-
- auto client = std::thread([&]
- {
- sockaddr_in remote;
- int len = sizeof remote;
- const SRTSOCKET accepted_sock = srt_accept(sock_lsn, (sockaddr*)&remote, &len);
- ASSERT_GT(accepted_sock, 0);
-
- if (accepted_sock == SRT_INVALID_SOCK)
- {
- std::cerr << srt_getlasterror_str() << std::endl;
- EXPECT_NE(srt_close(sock_lsn), SRT_ERROR);
- return;
- }
-
- std::ofstream copyfile("file.target", std::ios::out | std::ios::trunc | std::ios::binary);
-
- std::vector<char> buf(1456);
-
- for (;;)
- {
- int n = srt_recv(accepted_sock, buf.data(), 1456);
- ASSERT_NE(n, SRT_ERROR);
- if (n == 0)
- {
- std::cerr << "Received 0 bytes, breaking.\n";
- break;
- }
-
- // Write to file any amount of data received
- copyfile.write(buf.data(), n);
- }
-
- EXPECT_NE(srt_close(accepted_sock), SRT_ERROR);
-
- thread_exit = true;
- });
-
- sockaddr_in sa = sockaddr_in();
- sa.sin_family = AF_INET;
- sa.sin_port = sa_lsn.sin_port;
- ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1);
-
- srt_connect(sock_clr, (sockaddr*)&sa, sizeof(sa));
-
- std::cout << "Connection initialized" << std::endl;
-
- std::ifstream ifile("file.source", std::ios::in | std::ios::binary);
- std::vector<char> buf(1456);
-
- for (;;)
- {
- size_t n = ifile.read(buf.data(), 1456).gcount();
- size_t shift = 0;
- while (n > 0)
- {
- const int st = srt_send(sock_clr, buf.data()+shift, n);
- ASSERT_GT(st, 0) << srt_getlasterror_str();
-
- n -= st;
- shift += st;
- }
-
- if (ifile.eof())
- {
- break;
- }
-
- ASSERT_EQ(ifile.good(), true);
- }
-
- // Finished sending, close the socket
- std::cout << "Finished sending, closing sockets:\n";
- srt_close(sock_clr);
- srt_close(sock_lsn);
-
- std::cout << "Sockets closed, joining receiver thread\n";
- client.join();
-
- std::ifstream tarfile("file.target");
- EXPECT_EQ(!!tarfile, true);
-
- tarfile.seekg(0, std::ios::end);
- size_t tar_size = tarfile.tellg();
- EXPECT_EQ(tar_size, filesize);
-
- std::cout << "Comparing files\n";
- // Compare files
- tarfile.seekg(0, std::ios::end);
- ifile.seekg(0, std::ios::beg);
-
- for (size_t i = 0; i < tar_size; ++i)
- {
- EXPECT_EQ(ifile.get(), tarfile.get());
- }
-
- EXPECT_EQ(ifile.get(), EOF);
- EXPECT_EQ(tarfile.get(), EOF);
-
- remove("file.source");
- remove("file.target");
-
- (void)srt_cleanup();
-}
+++ /dev/null
-#include "gtest/gtest.h"
-#include <thread>
-#include <string>
-#include "srt.h"
-#include "netinet_any.h"
-
-using srt::sockaddr_any;
-
-class TestIPv6
- : public ::testing::Test
-{
-protected:
- int yes = 1;
- int no = 0;
-
- TestIPv6()
- {
- // initialization code here
- }
-
- ~TestIPv6()
- {
- // cleanup any pending stuff, but no exceptions allowed
- }
-
-protected:
- // SetUp() is run immediately before a test starts.
- void SetUp()
- {
- ASSERT_GE(srt_startup(), 0);
-
- m_caller_sock = srt_create_socket();
- ASSERT_NE(m_caller_sock, SRT_ERROR);
-
- m_listener_sock = srt_create_socket();
- ASSERT_NE(m_listener_sock, SRT_ERROR);
- }
-
- void TearDown()
- {
- // Code here will be called just after the test completes.
- // OK to throw exceptions from here if needed.
- srt_close(m_listener_sock);
- srt_close(m_caller_sock);
- srt_cleanup();
- }
-
-public:
- void ClientThread(int family, const std::string& address)
- {
- sockaddr_any sa (family);
- sa.hport(m_listen_port);
- EXPECT_EQ(inet_pton(family, address.c_str(), sa.get_addr()), 1);
-
- std::cout << "Calling: " << address << "(" << fam[family] << ")\n";
-
- const int connect_res = srt_connect(m_caller_sock, (sockaddr*)&sa, sizeof sa);
- EXPECT_NE(connect_res, SRT_ERROR) << "srt_connect() failed with: " << srt_getlasterror_str();
- if (connect_res == SRT_ERROR)
- srt_close(m_listener_sock);
-
- PrintAddresses(m_caller_sock, "CALLER");
- }
-
- std::map<int, std::string> fam = { {AF_INET, "IPv4"}, {AF_INET6, "IPv6"} };
-
- void ShowAddress(std::string src, const sockaddr_any& w)
- {
- EXPECT_NE(fam.count(w.family()), 0U) << "INVALID FAMILY";
- std::cout << src << ": " << w.str() << " (" << fam[w.family()] << ")" << std::endl;
- }
-
- sockaddr_any DoAccept()
- {
- sockaddr_any sc1;
-
- SRTSOCKET accepted_sock = srt_accept(m_listener_sock, sc1.get(), &sc1.len);
- EXPECT_NE(accepted_sock, SRT_INVALID_SOCK) << "accept() failed with: " << srt_getlasterror_str();
- if (accepted_sock == SRT_INVALID_SOCK) {
- return sockaddr_any();
- }
-
- PrintAddresses(accepted_sock, "ACCEPTED");
-
- sockaddr_any sn;
- EXPECT_NE(srt_getsockname(accepted_sock, sn.get(), &sn.len), SRT_ERROR);
- EXPECT_NE(sn.get_addr(), nullptr);
-
- if (sn.get_addr() != nullptr)
- {
- const int32_t ipv6_zero[] = { 0, 0, 0, 0 };
- EXPECT_NE(memcmp(ipv6_zero, sn.get_addr(), sizeof ipv6_zero), 0)
- << "EMPTY address in srt_getsockname";
- }
-
- srt_close(accepted_sock);
- return sn;
- }
-
-private:
- void PrintAddresses(SRTSOCKET sock, const char* who)
- {
- sockaddr_any sa;
- int sa_len = (int) sa.storage_size();
- srt_getsockname(sock, sa.get(), &sa_len);
- ShowAddress(std::string(who) + " Sock name: ", sa);
- //std::cout << who << " Sock name: " << << sa.str() << std::endl;
-
- sa_len = (int) sa.storage_size();
- srt_getpeername(sock, sa.get(), &sa_len);
- //std::cout << who << " Peer name: " << << sa.str() << std::endl;
- ShowAddress(std::string(who) + " Peer name: ", sa);
- }
-
-protected:
- SRTSOCKET m_caller_sock;
- SRTSOCKET m_listener_sock;
- const int m_listen_port = 4200;
-};
-
-
-TEST_F(TestIPv6, v4_calls_v6_mapped)
-{
- sockaddr_any sa (AF_INET6);
- sa.hport(m_listen_port);
-
- ASSERT_EQ(srt_setsockflag(m_listener_sock, SRTO_IPV6ONLY, &no, sizeof no), 0);
- ASSERT_NE(srt_bind(m_listener_sock, sa.get(), sa.size()), SRT_ERROR);
- ASSERT_NE(srt_listen(m_listener_sock, SOMAXCONN), SRT_ERROR);
-
- std::thread client(&TestIPv6::ClientThread, this, AF_INET, "127.0.0.1");
-
- const sockaddr_any sa_accepted = DoAccept();
- EXPECT_EQ(sa_accepted.str(), "::ffff:127.0.0.1:4200");
-
- client.join();
-}
-
-TEST_F(TestIPv6, v6_calls_v6_mapped)
-{
- sockaddr_any sa (AF_INET6);
- sa.hport(m_listen_port);
-
- ASSERT_EQ(srt_setsockflag(m_listener_sock, SRTO_IPV6ONLY, &no, sizeof no), 0);
- ASSERT_NE(srt_bind(m_listener_sock, sa.get(), sa.size()), SRT_ERROR);
- ASSERT_NE(srt_listen(m_listener_sock, SOMAXCONN), SRT_ERROR);
-
- std::thread client(&TestIPv6::ClientThread, this, AF_INET6, "::1");
-
- const sockaddr_any sa_accepted = DoAccept();
- EXPECT_EQ(sa_accepted.str(), "::1:4200");
-
- client.join();
-}
-
-TEST_F(TestIPv6, v6_calls_v6)
-{
- sockaddr_any sa (AF_INET6);
- sa.hport(m_listen_port);
-
- // This time bind the socket exclusively to IPv6.
- ASSERT_EQ(srt_setsockflag(m_listener_sock, SRTO_IPV6ONLY, &yes, sizeof yes), 0);
- ASSERT_EQ(inet_pton(AF_INET6, "::1", sa.get_addr()), 1);
-
- ASSERT_NE(srt_bind(m_listener_sock, sa.get(), sa.size()), SRT_ERROR);
- ASSERT_NE(srt_listen(m_listener_sock, SOMAXCONN), SRT_ERROR);
-
- std::thread client(&TestIPv6::ClientThread, this, AF_INET6, "::1");
-
- const sockaddr_any sa_accepted = DoAccept();
- EXPECT_EQ(sa_accepted.str(), "::1:4200");
-
- client.join();
-}
-
-TEST_F(TestIPv6, v6_calls_v4)
-{
- sockaddr_any sa (AF_INET);
- sa.hport(m_listen_port);
-
- // This time bind the socket exclusively to IPv4.
- ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", sa.get_addr()), 1);
-
- ASSERT_NE(srt_bind(m_listener_sock, sa.get(), sa.size()), SRT_ERROR);
- ASSERT_NE(srt_listen(m_listener_sock, SOMAXCONN), SRT_ERROR);
-
- std::thread client(&TestIPv6::ClientThread, this, AF_INET6, "0::FFFF:127.0.0.1");
-
- const sockaddr_any sa_accepted = DoAccept();
- EXPECT_EQ(sa_accepted.str(), "127.0.0.1:4200");
-
- client.join();
-}
-
+++ /dev/null
-#include <iostream>
-#include "gtest/gtest.h"
-#include "common.h"
-#include "list.h"
-
-using namespace std;
-using namespace srt;
-
-class CSndLossListTest
- : public ::testing::Test
-{
-protected:
- void SetUp() override
- {
- m_lossList = new CSndLossList(CSndLossListTest::SIZE);
- }
-
- void TearDown() override
- {
- delete m_lossList;
- }
-
- void CheckEmptyArray()
- {
- EXPECT_EQ(m_lossList->getLossLength(), 0);
- EXPECT_EQ(m_lossList->popLostSeq(), -1);
- }
-
- void CleanUpList()
- {
- while (m_lossList->popLostSeq() != -1);
- }
-
- CSndLossList* m_lossList;
-
-public:
- const int SIZE = 256;
-};
-
-/// Check the state of the freshly created list.
-/// Capacity, loss length and pop().
-TEST_F(CSndLossListTest, Create)
-{
- CheckEmptyArray();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-///
-/// The first group of tests checks insert and pop()
-///
-///////////////////////////////////////////////////////////////////////////////
-
-/// Insert and pop one element from the list.
-TEST_F(CSndLossListTest, InsertPopOneElem)
-{
- EXPECT_EQ(m_lossList->insert(1, 1), 1);
-
- EXPECT_EQ(m_lossList->getLossLength(), 1);
- EXPECT_EQ(m_lossList->popLostSeq(), 1);
- CheckEmptyArray();
-}
-
-TEST_F(CSndLossListTest, InsertNegativeSeqno)
-{
- cerr << "Expecting IPE message:" << endl;
- EXPECT_EQ(m_lossList->insert(1, SRT_SEQNO_NONE), 0);
- EXPECT_EQ(m_lossList->insert(SRT_SEQNO_NONE, SRT_SEQNO_NONE), 0);
- EXPECT_EQ(m_lossList->insert(SRT_SEQNO_NONE, 1), 0);
-
- CheckEmptyArray();
-}
-
-/// Insert two elements at once and pop one by one
-TEST_F(CSndLossListTest, InsertPopTwoElemsRange)
-{
- EXPECT_EQ(m_lossList->insert(1, 2), 2);
-
- EXPECT_EQ(m_lossList->getLossLength(), 2);
- EXPECT_EQ(m_lossList->popLostSeq(), 1);
- EXPECT_EQ(m_lossList->getLossLength(), 1);
- EXPECT_EQ(m_lossList->popLostSeq(), 2);
- CheckEmptyArray();
-}
-
-/// Insert 1 and 4 and pop() one by one
-TEST_F(CSndLossListTest, InsertPopTwoElems)
-{
- EXPECT_EQ(m_lossList->insert(1, 1), 1);
- EXPECT_EQ(m_lossList->insert(4, 4), 1);
-
- EXPECT_EQ(m_lossList->getLossLength(), 2);
- EXPECT_EQ(m_lossList->popLostSeq(), 1);
- EXPECT_EQ(m_lossList->getLossLength(), 1);
- EXPECT_EQ(m_lossList->popLostSeq(), 4);
- CheckEmptyArray();
-}
-
-/// Insert 1 and 2 and pop() one by one
-TEST_F(CSndLossListTest, InsertPopTwoSerialElems)
-{
- EXPECT_EQ(m_lossList->insert(1, 1), 1);
- EXPECT_EQ(m_lossList->insert(2, 2), 1);
-
- EXPECT_EQ(m_lossList->getLossLength(), 2);
- EXPECT_EQ(m_lossList->popLostSeq(), 1);
- EXPECT_EQ(m_lossList->getLossLength(), 1);
- EXPECT_EQ(m_lossList->popLostSeq(), 2);
- CheckEmptyArray();
-}
-
-/// Insert (1,2) and 4, then pop one by one
-TEST_F(CSndLossListTest, InsertPopRangeAndSingle)
-{
- EXPECT_EQ(m_lossList->insert(1, 2), 2);
- EXPECT_EQ(m_lossList->insert(4, 4), 1);
-
- EXPECT_EQ(m_lossList->getLossLength(), 3);
- EXPECT_EQ(m_lossList->popLostSeq(), 1);
- EXPECT_EQ(m_lossList->getLossLength(), 2);
- EXPECT_EQ(m_lossList->popLostSeq(), 2);
- EXPECT_EQ(m_lossList->getLossLength(), 1);
- EXPECT_EQ(m_lossList->popLostSeq(), 4);
- CheckEmptyArray();
-}
-
-/// Insert 1, 4, 2, 0, then pop
-TEST_F(CSndLossListTest, InsertPopFourElems)
-{
- EXPECT_EQ(m_lossList->insert(1, 1), 1);
- EXPECT_EQ(m_lossList->insert(4, 4), 1);
- EXPECT_EQ(m_lossList->insert(0, 0), 1);
- EXPECT_EQ(m_lossList->insert(2, 2), 1);
-
- EXPECT_EQ(m_lossList->getLossLength(), 4);
- EXPECT_EQ(m_lossList->popLostSeq(), 0);
- EXPECT_EQ(m_lossList->getLossLength(), 3);
- EXPECT_EQ(m_lossList->popLostSeq(), 1);
- EXPECT_EQ(m_lossList->getLossLength(), 2);
- EXPECT_EQ(m_lossList->popLostSeq(), 2);
- EXPECT_EQ(m_lossList->getLossLength(), 1);
- EXPECT_EQ(m_lossList->popLostSeq(), 4);
- CheckEmptyArray();
-}
-
-/// Insert (1,2) and 4, then pop one by one
-TEST_F(CSndLossListTest, InsertCoalesce)
-{
- EXPECT_EQ(m_lossList->insert(1, 2), 2);
- EXPECT_EQ(m_lossList->insert(4, 4), 1);
- EXPECT_EQ(m_lossList->insert(3, 3), 1);
-
- EXPECT_EQ(m_lossList->getLossLength(), 4);
- EXPECT_EQ(m_lossList->popLostSeq(), 1);
- EXPECT_EQ(m_lossList->getLossLength(), 3);
- EXPECT_EQ(m_lossList->popLostSeq(), 2);
- EXPECT_EQ(m_lossList->getLossLength(), 2);
- EXPECT_EQ(m_lossList->popLostSeq(), 3);
- EXPECT_EQ(m_lossList->getLossLength(), 1);
- EXPECT_EQ(m_lossList->popLostSeq(), 4);
- CheckEmptyArray();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-///
-/// The group of tests checks remove() from different positions in the list,
-///
-///////////////////////////////////////////////////////////////////////////////
-
-///
-///
-///
-TEST_F(CSndLossListTest, BasicRemoveInListNodeHead01)
-{
- EXPECT_EQ(m_lossList->insert(1, 2), 2);
- EXPECT_EQ(m_lossList->insert(4, 4), 1);
- EXPECT_EQ(m_lossList->getLossLength(), 3);
- // Remove up to element 4
- m_lossList->removeUpTo(4);
- EXPECT_EQ(m_lossList->getLossLength(), 0);
- EXPECT_EQ(m_lossList->popLostSeq(), -1);
- CheckEmptyArray();
-}
-
-TEST_F(CSndLossListTest, BasicRemoveInListNodeHead02)
-{
- EXPECT_EQ(m_lossList->insert(1, 2), 2);
- EXPECT_EQ(m_lossList->insert(4, 5), 2);
- EXPECT_EQ(m_lossList->getLossLength(), 4);
- m_lossList->removeUpTo(4);
- EXPECT_EQ(m_lossList->getLossLength(), 1);
- EXPECT_EQ(m_lossList->popLostSeq(), 5);
- EXPECT_EQ(m_lossList->getLossLength(), 0);
- CheckEmptyArray();
-}
-
-TEST_F(CSndLossListTest, BasicRemoveInListNodeHead03)
-{
- EXPECT_EQ(m_lossList->insert(1, 2), 2);
- EXPECT_EQ(m_lossList->insert(4, 4), 1);
- EXPECT_EQ(m_lossList->insert(8, 8), 1);
- EXPECT_EQ(m_lossList->getLossLength(), 4);
- m_lossList->removeUpTo(4);
- EXPECT_EQ(m_lossList->getLossLength(), 1);
- EXPECT_EQ(m_lossList->popLostSeq(), 8);
- CheckEmptyArray();
-}
-
-TEST_F(CSndLossListTest, BasicRemoveInListNodeHead04)
-{
- EXPECT_EQ(m_lossList->insert(1, 2), 2);
- EXPECT_EQ(m_lossList->insert(4, 6), 3);
- EXPECT_EQ(m_lossList->insert(8, 8), 1);
- EXPECT_EQ(m_lossList->getLossLength(), 6);
- m_lossList->removeUpTo(4);
- EXPECT_EQ(m_lossList->getLossLength(), 3);
- EXPECT_EQ(m_lossList->popLostSeq(), 5);
- EXPECT_EQ(m_lossList->popLostSeq(), 6);
- EXPECT_EQ(m_lossList->popLostSeq(), 8);
- CheckEmptyArray();
-}
-
-TEST_F(CSndLossListTest, BasicRemoveInListNotInNodeHead01)
-{
- EXPECT_EQ(m_lossList->insert(1, 2), 2);
- EXPECT_EQ(m_lossList->insert(4, 5), 2);
- EXPECT_EQ(m_lossList->getLossLength(), 4);
- m_lossList->removeUpTo(5);
- EXPECT_EQ(m_lossList->getLossLength(), 0);
- EXPECT_EQ(m_lossList->popLostSeq(), -1);
- CheckEmptyArray();
-}
-
-TEST_F(CSndLossListTest, BasicRemoveInListNotInNodeHead02)
-{
- EXPECT_EQ(m_lossList->insert(1, 2), 2);
- EXPECT_EQ(m_lossList->insert(4, 5), 2);
- EXPECT_EQ(m_lossList->insert(8, 8), 1);
- EXPECT_EQ(m_lossList->getLossLength(), 5);
- m_lossList->removeUpTo(5);
- EXPECT_EQ(m_lossList->getLossLength(), 1);
- EXPECT_EQ(m_lossList->popLostSeq(), 8);
- CheckEmptyArray();
-}
-
-TEST_F(CSndLossListTest, BasicRemoveInListNotInNodeHead03)
-{
- EXPECT_EQ(m_lossList->insert(1, 2), 2);
- EXPECT_EQ(m_lossList->insert(4, 8), 5);
- EXPECT_EQ(m_lossList->getLossLength(), 7);
- m_lossList->removeUpTo(5);
- EXPECT_EQ(m_lossList->getLossLength(), 3);
- EXPECT_EQ(m_lossList->popLostSeq(), 6);
- EXPECT_EQ(m_lossList->popLostSeq(), 7);
- EXPECT_EQ(m_lossList->popLostSeq(), 8);
- CheckEmptyArray();
-}
-
-TEST_F(CSndLossListTest, BasicRemoveInListNotInNodeHead04)
-{
- EXPECT_EQ(m_lossList->insert(1, 2), 2);
- EXPECT_EQ(m_lossList->insert(4, 8), 5);
- EXPECT_EQ(m_lossList->insert(10, 12), 3);
- EXPECT_EQ(m_lossList->getLossLength(), 10);
- m_lossList->removeUpTo(5);
- EXPECT_EQ(m_lossList->getLossLength(), 6);
- EXPECT_EQ(m_lossList->popLostSeq(), 6);
- EXPECT_EQ(m_lossList->popLostSeq(), 7);
- EXPECT_EQ(m_lossList->popLostSeq(), 8);
- EXPECT_EQ(m_lossList->popLostSeq(), 10);
- EXPECT_EQ(m_lossList->popLostSeq(), 11);
- EXPECT_EQ(m_lossList->popLostSeq(), 12);
- CheckEmptyArray();
-}
-
-TEST_F(CSndLossListTest, BasicRemoveInListNotInNodeHead05)
-{
- EXPECT_EQ(m_lossList->insert(1, 2), 2);
- EXPECT_EQ(m_lossList->insert(4, 8), 5);
- EXPECT_EQ(m_lossList->insert(10, 12), 3);
- EXPECT_EQ(m_lossList->getLossLength(), 10);
- m_lossList->removeUpTo(9);
- EXPECT_EQ(m_lossList->getLossLength(), 3);
- EXPECT_EQ(m_lossList->popLostSeq(), 10);
- EXPECT_EQ(m_lossList->popLostSeq(), 11);
- EXPECT_EQ(m_lossList->popLostSeq(), 12);
- CheckEmptyArray();
-}
-
-TEST_F(CSndLossListTest, BasicRemoveInListNotInNodeHead06)
-{
- EXPECT_EQ(m_lossList->insert(1, 2), 2);
- EXPECT_EQ(m_lossList->insert(4, 8), 5);
- EXPECT_EQ(m_lossList->insert(10, 12), 3);
- EXPECT_EQ(m_lossList->getLossLength(), 10);
- m_lossList->removeUpTo(50);
- EXPECT_EQ(m_lossList->getLossLength(), 0);
- EXPECT_EQ(m_lossList->popLostSeq(), -1);
- CheckEmptyArray();
-}
-
-TEST_F(CSndLossListTest, BasicRemoveInListNotInNodeHead07)
-{
- EXPECT_EQ(m_lossList->insert(1, 2), 2);
- EXPECT_EQ(m_lossList->insert(4, 8), 5);
- EXPECT_EQ(m_lossList->insert(10, 12), 3);
- EXPECT_EQ(m_lossList->getLossLength(), 10);
- m_lossList->removeUpTo(-50);
- EXPECT_EQ(m_lossList->getLossLength(), 10);
- EXPECT_EQ(m_lossList->popLostSeq(), 1);
- EXPECT_EQ(m_lossList->popLostSeq(), 2);
- EXPECT_EQ(m_lossList->popLostSeq(), 4);
- EXPECT_EQ(m_lossList->popLostSeq(), 5);
- EXPECT_EQ(m_lossList->popLostSeq(), 6);
- EXPECT_EQ(m_lossList->popLostSeq(), 7);
- EXPECT_EQ(m_lossList->popLostSeq(), 8);
- EXPECT_EQ(m_lossList->popLostSeq(), 10);
- EXPECT_EQ(m_lossList->popLostSeq(), 11);
- EXPECT_EQ(m_lossList->popLostSeq(), 12);
- CheckEmptyArray();
-}
-
-TEST_F(CSndLossListTest, BasicRemoveInListNotInNodeHead08)
-{
- EXPECT_EQ(m_lossList->insert(1, 2), 2);
- EXPECT_EQ(m_lossList->insert(5, 6), 2);
- EXPECT_EQ(m_lossList->getLossLength(), 4);
- m_lossList->removeUpTo(5);
- EXPECT_EQ(m_lossList->getLossLength(), 1);
- m_lossList->removeUpTo(6);
- EXPECT_EQ(m_lossList->getLossLength(), 0);
- EXPECT_EQ(m_lossList->popLostSeq(), -1);
- CheckEmptyArray();
-}
-
-TEST_F(CSndLossListTest, BasicRemoveInListNotInNodeHead09)
-{
- EXPECT_EQ(m_lossList->insert(1, 2), 2);
- EXPECT_EQ(m_lossList->insert(5, 6), 2);
- EXPECT_EQ(m_lossList->getLossLength(), 4);
- m_lossList->removeUpTo(5);
- EXPECT_EQ(m_lossList->getLossLength(), 1);
- EXPECT_EQ(m_lossList->insert(1, 2), 2);
- m_lossList->removeUpTo(6);
- EXPECT_EQ(m_lossList->getLossLength(), 0);
- EXPECT_EQ(m_lossList->popLostSeq(), -1);
- CheckEmptyArray();
-}
-
-TEST_F(CSndLossListTest, BasicRemoveInListNotInNodeHead10)
-{
- EXPECT_EQ(m_lossList->insert(1, 2), 2);
- EXPECT_EQ(m_lossList->insert(5, 6), 2);
- EXPECT_EQ(m_lossList->insert(10, 10), 1);
- EXPECT_EQ(m_lossList->getLossLength(), 5);
- m_lossList->removeUpTo(5);
- EXPECT_EQ(m_lossList->getLossLength(), 2);
- EXPECT_EQ(m_lossList->insert(1, 2), 2);
- m_lossList->removeUpTo(7);
- EXPECT_EQ(m_lossList->getLossLength(), 1);
- EXPECT_EQ(m_lossList->popLostSeq(), 10);
- CheckEmptyArray();
-}
-
-TEST_F(CSndLossListTest, BasicRemoveInListNotInNodeHead11)
-{
- EXPECT_EQ(m_lossList->insert(1, 2), 2);
- EXPECT_EQ(m_lossList->insert(5, 6), 2);
- EXPECT_EQ(m_lossList->getLossLength(), 4);
- m_lossList->removeUpTo(5);
- EXPECT_EQ(m_lossList->getLossLength(), 1);
- EXPECT_EQ(m_lossList->insert(1, 2), 2);
- m_lossList->removeUpTo(7);
- EXPECT_EQ(m_lossList->getLossLength(), 0);
- EXPECT_EQ(m_lossList->popLostSeq(), -1);
- CheckEmptyArray();
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-/////////////////////////////////////////////////////////////////////////////////////////
-TEST_F(CSndLossListTest, InsertRemoveInsert01)
-{
- EXPECT_EQ(m_lossList->insert(1, 2), 2);
- EXPECT_EQ(m_lossList->insert(5, 6), 2);
- EXPECT_EQ(m_lossList->getLossLength(), 4);
- m_lossList->removeUpTo(5);
- EXPECT_EQ(m_lossList->getLossLength(), 1);
- EXPECT_EQ(m_lossList->insert(1, 2), 2);
- m_lossList->removeUpTo(6);
- EXPECT_EQ(m_lossList->getLossLength(), 0);
- EXPECT_EQ(m_lossList->popLostSeq(), -1);
- CheckEmptyArray();
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-/////////////////////////////////////////////////////////////////////////////////////////
-TEST_F(CSndLossListTest, InsertHead01)
-{
- EXPECT_EQ(m_lossList->insert(1, 2), 2);
- EXPECT_EQ(m_lossList->getLossLength(), 2);
- EXPECT_EQ(m_lossList->popLostSeq(), 1);
- EXPECT_EQ(m_lossList->getLossLength(), 1);
- EXPECT_EQ(m_lossList->popLostSeq(), 2);
- CheckEmptyArray();
-}
-
-TEST_F(CSndLossListTest, InsertHead02)
-{
- EXPECT_EQ(m_lossList->insert(1, 1), 1);
- EXPECT_EQ(m_lossList->getLossLength(), 1);
- EXPECT_EQ(m_lossList->popLostSeq(), 1);
- CheckEmptyArray();
-}
-
-TEST_F(CSndLossListTest, InsertHeadIncrease01)
-{
- EXPECT_EQ(m_lossList->insert(1, 1), 1);
- EXPECT_EQ(m_lossList->getLossLength(), 1);
- EXPECT_EQ(m_lossList->insert(2, 2), 1);
- EXPECT_EQ(m_lossList->getLossLength(), 2);
- EXPECT_EQ(m_lossList->popLostSeq(), 1);
- EXPECT_EQ(m_lossList->getLossLength(), 1);
- EXPECT_EQ(m_lossList->popLostSeq(), 2);
- CheckEmptyArray();
-}
-
-TEST_F(CSndLossListTest, InsertHeadOverlap01)
-{
- EXPECT_EQ(m_lossList->insert(1, 5), 5);
- EXPECT_EQ(m_lossList->getLossLength(), 5);
- EXPECT_EQ(m_lossList->insert(6, 8), 3);
- EXPECT_EQ(m_lossList->getLossLength(), 8);
- EXPECT_EQ(m_lossList->insert(2, 10), 2);
- EXPECT_EQ(m_lossList->getLossLength(), 10);
- for (int i = 1; i < 11; i++)
- {
- EXPECT_EQ(m_lossList->popLostSeq(), i);
- EXPECT_EQ(m_lossList->getLossLength(), 10 - i);
- }
-
- CheckEmptyArray();
-}
-
-TEST_F(CSndLossListTest, InsertHeadOverlap02)
-{
- EXPECT_EQ(m_lossList->insert(1, 5), 5);
- EXPECT_EQ(m_lossList->getLossLength(), 5);
- EXPECT_EQ(m_lossList->insert(6, 8), 3);
- EXPECT_EQ(m_lossList->getLossLength(), 8);
- EXPECT_EQ(m_lossList->insert(2, 7), 0);
- EXPECT_EQ(m_lossList->getLossLength(), 8);
- EXPECT_EQ(m_lossList->insert(5, 5), 0);
- EXPECT_EQ(m_lossList->getLossLength(), 8);
-
- for (int i = 1; i < 9; i++)
- {
- EXPECT_EQ(m_lossList->popLostSeq(), i);
- EXPECT_EQ(m_lossList->getLossLength(), 8 - i);
- }
-
- CheckEmptyArray();
-}
-
-TEST_F(CSndLossListTest, InsertHeadNegativeOffset01)
-{
- EXPECT_EQ(m_lossList->insert(10000000, 10000000), 1);
- EXPECT_EQ(m_lossList->insert(10000001, 10000001), 1);
- EXPECT_EQ(m_lossList->getLossLength(), 2);
-
- // The offset of the sequence number being added does not fit
- // into the size of the loss list, it must be ignored.
- // Normally this situation should not happen.
- cerr << "Expecting IPE message:" << endl;
- EXPECT_EQ(m_lossList->insert(1, 1), 0);
- EXPECT_EQ(m_lossList->getLossLength(), 2);
- EXPECT_EQ(m_lossList->popLostSeq(), 10000000);
- EXPECT_EQ(m_lossList->getLossLength(), 1);
- EXPECT_EQ(m_lossList->popLostSeq(), 10000001);
-
- CheckEmptyArray();
-}
-
-// Check the part of the loss report the can fit into the list
-// goes into the list.
-TEST_F(CSndLossListTest, InsertHeadNegativeOffset02)
-{
- const int32_t head_seqno = 10000000;
- EXPECT_EQ(m_lossList->insert(head_seqno, head_seqno), 1);
- EXPECT_EQ(m_lossList->insert(head_seqno + 1, head_seqno + 1), 1);
- EXPECT_EQ(m_lossList->getLossLength(), 2);
-
- // The offset of the sequence number being added does not fit
- // into the size of the loss list, it must be ignored.
- // Normally this situation should not happen.
-
- const int32_t outofbound_seqno = head_seqno - CSndLossListTest::SIZE;
- EXPECT_EQ(m_lossList->insert(outofbound_seqno - 1, outofbound_seqno + 1), 3);
- EXPECT_EQ(m_lossList->getLossLength(), 5);
- EXPECT_EQ(m_lossList->popLostSeq(), outofbound_seqno - 1);
- EXPECT_EQ(m_lossList->getLossLength(), 4);
- EXPECT_EQ(m_lossList->popLostSeq(), outofbound_seqno);
- EXPECT_EQ(m_lossList->getLossLength(), 3);
- EXPECT_EQ(m_lossList->popLostSeq(), outofbound_seqno + 1);
- EXPECT_EQ(m_lossList->getLossLength(), 2);
- EXPECT_EQ(m_lossList->popLostSeq(), 10000000);
- EXPECT_EQ(m_lossList->getLossLength(), 1);
- EXPECT_EQ(m_lossList->popLostSeq(), 10000001);
-
- CheckEmptyArray();
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-/////////////////////////////////////////////////////////////////////////////////////////
-TEST_F(CSndLossListTest, InsertFullListCoalesce)
-{
- for (int i = 1; i <= CSndLossListTest::SIZE; i++)
- EXPECT_EQ(m_lossList->insert(i, i), 1);
- EXPECT_EQ(m_lossList->getLossLength(), CSndLossListTest::SIZE);
- // Inserting additional element: 1 item more than list size.
- // Given all elements coalesce into one entry, there is a place to insert it,
- // but sequence span now exceeds list size.
- EXPECT_EQ(m_lossList->insert(CSndLossListTest::SIZE + 1, CSndLossListTest::SIZE + 1), 0);
- EXPECT_EQ(m_lossList->getLossLength(), CSndLossListTest::SIZE);
- for (int i = 1; i <= CSndLossListTest::SIZE; i++)
- {
- EXPECT_EQ(m_lossList->popLostSeq(), i);
- EXPECT_EQ(m_lossList->getLossLength(), CSndLossListTest::SIZE - i);
- }
- EXPECT_EQ(m_lossList->popLostSeq(), -1);
- EXPECT_EQ(m_lossList->getLossLength(), 0);
-
- CheckEmptyArray();
-}
-
-TEST_F(CSndLossListTest, InsertFullListNoCoalesce)
-{
- // We will insert each element with a gap of one elements.
- // This should lead to having space for only [i; SIZE] sequence numbers.
- for (int i = 1; i <= CSndLossListTest::SIZE / 2; i++)
- EXPECT_EQ(m_lossList->insert(2 * i, 2 * i), 1);
-
- // At this point the list has every second element empty
- // [0]:taken, [1]: empty, [2]: taken, [3]: empty, ...
- EXPECT_EQ(m_lossList->getLossLength(), CSndLossListTest::SIZE / 2);
-
- // Inserting additional element out of the list span must fail.
- const int seqno1 = CSndLossListTest::SIZE + 2;
- EXPECT_EQ(m_lossList->insert(seqno1, seqno1), 0);
-
- // There should however be a place for one element right after the last inserted one.
- const int seqno_last = CSndLossListTest::SIZE + 1;
- EXPECT_EQ(m_lossList->insert(seqno_last, seqno_last), 1);
-
- const int initial_length = m_lossList->getLossLength();
- EXPECT_EQ(initial_length, CSndLossListTest::SIZE / 2 + 1);
- for (int i = 1; i <= CSndLossListTest::SIZE / 2; i++)
- {
- EXPECT_EQ(m_lossList->popLostSeq(), 2 * i);
- EXPECT_EQ(m_lossList->getLossLength(), initial_length - i);
- }
- EXPECT_EQ(m_lossList->popLostSeq(), seqno_last);
- EXPECT_EQ(m_lossList->popLostSeq(), -1);
- EXPECT_EQ(m_lossList->getLossLength(), 0);
-
- CheckEmptyArray();
-}
-
-TEST_F(CSndLossListTest, InsertFullListNegativeOffset)
-{
- for (int i = 10000000; i < 10000000 + CSndLossListTest::SIZE; i++)
- m_lossList->insert(i, i);
- EXPECT_EQ(m_lossList->getLossLength(), CSndLossListTest::SIZE);
- m_lossList->insert(1, CSndLossListTest::SIZE + 1);
- EXPECT_EQ(m_lossList->getLossLength(), CSndLossListTest::SIZE);
- for (int i = 10000000; i < 10000000 + CSndLossListTest::SIZE; i++)
- {
- EXPECT_EQ(m_lossList->popLostSeq(), i);
- EXPECT_EQ(m_lossList->getLossLength(), CSndLossListTest::SIZE - (i - 10000000 + 1));
- }
- EXPECT_EQ(m_lossList->popLostSeq(), -1);
- EXPECT_EQ(m_lossList->getLossLength(), 0);
-
- CheckEmptyArray();
-}
-
-TEST_F(CSndLossListTest, InsertPositiveOffsetTooFar)
-{
- const int32_t head_seqno = 1000;
- EXPECT_EQ(m_lossList->insert(head_seqno, head_seqno), 1);
- EXPECT_EQ(m_lossList->getLossLength(), 1);
-
- // The offset of the sequence number being added does not fit
- // into the size of the loss list, it must be ignored.
- // Normally this situation should not happen.
-
- const int32_t outofbound_seqno = head_seqno + CSndLossListTest::SIZE;
- m_lossList->insert(outofbound_seqno, outofbound_seqno);
-
- const int32_t outofbound_seqno2 = head_seqno + 2 * CSndLossListTest::SIZE;
- m_lossList->insert(outofbound_seqno2, outofbound_seqno2);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-/////////////////////////////////////////////////////////////////////////////////////////
-TEST_F(CSndLossListTest, InsertNoUpdateElement01)
-{
- EXPECT_EQ(m_lossList->insert(0, 1), 2);
- EXPECT_EQ(m_lossList->insert(3, 5), 3);
- m_lossList->removeUpTo(3); // Remove all to seq no 3
- EXPECT_EQ(m_lossList->insert(4, 5), 0); // Element not updated
- EXPECT_EQ(m_lossList->getLossLength(), 2);
- EXPECT_EQ(m_lossList->popLostSeq(), 4);
- EXPECT_EQ(m_lossList->popLostSeq(), 5);
-}
-
-TEST_F(CSndLossListTest, InsertNoUpdateElement03)
-{
- EXPECT_EQ(m_lossList->insert(1, 5), 5);
- EXPECT_EQ(m_lossList->getLossLength(), 5);
- EXPECT_EQ(m_lossList->insert(6, 8), 3);
- EXPECT_EQ(m_lossList->getLossLength(), 8);
- EXPECT_EQ(m_lossList->insert(2, 5), 0);
- EXPECT_EQ(m_lossList->getLossLength(), 8);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-/////////////////////////////////////////////////////////////////////////////////////////
-TEST_F(CSndLossListTest, InsertUpdateElement01)
-{
- EXPECT_EQ(m_lossList->insert(1, 5), 5);
- EXPECT_EQ(m_lossList->getLossLength(), 5);
- EXPECT_EQ(m_lossList->insert(1, 8), 3);
- EXPECT_EQ(m_lossList->getLossLength(), 8);
- EXPECT_EQ(m_lossList->insert(2, 5), 0);
- EXPECT_EQ(m_lossList->getLossLength(), 8);
-}
+++ /dev/null
-#include <gtest/gtest.h>
-#include <chrono>
-#include <string>
-#include <map>
-
-#ifdef _WIN32
-#define INC_SRT_WIN_WINTIME // exclude gettimeofday from srt headers
-#endif
-
-#include "srt.h"
-#include "utilities.h"
-
-
-srt_listen_callback_fn SrtTestListenCallback;
-
-/**
- * This test makes a service and a client connecting to it.
- * The service sets up a callback function on the listener.
- * The listener sets up different passwords depending on the user.
- * The test tests:
- * - correct connection with correct password
- * - rejected connection with wrong password
- * - rejected connection on nonexistent user
-*/
-TEST(Core, ListenCallback) {
-
- using namespace std;
-
- ASSERT_EQ(srt_startup(), 0);
-
- // Create server on 127.0.0.1:5555
-
- const SRTSOCKET server_sock = srt_create_socket();
- ASSERT_GT(server_sock, 0); // socket_id should be > 0
-
- sockaddr_in bind_sa;
- memset(&bind_sa, 0, sizeof bind_sa);
- bind_sa.sin_family = AF_INET;
- ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &bind_sa.sin_addr), 1);
- bind_sa.sin_port = htons(5555);
-
- ASSERT_NE(srt_bind(server_sock, (sockaddr*)&bind_sa, sizeof bind_sa), -1);
- ASSERT_NE(srt_listen(server_sock, 5), -1);
- (void)srt_listen_callback(server_sock, &SrtTestListenCallback, NULL);
-
- // Create client to connect to the above server
- SRTSOCKET client_sock;
- sockaddr_in sa;
- memset(&sa, 0, sizeof sa);
- sa.sin_family = AF_INET;
- sa.sin_port = htons(5555);
- ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1);
- sockaddr* psa = (sockaddr*)&sa;
-
-
- cerr << "TEST 1: Connect to an encrypted socket correctly (should succeed)\n";
-
- client_sock = srt_create_socket();
- ASSERT_GT(client_sock, 0); // socket_id should be > 0
-
- string username_spec = "#!::u=admin";
- string password = "thelocalmanager";
-
- ASSERT_NE(srt_setsockflag(client_sock, SRTO_STREAMID, username_spec.c_str(), username_spec.size()), -1);
-#if SRT_ENABLE_ENCRYPTION
- ASSERT_NE(srt_setsockflag(client_sock, SRTO_PASSPHRASE, password.c_str(), password.size()), -1);
-#endif
-
- // EXPECTED RESULT: connected successfully
- EXPECT_NE(srt_connect(client_sock, psa, sizeof sa), SRT_ERROR);
-
- // Close the socket
- EXPECT_EQ(srt_close(client_sock), SRT_SUCCESS);
-
-
- cerr << "TEST 2: Connect with a wrong password (should reject the handshake)\n";
-#if SRT_ENABLE_ENCRYPTION
- client_sock = srt_create_socket();
- ASSERT_GT(client_sock, 0); // socket_id should be > 0
-
- password = "thelokalmanager"; // (typo :D)
-
- ASSERT_NE(srt_setsockflag(client_sock, SRTO_STREAMID, username_spec.c_str(), username_spec.size()), -1);
- ASSERT_NE(srt_setsockflag(client_sock, SRTO_PASSPHRASE, password.c_str(), password.size()), -1);
-
- // EXPECTED RESULT: connection rejected
- EXPECT_EQ(srt_connect(client_sock, psa, sizeof sa), SRT_ERROR);
-
- // Close the socket
- EXPECT_EQ(srt_close(client_sock), SRT_SUCCESS);
-#endif
-
-
- cerr << "TEST 3: Connect with wrong username (should exit on exception)\n";
- client_sock = srt_create_socket();
- ASSERT_GT(client_sock, 0); // socket_id should be > 0
-
- username_spec = "#!::u=haivision";
- password = "thelocalmanager"; // (typo :D)
-
- ASSERT_NE(srt_setsockflag(client_sock, SRTO_STREAMID, username_spec.c_str(), username_spec.size()), -1);
-#if SRT_ENABLE_ENCRYPTION
- ASSERT_NE(srt_setsockflag(client_sock, SRTO_PASSPHRASE, password.c_str(), password.size()), -1);
-#endif
-
- // EXPECTED RESULT: connection rejected
- EXPECT_EQ(srt_connect(client_sock, psa, sizeof sa), SRT_ERROR);
-
- // Close the socket
- EXPECT_EQ(srt_close(client_sock), SRT_SUCCESS);
-
- (void)srt_cleanup();
-}
-
-int SrtTestListenCallback(void* opaq, SRTSOCKET ns SRT_ATR_UNUSED, int hsversion, const struct sockaddr* peeraddr, const char* streamid)
-{
- using namespace std;
-
- if (opaq)
- {
- cerr << "ERROR: opaq expected NULL, as passed\n";
- return -1; // enforce EXPECT to fail
- }
-
- if (hsversion != 5)
- {
- cerr << "ERROR: hsversion expected 5\n";
- return -1;
- }
-
- if (!peeraddr)
- {
- // XXX Might be better to check the content, too.
- cerr << "ERROR: null peeraddr\n";
- return -1;
- }
-
- static const map<string, string> passwd {
- {"admin", "thelocalmanager"},
- {"user", "verylongpassword"}
- };
-
- // Try the "standard interpretation" with username at key u
- string username;
-
- static const char stdhdr [] = "#!::";
- uint32_t* pattern = (uint32_t*)stdhdr;
- bool found = false;
-
- if (strlen(streamid) > 4 && *(uint32_t*)streamid == *pattern)
- {
- vector<string> items;
- Split(streamid+4, ',', back_inserter(items));
- for (auto& i: items)
- {
- vector<string> kv;
- Split(i, '=', back_inserter(kv));
- if (kv.size() == 2 && kv[0] == "u")
- {
- username = kv[1];
- found = true;
- }
- }
-
- if (!found)
- {
- cerr << "TEST: USER NOT FOUND, returning false.\n";
- return -1;
- }
- }
- else
- {
- // By default the whole streamid is username
- username = streamid;
- }
-
- // This hook sets the password to the just accepted socket
- // depending on the user
-
- // When not found, it will throw an exception
- cerr << "TEST: Accessing user '" << username << "', might throw if not found\n";
- string exp_pw = passwd.at(username);
-
-#if SRT_ENABLE_ENCRYPTION
- cerr << "TEST: Setting password '" << exp_pw << "' as per user '" << username << "'\n";
- EXPECT_EQ(srt_setsockflag(ns, SRTO_PASSPHRASE, exp_pw.c_str(), exp_pw.size()), SRT_SUCCESS);
-#endif
-
- // Checking that SRTO_RCVLATENCY (PRE option) can be altered in the listener callback.
- int optval = 200;
- EXPECT_EQ(srt_setsockflag(ns, SRTO_RCVLATENCY, &optval, sizeof optval), SRT_SUCCESS);
- return 0;
-}
-
-
+++ /dev/null
-#define _CRT_RAND_S // For Windows, rand_s
-
-#include <gtest/gtest.h>
-#include <array>
-#include <chrono>
-#include <future>
-#include <random>
-
-#ifdef _WIN32
-#include <stdlib.h>
-#define rand_r rand_s
-#define INC_SRT_WIN_WINTIME // exclude gettimeofday from srt headers
-#else
-typedef int SOCKET;
-#define INVALID_SOCKET ((SOCKET)-1)
-#define closesocket close
-#endif
-
-#include"platform_sys.h"
-#include "srt.h"
-#include "netinet_any.h"
-#include "api.h"
-
-using namespace std;
-using srt::sockaddr_any;
-
-
-class TestConnection
- : public ::testing::Test
-{
-protected:
- TestConnection()
- {
- // initialization code here
- }
-
- ~TestConnection()
- {
- // cleanup any pending stuff, but no exceptions allowed
- }
-
- // It should be as much as possible, but how many sockets can
- // be withstood, depends on the platform. Currently used CI test
- // servers seem not to withstand more than 240.
- static const size_t NSOCK = 60;
-
-protected:
- // SetUp() is run immediately before a test starts.
- void SetUp() override
- {
- ASSERT_EQ(srt_startup(), 0);
-
- m_sa.sin_family = AF_INET;
- m_sa.sin_addr.s_addr = INADDR_ANY;
-
- m_server_sock = srt_create_socket();
- ASSERT_NE(m_server_sock, SRT_INVALID_SOCK);
-
- // Find a port not used by another service.
- int bind_res = 0;
- const sockaddr* psa = reinterpret_cast<const sockaddr*>(&m_sa);
- for (int port = 5000; port <= 5100; ++port)
- {
- m_sa.sin_port = htons(port);
-
- bind_res = srt_bind(m_server_sock, psa, sizeof m_sa);
- if (bind_res == 0)
- {
- cerr << "Running test on port " << port << "\n";
- break;
- }
- }
-
- ASSERT_GE(bind_res, 0) << "srt_bind returned " << bind_res << ": " << srt_getlasterror_str();
- ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &m_sa.sin_addr), 1);
-
- // Fill the buffer with random data
- std::random_device rnd_device;
- std::mt19937 gen(rnd_device());
- std::uniform_int_distribution<short> dis(-128, 127);
- std::generate(m_buf.begin(), m_buf.end(), [dis, gen]() mutable { return (char)dis(gen); });
-
- ASSERT_NE(srt_listen(m_server_sock, NSOCK), -1);
- }
-
- void TearDown() override
- {
- srt_cleanup();
- }
-
- void AcceptLoop()
- {
- for (;;)
- {
- sockaddr_any addr;
- int len = sizeof addr;
- int acp = srt_accept(m_server_sock, addr.get(), &len);
- if (acp == -1)
- {
- cerr << "[T] Accept error at " << m_accepted.size()
- << "/" << NSOCK << ": " << srt_getlasterror_str() << endl;
- break;
- }
- m_accepted.push_back(acp);
- }
-
- cerr << "[T] Closing those accepted ones\n";
- m_accept_exit = true;
-
- for (const auto s : m_accepted)
- {
- srt_close(s);
- }
-
- cerr << "[T] End Accept Loop\n";
- }
-
-protected:
- sockaddr_in m_sa = sockaddr_in();
- SRTSOCKET m_server_sock = SRT_INVALID_SOCK;
- vector<SRTSOCKET> m_accepted;
- std::array<char, SRT_LIVE_DEF_PLSIZE> m_buf;
- SRTSOCKET m_connections[NSOCK];
- volatile bool m_accept_exit = false;
-};
-
-
-// This test establishes multiple connections to a single SRT listener on a localhost port.
-// Packets are submitted for sending to all those connections in a non-blocking mode.
-// Then all connections are closed. Some sockets may potentially still have undelivered packets.
-// This test tries to reproduce the issue described in #1182, and fixed by #1315.
-TEST_F(TestConnection, Multiple)
-{
- const sockaddr_in lsa = m_sa;
- const sockaddr* psa = reinterpret_cast<const sockaddr*>(&lsa);
-
- auto ex = std::async(std::launch::async, [this] { return AcceptLoop(); });
-
- cerr << "Opening " << NSOCK << " connections\n";
-
- for (size_t i = 0; i < NSOCK; i++)
- {
- m_connections[i] = srt_create_socket();
- EXPECT_NE(m_connections[i], SRT_INVALID_SOCK);
-
- // Give it 60s timeout, many platforms fail to process
- // so many connections in a short time.
- int conntimeo = 60;
- srt_setsockflag(m_connections[i], SRTO_CONNTIMEO, &conntimeo, sizeof conntimeo);
-
- //cerr << "Connecting #" << i << " to " << sockaddr_any(psa).str() << "...\n";
- //cerr << "Connecting to: " << sockaddr_any(psa).str() << endl;
- ASSERT_NE(srt_connect(m_connections[i], psa, sizeof lsa), SRT_ERROR);
-
- // Set now async sending so that sending isn't blocked
- int no = 0;
- ASSERT_NE(srt_setsockflag(m_connections[i], SRTO_SNDSYN, &no, sizeof no), -1);
- }
-
- for (size_t j = 1; j <= 100; j++)
- {
- for (size_t i = 0; i < NSOCK; i++)
- {
- EXPECT_GT(srt_send(m_connections[i], m_buf.data(), (int) m_buf.size()), 0);
- }
- }
- cerr << "Sending finished, closing caller sockets\n";
-
- for (size_t i = 0; i < NSOCK; i++)
- {
- EXPECT_EQ(srt_close(m_connections[i]), SRT_SUCCESS);
- }
-
- EXPECT_FALSE(m_accept_exit) << "AcceptLoop already broken for some reason!";
- // Up to this moment the server sock should survive
- cerr << "Closing server socket\n";
- // Close server socket to break the accept loop
- EXPECT_EQ(srt_close(m_server_sock), 0);
-
- cerr << "Synchronize with the accepting thread\n";
- ex.wait();
- cerr << "Synchronization done\n";
-}
-
-
-
+++ /dev/null
-#include "gtest/gtest.h"
-#include <thread>
-#include "srt.h"
-
-class TestMuxer
- : public ::testing::Test
-{
-protected:
- TestMuxer()
- {
- // initialization code here
- }
-
- ~TestMuxer()
- {
- // cleanup any pending stuff, but no exceptions allowed
- }
-
-protected:
- // SetUp() is run immediately before a test starts.
- void SetUp()
- {
- ASSERT_GE(srt_startup(), 0);
-
- m_caller_sock = srt_create_socket();
- ASSERT_NE(m_caller_sock, SRT_ERROR);
-
- m_server_pollid = srt_epoll_create();
- ASSERT_NE(SRT_ERROR, m_server_pollid);
-
- m_client_pollid = srt_epoll_create();
- ASSERT_NE(SRT_ERROR, m_client_pollid);
-
- int yes = 1;
- int no = 0;
- ASSERT_NE(srt_setsockopt(m_caller_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect
- ASSERT_NE(srt_setsockflag(m_caller_sock, SRTO_SENDER, &yes, sizeof yes), SRT_ERROR);
- ASSERT_NE(srt_setsockopt(m_caller_sock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR);
-
- int epoll_out = SRT_EPOLL_OUT;
- srt_epoll_add_usock(m_client_pollid, m_caller_sock, &epoll_out);
- }
-
- void TearDown()
- {
- // Code here will be called just after the test completes.
- // OK to throw exceptions from here if needed.
- srt_epoll_release(m_client_pollid);
- srt_epoll_release(m_server_pollid);
- srt_close(m_listener_sock_ipv4);
- srt_close(m_listener_sock_ipv6);
- srt_cleanup();
- }
-
-public:
- void ClientThread()
- {
- sockaddr_in sa;
- memset(&sa, 0, sizeof sa);
- sa.sin_family = AF_INET;
- sa.sin_port = htons(m_listen_port);
- ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1);
- ASSERT_NE(srt_connect(m_caller_sock, (sockaddr*)&sa, sizeof sa), SRT_ERROR);
-
- // Socket readiness for connection is checked by polling on WRITE allowed sockets.
- {
- int rlen = 2;
- SRTSOCKET read[2];
-
- int wlen = 2;
- SRTSOCKET write[2];
-
- ASSERT_NE(srt_epoll_wait(m_client_pollid, read, &rlen,
- write, &wlen,
- -1, // -1 is set for debuging purpose.
- // in case of production we need to set appropriate value
- 0, 0, 0, 0), SRT_ERROR);
-
- ASSERT_EQ(rlen, 0); // get exactly one write event without reads
- ASSERT_EQ(wlen, 1); // get exactly one write event without reads
- ASSERT_EQ(write[0], m_caller_sock); // for our client socket
- }
-
- char buffer[1316] = {1, 2, 3, 4};
- ASSERT_NE(srt_sendmsg(m_caller_sock, buffer, sizeof buffer,
- -1, // infinit ttl
- true // in order must be set to true
- ), SRT_ERROR);
- }
-
-protected:
- SRTSOCKET m_caller_sock;
- SRTSOCKET m_listener_sock_ipv4;
- SRTSOCKET m_listener_sock_ipv6;
- int m_client_pollid = SRT_ERROR;
- int m_server_pollid = SRT_ERROR;
- const int m_listen_port = 4200;
-};
-
-
-TEST_F(TestMuxer, IPv4_and_IPv6)
-{
- int yes = 1;
- int no = 0;
-
- // 1. Create IPv4 listening socket
- m_listener_sock_ipv4 = srt_create_socket();
- ASSERT_NE(m_listener_sock_ipv4, SRT_ERROR);
- ASSERT_NE(srt_setsockopt(m_listener_sock_ipv4, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect
- ASSERT_NE(srt_setsockopt(m_listener_sock_ipv4, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR);
-
- // 2. Add the IPv4 socket to epoll
- int epoll_in = SRT_EPOLL_IN;
- srt_epoll_add_usock(m_server_pollid, m_listener_sock_ipv4, &epoll_in);
-
- // 3. Bind to IPv4 address.
- sockaddr_in sa;
- memset(&sa, 0, sizeof sa);
- sa.sin_family = AF_INET;
- sa.sin_port = htons(m_listen_port);
- ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1);
- ASSERT_NE(srt_bind(m_listener_sock_ipv4, (sockaddr*)&sa, sizeof sa), SRT_ERROR);
- ASSERT_NE(srt_listen(m_listener_sock_ipv4, SOMAXCONN), SRT_ERROR);
-
- // 4. Create IPv6 socket bound to the same port as IPv4 socket
- m_listener_sock_ipv6 = srt_create_socket();
- ASSERT_NE(m_listener_sock_ipv6, SRT_ERROR);
- sockaddr_in6 sa_v6;
- memset(&sa_v6, 0, sizeof sa_v6);
- sa_v6.sin6_family = AF_INET6;
- sa_v6.sin6_port = htons(m_listen_port);
- ASSERT_EQ(inet_pton(AF_INET6, "::1", &sa_v6.sin6_addr), 1);
-
- // Set the IPv6only flag for the socket that should be bound to the same port
- // as another socket binding to IPv4 address, otherwise the binding may fail,
- // depending on the current value of IPV6ONLY option.
- ASSERT_EQ(srt_setsockflag(m_listener_sock_ipv6, SRTO_IPV6ONLY, &yes, sizeof yes), 0);
- ASSERT_NE(srt_bind(m_listener_sock_ipv6, (sockaddr*)&sa_v6, sizeof(sa_v6)), SRT_ERROR);
-
- std::thread client(&TestMuxer::ClientThread, this);
-
- { // wait for connection from client
- int rlen = 2;
- SRTSOCKET read[2];
-
- int wlen = 2;
- SRTSOCKET write[2];
-
- ASSERT_NE(srt_epoll_wait(m_server_pollid,
- read, &rlen,
- write, &wlen,
- -1, // -1 is set for debuging purpose.
- // in case of production we need to set appropriate value
- 0, 0, 0, 0), SRT_ERROR );
-
- ASSERT_EQ(rlen, 1); // get exactly one read event without writes
- ASSERT_EQ(read[0], m_listener_sock_ipv4) << "Read event on wrong socket";
- }
-
- sockaddr_storage scl;
- int sclen = sizeof scl;
-
- SRTSOCKET accepted_sock = srt_accept(m_listener_sock_ipv4, (sockaddr*)&scl, &sclen);
- ASSERT_NE(accepted_sock, SRT_INVALID_SOCK);
-
- srt_epoll_add_usock(m_server_pollid, accepted_sock, &epoll_in); // wait for input
-
- char buffer[1316];
- { // wait for 1316 packet from client
- int rlen = 2;
- SRTSOCKET read[2];
-
- int wlen = 2;
- SRTSOCKET write[2];
-
- ASSERT_NE(srt_epoll_wait(m_server_pollid,
- read, &rlen,
- write, &wlen,
- -1, // -1 is set for debuging purpose.
- // in case of production we need to set appropriate value
- 0, 0, 0, 0), SRT_ERROR );
-
-
- ASSERT_EQ(rlen, 1); // get exactly one read event without writes
- //ASSERT_EQ(wlen, 0); // get exactly one read event without writes
- ASSERT_EQ(read[0], accepted_sock); // read event is for bind socket
- }
-
- char pattern[4] = {1, 2, 3, 4};
- ASSERT_EQ(srt_recvmsg(accepted_sock, buffer, sizeof buffer), 1316);
- ASSERT_EQ(memcmp(pattern, buffer, 4), 0);
-
- srt_close(accepted_sock);
- client.join();
-}
+++ /dev/null
-#include "gtest/gtest.h"
-#include <thread>
-#ifndef _WIN32
-#include <ifaddrs.h>
-#endif
-
-#include "common.h"
-#include "srt.h"
-#include "udt.h"
-
-using srt::sockaddr_any;
-
-// Copied from ../apps/apputil.cpp, can't really link this file here.
-sockaddr_any CreateAddr(const std::string& name, unsigned short port, int pref_family = AF_INET)
-{
- using namespace std;
-
- // Handle empty name.
- // If family is specified, empty string resolves to ANY of that family.
- // If not, it resolves to IPv4 ANY (to specify IPv6 any, use [::]).
- if (name == "")
- {
- sockaddr_any result(pref_family == AF_INET6 ? pref_family : AF_INET);
- result.hport(port);
- return result;
- }
-
- bool first6 = pref_family != AF_INET;
- int families[2] = {AF_INET6, AF_INET};
- if (!first6)
- {
- families[0] = AF_INET;
- families[1] = AF_INET6;
- }
-
- for (int i = 0; i < 2; ++i)
- {
- int family = families[i];
- sockaddr_any result (family);
-
- // Try to resolve the name by pton first
- if (inet_pton(family, name.c_str(), result.get_addr()) == 1)
- {
- result.hport(port); // same addr location in ipv4 and ipv6
- return result;
- }
- }
-
- // If not, try to resolve by getaddrinfo
- // This time, use the exact value of pref_family
-
- sockaddr_any result;
- addrinfo fo = {
- 0,
- pref_family,
- 0, 0,
- 0, 0,
- NULL, NULL
- };
-
- addrinfo* val = nullptr;
- int erc = getaddrinfo(name.c_str(), nullptr, &fo, &val);
- if (erc == 0)
- {
- result.set(val->ai_addr);
- result.len = result.size();
- result.hport(port); // same addr location in ipv4 and ipv6
- }
- freeaddrinfo(val);
-
- return result;
-}
-
-#ifdef _WIN32
-
-// On Windows there's a function for it, but it requires an extra
-// iphlp library to be attached to the executable, which is kinda
-// problematic. Temporarily block tests using this function on Windows.
-
-std::string GetLocalIP()
-{
- std::cout << "!!!WARNING!!!: GetLocalIP not supported, test FORCEFULLY passed\n";
- return "";
-}
-#else
-struct IfAddr
-{
- ifaddrs* handle;
- IfAddr()
- {
- getifaddrs(&handle);
- }
-
- ~IfAddr()
- {
- freeifaddrs(handle);
- }
-};
-
-std::string GetLocalIP()
-{
- struct ifaddrs * ifa=NULL;
- void * tmpAddrPtr=NULL;
-
- IfAddr ifAddr;
-
- for (ifa = ifAddr.handle; ifa != NULL; ifa = ifa->ifa_next)
- {
- if (!ifa->ifa_addr)
- {
- continue;
- }
-
- if (ifa->ifa_addr->sa_family == AF_INET)
- {
- // is a valid IP4 Address
- sockaddr_in* psin = (struct sockaddr_in *)ifa->ifa_addr;
- tmpAddrPtr=&psin->sin_addr;
-
- if (ntohl(psin->sin_addr.s_addr) == 0x7f000001) // Skip 127.0.0.1
- continue;
-
- char addressBuffer[INET_ADDRSTRLEN] = "";
- inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
- return addressBuffer;
- printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer);
- } else if (ifa->ifa_addr->sa_family == AF_INET6) { // check it is IP6
- // is a valid IP6 Address
- tmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
- char addressBuffer[INET6_ADDRSTRLEN] = "";
- inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
- return addressBuffer;
- }
- }
-
- return "";
-}
-#endif
-
-int client_pollid = SRT_ERROR;
-SRTSOCKET m_client_sock = SRT_ERROR;
-
-void clientSocket(std::string ip, int port, bool expect_success)
-{
- int yes = 1;
- int no = 0;
-
- int family = AF_INET;
- if (ip.substr(0, 2) == "6.")
- {
- family = AF_INET6;
- ip = ip.substr(2);
- }
-
- m_client_sock = srt_create_socket();
- ASSERT_NE(m_client_sock, SRT_ERROR);
-
- ASSERT_NE(srt_setsockopt(m_client_sock, 0, SRTO_SNDSYN, &no, sizeof no), SRT_ERROR); // for async connect
- ASSERT_NE(srt_setsockflag(m_client_sock, SRTO_SENDER, &yes, sizeof yes), SRT_ERROR);
-
- ASSERT_NE(srt_setsockopt(m_client_sock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR);
-
- int epoll_out = SRT_EPOLL_OUT;
- srt_epoll_add_usock(client_pollid, m_client_sock, &epoll_out);
-
- sockaddr_any sa = CreateAddr(ip, port, family);
-
- std::cout << "Connecting to: " << sa.str() << std::endl;
-
- int connect_res = srt_connect(m_client_sock, sa.get(), sa.size());
-
- if (connect_res == -1)
- {
- std::cout << "srt_connect: " << srt_getlasterror_str() << std::endl;
- }
-
- if (expect_success)
- {
- EXPECT_NE(connect_res, -1);
- // Socket readiness for connection is checked by polling on WRITE allowed sockets.
-
- {
- int rlen = 2;
- SRTSOCKET read[2];
-
- int wlen = 2;
- SRTSOCKET write[2];
-
- EXPECT_NE(srt_epoll_wait(client_pollid, read, &rlen,
- write, &wlen,
- -1, // -1 is set for debuging purpose.
- // in case of production we need to set appropriate value
- 0, 0, 0, 0), SRT_ERROR);
-
-
- EXPECT_EQ(rlen, 0); // get exactly one write event without reads
- EXPECT_EQ(wlen, 1); // get exactly one write event without reads
- EXPECT_EQ(write[0], m_client_sock); // for our client socket
- }
-
- char buffer[1316] = {1, 2, 3, 4};
- EXPECT_NE(srt_sendmsg(m_client_sock, buffer, sizeof buffer,
- -1, // infinit ttl
- true // in order must be set to true
- ),
- SRT_ERROR);
- }
- else
- {
- EXPECT_EQ(connect_res, -1);
- }
-
- std::cout << "Client exit\n";
-}
-
-int server_pollid = SRT_ERROR;
-
-void serverSocket(std::string ip, int port, bool expect_success)
-{
- int yes = 1;
- int no = 0;
-
- SRTSOCKET m_bindsock = srt_create_socket();
- ASSERT_NE(m_bindsock, SRT_ERROR);
-
- ASSERT_NE(srt_setsockopt(m_bindsock, 0, SRTO_RCVSYN, &no, sizeof no), SRT_ERROR); // for async connect
- ASSERT_NE(srt_setsockopt(m_bindsock, 0, SRTO_TSBPDMODE, &yes, sizeof yes), SRT_ERROR);
-
- int epoll_in = SRT_EPOLL_IN;
- srt_epoll_add_usock(server_pollid, m_bindsock, &epoll_in);
-
- sockaddr_any sa = CreateAddr(ip, port);
-
- std::cout << "Bind to: " << sa.str() << std::endl;
-
- int bind_res = srt_bind(m_bindsock, sa.get(), sa.size());
- if (!expect_success)
- {
- std::cout << "Binding should fail: " << srt_getlasterror_str() << std::endl;
- ASSERT_EQ(bind_res, SRT_ERROR);
- return;
- }
-
- ASSERT_NE(bind_res, SRT_ERROR);
-
- ASSERT_NE(srt_listen(m_bindsock, SOMAXCONN), SRT_ERROR);
-
- if (ip == "0.0.0.0")
- ip = "127.0.0.1"; // override wildcard
- else if ( ip == "::")
- ip = "::1";
-
- std::thread client(clientSocket, ip, port, expect_success);
-
- { // wait for connection from client
- int rlen = 2;
- SRTSOCKET read[2];
-
- int wlen = 2;
- SRTSOCKET write[2];
-
- ASSERT_NE(srt_epoll_wait(server_pollid,
- read, &rlen,
- write, &wlen,
- 3000, // -1 is set for debuging purpose.
- // in case of production we need to set appropriate value
- 0, 0, 0, 0), SRT_ERROR );
-
-
- ASSERT_EQ(rlen, 1); // get exactly one read event without writes
- ASSERT_EQ(wlen, 0); // get exactly one read event without writes
- ASSERT_EQ(read[0], m_bindsock); // read event is for bind socket
- }
-
- sockaddr_any scl;
-
- SRTSOCKET m_sock = srt_accept(m_bindsock, scl.get(), &scl.len);
- if (m_sock == -1)
- {
- std::cout << "srt_accept: " << srt_getlasterror_str() << std::endl;
- }
- ASSERT_NE(m_sock, SRT_INVALID_SOCK);
-
- sockaddr_any showacp = (sockaddr*)&scl;
- std::cout << "Accepted from: " << showacp.str() << std::endl;
-
- srt_epoll_add_usock(server_pollid, m_sock, &epoll_in); // wait for input
-
- char buffer[1316];
- { // wait for 1316 packet from client
- int rlen = 2;
- SRTSOCKET read[2];
-
- int wlen = 2;
- SRTSOCKET write[2];
-
- ASSERT_NE(srt_epoll_wait(server_pollid,
- read, &rlen,
- write, &wlen,
- -1, // -1 is set for debuging purpose.
- // in case of production we need to set appropriate value
- 0, 0, 0, 0), SRT_ERROR );
-
-
- ASSERT_EQ(rlen, 1); // get exactly one read event without writes
- ASSERT_EQ(wlen, 0); // get exactly one read event without writes
- ASSERT_EQ(read[0], m_sock); // read event is for bind socket
- }
-
- char pattern[4] = {1, 2, 3, 4};
-
- ASSERT_EQ(srt_recvmsg(m_sock, buffer, sizeof buffer),
- 1316);
-
- EXPECT_EQ(memcmp(pattern, buffer, sizeof pattern), 0);
-
- client.join();
- srt_close(m_sock);
- srt_close(m_bindsock);
- srt_close(m_client_sock); // cannot close m_client_sock after srt_sendmsg because of issue in api.c:2346
-
- std::cout << "Server exit\n";
-}
-
-TEST(ReuseAddr, SameAddr1)
-{
- ASSERT_EQ(srt_startup(), 0);
-
- client_pollid = srt_epoll_create();
- ASSERT_NE(SRT_ERROR, client_pollid);
-
- server_pollid = srt_epoll_create();
- ASSERT_NE(SRT_ERROR, server_pollid);
-
- std::thread server_1(serverSocket, "127.0.0.1", 5000, true);
- server_1.join();
-
- std::thread server_2(serverSocket, "127.0.0.1", 5000, true);
- server_2.join();
-
- (void)srt_epoll_release(client_pollid);
- (void)srt_epoll_release(server_pollid);
- srt_cleanup();
-}
-
-TEST(ReuseAddr, SameAddr2)
-{
- std::string localip = GetLocalIP();
- if (localip == "")
- return; // DISABLE TEST if this doesn't work.
-
- ASSERT_EQ(srt_startup(), 0);
-
- client_pollid = srt_epoll_create();
- ASSERT_NE(SRT_ERROR, client_pollid);
-
- server_pollid = srt_epoll_create();
- ASSERT_NE(SRT_ERROR, server_pollid);
-
- std::thread server_1(serverSocket, localip, 5000, true);
- server_1.join();
-
- std::thread server_2(serverSocket, localip, 5000, true);
- server_2.join();
-
- (void)srt_epoll_release(client_pollid);
- (void)srt_epoll_release(server_pollid);
- srt_cleanup();
-}
-
-TEST(ReuseAddr, DiffAddr)
-{
- std::string localip = GetLocalIP();
- if (localip == "")
- return; // DISABLE TEST if this doesn't work.
-
- ASSERT_EQ(srt_startup(), 0);
-
- client_pollid = srt_epoll_create();
- ASSERT_NE(SRT_ERROR, client_pollid);
-
- server_pollid = srt_epoll_create();
- ASSERT_NE(SRT_ERROR, server_pollid);
-
- serverSocket("127.0.0.1", 5000, true);
- serverSocket(localip, 5000, true);
-
- (void)srt_epoll_release(client_pollid);
- (void)srt_epoll_release(server_pollid);
- srt_cleanup();
-}
-
-TEST(ReuseAddr, Wildcard)
-{
-#if defined(_WIN32) || defined(CYGWIN)
- std::cout << "!!!WARNING!!!: On Windows connection to localhost this way isn't possible.\n"
- "Forcing test to pass, PLEASE FIX.\n";
- return;
-#endif
- std::string localip = GetLocalIP();
- if (localip == "")
- return; // DISABLE TEST if this doesn't work.
-
- ASSERT_EQ(srt_startup(), 0);
-
- client_pollid = srt_epoll_create();
- ASSERT_NE(SRT_ERROR, client_pollid);
-
- server_pollid = srt_epoll_create();
- ASSERT_NE(SRT_ERROR, server_pollid);
-
- serverSocket("0.0.0.0", 5000, true);
- serverSocket(localip, 5000, false);
-
- (void)srt_epoll_release(client_pollid);
- (void)srt_epoll_release(server_pollid);
- srt_cleanup();
-}
-
-
-TEST(ReuseAddr, ProtocolVersion)
-{
-#if defined(_WIN32) || defined(CYGWIN)
- std::cout << "!!!WARNING!!!: On Windows connection to localhost this way isn't possible.\n"
- "Forcing test to pass, PLEASE FIX.\n";
- return;
-#endif
- ASSERT_EQ(srt_startup(), 0);
-
- client_pollid = srt_epoll_create();
- ASSERT_NE(SRT_ERROR, client_pollid);
-
- server_pollid = srt_epoll_create();
- ASSERT_NE(SRT_ERROR, server_pollid);
-
- serverSocket("::", 5000, true);
- serverSocket("0.0.0.0", 5000, true);
-
- (void)srt_epoll_release(client_pollid);
- (void)srt_epoll_release(server_pollid);
- srt_cleanup();
-}
+++ /dev/null
-#include "gtest/gtest.h"
-#include "common.h"
-#include "core.h"
-
-using namespace srt;
-
-
-const int32_t CSeqNo::m_iSeqNoTH;
-const int32_t CSeqNo::m_iMaxSeqNo;
-
-
-TEST(CSeqNo, constants)
-{
- // Compare two seq#, considering the wraping.
- EXPECT_EQ(CSeqNo::m_iMaxSeqNo, 0x7FFFFFFF);
- EXPECT_EQ(CSeqNo::m_iSeqNoTH, 0x3FFFFFFF);
-}
-
-TEST(CSeqNo, seqcmp)
-{
- // Compare two seq#, considering the wraping.
- EXPECT_EQ(CSeqNo::seqcmp(0x7FFFFFFF, 0x7FFFFFFF), 0);
-
- // abs(seq1 - seq2) < 0x3FFFFFFF : seq1 - seq2
- EXPECT_EQ(CSeqNo::seqcmp(128, 1), 127);
- EXPECT_EQ(CSeqNo::seqcmp(1, 128), -127);
-
- // abs(seq1 - seq2) >= 0x3FFFFFFF : seq2 - seq1
- EXPECT_EQ(CSeqNo::seqcmp(0x7FFFFFFF, 1), int(0x80000002)); // -2147483646
- EXPECT_EQ(CSeqNo::seqcmp(1, 0x7FFFFFFF), 0x7FFFFFFE); // 2147483646
-}
-
-TEST(CSeqNo, seqoff)
-{
- // seqoff: offset from the 2nd to the 1st seq#
- EXPECT_EQ(CSeqNo::seqoff(0x7FFFFFFF, 0x7FFFFFFF), 0);
-
- // distance(seq2 - seq1)
- EXPECT_EQ(CSeqNo::seqoff(125, 1), -124);
-
- EXPECT_EQ(CSeqNo::seqoff(1, 0x7FFFFFFF), -2);
- EXPECT_EQ(CSeqNo::seqoff(0x7FFFFFFF, 1), 2);
-}
-
-TEST(CSeqNo, seqlen)
-{
- EXPECT_EQ(CSeqNo::seqlen(125, 125), 1);
- EXPECT_EQ(CSeqNo::seqlen(125, 126), 2);
-
- EXPECT_EQ(CSeqNo::seqlen(2147483647, 0), 2);
- EXPECT_EQ(CSeqNo::seqlen(0, 2147483647), -2147483648);
-}
-
-TEST(CUDT, getFlightSpan)
-{
- const int test_values[3][3] = {
- // lastack curseq span
- { 125, 124, 0 }, // all sent packets are acknowledged
- { 125, 125, 1 },
- { 125, 130, 6 }
- };
-
- for (auto val : test_values)
- {
- EXPECT_EQ(CUDT::getFlightSpan(val[0], val[1]), val[2]) << "Span(" << val[0] << ", " << val[1] << ")";
- }
-}
-
-TEST(CSeqNo, incseq)
-{
- // incseq: increase the seq# by 1
- EXPECT_EQ(CSeqNo::incseq(1), 2);
- EXPECT_EQ(CSeqNo::incseq(125), 126);
- EXPECT_EQ(CSeqNo::incseq(0x7FFFFFFF), 0);
- EXPECT_EQ(CSeqNo::incseq(0x3FFFFFFF), 0x40000000);
-}
-
-TEST(CSeqNo, decseq)
-{
- // decseq: decrease the seq# by 1
- EXPECT_EQ(CSeqNo::decseq(1), 0);
- EXPECT_EQ(CSeqNo::decseq(125), 124);
- EXPECT_EQ(CSeqNo::decseq(0), 0x7FFFFFFF);
- EXPECT_EQ(CSeqNo::decseq(0x40000000), 0x3FFFFFFF);
-}
-
-TEST(CSeqNo, incseqint)
-{
- // incseq: increase the seq# by 1
- EXPECT_EQ(CSeqNo::incseq(1, 1), 2);
- EXPECT_EQ(CSeqNo::incseq(125, 1), 126);
- EXPECT_EQ(CSeqNo::incseq(0x7FFFFFFF, 1), 0);
- EXPECT_EQ(CSeqNo::incseq(0x3FFFFFFF, 1), 0x40000000);
-
- EXPECT_EQ(CSeqNo::incseq(0x3FFFFFFF, 0x3FFFFFFF), 0x7FFFFFFE);
- EXPECT_EQ(CSeqNo::incseq(0x3FFFFFFF, 0x40000000), 0x7FFFFFFF);
- EXPECT_EQ(CSeqNo::incseq(0x3FFFFFFF, 0x40000001), 0x00000000);
-}
-
-TEST(CSeqNo, decseqint)
-{
- // decseq: decrease the seq# by 1
- EXPECT_EQ(CSeqNo::decseq(1, 1), 0);
- EXPECT_EQ(CSeqNo::decseq(125, 1), 124);
- EXPECT_EQ(CSeqNo::decseq(0, 1), 0x7FFFFFFF);
- EXPECT_EQ(CSeqNo::decseq(0x40000000, 1), 0x3FFFFFFF);
-}
+++ /dev/null
-/*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2019 Haivision Systems Inc.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- * Written by:
- * Haivision Systems Inc.
- */
-
-#include <gtest/gtest.h>
-#include <future>
-#include <thread>
-#include <string>
-
-// SRT includes
-#include "any.hpp"
-#include "socketconfig.h"
-#include "srt.h"
-
-using namespace std;
-using namespace srt;
-
-
-class TestSocketOptions
- : public ::testing::Test
-{
-protected:
- TestSocketOptions()
- {
- // initialization code here
- }
-
- ~TestSocketOptions()
- {
- // cleanup any pending stuff, but no exceptions allowed
- }
-
-public:
- void BindListener()
- {
- // Specify address of the listener
- sockaddr* psa = (sockaddr*)&m_sa;
- ASSERT_NE(srt_bind(m_listen_sock, psa, sizeof m_sa), SRT_ERROR);
- }
-
- void StartListener()
- {
- BindListener();
-
- srt_listen(m_listen_sock, 1);
- }
-
- int Connect()
- {
- sockaddr* psa = (sockaddr*)&m_sa;
- return srt_connect(m_caller_sock, psa, sizeof m_sa);
- }
-
- SRTSOCKET EstablishConnection()
- {
- auto accept_async = [](SRTSOCKET listen_sock) {
- sockaddr_in client_address;
- int length = sizeof(sockaddr_in);
- const SRTSOCKET accepted_socket = srt_accept(listen_sock, (sockaddr*)&client_address, &length);
- return accepted_socket;
- };
- auto accept_res = async(launch::async, accept_async, m_listen_sock);
-
- const int connect_res = Connect();
- EXPECT_EQ(connect_res, SRT_SUCCESS);
-
- const SRTSOCKET accepted_sock = accept_res.get();
- EXPECT_NE(accepted_sock, SRT_INVALID_SOCK);
-
- return accepted_sock;
- }
-
-protected:
- // SetUp() is run immediately before a test starts.
- void SetUp()
- {
- ASSERT_GE(srt_startup(), 0);
- const int yes = 1;
-
- memset(&m_sa, 0, sizeof m_sa);
- m_sa.sin_family = AF_INET;
- m_sa.sin_port = htons(5200);
- ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &m_sa.sin_addr), 1);
-
- m_caller_sock = srt_create_socket();
- ASSERT_NE(m_caller_sock, SRT_INVALID_SOCK);
- ASSERT_EQ(srt_setsockopt(m_caller_sock, 0, SRTO_RCVSYN, &yes, sizeof yes), SRT_SUCCESS); // for async connect
- ASSERT_EQ(srt_setsockopt(m_caller_sock, 0, SRTO_SNDSYN, &yes, sizeof yes), SRT_SUCCESS); // for async connect
-
- m_listen_sock = srt_create_socket();
- ASSERT_NE(m_listen_sock, SRT_INVALID_SOCK);
- ASSERT_EQ(srt_setsockopt(m_listen_sock, 0, SRTO_RCVSYN, &yes, sizeof yes), SRT_SUCCESS); // for async connect
- ASSERT_EQ(srt_setsockopt(m_listen_sock, 0, SRTO_SNDSYN, &yes, sizeof yes), SRT_SUCCESS); // for async connect
- }
-
- void TearDown()
- {
- // Code here will be called just after the test completes.
- // OK to throw exceptions from here if needed.
- ASSERT_NE(srt_close(m_caller_sock), SRT_ERROR);
- ASSERT_NE(srt_close(m_listen_sock), SRT_ERROR);
- srt_cleanup();
- }
-
-protected:
- sockaddr_in m_sa;
- SRTSOCKET m_caller_sock = SRT_INVALID_SOCK;
- SRTSOCKET m_listen_sock = SRT_INVALID_SOCK;
-
- int m_pollid = 0;
-};
-
-
-enum class RestrictionType
-{
- PREBIND = 0,
- PRE = 1,
- POST = 2
-};
-
-const char* RestrictionTypeStr(RestrictionType val)
-{
- switch (val)
- {
- case RestrictionType::PREBIND:
- return "PREBIND";
- break;
- case RestrictionType::PRE:
- return "PRE";
- break;
- case RestrictionType::POST:
- return "POST";
- break;
- default:
- break;
- }
- return "INVALID";
-}
-
-struct OptionTestEntry
-{
- SRT_SOCKOPT optid;
- const char* optname; // TODO: move to a separate array, function or std::map.
- RestrictionType restriction; // TODO: consider using SRTO_R_PREBIND, etc. from core.cpp
- size_t opt_len;
- linb::any min_val;
- linb::any max_val;
- linb::any dflt_val;
- linb::any ndflt_val;
- vector<linb::any> invalid_vals;
-};
-
-static const size_t UDP_HDR_SIZE = 28; // 20 bytes IPv4 + 8 bytes of UDP { u16 sport, dport, len, csum }.
-static const size_t DFT_MTU_SIZE = 1500; // Default MTU size
-static const size_t SRT_PKT_SIZE = DFT_MTU_SIZE - UDP_HDR_SIZE; // MTU without UDP header
-
-const OptionTestEntry g_test_matrix_options[] =
-{
- // Option ID, Option Name | Restriction | optlen | min | max | default | nondefault | invalid vals |
- //SRTO_BINDTODEVICE
- //{ SRTO_CONGESTION, "SRTO_CONGESTION", RestrictionType::PRE, 4, "live", "file", "live", "file", {"liv", ""} },
- { SRTO_CONNTIMEO, "SRTO_CONNTIMEO", RestrictionType::PRE, sizeof(int), 0, INT32_MAX, 3000, 250, {-1} },
- { SRTO_DRIFTTRACER, "SRTO_DRIFTTRACER", RestrictionType::POST, sizeof(bool), false, true, true, false, {} },
- { SRTO_ENFORCEDENCRYPTION, "SRTO_ENFORCEDENCRYPTION", RestrictionType::PRE, sizeof(bool), false, true, true, false, {} },
- //SRTO_EVENT
- { SRTO_FC, "SRTO_FC", RestrictionType::PRE, sizeof(int), 32, INT32_MAX, 25600, 10000, {-1, 31} },
- //SRTO_GROUPCONNECT
-#if ENABLE_BONDING
- // Max value can't exceed SRTO_PEERIDLETIMEO
- { SRTO_GROUPMINSTABLETIMEO, "SRTO_GROUPMINSTABLETIMEO", RestrictionType::PRE, sizeof(int), 60, 5000, 60, 70, {0, -1, 50, 5001} },
-#endif
- //SRTO_GROUPTYPE
- //SRTO_INPUTBW
- //SRTO_IPTOS
- //SRTO_IPTTL
- //SRTO_IPV6ONLY
- //SRTO_ISN
- { SRTO_KMPREANNOUNCE, "SRTO_KMPREANNOUNCE", RestrictionType::PRE, sizeof(int), 0, INT32_MAX, 0, 1024, {-1} },
- { SRTO_KMREFRESHRATE, "SRTO_KMREFRESHRATE", RestrictionType::PRE, sizeof(int), 0, INT32_MAX, 0, 1024, {-1} },
- //SRTO_KMSTATE
- { SRTO_LATENCY, "SRTO_LATENCY", RestrictionType::PRE, sizeof(int), 0, INT32_MAX, 120, 200, {-1} },
- //SRTO_LINGER
- { SRTO_LOSSMAXTTL, "SRTO_LOSSMAXTTL", RestrictionType::POST, sizeof(int), 0, INT32_MAX, 0, 10, {} },
- //SRTO_MAXBW
- { SRTO_MESSAGEAPI, "SRTO_MESSAGEAPI", RestrictionType::PRE, sizeof(bool), false, true, true, false, {} },
- //SRTO_MININPUTBW
- { SRTO_MINVERSION, "SRTO_MINVERSION", RestrictionType::PRE, sizeof(int), 0, INT32_MAX, 0x010000, 0x010300, {} },
- { SRTO_MSS, "SRTO_MSS", RestrictionType::PREBIND, sizeof(int), 76, 65536, 1500, 1400, {-1, 0, 75} },
- { SRTO_NAKREPORT, "SRTO_NAKREPORT", RestrictionType::PRE, sizeof(bool), false, true, true, false, {} },
- { SRTO_OHEADBW, "SRTO_OHEADBW", RestrictionType::POST, sizeof(int), 5, 100, 25, 20, {-1, 0, 4, 101} },
- //SRTO_PACKETFILTER
- //SRTO_PASSPHRASE
- { SRTO_PAYLOADSIZE, "SRTO_PAYLOADSIZE", RestrictionType::PRE, sizeof(int), 0, 1456, 1316, 1400, {-1, 1500} },
- //SRTO_PBKEYLEN
- //SRTO_PEERIDLETIMEO
- { SRTO_PEERIDLETIMEO, "SRTO_PEERIDLETIMEO", RestrictionType::PRE, sizeof(int), 0, INT32_MAX, 5000, 4500, {-1} },
- { SRTO_PEERLATENCY, "SRTO_PEERLATENCY", RestrictionType::PRE, sizeof(int), 0, INT32_MAX, 0, 180, {-1} },
- //SRTO_PEERVERSION
- { SRTO_RCVBUF, "SRTO_RCVBUF", RestrictionType::PREBIND, sizeof(int), (int)(32 * SRT_PKT_SIZE), 2147483256, (int)(8192 * SRT_PKT_SIZE), 1000000, {-1} },
- //SRTO_RCVDATA
- //SRTO_RCVKMSTATE
- { SRTO_RCVLATENCY, "SRTO_RCVLATENCY", RestrictionType::PRE, sizeof(int), 0, INT32_MAX, 120, 1100, {-1} },
- //SRTO_RCVSYN
- { SRTO_RCVTIMEO, "SRTO_RCVTIMEO", RestrictionType::POST, sizeof(int), -1, INT32_MAX, -1, 2000, {-2} },
- //SRTO_RENDEZVOUS
- { SRTO_RETRANSMITALGO, "SRTO_RETRANSMITALGO", RestrictionType::PRE, sizeof(int), 0, 1, 1, 0, {-1, 2} },
- //SRTO_REUSEADDR
- //SRTO_SENDER
- { SRTO_SNDBUF, "SRTO_SNDBUF", RestrictionType::PREBIND, sizeof(int), (int)(32 * SRT_PKT_SIZE), 2147483256, (int)(8192 * SRT_PKT_SIZE), 1000000, {-1} },
- //SRTO_SNDDATA
- { SRTO_SNDDROPDELAY, "SRTO_SNDDROPDELAY", RestrictionType::POST, sizeof(int), -1, INT32_MAX, 0, 1500, {-2} },
- //SRTO_SNDKMSTATE
- //SRTO_SNDSYN
- { SRTO_SNDTIMEO, "SRTO_SNDTIMEO", RestrictionType::POST, sizeof(int), -1, INT32_MAX, -1, 1400, {-2} },
- //SRTO_STATE
- //SRTO_STREAMID
- { SRTO_TLPKTDROP, "SRTO_TLPKTDROP", RestrictionType::PRE, sizeof(bool), false, true, true, false, {} },
- //SRTO_TRANSTYPE
- //SRTO_TSBPDMODE
- //SRTO_UDP_RCVBUF
- //SRTO_UDP_SNDBUF
- //SRTO_VERSION
-};
-
-
-template<class ValueType>
-void CheckGetSockOpt(const OptionTestEntry& entry, SRTSOCKET sock, const ValueType& value, const char* desc)
-{
- ValueType opt_val;
- int opt_len = 0;
- EXPECT_EQ(srt_getsockopt(sock, 0, entry.optid, &opt_val, &opt_len), SRT_SUCCESS)
- << "Getting " << entry.optname << " returned error: " << srt_getlasterror_str();
-
- EXPECT_EQ(opt_val, value) << desc << ": Wrong " << entry.optname << " value " << opt_val;
- EXPECT_EQ(opt_len, entry.opt_len) << desc << "Wrong " << entry.optname << " value length";
-}
-
-typedef char const* strptr;
-template<>
-void CheckGetSockOpt<strptr>(const OptionTestEntry& entry, SRTSOCKET sock, const strptr& value, const char* desc)
-{
- char opt_val[16];
- int opt_len = 0;
- EXPECT_EQ(srt_getsockopt(sock, 0, entry.optid, &opt_val, &opt_len), SRT_SUCCESS)
- << "Getting " << entry.optname << " returned error: " << srt_getlasterror_str();
-
- EXPECT_EQ(strncmp(opt_val, value, min<int>(opt_len, entry.opt_len)), 0) << desc << ": Wrong " << entry.optname << " value " << opt_val;
- EXPECT_EQ(opt_len, entry.opt_len) << desc << "Wrong " << entry.optname << " value length";
-}
-
-template<class ValueType>
-void CheckSetSockOpt(const OptionTestEntry& entry, SRTSOCKET sock, const ValueType& value, int expect_return, const char* desc)
-{
- ValueType opt_val = value;
- int opt_len = entry.opt_len;
- EXPECT_EQ(srt_setsockopt(sock, 0, entry.optid, &opt_val, opt_len), expect_return)
- << "Setting " << entry.optname << " to " << opt_val << " must " << (expect_return == SRT_SUCCESS ? "succeed" : "fail");
-
- if (expect_return == SRT_SUCCESS)
- {
- CheckGetSockOpt<ValueType>(entry, sock, value, desc);
- }
- // TODO: else check the previous value is in force
-}
-
-template<class ValueType>
-bool CheckDefaultValue(const OptionTestEntry& entry, SRTSOCKET sock, const char* desc)
-{
- try {
- const ValueType dflt_val = linb::any_cast<ValueType>(entry.dflt_val);
- CheckGetSockOpt<ValueType>(entry, sock, dflt_val, desc);
- }
- catch (const linb::bad_any_cast&)
- {
- std::cerr << entry.optname << " default value type: " << entry.dflt_val.type().name() << "\n";
- return false;
- }
-
- return true;
-}
-
-template<class ValueType>
-bool CheckSetNonDefaultValue(const OptionTestEntry& entry, SRTSOCKET sock, int expected_return, const char* desc)
-{
- try {
- /*const ValueType dflt_val = linb::any_cast<ValueType>(entry.dflt_val);
- const ValueType min_val = linb::any_cast<ValueType>(entry.min_val);
- const ValueType max_val = linb::any_cast<ValueType>(entry.max_val);*/
- //const ValueType ndflt_val = (min_val != dflt_val) ? min_val : max_val;
-
- const ValueType ndflt_val = linb::any_cast<ValueType>(entry.ndflt_val);;
-
- CheckSetSockOpt<ValueType>(entry, sock, ndflt_val, expected_return, desc);
- }
- catch (const linb::bad_any_cast&)
- {
- std::cerr << entry.optname << " non-default value type: " << entry.ndflt_val.type().name() << "\n";
- return false;
- }
-
- return true;
-}
-
-template<class ValueType>
-bool CheckMinValue(const OptionTestEntry& entry, SRTSOCKET sock, const char* desc)
-{
- try {
- const ValueType min_val = linb::any_cast<ValueType>(entry.min_val);
- CheckSetSockOpt<ValueType>(entry, sock, min_val, SRT_SUCCESS, desc);
-
- const ValueType dflt_val = linb::any_cast<ValueType>(entry.dflt_val);
- CheckSetSockOpt<ValueType>(entry, sock, dflt_val, SRT_SUCCESS, desc);
- }
- catch (const linb::bad_any_cast&)
- {
- std::cerr << entry.optname << " min value type: " << entry.min_val.type().name() << "\n";
- return false;
- }
-
- return true;
-}
-
-template<class ValueType>
-bool CheckMaxValue(const OptionTestEntry& entry, SRTSOCKET sock, const char* desc)
-{
- try {
- const ValueType max_val = linb::any_cast<ValueType>(entry.max_val);
- CheckSetSockOpt<ValueType>(entry, sock, max_val, SRT_SUCCESS, desc);
- }
- catch (const linb::bad_any_cast&)
- {
- std::cerr << entry.optname << " max value type: " << entry.max_val.type().name() << "\n";
- return false;
- }
-
- return true;
-}
-
-template<class ValueType>
-bool CheckInvalidValues(const OptionTestEntry& entry, SRTSOCKET sock, const char* sock_name)
-{
- for (const auto& inval : entry.invalid_vals)
- {
- try {
- const ValueType val = linb::any_cast<ValueType>(inval);
- CheckSetSockOpt<ValueType>(entry, sock, val, SRT_ERROR, sock_name);
- }
- catch (const linb::bad_any_cast&)
- {
- std::cerr << entry.optname << " value type: " << inval.type().name() << "\n";
- return false;
- }
- }
-
- return true;
-}
-
-TEST_F(TestSocketOptions, DefaultVals)
-{
- for (const auto& entry : g_test_matrix_options)
- {
- const char* test_desc = "[Caller, default]";
- if (entry.dflt_val.type() == typeid(bool))
- {
- EXPECT_TRUE(CheckDefaultValue<bool>(entry, m_caller_sock, test_desc));
- }
- else if (entry.dflt_val.type() == typeid(int))
- {
- EXPECT_TRUE(CheckDefaultValue<int>(entry, m_caller_sock, test_desc));
- }
- else if (entry.dflt_val.type() == typeid(int64_t))
- {
- EXPECT_TRUE(CheckDefaultValue<int64_t>(entry, m_caller_sock, test_desc));
- }
- else if (entry.dflt_val.type() == typeid(const char*))
- {
- EXPECT_TRUE(CheckDefaultValue<const char*>(entry, m_caller_sock, test_desc));
- }
- else
- {
- FAIL() << entry.optname << ": Unexpected type " << entry.dflt_val.type().name();
- }
- }
-}
-
-TEST_F(TestSocketOptions, MaxVals)
-{
- // Note: Changing SRTO_FC changes SRTO_RCVBUF limitation
- for (const auto& entry : g_test_matrix_options)
- {
- if (entry.optid == SRTO_KMPREANNOUNCE || entry.optid == SRTO_KMREFRESHRATE)
- {
- cerr << "Skipping " << entry.optname << "\n";
- continue;
- }
-
- const char* test_desc = "[Caller, max value]";
- if (entry.max_val.type() == typeid(bool))
- {
- EXPECT_TRUE(CheckMaxValue<bool>(entry, m_caller_sock, test_desc));
- }
- else if (entry.max_val.type() == typeid(int))
- {
- EXPECT_TRUE(CheckMaxValue<int>(entry, m_caller_sock, test_desc));
- }
- else if (entry.max_val.type() == typeid(int64_t))
- {
- EXPECT_TRUE(CheckMaxValue<int64_t>(entry, m_caller_sock, test_desc));
- }
- else
- {
- FAIL() << "Unexpected type " << entry.max_val.type().name();
- }
-
- // TODO: back to default ?
- }
-}
-
-TEST_F(TestSocketOptions, MinVals)
-{
- // Note: Changing SRTO_FC changes SRTO_RCVBUF limitation
- for (const auto& entry : g_test_matrix_options)
- {
- const char* test_desc = "[Caller, min val]";
- if (entry.min_val.type() == typeid(bool))
- {
- EXPECT_TRUE(CheckMinValue<bool>(entry, m_caller_sock, test_desc));
- }
- else if (entry.min_val.type() == typeid(int))
- {
- EXPECT_TRUE(CheckMinValue<int>(entry, m_caller_sock, test_desc));
- }
- else if (entry.min_val.type() == typeid(int64_t))
- {
- EXPECT_TRUE(CheckMinValue<int64_t>(entry, m_caller_sock, test_desc));
- }
- else
- {
- FAIL() << entry.optname << ": Unexpected type " << entry.min_val.type().name();
- }
-
- // TODO: back to default
- }
-}
-
-TEST_F(TestSocketOptions, InvalidVals)
-{
- // Note: Changing SRTO_FC changes SRTO_RCVBUF limitation
- for (const auto& entry : g_test_matrix_options)
- {
- const char* desc = "[Caller, invalid val]";
- if (entry.dflt_val.type() == typeid(bool))
- {
- EXPECT_TRUE(CheckInvalidValues<bool>(entry, m_caller_sock, desc));
- }
- else if (entry.dflt_val.type() == typeid(int))
- {
- EXPECT_TRUE(CheckInvalidValues<int>(entry, m_caller_sock, desc));
- }
- else if (entry.dflt_val.type() == typeid(int64_t))
- {
- EXPECT_TRUE(CheckInvalidValues<int64_t>(entry, m_caller_sock, desc));
- }
- else
- {
- FAIL() << "Unexpected type " << entry.dflt_val.type().name();
- }
-
- // TODO: expect default is still in force?
- }
-}
-
-
-
-// TODO: taken from test_enforced_encryption
-static const char* const socket_state_array[] = {
- "IGNORE_SRTS",
- "SRTS_INVALID",
- "SRTS_INIT",
- "SRTS_OPENED",
- "SRTS_LISTENING",
- "SRTS_CONNECTING",
- "SRTS_CONNECTED",
- "SRTS_BROKEN",
- "SRTS_CLOSING",
- "SRTS_CLOSED",
- "SRTS_NONEXIST"
-};
-
-// A trick that allows the array to be indexed by -1
-const char* const* g_socket_state = socket_state_array + 1;
-
-#if 0
-// No socket option can be set in blocking mode because m_ConnectionLock is required by both srt_setsockopt and srt_connect
-// TODO: Use non-blocking mode
-TEST_F(TestSocketOptions, RestrictionCallerConnecting)
-{
- // The default SRTO_CONNTIMEO is 3 seconds. It is assumed all socket options could be checked.
- auto connect_async = [this]() {
- return Connect();
- };
- auto connect_res = async(launch::async, connect_async);
-
- for (int i = 0; i < 100; ++i)
- {
- if (srt_getsockstate(m_caller_sock) == SRTS_CONNECTING)
- break;
-
- this_thread::sleep_for(chrono::microseconds(100));
- }
-
- cout << "Running test\n";
-
- for (const auto& entry : g_test_matrix_options)
- {
- if (entry.restriction != RestrictionType::PRE)
- continue;
-
- // Setting a valid minimum value
- EXPECT_EQ(srt_setsockopt(m_caller_sock, 0, entry.optid, &entry.min_val, entry.opt_len), SRT_ERROR)
- << "Setting " << entry.optname << " (PRE) must not succeed while connecting. Sock state: " << g_socket_state[srt_getsockstate(m_caller_sock)];
- }
-
- connect_res.get();
-}
-#endif
-
-TEST_F(TestSocketOptions, RestrictionBind)
-{
- BindListener();
-
- for (const auto& entry : g_test_matrix_options)
- {
- const char* test_desc = "[Caller, after bind]";
- const int expected_res = (entry.restriction == RestrictionType::PREBIND) ? SRT_ERROR : SRT_SUCCESS;
-
- if (entry.dflt_val.type() == typeid(bool))
- {
- EXPECT_TRUE(CheckSetNonDefaultValue<bool>(entry, m_listen_sock, expected_res, test_desc))
- << "Sock state : " << g_socket_state[srt_getsockstate(m_listen_sock)];
- }
- else if (entry.dflt_val.type() == typeid(int))
- {
- EXPECT_TRUE(CheckSetNonDefaultValue<int>(entry, m_listen_sock, expected_res, test_desc))
- << "Sock state : " << g_socket_state[srt_getsockstate(m_listen_sock)];
- }
- else if (entry.dflt_val.type() == typeid(int64_t))
- {
- EXPECT_TRUE(CheckSetNonDefaultValue<int64_t>(entry, m_listen_sock, expected_res, test_desc))
- << "Sock state : " << g_socket_state[srt_getsockstate(m_listen_sock)];
- }
- else
- {
- FAIL() << "Unexpected type " << entry.dflt_val.type().name();
- }
- }
-}
-
-// Check that only socket option with POST binding can be set on a listener socket in "listening" state.
-TEST_F(TestSocketOptions, RestrictionListening)
-{
- StartListener();
-
- for (const auto& entry : g_test_matrix_options)
- {
- const int expected_res = (entry.restriction != RestrictionType::POST) ? SRT_ERROR : SRT_SUCCESS;
-
- // Setting a valid minimum value
- const char* test_desc ="[Listener, listening]";
-
- if (entry.dflt_val.type() == typeid(bool))
- {
- EXPECT_TRUE(CheckSetNonDefaultValue<bool>(entry, m_listen_sock, expected_res, test_desc))
- << test_desc << entry.optname << " Sock state: " << g_socket_state[srt_getsockstate(m_listen_sock)];
- }
- else if (entry.dflt_val.type() == typeid(int))
- {
- EXPECT_TRUE(CheckSetNonDefaultValue<int>(entry, m_listen_sock, expected_res, test_desc))
- << test_desc << entry.optname << " Sock state: " << g_socket_state[srt_getsockstate(m_listen_sock)];
- }
- else if (entry.dflt_val.type() == typeid(int64_t))
- {
- EXPECT_TRUE(CheckSetNonDefaultValue<int64_t>(entry, m_listen_sock, expected_res, test_desc))
- << test_desc << entry.optname << " Sock state: " << g_socket_state[srt_getsockstate(m_listen_sock)];
- }
- else
- {
- FAIL() << "Unexpected type " << entry.dflt_val.type().name();
- }
- }
-}
-
-// Check that only socket option with POST binding can be set on a connected socket (caller and accepted).
-TEST_F(TestSocketOptions, RestrictionConnected)
-{
- StartListener();
- const SRTSOCKET accepted_sock = EstablishConnection();
-
- for (const auto& entry : g_test_matrix_options)
- {
- const int expected_res = (entry.restriction != RestrictionType::POST) ? SRT_ERROR : SRT_SUCCESS;
-
- // Setting a valid minimum value
- for (SRTSOCKET sock : { m_caller_sock, accepted_sock })
- {
- const char* test_desc = sock == m_caller_sock ? "[Caller, connected]" : "[Accepted, connected]";
-
- if (entry.dflt_val.type() == typeid(bool))
- {
- EXPECT_TRUE(CheckSetNonDefaultValue<bool>(entry, sock, expected_res, test_desc))
- << test_desc << entry.optname << " Sock state: " << g_socket_state[srt_getsockstate(sock)];
- }
- else if (entry.dflt_val.type() == typeid(int))
- {
- EXPECT_TRUE(CheckSetNonDefaultValue<int>(entry, sock, expected_res, test_desc))
- << test_desc << entry.optname << " Sock state: " << g_socket_state[srt_getsockstate(sock)];
- }
- else if (entry.dflt_val.type() == typeid(int64_t))
- {
- EXPECT_TRUE(CheckSetNonDefaultValue<int64_t>(entry, sock, expected_res, test_desc))
- << test_desc << entry.optname << " Sock state: " << g_socket_state[srt_getsockstate(sock)];
- }
- else
- {
- FAIL() << "Unexpected type " << entry.dflt_val.type().name();
- }
- }
- }
-}
-
-// TODO: TEST_F(TestSocketOptions, CheckInheritedAfterConnection)
-// Check that accepted socket has correct socket option values.
-// Check setting and getting SRT_MININPUTBW
-TEST_F(TestSocketOptions, TLPktDropInherits)
-{
- const bool tlpktdrop_dflt = true;
- const bool tlpktdrop_new = false;
-
- bool optval = tlpktdrop_dflt;
- int optlen = (int)(sizeof optval);
- EXPECT_EQ(srt_setsockopt(m_listen_sock, 0, SRTO_TLPKTDROP, &tlpktdrop_new, sizeof tlpktdrop_new), SRT_SUCCESS);
- EXPECT_EQ(srt_getsockopt(m_listen_sock, 0, SRTO_TLPKTDROP, &optval, &optlen), SRT_SUCCESS);
- EXPECT_EQ(optval, tlpktdrop_new);
-
- StartListener();
- const SRTSOCKET accepted_sock = EstablishConnection();
-
- // Check accepted socket inherits values
- for (SRTSOCKET sock : { m_listen_sock, accepted_sock })
- {
- optval = tlpktdrop_dflt;
- optlen = (int)(sizeof optval);
- EXPECT_EQ(srt_getsockopt(sock, 0, SRTO_TLPKTDROP, &optval, &optlen), SRT_SUCCESS);
- EXPECT_EQ(optlen, (int)(sizeof optval));
- EXPECT_EQ(optval, tlpktdrop_new);
- }
-
- this_thread::sleep_for(chrono::seconds(2));
-
- ASSERT_NE(srt_close(accepted_sock), SRT_ERROR);
-}
-
-TEST_F(TestSocketOptions, Latency)
-{
- const int latency_a = 140;
- const int latency_b = 100;
- const int latency_dflt = 120;
-
- int optval;
- int optlen = (int)(sizeof optval);
- EXPECT_EQ(srt_setsockopt(m_listen_sock, 0, SRTO_RCVLATENCY, &latency_a, sizeof latency_a), SRT_SUCCESS);
- EXPECT_EQ(srt_setsockopt(m_listen_sock, 0, SRTO_PEERLATENCY, &latency_b, sizeof latency_b), SRT_SUCCESS);
-
- EXPECT_EQ(srt_getsockopt(m_listen_sock, 0, SRTO_RCVLATENCY, &optval, &optlen), SRT_SUCCESS);
- EXPECT_EQ(optval, latency_a);
- EXPECT_EQ(srt_getsockopt(m_listen_sock, 0, SRTO_PEERLATENCY, &optval, &optlen), SRT_SUCCESS);
- EXPECT_EQ(optval, latency_b);
-
- StartListener();
- const SRTSOCKET accepted_sock = EstablishConnection();
-
- // Check caller socket
- EXPECT_EQ(srt_getsockopt(m_caller_sock, 0, SRTO_RCVLATENCY, &optval, &optlen), SRT_SUCCESS);
- EXPECT_EQ(optval, latency_dflt);
- EXPECT_EQ(srt_getsockopt(m_caller_sock, 0, SRTO_PEERLATENCY, &optval, &optlen), SRT_SUCCESS);
- EXPECT_EQ(optval, latency_a);
-
- EXPECT_EQ(srt_getsockopt(accepted_sock, 0, SRTO_RCVLATENCY, &optval, &optlen), SRT_SUCCESS);
- EXPECT_EQ(optval, latency_a);
- EXPECT_EQ(srt_getsockopt(accepted_sock, 0, SRTO_PEERLATENCY, &optval, &optlen), SRT_SUCCESS);
- EXPECT_EQ(optval, latency_dflt);
-
- ASSERT_NE(srt_close(accepted_sock), SRT_ERROR);
-}
-
-/// A regression test for issue #735, fixed by PR #843.
-/// Checks propagation of listener's socket option SRTO_LOSSMAXTTL
-/// on SRT sockets being accepted.
-TEST_F(TestSocketOptions, LossMaxTTL)
-{
- const int loss_max_ttl = 5;
- ASSERT_EQ(srt_setsockopt(m_listen_sock, 0, SRTO_LOSSMAXTTL, &loss_max_ttl, sizeof loss_max_ttl), SRT_SUCCESS);
-
- StartListener();
- const SRTSOCKET accepted_sock = EstablishConnection();
-
- int opt_val = 0;
- int opt_len = 0;
- ASSERT_EQ(srt_getsockopt(accepted_sock, 0, SRTO_LOSSMAXTTL, &opt_val, &opt_len), SRT_SUCCESS);
- EXPECT_EQ(opt_val, loss_max_ttl) << "Wrong SRTO_LOSSMAXTTL value on the accepted socket";
- EXPECT_EQ(size_t(opt_len), sizeof opt_len) << "Wrong SRTO_LOSSMAXTTL value length on the accepted socket";
-
- SRT_TRACEBSTATS stats;
- EXPECT_EQ(srt_bstats(accepted_sock, &stats, 0), SRT_SUCCESS);
- EXPECT_EQ(stats.pktReorderTolerance, loss_max_ttl);
-
- ASSERT_EQ(srt_getsockopt(m_listen_sock, 0, SRTO_LOSSMAXTTL, &opt_val, &opt_len), SRT_SUCCESS);
- EXPECT_EQ(opt_val, loss_max_ttl) << "Wrong SRTO_LOSSMAXTTL value on the listener socket";
- EXPECT_EQ(size_t(opt_len), sizeof opt_len) << "Wrong SRTO_LOSSMAXTTL value length on the listener socket";
-
- ASSERT_NE(srt_close(accepted_sock), SRT_ERROR);
-}
-
-
-// Try to set/get SRTO_MININPUTBW with wrong optlen
-TEST_F(TestSocketOptions, MinInputBWWrongLen)
-{
- int64_t mininputbw = 0;
- int optlen = (int)(sizeof mininputbw) - 1;
- EXPECT_EQ(srt_getsockopt(m_listen_sock, 0, SRTO_MININPUTBW, &mininputbw, &optlen), SRT_ERROR);
- EXPECT_EQ(srt_getlasterror(NULL), SRT_EINVPARAM);
- optlen += 2;
- EXPECT_EQ(srt_getsockopt(m_listen_sock, 0, SRTO_MININPUTBW, &mininputbw, &optlen), SRT_SUCCESS) << "Bigger storage is allowed";
- EXPECT_EQ(optlen, (int)(sizeof mininputbw));
-
- EXPECT_EQ(srt_setsockopt(m_listen_sock, 0, SRTO_MININPUTBW, &mininputbw, sizeof mininputbw - 1), SRT_ERROR);
- EXPECT_EQ(srt_getlasterror(NULL), SRT_EINVPARAM);
- EXPECT_EQ(srt_setsockopt(m_listen_sock, 0, SRTO_MININPUTBW, &mininputbw, sizeof mininputbw + 1), SRT_ERROR);
- EXPECT_EQ(srt_getlasterror(NULL), SRT_EINVPARAM);
-}
-
-// Check the default SRTO_MININPUTBW is SRT_PACING_MAXBW_DEFAULT
-TEST_F(TestSocketOptions, MinInputBWDefault)
-{
- const int mininputbw_expected = 0;
- int64_t mininputbw = 1;
- int optlen = (int)(sizeof mininputbw);
- EXPECT_EQ(srt_getsockopt(m_listen_sock, 0, SRTO_MININPUTBW, &mininputbw, &optlen), SRT_SUCCESS);
- EXPECT_EQ(optlen, (int)(sizeof mininputbw));
- EXPECT_EQ(mininputbw, mininputbw_expected);
-
- StartListener();
- const SRTSOCKET accepted_sock = EstablishConnection();
-
- // Check both listener and accepted socket have default values
- for (SRTSOCKET sock : { m_listen_sock, accepted_sock })
- {
- optlen = (int)(sizeof mininputbw);
- EXPECT_EQ(srt_getsockopt(sock, 0, SRTO_MININPUTBW, &mininputbw, &optlen), SRT_SUCCESS);
- EXPECT_EQ(optlen, (int)(sizeof mininputbw));
- EXPECT_EQ(mininputbw, mininputbw_expected);
- }
-
- ASSERT_NE(srt_close(accepted_sock), SRT_ERROR);
-}
-
-// Check setting and getting SRT_MININPUTBW
-TEST_F(TestSocketOptions, MinInputBWSet)
-{
- const int64_t mininputbw_dflt = 0;
- const int64_t mininputbw = 50000000;
- int optlen = (int)(sizeof mininputbw);
-
- int64_t bw = -100;
- EXPECT_EQ(srt_setsockopt(m_listen_sock, 0, SRTO_MININPUTBW, &bw, sizeof bw), SRT_ERROR) << "Has to be a non-negative number";
- EXPECT_EQ(srt_getsockopt(m_listen_sock, 0, SRTO_MININPUTBW, &bw, &optlen), SRT_SUCCESS);
- EXPECT_EQ(bw, mininputbw_dflt);
-
- bw = mininputbw;
- EXPECT_EQ(srt_setsockopt(m_listen_sock, 0, SRTO_MININPUTBW, &bw, sizeof bw), SRT_SUCCESS);
- EXPECT_EQ(srt_getsockopt(m_listen_sock, 0, SRTO_MININPUTBW, &bw, &optlen), SRT_SUCCESS);
- EXPECT_EQ(bw, mininputbw);
-
- StartListener();
- const SRTSOCKET accepted_sock = EstablishConnection();
-
- // Check accepted socket inherits values
- for (SRTSOCKET sock : { m_listen_sock, accepted_sock })
- {
- optlen = (int)(sizeof bw);
- EXPECT_EQ(srt_getsockopt(sock, 0, SRTO_MININPUTBW, &bw, &optlen), SRT_SUCCESS);
- EXPECT_EQ(optlen, (int)(sizeof bw));
- EXPECT_EQ(bw, mininputbw);
- }
-
- ASSERT_NE(srt_close(accepted_sock), SRT_ERROR);
-}
-
-// Check setting and getting SRTO_MININPUTBW in runtime
-TEST_F(TestSocketOptions, MinInputBWRuntime)
-{
- const int64_t mininputbw = 50000000;
-
- // Establish a connection
- StartListener();
- const SRTSOCKET accepted_sock = EstablishConnection();
-
- // Test a connected socket
- int64_t bw = mininputbw;
- int optlen = (int)(sizeof bw);
- EXPECT_EQ(srt_setsockopt(accepted_sock, 0, SRTO_MININPUTBW, &bw, sizeof bw), SRT_SUCCESS);
- EXPECT_EQ(srt_getsockopt(accepted_sock, 0, SRTO_MININPUTBW, &bw, &optlen), SRT_SUCCESS);
- EXPECT_EQ(bw, mininputbw);
-
- bw = 0;
- EXPECT_EQ(srt_setsockopt(accepted_sock, 0, SRTO_INPUTBW, &bw, sizeof bw), SRT_SUCCESS);
- EXPECT_EQ(srt_getsockopt(accepted_sock, 0, SRTO_INPUTBW, &bw, &optlen), SRT_SUCCESS);
- EXPECT_EQ(bw, 0);
-
- EXPECT_EQ(srt_setsockopt(accepted_sock, 0, SRTO_MAXBW, &bw, sizeof bw), SRT_SUCCESS);
- EXPECT_EQ(srt_getsockopt(accepted_sock, 0, SRTO_MAXBW, &bw, &optlen), SRT_SUCCESS);
- EXPECT_EQ(bw, 0);
-
- EXPECT_EQ(srt_getsockopt(accepted_sock, 0, SRTO_MININPUTBW, &bw, &optlen), SRT_SUCCESS);
- EXPECT_EQ(bw, mininputbw);
-
- const int64_t new_mininputbw = 20000000;
- bw = new_mininputbw;
- EXPECT_EQ(srt_setsockopt(accepted_sock, 0, SRTO_MININPUTBW, &bw, sizeof bw), SRT_SUCCESS);
- EXPECT_EQ(srt_getsockopt(accepted_sock, 0, SRTO_MININPUTBW, &bw, &optlen), SRT_SUCCESS);
- EXPECT_EQ(bw, new_mininputbw);
-
- ASSERT_NE(srt_close(accepted_sock), SRT_ERROR);
-}
-
-TEST_F(TestSocketOptions, StreamIDWrongLen)
-{
- char buffer[CSrtConfig::MAX_SID_LENGTH + 135];
- for (size_t i = 0; i < sizeof buffer; ++i)
- buffer[i] = 'a' + i % 25;
-
- EXPECT_EQ(srt_setsockopt(m_caller_sock, 0, SRTO_STREAMID, buffer, CSrtConfig::MAX_SID_LENGTH+1), SRT_ERROR);
- EXPECT_EQ(srt_getlasterror(NULL), SRT_EINVPARAM);
-}
-
-// Try to set/get a 13-character string in SRTO_STREAMID.
-// This tests checks that the StreamID is set to the correct size
-// while it is transmitted as 16 characters in the Stream ID HS extension.
-TEST_F(TestSocketOptions, StreamIDOdd)
-{
- // 13 characters, that is, 3*4+1
- string sid_odd = "something1234";
-
- EXPECT_EQ(srt_setsockopt(m_caller_sock, 0, SRTO_STREAMID, sid_odd.c_str(), sid_odd.size()), SRT_SUCCESS);
-
- char buffer[CSrtConfig::MAX_SID_LENGTH + 135];
- int buffer_len = sizeof buffer;
- EXPECT_EQ(srt_getsockopt(m_caller_sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS);
- EXPECT_EQ(std::string(buffer), sid_odd);
- EXPECT_EQ(size_t(buffer_len), sid_odd.size());
- EXPECT_EQ(strlen(buffer), sid_odd.size());
-
- StartListener();
- const SRTSOCKET accepted_sock = EstablishConnection();
-
- // Check accepted socket inherits values
- for (size_t i = 0; i < sizeof buffer; ++i)
- buffer[i] = 'a';
- buffer_len = (int)(sizeof buffer);
- EXPECT_EQ(srt_getsockopt(accepted_sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS);
- EXPECT_EQ(size_t(buffer_len), sid_odd.size());
- EXPECT_EQ(strlen(buffer), sid_odd.size());
-
- ASSERT_NE(srt_close(accepted_sock), SRT_ERROR);
-}
-
-
-TEST_F(TestSocketOptions, StreamIDEven)
-{
- // 12 characters = 4*3, that is, aligned to 4
- string sid_even = "123412341234";
-
- EXPECT_EQ(srt_setsockopt(m_caller_sock, 0, SRTO_STREAMID, sid_even.c_str(), sid_even.size()), SRT_SUCCESS);
-
- char buffer[CSrtConfig::MAX_SID_LENGTH + 135];
- int buffer_len = sizeof buffer;
- EXPECT_EQ(srt_getsockopt(m_caller_sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS);
- EXPECT_EQ(std::string(buffer), sid_even);
- EXPECT_EQ(size_t(buffer_len), sid_even.size());
- EXPECT_EQ(strlen(buffer), sid_even.size());
-
- StartListener();
- const SRTSOCKET accepted_sock = EstablishConnection();
-
- // Check accepted socket inherits values
- for (size_t i = 0; i < sizeof buffer; ++i)
- buffer[i] = 'a';
- buffer_len = (int)(sizeof buffer);
- EXPECT_EQ(srt_getsockopt(accepted_sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS);
- EXPECT_EQ(size_t(buffer_len), sid_even.size());
- EXPECT_EQ(strlen(buffer), sid_even.size());
-
- ASSERT_NE(srt_close(accepted_sock), SRT_ERROR);
-}
-
-
-TEST_F(TestSocketOptions, StreamIDAlmostFull)
-{
- // 12 characters = 4*3, that is, aligned to 4
- string sid_amost_full;
- for (size_t i = 0; i < CSrtConfig::MAX_SID_LENGTH-2; ++i)
- sid_amost_full += 'x';
-
- // Just to manipulate the last ones.
- size_t size = sid_amost_full.size();
- sid_amost_full[size-2] = 'y';
- sid_amost_full[size-1] = 'z';
-
- EXPECT_EQ(srt_setsockopt(m_caller_sock, 0, SRTO_STREAMID, sid_amost_full.c_str(), sid_amost_full.size()), SRT_SUCCESS);
-
- char buffer[CSrtConfig::MAX_SID_LENGTH + 135];
- int buffer_len = sizeof buffer;
- EXPECT_EQ(srt_getsockopt(m_caller_sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS);
- EXPECT_EQ(std::string(buffer), sid_amost_full);
- EXPECT_EQ(size_t(buffer_len), sid_amost_full.size());
- EXPECT_EQ(strlen(buffer), sid_amost_full.size());
-
- StartListener();
- const SRTSOCKET accepted_sock = EstablishConnection();
-
- // Check accepted socket inherits values
- for (size_t i = 0; i < sizeof buffer; ++i)
- buffer[i] = 'a';
- buffer_len = (int)(sizeof buffer);
- EXPECT_EQ(srt_getsockopt(accepted_sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS);
- EXPECT_EQ(size_t(buffer_len), sid_amost_full.size());
- EXPECT_EQ(strlen(buffer), sid_amost_full.size());
- EXPECT_EQ(buffer[sid_amost_full.size()-1], 'z');
-
- ASSERT_NE(srt_close(accepted_sock), SRT_ERROR);
-}
-
-TEST_F(TestSocketOptions, StreamIDFull)
-{
- // 12 characters = 4*3, that is, aligned to 4
- string sid_full;
- for (size_t i = 0; i < CSrtConfig::MAX_SID_LENGTH; ++i)
- sid_full += 'x';
-
- // Just to manipulate the last ones.
- size_t size = sid_full.size();
- sid_full[size-2] = 'y';
- sid_full[size-1] = 'z';
-
- EXPECT_EQ(srt_setsockopt(m_caller_sock, 0, SRTO_STREAMID, sid_full.c_str(), sid_full.size()), SRT_SUCCESS);
-
- char buffer[CSrtConfig::MAX_SID_LENGTH + 135];
- int buffer_len = sizeof buffer;
- EXPECT_EQ(srt_getsockopt(m_caller_sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS);
- EXPECT_EQ(std::string(buffer), sid_full);
- EXPECT_EQ(size_t(buffer_len), sid_full.size());
- EXPECT_EQ(strlen(buffer), sid_full.size());
-
- StartListener();
- const SRTSOCKET accepted_sock = EstablishConnection();
-
- // Check accepted socket inherits values
- for (size_t i = 0; i < sizeof buffer; ++i)
- buffer[i] = 'a';
- buffer_len = (int)(sizeof buffer);
- EXPECT_EQ(srt_getsockopt(accepted_sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS);
- EXPECT_EQ(size_t(buffer_len), sid_full.size());
- EXPECT_EQ(strlen(buffer), sid_full.size());
- EXPECT_EQ(buffer[sid_full.size()-1], 'z');
-
- ASSERT_NE(srt_close(accepted_sock), SRT_ERROR);
-}
-
-// Check that StreamID assigned to a listener socket is not inherited by accepted sockets,
-// and is not derived by a caller socket.
-TEST_F(TestSocketOptions, StreamIDLenListener)
-{
- string stream_id_13 = "something1234";
-
- EXPECT_EQ(srt_setsockopt(m_listen_sock, 0, SRTO_STREAMID, stream_id_13.c_str(), stream_id_13.size()), SRT_SUCCESS);
-
- char buffer[648];
- int buffer_len = sizeof buffer;
- EXPECT_EQ(srt_getsockopt(m_listen_sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS);
- EXPECT_EQ(string(buffer), stream_id_13);
- EXPECT_EQ(size_t(buffer_len), stream_id_13.size());
-
- StartListener();
- const SRTSOCKET accepted_sock = EstablishConnection();
-
- // Check accepted and caller sockets do not inherit StreamID.
- for (SRTSOCKET sock : { m_caller_sock, accepted_sock })
- {
- buffer_len = (int)(sizeof buffer);
- fill_n(buffer, buffer_len, 'a');
- EXPECT_EQ(srt_getsockopt(sock, 0, SRTO_STREAMID, &buffer, &buffer_len), SRT_SUCCESS);
- EXPECT_EQ(buffer_len, 0) << (sock == accepted_sock ? "ACCEPTED" : "CALLER");
- }
-
- ASSERT_NE(srt_close(accepted_sock), SRT_ERROR);
-}
+++ /dev/null
-#include "gtest/gtest.h"
-#include <array>
-#include <chrono>
-#include <thread>
-#include <future>
-#include <numeric> // std::accumulate
-#include <regex> // Used in FormatTime test
-#include "sync.h"
-#include "common.h"
-
-// This test set requires support for C++14
-// * Uses "'" as a separator: 100'000
-// * Uses operator"ms" at al from chrono
-
-using namespace std;
-using namespace srt::sync;
-
-
-TEST(SyncDuration, BasicChecks)
-{
- const steady_clock::duration d = steady_clock::duration();
-
- EXPECT_EQ(d.count(), 0);
- EXPECT_TRUE(d == d); // operator==
- EXPECT_FALSE(d != d); // operator!=
- EXPECT_EQ(d, steady_clock::duration::zero());
- EXPECT_EQ(d, microseconds_from(0));
- EXPECT_EQ(d, milliseconds_from(0));
- EXPECT_EQ(d, seconds_from(0));
- EXPECT_EQ(count_milliseconds(d), 0);
- EXPECT_EQ(count_microseconds(d), 0);
- EXPECT_EQ(count_seconds(d), 0);
-
- const steady_clock::duration a = d + milliseconds_from(120);
- EXPECT_EQ(a, milliseconds_from(120));
- EXPECT_EQ(count_milliseconds(a), 120);
- EXPECT_EQ(count_microseconds(a), 120000);
- EXPECT_EQ(count_seconds(a), 0);
-}
-
-/// Check operations on (uint32_t + 1)
-TEST(SyncDuration, DurationFrom)
-{
- const int64_t val = int64_t(numeric_limits<uint32_t>::max()) + 1;
-
- const steady_clock::duration us_from = microseconds_from(val);
- EXPECT_EQ(count_microseconds(us_from), val);
-
- const steady_clock::duration ms_from = milliseconds_from(val);
- EXPECT_EQ(count_milliseconds(ms_from), val);
-
- const steady_clock::duration s_from = seconds_from(val);
- EXPECT_EQ(count_seconds(s_from), val);
-}
-
-TEST(SyncDuration, RelOperators)
-{
- const steady_clock::duration a = steady_clock::duration();
-
- EXPECT_EQ(a.count(), 0);
- EXPECT_TRUE(a == a); // operator==
- EXPECT_FALSE(a != a); // operator!=
- EXPECT_FALSE(a > a); // operator>
- EXPECT_FALSE(a < a); // operator<
- EXPECT_TRUE(a <= a); // operator<=
- EXPECT_TRUE(a >= a); // operator>=
-
- const steady_clock::duration b = a + milliseconds_from(120);
- EXPECT_FALSE(b == a); // operator==
- EXPECT_TRUE(b != a); // operator!=
- EXPECT_TRUE(b > a); // operator>
- EXPECT_FALSE(a > b); // operator>
- EXPECT_FALSE(b < a); // operator<
- EXPECT_TRUE(a < b); // operator<
- EXPECT_FALSE(b <= a); // operator<=
- EXPECT_TRUE(a <= b); // operator<=
- EXPECT_TRUE(b >= a); // operator>=
- EXPECT_FALSE(a >= b); // operator>=
-
- const steady_clock::duration c = steady_clock::duration(numeric_limits<int64_t>::max());
- EXPECT_EQ(c.count(), numeric_limits<int64_t>::max());
- const steady_clock::duration d = steady_clock::duration(numeric_limits<int64_t>::min());
- EXPECT_EQ(d.count(), numeric_limits<int64_t>::min());
-}
-
-TEST(SyncDuration, OperatorMinus)
-{
- const steady_clock::duration a = seconds_from(5);
- const steady_clock::duration b = milliseconds_from(3500);
-
- EXPECT_EQ(count_milliseconds(a - b), 1500);
- EXPECT_EQ(count_milliseconds(b - a), -1500);
- EXPECT_EQ((a - a).count(), 0);
-}
-
-TEST(SyncDuration, OperatorMinusEq)
-{
- const steady_clock::duration a = seconds_from(5);
- const steady_clock::duration b = milliseconds_from(3500);
-
- steady_clock::duration c = a;
- EXPECT_EQ(c, a);
- c -= b;
- EXPECT_EQ(count_milliseconds(c), 1500);
- c = b;
- EXPECT_EQ(c, b);
- c -= a;
- EXPECT_EQ(count_milliseconds(c), -1500);
-}
-
-TEST(SyncDuration, OperatorPlus)
-{
- const steady_clock::duration a = seconds_from(5);
- const steady_clock::duration b = milliseconds_from(3500);
-
- EXPECT_EQ(count_milliseconds(a + b), 8500);
- EXPECT_EQ(count_milliseconds(b + a), 8500);
-}
-
-TEST(SyncDuration, OperatorPlusEq)
-{
- const steady_clock::duration a = seconds_from(5);
- const steady_clock::duration b = milliseconds_from(3500);
-
- steady_clock::duration c = a;
- EXPECT_EQ(c, a);
- c += b;
- EXPECT_EQ(count_milliseconds(c), 8500);
- c = b;
- EXPECT_EQ(c, b);
- c += a;
- EXPECT_EQ(count_milliseconds(c), 8500);
-}
-
-TEST(SyncDuration, OperatorMultInt)
-{
- const steady_clock::duration a = milliseconds_from(3500);
-
- EXPECT_EQ(count_milliseconds(a), 3500);
- EXPECT_EQ(count_milliseconds(a * 2), 7000);
-}
-
-TEST(SyncDuration, OperatorMultIntEq)
-{
- steady_clock::duration a = milliseconds_from(3500);
-
- EXPECT_EQ(count_milliseconds(a), 3500);
- a *= 2;
- EXPECT_EQ(count_milliseconds(a), 7000);
-}
-
-TEST(SyncRandom, GenRandomInt)
-{
- array<int, 64> mn = {};
-
- // Check generated values are in the specified range.
- const size_t n = 2048;
- for (size_t i = 0; i < n; ++i)
- {
- const int rand_val = genRandomInt(0, mn.size() - 1);
- ASSERT_GE(rand_val, 0);
- ASSERT_LT(rand_val, mn.size());
- ++mn[rand_val];
- }
-
- // Check the distribution is more or less uniform.
- // 100% uniform if each value is generated (n / (2 * mn.size())) times.
- // We expect at least half of that value for a random uniform distribution.
- const int min_value = n / (2 * mn.size()) - 1;
- cout << "min value: " << min_value << endl;
- for (size_t i = 0; i < mn.size(); ++i)
- {
- EXPECT_GE(mn[i], min_value) << "i=" << i << ". Ok-ish if the count is non-zero.";
- }
-
- // Uncomment to see the distribution.
- //for (size_t i = 0; i < mn.size(); ++i)
- //{
- // cout << i << '\t';
- // for (int j=0; j<mn[i]; ++j) cout << '*';
- // cout << '\n';
- //}
-
- // Check INT32_MAX
- for (size_t i = 0; i < n; ++i)
- {
- const int rand_val = genRandomInt(INT32_MAX - 1, INT32_MAX);
-
- EXPECT_GE(rand_val, INT32_MAX - 1);
- EXPECT_LE(rand_val, INT32_MAX);
- }
-}
-
-/*****************************************************************************/
-/*
- * TimePoint tests
- */
-/*****************************************************************************/
-
-TEST(SyncTimePoint, DefaultConstructorZero)
-{
- steady_clock::time_point a;
- EXPECT_TRUE(is_zero(a));
-}
-
-TEST(SyncTimePoint, RelOperators)
-{
- const steady_clock::time_point a(steady_clock::time_point::max());
- const steady_clock::time_point b(steady_clock::time_point::min());
- EXPECT_TRUE(a == a);
- EXPECT_FALSE(a == b);
- EXPECT_TRUE(a != b);
- EXPECT_TRUE(a != b);
-
- EXPECT_TRUE(a >= a);
- EXPECT_FALSE(b >= a);
- EXPECT_TRUE(a > b);
- EXPECT_FALSE(a > a);
- EXPECT_TRUE(a <= a);
- EXPECT_TRUE(b <= a);
- EXPECT_FALSE(a <= b);
- EXPECT_FALSE(a < a);
- EXPECT_TRUE(b < a);
- EXPECT_FALSE(a < b);
-}
-
-#ifndef ENABLE_STDCXX_SYNC
-TEST(SyncTimePoint, OperatorMinus)
-{
- const int64_t delta = 1024;
- const steady_clock::time_point a(numeric_limits<uint64_t>::max());
- const steady_clock::time_point b(numeric_limits<uint64_t>::max() - delta);
- EXPECT_EQ((a - b).count(), delta);
- EXPECT_EQ((b - a).count(), -delta);
-}
-
-TEST(SyncTimePoint, OperatorEq)
-{
- const int64_t delta = 1024;
- const steady_clock::time_point a(numeric_limits<uint64_t>::max() - delta);
- const steady_clock::time_point b = a;
- EXPECT_EQ(a, b);
-}
-
-TEST(SyncTimePoint, OperatorMinusPlusDuration)
-{
- const int64_t delta = 1024;
- const steady_clock::time_point a(numeric_limits<uint64_t>::max());
- const steady_clock::time_point b(numeric_limits<uint64_t>::max() - delta);
-
- EXPECT_EQ((a + steady_clock::duration(-delta)), b);
- EXPECT_EQ((b + steady_clock::duration(+delta)), a);
-
- EXPECT_EQ((a - steady_clock::duration(+delta)), b);
- EXPECT_EQ((b - steady_clock::duration(-delta)), a);
-}
-
-TEST(SyncTimePoint, OperatorPlusEqDuration)
-{
- const int64_t delta = 1024;
- const steady_clock::time_point a(numeric_limits<uint64_t>::max());
- const steady_clock::time_point b(numeric_limits<uint64_t>::max() - delta);
- steady_clock::time_point r = a;
- EXPECT_EQ(r, a);
- r += steady_clock::duration(-delta);
- EXPECT_EQ(r, b);
- r = b;
- EXPECT_EQ(r, b);
- r += steady_clock::duration(+delta);
- EXPECT_EQ(r, a);
- r = a;
- EXPECT_EQ(r, a);
- r -= steady_clock::duration(+delta);
- EXPECT_EQ((a - steady_clock::duration(+delta)), b);
- EXPECT_EQ((b - steady_clock::duration(-delta)), a);
-}
-
-TEST(SyncTimePoint, OperatorMinusEqDuration)
-{
- const int64_t delta = 1024;
- const steady_clock::time_point a(numeric_limits<uint64_t>::max());
- const steady_clock::time_point b(numeric_limits<uint64_t>::max() - delta);
- steady_clock::time_point r = a;
- EXPECT_EQ(r, a);
- r -= steady_clock::duration(+delta);
- EXPECT_EQ(r, b);
- r = b;
- EXPECT_EQ(r, b);
- r -= steady_clock::duration(-delta);
- EXPECT_EQ(r, a);
-}
-#endif
-
-/*****************************************************************************/
-/*
- * UniqueLock tests
- */
-/*****************************************************************************/
-TEST(SyncUniqueLock, LockUnlock)
-{
- Mutex mtx;
- UniqueLock lock(mtx);
- EXPECT_FALSE(mtx.try_lock());
-
- lock.unlock();
- EXPECT_TRUE(mtx.try_lock());
-
- mtx.unlock();
- lock.lock();
- EXPECT_FALSE(mtx.try_lock());
-}
-
-TEST(SyncUniqueLock, Scope)
-{
- Mutex mtx;
-
- {
- UniqueLock lock(mtx);
- EXPECT_FALSE(mtx.try_lock());
- }
-
- EXPECT_TRUE(mtx.try_lock());
- mtx.unlock();
-}
-
-/*****************************************************************************/
-/*
- * SyncEvent tests
- */
-/*****************************************************************************/
-TEST(SyncEvent, WaitFor)
-{
- Mutex mutex;
- Condition cond;
- cond.init();
-
- for (int timeout_us : {50, 100, 500, 1000, 101000, 1001000})
- {
- const steady_clock::duration timeout = microseconds_from(timeout_us);
- UniqueLock lock(mutex);
- const steady_clock::time_point start = steady_clock::now();
- const bool on_timeout = !cond.wait_for(lock, timeout);
- const steady_clock::time_point stop = steady_clock::now();
- const steady_clock::duration waittime = stop - start;
- const int64_t waittime_us = count_microseconds(waittime);
-#if defined(ENABLE_STDCXX_SYNC) || !defined(_WIN32)
- // This check somehow fails on AppVeyor Windows VM with VS 2015 and pthreads.
- // - SyncEvent::wait_for( 50us) took 6us
- // - SyncEvent::wait_for(100us) took 4us
- if (on_timeout) {
- const int tolerance = timeout_us/1000;
- EXPECT_GE(waittime_us, timeout_us - tolerance);
- }
-#endif
- if (on_timeout) {
- // Give it 100 times the timeout, as this is
- // considered more than "crazy long", whereas we only
- // want to check if it has waited a finite amount of time.
- EXPECT_LE(waittime_us, 10 * 1001000); // biggest wait value
- }
-
- string spurious = on_timeout ? "" : " (SPURIOUS)";
-
- if (timeout_us < 1000)
- {
- cerr << "SyncEvent::wait_for(" << timeout_us << "us) took "
- << waittime_us << "us" << spurious << endl;
- }
- else
- {
- cerr << "SyncEvent::wait_for(" << count_milliseconds(timeout) << " ms) took "
- << (waittime_us / 1000.0) << " ms" << spurious << endl;
- }
- }
-
- cond.destroy();
-}
-
-TEST(SyncEvent, WaitForNotifyOne)
-{
- Mutex mutex;
- Condition cond;
- cond.init();
-
- const steady_clock::duration timeout = seconds_from(5);
-
- auto wait_async = [](Condition* cond, Mutex* mutex, const steady_clock::duration& timeout) {
- UniqueLock lock(*mutex);
- return cond->wait_for(lock, timeout);
- };
- auto wait_async_res = async(launch::async, wait_async, &cond, &mutex, timeout);
-
- EXPECT_EQ(wait_async_res.wait_for(chrono::milliseconds(100)), future_status::timeout);
- cond.notify_one();
- ASSERT_EQ(wait_async_res.wait_for(chrono::milliseconds(100)), future_status::ready);
- const bool wait_for_res = wait_async_res.get();
- EXPECT_TRUE(wait_for_res) << "Woken up by a notification";
-
- cond.destroy();
-}
-
-TEST(SyncEvent, WaitNotifyOne)
-{
- Mutex mutex;
- Condition cond;
- cond.init();
-
- auto wait_async = [](Condition* cond, Mutex* mutex) {
- UniqueLock lock(*mutex);
- return cond->wait(lock);
- };
- auto wait_async_res = async(launch::async, wait_async, &cond, &mutex);
-
- EXPECT_EQ(wait_async_res.wait_for(chrono::milliseconds(100)), future_status::timeout);
- cond.notify_one();
- ASSERT_EQ(wait_async_res.wait_for(chrono::milliseconds(100)), future_status::ready);
- wait_async_res.get();
-
- cond.destroy();
-}
-
-TEST(SyncEvent, WaitForTwoNotifyOne)
-{
- Mutex mutex;
- Condition cond;
- vector<int> notified_clients;
- cond.init();
- const steady_clock::duration timeout = seconds_from(3);
- const int VAL_SIGNAL = 42;
- const int VAL_NO_SIGNAL = 0;
-
- srt::sync::atomic<bool> resource_ready(true);
-
- auto wait_async = [&](Condition* cond, Mutex* mutex, const steady_clock::duration& timeout, int id) {
- UniqueLock lock(*mutex);
- if (cond->wait_for(lock, timeout) && resource_ready)
- {
- notified_clients.push_back(id);
- resource_ready = false;
- return VAL_SIGNAL;
- }
- return VAL_NO_SIGNAL;
- };
-
- using future_t = decltype(async(launch::async, wait_async, &cond, &mutex, timeout, 0));
-
- future_t future_result[2] = {
- async(launch::async, wait_async, &cond, &mutex, timeout, 0),
- async(launch::async, wait_async, &cond, &mutex, timeout, 1)
- };
-
- for (auto& wr: future_result)
- {
- ASSERT_EQ(wr.wait_for(chrono::milliseconds(100)), future_status::timeout);
- }
-
- {
- ScopedLock lk(mutex);
- cond.notify_one();
- }
-
- using wait_t = decltype(future_t().wait_for(chrono::microseconds(0)));
-
- wait_t wait_state[2] = {
- move(future_result[0].wait_for(chrono::microseconds(500))),
- move(future_result[1].wait_for(chrono::microseconds(500)))
- };
-
- cerr << "SyncEvent::WaitForTwoNotifyOne: NOTIFICATION came from " << notified_clients.size()
- << " clients:";
- for (auto& nof: notified_clients)
- cerr << " " << nof;
- cerr << endl;
-
- // Now exactly one waiting thread should become ready
- // Error if: 0 (none ready) or 2 (both ready, while notify_one was used)
- ASSERT_EQ(notified_clients.size(), 1U);
-
- const int ready = notified_clients[0];
- const int not_ready = (ready + 1) % 2;
-
- int future_val[2];
-
- // The READY client must have a valid value.
- ASSERT_TRUE(future_result[ready].valid());
- future_val[ready] = future_result[ready].get();
-
- // The NOT READY client MIGHT have a valid value, in which case we take expected 0,
- // or maybe not, in which case we set -1 value. Either of both must be the
- // result for the test to be valid.
- if (future_result[not_ready].valid())
- {
- future_val[not_ready] = future_result[not_ready].get();
- }
- else
- {
- future_val[not_ready] = VAL_NO_SIGNAL-1; // to match LE comparison
- }
-
- string disp_future[16];
- disp_future[int(future_status::timeout)] = "timeout";
- disp_future[int(future_status::ready)] = "ready";
-
- // Informational text
- cerr << "SyncEvent::WaitForTwoNotifyOne: READY THREAD: " << ready
- << " STATUS " << disp_future[int(wait_state[ready])]
- //<< " RESULT " << disp_state[0+future_val[ready]] << endl;
- << " RESULT " << future_val[ready] << endl;
-
- cerr << "SyncEvent::WaitForTwoNotifyOne: TMOUT THREAD: " << not_ready
- << " STATUS " << disp_future[int(wait_state[not_ready])]
- //<< " RESULT " << disp_state[0+future_val[not_ready]] << endl;
- << " RESULT " << future_val[not_ready] << endl;
-
- // The one that got the signal, should exit ready.
- // The one that didn't get the signal, should exit timeout.
- EXPECT_EQ(wait_state[ready], future_status::ready);
- EXPECT_EQ(wait_state[not_ready], future_status::timeout);
-
- // Same, expect these future to return the value
- // TURNED OFF for Windows, as there happens to be a
- // "spurious" signal causing this condition to fail,
- // even though it is declared valid and timed out.
- EXPECT_EQ(future_val[ready], VAL_SIGNAL);
-
- EXPECT_LE(future_val[not_ready], VAL_NO_SIGNAL);
-
- cond.destroy();
-}
-
-TEST(SyncEvent, WaitForTwoNotifyAll)
-{
- Mutex mutex;
- Condition cond;
- cond.init();
- const steady_clock::duration timeout = seconds_from(3);
-
- auto wait_async = [](Condition* cond, Mutex* mutex, const steady_clock::duration& timeout) {
- UniqueLock lock(*mutex);
- return cond->wait_for(lock, timeout);
- };
- auto wait_async1_res = async(launch::async, wait_async, &cond, &mutex, timeout);
- auto wait_async2_res = async(launch::async, wait_async, &cond, &mutex, timeout);
-
- EXPECT_EQ(wait_async1_res.wait_for(chrono::milliseconds(100)), future_status::timeout);
- EXPECT_EQ(wait_async2_res.wait_for(chrono::milliseconds(100)), future_status::timeout);
- cond.notify_all();
- // Now only one waiting thread should become ready
- const future_status status1 = wait_async1_res.wait_for(chrono::milliseconds(100));
- const future_status status2 = wait_async2_res.wait_for(chrono::milliseconds(100));
- EXPECT_EQ(status1, future_status::ready);
- EXPECT_EQ(status2, future_status::ready);
- // Expect both threads to wake up by condition
- EXPECT_TRUE(wait_async1_res.get());
- EXPECT_TRUE(wait_async2_res.get());
-
- cond.destroy();
-}
-
-TEST(SyncEvent, WaitForNotifyAll)
-{
- Mutex mutex;
- Condition cond;
- cond.init();
- const steady_clock::duration timeout = seconds_from(5);
-
- auto wait_async = [](Condition* cond, Mutex* mutex, const steady_clock::duration& timeout) {
- UniqueLock lock(*mutex);
- return cond->wait_for(lock, timeout);
- };
- auto wait_async_res = async(launch::async, wait_async, &cond, &mutex, timeout);
-
- EXPECT_EQ(wait_async_res.wait_for(chrono::milliseconds(500)), future_status::timeout);
- cond.notify_all();
- ASSERT_EQ(wait_async_res.wait_for(chrono::milliseconds(500)), future_status::ready);
- const bool wait_for_res = wait_async_res.get();
- EXPECT_TRUE(wait_for_res) << "Woken up by condition";
-
- cond.destroy();
-}
-
-/*****************************************************************************/
-/*
- * CThread
- */
- /*****************************************************************************/
-void* dummythread(void* param)
-{
- *(bool*)(param) = true;
- return nullptr;
-}
-
-TEST(SyncThread, Joinable)
-{
- CThread foo;
- srt::sync::atomic<bool> thread_finished;
-
- StartThread(foo, dummythread, (void*)&thread_finished, "DumyThread");
-
- EXPECT_TRUE(foo.joinable());
- while (!thread_finished)
- {
- std::this_thread::sleep_for(chrono::milliseconds(50));
- }
- EXPECT_TRUE(foo.joinable());
- foo.join();
- EXPECT_FALSE(foo.joinable());
-}
-
-/*****************************************************************************/
-/*
- * FormatTime
- */
-/*****************************************************************************/
-#if !defined(__GNUC__) || defined(__clang__) || (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9))
-//#if !defined(__GNUC__) || (__GNUC__ > 4)
-//#if !defined(__GNUC__) || (__GNUC__ >= 5)
-// g++ before 4.9 (?) does not support regex and crashes on execution.
-
-TEST(Sync, FormatTime)
-{
- auto parse_time = [](const string& timestr) -> long long {
- // Example string: 1D 02:10:55.972651 [STD]
- const regex rex("([[:digit:]]+D )?([[:digit:]]{2}):([[:digit:]]{2}):([[:digit:]]{2}).([[:digit:]]{6,}) \\[STDY\\]");
- std::smatch sm;
- EXPECT_TRUE(regex_match(timestr, sm, rex));
- EXPECT_LE(sm.size(), 6U);
- if (sm.size() != 6 && sm.size() != 5)
- return 0;
-
- // Day may be missing if zero
- const long long d = sm[1].matched ? std::stoi(sm[1]) : 0;
- const long long h = std::stoll(sm[2]);
- const long long m = std::stoll(sm[3]);
- const long long s = std::stoll(sm[4]);
- const long long u = std::stoll(sm[5]);
-
- return u + s * 1000000 + m * 60000000 + h * 60 * 60 * 1000000 + d * 24 * 60 * 60 * 1000000;
- };
-
- auto print_timediff = [&parse_time](const string& desc, const string& time, const string& time_base) {
- const long long diff = parse_time(time) - parse_time(time_base);
- cerr << desc << time << " (" << diff << " us)" << endl;
- };
-
- const auto a = steady_clock::now();
- const string time1 = FormatTime(a);
- const string time2 = FormatTime(a);
- const string time3 = FormatTime(a + milliseconds_from(500));
- const string time4 = FormatTime(a + seconds_from(1));
- const string time5 = FormatTime(a + seconds_from(5));
- const string time6 = FormatTime(a + milliseconds_from(-4350));
- cerr << "Current time formated: " << time1 << endl;
- const long long diff_2_1 = parse_time(time2) - parse_time(time1);
- cerr << "Same time formated again: " << time2 << " (" << diff_2_1 << " us)" << endl;
- print_timediff("Same time formated again: ", time2, time1);
- print_timediff("Time +500 ms formated: ", time3, time1);
- print_timediff("Time +1 sec formated: ", time4, time1);
- print_timediff("Time +5 sec formated: ", time5, time1);
- print_timediff("Time -4350 ms formated: ", time6, time1);
-
- EXPECT_TRUE(time1 == time2);
-}
-
-TEST(Sync, FormatTimeSys)
-{
- auto parse_time = [](const string& timestr) -> long long {
- const regex rex("([[:digit:]]{2}):([[:digit:]]{2}):([[:digit:]]{2}).([[:digit:]]{6}) \\[SYST\\]");
- std::smatch sm;
- EXPECT_TRUE(regex_match(timestr, sm, rex));
- EXPECT_EQ(sm.size(), 5U);
- if (sm.size() != 5)
- return 0;
-
- const long long h = std::stoi(sm[1]);
- const long long m = std::stoi(sm[2]);
- const long long s = std::stoi(sm[3]);
- const long long u = std::stoi(sm[4]);
-
- return u + s * 1000000 + m * 60000000 + h * 60 * 60 * 1000000;
- };
-
- auto print_timediff = [&parse_time](const string& desc, const string& time, const string& time_base) {
- const long long diff = parse_time(time) - parse_time(time_base);
- cerr << desc << time << " (" << diff << " us)" << endl;
- };
-
- const steady_clock::time_point a = steady_clock::now();
- const string time1 = FormatTimeSys(a);
- const string time2 = FormatTimeSys(a);
- const string time3 = FormatTimeSys(a + milliseconds_from(500));
- const string time4 = FormatTimeSys(a + seconds_from(1));
- const string time5 = FormatTimeSys(a + seconds_from(5));
- const string time6 = FormatTimeSys(a + milliseconds_from(-4350));
- cerr << "Current time formated: " << time1 << endl;
- const long long diff_2_1 = parse_time(time2) - parse_time(time1);
- cerr << "Same time formated again: " << time2 << " (" << diff_2_1 << " us)" << endl;
- print_timediff("Same time formated again: ", time2, time1);
- print_timediff("Time +500 ms formated: ", time3, time1);
- print_timediff("Time +1 sec formated: ", time4, time1);
- print_timediff("Time +5 sec formated: ", time5, time1);
- print_timediff("Time -4350 ms formated: ", time6, time1);
-
- EXPECT_TRUE(time1 == time2);
-}
-#endif
+++ /dev/null
-#include <algorithm>
-#include <string>
-
-#include "gtest/gtest.h"
-#include "threadname.h"
-
-using namespace srt;
-
-TEST(ThreadName, GetSet)
-{
- std::string name("getset");
- char buf[ThreadName::BUFSIZE * 2];
-
- memset(buf, 'a', sizeof(buf));
- ASSERT_EQ(ThreadName::get(buf), true);
- // ensure doesn't write out-of-range
- size_t max = ThreadName::BUFSIZE - 1;
- ASSERT_LE(strlen(buf), max);
-
- if (ThreadName::DUMMY_IMPL)
- return;
-
- ASSERT_EQ(ThreadName::set(name), true);
- memset(buf, 'a', sizeof(buf));
- ASSERT_EQ(ThreadName::get(buf), true);
- ASSERT_EQ(buf, name);
-}
-
-TEST(ThreadName, AutoReset)
-{
- const std::string old_name("old");
- std::string new_name("new-name");
- if (ThreadName::DUMMY_IMPL)
- {
- // just make sure the API is correct
- ThreadName t(std::string("test"));
- return;
- }
-
- ASSERT_EQ(ThreadName::set(old_name), true);
- std::string name;
- ASSERT_EQ(ThreadName::get(name), true);
- ASSERT_EQ(name, old_name);
-
- {
- ThreadName threadName(new_name);
- ASSERT_EQ(ThreadName::get(name), true);
- ASSERT_EQ(name, new_name);
- }
-
- ASSERT_EQ(ThreadName::get(name), true);
- ASSERT_EQ(name, old_name);
-
- {
- new_name.resize(std::max<size_t>(512, ThreadName::BUFSIZE * 2), 'z');
- ThreadName threadName(new_name);
- ASSERT_EQ(ThreadName::get(name), true);
- ASSERT_EQ(new_name.compare(0, name.size(), name), 0);
- }
-
- ASSERT_EQ(ThreadName::get(name), true);
- ASSERT_EQ(name, old_name);
-}
+++ /dev/null
-#include "gtest/gtest.h"
-#include <chrono>
-#include <thread>
-#include <array>
-#include <numeric> // std::accumulate
-#include "common.h"
-#include "sync.h"
-
-
-
-
-TEST(CTimer, DISABLED_SleeptoAccuracy)
-{
- using namespace std;
- using namespace srt::sync;
-
- const int num_samples = 1000;
- array<uint64_t, num_samples> sleeps_us;
-
- const uint64_t sleep_intervals_us[] = { 1, 5, 10, 50, 100, 250, 500, 1000, 5000, 10000 };
-
- CTimer timer;
-
- for (uint64_t interval_us : sleep_intervals_us)
- {
- for (int i = 0; i < num_samples; i++)
- {
- steady_clock::time_point currtime = steady_clock::now();
- timer.sleep_until(currtime + microseconds_from(interval_us));
-
- steady_clock::time_point new_time = steady_clock::now();
- sleeps_us[i] = count_microseconds(new_time - currtime);
- }
-
- cerr << "Target sleep duration: " << interval_us << " us\n";
- cerr << "avg sleep duration: " << accumulate(sleeps_us.begin(), sleeps_us.end(), (uint64_t) 0) / num_samples << " us\n";
- cerr << "min sleep duration: " << *min_element(sleeps_us.begin(), sleeps_us.end()) << " us\n";
- cerr << "max sleep duration: " << *max_element(sleeps_us.begin(), sleeps_us.end()) << " us\n";
- cerr << "\n";
- }
-}
-
-
+++ /dev/null
-#include <array>
-#include <vector>
-#include "gtest/gtest.h"
-#include "queue.h"
-
-using namespace std;
-using namespace srt;
-
-/// Create CUnitQueue with queue size of 4 units.
-/// The size of 4 is chosen on purpose, because
-/// CUnitQueue::getNextAvailUnit(..) has the following
-/// condition `if (m_iCount * 10 > m_iSize * 9)`. With m_iSize = 4
-/// it will be false up until m_iCount becomes 4.
-/// And there was an issue in getNextAvailUnit(..) in taking
-/// the very last element of the queue (it was skipped).
-TEST(CUnitQueue, Increase)
-{
- const int buffer_size_pkts = 4;
- CUnitQueue unit_queue;
- unit_queue.init(buffer_size_pkts, 1500, AF_INET);
-
- vector<CUnit*> taken_units;
- for (int i = 0; i < 5 * buffer_size_pkts; ++i)
- {
- CUnit* unit = unit_queue.getNextAvailUnit();
- ASSERT_NE(unit, nullptr);
- unit_queue.makeUnitGood(unit);
- taken_units.push_back(unit);
- }
-}
-
-/// Create CUnitQueue with queue size of 4 units.
-/// Then after requesting the 5th unit, free the previous
-/// four units. This makes the previous queue completely free.
-/// Requesting the 5th unit, there would be 3 units available in the
-/// beginning of the same queue.
-TEST(CUnitQueue, IncreaseAndFree)
-{
- const int buffer_size_pkts = 4;
- CUnitQueue unit_queue;
- unit_queue.init(buffer_size_pkts, 1500, AF_INET);
-
- CUnit* taken_unit = nullptr;
- for (int i = 0; i < 5 * buffer_size_pkts; ++i)
- {
- CUnit* unit = unit_queue.getNextAvailUnit();
- ASSERT_NE(unit, nullptr);
- unit_queue.makeUnitGood(unit);
-
- if (taken_unit)
- unit_queue.makeUnitFree(taken_unit);
-
- taken_unit = unit;
- }
-}
-
-/// Create CUnitQueue with queue size of 4 units.
-/// Then after requesting the 5th unit, free the previous
-/// four units. This makes the previous queue completely free.
-/// Requesting the 9th unit, there would be 4 units available in the
-/// Thus the test checks if
-TEST(CUnitQueue, IncreaseAndFreeGrouped)
-{
- const int buffer_size_pkts = 4;
- CUnitQueue unit_queue;
- unit_queue.init(buffer_size_pkts, 1500, AF_INET);
-
- vector<CUnit*> taken_units;
- for (int i = 0; i < 5 * buffer_size_pkts; ++i)
- {
- CUnit* unit = unit_queue.getNextAvailUnit();
- ASSERT_NE(unit, nullptr);
- unit_queue.makeUnitGood(unit);
-
- if (taken_units.size() >= buffer_size_pkts)
- {
- for_each(taken_units.begin(), taken_units.end(),
- [&unit_queue](CUnit* u) { unit_queue.makeUnitFree(u); });
-
- taken_units.clear();
- }
-
- taken_units.push_back(unit);
- EXPECT_LE(unit_queue.capacity(), 2 * buffer_size_pkts)
- << "Buffer capacity should not exceed two queues of 4 units";
- }
-}
+++ /dev/null
-#include <iostream>
-#include <chrono>
-#include <future>
-#include <thread>
-#include <condition_variable>
-#include "gtest/gtest.h"
-#define SRT_TEST_CIRCULAR_BUFFER
-#include "api.h"
-#include "common.h"
-
-using namespace std;
-using namespace srt;
-
-
-// To test CircularBuffer
-struct Double
-{
- double d;
- size_t instance;
- static size_t sourceid;
-
- Double(): d(0.0)
- {
- instance = ++sourceid;
- IF_HEAVY_LOGGING(cerr << "(Double/" << instance << ": empty costruction)\n");
- }
-
- Double(double dd): d(dd)
- {
- instance = ++sourceid;
- IF_HEAVY_LOGGING(cerr << "(Double:/" << instance << " init construction:" << dd << ")\n");
- }
-
- Double(const Double& dd): d(dd.d)
- {
- instance = ++sourceid;
- IF_HEAVY_LOGGING(cerr << "(Double:/" << instance << " copy construction:" << dd.d << " object/" << dd.instance << ")\n");
- }
-
- operator double() { return d; }
-
- ~Double()
- {
- IF_HEAVY_LOGGING(cerr << "(Double:/" << instance << " destruction:" << d << ")\n");
- }
-
- void operator=(double dd)
- {
- IF_HEAVY_LOGGING(cerr << "(Double:/" << instance << " copy assignment:" << d << " -> " << dd << " value)\n");
- d = dd;
- }
-
- void operator=(const Double& dd)
- {
- IF_HEAVY_LOGGING(cerr << "(Double:/" << instance << " copy assignment:" << d << " -> " << dd.d << " object/" << dd.instance << ")\n");
- d = dd.d;
- }
-
- // Required for template-based gtest :(
- friend bool operator==(const Double& l, double r) { return l.d == r; }
- friend bool operator==(double l, const Double r) { return l == r.d; }
- bool operator == (const Double& r) { return d == r; }
-};
-
-size_t Double::sourceid = 0;
-
-
-template <class Val> inline
-void ShowCircularBuffer(const CircularBuffer<Val>& buf)
-{
- cerr << "SIZE: " << buf.size() << " FREE:" << buf.spaceleft() << " BEGIN:" << buf.m_xBegin << " END: " << buf.m_xEnd << endl;
- for (int i = 0; i < buf.size(); ++i)
- {
- Double v;
- if (buf.get(i, (v)))
- cerr << "[" << i << "] = " << v << endl;
- else
- cerr << "[" << i << "] EMPTY!\n";
- }
-}
-
-struct Add
-{
- Double v;
- Add(const Double& vv): v(vv) {}
- void operator()(Double& accessed, bool isnew)
- {
- if (isnew)
- accessed = v;
- else
- accessed = Double(accessed.d + v.d);
- }
-};
-
-
-TEST(CircularBuffer, Overall)
-{
- using namespace std;
-
- // Create some odd number of elements in a circular buffer.
-
- CircularBuffer<Double> buf(7);
-
- cerr << dec;
-
- // Now, add 3 elements to it and check if succeeded.
- buf.push(11.2);
- buf.push(12.3);
- buf.push(13.4);
-
- IF_HEAVY_LOGGING(cerr << "After adding 3 elements: size=" << buf.size() << " capacity=" << buf.capacity() << ":\n");
- IF_HEAVY_LOGGING(ShowCircularBuffer(buf));
- ASSERT_EQ(buf.size(), 3);
-
- IF_HEAVY_LOGGING(cerr << "Adding element at position 5:\n");
- EXPECT_TRUE(buf.set(5, 15.5));
- IF_HEAVY_LOGGING(ShowCircularBuffer(buf));
- ASSERT_EQ(buf.size(), 6);
-
- IF_HEAVY_LOGGING(cerr << "Adding element at position 7 (should fail):\n");
- EXPECT_FALSE(buf.set(7, 10.0));
- IF_HEAVY_LOGGING(ShowCircularBuffer(buf));
- ASSERT_EQ(buf.size(), 6);
-
- IF_HEAVY_LOGGING(cerr << "Dropping first 2 elements:\n");
- buf.drop(2);
- IF_HEAVY_LOGGING(ShowCircularBuffer(buf));
- ASSERT_EQ(buf.size(), 4);
-
- IF_HEAVY_LOGGING(cerr << "Adding again element at position 6 (should roll):\n");
- buf.set(6, 22.1);
- IF_HEAVY_LOGGING(ShowCircularBuffer(buf));
-
- IF_HEAVY_LOGGING(cerr << "Adding element at existing position 2 (overwrite):\n");
- buf.set(2, 33.1);
- IF_HEAVY_LOGGING(ShowCircularBuffer(buf));
-
- IF_HEAVY_LOGGING(cerr << "Adding element at existing position 3 (no overwrite):\n");
- buf.set(3, 44.4, false);
- IF_HEAVY_LOGGING(ShowCircularBuffer(buf));
-
- Double output;
- // [0] = 13.4 (after dropping first 2 elements)
- EXPECT_TRUE(buf.get(0, (output)));
- ASSERT_EQ(output, 13.4);
- // [2] = 33.1 overwriting
- EXPECT_TRUE(buf.get(2, (output)));
- ASSERT_EQ(output, 33.1);
- // [3] = was 15.5, requested to set 44.4, but not overwriting
- EXPECT_TRUE(buf.get(3, (output)));
- ASSERT_EQ(output, 15.5);
- // [6] = 22.1 (as set with rolling)
- EXPECT_TRUE(buf.get(6, (output)));
- ASSERT_EQ(output, 22.1);
-
- IF_HEAVY_LOGGING(cerr << "Dropping first 4 positions:\n");
- buf.drop(4);
- IF_HEAVY_LOGGING(ShowCircularBuffer(buf));
- EXPECT_TRUE(buf.get(2, (output))); // Was 6 before dropping
- ASSERT_EQ(output.d, 22.1);
-
- IF_HEAVY_LOGGING(cerr << "Pushing 1 aslong there is capacity:\n");
- int i = 0;
- while (buf.push(1) != -1)
- {
- IF_HEAVY_LOGGING(cerr << "Pushed, begin=" << buf.m_xBegin << " end=" << buf.m_xEnd << endl);
- ++i;
- }
- IF_HEAVY_LOGGING(cerr << "Done " << i << " operations, buffer:\n");
- IF_HEAVY_LOGGING(ShowCircularBuffer(buf));
-
- IF_HEAVY_LOGGING(cerr << "Updating value at position 5:\n");
- EXPECT_TRUE(buf.update(5, Add(3.33)));
- IF_HEAVY_LOGGING(ShowCircularBuffer(buf));
- EXPECT_TRUE(buf.get(5, (output)));
- ASSERT_EQ(output, 4.33);
-
- int offset = 9;
- IF_HEAVY_LOGGING(cerr << "Forced adding at position 9 with dropping (capacity: " << buf.capacity() << "):\n");
- // State we already know it has failed. Calculate drop size.
- int dropshift = offset - (buf.capacity() - 1); // buf.capacity()-1 is the latest position
- offset -= dropshift;
- IF_HEAVY_LOGGING(cerr << "Need to drop: " << dropshift << " New offset:" << offset << endl);
- ASSERT_GE(dropshift, 0);
- if (dropshift > 0)
- {
- buf.drop(dropshift);
- IF_HEAVY_LOGGING(cerr << "AFTER DROPPING:\n");
- IF_HEAVY_LOGGING(ShowCircularBuffer(buf));
- EXPECT_TRUE(buf.set(offset, 99.1, true));
-
- // size() - 1 is the latest possible offset
- ASSERT_EQ(buf.size() - 1 + dropshift, 9);
- }
- else
- {
- IF_HEAVY_LOGGING(cerr << "NEGATIVE DROP!\n");
- }
- IF_HEAVY_LOGGING(ShowCircularBuffer(buf));
- int size = buf.size();
-
- IF_HEAVY_LOGGING(cerr << "Dropping rest of the items (passing " << (size) << "):\n");
-
- // NOTE: 'drop' gets the POSITION as argument, but this position
- // is allowed to be past the last addressable position. When passing
- // the current size, it should make the container empty.
- buf.drop(size);
- EXPECT_TRUE(buf.empty());
-
- IF_HEAVY_LOGGING(ShowCircularBuffer(buf));
-
- IF_HEAVY_LOGGING(cerr << "DONE.\n");
-}
-
-TEST(ConfigString, Setting)
-{
- using namespace std;
-
- static const auto STRSIZE = 20;
- StringStorage<STRSIZE> s;
-
- EXPECT_TRUE(s.empty());
- EXPECT_EQ(s.size(), 0U);
- EXPECT_EQ(s.str(), std::string());
-
- char example_ac1[] = "example_long";
- char example_ac2[] = "short";
- char example_ac3[] = "example_longer";
- char example_acx[] = "example_long_excessively";
- char example_ace[] = "";
-
- // According to the standard, this array gets automatically
- // the number of characters + 1 for terminating 0. Get sizeof()-1
- // to get the number of characters.
-
- EXPECT_TRUE(s.set(example_ac1, sizeof (example_ac1)-1));
- EXPECT_EQ(s.size(), sizeof (example_ac1)-1);
- EXPECT_FALSE(s.empty());
-
- EXPECT_TRUE(s.set(example_ac2, sizeof (example_ac2)-1));
- EXPECT_EQ(s.size(), sizeof (example_ac2)-1);
-
- EXPECT_TRUE(s.set(example_ac3, sizeof (example_ac3)-1));
- EXPECT_EQ(s.size(), sizeof (example_ac3)-1);
-
- EXPECT_FALSE(s.set(example_acx, sizeof (example_acx)-1));
- EXPECT_EQ(s.size(), sizeof (example_ac3)-1);
-
- EXPECT_TRUE(s.set(example_ace, sizeof (example_ace)-1));
- EXPECT_EQ(s.size(), 0U);
-
- string example_s1 = "example_long";
- string example_s2 = "short";
- string example_s3 = "example_longer";
- string example_sx = "example_long_excessively";
- string example_se = "";
-
- EXPECT_TRUE(s.set(example_s1));
- EXPECT_EQ(s.size(), example_s1.size());
- EXPECT_FALSE(s.empty());
-
- EXPECT_TRUE(s.set(example_s2));
- EXPECT_EQ(s.size(), example_s2.size());
-
- EXPECT_TRUE(s.set(example_s3));
- EXPECT_EQ(s.size(), example_s3.size());
-
- EXPECT_FALSE(s.set(example_sx));
- EXPECT_EQ(s.size(), example_s3.size());
-
- EXPECT_TRUE(s.set(example_se));
- EXPECT_EQ(s.size(), 0U);
- EXPECT_TRUE(s.empty());
-}
+++ /dev/null
-Testing
-=======
-
-This directory contains applications used for testing and development only.
-
-They may contain experimental versions or not fully functioning features.
-
-Every application has its own individual Manifest file (`*.maf`), which defines
-of which source files particular application comprises. They may be contained
-either in the same directory, or in any other subproject.
+++ /dev/null
-/*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2018 Haivision Systems Inc.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- */
-
-/*****************************************************************************
-written by
- Haivision Systems Inc.
- *****************************************************************************/
-
-#ifdef _WIN32
-#include <direct.h>
-#endif
-#include <iostream>
-#include <iterator>
-#include <vector>
-#include <map>
-#include <tuple>
-#include <stdexcept>
-#include <string>
-#include <thread>
-#include <chrono>
-#include <sys/stat.h>
-#include <srt.h>
-#include <udt.h>
-#include <logging.h>
-
-#include "apputil.hpp"
-#include "uriparser.hpp"
-#include "logsupport.hpp"
-#include "socketoptions.hpp"
-#include "verbose.hpp"
-#include "testmedia.hpp"
-
-#ifndef S_ISDIR
-#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
-#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
-#endif
-
-bool Upload(UriParser& srt, UriParser& file);
-bool Download(UriParser& srt, UriParser& file);
-
-static size_t g_buffer_size = 1456;
-static bool g_skip_flushing = false;
-
-using namespace std;
-
-srt_logging::Logger applog(SRT_LOGFA_APP, srt_logger_config, "srt-file");
-
-int main( int argc, char** argv )
-{
- vector<OptionScheme> optargs;
-
- OptionName
- o_loglevel ((optargs), "<severity=fatal|error|note|warning|debug> Minimum severity for logs", "ll", "loglevel"),
- o_buffer ((optargs), "<size[b]=1456> Size of the single reading operation", "b", "buffer"),
- o_verbose ((optargs), " Print extra verbos output", "v", "verbose"),
- o_noflush ((optargs), " Do not wait safely 5 seconds at the end to flush buffers", "sf", "skipflush"),
- o_help ((optargs), " This help", "?", "help", "-help")
- ;
-
- options_t params = ProcessOptions(argv, argc, optargs);
-
- bool need_help = OptionPresent(params, o_help);
-
- //*
- cerr << "OPTIONS (DEBUG)\n";
- for (auto o: params)
- {
- cerr << "[" << o.first << "] ";
- copy(o.second.begin(), o.second.end(), ostream_iterator<string>(cerr, " "));
- cerr << endl;
- }
- // */
-
- if (need_help)
- {
- cerr << "Usage:\n";
- cerr << " " << argv[0] << " [options] <input> <output>\n";
- cerr << "*** (Position of [options] is unrestricted.)\n";
- cerr << "*** (<variadic...> option parameters can be only terminated by a next option.)\n";
- cerr << "where:\n";
- cerr << " <input> and <output> is specified by an URI.\n";
- cerr << "SUPPORTED URI SCHEMES:\n";
- cerr << " srt: use SRT connection\n";
- cerr << " udp: read from bound UDP socket or send to given address as UDP\n";
- cerr << " file (default if scheme not specified) specified as:\n";
- cerr << " - empty host/port and absolute file path in the URI\n";
- cerr << " - only a filename, also as a relative path\n";
- cerr << " - file://con ('con' as host): designates stdin or stdout\n";
- cerr << "OPTIONS HELP SYNTAX: -option <parameter[unit]=default[meaning]>:\n";
- for (auto os: optargs)
- cout << OptionHelpItem(*os.pid) << endl;
- return 1;
- }
-
- vector<string> args = params[""];
- if ( args.size() < 2 )
- {
- cerr << "Usage: " << argv[0] << " <source> <target>\n";
- return 1;
- }
-
- string loglevel = Option<OutString>(params, "error", o_loglevel);
- srt_logging::LogLevel::type lev = SrtParseLogLevel(loglevel);
- srt::setloglevel(lev);
- srt::addlogfa(SRT_LOGFA_APP);
-
- bool verbo = OptionPresent(params, o_verbose);
- if (verbo)
- {
- Verbose::on = true;
- Verbose::cverb = &std::cout;
- }
-
- string bs = Option<OutString>(params, "", o_buffer);
- if ( bs != "" )
- {
- ::g_buffer_size = stoi(bs);
- }
-
- string sf = Option<OutString>(params, "no", o_noflush);
- if (sf == "" || !false_names.count(sf))
- ::g_skip_flushing = true;
-
- string source = args[0];
- string target = args[1];
-
- UriParser us(source), ut(target);
-
- Verb() << "SOURCE type=" << us.scheme() << ", TARGET type=" << ut.scheme();
-
- try
- {
- if (us.scheme() == "srt")
- {
- if (ut.scheme() != "file")
- {
- cerr << "SRT to FILE should be specified\n";
- return 1;
- }
- Download(us, ut);
- }
- else if (ut.scheme() == "srt")
- {
- if (us.scheme() != "file")
- {
- cerr << "FILE to SRT should be specified\n";
- return 1;
- }
- Upload(ut, us);
- }
- else
- {
- cerr << "SRT URI must be one of given media.\n";
- return 1;
- }
- }
- catch (std::exception& x)
- {
- cerr << "ERROR: " << x.what() << endl;
- return 1;
- }
-
-
- return 0;
-}
-
-tuple<string, string> ExtractPath(string path)
-{
- //string& dir = r_dir;
- //string& fname = r_fname;
-
- string directory = path;
- string filename = "";
-
- struct stat state;
- stat(path.c_str(), &state);
- if (!S_ISDIR(state.st_mode))
- {
- // Extract directory as a butlast part of path
- size_t pos = path.find_last_of("/");
- if ( pos == string::npos )
- {
- filename = path;
- directory = ".";
- }
- else
- {
- directory = path.substr(0, pos);
- filename = path.substr(pos+1);
- }
- }
-
- if (directory[0] != '/')
- {
- // Glue in the absolute prefix of the current directory
- // to make it absolute. This is needed to properly interpret
- // the fixed uri.
- static const size_t s_max_path = 4096; // don't care how proper this is
- char tmppath[s_max_path];
- char* gwd = getcwd(tmppath, s_max_path);
- if ( !gwd )
- {
- // Don't bother with that now. We need something better for that anyway.
- throw std::invalid_argument("Path too long");
- }
- string wd = gwd;
-
- directory = wd + "/" + directory;
- }
-
- return make_tuple(directory, filename);
-}
-
-bool DoUpload(UriParser& ut, string path, string filename)
-{
- SrtModel m(ut.host(), ut.portno(), ut.parameters());
-
- string id = filename;
- Verb() << "Passing '" << id << "' as stream ID\n";
-
- m.Establish((id));
-
- // Check if the filename was changed
- if (id != filename)
- {
- cerr << "SRT caller has changed the filename '" << filename << "' to '" << id << "' - rejecting\n";
- return false;
- }
-
- Verb() << "USING ID: " << id;
-
- // SrtTarget* tp = new SrtTarget;
- // tp->StealFrom(m);
- // unique_ptr<Target> target(tp);
-
- //SRTSOCKET ss = tp->Socket();
- SRTSOCKET ss = m.Socket();
-
- // Use a manual loop for reading from SRT
- vector<char> buf(::g_buffer_size);
-
- ifstream ifile(path, ios::binary);
- if ( !ifile )
- {
- cerr << "Error opening file: '" << path << "'";
- return false;
- }
-
- for (;;)
- {
- size_t n = ifile.read(buf.data(), ::g_buffer_size).gcount();
- size_t shift = 0;
- while (n > 0)
- {
- int st = srt_send(ss, buf.data()+shift, n);
- Verb() << "Upload: " << n << " --> " << st << (!shift ? string() : "+" + Sprint(shift));
- if (st == SRT_ERROR)
- {
- cerr << "Upload: SRT error: " << srt_getlasterror_str() << endl;
- return false;
- }
-
- n -= st;
- shift += st;
- }
-
- if (ifile.eof())
- break;
-
- if ( !ifile.good() )
- {
- cerr << "ERROR while reading file\n";
- return false;
- }
- }
-
- if ( !::g_skip_flushing )
- {
- // send-flush-loop
-
- for (;;)
- {
- size_t bytes;
- size_t blocks;
- int st = srt_getsndbuffer(ss, &blocks, &bytes);
- if (st == SRT_ERROR)
- {
- cerr << "Error in srt_getsndbuffer: " << srt_getlasterror_str() << endl;
- return false;
- }
- if (bytes == 0)
- {
- Verb() << "Sending buffer DEPLETED - ok.";
- break;
- }
- Verb() << "Sending buffer still: bytes=" << bytes << " blocks=" << blocks;
- this_thread::sleep_for(chrono::milliseconds(250));
- }
- }
-
- return true;
-}
-
-bool DoDownload(UriParser& us, string directory, string filename)
-{
- SrtModel m(us.host(), us.portno(), us.parameters());
-
- string id = filename;
- m.Establish((id));
-
- // Disregard the filename, unless the destination file exists.
-
- string path = directory + "/" + id;
- struct stat state;
- if ( stat(path.c_str(), &state) == -1 )
- {
- switch ( errno )
- {
- case ENOENT:
- // This is expected, go on.
- break;
-
- default:
- cerr << "Download: error '" << errno << "'when checking destination location: " << path << endl;
- return false;
- }
- }
- else
- {
- // Check if destination is a regular file, if so, allow to overwrite.
- // Otherwise reject.
- if (!S_ISREG(state.st_mode))
- {
- cerr << "Download: target location '" << path << "' does not designate a regular file.\n";
- return false;
- }
- }
-
- ofstream ofile(path, ios::out | ios::trunc | ios::binary);
- if ( !ofile.good() )
- {
- cerr << "Download: can't create output file: " << path;
- return false;
- }
- SRTSOCKET ss = m.Socket();
-
- Verb() << "Downloading from '" << us.uri() << "' to '" << path;
-
- vector<char> buf(::g_buffer_size);
-
- for (;;)
- {
- int n = srt_recv(ss, buf.data(), ::g_buffer_size);
- if (n == SRT_ERROR)
- {
- cerr << "Download: SRT error: " << srt_getlasterror_str() << endl;
- return false;
- }
-
- if (n == 0)
- {
- Verb() << "Download COMPLETE.";
- break;
- }
-
- // Write to file any amount of data received
-
- Verb() << "Download: --> " << n;
- ofile.write(buf.data(), n);
- }
-
- return true;
-}
-
-bool Upload(UriParser& srt_target_uri, UriParser& fileuri)
-{
- if ( fileuri.scheme() != "file" )
- {
- cerr << "Upload: source accepted only as a file\n";
- return false;
- }
- // fileuri is source-reading file
- // srt_target_uri is SRT target
-
- string path = fileuri.path();
- string directory, filename;
- tie(directory, filename) = ExtractPath(path);
- Verb() << "Extract path '" << path << "': directory=" << directory << " filename=" << filename;
- // Set ID to the filename.
- // Directory will be preserved.
-
- // Add some extra parameters.
- srt_target_uri["transtype"] = "file";
-
- return DoUpload(srt_target_uri, path, filename);
-}
-
-bool Download(UriParser& srt_source_uri, UriParser& fileuri)
-{
- if (fileuri.scheme() != "file" )
- {
- cerr << "Download: target accepted only as a file\n";
- return false;
- }
-
- string path = fileuri.path(), directory, filename;
- tie(directory, filename) = ExtractPath(path);
-
- srt_source_uri["transtype"] = "file";
-
- return DoDownload(srt_source_uri, directory, filename);
-}
-
+++ /dev/null
-
-SOURCES
-srt-test-file.cpp
-testmedia.cpp
-../apps/apputil.cpp
-../apps/verbose.cpp
-../apps/socketoptions.cpp
-../apps/uriparser.cpp
-../apps/logsupport.cpp
-../apps/logsupport_appdefs.cpp
-
+++ /dev/null
-/*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2018 Haivision Systems Inc.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- */
-
-// NOTE: This application uses C++11.
-
-// This program uses quite a simple architecture, which is mainly related to
-// the way how it's invoked: srt-test-live <source> <target> (plus options).
-//
-// The media for <source> and <target> are filled by abstract classes
-// named Source and Target respectively. Most important virtuals to
-// be filled by the derived classes are Source::Read and Target::Write.
-//
-// For SRT please take a look at the SrtCommon class first. This contains
-// everything that is needed for creating an SRT medium, that is, making
-// a connection as listener, as caller, and as rendezvous. The listener
-// and caller modes are built upon the same philosophy as those for
-// BSD/POSIX socket API (bind/listen/accept or connect).
-//
-// The instance class is selected per details in the URI (usually scheme)
-// and then this URI is used to configure the medium object. Medium-specific
-// options are specified in the URI: SCHEME://HOST:PORT?opt1=val1&opt2=val2 etc.
-//
-// Options for connection are set by ConfigurePre and ConfigurePost.
-// This is a philosophy that exists also in BSD/POSIX sockets, just not
-// officially mentioned:
-// - The "PRE" options must be set prior to connecting and can't be altered
-// on a connected socket, however if set on a listening socket, they are
-// derived by accept-ed socket.
-// - The "POST" options can be altered any time on a connected socket.
-// They MAY have also some meaning when set prior to connecting; such
-// option is SRTO_RCVSYN, which makes connect/accept call asynchronous.
-// Because of that this option is treated special way in this app.
-//
-// See 'srt_options' global variable (common/socketoptions.hpp) for a list of
-// all options.
-
-// MSVS likes to complain about lots of standard C functions being unsafe.
-#ifdef _MSC_VER
-#define _CRT_SECURE_NO_WARNINGS 1
-#endif
-
-#define REQUIRE_CXX11 1
-
-#include <cctype>
-#include <iostream>
-#include <fstream>
-#include <string>
-#include <map>
-#include <set>
-#include <vector>
-#include <memory>
-#include <algorithm>
-#include <iterator>
-#include <stdexcept>
-#include <cstring>
-#include <csignal>
-#include <chrono>
-#include <thread>
-
-#include "apputil.hpp"
-#include "uriparser.hpp" // UriParser
-#include "socketoptions.hpp"
-#include "logsupport.hpp"
-#include "testmedia.hpp" // requires access to SRT-dependent globals
-#include "verbose.hpp"
-
-// NOTE: This is without "haisrt/" because it uses an internal path
-// to the library. Application using the "installed" library should
-// use <srt/srt.h>
-#include <srt.h>
-#include <udt.h> // This TEMPORARILY contains extra C++-only SRT API.
-#include <logging.h>
-
-using namespace std;
-
-srt_logging::Logger applog(SRT_LOGFA_APP, srt_logger_config, "srt-live");
-
-map<string,string> g_options;
-
-struct ForcedExit: public std::runtime_error
-{
- ForcedExit(const std::string& arg):
- std::runtime_error(arg)
- {
- }
-};
-
-struct AlarmExit: public std::runtime_error
-{
- AlarmExit(const std::string& arg):
- std::runtime_error(arg)
- {
- }
-};
-
-srt::sync::atomic<bool> timer_state;
-void OnINT_ForceExit(int)
-{
- cerr << "\n-------- REQUESTED INTERRUPT!\n";
- transmit_int_state = true;
-}
-
-std::string g_interrupt_reason;
-
-void OnAlarm_Interrupt(int)
-{
- cerr << "\n---------- INTERRUPT ON TIMEOUT: hang on " << g_interrupt_reason << "!\n";
- transmit_int_state = true; // JIC
- timer_state = true;
- throw AlarmExit("Watchdog bites hangup");
-}
-
-struct BandwidthGuard
-{
- typedef std::chrono::steady_clock::time_point time_point;
- size_t conf_bw;
- time_point start_time, prev_time;
- size_t report_count = 0;
- double average_bw = 0;
- size_t transfer_size = 0;
-
- BandwidthGuard(size_t band): conf_bw(band), start_time(std::chrono::steady_clock::now()), prev_time(start_time) {}
-
- void Checkpoint(size_t size, size_t toreport )
- {
- using namespace std::chrono;
-
- time_point eop = steady_clock::now();
- auto dur = duration_cast<microseconds>(eop - start_time);
- //auto this_dur = duration_cast<microseconds>(eop - prev_time);
-
- transfer_size += size;
- average_bw = transfer_size*1000000.0/dur.count();
- //double this_bw = size*1000000.0/this_dur.count();
-
- if ( toreport )
- {
- // Show current bandwidth
- ++report_count;
- if ( report_count % toreport == toreport - 1 )
- {
- cout.precision(10);
- int abw = int(average_bw);
- int abw_trunc = abw/1024;
- int abw_frac = abw%1024;
- char bufbw[64];
- sprintf(bufbw, "%d.%03d", abw_trunc, abw_frac);
- cout << "+++/+++SRT TRANSFER: " << transfer_size << "B "
- "DURATION: " << duration_cast<milliseconds>(dur).count() << "ms SPEED: " << bufbw << "kB/s\n";
- }
- }
-
- prev_time = eop;
-
- if ( transfer_size > SIZE_MAX/2 )
- {
- transfer_size -= SIZE_MAX/2;
- start_time = eop;
- }
-
- if ( conf_bw == 0 )
- return; // don't guard anything
-
- // Calculate expected duration for the given size of bytes (in [ms])
- double expdur_ms = double(transfer_size)/conf_bw*1000;
-
- auto expdur = milliseconds(size_t(expdur_ms));
- // Now compare which is more
-
- if ( dur >= expdur ) // too slow, but there's nothing we can do. Exit now.
- return;
-
- std::this_thread::sleep_for(expdur-dur);
- }
-};
-
-bool CheckMediaSpec(const string& prefix, const vector<string>& spec, string& w_outspec)
-{
- // This function prints error messages by itself then returns false.
- // Otherwise nothing is printed and true is returned.
- // r_outspec is for a case when a redundancy specification should be translated.
-
- if (spec.empty())
- {
- cerr << prefix << ": Specification is empty\n";
- return false;
- }
-
- if (spec.size() == 1)
- {
- // Then, whatever.
- w_outspec = spec[0];
- return true;
- }
-
- // We have multiple items specified, check each one
- // it SRT, if so, craft the redundancy URI spec,
- // otherwise reject
- vector<string> adrs;
- map<string,string> uriparam;
- bool first = true;
- bool allow_raw_spec = false;
- for (auto uris: spec)
- {
- UriParser uri(uris, UriParser::EXPECT_HOST);
- if (!allow_raw_spec && uri.type() != UriParser::SRT)
- {
- cerr << ": Multiple media must be all with SRT scheme, or srt://* as first.\n";
- return false;
- }
-
- if (uri.host() == "*")
- {
- allow_raw_spec = true;
- first = false;
- uriparam = uri.parameters();
-
- // This does not specify the address, only options and URI.
- continue;
- }
-
- string aspec = uri.host() + ":" + uri.port();
- if (aspec[0] == ':' || aspec[aspec.size()-1] == ':')
- {
- cerr << "Empty host or port in the address specification: " << uris << endl;
- return false;
- }
-
- if (allow_raw_spec && !uri.parameters().empty())
- {
- bool cont = false;
- // Extract attributes if any and pass them there.
- for (UriParser::query_it i = uri.parameters().begin();
- i != uri.parameters().end(); ++i)
- {
- aspec += cont ? "&" : "?";
- cont = false;
- aspec += i->first + "=" + i->second;
- }
- }
-
- adrs.push_back(aspec);
- if (first)
- {
- uriparam = uri.parameters();
- first = false;
- }
- }
-
- w_outspec = "srt:////group?";
- if (map_getp(uriparam, "type") == nullptr)
- uriparam["type"] = "redundancy";
-
- for (auto& name_value: uriparam)
- {
- string name, value; tie(name, value) = name_value;
- w_outspec += name + "=" + value + "&";
- }
- w_outspec += "nodes=";
- for (string& a: adrs)
- w_outspec += a + ",";
-
- Verb() << "NOTE: " << prefix << " specification set as: " << (w_outspec);
-
- return true;
-}
-
-extern "C" void TestLogHandler(void* opaque, int level, const char* file, int line, const char* area, const char* message);
-
-namespace srt_logging
-{
- extern Logger glog;
-}
-
-#if ENABLE_BONDING
-extern "C" int SrtCheckGroupHook(void* , SRTSOCKET acpsock, int , const sockaddr*, const char* )
-{
- static string gtypes[] = {
- "undefined", // SRT_GTYPE_UNDEFINED
- "broadcast",
- "backup",
- "balancing",
- "multicast"
- };
-
- int type;
- int size = sizeof type;
- srt_getsockflag(acpsock, SRTO_GROUPCONNECT, &type, &size);
- Verb() << "listener: @" << acpsock << " - accepting " << (type ? "GROUP" : "SINGLE") << VerbNoEOL;
- if (type != 0)
- {
- SRT_GROUP_TYPE gt;
- size = sizeof gt;
- if (-1 != srt_getsockflag(acpsock, SRTO_GROUPTYPE, >, &size))
- {
- if (gt < Size(gtypes))
- Verb() << " type=" << gtypes[gt] << VerbNoEOL;
- else
- Verb() << " type=" << int(gt) << VerbNoEOL;
- }
- }
- Verb() << " connection";
-
- return 0;
-}
-#endif
-
-extern "C" int SrtUserPasswordHook(void* , SRTSOCKET acpsock, int hsv, const sockaddr*, const char* streamid)
-{
- if (hsv < 5)
- {
- Verb() << "SrtUserPasswordHook: HS version 4 doesn't support extended handshake";
- return -1;
- }
-
- static const map<string, string> passwd {
- {"admin", "thelocalmanager"},
- {"user", "verylongpassword"}
- };
-
- // Try the "standard interpretation" with username at key u
- string username;
-
- static const char stdhdr [] = "#!::";
- uint32_t* pattern = (uint32_t*)stdhdr;
-
- if (strlen(streamid) > 4 && *(uint32_t*)streamid == *pattern)
- {
- vector<string> items;
- Split(streamid+4, ',', back_inserter(items));
- for (auto& i: items)
- {
- vector<string> kv;
- Split(i, '=', back_inserter(kv));
- if (kv.size() == 2 && kv[0] == "u")
- {
- username = kv[1];
- }
- }
- }
- else
- {
- // By default the whole streamid is username
- username = streamid;
- }
-
- // This hook sets the password to the just accepted socket
- // depending on the user
-
- string exp_pw = passwd.at(username);
-
- srt_setsockflag(acpsock, SRTO_PASSPHRASE, exp_pw.c_str(), exp_pw.size());
-
- return 0;
-}
-
-struct RejectData
-{
- int code;
- string streaminfo;
-} g_reject_data;
-
-extern "C" int SrtRejectByCodeHook(void* op, SRTSOCKET acpsock, int , const sockaddr*, const char* )
-{
- RejectData* data = (RejectData*)op;
-
- srt_setrejectreason(acpsock, data->code);
- srt::setstreamid(acpsock, data->streaminfo);
-
- return -1;
-}
-
-int main( int argc, char** argv )
-{
- // This is mainly required on Windows to initialize the network system,
- // for a case when the instance would use UDP. SRT does it on its own, independently.
- if ( !SysInitializeNetwork() )
- throw std::runtime_error("Can't initialize network!");
-
- srt_startup();
-
- // Symmetrically, this does a cleanup; put into a local destructor to ensure that
- // it's called regardless of how this function returns.
- struct NetworkCleanup
- {
- ~NetworkCleanup()
- {
- SysCleanupNetwork();
- srt_cleanup();
- }
- } cleanupobj;
-
- vector<OptionScheme> optargs;
-
- OptionName
- o_timeout ((optargs), "<timeout[s]=0> Data transmission timeout", "t", "to", "timeout" ),
- o_chunk ((optargs), "<chunk=1316> Single reading operation buffer size", "c", "chunk"),
- o_bandwidth ((optargs), "<bw[ms]=0[unlimited]> Input reading speed limit", "b", "bandwidth", "bitrate"),
- o_report ((optargs), "<frequency[1/pkt]=0> Print bandwidth report periodically", "r", "bandwidth-report", "bitrate-report"),
- o_verbose ((optargs), "[channel=0|1|./file] Print size of every packet transferred on stdout or specified [channel]", "v", "verbose"),
- o_crash ((optargs), " Core-dump when connection got broken by whatever reason (developer mode)", "k", "crash"),
- o_loglevel ((optargs), "<severity> Minimum severity for logs (see --help logging)", "ll", "loglevel"),
- o_logfa ((optargs), "<FA=FA-list...> Enabled Functional Areas (see --help logging)", "lfa", "logfa"),
- o_logfile ((optargs), "<filepath> File to send logs to", "lf", "logfile"),
- o_stats ((optargs), "<freq[npkt]> How often stats should be reported", "s", "stats", "stats-report-frequency"),
- o_statspf ((optargs), "<format=default|csv|json> Format for printing statistics", "pf", "statspf", "statspformat"),
- o_logint ((optargs), " Use internal function for receiving logs (for testing)", "loginternal"),
- o_skipflush ((optargs), " Do not wait safely 5 seconds at the end to flush buffers", "sf", "skipflush"),
- o_stoptime ((optargs), "<time[s]=0[no timeout]> Time after which the application gets interrupted", "d", "stoptime"),
- o_hook ((optargs), "<hookspec> Use listener callback of given specification (internally coded)", "hook"),
-#if ENABLE_BONDING
- o_group ((optargs), "<URIs...> Using multiple SRT connections as redundancy group", "g"),
-#endif
- o_stime ((optargs), " Pass source time explicitly to SRT output", "st", "srctime", "sourcetime"),
- o_retry ((optargs), "<N=-1,0,+N> Retry connection N times if failed on timeout", "rc", "retry"),
- o_help ((optargs), "[special=logging] This help", "?", "help", "-help")
- ;
-
- options_t params = ProcessOptions(argv, argc, optargs);
-
- bool need_help = OptionPresent(params, o_help);
-
- vector<string> args = params[""];
-
- string source_spec, target_spec;
-#if ENABLE_BONDING
- vector<string> groupspec = Option<OutList>(params, vector<string>{}, o_group);
-#endif
- vector<string> source_items, target_items;
-
- if (!need_help)
- {
- // You may still need help.
-
-#if ENABLE_BONDING
- if ( !groupspec.empty() )
- {
- // Check if you have something before -g and after -g.
- if (args.empty())
- {
- // Then all items are sources, but the last one is a single target.
- if (groupspec.size() < 3)
- {
- cerr << "ERROR: Redundancy group: with nothing preceding -g, use -g <SRC-URI1> <SRC-URI2>... <TAR-URI> (at least 3 args)\n";
- need_help = true;
- }
- else
- {
- copy(groupspec.begin(), groupspec.end()-1, back_inserter(source_items));
- target_items.push_back(*(groupspec.end()-1));
- }
- }
- else
- {
- // Something before g, something after g. This time -g can accept also one argument.
- copy(args.begin(), args.end(), back_inserter(source_items));
- copy(groupspec.begin(), groupspec.end(), back_inserter(target_items));
- }
- }
- else
-#endif
- {
- if (args.size() < 2)
- {
- cerr << "ERROR: source and target URI must be specified.\n\n";
- need_help = true;
- }
- else
- {
- source_items.push_back(args[0]);
- target_items.push_back(args[1]);
- }
- }
- }
-
- // Check verbose option before extracting the argument so that Verb()s
- // can be displayed also when they report something about option parsing.
- string verbose_val = Option<OutString>(params, "no", o_verbose);
-
- unique_ptr<ofstream> pout_verb;
-
- int verbch = 1; // default cerr
- if (verbose_val != "no")
- {
- Verbose::on = true;
- if (verbose_val == "")
- verbch = 1;
- else if (verbose_val.substr(0, 2) == "./")
- verbch = 3;
- else try
- {
- verbch = stoi(verbose_val);
- }
- catch (...)
- {
- verbch = 1;
- }
-
- if (verbch == 1)
- {
- Verbose::cverb = &std::cout;
- }
- else if (verbch == 2)
- {
- Verbose::cverb = &std::cerr;
- }
- else if (verbch == 3)
- {
- pout_verb.reset(new ofstream(verbose_val.substr(2), ios::out | ios::trunc));
- if (!pout_verb->good())
- {
- cerr << "-v: error opening verbose output file: " << verbose_val << endl;
- return 1;
- }
- Verbose::cverb = pout_verb.get();
- }
- else
- {
- cerr << "-v or -v:1 (default) or -v:2 only allowed\n";
- return 1;
- }
- }
-
-
- if (!need_help)
- {
- // Redundancy is then simply recognized by the fact that there are
- // multiple specified inputs or outputs, for SRT caller only. Check
- // every URI in advance.
- if (!CheckMediaSpec("INPUT", source_items, (source_spec)))
- need_help = true;
- if (!CheckMediaSpec("OUTPUT", target_items, (target_spec)))
- need_help = true;
- }
-
- if (need_help)
- {
- string helpspec = Option<OutString>(params, o_help);
- if (helpspec == "logging")
- {
- cerr << "Logging options:\n";
- cerr << " -ll <LEVEL> - specify minimum log level\n";
- cerr << " -lfa <area...> - specify functional areas\n";
- cerr << "Where:\n\n";
- cerr << " <LEVEL>: fatal error note warning debug\n\n";
- cerr << "This turns on logs that are at the given log name and all on the left.\n";
- cerr << "(Names from syslog, like alert, crit, emerg, err, info, panic, are also\n";
- cerr << "recognized, but they are aligned to those that lie close in hierarchy.)\n\n";
- cerr << " <area...> is a space-sep list of areas to turn on or ~areas to turn off.\n\n";
- cerr << "The list may include 'all' to turn all on or off, beside those selected.\n";
- cerr << "Example: `-lfa ~all cc` - turns off all FA, except cc\n";
- cerr << "Default: all are on except haicrypt. NOTE: 'general' can't be off.\n\n";
- cerr << "List of functional areas:\n";
-
- map<int, string> revmap;
- for (auto entry: SrtLogFAList())
- revmap[entry.second] = entry.first;
-
- int en10 = 0;
- for (auto entry: revmap)
- {
- cerr << " " << entry.second;
- if (entry.first/10 != en10)
- {
- cerr << endl;
- en10 = entry.first/10;
- }
- }
- cerr << endl;
-
- return 1;
- }
-
- // Unrecognized helpspec is same as no helpspec, that is, general help.
- cerr << "Usage:\n";
- cerr << " (1) " << argv[0] << " [options] <input> <output>\n";
- cerr << " (2) " << argv[0] << " <inputs...> -g <outputs...> [options]\n";
- cerr << "*** (Position of [options] is unrestricted.)\n";
- cerr << "*** (<variadic...> option parameters can be only terminated by a next option.)\n";
- cerr << "where:\n";
- cerr << " (1) Exactly one input and one output URI spec is required,\n";
- cerr << " (2) Multiple SRT inputs or output as redundant links are allowed.\n";
- cerr << " `URI1 URI2 -g URI3` uses 1, 2 input and 3 output\n";
- cerr << " `-g URI1 URI2 URI3` like above\n";
- cerr << " `URI1 -g URI2 URI3` uses 1 input and 2, 3 output\n";
- cerr << "SUPPORTED URI SCHEMES:\n";
- cerr << " srt: use SRT connection\n";
- cerr << " udp: read from bound UDP socket or send to given address as UDP\n";
- cerr << " file (default if scheme not specified) specified as:\n";
- cerr << " - empty host/port and absolute file path in the URI\n";
- cerr << " - only a filename, also as a relative path\n";
- cerr << " - file://con ('con' as host): designates stdin or stdout\n";
- cerr << "OPTIONS HELP SYNTAX: -option <parameter[unit]=default[meaning]>:\n";
- for (auto os: optargs)
- cout << OptionHelpItem(*os.pid) << endl;
- return 1;
- }
-
- int timeout = Option<OutNumber>(params, "30", o_timeout);
- size_t chunk = Option<OutNumber>(params, "0", o_chunk);
- if ( chunk == 0 )
- {
- chunk = SRT_LIVE_DEF_PLSIZE;
- }
- else
- {
- transmit_chunk_size = chunk;
- }
-
- transmit_use_sourcetime = OptionPresent(params, o_stime);
- size_t bandwidth = Option<OutNumber>(params, "0", o_bandwidth);
- transmit_bw_report = Option<OutNumber>(params, "0", o_report);
- bool crashonx = OptionPresent(params, o_crash);
-
- string loglevel = Option<OutString>(params, "error", o_loglevel);
- vector<string> logfa = Option<OutList>(params, o_logfa);
- string logfile = Option<OutString>(params, "", o_logfile);
- transmit_stats_report = Option<OutNumber>(params, "0", o_stats);
-
- bool internal_log = OptionPresent(params, o_logint);
- bool skip_flushing = OptionPresent(params, o_skipflush);
-
- string hook = Option<OutString>(params, "", o_hook);
- if (hook != "")
- {
- vector<string> hargs;
- Split(hook, ':', back_inserter(hargs));
-
- if (hargs[0] == "user-password")
- {
- transmit_accept_hook_fn = &SrtUserPasswordHook;
- transmit_accept_hook_op = nullptr;
- }
- else if (hargs[0] == "reject")
- {
- hargs.resize(3); // make sure 3 elements exist, may be empty
- g_reject_data.code = stoi(hargs[1]);
- g_reject_data.streaminfo = hargs[2];
- transmit_accept_hook_op = (void*)&g_reject_data;
- transmit_accept_hook_fn = &SrtRejectByCodeHook;
- }
-#if ENABLE_BONDING
- else if (hargs[0] == "groupcheck")
- {
- transmit_accept_hook_fn = &SrtCheckGroupHook;
- transmit_accept_hook_op = nullptr;
- }
-#endif
- }
-
- string pfextra;
- SrtStatsPrintFormat statspf = ParsePrintFormat(Option<OutString>(params, "default", o_statspf), (pfextra));
- if (statspf == SRTSTATS_PROFMAT_INVALID)
- {
- cerr << "Invalid stats print format\n";
- return 1;
- }
- transmit_stats_writer = SrtStatsWriterFactory(statspf);
- if (pfextra != "")
- {
- vector<string> options;
- Split(pfextra, ',', back_inserter(options));
- for (auto& i: options)
- {
- vector<string> klv;
- Split(i, '=', back_inserter(klv));
- klv.resize(2);
- transmit_stats_writer->Option(klv[0], klv[1]);
- }
- }
-
- // Options that require integer conversion
- size_t stoptime = Option<OutNumber>(params, "0", o_stoptime);
- std::ofstream logfile_stream; // leave unused if not set
-
- srt_setloglevel(SrtParseLogLevel(loglevel));
- string logfa_on, logfa_off;
- ParseLogFASpec(logfa, (logfa_on), (logfa_off));
-
- set<srt_logging::LogFA> fasoff = SrtParseLogFA(logfa_off);
- set<srt_logging::LogFA> fason = SrtParseLogFA(logfa_on);
-
- auto fa_del = [fasoff]() {
- for (set<srt_logging::LogFA>::iterator i = fasoff.begin(); i != fasoff.end(); ++i)
- srt_dellogfa(*i);
- };
-
- auto fa_add = [fason]() {
- for (set<srt_logging::LogFA>::iterator i = fason.begin(); i != fason.end(); ++i)
- srt_addlogfa(*i);
- };
-
- if (logfa_off == "all")
- {
- // If the spec is:
- // -lfa ~all control app
- // then we first delete all, then enable given ones
- fa_del();
- fa_add();
- }
- else
- {
- // Otherwise we first add all those that have to be added,
- // then delete those unwanted. This embraces both
- // -lfa control app ~cc
- // and
- // -lfa all ~cc
- fa_add();
- fa_del();
- }
-
-
- srt::addlogfa(SRT_LOGFA_APP);
-
- char NAME[] = "SRTLIB";
- if ( internal_log )
- {
- srt_setlogflags( 0
- | SRT_LOGF_DISABLE_TIME
- | SRT_LOGF_DISABLE_SEVERITY
- | SRT_LOGF_DISABLE_THREADNAME
- | SRT_LOGF_DISABLE_EOL
- );
- srt_setloghandler(NAME, TestLogHandler);
- }
- else if ( logfile != "" )
- {
- logfile_stream.open(logfile.c_str());
- if ( !logfile_stream )
- {
- cerr << "ERROR: Can't open '" << logfile << "' for writing - fallback to cerr\n";
- }
- else
- {
- srt::setlogstream(logfile_stream);
- }
- }
-
- string retryphrase = Option<OutString>(params, "", o_retry);
- if (retryphrase != "")
- {
- if (retryphrase[retryphrase.size()-1] == 'a')
- {
- transmit_retry_always = true;
- retryphrase = retryphrase.substr(0, retryphrase.size()-1);
- }
-
- transmit_retry_connect = stoi(retryphrase);
- }
-
-#ifdef _WIN32
-#define alarm(argument) (void)0
-
- if (stoptime != 0)
- {
- cerr << "ERROR: The -stoptime option (-d) is not implemented on Windows\n";
- return 1;
- }
-
-#else
- signal(SIGALRM, OnAlarm_Interrupt);
-#endif
- signal(SIGINT, OnINT_ForceExit);
- signal(SIGTERM, OnINT_ForceExit);
-
- time_t start_time { time(0) };
- time_t end_time { -1 };
-
- if (stoptime != 0)
- {
- if (stoptime < 10)
- {
- cerr << "ERROR: -stoptime (-d) must be at least 10 seconds\n";
- return 1;
- }
- alarm(stoptime);
- cerr << "STOPTIME: will interrupt after " << stoptime << "s\n";
- if (timeout != 30)
- {
- cerr << "WARNING: -timeout (-t) option ignored due to specified -stoptime (-d)\n";
- }
- }
-
- // XXX This could be also controlled by an option.
- int final_delay = 5;
-
- // In the beginning, set Alarm
-
- unique_ptr<Source> src;
- unique_ptr<Target> tar;
-
- try
- {
- src = Source::Create(source_spec);
- tar = Target::Create(target_spec);
- }
- catch(std::exception& x)
- {
- if (::transmit_int_state)
- {
- // The application was terminated by SIGINT or SIGTERM.
- // Don't print anything, just exit gently like ffmpeg.
- cerr << "Exit on request.\n";
- return 255;
- }
-
- if (stoptime != 0 && ::timer_state)
- {
- cerr << "Exit on timeout.\n";
- return 0;
- }
-
- Verb() << "MEDIA CREATION FAILED: " << x.what() << " - exiting.";
-
- // Don't speak anything when no -v option.
- // (the "requested interrupt" will be printed anyway)
- return 2;
- }
- catch (...)
- {
- cerr << "ERROR: UNKNOWN EXCEPTION\n";
- return 2;
- }
-
- alarm(0);
- end_time = time(0);
-
- if (!src || !tar)
- {
- const string tarstate = tar ? "CREATED" : "FAILED";
- const string srcstate = src ? "CREATED" : "FAILED";
-
- cerr << "ERROR: not both media created; source:" << srcstate << " target:" << tarstate << endl;
- return 2;
- }
-
- // Now loop until broken
- BandwidthGuard bw(bandwidth);
-
- if (transmit_use_sourcetime && src->uri.type() != UriParser::SRT)
- {
- Verb() << "WARNING: -st option is effective only if the target type is SRT";
- }
-
- Verb() << "STARTING TRANSMISSION: '" << source_spec << "' --> '" << target_spec << "'";
-
- // After the time has been spent in the creation
- // (including waiting for connection)
- // rest of the time should be spent for transmission.
- if (stoptime != 0)
- {
- int elapsed = end_time - start_time;
- int remain = stoptime - elapsed;
-
- if (remain <= final_delay)
- {
- cerr << "NOTE: remained too little time for cleanup: " << remain << "s - exiting\n";
- return 0;
- }
-
- cerr << "NOTE: stoptime: remaining " << remain << " seconds (setting alarm to " << (remain - final_delay) << "s)\n";
- alarm(remain - final_delay);
- }
-
- try
- {
- for (;;)
- {
- if (stoptime == 0 && timeout != -1 )
- {
- Verb() << "[." << VerbNoEOL;
- alarm(timeout);
- }
- else
- {
- alarm(0);
- }
- Verb() << " << ... " << VerbNoEOL;
- g_interrupt_reason = "reading";
- const MediaPacket& data = src->Read(chunk);
- Verb() << " << " << data.payload.size() << " -> " << VerbNoEOL;
- if ( data.payload.empty() && src->End() )
- {
- Verb() << "EOS";
- break;
- }
- g_interrupt_reason = "writing";
- tar->Write(data);
- if (stoptime == 0 && timeout != -1 )
- {
- Verb() << ".] " << VerbNoEOL;
- alarm(0);
- }
-
- if ( tar->Broken() )
- {
- Verb() << " OUTPUT broken";
- break;
- }
-
- Verb() << "sent";
-
- if (::transmit_int_state)
- {
- Verror() << "\n (interrupted on request)";
- break;
- }
-
- bw.Checkpoint(chunk, transmit_bw_report);
-
- if (stoptime != 0)
- {
- int elapsed = time(0) - end_time;
- int remain = stoptime - final_delay - elapsed;
- if (remain < 0)
- {
- Verror() << "\n (interrupted on timeout: elapsed " << elapsed << "s) - waiting " << final_delay << "s for cleanup";
- this_thread::sleep_for(chrono::seconds(final_delay));
- break;
- }
- }
- }
-
- } catch (Source::ReadEOF&) {
- alarm(0);
-
- if (!skip_flushing)
- {
- Verror() << "(DEBUG) EOF when reading file. Looping until the sending bufer depletes.\n";
- for (;;)
- {
- size_t still = tar->Still();
- if (still == 0)
- {
- Verror() << "(DEBUG) DEPLETED. Done.\n";
- break;
- }
-
- Verror() << "(DEBUG)... still " << still << " bytes (sleep 1s)\n";
- this_thread::sleep_for(chrono::seconds(1));
- }
- }
- } catch (std::exception& x) { // Catches TransmissionError and AlarmExit
-
- if (stoptime != 0 && ::timer_state)
- {
- Verror() << "Exit on timeout.";
- }
- else if (::transmit_int_state)
- {
- Verror() << "Exit on interrupt.";
- // Do nothing.
- }
- else
- {
- Verror() << "STD EXCEPTION: " << x.what();
- }
-
- if ( crashonx )
- throw;
-
- if (final_delay > 0)
- {
- Verror() << "Waiting " << final_delay << "s for possible cleanup...";
- this_thread::sleep_for(chrono::seconds(final_delay));
- }
- if (stoptime != 0 && ::timer_state)
- return 0;
-
- return 255;
-
- } catch (...) {
-
- Verror() << "UNKNOWN type of EXCEPTION";
- if ( crashonx )
- throw;
-
- return 1;
- }
-
- return 0;
-}
-
-// Class utilities
-
-
-void TestLogHandler(void* opaque, int level, const char* file, int line, const char* area, const char* message)
-{
- char prefix[100] = "";
- if ( opaque )
- strncpy(prefix, (char*)opaque, 99);
- time_t now;
- time(&now);
- char buf[1024];
- struct tm local = SysLocalTime(now);
- size_t pos = strftime(buf, 1024, "[%c ", &local);
-
-#ifdef _MSC_VER
- // That's something weird that happens on Microsoft Visual Studio 2013
- // Trying to keep portability, while every version of MSVS is a different plaform.
- // On MSVS 2015 there's already a standard-compliant snprintf, whereas _snprintf
- // is available on backward compatibility and it doesn't work exactly the same way.
-#define snprintf _snprintf
-#endif
- snprintf(buf+pos, 1024-pos, "%s:%d(%s)]{%d} %s", file, line, area, level, message);
-
- cerr << buf << endl;
-}
+++ /dev/null
-
-
-SOURCES
-srt-test-live.cpp
-testmedia.cpp
-../apps/apputil.cpp
-../apps/statswriter.cpp
-../apps/verbose.cpp
-../apps/socketoptions.cpp
-../apps/uriparser.cpp
-../apps/logsupport.cpp
-../apps/logsupport_appdefs.cpp
-
+++ /dev/null
-/*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2018 Haivision Systems Inc.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- */
-
-#include <memory>
-#include <thread>
-#include <list>
-#include <utility>
-#include <chrono>
-#include <csignal>
-#include <iterator>
-#include <stdexcept>
-
-#define REQUIRE_CXX11 1
-
-#include "apputil.hpp" // CreateAddr
-#include "uriparser.hpp" // UriParser
-#include "socketoptions.hpp"
-#include "logsupport.hpp"
-#include "testmediabase.hpp"
-#include "testmedia.hpp"
-#include "netinet_any.h"
-#include "threadname.h"
-#include "verbose.hpp"
-
-#include <srt.h>
-#include <logging.h>
-
-// Make the windows-nonexistent alarm an empty call
-#ifdef _WIN32
-#define alarm(argument) (void)0
-#define signal_alarm(fn) (void)0
-#else
-#define signal_alarm(fn) signal(SIGALRM, fn)
-#endif
-
-srt_logging::Logger applog(SRT_LOGFA_APP, srt_logger_config, "srt-mpbond");
-
-volatile bool mpbond_int_state = false;
-void OnINT_SetIntState(int)
-{
- cerr << "\n-------- REQUESTED INTERRUPT!\n";
- mpbond_int_state = true;
-}
-
-using namespace srt;
-
-
-int main( int argc, char** argv )
-{
- // This is mainly required on Windows to initialize the network system,
- // for a case when the instance would use UDP. SRT does it on its own, independently.
- if ( !SysInitializeNetwork() )
- throw std::runtime_error("Can't initialize network!");
-
- // Symmetrically, this does a cleanup; put into a local destructor to ensure that
- // it's called regardless of how this function returns.
- struct NetworkCleanup
- {
- ~NetworkCleanup()
- {
- SysCleanupNetwork();
- }
- } cleanupobj;
-
- signal(SIGINT, OnINT_SetIntState);
- signal(SIGTERM, OnINT_SetIntState);
-
- vector<OptionScheme> optargs;
-
- OptionName
- o_input ((optargs), "<input-medium> Define input to send over SRT endpoint", "i", "input"),
- o_output ((optargs), "<output-medium> Define output to send data read from SRT endpoint", "o", "output"),
- o_verbose ((optargs), "[channel=0|1] Print size of every packet transferred on stdout or specified [channel]", "v", "verbose"),
- o_loglevel ((optargs), "<severity=fatal|error|note|warning|debug> Minimum severity for logs", "ll", "loglevel"),
- o_logfa ((optargs), "<FA=all> Enabled Functional Areas", "lfa", "logfa"),
- o_help ((optargs), " This help", "?", "help", "-help")
- ;
-
- options_t params = ProcessOptions(argv, argc, optargs);
-
- bool need_help = OptionPresent(params, o_help);
-
- vector<string> args = params[""];
-
- string srtspec;
-
- if (args.empty())
- need_help = true;
- else
- {
- for (size_t i = 0; i < args.size(); ++i)
- {
- UriParser u(args[i], UriParser::EXPECT_HOST);
- if (u.portno() == 0)
- {
- cerr << "ERROR: " << args[i] << " expected host:port or :port syntax.\n";
- return 1;
- }
- }
- }
-
- if (need_help)
- {
- cerr << "Usage:\n";
- cerr << " " << argv[0] << " <SRT listeners...> [-i INPUT] [-o OUTPUT]\n";
- cerr << "*** (Position of [options] is unrestricted.)\n";
- cerr << "*** (<variadic...> option parameters can be only terminated by a next option.)\n";
- cerr << "where:\n";
- cerr << " - <SRT listeners...>: a list of host:port specs for SRT listener\n";
- cerr << " - INPUT or OUTPUT: at least one of that kind must be specified\n";
- cerr << "SUPPORTED URI SCHEMES:\n";
- cerr << " srt: use SRT connection\n";
- cerr << " udp: read from bound UDP socket or send to given address as UDP\n";
- cerr << " file (default if scheme not specified) specified as:\n";
- cerr << " - empty host/port and absolute file path in the URI\n";
- cerr << " - only a filename, also as a relative path\n";
- cerr << " - file://con ('con' as host): designates stdin or stdout\n";
- cerr << "OPTIONS HELP SYNTAX: -option <parameter[unit]=default[meaning]>:\n";
- for (auto os: optargs)
- cout << OptionHelpItem(*os.pid) << endl;
- return 1;
- }
-
- bool skip_flushing = false; // non-configurable for now
-
- bool mode_output = OptionPresent(params, o_output);
-
- string loglevel = Option<OutString>(params, "error", "ll", "loglevel");
- srt_logging::LogLevel::type lev = SrtParseLogLevel(loglevel);
- srt::setloglevel(lev);
- srt::addlogfa(SRT_LOGFA_APP);
-
- // Check verbose option before extracting the argument so that Verb()s
- // can be displayed also when they report something about option parsing.
- string verbose_val = Option<OutString>(params, "no", o_verbose);
-
- int verbch = 1; // default cerr
- if (verbose_val != "no")
- {
- Verbose::on = true;
- try
- {
- verbch = stoi(verbose_val);
- }
- catch (...)
- {
- verbch = 1;
- }
- if (verbch != 1)
- {
- if (verbch != 2)
- {
- cerr << "-v or -v:1 (default) or -v:2 only allowed\n";
- return 1;
- }
- Verbose::cverb = &std::cerr;
- }
- else
- {
- Verbose::cverb = &std::cout;
- }
- }
-
-
- if (OptionPresent(params, o_input) == OptionPresent(params, o_output))
- {
- cerr << "One of -i and -o options must be specified (not both)\n";
- return 1;
- }
-
-
- // Create listeners according to the parameters
- vector<SRTSOCKET> listeners;
-
- Verb() << "LISTENERS [ " << VerbNoEOL;
-
- for (size_t i = 0; i < args.size(); ++i)
- {
- UriParser u(args[i], UriParser::EXPECT_HOST);
- sockaddr_any sa = CreateAddr(u.host(), u.portno());
-
- SRTSOCKET s = srt_create_socket();
-
- //SRT_GROUPCONNTYPE gcon = SRTGC_GROUPONLY;
- int gcon = 1;
- srt_setsockflag(s, SRTO_GROUPCONNECT, &gcon, sizeof gcon);
-
- srt_bind(s, sa.get(), sizeof sa);
- srt_listen(s, 5);
-
- listeners.push_back(s);
- Verb() << u.host() << ":" << u.portno() << " " << VerbNoEOL;
- }
-
- Verb() << "] accept...";
-
- SRTSOCKET conngrp = srt_accept_bond(listeners.data(), listeners.size(), -1);
- if (conngrp == SRT_INVALID_SOCK)
- {
- cerr << "ERROR: srt_accept_bond: " << srt_getlasterror_str() << endl;
- return 1;
- }
-
- auto s = new SrtSource;
- unique_ptr<Source> src;
- unique_ptr<Target> tar;
-
- try
- {
- // Now create input or output
- if (mode_output)
- {
- string outspec = Option<OutString>(params, o_output);
- Verb() << "SRT -> " << outspec;
- tar = Target::Create(outspec);
-
- s->Acquire(conngrp);
- src.reset(s);
- }
- else
- {
- string inspec = Option<OutString>(params, o_input);
- Verb() << "SRT <- " << inspec;
- src = Source::Create(inspec);
-
- auto s = new SrtTarget;
- s->Acquire(conngrp);
- tar.reset(s);
- }
- }
- catch (...)
- {
- return 2;
- }
-
- size_t chunk = SRT_LIVE_MAX_PLSIZE;
-
- // Now run the loop
- try
- {
- for (;;)
- {
- Verb() << " << ... " << VerbNoEOL;
- const MediaPacket& data = src->Read(chunk);
- Verb() << " << " << data.payload.size() << " -> " << VerbNoEOL;
- if ( data.payload.empty() && src->End() )
- {
- Verb() << "EOS";
- break;
- }
- tar->Write(data);
-
- if ( tar->Broken() )
- {
- Verb() << " OUTPUT broken";
- break;
- }
-
- Verb() << "sent";
-
- if ( mpbond_int_state )
- {
- Verror() << "\n (interrupted on request)";
- break;
- }
- }
- } catch (Source::ReadEOF&) {
- alarm(0);
-
- if (!skip_flushing)
- {
- Verror() << "(DEBUG) EOF when reading file. Looping until the sending bufer depletes.\n";
- for (;;)
- {
- size_t still = tar->Still();
- if (still == 0)
- {
- Verror() << "(DEBUG) DEPLETED. Done.\n";
- break;
- }
-
- Verror() << "(DEBUG)... still " << still << " bytes (sleep 1s)\n";
- this_thread::sleep_for(chrono::seconds(1));
- }
- }
- } catch (std::exception& x) { // Catches TransmissionError and AlarmExit
- if (::mpbond_int_state)
- {
- Verror() << "Exit on interrupt.";
- // Do nothing.
- }
- else
- {
- Verror() << "STD EXCEPTION: " << x.what();
- }
-
- return 255;
- } catch (...) {
- Verror() << "UNKNOWN type of EXCEPTION";
- return 1;
- }
-
- return 0;
-}
-
-
-
-
+++ /dev/null
-
-SOURCES
-srt-test-mpbond.cpp
-testmedia.cpp
-../apps/apputil.cpp
-../apps/verbose.cpp
-../apps/socketoptions.cpp
-../apps/uriparser.cpp
-../apps/logsupport.cpp
-../apps/logsupport_appdefs.cpp
-
+++ /dev/null
-/*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2018 Haivision Systems Inc.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- */
-
-#include <memory>
-#include <thread>
-#include <list>
-#include <utility>
-#include <chrono>
-#include <csignal>
-#include <iterator>
-#include <stdexcept>
-
-#define REQUIRE_CXX11 1
-
-#include "apputil.hpp" // CreateAddr
-#include "uriparser.hpp" // UriParser
-#include "socketoptions.hpp"
-#include "logsupport.hpp"
-#include "testmediabase.hpp"
-#include "testmedia.hpp"
-#include "netinet_any.h"
-#include "threadname.h"
-#include "verbose.hpp"
-
-#include <srt.h>
-#include <logging.h>
-
-// Make the windows-nonexistent alarm an empty call
-#ifdef _WIN32
-#define alarm(argument) (void)0
-#define signal_alarm(fn) (void)0
-#else
-#define signal_alarm(fn) signal(SIGALRM, fn)
-#endif
-
-
-using namespace std;
-
-// The length of the SRT payload used in srt_recvmsg call.
-// So far, this function must be used and up to this length of payload.
-const size_t DEFAULT_CHUNK = 1316;
-
-srt_logging::Logger applog(SRT_LOGFA_APP, srt_logger_config, "srt-mplex");
-
-volatile bool siplex_int_state = false;
-void OnINT_SetIntState(int)
-{
- cerr << "\n-------- REQUESTED INTERRUPT!\n";
- siplex_int_state = true;
-}
-
-volatile bool alarm_state = false;
-void OnALRM_SetAlarmState(int)
-{
- alarm_state = true;
-}
-
-map<string,string> defined_streams;
-string file_pattern = "output%.dat";
-
-struct MediumPair
-{
- unique_ptr<Source> src;
- unique_ptr<Target> tar;
- thread runner;
- size_t chunk = DEFAULT_CHUNK;
- volatile bool interrupted = false;
- volatile bool has_quit = false;
- bytevector initial_portion;
- string name;
-
- MediumPair(unique_ptr<Source> s, unique_ptr<Target> t): src(move(s)), tar(move(t)) {}
-
- void Stop()
- {
- interrupted = true;
- runner.join();
- src.reset();
- tar.reset();
- }
-
- void TransmissionLoop()
- {
- struct MarkQuit
- {
- volatile bool& q;
-
- ~MarkQuit()
- {
- q = true;
- applog.Note() << "MediumPair: Giving it 5 seconds delay before exiting";
- this_thread::sleep_for(chrono::seconds(5));
- }
- } mq { has_quit };
-
- applog.Note() << "STARTING TRANSMiSSION: " << name;
-
- if (!initial_portion.empty())
- {
- tar->Write(initial_portion);
- if (tar->Broken())
- {
- applog.Note() << "OUTPUT BROKEN for loop: " << name;
- return;
- }
- initial_portion.clear();
- }
-
- try
- {
- for (;;)
- {
- ostringstream sout;
- alarm(1);
- auto data = src->Read(chunk);
-
- alarm(0);
- if (alarm_state)
- {
- alarm_state = false;
- // This means that it's just a checkpoint.
- if ( interrupted )
- break;
- continue;
- }
- sout << " << " << data.payload.size() << " -> ";
- if ( data.payload.empty() && src->End() )
- {
- sout << "EOS";
- applog.Note() << sout.str();
- break;
- }
- tar->Write(data);
- if (tar->Broken())
- {
- sout << " OUTPUT broken";
- applog.Note() << sout.str();
- break;
- }
- sout << " sent";
- if ( siplex_int_state )
- {
- sout << " --- (interrupted on request)";
- applog.Note() << sout.str();
- break;
- }
- applog.Note() << sout.str();
- }
- }
- catch (const Source::ReadEOF&)
- {
- applog.Note() << "EOS - closing media for loop: " << name;
- src->Close();
- tar->Close();
- applog.Note() << "CLOSED: " << name;
- }
- catch (const std::runtime_error& x)
- {
- applog.Note() << "INTERRUPTED: " << x.what();
- src->Close();
- tar->Close();
- applog.Note() << "CLOSED: " << name;
- }
- catch (...)
- {
- applog.Note() << "UNEXPECTED EXCEPTION, rethrowing";
- throw;
- }
- }
-};
-
-
-class MediaBase
-{
-public:
- list<MediumPair> media;
-
- /// Take the Source and Target and bind them for a transmission.
- /// This spawns a thread for transmission.
- /// @param src source medium
- /// @param tar target medium
- /// @param initial_portion First portion of data read from @c src for any extra checks, which
- /// are still meant to be delivered to @c tar
- MediumPair& Link(std::unique_ptr<Source> src, std::unique_ptr<Target> tar, bytevector&& initial_portion, string name, string thread_name)
- {
- media.emplace_back(move(src), move(tar));
- MediumPair& med = media.back();
- med.initial_portion = move(initial_portion);
- med.name = name;
-
- // Ok, got this, so we can start transmission.
- srt::ThreadName tn(thread_name);
-
- med.runner = thread( [&med]() { med.TransmissionLoop(); });
- return med;
- }
-
- void StopAll()
- {
- for (auto& x: media)
- x.Stop();
- }
-
- ~MediaBase()
- {
- StopAll();
- }
-
-} g_media_base;
-
-string ResolveFilePattern(int number)
-{
- vector<string> parts;
- Split(::file_pattern, '%', back_inserter(parts));
- ostringstream os;
- os << parts[0];
- for (auto i = parts.begin()+1; i < parts.end(); ++i)
- os << number << *i;
-
- return os.str();
-}
-
-string SelectMedium(string id, bool mode_output)
-{
- static int number = 0;
-
- // Empty ID is incorrect.
- if ( id == "" )
- {
- applog.Error() << "SelectMedium: empty id";
- return "";
- }
-
- string uri = map_get(defined_streams, id);
-
- // Test the URI if it is openable.
- UriParser u(uri);
- if ( u.scheme() == "file" && u.path() == "" )
- {
- if (mode_output)
- {
- ++number;
- string sol = ResolveFilePattern(number);
- applog.Warn() << "SelectMedium: for [" << id << "] uri '" << uri << "' is file with no path - autogenerating filename: " << sol;
- return sol;
- }
- applog.Error() << "SelectMedium: id not found: [" << id << "]";
- return "";
- }
-
- applog.Note() << "SelectMedium: for [" << id << "] found medium: " << uri;
- return uri;
-}
-
-bool PrepareStreamNames(const map<string,vector<string>>& params, bool mode_output)
-{
- vector<string> v;
- string flag;
-
- if (mode_output)
- {
- // You have an incoming stream over SRT and you need to
- // redirect it to the correct locally defined output stream.
-
- if (params.count("o") && !params.at("o").empty())
- {
- // We have a defined list of parameters.
- // Check if there's just one item and it's a file pattern
-
- // Each stream needs to be defined separately, at least to have IDs
- // If this is a file without path, use the default file pattern.
-
- v = params.at("o");
- flag = "o";
- }
- }
- else
- {
- // You have some input media and you want to send them all
- // over SRT medium.
- if (params.count("i"))
- {
- v = params.at("i");
- flag = "i";
- }
- }
-
- if ( v.empty() )
- return false;
-
- for (string& s: v)
- {
- UriParser u(s);
- string id = u["id"];
- if ( id != "" )
- {
- defined_streams[id] = s;
- }
- else
- {
- cerr << "Parameter at -" << flag << " without id: " << s << endl;
- return false;
- }
- }
-
- return true;
-}
-
-bool SelectAndLink(SrtModel& m, string id, bool mode_output)
-{
- // So, we have made a connection that is now contained in m.
- // For that connection we need to select appropriate stream
- // to send.
- //
- // XXX
- // Currently only one method implemented: select appropriate number from the list.
-
- // If SRT mode is caller, then SelectMedium will always return
- // a nonempty string that is a key in defined_streams map.
- // This is because in this case the id comes directly from
- // that map's keys.
- string medium = SelectMedium(id, mode_output);
- if ( medium == "" )
- {
- // No medium available for that stream, ignore it.
- m.Close();
- return false;
- }
-
- // Now create a medium and store.
- unique_ptr<Source> source;
- unique_ptr<Target> target;
- string name;
- ostringstream os;
- SRTSOCKET sock = m.Socket();
-
- string thread_name;
-
- if ( mode_output )
- {
- // Create Source out of SrtModel and Target from the given medium
- auto s = new SrtSource();
- s->StealFrom(m);
- source.reset(s);
-
- target = Target::Create(medium);
-
- os << m.m_host << ":" << m.m_port << "[" << id << "]%" << sock << " -> " << medium;
- thread_name = "TL>" + medium;
- }
- else
- {
- // Create Source of given medium and Target of SrtModel.
- source = Source::Create(medium);
- auto t = new SrtTarget();
- t->StealFrom(m);
- target.reset(t);
-
- os << medium << " -> " << m.m_host << ":" << m.m_port << "[" << id << "]%" << sock;
- thread_name = "TL<" + medium;
- }
-
- bytevector dummy_initial_portion;
- g_media_base.Link(move(source), move(target), move(dummy_initial_portion), os.str(), thread_name);
-
- return true;
-}
-
-void Stall()
-{
- // Call this function if everything is running in their own
- // threads and there's nothing more to run. Check periodically
- // if all threads are still alive, quit if all are dead.
-
- while (!siplex_int_state)
- {
- this_thread::sleep_for(chrono::seconds(1));
-
- // Check all cars if any crashed
- for (auto i = g_media_base.media.begin(), i_next = i; i != g_media_base.media.end(); i = i_next)
- {
- ++i_next;
- if (i->has_quit)
- {
- Verb() << "Found QUIT mediumpair: " << i->name << " - removing from base";
- i->Stop();
- g_media_base.media.erase(i);
- }
- }
-
- if (g_media_base.media.empty())
- {
- Verb() << "All media have quit. Marking exit.";
- break;
- }
- }
-}
-
-
-void Usage(string program)
-{
- cerr << "Usage: " << program << " <SRT URI> [-i INPUT...] [-o OUTPUT...]\n";
-}
-
-void Help(string program)
-{
- Usage(program);
- cerr << endl;
- cerr <<
-"SIPLEX is a program that demonstrates two SRT features:\n"
-" - using one UDP outgoing port for multiple connecting SRT sockets\n"
-" - setting a resource ID on a socket visible on the listener side\n"
-"\n"
-"The <SRT URI> will be input or output depending on the further -i/-o option.\n"
-"The URIs specified as -i INPUT... will be used for input and therefore SRT for output,\n"
-"and in the other way around if you use -o OUTPUT...\n"
-"For every such URI you must specify additionally a parameter named 'id', which will be\n"
-"interperted by the application and used to set resource id on an SRT socket when connecting\n"
-"or to match with the id extracted from the accepted socket of incoming connection.\n"
-"Example:\n"
-"\tSender: srt-multiplex srt://remhost:2000 -i udp://:5000?id=low udp://:6000?id=high\n"
-"\tReceiver: srt-multiplex srt://:2000 -o output-high.ts?id=high output-low.ts?id=low\n"
-"\nHere you create a Sender which will connect to 'remhost' port 2000 using multiple SRT\n"
-"sockets, all of which will be using the same outgoing port. Here the port is autoselected\n"
-"by the first socket when connecting, every next one will reuse that port. Alternatively you\n"
-"can enforce the outgoing port using 'port' parameter in the SRT URI.\n\n"
-"Then for every input resource a separate connection is made and appropriate resource id\n"
-"will be set to particular socket assigned to that resource according to the 'id' parameter.\n"
-"When the listener side (here Receiver) gets the socket accepted, it will have the resource\n"
-"id set just as the caller side did, in which case srt-multiplex will search for this id among\n"
-"the registered resources and match the resource (output here) with this id. If the resource is\n"
-"not found, the connection is closed immediately. This works the same way regardless of which\n"
-"direction is used by caller or listener\n";
-
-}
-
-int main( int argc, char** argv )
-{
- // This is mainly required on Windows to initialize the network system,
- // for a case when the instance would use UDP. SRT does it on its own, independently.
- if ( !SysInitializeNetwork() )
- throw std::runtime_error("Can't initialize network!");
-
- // Initialize signals
-
- signal_alarm(OnALRM_SetAlarmState);
- signal(SIGINT, OnINT_SetIntState);
- signal(SIGTERM, OnINT_SetIntState);
-
- // Symmetrically, this does a cleanup; put into a local destructor to ensure that
- // it's called regardless of how this function returns.
- struct NetworkCleanup
- {
- ~NetworkCleanup()
- {
- SysCleanupNetwork();
- }
- } cleanupobj;
-
- const OptionName
- o_loglevel = { "ll", "loglevel" },
- o_input = { "i" },
- o_output = { "o" };
-
- vector<OptionScheme> optargs = {
- { o_loglevel, OptionScheme::ARG_ONE },
- { o_input, OptionScheme::ARG_VAR },
- { o_output, OptionScheme::ARG_VAR }
- };
-
- map<string, vector<string>> params = ProcessOptions(argv, argc, optargs);
-
- // The call syntax is:
- //
- // srt-multiplex <SRT URI> -o/-i ARGS...
- //
- // SRT URI should contain:
- // srt://[host]:port?mode=MODE&adapter=ADAPTER&port=PORT&otherparameters...
- //
- // Extra parameters:
- //
- // mode: caller/listener/rendezvous. Default: if host empty, listener, otherwise caller.
- // adapter: IP to select network device for listner or rendezvous. Default: for listener taken from host, otherwise 0.0.0.0
- // port: default=0. Used only for caller mode, sets the outgoing port number. If 0, system-selected (default behavior)
- //
- // Syntax cases for -i:
- //
- // Every item from ARGS... is an input URI. For every such case a new socket should be
- // created and the data should be transmitted through that socket.
- //
- // Syntax cases for -o:
- //
- // EMPTY ARGS...: use 'output%.dat' file patter for every stream.
- // PATTERN (one argument that contains % somewhere): define the output file pattern
- // URI...: try to match the input stream to particular URI by 'name' parameter. If none matches, ignore.
-
- if ( params.count("-help") )
- {
- Help(argv[0]);
- return 1;
- }
-
- if ( params[""].empty() )
- {
- Usage(argv[0]);
- return 1;
- }
-
- if (params[""].size() > 1)
- {
- cerr << "Extra parameter after the first one: " << Printable(params[""]) << endl;
- return 1;
- }
-
- // Force exist
- (void)params["o"];
- (void)params["i"];
-
- if (!params["o"].empty() && !params["i"].empty())
- {
- cerr << "Input-output mixed mode not supported. Specify either -i or -o.\n";
- return 1;
- }
-
- bool mode_output = false;
-
- if (params["i"].empty())
- {
- mode_output = true;
- }
-
- if ( !PrepareStreamNames(params, mode_output))
- {
- cerr << "Incorrect input/output specification\n";
- return 1;
- }
-
- if ( defined_streams.empty() )
- {
- cerr << "No streams defined\n";
- return 1;
- }
-
- string loglevel = Option<OutString>(params, "error", "ll", "loglevel");
- srt_logging::LogLevel::type lev = SrtParseLogLevel(loglevel);
- srt::setloglevel(lev);
- srt::addlogfa(SRT_LOGFA_APP);
-
- string verbo = Option<OutString>(params, "no", "v", "verbose");
- if ( verbo == "" || !false_names.count(verbo) )
- Verbose::on = true;
-
-
- string srt_uri = params[""][0];
-
- UriParser up(srt_uri);
-
- if ( up.scheme() != "srt" )
- {
- cerr << "First parameter must be a SRT-scheme URI\n";
- return 1;
- }
-
- int iport = atoi(up.port().c_str());
- if ( iport < 1024 )
- {
- cerr << "Port value invalid: " << iport << " - must be >=1024\n";
- return 1;
- }
-
- SrtModel m(up.host(), iport, up.parameters());
-
- srt::ThreadName::set("main");
-
- // Note: for input, there must be an exactly defined
- // number of sources. The loop rolls up to all these sources.
- //
- // For output, if you use defined output URI, roll the loop until
- // they are all managed.
- // If you use file pattern, then:
- // - if SRT is in listener mode, just listen infinitely
- // - if SRT is in caller mode, the limit number of the streams must be used. Default is 10.
-
- set<string> ids;
- for (auto& mp: defined_streams)
- ids.insert(mp.first);
-
- try
- {
- for(;;)
- {
- string id = *ids.begin();
- m.Establish((id));
-
- // The 'id' could have been altered.
- // If Establish did connect(), then it gave this stream id,
- // in which case it will return unchanged. If it did accept(),
- // then it will be overwritten with the received stream id.
- // Whatever the result was, we need to bind the transmitter with
- // the local resource of this id, and if this failed, simply
- // close the stream and ignore it.
-
- // Select medium from parameters.
- if (SelectAndLink(m, id, mode_output))
- {
- ids.erase(id);
- if (ids.empty())
- break;
- }
-
- srt::ThreadName::set("main");
- }
-
- applog.Note() << "All local stream definitions covered. Waiting for interrupt/broken all connections.";
- Stall();
- }
- catch (std::exception& x)
- {
- cerr << "CATCH!\n" << x.what() << endl;;
- }
-}
-
-
-
-
-
+++ /dev/null
-
-
-SOURCES
-srt-test-multiplex.cpp
-testmedia.cpp
-../apps/apputil.cpp
-../apps/verbose.cpp
-../apps/socketoptions.cpp
-../apps/uriparser.cpp
-../apps/logsupport.cpp
-../apps/logsupport_appdefs.cpp
-
+++ /dev/null
-/*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2018 Haivision Systems Inc.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- */
-
-/*****************************************************************************
-written by
- Haivision Systems Inc.
- *****************************************************************************/
-
-#include "platform_sys.h"
-
-#include <atomic>
-#include <iostream>
-#include <iterator>
-#include <vector>
-#include <list>
-#include <map>
-#include <stdexcept>
-#include <string>
-#include <thread>
-#include <chrono>
-#include <deque>
-#include <mutex>
-#include <condition_variable>
-#include <csignal>
-#include <sys/stat.h>
-#include <srt.h>
-#include <udt.h>
-
-#include "testactivemedia.hpp"
-
-#include "apputil.hpp"
-#include "uriparser.hpp"
-#include "logsupport.hpp"
-#include "logging.h"
-#include "socketoptions.hpp"
-#include "verbose.hpp"
-#include "testmedia.hpp"
-#include "threadname.h"
-
-
-
-
-bool Upload(UriParser& srt, UriParser& file);
-bool Download(UriParser& srt, UriParser& file);
-
-srt_logging::Logger applog(SRT_LOGFA_APP, srt_logger_config, "srt-relay");
-
-std::atomic<bool> g_program_established {false};
-
-SrtModel* g_pending_model = nullptr;
-
-thread::id g_root_thread = std::this_thread::get_id();
-
-static void OnINT_SetInterrupted(int)
-{
- Verb() << VerbLock << "SIGINT: Setting interrupt state.";
- ::transmit_int_state = true;
-
- // Just for a case, forcefully close all active SRT sockets.
- SrtModel* pm = ::g_pending_model;
- if (pm)
- {
- // The program is hanged on accepting a new SRT connection.
- // We need to check which thread we've fallen into.
- if (this_thread::get_id() == g_root_thread)
- {
- // Throw an exception, it will be caught in a predicted place.
- throw std::runtime_error("Interrupted on request");
- }
- else
- {
- // This is some other thread, so close the listener socket.
- // This will cause the accept block to be interrupted.
- for (SRTSOCKET i: { pm->Socket(), pm->Listener() })
- if (i != SRT_INVALID_SOCK)
- srt_close(i);
- }
- }
-
-}
-
-using namespace std;
-
-size_t g_chunksize = 0;
-size_t g_default_live_chunksize = 1316;
-size_t g_default_file_chunksize = 1456;
-
-class SrtMainLoop
-{
- UriParser m_srtspec;
-
- // Media used
- unique_ptr<SrtRelay> m_srt_relay;
- SourceMedium m_srt_source;
- SourceMedium m_input_medium;
- list<unique_ptr<TargetMedium>> m_output_media;
- thread m_input_thr;
- std::exception_ptr m_input_xp;
-
- void InputRunner();
- srt::sync::atomic<bool> m_input_running;
-
-public:
- SrtMainLoop(const string& srt_uri, bool input_echoback, const string& input_spec, const vector<string>& output_spec);
-
- void run();
-
- void MakeStop() { m_input_running = false; }
- bool IsRunning() { return m_input_running; }
-
- ~SrtMainLoop()
- {
- if (m_input_thr.joinable())
- m_input_thr.join();
- }
-};
-
-int main( int argc, char** argv )
-{
- OptionName
- o_loglevel = { "ll", "loglevel" },
- o_logfa = { "lf", "logfa" },
- o_verbose = {"v", "verbose" },
- o_input = {"i", "input"},
- o_output = {"o", "output"},
- o_echo = {"e", "io", "input-echoback"},
- o_chunksize = {"c", "chunk"}
- ;
-
- // Options that expect no arguments (ARG_NONE) need not be mentioned.
- vector<OptionScheme> optargs = {
- { o_loglevel, OptionScheme::ARG_ONE },
- { o_logfa, OptionScheme::ARG_ONE },
- { o_input, OptionScheme::ARG_ONE },
- { o_output, OptionScheme::ARG_VAR },
- { o_chunksize, OptionScheme::ARG_ONE }
- };
- options_t params = ProcessOptions(argv, argc, optargs);
-
- /*
- cerr << "OPTIONS (DEBUG)\n";
- for (auto o: params)
- {
- cerr << "[" << o.first << "] ";
- copy(o.second.begin(), o.second.end(), ostream_iterator<string>(cerr, " "));
- cerr << endl;
- }
- */
-
- vector<string> args = params[""];
- if ( args.size() != 1 )
- {
- cerr << "Usage: " << argv[0] << " <srt-endpoint> [ -i <input> | -e ] [ -o <output> ]\n";
- cerr << "Options:\n";
- cerr << "\t-v . . . . . . . . . . Verbose mode\n";
- cerr << "\t-ll <level=error> . . . . . Log level for SRT\n";
- cerr << "\t-lf <logfa=all> . . . . . Log Functional Areas enabled\n";
- cerr << "\t-c <size=1316[live]|1456[file]> Single reading buffer size\n";
- cerr << "\t-i <URI> . . . . . . . . Input medium spec\n";
- cerr << "\t-o <URI> . . . . . . . . Output medium spec\n";
- cerr << "\t-e . . . (conflicts with -i) Feed SRT output back to SRT input\n";
- cerr << "\nNote: specify `transtype=file` for using TCP-like stream mode\n";
- return 1;
- }
-
- string loglevel = Option<OutString>(params, "error", o_loglevel);
- string logfa = Option<OutString>(params, "", o_logfa);
- srt_logging::LogLevel::type lev = SrtParseLogLevel(loglevel);
- UDT::setloglevel(lev);
- if (logfa == "")
- {
- UDT::addlogfa(SRT_LOGFA_APP);
- }
- else
- {
- // Add only selected FAs
- set<string> unknown_fas;
- set<srt_logging::LogFA> fas = SrtParseLogFA(logfa, &unknown_fas);
- UDT::resetlogfa(fas);
-
- // The general parser doesn't recognize the "app" FA, we check it here.
- if (unknown_fas.count("app"))
- UDT::addlogfa(SRT_LOGFA_APP);
- }
-
- string verbo = Option<OutString>(params, "no", o_verbose);
- if ( verbo == "" || !false_names.count(verbo) )
- {
- Verbose::on = true;
- int verboch = atoi(verbo.c_str());
- if (verboch <= 0)
- {
- verboch = 1;
- }
- else if (verboch > 2)
- {
- cerr << "ERROR: -v option accepts value 1 (stdout, default) or 2 (stderr)\n";
- return 1;
- }
-
- if (verboch == 1)
- {
- Verbose::cverb = &std::cout;
- }
- else
- {
- Verbose::cverb = &std::cerr;
- }
- }
-
- string chunk = Option<OutString>(params, "", o_chunksize);
- if (chunk != "")
- {
- ::g_chunksize = stoi(chunk);
- }
-
- string srt_endpoint = args[0];
-
- UriParser usrt(srt_endpoint);
-
- if (usrt.scheme() != "srt")
- {
- cerr << "ERROR: the only one freestanding parameter should be an SRT uri.\n";
- cerr << "Usage: " << argv[0] << " <srt-endpoint> [ -i <input> ] [ -o <output> ] [ -e ]\n";
- return 1;
- }
-
- // Allowed are only one input and multiple outputs.
- // Input-echoback is treated as a single input.
- bool input_echoback = Option<OutString>(params, "no", o_echo) != "no";
- string input_spec = Option<OutString>(params, "", o_input);
-
- if (input_spec != "" && input_echoback)
- {
- cerr << "ERROR: input-echoback is treated as input specifcation, -i can't be specified together.\n";
- return 1;
- }
-
- vector<string> output_spec = Option<OutList>(params, vector<string>{}, o_output);
-
- if (!input_echoback)
- {
- if (input_spec == "" || output_spec.empty())
- {
- cerr << "ERROR: at least one input and one output must be specified (-io specifies both)\n";
- return 1;
- }
- }
-
- Verb() << "SETTINGS:";
- Verb() << "SRT connection: " << srt_endpoint;
- if (input_echoback)
- {
- Verb() << "INPUT: (from SRT connection)";
- }
- else
- {
- Verb() << "INPUT: " << input_spec;
- }
-
- Verb() << "OUTPUT LIST:";
- if (input_echoback)
- {
- Verb() << "\t(back to SRT connection)";
- }
- for (auto& s: output_spec)
- Verb() << "\t" << s;
-
-#ifdef _MSC_VER
- // Replacement for sigaction, just use 'signal'
- // This may make this working kinda impaired and unexpected,
- // but still better that not compiling at all.
- signal(SIGINT, OnINT_SetInterrupted);
-#else
- struct sigaction sigIntHandler;
-
- sigIntHandler.sa_handler = OnINT_SetInterrupted;
- sigemptyset(&sigIntHandler.sa_mask);
- sigIntHandler.sa_flags = 0;
-
- sigaction(SIGINT, &sigIntHandler, NULL);
-#endif
-
- try
- {
- SrtMainLoop loop(srt_endpoint, input_echoback, input_spec, output_spec);
- loop.run();
- }
- catch (std::exception& x)
- {
- cerr << "ERROR: " << x.what() << endl;
- return 1;
- }
-
-
- return 0;
-}
-
-SrtMainLoop::SrtMainLoop(const string& srt_uri, bool input_echoback, const string& input_spec, const vector<string>& output_spec)
-{
- // Now prepare all media
- // They use pointers instead of real variables
- // so that the creation time can be delayed
- // up to this moment, and the parameters prepared
- // before passing to the constructors.
-
- // Start with output media so that they are ready when
- // the data come in.
-
- for (string spec: output_spec)
- {
- Verb() << "Setting up output: " << spec;
- unique_ptr<TargetMedium> m { new TargetMedium };
- m->Setup(Target::Create(spec));
- m_output_media.push_back(move(m));
- }
-
-
- // Start with SRT.
-
- UriParser srtspec(srt_uri);
- string transtype = srtspec["transtype"].deflt("live");
-
- SrtModel m(srtspec.host(), srtspec.portno(), srtspec.parameters());
-
- // Just to keep it unchanged.
- string id = m_srtspec["streamid"];
-
- Verb() << "Establishing SRT connection: " << srt_uri;
-
- ::g_pending_model = &m;
- m.Establish((id));
-
- ::g_program_established = true;
- ::g_pending_model = nullptr;
-
- Verb() << "... Established. configuring other pipes:";
-
- // Once it's ready, use it to initialize the medium.
- bool file_mode = (transtype == "file");
- if (g_chunksize == 0)
- {
- if (file_mode)
- g_chunksize = g_default_file_chunksize;
- else
- g_chunksize = g_default_live_chunksize;
-
- Verb() << "DEFAULT CHUNKSIZE used: " << g_chunksize;
- }
-
- m_srt_relay.reset(new SrtRelay);
- m_srt_relay->StealFrom(m);
-
- m_srt_source.Setup(m_srt_relay.get(), g_chunksize);
-
- // Now check the input medium
- if (input_echoback)
- {
- Verb() << "SRT set up as input source and the first output target";
-
- // Add SRT medium to output targets, and keep input medium empty.
- unique_ptr<TargetMedium> m { new TargetMedium };
- m->Setup(m_srt_relay.get());
- m_output_media.push_back(move(m));
- }
- else
- {
- // Initialize input medium and do not add SRT medium
- // to the output list, as this will be fed directly
- // by the data from this input medium in a spearate engine.
- Verb() << "Setting up input: " << input_spec;
- m_input_medium.Setup(Source::Create(input_spec), g_chunksize);
-
- if (!file_mode)
- {
- // Also set writing to SRT non-blocking always.
- bool no = false;
- srt_setsockflag(m_srt_relay->Socket(), SRTO_SNDSYN, &no, sizeof no);
- }
- }
-
- // We're done here.
- Verb() << "MEDIA SUCCESSFULLY CREATED.";
-}
-
-void SrtMainLoop::InputRunner()
-{
- srt::ThreadName::set("InputRN");
- // An extra thread with a loop that reads from the external input
- // and writes into the SRT medium. When echoback mode is used,
- // this thread isn't started at all and instead the SRT reading
- // serves as both SRT reading and input reading.
-
- auto on_return_set = OnReturnSet(m_input_running, false);
-
- Verb() << VerbLock << "RUNNING INPUT LOOP";
- for (;;)
- {
- applog.Debug() << "SrtMainLoop::InputRunner: extracting...";
- auto data = m_input_medium.Extract();
-
- if (data.payload.empty())
- {
- Verb() << "INPUT READING INTERRUPTED.";
- break;
- }
-
- //Verb() << "INPUT [" << data.size() << "] " << VerbNoEOL;
- applog.Debug() << "SrtMainLoop::InputRunner: [" << data.payload.size() << "] CLIENT -> SRT-RELAY";
- m_srt_relay->Write(data);
- }
-}
-
-void SrtMainLoop::run()
-{
- // Start the media runners.
-
- Verb() << VerbLock << "STARTING OUTPUT threads:";
-
- for (auto& o: m_output_media)
- o->run();
-
- Verb() << VerbLock << "STARTING SRT INPUT LOOP";
- m_srt_source.run();
-
- Verb() << VerbLock << "STARTING INPUT ";
- if (m_input_medium.med)
- {
- m_input_medium.run();
- m_input_running = true;
-
- std::ostringstream tns;
- tns << "Input:" << this;
- srt::ThreadName tn(tns.str());
- m_input_thr = thread([this] {
- try {
- InputRunner();
- } catch (...) {
- m_input_xp = std::current_exception();
- }
-
- Verb() << "INPUT: thread exit";
- });
- }
-
- Verb() << VerbLock << "RUNNING SRT MEDIA LOOP";
- for (;;)
- {
- applog.Debug() << "SrtMainLoop::run: SRT-RELAY: extracting...";
- auto data = m_srt_source.Extract();
-
- if (data.payload.empty())
- {
- Verb() << "SRT READING INTERRUPTED.";
- break;
- }
-
- vector<string> output_report;
- bool any = false;
- int no = 1;
-
- for (auto i = m_output_media.begin(), i_next = i; i != m_output_media.end(); i = i_next)
- {
- ++i_next;
- auto& o = *i;
- applog.Debug() << "SrtMainLoop::run: [" << data.payload.size() << "] SRT-RELAY: resending to output #" << no << "...";
- if (!o->Schedule(data))
- {
- if (Verbose::on)
- {
- ostringstream os;
- os << " --XXX-> <" << no << ">";
- output_report.push_back(os.str());
- }
- m_output_media.erase(i);
- continue;
- }
-
- if (Verbose::on)
- {
- ostringstream os;
- os << " --> <" << no << ">";
- output_report.push_back(os.str());
- }
- any = true;
- ++no;
- }
- applog.Debug() << "SrtMainLoop::run: [" << data.payload.size() << "] SRT-RELAY -> OUTPUTS: " << Printable(output_report);
-
- if (Verbose::on)
- {
- string outputs;
- for (auto& r: output_report)
- outputs += " " + r;
- if (!any)
- outputs = " --> * (no output)";
-
- Verb() << VerbLock << "SRT [" << data.payload.size() << "] " << outputs;
- }
- }
-
- Verb() << "MEDIA LOOP EXIT";
- for (auto& m : m_output_media)
- {
- m->quit();
- }
- m_input_medium.quit();
- m_srt_source.quit();
-
- if (m_input_xp)
- {
- try {
- std::rethrow_exception(m_input_xp);
- } catch (std::exception& x) {
- cerr << "INPUT EXIT BY EXCEPTION: " << x.what() << endl;
- } catch (...) {
- cerr << "INPUT EXIT BY UNKNOWN EXCEPTION\n";
- }
- }
-}
+++ /dev/null
-
-
-SOURCES
-srt-test-relay.cpp
-testmedia.cpp
-testactivemedia.cpp
-../apps/apputil.cpp
-../apps/verbose.cpp
-../apps/socketoptions.cpp
-../apps/uriparser.cpp
-../apps/logsupport.cpp
-../apps/logsupport_appdefs.cpp
-
+++ /dev/null
-
-#include "testactivemedia.hpp"
-
-void SourceMedium::Runner()
-{
- srt::ThreadName::set("SourceRN");
-
- Verb() << VerbLock << "Starting SourceMedium: " << this;
- for (;;)
- {
- auto input = med->Read(chunksize_);
- if (input.payload.empty() && med->End())
- {
- Verb() << VerbLock << "Exiting SourceMedium: " << this;
- return;
- }
- LOGP(applog.Debug, "SourceMedium(", typeid(*med).name(), "): [", input.payload.size(), "] MEDIUM -> BUFFER. signal(", &ready, ")");
-
- lock_guard<std::mutex> g(buffer_lock);
- buffer.push_back(input);
- ready.notify_one();
- }
-}
-
-MediaPacket SourceMedium::Extract()
-{
- unique_lock<std::mutex> g(buffer_lock);
- for (;;)
- {
- if (::transmit_int_state)
- running = false;
-
- if (!buffer.empty())
- {
- MediaPacket top;
- swap(top, *buffer.begin());
- buffer.pop_front();
- LOGP(applog.Debug, "SourceMedium(", typeid(*med).name(), "): [", top.payload.size(), "] BUFFER -> CLIENT");
- return top;
- }
- else
- {
- // Don't worry about the media status as long as you have somthing in the buffer.
- // Purge the buffer first, then worry about the other things.
- if (!running)
- {
- //LOGP(applog.Debug, "Extract(", typeid(*med).name(), "): INTERRUPTED READING");
- //Verb() << "SourceMedium " << this << " not running";
- return {};
- }
-
- }
-
- // Block until ready
- //LOGP(applog.Debug, "Extract(", typeid(*med).name(), "): ", this, " wait(", &ready, ") -->");
-
- ready.wait_for(g, chrono::seconds(1), [this] { return running && !buffer.empty(); });
-
- // LOGP(applog.Debug, "Extract(", typeid(*med).name(), "): ", this, " <-- notified (running:"
- // << boolalpha << running << " buffer:" << buffer.size() << ")");
- }
-}
-
-void TargetMedium::Runner()
-{
- srt::ThreadName::set("TargetRN");
- auto on_return_set = OnReturnSet(running, false);
- Verb() << VerbLock << "Starting TargetMedium: " << this;
- for (;;)
- {
- MediaPacket val;
- {
- unique_lock<std::mutex> lg(buffer_lock);
- if (buffer.empty())
- {
- if (!running)
- {
- //LOGP(applog.Debug, "TargetMedium(", typeid(*med).name(), "): buffer empty, medium stopped, exiting.");
- return;
- }
-
- bool gotsomething = ready.wait_for(lg, chrono::seconds(1), [this] { return !running || !buffer.empty(); } );
- LOGP(applog.Debug, "TargetMedium(", typeid(*med).name(), "): [", val.payload.size(), "] BUFFER update (timeout:",
- boolalpha, gotsomething, " running: ", running, ")");
- if (::transmit_int_state || !running || !med || med->Broken())
- {
- LOGP(applog.Debug, "TargetMedium(", typeid(*med).name(), "): buffer empty, medium ",
- (!::transmit_int_state ?
- (running ?
- (med ?
- (med->Broken() ? "broken" : "UNKNOWN")
- : "deleted")
- : "stopped")
- : "killed"));
- return;
- }
- if (!gotsomething) // exit on timeout
- continue;
- }
- swap(val, *buffer.begin());
- LOGP(applog.Debug, "TargetMedium(", typeid(*med).name(), "): [", val.payload.size(), "] BUFFER extraction");
-
- buffer.pop_front();
- }
-
- // Check before writing
- if (med->Broken())
- {
- LOGP(applog.Debug, "TargetMedium(", typeid(*med).name(), "): [", val.payload.size(), "] BUFFER -> DISCARDED (medium broken)");
- running = false;
- return;
- }
-
- LOGP(applog.Debug, "TargetMedium(", typeid(*med).name(), "): [", val.payload.size(), "] BUFFER -> MEDIUM");
- // You get the data to send, send them.
- med->Write(val);
- }
-}
-
-
+++ /dev/null
-#include <string>
-#include <sstream>
-#include <vector>
-#include <list>
-#include <memory>
-#include <exception>
-#include <thread>
-#include <mutex>
-#include <atomic>
-#include <condition_variable>
-
-#include "testmedia.hpp"
-#include "logsupport.hpp"
-
-#define SRT_ENABLE_VERBOSE_LOCK 1
-#include "verbose.hpp"
-
-#include "logging.h"
-#include "threadname.h"
-
-extern srt_logging::Logger applog;
-
-template<class MediumDir>
-struct Medium
-{
- MediumDir* med = nullptr;
- std::unique_ptr<MediumDir> pinned_med;
- std::list<MediaPacket> buffer;
- std::mutex buffer_lock;
- std::thread thr;
- std::condition_variable ready;
- std::atomic<bool> running = {false};
- std::exception_ptr xp; // To catch exception thrown by a thread
-
- virtual void Runner() = 0;
-
- void RunnerBase()
- {
- try
- {
- running = true;
- Runner();
- }
- catch (...)
- {
- xp = std::current_exception();
- }
-
- //Verb() << "Medium: " << this << ": thread exit";
- std::unique_lock<std::mutex> g(buffer_lock);
- running = false;
- ready.notify_all();
- //Verb() << VerbLock << "Medium: EXIT NOTIFIED";
- }
-
- void run()
- {
- running = true;
- std::ostringstream tns;
- tns << typeid(*this).name() << ":" << this;
- srt::ThreadName tn(tns.str());
- thr = thread( [this] { RunnerBase(); } );
- }
-
- void quit()
- {
- if (!med)
- return;
-
- LOGP(applog.Debug, "Medium(", typeid(*med).name(), ") quit. Buffer contains ", buffer.size(), " blocks");
-
- std::string name;
- if (Verbose::on)
- name = typeid(*med).name();
-
- med->Close();
- if (thr.joinable())
- {
- LOGP(applog.Debug, "Medium::quit: Joining medium thread (", name, ") ...");
- thr.join();
- LOGP(applog.Debug, "... done");
- }
-
- if (xp)
- {
- try {
- std::rethrow_exception(xp);
- } catch (TransmissionError& e) {
- if (Verbose::on)
- Verb() << VerbLock << "Medium " << this << " exited with Transmission Error:\n\t" << e.what();
- else
- cerr << "Transmission Error: " << e.what() << endl;
- } catch (...) {
- if (Verbose::on)
- Verb() << VerbLock << "Medium " << this << " exited with UNKNOWN EXCEPTION:";
- else
- cerr << "UNKNOWN EXCEPTION on medium\n";
- }
- }
-
- // Prevent further quits from running
- med = nullptr;
- }
-
- void Setup(MediumDir* t)
- {
- med = t;
- // Leave pinned_med as 0
- }
-
- void Setup(std::unique_ptr<MediumDir>&& medbase)
- {
- pinned_med = std::move(medbase);
- med = pinned_med.get();
- }
-
- virtual ~Medium()
- {
- //Verb() << "Medium: " << this << " DESTROYED. Threads quit.";
- quit();
- }
-
- virtual void Start() { run(); }
- virtual void Stop() { quit(); }
-};
-
-struct SourceMedium: Medium<Source>
-{
- size_t chunksize_ = 0;
- typedef Medium<Source> Base;
-
- // Source Runner: read payloads and put on the buffer
- void Runner() override;
-
- // External user: call this to get the buffer.
- MediaPacket Extract();
-
- template<class Arg>
- void Setup(Arg&& medium, size_t chunksize)
- {
- chunksize_ = chunksize;
- return Base::Setup(std::move(medium));
- }
-};
-
-struct TargetMedium: Medium<Target>
-{
- void Runner() override;
-
- bool Schedule(const MediaPacket& data)
- {
- LOGP(applog.Debug, "TargetMedium::Schedule LOCK ... ");
- lock_guard<std::mutex> lg(buffer_lock);
- LOGP(applog.Debug, "TargetMedium::Schedule LOCKED - checking: running=", running, " interrupt=", ::transmit_int_state);
- if (!running || ::transmit_int_state)
- {
- LOGP(applog.Debug, "TargetMedium::Schedule: not running, discarding packet");
- return false;
- }
-
- LOGP(applog.Debug, "TargetMedium(", typeid(*med).name(), "): Schedule: [", data.payload.size(), "] CLIENT -> BUFFER");
- buffer.push_back(data);
- ready.notify_one();
- return true;
- }
-
- void Clear()
- {
- lock_guard<std::mutex> lg(buffer_lock);
- buffer.clear();
- }
-
- void Interrupt()
- {
- lock_guard<std::mutex> lg(buffer_lock);
- running = false;
- ready.notify_one();
- }
-
- ~TargetMedium()
- {
- //Verb() << "TargetMedium: DESTROYING";
- Interrupt();
- // ~Medium will do quit() additionally, which joins the thread
- }
-};
-
-
+++ /dev/null
-/*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2018 Haivision Systems Inc.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- */
-
-// Medium concretizations
-
-// Just for formality. This file should be used
-#include <iostream>
-#include <fstream>
-#include <sstream>
-#include <string>
-#include <stdexcept>
-#include <iterator>
-#include <map>
-#include <chrono>
-#include <thread>
-#include <atomic>
-#include <srt.h>
-#if !defined(_WIN32)
-#include <sys/ioctl.h>
-#endif
-
-// SRT protected includes
-#include "netinet_any.h"
-#include "common.h"
-#include "api.h"
-#include "udt.h"
-#include "logging.h"
-#include "utilities.h"
-
-#include "apputil.hpp"
-#include "socketoptions.hpp"
-#include "uriparser.hpp"
-#include "testmedia.hpp"
-#include "srt_compat.h"
-#include "verbose.hpp"
-
-using namespace std;
-using namespace srt;
-
-using srt_logging::KmStateStr;
-using srt_logging::SockStatusStr;
-#if ENABLE_BONDING
-using srt_logging::MemberStatusStr;
-#endif
-
-std::atomic<bool> transmit_throw_on_interrupt {false};
-std::atomic<bool> transmit_int_state {false};
-int transmit_bw_report = 0;
-unsigned transmit_stats_report = 0;
-size_t transmit_chunk_size = SRT_LIVE_DEF_PLSIZE;
-bool transmit_printformat_json = false;
-srt_listen_callback_fn* transmit_accept_hook_fn = nullptr;
-void* transmit_accept_hook_op = nullptr;
-bool transmit_use_sourcetime = false;
-int transmit_retry_connect = 0;
-bool transmit_retry_always = false;
-
-// Do not unblock. Copy this to an app that uses applog and set appropriate name.
-//srt_logging::Logger applog(SRT_LOGFA_APP, srt_logger_config, "srt-test");
-
-std::shared_ptr<SrtStatsWriter> transmit_stats_writer;
-
-string DirectionName(SRT_EPOLL_T direction)
-{
- string dir_name;
- if (direction & ~SRT_EPOLL_ERR)
- {
- if (direction & SRT_EPOLL_IN)
- {
- dir_name = "source";
- }
-
- if (direction & SRT_EPOLL_OUT)
- {
- if (!dir_name.empty())
- dir_name = "relay";
- else
- dir_name = "target";
- }
-
- if (direction & SRT_EPOLL_ERR)
- {
- dir_name += "+error";
- }
- }
- else
- {
- // stupid name for a case of IPE
- dir_name = "stone";
- }
-
- return dir_name;
-}
-
-template<class FileBase> inline
-bytevector FileRead(FileBase& ifile, size_t chunk, const string& filename)
-{
- bytevector data(chunk);
- ifile.read(data.data(), chunk);
- size_t nread = ifile.gcount();
- if (nread < data.size())
- data.resize(nread);
-
- if (data.empty())
- throw Source::ReadEOF(filename);
- return data;
-}
-
-
-class FileSource: public virtual Source
-{
- ifstream ifile;
- string filename_copy;
-public:
-
- FileSource(const string& path): ifile(path, ios::in | ios::binary), filename_copy(path)
- {
- if (!ifile)
- throw std::runtime_error(path + ": Can't open file for reading");
- }
-
- MediaPacket Read(size_t chunk) override { return FileRead(ifile, chunk, filename_copy); }
-
- bool IsOpen() override { return bool(ifile); }
- bool End() override { return ifile.eof(); }
- //~FileSource() { ifile.close(); }
-};
-
-class FileTarget: public virtual Target
-{
- ofstream ofile;
-public:
-
- FileTarget(const string& path): ofile(path, ios::out | ios::trunc | ios::binary) {}
-
- void Write(const MediaPacket& data) override
- {
- ofile.write(data.payload.data(), data.payload.size());
-#ifdef PLEASE_LOG
- applog.Debug() << "FileTarget::Write: " << data.size() << " written to a file";
-#endif
- }
-
- bool IsOpen() override { return !!ofile; }
- bool Broken() override { return !ofile.good(); }
- //~FileTarget() { ofile.close(); }
- void Close() override
- {
-#ifdef PLEASE_LOG
- applog.Debug() << "FileTarget::Close";
-#endif
- ofile.close();
- }
-};
-
-// Can't base this class on FileSource and FileTarget classes because they use two
-// separate fields, which makes it unable to reliably define IsOpen(). This would
-// require to use 'fstream' type field in some kind of FileCommon first. Not worth
-// a shot.
-class FileRelay: public Relay
-{
- fstream iofile;
- string filename_copy;
-public:
-
- FileRelay(const string& path):
- iofile(path, ios::in | ios::out | ios::binary), filename_copy(path)
- {
- if (!iofile)
- throw std::runtime_error(path + ": Can't open file for reading");
- }
- MediaPacket Read(size_t chunk) override { return FileRead(iofile, chunk, filename_copy); }
-
- void Write(const MediaPacket& data) override
- {
- iofile.write(data.payload.data(), data.payload.size());
- }
-
- bool IsOpen() override { return !!iofile; }
- bool End() override { return iofile.eof(); }
- bool Broken() override { return !iofile.good(); }
- void Close() override { iofile.close(); }
-};
-
-template <class Iface> struct File;
-template <> struct File<Source> { typedef FileSource type; };
-template <> struct File<Target> { typedef FileTarget type; };
-template <> struct File<Relay> { typedef FileRelay type; };
-
-template <class Iface>
-Iface* CreateFile(const string& name) { return new typename File<Iface>::type (name); }
-
-void SrtCommon::InitParameters(string host, string path, map<string,string> par)
-{
- // Application-specific options: mode, blocking, timeout, adapter
- if ( Verbose::on && !par.empty())
- {
- Verb() << "SRT parameters specified:\n";
- for (map<string,string>::iterator i = par.begin(); i != par.end(); ++i)
- {
- Verb() << "\t" << i->first << " = '" << i->second << "'\n";
- }
- }
-
- if (path != "")
- {
- // Special case handling of an unusual specification.
-
- if (path.substr(0, 2) != "//")
- {
- Error("Path specification not supported for SRT (use // in front for special cases)");
- }
-
- path = path.substr(2);
-
-#if ENABLE_BONDING
- if (path == "group")
- {
- // Group specified, check type.
- m_group_type = par["type"];
- if (m_group_type == "")
- {
- Error("With //group, the group 'type' must be specified.");
- }
-
- vector<string> parts;
- Split(m_group_type, '/', back_inserter(parts));
- if (parts.size() == 0 || parts.size() > 2)
- {
- Error("Invalid specification for 'type' parameter");
- }
-
- if (parts.size() == 2)
- {
- m_group_type = parts[0];
- m_group_config = parts[1];
- }
-
- vector<string> nodes;
- Split(par["nodes"], ',', back_inserter(nodes));
-
- if (nodes.empty())
- {
- Error("With //group, 'nodes' must specify comma-separated host:port specs.");
- }
-
- int token = 1;
-
- // Check if correctly specified
- for (string& hostport: nodes)
- {
- if (hostport == "")
- continue;
-
- // The attribute string, as it was embedded in another URI,
- // must have had replaced the & character with another ?, so
- // now all ? character, except the first one, must be now
- // restored so that UriParser interprets them correctly.
-
- size_t atq = hostport.find('?');
- if (atq != string::npos)
- {
- while (atq+1 < hostport.size())
- {
- size_t next = hostport.find('?', atq+1);
- if (next == string::npos)
- break;
- hostport[next] = '&';
- atq = next;
- }
- }
-
- UriParser check(hostport, UriParser::EXPECT_HOST);
- if (check.host() == "" || check.port() == "")
- {
- Error("With //group, 'nodes' must specify comma-separated host:port specs.");
- }
-
- if (check.portno() <= 1024)
- {
- Error("With //group, every node in 'nodes' must have port >1024");
- }
-
- Connection cc(check.host(), check.portno());
- if (check.parameters().count("weight"))
- {
- cc.weight = stoi(check.queryValue("weight"));
- }
-
- if (check.parameters().count("source"))
- {
- UriParser sourcehp(check.queryValue("source"), UriParser::EXPECT_HOST);
- cc.source = CreateAddr(sourcehp.host(), sourcehp.portno());
- }
-
- // Check if there's a key with 'srto.' prefix.
-
- UriParser::query_it start = check.parameters().lower_bound("srto.");
-
- SRT_SOCKOPT_CONFIG* config = nullptr;
- bool all_clear = true;
- vector<string> fails;
- map<string, string> options;
-
- if (start != check.parameters().end())
- {
- for (; start != check.parameters().end(); ++start)
- {
- auto& y = *start;
- if (y.first.substr(0, 5) != "srto.")
- break;
-
- options[y.first.substr(5)] = y.second;
- }
- }
-
- if (!options.empty())
- {
- config = srt_create_config();
-
- for (auto o: srt_options)
- {
- if (!options.count(o.name))
- continue;
- string value = options.at(o.name);
- bool ok = o.apply<SocketOption::SRT>(config, value);
- if ( !ok )
- {
- fails.push_back(o.name);
- all_clear = false;
- }
- }
-
- if (!all_clear)
- {
- srt_delete_config(config);
- Error("With //group, failed to set options: " + Printable(fails));
- }
-
- cc.options = config;
- }
-
- cc.token = token++;
- m_group_nodes.push_back(move(cc));
- }
-
- par.erase("type");
- par.erase("nodes");
-
- // For a group-connect specification, it's
- // always the caller mode.
- // XXX change it here if maybe rendezvous is also
- // possible in future.
- par["mode"] = "caller";
- }
-#endif
- }
-
- string adapter;
- if (par.count("adapter"))
- {
- adapter = par.at("adapter");
- }
-
- m_mode = "default";
- if (par.count("mode"))
- {
- m_mode = par.at("mode");
- }
- SocketOption::Mode mode = SrtInterpretMode(m_mode, host, adapter);
- if (mode == SocketOption::FAILURE)
- {
- Error("Invalid mode");
- }
-
- if (!m_group_nodes.empty() && mode != SocketOption::CALLER)
- {
- Error("Group node specification is only available in caller mode");
- }
-
- // Fix the mode name after successful interpretation
- m_mode = SocketOption::mode_names[mode];
-
- par.erase("mode");
-
- if (par.count("blocking"))
- {
- m_blocking_mode = !false_names.count(par.at("blocking"));
- par.erase("blocking");
- }
-
- if (par.count("timeout"))
- {
- m_timeout = stoi(par.at("timeout"), 0, 0);
- par.erase("timeout");
- }
-
- if (par.count("adapter"))
- {
- m_adapter = adapter;
- par.erase("adapter");
- }
- else if (m_mode == "listener")
- {
- // For listener mode, adapter is taken from host,
- // if 'adapter' parameter is not given
- m_adapter = host;
- }
-
- if (par.count("tsbpd") && false_names.count(par.at("tsbpd")))
- {
- m_tsbpdmode = false;
- }
-
- if (par.count("port"))
- {
- m_outgoing_port = stoi(par.at("port"), 0, 0);
- par.erase("port");
- }
-
- // That's kinda clumsy, but it must rely on the defaults.
- // Default mode is live, so check if the file mode was enforced
- if (par.count("transtype") == 0 || par["transtype"] != "file")
- {
- // If the Live chunk size was nondefault, enforce the size.
- if (transmit_chunk_size != SRT_LIVE_DEF_PLSIZE)
- {
- if (transmit_chunk_size > SRT_LIVE_MAX_PLSIZE)
- throw std::runtime_error("Chunk size in live mode exceeds 1456 bytes; this is not supported");
-
- par["payloadsize"] = Sprint(transmit_chunk_size);
- }
- }
-
- // Assigning group configuration from a special "groupconfig" attribute.
- // This is the only way how you can set up this configuration at the listener side.
- if (par.count("groupconfig"))
- {
- m_group_config = par.at("groupconfig");
- par.erase("groupconfig");
- }
-
- // Fix Minversion, if specified as string
- if (par.count("minversion"))
- {
- string v = par["minversion"];
- if (v.find('.') != string::npos)
- {
- int version = srt::SrtParseVersion(v.c_str());
- if (version == 0)
- {
- throw std::runtime_error(Sprint("Value for 'minversion' doesn't specify a valid version: ", v));
- }
- par["minversion"] = Sprint(version);
- Verb() << "\tFIXED: minversion = 0x" << std::hex << std::setfill('0') << std::setw(8) << version << std::dec;
- }
- }
-
- // Assign the others here.
- m_options = par;
- m_options["mode"] = m_mode;
-}
-
-void SrtCommon::PrepareListener(string host, int port, int backlog)
-{
- m_bindsock = srt_create_socket();
- if (m_bindsock == SRT_ERROR)
- Error("srt_create_socket");
-
- int stat = ConfigurePre(m_bindsock);
- if (stat == SRT_ERROR)
- Error("ConfigurePre");
-
- if (!m_blocking_mode)
- {
- srt_conn_epoll = AddPoller(m_bindsock, SRT_EPOLL_OUT);
- }
-
- auto sa = CreateAddr(host, port);
- Verb() << "Binding a server on " << host << ":" << port << " ...";
- stat = srt_bind(m_bindsock, sa.get(), sizeof sa);
- if (stat == SRT_ERROR)
- {
- srt_close(m_bindsock);
- Error("srt_bind");
- }
-
- Verb() << " listen... " << VerbNoEOL;
- stat = srt_listen(m_bindsock, backlog);
- if (stat == SRT_ERROR)
- {
- srt_close(m_bindsock);
- Error("srt_listen");
- }
-
-}
-
-void SrtCommon::StealFrom(SrtCommon& src)
-{
- // This is used when SrtCommon class designates a listener
- // object that is doing Accept in appropriate direction class.
- // The new object should get the accepted socket.
- m_direction = src.m_direction;
- m_blocking_mode = src.m_blocking_mode;
- m_timeout = src.m_timeout;
- m_tsbpdmode = src.m_tsbpdmode;
- m_options = src.m_options;
- m_bindsock = SRT_INVALID_SOCK; // no listener
- m_sock = src.m_sock;
- src.m_sock = SRT_INVALID_SOCK; // STEALING
-}
-
-void SrtCommon::AcceptNewClient()
-{
- sockaddr_any scl;
-
- ::transmit_throw_on_interrupt = true;
-
- if (!m_blocking_mode)
- {
- Verb() << "[ASYNC] (conn=" << srt_conn_epoll << ")";
-
- int len = 2;
- SRTSOCKET ready[2];
- while (srt_epoll_wait(srt_conn_epoll, 0, 0, ready, &len, 1000, 0, 0, 0, 0) == -1)
- {
- if (::transmit_int_state)
- Error("srt_epoll_wait for srt_accept: interrupt");
-
- if (srt_getlasterror(NULL) == SRT_ETIMEOUT)
- continue;
- Error("srt_epoll_wait(srt_conn_epoll)");
- }
-
- Verb() << "[EPOLL: " << len << " sockets] " << VerbNoEOL;
- }
- Verb() << " accept..." << VerbNoEOL;
-
- m_sock = srt_accept(m_bindsock, (scl.get()), (&scl.len));
- if (m_sock == SRT_INVALID_SOCK)
- {
- srt_close(m_bindsock);
- m_bindsock = SRT_INVALID_SOCK;
- Error("srt_accept");
- }
-
-#if ENABLE_BONDING
- if (m_sock & SRTGROUP_MASK)
- {
- m_listener_group = true;
- if (m_group_config != "")
- {
- // Don't break the connection basing on this, just ignore.
- Verb() << " (ignoring setting group config: '" << m_group_config << "') " << VerbNoEOL;
- }
- // There might be added a poller, remove it.
- // We need it work different way.
-
-#ifndef SRT_OLD_APP_READER
-
- if (srt_epoll != -1)
- {
- Verb() << "(Group: erasing epoll " << srt_epoll << ") " << VerbNoEOL;
- srt_epoll_release(srt_epoll);
- }
-
- // Don't add any sockets, they will have to be added
- // anew every time again.
- srt_epoll = srt_epoll_create();
-#endif
-
- // Group data must have a size of at least 1
- // otherwise the srt_group_data() call will fail
- if (m_group_data.empty())
- m_group_data.resize(1);
-
- Verb() << " connected(group epoll " << srt_epoll <<").";
- }
- else
-#endif
- {
- sockaddr_any peeraddr(AF_INET6);
- string peer = "<?PEER?>";
- if (-1 != srt_getpeername(m_sock, (peeraddr.get()), (&peeraddr.len)))
- {
- peer = peeraddr.str();
- }
-
- sockaddr_any agentaddr(AF_INET6);
- string agent = "<?AGENT?>";
- if (-1 != srt_getsockname(m_sock, (agentaddr.get()), (&agentaddr.len)))
- {
- agent = agentaddr.str();
- }
-
- Verb() << " connected [" << agent << "] <-- " << peer;
- }
- ::transmit_throw_on_interrupt = false;
-
- // ConfigurePre is done on bindsock, so any possible Pre flags
- // are DERIVED by sock. ConfigurePost is done exclusively on sock.
- int stat = ConfigurePost(m_sock);
- if (stat == SRT_ERROR)
- Error("ConfigurePost");
-}
-
-static string PrintEpollEvent(int events, int et_events)
-{
- static pair<int, const char*> const namemap [] = {
- make_pair(SRT_EPOLL_IN, "R"),
- make_pair(SRT_EPOLL_OUT, "W"),
- make_pair(SRT_EPOLL_ERR, "E"),
- make_pair(SRT_EPOLL_UPDATE, "U")
- };
-
- ostringstream os;
- int N = Size(namemap);
-
- for (int i = 0; i < N; ++i)
- {
- if (events & namemap[i].first)
- {
- os << "[";
- if (et_events & namemap[i].first)
- os << "^";
- os << namemap[i].second << "]";
- }
- }
-
- return os.str();
-}
-
-void SrtCommon::Init(string host, int port, string path, map<string,string> par, SRT_EPOLL_OPT dir)
-{
- m_direction = dir;
- InitParameters(host, path, par);
-
- int backlog = 1;
- if (m_mode == "listener" && par.count("groupconnect")
- && true_names.count(par["groupconnect"]))
- {
- backlog = 10;
- }
-
- Verb() << "Opening SRT " << DirectionName(dir) << " " << m_mode
- << "(" << (m_blocking_mode ? "" : "non-") << "blocking,"
- << " backlog=" << backlog << ") on "
- << host << ":" << port;
-
- try
- {
- if (m_mode == "caller")
- {
- if (m_group_nodes.empty())
- {
- OpenClient(host, port);
- }
-#if ENABLE_BONDING
- else
- {
- OpenGroupClient(); // Source data are in the fields already.
- }
-#endif
- }
- else if (m_mode == "listener")
- OpenServer(m_adapter, port, backlog);
- else if (m_mode == "rendezvous")
- OpenRendezvous(m_adapter, host, port);
- else
- {
- throw std::invalid_argument("Invalid 'mode'. Use 'client' or 'server'");
- }
- }
- catch (...)
- {
- // This is an in-constructor-called function, so
- // when the exception is thrown, the destructor won't
- // close the sockets. This intercepts the exception
- // to close them.
- Verb() << "Open FAILED - closing SRT sockets";
- if (m_bindsock != SRT_INVALID_SOCK)
- srt_close(m_bindsock);
- if (m_sock != SRT_INVALID_SOCK)
- srt_close(m_sock);
- m_sock = m_bindsock = SRT_INVALID_SOCK;
- throw;
- }
-
- int pbkeylen = 0;
- SRT_KM_STATE kmstate, snd_kmstate, rcv_kmstate;
- int len = sizeof (int);
- srt_getsockflag(m_sock, SRTO_PBKEYLEN, &pbkeylen, &len);
- srt_getsockflag(m_sock, SRTO_KMSTATE, &kmstate, &len);
- srt_getsockflag(m_sock, SRTO_SNDKMSTATE, &snd_kmstate, &len);
- srt_getsockflag(m_sock, SRTO_RCVKMSTATE, &rcv_kmstate, &len);
-
- Verb() << "ENCRYPTION status: " << KmStateStr(kmstate)
- << " (SND:" << KmStateStr(snd_kmstate) << " RCV:" << KmStateStr(rcv_kmstate)
- << ") PBKEYLEN=" << pbkeylen;
-
- // Display some selected options on the socket.
- if (Verbose::on)
- {
- int64_t bandwidth = 0;
- int latency = 0;
- bool blocking_snd = false, blocking_rcv = false;
- int dropdelay = 0;
- int size_int = sizeof (int), size_int64 = sizeof (int64_t), size_bool = sizeof (bool);
- char packetfilter[100] = "";
- int packetfilter_size = 100;
-
- srt_getsockflag(m_sock, SRTO_MAXBW, &bandwidth, &size_int64);
- srt_getsockflag(m_sock, SRTO_RCVLATENCY, &latency, &size_int);
- srt_getsockflag(m_sock, SRTO_RCVSYN, &blocking_rcv, &size_bool);
- srt_getsockflag(m_sock, SRTO_SNDSYN, &blocking_snd, &size_bool);
- srt_getsockflag(m_sock, SRTO_SNDDROPDELAY, &dropdelay, &size_int);
- srt_getsockflag(m_sock, SRTO_PACKETFILTER, (packetfilter), (&packetfilter_size));
-
- Verb() << "OPTIONS: maxbw=" << bandwidth << " rcvlatency=" << latency << boolalpha
- << " blocking{rcv=" << blocking_rcv << " snd=" << blocking_snd
- << "} snddropdelay=" << dropdelay << " packetfilter=" << packetfilter;
- }
-
- if (!m_blocking_mode)
- {
- // Don't add new epoll if already created as a part
- // of group management: if (srt_epoll == -1)...
-
- if (m_mode == "caller")
- dir = (dir | SRT_EPOLL_UPDATE);
- Verb() << "NON-BLOCKING MODE - SUB FOR " << PrintEpollEvent(dir, 0);
-
- srt_epoll = AddPoller(m_sock, dir);
- }
-}
-
-int SrtCommon::AddPoller(SRTSOCKET socket, int modes)
-{
- int pollid = srt_epoll_create();
- if (pollid == -1)
- throw std::runtime_error("Can't create epoll in nonblocking mode");
- Verb() << "EPOLL: creating eid=" << pollid << " and adding @" << socket
- << " in " << DirectionName(SRT_EPOLL_OPT(modes)) << " mode";
- srt_epoll_add_usock(pollid, socket, &modes);
- return pollid;
-}
-
-int SrtCommon::ConfigurePost(SRTSOCKET sock)
-{
- bool yes = m_blocking_mode;
- int result = 0;
- if (m_direction & SRT_EPOLL_OUT)
- {
- Verb() << "Setting SND blocking mode: " << boolalpha << yes << " timeout=" << m_timeout;
- result = srt_setsockopt(sock, 0, SRTO_SNDSYN, &yes, sizeof yes);
- if (result == -1)
- {
-#ifdef PLEASE_LOG
- extern srt_logging::Logger applog;
- applog.Error() << "ERROR SETTING OPTION: SRTO_SNDSYN";
-#endif
- return result;
- }
-
- if (m_timeout)
- result = srt_setsockopt(sock, 0, SRTO_SNDTIMEO, &m_timeout, sizeof m_timeout);
- if (result == -1)
- {
-#ifdef PLEASE_LOG
- extern srt_logging::Logger applog;
- applog.Error() << "ERROR SETTING OPTION: SRTO_SNDTIMEO";
-#endif
- return result;
- }
- }
-
- if (m_direction & SRT_EPOLL_IN)
- {
- Verb() << "Setting RCV blocking mode: " << boolalpha << yes << " timeout=" << m_timeout;
- result = srt_setsockopt(sock, 0, SRTO_RCVSYN, &yes, sizeof yes);
- if (result == -1)
- return result;
-
- if (m_timeout)
- result = srt_setsockopt(sock, 0, SRTO_RCVTIMEO, &m_timeout, sizeof m_timeout);
- else
- {
- int timeout = 1000;
- result = srt_setsockopt(sock, 0, SRTO_RCVTIMEO, &timeout, sizeof timeout);
- }
- if (result == -1)
- return result;
- }
-
- // host is only checked for emptiness and depending on that the connection mode is selected.
- // Here we are not exactly interested with that information.
- vector<string> failures;
-
- SrtConfigurePost(sock, m_options, &failures);
-
-
- if (!failures.empty())
- {
- if (Verbose::on)
- {
- Verb() << "WARNING: failed to set options: ";
- copy(failures.begin(), failures.end(), ostream_iterator<string>(*Verbose::cverb, ", "));
- Verb();
- }
- }
-
- return 0;
-}
-
-int SrtCommon::ConfigurePre(SRTSOCKET sock)
-{
- int result = 0;
-
- int no = 0;
- if (!m_tsbpdmode)
- {
- result = srt_setsockopt(sock, 0, SRTO_TSBPDMODE, &no, sizeof no);
- if (result == -1)
- return result;
- }
-
- // Let's pretend async mode is set this way.
- // This is for asynchronous connect.
- int maybe = m_blocking_mode;
- result = srt_setsockopt(sock, 0, SRTO_RCVSYN, &maybe, sizeof maybe);
- if (result == -1)
- return result;
-
- // host is only checked for emptiness and depending on that the connection mode is selected.
- // Here we are not exactly interested with that information.
- vector<string> failures;
-
- // NOTE: here host = "", so the 'connmode' will be returned as LISTENER always,
- // but it doesn't matter here. We don't use 'connmode' for anything else than
- // checking for failures.
- SocketOption::Mode conmode = SrtConfigurePre(sock, "", m_options, &failures);
-
- if (conmode == SocketOption::FAILURE)
- {
- if (Verbose::on )
- {
- Verb() << "WARNING: failed to set options: ";
- copy(failures.begin(), failures.end(), ostream_iterator<string>(*Verbose::cverb, ", "));
- Verb();
- }
-
- return SRT_ERROR;
- }
-
- return 0;
-}
-
-void SrtCommon::SetupAdapter(const string& host, int port)
-{
- auto lsa = CreateAddr(host, port);
- int stat = srt_bind(m_sock, lsa.get(), sizeof lsa);
- if (stat == SRT_ERROR)
- Error("srt_bind");
-}
-
-void SrtCommon::OpenClient(string host, int port)
-{
- PrepareClient();
-
- if (m_outgoing_port || m_adapter != "")
- {
- SetupAdapter(m_adapter, m_outgoing_port);
- }
-
- ConnectClient(host, port);
-}
-
-void SrtCommon::PrepareClient()
-{
- m_sock = srt_create_socket();
- if (m_sock == SRT_ERROR)
- Error("srt_create_socket");
-
- int stat = ConfigurePre(m_sock);
- if (stat == SRT_ERROR)
- Error("ConfigurePre");
-
- if (!m_blocking_mode)
- {
- srt_conn_epoll = AddPoller(m_sock, SRT_EPOLL_CONNECT | SRT_EPOLL_ERR);
- }
-
-}
-
-#if ENABLE_BONDING
-void TransmitGroupSocketConnect(void* srtcommon, SRTSOCKET sock, int error, const sockaddr* /*peer*/, int token)
-{
- SrtCommon* that = (SrtCommon*)srtcommon;
-
- if (error == SRT_SUCCESS)
- {
- return; // nothing to do for a successful socket
- }
-
-#ifdef PLEASE_LOG
- applog.Debug("connect callback: error on @", sock, " erc=", error, " token=", token);
-#endif
-
- /* Example: identify by target address
- sockaddr_any peersa = peer;
- sockaddr_any agentsa;
- bool haveso = (srt_getsockname(sock, agentsa.get(), &agentsa.len) != -1);
- */
-
- for (auto& n: that->m_group_nodes)
- {
- if (n.token != -1 && n.token == token)
- {
- n.error = error;
- n.reason = srt_getrejectreason(sock);
- return;
- }
-
- /*
-
- bool isso = haveso && !n.source.empty();
- if (n.target == peersa && (!isso || n.source.equal_address(agentsa)))
- {
- Verb() << " (by target)" << VerbNoEOL;
- n.error = error;
- n.reason = srt_getrejectreason(sock);
- return;
- }
- */
- }
-
- Verb() << " IPE: LINK NOT FOUND???]";
-}
-
-void SrtCommon::OpenGroupClient()
-{
- SRT_GROUP_TYPE type = SRT_GTYPE_UNDEFINED;
-
- // Resolve group type.
- if (m_group_type == "broadcast")
- type = SRT_GTYPE_BROADCAST;
- else if (m_group_type == "backup")
- type = SRT_GTYPE_BACKUP;
- else
- {
- Error("With //group, type='" + m_group_type + "' undefined");
- }
-
- m_sock = srt_create_group(type);
- if (m_sock == -1)
- Error("srt_create_group");
-
- srt_connect_callback(m_sock, &TransmitGroupSocketConnect, this);
-
- int stat = -1;
- if (m_group_config != "")
- {
- Verb() << "Ignoring setting group config: '" << m_group_config;
- }
-
- stat = ConfigurePre(m_sock);
-
- if ( stat == SRT_ERROR )
- Error("ConfigurePre");
-
- if (!m_blocking_mode)
- {
- // Note: here the GROUP is added to the poller.
- srt_conn_epoll = AddPoller(m_sock, SRT_EPOLL_CONNECT | SRT_EPOLL_ERR);
- }
-
- // Don't check this. Should this fail, the above would already.
-
- // XXX Now do it regardless whether it's blocking or non-blocking
- // mode - reading from group is currently manually from every socket.
- srt_epoll = srt_epoll_create();
-
- // ConnectClient can't be used here, the code must
- // be more-less repeated. In this case the situation
- // that not all connections can be established is tolerated,
- // the only case of error is when none of the connections
- // can be established.
-
- bool any_node = false;
-
- Verb() << "REDUNDANT connections with " << m_group_nodes.size() << " nodes:";
-
- if (m_group_data.empty())
- m_group_data.resize(1);
-
- vector<SRT_SOCKGROUPCONFIG> targets;
- int namelen = sizeof (sockaddr_any);
-
- Verb() << "Connecting to nodes:";
- int i = 1;
- for (Connection& c: m_group_nodes)
- {
- auto sa = CreateAddr(c.host, c.port);
- c.target = sa;
- Verb() << "\t[" << c.token << "] " << c.host << ":" << c.port << VerbNoEOL;
- vector<string> extras;
- if (c.weight)
- extras.push_back(Sprint("weight=", c.weight));
-
- if (!c.source.empty())
- extras.push_back("source=" + c.source.str());
-
- if (!extras.empty())
- {
- Verb() << "?" << extras[0] << VerbNoEOL;
- for (size_t i = 1; i < extras.size(); ++i)
- Verb() << "&" << extras[i] << VerbNoEOL;
- }
-
- Verb();
- ++i;
- const sockaddr* source = c.source.empty() ? nullptr : c.source.get();
- SRT_SOCKGROUPCONFIG gd = srt_prepare_endpoint(source, sa.get(), namelen);
- gd.weight = c.weight;
- gd.config = c.options;
-
- targets.push_back(gd);
- }
-
- ::transmit_throw_on_interrupt = true;
- for (;;) // REPEATABLE BLOCK
- {
-Connect_Again:
- Verb() << "Waiting for group connection... " << VerbNoEOL;
-
- int fisock = srt_connect_group(m_sock, targets.data(), targets.size());
-
- if (fisock == SRT_ERROR)
- {
- // Complete the error information for every member
- ostringstream out;
- set<int> reasons;
- for (Connection& c: m_group_nodes)
- {
- if (c.error != SRT_SUCCESS)
- {
- out << "[" << c.token << "] " << c.host << ":" << c.port;
- if (!c.source.empty())
- out << "[[" << c.source.str() << "]]";
- out << ": " << srt_strerror(c.error, 0) << ": " << srt_rejectreason_str(c.reason) << endl;
- }
- reasons.insert(c.reason);
- }
-
- if (transmit_retry_connect && (transmit_retry_always || (reasons.size() == 1 && *reasons.begin() == SRT_REJ_TIMEOUT)))
- {
- if (transmit_retry_connect != -1)
- --transmit_retry_connect;
-
- Verb() << "...all links timeout, retrying (" << transmit_retry_connect << ")...";
- continue;
- }
-
- Error("srt_connect_group, nodes:\n" + out.str());
- }
- else
- {
- Verb() << "[ASYNC] will wait..." << VerbNoEOL;
- }
-
- break;
- }
-
- if (m_blocking_mode)
- {
- Verb() << "SUCCESSFUL";
- }
- else
- {
- Verb() << "INITIATED [ASYNC]";
- }
-
- // Configuration change applied on a group should
- // spread the setting on all sockets.
- ConfigurePost(m_sock);
-
- for (size_t i = 0; i < targets.size(); ++i)
- {
- // As m_group_nodes is simply transformed into 'targets',
- // one index can be used to index them all. You don't
- // have to check if they have equal addresses because they
- // are equal by definition.
- if (targets[i].id != -1 && targets[i].errorcode == SRT_SUCCESS)
- {
- m_group_nodes[i].socket = targets[i].id;
- }
- }
-
- // Now check which sockets were successful, only those
- // should be added to epoll.
- size_t size = m_group_data.size();
- stat = srt_group_data(m_sock, m_group_data.data(), &size);
- if (stat == -1 && size > m_group_data.size())
- {
- // Just too small buffer. Resize and continue.
- m_group_data.resize(size);
- stat = srt_group_data(m_sock, m_group_data.data(), &size);
- }
-
- if (stat == -1)
- {
- Error("srt_group_data");
- }
- m_group_data.resize(size);
-
- for (size_t i = 0; i < m_group_nodes.size(); ++i)
- {
- SRTSOCKET insock = m_group_nodes[i].socket;
- if (insock == -1)
- {
- Verb() << "TARGET '" << sockaddr_any(targets[i].peeraddr).str() << "' connection failed.";
- continue;
- }
-
- // Have socket, store it into the group socket array.
- any_node = true;
- }
-
- if (!any_node)
- Error("All connections failed");
-
- // Wait for REAL connected state if nonblocking mode, for AT LEAST one node.
- if (!m_blocking_mode)
- {
- Verb() << "[ASYNC] " << VerbNoEOL;
-
- // SPIN-WAITING version. Don't use it unless you know what you're doing.
- // SpinWaitAsync();
-
- // Socket readiness for connection is checked by polling on WRITE allowed sockets.
- int len1 = 2, len2 = 2;
- SRTSOCKET ready_conn[2], ready_err[2];
- if (srt_epoll_wait(srt_conn_epoll,
- ready_err, &len2,
- ready_conn, &len1,
- -1, // Wait infinitely
- NULL, NULL,
- NULL, NULL) != -1)
- {
- Verb() << "[C]" << VerbNoEOL;
- for (int i = 0; i < len1; ++i)
- Verb() << " " << ready_conn[i] << VerbNoEOL;
- Verb() << "[E]" << VerbNoEOL;
- for (int i = 0; i < len2; ++i)
- Verb() << " " << ready_err[i] << VerbNoEOL;
-
- Verb() << "";
-
- // We are waiting for one entity to be ready so it's either
- // in one or the other
- if (find(ready_err, ready_err+len2, m_sock) != ready_err+len2)
- {
- Verb() << "[EPOLL: " << len2 << " entities FAILED]";
- // Complete the error information for every member
- ostringstream out;
- set<int> reasons;
- for (Connection& c: m_group_nodes)
- {
- if (c.error != SRT_SUCCESS)
- {
- out << "[" << c.token << "] " << c.host << ":" << c.port;
- if (!c.source.empty())
- out << "[[" << c.source.str() << "]]";
- out << ": " << srt_strerror(c.error, 0) << ": " << srt_rejectreason_str(c.reason) << endl;
- }
- reasons.insert(c.reason);
- }
-
- if (transmit_retry_connect && (transmit_retry_always || (reasons.size() == 1 && *reasons.begin() == SRT_REJ_TIMEOUT)))
- {
- if (transmit_retry_connect != -1)
- --transmit_retry_connect;
-
-
- Verb() << "...all links timeout, retrying NOW (" << transmit_retry_connect << ")...";
- goto Connect_Again;
- }
-
- Error("srt_connect_group, nodes:\n" + out.str());
- }
- else if (find(ready_conn, ready_conn+len1, m_sock) != ready_conn+len1)
- {
- Verb() << "[EPOLL: " << len1 << " entities] " << VerbNoEOL;
- }
- else
- {
- Error("Group: SPURIOUS epoll readiness");
- }
- }
- else
- {
- Error("srt_epoll_wait");
- }
- }
-
- stat = ConfigurePost(m_sock);
- if (stat == -1)
- {
- // This kind of error must reject the whole operation.
- // Usually you'll get this error on the first socket,
- // and doing this on the others would result in the same.
- Error("ConfigurePost");
- }
-
- ::transmit_throw_on_interrupt = false;
-
- Verb() << "Group connection report:";
- for (auto& d: m_group_data)
- {
- // id, status, result, peeraddr
- Verb() << "@" << d.id << " <" << SockStatusStr(d.sockstate) << "> (=" << d.result << ") PEER:"
- << sockaddr_any((sockaddr*)&d.peeraddr, sizeof d.peeraddr).str();
- }
-
- // Prepare group data for monitoring the group status.
- m_group_data.resize(m_group_nodes.size());
-}
-#endif
-
-/*
- This may be used sometimes for testing, but it's nonportable.
- void SrtCommon::SpinWaitAsync()
- {
- static string udt_status_names [] = {
- "INIT" , "OPENED", "LISTENING", "CONNECTING", "CONNECTED", "BROKEN", "CLOSING", "CLOSED", "NONEXIST"
- };
-
- for (;;)
- {
- SRT_SOCKSTATUS state = srt_getsockstate(m_sock);
- if (int(state) < SRTS_CONNECTED)
- {
- if (Verbose::on)
- Verb() << state;
- usleep(250000);
- continue;
- }
- else if (int(state) > SRTS_CONNECTED)
- {
- Error("UDT::connect status=" + udt_status_names[state]);
- }
-
- return;
- }
- }
- */
-
-struct TransmitErrorReason
-{
- int error;
- int reason;
-};
-
-static std::map<SRTSOCKET, TransmitErrorReason> transmit_error_storage;
-
-static void TransmitConnectCallback(void*, SRTSOCKET socket, int errorcode, const sockaddr* /*peer*/, int /*token*/)
-{
- int reason = srt_getrejectreason(socket);
- transmit_error_storage[socket] = TransmitErrorReason { errorcode, reason };
- Verb() << "[Connection error reported on @" << socket << "]";
-}
-
-void SrtCommon::ConnectClient(string host, int port)
-{
- auto sa = CreateAddr(host, port);
- Verb() << "Connecting to " << host << ":" << port << " ... " << VerbNoEOL;
-
- if (!m_blocking_mode)
- {
- srt_connect_callback(m_sock, &TransmitConnectCallback, 0);
- }
-
- int stat = -1;
- for (;;)
- {
- ::transmit_throw_on_interrupt = true;
- stat = srt_connect(m_sock, sa.get(), sizeof sa);
- ::transmit_throw_on_interrupt = false;
- if (stat == SRT_ERROR)
- {
- int reason = srt_getrejectreason(m_sock);
-#if PLEASE_LOG
- LOGP(applog.Error, "ERROR reported by srt_connect - closing socket @", m_sock);
-#endif
- if (transmit_retry_connect && (transmit_retry_always || reason == SRT_REJ_TIMEOUT))
- {
- if (transmit_retry_connect != -1)
- --transmit_retry_connect;
-
- Verb() << "...timeout, retrying (" << transmit_retry_connect << ")...";
- continue;
- }
-
- srt_close(m_sock);
- Error("srt_connect", reason);
- }
- break;
- }
-
- // Wait for REAL connected state if nonblocking mode
- if (!m_blocking_mode)
- {
- Verb() << "[ASYNC] " << VerbNoEOL;
-
- // SPIN-WAITING version. Don't use it unless you know what you're doing.
- // SpinWaitAsync();
-
- // Socket readiness for connection is checked by polling on WRITE allowed sockets.
- int lenc = 2, lene = 2;
- SRTSOCKET ready_connect[2], ready_error[2];
- if (srt_epoll_wait(srt_conn_epoll, ready_error, &lene, ready_connect, &lenc, -1, 0, 0, 0, 0) != -1)
- {
- // We should have just one socket, so check whatever socket
- // is in the transmit_error_storage.
- if (!transmit_error_storage.empty())
- {
- Verb() << "[CALLBACK(error): " << VerbNoEOL;
- int error, reason;
- bool failed = false;
- for (pair<const SRTSOCKET, TransmitErrorReason>& e: transmit_error_storage)
- {
- Verb() << "{@" << e.first << " error=" << e.second.error
- << " reason=" << e.second.reason << "} " << VerbNoEOL;
- error = e.second.error;
- reason = e.second.reason;
- if (error != SRT_SUCCESS)
- failed = true;
- }
- Verb() << "]";
- transmit_error_storage.clear();
- if (failed)
- Error("srt_connect(async/cb)", reason, error);
- }
-
- if (lene > 0)
- {
- Verb() << "[EPOLL(error): " << lene << " sockets]";
- int reason = srt_getrejectreason(ready_error[0]);
- Error("srt_connect(async)", reason, SRT_ECONNREJ);
- }
- Verb() << "[EPOLL: " << lenc << " sockets] " << VerbNoEOL;
- }
- else
- {
- transmit_error_storage.clear();
- Error("srt_epoll_wait(srt_conn_epoll)");
- }
-
- transmit_error_storage.clear();
- }
-
- Verb() << " connected.";
- stat = ConfigurePost(m_sock);
- if (stat == SRT_ERROR)
- Error("ConfigurePost");
-}
-
-void SrtCommon::Error(string src, int reason, int force_result)
-{
- int errnov = 0;
- const int result = force_result == 0 ? srt_getlasterror(&errnov) : force_result;
- if (result == SRT_SUCCESS)
- {
- cerr << "\nERROR (app): " << src << endl;
- throw std::runtime_error(src);
- }
- string message = srt_strerror(result, errnov);
- if (result == SRT_ECONNREJ)
- {
- if ( Verbose::on )
- Verb() << "FAILURE\n" << src << ": [" << result << "] "
- << "Connection rejected: [" << int(reason) << "]: "
- << srt_rejectreason_str(reason);
- else
- cerr << "\nERROR #" << result
- << ": Connection rejected: [" << int(reason) << "]: "
- << srt_rejectreason_str(reason);
- }
- else
- {
- if ( Verbose::on )
- Verb() << "FAILURE\n" << src << ": [" << result << "." << errnov << "] " << message;
- else
- cerr << "\nERROR #" << result << "." << errnov << ": " << message << endl;
- }
-
- throw TransmissionError("error: " + src + ": " + message);
-}
-
-void SrtCommon::SetupRendezvous(string adapter, string host, int port)
-{
- sockaddr_any target = CreateAddr(host, port);
- if (target.family() == AF_UNSPEC)
- {
- Error("Unable to resolve target host: " + host);
- }
-
- bool yes = true;
- srt_setsockopt(m_sock, 0, SRTO_RENDEZVOUS, &yes, sizeof yes);
-
- const int outport = m_outgoing_port ? m_outgoing_port : port;
-
- // Prefer the same IPv as target host
- auto localsa = CreateAddr(adapter, outport, target.family());
- string showhost = adapter;
- if (showhost == "")
- showhost = "ANY";
- if (target.family() == AF_INET6)
- showhost = "[" + showhost + "]";
- Verb() << "Binding rendezvous: " << showhost << ":" << outport << " ...";
- int stat = srt_bind(m_sock, localsa.get(), localsa.size());
- if (stat == SRT_ERROR)
- {
- srt_close(m_sock);
- Error("srt_bind");
- }
-}
-
-void SrtCommon::Close()
-{
-#if PLEASE_LOG
- extern srt_logging::Logger applog;
- LOGP(applog.Error, "CLOSE requested - closing socket @", m_sock);
-#endif
- bool any = false;
- bool yes = true;
- if (m_sock != SRT_INVALID_SOCK)
- {
- Verb() << "SrtCommon: DESTROYING CONNECTION, closing socket (rt%" << m_sock << ")...";
- srt_setsockflag(m_sock, SRTO_SNDSYN, &yes, sizeof yes);
- srt_close(m_sock);
- any = true;
- }
-
- if (m_bindsock != SRT_INVALID_SOCK)
- {
- Verb() << "SrtCommon: DESTROYING SERVER, closing socket (ls%" << m_bindsock << ")...";
- // Set sndsynchro to the socket to synch-close it.
- srt_setsockflag(m_bindsock, SRTO_SNDSYN, &yes, sizeof yes);
- srt_close(m_bindsock);
- any = true;
- }
-
- if (any)
- Verb() << "SrtCommon: ... done.";
-}
-
-SrtCommon::~SrtCommon()
-{
- Close();
-}
-
-#if ENABLE_BONDING
-void SrtCommon::UpdateGroupStatus(const SRT_SOCKGROUPDATA* grpdata, size_t grpdata_size)
-{
- if (!grpdata)
- {
- // This happens when you passed too small array. Treat this as error and stop.
- cerr << "ERROR: broadcast group update reports " << grpdata_size
- << " existing sockets, but app registerred only " << m_group_nodes.size() << endl;
- Error("Too many unpredicted sockets in the group");
- }
-
- // Clear the active flag in all nodes so that they are reactivated
- // if they are in the group list, REGARDLESS OF THE STATUS. We need to
- // see all connections that are in the nodes, but not in the group,
- // and this one would have to be activated.
- const SRT_SOCKGROUPDATA* gend = grpdata + grpdata_size;
- for (auto& n: m_group_nodes)
- {
- bool active = (find_if(grpdata, gend,
- [&n] (const SRT_SOCKGROUPDATA& sg) { return sg.id == n.socket; }) != gend);
- if (!active)
- n.socket = SRT_INVALID_SOCK;
- }
-
- // Note: sockets are not necessarily in the same order. Find
- // the socket by id.
- for (size_t i = 0; i < grpdata_size; ++i)
- {
- const SRT_SOCKGROUPDATA& d = grpdata[i];
- SRTSOCKET id = d.id;
-
- SRT_SOCKSTATUS status = d.sockstate;
- int result = d.result;
- SRT_MEMBERSTATUS mstatus = d.memberstate;
-
- if (result != -1 && status == SRTS_CONNECTED)
- {
- // Short report with the state.
- Verb() << "G@" << id << "<" << MemberStatusStr(mstatus) << "> " << VerbNoEOL;
- continue;
- }
- // id, status, result, peeraddr
- Verb() << "\n\tG@" << id << " <" << SockStatusStr(status) << "/" << MemberStatusStr(mstatus) << "> (=" << result << ") PEER:"
- << sockaddr_any((sockaddr*)&d.peeraddr, sizeof d.peeraddr).str() << VerbNoEOL;
-
- if (status >= SRTS_BROKEN)
- {
- Verb() << "NOTE: socket @" << id << " is pending for destruction, waiting for it.";
- }
- }
-
- // This was only informative. Now we check all nodes if they
- // are not active
-
- int i = 1;
- for (auto& n: m_group_nodes)
- {
- if (n.error != SRT_SUCCESS)
- {
- Verb() << "[" << i << "] CONNECTION FAILURE to '" << n.host << ":" << n.port << "': "
- << srt_strerror(n.error, 0) << ":" << srt_rejectreason_str(n.reason);
- }
-
- // Check which nodes are no longer active and activate them.
- if (n.socket != SRT_INVALID_SOCK)
- continue;
-
- auto sa = CreateAddr(n.host, n.port);
- Verb() << "[" << i << "] RECONNECTING to node " << n.host << ":" << n.port << " ... " << VerbNoEOL;
- ++i;
-
- n.error = SRT_SUCCESS;
- n.reason = SRT_REJ_UNKNOWN;
-
- const sockaddr* source = n.source.empty() ? nullptr : n.source.get();
- SRT_SOCKGROUPCONFIG gd = srt_prepare_endpoint(source, sa.get(), sa.size());
- gd.weight = n.weight;
- gd.config = n.options;
- gd.token = n.token;
-
- int fisock = srt_connect_group(m_sock, &gd, 1);
- if (fisock == SRT_ERROR)
- {
- // Whatever. Skip the node.
- Verb() << "FAILED: ";
- }
- else
- {
- // Have socket, store it into the group socket array.
- n.socket = gd.id;
- }
- }
-}
-#endif
-
-SrtSource::SrtSource(string host, int port, std::string path, const map<string,string>& par)
-{
- Init(host, port, path, par, SRT_EPOLL_IN);
- ostringstream os;
- os << host << ":" << port;
- hostport_copy = os.str();
-}
-
-static void PrintSrtStats(SRTSOCKET sock, bool clr, bool bw, bool stats)
-{
- CBytePerfMon perf;
- // clear only if stats report is to be read
- srt_bstats(sock, &perf, clr);
-
- if (bw)
- cout << transmit_stats_writer->WriteBandwidth(perf.mbpsBandwidth);
- if (stats)
- cout << transmit_stats_writer->WriteStats(sock, perf);
-}
-
-
-#ifdef SRT_OLD_APP_READER
-
-// NOTE: 'output' is expected to be EMPTY here.
-bool SrtSource::GroupCheckPacketAhead(bytevector& output)
-{
- bool status = false;
- vector<SRTSOCKET> past_ahead;
-
- // This map no longer maps only ahead links.
- // Here are all links, and whether ahead, it's defined by the sequence.
- for (auto i = m_group_positions.begin(); i != m_group_positions.end(); ++i)
- {
- // i->first: socket ID
- // i->second: ReadPos { sequence, packet }
- // We are not interested with the socket ID because we
- // aren't going to read from it - we have the packet already.
- ReadPos& a = i->second;
-
- int seqdiff = CSeqNo::seqcmp(a.sequence, m_group_seqno);
- if ( seqdiff == 1)
- {
- // The very next packet. Return it.
- m_group_seqno = a.sequence;
- Verb() << " (SRT group: ahead delivery %" << a.sequence << " from @" << i->first << ")";
- swap(output, a.packet);
- status = true;
- }
- else if (seqdiff < 1 && !a.packet.empty())
- {
- Verb() << " (@" << i->first << " dropping collected ahead %" << a.sequence << ")";
- a.packet.clear();
- }
- // In case when it's >1, keep it in ahead
- }
-
- return status;
-}
-
-static string DisplayEpollResults(const std::set<SRTSOCKET>& sockset, std::string prefix)
-{
- typedef set<SRTSOCKET> fset_t;
- ostringstream os;
- os << prefix << " ";
- for (fset_t::const_iterator i = sockset.begin(); i != sockset.end(); ++i)
- {
- os << "@" << *i << " ";
- }
-
- return os.str();
-}
-
-bytevector SrtSource::GroupRead(size_t chunk)
-{
- // Read the current group status. m_sock is here the group id.
- bytevector output;
-
- // Later iteration over it might be less efficient than
- // by vector, but we'll also often try to check a single id
- // if it was ever seen broken, so that it's skipped.
- set<SRTSOCKET> broken;
-
-RETRY_READING:
-
- size_t size = m_group_data.size();
- int stat = srt_group_data(m_sock, m_group_data.data(), &size);
- if (stat == -1 && size > m_group_data.size())
- {
- // Just too small buffer. Resize and continue.
- m_group_data.resize(size);
- stat = srt_group_data(m_sock, m_group_data.data(), &size);
- }
- else
- {
- // Downsize if needed.
- m_group_data.resize(size);
- }
-
- if (stat == -1) // Also after the above fix
- {
- Error(UDT::getlasterror(), "FAILURE when reading group data");
- }
-
- if (size == 0)
- {
- Error("No sockets in the group - disconnected");
- }
-
- bool connected = false;
- for (auto& d: m_group_data)
- {
- if (d.status == SRTS_CONNECTED)
- {
- connected = true;
- break;
- }
- }
- if (!connected)
- {
- Error("All sockets in the group disconnected");
- }
-
- if (Verbose::on)
- {
- for (auto& d: m_group_data)
- {
- if (d.status != SRTS_CONNECTED)
- // id, status, result, peeraddr
- Verb() << "@" << d.id << " <" << SockStatusStr(d.status) << "> (=" << d.result << ") PEER:"
- << sockaddr_any((sockaddr*)&d.peeraddr, sizeof d.peeraddr).str();
- }
- }
-
- // Check first the ahead packets if you have any to deliver.
- if (m_group_seqno != -1 && !m_group_positions.empty())
- {
- bytevector ahead_packet;
-
- // This function also updates the group sequence pointer.
- if (GroupCheckPacketAhead(ahead_packet))
- return move(ahead_packet);
- }
-
- // LINK QUALIFICATION NAMES:
- //
- // HORSE: Correct link, which delivers the very next sequence.
- // Not necessarily this link is currently active.
- //
- // KANGAROO: Got some packets dropped and the sequence number
- // of the packet jumps over the very next sequence and delivers
- // an ahead packet.
- //
- // ELEPHANT: Is not ready to read, while others are, or reading
- // up to the current latest delivery sequence number does not
- // reach this sequence and the link becomes non-readable earlier.
-
- // The above condition has ruled out one kangaroo and turned it
- // into a horse.
-
- // Below there's a loop that will try to extract packets. Kangaroos
- // will be among the polled ones because skipping them risks that
- // the elephants will take over the reading. Links already known as
- // elephants will be also polled in an attempt to revitalize the
- // connection that experienced just a short living choking.
- //
- // After polling we attempt to read from every link that reported
- // read-readiness and read at most up to the sequence equal to the
- // current delivery sequence.
-
- // Links that deliver a packet below that sequence will be retried
- // until they deliver no more packets or deliver the packet of
- // expected sequence. Links that don't have a record in m_group_positions
- // and report readiness will be always read, at least to know what
- // sequence they currently stand on.
- //
- // Links that are already known as kangaroos will be polled, but
- // no reading attempt will be done. If after the reading series
- // it will turn out that we have no more horses, the slowest kangaroo
- // will be "advanced to a horse" (the ahead link with a sequence
- // closest to the current delivery sequence will get its sequence
- // set as current delivered and its recorded ahead packet returned
- // as the read packet).
-
- // If we find at least one horse, the packet read from that link
- // will be delivered. All other link will be just ensured update
- // up to this sequence number, or at worst all available packets
- // will be read. In this case all kangaroos remain kangaroos,
- // until the current delivery sequence m_group_seqno will be lifted
- // to the sequence recorded for these links in m_group_positions,
- // during the next time ahead check, after which they will become
- // horses.
-
- Verb() << "E(" << srt_epoll << ") " << VerbNoEOL;
-
- for (size_t i = 0; i < size; ++i)
- {
- SRT_SOCKGROUPDATA& d = m_group_data[i];
- if (d.status == SRTS_CONNECTING)
- {
- Verb() << "@" << d.id << "<pending> " << VerbNoEOL;
- int modes = SRT_EPOLL_OUT | SRT_EPOLL_ERR;
- srt_epoll_add_usock(srt_epoll, d.id, &modes);
- continue; // don't read over a failed or pending socket
- }
-
- if (d.status >= SRTS_BROKEN)
- {
- broken.insert(d.id);
- }
-
- if (broken.count(d.id))
- {
- Verb() << "@" << d.id << "<broken> " << VerbNoEOL;
- continue;
- }
-
- if (d.status != SRTS_CONNECTED)
- {
- Verb() << "@" << d.id << "<idle:" << SockStatusStr(d.status) << "> " << VerbNoEOL;
- // Sockets in this state are ignored. We are waiting until it
- // achieves CONNECTING state, then it's added to write.
- continue;
- }
-
- // Don't skip packets that are ahead because if we have a situation
- // that all links are either "elephants" (do not report read readiness)
- // and "kangaroos" (have already delivered an ahead packet) then
- // omiting kangaroos will result in only elephants to be polled for
- // reading. Elephants, due to the strict timing requirements and
- // ensurance that TSBPD on every link will result in exactly the same
- // delivery time for a packet of given sequence, having an elephant
- // and kangaroo in one cage means that the elephant is simply a broken
- // or half-broken link (the data are not delivered, but it will get
- // repaired soon, enough for SRT to maintain the connection, but it
- // will still drop packets that didn't arrive in time), in both cases
- // it may potentially block the reading for an indefinite time, while
- // simultaneously a kangaroo might be a link that got some packets
- // dropped, but then it's still capable to deliver packets on time.
-
- // Note also that about the fact that some links turn out to be
- // elephants we'll learn only after we try to poll and read them.
-
- // Note that d.id might be a socket that was previously being polled
- // on write, when it's attempting to connect, but now it's connected.
- // This will update the socket with the new event set.
-
- int modes = SRT_EPOLL_IN | SRT_EPOLL_ERR;
- srt_epoll_add_usock(srt_epoll, d.id, &modes);
- Verb() << "@" << d.id << "[READ] " << VerbNoEOL;
- }
-
- Verb() << "";
-
- // Here we need to make an additional check.
- // There might be a possibility that all sockets that
- // were added to the reader group, are ahead. At least
- // surely we don't have a situation that any link contains
- // an ahead-read subsequent packet, because GroupCheckPacketAhead
- // already handled that case.
- //
- // What we can have is that every link has:
- // - no known seq position yet (is not registered in the position map yet)
- // - the position equal to the latest delivered sequence
- // - the ahead position
-
- // Now the situation is that we don't have any packets
- // waiting for delivery so we need to wait for any to report one.
- // XXX We support blocking mode only at the moment.
- // The non-blocking mode would need to simply check the readiness
- // with only immediate report, and read-readiness would have to
- // be done in background.
-
- SrtPollState sready;
-
- // Poll on this descriptor until reading is available, indefinitely.
- if (UDT::epoll_swait(srt_epoll, sready, -1) == SRT_ERROR)
- {
- Error(UDT::getlasterror(), "UDT::epoll_swait(srt_epoll, group)");
- }
- if (Verbose::on)
- {
- Verb() << "RDY: {"
- << DisplayEpollResults(sready.rd(), "[R]")
- << DisplayEpollResults(sready.wr(), "[W]")
- << DisplayEpollResults(sready.ex(), "[E]")
- << "} " << VerbNoEOL;
-
- }
-
- LOGC(applog.Debug, log << "epoll_swait: "
- << DisplayEpollResults(sready.rd(), "[R]")
- << DisplayEpollResults(sready.wr(), "[W]")
- << DisplayEpollResults(sready.ex(), "[E]"));
-
- typedef set<SRTSOCKET> fset_t;
-
- // Handle sockets of pending connection and with errors.
- broken = sready.ex();
-
- // We don't do anything about sockets that have been configured to
- // poll on writing (that is, pending for connection). What we need
- // is that the epoll_swait call exit on that fact. Probably if this
- // was the only socket reported, no broken and no read-ready, this
- // will later check on output if still empty, if so, repeat the whole
- // function. This write-ready socket will be there already in the
- // connected state and will be added to read-polling.
-
- // Ok, now we need to have some extra qualifications:
- // 1. If a socket has no registry yet, we read anyway, just
- // to notify the current position. We read ONLY ONE PACKET this time,
- // we'll worry later about adjusting it to the current group sequence
- // position.
- // 2. If a socket is already position ahead, DO NOT read from it, even
- // if it is ready.
-
- // The state of things whether we were able to extract the very next
- // sequence will be simply defined by the fact that `output` is nonempty.
-
- int32_t next_seq = m_group_seqno;
-
- // If this set is empty, it won't roll even once, therefore output
- // will be surely empty. This will be checked then same way as when
- // reading from every socket resulted in error.
- for (fset_t::const_iterator i = sready.rd().begin(); i != sready.rd().end(); ++i)
- {
- // Check if this socket is in aheads
- // If so, don't read from it, wait until the ahead is flushed.
-
- SRTSOCKET id = *i;
- ReadPos* p = nullptr;
- auto pe = m_group_positions.find(id);
- if (pe != m_group_positions.end())
- {
- p = &pe->second;
- // Possible results of comparison:
- // x < 0: the sequence is in the past, the socket should be adjusted FIRST
- // x = 0: the socket should be ready to get the exactly next packet
- // x = 1: the case is already handled by GroupCheckPacketAhead.
- // x > 1: AHEAD. DO NOT READ.
- int seqdiff = CSeqNo::seqcmp(p->sequence, m_group_seqno);
- if (seqdiff > 1)
- {
- Verb() << "EPOLL: @" << id << " %" << p->sequence << " AHEAD, not reading.";
- continue;
- }
- }
-
-
- // Read from this socket stubbornly, until:
- // - reading is no longer possible (AGAIN)
- // - the sequence difference is >= 1
-
- int fi = 1; // marker for Verb to display flushing
- for (;;)
- {
- bytevector data(chunk);
- SRT_MSGCTRL mctrl = srt_msgctrl_default;
- stat = srt_recvmsg2(id, data.data(), chunk, &mctrl);
- if (stat == SRT_ERROR)
- {
- if (fi == 0)
- {
- if (Verbose::on)
- {
- if (p)
- {
- int32_t pktseq = p->sequence;
- int seqdiff = CSeqNo::seqcmp(p->sequence, m_group_seqno);
- Verb() << ". %" << pktseq << " " << seqdiff << ")";
- }
- else
- {
- Verb() << ".)";
- }
- }
- fi = 1;
- }
- int err = srt_getlasterror(0);
- if (err == SRT_EASYNCRCV)
- {
- // Do not treat this as spurious, just stop reading.
- break;
- }
- Verb() << "Error @" << id << ": " << srt_getlasterror_str();
- broken.insert(id);
- break;
- }
-
- // NOTE: checks against m_group_seqno and decisions based on it
- // must NOT be done if m_group_seqno is -1, which means that we
- // are about to deliver the very first packet and we take its
- // sequence number as a good deal.
-
- // The order must be:
- // - check discrepancy
- // - record the sequence
- // - check ordering.
- // The second one must be done always, but failed discrepancy
- // check should exclude the socket from any further checks.
- // That's why the common check for m_group_seqno != -1 can't
- // embrace everything below.
-
- // We need to first qualify the sequence, just for a case
- if (m_group_seqno != -1 && abs(m_group_seqno - mctrl.pktseq) > CSeqNo::m_iSeqNoTH)
- {
- // This error should be returned if the link turns out
- // to be the only one, or set to the group data.
- // err = SRT_ESECFAIL;
- if (fi == 0)
- {
- Verb() << ".)";
- fi = 1;
- }
- Verb() << "Error @" << id << ": SEQUENCE DISCREPANCY: base=%" << m_group_seqno << " vs pkt=%" << mctrl.pktseq << ", setting ESECFAIL";
- broken.insert(id);
- break;
- }
-
- // Rewrite it to the state for a case when next reading
- // would not succeed. Do not insert the buffer here because
- // this is only required when the sequence is ahead; for that
- // it will be fixed later.
- if (!p)
- {
- p = &(m_group_positions[id] = ReadPos { mctrl.pktseq, {} });
- }
- else
- {
- p->sequence = mctrl.pktseq;
- }
-
- if (m_group_seqno != -1)
- {
- // Now we can safely check it.
- int seqdiff = CSeqNo::seqcmp(mctrl.pktseq, m_group_seqno);
-
- if (seqdiff <= 0)
- {
- if (fi == 1)
- {
- Verb() << "(@" << id << " FLUSH:" << VerbNoEOL;
- fi = 0;
- }
-
- Verb() << "." << VerbNoEOL;
-
- // The sequence is recorded, the packet has to be discarded.
- // That's all.
- continue;
- }
-
- // Finish flush reporting if fallen into here
- if (fi == 0)
- {
- Verb() << ". %" << mctrl.pktseq << " " << (-seqdiff) << ")";
- fi = 1;
- }
-
- // Now we have only two possibilities:
- // seqdiff == 1: The very next sequence, we want to read and return the packet.
- // seqdiff > 1: The packet is ahead - record the ahead packet, but continue with the others.
-
- if (seqdiff > 1)
- {
- Verb() << "@" << id << " %" << mctrl.pktseq << " AHEAD";
- p->packet = move(data);
- break; // Don't read from that socket anymore.
- }
- }
-
- // We have seqdiff = 1, or we simply have the very first packet
- // which's sequence is taken as a good deal. Update the sequence
- // and record output.
-
- if (!output.empty())
- {
- Verb() << "@" << id << " %" << mctrl.pktseq << " REDUNDANT";
- break;
- }
-
-
- Verb() << "@" << id << " %" << mctrl.pktseq << " DELIVERING";
- output = move(data);
-
- // Record, but do not update yet, until all sockets are handled.
- next_seq = mctrl.pktseq;
- break;
- }
- }
-
- // ready_len is only the length of currently reported
- // ready sockets, NOT NECESSARILY containing all sockets from the group.
- if (broken.size() == size)
- {
- // All broken
- Error("All sockets broken");
- }
-
- if (Verbose::on && !broken.empty())
- {
- Verb() << "BROKEN: " << Printable(broken) << " - removing";
- }
-
- // Now remove all broken sockets from aheads, if any.
- // Even if they have already delivered a packet.
- for (SRTSOCKET d: broken)
- {
- m_group_positions.erase(d);
- srt_close(d);
- }
-
- // May be required to be re-read.
- broken.clear();
-
- if (!output.empty())
- {
- // We have extracted something, meaning that we have the sequence shift.
- // Update it now and don't do anything else with the sockets.
-
- // Sanity check
- if (next_seq == -1)
- {
- Error("IPE: next_seq not set after output extracted!");
- }
- m_group_seqno = next_seq;
- return output;
- }
-
- // Check if we have any sockets left :D
-
- // Here we surely don't have any more HORSES,
- // only ELEPHANTS and KANGAROOS. Qualify them and
- // attempt to at least take advantage of KANGAROOS.
-
- // In this position all links are either:
- // - updated to the current position
- // - updated to the newest possible possition available
- // - not yet ready for extraction (not present in the group)
-
- // If we haven't extracted the very next sequence position,
- // it means that we might only have the ahead packets read,
- // that is, the next sequence has been dropped by all links.
-
- if (!m_group_positions.empty())
- {
- // This might notify both lingering links, which didn't
- // deliver the required sequence yet, and links that have
- // the sequence ahead. Review them, and if you find at
- // least one packet behind, just wait for it to be ready.
- // Use again the waiting function because we don't want
- // the general waiting procedure to skip others.
- set<SRTSOCKET> elephants;
-
- // const because it's `typename decltype(m_group_positions)::value_type`
- pair<const SRTSOCKET, ReadPos>* slowest_kangaroo = nullptr;
-
- for (auto& sock_rp: m_group_positions)
- {
- // NOTE that m_group_seqno in this place wasn't updated
- // because we haven't successfully extracted anything.
- int seqdiff = CSeqNo::seqcmp(sock_rp.second.sequence, m_group_seqno);
- if (seqdiff < 0)
- {
- elephants.insert(sock_rp.first);
- }
- // If seqdiff == 0, we have a socket ON TRACK.
- else if (seqdiff > 0)
- {
- if (!slowest_kangaroo)
- {
- slowest_kangaroo = &sock_rp;
- }
- else
- {
- // Update to find the slowest kangaroo.
- int seqdiff = CSeqNo::seqcmp(slowest_kangaroo->second.sequence, sock_rp.second.sequence);
- if (seqdiff > 0)
- {
- slowest_kangaroo = &sock_rp;
- }
- }
- }
- }
-
- // Note that if no "slowest_kangaroo" was found, it means
- // that we don't have kangaroos.
- if (slowest_kangaroo)
- {
- // We have a slowest kangaroo. Elephants must be ignored.
- // Best case, they will get revived, worst case they will be
- // soon broken.
- //
- // As we already have the packet delivered by the slowest
- // kangaroo, we can simply return it.
-
- m_group_seqno = slowest_kangaroo->second.sequence;
- Verb() << "@" << slowest_kangaroo->first << " %" << m_group_seqno << " KANGAROO->HORSE";
- swap(output, slowest_kangaroo->second.packet);
- return output;
- }
-
- // Here ALL LINKS ARE ELEPHANTS, stating that we still have any.
- if (Verbose::on)
- {
- if (!elephants.empty())
- {
- // If we don't have kangaroos, then simply reattempt to
- // poll all elephants again anyway (at worst they are all
- // broken and we'll learn about it soon).
- Verb() << "ALL LINKS ELEPHANTS. Re-polling.";
- }
- else
- {
- Verb() << "ONLY BROKEN WERE REPORTED. Re-polling.";
- }
- }
- goto RETRY_READING;
- }
-
- // We have checked so far only links that were ready to poll.
- // Links that are not ready should be re-checked.
- // Links that were not ready at the entrance should be checked
- // separately, and probably here is the best moment to do it.
- // After we make sure that at least one link is ready, we can
- // reattempt to read a packet from it.
-
- // Ok, so first collect all sockets that are in
- // connecting state, make a poll for connection.
- srt_epoll_clear_usocks(srt_epoll);
- bool have_connectors = false, have_ready = false;
- for (auto& d: m_group_data)
- {
- if (d.status < SRTS_CONNECTED)
- {
- // Not sure anymore if IN or OUT signals the connect-readiness,
- // but no matter. The signal will be cleared once it is used,
- // while it will be always on when there's anything ready to read.
- int modes = SRT_EPOLL_IN | SRT_EPOLL_OUT;
- srt_epoll_add_usock(srt_epoll, d.id, &modes);
- have_connectors = true;
- }
- else if (d.status == SRTS_CONNECTED)
- {
- have_ready = true;
- }
- }
-
- if (have_ready || have_connectors)
- {
- Verb() << "(still have: " << (have_ready ? "+" : "-") << "ready, "
- << (have_connectors ? "+" : "-") << "conenctors).";
- goto RETRY_READING;
- }
-
- if (have_ready)
- {
- Verb() << "(connected in the meantime)";
- // Some have connected in the meantime, don't
- // waste time on the pending ones.
- goto RETRY_READING;
- }
-
- if (have_connectors)
- {
- Verb() << "(waiting for pending connectors to connect)";
- // Wait here for them to be connected.
- vector<SRTSOCKET> sready;
- sready.resize(m_group_data.size());
- int ready_len = m_group_data.size();
- if (srt_epoll_wait(srt_epoll, sready.data(), &ready_len, 0, 0, -1, 0, 0, 0, 0) == SRT_ERROR)
- {
- Error("All sockets in the group disconnected");
- }
-
- goto RETRY_READING;
- }
-
- Error("No data extracted");
- return output; // Just a marker - this above function throws an exception
-}
-
-#endif
-
-MediaPacket SrtSource::Read(size_t chunk)
-{
- static size_t counter = 1;
-
- bool have_group SRT_ATR_UNUSED = !m_group_nodes.empty();
-
- bytevector data(chunk);
- // EXPERIMENTAL
-#ifdef SRT_OLD_APP_READER
- if (have_group || m_listener_group)
- {
- data = GroupRead(chunk);
- }
-
- if (have_group)
- {
- // This is to be done for caller mode only
- UpdateGroupStatus(m_group_data.data(), m_group_data.size());
- }
-#else
-
- SRT_MSGCTRL mctrl = srt_msgctrl_default;
- bool ready = true;
- int stat;
-
- do
- {
-#if ENABLE_BONDING
- if (have_group || m_listener_group)
- {
- mctrl.grpdata = m_group_data.data();
- mctrl.grpdata_size = m_group_data.size();
- }
-#endif
-
- if (::transmit_int_state)
- Error("srt_recvmsg2: interrupted");
-
- ::transmit_throw_on_interrupt = true;
- stat = srt_recvmsg2(m_sock, data.data(), chunk, &mctrl);
- ::transmit_throw_on_interrupt = false;
- if (stat != SRT_ERROR)
- {
- ready = true;
- }
- else
- {
- int syserr = 0;
- int err = srt_getlasterror(&syserr);
-
- if (!m_blocking_mode)
- {
- // EAGAIN for SRT READING
- if (err == SRT_EASYNCRCV)
- {
-Epoll_again:
- Verb() << "AGAIN: - waiting for data by epoll(" << srt_epoll << ")...";
- // Poll on this descriptor until reading is available, indefinitely.
- int len = 2;
- SRT_EPOLL_EVENT sready[2];
- len = srt_epoll_uwait(srt_epoll, sready, len, -1);
- if (len != -1)
- {
- Verb() << "... epoll reported ready " << len << " sockets";
- // If the event was SRT_EPOLL_UPDATE, report it, and still wait.
-
- bool any_read_ready = false;
- vector<int> errored;
- for (int i = 0; i < len; ++i)
- {
- if (sready[i].events & SRT_EPOLL_UPDATE)
- {
- Verb() << "... [BROKEN CONNECTION reported on @" << sready[i].fd << "]";
- }
-
- if (sready[i].events & SRT_EPOLL_IN)
- any_read_ready = true;
-
- if (sready[i].events & SRT_EPOLL_ERR)
- {
- errored.push_back(sready[i].fd);
- }
- }
-
- if (!any_read_ready)
- {
- Verb() << " ... [NOT READ READY - AGAIN (" << errored.size() << " errored: " << Printable(errored) << ")]";
- goto Epoll_again;
- }
-
- continue;
- }
- // If was -1, then passthru.
- }
- }
- else
- {
- // In blocking mode it uses a minimum of 1s timeout,
- // and continues only if interrupt not requested.
- if (!::transmit_int_state && (err == SRT_EASYNCRCV || err == SRT_ETIMEOUT))
- {
- ready = false;
- continue;
- }
- }
- Error("srt_recvmsg2");
- }
-
- if (stat == 0)
- {
- throw ReadEOF(hostport_copy);
- }
-#if PLEASE_LOG
- extern srt_logging::Logger applog;
- LOGC(applog.Debug, log << "recv: #" << mctrl.msgno << " %" << mctrl.pktseq << " "
- << BufferStamp(data.data(), stat) << " BELATED: " << ((CTimer::getTime()-mctrl.srctime)/1000.0) << "ms");
-#endif
-
- Verb() << "(#" << mctrl.msgno << " %" << mctrl.pktseq << " " << BufferStamp(data.data(), stat) << ") " << VerbNoEOL;
- }
- while (!ready);
-
- chunk = size_t(stat);
- if (chunk < data.size())
- data.resize(chunk);
-
- const bool need_bw_report = transmit_bw_report && int(counter % transmit_bw_report) == transmit_bw_report - 1;
- const bool need_stats_report = transmit_stats_report && counter % transmit_stats_report == transmit_stats_report - 1;
-
-#if ENABLE_BONDING
- if (have_group) // Means, group with caller mode
- {
- UpdateGroupStatus(mctrl.grpdata, mctrl.grpdata_size);
- if (transmit_stats_writer && (need_stats_report || need_bw_report))
- {
- PrintSrtStats(m_sock, need_stats_report, need_bw_report, need_stats_report);
- for (size_t i = 0; i < mctrl.grpdata_size; ++i)
- PrintSrtStats(mctrl.grpdata[i].id, need_stats_report, need_bw_report, need_stats_report);
- }
- }
- else
-#endif
- {
- if (transmit_stats_writer && (need_stats_report || need_bw_report))
- {
- PrintSrtStats(m_sock, need_stats_report, need_bw_report, need_stats_report);
- }
- }
-#endif
-
- ++counter;
-
- return MediaPacket(data, mctrl.srctime);
-}
-
-SrtTarget::SrtTarget(std::string host, int port, std::string path, const std::map<std::string,std::string>& par)
-{
- Init(host, port, path, par, SRT_EPOLL_OUT);
-}
-
-
-int SrtTarget::ConfigurePre(SRTSOCKET sock)
-{
- int result = SrtCommon::ConfigurePre(sock);
- if (result == -1)
- return result;
-
- int yes = 1;
- // This is for the HSv4 compatibility; if both parties are HSv5
- // (min. version 1.2.1), then this setting simply does nothing.
- // In HSv4 this setting is obligatory; otherwise the SRT handshake
- // extension will not be done at all.
- result = srt_setsockopt(sock, 0, SRTO_SENDER, &yes, sizeof yes);
- if (result == -1)
- return result;
-
- return 0;
-}
-
-void SrtTarget::Write(const MediaPacket& data)
-{
- static int counter = 1;
- ::transmit_throw_on_interrupt = true;
-
- // Check first if it's ready to write.
- // If not, wait indefinitely.
- if (!m_blocking_mode)
- {
-Epoll_again:
- int len = 2;
- SRT_EPOLL_EVENT sready[2];
- len = srt_epoll_uwait(srt_epoll, sready, len, -1);
- if (len != -1)
- {
- bool any_write_ready = false;
- for (int i = 0; i < len; ++i)
- {
- if (sready[i].events & SRT_EPOLL_UPDATE)
- {
- Verb() << "... [BROKEN CONNECTION reported on @" << sready[i].fd << "]";
- }
-
- if (sready[i].events & SRT_EPOLL_OUT)
- any_write_ready = true;
- }
-
- if (!any_write_ready)
- {
- Verb() << " ... [NOT WRITE READY - AGAIN]";
- goto Epoll_again;
- }
-
- // Pass on, write ready.
- }
- else
- {
- Error("srt_epoll_uwait");
- }
- }
-
- SRT_MSGCTRL mctrl = srt_msgctrl_default;
-#if ENABLE_BONDING
- bool have_group = !m_group_nodes.empty();
- if (have_group || m_listener_group)
- {
- mctrl.grpdata = m_group_data.data();
- mctrl.grpdata_size = m_group_data.size();
- }
-#endif
-
- if (transmit_use_sourcetime)
- {
- mctrl.srctime = data.time;
- }
-
- int stat = srt_sendmsg2(m_sock, data.payload.data(), data.payload.size(), &mctrl);
-
- // For a socket group, the error is reported only
- // if ALL links from the group have failed to perform
- // the operation. If only one did, the result will be
- // visible in the status array.
- if (stat == SRT_ERROR)
- Error("srt_sendmsg");
- ::transmit_throw_on_interrupt = false;
-
- const bool need_bw_report = transmit_bw_report && int(counter % transmit_bw_report) == transmit_bw_report - 1;
- const bool need_stats_report = transmit_stats_report && counter % transmit_stats_report == transmit_stats_report - 1;
-
-#if ENABLE_BONDING
- if (have_group)
- {
- // For listener group this is not necessary. The group information
- // is updated in mctrl.
- UpdateGroupStatus(mctrl.grpdata, mctrl.grpdata_size);
- if (transmit_stats_writer && (need_stats_report || need_bw_report))
- {
- PrintSrtStats(m_sock, need_stats_report, need_bw_report, need_stats_report);
- for (size_t i = 0; i < mctrl.grpdata_size; ++i)
- PrintSrtStats(mctrl.grpdata[i].id, need_stats_report, need_bw_report, need_stats_report);
- }
- }
- else
-#endif
- {
- if (transmit_stats_writer && (need_stats_report || need_bw_report))
- {
- PrintSrtStats(m_sock, need_stats_report, need_bw_report, need_stats_report);
- }
- }
-
- Verb() << "(#" << mctrl.msgno << " %" << mctrl.pktseq << " " << BufferStamp(data.payload.data(), data.payload.size()) << ") " << VerbNoEOL;
-
- ++counter;
-}
-
-SrtRelay::SrtRelay(std::string host, int port, std::string path, const std::map<std::string,std::string>& par)
-{
- Init(host, port, path, par, SRT_EPOLL_IN | SRT_EPOLL_OUT);
-}
-
-SrtModel::SrtModel(string host, int port, map<string,string> par)
-{
- InitParameters(host, "", par);
- if (m_mode == "caller")
- is_caller = true;
- else if (m_mode == "rendezvous")
- is_rend = true;
- else if (m_mode != "listener")
- throw std::invalid_argument("Wrong 'mode' attribute; expected: caller, listener, rendezvous");
-
- m_host = host;
- m_port = port;
-}
-
-void SrtModel::Establish(std::string& w_name)
-{
- // This does connect or accept.
- // When this returned true, the caller should create
- // a new SrtSource or SrtTaget then call StealFrom(*this) on it.
-
- // If this is a connector and the peer doesn't have a corresponding
- // medium, it should send back a single byte with value 0. This means
- // that agent should stop connecting.
-
- if (is_rend)
- {
- OpenRendezvous(m_adapter, m_host, m_port);
- }
- else if (is_caller)
- {
- // Establish a connection
-
- PrepareClient();
-
- if (w_name != "")
- {
- Verb() << "Connect with requesting stream [" << w_name << "]";
- srt::setstreamid(m_sock, w_name);
- }
- else
- {
- Verb() << "NO STREAM ID for SRT connection";
- }
-
- if (m_outgoing_port || m_adapter != "")
- {
- Verb() << "Setting outgoing port: " << m_outgoing_port << " adapter:" << m_adapter;
- SetupAdapter(m_adapter, m_outgoing_port);
- }
-
- ConnectClient(m_host, m_port);
-
- if (m_outgoing_port == 0)
- {
- // Must rely on a randomly selected one. Extract the port
- // so that it will be reused next time.
- sockaddr_any s(AF_INET);
- int namelen = s.size();
- if (srt_getsockname(Socket(), (s.get()), (&namelen)) == SRT_ERROR)
- {
- Error("srt_getsockname");
- }
-
- m_outgoing_port = s.hport();
- Verb() << "Extracted outgoing port: " << m_outgoing_port;
- }
- }
- else
- {
- // Listener - get a socket by accepting.
- // Check if the listener is already created first
- if (Listener() == SRT_INVALID_SOCK)
- {
- Verb() << "Setting up listener: port=" << m_port << " backlog=5";
- PrepareListener(m_adapter, m_port, 5);
- }
-
- Verb() << "Accepting a client...";
- AcceptNewClient();
- // This rewrites m_sock with a new SRT socket ("accepted" socket)
- w_name = UDT::getstreamid(m_sock);
- Verb() << "... GOT CLIENT for stream [" << w_name << "]";
- }
-}
-
-
-template <class Iface> struct Srt;
-template <> struct Srt<Source> { typedef SrtSource type; };
-template <> struct Srt<Target> { typedef SrtTarget type; };
-template <> struct Srt<Relay> { typedef SrtRelay type; };
-
-template <class Iface>
-Iface* CreateSrt(const string& host, int port, std::string path, const map<string,string>& par)
-{ return new typename Srt<Iface>::type (host, port, path, par); }
-
-MediaPacket ConsoleRead(size_t chunk)
-{
- bytevector data(chunk);
- bool st = cin.read(data.data(), chunk).good();
- chunk = cin.gcount();
- if (chunk == 0 && !st)
- return bytevector();
-
- int64_t stime = 0;
- if (transmit_use_sourcetime)
- stime = srt_time_now();
-
- if (chunk < data.size())
- data.resize(chunk);
- if (data.empty())
- throw Source::ReadEOF("CONSOLE device");
-
- return MediaPacket(data, stime);
-}
-
-class ConsoleSource: public virtual Source
-{
-public:
-
- ConsoleSource()
- {
- }
-
- MediaPacket Read(size_t chunk) override
- {
- return ConsoleRead(chunk);
- }
-
- bool IsOpen() override { return cin.good(); }
- bool End() override { return cin.eof(); }
-};
-
-class ConsoleTarget: public virtual Target
-{
-public:
-
- ConsoleTarget()
- {
- }
-
- void Write(const MediaPacket& data) override
- {
- cout.write(data.payload.data(), data.payload.size());
- }
-
- bool IsOpen() override { return cout.good(); }
- bool Broken() override { return cout.eof(); }
-};
-
-class ConsoleRelay: public Relay, public ConsoleSource, public ConsoleTarget
-{
-public:
- ConsoleRelay() = default;
-
- bool IsOpen() override { return cin.good() && cout.good(); }
-};
-
-template <class Iface> struct Console;
-template <> struct Console<Source> { typedef ConsoleSource type; };
-template <> struct Console<Target> { typedef ConsoleTarget type; };
-template <> struct Console<Relay> { typedef ConsoleRelay type; };
-
-template <class Iface>
-Iface* CreateConsole() { return new typename Console<Iface>::type (); }
-
-
-// More options can be added in future.
-SocketOption udp_options [] {
- { "iptos", IPPROTO_IP, IP_TOS, SocketOption::PRE, SocketOption::INT, nullptr },
- // IP_TTL and IP_MULTICAST_TTL are handled separately by a common option, "ttl".
- { "mcloop", IPPROTO_IP, IP_MULTICAST_LOOP, SocketOption::PRE, SocketOption::INT, nullptr }
-};
-
-static inline bool IsMulticast(in_addr adr)
-{
- unsigned char* abytes = (unsigned char*)&adr.s_addr;
- unsigned char c = abytes[0];
- return c >= 224 && c <= 239;
-}
-
-void UdpCommon::Setup(string host, int port, map<string,string> attr)
-{
- m_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
- if (m_sock == -1)
- Error(SysError(), "UdpCommon::Setup: socket");
-
- int yes = 1;
- ::setsockopt(m_sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&yes, sizeof yes);
-
- sadr = CreateAddr(host, port);
-
- bool is_multicast = false;
- if (sadr.family() == AF_INET)
- {
- if (attr.count("multicast"))
- {
- if (!IsMulticast(sadr.sin.sin_addr))
- {
- throw std::runtime_error("UdpCommon: requested multicast for a non-multicast-type IP address");
- }
- is_multicast = true;
- }
- else if (IsMulticast(sadr.sin.sin_addr))
- {
- is_multicast = true;
- }
-
- if (is_multicast)
- {
- ip_mreq_source mreq_ssm;
- ip_mreq mreq;
- sockaddr_any maddr;
- int opt_name;
- void* mreq_arg_ptr;
- socklen_t mreq_arg_size;
-
- adapter = attr.count("adapter") ? attr.at("adapter") : string();
- if (adapter == "")
- {
- Verb() << "Multicast: home address: INADDR_ANY:" << port;
- maddr.sin.sin_family = AF_INET;
- maddr.sin.sin_addr.s_addr = htonl(INADDR_ANY);
- maddr.sin.sin_port = htons(port); // necessary for temporary use
- }
- else
- {
- Verb() << "Multicast: home address: " << adapter << ":" << port;
- maddr = CreateAddr(adapter, port);
- }
-
- if (attr.count("source"))
- {
- /* this is an ssm. we need to use the right struct and opt */
- opt_name = IP_ADD_SOURCE_MEMBERSHIP;
- mreq_ssm.imr_multiaddr.s_addr = sadr.sin.sin_addr.s_addr;
- mreq_ssm.imr_interface.s_addr = maddr.sin.sin_addr.s_addr;
- inet_pton(AF_INET, attr.at("source").c_str(), &mreq_ssm.imr_sourceaddr);
- mreq_arg_size = sizeof(mreq_ssm);
- mreq_arg_ptr = &mreq_ssm;
- }
- else
- {
- opt_name = IP_ADD_MEMBERSHIP;
- mreq.imr_multiaddr.s_addr = sadr.sin.sin_addr.s_addr;
- mreq.imr_interface.s_addr = maddr.sin.sin_addr.s_addr;
- mreq_arg_size = sizeof(mreq);
- mreq_arg_ptr = &mreq;
- }
-
-#ifdef _WIN32
- const char* mreq_arg = (const char*)mreq_arg_ptr;
- const auto status_error = SOCKET_ERROR;
-#else
- const void* mreq_arg = mreq_arg_ptr;
- const auto status_error = -1;
-#endif
-
-#if defined(_WIN32) || defined(__CYGWIN__)
- // On Windows it somehow doesn't work when bind()
- // is called with multicast address. Write the address
- // that designates the network device here.
- // Also, sets port sharing when working with multicast
- sadr = maddr;
- int reuse = 1;
- int shareAddrRes = setsockopt(m_sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<const char*>(&reuse), sizeof(reuse));
- if (shareAddrRes == status_error)
- {
- throw runtime_error("marking socket for shared use failed");
- }
- Verb() << "Multicast(Windows): will bind to home address";
-#else
- Verb() << "Multicast(POSIX): will bind to IGMP address: " << host;
-#endif
- int res = setsockopt(m_sock, IPPROTO_IP, opt_name, mreq_arg, mreq_arg_size);
-
- if (res == status_error)
- {
- Error(errno, "adding to multicast membership failed");
- }
-
- attr.erase("multicast");
- attr.erase("adapter");
- }
- }
-
- // The "ttl" options is handled separately, it maps to both IP_TTL
- // and IP_MULTICAST_TTL so that TTL setting works for both uni- and multicast.
- if (attr.count("ttl"))
- {
- int ttl = stoi(attr.at("ttl"));
- int res = setsockopt(m_sock, IPPROTO_IP, IP_TTL, (const char*)&ttl, sizeof ttl);
- if (res == -1)
- Verb() << "WARNING: failed to set 'ttl' (IP_TTL) to " << ttl;
- res = setsockopt(m_sock, IPPROTO_IP, IP_MULTICAST_TTL, (const char*)&ttl, sizeof ttl);
- if (res == -1)
- Verb() << "WARNING: failed to set 'ttl' (IP_MULTICAST_TTL) to " << ttl;
-
- attr.erase("ttl");
- }
-
- m_options = attr;
-
- for (auto o: udp_options)
- {
- // Ignore "binding" - for UDP there are no post options.
- if (m_options.count(o.name))
- {
- string value = m_options.at(o.name);
- bool ok = o.apply<SocketOption::SYSTEM>(m_sock, value);
- if (!ok)
- Verb() << "WARNING: failed to set '" << o.name << "' to " << value;
- }
- }
-}
-
-void UdpCommon::Error(int err, string src)
-{
- char buf[512];
- string message = SysStrError(err, buf, 512u);
-
- if (Verbose::on)
- Verb() << "FAILURE\n" << src << ": [" << err << "] " << message;
- else
- cerr << "\nERROR #" << err << ": " << message << endl;
-
- throw TransmissionError("error: " + src + ": " + message);
-}
-
-UdpCommon::~UdpCommon()
-{
-#ifdef _WIN32
- if (m_sock != -1)
- {
- shutdown(m_sock, SD_BOTH);
- closesocket(m_sock);
- m_sock = -1;
- }
-#else
- close(m_sock);
-#endif
-}
-
-UdpSource::UdpSource(string host, int port, const map<string,string>& attr)
-{
- Setup(host, port, attr);
- int stat = ::bind(m_sock, sadr.get(), sadr.size());
- if (stat == -1)
- Error(SysError(), "Binding address for UDP");
- eof = false;
- struct timeval tv;
- tv.tv_sec = 1;
- tv.tv_usec = 0;
- if (::setsockopt(m_sock, SOL_SOCKET, SO_RCVTIMEO, (const char*) &tv, sizeof(tv)) < 0)
- Error(SysError(), "Setting timeout for UDP");
-}
-
-MediaPacket UdpSource::Read(size_t chunk)
-{
- bytevector data(chunk);
- sockaddr_any sa(sadr.family());
- int64_t srctime = 0;
-AGAIN:
- int stat = recvfrom(m_sock, data.data(), (int) chunk, 0, sa.get(), &sa.syslen());
- int err = SysError();
- if (transmit_use_sourcetime)
- {
- srctime = srt_time_now();
- }
- if (stat == -1)
- {
- if (!::transmit_int_state && err == SysAGAIN)
- goto AGAIN;
-
- Error(SysError(), "UDP Read/recvfrom");
- }
-
- if (stat < 1)
- {
- eof = true;
- return bytevector();
- }
-
- chunk = size_t(stat);
- if (chunk < data.size())
- data.resize(chunk);
-
- return MediaPacket(data, srctime);
-}
-
-UdpTarget::UdpTarget(string host, int port, const map<string,string>& attr)
-{
- Setup(host, port, attr);
- if (adapter != "")
- {
- auto maddr = CreateAddr(adapter, 0);
- in_addr addr = maddr.sin.sin_addr;
-
- int res = setsockopt(m_sock, IPPROTO_IP, IP_MULTICAST_IF, reinterpret_cast<const char*>(&addr), sizeof(addr));
- if (res == -1)
- {
- Error(SysError(), "setsockopt/IP_MULTICAST_IF: " + adapter);
- }
- }
-}
-
-void UdpTarget::Write(const MediaPacket& data)
-{
- int stat = sendto(m_sock, data.payload.data(), data.payload.size(), 0, (sockaddr*)&sadr, sizeof sadr);
- if (stat == -1)
- Error(SysError(), "UDP Write/sendto");
-}
-
-
-template <class Iface> struct Udp;
-template <> struct Udp<Source> { typedef UdpSource type; };
-template <> struct Udp<Target> { typedef UdpTarget type; };
-template <> struct Udp<Relay> { typedef UdpRelay type; };
-
-template <class Iface>
-Iface* CreateUdp(const string& host, int port, const map<string,string>& par) { return new typename Udp<Iface>::type (host, port, par); }
-
-template<class Base>
-inline bool IsOutput() { return false; }
-
-template<>
-inline bool IsOutput<Target>() { return true; }
-
-template <class Base>
-extern unique_ptr<Base> CreateMedium(const string& uri)
-{
- unique_ptr<Base> ptr;
-
- UriParser u(uri);
-
- int iport = 0;
- switch ( u.type() )
- {
- default:
- break; // do nothing, return nullptr
- case UriParser::FILE:
- if (u.host() == "con" || u.host() == "console")
- {
- if ( IsOutput<Base>() && (
- (Verbose::on && Verbose::cverb == &cout)
- || transmit_bw_report || transmit_stats_report) )
- {
- cerr << "ERROR: file://con with -v or -r or -s would result in mixing the data and text info.\n";
- cerr << "ERROR: HINT: you can stream through a FIFO (named pipe)\n";
- throw invalid_argument("incorrect parameter combination");
- }
- ptr.reset( CreateConsole<Base>() );
- }
- else
- ptr.reset( CreateFile<Base>(u.path()));
- break;
-
- case UriParser::SRT:
- ptr.reset( CreateSrt<Base>(u.host(), u.portno(), u.path(), u.parameters()) );
- break;
-
-
- case UriParser::UDP:
- iport = atoi(u.port().c_str());
- if (iport < 1024)
- {
- cerr << "Port value invalid: " << iport << " - must be >=1024\n";
- throw invalid_argument("Invalid port number");
- }
- ptr.reset( CreateUdp<Base>(u.host(), iport, u.parameters()) );
- break;
- }
-
- if (ptr)
- ptr->uri = move(u);
- return ptr;
-}
-
-
-std::unique_ptr<Source> Source::Create(const std::string& url)
-{
- return CreateMedium<Source>(url);
-}
-
-std::unique_ptr<Target> Target::Create(const std::string& url)
-{
- return CreateMedium<Target>(url);
-}
+++ /dev/null
-/*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2018 Haivision Systems Inc.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- */
-
-#ifndef INC_SRT_COMMON_TRANSMITMEDIA_HPP
-#define INC_SRT_COMMON_TRANSMITMEDIA_HPP
-
-#include <string>
-#include <map>
-#include <stdexcept>
-#include <deque>
-#include <atomic>
-
-#include "apputil.hpp"
-#include "statswriter.hpp"
-#include "testmediabase.hpp"
-#include <udt.h> // Needs access to CUDTException
-#include <netinet_any.h>
-
-extern srt_listen_callback_fn* transmit_accept_hook_fn;
-extern void* transmit_accept_hook_op;
-extern std::atomic<bool> transmit_int_state;
-
-extern std::shared_ptr<SrtStatsWriter> transmit_stats_writer;
-
-using namespace std;
-
-const srt_logging::LogFA SRT_LOGFA_APP = 10;
-extern srt_logging::Logger applog;
-
-// Trial version of an exception. Try to implement later an official
-// interruption mechanism in SRT using this.
-
-struct TransmissionError: public std::runtime_error
-{
- TransmissionError(const std::string& arg):
- std::runtime_error(arg)
- {
- }
-};
-
-class SrtCommon
-{
- int srt_conn_epoll = -1;
-
- void SpinWaitAsync();
-
-protected:
-
- friend void TransmitGroupSocketConnect(void* srtcommon, SRTSOCKET sock, int error, const sockaddr* peer, int token);
-
- struct ConnectionBase
- {
- string host;
- int port;
- int weight = 0;
- SRTSOCKET socket = SRT_INVALID_SOCK;
- srt::sockaddr_any source;
- srt::sockaddr_any target;
- int token = -1;
-
- ConnectionBase(string h, int p): host(h), port(p), source(AF_INET) {}
- };
-
- struct Connection: ConnectionBase
- {
-#if ENABLE_BONDING
- SRT_SOCKOPT_CONFIG* options = nullptr;
-#endif
- int error = SRT_SUCCESS;
- int reason = SRT_REJ_UNKNOWN;
-
- Connection(string h, int p): ConnectionBase(h, p) {}
- Connection(Connection&& old): ConnectionBase(old)
- {
-#if ENABLE_BONDING
- if (old.options)
- {
- options = old.options;
- old.options = nullptr;
- }
-#endif
- }
- ~Connection()
- {
-#if ENABLE_BONDING
- srt_delete_config(options);
-#endif
- }
- };
-
- int srt_epoll = -1;
- SRT_EPOLL_T m_direction = SRT_EPOLL_OPT_NONE; //< Defines which of SND or RCV option variant should be used, also to set SRT_SENDER for output
- bool m_blocking_mode = true; //< enforces using SRTO_SNDSYN or SRTO_RCVSYN, depending on @a m_direction
- int m_timeout = 0; //< enforces using SRTO_SNDTIMEO or SRTO_RCVTIMEO, depending on @a m_direction
- bool m_tsbpdmode = true;
- int m_outgoing_port = 0;
- string m_mode;
- string m_adapter;
- map<string, string> m_options; // All other options, as provided in the URI
- vector<Connection> m_group_nodes;
- string m_group_type;
- string m_group_config;
-#if ENABLE_BONDING
- vector<SRT_SOCKGROUPDATA> m_group_data;
-#ifdef SRT_OLD_APP_READER
- int32_t m_group_seqno = -1;
-
- struct ReadPos
- {
- int32_t sequence;
- bytevector packet;
- };
- map<SRTSOCKET, ReadPos> m_group_positions;
- SRTSOCKET m_group_active; // The link from which the last packet was delivered
-#endif
-#endif
-
- SRTSOCKET m_sock = SRT_INVALID_SOCK;
- SRTSOCKET m_bindsock = SRT_INVALID_SOCK;
- bool m_listener_group = false;
- bool IsUsable() { SRT_SOCKSTATUS st = srt_getsockstate(m_sock); return st > SRTS_INIT && st < SRTS_BROKEN; }
- bool IsBroken() { return srt_getsockstate(m_sock) > SRTS_CONNECTED; }
-
- void UpdateGroupStatus(const SRT_SOCKGROUPDATA* grpdata, size_t grpdata_size);
-
-public:
- void InitParameters(string host, string path, map<string,string> par);
- void PrepareListener(string host, int port, int backlog);
- void StealFrom(SrtCommon& src);
- void AcceptNewClient();
-
- SRTSOCKET Socket() const { return m_sock; }
- SRTSOCKET Listener() const { return m_bindsock; }
-
- void Acquire(SRTSOCKET s)
- {
- m_sock = s;
- if (s & SRTGROUP_MASK)
- m_listener_group = true;
- }
-
- virtual void Close();
-
-protected:
-
- void Error(string src, int reason = SRT_REJ_UNKNOWN, int force_result = 0);
- void Init(string host, int port, string path, map<string,string> par, SRT_EPOLL_OPT dir);
- int AddPoller(SRTSOCKET socket, int modes);
- virtual int ConfigurePost(SRTSOCKET sock);
- virtual int ConfigurePre(SRTSOCKET sock);
-
- void OpenClient(string host, int port);
-#if ENABLE_BONDING
- void OpenGroupClient();
-#endif
- void PrepareClient();
- void SetupAdapter(const std::string& host, int port);
- void ConnectClient(string host, int port);
- void SetupRendezvous(string adapter, string host, int port);
-
- void OpenServer(string host, int port, int backlog = 1)
- {
- PrepareListener(host, port, backlog);
- if (transmit_accept_hook_fn)
- {
- srt_listen_callback(m_bindsock, transmit_accept_hook_fn, transmit_accept_hook_op);
- }
- AcceptNewClient();
- }
-
- void OpenRendezvous(string adapter, string host, int port)
- {
- PrepareClient();
- SetupRendezvous(adapter, host, port);
- ConnectClient(host, port);
- }
-
- virtual ~SrtCommon();
-};
-
-
-class SrtSource: public virtual Source, public virtual SrtCommon
-{
- std::string hostport_copy;
-public:
-
- SrtSource(std::string host, int port, std::string path, const std::map<std::string,std::string>& par);
- SrtSource()
- {
- // Do nothing - create just to prepare for use
- }
-
- MediaPacket Read(size_t chunk) override;
- bytevector GroupRead(size_t chunk);
- bool GroupCheckPacketAhead(bytevector& output);
-
-
- /*
- In this form this isn't needed.
- Unblock if any extra settings have to be made.
- virtual int ConfigurePre(UDTSOCKET sock) override
- {
- int result = SrtCommon::ConfigurePre(sock);
- if ( result == -1 )
- return result;
- return 0;
- }
- */
-
- bool IsOpen() override { return IsUsable(); }
- bool End() override { return IsBroken(); }
- void Close() override { return SrtCommon::Close(); }
-};
-
-class SrtTarget: public virtual Target, public virtual SrtCommon
-{
-public:
-
- SrtTarget(std::string host, int port, std::string path, const std::map<std::string,std::string>& par);
- SrtTarget() {}
-
- int ConfigurePre(SRTSOCKET sock) override;
- void Write(const MediaPacket& data) override;
- bool IsOpen() override { return IsUsable(); }
- bool Broken() override { return IsBroken(); }
- void Close() override { return SrtCommon::Close(); }
-
- size_t Still() override
- {
- size_t bytes;
- int st = srt_getsndbuffer(m_sock, nullptr, &bytes);
- if (st == -1)
- return 0;
- return bytes;
- }
-
-};
-
-class SrtRelay: public Relay, public SrtSource, public SrtTarget
-{
-public:
- SrtRelay(std::string host, int port, std::string path, const std::map<std::string,std::string>& par);
- SrtRelay() {}
-
- int ConfigurePre(SRTSOCKET sock) override
- {
- // This overrides the change introduced in SrtTarget,
- // which sets the SRTO_SENDER flag. For a bidirectional transmission
- // this flag should not be set, as the connection should be checked
- // for being 1.3.0 clients only.
- return SrtCommon::ConfigurePre(sock);
- }
-
- // Override separately overridden methods by SrtSource and SrtTarget
- bool IsOpen() override { return IsUsable(); }
- void Close() override { return SrtCommon::Close(); }
-};
-
-
-// This class is used when we don't know yet whether the given URI
-// designates an effective listener or caller. So we create it, initialize,
-// then we know what mode we'll be using.
-//
-// When caller, then we will do connect() using this object, then clone out
-// a new object - of a direction specific class - which will steal the socket
-// from this one and then roll the data. After this, this object is ready
-// to connect again, and will create its own socket for that occasion, and
-// the whole procedure repeats.
-//
-// When listener, then this object will be doing accept() and with every
-// successful acceptation it will clone out a new object - of a direction
-// specific class - which will steal just the connection socket from this
-// object. This object will still live on and accept new connections and
-// so on.
-class SrtModel: public SrtCommon
-{
-public:
- bool is_caller = false;
- bool is_rend = false;
- string m_host;
- int m_port = 0;
-
-
- SrtModel(string host, int port, map<string,string> par);
- void Establish(std::string& w_name);
-
- void Close()
- {
- if (m_sock != SRT_INVALID_SOCK)
- {
- srt_close(m_sock);
- m_sock = SRT_INVALID_SOCK;
- }
- }
-};
-
-class UdpCommon
-{
-protected:
- int m_sock = -1;
- srt::sockaddr_any sadr;
- std::string adapter;
- std::map<std::string, std::string> m_options;
- void Setup(std::string host, int port, std::map<std::string,std::string> attr);
- void Error(int err, std::string src);
-
- ~UdpCommon();
-};
-
-
-class UdpSource: public virtual Source, public virtual UdpCommon
-{
- bool eof = true;
-public:
-
- UdpSource(string host, int port, const map<string,string>& attr);
-
- MediaPacket Read(size_t chunk) override;
-
- bool IsOpen() override { return m_sock != -1; }
- bool End() override { return eof; }
-};
-
-class UdpTarget: public virtual Target, public virtual UdpCommon
-{
-public:
- UdpTarget(string host, int port, const map<string,string>& attr);
-
- void Write(const MediaPacket& data) override;
- bool IsOpen() override { return m_sock != -1; }
- bool Broken() override { return false; }
-};
-
-class UdpRelay: public Relay, public UdpSource, public UdpTarget
-{
-public:
- UdpRelay(string host, int port, const map<string,string>& attr):
- UdpSource(host, port, attr),
- UdpTarget(host, port, attr)
- {
- }
-
- bool IsOpen() override { return m_sock != -1; }
-};
-
-
-
-#endif
+++ /dev/null
-/*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2018 Haivision Systems Inc.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- */
-
-#ifndef INC_SRT_COMMON_TRANMITBASE_HPP
-#define INC_SRT_COMMON_TRANMITBASE_HPP
-
-#include <atomic>
-#include <string>
-#include <memory>
-#include <vector>
-#include <iostream>
-#include <stdexcept>
-
-#include "uriparser.hpp"
-
-typedef std::vector<char> bytevector;
-extern std::atomic<bool> transmit_throw_on_interrupt;
-extern int transmit_bw_report;
-extern unsigned transmit_stats_report;
-extern size_t transmit_chunk_size;
-extern bool transmit_printformat_json;
-extern bool transmit_use_sourcetime;
-extern int transmit_retry_connect;
-extern bool transmit_retry_always;
-
-struct MediaPacket
-{
- bytevector payload;
- int64_t time = 0;
-
- MediaPacket(bytevector&& src): payload(std::move(src)) {}
- MediaPacket(bytevector&& src, int64_t stime): payload(std::move(src)), time(stime) {}
-
- MediaPacket(const bytevector& src): payload(src) {}
- MediaPacket(const bytevector& src, int64_t stime): payload(src), time(stime) {}
- MediaPacket() {}
-};
-
-
-class Location
-{
-public:
- UriParser uri;
- Location() {}
- virtual bool IsOpen() = 0;
- virtual void Close() {}
-};
-
-class Source: public virtual Location
-{
-public:
- virtual MediaPacket Read(size_t chunk) = 0;
- virtual bool End() = 0;
- static std::unique_ptr<Source> Create(const std::string& url);
- virtual ~Source() {}
-
- class ReadEOF: public std::runtime_error
- {
- public:
- ReadEOF(const std::string& fn): std::runtime_error( "EOF while reading file: " + fn )
- {
- }
- };
-};
-
-class Target: public virtual Location
-{
-public:
- virtual void Write(const MediaPacket& portion) = 0;
- virtual bool Broken() = 0;
- virtual size_t Still() { return 0; }
- static std::unique_ptr<Target> Create(const std::string& url);
- virtual ~Target() {}
-};
-
-
-class Relay: public virtual Source, public virtual Target, public virtual Location
-{
-public:
- static std::unique_ptr<Relay> Create(const std::string& url);
- virtual ~Relay() {}
-};
-
-
-#endif
+++ /dev/null
-
-SOURCES
-../apps/uriparser.cpp
+++ /dev/null
-/*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2018 Haivision Systems Inc.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- */
-
-#include <iostream>
-#include <string>
-#include <iomanip>
-
-#include <udt.h>
-#include <utilities.h>
-#include <packet.h>
-#include <crypto.h>
-#include <common.h>
-
-using namespace srt;
-
-void ShowDistance(int32_t s1, int32_t s2)
-{
- using namespace std;
-
- cout << "s1=" << s1 << "s2=" << s2 << " DISTANCE:\n";
- cout << "seqcmp -> " << CSeqNo::seqcmp(s1, s2) << endl;
- cout << "seqoff -> " << CSeqNo::seqoff(s2, s1) << endl;
-}
-
-int main()
-{
- using namespace std;
-
- cout << "PacketBoundary: " << hex << MSGNO_PACKET_BOUNDARY::mask << endl;
-
- cout << "PB_FIRST: " << hex << PacketBoundaryBits(PB_FIRST) << endl;
- cout << "PB_LAST: " << hex << PacketBoundaryBits(PB_LAST) << endl;
- cout << "PB_SOLO: " << hex << PacketBoundaryBits(PB_SOLO) << endl;
-
- cout << "inorder: " << hex << MSGNO_PACKET_INORDER::mask << " (1 << " << dec << MSGNO_PACKET_INORDER::offset << ")" << endl;
- cout << "msgno-seq mask: " << hex << MSGNO_SEQ::mask << endl;
- cout << "3 wrapped into enckeyspec: " << hex << setw(8) << setfill('0') << MSGNO_ENCKEYSPEC::wrap(3) << " - mask: " << MSGNO_ENCKEYSPEC::mask << endl;
-
- cout << "SrtVersion test: 2.3.8 == 0x020308 -- SrtVersion(2, 3, 8) == 0x" << hex << setw(8) << setfill('0') << SrtVersion(2, 3, 8) << endl;
-
- cout << "SEQNO_CONTROL::mask: " << hex << SEQNO_CONTROL::mask << " SEQNO 0x80050000 has control = " << SEQNO_CONTROL::unwrap(0x80050000)
- << " type = " << SEQNO_MSGTYPE::unwrap(0x80050000) << endl;
-
- cout << "Creating array of bytes: 10, 11, 20, 25 - FormatBinaryString: ";
- uint8_t array[4] = { 10, 11, 20, 25 };
- cout << FormatBinaryString(array, 4) << endl;
-
- cout << "-------------------------------\n";
- cout << "SEQUENCES:\n";
- int32_t s1 = 100, s2 = 200;
- ShowDistance(s1, s2);
-
- cout << "GO BACK BY -150:\n";
- s1 = CSeqNo::decseq(s1, 150);
- s2 = CSeqNo::decseq(s2, 150);
- ShowDistance(s1, s2);
-
- return 0;
-}
+++ /dev/null
-
-SOURCES
-utility-test.cpp