2 Copyright 2006-2008 Mark Calderbank
4 This file is part of VOMP.
6 VOMP is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 VOMP is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with VOMP; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 #include "demuxerts.h"
27 #define PTS_JUMP_MARGIN 10000
28 #define PTS_ALLOWANCE 90000
30 // TODO: PTS class to handle wrapping arithmetic & comparisons?
31 static ULLONG PTSDistance(ULLONG pts1, ULLONG pts2)
33 // Assume pts1, pts2 < 2^33; calculate shortest distance between
34 ULLONG ret = (pts1 > pts2) ? pts1 - pts2 : pts2 - pts1;
35 if (ret > (1LL<<32)) ret = (1LL<<33) - ret;
39 static ULLONG PTSDifference(ULLONG pts1, ULLONG pts2)
41 // Assume pts1, pts2 < 2^33; calculate pts1 - pts2
45 return (1LL<<33) + pts1 - pts2;
48 DemuxerTS::DemuxerTS(int p_vID, int p_aID, int p_subID, int p_tID)
50 vID = p_vID; vActive = false;
51 aID = p_aID; aActive = false;
52 subID = p_subID; subActive = false;
56 havechannelinfo=false;
57 //doubledframerate=false;
61 pinfo.hasaccessunit=false;
64 void DemuxerTS::flush()
68 havechannelinfo=false;
70 vPacket.init(PESTYPE_VID0);
74 aPacket.init(PESTYPE_PRIVATE_1, PESTYPE_SUBSTREAM_AC30);
78 aPacket.init(PESTYPE_AUD0);
81 subPacket.init(PESTYPE_PRIVATE_1);
82 tPacket.init(PESTYPE_PRIVATE_1,PESTYPE_SUBSTREAM_TELETEXTMAX);
89 // doubledframerate=false;
92 pinfo.hasaccessunit=false;
95 int DemuxerTS::scan(UCHAR *buf, int len)
100 return PESTYPE_PRIVATE_1;
107 void DemuxerTS::setVID(int p_vID)
110 vPacket.init(PESTYPE_VID0);
114 void DemuxerTS::setAID(int p_aID, int type, int streamtype, bool slivetv)
118 astreamtype = streamtype;
123 aPacket.init(PESTYPE_PRIVATE_1, PESTYPE_SUBSTREAM_AC30);
124 setAudioStream(PESTYPE_SUBSTREAM_AC30);
128 aPacket.init(PESTYPE_AUD0);
129 setAudioStream(PESTYPE_AUD0);
135 void DemuxerTS::setSubID(int p_subID)
138 subPacket.init(PESTYPE_PRIVATE_1);
143 void DemuxerTS::setTID(int p_tID)
146 tPacket.init(PESTYPE_PRIVATE_1,PESTYPE_SUBSTREAM_TELETEXTMAX);
154 int DemuxerTS::findPTS(UCHAR* buf, int len, ULLONG* dest)
158 while (len >= TS_SIZE)
160 if (*buf != TS_SIG) {buf++;len--; continue;}
162 //Pattern scanning won't work for ts
165 int datalen = TS_SIZE - 4;
166 int pid = ( (buf[1] & 0x1F) << 8 ) | buf[2];
167 UCHAR payload = buf[1] & 0x40;
169 if (buf[3] & 0x20) // Adaptation field is present
170 datalen -= (buf[4] + 1);
172 UCHAR* curbuf =buf+ (TS_SIZE - datalen);
176 if (pid == 0x00) {//PAT, only take first program number, ignore the rest
177 int pmtpid = (*(curbuf+11)<< 8) | *(curbuf+12);
178 if ((pmtpid >> 13) != 0x07)
180 Log::getInstance()->log("findPTS", Log::DEBUG, "PMTPID=%02x %02x TRAILING 111 not set but %x", *(curbuf+11),*(curbuf+12), (pmtpid >> 13));
184 pmtpid = pmtpid & 0x1FFF; //clear upper 3 bits
188 } else if (pid == PMTPID) { //PMT
189 int sectionlength = ((*(curbuf+2) << 8) & 0x0F ) | *(buf+3);
190 //sectionlength += 4; //include header but subtract crc in the end...
191 int p = 13; //skip fixed part of pmt
192 while ( p < sectionlength) {
193 int streamtype = *(curbuf+p);
195 int foundpid = (*(curbuf+p)<< 8) | *(curbuf+p+1);
196 p += 2; //skip ES Pid
197 int eslength = ((*(curbuf+p) << 8) & 0x0F ) | *(curbuf+p+1);
198 p += 2; //skip ES length
199 if ((foundpid >> 13) != 0x07)
201 Log::getInstance()->log("findPTS", Log::DEBUG, "FOUNDPID=%02x %02x TRAILING 111 not set but %x", *(buf+p),*(buf+p+1), (foundpid >> 13));
205 foundpid = foundpid & 0x1FFF; //clear upper 3 bits
207 if (streamtype==3 || streamtype ==4) {
211 p += eslength; //skip es descriptor
213 } else if (pid == scanaid) {
214 // UINT framelength = ((UINT)curbuf[4] << 8) | curbuf[5]; UNUSED?
216 if ( curbuf[7] & 0x80 ) // PTS_DTS_flags indicate that PTS is present
218 *dest = ( (ULLONG)(curbuf[9] & 0x0E) << 29 ) |
219 ( (ULLONG)(curbuf[10]) << 22 ) |
220 ( (ULLONG)(curbuf[11] & 0xFE) << 14 ) |
221 ( (ULLONG)(curbuf[12]) << 7 ) |
222 ( (ULLONG)(curbuf[13] & 0xFE) >> 1 );
234 void DemuxerTS::setFrameNum(ULONG frame)
236 frameCounting = true;
239 Log::getInstance()->log("DemuxerTS", Log::DEBUG, "setFrameNum %d", frame);
242 void DemuxerTS::setPacketNum(ULONG npacket)
244 packetCounting = true;
245 packetNumber = npacket;
246 Log::getInstance()->log("DemuxerTS", Log::DEBUG, "setPacketNum %d", npacket);
249 int DemuxerTS::put(UCHAR* buf, int len)
251 int ret = 0; // return number of bytes consumed
253 bool misaligned_mess=false;
256 if (len >= TS_SIZE + 1 - partPacket)
257 { // Remainder of partial packet is available, plus one
258 memcpy(store+partPacket, buf, TS_SIZE - partPacket);
259 ret += TS_SIZE - partPacket;
260 buf += TS_SIZE - partPacket;
261 len -= TS_SIZE - partPacket;
262 partPacket = TS_SIZE;
264 { // Packet is properly terminated
265 int rc = processTS(store);
267 partPacket = 0; // Successfully processed
269 return ret; // Try again later.
272 { // Packet not terminated. Find another candidate, and shift store
273 if (!misaligned_mess) {
274 Log::getInstance()->log("TS Demuxer", Log::ERR, "TS Misaligned!A");
275 misaligned_mess=true; // do not alarm more than once
278 while (search < partPacket && store[search] != TS_SIG)
280 partPacket -= search;
281 if (partPacket) memcpy(store, store+search, partPacket);
285 { // Still don't have complete packet. Consume what we do have.
286 memcpy(store+partPacket, buf, len);
293 // Position ourselves at a candidate TS packet
294 while (len > 0 && *buf != TS_SIG)
296 if (!misaligned_mess) {
297 Log::getInstance()->log("TS Demuxer", Log::ERR, "TS Misaligned!B");
298 misaligned_mess=true; // do not alarm more than once
305 if (len < TS_SIZE + 1)
306 { // Not enough data. Store what we have.
307 memcpy(store, buf, len);
313 if (buf[TS_SIZE] != TS_SIG)
314 { // Not terminated correctly.
316 while (len > 0 && *buf != TS_SIG)
323 int rc = processTS(buf);
325 { // Successfully processed
326 buf += TS_SIZE; ret += TS_SIZE; len -= TS_SIZE;
329 { // Processing failed.
338 int DemuxerTS::processTS(UCHAR* buf)
340 int datalen = TS_SIZE - 4;
342 int pid = ( (buf[1] & 0x1F) << 8 ) | buf[2];
343 UCHAR payload = buf[1] & 0x40;
346 if (buf[3] & 0x20) // Adaptation field is present
347 datalen -= (buf[4] + 1);
348 if (datalen < 0) // Error in stream TODO log this
350 if (datalen == 0) // Null packet
352 // if (!(buf[3] &0x10)) return 1; // no payload
353 buf += (TS_SIZE - datalen);
358 if (pid == 0x00) {//PAT, only take first program number, ignore the rest
359 int pmtpid = (*(buf+11)<< 8) | *(buf+12);
360 if ((pmtpid >> 13) != 0x07)
362 Log::getInstance()->log("ProcessTS", Log::DEBUG, "PMTPID=%02x %02x TRAILING 111 not set but %x", *(buf+11),*(buf+12), (pmtpid >> 13));
366 pmtpid = pmtpid & 0x1FFF; //clear upper 3 bits
373 int sectionlength = ((*(buf+2) << 8) & 0x0F ) | *(buf+3);
374 //sectionlength += 4; //include header but subtract crc in the end...
375 int p = 13; //skip fixed part of pmt
376 Channel new_channelinfo;
377 new_channelinfo.numAPids=0;
378 new_channelinfo.numDPids=0;
379 new_channelinfo.numSPids=0;
380 new_channelinfo.number=0;
381 new_channelinfo.type=VDR::RADIO;
382 new_channelinfo.name=NULL;
383 new_channelinfo.tpid=0xFFFFF; //unused, check this
384 new_channelinfo.vpid=0xFFFFF; //unused, check this
385 new_channelinfo.index=0;
387 new_channelinfo.apids.clear();
388 new_channelinfo.dpids.clear();
389 new_channelinfo.spids.clear();
391 while ( p < sectionlength) {
392 int streamtype = *(buf+p);
394 int foundpid = (*(buf+p)<< 8) | *(buf+p+1);
395 p += 2; //skip ES Pid
396 int eslength = ((*(buf+p) << 8) & 0x0F ) | *(buf+p+1);
397 p += 2; //skip ES length
398 if ((foundpid >> 13) != 0x07)
400 Log::getInstance()->log("ProcessTS", Log::DEBUG, "FOUNDPID=%02x %02x TRAILING 111 not set but %x", *(buf+p),*(buf+p+1), (foundpid >> 13));
404 foundpid = foundpid & 0x1FFF; //clear upper 3 bits
408 // Log::getInstance()->log("ProcessTS", Log::DEBUG, "FOUNDPID is %x %x", foundpid,streamtype);
411 case 0x1B: //MPEG 4 for future use
414 if (foundpid != getVID())
416 new_channelinfo.type=VDR::VIDEO;
417 new_channelinfo.vstreamtype=streamtype;
418 new_channelinfo.vpid=foundpid;
419 if (streamtype==0x1b) h264=true;
422 // Log::getInstance()->log("ProcessTS", Log::DEBUG, "Set video PID to %x", foundpid);
424 case 0x0F: //AAC ADTS packaging
425 case 0x11: // LATM packaging
429 newapid.pid = foundpid;
431 newapid.type=streamtype;
434 while (pos< eslength && nolang) {
435 switch (buf[p+pos]) {
437 newapid.desc[0]=buf[p+pos+2];
438 newapid.desc[1]=buf[p+pos+3];
439 newapid.desc[2]=buf[p+pos+4];
442 // Log::getInstance()->log("ProcessTS", Log::DEBUG, "FOUNDLANG is %s", newapid.desc);
449 new_channelinfo.apids.push_back(newapid);
450 new_channelinfo.numAPids++;
451 if (getAID() == 0 && Audio::getInstance()->streamTypeSupported(streamtype)) { //set unset AID to first audio pid that reports itself
452 setAID(foundpid,0,streamtype,false);
453 Log::getInstance()->log("ProcessTS", Log::DEBUG, "Set audio PID to %x", foundpid);
457 case 6: { //Private Data
459 newapid.pid = foundpid;
460 newapid.desc[0]=0; //set it in player
466 while (pos< eslength && (notfound || nolang)) {
467 switch (buf[p+pos]) {
468 case 0x7A: // Enhanced Ac3 Desriptor
469 case 0x6A: {//Ac3 descriptor
470 newapid.type=buf[p+pos];
475 case 0x59: {//SubtitlingDescriptor
477 newapid.type=buf[p+pos];
478 newapid.desc[0]=buf[p+pos+2];
479 newapid.desc[1]=buf[p+pos+3];
480 newapid.desc[2]=buf[p+pos+4];
482 newapid.data1=(buf[p+pos+5]<<8) |(buf[p+pos+6]);
483 newapid.data2=(buf[p+pos+7]<<8) |(buf[p+pos+8]);
484 // Log::getInstance()->log("ProcessTS", Log::DEBUG, "FOUNDSUB is %s", newapid.desc);
489 newapid.desc[0]=buf[p+pos+2];
490 newapid.desc[1]=buf[p+pos+3];
491 newapid.desc[2]=buf[p+pos+4];
494 // Log::getInstance()->log("ProcessTS", Log::DEBUG, "FOUNDLANG is %s", newapid.desc);
498 new_channelinfo.tpid=foundpid;
505 new_channelinfo.dpids.push_back(newapid);
506 new_channelinfo.numDPids++;
507 } else if (type==2) {
508 new_channelinfo.spids.push_back(newapid);
509 new_channelinfo.numSPids++;
513 default://TODO how about subtitles and second audio pids
518 p += eslength; //skip es descriptor
522 bool audioPIDpresent=false; //Check if pids chnages
524 for (i=0;i<channelinfo.numAPids;i++) {
525 if (aID == (int)channelinfo.apids[i].pid) {
526 audioPIDpresent=true;
529 for (i=0;i<channelinfo.numDPids && (! audioPIDpresent);i++) {
530 if (aID == (int)channelinfo.dpids[i].pid) {
531 audioPIDpresent=true;
534 if (! audioPIDpresent) {
536 if (channelinfo.numAPids>0) {
538 while (j<channelinfo.numAPids && !found) {
539 if (Audio::getInstance()->streamTypeSupported(channelinfo.apids[j].type)) {
541 setAID(channelinfo.apids[j].pid,0,channelinfo.apids[j].type,false);
547 if (channelinfo.numDPids>0 && !found) {
549 while (j<channelinfo.numDPids && !found) {
550 if (Audio::getInstance()->streamTypeSupported(channelinfo.dpids[j].type)) {
552 setAID(channelinfo.dpids[j].pid,1,channelinfo.dpids[j].type,false);
559 channelinfo=new_channelinfo;
562 havechannelinfo=true;
576 parseTSPacketDetails(vPacket);
579 rc = submitPacket(vPacket);
589 parseTSPacketDetails(aPacket);
592 rc = submitPacket(aPacket);
602 parseTSPacketDetails(subPacket);
605 rc = submitPacket(subPacket);
609 if (isteletextdecoded && pid == tID)
615 parseTSPacketDetails(tPacket);
618 rc = submitPacket(tPacket);
622 if (rc == 0) return 0;
627 vPacket.init(PESTYPE_VID0);
628 buf += 6; datalen -= 6;
635 aPacket.init(PESTYPE_PRIVATE_1, PESTYPE_SUBSTREAM_AC30);
639 aPacket.init(PESTYPE_AUD0);
642 buf += 6; datalen -= 6;
646 subPacket.init(PESTYPE_PRIVATE_1,PESTYPE_SUBSTREAM_DVBSUBTITLE0);
647 subLength = (buf[4] << 8) + buf[5];
648 buf += 6; datalen -= 6;
650 if (isteletextdecoded && pid == tID)
652 tPacket.init(PESTYPE_PRIVATE_1,PESTYPE_SUBSTREAM_TELETEXTMAX);
653 buf += 6; datalen -= 6;
657 if ( (pid == vID && vActive) ||
658 (pid == aID && aActive) ||
659 (pid == tID && tActive) ||
660 (pid == subID && subActive) )
662 PESPacket* packet = NULL;
663 if (pid == vID) packet = &vPacket;
664 if (pid == aID) packet = &aPacket;
668 if (pid == tID) packet = &tPacket;
671 if (packet->write(buf, datalen) == 0)
672 { // Writing to packet failed. It has overflowed.
675 parseTSPacketDetails(*packet);
678 if (submitPacket(*packet) == 0) return 0;
681 packet->write((UCHAR*)"\200\000\000", 3);
682 packet->write(buf, datalen);
687 if (pid == subID && subActive && subPacket.getLength() == subLength)
689 parsePacketDetails(subPacket);
690 Log::getInstance()->log("DEMUXERTS", Log::DEBUG, "SUBMITTING A SUBTITLE PACKET %d %x", subLength, subPacket.getSubstream());
691 submitPacket(subPacket);
698 ULONG DemuxerTS::getPacketNum()
703 ULONG DemuxerTS::getFrameNumFromPTS(ULLONG pts)
705 ULLONG difference = (1LL<<33);
707 int total = 0, actual = 0;
708 if (pts==0) return 0; //we are in startup
709 pts_map_mutex.Lock();
710 PTSMap::iterator iter = pts_map.begin();
711 while (iter != pts_map.end())
714 //Log::getInstance()->log("DemuxerTS", Log::DEBUG, "getFrameNumfromPTS pts1 %lld pts2 %lld", pts, iter->pts);
715 if (PTSDifference(iter->pts, pts) < PTS_ALLOWANCE)
718 ref_frame = iter->frame;
722 ULLONG newdiff = PTSDifference(pts, iter->pts);
723 if (newdiff < difference)
725 difference = newdiff;
726 ref_frame = iter->frame;
731 if (total > 1 && actual == 1) // We are using the most recent PTS ref.
732 { // Delete the rest.
733 iter = pts_map.begin(); iter++;
734 pts_map.erase(iter, pts_map.end());
736 pts_map_mutex.Unlock();
738 //Log::getInstance()->log("DemuxerTS", Log::DEBUG, "getFrameNumfromPTS pts %lld deleted %d difference %lld", pts, total,difference);
740 if (difference == (1LL<<33))
741 return 0; // We cannot make sense of the pts
743 return ref_frame + difference * fps / 90000;
747 void DemuxerTS::parseTSPacketDetails(PESPacket &packet) // Only important stuff for paket counting reminas
749 parsePacketDetails(packet);
750 if (packetCounting && packet.getPacketType() >= PESTYPE_AUD0 &&
751 packet.getPacketType() <= PESTYPE_AUDMAX)
755 UINT pictsinpacket=packet.countPictureHeaders(h264,pinfo);
758 /* if (!doubledframerate)
760 numpicts=pictsinpacket;
764 numpicts=(pictsinpacket+framereserve)>>1;
765 framereserve=(pictsinpacket+framereserve)%2;
769 if (frameCounting && numpicts &&
770 packet.getPacketType() >= PESTYPE_VID0 &&
771 packet.getPacketType() <= PESTYPE_VIDMAX)
773 frameNumber+=numpicts;
774 ULONG frame_num = frameNumber;
775 if ((h264 || packet.findSeqHeader(h264) > 1) && packet.hasPTS())
778 pts_map_mutex.Lock();
781 me.pts = packet.getPTS();
782 me.frame = frame_num;
783 pts_map_mutex.Unlock();
784 pts_map_mutex.Lock();
785 pts_map.push_front(me);
787 me = pts_map.front();
788 pts_map_mutex.Unlock();
790 //UINT fps = Video::getInstance()->getFPS();
792 // if (doubledframerate) tfps*=2.;
793 long long pts_expected = me.pts + 90000LL*((long long)(((double)(frame_num - me.frame)) / tfps));
795 while (pts_expected < 0) pts_expected += (1LL<<33);
796 while (pts_expected > (1LL<<33)) pts_expected -= (1LL<<33);
798 // this was a workaround for a vdr bug, now fixed, for older recordings deleter the index file
801 if (!doubledframerate
802 &&(abs((long long)PTSDistance(pts_expected, packet.getPTS())-1800) <= 1
803 || abs((long long)PTSDistance(pts_expected, packet.getPTS())-1501) <= 1)) {
804 doubledframerate=true; //Detected p50 or p60
807 if (PTSDistance(pts_expected, packet.getPTS()) > PTS_JUMP_MARGIN) // PTS jump!
809 me.pts = packet.getPTS();
810 me.frame = frame_num;
811 pts_map_mutex.Lock();
812 pts_map.push_front(me);
813 pts_map_mutex.Unlock();
820 bool DemuxerTS::scanForVideo(UCHAR* buf, UINT len, bool &ish264)
824 while (len >= TS_SIZE)
826 if (*buf != TS_SIG) {buf++;len--; continue;}
828 //Pattern scanning won't work for ts
831 int datalen = TS_SIZE - 4;
832 int pid = ( (buf[1] & 0x1F) << 8 ) | buf[2];
833 UCHAR payload = buf[1] & 0x40;
835 if (buf[3] & 0x20) // Adaptation field is present
836 datalen -= (buf[4] + 1);
838 UCHAR* curbuf =buf+ (TS_SIZE - datalen);
841 if (pid == 0x00) {//PAT, only take first program number, ignore the rest
842 int pmtpid = (*(curbuf+11)<< 8) | *(curbuf+12);
843 if ((pmtpid >> 13) != 0x07)
845 Log::getInstance()->log("DemuxerTS", Log::DEBUG, "PMTPID=%02x %02x TRAILING 111 not set but %x", *(curbuf+11),*(curbuf+12), (pmtpid >> 13));
849 pmtpid = pmtpid & 0x1FFF; //clear upper 3 bits
851 Log::getInstance()->log("DemuxerTS", Log::DEBUG, "PMT pid%02x",pmtpid );
854 } else if (pid == pmtpidy) { //PMT
855 int sectionlength = ((*(curbuf+2) << 8) & 0x0F ) | *(buf+3);
856 //sectionlength += 4; //include header but subtract crc in the end...
857 int p = 13; //skip fixed part of pmt
858 while ( p < sectionlength) {
859 int streamtype = *(curbuf+p);
861 int foundpid = (*(curbuf+p)<< 8) | *(curbuf+p+1);
862 p += 2; //skip ES Pid
863 int eslength = ((*(curbuf+p) << 8) & 0x0F ) | *(curbuf+p+1);
864 p += 2; //skip ES length
865 if ((foundpid >> 13) != 0x07)
867 Log::getInstance()->log("DemuxerTS", Log::DEBUG, "FOUNDPID=%02x %02x TRAILING 111 not set but %x", *(buf+p),*(buf+p+1), (foundpid >> 13));
871 foundpid = foundpid & 0x1FFF; //clear upper 3 bits
872 // int pos=0; UNUSED?
873 Log::getInstance()->log("DemuxerTS", Log::DEBUG, "Pid found %02x type %02x",foundpid ,streamtype);
874 if (streamtype==1 || streamtype ==2) {
876 Log::getInstance()->log("DemuxerTS", Log::DEBUG, "Found Mpeg2 Video");
879 if (streamtype==0x1b) {
881 Log::getInstance()->log("DemuxerTS", Log::DEBUG, "Found h264 Video");
885 p += eslength; //skip es descriptor
898 UINT DemuxerTS::stripAudio(UCHAR* buf, UINT len) //it has to be adapted
900 //This function strips all TS Headers and non video payload
905 while (readpos < len ) {
906 if (buf[readpos] != TS_SIG) {readpos++; continue;}
907 UINT oldreadpos=readpos;
909 int datalen = TS_SIZE - 4;
910 int pid = ( (buf[readpos+1] & 0x1F) << 8 ) | buf[readpos+2];
911 UCHAR payload = buf[readpos+1] & 0x40;
912 if (buf[readpos+3] & 0x20) { // Adaptation field is present
913 datalen -= (buf[readpos+4] + 1);
915 if (datalen < 0) {// Error in stream TODO log this
918 if (datalen == 0) {// Null packet
919 readpos=oldreadpos+TS_SIZE;
922 readpos += (TS_SIZE - datalen);
923 UINT towrite=min(datalen,len-readpos);
927 parsePacketDetails(destpaket);
928 memcpy(buf+writepos,destpaket.getData(),destpaket.getSize());
929 writepos+=destpaket.getSize();
931 destpaket.init(PESTYPE_VID0);
932 readpos += 6; towrite -= 6;
937 if (!destpaket.write(buf+readpos,towrite)) {
938 parsePacketDetails(destpaket);
939 memcpy(buf+writepos,destpaket.getData(),destpaket.getSize());
940 writepos+=destpaket.getSize();
941 destpaket.truncate();
942 destpaket.write((UCHAR*)"\200\000\000", 3);
943 destpaket.write(buf+readpos,towrite);
951 readpos=oldreadpos+TS_SIZE;
953 parsePacketDetails(destpaket);
954 memcpy(buf+writepos,destpaket.getData(),destpaket.getSize());
955 writepos+=destpaket.getSize();