Index: libs/libmythtv/dvbrecorder.cpp =================================================================== --- libs/libmythtv/dvbrecorder.cpp (revision 9417) +++ libs/libmythtv/dvbrecorder.cpp (working copy) @@ -703,7 +703,7 @@ PIDInfo *info = _pid_infos[pid]; if (!info) info = _pid_infos[pid] = new PIDInfo(); - +/* // track scrambled pids if (tspacket.ScramplingControl()) { @@ -721,7 +721,7 @@ QString("PID 0x%1 is no longer encrypted").arg(pid,0,16)); info->isEncrypted = false; } - +*/ // Check continuity counter if (tspacket.HasPayload()) { Index: libs/libmythtv/dbcheck.cpp =================================================================== --- libs/libmythtv/dbcheck.cpp (revision 9417) +++ libs/libmythtv/dbcheck.cpp (working copy) @@ -12,11 +12,18 @@ /// This is the DB schema version expected by the running MythTV instance. const QString currentDatabaseVersion = "1132"; +/// This is the DB schema minor version used by supplementary patches +const QString currentDatabaseVersionMinor = "2"; + static bool UpdateDBVersionNumber(const QString &newnumber); +static bool UpdateDBVersionMinor( const QString &newminor); static bool performActualUpdate(const QString updates[], QString version, QString &dbver); +static bool performMinorUpdate(const QString updates[], QString minor, + QString &dbminor); static bool InitializeDatabase(void); static bool doUpgradeTVDatabaseSchema(void); +static bool dbMinorUpdate(void); /** \defgroup db_schema MythTV Database Schema @@ -352,6 +359,48 @@ return true; } +static bool UpdateDBVersionMinor(const QString &newminor) +{ + // delete old schema version + MSqlQuery query(MSqlQuery::InitCon()); + + QString thequery = "DELETE FROM settings WHERE value='DBSchemaVerMinor';"; + query.prepare(thequery); + query.exec(); + + if (query.lastError().type() != QSqlError::None) + { + QString msg = + QString("DB Error (Deleting old DB minor version number): \n" + "Query was: %1 \nError was: %2 \nnew version: %3") + .arg(thequery) + .arg(MythContext::DBErrorMessage(query.lastError())) + .arg(newminor); + VERBOSE(VB_IMPORTANT, msg); + return false; + } + + // set new schema minor + thequery = QString("INSERT INTO settings (value, data, hostname) " + "VALUES ('DBSchemaVerMinor', %1, NULL);").arg(newminor); + query.prepare(thequery); + query.exec(); + + if (query.lastError().type() != QSqlError::None) + { + QString msg = + QString("DB Error (Setting new DB minor version number): \n" + "Query was: %1 \nError was: %2 \nnew minor version: %3") + .arg(thequery) + .arg(MythContext::DBErrorMessage(query.lastError())) + .arg(newminor); + VERBOSE(VB_IMPORTANT, msg); + return false; + } + + return true; +} + /** \fn performActualUpdate(const QString updates[], QString, QString&) * \brief Runs a number of SQL commands, and updates the schema version. * @@ -399,6 +448,44 @@ return true; } +static bool performMinorUpdate(const QString updates[], QString minor, + QString &dbminor) +{ + MSqlQuery query(MSqlQuery::InitCon()); + + VERBOSE(VB_IMPORTANT, QString("Upgrading to schema minor ") + minor); + + int counter = 0; + QString thequery = updates[counter]; + + while (thequery != "") + { + query.prepare(thequery); + query.exec(); + + if (query.lastError().type() != QSqlError::None) + { + QString msg = + QString("DB Error (Performing database upgrade): \n" + "Query was: %1 \nError was: %2 \nnew minor: %3") + .arg(thequery) + .arg(MythContext::DBErrorMessage(query.lastError())) + .arg(minor); + VERBOSE(VB_IMPORTANT, msg); + return false; + } + + counter++; + thequery = updates[counter]; + } + + if (!UpdateDBVersionMinor(minor)) + return false; + + dbminor = minor; + return true; +} + /** \fn UpgradeTVDatabaseSchema(void) * \brief This is the function called from outside dbcheck.cpp to update the schema. * @@ -416,17 +503,22 @@ { QString dbver = gContext->GetSetting("DBSchemaVer"); - VERBOSE(VB_IMPORTANT, QString("Current Schema Version: %1").arg(dbver)); + QString dbminor = gContext->GetSetting("DBSchemaVerMinor"); - if (dbver == currentDatabaseVersion) + VERBOSE(VB_IMPORTANT, QString("Current Schema Version: %1.%2").arg(dbver) + .arg(dbminor)); + + if ((dbver == currentDatabaseVersion) && + (dbminor == currentDatabaseVersionMinor)) return true; MSqlQuery chartype(MSqlQuery::InitCon()); chartype.prepare("ALTER DATABASE mythconverg DEFAULT CHARACTER SET latin1;"); chartype.exec(); - VERBOSE(VB_IMPORTANT, QString("Newest Schema Version : %1") - .arg(currentDatabaseVersion)); + VERBOSE(VB_IMPORTANT, QString("Newest Schema Version : %1.%2") + .arg(currentDatabaseVersion) + .arg(currentDatabaseVersionMinor)); MSqlQuery lockquery(MSqlQuery::InitCon()); @@ -458,7 +550,15 @@ bool ret = doUpgradeTVDatabaseSchema(); + // Database version minor updates if (ret) + ret = dbMinorUpdate(); + + VERBOSE(VB_IMPORTANT, QString("Newest Schema Version : %1.%2") + .arg(currentDatabaseVersion) + .arg(currentDatabaseVersionMinor)); + + if (ret) VERBOSE(VB_IMPORTANT, "Database Schema upgrade complete, unlocking."); else VERBOSE(VB_IMPORTANT, "Database Schema upgrade FAILED, unlocking."); @@ -2148,8 +2248,51 @@ return true; } +static bool dbMinorUpdate(void) +{ + QString dbminor = gContext->GetSetting("DBSchemaVerMinor"); + if (dbminor == currentDatabaseVersionMinor) + return true; + if ((dbminor == "") || (dbminor == "0")) + { + const QString updates[] = { +// Dish Network +"INSERT INTO dtv_privatetypes (sitype, networkid, private_type, private_value)" +" VALUES('dvb', 4102, 'force_guide_present', 'yes');", +"INSERT INTO dtv_privatetypes (sitype, networkid, private_type, private_value)" +" VALUES('dvb', 4102, 'extended_guide_tid', '229');", +// Bell ExpressVu +"INSERT INTO dtv_privatetypes (sitype, networkid, private_type, private_value)" +" VALUES('dvb', 256, 'force_guide_present', 'yes');", +"INSERT INTO dtv_privatetypes (sitype, networkid, private_type, private_value)" +" VALUES('dvb', 256, 'guide_on_single_tid', 'yes');", +"INSERT INTO dtv_privatetypes (sitype, networkid, private_type, private_value)" +" VALUES('dvb', 257, 'force_guide_present', 'yes');", +"INSERT INTO dtv_privatetypes (sitype, networkid, private_type, private_value)" +" VALUES('dvb', 257, 'guide_on_single_tid', 'yes');", +"" + }; + if (!performMinorUpdate(updates, "1", dbminor)) + return false; + } + if ((dbminor == "1")) + { + const QString updates[] = { +// Bell ExpressVu +"INSERT INTO dtv_privatetypes (sitype, networkid, private_type, private_value)" +" VALUES('dvb', 256, 'standard_guide_tid', '4');", +"INSERT INTO dtv_privatetypes (sitype, networkid, private_type, private_value)" +" VALUES('dvb', 257, 'standard_guide_tid', '102');", +"" + }; + if (!performMinorUpdate(updates, "2", dbminor)) + return false; + } + return true; +} + bool InitializeDatabase(void) { MSqlQuery query(MSqlQuery::InitCon()); Index: libs/libmythtv/dvbcam.h =================================================================== --- libs/libmythtv/dvbcam.h (revision 9417) +++ libs/libmythtv/dvbcam.h (working copy) @@ -47,6 +47,13 @@ bool pmt_sent; bool pmt_updated; bool pmt_added; + bool exitSkThread; + bool skThreadRunning; + pthread_t externSoftkeyThread; + int external_cam_fd; + void sendExternalCam(const ProgramMapTable *pmt); + bool useExternalCam(); + void stopExternalCam(); }; #endif // DVBCAM_H Index: libs/libmythtv/dvbcam.cpp =================================================================== --- libs/libmythtv/dvbcam.cpp (revision 9417) +++ libs/libmythtv/dvbcam.cpp (working copy) @@ -35,7 +35,7 @@ #include #include - +#include #include #include #include @@ -85,6 +85,8 @@ bool DVBCam::Start() { + if(useExternalCam()) + return true; if (numslots == 0) return false; @@ -116,6 +118,7 @@ bool DVBCam::Stop() { + stopExternalCam(); if (ciThreadRunning) { exitCiThread = true; @@ -262,7 +265,7 @@ void DVBCam::SetPMT(const ProgramMapTable *pmt) { VERBOSE(VB_CHANNEL, LOC + "SetPMT() program num #"<ProgramNumber()); - + sendExternalCam(pmt); QMutexLocker locker(&pmt_lock); PMTList.clear(); PMTList.push_back(*pmt); @@ -376,3 +379,147 @@ } return capmt; } + +bool DVBCam::useExternalCam() +{ + skThreadRunning = false; + exitSkThread = false; + external_cam_fd = -1; + QString extCam = QDir::homeDirPath() + + QString("/.mythtv/externcam_%1").arg(cardnum); + if (QFileInfo(extCam).isDir()) + { + QString cmd = gContext->GetSetting("ExternalCamApp",""); + if(cmd == "") { + ERROR("CAM - No external app set in SQL db."); + return false; + } + cmd += " --dir " + extCam + QString(" --card %1").arg(cardnum); + + QString keyFifo = QDir::homeDirPath() + + QString("/.mythtv/externcam_%1/keyfifo").arg(cardnum); + unlink(keyFifo.ascii()); + bool spawn_thread = false; + if(numslots==0 || QFileInfo(extCam + "/force_sw").isFile()) { + cmd += " --writekey " + keyFifo; + mkfifo(keyFifo.ascii(), 0600); + spawn_thread = true; + } + + int tmppid[2]; + if (pipe(tmppid) < 0) + { + perror("Failed to create pipe\n"); + return false; + } + + int child = fork(); + if (child < 0) + { + perror("Failed to fork\n"); + return false; + } + if (child == 0) + { + dup2(tmppid[0],fileno(stdin)); + for(int i = 3; i < sysconf(_SC_OPEN_MAX) - 1; ++i) + close(i); + execl("/bin/sh", "sh", "-c", cmd.ascii(), NULL); + perror("exec"); + _exit(1); + } + close(tmppid[0]); + external_cam_fd=tmppid[1]; + //we need to fake ciThreadRunning in oreder to get PMTs + ciThreadRunning = true; + return true; + } + return false; +} +void DVBCam::sendExternalCam(const ProgramMapTable *pmt) +{ + if (external_cam_fd < 0) + return; + + unsigned char newpmt[512]; + int hdr[34]; //Allow for up to 16 PIDs + int pmtlen=0, hdrlen = 0; + hdr[hdrlen++]=pmt->ProgramNumber(); + hdr[hdrlen++]=1; //Place holder for transportID (future) + + desc_list_t gdesc = MPEGDescriptor::ParseOnlyInclude( + pmt->ProgramInfo(), pmt->ProgramInfoLength(), + DescriptorID::conditional_access); + + for (desc_list_t::const_iterator it = gdesc.begin(); it != gdesc.end(); ++it) + { + ConditionalAccessDescriptor cad(*it); + newpmt[pmtlen++] = 0x09; + newpmt[pmtlen++] = cad.DataSize() + 4; + newpmt[pmtlen++] = (cad.SystemID()) >> 8; + newpmt[pmtlen++] = (cad.SystemID()) & 0xff; + newpmt[pmtlen++] = (cad.PID()) >> 8; + newpmt[pmtlen++] = (cad.PID()) & 0xff; + const unsigned char* p_data = cad.Data(); + for (int x = 0; x < cad.DataSize(); x++) + newpmt[pmtlen++] = p_data[x]; + } + for (uint i = 0; i < pmt->StreamCount(); i++) + { + int type; + if (StreamID::IsVideo(pmt->StreamType(i))) + type = 0; + else if (StreamID::IsAudio(pmt->StreamType(i))) + type = 1; + else + type = 5; + + hdr[hdrlen++] = pmt->StreamPID(i); //pid + hdr[hdrlen++] = type; //stream type + + desc_list_t gdesc = MPEGDescriptor::ParseOnlyInclude( + pmt->StreamInfo(i), pmt->StreamInfoLength(i), + DescriptorID::conditional_access); + + for (desc_list_t::const_iterator it = gdesc.begin(); it != gdesc.end(); ++it) + { + ConditionalAccessDescriptor cad(*it); + newpmt[pmtlen++] = 0x09; + newpmt[pmtlen++] = cad.DataSize() + 4; + newpmt[pmtlen++] = (cad.SystemID()) >> 8; + newpmt[pmtlen++] = (cad.SystemID()) & 0xff; + newpmt[pmtlen++] = (cad.PID()) >> 8; + newpmt[pmtlen++] = (cad.PID()) & 0xff; + const unsigned char* p_data = cad.Data(); + for (int x = 0; x < cad.DataSize(); x++) + newpmt[pmtlen++] = p_data[x]; + } + } + if (pmtlen) { + QString temp = QString("SID: %1 TID: %2").arg(hdr[0]).arg(hdr[1]); + for (int x = 2; x < hdrlen; x += 2) + temp += QString(" (%1 %2)").arg(hdr[x]).arg(hdr[x + 1]); + GENERAL(temp); + temp = QString("Found CA Descriptors: (%1)").arg(pmtlen); + for (int x = 0; x < pmtlen; x++) + temp += QString(" %1").arg(newpmt[x],2,16); + GENERAL(temp); + write(external_cam_fd, &hdrlen, sizeof(hdrlen)); + write(external_cam_fd, &hdr, hdrlen * sizeof(int)); + write(external_cam_fd, &pmtlen, sizeof(pmtlen)); + write(external_cam_fd, &newpmt, pmtlen); + } +} + +void DVBCam::stopExternalCam() +{ + if(external_cam_fd != -1) { + close(external_cam_fd); + ciThreadRunning = false; + if (skThreadRunning) + { + exitSkThread = true; + pthread_join(externSoftkeyThread, NULL); + } + } +} Index: libs/libmythtv/eitscanner.h =================================================================== --- libs/libmythtv/eitscanner.h (revision 9417) +++ libs/libmythtv/eitscanner.h (working copy) @@ -28,7 +28,7 @@ void StopPassiveScan(void); void StartActiveScan(TVRec*, uint max_seconds_per_source, - bool ignore_source); + bool ignore_source, bool limit_transport=false); void StopActiveScan(void); Index: libs/libmythtv/tv_rec.cpp =================================================================== --- libs/libmythtv/tv_rec.cpp (revision 9417) +++ libs/libmythtv/tv_rec.cpp (working copy) @@ -78,9 +78,6 @@ #define LOC QString("TVRec(%1): ").arg(cardid) #define LOC_ERR QString("TVRec(%1) Error: ").arg(cardid) -/// How many seconds after entering kState_None should we start EIT Scanner -const uint TVRec::kEITScanStartTimeout = 60; /* 1 minute */ - /// How many milliseconds the signal monitor should wait between checks const uint TVRec::kSignalMonitoringRate = 50; /* msec */ @@ -768,7 +765,8 @@ { // Add some randomness to avoid all cards starting // EIT scanning at nearly the same time. - uint timeout = kEITScanStartTimeout + random() % 59; + uint idle_start = gContext->GetNumSetting("EITCrawIdleStart", 1); + uint timeout = (idle_start * 60) + random() % 59; eitScanStartTime = eitScanStartTime.addSecs(timeout); } else @@ -1207,7 +1205,8 @@ // Add some randomness to avoid all cards starting // EIT scanning at nearly the same time. - uint timeout = kEITScanStartTimeout + random() % 59; + uint idle_start = gContext->GetNumSetting("EITCrawIdleStart", 1); + uint timeout = (idle_start * 60) + random() % 59; eitScanStartTime = QDateTime::currentDateTime().addSecs(timeout); while (HasFlags(kFlagRunMainLoop)) @@ -1342,8 +1341,11 @@ else { uint ttMin = gContext->GetNumSetting("EITTransportTimeout", 5); + + uint tid_limit = gContext->GetNumSetting("EITCrawLimitTransports", 0); uint ignore = gContext->GetNumSetting("EITIgnoresSource", 0); - scanner->StartActiveScan(this, ttMin * 60, ignore); + scanner->StartActiveScan(this, ttMin * 60, ignore, (tid_limit ? true : false)); + SetFlags(kFlagEITScannerRunning); eitScanStartTime = QDateTime::currentDateTime().addYears(1); } Index: libs/libmythtv/eitscanner.cpp =================================================================== --- libs/libmythtv/eitscanner.cpp (revision 9417) +++ libs/libmythtv/eitscanner.cpp (working copy) @@ -183,7 +183,7 @@ } void EITScanner::StartActiveScan(TVRec *_rec, uint max_seconds_per_source, - bool _ignore_source) + bool _ignore_source, bool limit_transports) { rec = _rec; ignore_source = _ignore_source; @@ -191,17 +191,42 @@ if (!activeScanChannels.size()) { MSqlQuery query(MSqlQuery::InitCon()); - query.prepare( - "SELECT channum, mplexid " - "FROM channel, cardinput, capturecard, videosource " - "WHERE cardinput.sourceid = channel.sourceid AND " - " videosource.sourceid = channel.sourceid AND " - " capturecard.cardid = cardinput.cardid AND " - " channel.mplexid IS NOT NULL AND " - " useonairguide = 1 AND " - " useeit = 1 AND " - " cardinput.cardid = :CARDID " - "ORDER BY cardinput.sourceid, atscsrcid"); + + if (limit_transports) + query.prepare( + "SELECT MIN(channum), channel.mplexid " + "FROM channel, dtv_multiplex, dtv_privatetypes, videosource, " + "cardinput, capturecard " + "WHERE dtv_multiplex.networkid = dtv_privatetypes.networkid " + "AND videosource.sourceid = channel.sourceid " + "AND cardinput.sourceid = channel.sourceid " + "AND capturecard.cardid = cardinput.cardid " + "AND (private_type = 'extended_guide_tid' " + "OR private_type = 'standard_guide_tid') " + "AND private_value = transportid " + "AND channel.mplexid = dtv_multiplex.mplexid " + "AND useonairguide = 1 " + "AND useeit = 1 " + "AND cardinput.cardid = :CARDID " + "GROUP BY transportid"); + else + query.prepare( + "SELECT MIN(channum), channel.mplexid " + "FROM channel,videosource,cardinput,capturecard,dtv_multiplex " + "LEFT JOIN dtv_privatetypes " + "ON dtv_multiplex.networkid = dtv_privatetypes.networkid " + "AND (private_type = 'extended_guide_tid' " + "OR private_type = 'standard_guide_tid') " + "AND private_value = transportid " + "WHERE videosource.sourceid = channel.sourceid " + "AND cardinput.sourceid = channel.sourceid " + "AND capturecard.cardid = cardinput.cardid " + "AND channel.mplexid = dtv_multiplex.mplexid " + "AND useonairguide = 1 " + "AND useeit = 1 " + "AND cardinput.cardid = :CARDID " + "GROUP BY transportid"); + query.bindValue(":CARDID", rec->GetCaptureCardNum()); if (!query.exec() || !query.isActive()) Index: libs/libmythtv/dtvsignalmonitor.cpp =================================================================== --- libs/libmythtv/dtvsignalmonitor.cpp (revision 9417) +++ libs/libmythtv/dtvsignalmonitor.cpp (working copy) @@ -234,9 +234,9 @@ if (pmt->ProgramNumber() != (uint)programNumber) { - VERBOSE(VB_IMPORTANT, LOC_ERR + - QString("Wrong PMT; pmt->pn(%1) desired(%2)") - .arg(pmt->ProgramNumber()).arg(programNumber)); +// VERBOSE(VB_IMPORTANT, LOC_ERR + +// QString("Wrong PMT; pmt->pn(%1) desired(%2)") +// .arg(pmt->ProgramNumber()).arg(programNumber)); return; // Not the PMT we are looking for... } Index: libs/libmythtv/tv_rec.h =================================================================== --- libs/libmythtv/tv_rec.h (revision 9417) +++ libs/libmythtv/tv_rec.h (working copy) @@ -372,7 +372,6 @@ QString rbFileExt; public: - static const uint kEITScanStartTimeout; static const uint kSignalMonitoringRate; // General State flags Index: setup/backendsettings.cpp =================================================================== --- setup/backendsettings.cpp (revision 9417) +++ setup/backendsettings.cpp (working copy) @@ -73,6 +73,15 @@ return gc; }; +static GlobalLineEdit *ExternalCamApp() +{ + GlobalLineEdit *gc = new GlobalLineEdit("ExternalCamApp"); + gc->setLabel(QObject::tr("External Cam App")); + gc->setValue("sasc"); + gc->setHelpText(QObject::tr("External Application for CAMless cards.")); + return gc; +}; + static GlobalComboBox *TVFormat() { GlobalComboBox *gc = new GlobalComboBox("TVFormat"); @@ -214,6 +223,30 @@ return gc; }; +static GlobalSpinBox *EITCrawIdleStart() +{ + GlobalSpinBox *gc = new GlobalSpinBox("EITCrawIdleStart", 1, 120, 1); + gc->setLabel(QObject::tr("Backend Idle Before EIT Craw (mins)")); + gc->setValue(1); + QString helpText = QObject::tr( + "The amount of time to wait after the backend becomes idle for a given " + "capture card before the EIT craw starts."); + gc->setHelpText(helpText); + return gc; +} + +static GlobalCheckBox *EITCrawLimitTransports() +{ + GlobalCheckBox *gc = new GlobalCheckBox("EITCrawLimitTransports"); + gc->setLabel(QObject::tr("EIT Craw Limits Transports")); + gc->setValue(false); + gc->setHelpText(QObject::tr("If set, EIT craw will scan only " + "those transports which have either " + "standard_guide_tid or extended_guide_tid " + "entries defined in dtv_privatetypes.")); + return gc; +}; + static GlobalSpinBox *EITTransportTimeout() { GlobalSpinBox *gc = new GlobalSpinBox("EITTransportTimeout", 1, 15, 1); @@ -654,6 +687,7 @@ VerticalConfigurationGroup* group1 = new VerticalConfigurationGroup(false); group1->setLabel(QObject::tr("Host-specific Backend Setup")); group1->addChild(RecordFilePrefix()); + group1->addChild(ExternalCamApp()); addChild(group1); VerticalConfigurationGroup* group2 = new VerticalConfigurationGroup(false); @@ -669,6 +703,8 @@ VerticalConfigurationGroup* group2a1 = new VerticalConfigurationGroup(false); group2a1->setLabel(QObject::tr("EIT Scanner Options")); group2a1->addChild(EITTransportTimeout()); + group2a1->addChild(EITCrawIdleStart()); + group2a1->addChild(EITCrawLimitTransports()); group2a1->addChild(EITIgnoresSource()); addChild(group2a1);