diff options
author | schmirl <schmirl> | 2009-06-19 06:32:38 +0000 |
---|---|---|
committer | schmirl <schmirl> | 2009-06-19 06:32:38 +0000 |
commit | 008e7c851013722a377aa9ff4485d4af2d991883 (patch) | |
tree | 8178a3251d528849d2ccbb0cef9b1bd712cfd6f9 /server | |
parent | 64ff2c08be769cf227ac6cf2e318fcb6b80c9689 (diff) | |
download | vdr-plugin-streamdev-008e7c851013722a377aa9ff4485d4af2d991883.tar.gz vdr-plugin-streamdev-008e7c851013722a377aa9ff4485d4af2d991883.tar.bz2 |
- added namespace to remuxers
- increased WRITERBUFSIZE - buffer was too small for high bandwidth content
- removed cStreamdevStreamer::m_Running
- eliminated potential busy waits in remuxers
- updated cTSRemux static helpers to code of their VDR 1.6.0 counterparts
- re-enabled PES vor VDR 1.7.3+. Streamdev now uses a copy of VDR 1.6.0's
cRemux for TS to PES remuxing.
- make sure that only complete TS packets are written to ringbuffers
- use signaling instead of sleeps when writing to ringbuffers
- optimized cStreamdevPatFilter PAT packet initialization
- fixed cStreamdevPatFilter not processing PATs with length > TS_SIZE - 5
- use a small ringbuffer for cStreamdevPatFilter instead of writing to
cStreamdevStreamers SendBuffer as two threads mustn't write to the same
ringbuffer
Modified Files:
CONTRIBUTORS HISTORY Makefile common.c common.h
streamdev-server.c libdvbmpeg/transform.h remux/extern.c
remux/extern.h remux/ts2es.c remux/ts2es.h remux/ts2ps.c
remux/ts2ps.h remux/tsremux.c remux/tsremux.h
server/connectionHTTP.c server/connectionVTP.c
server/livestreamer.c server/livestreamer.h server/menuHTTP.c
server/streamer.c server/streamer.h
Added Files:
remux/ts2pes.c remux/ts2pes.h
Diffstat (limited to 'server')
-rw-r--r-- | server/connectionHTTP.c | 6 | ||||
-rw-r--r-- | server/connectionVTP.c | 4 | ||||
-rw-r--r-- | server/livestreamer.c | 140 | ||||
-rw-r--r-- | server/livestreamer.h | 22 | ||||
-rw-r--r-- | server/menuHTTP.c | 4 | ||||
-rw-r--r-- | server/streamer.c | 22 | ||||
-rw-r--r-- | server/streamer.h | 43 |
7 files changed, 133 insertions, 108 deletions
diff --git a/server/connectionHTTP.c b/server/connectionHTTP.c index fc10bfc..83e568d 100644 --- a/server/connectionHTTP.c +++ b/server/connectionHTTP.c @@ -1,5 +1,5 @@ /* - * $Id: connectionHTTP.c,v 1.16 2009/02/13 07:02:19 schmirl Exp $ + * $Id: connectionHTTP.c,v 1.17 2009/06/19 06:32:45 schmirl Exp $ */ #include <ctype.h> @@ -211,10 +211,8 @@ bool cConnectionHTTP::CmdGET(const std::string &Opts) const char* pType = type.c_str(); if (strcasecmp(pType, "PS") == 0) { m_StreamType = stPS; -#if APIVERSNUM < 10703 } else if (strcasecmp(pType, "PES") == 0) { m_StreamType = stPES; -#endif } else if (strcasecmp(pType, "TS") == 0) { m_StreamType = stTS; } else if (strcasecmp(pType, "ES") == 0) { @@ -266,9 +264,7 @@ bool cConnectionHTTP::CmdGET(const std::string &Opts) { case stTS: base += "TS/"; break; case stPS: base += "PS/"; break; -#if APIVERSNUM < 10703 case stPES: base += "PES/"; break; -#endif case stES: base += "ES/"; break; case stExtern: base += "Extern/"; break; default: break; diff --git a/server/connectionVTP.c b/server/connectionVTP.c index e0edb6e..ebe339b 100644 --- a/server/connectionVTP.c +++ b/server/connectionVTP.c @@ -1,5 +1,5 @@ /* - * $Id: connectionVTP.c,v 1.19 2009/01/16 11:35:44 schmirl Exp $ + * $Id: connectionVTP.c,v 1.20 2009/06/19 06:32:45 schmirl Exp $ */ #include "server/connectionVTP.h" @@ -595,12 +595,10 @@ bool cConnectionVTP::CmdCAPS(char *Opts) return Respond(220, "Capability \"%s\" accepted", Opts); } -#if APIVERSNUM < 10703 if (strcasecmp(Opts, "PES") == 0) { m_StreamType = stPES; return Respond(220, "Capability \"%s\" accepted", Opts); } -#endif if (strcasecmp(Opts, "EXTERN") == 0) { m_StreamType = stExtern; diff --git a/server/livestreamer.c b/server/livestreamer.c index 97dffd7..684e701 100644 --- a/server/livestreamer.c +++ b/server/livestreamer.c @@ -4,6 +4,7 @@ #include <libsi/descriptor.h> #include "remux/ts2ps.h" +#include "remux/ts2pes.h" #include "remux/ts2es.h" #include "remux/extern.h" @@ -13,7 +14,7 @@ #include "server/livefilter.h" #include "common.h" -#define TSPATREPACKER +using namespace Streamdev; // --- cStreamdevLiveReceiver ------------------------------------------------- @@ -64,6 +65,8 @@ private: int pmtPid; int pmtSid; int pmtVersion; + uchar tspat_buf[TS_SIZE]; + cStreamdevBuffer siBuffer; const cChannel *m_Channel; cStreamdevLiveStreamer *m_Streamer; @@ -73,9 +76,11 @@ private: int GetPid(SI::PMT::Stream& stream); public: cStreamdevPatFilter(cStreamdevLiveStreamer *Streamer, const cChannel *Channel); + uchar* Get(int &Count) { return siBuffer.Get(Count); } + void Del(int Count) { return siBuffer.Del(Count); } }; -cStreamdevPatFilter::cStreamdevPatFilter(cStreamdevLiveStreamer *Streamer, const cChannel *Channel) +cStreamdevPatFilter::cStreamdevPatFilter(cStreamdevLiveStreamer *Streamer, const cChannel *Channel): siBuffer(10 * TS_SIZE, TS_SIZE) { Dprintf("cStreamdevPatFilter(\"%s\")\n", Channel->Name()); assert(Streamer); @@ -85,6 +90,29 @@ cStreamdevPatFilter::cStreamdevPatFilter(cStreamdevLiveStreamer *Streamer, const pmtSid = 0; pmtVersion = -1; Set(0x00, 0x00); // PAT + // initialize PAT buffer. Only some values are dynamic (see comments) + memset(tspat_buf, 0xff, TS_SIZE); + tspat_buf[0] = TS_SYNC_BYTE; // Transport packet header sunchronization byte (1000011 = 0x47h) + tspat_buf[1] = 0x40; // Set payload unit start indicator bit + tspat_buf[2] = 0x0; // PID + tspat_buf[3] = 0x10; // Set payload flag, DYNAMIC: Continuity counter + tspat_buf[4] = 0x0; // SI pointer field + tspat_buf[5] = 0x0; // PAT table id + tspat_buf[6] = 0xb0; // Section syntax indicator bit and reserved bits set + tspat_buf[7] = 12 + 1; // Section length (12 bit): PAT_TABLE_LEN + 1 + tspat_buf[8] = 0; // DYNAMIC: Transport stream ID (bits 8-15) + tspat_buf[9] = 0; // DYNAMIC: Transport stream ID (bits 0-7) + tspat_buf[10] = 0xc0; // Reserved, DYNAMIC: Version number, DYNAMIC: Current next indicator + tspat_buf[11] = 0x0; // Section number + tspat_buf[12] = 0x0; // Last section number + tspat_buf[13] = 0; // DYNAMIC: Program number (bits 8-15) + tspat_buf[14] = 0; // DYNAMIC: Program number (bits 0-7) + tspat_buf[15] = 0xe0; // Reserved, DYNAMIC: Network ID (bits 8-12) + tspat_buf[16] = 0; // DYNAMIC: Network ID (bits 0-7) + tspat_buf[17] = 0; // DYNAMIC: Checksum + tspat_buf[18] = 0; // DYNAMIC: Checksum + tspat_buf[19] = 0; // DYNAMIC: Checksum + tspat_buf[20] = 0; // DYNAMIC: Checksum } static const char * const psStreamTypes[] = { @@ -224,54 +252,37 @@ void cStreamdevPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, i if (0 != (pmtPid = assoc.getPid())) { Dprintf("cStreamdevPatFilter: PMT pid for channel %s: %d\n", Channel->Name(), pmtPid); pmtSid = assoc.getServiceId(); - if (Length < TS_SIZE-5) { - // repack PAT to TS frame and send to client -#ifndef TSPATREPACKER - uint8_t pat_ts[TS_SIZE] = {TS_SYNC_BYTE, 0x40 /* pusi=1 */, 0 /* pid=0 */, 0x10 /* adaption=1 */, 0 /* pointer */}; - memcpy(pat_ts + 5, Data, Length); - m_Streamer->Put(pat_ts, TS_SIZE); -#else - int ts_id; - unsigned int crc, i, len; - uint8_t *tmp, tspat_buf[TS_SIZE]; - static uint8_t ccounter = 0; - ccounter = (ccounter + 1) % 16; - memset(tspat_buf, 0xff, TS_SIZE); - ts_id = Channel->Tid(); // Get transport stream id of the channel - tspat_buf[0] = TS_SYNC_BYTE; // Transport packet header sunchronization byte (1000011 = 0x47h) - tspat_buf[1] = 0x40; // Set payload unit start indicator bit - tspat_buf[2] = 0x0; // PID - tspat_buf[3] = 0x10 | ccounter; // Set payload flag, Continuity counter - tspat_buf[4] = 0x0; // SI pointer field - tspat_buf[5] = 0x0; // PAT table id - tspat_buf[6] = 0xb0; // Section syntax indicator bit and reserved bits set - tspat_buf[7] = 12 + 1; // Section length (12 bit): PAT_TABLE_LEN + 1 - tspat_buf[8] = (ts_id >> 8); // Transport stream ID (bits 8-15) - tspat_buf[9] = (ts_id & 0xff); // Transport stream ID (bits 0-7) - tspat_buf[10] = 0xc0 | ((pat.getVersionNumber() << 1) & 0x3e) | - pat.getCurrentNextIndicator();// Version number, Current next indicator - tspat_buf[11] = 0x0; // Section number - tspat_buf[12] = 0x0; // Last section number - tspat_buf[13] = (pmtSid >> 8); // Program number (bits 8-15) - tspat_buf[14] = (pmtSid & 0xff); // Program number (bits 0-7) - tspat_buf[15] = 0xe0 | (pmtPid >> 8); // Network ID (bits 8-12) - tspat_buf[16] = (pmtPid & 0xff); // Network ID (bits 0-7) - crc = 0xffffffff; - len = 12; // PAT_TABLE_LEN - tmp = &tspat_buf[4 + 1]; // TS_HDR_LEN + 1 - while (len--) { - crc ^= *tmp++ << 24; - for (i = 0; i < 8; i++) - crc = (crc << 1) ^ ((crc & 0x80000000) ? 0x04c11db7 : 0); // CRC32POLY - } - tspat_buf[17] = crc >> 24 & 0xff; // Checksum - tspat_buf[18] = crc >> 16 & 0xff; // Checksum - tspat_buf[19] = crc >> 8 & 0xff; // Checksum - tspat_buf[20] = crc & 0xff; // Checksum - m_Streamer->Put(tspat_buf, TS_SIZE); -#endif - } else - isyslog("cStreamdevPatFilter: PAT size %d too large to fit in one TS", Length); + // repack PAT to TS frame and send to client + int ts_id; + unsigned int crc, i, len; + uint8_t *tmp; + static uint8_t ccounter = 0; + ccounter = (ccounter + 1) % 16; + ts_id = Channel->Tid(); // Get transport stream id of the channel + tspat_buf[3] = 0x10 | ccounter; // Set payload flag, Continuity counter + tspat_buf[8] = (ts_id >> 8); // Transport stream ID (bits 8-15) + tspat_buf[9] = (ts_id & 0xff); // Transport stream ID (bits 0-7) + tspat_buf[10] = 0xc0 | ((pat.getVersionNumber() << 1) & 0x3e) | + pat.getCurrentNextIndicator();// Version number, Current next indicator + tspat_buf[13] = (pmtSid >> 8); // Program number (bits 8-15) + tspat_buf[14] = (pmtSid & 0xff); // Program number (bits 0-7) + tspat_buf[15] = 0xe0 | (pmtPid >> 8); // Network ID (bits 8-12) + tspat_buf[16] = (pmtPid & 0xff); // Network ID (bits 0-7) + crc = 0xffffffff; + len = 12; // PAT_TABLE_LEN + tmp = &tspat_buf[4 + 1]; // TS_HDR_LEN + 1 + while (len--) { + crc ^= *tmp++ << 24; + for (i = 0; i < 8; i++) + crc = (crc << 1) ^ ((crc & 0x80000000) ? 0x04c11db7 : 0); // CRC32POLY + } + tspat_buf[17] = crc >> 24 & 0xff; // Checksum + tspat_buf[18] = crc >> 16 & 0xff; // Checksum + tspat_buf[19] = crc >> 8 & 0xff; // Checksum + tspat_buf[20] = crc & 0xff; // Checksum + int written = siBuffer.PutTS(tspat_buf, TS_SIZE); + if (written != TS_SIZE) + siBuffer.ReportOverflow(TS_SIZE - written); if (pmtPid != prevPmtPid) { m_Streamer->SetPids(pmtPid); Add(pmtPid, 0x02); @@ -292,7 +303,7 @@ void cStreamdevPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, i if (pmtVersion != -1) { if (pmtVersion != pmt.getVersionNumber()) { Dprintf("cStreamdevPatFilter: PMT version changed, detaching all pids\n"); - Del(pmtPid, 0x02); + cFilter::Del(pmtPid, 0x02); pmtPid = 0; // this triggers PAT scan } return; @@ -329,9 +340,7 @@ cStreamdevLiveStreamer::cStreamdevLiveStreamer(int Priority, std::string Paramet m_Device(NULL), m_Receiver(NULL), m_PatFilter(NULL), -#if APIVERSNUM < 10703 m_PESRemux(NULL), -#endif m_ESRemux(NULL), m_PSRemux(NULL), m_ExtRemux(NULL) @@ -347,9 +356,7 @@ cStreamdevLiveStreamer::~cStreamdevLiveStreamer() DELETENULL(m_PatFilter); } DELETENULL(m_Receiver); -#if APIVERSNUM < 10703 delete m_PESRemux; -#endif delete m_ESRemux; delete m_PSRemux; delete m_ExtRemux; @@ -463,12 +470,10 @@ bool cStreamdevLiveStreamer::SetChannel(const cChannel *Channel, eStreamType Str return SetPids(pid); } -#if APIVERSNUM < 10703 case stPES: - m_PESRemux = new cRemux(m_Channel->Vpid(), m_Channel->Apids(), m_Channel->Dpids(), - m_Channel->Spids(), false); + m_PESRemux = new cTS2PESRemux(m_Channel->Vpid(), m_Channel->Apids(), m_Channel->Dpids(), + m_Channel->Spids()); return SetPids(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids()); -#endif case stPS: m_PSRemux = new cTS2PSRemux(m_Channel->Vpid(), m_Channel->Apids(), m_Channel->Dpids(), @@ -505,13 +510,22 @@ int cStreamdevLiveStreamer::Put(const uchar *Data, int Count) { switch (m_StreamType) { case stTS: + // insert si data + if (m_PatFilter) { + int got; + uchar *si = m_PatFilter->Get(got); + if (si) { + int count = cStreamdevStreamer::Put(si, got); + if (count) + m_PatFilter->Del(count); + } + } + // fall through case stTSPIDS: return cStreamdevStreamer::Put(Data, Count); -#if APIVERSNUM < 10703 case stPES: return m_PESRemux->Put(Data, Count); -#endif case stES: return m_ESRemux->Put(Data, Count); @@ -534,10 +548,8 @@ uchar *cStreamdevLiveStreamer::Get(int &Count) case stTSPIDS: return cStreamdevStreamer::Get(Count); -#if APIVERSNUM < 10703 case stPES: return m_PESRemux->Get(Count); -#endif case stES: return m_ESRemux->Get(Count); @@ -561,11 +573,9 @@ void cStreamdevLiveStreamer::Del(int Count) cStreamdevStreamer::Del(Count); break; -#if APIVERSNUM < 10703 case stPES: m_PESRemux->Del(Count); break; -#endif case stES: m_ESRemux->Del(Count); diff --git a/server/livestreamer.h b/server/livestreamer.h index 5c4ae8f..cf64559 100644 --- a/server/livestreamer.h +++ b/server/livestreamer.h @@ -7,12 +7,12 @@ #include "server/streamer.h" #include "common.h" -class cTS2PSRemux; -class cTS2ESRemux; -class cExternRemux; -#if APIVERSNUM < 10703 -class cRemux; -#endif +namespace Streamdev { + class cTS2PSRemux; + class cTS2ESRemux; + class cExternRemux; + class cTS2PESRemux; +} class cStreamdevPatFilter; class cStreamdevLiveReceiver; @@ -29,12 +29,10 @@ private: cDevice *m_Device; cStreamdevLiveReceiver *m_Receiver; cStreamdevPatFilter *m_PatFilter; -#if APIVERSNUM < 10703 - cRemux *m_PESRemux; -#endif - cTS2ESRemux *m_ESRemux; - cTS2PSRemux *m_PSRemux; - cExternRemux *m_ExtRemux; + Streamdev::cTS2PESRemux *m_PESRemux; + Streamdev::cTS2ESRemux *m_ESRemux; + Streamdev::cTS2PSRemux *m_PSRemux; + Streamdev::cExternRemux *m_ExtRemux; void StartReceiver(void); bool HasPid(int Pid); diff --git a/server/menuHTTP.c b/server/menuHTTP.c index 41b1f10..8d3e404 100644 --- a/server/menuHTTP.c +++ b/server/menuHTTP.c @@ -201,10 +201,8 @@ std::string cHtmlChannelList::StreamTypeMenu() (std::string) "[<a href=\"/TS/" + self + "\">TS</a>] "); typeMenu += (streamType == stPS ? (std::string) "[PS] " : (std::string) "[<a href=\"/PS/" + self + "\">PS</a>] "); -#if APIVERSNUM < 10703 typeMenu += (streamType == stPES ? (std::string) "[PES] " : (std::string) "[<a href=\"/PES/" + self + "\">PES</a>] "); -#endif typeMenu += (streamType == stES ? (std::string) "[ES] " : (std::string) "[<a href=\"/ES/" + self + "\">ES</a>] "); typeMenu += (streamType == stExtern ? (std::string) "[Extern] " : @@ -343,10 +341,8 @@ std::string cHtmlChannelList::ItemText() switch (streamType) { case stTS: suffix = (std::string) ".ts"; break; case stPS: suffix = (std::string) ".vob"; break; -#if APIVERSNUM < 10703 // for Network Media Tank case stPES: suffix = (std::string) ".vdr"; break; -#endif default: suffix = ""; } line += (std::string) "<li value=\"" + (const char*) itoa(current->Number()) + "\">"; diff --git a/server/streamer.c b/server/streamer.c index 9795cc6..42e7efa 100644 --- a/server/streamer.c +++ b/server/streamer.c @@ -1,5 +1,5 @@ /* - * $Id: streamer.c,v 1.18 2009/02/13 10:39:22 schmirl Exp $ + * $Id: streamer.c,v 1.19 2009/06/19 06:32:45 schmirl Exp $ */ #include <vdr/ringbuffer.h> @@ -14,6 +14,13 @@ #include "tools/select.h" #include "common.h" +// --- cStreamdevBuffer ------------------------------------------------------- + +cStreamdevBuffer::cStreamdevBuffer(int Size, int Margin, bool Statistics, const char *Description): + cRingBufferLinear(Size, Margin, Statistics, Description) +{ +} + // --- cStreamdevWriter ------------------------------------------------------- cStreamdevWriter::cStreamdevWriter(cTBSocket *Socket, @@ -95,14 +102,13 @@ void cStreamdevWriter::Action(void) cStreamdevStreamer::cStreamdevStreamer(const char *Name): cThread(Name), - m_Running(false), m_Writer(NULL), - m_RingBuffer(new cRingBufferLinear(STREAMERBUFSIZE, TS_SIZE * 2, + m_RingBuffer(new cStreamdevBuffer(STREAMERBUFSIZE, TS_SIZE * 2, true, "streamdev-streamer")), - m_SendBuffer(new cRingBufferLinear(WRITERBUFSIZE, TS_SIZE * 2)) + m_SendBuffer(new cStreamdevBuffer(WRITERBUFSIZE, TS_SIZE * 2)) { m_RingBuffer->SetTimeouts(0, 100); - m_SendBuffer->SetTimeouts(0, 100); + m_SendBuffer->SetTimeouts(100, 100); } cStreamdevStreamer::~cStreamdevStreamer() @@ -116,7 +122,6 @@ void cStreamdevStreamer::Start(cTBSocket *Socket) { Dprintf("start streamer\n"); m_Writer = new cStreamdevWriter(Socket, this); - m_Running = true; Attach(); } @@ -135,9 +140,8 @@ void cStreamdevStreamer::Stop(void) Dprintf("stopping streamer\n"); Cancel(3); } - if (m_Running) { + if (m_Writer) { Detach(); - m_Running = false; DELETENULL(m_Writer); } } @@ -152,8 +156,6 @@ void cStreamdevStreamer::Action(void) int count = Put(block, got); if (count) m_RingBuffer->Del(count); - else - cCondWait::SleepMs(100); } } } diff --git a/server/streamer.h b/server/streamer.h index 20323b7..6561bc2 100644 --- a/server/streamer.h +++ b/server/streamer.h @@ -1,5 +1,5 @@ /* - * $Id: streamer.h,v 1.10 2009/02/13 10:39:22 schmirl Exp $ + * $Id: streamer.h,v 1.11 2009/06/19 06:32:45 schmirl Exp $ */ #ifndef VDR_STREAMDEV_STREAMER_H @@ -16,8 +16,34 @@ class cStreamdevStreamer; #define TS_SIZE 188 #endif -#define STREAMERBUFSIZE MEGABYTE(4) -#define WRITERBUFSIZE KILOBYTE(256) +#define STREAMERBUFSIZE (20000 * TS_SIZE) +#define WRITERBUFSIZE (5000 * TS_SIZE) + +// --- cStreamdevBuffer ------------------------------------------------------- + +class cStreamdevBuffer: public cRingBufferLinear { +public: + // make public + void WaitForPut(void) { cRingBuffer::WaitForPut(); } + // Always write complete TS packets + // (assumes Count is a multiple of TS_SIZE) + int PutTS(const uchar *Data, int Count); + cStreamdevBuffer(int Size, int Margin = 0, bool Statistics = false, const char *Description = NULL); +}; + +inline int cStreamdevBuffer::PutTS(const uchar *Data, int Count) +{ + int free = Free(); + if (free < Count) + Count = free; + + Count -= Count % TS_SIZE; + if (Count) + Count = Put(Data, Count); + else + WaitForPut(); + return Count; +} // --- cStreamdevWriter ------------------------------------------------------- @@ -38,15 +64,14 @@ public: class cStreamdevStreamer: public cThread { private: - bool m_Running; cStreamdevWriter *m_Writer; - cRingBufferLinear *m_RingBuffer; - cRingBufferLinear *m_SendBuffer; + cStreamdevBuffer *m_RingBuffer; + cStreamdevBuffer *m_SendBuffer; protected: virtual void Action(void); - bool IsRunning(void) const { return m_Running; } + bool IsRunning(void) const { return m_Writer; } public: cStreamdevStreamer(const char *Name); @@ -57,10 +82,10 @@ public: bool Abort(void); void Activate(bool On); - int Receive(uchar *Data, int Length) { return m_RingBuffer->Put(Data, Length); } + int Receive(uchar *Data, int Length) { return m_RingBuffer->PutTS(Data, Length); } void ReportOverflow(int Bytes) { m_RingBuffer->ReportOverflow(Bytes); } - virtual int Put(const uchar *Data, int Count) { return m_SendBuffer->Put(Data, Count); } + virtual int Put(const uchar *Data, int Count) { return m_SendBuffer->PutTS(Data, Count); } virtual uchar *Get(int &Count) { return m_SendBuffer->Get(Count); } virtual void Del(int Count) { m_SendBuffer->Del(Count); } |