Implemented recording and replaying with a single DVB card

This commit is contained in:
Klaus Schmidinger 2002-09-04 17:26:02 +02:00
parent 1967d0cd3d
commit 3e58bc64fe
12 changed files with 416 additions and 337 deletions

View File

@ -1431,9 +1431,16 @@ Video Disk Recorder Revision History
time changed into the future (thanks to Matthias Schniedermeyer for reporting
this one).
2002-08-28: Version 1.1.9
2002-09-04: Version 1.1.9
- Fixed the 'newplugin' script to make it name the target for creating the
distribution package 'dist', as stated in the PLUGINS.html documentation.
If you have already created a plugin source directory and Makefile you may
want to check it and replace the 'package' target with 'dist' if necessary.
- Changed device handling for being able to do simultaneous recording and
replay on the same device (Time Shifting). In order for this to work you need
to use a driver with a firmware version that has this feature implemented.
- cDevice::ProvidesCa() is no longer virtual. The new function
cDevice::ProvidesChannel() is now used to determine whether a device can
receive a given channel, and by default this function returns false. So a
device that is a pure replaying device doesn't need to do anything here.

View File

@ -21,18 +21,18 @@ VDR program and present itself to the user.
The <i>inside</i> interface provides the plugin code access to VDR's internal data
structures and allows it to hook itself into specific areas to perform special actions.
<p>
<!--X1.1.5--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
Important modifications introduced in version 1.1.5 are marked like this.
<!--X1.1.5--></td></tr></table>
<!--X1.1.6--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
<!--X1.1.6--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
Important modifications introduced in version 1.1.6 are marked like this.
<!--X1.1.6--></td></tr></table>
<!--X1.1.7--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
<!--X1.1.7--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
Important modifications introduced in version 1.1.7 are marked like this.
<!--X1.1.7--></td></tr></table>
<!--X1.1.8--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
<!--X1.1.8--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
Important modifications introduced in version 1.1.8 are marked like this.
<!--X1.1.8--></td></tr></table>
<!--X1.1.9--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
Important modifications introduced in version 1.1.9 are marked like this.
<!--X1.1.9--></td></tr></table>
<a name="Part I - The Outside Interface"><hr><center><h1>Part I - The Outside Interface</h1></center>
@ -352,20 +352,20 @@ these values inside the plugin. Here's an example:
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
bool cPluginHello::ProcessArgs(int argc, char *argv[])
{
{
// Implement command line argument processing here if applicable.
static struct option long_options[] = {
{ "aaa", required_argument, NULL, 'a' },
{ "bbb", no_argument, NULL, 'b' },
{ NULL }
};
int c;
while ((c = getopt_long(argc, argv, "a:b", long_options, NULL)) != -1) {
switch (c) {
case 'a': option_a = optarg;
break;
case 'b': option_b = true;
case 'b': option_b = true;
break;
default: return false;
}
@ -609,7 +609,7 @@ public:
};
cMenuSetupHello::cMenuSetupHello(void)
{
{
newGreetingTime = GreetingTime;
newUseAlternateGreeting = UseAlternateGreeting;
Add(new cMenuEditIntItem( tr("Greeting time (s)"), &amp;newGreetingTime));
@ -617,7 +617,7 @@ cMenuSetupHello::cMenuSetupHello(void)
}
void cMenuSetupHello::Store(void)
{
{
SetupStore("GreetingTime", GreetingTime = newGreetingTime);
SetupStore("UseAlternateGreeting", UseAlternateGreeting = newUseAlternateGreeting);
}
@ -957,7 +957,7 @@ stream. There are no prerequisites regarding the length or alignment of an
individual block of data. The sum of all blocks must simply result in the
desired video data stream, and it must be delivered fast enough so that the
DVB device doesn't run out of data.
<!--X1.1.7--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
<!--X1.1.7--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
To avoid busy loops the player should call its member function
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
@ -1034,7 +1034,7 @@ void DeviceStillPicture(const uchar *Data, int Length);
which can be called to display a still picture. VDR uses this function when handling
its editing marks. A special case of a "player" might use this function to implement
a "picture viewer".
a "picture viewer".
<p>
For detailed information on how to implement your own player, please take a look
at VDR's <tt>cDvbPlayer</tt> and <tt>cDvbPlayerControl</tt> classes.
@ -1065,7 +1065,7 @@ enjoy additional players, since they will be able to control them with actions
that they already know. If you absolutely want to do things differently, just go
ahead - it's your show...
<!--X1.1.6--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
<!--X1.1.6--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
<hr><h2>Receivers</h2>
<center><i><b>Tapping into the stream...</b></i></center><p>
@ -1121,7 +1121,6 @@ If the <tt>cReceiver</tt> isn't needed any more, it may simply be <i>deleted</i>
and will automatically detach itself from the <tt>cDevice</tt>.
<!--X1.1.6--></td></tr></table>
<!--X1.1.5--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
<hr><h2>The On Screen Display</h2>
<center><i><b>Express yourself</b></i></center><p>
@ -1151,9 +1150,8 @@ MyOsd-&gt;Create(...);
to define an actual OSD drawing area (see VDR/osdbase.h for the declarations
of these functions, and VDR/osd.c to see how VDR opens the OSD and sets up
its windows and color depths).
<!--X1.1.5--></td></tr></table>
<!--X1.1.6--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
<!--X1.1.6--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
<hr><h2>Devices</h2>
<center><i><b>Expanding the possibilities</b></i></center><p>
@ -1189,12 +1187,16 @@ the <tt>cDvbDevice</tt>, which is used to access the DVB PCI cards.
If the new device can receive, it most likely needs to provide a way of
selecting which channel it shall tune to:
<!--X1.1.9--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
virtual bool SetChannelDevice(const cChannel *Channel);
virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1, bool *NeedsSwitchChannel = NULL);
virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView);
</pre></td></tr></table><p>
This function will be called with the desired channel and shall return whether
tuning to it was successful.
These functions will be called with the desired channel and shall return whether
this device can provide the requested channel and whether tuning to it was successful,
repectively.
<!--X1.1.9--></td></tr></table>
<p>
<b>Recording</b>
<p>
@ -1226,7 +1228,7 @@ to indicate this to VDR.
<p>
The functions to implement replaying capabilites are
<!--X1.1.7--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
<!--X1.1.7--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
virtual bool HasDecoder(void) const;
virtual bool SetPlayMode(ePlayMode PlayMode);
@ -1250,7 +1252,7 @@ virtual void SetVideoFormat(bool VideoFormat16_9);
virtual void SetVolumeDevice(int Volume);
</pre></td></tr></table><p>
<!--X1.1.8--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
<!--X1.1.8--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
<p>
<b>On Screen Display</b>
<p>

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: config.c 1.104 2002/08/11 11:35:18 kls Exp $
* $Id: config.c 1.105 2002/09/04 13:45:56 kls Exp $
*/
#include "config.h"
@ -293,30 +293,6 @@ bool cChannel::Save(FILE *f)
return fprintf(f, ToText()) > 0;
}
bool cChannel::Switch(cDevice *Device, bool Log)
{
if (!Device)
Device = cDevice::PrimaryDevice();
if (!(Device->IsPrimaryDevice() && Device->Receiving()) && !groupSep) {
if (Log)
isyslog("switching to channel %d", number);
for (int i = 3; i--;) {
switch (Device->SetChannel(this)) {
case scrOk: return true;
case scrNoTransfer: if (Interface)
Interface->Error(tr("Can't start Transfer Mode!"));
return false;
case scrFailed: break; // loop will retry
}
esyslog("retrying");
}
return false;
}
if (Device->IsPrimaryDevice() && Device->Receiving())
Interface->Error(tr("Channel locked (recording)!"));
return false;
}
// -- cTimer -----------------------------------------------------------------
char *cTimer::buffer = NULL;
@ -836,10 +812,10 @@ cChannel *cChannels::GetByServiceID(unsigned short ServiceId)
return NULL;
}
bool cChannels::SwitchTo(int Number, cDevice *Device)
bool cChannels::SwitchTo(int Number)
{
cChannel *channel = GetByNumber(Number);
return channel && channel->Switch(Device);
return channel && cDevice::PrimaryDevice()->SwitchChannel(channel, true);
}
const char *cChannels::GetChannelNameByNumber(int Number)
@ -944,7 +920,7 @@ bool cSetupLine::operator< (const cListObject &ListObject)
{
const cSetupLine *sl = (cSetupLine *)&ListObject;
if (!plugin && !sl->plugin)
return strcasecmp(name, sl->name) < 0;
return strcasecmp(name, sl->name) < 0;
if (!plugin)
return true;
if (!sl->plugin)

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: config.h 1.125 2002/08/28 19:26:56 kls Exp $
* $Id: config.h 1.126 2002/09/04 11:04:55 kls Exp $
*/
#ifndef __CONFIG_H
@ -119,7 +119,6 @@ public:
const char *ToText(void);
bool Parse(const char *s);
bool Save(FILE *f);
bool Switch(cDevice *Device = NULL, bool Log = true);
};
enum eTimerActive { taInactive = 0,
@ -198,6 +197,8 @@ public:
bool Accepts(in_addr_t Address);
};
#define CACONFBASE 100
class cCaDefinition : public cListObject {
private:
int number;
@ -296,7 +297,7 @@ public:
cChannel *GetByNumber(int Number);
cChannel *GetByServiceID(unsigned short ServiceId);
const char *GetChannelNameByNumber(int Number);
bool SwitchTo(int Number, cDevice *Device = NULL);
bool SwitchTo(int Number);
int MaxNumber(void) { return maxNumber; }
};

128
device.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: device.c 1.13 2002/08/25 09:16:51 kls Exp $
* $Id: device.c 1.14 2002/09/04 17:26:02 kls Exp $
*/
#include "device.h"
@ -12,6 +12,7 @@
#include <sys/ioctl.h>
#include <sys/mman.h>
#include "eit.h"
#include "i18n.h"
#include "player.h"
#include "receiver.h"
#include "status.h"
@ -101,11 +102,6 @@ bool cDevice::SetPrimaryDevice(int n)
return false;
}
bool cDevice::CanBeReUsed(int Frequency, int Vpid)
{
return false;
}
bool cDevice::HasDecoder(void) const
{
return false;
@ -116,31 +112,26 @@ cOsdBase *cDevice::NewOsd(int x, int y)
return NULL;
}
cDevice *cDevice::GetDevice(int Ca, int Priority, int Frequency, int Vpid, bool *ReUse)
cDevice *cDevice::GetDevice(int Index)
{
return (0 <= Index && Index < numDevices) ? device[Index] : NULL;
}
cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool *NeedsSwitchChannel)
{
if (ReUse)
*ReUse = false;
cDevice *d = NULL;
int Provides[MAXDEVICES];
// Check which devices provide Ca:
for (int i = 0; i < numDevices; i++) {
if ((Provides[i] = device[i]->ProvidesCa(Ca)) != 0) { // this device is basicly able to do the job
//XXX+ dsyslog("GetDevice: %d %d %d %5d %5d", i, device[i]->HasDecoder(), device[i]->Receiving(), Frequency, device[i]->frequency);//XXX
if (device[i]->CanBeReUsed(Frequency, Vpid)) {
d = device[i];
if (ReUse)
*ReUse = true;
break;
}
if (Priority > device[i]->Priority() // Priority is high enough to use this device
&& (!d // we don't have a device yet, or...
|| device[i]->Priority() < d->Priority() // ...this one has an even lower Priority
|| (device[i]->Priority() == d->Priority() // ...same Priority...
&& Provides[i] < Provides[d->CardIndex()] // ...but this one provides fewer Ca values
)
)
bool nsc;
if (device[i]->ProvidesChannel(Channel, Priority, &nsc) // this device is basicly able to do the job
&& (!d // we don't have a device yet, or...
|| device[i]->Priority() < d->Priority() // ...this one has an even lower Priority, or...
|| device[i]->Priority() == d->Priority() // ...same Priority...
&& device[i]->ProvidesCa(Channel->ca) < d->ProvidesCa(Channel->ca) // ...but this one provides fewer Ca values
)
d = device[i];
) {
d = device[i];
if (NeedsSwitchChannel)
*NeedsSwitchChannel = nsc;
}
}
/*XXX+ too complex with multiple recordings per device
@ -189,9 +180,18 @@ void cDevice::SetVideoFormat(bool VideoFormat16_9)
{
}
//#define PRINTPIDS(s) { char b[500]; char *q = b; q += sprintf(q, "%d %s ", CardIndex(), s); for (int i = 0; i < MAXPIDHANDLES; i++) q += sprintf(q, " %s%4d %d", i == ptOther ? "* " : "", pidHandles[i].pid, pidHandles[i].used); dsyslog(b); } //XXX+
//#define PRINTPIDS(s) { char b[500]; char *q = b; q += sprintf(q, "%d %s ", CardIndex(), s); for (int i = 0; i < MAXPIDHANDLES; i++) q += sprintf(q, " %s%4d %d", i == ptOther ? "* " : "", pidHandles[i].pid, pidHandles[i].used); dsyslog(b); }
#define PRINTPIDS(s)
bool cDevice::HasPid(int Pid)
{
for (int i = 0; i < MAXPIDHANDLES; i++) {
if (pidHandles[i].pid == Pid)
return true;
}
return false;
}
bool cDevice::AddPid(int Pid, ePidType PidType)
{
if (Pid) {
@ -207,10 +207,10 @@ bool cDevice::AddPid(int Pid, ePidType PidType)
// The Pid is already in use
if (++pidHandles[n].used == 2 && n <= ptTeletext) {
// It's a special PID that may have to be switched into "tap" mode
PRINTPIDS("A");//XXX+
PRINTPIDS("A");
return SetPid(&pidHandles[n], n, true);
}
PRINTPIDS("a");//XXX+
PRINTPIDS("a");
return true;
}
else if (PidType < ptOther) {
@ -226,7 +226,7 @@ bool cDevice::AddPid(int Pid, ePidType PidType)
if (n >= 0) {
pidHandles[n].pid = Pid;
pidHandles[n].used = 1;
PRINTPIDS("C");//XXX+
PRINTPIDS("C");
return SetPid(&pidHandles[n], n, true);
}
}
@ -238,14 +238,15 @@ void cDevice::DelPid(int Pid)
if (Pid) {
for (int i = 0; i < MAXPIDHANDLES; i++) {
if (pidHandles[i].pid == Pid) {
PRINTPIDS("D");
if (--pidHandles[i].used < 2) {
SetPid(&pidHandles[i], i, false);
if (pidHandles[i].used == 0) {
pidHandles[i].handle = -1;
pidHandles[i].pid = 0;
}
pidHandles[i].handle = -1;
pidHandles[i].pid = 0;
}
}
PRINTPIDS("D");//XXX+
PRINTPIDS("E");
}
}
}
@ -256,11 +257,35 @@ bool cDevice::SetPid(cPidHandle *Handle, int Type, bool On)
return false;
}
eSetChannelResult cDevice::SetChannel(const cChannel *Channel)
bool cDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *NeedsSwitchChannel)
{
return false;
}
bool cDevice::SwitchChannel(const cChannel *Channel, bool LiveView)
{
if (LiveView)
isyslog("switching to channel %d", Channel->number);
for (int i = 3; i--;) {
switch (SetChannel(Channel, LiveView)) {
case scrOk: return true;
case scrNotAvailable: return false;
case scrNoTransfer: if (Interface)
Interface->Error(tr("Can't start Transfer Mode!"));
return false;
case scrFailed: break; // loop will retry
}
esyslog("retrying");
}
return false;
}
eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
{
cStatus::MsgChannelSwitch(this, 0);
StopReplay();
if (LiveView)
StopReplay();
// Must set this anyway to avoid getting stuck when switching through
// channels with 'Up' and 'Down' keys:
@ -269,7 +294,7 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel)
// If this card can't receive this channel, we must not actually switch
// the channel here, because that would irritate the driver when we
// start replaying in Transfer Mode immediately after switching the channel:
bool NeedsTransferMode = (IsPrimaryDevice() && !ProvidesCa(Channel->ca));
bool NeedsTransferMode = (LiveView && IsPrimaryDevice() && !ProvidesChannel(Channel, Setup.PrimaryLimit));
eSetChannelResult Result = scrOk;
@ -277,13 +302,18 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel)
// use the card that actually can receive it and transfer data from there:
if (NeedsTransferMode) {
cDevice *CaDevice = GetDevice(Channel->ca, 0);
if (CaDevice && !CaDevice->Receiving() && CaDevice->SetChannel(Channel) == scrOk)
cControl::Launch(new cTransferControl(CaDevice, Channel->vpid, Channel->apid1, 0, 0, 0));//XXX+
bool NeedsSwitchChannel = false;
cDevice *CaDevice = GetDevice(Channel, 0, &NeedsSwitchChannel);
if (CaDevice) {
if (!NeedsSwitchChannel || CaDevice->SetChannel(Channel, false) == scrOk) // calling SetChannel() directly, not SwitchChannel()!
cControl::Launch(new cTransferControl(CaDevice, Channel->vpid, Channel->apid1, 0, 0, 0));//XXX+
else
Result = scrNoTransfer;
}
else
Result = scrNoTransfer;
Result = scrNotAvailable;
}
else if (!SetChannelDevice(Channel))
else if (!SetChannelDevice(Channel, LiveView))
Result = scrFailed;
if (IsPrimaryDevice())
@ -294,7 +324,7 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel)
return Result;
}
bool cDevice::SetChannelDevice(const cChannel *Channel)
bool cDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
{
return false;
}
@ -357,10 +387,6 @@ bool cDevice::Replaying(void)
bool cDevice::AttachPlayer(cPlayer *Player)
{
if (Receiving()) {
esyslog("ERROR: attempt to attach a cPlayer while receiving on device %d - ignored", CardIndex() + 1);
return false;
}
if (HasDecoder()) {
if (player)
Detach(player);
@ -419,9 +445,7 @@ int cDevice::PlayAudio(const uchar *Data, int Length)
int cDevice::Priority(void)
{
if (IsPrimaryDevice() && !Receiving())
return Setup.PrimaryLimit - 1;
int priority = DEFAULTPRIORITY;
int priority = IsPrimaryDevice() ? Setup.PrimaryLimit - 1 : DEFAULTPRIORITY;
for (int i = 0; i < MAXRECEIVERS; i++) {
if (receiver[i])
priority = max(receiver[i]->priority, priority);
@ -551,12 +575,10 @@ int cDevice::GetTSPacket(uchar *Data)
bool cDevice::AttachReceiver(cReceiver *Receiver)
{
//XXX+ check for same transponder???
if (!Receiver)
return false;
if (Receiver->device == this)
return true;
StopReplay();
for (int i = 0; i < MAXRECEIVERS; i++) {
if (!receiver[i]) {
for (int n = 0; n < MAXRECEIVEPIDS; n++)

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: device.h 1.10 2002/08/25 09:16:34 kls Exp $
* $Id: device.h 1.11 2002/09/04 11:33:12 kls Exp $
*/
#ifndef __DEVICE_H
@ -24,7 +24,7 @@
#define TS_SYNC_BYTE 0x47
#define PID_MASK_HI 0x1F
enum eSetChannelResult { scrOk, scrNoTransfer, scrFailed };
enum eSetChannelResult { scrOk, scrNotAvailable, scrNoTransfer, scrFailed };
enum ePlayMode { pmNone, // audio/video from decoder
pmAudioVideo, // audio/video from player
@ -69,19 +69,13 @@ public:
// 1...numDevices) and returns true if this was possible.
static cDevice *PrimaryDevice(void) { return primaryDevice; }
// Returns the primary device.
static cDevice *GetDevice(int Ca, int Priority, int Frequency = 0, int Vpid = 0, bool *ReUse = NULL);
// Selects a free device, avoiding the primaryDevice if possible.
// If Ca is not 0, the device with the given number will be returned
// in case Ca is <= MAXDEVICES, or the device that provides the given
// value in its caCaps.
// If there is a device that is already receiving and can be re-used to
// receive another data stream, that device will be returned.
// If all devices are currently receiving, the one receiving with the
// lowest priority (if any) that is lower than the given Priority
// will be returned.
// If ReUse is given, the caller will be informed whether the device can be re-used
// for a new recording. If ReUse returns 'true', the caller must NOT switch the channel
// (the device is already properly tuned). Otherwise the caller MUST switch the channel.
static cDevice *GetDevice(int Index);
// Returns the device with the Index (if Index is in the range
// 0..numDevices-1, NULL otherwise).
static cDevice *GetDevice(const cChannel *Channel, int Priority = -1, bool *NeedsSwitchChannel = NULL);
// Returns a device that is able to receive the given Channel at the
// given Priority (see ProvidesChannel() for more information on how
// priorities are handled, and the meaning of NeedsSwitchChannel).
static void SetCaCaps(int Index = -1);
// Sets the CaCaps of the given device according to the Setup data.
// By default the CaCaps of all devices are set.
@ -115,18 +109,13 @@ public:
bool IsPrimaryDevice(void) const { return this == primaryDevice; }
int CardIndex(void) const { return cardIndex; }
// Returns the card index of this device (0 ... MAXDEVICES - 1).
virtual int ProvidesCa(int Ca);
//XXX TODO temporarily made this function virtual - until a general
//XXX mechanism has been implemented
int ProvidesCa(int Ca);
// Checks whether this device provides the given value in its
// caCaps. Returns 0 if the value is not provided, 1 if only this
// value is provided, and > 1 if this and other values are provided.
// If the given value is equal to the number of this device,
// 1 is returned. If it is 0 (FTA), 1 plus the number of other values
// in caCaps is returned.
virtual bool CanBeReUsed(int Frequency, int Vpid);//XXX TODO make it more abstract
// Tells whether this device is already receiving and allows another
// receiver with the given settings to be attached to it.
virtual bool HasDecoder(void) const;
// Tells whether this device has an MPEG decoder.
@ -145,10 +134,33 @@ public:
protected:
int currentChannel;
public:
eSetChannelResult SetChannel(const cChannel *Channel);
virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1, bool *NeedsSwitchChannel = NULL);
// Returns true if this device can provide the given channel.
// In case the device has cReceivers attached to it or it is the primary
// device, Priority is used to decide whether the caller's request can
// be honored.
// The special Priority value -1 will tell the caller whether this device
// is principally able to provide the given Channel, regardless of any
// attached cReceivers.
// If NeedsSwitchChannel is given, the resulting value in it will tell the
// caller whether or not it shall call SwitchChannel to actually switch the
// device to the desired channel. If NeedsSwitchChannel returns false, the
// caller must not call SwitchChannel, since there are receivers attached
// to the device and it is already switched to the given channel. Note
// that the return value in NeedsSwitchChannel is only meaningful if the
// function itself actually returns true.
// The default implementation always returns false, so a derived cDevice
// class that can provide channels must implement this function.
bool SwitchChannel(const cChannel *Channel, bool LiveView);
// Switches the device to the given Channel, initiating transfer mode
// if necessary.
private:
eSetChannelResult SetChannel(const cChannel *Channel, bool LiveView);
// Sets the device to the given channel (general setup).
virtual bool SetChannelDevice(const cChannel *Channel);
protected:
virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView);
// Sets the device to the given channel (actual physical setup).
public:
static int CurrentChannel(void) { return primaryDevice ? primaryDevice->currentChannel : 0; }
// Returns the number of the current channel on the primary device.
int Channel(void) { return currentChannel; }
@ -169,6 +181,8 @@ protected:
cPidHandle(void) { pid = used = 0; handle = -1; }
};
cPidHandle pidHandles[MAXPIDHANDLES];
bool HasPid(int Pid);
// Returns true if this device is currently receiving the given PID.
bool AddPid(int Pid, ePidType PidType = ptOther);
// Adds a PID to the set of PIDs this device shall receive.
void DelPid(int Pid);
@ -263,12 +277,12 @@ public:
private:
cReceiver *receiver[MAXRECEIVERS];
int ca;
int CanShift(int Ca, int Priority, int UsedCards = 0);
protected:
int Priority(void);
// Returns the priority of the current receiving session (0..MAXPRIORITY),
// or -1 if no receiver is currently active. The primary device will
// always return at least Setup.PrimaryLimit-1.
int CanShift(int Ca, int Priority, int UsedCards = 0);
protected:
virtual bool OpenDvr(void);
// Opens the DVR of this device and prepares it to deliver a Transport
// Stream for use in a cReceiver.

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.8 2002/08/25 09:20:53 kls Exp $
* $Id: dvbdevice.c 1.9 2002/09/04 13:46:03 kls Exp $
*/
#include "dvbdevice.h"
@ -20,10 +20,12 @@ extern "C" {
#include <linux/videodev.h>
#ifdef NEWSTRUCT
#include <linux/dvb/audio.h>
#include <linux/dvb/dmx.h>
#include <linux/dvb/frontend.h>
#include <linux/dvb/video.h>
#else
#include <ost/audio.h>
#include <ost/dmx.h>
#include <ost/sec.h>
#include <ost/video.h>
#endif
@ -35,8 +37,6 @@ extern "C" {
#include "status.h"
#include "transfer.h"
#define MAXDVBDEVICES 4
#define DEV_VIDEO "/dev/video"
#ifdef NEWSTRUCT
#define DEV_DVB_ADAPTER "/dev/dvb/adapter"
@ -91,10 +91,10 @@ cDvbDevice::cDvbDevice(int n)
fd_osd = DvbOpen(DEV_DVB_OSD, n, O_RDWR);
fd_video = DvbOpen(DEV_DVB_VIDEO, n, O_RDWR | O_NONBLOCK);
fd_audio = DvbOpen(DEV_DVB_AUDIO, n, O_RDWR | O_NONBLOCK);
#ifndef NEWSTRUCT
// Devices that are only present on DVB-S cards:
fd_sec = DvbOpen(DEV_DVB_SEC, n, O_RDWR);
#endif
@ -179,15 +179,6 @@ void cDvbDevice::MakePrimaryDevice(bool On)
cDvbOsd::SetDvbDevice(On ? this : NULL);
}
bool cDvbDevice::CanBeReUsed(int Frequency, int Vpid)
{
return Receiving() // to be reused the DVB device must already be receiving...
&& frequency == Frequency // ...and tuned to the requested frequency...
&& (!HasDecoder() // ...and either be a "budget card" which can receive multiple channels...
|| pidHandles[ptVideo].pid == Vpid // ...or be a "full featured card" that's already tuned to the requested video PID
);
}
bool cDvbDevice::HasDecoder(void) const
{
return fd_video >= 0 && fd_audio >= 0;
@ -235,10 +226,10 @@ bool cDvbDevice::GrabImage(const char *FileName, bool Jpeg, int Quality, int Siz
mem1[0] = tmp;
mem1 += 3;
}
if (Quality < 0)
Quality = 255; //XXX is this 'best'???
isyslog("grabbing to %s (%s %d %d %d)", FileName, Jpeg ? "JPEG" : "PNM", Quality, vm.width, vm.height);
FILE *f = fopen(FileName, "wb");
if (f) {
@ -253,11 +244,11 @@ bool cDvbDevice::GrabImage(const char *FileName, bool Jpeg, int Quality, int Siz
cinfo.image_height = vm.height;
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;
jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, Quality, true);
jpeg_start_compress(&cinfo, true);
int rs = vm.width * 3;
JSAMPROW rp[vm.height];
for (int k = 0; k < vm.height; k++)
@ -313,6 +304,14 @@ bool cDvbDevice::SetPid(cPidHandle *Handle, int Type, bool On)
else {
CHECK(ioctl(Handle->handle, DMX_STOP));
if (Handle->used == 0) {
dmxPesFilterParams pesFilterParams;
memset(&pesFilterParams, 0, sizeof(pesFilterParams));
pesFilterParams.pid = 0x1FFF;
pesFilterParams.input = DMX_IN_FRONTEND;
pesFilterParams.output = DMX_OUT_DECODER;
pesFilterParams.pesType = PesTypes[Type < ptOther ? Type : ptOther];
pesFilterParams.flags = DMX_IMMEDIATE_START;
CHECK(ioctl(Handle->handle, DMX_SET_PES_FILTER, &pesFilterParams));
close(Handle->handle);
Handle->handle = -1;
return true;
@ -339,8 +338,58 @@ bool cDvbDevice::SetPid(cPidHandle *Handle, int Type, bool On)
return true;
}
bool cDvbDevice::SetChannelDevice(const cChannel *Channel)
bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *NeedsSwitchChannel)
{
bool result = false;
bool hasPriority = Priority < 0 || Priority > this->Priority();
bool needsSwitchChannel = true;
if (ProvidesCa(Channel->ca)) {
if (Receiving()) {
if (frequency == Channel->frequency) {
needsSwitchChannel = false;
if (!HasPid(Channel->vpid)) {
if (Channel->ca > CACONFBASE) {
needsSwitchChannel = true;
result = hasPriority;
}
else if (!HasDecoder())
result = true; // if it has no decoder it can't be the primary device
else {
#define DVB_DRIVER_VERSION 2002090101 //XXX+
#define MIN_DVB_DRIVER_VERSION_FOR_TIMESHIFT 2002090101
#ifdef DVB_DRIVER_VERSION
#if (DVB_DRIVER_VERSION >= MIN_DVB_DRIVER_VERSION_FOR_TIMESHIFT)
if (pidHandles[ptVideo].used)
needsSwitchChannel = true; // to have it turn off the live PIDs
result = !IsPrimaryDevice() || Priority >= Setup.PrimaryLimit;
#endif
#else
#warning "DVB_DRIVER_VERSION not defined - time shift with only one DVB device disabled!"
#endif
}
}
else
result = !IsPrimaryDevice() || Priority >= Setup.PrimaryLimit;
}
else
result = hasPriority;
}
else
result = hasPriority;
}
if (NeedsSwitchChannel)
*NeedsSwitchChannel = needsSwitchChannel;
return result;
}
bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
{
#if (DVB_DRIVER_VERSION < MIN_DVB_DRIVER_VERSION_FOR_TIMESHIFT)
if (HasDecoder())
LiveView = true;
#endif
// Avoid noise while switching:
if (HasDecoder()) {
@ -357,204 +406,209 @@ bool cDvbDevice::SetChannelDevice(const cChannel *Channel)
// Turn off current PIDs:
if (HasDecoder()) {
if (HasDecoder() && (LiveView || pidHandles[ptVideo].pid == Channel->vpid)) {
DelPid(pidHandles[ptVideo].pid);
DelPid(pidHandles[ptAudio].pid);
DelPid(pidHandles[ptTeletext].pid);
DelPid(pidHandles[ptDolby].pid);
}
if (frequency != Channel->frequency || Channel->ca > CACONFBASE) { // CA channels can only be decrypted in "live" mode
#ifdef NEWSTRUCT
dvb_frontend_parameters Frontend;
dvb_frontend_parameters Frontend;
#else
FrontendParameters Frontend;
FrontendParameters Frontend;
#endif
memset(&Frontend, 0, sizeof(Frontend));
memset(&Frontend, 0, sizeof(Frontend));
switch (frontendType) {
case FE_QPSK: { // DVB-S
switch (frontendType) {
case FE_QPSK: { // DVB-S
// Frequency offsets:
// Frequency offsets:
unsigned int freq = Channel->frequency;
int tone = SEC_TONE_OFF;
unsigned int freq = Channel->frequency;
int tone = SEC_TONE_OFF;
if (freq < (unsigned int)Setup.LnbSLOF) {
freq -= Setup.LnbFrequLo;
tone = SEC_TONE_OFF;
if (freq < (unsigned int)Setup.LnbSLOF) {
freq -= Setup.LnbFrequLo;
tone = SEC_TONE_OFF;
}
else {
freq -= Setup.LnbFrequHi;
tone = SEC_TONE_ON;
}
#ifdef NEWSTRUCT
Frontend.frequency = freq * 1000UL;
Frontend.inversion = INVERSION_AUTO;
Frontend.u.qpsk.symbol_rate = Channel->srate * 1000UL;
Frontend.u.qpsk.fec_inner = FEC_AUTO;
#else
Frontend.Frequency = freq * 1000UL;
Frontend.Inversion = INVERSION_AUTO;
Frontend.u.qpsk.SymbolRate = Channel->srate * 1000UL;
Frontend.u.qpsk.FEC_inner = FEC_AUTO;
#endif
int volt = (Channel->polarization == 'v' || Channel->polarization == 'V') ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18;
// DiSEqC:
#ifdef NEWSTRUCT
struct dvb_diseqc_master_cmd cmd = { {0xE0, 0x10, 0x38, 0xF0, 0x00, 0x00}, 4};
cmd.msg[3] = 0xF0 | (((Channel->diseqc * 4) & 0x0F) | (tone == SEC_TONE_ON ? 1 : 0) | (volt == SEC_VOLTAGE_18 ? 2 : 0));
if (Setup.DiSEqC)
CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_OFF));
CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, volt));
if (Setup.DiSEqC) {
usleep(15 * 1000);
CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_MASTER_CMD, &cmd));
usleep(15 * 1000);
CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_BURST, (Channel->diseqc / 4) % 2 ? SEC_MINI_B : SEC_MINI_A));
usleep(15 * 1000);
}
CHECK(ioctl(fd_frontend, FE_SET_TONE, tone));
#else
secCommand scmd;
scmd.type = 0;
scmd.u.diseqc.addr = 0x10;
scmd.u.diseqc.cmd = 0x38;
scmd.u.diseqc.numParams = 1;
scmd.u.diseqc.params[0] = 0xF0 | ((Channel->diseqc * 4) & 0x0F) | (tone == SEC_TONE_ON ? 1 : 0) | (volt == SEC_VOLTAGE_18 ? 2 : 0);
secCmdSequence scmds;
scmds.voltage = volt;
scmds.miniCommand = SEC_MINI_NONE;
scmds.continuousTone = tone;
scmds.numCommands = Setup.DiSEqC ? 1 : 0;
scmds.commands = &scmd;
CHECK(ioctl(fd_sec, SEC_SEND_SEQUENCE, &scmds));
#endif
}
else {
freq -= Setup.LnbFrequHi;
tone = SEC_TONE_ON;
break;
case FE_QAM: { // DVB-C
// Frequency and symbol rate:
#ifdef NEWSTRUCT
Frontend.frequency = Channel->frequency * 1000000UL;
Frontend.inversion = INVERSION_AUTO;
Frontend.u.qam.symbol_rate = Channel->srate * 1000UL;
Frontend.u.qam.fec_inner = FEC_AUTO;
Frontend.u.qam.modulation = QAM_64;
#else
Frontend.Frequency = Channel->frequency * 1000000UL;
Frontend.Inversion = INVERSION_AUTO;
Frontend.u.qam.SymbolRate = Channel->srate * 1000UL;
Frontend.u.qam.FEC_inner = FEC_AUTO;
Frontend.u.qam.QAM = QAM_64;
#endif
}
break;
case FE_OFDM: { // DVB-T
// Frequency and OFDM paramaters:
#ifdef NEWSTRUCT
Frontend.frequency = freq * 1000UL;
Frontend.inversion = INVERSION_AUTO;
Frontend.u.qpsk.symbol_rate = Channel->srate * 1000UL;
Frontend.u.qpsk.fec_inner = FEC_AUTO;
Frontend.frequency = Channel->frequency * 1000UL;
Frontend.inversion = INVERSION_AUTO;
Frontend.u.ofdm.bandwidth=BANDWIDTH_8_MHZ;
Frontend.u.ofdm.code_rate_HP = FEC_2_3;
Frontend.u.ofdm.code_rate_LP = FEC_1_2;
Frontend.u.ofdm.constellation = QAM_64;
Frontend.u.ofdm.transmission_mode = TRANSMISSION_MODE_2K;
Frontend.u.ofdm.guard_interval = GUARD_INTERVAL_1_32;
Frontend.u.ofdm.hierarchy_information = HIERARCHY_NONE;
#else
Frontend.Frequency = freq * 1000UL;
Frontend.Inversion = INVERSION_AUTO;
Frontend.u.qpsk.SymbolRate = Channel->srate * 1000UL;
Frontend.u.qpsk.FEC_inner = FEC_AUTO;
Frontend.Frequency = Channel->frequency * 1000UL;
Frontend.Inversion = INVERSION_AUTO;
Frontend.u.ofdm.bandWidth=BANDWIDTH_8_MHZ;
Frontend.u.ofdm.HP_CodeRate=FEC_2_3;
Frontend.u.ofdm.LP_CodeRate=FEC_1_2;
Frontend.u.ofdm.Constellation=QAM_64;
Frontend.u.ofdm.TransmissionMode=TRANSMISSION_MODE_2K;
Frontend.u.ofdm.guardInterval=GUARD_INTERVAL_1_32;
Frontend.u.ofdm.HierarchyInformation=HIERARCHY_NONE;
#endif
int volt = (Channel->polarization == 'v' || Channel->polarization == 'V') ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18;
// DiSEqC:
#ifdef NEWSTRUCT
struct dvb_diseqc_master_cmd cmd = { {0xE0, 0x10, 0x38, 0xF0, 0x00, 0x00}, 4};
cmd.msg[3] = 0xF0 | (((Channel->diseqc * 4) & 0x0F) | (tone == SEC_TONE_ON ? 1 : 0) | (volt == SEC_VOLTAGE_18 ? 2 : 0));
if (Setup.DiSEqC)
CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_OFF));
CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, volt));
if (Setup.DiSEqC) {
usleep(15 * 1000);
CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_MASTER_CMD, &cmd));
usleep(15 * 1000);
CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_BURST, (Channel->diseqc / 4) % 2 ? SEC_MINI_B : SEC_MINI_A));
usleep(15 * 1000);
}
CHECK(ioctl(fd_frontend, FE_SET_TONE, tone));
#else
secCommand scmd;
scmd.type = 0;
scmd.u.diseqc.addr = 0x10;
scmd.u.diseqc.cmd = 0x38;
scmd.u.diseqc.numParams = 1;
scmd.u.diseqc.params[0] = 0xF0 | ((Channel->diseqc * 4) & 0x0F) | (tone == SEC_TONE_ON ? 1 : 0) | (volt == SEC_VOLTAGE_18 ? 2 : 0);
break;
default:
esyslog("ERROR: attempt to set channel with unknown DVB frontend type");
return false;
}
secCmdSequence scmds;
scmds.voltage = volt;
scmds.miniCommand = SEC_MINI_NONE;
scmds.continuousTone = tone;
scmds.numCommands = Setup.DiSEqC ? 1 : 0;
scmds.commands = &scmd;
#ifdef NEWSTRUCT
// Discard stale events:
CHECK(ioctl(fd_sec, SEC_SEND_SEQUENCE, &scmds));
#endif
for (;;) {
dvb_frontend_event event;
if (ioctl(fd_frontend, FE_GET_EVENT, &event) < 0)
break;
}
break;
case FE_QAM: { // DVB-C
#endif
// Frequency and symbol rate:
// Tuning:
CHECK(ioctl(fd_frontend, FE_SET_FRONTEND, &Frontend));
// Wait for channel lock:
#ifdef NEWSTRUCT
Frontend.frequency = Channel->frequency * 1000000UL;
Frontend.inversion = INVERSION_AUTO;
Frontend.u.qam.symbol_rate = Channel->srate * 1000UL;
Frontend.u.qam.fec_inner = FEC_AUTO;
Frontend.u.qam.modulation = QAM_64;
#else
Frontend.Frequency = Channel->frequency * 1000000UL;
Frontend.Inversion = INVERSION_AUTO;
Frontend.u.qam.SymbolRate = Channel->srate * 1000UL;
Frontend.u.qam.FEC_inner = FEC_AUTO;
Frontend.u.qam.QAM = QAM_64;
#endif
FrontendStatus status = FrontendStatus(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);
}
break;
case FE_OFDM: { // DVB-T
// Frequency and OFDM paramaters:
#ifdef NEWSTRUCT
Frontend.frequency = Channel->frequency * 1000UL;
Frontend.inversion = INVERSION_AUTO;
Frontend.u.ofdm.bandwidth=BANDWIDTH_8_MHZ;
Frontend.u.ofdm.code_rate_HP = FEC_2_3;
Frontend.u.ofdm.code_rate_LP = FEC_1_2;
Frontend.u.ofdm.constellation = QAM_64;
Frontend.u.ofdm.transmission_mode = TRANSMISSION_MODE_2K;
Frontend.u.ofdm.guard_interval = GUARD_INTERVAL_1_32;
Frontend.u.ofdm.hierarchy_information = HIERARCHY_NONE;
if (!(status & FE_HAS_LOCK)) {
esyslog("ERROR: channel %d not locked on DVB card %d!", Channel->number, CardIndex() + 1);
if (IsPrimaryDevice())
cThread::RaisePanic();
return false;
}
#else
Frontend.Frequency = Channel->frequency * 1000UL;
Frontend.Inversion = INVERSION_AUTO;
Frontend.u.ofdm.bandWidth=BANDWIDTH_8_MHZ;
Frontend.u.ofdm.HP_CodeRate=FEC_2_3;
Frontend.u.ofdm.LP_CodeRate=FEC_1_2;
Frontend.u.ofdm.Constellation=QAM_64;
Frontend.u.ofdm.TransmissionMode=TRANSMISSION_MODE_2K;
Frontend.u.ofdm.guardInterval=GUARD_INTERVAL_1_32;
Frontend.u.ofdm.HierarchyInformation=HIERARCHY_NONE;
#endif
}
break;
default:
esyslog("ERROR: attempt to set channel with unknown DVB frontend type");
return false;
}
#ifdef NEWSTRUCT
// Discard stale events:
for (;;) {
dvb_frontend_event event;
if (ioctl(fd_frontend, FE_GET_EVENT, &event) < 0)
break;
}
#endif
// Tuning:
CHECK(ioctl(fd_frontend, FE_SET_FRONTEND, &Frontend));
// Wait for channel lock:
#ifdef NEWSTRUCT
FrontendStatus status = FrontendStatus(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);
}
if (!(status & FE_HAS_LOCK)) {
esyslog("ERROR: channel %d not locked on DVB card %d!", Channel->number, CardIndex() + 1);
if (IsPrimaryDevice())
cThread::RaisePanic();
return false;
}
#else
if (cFile::FileReady(fd_frontend, 5000)) {
FrontendEvent event;
if (ioctl(fd_frontend, FE_GET_EVENT, &event) >= 0) {
if (event.type != FE_COMPLETION_EV) {
esyslog("ERROR: channel %d not sync'ed on DVB card %d!", Channel->number, CardIndex() + 1);
if (IsPrimaryDevice())
cThread::RaisePanic();
return false;
if (cFile::FileReady(fd_frontend, 5000)) {
FrontendEvent event;
if (ioctl(fd_frontend, FE_GET_EVENT, &event) >= 0) {
if (event.type != FE_COMPLETION_EV) {
esyslog("ERROR: channel %d not sync'ed on DVB card %d!", Channel->number, CardIndex() + 1);
if (IsPrimaryDevice())
cThread::RaisePanic();
return false;
}
}
else
esyslog("ERROR in frontend get event (channel %d, card %d): %m", Channel->number, CardIndex() + 1);
}
else
esyslog("ERROR in frontend get event (channel %d, card %d): %m", Channel->number, CardIndex() + 1);
}
else
esyslog("ERROR: timeout while tuning on DVB card %d", CardIndex() + 1);
esyslog("ERROR: timeout while tuning on DVB card %d", CardIndex() + 1);
#endif
frequency = Channel->frequency;
frequency = Channel->frequency;
}
// PID settings:
if (HasDecoder()) {
if (!(AddPid(Channel->vpid, ptVideo) && AddPid(Channel->apid1, ptAudio))) {//XXX+ dolby dpid1!!! (if audio plugins are attached)
esyslog("ERROR: failed to set PIDs for channel %d", Channel->number);
return false;
if (HasDecoder() && (LiveView || Channel->ca > CACONFBASE)) { // CA channels can only be decrypted in "live" mode
if (!HasPid(Channel->vpid)) {
if (!(AddPid(Channel->vpid, ptVideo) && AddPid(Channel->apid1, ptAudio))) {//XXX+ dolby dpid1!!! (if audio plugins are attached)
esyslog("ERROR: failed to set PIDs for channel %d", Channel->number);
return false;
}
if (IsPrimaryDevice())
AddPid(Channel->tpid, ptTeletext);
CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true));
CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, false));
CHECK(ioctl(fd_video, VIDEO_SET_BLANK, false));
}
if (IsPrimaryDevice())
AddPid(Channel->tpid, ptTeletext);
CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true));
}
if (HasDecoder()) {
CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, false));
CHECK(ioctl(fd_video, VIDEO_SET_BLANK, false));
else
cControl::Launch(new cTransferControl(this, Channel->vpid, Channel->apid1, 0, 0, 0));
}
// Start setting system time:

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.6 2002/08/25 09:19:34 kls Exp $
* $Id: dvbdevice.h 1.7 2002/09/04 13:31:42 kls Exp $
*/
#ifndef __DVBDEVICE_H
@ -23,6 +23,8 @@
#include "device.h"
#include "eit.h"
#define MAXDVBDEVICES 4
class cDvbDevice : public cDevice {
friend class cDvbOsd;
private:
@ -45,7 +47,6 @@ protected:
public:
cDvbDevice(int n);
virtual ~cDvbDevice();
virtual bool CanBeReUsed(int Frequency, int Vpid);
virtual bool HasDecoder(void) const;
// OSD facilities
@ -58,7 +59,9 @@ public:
private:
int frequency;
public:
virtual bool SetChannelDevice(const cChannel *Channel);
virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1, bool *NeedsSwitchChannel = NULL);
protected:
virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView);
// PID handle facilities

View File

@ -4,11 +4,12 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: eitscan.c 1.5 2002/08/11 11:11:39 kls Exp $
* $Id: eitscan.c 1.6 2002/09/04 13:32:38 kls Exp $
*/
#include "eitscan.h"
#include <stdlib.h>
#include "dvbdevice.h"
cEITScanner::cEITScanner(void)
{
@ -49,9 +50,9 @@ void cEITScanner::Process(void)
if (Setup.EPGScanTimeout && Channels.MaxNumber() > 1) {
time_t now = time(NULL);
if (now - lastScan > ScanTimeout && now - lastActivity > ActivityTimeout) {
for (int i = 0; i < MAXDEVICES; i++) {
cDevice *Device = cDevice::GetDevice(i + 1, MAXPRIORITY + 1);
if (Device) {
for (int i = 0; i < cDevice::NumDevices(); i++) {
cDevice *Device = cDevice::GetDevice(i);
if (Device && Device->CardIndex() < MAXDVBDEVICES) {
if (Device != cDevice::PrimaryDevice() || (cDevice::NumDevices() == 1 && Setup.EPGScanTimeout && now - lastActivity > Setup.EPGScanTimeout * 3600)) {
if (!(Device->Receiving() || Device->Replaying())) {
int oldCh = lastChannel;
@ -63,12 +64,12 @@ void cEITScanner::Process(void)
}
cChannel *Channel = Channels.GetByNumber(ch);
if (Channel) {
if (Channel->ca <= MAXDEVICES && !Device->ProvidesCa(Channel->ca))
break; // the channel says it explicitly needs a different card
if (!Device->ProvidesChannel(Channel))
break;
if (Channel->pnr && !TransponderScanned(Channel)) {
if (Device == cDevice::PrimaryDevice() && !currentChannel)
currentChannel = Device->Channel();
Channel->Switch(Device, false);
Device->SwitchChannel(Channel, false);
lastChannel = ch;
break;
}
@ -78,6 +79,8 @@ void cEITScanner::Process(void)
}
}
}
else
lastChannel++; // avoid hangup in case the last channel in the list is not provided by a DVB card
}
lastScan = time(NULL);
}

17
menu.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: menu.c 1.206 2002/08/25 10:56:09 kls Exp $
* $Id: menu.c 1.207 2002/09/04 13:27:13 kls Exp $
*/
#include "menu.h"
@ -508,7 +508,7 @@ eOSState cMenuChannels::Switch(void)
{
cChannel *ch = Channels.Get(Current());
if (ch)
ch->Switch();
cDevice::PrimaryDevice()->SwitchChannel(ch, true);
return osEnd;
}
@ -1054,7 +1054,7 @@ eOSState cMenuWhatsOn::Switch(void)
cMenuWhatsOnItem *item = (cMenuWhatsOnItem *)Get(Current());
if (item) {
cChannel *channel = Channels.GetByServiceID(item->eventInfo->GetServiceID());
if (channel && channel->Switch())
if (channel && cDevice::PrimaryDevice()->SwitchChannel(channel, true))
return osEnd;
}
Interface->Error(tr("Can't switch channel!"));
@ -2519,12 +2519,12 @@ bool cRecordControls::Start(cTimer *Timer)
cChannel *channel = Channels.GetByNumber(ch);
if (channel) {
bool ReUse = false;
cDevice *device = cDevice::GetDevice(channel->ca, Timer ? Timer->priority : Setup.DefaultPriority, channel->frequency, channel->vpid, &ReUse);
bool NeedsSwitchChannel = false;
cDevice *device = cDevice::GetDevice(channel, Timer ? Timer->priority : Setup.DefaultPriority, &NeedsSwitchChannel);
if (device) {
if (!ReUse) {
if (NeedsSwitchChannel) {
Stop(device);
if (!channel->Switch(device)) {
if (!device->SwitchChannel(channel, false)) {
cThread::EmergencyExit(true);
return false;
}
@ -2570,7 +2570,8 @@ void cRecordControls::Stop(cDevice *Device)
bool cRecordControls::StopPrimary(bool DoIt)
{
if (cDevice::PrimaryDevice()->Receiving()) {
cDevice *device = cDevice::GetDevice(cDevice::PrimaryDevice()->Ca(), 0);
//XXX+ disabled for the moment - might become obsolete with DVB_DRIVER_VERSION >= 2002090101
cDevice *device = NULL;//XXX cDevice::GetDevice(cDevice::PrimaryDevice()->Ca(), 0);
if (device) {
if (DoIt)
Stop(cDevice::PrimaryDevice());

View File

@ -10,7 +10,7 @@
* and interact with the Video Disk Recorder - or write a full featured
* graphical interface that sits on top of an SVDRP connection.
*
* $Id: svdrp.c 1.40 2002/08/25 10:40:46 kls Exp $
* $Id: svdrp.c 1.41 2002/09/04 10:49:42 kls Exp $
*/
#include "svdrp.h"
@ -417,13 +417,9 @@ void cSVDRP::CmdCHAN(const char *Option)
Reply(501, "Undefined channel \"%s\"", Option);
return;
}
if (Interface->Recording()) {
Reply(550, "Can't switch channel, interface is recording");
return;
}
cChannel *channel = Channels.GetByNumber(n);
if (channel) {
if (!channel->Switch()) {
if (!cDevice::PrimaryDevice()->SwitchChannel(channel, true)) {
Reply(554, "Error switching to channel \"%d\"", channel->number);
return;
}

4
vdr.c
View File

@ -22,7 +22,7 @@
*
* The project's page is at http://www.cadsoft.de/people/kls/vdr
*
* $Id: vdr.c 1.120 2002/08/16 09:54:03 kls Exp $
* $Id: vdr.c 1.121 2002/09/04 13:29:19 kls Exp $
*/
#include <getopt.h>
@ -548,7 +548,7 @@ int main(int argc, char *argv[])
int n = cDevice::CurrentChannel() + (NORMALKEY(key) == kUp ? 1 : -1);
cChannel *channel = Channels.GetByNumber(n);
if (channel)
channel->Switch();
cDevice::PrimaryDevice()->SwitchChannel(channel, true);
break;
}
// Viewing Control: