static int digest_times(pcapreport_ctx_t *ctx, pcapreport_stream_t * const st, const pcaprec_hdr_t *pcap_pkt_hdr, const ethernet_packet_t *epkt, const ipv4_header_t *ipv4_header, const ipv4_udp_header_t *udp_header, const byte *data, const uint32_t len) { int rv; const uint64_t ts_byte_start = st->ts_bytes; if (st->ts_r == NULL) { rv = build_TS_reader_with_fns(st, digest_times_read, digest_times_seek, &st->ts_r); if (rv) { print_err( "### pcapreport: Cannot create ts reader.\n"); return 1; } } // Add all our data to the pool. { unsigned int pkts = len / 188; unsigned int pktlen = pkts * 188; if (pktlen != len) ++st->pkts_overlength; st->tmp_buf = (byte *)realloc(st->tmp_buf, st->tmp_len + pktlen); memcpy(&st->tmp_buf[st->tmp_len], data, pktlen); st->tmp_len += pktlen; st->ts_bytes += pktlen; } // Now read out all the ts packets we can. while (1) { byte *pkt; int rv; rv = read_next_TS_packet(st->ts_r, &pkt); if (rv == EOF) { // Got to EOF - return for more data return 0; } // Right. Split it .. { const uint64_t t_pcr = pkt_time(pcap_pkt_hdr); uint32_t pid; int pusi; byte *adapt; int adapt_len; byte *payload; int payload_len; rv = split_TS_packet(pkt, &pid, &pusi, &adapt, &adapt_len, &payload, &payload_len); if (rv) { fprint_msg(">%d> WARNING: TS packet %d [ packet %d @ %d.%d s ] cannot be split.\n", st->stream_no, st->ts_counter, ctx->pkt_counter, pcap_pkt_hdr->ts_sec, pcap_pkt_hdr->ts_usec); } else { //int cc; // PCR ? if (adapt && adapt_len) { int has_pcr; uint64_t pcr; int64_t pcr_time_offset; get_PCR_from_adaptation_field(adapt, adapt_len, &has_pcr, &pcr); if (has_pcr) { int64_t skew; if (ctx->time_report) { fprint_msg(">%d> Found PCR %lld at %d.%d s \n", st->stream_no, pcr, pcap_pkt_hdr->ts_sec, pcap_pkt_hdr->ts_usec); } if (st->pcr_pid == 0) st->pcr_pid = pid; if (pid != st->pcr_pid) { // *** If this happens often then fix to track each Pid if (!st->multiple_pcr_pids) { fprint_msg("!%d! Multiple pids detected: pids: %d,%d,...\n", st->stream_no, st->pcr_pid, pid); } st->multiple_pcr_pids = TRUE; } else { // PCR pops out in 27MHz units. Let's do all our comparisons // in 90kHz. pcr /= 300; // fprint_msg("pcr = %lld t_pcr = %lld diff = %lld\n", // pcr, t_pcr, t_pcr - pcr); pcr_time_offset = ((int64_t)t_pcr - (int64_t)pcr); skew = st->section_last == NULL ? 0LL : pcr_time_offset - (st->section_last->time_start - st->section_last->pcr_start); if (st->section_last == NULL || skew > st->skew_discontinuity_threshold || skew < -st->skew_discontinuity_threshold) { pcapreport_section_t * const tsect = section_create(st); if (tsect->section_no != 0) { fprint_msg(">%d> Skew discontinuity! Skew = %lld (> %lld) at" " ts = %d network = %d (PCR %lld Time %d.%d)\n", st->stream_no, skew, st->skew_discontinuity_threshold, st->ts_counter, ctx->pkt_counter, pcr, pcap_pkt_hdr->ts_sec, pcap_pkt_hdr->ts_usec); } tsect->pkt_final = tsect->pkt_start = ctx->pkt_counter; tsect->pcr_last = tsect->pcr_start = pcr; tsect->time_last = tsect->time_start = t_pcr; tsect->ts_byte_start = tsect->ts_byte_final = ts_byte_start; jitter_clear(&st->jitter); st->last_time_offset = 0; } else { pcapreport_section_t * const tsect = st->section_last; // Extract jitter over up to the last 10s. skew will be within // an int by now unsigned int cur_jitter = jitter_add(&st->jitter, (int)skew, (uint32_t)(t_pcr & 0xffffffffU), 90000 * 10); if (tsect->jitter_max < cur_jitter) tsect->jitter_max = cur_jitter; if (ctx->time_report) { int64_t rel_tim = t_pcr - tsect->time_start; // 90kHz double skew_rate = (double)skew / ((double)((double)rel_tim / (60*90000))); fprint_msg(">%d> [ts %d net %d ] PCR %lld Time %d.%d [rel %d.%d] - skew = %lld (delta = %lld, rate = %.4g PTS/min) - jitter=%u\n", st->stream_no, st->ts_counter, ctx->pkt_counter, pcr, pcap_pkt_hdr->ts_sec, pcap_pkt_hdr->ts_usec, (int)(rel_tim / (int64_t)1000000), (int)rel_tim%1000000, skew, pcr_time_offset - st->last_time_offset, skew_rate, cur_jitter); } if (st->csv_name != NULL) // We should be outputting to file { if (st->csv_file == NULL) { if ((st->csv_file = fopen(st->csv_name, "wt")) == NULL) { fprint_err("### pcapreport: Cannot open %s .\n", st->csv_name); exit(1); } fprintf(st->csv_file, "\"PKT\",\"Time\",\"PCR\",\"Skew\",\"Jitter\"\n"); } fprintf(st->csv_file, "%d,%llu,%llu,%lld,%u\n", ctx->pkt_counter, t_pcr - ctx->time_start, pcr, skew, cur_jitter); } // Remember where we are for posterity tsect->pcr_last = pcr; tsect->time_last = t_pcr; st->last_time_offset = pcr_time_offset; } } } } } { pcapreport_section_t * const tsect = st->section_last; if (tsect != NULL) { tsect->time_final = t_pcr; tsect->ts_byte_final = st->ts_bytes; tsect->pkt_final = ctx->pkt_counter; } } ++st->ts_counter; } } }
/* * Read the next TS packet, coping with looping, etc. * * - `tsreader` is the TS reader context * - `count` is a running count of TS packets read from this input * - `data` is a pointer to the data for the packet * - `pid` is the PID of the TS packet * - `got_PCR` is TRUE if the adaptation field of this packet contains a PCR * - `pcr` is then the PCR value itself * - if `max` is greater than zero, then at most `max` TS packets should * be read from the input * - if `loop`, play the input file repeatedly (up to `max` TS packets * if applicable) - i.e., rewind to `start_posn` and start again if * `count` reaches `max` (obviously only if `max` is greater than zero). * - `start_count` is the value `count` should have after we've looped back * to `start_posn` * - if `quiet` is true, then only error messages should be written out * * Returns 0 if all went well, 1 if something went wrong, EOF if `loop` is * false and either EOF was read, or `max` TS packets were read. */ static int read_TS_packet(TS_reader_p tsreader, uint32_t *count, byte *data[TS_PACKET_SIZE], uint32_t *pid, int *got_pcr, uint64_t *pcr, int max, int loop, offset_t start_posn, uint32_t start_count, int quiet) { int err; int payload_unit_start_indicator; byte *adapt; int adapt_len; byte *payload; int payload_len; if (max > 0 && (*count) >= (uint32_t)max) { if (loop) { if (!quiet) fprint_msg("Read %d packets, rewinding and continuing\n",max); err = seek_using_TS_reader(tsreader,start_posn); if (err) return 1; *count = start_count; } else { if (!quiet) fprint_msg("Stopping after %d TS packets\n",max); return EOF; } } // Read the next packet err = read_next_TS_packet(tsreader,data); if (err) { if (err == EOF) { if (!loop) return EOF; if (!quiet) fprint_msg("EOF (after %d TS packets), rewinding and continuing\n", *count); } else { fprint_err("### Error reading TS packet %d\n",*count); if (!loop) return 1; if (!quiet) print_msg("!!! Rewinding and continuing anyway\n"); } err = seek_using_TS_reader(tsreader,start_posn); if (err) return 1; *count = start_count; } err = split_TS_packet(*data,pid,&payload_unit_start_indicator, &adapt,&adapt_len,&payload,&payload_len); if (err) { fprint_err("### Error splitting TS packet %d\n",*count); return 1; } get_PCR_from_adaptation_field(adapt,adapt_len,got_pcr,pcr); (*count) ++; return 0; }
int main(int argn, char *args[]) { int ii = 1; int verbose = FALSE; int invert = 0; unsigned int max_pkts = (unsigned int)-1; const char *input_file = NULL, *output_file = NULL; if (argn < 2) { print_usage(); return 0; } ensurePidList(1024); while (ii < argn) { if (args[ii][0] == '-') { if (!strcmp("--help", args[ii]) || !strcmp("-h", args[ii]) || !strcmp("-help", args[ii])) { print_usage(); return 0; } else if (!strcmp("-verbose", args[ii]) || !strcmp("-v", args[ii])) { verbose = TRUE; } else if (!strcmp("-m", args[ii]) || !strcmp("-max", args[ii])) { if (argn <= ii) { fprint_err("### tsfilter: -max requires an argument\n"); return 1; } max_pkts = atoi(args[ii+1]); ++ii; } else if (!strcmp("-!", args[ii]) || !strcmp("-invert", args[ii])) { invert = 1; } else if (!strcmp("-i", args[ii]) || !strcmp("-input", args[ii])) { if (argn <= ii) { fprint_err("### tsfilter: -input requires an argument\n"); return 1; } input_file = args[ii+1]; ++ii; } else if (!strcmp("-o", args[ii]) || !strcmp("-output", args[ii])) { if (argn <= ii) { fprint_err("### tsfilter: -output requires an argument\n"); return 1; } output_file = args[ii+1]; ++ii; } else { fprint_err("### tsfilter: " "Unrecognised command line switch '%s'\n", args[ii]); return 1; } } else { char *p = NULL; // It's a pid. if (pidListUsed >= pidListAlloc) { ensurePidList(pidListAlloc + 1024); } pidList[pidListUsed] = strtoul(args[ii], &p, 0); if (!(p && *p == '\0')) { fprint_err("### tsfilter: '%s' wasn't a valid number. \n", args[ii]); return 1; } ++pidListUsed; } ++ii; } if (!pidListUsed) { fprint_err("### tsfilter: No pids to filter. \n"); return 1; } // Now .. { int err; TS_reader_p tsreader; TS_writer_p tswriter; byte *pkt = NULL; unsigned int pid, pkt_num; int pusi, adapt_len, payload_len; byte *adapt, *payload; pkt_num = 0; err = open_file_for_TS_read((char *)input_file, &tsreader); if (err) { fprint_err("## tsfilter: Unable to open stdin for reading TS.\n"); return 1; } if (output_file) { err = tswrite_open(TS_W_FILE, (char *)output_file, NULL, 0, 1, &tswriter); } else { err = tswrite_open(TS_W_STDOUT, NULL, NULL, 0, 1, &tswriter); } if (err) { fprint_err("## tsfilter: Unable to open stdout for writing TS. \n"); return 1; } while (1) { err = read_next_TS_packet(tsreader, &pkt); if (err == EOF) { /* We're done */ break; } err = split_TS_packet(pkt, &pid, &pusi, &adapt, &adapt_len, &payload, &payload_len); if (err) { fprint_err("### Error splitting TS packet - continuing. \n"); } else { int i; int found = 0; for (i = 0 ;i < pidListUsed; ++i) { if (pid == pidList[i]) { ++found; // Yes! break; } } if (max_pkts != (unsigned int)-1 && pkt_num > max_pkts) { // We're done processing. If invert is on, // copy the rest of the output, otherwise quit. if (!invert) { break; } else { found = 0; } } // Invert the result, whatever it was. if (invert) { found = !found; } if (found) { // Write it out. err = tswrite_write(tswriter, pkt, pid, 0, 0); if (err) { fprint_err("### Error writing output - %d \n", err); return 2; } } ++pkt_num; } } // It's the end! tswrite_close(tswriter, 1); close_TS_reader(&tsreader); return 0; } }
/* * Read TS packets until we have found the PCR PID for our program stream, * outputting packets (without using their PCR) as we go. * * - `tsreader` is the TS reader context * - `tswriter` is our (buffered) writer * - `pcr_pid` is the PID containing PCRs as indicated by the PMT * - `num_read` is how many TS packets we read * - if `max` is greater than zero, then at most `max` TS packets should * be read from the input * - if `quiet` is true, then only error messages should be written out * * Returns 0 if all went well, 1 if something went wrong. */ static int find_PCR_PID(TS_reader_p tsreader, TS_writer_p tswriter, uint32_t *pcr_pid, uint32_t *num_read, int max, int quiet) { int err; int count = 0; byte *data; uint32_t pid; int payload_unit_start_indicator; byte *adapt; int adapt_len; byte *payload; int payload_len; int got_PAT = FALSE; pidint_list_p prog_list = NULL; pmt_p pmt = NULL; uint32_t pmt_pid = 0; // safe initial value byte *pat_data = NULL; int pat_data_len = 0; int pat_data_used = 0; byte *pmt_data = NULL; int pmt_data_len = 0; int pmt_data_used = 0; int pmt_program_number = -1; for (;;) { err = read_next_TS_packet(tsreader,&data); if (err == EOF) { fprint_err("### EOF (after %d TS packets), before finding program" " information\n",count); if (pmt_data) free(pmt_data); return 1; } else if (err) { fprint_err("### Error reading TS packet %d\n",count+1); if (pmt_data) free(pmt_data); return 1; } count++; err = split_TS_packet(data,&pid,&payload_unit_start_indicator, &adapt,&adapt_len,&payload,&payload_len); if (err) { fprint_err("### Error splitting TS packet %d\n",count); if (pmt_data) free(pmt_data); return 1; } // Whatever we've found, don't forget to write it out via the // circular buffer (and we *know* it doesn't have a PCR that is // useful to us, as yet) err = tswrite_write(tswriter,data,pid,FALSE,0); if (err) { fprint_err("### Error writing TS packet %d to circular buffer\n", count); if (pmt_data) free(pmt_data); return 1; } if (pid == 0x0000) { if (!quiet) fprint_msg("Packet %d is PAT\n",count); if (payload_unit_start_indicator && pat_data) { // This is the start of a new PAT packet, but we'd already // started one, so throw its data away print_err("!!! Discarding previous (uncompleted) PAT data\n"); free(pat_data); pat_data = NULL; pat_data_len = 0; pat_data_used = 0; } else if (!payload_unit_start_indicator && !pat_data) { // This is the continuation of a PAT packet, but we hadn't // started one yet print_err("!!! Discarding PAT continuation, no PAT started\n"); continue; } err = build_psi_data(FALSE,payload,payload_len,pid, &pat_data,&pat_data_len,&pat_data_used); if (err) { fprint_err("### Error %s PAT\n", (payload_unit_start_indicator?"starting new":"continuing")); if (pat_data) free(pat_data); return 1; } // Do we need more data to complete this PAT? if (pat_data_len > pat_data_used) continue; err = extract_prog_list_from_pat(FALSE,pat_data,pat_data_len,&prog_list); if (err != 0) { free(pat_data); return err; } if (!quiet) report_pidint_list(prog_list,"Program list","Program",FALSE); if (prog_list->length > 1 && !quiet) print_msg("Multiple programs in PAT - using the first\n\n"); pmt_pid = prog_list->pid[0]; pmt_program_number = prog_list->number[0]; got_PAT = TRUE; free_pidint_list(&prog_list); free(pat_data); pat_data = NULL; pat_data_len = 0; pat_data_used = 0; } else if (got_PAT && pid == pmt_pid) { if (!quiet) fprint_msg("Packet %d %s PMT with PID %04x\n", count, payload_unit_start_indicator?"starts":"continues", pmt_pid); if (payload_unit_start_indicator && pmt_data) { // This is the start of a new PMT packet, but we'd already // started one, so throw its data away print_err("!!! Discarding previous (uncompleted) PMT data\n"); free(pmt_data); pmt_data = NULL; pmt_data_len = 0; pmt_data_used = 0; } else if (!payload_unit_start_indicator && !pmt_data) { // This is the continuation of a PMT packet, but we hadn't // started one yet print_err("!!! Discarding PMT continuation, no PMT started\n"); continue; } err = build_psi_data(FALSE,payload,payload_len,pid, &pmt_data,&pmt_data_len,&pmt_data_used); if (err) { fprint_err("### Error %s PMT\n", (payload_unit_start_indicator?"starting new":"continuing")); if (pmt_data) free(pmt_data); return 1; } // Do we need more data to complete this PMT? if (pmt_data_len > pmt_data_used) continue; err = extract_pmt(FALSE,pmt_data,pmt_data_len,pmt_pid,&pmt); free(pmt_data); pmt_data = NULL; if (err) return err; if (pmt->program_number != pmt_program_number) { if (!quiet) fprint_msg("Discarding PMT program %d - looking for %d\n", pmt->program_number, pmt_program_number); free_pmt(&pmt); continue; } if (!quiet) report_pmt(TRUE," ",pmt); *pcr_pid = pmt->PCR_pid; free_pmt(&pmt); if (!quiet) fprint_msg("Taking timing information from PID 0x%03x\n",*pcr_pid); *num_read = count; return 0; } if (max > 0 && count >= max) { fprint_err("### Stopping after %d TS packets, before finding program" " information\n",max); if (pmt_data) free(pmt_data); return 1; } } }