/*
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
 */

/* the tspid packages and crc tables are from libmpeg */

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "stpti.h"

#define H264	0x1b
#define MPEG2	0x02
#define AC3		0x06
#define MP3		0x04

uint8_t cc = 0;

static uint8_t tspid0_tmpl[TS_SIZE] = { // PAT
	0x47, 0x40, 0x00, 0x10, 0x00, 0x00, 0xb0, 0x11,
	0x00, 0x00, 0xcb, 0x00, 0x00, 0x00, 0x00, 0xe0, // version, 0xcb (pos 10)
	0x10, 0x00, 0x01, 0xe0, 0x01, 0x2a, 0xd6, 0x1a, // pos 18, program number 0x01, pos 20 pmt pid
	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_tmpl[TS_SIZE] = { // PMT
	0x47, 0x40, 0x01, 0x10, 0x00, 0x02, 0xb0, 0x1c, // 0x02, section id
	0x00, 0x01, 0xcb, 0x00, 0x00, 0xe0, 0xa0, 0xf0, // program number from pat 0x01 (pos 9), version 0xcb, pcr pid 0xa0 (pos 14)
	0x05, 0x48, 0x03, 0x01, 0x00, 0x00, 0x02, 0xe0, // 0x02, mpeg2, 0x1b h264, pos 22
	0xa0, 0xf0, 0x00, 0x03, 0xe0, 0x50, 0xf0, 0x00, // vpid 0xa0 (pos 24), audio type 0x03 (pos 27)
	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
};

// CRC32 lookup table for polynomial 0x04c11db7
static const uint32_t crc_table[256] = {
  0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
  0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
  0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
  0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
  0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
  0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
  0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
  0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
  0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
  0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
  0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
  0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
  0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
  0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
  0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
  0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
  0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
  0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
  0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
  0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
  0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
  0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
  0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
  0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
  0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
  0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
  0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
  0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
  0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
  0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
  0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
  0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
  0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
  0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
  0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
  0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
  0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
  0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
  0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
  0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
  0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
  0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
  0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
};

static uint32_t calc_crc32 (const uint8_t *sec, uint8_t len)
{
  int i;
  uint32_t crc = 0xffffffff;

  for (i = 0; i < len; i++)
    crc = (crc << 8) ^ crc_table[((crc >> 24) ^ *sec++) & 0xff];

  return crc;
}

/* Search for the pid in the stream, before it is the stream type specified.
 * It's not a 100% percent method, the pid could be found more than once in the pmt.
 * But its easier than actually parsing the pmt.
 */
static unsigned int get_pid_type(uint16_t pid, unsigned int pos, uint16_t *type)
{
	unsigned char buf[188];
	int count, i;
	uint16_t curr_pid;

	int pmt_fd = open("/tmp/pmt.tmp", O_RDONLY);
	count = read(pmt_fd, buf, 188);

	// skip the first entry, always the same 0x02
	for (i=pos; i<count;i++)
	{
		curr_pid = ((uint16_t)(buf[i] & 0x1f) << 8) | buf[i+1];
		if (pid == curr_pid && buf[i-1] != 0x0)
		{
			*type = buf[i-1];
			return i+1;
		}
	}

	return 0;
}

static uint16_t get_video_type(uint16_t vpid)
{
	unsigned int pos = 1;
	uint16_t type;

	do
	{
		pos = get_pid_type(vpid, pos, &type);
		switch(type)
		{
			case 0x01: /* MPEG-1 video */
			case 0x02: /* MPEG-2 video */
			case 0x10: /* MPEG-4 video */
			case 0x1B: /* H264 */
				return type;
			default:
				break;
		}
	} while (pos > 0);
	
	printf("[streamts.c] Error: Could not determine video type!\n");
	exit(EXIT_FAILURE);
}

static uint16_t get_audio_type(uint16_t apid)
{
	unsigned int pos = 1;
	uint16_t type;

	do
	{
		pos = get_pid_type(apid, pos, &type);
		switch(type)
		{
			case 0x03: /* MPEG-1 audio */
        	case 0x04: /* MPEG-2 audio */
				return type;
			case 0x06: /* MPEG-4 video */
			case 0x81:
				return 0x81; // VLC does not know 0x06
			default:
				break;
		}
	} while (pos > 0);
	
	printf("[streamts.c] Error: Could not determine audio type!\n");
	exit(EXIT_FAILURE);
}

void reset_pat_counter(void)
{
	cc = 0;
}

void create_pat_pmt(unsigned char *buf_out, uint16_t pidpcr, uint16_t pidv, uint16_t pida)
{
	//static uint16_t p_num = 1;
    uint8_t pat_len, pmt_len;
    uint32_t crc32;
	uint8_t tspid0[TS_SIZE], tspid1[TS_SIZE];
   	uint8_t version;

	memcpy(tspid0, tspid0_tmpl, TS_SIZE);
	memcpy(tspid1, tspid1_tmpl, TS_SIZE);

    /* length is not correct, but we only create a very small
     * PMT, so it doesn't matter :-)
     */
	pat_len = tspid0[7] + 3;
    pmt_len = tspid1[7] + 3;

	// PCR Pid, else no audio because PTS cannot be interpreted
	tspid1[14]  = pidpcr;
	tspid1[13] |= (pidpcr >> 8) & 0x3F;
	// video pid
	tspid1[24]  = pidv;
	tspid1[23] |= (pidv >> 8) & 0x3F;
	// audio pid
	tspid1[29]  = pida;
	tspid1[28] |= (pida >> 8) & 0x3F;
	// set the video and audio stream type (h.264,ac3...)
	tspid1[22] = get_video_type(pidv);
	tspid1[27] = get_audio_type(pida);
	
	printf("[streamts.c] Videotype: 0x%x, Audiotype: 0x%x\n", tspid1[22], tspid1[27]);

	// advance program number, bad idea, crashes vlc when switching!
	/*tspid0[18] = p_num;
	tspid1[9] = p_num;
	p_num++;*/

	/* Randomly change version numbers.
     * vlc ingnores PAT if it has the same version as the previous.
	 * 5 bits of version_number | current_next_indicator */
	version = rand() % 32;
	tspid0[10] =   0xc0 /* reserved bits set to 1 */
                           | ((version & 0x1f) << 1)
                           | 0x01; // current_next
	tspid1[10] =   0xc0 /* reserved bits set to 1 */
                           | ((version & 0x1f) << 1)
                           | 0x01; // current_next

	// crc pmt
    crc32 = calc_crc32 (&tspid1[5], pmt_len - 4);
    tspid1[5 + pmt_len - 4] = (crc32 & 0xff000000) >> 24;
    tspid1[5 + pmt_len - 3] = (crc32 & 0x00ff0000) >> 16;
    tspid1[5 + pmt_len - 2] = (crc32 & 0x0000ff00) >>  8;
    tspid1[5 + pmt_len - 1] = (crc32 & 0x000000ff) >>  0;

	// crc pat
	crc32 = calc_crc32 (&tspid0[5], pat_len - 4);
    tspid0[5 + pat_len - 4] = (crc32 & 0xff000000) >> 24;
    tspid0[5 + pat_len - 3] = (crc32 & 0x00ff0000) >> 16;
    tspid0[5 + pat_len - 2] = (crc32 & 0x0000ff00) >>  8;
    tspid0[5 + pat_len - 1] = (crc32 & 0x000000ff) >>  0;

	// continuity counter
	//tspid0[3] &= 0xF0 ;
	tspid0[3] |= cc & 0x0F;
	//tspid1[3] &= 0xF0 ;
	tspid1[3] |= cc & 0x0F;
	cc++;

	memcpy(buf_out,tspid0,188);
	memcpy(buf_out+188,tspid1,188);
}
