capmt/cwc/mpegts: descrambler changes - register CA PIDs on demand

- capmt code was a bit restructured again
This commit is contained in:
Jaroslav Kysela 2014-06-06 11:39:30 +02:00
parent db59cf12a5
commit 8c4597022b
10 changed files with 382 additions and 159 deletions

View file

@ -26,6 +26,8 @@
struct service;
struct elementary_stream;
struct tvhcsa;
struct mpegts_table;
struct mpegts_mux;
/**
* Descrambler superclass
@ -45,13 +47,28 @@ typedef struct th_descrambler {
struct service *td_service;
struct tvhcsa *td_csa;
void (*td_table)(struct th_descrambler *d, struct elementary_stream *st,
const uint8_t *section, int section_len);
void (*td_stop)(struct th_descrambler *d);
void (*td_stop) (struct th_descrambler *d);
void (*td_caid_change)(struct th_descrambler *d);
} th_descrambler_t;
typedef void (*descrambler_section_callback_t)
(void *opaque, int pid, const uint8_t *section, int section_len);
/**
* Track required PIDs
*/
typedef struct descrambler_section {
TAILQ_ENTRY(descrambler_section) link;
descrambler_section_callback_t callback;
void *opaque;
} descrambler_section_t;
typedef struct descrambler_table {
TAILQ_ENTRY(descrambler_table) link;
struct mpegts_table *table;
TAILQ_HEAD(,descrambler_section) sections;
} descrambler_table_t;
/**
* List of CA ids
@ -87,11 +104,14 @@ void descrambler_init ( void );
void descrambler_done ( void );
void descrambler_service_start ( struct service *t );
void descrambler_service_stop ( struct service *t );
void descrambler_caid_changed ( struct service *t );
int descrambler_descramble ( struct service *t,
struct elementary_stream *st,
const uint8_t *tsb );
void descrambler_ca_section ( struct elementary_stream *st,
const uint8_t *data, size_t len );
int descrambler_open_pid ( struct mpegts_mux *mux, void *opaque, int pid,
descrambler_section_callback_t callback );
int descrambler_close_pid ( struct mpegts_mux *mux, void *opaque, int pid );
void descrambler_flush_tables ( struct mpegts_mux *mux );
const char *descrambler_caid2name( uint16_t caid );
uint16_t descrambler_name2caid ( const char *str );
card_type_t detect_card_type ( const uint16_t caid );

View file

@ -116,6 +116,7 @@ typedef struct dmx_sct_filter_params {
#define MAX_INDEX 64
#define MAX_FILTER 64
#define MAX_SOCKETS 16 // max sockets (simultaneous channels) per demux
#define MAX_PIDS 64 // max opened pids
/**
*
@ -215,6 +216,23 @@ typedef struct capmt_message {
sbuf_t cm_sb;
} capmt_message_t;
/**
*
*/
struct capmt;
typedef struct capmt_opaque {
struct capmt *capmt;
uint16_t adapter;
uint16_t pid;
} capmt_opaque_t;
typedef struct capmt_adapter {
ca_info_t ca_info[MAX_INDEX];
int ca_sock;
mpegts_input_t *ca_tuner;
capmt_opaque_t ca_pids[MAX_PIDS];
} capmt_adapter_t;
/**
*
*/
@ -244,7 +262,6 @@ typedef struct capmt {
int sids[MAX_SOCKETS];
int capmt_sock[MAX_SOCKETS];
int capmt_sock_reconnect[MAX_SOCKETS];
int capmt_sock_ca0[MAX_CA];
/* thread flags */
int capmt_connected;
@ -258,15 +275,16 @@ typedef struct capmt {
/* runtime status */
tvhpoll_t *capmt_poll;
th_pipe_t capmt_pipe;
mpegts_input_t *capmt_tuners[MAX_CA];
capmt_demuxes_t capmt_demuxes;
ca_info_t capmt_ca_info[MAX_CA][MAX_INDEX];
capmt_adapter_t capmt_adapters[MAX_CA];
TAILQ_HEAD(, capmt_message) capmt_writeq;
pthread_mutex_t capmt_mutex;
} capmt_t;
static void capmt_enumerate_services(capmt_t *capmt, int force);
static void capmt_send_request(capmt_service_t *ct, int lm);
static void capmt_table_input(void *opaque, int pid,
const uint8_t *data, int len);
/**
*
@ -325,6 +343,73 @@ capmt_poll_rem(capmt_t *capmt, int fd)
tvhpoll_rem(capmt->capmt_poll, &ev, 1);
}
static void
capmt_pid_add(capmt_t *capmt, int adapter, int pid)
{
capmt_adapter_t *ca = &capmt->capmt_adapters[adapter];
capmt_opaque_t *o = NULL, *t;
mpegts_mux_instance_t *mmi;
int i = 0;
for (i = 0; i < MAX_PIDS; i++) {
t = &ca->ca_pids[i];
if (t->pid == pid)
return;
if (t->pid == 0)
o = t;
}
if (o) {
o->capmt = capmt;
o->adapter = adapter;
o->pid = pid;
mmi = LIST_FIRST(&capmt->capmt_adapters[adapter].ca_tuner->mi_mux_active);
descrambler_open_pid(mmi->mmi_mux, o, pid, capmt_table_input);
}
}
static void
capmt_pid_remove(capmt_t *capmt, int adapter, int pid)
{
capmt_adapter_t *ca = &capmt->capmt_adapters[adapter];
mpegts_mux_instance_t *mmi;
int i = 0;
for (i = 0; i < MAX_PIDS; i++)
if (ca->ca_pids[i].pid == pid)
break;
if (i >= MAX_PIDS);
return;
mmi = LIST_FIRST(&capmt->capmt_adapters[adapter].ca_tuner->mi_mux_active);
descrambler_close_pid(mmi->mmi_mux, &ca->ca_pids[i], pid);
ca->ca_pids[i].pid = 0;
}
static void
capmt_pid_flush(capmt_t *capmt)
{
capmt_adapter_t *ca;
mpegts_mux_instance_t *mmi;
mpegts_input_t *tuner;
capmt_opaque_t *o;
int adapter, i;
for (adapter = 0; adapter < MAX_CA; adapter++) {
tuner = capmt->capmt_adapters[adapter].ca_tuner;
if (tuner == NULL)
continue;
ca = &capmt->capmt_adapters[adapter];
mmi = LIST_FIRST(&tuner->mi_mux_active);
for (i = 0; i < MAX_PIDS; i++) {
o = &ca->ca_pids[i];
if (o->pid) {
if (mmi)
descrambler_close_pid(mmi->mmi_mux, &ca->ca_pids[i], o->pid);
o->pid = 0;
}
}
}
}
/**
*
*/
@ -399,14 +484,16 @@ capmt_connect(capmt_t *capmt, int i)
*
*/
static void
capmt_socket_close(capmt_t *capmt, int i)
capmt_socket_close(capmt_t *capmt, int sock_idx)
{
int fd = capmt->capmt_sock[i];
int fd = capmt->capmt_sock[sock_idx];
if (fd < 0)
return;
capmt_poll_rem(capmt, fd);
close(fd);
capmt->capmt_sock[i] = -1;
capmt->capmt_sock[sock_idx] = -1;
if (capmt_oscam_new(capmt))
capmt_pid_flush(capmt);
}
/**
@ -505,7 +592,7 @@ capmt_queue_msg(capmt_t *capmt, int sid, const uint8_t *buf, size_t len)
sbuf_init_fixed(&msg->cm_sb, len);
sbuf_append(&msg->cm_sb, buf, len);
msg->cm_sid = sid;
msg->cm_sid = sid;
pthread_mutex_lock(&capmt->capmt_mutex);
TAILQ_INSERT_TAIL(&capmt->capmt_writeq, msg, cm_link);
pthread_mutex_unlock(&capmt->capmt_mutex);
@ -630,7 +717,7 @@ capmt_service_destroy(th_descrambler_t *td)
capmt_enumerate_services(ct->ct_capmt, 1);
if (LIST_EMPTY(&ct->ct_capmt->capmt_services)) {
ct->ct_capmt->capmt_tuners[ct->ct_adapter] = NULL;
ct->ct_capmt->capmt_adapters[ct->ct_adapter].ca_tuner = NULL;
memset(&ct->ct_capmt->capmt_demuxes, 0, sizeof(ct->ct_capmt->capmt_demuxes));
}
@ -639,7 +726,7 @@ capmt_service_destroy(th_descrambler_t *td)
}
static void
capmt_filter_data(capmt_t *capmt, int sid, uint8_t adapter, uint8_t demux_index,
capmt_filter_data(capmt_t *capmt, uint8_t adapter, uint8_t demux_index,
uint8_t filter_index, const uint8_t *data, int len)
{
uint8_t *buf = alloca(len + 6);
@ -649,7 +736,7 @@ capmt_filter_data(capmt_t *capmt, int sid, uint8_t adapter, uint8_t demux_index,
buf[5] = filter_index;
memcpy(buf + 6, data, len);
if (len - 3 == ((((uint16_t)buf[7] << 8) | buf[8]) & 0xfff))
capmt_queue_msg(capmt, sid, buf, len + 6);
capmt_queue_msg(capmt, 0, buf, len + 6);
}
static void
@ -657,7 +744,7 @@ capmt_set_filter(capmt_t *capmt, int adapter, sbuf_t *sb, int offset)
{
uint8_t demux_index = sbuf_peek_u8 (sb, offset + 4);
uint8_t filter_index = sbuf_peek_u8 (sb, offset + 5);
int pid = sbuf_peek_s32(sb, offset + 6);
uint16_t pid = sbuf_peek_u16(sb, offset + 6);
dmx_filter_params_t *filter;
dmx_filter_params_t *params = (dmx_filter_params_t *)sbuf_peek(sb, offset + 6);
capmt_filters_t *cf;
@ -666,7 +753,8 @@ capmt_set_filter(capmt_t *capmt, int adapter, sbuf_t *sb, int offset)
adapter, demux_index, filter_index, pid);
if (adapter >= MAX_CA ||
demux_index >= MAX_INDEX ||
filter_index >= MAX_FILTER)
filter_index >= MAX_FILTER ||
(pid < 0 || pid > 8191))
return;
cf = &capmt->capmt_demuxes.filters[demux_index];
if (cf->max && cf->adapter != adapter)
@ -674,6 +762,7 @@ capmt_set_filter(capmt_t *capmt, int adapter, sbuf_t *sb, int offset)
cf->adapter = adapter;
filter = &cf->dmx[filter_index];
filter->pid = pid;
capmt_pid_add(capmt, adapter, pid);
memcpy(&filter->filter, &params->filter, sizeof(params->filter));
filter->timeout = filter->flags = 0;
if (capmt->capmt_demuxes.max <= demux_index)
@ -707,6 +796,7 @@ capmt_stop_filter(capmt_t *capmt, int adapter, sbuf_t *sb, int offset)
if (filter->pid != pid)
return;
memset(filter, 0, sizeof(*filter));
capmt_pid_remove(capmt, adapter, pid);
/* short the max values */
filter_index = cf->max - 1;
while (filter_index != 255 && cf->dmx[filter_index].pid == 0)
@ -733,6 +823,28 @@ capmt_notify_server(capmt_t *capmt, capmt_service_t *ct)
}
}
static void
capmt_abort(capmt_t *capmt, int keystate)
{
mpegts_service_t *t;
capmt_service_t *ct;
pthread_mutex_lock(&global_lock);
LIST_FOREACH(ct, &capmt->capmt_services, ct_link) {
t = (mpegts_service_t *)ct->td_service;
if (ct->td_keystate != keystate) {
tvhlog(LOG_ERR, "capmt",
"Can not descramble service \"%s\", %s",
t->s_dvb_svcname,
keystate == DS_FORBIDDEN ?
"access denied" : "connection close");
ct->td_keystate = keystate;
}
}
pthread_mutex_unlock(&global_lock);
}
static void
capmt_process_key(capmt_t *capmt, uint8_t adapter, uint16_t seq,
const uint8_t *even, const uint8_t *odd,
@ -785,6 +897,7 @@ static int
capmt_msg_size(capmt_t *capmt, sbuf_t *sb, int offset)
{
uint32_t cmd;
int oscam_new = capmt_oscam_new(capmt);
if (sb->sb_ptr - offset < 4)
return 0;
@ -803,9 +916,9 @@ capmt_msg_size(capmt_t *capmt, sbuf_t *sb, int offset)
return 4 + 8;
else if (cmd == CA_SET_DESCR)
return 4 + 16;
else if (cmd == DMX_SET_FILTER)
else if (oscam_new && cmd == DMX_SET_FILTER)
return 4 + 2 + sizeof(dmx_filter_params_t);
else if (cmd == DMX_STOP)
else if (oscam_new && cmd == DMX_STOP)
return 4 + 4;
else {
sb->sb_err = 0;
@ -826,9 +939,10 @@ capmt_analyze_cmd(capmt_t *capmt, int adapter, sbuf_t *sb, int offset)
int32_t index = sbuf_peek_s32(sb, offset + 8);
tvhlog(LOG_DEBUG, "capmt", "CA_SET_PID adapter %d index %d pid 0x%04x", adapter, index, pid);
if (adapter < MAX_CA && index >= 0 && index < MAX_INDEX) {
capmt->capmt_ca_info[adapter][index].pid = pid;
capmt->capmt_adapters[adapter].ca_info[index].pid = pid;
} else if (index < 0) {
memset(&capmt->capmt_ca_info[adapter], 0, sizeof(capmt->capmt_ca_info[adapter]));
memset(&capmt->capmt_adapters[adapter].ca_info, 0,
sizeof(capmt->capmt_adapters[adapter].ca_info));
} else
tvhlog(LOG_ERR, "capmt", "Invalid index %d in CA_SET_PID (%d) for adapter %d", index, MAX_INDEX, adapter);
@ -844,7 +958,7 @@ capmt_analyze_cmd(capmt_t *capmt, int adapter, sbuf_t *sb, int offset)
return;
if (adapter >= MAX_CA || index >= MAX_INDEX)
return;
cai = &capmt->capmt_ca_info[adapter][index];
cai = &capmt->capmt_adapters[adapter].ca_info[index];
if (parity == 0) {
memcpy(cai->even, cw, 8); // even key
capmt_process_key(capmt, adapter, cai->pid, cai->even, cai->odd, 1);
@ -909,8 +1023,8 @@ handle_ca0(capmt_t *capmt) {
capmt->capmt_poll = tvhpoll_create(MAX_CA + 1);
capmt_poll_add(capmt, capmt->capmt_pipe.rd, 0);
for (i = 0; i < MAX_CA; i++)
if (capmt->capmt_sock_ca0[i])
capmt_poll_add(capmt, capmt->capmt_sock_ca0[i], i + 1);
if (capmt->capmt_adapters[i].ca_sock)
capmt_poll_add(capmt, capmt->capmt_adapters[i].ca_sock, i + 1);
i = 0;
adapter = -1;
@ -941,7 +1055,7 @@ handle_ca0(capmt_t *capmt) {
if (adapter >= MAX_CA)
continue;
recvsock = capmt->capmt_sock_ca0[adapter];
recvsock = capmt->capmt_adapters[adapter].ca_sock;
if (recvsock <= 0)
continue;
@ -953,7 +1067,7 @@ handle_ca0(capmt_t *capmt) {
close(recvsock);
capmt_poll_rem(capmt, recvsock);
capmt->capmt_sock_ca0[adapter] = -1;
capmt->capmt_adapters[adapter].ca_sock = -1;
continue;
}
@ -1013,14 +1127,11 @@ handle_single(capmt_t *capmt)
while (capmt->capmt_running) {
tvhtrace("capmt", "poll");
nfds = tvhpoll_wait(capmt->capmt_poll, &ev, 1, 500);
if (nfds <= 0)
continue;
tvhtrace("capmt", "poll ok for %i", ev.data.u32);
if (ev.data.u32 == 0) {
ret = read(capmt->capmt_pipe.rd, buf, 1);
if (ret == 1 && buf[0] == 'c') {
@ -1082,7 +1193,6 @@ handle_single(capmt_t *capmt)
tvhpoll_destroy(capmt->capmt_poll);
capmt->capmt_poll = NULL;
}
#if CONFIG_LINUXDVB
static void
handle_ca0_wrapper(capmt_t *capmt)
@ -1098,8 +1208,11 @@ handle_ca0_wrapper(capmt_t *capmt)
while (capmt->capmt_running) {
if (capmt->capmt_sock[0] < 0)
break;
/* receiving data from UDP socket */
ret = recv(capmt->capmt_sock_ca0[0], buffer, 18, MSG_WAITALL);
ret = recv(capmt->capmt_adapters[0].ca_sock, buffer, 18, MSG_WAITALL);
if (ret < 0) {
tvhlog(LOG_ERR, "capmt", "error receiving over socket");
@ -1110,7 +1223,7 @@ handle_ca0_wrapper(capmt_t *capmt)
break;
} else {
tvhtrace("capmt", "Received message from socket %i", capmt->capmt_sock_ca0[0]);
tvhtrace("capmt", "Received message from socket %i", capmt->capmt_adapters[0].ca_sock);
tvhlog_hexdump("capmt", buffer, ret);
capmt_process_key(capmt, 0,
@ -1120,6 +1233,7 @@ handle_ca0_wrapper(capmt_t *capmt)
}
}
capmt_abort(capmt, DS_UNKNOWN);
tvhlog(LOG_INFO, "capmt", "connection from client closed ...");
}
#endif
@ -1158,7 +1272,7 @@ capmt_thread(void *aux)
while (capmt->capmt_running) {
fatal = 0;
for (i = 0; i < MAX_CA; i++)
capmt->capmt_sock_ca0[i] = -1;
capmt->capmt_adapters[i].ca_sock = -1;
for (i = 0; i < MAX_SOCKETS; i++) {
capmt->sids[i] = 0;
capmt->capmt_sock[i] = -1;
@ -1191,7 +1305,8 @@ capmt_thread(void *aux)
int bind_ok = 0;
/* open connection to emulated ca0 device */
if (capmt->capmt_oscam == CAPMT_OSCAM_SO_WRAPPER) {
bind_ok = capmt_create_udp_socket(&capmt->capmt_sock_ca0[0], capmt->capmt_port);
bind_ok = capmt_create_udp_socket(&capmt->capmt_adapters[0].ca_sock,
capmt->capmt_port);
if (bind_ok)
handle_ca0_wrapper(capmt);
} else {
@ -1208,7 +1323,8 @@ capmt_thread(void *aux)
continue;
}
tvhlog(LOG_INFO, "capmt", "created UDP socket %d", n);
bind_ok = capmt_create_udp_socket(&capmt->capmt_sock_ca0[n], capmt->capmt_port + n);
bind_ok = capmt_create_udp_socket(&capmt->capmt_adapters[n].ca_sock,
capmt->capmt_port + n);
}
if (bind_ok)
handle_ca0(capmt);
@ -1231,11 +1347,10 @@ capmt_thread(void *aux)
/* close opened sockets */
for (i = 0; i < MAX_SOCKETS; i++)
if (capmt->capmt_sock[i] >= 0)
close(capmt->capmt_sock[i]);
capmt_socket_close(capmt, i);
for (i = 0; i < MAX_CA; i++)
if (capmt->capmt_sock_ca0[i] >= 0)
close(capmt->capmt_sock_ca0[i]);
if (capmt->capmt_adapters[i].ca_sock >= 0)
close(capmt->capmt_adapters[i].ca_sock);
if (!capmt->capmt_running)
break;
@ -1268,30 +1383,23 @@ capmt_thread(void *aux)
*
*/
static void
capmt_table_input(struct th_descrambler *td,
struct elementary_stream *st, const uint8_t *data, int len)
capmt_table_input(void *opaque, int pid, const uint8_t *data, int len)
{
capmt_service_t *ct = (capmt_service_t *)td;
capmt_t *capmt = ct->ct_capmt;
mpegts_service_t *t = (mpegts_service_t*)td->td_service;
capmt_caid_ecm_t *cce;
int i, change, demux_index, filter_index;
capmt_opaque_t *o = opaque;
capmt_t *capmt = o->capmt;
int i, demux_index, filter_index;
capmt_filters_t *cf;
dmx_filter_t *f;
caid_t *c;
/* Validate */
if (!idnode_is_instance(&td->td_service->s_id, &mpegts_service_class))
return;
if (!t->s_dvb_active_input) return;
if (len > 4096) return;
for (demux_index = 0; demux_index < capmt->capmt_demuxes.max; demux_index++) {
cf = &capmt->capmt_demuxes.filters[demux_index];
if (cf->adapter != ct->ct_adapter)
if (cf->adapter != o->adapter)
continue;
for (filter_index = 0; filter_index < cf->max; filter_index++)
if (cf->dmx[filter_index].pid == st->es_pid) {
if (cf->dmx[filter_index].pid == pid) {
f = &cf->dmx[filter_index].filter;
for (i = 0; i < DMX_FILTER_SIZE && i < len; i++) {
if (f->mode[i] != 0)
@ -1300,40 +1408,46 @@ capmt_table_input(struct th_descrambler *td,
break;
}
if (i >= DMX_FILTER_SIZE && i <= len) {
capmt_filter_data(capmt, t->s_dvb_service_id,
ct->ct_adapter, demux_index,
capmt_filter_data(capmt,
o->adapter, demux_index,
filter_index, data, len);
}
}
}
/* Add new caids, no ecm management */
if (data[0] != 0x80 && data[0] != 0x81) return; /* ignore EMM */
change = 0;
LIST_FOREACH(c, &st->es_caids, link) {
/* search ecmpid in list */
LIST_FOREACH(cce, &ct->ct_caid_ecm, cce_link)
if (cce->cce_caid == c->caid)
break;
if (cce)
continue;
}
tvhlog(LOG_DEBUG, "capmt",
"New caid 0x%04X for service \"%s\"", c->caid, t->s_dvb_svcname);
static void
capmt_caid_change(th_descrambler_t *td)
{
capmt_service_t *ct = (capmt_service_t *)td;
capmt_t *capmt = ct->ct_capmt;
mpegts_service_t *t = (mpegts_service_t*)td->td_service;
elementary_stream_t *st;
capmt_caid_ecm_t *cce;
caid_t *c;
int change = 0;
/* ecmpid not already seen, add it to list */
cce = calloc(1, sizeof(capmt_caid_ecm_t));
cce->cce_caid = c->caid;
cce->cce_ecmpid = st->es_pid;
cce->cce_providerid = c->providerid;
LIST_INSERT_HEAD(&ct->ct_caid_ecm, cce, cce_link);
change = 1;
}
TAILQ_FOREACH(st, &t->s_components, es_link) {
LIST_FOREACH(c, &st->es_caids, link) {
/* search ecmpid in list */
LIST_FOREACH(cce, &ct->ct_caid_ecm, cce_link)
if (cce->cce_caid == c->caid && cce->cce_providerid == c->providerid)
break;
if (cce)
continue;
tvhlog(LOG_DEBUG, "capmt",
"New caid 0x%04X:0x%06X for service \"%s\"",
c->caid, c->providerid, t->s_dvb_svcname);
if (capmt->capmt_oscam == CAPMT_OSCAM_SO_WRAPPER && capmt->capmt_sock[0] == 0) {
/* New key, but we are not connected (anymore), can not descramble */
ct->td_keystate = DS_UNKNOWN;
return;
/* ecmpid not already seen, add it to list */
cce = calloc(1, sizeof(capmt_caid_ecm_t));
cce->cce_caid = c->caid;
cce->cce_ecmpid = st->es_pid;
cce->cce_providerid = c->providerid;
LIST_INSERT_HEAD(&ct->ct_caid_ecm, cce, cce_link);
change = 1;
}
}
if (change)
@ -1553,9 +1667,9 @@ capmt_service_start(service_t *s)
if (tuner < 0) {
for (i = 0; i < MAX_CA; i++) {
if (capmt->capmt_tuners[i] == NULL && tuner < 0)
if (capmt->capmt_adapters[i].ca_tuner == NULL && tuner < 0)
tuner = i;
if (capmt->capmt_tuners[i] == t->s_dvb_active_input) {
if (capmt->capmt_adapters[i].ca_tuner == t->s_dvb_active_input) {
tuner = i;
break;
}
@ -1572,7 +1686,7 @@ capmt_service_start(service_t *s)
"Starting CAPMT server for service \"%s\" on adapter %d",
t->s_dvb_svcname, tuner);
capmt->capmt_tuners[tuner] = t->s_dvb_active_input;
capmt->capmt_adapters[tuner].ca_tuner = t->s_dvb_active_input;
/* create new capmt service */
ct = calloc(1, sizeof(capmt_service_t));
@ -1590,8 +1704,6 @@ capmt_service_start(service_t *s)
tvhlog(LOG_DEBUG, "capmt",
"New caid 0x%04X for service \"%s\"", c->caid, t->s_dvb_svcname);
mpegts_table_register_caid(((mpegts_service_t *)s)->s_dvb_mux, c->caid);
/* add it to list */
cce = calloc(1, sizeof(capmt_caid_ecm_t));
cce->cce_caid = c->caid;
@ -1604,9 +1716,9 @@ capmt_service_start(service_t *s)
td = (th_descrambler_t *)ct;
tvhcsa_init(td->td_csa = &ct->ct_csa);
td->td_service = s;
td->td_stop = capmt_service_destroy;
td->td_table = capmt_table_input;
td->td_service = s;
td->td_stop = capmt_service_destroy;
td->td_caid_change = capmt_caid_change;
LIST_INSERT_HEAD(&t->s_descramblers, td, td_service_link);
LIST_INSERT_HEAD(&capmt->capmt_services, ct, ct_link);

View file

@ -120,6 +120,17 @@ typedef struct ecm_pid {
} ecm_pid_t;
/**
*
*/
struct cwc_service;
typedef struct cwc_opaque {
struct cwc_service *service;
elementary_stream_t *estream;
mpegts_mux_t *mux;
} cwc_opaque_t;
/**
*
*/
@ -131,6 +142,8 @@ typedef struct cwc_service {
LIST_ENTRY(cwc_service) cs_link;
int cs_channel;
int cs_pid;
cwc_opaque_t cs_opaque;
/**
* ECM Status
@ -1594,11 +1607,12 @@ cwc_emm_viaccess(cwc_t *cwc, struct cs_card_data *pcard, uint8_t *data, int mlen
* t->s_streaming_mutex is held
*/
static void
cwc_table_input(struct th_descrambler *td,
struct elementary_stream *st, const uint8_t *data, int len)
cwc_table_input(void *opaque, int pid, const uint8_t *data, int len)
{
cwc_service_t *ct = (cwc_service_t *)td;
mpegts_service_t *t = (mpegts_service_t*)td->td_service;
cwc_opaque_t *o = opaque;
elementary_stream_t *st = o->estream;
cwc_service_t *ct = o->service;
mpegts_service_t *t = (mpegts_service_t*)ct->td_service;
uint16_t sid = t->s_dvb_service_id;
cwc_t *cwc = ct->cs_cwc;
int channel;
@ -1618,10 +1632,11 @@ cwc_table_input(struct th_descrambler *td,
return;
LIST_FOREACH(ep, &ct->cs_pids, ep_link) {
if(ep->ep_pid == st->es_pid)
if(ep->ep_pid == pid)
break;
}
pthread_mutex_lock(&t->s_stream_mutex);
if(ep == NULL) {
if (ct->ecm_state == ECM_RESET) {
ct->ecm_state = ECM_INIT;
@ -1641,7 +1656,7 @@ cwc_table_input(struct th_descrambler *td,
}
}
if(t->s_dvb_prefcapid == st->es_pid) {
if(t->s_dvb_prefcapid == pid) {
ep = calloc(1, sizeof(ecm_pid_t));
ep->ep_pid = t->s_dvb_prefcapid;
LIST_INSERT_HEAD(&ct->cs_pids, ep, ep_link);
@ -1649,12 +1664,13 @@ cwc_table_input(struct th_descrambler *td,
}
else if(t->s_dvb_prefcapid == 0) {
ep = calloc(1, sizeof(ecm_pid_t));
ep->ep_pid = st->es_pid;
ep->ep_pid = pid;
LIST_INSERT_HEAD(&ct->cs_pids, ep, ep_link);
tvhlog(LOG_DEBUG, "cwc", "Insert new ECM (PID %d) for service \"%s\"", st->es_pid, t->s_dvb_svcname);
tvhlog(LOG_DEBUG, "cwc", "Insert new ECM (PID %d) for service \"%s\"", pid, t->s_dvb_svcname);
}
}
}
pthread_mutex_unlock(&t->s_stream_mutex);
if(ep == NULL)
return;
@ -1688,7 +1704,7 @@ cwc_table_input(struct th_descrambler *td,
section = 0;
}
channel = st->es_pid;
channel = pid;
snprintf(chaninfo, sizeof(chaninfo), " (PID %d)", channel);
if(ep->ep_sections[section] == NULL)
@ -1901,6 +1917,8 @@ cwc_service_destroy(th_descrambler_t *td)
ecm_pid_t *ep;
int i;
descrambler_close_pid(ct->cs_opaque.mux, &ct->cs_opaque, ct->cs_pid);
while((ep = LIST_FIRST(&ct->cs_pids)) != NULL) {
for(i = 0; i < 256; i++)
free(ep->ep_sections[i]);
@ -1916,25 +1934,6 @@ cwc_service_destroy(th_descrambler_t *td)
free(ct);
}
/**
*
*/
static inline elementary_stream_t *
cwc_find_stream_by_caid(service_t *t, const short caid)
{
elementary_stream_t *st;
caid_t *c;
TAILQ_FOREACH(st, &t->s_components, es_link) {
LIST_FOREACH(c, &st->es_caids, link) {
if(c->caid == caid)
return st;
}
}
return NULL;
}
/**
* Check if our CAID's matches, and if so, link
*
@ -1946,6 +1945,8 @@ cwc_service_start(service_t *t)
cwc_t *cwc;
cwc_service_t *ct;
th_descrambler_t *td;
elementary_stream_t *st;
caid_t *c;
struct cs_card_data *pcard;
extern const idclass_t mpegts_service_class;
@ -1958,9 +1959,16 @@ cwc_service_start(service_t *t)
if (ct->td_service == t && ct->cs_cwc == cwc)
break;
}
LIST_FOREACH(pcard,&cwc->cwc_cards, cs_card) {
if((pcard->cwc_caid != 0) && cwc_find_stream_by_caid(t, pcard->cwc_caid))
break;
LIST_FOREACH(pcard, &cwc->cwc_cards, cs_card) {
if (pcard->cwc_caid == 0) continue;
TAILQ_FOREACH(st, &t->s_components, es_link) {
LIST_FOREACH(c, &st->es_caids, link) {
if (c->caid == pcard->cwc_caid)
break;
}
if (c) break;
}
if (st) break;
}
if (!pcard) {
if (ct) cwc_service_destroy((th_descrambler_t*)ct);
@ -1969,22 +1977,26 @@ cwc_service_start(service_t *t)
if (ct) continue;
mpegts_table_register_caid(((mpegts_service_t *)t)->s_dvb_mux, pcard->cwc_caid);
ct = calloc(1, sizeof(cwc_service_t));
ct->cs_cwc = cwc;
ct->cs_channel = -1;
ct->cs_pid = st->es_pid;
ct->cs_opaque.service = ct;
ct->cs_opaque.estream = st;
ct->cs_opaque.mux = ((mpegts_service_t *)t)->s_dvb_mux;
ct->ecm_state = ECM_INIT;
td = (th_descrambler_t *)ct;
tvhcsa_init(td->td_csa = &ct->cs_csa);
td->td_service = t;
td->td_stop = cwc_service_destroy;
td->td_table = cwc_table_input;
LIST_INSERT_HEAD(&t->s_descramblers, td, td_service_link);
LIST_INSERT_HEAD(&cwc->cwc_services, ct, cs_link);
descrambler_open_pid(ct->cs_opaque.mux, &ct->cs_opaque,
ct->cs_pid, cwc_table_input);
tvhlog(LOG_DEBUG, "cwc", "%s using CWC %s:%d",
service_nicename(t), cwc->cwc_hostname, cwc->cwc_port);

View file

@ -20,7 +20,7 @@
#include "cwc.h"
#include "capmt.h"
#include "ffdecsa/FFdecsa.h"
#include "service.h"
#include "input.h"
#include "tvhcsa.h"
static struct strtab caidnametab[] = {
@ -149,6 +149,17 @@ descrambler_service_stop ( service_t *t )
td->td_stop(td);
}
void
descrambler_caid_changed ( service_t *t )
{
th_descrambler_t *td;
LIST_FOREACH(td, &t->s_descramblers, td_service_link) {
if (td->td_caid_change)
td->td_caid_change(td);
}
}
int
descrambler_descramble ( service_t *t,
elementary_stream_t *st,
@ -173,14 +184,98 @@ descrambler_descramble ( service_t *t,
return count == failed ? -1 : 0;
}
void
descrambler_ca_section( elementary_stream_t *st,
const uint8_t *data, size_t len )
static int
descrambler_table_callback
(mpegts_table_t *mt, const uint8_t *ptr, int len, int tableid)
{
th_descrambler_t *td;
descrambler_table_t *dt = mt->mt_opaque;
descrambler_section_t *ds;
LIST_FOREACH(td, &st->es_service->s_descramblers, td_service_link)
td->td_table(td, st, data, len);
pthread_mutex_lock(&mt->mt_mux->mm_descrambler_lock);
TAILQ_FOREACH(ds, &dt->sections, link)
ds->callback(ds->opaque, mt->mt_pid, ptr, len);
pthread_mutex_unlock(&mt->mt_mux->mm_descrambler_lock);
return 0;
}
int
descrambler_open_pid( mpegts_mux_t *mux, void *opaque, int pid,
descrambler_section_callback_t callback )
{
descrambler_table_t *dt;
descrambler_section_t *ds;
pthread_mutex_lock(&mux->mm_descrambler_lock);
TAILQ_FOREACH(dt, &mux->mm_descrambler_tables, link) {
if (dt->table->mt_pid == pid) {
TAILQ_FOREACH(ds, &dt->sections, link) {
if (ds->opaque == opaque) {
pthread_mutex_unlock(&mux->mm_descrambler_lock);
return 0;
}
}
}
}
if (!dt) {
dt = calloc(1, sizeof(*dt));
TAILQ_INIT(&dt->sections);
dt->table = mpegts_table_add(mux, 0, 0, descrambler_table_callback,
dt, "descrambler", MT_FULL, pid);
TAILQ_INSERT_TAIL(&mux->mm_descrambler_tables, dt, link);
}
ds = calloc(1, sizeof(*ds));
ds->callback = callback;
ds->opaque = opaque;
TAILQ_INSERT_TAIL(&dt->sections, ds, link);
pthread_mutex_unlock(&mux->mm_descrambler_lock);
return 1;
}
int
descrambler_close_pid( mpegts_mux_t *mux, void *opaque, int pid )
{
descrambler_table_t *dt;
descrambler_section_t *ds;
pthread_mutex_lock(&mux->mm_descrambler_lock);
TAILQ_FOREACH(dt, &mux->mm_descrambler_tables, link) {
if (dt->table->mt_pid == pid) {
TAILQ_FOREACH(ds, &dt->sections, link) {
if (ds->opaque == opaque) {
TAILQ_REMOVE(&dt->sections, ds, link);
free(ds);
if (TAILQ_FIRST(&dt->sections) == NULL) {
TAILQ_REMOVE(&mux->mm_descrambler_tables, dt, link);
mpegts_table_destroy(dt->table);
free(dt);
}
pthread_mutex_unlock(&mux->mm_descrambler_lock);
return 1;
}
}
}
}
pthread_mutex_unlock(&mux->mm_descrambler_lock);
return 0;
}
void
descrambler_flush_tables( mpegts_mux_t *mux )
{
descrambler_table_t *dt;
descrambler_section_t *ds;
pthread_mutex_lock(&mux->mm_descrambler_lock);
while ((dt = TAILQ_FIRST(&mux->mm_descrambler_tables)) != NULL) {
while ((ds = TAILQ_FIRST(&dt->sections)) != NULL) {
TAILQ_REMOVE(&dt->sections, ds, link);
free(ds);
}
TAILQ_REMOVE(&mux->mm_descrambler_tables, dt, link);
mpegts_table_destroy(dt->table);
free(dt);
}
pthread_mutex_unlock(&mux->mm_descrambler_lock);
}
// TODO: might actually put const char* into caid_t

View file

@ -347,6 +347,9 @@ struct mpegts_mux
LIST_HEAD(, mpegts_table) mm_tables;
TAILQ_HEAD(, mpegts_table) mm_table_queue;
TAILQ_HEAD(, descrambler_table) mm_descrambler_tables;
pthread_mutex_t mm_descrambler_lock;
/*
* Functions
*/
@ -729,7 +732,6 @@ mpegts_table_t *mpegts_table_add
void mpegts_table_flush_all
(mpegts_mux_t *mm);
void mpegts_table_destroy ( mpegts_table_t *mt );
void mpegts_table_register_caid ( mpegts_mux_t *mm, uint16_t caid );
mpegts_service_t *mpegts_service_create0
( mpegts_service_t *ms, const idclass_t *class, const char *uuid,

View file

@ -1561,6 +1561,10 @@ psi_parse_pmt
if(t->s_status == SERVICE_RUNNING)
ret = 1;
}
// notify descrambler that we found another CAIDs
if (update & PMT_UPDATE_NEW_CAID)
descrambler_caid_changed((service_t *)t);
}
return ret;
}

View file

@ -314,8 +314,10 @@ mpegts_input_open_service ( mpegts_input_t *mi, mpegts_service_t *s, int init )
pthread_mutex_lock(&s->s_stream_mutex);
mi->mi_open_pid(mi, s->s_dvb_mux, s->s_pmt_pid, MPS_STREAM, s);
mi->mi_open_pid(mi, s->s_dvb_mux, s->s_pcr_pid, MPS_STREAM, s);
TAILQ_FOREACH(st, &s->s_components, es_link)
mi->mi_open_pid(mi, s->s_dvb_mux, st->es_pid, MPS_STREAM, s);
TAILQ_FOREACH(st, &s->s_components, es_link) {
if (st->es_type != SCT_CA)
mi->mi_open_pid(mi, s->s_dvb_mux, st->es_pid, MPS_STREAM, s);
}
pthread_mutex_unlock(&s->s_stream_mutex);
pthread_mutex_unlock(&mi->mi_output_lock);

View file

@ -769,6 +769,8 @@ mpegts_mux_create0
mm->mm_open_table = mpegts_mux_open_table;
mm->mm_close_table = mpegts_mux_close_table;
TAILQ_INIT(&mm->mm_table_queue);
TAILQ_INIT(&mm->mm_descrambler_tables);
pthread_mutex_init(&mm->mm_descrambler_lock, NULL);
mm->mm_last_pid = -1;

View file

@ -200,20 +200,11 @@ void
mpegts_table_flush_all ( mpegts_mux_t *mm )
{
mpegts_table_t *mt;
descrambler_flush_tables(mm);
while ((mt = LIST_FIRST(&mm->mm_tables)))
mpegts_table_destroy(mt);
}
/**
* Register wanted CAID
*/
void
mpegts_table_register_caid ( mpegts_mux_t *mm, uint16_t caid )
{
uintptr_t ca = caid;
mpegts_table_add(mm, 0, 0, NULL, (void *)ca, "ca", MT_FULL, -1);
}
/*
* Section assembly
*/

View file

@ -44,17 +44,6 @@
static void ts_remux(mpegts_service_t *t, const uint8_t *tsb);
/**
* Code for dealing with a complete section
*/
static void
got_ca_section(const uint8_t *data, size_t len, void *opaque)
{
elementary_stream_t *st = opaque;
assert(st->es_service->s_source_type == S_MPEG_TS);
descrambler_ca_section(st, data, len);
}
/**
* Continue processing of transport stream packets
*/
@ -62,7 +51,7 @@ static void
ts_recv_packet0
(mpegts_service_t *t, elementary_stream_t *st, const uint8_t *tsb)
{
int off, pusi, cc, error, ccerr;
int off, pusi, cc, error;
service_set_streaming_status_flags((service_t*)t, TSS_MUX_PACKETS);
@ -72,7 +61,6 @@ ts_recv_packet0
if (!st)
return;
ccerr = 0;
error = !!(tsb[1] & 0x80);
pusi = !!(tsb[1] & 0x40);
@ -81,7 +69,6 @@ ts_recv_packet0
if(tsb[3] & 0x10) {
cc = tsb[3] & 0xf;
if(st->es_cc != -1 && cc != st->es_cc) {
ccerr = 1;
/* Incorrect CC */
limitedlog(&st->es_loglimit_cc, "TS", service_component_nicename(st),
"Continuity counter error");
@ -100,10 +87,6 @@ ts_recv_packet0
switch(st->es_type) {
case SCT_CA:
if(st->es_section == NULL)
st->es_section = calloc(1, sizeof(mpegts_psi_section_t));
mpegts_psi_section_reassemble(st->es_section, tsb, 0, ccerr,
got_ca_section, st);
break;
default: