/* * device.c: The basic device interface * * See the main source file 'vdr.c' for copyright information and * how to reach the author. * * $Id: device.c 1.57 2004/10/09 12:53:02 kls Exp $ */ #include "device.h" #include #include #include #include "audio.h" #include "channels.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 -1 int cDevice::numDevices = 0; int cDevice::useDevice = 0; int cDevice::nextCardIndex = 0; int cDevice::currentChannel = 1; cDevice *cDevice::device[MAXDEVICES] = { NULL }; cDevice *cDevice::primaryDevice = NULL; cDevice::cDevice(void) { cardIndex = nextCardIndex++; SetDescription("receiver on device %d", CardIndex() + 1); SetVideoFormat(Setup.VideoFormat); active = false; mute = false; volume = Setup.CurrentVolume; sectionHandler = NULL; eitFilter = NULL; patFilter = NULL; sdtFilter = NULL; nitFilter = NULL; ciHandler = NULL; player = NULL; for (int i = 0; i < MAXRECEIVERS; i++) receiver[i] = NULL; if (numDevices < MAXDEVICES) device[numDevices++] = this; else esyslog("ERROR: too many devices!"); } cDevice::~cDevice() { Detach(player); for (int i = 0; i < MAXRECEIVERS; i++) Detach(receiver[i]); delete ciHandler; delete nitFilter; delete sdtFilter; delete patFilter; delete eitFilter; delete sectionHandler; } void cDevice::SetUseDevice(int n) { if (n < MAXDEVICES) useDevice |= (1 << n); } int cDevice::NextCardIndex(int n) { if (n > 0) { nextCardIndex += n; if (nextCardIndex >= MAXDEVICES) esyslog("ERROR: nextCardIndex too big (%d)", nextCardIndex); } else if (n < 0) esyslog("ERROR: illegal value in IncCardIndex(%d)", n); return nextCardIndex; } int cDevice::DeviceNumber(void) const { for (int i = 0; i < numDevices; i++) { if (device[i] == this) return i; } return -1; } void cDevice::MakePrimaryDevice(bool On) { } bool cDevice::SetPrimaryDevice(int n) { n--; if (0 <= n && n < numDevices && device[n]) { isyslog("setting primary device to %d", n + 1); if (primaryDevice) primaryDevice->MakePrimaryDevice(false); primaryDevice = device[n]; primaryDevice->MakePrimaryDevice(true); return true; } esyslog("ERROR: invalid primary device number: %d", n + 1); return false; } bool cDevice::HasDecoder(void) const { return false; } cSpuDecoder *cDevice::GetSpuDecoder(void) { return NULL; } cDevice *cDevice::ActualDevice(void) { cDevice *d = cTransferControl::ReceiverDevice(); if (!d) d = PrimaryDevice(); return d; } cDevice *cDevice::GetDevice(int Index) { return (0 <= Index && Index < numDevices) ? device[Index] : NULL; } cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers) { cDevice *d = NULL; int select = 7, pri; for (int i = 0; i < numDevices; i++) { bool ndr; if (device[i]->ProvidesChannel(Channel, Priority, &ndr)) { // this device is basicly able to do the job if (device[i]->Receiving() && !ndr) pri = 0; // receiving and allows additional receivers else if (d && !device[i]->Receiving() && device[i]->ProvidesCa(Channel) < d->ProvidesCa(Channel)) pri = 1; // free and fewer Ca's else if (!device[i]->Receiving() && !device[i]->IsPrimaryDevice()) pri = 2; // free and not the primary device else if (!device[i]->Receiving()) pri = 3; // free else if (d && device[i]->Priority() < d->Priority()) pri = 4; // receiving but priority is lower else if (d && device[i]->Priority() == d->Priority() && device[i]->ProvidesCa(Channel) < d->ProvidesCa(Channel)) pri = 5; // receiving with same priority but fewer Ca's else pri = 6; // all others if (pri < select) { select = pri; d = device[i]; if (NeedsDetachReceivers) *NeedsDetachReceivers = ndr; } } } /*XXX+ too complex with multiple recordings per device if (!d && Ca > MAXDEVICES) { // We didn't find one the easy way, so now we have to try harder: int ShiftLevel = -1; for (int i = 0; i < numDevices; i++) { if (Provides[i]) { // this device is basicly able to do the job, but for some reason we didn't get it above int sl = device[i]->CanShift(Ca, Priority); // asks this device to shift its job to another device if (sl >= 0 && (ShiftLevel < 0 || sl < ShiftLevel)) { d = device[i]; // found one that can be shifted with the fewest number of subsequent shifts ShiftLevel = sl; } } } } XXX*/ return d; } void cDevice::Shutdown(void) { for (int i = 0; i < numDevices; i++) { delete device[i]; device[i] = NULL; } primaryDevice = NULL; } bool cDevice::GrabImage(const char *FileName, bool Jpeg, int Quality, int SizeX, int SizeY) { return false; } void cDevice::SetVideoFormat(bool VideoFormat16_9) { } eVideoSystem cDevice::GetVideoSystem(void) { return vsPAL; } //#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) const { 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 || PidType == ptPcr) { int n = -1; int a = -1; if (PidType != ptPcr) { // PPID always has to be explicit for (int i = 0; i < MAXPIDHANDLES; i++) { if (i != ptPcr) { if (pidHandles[i].pid == Pid) n = i; else if (a < 0 && i >= ptOther && !pidHandles[i].used) a = i; } } } if (n >= 0) { // 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"); return SetPid(&pidHandles[n], n, true); } PRINTPIDS("a"); return true; } else if (PidType < ptOther) { // The Pid is not yet in use and it is a special one n = PidType; } else if (a >= 0) { // The Pid is not yet in use and we have a free slot n = a; } else esyslog("ERROR: no free slot for PID %d", Pid); if (n >= 0) { pidHandles[n].pid = Pid; pidHandles[n].used = 1; PRINTPIDS("C"); return SetPid(&pidHandles[n], n, true); } } return true; } void cDevice::DelPid(int Pid, ePidType PidType) { if (Pid || PidType == ptPcr) { int n = -1; if (PidType == ptPcr) n = PidType; // PPID always has to be explicit else { for (int i = 0; i < MAXPIDHANDLES; i++) { if (pidHandles[i].pid == Pid) { n = i; break; } } } if (n >= 0 && pidHandles[n].used) { PRINTPIDS("D"); if (--pidHandles[n].used < 2) { SetPid(&pidHandles[n], n, false); if (pidHandles[n].used == 0) { pidHandles[n].handle = -1; pidHandles[n].pid = 0; } } PRINTPIDS("E"); } } } bool cDevice::SetPid(cPidHandle *Handle, int Type, bool On) { return false; } void cDevice::StartSectionHandler(void) { if (!sectionHandler) { sectionHandler = new cSectionHandler(this); AttachFilter(eitFilter = new cEitFilter); AttachFilter(patFilter = new cPatFilter); AttachFilter(sdtFilter = new cSdtFilter(patFilter)); AttachFilter(nitFilter = new cNitFilter); sectionHandler->SetStatus(true); } } int cDevice::OpenFilter(u_short Pid, u_char Tid, u_char Mask) { return -1; } void cDevice::AttachFilter(cFilter *Filter) { if (sectionHandler) sectionHandler->Attach(Filter); } void cDevice::Detach(cFilter *Filter) { if (sectionHandler) sectionHandler->Detach(Filter); } bool cDevice::ProvidesSource(int Source) const { return false; } bool cDevice::ProvidesTransponder(const cChannel *Channel) const { return false; } bool cDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers) const { 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: Skins.Message(mtError, tr("Channel not available!")); return false; case scrNoTransfer: Skins.Message(mtError, tr("Can't start Transfer Mode!")); return false; case scrFailed: break; // loop will retry } esyslog("retrying"); } return false; } bool cDevice::SwitchChannel(int Direction) { bool result = false; Direction = sgn(Direction); if (Direction) { int n = CurrentChannel() + Direction; int first = n; cChannel *channel; while ((channel = Channels.GetByNumber(n, Direction)) != NULL) { // try only channels which are currently available if (PrimaryDevice()->ProvidesChannel(channel, Setup.PrimaryLimit) || PrimaryDevice()->CanReplay() && GetDevice(channel, 0)) break; n = channel->Number() + Direction; } if (channel) { 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)); if (PrimaryDevice()->SwitchChannel(channel, true)) result = true; } else if (n != first) Skins.Message(mtError, tr("Channel not available!")); } return result; } eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView) { 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 = (LiveView && IsPrimaryDevice() && !ProvidesChannel(Channel, Setup.PrimaryLimit)); eSetChannelResult Result = scrOk; // If this DVB card can't receive this channel, let's see if we can // use the card that actually can receive it and transfer data from there: if (NeedsTransferMode) { cDevice *CaDevice = GetDevice(Channel, 0); if (CaDevice && CanReplay()) { cStatus::MsgChannelSwitch(this, 0); // only report status if we are actually going to switch the channel if (CaDevice->SetChannel(Channel, false) == scrOk) // calling SetChannel() directly, not SwitchChannel()! cControl::Launch(new cTransferControl(CaDevice, Channel->Vpid(), Channel->Apid1(), Channel->Apid2(), Channel->Dpid1(), Channel->Dpid2()));//XXX+ else Result = scrNoTransfer; } else Result = scrNotAvailable; } else { Channels.Lock(false); cStatus::MsgChannelSwitch(this, 0); // only report status if we are actually going to switch the channel // Stop section handling: if (sectionHandler) { sectionHandler->SetStatus(false); sectionHandler->SetChannel(NULL); } if (SetChannelDevice(Channel, LiveView)) { // Start section handling: if (sectionHandler) { sectionHandler->SetChannel(Channel); sectionHandler->SetStatus(true); } } else Result = scrFailed; Channels.Unlock(); } if (Result == scrOk) { if (LiveView && IsPrimaryDevice()) currentChannel = Channel->Number(); cStatus::MsgChannelSwitch(this, Channel->Number()); // only report status if channel switch successfull } return Result; } bool cDevice::SetChannelDevice(const cChannel *Channel, bool LiveView) { return false; } bool cDevice::HasLock(void) { return true; } bool cDevice::HasProgramme(void) { return Replaying() || pidHandles[ptAudio].pid || pidHandles[ptVideo].pid; } void cDevice::SetVolumeDevice(int Volume) { } int cDevice::NumAudioTracksDevice(void) const { return 0; } const char **cDevice::GetAudioTracksDevice(int *CurrentTrack) const { return NULL; } void cDevice::SetAudioTrackDevice(int Index) { } bool cDevice::ToggleMute(void) { int OldVolume = volume; mute = !mute; //XXX why is it necessary to use different sequences??? if (mute) { SetVolume(0, mute); Audios.MuteAudio(mute); // Mute external audio after analog audio } else { Audios.MuteAudio(mute); // Enable external audio before analog audio SetVolume(0, mute); } volume = OldVolume; return mute; } void cDevice::SetVolume(int Volume, bool Absolute) { volume = min(max(Absolute ? Volume : volume + Volume, 0), MAXVOLUME); SetVolumeDevice(volume); cStatus::MsgSetVolume(volume, Absolute); if (volume > 0) { mute = false; Audios.MuteAudio(mute); } } int cDevice::NumAudioTracks(void) const { return player ? player->NumAudioTracks() : NumAudioTracksDevice(); } const char **cDevice::GetAudioTracks(int *CurrentTrack) const { return player ? player->GetAudioTracks(CurrentTrack) : GetAudioTracksDevice(CurrentTrack); } void cDevice::SetAudioTrack(int Index) { if (player) player->SetAudioTrack(Index); else SetAudioTrackDevice(Index); } bool cDevice::CanReplay(void) const { return HasDecoder(); } bool cDevice::SetPlayMode(ePlayMode PlayMode) { return false; } int64_t cDevice::GetSTC(void) { return -1; } void cDevice::TrickSpeed(int Speed) { } void cDevice::Clear(void) { Audios.ClearAudio(); } void cDevice::Play(void) { Audios.MuteAudio(mute); } void cDevice::Freeze(void) { Audios.MuteAudio(true); } void cDevice::Mute(void) { Audios.MuteAudio(true); } void cDevice::StillPicture(const uchar *Data, int Length) { } bool cDevice::Replaying(void) const { return player != NULL; } bool cDevice::AttachPlayer(cPlayer *Player) { if (CanReplay()) { if (player) Detach(player); player = Player; player->device = this; SetPlayMode(player->playMode); player->Activate(true); return true; } return false; } void cDevice::Detach(cPlayer *Player) { if (Player && player == Player) { player->Activate(false); player->device = NULL; player = NULL; SetPlayMode(pmNone); Audios.ClearAudio(); } } void cDevice::StopReplay(void) { if (player) { Detach(player); if (IsPrimaryDevice()) cControl::Shutdown(); } } bool cDevice::Poll(cPoller &Poller, int TimeoutMs) { return false; } bool cDevice::Flush(int TimeoutMs) { return true; } int cDevice::PlayVideo(const uchar *Data, int Length) { return -1; } void cDevice::PlayAudio(const uchar *Data, int Length) { Audios.PlayAudio(Data, Length); } int cDevice::Ca(void) const { int ca = 0; for (int i = 0; i < MAXRECEIVERS; i++) { if (receiver[i] && (ca = receiver[i]->ca) != 0) break; // all receivers have the same ca } return ca; } int cDevice::Priority(void) const { int priority = IsPrimaryDevice() ? Setup.PrimaryLimit - 1 : DEFAULTPRIORITY; for (int i = 0; i < MAXRECEIVERS; i++) { if (receiver[i]) priority = max(receiver[i]->priority, priority); } return priority; } int cDevice::CanShift(int Ca, int Priority, int UsedCards) const { return -1;//XXX+ too complex with multiple recordings per device /*XXX // Test whether a receiver on this device can be shifted to another one // in order to perform a new receiving with the given Ca and Priority on this device: int ShiftLevel = -1; // default means this device can't be shifted if (UsedCards & (1 << CardIndex()) != 0) return ShiftLevel; // otherwise we would get into a loop if (Receiving()) { if (ProvidesCa(Ca) // this device provides the requested Ca && (Ca != this->Ca() // the requested Ca is different from the one currently used... || Priority > this->Priority())) { // ...or the request comes from a higher priority cDevice *d = NULL; int Provides[MAXDEVICES]; UsedCards |= (1 << CardIndex()); for (int i = 0; i < numDevices; i++) { if ((Provides[i] = device[i]->ProvidesCa(this->Ca())) != 0) { // this device is basicly able to do the job if (device[i] != this) { // it is not _this_ device int sl = device[i]->CanShift(this->Ca(), Priority, UsedCards); // this is the original Priority! if (sl >= 0 && (ShiftLevel < 0 || sl < ShiftLevel)) { d = device[i]; ShiftLevel = sl; } } } } if (ShiftLevel >= 0) ShiftLevel++; // adds the device's own shift } } else if (Priority > this->Priority()) ShiftLevel = 0; // no shifting necessary, this device can do the job return ShiftLevel; XXX*/ } int cDevice::ProvidesCa(const cChannel *Channel) const { int Ca = Channel->Ca(); if (Ca == CardIndex() + 1) return 1; // exactly _this_ card was requested if (Ca && Ca <= MAXDEVICES) return 0; // a specific card was requested, but not _this_ one return !Ca; // by default every card can provide FTA } bool cDevice::Receiving(bool CheckAny) const { for (int i = 0; i < MAXRECEIVERS; i++) { if (receiver[i] && (CheckAny || receiver[i]->priority >= 0)) // cReceiver with priority < 0 doesn't count return true; } return false; } void cDevice::Action(void) { if (OpenDvr()) { active = true; for (; active;) { // Read data from the DVR device: 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(); for (int i = 0; i < MAXRECEIVERS; i++) { if (receiver[i] && receiver[i]->WantsPid(Pid)) receiver[i]->Receive(b, TS_SIZE); } Unlock(); } } else break; } CloseDvr(); } } bool cDevice::OpenDvr(void) { return false; } void cDevice::CloseDvr(void) { } bool cDevice::GetTSPacket(uchar *&Data) { return false; } bool cDevice::AttachReceiver(cReceiver *Receiver) { if (!Receiver) return false; if (Receiver->device == this) return true; for (int i = 0; i < MAXRECEIVERS; i++) { if (!receiver[i]) { for (int n = 0; n < MAXRECEIVEPIDS; n++) AddPid(Receiver->pids[n]);//XXX+ retval! Receiver->Activate(true); Lock(); Receiver->device = this; receiver[i] = Receiver; Unlock(); Start(); return true; } } esyslog("ERROR: no free receiver slot!"); return false; } void cDevice::Detach(cReceiver *Receiver) { if (!Receiver || Receiver->device != this) return; bool receiversLeft = false; for (int i = 0; i < MAXRECEIVERS; i++) { if (receiver[i] == Receiver) { Receiver->Activate(false); Lock(); receiver[i] = NULL; Receiver->device = NULL; Unlock(); for (int n = 0; n < MAXRECEIVEPIDS; n++) DelPid(Receiver->pids[n]); } else if (receiver[i]) receiversLeft = true; } if (!receiversLeft) { active = false; Cancel(3); } } // --- cTSBuffer ------------------------------------------------------------- cTSBuffer::cTSBuffer(int File, int Size, int CardIndex) { SetDescription("TS buffer on device %d", CardIndex); f = File; cardIndex = CardIndex; active = false; delivered = false; ringBuffer = new cRingBufferLinear(Size, TS_SIZE, true, "TS"); ringBuffer->SetTimeouts(100, 100); Start(); } cTSBuffer::~cTSBuffer() { active = false; Cancel(3); delete ringBuffer; } void cTSBuffer::Action(void) { if (ringBuffer) { bool firstRead = true; cPoller Poller(f); active = true; for (; active;) { if (firstRead || Poller.Poll(100)) { firstRead = false; int r = ringBuffer->Read(f); if (r < 0 && FATALERRNO) { if (errno == EOVERFLOW) esyslog("ERROR: driver buffer overflow on device %d", cardIndex); else { LOG_ERROR; break; } } } } } } uchar *cTSBuffer::Get(void) { int Count = 0; if (delivered) { ringBuffer->Del(TS_SIZE); delivered = false; } uchar *p = ringBuffer->Get(Count); if (p && Count >= TS_SIZE) { if (*p != TS_SYNC_BYTE) { for (int i = 1; i < Count; i++) { if (p[i] == TS_SYNC_BYTE) { Count = i; break; } } ringBuffer->Del(Count); esyslog("ERROR: skipped %d bytes to sync on TS packet on device %d", Count, cardIndex); return NULL; } delivered = true; return p; } return NULL; }