From 79eec1ed7f52b06395417bae225132be99f6b92e Mon Sep 17 00:00:00 2001 From: randomdan Date: Thu, 28 Nov 2013 20:56:20 +0000 Subject: First commit for big refactor into a Project2 daemon, much still to do, but much core stuff in place --- p2pvr/.p2config | 6 + p2pvr/Jamfile.jam | 27 +- p2pvr/carddaemon/Jamfile.jam | 10 + p2pvr/carddaemon/carddaemon.cpp | 53 ++ p2pvr/cron/epg.xml | 35 - p2pvr/cron/importEpg.xml | 67 -- p2pvr/cron/importServices.xml | 28 - p2pvr/cron/services.xml | 16 - p2pvr/daemon/Jamfile.jam | 10 + p2pvr/daemon/daemon.cpp | 64 ++ p2pvr/datasources/postgres.xml | 6 +- p2pvr/datasources/schema.sql | 382 ++++++++ p2pvr/doc/en_300468v011101p.pdf | Bin 0 -> 1424273 bytes p2pvr/ice/Jamfile.jam | 13 +- p2pvr/ice/commonHelpers.h | 22 + p2pvr/ice/p2pvr.ice | 248 ++++- p2pvr/lib/Jamfile.jam | 18 + p2pvr/lib/containerCreator.h | 32 + p2pvr/lib/containerIterator.h | 41 + p2pvr/lib/dvbsiHelpers.cpp | 190 ++++ p2pvr/lib/dvbsiHelpers.h | 60 ++ p2pvr/lib/fileHandle.cpp | 27 + p2pvr/lib/fileHandle.h | 19 + p2pvr/lib/frontend.cpp | 35 + p2pvr/lib/frontend.h | 32 + p2pvr/lib/frontends/ofdm.cpp | 145 +++ p2pvr/lib/globalDevices.cpp | 59 ++ p2pvr/lib/globalDevices.h | 24 + p2pvr/lib/localDevices.cpp | 151 +++ p2pvr/lib/localDevices.h | 42 + p2pvr/lib/maintenance.cpp | 288 ++++++ p2pvr/lib/maintenance.h | 18 + p2pvr/lib/objectRowState.h | 43 + p2pvr/lib/p2Helpers.cpp | 18 + p2pvr/lib/p2Helpers.h | 46 + p2pvr/lib/si.cpp | 33 + p2pvr/lib/si.h | 13 + p2pvr/lib/siParsers/event.cpp | 250 +++++ p2pvr/lib/siParsers/event.h | 35 + p2pvr/lib/siParsers/network.cpp | 219 +++++ p2pvr/lib/siParsers/network.h | 35 + p2pvr/lib/siParsers/service.cpp | 68 ++ p2pvr/lib/siParsers/service.h | 25 + p2pvr/lib/siParsers/table.cpp | 76 ++ p2pvr/lib/siParsers/table.h | 185 ++++ p2pvr/lib/singleIterator.h | 39 + p2pvr/lib/temporaryIceAdapterObject.h | 39 + p2pvr/lib/tuner.cpp | 355 +++++++ p2pvr/lib/tuner.h | 50 + p2pvr/scanner/Jamfile.jam | 17 - p2pvr/scanner/dvbSiReaderHelper.cpp | 133 --- p2pvr/scanner/dvbSiReaderHelper.h | 40 - p2pvr/scanner/epgRows.cpp | 528 ----------- p2pvr/scanner/epgRows.h | 52 - p2pvr/scanner/serviceRows.cpp | 155 --- p2pvr/scanner/serviceRows.h | 43 - p2pvr/scanner/si_tables.h | 1667 --------------------------------- 57 files changed, 3511 insertions(+), 2821 deletions(-) create mode 100644 p2pvr/.p2config create mode 100644 p2pvr/carddaemon/Jamfile.jam create mode 100644 p2pvr/carddaemon/carddaemon.cpp delete mode 100644 p2pvr/cron/epg.xml delete mode 100644 p2pvr/cron/importEpg.xml delete mode 100644 p2pvr/cron/importServices.xml delete mode 100644 p2pvr/cron/services.xml create mode 100644 p2pvr/daemon/Jamfile.jam create mode 100644 p2pvr/daemon/daemon.cpp create mode 100644 p2pvr/datasources/schema.sql create mode 100644 p2pvr/doc/en_300468v011101p.pdf create mode 100644 p2pvr/ice/commonHelpers.h create mode 100644 p2pvr/lib/Jamfile.jam create mode 100644 p2pvr/lib/containerCreator.h create mode 100644 p2pvr/lib/containerIterator.h create mode 100644 p2pvr/lib/dvbsiHelpers.cpp create mode 100644 p2pvr/lib/dvbsiHelpers.h create mode 100644 p2pvr/lib/fileHandle.cpp create mode 100644 p2pvr/lib/fileHandle.h create mode 100644 p2pvr/lib/frontend.cpp create mode 100644 p2pvr/lib/frontend.h create mode 100644 p2pvr/lib/frontends/ofdm.cpp create mode 100644 p2pvr/lib/globalDevices.cpp create mode 100644 p2pvr/lib/globalDevices.h create mode 100644 p2pvr/lib/localDevices.cpp create mode 100644 p2pvr/lib/localDevices.h create mode 100644 p2pvr/lib/maintenance.cpp create mode 100644 p2pvr/lib/maintenance.h create mode 100644 p2pvr/lib/objectRowState.h create mode 100644 p2pvr/lib/p2Helpers.cpp create mode 100644 p2pvr/lib/p2Helpers.h create mode 100644 p2pvr/lib/si.cpp create mode 100644 p2pvr/lib/si.h create mode 100644 p2pvr/lib/siParsers/event.cpp create mode 100644 p2pvr/lib/siParsers/event.h create mode 100644 p2pvr/lib/siParsers/network.cpp create mode 100644 p2pvr/lib/siParsers/network.h create mode 100644 p2pvr/lib/siParsers/service.cpp create mode 100644 p2pvr/lib/siParsers/service.h create mode 100644 p2pvr/lib/siParsers/table.cpp create mode 100644 p2pvr/lib/siParsers/table.h create mode 100644 p2pvr/lib/singleIterator.h create mode 100644 p2pvr/lib/temporaryIceAdapterObject.h create mode 100644 p2pvr/lib/tuner.cpp create mode 100644 p2pvr/lib/tuner.h delete mode 100644 p2pvr/scanner/Jamfile.jam delete mode 100644 p2pvr/scanner/dvbSiReaderHelper.cpp delete mode 100644 p2pvr/scanner/dvbSiReaderHelper.h delete mode 100644 p2pvr/scanner/epgRows.cpp delete mode 100644 p2pvr/scanner/epgRows.h delete mode 100644 p2pvr/scanner/serviceRows.cpp delete mode 100644 p2pvr/scanner/serviceRows.h delete mode 100644 p2pvr/scanner/si_tables.h diff --git a/p2pvr/.p2config b/p2pvr/.p2config new file mode 100644 index 0000000..8c4269f --- /dev/null +++ b/p2pvr/.p2config @@ -0,0 +1,6 @@ +library = libp2pvrdaemon.so +daemon.type = p2pvrdaemon +common.filelog.level = 9 +common.filelog.path = /tmp/p2daemon.log +common.consolelogLevel = 9 +p2pvr.globaldevices.carddaemon = Devices:default -h defiant -p 10001 diff --git a/p2pvr/Jamfile.jam b/p2pvr/Jamfile.jam index 5d63f0d..d709f2d 100644 --- a/p2pvr/Jamfile.jam +++ b/p2pvr/Jamfile.jam @@ -1,2 +1,25 @@ -build-project scanner ; -build-project ice ; +import package ; + +alias glibmm : : : : + "`pkg-config --cflags glibmm-2.4`" + "`pkg-config --libs glibmm-2.4`" + ; +alias p2common : glibmm : : : + /usr/include/project2/common + /usr/include/project2/lib + "-lp2common" +; +alias p2sql : glibmm : : : + /usr/include/project2/sql + "-lp2sql" +; +alias p2daemonlib : glibmm : : : + "-I /usr/include/project2/daemon/lib" + "-lp2daemonlib" +; +build-project daemon ; +build-project carddaemon ; + +install debuginstall : lib carddaemon daemon ice : ./testing ; +package.install install : : : carddaemon daemon ; + diff --git a/p2pvr/carddaemon/Jamfile.jam b/p2pvr/carddaemon/Jamfile.jam new file mode 100644 index 0000000..9b4aa83 --- /dev/null +++ b/p2pvr/carddaemon/Jamfile.jam @@ -0,0 +1,10 @@ +lib boost_filesystem ; +lib boost_system ; + +lib p2pvrcarddaemon : + [ glob *.cpp ] + : + ..//p2daemonlib + ../ice//p2pvrice + ../lib//p2pvrlib + ; diff --git a/p2pvr/carddaemon/carddaemon.cpp b/p2pvr/carddaemon/carddaemon.cpp new file mode 100644 index 0000000..f75109e --- /dev/null +++ b/p2pvr/carddaemon/carddaemon.cpp @@ -0,0 +1,53 @@ +#include +#include +#include +#include +#include "localDevices.h" + +class P2PvrCardDaemon : public Daemon { + public: + P2PvrCardDaemon(int argc, char ** argv) : + ic(Ice::initialize(argc, argv)) + { + } + + ~P2PvrCardDaemon() + { + ic->destroy(); + } + + void run() const + { + Logger()->messagebf(LOG_INFO, "Creating adapter (%s, %s)", Adapter, Endpoint); + auto adapter = ic->createObjectAdapterWithEndpoints(Adapter, Endpoint); + P2PVR::LocalDevicesPrx::checkedCast(adapter->add(new LocalDevices(), ic->stringToIdentity(Identity))); + adapter->activate(); + ic->waitForShutdown(); + } + + void shutdown() const + { + ic->shutdown(); + } + INITOPTIONS; + + private: + Ice::CommunicatorPtr ic; + + static std::string Adapter; + static std::string Endpoint; + static std::string Identity; +}; + +std::string P2PvrCardDaemon::Adapter; +std::string P2PvrCardDaemon::Endpoint; +std::string P2PvrCardDaemon::Identity; + +DECLARE_GENERIC_LOADER("p2pvrcarddaemon", DaemonLoader, P2PvrCardDaemon); + +DECLARE_OPTIONS(P2PvrCardDaemon, "P2PVR Card Daemon") +("p2pvr.carddaemon.iceadapter", Options::value(&Adapter, "DefaultAdapter"), "ICE Adapter name") +("p2pvr.carddaemon.iceendpoint", Options::value(&Endpoint, "default -p 10001"), "ICE Endpoint address") +("p2pvr.carddaemon.iceidentity", Options::value(&Identity, "Devices"), "ICE Interface identity") +END_OPTIONS(P2PvrCardDaemon); + diff --git a/p2pvr/cron/epg.xml b/p2pvr/cron/epg.xml deleted file mode 100644 index 8fa9828..0000000 --- a/p2pvr/cron/epg.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - <titleLang source="parent" attribute="titleLang" depth="1" /> - <subtitle source="parent" attribute="subtitle" depth="1" /> - <serviceID source="parent" attribute="serviceID" depth="1" /> - <descLang source="parent" attribute="descLang" depth="1" /> - <desc1 source="parent" attribute="desc1" depth="1" /> - <desc2 source="parent" attribute="desc2" depth="1" /> - <desc3 source="parent" attribute="desc3" depth="1" /> - <videoAspect source="parent" attribute="videoAspect" depth="1" /> - <videoFrameRate source="parent" attribute="videoFrameRate" depth="1" /> - <videoHD source="parent" attribute="videoHD" depth="1" /> - <audioChannels source="parent" attribute="audioChannels" depth="1" /> - <language source="parent" attribute="language" depth="1" /> - <teletextSubtitleLang source="parent" attribute="teletextSubtitleLang" depth="1" /> - <category source="parent" attribute="category" depth="1" /> - <dvbRating source="parent" attribute="dvbRating" depth="1" /> - <contentItemID source="parent" attribute="contentItemID" depth="1" /> - <contentRecommendation source="parent" attribute="contentRecommendation" depth="1" /> - <contentSeriesID source="parent" attribute="contentSeriesID" depth="1" /> - <startTime source="parent" attribute="startTime" depth="1" /> - <stopTime source="parent" attribute="stopTime" depth="1" /> - <eventID source="parent" attribute="eventID" depth="1" /> - <episode source="parent" attribute="episode" depth="1" /> - <episodes source="parent" attribute="episodes" depth="1" /> - <year source="parent" attribute="year" depth="1" /> - <flags source="parent" attribute="flags" depth="1" /> - </columns> - </p2:view> -</block> diff --git a/p2pvr/cron/importEpg.xml b/p2pvr/cron/importEpg.xml deleted file mode 100644 index c3858f5..0000000 --- a/p2pvr/cron/importEpg.xml +++ /dev/null @@ -1,67 +0,0 @@ -<?xml version="1.0"?> -<block name="importSchedule" xmlns:p2="http://project2.randomdan.homeip.net"> - <p2:library path="libp2pvr-scan-p2.so" /> - <p2:epgrows name="schedule" demux="/dev/dvb/adapter0/demux0" /> - <p2:sqlmerge name="mergeSchedule" datasource="postgres" targettable="programs"> - <p2:iterate name="programs" source="schedule"> - <p2:sqlmergeinsert name="insertProgram"> - <parameters> - <title source="parent" attribute="title" depth="1" /> - <titleLang source="parent" attribute="titleLang" depth="1" /> - <subtitle source="parent" attribute="subtitle" depth="1" /> - <serviceID source="parent" attribute="serviceID" depth="1" /> - <descLang source="parent" attribute="descLang" depth="1" /> - <desc1 source="parent" attribute="desc1" depth="1" /> - <desc2 source="parent" attribute="desc2" depth="1" /> - <desc3 source="parent" attribute="desc3" depth="1" /> - <videoAspect source="parent" attribute="videoAspect" depth="1" /> - <videoFrameRate source="parent" attribute="videoFrameRate" depth="1" /> - <videoHD source="parent" attribute="videoHD" depth="1" /> - <audioChannels source="parent" attribute="audioChannels" depth="1" /> - <language source="parent" attribute="language" depth="1" /> - <teletextSubtitleLang source="parent" attribute="teletextSubtitleLang" depth="1" /> - <category source="parent" attribute="category" depth="1" /> - <dvbRating source="parent" attribute="dvbRating" depth="1" /> - <contentItemID source="parent" attribute="contentItemID" depth="1" /> - <contentRecommendation source="parent" attribute="contentRecommendation" depth="1" /> - <contentSeriesID source="parent" attribute="contentSeriesID" depth="1" /> - <startTime source="parent" attribute="startTime" depth="1" /> - <stopTime source="parent" attribute="stopTime" depth="1" /> - <eventID source="parent" attribute="eventID" depth="1" /> - <episode source="parent" attribute="episode" depth="1" /> - <episodes source="parent" attribute="episodes" depth="1" /> - <year source="parent" attribute="year" depth="1" /> - <flags source="parent" attribute="flags" depth="1" /> - </parameters> - </p2:sqlmergeinsert> - </p2:iterate> - <columns> - <title /> - <titleLang /> - <subtitle /> - <serviceID key="true" /> - <eventID key="true" /> - <descLang /> - <desc1 /> - <desc2 /> - <desc3 /> - <videoAspect /> - <videoFrameRate /> - <videoHD /> - <audioChannels /> - <language /> - <teletextSubtitleLang /> - <category /> - <dvbRating /> - <contentItemID /> - <contentRecommendation /> - <contentSeriesID /> - <startTime /> - <stopTime /> - <episode /> - <episodes /> - <year /> - <flags /> - </columns> - </p2:sqlmerge> -</block> diff --git a/p2pvr/cron/importServices.xml b/p2pvr/cron/importServices.xml deleted file mode 100644 index d914d15..0000000 --- a/p2pvr/cron/importServices.xml +++ /dev/null @@ -1,28 +0,0 @@ -<?xml version="1.0"?> -<block name="importServices" xmlns:p2="http://project2.randomdan.homeip.net"> - <p2:library path="libp2pvr-scan-p2.so" /> - <p2:servicerows name="services" demux="/dev/dvb/adapter0/demux0" /> - <p2:sqlmerge name="mergeServices" datasource="postgres" targettable="services"> - <p2:iterate name="programs" source="services"> - <p2:sqlmergeinsert name="insertProgram"> - <parameters> - <serviceID source="parent" attribute="serviceID" depth="1" /> - <name source="parent" attribute="name" depth="1" /> - <providerName source="parent" attribute="providerName" depth="1" /> - <transportID source="parent" attribute="transportID" depth="1" /> - <type source="parent" attribute="type" depth="1" /> - <defaultAuthority source="parent" attribute="defaultAuthority" depth="1" /> - </parameters> - </p2:sqlmergeinsert> - </p2:iterate> - <columns> - <serviceID key="true" /> - <name /> - <providerName /> - <transportID /> - <type /> - <defaultAuthority /> - </columns> - </p2:sqlmerge> -</block> - diff --git a/p2pvr/cron/services.xml b/p2pvr/cron/services.xml deleted file mode 100644 index bdd038e..0000000 --- a/p2pvr/cron/services.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0"?> -<block name="showServices" xmlns:p2="http://project2.randomdan.homeip.net"> - <p2:library path="libp2pvr-scan-p2.so" /> - <p2:servicerows name="services" demux="/dev/dvb/adapter0/demux0" /> - <p2:view name="showServices" source="services" rootname="services" recordname="service"> - <columns> - <serviceID source="parent" attribute="serviceID" depth="1" /> - <name source="parent" attribute="name" depth="1" /> - <providerName source="parent" attribute="providerName" depth="1" /> - <transportID source="parent" attribute="transportID" depth="1" /> - <type source="parent" attribute="type" depth="1" /> - <defaultAuthority source="parent" attribute="defaultAuthority" depth="1" /> - </columns> - </p2:view> -</block> - diff --git a/p2pvr/daemon/Jamfile.jam b/p2pvr/daemon/Jamfile.jam new file mode 100644 index 0000000..7379c7a --- /dev/null +++ b/p2pvr/daemon/Jamfile.jam @@ -0,0 +1,10 @@ +lib Ice ; +lib IceUtil ; + +lib p2pvrdaemon : + [ glob *.cpp ] + : + <library>..//p2daemonlib + <library>../ice//p2pvrice + <library>../lib//p2pvrlib + ; diff --git a/p2pvr/daemon/daemon.cpp b/p2pvr/daemon/daemon.cpp new file mode 100644 index 0000000..de470d3 --- /dev/null +++ b/p2pvr/daemon/daemon.cpp @@ -0,0 +1,64 @@ +#include <daemon.h> +#include <options.h> +#include <Ice/Ice.h> +#include "localDevices.h" +#include "globalDevices.h" +#include "maintenance.h" +#include "si.h" +#include <logger.h> + +#include <linux/dvb/frontend.h> + +class P2PvrDaemon : public Daemon { + public: + P2PvrDaemon(int argc, char ** argv) : + ic(Ice::initialize(argc, argv)) + { + } + + ~P2PvrDaemon() + { + ic->destroy(); + } + + void run() const + { + Logger()->messagebf(LOG_INFO, "Creating adapter (%s, %s)", Adapter, Endpoint); + auto adapter = ic->createObjectAdapterWithEndpoints(Adapter, Endpoint); + adapter->add(new LocalDevices(), ic->stringToIdentity("Devices")); + adapter->add(new GlobalDevices(), ic->stringToIdentity("GlobalDevices")); + adapter->add(new Maintenance(), ic->stringToIdentity("Maintenance")); + adapter->add(new SI(), ic->stringToIdentity("SI")); + adapter->activate(); + + auto maint = P2PVR::MaintenancePrx::checkedCast(adapter->createProxy(ic->stringToIdentity("Maintenance"))); + maint->UpdateTransports(FE_OFDM); + maint->UpdateServices(FE_OFDM); + maint->UpdateEvents(FE_OFDM); + + //ic->waitForShutdown(); + } + + void shutdown() const + { + ic->shutdown(); + } + INITOPTIONS; + + private: + Ice::CommunicatorPtr ic; + + static std::string Adapter; + static std::string Endpoint; +}; + +std::string P2PvrDaemon::Adapter; +std::string P2PvrDaemon::Endpoint; + +DECLARE_GENERIC_LOADER("p2pvrdaemon", DaemonLoader, P2PvrDaemon); + +DECLARE_OPTIONS(P2PvrDaemon, "P2PVR Daemon") +("p2pvr.daemon.iceadapter", Options::value(&Adapter, "DefaultAdapter"), "ICE Adapter name") +("p2pvr.daemon.iceendpoint", Options::value(&Endpoint, "default -p 10000"), "ICE Endpoint address") +END_OPTIONS(P2PvrDaemon); + diff --git a/p2pvr/datasources/postgres.xml b/p2pvr/datasources/postgres.xml index 7d7440e..c538a8c 100644 --- a/p2pvr/datasources/postgres.xml +++ b/p2pvr/datasources/postgres.xml @@ -1,8 +1,4 @@ <?xml version="1.0"?> <project2:rdbmsdatasource xmlns:project2="http://project2.randomdan.homeip.net" name="postgres"> - <readonly preferlocal="true"> - <dsn host="defiant" provider="postgresql" dsn="host=defiant sslmode=disable user=p2tv dbname=gentoo options='-c search_path=p2tv,public'" /> - <dsn host="firebrand" provider="postgresql" dsn="host=firebrand sslmode=disable user=p2tv dbname=gentoo options='-c search_path=p2tv,public'" /> - </readonly> - <masterdsn host="defiant" provider="postgresql" dsn="host=defiant sslmode=disable user=p2tv dbname=gentoo options='-c search_path=p2tv,public'" /> + <masterdsn provider="postgresql" dsn="user=gentoo dbname=p2pvr" /> </project2:rdbmsdatasource> diff --git a/p2pvr/datasources/schema.sql b/p2pvr/datasources/schema.sql new file mode 100644 index 0000000..c628bf0 --- /dev/null +++ b/p2pvr/datasources/schema.sql @@ -0,0 +1,382 @@ +-- +-- PostgreSQL database dump +-- + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SET check_function_bodies = false; +SET client_min_messages = warning; + +-- +-- Name: plpgsql; Type: EXTENSION; Schema: -; Owner: +-- + +CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog; + + +-- +-- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner: +-- + +COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language'; + + +SET search_path = public, pg_catalog; + +SET default_tablespace = ''; + +SET default_with_oids = false; + +-- +-- Name: delivery_dvbc; Type: TABLE; Schema: public; Owner: gentoo; Tablespace: +-- + +CREATE TABLE delivery_dvbc ( + frequency bigint NOT NULL, + transportstreamid integer NOT NULL, + fecouter smallint, + modulation smallint, + symbolrate integer, + fecinner smallint +); + + +ALTER TABLE public.delivery_dvbc OWNER TO gentoo; + +-- +-- Name: delivery_dvbs; Type: TABLE; Schema: public; Owner: gentoo; Tablespace: +-- + +CREATE TABLE delivery_dvbs ( + frequency bigint NOT NULL, + transportstreamid integer NOT NULL, + orbitalposition integer, + westeastflag boolean, + polarization smallint, + rolloff smallint, + modulationsystem boolean, + modulationtype smallint, + symbolrate integer, + fecinner smallint +); + + +ALTER TABLE public.delivery_dvbs OWNER TO gentoo; + +-- +-- Name: delivery_dvbt; Type: TABLE; Schema: public; Owner: gentoo; Tablespace: +-- + +CREATE TABLE delivery_dvbt ( + frequency bigint NOT NULL, + transportstreamid integer NOT NULL, + bandwidth smallint, + priority boolean, + timeslicing boolean, + mpefec boolean, + constellation smallint, + hierarchy smallint, + coderatehp smallint, + coderatelp smallint, + guardinterval smallint, + transmissionmode smallint, + otherfrequencyflag boolean +); + + +ALTER TABLE public.delivery_dvbt OWNER TO gentoo; + +-- +-- Name: event_schedule; Type: TABLE; Schema: public; Owner: gentoo; Tablespace: +-- + +CREATE TABLE event_schedule ( + serviceid integer NOT NULL, + eventid integer NOT NULL, + scheduleid integer NOT NULL +); + + +ALTER TABLE public.event_schedule OWNER TO gentoo; + +-- +-- Name: events; Type: TABLE; Schema: public; Owner: gentoo; Tablespace: +-- + +CREATE TABLE events ( + serviceid integer NOT NULL, + eventid integer NOT NULL, + title text, + titlelang text, + subtitle text, + description text, + descriptionlang text, + videoaspect smallint, + videoframerate smallint, + videohd boolean, + audiochannels smallint, + audiolanguage text, + subtitlelanguage text, + category smallint, + subcategory smallint, + usercategory smallint, + dvbrating smallint, + starttime timestamp without time zone, + stoptime timestamp without time zone, + episode smallint, + episodes smallint, + year smallint, + flags text +); + + +ALTER TABLE public.events OWNER TO gentoo; + +-- +-- Name: networks; Type: TABLE; Schema: public; Owner: gentoo; Tablespace: +-- + +CREATE TABLE networks ( + networkid integer NOT NULL, + name text NOT NULL +); + + +ALTER TABLE public.networks OWNER TO gentoo; + +-- +-- Name: schedules; Type: TABLE; Schema: public; Owner: gentoo; Tablespace: +-- + +CREATE TABLE schedules ( + scheduleid integer NOT NULL, + serviceid integer, + eventid integer, + title text, + search text +); + + +ALTER TABLE public.schedules OWNER TO gentoo; + +-- +-- Name: schedules_scheduleid_seq; Type: SEQUENCE; Schema: public; Owner: gentoo +-- + +CREATE SEQUENCE schedules_scheduleid_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public.schedules_scheduleid_seq OWNER TO gentoo; + +-- +-- Name: schedules_scheduleid_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: gentoo +-- + +ALTER SEQUENCE schedules_scheduleid_seq OWNED BY schedules.scheduleid; + + +-- +-- Name: services; Type: TABLE; Schema: public; Owner: gentoo; Tablespace: +-- + +CREATE TABLE services ( + serviceid integer NOT NULL, + name text, + providername text, + type smallint, + defaultauthority text, + runningstatus smallint, + eitschedule boolean, + eitpresentfollowing boolean, + freecamode boolean, + transportstreamid integer NOT NULL +); + + +ALTER TABLE public.services OWNER TO gentoo; + +-- +-- Name: transportstreams; Type: TABLE; Schema: public; Owner: gentoo; Tablespace: +-- + +CREATE TABLE transportstreams ( + transportstreamid integer NOT NULL, + networkid integer NOT NULL, + originalnetworkid integer +); + + +ALTER TABLE public.transportstreams OWNER TO gentoo; + +-- +-- Name: scheduleid; Type: DEFAULT; Schema: public; Owner: gentoo +-- + +ALTER TABLE ONLY schedules ALTER COLUMN scheduleid SET DEFAULT nextval('schedules_scheduleid_seq'::regclass); + + +-- +-- Name: pk_delivery_dvbc; Type: CONSTRAINT; Schema: public; Owner: gentoo; Tablespace: +-- + +ALTER TABLE ONLY delivery_dvbc + ADD CONSTRAINT pk_delivery_dvbc PRIMARY KEY (frequency); + + +-- +-- Name: pk_delivery_dvbs; Type: CONSTRAINT; Schema: public; Owner: gentoo; Tablespace: +-- + +ALTER TABLE ONLY delivery_dvbs + ADD CONSTRAINT pk_delivery_dvbs PRIMARY KEY (frequency); + + +-- +-- Name: pk_delivery_dvbt; Type: CONSTRAINT; Schema: public; Owner: gentoo; Tablespace: +-- + +ALTER TABLE ONLY delivery_dvbt + ADD CONSTRAINT pk_delivery_dvbt PRIMARY KEY (frequency); + + +-- +-- Name: pk_events; Type: CONSTRAINT; Schema: public; Owner: gentoo; Tablespace: +-- + +ALTER TABLE ONLY events + ADD CONSTRAINT pk_events PRIMARY KEY (serviceid, eventid); + + +-- +-- Name: pk_eventschedule; Type: CONSTRAINT; Schema: public; Owner: gentoo; Tablespace: +-- + +ALTER TABLE ONLY event_schedule + ADD CONSTRAINT pk_eventschedule PRIMARY KEY (eventid, scheduleid); + + +-- +-- Name: pk_networks; Type: CONSTRAINT; Schema: public; Owner: gentoo; Tablespace: +-- + +ALTER TABLE ONLY networks + ADD CONSTRAINT pk_networks PRIMARY KEY (networkid); + + +-- +-- Name: pk_schedules; Type: CONSTRAINT; Schema: public; Owner: gentoo; Tablespace: +-- + +ALTER TABLE ONLY schedules + ADD CONSTRAINT pk_schedules PRIMARY KEY (scheduleid); + + +-- +-- Name: pk_services; Type: CONSTRAINT; Schema: public; Owner: gentoo; Tablespace: +-- + +ALTER TABLE ONLY services + ADD CONSTRAINT pk_services PRIMARY KEY (serviceid); + + +-- +-- Name: pk_transportstreams; Type: CONSTRAINT; Schema: public; Owner: gentoo; Tablespace: +-- + +ALTER TABLE ONLY transportstreams + ADD CONSTRAINT pk_transportstreams PRIMARY KEY (transportstreamid); + + +-- +-- Name: fk_delivery_dvbc_transportstream; Type: FK CONSTRAINT; Schema: public; Owner: gentoo +-- + +ALTER TABLE ONLY delivery_dvbc + ADD CONSTRAINT fk_delivery_dvbc_transportstream FOREIGN KEY (transportstreamid) REFERENCES transportstreams(transportstreamid) ON UPDATE CASCADE ON DELETE CASCADE; + + +-- +-- Name: fk_delivery_dvbs_transportstream; Type: FK CONSTRAINT; Schema: public; Owner: gentoo +-- + +ALTER TABLE ONLY delivery_dvbs + ADD CONSTRAINT fk_delivery_dvbs_transportstream FOREIGN KEY (transportstreamid) REFERENCES transportstreams(transportstreamid) ON UPDATE CASCADE ON DELETE CASCADE; + + +-- +-- Name: fk_delivery_dvbt_transportstream; Type: FK CONSTRAINT; Schema: public; Owner: gentoo +-- + +ALTER TABLE ONLY delivery_dvbt + ADD CONSTRAINT fk_delivery_dvbt_transportstream FOREIGN KEY (transportstreamid) REFERENCES transportstreams(transportstreamid) ON UPDATE CASCADE ON DELETE CASCADE; + + +-- +-- Name: fk_eventschedule; Type: FK CONSTRAINT; Schema: public; Owner: gentoo +-- + +ALTER TABLE ONLY event_schedule + ADD CONSTRAINT fk_eventschedule FOREIGN KEY (serviceid, eventid) REFERENCES events(serviceid, eventid) ON DELETE CASCADE; + + +-- +-- Name: fk_eventschedule_schedule; Type: FK CONSTRAINT; Schema: public; Owner: gentoo +-- + +ALTER TABLE ONLY event_schedule + ADD CONSTRAINT fk_eventschedule_schedule FOREIGN KEY (scheduleid) REFERENCES schedules(scheduleid) ON UPDATE CASCADE ON DELETE CASCADE; + + +-- +-- Name: fk_schedule_event; Type: FK CONSTRAINT; Schema: public; Owner: gentoo +-- + +ALTER TABLE ONLY schedules + ADD CONSTRAINT fk_schedule_event FOREIGN KEY (serviceid, eventid) REFERENCES events(serviceid, eventid) ON DELETE CASCADE; + + +-- +-- Name: fk_schedule_service; Type: FK CONSTRAINT; Schema: public; Owner: gentoo +-- + +ALTER TABLE ONLY schedules + ADD CONSTRAINT fk_schedule_service FOREIGN KEY (serviceid) REFERENCES services(serviceid) ON DELETE CASCADE; + + +-- +-- Name: fk_services_transportstreams; Type: FK CONSTRAINT; Schema: public; Owner: gentoo +-- + +ALTER TABLE ONLY services + ADD CONSTRAINT fk_services_transportstreams FOREIGN KEY (transportstreamid) REFERENCES transportstreams(transportstreamid) ON UPDATE CASCADE ON DELETE CASCADE; + + +-- +-- Name: fk_transportstreams_network; Type: FK CONSTRAINT; Schema: public; Owner: gentoo +-- + +ALTER TABLE ONLY transportstreams + ADD CONSTRAINT fk_transportstreams_network FOREIGN KEY (networkid) REFERENCES networks(networkid) ON UPDATE CASCADE ON DELETE CASCADE; + + +-- +-- Name: public; Type: ACL; Schema: -; Owner: postgres +-- + +REVOKE ALL ON SCHEMA public FROM PUBLIC; +REVOKE ALL ON SCHEMA public FROM postgres; +GRANT ALL ON SCHEMA public TO postgres; +GRANT ALL ON SCHEMA public TO PUBLIC; + + +-- +-- PostgreSQL database dump complete +-- + diff --git a/p2pvr/doc/en_300468v011101p.pdf b/p2pvr/doc/en_300468v011101p.pdf new file mode 100644 index 0000000..6aae6b2 Binary files /dev/null and b/p2pvr/doc/en_300468v011101p.pdf differ diff --git a/p2pvr/ice/Jamfile.jam b/p2pvr/ice/Jamfile.jam index 9055b2e..219c508 100644 --- a/p2pvr/ice/Jamfile.jam +++ b/p2pvr/ice/Jamfile.jam @@ -1,13 +1,16 @@ -import package ; - -lib Ice : : <name>Ice ; -lib IceUtil : : <name>IceUtil ; -lib pthread : : <name>pthread ; +lib Ice ; +lib IceUtil ; +lib pthread ; lib p2pvrice : [ glob *.ice ] : <library>Ice <library>IceUtil <library>pthread + : : + <include>. + <library>Ice + <library>IceUtil + <library>pthread ; diff --git a/p2pvr/ice/commonHelpers.h b/p2pvr/ice/commonHelpers.h new file mode 100644 index 0000000..b54523e --- /dev/null +++ b/p2pvr/ice/commonHelpers.h @@ -0,0 +1,22 @@ +#ifndef ICE_COMMON_HELPERS_H +#define ICE_COMMON_HELPERS_H + +#include <p2pvr.h> +#include <ostream> + +namespace Common { + template<typename C, typename T> + std::basic_ostream<C, T> & + operator<<(std::basic_ostream<C, T> & o, const Common::DateTime & dt) + { + o << std::setw(4) << std::setfill('0') << dt.Year + << "-" << std::setw(2) << std::setfill('0') << dt.Month + << "-" << std::setw(2) << std::setfill('0') << dt.Day + << "T" << std::setw(2) << std::setfill('0') << dt.Hour + << ":" << std::setw(2) << std::setfill('0') << dt.Minute; + return o; + } +} + +#endif + diff --git a/p2pvr/ice/p2pvr.ice b/p2pvr/ice/p2pvr.ice index aaa01bf..250cc37 100644 --- a/p2pvr/ice/p2pvr.ice +++ b/p2pvr/ice/p2pvr.ice @@ -1,40 +1,232 @@ -module P2TV { +module Common { + struct DateTime { + short Year; + short Month; + short Day; + short Hour; + short Minute; + }; + + struct TimeOfDay { + short Hour; + short Minute; + }; +}; + +// This attempts to define an ICE representation of domain objects contained +// within the DVB SI (Digital Video Broadcasting specification for Service +// Information - EN 300 468 v1.11.1) + +module DVBSI { + // Descriptors + class Delivery { + long Frequency; + }; + + class SatelliteDelivery extends Delivery { + int OrbitalPosition; + bool WestEastFlag; + short Polarization; + short RollOff; + bool ModulationSystem; + short ModulationType; + int SymbolRate; + short FecInner; + }; + + class CableDelivery extends Delivery { + short FecOuter; + short Modulation; + int SymbolRate; + short FecInner; + }; + + class TerrestrialDelivery extends Delivery { + short Bandwidth; + bool Priority; + bool TimeSlicing; + bool MpeFec; + short Constellation; + short Hierarchy; + short CodeRateHP; + short CodeRateLP; + short GuardInterval; + short TransmissionMode; + bool OtherFrequencyFlag; + }; + + struct NetworkService { + int ServiceId; + short ServiceType; + }; + sequence<NetworkService> NetworkServiceList; + + class NetworkTransportStream { + int TransportStreamId; + int OriginalNetworkId; + SatelliteDelivery Satellite; + CableDelivery Cable; + TerrestrialDelivery Terrestrial; + NetworkServiceList Services; + }; + sequence<NetworkTransportStream> NetworkTransportStreams; + + class Network { + int NetworkId; + optional(1) string Name; + NetworkTransportStreams TransportStreams; + }; + + class BouquetTransportStream { + int TransportStreamId; + int OriginalNetworkId; + }; + sequence <BouquetTransportStream> BouquetTransportStreamList; + + class Bouquet { + int BouquetId; + optional(1) string Name; + optional(2) string DefaultAuthority; + BouquetTransportStreamList Streams; + }; + + class Service { + int ServiceId; + bool EitSchedule; + bool EitPresentFollowing; + short RunningStatus; + bool FreeCaMode; + optional(1) string ProviderName; + optional(2) string Name; + optional(3) short Type; + optional(4) string DefaultAuthority; + }; + sequence<Service> ServiceList; + + class TransportStream { + int TransportStreamId; + int OriginalNetworkId; + ServiceList Services; + }; + + class EitInformation { + int ServiceId; + int TransportStreamId; + int OriginalNetworkId; + }; + + class Event { + int ServiceId; + int EventId; + string Title; + string TitleLang; + optional(1) string Subtitle; + optional(2) string Description; + optional(3) string DescriptionLang; + Common::DateTime StartTime; + Common::DateTime StopTime; + optional(4) short Episode; + optional(5) short Episodes; + optional(6) short Year; + optional(7) string Flags; + optional(8) short DvbRating; + optional(9) short VideoAspect; + optional(10) short VideoFrameRate; + optional(11) bool VideoHD; + optional(12) short AudioChannels; + optional(13) string AudioLanguage; + optional(14) string SubtitleLanguage; + optional(15) short Category; + optional(16) short SubCategory; + optional(17) short UserCategory; + }; +}; +module P2PVR { + exception DeviceError { + string Device; + string Message; + int Errno; + }; + exception IncorrectDeliveryType { }; + + // Event in the database, it has a unique Id. + class Program extends DVBSI::Event { + int ProgramId; + }; + sequence<Program> ProgramList; + + // Something that we have recorded. struct Recording { - int recordingId; + int RecordingId; string Title; string Subtitle; string Description; - long startTime; - long endTime; - long fileSize; - int serviceId; + Common::DateTime StartTime; + Common::DateTime EndTime; + long FileSize; + int ServiceId; }; sequence<Recording> RecordingList; - struct Schedule { - int scheduleId; + // Something that defines what we would like to record. + class Schedule { + int ScheduleId; + optional(1) string Title; + optional(2) string Subtitle; + optional(3) int ServiceId; + optional(4) Common::TimeOfDay Time; + optional(5) string Search; + bool Repeats; }; sequence<Schedule> ScheduleList; - - struct Program { - int programId; - int serviceId; - int eventId; - string Title; - string Subtitle; - string Description; - long startTime; - long endTime; + + sequence<byte> Data; + sequence<short> PacketIds; + + interface RawDataClient { + bool NewData(Data bytes); + }; + + interface Tuner { + idempotent int GetStatus(); + + idempotent void SendNetworkInformation(RawDataClient * client); + idempotent void SendBouquetAssociations(RawDataClient * client); + idempotent void SendServiceDescriptions(RawDataClient * client); + idempotent void SendEventInformation(RawDataClient * client); + + int StartSendingTS(PacketIds pids, RawDataClient * client); + idempotent void StopSendingTS(int handle); + }; + interface PrivateTuner extends Tuner { + idempotent void TuneTo(DVBSI::Delivery d); + idempotent void ScanAndSendNetworkInformation(RawDataClient * client); }; - sequence<Program> ProgramList; - struct Service { - int serviceId; - string name; - string authority; + interface Devices { + // Get a tuner that is tuned to <del>, acquire and tune to <del> if required. + Tuner * GetTunerSpecific(DVBSI::Delivery del, long until); + // Get any tuner that is tuned, acquire and tune to <del> if required. + Tuner * GetTunerAny(short type, DVBSI::Delivery del, long until); + // Get a private tuner, not shared or sharable + PrivateTuner * GetPrivateTuner(short type, long until); + // Release a tuner when no longer required. + idempotent void ReleaseTuner(Tuner * t); + }; + + interface LocalDevices extends Devices { + idempotent void Scan(); + idempotent void Add(string frontend); + idempotent void Remove(string frontend); }; - sequence<Service> ServiceList; + interface Maintenance { + idempotent void UpdateAll(); + idempotent void UpdateTransports(short type); + idempotent void UpdateServices(short type); + idempotent void UpdateEvents(short type); + }; + interface Recordings { idempotent void DeleteRecording(int recordingId); idempotent RecordingList GetRecordings(); @@ -47,10 +239,10 @@ module P2TV { idempotent void DoReschedule(); }; - interface EPG { - idempotent ServiceList GetServices(); - idempotent ProgramList GetPrograms(int serviceId, int limit); - idempotent ProgramList GetNowAndNext(); + sequence<DVBSI::Delivery> Deliveries; + interface SI { + // Get any delivery suitable for SI reading + idempotent Deliveries GetAllDeliveries(short type); }; }; diff --git a/p2pvr/lib/Jamfile.jam b/p2pvr/lib/Jamfile.jam new file mode 100644 index 0000000..b05e572 --- /dev/null +++ b/p2pvr/lib/Jamfile.jam @@ -0,0 +1,18 @@ +lib boost_system ; +lib boost_filesystem ; + +lib p2pvrlib : + [ glob-tree *.cpp ] + ../../libmisc/misc.cpp + : + <library>boost_system + <library>boost_filesystem + <library>../ice//p2pvrice + <library>..//p2common + <library>..//p2sql + <implicit-dependency>../ice//p2pvrice + : : + <library>boost_filesystem + <implicit-dependency>../ice//p2pvrice + <include>. + ; diff --git a/p2pvr/lib/containerCreator.h b/p2pvr/lib/containerCreator.h new file mode 100644 index 0000000..aed2479 --- /dev/null +++ b/p2pvr/lib/containerCreator.h @@ -0,0 +1,32 @@ +#ifndef CONTAINERCREATOR_H +#define CONTAINERCREATOR_H + +#include <boost/function.hpp> + +template <typename T, typename V> +class ContainerCreator { + public: + ContainerCreator(T & c) : container(c) { } + + void populate( + boost::function<bool()> fetch, + boost::function<VariableType(unsigned int)> get, + boost::function<unsigned int()> columns) + { + while (fetch()) { + auto v = new V; + container.push_back(v); + ObjectRowState<IceInternal::Handle<V>> rs; + const auto columnCount = columns(); + for (unsigned int c = 0; c < columnCount; c++) { + rs.fields[c] = get(c); + } + UnbindColumns<IceInternal::Handle<V>>(rs, v); + } + } + private: + T & container; +}; + +#endif + diff --git a/p2pvr/lib/containerIterator.h b/p2pvr/lib/containerIterator.h new file mode 100644 index 0000000..1c4e6bf --- /dev/null +++ b/p2pvr/lib/containerIterator.h @@ -0,0 +1,41 @@ +#ifndef CONTAINERITERATOR_H +#define CONTAINERITERATOR_H + +#include <iHaveSubTasks.h> +#include <boost/foreach.hpp> +#include "objectRowState.h" + +template <typename T> +class ContainerIterator : public IHaveSubTasks { + public: + template <typename ... Parents> + ContainerIterator(const T * con, const Parents & ... p) : + SourceObject(__PRETTY_FUNCTION__), + IHaveSubTasks(NULL), + binder(boost::bind(&BindColumns<typename T::value_type, Parents...>, _1, _2, p...)), + container(con) + { + } + void execute(ExecContext * ec) const + { + ObjectRowState<typename T::value_type> rs; + BOOST_FOREACH(const auto & i, *container) { + binder(rs, i); + rs.process(boost::bind(&ContainerIterator::executeChildren, this, ec)); + } + } + + private: + boost::function<void(RowState &, const typename T::value_type &)> binder; + const T * container; + + void executeChildren(ExecContext * ec) const + { + BOOST_FOREACH(const Tasks::value_type & sq, normal) { + sq->execute(ec); + } + } +}; + +#endif + diff --git a/p2pvr/lib/dvbsiHelpers.cpp b/p2pvr/lib/dvbsiHelpers.cpp new file mode 100644 index 0000000..3dcbdcc --- /dev/null +++ b/p2pvr/lib/dvbsiHelpers.cpp @@ -0,0 +1,190 @@ +#include "dvbsiHelpers.h" +#include "p2Helpers.h" + +template<> +void +CreateColumns<DVBSI::NetworkPtr>(const ColumnCreator & cc) +{ + cc("networkId", true); + cc("name", false); +} + +template<> +void +BindColumns(RowState & rs, const DVBSI::NetworkPtr & network) +{ + rs.fields[0] << network->NetworkId; + rs.fields[1] << network->Name; +} + +template<> +void +CreateColumns<DVBSI::NetworkTransportStreamPtr>(const ColumnCreator & cc) +{ + cc("transportStreamId", true); + cc("networkId", false); + cc("originalNetworkId", false); +} + +template<> +void +BindColumns(RowState & rs, const DVBSI::NetworkTransportStreamPtr & ts, const DVBSI::NetworkPtr & network) +{ + rs.fields[0] << ts->TransportStreamId; + rs.fields[1] << network->NetworkId; + rs.fields[2] << ts->OriginalNetworkId; +} + +template<> +void +CreateColumns<DVBSI::NetworkService>(const ColumnCreator & cc) +{ + cc("serviceId", true); + cc("type", false); + cc("transportStreamId", false); +} + +template<> +void +BindColumns(RowState & rs, const DVBSI::NetworkService & s, const DVBSI::NetworkTransportStreamPtr & ts) +{ + rs.fields[0] << s.ServiceId; + rs.fields[1] << s.ServiceType; + rs.fields[2] << ts->TransportStreamId; +} + +template<> +void +CreateColumns<DVBSI::ServicePtr>(const ColumnCreator & cc) +{ + cc("serviceId", true); + cc("transportStreamId", true); + cc("name", false); + cc("providerName", false); + cc("defaultAuthority", false); +} + +template<> +void +BindColumns(RowState & rs, const DVBSI::ServicePtr & s, const DVBSI::TransportStreamPtr & ts) +{ + rs.fields[0] << s->ServiceId; + rs.fields[1] << ts->TransportStreamId; + rs.fields[2] << s->Name; + rs.fields[4] << s->ProviderName; + rs.fields[4] << s->DefaultAuthority; +} + +template<> +void +CreateColumns<DVBSI::EventPtr>(const ColumnCreator & cc) +{ + cc("serviceId", true); + cc("eventId", true); + cc("title", false); + cc("titleLang", false); + cc("subtitle", false); + cc("description", false); + cc("descriptionLang", false); + cc("videoAspect", false); + cc("videoFrameRate", false); + cc("videoHd", false); + cc("audioChannels", false); + cc("audioLanguage", false); + cc("subtitleLanguage", false); + cc("category", false); + cc("subCategory", false); + cc("userCategory", false); + cc("dvbRating", false); + cc("startTime", false); + cc("stopTime", false); + cc("episode", false); + cc("episodes", false); + cc("year", false); + cc("flags", false); +} + +template<> +void +BindColumns(RowState & rs, const DVBSI::EventPtr & e) +{ + rs.fields[0] << e->ServiceId; + rs.fields[1] << e->EventId; + rs.fields[2] << e->Title; + rs.fields[3] << e->TitleLang; + rs.fields[4] << e->Subtitle; + rs.fields[5] << e->Description; + rs.fields[6] << e->DescriptionLang; + rs.fields[7] << e->VideoAspect; + rs.fields[8] << e->VideoFrameRate; + rs.fields[9] << e->VideoHD; + rs.fields[10] << e->AudioChannels; + rs.fields[11] << e->AudioLanguage; + rs.fields[12] << e->SubtitleLanguage; + rs.fields[13] << e->Category; + rs.fields[14] << e->SubCategory; + rs.fields[15] << e->UserCategory; + rs.fields[16] << e->DvbRating; + rs.fields[17] << e->StartTime; + rs.fields[18] << e->StopTime; + rs.fields[19] << e->Episode; + rs.fields[20] << e->Episodes; + rs.fields[21] << e->Year; + rs.fields[22] << e->Flags; +} + +template<> +void +CreateColumns<DVBSI::TerrestrialDeliveryPtr>(const ColumnCreator & cc) +{ + cc("frequency", false); + cc("transportStreamId", true); + cc("bandwidth", false); + cc("priority", false); + cc("timeSlicing", false); + cc("mpeFec", false); + cc("constellation", false); + cc("hierarchy", false); + cc("codeRateHP", false); + cc("codeRateLP", false); + cc("guardInterval", false); + cc("transmissionMode", false); + cc("otherFrequencyFlag", false); +} + +template<> +void +BindColumns(RowState & rs, const DVBSI::TerrestrialDeliveryPtr & s, const DVBSI::NetworkTransportStreamPtr & ts) +{ + rs.fields[0] << s->Frequency; + rs.fields[1] << ts->TransportStreamId; + rs.fields[2] << s->Bandwidth; + rs.fields[3] << s->Priority; + rs.fields[4] << s->TimeSlicing; + rs.fields[5] << s->MpeFec; + rs.fields[6] << s->Constellation; + rs.fields[7] << s->Hierarchy; + rs.fields[8] << s->CodeRateHP; + rs.fields[9] << s->CodeRateLP; + rs.fields[10] << s->GuardInterval; + rs.fields[11] << s->TransmissionMode; + rs.fields[12] << s->OtherFrequencyFlag; +} +template<> +void +UnbindColumns(RowState & rs, DVBSI::TerrestrialDeliveryPtr const & s) +{ + rs.fields[0] >> s->Frequency; + rs.fields[2] >> s->Bandwidth; + rs.fields[3] >> s->Priority; + rs.fields[4] >> s->TimeSlicing; + rs.fields[5] >> s->MpeFec; + rs.fields[6] >> s->Constellation; + rs.fields[7] >> s->Hierarchy; + rs.fields[8] >> s->CodeRateHP; + rs.fields[9] >> s->CodeRateLP; + rs.fields[10] >> s->GuardInterval; + rs.fields[11] >> s->TransmissionMode; + rs.fields[12] >> s->OtherFrequencyFlag; +} + diff --git a/p2pvr/lib/dvbsiHelpers.h b/p2pvr/lib/dvbsiHelpers.h new file mode 100644 index 0000000..49cb404 --- /dev/null +++ b/p2pvr/lib/dvbsiHelpers.h @@ -0,0 +1,60 @@ +#ifndef ICE_DVBSI_HELPERS_H +#define ICE_DVBSI_HELPERS_H + +#include <p2pvr.h> +#include "objectRowState.h" + +template<> +void +CreateColumns<DVBSI::NetworkPtr>(const ColumnCreator & cc); + +template<> +void +BindColumns(RowState & rs, const DVBSI::NetworkPtr & network); + +template<> +void +CreateColumns<DVBSI::NetworkTransportStreamPtr>(const ColumnCreator & cc); + +template<> +void +BindColumns(RowState & rs, const DVBSI::NetworkTransportStreamPtr & ts, const DVBSI::NetworkPtr & network); + +template<> +void +CreateColumns<DVBSI::NetworkService>(const ColumnCreator & cc); + +template<> +void +BindColumns(RowState & rs, const DVBSI::NetworkService & s, const DVBSI::NetworkTransportStreamPtr & ts); + +template<> +void +CreateColumns<DVBSI::ServicePtr>(const ColumnCreator & cc); + +template<> +void +BindColumns(RowState & rs, const DVBSI::ServicePtr & s, const DVBSI::TransportStreamPtr & ts); + +template<> +void +CreateColumns<DVBSI::EventPtr>(const ColumnCreator & cc); + +template<> +void +BindColumns(RowState & rs, const DVBSI::EventPtr & e); + +template<> +void +CreateColumns<DVBSI::TerrestrialDeliveryPtr>(const ColumnCreator & cc); + +template<> +void +BindColumns(RowState & rs, const DVBSI::TerrestrialDeliveryPtr & e, const DVBSI::TransportStreamPtr & ts); + +template<> +void +UnbindColumns(RowState & rs, const DVBSI::TerrestrialDeliveryPtr & s); + +#endif + diff --git a/p2pvr/lib/fileHandle.cpp b/p2pvr/lib/fileHandle.cpp new file mode 100644 index 0000000..08575b3 --- /dev/null +++ b/p2pvr/lib/fileHandle.cpp @@ -0,0 +1,27 @@ +#include "fileHandle.h" +#include <unistd.h> +#include <stdexcept> + +class InvalidFileHandle : public std::runtime_error { + public: + InvalidFileHandle() : std::runtime_error("Invalid file handle") { } +}; + +FileHandle::FileHandle(int f) : + fd(f) +{ + if (fd < 0) { + throw InvalidFileHandle(); + } +} + +FileHandle::~FileHandle() +{ + close(fd); +} + +FileHandle::operator int() const +{ + return fd; +} + diff --git a/p2pvr/lib/fileHandle.h b/p2pvr/lib/fileHandle.h new file mode 100644 index 0000000..3c8d45e --- /dev/null +++ b/p2pvr/lib/fileHandle.h @@ -0,0 +1,19 @@ +#ifndef FILEHANDLE_H +#define FILEHANDLE_H + +class FileHandle { + public: + FileHandle(int fd); + ~FileHandle(); + + FileHandle(const FileHandle &) = delete; + void operator=(const FileHandle &) = delete; + + operator int() const; + + private: + const int fd; +}; + +#endif + diff --git a/p2pvr/lib/frontend.cpp b/p2pvr/lib/frontend.cpp new file mode 100644 index 0000000..1044f6f --- /dev/null +++ b/p2pvr/lib/frontend.cpp @@ -0,0 +1,35 @@ +#include "frontend.h" +#include "tuner.h" +#include <logger.h> +#include <sys/ioctl.h> +#include <linux/dvb/frontend.h> + +Frontend::Frontend(Tuner * t, int fd, const struct dvb_frontend_info & i) : + tuner(t), + frontendFD(fd), + fe_info(i) +{ +} + +Frontend::~Frontend() +{ + close(frontendFD); +} + +const struct dvb_frontend_info & +Frontend::Info() const +{ + return fe_info; +} + +fe_status +Frontend::GetStatus() const +{ + fe_status_t status; + if (ioctl(frontendFD, FE_READ_STATUS, &status) < 0) { + Logger()->messagebf(LOG_ERR, "Reading frontend %s status failed (%s:%d)", tuner->Device(), strerror(errno), errno); + throw P2PVR::DeviceError(tuner->Device(), strerror(errno), errno); + } + return status; +} + diff --git a/p2pvr/lib/frontend.h b/p2pvr/lib/frontend.h new file mode 100644 index 0000000..994abfa --- /dev/null +++ b/p2pvr/lib/frontend.h @@ -0,0 +1,32 @@ +#ifndef P2PVR_FRONTEND_H +#define P2PVR_FRONTEND_H + +#include <linux/dvb/frontend.h> +#include <genLoader.h> +#include <p2pvr.h> + +class Tuner; + +class Frontend { + public: + typedef boost::function<bool(long)> OnFrequencyFound; + Frontend(Tuner *, int fd, const struct dvb_frontend_info &); + virtual ~Frontend(); + + fe_status_t GetStatus() const; + virtual void TuneTo(const DVBSI::DeliveryPtr &) const = 0; + virtual void FrequencyScan(const OnFrequencyFound & off) const = 0; + virtual std::string Type() const = 0; + const struct dvb_frontend_info & Info() const; + + protected: + const Tuner * tuner; + const int frontendFD; + const struct dvb_frontend_info fe_info; +}; + +typedef GenLoader<Frontend, fe_type, Tuner *, int, const struct dvb_frontend_info &> FrontendLoader; +typedef boost::shared_ptr<Frontend> FrontendPtr; + +#endif + diff --git a/p2pvr/lib/frontends/ofdm.cpp b/p2pvr/lib/frontends/ofdm.cpp new file mode 100644 index 0000000..8e2b23c --- /dev/null +++ b/p2pvr/lib/frontends/ofdm.cpp @@ -0,0 +1,145 @@ +#include "../frontend.h" +#include "../tuner.h" +#include <sys/ioctl.h> +#include <logger.h> +#include <linux/dvb/frontend.h> + +#define FREQ_OFFSET_MIN 0 +#define FREQ_OFFSET_MAX 4 + +class Frontend_OFDM : public Frontend { + public: + Frontend_OFDM(Tuner * t, int fd, const struct dvb_frontend_info & i) : Frontend(t, fd, i) { } + + void TuneTo(const DVBSI::DeliveryPtr & mp) const + { + auto td = DVBSI::TerrestrialDeliveryPtr::dynamicCast(mp); + if (!td) { + throw P2PVR::IncorrectDeliveryType(); + } + dvb_frontend_parameters feparams; + memset(&feparams, 0, sizeof(dvb_frontend_parameters)); + feparams.frequency = td->Frequency; + feparams.inversion = INVERSION_OFF; + feparams.u.ofdm.bandwidth = (fe_bandwidth)td->Bandwidth; + feparams.u.ofdm.code_rate_HP = (fe_code_rate_t)td->CodeRateHP; + feparams.u.ofdm.code_rate_LP = (fe_code_rate_t)td->CodeRateLP; + feparams.u.ofdm.constellation = (fe_modulation_t)td->Constellation; + feparams.u.ofdm.transmission_mode = (fe_transmit_mode)td->TransmissionMode; + feparams.u.ofdm.guard_interval = (fe_guard_interval_t)td->GuardInterval; + feparams.u.ofdm.hierarchy_information = (fe_hierarchy_t)td->Hierarchy; + SetParameters(feparams); + } + + dvb_frontend_parameters GetParameters() const + { + dvb_frontend_parameters feparams; + memset(&feparams, 0, sizeof(dvb_frontend_parameters)); + if (ioctl(frontendFD, FE_GET_FRONTEND, &feparams) < 0) { + Logger()->messagebf(LOG_ERR, "Reading frontend parameters failed (%s:%d)", tuner->Device(), strerror(errno), errno); + throw P2PVR::DeviceError(tuner->Device(), strerror(errno), errno); + } + return feparams; + } + + void SetParameters(const dvb_frontend_parameters & feparams) const + { + if (ioctl(frontendFD, FE_SET_FRONTEND, &feparams) < 0) { + Logger()->messagebf(LOG_ERR, "Tuning of device %s failed (%s:%d)", tuner->Device(), strerror(errno), errno); + throw P2PVR::DeviceError(tuner->Device(), strerror(errno), errno); + } + } + + std::string Type() const + { + return "OFDM (DVB-T)"; + } + + enum Country { DVBT_AU, DVBT_DE, DVBT_FR, DVBT_GB }; + + static uint32_t FrequencyForCountry(Country country, int channel) + { + switch (country) { + case DVBT_AU: //AUSTRALIA, 7MHz step list + switch (channel) { + case 5 ... 12: return 142500000; + case 21 ... 69: return 333500000; + } + case DVBT_DE: //GERMANY + case DVBT_FR: //FRANCE, +/- offset 166kHz & +offset 332kHz & +offset 498kHz + case DVBT_GB: //UNITED KINGDOM, +/- offset + switch (channel) { + case 5 ... 12: return 142500000; // VHF unused in FRANCE, skip those in offset loop + case 21 ... 69: return 306000000; + } + } + return 0; + } + static uint32_t FrequencyStepForCountry(Country country, int channel) + { + switch (country) { + case DVBT_AU: + return 7000000; // dvb-t australia, 7MHz step + case DVBT_DE: + case DVBT_FR: + case DVBT_GB: + switch (channel) { // dvb-t europe, 7MHz VHF ch5..12, all other 8MHz + case 5 ... 12: return 7000000; + case 21 ... 69: return 8000000; + } + } + return 0; + } + static uint32_t ChannelFrequencyForCountry(Country country, int channel, int) + { + return FrequencyForCountry(country, channel) + (channel * FrequencyStepForCountry(country, channel)); + } + + void FrequencyScan(const OnFrequencyFound & onFrequencyFound) const + { + struct dvb_frontend_parameters feparams; + memset(&feparams, 0, sizeof(dvb_frontend_parameters)); + feparams.inversion = (fe_info.caps & FE_CAN_INVERSION_AUTO ? INVERSION_AUTO : INVERSION_OFF); + feparams.u.ofdm.constellation = (fe_info.caps & FE_CAN_QAM_AUTO ? QAM_AUTO : QAM_64); + feparams.u.ofdm.hierarchy_information = HIERARCHY_NONE; + + for (int channel = 0; channel < 134; channel += 1) { + for (uint32_t offset = FREQ_OFFSET_MIN; offset <= 0/*FREQ_OFFSET_MAX*/; offset += 1) { + feparams.frequency = ChannelFrequencyForCountry(DVBT_GB, channel, offset); + if (feparams.frequency == 0) { + continue; + } + if (fe_info.frequency_min > feparams.frequency || fe_info.frequency_max < feparams.frequency) { + 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) { + Logger()->messagebf(LOG_INFO, "Found multiplex at %d Hz", feparams.frequency); + Logger()->messagebf(LOG_DEBUG, "frequency %d", feparams.frequency); + if (onFrequencyFound(feparams.frequency)) { + return; + } + } + } + } + } +}; + +DECLARE_GENERIC_LOADER(FE_OFDM, FrontendLoader, Frontend_OFDM); + diff --git a/p2pvr/lib/globalDevices.cpp b/p2pvr/lib/globalDevices.cpp new file mode 100644 index 0000000..8dfe77d --- /dev/null +++ b/p2pvr/lib/globalDevices.cpp @@ -0,0 +1,59 @@ +#include "globalDevices.h" +#include <Ice/Ice.h> + +std::vector<std::string> GlobalDevices::Devices; + +DECLARE_OPTIONS(GlobalDevices, "P2PVR Devices") +("p2pvr.globaldevices.carddaemon", + Options::functions( + [](const VariableType & df) { Devices.push_back(df); }, + []{ Devices.clear(); }), + "ICE address of remote device pools (<adapter>:<endpoint>)") +END_OPTIONS(GlobalDevices); + +P2PVR::TunerPrx +GlobalDevices::GetTunerSpecific(const DVBSI::DeliveryPtr & delivery, Ice::Long until, const Ice::Current & ice) +{ + auto ic = ice.adapter->getCommunicator(); + BOOST_FOREACH(const auto & pool, Devices) { + auto poolprx = P2PVR::DevicesPrx::checkedCast(ic->stringToProxy(pool)); + auto tuner = poolprx->GetTunerSpecific(delivery, until); + if (tuner) return tuner; + } + return NULL; +} + +P2PVR::TunerPrx +GlobalDevices::GetTunerAny(short type, const DVBSI::DeliveryPtr & delivery, Ice::Long until, const Ice::Current & ice) +{ + auto ic = ice.adapter->getCommunicator(); + BOOST_FOREACH(const auto & pool, Devices) { + auto poolprx = P2PVR::DevicesPrx::checkedCast(ic->stringToProxy(pool)); + auto tuner = poolprx->GetTunerAny(type, delivery, until); + if (tuner) return tuner; + } + return NULL; +} + +P2PVR::PrivateTunerPrx +GlobalDevices::GetPrivateTuner(short type, Ice::Long until, const Ice::Current & ice) +{ + auto ic = ice.adapter->getCommunicator(); + BOOST_FOREACH(const auto & pool, Devices) { + auto poolprx = P2PVR::DevicesPrx::checkedCast(ic->stringToProxy(pool)); + auto tuner = poolprx->GetPrivateTuner(type, until); + if (tuner) return tuner; + } + return NULL; +} + +void +GlobalDevices::ReleaseTuner(const P2PVR::TunerPrx & tuner, const Ice::Current & ice) +{ + auto ic = ice.adapter->getCommunicator(); + BOOST_FOREACH(const auto & pool, Devices) { + auto poolprx = P2PVR::DevicesPrx::checkedCast(ic->stringToProxy(pool)); + poolprx->ReleaseTuner(tuner); + } +} + diff --git a/p2pvr/lib/globalDevices.h b/p2pvr/lib/globalDevices.h new file mode 100644 index 0000000..0cf9636 --- /dev/null +++ b/p2pvr/lib/globalDevices.h @@ -0,0 +1,24 @@ +#ifndef GLOBALDEVICES_H +#define GLOBALDEVICES_H + +// Global devices implements a device collection (P2PVR::Devices) for any devices known +// throughout the system through other Devices interfaces + +#include <p2pvr.h> +#include <options.h> + +class GlobalDevices : public P2PVR::Devices { + public: + P2PVR::TunerPrx GetTunerSpecific(const DVBSI::DeliveryPtr &, Ice::Long until, const Ice::Current &); + P2PVR::TunerPrx GetTunerAny(short type, const DVBSI::DeliveryPtr &, Ice::Long until, const Ice::Current &); + P2PVR::PrivateTunerPrx GetPrivateTuner(short type, Ice::Long until, const Ice::Current &); + void ReleaseTuner(const P2PVR::TunerPrx &, const Ice::Current &); + + INITOPTIONS; + private: + static std::vector<std::string> Devices; +}; + +#endif + + diff --git a/p2pvr/lib/localDevices.cpp b/p2pvr/lib/localDevices.cpp new file mode 100644 index 0000000..330271a --- /dev/null +++ b/p2pvr/lib/localDevices.cpp @@ -0,0 +1,151 @@ +#include "localDevices.h" +#include <Ice/Ice.h> +#include "tuner.h" +#include <logger.h> + +LocalDevices::Devices LocalDevices::devices; +std::mutex LocalDevices::lock; + +DECLARE_OPTIONS(LocalDevices, "P2PVR Devices") +("p2pvr.localdevices.frontend", + Options::functions( + [](const VariableType & df) { devices.insert(Devices::value_type(df.as<std::string>(), OpenTunerPtr())); }, + []{ devices.clear(); }), + "Frontend of DVB devices to use (/dev/dvb/adapterX/frontendY)") +END_OPTIONS(LocalDevices); + +P2PVR::TunerPrx +LocalDevices::GetTunerSpecific(const DVBSI::DeliveryPtr & delivery, Ice::Long until, const Ice::Current & ice) +{ + std::lock_guard<std::mutex> g(lock); + Logger()->messagebf(LOG_DEBUG, "%s: Searching for an open sharable tuner (frequency %d)", __PRETTY_FUNCTION__, delivery->Frequency); + auto openTuner = std::find_if(devices.begin(), devices.end(), [delivery](const Devices::value_type & ot) { + return ot.second && !ot.second->openedPrivate && ot.second->delivery && ot.second->delivery->Frequency == delivery->Frequency; + }); + if (openTuner != devices.end()) { + openTuner->second->until = std::max(openTuner->second->until, time(NULL) + until); + openTuner->second->clients += 1; + return openTuner->second->tuner; + } + + openTuner = std::find_if(devices.begin(), devices.end(), [](const Devices::value_type & ot) { return !ot.second; }); + if (openTuner == devices.end()) { + Logger()->messagebf(LOG_DEBUG, "%s: None suitable and none free (frequency %d)", + __PRETTY_FUNCTION__, delivery->Frequency); + return NULL; + } + + 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))); + 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); + return tuner; +} + +P2PVR::TunerPrx +LocalDevices::GetTunerAny(short , const DVBSI::DeliveryPtr & delivery, Ice::Long until, const Ice::Current & ice) +{ + std::lock_guard<std::mutex> g(lock); + Logger()->messagebf(LOG_DEBUG, "%s: Searching for an open sharable tuner any frequency", __PRETTY_FUNCTION__); + auto openTuner = std::find_if(devices.begin(), devices.end(), [delivery](const Devices::value_type & ot) { + return ot.second && !ot.second->openedPrivate && ot.second->delivery; + }); + if (openTuner != devices.end()) { + openTuner->second->until = std::max(openTuner->second->until, time(NULL) + until); + openTuner->second->clients += 1; + return openTuner->second->tuner; + } + + openTuner = std::find_if(devices.begin(), devices.end(), [](const Devices::value_type & ot) { return !ot.second; }); + if (openTuner == devices.end()) { + Logger()->messagebf(LOG_DEBUG, "%s: None suitable and none free (frequency %d)", + __PRETTY_FUNCTION__, delivery->Frequency); + return NULL; + } + + 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))); + 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); + return tuner; +} + +P2PVR::PrivateTunerPrx +LocalDevices::GetPrivateTuner(short , Ice::Long until, const Ice::Current & ice) +{ + std::lock_guard<std::mutex> g(lock); + Logger()->messagebf(LOG_DEBUG, "%s: Opening a private tuner", __PRETTY_FUNCTION__); + auto openTuner = std::find_if(devices.begin(), devices.end(), [](const Devices::value_type & ot) { return !ot.second; }); + if (openTuner == devices.end()) { + Logger()->messagebf(LOG_DEBUG, "%s: None free", __PRETTY_FUNCTION__); + return NULL; + } + + Logger()->messagebf(LOG_DEBUG, "%s: Opening a private tuner (frontend %s)", + __PRETTY_FUNCTION__, openTuner->first); + auto tuner = P2PVR::PrivateTunerPrx::checkedCast(ice.adapter->addWithUUID(new Tuner(openTuner->first))); + openTuner->second = OpenTunerPtr(new OpenTuner(NULL, tuner, until, true)); + + return tuner; +} + +void +LocalDevices::ReleaseTuner(const P2PVR::TunerPrx & tuner, const Ice::Current & ice) +{ + std::lock_guard<std::mutex> g(lock); + Logger()->messagebf(LOG_DEBUG, "%s", __PRETTY_FUNCTION__); + auto openTuner = std::find_if(devices.begin(), devices.end(), [tuner](const Devices::value_type & ot) { + return ot.second && ot.second->tuner == tuner; + }); + auto id = tuner->ice_getIdentity(); + if (ice.adapter->find(id)) { + ice.adapter->remove(id); + } + if (openTuner == devices.end()) { + Logger()->messagebf(LOG_DEBUG, "%s: Not one of mine", __PRETTY_FUNCTION__); + return; + } + Logger()->messagebf(LOG_DEBUG, "%s: Locally owned deivce %s", __PRETTY_FUNCTION__, openTuner->first); + openTuner->second->clients -= 1; + if (openTuner->second->clients == 0) { + openTuner->second.reset(); + } +} + +void +LocalDevices::Scan(const Ice::Current &) +{ + std::lock_guard<std::mutex> g(lock); +} + +void +LocalDevices::Add(const std::string & frontend, const Ice::Current &) +{ + std::lock_guard<std::mutex> g(lock); + devices.insert(Devices::value_type(frontend, OpenTunerPtr())); +} + +void +LocalDevices::Remove(const std::string & frontend, const Ice::Current &) +{ + std::lock_guard<std::mutex> g(lock); + devices.erase(frontend); +} + +LocalDevices::OpenTuner::OpenTuner(DVBSI::DeliveryPtr d, P2PVR::PrivateTunerPrx t, Ice::Long u, bool op) : + openedPrivate(op), + delivery(d), + tuner(t), + until(u), + clients(1) +{ +} + diff --git a/p2pvr/lib/localDevices.h b/p2pvr/lib/localDevices.h new file mode 100644 index 0000000..a16443c --- /dev/null +++ b/p2pvr/lib/localDevices.h @@ -0,0 +1,42 @@ +#ifndef LOCALDEVICES_H +#define LOCALDEVICES_H + +// Local devices implements a device collection (P2PVR::Devices) for any devices physically +// attached to the local machine; that is, can be accessed directly through /dev/dvb/adapterX + +#include <p2pvr.h> +#include <options.h> +#include <mutex> + +class LocalDevices : public P2PVR::LocalDevices { + public: + P2PVR::TunerPrx GetTunerSpecific(const DVBSI::DeliveryPtr &, Ice::Long until, const Ice::Current &); + P2PVR::TunerPrx GetTunerAny(short type, const DVBSI::DeliveryPtr &, Ice::Long until, const Ice::Current &); + P2PVR::PrivateTunerPrx GetPrivateTuner(short type, Ice::Long until, const Ice::Current &); + void ReleaseTuner(const P2PVR::TunerPrx &, const Ice::Current &); + + void Scan(const Ice::Current &); + void Add(const std::string & frontend, const Ice::Current &); + void Remove(const std::string & frontend, const Ice::Current &); + + INITOPTIONS; + private: + class OpenTuner { + public: + OpenTuner(DVBSI::DeliveryPtr, P2PVR::PrivateTunerPrx, Ice::Long, bool); + + const bool openedPrivate; + const DVBSI::DeliveryPtr delivery; + const P2PVR::PrivateTunerPrx tuner; + + Ice::Long until; + unsigned int clients; + }; + typedef boost::shared_ptr<OpenTuner> OpenTunerPtr; + typedef std::map<std::string, OpenTunerPtr> Devices; + static Devices devices; + static std::mutex lock; +}; + +#endif + diff --git a/p2pvr/lib/maintenance.cpp b/p2pvr/lib/maintenance.cpp new file mode 100644 index 0000000..71ddf60 --- /dev/null +++ b/p2pvr/lib/maintenance.cpp @@ -0,0 +1,288 @@ +#include "maintenance.h" +#include "siParsers/network.h" +#include "siParsers/event.h" +#include "siParsers/service.h" +#include <Ice/Ice.h> +#include <stdexcept> +#include <iomanip> +#include <logger.h> +#include <sqlMergeTask.h> +#include <commonObjects.h> +#include <commonHelpers.h> +#include "p2Helpers.h" +#include "dvbsiHelpers.h" +#include "containerIterator.h" +#include "singleIterator.h" +#include "temporaryIceAdapterObject.h" + +#include <linux/dvb/frontend.h> + +void +SqlMergeColumnsInserter(SqlMergeTask * merge, const std::string & name, bool key) +{ + merge->cols.insert(new SqlMergeTask::TargetColumn(name, key)); + if (key) { + merge->keys.insert(name); + } +} + +class SiNetworkInformationMerger : public SiNetworkInformationParser { + public: + SiNetworkInformationMerger(CommonObjects * co) : commonObjects(co) { } + + void HandleTable(DVBSI::NetworkPtr n) + { + Logger()->messagebf(LOG_DEBUG, "Network Id: %d Name: %s", n->NetworkId, *n->Name); + BOOST_FOREACH(const auto & ts, n->TransportStreams) { + Logger()->messagebf(LOG_DEBUG, "\tTransport Stream Id: %d Original Network Id: %d", ts->TransportStreamId, ts->OriginalNetworkId); + BOOST_FOREACH(const auto & s, ts->Services) { + Logger()->messagebf(LOG_DEBUG, "\t\tService Id: %d Service Type: %d", s.ServiceId, s.ServiceType); + } + if (ts->Terrestrial) { + Logger()->messagebf(LOG_DEBUG, "\t\tDVB-T: Frequency: %d", ts->Terrestrial->Frequency); + } + } + + SqlMergeTask mergeNetwork("postgres", "networks"); + CreateColumns<DVBSI::NetworkPtr>(boost::bind(SqlMergeColumnsInserter, &mergeNetwork, _1, _2)); + std::vector<DVBSI::NetworkPtr> networks = { n }; + mergeNetwork.sources.insert(new ContainerIterator<std::vector<DVBSI::NetworkPtr>>(&networks)); + mergeNetwork.loadComplete(commonObjects); + mergeNetwork.execute(NULL); + + SqlMergeTask mergeTransports("postgres", "transportstreams"); + CreateColumns<DVBSI::NetworkTransportStreamPtr>(boost::bind(SqlMergeColumnsInserter, &mergeTransports, _1, _2)); + mergeTransports.sources.insert(new ContainerIterator<DVBSI::NetworkTransportStreams>(&n->TransportStreams, n)); + mergeTransports.loadComplete(commonObjects); + mergeTransports.execute(NULL); + + SqlMergeTask mergeDvbt("postgres", "delivery_dvbt"); + CreateColumns<DVBSI::TerrestrialDeliveryPtr>(boost::bind(SqlMergeColumnsInserter, &mergeDvbt, _1, _2)); + BOOST_FOREACH(const auto & s, n->TransportStreams) { + if (s->Terrestrial) { + mergeDvbt.sources.insert(new SingleIterator<DVBSI::TerrestrialDeliveryPtr>(&s->Terrestrial, s)); + } + } + mergeDvbt.loadComplete(commonObjects); + mergeDvbt.execute(NULL); + + SqlMergeTask mergeServices("postgres", "services"); + CreateColumns<DVBSI::NetworkService>(boost::bind(SqlMergeColumnsInserter, &mergeServices, _1, _2)); + BOOST_FOREACH(const auto & s, n->TransportStreams) { + mergeServices.sources.insert(new ContainerIterator<DVBSI::NetworkServiceList>(&s->Services, s)); + } + mergeServices.loadComplete(commonObjects); + mergeServices.execute(NULL); + } + private: + CommonObjects * commonObjects; +}; + +class SiServicesMerger : public SiServicesParser { + public: + SiServicesMerger(CommonObjects * co) : commonObjects(co) { } + + void HandleTable(DVBSI::TransportStreamPtr ts) + { + Logger()->messagebf(LOG_DEBUG, "Transport Stream Id: %d Original Network Id: %s", ts->TransportStreamId, ts->OriginalNetworkId); + BOOST_FOREACH(const auto & s, ts->Services) { + Logger()->messagebf(LOG_DEBUG, "\tService Id: %d Name: %s Type: %d, Provider: %s, DefaultAuthority: %s, RunningStatus %d FreeCaMode %d", + s->ServiceId, (s->Name ? *s->Name : "?"), (s->Type ? *s->Type : -1), + (s->ProviderName ? *s->ProviderName : "?"), (s->DefaultAuthority ? *s->DefaultAuthority : "?"), + s->RunningStatus, s->FreeCaMode); + } + + SqlMergeTask mergeServices("postgres", "services"); + CreateColumns<DVBSI::ServicePtr>(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); + mergeServices.sources.insert(new ContainerIterator<DVBSI::ServiceList>(&ts->Services, ts)); + mergeServices.loadComplete(commonObjects); + mergeServices.execute(NULL); + } + + private: + CommonObjects * commonObjects; +}; + +class SiEventsHandler : public SiEpgParser { + public: + SiEventsHandler(const RowProcessorCallback & cb) : + callBack(cb) {} + + void HandleTable(DVBSI::EventPtr e) + { + Logger()->messagebf(LOG_DEBUG, "Service Id: %d Program Id: %d Title: %s Time: %s - %s", + e->ServiceId, e->EventId, e->Title, e->StartTime, e->StopTime); + BindColumns<DVBSI::EventPtr>(rowState, e); + rowState.process(callBack); + } + + private: + ObjectRowState<DVBSI::EventPtr> rowState; + const RowProcessorCallback callBack; +}; + +void +Maintenance::UpdateAll(const Ice::Current & ice) +{ + UpdateAll(FE_OFDM, ice); + UpdateAll(FE_QPSK, ice); + UpdateAll(FE_ATSC, ice); + UpdateAll(FE_QAM, ice); +} + +void +Maintenance::UpdateAll(short type, const Ice::Current & ice) +{ + UpdateTransports(type, ice); + UpdateServices(type, ice); + UpdateEvents(type, ice); +} + +void +Maintenance::UpdateTransports(short type, const Ice::Current & ice) +{ + auto ic = ice.adapter->getCommunicator(); + UpdateTransports(type, ic, ice.adapter); +} + +void +Maintenance::UpdateTransports(short type, Ice::CommunicatorPtr ic, Ice::ObjectAdapterPtr adapter) +{ + auto devs = P2PVR::DevicesPrx::checkedCast(adapter->createProxy(ic->stringToIdentity("GlobalDevices"))); + auto si = P2PVR::SIPrx::checkedCast(adapter->createProxy(ic->stringToIdentity("SI"))); + auto siparser = new SiNetworkInformationMerger(this); + TemporarayIceAdapterObject<P2PVR::RawDataClient> parser(adapter, siparser); + + if (!devs) { + throw std::runtime_error("bad proxy(s)"); + } + const auto transports = si->GetAllDeliveries(FE_OFDM); + // Attempt to just download fresh data + BOOST_FOREACH(const auto & transport, transports) { + P2PVR::TunerPrx tuner; + try { + tuner = devs->GetTunerAny(type, transport, time(NULL) + 300); + } + catch (...) { + Logger()->messagebf(LOG_WARNING, "%s: Failed to get a suitable tuner", __PRETTY_FUNCTION__); + continue; + } + if (!tuner) { + continue; + } + try { + tuner->SendNetworkInformation(parser); + devs->ReleaseTuner(tuner); + BOOST_FOREACH(const CommonObjects::DataSources::value_type & ds, CommonObjects::datasources) { + ds.second->commit(); + } + return; + } + catch (const std::exception & ex) { + Logger()->messagebf(LOG_WARNING, "%s: Failed to fetch network information: %s", __PRETTY_FUNCTION__, ex.what()); + devs->ReleaseTuner(tuner); + throw; + } + catch (...) { + Logger()->messagebf(LOG_WARNING, "%s: Failed to fetch network information", __PRETTY_FUNCTION__); + devs->ReleaseTuner(tuner); + throw; + } + } + // If we can't do that, do a complete scan + auto tuner = devs->GetPrivateTuner(type, time(NULL) + 300); + tuner->ScanAndSendNetworkInformation(parser); + devs->ReleaseTuner(tuner); +} + +void +Maintenance::UpdateServices(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 SiServicesMerger(this); + TemporarayIceAdapterObject<P2PVR::RawDataClient> parser(ice.adapter, siparser); + + const auto deliveries = si->GetAllDeliveries(type); + if (deliveries.empty()) { + throw std::runtime_error("no delivery methods"); + } + + Logger()->messagebf(LOG_DEBUG, "%s: Getting a tuner", __PRETTY_FUNCTION__); + auto tuner = devs->GetTunerAny(type, deliveries.front(), time(NULL) + 90); + Logger()->messagebf(LOG_DEBUG, "%s: Fetching service list", __PRETTY_FUNCTION__); + tuner->SendServiceDescriptions(parser); + Logger()->messagebf(LOG_INFO, "%s: Updated service list", __PRETTY_FUNCTION__); + devs->ReleaseTuner(tuner); + 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) : + SourceObject(__PRETTY_FUNCTION__), + IHaveSubTasks(NULL), + 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<P2PVR::RawDataClient> parser(ice.adapter, + new SiEventsHandler(boost::bind(&SiEventsMerger::executeChildren, this, ec))); + + const auto deliveries = si->GetAllDeliveries(type); + if (deliveries.empty()) { + throw std::runtime_error("no delivery methods"); + } + + Logger()->messagebf(LOG_DEBUG, "%s: Getting a tuner", __PRETTY_FUNCTION__); + auto tuner = devs->GetTunerAny(type, deliveries.front(), time(NULL) + 600); + Logger()->messagebf(LOG_DEBUG, "%s: Fetching events", __PRETTY_FUNCTION__); + tuner->SendEventInformation(parser); + devs->ReleaseTuner(tuner); + } + + private: + 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::UpdateEvents(short type, const Ice::Current & ice) +{ + SqlMergeTask mergeEvents("postgres", "events"); + CreateColumns<DVBSI::EventPtr>(boost::bind(SqlMergeColumnsInserter, &mergeEvents, _1, _2)); + mergeEvents.sources.insert(new SiEventsMerger(type, ice)); + mergeEvents.loadComplete(this); + mergeEvents.execute(NULL); + BOOST_FOREACH(const CommonObjects::DataSources::value_type & ds, CommonObjects::datasources) { + ds.second->commit(); + } + Logger()->messagebf(LOG_INFO, "%s: Updated events", __PRETTY_FUNCTION__); +} diff --git a/p2pvr/lib/maintenance.h b/p2pvr/lib/maintenance.h new file mode 100644 index 0000000..b3773cf --- /dev/null +++ b/p2pvr/lib/maintenance.h @@ -0,0 +1,18 @@ +#ifndef P2PVR_MAINTENANCE_H +#define P2PVR_MAINTENANCE_H + +#include <p2pvr.h> +#include <commonObjects.h> + +class Maintenance : public P2PVR::Maintenance, public virtual CommonObjects { + public: + void UpdateAll(const Ice::Current &); + void UpdateAll(short type, const Ice::Current &); + void UpdateTransports(short type, const Ice::Current &); + void UpdateTransports(short type, Ice::CommunicatorPtr, Ice::ObjectAdapterPtr); + void UpdateServices(short type, const Ice::Current &); + void UpdateEvents(short type, const Ice::Current &); +}; + +#endif + diff --git a/p2pvr/lib/objectRowState.h b/p2pvr/lib/objectRowState.h new file mode 100644 index 0000000..9d53d43 --- /dev/null +++ b/p2pvr/lib/objectRowState.h @@ -0,0 +1,43 @@ +#ifndef OBJECT_ROW_STATE_H +#define OBJECT_ROW_STATE_H + +#include <boost/function.hpp> +#include <string> +#include <rowSet.h> + +typedef boost::function<void(const std::string &, bool)> ColumnCreator; + +template <typename V, typename... Parents> +void BindColumns(RowState &, const V &, const Parents & ...); + +template <typename V> +void UnbindColumns(RowState &, const V &); + +template <typename V> +void CreateColumns(const ColumnCreator &); + +template <typename T> +class ObjectRowState : public RowState { + public: + ObjectRowState() : + columns(ColumnCreatorHelper()) + { + fields.resize(columns.size()); + } + const Columns & getColumns() const { return columns; } + + private: + Columns ColumnCreatorHelper() + { + int index = 0; + Columns columns; + CreateColumns<T>([&columns, &index](const std::string & name, bool) { + columns.insert(new Column(index++, name)); + }); + return columns; + } + Columns columns; +}; + +#endif + diff --git a/p2pvr/lib/p2Helpers.cpp b/p2pvr/lib/p2Helpers.cpp new file mode 100644 index 0000000..b564e3b --- /dev/null +++ b/p2pvr/lib/p2Helpers.cpp @@ -0,0 +1,18 @@ +#include "p2Helpers.h" + +template <> +VariableType & +operator<<<Common::DateTime>(VariableType & vt, const Common::DateTime & dt) +{ + vt = boost::posix_time::ptime(boost::gregorian::date(dt.Year, dt.Month, dt.Day), + boost::posix_time::time_duration(dt.Hour, dt.Minute, 0)); + return vt; +} + +template <> +VariableType & +operator>>(VariableType & vt, short int & v) +{ + v = (int)vt; + return vt; +} diff --git a/p2pvr/lib/p2Helpers.h b/p2pvr/lib/p2Helpers.h new file mode 100644 index 0000000..7f2f577 --- /dev/null +++ b/p2pvr/lib/p2Helpers.h @@ -0,0 +1,46 @@ +#ifndef ICE_P2_HELPERS_H +#define ICE_P2_HELPERS_H + +#include <variableType.h> +#include <p2pvr.h> + +template <typename T> +VariableType & +operator>>(VariableType & vt, T & v) +{ + v = vt; + return vt; +} + +template <> +VariableType & +operator>>(VariableType & vt, short int & v); + +template <typename T> +VariableType & +operator<<(VariableType & vt, const T & v) +{ + vt = v; + return vt; +} + +template <> +VariableType & +operator<<<Common::DateTime>(VariableType & vt, const Common::DateTime & dt); + +template <typename T> +VariableType & +operator<<(VariableType & vt, const IceUtil::Optional<T> & v) +{ + if (v) { + vt << *v; + } + else { + vt = Null(); + } + return vt; +} + + +#endif + diff --git a/p2pvr/lib/si.cpp b/p2pvr/lib/si.cpp new file mode 100644 index 0000000..45fa793 --- /dev/null +++ b/p2pvr/lib/si.cpp @@ -0,0 +1,33 @@ +#include "si.h" +#include "dvbsiHelpers.h" +#include "containerCreator.h" +#include <linux/dvb/frontend.h> +#include <rdbmsDataSource.h> +#include <sqlHandleAsVariableType.h> +#include <selectcommand.h> +#include <logger.h> + +typedef boost::shared_ptr<DB::SelectCommand> SelectPtr; + +P2PVR::Deliveries +SI::GetAllDeliveries(short type, const Ice::Current &) +{ + auto db = dataSource<RdbmsDataSource>("postgres"); + SelectPtr sel; + switch (type) { + case FE_OFDM: + sel = SelectPtr(db->getReadonly().newSelectCommand("SELECT * FROM delivery_dvbt")); + break; + } + Logger()->messagebf(LOG_DEBUG, "%s: ", __PRETTY_FUNCTION__); + P2PVR::Deliveries rtn; + ContainerCreator<P2PVR::Deliveries, DVBSI::TerrestrialDelivery> cc(rtn); + cc.populate(boost::bind(&DB::SelectCommand::fetch, sel), [sel](unsigned int c) { + HandleAsVariableType h; + const DB::Column & col = (*sel)[c]; + col.apply(h); + return h.variable; + }, boost::bind(&DB::SelectCommand::columnCount, sel)); + return rtn; +} + diff --git a/p2pvr/lib/si.h b/p2pvr/lib/si.h new file mode 100644 index 0000000..2740c8e --- /dev/null +++ b/p2pvr/lib/si.h @@ -0,0 +1,13 @@ +#ifndef P2PVR_SI_H +#define P2PVR_SI_H + +#include <p2pvr.h> +#include <commonObjects.h> + +class SI : public P2PVR::SI, public virtual CommonObjects { + public: + P2PVR::Deliveries GetAllDeliveries(short type, const Ice::Current&); +}; + +#endif + diff --git a/p2pvr/lib/siParsers/event.cpp b/p2pvr/lib/siParsers/event.cpp new file mode 100644 index 0000000..6e5446d --- /dev/null +++ b/p2pvr/lib/siParsers/event.cpp @@ -0,0 +1,250 @@ +#include <time.h> +#include <glibmm/regex.h> +#include <boost/regex.hpp> +#include <boost/date_time/posix_time/posix_time_types.hpp> +#include <boost/date_time/gregorian_calendar.hpp> +#include <boost/algorithm/string/predicate.hpp> +#include <boost/algorithm/string/trim.hpp> +#include <boost/bind.hpp> +#include <boost/foreach.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/tuple/tuple_comparison.hpp> +#include "event.h" + +struct ShortEvent { + char lang_code[3]; + uint8_t event_name_length; + u_char data[]; +} __attribute__((packed)); + +struct Component { +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + u_char :4; + u_char stream_content :4; +#else + u_char stream_content :4; + u_char :4; +#endif + u_char component_type /*:8*/; + u_char component_tag /*:8*/; + char lang_code[3]; +} __attribute__((packed)); + +struct Content { +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + uint8_t content_nibble_level_1 :4; + uint8_t content_nibble_level_2 :4; +#else + uint8_t content_nibble_level_2 :4; + uint8_t content_nibble_level_1 :4; +#endif + uint8_t user_byte; +}; + +struct ParentalRating { + char country_code[3]; + uint8_t rating; +} __attribute__((packed)); + +struct EventDescriptor { + uint16_t EventId; + uint16_t mjd; + uint8_t start_time_h; + uint8_t start_time_m; + uint8_t start_time_s; + uint8_t duration_h; + uint8_t duration_m; + uint8_t duration_s; +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + u_char RunningStatus : 3; + u_char FreeCaMode : 1; + u_char descriptors_length_hi : 4; +#else + u_char descriptors_length_hi : 4; + u_char FreeCaMode : 1; + u_char RunningStatus : 3; +#endif + u_char descriptors_length_lo; + u_char data[]; +} __attribute__((packed)); + +static Glib::RefPtr<Glib::Regex> episodeRegex = Glib::Regex::create("(?:[ (]+|^)(?:\\w+ )?([0-9]+)(?: of |/)([0-9]+)[.)]+"); +static Glib::RefPtr<Glib::Regex> yearRegex = Glib::Regex::create("\\(([0-9]{4})[ )]+"); +static Glib::RefPtr<Glib::Regex> flagsRegex = Glib::Regex::create("[ []+([A-Z,]+)\\]"); + +void +SiEpgParser::parseDescriptor_ShortEvent(DVBSI::EventPtr current, const u_char * data) +{ + auto * evtdesc = reinterpret_cast<const struct ShortEvent *>(data); + StrPtr title, subtitle, desc; + + current->TitleLang = std::string(evtdesc->lang_code, 3); + + size_t evtlen = evtdesc->event_name_length; + if (evtlen) { + title = convert((const char *)evtdesc->data, evtlen); + } + + size_t dsclen = evtdesc->data[evtlen]; + if (dsclen) { + subtitle = convert((const char *)evtdesc->data + evtlen + 1, dsclen); + } + + if (subtitle) { + Glib::MatchInfo matches; + if (episodeRegex->match(*subtitle, matches)) { + current->Episode = boost::lexical_cast<int>(matches.fetch(1)); + current->Episodes = boost::lexical_cast<int>(matches.fetch(2)); + *subtitle = episodeRegex->replace_literal(*subtitle, 0, "", Glib::REGEX_MATCH_NOTEMPTY); + } + if (yearRegex->match(*subtitle, matches)) { + current->Year = boost::lexical_cast<int>(matches.fetch(1)); + *subtitle = yearRegex->replace_literal(*subtitle, 0, "", Glib::REGEX_MATCH_NOTEMPTY); + } + if (flagsRegex->match(*subtitle, matches)) { + current->Flags = matches.fetch(1); + *subtitle = yearRegex->replace_literal(*subtitle, 0, "", Glib::REGEX_MATCH_NOTEMPTY); + } + } + if (title && subtitle) { + if (boost::algorithm::ends_with(*title, "...") && boost::algorithm::starts_with(*subtitle, "...")) { + title->resize(title->length() - 3); + *title += " "; + size_t dot = subtitle->find('.', 4); + if (dot == Glib::ustring::npos) { + title->append(*subtitle, 3, subtitle->length() - 3); + } + else { + title->append(*subtitle, 3, dot - 3); + subtitle->erase(0, dot + 2); + } + } + size_t colon = subtitle->find(':'); + if (colon != Glib::ustring::npos) { + desc = StrPtr(new Glib::ustring(*subtitle, colon + 1, subtitle->length() - colon)); + subtitle->resize(colon); + } + else { + colon = title->find(':'); + desc = subtitle; + if (colon != Glib::ustring::npos) { + subtitle = StrPtr(new Glib::ustring(*title, colon + 1, title->length() - colon)); + title->resize(colon); + } + else { + subtitle.reset(); + } + } + } + if (title) { + boost::algorithm::trim_if(*title, isspace); + current->Title = *title; + } + if (subtitle) { + boost::algorithm::trim_if(*subtitle, isspace); + if (!subtitle->empty()) { + current->Subtitle = *subtitle; + } + } + if (desc) { + boost::algorithm::trim_if(*desc, isspace); + if (!desc->empty()) { + current->Description = *desc; + } + } +} + +void +SiEpgParser::parseDescriptor_Component(DVBSI::EventPtr current, const u_char * data) +{ + auto * dc = reinterpret_cast<const struct Component *>(data); + + switch (dc->stream_content) { + case 0x01: // Video Info + current->VideoHD = ((dc->component_type - 1) & 0x08) ? 1 : 0; + current->VideoFrameRate = ((dc->component_type - 1) & 0x04) ? 30 : 25; + current->VideoAspect = ((dc->component_type - 1) & 0x03); + break; + case 0x02: // Audio Info + current->AudioChannels = dc->component_type; + current->AudioLanguage = Glib::ustring(dc->lang_code, 3); + break; + case 0x03: // Teletext Info + current->SubtitleLanguage = Glib::ustring(dc->lang_code, 3); + break; + } +} + +void +SiEpgParser::parseDescriptor_Content(DVBSI::EventPtr current, const u_char * data) +{ + auto nc = reinterpret_cast<const Content *>(data); + current->Category = nc->content_nibble_level_1; + current->SubCategory = nc->content_nibble_level_2; + current->UserCategory = nc->user_byte; +} + +void +SiEpgParser::parseDescriptor_ParentalRating(DVBSI::EventPtr current, const u_char * data) +{ + auto * pr = reinterpret_cast<const ParentalRating *>(data); + switch (pr->rating) { + case 0x01 ... 0x0F: + current->DvbRating = pr->rating + 3; + break; + case 0x00: /*undefined*/ + case 0x10 ... 0xFF: /*broadcaster defined*/ + current->DvbRating = NULL; + break; + } +} + +bool +SiEpgParser::CheckTableId(u_char tableId) const +{ + return ((tableId >= 0x50 && tableId <= 0x5f) || (tableId >= 0x60 && tableId <= 0x6f)); +} + +void +SiEpgParser::HandleTable(DVBSI::EitInformationPtr) +{ +} + +Common::DateTime & +operator<<(Common::DateTime & dt, const boost::posix_time::ptime & pt) +{ + dt.Year = pt.date().year(); + dt.Month = pt.date().month(); + dt.Day = pt.date().day(); + dt.Hour = pt.time_of_day().hours(); + dt.Minute = pt.time_of_day().minutes(); + return dt; +} + +void +SiEpgParser::ParseSiTable(const EventInformation * eit, DVBSI::EitInformationPtr ei) +{ + ei->ServiceId = ntohs(eit->header.content_id); + ei->TransportStreamId = ntohs(eit->TransportStreamId); + ei->OriginalNetworkId = ntohs(eit->OriginalNetworkId); + LoopOver<EventDescriptor>(eit->data, HILO(eit->header.section_length) - 15, [this,ei](const EventDescriptor * ed) { + DVBSI::EventPtr e = new DVBSI::Event(); + e->EventId = ntohs(ed->EventId); + e->ServiceId = ei->ServiceId; + // <ew> + boost::gregorian::date startDate(boost::gregorian::gregorian_calendar::from_modjulian_day_number(ntohs(ed->mjd))); + boost::posix_time::ptime time(startDate); + time += boost::posix_time::time_duration(BcdCharToInt(ed->start_time_h), BcdCharToInt(ed->start_time_m), BcdCharToInt(ed->start_time_s)); + e->StartTime << time; + time += boost::posix_time::time_duration(BcdCharToInt(ed->duration_h), BcdCharToInt(ed->duration_m), BcdCharToInt(ed->duration_s)); + e->StopTime << time; + // </ew> + ParseDescriptors(ed->data, HILO(ed->descriptors_length), + 0x4d, boost::bind(&SiEpgParser::parseDescriptor_ShortEvent, e, _1), + 0x50, boost::bind(&SiEpgParser::parseDescriptor_Component, e, _1), + 0x54, boost::bind(&SiEpgParser::parseDescriptor_Content, e, _1), + 0x55, boost::bind(&SiEpgParser::parseDescriptor_ParentalRating, e, _1)); + HandleTable(e); + }); +} + diff --git a/p2pvr/lib/siParsers/event.h b/p2pvr/lib/siParsers/event.h new file mode 100644 index 0000000..0528d98 --- /dev/null +++ b/p2pvr/lib/siParsers/event.h @@ -0,0 +1,35 @@ +#ifndef EPGROWS_H +#define EPGROWS_H + +#include "table.h" +#include <p2pvr.h> + +struct EventInformation { + SiTableHeader header; + uint16_t TransportStreamId; + uint16_t OriginalNetworkId; + uint8_t SegmentLastSectionNumber; + uint8_t LastTableId; + u_char data[]; +} __attribute__((packed)); + +class SiEpgParser : public SiTableParser<EventInformation, DVBSI::EitInformationPtr, int> { + protected: + bool CheckTableId(u_char tableId) const; + int SectionNumberShift() const { return 3; } + uint8_t FirstTableId(const EventInformation * ei) { return (ei->header.tableid >= 0x60 ? 0x60 : 0x50); } + uint8_t LastTableId(const EventInformation * ei) { return ei->LastTableId; } + void ParseSiTable(const EventInformation * eit, DVBSI::EitInformationPtr); + void HandleTable(DVBSI::EitInformationPtr); + virtual void HandleTable(DVBSI::EventPtr) = 0; + + private: + static void parseStartTimeAndDuration(DVBSI::EventPtr, const u_char * data); + static void parseDescriptor_ShortEvent(DVBSI::EventPtr, const u_char * data); + static void parseDescriptor_Component(DVBSI::EventPtr, const u_char * data); + static void parseDescriptor_Content(DVBSI::EventPtr, const u_char * data); + static void parseDescriptor_ParentalRating(DVBSI::EventPtr, const u_char * data); +}; + +#endif + diff --git a/p2pvr/lib/siParsers/network.cpp b/p2pvr/lib/siParsers/network.cpp new file mode 100644 index 0000000..9f7434a --- /dev/null +++ b/p2pvr/lib/siParsers/network.cpp @@ -0,0 +1,219 @@ +#include "network.h" +#include <boost/bind.hpp> + +struct NetworkStreamsHeader { +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + u_char reserved1 :4; + u_char transport_stream_loop_length_hi :4; +#else + u_char transport_stream_loop_length_hi :4; + u_char reserved1 :4; +#endif + u_char transport_stream_loop_length_lo; + u_char data[]; +} __attribute__((packed)); + +struct TransportStream { + uint16_t transportStreamId; + uint16_t originalNetworkId; +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + u_char reserved1 :4; + u_char descriptors_length_hi :4; +#else + u_char descriptors_length_hi :4; + u_char reserved1 :4; +#endif + u_char descriptors_length_lo; + u_char data[]; +} __attribute__((packed)); + +struct TerrestrialDeliveryDescriptor { + uint32_t Frequency; +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + uint8_t Bandwidth : 3; + uint8_t Priority : 1; + uint8_t TimeSlicing : 1; + uint8_t MpeFec : 1; + uint8_t _reserved1 : 2; +#else + uint8_t _reserved1 : 2; + uint8_t MpeFec : 1; + uint8_t TimeSlicing : 1; + uint8_t Priority : 1; + uint8_t Bandwidth : 3; +#endif +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + uint8_t Constellation : 2; + uint8_t Hierarchy : 3; + uint8_t CodeRateHP : 3; +#else + uint8_t CodeRateHP : 3; + uint8_t Hierarchy : 3; + uint8_t Constellation : 2; +#endif +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + uint8_t CodeRateLP : 3; + uint8_t GuardInterval : 2; + uint8_t TransmissionMode : 2; + uint8_t OtherFrequencyFlag : 1; +#else + uint8_t OtherFrequencyFlag : 1; + uint8_t TransmissionMode : 2; + uint8_t GuardInterval : 2; + uint8_t CodeRateLP : 3; +#endif + uint32_t _reserved2; +} __attribute__((packed)); + +struct CableDeliveryDescriptor { + uint32_t Frequency; + uint8_t _reserved2; +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + uint8_t _reserved1 : 4; + uint8_t FecOuter : 4; +#else + uint8_t FecOuter : 4; + uint8_t _reserved1 : 4; +#endif + uint8_t Modulation; + uint8_t SymbolRate1; + uint8_t SymbolRate2; + uint8_t SymbolRate3; +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + uint8_t SymbolRate4 : 4; + uint8_t FecInner : 4; +#else + uint8_t FecInner : 4; + uint8_t SymbolRate4 : 4; +#endif +} __attribute__((packed)); + +struct SatelliteDeliveryDescriptor { + uint32_t Frequency; + uint16_t OrbitalPosition; +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + uint8_t WestEastFlag : 1; + uint8_t Polarization : 2; + uint8_t RollOff : 2; + uint8_t ModulationSystem : 1; + uint8_t ModulationType : 2; +#else + uint8_t ModulationType : 2; + uint8_t ModulationSystem : 1; + uint8_t RollOff : 2; + uint8_t Polarization : 2; + uint8_t WestEastFlag : 1; +#endif + uint8_t SymbolRate1; + uint8_t SymbolRate2; + uint8_t SymbolRate3; +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + uint8_t SymbolRate4 : 4; + uint8_t FecInner : 4; +#else + uint8_t FecInner : 4; + uint8_t SymbolRate4 : 4; +#endif +} __attribute__((packed)); + +struct ServiceListDescriptor { + uint16_t ServiceId; + uint8_t ServiceType; +} __attribute__((packed)); + +bool +SiNetworkInformationParser::CheckTableId(u_char tableId) const +{ + return (tableId == 0x40 || tableId == 0x041); +} + +void +SiNetworkInformationParser::ParseSiTable(const struct NetworkInformation * nit, DVBSI::NetworkPtr n) +{ + n->NetworkId = ntohs(nit->header.content_id); + auto nsh = ParseDescriptors<NetworkStreamsHeader>(nit->data, HILO(nit->network_descriptor_length), + 0x40, boost::bind(&SiNetworkInformationParser::parseDescriptor_NetworkName, n, _1, _2)); + LoopOver<TransportStream>(nsh->data, HILO(nsh->transport_stream_loop_length), [this,n](const TransportStream * ts) { + DVBSI::NetworkTransportStreamPtr nts = new DVBSI::NetworkTransportStream(); + nts->TransportStreamId = ntohs(ts->transportStreamId); + nts->OriginalNetworkId = ntohs(ts->originalNetworkId); + ParseDescriptors(ts->data, HILO(ts->descriptors_length), + 0x41, boost::bind(&SiNetworkInformationParser::parseDescriptor_ServiceList, nts, _1, _2), + 0x43, boost::bind(&SiNetworkInformationParser::parseDescriptor_SatelliteDelivery, nts, _1, _2), + 0x44, boost::bind(&SiNetworkInformationParser::parseDescriptor_CableDelivery, nts, _1, _2), + 0x5a, boost::bind(&SiNetworkInformationParser::parseDescriptor_TerrestrialDelivery, nts, _1, _2)); + n->TransportStreams.push_back(nts); + }); +} + +void +SiNetworkInformationParser::parseDescriptor_NetworkName(DVBSI::NetworkPtr n, const u_char * p, size_t len) +{ + n->Name = *convert((const char *)p, len); +} + +void +SiNetworkInformationParser::parseDescriptor_TerrestrialDelivery(DVBSI::NetworkTransportStreamPtr nts, const u_char * data, size_t len) +{ + assert(len == sizeof(TerrestrialDeliveryDescriptor)); + auto tdd = reinterpret_cast<const TerrestrialDeliveryDescriptor *>(data); + DVBSI::TerrestrialDeliveryPtr td = new DVBSI::TerrestrialDelivery; + td->Frequency = ((uint64_t)ntohl(tdd->Frequency)) * 10; + td->Bandwidth = tdd->Bandwidth; + td->Priority = tdd->Priority; + td->TimeSlicing = tdd->TimeSlicing; + td->MpeFec = tdd->MpeFec; + td->Constellation = tdd->Constellation; + td->Hierarchy = tdd->Hierarchy; + td->CodeRateHP = tdd->CodeRateHP; + td->CodeRateLP = tdd->CodeRateLP; + td->GuardInterval = tdd->GuardInterval; + td->TransmissionMode = tdd->TransmissionMode; + td->OtherFrequencyFlag = tdd->OtherFrequencyFlag; + nts->Terrestrial = td; +} + +void +SiNetworkInformationParser::parseDescriptor_CableDelivery(DVBSI::NetworkTransportStreamPtr nts, const u_char * data, size_t len) +{ + assert(len == sizeof(CableDeliveryDescriptor)); + auto cdd = reinterpret_cast<const CableDeliveryDescriptor *>(data); + DVBSI::CableDeliveryPtr cd = new DVBSI::CableDelivery; + cd->Frequency = ((uint64_t)ntohl(cdd->Frequency)) * 10; + cd->FecOuter = cdd->FecOuter; + cd->Modulation = cdd->Modulation; + cd->SymbolRate = HILO4(cdd->SymbolRate); + cd->FecInner = cdd->FecInner; + nts->Cable = cd; +} + +void +SiNetworkInformationParser::parseDescriptor_SatelliteDelivery(DVBSI::NetworkTransportStreamPtr nts, const u_char * data, size_t len) +{ + assert(len == sizeof(SatelliteDeliveryDescriptor)); + auto sdd = reinterpret_cast<const SatelliteDeliveryDescriptor *>(data); + DVBSI::SatelliteDeliveryPtr sd = new DVBSI::SatelliteDelivery; + sd->Frequency = ((uint64_t)ntohl(sdd->Frequency)) * 10; + sd->OrbitalPosition = ntohs(sdd->OrbitalPosition); + sd->WestEastFlag = sdd->WestEastFlag; + sd->Polarization = sdd->Polarization; + sd->RollOff = sdd->RollOff; + sd->ModulationSystem = sdd->ModulationSystem; + sd->ModulationType = sdd->ModulationType; + sd->SymbolRate = HILO4(sdd->SymbolRate); + sd->FecInner = sdd->FecInner; + nts->Satellite = sd; +} + +void +SiNetworkInformationParser::parseDescriptor_ServiceList(DVBSI::NetworkTransportStreamPtr nts, const u_char * data, size_t len) +{ + auto end = data + len; + while (data < end) { + auto d = reinterpret_cast<const ServiceListDescriptor *>(data); + nts->Services.push_back({ ntohs(d->ServiceId), d->ServiceType }); + data += sizeof(ServiceListDescriptor); + } +} + + diff --git a/p2pvr/lib/siParsers/network.h b/p2pvr/lib/siParsers/network.h new file mode 100644 index 0000000..6cf3b50 --- /dev/null +++ b/p2pvr/lib/siParsers/network.h @@ -0,0 +1,35 @@ +#ifndef NETWORKINFORMATIONPARSER_H +#define NETWORKINFORMATIONPARSER_H + +#include "table.h" +#include <p2pvr.h> + +struct NetworkInformation { + SiTableHeader header; +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + u_char reserved2 :4; + u_char network_descriptor_length_hi :4; +#else + u_char network_descriptor_length_hi :4; + u_char reserved2 :4; +#endif + u_char network_descriptor_length_lo /*:8*/; + u_char data[]; +} __attribute__((packed)); + +class SiNetworkInformationParser : public SiTableParser<NetworkInformation, DVBSI::NetworkPtr, u_char> { + protected: + bool CheckTableId(u_char tableId) const; + void ParseSiTable(const struct NetworkInformation * nit, DVBSI::NetworkPtr); + + private: + static void parseDescriptor_NetworkName(DVBSI::NetworkPtr, const u_char *data, size_t len); + static void parseDescriptor_ServiceList(DVBSI::NetworkTransportStreamPtr, const u_char *data, size_t len); + static void parseDescriptor_TerrestrialDelivery(DVBSI::NetworkTransportStreamPtr, const u_char *data, size_t len); + static void parseDescriptor_SatelliteDelivery(DVBSI::NetworkTransportStreamPtr, const u_char *data, size_t len); + static void parseDescriptor_CableDelivery(DVBSI::NetworkTransportStreamPtr, const u_char *data, size_t len); +}; + +#endif + + diff --git a/p2pvr/lib/siParsers/service.cpp b/p2pvr/lib/siParsers/service.cpp new file mode 100644 index 0000000..b8092af --- /dev/null +++ b/p2pvr/lib/siParsers/service.cpp @@ -0,0 +1,68 @@ +#include "service.h" +#include <boost/bind.hpp> + +struct ServiceDescriptor { + uint16_t ServiceId; +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + u_char _reserved1 : 6; + u_char EitSchedule : 1; + u_char EitPresentFollowing : 1; +#else + u_char EitPresentFollowing : 1; + u_char EitSchedule : 1; + u_char _reserved1 : 6; +#endif +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + u_char RunningStatus : 3; + u_char FreeCaMode : 1; + u_char descriptors_length_hi : 4; +#else + u_char descriptors_length_hi : 4; + u_char FreeCaMode : 1; + u_char RunningStatus : 3; +#endif + u_char descriptors_length_lo; + u_char data[]; +} __attribute__((packed)); + +bool +SiServicesParser::CheckTableId(u_char tableId) const +{ + return (tableId == 0x42 || tableId == 0x46); +} + +void +SiServicesParser::ParseSiTable(const TransportStreamDescriptor * tsd, DVBSI::TransportStreamPtr ts) +{ + ts->TransportStreamId = ntohs(tsd->header.content_id); + ts->OriginalNetworkId = ntohs(tsd->original_network_id); + LoopOver<ServiceDescriptor>(tsd->data, HILO(tsd->header.section_length) - 12, [this,ts](const ServiceDescriptor * sd) { + DVBSI::ServicePtr s = new DVBSI::Service(); + s->ServiceId = ntohs(sd->ServiceId); + s->EitSchedule = sd->EitSchedule; + s->EitPresentFollowing = sd->EitPresentFollowing; + s->RunningStatus = sd->RunningStatus; + s->FreeCaMode = sd->FreeCaMode; + ParseDescriptors(sd->data, HILO(sd->descriptors_length), + 0x48, boost::bind(&SiServicesParser::parseDescriptor_Service, s, _1, _2), + 0x73, boost::bind(&SiServicesParser::parseDescriptor_DefaultAuthority, s, _1, _2)); + ts->Services.push_back(s); + }); +} + +void +SiServicesParser::parseDescriptor_Service(DVBSI::ServicePtr s, const u_char * p, size_t) +{ + s->Type = p[0]; + if (p[1]) { + s->ProviderName = *convert((const char *)(p + 2), p[1]); + } + s->Name = *convert((const char *)(p + 3 + p[1]), p[2 + p[1]]); +} + +void +SiServicesParser::parseDescriptor_DefaultAuthority(DVBSI::ServicePtr s, const u_char * p, size_t len) +{ + s->DefaultAuthority = *convert((const char *)p, len); +} + diff --git a/p2pvr/lib/siParsers/service.h b/p2pvr/lib/siParsers/service.h new file mode 100644 index 0000000..65cdbdb --- /dev/null +++ b/p2pvr/lib/siParsers/service.h @@ -0,0 +1,25 @@ +#ifndef SERVICEROWS_H +#define SERVICEROWS_H + +#include "table.h" +#include <p2pvr.h> + +struct TransportStreamDescriptor { + SiTableHeader header; + uint16_t original_network_id; + uint8_t _reserved1; + u_char data[]; +} __attribute__((packed)); + +class SiServicesParser : public SiTableParser<TransportStreamDescriptor, DVBSI::TransportStreamPtr, int> { + protected: + bool CheckTableId(u_char tableId) const; + void ParseSiTable(const struct TransportStreamDescriptor * nit, DVBSI::TransportStreamPtr); + + private: + static void parseDescriptor_Service(DVBSI::ServicePtr, const u_char *data, size_t len); + static void parseDescriptor_DefaultAuthority(DVBSI::ServicePtr, const u_char *data, size_t len); +}; + +#endif + diff --git a/p2pvr/lib/siParsers/table.cpp b/p2pvr/lib/siParsers/table.cpp new file mode 100644 index 0000000..48efd0a --- /dev/null +++ b/p2pvr/lib/siParsers/table.cpp @@ -0,0 +1,76 @@ +#include "table.h" +#include <string.h> +#include <stdio.h> +#include <glibmm.h> +#include <exceptions.h> +#include <logger.h> + +SimpleMessageException(ErrorReadingData); +SimpleMessageException(TimeoutReadingData); +SimpleMessageException(DemuxOpenFailure); + +const std::string SiTableParserBase::ISO10646("ISO-10646"); +const std::string SiTableParserBase::EitEncoding("ISO6937"); +const std::string SiTableParserBase::UTF8("UTF8"); + +SiTableParserBase::SiTableParserBase() : + startTime(time(NULL)), + incomplete(0) +{ +} + +SiTableParserBase::~SiTableParserBase() +{ +} + +bool +SiTableParserBase::NewData(const P2PVR::Data & bytes, const Ice::Current&) +{ + //Logger()->messagebf(LOG_DEBUG, "%s: Got %d bytes", __PRETTY_FUNCTION__, bytes.size()); + return ParseInfoTable(&bytes.front(), bytes.size()); +} + +SiTableParserBase::StrPtr +SiTableParserBase::convert(const char * txt, size_t len) +{ + if (len == 0) { + return boost::shared_ptr<Glib::ustring>(new Glib::ustring()); + } + char enc[20]; + switch (*txt) { + default: + EitEncoding.copy(enc, EitEncoding.length()); + enc[EitEncoding.length()] = '\0'; + break; + case 0x01 ... 0x05: + snprintf(enc, sizeof(enc), "ISO-8859-%d\n", txt[0] + 4); + txt += 1; + len -= 1; + break; + case 0x10: + snprintf(enc, sizeof(enc), "ISO-8859-%02x%02x\n", txt[1], txt[2]); + txt += 3; + len -= 3; + break; + case 0x11: + ISO10646.copy(enc, ISO10646.length()); + enc[ISO10646.length()] = '\0'; + break; + case 0x1F: + // Values for the first byte of "0x00", "0x06" to "0x0F", and "0x12" to "0x1F" are reserved for future use. + //fprintf(stderr, "Reserved encoding: %02x\n", txt[0]); + //fprintf(stderr, "%d: %.*s\n", txt[1], len - 2, txt + 2); + case 0x06 ... 0x0F: + case 0x12 ... 0x1E: + case 0x00: // empty string + return boost::shared_ptr<Glib::ustring>(new Glib::ustring()); + } + size_t used = 0, newlen = 0; + GError * err = NULL; + boost::shared_ptr<gchar> utf8 = boost::shared_ptr<gchar>(g_convert(txt, len, "utf-8", enc, &used, &newlen, &err), g_free); + if (err) { + throw Glib::ConvertError(err); + } + return boost::shared_ptr<Glib::ustring>(new Glib::ustring(utf8.get())); +} + diff --git a/p2pvr/lib/siParsers/table.h b/p2pvr/lib/siParsers/table.h new file mode 100644 index 0000000..be40813 --- /dev/null +++ b/p2pvr/lib/siParsers/table.h @@ -0,0 +1,185 @@ +#ifndef DVBSIREADERHELPER_H +#define DVBSIREADERHELPER_H + +#include <glibmm/ustring.h> +#include <boost/shared_ptr.hpp> +#include <boost/function.hpp> +#include <boost/tuple/tuple.hpp> +#include <boost/tuple/tuple_comparison.hpp> +#include <arpa/inet.h> +#include <p2pvr.h> +#include <logger.h> + +typedef unsigned char u_char; + +#define HILO(x) (x##_hi << 8 | x##_lo) +#define HILO2(x) (x##1 << 8 | x##2) +#define HILO3(x) (x##1 << 16 | x##2 << 8 | x##3) +#define HILO4(x) (x##4 << 24 | x##2 << 16 | x##3 << 8 | x##4) +#define BcdCharToInt(x) (10*((x & 0xF0)>>4) + (x & 0xF)) + +class SiTableParserBase : public P2PVR::RawDataClient { + protected: + SiTableParserBase(); + virtual ~SiTableParserBase() = 0; + + typedef boost::shared_ptr<Glib::ustring> StrPtr; + + bool NewData(const P2PVR::Data & bytes, const Ice::Current&); + + static StrPtr convert(const char * txt, size_t len); + + static const std::string ISO10646; + static const std::string EitEncoding; + static const std::string UTF8; + protected: + virtual bool ParseInfoTable(const u_char * data, size_t len) = 0; + time_t startTime; + unsigned int incomplete; +}; + +struct SiTableHeaderBase { + uint8_t tableid; +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + u_char section_syntax_indicator :1; + u_char reserved2 :3; + u_char section_length_hi :4; +#else + u_char section_length_hi :4; + u_char reserved2 :3; + u_char section_syntax_indicator :1; +#endif + uint16_t section_length_lo :8; +} __attribute__((packed)); + +struct SiTableHeader : public SiTableHeaderBase { + uint16_t content_id; +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + u_char reserved1 :2; + u_char version_number :5; + u_char current_next_indicator :1; +#else + u_char current_next_indicator :1; + u_char version_number :5; + u_char reserved1 :2; +#endif + uint8_t section_number; + uint8_t last_section_number; +} __attribute__((packed)); + +struct SiDescriptorHeader { + u_char tag; + u_char length; + u_char data[]; +} __attribute__((packed)); + +template <class TableType, class TargetType, class Key> +class SiTableParser : public SiTableParserBase { + protected: + typedef std::set<uint8_t> Sections; // Section numbers + typedef boost::tuple<uint8_t, Sections> TargetSections; // Last section number | Seen section numbers + typedef std::map<uint8_t, TargetSections> TableTargetSections; // TableID -> Needed references + typedef boost::tuple<TargetType, TableTargetSections> ContentType; // TemplateData | Needed references + typedef std::map<uint16_t, ContentType> Contents; // ContentID -> data + + virtual int SectionNumberShift() const { return 0; } + virtual uint8_t LastTableId(const TableType * t) { return t->header.tableid; } + virtual uint8_t FirstTableId(const TableType * t) { return t->header.tableid; } + + bool ParseInfoTable(const u_char * data, size_t len) + { + const u_char * dataEnd = data + len; + while (data < dataEnd) { + auto siTable = reinterpret_cast<const TableType *>(data); + if (siTable->header.current_next_indicator == 1 // current only, please. + && CheckTableId(siTable->header.tableid)) { // only tables we're interested in, please. + uint16_t contentId = ntohs(siTable->header.content_id); + ContentType & content = contents[contentId]; + uint8_t sectionNumber = siTable->header.section_number >> SectionNumberShift(); + TableTargetSections & targetTableSections = boost::get<1>(content); + TargetSections & targetSections = targetTableSections[siTable->header.tableid]; + boost::get<0>(targetSections) = siTable->header.last_section_number >> SectionNumberShift(); + Sections & seen = boost::get<1>(targetSections); + if (seen.find(sectionNumber) == seen.end()) { + auto & obj = boost::get<0>(content); + if (!obj) { + obj = new typename TargetType::element_type(); + incomplete += 1; + } + ParseSiTable(siTable, obj); + seen.insert(sectionNumber); + bool complete = true; + for (int tid = FirstTableId(siTable); tid <= LastTableId(siTable); tid += 1) { + TableTargetSections::const_iterator tts = targetTableSections.find(tid); + if (tts == targetTableSections.end()) { + complete = false; + break; + } + if (boost::get<1>(tts->second).size() <= boost::get<0>(tts->second)) { + complete = false; + break; + } + } + if (complete) { + HandleTable(obj); + obj = NULL; + incomplete -= 1; + } + } + } + data += HILO(siTable->header.section_length) + 4; + } + return ((incomplete == 0) && (startTime < (time(NULL) - 10))); + } + + static void ParseDescriptor(const SiDescriptorHeader * descriptor) + { + (void)descriptor; + // Logger()->messagef(LOG_DEBUG, "Dropped descriptor with tag 0x%02x", descriptor->tag); + return; + } + + template <typename ... OtherParers> + static void ParseDescriptor(const SiDescriptorHeader * descriptor, u_char tag, boost::function<void(const u_char *, size_t)> parser, OtherParers ... otherParers) + { + if (tag == descriptor->tag) { + parser(descriptor->data, descriptor->length); + } + else { + ParseDescriptor(descriptor, otherParers...); + } + } + + template <typename NextData = void, typename ... Parsers> + static const NextData * ParseDescriptors(const u_char * data, size_t len, Parsers ... parsers) + { + auto end = data + len; + while (data < end) { + auto descriptor = reinterpret_cast<const SiDescriptorHeader *>(data); + ParseDescriptor(descriptor, parsers...); + data += descriptor->length + 2; + } + return reinterpret_cast<const NextData *>(end); + } + + template<typename LoopContent> + static void LoopOver(const u_char * data, size_t len, boost::function<void(const LoopContent *)> parser) + { + auto end = data + len; + while (data < end) { + auto loopData = reinterpret_cast<const LoopContent *>(data); + parser(loopData); + data += HILO(loopData->descriptors_length) + (loopData->data - data); + } + } + + virtual bool CheckTableId(u_char tableId) const = 0; + virtual void ParseSiTable(const TableType *, TargetType) = 0; + virtual void HandleTable(TargetType table) = 0; + + private: + mutable Contents contents; +}; + +#endif + diff --git a/p2pvr/lib/singleIterator.h b/p2pvr/lib/singleIterator.h new file mode 100644 index 0000000..232ad91 --- /dev/null +++ b/p2pvr/lib/singleIterator.h @@ -0,0 +1,39 @@ +#ifndef SINGLEITERATOR_H +#define SINGLEITERATOR_H + +#include <iHaveSubTasks.h> +#include <boost/foreach.hpp> +#include "objectRowState.h" + +template <typename T> +class SingleIterator : public IHaveSubTasks { + public: + template <typename ... Parents> + SingleIterator(const T * i, const Parents & ... p) : + SourceObject(__PRETTY_FUNCTION__), + IHaveSubTasks(NULL), + binder(boost::bind(&BindColumns<T, Parents...>, _1, _2, p...)), + item(i) + { + } + void execute(ExecContext * ec) const + { + ObjectRowState<T> rs; + binder(rs, *item); + rs.process(boost::bind(&SingleIterator::executeChildren, this, ec)); + } + + private: + boost::function<void(RowState &, const T &)> binder; + const T * item; + + void executeChildren(ExecContext * ec) const + { + BOOST_FOREACH(const Tasks::value_type & sq, normal) { + sq->execute(ec); + } + } +}; + +#endif + diff --git a/p2pvr/lib/temporaryIceAdapterObject.h b/p2pvr/lib/temporaryIceAdapterObject.h new file mode 100644 index 0000000..db4b340 --- /dev/null +++ b/p2pvr/lib/temporaryIceAdapterObject.h @@ -0,0 +1,39 @@ +#ifndef TEMPORARYICEADAPTER_H +#define TEMPORARYICEADAPTER_H + +template <typename Object> +class TemporarayIceAdapterObject { + public: + TemporarayIceAdapterObject(Ice::ObjectAdapterPtr a, Object * object) : + adapter(a), + proxy(Object::ProxyType::checkedCast(adapter->addWithUUID(object))) + { + if (!proxy) { + } + } + + ~TemporarayIceAdapterObject() + { + adapter->remove(proxy->ice_getIdentity()); + } + + TemporarayIceAdapterObject(const TemporarayIceAdapterObject &) = delete; + void operator=(const TemporarayIceAdapterObject &) = delete; + + operator typename Object::ProxyType() const + { + return proxy; + } + + typename Object::ProxyType operator->() const + { + return proxy; + } + + private: + Ice::ObjectAdapterPtr adapter; + typename Object::ProxyType proxy; +}; + +#endif + diff --git a/p2pvr/lib/tuner.cpp b/p2pvr/lib/tuner.cpp new file mode 100644 index 0000000..e508152 --- /dev/null +++ b/p2pvr/lib/tuner.cpp @@ -0,0 +1,355 @@ +#include "tuner.h" +#include <fcntl.h> +#include <Ice/Ice.h> +#include <sys/ioctl.h> +#include <poll.h> +#include <logger.h> +#include <misc.h> +#include <plugable.h> +#include <linux/dvb/frontend.h> +#include <linux/dvb/dmx.h> +#include <boost/crc.hpp> +#include <boost/tuple/tuple.hpp> +#include "fileHandle.h" +#include "siParsers/table.h" + +class FrontendNotSupported : public NotSupported { + public: + FrontendNotSupported(fe_type t) : NotSupported(stringbf("Frontend not supported: %s", t)) + { } +}; + +Tuner::Tuner(const boost::filesystem::path & df) : + deviceFrontend(df), + deviceRoot(df.branch_path()), + timeout(20000), + backgroundThread(NULL) +{ + int fd = open(deviceFrontend.string().c_str(), O_RDWR); + if (fd < 0) { + throw P2PVR::DeviceError(deviceFrontend.string(), strerror(errno), errno); + } + try { + struct dvb_frontend_info fe_info; + if (ioctl(fd, FE_GET_INFO, &fe_info) < 0) { + throw P2PVR::DeviceError(deviceFrontend.string(), strerror(errno), errno); + } + frontend = FrontendPtr(FrontendLoader::createNew<FrontendNotSupported>(fe_info.type, this, fd, fe_info)); + } + catch (...) { + close(fd); + throw; + } + Logger()->messagebf(LOG_INFO, "%s: Attached to %s (%s, type %s)", __PRETTY_FUNCTION__, + deviceRoot, frontend->Info().name, frontend->Type()); +} + +Tuner::~Tuner() +{ + while (!backgroundClients.empty()) { + close(backgroundClients.begin()->first); + backgroundClients.erase(backgroundClients.begin()); + } + if (backgroundThread) { + backgroundThread->join(); + delete backgroundThread; + } +} + +void +Tuner::TuneTo(const DVBSI::DeliveryPtr & mp, const Ice::Current&) +{ + frontend->TuneTo(mp); +} + +int +Tuner::GetStatus(const Ice::Current &) +{ + return frontend->GetStatus(); +} + +std::string +Tuner::Device() const +{ + return deviceRoot.string(); +} + +int +Tuner::OpenDemux() const +{ + int demux = open((deviceRoot / "demux0").string().c_str(), O_RDWR | O_NONBLOCK); + if (demux < 0) { + throw P2PVR::DeviceError(deviceRoot.string(), strerror(errno), errno); + } + return demux; +} + +void +Tuner::ScanAndSendNetworkInformation(const P2PVR::RawDataClientPrx & client, const Ice::Current & ice) +{ + frontend->FrequencyScan([this, &client, &ice](long) { + try { + SendNetworkInformation(client, ice); + return true; + } + catch (...) { + return false; + } + }); +} + +void +Tuner::SendNetworkInformation(const P2PVR::RawDataClientPrx & client, const Ice::Current & ice) +{ + SendPID(0x10, client, ice); +} + +void +Tuner::SendBouquetAssociations(const P2PVR::RawDataClientPrx & client, const Ice::Current & ice) +{ + SendPID(0x11, client, ice); +} + +void +Tuner::SendServiceDescriptions(const P2PVR::RawDataClientPrx & client, const Ice::Current & ice) +{ + SendPID(0x11, client, ice); +} + +void +Tuner::SendEventInformation(const P2PVR::RawDataClientPrx & client, const Ice::Current & ice) +{ + SendPID(0x12, client, ice); +} + +void +Tuner::SendPID(int pid, const P2PVR::RawDataClientPrx & client, const Ice::Current & ice) const +{ + Logger()->messagebf(LOG_DEBUG, "%s: pid = 0x%x", __PRETTY_FUNCTION__, pid); + + ice.con->createProxy(client->ice_getIdentity()); + FileHandle demux(OpenDemux()); + struct dmx_sct_filter_params sctFilterParams; + memset(&sctFilterParams, 0, sizeof(dmx_sct_filter_params)); + sctFilterParams.pid = pid; + sctFilterParams.flags = DMX_IMMEDIATE_START; + + if (ioctl(demux, DMX_SET_FILTER, &sctFilterParams) < 0) { + throw P2PVR::DeviceError("demux", strerror(errno), errno); + } + + ReadDemuxAndSend(demux, client); +} + +void +Tuner::ReadDemuxAndSend(int demux, const P2PVR::RawDataClientPrx & client) const +{ + Logger()->messagebf(LOG_DEBUG, "%s: begin", __PRETTY_FUNCTION__); + std::vector<Ice::AsyncResultPtr> asyncs; + struct pollfd ufd; + bool exitFlag = false; + do { + // Wait for data to appear + memset(&ufd, 0, sizeof(pollfd)); + ufd.fd = demux; + 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); + } + + // Read it + P2PVR::Data buf(1 << 12); + int nr = read(demux, &buf.front(), buf.size()); + if (nr < 0) { + throw P2PVR::DeviceError("demux", strerror(errno), errno); + } + size_t n = nr; + buf.resize(n); + + // Verify it + if (n < sizeof(SiTableHeader)) { + Logger()->messagebf(LOG_WARNING, "Received data too small to be an SI table."); + } + auto * tab = (const SiTableHeader *)(&buf.front()); + size_t l = sizeof(SiTableHeaderBase) + HILO(tab->section_length); + if (n < l) { + Logger()->messagebf(LOG_WARNING, "Received data shorter than its defined length."); + continue; + } + if (n > l) { + Logger()->messagebf(LOG_WARNING, "Received data longer than its defined length."); + continue; + } + if (!crc32(buf)) { + Logger()->messagebf(LOG_WARNING, "Received data is corrupted (crc32 failed)."); + continue; + } + + asyncs.push_back(client->begin_NewData(buf)); + + asyncs.erase(std::remove_if(asyncs.begin(), asyncs.end(), [&exitFlag, &client](const Ice::AsyncResultPtr & a) { + if (a->isCompleted()) { + exitFlag = client->end_NewData(a); + return true; + } + return false; + }), asyncs.end()); + } while (!exitFlag); + BOOST_FOREACH(const auto & a, asyncs) { + client->end_NewData(a); + } + Logger()->messagebf(LOG_DEBUG, "%s: end", __PRETTY_FUNCTION__); +} + +int +Tuner::StartSendingTS(const P2PVR::PacketIds & pids, const P2PVR::RawDataClientPrx & client, const Ice::Current &) +{ + Logger()->message(LOG_DEBUG, __PRETTY_FUNCTION__); + if (pids.empty()) { + throw P2PVR::DeviceError("demux", "Packet Id list cannot be empty", 0); + } + + std::lock_guard<std::mutex> g(lock); + int demux = backgroundClients.insert(BackgroundClients::value_type(OpenDemux(), client)).first->first; + + struct dmx_pes_filter_params pesFilterParams; + memset(&pesFilterParams, 0, sizeof(struct dmx_pes_filter_params)); + Logger()->messagebf(LOG_ERR, "%s: DMX_SET_PES_FILTER for pid %d", __PRETTY_FUNCTION__, pids[0]); + pesFilterParams.pid = pids[0]; + pesFilterParams.input = DMX_IN_FRONTEND; + pesFilterParams.output = DMX_OUT_TSDEMUX_TAP; + pesFilterParams.pes_type = DMX_PES_OTHER; + pesFilterParams.flags = 0; + + if (ioctl(demux, DMX_SET_PES_FILTER, &pesFilterParams) < 0) { + backgroundClients.erase(demux); + Logger()->messagebf(LOG_ERR, "%s: DMX_SET_PES_FILTER failed (%d: %s)", __PRETTY_FUNCTION__, errno, strerror(errno)); + throw P2PVR::DeviceError("demux", strerror(errno), errno); + } + + for (unsigned int x = 1; x < pids.size(); x += 1) { + __u16 p = pids[x]; + Logger()->messagebf(LOG_ERR, "%s: DMX_ADD_PID for pid %d", __PRETTY_FUNCTION__, p); + if (ioctl(demux, DMX_ADD_PID, &p) < 0) { + backgroundClients.erase(demux); + Logger()->messagebf(LOG_ERR, "%s: DMX_ADD_PID failed (%d: %s)", __PRETTY_FUNCTION__, errno, strerror(errno)); + throw P2PVR::DeviceError("demux", strerror(errno), errno); + } + } + + if (ioctl(demux, DMX_START) < 0) { + backgroundClients.erase(demux); + Logger()->messagebf(LOG_ERR, "%s: DMX_START failed (%d: %s)", __PRETTY_FUNCTION__, errno, strerror(errno)); + throw P2PVR::DeviceError("demux", strerror(errno), errno); + } + + startSenderThread(); + return demux; +} + +void +Tuner::StopSendingTS(int handle, const Ice::Current &) +{ + Logger()->message(LOG_DEBUG, __PRETTY_FUNCTION__); + std::lock_guard<std::mutex> g(lock); + if (backgroundClients.find(handle) != backgroundClients.end()) { + close(handle); + backgroundClients.erase(handle); + } +} + +void +Tuner::startSenderThread() +{ + if (!backgroundThread) { + backgroundThread = new std::thread(&Tuner::senderThread, this); + } +} + +void +Tuner::senderThread() +{ + lock.lock(); + typedef boost::tuple<P2PVR::RawDataClientPrx, Ice::AsyncResultPtr, int> AsyncCall; + std::vector<AsyncCall> asyncs; + while (!backgroundClients.empty()) { + int n = backgroundClients.rbegin()->first + 1; + fd_set rfds; + FD_ZERO(&rfds); + BOOST_FOREACH(const auto & c, backgroundClients) { + FD_SET(c.first, &rfds); + } + lock.unlock(); + + struct timeval tv { 2, 0 }; + switch (select(n, &rfds, NULL, NULL, &tv)) { + case -1: // error + case 0: // nothing to read, but all is well + break; + default: + { // stuff to do + std::lock_guard<std::mutex> g(lock); + BOOST_FOREACH(const auto & c, backgroundClients) { + if (FD_ISSET(c.first, &rfds)) { + // Read it + P2PVR::Data buf(1 << 16); + int nr = read(c.first, &buf.front(), buf.size()); + if (nr < 0) { + close(c.first); + backgroundClients.erase(c.first); + break; // backgroundClients has changed, bailout and start again + } + size_t n = nr; + buf.resize(n); + // Send it + asyncs.push_back(AsyncCall(c.second, c.second->begin_NewData(buf), c.first)); + //c.second->NewData(buf); + } + } + } + break; + } + // Clean up finished async requests + asyncs.erase(std::remove_if(asyncs.begin(), asyncs.end(), [this](const AsyncCall & a) { + try { + if (a.get<1>()->isCompleted()) { + if (!a.get<0>()->end_NewData(a.get<1>())) { + close(a.get<2>()); + std::lock_guard<std::mutex> g(lock); + backgroundClients.erase(a.get<2>()); + } + return true; + } + return false; + } + catch (...) { + close(a.get<2>()); + std::lock_guard<std::mutex> g(lock); + backgroundClients.erase(a.get<2>()); + return true; + } + }), asyncs.end()); + lock.lock(); + } + Logger()->messagebf(LOG_DEBUG, "%s: Cleaning up", __PRETTY_FUNCTION__); + BOOST_FOREACH(const auto & a, asyncs) { + try { + a.get<0>()->end_NewData(a.get<1>()); + } + catch (...) { + } + } + backgroundThread = NULL; + Logger()->messagebf(LOG_DEBUG, "%s: Unlocking", __PRETTY_FUNCTION__); + lock.unlock(); +} + +bool +Tuner::crc32(const P2PVR::Data & buf) +{ + boost::crc_optimal<32, 0x0, 0xFFFFFFFF, 0x0, true, false> crc; + crc.process_bytes(&buf.front(), buf.size()); + return crc.checksum() == 0; +} + diff --git a/p2pvr/lib/tuner.h b/p2pvr/lib/tuner.h new file mode 100644 index 0000000..8111f1e --- /dev/null +++ b/p2pvr/lib/tuner.h @@ -0,0 +1,50 @@ +#ifndef P2PVR_TUNER_H +#define P2PVR_TUNER_H + +#include <p2pvr.h> +#include <boost/filesystem/path.hpp> +#include "frontend.h" +#include <map> +#include <thread> +#include <mutex> +#include <boost/bind.hpp> + +class Tuner : public P2PVR::PrivateTuner { + public: + Tuner(const boost::filesystem::path & deviceFrontend); + ~Tuner(); + + void TuneTo(const DVBSI::DeliveryPtr &, const Ice::Current&); + int GetStatus(const Ice::Current&); + std::string Device() const; + + void ScanAndSendNetworkInformation(const P2PVR::RawDataClientPrx & client, const Ice::Current&); + 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 SendEventInformation(const P2PVR::RawDataClientPrx & client, const Ice::Current&); + + int StartSendingTS(const P2PVR::PacketIds & pids, const P2PVR::RawDataClientPrx & client, const Ice::Current &); + void StopSendingTS(int handle, const Ice::Current &); + + 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; + void startSenderThread(); + void senderThread(); + + const boost::filesystem::path deviceFrontend; + const boost::filesystem::path deviceRoot; + const int timeout; + typedef std::map<int, P2PVR::RawDataClientPrx> BackgroundClients; + BackgroundClients backgroundClients; + std::thread * backgroundThread; + std::mutex lock; + + FrontendPtr frontend; +}; + +#endif + diff --git a/p2pvr/scanner/Jamfile.jam b/p2pvr/scanner/Jamfile.jam deleted file mode 100644 index 768e86c..0000000 --- a/p2pvr/scanner/Jamfile.jam +++ /dev/null @@ -1,17 +0,0 @@ -alias glibmm : : : : - <cflags>"`pkg-config --cflags glibmm-2.4`" - <linkflags>"`pkg-config --libs glibmm-2.4`" - ; -project - : requirements - <variant>debug:<linkflags>-Wl,-z,defs <cflags>"-W -Wall -Werror -Wwrite-strings" - ; - -# Scanner - t -lib p2pvrscanner : - [ glob *.cpp ] - : - <library>../../project2/common//p2common - <library>glibmm - ; - diff --git a/p2pvr/scanner/dvbSiReaderHelper.cpp b/p2pvr/scanner/dvbSiReaderHelper.cpp deleted file mode 100644 index 88b025d..0000000 --- a/p2pvr/scanner/dvbSiReaderHelper.cpp +++ /dev/null @@ -1,133 +0,0 @@ -#include "dvbSiReaderHelper.h" -#include <errno.h> -#include <fcntl.h> -#include <string.h> -#include <stdio.h> -#include <glibmm.h> -#include <sys/poll.h> -#include "si_tables.h" -#include <boost/crc.hpp> - -SimpleMessageException(ErrorReadingData); -SimpleMessageException(TimeoutReadingData); -SimpleMessageException(DemuxOpenFailure); - -const std::string DvbSiParserHelper::ISO10646("ISO-10646"); -const std::string DvbSiParserHelper::EitEncoding("ISO6937"); -const std::string DvbSiParserHelper::UTF8("UTF8"); - -DvbSiReaderHelper::DvbSiReaderHelper(const ScriptNodePtr p) : - demux(p, "demux", "/dev/dvb/adapter0/demux0"), - timeout(p, "timeout", 10000), - fd_epg(0) -{ -} - -DvbSiReaderHelper::~DvbSiReaderHelper() -{ - closeInput(); -} - -void -DvbSiReaderHelper::openInput(ExecContext * ec) const -{ - if ((fd_epg = open(demux(ec), O_RDWR)) < 0) { - throw DemuxOpenFailure(strerror(errno)); - } - filterInput(fd_epg); -} - -void -DvbSiReaderHelper::closeInput() const -{ - if (fd_epg) { - close(fd_epg); - fd_epg = 0; - } -} - -DvbSiParserHelper::StrPtr -DvbSiParserHelper::convert(const char * txt, size_t len) -{ - if (len == 0) { - return boost::shared_ptr<Glib::ustring>(new Glib::ustring()); - } - char enc[20]; - switch (*txt) { - default: - EitEncoding.copy(enc, EitEncoding.length()); - enc[EitEncoding.length()] = '\0'; - break; - case 0x01 ... 0x05: - snprintf(enc, sizeof(enc), "ISO-8859-%d\n", txt[0] + 4); - txt += 1; - len -= 1; - break; - case 0x10: - snprintf(enc, sizeof(enc), "ISO-8859-%02x%02x\n", txt[1], txt[2]); - txt += 3; - len -= 3; - break; - case 0x11: - ISO10646.copy(enc, ISO10646.length()); - enc[ISO10646.length()] = '\0'; - break; - case 0x1F: - // Values for the first byte of "0x00", "0x06" to "0x0F", and "0x12" to "0x1F" are reserved for future use. - //fprintf(stderr, "Reserved encoding: %02x\n", txt[0]); - //fprintf(stderr, "%d: %.*s\n", txt[1], len - 2, txt + 2); - case 0x06 ... 0x0F: - case 0x12 ... 0x1E: - case 0x00: // empty string - return boost::shared_ptr<Glib::ustring>(new Glib::ustring()); - } - size_t used = 0, newlen = 0; - GError * err = NULL; - boost::shared_ptr<gchar> utf8 = boost::shared_ptr<gchar>(g_convert(txt, len, "utf-8", enc, &used, &newlen, &err), g_free); - if (err) { - throw Glib::ConvertError(err); - } - return boost::shared_ptr<Glib::ustring>(new Glib::ustring(utf8.get())); -} - -bool -_dvb_crc32(void * buf, size_t len) -{ - boost::crc_optimal<32, 0x0, 0xFFFFFFFF, 0x0, true, false> crc; - crc.process_bytes(buf, len); - return crc.checksum() == 0; -} - -void -DvbSiReaderHelper::readTables(const InfoTableParser & parseInfoTable) const -{ - u_char buf[1<<12]; - struct pollfd ufd; - memset(&ufd, 0, sizeof(pollfd)); - ufd.fd = fd_epg; - ufd.events = POLLIN; - size_t n; - int prtn = 0; - time_t lastuseful = time(NULL); - while (((prtn = poll(&ufd, 1, timeout(NULL))) == 1) && (n = read(fd_epg, buf, sizeof(buf)))) { - if (n < sizeof(struct si_tab)) - throw ErrorReadingData("Smaller that si_tab"); - struct si_tab *tab = (struct si_tab *)buf; - size_t l = sizeof(struct si_tab) + GetSectionLength(tab); - if (n < l) - throw ErrorReadingData("Smaller that section length"); - if (_dvb_crc32(buf, l)) { - if (parseInfoTable(buf, l)) { - time(&lastuseful); - } - else { - if (lastuseful < time(NULL) - 15) { - break; - } - } - } - } - if (prtn < 1) { - throw TimeoutReadingData("Tuned to a multiplex?"); - } -} diff --git a/p2pvr/scanner/dvbSiReaderHelper.h b/p2pvr/scanner/dvbSiReaderHelper.h deleted file mode 100644 index d0080f1..0000000 --- a/p2pvr/scanner/dvbSiReaderHelper.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef DVBSIREADERHELPER_H -#define DVBSIREADERHELPER_H - -#include "scripts.h" -#include "variables.h" -#include <boost/function.hpp> - -class RowProcessor; - -class DvbSiReaderHelper { - public: - typedef boost::function<bool (const u_char *, size_t)> InfoTableParser; - - DvbSiReaderHelper(const ScriptNodePtr p); - ~DvbSiReaderHelper(); - - const Variable demux; - const Variable timeout; - - protected: - void openInput(ExecContext *) const; - virtual void filterInput(int fd) const = 0; - void readTables(const InfoTableParser &) const; - void closeInput() const; - - private: - mutable int fd_epg; -}; - -class DvbSiParserHelper { - protected: - typedef boost::shared_ptr<Glib::ustring> StrPtr; - static StrPtr convert(const char * txt, size_t len); - static const std::string ISO10646; - static const std::string EitEncoding; - static const std::string UTF8; -}; - -#endif - diff --git a/p2pvr/scanner/epgRows.cpp b/p2pvr/scanner/epgRows.cpp deleted file mode 100644 index e27769d..0000000 --- a/p2pvr/scanner/epgRows.cpp +++ /dev/null @@ -1,528 +0,0 @@ -/* - * tv_grab_dvb - dump dvb epg info in xmltv - * Version 0.2 - 20/04/2004 - First Public Release - * - * Copyright (C) 2004 Mark Bryars <dvb at darkskiez d0t co d0t uk> - * - * DVB code Mercilessly ripped off from dvddate - * dvbdate Copyright (C) Laurence Culhane 2002 <dvbdate@holmes.demon.co.uk> - * - * 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 <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <string.h> -#include <sys/ioctl.h> -#include <errno.h> -#include <stdint.h> -#include <time.h> -#include <glibmm/regex.h> -#include <boost/regex.hpp> -#include <boost/bind.hpp> -#include <boost/date_time/gregorian_calendar.hpp> -#include <boost/algorithm/string/predicate.hpp> -#include <boost/algorithm/string/trim.hpp> -#include <boost/foreach.hpp> -#include <boost/lexical_cast.hpp> - -#include <linux/dvb/dmx.h> -#include "si_tables.h" - -#include "rowProcessor.h" -#include "epgRows.h" -#include <boost/tuple/tuple_comparison.hpp> - -struct EpgProgram { - VariableType serviceID; - VariableType eventID; - VariableType title; - VariableType titleLang; - VariableType subtitle; - VariableType descLang; - VariableType desc1; - VariableType desc2; - VariableType desc3; - VariableType videoAspect; - VariableType videoFrameRate; - VariableType videoHD; - VariableType audioChannels; - VariableType language; - VariableType teletextSubtitleLang; - VariableType category; - VariableType dvbRating; - VariableType contentItemID; - VariableType contentSeriesID; - VariableType contentRecommendation; - VariableType startTime; - VariableType stopTime; - VariableType episode; - VariableType episodes; - VariableType year; - VariableType flags; -}; - -static Glib::ustring title("title"); -template <class C, class M> -const M & -getMember(M C::*t, C * p) { - return p->*t; -} -SimpleMessageException(NoSuchAttribute); - -void -EpgRows::loadComplete(const CommonObjects *) -{ -} - -EpgRowState::EpgRowState() : - current(NULL) -{ -} - -const Columns & -EpgRowState::getColumns() const -{ - return columns; -} - -#define returnAttr(name) if (attrName == #name) return boost::bind(getMember<EpgProgram, VariableType>, &EpgProgram::name, boost::ref(current)) -RowState::RowAttribute -EpgRowState::resolveAttr(const Glib::ustring & attrName) const { - returnAttr(serviceID); - returnAttr(eventID); - returnAttr(title); - returnAttr(titleLang); - returnAttr(subtitle); - returnAttr(descLang); - returnAttr(desc1); - returnAttr(desc2); - returnAttr(desc3); - returnAttr(videoAspect); - returnAttr(videoFrameRate); - returnAttr(videoHD); - returnAttr(audioChannels); - returnAttr(language); - returnAttr(teletextSubtitleLang); - returnAttr(category); - returnAttr(dvbRating); - returnAttr(contentItemID); - returnAttr(contentSeriesID); - returnAttr(contentRecommendation); - returnAttr(startTime); - returnAttr(stopTime); - returnAttr(episode); - returnAttr(episodes); - returnAttr(year); - returnAttr(flags); - throw NoSuchAttribute(attrName); -} - -DECLARE_LOADER("epgrows", EpgRows); - -EpgRows::EpgRows(const ScriptNodePtr p) : - RowSet(p), - DvbSiReaderHelper(p) -{ -} - -EpgRows::~EpgRows() -{ -} - -static int time_offset = 0; -static int chan_filter = 0; -static int chan_filter_mask = 0; -static Glib::RefPtr<Glib::Regex> episodeRegex = Glib::Regex::create("[ (]+(?:\\w+ )?([0-9]+)(?: of |/)([0-9]+)[.)]+"); -static Glib::RefPtr<Glib::Regex> yearRegex = Glib::Regex::create("\\(([0-9]{4})[ )]+"); -static Glib::RefPtr<Glib::Regex> flagsRegex = Glib::Regex::create("[ []+([A-Z,]+)\\]"); - -void -EpgRowState::parseEventDescription(const u_char * data, EpgProgram * current) const { - assert(GetDescriptorTag(data) == 0x4D); - const struct descr_short_event *evtdesc = reinterpret_cast<const struct descr_short_event *>(data); - - size_t evtlen = evtdesc->event_name_length; - StrPtr title, subtitle, desc; - if (evtlen) { - current->titleLang = Glib::ustring((const char *)&evtdesc->lang_code1, 3); - title = convert((const char *)evtdesc->data, evtlen); - } - - size_t dsclen = evtdesc->data[evtlen]; - if (dsclen) { - subtitle = convert((const char *)evtdesc->data + evtlen + 1, dsclen); - } - if (subtitle) { - Glib::MatchInfo matches; - if (episodeRegex->match(*subtitle, matches)) { - current->episode = boost::lexical_cast<int>(matches.fetch(1)); - current->episodes = boost::lexical_cast<int>(matches.fetch(2)); - *subtitle = episodeRegex->replace_literal(*subtitle, 0, "", Glib::REGEX_MATCH_NOTEMPTY); - } - if (yearRegex->match(*subtitle, matches)) { - current->year = boost::lexical_cast<int>(matches.fetch(1)); - *subtitle = yearRegex->replace_literal(*subtitle, 0, "", Glib::REGEX_MATCH_NOTEMPTY); - } - if (flagsRegex->match(*subtitle, matches)) { - current->flags = matches.fetch(1); - *subtitle = yearRegex->replace_literal(*subtitle, 0, "", Glib::REGEX_MATCH_NOTEMPTY); - } - } - if (title && subtitle) { - if (boost::algorithm::ends_with(*title, "...") && boost::algorithm::starts_with(*subtitle, "...")) { - title->resize(title->length() - 3); - *title += " "; - size_t dot = subtitle->find('.', 4); - if (dot == Glib::ustring::npos) { - title->append(*subtitle, 3, subtitle->length() - 3); - } - else { - title->append(*subtitle, 3, dot - 3); - subtitle->erase(0, dot + 2); - } - } - size_t colon = subtitle->find(':'); - if (colon != Glib::ustring::npos) { - desc = StrPtr(new Glib::ustring(*subtitle, colon + 1, subtitle->length() - colon)); - subtitle->resize(colon); - } - else { - colon = title->find(':'); - desc = subtitle; - if (colon != Glib::ustring::npos) { - subtitle = StrPtr(new Glib::ustring(*title, colon + 1, title->length() - colon)); - title->resize(colon); - } - else { - subtitle.reset(); - } - } - } - if (title) { - boost::algorithm::trim_if(*title, isspace); - current->title = *title; - } - if (subtitle) { - boost::algorithm::trim_if(*subtitle, isspace); - if (!subtitle->empty()) { - current->subtitle = *subtitle; - } - } - if (desc) { - boost::algorithm::trim_if(*desc, isspace); - if (!desc->empty()) { - current->desc1 = *desc; - } - } -} - -/* Parse 0x4E Extended Event Descriptor. {{{ */ -void -EpgRowState::parseLongEventDescription(const u_char * data, EpgProgram * current) const { - assert(GetDescriptorTag(data) == 0x4E); - const struct descr_extended_event *levt = reinterpret_cast<const struct descr_extended_event *>(data); - bool non_empty = (levt->descriptor_number || levt->last_descriptor_number || levt->length_of_items || levt->data[0]); - - if (non_empty && levt->descriptor_number == 0) { - current->descLang = Glib::ustring((const char *)&levt->lang_code1, 3); - - const u_char *p = reinterpret_cast<const u_char *>(&levt->data); -#ifndef NDEBUG - const void *data_end = data + DESCR_GEN_LEN + GetDescriptorLength(data); -#endif - while (p < levt->data + levt->length_of_items) { - const struct item_extended_event *name = reinterpret_cast<const struct item_extended_event *>(p); - size_t name_len = name->item_description_length; - assert(p + ITEM_EXTENDED_EVENT_LEN + name_len < data_end); - current->desc1 = *convert((const char *)name->data, name_len); - - p += ITEM_EXTENDED_EVENT_LEN + name_len; - - const struct item_extended_event *value = reinterpret_cast<const struct item_extended_event *>(p); - size_t value_len = value->item_description_length; - assert(p + ITEM_EXTENDED_EVENT_LEN + value_len < data_end); - current->desc2 = *convert((const char *)value->data, value_len); - - p += ITEM_EXTENDED_EVENT_LEN + value_len; - } - const struct item_extended_event *text = reinterpret_cast<const struct item_extended_event *>(p); - size_t len = text->item_description_length; - if (non_empty && len) { - current->desc3 = *convert((const char *)text->data, len); - } - - } -} /*}}}*/ - -/* Parse 0x50 Component Descriptor. {{{ - video is a flag, 1=> output the video information, 0=> output the - audio information. seen is a pointer to a counter to ensure we - only output the first one of each (XMLTV can't cope with more than - one) */ -void -EpgRowState::parseComponentDescription(const u_char * data, EpgProgram * current) const { - assert(GetDescriptorTag(data) == 0x50); - const struct descr_component *dc = reinterpret_cast<const struct descr_component *>(data); - - switch (dc->stream_content) { - case 0x01: // Video Info - current->videoHD = ((dc->component_type - 1) & 0x08) ? 1 : 0; - current->videoFrameRate = ((dc->component_type - 1) & 0x04) ? 30 : 25; - current->videoAspect = ((dc->component_type - 1) & 0x03); - break; - case 0x02: // Audio Info - current->audioChannels = dc->component_type; - current->language = Glib::ustring((const char *)&dc->lang_code1, 3); - break; - case 0x03: // Teletext Info - // FIXME: is there a suitable XMLTV output for this? - // if ((dc->component_type)&0x10) //subtitles - // if ((dc->component_type)&0x20) //subtitles for hard of hearing - current->teletextSubtitleLang = Glib::ustring((const char *)&dc->lang_code1, 3); - break; - // case 0x04: // AC3 info - } -} /*}}}*/ - -void -EpgRowState::parseContentDescription(const u_char * data, EpgProgram * current) const { - assert(GetDescriptorTag(data) == 0x54); - const struct descr_content * dc = reinterpret_cast<const struct descr_content *>(data); - for (const u_char * p = reinterpret_cast<const u_char*>(&dc->data); p < data + DESCR_GEN_LEN + dc->descriptor_length; p += NIBBLE_CONTENT_LEN) { - const struct nibble_content *nc = reinterpret_cast<const nibble_content *>(p); - int c1 = (nc->content_nibble_level_1 << 4) + nc->content_nibble_level_2; - // This is weird in the uk, they use user but not content, and almost the same values - current->category = c1 ? c1 : (nc->user_nibble_1 << 4) + nc->user_nibble_2; - } -} - -void -EpgRowState::parseRatingDescription(const u_char * data, EpgProgram * current) const { - assert(GetDescriptorTag(data) == 0x55); - const struct descr_parental_rating * pr = reinterpret_cast<const struct descr_parental_rating *>(data); - for (const u_char * p = reinterpret_cast<const u_char *>(&pr->data); p < data + DESCR_GEN_LEN + pr->descriptor_length; p += PARENTAL_RATING_ITEM_LEN) { - const struct parental_rating_item *pr = reinterpret_cast<const struct parental_rating_item *>(p); - switch (pr->rating) { - case 0x00: /*undefined*/ - break; - case 0x01 ... 0x0F: - current->dvbRating = pr->rating + 3; - break; - case 0x10 ... 0xFF: /*broadcaster defined*/ - break; - } - } -} - -int parsePrivateDataSpecifier(const u_char *data) { - assert(GetDescriptorTag(data) == 0x5F); - return GetPrivateDataSpecifier(data); -} - -/* Parse 0x76 Content Identifier Descriptor. {{{ */ -/* See ETSI TS 102 323, section 12 */ -void -EpgRowState::parseContentIdentifierDescription(const u_char * data, EpgProgram * current) const { - assert(GetDescriptorTag(data) == 0x76); - const struct descr_content_identifier *ci = reinterpret_cast<const struct descr_content_identifier *>(data); - for (const u_char * p = reinterpret_cast<const u_char *>(&ci->data); p < data + DESCR_GEN_LEN + ci->descriptor_length; p += DESCR_GEN_LEN + ci->descriptor_length) { - const struct descr_content_identifier_crid *crid = reinterpret_cast<const struct descr_content_identifier_crid *>(p); - - switch (crid->crid_location) - { - case 0x01: /* Carried in Content Identifier Table (CIT) */ - default: - break; - case 0x00: /* Carried explicitly within descriptor */ - struct descr_content_identifier_crid_local * crid_data = (descr_content_identifier_crid_local_t *)&crid->crid_ref_data; - size_t len = crid_data->crid_length; - switch (crid->crid_type) { - case 0x01: - case 0x31: - current->contentItemID = Glib::ustring((const char *)crid_data->crid_byte, len); - break; - case 0x02: - case 0x32: - current->contentSeriesID = Glib::ustring((const char *)crid_data->crid_byte, len); - break; - case 0x03: - case 0x33: - current->contentRecommendation = Glib::ustring((const char *)crid_data->crid_byte, len); - break; - } - break; - } - } -} /*}}}*/ - -/* Parse Descriptor. {{{ - * Tags should be output in this order: - -'title', 'sub-title', 'desc', 'credits', 'date', 'category', 'language', -'orig-language', 'length', 'icon', 'url', 'country', 'episode-num', -'video', 'audio', 'previously-shown', 'premiere', 'last-chance', -'new', 'subtitles', 'rating', 'star-rating' -*/ -void -EpgRowState::parseDescription(const u_char * data, size_t len, EpgProgram * current) const { - int pds = 0; - for (const u_char * p = data; p < data + len; p += DESCR_GEN_LEN + GetDescriptorLength(p)) { - const struct descr_gen *desc = reinterpret_cast<const struct descr_gen *>(p); - switch (GetDescriptorTag(desc)) { - case 0: - break; - case 0x4D: //short evt desc, [title] [sub-title] - // there can be multiple language versions of these - parseEventDescription(p, current); - break; - case 0x4E: //long evt descriptor [desc] - parseLongEventDescription(p, current); - break; - case 0x50: //component desc [language] [video] [audio] [subtitles] - parseComponentDescription(p, current); - break; - case 0x53: // CA Identifier Descriptor - break; - case 0x54: // content desc [category] - parseContentDescription(p, current); - break; - case 0x55: // Parental Rating Descriptor [rating] - parseRatingDescription(p, current); - break; - case 0x5f: // Private Data Specifier - pds = parsePrivateDataSpecifier(p); - break; - case 0x64: // Data broadcast desc - Text Desc for Data components - break; - case 0x69: // Programm Identification Label - break; - case 0x81: // TODO ??? - if (pds == 5) // ARD_ZDF_ORF - break; - case 0x82: // VPS (ARD, ZDF, ORF) - if (pds == 5) // ARD_ZDF_ORF - // TODO: <programme @vps-start="???"> - break; - case 0x4F: // Time Shifted Event - case 0x52: // Stream Identifier Descriptor - case 0x5E: // Multi Lingual Component Descriptor - case 0x83: // Logical Channel Descriptor (some kind of news-ticker on ARD-MHP-Data?) - case 0x84: // Preferred Name List Descriptor - case 0x85: // Preferred Name Identifier Descriptor - case 0x86: // Eacem Stream Identifier Descriptor - break; - case 0x76: // Content identifier descriptor - parseContentIdentifierDescription(p, current); - break; - default: - break; - } - } -} /*}}}*/ - -/* Check that program has at least a title as is required by xmltv.dtd. {{{ */ -static bool validateDescription(const u_char *data, size_t len) { - for (const u_char * p = data; p < data + len; p += DESCR_GEN_LEN + GetDescriptorLength(p)) { - const struct descr_gen *desc = reinterpret_cast<const struct descr_gen *>(p); - if (GetDescriptorTag(desc) == 0x4D) { - const struct descr_short_event *evtdesc = reinterpret_cast<const struct descr_short_event *>(p); - // make sure that title isn't empty - if (evtdesc->event_name_length) return true; - } - } - return false; -} /*}}}*/ - -bool -EpgRowState::parseInfoTable(const u_char *data, size_t len, const RowProcessorCallback & rp) { - const struct eit *e = reinterpret_cast<const struct eit *>(data); - - len -= 4; //remove CRC - - // For each event listing - bool found = false; - for (const u_char *p = reinterpret_cast<const u_char *>(&e->data); p < data + len; p += EIT_EVENT_LEN + GetEITDescriptorsLoopLength(p)) { - const struct eit_event *evt = reinterpret_cast<const struct eit_event *>(p); - SeenProgram sp(HILO(e->service_id), HILO(evt->event_id)); - if (seenPrograms.find(sp) != seenPrograms.end()) { - continue; - } - seenPrograms.insert(sp); - - // No program info at end! Just skip it - if (GetEITDescriptorsLoopLength(evt) == 0) { - continue; - } - - boost::gregorian::date startDate(boost::gregorian::gregorian_calendar::from_modjulian_day_number(HILO(evt->mjd))); - boost::posix_time::ptime startTime(startDate); - startTime += boost::posix_time::time_duration( - BcdCharToInt(evt->start_time_h) + time_offset, - BcdCharToInt(evt->start_time_m), - BcdCharToInt(evt->start_time_s)); - EpgProgram results; - current = &results; - results.startTime = startTime; - results.stopTime = startTime + boost::posix_time::time_duration( - BcdCharToInt(evt->duration_h), - BcdCharToInt(evt->duration_m), - BcdCharToInt(evt->duration_s)); - - // a program must have a title that isn't empty - if (!validateDescription(reinterpret_cast<const u_char *>(&evt->data), GetEITDescriptorsLoopLength(evt))) { - continue; - } - - - results.serviceID = HILO(e->service_id); - results.eventID = HILO(evt->event_id); - - parseDescription(reinterpret_cast<const u_char *>(&evt->data), GetEITDescriptorsLoopLength(evt), &results); - process(rp); - found = true; - } - return found; -} - -SimpleMessageException(DemuxSetFilterFailure); - -void -EpgRows::filterInput(int fd) const -{ - struct dmx_sct_filter_params sctFilterParams; - memset(&sctFilterParams, 0, sizeof(dmx_sct_filter_params)); - sctFilterParams.pid = 0x12; // EIT data - sctFilterParams.timeout = 0; - sctFilterParams.flags = DMX_IMMEDIATE_START; - sctFilterParams.filter.filter[0] = chan_filter; // 4e is now/next this multiplex, 4f others - sctFilterParams.filter.mask[0] = chan_filter_mask; - - if (ioctl(fd, DMX_SET_FILTER, &sctFilterParams) < 0) { - throw DemuxSetFilterFailure(strerror(errno)); - } -} - -void -EpgRows::execute(const Glib::ustring &, const RowProcessorCallback & rp, ExecContext * ec) const -{ - EpgRowState state; - openInput(ec); - readTables(boost::bind(&EpgRowState::parseInfoTable, &state, _1, _2, rp)); - closeInput(); -} - diff --git a/p2pvr/scanner/epgRows.h b/p2pvr/scanner/epgRows.h deleted file mode 100644 index 61c55ff..0000000 --- a/p2pvr/scanner/epgRows.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef EPGROWS_H -#define EPGROWS_H - -#include "scripts.h" -#include "rowSet.h" -#include "variables.h" -#include "dvbSiReaderHelper.h" -#include <boost/tuple/tuple.hpp> - -class EpgProgram; - -class EpgRows : public RowSet, DvbSiReaderHelper { - public: - EpgRows(const ScriptNodePtr p); - ~EpgRows(); - - void execute(const Glib::ustring &, const RowProcessorCallback &, ExecContext *) const; - void loadComplete(const CommonObjects *); - - private: - void filterInput(int fd) const; -}; - -class EpgRowState : public RowState, DvbSiParserHelper { - public: - EpgRowState(); - const Columns & getColumns() const; - RowAttribute resolveAttr(const Glib::ustring & attrName) const; - - bool parseInfoTable(const u_char *data, size_t len, const RowProcessorCallback &); - - private: - void parseEventDescription(const u_char *data, EpgProgram * current) const; - void parseLongEventDescription(const u_char *data, EpgProgram * current) const; - void parseComponentDescription(const u_char *data, EpgProgram * current) const; - void parseContentDescription(const u_char *data, EpgProgram * current) const; - void parseRatingDescription(const u_char *data, EpgProgram * current) const; - void parseContentIdentifierDescription(const u_char *data, EpgProgram * current) const; - void parseDescription(const u_char * data, size_t len, EpgProgram * current) const; - - typedef boost::tuple<int, int> SeenProgram; - typedef std::set<SeenProgram> SeenPrograms; - SeenPrograms seenPrograms; - - Columns columns; - friend class EpgRows; - mutable EpgProgram * current; -}; - - -#endif - diff --git a/p2pvr/scanner/serviceRows.cpp b/p2pvr/scanner/serviceRows.cpp deleted file mode 100644 index e743858..0000000 --- a/p2pvr/scanner/serviceRows.cpp +++ /dev/null @@ -1,155 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <string.h> -#include <sys/ioctl.h> -#include <errno.h> -#include <stdint.h> -#include <time.h> -#include <boost/bind.hpp> -#include <boost/date_time/gregorian_calendar.hpp> - -#include <linux/dvb/dmx.h> -#include "si_tables.h" - -#include "rowProcessor.h" -#include "serviceRows.h" - -class Service { - public: - VariableType serviceID; - VariableType name; - VariableType type; - VariableType providerName; - VariableType defaultAuthority; - VariableType transportID; -}; - -void -ServiceRows::loadComplete(const CommonObjects *) -{ -} - -ServiceRowState::ServiceRowState() : - current(NULL) -{ -} - -const Columns & -ServiceRowState::getColumns() const -{ - return columns; -} - -SimpleMessageException(NoSuchAttribute); -template <class C, class M> -const M & -getMember(M C::*t, C * p) { - return p->*t; -} -#define returnAttr(name) if (attrName == #name) return boost::bind(getMember<Service, VariableType>, &Service::name, boost::ref(current)) -RowState::RowAttribute -ServiceRowState::resolveAttr(const Glib::ustring & attrName) const { - returnAttr(serviceID); - returnAttr(name); - returnAttr(type); - returnAttr(providerName); - returnAttr(defaultAuthority); - returnAttr(transportID); - throw NoSuchAttribute(attrName); -} - -DECLARE_LOADER("servicerows", ServiceRows); - -ServiceRows::ServiceRows(const ScriptNodePtr e) : - RowSet(e), - DvbSiReaderHelper(e) -{ -} - -ServiceRows::~ServiceRows() -{ -} - -SimpleMessageException(DemuxSetFilterFailure); - -void -ServiceRows::filterInput(int fd) const -{ - struct dmx_sct_filter_params sctFilterParams; - memset(&sctFilterParams, 0, sizeof(dmx_sct_filter_params)); - sctFilterParams.pid = 0x11; // Service data - sctFilterParams.timeout = 0; - sctFilterParams.flags = DMX_IMMEDIATE_START; - - if (ioctl(fd, DMX_SET_FILTER, &sctFilterParams) < 0) { - throw DemuxSetFilterFailure(strerror(errno)); - } -} - -void -ServiceRows::execute(const Glib::ustring &, const RowProcessorCallback & rp, ExecContext * ec) const -{ - ServiceRowState state; - openInput(ec); - readTables(boost::bind(&ServiceRowState::parseInfoTable, &state, _1, _2, rp)); - closeInput(); -} - -bool -ServiceRowState::parseInfoTable(const u_char * data, size_t len, const RowProcessorCallback & rp) { - const struct sit *e = reinterpret_cast<const struct sit *>(data); - assert(e->tableid == 0x42 || e->tableid == 0x46); - len -= 4; //remove CRC - - // For each event listing - bool found = false; - for (const u_char * p = e->serviceData; p < data + len; ) { - const struct si * sid = reinterpret_cast<const struct si *>(p); - int sID = be16toh(sid->serviceid); - bool seen = (seenServices.find(sID) != seenServices.end()); - size_t dll = HILO(sid->desc_loop_len); - Service serv; - current = &serv; - for (p = sid->descData; p < sid->descData + dll; p += DESCR_GEN_LEN + GetDescriptorLength(p)) { - if (!seen) { - serv.serviceID = sID; - serv.transportID = be16toh(e->transport_stream_id); - const struct descr_gen *desc = reinterpret_cast<const struct descr_gen *>(p); - switch (GetDescriptorTag(desc)) { - case 0x48: // Service descriptor - parseServiceDescriptor(p, GetDescriptorLength(desc), &serv); - break; - case 0x73: // Service authority descriptor - parseServiceAuthDescriptor(p, GetDescriptorLength(desc), &serv); - break; - default: - break; - } - } - } - if (!seen) { - found = true; - seenServices.insert(sID); - process(rp); - } - } - return found; -} - -void -ServiceRowState::parseServiceDescriptor(const u_char * p, size_t, Service * current) const -{ - current->type = p[2]; - if (p[3]) { - current->providerName = *convert((const char *)(p + 4), p[3]); - } - current->name = *convert((const char *)(p + 5 + p[3]), p[4 + p[3]]); -} - -void -ServiceRowState::parseServiceAuthDescriptor(const u_char * p, size_t len, Service * current) const -{ - current->defaultAuthority = *convert((const char *)(p + 2), len); -} - diff --git a/p2pvr/scanner/serviceRows.h b/p2pvr/scanner/serviceRows.h deleted file mode 100644 index 36cbee7..0000000 --- a/p2pvr/scanner/serviceRows.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef SERVICEROWS_H -#define SERVICEROWS_H - -#include "scripts.h" -#include "rowSet.h" -#include "variables.h" -#include "dvbSiReaderHelper.h" - -class Service; - -class ServiceRows : public RowSet, DvbSiReaderHelper { - public: - ServiceRows(const ScriptNodePtr p); - ~ServiceRows(); - - void execute(const Glib::ustring &, const RowProcessorCallback &, ExecContext *) const; - void loadComplete(const CommonObjects *); - - private: - void filterInput(int fd) const; -}; - -class ServiceRowState : public RowState, DvbSiParserHelper { - public: - ServiceRowState(); - const Columns & getColumns() const; - RowAttribute resolveAttr(const Glib::ustring & attrName) const; - bool parseInfoTable(const u_char *data, size_t len, const RowProcessorCallback &); - - private: - void parseServiceDescriptor(const u_char *data, size_t len, Service * current) const; - void parseServiceAuthDescriptor(const u_char *data, size_t len, Service * current) const; - - typedef std::set<int> SeenServices; - SeenServices seenServices; - - Columns columns; - friend class ServiceRows; - mutable Service * current; -}; - -#endif - diff --git a/p2pvr/scanner/si_tables.h b/p2pvr/scanner/si_tables.h deleted file mode 100644 index c48fdae..0000000 --- a/p2pvr/scanner/si_tables.h +++ /dev/null @@ -1,1667 +0,0 @@ -////////////////////////////////////////////////////////////// -/// /// -/// si_tables.h: definitions for data structures of the /// -/// incoming SI data stream /// -/// /// -////////////////////////////////////////////////////////////// - -// $Revision: 84 $ -// $Date: 2010-03-05 17:46:16 +0000 (Fri, 05 Mar 2010) $ -// $Author: cpaton $ -// -// (C) 2001-03 Rolf Hakenes <hakenes@hippomi.de>, under the -// GNU GPL with contribution of Oleg Assovski, -// www.satmania.com -// -// libsi 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, or (at your option) -// any later version. -// -// libsi 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 may have received a copy of the GNU General Public License -// along with libsi; see the file COPYING. If not, write to the -// Free Software Foundation, Inc., 59 Temple Place - Suite 330, -// Boston, MA 02111-1307, USA. - -#include <stdint.h> - -#define HILO(x) (x##_hi << 8 | x##_lo) -#define HILO2(x) (x##1 << 8 | x##2) -#define HILO3(x) (x##1 << 16 | x##2 << 8 | x##3) -#define HILO4(x) (x##4 << 24 | x##2 << 16 | x##3 << 8 | x##4) - -#define MjdToEpochTime(x) ((HILO(x)-40587)*86400) -#define BcdTimeToSeconds(x) ((3600 * ((10*((x##_h & 0xF0)>>4)) + (x##_h & 0xF))) + \ - (60 * ((10*((x##_m & 0xF0)>>4)) + (x##_m & 0xF))) + \ - ((10*((x##_s & 0xF0)>>4)) + (x##_s & 0xF))) -#define BcdTimeToMinutes(x) ((60 * ((10*((x##_h & 0xF0)>>4)) + (x##_h & 0xF))) + \ - (((10*((x##_m & 0xF0)>>4)) + (x##_m & 0xF)))) -#define BcdCharToInt(x) (10*((x & 0xF0)>>4) + (x & 0xF)) -#define CheckBcdChar(x) ((((x & 0xF0)>>4) <= 9) && \ - ((x & 0x0F) <= 9)) -#define CheckBcdSignedChar(x) ((((x & 0xF0)>>4) >= 0) && (((x & 0xF0)>>4) <= 9) && \ - ((x & 0x0F) >= 0) && ((x & 0x0F) <= 9)) - -#define GetTableId(x) (((si_tab_t *)(x))->table_id) -#define GetSectionLength(x) HILO(((si_tab_t *)(x))->section_length) -typedef struct si_tab { - u_char table_id /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char section_syntax_indicator :1; - u_char dummy :1; // has to be 0 - u_char :2; - u_char section_length_hi :4; -#else - u_char section_length_hi :4; - u_char :2; - u_char dummy :1; // has to be 0 - u_char section_syntax_indicator :1; -#endif - u_char section_length_lo /*:8*/; -} si_tab_t; - -/* - * - * ETSI ISO/IEC 13818-1 specifies SI which is referred to as PSI. The PSI - * data provides information to enable automatic configuration of the - * receiver to demultiplex and decode the various streams of programs - * within the multiplex. The PSI data is structured as four types of table. - * The tables are transmitted in sections. - * - * 1) Program Association Table (PAT): - * - * - for each service in the multiplex, the PAT indicates the location - * (the Packet Identifier (PID) values of the Transport Stream (TS) - * packets) of the corresponding Program Map Table (PMT). - * It also gives the location of the Network Information Table (NIT). - * - */ -#define TableHasMoreSections(x) (((pat_t *)(x))->last_section_number > ((pat_t *)(x))->section_number) -#define GetSectionNumber(x) ((pat_t *)(x))->section_number -#define GetLastSectionNumber(x) ((pat_t *)(x))->last_section_number -#define GetServiceId(x) HILO(((eit_t *)(x))->service_id) -#define GetLastTableId(x) ((eit_t *)(x))->segment_last_table_id -#define GetSegmentLastSectionNumber(x) ((eit_t *)(x))->segment_last_section_number -typedef struct pat { - u_char table_id /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char section_syntax_indicator :1; - u_char dummy :1; // has to be 0 - u_char :2; - u_char section_length_hi :4; -#else - u_char section_length_hi :4; - u_char :2; - u_char dummy :1; // has to be 0 - u_char section_syntax_indicator :1; -#endif - u_char section_length_lo /*:8*/; - u_char transport_stream_id_hi /*:8*/; - u_char transport_stream_id_lo /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char :2; - u_char version_number :5; - u_char current_next_indicator :1; -#else - u_char current_next_indicator :1; - u_char version_number :5; - u_char :2; -#endif - u_char section_number /*:8*/; - u_char last_section_number /*:8*/; -} pat_t; -#define PAT_LEN sizeof (pat_t) - -typedef struct pat_prog { - u_char program_number_hi /*:8*/; - u_char program_number_lo /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char :3; - u_char network_pid_hi :5; -#else - u_char network_pid_hi :5; - u_char :3; -#endif - u_char network_pid_lo /*:8*/; - /* or program_map_pid (if prog_num=0)*/ -} pat_prog_t; -#define PAT_PROG_LEN sizeof (pat_prog_t) - -/* - * - * 2) Conditional Access Table (CAT): - * - * - the CAT provides information on the CA systems used in the - * multiplex; the information is private and dependent on the CA - * system, but includes the location of the EMM stream, when - * applicable. - * - */ -typedef struct cat { - u_char table_id /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char section_syntax_indicator :1; - u_char dummy :1; // has to be 0 - u_char :2; - u_char section_length_hi :4; -#else - u_char section_length_hi :4; - u_char :2; - u_char dummy :1; // has to be 0 - u_char section_syntax_indicator :1; -#endif - u_char section_length_lo /*:8*/; - u_char :8; - u_char :8; -#if BYTE_ORDER == BIG_ENDIAN - u_char :2; - u_char version_number :5; - u_char current_next_indicator :1; -#else - u_char current_next_indicator :1; - u_char version_number :5; - u_char :2; -#endif - u_char section_number /*:8*/; - u_char last_section_number /*:8*/; -} cat_t; -#define CAT_LEN sizeof (cat_t) - -/* - * - * 3) Program Map Table (PMT): - * - * - the PMT identifies and indicates the locations of the streams that - * make up each service, and the location of the Program Clock - * Reference fields for a service. - * - */ -typedef struct pmt { - u_char table_id /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char section_syntax_indicator :1; - u_char dummy :1; // has to be 0 - u_char :2; - u_char section_length_hi :4; -#else - u_char section_length_hi :4; - u_char :2; - u_char dummy :1; // has to be 0 - u_char section_syntax_indicator :1; -#endif - u_char section_length_lo /*:8*/; - u_char program_number_hi /*:8*/; - u_char program_number_lo /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char :2; - u_char version_number :5; - u_char current_next_indicator :1; -#else - u_char current_next_indicator :1; - u_char version_number :5; - u_char :2; -#endif - u_char section_number /*:8*/; - u_char last_section_number /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char :3; - u_char PCR_PID_hi :5; -#else - u_char PCR_PID_hi :5; - u_char :3; -#endif - u_char PCR_PID_lo /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char :4; - u_char program_info_length_hi :4; -#else - u_char program_info_length_hi :4; - u_char :4; -#endif - u_char program_info_length_lo /*:8*/; - //descriptors -} pmt_t; -#define PMT_LEN sizeof (pmt_t) - -typedef struct pmt_info { - u_char stream_type /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char :3; - u_char elementary_PID_hi :5; -#else - u_char elementary_PID_hi :5; - u_char :3; -#endif - u_char elementary_PID_lo /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char :4; - u_char ES_info_length_hi :4; -#else - u_char ES_info_length_hi :4; - u_char :4; -#endif - u_char ES_info_length_lo /*:8*/; - // descriptors -} pmt_info_t; -#define PMT_INFO_LEN sizeof (pmt_info_t) - -/* - * - * 4) Network Information Table (NIT): - * - * - the NIT is intended to provide information about the physical - * network. The syntax and semantics of the NIT are defined in - * ETSI EN 300 468. - * - */ -typedef struct nit { - u_char table_id /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char section_syntax_indicator :1; - u_char :3; - u_char section_length_hi :4; -#else - u_char section_length_hi :4; - u_char :3; - u_char section_syntax_indicator :1; -#endif - u_char section_length_lo /*:8*/; - u_char network_id_hi /*:8*/; - u_char network_id_lo /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char :2; - u_char version_number :5; - u_char current_next_indicator :1; -#else - u_char current_next_indicator :1; - u_char version_number :5; - u_char :2; -#endif - u_char section_number /*:8*/; - u_char last_section_number /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char :4; - u_char network_descriptor_length_hi :4; -#else - u_char network_descriptor_length_hi :4; - u_char :4; -#endif - u_char network_descriptor_length_lo /*:8*/; - /* descriptors */ -} nit_t; -#define NIT_LEN sizeof (nit_t) - -typedef struct nit_mid { // after descriptors -#if BYTE_ORDER == BIG_ENDIAN - u_char :4; - u_char transport_stream_loop_length_hi :4; -#else - u_char transport_stream_loop_length_hi :4; - u_char :4; -#endif - u_char transport_stream_loop_length_lo /*:8*/; -} nit_mid_t; -#define SIZE_NIT_MID sizeof (nit_mid_t) - -typedef struct nit_end { - long CRC; -} nit_end_t; -#define SIZE_NIT_END sizeof (nit_end_t) - -typedef struct nit_ts { - u_char transport_stream_id_hi /*:8*/; - u_char transport_stream_id_lo /*:8*/; - u_char original_network_id_hi /*:8*/; - u_char original_network_id_lo /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char :4; - u_char transport_descriptors_length_hi :4; -#else - u_char transport_descriptors_length_hi :4; - u_char :4; -#endif - u_char transport_descriptors_length_lo /*:8*/; - /* descriptors */ -} nit_ts_t; -#define NIT_TS_LEN sizeof (nit_ts_t) - -/* - * - * In addition to the PSI, data is needed to provide identification of - * services and events for the user. In contrast with the PAT, CAT, and - * PMT of the PSI, which give information only for the multiplex in which - * they are contained (the actual multiplex), the additional information - * defined within the present document can also provide information on - * services and events carried by different multiplexes, and even on other - * networks. This data is structured as nine tables: - * - * 1) Bouquet Association Table (BAT): - * - * - the BAT provides information regarding bouquets. As well as giving - * the name of the bouquet, it provides a list of services for each - * bouquet. - * - */ -/* SEE NIT (It has the same structure but has different allowed descriptors) */ -/* - * - * 2) Service Description Table (SDT): - * - * - the SDT contains data describing the services in the system e.g. - * names of services, the service provider, etc. - * - */ -typedef struct sdt { - u_char table_id /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char section_syntax_indicator :1; - u_char :3; - u_char section_length_hi :4; -#else - u_char section_length_hi :4; - u_char :3; - u_char section_syntax_indicator :1; -#endif - u_char section_length_lo /*:8*/; - u_char transport_stream_id_hi /*:8*/; - u_char transport_stream_id_lo /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char :2; - u_char version_number :5; - u_char current_next_indicator :1; -#else - u_char current_next_indicator :1; - u_char version_number :5; - u_char :2; -#endif - u_char section_number /*:8*/; - u_char last_section_number /*:8*/; - u_char original_network_id_hi /*:8*/; - u_char original_network_id_lo /*:8*/; - u_char :8; -} sdt_t; -#define SDT_LEN sizeof (sdt_t) -#define GetSDTTransportStreamId(x) HILO(((sdt_t *)x)->transport_stream_id) -#define GetSDTOriginalNetworkId(x) HILO(((sdt_t *)x)->original_network_id) - -typedef struct sdt_descr { - u_char service_id_hi /*:8*/; - u_char service_id_lo /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char :6; - u_char eit_schedule_flag :1; - u_char eit_present_following_flag :1; - u_char running_status :3; - u_char free_ca_mode :1; - u_char descriptors_loop_length_hi :4; -#else - u_char eit_present_following_flag :1; - u_char eit_schedule_flag :1; - u_char :6; - u_char descriptors_loop_length_hi :4; - u_char free_ca_mode :1; - u_char running_status :3; -#endif - u_char descriptors_loop_length_lo /*:8*/; - u_char data[]; -} sdt_descr_t; -#define SDT_DESCR_LEN sizeof (sdt_descr_t) -#define GetSDTDescriptorsLoopLength(x) HILO(((sdt_descr_t *)x)->descriptors_loop_length) - -/* - * - * 3) Event Information Table (EIT): - * - * - the EIT contains data concerning events or programmes such as event - * name, start time, duration, etc.; - the use of different descriptors - * allows the transmission of different kinds of event information e.g. - * for different service types. - * - */ -typedef struct eit { - u_char table_id /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char section_syntax_indicator :1; - u_char :3; - u_char section_length_hi :4; -#else - u_char section_length_hi :4; - u_char :3; - u_char section_syntax_indicator :1; -#endif - u_char section_length_lo /*:8*/; - u_char service_id_hi /*:8*/; - u_char service_id_lo /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char :2; - u_char version_number :5; - u_char current_next_indicator :1; -#else - u_char current_next_indicator :1; - u_char version_number :5; - u_char :2; -#endif - u_char section_number /*:8*/; - u_char last_section_number /*:8*/; - u_char transport_stream_id_hi /*:8*/; - u_char transport_stream_id_lo /*:8*/; - u_char original_network_id_hi /*:8*/; - u_char original_network_id_lo /*:8*/; - u_char segment_last_section_number /*:8*/; - u_char segment_last_table_id /*:8*/; - u_char data[]; /* struct eit_event */ -} eit_t; -#define EIT_LEN sizeof (eit_t) - -typedef struct eit_event { - u_char event_id_hi /*:8*/; - u_char event_id_lo /*:8*/; - u_char mjd_hi /*:8*/; - u_char mjd_lo /*:8*/; - u_char start_time_h /*:8*/; - u_char start_time_m /*:8*/; - u_char start_time_s /*:8*/; - u_char duration_h /*:8*/; - u_char duration_m /*:8*/; - u_char duration_s /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char running_status :3; - u_char free_ca_mode :1; - u_char descriptors_loop_length_hi :4; -#else - u_char descriptors_loop_length_hi :4; - u_char free_ca_mode :1; - u_char running_status :3; -#endif - u_char descriptors_loop_length_lo /*:8*/; - u_char data[]; /* struct descr_gen */ -} eit_event_t; -#define EIT_EVENT_LEN sizeof (eit_event_t) -#define GetEITDescriptorsLoopLength(x) HILO(((eit_event_t *)x)->descriptors_loop_length) - -/* - * - * 4) Running Status Table (RST): - * - * - the RST gives the status of an event (running/not running). The RST - * updates this information and allows timely automatic switching to - * events. - * - */ - /* TO BE DONE */ -/* - * - * 5) Time and Date Table (TDT): - * - * - the TDT gives information relating to the present time and date. - * This information is given in a separate table due to the frequent - * updating of this information. - * - */ - -typedef struct tdt { - u_char table_id /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char section_syntax_indicator :1; - u_char :3; - u_char section_length_hi :4; -#else - u_char section_length_hi :4; - u_char :3; - u_char section_syntax_indicator :1; -#endif - u_char section_length_lo /*:8*/; - u_char utc_mjd_hi /*:8*/; - u_char utc_mjd_lo /*:8*/; - u_char utc_time_h /*:8*/; - u_char utc_time_m /*:8*/; - u_char utc_time_s /*:8*/; -} tdt_t; -#define TDT_LEN sizeof (tdt_t) - -/* - * - * 6) Time Offset Table (TOT): - * - * - the TOT gives information relating to the present time and date and - * local time offset. This information is given in a separate table due - * to the frequent updating of the time information. - * - */ -typedef struct tot { - u_char table_id /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char section_syntax_indicator :1; - u_char :3; - u_char section_length_hi :4; -#else - u_char section_length_hi :4; - u_char :3; - u_char section_syntax_indicator :1; -#endif - u_char section_length_lo /*:8*/; - u_char utc_mjd_hi /*:8*/; - u_char utc_mjd_lo /*:8*/; - u_char utc_time_h /*:8*/; - u_char utc_time_m /*:8*/; - u_char utc_time_s /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char :4; - u_char descriptors_loop_length_hi :4; -#else - u_char descriptors_loop_length_hi :4; - u_char :4; -#endif - u_char descriptors_loop_length_lo /*:8*/; -} tot_t; -#define TOT_LEN sizeof (tot_t) - -/* - * - * 7) Stuffing Table (ST): - * - * - the ST is used to invalidate existing sections, for example at - * delivery system boundaries. - * - */ - /* TO BE DONE */ -/* - * - * 8) Selection Information Table (SIT): - * - * - the SIT is used only in "partial" (i.e. recorded) bitstreams. It - * carries a summary of the SI information required to describe the - * streams in the partial bitstream. - * - */ -struct sit { - uint8_t tableid; -#if BYTE_ORDER == BIG_ENDIAN - uint8_t section_length_hi : 4; - bool reserved1 : 2; - bool reserved_fu : 1; - bool section_syntax : 1; -#else - bool section_syntax : 1; - bool reserved_fu : 1; - bool reserved1 : 2; - uint8_t section_length_hi : 4; -#endif - uint8_t section_length_lo; - uint16_t transport_stream_id ; - bool reserved2 : 2; - uint8_t version : 5; - bool current_next_ind : 1; - uint8_t section_number; - uint8_t last_section_number; - uint16_t original_network_id; - uint8_t reserved3; - u_char serviceData[]; -} __attribute__((packed)); -struct si { - uint16_t serviceid; - uint8_t reserved_fu : 6; - bool eit_sched : 1; - bool eit_now_and_next : 1; -#if BYTE_ORDER == BIG_ENDIAN - uint8_t running_status : 3; - bool free_ca_mode : 1; - uint8_t desc_loop_len_hi : 4; -#else - uint8_t desc_loop_len_hi : 4; - bool free_ca_mode : 1; - uint8_t running_status : 3; -#endif - uint8_t desc_loop_len_lo; - u_char descData[]; -} __attribute__((packed)); -/* - * - * 9) Discontinuity Information Table (DIT): - * - * - the DIT is used only in "partial" (i.e. recorded) bitstreams. - * It is inserted where the SI information in the partial bitstream may - * be discontinuous. Where applicable the use of descriptors allows a - * flexible approach to the organization of the tables and allows for - * future compatible extensions. - * - */ - /* TO BE DONE */ -/* - * - * The following describes the different descriptors that can be used within - * the SI. - * - * The following semantics apply to all the descriptors defined in this - * subclause: - * - * descriptor_tag: The descriptor tag is an 8-bit field which identifies - * each descriptor. Those values with MPEG-2 normative - * meaning are described in ISO/IEC 13818-1. The values of - * descriptor_tag are defined in 'libsi.h' - * descriptor_length: The descriptor length is an 8-bit field specifying the - * total number of bytes of the data portion of the - * descriptor following the byte defining the value of - * this field. - * - */ - -typedef struct descr_gen { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; -} descr_gen_t; -#define DESCR_GEN_LEN sizeof (descr_gen_t) -#define CastGenericDescriptor(x) ((descr_gen_t *)(x)) - -#define GetDescriptorTag(x) (((descr_gen_t *)x)->descriptor_tag) -#define GetDescriptorLength(x) (((descr_gen_t *)x)->descriptor_length) - -/* 0x09 ca_descriptor */ -typedef struct descr_ca { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; - u_char CA_type_hi /*:8*/; - u_char CA_type_lo /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char :3; - u_char CA_PID_hi :5; -#else - u_char CA_PID_hi :5; - u_char :3; -#endif - u_char CA_PID_lo /*:8*/; -} descr_ca_t; -#define DESCR_CA_LEN sizeof (descr_ca_t) -#define CastCaDescriptor(x) ((descr_ca_t *)(x)) - -/* 0x0A iso_639_language_descriptor */ -typedef struct descr_iso_639_language { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; - u_char lang_code1 /*:8*/; - u_char lang_code2 /*:8*/; - u_char lang_code3 /*:8*/; -} descr_iso_639_language_t; -#define DESCR_ISO_639_LANGUAGE_LEN sizeof (descr_iso_639_language_t) -#define CastIso639LanguageDescriptor(x) ((descr_iso_639_language_t *)(x)) - -/* 0x40 network_name_descriptor */ -typedef struct descr_network_name { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; -} descr_network_name_t; -#define DESCR_NETWORK_NAME_LEN sizeof (descr_network_name_t) -#define CastNetworkNameDescriptor(x) ((descr_network_name_t *)(x)) - -/* 0x41 service_list_descriptor */ -typedef struct descr_service_list { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; -} descr_service_list_t; -#define DESCR_SERVICE_LIST_LEN sizeof (descr_service_list_t) -#define CastServiceListDescriptor(x) ((descr_service_list_t *)(x)) - -typedef struct descr_service_list_loop { - u_char service_id_hi /*:8*/; - u_char service_id_lo /*:8*/; - u_char service_type /*:8*/; -} descr_service_list_loop_t; -#define DESCR_SERVICE_LIST_LOOP_LEN sizeof (descr_service_list_loop_t) -#define CastServiceListDescriptorLoop(x) ((descr_service_list_loop_t *)(x)) - -/* 0x42 stuffing_descriptor */ -typedef struct descr_stuffing { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; - u_char data[]; -} descr_stuffing_t; -#define DESCR_STUFFING_LEN sizeof (descr_stuffing_t) -#define CastStuffingDescriptor(x) ((descr_stuffing_t *)(x)) - -/* 0x43 satellite_delivery_system_descriptor */ -typedef struct descr_satellite_delivery_system { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; - u_char frequency1 /*:8*/; - u_char frequency2 /*:8*/; - u_char frequency3 /*:8*/; - u_char frequency4 /*:8*/; - u_char orbital_position1 /*:8*/; - u_char orbital_position2 /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char west_east_flag :1; - u_char polarization :2; - u_char modulation :5; -#else - u_char modulation :5; - u_char polarization :2; - u_char west_east_flag :1; -#endif - u_char symbol_rate1 /*:8*/; - u_char symbol_rate2 /*:8*/; - u_char symbol_rate3 /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char symbol_rate4 :4; - u_char fec_inner :4; -#else - u_char fec_inner :4; - u_char symbol_rate4 :4; -#endif -} descr_satellite_delivery_system_t; -#define DESCR_SATELLITE_DELIVERY_SYSTEM_LEN sizeof (descr_satellite_delivery_system_t) -#define CastSatelliteDeliverySystemDescriptor(x) ((descr_satellite_delivery_system_t *)(x)) -#define GetSatelliteDeliverySystemFrequency(x) HILO4(((descr_satellite_delivert_system_t *)x)->frequency) -#define GetSatelliteDeliverySystemSymbolRate(x) HILO4(((descr_satellite_delivert_system_t *)x)->symbol_rate) - - -/* 0x44 cable_delivery_system_descriptor */ -typedef struct descr_cable_delivery_system { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; - u_char frequency1 /*:8*/; - u_char frequency2 /*:8*/; - u_char frequency3 /*:8*/; - u_char frequency4 /*:8*/; - u_char :8; -#if BYTE_ORDER == BIG_ENDIAN - u_char :4; - u_char fec_outer :4; -#else - u_char fec_outer :4; - u_char :4; -#endif - u_char modulation /*:8*/; - u_char symbol_rate1 /*:8*/; - u_char symbol_rate2 /*:8*/; - u_char symbol_rate3 /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char symbol_rate4 :4; - u_char fec_inner :4; -#else - u_char fec_inner :4; - u_char symbol_rate4 :4; -#endif -} descr_cable_delivery_system_t; -#define DESCR_CABLE_DELIVERY_SYSTEM_LEN sizeof (descr_cable_delivery_system_t) -#define CastCableDeliverySystemDescriptor(x) ((descr_cable_delivery_system_t *)(x)) -#define GetCableDeliverySystemFrequency(x) HILO4(((descr_cable_delivert_system_t *)x)->frequency) -#define GetCableDeliverySystemSymbolRate(x) HILO4(((descr_cable_delivert_system_t *)x)->symbol_rate) - -/* 0x45 vbi_data_descriptor */ -typedef struct descr_vbi_data { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; - /* TBD */ -} descr_vbi_data_t; -#define DESCR_VBI_DATA_LEN sizeof (descr_vbi_data_t) -#define CastVbiDataDescriptor(x) ((descr_vbi_data_t *)(x)) - -/* 0x46 vbi_teletext_descriptor */ -typedef struct descr_vbi_teletext { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; - /* TBD */ -} descr_vbi_teletext_t; -#define DESCR_VBI_TELETEXT_LEN sizeof (descr_vbi_teletext_t) -#define CastVbiDescriptor(x) ((descr_vbi_teletext_t *)(x)) - -/* 0x47 bouquet_name_descriptor */ -typedef struct descr_bouquet_name { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; -} descr_bouquet_name_t; -#define DESCR_BOUQUET_NAME_LEN sizeof (descr_bouquet_name_t) - -#define CastBouquetNameDescriptor(x) ((descr_bouquet_name_t *)(x)) - -/* 0x48 service_descriptor */ -typedef struct descr_service { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; - u_char service_type /*:8*/; - u_char provider_name_length /*:8*/; -} descr_service_t; -#define DESCR_SERVICE_LEN sizeof (descr_service_t) -#define CastServiceDescriptor(x) ((descr_service_t *)(x)) - -/* 0x49 country_availability_descriptor */ -typedef struct descr_country_availability { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char country_availability_flag :1; - u_char :7; -#else - u_char :7; - u_char country_availability_flag :1; -#endif -} descr_country_availability_t; -#define DESCR_COUNTRY_AVAILABILITY_LEN sizeof (descr_country_availability_t) -#define CastCountryAvailabilityDescriptor(x) ((descr_country_availability_t *)(x)) - -/* 0x4A linkage_descriptor */ -typedef struct descr_linkage { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; - u_char transport_stream_id_hi /*:8*/; - u_char transport_stream_id_lo /*:8*/; - u_char original_network_id_hi /*:8*/; - u_char original_network_id_lo /*:8*/; - u_char service_id_hi /*:8*/; - u_char service_id_lo /*:8*/; - u_char linkage_type /*:8*/; -} descr_linkage_t; -#define DESCR_LINKAGE_LEN sizeof (descr_linkage_t) -#define CastLinkageDescriptor(x) ((descr_linkage_t *)(x)) - -/* 0x4B nvod_reference_descriptor */ -typedef struct descr_nvod_reference { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; - u_char data[]; /* struct item_nvod_reference */ -} descr_nvod_reference_t; -#define DESCR_NVOD_REFERENCE_LEN sizeof (descr_nvod_reference_t) -#define CastNvodReferenceDescriptor(x) ((descr_nvod_reference_t *)(x)) - -typedef struct item_nvod_reference { - u_char transport_stream_id_hi /*:8*/; - u_char transport_stream_id_lo /*:8*/; - u_char original_network_id_hi /*:8*/; - u_char original_network_id_lo /*:8*/; - u_char service_id_hi /*:8*/; - u_char service_id_lo /*:8*/; -} item_nvod_reference_t; -#define ITEM_NVOD_REFERENCE_LEN sizeof (item_nvod_reference_t) -#define CastNvodReferenceItem(x) ((item_nvod_reference_t *)(x)) - -/* 0x4C time_shifted_service_descriptor */ -typedef struct descr_time_shifted_service { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; - u_char reference_service_id_hi /*:8*/; - u_char reference_service_id_lo /*:8*/; -} descr_time_shifted_service_t; -#define DESCR_TIME_SHIFTED_SERVICE_LEN sizeof (descr_time_shifted_service_t) -#define CastTimeShiftedServiceDescriptor(x) ((descr_time_shifted_service_t *)(x)) - -/* 0x4D short_event_descriptor */ -typedef struct descr_short_event { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; - u_char lang_code1 /*:8*/; - u_char lang_code2 /*:8*/; - u_char lang_code3 /*:8*/; - u_char event_name_length /*:8*/; - u_char data[]; -} descr_short_event_t; -#define DESCR_SHORT_EVENT_LEN sizeof (descr_short_event_t) -#define CastShortEventDescriptor(x) ((descr_short_event_t *)(x)) - -/* 0x4E extended_event_descriptor */ -typedef struct descr_extended_event { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char descriptor_number :4; - u_char last_descriptor_number :4; -#else - u_char last_descriptor_number :4; - u_char descriptor_number :4; -#endif - u_char lang_code1 /*:8*/; - u_char lang_code2 /*:8*/; - u_char lang_code3 /*:8*/; - u_char length_of_items /*:8*/; - u_char data[]; /* struct item_extended_event */ -} descr_extended_event_t; -#define DESCR_EXTENDED_EVENT_LEN sizeof (descr_extended_event_t) -#define CastExtendedEventDescriptor(x) ((descr_extended_event_t *)(x)) - -typedef struct item_extended_event { - u_char item_description_length /*:8*/; - u_char data[]; -} item_extended_event_t; -#define ITEM_EXTENDED_EVENT_LEN sizeof (item_extended_event_t) -#define CastExtendedEventItem(x) ((item_extended_event_t *)(x)) - -/* 0x4F time_shifted_event_descriptor */ -typedef struct descr_time_shifted_event { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; - u_char reference_service_id_hi /*:8*/; - u_char reference_service_id_lo /*:8*/; - u_char reference_event_id_hi /*:8*/; - u_char reference_event_id_lo /*:8*/; -} descr_time_shifted_event_t; -#define DESCR_TIME_SHIFTED_EVENT_LEN sizeof (descr_time_shifted_event_t) -#define CastTimeShiftedEventDescriptor(x) ((descr_time_shifted_event_t *)(x)) - -/* 0x50 component_descriptor */ -typedef struct descr_component { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char :4; - u_char stream_content :4; -#else - u_char stream_content :4; - u_char :4; -#endif - u_char component_type /*:8*/; - u_char component_tag /*:8*/; - u_char lang_code1 /*:8*/; - u_char lang_code2 /*:8*/; - u_char lang_code3 /*:8*/; - u_char data[]; -} descr_component_t; -#define DESCR_COMPONENT_LEN sizeof (descr_component_t) -#define CastComponentDescriptor(x) ((descr_component_t *)(x)) - -/* 0x51 mosaic_descriptor */ -typedef struct descr_mosaic { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char mosaic_entry_point :1; - u_char number_of_horizontal_elementary_cells :3; - u_char :1; - u_char number_of_vertical_elementary_cells :3; -#else - u_char number_of_vertical_elementary_cells :3; - u_char :1; - u_char number_of_horizontal_elementary_cells :3; - u_char mosaic_entry_point :1; -#endif - u_char data[]; /* struct item_mosaic */ -} descr_mosaic_t; -#define DESCR_MOSAIC_LEN sizeof (descr_mosaic_t) -#define CastMosaicDescriptor(x) ((descr_mosaic_t *)(x)) - -typedef struct item_mosaic { -#if BYTE_ORDER == BIG_ENDIAN - u_char logical_cell_id :6; - u_char :7; - u_char logical_cell_presentation_info :3; -#else - u_char :2; - u_char logical_cell_id :6; - u_char logical_cell_presentation_info :3; /*0=undefined, 1=video, 2=still picture, 3=graphical text, 4--7=reserved*/ - u_char :5; -#endif - u_char elementary_cell_field_length /*:8*/; - u_char data[]; /* struct item_mosaic_cell; struct item_mosaic_end */ -} item_mosaic_t; -typedef struct item_mosaic_end { - u_char cell_linkage_info /*:8*/; /*0=undefined, 1=bouquet, 2=service, 3=other mosaic, 4=event, 5--255=reserved*/ - u_char data[]; /* union item_cell_linkage */ -} item_mosaic_end_t; - -typedef struct item_mosaic_cell { -#if BYTE_ORDER == BIG_ENDIAN - u_char :2; - u_char elementary_cell_id :6; -#else - u_char elementary_cell_id :6; - u_char :2; -#endif -} item_mosaic_cell_t; -typedef union item_mosaic_cell_linkage { - struct item_mosaic_cell_bouquet { - u_char bouquet_id_hi /*:8*/; - u_char bouquet_id_lo /*:8*/; - } bouquet; - struct item_mosaic_cell_service { - u_char original_network_id_hi /*:8*/; - u_char original_network_id_lo /*:8*/; - u_char transport_stream_id_hi /*:8*/; - u_char transport_stream_id_lo /*:8*/; - u_char service_id_hi /*:8*/; - u_char service_id_lo /*:8*/; - } service; - struct item_mosaic_cell_other { - u_char original_network_id_hi /*:8*/; - u_char original_network_id_lo /*:8*/; - u_char transport_stream_id_hi /*:8*/; - u_char transport_stream_id_lo /*:8*/; - u_char service_id_hi /*:8*/; - u_char service_id_lo /*:8*/; - } other; - struct item_mosaic_cell_event { - u_char original_network_id_hi /*:8*/; - u_char original_network_id_lo /*:8*/; - u_char transport_stream_id_hi /*:8*/; - u_char transport_stream_id_lo /*:8*/; - u_char service_id_hi /*:8*/; - u_char service_id_lo /*:8*/; - u_char event_id_hi /*:8*/; - u_char event_id_lo /*:8*/; - } event; -} item_mosaic_cell_linkage_t; - -/* 0x52 stream_identifier_descriptor */ -typedef struct descr_stream_identifier { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; - u_char component_tag /*:8*/; -} descr_stream_identifier_t; -#define DESCR_STREAM_IDENTIFIER_LEN sizeof (descr_stream_identifier_t) -#define CastStreamIdentifierDescriptor(x) ((descr_stream_identifier_t *)(x)) - -/* 0x53 ca_identifier_descriptor */ -typedef struct descr_ca_identifier { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; -} descr_ca_identifier_t; -#define DESCR_CA_IDENTIFIER_LEN sizeof (descr_ca_identifier_t) -#define CastCaIdentifierDescriptor(x) ((descr_ca_identifier_t *)(x)) - -/* 0x54 content_descriptor */ -typedef struct descr_content { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; - u_char data[]; /* struct nibble_content */ -} descr_content_t; -#define DESCR_CONTENT_LEN sizeof (descr_content_t) -#define CastContentDescriptor(x) ((descr_content_t *)(x)) - -typedef struct nibble_content { -#if BYTE_ORDER == BIG_ENDIAN - u_char content_nibble_level_1 :4; - u_char content_nibble_level_2 :4; - u_char user_nibble_1 :4; - u_char user_nibble_2 :4; -#else - u_char user_nibble_2 :4; - u_char user_nibble_1 :4; - u_char content_nibble_level_2 :4; - u_char content_nibble_level_1 :4; -#endif -} nibble_content_t; -#define NIBBLE_CONTENT_LEN sizeof (nibble_content_t) -#define CastContentNibble(x) ((nibble_content_t *)(x)) - -/* 0x55 parental_rating_descriptor */ -typedef struct descr_parental_rating { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; - u_char data[]; /* struct parental_rating_item */ -} descr_parental_rating_t; -#define DESCR_PARENTAL_RATING_LEN sizeof (descr_parental_rating_t) -#define CastParentalRatingDescriptor(x) ((descr_parental_rating_t *)(x)) - -typedef struct parental_rating_item { - u_char lang_code1 /*:8*/; - u_char lang_code2 /*:8*/; - u_char lang_code3 /*:8*/; - u_char rating /*:8*/; -} parental_rating_item_t; -#define PARENTAL_RATING_ITEM_LEN sizeof (parental_rating_item_t) -#define CastParentalRatingItem(x) ((parental_rating_item_t *)(x)) - -/* 0x56 teletext_descriptor */ -typedef struct descr_teletext { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; - u_char data[]; /* struct item_teletext */ -} descr_teletext_t; -#define DESCR_TELETEXT_LEN sizeof (descr_teletext_t) -#define CastTeletextDescriptor(x) ((descr_teletext_t *)(x)) - -typedef struct item_teletext { - u_char lang_code1 /*:8*/; - u_char lang_code2 /*:8*/; - u_char lang_code3 /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char type :5; - u_char magazine_number :3; -#else - u_char magazine_number :3; - u_char type :5; -#endif - u_char page_number /*:8*/; -} item_teletext_t; -#define ITEM_TELETEXT_LEN sizeof (item_teletext_t) -#define CastTeletextItem(x) ((item_teletext_t *)(x)) - -/* 0x57 telephone_descriptor */ -typedef struct descr_telephone { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char :2; - u_char foreign_availability :1; - u_char connection_type :5; -#else - u_char connection_type :5; - u_char foreign_availability :1; - u_char :2; -#endif -#if BYTE_ORDER == BIG_ENDIAN - u_char :1; - u_char country_prefix_length :2; - u_char international_area_code_length :3; - u_char operator_code_length :2; -#else - u_char operator_code_length :2; - u_char international_area_code_length :3; - u_char country_prefix_length :2; - u_char :1; -#endif -#if BYTE_ORDER == BIG_ENDIAN - u_char :1; - u_char national_area_code_length :3; - u_char core_number_length :4; -#else - u_char core_number_length :4; - u_char national_area_code_length :3; - u_char :1; -#endif - u_char data[]; /* coutry area operator national core */ -} descr_telephone_t; -#define DESCR_TELEPHONE_LEN sizeof (descr_telephone_t) -#define CastTelephoneDescriptor(x) ((descr_telephone_t *)(x)) - -/* 0x58 local_time_offset_descriptor */ -typedef struct descr_local_time_offset { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; -} descr_local_time_offset_t; -#define DESCR_LOCAL_TIME_OFFSET_LEN sizeof (descr_local_time_offset_t) -#define CastLocalTimeOffsetDescriptor(x) ((descr_local_time_offset_t *)(x)) - -typedef struct local_time_offset_entry { - u_char country_code1 /*:8*/; - u_char country_code2 /*:8*/; - u_char country_code3 /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char country_region_id :6; - u_char :1; - u_char local_time_offset_polarity :1; -#else - u_char local_time_offset_polarity :1; - u_char :1; - u_char country_region_id :6; -#endif - u_char local_time_offset_h /*:8*/; - u_char local_time_offset_m /*:8*/; - u_char time_of_change_mjd_hi /*:8*/; - u_char time_of_change_mjd_lo /*:8*/; - u_char time_of_change_time_h /*:8*/; - u_char time_of_change_time_m /*:8*/; - u_char time_of_change_time_s /*:8*/; - u_char next_time_offset_h /*:8*/; - u_char next_time_offset_m /*:8*/; -} local_time_offset_entry_t ; -#define LOCAL_TIME_OFFSET_ENTRY_LEN sizeof (local_time_offset_entry_t) -#define CastLocalTimeOffsetEntry(x) ((local_time_offset_entry_t *)(x)) - -/* 0x59 subtitling_descriptor */ -typedef struct descr_subtitling { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; - u_char data[]; /* item_subtitling */ -} descr_subtitling_t; -#define DESCR_SUBTITLING_LEN sizeof (descr_subtitling_t) -#define CastSubtitlingDescriptor(x) ((descr_subtitling_t *)(x)) - -typedef struct item_subtitling { - u_char lang_code1 /*:8*/; - u_char lang_code2 /*:8*/; - u_char lang_code3 /*:8*/; - u_char subtitling_type /*:8*/; - u_char composition_page_id_hi /*:8*/; - u_char composition_page_id_lo /*:8*/; - u_char ancillary_page_id_hi /*:8*/; - u_char ancillary_page_id_lo /*:8*/; -} item_subtitling_t; -#define ITEM_SUBTITLING_LEN sizeof (item_subtitling_t) -#define CastSubtitlingItem(x) ((item_subtitling_t *)(x)) - -/* 0x5A terrestrial_delivery_system_descriptor */ -typedef struct descr_terrestrial_delivery { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; - u_char frequency1 /*:8*/; - u_char frequency2 /*:8*/; - u_char frequency3 /*:8*/; - u_char frequency4 /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char bandwidth :3; - u_char :5; -#else - u_char :5; - u_char bandwidth :3; -#endif -#if BYTE_ORDER == BIG_ENDIAN - u_char constellation :2; - u_char hierarchy :3; - u_char code_rate_HP :3; -#else - u_char code_rate_HP :3; - u_char hierarchy :3; - u_char constellation :2; -#endif -#if BYTE_ORDER == BIG_ENDIAN - u_char code_rate_LP :3; - u_char guard_interval :2; - u_char transmission_mode :2; - u_char other_frequency_flag :1; -#else - u_char other_frequency_flag :1; - u_char transmission_mode :2; - u_char guard_interval :2; - u_char code_rate_LP :3; -#endif - u_char reserver2 /*:8*/; - u_char reserver3 /*:8*/; - u_char reserver4 /*:8*/; - u_char reserver5 /*:8*/; -} descr_terrestrial_delivery_system_t; -#define DESCR_TERRESTRIAL_DELIVERY_SYSTEM_LEN sizeof (descr_terrestrial_delivery_system_t) -#define CastTerrestrialDeliverySystemDescriptor(x) ((descr_terrestrial_delivery_system_t *)(x)) -#define GetTerrestrialDeliverySystemFrequency(x) HILO4(((descr_terrestrial_delivert_system_t *)x)->frequency) - -/* 0x5B multilingual_network_name_descriptor */ -typedef struct descr_multilingual_network_name { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; - u_char data[]; /* struct item_multilingual_network_name */ -} descr_multilingual_network_name_t; -#define DESCR_MULTILINGUAL_NETWORK_NAME_LEN sizeof (descr_multilingual_network_name_t) -#define CastMultilingualNetworkNameDescriptor(x) ((descr_multilingual_network_name_t *)(x)) - -typedef struct item_multilingual_network_name { - u_char lang_code1 /*:8*/; - u_char lang_code2 /*:8*/; - u_char lang_code3 /*:8*/; - u_char network_name_length /*:8*/; - u_char network_name[]; -} item_multilingual_network_name_t; - -/* 0x5C multilingual_bouquet_name_descriptor */ -typedef struct descr_multilingual_bouquet_name { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; - u_char names[]; /* struct item_multilingual_bouquet_name */ -} descr_multilingual_bouquet_name_t; -#define DESCR_MULTILINGUAL_BOUQUET_NAME_LEN sizeof (descr_multilingual_bouquet_name_t) -#define CastMultilingualBouquetNameDescriptor(x) ((descr_multilingual_bouquet_name_t *)(x)) - -typedef struct item_multilingual_bouquet_name { - u_char lang_code1 /*:8*/; - u_char lang_code2 /*:8*/; - u_char lang_code3 /*:8*/; - u_char bouquet_name_length /*:8*/; - u_char bouquet_name[]; -} item_multilingual_bouquet_name_t; - -/* 0x5D multilingual_service_name_descriptor */ -typedef struct descr_multilingual_service_name { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; - u_char data[]; /* struct multilingual_service_name_item */ -} descr_multilingual_service_name_t; -#define DESCR_MULTILINGUAL_SERVICE_NAME_LEN sizeof (descr_multilingual_service_name_t) -#define CastMultilingualServiceNameDescriptor(x) ((descr_multilingual_service_name_t *)(x)) - -typedef struct multilingual_service_name_item { - u_char lang_code1 /*:8*/; - u_char lang_code2 /*:8*/; - u_char lang_code3 /*:8*/; - u_char data[]; /* struct multilingual_service_name_item_(mid|end) */ -} multilingual_service_name_item_t; -typedef struct multilingual_service_name_item_mid { - u_char service_provider_name_length /*:8*/; - u_char service_provider_name[]; -} multilingual_service_name_item_mid_t; -typedef struct multilingual_service_name_item_end { - u_char service_name_length /*:8*/; - u_char service_name[]; -} multilingual_service_name_item_end_t; - -/* 0x5E multilingual_component_descriptor */ -typedef struct descr_multilingual_component { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; - u_char component_tag /*:8*/; - u_char data[]; /* struct multilingual_component_item */ -} descr_multilingual_component_t; -#define DESCR_MULTILINGUAL_COMPONENT_LEN sizeof (descr_multilingual_component_t) -#define CastMultilingualComponentDescriptor(x) ((descr_multilingual_component_t *)(x)) - -typedef struct item_multilingual_component { - u_char lang_code1 /*:8*/; - u_char lang_code2 /*:8*/; - u_char lang_code3 /*:8*/; - u_char text_description_length /*:8*/; - u_char text_description[]; -} item_multilingual_component_t; - -/* 0x5F private_data_specifier_descriptor */ -typedef struct descr_private_data_specifier { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; - u_char private_data_specifier1 /*:8*/; - u_char private_data_specifier2 /*:8*/; - u_char private_data_specifier3 /*:8*/; - u_char private_data_specifier4 /*:8*/; -} descr_private_data_specifier_t; -#define DESCR_PRIVATE_DATA_SPECIFIER_LEN sizeof (descr_private_data_specifier_t) -#define CastPrivateDataSpecifierDescriptor(x) ((descr_private_data_specifier_t *)(x)) -#define GetPrivateDataSpecifier(x) HILO4(((descr_private_data_specifier_t *)x)->private_data_specifier) - -/* 0x60 service_move_descriptor */ -typedef struct descr_service_move { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; - u_char new_original_network_id_hi /*:8*/; - u_char new_original_network_id_lo /*:8*/; - u_char new_transport_stream_id_hi /*:8*/; - u_char new_transport_stream_id_lo /*:8*/; - u_char new_service_id_hi /*:8*/; - u_char new_service_id_lo /*:8*/; -} descr_service_move_t; -#define DESCR_SERVICE_MOVE_LEN sizeof (descr_service_move_t) -#define CastServiceMoveDescriptor(x) ((descr_service_move_t *)(x)) - -/* 0x61 short_smoothing_buffer_descriptor */ -typedef struct descr_short_smoothing_buffer { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char sb_size :2; - u_char sb_leak_rate :6; -#else - u_char sb_leak_rate :6; - u_char sb_size :2; -#endif - u_char data[]; -} descr_short_smoothing_buffer_t; -#define DESCR_SHORT_SMOOTHING_BUFFER_LEN sizeof (descr_short_smoothing_buffer_t) -#define CastShortSmoothingBufferDescriptor(x) ((descr_short_smoothing_buffer_t *)(x)) - -/* 0x62 frequency_list_descriptor */ -typedef struct descr_frequency_list { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char :6; - u_char coding_type :2; /* 00=not def 01=satelite 10=cable 11=terrestrial */ -#else - u_char coding_type :2; /* 00=not def 01=satelite 10=cable 11=terrestrial */ - u_char :6; -#endif - u_char centre_frequency1 /*:8*/; - u_char centre_frequency2 /*:8*/; - u_char centre_frequency3 /*:8*/; - u_char centre_frequency4 /*:8*/; -} descr_frequency_list_t; -#define DESCR_FREQUENCY_LIST_LEN sizeof (descr_frequency_list_t) -#define CastFrequencyListDescriptor(x) ((descr_frequency_list_t *)(x)) - -/* 0x63 partial_transport_stream_descriptor */ -typedef struct descr_partial_transport_stream { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char :2; - u_char peak_rate1 :6; -#else - u_char peak_rate1 :6; - u_char :2; -#endif - u_char peak_rate2 /*:8*/; - u_char peak_rate3 /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char :2; - u_char minimum_overall_smoothing_rate1 :6; -#else - u_char minimum_overall_smoothing_rate1 :6; - u_char :2; -#endif - u_char minimum_overall_smoothing_rate2 /*:8*/; - u_char minimum_overall_smoothing_rate3 /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char :2; - u_char maximum_overall_smoothing_rate1 :6; -#else - u_char maximum_overall_smoothing_rate1 :6; - u_char :2; -#endif - u_char maximum_overall_smoothing_rate2 /*:8*/; -} descr_partial_transport_stream_t; -#define DESCR_PARTIAL_TRANSPORT_STREAM_LEN sizeof (descr_partial_transport_stream_t) -#define CastPartialDescriptor(x) ((descr_partial_transport_stream_t *)(x)) -#define GetPartialTransportStreamCentreFrequency(x) HILO4(((descr_partial_transport_stream_t *)x)->centre_frequency) -#define GetPTSPeakRate(x) HILO2(((descr_partial_transport_stream *)x)->peak_rate) -#define GetPTSMinOSRate(x) HILO3(((descr_partial_transport_stream *)x)->minimum_overall_smoothing_rate) -#define GetPTSMaxOSRate(x) HILO2(((descr_partial_transport_stream *)x)->minimum_overall_smoothing_rate) - -/* 0x64 data_broadcast_descriptor */ -typedef struct descr_data_broadcast { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; - u_char data_broadcast_id_hi /*:8*/; - u_char data_broadcast_id_lo /*:8*/; - u_char component_tag /*:8*/; - u_char selector_length /*:8*/; - u_char data[]; /* char[]; struct descr_data_broadcast_end */ -} descr_data_broadcast_t; -typedef struct descr_data_broadcast_end { - u_char lang_code1 /*:8*/; - u_char lang_code2 /*:8*/; - u_char lang_code3 /*:8*/; - u_char text_length /*:8*/; - u_char text[]; -} descr_data_broadcast_end_t; - -/* 0x65 ca_system_descriptor */ -typedef struct descr_ca_system { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; - u_char data[]; /* struct item_ca_system */ -} descr_ca_system_t; -#define DESCR_CA_SYSTEM_LEN sizeof (descr_ca_system_t) -#define CastCaSystemDescriptor(x) ((descr_ca_system_t *)(x)) - -typedef struct item_ca_system { - u_char CA_system_id_hi /*:8*/; - u_char CA_system_id_lo /*:8*/; -} item_ca_system_t; - -/* 0x66 data_broadcast_id_descriptor */ -typedef struct descr_data_broadcast_id { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; - u_char data_broadcast_id_hi /*:8*/; - u_char data_broadcast_id_lo /*:8*/; -} descr_data_broadcast_id_t; -#define DESCR_DATA_BROADCAST_ID_LEN sizeof (descr_data_broadcast_id_t) -#define CastDataBroadcastIdDescriptor(x) ((descr_data_broadcast_id_t *)(x)) - -/* 0x67 transport_stream_descriptor */ -typedef struct descr_transport_stream { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; - /* TBD */ -} descr_transport_stream_t; -#define DESCR_TRANSPORT_STREAM_LEN sizeof (descr_transport_stream_t) -#define CastTransportStreamDescriptor(x) ((descr_transport_stream_t *)(x)) - -/* 0x68 dsng_descriptor */ -typedef struct descr_dsng { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; - /* TBD */ -} descr_dsng_t; -#define DESCR_DSNG_LEN sizeof (descr_dsng_t) -#define CastDsngDescriptor(x) ((descr_dsng_t *)(x)) - -/* 0x69 programme_identificaion_label_descriptor */ -typedef struct descr_pdc { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char :4; - u_char day :5; - u_char month :4; - u_char hour :5; - u_char minute :6; -#else - u_char minute :6; - u_char hour :5; - u_char month :4; - u_char day :5; - u_char :4; -#endif -} descr_pdc_t; -#define DESCR_PDC_LEN sizeof (descr_pdc_t) -#define CastPdcDescriptor(x) ((descr_pdc_t *)(x)) - -/* 0x6A ac3_descriptor */ -typedef struct descr_ac3 { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; -#if BYTE_ORDER == BIG_ENDIAN - u_char ac3_type_flag :1; - u_char bsid_flag :1; - u_char mainid_flag :1; - u_char asvc_flag :1; - u_char :4; -#else - u_char :4; - u_char asvc_flag :1; - u_char mainid_flag :1; - u_char bsid_flag :1; - u_char ac3_type_flag :1; -#endif - u_char ac3_type /*:8*/; - u_char bsid /*:8*/; - u_char mainid /*:8*/; - u_char asvc /*:8*/; -} descr_ac3_t; -#define DESCR_AC3_LEN sizeof (descr_ac3_t) -#define CastAc3Descriptor(x) ((descr_ac3_t *)(x)) - -/* 0x6B ancillary_data_descriptor */ -typedef struct descr_ancillary_data { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; - u_char ancillary_data_identifier /*:8*/; -} descr_ancillary_data_t; -#define DESCR_ANCILLARY_DATA_LEN sizeof (descr_ancillary_data_t) -#define CastAncillaryDataDescriptor(x) ((descr_ancillary_data_t *)(x)) - -/* 0x6C cell_list_descriptor */ -typedef struct descr_cell_list { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; - /* TBD */ -} descr_cell_list_t; -#define DESCR_CELL_LIST_LEN sizeof (descr_cell_list_t) -#define CastCellListDescriptor(x) ((descr_cell_list_t *)(x)) - -/* 0x6D cell_frequency_link_descriptor */ -typedef struct descr_cell_frequency_link { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; - /* TBD */ -} descr_cell_frequency_link_t; -#define DESCR_CELL_FREQUENCY_LINK_LEN sizeof (descr_cell_frequency_link_t) -#define CastCellFrequencyLinkDescriptor(x) ((descr_cell_frequency_link_t *)(x)) - -/* 0x6E announcement_support_descriptor */ -typedef struct descr_announcement_support { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; - /* TBD */ -} descr_announcement_support_t; -#define DESCR_ANNOUNCEMENT_SUPPORT_LEN sizeof (descr_announcement_support_t) -#define CastAnnouncementSupportDescriptor(x) ((descr_announcement_support_t *)(x)) - -/* 0x76 content_identifier_descriptor */ -typedef struct descr_content_identifier { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; - u_char data[]; -} descr_content_identifier_t; - -typedef struct descr_content_identifier_crid { -#if BYTE_ORDER == BIG_ENDIAN - u_char crid_type :6; - u_char crid_location :2; -#else - u_char crid_location :2; - u_char crid_type :6; -#endif - u_char crid_ref_data[]; -} descr_content_identifier_crid_t; - -typedef struct descr_content_identifier_crid_local { - u_char crid_length /*:8*/; - u_char crid_byte[]; -} descr_content_identifier_crid_local_t; - - -/* 0x80 custom_category_descriptor TODO */ -typedef struct descr_custom_category { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; - u_char dummy /*:8*/; // 7F - u_char data_length /*:8*/; - u_char data[]; /* struct custom_category_item(1|2) */ -} descr_custom_category_t; -struct custom_category_item1 { - u_char dummy /*:8*/; // 10 40 -}; -struct custom_category_item2 { - u_char length /*:8*/; - u_char data[]; /* struct custom_category_item3 */ -}; -struct custom_category_item3 { - u_char dummy0 /*:8*/; // FF -#if BYTE_ORDER == BIG_ENDIAN - u_char content_nibble_level_1 :4; - u_char content_nibble_level_2 :4; - u_char user_nibble_1 :4; - u_char user_nibble_2 :4; -#else - u_char user_nibble_2 :4; - u_char user_nibble_1 :4; - u_char content_nibble_level_2 :4; - u_char content_nibble_level_1 :4; -#endif - u_char dummy1[2] /*:8*/; // CF E2 | D4 48 - u_char text_length /*:8*/; - u_char text[]; -}; - -/* 0x81 xxx_descriptor TODO */ -typedef struct descr_xxx { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; - u_char dummy /*:8 FF*/; -} descr_xxx_t; -#define DESCR_XXX_LEN sizeof (descr_xxx_t) -#define CastXxxDescriptor(x) ((descr_xxx_t *)(x)) - -/* 0x82 vps_descriptor TODO */ -typedef struct descr_vps { - u_char descriptor_tag /*:8*/; - u_char descriptor_length /*:8*/; - u_char hour[2] /*:8*/; - u_char delimiter_time /*:8 ':'*/; - u_char minute[2] /*:8*/; - u_char day[2] /*:8*/; - u_char delimiter_date /*:8 '.'*/; - u_char month[2] /*:8*/; - u_char delimiter /*:8 '#'*/; - u_char number[2] /*:8*/; -} descr_vps_t; -#define DESCR_VPS_LEN sizeof (descr_vps_t) -#define CastVpsDescriptor(x) ((descr_vps_t *)(x)) -- cgit v1.2.3