X-Git-Url: https://git.cweiske.de/enigma2.git/blobdiff_plain/bcfb71b423699d8f7e1d1e7bb5dc24ad4413a4ae..3931e5f4380d167e1d651f0d056618294cf8234f:/lib/dvb/pmt.cpp diff --git a/lib/dvb/pmt.cpp b/lib/dvb/pmt.cpp index 5e9c7602..e1aa096d 100644 --- a/lib/dvb/pmt.cpp +++ b/lib/dvb/pmt.cpp @@ -1,3 +1,4 @@ +#include // access to python config #include #include #include @@ -5,15 +6,21 @@ #include #include #include +#include +#include #include #include #include #include #include #include +#include +#include +#include +#include eDVBServicePMTHandler::eDVBServicePMTHandler() - :m_ca_servicePtr(0), m_decode_demux_num(0xFF) + :m_ca_servicePtr(0), m_dvb_scan(0), m_decode_demux_num(0xFF) { m_use_decode_demux = 0; m_pmt_pid = -1; @@ -45,10 +52,13 @@ void eDVBServicePMTHandler::channelStateChanged(iDVBChannel *channel) { eDebug("ok ... now we start!!"); - if (m_pmt_pid == -1) - m_PAT.begin(eApp, eDVBPATSpec(), m_demux); - else - m_PMT.begin(eApp, eDVBPMTSpec(m_pmt_pid, m_reference.getServiceID().get()), m_demux); + if (!m_service || m_service->usePMT()) + { + if (m_pmt_pid == -1) + m_PAT.begin(eApp, eDVBPATSpec(), m_demux); + else + m_PMT.begin(eApp, eDVBPMTSpec(m_pmt_pid, m_reference.getServiceID().get()), m_demux); + } if ( m_service && !m_service->cacheEmpty() ) serviceEvent(eventNewProgramInfo); @@ -82,11 +92,12 @@ void eDVBServicePMTHandler::PMTready(int error) serviceEvent(eventNoPMT); else { + m_have_cached_program = false; serviceEvent(eventNewProgramInfo); - eEPGCache::getInstance()->PMTready(this); - if (!m_pvr_channel) + if (!m_pvr_channel) // don't send campmt to camd.socket for playbacked services { - if(!m_ca_servicePtr) // don't send campmt to camd.socket for playbacked services + eEPGCache::getInstance()->PMTready(this); + if(!m_ca_servicePtr) { int demuxes[2] = {0,0}; uint8_t tmp; @@ -97,7 +108,7 @@ void eDVBServicePMTHandler::PMTready(int error) else demuxes[1]=demuxes[0]; eDVBCAService::register_service(m_reference, demuxes, m_ca_servicePtr); - eDVBCIInterfaces::getInstance()->addPMTHandler(this); + eDVBCIInterfaces::getInstance()->recheckPMTHandlers(); } eDVBCIInterfaces::getInstance()->gotPMT(this); } @@ -137,260 +148,428 @@ void eDVBServicePMTHandler::PATready(int) PyObject *eDVBServicePMTHandler::getCaIds() { - PyObject *ret=0; + ePyObject ret; - ePtr > ptr; + program prog; - if ( ((m_service && m_service->usePMT()) || !m_service) && !m_PMT.getCurrent(ptr)) + if ( !getProgramInfo(prog) ) { - uint16_t caids[255]; - memset(caids, 0, sizeof(caids)); - std::vector::const_iterator i = ptr->getSections().begin(); - for (; i != ptr->getSections().end(); ++i) - { - const ProgramMapSection &pmt = **i; - ElementaryStreamInfoConstIterator es = pmt.getEsInfo()->begin(); - for (; es != pmt.getEsInfo()->end(); ++es) - { - for (DescriptorConstIterator desc = (*es)->getDescriptors()->begin(); - desc != (*es)->getDescriptors()->end(); ++desc) - { - switch ((*desc)->getTag()) - { - case CA_DESCRIPTOR: - { - CaDescriptor *cadescr = *desc; - uint16_t caid = cadescr->getCaSystemId(); - int idx=0; - while (caids[idx] && caids[idx] != caid) - ++idx; - caids[idx]=caid; - break; - } - } - } - } - for (DescriptorConstIterator desc = pmt.getDescriptors()->begin(); - desc != pmt.getDescriptors()->end(); ++desc) - { - switch ((*desc)->getTag()) - { - case CA_DESCRIPTOR: - { - CaDescriptor *cadescr = *desc; - uint16_t caid = cadescr->getCaSystemId(); - int idx=0; - while (caids[idx] && caids[idx] != caid) - ++idx; - caids[idx]=caid; - break; - } - } - } - } - int cnt=0; - while (caids[cnt]) - ++cnt; + int cnt=prog.caids.size(); if (cnt) { ret=PyList_New(cnt); + std::set::iterator it(prog.caids.begin()); while(cnt--) - PyList_SET_ITEM(ret, cnt, PyInt_FromLong(caids[cnt])); + PyList_SET_ITEM(ret, cnt, PyInt_FromLong(*it++)); } } - if (!ret) - ret=PyList_New(0); - - return ret; + return ret ? (PyObject*)ret : (PyObject*)PyList_New(0); } int eDVBServicePMTHandler::getProgramInfo(struct program &program) { ePtr > ptr; + int cached_apid_ac3 = -1; + int cached_apid_mpeg = -1; + int cached_vpid = -1; + int cached_tpid = -1; + int ret = -1; program.videoStreams.clear(); program.audioStreams.clear(); program.pcrPid = -1; - program.isCrypted = false; program.pmtPid = -1; program.textPid = -1; + int first_ac3 = -1; + program.defaultAudioStream = 0; + + if ( m_service && !m_service->cacheEmpty() ) + { + cached_vpid = m_service->getCacheEntry(eDVBService::cVPID); + cached_apid_mpeg = m_service->getCacheEntry(eDVBService::cAPID); + cached_apid_ac3 = m_service->getCacheEntry(eDVBService::cAC3PID); + cached_tpid = m_service->getCacheEntry(eDVBService::cTPID); + } + if ( ((m_service && m_service->usePMT()) || !m_service) && !m_PMT.getCurrent(ptr)) { - int cached_apid_ac3 = -1; - int cached_apid_mpeg = -1; - int cached_vpid = -1; - int cached_tpid = -1; - if ( m_service && !m_service->cacheEmpty() ) + if (m_have_cached_program) { - cached_vpid = m_service->getCachePID(eDVBService::cVPID); - cached_apid_mpeg = m_service->getCachePID(eDVBService::cAC3PID); - cached_apid_ac3 = m_service->getCachePID(eDVBService::cAPID); - cached_tpid = m_service->getCachePID(eDVBService::cTPID); + program = m_cached_program; + ret = 0; } - eDVBTableSpec table_spec; - ptr->getSpec(table_spec); - program.pmtPid = table_spec.pid < 0x1fff ? table_spec.pid : -1; - std::vector::const_iterator i; - for (i = ptr->getSections().begin(); i != ptr->getSections().end(); ++i) + else { - const ProgramMapSection &pmt = **i; - program.pcrPid = pmt.getPcrPid(); - - ElementaryStreamInfoConstIterator es; - for (es = pmt.getEsInfo()->begin(); es != pmt.getEsInfo()->end(); ++es) + eDVBTableSpec table_spec; + ptr->getSpec(table_spec); + program.pmtPid = table_spec.pid < 0x1fff ? table_spec.pid : -1; + std::vector::const_iterator i; + for (i = ptr->getSections().begin(); i != ptr->getSections().end(); ++i) { - int isaudio = 0, isvideo = 0, cadescriptors = 0; - videoStream video; - audioStream audio; - audio.component_tag=-1; - video.component_tag=-1; - - video.pid = (*es)->getPid(); - audio.pid = (*es)->getPid(); + const ProgramMapSection &pmt = **i; + program.pcrPid = pmt.getPcrPid(); - switch ((*es)->getType()) + ElementaryStreamInfoConstIterator es; + for (es = pmt.getEsInfo()->begin(); es != pmt.getEsInfo()->end(); ++es) { - case 0x01: // MPEG 1 video - case 0x02: // MPEG 2 video - isvideo = 1; - //break; fall through !!! - case 0x03: // MPEG 1 audio - case 0x04: // MPEG 2 audio: - if (!isvideo) + int isaudio = 0, isvideo = 0, issubtitle = 0, forced_video = 0, forced_audio = 0; + videoStream video; + audioStream audio; + audio.component_tag=video.component_tag=-1; + video.type = videoStream::vtMPEG2; + audio.type = audioStream::atMPEG; + + switch ((*es)->getType()) { - isaudio = 1; - audio.type = audioStream::atMPEG; - } - //break; fall through !!! - case 0x06: // PES Private - /* PES private can contain AC-3, DTS or lots of other stuff. - check descriptors to get the exact type. */ - for (DescriptorConstIterator desc = (*es)->getDescriptors()->begin(); + case 0x1b: // AVC Video Stream (MPEG4 H264) + video.type = videoStream::vtMPEG4_H264; + isvideo = 1; + //break; fall through !!! + case 0x10: // MPEG 4 Part 2 + if (!isvideo) + { + video.type = videoStream::vtMPEG4_Part2; + isvideo = 1; + } + //break; fall through !!! + case 0x01: // MPEG 1 video + if (!isvideo) + video.type = videoStream::vtMPEG1; + //break; fall through !!! + case 0x02: // MPEG 2 video + isvideo = 1; + forced_video = 1; + //break; fall through !!! + case 0x03: // MPEG 1 audio + case 0x04: // MPEG 2 audio: + if (!isvideo) { + isaudio = 1; + forced_audio = 1; + } + //break; fall through !!! + case 0x0f: // MPEG 2 AAC + if (!isvideo && !isaudio) + { + isaudio = 1; + audio.type = audioStream::atAAC; + forced_audio = 1; + } + //break; fall through !!! + case 0x11: // MPEG 4 AAC + if (!isvideo && !isaudio) + { + isaudio = 1; + audio.type = audioStream::atAACHE; + forced_audio = 1; + } + case 0x80: // user private ... but blueray LPCM + if (!isvideo && !isaudio) + { + isaudio = 1; + audio.type = audioStream::atLPCM; + } + case 0x81: // user private ... but blueray AC3 + if (!isvideo && !isaudio) + { + isaudio = 1; + audio.type = audioStream::atAC3; + } + case 0x82: // Blueray DTS (dvb user private...) + case 0xA2: // Blueray secondary DTS + if (!isvideo && !isaudio) + { + isaudio = 1; + audio.type = audioStream::atDTS; + } + case 0x06: // PES Private + case 0xEA: // TS_PSI_ST_SMPTE_VC1 + for (DescriptorConstIterator desc = (*es)->getDescriptors()->begin(); desc != (*es)->getDescriptors()->end(); ++desc) - { - switch ((*desc)->getTag()) { - case TELETEXT_DESCRIPTOR: - if ( program.textPid == -1 || (*es)->getPid() == cached_tpid ) - program.textPid = (*es)->getPid(); - break; - case AC3_DESCRIPTOR: - if (!isvideo) + uint8_t tag = (*desc)->getTag(); + /* check descriptors to get the exakt stream type. */ + if (!forced_video && !forced_audio) { - isaudio = 1; - audio.type = audioStream::atAC3; + switch (tag) + { + case AUDIO_STREAM_DESCRIPTOR: + isaudio = 1; + break; + case VIDEO_STREAM_DESCRIPTOR: + { + isvideo = 1; + VideoStreamDescriptor *d = (VideoStreamDescriptor*)(*desc); + if (d->getMpeg1OnlyFlag()) + video.type = videoStream::vtMPEG1; + break; + } + case SUBTITLING_DESCRIPTOR: + { + SubtitlingDescriptor *d = (SubtitlingDescriptor*)(*desc); + const SubtitlingList *list = d->getSubtitlings(); + subtitleStream s; + s.pid = (*es)->getPid(); + for (SubtitlingConstIterator it(list->begin()); it != list->end(); ++it) + { + s.subtitling_type = (*it)->getSubtitlingType(); + switch(s.subtitling_type) + { + case 0x10 ... 0x13: + case 0x20 ... 0x23: // dvb subtitles + break; + default: + eDebug("dvb subtitle %s PID %04x with wrong subtitling type (%02x)... force 0x10!!", + s.language_code.c_str(), s.pid, s.subtitling_type); + s.subtitling_type = 0x10; + break; + } + s.composition_page_id = (*it)->getCompositionPageId(); + s.ancillary_page_id = (*it)->getAncillaryPageId(); + s.language_code = (*it)->getIso639LanguageCode(); +// eDebug("add dvb subtitle %s PID %04x, type %d, composition page %d, ancillary_page %d", +// s.language_code.c_str(), s.pid, s.subtitling_type, s.composition_page_id, s.ancillary_page_id); + issubtitle=1; + program.subtitleStreams.push_back(s); + } + break; + } + case TELETEXT_DESCRIPTOR: + if ( program.textPid == -1 || (*es)->getPid() == cached_tpid ) + { + subtitleStream s; + s.subtitling_type = 0x01; // EBU TELETEXT SUBTITLES + s.pid = program.textPid = (*es)->getPid(); + TeletextDescriptor *d = (TeletextDescriptor*)(*desc); + const VbiTeletextList *list = d->getVbiTeletexts(); + for (VbiTeletextConstIterator it(list->begin()); it != list->end(); ++it) + { + switch((*it)->getTeletextType()) + { + case 0x02: // Teletext subtitle page + case 0x05: // Teletext subtitle page for hearing impaired pepople + s.language_code = (*it)->getIso639LanguageCode(); + s.teletext_page_number = (*it)->getTeletextPageNumber(); + s.teletext_magazine_number = (*it)->getTeletextMagazineNumber(); +// eDebug("add teletext subtitle %s PID %04x, page number %d, magazine number %d", +// s.language_code.c_str(), s.pid, s.teletext_page_number, s.teletext_magazine_number); + program.subtitleStreams.push_back(s); + issubtitle=1; + default: + break; + } + } + } + break; + case DTS_DESCRIPTOR: + isaudio = 1; + audio.type = audioStream::atDTS; + break; + case 0x2B: // TS_PSI_DT_MPEG2_AAC + isaudio = 1; + audio.type = audioStream::atAAC; // MPEG2-AAC + break; + case 0x1C: // TS_PSI_DT_MPEG4_Audio + case AAC_DESCRIPTOR: + isaudio = 1; + audio.type = audioStream::atAACHE; // MPEG4-AAC + break; + case AC3_DESCRIPTOR: + isaudio = 1; + audio.type = audioStream::atAC3; + break; + case REGISTRATION_DESCRIPTOR: /* some services don't have a separate AC3 descriptor */ + { + RegistrationDescriptor *d = (RegistrationDescriptor*)(*desc); + switch (d->getFormatIdentifier()) + { + case 0x44545331 ... 0x44545333: // DTS1/DTS2/DTS3 + isaudio = 1; + audio.type = audioStream::atDTS; + break; + case 0x41432d33: // == 'AC-3' + isaudio = 1; + audio.type = audioStream::atAC3; + break; + case 0x42535344: // == 'BSSD' (LPCM) + isaudio = 1; + audio.type = audioStream::atLPCM; + break; + case 0x56432d31: // == 'VC-1' + { + const AdditionalIdentificationInfoVector *vec = d->getAdditionalIdentificationInfo(); + if (vec->size() > 1 && (*vec)[1] == 0x01) // subdescriptor tag + { + if ((*vec)[2] >= 0x90) // profile_level + video.type = videoStream::vtVC1; // advanced profile + else + video.type = videoStream::vtVC1_SM; // simple main + isvideo = 1; + } + } + default: + break; + } + break; + } + case 0x28: // TS_PSI_DT_AVC + isvideo = 1; + video.type = videoStream::vtMPEG4_H264; + break; + case 0x1B: // TS_PSI_DT_MPEG4_Video + isvideo = 1; + video.type = videoStream::vtMPEG4_Part2; + break; + default: + break; + } } - break; - case ISO_639_LANGUAGE_DESCRIPTOR: - if (!isvideo) + switch (tag) + { + case ISO_639_LANGUAGE_DESCRIPTOR: + if (!isvideo) + { + int cnt=0; + const Iso639LanguageList *languages = ((Iso639LanguageDescriptor*)*desc)->getIso639Languages(); + /* use last language code */ + for (Iso639LanguageConstIterator i(languages->begin()); i != languages->end(); ++i, ++cnt) + { + if (cnt == 0) + audio.language_code = (*i)->getIso639LanguageCode(); + else + audio.language_code += "/" + (*i)->getIso639LanguageCode(); + } + } + break; + case STREAM_IDENTIFIER_DESCRIPTOR: + audio.component_tag = + video.component_tag = + ((StreamIdentifierDescriptor*)*desc)->getComponentTag(); + break; + case CA_DESCRIPTOR: { - const Iso639LanguageList *languages = ((Iso639LanguageDescriptor*)*desc)->getIso639Languages(); - /* use last language code */ - for (Iso639LanguageConstIterator i(languages->begin()); i != languages->end(); ++i) - audio.language_code = (*i)->getIso639LanguageCode(); + CaDescriptor *descr = (CaDescriptor*)(*desc); + program.caids.insert(descr->getCaSystemId()); + break; + } + default: + break; } - break; - case STREAM_IDENTIFIER_DESCRIPTOR: - audio.component_tag = - video.component_tag = - ((StreamIdentifierDescriptor*)*desc)->getComponentTag(); - break; - case CA_DESCRIPTOR: - ++cadescriptors; - break; } + default: + break; } - break; - } - if (isaudio) - { - if ( !program.audioStreams.empty() && - ( audio.pid == cached_apid_ac3 || audio.pid == cached_apid_mpeg) ) + if (issubtitle && (isaudio || isvideo)) + eDebug("ambiguous streamtype for PID %04x detected.. forced as subtitle!", (*es)->getPid()); + else if (isaudio && isvideo) + eDebug("ambiguous streamtype for PID %04x detected.. forced as video!", (*es)->getPid()); + if (issubtitle) + continue; + else if (isvideo) { - program.audioStreams.push_back(program.audioStreams[0]); - program.audioStreams[0] = audio; + video.pid = (*es)->getPid(); + if ( !program.videoStreams.empty() && video.pid == cached_vpid ) + { + program.videoStreams.push_back(program.videoStreams[0]); + program.videoStreams[0] = video; + } + else + program.videoStreams.push_back(video); } - else - program.audioStreams.push_back(audio); - } - else if (isvideo) - { - if ( !program.videoStreams.empty() && video.pid == cached_vpid ) + else if (isaudio) { - program.videoStreams.push_back(program.videoStreams[0]); - program.videoStreams[0] = video; + audio.pid = (*es)->getPid(); + + /* if we find the cached pids, this will be our default stream */ + if (audio.pid == cached_apid_ac3 || audio.pid == cached_apid_mpeg) + program.defaultAudioStream = program.audioStreams.size(); + + /* also, we need to know the first non-mpeg (i.e. "ac3"/dts/...) stream */ + if ((audio.type != audioStream::atMPEG) && ((first_ac3 == -1) || (audio.pid == cached_apid_ac3))) + first_ac3 = program.audioStreams.size(); + + program.audioStreams.push_back(audio); } else - program.videoStreams.push_back(video); + continue; } - else - continue; - if ( cadescriptors > 0 ) - program.isCrypted=true; - } - if ( !program.isCrypted ) - { for (DescriptorConstIterator desc = pmt.getDescriptors()->begin(); desc != pmt.getDescriptors()->end(); ++desc) { - switch ((*desc)->getTag()) + if ((*desc)->getTag() == CA_DESCRIPTOR) { - case CA_DESCRIPTOR: - program.isCrypted=true; - break; + CaDescriptor *descr = (CaDescriptor*)(*desc); + program.caids.insert(descr->getCaSystemId()); } } - break; } + ret = 0; + + /* finally some fixup: if our default audio stream is an MPEG audio stream, + and we have 'defaultac3' set, use the first available ac3 stream instead. + (note: if an ac3 audio stream was selected before, this will be also stored + in 'fisrt_ac3', so we don't need to worry. */ + bool defaultac3 = false; + std::string default_ac3; + + if (!ePythonConfigQuery::getConfigValue("config.av.defaultac3", default_ac3)) + defaultac3 = default_ac3 == "True"; + + if (defaultac3 && (first_ac3 != -1)) + program.defaultAudioStream = first_ac3; + + m_cached_program = program; + m_have_cached_program = true; } - return 0; } else if ( m_service && !m_service->cacheEmpty() ) { - int vpid = m_service->getCachePID(eDVBService::cVPID), - apid_ac3 = m_service->getCachePID(eDVBService::cAC3PID), - apid_mpeg = m_service->getCachePID(eDVBService::cAPID), - pcrpid = m_service->getCachePID(eDVBService::cPCRPID), - tpid = m_service->getCachePID(eDVBService::cTPID), + int cached_pcrpid = m_service->getCacheEntry(eDVBService::cPCRPID), + vpidtype = m_service->getCacheEntry(eDVBService::cVTYPE), cnt=0; - if ( vpid != -1 ) + if ( vpidtype == -1 ) + vpidtype = videoStream::vtMPEG2; + if ( cached_vpid != -1 ) { videoStream s; - s.pid = vpid; + s.pid = cached_vpid; + s.type = vpidtype; program.videoStreams.push_back(s); ++cnt; } - if ( apid_ac3 != -1 ) + if ( cached_apid_ac3 != -1 ) { audioStream s; s.type = audioStream::atAC3; - s.pid = apid_ac3; + s.pid = cached_apid_ac3; program.audioStreams.push_back(s); ++cnt; } - if ( apid_mpeg != -1 ) + if ( cached_apid_mpeg != -1 ) { audioStream s; s.type = audioStream::atMPEG; - s.pid = apid_mpeg; + s.pid = cached_apid_mpeg; program.audioStreams.push_back(s); ++cnt; } - if ( pcrpid != -1 ) + if ( cached_pcrpid != -1 ) { ++cnt; - program.pcrPid = pcrpid; + program.pcrPid = cached_pcrpid; } - if ( tpid != -1 ) + if ( cached_tpid != -1 ) { ++cnt; - program.textPid = tpid; + program.textPid = cached_tpid; } + CAID_LIST &caids = m_service->m_ca; + for (CAID_LIST::iterator it(caids.begin()); it != caids.end(); ++it) + program.caids.insert(*it); if ( cnt ) - return 0; + ret = 0; } - return -1; + return ret; } int eDVBServicePMTHandler::getChannel(eUsePtr &channel) @@ -440,25 +619,66 @@ int eDVBServicePMTHandler::getPVRChannel(ePtr &pvr_channel) return -1; } -int eDVBServicePMTHandler::tune(eServiceReferenceDVB &ref, int use_decode_demux, eCueSheet *cue) +void eDVBServicePMTHandler::SDTScanEvent(int event) +{ + switch (event) + { + case eDVBScan::evtFinish: + { + ePtr db; + if (m_resourceManager->getChannelList(db) != 0) + eDebug("no channel list"); + else + { + eDVBChannelID chid; + m_reference.getChannelID(chid); + if (chid == m_dvb_scan->getCurrentChannelID()) + { + m_dvb_scan->insertInto(db, true); + eDebug("sdt update done!"); + } + else + eDebug("ignore sdt update data.... incorrect transponder tuned!!!"); + } + break; + } + + default: + break; + } +} + +int eDVBServicePMTHandler::tune(eServiceReferenceDVB &ref, int use_decode_demux, eCueSheet *cue, bool simulate, eDVBService *service) { - RESULT res; + RESULT res=0; m_reference = ref; m_use_decode_demux = use_decode_demux; + + /* use given service as backup. This is used for timeshift where we want to clone the live stream using the cache, but in fact have a PVR channel */ + m_service = service; /* is this a normal (non PVR) channel? */ if (ref.path.empty()) { eDVBChannelID chid; ref.getChannelID(chid); - res = m_resourceManager->allocateChannel(chid, m_channel); - eDebug("allocate Channel: res %d", res); - } else + res = m_resourceManager->allocateChannel(chid, m_channel, simulate); + if (!simulate) + eDebug("allocate Channel: res %d", res); + + ePtr db; + if (!m_resourceManager->getChannelList(db)) + db->getService((eServiceReferenceDVB&)m_reference, m_service); + + if (!res && !simulate) + eDVBCIInterfaces::getInstance()->addPMTHandler(this); + } else if (!simulate) // no simulation of playback services { eDVBMetaParser parser; - - if (parser.parseFile(ref.path)) + + int ret=parser.parseFile(ref.path); + if (ret || !parser.m_ref.getServiceID().get() /* incorrect sid in meta file or recordings.epl*/ ) { eWarning("no .meta file found, trying to find PMT pid"); eDVBTSTools tstools; @@ -485,31 +705,40 @@ int eDVBServicePMTHandler::tune(eServiceReferenceDVB &ref, int use_decode_demux, m_channel = m_pvr_channel; } - ePtr db; - if (!m_resourceManager->getChannelList(db)) - db->getService((eServiceReferenceDVB&)m_reference, m_service); - - if (m_channel) + if (!simulate) { - m_channel->connectStateChange( - slot(*this, &eDVBServicePMTHandler::channelStateChanged), - m_channelStateChanged_connection); - m_last_channel_state = -1; - channelStateChanged(m_channel); + if (m_channel) + { + m_channel->connectStateChange( + slot(*this, &eDVBServicePMTHandler::channelStateChanged), + m_channelStateChanged_connection); + m_last_channel_state = -1; + channelStateChanged(m_channel); + + m_channel->connectEvent( + slot(*this, &eDVBServicePMTHandler::channelEvent), + m_channelEvent_connection); - m_channel->connectEvent( - slot(*this, &eDVBServicePMTHandler::channelEvent), - m_channelEvent_connection); - } else - { - serviceEvent(eventTuneFailed); - return res; - } + if (ref.path.empty()) + { + m_dvb_scan = 0; + m_dvb_scan = new eDVBScan(m_channel, true, false); + m_dvb_scan->connectEvent(slot(*this, &eDVBServicePMTHandler::SDTScanEvent), m_scan_event_connection); + } + } else + { + if (res == eDVBResourceManager::errAllSourcesBusy) + serviceEvent(eventNoResources); + else /* errChidNotFound, errNoChannelList, errChannelNotInList, errNoSourceFound */ + serviceEvent(eventMisconfiguration); + return res; + } - if (m_pvr_channel) - { - m_pvr_channel->setCueSheet(cue); - m_pvr_channel->playFile(ref.path.c_str()); + if (m_pvr_channel) + { + m_pvr_channel->setCueSheet(cue); + m_pvr_channel->playFile(ref.path.c_str()); + } } return res; @@ -517,6 +746,8 @@ int eDVBServicePMTHandler::tune(eServiceReferenceDVB &ref, int use_decode_demux, void eDVBServicePMTHandler::free() { + m_dvb_scan = 0; + if (m_ca_servicePtr) { int demuxes[2] = {0,0}; @@ -530,15 +761,18 @@ void eDVBServicePMTHandler::free() ePtr > ptr; m_PMT.getCurrent(ptr); eDVBCAService::unregister_service(m_reference, demuxes, ptr); - eDVBCIInterfaces::getInstance()->removePMTHandler(this); m_ca_servicePtr = 0; } + if (m_channel) + eDVBCIInterfaces::getInstance()->removePMTHandler(this); + if (m_pvr_channel) { m_pvr_channel->stopFile(); m_pvr_channel->setCueSheet(0); } + m_PMT.stop(); m_PAT.stop(); m_service = 0; @@ -547,13 +781,15 @@ void eDVBServicePMTHandler::free() m_demux = 0; } -std::map eDVBCAService::exist; +CAServiceMap eDVBCAService::exist; +ChannelMap eDVBCAService::exist_channels; +ePtr eDVBCAService::m_chanAddedConn; eDVBCAService::eDVBCAService() - :m_prev_build_hash(0), m_sendstate(0), m_retryTimer(eApp) + :m_buffer(512), m_prev_build_hash(0), m_sendstate(0), m_retryTimer(eTimer::create(eApp)) { memset(m_used_demux, 0xFF, sizeof(m_used_demux)); - CONNECT(m_retryTimer.timeout, eDVBCAService::sendCAPMT); + CONNECT(m_retryTimer->timeout, eDVBCAService::sendCAPMT); Connect(); } @@ -563,6 +799,7 @@ eDVBCAService::~eDVBCAService() ::close(m_sock); } +// begin static methods RESULT eDVBCAService::register_service( const eServiceReferenceDVB &ref, int demux_nums[2], eDVBCAService *&caservice ) { CAServiceMap::iterator it = exist.find(ref); @@ -653,17 +890,210 @@ RESULT eDVBCAService::unregister_service( const eServiceReferenceDVB &ref, int d return 0; } +void eDVBCAService::registerChannelCallback(eDVBResourceManager *res_mgr) +{ + res_mgr->connectChannelAdded(slot(&DVBChannelAdded), m_chanAddedConn); +} + +void eDVBCAService::DVBChannelAdded(eDVBChannel *chan) +{ + if ( chan ) + { + eDebug("[eDVBCAService] new channel %p!", chan); + channel_data *data = new channel_data(); + data->m_channel = chan; + data->m_prevChannelState = -1; + data->m_dataDemux = -1; + exist_channels[chan] = data; + chan->connectStateChange(slot(&DVBChannelStateChanged), data->m_stateChangedConn); + } +} + +void eDVBCAService::DVBChannelStateChanged(iDVBChannel *chan) +{ + ChannelMap::iterator it = + exist_channels.find(chan); + if ( it != exist_channels.end() ) + { + int state=0; + chan->getState(state); + if ( it->second->m_prevChannelState != state ) + { + switch (state) + { + case iDVBChannel::state_ok: + { + eDebug("[eDVBCAService] channel %p running", chan); + break; + } + case iDVBChannel::state_release: + { + eDebug("[eDVBCAService] remove channel %p", chan); + unsigned char msg[8] = { 0x9f,0x80,0x3f,0x04,0x83,0x02,0x00,0x00 }; + msg[7] = it->second->m_dataDemux & 0xFF; + int sock, clilen; + struct sockaddr_un servaddr; + memset(&servaddr, 0, sizeof(struct sockaddr_un)); + servaddr.sun_family = AF_UNIX; + strcpy(servaddr.sun_path, "/tmp/camd.socket"); + clilen = sizeof(servaddr.sun_family) + strlen(servaddr.sun_path); + sock = socket(PF_UNIX, SOCK_STREAM, 0); + if (sock > -1) + { + connect(sock, (struct sockaddr *) &servaddr, clilen); + fcntl(sock, F_SETFL, O_NONBLOCK); + int val=1; + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, 4); + if (write(sock, msg, 8) != 8) + eDebug("[eDVBCAService] write leave transponder failed!!"); + close(sock); + } + exist_channels.erase(it); + delete it->second; + it->second=0; + break; + } + default: // ignore all other events + return; + } + if (it->second) + it->second->m_prevChannelState = state; + } + } +} + +channel_data *eDVBCAService::getChannelData(eDVBChannelID &chid) +{ + for (ChannelMap::iterator it(exist_channels.begin()); it != exist_channels.end(); ++it) + { + if (chid == it->second->m_channel->getChannelID()) + return it->second; + } + return 0; +} +// end static methods + +#define CA_REPLY_DEBUG +#define MAX_LENGTH_BYTES 4 +#define MIN_LENGTH_BYTES 1 + +void eDVBCAService::socketCB(int what) +{ + if (what & (eSocketNotifier::Read | eSocketNotifier::Priority)) + { + char msgbuffer[4096]; + ssize_t length = read(m_sock, msgbuffer, sizeof(msgbuffer)); + if (length == -1) + { + if (errno != EAGAIN && errno != EINTR && errno != EBUSY) + { + eDebug("[eSocketMMIHandler] read (%m)"); + what |= eSocketNotifier::Error; + } + } else if (length == 0) + { + what |= eSocketNotifier::Hungup; + } else + { + int len = length; + unsigned char *data = (unsigned char*)msgbuffer; + int clear = 1; + // If a new message starts, then the previous message + // should already have been processed. Otherwise the + // previous message was incomplete and should therefore + // be deleted. + if ((len >= 1) && ((data[0] & 0xFF) != 0x9f)) + clear = 0; + if ((len >= 2) && ((data[1] & 0x80) != 0x80)) + clear = 0; + if ((len >= 3) && ((data[2] & 0x80) != 0x00)) + clear = 0; + if (clear) + { + m_buffer.clear(); +#ifdef CA_REPLY_DEBUG + eDebug("clear buffer"); +#endif + } +#ifdef CA_REPLY_DEBUG + eDebug("Put to buffer:"); + for (int i=0; i < len; ++i) + eDebugNoNewLine("%02x ", data[i]); + eDebug("\n--------"); +#endif + m_buffer.write( data, len ); + + while ( m_buffer.size() >= (3 + MIN_LENGTH_BYTES) ) + { + unsigned char tmp[3+MAX_LENGTH_BYTES]; + m_buffer.peek(tmp, 3+MIN_LENGTH_BYTES); + if (((tmp[0] & 0xFF) != 0x9f) || ((tmp[1] & 0x80) != 0x80) || ((tmp[2] & 0x80) != 0x00)) + { + m_buffer.skip(1); +#ifdef CA_REPLY_DEBUG + eDebug("skip %02x", tmp[0]); +#endif + continue; + } + if (tmp[3] & 0x80) + { + int peekLength = (tmp[3] & 0x7f) + 4; + if (m_buffer.size() < peekLength) + continue; + m_buffer.peek(tmp, peekLength); + } + int size=0; + int LengthBytes=eDVBCISession::parseLengthField(tmp+3, size); + int messageLength = 3+LengthBytes+size; + if ( m_buffer.size() >= messageLength ) + { + unsigned char dest[messageLength]; + m_buffer.read(dest, messageLength); +#ifdef CA_REPLY_DEBUG + eDebug("dump ca reply:"); + for (int i=0; i < messageLength; ++i) + eDebugNoNewLine("%02x ", dest[i]); + eDebug("\n--------"); +#endif +// /*emit*/ mmi_progress(0, dest, (const void*)(dest+3+LengthBytes), messageLength-3-LengthBytes); + } + } + } + } + if (what & eSocketNotifier::Hungup) { + /*eDebug("[eDVBCAService] connection closed")*/; + m_sendstate=1; + sendCAPMT(); + } + if (what & eSocketNotifier::Error) + eDebug("[eDVBCAService] connection error"); +} + void eDVBCAService::Connect() { + m_sn=0; memset(&m_servaddr, 0, sizeof(struct sockaddr_un)); m_servaddr.sun_family = AF_UNIX; strcpy(m_servaddr.sun_path, "/tmp/camd.socket"); m_clilen = sizeof(m_servaddr.sun_family) + strlen(m_servaddr.sun_path); m_sock = socket(PF_UNIX, SOCK_STREAM, 0); - connect(m_sock, (struct sockaddr *) &m_servaddr, m_clilen); - fcntl(m_sock, F_SETFL, O_NONBLOCK); - int val=1; - setsockopt(m_sock, SOL_SOCKET, SO_REUSEADDR, &val, 4); + if (m_sock != -1) + { + if (!connect(m_sock, (struct sockaddr *) &m_servaddr, m_clilen)) + { + int val=1; + fcntl(m_sock, F_SETFL, O_NONBLOCK); + setsockopt(m_sock, SOL_SOCKET, SO_REUSEADDR, &val, 4); + m_sn = eSocketNotifier::create(eApp, m_sock, + eSocketNotifier::Read|eSocketNotifier::Priority|eSocketNotifier::Error|eSocketNotifier::Hungup); + CONNECT(m_sn->activated, eDVBCAService::socketCB); + + } +// else +// eDebug("[eDVBCAService] connect failed %m"); + } + else + eDebug("[eDVBCAService] create socket failed %m"); } void eDVBCAService::buildCAPMT(eTable *ptr) @@ -700,7 +1130,7 @@ void eDVBCAService::buildCAPMT(eTable *ptr) eDebug("demux %d mask %02x prevhash %08x", data_demux, demux_mask, m_prev_build_hash); - unsigned int build_hash = (pmtpid << 16); + unsigned int build_hash = ( pmtpid << 16); build_hash |= (demux_mask << 8); build_hash |= (pmt_version&0xFF); @@ -786,6 +1216,14 @@ void eDVBCAService::sendCAPMT() { m_sendstate=0xFFFFFFFF; eDebug("[eDVBCAService] send %d bytes",wp); + eDVBChannelID chid; + m_service.getChannelID(chid); + channel_data *data = getChannelData(chid); + if (data) + { + int lenbytes = m_capmt[3] & 0x80 ? m_capmt[3] & ~0x80 : 0; + data->m_dataDemux = m_capmt[24+lenbytes]; + } #if 1 for(int i=0;istart(0,true); // eDebug("[eDVBCAService] send failed .. immediate retry"); break; default: - m_retryTimer.start(5000,true); + m_retryTimer->start(5000,true); // eDebug("[eDVBCAService] send failed .. retry in 5 sec"); break; } ++m_sendstate; } } + +static PyObject *createTuple(int pid, const char *type) +{ + PyObject *r = PyTuple_New(2); + PyTuple_SET_ITEM(r, 0, PyInt_FromLong(pid)); + PyTuple_SET_ITEM(r, 1, PyString_FromString(type)); + return r; +} + +static inline void PyList_AppendSteal(PyObject *list, PyObject *item) +{ + PyList_Append(list, item); + Py_DECREF(item); +} + +extern void PutToDict(ePyObject &dict, const char*key, ePyObject item); // defined in dvb/frontend.cpp + +PyObject *eDVBServicePMTHandler::program::createPythonObject() +{ + ePyObject r = PyDict_New(); + ePyObject l = PyList_New(0); + + PyList_AppendSteal(l, createTuple(0, "pat")); + + if (pmtPid != -1) + PyList_AppendSteal(l, createTuple(pmtPid, "pmt")); + + for (std::vector::const_iterator + i(videoStreams.begin()); + i != videoStreams.end(); ++i) + PyList_AppendSteal(l, createTuple(i->pid, "video")); + + for (std::vector::const_iterator + i(audioStreams.begin()); + i != audioStreams.end(); ++i) + PyList_AppendSteal(l, createTuple(i->pid, "audio")); + + for (std::vector::const_iterator + i(subtitleStreams.begin()); + i != subtitleStreams.end(); ++i) + PyList_AppendSteal(l, createTuple(i->pid, "subtitle")); + + PyList_AppendSteal(l, createTuple(pcrPid, "pcr")); + + if (textPid != -1) + PyList_AppendSteal(l, createTuple(textPid, "text")); + + PutToDict(r, "pids", l); + + return r; +}