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
|
#include <string.h>
#include <stdlib.h>
#include "getstream.h"
#include "psi.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;
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;
}
|