Home

Dokumentation

Impressum

Dokumentation VDR
 

Main Page   Class Hierarchy   Alphabetical List   Data Structures   File List   Data Fields   Globals  

dvbdevice.c

Go to the documentation of this file.
00001 /*
00002  * dvbdevice.c: The DVB device interface
00003  *
00004  * See the main source file 'vdr.c' for copyright information and
00005  * how to reach the author.
00006  *
00007  * $Id: dvbdevice.c 1.40 2002/12/14 10:52:13 kls Exp $
00008  */
00009 
00010 #include "dvbdevice.h"
00011 #include <errno.h>
00012 extern "C" {
00013 #ifdef boolean
00014 #define HAVE_BOOLEAN
00015 #endif
00016 #include <jpeglib.h>
00017 #undef boolean
00018 }
00019 #include <limits.h>
00020 #include <linux/videodev.h>
00021 #include <linux/dvb/audio.h>
00022 #include <linux/dvb/dmx.h>
00023 #include <linux/dvb/frontend.h>
00024 #include <linux/dvb/video.h>
00025 #include <sys/ioctl.h>
00026 #include <sys/mman.h>
00027 #include "channels.h"
00028 #include "diseqc.h"
00029 #include "dvbosd.h"
00030 #include "player.h"
00031 #include "receiver.h"
00032 #include "status.h"
00033 #include "transfer.h"
00034 
00035 #define DO_REC_AND_PLAY_ON_PRIMARY_DEVICE 1
00036 #define DO_MULTIPLE_RECORDINGS 1
00037 
00038 #define DEV_VIDEO         "/dev/video"
00039 #define DEV_DVB_ADAPTER   "/dev/dvb/adapter"
00040 #define DEV_DVB_OSD       "osd"
00041 #define DEV_DVB_FRONTEND  "frontend"
00042 #define DEV_DVB_DVR       "dvr"
00043 #define DEV_DVB_DEMUX     "demux"
00044 #define DEV_DVB_VIDEO     "video"
00045 #define DEV_DVB_AUDIO     "audio"
00046 
00047 static const char *DvbName(const char *Name, int n)
00048 {
00049   static char buffer[PATH_MAX];
00050   snprintf(buffer, sizeof(buffer), "%s%d/%s%d", DEV_DVB_ADAPTER, n, Name, 0);
00051   return buffer;
00052 }
00053 
00054 static int DvbOpen(const char *Name, int n, int Mode, bool ReportError = false)
00055 {
00056   const char *FileName = DvbName(Name, n);
00057   int fd = open(FileName, Mode);
00058   if (fd < 0 && ReportError)
00059      LOG_ERROR_STR(FileName);
00060   return fd;
00061 }
00062 
00063 // --- cDvbTuner -------------------------------------------------------------
00064 
00065 class cDvbTuner : public cThread {
00066 private:
00067   enum eTunerStatus { tsIdle, tsSet, tsTuned, tsLocked };
00068   int fd_frontend;
00069   int cardIndex;
00070   fe_type_t frontendType;
00071   cChannel channel;
00072   const char *diseqcCommands;
00073   bool active;
00074   eTunerStatus tunerStatus;
00075   cMutex mutex;
00076   cCondVar newSet;
00077   bool SetFrontend(void);
00078   virtual void Action(void);
00079 public:
00080   cDvbTuner(int Fd_Frontend, int CardIndex, fe_type_t FrontendType);
00081   virtual ~cDvbTuner();
00082   bool IsTunedTo(const cChannel *Channel) const;
00083   void Set(const cChannel *Channel);
00084   bool Locked(void) { return tunerStatus == tsLocked; }
00085   };
00086 
00087 cDvbTuner::cDvbTuner(int Fd_Frontend, int CardIndex, fe_type_t FrontendType)
00088 {
00089   fd_frontend = Fd_Frontend;
00090   cardIndex = CardIndex;
00091   frontendType = FrontendType;
00092   diseqcCommands = NULL;
00093   active = false;
00094   tunerStatus = tsIdle;
00095   Start();
00096 }
00097 
00098 cDvbTuner::~cDvbTuner()
00099 {
00100   active = false;
00101   tunerStatus = tsIdle;
00102   newSet.Broadcast();
00103   Cancel(3);
00104 }
00105 
00106 bool cDvbTuner::IsTunedTo(const cChannel *Channel) const
00107 {
00108   return tunerStatus != tsIdle && channel.Source() == Channel->Source() && channel.Frequency() == Channel->Frequency();
00109 }
00110 
00111 void cDvbTuner::Set(const cChannel *Channel)
00112 {
00113   cMutexLock MutexLock(&mutex);
00114   channel = *Channel;
00115   tunerStatus = tsSet;
00116   newSet.Broadcast();
00117 }
00118 
00119 static unsigned int FrequencyToHz(unsigned int f)
00120 {
00121   while (f && f < 1000000)
00122         f *= 1000;
00123   return f;
00124 }
00125 
00126 bool cDvbTuner::SetFrontend(void)
00127 {
00128   dvb_frontend_parameters Frontend;
00129 
00130   memset(&Frontend, 0, sizeof(Frontend));
00131 
00132   switch (frontendType) {
00133     case FE_QPSK: { // DVB-S
00134 
00135          unsigned int frequency = channel.Frequency();
00136 
00137          if (Setup.DiSEqC) {
00138             cDiseqc *diseqc = Diseqcs.Get(channel.Source(), channel.Frequency(), channel.Polarization());
00139             if (diseqc) {
00140                if (diseqc->Commands() && (!diseqcCommands || strcmp(diseqcCommands, diseqc->Commands()) != 0)) {
00141                   cDiseqc::eDiseqcActions da;
00142                   for (char *CurrentAction = NULL; (da = diseqc->Execute(&CurrentAction)) != cDiseqc::daNone; ) {
00143                       switch (da) {
00144                         case cDiseqc::daNone:      break;
00145                         case cDiseqc::daToneOff:   CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_OFF)); break;
00146                         case cDiseqc::daToneOn:    CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_ON)); break;
00147                         case cDiseqc::daVoltage13: CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_13)); break;
00148                         case cDiseqc::daVoltage18: CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_18)); break;
00149                         case cDiseqc::daMiniA:     CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_BURST, SEC_MINI_A)); break;
00150                         case cDiseqc::daMiniB:     CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_BURST, SEC_MINI_B)); break;
00151                         case cDiseqc::daCodes: {
00152                              int n = 0;
00153                              uchar *codes = diseqc->Codes(n);
00154                              if (codes) {
00155                                 struct dvb_diseqc_master_cmd cmd;
00156                                 memcpy(cmd.msg, codes, min(n, int(sizeof(cmd.msg))));
00157                                 cmd.msg_len = n;
00158                                 CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_MASTER_CMD, &cmd));
00159                                 }
00160                              }
00161                              break;
00162                         }
00163                       }
00164                   diseqcCommands = diseqc->Commands();
00165                   }
00166                frequency -= diseqc->Lof();
00167                }
00168             else {
00169                esyslog("ERROR: no DiSEqC parameters found for channel %d", channel.Number());
00170                return false;
00171                }
00172             }
00173          else {
00174             int tone = SEC_TONE_OFF;
00175 
00176             if (frequency < (unsigned int)Setup.LnbSLOF) {
00177                frequency -= Setup.LnbFrequLo;
00178                tone = SEC_TONE_OFF;
00179                }
00180             else {
00181                frequency -= Setup.LnbFrequHi;
00182                tone = SEC_TONE_ON;
00183                }
00184             int volt = (channel.Polarization() == 'v' || channel.Polarization() == 'V') ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18;
00185             CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, volt));
00186             CHECK(ioctl(fd_frontend, FE_SET_TONE, tone));
00187             }
00188 
00189          Frontend.frequency = frequency * 1000UL;
00190          Frontend.inversion = fe_spectral_inversion_t(channel.Inversion());
00191          Frontend.u.qpsk.symbol_rate = channel.Srate() * 1000UL;
00192          Frontend.u.qpsk.fec_inner = fe_code_rate_t(channel.CoderateH());
00193          }
00194          break;
00195     case FE_QAM: { // DVB-C
00196 
00197          // Frequency and symbol rate:
00198 
00199          Frontend.frequency = FrequencyToHz(channel.Frequency());
00200          Frontend.inversion = fe_spectral_inversion_t(channel.Inversion());
00201          Frontend.u.qam.symbol_rate = channel.Srate() * 1000UL;
00202          Frontend.u.qam.fec_inner = fe_code_rate_t(channel.CoderateH());
00203          Frontend.u.qam.modulation = fe_modulation_t(channel.Modulation());
00204          }
00205          break;
00206     case FE_OFDM: { // DVB-T
00207 
00208          // Frequency and OFDM paramaters:
00209 
00210          Frontend.frequency = FrequencyToHz(channel.Frequency());
00211          Frontend.inversion = fe_spectral_inversion_t(channel.Inversion());
00212          Frontend.u.ofdm.bandwidth = fe_bandwidth_t(channel.Bandwidth());
00213          Frontend.u.ofdm.code_rate_HP = fe_code_rate_t(channel.CoderateH());
00214          Frontend.u.ofdm.code_rate_LP = fe_code_rate_t(channel.CoderateL());
00215          Frontend.u.ofdm.constellation = fe_modulation_t(channel.Modulation());
00216          Frontend.u.ofdm.transmission_mode = fe_transmit_mode_t(channel.Transmission());
00217          Frontend.u.ofdm.guard_interval = fe_guard_interval_t(channel.Guard());
00218          Frontend.u.ofdm.hierarchy_information = fe_hierarchy_t(channel.Hierarchy());
00219          }
00220          break;
00221     default:
00222          esyslog("ERROR: attempt to set channel with unknown DVB frontend type");
00223          return false;
00224     }
00225   if (ioctl(fd_frontend, FE_SET_FRONTEND, &Frontend) < 0) {
00226      esyslog("ERROR: frontend %d: %m", cardIndex);
00227      return false;
00228      }
00229   return true;
00230 }
00231 
00232 void cDvbTuner::Action(void)
00233 {
00234   dsyslog("tuner thread started on device %d (pid=%d)", cardIndex + 1, getpid());
00235   active = true;
00236   while (active) {
00237         cMutexLock MutexLock(&mutex);
00238         if (tunerStatus == tsSet)
00239            tunerStatus = SetFrontend() ? tsTuned : tsIdle;
00240         if (tunerStatus == tsTuned) {
00241            fe_status_t status = fe_status_t(0);
00242            CHECK(ioctl(fd_frontend, FE_READ_STATUS, &status));
00243            if (status & FE_HAS_LOCK)
00244               tunerStatus = tsLocked;
00245            }
00246         dvb_frontend_event event;
00247         if (ioctl(fd_frontend, FE_GET_EVENT, &event) == 0) {
00248            if (tunerStatus != tsIdle && event.status & FE_REINIT) {
00249               tunerStatus = tsSet;
00250               esyslog("ERROR: frontend %d was reinitialized - re-tuning", cardIndex);
00251               continue;
00252               }
00253            }
00254         newSet.TimedWait(mutex, 1000);
00255         }
00256   dsyslog("tuner thread ended on device %d (pid=%d)", cardIndex + 1, getpid());
00257 }
00258 
00259 // --- cDvbDevice ------------------------------------------------------------
00260 
00261 cDvbDevice::cDvbDevice(int n)
00262 {
00263   dvbTuner = NULL;
00264   frontendType = fe_type_t(-1); // don't know how else to initialize this - there is no FE_UNKNOWN
00265   siProcessor = NULL;
00266   spuDecoder = NULL;
00267   playMode = pmNone;
00268 
00269   // Devices that are present on all card types:
00270 
00271   int fd_frontend = DvbOpen(DEV_DVB_FRONTEND, n, O_RDWR | O_NONBLOCK);
00272 
00273   // Devices that are only present on cards with decoders:
00274 
00275   fd_osd      = DvbOpen(DEV_DVB_OSD,    n, O_RDWR);
00276   fd_video    = DvbOpen(DEV_DVB_VIDEO,  n, O_RDWR | O_NONBLOCK);
00277   fd_audio    = DvbOpen(DEV_DVB_AUDIO,  n, O_RDWR | O_NONBLOCK);
00278 
00279   // The DVR device (will be opened and closed as needed):
00280 
00281   fd_dvr = -1;
00282 
00283   // Video format:
00284 
00285   SetVideoFormat(Setup.VideoFormat ? VIDEO_FORMAT_16_9 : VIDEO_FORMAT_4_3);
00286 
00287   // We only check the devices that must be present - the others will be checked before accessing them://XXX
00288 
00289   if (fd_frontend >= 0) {
00290      dvb_frontend_info feinfo;
00291      siProcessor = new cSIProcessor(DvbName(DEV_DVB_DEMUX, n));
00292      if (ioctl(fd_frontend, FE_GET_INFO, &feinfo) >= 0) {
00293         frontendType = feinfo.type;
00294         dvbTuner = new cDvbTuner(fd_frontend, CardIndex(), frontendType);
00295         }
00296      else
00297         LOG_ERROR;
00298      }
00299   else
00300      esyslog("ERROR: can't open DVB device %d", n);
00301 
00302   aPid1 = aPid2 = 0;
00303 }
00304 
00305 cDvbDevice::~cDvbDevice()
00306 {
00307   delete spuDecoder;
00308   delete siProcessor;
00309   delete dvbTuner;
00310   // We're not explicitly closing any device files here, since this sometimes
00311   // caused segfaults. Besides, the program is about to terminate anyway...
00312 }
00313 
00314 bool cDvbDevice::Probe(const char *FileName)
00315 {
00316   if (access(FileName, F_OK) == 0) {
00317      dsyslog("probing %s", FileName);
00318      int f = open(FileName, O_RDONLY);
00319      if (f >= 0) {
00320         close(f);
00321         return true;
00322         }
00323      else if (errno != ENODEV && errno != EINVAL)
00324         LOG_ERROR_STR(FileName);
00325      }
00326   else if (errno != ENOENT)
00327      LOG_ERROR_STR(FileName);
00328   return false;
00329 }
00330 
00331 bool cDvbDevice::Initialize(void)
00332 {
00333   int found = 0;
00334   int i;
00335   for (i = 0; i < MAXDVBDEVICES; i++) {
00336       if (UseDevice(NextCardIndex())) {
00337          if (Probe(DvbName(DEV_DVB_FRONTEND, i))) {
00338             new cDvbDevice(i);
00339             found++;
00340             }
00341          else
00342             break;
00343          }
00344       else
00345          NextCardIndex(1); // skips this one
00346       }
00347   NextCardIndex(MAXDVBDEVICES - i); // skips the rest
00348   if (found > 0)
00349      isyslog("found %d video device%s", found, found > 1 ? "s" : "");
00350   else
00351      isyslog("no DVB device found");
00352   return found > 0;
00353 }
00354 
00355 void cDvbDevice::MakePrimaryDevice(bool On)
00356 {
00357   cDvbOsd::SetDvbDevice(On ? this : NULL);
00358 }
00359 
00360 bool cDvbDevice::HasDecoder(void) const
00361 {
00362   return fd_video >= 0 && fd_audio >= 0;
00363 }
00364 
00365 cOsdBase *cDvbDevice::NewOsd(int x, int y)
00366 {
00367   return new cDvbOsd(x, y);
00368 }
00369 
00370 cSpuDecoder *cDvbDevice::GetSpuDecoder(void)
00371 {
00372   if (!spuDecoder && IsPrimaryDevice())
00373      spuDecoder = new cDvbSpuDecoder();
00374   return spuDecoder;
00375 }
00376 
00377 bool cDvbDevice::GrabImage(const char *FileName, bool Jpeg, int Quality, int SizeX, int SizeY)
00378 {
00379   char buffer[PATH_MAX];
00380   snprintf(buffer, sizeof(buffer), "%s%d", DEV_VIDEO, CardIndex());
00381   int videoDev = open(buffer, O_RDWR);
00382   if (videoDev < 0)
00383      LOG_ERROR_STR(buffer);
00384   if (videoDev >= 0) {
00385      int result = 0;
00386      struct video_mbuf mbuf;
00387      result |= ioctl(videoDev, VIDIOCGMBUF, &mbuf);
00388      if (result == 0) {
00389         int msize = mbuf.size;
00390         unsigned char *mem = (unsigned char *)mmap(0, msize, PROT_READ | PROT_WRITE, MAP_SHARED, videoDev, 0);
00391         if (mem && mem != (unsigned char *)-1) {
00392            // set up the size and RGB
00393            struct video_capability vc;
00394            result |= ioctl(videoDev, VIDIOCGCAP, &vc);
00395            struct video_mmap vm;
00396            vm.frame = 0;
00397            if ((SizeX > 0) && (SizeX <= vc.maxwidth) &&
00398                (SizeY > 0) && (SizeY <= vc.maxheight)) {
00399               vm.width = SizeX;
00400               vm.height = SizeY;
00401               }
00402            else {
00403               vm.width = vc.maxwidth;
00404               vm.height = vc.maxheight;
00405               }
00406            vm.format = VIDEO_PALETTE_RGB24;
00407            result |= ioctl(videoDev, VIDIOCMCAPTURE, &vm);
00408            result |= ioctl(videoDev, VIDIOCSYNC, &vm.frame);
00409            // make RGB out of BGR:
00410            int memsize = vm.width * vm.height;
00411            unsigned char *mem1 = mem;
00412            for (int i = 0; i < memsize; i++) {
00413                unsigned char tmp = mem1[2];
00414                mem1[2] = mem1[0];
00415                mem1[0] = tmp;
00416                mem1 += 3;
00417                }
00418 
00419            if (Quality < 0)
00420               Quality = 255; //XXX is this 'best'???
00421 
00422            isyslog("grabbing to %s (%s %d %d %d)", FileName, Jpeg ? "JPEG" : "PNM", Quality, vm.width, vm.height);
00423            FILE *f = fopen(FileName, "wb");
00424            if (f) {
00425               if (Jpeg) {
00426                  // write JPEG file:
00427                  struct jpeg_compress_struct cinfo;
00428                  struct jpeg_error_mgr jerr;
00429                  cinfo.err = jpeg_std_error(&jerr);
00430                  jpeg_create_compress(&cinfo);
00431                  jpeg_stdio_dest(&cinfo, f);
00432                  cinfo.image_width = vm.width;
00433                  cinfo.image_height = vm.height;
00434                  cinfo.input_components = 3;
00435                  cinfo.in_color_space = JCS_RGB;
00436 
00437                  jpeg_set_defaults(&cinfo);
00438                  jpeg_set_quality(&cinfo, Quality, true);
00439                  jpeg_start_compress(&cinfo, true);
00440 
00441                  int rs = vm.width * 3;
00442                  JSAMPROW rp[vm.height];
00443                  for (int k = 0; k < vm.height; k++)
00444                      rp[k] = &mem[rs * k];
00445                  jpeg_write_scanlines(&cinfo, rp, vm.height);
00446                  jpeg_finish_compress(&cinfo);
00447                  jpeg_destroy_compress(&cinfo);
00448                  }
00449               else {
00450                  // write PNM file:
00451                  if (fprintf(f, "P6\n%d\n%d\n255\n", vm.width, vm.height) < 0 ||
00452                      fwrite(mem, vm.width * vm.height * 3, 1, f) < 0) {
00453                     LOG_ERROR_STR(FileName);
00454                     result |= 1;
00455                     }
00456                  }
00457               fclose(f);
00458               }
00459            else {
00460               LOG_ERROR_STR(FileName);
00461               result |= 1;
00462               }
00463            munmap(mem, msize);
00464            }
00465         else
00466            result |= 1;
00467         }
00468      close(videoDev);
00469      return result == 0;
00470      }
00471   return false;
00472 }
00473 
00474 void cDvbDevice::SetVideoFormat(bool VideoFormat16_9)
00475 {
00476   if (HasDecoder())
00477      CHECK(ioctl(fd_video, VIDEO_SET_FORMAT, VideoFormat16_9 ? VIDEO_FORMAT_16_9 : VIDEO_FORMAT_4_3));
00478 }
00479 
00480 //                          ptAudio        ptVideo        ptTeletext        ptDolby        ptOther
00481 dmx_pes_type_t PesTypes[] = { DMX_PES_AUDIO, DMX_PES_VIDEO, DMX_PES_TELETEXT, DMX_PES_OTHER, DMX_PES_OTHER };
00482 
00483 bool cDvbDevice::SetPid(cPidHandle *Handle, int Type, bool On)
00484 {
00485   if (Handle->pid) {
00486      dmx_pes_filter_params pesFilterParams;
00487      memset(&pesFilterParams, 0, sizeof(pesFilterParams));
00488      if (On) {
00489         if (Handle->handle < 0) {
00490            Handle->handle = DvbOpen(DEV_DVB_DEMUX, CardIndex(), O_RDWR | O_NONBLOCK, true);
00491            if (Handle->handle < 0)
00492               return false;
00493            }
00494         pesFilterParams.pid     = Handle->pid;
00495         pesFilterParams.input   = DMX_IN_FRONTEND;
00496         pesFilterParams.output  = (Type <= ptTeletext && Handle->used <= 1) ? DMX_OUT_DECODER : DMX_OUT_TS_TAP;
00497         pesFilterParams.pes_type= PesTypes[Type < ptOther ? Type : ptOther];
00498         pesFilterParams.flags   = DMX_IMMEDIATE_START;
00499         if (ioctl(Handle->handle, DMX_SET_PES_FILTER, &pesFilterParams) < 0) {
00500            LOG_ERROR;
00501            return false;
00502            }
00503         }
00504      else if (!Handle->used) {
00505         CHECK(ioctl(Handle->handle, DMX_STOP));
00506         if (Type <= ptTeletext) {
00507            pesFilterParams.pid     = 0x1FFF;
00508            pesFilterParams.input   = DMX_IN_FRONTEND;
00509            pesFilterParams.output  = DMX_OUT_DECODER;
00510            pesFilterParams.pes_type= PesTypes[Type];
00511            pesFilterParams.flags   = DMX_IMMEDIATE_START;
00512            CHECK(ioctl(Handle->handle, DMX_SET_PES_FILTER, &pesFilterParams));
00513            if (PesTypes[Type] == DMX_PES_VIDEO) // let's only do this once
00514               SetPlayMode(pmNone); // necessary to switch a PID from DMX_PES_VIDEO/AUDIO to DMX_PES_OTHER
00515            }
00516         close(Handle->handle);
00517         Handle->handle = -1;
00518         }
00519      }
00520   return true;
00521 }
00522 
00523 bool cDvbDevice::ProvidesSource(int Source) const
00524 {
00525   int type = Source & cSource::st_Mask;
00526   return type == cSource::stNone
00527       || type == cSource::stCable && frontendType == FE_QAM
00528       || type == cSource::stSat   && frontendType == FE_QPSK
00529       || type == cSource::stTerr  && frontendType == FE_OFDM;
00530   return true;
00531 }
00532 
00533 bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers) const
00534 {
00535   bool result = false;
00536   bool hasPriority = Priority < 0 || Priority > this->Priority();
00537   bool needsDetachReceivers = true;
00538 
00539   if (ProvidesSource(Channel->Source()) && ProvidesCa(Channel->Ca())) {
00540      result = hasPriority;
00541      if (Receiving()) {
00542         if (dvbTuner->IsTunedTo(Channel)) {
00543            needsDetachReceivers = false;
00544            if (!HasPid(Channel->Vpid())) {
00545 #ifdef DO_MULTIPLE_RECORDINGS
00546               if (Channel->Ca() > CACONFBASE)
00547                  needsDetachReceivers = true;
00548               else if (!IsPrimaryDevice())
00549                  result = true;
00550 #ifdef DO_REC_AND_PLAY_ON_PRIMARY_DEVICE
00551               else
00552                  result = Priority >= Setup.PrimaryLimit;
00553 #endif
00554 #endif
00555               }
00556            else
00557               result = !IsPrimaryDevice() || Priority >= Setup.PrimaryLimit;
00558            }
00559         }
00560      }
00561   if (NeedsDetachReceivers)
00562      *NeedsDetachReceivers = needsDetachReceivers;
00563   return result;
00564 }
00565 
00566 bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
00567 {
00568   bool IsEncrypted = Channel->Ca() > CACONFBASE;
00569 
00570   bool DoTune = !dvbTuner->IsTunedTo(Channel);
00571 
00572   bool TurnOffLivePIDs = HasDecoder()
00573                          && (DoTune
00574                             || IsEncrypted && pidHandles[ptVideo].pid != Channel->Vpid() // CA channels can only be decrypted in "live" mode
00575                             || !IsPrimaryDevice()
00576                             || LiveView // for a new live view the old PIDs need to be turned off
00577                             || pidHandles[ptVideo].pid == Channel->Vpid() // for recording the PIDs must be shifted from DMX_PES_AUDIO/VIDEO to DMX_PES_OTHER
00578                             );
00579 
00580   bool StartTransferMode = IsPrimaryDevice() && !IsEncrypted && !DoTune
00581                            && (LiveView && HasPid(Channel->Vpid()) && pidHandles[ptVideo].pid != Channel->Vpid() // the PID is already set as DMX_PES_OTHER
00582                               || !LiveView && pidHandles[ptVideo].pid == Channel->Vpid() // a recording is going to shift the PIDs from DMX_PES_AUDIO/VIDEO to DMX_PES_OTHER
00583                               );
00584 
00585   bool TurnOnLivePIDs = HasDecoder() && !StartTransferMode
00586                         && (IsEncrypted // CA channels can only be decrypted in "live" mode
00587                            || LiveView
00588                            );
00589 
00590 #ifndef DO_MULTIPLE_RECORDINGS
00591   TurnOffLivePIDs = TurnOnLivePIDs = true;
00592   StartTransferMode = false;
00593 #endif
00594 
00595   // Stop setting system time:
00596 
00597   if (siProcessor)
00598      siProcessor->SetCurrentTransponder(0, 0);
00599 
00600   // Turn off live PIDs if necessary:
00601 
00602   if (TurnOffLivePIDs) {
00603 
00604      // Avoid noise while switching:
00605 
00606      CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, true));
00607      CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true));
00608      CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER));
00609      CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER));
00610 
00611      // Turn off live PIDs:
00612 
00613      DelPid(pidHandles[ptAudio].pid);
00614      DelPid(pidHandles[ptVideo].pid);
00615      DelPid(pidHandles[ptTeletext].pid);
00616      DelPid(pidHandles[ptDolby].pid);
00617      }
00618 
00619   if (DoTune) {
00620      dvbTuner->Set(Channel);
00621      /*XXX do we still need this???
00622      if (!(status & FE_HAS_LOCK)) {
00623         esyslog("ERROR: channel %d not locked on DVB card %d!", Channel->Number(), CardIndex() + 1);
00624         if (LiveView && IsPrimaryDevice())
00625            cThread::RaisePanic();
00626         return false;
00627         }
00628      XXX*/
00629      }
00630 
00631   // PID settings:
00632 
00633   if (TurnOnLivePIDs) {
00634      aPid1 = Channel->Apid1();
00635      aPid2 = Channel->Apid2();
00636      if (!(AddPid(Channel->Apid1(), ptAudio) && AddPid(Channel->Vpid(), ptVideo))) {//XXX+ dolby dpid1!!! (if audio plugins are attached)
00637         esyslog("ERROR: failed to set PIDs for channel %d on device %d", Channel->Number(), CardIndex() + 1);
00638         return false;
00639         }
00640      if (IsPrimaryDevice())
00641         AddPid(Channel->Tpid(), ptTeletext);
00642      CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true));
00643      }
00644   else if (StartTransferMode)
00645      cControl::Launch(new cTransferControl(this, Channel->Vpid(), Channel->Apid1(), Channel->Apid2(), Channel->Dpid1(), Channel->Dpid2()));
00646 
00647   // Start setting system time:
00648 
00649   if (siProcessor)
00650      siProcessor->SetCurrentTransponder(Channel->Source(), Channel->Frequency());
00651 
00652   return true;
00653 }
00654 
00655 void cDvbDevice::SetVolumeDevice(int Volume)
00656 {
00657   if (HasDecoder()) {
00658      audio_mixer_t am;
00659      am.volume_left = am.volume_right = Volume;
00660      CHECK(ioctl(fd_audio, AUDIO_SET_MIXER, &am));
00661      }
00662 }
00663 
00664 int cDvbDevice::NumAudioTracksDevice(void) const
00665 {
00666   int n = 0;
00667   if (aPid1)
00668      n++;
00669   if (!Ca() && aPid2 && aPid1 != aPid2) // a Ca recording session blocks switching live audio tracks
00670      n++;
00671   return n;
00672 }
00673 
00674 const char **cDvbDevice::GetAudioTracksDevice(int *CurrentTrack) const
00675 {
00676   if (NumAudioTracksDevice()) {
00677      if (CurrentTrack)
00678         *CurrentTrack = (pidHandles[ptAudio].pid == aPid1) ? 0 : 1;
00679      static const char *audioTracks1[] = { "Audio 1", NULL };
00680      static const char *audioTracks2[] = { "Audio 1", "Audio 2", NULL };
00681      return NumAudioTracksDevice() > 1 ? audioTracks2 : audioTracks1;
00682      }
00683   return NULL;
00684 }
00685 
00686 void cDvbDevice::SetAudioTrackDevice(int Index)
00687 {
00688   if (0 <= Index && Index < NumAudioTracksDevice()) {
00689      int Pid = Index ? aPid2 : aPid1;
00690      pidHandles[ptAudio].pid = Pid;
00691      SetPid(&pidHandles[ptAudio], ptAudio, true);
00692      }
00693 }
00694 
00695 bool cDvbDevice::CanReplay(void) const
00696 {
00697 #ifndef DO_REC_AND_PLAY_ON_PRIMARY_DEVICE
00698   if (Receiving())
00699      return false;
00700 #endif
00701   return cDevice::CanReplay() && !Ca(); // we can only replay if there is no Ca recording going on
00702 }
00703 
00704 bool cDvbDevice::SetPlayMode(ePlayMode PlayMode)
00705 {
00706   if (PlayMode != pmExtern_THIS_SHOULD_BE_AVOIDED && fd_video < 0 && fd_audio < 0) {
00707      // reopen the devices
00708      fd_video = DvbOpen(DEV_DVB_VIDEO,  CardIndex(), O_RDWR | O_NONBLOCK);
00709      fd_audio = DvbOpen(DEV_DVB_AUDIO,  CardIndex(), O_RDWR | O_NONBLOCK);
00710      SetVideoFormat(Setup.VideoFormat);
00711      }
00712 
00713   switch (PlayMode) {
00714     case pmNone:
00715          // special handling to return from PCM replay:
00716          CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true));
00717          CHECK(ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY));
00718          CHECK(ioctl(fd_video, VIDEO_PLAY));
00719 
00720          CHECK(ioctl(fd_video, VIDEO_STOP, true));
00721          CHECK(ioctl(fd_audio, AUDIO_STOP, true));
00722          CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER));
00723          CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER));
00724          CHECK(ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_DEMUX));
00725          CHECK(ioctl(fd_audio, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_DEMUX));
00726          CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true));
00727          CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, false));
00728          if (siProcessor)
00729             siProcessor->SetStatus(true);
00730          break;
00731     case pmAudioVideo:
00732     case pmAudioOnlyBlack:
00733          if (siProcessor)
00734             siProcessor->SetStatus(false);
00735          CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true));
00736          CHECK(ioctl(fd_audio, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_MEMORY));
00737          CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, PlayMode == pmAudioVideo));
00738          CHECK(ioctl(fd_audio, AUDIO_PLAY));
00739          CHECK(ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY));
00740          CHECK(ioctl(fd_video, VIDEO_PLAY));
00741          break;
00742     case pmAudioOnly:
00743          if (siProcessor)
00744             siProcessor->SetStatus(false);
00745          CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true));
00746          CHECK(ioctl(fd_audio, AUDIO_STOP, true));
00747          CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER));
00748          CHECK(ioctl(fd_audio, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_MEMORY));
00749          CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, false));
00750          CHECK(ioctl(fd_audio, AUDIO_PLAY));
00751          CHECK(ioctl(fd_video, VIDEO_SET_BLANK, false));
00752          break;
00753     case pmExtern_THIS_SHOULD_BE_AVOIDED:
00754          if (siProcessor)
00755             siProcessor->SetStatus(false);
00756          close(fd_video);
00757          close(fd_audio);
00758          fd_video = fd_audio = -1;
00759          break;
00760     }
00761   playMode = PlayMode;
00762   return true;
00763 }
00764 
00765 void cDvbDevice::TrickSpeed(int Speed)
00766 {
00767   if (fd_video >= 0)
00768      CHECK(ioctl(fd_video, VIDEO_SLOWMOTION, Speed));
00769 }
00770 
00771 void cDvbDevice::Clear(void)
00772 {
00773   if (fd_video >= 0)
00774      CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER));
00775   if (fd_audio >= 0)
00776      CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER));
00777 }
00778 
00779 void cDvbDevice::Play(void)
00780 {
00781   if (playMode == pmAudioOnly || playMode == pmAudioOnlyBlack) {
00782      if (fd_audio >= 0)
00783         CHECK(ioctl(fd_audio, AUDIO_CONTINUE));
00784      }
00785   else {
00786      if (fd_audio >= 0)
00787         CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true));
00788      if (fd_video >= 0)
00789         CHECK(ioctl(fd_video, VIDEO_CONTINUE));
00790      }
00791 }
00792 
00793 void cDvbDevice::Freeze(void)
00794 {
00795   if (playMode == pmAudioOnly || playMode == pmAudioOnlyBlack) {
00796      if (fd_audio >= 0)
00797         CHECK(ioctl(fd_audio, AUDIO_PAUSE));
00798      }
00799   else {
00800      if (fd_audio >= 0)
00801         CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, false));
00802      if (fd_video >= 0)
00803         CHECK(ioctl(fd_video, VIDEO_FREEZE));
00804      }
00805 }
00806 
00807 void cDvbDevice::Mute(void)
00808 {
00809   if (fd_audio >= 0) {
00810      CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, false));
00811      CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, true));
00812      }
00813 }
00814 
00815 void cDvbDevice::StillPicture(const uchar *Data, int Length)
00816 {
00817   Mute();
00818 /* Using the VIDEO_STILLPICTURE ioctl call would be the
00819    correct way to display a still frame, but unfortunately this
00820    doesn't work with frames from VDR. So let's do pretty much the
00821    same here as in DVB/driver/dvb.c's play_iframe() - I have absolutely
00822    no idea why it works this way, but doesn't work with VIDEO_STILLPICTURE.
00823    If anybody ever finds out what could be changed so that VIDEO_STILLPICTURE
00824    could be used, please let me know!
00825    kls 2002-03-23
00826 */
00827 //#define VIDEO_STILLPICTURE_WORKS_WITH_VDR_FRAMES
00828 #ifdef VIDEO_STILLPICTURE_WORKS_WITH_VDR_FRAMES
00829   videoDisplayStillPicture sp = { (char *)Data, Length };
00830   CHECK(ioctl(fd_video, VIDEO_STILLPICTURE, &sp));
00831 #else
00832 #define MIN_IFRAME 400000
00833   for (int i = MIN_IFRAME / Length + 1; i > 0; i--) {
00834       safe_write(fd_video, Data, Length);
00835       usleep(1); // allows the buffer to be displayed in case the progress display is active
00836       }
00837 #endif
00838 }
00839 
00840 bool cDvbDevice::Poll(cPoller &Poller, int TimeoutMs)
00841 {
00842   Poller.Add((playMode == pmAudioOnly || playMode == pmAudioOnlyBlack) ? fd_audio : fd_video, true);
00843   return Poller.Poll(TimeoutMs);
00844 }
00845 
00846 int cDvbDevice::PlayVideo(const uchar *Data, int Length)
00847 {
00848   int fd = (playMode == pmAudioOnly || playMode == pmAudioOnlyBlack) ? fd_audio : fd_video;
00849   if (fd >= 0)
00850      return write(fd, Data, Length);
00851   return -1;
00852 }
00853 
00854 void cDvbDevice::PlayAudio(const uchar *Data, int Length)
00855 {
00856   //XXX actually this function will only be needed to implement replaying AC3 over the DVB card's S/PDIF
00857   cDevice::PlayAudio(Data, Length);
00858 }
00859 
00860 bool cDvbDevice::OpenDvr(void)
00861 {
00862   CloseDvr();
00863   fd_dvr = DvbOpen(DEV_DVB_DVR, CardIndex(), O_RDONLY | O_NONBLOCK, true);
00864   if (fd_dvr >= 0)
00865      tsBuffer = new cTSBuffer(fd_dvr, MEGABYTE(2), CardIndex() + 1);
00866   return fd_dvr >= 0;
00867 }
00868 
00869 void cDvbDevice::CloseDvr(void)
00870 {
00871   if (fd_dvr >= 0) {
00872      close(fd_dvr);
00873      fd_dvr = -1;
00874      delete tsBuffer;
00875      tsBuffer = NULL;
00876      }
00877 }
00878 
00879 bool cDvbDevice::GetTSPacket(uchar *&Data)
00880 {
00881   if (tsBuffer) {
00882      int r = tsBuffer->Read();
00883      if (r >= 0) {
00884         Data = tsBuffer->Get();
00885         return true;
00886         }
00887      else if (FATALERRNO) {
00888         if (errno == EOVERFLOW)
00889            esyslog("ERROR: DVB driver buffer overflow on device %d", CardIndex() + 1);
00890         else {
00891            LOG_ERROR;
00892            return false;
00893            }
00894         }
00895      return true;
00896      }
00897   return false;
00898 }

Generated on Wed Feb 5 23:30:08 2003 for VDR by doxygen1.3-rc2