summaryrefslogtreecommitdiff
path: root/device.c
diff options
context:
space:
mode:
authorKlaus Schmidinger <kls (at) cadsoft (dot) de>2002-09-08 18:00:00 +0200
committerKlaus Schmidinger <kls (at) cadsoft (dot) de>2002-09-08 18:00:00 +0200
commit523c4a07aa9112841743fca2ebcce957fde03bc8 (patch)
tree6980f2c5644d926410c359574fa11732860d7eac /device.c
parenta2a215d5e12ad35df8d0731dd00b6e41d5dd77fa (diff)
downloadvdr-patch-lnbsharing-523c4a07aa9112841743fca2ebcce957fde03bc8.tar.gz
vdr-patch-lnbsharing-523c4a07aa9112841743fca2ebcce957fde03bc8.tar.bz2
Version 1.1.9vdr-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. - Increased the recorder buffer size to 5MB in order to be able to better handle multiple recordings. - Implemented cTSBuffer for better handling TS packet buffering in derived cDevice classes. - Changed the interface if cDevice::GetTSPacket() to avoid unnecessary copying of data. - Removed cDevice::Channel(), since this makes no more sense with devices receiving multiple channels. - Switching through channels with the 'Up' and 'Down' keys now skips channels that are currently not available (for instance because all devices are recording and these channels are on different transponders). - Implemented an SPU decoder (thanks to Andreas Schultz). - Fixed a crash when entering an integer value outside the limits (thanks to Stefan Huelswitt for reporting this one). - Added play mode pmAudioOnlyBlack (thanks to Stefan Huelswitt).
Diffstat (limited to 'device.c')
-rw-r--r--device.c270
1 files changed, 201 insertions, 69 deletions
diff --git a/device.c b/device.c
index 48f5d40..72f1fc6 100644
--- a/device.c
+++ b/device.c
@@ -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.19 2002/09/08 14:03:43 kls Exp $
*/
#include "device.h"
@@ -12,11 +12,14 @@
#include <sys/ioctl.h>
#include <sys/mman.h>
#include "eit.h"
+#include "i18n.h"
#include "player.h"
#include "receiver.h"
#include "status.h"
#include "transfer.h"
+// --- cDevice ---------------------------------------------------------------
+
// The default priority for non-primary devices:
#define DEFAULTPRIORITY -2
@@ -27,6 +30,7 @@
int cDevice::numDevices = 0;
int cDevice::useDevice = 0;
int cDevice::nextCardIndex = 0;
+int cDevice::currentChannel = 0;
cDevice *cDevice::device[MAXDEVICES] = { NULL };
cDevice *cDevice::primaryDevice = NULL;
@@ -38,8 +42,6 @@ cDevice::cDevice(void)
active = false;
- currentChannel = 0;
-
mute = false;
volume = Setup.CurrentVolume;
@@ -101,46 +103,44 @@ bool cDevice::SetPrimaryDevice(int n)
return false;
}
-bool cDevice::CanBeReUsed(int Frequency, int Vpid)
+bool cDevice::HasDecoder(void) const
{
return false;
}
-bool cDevice::HasDecoder(void) const
+cOsdBase *cDevice::NewOsd(int x, int y)
{
- return false;
+ return NULL;
}
-cOsdBase *cDevice::NewOsd(int x, int y)
+cSpuDecoder *cDevice::GetSpuDecoder(void)
{
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 *NeedsDetachReceivers)
{
- 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 ndr;
+ if (device[i]->ProvidesChannel(Channel, Priority, &ndr) // this device is basicly able to do the job
+ && (!d // we don't have a device yet, or...
+ || (device[i]->Receiving() && !ndr) // ...this one is already receiving and allows additional receivers, or...
+ || !d->Receiving() // ...the one we have is not receiving...
+ && (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 (NeedsDetachReceivers)
+ *NeedsDetachReceivers = ndr;
}
}
/*XXX+ too complex with multiple recordings per device
@@ -189,9 +189,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 +216,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 +235,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 +247,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,20 +266,66 @@ 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 *NeedsDetachReceivers)
{
- cStatus::MsgChannelSwitch(this, 0);
+ return false;
+}
- StopReplay();
+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;
+}
- // Must set this anyway to avoid getting stuck when switching through
- // channels with 'Up' and 'Down' keys:
- currentChannel = Channel->number;
+bool cDevice::SwitchChannel(int Direction)
+{
+ bool result = false;
+ Direction = sgn(Direction);
+ if (Direction) {
+ int n = CurrentChannel() + Direction;
+ int first = n;
+ for (;;) {
+ cChannel *channel = Channels.GetByNumber(n);
+ if (!channel)
+ break;
+ if (PrimaryDevice()->SwitchChannel(channel, true)) {
+ result = true;
+ break;
+ }
+ n += Direction;
+ }
+ int d = n - first;
+ if (abs(d) == 1)
+ dsyslog("skipped channel %d", first);
+ else if (d)
+ dsyslog("skipped channels %d..%d", first, n - sgn(d));
+ }
+ return result;
+}
+
+eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
+{
+ cStatus::MsgChannelSwitch(this, 0);
+
+ if (LiveView)
+ StopReplay();
// 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,24 +333,30 @@ 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+
+ cDevice *CaDevice = GetDevice(Channel, 0);
+ if (CaDevice) {
+ if (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())
+ if (Result == scrOk && LiveView && IsPrimaryDevice()) {
cSIProcessor::SetCurrentServiceID(Channel->pnr);
+ currentChannel = Channel->number;
+ }
cStatus::MsgChannelSwitch(this, Channel->number);
return Result;
}
-bool cDevice::SetChannelDevice(const cChannel *Channel)
+bool cDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
{
return false;
}
@@ -357,10 +419,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 +477,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);
@@ -497,15 +553,13 @@ void cDevice::Action(void)
dsyslog("receiver thread started on device %d (pid=%d)", CardIndex() + 1, getpid());
if (OpenDvr()) {
- uchar b[TS_SIZE];
time_t t = time(NULL);
active = true;
for (; active;) {
// Read data from the DVR device:
- int r = GetTSPacket(b);
- if (r == TS_SIZE) {
- if (*b == TS_SYNC_BYTE) {
- // We're locked on to a TS packet
+ uchar *b = NULL;
+ if (GetTSPacket(b)) {
+ if (b) {
int Pid = (((uint16_t)b[1] & PID_MASK_HI) << 8) | b[2];
// Distribute the packet to all attached receivers:
Lock();
@@ -514,12 +568,10 @@ void cDevice::Action(void)
receiver[i]->Receive(b, TS_SIZE);
}
Unlock();
+ t = time(NULL);
}
- t = time(NULL);
}
- else if (r > 0)
- esyslog("ERROR: got incomplete TS packet (%d bytes) on device %d", r, CardIndex() + 1);//XXX+ TODO do we have to read the rest???
- else if (r < 0)
+ else
break;
//XXX+ put this into the recorder??? or give the receiver a flag whether it wants this?
@@ -544,19 +596,17 @@ void cDevice::CloseDvr(void)
{
}
-int cDevice::GetTSPacket(uchar *Data)
+bool cDevice::GetTSPacket(uchar *&Data)
{
- return -1;
+ return false;
}
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++)
@@ -597,3 +647,85 @@ void cDevice::Detach(cReceiver *Receiver)
Cancel(3);
}
}
+
+// --- cTSBuffer -------------------------------------------------------------
+
+cTSBuffer::cTSBuffer(int File, int Size, int CardIndex)
+{
+ f = File;
+ size = Size / TS_SIZE * TS_SIZE;
+ cardIndex = CardIndex;
+ tsRead = tsWrite = 0;
+ buf = (f >= 0 && size >= TS_SIZE) ? MALLOC(uchar, size + TS_SIZE) : NULL;
+ // the '+ TS_SIZE' allocates some extra space for handling packets that got split by a buffer roll-over
+ firstRead = true;
+}
+
+cTSBuffer::~cTSBuffer()
+{
+ free(buf);
+}
+
+int cTSBuffer::Read(void)
+{
+ if (buf) {
+ cPoller Poller(f, false);
+ bool repeat;
+ int total = 0;
+ do {
+ repeat = false;
+ if (firstRead || Used() > TS_SIZE || Poller.Poll(100)) { // only wait if there's not enough data in the buffer
+ firstRead = false;
+ if (tsRead == tsWrite)
+ tsRead = tsWrite = 0; // keep the maximum buffer space available
+ if (tsWrite >= size && tsRead > 0)
+ tsWrite = 0;
+ int free = tsRead <= tsWrite ? size - tsWrite : tsRead - tsWrite - 1;
+ if (free > 0) {
+ int r = read(f, buf + tsWrite, free);
+ if (r > 0) {
+ total += r;
+ tsWrite += r;
+ if (tsWrite >= size && tsRead > 0) {
+ tsWrite = 0;
+ repeat = true; // read again after a boundary roll-over
+ }
+ }
+ }
+ }
+ } while (repeat);
+ return total;
+ }
+ return -1;
+}
+
+uchar *cTSBuffer::Get(void)
+{
+ if (Used() >= TS_SIZE) {
+ uchar *p = buf + tsRead;
+ if (*p != TS_SYNC_BYTE) {
+ esyslog("ERROR: not sync'ed to TS packet on device %d", cardIndex);
+ int tsMax = tsRead < tsWrite ? tsWrite : size;
+ for (int i = tsRead; i < tsMax; i++) {
+ if (buf[i] == TS_SYNC_BYTE) {
+ esyslog("ERROR: skipped %d bytes to sync on TS packet on device %d", i - tsRead, cardIndex);
+ tsRead = i;
+ return NULL;
+ }
+ }
+ if ((tsRead = tsMax) >= size)
+ tsRead = 0;
+ return NULL;
+ }
+ if (tsRead + TS_SIZE > size) {
+ // the packet rolled over the buffer boundary, so let's fetch the rest from the beginning (which MUST be there, since Used() >= TS_SIZE)
+ int rest = TS_SIZE - (size - tsRead);
+ memcpy(buf + size, buf, rest);
+ tsRead = rest;
+ }
+ else if ((tsRead += TS_SIZE) >= size)
+ tsRead = 0;
+ return p;
+ }
+ return NULL;
+}