1 /*
2  * PAT table parser and generator
3  * Copyright (C) 2010-2011 Unix Solutions Ltd.
4  *
5  * Released under MIT license.
6  * See LICENSE-MIT.txt for license terms.
7  */
8 #include <stdio.h>
9 #include <unistd.h>
10 #include <netdb.h>
11 #include <stdlib.h>
12 #include <string.h>
13 
14 #include "tsfuncs.h"
15 
ts_pat_alloc()16 struct ts_pat *ts_pat_alloc() {
17 	struct ts_pat *pat = calloc(1, sizeof(struct ts_pat));
18 	pat->section_header	= ts_section_data_alloc();
19 	pat->programs_max	= 128;
20 	pat->programs		= calloc(pat->programs_max, sizeof(void *));
21 	return pat;
22 }
23 
ts_pat_programs_data_free(struct ts_pat * pat)24 static void ts_pat_programs_data_free(struct ts_pat *pat) {
25 	int i;
26 	for (i=0;i<pat->programs_num;i++) {
27 		if (pat->programs[i]) {
28 			FREE(pat->programs[i]);
29 		}
30 	}
31 }
32 
ts_pat_clear(struct ts_pat * pat)33 void ts_pat_clear(struct ts_pat *pat) {
34 	if (!pat)
35 		return;
36 	// save
37 	struct ts_section_header *section_header = pat->section_header;
38 	struct ts_pat_program **programs = pat->programs;
39 	int programs_max = pat->programs_max;
40 	// free
41 	ts_pat_programs_data_free(pat);
42 	// clear
43 	ts_section_data_clear(section_header);
44 	memset(pat, 0, sizeof(struct ts_pat));
45 	// restore
46 	pat->section_header = section_header;
47 	pat->programs = programs;
48 	pat->programs_max = programs_max;
49 }
50 
ts_pat_free(struct ts_pat ** ppat)51 void ts_pat_free(struct ts_pat **ppat) {
52 	struct ts_pat *pat = *ppat;
53 	if (pat) {
54 		ts_section_data_free(&pat->section_header);
55 		ts_pat_programs_data_free(pat);
56 		FREE(pat->programs);
57 		FREE(*ppat);
58 	}
59 }
60 
ts_pat_push_packet(struct ts_pat * pat,uint8_t * ts_packet)61 struct ts_pat *ts_pat_push_packet(struct ts_pat *pat, uint8_t *ts_packet) {
62 	struct ts_header ts_header;
63 	memset(&ts_header, 0, sizeof(struct ts_header));
64 
65 	if (ts_packet_header_parse(ts_packet, &ts_header)) {
66 		// PAT should be with PID 0x00
67 		if (ts_header.pid != 0x00)
68 			goto OUT;
69 		// Received PUSI packet before table END, clear the table to start gathering new one
70 		if (ts_header.pusi && pat->ts_header.pusi)
71 			ts_pat_clear(pat);
72 		if (!pat->ts_header.pusi)
73 			pat->ts_header = ts_header;
74 	}
75 
76 	if (ts_header.pusi) {
77 		struct ts_section_header section_header;
78 		memset(&section_header, 0, sizeof(struct ts_section_header));
79 
80 		uint8_t *section_data = ts_section_header_parse(ts_packet, &pat->ts_header, &section_header);
81 		if (!section_data) {
82 			memset(&pat->ts_header, 0, sizeof(struct ts_header));
83 			goto OUT;
84 		}
85 		// table_id should be 0x00 (program_association_section)
86 		if (section_header.table_id != 0x00) {
87 			memset(&pat->ts_header, 0, sizeof(struct ts_header));
88 			goto OUT;
89 		}
90 
91 		// Set correct section_header
92 		ts_section_header_parse(ts_packet, &pat->ts_header, pat->section_header);
93 	}
94 
95 	if (!pat->initialized) {
96 		ts_section_add_packet(pat->section_header, &ts_header, ts_packet);
97 		if (pat->section_header->initialized) {
98 			if (!ts_pat_parse(pat))
99 				goto ERROR;
100 		}
101 	}
102 
103 OUT:
104 	return pat;
105 
106 ERROR:
107 	ts_pat_clear(pat);
108 	return pat;
109 }
110 
ts_pat_parse(struct ts_pat * pat)111 int ts_pat_parse(struct ts_pat *pat) {
112 	uint8_t *section_data = pat->section_header->data;
113 	int section_len = pat->section_header->data_len;
114 
115 	while (section_len > 0) {
116 		if (pat->programs_num == pat->programs_max) {
117 			ts_LOGf("PAT contains too many programs (>%d), not all are initialized!\n", pat->programs_max);
118 			break;
119 		}
120 		struct ts_pat_program *pinfo = calloc(1, sizeof(struct ts_pat_program));
121 
122 		pinfo->program  = (section_data[0] << 8) | section_data[1];				// xxxxxxxx xxxxxxxx
123 		pinfo->reserved = (section_data[2] &~ 0x1F) >> 5;						// xxx11111
124 		pinfo->pid      = ((section_data[2] &~ 0xE0) << 8) | section_data[3];	// 111xxxxx xxxxxxxx
125 
126 		pat->programs[pat->programs_num] = pinfo;
127 		pat->programs_num++;
128 
129 		section_data += 4;
130 		section_len  -= 4;
131 	}
132 
133 	if (!ts_crc32_section_check(pat->section_header, "PAT"))
134 		return 0;
135 
136 	pat->initialized = 1;
137 	return 1;
138 }
139 
ts_pat_generate(struct ts_pat * pat,uint8_t ** ts_packets,int * num_packets)140 void ts_pat_generate(struct ts_pat *pat, uint8_t **ts_packets, int *num_packets) {
141 	uint8_t *secdata = ts_section_data_alloc_section();
142 	ts_section_header_generate(secdata, pat->section_header, 0);
143 	int curpos = 8; // Compensate for the section header, frist data byte is at offset 8
144 
145 	int i;
146 	for (i=0;i<pat->programs_num;i++) {
147 		struct ts_pat_program *prg = pat->programs[i];
148 		secdata[curpos + 0] = prg->program >> 8;
149 		secdata[curpos + 1] = prg->program &~ 0xff00;
150 
151 		secdata[curpos + 2]  = prg->reserved << 5;
152 		secdata[curpos + 2] |= prg->pid >> 8;
153 		secdata[curpos + 3]  = prg->pid &~ 0xff00;
154 		curpos += 4; // Compensate for the above
155 	}
156 	pat->section_header->CRC = ts_section_data_calculate_crc(secdata, curpos);
157 	curpos += 4; // CRC
158 
159 	ts_section_data_gen_ts_packets(&pat->ts_header, secdata, curpos, pat->section_header->pointer_field, ts_packets, num_packets);
160 
161 	FREE(secdata);
162 }
163 
ts_pat_regenerate_packets(struct ts_pat * pat)164 void ts_pat_regenerate_packets(struct ts_pat *pat) {
165 	uint8_t *ts_packets;
166 	int num_packets;
167 	ts_pat_generate(pat, &ts_packets, &num_packets);
168 	FREE(pat->section_header->packet_data);
169 	pat->section_header->packet_data = ts_packets;
170 	pat->section_header->num_packets = num_packets;
171 }
172 
ts_pat_copy(struct ts_pat * pat)173 struct ts_pat *ts_pat_copy(struct ts_pat *pat) {
174 	struct ts_pat *newpat = ts_pat_alloc();
175 	int i;
176 	for (i=0;i<pat->section_header->num_packets; i++) {
177 		newpat = ts_pat_push_packet(newpat, pat->section_header->packet_data + (i * TS_PACKET_SIZE));
178 	}
179 	if (newpat->initialized) {
180 		return newpat;
181 	} else {
182 		ts_LOGf("Error copying PAT!\n");
183 		ts_pat_free(&newpat);
184 		return NULL;
185 	}
186 }
187 
ts_pat_check_generator(struct ts_pat * pat)188 void ts_pat_check_generator(struct ts_pat *pat) {
189 	struct ts_pat *pat1 = ts_pat_copy(pat);
190 	if (pat1) {
191 		ts_compare_data("PAT (tspacket->struct)",
192 			pat1->section_header->packet_data,
193 			pat->section_header->packet_data,
194 			pat->section_header->num_packets * TS_PACKET_SIZE);
195 		ts_pat_free(&pat1);
196 	}
197 
198 	uint8_t *ts_packets;
199 	int num_packets;
200 	ts_pat_generate(pat, &ts_packets, &num_packets);
201 	if (num_packets != pat->section_header->num_packets) {
202 		ts_LOGf("ERROR: num_packets:%d != sec->num_packets:%d\n", num_packets, pat->section_header->num_packets);
203 	}
204 	ts_compare_data("PAT (struct->tspacket)", pat->section_header->packet_data, ts_packets, num_packets * TS_PACKET_SIZE);
205 	free(ts_packets);
206 }
207 
ts_pat_dump(struct ts_pat * pat)208 void ts_pat_dump(struct ts_pat *pat) {
209 	struct ts_section_header *sec = pat->section_header;
210 	int i;
211 
212 	ts_section_dump(sec);
213 
214 	ts_LOGf("  * PAT data\n");
215 	ts_LOGf("    * num_programs: %d\n", pat->programs_num);
216 	for (i=0;i<pat->programs_num;i++) {
217 		struct ts_pat_program *prg = pat->programs[i];
218 		ts_LOGf("      * [%02d/%02d]: Program No 0x%04x (%5d) -> PID %04x (%d) /res: 0x%02x/\n",
219 			i+1, pat->programs_num,
220 			prg->program, prg->program,
221 			prg->pid, prg->pid,
222 			prg->reserved);
223 		// Program number 0 is Network ID, not program id
224 		if (prg->program == 0) {
225 			ts_LOGf("      - NIT PID %04x (%d)\n", prg->pid, prg->pid);
226 		}
227 	}
228 
229 	ts_pat_check_generator(pat);
230 }
231 
ts_pat_is_same(struct ts_pat * pat1,struct ts_pat * pat2)232 int ts_pat_is_same(struct ts_pat *pat1, struct ts_pat *pat2) {
233 	if (pat1 == pat2) return 1; // Same
234 	if ((!pat1 && pat2) || (pat1 && !pat2)) return 0; // Not same (one is NULL)
235 	return ts_section_is_same(pat1->section_header, pat2->section_header);
236 }
237