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 and then output them, using the buffered approach * so that we read-ahead to get the next PCR, and thus have reliable * timing information. * * Assumes (strongly) that it is starting from the start of the file. * * - `tsreader` is the TS reader context * - `tswriter` is our (maybe buffered) writer * - if `pid_to_ignore` is non-zero, then any TS packets with that PID * will not be written out (note: any PCR information in them may still * be used) * - if `override_pcr_pid` is non-zero, then it is the PID to use for PCRs, * ignoring any value found in a PMT * - 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) * - if `quiet` is true, then only error messages should be written out * - if `verbose` is true, then give extra progress messages * * Returns 0 if all went well, 1 if something went wrong. */ static int play_buffered_TS_packets(TS_reader_p tsreader, TS_writer_p tswriter, uint32_t pid_to_ignore, uint32_t override_pcr_pid, int max, int loop, int quiet, int verbose) { int err; int total = 0; uint32_t count = 0; uint32_t pcr_pid; uint32_t start_count = 0; // which TS packet to loop from offset_t start_posn = 0; // These are only used in the loop below, but the compiler grumbles if // they're uninitialised (it isn't sure if they're being set by the call // to read_buffered_TS_packet() or not). I don't want to have to keep // thinking about the compiler warning, but I also know that these values // *will* be set by the function, so I don't want them reinitialised // every time round the loop. So hoist them back up to here... byte *data = NULL; uint32_t pid = 0; uint64_t pcr = 0; // Before we can use PCRs for timing, we need to read a PMT which tells us // what our video stream is (so we can get our PCRs therefrom). if (override_pcr_pid) { pcr_pid = override_pcr_pid; if (!quiet) fprint_msg("Forcing use of PCR PID 0x%03x (%d)\n",pcr_pid,pcr_pid); } else { err = find_PCR_PID(tsreader,tswriter,&pcr_pid,&start_count,max,quiet); if (err) { fprint_err("### Unable to find PCR PID for timing information\n" " Looked in first %d TS packets\n",max); return 1; } } // Once we've found that, we're ready to play our data err = prime_read_buffered_TS_packet(tsreader,pcr_pid); if (err) return 1; // If we're looping, remember the location of the first packet of (probable) // data - there's not much point rewinding before that point if (loop) start_posn = start_count * TS_PACKET_SIZE; count = start_count; for (;;) { err = read_buffered_TS_packet(tsreader,&count,&data,&pid,&pcr, max,loop,start_posn,start_count,quiet); if (err == EOF) // shouldn't occur if `loop` break; else if (err) { if (tsreader->file != STDIN_FILENO) { fprint_err("### Last TS packet read was at " LLU_FORMAT "\n", (uint64_t)count * TS_PACKET_SIZE); } return 1; } total ++; // If we've been asked to ignore this packet, we should be able to // just ignore it -- since all TS packets have their time associated // with them, we shouldn't need to send a "dummy" packet, just in // case it had time on it. if (pid_to_ignore != 0 && pid == pid_to_ignore) continue; // And write it out via the circular buffer err = tswrite_write(tswriter,data,pid,TRUE,pcr); if (err) { fprint_err("### Error writing TS packet %d to circular buffer\n", count); return 1; } if (!quiet && verbose && total%TSPLAY_REPORT_EVERY == 0) fprint_msg("Transferred %d TS packets\n",total); } if (!quiet) fprint_msg("Transferred %d TS packet%s in total\n",total,(total==1?"":"s")); return 0; }
/* * Read TS packets and then output them. * * Assumes (strongly) that it is starting from the start of the file. * * - `tsreader` is the TS reader context * - `tswriter` is our (maybe buffered) writer * - if `pid_to_ignore` is non-zero, then any TS packets with that PID * will not be written out (note: any PCR information in them may still * be used) * - 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) * - if `quiet` is true, then only error messages should be written out * - if `verbose` is true, then give extra progress messages * * Returns 0 if all went well, 1 if something went wrong. */ extern int play_TS_packets(TS_reader_p tsreader, TS_writer_p tswriter, uint32_t pid_to_ignore, int max, int loop, int quiet, int verbose) { int err; int total = 0; uint32_t count = 0; int pcrs_used = 0; int pcrs_ignored = 0; uint32_t pcr_pid; uint32_t start_count = 0; // which TS packet to loop from offset_t start_posn = 0; // Before we can use PCRs for timing, we need to read a PMT which tells us // what our video stream is (so we can get our PCRs therefrom). err = find_PCR_PID(tsreader,tswriter,&pcr_pid,&start_count,max,quiet); if (err) { fprint_err("### Unable to find PCR PID for timing information\n" " Looked in first %d TS packets\n",max); return 1; } // Once we've found that, we're ready to play our data // If we're looping, remember the location of the first packet of (probable) // data - there's not much point rewinding before that point if (loop) start_posn = start_count * TS_PACKET_SIZE; count = start_count; for (;;) { byte *data; uint32_t pid; int got_pcr; uint64_t pcr; err = read_TS_packet(tsreader,&count,&data,&pid,&got_pcr,&pcr, max,loop,start_posn,start_count,quiet); if (err == EOF) // shouldn't occur if `loop` break; else if (err) { if (tsreader->file != STDIN_FILENO) { fprint_err("### Last TS packet read was at " LLU_FORMAT "\n", (uint64_t)count * TS_PACKET_SIZE); } return 1; } total ++; // We are only interested in timing information from our PCR PID stream if (got_pcr) { if (pid == pcr_pid) pcrs_used ++; else { pcrs_ignored ++; got_pcr = FALSE; } } if (pid_to_ignore != 0 && pid == pid_to_ignore) { // We want to "transmit" this packet, since that's the simplest // way of sending its timing information (if any) to the writer. // However, we don't want to *actually* send meaningful data. // The simplest thing is to ignore it if it doesn't have a PCR: // and otherwise, change it to a null packet, by resetting its PID. if (!got_pcr) continue; else { data[2] = 0xFF; data[1] |= 0x1F; } } // And write it out via the circular buffer err = tswrite_write(tswriter,data,pid,got_pcr,pcr); if (err) { fprint_err("### Error writing TS packet %d to circular buffer\n", count); return 1; } if (!quiet && verbose && total%TSPLAY_REPORT_EVERY == 0) fprint_msg("Transferred %d TS packets\n",total); } if (!quiet) { fprint_msg("Transferred %d TS packet%s in total\n",total,(total==1?"":"s")); fprint_msg("Used PCRs from %d packets, ignored PCRs from %d packets\n", pcrs_used,pcrs_ignored); } 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; } } }