[PATCH] Rewrite of mpeg/ps2ts.cpp

Andrew de Quincey <[email protected]> Mon, 11 Aug 2003 17:14:19 +0100
Newsgroups gmane.comp.video.videolan.vls.devel
Message-ID <[email protected]>
Hi, I was trying to use vls to multicast an MPEG2-ps stream and ran into lots 
of problems. Investigating the code showed many issues, so I decided to 
rewrite it to fix them.

This patch fixes/adds the following features:

1) --loop on file inputs now works.

2) ps2ts now copes with PS with Program map tables in them.

I decided it wasn't actually worth parsing the program map itself as then you 
can't get the private stream ids for encapsulated MPEG1 streams... so I just 
made it skip PMTs correctly.

3) Lookahead removed to fix end-of-file issues.

4) All potential buffer overflows and the like removed... well _maybe_ :). If 
anyone spots something I've missed, please tell me....

5) Code should cope with duff MPEG files... streams ending too soon etc.

6) ps2ts now supports looping correctly.

I had to add support to the reader code to allow support for looping... but 
what I've added should not interfere with other parts of the system which do 
not know about the enhancements.

7) Various other potential issues (e.g. duff data from preparsing) removed by 
adding explicit support to ps2ts.

8) ps2ts _should_ now be much more robust.

Phew!

Theres probably a few others I've forgotten about in the meantime. All this 
should be back-compatable with the rest of the system.
vls-cvs-ps2ts.patch (text/x-diff, 39.9 KB)
--- vls.orig/src/modules/ps2ts/ps2ts.cpp	2003-08-11 11:15:48.000000000 +0100
+++ vls/src/modules/ps2ts/ps2ts.cpp	2003-08-11 15:11:26.000000000 +0100
@@ -105,7 +105,8 @@
         C_MpegConverter(pModule, cConfig),
         m_cConverter(cConfig.m_pReader,
                      cConfig.m_pTsProvider, 2,
-                     cConfig.m_pBroadcast->GetOption("mpegversion").ToInt())
+                     cConfig.m_pBroadcast->GetOption("mpegversion").ToInt(),
+                     cConfig.m_pBroadcast->GetOption("loop").ToInt())
 {
   m_bPreParse = (cConfig.m_pBroadcast->GetOption("preparse") == "1");
 
@@ -133,14 +134,13 @@
   C_MpegConverter::InitWork();
 
   m_cConverter.Synch();
-
   if(m_bPreParse)
   {
     for(unsigned int i = 0; i < m_pReader->Size() / 188 / 256; i++)
     {
       // preparsing to make the PAT and the PMT
-      C_TsPacket* pPacket = m_cConverter.GetPacket();
-      ASSERT(pPacket);
+      C_TsPacket* pPacket = m_cConverter.GetPacket(true);
+      if (!pPacket) break;
       m_pTsProvider->ReleasePacket(pPacket);
     }
 
@@ -166,8 +166,9 @@
   switch(m_cConverter.GetStatus())
   {
   case 0:
-    if(!pPacket)
+    if(!pPacket) {
       iRc = MPEG_STREAMERROR;
+    }
     break;
   case -99:
     iRc = MPEG_ENDOFSTREAM;
--- vls.orig/src/modules/filereader/filereader.h	2003-08-11 11:15:48.000000000 +0100
+++ vls/src/modules/filereader/filereader.h	2003-08-11 13:00:04.000000000 +0100
@@ -49,6 +49,7 @@
   virtual s64 Seek(s64 iOffset, s64 bStartPos);
   virtual s64 Size();
   virtual s64 GetPos();
+  virtual void ResetEndOfStream();
 
 protected:
   C_File m_cFile;
--- vls.orig/src/modules/filereader/filereader.cpp	2003-08-11 11:15:48.000000000 +0100
+++ vls/src/modules/filereader/filereader.cpp	2003-08-11 16:49:10.000000000 +0100
@@ -80,7 +80,6 @@
                         m_cFile(pBroadcast->GetOption("filename"))
 {
   m_iLoop = pBroadcast->GetOption("loop").ToInt();
-  if(m_iLoop) m_iLoop--;
 
   if(pBroadcast->GetOption("end") == "1")
     m_bEnd = true;
@@ -123,13 +122,13 @@
   try 
   {
     s64 iRc = m_cFile.Read(pBuff, iSize);
-    // Loop on stream and set the discontinuity flag
-    if((iRc == FILE_EOF) && m_iLoop--)
-    {
-      m_cFile.Seek((s64)0, FILE_SEEK_BEGIN);
-      m_bDiscontinuity = true;
-      iRc = m_cFile.Read(pBuff, iSize);
+    
+    // check for end of stream
+    if ((iRc == FILE_EOF) || (iRc != iSize)) {
+      m_bEndOfStream = true;
+      iRc = 0;
     }
+    
     return iRc;
   }
   catch(E_File e)
@@ -138,6 +137,15 @@
   }
 }
 
+//------------------------------------------------------------------------------
+//
+//------------------------------------------------------------------------------
+void C_FileMpegReader::ResetEndOfStream()
+{
+  m_cFile.Seek((s64)0, FILE_SEEK_BEGIN);
+  m_bDiscontinuity = true;
+  m_bEndOfStream = false;
+}
 
 //------------------------------------------------------------------------------
 //
--- vls.orig/src/mpeg/reader.h	2003-08-11 11:15:48.000000000 +0100
+++ vls/src/mpeg/reader.h	2003-08-11 13:44:45.000000000 +0100
@@ -71,6 +71,11 @@
   void ResetDiscontinuity()
   { m_bDiscontinuity = false; };
 
+  virtual bool EndOfStream()
+  { return m_bEndOfStream; };
+  virtual void ResetEndOfStream()
+  { /* By default, this is not supported */ };
+   
 protected:
   C_Module* m_pModule;
 
@@ -80,6 +85,8 @@
 
   bool m_bDiscontinuity;
 
+  bool m_bEndOfStream;
+   
   handle m_hLog;
 };
 
--- vls.orig/src/mpeg/reader.cpp	2003-08-11 11:15:48.000000000 +0100
+++ vls/src/mpeg/reader.cpp	2003-08-11 16:48:55.000000000 +0100
@@ -63,6 +63,7 @@
   m_hLog = pModule->GetLogger();
 
   m_bDiscontinuity = false;
+  m_bEndOfStream = false;
 }
 
 
--- vls.orig/src/mpeg/ps2ts.cpp	2003-03-14 10:32:49.000000000 +0000
+++ vls/src/mpeg/ps2ts.cpp	2003-08-11 16:41:57.000000000 +0100
@@ -6,6 +6,7 @@
 *
 * Authors: Benoit Steiner <[email protected]>
 *          Arnaud de Bossoreille de Ribou <[email protected]>
+*          Andrew de Quincey <[email protected]>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
@@ -50,7 +51,8 @@
 template <class Reader, class TsProvider>
  C_Ps2Ts<Reader, TsProvider>::C_Ps2Ts(Reader* pReader, TsProvider* pTsProvider,
                                       unsigned int iMaxBufferedTs,
-                                      unsigned int iMpegVersion) :
+                                      unsigned int iMpegVersion,
+				      bool bLooping) :
                              m_cPgrmDescriptor(iMpegVersion),
                              m_cPat(0, 0, 0), m_cPmt(0, 0, 0, 0, 0x50),
                              m_cPendingTS(NO)
@@ -65,20 +67,23 @@
   m_iMaxBufferedTs = iMaxBufferedTs;
 
   m_iDataType = UNKNOWN_DATA;
+  m_iPrivateId = 0;
   m_iStatus = NO_ERR;
-  m_pDelayedPacket = NULL;
   m_bDiscontinuity = false;
 
-  ZERO(m_pCurrentData);
-
-  m_bSendPSI = m_bGenPat = m_bGenPmt = m_bResetPSI = true;
+  m_bSendPSI = m_bGenPat = m_bGenPmt = true;
   m_iTSCounter = 0;
-  m_iPrevSCR = 0;
+  m_bNeedToSendPCR = true;
+  m_iNextPCR = 0;
+  m_iPrevPCR = 0;
+  m_bLooping = bLooping;
+  m_bInMiddleOfPES = false;
+  m_bJustSynched = false;
   
   if(iMpegVersion == 1)
-    m_iPackHeaderLen = MPEG1_PACK_HEADER_LEN;
+    m_iPackHeaderLen = MPEG1_PACK_HEADER_LEN - START_CODE_LEN;
   else
-    m_iPackHeaderLen = MPEG2_PACK_HEADER_LEN;
+    m_iPackHeaderLen = MPEG2_PACK_HEADER_LEN - START_CODE_LEN;
 }
 
 
@@ -89,62 +94,62 @@
  C_Ps2Ts<Reader, TsProvider>::~C_Ps2Ts()
 {
   // The packets belong to the netlist, so don't delete them twice
-  for(unsigned int i = 0; i < m_cPendingTS.Size(); i++)
+  while(m_cPendingTS.Size() > 0)
   {
     C_TsPacket* pPacket = m_cPendingTS.Remove(0);
-    ASSERT(pPacket);
+    if (pPacket) {
     m_pTsProvider->ReleasePacket(pPacket);
   }
+  }
 }
 
 
 //------------------------------------------------------------------------------
 //
 //------------------------------------------------------------------------------
-// The data in the buffer can be anything: only rely on DataType and DataLen
+// The data in the buffer can be anything: only rely on DataType
 //------------------------------------------------------------------------------
 template <class Reader, class TsProvider>
    int C_Ps2Ts<Reader, TsProvider>::Synch()
 {
-  int iRc = m_pReader->Read(m_bBuff, START_CODE_LEN);
-  ASSERT(iRc >= 0);
-  m_iStatus = (iRc != START_CODE_LEN);
-
-  unsigned int iPos = START_CODE_LEN;
-
-  // Check the last bytes read to look for a start code
-  while((U32_AT(m_bBuff[iPos-START_CODE_LEN]) != PES_H_PACK_HEADER) &&
-        !m_iStatus)
-  {
-    iRc = m_pReader->Read(m_bBuff+iPos, 1);
-    ASSERT(iRc >= 0);
-    m_iStatus = (iRc != 1);
+  int iRc;
+  u8 headerCode[4];
+  u64 startPos;
+   
+  // prefill the headercode
+  startPos = m_pReader->GetPos();
+  if (iRc = ReadData(headerCode, 3)) {
+    m_iStatus = iRc;
+    return m_iStatus;
+  }
 
-    iPos++;
-    if(iPos >= sizeof(m_bBuff))
-    {
-      printf("Looping in Synch !!!!!!!!! (buffer size=%d)\n", sizeof(m_bBuff));
-      memcpy(m_bBuff, &m_bBuff[sizeof(m_bBuff)-(START_CODE_LEN-1)],
-             (START_CODE_LEN-1));
-      iPos = START_CODE_LEN-1;
+  // loop, trying to find a PES_H_PACK_HEADER
+  while(true) {
+    // read the last byte of the headercode
+    if (iRc = ReadData(headerCode+3, 1)) {
+      m_iStatus = iRc;
+      break;
     }
+    
+    // found it?
+    if (U32_AT(headerCode) == PES_H_PACK_HEADER) {
+      m_bInMiddleOfPES = false;
+      m_bJustSynched = true;
+      m_bNeedToSendPCR = false;
+      m_iNextPCR = 0;
+      m_iPrevPCR = 0;
+      break;
   }
 
-  // Check the last bytes read to detect why we left the loop
-  if(U32_AT(m_bBuff[iPos-START_CODE_LEN]) != PES_H_PACK_HEADER)
-  {
-    // We left the while loop because of a read error
+    // OK, move the topmost 3 bytes into position
+    memcpy(headerCode, headerCode+1, 3);
+    
+    // just give up if we haven't found one within a reasonable distance
+    if ((m_pReader->GetPos() - startPos) > 1024 * 1024) {
+      printf("Failed to find a PACK_HEADER within 1Mb.\n");
     m_iStatus = GEN_ERR;
+      break;
   }
-  else
-  {
-    printf("Synchronised with PS stream\n");
-    m_iDataType = PES_H_PACK_HEADER;
-    iRc =  m_pReader->Read(m_bBuff+iPos, PES_SIZE_LEN);
-    ASSERT(iRc >= 0);
-    m_iStatus = (iRc != PES_SIZE_LEN);
-    m_pCurrentData = m_cPgrmDescriptor.GetDescriptor(m_iDataType & 0xFF);
-    m_iDataLen = U16_AT(m_bBuff[iPos]);
   }
 
   return m_iStatus;
@@ -155,58 +160,44 @@
 //
 //------------------------------------------------------------------------------
 template <class Reader, class TsProvider>
-   C_TsPacket* C_Ps2Ts<Reader, TsProvider>::GetPacket()
+   C_TsPacket* C_Ps2Ts<Reader, TsProvider>::GetPacket(bool bPreparsing)
 {
-  if(m_cPendingTS.Size() == 0)
-  {
-    if(!m_bSendPSI)
-    {
-      if(m_pDelayedPacket)
-      {
-        m_cPendingTS.PushEnd(m_pDelayedPacket);
-        m_pDelayedPacket = NULL;
-      }
-      else
-      {
-        int iRc = FetchPackets();
-        if(iRc && !m_bSendPSI)
-        {
-          do
-          {
-            if(iRc == UNKNOWN_DATA)
-            {
-              iRc = Synch();
-              if(!iRc)
-                iRc = FetchPackets();
-            }
-            else if(iRc == SKIPPED_DATA)
-              iRc = FetchPackets();
-          }
-          while((iRc == SKIPPED_DATA || iRc == UNKNOWN_DATA) && !m_bSendPSI);
-        }
-        else if(!m_bSendPSI)
-        {
-          // Check if we will have to insert PSI on the next iteration: we
-          // add the PSI in the stream every half second
-          // (For 1.5 Mbps streams, about 1000 TS packets are sent every second)
-          m_bSendPSI = m_bResetPSI = (m_iTSCounter % 500 <= m_iMaxBufferedTs);
-//          printf("mitscounter: %d\n", m_iTSCounter);
+  int iRc;
+  bool bStreamLooped = false;
+   
+  // if there's nothing left in the buffer. we need to fill it
+  if (m_cPendingTS.Size() == 0) {
+    
+    // keep looping until the buffer is full
+    while((m_cPendingTS.Size() < m_iMaxBufferedTs) && (!bStreamLooped)) {
+      
+      // If we're not already sending PSI, check if we need to send it again
+      if ((!m_bSendPSI) && (!bPreparsing)) {
+	// check if resend interval is up
+	if (m_bSendPSI = (m_iTSCounter % 500 <= m_iMaxBufferedTs)) {
+	  m_cPat.TsReset();
+	  m_cPmt.TsReset();
         }
+	
+	// check if a new PMT has appeared
+	if (m_cPgrmDescriptor.m_bNewPmt) {
+	  m_bSendPSI = true;
+	  m_bGenPmt = true;
+	  m_cPgrmDescriptor.m_bNewPmt = false;
       }
     }
 
-    if(m_bSendPSI)
-    {
-      if(m_bGenPat)
-      {
+      // Send PSI packets if necessary
+      if ((m_bSendPSI) && (!bPreparsing)) {
+	// first of all regenerate any of the PSI information necessary
+	if(m_bGenPat) {
         *m_cPat.GetLowLevelPat() = m_cPgrmDescriptor.m_sPat;
         m_cPat.Generate();
         m_cPat.GetLowLevelPat()->p_first_program = NULL;
         m_bGenPat = false;
+	  m_cPat.TsReset();
       }
-
-      if(m_bGenPmt)
-      {
+	if(m_bGenPmt) {
         m_cPgrmDescriptor.m_sPmt.i_version =
                         (m_cPgrmDescriptor.m_sPmt.i_version + 1) & 0x1f;
         *m_cPmt.GetLowLevelPmt() = m_cPgrmDescriptor.m_sPmt;
@@ -214,50 +205,85 @@
         m_cPmt.GetLowLevelPmt()->p_first_es = NULL;
         m_cPmt.GetLowLevelPmt()->p_first_descriptor = NULL;
         m_bGenPmt = false;
-      }
-
-      if(m_bResetPSI)
-      {
-        m_cPat.TsReset();
         m_cPmt.TsReset();
-        m_bResetPSI = false;
       }
 
-      for(unsigned int i = m_cPendingTS.Size(); i < m_iMaxBufferedTs; i++)
-      {
+	// output PSI information as necessary
+        if (m_cPat.TsHasNext()) {
         C_TsPacket* pPacket = m_pTsProvider->GetPacket();
-        ASSERT(pPacket);
+	  if (pPacket == NULL) {
+	    m_iStatus = GEN_ERR;
+	    break;
+	  }
 
-        if(m_cPat.TsHasNext())
-        {
           m_cPat.TsWrite(pPacket);
           m_cPendingTS.PushEnd(pPacket);
           m_iTSCounter++;
+        } else if (m_cPmt.TsHasNext()) {
+	  C_TsPacket* pPacket = m_pTsProvider->GetPacket();
+	  if (pPacket == NULL) {
+	    m_iStatus = GEN_ERR;
+	    break;
         }
-        else if(m_cPmt.TsHasNext())
-        {
+	  
           m_cPmt.TsWrite(pPacket);
           m_cPendingTS.PushEnd(pPacket);
           m_iTSCounter++;
-        }
-        else
-        {
+        } else {
+	  // we've finished sending PSI for this time
           m_bSendPSI = false;
-          m_pTsProvider->ReleasePacket(pPacket);
-          if(m_pDelayedPacket)
-          {
-            m_cPendingTS.PushEnd(m_pDelayedPacket);
-            m_pDelayedPacket = NULL;
           }
         }
-        if(!m_cPat.TsHasNext() && !m_cPmt.TsHasNext())
-          m_bSendPSI = false;
+      
+      // not sending PSI => grab some REAL data packets
+      if ((!m_bSendPSI) || (bPreparsing)) {
+	// fetch some packets
+	if (iRc = FetchPackets(bPreparsing)) {
+	  // special handling for some return codes
+	  switch(iRc) {
+	  case UNKNOWN_DATA:
+	    iRc = Synch();
+	    break;
+	    
+	  case SKIPPED_DATA:
+	    iRc = 0;
+	    break;
+	    
+	  case END_OF_STREAM:
+	    if (m_bLooping) {
+	      // try and reset the stream
+	      m_pReader->ResetEndOfStream();
+	      
+	      // if that failed, the reader must not support 
+	      // resetting the end of stream. We have to stop.
+	      if (m_pReader->EndOfStream()) {
+		break;
       }
+	      
+	      // OK, synch with the stream again
+	      iRc = Synch();
+	      m_bSendPSI = true;
+	      m_bGenPat = true;
+	      m_bGenPmt = true;
+	      bStreamLooped = true;
     }
+	    break;
   }
 
-  ASSERT(m_cPendingTS.Size() > 0);
-  ASSERT(m_cPendingTS.Size() <= m_iMaxBufferedTs);
+	  // OK, was there really a problem?
+	  if (iRc) {
+	    m_iStatus = iRc;
+	    break;
+	  }
+	}
+	
+	// if we're preparsing, exit as soon as we have one packet
+	if (bPreparsing && m_cPendingTS.Size()) {
+	  break;
+	}
+      }
+    }
+  }
 
   // Return the first packet
   if(m_cPendingTS.Size() > 0)
@@ -271,83 +297,58 @@
 //
 //------------------------------------------------------------------------------
 template <class Reader, class TsProvider>
-   int C_Ps2Ts<Reader, TsProvider>::FetchPackets()
+   int C_Ps2Ts<Reader, TsProvider>::FetchPackets(bool bPreparsing)
 {
-  ASSERT(!m_iStatus || m_iStatus == SKIPPED_DATA || m_iStatus == UNKNOWN_DATA);
-
-  u8 iPosInTs = 0;
-  C_TsPacket* pPacket = m_pTsProvider->GetPacket();
-  ASSERT(pPacket);
-
-  // Handle the pack header if any at the current position in the stream
-  if(m_iDataType == PES_H_PACK_HEADER)
-  {
-    m_iStatus = ParsePackHeader(pPacket, &iPosInTs);
+  int iRc;
 
-    // There also can be a system header following the pack header
-    if(m_iDataType == PES_H_SYSTEM_HEADER && !m_iStatus)
-    {
-      m_iStatus = ParseSystemHeader(pPacket, &iPosInTs);
+  // if we're in the middle of parsing a PES packet,
+  // just do that immediately with no further ado
+  if (m_bInMiddleOfPES) {
+    return ParsePES(bPreparsing);
     }
 
-    // Leave now if something wrong occured
-    if(m_iStatus != NO_ERR)
-      return GEN_ERR;
+  // attempt to read in the start code/ data type
+  if (!m_bJustSynched) {
+    if (iRc = ReadData(m_bBuff, START_CODE_LEN)) {
+      return iRc;
   }
-
-  // No we must have reached the beginning of a PES packet or the end of
-  // the stream
-  if(IsDataPesHeader(m_iDataType))
-  {
-    // This is a data PES
-    m_iStatus = ParsePES(pPacket, &iPosInTs);
+    m_iDataType = U32_AT(m_bBuff);
+  } else {
+    // if we've just synched we _know_ we're just after a 
+    // PACK_HEADER start code.
+    m_bJustSynched = false;
+    return ParsePackHeader(bPreparsing);
   }
-  else
-  {
-    // This is a control PES
-    switch(m_iDataType)
-    {
+   
+  // OK, process the packet
+  switch(m_iDataType) {
+  case PES_H_PACK_HEADER:
+    return ParsePackHeader(bPreparsing);
+    
+  case PES_H_SYSTEM_HEADER:
+    return ParseSystemHeader();
+    
       case PES_H_PADDING:
-      {
-//        printf("Padding pes encountered\n");
-        //m_iStatus = ParsePadding(pPacket, &iPosInTs);
-        m_iStatus = ParsePES(pPacket, &iPosInTs);
-        break;
-      }
+    return ParsePES(bPreparsing);
+
       case PES_H_PGRM_MAP:
-      {
-//        printf("Program map pes encountered\n");
-        m_iStatus = ParsePgrmMap(pPacket, &iPosInTs);
-        // A foutre dans parse pgrm_map_pes quand le pes est completement fini
-        m_bSendPSI = m_bResetPSI = true;
-        break;
-      }
       case PES_H_PRIVATE_2:
       case PES_H_PGRM_DIR:
-      {
-        // Not interessting -> Trash
-//        printf("not interesting pes encountered -> trash \n");
-        m_iStatus = SkipPES(pPacket, &iPosInTs);
-        break;
-      }
+    return SkipPES();
+    
       case PES_H_END_OF_PS:
-      {
-        // Just return NULL
-//        printf("end of ps encountered\n");
-        m_pTsProvider->ReleasePacket(pPacket);
-        m_iStatus = END_OF_STREAM;
-        break;
-      }
+    return END_OF_STREAM;
+        
       default:
-      {
-//        printf("unknown packet (%x) encoutered\n", m_iDataType);
-        m_pTsProvider->ReleasePacket(pPacket);
-        m_iStatus = UNKNOWN_DATA;
-      }
+    if (IsDataPesHeader(m_iDataType)) {
+      return ParsePES(bPreparsing);
+    } else {
+      return UNKNOWN_DATA;
     }
   }
   
-  return m_iStatus;
+  // should never get here
+  return GEN_ERR;
 }
 
 
@@ -355,88 +356,62 @@
 //
 //------------------------------------------------------------------------------
 template <class Reader, class TsProvider>
-   int C_Ps2Ts<Reader, TsProvider>::ParsePackHeader(C_TsPacket* pPacket, u8* pPosInTs)
+   int C_Ps2Ts<Reader, TsProvider>::ParsePackHeader(bool bPreparsing)
 {
-  ASSERT(pPacket);
-
-//  printf("Parsing PACK_HEADER\n");
+  int iRc;
 
-  int iRc = m_pReader->Read(m_bBuff+LOOK_AHEAD_LEN, m_iPackHeaderLen);
-  ASSERT(iRc >= 0);
-  iRc = (iRc != m_iPackHeaderLen);
+  // try to read in the data for the pack header
+  if (iRc = ReadData(m_bBuff, m_iPackHeaderLen)) {
+    return iRc;
+  }
 
-  // No stuffing by default
-  u8 iStuffLen = 0;
+  // don't bother doing anything more if we're preparsing
+  if (bPreparsing) {
+    return iRc;
+  }
 
   if(m_iPackHeaderLen == MPEG1_PACK_HEADER_LEN)
   {
     // Parse the SCR (MPEG1 format)
-    u64 iHighBits = m_bBuff[START_CODE_LEN] & 0x0E;
-    u64 iMiddleBits = U16_AT(m_bBuff[START_CODE_LEN+1]) & 0xFFFE;
-    u64 iLowBits = U16_AT(m_bBuff[START_CODE_LEN+3]) & 0xFFFE;
-    ASSERT((m_bBuff[START_CODE_LEN] & 0x01));
-    ASSERT((m_bBuff[START_CODE_LEN+2] & 0x01));
-    ASSERT((m_bBuff[START_CODE_LEN+4] & 0x01));
-   
-    u64 iSCR = iHighBits << 29 | iMiddleBits << 14 | iLowBits >> 1;
-//    printf("Date mpeg1: %Ld\n", iSCR);
- 
-    // Build the TS header to put this date
-    *pPosInTs = pPacket->BuildAdaptionField(iSCR);
+    u64 iHighBits = m_bBuff[0] & 0x0E;
+    u64 iMiddleBits = U16_AT(m_bBuff[1]) & 0xFFFE;
+    u64 iLowBits = U16_AT(m_bBuff[3]) & 0xFFFE;
+    ASSERT((m_bBuff[0] & 0x01));
+    ASSERT((m_bBuff[2] & 0x01));
+    ASSERT((m_bBuff[4] & 0x01));
+   
+    // store it for when we need to output it
+    m_iNextPCR = iHighBits << 29 | iMiddleBits << 14 | iLowBits >> 1;
+    m_bNeedToSendPCR = true;
   }
   else
   {
     // Parse the SCR (MPEG2 format)
-    u64 iHighBits = m_bBuff[START_CODE_LEN] & 0x38;
-    u64 iMiddleBits = U32_AT(m_bBuff[START_CODE_LEN]) & 0x03FFF800;
-    u64 iLowBits = U32_AT(m_bBuff[START_CODE_LEN+2]) & 0x03FFF800;
-    ASSERT((m_bBuff[START_CODE_LEN] & 0x4));
-    ASSERT((m_bBuff[START_CODE_LEN+2] & 0x4));
-    ASSERT((m_bBuff[START_CODE_LEN+4] & 0x4));
-
-    u64 iSCR = iHighBits << 27 | iMiddleBits << 4 | iLowBits >> 11;
-
-    // Kludge pour tester horloge
-//    struct timeval sTimeval;
-//    gettimeofday(&sTimeval, NULL);
-//    printf("Date mpeg2: %Ld, date systeme %d (en s)\n", iSCR, sTimeval.tv_sec);
-    
-    // Build the TS header to put this date
-    *pPosInTs = pPacket->BuildAdaptionField(iSCR);
-
-//    if(iSCR - m_iPrevSCR > 800)
-//      printf("time : %Ld ; delta : %Ld\n", iSCR, iSCR - m_iPrevSCR);
-    if(m_iPrevSCR >= iSCR)
-    {
-      printf("Time discontinuity in PS stream\n");
-      m_bDiscontinuity = true;
+    u64 iHighBits = m_bBuff[0] & 0x38;
+    u64 iMiddleBits = U32_AT(m_bBuff[0]) & 0x03FFF800;
+    u64 iLowBits = U32_AT(m_bBuff[2]) & 0x03FFF800;
+    ASSERT((m_bBuff[0] & 0x4));
+    ASSERT((m_bBuff[2] & 0x4));
+    ASSERT((m_bBuff[4] & 0x4));
+
+    // store it for when we need to output it
+    m_iNextPCR = iHighBits << 27 | iMiddleBits << 4 | iLowBits >> 11;
+    m_bNeedToSendPCR = true;
+    
+    // Read additional stuffing bytes if any
+    int iStuffLen = m_bBuff[m_iPackHeaderLen-1] & 0x7;
+    if (iStuffLen) {
+      iRc = SkipData(iStuffLen);
     }
-    m_iPrevSCR = iSCR;
-  
-    // Read additional stuffing bytes if any (MPEG2 only)
-    if(m_iPackHeaderLen == 10 + START_CODE_LEN)
-    {
-      iStuffLen = m_bBuff[m_iPackHeaderLen-1] & 0x7;
-      if(iStuffLen && !iRc)
-      {
-        iRc = m_pReader->Read(m_bBuff+LOOK_AHEAD_LEN+m_iPackHeaderLen, iStuffLen);
-        ASSERT(iRc >= 0);
-        iRc = (iRc != iStuffLen);
       }
 
-//      printf("Stuffing of size %d found: actual length is: %d\n", iStuffLen, m_iPackHeaderLen+iStuffLen);
-    }
+  // check for discontinuity
+  if (m_iPrevPCR >= m_iNextPCR) {
+    m_bDiscontinuity = true;
   }
+  m_iPrevPCR = m_iNextPCR;
   
-  // Look Ahead of 6
-  m_iDataType = U32_AT(m_bBuff[m_iPackHeaderLen+iStuffLen]);
-  m_pCurrentData = m_cPgrmDescriptor.GetDescriptor(m_iDataType & 0xFF);
-  m_iDataLen = U16_AT(m_bBuff[m_iPackHeaderLen+iStuffLen+START_CODE_LEN]);
-  m_bPESStart = true;
-
-  //printf("Next data will be: %X (len: %d)\n",  m_iNextData, m_iNextLength);
-//  printf("Next data will be: %X (len: %d)\n",  m_iDataType, m_iDataLen);
-
+  // finished
   return iRc;
 }
 
@@ -445,21 +420,17 @@
 //
 //------------------------------------------------------------------------------
 template <class Reader, class TsProvider>
-   int C_Ps2Ts<Reader, TsProvider>::ParseSystemHeader(C_TsPacket* pPacket, u8* iPosInTs)
+   int C_Ps2Ts<Reader, TsProvider>::ParseSystemHeader()
 {
-//  printf("Parsing SYSTEM_HEADER\n"); 
-
-  int iRc = m_pReader->Read(m_bBuff, m_iDataLen+LOOK_AHEAD_LEN);
-  ASSERT(iRc >= 0);
-  iRc = (iRc != m_iDataLen+LOOK_AHEAD_LEN);
-
-//  printf("Length: %d\n", m_iDataLen);
-
-  m_iDataType = U32_AT(m_bBuff[m_iDataLen]);
-  m_pCurrentData = m_cPgrmDescriptor.GetDescriptor(m_iDataType & 0xFF);
-  m_iDataLen = U16_AT(m_bBuff[m_iDataLen+START_CODE_LEN]);
+  int iRc;
 
+  // read size of the system header
+  if (iRc = ReadDataLength(&m_iDataLen)) {
   return iRc;
+  }
+   
+  // skip the data
+  return SkipData(m_iDataLen);
 }
 
 
@@ -467,242 +438,170 @@
 //
 //------------------------------------------------------------------------------
 template <class Reader, class TsProvider>
-   int C_Ps2Ts<Reader, TsProvider>::ParsePES(C_TsPacket* pPacket, u8* pPosInTs)
+   int C_Ps2Ts<Reader, TsProvider>::ParsePES(bool bPreparsing)
 {
-  ASSERT(pPacket);
-  ASSERT(pPosInTs);
-
-  // Pour le debug uniquement
-//  if(m_bPESStart)
-//  {
-//    printf("Parsing PES of type %x\n", m_iDataType);
-//    printf("PES Length: %d (without the 6 first bytes)\n", m_iDataLen);
-//  }
-//  else
-//    printf("Continuing to parse PES (remaining length: %d)\n", m_iDataLen);
-
-
-  // This variable only exists to speed up the process of looking the value of
-  // pPosInTs
-  u8 iPos;
-
-  // Write the data in the TS that depends on our position in the PES
-  if(m_bPESStart)
-  {
-    // Write the header with the bUnitStart flag set to true
-    ASSERT(m_pCurrentData);
-    ASSERT(m_pCurrentData->GetId() == (m_iDataType&0xFF));
-    u8 iCounter = m_pCurrentData->GetCounter();
-    u16 iPid =  m_pCurrentData->GetPid();
-    iPos = pPacket->BuildHeader(iPid, true, iCounter);
+  int iPos = 0;
+  int iPesPos = 0;
+  int iRc;
+
+  // read size of the PES packet
+  if (!m_bInMiddleOfPES) {
+    if (iRc = ReadDataLength(&m_iDataLen)) {
+      return iRc;
+    }
+  }
 
-    // We can have parsed an adaption field if the PES comes just after a
-    // pack header
-    if(iPos < *pPosInTs)
-    {
-      if(iPid == m_cPgrmDescriptor.GetPcrPid())
-      {
-      // The adaption_field_control has been overwritten so that the bit
-      // which indicates an adaption_field is no more set to 1
-      byte* pTsPayload = (byte*)(*pPacket);
-      pTsPayload[iPos-1] |= 0x20;
+  // Extract the current TS descriptor.
+  C_ElementDescriptor* curDesc;
+  if ((m_iDataType & 0xff) != PES_ID_PRIVATE_1) {
+    curDesc = m_cPgrmDescriptor.GetDescriptor(m_iDataType & 0xFF);
+  } else { 
+    // Is an encapsulated MPEG1 stream.. special handling needed
+    // if we're at the start of a PES packet, read (and cache) the 
+    // header and find the MPEG1 stream ID
+    if (!m_bInMiddleOfPES) {
+      // read in the start of the PES header
+      if (iRc = ReadData(m_bCache, 3)) {
+	return iRc;
+      }
+      m_iCacheLen = 3;
 
-      // Set the right position
-      iPos = *pPosInTs;
-      if(m_bDiscontinuity)
-      {
-        ASSERT(pPacket->HasPCR());
-        ASSERT(pPacket->SetDiscontinuityFlag());
-        pPacket->SetDiscontinuityFlag();
-        m_bDiscontinuity = false;
+      // read in the rest of the PES header
+      if (iRc = ReadData(m_bCache + m_iCacheLen, m_bCache[2])) {
+	return iRc;
       }
+      m_iCacheLen += m_bCache[2];
+      
+      // read in the start of the MPEG1 header
+      u8* mpeg1Header = m_bCache + m_iCacheLen;
+      if (iRc = ReadData(m_bCache + m_iCacheLen, 4)) {
+	return iRc;
       }
+      m_iCacheLen += 4;
+      
+      // finally, grab the mpeg1 header stream id
+      m_iPrivateId = mpeg1Header[3];
     }
     
-    // Write the beginning of the PES packet (corresponds to the look ahead)
-    byte* pTsPayload = (byte*)(*pPacket);
-    SET_U32_TO(pTsPayload[iPos], m_iDataType);
-    SET_U16_TO(pTsPayload[iPos+START_CODE_LEN], m_iDataLen);
-    iPos += START_CODE_LEN + PES_SIZE_LEN;
+    // get the descriptor taking into account the MPEG1 stream id
+    curDesc = m_cPgrmDescriptor.GetDescriptor(m_iDataType & 0xFF, m_iPrivateId);
   }
-  else
-  {
-    // We shouldn't have any adaption field
-    ASSERT(*pPosInTs == 0);
 
-    // Write the header with the bUnitStart flag set to false
-    ASSERT(m_pCurrentData);
-    ASSERT(m_pCurrentData->GetId() == (m_iDataType&0xFF));   
-    u8 iCounter = m_pCurrentData->GetCounter();
-    u16 iPid =  m_pCurrentData->GetPid();
-    iPos = pPacket->BuildHeader(iPid, false, iCounter);
+  // process the PES data
+  while((m_iDataLen > 0) && (m_cPendingTS.Size() < m_iMaxBufferedTs)) {
+    // check if a new PMT has appeared. If so, just exit this loop.
+    // The caller will take care of generating it and possibly calling us
+    // again to add in more PES data
+    if ((!bPreparsing) && (m_cPgrmDescriptor.m_bNewPmt)) {
+      break;
   }
 
+    // Grab a new TS packet to use
+    C_TsPacket* pPacket = m_pTsProvider->GetPacket();
 
-  // Now read the data carried in the PES
-  for(unsigned int i = 0; i < m_iMaxBufferedTs; i++)
-  {
-    // Fill the Ts packet
-    if(m_iDataLen >= TS_PACKET_LEN - iPos)
-    {
-      // Fill the remaining payload of the TS packet
-      m_iStatus = m_pReader->Read(((byte*)(*pPacket)+iPos), TS_PACKET_LEN-iPos);
-      ASSERT(m_iStatus >= 0);
-      m_iStatus = (m_iStatus != TS_PACKET_LEN-iPos);
+    // build the start of the TS packet
+    if(!m_bInMiddleOfPES) { // we're at the start of the PES packet
+      // build the header
+      u16 iPid =  curDesc->GetPid();
+      u8 iCounter = curDesc->GetCounter();
+      iPos = pPacket->BuildHeader(iPid, true, iCounter);
 
-      m_iDataLen -= (TS_PACKET_LEN-iPos);
+      // if we need to send the PCR, and we're sending a packet for the stream
+      // which contains the PCR, do it!
+      if ((m_bNeedToSendPCR) && (iPid == m_cPgrmDescriptor.GetPcrPid())) {
+	// build the adaptation field
+	iPos = pPacket->BuildAdaptionField(m_iNextPCR);
+	m_bNeedToSendPCR = false;
 
-      //-----------------
-      if(m_bPESStart)
-      {
-      if(*((byte*)(*pPacket) + iPos - 3) == PES_ID_PRIVATE_1)
-      {
-//      printf("Id : 0x%x", *((byte*)(*pPacket) + iPos - 3));
-        u8 iOffset = *((byte*)(*pPacket) + iPos + 2);
-//        printf(" Offset : 0x%x", iOffset);
-        u8 iPrivateId = *((byte*)(*pPacket) + iPos + 3 + iOffset);
-//        printf(" PrivateId : 0x%x", iPrivateId);
-/*        if((iPrivateId & 0xf0) == 0x80)
-        {*/
-          m_pCurrentData = m_cPgrmDescriptor.GetDescriptor(m_iDataType & 0xFF,
-                                                           iPrivateId);
-          u16 iPid =  m_pCurrentData->GetPid();
-          u16 iData = U16_AT(*((byte*)(pPacket) + 1));
-//          printf(" Data : 0x%x", iData);
-          iPid = (iPid & 0x1fff) | (iData & 0xe000);
-//          printf(" NewPid : 0x%x", iPid);
-          SET_U16_TO(*((byte*)(pPacket) + 1), iPid);
-          *((byte*)(pPacket) + 3) = (m_pCurrentData->GetCounter() & 0x0f) |
-                                    (*((byte*)(pPacket) + 3) & 0xf0);
-/*        }*/
-//      printf("\n");
-      }
+	// handle any discontinuity
+	if(m_bDiscontinuity) {
+	  pPacket->SetDiscontinuityFlag();
+	  m_bDiscontinuity = false;
       }
-      //-----------------
     }
-    else
-    {
-      // m_iDataLen shouldn't be null
-      ASSERT(m_iDataLen > 0);
 
-//      printf("Stuffing needed: iPos=%d, DataLen=%d\n", iPos, m_iDataLen);
+      // write the beginning of the PES packet (since we've already read it)
+      byte* pTsPayload = (byte*)(*pPacket);
+      SET_U32_TO(pTsPayload[iPos], m_iDataType);
+      SET_U16_TO(pTsPayload[iPos+START_CODE_LEN], m_iDataLen);
+      iPesPos = START_CODE_LEN + PES_SIZE_LEN;
+      iPos += iPesPos;
+    } else {
+      // somewhere in the middle of a PES packet
+      u16 iPid = curDesc->GetPid();
+      u8 iCounter = curDesc->GetCounter();
+      iPos = pPacket->BuildHeader(iPid, false, iCounter);
+      iPesPos = 0;
+    }
 
-      // Write the end of the PES packet in the TS packet
+    // calculate how much data we can send
+    int readSize = TS_PACKET_LEN - iPos;
 
-      if(m_bPESStart)
-      {
-        // We have already written the PES start code and the pes length at the
-        // beginning of the TS packet, which will be erased by the stuffing bytes:
-        // we must rewrite them
-        iPos = pPacket->AddStuffingBytes(m_iDataLen+START_CODE_LEN+PES_SIZE_LEN);
+    // If there is not enough data to completely fill the TS packet, 
+    // add stuffing bytes
+    if((m_iDataLen+iPesPos) < readSize) {
+      // add enough stuffing bytes
+      iPos = pPacket->AddStuffingBytes(m_iDataLen+iPesPos);
+      readSize = m_iDataLen;
+      
+      // if we're at the start of a PES packet,
+      // we'll need to rewrite the start of it again as it has moved
+      if (!m_bInMiddleOfPES) {
         byte* pTsPayload = (byte*)(*pPacket);
         SET_U32_TO(pTsPayload[iPos], m_iDataType);
         SET_U16_TO(pTsPayload[iPos+START_CODE_LEN], m_iDataLen);        
-        iPos += START_CODE_LEN+PES_SIZE_LEN;
+	iPos += START_CODE_LEN + PES_SIZE_LEN;
       }
-      else
-      {
-        // Simply add the stuffing bytes at the end of the TS header
-        iPos = pPacket->AddStuffingBytes(m_iDataLen);
       }
       
-      m_iStatus = m_pReader->Read(((byte*)(*pPacket)+iPos), m_iDataLen);
-      ASSERT(m_iStatus >= 0);
-      m_iStatus = (m_iStatus != m_iDataLen);
-        
-      //-----------------
-      if(m_bPESStart)
-      {
-      if(*((byte*)(*pPacket) + iPos - 3) == PES_ID_PRIVATE_1)
-      {
-//      printf("Id : 0x%x", *((byte*)(*pPacket) + iPos - 3));
-        u8 iOffset = *((byte*)(*pPacket) + iPos + 2);
-//        printf(" Offset : 0x%x", iOffset);
-        u8 iPrivateId = *((byte*)(*pPacket) + iPos + 3 + iOffset);
-//        printf(" PrivateId : 0x%x", iPrivateId);
-/*        if((iPrivateId & 0xf0) == 0x80)
-        {*/
-          m_pCurrentData = m_cPgrmDescriptor.GetDescriptor(m_iDataType & 0xFF,
-                                                           iPrivateId);
-          u16 iPid =  m_pCurrentData->GetPid();
-          u16 iData = U16_AT(*((byte*)(pPacket) + 1));
-//          printf(" Data : 0x%x", iData);
-          iPid = (iPid & 0x1fff) | (iData & 0xe000);
-//          printf(" NewPid : 0x%x", iPid);
-          SET_U16_TO(*((byte*)(pPacket) + 1), iPid);
-          *((byte*)(pPacket) + 3) = (m_pCurrentData->GetCounter() & 0x0f) |
-                                    (*((byte*)(pPacket) + 3) & 0xf0);
-/*        }*/
-//      printf("\n");
-      }
-      }
-      //-----------------
-
-      // All data have been read
-      m_iDataLen = 0;
+    // first of all copy any data we've cached across
+    if (m_iCacheLen) {
+      // work out how much to read from the cache
+      int cacheReadSize = m_iCacheLen;
+      if (cacheReadSize > readSize) {
+	cacheReadSize = readSize;
+      }
+
+      // copy it across and update
+      memcpy(((byte*)(*pPacket)+iPos), m_bCache, cacheReadSize);
+      readSize -= cacheReadSize;
+      iPos += cacheReadSize;
+      m_iDataLen -= cacheReadSize;
+      m_iCacheLen -= cacheReadSize;
+    }
+    
+    // OK, read the data from the stream into the TS packet
+    if (readSize) {
+      if (iRc = ReadData(((byte*)(*pPacket)+iPos), readSize)) {
+	m_pTsProvider->ReleasePacket(pPacket);
+	return iRc;
     }
-
-    // We are no more at the beginning of the PES yet
-    m_bPESStart = false;
-
-    // Increase the number of TS packets that have been build
-    m_pCurrentData->IncreaseCounter();
-    m_iTSCounter++;
-
-    if(m_cPgrmDescriptor.m_bNewPmt)
-    {
-      // Delay this packet to send PSI first
-      ASSERT(!m_pDelayedPacket);
-      m_pDelayedPacket = pPacket;
-      m_bSendPSI = m_bGenPmt = m_bResetPSI = true;
-      m_cPgrmDescriptor.m_bNewPmt = false;
-      i = m_iMaxBufferedTs - 1;
+      m_iDataLen -= readSize;
     }
-    else
-    {
+
       // Put the TS packet in the list of pending TS
       m_cPendingTS.PushEnd(pPacket);
-    }
-    ZERO(pPacket);
 
-    // Prepare the next iteration if we must loop
-    if(m_iDataLen > 0)
-    {
-      // There is a second condition for looping since we don't read
-      // the complete PES in a single operation
-      if(i < m_iMaxBufferedTs - 1)
-      {   
-        // Get another TS packet to fill
-        pPacket = m_pTsProvider->GetPacket();
-        ASSERT(pPacket);
-
-        // Build the new TS header
-        ASSERT(m_pCurrentData);
-        ASSERT(m_pCurrentData->GetId() == (m_iDataType&0xFF));
-        u8 iCounter = m_pCurrentData->GetCounter();
-        u16 iPid =  m_pCurrentData->GetPid();
-        iPos = pPacket->BuildHeader(iPid, false, iCounter);
-      }
+    // Update TS counters
+    if (!bPreparsing) {
+      curDesc->IncreaseCounter();
+      m_iTSCounter++;
     }
-    else
-    {
-      // Look ahead for next iteration
-      m_iStatus = m_pReader->Read(m_bBuff, LOOK_AHEAD_LEN);
-      ASSERT(m_iStatus >= 0);
-      m_iStatus = (m_iStatus != LOOK_AHEAD_LEN);
-      m_iDataType = U32_AT(m_bBuff[0]);
-      m_pCurrentData = m_cPgrmDescriptor.GetDescriptor(m_iDataType & 0xFF);
-      m_iDataLen = U16_AT(m_bBuff[4]);
-      m_bPESStart = true;
 
-//      printf("Next data will be: %x (len = %d)\n", m_iDataType, m_iDataLen);
+    // Update the flag to show we're in the middle of a PES packet 
+    m_bInMiddleOfPES = true;
       
-      // Don't loop
-      break;
+    // if we're preparsing, we only ever deal with one packet at a time
+    if (bPreparsing) break;
     }
+
+  // if we're NOT still in the middle of a PES, clear the flag
+  if (!m_iDataLen) {
+    m_bInMiddleOfPES = false;
   }
   
-  return m_iStatus;
+  // if we get here, there isn't a problem
+  return NO_ERR;
 }
 
 
@@ -710,54 +609,66 @@
 //
 //------------------------------------------------------------------------------
 template <class Reader, class TsProvider>
-   int C_Ps2Ts<Reader, TsProvider>::SkipPES(C_TsPacket* pPacket, u8* iPosInTs)
+   int C_Ps2Ts<Reader, TsProvider>::SkipPES()
 {
-  // Release the TS packet, we won't need it
-  m_pTsProvider->ReleasePacket(pPacket);
+  int iRc;
 
-  int iRc = m_pReader->Seek(m_iDataLen, SEEK_CUR);
-  if(iRc)
-    m_iStatus = FILE_ERR;
-  else
-  {
-    // Look ahead for next iteration
-    m_iStatus = m_pReader->Read(m_bBuff, LOOK_AHEAD_LEN);
-    ASSERT(m_iStatus >= 0);
-    m_iStatus = (m_iStatus != LOOK_AHEAD_LEN);
-    m_iDataType = U32_AT(m_bBuff[0]);
-    m_pCurrentData = m_cPgrmDescriptor.GetDescriptor(m_iDataType & 0xFF);
-    m_iDataLen = U16_AT(m_bBuff[4]);
-    m_bPESStart = true;
-//    printf("Next data will be: %x (len = %d)\n", m_iDataType, m_iDataLen);
-
-    if(m_iStatus == NO_ERR)
-      m_iStatus = SKIPPED_DATA;
+  // read size of the PS packet
+  if (iRc = ReadDataLength(&m_iDataLen)) {
+    return iRc;
   }
   
-  return m_iStatus;
+  // skip it
+  return SkipData(m_iDataLen);
 }
 
 
-//------------------------------------------------------------------------------
-//
-//------------------------------------------------------------------------------
 template <class Reader, class TsProvider>
-   int C_Ps2Ts<Reader, TsProvider>::ParsePgrmMap(C_TsPacket* pPacket, u8* iPosInTs)
+  int C_Ps2Ts<Reader, TsProvider>::ReadData(u8* buf, int length) 
 {
-//  printf("Parsing pgrm map PES: WARNING, code never tested\n");
+  int iRc;
 
-//  printf("Pgrm map length: %d\n", m_iDataLen);
+  // try to read the data
+  iRc = m_pReader->Read(buf, length);
+  if (iRc < 0) return iRc;
 
-  ASSERT(false);
+  // check for lack of data
+  if (m_pReader->EndOfStream()) return END_OF_STREAM;
 
-  while(m_iDataLen >= sizeof(m_bBuff))
-  {
-/*    int iRc = */m_pReader->Read(m_bBuff, sizeof(m_bBuff));
-    m_iDataLen -= sizeof(m_bBuff);
-  }
+  // success
+  return 0;
+}
 
-  //  push PAT et PMT dans la liste des pendingpackets 
-  return NO_ERR;
+template <class Reader, class TsProvider>
+  int C_Ps2Ts<Reader, TsProvider>::SkipData(int length) 
+{
+  int iRc;
+   
+  // try to skip
+  iRc = m_pReader->Seek(length, FILE_SEEK_CURRENT);
+  if (iRc < 0) return iRc;
+   
+  // check for lack of data
+  if (m_pReader->EndOfStream()) return END_OF_STREAM;
+
+  // success
+  return 0;
 }
 
+template <class Reader, class TsProvider>
+  int C_Ps2Ts<Reader, TsProvider>::ReadDataLength(u16* lengthDest)
+{
+  int iRc;
+  u8 buf[2];
 
+  // try to skip  
+  iRc = m_pReader->Read(buf, 2);
+  if (iRc < 0) return iRc;
+  *lengthDest = U16_AT(buf);
+   
+  // check for lack of data
+  if (m_pReader->EndOfStream()) return END_OF_STREAM;
+
+  // success
+  return 0;
+}
--- vls.orig/src/mpeg/ps2ts.h	2002-05-14 23:10:08.000000000 +0100
+++ vls/src/mpeg/ps2ts.h	2003-08-11 15:38:04.000000000 +0100
@@ -37,29 +37,31 @@
 {
  public:
   C_Ps2Ts(Reader* pReader, TsProvider* pTsProvider, unsigned int iMaxBufferedTs,
-          unsigned int iMpegVersion);
+          unsigned int iMpegVersion, bool bLooping);
   ~C_Ps2Ts();
   
   // Find the first pack header in the stream
   int Synch();
 
   // 
-  C_TsPacket* GetPacket();
+  C_TsPacket* GetPacket(bool bPreparsing = false);
+   
   // To check whenever GetPacket returns NULL
   int GetStatus() { return m_iStatus; };
 
   C_ProgramDescriptor* GetPgrmDescriptor() { return &m_cPgrmDescriptor; };
 
  protected:
-  int FetchPackets();
-
-  int ParsePackHeader(C_TsPacket* pPacket, u8* iPosInTs);
-  int ParseSystemHeader(C_TsPacket* pPacket, u8* iPosInTs);
-  int ParsePrivate1Header(C_TsPacket* pPacket, u8* iPosInTs);
-  int ParsePES(C_TsPacket* pPacket, u8* iPosInTs);
-  int ParsePgrmMap(C_TsPacket* pPacket, u8* iPosInTs);
+  int FetchPackets(bool bPreparsing);
 
-  int SkipPES(C_TsPacket* pPacket, u8* iPosInTs);
+  int ParsePackHeader(bool bPreparsing);
+  int ParseSystemHeader();
+  int ParsePES(bool bPreparsing);
+  int SkipPES();
+
+  int ReadData(u8* buf, int length);
+  int SkipData(int length);
+  int ReadDataLength(u16* lengthDest);
 
  private:
   Reader* m_pReader;
@@ -72,8 +74,10 @@
   // Parser state
   u32 m_iDataType;
   u16 m_iDataLen;
+  u8 m_iPrivateId;
   bool m_bPESStart;
   byte m_bBuff[TS_PACKET_LEN];
+  bool m_bJustSynched;
 
   // Output state
   u32 m_iTSCounter;  
@@ -81,7 +85,6 @@
   bool m_bGenPat;
   bool m_bGenPmt;
   bool m_bResetPSI;
-  C_ElementDescriptor* m_pCurrentData;
   C_ProgramDescriptor m_cPgrmDescriptor;
 
   C_DvbPsiPat m_cPat;
@@ -93,9 +96,16 @@
 
   // Global status
   int m_iStatus;
+  u64 m_iNextPCR;
+  bool m_bNeedToSendPCR;
+  bool m_bLooping;
+   
+  u8 m_bCache[512];
+  int m_iCacheLen;
+  bool m_bInMiddleOfPES;
 
   // discontinuity management
-  u64 m_iPrevSCR;
+  u64 m_iPrevPCR;
   bool m_bDiscontinuity;
 };