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 <time.h>
#include <stdlib.h>
#include <string.h>
#include <glib.h>
#include "getstream.h"
#include "psi.h"
/*
* PAT Programm Association Table
*
* The PAT is multiplexed into the TS (Transport Stream)
* at PID 0x0. It contains the table of all PMT (Program Map Table)
* pids within the TS.
*
* Name Bits Offset
*
* table_id 8 0
* section_syntax_indicator 1 1
* pad (0) 1 1
* reserved 2 1
* section_length 12 1/2
* transport_stream_id 16 3/4
* reserved 2 5
* version_number 5 5
* current_next_indicator 1 5
* section_number 8 6
* last_section_number 8 7
*
* 1..N
* program_number 16
* reserved 3
* pid 13
* (if pnum==0 network_pid else program_map_pid)
*
* crc32 32
*
*/
struct patprog_s {
uint16_t pnr;
uint16_t pid;
};
static int pat_pnrno(struct psisec_s *section) {
return psi_payload_len(section)/PAT_PNR_LEN;
}
static int pat_pnrfrompat(struct psisec_s *section, int i) {
uint8_t *pat=section->data;
return pat[PAT_HDR_LEN+i*4+0] << 8 | pat[PAT_HDR_LEN+i*4+1];
}
static int pat_pidfrompat(struct psisec_s *section, int i) {
uint8_t *pat=section->data;
return (pat[PAT_HDR_LEN+i*4+2] << 8 | pat[PAT_HDR_LEN+i*4+3]) & PID_MASK;
}
/*
* Send out a pat created from a struct pat_s. Things like the CC (Continuity Counter)
* version and transport id are passed from the caller.
* pat_send assembles the sections and passes them onto psi_segment_and_send which
* will create TS packets and feed them into the callback.
*
* FIXME This is completely untested with multisegment PATs so beware of the dogs.
*
*
*/
unsigned int pat_send(struct pat_s *pat, uint8_t cc, uint8_t version, uint16_t tid,
void (*callback)(void *data, void *arg), void *arg) {
struct psisec_s *section=psi_section_new();
uint8_t *p;
uint32_t ccrc;
GList *pl=g_list_first(pat->program);
int i, seclen, patlen;
int pkts=0;
/* FIXME - Need to calculate number of sections */
p=section->data;
while(1) {
memset(p, 0xff, PSI_SECTION_MAX);
p[PSI_TABLE_ID_OFF]=PAT_TABLE_ID;
p[PAT_TID_OFF1]=(tid>>8);
p[PAT_TID_OFF2]=(tid&0xff);
/* Version and set lowest bit to 1 (current next) */
p[PSI_VERSION_OFF]=(version&0x1f)<<1|1;
p[PSI_SECNO_OFF]=0x0;
p[PSI_LASTSECNO_OFF]=0x0;
i=0;
for(;pl;pl=g_list_next(pl)) {
struct patprog_s *pp=pl->data;
p[PAT_HDR_LEN+i*4+0]=pp->pnr >> 8;
p[PAT_HDR_LEN+i*4+1]=pp->pnr & 0xff;
p[PAT_HDR_LEN+i*4+2]=pp->pid >> 8;
p[PAT_HDR_LEN+i*4+3]=pp->pid & 0xff;
/* FIXME - Should check for PSI Section overflow (multi section PAT) */
i++;
}
patlen=PAT_HDR_LEN+i*4+CRC32_LEN;
seclen=(patlen-PSI_SECLEN_ADD)&PSI_SECLEN_MASK;
p[PSI_SECLEN_OFF+0]=0x80|(seclen>>8);
p[PSI_SECLEN_OFF+1]=(seclen&0xff);
ccrc=crc32_be(0xffffffff, p, patlen-CRC32_LEN);
p[patlen-CRC32_LEN+0]=(ccrc>>24)&0xff;
p[patlen-CRC32_LEN+1]=(ccrc>>16)&0xff;
p[patlen-CRC32_LEN+2]=(ccrc>>8)&0xff;
p[patlen-CRC32_LEN+3]=(ccrc&0xff);
pkts=psi_segment_and_send(section, 0, cc+pkts, callback, arg);
/* If we have all programs in this section */
if (!pl)
break;
}
psi_section_free(section);
return pkts;
}
struct pat_s *pat_new() {
return calloc(1, sizeof(struct pat_s));
}
void pat_free(struct pat_s *pat) {
GList *pl=g_list_first(pat->program);
while(pl) {
g_free(pl->data);
pl=g_list_next(pl);
}
g_list_free(pat->program);
free(pat);
}
void pat_add_program(struct pat_s *pat, uint16_t pnr, uint16_t pid) {
struct patprog_s *prog=g_new(struct patprog_s, 1);
prog->pnr=pnr;
prog->pid=pid;
pat->program=g_list_append(pat->program, prog);
}
struct pat_s *pat_parse(struct adapter_s *a) {
struct psisec_s *s;
struct pat_s *next;
unsigned int secnum;
unsigned int lastsecnum;
unsigned int pnr;
if (!a->pat.psi.section[0])
return NULL;
next=pat_new();
lastsecnum=psi_last_section_number(a->pat.psi.section[0]);
for(secnum=0;secnum<=lastsecnum;secnum++) {
s=a->pat.psi.section[secnum];
if (!s)
continue;
for(pnr=0;pnr<=pat_pnrno(s);pnr++) {
pat_add_program(next,
pat_pnrfrompat(s, pnr),
pat_pidfrompat(s, pnr));
next->progcount++;
}
}
logwrite(LOG_DEBUG, "pat: pat parse found %d programs", next->progcount);
return next;
}
static struct patprog_s *pat_findpnr(struct pat_s *pat, uint16_t pnr) {
GList *ppl=g_list_first(pat->program);
while(ppl) {
struct patprog_s *prog=ppl->data;
if (prog->pnr == pnr)
return prog;
ppl=g_list_next(ppl);
}
return NULL;
}
/*
* The PAT did change e.g. we added or changed a section
* Parse it into our pat_s structure and check which PMTs
* changed or were added and service the PMT callback
*
*/
static void pat_update(struct adapter_s *a) {
struct pat_s *current=a->pat.current;
struct pat_s *last=a->pat.last;
GList *pl;
struct patprog_s *plast, *pcur;
/* Check whether new programs appeared or the programs PMTPIDs changed */
for(pl=g_list_first(current->program);pl;pl=g_list_next(pl)) {
pcur=pl->data;
/* Do we have a current PAT ? */
if (!last) {
pmt_pidfrompat(a, pcur->pnr, pcur->pid);
} else {
/* Do we have the program and if so did the PMT pid change ? */
plast=pat_findpnr(last, pcur->pnr);
if (plast && plast->pid != pcur->pid)
pmt_pidfrompat(a, pcur->pnr, pcur->pid);
}
}
if (!last)
return;
/* Did programs disappear e.g. we dont have their PNR anymore */
for(pl=g_list_first(last->program);pl;pl=g_list_next(pl)) {
plast=pl->data;
pcur=pat_findpnr(current, plast->pnr);
if (!pcur)
pmt_pidfrompat(a, plast->pnr, 0);
}
}
/* Add/Update a section in our PAT.
*
* - Check whether this is a current section
* - Check whether its really a PAT
* - Add/Update the section in our PSI table
* - If we had an update/change parse the PAT and call pat_update
*
* Input:
* - pointer to our adapter
* - a static section (need to clone before usage)
*
*/
static void pat_section_cb(void *data, void *arg) {
struct psisec_s *section=data;
struct adapter_s *adapter=arg;
if (!psi_currentnext(section))
return;
if (psi_tableid(section) != PAT_TABLE_ID)
return;
if (!psi_update_table(&adapter->pat.psi, section))
return;
if (adapter->pat.last)
pat_free(adapter->pat.last);
adapter->pat.last=adapter->pat.current;
adapter->pat.current=pat_parse(adapter);
pat_update(adapter);
return;
}
void pat_init(struct adapter_s *adapter) {
/* Did we already add a callback? */
if (adapter->pat.cbc != NULL)
return;
adapter->pat.cbc=dvr_add_pcb(adapter,
0, DVRCB_SECTION, PID_PAT,
pat_section_cb, adapter);
}
|