/* * 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.47.1.1 2003/11/07 13:16:12 kls Exp $ */ #include "device.h" #include #include #include #include "audio.h" #include "channels.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 -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++; SetVideoFormat(Setup.VideoFormat); active = false; mute = false; volume = Setup.CurrentVolume; ciHandler = NULL; player = NULL; for (int i = 0; i < MAXRECEIVERS; i++) receiver[i] = NULL; if (numDevices < MAXDEVICES) { device[numDevices++] = this; SetCaCaps(cardIndex); } else esyslog("ERROR: too many devices!"); } cDevice::~cDevice() { Detach(player); for (int i = 0; i < MAXRECEIVERS; i++) Detach(receiver[i]); delete ciHandler; } 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; } cOsdBase *cDevice::NewOsd(int x, int y) { return NULL; } 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->Ca()) < d->ProvidesCa(Channel->Ca())) 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->Ca()) < d->ProvidesCa(Channel->Ca())) 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::SetCaCaps(int Index) { for (int d = 0; d < numDevices; d++) { if (Index < 0 || Index == device[d]->CardIndex()) { for (int i = 0; i < MAXCACAPS; i++) device[d]->caCaps[i] = Setup.CaCaps[device[d]->CardIndex()][i]; } } } 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; } bool cDevice::ProvidesSource(int Source) 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: if (Interface) Interface->Error(tr("Channel not available!")); 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; } 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 && Interface) Interface->Error(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 { cStatus::MsgChannelSwitch(this, 0); // only report status if we are actually going to switch the channel if (!SetChannelDevice(Channel, LiveView)) Result = scrFailed; } if (Result == scrOk) { if (LiveView && IsPrimaryDevice()) { cSIProcessor::SetCurrentChannelID(Channel->GetChannelID()); 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::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; } 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 // 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; } int cDevice::ProvidesCa(int Ca) const { 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 int result = Ca ? 0 : 1; // by default every card can provide FTA int others = Ca ? 1 : 0; for (int i = 0; i < MAXCACAPS; i++) { if (caCaps[i]) { if (caCaps[i] == Ca) result = 1; else others++; } } return result ? result + others : 0; } 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) { dsyslog("receiver thread started on device %d (pid=%d)", CardIndex() + 1, getpid()); 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(); } dsyslog("receiver thread ended on device %d (pid=%d)", CardIndex() + 1, getpid()); } 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) { 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; }