/* 
 * pes2ts.c: PES2TS remux
 *
 * See the README file for copyright information and how to reach the author.
 *
 */
#include <vdr/tools.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>

#include "pes2ts.h"

cPES2TSRemux::cPES2TSRemux(int VPid, int APid):
    cThread("[ffnetdev] PES2TS remux"),
    m_OutputBuffer(new cRingBufferLinear(OUTPUTBUFSIZE, TS_SIZE * 2)),
    m_InputBuffer(new cRingBufferLinear(INPUTBUFSIZE, IPACKS)),
    m_Active(false)
{
  vpid = VPid;
  apid = APid;
  m_InputBuffer->SetTimeouts(0, 1000);  // IMPORTANT to avoid busy wait in threads main loop and thus a high CPU load
  Start();
}

cPES2TSRemux::~cPES2TSRemux()
{
  m_Active = false;
  delete m_InputBuffer;
  delete m_OutputBuffer;

}

void cPES2TSRemux::Action(void)
{
  unsigned int i;
  uchar acc=0;    // continutiy counter for audio packets
  uchar vcc=0;      // continutiy counter for video packets
  uchar *cc;        // either cc=&vcc; or cc=&acc;
  unsigned short pid=0;
  unsigned int packetlen;
  uchar ts[188];
  uchar pes[IPACKS];

  unsigned int minNeededPacketlen = 10; // needed for read packet len: 6 Should be enought ... but makes no sense

  
  m_Active = true;
  while (m_Active) {
    int count=0;
    
    mutex.Lock();
    uchar *data = m_InputBuffer->Get(count);
    if (data==NULL) {
       mutex.Unlock();
       cCondWait::SleepMs(3);
       continue;
    }

    if ( count < minNeededPacketlen )	{
       fprintf (stderr, "[ffnetdev] Remuxer: not enought bytes for PacketLen-Analysis, have only: %d\n", count);
       mutex.Unlock();
       cCondWait::SleepMs(2);
      continue;
    }
    
    //DEBUG
    //fprintf(stderr, "Data ready to read: %d\n", count);
    //for (i=0; i<20; i++)
    //  fprintf(stderr, "%02X ", data[i]);
    // END DEBUG  
      
  
       // check for valid PES signature in PES header
    if ( (data[0]==0x00) && (data[1]==0x00) && (data[2]==0x01) ) {
          
          packetlen = ((data[4]<<8) | data[5]) + 6 ;
    
          if ( packetlen>IPACKS) {
              fprintf (stderr, "[ffnetdev] Remuxer: IPACKS changed? packet length was %d, maximum: %d\n"
                               "This should not happen! Please report!\n", packetlen, IPACKS);
          }

	  if ( count < packetlen) {
		fprintf (stderr, "[ffnetdev] Remuxer: not enought bytes for whole packet, have only: %d but LenShoud be %d\n", count, packetlen);
	       mutex.Unlock();
           cCondWait::SleepMs(1);
	       continue;
	  }


          // check for valid stream id type: is it video or audio or unknown?
          if ( (data[3]>=0xC0) && (data[3]<=0xDF) ) {
              pid=apid;
              cc=&acc;
          } 
          else {
              if ( (data[3]>=0xE0) && (data[3]<=0xEF) ) {
                  pid=vpid;
                  cc=&vcc;
              } 
              else {
                  fprintf(stderr, "[ffnetdev] Remuxer: Unknown stream id: neither video nor audio type.\n");
                  // throw away whole PES packet
                  m_InputBuffer->Del(packetlen);
                  mutex.Unlock();
                  continue;
              }
          }
          
          memcpy( pes, data, packetlen);
          // we are now finished with the PES packet, delete it from ring buffer
          m_InputBuffer->Del(packetlen);    
          mutex.Unlock();
    }
    else {
      // no valid PES signature was found, so delete this stuff from ring buffer
      // normally we should always receive a whole PES packet, since VDR always gives us a whole packet and not less or more
      // with each call in streamdevice.c (PlayVideo, PlayAudio)
      fprintf(stderr, "[ffnetdev] Remuxer: No valid PES signature found. This should not happen.\n");

      m_InputBuffer->Del(1); // Probably it is better to delete 1 byte only to get in sync again!?
      mutex.Unlock();
      continue;
    } 
    bool first = true;    
  //--------------------------------------divide PES packet into small TS packets-----------------------    
    for (i=0; i< packetlen/184; i++) {
      ts[0] = 0x47; //SYNC Byte
      if (first) ts[1] = 0x40;        // Set PUSI or
      else       ts[1] = 0x00;        // clear PUSI,  TODO: PID (high) is missing
      ts[2] = pid & 0xFF;             // PID (low)  
      ts[3] = 0x10 | ((*cc)&0x0F);    // No adaptation field, payload only, continuity counter 
      memcpy(ts + 4, pes + i * 184, 184);
      ++(*cc);
      m_OutputBuffer->Put(ts, 188);
      first = false;
    }
    uchar rest = packetlen % 184;
    if (rest>0) {
      ts[0] = 0x47; //SYNC Byte
      if (first) ts[1] = 0x40;        // Set PUSI or
      else       ts[1] = 0x00;        // clear PUSI,  TODO: PID (high) is missing
      ts[2] = pid & 0xFF;             // PID (low)  
      ts[3] = 0x30 | ((*cc)&0x0F);    // adaptation field, payload, continuity counter 
      ++(*cc);
      ts[4] = 183-rest;
      if (ts[4]>0) {
        ts[5] = 0x00;
        memset(ts + 6, 0xFF, ts[4] - 1);
      }
      memcpy(ts + 5 + ts[4], pes + i * 184, rest);
      m_OutputBuffer->Put(ts, 188);
      first = false;
    }
  }
  m_Active = false;
}


int cPES2TSRemux::Put(const uchar *Data, int Count)
{
  mutex.Lock();
  int result = m_InputBuffer->Put(Data, Count);
  mutex.Unlock();
  return ( result);
}




