diff --git a/Makefile b/Makefile index 649542f7..84544edf 100755 --- a/Makefile +++ b/Makefile @@ -89,6 +89,8 @@ SOURCES = Version.cpp \ input/stream/Streamer.cpp \ input/stream/StreamerData.cpp \ mpegts/Filter.cpp \ + mpegts/Generator.cpp \ + mpegts/NIT.cpp \ mpegts/PacketBuffer.cpp \ mpegts/PAT.cpp \ mpegts/PCR.cpp \ diff --git a/src/StringConverter.h b/src/StringConverter.h index 9ea512a0..c149e50b 100644 --- a/src/StringConverter.h +++ b/src/StringConverter.h @@ -124,6 +124,18 @@ class StringConverter { return stream.str(); } + /// + template + static std::string toStringFrom4BitBCD(const T bcd, const int charNr) { + std::ostringstream stream; + if (charNr > 0) { + for (int i = 0; i < charNr; ++i) { + stream << std::right << ((bcd >> ((charNr - 1 - i) * 4)) & 0xF); + } + } + return stream.str(); + } + /// Get next line with line_delim (if available) from msg /// @return @c line or empty line static std::string getline(std::string_view msg, @@ -135,9 +147,6 @@ class StringConverter { /// static std::string stringToUpper(std::string_view str); - /// - static std::string getProtocol(const std::string &msg); - /// static void splitPath(const std::string &fullPath, std::string &path, std::string &file); diff --git a/src/base/M3UParser.h b/src/base/M3UParser.h index b80bb569..d704ce75 100644 --- a/src/base/M3UParser.h +++ b/src/base/M3UParser.h @@ -68,6 +68,11 @@ namespace base { /// transformation bool exist(const double freq) const; + /// This will return a copy of the M3U map + TransformationMap getTransformationMap() const { + return _transformationMap; + } + // ======================================================================= // -- Data members ------------------------------------------------------- // ======================================================================= diff --git a/src/input/Device.h b/src/input/Device.h index d56c43f5..1a05bd57 100644 --- a/src/input/Device.h +++ b/src/input/Device.h @@ -102,11 +102,11 @@ class Device : virtual std::string attributeDescribeString() const = 0; /// - virtual mpegts::Filter &getFilterData() = 0; + virtual mpegts::Filter &getFilter() = 0; /// Generic pid filtering Update function virtual void updatePIDFilters() { - getFilterData().updatePIDFilters(_feID, + getFilter().updatePIDFilters(_feID, // openPid lambda function [&](const int) { return true; @@ -119,7 +119,7 @@ class Device : /// Generic pid filtering Close function virtual void closeActivePIDFilters() { - getFilterData().closeActivePIDFilters(_feID, + getFilter().closeActivePIDFilters(_feID, // closePid lambda function [&](const int) { return true; diff --git a/src/input/DeviceData.cpp b/src/input/DeviceData.cpp index f0ead154..f5614f87 100644 --- a/src/input/DeviceData.cpp +++ b/src/input/DeviceData.cpp @@ -98,24 +98,27 @@ std::string DeviceData::attributeDescribeString(FeID id) const { return doAttributeDescribeString(id); } -void DeviceData::setDeliverySystem(const input::InputSystem system) { - base::MutexLock lock(_mutex); - _delsys = system; -} - input::InputSystem DeviceData::getDeliverySystem() const { base::MutexLock lock(_mutex); return _delsys; } -const mpegts::Filter &DeviceData::getFilterData() const { +const mpegts::Filter &DeviceData::getFilter() const { return _filter; } -mpegts::Filter &DeviceData::getFilterData() { +mpegts::Filter &DeviceData::getFilter() { return _filter; } +const mpegts::Generator &DeviceData::getPSIGenerator() const { + return _generator; +} + +mpegts::Generator &DeviceData::getPSIGenerator() { + return _generator; +} + fe_delivery_system DeviceData::convertDeliverySystem() const { base::MutexLock lock(_mutex); switch (_delsys) { diff --git a/src/input/DeviceData.h b/src/input/DeviceData.h index 56a92b28..220a6e6a 100644 --- a/src/input/DeviceData.h +++ b/src/input/DeviceData.h @@ -27,6 +27,7 @@ #include #include #include +#include #include FW_DECL_NS1(mpegts, PacketBuffer); @@ -107,17 +108,20 @@ class DeviceData : /// Reset/clear the 'Channel Data changed' flag void resetDeviceDataChanged(); - /// Set the current Delivery System - void setDeliverySystem(input::InputSystem system); - /// Get the current Delivery System input::InputSystem getDeliverySystem() const; /// - const mpegts::Filter &getFilterData() const; + const mpegts::Filter &getFilter() const; + + /// + mpegts::Filter &getFilter(); + + /// + const mpegts::Generator &getPSIGenerator() const; /// - mpegts::Filter &getFilterData(); + mpegts::Generator &getPSIGenerator(); /// fe_delivery_system convertDeliverySystem() const; @@ -153,6 +157,7 @@ class DeviceData : bool _changed; input::InputSystem _delsys; mpegts::Filter _filter; + mpegts::Generator _generator; bool _internalPidFiltering; // ======================================================================= diff --git a/src/input/Transformation.cpp b/src/input/Transformation.cpp index 2e456224..013cd772 100644 --- a/src/input/Transformation.cpp +++ b/src/input/Transformation.cpp @@ -35,13 +35,17 @@ namespace input { // -- Constructors and destructor ---------------------------------------------- // ============================================================================= -Transformation::Transformation(const std::string &appDataPath) : +Transformation::Transformation( + const std::string &appDataPath, + const input::InputSystem ownInputSystem) : _enabled(false), _transform(false), _advertiseAs(AdvertiseAs::NONE), + _ownInputSystem(ownInputSystem), _appDataPath(appDataPath), _transformFileM3U("mapping.m3u"), - _transformFreq(0) { + _transformFreq(0), + _generatePSIFreq(10111) { _fileParsed = _m3u.parse(_appDataPath + "/" + _transformFileM3U); } @@ -173,7 +177,7 @@ TransportParamVector Transformation::transformStreamString( // Parse the Stream String to the 'dummy' FrontendData and update PID filters // for correct RTCP Attribute Describe String when transforming request _transformedDeviceData.parseStreamString(id, params); - _transformedDeviceData.getFilterData().updatePIDFilters(id, + _transformedDeviceData.getFilter().updatePIDFilters(id, // openPid lambda function [&](const int) { return true; diff --git a/src/input/Transformation.h b/src/input/Transformation.h index e1d41fb8..a25092ef 100644 --- a/src/input/Transformation.h +++ b/src/input/Transformation.h @@ -44,7 +44,10 @@ class Transformation : // ===================================================================== public: - Transformation(const std::string &appDataPath); + Transformation(const std::string &appDataPath) : + Transformation(appDataPath, input::InputSystem::UNDEFINED) {} + + Transformation(const std::string &appDataPath, input::InputSystem ownInputSystem); virtual ~Transformation() = default; @@ -109,11 +112,13 @@ class Transformation : bool _transform; bool _fileParsed; AdvertiseAs _advertiseAs; + input::InputSystem _ownInputSystem; base::M3UParser _m3u; std::string _appDataPath; std::string _transformFileM3U; mutable input::dvb::FrontendData _transformedDeviceData; uint32_t _transformFreq; + uint32_t _generatePSIFreq; }; } // namespace input diff --git a/src/input/childpipe/TSReader.cpp b/src/input/childpipe/TSReader.cpp index e3d99957..a5379702 100644 --- a/src/input/childpipe/TSReader.cpp +++ b/src/input/childpipe/TSReader.cpp @@ -28,8 +28,7 @@ #include #include -namespace input { -namespace childpipe { +namespace input::childpipe { // ============================================================================= // -- Constructors and destructor --------------------------------------------- @@ -96,7 +95,7 @@ void TSReader::addDeliverySystemCount( bool TSReader::isDataAvailable() { const int pcrTimer = _deviceData.getPCRTimer(); - const std::int64_t pcrDelta = _deviceData.getFilterData().getPCRData()->getPCRDelta(); + const std::int64_t pcrDelta = _deviceData.getFilter().getPCRData()->getPCRDelta(); if (pcrDelta != 0 && pcrTimer == 0) { _t2 = _t1; _t1 = std::chrono::steady_clock::now(); @@ -106,7 +105,7 @@ bool TSReader::isDataAvailable() { std::this_thread::sleep_for(std::chrono::microseconds(interval)); } _t1 = std::chrono::steady_clock::now(); - _deviceData.getFilterData().getPCRData()->clearPCRDelta(); + _deviceData.getFilter().getPCRData()->clearPCRDelta(); } else { std::this_thread::sleep_for(std::chrono::microseconds(150 + pcrTimer)); } @@ -123,7 +122,7 @@ bool TSReader::readFullTSPacket(mpegts::PacketBuffer &buffer) { buffer.trySyncing(); if (buffer.full()) { // Add data to Filter - _deviceData.getFilterData().addData(_feID, buffer, _deviceData.isInternalPidFilteringEnabled()); + _deviceData.getFilter().filterData(_feID, buffer, _deviceData.isInternalPidFilteringEnabled()); } } // Check again if buffer is still full @@ -180,7 +179,7 @@ bool TSReader::update() { } } updatePIDFilters(); - SI_LOG_DEBUG("Frontend: @#1, PIDs Table: @#2", _feID, _deviceData.getFilterData().getPidCSV()); + SI_LOG_DEBUG("Frontend: @#1, PIDs Table: @#2", _feID, _deviceData.getFilter().getPidCSV()); SI_LOG_DEBUG("Frontend: @#1, Updating frontend (Finished)", _feID); return true; } @@ -205,5 +204,4 @@ std::string TSReader::attributeDescribeString() const { // -- Other member functions -------------------------------------------------- // ============================================================================= -} // namespace childpipe -} // namespace input +} diff --git a/src/input/childpipe/TSReader.h b/src/input/childpipe/TSReader.h index 4b00afa9..7f992068 100644 --- a/src/input/childpipe/TSReader.h +++ b/src/input/childpipe/TSReader.h @@ -33,8 +33,7 @@ FW_DECL_SP_NS2(input, childpipe, TSReader); FW_DECL_VECTOR_OF_SP_NS0(Stream); -namespace input { -namespace childpipe { +namespace input::childpipe { /// The class @c TSReader is for reading from an Child PIPE as input device /// Some example for opening a TS file with 'cat /dir/test.ts': @@ -108,8 +107,8 @@ class TSReader : virtual std::string attributeDescribeString() const final; - virtual mpegts::Filter &getFilterData() final { - return _deviceData.getFilterData(); + virtual mpegts::Filter &getFilter() final { + return _deviceData.getFilter(); } // ===================================================================== @@ -131,7 +130,6 @@ class TSReader : std::chrono::steady_clock::time_point _t2; }; -} // namespace childpipe -} // namespace input +} #endif // INPUT_CHILD_PIPE_TSREADER_H_INCLUDE diff --git a/src/input/childpipe/TSReaderData.h b/src/input/childpipe/TSReaderData.h index 4520082d..7ecb305a 100644 --- a/src/input/childpipe/TSReaderData.h +++ b/src/input/childpipe/TSReaderData.h @@ -24,8 +24,7 @@ #include -namespace input { -namespace childpipe { +namespace input::childpipe { /// The class @c TSReaderData carries all the data/information for Reading /// from an Child PIPE @@ -86,7 +85,6 @@ class TSReaderData : }; -} // namespace childpipe -} // namespace input +} #endif // INPUT_CHILD_PIPE_TSREADER_DATA_H_INCLUDE diff --git a/src/input/dvb/Frontend.cpp b/src/input/dvb/Frontend.cpp index 4739b3db..e4e6805c 100644 --- a/src/input/dvb/Frontend.cpp +++ b/src/input/dvb/Frontend.cpp @@ -268,7 +268,7 @@ namespace input::dvb { buffer.addAmountOfBytesWritten(readSize); if (buffer.full()) { // Add data to Filter - _frontendData.getFilterData().addData(_feID, buffer); + _frontendData.getFilter().filterData(_feID, buffer); } } else if (readSize < 0) { SI_LOG_PERROR("Frontend: @#1, Error reading data..", _feID); @@ -460,7 +460,7 @@ namespace input::dvb { } void Frontend::closeActivePIDFilters() { - _frontendData.getFilterData().closeActivePIDFilters(_feID, + _frontendData.getFilter().closeActivePIDFilters(_feID, // closePid lambda function [&](const int pid) { if (::ioctl(_fd_dmx, DMX_REMOVE_PID, &pid) != 0) { @@ -476,7 +476,7 @@ namespace input::dvb { SI_LOG_INFO("Frontend: @#1, Update PID filters requested, but frontend not tuned!", _feID); return; } - _frontendData.getFilterData().updatePIDFilters(_feID, + _frontendData.getFilter().updatePIDFilters(_feID, // openPid lambda function [&](const int pid) { // Check if we have already a DMX open diff --git a/src/input/dvb/Frontend.h b/src/input/dvb/Frontend.h index beb710fb..243f6418 100644 --- a/src/input/dvb/Frontend.h +++ b/src/input/dvb/Frontend.h @@ -171,8 +171,8 @@ class Frontend : virtual std::string attributeDescribeString() const final; - virtual mpegts::Filter &getFilterData() final { - return _frontendData.getFilterData(); + virtual mpegts::Filter &getFilter() final { + return _frontendData.getFilter(); } /// diff --git a/src/input/dvb/Frontend_DecryptInterface.cpp b/src/input/dvb/Frontend_DecryptInterface.cpp index 65a147af..d2a4848d 100644 --- a/src/input/dvb/Frontend_DecryptInterface.cpp +++ b/src/input/dvb/Frontend_DecryptInterface.cpp @@ -62,7 +62,7 @@ namespace input::dvb { HEX2(filterData[0]), HEX2(filterData[1]), HEX2(filterData[2]), HEX2(filterMask[0]), HEX2(filterMask[1]), HEX2(filterMask[2]), HEX2(filterMask[3])); _dvbapiData.startOSCamFilterData(_feID, pid, demux, filter, filterData, filterMask); - _frontendData.getFilterData().setPID(pid, true); + _frontendData.getFilter().setPID(pid, true); // now update frontend, PID list has changed updatePIDFilters(); } @@ -96,14 +96,14 @@ namespace input::dvb { } bool Frontend::isMarkedAsActivePMT(int pid) const { - return _frontendData.getFilterData().isMarkedAsActivePMT(pid); + return _frontendData.getFilter().isMarkedAsActivePMT(pid); } mpegts::SpPMT Frontend::getPMTData() const { - return _frontendData.getFilterData().getPMTData(); + return _frontendData.getFilter().getPMTData(); } mpegts::SpSDT Frontend::getSDTData() const { - return _frontendData.getFilterData().getSDTData(); + return _frontendData.getFilter().getSDTData(); } } diff --git a/src/input/file/TSReader.cpp b/src/input/file/TSReader.cpp index 599ec802..83acd8b4 100644 --- a/src/input/file/TSReader.cpp +++ b/src/input/file/TSReader.cpp @@ -95,7 +95,7 @@ void TSReader::addDeliverySystemCount( } bool TSReader::isDataAvailable() { - const std::int64_t pcrDelta = _deviceData.getFilterData().getPCRData()->getPCRDelta(); + const std::int64_t pcrDelta = _deviceData.getFilter().getPCRData()->getPCRDelta(); if (pcrDelta != 0) { _t2 = _t1; _t1 = std::chrono::steady_clock::now(); @@ -105,7 +105,7 @@ bool TSReader::isDataAvailable() { std::this_thread::sleep_for(std::chrono::microseconds(interval)); } _t1 = std::chrono::steady_clock::now(); - _deviceData.getFilterData().getPCRData()->clearPCRDelta(); + _deviceData.getFilter().getPCRData()->clearPCRDelta(); } else { std::this_thread::sleep_for(std::chrono::microseconds(150)); } @@ -124,7 +124,7 @@ bool TSReader::readFullTSPacket(mpegts::PacketBuffer &buffer) { return false; } // Add data to Filter - _deviceData.getFilterData().addData(_feID, buffer); + _deviceData.getFilter().filterData(_feID, buffer); return true; } diff --git a/src/input/file/TSReader.h b/src/input/file/TSReader.h index eb1fed01..9b9a28da 100644 --- a/src/input/file/TSReader.h +++ b/src/input/file/TSReader.h @@ -108,9 +108,8 @@ class TSReader : virtual std::string attributeDescribeString() const final; - virtual mpegts::Filter &getFilterData() final - { - return _deviceData.getFilterData(); + virtual mpegts::Filter &getFilter() final { + return _deviceData.getFilter(); } // ===================================================================== diff --git a/src/input/stream/Streamer.cpp b/src/input/stream/Streamer.cpp index 5360062d..8f1706fe 100644 --- a/src/input/stream/Streamer.cpp +++ b/src/input/stream/Streamer.cpp @@ -27,8 +27,7 @@ #include -namespace input { -namespace stream { +namespace input::stream { // ============================================================================= // -- Constructors and destructor --------------------------------------------- @@ -117,7 +116,7 @@ bool Streamer::readFullTSPacket(mpegts::PacketBuffer &buffer) { buffer.trySyncing(); if (buffer.full()) { // Add data to Filter - _deviceData.getFilterData().addData(_feID, buffer, _deviceData.isInternalPidFilteringEnabled()); + _deviceData.getFilter().filterData(_feID, buffer, _deviceData.isInternalPidFilteringEnabled()); } } // Check again if buffer is still full @@ -184,7 +183,7 @@ bool Streamer::update() { } } updatePIDFilters(); - SI_LOG_DEBUG("Frontend: @#1, PIDs Table: @#2", _feID, _deviceData.getFilterData().getPidCSV()); + SI_LOG_DEBUG("Frontend: @#1, PIDs Table: @#2", _feID, _deviceData.getFilter().getPidCSV()); SI_LOG_DEBUG("Frontend: @#1, Updating frontend (Finished)", _feID); return true; } @@ -209,5 +208,4 @@ std::string Streamer::attributeDescribeString() const { // -- Other member functions -------------------------------------------------- // ============================================================================= -} // namespace stream -} // namespace input +} diff --git a/src/input/stream/Streamer.h b/src/input/stream/Streamer.h index a15dbeb0..0786207f 100644 --- a/src/input/stream/Streamer.h +++ b/src/input/stream/Streamer.h @@ -35,8 +35,7 @@ FW_DECL_SP_NS2(input, stream, Streamer); FW_DECL_VECTOR_OF_SP_NS0(Stream); -namespace input { -namespace stream { +namespace input::stream { /// The class @c Streamer is for reading from an TS stream as input device. /// Stream can be an Multicast UDP e.g. @@ -109,8 +108,8 @@ class Streamer : virtual std::string attributeDescribeString() const final; - virtual mpegts::Filter &getFilterData() final { - return _deviceData.getFilterData(); + virtual mpegts::Filter &getFilter() final { + return _deviceData.getFilter(); } // ===================================================================== @@ -132,7 +131,6 @@ class Streamer : std::string _bindIPAddress; }; -} // namespace stream -} // namespace input +} #endif // INPUT_STREAM_STREAMER_H_INCLUDE diff --git a/src/input/stream/StreamerData.cpp b/src/input/stream/StreamerData.cpp index 6f84b58c..db51b8b7 100644 --- a/src/input/stream/StreamerData.cpp +++ b/src/input/stream/StreamerData.cpp @@ -24,8 +24,7 @@ #include #include -namespace input { -namespace stream { +namespace input::stream { // ======================================================================= // -- Constructors and destructor ---------------------------------------- @@ -108,5 +107,4 @@ namespace stream { return _uri != "None"; } -} // namespace stream -} // namespace input +} diff --git a/src/input/stream/StreamerData.h b/src/input/stream/StreamerData.h index a5cf3c51..60cc0360 100644 --- a/src/input/stream/StreamerData.h +++ b/src/input/stream/StreamerData.h @@ -24,8 +24,7 @@ #include -namespace input { -namespace stream { +namespace input::stream { /// The class @c StreamerData carries all the data/information for Reading /// from an Child PIPE @@ -90,7 +89,6 @@ class StreamerData : }; -} // namespace stream -} // namespace input +} #endif // INPUT_STREAM_STREAMER_DATA_H_INCLUDE diff --git a/src/mpegts/Filter.cpp b/src/mpegts/Filter.cpp index 575e53ef..0fa2a05c 100644 --- a/src/mpegts/Filter.cpp +++ b/src/mpegts/Filter.cpp @@ -35,6 +35,7 @@ namespace mpegts { // ============================================================================= Filter::Filter() { + _nit = std::make_shared(); _pat = std::make_shared(); _pcr = std::make_shared(); _pmt = std::make_shared(); @@ -47,6 +48,7 @@ Filter::Filter() { void Filter::clear() { base::MutexLock lock(_mutex); + _nit = std::make_shared(); _pat = std::make_shared(); _pcr = std::make_shared(); _pmt = std::make_shared(); @@ -75,10 +77,9 @@ void Filter::parsePIDString(const std::string &reqPids, } } -void Filter::addData(const FeID id, mpegts::PacketBuffer &buffer, const bool filter) { +void Filter::filterData(const FeID id, mpegts::PacketBuffer &buffer, const bool filter) { // base::MutexLock lock(_mutex); static constexpr std::size_t size = mpegts::PacketBuffer::getNumberOfTSPackets(); -// SI_LOG_INFO("Filter::addDataOrFilter() PIDs Table: @#1", getPidCSV()); for (std::size_t i = 0; i < size; ++i) { const unsigned char *ptr = buffer.getTSPacketPtr(i); // Check is this the beginning of the TS and no Transport error indicator @@ -97,24 +98,23 @@ void Filter::addData(const FeID id, mpegts::PacketBuffer &buffer, const bool fil } continue; } - const uint8_t cc = ptr[3] & 0x0f; + const uint8_t cc = ptr[3] & 0x0f; _pidTable.addPIDData(pid, cc); if (pid == 0) { if (!_pat->isCollected()) { // collect PAT data - _pat->collectData(id.getID(), PAT_TABLE_ID, ptr, false); - + _pat->collectData(id, PAT_TABLE_ID, ptr, false); // Did we finish collecting PAT if (_pat->isCollected()) { - _pat->parse(id.getID()); + _pat->parse(id); } } } else if (_pat->isMarkedAsPMT(pid)) { // Did we finish collecting PMT if (!_pmt->isCollected()) { // collect PMT data - _pmt->collectData(id.getID(), PMT_TABLE_ID, ptr, false); + _pmt->collectData(id, PMT_TABLE_ID, ptr, false); if (_pmt->isCollected()) { // Collected, check if this is the PMT we need, by getting PCR Pid const int pcrPID = _pmt->parsePCRPid(); @@ -125,7 +125,7 @@ void Filter::addData(const FeID id, mpegts::PacketBuffer &buffer, const bool fil id, DIGIT(pid, 4), DIGIT(pcrPID, 4)); } else { // Yes, the correct one, then parse it - _pmt->parse(id.getID()); + _pmt->parse(id); } } #ifdef ADDDVBCA @@ -139,14 +139,23 @@ void Filter::addData(const FeID id, mpegts::PacketBuffer &buffer, const bool fil } #endif } + } else if (pid == 16) { + if (!_nit->isCollected()) { + // collect NIT data + _nit->collectData(id, NIT_TABLE_ID, ptr, false); + + // Did we finish collecting SDT + if (_nit->isCollected()) { + _nit->parse(id); + } + } } else if (pid == 17) { if (!_sdt->isCollected()) { // collect SDT data - _sdt->collectData(id.getID(), SDT_TABLE_ID, ptr, false); - + _sdt->collectData(id, SDT_TABLE_ID, ptr, false); // Did we finish collecting SDT if (_sdt->isCollected()) { - _sdt->parse(id.getID()); + _sdt->parse(id); } } } else if (pid == 20) { @@ -175,7 +184,7 @@ void Filter::addData(const FeID id, mpegts::PacketBuffer &buffer, const bool fil } #endif } else if (pid == _pmt->getPCRPid()) { - _pcr->collectData(id.getID(), ptr); + _pcr->collectData(id, ptr); } } if (filter) { diff --git a/src/mpegts/Filter.h b/src/mpegts/Filter.h index 34675c1b..ac376a81 100644 --- a/src/mpegts/Filter.h +++ b/src/mpegts/Filter.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -65,7 +66,7 @@ class Filter { /// @param feID specifies the frontend ID /// @param buffer specifies the mpegts buffer from the frontend /// @param filter enables the pid filtering - void addData(FeID id, mpegts::PacketBuffer &buffer, const bool filter = false); + void filterData(FeID id, mpegts::PacketBuffer &buffer, const bool filter = false); /// bool isMarkedAsActivePMT(int pid) const; @@ -199,6 +200,7 @@ class Filter { mutable base::Mutex _mutex; mutable mpegts::PidTable _pidTable; + mutable mpegts::SpNIT _nit; mutable mpegts::SpPAT _pat; mutable mpegts::SpPCR _pcr; mutable mpegts::SpPMT _pmt; diff --git a/src/mpegts/Generator.cpp b/src/mpegts/Generator.cpp new file mode 100644 index 00000000..0c41958f --- /dev/null +++ b/src/mpegts/Generator.cpp @@ -0,0 +1,55 @@ +/* Generator.cpp + + Copyright (C) 2014 - 2021 Marc Postema (mpostema09 -at- gmail.com) + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + Or, point your browser to http://www.gnu.org/copyleft/gpl.html +*/ +#include + +#include +#include +#include + +#include +#include +#include +#include + +namespace mpegts { + +// ============================================================================= +// -- Constructors and destructor ---------------------------------------------- +// ============================================================================= + +Generator::Generator() { + _pat = std::make_shared(); + _pcr = std::make_shared(); + _pmt = std::make_shared(); + _sdt = std::make_shared(); +} + +// ============================================================================= +// -- Other member functions -------------------------------------------------- +// ============================================================================= + +mpegts::PacketBuffer Generator::generatePSIFrom( + FeID id, const base::M3UParser::TransformationMap &info) { + mpegts::PacketBuffer packet; + _pat->generateFrom(id, info); + return packet; +} + +} // namespace mpegts diff --git a/src/mpegts/Generator.h b/src/mpegts/Generator.h new file mode 100644 index 00000000..def912f4 --- /dev/null +++ b/src/mpegts/Generator.h @@ -0,0 +1,76 @@ +/* Generator.h + + Copyright (C) 2014 - 2021 Marc Postema (mpostema09 -at- gmail.com) + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + Or, point your browser to http://www.gnu.org/copyleft/gpl.html +*/ +#ifndef MPEGTS_GENERATOR_H_INCLUDE +#define MPEGTS_GENERATOR_H_INCLUDE MPEGTS_GENERATOR_H_INCLUDE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +FW_DECL_NS1(mpegts, PacketBuffer); + +namespace mpegts { + +/// The class @c Generator will generate PSI metadata for a given TransformationMap +class Generator { + // ========================================================================= + // -- Constructors and destructor ----------------------------------------- + // ========================================================================= + public: + + Generator(); + + virtual ~Generator() = default; + + // ========================================================================= + // -- Other member functions ---------------------------------------------- + // ========================================================================= + public: + + /// This will generate PSI metadata for the given TransformationMap + /// @param feID specifies the frontend ID + /// @param info specifies the info for generating the PSI metadata + mpegts::PacketBuffer generatePSIFrom( + FeID id, const base::M3UParser::TransformationMap &info); + + // ========================================================================= + // -- Data members -------------------------------------------------------- + // ========================================================================= + private: + + mutable base::Mutex _mutex; + + mutable mpegts::SpNIT _nit; + mutable mpegts::SpPAT _pat; + mutable mpegts::SpPCR _pcr; + mutable mpegts::SpPMT _pmt; + mutable mpegts::SpSDT _sdt; +}; + +} // namespace mpegts + +#endif // MPEGTS_GENERATOR_H_INCLUDE diff --git a/src/mpegts/NIT.cpp b/src/mpegts/NIT.cpp new file mode 100644 index 00000000..43d21ac9 --- /dev/null +++ b/src/mpegts/NIT.cpp @@ -0,0 +1,270 @@ +/* NIT.cpp + + Copyright (C) 2014 - 2021 Marc Postema (mpostema09 -at- gmail.com) + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + Or, point your browser to http://www.gnu.org/copyleft/gpl.html +*/ +#include +#include + +namespace mpegts { + +// ============================================================================= +// -- Static Functions -------------------------------------------------------- +// ============================================================================= + +static std::string polTostring(int fec) { + switch (fec) { + case 0: + return "h"; + case 1: + return "v"; + case 3: + return "l"; + case 4: + return "r"; + default: + return "unknown"; + }; +} + +static std::string fecInnerTostring(int fec) { + switch (fec) { + case 0: + return "Not def"; + case 1: + return "12"; + case 2: + return "23"; + case 3: + return "34"; + case 4: + return "56"; + case 5: + return "78"; + case 6: + return "89"; + case 7: + return "35"; + case 8: + return "45"; + case 9: + return "910"; + case 15: + return "none"; + default: + return StringConverter::stringFormat("FEC Inner res. @#1", DIGIT(fec, 2)); + } +} + +static std::string fecOuterTostring(int fec) { + switch (fec) { + case 0: + return "not defined"; + case 1: + return "none"; + case 2: + return "RS(204/188)"; + default: + return StringConverter::stringFormat("FEC Outer res. @#1", DIGIT(fec, 2)); + } +} + +static std::string msysTostring(int msys) { + switch (msys) { + case 0: + return "DVB-S"; + case 1: + return "DVB-S2"; + default: + return "Unknown msys"; + }; +} + +static std::string mtypeSatTostring(int mtype) { + switch (mtype) { + case 0: + return "auto"; + case 1: + return "qpsk"; + case 2: + return "8psk"; + case 3: + return "16qam"; + default: + return "Unknown mtype"; + }; +} + +static std::string mtypeCableTostring(int mtype) { + switch (mtype) { + case 0: + return "auto"; + case 1: + return "16qam"; + case 2: + return "32qam"; + case 3: + return "64qam"; + case 4: + return "128qam"; + case 5: + return "256qam"; + default: + return "Unknown mtype"; + }; +} + +static std::string rolloffTostring(int rolloff) { + switch (rolloff) { + case 0: + return "0.35"; + case 1: + return "0.25"; + case 2: + return "0.20"; + case 3: + return "reserved"; + default: + return "Unknown Rolloff"; + }; +} + +// ============================================================================= +// -- mpegts::TableData ------------------------------------------------------- +// ============================================================================= + +void NIT::clear() { + TableData::clear(); +} + +// ============================================================================= +// -- Other member functions -------------------------------------------------- +// ============================================================================= + +void NIT::parse(const FeID id) { + for (std::size_t secNr = 0; secNr < _numberOfSections; ++secNr) { + TableData::Data tableData; + if (getDataForSectionNumber(secNr, tableData)) { + const unsigned char *data = tableData.data.c_str(); + size_t index = 8; + _nid = getWord(index, data); + +// SI_LOG_BIN_DEBUG(data, tableData.data.size(), "Frontend: @#1, NIT data", id); + + SI_LOG_INFO("Frontend: @#1, NIT - Section Length: @#2 NID: @#3 Version: @#4 secNr: @#5 lastSecNr: @#6 CRC: @#7", + id, tableData.sectionLength, DIGIT(_nid, 4), tableData.version, tableData.secNr, tableData.lastSecNr, HEX(tableData.crc, 4)); + + // Network Descriptors + index = 13; + const size_t netDescLenEnd = (getWord(index, data) & 0xFFF) + index; + while (index < netDescLenEnd) { + const uint8_t tag = getByte(index, data); + const size_t descLenEnd = getByte(index, data) + index; + switch (tag) { + case 0x40: { // networkNameDescriptor + std::string name; + while (index < descLenEnd) { + name += static_cast(getByte(index, data)); + } + SI_LOG_INFO("Frontend: @#1, NIT - Network Name Descriptor: @#2", id, name); + break; + } + case 0x4A: // linkageDescriptor + case 0x5F: // privateDataSpecifierDescriptor + default: + index = descLenEnd; + break; + }; + } + // Transport Stream Descriptors + unsigned int streamDescLenEnd = (getWord(index, data) & 0xFFF) + index; + while (index < streamDescLenEnd) { + const uint16_t transportStreamID = getWord(index, data); + const uint16_t originalNetworkID = getWord(index, data); + const size_t transportDescLenEnd = (getWord(index, data) & 0xFFF) + index; + while (index < transportDescLenEnd) { + const uint8_t tag = getByte(index, data); + const size_t descLenEnd = getByte(index, data) + index; + switch (tag) { + case 0x43: { // satelliteDeliverySystemDescriptor + const uint32_t freq = getDWord(index, data); + const uint16_t orbit = getWord(index, data); + const uint8_t d1 = getByte(index, data); + const uint32_t d2 = getDWord(index, data); + const int westEastFlag = (d1 >> 7); + const int pol = (d1 >> 5) & 0x3; + const int msys = ((d1 >> 2) & 0x1); + const int mtype = (d1 & 0x3); + const int rolloff = ((d1 >> 3) & 0x3); + const int sb = d2 >> 4; + const int fec = d2 & 0x3; + SI_LOG_INFO("Frontend: @#1, NIT - TID: @#2 ID: @#3 Orbit: @#4 WEFlag: @#5 Freq: @#6 SymbolRate: @#7 msys: @#8 mtype: @#9 fec: @#10 pol: @#11 ro: @#12", id, + transportStreamID, + HEX(originalNetworkID, 4), + StringConverter::toStringFrom4BitBCD(orbit, 4), + westEastFlag, + StringConverter::toStringFrom4BitBCD(freq, 8), + StringConverter::toStringFrom4BitBCD(sb, 7), + msysTostring(msys), + mtypeSatTostring(mtype), + fecInnerTostring(fec), + polTostring(pol), + rolloffTostring(rolloff)); + break; + } + case 0x44: { // cableDeliverySystemDescriptor + const uint32_t freq = getDWord(index, data); + const uint32_t d1 = get24Bits(index, data); + const uint32_t d2 = getDWord(index, data); + const int fecOuter = ((d1 >> 8) & 0xF); + const int mtype = (d1 & 0xFF); + const int sb = d2 >> 4; + const int fecInner = d2 & 0xF; + SI_LOG_INFO("Frontend: @#1, NIT - TID: @#2 ID: @#3 Freq: @#4 SymbolRate: @#5 mtype: @#6 fec-inner: @#7 fec-outer: @#8", id, + transportStreamID, + HEX(originalNetworkID, 4), + StringConverter::toStringFrom4BitBCD(freq, 8), + StringConverter::toStringFrom4BitBCD(sb, 7), + mtypeCableTostring(mtype), + fecInnerTostring(fecInner), + fecOuterTostring(fecOuter)); + break; + } + case 0x41: // serviceListDescriptor + default: + index = descLenEnd; + break; + }; + } + } + } + } +} + +mpegts::TSData NIT::generateFrom( + FeID id, const base::M3UParser::TransformationMap &info) { + mpegts::TSData data; + + int prognr = 0; + for (auto [freq, element] : info) { + SI_LOG_DEBUG("Frontend: @#1, Generating NIT: Prog NR: @#2 - @#3 PMT PID: @#4", + id, HEX(prognr, 4), DIGIT(prognr, 5), element.freq); + } + return data; +} + +} // namespace diff --git a/src/mpegts/NIT.h b/src/mpegts/NIT.h new file mode 100644 index 00000000..4d60e49d --- /dev/null +++ b/src/mpegts/NIT.h @@ -0,0 +1,72 @@ +/* NIT.h + + Copyright (C) 2014 - 2021 Marc Postema (mpostema09 -at- gmail.com) + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + Or, point your browser to http://www.gnu.org/copyleft/gpl.html +*/ +#ifndef MPEGTS_NIT_DATA_H_INCLUDE +#define MPEGTS_NIT_DATA_H_INCLUDE MPEGTS_NIT_DATA_H_INCLUDE + +#include +#include +#include + +#include +#include + +FW_DECL_SP_NS1(mpegts, NIT); + +namespace mpegts { + +class NIT : + public TableData { + // ===================================================================== + // -- Constructors and destructor -------------------------------------- + // ===================================================================== + public: + + NIT() = default; + + virtual ~NIT() = default; + + // ===================================================================== + // -- mpegts::TableData ------------------------------------------------ + // ===================================================================== + public: + + virtual void clear() final; + + // ===================================================================== + // -- Other member functions ------------------------------------------ + // ===================================================================== + public: + + void parse(FeID id); + + TSData generateFrom( + FeID id, const base::M3UParser::TransformationMap &info); + + // ===================================================================== + // -- Data members ---------------------------------------------------- + // ===================================================================== + private: + + uint16_t _nid = 0; +}; + +} // namespace + +#endif // MPEGTS_NIT_DATA_H_INCLUDE diff --git a/src/mpegts/PAT.cpp b/src/mpegts/PAT.cpp index 611e424a..78ce818f 100644 --- a/src/mpegts/PAT.cpp +++ b/src/mpegts/PAT.cpp @@ -76,4 +76,17 @@ bool PAT::isMarkedAsPMT(int pid) const { return false; } +mpegts::TSData PAT::generateFrom( + FeID id, const base::M3UParser::TransformationMap &info) { + mpegts::TSData data; + + int transportStreamID = 0; + int prognr = 0; + for (auto [freq, element] : info) { + SI_LOG_DEBUG("Frontend: @#1, Generating PAT: TID: @#2 Prog NR: @#3 - @#4 PMT PID: @#5", + id, transportStreamID, HEX(prognr, 4), DIGIT(prognr, 5), element.freq); + } + return data; +} + } // namespace diff --git a/src/mpegts/PAT.h b/src/mpegts/PAT.h index ef26a3d2..10e8666a 100644 --- a/src/mpegts/PAT.h +++ b/src/mpegts/PAT.h @@ -21,6 +21,7 @@ #define MPEGTS_PAT_DATA_H_INCLUDE MPEGTS_PAT_DATA_H_INCLUDE #include +#include #include #include @@ -57,6 +58,9 @@ class PAT : bool isMarkedAsPMT(int pid) const; + TSData generateFrom( + FeID id, const base::M3UParser::TransformationMap &info); + // ===================================================================== // -- Data members ---------------------------------------------------- // ===================================================================== diff --git a/src/mpegts/PacketBuffer.cpp b/src/mpegts/PacketBuffer.cpp index b8f7fc97..9439d995 100644 --- a/src/mpegts/PacketBuffer.cpp +++ b/src/mpegts/PacketBuffer.cpp @@ -114,16 +114,6 @@ void PacketBuffer::purge() { } } -bool PacketBuffer::markToFlush() { - if (getBufferSize() <= 0) { - //SI_LOG_DEBUG("PacketBuffer::markToFlush(): can't be flushed"); - _flushable = false; - } else { - _flushable = true; - } - return _flushable; -} - void PacketBuffer::tagRTPHeaderWith(const uint16_t cseq, const long timestamp) { // update sequence number _buffer[2] = ((cseq >> 8) & 0xFF); // sequence number @@ -138,7 +128,7 @@ void PacketBuffer::tagRTPHeaderWith(const uint16_t cseq, const long timestamp) { bool PacketBuffer::isReadyToSend() const { // can only be ready when buffer is full (or ready to flush), so start from there - bool ready = _flushable | full(); + bool ready = full(); if (_decryptPending && ready) { for (std::size_t i = 0; i < (_writeIndex - RTP_HEADER_LEN) / TS_PACKET_SIZE; ++i) { const unsigned char *ts = getTSPacketPtr(i); diff --git a/src/mpegts/PacketBuffer.h b/src/mpegts/PacketBuffer.h index 1d05542e..a33d6a23 100644 --- a/src/mpegts/PacketBuffer.h +++ b/src/mpegts/PacketBuffer.h @@ -46,7 +46,6 @@ class PacketBuffer { void reset() { _decryptPending = false; _purgePending = false; - _flushable = false; _writeIndex = RTP_HEADER_LEN; } @@ -128,9 +127,6 @@ class PacketBuffer { return &_buffer[index]; } - /// Mark the buffer as ready to be sent even if it is not full. - bool markToFlush(); - /// Set the decrypt pending flag, so we should check scramble flag if this /// buffer is ready for sending void setDecryptPending() { @@ -160,7 +156,6 @@ class PacketBuffer { bool _initialized = false; bool _decryptPending = false; bool _purgePending = false; - bool _flushable = false; }; diff --git a/src/mpegts/TableData.cpp b/src/mpegts/TableData.cpp index abaeab90..0990d0e9 100644 --- a/src/mpegts/TableData.cpp +++ b/src/mpegts/TableData.cpp @@ -123,6 +123,9 @@ const char* TableData::getTableTXT(const int tableID) const { return "CAT"; case PMT_TABLE_ID: return "PMT"; + case NIT_TABLE_ID: + case NIT_OTHER_TABLE_ID: + return "NIT"; case SDT_TABLE_ID: return "SDT"; case EIT1_TABLE_ID: @@ -153,7 +156,8 @@ void TableData::collectData(const FeID id, const int tableID, const int pid = ((data[1] & 0x1F) << 8) | data[2]; const int cc = data[3] & 0x0F; const std::size_t sectionLength = ((data[6] & 0x0F) << 8) | data[7]; - const int version = data[10]; + const int version = (data[10] & 0x1F) >> 1; + const int nextIndicator = data[10] & 0x1; const std::size_t secNr = data[11]; const std::size_t lastSecNr = data[12]; @@ -165,6 +169,7 @@ void TableData::collectData(const FeID id, const int tableID, } currentTableData.sectionLength = sectionLength; currentTableData.version = version; + currentTableData.nextIndicator = nextIndicator; currentTableData.secNr = secNr; currentTableData.lastSecNr = lastSecNr; @@ -206,7 +211,7 @@ void TableData::collectData(const FeID id, const int tableID, // Add Table Data without TS Header if (addData(tableID, &data[4], 188 - 4, pid, cc)) { // 4 = TS Header const std::size_t tableDataSize = currentTableData.data.size(); - if (!raw) { + if (trace) { SI_LOG_INFO("Frontend: @#1, @#2 - PID @#3: sectionLength: @#4 tableDataSize: @#5 secNr: @#6 lastSecNr: @#7 currSecNr: @#8", id, getTableTXT(tableID), DIGIT(pid, 4), sectionLength, tableDataSize, currentTableData.secNr, currentTableData.lastSecNr, _currentSectionNumber); } diff --git a/src/mpegts/TableData.h b/src/mpegts/TableData.h index 86e51e2d..549801ea 100644 --- a/src/mpegts/TableData.h +++ b/src/mpegts/TableData.h @@ -49,6 +49,8 @@ class TableData { #define PAT_TABLE_ID 0x00 #define CAT_TABLE_ID 0x01 #define PMT_TABLE_ID 0x02 + #define NIT_TABLE_ID 0x40 + #define NIT_OTHER_TABLE_ID 0x41 #define SDT_TABLE_ID 0x42 #define EIT1_TABLE_ID 0x4E #define EIT2_TABLE_ID 0x4F @@ -117,6 +119,32 @@ class TableData { /// const char* getTableTXT(int tableID) const; + /// + uint8_t getByte(size_t &i, const unsigned char *buf) { + uint8_t d = buf[i]; + ++i; + return d; + } + + /// + uint16_t getWord(size_t &i, const unsigned char *buf) { + uint16_t d = (buf[i] << 8) | buf[i + 1]; + i += 2; + return d; + } + + uint32_t get24Bits(size_t &i, const unsigned char *buf) { + uint32_t d = (buf[i] << 16) | (buf[i + 1] << 8) | buf[i + 2]; + i += 3; + return d; + } + + uint32_t getDWord(size_t &i, const unsigned char *buf) { + uint32_t d = (buf[i] << 24) | (buf[i + 1] << 16) | (buf[i + 2] << 8) | buf[i + 3]; + i += 4; + return d; + } + private: /// Collect Table data for tableID @@ -131,6 +159,7 @@ class TableData { int tableID; std::size_t sectionLength; int version; + int nextIndicator; int secNr; int lastSecNr; uint32_t crc;