int camd_tcp_connect(struct in_addr ip, int port) { ts_LOGf("CAM | Connecting to server %s:%d\n", inet_ntoa(ip), port); int fd = socket(PF_INET, SOCK_STREAM, 0); if (fd < 0) { ts_LOGf("CAM | Could not create socket | %s\n", strerror(errno)); return -1; } struct sockaddr_in sock; sock.sin_family = AF_INET; sock.sin_port = htons(port); sock.sin_addr = ip; if (do_connect(fd, (struct sockaddr *)&sock, sizeof(sock), 1000) < 0) { ts_LOGf("CAM | Could not connect to server %s:%d | %s\n", inet_ntoa(ip), port, strerror(errno)); close(fd); sleep(1); return -1; } int flag = 1; setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(int)); ts_LOGf("CAM | Connected to fd:%d\n", fd); return fd; }
int udp_connect_input(struct io *io) { struct sockaddr_storage addr; int addrlen = sizeof(addr); int sock = -1; memset(&addr, 0, sizeof(addr)); ts_LOGf("Connecting input to %s port %s\n", io->hostname, io->service); if (bind_addr(io->hostname, io->service, SOCK_DGRAM, &addr, &addrlen, &sock) < 0) return -1; /* Set receive buffer size to ~2.0MB */ int bufsize = (2000000 / 1316) * 1316; setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (void *)&bufsize, sizeof(bufsize)); if (is_multicast(&addr)) { if (join_multicast_group(sock, io->ttl, &addr) < 0) { close(sock); return -1; } } io->fd = sock; ts_LOGf("Input connected to fd:%d\n", io->fd); return 1; }
static int join_multicast_group(int sock, int ttl, struct sockaddr_storage *addr) { switch (addr->ss_family) { case AF_INET: { struct ip_mreq mreq; mreq.imr_multiaddr.s_addr = ((struct sockaddr_in *)addr)->sin_addr.s_addr; mreq.imr_interface.s_addr = INADDR_ANY; if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const void *)&mreq, sizeof(mreq)) < 0) { ts_LOGf("ERROR: setsockopt(IP_ADD_MEMBERSHIP): %s\n", strerror(errno)); return -1; } if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) { ts_LOGf("ERROR: setsockopt(IP_MULTICAST_TTL %d): %s\n", ttl, strerror(errno)); } break; } case AF_INET6: { struct ipv6_mreq mreq6; memcpy(&mreq6.ipv6mr_multiaddr, &(((struct sockaddr_in6 *)addr)->sin6_addr), sizeof(struct in6_addr)); mreq6.ipv6mr_interface = 0; // interface index, will be set later if (setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) { ts_LOGf("ERROR: setsockopt(IPV6_ADD_MEMBERSHIP): %s\n", strerror(errno)); return -1; } if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)) < 0) { ts_LOGf("ERROR: setsockopt(IPV6_MULTICAST_HOPS %d): %s\n", ttl, strerror(errno)); } break; } } return 0; }
void ts_nit_dump(struct ts_nit *nit) { struct ts_section_header *sec = nit->section_header; int i; ts_section_dump(sec); ts_LOGf(" * NIT data\n"); ts_LOGf(" * PID : 0x%04x (%d)\n", nit->ts_header.pid, nit->ts_header.pid); ts_LOGf(" * reserved1 : 0x%02x\n", nit->reserved1); ts_LOGf(" * network_len : 0x%02x (%d)\n", nit->network_info_size, nit->network_info_size); ts_LOGf(" * reserved2 : 0x%02x\n", nit->reserved1); ts_LOGf(" * ts_loop_len : %d\n", nit->ts_loop_size); ts_LOGf(" * num_streams : %d\n", nit->streams_num); if (nit->network_info_size > 0) { ts_LOGf(" * Network info:\n"); ts_LOGf(" * network info size: %d\n", nit->network_info_size); ts_descriptor_dump(nit->network_info, nit->network_info_size); } for(i=0;i<nit->streams_num;i++) { struct ts_nit_stream *stream = nit->streams[i]; ts_LOGf(" - [%02d/%02d] | TS_id: 0x%04x (%d) ORG_net_id: 0x%04x (%d) Reserved: 0x%0x Desc_size: %d\n", i+1, nit->streams_num, stream->transport_stream_id, stream->transport_stream_id, stream->original_network_id, stream->original_network_id, stream->reserved1, stream->descriptor_size); if (stream->descriptor_data) { ts_descriptor_dump(stream->descriptor_data, stream->descriptor_size); } } ts_nit_check_generator(nit); }
static int camd_send_ecm(struct ts *ts, struct camd_msg *msg) { struct camd *c = &ts->camd; int ret = c->ops.do_ecm(c, msg); if (ret <= 0) { ts_LOGf("ERR | Error sending ecm packet, reconnecting to camd.\n"); ts->is_cw_error = 1; c->ops.reconnect(c); return ret; } ret = camd_recv_cw(ts); if (ret < 1) { time_t now = time(NULL); ts->is_cw_error = 1; if (ts->key.ts && now - ts->key.ts > KEY_VALID_TIME) { if (c->key->is_valid_cw) { notify(ts, "NO_CODE_WORD", "No code word was set in %ld sec. Decryption is disabled.", now - ts->key.ts); ts_LOGf("CW | *ERR* No valid code word was received in %ld seconds. Decryption is disabled.\n", now - ts->key.ts); ts->cw_last_warn = time(NULL); ts->cw_next_warn = ts->cw_last_warn + ts->cw_warn_sec; ts->cw_next_warn -= now - ts->key.ts; if (ts->cw_next_warn <= ts->cw_last_warn) ts->cw_next_warn = ts->cw_last_warn + ts->cw_warn_sec; } c->key->is_valid_cw = 0; } return 0; } return ret; }
static int camd_recv_cw(struct ts *ts) { struct camd *c = &ts->camd; struct timeval tv1, tv2, last_ts_keyset; uint16_t ca_id = 0; uint16_t idx = 0; int ret; gettimeofday(&tv1, NULL); ret = c->ops.get_cw(c, &ca_id, &idx, c->key->cw); gettimeofday(&tv2, NULL); if (ret <= 0) { if (ret == -1) { // Fatal error it is better to reconnect to server. ts_LOGf("ERR | No code word has been received (ret = %d)\n", ret); c->ops.reconnect(c); } c->ecm_recv_errors++; if (c->ecm_recv_errors >= ECM_RECV_ERRORS_LIMIT) { c->key->is_valid_cw = 0; memset(c->key->cw, 0, 16); // Invalid CW } usleep(10000); return 0; } char cw_dump[16 * 6]; ts_hex_dump_buf(cw_dump, 16 * 6, c->key->cw, 16, 0); int valid_cw = memcmp(c->key->cw, invalid_cw, 16) != 0; if (!c->key->is_valid_cw && valid_cw) { ts_LOGf("CW | OK: Valid code word was received.\n"); notify(ts, "CODE_WORD_OK", "Valid code word was received."); } c->key->is_valid_cw = valid_cw; // At first ts_keyset is not initialized last_ts_keyset = c->key->ts_keyset; if (c->key->is_valid_cw) camd_set_cw(ts, c->key->cw, 1); if (ts->ecm_cw_log) { ts_LOGf("CW | SID 0x%04x CAID: 0x%04x CW_recv: %5llu ms LastKey: %5llu ms Data: %s\n", ts->service_id, ca_id, timeval_diff_msec(&tv1, &tv2), timeval_diff_msec(&last_ts_keyset, &tv2), cw_dump ); } return 1; }
int udp_connect_input(struct io *io) { struct sockaddr_storage addr; int addrlen = sizeof(addr); int sock = -1; memset(&addr, 0, sizeof(addr)); if (!io->isrc.s_addr) ts_LOGf("Connecting input to %s port %s\n", io->hostname, io->service); else ts_LOGf("Connecting input to %s port %s source %s\n", io->hostname, io->service, inet_ntoa(io->isrc)); if (get_input_socket(io->hostname, io->service, SOCK_DGRAM, &addr, &addrlen, &sock) < 0) return -1; /* Set receive buffer size to ~2.0MB */ int bufsize = (2000000 / 1316) * 1316; setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (void *)&bufsize, sizeof(bufsize)); if (is_multicast(&addr)) { if (join_multicast_group(sock, io->ttl, &addr) < 0) { close(sock); return -1; } else { #ifdef IP_ADD_SOURCE_MEMBERSHIP if (io->isrc.s_addr && addr.ss_family == AF_INET) { /* Source-specific multicast */ struct sockaddr_in *src = (struct sockaddr_in *)&addr; struct ip_mreq_source imr; memset(&imr, 0, sizeof(imr)); imr.imr_multiaddr = src->sin_addr; imr.imr_sourceaddr = io->isrc; if (setsockopt(sock, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, (char *)&imr, sizeof(struct ip_mreq_source)) < 0) { char str_addr[INET6_ADDRSTRLEN]; my_inet_ntop(addr.ss_family, (struct sockaddr *)&addr, str_addr, sizeof(str_addr)); ts_LOGf("ERROR: Can't set multicast group %s source %s: %s\n", str_addr, inet_ntoa(io->isrc), strerror(errno)); } } #endif } } io->fd = sock; ts_LOGf("Input connected to fd:%d\n", io->fd); return 1; }
static void *camd_thread(void *in_ts) { struct ts *ts = in_ts; set_thread_name("tsdec-camd"); while (1) { struct camd_msg *msg; void *req = queue_get(ts->camd.req_queue); // Waits... if (ts->camd_stop) break; if (!req) continue; msg = queue_get_nowait(ts->camd.ecm_queue); if (!msg) msg = queue_get_nowait(ts->camd.emm_queue); if (!msg) continue; camd_do_msg(msg); if (ts->camd.ecm_queue->items >= ECM_QUEUE_HARD_LIMIT) { ts_LOGf("WRN | Too much items (%d) in ECM queue, dropping the oldest.\n", ts->camd.ecm_queue->items); while(ts->camd.ecm_queue->items >= ECM_QUEUE_SOFT_LIMIT) { msg = queue_get_nowait(ts->camd.ecm_queue); camd_msg_free(&msg); } } if (ts->camd.emm_queue->items >= EMM_QUEUE_HARD_LIMIT) { ts_LOGf("WRN | Too much items (%d) in EMM queue, dropping the oldest.%s\n", ts->camd.emm_queue->items, ts->camd.ops.proto == CAMD_NEWCAMD ? " Consider switching to cs378x protocol!" : ""); while(ts->camd.emm_queue->items >= EMM_QUEUE_SOFT_LIMIT) { msg = queue_get_nowait(ts->camd.emm_queue); camd_msg_free(&msg); } } } // Flush ECM queue while (ts->camd.ecm_queue->items) { struct camd_msg *msg = queue_get_nowait(ts->camd.ecm_queue); camd_msg_free(&msg); } // Flush EMM queue while (ts->camd.emm_queue->items) { struct camd_msg *msg = queue_get_nowait(ts->camd.emm_queue); camd_msg_free(&msg); } pthread_exit(EXIT_SUCCESS); }
int ts_pat_parse(struct ts_pat *pat) { uint8_t *section_data = pat->section_header->data; int section_len = pat->section_header->data_len; while (section_len > 0) { if (pat->programs_num == pat->programs_max) { ts_LOGf("PAT contains too many programs (>%d), not all are initialized!\n", pat->programs_max); break; } struct ts_pat_program *pinfo = calloc(1, sizeof(struct ts_pat_program)); pinfo->program = (section_data[0] << 8) | section_data[1]; // xxxxxxxx xxxxxxxx pinfo->reserved = (section_data[2] &~ 0x1F) >> 5; // xxx11111 pinfo->pid = ((section_data[2] &~ 0xE0) << 8) | section_data[3]; // 111xxxxx xxxxxxxx pat->programs[pat->programs_num] = pinfo; pat->programs_num++; section_data += 4; section_len -= 4; } if (!ts_crc32_section_check(pat->section_header, "PAT")) return 0; pat->initialized = 1; return 1; }
static int get_socket(const char *hostname, const char *service, int socktype, struct sockaddr_storage *addr, int *addrlen, int *sock, bool is_output) { struct addrinfo hints, *res, *ressave; int n, ret = -1; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = ai_family; hints.ai_socktype = socktype; n = getaddrinfo(hostname, service, &hints, &res); if (n < 0) { ts_LOGf("ERROR: getaddrinfo(%s): %s\n", hostname, gai_strerror(n)); return ret; } ressave = res; while (res) { *sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (*sock > -1) { int on = 1; setsockopt(*sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); set_sock_nonblock(*sock); if (is_output) { memcpy(addr, res->ai_addr, res->ai_addrlen); *addrlen = res->ai_addrlen; ret = 0; break; } if (bind(*sock, res->ai_addr, res->ai_addrlen) == 0) { memcpy(addr, res->ai_addr, res->ai_addrlen); *addrlen = res->ai_addrlen; ret = 0; break; } else { char str_addr[INET6_ADDRSTRLEN]; my_inet_ntop(res->ai_family, res->ai_addr, str_addr, sizeof(str_addr)); ts_LOGf("ERROR: bind: %s:%s (%s): %s\n", hostname, service, str_addr, strerror(errno)); } close(*sock); *sock = -1; } res = res->ai_next; } freeaddrinfo(ressave); return ret; }
int ts_nit_parse(struct ts_nit *nit) { uint8_t *section_data = nit->section_header->data; int section_len = nit->section_header->data_len; /* Table data (2 bytes) */ nit->reserved1 = (section_data[0] &~ 0x0F) >> 4; // xxxx1111 nit->network_info_size = ((section_data[0] &~ 0xF0) << 8) | section_data[1]; // 1111xxxx xxxxxxxx /* Handle streams */ uint8_t *stream_data = section_data + 2 + nit->network_info_size; // +2 is to compensate for reserved1 and network_info_size int stream_len = section_len - nit->network_info_size - 4; // -4 for the CRC at the end nit->network_info = NULL; if (nit->network_info_size) { nit->network_info = malloc(nit->network_info_size); if (nit->network_info) { memcpy(nit->network_info, stream_data - nit->network_info_size, nit->network_info_size); } } // Before the table there are two more fields nit->reserved2 = (stream_data[0] &~ 0x0F) >> 4; // xxxx1111 nit->ts_loop_size = ((stream_data[0] &~ 0xF0) << 8) | stream_data[1]; // 1111xxxx xxxxxxxx stream_data += 2; stream_len = nit->ts_loop_size; while (stream_len > 0) { if (nit->streams_num == nit->streams_max) { ts_LOGf("!!! Too many streams in NIT, max %d\n", nit->streams_max); break; } struct ts_nit_stream *sinfo = calloc(1, sizeof(struct ts_nit_stream)); sinfo->transport_stream_id = (stream_data[0] << 8) | stream_data[1]; sinfo->original_network_id = (stream_data[2] << 8) | stream_data[3]; sinfo->reserved1 = (stream_data[4] &~ 0x0F) >> 4; // xxxx1111 sinfo->descriptor_size = ((stream_data[4] &~ 0xF0) << 8) | stream_data[5]; // 1111xxxx xxxxxxxx sinfo->descriptor_data = NULL; if (sinfo->descriptor_size > 0) { sinfo->descriptor_data = malloc(sinfo->descriptor_size); memcpy(sinfo->descriptor_data, &stream_data[6], sinfo->descriptor_size); } nit->streams[nit->streams_num] = sinfo; nit->streams_num++; stream_data += 6 + sinfo->descriptor_size; stream_len -= 6 + sinfo->descriptor_size; } if (!ts_crc32_section_check(nit->section_header, "NIT")) return 0; nit->initialized = 1; return 1; }
static void dvb_print_string(char *pad, char *prefix, uint8_t *input, int text_length) { if (text_length < 0) { ts_LOGf("%s !!! %s text_length < 0, %d\n", pad, prefix, text_length); return; } if (text_length == 0) { ts_LOGf("%s %s \"\" (size: %d)\n", pad, prefix, text_length); return; } char *text = calloc(1, text_length + 1); memcpy(text, input, text_length); if (text[0] < 32) ts_LOGf("%s %s \"%s\" (charset: 0x%02x size: %d)\n", pad, prefix, text+1, text[0], text_length-1); else ts_LOGf("%s %s \"%s\" (size: %d)\n", pad, prefix, text, text_length); free(text); }
void ts_compare_data(char *prefix, uint8_t *a, uint8_t *b, int size) { if (!a) { ts_LOGf("%s: !a\n", prefix); return; } if (!b) { ts_LOGf("%s: !b\n", prefix); return; } if (memcmp(a, b, size) == 0) { ts_LOGf(" **** %s generator is correct ****\n", prefix); } else { int i; for (i=0;i<size;i++) { ts_LOGf("%03d - %02x %02x | %s\n", i, a[i], b[i], a[i] == b[i] ? "ok" : "err"); } } }
int ts_pmt_parse(struct ts_pmt *pmt) { uint8_t *section_data = pmt->section_header->data; int section_len = pmt->section_header->data_len; pmt->reserved1 = (section_data[0] &~ 0x1F) >> 5; // xxx11111 pmt->PCR_pid = ((section_data[0] &~ 0xE0) << 8) | section_data[1]; // 111xxxxx xxxxxxxx pmt->reserved2 = (section_data[2] &~ 0x0F) >> 4; // xxxx1111 pmt->program_info_size = ((section_data[2] &~ 0xF0) << 8) | section_data[3]; // 1111xxxx xxxxxxxx /* Handle streams */ uint8_t *stream_data = section_data + 4 + pmt->program_info_size; // +4 is to compensate for reserved1,PCR,reserved2,program_info_size int stream_len = section_len - pmt->program_info_size - 4; // -4 for the CRC at the end pmt->program_info = NULL; if (pmt->program_info_size) { pmt->program_info = malloc(pmt->program_info_size); if (pmt->program_info) { memcpy(pmt->program_info, stream_data - pmt->program_info_size, pmt->program_info_size); } } while (stream_len > 0) { if (pmt->streams_num == pmt->streams_max) { ts_LOGf("PMT contains too many streams (>%d), not all are initialized!\n", pmt->streams_max); break; } struct ts_pmt_stream *sinfo = calloc(1, sizeof(struct ts_pmt_stream)); sinfo->stream_type = stream_data[0]; sinfo->reserved1 = (stream_data[1] &~ 0x1F) >> 5; // xxx11111 sinfo->pid = ((stream_data[1] &~ 0xE0) << 8) | stream_data[2]; // 111xxxxx xxxxxxxx sinfo->reserved2 = (stream_data[3] &~ 0x0F) >> 4; // xxxx1111 sinfo->ES_info_size = ((stream_data[3] &~ 0xF0) << 8) | stream_data[4]; // 1111xxxx xxxxxxxx sinfo->ES_info = NULL; if (sinfo->ES_info_size > 0) { sinfo->ES_info = malloc(sinfo->ES_info_size); memcpy(sinfo->ES_info, &stream_data[5], sinfo->ES_info_size); } pmt->streams[pmt->streams_num] = sinfo; pmt->streams_num++; stream_data += 5 + sinfo->ES_info_size; stream_len -= 5 + sinfo->ES_info_size; } if (!ts_crc32_section_check(pmt->section_header, "PMT")) return 0; pmt->initialized = 1; return 1; }
static void report_cw_warn(struct ts *ts, time_t now) { if (now - ts->key.ts > 1) { notify(ts, "NO_CODE_WORD", "No valid code word was received in %ld sec.", now - ts->key.ts); ts_LOGf("CW | *ERR* No valid code word was received for %ld seconds!\n", now - ts->key.ts); } ts->cw_last_warn = now; ts->cw_next_warn = now + ts->cw_warn_sec; }
static void do_reports(struct ts *ts) { static int first_emm_report = 1; static int first_ecm_report = 1; time_t now = time(NULL); if (ts->process_emm && ts->emm_report_interval) { if (first_emm_report && now >= ts->emm_last_report) { first_emm_report = 0; ts->emm_last_report -= FIRST_REPORT_SEC; report_emms(ts, now); } else if ((time_t)(ts->emm_last_report + ts->emm_report_interval) <= now) { report_emms(ts, now); } } if (ts->process_ecm && ts->ecm_report_interval) { if (first_ecm_report && now >= ts->ecm_last_report) { first_ecm_report = 0; ts->ecm_last_report -= FIRST_REPORT_SEC; report_ecms(ts, now); } else if ((time_t)(ts->ecm_last_report + ts->ecm_report_interval) <= now) { report_ecms(ts, now); } } if (ts->stream_is_not_scrambled && ts->last_not_scrambled_report_ts <= now - 60) { ts_LOGf("CLR | No encrypted packets in the last %ld seconds. Stream is clear.\n", now - ts->last_scrambled_packet_ts); notify(ts, "STREAM_CLEAR", "No encrypted packets in the last %ld seconds. Stream is clear.", now - ts->last_scrambled_packet_ts); ts->last_not_scrambled_report_ts = now; } if (ts->process_ecm && !ts->key.is_valid_cw) { if (ts->cw_warn_sec && now >= ts->cw_next_warn) { report_cw_warn(ts, now); } } if (!ts->no_input) { if (ts->last_pmt_ts <= now - 3) { if (ts->have_valid_pmt) { ts_LOGf("MIS | There is no valid PMT in the input.\n"); notify(ts, "NO_PROGRAM", "The input is missing valid program."); ts->have_valid_pmt = 0; } } } }
void signal_quit(int sig) { if (!keep_running) raise(sig); keep_running = 0; ts_LOGf("Killed %s with signal %d\n", program_id, sig); if (ts.input.type == NET_IO) shutdown_fd(&ts.input.fd); if (ts.output.type == NET_IO) shutdown_fd(&ts.output.fd); signal(sig, SIG_DFL); }
static void report_ecms(struct ts *ts, time_t now) { ts_LOGf("ECM | Received %u (%u dup) and processed %u in %lu seconds.\n", ts->ecm_seen_count, ts->ecm_duplicate_count, ts->ecm_processed_count, now - ts->ecm_last_report); ts->ecm_last_report = now; ts->ecm_seen_count = 0; ts->ecm_duplicate_count = 0; ts->ecm_processed_count = 0; }
void ts_cat_dump(struct ts_cat *cat) { struct ts_section_header *sec = cat->section_header; ts_section_dump(sec); if (cat->program_info_size > 0) { ts_LOGf(" * Descriptor dump:\n"); ts_descriptor_dump(cat->program_info, cat->program_info_size); } ts_cat_check_generator(cat); }
static void report_cw_warn(struct ts *ts, time_t now) { if (ts->stream_is_not_scrambled || !ts->have_valid_pmt || ts->no_input) return; if (now - ts->key.ts > 1) { notify(ts, "NO_CODE_WORD", "No valid code word was received in %ld sec.", now - ts->key.ts); ts_LOGf("CW | *ERR* No valid code word was received for %ld seconds!\n", now - ts->key.ts); } ts->cw_last_warn = now; ts->cw_next_warn = now + ts->cw_warn_sec; }
static void report_ecms(struct ts *ts, time_t now) { if ((ts->stream_is_not_scrambled || !ts->have_valid_pmt || ts->no_input) && ts->ecm_seen_count == 0) return; ts_LOGf("ECM | Received %u (%u dup) and processed %u in %lu seconds.\n", ts->ecm_seen_count, ts->ecm_duplicate_count, ts->ecm_processed_count, now - ts->ecm_last_report); ts->ecm_last_report = now; ts->ecm_seen_count = 0; ts->ecm_duplicate_count = 0; ts->ecm_processed_count = 0; }
void ts_pat_dump(struct ts_pat *pat) { struct ts_section_header *sec = pat->section_header; int i; ts_section_dump(sec); ts_LOGf(" * PAT data\n"); ts_LOGf(" * num_programs: %d\n", pat->programs_num); for (i=0;i<pat->programs_num;i++) { struct ts_pat_program *prg = pat->programs[i]; ts_LOGf(" * [%02d/%02d]: Program No 0x%04x (%5d) -> PID %04x (%d) /res: 0x%02x/\n", i+1, pat->programs_num, prg->program, prg->program, prg->pid, prg->pid, prg->reserved); // Program number 0 is Network ID, not program id if (prg->program == 0) { ts_LOGf(" - NIT PID %04x (%d)\n", prg->pid, prg->pid); } } ts_pat_check_generator(pat); }
struct ts_pat *ts_pat_copy(struct ts_pat *pat) { struct ts_pat *newpat = ts_pat_alloc(); int i; for (i=0;i<pat->section_header->num_packets; i++) { newpat = ts_pat_push_packet(newpat, pat->section_header->packet_data + (i * TS_PACKET_SIZE)); } if (newpat->initialized) { return newpat; } else { ts_LOGf("Error copying PAT!\n"); ts_pat_free(&newpat); return NULL; } }
static int camd_send_emm(struct ts *ts, struct camd_msg *msg) { struct camd *c = &ts->camd; int ret = c->ops.do_emm(c, msg); if (ret < 1) { c->emm_recv_errors++; if (c->emm_recv_errors >= EMM_RECV_ERRORS_LIMIT) { ts_LOGf("ERR | Error sending emm packet, reconnecting to camd.\n"); c->ops.reconnect(c); c->emm_recv_errors = 0; } } else { c->emm_recv_errors = 0; } return ret; }
void ts_pmt_dump(struct ts_pmt *pmt) { struct ts_section_header *sec = pmt->section_header; int i; ts_section_dump(sec); ts_LOGf(" * PMT data\n"); ts_LOGf(" * PID : %04x (%d)\n", pmt->ts_header.pid, pmt->ts_header.pid); ts_LOGf(" * reserved1 : %d\n", pmt->reserved1); ts_LOGf(" * PCR PID : %04x (%d)\n", pmt->PCR_pid, pmt->PCR_pid); ts_LOGf(" * reserved2 : %d\n", pmt->reserved2); ts_LOGf(" * program_len : %d\n", pmt->program_info_size); ts_LOGf(" * num_streams : %d\n", pmt->streams_num); if (pmt->program_info_size > 0) { ts_LOGf(" * Program info:\n"); ts_LOGf(" * program info size: %d\n", pmt->program_info_size); ts_descriptor_dump(pmt->program_info, pmt->program_info_size); } for(i=0;i<pmt->streams_num;i++) { struct ts_pmt_stream *stream = pmt->streams[i]; ts_LOGf(" * [%02d/%02d] PID %04x (%d) -> Stream type: 0x%02x (%d) /es_info_size: %d/ %s\n", i+1, pmt->streams_num, stream->pid, stream->pid, stream->stream_type, stream->stream_type, stream->ES_info_size, h222_stream_type_desc(stream->stream_type) ); if (stream->ES_info) { ts_descriptor_dump(stream->ES_info, stream->ES_info_size); } } ts_pmt_check_generator(pmt); }
int udp_connect_output(struct io *io) { struct sockaddr_storage addr; int addrlen = sizeof(addr); int sock = -1; memset(&addr, 0, sizeof(addr)); ts_LOGf("Connecting output to %s port %s ttl: %d\n", io->hostname, io->service, io->ttl); if (bind_addr(io->hostname, io->service, SOCK_DGRAM, &addr, &addrlen, &sock) < 0) return -1; /* Set send buffer size to ~2.0MB */ int bufsize = (2000000 / 1316) * 1316; setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (void *)&bufsize, sizeof(bufsize)); if (is_multicast(&addr)) { if (join_multicast_group(sock, io->ttl, &addr) < 0) { close(sock); return -1; } else { if (addr.ss_family == AF_INET) { if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &io->intf, sizeof(io->intf)) < 0) { ts_LOGf("ERROR: setsockopt(IP_MUTICAST_IF %s): %s\n", inet_ntoa(io->intf), strerror(errno)); close(sock); return -1; } } if (addr.ss_family == AF_INET6 && io->v6_if_index > -1) { if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, (void *)&io->v6_if_index, sizeof(io->v6_if_index)) < 0) { ts_LOGf("ERROR: setsockopt(IPV6_MUTICAST_IF %d): %s\n", io->v6_if_index, strerror(errno)); close(sock); return -1; } } } } if (addr.ss_family == AF_INET && io->tos > -1) { if (setsockopt(sock, IPPROTO_IP, IP_TOS, &io->tos, sizeof(io->tos)) < 0) { ts_LOGf("ERROR: setsockopt(IP_TOS 0x%02x): %s\n", io->tos, strerror(errno)); } } if (connect(sock, (struct sockaddr *)&addr, addrlen) < 0) { ts_LOGf("ERROR: udp_connect(): %s\n", strerror(errno)); close(sock); return -1; } io->fd = sock; ts_LOGf("Output connected to fd:%d\n", io->fd); return 1; }
static void report_emms(struct ts *ts, time_t now) { ts_LOGf("EMM | Received %u, Skipped %u, Sent %u, Processed %u in %lu seconds.\n", ts->emm_input_count, ts->emm_skipped_count, ts->emm_seen_count, ts->emm_processed_count, now - ts->emm_last_report); if (ts->emm_seen_count == 0) { notify(ts, "NO_EMM_RECEIVED", "No EMMs were received in last %lu seconds.", now - ts->emm_last_report); } ts->emm_last_report = now; ts->emm_input_count = 0; ts->emm_seen_count = 0; ts->emm_skipped_count = 0; ts->emm_processed_count = 0; }
void ts_pat_check_generator(struct ts_pat *pat) { struct ts_pat *pat1 = ts_pat_copy(pat); if (pat1) { ts_compare_data("PAT (tspacket->struct)", pat1->section_header->packet_data, pat->section_header->packet_data, pat->section_header->num_packets * TS_PACKET_SIZE); ts_pat_free(&pat1); } uint8_t *ts_packets; int num_packets; ts_pat_generate(pat, &ts_packets, &num_packets); if (num_packets != pat->section_header->num_packets) { ts_LOGf("ERROR: num_packets:%d != sec->num_packets:%d\n", num_packets, pat->section_header->num_packets); } ts_compare_data("PAT (struct->tspacket)", pat->section_header->packet_data, ts_packets, num_packets * TS_PACKET_SIZE); free(ts_packets); }
void ts_nit_check_generator(struct ts_nit *nit) { struct ts_nit *nit1 = ts_nit_alloc(); int i; for (i=0;i<nit->section_header->num_packets;i++) { nit1 = ts_nit_push_packet(nit1, nit->section_header->packet_data + (i * TS_PACKET_SIZE)); } ts_compare_data("NIT (tspacket->struct)", nit1->section_header->packet_data, nit->section_header->packet_data, nit->section_header->num_packets * TS_PACKET_SIZE); ts_nit_free(&nit1); uint8_t *ts_packets; int num_packets; ts_nit_generate(nit, &ts_packets, &num_packets); if (num_packets != nit->section_header->num_packets) { ts_LOGf("ERROR: num_packets:%d != sec->num_packets:%d\n", num_packets, nit->section_header->num_packets); } ts_compare_data("NIT (struct->tspacket)", nit->section_header->packet_data, ts_packets, num_packets * TS_PACKET_SIZE); free(ts_packets); }
void ts_descriptor_dump(uint8_t *desc_data, int desc_data_len) { char *pad = " * "; uint8_t *data = desc_data; int data_len = desc_data_len; while (data_len >= 2) { int i; uint8_t tag = data[0]; uint8_t this_length = data[1]; // ts_LOGf("%sDescriptor tag: 0x%02x (%d) size: %d\n", padA, tag, tag, this_length); data += 2; data_len -= 2; if (this_length > data_len) { // Not much we can do - try giving up? ts_LOGf("%s!!! Descriptor 0x%02x says length %d, but only %d bytes left\n", pad, tag, this_length, data_len); return; } switch (tag) { case 2: { // Video stream descriptor char *dump = ts_hex_dump(data, this_length, 0); ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Video stream descriptor: %s\n", pad, tag, tag, this_length, dump); free(dump); struct { uint8_t multiple_frame_rate_flag : 1, frame_rate_code : 4, mpeg1_only_flag : 1, constraint_parameter_flag : 1, still_picture_flag : 1; uint8_t profile_and_level_indication; uint8_t chroma_format : 2, frame_rate_extension_flag : 1, reserved : 5; uint8_t escape:1, profile:3, level:4; } vs; if (this_length >= 1) { vs.multiple_frame_rate_flag = bit_on(data[0], bit_8); vs.frame_rate_code = (data[0] &~ 0x80) >> 3; // 1xxxx111 vs.mpeg1_only_flag = bit_on(data[0], bit_3); vs.constraint_parameter_flag = bit_on(data[0], bit_2); vs.still_picture_flag = bit_on(data[0], bit_1); ts_LOGf("%s - multiple_frame_rate_flag : %d\n", pad, vs.multiple_frame_rate_flag); ts_LOGf("%s - frame_rate_code : %d (%s)\n", pad, vs.frame_rate_code, vs.frame_rate_code == 0 ? "forbidden" : vs.frame_rate_code == 1 ? "23.976" : vs.frame_rate_code == 2 ? "24.00" : vs.frame_rate_code == 3 ? "25.00" : vs.frame_rate_code == 4 ? "29.97" : vs.frame_rate_code == 5 ? "30.00" : vs.frame_rate_code == 6 ? "50.00" : vs.frame_rate_code == 7 ? "59.94" : vs.frame_rate_code == 8 ? "60.00" : "reserved" ); ts_LOGf("%s - mpeg1_only_flag : %d\n", pad, vs.mpeg1_only_flag); ts_LOGf("%s - constraint_parameter_flag : %d\n", pad, vs.constraint_parameter_flag); ts_LOGf("%s - still_picture_flag : %d\n", pad, vs.still_picture_flag); } if (this_length >= 2 && vs.mpeg1_only_flag == 0) { vs.profile_and_level_indication = data[1]; vs.chroma_format = data[2] >> 6; // xx111111 vs.frame_rate_extension_flag = bit_on(data[2], bit_6); // 11x11111 vs.reserved = data[2] &~ 0xE0; // 111xxxxx vs.profile = (vs.profile_and_level_indication &~ 0x8f) >> 4; // x111xxxx vs.level = vs.profile_and_level_indication &~ 0xf0; // xxxx1111 ts_LOGf("%s - profile_and_level_indication : 0x%02x, Profile: %d (%s), Level: %d (%s)\n", pad, vs.profile_and_level_indication, vs.profile, vs.profile == 1 ? "High" : vs.profile == 2 ? "Spatially Scalable" : vs.profile == 3 ? "SNR Scalable" : vs.profile == 4 ? "Main" : vs.profile == 5 ? "Simple" : "Reserved", vs.level, vs.level == 4 ? "High" : vs.level == 6 ? "High 1440" : vs.level == 8 ? "Main" : vs.level == 10 ? "Low" : "Reserved" ); ts_LOGf("%s - chroma_format : %d (%s)\n", pad, vs.chroma_format, vs.chroma_format == 0 ? "reserved" : vs.chroma_format == 1 ? "4:2:0" : vs.chroma_format == 2 ? "4:2:2" : vs.chroma_format == 3 ? "4:4:4" : "unknown" ); ts_LOGf("%s - frame_rate_extension_flag : %d\n", pad, vs.frame_rate_extension_flag); ts_LOGf("%s - reserved : 0x%x\n", pad, vs.reserved); } break; } case 3: { // Audio stream descriptor char *dump = ts_hex_dump(data, this_length, 0); ts_LOGf("%sTag 0x%02x (%02d), sz: %d, Audio stream descriptor: %s\n", pad, tag, tag, this_length, dump); free(dump); struct { uint8_t free_format_flag : 1, ID : 1, layer : 2, vbr_flag : 1, reserved : 3; } as; if (this_length >= 1) { as.free_format_flag = bit_on(data[0], bit_8); as.ID = bit_on(data[0], bit_7); as.layer = (data[0] &~ 0xcf) >> 4; // 11xx1111 as.vbr_flag = bit_on(data[0], bit_4); as.reserved = data[0] &~ 0xf0; // 1111xxxx ts_LOGf("%s - free_format_flag : %d\n", pad, as.free_format_flag); ts_LOGf("%s - ID : %d (%s)\n", pad, as.ID, as.ID ? "MPEG Audio" : "Other"); ts_LOGf("%s - layer : %d (%s)\n", pad, as.layer, as.layer == 0 ? "reserved" : as.layer == 1 ? "Layer III" : as.layer == 2 ? "Layer II" : as.layer == 3 ? "Layer I" : "reserved" ); ts_LOGf("%s - vbr_audio_flag : %d\n", pad, as.vbr_flag); ts_LOGf("%s - reserved : 0x%x\n", pad, as.reserved); } break; }