/* * Report on the given file * * Returns 0 if all went well, 1 if something went wrong. */ static int report_buffering_stats(TS_reader_p tsreader, int max, int verbose, int quiet, char *output_name, uint32_t continuity_cnt_pid, uint64_t report_mask) { pmt_p pmt = NULL; int err; FILE *file = NULL; int num_streams = 0; FILE *file_cnt = NULL; // Define an arbitrary maximum number of streams we support // -- this is simpler than coping with changing the array sizes // when we find PMTs that indicate we need more streams -- this // limit should be big enough (we believe!) for any file we're // likely to find. #define MAX_NUM_STREAMS 100 struct stream_data stats[MAX_NUM_STREAMS]; // We want to be able to report on how well a simple linear-prediction // model for PCRs would work (i.e., given the last two PCRs, how well // can we predict the *actual* PCR value). It's useful to bundle the // data for that in one place... struct linear_prediction_data { int had_a_pcr; // Have we had a PCR from a PCR PID TS? uint64_t prev_pcr; // if we have, what the last one was offset_t prev_pcr_posn; // and which TS it was from double pcr_rate; int know_pcr_rate; int64_t min_pcr_error; // 27MHz int64_t max_pcr_error; // 27MHz }; struct linear_prediction_data predict = {0}; uint32_t pcr_pid; uint64_t first_pcr = 0; int pmt_at = 0; // in case we don't look for a PMT int index; int ii; int first = TRUE; offset_t posn = 0; offset_t start_posn = 0; uint32_t count = 0; uint32_t start_count = 0; memset(stats,0,sizeof(stats)); for (ii=0; ii<MAX_NUM_STREAMS; ii++) { stats[ii].pcr_pts_diff.min = LONG_MAX; stats[ii].pcr_dts_diff.min = LONG_MAX; stats[ii].pcr_pts_diff.max = LONG_MIN; stats[ii].pcr_dts_diff.max = LONG_MIN; stats[ii].last_cc = -1; } predict.min_pcr_error = LONG_MAX; predict.max_pcr_error = LONG_MIN; if (output_name) { file = fopen(output_name,"w"); if (file == NULL) { fprintf(stderr,"### tsreport: Unable to open file %s: %s\n", output_name,strerror(errno)); return 1; } printf("Writing CSV data to file %s\n",output_name); fprintf(file,"#TSoffset,calc|read,PCR/300,stream,audio|video,PTS,DTS,ESCR\n"); } // First we need to determine what we're taking our data from. err = find_pmt(tsreader,max,FALSE,quiet,&pmt_at,&pmt); if (err) return 1; pcr_pid = pmt->PCR_pid; for (ii=0; ii<pmt->num_streams; ii++) { uint32_t pid = pmt->streams[ii].elementary_PID; if (ii >= MAX_NUM_STREAMS) { printf("!!! Found more than %d streams -- just reporting on the first %d found\n", MAX_NUM_STREAMS,MAX_NUM_STREAMS); break; } if (pid >= 0x10 && pid <= 0x1FFE) { stats[num_streams].stream_type = pmt->streams[ii].stream_type; stats[num_streams].pid = pid; num_streams ++; } } printf("Looking at PCR PID %04x (%d)\n",pcr_pid,pcr_pid); for (ii=0; ii<num_streams; ii++) printf(" Stream %d: PID %04x (%d), %s\n",ii,stats[ii].pid,stats[ii].pid, h222_stream_type_str(stats[ii].stream_type)); // Now do the actual work... start_count = count = pmt_at; start_posn = posn = tsreader->posn - TS_PACKET_SIZE; if (continuity_cnt_pid != INVALID_PID) { file_cnt = fopen("continuity_counter.txt","w"); //lorenzo if (file_cnt == NULL) { fprintf(stderr,"### tsreport: Unable to open file continuity_counter.txt\n"); return 1; } } for (;;) { uint32_t pid; int payload_unit_start_indicator; byte *packet; byte *adapt, *payload; int adapt_len, payload_len; int got_pcr = FALSE; uint64_t acc_pcr = 0; // The accurate PCR per TS packet if (max > 0 && count >= (uint32_t)max) { printf("Stopping after %d packets (PMT was at %d)\n",max,pmt_at); break; } // Read the next TS packet, taking advantage of our read-ahead buffering // so that we know what its PCR *really* is if (first) { err = read_first_TS_packet_from_buffer(tsreader,pcr_pid,start_count, &packet,&pid,&acc_pcr,&count); posn = start_posn + (count-start_count)*TS_PACKET_SIZE; first = FALSE; } else { err = read_next_TS_packet_from_buffer(tsreader,&packet,&pid,&acc_pcr); count ++; posn += TS_PACKET_SIZE; } if (err == EOF) break; else if (err) { fprintf(stderr,"### Error reading TS packet %d at " OFFSET_T_FORMAT "\n",count,posn); return 1; } err = split_TS_packet(packet,&pid, &payload_unit_start_indicator, &adapt,&adapt_len,&payload,&payload_len); if (err) { fprintf(stderr,"### Error splitting TS packet %d at " OFFSET_T_FORMAT "\n",count,posn); return 1; } // ======================================================================== // If we actually had a PCR, then we need to remember it if (pid == pcr_pid) { uint64_t adapt_pcr; // Do I need to check that this is the same PCR I got earlier? // I certainly hope not... get_PCR_from_adaptation_field(adapt,adapt_len,&got_pcr,&adapt_pcr); if (got_pcr) { if (predict.know_pcr_rate) { // OK, so what we have predicted this PCR would be, // given the previous two PCRs and a linear rate? uint64_t guess_pcr = estimate_pcr(posn,predict.prev_pcr_posn, predict.prev_pcr,predict.pcr_rate); int64_t delta = adapt_pcr - guess_pcr; if (delta < predict.min_pcr_error) predict.min_pcr_error = delta; if (delta > predict.max_pcr_error) predict.max_pcr_error = delta; } if (verbose) printf(OFFSET_T_FORMAT_8 ": read PCR %s\n", posn, fmtx_timestamp(adapt_pcr, tfmt_abs | FMTX_TS_N_27MHz)); if (file) fprintf(file,LLU_FORMAT ",read," LLU_FORMAT ",,,,\n", posn,(adapt_pcr / (int64_t)300) & report_mask); if (predict.had_a_pcr) { if (predict.prev_pcr > adapt_pcr) { fprintf(stderr,"!!! PCR %s at TS packet " OFFSET_T_FORMAT " is not more than previous PCR %s\n", fmtx_timestamp(adapt_pcr, tfmt_abs | FMTX_TS_N_27MHz), posn, fmtx_timestamp(predict.prev_pcr, tfmt_abs | FMTX_TS_N_27MHz)); } else { uint64_t delta_pcr = adapt_pcr - predict.prev_pcr; int delta_bytes = (int)(posn - predict.prev_pcr_posn); predict.pcr_rate = ((double)delta_bytes * 27.0 / (double)delta_pcr) * 1000000.0; predict.know_pcr_rate = TRUE; #if 0 // XXX printf("PCR RATE = %f, DELTA_BYTES = %d, DELTA_PCR " LLU_FORMAT ", PCR = " LLU_FORMAT "\n", predict.pcr_rate,delta_bytes,delta_pcr,adapt_pcr); #endif } } else { if (!quiet) printf("First PCR at " OFFSET_T_FORMAT "\n",posn); first_pcr = adapt_pcr; predict.had_a_pcr = TRUE; } predict.prev_pcr = adapt_pcr; predict.prev_pcr_posn = posn; } } // end of working with a PCR PID packet // ======================================================================== index = pid_index(stats,num_streams,pid); if (index != -1) { // Do continuity counter checking int cc = packet[3] & 15; // Log if required if (continuity_cnt_pid == pid) fprintf(file_cnt, "%d%c", cc, cc == 15 ? '\n' : ' '); if (stats[index].last_cc > 0) { // We are allowed 1 dup packet // *** Could check that it actually is a dup... if (stats[index].last_cc == cc) { if (stats[index].cc_dup_count++ != 0) { if (stats[index].err_cc_dup_error++ == 0) { if (continuity_cnt_pid == pid) fprintf(file_cnt, "[Duplicate error] "); printf("### PID(%d): Continuity Counter >1 duplicate %d at " OFFSET_T_FORMAT "\n", stats[index].pid, cc, posn); } } } else { // Otherwise CC must go up by 1 mod 16 stats[index].cc_dup_count = 0; if (((stats[index].last_cc + 1) & 15) != cc) { if (stats[index].err_cc_error++ == 0) { if (continuity_cnt_pid == pid) fprintf(file_cnt, "[Discontinuity] "); printf("### PID(%d): Continuity Counter discontinuity %d->%d at " OFFSET_T_FORMAT "\n", stats[index].pid, stats[index].last_cc, cc, posn); } } } } stats[index].last_cc = cc; } if (index != -1 && payload && payload_unit_start_indicator) { // We are the start of a PES packet // We'll assume "enough" of the PES packet is in this TS int got_pts, got_dts; const uint64_t last_dts = stats[index].dts; uint64_t pcr_time_now_div300 = 0; int64_t difference; err = find_PTS_DTS_in_PES(payload,payload_len, &got_pts,&stats[index].pts,&got_dts,&stats[index].dts); if (err) { fprintf(stderr,"### Error looking for PTS/DTS in TS packet at " OFFSET_T_FORMAT "\n",posn); return 1; } if (got_dts && !got_pts) { fprintf(stderr,"### Got DTS but not PTS, in TS packet at " OFFSET_T_FORMAT "\n",posn); return 1; } if (!got_pts) continue; pcr_time_now_div300 = acc_pcr/300; // Do a few simple checks // For the sake of simplicity we ignore 33bit wrap... if (stats[index].pts < stats[index].dts) { if (stats[index].err_pts_lt_dts++ == 0) printf("### PID(%d): PTS (%s) < DTS (%s)\n", stats[index].pid, fmtx_timestamp(stats[index].pts, tfmt_abs), fmtx_timestamp(stats[index].dts, tfmt_abs)); } if (stats[index].had_a_dts && stats[index].dts < last_dts) { if (stats[index].err_dts_lt_prev_dts++ == 0) printf("### PID(%d): DTS (%s) < previous DTS (%s)\n", stats[index].pid, fmtx_timestamp(stats[index].dts, tfmt_abs), fmtx_timestamp(last_dts, tfmt_abs)); } if (stats[index].dts < pcr_time_now_div300) { if (stats[index].err_dts_lt_pcr++ == 0) printf("### PID(%d): DTS (%s) < PCR (%s)\n", stats[index].pid, fmtx_timestamp(stats[index].dts, tfmt_abs), fmtx_timestamp(acc_pcr, tfmt_abs | FMTX_TS_N_27MHz)); } if (!stats[index].had_a_pts) { #if 0 // XXX Sometimes useful to know printf(" First stream %d PTS (after first PCR) at " OFFSET_T_FORMAT "\n", index,posn); #endif stats[index].first_pts = stats[index].pts; stats[index].had_a_pts = TRUE; } if (got_dts && !stats[index].had_a_dts) { #if 0 // XXX Sometimes useful to know printf(" First stream %d DTS (after first PCR) at " OFFSET_T_FORMAT "\n", index,posn); #endif stats[index].first_dts = stats[index].dts; stats[index].had_a_dts = TRUE; } if (got_pts != got_dts || (got_pts && stats[index].pts != stats[index].dts)) stats[index].pts_ne_dts = TRUE; if (file) { // At the moment, we only report any ESCR to the file int got_escr = FALSE; uint64_t escr; (void) find_ESCR_in_PES(payload,payload_len,&got_escr,&escr); fprintf(file,OFFSET_T_FORMAT ",%s," LLU_FORMAT ",%d,%s,", posn, (pcr_pid == pid && got_pcr)?"read":"calc", pcr_time_now_div300 & report_mask, index, IS_AUDIO_STREAM_TYPE(stats[index].stream_type)?"audio": IS_VIDEO_STREAM_TYPE(stats[index].stream_type)?"video":""); fprintf(file,LLU_FORMAT ",",stats[index].pts & report_mask); if (got_dts) fprintf(file,LLU_FORMAT,stats[index].dts & report_mask); else fprintf(file,LLU_FORMAT,stats[index].pts & report_mask); fprintf(file,","); if (got_escr) { if (!quiet) printf("Found ESCR " LLU_FORMAT " at " OFFSET_T_FORMAT "\n", escr,posn); fprintf(file,LLU_FORMAT,escr & report_mask); } fprintf(file,"\n"); } if (verbose) { printf(OFFSET_T_FORMAT_8 ": %s PCR " LLU_FORMAT " %d %5s", posn, (pcr_pid == pid && got_pcr)?" ":"calc", pcr_time_now_div300, index, IS_AUDIO_STREAM_TYPE(stats[index].stream_type)?"audio": IS_VIDEO_STREAM_TYPE(stats[index].stream_type)?"video":""); } difference = stats[index].pts - pcr_time_now_div300; if (verbose) { printf(" PTS " LLU_FORMAT,stats[index].pts); printf(" PTS-PCR "); printf(LLD_FORMAT, difference); } if (difference > stats[index].pcr_pts_diff.max) { stats[index].pcr_pts_diff.max = difference; stats[index].pcr_pts_diff.max_at = stats[index].pts; stats[index].pcr_pts_diff.max_posn = posn; } if (difference < stats[index].pcr_pts_diff.min) { stats[index].pcr_pts_diff.min = difference; stats[index].pcr_pts_diff.min_at = stats[index].pts; stats[index].pcr_pts_diff.min_posn = posn; } stats[index].pcr_pts_diff.sum += difference; stats[index].pcr_pts_diff.num ++; if (got_dts) { difference = stats[index].dts - pcr_time_now_div300; if (verbose) { printf(" DTS " LLU_FORMAT,stats[index].dts); printf(" DTS-PCR "); printf(LLD_FORMAT, difference & report_mask); } if (difference > stats[index].pcr_dts_diff.max) { stats[index].pcr_dts_diff.max = difference; stats[index].pcr_dts_diff.max_at = stats[index].dts; stats[index].pcr_dts_diff.max_posn = posn; } if (difference < stats[index].pcr_dts_diff.min) { stats[index].pcr_dts_diff.min = difference; stats[index].pcr_dts_diff.min_at = stats[index].dts; stats[index].pcr_dts_diff.min_posn = posn; } stats[index].pcr_dts_diff.sum += difference; stats[index].pcr_dts_diff.num ++; } if (verbose) printf("\n"); } } if (continuity_cnt_pid != INVALID_PID) { fprintf(file_cnt, "\n"); fclose(file_cnt); //lorenzo } if (!quiet) printf("Last PCR at " OFFSET_T_FORMAT "\n",predict.prev_pcr_posn); printf("Read %d TS packet%s\n",count,(count==1?"":"s")); if (pmt) free_pmt(&pmt); if (file) fclose(file); printf("Linear PCR prediction errors: min=%s, max=%s\n", fmtx_timestamp(predict.min_pcr_error, tfmt_diff), fmtx_timestamp(predict.max_pcr_error, tfmt_diff)); if (!stats[0].had_a_pts && !stats[1].had_a_pts && !stats[0].had_a_dts && !stats[1].had_a_dts) printf("\n" "No PTS or DTS values found\n"); for (ii = 0; ii < num_streams; ii++) { printf("\nStream %d: PID %04x (%d), %s\n",ii,stats[ii].pid,stats[ii].pid, h222_stream_type_str(stats[ii].stream_type)); if (stats[ii].pcr_pts_diff.num > 0) { printf(" PCR/%s:\n Minimum difference was %6s at DTS %8s, TS packet at " OFFSET_T_FORMAT_8 "\n", stats[ii].pts_ne_dts ? "PTS" : "PTS,DTS", fmtx_timestamp(stats[ii].pcr_pts_diff.min, tfmt_diff), fmtx_timestamp(stats[ii].pcr_pts_diff.min_at, tfmt_abs), stats[ii].pcr_pts_diff.min_posn); printf(" Maximum difference was %6s at DTS %8s, TS packet at " OFFSET_T_FORMAT_8 "\n", fmtx_timestamp(stats[ii].pcr_pts_diff.max, tfmt_diff), fmtx_timestamp(stats[ii].pcr_pts_diff.max_at, tfmt_abs), stats[ii].pcr_pts_diff.max_posn); printf(" i.e., a span of %s\n", fmtx_timestamp(stats[ii].pcr_pts_diff.max - stats[ii].pcr_pts_diff.min, tfmt_diff)); printf(" Mean difference (of %u) is %s\n", stats[ii].pcr_pts_diff.num, fmtx_timestamp((int64_t)(stats[ii].pcr_pts_diff.sum/(double)stats[ii].pcr_pts_diff.num), tfmt_diff)); } if (stats[ii].pcr_dts_diff.num > 0 && stats[ii].pts_ne_dts) { printf(" PCR/DTS:\n Minimum difference was %6s at DTS %8s, TS packet at " OFFSET_T_FORMAT_8 "\n", fmtx_timestamp(stats[ii].pcr_dts_diff.min, tfmt_diff), fmtx_timestamp(stats[ii].pcr_dts_diff.min_at, tfmt_abs), stats[ii].pcr_dts_diff.min_posn); printf(" Maximum difference was %6s at DTS %8s, TS packet at " OFFSET_T_FORMAT_8 "\n", fmtx_timestamp(stats[ii].pcr_dts_diff.max, tfmt_diff), fmtx_timestamp(stats[ii].pcr_dts_diff.max_at, tfmt_abs), stats[ii].pcr_dts_diff.max_posn); printf(" i.e., a span of %s\n", fmtx_timestamp(stats[ii].pcr_dts_diff.max - stats[ii].pcr_dts_diff.min, tfmt_diff)); printf(" Mean difference (of %u) is %s\n", stats[ii].pcr_dts_diff.num, fmtx_timestamp((int64_t)(stats[ii].pcr_dts_diff.sum/(double)stats[ii].pcr_dts_diff.num), tfmt_diff)); } printf(" First PCR %8s, last %8s\n", fmtx_timestamp(first_pcr, tfmt_abs | FMTX_TS_N_27MHz), fmtx_timestamp(predict.prev_pcr, tfmt_abs | FMTX_TS_N_27MHz)); if (stats[ii].pcr_pts_diff.num > 0) printf(" First PTS %8s, last %8s\n", fmtx_timestamp(stats[ii].first_pts, tfmt_abs), fmtx_timestamp(stats[ii].pts, tfmt_abs)); if (stats[ii].pcr_dts_diff.num > 0) printf(" First DTS %8s, last %8s\n", fmtx_timestamp(stats[ii].first_dts, tfmt_abs), fmtx_timestamp(stats[ii].dts, tfmt_abs)); if (stats[ii].err_cc_error != 0) printf(" ### CC error * %d\n", stats[ii].err_cc_error); if (stats[ii].err_cc_dup_error != 0) printf(" ### CC duplicate error * %d\n", stats[ii].err_cc_dup_error); if (stats[ii].err_pts_lt_dts != 0) printf(" ### PTS < DTS * %d\n", stats[ii].err_pts_lt_dts); if (stats[ii].err_dts_lt_prev_dts != 0) printf(" ### DTS < prev DTS * %d\n", stats[ii].err_dts_lt_prev_dts); if (stats[ii].err_dts_lt_pcr != 0) printf(" ### DTS < PCR * %d\n", stats[ii].err_dts_lt_pcr); } return 0; }
/* * Extract all the TS packets for either a video or audio stream. * * Returns 0 if all went well, 1 if something went wrong. */ static int extract_av(int input, FILE *output, int want_video, int max, int verbose, int quiet) { int err, ii; int max_to_read = max; int total_num_read = 0; uint32_t pid = 0; TS_reader_p tsreader = NULL; pmt_p pmt = NULL; // Turn our file into a TS reader err = build_TS_reader(input,&tsreader); if (err) return 1; // First, find out what program streams we actually have for (;;) { int num_read; // Give up if we've read more than our limit if (max > 0 && max_to_read <= 0) break; err = find_pmt(tsreader,max_to_read,verbose,quiet,&num_read,&pmt); if (err == EOF) { if (!quiet) print_msg("No program stream information in the input file\n"); free_TS_reader(&tsreader); free_pmt(&pmt); return 0; } else if (err) { print_err("### Error finding program stream information\n"); free_TS_reader(&tsreader); free_pmt(&pmt); return 1; } max_to_read -= num_read; total_num_read += num_read; // From that, find a stream of the type we want... // Note that the audio detection will accept either DVB or ADTS Dolby (AC-3) // stream types for (ii=0; ii < pmt->num_streams; ii++) { if (( want_video && IS_VIDEO_STREAM_TYPE(pmt->streams[ii].stream_type)) || (!want_video && (IS_AUDIO_STREAM_TYPE(pmt->streams[ii].stream_type)))) { pid = pmt->streams[ii].elementary_PID; break; } } free_pmt(&pmt); // Did we find what we want? If not, go round again and look for the // next PMT (subject to the number of records we're willing to search) if (pid != 0) break; } if (pid == 0) { fprint_err("### No %s stream specified in first %d TS packets in input file\n", (want_video?"video":"audio"),max); free_TS_reader(&tsreader); return 1; } if (!quiet) fprint_msg("Extracting %s PID %04x (%d)\n",(want_video?"video":"audio"), pid,pid); // Amend max to take account of the packets we've already read max -= total_num_read; // And do the extraction. err = extract_pid_packets(tsreader,output,pid,max,verbose,quiet); free_TS_reader(&tsreader); return err; }