diff --git a/plugins/channeltx/moddatv/datvmod.cpp b/plugins/channeltx/moddatv/datvmod.cpp index a0a75e5e1..9ec7d1e71 100644 --- a/plugins/channeltx/moddatv/datvmod.cpp +++ b/plugins/channeltx/moddatv/datvmod.cpp @@ -52,6 +52,7 @@ MESSAGE_CLASS_DEFINITION(DATVMod::MsgConfigureTsFileName, Message) MESSAGE_CLASS_DEFINITION(DATVMod::MsgConfigureTsFileSourceSeek, Message) MESSAGE_CLASS_DEFINITION(DATVMod::MsgConfigureTsFileSourceStreamTiming, Message) MESSAGE_CLASS_DEFINITION(DATVMod::MsgGetUDPBitrate, Message) +MESSAGE_CLASS_DEFINITION(DATVMod::MsgGetUDPBufferUtilization, Message) const char* const DATVMod::m_channelIdURI = "sdrangel.channeltx.moddatv"; const char* const DATVMod::m_channelId = "DATVMod"; @@ -185,6 +186,12 @@ bool DATVMod::handleMessage(const Message& cmd) return true; } + else if (MsgGetUDPBufferUtilization::match(cmd)) + { + m_basebandSource->getInputMessageQueue()->push(DATVMod::MsgGetUDPBufferUtilization::create()); + + return true; + } else { return false; diff --git a/plugins/channeltx/moddatv/datvmod.h b/plugins/channeltx/moddatv/datvmod.h index 9b7fb86e8..7c36b0e2c 100644 --- a/plugins/channeltx/moddatv/datvmod.h +++ b/plugins/channeltx/moddatv/datvmod.h @@ -181,6 +181,23 @@ public: { } }; + class MsgGetUDPBufferUtilization : public Message { + MESSAGE_CLASS_DECLARATION + + public: + + static MsgGetUDPBufferUtilization* create() + { + return new MsgGetUDPBufferUtilization(); + } + + private: + + MsgGetUDPBufferUtilization() : + Message() + { } + }; + //================================================================= DATVMod(DeviceAPI *deviceAPI); diff --git a/plugins/channeltx/moddatv/datvmodbaseband.cpp b/plugins/channeltx/moddatv/datvmodbaseband.cpp index b4e3af124..d1dfe5c0b 100644 --- a/plugins/channeltx/moddatv/datvmodbaseband.cpp +++ b/plugins/channeltx/moddatv/datvmodbaseband.cpp @@ -192,6 +192,11 @@ bool DATVModBaseband::handleMessage(const Message& cmd) m_source.reportUDPBitrate(); return true; } + else if (DATVMod::MsgGetUDPBufferUtilization::match(cmd)) + { + m_source.reportUDPBufferUtilization(); + return true; + } else { return false; diff --git a/plugins/channeltx/moddatv/datvmodgui.cpp b/plugins/channeltx/moddatv/datvmodgui.cpp index 2acbf2129..643de1006 100644 --- a/plugins/channeltx/moddatv/datvmodgui.cpp +++ b/plugins/channeltx/moddatv/datvmodgui.cpp @@ -98,6 +98,12 @@ DATVModGUI::DATVModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandS connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleSourceMessages())); +#ifndef _WIN32 + // Only currently works on Windows, so hide on other OSes + ui->udpBufferUtilization->setVisible(false); + ui->udpBufferUtilizationLine->setVisible(false); +#endif + displaySettings(); applySettings(true); if (!m_settings.m_tsFileName.isEmpty()) @@ -174,6 +180,13 @@ bool DATVModGUI::handleMessage(const Message& message) m_tickMsgOutstanding = false; return true; } + else if (DATVModReport::MsgReportUDPBufferUtilization::match(message)) + { + DATVModReport::MsgReportUDPBufferUtilization& report = (DATVModReport::MsgReportUDPBufferUtilization&)message; + ui->udpBufferUtilization->setText(tr("%1%").arg(report.getUtilization(), 0, 'f', 1)); + m_tickMsgOutstanding = false; + return true; + } else if (DATVMod::MsgConfigureDATVMod::match(message)) { const DATVMod::MsgConfigureDATVMod& cfg = (DATVMod::MsgConfigureDATVMod&) message; @@ -602,6 +615,7 @@ void DATVModGUI::tick() { m_tickMsgOutstanding = true; m_datvMod->getInputMessageQueue()->push(DATVMod::MsgGetUDPBitrate::create()); + m_datvMod->getInputMessageQueue()->push(DATVMod::MsgGetUDPBufferUtilization::create()); } } } diff --git a/plugins/channeltx/moddatv/datvmodgui.ui b/plugins/channeltx/moddatv/datvmodgui.ui index 2eff43ce1..f33e011ab 100644 --- a/plugins/channeltx/moddatv/datvmodgui.ui +++ b/plugins/channeltx/moddatv/datvmodgui.ui @@ -568,6 +568,23 @@ + + + + Qt::Vertical + + + + + + + Indicates how full the UDP receive buffer is. If this reaches 100%, packets will likely be dropped. + + + 0% + + + diff --git a/plugins/channeltx/moddatv/datvmodreport.cpp b/plugins/channeltx/moddatv/datvmodreport.cpp index 91dd081b2..fc2cb340a 100644 --- a/plugins/channeltx/moddatv/datvmodreport.cpp +++ b/plugins/channeltx/moddatv/datvmodreport.cpp @@ -22,6 +22,7 @@ MESSAGE_CLASS_DEFINITION(DATVModReport::MsgReportTsFileSourceStreamTiming, Messa MESSAGE_CLASS_DEFINITION(DATVModReport::MsgReportTsFileSourceStreamData, Message) MESSAGE_CLASS_DEFINITION(DATVModReport::MsgReportRates, Message) MESSAGE_CLASS_DEFINITION(DATVModReport::MsgReportUDPBitrate, Message) +MESSAGE_CLASS_DEFINITION(DATVModReport::MsgReportUDPBufferUtilization, Message) DATVModReport::DATVModReport() { } diff --git a/plugins/channeltx/moddatv/datvmodreport.h b/plugins/channeltx/moddatv/datvmodreport.h index 368ce5b1a..7589cc76f 100644 --- a/plugins/channeltx/moddatv/datvmodreport.h +++ b/plugins/channeltx/moddatv/datvmodreport.h @@ -125,6 +125,27 @@ public: { } }; + class MsgReportUDPBufferUtilization : public Message + { + MESSAGE_CLASS_DECLARATION + + public: + float getUtilization() const { return m_utilization; } + + static MsgReportUDPBufferUtilization* create(int utilization) + { + return new MsgReportUDPBufferUtilization(utilization); + } + + protected: + float m_utilization; + + MsgReportUDPBufferUtilization(int utilization) : + Message(), + m_utilization(utilization) + { } + }; + public: DATVModReport(); ~DATVModReport(); diff --git a/plugins/channeltx/moddatv/datvmodsettings.h b/plugins/channeltx/moddatv/datvmodsettings.h index 63490de7e..25a2c5289 100644 --- a/plugins/channeltx/moddatv/datvmodsettings.h +++ b/plugins/channeltx/moddatv/datvmodsettings.h @@ -108,6 +108,8 @@ struct DATVModSettings static DATVModulation mapModulation(const QString& string); static QString mapModulation(DATVModulation modulation); + static const int m_udpBufferSize = 5000000; + }; #endif /* PLUGINS_CHANNELTX_MODDATV_DATVMODSETTINGS_H_ */ diff --git a/plugins/channeltx/moddatv/datvmodsource.cpp b/plugins/channeltx/moddatv/datvmodsource.cpp index 70917e498..bfbb4b4dd 100644 --- a/plugins/channeltx/moddatv/datvmodsource.cpp +++ b/plugins/channeltx/moddatv/datvmodsource.cpp @@ -43,6 +43,11 @@ extern "C" #include "datvmodreport.h" #include "datvmodsource.h" +#ifdef _WIN32 +#include +#pragma comment(lib, "Ws2_32.lib") +#endif + const int DATVModSource::m_levelNbSamples = 10000; // every 10ms // Get transport stream bitrate from file @@ -223,6 +228,7 @@ DATVModSource::DATVModSource() : m_udpByteCount(0), m_udpBufferIdx(0), m_udpBufferCount(0), + m_udpMaxBufferUtilization(0), m_sampleRate(0), m_channelSampleRate(1000000), m_channelFrequencyOffset(0), @@ -337,6 +343,8 @@ void DATVModSource::modulateSample() && ((m_udpSocket != nullptr) && m_udpSocket->hasPendingDatagrams()) ) { + updateUDPBufferUtilization(); + // Get transport stream packets from UDP - buffer if more than one QNetworkDatagram datagram = m_udpSocket->receiveDatagram(); QByteArray ba = datagram.data(); @@ -577,6 +585,28 @@ void DATVModSource::reportUDPBitrate() m_udpByteCount = 0; } +void DATVModSource::updateUDPBufferUtilization() +{ +#ifdef _WIN32 + u_long count; + ioctlsocket(m_udpSocket->socketDescriptor(), FIONREAD, &count); + if (count > m_udpMaxBufferUtilization) { + m_udpMaxBufferUtilization = count; + } +#else + // On linux, ioctl(s, SIOCINQ, &count); only returns length of first datagram, so we can't support this +#endif +} + +void DATVModSource::reportUDPBufferUtilization() +{ + // Report maximum utilization since last call + updateUDPBufferUtilization(); + if (getMessageQueueToGUI()) + getMessageQueueToGUI()->push(DATVModReport::MsgReportUDPBufferUtilization::create(m_udpMaxBufferUtilization / (float)DATVModSettings::m_udpBufferSize * 100.0)); + m_udpMaxBufferUtilization = 0; +} + void DATVModSource::applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force) { qDebug() << "DATVModSource::applyChannelSettings:" @@ -680,7 +710,7 @@ void DATVModSource::applySettings(const DATVModSettings& settings, bool force) { m_udpSocket = new QUdpSocket(); m_udpSocket->bind(QHostAddress(settings.m_udpAddress), settings.m_udpPort); - m_udpSocket->setSocketOption(QAbstractSocket::ReceiveBufferSizeSocketOption, 1000000); + m_udpSocket->setSocketOption(QAbstractSocket::ReceiveBufferSizeSocketOption, DATVModSettings::m_udpBufferSize); m_udpTimingStart = boost::chrono::steady_clock::now(); m_udpByteCount = 0; } diff --git a/plugins/channeltx/moddatv/datvmodsource.h b/plugins/channeltx/moddatv/datvmodsource.h index 6e35c8d4a..95ccc4052 100644 --- a/plugins/channeltx/moddatv/datvmodsource.h +++ b/plugins/channeltx/moddatv/datvmodsource.h @@ -70,6 +70,7 @@ public: void seekTsFileStream(int seekPercentage); void reportTsFileSourceStreamTiming(); void reportUDPBitrate(); + void reportUDPBufferUtilization(); private: uint8_t m_mpegTS[188]; //!< MPEG transport stream packet @@ -99,6 +100,7 @@ private: uint8_t m_udpBuffer[188*10]; int m_udpBufferIdx; //!< TS frame index into buffer int m_udpBufferCount; //!< Number of TS frames in buffer + int m_udpMaxBufferUtilization; int m_sampleRate; int m_channelSampleRate; @@ -131,6 +133,7 @@ private: int getTSBitrate(const QString& filename); int getDVBSDataBitrate(const DATVModSettings& settings); void checkBitrates(); + void updateUDPBufferUtilization(); MessageQueue *getMessageQueueToGUI() { return m_messageQueueToGUI; }