// compiles successfully using a c++ version 2.7.2.1 compiler or later
// typical environment SOLARIS 2.6 ( = SunOS 5.6 )
// To Compile: c++ MPEG2RTP.c -o MPEG2RTP
//
// Note: the function Send_RTP_Packet() is not implemented here.
//	 However, the code here indicates where it should be called.
//	 The Send_RTP_Packet() functions takes two arguments, the
//	 name of the buffer (provided), and the number of bytes that
//	 should be sent (also provided). So all is needed is to connect
//	 to a socket and send the contents of the buffer.
//
// For more information about this code email: jhazboun@lucent.com
//


#include "MPEG2RTP.h"

extern void display_rtp_hdr();
extern void display_mpeg_hdr();
extern void validate_file();
extern unsigned int read_picture_type();
extern unsigned int read_FBV();
extern unsigned int read_BFC();
extern unsigned int read_FFV();
extern unsigned int read_FFC();
extern unsigned int extract_temporal_reference();
extern unsigned int FIND_next_start_code();
extern void update_header();

unsigned char buf[MAX_RTP_PKT_LENGTH + 4]; //input buffer
int indx = HEADER_LENGTH; //an index to the buffer
enum reading_status state = SEQUENCE_HEADER;
int max_to_send = MAX_RTP_PKT_LENGTH - 1; //index to where to send in buf
unsigned int PACKET_DONE = 0; // when true, a packet is sent

void main(int argc, char *argv[])
{

int mpfd,j,i,v,bytes;
unsigned int next_start_code;

if (argc < 2)
	{
	printf("\nUSAGE: %s mpegfile\nExiting..\n\n",argv[0]);
	exit(0);
	}

mpfd = open(argv[1], O_RDONLY);            

if (mpfd < 0 ) 
	{
	printf("\nERROR: could not open input file %s\n\n",argv[1]);
	exit(0);
	}

bzero((void *)&rtp_hdr,8); //zero-out the rtp fixed hdr
bzero((void *)&mpeg_hdr,4); //zero-out the video specific hdr
bzero((void *)buf,MAX_RTP_PKT_LENGTH + 4);

//read the first packet from the mpeg file
//always read 4 extra bytes in (in case there's a startcode there
//but dont send  more than MAX_RTP_PKT_LENGTH in one packet
read(mpfd,&(buf[HEADER_LENGTH]),MAX_RTP_PKT_LENGTH-HEADER_LENGTH+4); 

validate_file();
 
do
{    

/* initialization of the two RTP headers */
indx = HEADER_LENGTH;
max_to_send = MAX_RTP_PKT_LENGTH;
rtp_hdr.seq++;
rtp_hdr.pt = MPA;
rtp_hdr.version = 2;
rtp_hdr.m =0;
mpeg_hdr.S=0;
mpeg_hdr.E=0;
update_header();
PACKET_DONE = 0;

while (!PACKET_DONE) /* start processing one packet */
{

next_start_code = FIND_next_start_code();

switch(next_start_code)
{
	case SEQUENCE_HEADER_CODE:
		mpeg_hdr.S=1;
		update_header();
		state= SEQUENCE_HEADER;
		break;

	case PICTURE_START_CODE:
		mpeg_hdr.TR = extract_temporal_reference();
		mpeg_hdr.P = read_picture_type();
		//now read the motion vectors information
		if( (mpeg_hdr.P==2) || (mpeg_hdr.P==3))
			{ //if B- or P-type picture, need forward mv
			mpeg_hdr.FFV = read_FFV(); 
			mpeg_hdr.FFC = read_FFC();
			}
		if( mpeg_hdr.P==3)
			{ // if B-type pictue, need backward mv
			mpeg_hdr.FBV = read_FBV();
			mpeg_hdr.BFC = read_BFC();
			}
		update_header();
		state = PICTURE;
		break;
	case 0:
		break;
}

if ((next_start_code >0x100) && (next_start_code<0x1b0) )
   {
   if (indx < 24)
	{ 
	// if it's a slice start code and follows GOP startcode, pic startcode,
	// or sequence header code
	mpeg_hdr.B = 1;
	update_header();
	}
   state = SLICE; 
   } 

if ((state == SLICE) && (PACKET_DONE) )
    { //if the last byte of the payload is a slice end
    if (!((int)buf[indx+1]) && !((int)buf[indx+2]) && ((int)buf[indx+3]==1))
	{
	mpeg_hdr.E = 1;
	update_header();
	state = SLICE_END;
	}
    }

if ( ((state==SLICE) || (state==SLICE_END)) && (next_start_code > 0x1af))
	{
	rtp_hdr.m = 1; //set the M bit in the RTP fixed header
	update_header();
	}

} //end while: processing one packet


//now send (max_to_send+1) bytes of the buffer "buf"
//Send_RTP_Packet(buf,max_to_send+1); 

//Now copy the last bits in the buffer so they can be sent in the next pkt.
v=12;
for (j=0;j<(MAX_RTP_PKT_LENGTH-1-max_to_send+4+1);j++)
   buf[v++] = buf[j+max_to_send+1];
                               
}
while( (bytes=read(mpfd, (char *)&(buf[HEADER_LENGTH+ \
	MAX_RTP_PKT_LENGTH-1-max_to_send+4]), \
	(max_to_send - HEADER_LENGTH + 1))) \
	>= (MAX_RTP_PKT_LENGTH - HEADER_LENGTH -4 )); //while !EOF


if (bytes > 0) /* then process the last packet */
	{
	update_header();
	rtp_hdr.seq++;
	//now send the very last packet (# of bytes = (v-1+bytes) )
	//Send_RTP_Packet(buf,(v-1)+bytes);
	}

close(mpfd);
}

//==================================================================

unsigned int FIND_next_start_code() //NOTE: all start codes ARE byte-aligned
{
unsigned int byte0=0,byte1=0,byte2=0,byte3=0,startcode=0;

//while not startcode and have not exceeded max packet length
while ( (indx < MAX_RTP_PKT_LENGTH) && (startcode == 0) )
  { 
   if (!((int)buf[indx+0] + (int)buf[indx+1]) && (((int)buf[indx+2])==1) )
	{
	//printf("FOUND startcode %d\n",indx);
	byte0=(int)buf[indx+0];
	byte1=(int)buf[indx+1];
	byte2=(int)buf[indx+2];
	byte3=(int)buf[indx+3];
	startcode=(byte0 << 24) + (byte1 << 16) + (byte2 << 8) + byte3;
	indx=indx+4;
	}
   else
   	{indx++;}
  }
	
if ((startcode != 0) && (indx >= (MAX_RTP_PKT_LENGTH)))
	{
	//found startcode but it's at the end of payload,
	//therefore back up so the startcode will be sent in the next pkt
	indx=indx-5;
	max_to_send = indx;
	PACKET_DONE = 1;
	startcode =0;
	}
	
else if ((startcode == 0) && (indx >= (MAX_RTP_PKT_LENGTH)))
	{
	//startcode not found, and reached the max payload size
	indx = indx - 1;
	PACKET_DONE = 1;
	max_to_send = indx;
	}

return(startcode);

}

//========================================================

unsigned int extract_temporal_reference() // 10 bits
{
unsigned int low2bits=0,TR=0; // TR = temporal reference;

TR = (unsigned int) (buf[indx]);
TR << 2;
low2bits = (unsigned int) (buf[indx+1]);
TR |= (low2bits >> 6);
return(TR);
}

//========================================================
  
unsigned int read_picture_type()
{
unsigned int pictype=0;

pictype = (unsigned int) buf[indx+1];
pictype = (pictype >> 3) & (0x7);
return (pictype);
}

//=======================================================
unsigned int read_FFV() // 1 bit
{
return( (int) ((buf[indx+1] & (0x4)) >> 2));
}
//=======================================================
unsigned int read_FFC() // 3 bits
{
unsigned int FFC=0,lowbit=0;
FFC = (int) (buf[indx+1] & (0x3));
FFC << 1;
lowbit = (int) ((buf[indx+2]) & (0x80));
FFC = FFC | (lowbit >> 7 );
return(FFC);
}

//=======================================================
unsigned int read_FBV() // 1 bit
{       
return( (int) ((buf[indx+3] & (0x40))>>6) );
}

//=======================================================
unsigned int read_BFC() // 3 bits
{                
return( (int) ( (buf[indx+3] & (0x56) ) >> 3 ) );
}
//=======================================================

void update_header()
{
strncpy((char *)&(buf[0]),(const char *)&rtp_hdr,8);
strncpy((char *)&(buf[8]),(const char *)&mpeg_hdr,4);
}

//=======================================================
void validate_file()
{
/* to validate the file, ensure the existance of a startcode */
int j=0,valid=0;

while ((j++<MAX_RTP_PKT_LENGTH) && (!valid))
{
if (!((int)buf[j+0] + (int)buf[j+1]) && (((int)buf[j+2])==1))
        valid=1;
}
if (!valid)
        {
        printf("\nERROR: start code not found. \
                \nInput file must be a valid MPEG I file.\n");
        exit(0);
        }           
}
//=======================================================
/* THIS FUNCTION is not used. It is implemented here for
   purposes of debugging (in case future work is done in this project) */

void display_rtp_hdr()
{
cout << "\n\nRTP FIXED HEADER INFO: ";
cout << "\nversion\t\t=" << rtp_hdr.version
     << "\npadding flag\t=" <<  rtp_hdr.p
     << "\nhdr ext flag \t=" <<  rtp_hdr.x
     << "\nM bit\t\t=" <<  rtp_hdr.m
     << "\npayload type\t=" <<  rtp_hdr.pt
     << "\nsequence # \t=" <<  rtp_hdr.seq
     << "\nPTS \t=" <<  rtp_hdr.pts;
}

void display_mpeg_hdr()
{
cout << "\n\nMPEG_VID_SPECIFIC_HDR INFO: ";
cout << "\nExtension Flag \t\t=" << mpeg_hdr.T
     << "\nTemporal Reference \t=" << mpeg_hdr.TR
     << "\nAvtive N bit \t\t=" << mpeg_hdr.AN
     << "\nN bit\t\t\t=" << mpeg_hdr.N
     << "\nsequence hdr present\t=" << mpeg_hdr.S
     << "\nslice start\t\t=" << mpeg_hdr.B
     << "\nslice end\t\t=" << mpeg_hdr.E
     << "\npicture type\t\t=" << mpeg_hdr.P
     << "\nfull_pel_backward_vector=" << mpeg_hdr.FBV
     << "\nbackward_f_code\t\t=" << mpeg_hdr.BFC
     << "\nfull_pel_forward_vector =" << mpeg_hdr.FFV
     << "\nforward_f_code\t\t=" << mpeg_hdr.FFC;
}
//========================================================
