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;
}
Exemple #3
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;
    }
  }
}