/* libdvb_parts.c

 this file contains parts of libdvb-0.5.5.1/transform.{c,h}
 http://freshmeat.net/projects/libdvb/

*/

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include "device.h"
#include "libdvb_parts.h"

#define TS_SIZE          188
#define AUDIO_STREAM_S   0xC0
#define AUDIO_STREAM_E   0xDF
#define VIDEO_STREAM_S   0xE0
#define VIDEO_STREAM_E   0xEF
#define PROG_STREAM_MAP  0xBC
#define PROG_STREAM_DIR  0xFF
#define ECM_STREAM       0xF0
#define EMM_STREAM       0xF1
#define PADDING_STREAM   0xBE
#define DSM_CC_STREAM    0xF2
#define PRIVATE_STREAM1  0xBD
#define PRIVATE_STREAM2  0xBF
#define PTS_ONLY         0x80
#define PTS_DTS          0xC0
#define PTS_DTS_FLAGS    0xC0
#define ISO13522_STREAM  0xF3

static uint8_t tspid0[TS_SIZE] =
  { 
  0x47, 0x40, 0x00, 0x10, 0x00, 0x00, 0xb0, 0x11, 
  0x00, 0x00, 0xcb, 0x00, 0x00, 0x00, 0x00, 0xe0, 
  0x10, 0x00, 0x01, 0xe4, 0x00, 0x2a, 0xd6, 0x1a, 
  0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff
  };

static	uint8_t tspid1[TS_SIZE] = 
  { 
  0x47, 0x44, 0x00, 0x10, 0x00, 0x02, 0xb0, 0x1c,
  0x00, 0x01, 0xcb, 0x00, 0x00, 0xe0, 0xa0, 0xf0, 
  0x05, 0x48, 0x03, 0x01, 0x00, 0x00, 0x02, 0xe0,
  0xa0, 0xf0, 0x00, 0x03, 0xe0, 0x50, 0xf0, 0x00, 
  0xae, 0xea, 0x4e, 0x48, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff
  };

void cPTV_Device::init_p2p(p2p *p, int repack)
  {
  p->found = p->cid = p->mpeg = p->done = 0;
  memset(p->buf,0,MMAX_PLENGTH);
  p->fd1 = -1;
  if ( repack < MAX_PLENGTH && repack > 265 )
    {
    p->repack = repack-6;
    p->bigend_repack = (uint16_t)htons((short)((repack-6) & 0xFFFF));
    }
  else
    fprintf(stderr, "Repack size %d is out of range\n",repack);
  }

int cPTV_Device::write_ts_header(uint16_t pid, uint8_t *counter, int pes_start, uint8_t *buf, uint8_t length)
  {
  int i;
  int c = 0;
  int fill;
  uint8_t tshead[4] = {0x47, 0x00, 0x00, 0x10}; 
  fill = TS_SIZE-4-length;
  if (pes_start)
    tshead[1] = 0x40;
  if (fill)
    tshead[3] = 0x30;
  tshead[1] |= (uint8_t)((pid & 0x1F00) >> 8);
  tshead[2] |= (uint8_t)(pid & 0x00FF);
  tshead[3] |= ((*counter)++ & 0x0F);
  memcpy(buf,tshead,4);
  c+=4;
  if (fill)
    {
    buf[4] = fill-1;
    c++;
    if (fill >1)
      {
      buf[5] = 0x00;
      c++;
      }
    for ( i = 6; i < fill+4; i++)
      {
      buf[i] = 0xFF;
      c++;
      }
    }
  return c;
  }

void cPTV_Device::pes_in_ts(p2p *p)
  {
  int l, pes_start;
  uint8_t obuf[TS_SIZE];
  long int c = 0;
  int length = p->plength+6;
  uint16_t pid;
  uint8_t *counter;
  pes_start = 1;
  switch ( p->cid )
    {
    case AUDIO_STREAM_S ... AUDIO_STREAM_E:
      pid = p->pida;
      counter = &p->acounter;
      break;
    case VIDEO_STREAM_S ... VIDEO_STREAM_E:
      pid = p->pidv;
      counter = &p->acounter;
      tspid0[3] |= (p->count0++) & 0x0F;
      tspid1[3] |= (p->count1++) & 0x0F;
      tspid1[24] = p->pidv;
      tspid1[23] |= (p->pidv >> 8) & 0x3F;
      tspid1[29] = p->pida;
      tspid1[28] |= (p->pida >> 8) & 0x3F;
      WriteBuffer(tspid0,TS_SIZE,p);
      WriteBuffer(tspid1,TS_SIZE,p);
      break;
    default:
      return;
    }
  while ( c < length )
    {
    memset(obuf,0,TS_SIZE);
    if (length - c >= TS_SIZE-4)
      {
      l = write_ts_header(pid, counter, pes_start, obuf, TS_SIZE-4);
      memcpy(obuf+l, p->buf+c, TS_SIZE-l);
      c += TS_SIZE-l;
      }
    else
      {
      l = write_ts_header(pid, counter, pes_start, obuf, length-c);
      memcpy(obuf+l, p->buf+c, TS_SIZE-l);
      c = length;
      }
    WriteBuffer(obuf,TS_SIZE,p);
    pes_start = 0;
    }
  }

void cPTV_Device::get_pes (uint8_t *buf, int count, p2p *p)
  {
  int l;
  unsigned short *pl;
  int c=0;
  uint8_t headr[3] = { 0x00, 0x00, 0x01} ;
  while (c < count && (p->mpeg == 0 || \
        (p->mpeg == 1 && p->found < 7) || \
        (p->mpeg == 2 && p->found < 9)) && \
        (p->found < 5 || !p->done))
    {
    switch ( p->found )
      {
      case 0:
      case 1:   if (buf[c] == 0x00)
                  p->found++;
		else
                  p->found = 0;
		c++;
		break;
      case 2:   if (buf[c] == 0x01)
                  p->found++;
		else
                  if (buf[c] == 0)
                    p->found = 2;
                  else
                    p->found = 0;
			c++;
		break;
      case 3:   p->cid = 0;
		switch (buf[c])
                  {
		  case PROG_STREAM_MAP:
		  case PRIVATE_STREAM2:
		  case PROG_STREAM_DIR:
		  case ECM_STREAM     :
		  case EMM_STREAM     :
		  case PADDING_STREAM :
		  case DSM_CC_STREAM  :
		  case ISO13522_STREAM: p->done = 1;
		  case PRIVATE_STREAM1:
		  case VIDEO_STREAM_S ... VIDEO_STREAM_E:
		  case AUDIO_STREAM_S ... AUDIO_STREAM_E:
		  		        p->found++;
				        p->cid = buf[c];
				        c++;
				        break;
		  default:              p->found = 0;
				        break;
		  }
		  break;
			

      case 4:   if (count-c > 1)
                  {
		  pl = (unsigned short *) (buf+c);
		  p->plength =  ntohs(*pl);
		  p->plen[0] = buf[c];
		  c++;
		  p->plen[1] = buf[c];
		  c++;
		  p->found+=2;
		  }
                else
                  {
                  p->plen[0] = buf[c];
		  p->found++;
		  return;
		  }
		break;
      case 5:   p->plen[1] = buf[c];
		c++;
		pl = (unsigned short *) p->plen;
		p->plength = ntohs(*pl);
		p->found++;
		break;
      case 6:   if (!p->done)
                  {
		  p->flag1 = buf[c];
		  c++;
		  p->found++;
		  if ( (p->flag1 & 0xC0) == 0x80 )
                    p->mpeg = 2;
		  else
                    {
		    p->hlength = 0;
		    p->which = 0;
		    p->mpeg = 1;
		    p->flag2 = 0;
	            }
		  }
		break;
      case 7:   if ( !p->done && p->mpeg == 2)
                  {
		  p->flag2 = buf[c];
		  c++;
		  p->found++;
		  }	
		break;
      case 8:   if ( !p->done && p->mpeg == 2)
                  {
		  p->hlength = buf[c];
		  c++;
		  p->found++;
		  }
		break;
      default:  break;
      }
    }
  if (!p->plength)
    p->plength = MMAX_PLENGTH-6;
  if (p->done || ((p->mpeg == 2 && p->found >= 9) || \
     (p->mpeg == 1 && p->found >= 7)))
    {
    switch (p->cid)
      {
      case AUDIO_STREAM_S ... AUDIO_STREAM_E:			
      case VIDEO_STREAM_S ... VIDEO_STREAM_E:
      case PRIVATE_STREAM1:  memcpy(p->buf, headr, 3);
			     p->buf[3] = p->cid;
			     memcpy(p->buf+4,p->plen,2);
                             if (p->mpeg == 2 && p->found == 9)
                               {
			       p->buf[6] = p->flag1;
			       p->buf[7] = p->flag2;
			       p->buf[8] = p->hlength;
			       }
                             if (p->mpeg == 1 && p->found == 7)
                               {
			       p->buf[6] = p->flag1;
			       }
                             if (p->mpeg == 2 && (p->flag2 & PTS_ONLY) && \
                                 p->found < 14)
                               {
			       while (c < count && p->found < 14)
                                 {
                                 p->pts[p->found-9] = buf[c];
				 p->buf[p->found] = buf[c];
				 c++;
				 p->found++;
				 }
			       if (c == count)
                                 return;
			       }
                             if (p->mpeg == 1 && p->which < 2000)
                               {
                               if (p->found == 7)
                                 {
				 p->check = p->flag1;
				 p->hlength = 1;
				 }
			       while (!p->which && c < count && 
				       p->check == 0xFF)
                                 {
				 p->check = buf[c];
				 p->buf[p->found] = buf[c];
				 c++;
				 p->found++;
				 p->hlength++;
				 }
			       if ( c == count)
                                 return;
			       if ( (p->check & 0xC0) == 0x40 && !p->which)
                                 {
				 p->check = buf[c];
				 p->buf[p->found] = buf[c];
				 c++;
				 p->found++;
				 p->hlength++;
                                 p->which = 1;
				 if ( c == count)
                                   return;
				 p->check = buf[c];
				 p->buf[p->found] = buf[c];
				 c++;
				 p->found++;
				 p->hlength++;
				 p->which = 2;
				 if ( c == count)
                                   return;
				 }
			       if (p->which == 1)
                                 {
				 p->check = buf[c];
				 p->buf[p->found] = buf[c];
				 c++;
				 p->found++;
				 p->hlength++;
				 p->which = 2;
				 if ( c == count)
                                   return;
				 }
			       if ( (p->check & 0x30) && p->check != 0xFF)
                                 {
				 p->flag2 = (p->check & 0xF0) << 2;
				 p->pts[0] = p->check;
				 p->which = 3;
				 } 
                                if ( c == count)
                                  return;
				if (p->which > 2)
                                  {
				  if ((p->flag2 & PTS_DTS_FLAGS) == PTS_ONLY)
                                    {
				    while (c < count && p->which < 7)
                                      {
				      p->pts[p->which-2] = buf[c];
				      p->buf[p->found] = buf[c];
				      c++;
				      p->found++;
				      p->which++;
				      p->hlength++;
				      }
				    if ( c == count)
                                      return;
                                    }
                                  else
                                    if ((p->flag2 & PTS_DTS_FLAGS) == PTS_DTS)
                                      {
				      while (c < count && p->which < 12)
                                        {
					if (p->which < 7)
					  p->pts[p->which-2] = buf[c];
					p->buf[p->found] = buf[c];
					c++;
					p->found++;
					p->which++;
					p->hlength++;
					}
				      if ( c == count)
                                        return;
				      }
                                  p->which = 2000;
				  }
				}
                                while (c < count && p->found < p->plength+6)
                                  {
				  l = count -c;
				  if (l+p->found > p->plength+6)
				    l = p->plength+6-p->found;
				   memcpy(p->buf+p->found, buf+c, l);
				   p->found += l;
				   c += l;
                                   }	  	
			         if(p->found == p->plength+6)
				   //func(p);
                                   pes_in_ts(p);
			         break;
      }
    if ( p->done )
      {
      if( p->found + count - c < p->plength+6)
        {
	p->found += count-c;
	c = count;
	}
      else
        {
        c += p->plength+6 - p->found;
	p->found = p->plength+6;
	}
      }
    if (p->plength && p->found == p->plength+6)
      {
      p->found = 0;
      p->done = 0;
      p->plength = 0;
      memset(p->buf, 0, MAX_PLENGTH);
      if (c < count)
	get_pes(buf+c, count-c, p);
      }
    }
  return;
  }


