vdr-1.4.7/device.c

Go to the documentation of this file.
00001 /*
00002  * device.c: The basic device interface
00003  *
00004  * See the main source file 'vdr.c' for copyright information and
00005  * how to reach the author.
00006  *
00007  * $Id: device.c 1.137 2006/09/03 10:13:25 kls Exp $
00008  */
00009 
00010 #include "device.h"
00011 #include <errno.h>
00012 #include <sys/ioctl.h>
00013 #include <sys/mman.h>
00014 #include "audio.h"
00015 #include "channels.h"
00016 #include "i18n.h"
00017 #include "player.h"
00018 #include "receiver.h"
00019 #include "status.h"
00020 #include "transfer.h"
00021 
00022 // --- cPesAssembler ---------------------------------------------------------
00023 
00024 class cPesAssembler {
00025 private:
00026   uchar *data;
00027   uint32_t tag;
00028   int length;
00029   int size;
00030   bool Realloc(int Size);
00031 public:
00032   cPesAssembler(void);
00033   ~cPesAssembler();
00034   int ExpectedLength(void) { return PacketSize(data); }
00035   static int PacketSize(const uchar *data);
00036   int Length(void) { return length; }
00037   const uchar *Data(void) { return data; } // only valid if Length() >= 4
00038   void Reset(void);
00039   void Put(uchar c);
00040   void Put(const uchar *Data, int Length);
00041   bool IsPes(void);
00042   };
00043 
00044 cPesAssembler::cPesAssembler(void)
00045 {
00046   data = NULL;
00047   size = 0;
00048   Reset();
00049 }
00050 
00051 cPesAssembler::~cPesAssembler()
00052 {
00053   free(data);
00054 }
00055 
00056 void cPesAssembler::Reset(void)
00057 {
00058   tag = 0xFFFFFFFF;
00059   length = 0;
00060 }
00061 
00062 bool cPesAssembler::Realloc(int Size)
00063 {
00064   if (Size > size) {
00065      size = max(Size, 2048);
00066      data = (uchar *)realloc(data, size);
00067      if (!data) {
00068         esyslog("ERROR: can't allocate memory for PES assembler");
00069         length = 0;
00070         size = 0;
00071         return false;
00072         }
00073      }
00074   return true;
00075 }
00076 
00077 void cPesAssembler::Put(uchar c)
00078 {
00079   if (length < 4) {
00080      tag = (tag << 8) | c;
00081      if ((tag & 0xFFFFFF00) == 0x00000100) {
00082         if (Realloc(4)) {
00083            *(uint32_t *)data = htonl(tag);
00084            length = 4;
00085            }
00086         }
00087      else if (length < 3)
00088         length++;
00089      }
00090   else if (Realloc(length + 1))
00091      data[length++] = c;
00092 }
00093 
00094 void cPesAssembler::Put(const uchar *Data, int Length)
00095 {
00096   while (length < 4 && Length > 0) {
00097         Put(*Data++);
00098         Length--;
00099         }
00100   if (Length && Realloc(length + Length)) {
00101      memcpy(data + length, Data, Length);
00102      length += Length;
00103      }
00104 }
00105 
00106 int cPesAssembler::PacketSize(const uchar *data)
00107 {
00108   // we need atleast 6 bytes of data here !!!
00109   switch (data[3]) {
00110     default:
00111     case 0x00 ... 0xB8: // video stream start codes
00112     case 0xB9: // Program end
00113     case 0xBC: // Programm stream map
00114     case 0xF0 ... 0xFF: // reserved
00115          return 6;
00116 
00117     case 0xBA: // Pack header
00118          if ((data[4] & 0xC0) == 0x40) // MPEG2
00119             return 14;
00120          // to be absolutely correct we would have to add the stuffing bytes
00121          // as well, but at this point we only may have 6 bytes of data avail-
00122          // able. So it's up to the higher level to resync...
00123          //return 14 + (data[13] & 0x07); // add stuffing bytes
00124          else // MPEG1
00125             return 12;
00126 
00127     case 0xBB: // System header
00128     case 0xBD: // Private stream1
00129     case 0xBE: // Padding stream
00130     case 0xBF: // Private stream2 (navigation data)
00131     case 0xC0 ... 0xCF: // all the rest (the real packets)
00132     case 0xD0 ... 0xDF:
00133     case 0xE0 ... 0xEF:
00134          return 6 + data[4] * 256 + data[5];
00135     }
00136 }
00137 
00138 // --- cDevice ---------------------------------------------------------------
00139 
00140 // The default priority for non-primary devices:
00141 #define DEFAULTPRIORITY  -1
00142 
00143 int cDevice::numDevices = 0;
00144 int cDevice::useDevice = 0;
00145 int cDevice::nextCardIndex = 0;
00146 int cDevice::currentChannel = 1;
00147 cDevice *cDevice::device[MAXDEVICES] = { NULL };
00148 cDevice *cDevice::primaryDevice = NULL;
00149 
00150 cDevice::cDevice(void)
00151 {
00152   cardIndex = nextCardIndex++;
00153 
00154   SetDescription("receiver on device %d", CardIndex() + 1);
00155 
00156   SetVideoFormat(Setup.VideoFormat);
00157 
00158   mute = false;
00159   volume = Setup.CurrentVolume;
00160 
00161   sectionHandler = NULL;
00162   eitFilter = NULL;
00163   patFilter = NULL;
00164   sdtFilter = NULL;
00165   nitFilter = NULL;
00166 
00167   ciHandler = NULL;
00168   player = NULL;
00169   pesAssembler = new cPesAssembler;
00170   ClrAvailableTracks();
00171   currentAudioTrack = ttNone;
00172   currentAudioTrackMissingCount = 0;
00173 
00174   for (int i = 0; i < MAXRECEIVERS; i++)
00175       receiver[i] = NULL;
00176 
00177   if (numDevices < MAXDEVICES)
00178      device[numDevices++] = this;
00179   else
00180      esyslog("ERROR: too many devices!");
00181 }
00182 
00183 cDevice::~cDevice()
00184 {
00185   Detach(player);
00186   for (int i = 0; i < MAXRECEIVERS; i++)
00187       Detach(receiver[i]);
00188   delete ciHandler;
00189   delete nitFilter;
00190   delete sdtFilter;
00191   delete patFilter;
00192   delete eitFilter;
00193   delete sectionHandler;
00194   delete pesAssembler;
00195 }
00196 
00197 bool cDevice::WaitForAllDevicesReady(int Timeout)
00198 {
00199   for (time_t t0 = time(NULL); time(NULL) - t0 < Timeout; ) {
00200       bool ready = true;
00201       for (int i = 0; i < numDevices; i++) {
00202           if (device[i] && !device[i]->Ready())
00203              ready = false;
00204           }
00205       if (ready)
00206          return true;
00207       }
00208   return false;
00209 }
00210 
00211 void cDevice::SetUseDevice(int n)
00212 {
00213   if (n < MAXDEVICES)
00214      useDevice |= (1 << n);
00215 }
00216 
00217 int cDevice::NextCardIndex(int n)
00218 {
00219   if (n > 0) {
00220      nextCardIndex += n;
00221      if (nextCardIndex >= MAXDEVICES)
00222         esyslog("ERROR: nextCardIndex too big (%d)", nextCardIndex);
00223      }
00224   else if (n < 0)
00225      esyslog("ERROR: invalid value in IncCardIndex(%d)", n);
00226   return nextCardIndex;
00227 }
00228 
00229 int cDevice::DeviceNumber(void) const
00230 {
00231   for (int i = 0; i < numDevices; i++) {
00232       if (device[i] == this)
00233          return i;
00234       }
00235   return -1;
00236 }
00237 
00238 void cDevice::MakePrimaryDevice(bool On)
00239 {
00240 }
00241 
00242 bool cDevice::SetPrimaryDevice(int n)
00243 {
00244   n--;
00245   if (0 <= n && n < numDevices && device[n]) {
00246      isyslog("setting primary device to %d", n + 1);
00247      if (primaryDevice)
00248         primaryDevice->MakePrimaryDevice(false);
00249      primaryDevice = device[n];
00250      primaryDevice->MakePrimaryDevice(true);
00251      primaryDevice->SetVideoFormat(Setup.VideoFormat);
00252      return true;
00253      }
00254   esyslog("ERROR: invalid primary device number: %d", n + 1);
00255   return false;
00256 }
00257 
00258 bool cDevice::HasDecoder(void) const
00259 {
00260   return false;
00261 }
00262 
00263 cSpuDecoder *cDevice::GetSpuDecoder(void)
00264 {
00265   return NULL;
00266 }
00267 
00268 cDevice *cDevice::ActualDevice(void)
00269 {
00270   cDevice *d = cTransferControl::ReceiverDevice();
00271   if (!d)
00272      d = PrimaryDevice();
00273   return d;
00274 }
00275 
00276 cDevice *cDevice::GetDevice(int Index)
00277 {
00278   return (0 <= Index && Index < numDevices) ? device[Index] : NULL;
00279 }
00280 
00281 cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers)
00282 {
00283   cDevice *d = NULL;
00284   uint Impact = 0xFFFFFFFF; // we're looking for a device with the least impact
00285   for (int i = 0; i < numDevices; i++) {
00286       bool ndr;
00287       if (device[i]->ProvidesChannel(Channel, Priority, &ndr)) { // this device is basicly able to do the job
00288          // Put together an integer number that reflects the "impact" using
00289          // this device would have on the overall system. Each condition is represented
00290          // by one bit in the number (or several bits, if the condition is actually
00291          // a numeric value). The sequence in which the conditions are listed corresponds
00292          // to their individual severity, where the one listed first will make the most
00293          // difference, because it results in the most significant bit of the result.
00294          uint imp = 0;
00295          imp <<= 1; imp |= !device[i]->Receiving() || ndr;                         // use receiving devices if we don't need to detach existing receivers
00296          imp <<= 1; imp |= device[i]->Receiving();                                 // avoid devices that are receiving
00297          imp <<= 1; imp |= device[i] == cTransferControl::ReceiverDevice();        // avoid the Transfer Mode receiver device
00298          imp <<= 8; imp |= min(max(device[i]->Priority() + MAXPRIORITY, 0), 0xFF); // use the device with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used)
00299          imp <<= 8; imp |= min(max(device[i]->ProvidesCa(Channel), 0), 0xFF);      // use the device that provides the lowest number of conditional access methods
00300          imp <<= 1; imp |= device[i]->IsPrimaryDevice();                           // avoid the primary device
00301          imp <<= 1; imp |= device[i]->HasDecoder();                                // avoid full featured cards
00302          if (imp < Impact) {
00303             // This device has less impact than any previous one, so we take it.
00304             Impact = imp;
00305             d = device[i];
00306             if (NeedsDetachReceivers)
00307                *NeedsDetachReceivers = ndr;
00308             }
00309          }
00310       }
00311   return d;
00312 }
00313 
00314 void cDevice::Shutdown(void)
00315 {
00316   primaryDevice = NULL;
00317   for (int i = 0; i < numDevices; i++) {
00318       delete device[i];
00319       device[i] = NULL;
00320       }
00321 }
00322 
00323 uchar *cDevice::GrabImage(int &Size, bool Jpeg, int Quality, int SizeX, int SizeY)
00324 {
00325   return NULL;
00326 }
00327 
00328 bool cDevice::GrabImageFile(const char *FileName, bool Jpeg, int Quality, int SizeX, int SizeY)
00329 {
00330   int result = 0;
00331   int fd = open(FileName, O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC, DEFFILEMODE);
00332   if (fd >= 0) {
00333      int ImageSize;
00334      uchar *Image = GrabImage(ImageSize, Jpeg, Quality, SizeX, SizeY);
00335      if (Image) {
00336         if (safe_write(fd, Image, ImageSize) == ImageSize)
00337            isyslog("grabbed image to %s", FileName);
00338         else {
00339            LOG_ERROR_STR(FileName);
00340            result |= 1;
00341            }
00342         free(Image);
00343         }
00344      else
00345         result |= 1;
00346      close(fd);
00347      }
00348   else {
00349      LOG_ERROR_STR(FileName);
00350      result |= 1;
00351      }
00352   return result == 0;
00353 }
00354 
00355 void cDevice::SetVideoDisplayFormat(eVideoDisplayFormat VideoDisplayFormat)
00356 {
00357   cSpuDecoder *spuDecoder = GetSpuDecoder();
00358   if (spuDecoder) {
00359      if (Setup.VideoFormat)
00360         spuDecoder->setScaleMode(cSpuDecoder::eSpuNormal);
00361      else {
00362         switch (VideoDisplayFormat) {
00363                case vdfPanAndScan:
00364                     spuDecoder->setScaleMode(cSpuDecoder::eSpuPanAndScan);
00365                     break;
00366                case vdfLetterBox:
00367                     spuDecoder->setScaleMode(cSpuDecoder::eSpuLetterBox);
00368                     break;
00369                case vdfCenterCutOut:
00370                     spuDecoder->setScaleMode(cSpuDecoder::eSpuNormal);
00371                     break;
00372                }
00373         }
00374      }
00375 }
00376 
00377 void cDevice::SetVideoFormat(bool VideoFormat16_9)
00378 {
00379 }
00380 
00381 eVideoSystem cDevice::GetVideoSystem(void)
00382 {
00383   return vsPAL;
00384 }
00385 
00386 //#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); }
00387 #define PRINTPIDS(s)
00388 
00389 bool cDevice::HasPid(int Pid) const
00390 {
00391   for (int i = 0; i < MAXPIDHANDLES; i++) {
00392       if (pidHandles[i].pid == Pid)
00393          return true;
00394       }
00395   return false;
00396 }
00397 
00398 bool cDevice::AddPid(int Pid, ePidType PidType)
00399 {
00400   if (Pid || PidType == ptPcr) {
00401      int n = -1;
00402      int a = -1;
00403      if (PidType != ptPcr) { // PPID always has to be explicit
00404         for (int i = 0; i < MAXPIDHANDLES; i++) {
00405             if (i != ptPcr) {
00406                if (pidHandles[i].pid == Pid)
00407                   n = i;
00408                else if (a < 0 && i >= ptOther && !pidHandles[i].used)
00409                   a = i;
00410                }
00411             }
00412         }
00413      if (n >= 0) {
00414         // The Pid is already in use
00415         if (++pidHandles[n].used == 2 && n <= ptTeletext) {
00416            // It's a special PID that may have to be switched into "tap" mode
00417            PRINTPIDS("A");
00418            if (!SetPid(&pidHandles[n], n, true)) {
00419               esyslog("ERROR: can't set PID %d on device %d", Pid, CardIndex() + 1);
00420               if (PidType <= ptTeletext)
00421                  DetachAll(Pid);
00422               DelPid(Pid, PidType);
00423               return false;
00424               }
00425            if (ciHandler)
00426               ciHandler->SetPid(Pid, true);
00427            }
00428         PRINTPIDS("a");
00429         return true;
00430         }
00431      else if (PidType < ptOther) {
00432         // The Pid is not yet in use and it is a special one
00433         n = PidType;
00434         }
00435      else if (a >= 0) {
00436         // The Pid is not yet in use and we have a free slot
00437         n = a;
00438         }
00439      else {
00440         esyslog("ERROR: no free slot for PID %d on device %d", Pid, CardIndex() + 1);
00441         return false;
00442         }
00443      if (n >= 0) {
00444         pidHandles[n].pid = Pid;
00445         pidHandles[n].used = 1;
00446         PRINTPIDS("C");
00447         if (!SetPid(&pidHandles[n], n, true)) {
00448            esyslog("ERROR: can't set PID %d on device %d", Pid, CardIndex() + 1);
00449            if (PidType <= ptTeletext)
00450               DetachAll(Pid);
00451            DelPid(Pid, PidType);
00452            return false;
00453            }
00454         if (ciHandler)
00455            ciHandler->SetPid(Pid, true);
00456         }
00457      }
00458   return true;
00459 }
00460 
00461 void cDevice::DelPid(int Pid, ePidType PidType)
00462 {
00463   if (Pid || PidType == ptPcr) {
00464      int n = -1;
00465      if (PidType == ptPcr)
00466         n = PidType; // PPID always has to be explicit
00467      else {
00468         for (int i = 0; i < MAXPIDHANDLES; i++) {
00469             if (pidHandles[i].pid == Pid) {
00470                n = i;
00471                break;
00472                }
00473             }
00474         }
00475      if (n >= 0 && pidHandles[n].used) {
00476         PRINTPIDS("D");
00477         if (--pidHandles[n].used < 2) {
00478            SetPid(&pidHandles[n], n, false);
00479            if (pidHandles[n].used == 0) {
00480               pidHandles[n].handle = -1;
00481               pidHandles[n].pid = 0;
00482               if (ciHandler)
00483                  ciHandler->SetPid(Pid, false);
00484               }
00485            }
00486         PRINTPIDS("E");
00487         }
00488      }
00489 }
00490 
00491 bool cDevice::SetPid(cPidHandle *Handle, int Type, bool On)
00492 {
00493   return false;
00494 }
00495 
00496 void cDevice::StartSectionHandler(void)
00497 {
00498   if (!sectionHandler) {
00499      sectionHandler = new cSectionHandler(this);
00500      AttachFilter(eitFilter = new cEitFilter);
00501      AttachFilter(patFilter = new cPatFilter);
00502      AttachFilter(sdtFilter = new cSdtFilter(patFilter));
00503      AttachFilter(nitFilter = new cNitFilter);
00504      }
00505 }
00506 
00507 int cDevice::OpenFilter(u_short Pid, u_char Tid, u_char Mask)
00508 {
00509   return -1;
00510 }
00511 
00512 void cDevice::AttachFilter(cFilter *Filter)
00513 {
00514   if (sectionHandler)
00515      sectionHandler->Attach(Filter);
00516 }
00517 
00518 void cDevice::Detach(cFilter *Filter)
00519 {
00520   if (sectionHandler)
00521      sectionHandler->Detach(Filter);
00522 }
00523 
00524 bool cDevice::ProvidesSource(int Source) const
00525 {
00526   return false;
00527 }
00528 
00529 bool cDevice::ProvidesTransponder(const cChannel *Channel) const
00530 {
00531   return false;
00532 }
00533 
00534 bool cDevice::ProvidesTransponderExclusively(const cChannel *Channel) const
00535 {
00536   for (int i = 0; i < numDevices; i++) {
00537       if (device[i] && device[i] != this && device[i]->ProvidesTransponder(Channel))
00538          return false;
00539       }
00540   return true;
00541 }
00542 
00543 bool cDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers) const
00544 {
00545   return false;
00546 }
00547 
00548 bool cDevice::IsTunedToTransponder(const cChannel *Channel)
00549 {
00550   return false;
00551 }
00552 
00553 bool cDevice::MaySwitchTransponder(void)
00554 {
00555   return !Receiving(true) && !(pidHandles[ptAudio].pid || pidHandles[ptVideo].pid || pidHandles[ptDolby].pid);
00556 }
00557 
00558 bool cDevice::SwitchChannel(const cChannel *Channel, bool LiveView)
00559 {
00560   if (LiveView)
00561      isyslog("switching to channel %d", Channel->Number());
00562   for (int i = 3; i--;) {
00563       switch (SetChannel(Channel, LiveView)) {
00564         case scrOk:           return true;
00565         case scrNotAvailable: Skins.Message(mtInfo, tr("Channel not available!"));
00566                               return false;
00567         case scrNoTransfer:   Skins.Message(mtError, tr("Can't start Transfer Mode!"));
00568                               return false;
00569         case scrFailed:       break; // loop will retry
00570         }
00571       esyslog("retrying");
00572       }
00573   return false;
00574 }
00575 
00576 bool cDevice::SwitchChannel(int Direction)
00577 {
00578   bool result = false;
00579   Direction = sgn(Direction);
00580   if (Direction) {
00581      int n = CurrentChannel() + Direction;
00582      int first = n;
00583      cChannel *channel;
00584      while ((channel = Channels.GetByNumber(n, Direction)) != NULL) {
00585            // try only channels which are currently available
00586            if (PrimaryDevice()->ProvidesChannel(channel, Setup.PrimaryLimit) || PrimaryDevice()->CanReplay() && GetDevice(channel, 0))
00587               break;
00588            n = channel->Number() + Direction;
00589            }
00590      if (channel) {
00591         int d = n - first;
00592         if (abs(d) == 1)
00593            dsyslog("skipped channel %d", first);
00594         else if (d)
00595            dsyslog("skipped channels %d..%d", first, n - sgn(d));
00596         if (PrimaryDevice()->SwitchChannel(channel, true))
00597            result = true;
00598         }
00599      else if (n != first)
00600         Skins.Message(mtError, tr("Channel not available!"));
00601      }
00602   return result;
00603 }
00604 
00605 eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
00606 {
00607   if (LiveView)
00608      StopReplay();
00609 
00610   // If this card is switched to an other transponder, any receivers still
00611   // attached to it need to be automatically detached:
00612   bool NeedsDetachReceivers = false;
00613 
00614   // If this card can't receive this channel, we must not actually switch
00615   // the channel here, because that would irritate the driver when we
00616   // start replaying in Transfer Mode immediately after switching the channel:
00617   bool NeedsTransferMode = (LiveView && IsPrimaryDevice() && !ProvidesChannel(Channel, Setup.PrimaryLimit, &NeedsDetachReceivers));
00618 
00619   eSetChannelResult Result = scrOk;
00620 
00621   // If this DVB card can't receive this channel, let's see if we can
00622   // use the card that actually can receive it and transfer data from there:
00623 
00624   if (NeedsTransferMode) {
00625      cDevice *CaDevice = GetDevice(Channel, 0, &NeedsDetachReceivers);
00626      if (CaDevice && CanReplay()) {
00627         cStatus::MsgChannelSwitch(this, 0); // only report status if we are actually going to switch the channel
00628         if (CaDevice->SetChannel(Channel, false) == scrOk) { // calling SetChannel() directly, not SwitchChannel()!
00629            if (NeedsDetachReceivers)
00630               CaDevice->DetachAllReceivers();
00631            cControl::Launch(new cTransferControl(CaDevice, Channel->Vpid(), Channel->Apids(), Channel->Dpids(), Channel->Spids()));
00632            }
00633         else
00634            Result = scrNoTransfer;
00635         }
00636      else
00637         Result = scrNotAvailable;
00638      }
00639   else {
00640      Channels.Lock(false);
00641      cStatus::MsgChannelSwitch(this, 0); // only report status if we are actually going to switch the channel
00642      // Stop section handling:
00643      if (sectionHandler) {
00644         sectionHandler->SetStatus(false);
00645         sectionHandler->SetChannel(NULL);
00646         }
00647      // Tell the ciHandler about the channel switch and add all PIDs of this
00648      // channel to it, for possible later decryption:
00649      if (ciHandler) {
00650         ciHandler->SetSource(Channel->Source(), Channel->Transponder());
00651 // Men at work - please stand clear! ;-)
00652 #ifdef XXX_DO_MULTIPLE_CA_CHANNELS
00653         if (Channel->Ca() >= CA_ENCRYPTED_MIN) {
00654 #endif
00655            ciHandler->AddPid(Channel->Sid(), Channel->Vpid(), 2);
00656            for (const int *Apid = Channel->Apids(); *Apid; Apid++)
00657                ciHandler->AddPid(Channel->Sid(), *Apid, 4);
00658            for (const int *Dpid = Channel->Dpids(); *Dpid; Dpid++)
00659                ciHandler->AddPid(Channel->Sid(), *Dpid, 0);
00660 #ifdef XXX_DO_MULTIPLE_CA_CHANNELS
00661            bool CanDecrypt = ciHandler->CanDecrypt(Channel->Sid());//XXX
00662            dsyslog("CanDecrypt %d %d %d %s", CardIndex() + 1, CanDecrypt, Channel->Number(), Channel->Name());//XXX
00663            }
00664 #endif
00665         }
00666      if (NeedsDetachReceivers)
00667         DetachAllReceivers();
00668      if (SetChannelDevice(Channel, LiveView)) {
00669         // Start section handling:
00670         if (sectionHandler) {
00671            sectionHandler->SetChannel(Channel);
00672            sectionHandler->SetStatus(true);
00673            }
00674         // Start decrypting any PIDs that might have been set in SetChannelDevice():
00675         if (ciHandler)
00676            ciHandler->StartDecrypting();
00677         }
00678      else
00679         Result = scrFailed;
00680      Channels.Unlock();
00681      }
00682 
00683   if (Result == scrOk) {
00684      if (LiveView && IsPrimaryDevice()) {
00685         currentChannel = Channel->Number();
00686         // Set the available audio tracks:
00687         ClrAvailableTracks();
00688         for (int i = 0; i < MAXAPIDS; i++)
00689             SetAvailableTrack(ttAudio, i, Channel->Apid(i), Channel->Alang(i));
00690         if (Setup.UseDolbyDigital) {
00691            for (int i = 0; i < MAXDPIDS; i++)
00692                SetAvailableTrack(ttDolby, i, Channel->Dpid(i), Channel->Dlang(i));
00693            }
00694         if (!NeedsTransferMode)
00695            EnsureAudioTrack(true);
00696         }
00697      cStatus::MsgChannelSwitch(this, Channel->Number()); // only report status if channel switch successfull
00698      }
00699 
00700   return Result;
00701 }
00702 
00703 void cDevice::ForceTransferMode(void)
00704 {
00705   if (!cTransferControl::ReceiverDevice()) {
00706      cChannel *Channel = Channels.GetByNumber(CurrentChannel());
00707      if (Channel)
00708         SetChannelDevice(Channel, false); // this implicitly starts Transfer Mode
00709      }
00710 }
00711 
00712 bool cDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
00713 {
00714   return false;
00715 }
00716 
00717 bool cDevice::HasLock(int TimeoutMs)
00718 {
00719   return true;
00720 }
00721 
00722 bool cDevice::HasProgramme(void)
00723 {
00724   return Replaying() || pidHandles[ptAudio].pid || pidHandles[ptVideo].pid;
00725 }
00726 
00727 int cDevice::GetAudioChannelDevice(void)
00728 {
00729   return 0;
00730 }
00731 
00732 void cDevice::SetAudioChannelDevice(int AudioChannel)
00733 {
00734 }
00735 
00736 void cDevice::SetVolumeDevice(int Volume)
00737 {
00738 }
00739 
00740 void cDevice::SetDigitalAudioDevice(bool On)
00741 {
00742 }
00743 
00744 void cDevice::SetAudioTrackDevice(eTrackType Type)
00745 {
00746 }
00747 
00748 bool cDevice::ToggleMute(void)
00749 {
00750   int OldVolume = volume;
00751   mute = !mute;
00752   //XXX why is it necessary to use different sequences???
00753   if (mute) {
00754      SetVolume(0, true);
00755      Audios.MuteAudio(mute); // Mute external audio after analog audio
00756      }
00757   else {
00758      Audios.MuteAudio(mute); // Enable external audio before analog audio
00759      SetVolume(OldVolume, true);
00760      }
00761   volume = OldVolume;
00762   return mute;
00763 }
00764 
00765 int cDevice::GetAudioChannel(void)
00766 {
00767   int c = GetAudioChannelDevice();
00768   return (0 <= c && c <= 2) ? c : 0;
00769 }
00770 
00771 void cDevice::SetAudioChannel(int AudioChannel)
00772 {
00773   if (0 <= AudioChannel && AudioChannel <= 2)
00774      SetAudioChannelDevice(AudioChannel);
00775 }
00776 
00777 void cDevice::SetVolume(int Volume, bool Absolute)
00778 {
00779   int OldVolume = volume;
00780   volume = min(max(Absolute ? Volume : volume + Volume, 0), MAXVOLUME);
00781   SetVolumeDevice(volume);
00782   Absolute |= mute;
00783   cStatus::MsgSetVolume(Absolute ? volume : volume - OldVolume, Absolute);
00784   if (volume > 0) {
00785      mute = false;
00786      Audios.MuteAudio(mute);
00787      }
00788 }
00789 
00790 void cDevice::ClrAvailableTracks(bool DescriptionsOnly, bool IdsOnly)
00791 {
00792   if (DescriptionsOnly) {
00793      for (int i = ttNone; i < ttMaxTrackTypes; i++)
00794          *availableTracks[i].description = 0;
00795      }
00796   else {
00797      if (IdsOnly) {
00798         for (int i = ttNone; i < ttMaxTrackTypes; i++)
00799             availableTracks[i].id = 0;
00800         }
00801      else
00802         memset(availableTracks, 0, sizeof(availableTracks));
00803      pre_1_3_19_PrivateStream = false;
00804      SetAudioChannel(0); // fall back to stereo
00805      currentAudioTrackMissingCount = 0;
00806      currentAudioTrack = ttNone;
00807      }
00808 }
00809 
00810 bool cDevice::SetAvailableTrack(eTrackType Type, int Index, uint16_t Id, const char *Language, const char *Description)
00811 {
00812   eTrackType t = eTrackType(Type + Index);
00813   if (Type == ttAudio && IS_AUDIO_TRACK(t) ||
00814       Type == ttDolby && IS_DOLBY_TRACK(t)) {
00815      if (Language)
00816         strn0cpy(availableTracks[t].language, Language, sizeof(availableTracks[t].language));
00817      if (Description)
00818         strn0cpy(availableTracks[t].description, Description, sizeof(availableTracks[t].description));
00819      if (Id) {
00820         availableTracks[t].id = Id; // setting 'id' last to avoid the need for extensive locking
00821         int numAudioTracks = NumAudioTracks();
00822         if (!availableTracks[currentAudioTrack].id && numAudioTracks && currentAudioTrackMissingCount++ > numAudioTracks * 10)
00823            EnsureAudioTrack();
00824         else if (t == currentAudioTrack)
00825            currentAudioTrackMissingCount = 0;
00826         }
00827      return true;
00828      }
00829   else
00830      esyslog("ERROR: SetAvailableTrack called with invalid Type/Index (%d/%d)", Type, Index);
00831   return false;
00832 }
00833 
00834 const tTrackId *cDevice::GetTrack(eTrackType Type)
00835 {
00836   return (ttNone < Type && Type < ttMaxTrackTypes) ? &availableTracks[Type] : NULL;
00837 }
00838 
00839 int cDevice::NumAudioTracks(void) const
00840 {
00841   int n = 0;
00842   for (int i = ttAudioFirst; i <= ttDolbyLast; i++) {
00843       if (availableTracks[i].id)
00844          n++;
00845       }
00846   return n;
00847 }
00848 
00849 bool cDevice::SetCurrentAudioTrack(eTrackType Type)
00850 {
00851   if (ttNone < Type && Type < ttDolbyLast) {
00852      cMutexLock MutexLock(&mutexCurrentAudioTrack);
00853      if (IS_DOLBY_TRACK(Type))
00854         SetDigitalAudioDevice(true);
00855      currentAudioTrack = Type;
00856      if (player)
00857         player->SetAudioTrack(currentAudioTrack, GetTrack(currentAudioTrack));
00858      else
00859         SetAudioTrackDevice(currentAudioTrack);
00860      if (IS_AUDIO_TRACK(Type))
00861         SetDigitalAudioDevice(false);
00862      return true;
00863      }
00864   return false;
00865 }
00866 
00867 void cDevice::EnsureAudioTrack(bool Force)
00868 {
00869   if (Force || !availableTracks[currentAudioTrack].id) {
00870      eTrackType PreferredTrack = ttAudioFirst;
00871      int PreferredAudioChannel = 0;
00872      int LanguagePreference = -1;
00873      int StartCheck = Setup.CurrentDolby ? ttDolbyFirst : ttAudioFirst;
00874      int EndCheck = ttDolbyLast;
00875      for (int i = StartCheck; i <= EndCheck; i++) {
00876          const tTrackId *TrackId = GetTrack(eTrackType(i));
00877          int pos = 0;
00878          if (TrackId && TrackId->id && I18nIsPreferredLanguage(Setup.AudioLanguages, TrackId->language, LanguagePreference, &pos)) {
00879             PreferredTrack = eTrackType(i);
00880             PreferredAudioChannel = pos;
00881             }
00882          if (Setup.CurrentDolby && i == ttDolbyLast) {
00883             i = ttAudioFirst - 1;
00884             EndCheck = ttAudioLast;
00885             }
00886          }
00887      // Make sure we're set to an available audio track:
00888      const tTrackId *Track = GetTrack(GetCurrentAudioTrack());
00889      if (Force || !Track || !Track->id || PreferredTrack != GetCurrentAudioTrack()) {
00890         if (!Force) // only log this for automatic changes
00891            dsyslog("setting audio track to %d (%d)", PreferredTrack, PreferredAudioChannel);
00892         SetCurrentAudioTrack(PreferredTrack);
00893         SetAudioChannel(PreferredAudioChannel);
00894         }
00895      }
00896 }
00897 
00898 bool cDevice::CanReplay(void) const
00899 {
00900   return HasDecoder();
00901 }
00902 
00903 bool cDevice::SetPlayMode(ePlayMode PlayMode)
00904 {
00905   return false;
00906 }
00907 
00908 int64_t cDevice::GetSTC(void)
00909 {
00910   return -1;
00911 }
00912 
00913 void cDevice::TrickSpeed(int Speed)
00914 {
00915 }
00916 
00917 void cDevice::Clear(void)
00918 {
00919   Audios.ClearAudio();
00920 }
00921 
00922 void cDevice::Play(void)
00923 {
00924   Audios.MuteAudio(mute);
00925 }
00926 
00927 void cDevice::Freeze(void)
00928 {
00929   Audios.MuteAudio(true);
00930 }
00931 
00932 void cDevice::Mute(void)
00933 {
00934   Audios.MuteAudio(true);
00935 }
00936 
00937 void cDevice::StillPicture(const uchar *Data, int Length)
00938 {
00939 }
00940 
00941 bool cDevice::Replaying(void) const
00942 {
00943   return player != NULL;
00944 }
00945 
00946 bool cDevice::Transferring(void) const
00947 {
00948   return dynamic_cast<cTransfer *>(player) != NULL;
00949 }
00950 
00951 bool cDevice::AttachPlayer(cPlayer *Player)
00952 {
00953   if (CanReplay()) {
00954      if (player)
00955         Detach(player);
00956      pesAssembler->Reset();
00957      player = Player;
00958      if (!Transferring())
00959         ClrAvailableTracks(false, true);
00960      SetPlayMode(player->playMode);
00961      player->device = this;
00962      player->Activate(true);
00963      return true;
00964      }
00965   return false;
00966 }
00967 
00968 void cDevice::Detach(cPlayer *Player)
00969 {
00970   if (Player && player == Player) {
00971      player->Activate(false);
00972      player->device = NULL;
00973      player = NULL;
00974      SetPlayMode(pmNone);
00975      SetVideoDisplayFormat(eVideoDisplayFormat(Setup.VideoDisplayFormat));
00976      Audios.ClearAudio();
00977      }
00978 }
00979 
00980 void cDevice::StopReplay(void)
00981 {
00982   if (player) {
00983      Detach(player);
00984      if (IsPrimaryDevice())
00985         cControl::Shutdown();
00986      }
00987 }
00988 
00989 bool cDevice::Poll(cPoller &Poller, int TimeoutMs)
00990 {
00991   return false;
00992 }
00993 
00994 bool cDevice::Flush(int TimeoutMs)
00995 {
00996   return true;
00997 }
00998 
00999 int cDevice::PlayVideo(const uchar *Data, int Length)
01000 {
01001   return -1;
01002 }
01003 
01004 int cDevice::PlayAudio(const uchar *Data, int Length, uchar Id)
01005 {
01006   return -1;
01007 }
01008 
01009 int cDevice::PlayPesPacket(const uchar *Data, int Length, bool VideoOnly)
01010 {
01011   cMutexLock MutexLock(&mutexCurrentAudioTrack);
01012   bool FirstLoop = true;
01013   uchar c = Data[3];
01014   const uchar *Start = Data;
01015   const uchar *End = Start + Length;
01016   while (Start < End) {
01017         int d = End - Start;
01018         int w = d;
01019         switch (c) {
01020           case 0xBE:          // padding stream, needed for MPEG1
01021           case 0xE0 ... 0xEF: // video
01022                w = PlayVideo(Start, d);
01023                break;
01024           case 0xC0 ... 0xDF: // audio
01025                SetAvailableTrack(ttAudio, c - 0xC0, c);
01026                if (!VideoOnly && c == availableTracks[currentAudioTrack].id) {
01027                   w = PlayAudio(Start, d, c);
01028                   if (FirstLoop)
01029                      Audios.PlayAudio(Data, Length, c);
01030                   }
01031                break;
01032           case 0xBD: { // private stream 1
01033                int PayloadOffset = Data[8] + 9;
01034                uchar SubStreamId = Data[PayloadOffset];
01035                uchar SubStreamType = SubStreamId & 0xF0;
01036                uchar SubStreamIndex = SubStreamId & 0x1F;
01037 
01038                // Compatibility mode for old VDR recordings, where 0xBD was only AC3:
01039 pre_1_3_19_PrivateStreamDeteced:
01040                if (pre_1_3_19_PrivateStream) {
01041                   SubStreamId = c;
01042                   SubStreamType = 0x80;
01043                   SubStreamIndex = 0;
01044                   }
01045                switch (SubStreamType) {
01046                  case 0x20: // SPU
01047                  case 0x30: // SPU
01048                       break;
01049                  case 0x80: // AC3 & DTS
01050                       if (Setup.UseDolbyDigital) {
01051                          SetAvailableTrack(ttDolby, SubStreamIndex, SubStreamId);
01052                          if (!VideoOnly && SubStreamId == availableTracks[currentAudioTrack].id) {
01053                             w = PlayAudio(Start, d, SubStreamId);
01054                             if (FirstLoop)
01055                                Audios.PlayAudio(Data, Length, SubStreamId);
01056                             }
01057                          }
01058                       break;
01059                  case 0xA0: // LPCM
01060                       SetAvailableTrack(ttAudio, SubStreamIndex, SubStreamId);
01061                       if (!VideoOnly && SubStreamId == availableTracks[currentAudioTrack].id) {
01062                          w = PlayAudio(Start, d, SubStreamId);
01063                          if (FirstLoop)
01064                             Audios.PlayAudio(Data, Length, SubStreamId);
01065                          }
01066                       break;
01067                  default:
01068                       // Compatibility mode for old VDR recordings, where 0xBD was only AC3:
01069                       if (!pre_1_3_19_PrivateStream) {
01070                          dsyslog("switching to pre 1.3.19 Dolby Digital compatibility mode");
01071                          ClrAvailableTracks();
01072                          pre_1_3_19_PrivateStream = true;
01073                          goto pre_1_3_19_PrivateStreamDeteced;
01074                          }
01075                  }
01076                }
01077                break;
01078           default:
01079                ;//esyslog("ERROR: unexpected packet id %02X", c);
01080           }
01081         if (w > 0)
01082            Start += w;
01083         else {
01084            if (Start != Data)
01085               esyslog("ERROR: incomplete PES packet write!");
01086            return Start == Data ? w : Start - Data;
01087            }
01088         FirstLoop = false;
01089         }
01090   return Length;
01091 }
01092 
01093 int cDevice::PlayPes(const uchar *Data, int Length, bool VideoOnly)
01094 {
01095   if (!Data) {
01096      pesAssembler->Reset();
01097      return 0;
01098      }
01099   int Result = 0;
01100   if (pesAssembler->Length()) {
01101      // Make sure we have a complete PES header:
01102      while (pesAssembler->Length() < 6 && Length > 0) {
01103            pesAssembler->Put(*Data++);
01104            Length--;
01105            Result++;
01106            }
01107      if (pesAssembler->Length() < 6)
01108         return Result; // Still no complete PES header - wait for more
01109      int l = pesAssembler->ExpectedLength();
01110      int Rest = min(l - pesAssembler->Length(), Length);
01111      pesAssembler->Put(Data, Rest);
01112      Data += Rest;
01113      Length -= Rest;
01114      Result += Rest;
01115      if (pesAssembler->Length() < l)
01116         return Result; // Still no complete PES packet - wait for more
01117      // Now pesAssembler contains one complete PES packet.
01118      int w = PlayPesPacket(pesAssembler->Data(), pesAssembler->Length(), VideoOnly);
01119      if (w > 0)
01120         pesAssembler->Reset();
01121      return Result > 0 ? Result : w < 0 ? w : 0;
01122      }
01123   int i = 0;
01124   while (i <= Length - 6) {
01125         if (Data[i] == 0x00 && Data[i + 1] == 0x00 && Data[i + 2] == 0x01) {
01126            int l = cPesAssembler::PacketSize(&Data[i]);
01127            if (i + l > Length) {
01128               // Store incomplete PES packet for later completion:
01129               pesAssembler->Put(Data + i, Length - i);
01130               return Length;
01131               }
01132            int w = PlayPesPacket(Data + i, l, VideoOnly);
01133            if (w > 0)
01134               i += l;
01135            else
01136               return i == 0 ? w : i;
01137            }
01138         else
01139            i++;
01140         }
01141   if (i < Length)
01142      pesAssembler->Put(Data + i, Length - i);
01143   return Length;
01144 }
01145 
01146 int cDevice::Ca(void) const
01147 {
01148   int ca = 0;
01149   for (int i = 0; i < MAXRECEIVERS; i++) {
01150       if (receiver[i] && (ca = receiver[i]->ca) != 0)
01151          break; // all receivers have the same ca
01152       }
01153   return ca;
01154 }
01155 
01156 int cDevice::Priority(void) const
01157 {
01158   int priority = IsPrimaryDevice() ? Setup.PrimaryLimit - 1 : DEFAULTPRIORITY;
01159   for (int i = 0; i < MAXRECEIVERS; i++) {
01160       if (receiver[i])
01161          priority = max(receiver[i]->priority, priority);
01162       }
01163   return priority;
01164 }
01165 
01166 bool cDevice::Ready(void)
01167 {
01168   return true;
01169 }
01170 
01171 int cDevice::ProvidesCa(const cChannel *Channel) const
01172 {
01173   int Ca = Channel->Ca();
01174   if (Ca == CardIndex() + 1)
01175      return 1; // exactly _this_ card was requested
01176   if (Ca && Ca <= CA_DVB_MAX)
01177      return 0; // a specific card was requested, but not _this_ one
01178   return !Ca; // by default every card can provide FTA
01179 }
01180 
01181 bool cDevice::Receiving(bool CheckAny) const
01182 {
01183   for (int i = 0; i < MAXRECEIVERS; i++) {
01184       if (receiver[i] && (CheckAny || receiver[i]->priority >= 0)) // cReceiver with priority < 0 doesn't count
01185          return true;
01186       }
01187   return false;
01188 }
01189 
01190 void cDevice::Action(void)
01191 {
01192   if (Running() && OpenDvr()) {
01193      while (Running()) {
01194            // Read data from the DVR device:
01195            uchar *b = NULL;
01196            if (GetTSPacket(b)) {
01197               if (b) {
01198                  int Pid = (((uint16_t)b[1] & PID_MASK_HI) << 8) | b[2];
01199                  // Distribute the packet to all attached receivers:
01200                  Lock();
01201                  for (int i = 0; i < MAXRECEIVERS; i++) {
01202                      if (receiver[i] && receiver[i]->WantsPid(Pid))
01203                         receiver[i]->Receive(b, TS_SIZE);
01204                      }
01205                  Unlock();
01206                  }
01207               }
01208            else
01209               break;
01210            }
01211      CloseDvr();
01212      }
01213 }
01214 
01215 bool cDevice::OpenDvr(void)
01216 {
01217   return false;
01218 }
01219 
01220 void cDevice::CloseDvr(void)
01221 {
01222 }
01223 
01224 bool cDevice::GetTSPacket(uchar *&Data)
01225 {
01226   return false;
01227 }
01228 
01229 bool cDevice::AttachReceiver(cReceiver *Receiver)
01230 {
01231   if (!Receiver)
01232      return false;
01233   if (Receiver->device == this)
01234      return true;
01235 // activate the following line if you need it - actually the driver should be fixed!
01236 //#define WAIT_FOR_TUNER_LOCK
01237 #ifdef WAIT_FOR_TUNER_LOCK
01238 #define TUNER_LOCK_TIMEOUT 5000 // ms
01239   if (!HasLock(TUNER_LOCK_TIMEOUT)) {
01240      esyslog("ERROR: device %d has no lock, can't attach receiver!", CardIndex() + 1);
01241      return false;
01242      }
01243 #endif
01244   cMutexLock MutexLock(&mutexReceiver);
01245   for (int i = 0; i < MAXRECEIVERS; i++) {
01246       if (!receiver[i]) {
01247          for (int n = 0; n < Receiver->numPids; n++) {
01248              if (!AddPid(Receiver->pids[n])) {
01249                 for ( ; n-- > 0; )
01250                     DelPid(Receiver->pids[n]);
01251                 return false;
01252                 }
01253              }
01254          Receiver->Activate(true);
01255          Lock();
01256          Receiver->device = this;
01257          receiver[i] = Receiver;
01258          Unlock();
01259          if (!Running())
01260             Start();
01261          if (ciHandler)
01262             ciHandler->StartDecrypting();
01263          return true;
01264          }
01265       }
01266   esyslog("ERROR: no free receiver slot!");
01267   return false;
01268 }
01269 
01270 void cDevice::Detach(cReceiver *Receiver)
01271 {
01272   if (!Receiver || Receiver->device != this)
01273      return;
01274   bool receiversLeft = false;
01275   cMutexLock MutexLock(&mutexReceiver);
01276   for (int i = 0; i < MAXRECEIVERS; i++) {
01277       if (receiver[i] == Receiver) {
01278          Receiver->Activate(false);
01279          Lock();
01280          receiver[i] = NULL;
01281          Receiver->device = NULL;
01282          Unlock();
01283          for (int n = 0; n < Receiver->numPids; n++)
01284              DelPid(Receiver->pids[n]);
01285          }
01286       else if (receiver[i])
01287          receiversLeft = true;
01288       }
01289   if (ciHandler)
01290      ciHandler->StartDecrypting();
01291   if (!receiversLeft)
01292      Cancel(3);
01293 }
01294 
01295 void cDevice::DetachAll(int Pid)
01296 {
01297   if (Pid) {
01298      cMutexLock MutexLock(&mutexReceiver);
01299      for (int i = 0; i < MAXRECEIVERS; i++) {
01300          cReceiver *Receiver = receiver[i];
01301          if (Receiver && Receiver->WantsPid(Pid))
01302             Detach(Receiver);
01303          }
01304      }
01305 }
01306 
01307 void cDevice::DetachAllReceivers(void)
01308 {
01309   cMutexLock MutexLock(&mutexReceiver);
01310   for (int i = 0; i < MAXRECEIVERS; i++) {
01311       if (receiver[i])
01312          Detach(receiver[i]);
01313       }
01314 }
01315 
01316 // --- cTSBuffer -------------------------------------------------------------
01317 
01318 cTSBuffer::cTSBuffer(int File, int Size, int CardIndex)
01319 {
01320   SetDescription("TS buffer on device %d", CardIndex);
01321   f = File;
01322   cardIndex = CardIndex;
01323   delivered = false;
01324   ringBuffer = new cRingBufferLinear(Size, TS_SIZE, true, "TS");
01325   ringBuffer->SetTimeouts(100, 100);
01326   Start();
01327 }
01328 
01329 cTSBuffer::~cTSBuffer()
01330 {
01331   Cancel(3);
01332   delete ringBuffer;
01333 }
01334 
01335 void cTSBuffer::Action(void)
01336 {
01337   if (ringBuffer) {
01338      bool firstRead = true;
01339      cPoller Poller(f);
01340      while (Running()) {
01341            if (firstRead || Poller.Poll(100)) {
01342               firstRead = false;
01343               int r = ringBuffer->Read(f);
01344               if (r < 0 && FATALERRNO) {
01345                  if (errno == EOVERFLOW)
01346                     esyslog("ERROR: driver buffer overflow on device %d", cardIndex);
01347                  else {
01348                     LOG_ERROR;
01349                     break;
01350                     }
01351                  }
01352               }
01353            }
01354      }
01355 }
01356 
01357 uchar *cTSBuffer::Get(void)
01358 {
01359   int Count = 0;
01360   if (delivered) {
01361      ringBuffer->Del(TS_SIZE);
01362      delivered = false;
01363      }
01364   uchar *p = ringBuffer->Get(Count);
01365   if (p && Count >= TS_SIZE) {
01366      if (*p != TS_SYNC_BYTE) {
01367         for (int i = 1; i < Count; i++) {
01368             if (p[i] == TS_SYNC_BYTE) {
01369                Count = i;
01370                break;
01371                }
01372             }
01373         ringBuffer->Del(Count);
01374         esyslog("ERROR: skipped %d bytes to sync on TS packet on device %d", Count, cardIndex);
01375         return NULL;
01376         }
01377      delivered = true;
01378      return p;
01379      }
01380   return NULL;
01381 }

Generated on Tue Nov 6 19:57:52 2007 for VDR by  doxygen 1.5.3-20071008