#include <cstring>
#include <chrono>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <utility>
#include <memory>
#include "apputil.hpp"
#include "netinet_any.h"
#include "srt_compat.h"
using namespace std;
#if defined(_WIN32) && !defined(HAVE_INET_PTON)
namespace {
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));
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 :
{
}
}
return 0;
}
}
#endif
sockaddr_any CreateAddr(const string& name, unsigned short port, int pref_family)
{
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);
if (inet_pton(family, name.c_str(), result.get_addr()) == 1)
{
result.hport(port); return result;
}
}
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); }
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();
}
OptionScheme::Args OptionName::DetermineTypeFromHelpText(const std::string& helptext)
{
if (helptext.empty())
return OptionScheme::ARG_NONE;
if (helptext[0] == '<')
{
size_t pos = helptext.find('>');
if (pos == std::string::npos)
return OptionScheme::ARG_ONE;
if (pos >= 4 && helptext.substr(pos-3, 4) == "...>")
return OptionScheme::ARG_VAR;
return OptionScheme::ARG_ONE;
}
if (helptext[0] == '[')
{
return OptionScheme::ARG_VAR;
}
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; map<string, vector<string>> params;
bool moreoptions = true;
for (char* const* p = argv+1; p != argv+argc; ++p)
{
const char* a = *p;
if (moreoptions && a[0] == '-')
{
bool arg_specified = false;
size_t seppos; current_key = a+1;
if ( current_key == "-" )
{
moreoptions = false;
goto EndOfArgs;
}
seppos = current_key.find(':');
if (seppos == string::npos)
seppos = current_key.find(' ');
if (seppos != string::npos)
{
extra_arg = current_key.substr(seppos + 1);
current_key = current_key.substr(0, 0 + seppos);
arg_specified = true; }
params[current_key].clear();
vals = 0;
if (extra_arg != "")
{
params[current_key].push_back(extra_arg);
++vals;
extra_arg.clear();
}
for (auto s: scheme)
{
if (s.names().count(current_key))
{
if (s.type == OptionScheme::ARG_NONE || arg_specified)
{
break;
}
type = s.type;
if ( vals == 1 && type == OptionScheme::ARG_ONE )
{
goto EndOfArgs;
}
goto Found;
}
}
EndOfArgs:
type = OptionScheme::ARG_VAR;
current_key = "";
Found:
continue;
}
params[current_key].push_back(a);
++vals;
if ( vals == 1 && type == OptionScheme::ARG_ONE )
{
current_key = "";
vals = 0;
type = OptionScheme::ARG_VAR;
}
else
{
}
}
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);
hlp = hlp.substr(end);
out += " " + prefix;
}
}
out += " -" + hlp;
return out;
}
class SrtStatsJson : public SrtStatsWriter
{
public:
string WriteStats(int sid, const CBytePerfMon& mon) override
{
std::ostringstream output;
output << "{";
output << "\"sid\":" << sid << ",";
output << "\"time\":" << mon.msTimeStamp << ",";
output << "\"window\":{";
output << "\"flow\":" << mon.pktFlowWindow << ",";
output << "\"congestion\":" << mon.pktCongestionWindow << ",";
output << "\"flight\":" << mon.pktFlightSize;
output << "},";
output << "\"link\":{";
output << "\"rtt\":" << mon.msRTT << ",";
output << "\"bandwidth\":" << mon.mbpsBandwidth << ",";
output << "\"maxBandwidth\":" << mon.mbpsMaxBW;
output << "},";
output << "\"send\":{";
output << "\"packets\":" << mon.pktSent << ",";
output << "\"packetsUnique\":" << mon.pktSentUnique << ",";
output << "\"packetsLost\":" << mon.pktSndLoss << ",";
output << "\"packetsDropped\":" << mon.pktSndDrop << ",";
output << "\"packetsRetransmitted\":" << mon.pktRetrans << ",";
output << "\"packetsFilterExtra\":" << mon.pktSndFilterExtra << ",";
output << "\"bytes\":" << mon.byteSent << ",";
output << "\"bytesUnique\":" << mon.byteSentUnique << ",";
output << "\"bytesDropped\":" << mon.byteSndDrop << ",";
output << "\"mbitRate\":" << mon.mbpsSendRate;
output << "},";
output << "\"recv\": {";
output << "\"packets\":" << mon.pktRecv << ",";
output << "\"packetsUnique\":" << mon.pktRecvUnique << ",";
output << "\"packetsLost\":" << mon.pktRcvLoss << ",";
output << "\"packetsDropped\":" << mon.pktRcvDrop << ",";
output << "\"packetsRetransmitted\":" << mon.pktRcvRetrans << ",";
output << "\"packetsBelated\":" << mon.pktRcvBelated << ",";
output << "\"packetsFilterExtra\":" << mon.pktRcvFilterExtra << ",";
output << "\"packetsFilterSupply\":" << mon.pktRcvFilterSupply << ",";
output << "\"packetsFilterLoss\":" << mon.pktRcvFilterLoss << ",";
output << "\"bytes\":" << mon.byteRecv << ",";
output << "\"bytesUnique\":" << mon.byteRecvUnique << ",";
output << "\"bytesLost\":" << mon.byteRcvLoss << ",";
output << "\"bytesDropped\":" << mon.byteRcvDrop << ",";
output << "\"mbitRate\":" << mon.mbpsRecvRate;
output << "}";
output << "}" << 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
{
#if !defined(__GNUC__) || defined(__clang__) || (__GNUC__ >= 5)
#define HAS_PUT_TIME
#endif
std::ostringstream output;
if (!first_line_printed)
{
#ifdef HAS_PUT_TIME
output << "Timepoint,";
#endif
output << "Time,SocketID,pktFlowWindow,pktCongestionWindow,pktFlightSize,";
output << "msRTT,mbpsBandwidth,mbpsMaxBW,pktSent,pktSndLoss,pktSndDrop,";
output << "pktRetrans,byteSent,byteSndDrop,mbpsSendRate,usPktSndPeriod,";
output << "pktRecv,pktRcvLoss,pktRcvDrop,pktRcvRetrans,pktRcvBelated,";
output << "byteRecv,byteRcvLoss,byteRcvDrop,mbpsRecvRate,RCVLATENCYms,";
output << "pktSndFilterExtra,pktRcvFilterExtra,pktRcvFilterSupply,pktRcvFilterLoss";
output << endl;
first_line_printed = true;
}
int rcv_latency = 0;
int int_len = sizeof rcv_latency;
srt_getsockopt(sid, 0, SRTO_RCVLATENCY, &rcv_latency, &int_len);
#ifdef HAS_PUT_TIME
auto print_timestamp = [&output]() {
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);
const tm tm_now = SysLocalTime(time_now);
output << std::put_time(&tm_now, "%d.%m.%Y %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");
output << ",";
};
print_timestamp();
#endif
output << mon.msTimeStamp << ",";
output << sid << ",";
output << mon.pktFlowWindow << ",";
output << mon.pktCongestionWindow << ",";
output << mon.pktFlightSize << ",";
output << mon.msRTT << ",";
output << mon.mbpsBandwidth << ",";
output << mon.mbpsMaxBW << ",";
output << mon.pktSent << ",";
output << mon.pktSndLoss << ",";
output << mon.pktSndDrop << ",";
output << mon.pktRetrans << ",";
output << mon.byteSent << ",";
output << mon.byteSndDrop << ",";
output << mon.mbpsSendRate << ",";
output << mon.usPktSndPeriod << ",";
output << mon.pktRecv << ",";
output << mon.pktRcvLoss << ",";
output << mon.pktRcvDrop << ",";
output << mon.pktRcvRetrans << ",";
output << mon.pktRcvBelated << ",";
output << mon.byteRecv << ",";
output << mon.byteRcvLoss << ",";
output << mon.byteRcvDrop << ",";
output << mon.mbpsRecvRate << ",";
output << rcv_latency << ",";
output << mon.pktSndFilterExtra << ",";
output << mon.pktRcvFilterExtra << ",";
output << mon.pktRcvFilterSupply << ",";
output << mon.pktRcvFilterLoss; 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)
{
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;
}