-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpsi.c
293 lines (231 loc) · 6.61 KB
/
psi.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
#include <string.h>
#include <stdlib.h>
#include "getstream.h"
#include "psi.h"
#include "crc32.h"
/*
* PSI (Program Specific Information) handling
*
* Reassembly of TS Packets into sections
* Handling of multiple sections
* Callback on change
*
*
*
*/
static uint32_t psi_crc(struct psisec_s *section) {
uint8_t *crcp=§ion->data[psi_len(section)-4];
return crcp[0]<<24|crcp[1]<<16|crcp[2]<<8|crcp[3];
}
/* Calculate the CRC of the section */
static uint32_t psi_ccrc(struct psisec_s *section) {
return crc32_be(0xffffffff, section->data, psi_len(section)-4);
}
/* Check if PSI has valid CRC32 - return true if it has */
static int psi_crc_valid(struct psisec_s *section) {
return (psi_crc(section) == psi_ccrc(section));
}
static void psi_section_clear(struct psisec_s *section) {
section->valid=0;
section->len=0;
}
int psi_reassemble_continue(struct psisec_s *section, uint8_t *ts, int off) {
int copylen;
uint8_t ccexp, cc;
/*
* Calculate the next CC counter value. As a section needs to
* be completed before the next may begin on a PID we only
* accept continuous packets. If we fail the CC test we zap
* the whole section.
*
*/
ccexp=(section->cc+1)&TS_CC_MASK;
cc=ts_cc(ts);
if (ccexp != cc) {
psi_section_clear(section);
return PSI_RC_CCFAIL;
}
/*
* If we didnt have a hdr - complete it otherwise we dont
* even know the length and cant tell whether the section is
* complete.
*/
if (!section->len) {
copylen=PSI_HDR_LEN-section->valid;
memcpy(§ion->data[section->valid], &ts[off], copylen);
section->valid+=copylen;
section->len=_psi_len(section->data);
off+=copylen;
}
copylen=MIN(TS_PACKET_SIZE-off, section->len-section->valid);
memcpy(§ion->data[section->valid], &ts[off], copylen);
section->valid+=copylen;
section->cc=cc;
return off+copylen;
}
/*
* Copy the start of an PSI packet into our section buffer beginning
* at offset and fill section->len if possible
*
* Return the new offset
*/
int psi_reassemble_start(struct psisec_s *section, uint8_t *ts, int off) {
uint8_t *payloadptr=&ts[off];
int copylen;
psi_section_clear(section);
section->cc=ts_cc(ts);
section->pid=ts_pid(ts);
/* Copy until the end of the packet */
copylen=TS_PACKET_SIZE-off;
/*
* If not we include the PSI header in which case we
* can get the real length
*
*/
if (TS_PACKET_SIZE-off > PSI_HDR_LEN) {
section->len=_psi_len(payloadptr);
copylen=MIN(section->len, TS_PACKET_SIZE-off);
}
memcpy(section->data, payloadptr, copylen);
section->valid=copylen;
return off+copylen;
}
/*
* We get passed a static allocated psisec structure, a TS packet and an
* offset into the packet where we need to start looking for sections.
*
* Input:
* PSI section structute
* TS packet
* Offset to start looking for PSI data
*
* Output:
* Fills PSI section as far as possible
*
* Returns:
* 0 - If section is finished and no more bytes in TS
* positive- If section is finished and more bytes in TS
* negative- If section is not finished and we are done
*
*/
/* Reassemble a generic PSI (Program Specific Information) section e.g. PMT PAT CA packet */
int psi_reassemble(struct psisec_s *section, uint8_t *ts, int off) {
int noff;
int payload;
if (off) {
payload=off;
if (ts[payload] == 0xff)
return PSI_RC_NOPAYLOAD;
} else {
if (ts_tei(ts))
return PSI_RC_TEI;
if (!ts_has_payload(ts))
return PSI_RC_NOPAYLOAD;
payload=ts_payload_start(ts);
/*
* If "Payload Unit Start Indicator" is set the first byte
* payload is the pointer to the first section.
* ISO 13818-1 2.4.4.2
*/
if (ts_pusi(ts))
payload+=ts[payload]+1;
if (payload >= TS_PACKET_SIZE)
return PSI_RC_CORRUPT;
}
/* If we dont have a Payload Unit Start Indicator and we already
* started a section on this PID (valid is non null) we continue
* otherwise we start the reassembly ...
*
* This means that a packet with a
*/
if (!ts_pusi(ts)) {
if (!section->valid)
return PSI_RC_NOPAYLOAD;
noff=psi_reassemble_continue(section, ts, payload);
} else {
noff=psi_reassemble_start(section, ts, payload);
}
/*
* We didnt finish this section in this packet so wait for the next packet
* The section->valid check is another precaution for broken/empty
* invalid section parts collected ...
*/
if (section->len != section->valid || section->valid < PSI_HDR_LEN)
return PSI_RC_INCOMPLETE;
if (!psi_crc_valid(section))
return PSI_RC_CRCFAIL;
return noff;
}
int psi_section_valid(unsigned int pid, struct psisec_s *section, int len) {
section->pid=pid;
section->len=len;
section->valid=0;
if (!psi_crc_valid(section))
return PSI_RC_CRCFAIL;
if (psi_len(section)!=len)
return PSI_RC_LENFAIL;
return PSI_RC_OK;
}
struct psisec_s *psi_section_new(void ) {
return calloc(1, sizeof(struct psisec_s));
}
void psi_section_free(struct psisec_s *section) {
free(section);
}
struct psisec_s *psi_section_clone(struct psisec_s *section) {
struct psisec_s *new=psi_section_new();
memcpy(new, section, sizeof(struct psisec_s));
return new;
}
int psi_section_fromdata(struct psisec_s *section, unsigned int pid, uint8_t *data, int len) {
psi_section_clear(section);
memcpy(§ion->data, data, len);
return psi_section_valid(pid, section, len);
}
unsigned int psi_segment_and_send(struct psisec_s *section, unsigned int pid, uint8_t cc,
void (*callback)(void *data, void *arg), void *arg) {
uint8_t ts[TS_PACKET_SIZE];
int pkts=0;
int plen=psi_len(section);
int left=plen;
int tspayloadoff;
int copylen;
while(1) {
memset(&ts, 0xff, TS_PACKET_SIZE);
ts[TS_SYNC_OFF]=TS_SYNC;
ts[TS_PID_OFF1]=pid>>8;
ts[TS_PID_OFF2]=pid&0xff;
ts[TS_AFC_OFF]=0x1<<TS_AFC_SHIFT; /* Payload only */
ts[TS_CC_OFF]|=(cc+pkts)&TS_CC_MASK; /* Continuity Counter */
tspayloadoff=TS_PAYLOAD_OFF;
if (!pkts) {
ts[TS_PID_OFF1]|=TS_PUSI_MASK;
ts[TS_PAYLOAD_OFF]=0x0; /* Clear PSI Pointer */
tspayloadoff++;
}
/* Either full section or as much as fits */
copylen=MIN(left, TS_PACKET_SIZE-tspayloadoff);
/* Copy PSI section into TS packet */
memcpy(&ts[tspayloadoff], §ion->data[plen-left], copylen);
callback(ts, arg);
left-=copylen;
pkts++;
if (left <= 0)
break;
}
return pkts;
}
int psi_update_table(struct psi_s *psi, struct psisec_s *section) {
uint8_t secnum;
uint8_t version;
secnum=psi_section_number(section);
version=psi_version(section);
/* Check if we have this section or if the section version changed */
if (psi->section[secnum]) {
if (version == psi_version(psi->section[secnum]))
return 0;
psi_section_free(psi->section[secnum]);
}
psi->section[secnum]=psi_section_clone(section);
return 1;
}