1
0
mirror of https://github.com/VDR4Arch/vdr.git synced 2023-10-10 13:36:52 +02:00

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 time changed into the future (thanks to Matthias Schniedermeyer for reporting
this one). 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 - Fixed the 'newplugin' script to make it name the target for creating the
distribution package 'dist', as stated in the PLUGINS.html documentation. distribution package 'dist', as stated in the PLUGINS.html documentation.
If you have already created a plugin source directory and Makefile you may 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. 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 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. structures and allows it to hook itself into specific areas to perform special actions.
<p> <p>
<!--X1.1.5--><table width=100%><tr><td bgcolor=#0000AA>&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.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%>
Important modifications introduced in version 1.1.6 are marked like this. Important modifications introduced in version 1.1.6 are marked like this.
<!--X1.1.6--></td></tr></table> <!--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. Important modifications introduced in version 1.1.7 are marked like this.
<!--X1.1.7--></td></tr></table> <!--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. Important modifications introduced in version 1.1.8 are marked like this.
<!--X1.1.8--></td></tr></table> <!--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> <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> <p><table><tr><td bgcolor=#F0F0F0><pre><br>
bool cPluginHello::ProcessArgs(int argc, char *argv[]) bool cPluginHello::ProcessArgs(int argc, char *argv[])
{ {
// Implement command line argument processing here if applicable. // Implement command line argument processing here if applicable.
static struct option long_options[] = { static struct option long_options[] = {
{ "aaa", required_argument, NULL, 'a' }, { "aaa", required_argument, NULL, 'a' },
{ "bbb", no_argument, NULL, 'b' }, { "bbb", no_argument, NULL, 'b' },
{ NULL } { NULL }
}; };
int c; int c;
while ((c = getopt_long(argc, argv, "a:b", long_options, NULL)) != -1) { while ((c = getopt_long(argc, argv, "a:b", long_options, NULL)) != -1) {
switch (c) { switch (c) {
case 'a': option_a = optarg; case 'a': option_a = optarg;
break; break;
case 'b': option_b = true; case 'b': option_b = true;
break; break;
default: return false; default: return false;
} }
@ -609,7 +609,7 @@ public:
}; };
cMenuSetupHello::cMenuSetupHello(void) cMenuSetupHello::cMenuSetupHello(void)
{ {
newGreetingTime = GreetingTime; newGreetingTime = GreetingTime;
newUseAlternateGreeting = UseAlternateGreeting; newUseAlternateGreeting = UseAlternateGreeting;
Add(new cMenuEditIntItem( tr("Greeting time (s)"), &amp;newGreetingTime)); Add(new cMenuEditIntItem( tr("Greeting time (s)"), &amp;newGreetingTime));
@ -617,7 +617,7 @@ cMenuSetupHello::cMenuSetupHello(void)
} }
void cMenuSetupHello::Store(void) void cMenuSetupHello::Store(void)
{ {
SetupStore("GreetingTime", GreetingTime = newGreetingTime); SetupStore("GreetingTime", GreetingTime = newGreetingTime);
SetupStore("UseAlternateGreeting", UseAlternateGreeting = newUseAlternateGreeting); 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 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 desired video data stream, and it must be delivered fast enough so that the
DVB device doesn't run out of data. 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 To avoid busy loops the player should call its member function
<p><table><tr><td bgcolor=#F0F0F0><pre><br> <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 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 its editing marks. A special case of a "player" might use this function to implement
a "picture viewer". a "picture viewer".
<p> <p>
For detailed information on how to implement your own player, please take a look 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. 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 that they already know. If you absolutely want to do things differently, just go
ahead - it's your show... 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> <hr><h2>Receivers</h2>
<center><i><b>Tapping into the stream...</b></i></center><p> <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>. and will automatically detach itself from the <tt>cDevice</tt>.
<!--X1.1.6--></td></tr></table> <!--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> <hr><h2>The On Screen Display</h2>
<center><i><b>Express yourself</b></i></center><p> <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 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 of these functions, and VDR/osd.c to see how VDR opens the OSD and sets up
its windows and color depths). 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> <hr><h2>Devices</h2>
<center><i><b>Expanding the possibilities</b></i></center><p> <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 If the new device can receive, it most likely needs to provide a way of
selecting which channel it shall tune to: 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> <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> </pre></td></tr></table><p>
This function will be called with the desired channel and shall return whether These functions will be called with the desired channel and shall return whether
tuning to it was successful. this device can provide the requested channel and whether tuning to it was successful,
repectively.
<!--X1.1.9--></td></tr></table>
<p> <p>
<b>Recording</b> <b>Recording</b>
<p> <p>
@ -1226,7 +1228,7 @@ to indicate this to VDR.
<p> <p>
The functions to implement replaying capabilites are 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> <p><table><tr><td bgcolor=#F0F0F0><pre><br>
virtual bool HasDecoder(void) const; virtual bool HasDecoder(void) const;
virtual bool SetPlayMode(ePlayMode PlayMode); virtual bool SetPlayMode(ePlayMode PlayMode);
@ -1250,7 +1252,7 @@ virtual void SetVideoFormat(bool VideoFormat16_9);
virtual void SetVolumeDevice(int Volume); virtual void SetVolumeDevice(int Volume);
</pre></td></tr></table><p> </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> <p>
<b>On Screen Display</b> <b>On Screen Display</b>
<p> <p>

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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" #include "config.h"
@ -293,30 +293,6 @@ bool cChannel::Save(FILE *f)
return fprintf(f, ToText()) > 0; 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 ----------------------------------------------------------------- // -- cTimer -----------------------------------------------------------------
char *cTimer::buffer = NULL; char *cTimer::buffer = NULL;
@ -836,10 +812,10 @@ cChannel *cChannels::GetByServiceID(unsigned short ServiceId)
return NULL; return NULL;
} }
bool cChannels::SwitchTo(int Number, cDevice *Device) bool cChannels::SwitchTo(int Number)
{ {
cChannel *channel = GetByNumber(Number); cChannel *channel = GetByNumber(Number);
return channel && channel->Switch(Device); return channel && cDevice::PrimaryDevice()->SwitchChannel(channel, true);
} }
const char *cChannels::GetChannelNameByNumber(int Number) const char *cChannels::GetChannelNameByNumber(int Number)
@ -944,7 +920,7 @@ bool cSetupLine::operator< (const cListObject &ListObject)
{ {
const cSetupLine *sl = (cSetupLine *)&ListObject; const cSetupLine *sl = (cSetupLine *)&ListObject;
if (!plugin && !sl->plugin) if (!plugin && !sl->plugin)
return strcasecmp(name, sl->name) < 0; return strcasecmp(name, sl->name) < 0;
if (!plugin) if (!plugin)
return true; return true;
if (!sl->plugin) if (!sl->plugin)

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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 #ifndef __CONFIG_H
@ -119,7 +119,6 @@ public:
const char *ToText(void); const char *ToText(void);
bool Parse(const char *s); bool Parse(const char *s);
bool Save(FILE *f); bool Save(FILE *f);
bool Switch(cDevice *Device = NULL, bool Log = true);
}; };
enum eTimerActive { taInactive = 0, enum eTimerActive { taInactive = 0,
@ -198,6 +197,8 @@ public:
bool Accepts(in_addr_t Address); bool Accepts(in_addr_t Address);
}; };
#define CACONFBASE 100
class cCaDefinition : public cListObject { class cCaDefinition : public cListObject {
private: private:
int number; int number;
@ -296,7 +297,7 @@ public:
cChannel *GetByNumber(int Number); cChannel *GetByNumber(int Number);
cChannel *GetByServiceID(unsigned short ServiceId); cChannel *GetByServiceID(unsigned short ServiceId);
const char *GetChannelNameByNumber(int Number); const char *GetChannelNameByNumber(int Number);
bool SwitchTo(int Number, cDevice *Device = NULL); bool SwitchTo(int Number);
int MaxNumber(void) { return maxNumber; } 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 * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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" #include "device.h"
@ -12,6 +12,7 @@
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/mman.h> #include <sys/mman.h>
#include "eit.h" #include "eit.h"
#include "i18n.h"
#include "player.h" #include "player.h"
#include "receiver.h" #include "receiver.h"
#include "status.h" #include "status.h"
@ -101,11 +102,6 @@ bool cDevice::SetPrimaryDevice(int n)
return false; return false;
} }
bool cDevice::CanBeReUsed(int Frequency, int Vpid)
{
return false;
}
bool cDevice::HasDecoder(void) const bool cDevice::HasDecoder(void) const
{ {
return false; return false;
@ -116,31 +112,26 @@ cOsdBase *cDevice::NewOsd(int x, int y)
return NULL; 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; cDevice *d = NULL;
int Provides[MAXDEVICES];
// Check which devices provide Ca:
for (int i = 0; i < numDevices; i++) { for (int i = 0; i < numDevices; i++) {
if ((Provides[i] = device[i]->ProvidesCa(Ca)) != 0) { // this device is basicly able to do the job bool nsc;
//XXX+ dsyslog("GetDevice: %d %d %d %5d %5d", i, device[i]->HasDecoder(), device[i]->Receiving(), Frequency, device[i]->frequency);//XXX if (device[i]->ProvidesChannel(Channel, Priority, &nsc) // this device is basicly able to do the job
if (device[i]->CanBeReUsed(Frequency, Vpid)) { && (!d // we don't have a device yet, or...
d = device[i]; || device[i]->Priority() < d->Priority() // ...this one has an even lower Priority, or...
if (ReUse) || device[i]->Priority() == d->Priority() // ...same Priority...
*ReUse = true; && device[i]->ProvidesCa(Channel->ca) < d->ProvidesCa(Channel->ca) // ...but this one provides fewer Ca values
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
)
)
) )
d = device[i]; ) {
d = device[i];
if (NeedsSwitchChannel)
*NeedsSwitchChannel = nsc;
} }
} }
/*XXX+ too complex with multiple recordings per device /*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) #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) bool cDevice::AddPid(int Pid, ePidType PidType)
{ {
if (Pid) { if (Pid) {
@ -207,10 +207,10 @@ bool cDevice::AddPid(int Pid, ePidType PidType)
// The Pid is already in use // The Pid is already in use
if (++pidHandles[n].used == 2 && n <= ptTeletext) { if (++pidHandles[n].used == 2 && n <= ptTeletext) {
// It's a special PID that may have to be switched into "tap" mode // 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); return SetPid(&pidHandles[n], n, true);
} }
PRINTPIDS("a");//XXX+ PRINTPIDS("a");
return true; return true;
} }
else if (PidType < ptOther) { else if (PidType < ptOther) {
@ -226,7 +226,7 @@ bool cDevice::AddPid(int Pid, ePidType PidType)
if (n >= 0) { if (n >= 0) {
pidHandles[n].pid = Pid; pidHandles[n].pid = Pid;
pidHandles[n].used = 1; pidHandles[n].used = 1;
PRINTPIDS("C");//XXX+ PRINTPIDS("C");
return SetPid(&pidHandles[n], n, true); return SetPid(&pidHandles[n], n, true);
} }
} }
@ -238,14 +238,15 @@ void cDevice::DelPid(int Pid)
if (Pid) { if (Pid) {
for (int i = 0; i < MAXPIDHANDLES; i++) { for (int i = 0; i < MAXPIDHANDLES; i++) {
if (pidHandles[i].pid == Pid) { if (pidHandles[i].pid == Pid) {
PRINTPIDS("D");
if (--pidHandles[i].used < 2) { if (--pidHandles[i].used < 2) {
SetPid(&pidHandles[i], i, false); SetPid(&pidHandles[i], i, false);
if (pidHandles[i].used == 0) { if (pidHandles[i].used == 0) {
pidHandles[i].handle = -1; pidHandles[i].handle = -1;
pidHandles[i].pid = 0; pidHandles[i].pid = 0;
} }
} }
PRINTPIDS("D");//XXX+ PRINTPIDS("E");
} }
} }
} }
@ -256,11 +257,35 @@ bool cDevice::SetPid(cPidHandle *Handle, int Type, bool On)
return false; 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); cStatus::MsgChannelSwitch(this, 0);
StopReplay(); if (LiveView)
StopReplay();
// Must set this anyway to avoid getting stuck when switching through // Must set this anyway to avoid getting stuck when switching through
// channels with 'Up' and 'Down' keys: // 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 // If this card can't receive this channel, we must not actually switch
// the channel here, because that would irritate the driver when we // the channel here, because that would irritate the driver when we
// start replaying in Transfer Mode immediately after switching the channel: // 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; 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: // use the card that actually can receive it and transfer data from there:
if (NeedsTransferMode) { if (NeedsTransferMode) {
cDevice *CaDevice = GetDevice(Channel->ca, 0); bool NeedsSwitchChannel = false;
if (CaDevice && !CaDevice->Receiving() && CaDevice->SetChannel(Channel) == scrOk) cDevice *CaDevice = GetDevice(Channel, 0, &NeedsSwitchChannel);
cControl::Launch(new cTransferControl(CaDevice, Channel->vpid, Channel->apid1, 0, 0, 0));//XXX+ 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 else
Result = scrNoTransfer; Result = scrNotAvailable;
} }
else if (!SetChannelDevice(Channel)) else if (!SetChannelDevice(Channel, LiveView))
Result = scrFailed; Result = scrFailed;
if (IsPrimaryDevice()) if (IsPrimaryDevice())
@ -294,7 +324,7 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel)
return Result; return Result;
} }
bool cDevice::SetChannelDevice(const cChannel *Channel) bool cDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
{ {
return false; return false;
} }
@ -357,10 +387,6 @@ bool cDevice::Replaying(void)
bool cDevice::AttachPlayer(cPlayer *Player) 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 (HasDecoder()) {
if (player) if (player)
Detach(player); Detach(player);
@ -419,9 +445,7 @@ int cDevice::PlayAudio(const uchar *Data, int Length)
int cDevice::Priority(void) int cDevice::Priority(void)
{ {
if (IsPrimaryDevice() && !Receiving()) int priority = IsPrimaryDevice() ? Setup.PrimaryLimit - 1 : DEFAULTPRIORITY;
return Setup.PrimaryLimit - 1;
int priority = DEFAULTPRIORITY;
for (int i = 0; i < MAXRECEIVERS; i++) { for (int i = 0; i < MAXRECEIVERS; i++) {
if (receiver[i]) if (receiver[i])
priority = max(receiver[i]->priority, priority); priority = max(receiver[i]->priority, priority);
@ -551,12 +575,10 @@ int cDevice::GetTSPacket(uchar *Data)
bool cDevice::AttachReceiver(cReceiver *Receiver) bool cDevice::AttachReceiver(cReceiver *Receiver)
{ {
//XXX+ check for same transponder???
if (!Receiver) if (!Receiver)
return false; return false;
if (Receiver->device == this) if (Receiver->device == this)
return true; return true;
StopReplay();
for (int i = 0; i < MAXRECEIVERS; i++) { for (int i = 0; i < MAXRECEIVERS; i++) {
if (!receiver[i]) { if (!receiver[i]) {
for (int n = 0; n < MAXRECEIVEPIDS; n++) for (int n = 0; n < MAXRECEIVEPIDS; n++)

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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 #ifndef __DEVICE_H
@ -24,7 +24,7 @@
#define TS_SYNC_BYTE 0x47 #define TS_SYNC_BYTE 0x47
#define PID_MASK_HI 0x1F #define PID_MASK_HI 0x1F
enum eSetChannelResult { scrOk, scrNoTransfer, scrFailed }; enum eSetChannelResult { scrOk, scrNotAvailable, scrNoTransfer, scrFailed };
enum ePlayMode { pmNone, // audio/video from decoder enum ePlayMode { pmNone, // audio/video from decoder
pmAudioVideo, // audio/video from player pmAudioVideo, // audio/video from player
@ -69,19 +69,13 @@ public:
// 1...numDevices) and returns true if this was possible. // 1...numDevices) and returns true if this was possible.
static cDevice *PrimaryDevice(void) { return primaryDevice; } static cDevice *PrimaryDevice(void) { return primaryDevice; }
// Returns the primary device. // Returns the primary device.
static cDevice *GetDevice(int Ca, int Priority, int Frequency = 0, int Vpid = 0, bool *ReUse = NULL); static cDevice *GetDevice(int Index);
// Selects a free device, avoiding the primaryDevice if possible. // Returns the device with the Index (if Index is in the range
// If Ca is not 0, the device with the given number will be returned // 0..numDevices-1, NULL otherwise).
// in case Ca is <= MAXDEVICES, or the device that provides the given static cDevice *GetDevice(const cChannel *Channel, int Priority = -1, bool *NeedsSwitchChannel = NULL);
// value in its caCaps. // Returns a device that is able to receive the given Channel at the
// If there is a device that is already receiving and can be re-used to // given Priority (see ProvidesChannel() for more information on how
// receive another data stream, that device will be returned. // priorities are handled, and the meaning of NeedsSwitchChannel).
// 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 void SetCaCaps(int Index = -1); static void SetCaCaps(int Index = -1);
// Sets the CaCaps of the given device according to the Setup data. // Sets the CaCaps of the given device according to the Setup data.
// By default the CaCaps of all devices are set. // By default the CaCaps of all devices are set.
@ -115,18 +109,13 @@ public:
bool IsPrimaryDevice(void) const { return this == primaryDevice; } bool IsPrimaryDevice(void) const { return this == primaryDevice; }
int CardIndex(void) const { return cardIndex; } int CardIndex(void) const { return cardIndex; }
// Returns the card index of this device (0 ... MAXDEVICES - 1). // Returns the card index of this device (0 ... MAXDEVICES - 1).
virtual int ProvidesCa(int Ca); int ProvidesCa(int Ca);
//XXX TODO temporarily made this function virtual - until a general
//XXX mechanism has been implemented
// Checks whether this device provides the given value in its // Checks whether this device provides the given value in its
// caCaps. Returns 0 if the value is not provided, 1 if only this // 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. // value is provided, and > 1 if this and other values are provided.
// If the given value is equal to the number of this device, // 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 // 1 is returned. If it is 0 (FTA), 1 plus the number of other values
// in caCaps is returned. // 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; virtual bool HasDecoder(void) const;
// Tells whether this device has an MPEG decoder. // Tells whether this device has an MPEG decoder.
@ -145,10 +134,33 @@ public:
protected: protected:
int currentChannel; int currentChannel;
public: 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). // 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). // Sets the device to the given channel (actual physical setup).
public:
static int CurrentChannel(void) { return primaryDevice ? primaryDevice->currentChannel : 0; } static int CurrentChannel(void) { return primaryDevice ? primaryDevice->currentChannel : 0; }
// Returns the number of the current channel on the primary device. // Returns the number of the current channel on the primary device.
int Channel(void) { return currentChannel; } int Channel(void) { return currentChannel; }
@ -169,6 +181,8 @@ protected:
cPidHandle(void) { pid = used = 0; handle = -1; } cPidHandle(void) { pid = used = 0; handle = -1; }
}; };
cPidHandle pidHandles[MAXPIDHANDLES]; 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); bool AddPid(int Pid, ePidType PidType = ptOther);
// Adds a PID to the set of PIDs this device shall receive. // Adds a PID to the set of PIDs this device shall receive.
void DelPid(int Pid); void DelPid(int Pid);
@ -263,12 +277,12 @@ public:
private: private:
cReceiver *receiver[MAXRECEIVERS]; cReceiver *receiver[MAXRECEIVERS];
int ca; int ca;
int CanShift(int Ca, int Priority, int UsedCards = 0);
protected:
int Priority(void); int Priority(void);
// Returns the priority of the current receiving session (0..MAXPRIORITY), // Returns the priority of the current receiving session (0..MAXPRIORITY),
// or -1 if no receiver is currently active. The primary device will // or -1 if no receiver is currently active. The primary device will
// always return at least Setup.PrimaryLimit-1. // always return at least Setup.PrimaryLimit-1.
int CanShift(int Ca, int Priority, int UsedCards = 0);
protected:
virtual bool OpenDvr(void); virtual bool OpenDvr(void);
// Opens the DVR of this device and prepares it to deliver a Transport // Opens the DVR of this device and prepares it to deliver a Transport
// Stream for use in a cReceiver. // Stream for use in a cReceiver.

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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" #include "dvbdevice.h"
@ -20,10 +20,12 @@ extern "C" {
#include <linux/videodev.h> #include <linux/videodev.h>
#ifdef NEWSTRUCT #ifdef NEWSTRUCT
#include <linux/dvb/audio.h> #include <linux/dvb/audio.h>
#include <linux/dvb/dmx.h>
#include <linux/dvb/frontend.h> #include <linux/dvb/frontend.h>
#include <linux/dvb/video.h> #include <linux/dvb/video.h>
#else #else
#include <ost/audio.h> #include <ost/audio.h>
#include <ost/dmx.h>
#include <ost/sec.h> #include <ost/sec.h>
#include <ost/video.h> #include <ost/video.h>
#endif #endif
@ -35,8 +37,6 @@ extern "C" {
#include "status.h" #include "status.h"
#include "transfer.h" #include "transfer.h"
#define MAXDVBDEVICES 4
#define DEV_VIDEO "/dev/video" #define DEV_VIDEO "/dev/video"
#ifdef NEWSTRUCT #ifdef NEWSTRUCT
#define DEV_DVB_ADAPTER "/dev/dvb/adapter" #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_osd = DvbOpen(DEV_DVB_OSD, n, O_RDWR);
fd_video = DvbOpen(DEV_DVB_VIDEO, n, O_RDWR | O_NONBLOCK); fd_video = DvbOpen(DEV_DVB_VIDEO, n, O_RDWR | O_NONBLOCK);
fd_audio = DvbOpen(DEV_DVB_AUDIO, n, O_RDWR | O_NONBLOCK); fd_audio = DvbOpen(DEV_DVB_AUDIO, n, O_RDWR | O_NONBLOCK);
#ifndef NEWSTRUCT #ifndef NEWSTRUCT
// Devices that are only present on DVB-S cards: // Devices that are only present on DVB-S cards:
fd_sec = DvbOpen(DEV_DVB_SEC, n, O_RDWR); fd_sec = DvbOpen(DEV_DVB_SEC, n, O_RDWR);
#endif #endif
@ -179,15 +179,6 @@ void cDvbDevice::MakePrimaryDevice(bool On)
cDvbOsd::SetDvbDevice(On ? this : NULL); 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 bool cDvbDevice::HasDecoder(void) const
{ {
return fd_video >= 0 && fd_audio >= 0; 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[0] = tmp;
mem1 += 3; mem1 += 3;
} }
if (Quality < 0) if (Quality < 0)
Quality = 255; //XXX is this 'best'??? Quality = 255; //XXX is this 'best'???
isyslog("grabbing to %s (%s %d %d %d)", FileName, Jpeg ? "JPEG" : "PNM", Quality, vm.width, vm.height); isyslog("grabbing to %s (%s %d %d %d)", FileName, Jpeg ? "JPEG" : "PNM", Quality, vm.width, vm.height);
FILE *f = fopen(FileName, "wb"); FILE *f = fopen(FileName, "wb");
if (f) { if (f) {
@ -253,11 +244,11 @@ bool cDvbDevice::GrabImage(const char *FileName, bool Jpeg, int Quality, int Siz
cinfo.image_height = vm.height; cinfo.image_height = vm.height;
cinfo.input_components = 3; cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB; cinfo.in_color_space = JCS_RGB;
jpeg_set_defaults(&cinfo); jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, Quality, true); jpeg_set_quality(&cinfo, Quality, true);
jpeg_start_compress(&cinfo, true); jpeg_start_compress(&cinfo, true);
int rs = vm.width * 3; int rs = vm.width * 3;
JSAMPROW rp[vm.height]; JSAMPROW rp[vm.height];
for (int k = 0; k < vm.height; k++) for (int k = 0; k < vm.height; k++)
@ -313,6 +304,14 @@ bool cDvbDevice::SetPid(cPidHandle *Handle, int Type, bool On)
else { else {
CHECK(ioctl(Handle->handle, DMX_STOP)); CHECK(ioctl(Handle->handle, DMX_STOP));
if (Handle->used == 0) { 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); close(Handle->handle);
Handle->handle = -1; Handle->handle = -1;
return true; return true;
@ -339,8 +338,58 @@ bool cDvbDevice::SetPid(cPidHandle *Handle, int Type, bool On)
return true; 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: // Avoid noise while switching:
if (HasDecoder()) { if (HasDecoder()) {
@ -357,204 +406,209 @@ bool cDvbDevice::SetChannelDevice(const cChannel *Channel)
// Turn off current PIDs: // Turn off current PIDs:
if (HasDecoder()) { if (HasDecoder() && (LiveView || pidHandles[ptVideo].pid == Channel->vpid)) {
DelPid(pidHandles[ptVideo].pid); DelPid(pidHandles[ptVideo].pid);
DelPid(pidHandles[ptAudio].pid); DelPid(pidHandles[ptAudio].pid);
DelPid(pidHandles[ptTeletext].pid); DelPid(pidHandles[ptTeletext].pid);
DelPid(pidHandles[ptDolby].pid); DelPid(pidHandles[ptDolby].pid);
} }
if (frequency != Channel->frequency || Channel->ca > CACONFBASE) { // CA channels can only be decrypted in "live" mode
#ifdef NEWSTRUCT #ifdef NEWSTRUCT
dvb_frontend_parameters Frontend; dvb_frontend_parameters Frontend;
#else #else
FrontendParameters Frontend; FrontendParameters Frontend;
#endif #endif
memset(&Frontend, 0, sizeof(Frontend)); memset(&Frontend, 0, sizeof(Frontend));
switch (frontendType) { switch (frontendType) {
case FE_QPSK: { // DVB-S case FE_QPSK: { // DVB-S
// Frequency offsets: // Frequency offsets:
unsigned int freq = Channel->frequency; unsigned int freq = Channel->frequency;
int tone = SEC_TONE_OFF; int tone = SEC_TONE_OFF;
if (freq < (unsigned int)Setup.LnbSLOF) { if (freq < (unsigned int)Setup.LnbSLOF) {
freq -= Setup.LnbFrequLo; freq -= Setup.LnbFrequLo;
tone = SEC_TONE_OFF; 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 { break;
freq -= Setup.LnbFrequHi; case FE_QAM: { // DVB-C
tone = SEC_TONE_ON;
// 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 #ifdef NEWSTRUCT
Frontend.frequency = freq * 1000UL; Frontend.frequency = Channel->frequency * 1000UL;
Frontend.inversion = INVERSION_AUTO; Frontend.inversion = INVERSION_AUTO;
Frontend.u.qpsk.symbol_rate = Channel->srate * 1000UL; Frontend.u.ofdm.bandwidth=BANDWIDTH_8_MHZ;
Frontend.u.qpsk.fec_inner = FEC_AUTO; 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 #else
Frontend.Frequency = freq * 1000UL; Frontend.Frequency = Channel->frequency * 1000UL;
Frontend.Inversion = INVERSION_AUTO; Frontend.Inversion = INVERSION_AUTO;
Frontend.u.qpsk.SymbolRate = Channel->srate * 1000UL; Frontend.u.ofdm.bandWidth=BANDWIDTH_8_MHZ;
Frontend.u.qpsk.FEC_inner = FEC_AUTO; 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 #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)); break;
#else default:
secCommand scmd; esyslog("ERROR: attempt to set channel with unknown DVB frontend type");
scmd.type = 0; return false;
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; #ifdef NEWSTRUCT
scmds.voltage = volt; // Discard stale events:
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)); for (;;) {
#endif dvb_frontend_event event;
if (ioctl(fd_frontend, FE_GET_EVENT, &event) < 0)
break;
} }
break; #endif
case FE_QAM: { // DVB-C
// Frequency and symbol rate: // Tuning:
CHECK(ioctl(fd_frontend, FE_SET_FRONTEND, &Frontend));
// Wait for channel lock:
#ifdef NEWSTRUCT #ifdef NEWSTRUCT
Frontend.frequency = Channel->frequency * 1000000UL; FrontendStatus status = FrontendStatus(0);
Frontend.inversion = INVERSION_AUTO; for (int i = 0; i < 100; i++) {
Frontend.u.qam.symbol_rate = Channel->srate * 1000UL; CHECK(ioctl(fd_frontend, FE_READ_STATUS, &status));
Frontend.u.qam.fec_inner = FEC_AUTO; if (status & FE_HAS_LOCK)
Frontend.u.qam.modulation = QAM_64; break;
#else usleep(10 * 1000);
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; if (!(status & FE_HAS_LOCK)) {
case FE_OFDM: { // DVB-T esyslog("ERROR: channel %d not locked on DVB card %d!", Channel->number, CardIndex() + 1);
if (IsPrimaryDevice())
// Frequency and OFDM paramaters: cThread::RaisePanic();
return false;
#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;
#else #else
Frontend.Frequency = Channel->frequency * 1000UL; if (cFile::FileReady(fd_frontend, 5000)) {
Frontend.Inversion = INVERSION_AUTO; FrontendEvent event;
Frontend.u.ofdm.bandWidth=BANDWIDTH_8_MHZ; if (ioctl(fd_frontend, FE_GET_EVENT, &event) >= 0) {
Frontend.u.ofdm.HP_CodeRate=FEC_2_3; if (event.type != FE_COMPLETION_EV) {
Frontend.u.ofdm.LP_CodeRate=FEC_1_2; esyslog("ERROR: channel %d not sync'ed on DVB card %d!", Channel->number, CardIndex() + 1);
Frontend.u.ofdm.Constellation=QAM_64; if (IsPrimaryDevice())
Frontend.u.ofdm.TransmissionMode=TRANSMISSION_MODE_2K; cThread::RaisePanic();
Frontend.u.ofdm.guardInterval=GUARD_INTERVAL_1_32; return false;
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;
} }
else
esyslog("ERROR in frontend get event (channel %d, card %d): %m", Channel->number, CardIndex() + 1);
} }
else else
esyslog("ERROR in frontend get event (channel %d, card %d): %m", Channel->number, CardIndex() + 1); esyslog("ERROR: timeout while tuning on DVB card %d", CardIndex() + 1);
}
else
esyslog("ERROR: timeout while tuning on DVB card %d", CardIndex() + 1);
#endif #endif
frequency = Channel->frequency; frequency = Channel->frequency;
}
// PID settings: // PID settings:
if (HasDecoder()) { if (HasDecoder() && (LiveView || Channel->ca > CACONFBASE)) { // CA channels can only be decrypted in "live" mode
if (!(AddPid(Channel->vpid, ptVideo) && AddPid(Channel->apid1, ptAudio))) {//XXX+ dolby dpid1!!! (if audio plugins are attached) if (!HasPid(Channel->vpid)) {
esyslog("ERROR: failed to set PIDs for channel %d", Channel->number); if (!(AddPid(Channel->vpid, ptVideo) && AddPid(Channel->apid1, ptAudio))) {//XXX+ dolby dpid1!!! (if audio plugins are attached)
return false; 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()) else
AddPid(Channel->tpid, ptTeletext); cControl::Launch(new cTransferControl(this, Channel->vpid, Channel->apid1, 0, 0, 0));
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));
} }
// Start setting system time: // Start setting system time:

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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 #ifndef __DVBDEVICE_H
@ -23,6 +23,8 @@
#include "device.h" #include "device.h"
#include "eit.h" #include "eit.h"
#define MAXDVBDEVICES 4
class cDvbDevice : public cDevice { class cDvbDevice : public cDevice {
friend class cDvbOsd; friend class cDvbOsd;
private: private:
@ -45,7 +47,6 @@ protected:
public: public:
cDvbDevice(int n); cDvbDevice(int n);
virtual ~cDvbDevice(); virtual ~cDvbDevice();
virtual bool CanBeReUsed(int Frequency, int Vpid);
virtual bool HasDecoder(void) const; virtual bool HasDecoder(void) const;
// OSD facilities // OSD facilities
@ -58,7 +59,9 @@ public:
private: private:
int frequency; int frequency;
public: 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 // PID handle facilities

View File

@ -4,11 +4,12 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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 "eitscan.h"
#include <stdlib.h> #include <stdlib.h>
#include "dvbdevice.h"
cEITScanner::cEITScanner(void) cEITScanner::cEITScanner(void)
{ {
@ -49,9 +50,9 @@ void cEITScanner::Process(void)
if (Setup.EPGScanTimeout && Channels.MaxNumber() > 1) { if (Setup.EPGScanTimeout && Channels.MaxNumber() > 1) {
time_t now = time(NULL); time_t now = time(NULL);
if (now - lastScan > ScanTimeout && now - lastActivity > ActivityTimeout) { if (now - lastScan > ScanTimeout && now - lastActivity > ActivityTimeout) {
for (int i = 0; i < MAXDEVICES; i++) { for (int i = 0; i < cDevice::NumDevices(); i++) {
cDevice *Device = cDevice::GetDevice(i + 1, MAXPRIORITY + 1); cDevice *Device = cDevice::GetDevice(i);
if (Device) { if (Device && Device->CardIndex() < MAXDVBDEVICES) {
if (Device != cDevice::PrimaryDevice() || (cDevice::NumDevices() == 1 && Setup.EPGScanTimeout && now - lastActivity > Setup.EPGScanTimeout * 3600)) { if (Device != cDevice::PrimaryDevice() || (cDevice::NumDevices() == 1 && Setup.EPGScanTimeout && now - lastActivity > Setup.EPGScanTimeout * 3600)) {
if (!(Device->Receiving() || Device->Replaying())) { if (!(Device->Receiving() || Device->Replaying())) {
int oldCh = lastChannel; int oldCh = lastChannel;
@ -63,12 +64,12 @@ void cEITScanner::Process(void)
} }
cChannel *Channel = Channels.GetByNumber(ch); cChannel *Channel = Channels.GetByNumber(ch);
if (Channel) { if (Channel) {
if (Channel->ca <= MAXDEVICES && !Device->ProvidesCa(Channel->ca)) if (!Device->ProvidesChannel(Channel))
break; // the channel says it explicitly needs a different card break;
if (Channel->pnr && !TransponderScanned(Channel)) { if (Channel->pnr && !TransponderScanned(Channel)) {
if (Device == cDevice::PrimaryDevice() && !currentChannel) if (Device == cDevice::PrimaryDevice() && !currentChannel)
currentChannel = Device->Channel(); currentChannel = Device->Channel();
Channel->Switch(Device, false); Device->SwitchChannel(Channel, false);
lastChannel = ch; lastChannel = ch;
break; 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); lastScan = time(NULL);
} }

17
menu.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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" #include "menu.h"
@ -508,7 +508,7 @@ eOSState cMenuChannels::Switch(void)
{ {
cChannel *ch = Channels.Get(Current()); cChannel *ch = Channels.Get(Current());
if (ch) if (ch)
ch->Switch(); cDevice::PrimaryDevice()->SwitchChannel(ch, true);
return osEnd; return osEnd;
} }
@ -1054,7 +1054,7 @@ eOSState cMenuWhatsOn::Switch(void)
cMenuWhatsOnItem *item = (cMenuWhatsOnItem *)Get(Current()); cMenuWhatsOnItem *item = (cMenuWhatsOnItem *)Get(Current());
if (item) { if (item) {
cChannel *channel = Channels.GetByServiceID(item->eventInfo->GetServiceID()); cChannel *channel = Channels.GetByServiceID(item->eventInfo->GetServiceID());
if (channel && channel->Switch()) if (channel && cDevice::PrimaryDevice()->SwitchChannel(channel, true))
return osEnd; return osEnd;
} }
Interface->Error(tr("Can't switch channel!")); Interface->Error(tr("Can't switch channel!"));
@ -2519,12 +2519,12 @@ bool cRecordControls::Start(cTimer *Timer)
cChannel *channel = Channels.GetByNumber(ch); cChannel *channel = Channels.GetByNumber(ch);
if (channel) { if (channel) {
bool ReUse = false; bool NeedsSwitchChannel = false;
cDevice *device = cDevice::GetDevice(channel->ca, Timer ? Timer->priority : Setup.DefaultPriority, channel->frequency, channel->vpid, &ReUse); cDevice *device = cDevice::GetDevice(channel, Timer ? Timer->priority : Setup.DefaultPriority, &NeedsSwitchChannel);
if (device) { if (device) {
if (!ReUse) { if (NeedsSwitchChannel) {
Stop(device); Stop(device);
if (!channel->Switch(device)) { if (!device->SwitchChannel(channel, false)) {
cThread::EmergencyExit(true); cThread::EmergencyExit(true);
return false; return false;
} }
@ -2570,7 +2570,8 @@ void cRecordControls::Stop(cDevice *Device)
bool cRecordControls::StopPrimary(bool DoIt) bool cRecordControls::StopPrimary(bool DoIt)
{ {
if (cDevice::PrimaryDevice()->Receiving()) { 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 (device) {
if (DoIt) if (DoIt)
Stop(cDevice::PrimaryDevice()); Stop(cDevice::PrimaryDevice());

View File

@ -10,7 +10,7 @@
* and interact with the Video Disk Recorder - or write a full featured * and interact with the Video Disk Recorder - or write a full featured
* graphical interface that sits on top of an SVDRP connection. * 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" #include "svdrp.h"
@ -417,13 +417,9 @@ void cSVDRP::CmdCHAN(const char *Option)
Reply(501, "Undefined channel \"%s\"", Option); Reply(501, "Undefined channel \"%s\"", Option);
return; return;
} }
if (Interface->Recording()) {
Reply(550, "Can't switch channel, interface is recording");
return;
}
cChannel *channel = Channels.GetByNumber(n); cChannel *channel = Channels.GetByNumber(n);
if (channel) { if (channel) {
if (!channel->Switch()) { if (!cDevice::PrimaryDevice()->SwitchChannel(channel, true)) {
Reply(554, "Error switching to channel \"%d\"", channel->number); Reply(554, "Error switching to channel \"%d\"", channel->number);
return; return;
} }

4
vdr.c
View File

@ -22,7 +22,7 @@
* *
* The project's page is at http://www.cadsoft.de/people/kls/vdr * 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> #include <getopt.h>
@ -548,7 +548,7 @@ int main(int argc, char *argv[])
int n = cDevice::CurrentChannel() + (NORMALKEY(key) == kUp ? 1 : -1); int n = cDevice::CurrentChannel() + (NORMALKEY(key) == kUp ? 1 : -1);
cChannel *channel = Channels.GetByNumber(n); cChannel *channel = Channels.GetByNumber(n);
if (channel) if (channel)
channel->Switch(); cDevice::PrimaryDevice()->SwitchChannel(channel, true);
break; break;
} }
// Viewing Control: // Viewing Control: