From 41c1a2777f87c25a092cd122f1d9c422d71d9ff4 Mon Sep 17 00:00:00 2001 From: randomdan Date: Tue, 3 Dec 2013 03:40:35 +0000 Subject: Changes to add downloading program association table and program map (big mess in maintenance.cpp now) Supporting changes in slice, general libs etc Wait for lock after tuning, throw on fail Don't register tuner as in use until tuning has succeeded Tweaks in table parser to be more helpful Count packets transmitted in a demux send --- p2pvr/.p2config | 1 + p2pvr/daemon/daemon.cpp | 2 + p2pvr/datasources/schema.sql | 32 ++++- p2pvr/ice/p2pvr.ice | 15 +++ p2pvr/lib/dvbsiHelpers.h | 1 + p2pvr/lib/dvbsiHelpers/programMap.cpp | 23 ++++ p2pvr/lib/frontends/ofdm.cpp | 47 ++++--- p2pvr/lib/localDevices.cpp | 10 +- p2pvr/lib/maintenance.cpp | 192 +++++++++++++++++++++++++++++ p2pvr/lib/maintenance.h | 2 + p2pvr/lib/mapIterator.cpp | 10 ++ p2pvr/lib/mapIterator.h | 45 +++++++ p2pvr/lib/objectRowState.h | 9 +- p2pvr/lib/siParsers/programAssociation.cpp | 29 +++++ p2pvr/lib/siParsers/programAssociation.h | 23 ++++ p2pvr/lib/siParsers/programMap.cpp | 46 +++++++ p2pvr/lib/siParsers/programMap.h | 37 ++++++ p2pvr/lib/siParsers/table.h | 15 ++- p2pvr/lib/tuner.cpp | 26 +++- p2pvr/lib/tuner.h | 6 +- 20 files changed, 535 insertions(+), 36 deletions(-) create mode 100644 p2pvr/lib/dvbsiHelpers/programMap.cpp create mode 100644 p2pvr/lib/mapIterator.cpp create mode 100644 p2pvr/lib/mapIterator.h create mode 100644 p2pvr/lib/siParsers/programAssociation.cpp create mode 100644 p2pvr/lib/siParsers/programAssociation.h create mode 100644 p2pvr/lib/siParsers/programMap.cpp create mode 100644 p2pvr/lib/siParsers/programMap.h diff --git a/p2pvr/.p2config b/p2pvr/.p2config index 8c4269f..5c820ab 100644 --- a/p2pvr/.p2config +++ b/p2pvr/.p2config @@ -2,5 +2,6 @@ library = libp2pvrdaemon.so daemon.type = p2pvrdaemon common.filelog.level = 9 common.filelog.path = /tmp/p2daemon.log +common.filelog.openmode = w common.consolelogLevel = 9 p2pvr.globaldevices.carddaemon = Devices:default -h defiant -p 10001 diff --git a/p2pvr/daemon/daemon.cpp b/p2pvr/daemon/daemon.cpp index de470d3..f3fe1ad 100644 --- a/p2pvr/daemon/daemon.cpp +++ b/p2pvr/daemon/daemon.cpp @@ -34,6 +34,8 @@ class P2PvrDaemon : public Daemon { auto maint = P2PVR::MaintenancePrx::checkedCast(adapter->createProxy(ic->stringToIdentity("Maintenance"))); maint->UpdateTransports(FE_OFDM); maint->UpdateServices(FE_OFDM); + maint->UpdateProgramAssociations(FE_OFDM); + maint->UpdateProgramMaps(FE_OFDM); maint->UpdateEvents(FE_OFDM); //ic->waitForShutdown(); diff --git a/p2pvr/datasources/schema.sql b/p2pvr/datasources/schema.sql index c628bf0..c51eb06 100644 --- a/p2pvr/datasources/schema.sql +++ b/p2pvr/datasources/schema.sql @@ -196,12 +196,26 @@ CREATE TABLE services ( eitschedule boolean, eitpresentfollowing boolean, freecamode boolean, - transportstreamid integer NOT NULL + transportstreamid integer NOT NULL, + programid integer ); ALTER TABLE public.services OWNER TO gentoo; +-- +-- Name: servicestreams; Type: TABLE; Schema: public; Owner: gentoo; Tablespace: +-- + +CREATE TABLE servicestreams ( + serviceid integer NOT NULL, + id integer NOT NULL, + type smallint NOT NULL +); + + +ALTER TABLE public.servicestreams OWNER TO gentoo; + -- -- Name: transportstreams; Type: TABLE; Schema: public; Owner: gentoo; Tablespace: -- @@ -286,6 +300,14 @@ ALTER TABLE ONLY services ADD CONSTRAINT pk_services PRIMARY KEY (serviceid); +-- +-- Name: pk_servicestreams; Type: CONSTRAINT; Schema: public; Owner: gentoo; Tablespace: +-- + +ALTER TABLE ONLY servicestreams + ADD CONSTRAINT pk_servicestreams PRIMARY KEY (serviceid, id); + + -- -- Name: pk_transportstreams; Type: CONSTRAINT; Schema: public; Owner: gentoo; Tablespace: -- @@ -358,6 +380,14 @@ ALTER TABLE ONLY services ADD CONSTRAINT fk_services_transportstreams FOREIGN KEY (transportstreamid) REFERENCES transportstreams(transportstreamid) ON UPDATE CASCADE ON DELETE CASCADE; +-- +-- Name: fk_servicestreams_service; Type: FK CONSTRAINT; Schema: public; Owner: gentoo +-- + +ALTER TABLE ONLY servicestreams + ADD CONSTRAINT fk_servicestreams_service FOREIGN KEY (serviceid) REFERENCES services(serviceid) ON UPDATE CASCADE ON DELETE CASCADE; + + -- -- Name: fk_transportstreams_network; Type: FK CONSTRAINT; Schema: public; Owner: gentoo -- diff --git a/p2pvr/ice/p2pvr.ice b/p2pvr/ice/p2pvr.ice index 62c58ca..15013de 100644 --- a/p2pvr/ice/p2pvr.ice +++ b/p2pvr/ice/p2pvr.ice @@ -109,6 +109,17 @@ module DVBSI { ServiceList Services; }; + class Stream { + short Type; + int Id; + }; + sequence StreamList; + + class ProgramMap { + int ServiceId; + StreamList Streams; + }; + class EitInformation { int ServiceId; int TransportStreamId; @@ -193,6 +204,8 @@ module P2PVR { idempotent void SendNetworkInformation(RawDataClient * client); idempotent void SendBouquetAssociations(RawDataClient * client); idempotent void SendServiceDescriptions(RawDataClient * client); + idempotent void SendProgramAssociationTable(RawDataClient * client); + idempotent void SendProgramMap(int pid, RawDataClient * client); idempotent void SendEventInformation(RawDataClient * client); int StartSendingTS(PacketIds pids, RawDataClient * client); @@ -224,6 +237,8 @@ module P2PVR { idempotent void UpdateAll(); idempotent void UpdateTransports(short type); idempotent void UpdateServices(short type); + idempotent void UpdateProgramAssociations(short type); + idempotent void UpdateProgramMaps(short type); idempotent void UpdateEvents(short type); }; diff --git a/p2pvr/lib/dvbsiHelpers.h b/p2pvr/lib/dvbsiHelpers.h index bf0de82..132c1b5 100644 --- a/p2pvr/lib/dvbsiHelpers.h +++ b/p2pvr/lib/dvbsiHelpers.h @@ -21,6 +21,7 @@ ColumnHelperParent(DVBSI::TerrestrialDeliveryPtr, DVBSI::TransportStreamPtr); ColumnHelperParent(DVBSI::CableDeliveryPtr, DVBSI::TransportStreamPtr); ColumnHelperParent(DVBSI::SatelliteDeliveryPtr, DVBSI::TransportStreamPtr); ColumnHelperParent(DVBSI::ServicePtr, DVBSI::TransportStreamPtr); +ColumnHelperParent(DVBSI::StreamPtr, DVBSI::ProgramMapPtr); ColumnHelper(DVBSI::EventPtr); #endif diff --git a/p2pvr/lib/dvbsiHelpers/programMap.cpp b/p2pvr/lib/dvbsiHelpers/programMap.cpp new file mode 100644 index 0000000..5b543a9 --- /dev/null +++ b/p2pvr/lib/dvbsiHelpers/programMap.cpp @@ -0,0 +1,23 @@ +#include "../dvbsiHelpers.h" +#include "../p2Helpers.h" + +template<> +void +CreateColumns(const ColumnCreator & cc) +{ + cc("serviceId", true); + cc("id", true); + cc("type", false); +} + +template<> +void +BindColumns(RowState & rs, const DVBSI::StreamPtr & stream, const DVBSI::ProgramMapPtr & pmp) +{ + rs.fields[0] << pmp->ServiceId; + rs.fields[1] << stream->Id; + rs.fields[2] << stream->Type; +} + + + diff --git a/p2pvr/lib/frontends/ofdm.cpp b/p2pvr/lib/frontends/ofdm.cpp index 8e2b23c..fca94ae 100644 --- a/p2pvr/lib/frontends/ofdm.cpp +++ b/p2pvr/lib/frontends/ofdm.cpp @@ -29,6 +29,7 @@ class Frontend_OFDM : public Frontend { feparams.u.ofdm.guard_interval = (fe_guard_interval_t)td->GuardInterval; feparams.u.ofdm.hierarchy_information = (fe_hierarchy_t)td->Hierarchy; SetParameters(feparams); + WaitForLock(); } dvb_frontend_parameters GetParameters() const @@ -42,6 +43,28 @@ class Frontend_OFDM : public Frontend { return feparams; } + void WaitForLock() const + { + fe_status status; + // Wait for something (500ms) + for (int x = 0; x < 50 && (status = GetStatus()) == 0; x += 1) { + usleep(10000); + } + // Was it useful? + if (!(status & (FE_HAS_SIGNAL | FE_HAS_CARRIER))) { + Logger()->messagebf(LOG_ERR, "Tuning of device %s failed (%s)", tuner->Device(), "No carrier"); + throw P2PVR::DeviceError(tuner->Device(), "No carrier", 0); + } + // Wait for lock (4000ms) + for (int x = 0; x < 400 && ((status = GetStatus()) & FE_HAS_LOCK) == 0; x += 1) { + usleep(10000); + } + if (!(status & FE_HAS_LOCK)) { + Logger()->messagebf(LOG_ERR, "Tuning of device %s failed (%s)", tuner->Device(), "No lock"); + throw P2PVR::DeviceError(tuner->Device(), "No lock", 0); + } + } + void SetParameters(const dvb_frontend_parameters & feparams) const { if (ioctl(frontendFD, FE_SET_FRONTEND, &feparams) < 0) { @@ -113,29 +136,19 @@ class Frontend_OFDM : public Frontend { Logger()->messagebf(LOG_WARNING, "Channel %d, freq (%d Hz) outside card range", channel, feparams.frequency); continue; } - Logger()->messagebf(LOG_DEBUG, "Channel %d, Frequency %d Hz", channel, feparams.frequency); - SetParameters(feparams); - fe_status status; - // Wait for something - for (int x = 0; x < 150 && (status = GetStatus()) == 0; x += 100) { - usleep(1000); - } - // Was it useful? - if (!(status & (FE_HAS_SIGNAL | FE_HAS_CARRIER))) { - continue; - } - // Wait for lock - for (int x = 0; x < 500 && ((status = GetStatus()) & FE_HAS_LOCK) == 0; x += 100) { - usleep(10000); - } - // Did we get lock? - if (status & FE_HAS_LOCK) { + try { + Logger()->messagebf(LOG_DEBUG, "Channel %d, Frequency %d Hz", channel, feparams.frequency); + SetParameters(feparams); + WaitForLock(); Logger()->messagebf(LOG_INFO, "Found multiplex at %d Hz", feparams.frequency); Logger()->messagebf(LOG_DEBUG, "frequency %d", feparams.frequency); if (onFrequencyFound(feparams.frequency)) { return; } } + catch (const P2PVR::DeviceError &) { + // Moving on... + } } } } diff --git a/p2pvr/lib/localDevices.cpp b/p2pvr/lib/localDevices.cpp index 5478301..b93adee 100644 --- a/p2pvr/lib/localDevices.cpp +++ b/p2pvr/lib/localDevices.cpp @@ -37,9 +37,10 @@ LocalDevices::GetTunerSpecific(const DVBSI::DeliveryPtr & delivery, Ice::Long un Logger()->messagebf(LOG_DEBUG, "%s: Opening a sharable tuner (frequency %d, frontend %s)", __PRETTY_FUNCTION__, delivery->Frequency, openTuner->first); - auto tuner = P2PVR::PrivateTunerPrx::checkedCast(ice.adapter->addWithUUID(new Tuner(openTuner->first))); + P2PVR::PrivateTunerPtr t = new Tuner(openTuner->first); + t->TuneTo(delivery, ice); + auto tuner = P2PVR::PrivateTunerPrx::checkedCast(ice.adapter->addWithUUID(t)); openTuner->second = OpenTunerPtr(new OpenTuner(delivery, tuner, until, false)); - tuner->TuneTo(delivery); Logger()->messagebf(LOG_DEBUG, "%s: Tuned, returning (frequency %d, frontend %s)", __PRETTY_FUNCTION__, delivery->Frequency, openTuner->first); @@ -69,9 +70,10 @@ LocalDevices::GetTunerAny(short , const DVBSI::DeliveryPtr & delivery, Ice::Long Logger()->messagebf(LOG_DEBUG, "%s: Opening a sharable tuner (frequency %d, frontend %s)", __PRETTY_FUNCTION__, delivery->Frequency, openTuner->first); - auto tuner = P2PVR::PrivateTunerPrx::checkedCast(ice.adapter->addWithUUID(new Tuner(openTuner->first))); + P2PVR::PrivateTunerPtr t = new Tuner(openTuner->first); + t->TuneTo(delivery, ice); + auto tuner = P2PVR::PrivateTunerPrx::checkedCast(ice.adapter->addWithUUID(t)); openTuner->second = OpenTunerPtr(new OpenTuner(delivery, tuner, until, false)); - tuner->TuneTo(delivery); Logger()->messagebf(LOG_DEBUG, "%s: Tuned, returning (frequency %d, frontend %s)", __PRETTY_FUNCTION__, delivery->Frequency, openTuner->first); diff --git a/p2pvr/lib/maintenance.cpp b/p2pvr/lib/maintenance.cpp index 71ddf60..6fe44b2 100644 --- a/p2pvr/lib/maintenance.cpp +++ b/p2pvr/lib/maintenance.cpp @@ -1,6 +1,8 @@ #include "maintenance.h" #include "siParsers/network.h" #include "siParsers/event.h" +#include "siParsers/programAssociation.h" +#include "siParsers/programMap.h" #include "siParsers/service.h" #include #include @@ -12,6 +14,7 @@ #include "p2Helpers.h" #include "dvbsiHelpers.h" #include "containerIterator.h" +#include "mapIterator.h" #include "singleIterator.h" #include "temporaryIceAdapterObject.h" @@ -78,6 +81,42 @@ class SiNetworkInformationMerger : public SiNetworkInformationParser { CommonObjects * commonObjects; }; +class SiProgramMapHandler : public SiProgramMapParser { + public: + SiProgramMapHandler(const RowProcessorCallback & cb) : + callBack(cb) {} + + void HandleTable(DVBSI::ProgramMapPtr pmp) + { + Logger()->messagebf(LOG_DEBUG, "Program map: serviceId = %d", pmp->ServiceId); + BOOST_FOREACH(const auto & s, pmp->Streams) { + Logger()->messagef(LOG_DEBUG, "type: %02x id: %d", s->Type, s->Id); + BindColumns(rowState, s, pmp); + rowState.process(callBack); + } + } + + private: + ObjectRowState rowState; + const RowProcessorCallback callBack; +}; + +class SiProgramAssociationHandler : public SiProgramAssociationParser { + public: + void HandleTable(ProgramAssociationMapPtr pam) + { + Logger()->messagebf(LOG_DEBUG, "Program association table"); + BOOST_FOREACH(const auto & pa, *pam) { + Logger()->messagebf(LOG_DEBUG, " %d -> %d", pa.first, pa.second); + } + BOOST_FOREACH(const auto & pa, *pam) { + map[pa.first] = pa.second; + } + } + + ProgramAssociationMap map; +}; + class SiServicesMerger : public SiServicesParser { public: SiServicesMerger(CommonObjects * co) : commonObjects(co) { } @@ -228,6 +267,159 @@ Maintenance::UpdateServices(short type, const Ice::Current & ice) } } +void +CreatePMTColumns(const ColumnCreator & cc) +{ + cc("serviceId", true); + cc("programId", false); +} + +void +Maintenance::UpdateProgramAssociations(short type, const Ice::Current & ice) +{ + auto ic = ice.adapter->getCommunicator(); + auto devs = P2PVR::DevicesPrx::checkedCast(ice.adapter->createProxy(ic->stringToIdentity("GlobalDevices"))); + auto si = P2PVR::SIPrx::checkedCast(ice.adapter->createProxy(ic->stringToIdentity("SI"))); + + if (!devs || !si) { + throw std::runtime_error("bad proxy(s)"); + } + + auto siparser = new SiProgramAssociationHandler(); + TemporarayIceAdapterObject parser(ice.adapter, siparser); + + const auto deliveries = si->GetAllDeliveries(type); + if (deliveries.empty()) { + throw std::runtime_error("no delivery methods"); + } + + BOOST_FOREACH(const auto & transport, deliveries) { + try { + Logger()->messagebf(LOG_DEBUG, "%s: Getting a tuner", __PRETTY_FUNCTION__); + auto tuner = devs->GetTunerSpecific(transport, time(NULL) + 300); + Logger()->messagebf(LOG_DEBUG, "%s: Fetching associations", __PRETTY_FUNCTION__); + tuner->SendProgramAssociationTable(parser); + Logger()->messagebf(LOG_INFO, "%s: Updated associations", __PRETTY_FUNCTION__); + devs->ReleaseTuner(tuner); + } + catch (...) { + // Tuning can fail + } + } + + SqlMergeTask mergeServices("postgres", "services"); + CreatePMTColumns(boost::bind(SqlMergeColumnsInserter, &mergeServices, _1, _2)); + // Don't change the list of services available from the network + mergeServices.doDelete = VariableType(false); + mergeServices.doInsert = VariableType(false); + Columns cols; + mergeServices.sources.insert(new MapIterator(CreatePMTColumns, &siparser->map)); + mergeServices.loadComplete(this); + mergeServices.execute(NULL); + BOOST_FOREACH(const CommonObjects::DataSources::value_type & ds, CommonObjects::datasources) { + ds.second->commit(); + } +} + +#include +#include +#include +typedef boost::shared_ptr SelectPtr; +class HandleAsInt : public DB::HandleField { + public: + virtual void null() { } + virtual void string(const char *, size_t ) { } + virtual void integer(int64_t v) { integerValue = v; } + virtual void floatingpoint(double) { } + virtual void timestamp(const struct tm &) { } + int64_t integerValue; +}; +class SiProgramMapMerger : public IHaveSubTasks { + public: + SiProgramMapMerger(short t, CommonObjects * co, const Ice::Current & i) : + SourceObject(__PRETTY_FUNCTION__), + IHaveSubTasks(NULL), + commonObjects(co), + type(t), + ice(i) { } + + void execute(ExecContext * ec) const + { + auto ic = ice.adapter->getCommunicator(); + auto devs = P2PVR::DevicesPrx::checkedCast(ice.adapter->createProxy(ic->stringToIdentity("GlobalDevices"))); + auto si = P2PVR::SIPrx::checkedCast(ice.adapter->createProxy(ic->stringToIdentity("SI"))); + + if (!devs || !si) { + throw std::runtime_error("bad proxy(s)"); + } + + TemporarayIceAdapterObject parser(ice.adapter, + new SiProgramMapHandler(boost::bind(&SiProgramMapMerger::executeChildren, this, ec))); + + const auto deliveries = si->GetAllDeliveries(type); + if (deliveries.empty()) { + throw std::runtime_error("no delivery methods"); + } + + auto db = commonObjects->dataSource("postgres"); + SelectPtr sel = SelectPtr(db->getReadonly().newSelectCommand("select d.frequency, s.programid \ + from delivery_dvbt d, services s \ + where d.transportstreamid = s.transportstreamid \ + and s.programid is not null \ + order by s.transportstreamid, s.serviceid")); + int64_t curFreq = 0; + P2PVR::TunerPrx tuner; + while (sel->fetch()) { + HandleAsInt freq, pid; + (*sel)[0].apply(freq); + (*sel)[1].apply(pid); + + if (freq.integerValue != curFreq) { + if (tuner) { + devs->ReleaseTuner(tuner); + } + Logger()->messagebf(LOG_DEBUG, "%s: Getting a tuner", __PRETTY_FUNCTION__); + const auto transport = *std::find_if(deliveries.begin(), deliveries.end(), + [freq](const DVBSI::DeliveryPtr & del) { return del->Frequency == freq.integerValue; }); + tuner = devs->GetTunerSpecific(transport, time(NULL) + 10); + curFreq = freq.integerValue; + } + + Logger()->messagebf(LOG_DEBUG, "%s: Fetching associations", __PRETTY_FUNCTION__); + tuner->SendProgramMap(pid.integerValue, parser); + Logger()->messagebf(LOG_INFO, "%s: Updated associations", __PRETTY_FUNCTION__); + } + if (tuner) { + devs->ReleaseTuner(tuner); + } + } + + private: + CommonObjects * commonObjects; + const short type; + const Ice::Current & ice; + + void executeChildren(ExecContext * ec) const + { + BOOST_FOREACH(const Tasks::value_type & sq, normal) { + sq->execute(ec); + } + } +}; + +void +Maintenance::UpdateProgramMaps(short type, const Ice::Current & ice) +{ + SqlMergeTask mergeServiceStreams("postgres", "servicestreams"); + CreateColumns(boost::bind(SqlMergeColumnsInserter, &mergeServiceStreams, _1, _2)); + mergeServiceStreams.sources.insert(new SiProgramMapMerger(type, this, ice)); + mergeServiceStreams.loadComplete(this); + mergeServiceStreams.execute(NULL); + BOOST_FOREACH(const CommonObjects::DataSources::value_type & ds, CommonObjects::datasources) { + ds.second->commit(); + } +} + class SiEventsMerger : public IHaveSubTasks { public: SiEventsMerger(short t, const Ice::Current & i) : diff --git a/p2pvr/lib/maintenance.h b/p2pvr/lib/maintenance.h index b3773cf..acffb72 100644 --- a/p2pvr/lib/maintenance.h +++ b/p2pvr/lib/maintenance.h @@ -11,6 +11,8 @@ class Maintenance : public P2PVR::Maintenance, public virtual CommonObjects { void UpdateTransports(short type, const Ice::Current &); void UpdateTransports(short type, Ice::CommunicatorPtr, Ice::ObjectAdapterPtr); void UpdateServices(short type, const Ice::Current &); + void UpdateProgramAssociations(short type, const Ice::Current &); + void UpdateProgramMaps(short type, const Ice::Current &); void UpdateEvents(short type, const Ice::Current &); }; diff --git a/p2pvr/lib/mapIterator.cpp b/p2pvr/lib/mapIterator.cpp new file mode 100644 index 0000000..5f0fbb0 --- /dev/null +++ b/p2pvr/lib/mapIterator.cpp @@ -0,0 +1,10 @@ +#include "mapIterator.h" +#include "p2Helpers.h" + +template<> +void BindColumns>(RowState & rs, std::pair const & p) +{ + rs.fields[0] << p.first; + rs.fields[1] << p.second; +} + diff --git a/p2pvr/lib/mapIterator.h b/p2pvr/lib/mapIterator.h new file mode 100644 index 0000000..8a06fe6 --- /dev/null +++ b/p2pvr/lib/mapIterator.h @@ -0,0 +1,45 @@ +#ifndef MAPITERATOR_H +#define MAPITERATOR_H + +#include +#include +#include "objectRowState.h" + +template +class MapIterator : public IHaveSubTasks { + public: + template + MapIterator(const ColumnSpecifier & cs, const T * m, const Parents & ... p) : + SourceObject(__PRETTY_FUNCTION__), + IHaveSubTasks(NULL), + binder(boost::bind(&BindColumns, _1, _2, p...)), + columnSpecifier(cs), + map(m) + { + } + + void execute(ExecContext * ec) const + { + ObjectRowState rs(columnSpecifier); + BOOST_FOREACH(const auto & i, *map) { + binder(rs, i); + rs.process(boost::bind(&MapIterator::executeChildren, this, ec)); + } + } + + private: + boost::function binder; + const ColumnSpecifier columnSpecifier; + const T * map; + + void executeChildren(ExecContext * ec) const + { + BOOST_FOREACH(const Tasks::value_type & sq, normal) { + sq->execute(ec); + } + } +}; + +#endif + + diff --git a/p2pvr/lib/objectRowState.h b/p2pvr/lib/objectRowState.h index 945ea89..cf42055 100644 --- a/p2pvr/lib/objectRowState.h +++ b/p2pvr/lib/objectRowState.h @@ -6,6 +6,7 @@ #include typedef boost::function ColumnCreator; +typedef boost::function ColumnSpecifier; template void BindColumns(RowState &, const V &, const Parents & ...); @@ -19,19 +20,19 @@ void CreateColumns(const ColumnCreator &); template class ObjectRowState : public RowState { public: - ObjectRowState() : - columns(ColumnCreatorHelper()) + ObjectRowState(const ColumnSpecifier & cs = CreateColumns) : + columns(ColumnCreatorHelper(cs)) { fields.resize(columns.size()); } const Columns & getColumns() const { return columns; } private: - static Columns ColumnCreatorHelper() + static Columns ColumnCreatorHelper(const ColumnSpecifier & cs) { int index = 0; Columns columns; - CreateColumns([&columns, &index](const std::string & name, bool) { + cs([&columns, &index](const std::string & name, bool) { columns.insert(new Column(index++, name)); }); return columns; diff --git a/p2pvr/lib/siParsers/programAssociation.cpp b/p2pvr/lib/siParsers/programAssociation.cpp new file mode 100644 index 0000000..0add29e --- /dev/null +++ b/p2pvr/lib/siParsers/programAssociation.cpp @@ -0,0 +1,29 @@ +#include "programAssociation.h" + +struct ProgramAssociation { + uint16_t program_number; +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + uint8_t reserved : 3; + uint8_t pid_hi : 5; +#else + uint8_t pid_hi : 5; + uint8_t reserved : 3; +#endif + uint8_t pid_lo; +}; + +bool +SiProgramAssociationParser::CheckTableId(u_char tableId) const +{ + return (tableId == 0x00); +} + +void +SiProgramAssociationParser::ParseSiTable(const ProgramAssociationSection * pas, ProgramAssociationMapPtr pam) +{ + LoopOverSection(pas->data, HILO(pas->header.section_length) - 12, [this,pam](const ProgramAssociation * sd) { + (*pam)[ntohs(sd->program_number)] = HILO(sd->pid); + }); +} + + diff --git a/p2pvr/lib/siParsers/programAssociation.h b/p2pvr/lib/siParsers/programAssociation.h new file mode 100644 index 0000000..63c7e4b --- /dev/null +++ b/p2pvr/lib/siParsers/programAssociation.h @@ -0,0 +1,23 @@ +#ifndef PROGRAMASSOCIATION_H +#define PROGRAMASSOCIATION_H + +#include "table.h" +#include + +struct ProgramAssociationSection { + SiTableHeader header; + u_char data[]; +} __attribute__((packed)); + +typedef std::map ProgramAssociationMap; +typedef boost::shared_ptr ProgramAssociationMapPtr; + +class SiProgramAssociationParser : public SiTableParser { + protected: + bool CheckTableId(u_char tableId) const; + void ParseSiTable(const struct ProgramAssociationSection * pas, ProgramAssociationMapPtr); +}; + +#endif + + diff --git a/p2pvr/lib/siParsers/programMap.cpp b/p2pvr/lib/siParsers/programMap.cpp new file mode 100644 index 0000000..bb59017 --- /dev/null +++ b/p2pvr/lib/siParsers/programMap.cpp @@ -0,0 +1,46 @@ +#include "programMap.h" +#include +#include + +struct ProgramMapStream { + uint8_t stream_type; +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + u_char reserved1 :3; + u_char elementary_PID_hi :5; +#else + u_char elementary_PID_hi :5; + u_char reserved1 :3; +#endif + u_char elementary_PID_lo; +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + u_char reserved2 :4; + u_char ES_info_length_hi :4; +#else + u_char ES_info_length_hi :4; + u_char reserved2 :4; +#endif + u_char ES_info_length_lo; + u_char data[]; +}; + + +bool +SiProgramMapParser::CheckTableId(u_char tableId) const +{ + return (tableId == 0x02); +} + +void +SiProgramMapParser::ParseSiTable(const struct ProgramMap * pm, DVBSI::ProgramMapPtr pmp) +{ + pmp->ServiceId = ntohs(pm->header.content_id); + auto pms = ParseDescriptors(pm->data, HILO(pm->program_info_len)); + while (reinterpret_cast(pms) < &pm->header.section_length_lo + HILO(pm->header.section_length) - 4) { + DVBSI::StreamPtr s = new DVBSI::Stream(); + s->Type = pms->stream_type; + s->Id = HILO(pms->elementary_PID); + pmp->Streams.push_back(s); + // Don't care what's in here, just need to move along + pms = ParseDescriptors(pms->data, HILO(pms->ES_info_length)); + } +} diff --git a/p2pvr/lib/siParsers/programMap.h b/p2pvr/lib/siParsers/programMap.h new file mode 100644 index 0000000..a00faa1 --- /dev/null +++ b/p2pvr/lib/siParsers/programMap.h @@ -0,0 +1,37 @@ +#ifndef PROGRAMMAP_H +#define PROGRAMMAP_H + +#include "table.h" +#include + +struct ProgramMap { + SiTableHeader header; +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + u_char reserved1 :3; + u_char pcr_pid_hi :5; +#else + u_char pcr_pid_hi :5; + u_char reserved1 :3; +#endif + u_char pcr_pid_lo; +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + u_char reserved2 :4; + u_char program_info_len_hi :4; +#else + u_char program_info_len_hi :4; + u_char reserved2 :4; +#endif + u_char program_info_len_lo; + u_char data[]; +} __attribute__((packed)); + +class SiProgramMapParser : public SiTableParser { + protected: + bool CheckTableId(u_char tableId) const; + void ParseSiTable(const struct ProgramMap * nit, DVBSI::ProgramMapPtr); +}; + +#endif + + + diff --git a/p2pvr/lib/siParsers/table.h b/p2pvr/lib/siParsers/table.h index be40813..55dbd17 100644 --- a/p2pvr/lib/siParsers/table.h +++ b/p2pvr/lib/siParsers/table.h @@ -49,7 +49,7 @@ struct SiTableHeaderBase { u_char reserved2 :3; u_char section_syntax_indicator :1; #endif - uint16_t section_length_lo :8; + uint8_t section_length_lo; } __attribute__((packed)); struct SiTableHeader : public SiTableHeaderBase { @@ -103,7 +103,7 @@ class SiTableParser : public SiTableParserBase { if (seen.find(sectionNumber) == seen.end()) { auto & obj = boost::get<0>(content); if (!obj) { - obj = new typename TargetType::element_type(); + obj = TargetType(new typename TargetType::element_type()); incomplete += 1; } ParseSiTable(siTable, obj); @@ -173,6 +173,17 @@ class SiTableParser : public SiTableParserBase { } } + template + static void LoopOverSection(const u_char * data, size_t len, boost::function parser) + { + auto end = data + len; + while (data < end) { + auto loopData = reinterpret_cast(data); + parser(loopData); + data += sizeof(LoopContent); + } + } + virtual bool CheckTableId(u_char tableId) const = 0; virtual void ParseSiTable(const TableType *, TargetType) = 0; virtual void HandleTable(TargetType table) = 0; diff --git a/p2pvr/lib/tuner.cpp b/p2pvr/lib/tuner.cpp index e508152..3cfb8a4 100644 --- a/p2pvr/lib/tuner.cpp +++ b/p2pvr/lib/tuner.cpp @@ -89,8 +89,7 @@ Tuner::ScanAndSendNetworkInformation(const P2PVR::RawDataClientPrx & client, con { frontend->FrequencyScan([this, &client, &ice](long) { try { - SendNetworkInformation(client, ice); - return true; + return (SendPID(0x10, client, ice) > 0); } catch (...) { return false; @@ -116,13 +115,25 @@ Tuner::SendServiceDescriptions(const P2PVR::RawDataClientPrx & client, const Ice SendPID(0x11, client, ice); } +void +Tuner::SendProgramMap(Ice::Int pid, const P2PVR::RawDataClientPrx & client, const Ice::Current & ice) +{ + SendPID(pid, client, ice); +} + +void +Tuner::SendProgramAssociationTable(const P2PVR::RawDataClientPrx & client, const Ice::Current & ice) +{ + SendPID(0x00, client, ice); +} + void Tuner::SendEventInformation(const P2PVR::RawDataClientPrx & client, const Ice::Current & ice) { SendPID(0x12, client, ice); } -void +uint64_t Tuner::SendPID(int pid, const P2PVR::RawDataClientPrx & client, const Ice::Current & ice) const { Logger()->messagebf(LOG_DEBUG, "%s: pid = 0x%x", __PRETTY_FUNCTION__, pid); @@ -138,15 +149,16 @@ Tuner::SendPID(int pid, const P2PVR::RawDataClientPrx & client, const Ice::Curre throw P2PVR::DeviceError("demux", strerror(errno), errno); } - ReadDemuxAndSend(demux, client); + return ReadDemuxAndSend(demux, client); } -void +uint64_t Tuner::ReadDemuxAndSend(int demux, const P2PVR::RawDataClientPrx & client) const { Logger()->messagebf(LOG_DEBUG, "%s: begin", __PRETTY_FUNCTION__); std::vector asyncs; struct pollfd ufd; + uint64_t packetsSent = 0; bool exitFlag = false; do { // Wait for data to appear @@ -155,7 +167,7 @@ Tuner::ReadDemuxAndSend(int demux, const P2PVR::RawDataClientPrx & client) const ufd.events = POLLIN; if (poll(&ufd, 1, timeout) < 1) { Logger()->messagebf(LOG_DEBUG, "%s: Timed out waiting for data", __PRETTY_FUNCTION__); - throw P2PVR::DeviceError("demux", "Timed out. Tuned to a multiplex?", 0); + break; } // Read it @@ -187,6 +199,7 @@ Tuner::ReadDemuxAndSend(int demux, const P2PVR::RawDataClientPrx & client) const } asyncs.push_back(client->begin_NewData(buf)); + packetsSent += 1; asyncs.erase(std::remove_if(asyncs.begin(), asyncs.end(), [&exitFlag, &client](const Ice::AsyncResultPtr & a) { if (a->isCompleted()) { @@ -200,6 +213,7 @@ Tuner::ReadDemuxAndSend(int demux, const P2PVR::RawDataClientPrx & client) const client->end_NewData(a); } Logger()->messagebf(LOG_DEBUG, "%s: end", __PRETTY_FUNCTION__); + return packetsSent; } int diff --git a/p2pvr/lib/tuner.h b/p2pvr/lib/tuner.h index 8111f1e..4efde20 100644 --- a/p2pvr/lib/tuner.h +++ b/p2pvr/lib/tuner.h @@ -22,6 +22,8 @@ class Tuner : public P2PVR::PrivateTuner { void SendNetworkInformation(const P2PVR::RawDataClientPrx & client, const Ice::Current&); void SendBouquetAssociations(const P2PVR::RawDataClientPrx & client, const Ice::Current&); void SendServiceDescriptions(const P2PVR::RawDataClientPrx & client, const Ice::Current&); + void SendProgramMap(Ice::Int pid, const P2PVR::RawDataClientPrx & client, const Ice::Current&); + void SendProgramAssociationTable(const P2PVR::RawDataClientPrx & client, const Ice::Current&); void SendEventInformation(const P2PVR::RawDataClientPrx & client, const Ice::Current&); int StartSendingTS(const P2PVR::PacketIds & pids, const P2PVR::RawDataClientPrx & client, const Ice::Current &); @@ -30,8 +32,8 @@ class Tuner : public P2PVR::PrivateTuner { private: static bool crc32(const P2PVR::Data &); int OpenDemux() const; - void SendPID(int pid, const P2PVR::RawDataClientPrx & client, const Ice::Current &) const; - void ReadDemuxAndSend(int fd, const P2PVR::RawDataClientPrx & client) const; + uint64_t SendPID(int pid, const P2PVR::RawDataClientPrx & client, const Ice::Current &) const; + uint64_t ReadDemuxAndSend(int fd, const P2PVR::RawDataClientPrx & client) const; void startSenderThread(); void senderThread(); -- cgit v1.2.3