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(§ion_header, 0, sizeof(struct ts_section_header));
79
80 uint8_t *section_data = ts_section_header_parse(ts_packet, &pat->ts_header, §ion_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