diff options
| -rw-r--r-- | HISTORY | 7 | ||||
| -rw-r--r-- | Makefile | 41 | ||||
| -rw-r--r-- | config.h | 4 | ||||
| -rw-r--r-- | dvbapi.c | 314 | ||||
| -rw-r--r-- | remux.c | 173 | ||||
| -rw-r--r-- | remux.h | 51 | ||||
| -rw-r--r-- | ringbuffer.c | 170 | ||||
| -rw-r--r-- | ringbuffer.h | 55 | 
8 files changed, 589 insertions, 226 deletions
| @@ -414,7 +414,7 @@ Video Disk Recorder Revision History    given number of seconds. This is mainly useful in combination with the new    'runvdr' script that restarts VDR in case is has exited. -2001-03-02: Version 0.72 +2001-04-01: Version 0.72  - Fixed SVDRP commands LSTC and LSTT to make them return an error message if    no channels or timers are defined. @@ -427,3 +427,8 @@ Video Disk Recorder Revision History  - Fixed internationalization of some Main menu texts.  - Updated 'channels.conf' after the recent changes of Premiere World (thanks    to Axel Gruber). +- Redesigned the ring buffer to make it work with two separate threads for +  input and output (also prepared for using a remultiplexer). +- Fixed setting system time from transponders. +- Fixed a segfault in the Schedule menu in case there is no EPG information. +- The 'runvdr' script now kills any leftover vdr threads before restarting it. @@ -4,13 +4,14 @@  # See the main source file 'vdr.c' for copyright information and  # how to reach the author.  # -# $Id: Makefile 1.20 2001/02/24 15:52:58 kls Exp $ +# $Id: Makefile 1.21 2001/03/18 16:47:00 kls Exp $  DVBDIR   = ../DVB  INCLUDES = -I$(DVBDIR)/driver  OBJS = config.o dvbapi.o dvbosd.o eit.o font.o i18n.o interface.o menu.o osd.o\ -       recording.o remote.o svdrp.o thread.o tools.o vdr.o videodir.o +       recording.o remote.o remux.o ringbuffer.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 @@ -37,26 +38,28 @@ font: genfontfile fontfix.c fontosd.c  # Implicit rules:  %.o: %.c -	g++ -g -O2 -Wall -m486 -c $(DEFINES) $(INCLUDES) $< +	g++ -g -O2 -Wall -Woverloaded-virtual -m486 -c $(DEFINES) $(INCLUDES) $<  # Dependencies: -config.o   : config.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h interface.h remote.h svdrp.h thread.h tools.h -dvbapi.o   : dvbapi.c config.h dvbapi.h dvbosd.h eit.h font.h interface.h recording.h remote.h svdrp.h thread.h tools.h videodir.h -dvbosd.o   : dvbosd.c dvbosd.h font.h tools.h -eit.o      : eit.c config.h dvbapi.h dvbosd.h eit.h font.h thread.h tools.h videodir.h -font.o     : font.c font.h fontfix.c fontosd.c tools.h -i18n.o     : i18n.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h thread.h tools.h -interface.o: interface.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h interface.h remote.h svdrp.h thread.h tools.h -menu.o     : menu.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h interface.h menu.h osd.h recording.h remote.h svdrp.h thread.h tools.h -osd.o      : osd.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h interface.h osd.h remote.h svdrp.h thread.h tools.h -recording.o: recording.c config.h dvbapi.h dvbosd.h eit.h font.h interface.h recording.h remote.h svdrp.h thread.h tools.h videodir.h -remote.o   : remote.c config.h dvbapi.h dvbosd.h eit.h font.h remote.h thread.h tools.h -svdrp.o    : svdrp.c config.h dvbapi.h dvbosd.h eit.h font.h interface.h remote.h svdrp.h thread.h tools.h -thread.o   : thread.c thread.h tools.h -tools.o    : tools.c tools.h -vdr.o      : vdr.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h interface.h menu.h osd.h recording.h remote.h svdrp.h thread.h tools.h videodir.h -videodir.o : videodir.c tools.h videodir.h +config.o    : config.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h interface.h remote.h svdrp.h thread.h tools.h +dvbapi.o    : dvbapi.c config.h dvbapi.h dvbosd.h eit.h font.h interface.h recording.h remote.h remux.h ringbuffer.h svdrp.h thread.h tools.h videodir.h +dvbosd.o    : dvbosd.c dvbosd.h font.h tools.h +eit.o       : eit.c config.h dvbapi.h dvbosd.h eit.h font.h thread.h tools.h videodir.h +font.o      : font.c font.h fontfix.c fontosd.c tools.h +i18n.o      : i18n.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h thread.h tools.h +interface.o : interface.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h interface.h remote.h svdrp.h thread.h tools.h +menu.o      : menu.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h interface.h menu.h osd.h recording.h remote.h svdrp.h thread.h tools.h +osd.o       : osd.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h interface.h osd.h remote.h svdrp.h thread.h tools.h +recording.o : recording.c config.h dvbapi.h dvbosd.h eit.h font.h interface.h recording.h remote.h svdrp.h thread.h tools.h videodir.h +remote.o    : remote.c config.h dvbapi.h dvbosd.h eit.h font.h remote.h thread.h tools.h +remux.o     : remux.c remux.h tools.h +ringbuffer.o: ringbuffer.c ringbuffer.h thread.h tools.h +svdrp.o     : svdrp.c config.h dvbapi.h dvbosd.h eit.h font.h interface.h remote.h svdrp.h thread.h tools.h +thread.o    : thread.c thread.h tools.h +tools.o     : tools.c tools.h +vdr.o       : vdr.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h interface.h menu.h osd.h recording.h remote.h svdrp.h thread.h tools.h videodir.h +videodir.o  : videodir.c tools.h videodir.h  # The main program: @@ -4,7 +4,7 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: config.h 1.42 2001/02/24 13:19:39 kls Exp $ + * $Id: config.h 1.43 2001/03/18 16:47:00 kls Exp $   */  #ifndef __CONFIG_H @@ -19,7 +19,7 @@  #include "eit.h"  #include "tools.h" -#define VDRVERSION "0.71" +#define VDRVERSION "0.72"  #define MaxBuffer 10000 @@ -4,7 +4,7 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: dvbapi.c 1.63 2001/03/14 18:39:53 kls Exp $ + * $Id: dvbapi.c 1.64 2001/03/18 16:47:16 kls Exp $   */  #include "dvbapi.h" @@ -22,6 +22,8 @@ extern "C" {  #include "config.h"  #include "interface.h"  #include "recording.h" +#include "remux.h" +#include "ringbuffer.h"  #include "tools.h"  #include "videodir.h" @@ -29,29 +31,9 @@ extern "C" {  #define VBIDEVICE   "/dev/vbi"  // The size of the array used to buffer video data: +// (must be larger than MINVIDEODATA - see remux.h)  #define VIDEOBUFSIZE (1024*1024) -// The minimum amount of video data necessary to identify frames -// (must be smaller than VIDEOBUFSIZE!): -#define MINVIDEODATA (256*1024) // just a safe guess (max. size of any frame block, plus some safety) - -// The maximum time the buffer is allowed to write data to disk when recording: -#define MAXRECORDWRITETIME 50 // ms - -// Picture types: -#define NO_PICTURE 0 -#define I_FRAME    1 -#define P_FRAME    2 -#define B_FRAME    3 - -// Start codes: -#define SC_PICTURE 0x00  // "picture header" -#define SC_SEQU    0xB3  // "sequence header" -#define SC_PHEAD   0xBA  // "pack header" -#define SC_SHEAD   0xBB  // "system header" -#define SC_AUDIO   0xC0 -#define SC_VIDEO   0xE0 -  #define FRAMESPERSEC 25  // The maximum file size is limited by the range that can be covered @@ -333,7 +315,7 @@ int cIndexFile::Get(uchar FileNumber, int FileOffset)    return -1;  } -// --- cRingBuffer ----------------------------------------------------------- +// --- cRingBuffer_ -----------------------------------------------------------  /* cRingBuffer reads data from an input file, stores it in a buffer and writes     it to an output file upon request. The Read() and Write() functions should @@ -344,7 +326,7 @@ int cIndexFile::Get(uchar FileNumber, int FileOffset)     will be made.  */ -class cRingBuffer { +class cRingBuffer_ {  private:    uchar *buffer;    int size, head, tail, freeLimit, availLimit; @@ -367,8 +349,8 @@ protected:    int FindStartCode(uchar Code, int Offset = 0);    int GetPacketLength(int Offset = 0);  public: -  cRingBuffer(int *InFile, int *OutFile, int Size, int FreeLimit = 0, int AvailLimit = 0); -  virtual ~cRingBuffer(); +  cRingBuffer_(int *InFile, int *OutFile, int Size, int FreeLimit = 0, int AvailLimit = 0); +  virtual ~cRingBuffer_();    virtual int Read(int Max = -1);    virtual int Write(int Max = -1);    bool EndOfFile(void) { return eof; } @@ -377,7 +359,7 @@ public:    void Skip(int n);    }; -cRingBuffer::cRingBuffer(int *InFile, int *OutFile, int Size, int FreeLimit, int AvailLimit) +cRingBuffer_::cRingBuffer_(int *InFile, int *OutFile, int Size, int FreeLimit, int AvailLimit)  {    inFile = InFile;    outFile = OutFile; @@ -393,13 +375,13 @@ cRingBuffer::cRingBuffer(int *InFile, int *OutFile, int Size, int FreeLimit, int       esyslog(LOG_ERR, "ERROR: can't allocate ring buffer (size=%d)", size);  } -cRingBuffer::~cRingBuffer() +cRingBuffer_::~cRingBuffer_()  {    dsyslog(LOG_INFO, "buffer stats: %d free, %d overflows, limit exceeded %d times", minFree, countOverflow, countLimit);    delete buffer;  } -int cRingBuffer::Byte(int Offset) +int cRingBuffer_::Byte(int Offset)  {    if (buffer && Offset < Available()) {       Offset += head; @@ -410,7 +392,7 @@ int cRingBuffer::Byte(int Offset)    return -1;  } -bool cRingBuffer::Set(int Offset, int Length, int Value) +bool cRingBuffer_::Set(int Offset, int Length, int Value)  {    if (buffer && Offset + Length <= Available() ) {       Offset += head; @@ -425,7 +407,7 @@ bool cRingBuffer::Set(int Offset, int Length, int Value)    return false;  } -void cRingBuffer::Skip(int n) +void cRingBuffer_::Skip(int n)  {    if (n > 0) {       if (head < tail) { @@ -443,7 +425,7 @@ void cRingBuffer::Skip(int n)       }  } -int cRingBuffer::Read(int Max) +int cRingBuffer_::Read(int Max)  {    if (buffer) {       eof = false; @@ -501,7 +483,7 @@ int cRingBuffer::Read(int Max)    return -1;  } -int cRingBuffer::Write(int Max) +int cRingBuffer_::Write(int Max)  {    if (buffer) {       int avail = Available(); @@ -540,7 +522,7 @@ int cRingBuffer::Write(int Max)    return -1;  } -int cRingBuffer::FindStartCode(uchar Code, int Offset) +int cRingBuffer_::FindStartCode(uchar Code, int Offset)  {    // Searches for a start code (beginning at Offset) and returns the number    // of bytes from Offset to the start code. @@ -557,7 +539,7 @@ int cRingBuffer::FindStartCode(uchar Code, int Offset)    return -1;  } -int cRingBuffer::GetPacketLength(int Offset) +int cRingBuffer_::GetPacketLength(int Offset)  {    // Returns the entire length of the packet starting at offset.    return (Byte(Offset + 4) << 8) + Byte(Offset + 5) + 6; @@ -671,31 +653,29 @@ int cFileName::NextFile(void)  // --- cRecordBuffer --------------------------------------------------------- -class cRecordBuffer : public cRingBuffer, public cThread { +class cRecordBuffer : public cRingBuffer {  private:    cFileName fileName;    cIndexFile *index; +  cRemux remux;    uchar pictureType;    int fileSize;    int videoDev;    int recordFile; -  bool ok, synced, stop; +  bool recording;    time_t lastDiskSpaceCheck;    bool RunningLowOnDiskSpace(void); -  int ScanVideoPacket(int *PictureType, int Offset); -  int Synchronize(void);    bool NextFile(void); -  virtual int Write(int Max = -1); -  bool WriteWithTimeout(void);  protected: -  virtual void Action(void); +  virtual void Input(void); +  virtual void Output(void);  public:    cRecordBuffer(int *InFile, const char *FileName);    virtual ~cRecordBuffer();    };  cRecordBuffer::cRecordBuffer(int *InFile, const char *FileName) -:cRingBuffer(InFile, &recordFile, VIDEOBUFSIZE, VIDEOBUFSIZE / 10, 0) +:cRingBuffer(VIDEOBUFSIZE)  ,fileName(FileName, true)  {    index = NULL; @@ -703,7 +683,7 @@ cRecordBuffer::cRecordBuffer(int *InFile, const char *FileName)    fileSize = 0;    videoDev = *InFile;    recordFile = fileName.Open(); -  ok = synced = stop = false; +  recording = false;    lastDiskSpaceCheck = time(NULL);    if (!fileName.Name())       return; @@ -712,44 +692,15 @@ cRecordBuffer::cRecordBuffer(int *InFile, const char *FileName)    if (!index)       esyslog(LOG_ERR, "ERROR: can't allocate index");       // let's continue without index, so we'll at least have the recording -  ok = true;    Start();  }  cRecordBuffer::~cRecordBuffer()  { -  stop = true; -  Cancel(3); +  Stop();    delete index;  } -void cRecordBuffer::Action(void) -{ -  dsyslog(LOG_INFO, "recording thread started (pid=%d)", getpid()); - -  time_t t = time(NULL); -  for (;;) { -      usleep(1); // this keeps the CPU load low - -      LOCK_THREAD; - -      int r = Read(); -      if (r >= 0) { -         if (r > 0) -            t = time(NULL); -         if (!WriteWithTimeout()) -            break; -         } -      if (r < 0 || (r == 0 && time(NULL) - t > 5)) { -         esyslog(LOG_ERR, "ERROR: video data stream broken"); -         t = time(NULL); -         } -      } -  SetPlayMode(videoDev, VID_PLAY_RESET); - -  dsyslog(LOG_INFO, "end recording thread"); -} -  bool cRecordBuffer::RunningLowOnDiskSpace(void)  {    if (time(NULL) > lastDiskSpaceCheck + DISKCHECKINTERVAL) { @@ -763,88 +714,6 @@ bool cRecordBuffer::RunningLowOnDiskSpace(void)    return false;  } -int cRecordBuffer::ScanVideoPacket(int *PictureType, int Offset) -{ -  // Scans the video packet starting at Offset and returns its length. -  // If the return value is -1 the packet was not completely in the buffer. - -  int Length = GetPacketLength(Offset); -  if (Length <= Available()) { -     int i = Offset + 8; // the minimum length of the video packet header -     i += Byte(i) + 1;   // possible additional header bytes -     for (; i < Offset + Length; i++) { -         if (Byte(i) == 0 && Byte(i + 1) == 0 && Byte(i + 2) == 1) { -            switch (Byte(i + 3)) { -              case SC_PICTURE: *PictureType = GetPictureType(i); -                               return Length; -              } -            } -         } -     *PictureType = NO_PICTURE; -     return Length; -     } -  return -1; -} - -int cRecordBuffer::Synchronize(void) -{ -  // Positions to the start of a data block (skipping everything up to -  // an I-frame if not synced) and returns the block length. - -  pictureType = NO_PICTURE; - -  //XXX remove this once the buffer is handled with two separate threads: -  if (!synced && Free() < 100000) { -     dsyslog(LOG_INFO, "unable to synchronize, dropped %d bytes", Available()); -     Clear(); -     return 0; -     } -  for (int i = 0; Available() > MINVIDEODATA && i < MINVIDEODATA; i++) { -      if (Byte(i) == 0 && Byte(i + 1) == 0 && Byte(i + 2) == 1) { -         switch (Byte(i + 3)) { -           case SC_VIDEO:   { -                              int pt = NO_PICTURE; -                              int l = ScanVideoPacket(&pt, i); -                              if (l < 0) -                                 return 0; // no useful data found, wait for more -                              if (pt != NO_PICTURE) { -                                 if (pt < I_FRAME || B_FRAME < pt) { -                                    esyslog(LOG_ERR, "ERROR: unknown picture type '%d'", pt); -                                    } -                                 else if (pictureType == NO_PICTURE) { -                                    if (!synced) { -                                       if (pt == I_FRAME) { -                                          Skip(i); -                                          synced = true; -                                          } -                                       else { -                                          Skip(i + l); -                                          i = 0; -                                          break; -                                          } -                                       } -                                    if (synced) -                                       pictureType = pt; -                                    } -                                 else -                                    return i; -                                 } -                              else if (!synced) { -                                 Skip(i + l); -                                 i = 0; -                                 break; -                                 } -                              i += l - 1; // -1 to compensate for i++ in the loop! -                            } -                            break; -           case SC_AUDIO:   i += GetPacketLength(i) - 1; // -1 to compensate for i++ in the loop! -                            break; -           } -         } -      } -  return 0; // no useful data found, wait for more -} -  bool cRecordBuffer::NextFile(void)  {    if (recordFile >= 0 && pictureType == I_FRAME) { // every file shall start with an I_FRAME @@ -856,56 +725,93 @@ bool cRecordBuffer::NextFile(void)    return recordFile >= 0;  } -int cRecordBuffer::Write(int Max) +void cRecordBuffer::Input(void)  { -  // This function ignores the incoming 'Max'! -  // It tries to write out exactly *one* frame block. -  if (!ok) -     return -1; -  int n = Synchronize(); -  if (n) { -     if (stop && pictureType == I_FRAME) { -        ok = false; -        return -1; // finish the recording before the next 'I' frame -        } -     if (NextFile()) { -        if (index && pictureType != NO_PICTURE) -           index->Write(pictureType, fileName.Number(), fileSize); -        int written = 0; -        for (;;) { -            int w = cRingBuffer::Write(n); -            if (w >= 0) { -               fileSize += w; -               written += w; -               n -= w; -               if (n == 0) -                  return written; +  dsyslog(LOG_INFO, "input thread started (pid=%d)", getpid()); + +  uchar b[MINVIDEODATA]; +  time_t t = time(NULL); +  recording = true; +  for (;;) { +      int r = read(videoDev, b, sizeof(b)); +      if (r > 0) { +         uchar *p = b; +         while (r > 0) { +               int w = Put(p, r); +               p += w; +               r -= w;                 } -            else -               return w; +         t = time(NULL); +         } +      else if (r < 0) { +         if (errno != EAGAIN) { +            LOG_ERROR; +            break;              } -        } -     return -1; -     } -  return 0; +         } +      else if (time(NULL) - t > 5) { +         esyslog(LOG_ERR, "ERROR: video data stream broken"); +         t = time(NULL); +         } +      cFile::FileReady(videoDev, 100); +      if (!recording) +         break; +      } +  SetPlayMode(videoDev, VID_PLAY_RESET); + +  dsyslog(LOG_INFO, "input thread ended (pid=%d)", getpid());  } -bool cRecordBuffer::WriteWithTimeout(void) +void cRecordBuffer::Output(void)  { -  int t0 = time_ms(); -  do { -     int w = Write(); -     if (w < 0) -        return false; -     if (w == 0) -        break; -     } while (time_ms() - t0 < MAXRECORDWRITETIME); -  return true; +  dsyslog(LOG_INFO, "output thread started (pid=%d)", getpid()); + +  uchar b[MINVIDEODATA * 2]; +  int r = 0; +  for (;;) { +      usleep(1); // this keeps the CPU load low +      r += Get(b + r, sizeof(b) - r); +      if (r > 0) { +         //XXX buffer full??? +         int Count = r, Result; +         const uchar *p = remux.Process(b, Count, Result, pictureType); +         if (p) { +            if (!Busy() && 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); +               while (Result > 0) { +                     int w = write(recordFile, p, Result); +                     if (w < 0) { +                        LOG_ERROR_STR(fileName.Name()); +                        recording = false; +                        return; +                        } +                     p += w; +                     Result -= w; +                     fileSize += w; +                     } +               } +            else +               break; +            } +         if (Count > 0) { +            r -= Count; +            memmove(b, b + Count, r); +            } +         if (!recording) +            break; +         } +      } +  recording = false; + +  dsyslog(LOG_INFO, "output thread ended (pid=%d)", getpid());  }  // --- cReplayBuffer --------------------------------------------------------- -class cReplayBuffer : public cRingBuffer, public cThread { +class cReplayBuffer : public cRingBuffer_, public cThread {  private:    enum eReplayCmd { rcNone, rcStill, rcPause, rcPlay, rcForward, rcBackward };    enum eReplayMode { rmStill, rmPlay, rmFastForward, rmFastRewind, rmSlowRewind }; @@ -944,7 +850,7 @@ public:    };  cReplayBuffer::cReplayBuffer(int *OutFile, const char *FileName) -:cRingBuffer(&replayFile, OutFile, VIDEOBUFSIZE, 0, VIDEOBUFSIZE / 10) +:cRingBuffer_(&replayFile, OutFile, VIDEOBUFSIZE, 0, VIDEOBUFSIZE / 10)  ,fileName(FileName, false)  {    index = NULL; @@ -1271,7 +1177,7 @@ int cReplayBuffer::Read(int Max = -1)       int readin = 0;       do {          // If Max is > 0 here we need to make sure we read in the entire block! -        int r = cRingBuffer::Read(Max); +        int r = cRingBuffer_::Read(Max);          if (r >= 0)             readin += r;          else @@ -1300,7 +1206,7 @@ int cReplayBuffer::Write(int Max)    if (Max) {       int w;       do { -        w = cRingBuffer::Write(Max); +        w = cRingBuffer_::Write(Max);          if (w >= 0) {             fileOffset += w;             Written += w; @@ -1348,7 +1254,7 @@ void cTransferBuffer::Action(void)  {    dsyslog(LOG_INFO, "data transfer thread started (pid=%d)", getpid()); -  cRingBuffer Buffer(&fromDevice, &toDevice, VIDEOBUFSIZE, 0, 0); +  cRingBuffer_ Buffer(&fromDevice, &toDevice, VIDEOBUFSIZE, 0, 0);    active = true;    while (active && Buffer.Available() < 100000) { // need to give the read buffer a head start          Buffer.Read(); // initializes fromDevice for reading @@ -1364,7 +1270,7 @@ void cTransferBuffer::Action(void)  // --- cCuttingBuffer -------------------------------------------------------- -class cCuttingBuffer : public cRingBuffer, public cThread { +class cCuttingBuffer : public cRingBuffer_, public cThread {  private:    bool active;    int fromFile, toFile; @@ -1379,7 +1285,7 @@ public:    };  cCuttingBuffer::cCuttingBuffer(const char *FromFileName, const char *ToFileName) -:cRingBuffer(&fromFile, &toFile, VIDEOBUFSIZE, 0, VIDEOBUFSIZE / 10) +:cRingBuffer_(&fromFile, &toFile, VIDEOBUFSIZE, 0, VIDEOBUFSIZE / 10)  {    active = false;    fromFile = toFile = -1; @@ -1438,7 +1344,7 @@ void cCuttingBuffer::Action(void)                   CurrentFileNumber = FileNumber;                   }                if (fromFile >= 0) -                 Length = cRingBuffer::Read(Length); +                 Length = cRingBuffer_::Read(Length);                else                   break;                } @@ -1456,7 +1362,7 @@ void cCuttingBuffer::Action(void)                   }                LastIFrame = 0;                } -           cRingBuffer::Write(Length); +           cRingBuffer_::Write(Length);             toIndex->Write(PictureType, toFileName->Number(), FileSize);             FileSize += Length;             if (!LastIFrame) diff --git a/remux.c b/remux.c new file mode 100644 index 00000000..e48296d8 --- /dev/null +++ b/remux.c @@ -0,0 +1,173 @@ +/* + * remux.c: A streaming MPEG2 remultiplexer + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * $Id: remux.c 1.1 2001/03/31 08:42:17 kls Exp $ + */ + +/* The calling interface of the 'cRemux::Process()' function is defined +   as follows: + +   'Data' points to a chunk of data that consists of 'Count' bytes. +   The 'Process' function shall try to remultiplex as much of the +   data as possible and return a pointer to the resulting buffer. +   That buffer typically is different from the incoming 'Data', +   but in the simplest case (when 'Process' does nothing) might +   as well point to the original 'Data'. When returning, 'Count' +   shall be set to the number of bytes that have been processed +   (i.e. have been taken from 'Data'), while 'Result' indicates +   how many bytes the returned buffer contains. 'PictureType' shall +   be set to NO_PICTURE if the returned data does not start a new +   picture, or one of I_FRAME, P_FRAME or B_FRAME if a new picture +   starting point has been found. This also means that the returned +   data buffer may contain at most one entire video frame, because +   the next frame must be returned with its own value for 'PictureType'. + +   'Process' shall do it's best to keep the latency time as short +   as possible in order to allow a quick start of VDR's "Transfer +   mode" (displaying the signal of one DVB card on another card). +   In order to do that, this function may decide to first pass +   through the incoming data (almost) unprocessed, and make +   actual processing kick in after a few seconds (if that is at +   all possible for the algorithm). This may result in a non- +   optimal stream at the beginning, which won't matter for normal +   recordings but may make switching through encrypted channels +   in "Transfer mode" faster. + +   In the resulting data stream, a new packet shall always be started +   when a frame border is encountered. VDR needs this in order to +   be able to detect and store the frame indexes, and to easily +   display single frames in fast forward/back mode. The very first +   data block returned shall be the starting point of an I_FRAME. +   Everything before that shall be silently dropped. + +   If the incoming data is not enough to do remultiplexing, a value +   of NULL shall be returned ('Result' has no meaning then). This +   will tell the caller to wait for more data to be presented in +   the next call. If NULL is returned and 'Count' is not 0, the +   caller shall remove 'Count' bytes from the beginning of 'Data' +   before the next call. This is the way 'Process' indicates that +   it must skip that data. + +   Any data that is not used during this call will appear at the +   beginning of the incoming 'Data' buffer at the next call, plus +   any new data that has become available. + +   It is guaranteed that the caller will completely process any +   returned data before the next call to 'Process'. That way, 'Process' +   can dynamically allocate its return buffer and be sure the caller +   doesn't keep any pointers into that buffer. +*/ + +#include "remux.h" +#include "tools.h" + +#if defined(REMUX_NONE) + +cRemux::cRemux(void) +{ +  synced = false; +} + +cRemux::~cRemux() +{ +} + +int cRemux::GetPacketLength(const uchar *Data, int Count, int Offset) +{ +  // Returns the entire length of the packet starting at offset, or -1 in case of error. +  return (Offset + 5 < Count) ? (Data[Offset + 4] << 8) + Data[Offset + 5] + 6 : -1; +} + +int cRemux::ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType) +{ +  // Scans the video packet starting at Offset and returns its length. +  // If the return value is -1 the packet was not completely in the buffer. + +  int Length = GetPacketLength(Data, Count, Offset); +  if (Length > 0 && Offset + Length <= Count) { +     int i = Offset + 8; // the minimum length of the video packet header +     i += Data[i] + 1;   // possible additional header bytes +     for (; i < Offset + Length; i++) { +         if (Data[i] == 0 && Data[i + 1] == 0 && Data[i + 2] == 1) { +            switch (Data[i + 3]) { +              case SC_PICTURE: PictureType = (Data[i + 5] >> 3) & 0x07; +                               return Length; +              } +            } +         } +     PictureType = NO_PICTURE; +     return Length; +     } +  return -1; +} + +const uchar *cRemux::Process(const uchar *Data, int &Count, int &Result, uchar &PictureType) +{ +  int Skip = 0; + +  PictureType = NO_PICTURE; + +  if (Count >= MINVIDEODATA) { +     for (int i = 0; i < Count; i++) { +         if (Data[i] == 0 && Data[i + 1] == 0 && Data[i + 2] == 1) { +            switch (Data[i + 3]) { +              case SC_VIDEO: +                   { +                     uchar pt = NO_PICTURE; +                     int l = ScanVideoPacket(Data, Count, i, pt); +                     if (l < 0) { +                        if (Skip < Count) +                           Count = Skip; +                        return NULL; // no useful data found, wait for more +                        } +                     if (pt != NO_PICTURE) { +                        if (pt < I_FRAME || B_FRAME < pt) { +                           esyslog(LOG_ERR, "ERROR: unknown picture type '%d'", pt); +                           } +                        else if (PictureType == NO_PICTURE) { +                           if (!synced) { +                              if (pt == I_FRAME) { +                                 Skip = i; +                                 synced = true; +                                 } +                              else { +                                 i += l; +                                 Skip = i; +                                 break; +                                 } +                              } +                           if (synced) +                              PictureType = pt; +                           } +                        else { +                           Count = i; +                           Result = i - Skip; +                           return Data + Skip; +                           } +                        } +                     else if (!synced) { +                        i += l; +                        Skip = i; +                        break; +                        } +                     i += l - 1; // -1 to compensate for i++ in the loop! +                   } +                   break; +              case SC_AUDIO: +                   i += GetPacketLength(Data, Count, i) - 1; // -1 to compensate for i++ in the loop! +                   break; +              } +            } +         } +     } +  if (Skip < Count) +     Count = Skip; +  return NULL; // no useful data found, wait for more +} + +#elif defined(REMUX_TEST) +#endif + diff --git a/remux.h b/remux.h new file mode 100644 index 00000000..bceb676b --- /dev/null +++ b/remux.h @@ -0,0 +1,51 @@ +/* + * remux.h: A streaming MPEG2 remultiplexer + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * $Id: remux.h 1.1 2001/03/31 08:42:27 kls Exp $ + */ + +#ifndef __REMUX_H +#define __REMUX_H + +// There are various experiments with different types of remultiplexers +// going on at the moment. Select the remultiplexer here: +#define REMUX_NONE 1 +//#define REMUX_TEST 1 + +// Picture types: +#define NO_PICTURE 0 +#define I_FRAME    1 +#define P_FRAME    2 +#define B_FRAME    3 + +// Start codes: +#define SC_PICTURE 0x00  // "picture header" +#define SC_SEQU    0xB3  // "sequence header" +#define SC_PHEAD   0xBA  // "pack header" +#define SC_SHEAD   0xBB  // "system header" +#define SC_AUDIO   0xC0 +#define SC_VIDEO   0xE0 + +// The minimum amount of video data necessary to identify frames: +#define MINVIDEODATA (256*1024) // just a safe guess (max. size of any frame block, plus some safety) + +typedef unsigned char uchar; + +class cRemux { +private: +#if defined(REMUX_NONE) +  bool synced; +  int GetPacketLength(const uchar *Data, int Count, int Offset); +  int ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType); +#elif defined(REMUX_TEST) +#endif +public: +  cRemux(void); +  ~cRemux(); +  const uchar *Process(const uchar *Data, int &Count, int &Result, uchar &PictureType); +  }; + +#endif // __REMUX_H diff --git a/ringbuffer.c b/ringbuffer.c new file mode 100644 index 00000000..3b2f5cac --- /dev/null +++ b/ringbuffer.c @@ -0,0 +1,170 @@ +/* + * ringbuffer.c: A threaded ring buffer + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * Parts of this file were inspired by the 'ringbuffy.c' from the + * LinuxDVB driver (see linuxtv.org). + * + * $Id: ringbuffer.c 1.1 2001/03/18 16:47:00 kls Exp $ + */ + +#include "ringbuffer.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(int Size) +{ +  size = Size; +  buffer = NULL; +  inputThread = NULL; +  outputThread = NULL; +  maxFill = 0; +  busy = false; +  if (size > 1) { // 'size - 1' must not be 0! +     buffer = new uchar[size]; +     if (!buffer) +        esyslog(LOG_ERR, "ERROR: can't allocate ring buffer (size=%d)", size); +     Clear(); +     } +  else +     esyslog(LOG_ERR, "ERROR: illegal size for ring buffer (%d)", size); +} + +cRingBuffer::~cRingBuffer() +{ +  delete inputThread; +  delete outputThread; +  delete buffer; +  dsyslog(LOG_INFO, "buffer stats: %d (%d%%) used", maxFill, maxFill * 100 / (size - 1)); +} + +void cRingBuffer::Clear(void) +{ +  mutex.Lock(); +  head = tail = 0; +  mutex.Unlock(); +} + +int cRingBuffer::Put(const uchar *Data, int Count) +{ +  if (Count > 0) { +     mutex.Lock(); +     int rest = size - head; +     int diff = tail - head; +     mutex.Unlock(); +     int free = (diff > 0) ? diff - 1 : size + diff - 1; +     // Statistics: +     int fill = size - free - 1 + Count; +     if (fill >= size) +        fill = size - 1; +     if (fill > maxFill) { +        maxFill = fill; +        int percent = maxFill * 100 / (size - 1); +        if (percent > 75) +           dsyslog(LOG_INFO, "buffer usage: %d%%", percent); +        } +     // +     if (free <= 0) +        return 0; +     if (free < Count) +        Count = free; +     if (Count > maxFill) +        maxFill = Count; +     if (Count >= rest) { +        memcpy(buffer + head, Data, rest); +        if (Count - rest) +           memcpy(buffer, Data + rest, Count - rest); +        head = Count - rest; +        } +     else { +        memcpy(buffer + head, Data, Count); +        head += Count; +        } +     } +  return Count; +} + +int cRingBuffer::Get(uchar *Data, int Count) +{ +  if (Count > 0) { +     mutex.Lock(); +     int rest = size - tail; +     int diff = head - tail; +     mutex.Unlock(); +     int cont = (diff >= 0) ? diff : size + diff; +     if (rest <= 0) +        return 0; +     if (cont < Count) +        Count = cont; +     if (Count >= rest) { +        memcpy(Data, buffer + tail, rest); +        if (Count - rest) +           memcpy(Data + rest, buffer, Count - rest); +        tail = Count - rest; +        } +     else { +        memcpy(Data, buffer + tail, Count); +        tail += Count; +        } +     } +  return Count; +} + +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); +} + diff --git a/ringbuffer.h b/ringbuffer.h new file mode 100644 index 00000000..605f553f --- /dev/null +++ b/ringbuffer.h @@ -0,0 +1,55 @@ +/* + * ringbuffer.h: A threaded ring buffer + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * $Id: ringbuffer.h 1.1 2001/03/18 16:47:00 kls Exp $ + */ + +#ifndef __RINGBUFFER_H +#define __RINGBUFFER_H + +#include "thread.h" + +typedef unsigned char uchar; + +class cRingBufferInputThread; +class cRingBufferOutputThread; + +class cRingBuffer { +  friend class cRingBufferInputThread; +  friend class cRingBufferOutputThread; +private: +  cRingBufferInputThread *inputThread; +  cRingBufferOutputThread *outputThread; +  cMutex mutex; +  int size, head, tail; +  uchar *buffer; +  int maxFill; +  bool busy; +protected: +  bool Busy(void) { return busy; } +  void Clear(void); +    // Immediately clears the ring buffer. +  int Put(const uchar *Data, int Count); +    // Puts at most Count bytes of Data into the ring buffer. +    // Returns the number of bytes actually stored. +  int Get(uchar *Data, int Count); +    // Gets at most Count bytes of Data from the ring buffer. +    // Returns the number of bytes actually retrieved. +  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); +  virtual ~cRingBuffer(); +  bool Start(void); +  bool Active(void); +  void Stop(void); +  }; + +#endif // __RINGBUFFER_H | 
