diff --git a/CONTRIBUTORS b/CONTRIBUTORS index f3b5ef3b..f38b3c7b 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -530,3 +530,4 @@ Teemu Rantanen for increased the maximum possible packet size in remux.c to avoid corrupted streams with broadcasters that send extremely large PES packets for adding TS error checking to remux.c + for pinpointing a problem with excessive memmove() calls in 'Transfer Mode' diff --git a/HISTORY b/HISTORY index a784e062..761925c6 100644 --- a/HISTORY +++ b/HISTORY @@ -1917,7 +1917,7 @@ Video Disk Recorder Revision History EPG data, that string is now limited in length when used in a recording's file name. -2003-01-24: Version 1.1.22 +2003-01-25: Version 1.1.22 - Added 'Hrvatska radiotelevizija' and 'RTV Slovenija' to ca.conf (thanks to Paul Gohn). @@ -1928,3 +1928,6 @@ Video Disk Recorder Revision History - Increased the maximum possible packet size in remux.c to avoid corrupted streams with broadcasters that send extremely large PES packets (thanks to Teemu Rantanen). - Added TS error checking to remux.c (thanks to Teemu Rantanen). +- Modified cRingBufferLinear to avoid excessive memmove() calls in 'Transfer Mode' + and during recordings, which dramatically reduces CPU load. Thanks to Teemu + Rantanen for pinpointing the problem with the excessive memmove() calls. diff --git a/recorder.c b/recorder.c index 426b5551..15e86463 100644 --- a/recorder.c +++ b/recorder.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: recorder.c 1.4 2002/12/22 11:33:08 kls Exp $ + * $Id: recorder.c 1.5 2003/01/25 16:23:36 kls Exp $ */ #include @@ -41,7 +41,7 @@ cRecorder::cRecorder(const char *FileName, int Ca, int Priority, int VPid, int A SpinUpDisk(FileName); - ringBuffer = new cRingBufferLinear(VIDEOBUFSIZE, true); + ringBuffer = new cRingBufferLinear(VIDEOBUFSIZE, TS_SIZE * 2, true); remux = new cRemux(VPid, APid1, APid2, DPid1, DPid2, true); fileName = new cFileName(FileName, true); recordFile = fileName->Open(); @@ -110,16 +110,14 @@ 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 r; + const uchar *b = ringBuffer->Get(r); + if (b) { int Count = r, Result; uchar *p = remux->Process(b, Count, Result, &pictureType); + ringBuffer->Del(Count); if (p) { //XXX+ active??? see old version (Busy) if (!active && pictureType == I_FRAME) // finish the recording before the next 'I' frame @@ -136,10 +134,6 @@ void cRecorder::Action(void) else break; } - if (Count > 0) { - r -= Count; - memmove(b, b + Count, r); - } } else usleep(1); // this keeps the CPU load low diff --git a/ringbuffer.c b/ringbuffer.c index ed92be59..0734c28d 100644 --- a/ringbuffer.c +++ b/ringbuffer.c @@ -7,7 +7,7 @@ * Parts of this file were inspired by the 'ringbuffy.c' from the * LinuxDVB driver (see linuxtv.org). * - * $Id: ringbuffer.c 1.11 2003/01/19 15:03:00 kls Exp $ + * $Id: ringbuffer.c 1.12 2003/01/26 09:39:24 kls Exp $ */ #include "ringbuffer.h" @@ -57,13 +57,14 @@ void cRingBuffer::EnableGet(void) // --- cRingBufferLinear ----------------------------------------------------- -cRingBufferLinear::cRingBufferLinear(int Size, bool Statistics) +cRingBufferLinear::cRingBufferLinear(int Size, int Margin, bool Statistics) :cRingBuffer(Size, Statistics) { + margin = Margin; buffer = NULL; getThreadPid = -1; if (Size > 1) { // 'Size - 1' must not be 0! - buffer = new uchar[Size]; + buffer = MALLOC(uchar, Size); if (!buffer) esyslog("ERROR: can't allocate ring buffer (size=%d)", Size); Clear(); @@ -74,7 +75,7 @@ cRingBufferLinear::cRingBufferLinear(int Size, bool Statistics) cRingBufferLinear::~cRingBufferLinear() { - delete buffer; + free(buffer); } int cRingBufferLinear::Available(void) @@ -82,13 +83,14 @@ int cRingBufferLinear::Available(void) Lock(); int diff = head - tail; Unlock(); - return (diff >= 0) ? diff : Size() + diff; + return (diff >= 0) ? diff : Size() + diff - margin; } void cRingBufferLinear::Clear(void) { Lock(); - head = tail = 0; + head = tail = margin; + lastGet = -1; Unlock(); EnablePut(); EnableGet(); @@ -100,7 +102,7 @@ int cRingBufferLinear::Put(const uchar *Data, int Count) Lock(); int rest = Size() - head; int diff = tail - head; - int free = (diff > 0) ? diff - 1 : Size() + diff - 1; + int free = (diff > 0) ? diff - 1 : Size() + diff - (tail < margin ? -(margin - tail) : margin) - 1; if (statistics) { int fill = Size() - free - 1 + Count; if (fill >= Size()) @@ -122,8 +124,8 @@ int cRingBufferLinear::Put(const uchar *Data, int Count) if (Count >= rest) { memcpy(buffer + head, Data, rest); if (Count - rest) - memcpy(buffer, Data + rest, Count - rest); - head = Count - rest; + memcpy(buffer + margin, Data + rest, Count - rest); + head = margin + Count - rest; } else { memcpy(buffer + head, Data, Count); @@ -138,36 +140,42 @@ int cRingBufferLinear::Put(const uchar *Data, int Count) return Count; } -int cRingBufferLinear::Get(uchar *Data, int Count) +const uchar *cRingBufferLinear::Get(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; - if (rest > 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; - } - } - else - Count = 0; - Unlock(); - if (Count == 0) - WaitForGet(); + const uchar *p = NULL; + Lock(); + if (getThreadPid < 0) + getThreadPid = getpid(); + int rest = Size() - tail; + if (tail > Size() - margin && head < tail) { + int t = margin - rest; + memcpy(buffer + t, buffer + tail, rest); + tail = t; } - return Count; + int diff = head - tail; + int cont = (diff >= 0) ? diff : Size() + diff - margin; + if (cont > rest) + cont = rest; + if (cont >= margin) { + p = buffer + tail; + Count = lastGet = cont; + } + Unlock(); + if (!p) + WaitForGet(); + return p; +} + +void cRingBufferLinear::Del(int Count) +{ + if (Count > 0 && Count <= lastGet) { + tail += Count; + lastGet -= Count; + if (tail >= Size()) + tail = margin; + } + else + esyslog("ERROR: invalid Count in cRingBufferLinear::Del: %d", Count); } // --- cFrame ---------------------------------------------------------------- diff --git a/ringbuffer.h b/ringbuffer.h index f96143c9..9205df7f 100644 --- a/ringbuffer.h +++ b/ringbuffer.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: ringbuffer.h 1.8 2003/01/19 15:03:00 kls Exp $ + * $Id: ringbuffer.h 1.9 2003/01/26 09:47:39 kls Exp $ */ #ifndef __RINGBUFFER_H @@ -40,21 +40,31 @@ public: class cRingBufferLinear : public cRingBuffer { private: - int head, tail; + int margin, head, tail; + int lastGet; uchar *buffer; pid_t getThreadPid; public: - cRingBufferLinear(int Size, bool Statistics = false); + cRingBufferLinear(int Size, int Margin = 0, bool Statistics = false); + ///< Creates a linear ring buffer. + ///< The buffer will be able to hold at most Size bytes of data, and will + ///< be guaranteed to return at least Margin bytes in one consecutive block. virtual ~cRingBufferLinear(); virtual int Available(void); virtual void Clear(void); - // Immediately clears the ring buffer. + ///< 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. + ///< Puts at most Count bytes of Data into the ring buffer. + ///< \return Returns the number of bytes actually stored. + const uchar *Get(int &Count); + ///< Gets data from the ring buffer. + ///< The data will remain in the buffer until a call to Del() deletes it. + ///< \return Returns a pointer to the data, and stores the number of bytes + ///< actually retrieved in Count. If the returned pointer is NULL, Count has no meaning. + void Del(int Count); + ///< Deletes at most Count bytes from the ring buffer. + ///< Count must be less or equal to the number that was returned by a previous + ///< call to Get(). }; enum eFrameType { ftUnknown, ftVideo, ftAudio, ftDolby }; diff --git a/transfer.c b/transfer.c index d8975a92..abcb9d08 100644 --- a/transfer.c +++ b/transfer.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: transfer.c 1.8 2002/12/14 13:14:53 kls Exp $ + * $Id: transfer.c 1.9 2003/01/26 09:59:35 kls Exp $ */ #include "transfer.h" @@ -19,7 +19,7 @@ cTransfer::cTransfer(int VPid, int APid1, int APid2, int DPid1, int DPid2) :cReceiver(0, -1, 5, VPid, APid1, APid2, DPid1, DPid2) { - ringBuffer = new cRingBufferLinear(VIDEOBUFSIZE, true); + ringBuffer = new cRingBufferLinear(VIDEOBUFSIZE, TS_SIZE * 2, true); remux = new cRemux(VPid, APid1, APid2, DPid1, DPid2); canToggleAudioTrack = false; audioTrack = 0xC0; @@ -60,8 +60,6 @@ void cTransfer::Action(void) { dsyslog("transfer thread started (pid=%d)", getpid()); - uchar b[MINVIDEODATA]; - int r = 0; active = true; while (active) { @@ -80,15 +78,15 @@ void cTransfer::Action(void) // Get data from the buffer: - int g = ringBuffer->Get(b + r, sizeof(b) - r); - if (g > 0) - r += g; + int r; + const uchar *b = ringBuffer->Get(r); // Play the data: - if (r > 0) { + if (b) { int Count = r, Result; uchar *p = remux->Process(b, Count, Result); + ringBuffer->Del(Count); if (p) { StripAudioPackets(p, Result, audioTrack); while (Result > 0 && active) { @@ -103,11 +101,6 @@ void cTransfer::Action(void) } } } - if (Count > 0) { - r -= Count; - if (r > 0) - memmove(b, b + Count, r); - } } else usleep(1); // this keeps the CPU load low