1131 lines
24 KiB
C
1131 lines
24 KiB
C
/*
|
|
* MPEG TS Program Specific Information code
|
|
* Copyright (C) 2007 - 2010 Andreas Öman
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "tvheadend.h"
|
|
#include "psi.h"
|
|
#include "dvb/dvb_support.h"
|
|
#include "tsdemux.h"
|
|
#include "parsers.h"
|
|
|
|
static int
|
|
psi_section_reassemble0(psi_section_t *ps, const uint8_t *data,
|
|
int len, int start, int crc,
|
|
section_handler_t *cb, void *opaque)
|
|
{
|
|
int excess, tsize;
|
|
|
|
if(start) {
|
|
// Payload unit start indicator
|
|
ps->ps_offset = 0;
|
|
ps->ps_lock = 1;
|
|
}
|
|
|
|
if(!ps->ps_lock)
|
|
return -1;
|
|
|
|
memcpy(ps->ps_data + ps->ps_offset, data, len);
|
|
ps->ps_offset += len;
|
|
|
|
if(ps->ps_offset < 3) {
|
|
/* We don't know the total length yet */
|
|
return len;
|
|
}
|
|
|
|
tsize = 3 + (((ps->ps_data[1] & 0xf) << 8) | ps->ps_data[2]);
|
|
|
|
if(ps->ps_offset < tsize)
|
|
return len; // Not there yet
|
|
|
|
excess = ps->ps_offset - tsize;
|
|
|
|
if(crc && crc32(ps->ps_data, tsize, 0xffffffff))
|
|
return -1;
|
|
|
|
cb(ps->ps_data, tsize - (crc ? 4 : 0), opaque);
|
|
ps->ps_offset = 0;
|
|
return len - excess;
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
*/
|
|
void
|
|
psi_section_reassemble(psi_section_t *ps, const uint8_t *tsb, int crc,
|
|
section_handler_t *cb, void *opaque)
|
|
{
|
|
int off = tsb[3] & 0x20 ? tsb[4] + 5 : 4;
|
|
int pusi = tsb[1] & 0x40;
|
|
int r;
|
|
|
|
if(off >= 188) {
|
|
ps->ps_lock = 0;
|
|
return;
|
|
}
|
|
|
|
if(pusi) {
|
|
int len = tsb[off++];
|
|
if(len > 0) {
|
|
if(len > 188 - off) {
|
|
ps->ps_lock = 0;
|
|
return;
|
|
}
|
|
psi_section_reassemble0(ps, tsb + off, len, 0, crc, cb, opaque);
|
|
off += len;
|
|
}
|
|
}
|
|
|
|
while(off < 188 && tsb[off] != 0xff) {
|
|
r = psi_section_reassemble0(ps, tsb + off, 188 - off, pusi, crc,
|
|
cb, opaque);
|
|
if(r < 0) {
|
|
ps->ps_lock = 0;
|
|
break;
|
|
}
|
|
off += r;
|
|
pusi = 0;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* PAT parser, from ISO 13818-1
|
|
*/
|
|
int
|
|
psi_parse_pat(service_t *t, uint8_t *ptr, int len,
|
|
pid_section_callback_t *pmt_callback)
|
|
{
|
|
uint16_t prognum;
|
|
uint16_t pid;
|
|
elementary_stream_t *st;
|
|
|
|
lock_assert(&t->s_stream_mutex);
|
|
|
|
if(len < 5)
|
|
return -1;
|
|
|
|
ptr += 5;
|
|
len -= 5;
|
|
|
|
while(len >= 4) {
|
|
|
|
prognum = ptr[0] << 8 | ptr[1];
|
|
pid = (ptr[2] & 0x1f) << 8 | ptr[3];
|
|
|
|
if(prognum != 0) {
|
|
if(service_stream_find(t, pid) == NULL) {
|
|
st = service_stream_create(t, pid, SCT_PMT);
|
|
st->es_section_docrc = 1;
|
|
st->es_got_section = pmt_callback;
|
|
}
|
|
}
|
|
ptr += 4;
|
|
len -= 4;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Append CRC
|
|
*/
|
|
|
|
static int
|
|
psi_append_crc32(uint8_t *buf, int offset, int maxlen)
|
|
{
|
|
uint32_t crc;
|
|
|
|
if(offset + 4 > maxlen)
|
|
return -1;
|
|
|
|
crc = crc32(buf, offset, 0xffffffff);
|
|
|
|
buf[offset + 0] = crc >> 24;
|
|
buf[offset + 1] = crc >> 16;
|
|
buf[offset + 2] = crc >> 8;
|
|
buf[offset + 3] = crc;
|
|
|
|
assert(crc32(buf, offset + 4, 0xffffffff) == 0);
|
|
|
|
return offset + 4;
|
|
}
|
|
|
|
|
|
/**
|
|
* PAT generator
|
|
*/
|
|
|
|
int
|
|
psi_build_pat(service_t *t, uint8_t *buf, int maxlen, int pmtpid)
|
|
{
|
|
if(maxlen < 12)
|
|
return -1;
|
|
|
|
buf[0] = 0;
|
|
buf[1] = 0xb0; /* reserved */
|
|
buf[2] = 12 + 4 - 3; /* Length */
|
|
|
|
buf[3] = 0x00; /* transport stream id */
|
|
buf[4] = 0x01;
|
|
|
|
buf[5] = 0xc1; /* reserved + current_next_indicator + version */
|
|
buf[6] = 0;
|
|
buf[7] = 0;
|
|
|
|
buf[8] = 0; /* Program number, we only have one program */
|
|
buf[9] = 1;
|
|
|
|
buf[10] = 0xe0 | (pmtpid >> 8);
|
|
buf[11] = pmtpid;
|
|
|
|
return psi_append_crc32(buf, 12, maxlen);
|
|
}
|
|
|
|
|
|
/**
|
|
* PMT update reason flags
|
|
*/
|
|
#define PMT_UPDATE_PCR 0x1
|
|
#define PMT_UPDATE_NEW_STREAM 0x2
|
|
#define PMT_UPDATE_LANGUAGE 0x4
|
|
#define PMT_UPDATE_FRAME_DURATION 0x8
|
|
#define PMT_UPDATE_COMPOSITION_ID 0x10
|
|
#define PMT_UPDATE_ANCILLARY_ID 0x20
|
|
#define PMT_UPDATE_STREAM_DELETED 0x40
|
|
#define PMT_UPDATE_NEW_CA_STREAM 0x80
|
|
#define PMT_UPDATE_NEW_CAID 0x100
|
|
#define PMT_UPDATE_CA_PROVIDER_CHANGE 0x200
|
|
#define PMT_UPDATE_PARENT_PID 0x400
|
|
#define PMT_UPDATE_CAID_DELETED 0x800
|
|
#define PMT_REORDERED 0x1000
|
|
|
|
/**
|
|
* Add a CA descriptor
|
|
*/
|
|
static int
|
|
psi_desc_add_ca(service_t *t, uint16_t caid, uint32_t provid, uint16_t pid)
|
|
{
|
|
elementary_stream_t *st;
|
|
caid_t *c;
|
|
int r = 0;
|
|
|
|
if((st = service_stream_find(t, pid)) == NULL) {
|
|
st = service_stream_create(t, pid, SCT_CA);
|
|
r |= PMT_UPDATE_NEW_CA_STREAM;
|
|
}
|
|
|
|
st->es_delete_me = 0;
|
|
|
|
st->es_position = 0x40000;
|
|
|
|
LIST_FOREACH(c, &st->es_caids, link) {
|
|
if(c->caid == caid) {
|
|
c->delete_me = 0;
|
|
|
|
if(c->providerid != provid) {
|
|
c->providerid = provid;
|
|
r |= PMT_UPDATE_CA_PROVIDER_CHANGE;
|
|
}
|
|
return r;
|
|
}
|
|
}
|
|
|
|
c = malloc(sizeof(caid_t));
|
|
|
|
c->caid = caid;
|
|
c->providerid = provid;
|
|
|
|
c->delete_me = 0;
|
|
LIST_INSERT_HEAD(&st->es_caids, c, link);
|
|
r |= PMT_UPDATE_NEW_CAID;
|
|
return r;
|
|
}
|
|
|
|
/**
|
|
* Parser for CA descriptors
|
|
*/
|
|
static int
|
|
psi_desc_ca(service_t *t, const uint8_t *buffer, int size)
|
|
{
|
|
int r = 0;
|
|
int i;
|
|
uint32_t provid = 0;
|
|
uint16_t caid = (buffer[0] << 8) | buffer[1];
|
|
uint16_t pid = ((buffer[2]&0x1F) << 8) | buffer[3];
|
|
|
|
#if 0
|
|
printf("CA_DESC: ");
|
|
for(i = 0; i < size; i++)
|
|
printf("%02x.", buffer[i]);
|
|
printf("\n");
|
|
#endif
|
|
|
|
switch (caid & 0xFF00) {
|
|
case 0x0100: // SECA/Mediaguard
|
|
provid = (buffer[4] << 8) | buffer[5];
|
|
|
|
//Add extra providers, if any
|
|
for (i = 17; i < size; i += 15){
|
|
uint16_t xpid = ((buffer[i]&0x1F) << 8) | buffer[i + 1];
|
|
uint16_t xprovid = (buffer[i + 2] << 8) | buffer[i + 3];
|
|
|
|
r |= psi_desc_add_ca(t, caid, xprovid, xpid);
|
|
}
|
|
break;
|
|
case 0x0500:// Viaccess
|
|
for (i = 4; i < size;) {
|
|
unsigned char nano = buffer[i++];
|
|
unsigned char nanolen = buffer[i++];
|
|
|
|
if (nano == 0x14) {
|
|
provid = (buffer[i] << 16) | (buffer[i + 1] << 8) | (buffer[i + 2] & 0xf0);
|
|
break;
|
|
}
|
|
|
|
i += nanolen;
|
|
}
|
|
break;
|
|
case 0x4a00://DRECrypt
|
|
if (caid != 0x4aee) { // Bulcrypt
|
|
provid = size < 4 ? 0 : buffer[4];
|
|
break;
|
|
}
|
|
default:
|
|
provid = 0;
|
|
break;
|
|
}
|
|
|
|
r |= psi_desc_add_ca(t, caid, provid, pid);
|
|
|
|
return r;
|
|
}
|
|
|
|
/**
|
|
* Parser for teletext descriptor
|
|
*/
|
|
static int
|
|
psi_desc_teletext(service_t *t, const uint8_t *ptr, int size,
|
|
int parent_pid, int *position)
|
|
{
|
|
int r = 0;
|
|
elementary_stream_t *st;
|
|
|
|
while(size >= 5) {
|
|
int page = (ptr[3] & 0x7 ?: 8) * 100 + (ptr[4] >> 4) * 10 + (ptr[4] & 0xf);
|
|
int type = ptr[3] >> 3;
|
|
|
|
if(type == 2 || type == 5) {
|
|
// 2 = subtitle page, 5 = subtitle page [hearing impaired]
|
|
|
|
// We put the teletext subtitle driven streams on a list of pids
|
|
// higher than normal MPEG TS (0x2000 ++)
|
|
int pid = PID_TELETEXT_BASE + page;
|
|
|
|
if((st = service_stream_find(t, pid)) == NULL) {
|
|
r |= PMT_UPDATE_NEW_STREAM;
|
|
st = service_stream_create(t, pid, SCT_TEXTSUB);
|
|
}
|
|
|
|
st->es_delete_me = 0;
|
|
|
|
if(memcmp(st->es_lang, ptr, 3)) {
|
|
r |= PMT_UPDATE_LANGUAGE;
|
|
memcpy(st->es_lang, ptr, 3);
|
|
}
|
|
|
|
if(st->es_parent_pid != parent_pid) {
|
|
r |= PMT_UPDATE_PARENT_PID;
|
|
st->es_parent_pid = parent_pid;
|
|
}
|
|
|
|
if(st->es_position != *position) {
|
|
st->es_position = *position;
|
|
r |= PMT_REORDERED;
|
|
}
|
|
(*position)++;
|
|
}
|
|
ptr += 5;
|
|
size -= 5;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
static int
|
|
pidcmp(const void *A, const void *B)
|
|
{
|
|
elementary_stream_t *a = *(elementary_stream_t **)A;
|
|
elementary_stream_t *b = *(elementary_stream_t **)B;
|
|
return a->es_position - b->es_position;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
*
|
|
*/
|
|
static void
|
|
sort_pids(service_t *t)
|
|
{
|
|
elementary_stream_t *st, **v;
|
|
int num = 0, i = 0;
|
|
|
|
TAILQ_FOREACH(st, &t->s_components, es_link)
|
|
num++;
|
|
|
|
v = alloca(num * sizeof(elementary_stream_t *));
|
|
TAILQ_FOREACH(st, &t->s_components, es_link)
|
|
v[i++] = st;
|
|
|
|
qsort(v, num, sizeof(elementary_stream_t *), pidcmp);
|
|
|
|
TAILQ_INIT(&t->s_components);
|
|
for(i = 0; i < num; i++)
|
|
TAILQ_INSERT_TAIL(&t->s_components, v[i], es_link);
|
|
}
|
|
|
|
|
|
/**
|
|
* PMT parser, from ISO 13818-1 and ETSI EN 300 468
|
|
*/
|
|
int
|
|
psi_parse_pmt(service_t *t, const uint8_t *ptr, int len, int chksvcid,
|
|
int delete)
|
|
{
|
|
uint16_t pcr_pid, pid;
|
|
uint8_t estype;
|
|
int dllen;
|
|
uint8_t dtag, dlen;
|
|
uint16_t sid;
|
|
streaming_component_type_t hts_stream_type;
|
|
elementary_stream_t *st, *next;
|
|
char lang[4];
|
|
int update = 0;
|
|
int had_components;
|
|
int composition_id;
|
|
int ancillary_id;
|
|
int version;
|
|
int position = 0;
|
|
int tt_position = 1000;
|
|
|
|
caid_t *c, *cn;
|
|
|
|
if(len < 9)
|
|
return -1;
|
|
|
|
lock_assert(&t->s_stream_mutex);
|
|
|
|
had_components = !!TAILQ_FIRST(&t->s_components);
|
|
|
|
sid = ptr[0] << 8 | ptr[1];
|
|
version = ptr[2] >> 1 & 0x1f;
|
|
|
|
if((ptr[2] & 1) == 0) {
|
|
/* current_next_indicator == next, skip this */
|
|
return -1;
|
|
}
|
|
|
|
pcr_pid = (ptr[5] & 0x1f) << 8 | ptr[6];
|
|
dllen = (ptr[7] & 0xf) << 8 | ptr[8];
|
|
|
|
if(chksvcid && sid != t->s_dvb_service_id)
|
|
return -1;
|
|
|
|
if(t->s_pcr_pid != pcr_pid) {
|
|
t->s_pcr_pid = pcr_pid;
|
|
update |= PMT_UPDATE_PCR;
|
|
}
|
|
|
|
ptr += 9;
|
|
len -= 9;
|
|
|
|
/* Mark all streams for deletion */
|
|
if(delete) {
|
|
TAILQ_FOREACH(st, &t->s_components, es_link) {
|
|
st->es_delete_me = 1;
|
|
|
|
LIST_FOREACH(c, &st->es_caids, link)
|
|
c->delete_me = 1;
|
|
}
|
|
}
|
|
|
|
// Common descriptors
|
|
while(dllen > 1) {
|
|
dtag = ptr[0];
|
|
dlen = ptr[1];
|
|
|
|
len -= 2; ptr += 2; dllen -= 2;
|
|
if(dlen > len)
|
|
break;
|
|
|
|
switch(dtag) {
|
|
case DVB_DESC_CA:
|
|
update |= psi_desc_ca(t, ptr, dlen);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
len -= dlen; ptr += dlen; dllen -= dlen;
|
|
}
|
|
|
|
|
|
|
|
while(len >= 5) {
|
|
estype = ptr[0];
|
|
pid = (ptr[1] & 0x1f) << 8 | ptr[2];
|
|
dllen = (ptr[3] & 0xf) << 8 | ptr[4];
|
|
|
|
ptr += 5;
|
|
len -= 5;
|
|
|
|
hts_stream_type = SCT_UNKNOWN;
|
|
memset(lang, 0, 4);
|
|
composition_id = -1;
|
|
ancillary_id = -1;
|
|
|
|
switch(estype) {
|
|
case 0x01:
|
|
case 0x02:
|
|
hts_stream_type = SCT_MPEG2VIDEO;
|
|
break;
|
|
|
|
case 0x03:
|
|
case 0x04:
|
|
hts_stream_type = SCT_MPEG2AUDIO;
|
|
break;
|
|
|
|
case 0x81:
|
|
hts_stream_type = SCT_AC3;
|
|
break;
|
|
|
|
case 0x0f:
|
|
hts_stream_type = SCT_MP4A;
|
|
break;
|
|
|
|
case 0x11:
|
|
hts_stream_type = SCT_AAC;
|
|
break;
|
|
|
|
case 0x1b:
|
|
hts_stream_type = SCT_H264;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
while(dllen > 1) {
|
|
dtag = ptr[0];
|
|
dlen = ptr[1];
|
|
|
|
len -= 2; ptr += 2; dllen -= 2;
|
|
if(dlen > len)
|
|
break;
|
|
|
|
switch(dtag) {
|
|
case DVB_DESC_CA:
|
|
update |= psi_desc_ca(t, ptr, dlen);
|
|
break;
|
|
|
|
case DVB_DESC_REGISTRATION:
|
|
if(dlen == 4 &&
|
|
ptr[0] == 'A' && ptr[1] == 'C' && ptr[2] == '-' && ptr[3] == '3')
|
|
hts_stream_type = SCT_AC3;
|
|
break;
|
|
|
|
case DVB_DESC_LANGUAGE:
|
|
memcpy(lang, ptr, 3);
|
|
break;
|
|
|
|
case DVB_DESC_TELETEXT:
|
|
if(estype == 0x06)
|
|
hts_stream_type = SCT_TELETEXT;
|
|
|
|
update |= psi_desc_teletext(t, ptr, dlen, pid, &tt_position);
|
|
break;
|
|
|
|
case DVB_DESC_AC3:
|
|
if(estype == 0x06 || estype == 0x81)
|
|
hts_stream_type = SCT_AC3;
|
|
break;
|
|
|
|
case DVB_DESC_AAC:
|
|
if(estype == 0x0f)
|
|
hts_stream_type = SCT_MP4A;
|
|
else if(estype == 0x11)
|
|
hts_stream_type = SCT_AAC;
|
|
break;
|
|
|
|
case DVB_DESC_SUBTITLE:
|
|
if(dlen < 8)
|
|
break;
|
|
|
|
memcpy(lang, ptr, 3);
|
|
composition_id = ptr[4] << 8 | ptr[5];
|
|
ancillary_id = ptr[6] << 8 | ptr[7];
|
|
hts_stream_type = SCT_DVBSUB;
|
|
break;
|
|
|
|
case DVB_DESC_EAC3:
|
|
if(estype == 0x06 || estype == 0x81)
|
|
hts_stream_type = SCT_EAC3;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
len -= dlen; ptr += dlen; dllen -= dlen;
|
|
}
|
|
|
|
if(hts_stream_type == SCT_UNKNOWN && estype == 0x06 &&
|
|
pid == 3401 && t->s_dvb_service_id == 10510) {
|
|
// Workaround for ITV HD
|
|
hts_stream_type = SCT_H264;
|
|
}
|
|
|
|
if(hts_stream_type != SCT_UNKNOWN) {
|
|
|
|
if((st = service_stream_find(t, pid)) == NULL) {
|
|
update |= PMT_UPDATE_NEW_STREAM;
|
|
st = service_stream_create(t, pid, hts_stream_type);
|
|
}
|
|
|
|
// Jernej: I don't know why. But it seems that sometimes the stream is created with a wrong es_type??
|
|
if(st->es_type != hts_stream_type) {
|
|
st->es_type = hts_stream_type;
|
|
}
|
|
|
|
st->es_delete_me = 0;
|
|
|
|
if(st->es_position != position) {
|
|
update |= PMT_REORDERED;
|
|
st->es_position = position;
|
|
}
|
|
|
|
if(memcmp(st->es_lang, lang, 4)) {
|
|
update |= PMT_UPDATE_LANGUAGE;
|
|
memcpy(st->es_lang, lang, 4);
|
|
}
|
|
|
|
if(composition_id != -1 && st->es_composition_id != composition_id) {
|
|
st->es_composition_id = composition_id;
|
|
update |= PMT_UPDATE_COMPOSITION_ID;
|
|
}
|
|
|
|
if(ancillary_id != -1 && st->es_ancillary_id != ancillary_id) {
|
|
st->es_ancillary_id = ancillary_id;
|
|
update |= PMT_UPDATE_ANCILLARY_ID;
|
|
}
|
|
}
|
|
position++;
|
|
}
|
|
|
|
/* Scan again to see if any streams should be deleted */
|
|
for(st = TAILQ_FIRST(&t->s_components); st != NULL; st = next) {
|
|
next = TAILQ_NEXT(st, es_link);
|
|
|
|
for(c = LIST_FIRST(&st->es_caids); c != NULL; c = cn) {
|
|
cn = LIST_NEXT(c, link);
|
|
if(c->delete_me) {
|
|
LIST_REMOVE(c, link);
|
|
free(c);
|
|
update |= PMT_UPDATE_CAID_DELETED;
|
|
}
|
|
}
|
|
|
|
|
|
if(st->es_delete_me) {
|
|
service_stream_destroy(t, st);
|
|
update |= PMT_UPDATE_STREAM_DELETED;
|
|
}
|
|
}
|
|
|
|
if(update & PMT_REORDERED)
|
|
sort_pids(t);
|
|
|
|
if(update) {
|
|
tvhlog(LOG_DEBUG, "PSI", "Service \"%s\" PMT (version %d) updated"
|
|
"%s%s%s%s%s%s%s%s%s%s%s%s%s",
|
|
service_nicename(t), version,
|
|
update&PMT_UPDATE_PCR ? ", PCR PID changed":"",
|
|
update&PMT_UPDATE_NEW_STREAM ? ", New elementary stream":"",
|
|
update&PMT_UPDATE_LANGUAGE ? ", Language changed":"",
|
|
update&PMT_UPDATE_FRAME_DURATION ? ", Frame duration changed":"",
|
|
update&PMT_UPDATE_COMPOSITION_ID ? ", Composition ID changed":"",
|
|
update&PMT_UPDATE_ANCILLARY_ID ? ", Ancillary ID changed":"",
|
|
update&PMT_UPDATE_STREAM_DELETED ? ", Stream deleted":"",
|
|
update&PMT_UPDATE_NEW_CA_STREAM ? ", New CA stream":"",
|
|
update&PMT_UPDATE_NEW_CAID ? ", New CAID":"",
|
|
update&PMT_UPDATE_CA_PROVIDER_CHANGE? ", CA provider changed":"",
|
|
update&PMT_UPDATE_PARENT_PID ? ", Parent PID changed":"",
|
|
update&PMT_UPDATE_CAID_DELETED ? ", CAID deleted":"",
|
|
update&PMT_REORDERED ? ", PIDs reordered":"");
|
|
|
|
service_request_save(t, 0);
|
|
|
|
// Only restart if something that our clients worry about did change
|
|
if(update & !(PMT_UPDATE_NEW_CA_STREAM |
|
|
PMT_UPDATE_NEW_CAID |
|
|
PMT_UPDATE_CA_PROVIDER_CHANGE |
|
|
PMT_UPDATE_CAID_DELETED)) {
|
|
if(t->s_status == SERVICE_RUNNING)
|
|
service_restart(t, had_components);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* PMT generator
|
|
*/
|
|
int
|
|
psi_build_pmt(streaming_start_t *ss, uint8_t *buf0, int maxlen, int pcrpid)
|
|
{
|
|
int c, tlen, dlen, l, i;
|
|
uint8_t *buf, *buf1;
|
|
|
|
buf = buf0;
|
|
|
|
if(maxlen < 12)
|
|
return -1;
|
|
|
|
buf[0] = 2; /* table id, always 2 */
|
|
|
|
buf[3] = 0x00; /* program id */
|
|
buf[4] = 0x01;
|
|
|
|
buf[5] = 0xc1; /* current_next_indicator + version */
|
|
buf[6] = 0;
|
|
buf[7] = 0;
|
|
|
|
buf[8] = 0xe0 | (pcrpid >> 8);
|
|
buf[9] = pcrpid;
|
|
|
|
buf[10] = 0xf0; /* Program info length */
|
|
buf[11] = 0x00; /* We dont have any such things atm */
|
|
|
|
buf += 12;
|
|
tlen = 12;
|
|
|
|
for(i = 0; i < ss->ss_num_components; i++) {
|
|
streaming_start_component_t *ssc = &ss->ss_components[i];
|
|
|
|
switch(ssc->ssc_type) {
|
|
case SCT_MPEG2VIDEO:
|
|
c = 0x02;
|
|
break;
|
|
|
|
case SCT_MPEG2AUDIO:
|
|
c = 0x04;
|
|
break;
|
|
|
|
case SCT_DVBSUB:
|
|
c = 0x06;
|
|
break;
|
|
|
|
case SCT_MP4A:
|
|
case SCT_AAC:
|
|
c = 0x11;
|
|
break;
|
|
|
|
case SCT_H264:
|
|
c = 0x1b;
|
|
break;
|
|
|
|
case SCT_AC3:
|
|
c = 0x81;
|
|
break;
|
|
|
|
default:
|
|
continue;
|
|
}
|
|
|
|
|
|
buf[0] = c;
|
|
buf[1] = 0xe0 | (ssc->ssc_pid >> 8);
|
|
buf[2] = ssc->ssc_pid;
|
|
|
|
buf1 = &buf[3];
|
|
tlen += 5;
|
|
buf += 5;
|
|
dlen = 0;
|
|
|
|
switch(ssc->ssc_type) {
|
|
case SCT_MPEG2AUDIO:
|
|
case SCT_MP4A:
|
|
case SCT_AAC:
|
|
buf[0] = DVB_DESC_LANGUAGE;
|
|
buf[1] = 4;
|
|
memcpy(&buf[2],ssc->ssc_lang,3);
|
|
buf[5] = 0; /* Main audio */
|
|
dlen = 6;
|
|
break;
|
|
case SCT_DVBSUB:
|
|
buf[0] = DVB_DESC_SUBTITLE;
|
|
buf[1] = 8;
|
|
memcpy(&buf[2],ssc->ssc_lang,3);
|
|
buf[5] = 16; /* Subtitling type */
|
|
buf[6] = ssc->ssc_composition_id >> 8;
|
|
buf[7] = ssc->ssc_composition_id;
|
|
buf[8] = ssc->ssc_ancillary_id >> 8;
|
|
buf[9] = ssc->ssc_ancillary_id;
|
|
dlen = 10;
|
|
break;
|
|
case SCT_AC3:
|
|
buf[0] = DVB_DESC_LANGUAGE;
|
|
buf[1] = 4;
|
|
memcpy(&buf[2],ssc->ssc_lang,3);
|
|
buf[5] = 0; /* Main audio */
|
|
buf[6] = DVB_DESC_AC3;
|
|
buf[7] = 1;
|
|
buf[8] = 0; /* XXX: generate real AC3 desc */
|
|
dlen = 9;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
tlen += dlen;
|
|
buf += dlen;
|
|
|
|
buf1[0] = 0xf0 | (dlen >> 8);
|
|
buf1[1] = dlen;
|
|
}
|
|
|
|
l = tlen - 3 + 4;
|
|
|
|
buf0[1] = 0xb0 | (l >> 8);
|
|
buf0[2] = l;
|
|
|
|
return psi_append_crc32(buf0, tlen, maxlen);
|
|
}
|
|
|
|
|
|
|
|
static struct strtab caidnametab[] = {
|
|
{ "Seca", 0x0100 },
|
|
{ "CCETT", 0x0200 },
|
|
{ "Deutsche Telecom", 0x0300 },
|
|
{ "Eurodec", 0x0400 },
|
|
{ "Viaccess", 0x0500 },
|
|
{ "Irdeto", 0x0600 },
|
|
{ "Irdeto", 0x0602 },
|
|
{ "Irdeto", 0x0604 },
|
|
{ "Irdeto", 0x0624 },
|
|
{ "Irdeto", 0x0666 },
|
|
{ "Jerroldgi", 0x0700 },
|
|
{ "Matra", 0x0800 },
|
|
{ "NDS", 0x0900 },
|
|
{ "Nokia", 0x0A00 },
|
|
{ "Conax", 0x0B00 },
|
|
{ "NTL", 0x0C00 },
|
|
{ "CryptoWorks", 0x0D00 },
|
|
{ "CryptoWorks", 0x0D01 },
|
|
{ "CryptoWorks", 0x0D02 },
|
|
{ "CryptoWorks", 0x0D03 },
|
|
{ "CryptoWorks", 0x0D05 },
|
|
{ "CryptoWorks", 0x0D0F },
|
|
{ "CryptoWorks", 0x0D70 },
|
|
{ "CryptoWorks ICE", 0x0D96 },
|
|
{ "CryptoWorks ICE", 0x0D97 },
|
|
{ "PowerVu", 0x0E00 },
|
|
{ "Sony", 0x0F00 },
|
|
{ "Tandberg", 0x1000 },
|
|
{ "Thompson", 0x1100 },
|
|
{ "TV-Com", 0x1200 },
|
|
{ "HPT", 0x1300 },
|
|
{ "HRT", 0x1400 },
|
|
{ "IBM", 0x1500 },
|
|
{ "Nera", 0x1600 },
|
|
{ "BetaCrypt", 0x1700 },
|
|
{ "BetaCrypt", 0x1702 },
|
|
{ "BetaCrypt", 0x1722 },
|
|
{ "BetaCrypt", 0x1762 },
|
|
{ "NagraVision", 0x1800 },
|
|
{ "Titan", 0x1900 },
|
|
{ "Telefonica", 0x2000 },
|
|
{ "Stentor", 0x2100 },
|
|
{ "Tadiran Scopus", 0x2200 },
|
|
{ "BARCO AS", 0x2300 },
|
|
{ "StarGuide", 0x2400 },
|
|
{ "Mentor", 0x2500 },
|
|
{ "EBU", 0x2600 },
|
|
{ "GI", 0x4700 },
|
|
{ "Telemann", 0x4800 },
|
|
{ "DRECrypt", 0x4ae0 },
|
|
{ "DRECrypt2", 0x4ae1 },
|
|
{ "Bulcrypt", 0x4aee },
|
|
{ "Bulcrypt", 0x5581 },
|
|
};
|
|
|
|
const char *
|
|
psi_caid2name(uint16_t caid)
|
|
{
|
|
const char *s = val2str(caid, caidnametab);
|
|
static char buf[20];
|
|
|
|
if(s != NULL)
|
|
return s;
|
|
snprintf(buf, sizeof(buf), "0x%x", caid);
|
|
return buf;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
static struct strtab streamtypetab[] = {
|
|
{ "MPEG2VIDEO", SCT_MPEG2VIDEO },
|
|
{ "MPEG2AUDIO", SCT_MPEG2AUDIO },
|
|
{ "H264", SCT_H264 },
|
|
{ "AC3", SCT_AC3 },
|
|
{ "TELETEXT", SCT_TELETEXT },
|
|
{ "DVBSUB", SCT_DVBSUB },
|
|
{ "CA", SCT_CA },
|
|
{ "PMT", SCT_PMT },
|
|
{ "PAT", SCT_PAT },
|
|
{ "AAC", SCT_AAC },
|
|
{ "MPEGTS", SCT_MPEGTS },
|
|
{ "TEXTSUB", SCT_TEXTSUB },
|
|
{ "EAC3", SCT_EAC3 },
|
|
{ "AAC", SCT_MP4A },
|
|
};
|
|
|
|
|
|
/**
|
|
*
|
|
*/
|
|
const char *
|
|
streaming_component_type2txt(streaming_component_type_t s)
|
|
{
|
|
return val2str(s, streamtypetab) ?: "INVALID";
|
|
}
|
|
|
|
|
|
/**
|
|
* Store service settings into message
|
|
*/
|
|
void
|
|
psi_save_service_settings(htsmsg_t *m, service_t *t)
|
|
{
|
|
elementary_stream_t *st;
|
|
htsmsg_t *sub;
|
|
|
|
htsmsg_add_u32(m, "pcr", t->s_pcr_pid);
|
|
|
|
htsmsg_add_u32(m, "disabled", !t->s_enabled);
|
|
|
|
lock_assert(&t->s_stream_mutex);
|
|
|
|
TAILQ_FOREACH(st, &t->s_components, es_link) {
|
|
sub = htsmsg_create_map();
|
|
|
|
htsmsg_add_u32(sub, "pid", st->es_pid);
|
|
htsmsg_add_str(sub, "type", val2str(st->es_type, streamtypetab) ?: "?");
|
|
htsmsg_add_u32(sub, "position", st->es_position);
|
|
|
|
if(st->es_lang[0])
|
|
htsmsg_add_str(sub, "language", st->es_lang);
|
|
|
|
if(st->es_type == SCT_CA) {
|
|
|
|
caid_t *c;
|
|
htsmsg_t *v = htsmsg_create_list();
|
|
|
|
LIST_FOREACH(c, &st->es_caids, link) {
|
|
htsmsg_t *caid = htsmsg_create_map();
|
|
|
|
htsmsg_add_u32(caid, "caid", c->caid);
|
|
if(c->providerid)
|
|
htsmsg_add_u32(caid, "providerid", c->providerid);
|
|
htsmsg_add_msg(v, NULL, caid);
|
|
}
|
|
|
|
htsmsg_add_msg(sub, "caidlist", v);
|
|
}
|
|
|
|
if(st->es_type == SCT_DVBSUB) {
|
|
htsmsg_add_u32(sub, "compositionid", st->es_composition_id);
|
|
htsmsg_add_u32(sub, "ancillartyid", st->es_ancillary_id);
|
|
}
|
|
|
|
if(st->es_type == SCT_TEXTSUB)
|
|
htsmsg_add_u32(sub, "parentpid", st->es_parent_pid);
|
|
|
|
if(st->es_type == SCT_MPEG2VIDEO || st->es_type == SCT_H264) {
|
|
if(st->es_width && st->es_height) {
|
|
htsmsg_add_u32(sub, "width", st->es_width);
|
|
htsmsg_add_u32(sub, "height", st->es_height);
|
|
}
|
|
}
|
|
|
|
htsmsg_add_msg(m, "stream", sub);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
*/
|
|
static void
|
|
add_caid(elementary_stream_t *st, uint16_t caid, uint32_t providerid)
|
|
{
|
|
caid_t *c = malloc(sizeof(caid_t));
|
|
c->caid = caid;
|
|
c->providerid = providerid;
|
|
c->delete_me = 0;
|
|
LIST_INSERT_HEAD(&st->es_caids, c, link);
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
*/
|
|
static void
|
|
load_legacy_caid(htsmsg_t *c, elementary_stream_t *st)
|
|
{
|
|
uint32_t a, b;
|
|
const char *v;
|
|
|
|
if(htsmsg_get_u32(c, "caproviderid", &b))
|
|
b = 0;
|
|
|
|
if(htsmsg_get_u32(c, "caidnum", &a)) {
|
|
if((v = htsmsg_get_str(c, "caid")) != NULL) {
|
|
int i = str2val(v, caidnametab);
|
|
a = i < 0 ? strtol(v, NULL, 0) : i;
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
add_caid(st, a, b);
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
*/
|
|
static void
|
|
load_caid(htsmsg_t *m, elementary_stream_t *st)
|
|
{
|
|
htsmsg_field_t *f;
|
|
htsmsg_t *c, *v = htsmsg_get_list(m, "caidlist");
|
|
uint32_t a, b;
|
|
|
|
if(v == NULL)
|
|
return;
|
|
|
|
HTSMSG_FOREACH(f, v) {
|
|
if((c = htsmsg_get_map_by_field(f)) == NULL)
|
|
continue;
|
|
|
|
if(htsmsg_get_u32(c, "caid", &a))
|
|
continue;
|
|
|
|
if(htsmsg_get_u32(c, "providerid", &b))
|
|
b = 0;
|
|
|
|
add_caid(st, a, b);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Load service info from htsmsg
|
|
*/
|
|
void
|
|
psi_load_service_settings(htsmsg_t *m, service_t *t)
|
|
{
|
|
htsmsg_t *c;
|
|
htsmsg_field_t *f;
|
|
uint32_t u32, pid;
|
|
elementary_stream_t *st;
|
|
streaming_component_type_t type;
|
|
const char *v;
|
|
|
|
if(!htsmsg_get_u32(m, "pcr", &u32))
|
|
t->s_pcr_pid = u32;
|
|
|
|
if(!htsmsg_get_u32(m, "disabled", &u32))
|
|
t->s_enabled = !u32;
|
|
else
|
|
t->s_enabled = 1;
|
|
|
|
HTSMSG_FOREACH(f, m) {
|
|
if(strcmp(f->hmf_name, "stream"))
|
|
continue;
|
|
|
|
if((c = htsmsg_get_map_by_field(f)) == NULL)
|
|
continue;
|
|
|
|
if((v = htsmsg_get_str(c, "type")) == NULL)
|
|
continue;
|
|
|
|
type = str2val(v, streamtypetab);
|
|
if(type == -1)
|
|
continue;
|
|
|
|
if(htsmsg_get_u32(c, "pid", &pid))
|
|
continue;
|
|
|
|
st = service_stream_create(t, pid, type);
|
|
|
|
if((v = htsmsg_get_str(c, "language")) != NULL)
|
|
snprintf(st->es_lang, 4, "%s", v);
|
|
|
|
if(!htsmsg_get_u32(c, "position", &u32))
|
|
st->es_position = u32;
|
|
|
|
load_legacy_caid(c, st);
|
|
load_caid(c, st);
|
|
|
|
if(type == SCT_DVBSUB) {
|
|
if(!htsmsg_get_u32(c, "compositionid", &u32))
|
|
st->es_composition_id = u32;
|
|
|
|
if(!htsmsg_get_u32(c, "ancillartyid", &u32))
|
|
st->es_ancillary_id = u32;
|
|
}
|
|
|
|
if(type == SCT_TEXTSUB) {
|
|
if(!htsmsg_get_u32(c, "parentpid", &u32))
|
|
st->es_parent_pid = u32;
|
|
}
|
|
|
|
if(type == SCT_MPEG2VIDEO || type == SCT_H264) {
|
|
if(!htsmsg_get_u32(c, "width", &u32))
|
|
st->es_width = u32;
|
|
|
|
if(!htsmsg_get_u32(c, "height", &u32))
|
|
st->es_height = u32;
|
|
}
|
|
|
|
}
|
|
sort_pids(t);
|
|
}
|