diff --git a/lib/http_parser.cpp b/lib/http_parser.cpp index 46510b74..10897236 100644 --- a/lib/http_parser.cpp +++ b/lib/http_parser.cpp @@ -30,6 +30,11 @@ HTTP::URL::URL(const std::string & url){ size_t first_slash = url.find('/', proto_sep); if (first_slash != std::string::npos){ path = url.substr(first_slash+1); + size_t qmark = path.find('?'); + if (qmark != std::string::npos){ + args = path.substr(qmark+1); + path.erase(qmark); + } } //host and port are now definitely between proto_sep and first_slash //we check for [ at the start because we may have an IPv6 address as host @@ -73,6 +78,7 @@ HTTP::URL::URL(const std::string & url){ EXTREME_MSG("URL protocol: %s", protocol.c_str()); EXTREME_MSG("URL port: %s", port.c_str()); EXTREME_MSG("URL path: %s", path.c_str()); + EXTREME_MSG("URL args: %s", args.c_str()); } ///Returns the port in numeric format diff --git a/lib/http_parser.h b/lib/http_parser.h index f186f4f3..181ea5f5 100644 --- a/lib/http_parser.h +++ b/lib/http_parser.h @@ -77,7 +77,8 @@ namespace HTTP { std::string host;///< Hostname or IP address of URL std::string protocol;///ai_family; sock = socket(family, SOCK_DGRAM, 0); + HIGH_MSG("Set UDP destination: %s:%d (%s)", destIp.c_str(), port, addrFam(family)); freeaddrinfo(result); return; //\todo Possibly detect and handle failure @@ -1103,15 +1116,20 @@ void Socket::UDPConnection::SendNow(const char *sdata, size_t len){ /// \arg multicastInterfaces Comma-separated list of interfaces to listen on for multicast packets. Optional, left out means automatically chosen /// by kernel. /// \return Actually bound port number, or zero on error. -int Socket::UDPConnection::bind(int port, std::string iface, const std::string &multicastInterfaces){ +uint16_t Socket::UDPConnection::bind(int port, std::string iface, const std::string &multicastInterfaces){ close(); // we open a new socket for each attempt int result = 0; int addr_ret; bool multicast = false; struct addrinfo hints, *addr_result, *rp; memset(&hints, 0, sizeof(hints)); - hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG | AI_PASSIVE; - hints.ai_family = family; + hints.ai_flags = AI_ADDRCONFIG | AI_PASSIVE | AI_V4MAPPED; + if (destAddr && destAddr_size){ + hints.ai_family = ((struct sockaddr_in *)destAddr)->sin_family; + }else{ + hints.ai_family = AF_UNSPEC; + } + hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; @@ -1135,11 +1153,34 @@ int Socket::UDPConnection::bind(int port, std::string iface, const std::string & sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (sock == -1){continue;} char human_addr[INET6_ADDRSTRLEN]; - getnameinfo(rp->ai_addr, rp->ai_addrlen, human_addr, INET6_ADDRSTRLEN, 0, 0, NI_NUMERICHOST); - MEDIUM_MSG("Attempting bind to %s (%s)", human_addr, rp->ai_family == AF_INET6 ? "IPv6" : "IPv4"); + char human_port[16]; + getnameinfo(rp->ai_addr, rp->ai_addrlen, human_addr, INET6_ADDRSTRLEN, human_port, 16, NI_NUMERICHOST | NI_NUMERICSERV); + MEDIUM_MSG("Attempting bind to %s:%s (%s)", human_addr, human_port, addrFam(rp->ai_family)); + family = rp->ai_family; + hints.ai_family = family; + if (family == AF_INET6){ + sockaddr_in6 *addr6 = (sockaddr_in6 *)(rp->ai_addr); + result = ntohs(addr6->sin6_port); + if (memcmp((char *)&(addr6->sin6_addr), "\000\000\000\000\000\000\000\000\000\000\377\377", 12) == 0){ + // IPv6-mapped IPv4 address - 13th byte ([12]) holds the first IPv4 byte + multicast = (((char *)&(addr6->sin6_addr))[12] & 0xF0) == 0xE0; + }else{ + //"normal" IPv6 address - prefix ff00::/8 + multicast = (((char *)&(addr6->sin6_addr))[0] == 0xFF); + } + }else{ + sockaddr_in *addr4 = (sockaddr_in *)(rp->ai_addr); + result = ntohs(addr4->sin_port); + // multicast has a "1110" bit prefix + multicast = (((char *)&(addr4->sin_addr))[0] & 0xF0) == 0xE0; + } + if (multicast){ + const int optval = 1; + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) { + WARN_MSG("Could not set multicast UDP socket re-use!"); + } + } if (::bind(sock, rp->ai_addr, rp->ai_addrlen) == 0){ - family = rp->ai_family; - hints.ai_family = family; break; } if (err_str.size()){err_str += ", ";} @@ -1148,29 +1189,11 @@ int Socket::UDPConnection::bind(int port, std::string iface, const std::string & err_str += strerror(errno); close(); // we open a new socket for each attempt } + freeaddrinfo(addr_result); if (sock == -1){ FAIL_MSG("Could not open %s for UDP: %s", iface.c_str(), err_str.c_str()); - freeaddrinfo(addr_result); return 0; } - // socket is bound! Let's collect some more data... - if (family == AF_INET6){ - sockaddr_in6 *addr6 = (sockaddr_in6 *)(rp->ai_addr); - result = ntohs(addr6->sin6_port); - if (memcmp((char *)&(addr6->sin6_addr), "\000\000\000\000\000\000\000\000\000\000\377\377", 12) == 0){ - // IPv6-mapped IPv4 address - 13th byte ([12]) holds the first IPv4 byte - multicast = (((char *)&(addr6->sin6_addr))[12] & 0xF0) == 0xE0; - }else{ - //"normal" IPv6 address - prefix ff00::/8 - multicast = (((char *)&(addr6->sin6_addr))[0] == 0xFF); - } - }else{ - sockaddr_in *addr4 = (sockaddr_in *)(rp->ai_addr); - result = ntohs(addr4->sin_port); - // multicast has a "1110" bit prefix - multicast = (((char *)&(addr4->sin_addr))[0] & 0xF0) == 0xE0; - } - freeaddrinfo(addr_result); // handle multicast membership(s) if (multicast){ @@ -1182,8 +1205,7 @@ int Socket::UDPConnection::bind(int port, std::string iface, const std::string & if ((addr_ret = getaddrinfo(iface.c_str(), 0, &hints, &resmulti)) != 0){ WARN_MSG("Unable to parse multicast address: %s", gai_strerror(addr_ret)); close(); - result = -1; - return result; + return 0; } if (!multicastInterfaces.length()){ @@ -1254,7 +1276,18 @@ int Socket::UDPConnection::bind(int port, std::string iface, const std::string & } freeaddrinfo(resmulti); // free resolved multicast addr } - return result; + //get port number + struct sockaddr_storage fin_addr; + socklen_t alen = sizeof(fin_addr); + if (getsockname(sock, (struct sockaddr*)&fin_addr, &alen) == 0){ + if (family == AF_INET6){ + return ntohs(((struct sockaddr_in6*)&fin_addr)->sin6_port); + }else{ + return ntohs(((struct sockaddr_in*)&fin_addr)->sin_port); + } + }else{ + return 0; + } } /// Attempt to receive a UDP packet. diff --git a/lib/socket.h b/lib/socket.h index 0760346c..3f25a28d 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -142,7 +142,7 @@ namespace Socket{ ~UDPConnection(); void close(); int getSock(); - int bind(int port, std::string iface = "", const std::string &multicastAddress = ""); + uint16_t bind(int port, std::string iface = "", const std::string &multicastAddress = ""); void setBlocking(bool blocking); void SetDestination(std::string hostname, uint32_t port); void GetDestination(std::string &hostname, uint32_t &port); diff --git a/lib/ts_packet.cpp b/lib/ts_packet.cpp index aa3074fb..b041297f 100644 --- a/lib/ts_packet.cpp +++ b/lib/ts_packet.cpp @@ -787,6 +787,7 @@ namespace TS { case 0x15: return "meta PES"; case 0x16: return "meta section"; case 0x1B: return "H264"; + case 0x24: return "H265"; case 0x81: return "AC3"; default: return "unknown"; } @@ -798,6 +799,7 @@ namespace TS { case 0x02: case 0x09: case 0x10: + case 0x24: case 0x1B: return "video"; case 0x03: case 0x04: diff --git a/src/output/output.cpp b/src/output/output.cpp index 0d1cbc35..68ffe646 100644 --- a/src/output/output.cpp +++ b/src/output/output.cpp @@ -36,6 +36,13 @@ namespace Mist{ capa["optional"]["debug"]["help"] = "The debug level at which messages need to be printed."; capa["optional"]["debug"]["option"] = "--debug"; capa["optional"]["debug"]["type"] = "debug"; + + JSON::Value option; + option["long"] = "noinput"; + option["short"] = "N"; + option["help"] = "Do not start input if not already started"; + option["value"].append(0ll); + cfg->addOption("noinput", option); } Output::Output(Socket::Connection & conn) : myConn(conn){ @@ -51,6 +58,7 @@ namespace Mist{ maxSkipAhead = 7500; minSkipAhead = 5000; realTime = 1000; + lastRecv = Util::epoch(); if (myConn){ setBlocking(true); }else{ @@ -288,10 +296,19 @@ namespace Mist{ /// Finally, calls updateMeta() void Output::reconnect(){ thisPacket.null(); - if (!Util::startInput(streamName)){ - FAIL_MSG("Opening stream %s failed - aborting initialization", streamName.c_str()); - onFail(); - return; + if (config->hasOption("noinput") && config->getBool("noinput")){ + Util::sanitizeName(streamName); + if (!Util::streamAlive(streamName)){ + FAIL_MSG("Stream %s not already active - aborting initialization", streamName.c_str()); + onFail(); + return; + } + }else{ + if (!Util::startInput(streamName)){ + FAIL_MSG("Opening stream %s failed - aborting initialization", streamName.c_str()); + onFail(); + return; + } } if (statsPage.getData()){ statsPage.finish(); @@ -788,11 +805,17 @@ namespace Mist{ static bool firstData = true;//only the first time, we call onRequest if there's data buffered already. if ((firstData && myConn.Received().size()) || myConn.spool()){ firstData = false; - DEBUG_MSG(DLVL_DONTEVEN, "onRequest"); + DONTEVEN_MSG("onRequest"); onRequest(); + lastRecv = Util::epoch(); }else{ if (!isBlocking && !parseData){ - Util::sleep(500); + if (Util::epoch() - lastRecv > 300){ + WARN_MSG("Disconnecting 5 minute idle connection"); + myConn.close(); + }else{ + Util::sleep(500); + } } } } diff --git a/src/output/output.h b/src/output/output.h index 823676c3..d7d4025e 100644 --- a/src/output/output.h +++ b/src/output/output.h @@ -99,6 +99,7 @@ namespace Mist { std::map nxtKeyNum;///< Contains the number of the next key, for page seeking purposes. std::set buffer;///< A sorted list of next-to-be-loaded packets. bool sought;///