X-Git-Url: https://git.cweiske.de/enigma2.git/blobdiff_plain/0af11516cabc973907890f548925a66313c8d18c..cc8dceba41cdc3f1cb5a16c86bd3676101233c00:/lib/dvb/pmt.cpp diff --git a/lib/dvb/pmt.cpp b/lib/dvb/pmt.cpp index e62de9b8..4047be5c 100644 --- a/lib/dvb/pmt.cpp +++ b/lib/dvb/pmt.cpp @@ -1,16 +1,36 @@ - #include +#include #include #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // access to python config eDVBServicePMTHandler::eDVBServicePMTHandler() + :m_ca_servicePtr(0), m_dvb_scan(0), m_decode_demux_num(0xFF) { + m_use_decode_demux = 0; + m_pmt_pid = -1; eDVBResourceManager::getInstance(m_resourceManager); CONNECT(m_PMT.tableReady, eDVBServicePMTHandler::PMTready); CONNECT(m_PAT.tableReady, eDVBServicePMTHandler::PATready); } +eDVBServicePMTHandler::~eDVBServicePMTHandler() +{ + free(); +} + void eDVBServicePMTHandler::channelStateChanged(iDVBChannel *channel) { int state; @@ -20,8 +40,8 @@ void eDVBServicePMTHandler::channelStateChanged(iDVBChannel *channel) && (state == iDVBChannel::state_ok) && (!m_demux)) { if (m_channel) - if (m_channel->getDemux(m_demux)) - eDebug("shit it failed.. again."); + if (m_channel->getDemux(m_demux, (!m_use_decode_demux) ? 0 : iDVBChannel::capDecode)) + eDebug("Allocating %s-decoding a demux for now tuned-in channel failed.", m_use_decode_demux ? "" : "non-"); serviceEvent(eventTuned); @@ -29,13 +49,34 @@ void eDVBServicePMTHandler::channelStateChanged(iDVBChannel *channel) { eDebug("ok ... now we start!!"); - /* emit */ m_resourceManager->m_channelRunning(channel); - - m_PAT.begin(eApp, eDVBPATSpec(), m_demux); + 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); } + } else if ((m_last_channel_state != iDVBChannel::state_failed) && + (state == iDVBChannel::state_failed)) + { + eDebug("tune failed."); + serviceEvent(eventTuneFailed); + } +} + +void eDVBServicePMTHandler::channelEvent(iDVBChannel *channel, int event) +{ + switch (event) + { + case iDVBChannel::evtEOF: + serviceEvent(eventEOF); + break; + case iDVBChannel::evtSOF: + serviceEvent(eventSOF); + break; + default: + break; } } @@ -44,20 +85,48 @@ void eDVBServicePMTHandler::PMTready(int error) if (error) serviceEvent(eventNoPMT); else + { + m_have_cached_program = false; serviceEvent(eventNewProgramInfo); + if (!m_pvr_channel) // 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; + m_demux->getCADemuxID(tmp); + demuxes[0]=tmp; + if (m_decode_demux_num != 0xFF) + demuxes[1]=m_decode_demux_num; + else + demuxes[1]=demuxes[0]; + eDVBCAService::register_service(m_reference, demuxes, m_ca_servicePtr); + eDVBCIInterfaces::getInstance()->recheckPMTHandlers(); + } + eDVBCIInterfaces::getInstance()->gotPMT(this); + } + if (m_ca_servicePtr) + { + ePtr > ptr; + if (!m_PMT.getCurrent(ptr)) + m_ca_servicePtr->buildCAPMT(ptr); + else + eDebug("eDVBServicePMTHandler cannot call buildCAPMT"); + } + } } void eDVBServicePMTHandler::PATready(int) { - eDebug("got PAT"); - ePtr > ptr; + ePtr > ptr; if (!m_PAT.getCurrent(ptr)) { int pmtpid = -1; - ProgramAssociationTableConstIterator i; + std::vector::const_iterator i; for (i = ptr->getSections().begin(); i != ptr->getSections().end(); ++i) { - const ProgramAssociationTable &pat = **i; + const ProgramAssociationSection &pat = **i; ProgramAssociationConstIterator program; for (program = pat.getPrograms()->begin(); program != pat.getPrograms()->end(); ++program) if (eServiceID((*program)->getProgramNumber()) == m_reference.getServiceID()) @@ -71,95 +140,331 @@ void eDVBServicePMTHandler::PATready(int) serviceEvent(eventNoPAT); } +PyObject *eDVBServicePMTHandler::getCaIds() +{ + ePyObject ret; + + program prog; + + if ( !getProgramInfo(prog) ) + { + 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(*it++)); + } + } + + return ret ? (PyObject*)ret : (PyObject*)PyList_New(0); +} + int eDVBServicePMTHandler::getProgramInfo(struct program &program) { - eDebug("got PMT"); - ePtr > ptr; + 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.pmtPid = -1; + program.textPid = -1; - if (!m_PMT.getCurrent(ptr)) + int first_ac3 = -1; + program.defaultAudioStream = 0; + + if ( m_service && !m_service->cacheEmpty() ) { - ProgramMapTableConstIterator i; - for (i = ptr->getSections().begin(); i != ptr->getSections().end(); ++i) + 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)) + { + if (m_have_cached_program) { - const ProgramMapTable &pmt = **i; - program.pcrPid = pmt.getPcrPid(); - - ElementaryStreamInfoConstIterator es; - for (es = pmt.getEsInfo()->begin(); es != pmt.getEsInfo()->end(); ++es) + program = m_cached_program; + ret = 0; + } + else + { + 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; - videoStream video; - audioStream audio; - - video.pid = (*es)->getPid(); - audio.pid = (*es)->getPid(); - - switch ((*es)->getType()) + const ProgramMapSection &pmt = **i; + program.pcrPid = pmt.getPcrPid(); + + 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; - case 0x03: // MPEG 1 audio - case 0x04: // MPEG 2 audio: - isaudio = 1; - audio.type = audioStream::atMPEG; - break; + int isaudio = 0, isvideo = 0; + videoStream video; + audioStream audio; + audio.component_tag=video.component_tag=-1; + video.type = videoStream::vtMPEG2; + + switch ((*es)->getType()) + { + case 0x1b: // AVC Video Stream (MPEG4 H264) + video.type = videoStream::vtMPEG4_H264; + 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) + { + isaudio = 1; + audio.type = audioStream::atMPEG; + } + //break; fall through !!! + case 0x06: // PES Private + case 0x81: // user private + for (DescriptorConstIterator desc = (*es)->getDescriptors()->begin(); + desc != (*es)->getDescriptors()->end(); ++desc) + { + uint8_t tag = (*desc)->getTag(); + if (!isaudio && !isvideo) + { + /* PES private can contain AC-3, DTS or lots of other stuff. + check descriptors to get the exakt type. */ + switch (tag) + { + 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(); + 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); + 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); + break; + } + } + } + break; + case DTS_DESCRIPTOR: + isaudio = 1; + audio.type = audioStream::atDTS; + break; + case AAC_DESCRIPTOR: + isaudio = 1; + audio.type = audioStream::atAAC; + break; + case AC3_DESCRIPTOR: + isaudio = 1; + audio.type = audioStream::atAC3; + break; + case REGISTRATION_DESCRIPTOR: /* some services don't have a separate AC3 descriptor */ + { + /* libdvbsi++ doesn't yet support this descriptor type, so work around. */ + if ((*desc)->getLength() != 4) + break; + unsigned char descr[6]; + (*desc)->writeToBuffer(descr); + int format_identifier = (descr[2] << 24) | (descr[3] << 16) | (descr[4] << 8) | (descr[5]); + switch (format_identifier) + { + case 0x41432d33: + isaudio = 1; + audio.type = audioStream::atAC3; + break; + default: + break; + } + break; + } + default: + break; + } + } + switch (tag) + { + case ISO_639_LANGUAGE_DESCRIPTOR: + if (!isvideo) + { + const Iso639LanguageList *languages = ((Iso639LanguageDescriptor*)*desc)->getIso639Languages(); + /* use last language code */ + for (Iso639LanguageConstIterator i(languages->begin()); i != languages->end(); ++i) + audio.language_code = (*i)->getIso639LanguageCode(); + } + break; + case STREAM_IDENTIFIER_DESCRIPTOR: + audio.component_tag = + video.component_tag = + ((StreamIdentifierDescriptor*)*desc)->getComponentTag(); + break; + case CA_DESCRIPTOR: + { + CaDescriptor *descr = (CaDescriptor*)(*desc); + program.caids.insert(descr->getCaSystemId()); + break; + } + default: + break; + } + } + break; + } + if (isaudio) + { + 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 if (isvideo) + { + 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 + continue; + } + for (DescriptorConstIterator desc = pmt.getDescriptors()->begin(); + desc != pmt.getDescriptors()->end(); ++desc) + { + if ((*desc)->getTag() == CA_DESCRIPTOR) + { + CaDescriptor *descr = (CaDescriptor*)(*desc); + program.caids.insert(descr->getCaSystemId()); + } } - if (isaudio) - program.audioStreams.push_back(audio); - if (isvideo) - program.videoStreams.push_back(video); } + 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() ) + } else if ( m_service && !m_service->cacheEmpty() ) { - int vpid = m_service->getCachePID(eDVBService::cVPID), - apid_ac3 = m_service->getCachePID(eDVBService::cAPID), - apid_mpeg = m_service->getCachePID(eDVBService::cAC3PID), - pcrpid = m_service->getCachePID(eDVBService::cPCRPID), + 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 = cached_pcrpid; + } + if ( cached_tpid != -1 ) { ++cnt; - program.pcrPid = pcrpid; + 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::getDemux(ePtr &demux) +int eDVBServicePMTHandler::getChannel(eUsePtr &channel) +{ + channel = m_channel; + if (channel) + return 0; + else + return -1; +} + +int eDVBServicePMTHandler::getDataDemux(ePtr &demux) { demux = m_demux; if (demux) @@ -168,6 +473,26 @@ int eDVBServicePMTHandler::getDemux(ePtr &demux) return -1; } +int eDVBServicePMTHandler::getDecodeDemux(ePtr &demux) +{ + int ret=0; + /* if we're using the decoding demux as data source + (for example in pvr playbacks), return that one. */ + if (m_use_decode_demux) + { + demux = m_demux; + return ret; + } + + ASSERT(m_channel); /* calling without a previous ::tune is certainly bad. */ + + ret = m_channel->getDemux(demux, iDVBChannel::capDecode); + if (!ret) + demux->getCADemuxID(m_decode_demux_num); + + return ret; +} + int eDVBServicePMTHandler::getPVRChannel(ePtr &pvr_channel) { pvr_channel = m_pvr_channel; @@ -177,12 +502,34 @@ int eDVBServicePMTHandler::getPVRChannel(ePtr &pvr_channel) return -1; } -int eDVBServicePMTHandler::tune(eServiceReferenceDVB &ref) +void eDVBServicePMTHandler::SDTScanEvent(int event) +{ + switch (event) + { + case eDVBScan::evtFinish: + { + ePtr db; + if (m_resourceManager->getChannelList(db) != 0) + eDebug("no channel list"); + else + { + m_dvb_scan->insertInto(db, true); + eDebug("sdt update done!"); + } + break; + } + + default: + break; + } +} + +int eDVBServicePMTHandler::tune(eServiceReferenceDVB &ref, int use_decode_demux, eCueSheet *cue) { RESULT res; m_reference = ref; -// ref.path = "/viva.ts"; // hrhr. + m_use_decode_demux = use_decode_demux; /* is this a normal (non PVR) channel? */ if (ref.path.empty()) @@ -190,13 +537,36 @@ int eDVBServicePMTHandler::tune(eServiceReferenceDVB &ref) eDVBChannelID chid; ref.getChannelID(chid); res = m_resourceManager->allocateChannel(chid, m_channel); + eDebug("allocate Channel: res %d", res); + + ePtr db; + if (!m_resourceManager->getChannelList(db)) + db->getService((eServiceReferenceDVB&)m_reference, m_service); + + if (!res) + eDVBCIInterfaces::getInstance()->addPMTHandler(this); } else { eDVBMetaParser parser; - - if (parser.parseFile(ref.path)) - eWarning("no .meta file found, trying original service ref."); - else + + 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; + if (tstools.openFile(ref.path.c_str())) + eWarning("failed to open file"); + else + { + int service_id, pmt_pid; + if (!tstools.findPMT(pmt_pid, service_id)) + { + eDebug("PMT pid found on pid %04x, service id %d", pmt_pid, service_id); + m_reference.setServiceID(service_id); + m_pmt_pid = pmt_pid; + } + } + } else m_reference = parser.m_ref; eDebug("alloc PVR"); @@ -206,7 +576,7 @@ int eDVBServicePMTHandler::tune(eServiceReferenceDVB &ref) eDebug("allocatePVRChannel failed!\n"); m_channel = m_pvr_channel; } - + if (m_channel) { m_channel->connectStateChange( @@ -214,14 +584,507 @@ int eDVBServicePMTHandler::tune(eServiceReferenceDVB &ref) m_channelStateChanged_connection); m_last_channel_state = -1; channelStateChanged(m_channel); + + m_channel->connectEvent( + slot(*this, &eDVBServicePMTHandler::channelEvent), + m_channelEvent_connection); + + if (ref.path.empty()) + { + delete m_dvb_scan; + 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()); - - ePtr db; - if (!m_resourceManager->getChannelList(db)) - db->getService((eServiceReferenceDVB&)m_reference, m_service); + } return res; } + +void eDVBServicePMTHandler::free() +{ + m_dvb_scan = 0; + delete m_dvb_scan; + + if (m_ca_servicePtr) + { + int demuxes[2] = {0,0}; + uint8_t tmp; + m_demux->getCADemuxID(tmp); + demuxes[0]=tmp; + if (m_decode_demux_num != 0xFF) + demuxes[1]=m_decode_demux_num; + else + demuxes[1]=demuxes[0]; + ePtr > ptr; + m_PMT.getCurrent(ptr); + eDVBCAService::unregister_service(m_reference, demuxes, ptr); + 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; + m_channel = 0; + m_pvr_channel = 0; + m_demux = 0; +} + +CAServiceMap eDVBCAService::exist; +ChannelMap eDVBCAService::exist_channels; +ePtr eDVBCAService::m_chanAddedConn; + +eDVBCAService::eDVBCAService() + :m_sn(0), m_prev_build_hash(0), m_sendstate(0), m_retryTimer(eApp) +{ + memset(m_used_demux, 0xFF, sizeof(m_used_demux)); + CONNECT(m_retryTimer.timeout, eDVBCAService::sendCAPMT); + Connect(); +} + +eDVBCAService::~eDVBCAService() +{ + eDebug("[eDVBCAService] free service %s", m_service.toString().c_str()); + ::close(m_sock); + delete m_sn; +} + +// begin static methods +RESULT eDVBCAService::register_service( const eServiceReferenceDVB &ref, int demux_nums[2], eDVBCAService *&caservice ) +{ + CAServiceMap::iterator it = exist.find(ref); + if ( it != exist.end() ) + caservice = it->second; + else + { + caservice = (exist[ref]=new eDVBCAService()); + caservice->m_service = ref; + eDebug("[eDVBCAService] new service %s", ref.toString().c_str() ); + } + + int loops = demux_nums[0] != demux_nums[1] ? 2 : 1; + for (int i=0; i < loops; ++i) + { +// search free demux entry + int iter=0, max_demux_slots = sizeof(caservice->m_used_demux); + + while ( iter < max_demux_slots && caservice->m_used_demux[iter] != 0xFF ) + ++iter; + + if ( iter < max_demux_slots ) + { + caservice->m_used_demux[iter] = demux_nums[i] & 0xFF; + eDebug("[eDVBCAService] add demux %d to slot %d service %s", caservice->m_used_demux[iter], iter, ref.toString().c_str()); + } + else + { + eDebug("[eDVBCAService] no more demux slots free for service %s!!", ref.toString().c_str()); + return -1; + } + } + return 0; +} + +RESULT eDVBCAService::unregister_service( const eServiceReferenceDVB &ref, int demux_nums[2], eTable *ptr ) +{ + CAServiceMap::iterator it = exist.find(ref); + if ( it == exist.end() ) + { + eDebug("[eDVBCAService] try to unregister non registered %s", ref.toString().c_str()); + return -1; + } + else + { + eDVBCAService *caservice = it->second; + int loops = demux_nums[0] != demux_nums[1] ? 2 : 1; + for (int i=0; i < loops; ++i) + { + bool freed = false; + int iter = 0, + used_demux_slots = 0, + max_demux_slots = sizeof(caservice->m_used_demux)/sizeof(int); + while ( iter < max_demux_slots ) + { + if ( caservice->m_used_demux[iter] != 0xFF ) + { + if ( !freed && caservice->m_used_demux[iter] == demux_nums[i] ) + { + eDebug("[eDVBCAService] free slot %d demux %d for service %s", iter, caservice->m_used_demux[iter], caservice->m_service.toString().c_str() ); + caservice->m_used_demux[iter] = 0xFF; + freed=true; + } + else + ++used_demux_slots; + } + ++iter; + } + if (!freed) + eDebug("[eDVBCAService] couldn't free demux slot for demux %d", demux_nums[i]); + if (i || loops == 1) + { + if (!used_demux_slots) // no more used.. so we remove it + { + delete it->second; + exist.erase(it); + } + else + { + if (ptr) + it->second->buildCAPMT(ptr); + else + eDebug("[eDVBCAService] can not send updated demux info"); + } + } + } + } + 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 + +void eDVBCAService::socketCB(int what) +{ + if (what & eSocketNotifier::Read) + /*eDebug("[eDVBCAService] data to read\n")*/; + if (what & eSocketNotifier::Priority) + /*eDebug("[eDVBCAService] priority data to read\n")*/; + if (what & eSocketNotifier::Hungup) { + /*eDebug("[eDVBCAService] connection closed\n")*/; + m_sendstate=1; + sendCAPMT(); + } + if (what & eSocketNotifier::Error) + /*eDebug("[eDVBCAService] connection error\n")*/; +} + +void eDVBCAService::Connect() +{ + if (m_sn) { + delete m_sn; + 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); + 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 = new eSocketNotifier(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) +{ + if (!ptr) + return; + + eDVBTableSpec table_spec; + ptr->getSpec(table_spec); + + int pmtpid = table_spec.pid, + pmt_version = table_spec.version; + + uint8_t demux_mask = 0; + int data_demux = -1; + + int iter=0, max_demux_slots = sizeof(m_used_demux); + while ( iter < max_demux_slots ) + { + if ( m_used_demux[iter] != 0xFF ) + { + if ( m_used_demux[iter] > data_demux ) + data_demux = m_used_demux[iter]; + demux_mask |= (1 << m_used_demux[iter]); + } + ++iter; + } + + if ( data_demux == -1 ) + { + eDebug("[eDVBCAService] no data demux found for service %s", m_service.toString().c_str() ); + return; + } + + eDebug("demux %d mask %02x prevhash %08x", data_demux, demux_mask, m_prev_build_hash); + + unsigned int build_hash = ( pmtpid << 16); + build_hash |= (demux_mask << 8); + build_hash |= (pmt_version&0xFF); + + if ( build_hash == m_prev_build_hash ) + { + eDebug("[eDVBCAService] don't build/send the same CA PMT twice"); + return; + } + + std::vector::const_iterator i=ptr->getSections().begin(); + if ( i != ptr->getSections().end() ) + { + CaProgramMapSection capmt(*i++, m_prev_build_hash ? 0x05 /*update*/ : 0x03 /*only*/, 0x01 ); + + while( i != ptr->getSections().end() ) + { +// eDebug("append"); + capmt.append(*i++); + } + + // add our private descriptors to capmt + uint8_t tmp[10]; + + tmp[0]=0x84; // pmt pid + tmp[1]=0x02; + tmp[2]=pmtpid>>8; + tmp[3]=pmtpid&0xFF; + capmt.injectDescriptor(tmp, false); + + tmp[0] = 0x82; // demux + tmp[1] = 0x02; + tmp[2] = demux_mask; // descramble bitmask + tmp[3] = data_demux&0xFF; // read section data from demux number + capmt.injectDescriptor(tmp, false); + + tmp[0] = 0x81; // dvbnamespace + tmp[1] = 0x08; + tmp[2] = m_service.getDVBNamespace().get()>>24; + tmp[3]=(m_service.getDVBNamespace().get()>>16)&0xFF; + tmp[4]=(m_service.getDVBNamespace().get()>>8)&0xFF; + tmp[5]=m_service.getDVBNamespace().get()&0xFF; + tmp[6]=m_service.getTransportStreamID().get()>>8; + tmp[7]=m_service.getTransportStreamID().get()&0xFF; + tmp[8]=m_service.getOriginalNetworkID().get()>>8; + tmp[9]=m_service.getOriginalNetworkID().get()&0xFF; + capmt.injectDescriptor(tmp, false); + + capmt.writeToBuffer(m_capmt); + } + + m_prev_build_hash = build_hash; + + if ( m_sendstate != 0xFFFFFFFF ) + m_sendstate=0; + sendCAPMT(); +} + +void eDVBCAService::sendCAPMT() +{ + if ( m_sendstate && m_sendstate != 0xFFFFFFFF ) // broken pipe retry + { + ::close(m_sock); + Connect(); + } + + int wp=0; + if ( m_capmt[3] & 0x80 ) + { + int i=0; + int lenbytes = m_capmt[3] & ~0x80; + while(i < lenbytes) + wp = (wp << 8) | m_capmt[4 + i++]; + wp+=4; + wp+=lenbytes; + } + else + { + wp = m_capmt[3]; + wp+=4; + } + + if ( write(m_sock, m_capmt, wp) == wp ) + { + 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;i::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")); + + PyDict_SetItemString(r, "pids", l); + return r; +}