/**********
This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 2.1 of the License, or (at your
option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)

This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
more details.

You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
**********/
/* 
 * Written by Engin Kurutepe.
 * kurutepe@nue.tu-berlin.de
 */

#include "H264RTPStreamFramer.h"

/*
** Translation Table as described in RFC1113
*/
static const char cb64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/*
** encodeblock
**
** encode 3 8-bit binary bytes as 4 '6-bit' characters
*/
void encodeblock( unsigned char in[3], unsigned char out[4], int len )
{
    out[0] = cb64[ in[0] >> 2 ];
    out[1] = cb64[ ((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4) ];
    out[2] = (unsigned char) (len > 1 ? cb64[ ((in[1] & 0x0f) << 2) | ((in[2] & 0xc0) >> 6) ] : '=');
    out[3] = (unsigned char) (len > 2 ? cb64[ in[2] & 0x3f ] : '=');
}

void encode( unsigned char* in, size_t in_size, 
			 char* out, size_t out_size, 
			 int linesize )
{
    unsigned char *p_in;
	char *p_out;
    int len, blocksout = 0;

	if( 4*in_size > 3*out_size )
	{
		printf("output buffer not large enough to encode all characters to base64! truncating input!\n");
		in_size = (3*out_size)<<2;
	}

	p_out = out;
    for( int index=0; index<in_size; index+=3)
	{
  //      len = 0;
        p_in = in + index;
		len = in_size - index;

		if( len > 3 ) len = 3;

        if( len ) {
            encodeblock( p_in, (unsigned char*)p_out, len );
			p_out += 4;           
            blocksout++;
        }
        //if( blocksout >= (linesize/4) || feof( infile ) ) {
        //    if( blocksout ) {
        //        fprintf( outfile, "\r\n" );
        //    }
        //    blocksout = 0;
        //}
    }

	*p_out = '\0';
}

H264RTPStreamFramer::H264RTPStreamFramer(UsageEnvironment& env, FramedSource* inputSource)
  : H264VideoStreamFramer(env, inputSource)
{
	packetBuffer = new unsigned char[24];
	lastPresentationTime.tv_sec = 0;
	lastPresentationTime.tv_usec = 0;
	fsps = NULL;
	fpps = NULL;
	profileLevelID = 0;
	isFirstFrame = true;
}


H264RTPStreamFramer::~H264RTPStreamFramer(void)
{
	delete [] fsps;
//	delete [] fpps;
	delete [] packetBuffer;
//	Medium::close(fInputSource);
}

Boolean H264RTPStreamFramer::currentNALUnitEndsAccessUnit()
{
	return true;
}

timeval* incrementTimeval(timeval* tv, unsigned int duration)
{
	tv->tv_usec+=duration;

	if( tv->tv_usec > 1000000)
	{
		tv->tv_sec++;
		tv->tv_usec-=1000000;
	}
	return tv;
}

//size_t H264RTPStreamFramer::getNALU()
//{
//		
//}

#define DELTA_EPOCH_IN_USEC  11644473600000000Ui64

int gettimeofday(struct timeval *tv)
{
  FILETIME        ft;
  LARGE_INTEGER   li;
  __int64         t;
  static int      tzflag;

  if (tv)
  {
      GetSystemTimeAsFileTime(&ft);
      li.LowPart  = ft.dwLowDateTime;
      li.HighPart = ft.dwHighDateTime;
      t  = li.QuadPart;       /* In 100-nanosecond intervals */
      t -= DELTA_EPOCH_IN_USEC;     /* Offset to the Epoch time */
      t /= 10;                /* In microseconds */
      tv->tv_sec  = (long)(t / 1000000);
      tv->tv_usec = (long)(t % 1000000);
  }

  return 0;
}


void H264RTPStreamFramer::doGetNextFrame()
{
	if ( ! isCurrentlyAwaitingData() ) return;
	char first_byte = packetBuffer[0];
	fInputSource->getNextFrame(packetBuffer, 20, // 4 + 4 + 12 
							   afterGettingHeader,
							   this,
							   handleClosure,
							   this);

}

void H264RTPStreamFramer::afterGettingHeader( void* clientdata,
											  unsigned frameSize,
											  unsigned numTruncatedBytes,
											  struct timeval presentationTime,
											  unsigned durationInMicroseconds )
{
	H264RTPStreamFramer *framer = (H264RTPStreamFramer*)clientdata;

	if(framer == NULL)
		return;

	framer->afterGettingHeader1();
}

void H264RTPStreamFramer::afterGettingHeader1()
{
	unsigned int nalu_size = *(int*)packetBuffer;
	nalu_size -= 12; // minus the rtp header which we already read and don't need.
	
	if( nalu_size > fMaxSize )
	{
		fFrameSize = fMaxSize;
		fNumTruncatedBytes = fMaxSize - nalu_size;
		nalu_size = fMaxSize;
	}
	else
		fFrameSize = nalu_size;

	fInputSource->getNextFrame( fTo, nalu_size,
								afterGettingNALU,
								this,
								handleClosure,
								this);

	
}

void H264RTPStreamFramer::afterGettingNALU( void* clientdata,
											  unsigned frameSize,
											  unsigned numTruncatedBytes,
											  struct timeval presentationTime,
											  unsigned durationInMicroseconds )
{
	H264RTPStreamFramer *framer = (H264RTPStreamFramer*)clientdata;

	if(framer == NULL)
		return;

	framer->afterGettingNALU1();
}

void H264RTPStreamFramer::afterGettingNALU1()
{
	int temp32;
	short temp16;

	int i_type = fTo[0]&0x1f;
	int i_ref_idc = (fTo[0] >> 5)&0x03;
	
	if( i_type != 7 && i_type != 8 )
	{
		if( isFirstFrame )
		{
			isFirstFrame = false;
			gettimeofday( &lastPresentationTime);
		}
		else
		{
			incrementTimeval(&lastPresentationTime,33000);
		}

		fPresentationTime = lastPresentationTime;			

		fDurationInMicroseconds = 33000;

	//	printf("NAL Unit read\n");
		FramedSource::afterGetting(this);
		printf("NALU delivered. Type: %d Size: %d\n",i_type, fFrameSize);
		return;
	}
	else if ( i_type == 7 )
	{
		unsigned char* sps = fTo + 1;

		size_t sps_size = fFrameSize - 1;
		size_t fsps_size = (sps_size/3 + 1)<<2;
				
		fsps = new char[fsps_size];

		encode(sps, sps_size, fsps, fsps_size, 255);

	//	printf("sps: %s\n",fsps);

		memcpy( ((char*)&profileLevelID)+2, sps, 1);
		memcpy( ((char*)&profileLevelID)+1, sps+1, 1);
		memcpy( ((char*)&profileLevelID), sps+2, 1);

		profileLevelID &= 0xffe0ff;
	//	printf("profile level ID: %06x\n",profileLevelID);

	//	fFrameSize = 0;
		FramedSource::afterGetting(this);
		return;
	}
	else if( i_type == 8)
	{
		unsigned char* pps = fTo + 1;

		size_t pps_size = fFrameSize - 1;
		size_t fpps_size = (pps_size/3 + 1)<<2;
		
		fpps = new char[fpps_size];

		encode(pps, pps_size, fpps, fpps_size, 255);

	//	printf("pps: %s\n",fpps);
		
	//	fFrameSize = 0;
		FramedSource::afterGetting(this);
		return;
	}

}

H264RTPStreamFramer* H264RTPStreamFramer::createNew( UsageEnvironment& env, FramedSource* inputSource) 
{

	H264RTPStreamFramer* newFramer = new H264RTPStreamFramer(env,inputSource);

	if( newFramer == NULL )
		return NULL;

	return newFramer;
}

