The actual tuning is now done in a separate thread

This commit is contained in:
Klaus Schmidinger 2002-12-08 09:55:26 +01:00
parent 2d2369fca0
commit b9422baff2
6 changed files with 243 additions and 167 deletions

View File

@ -441,6 +441,7 @@ Oliver Endriss <o.endriss@gmx.de>
Reinhard Walter Buchner <rw.buchner@freenet.de>
for adding some satellites to 'sources.conf'
for his help in testing tuning with "Motor-DiSEqC"
Lauri Tischler <lauri.tischler@efore.fi>
for helping to test and debug the new channel source and DiSEqC handling

View File

@ -1845,3 +1845,9 @@ Video Disk Recorder Revision History
- Improved handling of repeated remote keys.
- The RCU now only sets the channel number display when there are no incoming remote
control keys, which improves reaction on repeated keys.
- The actual tuning is now done in a separate thread, which makes zapping through the
channels a lot faster and no longer gets stuck on channels that don't broadcast.
This also makes "Motor-DiSEqC" work (thanks to Reinhard Walter Buchner for his help
in testing this). Since switching channels now no longer explicitly waits for a
channel lock in the foreground thread, the "panic level" mechanism is no longer
used (maybe we don't need it nay more, anyway).

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: diseqc.c 1.1 2002/10/05 13:54:32 kls Exp $
* $Id: diseqc.c 1.2 2002/12/07 13:44:56 kls Exp $
*/
#include "diseqc.h"
@ -16,7 +16,6 @@
cDiseqc::cDiseqc(void)
{
commands = NULL;
currentAction = NULL;;
parsing = false;
numCodes = 0;
}
@ -39,11 +38,11 @@ bool cDiseqc::Parse(const char *s)
polarization = toupper(polarization);
if (polarization == 'V' || polarization == 'H') {
parsing = true;
bool Start = true;
while (Execute(Start) != daNone)
Start = false;
char *CurrentAction = NULL;
while (Execute(&CurrentAction) != daNone)
;
parsing = false;
result = !commands || currentAction && !*currentAction;
result = !commands || !*CurrentAction;
}
else
esyslog("ERROR: unknown polarization '%c'", polarization);
@ -101,12 +100,12 @@ char *cDiseqc::Codes(char *s)
return NULL;
}
cDiseqc::eDiseqcActions cDiseqc::Execute(bool Start)
cDiseqc::eDiseqcActions cDiseqc::Execute(char **CurrentAction)
{
if (Start)
currentAction = commands;
while (currentAction && *currentAction) {
switch (*currentAction++) {
if (!*CurrentAction)
*CurrentAction = commands;
while (*CurrentAction && **CurrentAction) {
switch (*(*CurrentAction)++) {
case ' ': break;
case 't': return daToneOff;
case 'T': return daToneOn;
@ -114,8 +113,8 @@ cDiseqc::eDiseqcActions cDiseqc::Execute(bool Start)
case 'V': return daVoltage18;
case 'A': return daMiniA;
case 'B': return daMiniB;
case 'W': currentAction = Wait(currentAction); break;
case '[': currentAction = Codes(currentAction); return currentAction ? daCodes : daNone;
case 'W': *CurrentAction = Wait(*CurrentAction); break;
case '[': *CurrentAction = Codes(*CurrentAction); return *CurrentAction ? daCodes : daNone;
default: return daNone;
}
}

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: diseqc.h 1.1 2002/10/05 13:02:52 kls Exp $
* $Id: diseqc.h 1.2 2002/12/07 13:54:02 kls Exp $
*/
#ifndef __DISEQC_H
@ -31,7 +31,6 @@ private:
char polarization;
int lof;
char *commands;
char *currentAction;
bool parsing;
uchar codes[MaxDiseqcCodes];
int numCodes;
@ -41,7 +40,14 @@ public:
cDiseqc(void);
~cDiseqc();
bool Parse(const char *s);
eDiseqcActions Execute(bool Start = false);
eDiseqcActions Execute(char **CurrentAction);
// Parses the DiSEqC commands and returns the appropriate action code
// with every call. CurrentAction must be the address of a character pointer,
// which is initialized to NULL. This pointer is used internally while parsing
// the commands and shall not be modified once Execute() has been called with
// it. Call Execute() repeatedly (always providing the same CurrentAction pointer)
// until it returns daNone. After a successful execution of all commands
// *CurrentAction points to the value 0x00.
int Source(void) const { return source; }
int Slof(void) const { return slof; }
char Polarization(void) const { return polarization; }

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: dvbdevice.c 1.37 2002/11/16 12:36:50 kls Exp $
* $Id: dvbdevice.c 1.38 2002/12/07 14:50:46 kls Exp $
*/
#include "dvbdevice.h"
@ -60,8 +60,207 @@ static int DvbOpen(const char *Name, int n, int Mode, bool ReportError = false)
return fd;
}
// --- cDvbTuner -------------------------------------------------------------
class cDvbTuner : public cThread {
private:
enum eTunerStatus { tsIdle, tsSet, tsTuned, tsLocked };
int fd_frontend;
int cardIndex;
fe_type_t frontendType;
cChannel channel;
const char *diseqcCommands;
bool active;
eTunerStatus tunerStatus;
cMutex mutex;
cCondVar newSet;
bool SetFrontend(void);
virtual void Action(void);
public:
cDvbTuner(int Fd_Frontend, int CardIndex, fe_type_t FrontendType);
virtual ~cDvbTuner();
bool IsTunedTo(const cChannel *Channel) const;
void Set(const cChannel *Channel);
bool Locked(void) { return tunerStatus == tsLocked; }
};
cDvbTuner::cDvbTuner(int Fd_Frontend, int CardIndex, fe_type_t FrontendType)
{
fd_frontend = Fd_Frontend;
cardIndex = CardIndex;
frontendType = FrontendType;
diseqcCommands = NULL;
active = false;
tunerStatus = tsIdle;
Start();
}
cDvbTuner::~cDvbTuner()
{
active = false;
tunerStatus = tsIdle;
newSet.Broadcast();
Cancel(3);
}
bool cDvbTuner::IsTunedTo(const cChannel *Channel) const
{
return tunerStatus != tsIdle && channel.Source() == Channel->Source() && channel.Frequency() == Channel->Frequency();
}
void cDvbTuner::Set(const cChannel *Channel)
{
cMutexLock MutexLock(&mutex);
channel = *Channel;
tunerStatus = tsSet;
newSet.Broadcast();
}
static unsigned int FrequencyToHz(unsigned int f)
{
while (f && f < 1000000)
f *= 1000;
return f;
}
bool cDvbTuner::SetFrontend(void)
{
dvb_frontend_parameters Frontend;
memset(&Frontend, 0, sizeof(Frontend));
switch (frontendType) {
case FE_QPSK: { // DVB-S
unsigned int frequency = channel.Frequency();
if (Setup.DiSEqC) {
cDiseqc *diseqc = Diseqcs.Get(channel.Source(), channel.Frequency(), channel.Polarization());
if (diseqc) {
if (diseqc->Commands() && (!diseqcCommands || strcmp(diseqcCommands, diseqc->Commands()) != 0)) {
cDiseqc::eDiseqcActions da;
for (char *CurrentAction = NULL; (da = diseqc->Execute(&CurrentAction)) != cDiseqc::daNone; ) {
switch (da) {
case cDiseqc::daNone: break;
case cDiseqc::daToneOff: CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_OFF)); break;
case cDiseqc::daToneOn: CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_ON)); break;
case cDiseqc::daVoltage13: CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_13)); break;
case cDiseqc::daVoltage18: CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_18)); break;
case cDiseqc::daMiniA: CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_BURST, SEC_MINI_A)); break;
case cDiseqc::daMiniB: CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_BURST, SEC_MINI_B)); break;
case cDiseqc::daCodes: {
int n = 0;
uchar *codes = diseqc->Codes(n);
if (codes) {
struct dvb_diseqc_master_cmd cmd;
memcpy(cmd.msg, codes, min(n, int(sizeof(cmd.msg))));
cmd.msg_len = n;
CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_MASTER_CMD, &cmd));
}
}
break;
}
}
diseqcCommands = diseqc->Commands();
}
frequency -= diseqc->Lof();
}
else {
esyslog("ERROR: no DiSEqC parameters found for channel %d", channel.Number());
return false;
}
}
else {
int tone = SEC_TONE_OFF;
if (frequency < (unsigned int)Setup.LnbSLOF) {
frequency -= Setup.LnbFrequLo;
tone = SEC_TONE_OFF;
}
else {
frequency -= Setup.LnbFrequHi;
tone = SEC_TONE_ON;
}
int volt = (channel.Polarization() == 'v' || channel.Polarization() == 'V') ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18;
CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, volt));
CHECK(ioctl(fd_frontend, FE_SET_TONE, tone));
}
Frontend.frequency = frequency * 1000UL;
Frontend.inversion = fe_spectral_inversion_t(channel.Inversion());
Frontend.u.qpsk.symbol_rate = channel.Srate() * 1000UL;
Frontend.u.qpsk.fec_inner = fe_code_rate_t(channel.CoderateH());
}
break;
case FE_QAM: { // DVB-C
// Frequency and symbol rate:
Frontend.frequency = FrequencyToHz(channel.Frequency());
Frontend.inversion = fe_spectral_inversion_t(channel.Inversion());
Frontend.u.qam.symbol_rate = channel.Srate() * 1000UL;
Frontend.u.qam.fec_inner = fe_code_rate_t(channel.CoderateH());
Frontend.u.qam.modulation = fe_modulation_t(channel.Modulation());
}
break;
case FE_OFDM: { // DVB-T
// Frequency and OFDM paramaters:
Frontend.frequency = FrequencyToHz(channel.Frequency());
Frontend.inversion = fe_spectral_inversion_t(channel.Inversion());
Frontend.u.ofdm.bandwidth = fe_bandwidth_t(channel.Bandwidth());
Frontend.u.ofdm.code_rate_HP = fe_code_rate_t(channel.CoderateH());
Frontend.u.ofdm.code_rate_LP = fe_code_rate_t(channel.CoderateL());
Frontend.u.ofdm.constellation = fe_modulation_t(channel.Modulation());
Frontend.u.ofdm.transmission_mode = fe_transmit_mode_t(channel.Transmission());
Frontend.u.ofdm.guard_interval = fe_guard_interval_t(channel.Guard());
Frontend.u.ofdm.hierarchy_information = fe_hierarchy_t(channel.Hierarchy());
}
break;
default:
esyslog("ERROR: attempt to set channel with unknown DVB frontend type");
return false;
}
if (ioctl(fd_frontend, FE_SET_FRONTEND, &Frontend) < 0) {
esyslog("ERROR: frontend %d: %m", cardIndex);
return false;
}
return true;
}
void cDvbTuner::Action(void)
{
dsyslog("tuner thread started on device %d (pid=%d)", cardIndex + 1, getpid());
active = true;
while (active) {
cMutexLock MutexLock(&mutex);
if (tunerStatus == tsSet)
tunerStatus = SetFrontend() ? tsTuned : tsIdle;
if (tunerStatus == tsTuned) {
fe_status_t status = fe_status_t(0);
CHECK(ioctl(fd_frontend, FE_READ_STATUS, &status));
if (status & FE_HAS_LOCK)
tunerStatus = tsLocked;
}
dvb_frontend_event event;
if (ioctl(fd_frontend, FE_GET_EVENT, &event) == 0) {
if (tunerStatus != tsIdle && event.status & FE_REINIT) {
tunerStatus = tsSet;
esyslog("ERROR: frontend %d was reinitialized - re-tuning", cardIndex);
continue;
}
}
newSet.TimedWait(mutex, 1000);
}
dsyslog("tuner thread ended on device %d (pid=%d)", cardIndex + 1, getpid());
}
// --- cDvbDevice ------------------------------------------------------------
cDvbDevice::cDvbDevice(int n)
{
dvbTuner = NULL;
frontendType = fe_type_t(-1); // don't know how else to initialize this - there is no FE_UNKNOWN
siProcessor = NULL;
spuDecoder = NULL;
@ -69,7 +268,7 @@ cDvbDevice::cDvbDevice(int n)
// Devices that are present on all card types:
fd_frontend = DvbOpen(DEV_DVB_FRONTEND, n, O_RDWR | O_NONBLOCK);
int fd_frontend = DvbOpen(DEV_DVB_FRONTEND, n, O_RDWR | O_NONBLOCK);
// Devices that are only present on cards with decoders:
@ -90,8 +289,10 @@ cDvbDevice::cDvbDevice(int n)
if (fd_frontend >= 0) {
dvb_frontend_info feinfo;
siProcessor = new cSIProcessor(DvbName(DEV_DVB_DEMUX, n));
if (ioctl(fd_frontend, FE_GET_INFO, &feinfo) >= 0)
if (ioctl(fd_frontend, FE_GET_INFO, &feinfo) >= 0) {
frontendType = feinfo.type;
dvbTuner = new cDvbTuner(fd_frontend, CardIndex(), frontendType);
}
else
LOG_ERROR;
}
@ -99,16 +300,13 @@ cDvbDevice::cDvbDevice(int n)
esyslog("ERROR: can't open DVB device %d", n);
aPid1 = aPid2 = 0;
source = -1;
frequency = -1;
diseqcCommands = NULL;
}
cDvbDevice::~cDvbDevice()
{
delete spuDecoder;
delete siProcessor;
delete dvbTuner;
// We're not explicitly closing any device files here, since this sometimes
// caused segfaults. Besides, the program is about to terminate anyway...
}
@ -322,11 +520,6 @@ bool cDvbDevice::SetPid(cPidHandle *Handle, int Type, bool On)
return true;
}
bool cDvbDevice::IsTunedTo(const cChannel *Channel) const
{
return source == Channel->Source() && frequency == Channel->Frequency();
}
bool cDvbDevice::ProvidesSource(int Source) const
{
int type = Source & cSource::st_Mask;
@ -346,7 +539,7 @@ bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *Ne
if (ProvidesSource(Channel->Source()) && ProvidesCa(Channel->Ca())) {
#ifdef DO_MULTIPLE_RECORDINGS
if (Receiving()) {
if (IsTunedTo(Channel)) {
if (dvbTuner->IsTunedTo(Channel)) {
needsDetachReceivers = false;
if (!HasPid(Channel->Vpid())) {
if (Channel->Ca() > CACONFBASE) {
@ -375,18 +568,11 @@ bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *Ne
return result;
}
static unsigned int FrequencyToHz(unsigned int f)
{
while (f && f < 1000000)
f *= 1000;
return f;
}
bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
{
bool IsEncrypted = Channel->Ca() > CACONFBASE;
bool DoTune = !IsTunedTo(Channel);
bool DoTune = !dvbTuner->IsTunedTo(Channel);
bool TurnOffLivePIDs = HasDecoder()
&& (DoTune
@ -436,136 +622,15 @@ bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
}
if (DoTune) {
dvb_frontend_parameters Frontend;
memset(&Frontend, 0, sizeof(Frontend));
switch (frontendType) {
case FE_QPSK: { // DVB-S
unsigned int frequency = Channel->Frequency();
if (Setup.DiSEqC) {
cDiseqc *diseqc = Diseqcs.Get(Channel->Source(), Channel->Frequency(), Channel->Polarization());
if (diseqc) {
if (diseqc->Commands() && (!diseqcCommands || strcmp(diseqcCommands, diseqc->Commands()) != 0)) {
cDiseqc::eDiseqcActions da;
for (bool Start = true; (da = diseqc->Execute(Start)) != cDiseqc::daNone; Start = false) {
switch (da) {
case cDiseqc::daNone: break;
case cDiseqc::daToneOff: CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_OFF)); break;
case cDiseqc::daToneOn: CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_ON)); break;
case cDiseqc::daVoltage13: CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_13)); break;
case cDiseqc::daVoltage18: CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_18)); break;
case cDiseqc::daMiniA: CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_BURST, SEC_MINI_A)); break;
case cDiseqc::daMiniB: CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_BURST, SEC_MINI_B)); break;
case cDiseqc::daCodes: {
int n = 0;
uchar *codes = diseqc->Codes(n);
if (codes) {
struct dvb_diseqc_master_cmd cmd;
memcpy(cmd.msg, codes, min(n, int(sizeof(cmd.msg))));
cmd.msg_len = n;
CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_MASTER_CMD, &cmd));
}
}
break;
}
}
diseqcCommands = diseqc->Commands();
}
frequency -= diseqc->Lof();
}
else {
esyslog("ERROR: no DiSEqC parameters found for channel %d", Channel->Number());
return false;
}
}
else {
int tone = SEC_TONE_OFF;
if (frequency < (unsigned int)Setup.LnbSLOF) {
frequency -= Setup.LnbFrequLo;
tone = SEC_TONE_OFF;
}
else {
frequency -= Setup.LnbFrequHi;
tone = SEC_TONE_ON;
}
int volt = (Channel->Polarization() == 'v' || Channel->Polarization() == 'V') ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18;
CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, volt));
CHECK(ioctl(fd_frontend, FE_SET_TONE, tone));
}
Frontend.frequency = frequency * 1000UL;
Frontend.inversion = fe_spectral_inversion_t(Channel->Inversion());
Frontend.u.qpsk.symbol_rate = Channel->Srate() * 1000UL;
Frontend.u.qpsk.fec_inner = fe_code_rate_t(Channel->CoderateH());
}
break;
case FE_QAM: { // DVB-C
// Frequency and symbol rate:
Frontend.frequency = FrequencyToHz(Channel->Frequency());
Frontend.inversion = fe_spectral_inversion_t(Channel->Inversion());
Frontend.u.qam.symbol_rate = Channel->Srate() * 1000UL;
Frontend.u.qam.fec_inner = fe_code_rate_t(Channel->CoderateH());
Frontend.u.qam.modulation = fe_modulation_t(Channel->Modulation());
}
break;
case FE_OFDM: { // DVB-T
// Frequency and OFDM paramaters:
Frontend.frequency = FrequencyToHz(Channel->Frequency());
Frontend.inversion = fe_spectral_inversion_t(Channel->Inversion());
Frontend.u.ofdm.bandwidth = fe_bandwidth_t(Channel->Bandwidth());
Frontend.u.ofdm.code_rate_HP = fe_code_rate_t(Channel->CoderateH());
Frontend.u.ofdm.code_rate_LP = fe_code_rate_t(Channel->CoderateL());
Frontend.u.ofdm.constellation = fe_modulation_t(Channel->Modulation());
Frontend.u.ofdm.transmission_mode = fe_transmit_mode_t(Channel->Transmission());
Frontend.u.ofdm.guard_interval = fe_guard_interval_t(Channel->Guard());
Frontend.u.ofdm.hierarchy_information = fe_hierarchy_t(Channel->Hierarchy());
}
break;
default:
esyslog("ERROR: attempt to set channel with unknown DVB frontend type");
return false;
}
// Discard stale events:
for (;;) {
dvb_frontend_event event;
if (ioctl(fd_frontend, FE_GET_EVENT, &event) < 0)
break;
}
// Tuning:
CHECK(ioctl(fd_frontend, FE_SET_FRONTEND, &Frontend));
// Wait for channel lock:
fe_status_t status = fe_status_t(0);
for (int i = 0; i < 100; i++) {
CHECK(ioctl(fd_frontend, FE_READ_STATUS, &status));
if (status & FE_HAS_LOCK)
break;
usleep(10 * 1000);
}
dvbTuner->Set(Channel);
/*XXX do we still need this???
if (!(status & FE_HAS_LOCK)) {
esyslog("ERROR: channel %d not locked on DVB card %d!", Channel->Number(), CardIndex() + 1);
if (LiveView && IsPrimaryDevice())
cThread::RaisePanic();
return false;
}
source = Channel->Source();
frequency = Channel->Frequency();
XXX*/
}
// PID settings:

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: dvbdevice.h 1.18 2002/11/03 12:31:15 kls Exp $
* $Id: dvbdevice.h 1.19 2002/12/07 14:44:29 kls Exp $
*/
#ifndef __DVBDEVICE_H
@ -22,6 +22,8 @@
#define MAXDVBDEVICES 4
class cDvbTuner;
class cDvbDevice : public cDevice {
friend class cDvbOsd;
private:
@ -33,7 +35,7 @@ public:
// Must be called before accessing any DVB functions.
private:
fe_type_t frontendType;
int fd_osd, fd_frontend, fd_audio, fd_video, fd_dvr;
int fd_osd, fd_audio, fd_video, fd_dvr;
int OsdDeviceHandle(void) const { return fd_osd; }
protected:
virtual void MakePrimaryDevice(bool On);
@ -53,10 +55,7 @@ public:
// Channel facilities
private:
int source;
int frequency;
const char *diseqcCommands;
bool IsTunedTo(const cChannel *Channel) const;
cDvbTuner *dvbTuner;
public:
virtual bool ProvidesSource(int Source) const;
virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL) const;