diff --git a/src/descrambler.h b/src/descrambler.h index 921f7e1e..758c1dc1 100755 --- a/src/descrambler.h +++ b/src/descrambler.h @@ -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 ); diff --git a/src/descrambler/capmt.c b/src/descrambler/capmt.c index e0ce57ed..8b05132f 100644 --- a/src/descrambler/capmt.c +++ b/src/descrambler/capmt.c @@ -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, ¶ms->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); diff --git a/src/descrambler/cwc.c b/src/descrambler/cwc.c index 2545cad4..3a1534fb 100755 --- a/src/descrambler/cwc.c +++ b/src/descrambler/cwc.c @@ -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); diff --git a/src/descrambler/descrambler.c b/src/descrambler/descrambler.c index 83a9a725..6cd9bb4b 100755 --- a/src/descrambler/descrambler.c +++ b/src/descrambler/descrambler.c @@ -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 diff --git a/src/input/mpegts.h b/src/input/mpegts.h index 086f972e..a20ab9db 100644 --- a/src/input/mpegts.h +++ b/src/input/mpegts.h @@ -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, diff --git a/src/input/mpegts/dvb_psi.c b/src/input/mpegts/dvb_psi.c index 21521d73..41878d2d 100644 --- a/src/input/mpegts/dvb_psi.c +++ b/src/input/mpegts/dvb_psi.c @@ -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; } diff --git a/src/input/mpegts/mpegts_input.c b/src/input/mpegts/mpegts_input.c index 456bdaab..d42e6175 100644 --- a/src/input/mpegts/mpegts_input.c +++ b/src/input/mpegts/mpegts_input.c @@ -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); diff --git a/src/input/mpegts/mpegts_mux.c b/src/input/mpegts/mpegts_mux.c index c7fb2f09..cfdbe4cd 100644 --- a/src/input/mpegts/mpegts_mux.c +++ b/src/input/mpegts/mpegts_mux.c @@ -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; diff --git a/src/input/mpegts/mpegts_table.c b/src/input/mpegts/mpegts_table.c index 773e0d09..6f1f11b7 100644 --- a/src/input/mpegts/mpegts_table.c +++ b/src/input/mpegts/mpegts_table.c @@ -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 */ diff --git a/src/input/mpegts/tsdemux.c b/src/input/mpegts/tsdemux.c index 7f22ae2a..621a4bfe 100644 --- a/src/input/mpegts/tsdemux.c +++ b/src/input/mpegts/tsdemux.c @@ -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: