Mailing List archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[vdr] Re: LiveAC3-Patch for vdr1.0.0pre4



On Thu, Mar 28, 2002 at 12:51:59PM +0100, Andreas Wohlfeld wrote:
> On Wed, Mar 27, 2002 at 10:50:22PM +0100, Roland Praml wrote:
> > I've made a new LiveAC3-Patch:
> > http://bus.ath.cx/~praml/vdr-1.0.0pre4-liveac3.patch.gz
> > 
> > Download VDR
> > Apply the AIO-Patch (maybe it works also without)
> 
> (it doesn't)
> 
> > compile with LIVEAC3=1
> > 
> I did and Pro 7 works (at least with DD 2.0), but when switching to 
> Cinedome Delux vdr display the channel info on the OSD and then exits
> with segmentation fault, even with out the -a option.

Is Cinedome Delux viewed in Transfer Mode (other Card than Primary)?
If so I'm suppose that there is missing a

#ifdef LIVEAC3SUPPORT
  EndBypassAC3(true);
#endif

at the place where Tranfer is started (the first few things which
are done in that case are

  StopTransfer();
  StopReplay();

and here should also be called

#ifdef LIVEAC3SUPPORT
  EndBypassAC3(true);
#endif

and afterwards something like

#ifdef LIVEAC3SUPPORT
  if (ResumeLiveAC3)
     BypassAC3();
#endif

should be called to resume AC3 eventually.
There should be only on pipe be opened to ac3play/ac3dec otherwise
it will crash.

I'll append my patch against vdr-0.99pre4+AIO-ELCHI-31.01 and some
other fixes.  This one uses asynchrony I/O support of the glibc
which is part olf the librt.so (therefore -lrt on linkage is
required) which should lead to a more synchrony AC3 in comparison
to the lips during talks.

If Klaus is ready with VDR 1.0.0 and I'll find some time then
I'll do the port to 1.0.0 if not done elsewhere :^)


       Werner


-------------------------------------------------------------------------
--- Makefile
+++ Makefile	Thu Mar 21 12:34:04 2002
@@ -80,6 +82,8 @@
 
 # END MP3SUPPORT
 
+DEFINES += -DUSE_AIO
+
 all: vdr
 font: genfontfile fontfix.c fontosd.c
 	@echo "font files created."
@@ -101,7 +105,7 @@
 # The main program:
 
 vdr: $(OBJS) $(AC3LIB) $(DTVLIB)
-	g++ -g -O2 $(OBJS) $(NCURSESLIB) -ljpeg -lpthread $(LIBDIRS) $(DVDLIB) $(AC3LIB) $(DTVLIB) -o vdr
+	g++ -g -O2 $(OBJS) $(NCURSESLIB) -ljpeg -lpthread -lrt $(LIBDIRS) $(DVDLIB) $(AC3LIB) $(DTVLIB) -o vdr
 
 # The font files:
 
--- dvbapi.c
+++ dvbapi.c	Wed Mar 27 12:20:10 2002
@@ -22,6 +22,9 @@
 #define HAVE_BOOLEAN
 #include <jpeglib.h>
 }
+#if defined(USE_AIO)
+#include <aio.h>
+#endif
 #include <stdlib.h>
 #include <sys/ioctl.h>
 #include <sys/mman.h>
@@ -734,26 +737,53 @@
   canToggleAudioTrack = false;
   skipAC3bytes = false;
   audioTrack = 0xC0;
-  if (cDvbApi::AudioCommand()) {
-     if (!dolbyDev.Open(cDvbApi::AudioCommand(), "w"))
-        esyslog(LOG_ERR, "ERROR: can't open pipe to audio command '%s'", cDvbApi::AudioCommand());
-     }
 }
 
+#if defined(USE_AIO)
+static aFile dolbyAIO;
+#endif
+static volatile bool LiveAC3 = false;	// Global to check if LiveAC3 thread is running
+static cPipe dolbyDev;
+
 cPlayBuffer::~cPlayBuffer()
 {
+  if (dolbyDev)
+     dolbyDev.Close();			// Explicit close because, dolbyDev is a global
 }
 
-void cPlayBuffer::PlayExternalDolby(const uchar *b, int MaxLength)
+static void PlayExternalDolby(const uchar *b, int MaxLength, boolean skipAC3bytes)
 {
-  if (dolbyDev) {
+  if (cDvbApi::AudioCommand()) {
+     if (!dolbyDev) {
+        if (!dolbyDev.Open(cDvbApi::AudioCommand(), "w")) {
+           esyslog(LOG_ERR, "ERROR: can't open pipe to audio command '%s'", cDvbApi::AudioCommand());
+           return;
+           }
+#if defined(USE_AIO)
+        if (LiveAC3) {
+	   bool ret = dolbyAIO.Open(fileno(dolbyDev), "w");
+	   if (!ret)
+	      LOG_ERROR;;
+           usleep(100);
+           }
+#endif
+        }
+
      if (b[0] == 0x00 && b[1] == 0x00 && b[2] == 0x01) {
         if (b[3] == 0xBD) { // dolby
-           int l = b[4] * 256 + b[5] + 6;
+           int l = ((b[4] << 8) | b[5]) + 6;
            int written = b[8] + (skipAC3bytes ? 13 : 9); // skips the PES header
            int n = min(l - written, MaxLength);
+#if defined(USE_AIO)
+           if (dolbyAIO) {
+              bool ret = dolbyAIO.Write(&b[written], n);
+              if (!ret)
+                 LOG_ERROR;
+              return;
+              }
+#endif
            while (n > 0) {
-                 int w = fwrite(&b[written], 1, n, dolbyDev);
+                 long w = fwrite(&b[written], 1, n, dolbyDev);
                  if (w < 0) {
                     LOG_ERROR;
                     break;
@@ -766,6 +796,11 @@
      }
 }
 
+void cPlayBuffer::PlayExternalDolby(const uchar *b, int MaxLength)
+{
+  std::PlayExternalDolby(b, MaxLength, skipAC3bytes);
+}
+
 void cPlayBuffer::Output(void)
 {
   dsyslog(LOG_INFO, "output thread started (pid=%d)", getpid());
@@ -1164,20 +1199,17 @@
      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;
+            int l = ((b[i + 4] << 8) | b[i + 5]) + 6;
             switch (c) {
               case 0xBD: // dolby
-                   if (Except && dolbyDev)
+                   if (Except)
                       PlayExternalDolby(&b[i], Length - i);
                    // continue with deleting the data - otherwise it disturbs DVB replay
               case 0xC0 ... 0xC1: // audio
                    if (c == 0xC1)
                       canToggleAudioTrack = true;
-                   if (!Except || c != Except) {
-                      int n = l;
-                      for (int j = i; j < Length && n--; j++)
-                          b[j] = 0x00;
-                      }
+                   if (!Except || c != Except)
+                      memset(&b[i], 0x00, min(l, Length-i));
                    break;
               case 0xE0 ... 0xEF: // video
                    break;
@@ -1563,6 +1595,209 @@
   return NULL;
 }
 
+// --- cLiveAC3 --------------------------------------------------------------
+
+void cLiveAC3::Action(void)
+{
+  dsyslog(LOG_INFO, "LiveAC3 thread started (pid=%d)", getpid());
+  LiveAC3 = true;
+
+  // We increase priority to decrease delays
+  struct sched_param param;
+  param.sched_priority = sched_get_priority_max(SCHED_RR);
+  if (pthread_setschedparam(pthread_self(), SCHED_RR, &param) < 0)
+     esyslog(LOG_ERR, "LiveAC3 thread can not set scheduling priority: %s", strerror(errno));
+  if (nice(-20) < 0)
+     esyslog(LOG_ERR, "LiveAC3 thread can not set process priority: %s", strerror(errno));
+  // Free resources immediately on exit
+  pthread_detach(pthread_self());
+
+  while (active) {
+        int header = 2048;
+        int bytes  = 0;
+        int length = 0;
+        ssize_t already = 0;
+        uchar * ptr = &buffer[0];
+        uchar * start = NULL;
+
+        while (header > 0 && active) {
+              cFile::FileReady(fd_dolby, 30);	// One AC3 frame represents 32ms
+              ssize_t r = read(fd_dolby, ptr, header);
+              if (r < 0) {
+                 if (FATALERRNO) {
+                    if (errno == EBUFFEROVERFLOW) {
+                       esyslog(LOG_ERR, "ERROR (%s,%d): DVB driver buffer overflow", __FILE__, __LINE__);
+                       goto __retry;
+                       }
+                    else {
+                       LOG_ERROR;
+                       goto __exit;
+                       }
+                    }
+                 continue;
+                 }
+              header  -= r;
+              ptr     += r;
+              already += r;
+
+              if (already > 5)			// If we've read that many bytes we're
+                 break;				// ready for scanning for 0xBD header
+              }
+
+        start = &buffer[0];
+        while (start - &buffer[0] <= (2048 - 6)) {
+              if (start[0] == 0x00 && start[1] == 0x00 && start[2] == 0x01 && start[3] == 0xBD) {
+                 length = ((start[4] << 8) | start[5]) + 6;
+                 break;
+                 }
+              start++;
+              }
+
+        if (!length)
+           goto __retry;
+
+        bytes = ((start - &buffer[0]) + length) - already;
+        if (bytes < 0) {			// This is a second frame (may happen with SCHED_FIFO)
+           already = already - length;
+           StripAudioPackets(start, length);
+           start += length;
+           if (start[0] == 0x00 && start[1] == 0x00 && start[2] == 0x01 && start[3] == 0xBD) {
+              length = ((start[4] << 8) | start[5]) + 6;
+              bytes = length - already;
+              }
+           else
+              goto __retry;
+           }
+
+        while (bytes > 0 && active) {
+              cFile::FileReady(fd_dolby, 10);
+              ssize_t r = read(fd_dolby, ptr, bytes);
+              if (r < 0) {
+                 if (FATALERRNO) {
+                    if (errno == EBUFFEROVERFLOW) {
+                       esyslog(LOG_ERR, "ERROR (%s,%d): DVB driver buffer overflow", __FILE__, __LINE__);
+                       goto __retry;
+                       }
+                    else {
+                       LOG_ERROR;
+                       goto __exit;
+                       }
+                    }
+                 continue;
+                 }
+              bytes -= r;
+              ptr   += r;
+              }
+
+        StripAudioPackets(start, length);
+__retry:
+        memset(buffer, 0x00, sizeof(buffer));
+  }
+
+__exit:
+#if defined(USE_AIO)
+  if (dolbyAIO)
+      dolbyAIO.Close(false); // Do not close file descriptor
+#endif
+  if (dolbyDev)
+    dolbyDev.Close();
+
+  // Eat up the buffer, avoids DVB driver buffer overflow on next start.
+  ssize_t r;
+  while ((r = read(fd_dolby, &buffer[0], 2048))) {
+     if (r < 0 && errno != EINTR)
+        break;
+     usleep(10);
+  }
+
+  LiveAC3 = false;
+  dsyslog(LOG_INFO, "LiveAC3 thread ended (pid=%d)", getpid());
+}
+
+bool cLiveAC3::SetDpid1(int Dpid, dmxOutput_t Output)
+{
+  if (Dpid) {
+     CHECK(ioctl(fd_dolby, DMX_STOP));
+     dmxPesFilterParams pesFilterParams;
+     pesFilterParams.pid     = Dpid;
+     pesFilterParams.input   = DMX_IN_FRONTEND;
+     pesFilterParams.output  = Output;
+     pesFilterParams.pesType = DMX_PES_OTHER;
+     pesFilterParams.flags   = 0;
+     if (ioctl(fd_dolby, DMX_SET_PES_FILTER, &pesFilterParams) < 0) {
+        if (Dpid != 0x1FFF)
+           LOG_ERROR;
+        return false;
+        }
+     if (Dpid != 0x1FFF) {
+        CHECK(ioctl(fd_dolby, DMX_SET_BUFFER_SIZE, 8192));
+        CHECK(ioctl(fd_dolby, DMX_START, 0));
+        }
+     else {
+#if 0
+	// Can cause an oops due driver bug.
+        CHECK(ioctl(fd_dolby, DMX_SET_BUFFER_SIZE, 0));
+#endif
+        CHECK(ioctl(fd_dolby, DMX_START, 0));
+        }
+     }
+  return true;
+}
+
+cLiveAC3::cLiveAC3(int fd, int Dpid)
+{
+  memset(buffer, 0x00, sizeof(buffer));
+  fd_dolby = fd;
+  active = true;
+  SetDpid1(Dpid, DMX_OUT_TAP);
+  Start();
+}
+
+cLiveAC3::~cLiveAC3(void)
+{
+  active = false;
+  Cancel(1);
+}
+
+void cLiveAC3::Close(void)
+{
+  active = false;
+  SetDpid1(0x1FFF, DMX_OUT_DECODER);
+  while (LiveAC3)
+        usleep(1000);
+}
+
+void cLiveAC3::StripAudioPackets(uchar *b, int Length, uchar Except = 0x00)
+{
+     if (b[0] == 0x00 && b[1] == 0x00 && b[2] == 0x01) {
+        switch (b[3]) {
+           case 0xBD: // dolby
+              std::PlayExternalDolby(&b[0], Length-6, false);
+              break;
+           default:
+              dsyslog(LOG_INFO, "LiveAC3 has seen broken frame");
+              break;
+           }
+        }
+}
+
+void cDvbApi::BypassAC3(void)
+{
+  if (!LiveAC3) {
+     LiveAC3 = new cLiveAC3(fd_demuxd1, dPid1);
+     ResumeLiveAC3 = false;
+     }
+}
+
+void cDvbApi::EndBypassAC3(boolean resume)
+{
+  if (LiveAC3) {
+     LiveAC3->Close();
+     DELETENULL(LiveAC3);
+     ResumeLiveAC3 = resume;
+     }
+}
+
 // --- cDVDplayBuffer --------------------------------------------------------
 
 class cDVDplayBuffer : public cPlayBuffer {
@@ -2587,7 +2822,7 @@
      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;
+           int l = ((b[i+4] << 8) | b[i+5]) + 6;
            switch (c) {
               case 0xC0 ... 0xC1:
                  if (c==0xC1)
@@ -2762,7 +2997,7 @@
         int r = Get(b, sizeof(b));
         if (r > 0) {
            uchar *p = b;
-           while (r > 0 && Busy()) {
+           while (r > 0 && Busy() && cFile::FileReadyForWriting(toDevice, 100)) {
                  int w = write(toDevice, p, r);
                  if (w > 0) {
                     p += w;
@@ -3038,6 +3273,8 @@
   replayBuffer = NULL;
   transferBuffer = NULL;
   transferringFromDvbApi = NULL;
+  LiveAC3 = NULL;
+  ResumeLiveAC3 = false;
   ca = 0;
   priority = -1;
   cardIndex = n;
@@ -3112,6 +3349,7 @@
   StopReplay();
   StopRecord();
   StopTransfer();
+  EndBypassAC3();
   // We're not explicitly closing any device files here, since this sometimes
   // caused segfaults. Besides, the program is about to terminate anyway...
 #if defined(DEBUG_OSD) || defined(REMOTE_KBD)
@@ -3658,6 +3896,10 @@
 
   if (!NeedsTransferMode) {
 
+     // Stop AC3 live mode
+     if (this == PrimaryDvbApi)
+        EndBypassAC3();
+
      // Turn off current PIDs:
 
      SetVpid( 0x1FFF, DMX_OUT_DECODER);
@@ -3775,6 +4017,10 @@
      SetTpid(Tpid, DMX_OUT_DECODER);
      if (fd_audio >= 0)
         CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true));
+
+     // Start AC3 live mode
+     if ((this == PrimaryDvbApi) && (dPid1))
+	BypassAC3();
      }
 
   if (this == PrimaryDvbApi && siProcessor)
@@ -3856,8 +4102,8 @@
      }
 
   StopTransfer();
-
   StopReplay(); // TODO: remove this if the driver is able to do record and replay at the same time
+  EndBypassAC3(true);
 
   // Check FileName:
 
@@ -3899,6 +4145,8 @@
      ca = 0;
      priority = -1;
      }
+  if (ResumeLiveAC3)
+     BypassAC3();
 }
 
 bool cDvbApi::StartReplay(const char *FileName)
@@ -3909,6 +4157,7 @@
      }
   StopTransfer();
   StopReplay();
+  EndBypassAC3(true);
   if (fd_video >= 0 && fd_audio >= 0) {
 
      // Check FileName:
@@ -3961,6 +4210,7 @@
   if(!Recording()) {
     StopTransfer();
     StopReplay();
+    EndBypassAC3(true);
     if(FileName && fd_video >= 0 && fd_audio >= 0) {
       isyslog(LOG_INFO, "starting MPlayer with file %s", FileName);
       replayBuffer = new cMPlayBuffer(this, fd_video, fd_audio, FileName);
@@ -3991,6 +4241,7 @@
      }
   StopTransfer();
   StopReplay();
+  EndBypassAC3(true);
   if (fd_video >= 0 && fd_audio >= 0) {
 
      // Check DeviceName:
@@ -4021,6 +4272,7 @@
      }
   StopTransfer();
   StopReplay();
+  EndBypassAC3(true);
   if (fd_video >= 0 && fd_audio >= 0) {
 
      // Check Device:
@@ -4056,6 +4308,8 @@
            }
         }
      }
+  if (ResumeLiveAC3)
+     BypassAC3();
 }
 
 void cDvbApi::Pause(void)
--- dvbapi.h
+++ dvbapi.h	Tue Mar 26 11:32:41 2002
@@ -14,6 +14,7 @@
 #include <ncurses.h>
 #endif
 #include <dirent.h>
+#include <signal.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
@@ -79,6 +80,7 @@
 #endif //VCDSUPPORT
 class cTransferBuffer;
 class cCuttingBuffer;
+class cLiveAC3;
 
 #include "spu.h"
 
@@ -368,6 +370,13 @@
        // Sets the volume to the given value, either absolutely or relative to
        // the current volume.
   static int CurrentVolume(void) { return PrimaryDvbApi ? PrimaryDvbApi->volume : 0; }
+
+  // AC3 Live bypass
+private:
+  cLiveAC3 *LiveAC3;
+  boolean ResumeLiveAC3;
+  void BypassAC3(void);
+  void EndBypassAC3(boolean resume = false);
   };
 
 #define AVG_FRAME_SIZE 15000         // an assumption about the average frame size
@@ -395,7 +404,6 @@
   static int Speeds[];
   cDvbApi *dvbApi;
   int videoDev, audioDev;
-  cPipe dolbyDev;
   int blockInput, blockOutput;
   ePlayModes playMode;
   ePlayDirs playDir;
@@ -450,8 +458,20 @@
   void Process(void);
   };
 
+class cLiveAC3 : public cThread {
+private:
+  sigset_t oldset;
+  bool active;
+  bool busy;
+  uint8_t buffer[6144];
+  int fd_dolby;
+  virtual void StripAudioPackets(uchar *b, int Length, uchar Except = 0x00);
+  virtual bool SetDpid1(int Dpid, dmxOutput_t Output);
+protected:
+  virtual void Action(void);
+public:
+  cLiveAC3(int fd, int pid);
+  virtual ~cLiveAC3(void);
+  void Close(void);
+  };
 #endif //__DVBAPI_H
-
-
-
-
--- tools.c
+++ tools.c	Tue Mar 26 12:13:27 2002
@@ -11,11 +11,13 @@
 #include <ctype.h>
 #include <dirent.h>
 #include <errno.h>
+#include <error.h>
 #if defined(DEBUG_OSD)
 #include <ncurses.h>
 #endif
 #include <stdlib.h>
 #include <sys/time.h>
+#include <sys/stat.h>
 #include <time.h>
 #include <unistd.h>
 #include "i18n.h"
@@ -846,7 +912,246 @@
      } while (swapped);
 }
 
+// --- Afile -----------------------------------------------------------------
+
+void* aFile::memcpyrealloc(void *dest, const void *src, size_t n)
+{
+    if (!src)
+	return NULL;
+    dest = realloc(dest, n);
+    if (dest)
+	memcpy(dest, src, n);
+    else
+	esyslog(LOG_ERR, "realloc error: %s", strerror(errno));
+    return dest;
+}
+
+ssize_t aFile::waiton_aio(int allowed_err)
+{
+    ssize_t res = 0;
+    struct aiocb *cbp[1];
+
+    cbp[0] = &af->cbs;
+    while (1) {
+	int err = 0;
+	int ret = aio_suspend ((const struct aiocb *const *) cbp, 1, NULL);
+	if (ret < 0) {
+	    if (errno == EAGAIN || errno == EINTR)
+		continue;
+	    esyslog(LOG_ERR, "aio_suspend error: %s", strerror(errno));
+	    exit(1);
+	}
+
+	err = aio_error(cbp[0]);
+	if (err == EINPROGRESS)
+	    continue;
+	else {
+	    res = aio_return(cbp[0]);
+	    if (res < 0) {
+		if (allowed_err == 0 || err != allowed_err)
+		    esyslog(LOG_ERR, "aio_return failed");
+		break;
+	    }
+	    af->cbs.aio_offset += res;
+	    break;
+	}
+    }
+    return res;
+}
+
+ssize_t aFile::Sync(void)
+{
+    af->aio_sync = 1;
+    return waiton_aio(0);
+}
+
+bool aFile::Open(int fd, const char *mode)
+{
+    af = (AFILE*)malloc(sizeof(AFILE));
+    if (!af)
+	return false;
+    memset(af, 0, sizeof(AFILE));
+
+    af->cbs.aio_offset = 0;
+    if      (*mode == 'r')
+	af->cbs.aio_lio_opcode = LIO_READ;
+    else if (*mode == 'w')
+	af->cbs.aio_lio_opcode = LIO_WRITE;
+    else if (*mode == 'a') {
+	struct stat st;
+	af->cbs.aio_lio_opcode = LIO_WRITE;
+	if (fstat(fd, &st) < 0)
+	    return false;
+	af->cbs.aio_offset = st.st_size;
+    } else
+	af->cbs.aio_lio_opcode = LIO_NOP;
+
+    af->cbs.aio_fildes = fd;
+    af->cbs.aio_reqprio = 0;
+    af->cbs.aio_buf = NULL;
+    af->cbs.aio_nbytes = 0;
+    af->cbs.aio_sigevent.sigev_notify = SIGEV_NONE;
+
+    af->aio_bufp = NULL;
+    af->aio_bufl = 0;
+    af->aio_sync = 1;
+
+    return true;
+}
+
+bool aFile::Open(const char *path, const char *mode)
+{
+    int fd = 0;
+
+    if      (*mode == 'r')
+	fd = open(path, O_RDONLY);
+    else if (*mode == 'w')
+	fd = open(path, O_WRONLY|O_CREAT|O_TRUNC,
+			S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
+    else if (*mode == 'a')
+	fd = open(path, O_WRONLY|O_CREAT|O_APPEND,
+			S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
+    else {
+	fd = -1;
+	errno = EINVAL;
+    }
+
+    if (fd < 0)
+	return false;
+
+    return Open(fd, mode);
+}
+
+void aFile::Close(bool clfd)
+{
+    ssize_t ret = 0;
+
+    if (!af)
+	return;
+
+    if (!af->aio_sync)
+	(void)Sync();
+
+    ret = aio_cancel (af->cbs.aio_fildes, &af->cbs);
+    if (ret < 0 && errno != EINVAL) {
+	esyslog(LOG_ERR, "aio_cancel error: %s", strerror(errno));
+	return;
+    }
+
+    switch (ret) {
+    case AIO_CANCELED:
+	break;
+    default:
+	ret = waiton_aio(ECANCELED);
+	if (ret < 0)
+	    return;
+    }
+
+    if (clfd) {
+	ret = close(af->cbs.aio_fildes);
+	if (ret < 0) {
+	    esyslog(LOG_ERR, "close error: %s", strerror(errno));
+	    return;
+	}
+    }
+    if (af->aio_bufp) {
+        free(af->aio_bufp);
+	af->aio_bufl = 0;
+    }
+    free(af);
+    af = NULL;
+}
 
+bool aFile::Offset(off_t offset, int whence)
+{
+    if (!af->aio_sync)
+        (void)Sync();
 
+    switch (whence) {
+    case SEEK_SET:
+	af->cbs.aio_offset  = offset;
+	break;
+    case SEEK_CUR:
+	af->cbs.aio_offset += offset;
+	break;
+    case SEEK_END:
+	{
+	    struct stat st;
+	    if (fstat(af->cbs.aio_fildes, &st) < 0)
+		return -1;
+	    af->cbs.aio_offset  = st.st_size;
+	    af->cbs.aio_offset -= offset;
+	}
+	break;
+    default:
+	errno = EINVAL;
+	return false;
+    }
+    return true;
+}
 
+off_t aFile::Current()
+{
+    if (!af->aio_sync)
+        (void)Sync();
+    return af->cbs.aio_offset;
+}
+
+bool aFile::Write(const void *ptr, size_t length)
+{
+    int err = 0;
+
+    if (!af->aio_sync)
+        (void)Sync();
+    af->aio_sync = 0;
+
+    if (af->aio_bufl < length) {
+	af->aio_bufp = memcpyrealloc(af->aio_bufp, ptr, length);
+	if (!af->aio_bufp)
+	    return 1;
+	af->aio_bufl = length;
+    } else
+	(void)memcpy(af->aio_bufp, ptr, length);
+
+    af->cbs.aio_buf = af->aio_bufp;
+    af->cbs.aio_nbytes = length;
+    while (1) {
+	err = aio_write (&af->cbs);
+	if (err < 0) {
+	    if (errno == EAGAIN)
+		continue;
+	    esyslog(LOG_ERR, "aio_write error: %s", strerror(errno));
+	    break;
+	}
+	break;
+    } 
+    return (err < 0) ? false : true;
+}
+
+bool aFile::Read(void *ptr, size_t length)
+{
+    int err = 0;
+
+    if (!af->aio_sync) {
+	ssize_t l = Sync();
+	if (l < 0)
+	    return false;
+	if ((size_t)l < length)
+	    return false;
+	af->aio_sync = 0;
+    }
 
+    af->cbs.aio_buf = ptr;
+    af->cbs.aio_nbytes = length;
+    while (1) {
+	err = aio_read (&af->cbs);
+	if (err < 0) {
+	    if (errno == EAGAIN)
+		continue;
+	    esyslog(LOG_ERR, "aio_read error: %s", strerror(errno));
+	    break;
+	}
+	break;
+    }
+    return (err < 0) ? false : true;
+}
--- tools.h
+++ tools.h	Tue Mar 26 11:18:34 2002
@@ -10,6 +10,8 @@
 #ifndef __TOOLS_H
 #define __TOOLS_H
 
+#include <unistd.h>
+#include <aio.h>
 //#include <errno.h>
 #include <fcntl.h>
 #include <stdio.h>
@@ -151,5 +153,34 @@
   T *Next(const T *object) const { return (T *)object->cListObject::Next(); }
   };
 
-#endif //__TOOLS_H
+class aFile {
+protected:
+    typedef struct _AFILE {
+	struct aiocb  cbs;	/* The AIO control block */
+	void * aio_bufp;	/* For a copy of a buffer to write */
+	size_t aio_bufl;	/* Length control for realloc() */
+	int    aio_sync;	/* Is this buffer in sync? */
+    } AFILE;
+private:
+    AFILE * af;
+    static void*    memcpyrealloc(void *dest, const void *src, size_t n);
+    virtual ssize_t waiton_aio(int allowed_err = 0);
+public:
+    virtual void Close(bool clfd = true);
+    virtual bool Open(int fd, const char *mode);
+    virtual bool Open(const char *path, const char *mode);
+    virtual ssize_t Sync(void);
+    virtual bool Offset(off_t offset, int whence);
+    virtual void Rewind (void) { (void)Offset(0L, SEEK_SET); };
+    virtual off_t Current (void);
+    virtual bool Write(const void *ptr, size_t length);
+    virtual bool Read (void *ptr, size_t length);
+    //
+    // Constructor/Destructor/Operator
+    //
+    aFile(void) { af = NULL; };
+    virtual ~aFile(void) { Close(); };
+    virtual operator AFILE* () { return af; }
+};
 
+#endif //__TOOLS_H



Home | Main Index | Thread Index