diff options
| -rw-r--r-- | HISTORY | 21 | ||||
| -rw-r--r-- | Makefile | 9 | ||||
| -rw-r--r-- | PLUGINS.html | 8 | ||||
| -rw-r--r-- | audio.c | 11 | ||||
| -rw-r--r-- | audio.h | 13 | ||||
| -rw-r--r-- | config.c | 29 | ||||
| -rw-r--r-- | config.h | 10 | ||||
| -rw-r--r-- | device.c | 1017 | ||||
| -rw-r--r-- | device.h | 199 | ||||
| -rw-r--r-- | dvbplayer.c | 733 | ||||
| -rw-r--r-- | dvbplayer.h | 60 | ||||
| -rw-r--r-- | eit.c | 3 | ||||
| -rw-r--r-- | eitscan.c | 20 | ||||
| -rw-r--r-- | interface.c | 6 | ||||
| -rw-r--r-- | menu.c | 212 | ||||
| -rw-r--r-- | menu.h | 21 | ||||
| -rw-r--r-- | osd.c | 6 | ||||
| -rw-r--r-- | player.c | 55 | ||||
| -rw-r--r-- | player.h | 51 | ||||
| -rw-r--r-- | receiver.c | 55 | ||||
| -rw-r--r-- | receiver.h | 48 | ||||
| -rw-r--r-- | recorder.c | 149 | ||||
| -rw-r--r-- | recorder.h | 43 | ||||
| -rw-r--r-- | recording.c | 350 | ||||
| -rw-r--r-- | recording.h | 61 | ||||
| -rw-r--r-- | ringbuffer.c | 93 | ||||
| -rw-r--r-- | ringbuffer.h | 42 | ||||
| -rw-r--r-- | status.c | 14 | ||||
| -rw-r--r-- | status.h | 23 | ||||
| -rw-r--r-- | svdrp.c | 26 | ||||
| -rw-r--r-- | thread.c | 3 | ||||
| -rw-r--r-- | thread.h | 4 | ||||
| -rw-r--r-- | tools.h | 9 | ||||
| -rw-r--r-- | vdr.c | 51 | 
34 files changed, 3144 insertions, 311 deletions
| @@ -1301,7 +1301,7 @@ Video Disk Recorder Revision History  - Removed compiler option '-m486' to make it work on non-Intel platforms (thanks    to Alastair McKinstry for pointing this out). -2002-05-26: Version 1.1.3 +2002-06-16: Version 1.1.3  - Improved the VDR Makefile to avoid a warning if the '.dependencies' file does    not exist, and also using $(MAKE) to call recursive makes. @@ -1328,3 +1328,22 @@ Video Disk Recorder Revision History  - Removed compiler option '-m486' to make it work on non-Intel platforms. If you    have already started a plugin project, you may want to make sure you remove this    option from your existing Makefile. +- Completely rearranged the recording and replay functions to make them available +  to plugins. +- Replay is now done in a single thread (no more syncing between input and output +  thread necessary). +- It is now possible to record several channels on the same transponder with "budget +  cards". VDR automatically attaches a recording timer to a card that already +  records on the appropriate transponder. How many parallel recordings can actually +  be done depends on the computer's performance. Currently any number of recordings +  gets attached to a card, so you should carefully plan your timers to not exceed +  the limit. On a K6-II/450 it was possible to record three channels from transponder +  12480 with a single WinTV NOVA-S. +- Timers that record two successive shows on the same channel may now overlap and +  will use the same DVB card. During the time where both timers record the data +  is simply saved to both files. +- The following limitations apply to this version: +  + Transfer mode doesn't work yet. +  + The '-a' option (for Dolby Digital audio) doesn't work yet. +  + Switching between different language tracks doesn't work yet. +  + Cutting doesn't work yet. @@ -4,7 +4,7 @@  # See the main source file 'vdr.c' for copyright information and  # how to reach the author.  # -# $Id: Makefile 1.39 2002/06/10 16:26:51 kls Exp $ +# $Id: Makefile 1.40 2002/06/10 16:31:34 kls Exp $  .DELETE_ON_ERROR: @@ -21,9 +21,10 @@ INCLUDES = -I$(DVBDIR)/ost/include  DTVLIB   = $(DTVDIR)/libdtv.a -OBJS = config.o dvbapi.o dvbosd.o eit.o eitscan.o font.o i18n.o interface.o menu.o\ -       menuitems.o osdbase.o osd.o plugin.o recording.o remote.o remux.o ringbuffer.o\ -       status.o svdrp.o thread.o tools.o vdr.o videodir.o +OBJS = audio.o config.o device.o dvbplayer.o dvbosd.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 vdr.o videodir.o  OSDFONT = -adobe-helvetica-medium-r-normal--23-*-100-100-p-*-iso8859-1  FIXFONT = -adobe-courier-bold-r-normal--25-*-100-100-m-*-iso8859-1 diff --git a/PLUGINS.html b/PLUGINS.html index f531f361..a0f7ebc9 100644 --- a/PLUGINS.html +++ b/PLUGINS.html @@ -864,15 +864,15 @@ If a plugin wants to get informed on various events in VDR, it can derive a clas  class cMyStatusMonitor : public cStatusMonitor {  protected: -  virtual void ChannelSwitch(const cDvbApi *DvbApi, int ChannelNumber); +  virtual void ChannelSwitch(const cDevice *Device, int ChannelNumber);    }; -void cMyStatusMonitor::ChannelSwitch(const cDvbApi *DvbApi, int ChannelNumber) +void cMyStatusMonitor::ChannelSwitch(const cDevice *Device, int ChannelNumber)  {    if (ChannelNumber) -     dsyslog("channel switched to %d on DVB %d", ChannelNumber, DvbApi->CardIndex()); +     dsyslog("channel switched to %d on DVB %d", ChannelNumber, Device->CardIndex());    else -     dsyslog("about to switch channel on DVB %d", DvbApi->CardIndex()); +     dsyslog("about to switch channel on DVB %d", Device->CardIndex());  }  </pre></td></tr></table><p> diff --git a/audio.c b/audio.c new file mode 100644 index 00000000..5e7bb73b --- /dev/null +++ b/audio.c @@ -0,0 +1,11 @@ +/* + * audio.c: The basic audio interface + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * $Id: audio.c 1.1 2002/06/10 16:30:00 kls Exp $ + */ + +#include "audio.h" + diff --git a/audio.h b/audio.h new file mode 100644 index 00000000..2541cdcb --- /dev/null +++ b/audio.h @@ -0,0 +1,13 @@ +/* + * audio.h: The basic audio interface + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * $Id: audio.h 1.1 2002/06/10 16:30:00 kls Exp $ + */ + +#ifndef __AUDIO_H +#define __AUDIO_H + +#endif //__AUDIO_H @@ -4,16 +4,16 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: config.c 1.101 2002/05/13 16:28:12 kls Exp $ + * $Id: config.c 1.102 2002/06/16 12:57:31 kls Exp $   */  #include "config.h"  #include <ctype.h>  #include <stdlib.h> -#include "dvbapi.h"  #include "i18n.h"  #include "interface.h"  #include "plugin.h" +#include "recording.h"  // IMPORTANT NOTE: in the 'sscanf()' calls there is a blank after the '%d'  // format characters in order to allow any number of blanks after a numeric @@ -293,15 +293,15 @@ bool cChannel::Save(FILE *f)    return fprintf(f, ToText()) > 0;  } -bool cChannel::Switch(cDvbApi *DvbApi, bool Log) +bool cChannel::Switch(cDevice *Device, bool Log)  { -  if (!DvbApi) -     DvbApi = cDvbApi::PrimaryDvbApi; -  if (!DvbApi->Recording() && !groupSep) { +  if (!Device) +     Device = cDevice::PrimaryDevice(); +  if (!(Device->IsPrimaryDevice() && Device->Receiving()) && !groupSep) {       if (Log)          isyslog("switching to channel %d", number);       for (int i = 3; i--;) { -         switch (DvbApi->SetChannel(number, frequency, polarization, diseqc, srate, vpid, apid1, apid2, dpid1, dpid2, tpid, ca, pnr)) { +         switch (Device->SetChannel(number, frequency, polarization, diseqc, srate, vpid, apid1, tpid, ca, pnr)) {             case scrOk:         return true;             case scrNoTransfer: if (Interface)                                    Interface->Error(tr("Can't start Transfer Mode!")); @@ -312,7 +312,7 @@ bool cChannel::Switch(cDvbApi *DvbApi, bool Log)           }       return false;       } -  if (DvbApi->Recording()) +  if (Device->IsPrimaryDevice() && Device->Receiving())       Interface->Error(tr("Channel locked (recording)!"));    return false;  } @@ -326,7 +326,7 @@ cTimer::cTimer(bool Instant)    startTime = stopTime = 0;    recording = pending = false;    active = Instant ? taActInst : taInactive; -  cChannel *ch = Channels.GetByNumber(cDvbApi::CurrentChannel()); +  cChannel *ch = Channels.GetByNumber(cDevice::CurrentChannel());    channel = ch ? ch->number : 0;    time_t t = time(NULL);    struct tm tm_r; @@ -836,10 +836,10 @@ cChannel *cChannels::GetByServiceID(unsigned short ServiceId)    return NULL;  } -bool cChannels::SwitchTo(int Number, cDvbApi *DvbApi) +bool cChannels::SwitchTo(int Number, cDevice *Device)  {    cChannel *channel = GetByNumber(Number); -  return channel && channel->Switch(DvbApi); +  return channel && channel->Switch(Device);  }  const char *cChannels::GetChannelNameByNumber(int Number) @@ -957,6 +957,7 @@ bool cSetupLine::operator< (const cListObject &ListObject)  bool cSetupLine::Parse(char *s)  { +  //dsyslog("cSetupLine::Parse '%s'", s);//XXX-    char *p = strchr(s, '=');    if (p) {       *p = 0; @@ -974,6 +975,7 @@ bool cSetupLine::Parse(char *s)             }          name = strdup(Name);          value = strdup(Value); +        //dsyslog("cSetupLine::Parse '%s' = '%s'", name, value);//XXX-          return true;          }       } @@ -982,6 +984,7 @@ bool cSetupLine::Parse(char *s)  bool cSetupLine::Save(FILE *f)  { +  //dsyslog("cSetupLine::Save '%s' = '%s'", name, value);//XXX-    return fprintf(f, "%s%s%s = %s\n", plugin ? plugin : "", plugin ? "." : "", name, value) > 0;  } @@ -1095,7 +1098,7 @@ bool cSetup::Load(const char *FileName)  void cSetup::StoreCaCaps(const char *Name)  { -  for (int d = 0; d < MAXDVBAPI; d++) { +  for (int d = 0; d < MAXDEVICES; d++) {        char buffer[MAXPARSEBUFFER];        char *q = buffer;        *buffer = 0; @@ -1115,7 +1118,7 @@ bool cSetup::ParseCaCaps(const char *Value)  {    char *p;    int d = strtol(Value, &p, 10); -  if (d > 0 && d <= MAXDVBAPI) { +  if (d > 0 && d <= MAXDEVICES) {       d--;       int i = 0;       while (p != Value && p && *p) { @@ -4,7 +4,7 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: config.h 1.117 2002/05/14 21:21:53 kls Exp $ + * $Id: config.h 1.118 2002/06/10 16:30:00 kls Exp $   */  #ifndef __CONFIG_H @@ -15,7 +15,7 @@  #include <string.h>  #include <time.h>  #include <unistd.h> -#include "dvbapi.h" +#include "device.h"  #include "eit.h"  #include "tools.h" @@ -118,7 +118,7 @@ public:    const char *ToText(void);    bool Parse(const char *s);    bool Save(FILE *f); -  bool Switch(cDvbApi *DvbApi = NULL, bool Log = true); +  bool Switch(cDevice *Device = NULL, bool Log = true);    };  enum eTimerActive { taInactive = 0, @@ -294,7 +294,7 @@ public:    cChannel *GetByNumber(int Number);    cChannel *GetByServiceID(unsigned short ServiceId);    const char *GetChannelNameByNumber(int Number); -  bool SwitchTo(int Number, cDvbApi *DvbApi = NULL); +  bool SwitchTo(int Number, cDevice *Device = NULL);    int MaxNumber(void) { return maxNumber; }    }; @@ -385,7 +385,7 @@ public:    int MinEventTimeout, MinUserInactivity;    int MultiSpeedMode;    int ShowReplayMode; -  int CaCaps[MAXDVBAPI][MAXCACAPS]; +  int CaCaps[MAXDEVICES][MAXCACAPS];    int CurrentChannel;    int CurrentVolume;    int __EndData__; diff --git a/device.c b/device.c new file mode 100644 index 00000000..8da7fdde --- /dev/null +++ b/device.c @@ -0,0 +1,1017 @@ +/* + * device.c: The basic device interface + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * $Id: device.c 1.1 2002/06/16 12:29:09 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 "player.h" +#include "receiver.h" +#include "status.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: +#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; +cDevice *cDevice::device[MAXDEVICES] = { NULL }; +cDevice *cDevice::primaryDevice = NULL; + +cDevice::cDevice(int n) +{ +  frontendType = FrontendType(-1); // don't know how else to initialize this - there is no FE_UNKNOWN +  siProcessor = NULL; +  cardIndex = n; + +  // 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); + +  // 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; + +  player = NULL; + +  for (int i = 0; i < MAXRECEIVERS; i++) +      receiver[i] = NULL; +  ca = -1; +} + +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) +{ +  if (n < MAXDEVICES) +     useDevice |= (1 << n); +} + +bool cDevice::SetPrimaryDevice(int n) +{ +  n--; +  if (0 <= n && n < numDevices && device[n]) { +     isyslog("setting primary device to %d", n + 1); +     primaryDevice = device[n]; +     return true; +     } +  esyslog("invalid devive number: %d", n + 1); +  return false; +} + +cDevice *cDevice::GetDevice(int Ca, int Priority, int Frequency, int Vpid, bool *ReUse) +{ +  if (ReUse) +     *ReUse = false; +  cDevice *d = NULL; +  int Provides[MAXDEVICES]; +  // Check which devices provide Ca: +  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 +               ) +            ) { +            d = device[i]; +            if (ReUse) +               *ReUse = true; +            break; +            } +         if (Priority > device[i]->Priority() // Priority is high enough to use this device +            && (!d // we don't have a device yet, or... +               || device[i]->Priority() < d->Priority() // ...this one has an even lower Priority +               || (device[i]->Priority() == d->Priority() // ...same Priority... +                  && Provides[i] < Provides[d->CardIndex()] // ...but this one provides fewer Ca values +                  ) +               ) +            ) +            d = device[i]; +         } +      } +  /*XXX+ too complex with multiple recordings per device +  if (!d && Ca > MAXDEVICES) { +     // We didn't find one the easy way, so now we have to try harder: +     int ShiftLevel = -1; +     for (int i = 0; i < numDevices; i++) { +         if (Provides[i]) { // this device is basicly able to do the job, but for some reason we didn't get it above +            int sl = device[i]->CanShift(Ca, Priority); // asks this device to shift its job to another device +            if (sl >= 0 && (ShiftLevel < 0 || sl < ShiftLevel)) { +               d = device[i]; // found one that can be shifted with the fewest number of subsequent shifts +               ShiftLevel = sl; +               } +            } +         } +     } +  XXX*/ +  return d; +} + +void cDevice::SetCaCaps(void) +{ +  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; +         } +      } +  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) +{ +  for (int i = 0; i < numDevices; i++) { +      delete device[i]; +      device[i] = NULL; +      } +  primaryDevice = NULL; +} + +bool cDevice::GrabImage(const char *FileName, bool Jpeg, int Quality, int SizeX, int SizeY) +{ +  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) +{ +  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) + +bool cDevice::AddPid(int Pid, ePidType PidType) +{ +  if (Pid) { +     int n = -1; +     int a = -1; +     for (int i = 0; i < MAXPIDHANDLES; i++) { +         if (pidHandles[i].pid == Pid) +            n = i; +         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 +           PRINTPIDS("A");//XXX+ +           return SetPid(pidHandles[n].fd, PesTypes[n], Pid, DMX_OUT_TS_TAP); +           } +        PRINTPIDS("a");//XXX+ +        return true; +        } +     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 +        n = a; +        } +     else +        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 true; +} + +bool 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; +              } +            PRINTPIDS("D");//XXX+ +            return pidHandles[i].used; +            } +         } +     } +  return false; +} + +bool cDevice::SetPid(int fd, dmxPesType_t PesType, int Pid, dmxOutput_t Output) +{ +  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) +{ +  //XXX+StopTransfer(); +  //XXX+StopReplay(); + +  cStatusMonitor::MsgChannelSwitch(this, 0); + +  // 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); + +  // 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); + +  eSetChannelResult Result = scrOk; + +  // If this DVB card can't receive this channel, let's see if we can +  // use the card that actually can receive it and transfer data from there: + +  if (NeedsTransferMode) { +     cDevice *CaDevice = GetDevice(Ca, 0); +     if (CaDevice && !CaDevice->Receiving()) { +        if ((Result = CaDevice->SetChannel(ChannelNumber, Frequency, Polarization, Diseqc, Srate, Vpid, Apid, Tpid, Ca, Pnr)) == scrOk) { +           //XXX+SetModeReplay(); +           //XXX+transferringFromDevice = CaDevice->StartTransfer(fd_video); +           } +        } +     else +        Result = scrNoTransfer; +     } + +  if (HasDecoder()) { +     CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, false)); +     CHECK(ioctl(fd_video, VIDEO_SET_BLANK, false)); +     } + +  // Start setting system time: + +  if (Result == scrOk && siProcessor) +     siProcessor->SetCurrentTransponder(Frequency); + +  cStatusMonitor::MsgChannelSwitch(this, ChannelNumber); + +  return Result; +} + +bool cDevice::ToggleMute(void) +{ +  int OldVolume = volume; +  mute = !mute; +  SetVolume(0, mute); +  volume = OldVolume; +  return mute; +} + +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)); +     cStatusMonitor::MsgSetVolume(volume, Absolute); +     if (volume > 0) +        mute = false; +     } +} + +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) +{ +  /*XXX+ +  if (replayBuffer && !replayBuffer->Active()) +     StopReplay(); +  return replayBuffer != NULL; +  XXX*/ +  return player != NULL; +} + +bool cDevice::Attach(cPlayer *Player) +{ +  if (Receiving()) { +     esyslog("ERROR: attempt to attach a cPlayer while receiving on device %d - ignored", CardIndex() + 1); +     return false; +     } +  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->Activate(true); +     return true; +     } +  return false; +} + +void cDevice::Detach(cPlayer *Player) +{ +  if (Player && player == Player) { +     player->Activate(false); +     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); +     } +} + +void cDevice::StopReplay(void) +{ +  if (player) { +     Detach(player); +     /*XXX+ +     if (IsPrimaryDevice()) { +        // let's explicitly switch the channel back in case it was in Transfer Mode: +        cChannel *Channel = Channels.GetByNumber(currentChannel); +        if (Channel) { +           Channel->Switch(this, false); +           usleep(100000); // allow driver to sync in case a new replay will start immediately +           } +        } +        XXX*/ +     } +} + +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; +} + +int cDevice::Priority(void) +{ +  if (IsPrimaryDevice() && !Receiving()) +     return Setup.PrimaryLimit - 1; +  int priority = DEFAULTPRIORITY; +  for (int i = 0; i < MAXRECEIVERS; i++) { +      if (receiver[i]) +         priority = max(receiver[i]->priority, priority); +      } +  return priority; +} + +int cDevice::CanShift(int Ca, int Priority, int UsedCards) +{ +  return -1;//XXX+ too complex with multiple recordings per device +  // Test whether a receiving on this DVB device can be shifted to another one +  // in order to perform a new receiving with the given Ca and Priority on this device: +  int ShiftLevel = -1; // default means this device can't be shifted +  if (UsedCards & (1 << CardIndex()) != 0) +     return ShiftLevel; // otherwise we would get into a loop +  if (Receiving()) { +     if (ProvidesCa(Ca) // this device provides the requested Ca +        && (Ca != this->Ca() // the requested Ca is different from the one currently used... +           || Priority > this->Priority())) { // ...or the request comes from a higher priority +        cDevice *d = NULL; +        int Provides[MAXDEVICES]; +        UsedCards |= (1 << CardIndex()); +        for (int i = 0; i < numDevices; i++) { +            if ((Provides[i] = device[i]->ProvidesCa(this->Ca())) != 0) { // this device is basicly able to do the job +               if (device[i] != this) { // it is not _this_ device +                  int sl = device[i]->CanShift(this->Ca(), Priority, UsedCards); // this is the original Priority! +                  if (sl >= 0 && (ShiftLevel < 0 || sl < ShiftLevel)) { +                     d = device[i]; +                     ShiftLevel = sl; +                     } +                  } +               } +            } +        if (ShiftLevel >= 0) +           ShiftLevel++; // adds the device's own shift +        } +     } +  else if (Priority > this->Priority()) +     ShiftLevel = 0; // no shifting necessary, this device can do the job +  return ShiftLevel; +} + +int cDevice::ProvidesCa(int Ca) +{ +  if (Ca == CardIndex() + 1) +     return 1; // exactly _this_ card was requested +  if (Ca && Ca <= MAXDEVICES) +     return 0; // a specific card was requested, but not _this_ one +  int result = Ca ? 0 : 1; // by default every card can provide FTA +  int others = Ca ? 1 : 0; +  for (int i = 0; i < MAXCACAPS; i++) { +      if (caCaps[i]) { +         if (caCaps[i] == Ca) +            result = 1; +         else +            others++; +         } +      } +  return result ? result + others : 0; +} + +bool cDevice::Receiving(void) +{ +  for (int i = 0; i < MAXRECEIVERS; i++) { +      if (receiver[i]) +         return true; +      } +  return false; +} + +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; +     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; +                     } +                  } +               } +            } + +         // Wait for more data to become available: + +         poll(&pfd, 1, 100); + +         //XXX+ put this into the recorder??? or give the receiver a flag whether it wants this? +         if (time(NULL) - t > MAXBROKENTIMEOUT) { +            esyslog("ERROR: video data stream broken on device %d", CardIndex() + 1); +            cThread::EmergencyExit(true); +            t = time(NULL); +            } +         } +     close(fd_dvr); +     } +  else +     LOG_ERROR_STR(dvrFileName); + +  dsyslog("receiver thread ended on device %d (pid=%d)", CardIndex() + 1, getpid()); +} + +bool cDevice::Attach(cReceiver *Receiver) +{ +  //XXX+ check for same transponder??? +  if (!Receiver) +     return false; +  if (Receiver->device == this) +     return true; +  StopReplay(); +  for (int i = 0; i < MAXRECEIVERS; i++) { +      if (!receiver[i]) { +         //siProcessor->SetStatus(false);//XXX+ +         for (int n = 0; n < MAXRECEIVEPIDS; n++) { +             if (Receiver->pids[n]) +                AddPid(Receiver->pids[n]);//XXX+ retval! +             else +                break; +             } +         Receiver->Activate(true); +         Lock(); +         Receiver->device = this; +         receiver[i] = Receiver; +         Unlock(); +         Start(); +         return true; +         } +      } +  esyslog("ERROR: no free receiver slot!"); +  return false; +} + +void cDevice::Detach(cReceiver *Receiver) +{ +  if (!Receiver || Receiver->device != this) +     return; +  bool receiversLeft = false; +  for (int i = 0; i < MAXRECEIVERS; i++) { +      if (receiver[i] == Receiver) { +         Receiver->Activate(false); +         Lock(); +         receiver[i] = NULL; +         Receiver->device = NULL; +         Unlock(); +         for (int n = 0; n < MAXRECEIVEPIDS; n++) { +             if (Receiver->pids[n]) +                DelPid(Receiver->pids[n]); +             else +                break; +             } +         } +      else if (receiver[i]) +         receiversLeft = true; +      } +  if (!receiversLeft) { +     active = false; +     Cancel(3); +     } +} diff --git a/device.h b/device.h new file mode 100644 index 00000000..e598430a --- /dev/null +++ b/device.h @@ -0,0 +1,199 @@ +/* + * device.h: The basic device interface + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * $Id: device.h 1.1 2002/06/10 16:30:00 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" + +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 MAXVOLUME         255 +#define VOLUMEDELTA         5 // used to increase/decrease the volume + +class cPlayer; +class cReceiver; + +class cDevice : cThread { +  friend class cOsd;//XXX +private: +  static int numDevices; +  static int useDevice; +  static cDevice *device[MAXDEVICES]; +  static cDevice *primaryDevice; +public: +  static int NumDevices(void) { return numDevices; } +         // Returns the total number of DVB 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 +         // will be used. +  static bool SetPrimaryDevice(int n); +         // Sets the primary DVB 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. +  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. +         // 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 +         // 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 Shutdown(void); +         // Closes down all DVB devices. +         // Must be called at the end of the program. +private: +  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); +  virtual ~cDevice(); +  bool IsPrimaryDevice(void) { 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 +         // 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, +         // 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; } + +// Channel facilities + +private: +  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); +  static int CurrentChannel(void) { return primaryDevice ? primaryDevice->currentChannel : 0; } +  int Channel(void) { return currentChannel; } + +// PID handle facilities + +private: +  enum ePidType { ptVideo, ptAudio, ptTeletext, ptDolby, ptOther }; +  class cPidHandle { +  public: +    int pid; +    int fd; +    int used; +    cPidHandle(void) { pid = used = 0; fd = -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); + +// Image Grab facilities + +public: +  bool GrabImage(const char *FileName, bool Jpeg = true, int Quality = -1, int SizeX = -1, int SizeY = -1); + +// Video format facilities + +public: +  virtual void SetVideoFormat(videoFormat_t Format); + +// Volume facilities + +private: +  bool mute; +  int volume; +public: +  bool IsMute(void) { return mute; } +  bool ToggleMute(void); +       // Turns the volume off or on and returns the new mute state. +  void SetVolume(int Volume, bool Absolute = false); +       // Sets the volume to the given value, either absolutely or relative to +       // the current volume. +  static int CurrentVolume(void) { return primaryDevice ? primaryDevice->volume : 0; }//XXX??? + +  // EIT facilities + +private: +  cSIProcessor *siProcessor; + +// Player facilities + +private: +  cPlayer *player; +public: +  void TrickSpeed(int Speed); +  void Clear(void); +  void Play(void); +  void Freeze(void); +  void Mute(void); +  void StillPicture(const uchar *Data, int Length); +  bool Replaying(void); +       // Returns true if we are currently replaying. +  void StopReplay(void); +       // Stops the current replay session (if any). +  bool Attach(cPlayer *Player); +  void Detach(cPlayer *Player); +  virtual int PlayVideo(const uchar *Data, int Length); +  virtual int PlayAudio(const uchar *Data, int Length); + +// Receiver facilities + +private: +  cReceiver *receiver[MAXRECEIVERS]; +  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 +      // always return at least Setup.PrimaryLimit-1. +  int CanShift(int Ca, int Priority, int UsedCards = 0); +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 Attach(cReceiver *Receiver); +  void Detach(cReceiver *Receiver); +  }; + +#endif //__DEVICE_H diff --git a/dvbplayer.c b/dvbplayer.c new file mode 100644 index 00000000..10fdf4b5 --- /dev/null +++ b/dvbplayer.c @@ -0,0 +1,733 @@ +/* + * dvbplayer.c: The DVB player + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * $Id: dvbplayer.c 1.1 2002/06/16 10:59:45 kls Exp $ + */ + +#include "dvbplayer.h" +#include <poll.h> +#include "recording.h" +#include "ringbuffer.h" +#include "thread.h" + +// --- ReadFrame ------------------------------------------------------------- + +int ReadFrame(int f, uchar *b, int Length, int Max) +{ +  if (Length == -1) +     Length = Max; // this means we read up to EOF (see cIndex) +  else if (Length > Max) { +     esyslog("ERROR: frame larger than buffer (%d > %d)", Length, Max); +     Length = Max; +     } +  int r = safe_read(f, b, Length); +  if (r < 0) +     LOG_ERROR; +  return r; +} + +// --- cBackTrace ---------------------------------------------------------- + +#define AVG_FRAME_SIZE 15000         // an assumption about the average frame size +#define DVB_BUF_SIZE   (256 * 1024)  // an assumption about the dvb firmware buffer size +#define BACKTRACE_ENTRIES (DVB_BUF_SIZE / AVG_FRAME_SIZE + 20) // how many entries are needed to backtrace buffer contents + +class cBackTrace { +private: +  int index[BACKTRACE_ENTRIES]; +  int length[BACKTRACE_ENTRIES]; +  int pos, num; +public: +  cBackTrace(void); +  void Clear(void); +  void Add(int Index, int Length); +  int Get(bool Forward); +  }; + +cBackTrace::cBackTrace(void) +{ +  Clear(); +} + +void cBackTrace::Clear(void) +{ +  pos = num = 0; +} + +void cBackTrace::Add(int Index, int Length) +{ +  index[pos] = Index; +  length[pos] = Length; +  if (++pos >= BACKTRACE_ENTRIES) +     pos = 0; +  if (num < BACKTRACE_ENTRIES) +     num++; +} + +int cBackTrace::Get(bool Forward) +{ +  int p = pos; +  int n = num; +  int l = DVB_BUF_SIZE + (Forward ? 0 : 256 * 1024); //XXX (256 * 1024) == DVB_BUF_SIZE ??? +  int i = -1; + +  while (n && l > 0) { +        if (--p < 0) +           p = BACKTRACE_ENTRIES - 1; +        i = index[p] - 1; +        l -= length[p]; +        n--;  +        } +  return i; +} + +// --- cDvbPlayer ------------------------------------------------------------ + +//XXX+ also used in recorder.c - find a better place??? +// The size of the array used to buffer video data: +// (must be larger than MINVIDEODATA - see remux.h) +#define VIDEOBUFSIZE  MEGABYTE(1) + +// The maximum size of a single frame: +#define MAXFRAMESIZE  KILOBYTE(192) + +// The number of frames to back up when resuming an interrupted replay session: +#define RESUMEBACKUP (10 * FRAMESPERSEC) + +class cDvbPlayer : public cPlayer, cThread { +private: +  enum ePlayModes { pmPlay, pmPause, pmSlow, pmFast, pmStill }; +  enum ePlayDirs { pdForward, pdBackward }; +  static int Speeds[]; +  cRingBufferFrame *ringBuffer; +  cBackTrace *backTrace; +  cFileName *fileName; +  cIndexFile *index; +  int replayFile; +  bool eof; +  bool active; +  ePlayModes playMode; +  ePlayDirs playDir; +  int trickSpeed; +  int readIndex, writeIndex; +  cFrame *readFrame; +  const cFrame *playFrame; +  void TrickSpeed(int Increment); +  void Empty(void); +  void StripAudioPackets(uchar *b, int Length, uchar Except = 0x00); +  bool NextFile(uchar FileNumber = 0, int FileOffset = -1); +  int Resume(void); +  bool Save(void); +protected: +  virtual void Activate(bool On); +  virtual void Action(void); +public: +  cDvbPlayer(const char *FileName); +  virtual ~cDvbPlayer(); +  bool Active(void) { return active; } +  void Pause(void); +  void Play(void); +  void Forward(void); +  void Backward(void); +  int SkipFrames(int Frames); +  void SkipSeconds(int Seconds); +  void Goto(int Position, bool Still = false); +  void GetIndex(int &Current, int &Total, bool SnapToIFrame = false); +  bool GetReplayMode(bool &Play, bool &Forward, int &Speed); +  }; + +#define MAX_VIDEO_SLOWMOTION 63 // max. arg to pass to VIDEO_SLOWMOTION // TODO is this value correct? +#define NORMAL_SPEED  4 // the index of the '1' entry in the following array +#define MAX_SPEEDS    3 // the offset of the maximum speed from normal speed in either direction +#define SPEED_MULT   12 // the speed multiplier +int cDvbPlayer::Speeds[] = { 0, -2, -4, -8, 1, 2, 4, 12, 0 }; + +cDvbPlayer::cDvbPlayer(const char *FileName) +{ +  ringBuffer = NULL; +  backTrace = NULL; +  index = NULL; +  eof = false; +  active = false; +  playMode = pmPlay; +  playDir = pdForward; +  trickSpeed = NORMAL_SPEED; +  readIndex = writeIndex = -1; +  readFrame = NULL; +  playFrame = NULL; +  isyslog("replay %s", FileName); +  fileName = new cFileName(FileName, false); +  replayFile = fileName->Open(); +  if (replayFile < 0) +     return; +  ringBuffer = new cRingBufferFrame(VIDEOBUFSIZE); +  // Create the index file: +  index = new cIndexFile(FileName, false); +  if (!index) +     esyslog("ERROR: can't allocate index"); +  else if (!index->Ok()) { +     delete index; +     index = NULL; +     } +  backTrace = new cBackTrace; +} + +cDvbPlayer::~cDvbPlayer() +{ +  Detach(); +  Save(); +  delete index; +  delete fileName; +  delete backTrace; +  delete ringBuffer; +} + +void cDvbPlayer::TrickSpeed(int Increment) +{ +  int nts = trickSpeed + Increment; +  if (Speeds[nts] == 1) { +     trickSpeed = nts; +     if (playMode == pmFast) +        Play(); +     else +        Pause(); +     } +  else if (Speeds[nts]) { +     trickSpeed = nts; +     int Mult = (playMode == pmSlow && playDir == pdForward) ? 1 : SPEED_MULT; +     int sp = (Speeds[nts] > 0) ? Mult / Speeds[nts] : -Speeds[nts] * Mult; +     if (sp > MAX_VIDEO_SLOWMOTION) +        sp = MAX_VIDEO_SLOWMOTION; +     DeviceTrickSpeed(sp); +     } +} + +void cDvbPlayer::Empty(void) +{ +  Lock(); +  if ((readIndex = backTrace->Get(playDir == pdForward)) < 0) +     readIndex = writeIndex; +  readFrame = NULL; +  playFrame = NULL; +  ringBuffer->Clear(); +  backTrace->Clear(); +  DeviceClear(); +  Unlock(); +} + +void cDvbPlayer::StripAudioPackets(uchar *b, int Length, uchar Except) +{ +  if (index) { +     for (int i = 0; i < Length - 6; i++) { +         if (b[i] == 0x00 && b[i + 1] == 0x00 && b[i + 2] == 0x01) { +            uchar c = b[i + 3]; +            int l = b[i + 4] * 256 + b[i + 5] + 6; +            switch (c) { +              case 0xBD: // dolby +                   if (Except) +                      ;//XXX+ PlayExternalDolby(&b[i], Length - i); +                   // continue with deleting the data - otherwise it disturbs DVB replay +              case 0xC0 ... 0xC1: // audio +                   if (c == 0xC1) +                      ;//XXX+ canToggleAudioTrack = true; +                   if (!Except || c != Except) { +                      int n = l; +                      for (int j = i; j < Length && n--; j++) +                          b[j] = 0x00; +                      } +                   break; +              case 0xE0 ... 0xEF: // video +                   break; +              default: +                   //esyslog("ERROR: unexpected packet id %02X", c); +                   l = 0; +              } +            if (l) +               i += l - 1; // the loop increments, too! +            } +         /*XXX +         else +            esyslog("ERROR: broken packet header"); +            XXX*/ +         } +     } +} + +bool cDvbPlayer::NextFile(uchar FileNumber, int FileOffset) +{ +  if (FileNumber > 0) +     replayFile = fileName->SetOffset(FileNumber, FileOffset); +  else if (replayFile >= 0 && eof) +     replayFile = fileName->NextFile(); +  eof = false; +  return replayFile >= 0; +} + +int cDvbPlayer::Resume(void) +{ +  if (index) { +     int Index = index->GetResume(); +     if (Index >= 0) { +        uchar FileNumber; +        int FileOffset; +        if (index->Get(Index, &FileNumber, &FileOffset) && NextFile(FileNumber, FileOffset)) +           return Index; +        } +     } +  return -1; +} + +bool cDvbPlayer::Save(void) +{ +  if (index) { +     int Index = writeIndex; +     if (Index >= 0) { +        Index -= RESUMEBACKUP; +        if (Index > 0) +           Index = index->GetNextIFrame(Index, false); +        else +           Index = 0; +        if (Index >= 0) +           return index->StoreResume(Index); +        } +     } +  return false; +} + +void cDvbPlayer::Activate(bool On) +{ +  if (On) { +     if (replayFile >= 0) +        Start(); +     } +  else if (active) { +     active = false; +     Cancel(3); +     } +} + +void cDvbPlayer::Action(void) +{ +  active = true; +  dsyslog("dvbplayer thread started (pid=%d)", getpid()); + +  uchar b[MAXFRAMESIZE]; +  const uchar *p = NULL; +  int pc = 0; + +  pollfd pfd[2]; +  pfd[0].fd = DeviceFileHandle(); +  pfd[0].events = pfd[0].revents = POLLOUT; +  pfd[1].fd = replayFile; +  pfd[1].events = pfd[1].revents = POLLIN; + +  readIndex = Resume(); +  if (readIndex >= 0) +     isyslog("resuming replay at index %d (%s)", readIndex, IndexToHMSF(readIndex, true)); + +  while (active && NextFile()) { +        { +          LOCK_THREAD; + +          // Read the next frame from the file: + +          if (!readFrame && (pfd[1].revents & POLLIN)) { +             if (playMode != pmStill) { +                int r = 0; +                if (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward)) { +                   uchar FileNumber; +                   int FileOffset, Length; +                   int Index = index->GetNextIFrame(readIndex, playDir == pdForward, &FileNumber, &FileOffset, &Length, true); +                   if (Index >= 0) { +                      if (!NextFile(FileNumber, FileOffset)) +                         break; +                      } +                   else { +                      // can't call Play() here, because those functions may only be +                      // called from the foreground thread - and we also don't need +                      // to empty the buffer here +                      DevicePlay(); +                      playMode = pmPlay; +                      playDir = pdForward; +                      continue; +                      } +                   readIndex = Index; +                   r = ReadFrame(replayFile, b, Length, sizeof(b)); +                   // must call StripAudioPackets() here because the buffer is not emptied +                   // when falling back from "fast forward" to "play" (see above) +                   StripAudioPackets(b, r); +                   } +                else if (index) { +                   uchar FileNumber; +                   int FileOffset, Length; +                   readIndex++; +                   if (!(index->Get(readIndex, &FileNumber, &FileOffset, NULL, &Length) && NextFile(FileNumber, FileOffset))) +                      break; +                   r = ReadFrame(replayFile, b, Length, sizeof(b)); +                   } +                else // allows replay even if the index file is missing +                   r = read(replayFile, b, sizeof(b)); +                if (r > 0) +                   readFrame = new cFrame(b, r, ftUnknown, readIndex); +                else if (r == 0) +                   eof = true; +                else if (r < 0 && FATALERRNO) { +                   LOG_ERROR; +                   break; +                   } +                } +             else//XXX +                usleep(1); // this keeps the CPU load low +             } + +          // Store the frame in the buffer: + +          if (readFrame) { +             if (ringBuffer->Put(readFrame)) +                readFrame = NULL; +             } + +          // Get the next frame from the buffer: +         +          if (!playFrame) { +             playFrame = ringBuffer->Get(); +             p = NULL; +             pc = 0; +             } + +          // Play the frame: + +          if (playFrame && (pfd[0].revents & POLLOUT)) { +             if (!p) { +                p = playFrame->Data(); +                pc = playFrame->Count(); +                } +             if (p) { +                int w = PlayVideo(p, pc); +                if (w > 0) { +                   p += w; +                   pc -= w; +                   } +                else if (w < 0 && FATALERRNO) { +                   LOG_ERROR; +                   break; +                   } +                } +             if (pc == 0) { +                writeIndex = playFrame->Index(); +                backTrace->Add(playFrame->Index(), playFrame->Count()); +                ringBuffer->Drop(playFrame); +                playFrame = NULL; +                p = 0; +                } +             } +        } + +        // Wait for input or output to become ready: + +        if (poll(pfd, readFrame ? 1 : 2, 10) < 0 && FATALERRNO) { +           LOG_ERROR; +           break; +           } +        } + +  dsyslog("dvbplayer thread ended (pid=%d)", getpid()); +  active = false; +} + +void cDvbPlayer::Pause(void) +{ +  if (playMode == pmPause || playMode == pmStill) +     Play(); +  else { +     LOCK_THREAD; +     if (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward)) +        Empty(); +     DeviceFreeze(); +     playMode = pmPause; +     } +} + +void cDvbPlayer::Play(void) +{ +  if (playMode != pmPlay) { +     LOCK_THREAD; +     if (playMode == pmStill || playMode == pmFast || (playMode == pmSlow && playDir == pdBackward)) +        Empty(); +     DevicePlay(); +     playMode = pmPlay; +     playDir = pdForward; +    } +} + +void cDvbPlayer::Forward(void) +{ +  if (index) { +     switch (playMode) { +       case pmFast: +            if (Setup.MultiSpeedMode) { +               TrickSpeed(playDir == pdForward ? 1 : -1); +               break; +               } +            else if (playDir == pdForward) { +               Play(); +               break; +               } +            // run into pmPlay +       case pmPlay: { +            LOCK_THREAD; +            Empty(); +            DeviceMute(); +            playMode = pmFast; +            playDir = pdForward; +            trickSpeed = NORMAL_SPEED; +            TrickSpeed(Setup.MultiSpeedMode ? 1 : MAX_SPEEDS); +            } +            break; +       case pmSlow: +            if (Setup.MultiSpeedMode) { +               TrickSpeed(playDir == pdForward ? -1 : 1); +               break; +               } +            else if (playDir == pdForward) { +               Pause(); +               break; +               } +            // run into pmPause +       case pmStill: +       case pmPause: +            DeviceMute(); +            playMode = pmSlow; +            playDir = pdForward; +            trickSpeed = NORMAL_SPEED; +            TrickSpeed(Setup.MultiSpeedMode ? -1 : -MAX_SPEEDS); +            break; +       } +     } +} + +void cDvbPlayer::Backward(void) +{ +  if (index) { +     switch (playMode) { +       case pmFast: +            if (Setup.MultiSpeedMode) { +               TrickSpeed(playDir == pdBackward ? 1 : -1); +               break; +               } +            else if (playDir == pdBackward) { +               Play(); +               break; +               } +            // run into pmPlay +       case pmPlay: { +            LOCK_THREAD; +            Empty(); +            DeviceMute(); +            playMode = pmFast; +            playDir = pdBackward; +            trickSpeed = NORMAL_SPEED; +            TrickSpeed(Setup.MultiSpeedMode ? 1 : MAX_SPEEDS); +            } +            break; +       case pmSlow: +            if (Setup.MultiSpeedMode) { +               TrickSpeed(playDir == pdBackward ? -1 : 1); +               break; +               } +            else if (playDir == pdBackward) { +               Pause(); +               break; +               } +            // run into pmPause +       case pmStill: +       case pmPause: { +            LOCK_THREAD; +            Empty(); +            DeviceMute(); +            playMode = pmSlow; +            playDir = pdBackward; +            trickSpeed = NORMAL_SPEED; +            TrickSpeed(Setup.MultiSpeedMode ? -1 : -MAX_SPEEDS); +            } +            break; +       } +     } +} + +int cDvbPlayer::SkipFrames(int Frames) +{ +  if (index && Frames) { +     int Current, Total; +     GetIndex(Current, Total, true); +     int OldCurrent = Current; +     Current = index->GetNextIFrame(Current + Frames, Frames > 0); +     return Current >= 0 ? Current : OldCurrent; +     } +  return -1; +} + +void cDvbPlayer::SkipSeconds(int Seconds) +{ +  if (index && Seconds) { +     LOCK_THREAD; +     Empty(); +     int Index = writeIndex; +     if (Index >= 0) { +        Index = max(Index + Seconds * FRAMESPERSEC, 0); +        if (Index > 0) +           Index = index->GetNextIFrame(Index, false, NULL, NULL, NULL, true); +        if (Index >= 0) +           readIndex = writeIndex = Index - 1; // Input() will first increment it! +        } +     Play(); +     } +} + +void cDvbPlayer::Goto(int Index, bool Still) +{ +  if (index) { +     LOCK_THREAD; +     Empty(); +     if (++Index <= 0) +        Index = 1; // not '0', to allow GetNextIFrame() below to work! +     uchar FileNumber; +     int FileOffset, Length; +     Index = index->GetNextIFrame(Index, false, &FileNumber, &FileOffset, &Length); +     if (Index >= 0 && NextFile(FileNumber, FileOffset) && Still) { +        uchar b[MAXFRAMESIZE]; +        int r = ReadFrame(replayFile, b, Length, sizeof(b)); +        if (r > 0) { +           if (playMode == pmPause) +              DevicePlay(); +           StripAudioPackets(b, r); +           DeviceStillPicture(b, r); +           } +        playMode = pmStill; +        } +     readIndex = writeIndex = Index; +     } +} + +void cDvbPlayer::GetIndex(int &Current, int &Total, bool SnapToIFrame) +{ +  if (index) { +     if (playMode == pmStill) +        Current = max(readIndex, 0); +     else { +        Current = max(writeIndex, 0); +        if (SnapToIFrame) { +           int i1 = index->GetNextIFrame(Current + 1, false); +           int i2 = index->GetNextIFrame(Current, true); +           Current = (abs(Current - i1) <= abs(Current - i2)) ? i1 : i2; +           } +        } +     Total = index->Last(); +     } +  else +     Current = Total = -1; +} + +bool cDvbPlayer::GetReplayMode(bool &Play, bool &Forward, int &Speed) +{ +  Play = (playMode == pmPlay || playMode == pmFast); +  Forward = (playDir == pdForward); +  if (playMode == pmFast || playMode == pmSlow) +     Speed = Setup.MultiSpeedMode ? abs(trickSpeed - NORMAL_SPEED) : 0; +  else +     Speed = -1; +  return true; +} + +// --- cDvbPlayerControl ----------------------------------------------------- + +cDvbPlayerControl::cDvbPlayerControl(void) +{ +  player = NULL; +} + +cDvbPlayerControl::~cDvbPlayerControl() +{ +  Stop(); +} + +bool cDvbPlayerControl::Active(void) +{ +  return player && player->Active(); +} + +bool cDvbPlayerControl::Start(const char *FileName) +{ +  delete player; +  player = new cDvbPlayer(FileName); +  if (cDevice::PrimaryDevice()->Attach(player)) +     return true; +  Stop(); +  return false; +} + +void cDvbPlayerControl::Stop(void) +{ +  delete player; +  player = NULL; +} + +void cDvbPlayerControl::Pause(void) +{ +  if (player) +     player->Pause(); +} + +void cDvbPlayerControl::Play(void) +{ +  if (player) +     player->Play(); +} + +void cDvbPlayerControl::Forward(void) +{ +  if (player) +     player->Forward(); +} + +void cDvbPlayerControl::Backward(void) +{ +  if (player) +     player->Backward(); +} + +void cDvbPlayerControl::SkipSeconds(int Seconds) +{ +  if (player) +     player->SkipSeconds(Seconds); +} + +int cDvbPlayerControl::SkipFrames(int Frames) +{ +  if (player) +     return player->SkipFrames(Frames); +  return -1; +} + +bool cDvbPlayerControl::GetIndex(int &Current, int &Total, bool SnapToIFrame) +{ +  if (player) { +     player->GetIndex(Current, Total, SnapToIFrame); +     return true; +     } +  return false; +} + +bool cDvbPlayerControl::GetReplayMode(bool &Play, bool &Forward, int &Speed) +{ +  return player && player->GetReplayMode(Play, Forward, Speed); +} + +void cDvbPlayerControl::Goto(int Position, bool Still) +{ +  if (player) +     player->Goto(Position, Still); +} diff --git a/dvbplayer.h b/dvbplayer.h new file mode 100644 index 00000000..b05bcdfa --- /dev/null +++ b/dvbplayer.h @@ -0,0 +1,60 @@ +/* + * dvbplayer.h: The DVB player + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * $Id: dvbplayer.h 1.1 2002/06/16 10:59:14 kls Exp $ + */ + +#ifndef __DVBPLAYER_H +#define __DVBPLAYER_H + +#include "player.h" +#include "thread.h" + +class cDvbPlayer; + +class cDvbPlayerControl : public cControl { +private: +  cDvbPlayer *player; +public: +  cDvbPlayerControl(void); +  virtual ~cDvbPlayerControl(); +  bool Active(void); +  bool Start(const char *FileName); +       // Starts replaying the given file. +  void Stop(void); +       // Stops the current replay session (if any). +  void Pause(void); +       // Pauses the current replay session, or resumes a paused session. +  void Play(void); +       // Resumes normal replay mode. +  void Forward(void); +       // Runs the current replay session forward at a higher speed. +  void Backward(void); +       // Runs the current replay session backwards at a higher speed. +  int  SkipFrames(int Frames); +       // Returns the new index into the current replay session after skipping +       // the given number of frames (no actual repositioning is done!). +       // The sign of 'Frames' determines the direction in which to skip. +  void SkipSeconds(int Seconds); +       // Skips the given number of seconds in the current replay session. +       // The sign of 'Seconds' determines the direction in which to skip. +       // Use a very large negative value to go all the way back to the +       // beginning of the recording. +  bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false); +       // Returns the current and total frame index, optionally snapped to the +       // nearest I-frame. +  bool GetReplayMode(bool &Play, bool &Forward, int &Speed); +       // Returns the current replay mode (if applicable). +       // 'Play' tells whether we are playing or pausing, 'Forward' tells whether +       // we are going forward or backward and 'Speed' is -1 if this is normal +       // play/pause mode, 0 if it is single speed fast/slow forward/back mode +       // and >0 if this is multi speed mode. +  void Goto(int Index, bool Still = false); +       // Positions to the given index and displays that frame as a still picture +       // if Still is true. +  }; + +#endif //__DVBPLAYER_H @@ -16,7 +16,7 @@   *   the Free Software Foundation; either version 2 of the License, or     *   *   (at your option) any later version.                                   *   *                                                                         * - * $Id: eit.c 1.45 2002/05/13 16:35:49 kls Exp $ + * $Id: eit.c 1.46 2002/06/10 16:30:00 kls Exp $   ***************************************************************************/  #include "eit.h" @@ -26,6 +26,7 @@  #include <iomanip.h>  #include <iostream.h>  #include <limits.h> +#include <ost/dmx.h>  #include <stdio.h>  #include <stdlib.h>  #include <string.h> @@ -4,7 +4,7 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: eitscan.c 1.1 2002/05/20 10:58:10 kls Exp $ + * $Id: eitscan.c 1.2 2002/06/10 16:30:00 kls Exp $   */  #include "eitscan.h" @@ -48,11 +48,11 @@ void cEITScanner::Process(void)    if (Setup.EPGScanTimeout && Channels.MaxNumber() > 1) {       time_t now = time(NULL);       if (now - lastScan > ScanTimeout && now - lastActivity > ActivityTimeout) { -        for (int i = 0; i < MAXDVBAPI; i++) { -            cDvbApi *DvbApi = cDvbApi::GetDvbApi(i + 1, MAXPRIORITY + 1); -            if (DvbApi) { -               if (DvbApi != cDvbApi::PrimaryDvbApi || (cDvbApi::NumDvbApis == 1 && Setup.EPGScanTimeout && now - lastActivity > Setup.EPGScanTimeout * 3600)) { -                  if (!(DvbApi->Recording() || DvbApi->Replaying() || DvbApi->Transferring())) { +        for (int i = 0; i < MAXDEVICES; i++) { +            cDevice *Device = cDevice::GetDevice(i + 1, MAXPRIORITY + 1); +            if (Device) { +               if (Device != cDevice::PrimaryDevice() || (cDevice::NumDevices() == 1 && Setup.EPGScanTimeout && now - lastActivity > Setup.EPGScanTimeout * 3600)) { +                  if (!(Device->Receiving() || Device->Replaying()/*XXX+ || Device->Transferring()XXX*/)) {                       int oldCh = lastChannel;                       int ch = oldCh + 1;                       while (ch != oldCh) { @@ -62,12 +62,12 @@ void cEITScanner::Process(void)                                }                             cChannel *Channel = Channels.GetByNumber(ch);                             if (Channel) { -                              if (Channel->ca <= MAXDVBAPI && !DvbApi->ProvidesCa(Channel->ca)) +                              if (Channel->ca <= MAXDEVICES && !Device->ProvidesCa(Channel->ca))                                   break; // the channel says it explicitly needs a different card                                if (Channel->pnr && !TransponderScanned(Channel)) { -                                 if (DvbApi == cDvbApi::PrimaryDvbApi && !currentChannel) -                                    currentChannel = DvbApi->Channel(); -                                 Channel->Switch(DvbApi, false); +                                 if (Device == cDevice::PrimaryDevice() && !currentChannel) +                                    currentChannel = Device->Channel(); +                                 Channel->Switch(Device, false);                                   lastChannel = ch;                                   break;                                   } diff --git a/interface.c b/interface.c index 9c2e918d..ed9d0b1e 100644 --- a/interface.c +++ b/interface.c @@ -4,7 +4,7 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: interface.c 1.50 2002/05/19 12:02:57 kls Exp $ + * $Id: interface.c 1.51 2002/06/10 16:30:00 kls Exp $   */  #include "interface.h" @@ -484,6 +484,6 @@ void cInterface::DisplayRecording(int Index, bool On)  bool cInterface::Recording(void)  { -  // This is located here because the Interface has to do with the "PrimaryDvbApi" anyway -  return cDvbApi::PrimaryDvbApi->Recording(); +  // This is located here because the Interface has to do with the "PrimaryDevice" anyway +  return cDevice::PrimaryDevice()->Receiving();  } @@ -4,7 +4,7 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: menu.c 1.195 2002/05/19 14:55:22 kls Exp $ + * $Id: menu.c 1.196 2002/06/16 12:11:02 kls Exp $   */  #include "menu.h" @@ -18,6 +18,7 @@  #include "i18n.h"  #include "menuitems.h"  #include "plugin.h" +#include "recording.h"  #include "status.h"  #include "videodir.h" @@ -25,6 +26,7 @@  #define MAXWAIT4EPGINFO  10 // seconds  #define MODETIMEOUT       3 // seconds +#define MAXRECORDCONTROLS (MAXDEVICES * MAXRECEIVERS)  #define MAXINSTANTRECTIME (24 * 60 - 1) // 23:59 hours  #define CHNUMWIDTH  (Channels.Count() > 999 ? 5 : 4) // there are people with more than 999 channels... @@ -376,7 +378,7 @@ eOSState cMenuEditCaItem::ProcessKey(eKeys Key)             }          }       else if (NORMALKEY(Key) == kRight) { -        if (ca && ca->Next() && (allowCardNr || ((cCaDefinition *)ca->Next())->Number() > MAXDVBAPI)) { +        if (ca && ca->Next() && (allowCardNr || ((cCaDefinition *)ca->Next())->Number() > MAXDEVICES)) {             ca = (cCaDefinition *)ca->Next();             *value = ca->Number();             } @@ -494,7 +496,7 @@ cMenuChannels::cMenuChannels(void)    //TODO    int i = 0;    cChannel *channel; -  int curr = ((channel = Channels.GetByNumber(cDvbApi::CurrentChannel())) != NULL) ? channel->Index() : -1; +  int curr = ((channel = Channels.GetByNumber(cDevice::CurrentChannel())) != NULL) ? channel->Index() : -1;    while ((channel = Channels.Get(i)) != NULL) {          Add(new cMenuChannelItem(i, channel), i == curr); @@ -1145,7 +1147,7 @@ cMenuSchedule::cMenuSchedule(void)  {    now = next = false;    otherChannel = 0; -  cChannel *channel = Channels.GetByNumber(cDvbApi::CurrentChannel()); +  cChannel *channel = Channels.GetByNumber(cDevice::CurrentChannel());    if (channel) {       cMenuWhatsOn::SetCurrentChannel(channel->number);       schedules = cSIProcessor::Schedules(mutexLock); @@ -1264,7 +1266,7 @@ eOSState cMenuSchedule::ProcessKey(eKeys Key)          cChannel *channel = Channels.GetByServiceID(ei->GetServiceID());          if (channel) {             PrepareSchedule(channel); -           if (channel->number != cDvbApi::CurrentChannel()) { +           if (channel->number != cDevice::CurrentChannel()) {                otherChannel = channel->number;                SetHelp(tr("Record"), tr("Now"), tr("Next"), tr("Switch"));                } @@ -1441,7 +1443,7 @@ eOSState cMenuRecordings::Rewind(void)  {    cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current());    if (ri && !ri->IsDirectory()) { -     cDvbApi::PrimaryDvbApi->StopReplay(); // must do this first to be able to rewind the currently replayed recording +     cDevice::PrimaryDevice()->StopReplay(); // must do this first to be able to rewind the currently replayed recording       cResumeFile ResumeFile(ri->FileName());       ResumeFile.Delete();       return Play(); @@ -1614,7 +1616,7 @@ public:  cMenuSetupDVB::cMenuSetupDVB(void)  {    SetSection(tr("DVB")); -  Add(new cMenuEditIntItem( tr("Setup.DVB$Primary DVB interface"), &data.PrimaryDVB, 1, cDvbApi::NumDvbApis)); +  Add(new cMenuEditIntItem( tr("Setup.DVB$Primary DVB interface"), &data.PrimaryDVB, 1, cDevice::NumDevices()));    Add(new cMenuEditBoolItem(tr("Setup.DVB$Video format"),          &data.VideoFormat, "4:3", "16:9"));  } @@ -1626,7 +1628,7 @@ eOSState cMenuSetupDVB::ProcessKey(eKeys Key)    if (state == osBack && Key == kOk) {       if (Setup.PrimaryDVB != oldPrimaryDVB) {          state = osSwitchDvb; -        cDvbApi::PrimaryDvbApi->SetVideoFormat(Setup.VideoFormat ? VIDEO_FORMAT_16_9 : VIDEO_FORMAT_4_3); +        cDevice::PrimaryDevice()->SetVideoFormat(Setup.VideoFormat ? VIDEO_FORMAT_16_9 : VIDEO_FORMAT_4_3);          }       }    return state; @@ -1659,7 +1661,7 @@ public:  cMenuSetupCICAM::cMenuSetupCICAM(void)  {    SetSection(tr("CICAM")); -  for (int d = 0; d < cDvbApi::NumDvbApis; d++) { +  for (int d = 0; d < cDevice::NumDevices(); d++) {        for (int i = 0; i < 2; i++) {            char buffer[32];            snprintf(buffer, sizeof(buffer), "%s%d %d", tr("Setup.CICAM$CICAM DVB"), d + 1, i + 1); @@ -1673,7 +1675,7 @@ eOSState cMenuSetupCICAM::ProcessKey(eKeys Key)    eOSState state = cMenuSetupBase::ProcessKey(Key);    if (state == osBack && Key == kOk) -     cDvbApi::SetCaCaps(); +     cDevice::SetCaCaps();    return state;  } @@ -2024,12 +2026,14 @@ void cMenuMain::Set(void)    // Editing control: +  /*XXX+    if (cVideoCutter::Active())       Add(new cOsdItem(tr(" Cancel editing"), osCancelEdit)); +     XXX*/    // Color buttons: -  SetHelp(tr("Record"), cDvbApi::PrimaryDvbApi->CanToggleAudioTrack() ? tr("Language") : NULL, NULL, replaying ? tr("Button$Stop") : cReplayControl::LastReplayed() ? tr("Resume") : NULL); +  SetHelp(tr("Record"), /*XXX+ cDevice::PrimaryDevice()->CanToggleAudioTrack() ? tr("Language") :XXX*/ NULL, NULL, replaying ? tr("Button$Stop") : cReplayControl::LastReplayed() ? tr("Resume") : NULL);    Display();    lastActivity = time(NULL);  } @@ -2059,7 +2063,7 @@ eOSState cMenuMain::ProcessKey(eKeys Key)                            }                         break;      case osCancelEdit: if (Interface->Confirm(tr("Cancel editing?"))) { -                          cVideoCutter::Stop(); +                          //XXX+cVideoCutter::Stop();                            return osEnd;                            }                         break; @@ -2082,11 +2086,13 @@ eOSState cMenuMain::ProcessKey(eKeys Key)                                  state = osRecord;                               break;                 case kGreen:  if (!HasSubMenu()) { -                                if (cDvbApi::PrimaryDvbApi->CanToggleAudioTrack()) { +                                /*XXX+ +                                if (cDevice::PrimaryDevice()->CanToggleAudioTrack()) {                                     Interface->Clear(); -                                   cDvbApi::PrimaryDvbApi->ToggleAudioTrack(); +                                   cDevice::PrimaryDevice()->ToggleAudioTrack();                                     state = osEnd;                                     } +                                   XXX*/                                  }                               break;                 case kBlue:   if (!HasSubMenu()) @@ -2134,7 +2140,7 @@ cDisplayChannel::cDisplayChannel(eKeys FirstKey)  :cOsdObject(true)  {    group = -1; -  oldNumber = cDvbApi::CurrentChannel(); +  oldNumber = cDevice::CurrentChannel();    number = 0;    lastTime = time_ms();    int EpgLines = Setup.ShowInfoOnChSwitch ? 5 : 1; @@ -2253,7 +2259,7 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key)      case kRight:           withInfo = false;           if (group < 0) { -            cChannel *channel = Channels.GetByNumber(cDvbApi::CurrentChannel()); +            cChannel *channel = Channels.GetByNumber(cDevice::CurrentChannel());              if (channel)                 group = channel->Index();              } @@ -2328,7 +2334,7 @@ cDisplayVolume::cDisplayVolume(void)  :cOsdObject(true)  {    displayVolume = this; -  timeout = time_ms() + (cDvbApi::PrimaryDvbApi->IsMute() ? MUTETIMEOUT : VOLUMETIMEOUT); +  timeout = time_ms() + (cDevice::PrimaryDevice()->IsMute() ? MUTETIMEOUT : VOLUMETIMEOUT);    Interface->Open(Setup.OSDwidth, -1);    Show();  } @@ -2341,13 +2347,13 @@ cDisplayVolume::~cDisplayVolume()  void cDisplayVolume::Show(void)  { -  cDvbApi *dvbApi = cDvbApi::PrimaryDvbApi; -  if (dvbApi->IsMute()) { +  cDevice *device = cDevice::PrimaryDevice(); +  if (device->IsMute()) {       Interface->Fill(0, 0, Width(), 1, clrTransparent);       Interface->Write(0, 0, tr("Mute"), clrGreen);       }    else { -     int Current = cDvbApi::CurrentVolume(); +     int Current = cDevice::CurrentVolume();       int Total = MAXVOLUME;       const char *Prompt = tr("Volume ");  #ifdef DEBUG_OSD @@ -2387,7 +2393,7 @@ eOSState cDisplayVolume::ProcessKey(eKeys Key)           timeout = time_ms() + VOLUMETIMEOUT;           break;      case kMute: -         if (cDvbApi::PrimaryDvbApi->IsMute()) { +         if (cDevice::PrimaryDevice()->IsMute()) {              Show();              timeout = time_ms() + MUTETIMEOUT;              } @@ -2405,43 +2411,45 @@ eOSState cDisplayVolume::ProcessKey(eKeys Key)  // --- cRecordControl -------------------------------------------------------- -cRecordControl::cRecordControl(cDvbApi *DvbApi, cTimer *Timer) +cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer)  {    eventInfo = NULL;    instantId = NULL;    fileName = NULL; -  dvbApi = DvbApi; -  if (!dvbApi) dvbApi = cDvbApi::PrimaryDvbApi;//XXX +  recorder = NULL; +  device = Device; +  if (!device) device = cDevice::PrimaryDevice();//XXX    timer = Timer;    if (!timer) {       timer = new cTimer(true);       Timers.Add(timer);       Timers.Save(); -     asprintf(&instantId, cDvbApi::NumDvbApis > 1 ? "%s - %d" : "%s", Channels.GetChannelNameByNumber(timer->channel), dvbApi->CardIndex() + 1); +     asprintf(&instantId, cDevice::NumDevices() > 1 ? "%s - %d" : "%s", Channels.GetChannelNameByNumber(timer->channel), device->CardIndex() + 1);       }    timer->SetPending(true);    timer->SetRecording(true); -  if (Channels.SwitchTo(timer->channel, dvbApi)) { -     const char *Title = NULL; -     const char *Subtitle = NULL; -     const char *Summary = NULL; -     if (GetEventInfo()) { -        Title = eventInfo->GetTitle(); -        Subtitle = eventInfo->GetSubtitle(); -        Summary = eventInfo->GetExtendedDescription(); -        dsyslog("Title: '%s' Subtitle: '%s'", Title, Subtitle); -        } -     cRecording Recording(timer, Title, Subtitle, Summary); -     fileName = strdup(Recording.FileName()); -     cRecordingUserCommand::InvokeCommand(RUC_BEFORERECORDING, fileName); -     if (dvbApi->StartRecord(fileName, Channels.GetByNumber(timer->channel)->ca, timer->priority)) { -        Recording.WriteSummary(); -        cStatusMonitor::MsgRecording(dvbApi, fileName); -        } -     Interface->DisplayRecording(dvbApi->CardIndex(), true); + +  const char *Title = NULL; +  const char *Subtitle = NULL; +  const char *Summary = NULL; +  if (GetEventInfo()) { +     Title = eventInfo->GetTitle(); +     Subtitle = eventInfo->GetSubtitle(); +     Summary = eventInfo->GetExtendedDescription(); +     dsyslog("Title: '%s' Subtitle: '%s'", Title, Subtitle); +     } +  cRecording Recording(timer, Title, Subtitle, Summary); +  fileName = strdup(Recording.FileName()); +  cRecordingUserCommand::InvokeCommand(RUC_BEFORERECORDING, fileName); +  cChannel *ch = Channels.GetByNumber(timer->channel); +  recorder = new cRecorder(fileName, ch->ca, timer->priority, ch->vpid, ch->apid1, ch->apid2, ch->dpid1, ch->dpid2); +  if (device->Attach(recorder)) { +     Recording.WriteSummary(); +     cStatusMonitor::MsgRecording(device, fileName); +     Interface->DisplayRecording(device->CardIndex(), true);       }    else -     cThread::EmergencyExit(true); +     DELETENULL(recorder);  }  cRecordControl::~cRecordControl() @@ -2484,8 +2492,8 @@ bool cRecordControl::GetEventInfo(void)  void cRecordControl::Stop(bool KeepInstant)  {    if (timer) { -     cStatusMonitor::MsgRecording(dvbApi, NULL); -     dvbApi->StopRecord(); +     cStatusMonitor::MsgRecording(device, NULL); +     DELETENULL(recorder);       timer->SetRecording(false);       if ((IsInstant() && !KeepInstant) || (timer->IsSingleEvent() && !timer->Matches())) {          // checking timer->Matches() to make sure we don't delete the timer @@ -2495,14 +2503,14 @@ void cRecordControl::Stop(bool KeepInstant)          Timers.Save();          }       timer = NULL; -     Interface->DisplayRecording(dvbApi->CardIndex(), false); +     Interface->DisplayRecording(device->CardIndex(), false);       cRecordingUserCommand::InvokeCommand(RUC_AFTERRECORDING, fileName);       }  }  bool cRecordControl::Process(time_t t)  { -  if (!timer || !timer->Matches(t)) +  if (!recorder || !timer || !timer->Matches(t))       return false;    AssertFreeDiskSpace(timer->priority);    return true; @@ -2510,20 +2518,27 @@ bool cRecordControl::Process(time_t t)  // --- cRecordControls ------------------------------------------------------- -cRecordControl *cRecordControls::RecordControls[MAXDVBAPI] = { NULL }; +cRecordControl *cRecordControls::RecordControls[MAXRECORDCONTROLS] = { NULL };  bool cRecordControls::Start(cTimer *Timer)  { -  int ch = Timer ? Timer->channel : cDvbApi::CurrentChannel(); +  int ch = Timer ? Timer->channel : cDevice::CurrentChannel();    cChannel *channel = Channels.GetByNumber(ch);    if (channel) { -     cDvbApi *dvbApi = cDvbApi::GetDvbApi(channel->ca, Timer ? Timer->priority : Setup.DefaultPriority); -     if (dvbApi) { -        Stop(dvbApi); -        for (int i = 0; i < MAXDVBAPI; i++) { +     bool ReUse = false; +     cDevice *device = cDevice::GetDevice(channel->ca, Timer ? Timer->priority : Setup.DefaultPriority, channel->frequency, channel->vpid, &ReUse); +     if (device) { +        if (!ReUse) { +           Stop(device); +           if (!channel->Switch(device)) { +              cThread::EmergencyExit(true); +              return false; +              } +           } +        for (int i = 0; i < MAXRECORDCONTROLS; i++) {              if (!RecordControls[i]) { -               RecordControls[i] = new cRecordControl(dvbApi, Timer); +               RecordControls[i] = new cRecordControl(device, Timer);                 return true;                 }              } @@ -2538,7 +2553,7 @@ bool cRecordControls::Start(cTimer *Timer)  void cRecordControls::Stop(const char *InstantId)  { -  for (int i = 0; i < MAXDVBAPI; i++) { +  for (int i = 0; i < MAXRECORDCONTROLS; i++) {        if (RecordControls[i]) {           const char *id = RecordControls[i]->InstantId();           if (id && strcmp(id, InstantId) == 0) @@ -2547,12 +2562,12 @@ void cRecordControls::Stop(const char *InstantId)        }  } -void cRecordControls::Stop(cDvbApi *DvbApi) +void cRecordControls::Stop(cDevice *Device)  { -  for (int i = 0; i < MAXDVBAPI; i++) { +  for (int i = 0; i < MAXRECORDCONTROLS; i++) {        if (RecordControls[i]) { -         if (RecordControls[i]->Uses(DvbApi)) { -            isyslog("stopping recording on DVB device %d due to higher priority", DvbApi->CardIndex() + 1); +         if (RecordControls[i]->Uses(Device)) { +            isyslog("stopping recording on DVB device %d due to higher priority", Device->CardIndex() + 1);              RecordControls[i]->Stop(true);              }           } @@ -2561,11 +2576,11 @@ void cRecordControls::Stop(cDvbApi *DvbApi)  bool cRecordControls::StopPrimary(bool DoIt)  { -  if (cDvbApi::PrimaryDvbApi->Recording()) { -     cDvbApi *dvbApi = cDvbApi::GetDvbApi(cDvbApi::PrimaryDvbApi->Ca(), 0); -     if (dvbApi) { +  if (cDevice::PrimaryDevice()->Receiving()) { +     cDevice *device = cDevice::GetDevice(cDevice::PrimaryDevice()->Ca(), 0); +     if (device) {          if (DoIt) -           Stop(cDvbApi::PrimaryDvbApi); +           Stop(cDevice::PrimaryDevice());          return true;          }       } @@ -2574,7 +2589,7 @@ bool cRecordControls::StopPrimary(bool DoIt)  const char *cRecordControls::GetInstantId(const char *LastInstantId)  { -  for (int i = 0; i < MAXDVBAPI; i++) { +  for (int i = 0; i < MAXRECORDCONTROLS; i++) {        if (RecordControls[i]) {           if (!LastInstantId && RecordControls[i]->InstantId())              return RecordControls[i]->InstantId(); @@ -2587,7 +2602,7 @@ const char *cRecordControls::GetInstantId(const char *LastInstantId)  cRecordControl *cRecordControls::GetRecordControl(const char *FileName)  { -  for (int i = 0; i < MAXDVBAPI; i++) { +  for (int i = 0; i < MAXRECORDCONTROLS; i++) {        if (RecordControls[i] && strcmp(RecordControls[i]->FileName(), FileName) == 0)           return RecordControls[i];        } @@ -2596,7 +2611,7 @@ cRecordControl *cRecordControls::GetRecordControl(const char *FileName)  void cRecordControls::Process(time_t t)  { -  for (int i = 0; i < MAXDVBAPI; i++) { +  for (int i = 0; i < MAXRECORDCONTROLS; i++) {        if (RecordControls[i]) {           if (!RecordControls[i]->Process(t))              DELETENULL(RecordControls[i]); @@ -2606,13 +2621,19 @@ void cRecordControls::Process(time_t t)  bool cRecordControls::Active(void)  { -  for (int i = 0; i < MAXDVBAPI; i++) { +  for (int i = 0; i < MAXRECORDCONTROLS; i++) {        if (RecordControls[i])           return true;        }    return false;  } +void cRecordControls::Shutdown(void) +{ +  for (int i = 0; i < MAXRECORDCONTROLS; i++) +      DELETENULL(RecordControls[i]); +} +  // --- cProgressBar ----------------------------------------------------------  class cProgressBar : public cBitmap { @@ -2664,25 +2685,24 @@ char *cReplayControl::title = NULL;  cReplayControl::cReplayControl(void)  { -  dvbApi = cDvbApi::PrimaryDvbApi;    visible = modeOnly = shown = displayFrames = false;    lastCurrent = lastTotal = -1;    timeoutShow = 0;    timeSearchActive = false;    if (fileName) {       marks.Load(fileName); -     if (!dvbApi->StartReplay(fileName)) -        Interface->Error(tr("Channel locked (recording)!")); +     if (!Start(fileName)) +        Interface->Error(tr("Channel locked (recording)!"));//XXX+       else -        cStatusMonitor::MsgReplaying(dvbApi, fileName); +        cStatusMonitor::MsgReplaying(this, fileName);       }  }  cReplayControl::~cReplayControl()  {    Hide(); -  cStatusMonitor::MsgReplaying(dvbApi, NULL); -  dvbApi->StopReplay(); +  cStatusMonitor::MsgReplaying(this, NULL); +  Stop();  }  void cReplayControl::SetRecording(const char *FileName, const char *Title) @@ -2744,7 +2764,7 @@ void cReplayControl::ShowMode(void)    if (Setup.ShowReplayMode && !timeSearchActive) {       bool Play, Forward;       int Speed; -     if (dvbApi->GetReplayMode(Play, Forward, Speed)) { +     if (GetReplayMode(Play, Forward, Speed)) {          bool NormalPlay = (Play && Speed == -1);          if (!visible) { @@ -2782,7 +2802,7 @@ bool cReplayControl::ShowProgress(bool Initial)  {    int Current, Total; -  if (dvbApi->GetIndex(Current, Total) && Total > 0) { +  if (GetIndex(Current, Total) && Total > 0) {       if (!visible) {          Interface->Open(Setup.OSDwidth, -3);          needsFastResponse = visible = true; @@ -2857,14 +2877,14 @@ void cReplayControl::TimeSearchProcess(eKeys Key)           int dir = (Key == kRight ? 1 : -1);           if (dir > 0)              Seconds = min(Total - Current - STAY_SECONDS_OFF_END, Seconds); -         dvbApi->SkipSeconds(Seconds * dir); +         SkipSeconds(Seconds * dir);           timeSearchActive = false;           }           break;      case kUp:      case kDown:           Seconds = min(Total - STAY_SECONDS_OFF_END, Seconds); -         dvbApi->Goto(Seconds * FRAMESPERSEC, Key == kDown); +         Goto(Seconds * FRAMESPERSEC, Key == kDown);           timeSearchActive = false;           break;      default: @@ -2902,7 +2922,7 @@ void cReplayControl::TimeSearch(void)  void cReplayControl::MarkToggle(void)  {    int Current, Total; -  if (dvbApi->GetIndex(Current, Total, true)) { +  if (GetIndex(Current, Total, true)) {       cMark *m = marks.Get(Current);       lastCurrent = -1; // triggers redisplay       if (m) @@ -2919,10 +2939,10 @@ void cReplayControl::MarkJump(bool Forward)  {    if (marks.Count()) {       int Current, Total; -     if (dvbApi->GetIndex(Current, Total)) { +     if (GetIndex(Current, Total)) {          cMark *m = Forward ? marks.GetNext(Current) : marks.GetPrev(Current);          if (m) -           dvbApi->Goto(m->position, true); +           Goto(m->position, true);          }       displayFrames = true;       } @@ -2931,11 +2951,11 @@ void cReplayControl::MarkJump(bool Forward)  void cReplayControl::MarkMove(bool Forward)  {    int Current, Total; -  if (dvbApi->GetIndex(Current, Total)) { +  if (GetIndex(Current, Total)) {       cMark *m = marks.Get(Current);       if (m) {          displayFrames = true; -        int p = dvbApi->SkipFrames(Forward ? 1 : -1); +        int p = SkipFrames(Forward ? 1 : -1);          cMark *m2;          if (Forward) {             if ((m2 = marks.Next(m)) != NULL && m2->position <= p) @@ -2945,7 +2965,7 @@ void cReplayControl::MarkMove(bool Forward)             if ((m2 = marks.Prev(m)) != NULL && m2->position >= p)                return;             } -        dvbApi->Goto(m->position = p, true); +        Goto(m->position = p, true);          marks.Save();          }       } @@ -2953,6 +2973,7 @@ void cReplayControl::MarkMove(bool Forward)  void cReplayControl::EditCut(void)  { +  /*XXX+    if (fileName) {       Hide();       if (!cVideoCutter::Active()) { @@ -2965,12 +2986,13 @@ void cReplayControl::EditCut(void)          Interface->Error(tr("Editing process already active!"));       ShowMode();       } +     XXX*/  }  void cReplayControl::EditTest(void)  {    int Current, Total; -  if (dvbApi->GetIndex(Current, Total)) { +  if (GetIndex(Current, Total)) {       cMark *m = marks.Get(Current);       if (!m)          m = marks.GetNext(Current); @@ -2978,8 +3000,8 @@ void cReplayControl::EditTest(void)          if ((m->Index() & 0x01) != 0)             m = marks.Next(m);          if (m) { -           dvbApi->Goto(m->position - dvbApi->SecondsToFrames(3)); -           dvbApi->Play(); +           Goto(m->position - SecondsToFrames(3)); +           Play();             }          }       } @@ -2987,7 +3009,7 @@ void cReplayControl::EditTest(void)  eOSState cReplayControl::ProcessKey(eKeys Key)  { -  if (!dvbApi->Replaying()) +  if (!Active())       return osEnd;    if (visible) {       if (timeoutShow && time(NULL) > timeoutShow) { @@ -3009,21 +3031,21 @@ eOSState cReplayControl::ProcessKey(eKeys Key)    bool DoShowMode = true;    switch (Key) {      // Positioning: -    case kUp:      dvbApi->Play(); break; -    case kDown:    dvbApi->Pause(); break; +    case kUp:      Play(); break; +    case kDown:    Pause(); break;      case kLeft|k_Release:                     if (Setup.MultiSpeedMode) break; -    case kLeft:    dvbApi->Backward(); break; +    case kLeft:    Backward(); break;      case kRight|k_Release:                     if (Setup.MultiSpeedMode) break; -    case kRight:   dvbApi->Forward(); break; +    case kRight:   Forward(); break;      case kRed:     TimeSearch(); break;      case kGreen|k_Repeat: -    case kGreen:   dvbApi->SkipSeconds(-60); break; +    case kGreen:   SkipSeconds(-60); break;      case kYellow|k_Repeat: -    case kYellow:  dvbApi->SkipSeconds( 60); break; +    case kYellow:  SkipSeconds( 60); break;      case kBlue:    Hide(); -                   dvbApi->StopReplay(); +                   Stop();                     return osEnd;      default: {        DoShowMode = false; @@ -4,14 +4,16 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: menu.h 1.43 2002/05/18 12:36:06 kls Exp $ + * $Id: menu.h 1.44 2002/06/14 12:33:35 kls Exp $   */  #ifndef __MENU_H  #define __MENU_H -#include "dvbapi.h" +#include "device.h"  #include "osd.h" +#include "dvbplayer.h" +#include "recorder.h"  #include "recording.h"  class cMenuMain : public cOsdMenu { @@ -76,17 +78,18 @@ public:  class cRecordControl {  private: -  cDvbApi *dvbApi; +  cDevice *device;    cTimer *timer; +  cRecorder *recorder;    const cEventInfo *eventInfo;    char *instantId;    char *fileName;    bool GetEventInfo(void);  public: -  cRecordControl(cDvbApi *DvbApi, cTimer *Timer = NULL); +  cRecordControl(cDevice *Device, cTimer *Timer = NULL);    virtual ~cRecordControl();    bool Process(time_t t); -  bool Uses(cDvbApi *DvbApi) { return DvbApi == dvbApi; } +  bool Uses(cDevice *Device) { return Device == device; }    void Stop(bool KeepInstant = false);    bool IsInstant(void) { return instantId; }    const char *InstantId(void) { return instantId; } @@ -96,21 +99,21 @@ public:  class cRecordControls {  private: -  static cRecordControl *RecordControls[MAXDVBAPI]; +  static cRecordControl *RecordControls[];  public:    static bool Start(cTimer *Timer = NULL);    static void Stop(const char *InstantId); -  static void Stop(cDvbApi *DvbApi); +  static void Stop(cDevice *Device);    static bool StopPrimary(bool DoIt = false);    static const char *GetInstantId(const char *LastInstantId);    static cRecordControl *GetRecordControl(const char *FileName);    static void Process(time_t t);    static bool Active(void); +  static void Shutdown(void);    }; -class cReplayControl : public cOsdObject { +class cReplayControl : public cDvbPlayerControl {  private: -  cDvbApi *dvbApi;    cMarks marks;    bool visible, modeOnly, shown, displayFrames;    int lastCurrent, lastTotal; @@ -4,12 +4,12 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: osd.c 1.27 2002/05/19 12:56:57 kls Exp $ + * $Id: osd.c 1.28 2002/06/10 16:30:00 kls Exp $   */  #include "osd.h"  #include <string.h> -#include "dvbapi.h" +#include "device.h"  #include "i18n.h"  #include "status.h" @@ -95,7 +95,7 @@ void cOsd::Open(int w, int h)    int x = (720 - w + charWidth) / 2; //TODO PAL vs. NTSC???    int y = (576 - Setup.OSDheight * lineHeight) / 2 + d;    //XXX -  osd = new cDvbOsd(cDvbApi::PrimaryDvbApi->OsdDeviceHandle(), x, y); +  osd = new cDvbOsd(cDevice::PrimaryDevice()->OsdDeviceHandle(), x, y);    //XXX TODO this should be transferred to the places where the individual windows are requested (there's too much detailed knowledge here!)    if (h / lineHeight == 5) { //XXX channel display       osd->Create(0,              0, w, h, 4); diff --git a/player.c b/player.c new file mode 100644 index 00000000..fe0a2771 --- /dev/null +++ b/player.c @@ -0,0 +1,55 @@ +/* + * player.c: The basic player interface + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * $Id: player.c 1.1 2002/06/16 10:34:50 kls Exp $ + */ + +#include "player.h" + +// --- cPlayer --------------------------------------------------------------- + +cPlayer::cPlayer(void) +{ +  device = NULL; +  deviceFileHandle = -1; +} + +cPlayer::~cPlayer() +{ +  Detach(); +} + +int cPlayer::PlayVideo(const uchar *Data, int Length) +{ +  if (device) +     return device->PlayVideo(Data, Length); +  esyslog("ERROR: attempt to use cPlayer::PlayVideo() without attaching to a cDevice!"); +  return -1; +} + +int cPlayer::PlayAudio(const uchar *Data, int Length) +{ +  if (device) +     return device->PlayAudio(Data, Length); +  esyslog("ERROR: attempt to use cPlayer::PlayAudio() without attaching to a cDevice!"); +  return -1; +} + +void cPlayer::Detach(void) +{ +  if (device) +     device->Detach(this); +} + +// --- cControl -------------------------------------------------------------- + +cControl::cControl(void) +{ +} + +cControl::~cControl() +{ +} diff --git a/player.h b/player.h new file mode 100644 index 00000000..1221c244 --- /dev/null +++ b/player.h @@ -0,0 +1,51 @@ +/* + * player.h: The basic player interface + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * $Id: player.h 1.1 2002/06/16 11:52:45 kls Exp $ + */ + +#ifndef __PLAYER_H +#define __PLAYER_H + +#include "device.h" +#include "osd.h" + +class cPlayer { +  friend class cDevice; +private: +  cDevice *device; +  int deviceFileHandle; +protected: +  int DeviceFileHandle(void) { return deviceFileHandle; } //XXX+ needed for polling +  void DeviceTrickSpeed(int Speed) { if (device) device->TrickSpeed(Speed); } +  void DeviceClear(void) { if (device) device->Clear(); } +  void DevicePlay(void) { if (device) device->Play(); } +  void DeviceFreeze(void) { if (device) device->Freeze(); } +  void DeviceMute(void) { if (device) device->Mute(); } +  void DeviceStillPicture(const uchar *Data, int Length) { if (device) device->StillPicture(Data, Length); } +  void Detach(void); +  virtual void Activate(bool On) {} +               // This function is called right after the cPlayer has been attached to +               // (On == true) or before it gets detached from (On == false) a cDevice. +               // It can be used to do things like starting/stopping a thread. +  int PlayVideo(const uchar *Data, int Length); +               // Sends the given Data to the video device and returns the number of +               // bytes that have actually been accepted by the video device (or a +               // negative value in case of an error). +  int PlayAudio(const uchar *Data, int Length); +               // XXX+ TODO +public: +  cPlayer(void); +  virtual ~cPlayer(); +  }; + +class cControl : public cOsdObject { +public: +  cControl(void); +  virtual ~cControl(); +  }; + +#endif //__PLAYER_H diff --git a/receiver.c b/receiver.c new file mode 100644 index 00000000..6ddbaa64 --- /dev/null +++ b/receiver.c @@ -0,0 +1,55 @@ +/* + * receiver.c: The basic receiver interface + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * $Id: receiver.c 1.1 2002/06/10 16:30:00 kls Exp $ + */ + +#include <stdarg.h> +#include <stdio.h> +#include "receiver.h" + +cReceiver::cReceiver(int Ca, int Priority, int NumPids, ...) +{ +  device = NULL; +  ca = Ca; +  priority = Priority; +  if (NumPids) { +     va_list ap; +     va_start(ap, NumPids); +     int n = 0; +     while (n < MAXRECEIVEPIDS && NumPids--) { +           if ((pids[n] = va_arg(ap, int)) != 0) +              n++; +           } +     va_end(ap); +     } +  else +     esyslog("ERROR: cReceiver called without a PID!"); +} + +cReceiver::~cReceiver() +{ +  Detach(); +} + +bool cReceiver::WantsPid(int Pid) +{ +  if (Pid) { +     for (int i = 0; i < MAXRECEIVEPIDS; i++) { +         if (pids[i] == Pid) +            return true; +         if (!pids[i]) +            break; +         } +     } +  return false; +} + +void cReceiver::Detach(void) +{ +  if (device) +     device->Detach(this); +} diff --git a/receiver.h b/receiver.h new file mode 100644 index 00000000..87fe9b86 --- /dev/null +++ b/receiver.h @@ -0,0 +1,48 @@ +/* + * receiver.h: The basic receiver interface + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * $Id: receiver.h 1.1 2002/06/10 16:30:00 kls Exp $ + */ + +#ifndef __RECEIVER_H +#define __RECEIVER_H + +#include "device.h" + +#define MAXRECEIVEPIDS  16 // the maximum number of PIDs per receiver + +class cReceiver { +  friend class cDevice; +private: +  cDevice *device; +  int ca; +  int priority; +  int pids[MAXRECEIVEPIDS]; +  bool WantsPid(int Pid); +protected: +  void Detach(void); +  virtual void Activate(bool On) {} +               // This function is called just before the cReceiver gets attached to +               // (On == true) or detached from (On == false) a cDevice. It can be used +               // to do things like starting/stopping a thread. +               // It is guaranteed that Receive() will not be called before Activate(true). +  virtual void Receive(uchar *Data, int Length) = 0; +               // This function is called from the cDevice we are attached to, and +               // delivers one TS packet from the set of PIDs the cReceiver has requested. +               // The data packet must be accepted immediately, and the call must return +               // as soon as possible, without any unnecessary delay. Each TS packet +               // will be delivered only ONCE, so the cReceiver must make sure that +               // it will be able to buffer the data if necessary. +public: +  cReceiver(int Ca, int Priority, int NumPids, ...); +               // Creates a new receiver that requires conditional access Ca and has +               // the given Priority. NumPids defines the number of PIDs that follow +               // this parameter. If any of these PIDs are 0, they will be silently ignored. +               // The total number of non-zero PIDs must not exceed MAXRECEIVEPIDS. +  virtual ~cReceiver(); +  }; + +#endif //__RECEIVER_H diff --git a/recorder.c b/recorder.c new file mode 100644 index 00000000..3a0941bb --- /dev/null +++ b/recorder.c @@ -0,0 +1,149 @@ +/* + * recorder.h: The actual DVB recorder + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * $Id: recorder.c 1.1 2002/06/16 10:03:25 kls Exp $ + */ + +#include <stdarg.h> +#include <stdio.h> +#include <unistd.h> +#include "recorder.h" + +// The size of the array used to buffer video data: +// (must be larger than MINVIDEODATA - see remux.h) +#define VIDEOBUFSIZE  MEGABYTE(1) + +#define MINFREEDISKSPACE    (512) // MB +#define DISKCHECKINTERVAL   100 // seconds + +cRecorder::cRecorder(const char *FileName, int Ca, int Priority, int VPid, int APid1, int APid2, int DPid1, int DPid2) +:cReceiver(Ca, Priority, 5, VPid, APid1, APid2, DPid1, DPid2) +{ +  ringBuffer = NULL; +  remux = NULL; +  fileName = NULL; +  index = NULL; +  pictureType = NO_PICTURE; +  fileSize = 0; +  active = false; +  lastDiskSpaceCheck = time(NULL); +  isyslog("record %s", FileName); + +  // Create directories if necessary: + +  if (!MakeDirs(FileName, true)) +     return; + +  // Make sure the disk is up and running: + +  SpinUpDisk(FileName); + +  ringBuffer = new cRingBufferLinear(VIDEOBUFSIZE, true); +  remux = new cRemux(VPid, APid1, APid2, DPid1, DPid2, true); +  fileName = new cFileName(FileName, true); +  recordFile = fileName->Open(); +  if (recordFile < 0) +     return; +  // Create the index file: +  index = new cIndexFile(FileName, true); +  if (!index) +     esyslog("ERROR: can't allocate index"); +     // let's continue without index, so we'll at least have the recording +} + +cRecorder::~cRecorder() +{ +  Detach(); +  delete index; +  delete fileName; +  delete remux; +  delete ringBuffer; +} + +void cRecorder::Activate(bool On) +{ +  if (On) { +     if (recordFile >= 0) +        Start(); +     } +  else if (active) { +     active = false; +     Cancel(3); +     } +} + +bool cRecorder::RunningLowOnDiskSpace(void) +{ +  if (time(NULL) > lastDiskSpaceCheck + DISKCHECKINTERVAL) { +     int Free = FreeDiskSpaceMB(fileName->Name()); +     lastDiskSpaceCheck = time(NULL); +     if (Free < MINFREEDISKSPACE) { +        dsyslog("low disk space (%d MB, limit is %d MB)", Free, MINFREEDISKSPACE); +        return true; +        } +     } +  return false; +} + +bool cRecorder::NextFile(void) +{ +  if (recordFile >= 0 && pictureType == I_FRAME) { // every file shall start with an I_FRAME +     if (fileSize > MEGABYTE(Setup.MaxVideoFileSize) || RunningLowOnDiskSpace()) { +        recordFile = fileName->NextFile(); +        fileSize = 0; +        } +     } +  return recordFile >= 0; +} + +void cRecorder::Receive(uchar *Data, int Length) +{ +  int p = ringBuffer->Put(Data, Length); +  if (p != Length && active) +     esyslog("ERROR: ring buffer overflow (%d bytes dropped)", Length - p); +} + +void cRecorder::Action(void) +{ +  dsyslog("recording thread started (pid=%d)", getpid()); + +  uchar b[MINVIDEODATA]; +  int r = 0; +  active = true; +  while (active) { +        int g = ringBuffer->Get(b + r, sizeof(b) - r); +        if (g > 0) +           r += g; +        if (r > 0) { +           int Count = r, Result; +           const uchar *p = remux->Process(b, Count, Result, &pictureType); +           if (p) { +              //XXX+ active??? see old version (Busy) +              if (!active && pictureType == I_FRAME) // finish the recording before the next 'I' frame +                 break; +              if (NextFile()) { +                 if (index && pictureType != NO_PICTURE) +                    index->Write(pictureType, fileName->Number(), fileSize); +                 if (safe_write(recordFile, p, Result) < 0) { +                    LOG_ERROR_STR(fileName->Name()); +                    break; +                    } +                 fileSize += Result; +                 } +              else +                 break; +              } +           if (Count > 0) { +              r -= Count; +              memmove(b, b + Count, r); +              } +           } +        else +           usleep(1); // this keeps the CPU load low +        } + +  dsyslog("recording thread ended (pid=%d)", getpid()); +} diff --git a/recorder.h b/recorder.h new file mode 100644 index 00000000..7493f0b2 --- /dev/null +++ b/recorder.h @@ -0,0 +1,43 @@ +/* + * recorder.h: The actual DVB recorder + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * $Id: recorder.h 1.1 2002/06/10 16:30:00 kls Exp $ + */ + +#ifndef __RECORDER_H +#define __RECORDER_H + +#include "receiver.h" +#include "recording.h" +#include "remux.h" +#include "ringbuffer.h" +#include "thread.h" + +class cRecorder : public cReceiver, cThread { +private: +  cRingBufferLinear *ringBuffer; +  cRemux *remux; +  cFileName *fileName; +  cIndexFile *index; +  uchar pictureType; +  int fileSize; +  int recordFile; +  bool active; +  time_t lastDiskSpaceCheck; +  bool RunningLowOnDiskSpace(void); +  bool NextFile(void); +protected: +  virtual void Activate(bool On); +  virtual void Receive(uchar *Data, int Length); +  virtual void Action(void); +public: +  cRecorder(const char *FileName, int Ca, int Priority, int VPid, int APid1, int APid2, int DPid1, int DPid2); +               // Creates a new recorder that requires conditional access Ca, has +               // the given Priority and will record the given PIDs into the file FileName. +  virtual ~cRecorder(); +  }; + +#endif //__RECORDER_H diff --git a/recording.c b/recording.c index c65aaaf9..7bc896c5 100644 --- a/recording.c +++ b/recording.c @@ -4,7 +4,7 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: recording.c 1.62 2002/05/13 16:31:21 kls Exp $ + * $Id: recording.c 1.63 2002/06/16 11:29:27 kls Exp $   */  #include "recording.h" @@ -16,6 +16,7 @@  #include <unistd.h>  #include "i18n.h"  #include "interface.h" +#include "remux.h" //XXX+ I_FRAME  #include "tools.h"  #include "videodir.h" @@ -732,3 +733,350 @@ void cRecordingUserCommand::InvokeCommand(const char *State, const char *Recordi       delete cmd;       }  } + +// --- XXX+ + +//XXX+ somewhere else??? +// --- cIndexFile ------------------------------------------------------------ + +#define INDEXFILESUFFIX     "/index.vdr" + +// The maximum time to wait before giving up while catching up on an index file: +#define MAXINDEXCATCHUP   2 // seconds + +cIndexFile::cIndexFile(const char *FileName, bool Record) +:resumeFile(FileName) +{ +  f = -1; +  fileName = NULL; +  size = 0; +  last = -1; +  index = NULL; +  if (FileName) { +     fileName = new char[strlen(FileName) + strlen(INDEXFILESUFFIX) + 1]; +     if (fileName) { +        strcpy(fileName, FileName); +        char *pFileExt = fileName + strlen(fileName); +        strcpy(pFileExt, INDEXFILESUFFIX); +        int delta = 0; +        if (access(fileName, R_OK) == 0) { +           struct stat buf; +           if (stat(fileName, &buf) == 0) { +              delta = buf.st_size % sizeof(tIndex); +              if (delta) { +                 delta = sizeof(tIndex) - delta; +                 esyslog("ERROR: invalid file size (%ld) in '%s'", buf.st_size, fileName); +                 } +              last = (buf.st_size + delta) / sizeof(tIndex) - 1; +              if (!Record && last >= 0) { +                 size = last + 1; +                 index = new tIndex[size]; +                 if (index) { +                    f = open(fileName, O_RDONLY); +                    if (f >= 0) { +                       if ((int)safe_read(f, index, buf.st_size) != buf.st_size) { +                          esyslog("ERROR: can't read from file '%s'", fileName); +                          delete index; +                          index = NULL; +                          close(f); +                          f = -1; +                          } +                       // we don't close f here, see CatchUp()! +                       } +                    else +                       LOG_ERROR_STR(fileName); +                    } +                 else +                    esyslog("ERROR: can't allocate %d bytes for index '%s'", size * sizeof(tIndex), fileName); +                 } +              } +           else +              LOG_ERROR; +           } +        else if (!Record) +           isyslog("missing index file %s", fileName); +        if (Record) { +           if ((f = open(fileName, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) >= 0) { +              if (delta) { +                 esyslog("ERROR: padding index file with %d '0' bytes", delta); +                 while (delta--) +                       writechar(f, 0); +                 } +              } +           else +              LOG_ERROR_STR(fileName); +           } +        } +     else +        esyslog("ERROR: can't copy file name '%s'", FileName); +     } +} + +cIndexFile::~cIndexFile() +{ +  if (f >= 0) +     close(f); +  delete fileName; +  delete index; +} + +bool cIndexFile::CatchUp(int Index) +{ +  if (index && f >= 0) { +     for (int i = 0; i <= MAXINDEXCATCHUP && (Index < 0 || Index >= last); i++) { +         struct stat buf; +         if (fstat(f, &buf) == 0) { +            int newLast = buf.st_size / sizeof(tIndex) - 1; +            if (newLast > last) { +               if (size <= newLast) { +                  size *= 2; +                  if (size <= newLast) +                     size = newLast + 1; +                  } +               index = (tIndex *)realloc(index, size * sizeof(tIndex)); +               if (index) { +                  int offset = (last + 1) * sizeof(tIndex); +                  int delta = (newLast - last) * sizeof(tIndex); +                  if (lseek(f, offset, SEEK_SET) == offset) { +                     if (safe_read(f, &index[last + 1], delta) != delta) { +                        esyslog("ERROR: can't read from index"); +                        delete index; +                        index = NULL; +                        close(f); +                        f = -1; +                        break; +                        } +                     last = newLast; +                     } +                  else +                     LOG_ERROR_STR(fileName); +                  } +               else +                  esyslog("ERROR: can't realloc() index"); +               } +            } +         else +            LOG_ERROR_STR(fileName); +         if (Index >= last) +            sleep(1); +         else +            return true; +         } +     } +  return false; +} + +bool cIndexFile::Write(uchar PictureType, uchar FileNumber, int FileOffset) +{ +  if (f >= 0) { +     tIndex i = { FileOffset, PictureType, FileNumber, 0 }; +     if (safe_write(f, &i, sizeof(i)) < 0) { +        LOG_ERROR_STR(fileName); +        close(f); +        f = -1; +        return false; +        } +     last++; +     } +  return f >= 0; +} + +bool cIndexFile::Get(int Index, uchar *FileNumber, int *FileOffset, uchar *PictureType, int *Length) +{ +  if (index) { +     CatchUp(Index); +     if (Index >= 0 && Index <= last) { +        *FileNumber = index[Index].number; +        *FileOffset = index[Index].offset; +        if (PictureType) +           *PictureType = index[Index].type; +        if (Length) { +           int fn = index[Index + 1].number; +           int fo = index[Index + 1].offset; +           if (fn == *FileNumber) +              *Length = fo - *FileOffset; +           else +              *Length = -1; // this means "everything up to EOF" (the buffer's Read function will act accordingly) +           } +        return true; +        } +     } +  return false; +} + +int cIndexFile::GetNextIFrame(int Index, bool Forward, uchar *FileNumber, int *FileOffset, int *Length, bool StayOffEnd) +{ +  if (index) { +     CatchUp(); +     int d = Forward ? 1 : -1; +     for (;;) { +         Index += d; +         if (Index >= 0 && Index < last - ((Forward && StayOffEnd) ? 100 : 0)) { +            if (index[Index].type == I_FRAME) { +               if (FileNumber) +                  *FileNumber = index[Index].number; +               else +                  FileNumber = &index[Index].number; +               if (FileOffset) +                  *FileOffset = index[Index].offset; +               else +                  FileOffset = &index[Index].offset; +               if (Length) { +                  // all recordings end with a non-I_FRAME, so the following should be safe: +                  int fn = index[Index + 1].number; +                  int fo = index[Index + 1].offset; +                  if (fn == *FileNumber) +                     *Length = fo - *FileOffset; +                  else { +                     esyslog("ERROR: 'I' frame at end of file #%d", *FileNumber); +                     *Length = -1; +                     } +                  } +               return Index; +               } +            } +         else +            break; +         } +     } +  return -1; +} + +int cIndexFile::Get(uchar FileNumber, int FileOffset) +{ +  if (index) { +     CatchUp(); +     //TODO implement binary search! +     int i; +     for (i = 0; i < last; i++) { +         if (index[i].number > FileNumber || (index[i].number == FileNumber) && index[i].offset >= FileOffset) +            break; +         } +     return i; +     } +  return -1; +} + +// --- cFileName ------------------------------------------------------------- + +#include <errno.h> +#include <unistd.h> +#include "videodir.h" + +#define MAXFILESPERRECORDING 255 +#define RECORDFILESUFFIX    "/%03d.vdr" +#define RECORDFILESUFFIXLEN 20 // some additional bytes for safety... + +cFileName::cFileName(const char *FileName, bool Record, bool Blocking) +{ +  file = -1; +  fileNumber = 0; +  record = Record; +  blocking = Blocking; +  // Prepare the file name: +  fileName = new char[strlen(FileName) + RECORDFILESUFFIXLEN]; +  if (!fileName) { +     esyslog("ERROR: can't copy file name '%s'", fileName); +     return; +     } +  strcpy(fileName, FileName); +  pFileNumber = fileName + strlen(fileName); +  SetOffset(1); +} + +cFileName::~cFileName() +{ +  Close(); +  delete fileName; +} + +int cFileName::Open(void) +{ +  if (file < 0) { +     int BlockingFlag = blocking ? 0 : O_NONBLOCK; +     if (record) { +        dsyslog("recording to '%s'", fileName); +        file = OpenVideoFile(fileName, O_RDWR | O_CREAT | BlockingFlag); +        if (file < 0) +           LOG_ERROR_STR(fileName); +        } +     else { +        if (access(fileName, R_OK) == 0) { +           dsyslog("playing '%s'", fileName); +           file = open(fileName, O_RDONLY | BlockingFlag); +           if (file < 0) +              LOG_ERROR_STR(fileName); +           } +        else if (errno != ENOENT) +           LOG_ERROR_STR(fileName); +        } +     } +  return file; +} + +void cFileName::Close(void) +{ +  if (file >= 0) { +     if ((record && CloseVideoFile(file) < 0) || (!record && close(file) < 0)) +        LOG_ERROR_STR(fileName); +     file = -1; +     } +} + +int cFileName::SetOffset(int Number, int Offset) +{ +  if (fileNumber != Number) +     Close(); +  if (0 < Number && Number <= MAXFILESPERRECORDING) { +     fileNumber = Number; +     sprintf(pFileNumber, RECORDFILESUFFIX, fileNumber); +     if (record) { +        if (access(fileName, F_OK) == 0) // file exists, let's try next suffix +           return SetOffset(Number + 1); +        else if (errno != ENOENT) { // something serious has happened +           LOG_ERROR_STR(fileName); +           return -1; +           } +        // found a non existing file suffix +        } +     if (Open() >= 0) { +        if (!record && Offset >= 0 && lseek(file, Offset, SEEK_SET) != Offset) { +           LOG_ERROR_STR(fileName); +           return -1; +           } +        } +     return file; +     } +  esyslog("ERROR: max number of files (%d) exceeded", MAXFILESPERRECORDING); +  return -1; +} + +int cFileName::NextFile(void) +{ +  return SetOffset(fileNumber + 1); +} + +const char *IndexToHMSF(int Index, bool WithFrame) +{ +  static char buffer[16]; +  int f = (Index % FRAMESPERSEC) + 1; +  int s = (Index / FRAMESPERSEC); +  int m = s / 60 % 60; +  int h = s / 3600; +  s %= 60; +  snprintf(buffer, sizeof(buffer), WithFrame ? "%d:%02d:%02d.%02d" : "%d:%02d:%02d", h, m, s, f); +  return buffer; +} + +int HMSFToIndex(const char *HMSF) +{ +  int h, m, s, f = 0; +  if (3 <= sscanf(HMSF, "%d:%d:%d.%d", &h, &m, &s, &f)) +     return (h * 3600 + m * 60 + s) * FRAMESPERSEC + f - 1; +  return 0; +} + +int SecondsToFrames(int Seconds) +{ +  return Seconds * FRAMESPERSEC; +} diff --git a/recording.h b/recording.h index 38626f85..16c6506f 100644 --- a/recording.h +++ b/recording.h @@ -4,7 +4,7 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: recording.h 1.22 2002/02/03 11:59:49 kls Exp $ + * $Id: recording.h 1.23 2002/06/16 11:29:47 kls Exp $   */  #ifndef __RECORDING_H @@ -104,4 +104,63 @@ public:    static void InvokeCommand(const char *State, const char *RecordingFileName);    }; +//XXX+ +#define FRAMESPERSEC 25 + +// The maximum file size is limited by the range that can be covered +// with 'int'. 4GB might be possible (if the range is considered +// 'unsigned'), 2GB should be possible (even if the range is considered +// 'signed'), so let's use 2000MB for absolute safety (the actual file size +// may be slightly higher because we stop recording only before the next +// 'I' frame, to have a complete Group Of Pictures): +#define MAXVIDEOFILESIZE 2000 // MB +#define MINVIDEOFILESIZE  100 // MB + +class cIndexFile { +private: +  struct tIndex { int offset; uchar type; uchar number; short reserved; }; +  int f; +  char *fileName; +  int size, last; +  tIndex *index; +  cResumeFile resumeFile; +  bool CatchUp(int Index = -1); +public: +  cIndexFile(const char *FileName, bool Record); +  ~cIndexFile(); +  bool Ok(void) { return index != NULL; } +  bool Write(uchar PictureType, uchar FileNumber, int FileOffset); +  bool Get(int Index, uchar *FileNumber, int *FileOffset, uchar *PictureType = NULL, int *Length = NULL); +  int GetNextIFrame(int Index, bool Forward, uchar *FileNumber = NULL, int *FileOffset = NULL, int *Length = NULL, bool StayOffEnd = false); +  int Get(uchar FileNumber, int FileOffset); +  int Last(void) { CatchUp(); return last; } +  int GetResume(void) { return resumeFile.Read(); } +  bool StoreResume(int Index) { return resumeFile.Save(Index); } +  }; + +class cFileName { +private: +  int file; +  int fileNumber; +  char *fileName, *pFileNumber; +  bool record; +  bool blocking; +public: +  cFileName(const char *FileName, bool Record, bool Blocking = false); +  ~cFileName(); +  const char *Name(void) { return fileName; } +  int Number(void) { return fileNumber; } +  int Open(void); +  void Close(void); +  int SetOffset(int Number, int Offset = 0); +  int NextFile(void); +  }; + +const char *IndexToHMSF(int Index, bool WithFrame = false); +      // Converts the given index to a string, optionally containing the frame number. +int HMSFToIndex(const char *HMSF); +      // Converts the given string (format: "hh:mm:ss.ff") to an index. +int SecondsToFrames(int Seconds); //XXX+ ->player??? +      // Returns the number of frames corresponding to the given number of seconds. +  #endif //__RECORDING_H diff --git a/ringbuffer.c b/ringbuffer.c index d7562f96..421f19f0 100644 --- a/ringbuffer.c +++ b/ringbuffer.c @@ -1,5 +1,5 @@  /* - * ringbuffer.c: A threaded ring buffer + * ringbuffer.c: A ring buffer   *   * See the main source file 'vdr.c' for copyright information and   * how to reach the author. @@ -7,50 +7,25 @@   * Parts of this file were inspired by the 'ringbuffy.c' from the   * LinuxDVB driver (see linuxtv.org).   * - * $Id: ringbuffer.c 1.8 2002/05/18 08:54:52 kls Exp $ + * $Id: ringbuffer.c 1.9 2002/06/16 11:24:40 kls Exp $   */  #include "ringbuffer.h" +#include <unistd.h>  #include "tools.h" -// --- cRingBufferInputThread ------------------------------------------------- - -class cRingBufferInputThread : public cThread { -private: -  cRingBuffer *ringBuffer; -protected: -  virtual void Action(void) { ringBuffer->Input(); } -public: -  cRingBufferInputThread(cRingBuffer *RingBuffer) { ringBuffer = RingBuffer; } -  }; - -// --- cRingBufferOutputThread ------------------------------------------------ - -class cRingBufferOutputThread : public cThread { -private: -  cRingBuffer *ringBuffer; -protected: -  virtual void Action(void) { ringBuffer->Output(); } -public: -  cRingBufferOutputThread(cRingBuffer *RingBuffer) { ringBuffer = RingBuffer; } -  }; - -// --- cRingBuffer ------------------------------------------------------------ +// --- cRingBuffer -----------------------------------------------------------  cRingBuffer::cRingBuffer(int Size, bool Statistics)  {    size = Size;    statistics = Statistics; -  inputThread = NULL; -  outputThread = NULL; -  busy = false;    maxFill = 0; +  lastPercent = 0;  }  cRingBuffer::~cRingBuffer()  { -  delete inputThread; -  delete outputThread;    if (statistics)       dsyslog("buffer stats: %d (%d%%) used", maxFill, maxFill * 100 / (size - 1));  } @@ -79,45 +54,13 @@ void cRingBuffer::EnableGet(void)    readyForGet.Broadcast();  } -bool cRingBuffer::Start(void) -{ -  if (!busy) { -     busy = true; -     outputThread = new cRingBufferOutputThread(this); -     if (!outputThread->Start()) -        DELETENULL(outputThread); -     inputThread = new cRingBufferInputThread(this); -     if (!inputThread->Start()) { -        DELETENULL(inputThread); -        DELETENULL(outputThread); -        } -     busy = outputThread && inputThread; -     } -  return busy; -} - -bool cRingBuffer::Active(void) -{ -  return outputThread && outputThread->Active() && inputThread && inputThread->Active(); -} - -void cRingBuffer::Stop(void) -{ -  busy = false; -  for (time_t t0 = time(NULL) + 3; time(NULL) < t0; ) { -      if (!((outputThread && outputThread->Active()) || (inputThread && inputThread->Active()))) -         break; -      } -  DELETENULL(inputThread); -  DELETENULL(outputThread); -} - -// --- cRingBufferLinear ---------------------------------------------------- +// --- cRingBufferLinear -----------------------------------------------------  cRingBufferLinear::cRingBufferLinear(int Size, bool Statistics)  :cRingBuffer(Size, Statistics)  {    buffer = NULL; +  getThreadPid = -1;    if (Size > 1) { // 'Size - 1' must not be 0!       buffer = new uchar[Size];       if (!buffer) @@ -146,6 +89,8 @@ void cRingBufferLinear::Clear(void)    Lock();    head = tail = 0;    Unlock(); +  EnablePut(); +  EnableGet();  }  int cRingBufferLinear::Put(const uchar *Data, int Count) @@ -159,11 +104,13 @@ int cRingBufferLinear::Put(const uchar *Data, int Count)          int fill = Size() - free - 1 + Count;          if (fill >= Size())             fill = Size() - 1; -        if (fill > maxFill) { +        if (fill > maxFill)             maxFill = fill; -           int percent = maxFill * 100 / (Size() - 1); +        int percent = maxFill * 100 / (Size() - 1) / 5 * 5; +        if (abs(lastPercent - percent) >= 5) {             if (percent > 75) -              dsyslog("buffer usage: %d%%", percent); +              dsyslog("buffer usage: %d%% (pid=%d)", percent, getThreadPid); +           lastPercent = percent;             }          }       if (free > 0) { @@ -185,6 +132,7 @@ int cRingBufferLinear::Put(const uchar *Data, int Count)       else          Count = 0;       Unlock(); +     EnableGet();       }    return Count;  } @@ -193,6 +141,8 @@ int cRingBufferLinear::Get(uchar *Data, int Count)  {    if (Count > 0) {       Lock(); +     if (getThreadPid < 0) +        getThreadPid = getpid();       int rest = Size() - tail;       int diff = head - tail;       int cont = (diff >= 0) ? diff : Size() + diff; @@ -213,6 +163,8 @@ int cRingBufferLinear::Get(uchar *Data, int Count)       else          Count = 0;       Unlock(); +     if (Count == 0) +        WaitForGet();       }    return Count;  } @@ -255,7 +207,7 @@ void cRingBufferFrame::Clear(void)  {    Lock();    const cFrame *p; -  while ((p = Get(false)) != NULL) +  while ((p = Get()) != NULL)          Drop(p);    Unlock();    EnablePut(); @@ -279,17 +231,14 @@ bool cRingBufferFrame::Put(cFrame *Frame)       EnableGet();       return true;       } -  WaitForPut();    return false;  } -const cFrame *cRingBufferFrame::Get(bool Wait) +const cFrame *cRingBufferFrame::Get(void)  {    Lock();    cFrame *p = head ? head->next : NULL;    Unlock(); -  if (!p && Wait) -     WaitForGet();    return p;  } diff --git a/ringbuffer.h b/ringbuffer.h index 7e1025b1..43176bde 100644 --- a/ringbuffer.h +++ b/ringbuffer.h @@ -1,10 +1,10 @@  /* - * ringbuffer.h: A threaded ring buffer + * ringbuffer.h: A ring buffer   *   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: ringbuffer.h 1.5 2001/11/03 10:41:33 kls Exp $ + * $Id: ringbuffer.h 1.6 2002/06/16 11:30:07 kls Exp $   */  #ifndef __RINGBUFFER_H @@ -12,24 +12,17 @@  #include "thread.h" -typedef unsigned char uchar; - -class cRingBufferInputThread; -class cRingBufferOutputThread; +typedef unsigned char uchar;//XXX+  class cRingBuffer { -  friend class cRingBufferInputThread; -  friend class cRingBufferOutputThread;  private: -  cRingBufferInputThread *inputThread; -  cRingBufferOutputThread *outputThread;    cMutex mutex;    cCondVar readyForPut, readyForGet;    cMutex putMutex, getMutex;    int size; -  bool busy;  protected:    int maxFill;//XXX +  int lastPercent;    bool statistics;//XXX    void WaitForPut(void);    void WaitForGet(void); @@ -41,26 +34,19 @@ protected:    void Lock(void) { mutex.Lock(); }    void Unlock(void) { mutex.Unlock(); }    int Size(void) { return size; } -  bool Busy(void) { return busy; } -  virtual void Input(void) = 0; -    // Runs as a separate thread and shall continuously read data from -    // a source and call Put() to store the data in the ring buffer. -  virtual void Output(void) = 0; -    // Runs as a separate thread and shall continuously call Get() to -    // retrieve data from the ring buffer and write it to a destination.  public:    cRingBuffer(int Size, bool Statistics = false);    virtual ~cRingBuffer(); -  bool Start(void); -  bool Active(void); -  void Stop(void);    };  class cRingBufferLinear : public cRingBuffer {  private:    int head, tail;    uchar *buffer; -protected: +  pid_t getThreadPid; +public: +  cRingBufferLinear(int Size, bool Statistics = false); +  virtual ~cRingBufferLinear();    virtual int Available(void);    virtual void Clear(void);      // Immediately clears the ring buffer. @@ -70,9 +56,6 @@ protected:    int Get(uchar *Data, int Count);      // Gets at most Count bytes of Data from the ring buffer.      // Returns the number of bytes actually retrieved. -public: -  cRingBufferLinear(int Size, bool Statistics = false); -  virtual ~cRingBufferLinear();    };  enum eFrameType { ftUnknown, ftVideo, ftAudio, ftDolby }; @@ -99,21 +82,20 @@ private:    cFrame *head;    int currentFill;    void Delete(const cFrame *Frame); -protected: +public: +  cRingBufferFrame(int Size, bool Statistics = false); +  virtual ~cRingBufferFrame();    virtual int Available(void);    virtual void Clear(void);      // Immediately clears the ring buffer.    bool Put(cFrame *Frame);      // Puts the Frame into the ring buffer.      // Returns true if this was possible. -  const cFrame *Get(bool Wait = true); +  const cFrame *Get(void);      // Gets the next frame from the ring buffer.      // The actual data still remains in the buffer until Drop() is called.    void Drop(const cFrame *Frame);      // Drops the Frame that has just been fetched with Get(). -public: -  cRingBufferFrame(int Size, bool Statistics = false); -  virtual ~cRingBufferFrame();    };  #endif // __RINGBUFFER_H @@ -4,7 +4,7 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: status.c 1.1 2002/05/19 14:54:30 kls Exp $ + * $Id: status.c 1.2 2002/06/16 12:10:44 kls Exp $   */  #include "status.h" @@ -23,22 +23,22 @@ cStatusMonitor::~cStatusMonitor()    statusMonitors.Del(this, false);  } -void cStatusMonitor::MsgChannelSwitch(const cDvbApi *DvbApi, int ChannelNumber) +void cStatusMonitor::MsgChannelSwitch(const cDevice *Device, int ChannelNumber)  {    for (cStatusMonitor *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm)) -      sm->ChannelSwitch(DvbApi, ChannelNumber); +      sm->ChannelSwitch(Device, ChannelNumber);  } -void cStatusMonitor::MsgRecording(const cDvbApi *DvbApi, const char *Name) +void cStatusMonitor::MsgRecording(const cDevice *Device, const char *Name)  {    for (cStatusMonitor *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm)) -      sm->Recording(DvbApi, Name); +      sm->Recording(Device, Name);  } -void cStatusMonitor::MsgReplaying(const cDvbApi *DvbApi, const char *Name) +void cStatusMonitor::MsgReplaying(const cDvbPlayerControl *DvbPlayerControl, const char *Name)  {    for (cStatusMonitor *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm)) -      sm->Replaying(DvbApi, Name); +      sm->Replaying(DvbPlayerControl, Name);  }  void cStatusMonitor::MsgSetVolume(int Volume, bool Absolute) @@ -4,14 +4,15 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: status.h 1.1 2002/05/19 14:54:15 kls Exp $ + * $Id: status.h 1.2 2002/06/16 12:09:55 kls Exp $   */  #ifndef __STATUS_H  #define __STATUS_H  #include "config.h" -#include "dvbapi.h" +#include "device.h" +#include "dvbplayer.h"  #include "tools.h"  class cStatusMonitor : public cListObject { @@ -19,15 +20,15 @@ private:    static cList<cStatusMonitor> statusMonitors;  protected:    // These functions can be implemented by derived classes to receive status information: -  virtual void ChannelSwitch(const cDvbApi *DvbApi, int ChannelNumber) {} -               // Indicates a channel switch on DVB device DvbApi. +  virtual void ChannelSwitch(const cDevice *Device, int ChannelNumber) {} +               // Indicates a channel switch on the given DVB device.                 // If ChannelNumber is 0, this is before the channel is being switched,                 // otherwise ChannelNumber is the number of the channel that has been switched to. -  virtual void Recording(const cDvbApi *DvbApi, const char *Name) {} -               // DVB device DvbApi has started recording Name. Name is the full directory +  virtual void Recording(const cDevice *Device, const char *Name) {} +               // The given DVB device has started recording Name. Name is the full directory                 // name of the recording. If Name is NULL, the recording has ended.  -  virtual void Replaying(const cDvbApi *DvbApi, const char *Name) {} -               // DVB device DvbApi has started replaying Name. Name is the full directory +  virtual void Replaying(const cDvbPlayerControl *DvbPlayerControl, const char *Name) {} +               // The given player control has started replaying Name. Name is the full directory                 // name of the recording. If Name is NULL, the replay has ended.     virtual void SetVolume(int Volume, bool Absolute) {}                 // The volume has been set to the given value, either @@ -57,9 +58,9 @@ public:    cStatusMonitor(void);    virtual ~cStatusMonitor();    // These functions are called whenever the related status information changes: -  static void MsgChannelSwitch(const cDvbApi *DvbApi, int ChannelNumber); -  static void MsgRecording(const cDvbApi *DvbApi, const char *Name); -  static void MsgReplaying(const cDvbApi *DvbApi, const char *Name); +  static void MsgChannelSwitch(const cDevice *Device, int ChannelNumber); +  static void MsgRecording(const cDevice *Device, const char *Name); +  static void MsgReplaying(const cDvbPlayerControl *DvbPlayerControl, const char *Name);    static void MsgSetVolume(int Volume, bool Absolute);    static void MsgOsdClear(void);    static void MsgOsdTitle(const char *Title); @@ -10,7 +10,7 @@   * and interact with the Video Disk Recorder - or write a full featured   * graphical interface that sits on top of an SVDRP connection.   * - * $Id: svdrp.c 1.37 2002/05/13 16:32:05 kls Exp $ + * $Id: svdrp.c 1.38 2002/06/10 16:30:00 kls Exp $   */  #include "svdrp.h" @@ -27,7 +27,7 @@  #include <sys/time.h>  #include <unistd.h>  #include "config.h" -#include "dvbapi.h" +#include "device.h"  #include "interface.h"  #include "tools.h" @@ -390,12 +390,12 @@ void cSVDRP::CmdCHAN(const char *Option)             n = o;          }       else if (strcmp(Option, "-") == 0) { -        n = cDvbApi::CurrentChannel(); +        n = cDevice::CurrentChannel();          if (n > 1)             n--;          }       else if (strcmp(Option, "+") == 0) { -        n = cDvbApi::CurrentChannel(); +        n = cDevice::CurrentChannel();          if (n < Channels.MaxNumber())             n++;          } @@ -430,11 +430,11 @@ void cSVDRP::CmdCHAN(const char *Option)          return;          }       } -  cChannel *channel = Channels.GetByNumber(cDvbApi::CurrentChannel()); +  cChannel *channel = Channels.GetByNumber(cDevice::CurrentChannel());    if (channel)       Reply(250, "%d %s", channel->number, channel->name);    else -     Reply(550, "Unable to find channel \"%d\"", cDvbApi::CurrentChannel()); +     Reply(550, "Unable to find channel \"%d\"", cDevice::CurrentChannel());  }  void cSVDRP::CmdDELC(const char *Option) @@ -541,7 +541,7 @@ void cSVDRP::CmdGRAB(const char *Option)          Reply(501, "Unexpected parameter \"%s\"", p);          return;          } -     if (cDvbApi::PrimaryDvbApi->GrabImage(FileName, Jpeg, Quality, SizeX, SizeY)) +     if (cDevice::PrimaryDevice()->GrabImage(FileName, Jpeg, Quality, SizeX, SizeY))          Reply(250, "Grabbed image %s", Option);       else          Reply(451, "Grab image failed"); @@ -926,22 +926,22 @@ void cSVDRP::CmdVOLU(const char *Option)  {    if (*Option) {       if (isnumber(Option)) -        cDvbApi::PrimaryDvbApi->SetVolume(strtol(Option, NULL, 10), true); +        cDevice::PrimaryDevice()->SetVolume(strtol(Option, NULL, 10), true);       else if (strcmp(Option, "+") == 0) -        cDvbApi::PrimaryDvbApi->SetVolume(VOLUMEDELTA); +        cDevice::PrimaryDevice()->SetVolume(VOLUMEDELTA);       else if (strcmp(Option, "-") == 0) -        cDvbApi::PrimaryDvbApi->SetVolume(-VOLUMEDELTA); +        cDevice::PrimaryDevice()->SetVolume(-VOLUMEDELTA);       else if (strcasecmp(Option, "MUTE") == 0) -        cDvbApi::PrimaryDvbApi->ToggleMute(); +        cDevice::PrimaryDevice()->ToggleMute();       else {          Reply(501, "Unknown option: \"%s\"", Option);          return;          }       } -  if (cDvbApi::PrimaryDvbApi->IsMute()) +  if (cDevice::PrimaryDevice()->IsMute())       Reply(250, "Audio is mute");    else -     Reply(250, "Audio volume is %d", cDvbApi::CurrentVolume()); +     Reply(250, "Audio volume is %d", cDevice::CurrentVolume());  }  #define CMD(c) (strcasecmp(Cmd, c) == 0) @@ -4,7 +4,7 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: thread.c 1.20 2002/05/13 16:32:09 kls Exp $ + * $Id: thread.c 1.21 2002/06/10 16:30:00 kls Exp $   */  #include "thread.h" @@ -147,6 +147,7 @@ bool cThread::Active(void)  void cThread::Cancel(int WaitSeconds)  { +  running = false;    if (WaitSeconds > 0) {       for (time_t t0 = time(NULL) + WaitSeconds; time(NULL) < t0; ) {           if (!Active()) @@ -4,7 +4,7 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: thread.h 1.12 2002/02/23 13:53:38 kls Exp $ + * $Id: thread.h 1.13 2002/06/10 16:30:00 kls Exp $   */  #ifndef __THREAD_H @@ -54,9 +54,9 @@ private:    static bool signalHandlerInstalled;    static void SignalHandler(int signum);    static void *StartThread(cThread *Thread); +protected:    void Lock(void) { mutex.Lock(); }    void Unlock(void) { mutex.Unlock(); } -protected:    void WakeUp(void);    virtual void Action(void) = 0;    void Cancel(int WaitSeconds = 0); @@ -4,13 +4,13 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: tools.h 1.46 2002/05/18 15:10:10 kls Exp $ + * $Id: tools.h 1.47 2002/06/10 16:30:00 kls Exp $   */  #ifndef __TOOLS_H  #define __TOOLS_H -//#include <errno.h> +#include <errno.h>  #include <fcntl.h>  #include <stdio.h>  #include <string.h> @@ -18,6 +18,8 @@  #include <sys/stat.h>  #include <sys/types.h> +typedef unsigned char uchar; +  extern int SysLogLevel;  #define esyslog(a...) void( (SysLogLevel > 0) ? syslog(LOG_ERR,   a) : void() ) @@ -36,6 +38,9 @@ extern int SysLogLevel;  #define DELETENULL(p) (delete (p), p = NULL) +#define CHECK(s) { if ((s) < 0) LOG_ERROR; } // used for 'ioctl()' calls +#define FATALERRNO (errno != EAGAIN && errno != EINTR) +  template<class T> inline T min(T a, T b) { return a <= b ? a : b; }  template<class T> inline T max(T a, T b) { return a >= b ? a : b; }  template<class T> inline void swap(T &a, T &b) { T t = a; a = b; b = t; } @@ -22,7 +22,7 @@   *   * The project's page is at http://www.cadsoft.de/people/kls/vdr   * - * $Id: vdr.c 1.113 2002/05/20 11:02:10 kls Exp $ + * $Id: vdr.c 1.114 2002/06/16 11:30:28 kls Exp $   */  #include <getopt.h> @@ -31,7 +31,7 @@  #include <stdlib.h>  #include <unistd.h>  #include "config.h" -#include "dvbapi.h" +#include "device.h"  #include "eitscan.h"  #include "i18n.h"  #include "interface.h" @@ -118,15 +118,17 @@ int main(int argc, char *argv[])    int c;    while ((c = getopt_long(argc, argv, "a:c:dD:E:hl:L:mp:P:r:s:t:v:Vw:", long_options, NULL)) != -1) {          switch (c) { -          case 'a': cDvbApi::SetAudioCommand(optarg); +          /*XXX+ +          case 'a': cDevice::SetAudioCommand(optarg);                      break; +                    XXX*/            case 'c': ConfigDirectory = optarg;                      break;            case 'd': DaemonMode = true; break;            case 'D': if (isnumber(optarg)) {                         int n = atoi(optarg); -                       if (0 <= n && n < MAXDVBAPI) { -                          cDvbApi::SetUseDvbApi(n); +                       if (0 <= n && n < MAXDEVICES) { +                          cDevice::SetUseDevice(n);                            break;                            }                         } @@ -323,10 +325,10 @@ int main(int argc, char *argv[])    // DVB interfaces: -  if (!cDvbApi::Initialize()) +  if (!cDevice::Initialize())       return 2; -  cDvbApi::SetPrimaryDvbApi(Setup.PrimaryDVB); +  cDevice::SetPrimaryDevice(Setup.PrimaryDVB);    cSIProcessor::Read(); @@ -343,9 +345,9 @@ int main(int argc, char *argv[])    Channels.SwitchTo(Setup.CurrentChannel);    if (MuteAudio) -     cDvbApi::PrimaryDvbApi->ToggleMute(); +     cDevice::PrimaryDevice()->ToggleMute();    else -     cDvbApi::PrimaryDvbApi->SetVolume(Setup.CurrentVolume, true); +     cDevice::PrimaryDevice()->SetVolume(Setup.CurrentVolume, true);    cEITScanner EITScanner; @@ -371,7 +373,7 @@ int main(int argc, char *argv[])    cOsdObject *Menu = NULL;    cReplayControl *ReplayControl = NULL;    int LastChannel = -1; -  int PreviousChannel = cDvbApi::CurrentChannel(); +  int PreviousChannel = cDevice::CurrentChannel();    time_t LastActivity = 0;    int MaxLatencyTime = 0;    bool ForceShutdown = false; @@ -396,12 +398,12 @@ int main(int argc, char *argv[])                }             }          // Channel display: -        if (!EITScanner.Active() && cDvbApi::CurrentChannel() != LastChannel) { +        if (!EITScanner.Active() && cDevice::CurrentChannel() != LastChannel) {             if (!Menu) -              Menu = new cDisplayChannel(cDvbApi::CurrentChannel(), LastChannel > 0); +              Menu = new cDisplayChannel(cDevice::CurrentChannel(), LastChannel > 0);             if (LastChannel > 0)                PreviousChannel = LastChannel; -           LastChannel = cDvbApi::CurrentChannel(); +           LastChannel = cDevice::CurrentChannel();             }          // Timers and Recordings:          if (!Menu) { @@ -429,11 +431,11 @@ int main(int argc, char *argv[])            case kVolDn:            case kMute:                 if (key == kMute) { -                  if (!cDvbApi::PrimaryDvbApi->ToggleMute() && !Menu) +                  if (!cDevice::PrimaryDevice()->ToggleMute() && !Menu)                       break; // no need to display "mute off"                    }                 else -                  cDvbApi::PrimaryDvbApi->SetVolume(NORMALKEY(key) == kVolDn ? -VOLUMEDELTA : VOLUMEDELTA); +                  cDevice::PrimaryDevice()->SetVolume(NORMALKEY(key) == kVolDn ? -VOLUMEDELTA : VOLUMEDELTA);                 if (!Menu && (!ReplayControl || !ReplayControl->Visible()))                    Menu = cDisplayVolume::Create();                 cDisplayVolume::Process(key); @@ -477,7 +479,7 @@ int main(int argc, char *argv[])                   case osSwitchDvb:                                  DELETENULL(*Interact);                                  Interface->Info(tr("Switching primary DVB...")); -                                cDvbApi::SetPrimaryDvbApi(Setup.PrimaryDVB); +                                cDevice::SetPrimaryDevice(Setup.PrimaryDVB);                                  break;                   case osBack:                   case osEnd:    DELETENULL(*Interact); @@ -490,7 +492,7 @@ int main(int argc, char *argv[])                 switch (key) {                   // Toggle channels:                   case k0: { -                      int CurrentChannel = cDvbApi::CurrentChannel(); +                      int CurrentChannel = cDevice::CurrentChannel();                        Channels.SwitchTo(PreviousChannel);                        PreviousChannel = CurrentChannel;                        break; @@ -511,7 +513,7 @@ int main(int argc, char *argv[])                   case kUp:                   case kDown|k_Repeat:                   case kDown: { -                      int n = cDvbApi::CurrentChannel() + (NORMALKEY(key) == kUp ? 1 : -1); +                      int n = cDevice::CurrentChannel() + (NORMALKEY(key) == kUp ? 1 : -1);                        cChannel *channel = Channels.GetByNumber(n);                        if (channel)                           channel->Switch(); @@ -527,14 +529,16 @@ int main(int argc, char *argv[])            }          if (!Menu) {             EITScanner.Process(); +           /*XXX+             if (!cVideoCutter::Active() && cVideoCutter::Ended()) {                if (cVideoCutter::Error())                   Interface->Error(tr("Editing process failed!"));                else                   Interface->Info(tr("Editing process finished"));                } +              XXX*/             } -        if (!*Interact && ((!cRecordControls::Active() && !cVideoCutter::Active()) || ForceShutdown)) { +        if (!*Interact && ((!cRecordControls::Active() /*XXX+&& !cVideoCutter::Active()XXX*/) || ForceShutdown)) {             time_t Now = time(NULL);             if (Now - LastActivity > ACTIVITYTIMEOUT) {                // Shutdown: @@ -593,16 +597,17 @@ int main(int argc, char *argv[])          }    if (Interrupted)       isyslog("caught signal %d", Interrupted); -  cVideoCutter::Stop(); +  cRecordControls::Shutdown(); +  //XXX+cVideoCutter::Stop();    delete Menu;    delete ReplayControl;    delete Interface;    cOsd::Shutdown();    PluginManager.Shutdown(true); -  Setup.CurrentChannel = cDvbApi::CurrentChannel(); -  Setup.CurrentVolume  = cDvbApi::CurrentVolume(); +  Setup.CurrentChannel = cDevice::CurrentChannel(); +  Setup.CurrentVolume  = cDevice::CurrentVolume();    Setup.Save(); -  cDvbApi::Shutdown(); +  cDevice::Shutdown();    if (WatchdogTimeout > 0)       dsyslog("max. latency time %d seconds", MaxLatencyTime);    isyslog("exiting"); | 
