From 0da688aba6cca5cc5b0b4406e384fa9e3345859c Mon Sep 17 00:00:00 2001 From: kos Date: Fri, 24 Aug 2012 10:57:20 +0900 Subject: [PATCH] - add opera browser.(with hbbtv) - handle error for forbidden stream. - activate red button.(ready for ait..) --- configure.ac | 2 + data/keymap.xml | 6 + lib/dvb/esection.h | 5 + lib/dvb/pmt.cpp | 214 ++++ lib/dvb/pmt.h | 58 ++ lib/dvb/specs.h | 40 + .../Components/Converter/HbbtvApplicationInfo.py | 20 + lib/python/Components/Converter/Makefile.am | 2 +- lib/python/Components/Converter/ServiceInfo.py | 6 +- lib/python/Components/Sources/HbbtvApplication.py | 25 + lib/python/Components/Sources/Makefile.am | 2 +- lib/python/Plugins/Extensions/HbbTV/Makefile.am | 9 + lib/python/Plugins/Extensions/HbbTV/__init__.py | 1 + .../Plugins/Extensions/HbbTV/meta/Makefile.am | 3 + .../Plugins/Extensions/HbbTV/meta/plugin_hbbtv.xml | 21 + lib/python/Plugins/Extensions/HbbTV/plugin.py | 1082 ++++++++++++++++++++ lib/python/Plugins/Extensions/Makefile.am | 2 +- lib/python/Screens/InfoBar.py | 6 +- lib/python/Screens/InfoBarGenerics.py | 57 ++ lib/service/iservice.h | 3 + lib/service/servicedvb.cpp | 15 + lib/service/servicemp3.cpp | 7 +- 22 files changed, 1578 insertions(+), 8 deletions(-) create mode 100644 lib/python/Components/Converter/HbbtvApplicationInfo.py create mode 100644 lib/python/Components/Sources/HbbtvApplication.py create mode 100644 lib/python/Plugins/Extensions/HbbTV/Makefile.am create mode 100644 lib/python/Plugins/Extensions/HbbTV/__init__.py create mode 100644 lib/python/Plugins/Extensions/HbbTV/meta/Makefile.am create mode 100644 lib/python/Plugins/Extensions/HbbTV/meta/plugin_hbbtv.xml create mode 100644 lib/python/Plugins/Extensions/HbbTV/plugin.py diff --git a/configure.ac b/configure.ac index ab3b786..aac8f11 100644 --- a/configure.ac +++ b/configure.ac @@ -193,6 +193,8 @@ lib/python/Plugins/Extensions/DLNAServer/Makefile lib/python/Plugins/Extensions/DLNAServer/meta/Makefile lib/python/Plugins/Extensions/DLNABrowser/Makefile lib/python/Plugins/Extensions/DLNABrowser/meta/Makefile +lib/python/Plugins/Extensions/HbbTV/Makefile +lib/python/Plugins/Extensions/HbbTV/meta/Makefile lib/python/Plugins/SystemPlugins/CleanupWizard/Makefile lib/python/Plugins/SystemPlugins/CleanupWizard/meta/Makefile lib/python/Plugins/SystemPlugins/CommonInterfaceAssignment/Makefile diff --git a/data/keymap.xml b/data/keymap.xml index 10a36de..cdc583e 100755 --- a/data/keymap.xml +++ b/data/keymap.xml @@ -225,6 +225,12 @@ + + + + + + diff --git a/lib/dvb/esection.h b/lib/dvb/esection.h index 3e097cc..6efe8c3 100644 --- a/lib/dvb/esection.h +++ b/lib/dvb/esection.h @@ -40,6 +40,7 @@ class eTable: public eGTable private: std::vector sections; std::set avail; + unsigned char m_section_data[4096]; protected: int createTable(unsigned int nr, const __u8 *data, unsigned int max) { @@ -53,6 +54,9 @@ protected: if (avail.find(nr) != avail.end()) delete sections[nr]; + memset(m_section_data, 0, 4096); + memcpy(m_section_data, data, 4096); + sections.resize(max); sections[nr] = new Section(data); avail.insert(nr); @@ -74,6 +78,7 @@ protected: } public: std::vector &getSections() { return sections; } + unsigned char* getBufferData() { return m_section_data; } eTable(bool debug=true): eGTable(debug) { } diff --git a/lib/dvb/pmt.cpp b/lib/dvb/pmt.cpp index 25577f0..5b19ebd 100644 --- a/lib/dvb/pmt.cpp +++ b/lib/dvb/pmt.cpp @@ -20,16 +20,26 @@ #include #include +#include +#include +#include +#include + + eDVBServicePMTHandler::eDVBServicePMTHandler() :m_ca_servicePtr(0), m_dvb_scan(0), m_decode_demux_num(0xFF), m_no_pat_entry_delay(eTimer::create()) { m_use_decode_demux = 0; m_pmt_pid = -1; + m_dsmcc_pid = -1; m_isstreamclient = false; eDVBResourceManager::getInstance(m_resourceManager); CONNECT(m_PMT.tableReady, eDVBServicePMTHandler::PMTready); CONNECT(m_PAT.tableReady, eDVBServicePMTHandler::PATready); CONNECT(m_no_pat_entry_delay->timeout, eDVBServicePMTHandler::sendEventNoPatEntry); + + CONNECT(m_AIT.tableReady, eDVBServicePMTHandler::AITready); + CONNECT(m_OC.tableReady, eDVBServicePMTHandler::OCready); } eDVBServicePMTHandler::~eDVBServicePMTHandler() @@ -189,6 +199,154 @@ void eDVBServicePMTHandler::PATready(int) serviceEvent(eventNoPAT); } +static void eraseHbbTVApplications(HbbTVApplicationInfoList *applications) +{ + if(applications->size() == 0) + return; + for(HbbTVApplicationInfoListConstIterator info = applications->begin() ; info != applications->end() ; ++info) + delete(*info); + applications->clear(); +} + +void saveData(int orgid, unsigned char* data, int sectionLength) +{ + int fd = 0, rc = 0; + char fileName[255] = {0}; + sprintf(fileName, "/tmp/ait.%d", orgid); + if((fd = open(fileName, O_RDWR|O_CREAT|O_TRUNC)) < 0) + { + eDebug("Fail to save a AIT Data."); + return; + } + rc = write(fd, data, sectionLength); + eDebug("Save Data Len : [%d]", rc); + close(fd); +} + +void eDVBServicePMTHandler::AITready(int error) +{ + eDebug("AITready"); + ePtr > ptr; + if (!m_AIT.getCurrent(ptr)) + { + int orgid = 0, appid = 0; + m_ApplicationName = m_HBBTVUrl = ""; + + eraseHbbTVApplications(&m_HbbTVApplications); + + memcpy(m_AITData, ptr->getBufferData(), 4096); + + int sectionLength = 0; + for (std::vector::const_iterator it = ptr->getSections().begin(); it != ptr->getSections().end(); ++it) + { + std::list::const_iterator i = (*it)->getApplicationInformation()->begin(); + sectionLength += (*it)->getSectionLength(); + eDebug("Section Length : %d, Total Section Length : %d", (*it)->getSectionLength(), sectionLength); + for (; i != (*it)->getApplicationInformation()->end(); ++i) + { + std::string hbbtvUrl = "", applicaionName = ""; + + int controlCode = (*i)->getApplicationControlCode(); + ApplicationIdentifier * applicationIdentifier = (*i)->getApplicationIdentifier(); + orgid = applicationIdentifier->getOrganisationId(); + appid = applicationIdentifier->getApplicationId(); + eDebug("found applicaions ids >> pid : %x, orgid : %d, appid : %d", m_ait_pid, orgid, appid); + if (controlCode == 1 || controlCode == 2) /* 1:AUTOSTART, 2:ETC */ + { + for (DescriptorConstIterator desc = (*i)->getDescriptors()->begin(); + desc != (*i)->getDescriptors()->end(); ++desc) + { + switch ((*desc)->getTag()) + { + case APPLICATION_DESCRIPTOR: + break; + case APPLICATION_NAME_DESCRIPTOR: + { + ApplicationNameDescriptor *nameDescriptor = (ApplicationNameDescriptor*)(*desc); + ApplicationNameConstIterator interactionit = nameDescriptor->getApplicationNames()->begin(); + for(; interactionit != nameDescriptor->getApplicationNames()->end(); ++interactionit) + { + applicaionName = (*interactionit)->getApplicationName(); + if(controlCode == 1) m_ApplicationName = applicaionName; + break; + } + break; + } + case TRANSPORT_PROTOCOL_DESCRIPTOR: + { + TransportProtocolDescriptor *transport = (TransportProtocolDescriptor*)(*desc); + switch (transport->getProtocolId()) + { + case 1: /* object carousel */ + if (m_dsmcc_pid >= 0) + { + m_OC.begin(eApp, eDVBDSMCCDLDataSpec(m_dsmcc_pid), m_demux); + } + break; + case 2: /* ip */ + break; + case 3: /* interaction */ + { + InterActionTransportConstIterator interactionit = transport->getInteractionTransports()->begin(); + for(; interactionit != transport->getInteractionTransports()->end(); ++interactionit) + { + hbbtvUrl = (*interactionit)->getUrlBase()->getUrl(); + if(controlCode == 1) m_HBBTVUrl = hbbtvUrl; + break; + } + break; + } + } + break; + } + case GRAPHICS_CONSTRAINTS_DESCRIPTOR: + break; + case SIMPLE_APPLICATION_LOCATION_DESCRIPTOR: + { + SimpleApplicationLocationDescriptor *applicationlocation = (SimpleApplicationLocationDescriptor*)(*desc); + hbbtvUrl += applicationlocation->getInitialPath(); + break; + } + case APPLICATION_USAGE_DESCRIPTOR: + break; + case SIMPLE_APPLICATION_BOUNDARY_DESCRIPTOR: + break; + } + } + } + m_HbbTVApplications.push_back(new HbbTVApplicationInfo(controlCode, orgid, appid, hbbtvUrl, applicaionName)); + } + } + + if (m_HbbTVApplications.size()) + { + saveData(orgid, m_AITData, sectionLength);//4096); + for(HbbTVApplicationInfoListConstIterator infoiter = m_HbbTVApplications.begin() ; infoiter != m_HbbTVApplications.end() ; ++infoiter) + eDebug("Found : control[%d], name[%s], url[%s]", + (*infoiter)->m_ControlCode, (*infoiter)->m_ApplicationName.c_str(), (*infoiter)->m_HbbTVUrl.c_str()); + serviceEvent(eventHBBTVInfo); + } + else eDebug("No found anything."); + } + /* for now, do not keep listening for table updates */ + m_AIT.stop(); +} + +void eDVBServicePMTHandler::OCready(int error) +{ + eDebug("OCready"); + ePtr > ptr; + if (!m_OC.getCurrent(ptr)) + { + for (std::vector::const_iterator it = ptr->getSections().begin(); it != ptr->getSections().end(); ++it) + { + unsigned char* sectionData = (*it)->getData(); + } + } + /* for now, do not keep listening for table updates */ + m_OC.stop(); +} + PyObject *eDVBServicePMTHandler::getCaIds(bool pair) { ePyObject ret; @@ -227,6 +385,26 @@ PyObject *eDVBServicePMTHandler::getCaIds(bool pair) return ret ? (PyObject*)ret : (PyObject*)PyList_New(0); } +PyObject *eDVBServicePMTHandler::getHbbTVApplications(void) +{ + ePyObject ret= PyList_New(0);; + if(m_HbbTVApplications.size()) + { + for(HbbTVApplicationInfoListConstIterator infoiter = m_HbbTVApplications.begin() ; infoiter != m_HbbTVApplications.end() ; ++infoiter) + { + ePyObject tuple = PyTuple_New(5); + PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong((*infoiter)->m_ControlCode)); + PyTuple_SET_ITEM(tuple, 1, PyString_FromString((*infoiter)->m_ApplicationName.c_str())); + PyTuple_SET_ITEM(tuple, 2, PyString_FromString((*infoiter)->m_HbbTVUrl.c_str())); + PyTuple_SET_ITEM(tuple, 3, PyInt_FromLong((*infoiter)->m_OrgId)); + PyTuple_SET_ITEM(tuple, 4, PyInt_FromLong((*infoiter)->m_AppId)); + PyList_Append(ret, tuple); + Py_DECREF(tuple); + } + } + return (PyObject*)ret; +} + int eDVBServicePMTHandler::getProgramInfo(program &program) { ePtr > ptr; @@ -241,6 +419,7 @@ int eDVBServicePMTHandler::getProgramInfo(program &program) program.pcrPid = -1; program.pmtPid = -1; program.textPid = -1; + program.aitPid = -1; int first_ac3 = -1; program.defaultAudioStream = 0; @@ -583,6 +762,38 @@ int eDVBServicePMTHandler::getProgramInfo(program &program) } prev_audio = 0; } + case 0x05: /* ITU-T Rec. H.222.0 | ISO/IEC 13818-1 private sections */ + { + for (DescriptorConstIterator desc = (*es)->getDescriptors()->begin(); + desc != (*es)->getDescriptors()->end(); ++desc) + { + m_ait_pid = -1; + switch ((*desc)->getTag()) + { + case APPLICATION_SIGNALLING_DESCRIPTOR: + m_ait_pid = program.aitPid = (*es)->getPid(); + m_AIT.begin(eApp, eDVBAITSpec(program.aitPid), m_demux); + break; + } + } + break; + } + case 0x0b: /* ISO/IEC 13818-6 DSM-CC U-N Messages */ + { + for (DescriptorConstIterator desc = (*es)->getDescriptors()->begin(); + desc != (*es)->getDescriptors()->end(); ++desc) + { + switch ((*desc)->getTag()) + { + case CAROUSEL_IDENTIFIER_DESCRIPTOR: + m_dsmcc_pid = (*es)->getPid(); + break; + case STREAM_IDENTIFIER_DESCRIPTOR: + break; + } + } + break; + } default: break; } @@ -923,6 +1134,9 @@ void eDVBServicePMTHandler::free() m_pvr_channel->setCueSheet(0); } + m_OC.stop(); + m_AIT.stop(); + m_PMT.stop(); m_PAT.stop(); m_service = 0; diff --git a/lib/dvb/pmt.h b/lib/dvb/pmt.h index 363176d..9216adc 100644 --- a/lib/dvb/pmt.h +++ b/lib/dvb/pmt.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -21,6 +22,26 @@ class eDVBCAService; class eDVBScan; +#include +class OCSection : public LongCrcSection +{ +protected: + void *data; + +public: + OCSection(const uint8_t * const buffer) + : LongCrcSection(buffer) + { + data = malloc(getSectionLength()); + memcpy(data, buffer, getSectionLength()); + } + ~OCSection() + { + free(data); + } + void *getData() { return data; } +}; + struct channel_data: public Object { ePtr m_channel; @@ -69,6 +90,25 @@ public: #endif +#include +#include +class HbbTVApplicationInfo +{ +public: + int m_OrgId; + int m_AppId; + int m_ControlCode; + std::string m_HbbTVUrl; + std::string m_ApplicationName; +public: + HbbTVApplicationInfo(int controlCode, int orgid, int appid, std::string hbbtvUrl, std::string applicationName) + : m_ControlCode(controlCode), m_HbbTVUrl(hbbtvUrl), m_ApplicationName(applicationName), m_OrgId(orgid), m_AppId(appid) + {} +}; +typedef std::list HbbTVApplicationInfoList; +typedef HbbTVApplicationInfoList::iterator HbbTVApplicationInfoListIterator; +typedef HbbTVApplicationInfoList::const_iterator HbbTVApplicationInfoListConstIterator; + class eDVBServicePMTHandler: public Object { #ifndef SWIG @@ -95,11 +135,23 @@ class eDVBServicePMTHandler: public Object void SDTScanEvent(int); ePtr m_scan_event_connection; + eAUTable > m_AIT; + eAUTable > m_OC; + void PMTready(int error); void PATready(int error); int m_pmt_pid; + void AITready(int error); + void OCready(int error); + int m_dsmcc_pid; + int m_ait_pid; + HbbTVApplicationInfoList m_HbbTVApplications; + std::string m_HBBTVUrl; + std::string m_ApplicationName; + unsigned char m_AITData[4096]; + int m_use_decode_demux; uint8_t m_decode_demux_num; ePtr m_no_pat_entry_delay; @@ -128,6 +180,8 @@ public: eventSOF, // seek pre start eventEOF, // a file playback did end + eventHBBTVInfo, /* HBBTV information was detected in the AIT */ + eventMisconfiguration, // a channel was not found in any list, or no frontend was found which could provide this channel }; #ifndef SWIG @@ -196,6 +250,7 @@ public: int pcrPid; int pmtPid; int textPid; + int aitPid; bool isCrypted() { return !caids.empty(); } PyObject *createPythonObject(); }; @@ -204,6 +259,7 @@ public: int getDataDemux(ePtr &demux); int getDecodeDemux(ePtr &demux); PyObject *getCaIds(bool pair=false); // caid / ecmpid pair + PyObject *getHbbTVApplications(void); int getPVRChannel(ePtr &pvr_channel); int getServiceReference(eServiceReferenceDVB &service) { service = m_reference; return 0; } @@ -213,6 +269,8 @@ public: void resetCachedProgram() { m_have_cached_program = false; } void sendEventNoPatEntry(); + void getHBBTVUrl(std::string &ret) { ret = m_HBBTVUrl; } + /* deprecated interface */ int tune(eServiceReferenceDVB &ref, int use_decode_demux, eCueSheet *sg=0, bool simulate=false, eDVBService *service = 0); diff --git a/lib/dvb/specs.h b/lib/dvb/specs.h index 6be938c..d6ddde4 100644 --- a/lib/dvb/specs.h +++ b/lib/dvb/specs.h @@ -165,4 +165,44 @@ public: } }; +#include + +struct eDVBAITSpec +{ + eDVBTableSpec m_spec; +public: + eDVBAITSpec(int pid) + { + m_spec.pid = pid; + m_spec.tid = ApplicationInformationSection::TID; + m_spec.timeout = ApplicationInformationSection::TIMEOUT; + m_spec.flags = eDVBTableSpec::tfAnyVersion | + eDVBTableSpec::tfHaveTID | eDVBTableSpec::tfCheckCRC | + eDVBTableSpec::tfHaveTimeout; + } + operator eDVBTableSpec &() + { + return m_spec; + } +}; + +struct eDVBDSMCCDLDataSpec +{ + eDVBTableSpec m_spec; +public: + eDVBDSMCCDLDataSpec(int pid) + { + m_spec.pid = pid; + m_spec.tid = TID_DSMCC_DL_DATA; + m_spec.timeout = 20000; + m_spec.flags = eDVBTableSpec::tfAnyVersion | + eDVBTableSpec::tfHaveTID | eDVBTableSpec::tfCheckCRC | + eDVBTableSpec::tfHaveTimeout; + } + operator eDVBTableSpec &() + { + return m_spec; + } +}; + #endif diff --git a/lib/python/Components/Converter/HbbtvApplicationInfo.py b/lib/python/Components/Converter/HbbtvApplicationInfo.py new file mode 100644 index 0000000..4f20c6f --- /dev/null +++ b/lib/python/Components/Converter/HbbtvApplicationInfo.py @@ -0,0 +1,20 @@ +from Components.Converter.Converter import Converter +from Components.Element import cached + +class HbbtvApplicationInfo(Converter, object): + NAME = 0 + + def __init__(self, type): + Converter.__init__(self, type) + self.type = "" + if type == "Name": + self.type = self.NAME + + @cached + def getText(self): + if self.type == self.NAME: + return self.source.name + else: + return "" + + text = property(getText) \ No newline at end of file diff --git a/lib/python/Components/Converter/Makefile.am b/lib/python/Components/Converter/Makefile.am index b73f6d5..ee817d4 100644 --- a/lib/python/Components/Converter/Makefile.am +++ b/lib/python/Components/Converter/Makefile.am @@ -6,4 +6,4 @@ install_PYTHON = \ ConditionalShowHide.py ServicePosition.py ValueRange.py RdsInfo.py Streaming.py \ StaticMultiList.py ServiceTime.py MovieInfo.py MenuEntryCompare.py StringListSelection.py \ ValueBitTest.py TunerInfo.py ConfigEntryTest.py TemplatedMultiContent.py ProgressToText.py \ - Combine.py SensorToText.py ValueToPixmap.py + Combine.py SensorToText.py ValueToPixmap.py HbbtvApplicationInfo.py diff --git a/lib/python/Components/Converter/ServiceInfo.py b/lib/python/Components/Converter/ServiceInfo.py index e2fa12c..b39aaa1 100644 --- a/lib/python/Components/Converter/ServiceInfo.py +++ b/lib/python/Components/Converter/ServiceInfo.py @@ -20,7 +20,7 @@ class ServiceInfo(Converter, object): SID = 14 FRAMERATE = 15 TRANSFERBPS = 16 - + HAS_HBBTV = 17 def __init__(self, type): Converter.__init__(self, type) @@ -42,6 +42,7 @@ class ServiceInfo(Converter, object): "Sid": (self.SID, (iPlayableService.evUpdatedInfo,)), "Framerate": (self.FRAMERATE, (iPlayableService.evVideoSizeChanged,iPlayableService.evUpdatedInfo,)), "TransferBPS": (self.TRANSFERBPS, (iPlayableService.evUpdatedInfo,)), + "HasHBBTV": (self.HAS_HBBTV, (iPlayableService.evUpdatedInfo,iPlayableService.evHBBTVInfo,)), }[type] def getServiceInfoString(self, info, what, convert = lambda x: "%d" % x): @@ -82,6 +83,9 @@ class ServiceInfo(Converter, object): elif self.type == self.SUBSERVICES_AVAILABLE: subservices = service.subServices() return subservices and subservices.getNumberOfSubservices() > 0 + elif self.type == self.HAS_HBBTV: + return info.getInfoString(iServiceInformation.sHBBTVUrl) != "" + boolean = property(getBoolean) diff --git a/lib/python/Components/Sources/HbbtvApplication.py b/lib/python/Components/Sources/HbbtvApplication.py new file mode 100644 index 0000000..211e87f --- /dev/null +++ b/lib/python/Components/Sources/HbbtvApplication.py @@ -0,0 +1,25 @@ +from Source import Source +from Components.Element import cached + +class HbbtvApplication(Source): + def __init__(self): + Source.__init__(self) + self._available = False + self._appname = "" + + def setApplicationName(self, name): + self._appname = name + self._available = False + if name is not None and name != "": + self._available = True + self.changed((self.CHANGED_ALL,)) + + @cached + def getBoolean(self): + return self._available + boolean = property(getBoolean) + + @cached + def getName(self): + return self._appname + name = property(getName) diff --git a/lib/python/Components/Sources/Makefile.am b/lib/python/Components/Sources/Makefile.am index 436d31b..18d2ed0 100644 --- a/lib/python/Components/Sources/Makefile.am +++ b/lib/python/Components/Sources/Makefile.am @@ -4,4 +4,4 @@ install_PYTHON = \ __init__.py Clock.py EventInfo.py Source.py List.py CurrentService.py \ FrontendStatus.py Boolean.py Config.py ServiceList.py RdsDecoder.py StreamService.py \ StaticText.py CanvasSource.py ServiceEvent.py Event.py FrontendInfo.py TunerInfo.py \ - RecordState.py Progress.py Sensor.py + RecordState.py Progress.py Sensor.py HbbtvApplication.py diff --git a/lib/python/Plugins/Extensions/HbbTV/Makefile.am b/lib/python/Plugins/Extensions/HbbTV/Makefile.am new file mode 100644 index 0000000..18e39a1 --- /dev/null +++ b/lib/python/Plugins/Extensions/HbbTV/Makefile.am @@ -0,0 +1,9 @@ +installdir = $(pkglibdir)/python/Plugins/Extensions/HbbTV + +SUBDIRS = meta + +install_PYTHON = \ + __init__.py \ + plugin.py + + diff --git a/lib/python/Plugins/Extensions/HbbTV/__init__.py b/lib/python/Plugins/Extensions/HbbTV/__init__.py new file mode 100644 index 0000000..a103e92 --- /dev/null +++ b/lib/python/Plugins/Extensions/HbbTV/__init__.py @@ -0,0 +1 @@ +#dumy diff --git a/lib/python/Plugins/Extensions/HbbTV/meta/Makefile.am b/lib/python/Plugins/Extensions/HbbTV/meta/Makefile.am new file mode 100644 index 0000000..4b556b3 --- /dev/null +++ b/lib/python/Plugins/Extensions/HbbTV/meta/Makefile.am @@ -0,0 +1,3 @@ +installdir = $(datadir)/meta + +dist_install_DATA = plugin_hbbtv.xml diff --git a/lib/python/Plugins/Extensions/HbbTV/meta/plugin_hbbtv.xml b/lib/python/Plugins/Extensions/HbbTV/meta/plugin_hbbtv.xml new file mode 100644 index 0000000..830b46e --- /dev/null +++ b/lib/python/Plugins/Extensions/HbbTV/meta/plugin_hbbtv.xml @@ -0,0 +1,21 @@ + + + + + + kos + HbbTV + enigma2-plugin-extensions-hbbtv + this is hbbtv plugin + this is hbbtv plugin + + + + + + + + + + + diff --git a/lib/python/Plugins/Extensions/HbbTV/plugin.py b/lib/python/Plugins/Extensions/HbbTV/plugin.py new file mode 100644 index 0000000..9ae4d2e --- /dev/null +++ b/lib/python/Plugins/Extensions/HbbTV/plugin.py @@ -0,0 +1,1082 @@ +from Plugins.Plugin import PluginDescriptor + +from Screens.Screen import Screen +from Screens.InfoBar import InfoBar +from Screens.ChoiceBox import ChoiceBox +from Screens.MessageBox import MessageBox +from Screens.InfoBarGenerics import InfoBarNotifications +from Screens.VirtualKeyBoard import VirtualKeyBoard + +from Components.PluginComponent import plugins +from Components.Button import Button +from Components.Label import Label +from Components.Sources.StaticText import StaticText +from Components.ActionMap import NumberActionMap, ActionMap +from Components.ServiceEventTracker import ServiceEventTracker +from Components.MenuList import MenuList +from Components.Label import Label, MultiColorLabel +from Components.ConfigList import ConfigListScreen +from Components.config import config, ConfigSubsection, ConfigPosition, getConfigListEntry, ConfigBoolean, ConfigInteger, ConfigText, ConfigSelection, configfile, getCharValue + +from enigma import eTimer, eConsoleAppContainer, getDesktop, eServiceReference, iPlayableService, iServiceInformation, RT_HALIGN_LEFT, RT_HALIGN_RIGHT, RT_HALIGN_CENTER, RT_VALIGN_CENTER, getPrevAsciiCode, eRCInput, fbClass + +import os, struct, threading, stat, select, time, socket, select + +strIsEmpty = lambda x: x is None or len(x) == 0 + +HBBTVAPP_PATH = "/usr/local/hbb-browser" +COMMAND_PATH = '/tmp/.sock.hbbtv.cmd' + +class GlobalValues: + command_util = None + command_server = None + + before_service = None + + channel_info_sid = None + channel_info_onid = None + channel_info_tsid = None + channel_info_name = None + channel_info_orgid = None + + hbbtv_handelr = None + + packet_m = 0xBBADBEE + packet_h = '!IIII' + packet_hl = struct.calcsize(packet_h) +__gval__ = GlobalValues() + +def getPacketHeaders(): + global __gval__ + return (__gval__.packet_m, __gval__.packet_h, __gval__.packet_hl) + +def setChannelInfo(sid, onid, tsid, name, orgid): + if sid is None: sid = 0; + if onid is None: onid = 0; + if tsid is None: tsid = 0; + if name is None: name = ""; + if orgid is None: orgid = 0; + global __gval__ + __gval__.channel_info_sid = sid + __gval__.channel_info_onid = onid + __gval__.channel_info_tsid = tsid + __gval__.channel_info_name = name + __gval__.channel_info_orgid = orgid + print "Set Channel Info >> sid : %X, onid : %X, tsid : %X, name : %s, orgid : %d " % (sid, onid, tsid, name, orgid) +def getChannelInfos(): + global __gval__ + print "Get Channel Info >> sid : %X, onid : %X, tsid : %X, name : %s, orgid : %d " % (__gval__.channel_info_sid, + __gval__.channel_info_onid, __gval__.channel_info_tsid, __gval__.channel_info_name, __gval__.channel_info_orgid) + return (__gval__.channel_info_sid, + __gval__.channel_info_onid, + __gval__.channel_info_tsid, + __gval__.channel_info_name, + __gval__.channel_info_orgid) + +def getCommandUtil(): + global __gval__ + return __gval__.command_util +def getCommandServer(): + global __gval__ + return __gval__.command_server + +def setBeforeService(s): + global __gval__ + __gval__.before_service = s +def getBeforeService(): + global __gval__ + return __gval__.before_service + +def _unpack(packed_data): + (mg, h, hlen) = getPacketHeaders() + + if strIsEmpty(packed_data): + return None + (m, o, l, s) = struct.unpack(h, packed_data[:hlen]) + if m != mg: + return None + d = 0 + if l > 0: + d = packed_data[hlen:hlen+l] + return (o,d,s) + +def _pack(opcode, params=None, reserved=0): + (m, h, hlen) = getPacketHeaders() + if strIsEmpty(params): + params = '' + packed_data = struct.pack(h, m, opcode, len(params), reserved) + return packed_data + params + +class MMSStreamURL: + headers = [ + 'GET %s HTTP/1.0' + ,'Accept: */* ' + ,'User-Agent: NSPlayer/7.10.0.3059 ' + ,'Host: %s ' + ,'Connection: Close ' + ] + + def __init__(self): + self.sendmsg = '' + for m in self.headers: + self.sendmsg += m + '\n' + self.sendmsg += '\n\n' + + def request(self, host, port=80, location='/'): + sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) + sock.connect((host, port)) + sock.send(self.sendmsg%(location, host)) + print "Send Data : " + print self.sendmsg%(location, host) + fullydata = '' + while 1: + res = sock.recv(1024) + if res == '': break + fullydata += res + sock.close() + return fullydata + + def parse(self, data): + for d in data.splitlines(): + if d.startswith('Location: '): + return d[9:] + return None + + def getLocationData(self, url): + url_list,host,location = None,None,None + try: + url = url[url.find(':')+3:] + url_list = url.split('/') + host = url_list[0] + location = url[len(url_list[0]):] + except Exception, err_msg: + print err_msg + return None + html = self.request(host=host, location=location) + return self.parse(html) + +class OpCodeSet: + def __init__(self): + self._opcode_ = { + "OP_UNKNOWN" : 0x0000 + ,"OP_HBBTV_EXIT" : 0x0001 + ,"OP_HBBTV_OPEN_URL" : 0x0002 + ,"OP_HBBTV_LOAD_AIT" : 0x0003 + ,"OP_HBBTV_UNLOAD_AIT" : 0x0004 + ,"OP_HBBTV_FULLSCREEN" : 0x0005 + ,"OP_HBBTV_TITLE" : 0x0006 + ,"OP_OIPF_GET_CHANNEL_INFO_URL" : 0x0101 + ,"OP_OIPF_GET_CHANNEL_INFO_AIT" : 0x0102 + ,"OP_OIPF_GET_CHANNEL_INFO_LIST": 0x0103 + ,"OP_VOD_URI" : 0x0201 + ,"OP_VOD_PLAY" : 0x0202 + ,"OP_VOD_STOP" : 0x0203 + ,"OP_VOD_PAUSE" : 0x0204 + ,"OP_VOD_STATUS" : 0x0205 + ,"OP_VOD_FORBIDDEN" : 0x0206 + ,"OP_BROWSER_OPEN_URL" : 0x0301 + } + self._opstr_ = { + 0x0000 : "OP_UNKNOWN" + ,0x0001 : "OP_HBBTV_EXIT" + ,0x0002 : "OP_HBBTV_OPEN_URL" + ,0x0003 : "OP_HBBTV_LOAD_AIT" + ,0x0004 : "OP_HBBTV_UNLOAD_AIT" + ,0x0005 : "OP_HBBTV_FULLSCREEN" + ,0x0006 : "OP_HBBTV_TITLE" + ,0x0101 : "OP_OIPF_GET_CHANNEL_INFO_URL" + ,0x0102 : "OP_OIPF_GET_CHANNEL_INFO_AIT" + ,0x0103 : "OP_OIPF_GET_CHANNEL_INFO_LIST" + ,0x0201 : "OP_VOD_URI" + ,0x0202 : "OP_VOD_PLAY" + ,0x0203 : "OP_VOD_STOP" + ,0x0204 : "OP_VOD_PAUSE" + ,0x0205 : "OP_VOD_STATUS" + ,0x0206 : "OP_VOD_FORBIDDEN" + ,0x0301 : "OP_BROWSER_OPEN_URL" + } + + def get(self, opstr): + try: + return self._opcode_[opstr] + except: pass + return self._opcode_["OP_UNKNOWN"] + + def what(self, opcode): + try: + return self._opstr_[opcode] + except: pass + return self._opstr_["0x0000"] + +class SocketParams: + def __init__(self): + self.protocol = None + self.type = None + self.addr = None + self.buf_size = 4096 + self.handler = None + self.timeout = 5 + self.destroy = None + +class StreamServer: + def __init__(self, params): + self._protocol = params.protocol + self._type = params.type + self._addr = params.addr + self._buf_size = params.buf_size + self._handler = params.handler + self._timeout = params.timeout + self._destroy = params.destroy + + self._terminated = False + self._server_thread = None + + self.onHbbTVCloseCB = [] + self.onSetPageTitleCB = [] + + def __del__(self): + if self._destroy is not None: + self._destroy(self._addr) + + def stop(self): + self._terminated = True + if self._server_thread is not None: + self._server_thread.join() + self._server_thread = None + + def start(self): + self._socket = socket.socket(self._protocol, self._type) + self._socket.settimeout(self._timeout) + self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self._socket.bind(self._addr) + self._socket.listen(True) + + self._server_thread = threading.Thread(target=self._listen) + self._server_thread.start() + + def _listen(self): + select_list = [self._socket] + def _accept(): + try: + conn, addr = self._socket.accept() + self._client(conn, addr) + except Exception, ErrMsg: + print "ServerSocket Error >>", ErrMsg + pass + + while not self._terminated: + readable, writable, errored = select.select(select_list, [], [], self._timeout) + for s in readable: + if s is self._socket: + _accept() + + def _client(self, conn, addr): + try: + send_data = '' + received_data = conn.recv(self._buf_size) + if self._handler is not None and not strIsEmpty(received_data): + send_data = self._handler.doHandle(received_data, self.onHbbTVCloseCB, self.onSetPageTitleCB) + self._send(conn, send_data) + except Exception, ErrMsg: + try: conn.close() + except:pass + if self._handler is not None: + self._handler.printError(ErrMsg) + def _send(self, conn, data) : + conn.send(data) + conn.close() + +class ServerFactory: + def doListenUnixTCP(self, name, handler): + def destroy(name): + if os.path.exists(name): + os.unlink(name) + print "Removed ", name + destroy(name) + + params = SocketParams() + params.protocol = socket.AF_UNIX + params.type = socket.SOCK_STREAM + params.addr = name + params.handler = handler + params.destroy = destroy + + streamServer = StreamServer(params) + streamServer.start() + return streamServer + + def doListenInetTCP(self, ip, port, handler): + print "not implemented yet!!" + def doListenUnixDGRAM(self, name, handler): + print "not implemented yet!!" + def doListenInetDGRAM(self, ip, port, handler): + print "not implemented yet!!" + +class Handler: + def doUnpack(self, data): + return _unpack(data) + + def doPack(self, opcode, params, reserved=0): + return _pack(opcode, params, reserved) + + def doHandle(self, data, onCloseCB): + opcode, params = 0x0, 'Invalid Request!!' + return _pack(opcode, params) + + def printError(self, reason): + print reason + +class BrowserCommandUtil(OpCodeSet): + def __init__(self): + self._fd = None + OpCodeSet.__init__(self) + + def isConnected(self): + if self._fd is None: + return False + return True + + def doConnect(self, filename): + if not os.path.exists(filename): + print "file not exists :", filename + return False + try: + self._fd = os.open(filename, os.O_WRONLY|os.O_NONBLOCK) + if self._fd is None: + print "fail to open file :", filename + return False + except Exception, ErrMsg: + print ErrMsg + self._fd = None + return False + print "connected!! to ", filename + return True + + def doDisconnect(self): + if self._fd is None: + return + os.close(self._fd) + self._fd = None + + def doSend(self, command, params=None, reserved=0): + if self._fd is None: + print "connected pipe was not exists!!" + return False + data = '' + try: + data = _pack(self.get(command), params, reserved) + if data is None: + return False + os.write(self._fd, data) + print "Send OK!! :", command + except: return False + return True + + def sendCommand(self, command, params=None, reserved=0): + if not self.isConnected(): + global COMMAND_PATH + self.doConnect(COMMAND_PATH) + result = self.doSend(command, params, reserved) + self.doDisconnect() + return result + +class HandlerHbbTV(Handler): + _vod_service = None + def __init__(self, session): + self._session = session + self.opcode = OpCodeSet() + self.handle_map = { + 0x0001 : self._cb_handleCloseHbbTVBrowser + ,0x0006 : self._cb_handleSetPageTitle + ,0x0101 : self._cb_handleGetChannelInfoForUrl + ,0x0102 : self._cb_handleGetChannelInfoForAIT + ,0x0103 : self._cb_handleGetChannelInfoList + ,0x0201 : self._cb_handleVODPlayerURI + ,0x0202 : self._cb_handleVODPlayerPlay + ,0x0203 : self._cb_handleVODPlayerStop + ,0x0204 : self._cb_handleVODPlayerPlayPause + } + self._on_close_cb = None + self._on_set_title_cb = None + + self._vod_uri = None + + def _handle_dump(self, handle, opcode, data=None): + if True: return + print str(handle) + try: + print " - opcode : ", self.opcode.what(opcode) + except: pass + print " - data : ", data + + def doHandle(self, data, onCloseCB, onSetPageTitleCB): + opcode, params, reserved = None, None, 0 + self._on_close_cb = onCloseCB + self._on_set_title_cb = onSetPageTitleCB + try: + datas = self.doUnpack(data) + except Exception, ErrMsg: + print "Unpacking packet ERR :", ErrMsg + params = 'fail to unpack packet!!' + opcode = self.opcode.get("OP_UNKNOWN") + return self.doPack(opcode, params) + else: + opcode = datas[0] + params = datas[1] + self.opcode.what(opcode) + + try: + #print self.handle_map[opcode] + (reserved, params) = self.handle_map[opcode](opcode, params) + except Exception, ErrMsg: + print "Handling packet ERR :", ErrMsg + params = 'fail to handle packet!!' + opcode = self.opcode.get("OP_UNKNOWN") + return self.doPack(opcode, params) + self._on_close_cb = None + self._on_set_title_cb = None + return self.doPack(opcode, params, reserved) + + def _cb_handleGetChannelInfoForUrl(self, opcode, data): + self._handle_dump(self._cb_handleGetChannelInfoForUrl, opcode, data) + (sid, onid, tsid, name, orgid) = getChannelInfos() + namelen = len(name) + return (0, struct.pack('!IIII', sid, onid, tsid, namelen) + name) + + def _cb_handleGetChannelInfoForAIT(self, opcode, data): + self._handle_dump(self._cb_handleGetChannelInfoForAIT, opcode, data) + (sid, onid, tsid, name, orgid) = getChannelInfos() + namelen = len(name) + return (0, struct.pack('!IIIII', orgid, sid, onid, tsid, namelen) + name) + + def _cb_handleGetChannelInfoList(self, opcode, data): + self._handle_dump(self._cb_handleGetChannelInfoList, opcode, data) + (sid, onid, tsid, name, orgid) = getChannelInfos() + namelen = len(name) + channel_list_size = 1 + return (channel_list_size, struct.pack('!IIII', sid, onid, tsid, namelen) + name) + + def _cb_handleSetPageTitle(self, opcode, data): + self._handle_dump(self._cb_handleCloseHbbTVBrowser, opcode, data) + if data.startswith('file://') or data.startswith('http://'): + return "OK" + if self._on_set_title_cb is not None: + for x in self._on_set_title_cb: + try: + x(data) + except Exception, ErrMsg: + if x in self._on_set_title_cb: + self._on_set_title_cb.remove(x) + return (0, "OK") + + def _cb_handleCloseHbbTVBrowser(self, opcode, data): + self._handle_dump(self._cb_handleCloseHbbTVBrowser, opcode, data) + + if self._on_close_cb: + for x in self._on_close_cb: + try: + x() + except Exception, ErrMsg: + if x in self._on_close_cb: + self._on_close_cb.remove(x) + + command_util = getCommandUtil() + command_util.sendCommand('OP_HBBTV_FULLSCREEN', None) + + before_service = getBeforeService() + if before_service is not None: + self._session.nav.playService(before_service) + return (0, "OK") + + def _cb_handleVODPlayerURI(self, opcode, data): + self._vod_uri = None + hl = struct.calcsize('!II') + datas = struct.unpack('!II', data[:hl]) + uriLength = datas[1] + vodUri = data[hl:hl+uriLength] + self._handle_dump(self._cb_handleVODPlayerURI, opcode, vodUri) + self._vod_uri = vodUri + return (0, "OK") + + def doStop(self, restoreBeforeService=True, needStop=True): + if needStop == True: + self._session.nav.stopService() + if self._vod_service is not None and restoreBeforeService: + before_service = getBeforeService() + self._session.nav.playService(before_service) + self._vod_uri = None + self._vod_service = None + + def getUrl(self): + return self._vod_uri + + def doRetryOpen(self, url): + if url is None: + return False + for ii in range(5): + self._vod_service = None + try: + print "try to open vod [%d] : %s" % (ii, url) + self._vod_service = eServiceReference(4097, 0, url) + self._session.nav.playService(self._vod_service) + if self._vod_service is not None: + return True + except Exception, ErrMsg: + print "OpenVOD ERR :", ErrMsg + time.sleep(1) + return False + + def _cb_handleVODPlayerPlay(self, opcode, data): + self._handle_dump(self._cb_handleVODPlayerPlay, opcode, data) + self.doStop(restoreBeforeService=False) + if self.doRetryOpen(url=self._vod_uri) == False: + self.doStop() + return (0, "OK") + + def _cb_handleVODPlayerStop(self, opcode, data): + self._handle_dump(self._cb_handleVODPlayerStop, opcode, data) + self.doStop() + return (0, "OK") + + def _cb_handleVODPlayerPlayPause(self, opcode, data): + self._handle_dump(self._cb_handleVODPlayerPlayPause, opcode, data) + service = self._session.nav.getCurrentService() + try: + pauseFlag = data[0] + servicePause = service.pause() + if pauseFlag == 'U': + servicePause.unpause() + elif pauseFlag == 'P': + servicePause.pause() + except Exception, ErrMsg: + print "onPause ERR :", ErrMsg + return (0, "OK") + +class HbbTVWindow(Screen, InfoBarNotifications): + skin = """ + + + """ + def __init__(self, session, url=None, cbf=None, useAIT=False): + self._session = session + eRCInput.getInstance().lock() + + Screen.__init__(self, session) + InfoBarNotifications.__init__(self) + self.__event_tracker = ServiceEventTracker(screen = self, eventmap = { + iPlayableService.evUser+20: self._serviceForbiden, + }) + + self._url = url + self._use_ait = useAIT + self._cb_closed_func = cbf + self.onLayoutFinish.append(self._layoutFinished) + + command_server = getCommandServer() + if self._cb_set_page_title not in command_server.onSetPageTitleCB: + command_server.onSetPageTitleCB.append(self._cb_set_page_title) + + if self._cb_close_window not in command_server.onHbbTVCloseCB: + command_server.onHbbTVCloseCB.append(self._cb_close_window) + + self._closeTimer = eTimer() + self._closeTimer.callback.append(self._do_close) + + def _layoutFinished(self): + command_util = getCommandUtil() + (sid, onid, tsid, name, orgid) = getChannelInfos() + params = struct.pack('!IIIII', orgid, sid, onid, tsid, len(name)) + name + if self._use_ait: + command_util.sendCommand('OP_HBBTV_UNLOAD_AIT') + time.sleep(1) + command_util.sendCommand('OP_HBBTV_LOAD_AIT', params, 1) + return + command_util.sendCommand('OP_HBBTV_LOAD_AIT', params) + time.sleep(1) + command_util.sendCommand('OP_HBBTV_OPEN_URL', self._url) + + def _cb_close_window(self): + self._closeTimer.start(1000) + + def _do_close(self): + self._closeTimer.stop() + command_server = getCommandServer() + try: + if self._cb_set_page_title in command_server.onSetPageTitleCB: + command_server.onSetPageTitleCB.remove(self._cb_set_page_title) + except Exception, ErrMsg: pass + try: + if self._cb_close_window in command_server.onHbbTVCloseCB: + command_server.onHbbTVCloseCB.remove(self._cb_close_window) + except Exception, ErrMsg: pass + try: + if self._cb_closed_func is not None: + self._cb_closed_func() + except: pass + eRCInput.getInstance().unlock() + self.close() + + def _serviceForbiden(self): + global __gval__ + real_url = MMSStreamURL().getLocationData(__gval__.hbbtv_handelr.getUrl()) + print "Received URI :\n",real_url + + if real_url is not None: + __gval__.hbbtv_handelr.doRetryOpen(real_url.strip()) + + def _cb_set_page_title(self, title=None): + print "page title :",title + if title is None: + return + self.setTitle(title) + +class HbbTVHelper(Screen): + skin = """""" + def __init__(self, session): + global __gval__ + __gval__.hbbtv_handelr = HandlerHbbTV(session) + __gval__.command_server = ServerFactory().doListenUnixTCP('/tmp/.sock.hbbtv.url', __gval__.hbbtv_handelr) + + self._urls = None + self._stop_opera() + self._start_opera() + + Screen.__init__(self, session) + self._session = session + self._timer_infobar = eTimer() + self._timer_infobar.callback.append(self._cb_registrate_infobar) + self._timer_infobar.start(1000) + + self._excuted_browser = False + + __gval__.command_util = BrowserCommandUtil() + + def _cb_registrate_infobar(self): + if InfoBar.instance: + self._timer_infobar.stop() + if self._cb_ready_for_ait not in InfoBar.instance.onReadyForAIT: + InfoBar.instance.onReadyForAIT.append(self._cb_ready_for_ait) + if self._cb_hbbtv_activated not in InfoBar.instance.onHBBTVActivation: + InfoBar.instance.onHBBTVActivation.append(self._cb_hbbtv_activated) + + def _cb_ready_for_ait(self, orgId=0): + if orgId == 0: + if not self._excuted_browser: + command_util = getCommandUtil() + command_util.sendCommand('OP_HBBTV_UNLOAD_AIT') + return + setChannelInfo(None, None, None, None, None) + + service = self._session.nav.getCurrentService() + info = service and service.info() + if info is not None: + sid = info.getInfo(iServiceInformation.sSID) + onid = info.getInfo(iServiceInformation.sONID) + tsid = info.getInfo(iServiceInformation.sTSID) + name = info.getName() + if name is None: + name = "" + orgid = 0 + namelen = len(name) + for x in info.getInfoObject(iServiceInformation.sHBBTVUrl): + if x[0] == 1 : + orgid = x[3] + break + setChannelInfo(sid, onid, tsid, name, orgid) + + def _cb_hbbtv_activated(self, title=None, url=None): + if not self._is_browser_running(): + message = "HbbTV Browser was not running.\nPlease running browser before start HbbTV Application." + self.session.open(MessageBox, message, MessageBox.TYPE_INFO) + return + service = self._session.nav.getCurrentlyPlayingServiceReference() + setBeforeService(service) + self._start_hbbtv_application(title, url) + + def _start_hbbtv_application(self, title, url): + tmp_url = self.getStartHbbTVUrl() + if url is None: + url = tmp_url + if strIsEmpty(url): + print "can't get url of hbbtv!!" + return + print "success to get url of hbbtv!! >>", url + if self._excuted_browser: + print "already excuted opera browser!!" + return + + use_ait = False + for x in self._urls: + control_code = x[0] + tmp_url = x[2] + if tmp_url == url and control_code == 1: + use_ait = True + self._excuted_browser = True + self._session.open(HbbTVWindow, url, self._cb_closed_browser, use_ait) + + def _cb_closed_browser(self): + self._excuted_browser = False + + def _start_opera(self): + if not self._is_browser_running(): + global HBBTVAPP_PATH + start_command = '%s/launcher start'%(HBBTVAPP_PATH) + os.system(start_command) + + def _stop_opera(self): + global HBBTVAPP_PATH + try: os.system('%s/launcher stop'%(HBBTVAPP_PATH)) + except: pass + + def getStartHbbTVUrl(self): + url, self._urls = None, None + service = self._session.nav.getCurrentService() + info = service and service.info() + if not info: return None + self._urls = info.getInfoObject(iServiceInformation.sHBBTVUrl) + for u in self._urls: + if u[0] == 1: # 0:control code, 1:name, 2:url, 3:orgid, 4:appid + url = u[2] + if url is None: + url = info.getInfoString(iServiceInformation.sHBBTVUrl) + return url + + def showApplicationSelectionBox(self): + applications = [] + if self.getStartHbbTVUrl(): + for x in self._urls: + applications.append((x[1], x)) + else: applications.append(("No detected HbbTV applications.", None)) + self._session.openWithCallback(self._application_selected, ChoiceBox, title=_("Please choose an HbbTV application."), list=applications) + + def _application_selected(self, selected): + try: + if selected[1] is None: return + self._cb_hbbtv_activated(selected[1][1], selected[1][2]) + except Exception, ErrMsg: print ErrMsg + + def showBrowserConfigBox(self): + start_stop_mode = [] + if self._is_browser_running(): + start_stop_mode.append(('Stop',None)) + else: start_stop_mode.append(('Start',None)) + self._session.openWithCallback(self._browser_config_selected, ChoiceBox, title=_("Please choose one."), list=start_stop_mode) + + def _browser_config_selected(self, selected): + if selected is None: + return + try: + mode = selected[0] + if mode == 'Start': + if not self._is_browser_running(): + self._start_opera() + elif mode == 'Stop': + self._stop_opera() + except Exception, ErrMsg: print "Config ERR :", ErrMsg + + def _is_browser_running(self): + try: + global HBBTVAPP_PATH + ret = os.popen('%s/launcher check'%(HBBTVAPP_PATH)).read() + return ret.strip() != "0" + except Exception, ErrMsg: + print "Check Browser Running ERR :", ErrMsg + return False + +_g_helper = None +class OperaBrowser(Screen): + MENUBAR_ITEM_WIDTH = 150 + MENUBAR_ITEM_HEIGHT = 30 + SUBMENULIST_WIDTH = 200 + SUBMENULIST_HEIGHT = 25 + SUBMENULIST_NEXT = 2 + + skin = """ + + + + + + + + + """ % (MENUBAR_ITEM_HEIGHT+30, SUBMENULIST_WIDTH, SUBMENULIST_WIDTH+50+SUBMENULIST_NEXT, MENUBAR_ITEM_HEIGHT+30, SUBMENULIST_WIDTH) + + MENUITEMS_LIST =[[('Open Location', None), ('Start/Stop',None), ('Exit', None)], + [('About', None)]] + def __init__(self, session): + Screen.__init__(self, session) + + self["actions"] = ActionMap(["MinuteInputActions", "ColorActions", "InputActions", "InfobarChannelSelection", "EPGSelectActions", "KeyboardInputActions"], { + "cancel" : self.keyCancel + ,"ok" : self.keyOK + ,"left" : self.keyLeft + ,"right" : self.keyRight + ,"up" : self.keyUp + ,"down" : self.keyDown + ,"menu" : self.keyCancel + }, -2) + + self.menubarCurrentIndex = 0 + self.lvMenuItems = [] + self.lvSubMenuItems = [] + + self["topArea"] = Label() + self["bottomArea"] = Label() + + self["menuitemFile"] = MultiColorLabel() + self["menuitemHelp"] = MultiColorLabel() + + self["menulist"] = MenuList(self.setListOnView()) + self["submenulist"] = MenuList(self.setSubListOnView()) + + self.toggleMainScreenFlag = True + self.toggleListViewFlag = False + self.toggleSubListViewFlag = False + self.currentListView = self["menulist"] + + self.onLayoutFinish.append(self.layoutFinished) + + self._onCloseTimer = eTimer() + self._onCloseTimer.callback.append(self._cb_onClose) + + def enableRCMouse(self, mode): #mode=[0|1]|[False|True] + rcmouse_path = "/proc/stb/fp/mouse" + if os.path.exists(rcmouse_path): + os.system("echo %d > %s" % (mode, rcmouse_path)) + + def layoutFinished(self): + self["menuitemFile"].setText("File") + self["menuitemHelp"].setText("Help") + + self["menulist"].hide() + self["submenulist"].hide() + + self["bottomArea"].setText("Opera Web Browser Plugin v0.1") + self.setTitle("BrowserMain") + self.selectMenuitem() + + def selectMenuitem(self): + tmp = [self["menuitemFile"], self["menuitemHelp"]] + self["menuitemFile"].setForegroundColorNum(0) + self["menuitemHelp"].setForegroundColorNum(0) + tmp[self.menubarCurrentIndex].setForegroundColorNum(1) + + def popupCloseAll(self): + self.keyLeft() + self.keyLeft() + self.keyUp() + self.keyCancel() + + def setListOnView(self): + self.lvMenuItems = self.MENUITEMS_LIST[self.menubarCurrentIndex] + return self.lvMenuItems + + def setSubListOnView(self): + self.lvSubMenuItems = [] + xl = self["menulist"].getCurrent()[1] + if xl is None: return [] + for x in xl: + self.lvSubMenuItems.append((x,None)) + return self.lvSubMenuItems + + def toggleMainScreen(self): + if not self.toggleMainScreenFlag: + self.show() + else: self.hide() + self.toggleMainScreenFlag = not self.toggleMainScreenFlag + + def toggleListView(self): + if not self.toggleListViewFlag: + self["menulist"].show() + else: self["menulist"].hide() + self.toggleListViewFlag = not self.toggleListViewFlag + + def toggleSubListView(self): + if not self.toggleSubListViewFlag: + self["submenulist"].show() + else: self["submenulist"].hide() + self.toggleSubListViewFlag = not self.toggleSubListViewFlag + + def setCurrentListView(self, listViewIdx): + if listViewIdx == 0: + self.currentListView = None + elif listViewIdx == 1: + self.currentListView = self["menulist"] + elif listViewIdx == 2: + self.currentListView = self["submenulist"] + + def _cb_onClose(self): + self._onCloseTimer.stop() + command_server = getCommandServer() + try: + if self._on_close_window in command_server.onHbbTVCloseCB: + command_server.onHbbTVCloseCB.remove(self._on_close_window) + except Exception, ErrMsg: pass + try: + if self._on_setPageTitle in command_server.onSetPageTitleCB: + command_server.onSetPageTitleCB.remove(self._on_setPageTitle) + except Exception, ErrMsg: pass + self._on_setPageTitle('Opera Browser') + self.enableRCMouse(False) + self.toggleMainScreen() + eRCInput.getInstance().unlock() + + def _on_setPageTitle(self, title=None): + print "page title :",title + if title is None: + return + self.setTitle(title) + + def cbUrlText(self, data=None): + print "Inputed Url :", data + if strIsEmpty(data): + return + command_server = getCommandServer() + if self._on_setPageTitle not in command_server.onSetPageTitleCB: + command_server.onSetPageTitleCB.append(self._on_setPageTitle) + if self._on_close_window not in command_server.onHbbTVCloseCB: + command_server.onHbbTVCloseCB.append(self._on_close_window) + self.toggleMainScreen() + self.enableRCMouse(True) + eRCInput.getInstance().lock() + command_util = getCommandUtil() + command_util.sendCommand('OP_BROWSER_OPEN_URL', data) + + def _on_close_window(self): + self._onCloseTimer.start(1000) + + def _cmd_on_OpenLocation(self): + global _g_helper + if not _g_helper._is_browser_running(): + message = "Opera Browser was not running.\nPlease running browser using [File]>[Start/Stop] menu." + self.session.open(MessageBox, message, MessageBox.TYPE_INFO) + return + self.session.openWithCallback(self.cbUrlText, VirtualKeyBoard, title=("Please enter URL here"), text='http://') + def _cmd_on_About(self): + self.session.open(MessageBox, 'Opera Web Browser Plugin v0.1(beta)', type = MessageBox.TYPE_INFO) + def _cmd_on_Exit(self): + self.close() + def _cmd_on_StartStop(self): + global _g_helper + if _g_helper is None: + return + _g_helper.showBrowserConfigBox() + def doCommand(self, command): + cmd_map = { + 'Exit' :self._cmd_on_Exit + ,'About' :self._cmd_on_About + ,'Open Location' :self._cmd_on_OpenLocation + ,'Start/Stop' :self._cmd_on_StartStop + } + try: + cmd_map[command]() + except: pass + + def keyOK(self): + if not self.toggleListViewFlag: + self.keyDown() + return + if self.currentListView.getCurrent()[1] is None: + self.doCommand(self.currentListView.getCurrent()[0]) + #self.session.open(MessageBox, _(self.currentListView.getCurrent()[0]), type = MessageBox.TYPE_INFO) + return + self.keyRight() + + def updateSelectedMenuitem(self, status): + if self.menubarCurrentIndex == 0 and status < 0: + self.menubarCurrentIndex = 1 + elif self.menubarCurrentIndex == 1 and status > 0: + self.menubarCurrentIndex = 0 + else: self.menubarCurrentIndex += status + self.selectMenuitem() + + def keyLeft(self): + if not self.toggleMainScreenFlag: + return + if not self.toggleListViewFlag: + self.updateSelectedMenuitem(-1) + return + if self.toggleSubListViewFlag: + self.setCurrentListView(1) + self.toggleSubListView() + return + if self.currentListView.getSelectedIndex(): + self.currentListView.pageUp() + + def keyRight(self): + if not self.toggleMainScreenFlag: + return + if not self.toggleListViewFlag: + self.updateSelectedMenuitem(1) + return + if self.currentListView is None: + return + if self.currentListView.getCurrent()[1] is not None: + parentSelectedIndex = self.currentListView.getSelectedIndex() + self.setCurrentListView(2) + self.currentListView.setList(self.setSubListOnView()) + self.currentListView.resize(self.SUBMENULIST_WIDTH, self.SUBMENULIST_HEIGHT*len(self.lvSubMenuItems)+5) + self.currentListView.move(self.MENUBAR_ITEM_WIDTH*self.menubarCurrentIndex + self.SUBMENULIST_WIDTH+self.SUBMENULIST_NEXT + 50,self.MENUBAR_ITEM_HEIGHT+30+(parentSelectedIndex*self.SUBMENULIST_HEIGHT)) + self.toggleSubListView() + + def keyDown(self): + if not self.toggleMainScreenFlag: + return + if self.currentListView is None: + return + if not self.toggleListViewFlag: + self.currentListView.setList(self.setListOnView()) + self.currentListView.resize(self.SUBMENULIST_WIDTH, self.SUBMENULIST_HEIGHT*len(self.lvMenuItems)+5) + self.currentListView.move(self.MENUBAR_ITEM_WIDTH*self.menubarCurrentIndex+1+ 50,self.MENUBAR_ITEM_HEIGHT+30) + self.toggleListView() + return + self.currentListView.down() + + def keyUp(self): + if not self.toggleMainScreenFlag: + return + if self.currentListView is None: + return + if self.currentListView == self["menulist"]: + if self.currentListView.getSelectedIndex() == 0: + self.toggleListView() + return + self.currentListView.up() + + def keyCancel(self): + self.toggleMainScreen() + +def auto_start_main(reason, **kwargs): + if reason: + command_server = getCommandServer() + command_server.stop() + +def session_start_main(session, reason, **kwargs): + global _g_helper + _g_helper = session.open(HbbTVHelper) + +def plugin_start_main(session, **kwargs): + session.open(OperaBrowser) + +def plugin_extension_start_application(session, **kwargs): + global _g_helper + if _g_helper is None: + return + _g_helper.showApplicationSelectionBox() + +def plugin_extension_browser_config(session, **kwargs): + global _g_helper + if _g_helper is None: + return + _g_helper.showBrowserConfigBox() + +def Plugins(path, **kwargs): + return [ + PluginDescriptor(where=PluginDescriptor.WHERE_AUTOSTART, fnc=auto_start_main), + PluginDescriptor(where=PluginDescriptor.WHERE_SESSIONSTART, needsRestart=True, fnc=session_start_main, weight=-10), + PluginDescriptor(name="HbbTV Applications", where=PluginDescriptor.WHERE_EXTENSIONSMENU, needsRestart=True, fnc=plugin_extension_start_application), + PluginDescriptor(name="Browser Start/Stop", where=PluginDescriptor.WHERE_EXTENSIONSMENU, needsRestart=True, fnc=plugin_extension_browser_config), + PluginDescriptor(name="Opera Web Browser", description="start opera web browser", where=PluginDescriptor.WHERE_PLUGINMENU, needsRestart=True, fnc=plugin_start_main) + ] + diff --git a/lib/python/Plugins/Extensions/Makefile.am b/lib/python/Plugins/Extensions/Makefile.am index c5806a3..0daed1f 100755 --- a/lib/python/Plugins/Extensions/Makefile.am +++ b/lib/python/Plugins/Extensions/Makefile.am @@ -1,7 +1,7 @@ installdir = $(pkglibdir)/python/Plugins/Extensions SUBDIRS = TuxboxPlugins CutListEditor PicturePlayer MediaScanner MediaPlayer GraphMultiEPG SocketMMI DVDBurn Modem WebBrowser \ - VuplusEvent StreamTV DLNABrowser DLNAServer + VuplusEvent StreamTV DLNABrowser DLNAServer HbbTV if HAVE_LIBDDVD SUBDIRS += DVDPlayer diff --git a/lib/python/Screens/InfoBar.py b/lib/python/Screens/InfoBar.py index 860022a..ed8acf6 100755 --- a/lib/python/Screens/InfoBar.py +++ b/lib/python/Screens/InfoBar.py @@ -11,7 +11,7 @@ from enigma import iPlayableService profile("LOAD:InfoBarGenerics") from Screens.InfoBarGenerics import InfoBarShowHide, \ InfoBarNumberZap, InfoBarChannelSelection, InfoBarMenu, InfoBarRdsDecoder, \ - InfoBarEPG, InfoBarSeek, InfoBarInstantRecord, \ + InfoBarEPG, InfoBarSeek, InfoBarInstantRecord, InfoBarRedButton, \ InfoBarAudioSelection, InfoBarAdditionalInfo, InfoBarNotifications, InfoBarDish, InfoBarUnhandledKey, \ InfoBarSubserviceSelection, InfoBarShowMovies, InfoBarTimeshift, \ InfoBarServiceNotifications, InfoBarPVRState, InfoBarCueSheetSupport, InfoBarSimpleEventView, \ @@ -28,7 +28,7 @@ from Screens.HelpMenu import HelpableScreen class InfoBar(InfoBarBase, InfoBarShowHide, InfoBarNumberZap, InfoBarChannelSelection, InfoBarMenu, InfoBarEPG, InfoBarRdsDecoder, - InfoBarInstantRecord, InfoBarAudioSelection, + InfoBarInstantRecord, InfoBarAudioSelection, InfoBarRedButton, HelpableScreen, InfoBarAdditionalInfo, InfoBarNotifications, InfoBarDish, InfoBarUnhandledKey, InfoBarSubserviceSelection, InfoBarTimeshift, InfoBarSeek, InfoBarSummarySupport, InfoBarTimeshiftState, InfoBarTeletextPlugin, InfoBarExtensions, @@ -54,7 +54,7 @@ class InfoBar(InfoBarBase, InfoBarShowHide, for x in HelpableScreen, \ InfoBarBase, InfoBarShowHide, \ InfoBarNumberZap, InfoBarChannelSelection, InfoBarMenu, InfoBarEPG, InfoBarRdsDecoder, \ - InfoBarInstantRecord, InfoBarAudioSelection, InfoBarUnhandledKey, \ + InfoBarInstantRecord, InfoBarAudioSelection, InfoBarRedButton, InfoBarUnhandledKey, \ InfoBarAdditionalInfo, InfoBarNotifications, InfoBarDish, InfoBarSubserviceSelection, \ InfoBarTimeshift, InfoBarSeek, InfoBarSummarySupport, InfoBarTimeshiftState, \ InfoBarTeletextPlugin, InfoBarExtensions, InfoBarPiP, InfoBarSubtitleSupport, InfoBarJobman, \ diff --git a/lib/python/Screens/InfoBarGenerics.py b/lib/python/Screens/InfoBarGenerics.py index 07fbac8..6a148eb 100755 --- a/lib/python/Screens/InfoBarGenerics.py +++ b/lib/python/Screens/InfoBarGenerics.py @@ -1828,6 +1828,63 @@ class InfoBarSubserviceSelection: else: del self.selectedSubservice +from Components.Sources.HbbtvApplication import HbbtvApplication +class InfoBarRedButton: + def __init__(self): + if not (config.misc.rcused.value == 1): + self["RedButtonActions"] = HelpableActionMap(self, "InfobarRedButtonActions", + { + "activateRedButton": (self.activateRedButton, _("Red button...")), + }) + self["HbbtvApplication"] = HbbtvApplication() + else: + self["HbbtvApplication"] = Boolean(fixed=0) + self["HbbtvApplication"].name = "" #is this a hack? + + self.onHBBTVActivation = [ ] + self.onRedButtonActivation = [ ] + self.onReadyForAIT = [ ] + self.__et = ServiceEventTracker(screen=self, eventmap= + { + iPlayableService.evHBBTVInfo: self.detectedHbbtvApplication, + iPlayableService.evUpdatedInfo: self.updateInfomation + }) + + def updateAIT(self, orgId=0): + for x in self.onReadyForAIT: + try: + x(orgId) + except Exception, ErrMsg: + print ErrMsg + #self.onReadyForAIT.remove(x) + + def updateInfomation(self): + self["HbbtvApplication"].setApplicationName("") + self.updateAIT() + + def detectedHbbtvApplication(self): + service = self.session.nav.getCurrentService() + info = service and service.info() + try: + for x in info.getInfoObject(iServiceInformation.sHBBTVUrl): + print x + if x[0] == 1: + self.updateAIT(x[3]) + self["HbbtvApplication"].setApplicationName(x[1]) + break + except Exception, ErrMsg: + pass + + def activateRedButton(self): + service = self.session.nav.getCurrentService() + info = service and service.info() + if info and info.getInfoString(iServiceInformation.sHBBTVUrl) != "": + for x in self.onHBBTVActivation: + x() + elif False: # TODO: other red button services + for x in self.onRedButtonActivation: + x() + class InfoBarAdditionalInfo: def __init__(self): diff --git a/lib/service/iservice.h b/lib/service/iservice.h index 7f58249..dffea52 100644 --- a/lib/service/iservice.h +++ b/lib/service/iservice.h @@ -359,6 +359,8 @@ public: sTransferBPS, + sHBBTVUrl, + sUser = 0x100 }; enum { @@ -836,6 +838,7 @@ public: evBuffering, evStopped, + evHBBTVInfo, evUser = 0x100 }; diff --git a/lib/service/servicedvb.cpp b/lib/service/servicedvb.cpp index 93b0cc6..7c8a660 100644 --- a/lib/service/servicedvb.cpp +++ b/lib/service/servicedvb.cpp @@ -1061,6 +1061,9 @@ void eDVBServicePlay::serviceEvent(int event) case eDVBServicePMTHandler::eventSOF: m_event((iPlayableService*)this, evSOF); break; + case eDVBServicePMTHandler::eventHBBTVInfo: + m_event((iPlayableService*)this, evHBBTVInfo); + break; } } @@ -1751,6 +1754,13 @@ std::string eDVBServicePlay::getInfoString(int w) return m_dvb_service->m_provider_name; case sServiceref: return m_reference.toString(); + case sHBBTVUrl: + { + std::string url; + eDVBServicePMTHandler &h = m_timeshift_active ? m_service_handler_timeshift : m_service_handler; + h.getHBBTVUrl(url); + return url; + } default: break; } @@ -1767,6 +1777,11 @@ PyObject *eDVBServicePlay::getInfoObject(int w) return m_service_handler.getCaIds(true); case sTransponderData: return eStaticServiceDVBInformation().getInfoObject(m_reference, w); + case sHBBTVUrl: + { + eDVBServicePMTHandler &h = m_timeshift_active ? m_service_handler_timeshift : m_service_handler; + return h.getHbbTVApplications(); + } default: break; } diff --git a/lib/service/servicemp3.cpp b/lib/service/servicemp3.cpp index 6aac29e..0b85775 100644 --- a/lib/service/servicemp3.cpp +++ b/lib/service/servicemp3.cpp @@ -1253,7 +1253,7 @@ void eServiceMP3::gstBusCall(GstBus *bus, GstMessage *msg) GError *err; gst_message_parse_error (msg, &err, &debug); g_free (debug); - eWarning("Gstreamer error: %s (%i) from %s", err->message, err->code, sourceName ); + eWarning("Gstreamer error: %s (domain:%i, code:%i) from %s", err->message, err->domain, err->code, sourceName ); if ( err->domain == GST_STREAM_ERROR ) { if ( err->code == GST_STREAM_ERROR_CODEC_NOT_FOUND ) @@ -1264,6 +1264,11 @@ void eServiceMP3::gstBusCall(GstBus *bus, GstMessage *msg) m_event((iPlayableService*)this, evUser+10); } } + else //if( err->domain == 1232 ) + { + if ( err->code == 5 ) + m_event((iPlayableService*)this, evUser+20); + } g_error_free(err); break; } -- 2.7.4