mirror of
				https://github.com/vdr-projects/vdr.git
				synced 2025-03-01 10:50:46 +00:00 
			
		
		
		
	Changed the cDevice class to allow plugins to implement their own devices
This commit is contained in:
		
							
								
								
									
										4
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								Makefile
									
									
									
									
									
								
							| @@ -4,7 +4,7 @@ | ||||
| # See the main source file 'vdr.c' for copyright information and | ||||
| # how to reach the author. | ||||
| # | ||||
| # $Id: Makefile 1.43 2002/07/28 13:24:58 kls Exp $ | ||||
| # $Id: Makefile 1.44 2002/07/28 15:20:47 kls Exp $ | ||||
|  | ||||
| .DELETE_ON_ERROR: | ||||
|  | ||||
| @@ -27,7 +27,7 @@ INCLUDES = -I$(DVBDIR)/ost/include | ||||
|  | ||||
| DTVLIB   = $(DTVDIR)/libdtv.a | ||||
|  | ||||
| OBJS = audio.o config.o cutter.o device.o dvbplayer.o dvbosd.o eit.o eitscan.o font.o i18n.o\ | ||||
| OBJS = audio.o config.o cutter.o device.o dvbdevice.o dvbosd.o dvbplayer.o eit.o eitscan.o font.o i18n.o\ | ||||
|        interface.o menu.o menuitems.o osdbase.o osd.o player.o plugin.o receiver.o\ | ||||
|        recorder.o recording.o remote.o remux.o ringbuffer.o status.o svdrp.o thread.o\ | ||||
|        tools.o transfer.o vdr.o videodir.o | ||||
|   | ||||
							
								
								
									
										117
									
								
								PLUGINS.html
									
									
									
									
									
								
							
							
						
						
									
										117
									
								
								PLUGINS.html
									
									
									
									
									
								
							| @@ -1152,5 +1152,122 @@ of these functions, and VDR/osd.c to see how VDR opens the OSD and sets up | ||||
| its windows and color depths). | ||||
| <!--X1.1.5--></td></tr></table> | ||||
|  | ||||
| <!--X1.1.6--><table width=100%><tr><td bgcolor=#FF0000> </td><td width=100%> | ||||
| <hr><h2>Devices</h2> | ||||
|  | ||||
| <center><i><b>Expanding the possibilities</b></i></center><p> | ||||
|  | ||||
| By default VDR is based on using DVB PCI cards that are supported by the | ||||
| LinuxDVB driver. However, a plugin can implement additional devices that | ||||
| can be used as sources of MPEG data for viewing or recording, and also | ||||
| as output devices for replaying. Such a device can be a physical card | ||||
| that is installed in the PC (like, for instance, an MPEG encoder card that | ||||
| allows the analog signal of a proprietary set-top box to be integrated | ||||
| into a VDR system; or an analog TV receiver card, which does the MPEG encoding | ||||
| "on the fly" - assuming your machine is fast enough), or just a software program that takes an MPEG data | ||||
| stream and displays it, for instance, on an existing graphics adapter. | ||||
| <p> | ||||
| To implement an additional device, a plugin must derive a class from cDevice: | ||||
|  | ||||
| <p><table><tr><td bgcolor=#F0F0F0><pre><br> | ||||
| #include <vdr/device.h> | ||||
|  | ||||
| class cMyDevice : public cDevice { | ||||
|   ... | ||||
|   }; | ||||
| </pre></td></tr></table><p> | ||||
|  | ||||
| The derived class must implement several virtual functions, according to | ||||
| the abilities this new class of devices can provide. See the comments in the | ||||
| file <tt>VDR/device.h</tt> for more information on the various functions, | ||||
| and also <tt>VDR/dvbdevice.[hc]</tt> for details on the implementation of | ||||
| the <tt>cDvbDevice</tt>, which is used to access the DVB PCI cards. | ||||
| <p> | ||||
| <b>Channel selection</b> | ||||
| <p> | ||||
| If the new device can receive, it most likely needs to provide a way of | ||||
| selecting which channel it shall tune to: | ||||
|  | ||||
| <p><table><tr><td bgcolor=#F0F0F0><pre><br> | ||||
| virtual bool SetChannelDevice(const cChannel *Channel); | ||||
| </pre></td></tr></table><p> | ||||
|  | ||||
| This function will be called with the desired channel and shall return whether | ||||
| tuning to it was successful. | ||||
| <p> | ||||
| <b>Recording</b> | ||||
| <p> | ||||
| A device that can be used for recording must implement the functions | ||||
|  | ||||
| <p><table><tr><td bgcolor=#F0F0F0><pre><br> | ||||
| virtual bool SetPid(cPidHandle *Handle, int Type, bool On); | ||||
| virtual bool OpenDvr(void); | ||||
| virtual void CloseDvr(void); | ||||
| virtual int GetTSPacket(uchar *Data); | ||||
| </pre></td></tr></table><p> | ||||
|  | ||||
| which allow VDR to set the PIDs that shall be recorded, set up the device fro | ||||
| recording (and shut it down again), and receive the MPEG data stream. The data | ||||
| must be delivered in the form of a Transport Stream (TS), which consists of | ||||
| packets that are all 188 bytes in size. Each call to <tt>GetTSPacket()</tt> | ||||
| must deliver exactly one such packet (if one is currently available). | ||||
| <p> | ||||
| If this device allows receiving several different data streams, it can | ||||
| implement | ||||
|  | ||||
| <p><table><tr><td bgcolor=#F0F0F0><pre><br> | ||||
| virtual bool CanBeReUsed(int Frequency, int Vpid); | ||||
| </pre></td></tr></table><p> | ||||
|  | ||||
| to indicate this to VDR. | ||||
| <p> | ||||
| <b>Replaying</b> | ||||
| <p> | ||||
| The functions to implement replaying capabilites are | ||||
|  | ||||
| <p><table><tr><td bgcolor=#F0F0F0><pre><br> | ||||
| virtual bool HasDecoder(void) const; | ||||
| virtual int SetPlayMode(bool On); | ||||
| virtual void TrickSpeed(int Speed); | ||||
| virtual void Clear(void); | ||||
| virtual void Play(void); | ||||
| virtual void Freeze(void); | ||||
| virtual void Mute(void); | ||||
| virtual void StillPicture(const uchar *Data, int Length); | ||||
| virtual int PlayVideo(const uchar *Data, int Length); | ||||
| </pre></td></tr></table><p> | ||||
|  | ||||
| In addition, the following functions may be implemented to provide further | ||||
| functionality: | ||||
|  | ||||
| <p><table><tr><td bgcolor=#F0F0F0><pre><br> | ||||
| virtual bool GrabImage(const char *FileName, bool Jpeg = true, int Quality = -1, int Si | ||||
| virtual void SetVideoFormat(bool VideoFormat16_9); | ||||
| virtual void SetVolumeDevice(int Volume); | ||||
| </pre></td></tr></table><p> | ||||
|  | ||||
| <b>Initializing new devices</b> | ||||
| <p> | ||||
| A derived cDevice class shall implement a static function | ||||
|  | ||||
| <p><table><tr><td bgcolor=#F0F0F0><pre><br> | ||||
| static bool Initialize(void); | ||||
| </pre></td></tr></table><p> | ||||
|  | ||||
| in which it determines whether the necessary hardware to run this sort of | ||||
| device is actually present in this machine (or whatever other prerequisites | ||||
| might be important), and then creates as many device objects as necessary. | ||||
| See <tt>VDR/dvbdevice.c</tt> for the implementation of the <tt>cDvbDevice</tt> | ||||
| initialize function. | ||||
| <p> | ||||
| A plugin that adds devices to a VDR instance shall call this initializing | ||||
| function from its <a href="#Getting started"><tt>Start()</tt></a> function. | ||||
| <p> | ||||
| Nothing needs to be done to shut down the devices. VDR will automatically | ||||
| shut down (delete) all devices when the program terminates. It is therefore | ||||
| important that the devices are created on the heap, using the <tt>new</tt> | ||||
| operator! | ||||
| <!--X1.1.6--></td></tr></table> | ||||
|  | ||||
| </body> | ||||
| </html> | ||||
|   | ||||
							
								
								
									
										6
									
								
								config.c
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								config.c
									
									
									
									
									
								
							| @@ -4,7 +4,7 @@ | ||||
|  * See the main source file 'vdr.c' for copyright information and | ||||
|  * how to reach the author. | ||||
|  * | ||||
|  * $Id: config.c 1.102 2002/06/16 12:57:31 kls Exp $ | ||||
|  * $Id: config.c 1.103 2002/08/04 12:03:11 kls Exp $ | ||||
|  */ | ||||
|  | ||||
| #include "config.h" | ||||
| @@ -301,7 +301,7 @@ bool cChannel::Switch(cDevice *Device, bool Log) | ||||
|      if (Log) | ||||
|         isyslog("switching to channel %d", number); | ||||
|      for (int i = 3; i--;) { | ||||
|          switch (Device->SetChannel(number, frequency, polarization, diseqc, srate, vpid, apid1, tpid, ca, pnr)) { | ||||
|          switch (Device->SetChannel(this)) { | ||||
|            case scrOk:         return true; | ||||
|            case scrNoTransfer: if (Interface) | ||||
|                                   Interface->Error(tr("Can't start Transfer Mode!")); | ||||
| @@ -1018,7 +1018,7 @@ cSetup::cSetup(void) | ||||
|   DefaultLifetime = 50; | ||||
|   UseSubtitle = 1; | ||||
|   RecordingDirs = 1; | ||||
|   VideoFormat = VIDEO_FORMAT_4_3; | ||||
|   VideoFormat = 0; | ||||
|   RecordDolbyDigital = 1; | ||||
|   ChannelInfoPos = 0; | ||||
|   OSDwidth = 52; | ||||
|   | ||||
							
								
								
									
										679
									
								
								device.c
									
									
									
									
									
								
							
							
						
						
									
										679
									
								
								device.c
									
									
									
									
									
								
							| @@ -4,108 +4,42 @@ | ||||
|  * See the main source file 'vdr.c' for copyright information and | ||||
|  * how to reach the author. | ||||
|  * | ||||
|  * $Id: device.c 1.6 2002/07/28 11:03:53 kls Exp $ | ||||
|  * $Id: device.c 1.7 2002/08/04 12:32:49 kls Exp $ | ||||
|  */ | ||||
|  | ||||
| #include "device.h" | ||||
| #include <errno.h> | ||||
| extern "C" { | ||||
| #define HAVE_BOOLEAN | ||||
| #include <jpeglib.h> | ||||
| } | ||||
| #include <linux/videodev.h> | ||||
| #include <ost/sec.h> | ||||
| #include <poll.h> | ||||
| #include <sys/ioctl.h> | ||||
| #include <sys/mman.h> | ||||
| #include "eit.h" | ||||
| #include "player.h" | ||||
| #include "receiver.h" | ||||
| #include "status.h" | ||||
| #include "transfer.h" | ||||
|  | ||||
| #define DEV_VIDEO         "/dev/video" | ||||
| #define DEV_OST_OSD       "/dev/ost/osd" | ||||
| #define DEV_OST_FRONTEND  "/dev/ost/frontend" | ||||
| #define DEV_OST_SEC       "/dev/ost/sec" | ||||
| #define DEV_OST_DVR       "/dev/ost/dvr" | ||||
| #define DEV_OST_DEMUX     "/dev/ost/demux" | ||||
| #define DEV_OST_VIDEO     "/dev/ost/video" | ||||
| #define DEV_OST_AUDIO     "/dev/ost/audio" | ||||
|  | ||||
| // The default priority for non-primary DVB cards: | ||||
| // The default priority for non-primary devices: | ||||
| #define DEFAULTPRIORITY  -2 | ||||
|  | ||||
| #define TS_SIZE          188 | ||||
| #define TS_SYNC_BYTE     0x47 | ||||
| #define PID_MASK_HI      0x1F | ||||
|  | ||||
| // The maximum time we wait before assuming that a recorded video data stream | ||||
| // is broken: | ||||
| #define MAXBROKENTIMEOUT 30 // seconds | ||||
|  | ||||
| static const char *OstName(const char *Name, int n) | ||||
| { | ||||
|   static char buffer[_POSIX_PATH_MAX]; | ||||
|   snprintf(buffer, sizeof(buffer), "%s%d", Name, n); | ||||
|   return buffer; | ||||
| } | ||||
|  | ||||
| static int OstOpen(const char *Name, int n, int Mode, bool ReportError = false) | ||||
| { | ||||
|   const char *FileName = OstName(Name, n); | ||||
|   int fd = open(FileName, Mode); | ||||
|   if (fd < 0 && ReportError) | ||||
|      LOG_ERROR_STR(FileName); | ||||
|   return fd; | ||||
| } | ||||
|  | ||||
| int cDevice::numDevices = 0; | ||||
| int cDevice::useDevice = 0; | ||||
| int cDevice::nextCardIndex = 0; | ||||
| cDevice *cDevice::device[MAXDEVICES] = { NULL }; | ||||
| cDevice *cDevice::primaryDevice = NULL; | ||||
|  | ||||
| cDevice::cDevice(int n) | ||||
| cDevice::cDevice(void) | ||||
| { | ||||
|   frontendType = FrontendType(-1); // don't know how else to initialize this - there is no FE_UNKNOWN | ||||
|   siProcessor = NULL; | ||||
|   cardIndex = n; | ||||
|   cardIndex = nextCardIndex++; | ||||
|  | ||||
|   // Devices that are present on all card types: | ||||
|   SetVideoFormat(Setup.VideoFormat); | ||||
|  | ||||
|   fd_frontend = OstOpen(DEV_OST_FRONTEND, n, O_RDWR); | ||||
|  | ||||
|   // Devices that are only present on DVB-S cards: | ||||
|  | ||||
|   fd_sec      = OstOpen(DEV_OST_SEC,      n, O_RDWR); | ||||
|  | ||||
|   // Devices that are only present on cards with decoders: | ||||
|  | ||||
|   fd_osd      = OstOpen(DEV_OST_OSD,    n, O_RDWR); | ||||
|   fd_video    = OstOpen(DEV_OST_VIDEO,  n, O_RDWR | O_NONBLOCK); | ||||
|   fd_audio    = OstOpen(DEV_OST_AUDIO,  n, O_RDWR | O_NONBLOCK); | ||||
|  | ||||
|   // Video format: | ||||
|  | ||||
|   SetVideoFormat(Setup.VideoFormat ? VIDEO_FORMAT_16_9 : VIDEO_FORMAT_4_3); | ||||
|  | ||||
|   // We only check the devices that must be present - the others will be checked before accessing them://XXX | ||||
|  | ||||
|   if (fd_frontend >= 0) { | ||||
|      siProcessor = new cSIProcessor(OstName(DEV_OST_DEMUX, n)); | ||||
|      FrontendInfo feinfo; | ||||
|      if (ioctl(fd_frontend, FE_GET_INFO, &feinfo) >= 0) | ||||
|         frontendType = feinfo.type; | ||||
|      else | ||||
|         LOG_ERROR; | ||||
|      } | ||||
|   else | ||||
|      esyslog("ERROR: can't open video device %d", n); | ||||
|  | ||||
|   dvrFileName = strdup(OstName(DEV_OST_DVR, CardIndex())); | ||||
|   active = false; | ||||
|  | ||||
|   currentChannel = 0; | ||||
|   frequency = 0; | ||||
|  | ||||
|   mute = false; | ||||
|   volume = Setup.CurrentVolume; | ||||
| @@ -115,17 +49,20 @@ cDevice::cDevice(int n) | ||||
|   for (int i = 0; i < MAXRECEIVERS; i++) | ||||
|       receiver[i] = NULL; | ||||
|   ca = -1; | ||||
|  | ||||
|   if (numDevices < MAXDEVICES) { | ||||
|      device[numDevices++] = this; | ||||
|      SetCaCaps(cardIndex); | ||||
|      } | ||||
|   else | ||||
|      esyslog("ERROR: too many devices!"); | ||||
| } | ||||
|  | ||||
| cDevice::~cDevice() | ||||
| { | ||||
|   delete dvrFileName; | ||||
|   delete siProcessor; | ||||
|   Detach(player); | ||||
|   for (int i = 0; i < MAXRECEIVERS; i++) | ||||
|       Detach(receiver[i]); | ||||
|   // We're not explicitly closing any device files here, since this sometimes | ||||
|   // caused segfaults. Besides, the program is about to terminate anyway... | ||||
| } | ||||
|  | ||||
| void cDevice::SetUseDevice(int n) | ||||
| @@ -134,15 +71,44 @@ void cDevice::SetUseDevice(int n) | ||||
|      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; | ||||
| } | ||||
|  | ||||
| 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("invalid devive number: %d", n + 1); | ||||
|   esyslog("invalid device number: %d", n + 1); | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| bool cDevice::CanBeReUsed(int Frequency, int Vpid) | ||||
| { | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| bool cDevice::HasDecoder(void) const | ||||
| { | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| @@ -156,20 +122,7 @@ cDevice *cDevice::GetDevice(int Ca, int Priority, int Frequency, int Vpid, bool | ||||
|   for (int i = 0; i < numDevices; i++) { | ||||
|       if ((Provides[i] = device[i]->ProvidesCa(Ca)) != 0) { // this device is basicly able to do the job | ||||
|          //XXX+ dsyslog("GetDevice: %d %d %d %5d %5d", i, device[i]->HasDecoder(), device[i]->Receiving(), Frequency, device[i]->frequency);//XXX | ||||
|          if (  (!device[i]->HasDecoder() // it's a "budget card" which can receive multiple channels... | ||||
|                && device[i]->frequency == Frequency // ...and it is tuned to the requested frequency... | ||||
|                && device[i]->Receiving() // ...and is already receiving | ||||
|                // need not check priority - if a budget card is already receiving on the requested | ||||
|                // frequency, we can attach another receiver regardless of priority | ||||
|                ) | ||||
|             || (device[i]->HasDecoder() // it's a "full featured card" which can receive only one channel... | ||||
|                && device[i]->frequency == Frequency // ...and it is tuned to the requested frequency... | ||||
|                && device[i]->pidHandles[ptVideo].pid == Vpid // ...and the requested video PID... | ||||
|                && device[i]->Receiving() // ...and is already receiving | ||||
|                // need not check priority - if a full featured card is already receiving the requested | ||||
|                // frequency and video PID, we can attach another receiver regardless of priority | ||||
|                ) | ||||
|             ) { | ||||
|          if (device[i]->CanBeReUsed(Frequency, Vpid)) { | ||||
|             d = device[i]; | ||||
|             if (ReUse) | ||||
|                *ReUse = true; | ||||
| @@ -204,50 +157,14 @@ cDevice *cDevice::GetDevice(int Ca, int Priority, int Frequency, int Vpid, bool | ||||
|   return d; | ||||
| } | ||||
|  | ||||
| void cDevice::SetCaCaps(void) | ||||
| void cDevice::SetCaCaps(int Index) | ||||
| { | ||||
|   for (int d = 0; d < numDevices; d++) { | ||||
|       for (int i = 0; i < MAXCACAPS; i++) | ||||
|           device[d]->caCaps[i] = Setup.CaCaps[device[d]->CardIndex()][i]; | ||||
|       } | ||||
| } | ||||
|  | ||||
| bool cDevice::Probe(const char *FileName) | ||||
| { | ||||
|   if (access(FileName, F_OK) == 0) { | ||||
|      dsyslog("probing %s", FileName); | ||||
|      int f = open(FileName, O_RDONLY); | ||||
|      if (f >= 0) { | ||||
|         close(f); | ||||
|         return true; | ||||
|         } | ||||
|      else if (errno != ENODEV && errno != EINVAL) | ||||
|         LOG_ERROR_STR(FileName); | ||||
|      } | ||||
|   else if (errno != ENOENT) | ||||
|      LOG_ERROR_STR(FileName); | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| bool cDevice::Initialize(void) | ||||
| { | ||||
|   numDevices = 0; | ||||
|   for (int i = 0; i < MAXDEVICES; i++) { | ||||
|       if (useDevice == 0 || (useDevice & (1 << i)) != 0) { | ||||
|          if (Probe(OstName(DEV_OST_FRONTEND, i))) | ||||
|             device[numDevices++] = new cDevice(i); | ||||
|          else | ||||
|             break; | ||||
|       if (Index < 0 || Index == device[d]->CardIndex()) { | ||||
|          for (int i = 0; i < MAXCACAPS; i++) | ||||
|              device[d]->caCaps[i] = Setup.CaCaps[device[d]->CardIndex()][i]; | ||||
|          } | ||||
|       } | ||||
|   primaryDevice = device[0]; | ||||
|   if (numDevices > 0) { | ||||
|      isyslog("found %d video device%s", numDevices, numDevices > 1 ? "s" : ""); | ||||
|      SetCaCaps(); | ||||
|      } | ||||
|   else | ||||
|      esyslog("ERROR: no video device found, giving up!"); | ||||
|   return numDevices > 0; | ||||
| } | ||||
|  | ||||
| void cDevice::Shutdown(void) | ||||
| @@ -261,106 +178,13 @@ void cDevice::Shutdown(void) | ||||
|  | ||||
| bool cDevice::GrabImage(const char *FileName, bool Jpeg, int Quality, int SizeX, int SizeY) | ||||
| { | ||||
|   int videoDev = OstOpen(DEV_VIDEO, CardIndex(), O_RDWR, true); | ||||
|   if (videoDev >= 0) { | ||||
|      int result = 0; | ||||
|      struct video_mbuf mbuf; | ||||
|      result |= ioctl(videoDev, VIDIOCGMBUF, &mbuf); | ||||
|      if (result == 0) { | ||||
|         int msize = mbuf.size; | ||||
|         unsigned char *mem = (unsigned char *)mmap(0, msize, PROT_READ | PROT_WRITE, MAP_SHARED, videoDev, 0); | ||||
|         if (mem && mem != (unsigned char *)-1) { | ||||
|            // set up the size and RGB | ||||
|            struct video_capability vc; | ||||
|            result |= ioctl(videoDev, VIDIOCGCAP, &vc); | ||||
|            struct video_mmap vm; | ||||
|            vm.frame = 0; | ||||
|            if ((SizeX > 0) && (SizeX <= vc.maxwidth) && | ||||
|                (SizeY > 0) && (SizeY <= vc.maxheight)) { | ||||
|               vm.width = SizeX; | ||||
|               vm.height = SizeY; | ||||
|               } | ||||
|            else { | ||||
|               vm.width = vc.maxwidth; | ||||
|               vm.height = vc.maxheight; | ||||
|               } | ||||
|            vm.format = VIDEO_PALETTE_RGB24; | ||||
|            result |= ioctl(videoDev, VIDIOCMCAPTURE, &vm); | ||||
|            result |= ioctl(videoDev, VIDIOCSYNC, &vm.frame); | ||||
|            // make RGB out of BGR: | ||||
|            int memsize = vm.width * vm.height; | ||||
|            unsigned char *mem1 = mem; | ||||
|            for (int i = 0; i < memsize; i++) { | ||||
|                unsigned char tmp = mem1[2]; | ||||
|                mem1[2] = mem1[0]; | ||||
|                mem1[0] = tmp; | ||||
|                mem1 += 3; | ||||
|                } | ||||
|           | ||||
|            if (Quality < 0) | ||||
|               Quality = 255; //XXX is this 'best'??? | ||||
|           | ||||
|            isyslog("grabbing to %s (%s %d %d %d)", FileName, Jpeg ? "JPEG" : "PNM", Quality, vm.width, vm.height); | ||||
|            FILE *f = fopen(FileName, "wb"); | ||||
|            if (f) { | ||||
|               if (Jpeg) { | ||||
|                  // write JPEG file: | ||||
|                  struct jpeg_compress_struct cinfo; | ||||
|                  struct jpeg_error_mgr jerr; | ||||
|                  cinfo.err = jpeg_std_error(&jerr); | ||||
|                  jpeg_create_compress(&cinfo); | ||||
|                  jpeg_stdio_dest(&cinfo, f); | ||||
|                  cinfo.image_width = vm.width; | ||||
|                  cinfo.image_height = vm.height; | ||||
|                  cinfo.input_components = 3; | ||||
|                  cinfo.in_color_space = JCS_RGB; | ||||
|           | ||||
|                  jpeg_set_defaults(&cinfo); | ||||
|                  jpeg_set_quality(&cinfo, Quality, true); | ||||
|                  jpeg_start_compress(&cinfo, true); | ||||
|           | ||||
|                  int rs = vm.width * 3; | ||||
|                  JSAMPROW rp[vm.height]; | ||||
|                  for (int k = 0; k < vm.height; k++) | ||||
|                      rp[k] = &mem[rs * k]; | ||||
|                  jpeg_write_scanlines(&cinfo, rp, vm.height); | ||||
|                  jpeg_finish_compress(&cinfo); | ||||
|                  jpeg_destroy_compress(&cinfo); | ||||
|                  } | ||||
|               else { | ||||
|                  // write PNM file: | ||||
|                  if (fprintf(f, "P6\n%d\n%d\n255\n", vm.width, vm.height) < 0 || | ||||
|                      fwrite(mem, vm.width * vm.height * 3, 1, f) < 0) { | ||||
|                     LOG_ERROR_STR(FileName); | ||||
|                     result |= 1; | ||||
|                     } | ||||
|                  } | ||||
|               fclose(f); | ||||
|               } | ||||
|            else { | ||||
|               LOG_ERROR_STR(FileName); | ||||
|               result |= 1; | ||||
|               } | ||||
|            munmap(mem, msize); | ||||
|            } | ||||
|         else | ||||
|            result |= 1; | ||||
|         } | ||||
|      close(videoDev); | ||||
|      return result == 0; | ||||
|      } | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| void cDevice::SetVideoFormat(videoFormat_t Format) | ||||
| void cDevice::SetVideoFormat(bool VideoFormat16_9) | ||||
| { | ||||
|   if (HasDecoder()) | ||||
|      CHECK(ioctl(fd_video, VIDEO_SET_FORMAT, Format)); | ||||
| } | ||||
|  | ||||
| //                          ptVideo        ptAudio        ptTeletext        ptDolby        ptOther | ||||
| dmxPesType_t PesTypes[] = { DMX_PES_VIDEO, DMX_PES_AUDIO, DMX_PES_TELETEXT, DMX_PES_OTHER, DMX_PES_OTHER }; | ||||
|  | ||||
| //#define PRINTPIDS(s) { char b[500]; char *q = b; q += sprintf(q, "%d %s ", CardIndex(), s); for (int i = 0; i < MAXPIDHANDLES; i++) q += sprintf(q, " %s%4d %d", i == ptOther ? "* " : "", pidHandles[i].pid, pidHandles[i].used); dsyslog(b); } //XXX+ | ||||
| #define PRINTPIDS(s) | ||||
|  | ||||
| @@ -375,13 +199,12 @@ bool cDevice::AddPid(int Pid, ePidType PidType) | ||||
|          else if (a < 0 && i >= ptOther && !pidHandles[i].used) | ||||
|             a = i; | ||||
|          } | ||||
|      dmxPesType_t PesType = PesTypes[ptOther]; | ||||
|      if (n >= 0) { | ||||
|         // The Pid is already in use | ||||
|         if (++pidHandles[n].used == 2 && n <= ptTeletext) { | ||||
|            // It's a special PID that has to be switched into "tap" mode | ||||
|            // It's a special PID that may have to be switched into "tap" mode | ||||
|            PRINTPIDS("A");//XXX+ | ||||
|            return SetPid(pidHandles[n].fd, PesTypes[n], Pid, DMX_OUT_TS_TAP); | ||||
|            return SetPid(&pidHandles[n], n, true); | ||||
|            } | ||||
|         PRINTPIDS("a");//XXX+ | ||||
|         return true; | ||||
| @@ -389,8 +212,6 @@ bool cDevice::AddPid(int Pid, ePidType PidType) | ||||
|      else if (PidType < ptOther) { | ||||
|         // The Pid is not yet in use and it is a special one | ||||
|         n = PidType; | ||||
|         PesType = PesTypes[PidType]; | ||||
|         PRINTPIDS("B");//XXX+ | ||||
|         } | ||||
|      else if (a >= 0) { | ||||
|         // The Pid is not yet in use and we have a free slot | ||||
| @@ -400,217 +221,51 @@ bool cDevice::AddPid(int Pid, ePidType PidType) | ||||
|         esyslog("ERROR: no free slot for PID %d", Pid); | ||||
|      if (n >= 0) { | ||||
|         pidHandles[n].pid = Pid; | ||||
|         pidHandles[n].fd = OstOpen(DEV_OST_DEMUX, CardIndex(), O_RDWR | O_NONBLOCK, true); | ||||
|         pidHandles[n].used = 1; | ||||
|         PRINTPIDS("C");//XXX+ | ||||
|         return SetPid(pidHandles[n].fd, PesType, Pid, PidType <= ptTeletext ? DMX_OUT_DECODER : DMX_OUT_TS_TAP); | ||||
|         return SetPid(&pidHandles[n], n, true); | ||||
|         } | ||||
|      } | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| bool cDevice::DelPid(int Pid) | ||||
| void cDevice::DelPid(int Pid) | ||||
| { | ||||
|   if (Pid) { | ||||
|      for (int i = 0; i < MAXPIDHANDLES; i++) { | ||||
|          if (pidHandles[i].pid == Pid) { | ||||
|             switch (--pidHandles[i].used) { | ||||
|               case 0: CHECK(ioctl(pidHandles[i].fd, DMX_STOP));//XXX+ is this necessary??? | ||||
|                       close(pidHandles[i].fd); | ||||
|                       pidHandles[i].fd = -1; | ||||
|                       pidHandles[i].pid = 0; | ||||
|                       break; | ||||
|               case 1: if (i <= ptTeletext) | ||||
|                          SetPid(pidHandles[i].fd, PesTypes[i], Pid, DMX_OUT_DECODER); | ||||
|                       break; | ||||
|               } | ||||
|             if (--pidHandles[i].used < 2) { | ||||
|                SetPid(&pidHandles[i], i, false); | ||||
|                if (pidHandles[i].used == 0) { | ||||
|                    pidHandles[i].handle = -1; | ||||
|                    pidHandles[i].pid = 0; | ||||
|                    } | ||||
|                } | ||||
|             PRINTPIDS("D");//XXX+ | ||||
|             return pidHandles[i].used; | ||||
|             } | ||||
|          } | ||||
|      } | ||||
| } | ||||
|  | ||||
| bool cDevice::SetPid(cPidHandle *Handle, int Type, bool On) | ||||
| { | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| bool cDevice::SetPid(int fd, dmxPesType_t PesType, int Pid, dmxOutput_t Output) | ||||
| eSetChannelResult cDevice::SetChannel(const cChannel *Channel) | ||||
| { | ||||
|   if (Pid) { | ||||
|      CHECK(ioctl(fd, DMX_STOP)); | ||||
|      if (Pid != 0x1FFF) { | ||||
|         dmxPesFilterParams pesFilterParams; | ||||
|         pesFilterParams.pid     = Pid; | ||||
|         pesFilterParams.input   = DMX_IN_FRONTEND; | ||||
|         pesFilterParams.output  = Output; | ||||
|         pesFilterParams.pesType = PesType; | ||||
|         pesFilterParams.flags   = DMX_IMMEDIATE_START; | ||||
|         //XXX+ pesFilterParams.flags   = DMX_CHECK_CRC;//XXX | ||||
|         if (ioctl(fd, DMX_SET_PES_FILTER, &pesFilterParams) < 0) { | ||||
|            LOG_ERROR; | ||||
|            return false; | ||||
|            } | ||||
|         //XXX+ CHECK(ioctl(fd, DMX_SET_BUFFER_SIZE, KILOBYTE(32)));//XXX | ||||
|         //XXX+ CHECK(ioctl(fd, DMX_START));//XXX | ||||
|         } | ||||
|      } | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| eSetChannelResult cDevice::SetChannel(int ChannelNumber, int Frequency, char Polarization, int Diseqc, int Srate, int Vpid, int Apid, int Tpid, int Ca, int Pnr) | ||||
| { | ||||
|   StopReplay(); | ||||
|  | ||||
|   cStatus::MsgChannelSwitch(this, 0); | ||||
|  | ||||
|   StopReplay(); | ||||
|  | ||||
|   // Must set this anyway to avoid getting stuck when switching through | ||||
|   // channels with 'Up' and 'Down' keys: | ||||
|   currentChannel = ChannelNumber; | ||||
|  | ||||
|   // Avoid noise while switching: | ||||
|  | ||||
|   if (HasDecoder()) { | ||||
|      CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, true)); | ||||
|      CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true)); | ||||
|      CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER)); | ||||
|      CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER)); | ||||
|      } | ||||
|  | ||||
|   // Stop setting system time: | ||||
|  | ||||
|   if (siProcessor) | ||||
|      siProcessor->SetCurrentTransponder(0); | ||||
|   currentChannel = Channel->number; | ||||
|  | ||||
|   // If this card can't receive this channel, we must not actually switch | ||||
|   // the channel here, because that would irritate the driver when we | ||||
|   // start replaying in Transfer Mode immediately after switching the channel: | ||||
|   bool NeedsTransferMode = (IsPrimaryDevice() && !ProvidesCa(Ca)); | ||||
|  | ||||
|   if (!NeedsTransferMode) { | ||||
|  | ||||
|      // Turn off current PIDs: | ||||
|  | ||||
|      if (HasDecoder()) { | ||||
|         DelPid(pidHandles[ptVideo].pid); | ||||
|         DelPid(pidHandles[ptAudio].pid); | ||||
|         DelPid(pidHandles[ptTeletext].pid); | ||||
|         DelPid(pidHandles[ptDolby].pid); | ||||
|         } | ||||
|  | ||||
|      FrontendParameters Frontend; | ||||
|  | ||||
|      switch (frontendType) { | ||||
|        case FE_QPSK: { // DVB-S | ||||
|  | ||||
|             // Frequency offsets: | ||||
|  | ||||
|             unsigned int freq = Frequency; | ||||
|             int tone = SEC_TONE_OFF; | ||||
|  | ||||
|             if (freq < (unsigned int)Setup.LnbSLOF) { | ||||
|                freq -= Setup.LnbFrequLo; | ||||
|                tone = SEC_TONE_OFF; | ||||
|                } | ||||
|             else { | ||||
|                freq -= Setup.LnbFrequHi; | ||||
|                tone = SEC_TONE_ON; | ||||
|                } | ||||
|  | ||||
|             Frontend.Frequency = freq * 1000UL; | ||||
|             Frontend.Inversion = INVERSION_AUTO; | ||||
|             Frontend.u.qpsk.SymbolRate = Srate * 1000UL; | ||||
|             Frontend.u.qpsk.FEC_inner = FEC_AUTO; | ||||
|  | ||||
|             int volt = (Polarization == 'v' || Polarization == 'V') ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18; | ||||
|  | ||||
|             // DiseqC: | ||||
|  | ||||
|             secCommand scmd; | ||||
|             scmd.type = 0; | ||||
|             scmd.u.diseqc.addr = 0x10; | ||||
|             scmd.u.diseqc.cmd = 0x38; | ||||
|             scmd.u.diseqc.numParams = 1; | ||||
|             scmd.u.diseqc.params[0] = 0xF0 | ((Diseqc * 4) & 0x0F) | (tone == SEC_TONE_ON ? 1 : 0) | (volt == SEC_VOLTAGE_18 ? 2 : 0); | ||||
|  | ||||
|             secCmdSequence scmds; | ||||
|             scmds.voltage = volt; | ||||
|             scmds.miniCommand = SEC_MINI_NONE; | ||||
|             scmds.continuousTone = tone; | ||||
|             scmds.numCommands = Setup.DiSEqC ? 1 : 0; | ||||
|             scmds.commands = &scmd; | ||||
|  | ||||
|             CHECK(ioctl(fd_sec, SEC_SEND_SEQUENCE, &scmds)); | ||||
|             } | ||||
|             break; | ||||
|        case FE_QAM: { // DVB-C | ||||
|  | ||||
|             // Frequency and symbol rate: | ||||
|  | ||||
|             Frontend.Frequency = Frequency * 1000000UL; | ||||
|             Frontend.Inversion = INVERSION_AUTO; | ||||
|             Frontend.u.qam.SymbolRate = Srate * 1000UL; | ||||
|             Frontend.u.qam.FEC_inner = FEC_AUTO; | ||||
|             Frontend.u.qam.QAM = QAM_64; | ||||
|             } | ||||
|             break; | ||||
|        case FE_OFDM: { // DVB-T | ||||
|  | ||||
|             // Frequency and OFDM paramaters: | ||||
|  | ||||
|             Frontend.Frequency = Frequency * 1000UL; | ||||
|             Frontend.Inversion = INVERSION_AUTO; | ||||
|             Frontend.u.ofdm.bandWidth=BANDWIDTH_8_MHZ; | ||||
|             Frontend.u.ofdm.HP_CodeRate=FEC_2_3; | ||||
|             Frontend.u.ofdm.LP_CodeRate=FEC_1_2; | ||||
|             Frontend.u.ofdm.Constellation=QAM_64; | ||||
|             Frontend.u.ofdm.TransmissionMode=TRANSMISSION_MODE_2K; | ||||
|             Frontend.u.ofdm.guardInterval=GUARD_INTERVAL_1_32; | ||||
|             Frontend.u.ofdm.HierarchyInformation=HIERARCHY_NONE; | ||||
|             } | ||||
|             break; | ||||
|        default: | ||||
|             esyslog("ERROR: attempt to set channel with unknown DVB frontend type"); | ||||
|             return scrFailed; | ||||
|        } | ||||
|  | ||||
|      // Tuning: | ||||
|  | ||||
|      CHECK(ioctl(fd_frontend, FE_SET_FRONTEND, &Frontend)); | ||||
|  | ||||
|      // Wait for channel sync: | ||||
|  | ||||
|      if (cFile::FileReady(fd_frontend, 5000)) { | ||||
|         FrontendEvent event; | ||||
|         int res = ioctl(fd_frontend, FE_GET_EVENT, &event); | ||||
|         if (res >= 0) { | ||||
|            if (event.type != FE_COMPLETION_EV) { | ||||
|               esyslog("ERROR: channel %d not sync'ed on DVB card %d!", ChannelNumber, CardIndex() + 1); | ||||
|               if (IsPrimaryDevice()) | ||||
|                  cThread::RaisePanic(); | ||||
|               return scrFailed; | ||||
|               } | ||||
|            } | ||||
|         else | ||||
|            esyslog("ERROR %d in frontend get event (channel %d, card %d)", res, ChannelNumber, CardIndex() + 1); | ||||
|         } | ||||
|      else | ||||
|         esyslog("ERROR: timeout while tuning"); | ||||
|  | ||||
|      frequency = Frequency; | ||||
|  | ||||
|      // PID settings: | ||||
|  | ||||
|      if (HasDecoder()) { | ||||
|         if (!(AddPid(Vpid, ptVideo) && AddPid(Apid, ptAudio))) {//XXX+ dolby Dpid1!!! (if audio plugins are attached) | ||||
|            esyslog("ERROR: failed to set PIDs for channel %d", ChannelNumber); | ||||
|            return scrFailed; | ||||
|            } | ||||
|         if (IsPrimaryDevice()) | ||||
|            AddPid(Tpid, ptTeletext); | ||||
|         CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true)); | ||||
|         } | ||||
|      } | ||||
|  | ||||
|   if (IsPrimaryDevice() && siProcessor) | ||||
|      siProcessor->SetCurrentServiceID(Pnr); | ||||
|   bool NeedsTransferMode = (IsPrimaryDevice() && !ProvidesCa(Channel->ca)); | ||||
|  | ||||
|   eSetChannelResult Result = scrOk; | ||||
|  | ||||
| @@ -618,30 +273,32 @@ eSetChannelResult cDevice::SetChannel(int ChannelNumber, int Frequency, char Pol | ||||
|   // use the card that actually can receive it and transfer data from there: | ||||
|  | ||||
|   if (NeedsTransferMode) { | ||||
|      cDevice *CaDevice = GetDevice(Ca, 0); | ||||
|      if (CaDevice && !CaDevice->Receiving()) { | ||||
|         if ((Result = CaDevice->SetChannel(ChannelNumber, Frequency, Polarization, Diseqc, Srate, Vpid, Apid, Tpid, Ca, Pnr)) == scrOk) | ||||
|            cControl::Launch(new cTransferControl(CaDevice, Vpid, Apid, 0, 0, 0));//XXX+ | ||||
|         } | ||||
|      cDevice *CaDevice = GetDevice(Channel->ca, 0); | ||||
|      if (CaDevice && !CaDevice->Receiving() && CaDevice->SetChannel(Channel) == scrOk) | ||||
|         cControl::Launch(new cTransferControl(CaDevice, Channel->vpid, Channel->apid1, 0, 0, 0));//XXX+ | ||||
|      else | ||||
|         Result = scrNoTransfer; | ||||
|      } | ||||
|   else if (!SetChannelDevice(Channel)) | ||||
|      Result = scrFailed; | ||||
|  | ||||
|   if (HasDecoder()) { | ||||
|      CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, false)); | ||||
|      CHECK(ioctl(fd_video, VIDEO_SET_BLANK, false)); | ||||
|      } | ||||
|   if (IsPrimaryDevice()) | ||||
|      cSIProcessor::SetCurrentServiceID(Channel->pnr); | ||||
|  | ||||
|   // Start setting system time: | ||||
|  | ||||
|   if (Result == scrOk && siProcessor) | ||||
|      siProcessor->SetCurrentTransponder(Frequency); | ||||
|  | ||||
|   cStatus::MsgChannelSwitch(this, ChannelNumber); | ||||
|   cStatus::MsgChannelSwitch(this, Channel->number); | ||||
|  | ||||
|   return Result; | ||||
| } | ||||
|  | ||||
| bool cDevice::SetChannelDevice(const cChannel *Channel) | ||||
| { | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| void cDevice::SetVolumeDevice(int Volume) | ||||
| { | ||||
| } | ||||
|  | ||||
| bool cDevice::ToggleMute(void) | ||||
| { | ||||
|   int OldVolume = volume; | ||||
| @@ -653,78 +310,40 @@ bool cDevice::ToggleMute(void) | ||||
|  | ||||
| void cDevice::SetVolume(int Volume, bool Absolute) | ||||
| { | ||||
|   if (HasDecoder()) { | ||||
|      volume = min(max(Absolute ? Volume : volume + Volume, 0), MAXVOLUME); | ||||
|      audioMixer_t am; | ||||
|      am.volume_left = am.volume_right = volume; | ||||
|      CHECK(ioctl(fd_audio, AUDIO_SET_MIXER, &am)); | ||||
|      cStatus::MsgSetVolume(volume, Absolute); | ||||
|      if (volume > 0) | ||||
|         mute = false; | ||||
|      } | ||||
|   volume = min(max(Absolute ? Volume : volume + Volume, 0), MAXVOLUME); | ||||
|   SetVolumeDevice(volume); | ||||
|   cStatus::MsgSetVolume(volume, Absolute); | ||||
|   if (volume > 0) | ||||
|      mute = false; | ||||
| } | ||||
|  | ||||
| int cDevice::SetPlayMode(bool On) | ||||
| { | ||||
|   return -1; | ||||
| } | ||||
|  | ||||
| void cDevice::TrickSpeed(int Speed) | ||||
| { | ||||
|   if (fd_video >= 0) | ||||
|      CHECK(ioctl(fd_video, VIDEO_SLOWMOTION, Speed)); | ||||
| } | ||||
|  | ||||
| void cDevice::Clear(void) | ||||
| { | ||||
|   if (fd_video >= 0) | ||||
|      CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER)); | ||||
|   if (fd_audio >= 0) | ||||
|      CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER)); | ||||
| } | ||||
|  | ||||
| void cDevice::Play(void) | ||||
| { | ||||
|   if (fd_audio >= 0) | ||||
|      CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true)); | ||||
|   if (fd_video >= 0) | ||||
|      CHECK(ioctl(fd_video, VIDEO_CONTINUE)); | ||||
| } | ||||
|  | ||||
| void cDevice::Freeze(void) | ||||
| { | ||||
|   if (fd_audio >= 0) | ||||
|      CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, false)); | ||||
|   if (fd_video >= 0) | ||||
|      CHECK(ioctl(fd_video, VIDEO_FREEZE)); | ||||
| } | ||||
|  | ||||
| void cDevice::Mute(void) | ||||
| { | ||||
|   if (fd_audio >= 0) { | ||||
|      CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, false)); | ||||
|      CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, true)); | ||||
|      } | ||||
| } | ||||
|  | ||||
| void cDevice::StillPicture(const uchar *Data, int Length) | ||||
| { | ||||
|   Mute(); | ||||
| /* Using the VIDEO_STILLPICTURE ioctl call would be the | ||||
|    correct way to display a still frame, but unfortunately this | ||||
|    doesn't work with frames from VDR. So let's do pretty much the | ||||
|    same here as in DVB/driver/dvb.c's play_iframe() - I have absolutely | ||||
|    no idea why it works this way, but doesn't work with VIDEO_STILLPICTURE. | ||||
|    If anybody ever finds out what could be changed so that VIDEO_STILLPICTURE | ||||
|    could be used, please let me know! | ||||
|    kls 2002-03-23 | ||||
| */ | ||||
| //#define VIDEO_STILLPICTURE_WORKS_WITH_VDR_FRAMES | ||||
| #ifdef VIDEO_STILLPICTURE_WORKS_WITH_VDR_FRAMES | ||||
|   videoDisplayStillPicture sp = { (char *)Data, Length }; | ||||
|   CHECK(ioctl(fd_video, VIDEO_STILLPICTURE, &sp)); | ||||
| #else | ||||
| #define MIN_IFRAME 400000 | ||||
|   for (int i = MIN_IFRAME / Length + 1; i > 0; i--) { | ||||
|       safe_write(fd_video, Data, Length); | ||||
|       usleep(1); // allows the buffer to be displayed in case the progress display is active | ||||
|       } | ||||
| #endif | ||||
| } | ||||
|  | ||||
| bool cDevice::Replaying(void) | ||||
| @@ -741,19 +360,9 @@ bool cDevice::AttachPlayer(cPlayer *Player) | ||||
|   if (HasDecoder()) { | ||||
|      if (player) | ||||
|         Detach(player); | ||||
|  | ||||
|      if (siProcessor) | ||||
|         siProcessor->SetStatus(false); | ||||
|      CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true)); | ||||
|      CHECK(ioctl(fd_audio, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_MEMORY)); | ||||
|      CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true)); | ||||
|      CHECK(ioctl(fd_audio, AUDIO_PLAY)); | ||||
|      CHECK(ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY)); | ||||
|      CHECK(ioctl(fd_video, VIDEO_PLAY)); | ||||
|  | ||||
|      player = Player; | ||||
|      player->device = this; | ||||
|      player->deviceFileHandle = fd_video; | ||||
|      player->deviceFileHandle = SetPlayMode(true); | ||||
|      player->Activate(true); | ||||
|      return true; | ||||
|      } | ||||
| @@ -767,17 +376,7 @@ void cDevice::Detach(cPlayer *Player) | ||||
|      player->deviceFileHandle = -1; | ||||
|      player->device = NULL; | ||||
|      player = NULL; | ||||
|  | ||||
|      CHECK(ioctl(fd_video, VIDEO_STOP, true)); | ||||
|      CHECK(ioctl(fd_audio, AUDIO_STOP, true)); | ||||
|      CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER)); | ||||
|      CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER)); | ||||
|      CHECK(ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_DEMUX)); | ||||
|      CHECK(ioctl(fd_audio, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_DEMUX)); | ||||
|      CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true)); | ||||
|      CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, false)); | ||||
|      if (siProcessor) | ||||
|         siProcessor->SetStatus(true); | ||||
|      SetPlayMode(false); | ||||
|      } | ||||
| } | ||||
|  | ||||
| @@ -802,14 +401,11 @@ void cDevice::StopReplay(void) | ||||
|  | ||||
| int cDevice::PlayVideo(const uchar *Data, int Length) | ||||
| { | ||||
|   if (fd_video >= 0) | ||||
|      return write(fd_video, Data, Length); | ||||
|   return -1; | ||||
| } | ||||
|  | ||||
| int cDevice::PlayAudio(const uchar *Data, int Length) | ||||
| { | ||||
|   //XXX+ | ||||
|   return -1; | ||||
| } | ||||
|  | ||||
| @@ -828,7 +424,7 @@ int cDevice::Priority(void) | ||||
| int cDevice::CanShift(int Ca, int Priority, int UsedCards) | ||||
| { | ||||
|   return -1;//XXX+ too complex with multiple recordings per device | ||||
|   // Test whether a receiving on this DVB device can be shifted to another one | ||||
|   // 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) | ||||
| @@ -892,51 +488,31 @@ void cDevice::Action(void) | ||||
| { | ||||
|   dsyslog("receiver thread started on device %d (pid=%d)", CardIndex() + 1, getpid()); | ||||
|  | ||||
|   int fd_dvr = open(dvrFileName, O_RDONLY | O_NONBLOCK); | ||||
|   if (fd_dvr >= 0) { | ||||
|      pollfd pfd; | ||||
|      pfd.fd = fd_dvr; | ||||
|      pfd.events = pfd.revents = POLLIN; | ||||
|   if (OpenDvr()) { | ||||
|      uchar b[TS_SIZE]; | ||||
|      time_t t = time(NULL); | ||||
|      active = true; | ||||
|      for (; active;) { | ||||
|  | ||||
|          // Read data from the DVR device: | ||||
|  | ||||
|          if (pfd.revents & POLLIN != 0) { | ||||
|             int r = read(fd_dvr, b, sizeof(b)); | ||||
|             if (r == TS_SIZE) { | ||||
|                if (*b == TS_SYNC_BYTE) { | ||||
|                   // We're locked on to a TS packet | ||||
|                   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(); | ||||
|                   } | ||||
|                t = time(NULL); | ||||
|                } | ||||
|             else if (r > 0) | ||||
|                esyslog("ERROR: got incomplete TS packet (%d bytes)", r);//XXX+ TODO do we have to read the rest??? | ||||
|             else if (r < 0) { | ||||
|                if (FATALERRNO) { | ||||
|                   if (errno == EBUFFEROVERFLOW) // this error code is not defined in the library | ||||
|                      esyslog("ERROR: DVB driver buffer overflow on device %d", CardIndex() + 1); | ||||
|                   else { | ||||
|                      LOG_ERROR; | ||||
|                      break; | ||||
|                      } | ||||
|                   } | ||||
|          int r = GetTSPacket(b); | ||||
|          if (r == TS_SIZE) { | ||||
|             if (*b == TS_SYNC_BYTE) { | ||||
|                // We're locked on to a TS packet | ||||
|                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(); | ||||
|                } | ||||
|             t = time(NULL); | ||||
|             } | ||||
|  | ||||
|          // Wait for more data to become available: | ||||
|  | ||||
|          poll(&pfd, 1, 100); | ||||
|          else if (r > 0) | ||||
|             esyslog("ERROR: got incomplete TS packet (%d bytes) on device %d", r, CardIndex() + 1);//XXX+ TODO do we have to read the rest??? | ||||
|          else if (r < 0) | ||||
|             break; | ||||
|  | ||||
|          //XXX+ put this into the recorder??? or give the receiver a flag whether it wants this? | ||||
|          if (time(NULL) - t > MAXBROKENTIMEOUT) { | ||||
| @@ -945,14 +521,26 @@ void cDevice::Action(void) | ||||
|             t = time(NULL); | ||||
|             } | ||||
|          } | ||||
|      close(fd_dvr); | ||||
|      CloseDvr(); | ||||
|      } | ||||
|   else | ||||
|      LOG_ERROR_STR(dvrFileName); | ||||
|  | ||||
|   dsyslog("receiver thread ended on device %d (pid=%d)", CardIndex() + 1, getpid()); | ||||
| } | ||||
|  | ||||
| bool cDevice::OpenDvr(void) | ||||
| { | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| void cDevice::CloseDvr(void) | ||||
| { | ||||
| } | ||||
|  | ||||
| int cDevice::GetTSPacket(uchar *Data) | ||||
| { | ||||
|   return -1; | ||||
| } | ||||
|  | ||||
| bool cDevice::AttachReceiver(cReceiver *Receiver) | ||||
| { | ||||
|   //XXX+ check for same transponder??? | ||||
| @@ -963,7 +551,6 @@ bool cDevice::AttachReceiver(cReceiver *Receiver) | ||||
|   StopReplay(); | ||||
|   for (int i = 0; i < MAXRECEIVERS; i++) { | ||||
|       if (!receiver[i]) { | ||||
|          //siProcessor->SetStatus(false);//XXX+ | ||||
|          for (int n = 0; n < MAXRECEIVEPIDS; n++) | ||||
|              AddPid(Receiver->pids[n]);//XXX+ retval! | ||||
|          Receiver->Activate(true); | ||||
|   | ||||
							
								
								
									
										205
									
								
								device.h
									
									
									
									
									
								
							
							
						
						
									
										205
									
								
								device.h
									
									
									
									
									
								
							| @@ -4,38 +4,33 @@ | ||||
|  * See the main source file 'vdr.c' for copyright information and | ||||
|  * how to reach the author. | ||||
|  * | ||||
|  * $Id: device.h 1.4 2002/07/28 10:48:12 kls Exp $ | ||||
|  * $Id: device.h 1.5 2002/08/04 14:02:19 kls Exp $ | ||||
|  */ | ||||
|  | ||||
| #ifndef __DEVICE_H | ||||
| #define __DEVICE_H | ||||
|  | ||||
| #include <stdlib.h> // FIXME: this is apparently necessary for the ost/... header files | ||||
|                     // FIXME: shouldn't every header file include ALL the other header | ||||
|                     // FIXME: files it depends on? The sequence in which header files | ||||
|                     // FIXME: are included here should not matter - and it should NOT | ||||
|                     // FIXME: be necessary to include <stdlib.h> here! | ||||
| #include <ost/dmx.h> | ||||
| #include <ost/frontend.h> | ||||
| #include <ost/audio.h> | ||||
| #include <ost/video.h> | ||||
| #include "eit.h" | ||||
| #include "thread.h" | ||||
| #include "tools.h" | ||||
|  | ||||
| enum eSetChannelResult { scrOk, scrNoTransfer, scrFailed }; | ||||
|  | ||||
| #define MAXDEVICES          4 // the maximum number of devices in the system | ||||
| #define MAXCACAPS          16 // the maximum number of different CA values per DVB device | ||||
| #define MAXPIDHANDLES      16 // the maximum number of different PIDs per DVB device | ||||
| #define MAXRECEIVERS       16 // the maximum number of receivers per DVB device | ||||
| #define MAXDEVICES         16 // the maximum number of devices in the system | ||||
| #define MAXCACAPS          16 // the maximum number of different CA values per device | ||||
| #define MAXPIDHANDLES      16 // the maximum number of different PIDs per device | ||||
| #define MAXRECEIVERS       16 // the maximum number of receivers per device | ||||
| #define MAXVOLUME         255 | ||||
| #define VOLUMEDELTA         5 // used to increase/decrease the volume | ||||
|  | ||||
| #define TS_SIZE          188 | ||||
| #define TS_SYNC_BYTE     0x47 | ||||
| #define PID_MASK_HI      0x1F | ||||
|  | ||||
| enum eSetChannelResult { scrOk, scrNoTransfer, scrFailed }; | ||||
|  | ||||
| class cChannel; | ||||
| class cPlayer; | ||||
| class cReceiver; | ||||
|  | ||||
| class cDevice : cThread { | ||||
|   friend class cOsd;//XXX | ||||
| private: | ||||
|   static int numDevices; | ||||
|   static int useDevice; | ||||
| @@ -43,106 +38,142 @@ private: | ||||
|   static cDevice *primaryDevice; | ||||
| public: | ||||
|   static int NumDevices(void) { return numDevices; } | ||||
|          // Returns the total number of DVB devices. | ||||
|          // Returns the total number of devices. | ||||
|   static void SetUseDevice(int n); | ||||
|          // Sets the 'useDevice' flag of the given DVB device. | ||||
|          // If this function is not called before Initialize(), all DVB devices | ||||
|          // Sets the 'useDevice' flag of the given device. | ||||
|          // If this function is not called before initializing, all devices | ||||
|          // will be used. | ||||
|   static bool UseDevice(int n) { return useDevice == 0 || (useDevice & (1 << n)) != 0; } | ||||
|          // Tells whether the device with the given card index shall be used in | ||||
|          // this instance of VDR. | ||||
|   static bool SetPrimaryDevice(int n); | ||||
|          // Sets the primary DVB device to 'n' (which must be in the range | ||||
|          // Sets the primary device to 'n' (which must be in the range | ||||
|          // 1...numDevices) and returns true if this was possible. | ||||
|   static cDevice *PrimaryDevice(void) { return primaryDevice; } | ||||
|          // Returns the primary DVB device. | ||||
|          // Returns the primary device. | ||||
|   static cDevice *GetDevice(int Ca, int Priority, int Frequency = 0, int Vpid = 0, bool *ReUse = NULL); | ||||
|          // Selects a free DVB device, avoiding the primaryDevice if possible. | ||||
|          // Selects a free device, avoiding the primaryDevice if possible. | ||||
|          // If Ca is not 0, the device with the given number will be returned | ||||
|          // in case Ca is <= MAXDEVICES, or the device that provides the given | ||||
|          // value in its caCaps. | ||||
|          // If there is a device that is already tuned to the given Frequency, | ||||
|          // and that device is able to receive multiple channels ("budget" cards), | ||||
|          // that device will be returned. Else if a ("full featured") device is | ||||
|          // tuned to Frequency and Vpid, that one will be returned. | ||||
|          // If all DVB devices are currently receiving, the one receiving the | ||||
|          // lowest priority timer (if any) that is lower than the given Priority | ||||
|          // If there is a device that is already receiving and can be re-used to | ||||
|          // receive another data stream, that device will be returned. | ||||
|          // If all devices are currently receiving, the one receiving with the | ||||
|          // lowest priority (if any) that is lower than the given Priority | ||||
|          // will be returned. | ||||
|          // If ReUse is given, the caller will be informed whether the device can be re-used | ||||
|          // for a new recording. If ReUse returns 'true', the caller must NOT switch the channel | ||||
|          // (the device is already properly tuned). Otherwise the caller MUST switch the channel. | ||||
|   static void SetCaCaps(void); | ||||
|          // Sets the CaCaps of all DVB devices according to the Setup data. | ||||
|   static bool Probe(const char *FileName); | ||||
|          // Probes for existing DVB devices. | ||||
|   static bool Initialize(void); | ||||
|          // Initializes the DVB devices. | ||||
|          // Must be called before accessing any DVB functions. | ||||
|   static void SetCaCaps(int Index = -1); | ||||
|          // Sets the CaCaps of the given device according to the Setup data. | ||||
|          // By default the CaCaps of all devices are set. | ||||
|   static void Shutdown(void); | ||||
|          // Closes down all DVB devices. | ||||
|          // Closes down all devices. | ||||
|          // Must be called at the end of the program. | ||||
| private: | ||||
|   static int nextCardIndex; | ||||
|   int cardIndex; | ||||
|   int caCaps[MAXCACAPS]; | ||||
|   FrontendType frontendType; | ||||
|   char *dvrFileName; | ||||
|   bool active; | ||||
|   int fd_osd, fd_frontend, fd_sec, fd_audio, fd_video; | ||||
|   int OsdDeviceHandle(void) { return fd_osd; } | ||||
| public: | ||||
|   cDevice(int n); | ||||
| protected: | ||||
|   cDevice(void); | ||||
|   virtual ~cDevice(); | ||||
|   static int NextCardIndex(int n = 0); | ||||
|          // Each device in a given machine must have a unique card index, which | ||||
|          // will be used to identify the device for assigning Ca parameters and | ||||
|          // deciding whether to actually use that device in this particular | ||||
|          // instance of VDR. Every time a new cDevice is created, it will be | ||||
|          // given the current nextCardIndex, and then nextCardIndex will be | ||||
|          // automatically incremented by 1. A derived class can determine whether | ||||
|          // a given device shall be used by checking UseDevice(NextCardIndex()). | ||||
|          // If a device is skipped, or if there are possible device indexes left | ||||
|          // after a derived class has set up all its devices, NextCardIndex(n) | ||||
|          // must be called, where n is the number of card indexes to skip. | ||||
|   virtual void MakePrimaryDevice(bool On); | ||||
|          // Informs a device that it will be the primary device. If there is | ||||
|          // anything the device needs to set up when it becomes the primary | ||||
|          // device (On = true) or to shut down when it no longer is the primary | ||||
|          // device (On = false), it should do so in this function. | ||||
| public: | ||||
|   bool IsPrimaryDevice(void) const { return this == primaryDevice; } | ||||
|   int CardIndex(void) const { return cardIndex; } | ||||
|          // Returns the card index of this device (0 ... MAXDEVICES - 1). | ||||
|   int ProvidesCa(int Ca); | ||||
|          // Checks whether this DVB device provides the given value in its | ||||
|          // Checks whether this device provides the given value in its | ||||
|          // caCaps. Returns 0 if the value is not provided, 1 if only this | ||||
|          // value is provided, and > 1 if this and other values are provided. | ||||
|          // If the given value is equal to the number of this DVB device, | ||||
|          // If the given value is equal to the number of this device, | ||||
|          // 1 is returned. If it is 0 (FTA), 1 plus the number of other values | ||||
|          // in caCaps is returned. | ||||
|   bool HasDecoder(void) const { return fd_video >= 0 && fd_audio >= 0; } | ||||
|   virtual bool CanBeReUsed(int Frequency, int Vpid);//XXX TODO make it more abstract | ||||
|          // Tells whether this device is already receiving and allows another | ||||
|          // receiver with the given settings to be attached to it. | ||||
|   virtual bool HasDecoder(void) const; | ||||
|          // Tells whether this device has an MPEG decoder. | ||||
|  | ||||
| // Channel facilities | ||||
|  | ||||
| private: | ||||
| protected: | ||||
|   int currentChannel; | ||||
|   int frequency; | ||||
| public: | ||||
|   eSetChannelResult SetChannel(int ChannelNumber, int Frequency, char Polarization, int Diseqc, int Srate, int Vpid, int Apid, int Tpid, int Ca, int Pnr); | ||||
|   eSetChannelResult SetChannel(const cChannel *Channel); | ||||
|          // Sets the device to the given channel (general setup). | ||||
|   virtual bool SetChannelDevice(const cChannel *Channel); | ||||
|          // Sets the device to the given channel (actual physical setup). | ||||
|   static int CurrentChannel(void) { return primaryDevice ? primaryDevice->currentChannel : 0; } | ||||
|          // Returns the number of the current channel on the primary device. | ||||
|   int Channel(void) { return currentChannel; } | ||||
|          // Returns the number of the current channel on this device. | ||||
|  | ||||
| // PID handle facilities | ||||
|  | ||||
| private: | ||||
|   bool active; | ||||
|   virtual void Action(void); | ||||
| protected: | ||||
|   enum ePidType { ptVideo, ptAudio, ptTeletext, ptDolby, ptOther }; | ||||
|   class cPidHandle { | ||||
|   public: | ||||
|     int pid; | ||||
|     int fd; | ||||
|     int handle; | ||||
|     int used; | ||||
|     cPidHandle(void) { pid = used = 0; fd = -1; } | ||||
|     cPidHandle(void) { pid = used = 0; handle = -1; } | ||||
|     }; | ||||
|   cPidHandle pidHandles[MAXPIDHANDLES]; | ||||
|   bool AddPid(int Pid, ePidType PidType = ptOther); | ||||
|   bool DelPid(int Pid); | ||||
|   bool SetPid(int fd, dmxPesType_t PesType, int Pid, dmxOutput_t Output); | ||||
|   virtual void Action(void); | ||||
|          // Adds a PID to the set of PIDs this device shall receive. | ||||
|   void DelPid(int Pid); | ||||
|          // Deletes a PID from the set of PIDs this device shall receive. | ||||
|   virtual bool SetPid(cPidHandle *Handle, int Type, bool On); | ||||
|          // Does the actual PID setting on this device. | ||||
|          // On indicates whether the PID shall be added or deleted. | ||||
|          // Handle->handle can be used by the device to store information it | ||||
|          // needs to receive this PID (for instance a file handle). | ||||
|          // Handle->used indicated how many receivers are using this PID. | ||||
|          // Type indicates some special types of PIDs, which the device may | ||||
|          // need to set in a specific way. | ||||
|  | ||||
| // Image Grab facilities | ||||
|  | ||||
| public: | ||||
|   bool GrabImage(const char *FileName, bool Jpeg = true, int Quality = -1, int SizeX = -1, int SizeY = -1); | ||||
|   virtual bool GrabImage(const char *FileName, bool Jpeg = true, int Quality = -1, int SizeX = -1, int SizeY = -1); | ||||
|          // Grabs the currently visible screen image into the given file, with the | ||||
|          // given parameters. | ||||
|  | ||||
| // Video format facilities | ||||
|  | ||||
| public: | ||||
|   virtual void SetVideoFormat(videoFormat_t Format); | ||||
|   virtual void SetVideoFormat(bool VideoFormat16_9); | ||||
|          // Sets the output video format to either 16:9 or 4:3 (only useful | ||||
|          // if this device has an MPEG decoder). | ||||
|  | ||||
| // Volume facilities | ||||
|  | ||||
| private: | ||||
|   bool mute; | ||||
|   int volume; | ||||
| protected: | ||||
|   virtual void SetVolumeDevice(int Volume); | ||||
|        // Sets the audio volume on this device (Volume = 0...255). | ||||
| public: | ||||
|   bool IsMute(void) { return mute; } | ||||
|   bool ToggleMute(void); | ||||
| @@ -152,30 +183,46 @@ public: | ||||
|        // the current volume. | ||||
|   static int CurrentVolume(void) { return primaryDevice ? primaryDevice->volume : 0; }//XXX??? | ||||
|  | ||||
|   // EIT facilities | ||||
|  | ||||
| private: | ||||
|   cSIProcessor *siProcessor; | ||||
|  | ||||
| // Player facilities | ||||
|  | ||||
| private: | ||||
|   cPlayer *player; | ||||
| protected: | ||||
|   virtual int SetPlayMode(bool On); | ||||
|        // Sets the device into play mode (On = true) or normal | ||||
|        // viewing mode (On = false). If On is true, it may return a file | ||||
|        // handle that a player can use to poll this device when replaying. | ||||
|        //XXX TODO should be implemented differently | ||||
| public: | ||||
|   void TrickSpeed(int Speed); | ||||
|   void Clear(void); | ||||
|   void Play(void); | ||||
|   void Freeze(void); | ||||
|   void Mute(void); | ||||
|   void StillPicture(const uchar *Data, int Length); | ||||
|   virtual void TrickSpeed(int Speed); | ||||
|        // Sets the device into a mode where replay is done slower. | ||||
|        // Every single frame shall then be displayed the given number of | ||||
|        // times. | ||||
|   virtual void Clear(void); | ||||
|        // Clears all video and audio data from the device. | ||||
|   virtual void Play(void); | ||||
|        // Sets the device into play mode (after a previous trick | ||||
|        // mode). | ||||
|   virtual void Freeze(void); | ||||
|        // Puts the device into "freeze frame" mode. | ||||
|   virtual void Mute(void); | ||||
|        // Turns off audio while replaying. | ||||
|   virtual void StillPicture(const uchar *Data, int Length); | ||||
|        // Displays the given I-frame as a still picture. | ||||
|   virtual int PlayVideo(const uchar *Data, int Length); | ||||
|        // Actually plays the given data block as video. The data must be | ||||
|        // part of a PES (Packetized Elementary Stream) which can contain | ||||
|        // one video and one audio strem. | ||||
|   virtual int PlayAudio(const uchar *Data, int Length); | ||||
|        // Plays additional audio streams, like Dolby Digital. | ||||
|   bool Replaying(void); | ||||
|        // Returns true if we are currently replaying. | ||||
|   void StopReplay(void); | ||||
|        // Stops the current replay session (if any). | ||||
|   bool AttachPlayer(cPlayer *Player); | ||||
|        // Attaches the given player to this device. | ||||
|   void Detach(cPlayer *Player); | ||||
|   virtual int PlayVideo(const uchar *Data, int Length); | ||||
|   virtual int PlayAudio(const uchar *Data, int Length); | ||||
|        // Detaches the given player from this device. | ||||
|  | ||||
| // Receiver facilities | ||||
|  | ||||
| @@ -184,16 +231,30 @@ private: | ||||
|   int ca; | ||||
|   int Priority(void); | ||||
|       // Returns the priority of the current receiving session (0..MAXPRIORITY), | ||||
|       // or -1 if no receiver is currently active. The primary DVB device will | ||||
|       // or -1 if no receiver is currently active. The primary device will | ||||
|       // always return at least Setup.PrimaryLimit-1. | ||||
|   int CanShift(int Ca, int Priority, int UsedCards = 0); | ||||
| protected: | ||||
|   virtual bool OpenDvr(void); | ||||
|       // Opens the DVR of this device and prepares it to deliver a Transport | ||||
|       // Stream for use in a cReceiver. | ||||
|   virtual void CloseDvr(void); | ||||
|       // Shuts down the DVR. | ||||
|   virtual int GetTSPacket(uchar *Data); | ||||
|       // Gets exactly one TS packet from the DVR of this device and copies it | ||||
|       // into the given memory area (which is exactly 188 bytes in size). | ||||
|       // Returns the number of bytes copied into Data (which must be 188). | ||||
|       // If there is currently no TS packet available, 0 should be returned. | ||||
|       // In case of a non recoverable error, returns -1. | ||||
| public: | ||||
|   int  Ca(void) { return ca; } | ||||
|        // Returns the ca of the current receiving session. | ||||
|   bool Receiving(void); | ||||
|        // Returns true if we are currently receiving. | ||||
|   bool AttachReceiver(cReceiver *Receiver); | ||||
|        // Attaches the given receiver to this device. | ||||
|   void Detach(cReceiver *Receiver); | ||||
|        // Detaches the given receiver from this device. | ||||
|   }; | ||||
|  | ||||
| #endif //__DEVICE_H | ||||
|   | ||||
							
								
								
									
										615
									
								
								dvbdevice.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										615
									
								
								dvbdevice.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,615 @@ | ||||
| /* | ||||
|  * dvbdevice.c: The DVB device interface | ||||
|  * | ||||
|  * See the main source file 'vdr.c' for copyright information and | ||||
|  * how to reach the author. | ||||
|  * | ||||
|  * $Id: dvbdevice.c 1.1 2002/08/04 12:24:25 kls Exp $ | ||||
|  */ | ||||
|  | ||||
| #include "dvbdevice.h" | ||||
| #include <errno.h> | ||||
| extern "C" { | ||||
| #ifdef boolean | ||||
| #define HAVE_BOOLEAN | ||||
| #endif | ||||
| #include <jpeglib.h> | ||||
| #undef boolean | ||||
| } | ||||
| #include <limits.h> | ||||
| #include <linux/videodev.h> | ||||
| #include <ost/audio.h> | ||||
| #include <ost/sec.h> | ||||
| #include <ost/video.h> | ||||
| #include <poll.h> | ||||
| #include <sys/ioctl.h> | ||||
| #include <sys/mman.h> | ||||
| #include "dvbosd.h" | ||||
| #include "player.h" | ||||
| #include "receiver.h" | ||||
| #include "status.h" | ||||
| #include "transfer.h" | ||||
|  | ||||
| #define MAXDVBDEVICES     4 | ||||
|  | ||||
| #define DEV_VIDEO         "/dev/video" | ||||
| #define DEV_OST_OSD       "/dev/ost/osd" | ||||
| #define DEV_OST_FRONTEND  "/dev/ost/frontend" | ||||
| #define DEV_OST_SEC       "/dev/ost/sec" | ||||
| #define DEV_OST_DVR       "/dev/ost/dvr" | ||||
| #define DEV_OST_DEMUX     "/dev/ost/demux" | ||||
| #define DEV_OST_VIDEO     "/dev/ost/video" | ||||
| #define DEV_OST_AUDIO     "/dev/ost/audio" | ||||
|  | ||||
| static const char *OstName(const char *Name, int n) | ||||
| { | ||||
|   static char buffer[PATH_MAX]; | ||||
|   snprintf(buffer, sizeof(buffer), "%s%d", Name, n); | ||||
|   return buffer; | ||||
| } | ||||
|  | ||||
| static int OstOpen(const char *Name, int n, int Mode, bool ReportError = false) | ||||
| { | ||||
|   const char *FileName = OstName(Name, n); | ||||
|   int fd = open(FileName, Mode); | ||||
|   if (fd < 0 && ReportError) | ||||
|      LOG_ERROR_STR(FileName); | ||||
|   return fd; | ||||
| } | ||||
|  | ||||
| cDvbDevice::cDvbDevice(int n) | ||||
| { | ||||
|   frontendType = FrontendType(-1); // don't know how else to initialize this - there is no FE_UNKNOWN | ||||
|   siProcessor = NULL; | ||||
|  | ||||
|   // Devices that are present on all card types: | ||||
|  | ||||
|   fd_frontend = OstOpen(DEV_OST_FRONTEND, n, O_RDWR); | ||||
|  | ||||
|   // Devices that are only present on DVB-S cards: | ||||
|  | ||||
|   fd_sec      = OstOpen(DEV_OST_SEC,      n, O_RDWR); | ||||
|  | ||||
|   // Devices that are only present on cards with decoders: | ||||
|  | ||||
|   fd_osd      = OstOpen(DEV_OST_OSD,    n, O_RDWR); | ||||
|   fd_video    = OstOpen(DEV_OST_VIDEO,  n, O_RDWR | O_NONBLOCK); | ||||
|   fd_audio    = OstOpen(DEV_OST_AUDIO,  n, O_RDWR | O_NONBLOCK); | ||||
|  | ||||
|   // The DVR device (will be opened and closed as needed): | ||||
|  | ||||
|   fd_dvr = -1; | ||||
|  | ||||
|   // Video format: | ||||
|  | ||||
|   SetVideoFormat(Setup.VideoFormat ? VIDEO_FORMAT_16_9 : VIDEO_FORMAT_4_3); | ||||
|  | ||||
|   // We only check the devices that must be present - the others will be checked before accessing them://XXX | ||||
|  | ||||
|   if (fd_frontend >= 0) { | ||||
|      siProcessor = new cSIProcessor(OstName(DEV_OST_DEMUX, n)); | ||||
|      FrontendInfo feinfo; | ||||
|      if (ioctl(fd_frontend, FE_GET_INFO, &feinfo) >= 0) | ||||
|         frontendType = feinfo.type; | ||||
|      else | ||||
|         LOG_ERROR; | ||||
|      } | ||||
|   else | ||||
|      esyslog("ERROR: can't open DVB device %d", n); | ||||
|  | ||||
|   frequency = 0; | ||||
| } | ||||
|  | ||||
| cDvbDevice::~cDvbDevice() | ||||
| { | ||||
|   delete siProcessor; | ||||
|   // We're not explicitly closing any device files here, since this sometimes | ||||
|   // caused segfaults. Besides, the program is about to terminate anyway... | ||||
| } | ||||
|  | ||||
| bool cDvbDevice::Probe(const char *FileName) | ||||
| { | ||||
|   if (access(FileName, F_OK) == 0) { | ||||
|      dsyslog("probing %s", FileName); | ||||
|      int f = open(FileName, O_RDONLY); | ||||
|      if (f >= 0) { | ||||
|         close(f); | ||||
|         return true; | ||||
|         } | ||||
|      else if (errno != ENODEV && errno != EINVAL) | ||||
|         LOG_ERROR_STR(FileName); | ||||
|      } | ||||
|   else if (errno != ENOENT) | ||||
|      LOG_ERROR_STR(FileName); | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| bool cDvbDevice::Initialize(void) | ||||
| { | ||||
|   int found = 0; | ||||
|   int i; | ||||
|   for (i = 0; i < MAXDVBDEVICES; i++) { | ||||
|       if (UseDevice(NextCardIndex())) { | ||||
|          if (Probe(OstName(DEV_OST_FRONTEND, i))) { | ||||
|             new cDvbDevice(i); | ||||
|             found++; | ||||
|             } | ||||
|          else | ||||
|             break; | ||||
|          } | ||||
|       else | ||||
|          NextCardIndex(1); // skips this one | ||||
|       } | ||||
|   NextCardIndex(MAXDVBDEVICES - i); // skips the rest | ||||
|   if (found > 0) | ||||
|      isyslog("found %d video device%s", found, found > 1 ? "s" : ""); | ||||
|   else | ||||
|      isyslog("no DVB device found"); | ||||
|   return found > 0; | ||||
| } | ||||
|  | ||||
| void cDvbDevice::MakePrimaryDevice(bool On) | ||||
| { | ||||
|   cDvbOsd::SetDvbDevice(On ? this : NULL); | ||||
| } | ||||
|  | ||||
| bool cDvbDevice::CanBeReUsed(int Frequency, int Vpid) | ||||
| { | ||||
|   return Receiving() // to be reused the DVB device must already be receiving... | ||||
|       && frequency == Frequency // ...and tuned to the requested frequency... | ||||
|       && (!HasDecoder() // ...and either be a "budget card" which can receive multiple channels... | ||||
|           || pidHandles[ptVideo].pid == Vpid // ...or be a "full featured card" that's already tuned to the requested video PID | ||||
|          ); | ||||
| } | ||||
|  | ||||
| bool cDvbDevice::HasDecoder(void) const | ||||
| { | ||||
|   return fd_video >= 0 && fd_audio >= 0; | ||||
| } | ||||
|  | ||||
| bool cDvbDevice::GrabImage(const char *FileName, bool Jpeg, int Quality, int SizeX, int SizeY) | ||||
| { | ||||
|   int videoDev = OstOpen(DEV_VIDEO, CardIndex(), O_RDWR, true); | ||||
|   if (videoDev >= 0) { | ||||
|      int result = 0; | ||||
|      struct video_mbuf mbuf; | ||||
|      result |= ioctl(videoDev, VIDIOCGMBUF, &mbuf); | ||||
|      if (result == 0) { | ||||
|         int msize = mbuf.size; | ||||
|         unsigned char *mem = (unsigned char *)mmap(0, msize, PROT_READ | PROT_WRITE, MAP_SHARED, videoDev, 0); | ||||
|         if (mem && mem != (unsigned char *)-1) { | ||||
|            // set up the size and RGB | ||||
|            struct video_capability vc; | ||||
|            result |= ioctl(videoDev, VIDIOCGCAP, &vc); | ||||
|            struct video_mmap vm; | ||||
|            vm.frame = 0; | ||||
|            if ((SizeX > 0) && (SizeX <= vc.maxwidth) && | ||||
|                (SizeY > 0) && (SizeY <= vc.maxheight)) { | ||||
|               vm.width = SizeX; | ||||
|               vm.height = SizeY; | ||||
|               } | ||||
|            else { | ||||
|               vm.width = vc.maxwidth; | ||||
|               vm.height = vc.maxheight; | ||||
|               } | ||||
|            vm.format = VIDEO_PALETTE_RGB24; | ||||
|            result |= ioctl(videoDev, VIDIOCMCAPTURE, &vm); | ||||
|            result |= ioctl(videoDev, VIDIOCSYNC, &vm.frame); | ||||
|            // make RGB out of BGR: | ||||
|            int memsize = vm.width * vm.height; | ||||
|            unsigned char *mem1 = mem; | ||||
|            for (int i = 0; i < memsize; i++) { | ||||
|                unsigned char tmp = mem1[2]; | ||||
|                mem1[2] = mem1[0]; | ||||
|                mem1[0] = tmp; | ||||
|                mem1 += 3; | ||||
|                } | ||||
|           | ||||
|            if (Quality < 0) | ||||
|               Quality = 255; //XXX is this 'best'??? | ||||
|           | ||||
|            isyslog("grabbing to %s (%s %d %d %d)", FileName, Jpeg ? "JPEG" : "PNM", Quality, vm.width, vm.height); | ||||
|            FILE *f = fopen(FileName, "wb"); | ||||
|            if (f) { | ||||
|               if (Jpeg) { | ||||
|                  // write JPEG file: | ||||
|                  struct jpeg_compress_struct cinfo; | ||||
|                  struct jpeg_error_mgr jerr; | ||||
|                  cinfo.err = jpeg_std_error(&jerr); | ||||
|                  jpeg_create_compress(&cinfo); | ||||
|                  jpeg_stdio_dest(&cinfo, f); | ||||
|                  cinfo.image_width = vm.width; | ||||
|                  cinfo.image_height = vm.height; | ||||
|                  cinfo.input_components = 3; | ||||
|                  cinfo.in_color_space = JCS_RGB; | ||||
|           | ||||
|                  jpeg_set_defaults(&cinfo); | ||||
|                  jpeg_set_quality(&cinfo, Quality, true); | ||||
|                  jpeg_start_compress(&cinfo, true); | ||||
|           | ||||
|                  int rs = vm.width * 3; | ||||
|                  JSAMPROW rp[vm.height]; | ||||
|                  for (int k = 0; k < vm.height; k++) | ||||
|                      rp[k] = &mem[rs * k]; | ||||
|                  jpeg_write_scanlines(&cinfo, rp, vm.height); | ||||
|                  jpeg_finish_compress(&cinfo); | ||||
|                  jpeg_destroy_compress(&cinfo); | ||||
|                  } | ||||
|               else { | ||||
|                  // write PNM file: | ||||
|                  if (fprintf(f, "P6\n%d\n%d\n255\n", vm.width, vm.height) < 0 || | ||||
|                      fwrite(mem, vm.width * vm.height * 3, 1, f) < 0) { | ||||
|                     LOG_ERROR_STR(FileName); | ||||
|                     result |= 1; | ||||
|                     } | ||||
|                  } | ||||
|               fclose(f); | ||||
|               } | ||||
|            else { | ||||
|               LOG_ERROR_STR(FileName); | ||||
|               result |= 1; | ||||
|               } | ||||
|            munmap(mem, msize); | ||||
|            } | ||||
|         else | ||||
|            result |= 1; | ||||
|         } | ||||
|      close(videoDev); | ||||
|      return result == 0; | ||||
|      } | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| void cDvbDevice::SetVideoFormat(bool VideoFormat16_9) | ||||
| { | ||||
|   if (HasDecoder()) | ||||
|      CHECK(ioctl(fd_video, VIDEO_SET_FORMAT, VideoFormat16_9 ? VIDEO_FORMAT_16_9 : VIDEO_FORMAT_4_3)); | ||||
| } | ||||
|  | ||||
| //                          ptVideo        ptAudio        ptTeletext        ptDolby        ptOther | ||||
| dmxPesType_t PesTypes[] = { DMX_PES_VIDEO, DMX_PES_AUDIO, DMX_PES_TELETEXT, DMX_PES_OTHER, DMX_PES_OTHER }; | ||||
|  | ||||
| bool cDvbDevice::SetPid(cPidHandle *Handle, int Type, bool On) | ||||
| { | ||||
|   if (Handle->pid) { | ||||
|      if (On) { | ||||
|         if (Handle->handle < 0) { | ||||
|            Handle->handle = OstOpen(DEV_OST_DEMUX, CardIndex(), O_RDWR | O_NONBLOCK, true); | ||||
|            if (Handle->handle < 0) | ||||
|               return false; | ||||
|            } | ||||
|         } | ||||
|      else { | ||||
|         CHECK(ioctl(Handle->handle, DMX_STOP)); | ||||
|         if (Handle->used == 0) { | ||||
|            close(Handle->handle); | ||||
|            Handle->handle = -1; | ||||
|            return true; | ||||
|            } | ||||
|         } | ||||
|  | ||||
|      if (Handle->pid != 0x1FFF) { | ||||
|         dmxPesFilterParams pesFilterParams; | ||||
|         pesFilterParams.pid     = Handle->pid; | ||||
|         pesFilterParams.input   = DMX_IN_FRONTEND; | ||||
|         pesFilterParams.output  = (Type <= ptTeletext && Handle->used <= 1) ? DMX_OUT_DECODER : DMX_OUT_TS_TAP; | ||||
|         pesFilterParams.pesType = PesTypes[Type < ptOther ? Type : ptOther]; | ||||
|         pesFilterParams.flags   = DMX_IMMEDIATE_START; | ||||
|         //XXX+ pesFilterParams.flags   = DMX_CHECK_CRC;//XXX | ||||
|         if (ioctl(Handle->handle, DMX_SET_PES_FILTER, &pesFilterParams) < 0) { | ||||
|            LOG_ERROR; | ||||
|            return false; | ||||
|            } | ||||
|         //XXX+ CHECK(ioctl(Handle->handle, DMX_SET_BUFFER_SIZE, KILOBYTE(32)));//XXX | ||||
|         //XXX+ CHECK(ioctl(Handle->handle, DMX_START));//XXX | ||||
|         } | ||||
|      } | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| bool cDvbDevice::SetChannelDevice(const cChannel *Channel) | ||||
| { | ||||
|   // Avoid noise while switching: | ||||
|  | ||||
|   if (HasDecoder()) { | ||||
|      CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, true)); | ||||
|      CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true)); | ||||
|      CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER)); | ||||
|      CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER)); | ||||
|      } | ||||
|  | ||||
|   // Stop setting system time: | ||||
|  | ||||
|   if (siProcessor) | ||||
|      siProcessor->SetCurrentTransponder(0); | ||||
|  | ||||
|   // Turn off current PIDs: | ||||
|  | ||||
|   if (HasDecoder()) { | ||||
|      DelPid(pidHandles[ptVideo].pid); | ||||
|      DelPid(pidHandles[ptAudio].pid); | ||||
|      DelPid(pidHandles[ptTeletext].pid); | ||||
|      DelPid(pidHandles[ptDolby].pid); | ||||
|      } | ||||
|  | ||||
|   FrontendParameters Frontend; | ||||
|  | ||||
|   switch (frontendType) { | ||||
|     case FE_QPSK: { // DVB-S | ||||
|  | ||||
|          // Frequency offsets: | ||||
|  | ||||
|          unsigned int freq = Channel->frequency; | ||||
|          int tone = SEC_TONE_OFF; | ||||
|  | ||||
|          if (freq < (unsigned int)Setup.LnbSLOF) { | ||||
|             freq -= Setup.LnbFrequLo; | ||||
|             tone = SEC_TONE_OFF; | ||||
|             } | ||||
|          else { | ||||
|             freq -= Setup.LnbFrequHi; | ||||
|             tone = SEC_TONE_ON; | ||||
|             } | ||||
|  | ||||
|          Frontend.Frequency = freq * 1000UL; | ||||
|          Frontend.Inversion = INVERSION_AUTO; | ||||
|          Frontend.u.qpsk.SymbolRate = Channel->srate * 1000UL; | ||||
|          Frontend.u.qpsk.FEC_inner = FEC_AUTO; | ||||
|  | ||||
|          int volt = (Channel->polarization == 'v' || Channel->polarization == 'V') ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18; | ||||
|  | ||||
|          // DiseqC: | ||||
|  | ||||
|          secCommand scmd; | ||||
|          scmd.type = 0; | ||||
|          scmd.u.diseqc.addr = 0x10; | ||||
|          scmd.u.diseqc.cmd = 0x38; | ||||
|          scmd.u.diseqc.numParams = 1; | ||||
|          scmd.u.diseqc.params[0] = 0xF0 | ((Channel->diseqc * 4) & 0x0F) | (tone == SEC_TONE_ON ? 1 : 0) | (volt == SEC_VOLTAGE_18 ? 2 : 0); | ||||
|  | ||||
|          secCmdSequence scmds; | ||||
|          scmds.voltage = volt; | ||||
|          scmds.miniCommand = SEC_MINI_NONE; | ||||
|          scmds.continuousTone = tone; | ||||
|          scmds.numCommands = Setup.DiSEqC ? 1 : 0; | ||||
|          scmds.commands = &scmd; | ||||
|  | ||||
|          CHECK(ioctl(fd_sec, SEC_SEND_SEQUENCE, &scmds)); | ||||
|          } | ||||
|          break; | ||||
|     case FE_QAM: { // DVB-C | ||||
|  | ||||
|          // Frequency and symbol rate: | ||||
|  | ||||
|          Frontend.Frequency = Channel->frequency * 1000000UL; | ||||
|          Frontend.Inversion = INVERSION_AUTO; | ||||
|          Frontend.u.qam.SymbolRate = Channel->srate * 1000UL; | ||||
|          Frontend.u.qam.FEC_inner = FEC_AUTO; | ||||
|          Frontend.u.qam.QAM = QAM_64; | ||||
|          } | ||||
|          break; | ||||
|     case FE_OFDM: { // DVB-T | ||||
|  | ||||
|          // Frequency and OFDM paramaters: | ||||
|  | ||||
|          Frontend.Frequency = Channel->frequency * 1000UL; | ||||
|          Frontend.Inversion = INVERSION_AUTO; | ||||
|          Frontend.u.ofdm.bandWidth=BANDWIDTH_8_MHZ; | ||||
|          Frontend.u.ofdm.HP_CodeRate=FEC_2_3; | ||||
|          Frontend.u.ofdm.LP_CodeRate=FEC_1_2; | ||||
|          Frontend.u.ofdm.Constellation=QAM_64; | ||||
|          Frontend.u.ofdm.TransmissionMode=TRANSMISSION_MODE_2K; | ||||
|          Frontend.u.ofdm.guardInterval=GUARD_INTERVAL_1_32; | ||||
|          Frontend.u.ofdm.HierarchyInformation=HIERARCHY_NONE; | ||||
|          } | ||||
|          break; | ||||
|     default: | ||||
|          esyslog("ERROR: attempt to set channel with unknown DVB frontend type"); | ||||
|          return false; | ||||
|     } | ||||
|  | ||||
|   // Tuning: | ||||
|  | ||||
|   CHECK(ioctl(fd_frontend, FE_SET_FRONTEND, &Frontend)); | ||||
|  | ||||
|   // Wait for channel sync: | ||||
|  | ||||
|   if (cFile::FileReady(fd_frontend, 5000)) { | ||||
|      FrontendEvent event; | ||||
|      int res = ioctl(fd_frontend, FE_GET_EVENT, &event); | ||||
|      if (res >= 0) { | ||||
|         if (event.type != FE_COMPLETION_EV) { | ||||
|            esyslog("ERROR: channel %d not sync'ed on DVB card %d!", Channel->number, CardIndex() + 1); | ||||
|            if (IsPrimaryDevice()) | ||||
|               cThread::RaisePanic(); | ||||
|            return false; | ||||
|            } | ||||
|         } | ||||
|      else | ||||
|         esyslog("ERROR %d in frontend get event (channel %d, card %d)", res, Channel->number, CardIndex() + 1); | ||||
|      } | ||||
|   else | ||||
|      esyslog("ERROR: timeout while tuning"); | ||||
|  | ||||
|   frequency = Channel->frequency; | ||||
|  | ||||
|   // PID settings: | ||||
|  | ||||
|   if (HasDecoder()) { | ||||
|      if (!(AddPid(Channel->vpid, ptVideo) && AddPid(Channel->apid1, ptAudio))) {//XXX+ dolby dpid1!!! (if audio plugins are attached) | ||||
|         esyslog("ERROR: failed to set PIDs for channel %d", Channel->number); | ||||
|         return false; | ||||
|         } | ||||
|      if (IsPrimaryDevice()) | ||||
|         AddPid(Channel->tpid, ptTeletext); | ||||
|      CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true)); | ||||
|      } | ||||
|  | ||||
|   if (HasDecoder()) { | ||||
|      CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, false)); | ||||
|      CHECK(ioctl(fd_video, VIDEO_SET_BLANK, false)); | ||||
|      } | ||||
|  | ||||
|   // Start setting system time: | ||||
|  | ||||
|   if (siProcessor) | ||||
|      siProcessor->SetCurrentTransponder(Channel->frequency); | ||||
|  | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| void cDvbDevice::SetVolumeDevice(int Volume) | ||||
| { | ||||
|   if (HasDecoder()) { | ||||
|      audioMixer_t am; | ||||
|      am.volume_left = am.volume_right = Volume; | ||||
|      CHECK(ioctl(fd_audio, AUDIO_SET_MIXER, &am)); | ||||
|      } | ||||
| } | ||||
|  | ||||
| int cDvbDevice::SetPlayMode(bool On) | ||||
| { | ||||
|   if (On) { | ||||
|      if (siProcessor) | ||||
|         siProcessor->SetStatus(false); | ||||
|      CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true)); | ||||
|      CHECK(ioctl(fd_audio, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_MEMORY)); | ||||
|      CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true)); | ||||
|      CHECK(ioctl(fd_audio, AUDIO_PLAY)); | ||||
|      CHECK(ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY)); | ||||
|      CHECK(ioctl(fd_video, VIDEO_PLAY)); | ||||
|      return fd_video; | ||||
|      } | ||||
|   else { | ||||
|      CHECK(ioctl(fd_video, VIDEO_STOP, true)); | ||||
|      CHECK(ioctl(fd_audio, AUDIO_STOP, true)); | ||||
|      CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER)); | ||||
|      CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER)); | ||||
|      CHECK(ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_DEMUX)); | ||||
|      CHECK(ioctl(fd_audio, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_DEMUX)); | ||||
|      CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true)); | ||||
|      CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, false)); | ||||
|      if (siProcessor) | ||||
|         siProcessor->SetStatus(true); | ||||
|      return -1; | ||||
|      } | ||||
| } | ||||
|  | ||||
| void cDvbDevice::TrickSpeed(int Speed) | ||||
| { | ||||
|   if (fd_video >= 0) | ||||
|      CHECK(ioctl(fd_video, VIDEO_SLOWMOTION, Speed)); | ||||
| } | ||||
|  | ||||
| void cDvbDevice::Clear(void) | ||||
| { | ||||
|   if (fd_video >= 0) | ||||
|      CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER)); | ||||
|   if (fd_audio >= 0) | ||||
|      CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER)); | ||||
| } | ||||
|  | ||||
| void cDvbDevice::Play(void) | ||||
| { | ||||
|   if (fd_audio >= 0) | ||||
|      CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true)); | ||||
|   if (fd_video >= 0) | ||||
|      CHECK(ioctl(fd_video, VIDEO_CONTINUE)); | ||||
| } | ||||
|  | ||||
| void cDvbDevice::Freeze(void) | ||||
| { | ||||
|   if (fd_audio >= 0) | ||||
|      CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, false)); | ||||
|   if (fd_video >= 0) | ||||
|      CHECK(ioctl(fd_video, VIDEO_FREEZE)); | ||||
| } | ||||
|  | ||||
| void cDvbDevice::Mute(void) | ||||
| { | ||||
|   if (fd_audio >= 0) { | ||||
|      CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, false)); | ||||
|      CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, true)); | ||||
|      } | ||||
| } | ||||
|  | ||||
| void cDvbDevice::StillPicture(const uchar *Data, int Length) | ||||
| { | ||||
|   Mute(); | ||||
| /* Using the VIDEO_STILLPICTURE ioctl call would be the | ||||
|    correct way to display a still frame, but unfortunately this | ||||
|    doesn't work with frames from VDR. So let's do pretty much the | ||||
|    same here as in DVB/driver/dvb.c's play_iframe() - I have absolutely | ||||
|    no idea why it works this way, but doesn't work with VIDEO_STILLPICTURE. | ||||
|    If anybody ever finds out what could be changed so that VIDEO_STILLPICTURE | ||||
|    could be used, please let me know! | ||||
|    kls 2002-03-23 | ||||
| */ | ||||
| //#define VIDEO_STILLPICTURE_WORKS_WITH_VDR_FRAMES | ||||
| #ifdef VIDEO_STILLPICTURE_WORKS_WITH_VDR_FRAMES | ||||
|   videoDisplayStillPicture sp = { (char *)Data, Length }; | ||||
|   CHECK(ioctl(fd_video, VIDEO_STILLPICTURE, &sp)); | ||||
| #else | ||||
| #define MIN_IFRAME 400000 | ||||
|   for (int i = MIN_IFRAME / Length + 1; i > 0; i--) { | ||||
|       safe_write(fd_video, Data, Length); | ||||
|       usleep(1); // allows the buffer to be displayed in case the progress display is active | ||||
|       } | ||||
| #endif | ||||
| } | ||||
|  | ||||
| int cDvbDevice::PlayVideo(const uchar *Data, int Length) | ||||
| { | ||||
|   if (fd_video >= 0) | ||||
|      return write(fd_video, Data, Length); | ||||
|   return -1; | ||||
| } | ||||
|  | ||||
| int cDvbDevice::PlayAudio(const uchar *Data, int Length) | ||||
| { | ||||
|   //XXX+ | ||||
|   return -1; | ||||
| } | ||||
|  | ||||
| bool cDvbDevice::OpenDvr(void) | ||||
| { | ||||
|   CloseDvr(); | ||||
|   fd_dvr = OstOpen(DEV_OST_DVR, CardIndex(), O_RDONLY | O_NONBLOCK, true); | ||||
|   return fd_dvr >= 0; | ||||
| } | ||||
|  | ||||
| void cDvbDevice::CloseDvr(void) | ||||
| { | ||||
|   if (fd_dvr >= 0) { | ||||
|      close(fd_dvr); | ||||
|      fd_dvr = -1; | ||||
|      } | ||||
| } | ||||
|  | ||||
| int cDvbDevice::GetTSPacket(uchar *Data) | ||||
| { | ||||
|   if (fd_dvr >= 0) { | ||||
|      pollfd pfd; | ||||
|      pfd.fd = fd_dvr; | ||||
|      pfd.events = POLLIN; | ||||
|  | ||||
|      poll(&pfd, 1, 100); | ||||
|  | ||||
|      if (pfd.revents & POLLIN != 0) { | ||||
|         int r = read(fd_dvr, Data, TS_SIZE); | ||||
|         if (r >= 0) | ||||
|            return r; | ||||
|         else if (FATALERRNO) { | ||||
|            if (errno == EBUFFEROVERFLOW) // this error code is not defined in the library | ||||
|               esyslog("ERROR: DVB driver buffer overflow on device %d", CardIndex() + 1); | ||||
|            else { | ||||
|               LOG_ERROR; | ||||
|               return -1; | ||||
|               } | ||||
|            } | ||||
|         } | ||||
|      return 0; | ||||
|      } | ||||
|   else | ||||
|      return -1; | ||||
| } | ||||
							
								
								
									
										97
									
								
								dvbdevice.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								dvbdevice.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,97 @@ | ||||
| /* | ||||
|  * dvbdevice.h: The DVB device interface | ||||
|  * | ||||
|  * See the main source file 'vdr.c' for copyright information and | ||||
|  * how to reach the author. | ||||
|  * | ||||
|  * $Id: dvbdevice.h 1.1 2002/08/04 12:19:10 kls Exp $ | ||||
|  */ | ||||
|  | ||||
| #ifndef __DVBDEVICE_H | ||||
| #define __DVBDEVICE_H | ||||
|  | ||||
| #include <stdlib.h> // FIXME: this is apparently necessary for the ost/... header files | ||||
|                     // FIXME: shouldn't every header file include ALL the other header | ||||
|                     // FIXME: files it depends on? The sequence in which header files | ||||
|                     // FIXME: are included here should not matter - and it should NOT | ||||
|                     // FIXME: be necessary to include <stdlib.h> here! | ||||
| #include <ost/frontend.h> | ||||
| #include "device.h" | ||||
| #include "eit.h" | ||||
|  | ||||
| class cDvbDevice : public cDevice { | ||||
|   friend class cDvbOsd; | ||||
| private: | ||||
|   static bool Probe(const char *FileName); | ||||
|          // Probes for existing DVB devices. | ||||
| public: | ||||
|   static bool Initialize(void); | ||||
|          // Initializes the DVB devices. | ||||
|          // Must be called before accessing any DVB functions. | ||||
| private: | ||||
|   FrontendType frontendType; | ||||
|   int fd_osd, fd_frontend, fd_sec, fd_audio, fd_video, fd_dvr; | ||||
|   int OsdDeviceHandle(void) const { return fd_osd; } | ||||
| protected: | ||||
|   virtual void MakePrimaryDevice(bool On); | ||||
| public: | ||||
|   cDvbDevice(int n); | ||||
|   virtual ~cDvbDevice(); | ||||
|   virtual bool CanBeReUsed(int Frequency, int Vpid); | ||||
|   virtual bool HasDecoder(void) const; | ||||
|  | ||||
| // Channel facilities | ||||
|  | ||||
| private: | ||||
|   int frequency; | ||||
| public: | ||||
|   virtual bool SetChannelDevice(const cChannel *Channel); | ||||
|  | ||||
| // PID handle facilities | ||||
|  | ||||
| protected: | ||||
|   virtual bool SetPid(cPidHandle *Handle, int Type, bool On); | ||||
|  | ||||
| // Image Grab facilities | ||||
|  | ||||
| public: | ||||
|   virtual bool GrabImage(const char *FileName, bool Jpeg = true, int Quality = -1, int SizeX = -1, int SizeY = -1); | ||||
|  | ||||
| // Video format facilities | ||||
|  | ||||
| public: | ||||
|   virtual void SetVideoFormat(bool VideoFormat16_9); | ||||
|  | ||||
| // Volume facilities | ||||
|  | ||||
| protected: | ||||
|   virtual void SetVolumeDevice(int Volume); | ||||
|  | ||||
| // EIT facilities | ||||
|  | ||||
| private: | ||||
|   cSIProcessor *siProcessor; | ||||
|  | ||||
| // Player facilities | ||||
|  | ||||
| protected: | ||||
|   virtual int SetPlayMode(bool On); | ||||
| public: | ||||
|   virtual void TrickSpeed(int Speed); | ||||
|   virtual void Clear(void); | ||||
|   virtual void Play(void); | ||||
|   virtual void Freeze(void); | ||||
|   virtual void Mute(void); | ||||
|   virtual void StillPicture(const uchar *Data, int Length); | ||||
|   virtual int PlayVideo(const uchar *Data, int Length); | ||||
|   virtual int PlayAudio(const uchar *Data, int Length); | ||||
|  | ||||
| // Receiver facilities | ||||
|  | ||||
| protected: | ||||
|   virtual bool OpenDvr(void); | ||||
|   virtual void CloseDvr(void); | ||||
|   virtual int GetTSPacket(uchar *Data); | ||||
|   }; | ||||
|  | ||||
| #endif //__DVBDEVICE_H | ||||
							
								
								
									
										13
									
								
								dvbosd.c
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								dvbosd.c
									
									
									
									
									
								
							| @@ -4,7 +4,7 @@ | ||||
|  * See the main source file 'vdr.c' for copyright information and | ||||
|  * how to reach the author. | ||||
|  * | ||||
|  * $Id: dvbosd.c 1.17 2002/05/18 13:39:02 kls Exp $ | ||||
|  * $Id: dvbosd.c 1.18 2002/08/04 10:13:21 kls Exp $ | ||||
|  */ | ||||
|  | ||||
| #include "dvbosd.h" | ||||
| @@ -13,10 +13,12 @@ | ||||
| #include <sys/unistd.h> | ||||
| #include "tools.h" | ||||
|  | ||||
| cDvbOsd::cDvbOsd(int OsdDev, int x, int y) | ||||
| const cDvbDevice *cDvbOsd::dvbDevice = NULL; | ||||
|  | ||||
| cDvbOsd::cDvbOsd(int x, int y) | ||||
| :cOsdBase(x, y) | ||||
| { | ||||
|   osdDev = OsdDev; | ||||
|   osdDev = dvbDevice ? dvbDevice->OsdDeviceHandle() : -1; | ||||
|   if (osdDev < 0) | ||||
|      esyslog("ERROR: illegal OSD device handle (%d)!", osdDev); | ||||
| } | ||||
| @@ -27,6 +29,11 @@ cDvbOsd::~cDvbOsd() | ||||
|       CloseWindow(GetWindowNr(i)); | ||||
| } | ||||
|  | ||||
| void cDvbOsd::SetDvbDevice(const cDvbDevice *DvbDevice) | ||||
| { | ||||
|   dvbDevice = DvbDevice; | ||||
| } | ||||
|  | ||||
| bool cDvbOsd::SetWindow(cWindow *Window) | ||||
| { | ||||
|   if (Window) { | ||||
|   | ||||
							
								
								
									
										7
									
								
								dvbosd.h
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								dvbosd.h
									
									
									
									
									
								
							| @@ -4,17 +4,19 @@ | ||||
|  * See the main source file 'vdr.c' for copyright information and | ||||
|  * how to reach the author. | ||||
|  * | ||||
|  * $Id: dvbosd.h 1.13 2002/05/18 13:38:09 kls Exp $ | ||||
|  * $Id: dvbosd.h 1.14 2002/08/04 10:12:14 kls Exp $ | ||||
|  */ | ||||
|  | ||||
| #ifndef __DVBOSD_H | ||||
| #define __DVBOSD_H | ||||
|  | ||||
| #include <ost/osd.h> | ||||
| #include "dvbdevice.h" | ||||
| #include "osdbase.h" | ||||
|  | ||||
| class cDvbOsd : public cOsdBase { | ||||
| private: | ||||
|   static const cDvbDevice *dvbDevice; | ||||
|   int osdDev; | ||||
|   bool SetWindow(cWindow *Window); | ||||
|   void Cmd(OSD_Command cmd, int color = 0, int x0 = 0, int y0 = 0, int x1 = 0, int y1 = 0, const void *data = NULL); | ||||
| @@ -26,8 +28,9 @@ protected: | ||||
|   virtual void MoveWindow(cWindow *Window, int x, int y); | ||||
|   virtual void CloseWindow(cWindow *Window); | ||||
| public: | ||||
|   cDvbOsd(int OsdDev, int x, int y); | ||||
|   cDvbOsd(int x, int y); | ||||
|   virtual ~cDvbOsd(); | ||||
|   static void SetDvbDevice(const cDvbDevice *DvbDevice); | ||||
|   }; | ||||
|  | ||||
| #endif //__DVBOSD_H | ||||
|   | ||||
							
								
								
									
										4
									
								
								eit.h
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								eit.h
									
									
									
									
									
								
							| @@ -16,7 +16,7 @@ | ||||
|  *   the Free Software Foundation; either version 2 of the License, or     * | ||||
|  *   (at your option) any later version.                                   * | ||||
|  *                                                                         * | ||||
|  * $Id: eit.h 1.16 2002/03/10 10:56:57 kls Exp $ | ||||
|  * $Id: eit.h 1.17 2002/08/04 11:30:24 kls Exp $ | ||||
|  ***************************************************************************/ | ||||
|  | ||||
| #ifndef __EIT_H | ||||
| @@ -158,7 +158,7 @@ public: | ||||
|   static bool Read(FILE *f = NULL); | ||||
|   void SetStatus(bool On); | ||||
|   void SetCurrentTransponder(int CurrentTransponder); | ||||
|   bool SetCurrentServiceID(unsigned short servid); | ||||
|   static bool SetCurrentServiceID(unsigned short servid); | ||||
|   }; | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -4,10 +4,11 @@ | ||||
|  * See the main source file 'vdr.c' for copyright information and | ||||
|  * how to reach the author. | ||||
|  * | ||||
|  * $Id: eitscan.c 1.3 2002/06/22 13:02:40 kls Exp $ | ||||
|  * $Id: eitscan.c 1.4 2002/07/28 15:10:23 kls Exp $ | ||||
|  */ | ||||
|  | ||||
| #include "eitscan.h" | ||||
| #include <stdlib.h> | ||||
|  | ||||
| cEITScanner::cEITScanner(void) | ||||
| { | ||||
|   | ||||
							
								
								
									
										4
									
								
								menu.c
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								menu.c
									
									
									
									
									
								
							| @@ -4,7 +4,7 @@ | ||||
|  * See the main source file 'vdr.c' for copyright information and | ||||
|  * how to reach the author. | ||||
|  * | ||||
|  * $Id: menu.c 1.202 2002/07/14 10:55:37 kls Exp $ | ||||
|  * $Id: menu.c 1.203 2002/08/03 09:55:44 kls Exp $ | ||||
|  */ | ||||
|  | ||||
| #include "menu.h" | ||||
| @@ -1628,7 +1628,7 @@ eOSState cMenuSetupDVB::ProcessKey(eKeys Key) | ||||
|   if (state == osBack && Key == kOk) { | ||||
|      if (Setup.PrimaryDVB != oldPrimaryDVB) { | ||||
|         state = osSwitchDvb; | ||||
|         cDevice::PrimaryDevice()->SetVideoFormat(Setup.VideoFormat ? VIDEO_FORMAT_16_9 : VIDEO_FORMAT_4_3); | ||||
|         cDevice::PrimaryDevice()->SetVideoFormat(Setup.VideoFormat); | ||||
|         } | ||||
|      } | ||||
|   return state; | ||||
|   | ||||
							
								
								
									
										4
									
								
								osd.c
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								osd.c
									
									
									
									
									
								
							| @@ -4,7 +4,7 @@ | ||||
|  * See the main source file 'vdr.c' for copyright information and | ||||
|  * how to reach the author. | ||||
|  * | ||||
|  * $Id: osd.c 1.31 2002/07/14 11:05:30 kls Exp $ | ||||
|  * $Id: osd.c 1.32 2002/08/04 10:11:26 kls Exp $ | ||||
|  */ | ||||
|  | ||||
| #include "osd.h" | ||||
| @@ -73,7 +73,7 @@ cOsdBase *cOsd::OpenRaw(int x, int y) | ||||
| #ifdef DEBUG_OSD | ||||
|   return NULL; | ||||
| #else | ||||
|   return osd ? NULL : new cDvbOsd(cDevice::PrimaryDevice()->OsdDeviceHandle(), x, y); | ||||
|   return osd ? NULL : new cDvbOsd(x, y); | ||||
| #endif | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -4,12 +4,13 @@ | ||||
|  * See the main source file 'vdr.c' for copyright information and | ||||
|  * how to reach the author. | ||||
|  * | ||||
|  * $Id: receiver.c 1.2 2002/07/28 10:48:42 kls Exp $ | ||||
|  * $Id: receiver.c 1.3 2002/07/28 15:14:49 kls Exp $ | ||||
|  */ | ||||
|  | ||||
| #include <stdarg.h> | ||||
| #include <stdio.h> | ||||
| #include "receiver.h" | ||||
| #include "tools.h" | ||||
|  | ||||
| cReceiver::cReceiver(int Ca, int Priority, int NumPids, ...) | ||||
| { | ||||
|   | ||||
							
								
								
									
										4
									
								
								remux.h
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								remux.h
									
									
									
									
									
								
							| @@ -4,7 +4,7 @@ | ||||
|  * See the main source file 'vdr.c' for copyright information and | ||||
|  * how to reach the author. | ||||
|  * | ||||
|  * $Id: remux.h 1.5 2001/06/23 14:06:59 kls Exp $ | ||||
|  * $Id: remux.h 1.6 2002/08/04 10:27:07 kls Exp $ | ||||
|  */ | ||||
|  | ||||
| #ifndef __REMUX_H | ||||
| @@ -12,6 +12,7 @@ | ||||
|  | ||||
| #include <time.h> //XXX FIXME: DVB/ost/include/ost/dmx.h should include <time.h> itself!!! | ||||
| #include <ost/dmx.h> | ||||
| #include "tools.h" | ||||
|  | ||||
| // Picture types: | ||||
| #define NO_PICTURE 0 | ||||
| @@ -24,7 +25,6 @@ | ||||
|  | ||||
| #define RESULTBUFFERSIZE (MINVIDEODATA * 4) | ||||
|  | ||||
| typedef unsigned char uchar; | ||||
| class cTS2PES; | ||||
|  | ||||
| class cRemux { | ||||
|   | ||||
| @@ -4,15 +4,14 @@ | ||||
|  * See the main source file 'vdr.c' for copyright information and | ||||
|  * how to reach the author. | ||||
|  * | ||||
|  * $Id: ringbuffer.h 1.6 2002/06/16 11:30:07 kls Exp $ | ||||
|  * $Id: ringbuffer.h 1.7 2002/08/04 10:27:30 kls Exp $ | ||||
|  */ | ||||
|  | ||||
| #ifndef __RINGBUFFER_H | ||||
| #define __RINGBUFFER_H | ||||
|  | ||||
| #include "thread.h" | ||||
|  | ||||
| typedef unsigned char uchar;//XXX+ | ||||
| #include "tools.h" | ||||
|  | ||||
| class cRingBuffer { | ||||
| private: | ||||
|   | ||||
							
								
								
									
										4
									
								
								tools.c
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								tools.c
									
									
									
									
									
								
							| @@ -4,7 +4,7 @@ | ||||
|  * See the main source file 'vdr.c' for copyright information and | ||||
|  * how to reach the author. | ||||
|  * | ||||
|  * $Id: tools.c 1.67 2002/05/18 15:10:45 kls Exp $ | ||||
|  * $Id: tools.c 1.68 2002/08/03 15:44:53 kls Exp $ | ||||
|  */ | ||||
|  | ||||
| #include "tools.h" | ||||
| @@ -407,7 +407,7 @@ bool RemoveEmptyDirectories(const char *DirName, bool RemoveThis) | ||||
|  | ||||
| char *ReadLink(const char *FileName) | ||||
| { | ||||
|   char RealName[_POSIX_PATH_MAX]; | ||||
|   char RealName[PATH_MAX]; | ||||
|   const char *TargetName = NULL; | ||||
|   int n = readlink(FileName, RealName, sizeof(RealName) - 1); | ||||
|   if (n < 0) { | ||||
|   | ||||
							
								
								
									
										11
									
								
								vdr.c
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								vdr.c
									
									
									
									
									
								
							| @@ -22,7 +22,7 @@ | ||||
|  * | ||||
|  * The project's page is at http://www.cadsoft.de/people/kls/vdr | ||||
|  * | ||||
|  * $Id: vdr.c 1.117 2002/06/23 11:23:34 kls Exp $ | ||||
|  * $Id: vdr.c 1.118 2002/08/04 09:56:30 kls Exp $ | ||||
|  */ | ||||
|  | ||||
| #include <getopt.h> | ||||
| @@ -33,6 +33,7 @@ | ||||
| #include "config.h" | ||||
| #include "cutter.h" | ||||
| #include "device.h" | ||||
| #include "dvbdevice.h" | ||||
| #include "eitscan.h" | ||||
| #include "i18n.h" | ||||
| #include "interface.h" | ||||
| @@ -326,11 +327,9 @@ int main(int argc, char *argv[]) | ||||
|  | ||||
|   // DVB interfaces: | ||||
|  | ||||
|   if (!cDevice::Initialize()) | ||||
|   if (!cDvbDevice::Initialize()) | ||||
|      return 2; | ||||
|  | ||||
|   cDevice::SetPrimaryDevice(Setup.PrimaryDVB); | ||||
|  | ||||
|   cSIProcessor::Read(); | ||||
|  | ||||
|   // Start plugins: | ||||
| @@ -338,6 +337,10 @@ int main(int argc, char *argv[]) | ||||
|   if (!PluginManager.StartPlugins()) | ||||
|      return 2; | ||||
|  | ||||
|   // Primary device: | ||||
|  | ||||
|   cDevice::SetPrimaryDevice(Setup.PrimaryDVB); | ||||
|  | ||||
|   // OSD: | ||||
|  | ||||
|   cOsd::Initialize(); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user