/* * Locate the PAT and PMT packets in an H.222 transport stream (TS), * and report on their contents (i.e., the program and stream info). * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is the MPEG TS, PS and ES tools. * * The Initial Developer of the Original Code is Amino Communications Ltd. * Portions created by the Initial Developer are Copyright (C) 2008 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Amino Communications Ltd, Swavesey, Cambridge UK * * ***** END LICENSE BLOCK ***** */ #include #include #include #include #include #ifdef _WIN32 #include #else // _WIN32 #include #endif // _WIN32 #include "compat.h" #include "ts_fns.h" #include "misc_fns.h" #include "printing_fns.h" #include "pidint_fns.h" #include "version.h" /* * Report on the program streams, by looking at the PAT and PMT packets * in the first `max` TS packets of the given input stream * * Returns 0 if all went well, 1 if something went wrong. */ static int report_streams(TS_reader_p tsreader, int max, int verbose) { int err; int ii; // TODO: Should really support multiple programs // (some use of pidint_list to support program number -> PMT?) pidint_list_p this_prog_list = NULL; pidint_list_p last_prog_list = NULL; pmt_p this_pmt = NULL; pmt_p last_pmt = NULL; uint32_t pmt_pid = 0; // which will get "masked" by the PAT pid 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 num_pats = 0; int num_pmts = 0; fprint_msg("Scanning %d TS packets\n",max); for (ii=0; ii pat_data_used) continue; err = extract_prog_list_from_pat(verbose,pat_data,pat_data_len, &this_prog_list); if (err) { free_pidint_list(&last_prog_list); free_pmt(&last_pmt); if (pmt_data) free(pmt_data); free(pat_data); return err; } free(pat_data); pat_data = NULL; pat_data_len = 0; pat_data_used = 0; num_pats++; if (err) { free_pidint_list(&last_prog_list); free_pmt(&last_pmt); if (pmt_data) free(pmt_data); return err; } if (!same_pidint_list(this_prog_list,last_prog_list)) { if (last_prog_list != NULL) fprint_msg("\nPacket %d is PAT - content changed\n",ii+1); else if (!verbose) fprint_msg("\nPacket %d is PAT\n",ii+1); report_pidint_list(this_prog_list,"Program list","Program",FALSE); if (this_prog_list->length == 0) fprint_msg("No programs defined in PAT (packet %d)\n",ii+1); else { if (this_prog_list->length > 1) fprint_msg("Multiple programs in PAT - using the first\n"); pmt_pid = this_prog_list->pid[0]; } } free_pidint_list(&last_prog_list); last_prog_list = this_prog_list; } else if (pid == pmt_pid) { if (verbose) fprint_msg("Packet %d is PMT with PID %04x (%d)%s\n",ii+1,pid,pid, (payload_unit_start_indicator?"[pusi]":"")); if (payload_len == 0) { fprint_msg("Packet %d is PMT, but has no payload\n",ii+1); continue; } 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(verbose,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")); free_pidint_list(&this_prog_list); free_pmt(&last_pmt); 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(verbose,pmt_data,pmt_data_len,pid,&this_pmt); if (err) { free_pidint_list(&this_prog_list); free_pmt(&last_pmt); if (pmt_data) free(pmt_data); return err; } free(pmt_data); pmt_data = NULL; pmt_data_len = 0; pmt_data_used = 0; num_pmts++; if (same_pmt(this_pmt,last_pmt)) // Nothing to do { free_pmt(&this_pmt); continue; } if (last_pmt != NULL) fprint_msg("\nPacket %d is PMT with PID %04x (%d)" " - content changed\n",ii+1,pid,pid); else if (!verbose) fprint_msg("\nPacket %d is PMT with PID %04x (%d)\n",ii+1,pid,pid); report_pmt(TRUE," ",this_pmt); free_pmt(&last_pmt); last_pmt = this_pmt; } } fprint_msg("\nFound %d PAT packet%s and %d PMT packet%s in %d TS packets\n", num_pats,(num_pats==1?"":"s"), num_pmts,(num_pmts==1?"":"s"),max); free_pidint_list(&last_prog_list); free_pmt(&last_pmt); if (pmt_data) free(pmt_data); return 0; } static void print_usage() { print_msg( "Usage: tsinfo [switches] []\n" "\n" ); REPORT_VERSION("tsinfo"); print_msg( "\n" " Report on the program streams in a Transport Stream.\n" "\n" "Files:\n" " is an H.222 Transport Stream file (but see -stdin)\n" "\n" "Switches:\n" " -err stdout Write error messages to standard output (the default)\n" " -err stderr Write error messages to standard error (Unix traditional)\n" " -stdin Input from standard input, instead of a file\n" " -verbose, -v Output extra information about packets\n" " -max , -m Number of TS packets to scan. Defaults to 10000.\n" " -repeat Look for PMT packets, and report on each\n" ); } int main(int argc, char **argv) { int use_stdin = FALSE; char *input_name = NULL; int had_input_name = FALSE; int max = 10000; int verbose = FALSE; // True => output diagnostic/progress messages int lookfor = 1; int err = 0; TS_reader_p tsreader = NULL; int ii = 1; if (argc < 2) { print_usage(); return 0; } while (ii < argc) { if (argv[ii][0] == '-') { if (!strcmp("--help",argv[ii]) || !strcmp("-h",argv[ii]) || !strcmp("-help",argv[ii])) { print_usage(); return 0; } else if (!strcmp("-err",argv[ii])) { CHECKARG("tsinfo",ii); if (!strcmp(argv[ii+1],"stderr")) redirect_output_stderr(); else if (!strcmp(argv[ii+1],"stdout")) redirect_output_stdout(); else { fprint_err("### tsinfo: " "Unrecognised option '%s' to -err (not 'stdout' or" " 'stderr')\n",argv[ii+1]); return 1; } ii++; } else if (!strcmp("-verbose",argv[ii]) || !strcmp("-v",argv[ii])) { verbose = TRUE; } else if (!strcmp("-max",argv[ii]) || !strcmp("-m",argv[ii])) { CHECKARG("tsinfo",ii); err = int_value("tsinfo",argv[ii],argv[ii+1],TRUE,10,&max); if (err) return 1; ii++; } else if (!strcmp("-repeat",argv[ii])) { CHECKARG("tsinfo",ii); err = int_value("tsinfo",argv[ii],argv[ii+1],TRUE,10,&lookfor); if (err) return 1; ii++; } else if (!strcmp("-stdin",argv[ii])) { use_stdin = TRUE; had_input_name = TRUE; // so to speak } else { fprint_err("### tsinfo: " "Unrecognised command line switch '%s'\n",argv[ii]); return 1; } } else { if (had_input_name) { fprint_err("### tsinfo: Unexpected '%s'\n",argv[ii]); return 1; } else { input_name = argv[ii]; had_input_name = TRUE; } } ii++; } if (!had_input_name) { print_err("### tsinfo: No input file specified\n"); return 1; } err = open_file_for_TS_read((use_stdin?NULL:input_name),&tsreader); if (err) { fprint_err("### tsinfo: Unable to open input file %s for reading TS\n", use_stdin?"":input_name); return 1; } fprint_msg("Reading from %s\n",(use_stdin?"":input_name)); err = report_streams(tsreader,max,verbose); if (err) { print_err("### tsinfo: Error reporting on stream\n"); (void) close_TS_reader(&tsreader); return 1; } err = close_TS_reader(&tsreader); if (err) return 1; return 0; } // Local Variables: // tab-width: 8 // indent-tabs-mode: nil // c-basic-offset: 2 // End: // vim: set tabstop=8 shiftwidth=2 expandtab: