4#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
5#include <QtSystemDetection>
12# include <sys/types.h>
13# include <sys/socket.h>
14# include <netinet/in.h>
15# include <netinet/ip.h>
35#define LOC QString("IPTVSH[%1](%2): ").arg(m_inputId).arg(m_device)
48 QMap<QString,IPTVStreamHandler*>::iterator it =
s_iptvhandlers.find(devkey);
57 LOG(VB_RECORD, LOG_INFO,
58 QString(
"IPTVSH[%1]: Creating new stream handler %2 for %3")
59 .arg(QString::number(inputid), devkey, tuning.
GetDeviceName()));
65 LOG(VB_RECORD, LOG_INFO,
66 QString(
"IPTVSH[%1]: Using existing stream handler %2 for %3")
67 .arg(QString::number(inputid), devkey, tuning.
GetDeviceName()) +
68 QString(
" (%1 in use)").arg(rcount));
84 LOG(VB_RECORD, LOG_INFO, QString(
"IPTVSH[%1]: Return(%2) has %3 handlers")
85 .arg(QString::number(inputid), devname).arg(*rit));
94 QMap<QString,IPTVStreamHandler*>::iterator it =
s_iptvhandlers.find(devname);
97 LOG(VB_RECORD, LOG_INFO, QString(
"IPTVSH[%1]: Closing handler for %2")
98 .arg(QString::number(inputid), devname));
105 LOG(VB_GENERAL, LOG_ERR,
106 QString(
"IPTVSH[%1] Error: Couldn't find handler for %2")
107 .arg(QString::number(inputid), devname));
117 , m_useRtpStreaming(m_tuning.IsRTP())
125 LOG(VB_GENERAL, LOG_INFO,
LOC +
"run()");
144 LOG(VB_RECORD, LOG_ERR,
LOC +
145 "RTSP interface did not support the necessary options");
154 LOG(VB_RECORD, LOG_ERR,
LOC +
155 "RTSP Describe command failed");
165 urltuned.setScheme(
"rtp");
168 urltuned.toString(), 0,
"", 0);
176 QUrl url = tuning.
GetURL(i);
180 LOG(VB_RECORD, LOG_DEBUG,
LOC +
181 QString(
"setting up url[%1]:%2").arg(i).arg(url.toString()));
184 int port = start_port ? start_port + 1 : url.port();
185 QString host = url.host();
186 QHostAddress dest_addr(host);
188 if (!host.isEmpty() && dest_addr.isNull())
191 QHostInfo
info = QHostInfo::fromName(host);
192 QList<QHostAddress> list =
info.addresses();
196 LOG(VB_RECORD, LOG_ERR,
LOC +
197 QString(
"Can't resolve hostname:'%1'").arg(host));
201 for (
const auto & addr : std::as_const(list))
204 if (addr.protocol() == QAbstractSocket::IPv6Protocol)
210 LOG(VB_RECORD, LOG_DEBUG,
LOC +
211 QString(
"resolved %1 as %2").arg(host, dest_addr.toString()));
214 bool ipv6 = dest_addr.protocol() == QAbstractSocket::IPv6Protocol;
215 bool is_multicast = ipv6 ?
216 dest_addr.isInSubnet(QHostAddress::parseSubnet(
"ff00::/8")) :
217 (dest_addr.toIPv4Address() & 0xf0000000) == 0xe0000000;
230 int fd = socket(ipv6 ? AF_INET6 : AF_INET, SOCK_DGRAM, 0);
233 LOG(VB_GENERAL, LOG_ERR,
LOC +
234 "Unable to create socket " +
ENO);
237 int buf_size = 2 * 1024 * std::max(tuning.
GetBitrate(i)/1000, 500U);
239 buf_size = 2 * 1024 * 1024;
240 int err = setsockopt(fd, SOL_SOCKET, SO_RCVBUF,
241 (
char *)&buf_size,
sizeof(buf_size));
244 LOG(VB_GENERAL, LOG_INFO,
LOC +
245 QString(
"Increasing buffer size to %1 failed")
246 .arg(buf_size) +
ENO);
250 fd, QAbstractSocket::UnconnectedState, QIODevice::ReadOnly);
254 QHostAddress a {QHostAddress::Any};
258 a = QHostAddress::AnyIPv6;
261 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Binding to port failed.");
271 m_sockets[i]->joinMulticastGroup(dest_addr);
272 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Joining %1")
273 .arg(dest_addr.toString()));
276 if (!is_multicast && rtsp && i == 1)
299 LOG(VB_RECORD, LOG_ERR,
LOC +
300 "Starting recording (RTP initialization failed). Aborting.");
344 m_parent(
p), m_socket(s), m_sender(
p->m_sender[stream]),
347 connect(
m_socket, &QIODevice::readyRead,
351#define LOC_WH QString("IPTVSH(%1): ").arg(m_parent->m_device)
356 quint16 senderPort = 0;
357 bool sender_null =
m_sender.isNull();
361 while (
m_socket->hasPendingDatagrams())
365 data.resize(
m_socket->pendingDatagramSize());
366 m_socket->readDatagram(data.data(), data.size(),
367 &sender, &senderPort);
368 if (sender_null || sender ==
m_sender)
375 QString(
"Received on socket(%1) %2 bytes from non expected "
376 "sender:%3 (expected:%4) ignoring")
378 .arg(sender.toString(),
m_sender.toString()));
384 while (
m_socket->hasPendingDatagrams())
388 data.resize(
m_socket->pendingDatagramSize());
389 m_socket->readDatagram(data.data(), data.size(),
390 &sender, &senderPort);
391 if (sender_null || sender ==
m_sender)
398 QString(
"Received on socket(%1) %2 bytes from non expected "
399 "sender:%3 (expected:%4) ignoring")
401 .arg(sender.toString(),
m_sender.toString()));
447 remainder = sit.key()->ProcessData(
448 reinterpret_cast<const unsigned char*
>(data.data()),
456 QString(
"data_length = %1 remainder = %2")
483 ((exp_seq_num&0xFFFF) != (seq_num&0xFFFF)))
486 QString(
"Sequence number mismatch %1!=%2")
487 .arg(seq_num).arg(exp_seq_num));
488 if (seq_num > exp_seq_num)
496 LOG(VB_RECORD, LOG_DEBUG,
497 QString(
"Processing RTP packet(seq:%1 ts:%2)")
506 remainder = sit.key()->ProcessData(
515 QString(
"data_length = %1 remainder = %2")
535 QByteArray buf = rtcp.
GetData();
538 QString(
"Sending RTCPReport to %1:%2")
bool Setup(ushort clientPort1, ushort clientPort2, ushort &rtpPort, ushort &rtcpPort, uint32_t &ssrc)
bool GetOptions(QStringList &options)
IPTVStreamHandler * m_parent
IPTVStreamHandlerReadHelper(IPTVStreamHandler *p, QUdpSocket *s, uint stream)
uint m_previousLastSequenceNumber
void SendRTCPReport(void)
void timerEvent(QTimerEvent *event) override
IPTVStreamHandler * m_parent
~IPTVStreamHandlerWriteHelper() override
uint m_lastSequenceNumber
std::array< QHostAddress, IPTV_SOCKET_COUNT > m_sender
IPTVStreamHandler(const IPTVTuningData &tuning, int inputid)
void run(void) override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
static QMap< QString, uint > s_iptvhandlers_refcnt
friend class IPTVStreamHandlerWriteHelper
std::array< QUdpSocket *, IPTV_SOCKET_COUNT > m_sockets
std::array< IPTVStreamHandlerReadHelper *, IPTV_SOCKET_COUNT > m_readHelpers
static IPTVStreamHandler * Get(const IPTVTuningData &tuning, int inputid)
friend class IPTVStreamHandlerReadHelper
static QMutex s_iptvhandlers_lock
static QMap< QString, IPTVStreamHandler * > s_iptvhandlers
static void Return(IPTVStreamHandler *&ref, int inputid)
IPTVStreamHandlerWriteHelper * m_writeHelper
QString GetDeviceKey(void) const
uint GetBitrate(uint i) const
QString GetDeviceName(void) const
QUrl GetURL(uint i) const
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
int exec(void)
Enters the qt event loop. call exit or quit to exit thread.
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
UDPPacket PopDataPacket(void)
Fetches a data packet for processing.
virtual void PushFECPacket(const UDPPacket &, unsigned int)=0
UDPPacket GetEmptyPacket(void)
Gets a packet for use in PushDataPacket/PushFECPacket.
virtual void PushDataPacket(const UDPPacket &)=0
void FreePacket(const UDPPacket &packet)
Frees an RTPDataPacket returned by PopDataPacket.
bool HasAvailablePacket(void) const
Returns true if there are ordered packets ready for processing.
QByteArray GetData(void) const
bool IsValid(void) const override
IsValid() must return true before any data access methods are called, other than GetDataReference() a...
uint GetTimeStamp(void) const
uint GetPayloadType(void) const
uint GetSequenceNumber(void) const
RTP Transport Stream Data Packet.
unsigned int GetTSDataSize(void) const
const unsigned char * GetTSData(void) const
StreamDataList m_streamDataList
void SetRunning(bool running, bool using_buffering, bool using_section_reader)
QRecursiveMutex m_listenerLock
QByteArray & GetDataReference(void)
static constexpr std::chrono::milliseconds RTCP_TIMER
static constexpr size_t IPTV_SOCKET_COUNT
#define ENO
This can be appended to the LOG args with "+".
#define LOG(_MASK_, _LEVEL_, _QSTRING_)