www.pudn.com > MPEG2systemsrc.rar > PESConsumer.C


/* Copyright (C) 1995, Tektronix Inc. All Rights Reserved.
 *
 *   Usage Restrictions
 *
 * License is granted to copy, to use, and to make and to use derivative
 * works for research and evaluation purposes only.
 *
 *   Disclaimer of Warranty
 *
 * These software programs are available to the user without any license
 * fee or royalty on an "as is" basis.  Tektronix Inc. disclaims any and
 * all warranties, whether express, implied, or statuary, including any
 * implied warranties or merchantability or of fitness for a particular
 * purpose.  In no event shall the copyright-holder be liable for any 
 * incidental, punitive, or consequential damages of any kind whatsoever
 * arising from the use of these programs.
 *
 * This disclaimer of warranty extends to the user of these programs and
 * user's customers, employees, agents, transferees, successors, and
 * assigns.
 *
 * The Tektronix Inc. does not represent or warrant that the programs
 * furnished hereunder are free of infringement of any third-party
 * patents.
*/

/* PES class implementation */

#include "Utilities.H"
#include "PESConsumer.H"
#include "IPortFromRam.H"
#include "Decoder.H"
#include "OPortToFile.H"

extern "C"
{
#include 
#include   
#include 
int write (int, char*, int);
}

PESConsumer::PESConsumer (Decoder* d, PES* p) : Consumer(d)
{
  pes = p;
  cstate = CSTART;
  pes_out_flag = TRUE;
  esport = NULL;
}

static int char2int (char c)  
{
  int result = 0;
  for (int i = 7; i >= 0; i--)
    {
      if ((c & (1 << i)) == 0) result = result * 2;
      else result = result * 2 + 1;
    }
  return result;
}

/* This function is called to read TP payloads. */
int PESConsumer::read_partial (int n)
{
  int i = 0;
  while (i < n)
    {
      switch (cstate)
	{
	case CSTART:
	  buf = (char*) malloc (512);
	  buf_index = 0;
	  cstate = PCHEAD1;
	  break;
	case PCHEAD1:
	  if (buf_index < 9)
	    {
	      buf[buf_index] = iport->read_byte();
    	      buf_index++; i++;
	    }
	  if (buf_index == 9)
	    {
	      header_length = char2int(buf[8]) + 9;
	      cstate = PCHEAD2;
	    }
	  break;
	case PCHEAD2:
	  if (buf_index < header_length)
	    {
	      buf[buf_index] = iport->read_byte();
	      buf_index++; i++;
	    }
          if (buf_index == header_length)
	    {
	      InputPort* save_port = iport;
	      iport = new IPortFromRam(buf, header_length);
	      read_pes_header();
	      delete iport;
	      iport = save_port;
	      if (pes_out_flag)
		write_elementary_stream_chunk(buf, header_length);
	      delete buf;  buf_index = 0;  // done with buf
	      cstate = PCPAYLOAD;
	    }
	  break;
	case PCPAYLOAD:
          // dispense with remaining n-i payload bytes
	  int plen = n - i;
	  char* pbuf = (char*) malloc(plen);
          for (int j = 0; j < plen; j++)
	    {
    	      pbuf[j] = iport->read_byte();
	      i++;
	    }
	  write_elementary_stream_chunk(pbuf, plen);
	  delete pbuf;
	  assert(i == n);
	  // stay in PCPAYLOAD state;
	  // state changes when payload_unit_start_indicator is seen
	  break;
	default:
	  sys_error("unknown PESConsumer state");
	}
    }
  return n;
}

void PESConsumer::flush ()
{
  esport->flush();
}

void PESConsumer::write_elementary_stream_chunk (char* pbuf, int plen)
{
  // prepare esport
  if (!esport)
    {
      // use decoder->get_ts()->pid or pes->stream_id?
      int cur_pid = decoder->get_ts()->pid; 
      // FIX - provide access function to current pid from decoder?
      char fileName[32];
      sprintf(fileName, "stream%d", cur_pid);
      esport = new OPortToFile(fileName, 0);
    }

  // output loop
  for (int i = 0; i < plen; i++)
    {
      esport->write_byte(pbuf[i]);
    }
}

void PESConsumer::read_pes_header ()
{
  int result = iport->read_pattern("000000000000000000000001");
  
  pes->stream_id = iport->read_uimsbf(8, "reading stream_id");
  
  pes->PES_packet_length = iport->read_uimsbf(16, "reading PES_packet_length");
  
  if ((pes->stream_id != id_private_stream_2) && (pes->stream_id != id_padding_stream))
    {
      // read "01"
      int result = iport->read_pattern("10");
      
      // read PES_scrambling_control; currently uses only "00" or "11"
      char scramble1 = iport->read_bit();
      char scramble2 = iport->read_bit();
      if ((scramble1 == ZERO) && (scramble2 == ZERO))
	pes->scrambling_code = '0';
      else
	pes->scrambling_code = 'S';	
      
      // read PES_priority
      char priority = iport->read_bit();
      if (priority == ONE)
	pes->priority_code = 'H';
      else
	pes->priority_code = 'L';
      
      // read data_alignment_indicator
      char align = iport->read_bit();
      if (align == ONE)
	pes->alignment_indicator_code = 'A';
      else
	pes->alignment_indicator_code = '0';
      
      // read copyright
      char copyright = iport->read_bit();
      if (copyright == ONE)
	pes->copyright_code = 'C';
      else
	pes->copyright_code = '0';
      
      // read copy_or_original
      char original = iport->read_bit();
      if (original == ONE)
	pes->original_code = 'C';
      else
	pes->original_code = '0';

      // read 8 flags
      char pts_flag = iport->read_bit();
      char dts_flag = iport->read_bit();      
      char escr_flag = iport->read_bit();
      char esrate_flag = iport->read_bit();
      char dsm_trick_flag = iport->read_bit();
      char additional_copy_flag = iport->read_bit();
      char crc_flag = iport->read_bit();      
      char extension_flag = iport->read_bit();      
      
      // read PES_header_data_length 
      int PES_header_length =
	iport->read_uimsbf(8, "reading PES_header_length");

      // read optional fields 
      // read pts,dts
      if ((pts_flag == ONE) && (dts_flag == ZERO))
	{
	  iport->read_pattern("0010");
	  pes->PTS = iport->read_timestamp90();
	  pes->DTS = NULL;
          decoder->get_manager()->Trigger(PTSDTSEvent, pes);
	}
      else if ((pts_flag == ONE) && (dts_flag == ONE))
	{
	  iport->read_pattern("0011");
	  pes->PTS = iport->read_timestamp90();
	  iport->read_pattern("0001");
	  pes->DTS = iport->read_timestamp90();
          decoder->get_manager()->Trigger(PTSDTSEvent, pes);
	}
      else
	{
	  pes->PTS = NULL;
	  pes->DTS = NULL;
	}

      // read escr
      if (escr_flag == ONE)
	{
	  iport->read_reserved_bits(2);
	  pes->ESCR = iport->read_timestamp27_pes_format();
	}

      // read es_rate
      if (esrate_flag == ONE)
	{
	  iport->read_markerbit();
	  pes->ES_rate = iport->read_uimsbf(22, "reading ES_rate");
	  iport->read_markerbit();
	}
	  
      // read DSM_trick_mode
      if (dsm_trick_flag == ONE)
	{
	  read_trick_mode();
	}

      // read additional_copy_info
      if (additional_copy_flag == ONE)
	{
	  iport->read_markerbit();
	  pes->additional_copy_info = NULL;
	  char c;
	  for (int i = 0; i < 7; i++)
	    {
	      c = iport->read_bit();
	      if (c == '1')
		{
		  pes->additional_copy_info |= (1 << i);
		}
	    }
	}

      // read previous_PES_packet_CRC
      if (crc_flag == ONE)
	{
	  pes->previous_CRC = iport->read_uimsbf(16, "reading previous_CRC");
        }

      // read PES extension fields
      if (extension_flag == ONE)
	{
	  read_extension();
	}

      // skip the stuffing bytes.  N.B. the "9" added to PES_header_length
      pes->number_stuffing_bytes =
	PES_header_length + 9 - pes->get_header_data_length();
      for (int i = 0; i < pes->number_stuffing_bytes; i++)
	iport->read_byte();
    }

  // call PESParsed callback
  decoder->get_manager()->Trigger(PESHeaderParsed, pes);
}

void PESConsumer::read_trick_mode ()
{
  char intra;
  pes->trick_mode = new TrickMode();
  int code = iport->read_uimsbf(3, "reading trick_control_code");
  switch (code)
    {
    case 0:
      pes->trick_mode->trick_mode_control_code = 'F';
      read_field_id();
      intra = iport->read_bit();
      if (intra == ONE)
	{
	  pes->trick_mode->intra_slice_refresh_code = 'I';
	}
      else
	{
	  pes->trick_mode->intra_slice_refresh_code = '0';
	}
      read_frequency_truncation();
      break;
    case 1:
      pes->trick_mode->trick_mode_control_code = 'S';
      pes->trick_mode->field_rep_control =
	iport->read_uimsbf(5, "reading field rep_control");
      break;
    case 2:
      pes->trick_mode->trick_mode_control_code = 'Z';
      read_field_id();
      iport->read_reserved_bits(3);
      break;
    case 3:
      pes->trick_mode->trick_mode_control_code = 'R';
      read_field_id();
      intra = iport->read_bit();
      if (intra == ONE)
	{
	  pes->trick_mode->intra_slice_refresh_code = 'I';
	}
      else
	{
	  pes->trick_mode->intra_slice_refresh_code = '0';
	}
      read_frequency_truncation();
      break;
    default:
      // It must be one of the reserved modes
      iport->read_reserved_bits(5);
      break;
    }
}

void PESConsumer::read_field_id ()
{
  if (pes->trick_mode == NULL) return;
  int code = iport->read_uimsbf(2, "reading field_id");
  switch (code)
    {
    case 0:
      pes->trick_mode->field_id_code = '1';
      break;
    case 1:
      pes->trick_mode->field_id_code = '2';
      break;
    case 2:
      pes->trick_mode->field_id_code = 'B';
      break;
    }
}

void PESConsumer::read_frequency_truncation ()
{
  if (pes->trick_mode == NULL) return;
  int code = iport->read_uimsbf(2, "reading frequency_truncation");
  switch (code)
    {
    case 0:
      pes->trick_mode->frequency_truncation_code = 'D';
      break;
    case 1:
      pes->trick_mode->frequency_truncation_code = '3';
      break;
    case 2:
      pes->trick_mode->frequency_truncation_code = '6';
      break;
    case 3:
      pes->trick_mode->frequency_truncation_code = 'A';      
      break;
    }
}

void PESConsumer::read_extension ()
{
  pes->extension = new PESExtension();

  // read flags
  char PES_private_data_flag = iport->read_bit();
  char pack_header_field_flag = iport->read_bit();
  char pp_seq_counter_flag = iport->read_bit();
  char P_STD_buffer_flag = iport->read_bit();
  iport->read_reserved_bits(3);
  char PES_extension_field_flag = iport->read_bit();

  if (PES_private_data_flag == ONE)
    {
      pes->extension->PES_private_data = (char*) malloc(16);
      for (int i = 0; i < 16; i++)
	pes->extension->PES_private_data[i] = iport->read_byte();
    }

  if (pack_header_field_flag == ONE)
    {
      pes->extension->pack_field_length =
	iport->read_uimsbf(8, "reading pack_field_length");
      for (int j = 0; j < pes->extension->pack_field_length; j++)
	pes->extension->pack_header[j] = iport->read_byte();
    }

  if (pp_seq_counter_flag == ONE)
    {
      pes->extension->pp_seq_counter_code = 'P';
      iport->read_markerbit();
      pes->extension->pp_seq_counter =
	iport->read_uimsbf(7, "reading pp_seq_counter");
      iport->read_markerbit();
      pes->extension->original_stuff_length =
	iport->read_uimsbf(7, "reading orig. stuff len");
    }
  else
    pes->extension->pp_seq_counter_code = '0';

  if (P_STD_buffer_flag == ONE)
    {
      pes->extension->P_STD_buffer_code = 'B';
      iport->read_pattern("01");
      pes->extension->P_STD_buffer_scale = iport->read_bit();
      pes->extension->P_STD_buffer_size =
	iport->read_uimsbf(13, "reading P_STD_buf_size");
    }
  else
    pes->extension->P_STD_buffer_code = '0';

  if (PES_extension_field_flag == ONE)
    {
      iport->read_markerbit();
      pes->extension->PES_extension_field_length =
	iport->read_uimsbf(7, "reading PES_ext_field_length");
      for (int k = 0; k < pes->extension->PES_extension_field_length; k++)
	(void) iport->read_reserved_bits(8);
    }
}