/* * Attempt to determine if an input stream is Transport Stream or Elementary * Stream, and if the latter, if it is H.262 or H.264 (MPEG-2 or MPEG-4/AVC). * * ***** 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 #include #else // _WIN32 #include #endif // _WIN32 #include "compat.h" #include "es_fns.h" #include "ts_fns.h" #include "nalunit_fns.h" #include "h262_fns.h" #include "misc_fns.h" #include "printing_fns.h" #include "version.h" #define STREAM_IS_TS 10 #define STREAM_IS_PS 11 #define STREAM_IS_H262 12 #define STREAM_IS_H264 14 #define STREAM_IS_AVS 15 #define STREAM_MAYBE_PES 5 #define STREAM_IS_UNSURE 9 #define STREAM_IS_ERROR 0 /* * Look for an initial 0x47 sync byte, and check for TS * * Returns 0 if nothing went wrong, 1 if something did. */ static int check_if_TS(int input, byte cur_byte, int verbose, int *decided, int *result) { int ii; if (verbose) print_msg("Is it Transport Stream?\n"); // It may be enough to look at the first byte of the stream if (cur_byte != 0x47) { if (verbose) fprint_msg(" First byte in file is 0x%02X not 0x47, so it is not\n",cur_byte); return 0; } // Transport Stream packets start with 0x47, so it's a good bet. if (verbose) print_msg(" First byte in file is 0x47, so it looks like Transport Stream\n"); // To check a bit, we can try looking at every 188th byte if (verbose) print_msg(" Checking next 500 packets to see if they start 0x47\n"); for (ii=0; ii<500; ii++) { byte buf[TS_PACKET_SIZE]; int err = read_bytes(input,TS_PACKET_SIZE,buf); if (err) { fprint_err("### %s trying to read start of packet %d\n", (err==EOF?"EOF":"Error"),ii+1); return 1; } if (buf[TS_PACKET_SIZE-1] != 0x47) { if (verbose) fprint_msg(" Packet %d does not start with 0x47 (%02x instead)\n", ii+1,buf[TS_PACKET_SIZE-1]); return 0; } } if (verbose) print_msg("The checked packets all start with 0x47 - looks like TS\n"); *decided = TRUE; *result = STREAM_IS_TS; return 0; } /* * Try to decide if we *have* got program stream * * Returns 0 if nothing went wrong, 1 if something did. */ static int check_if_PS(int input, int verbose, int *decided, int *result) { int err; byte buf[10]; int stuffing_length; if (verbose) { print_msg("Is it Program Stream?\n"); print_msg(" Trying to read pack header\n"); } err = read_bytes(input,4,buf); if (err) { fprint_err("### %s trying to read start of first PS packet\n", (err==EOF?"EOF":"Error")); return 1; } if (buf[0] != 0 || buf[1] != 0 || buf[2] != 1 || buf[3] != 0xba) { if (verbose) fprint_msg(" File starts %02X %02X %02X %02X, not 00 00 01 BA - not PS\n", buf[0],buf[1],buf[2],buf[3]); return 0; } if (verbose) print_msg(" File starts 00 00 01 BA - could be PS," " reading pack header body\n"); err = read_bytes(input,8,buf); if (err) { fprint_err("### %s trying to read body of PS pack header\n", (err==EOF?"EOF":"Error")); return 1; } if ((buf[0] & 0xF0) == 0x20) { if (verbose) print_msg(" Looks like ISO/IEC 11171-1/MPEG-1 pack header\n"); } else if ((buf[0] & 0xC0) == 0x40) { if (verbose) print_msg(" Looks like ISO/IEC 13818-1/H.222.0 pack header\n"); err = read_bytes(input,2,&(buf[8])); if (err) { fprint_err("### %s trying to read last 2 bytes of body of PS pack header\n", (err==EOF?"EOF":"Error")); return 1; } stuffing_length = buf[9] & 0x07; // And ignore that many stuffing bytes... if (stuffing_length > 0) { err = read_bytes(input,stuffing_length,buf); if (err) { fprint_err("### %s trying to read PS pack header stuffing bytes\n", (err==EOF?"EOF":"Error")); return 1; } } } // We could check for reserved bits - maybe at another time if (verbose) print_msg(" OK, trying to read start of next packet\n"); err = read_bytes(input,4,buf); if (err) { fprint_err("### %s trying to read start of next PS packet\n", (err==EOF?"EOF":"Error")); return 1; } if (buf[0] != 0 || buf[1] != 0 || buf[2] != 1) { if (verbose) fprint_msg(" Next 'packet' starts %02X %02X %02X, not 00 00 01 - not PS\n", buf[0],buf[1],buf[2]); return 0; } if (verbose) print_msg(" Start of second packet found at right place - looks like PS\n"); *decided = TRUE; *result = STREAM_IS_PS; return 0; } /* * Look at the start of our "elementary" stream, and try to determine * its actual type. * * - `input` is the input stream to inspect * - if `verbose` is true, the caller wants details of how the decision * is being made * - `decided` is returned TRUE if the function believes it has identified * the stream type, in which case: * - `result` will an appropriate value indicating what we've decided * * Note that this function reads into the stream, and may attempt to * rewind it. * * Returns 0 if nothing went wrong, 1 if an error occurred */ static int determine_packet_type(int input, int verbose, int *decided, int *result) { int err; #ifdef _WIN32 int length; #else ssize_t length; #endif byte first_byte; int video_type; length = read(input,&first_byte,1); if (length == 0) { print_err("### EOF reading first byte\n"); return 1; } else if (length == -1) { fprint_err("### Error reading first byte: %s\n",strerror(errno)); return 1; } // Does it look like transport stream? err = check_if_TS(input,first_byte,verbose,decided,result); if (err) { close_file(input); return 1; } if (*decided) { close_file(input); return 0; } seek_file(input,0); // Does it look like program stream? err = check_if_PS(input,verbose,decided,result); if (err) { close_file(input); return 1; } if (*decided) { close_file(input); return 0; } seek_file(input,0); // Does it look like one of the types of ES we recognise? if (verbose) print_msg("Is it an Elementary Stream we recognise?\n"); err = decide_ES_file_video_type(input,!verbose,verbose,&video_type); if (err) { close_file(input); return 1; } switch (video_type) { case VIDEO_H264: *result = STREAM_IS_H264; *decided = TRUE; break; case VIDEO_H262: *result = STREAM_IS_H262; *decided = TRUE; break; case VIDEO_AVS: *result = STREAM_IS_AVS; *decided = TRUE; break; case VIDEO_UNKNOWN: *result = STREAM_IS_UNSURE; *decided = FALSE; if (verbose) print_msg("Still not sure\n"); break; default: fprint_msg("### stream_type: Unexpected decision from" " decide_ES_file_video_type: %d\n",video_type); close_file(input); return 1; } close_file(input); return 0; } static void print_usage() { print_msg( "Usage: stream_type [switches] \n" "\n" ); REPORT_VERSION("stream_type"); print_msg( "\n" " Attempt to determine if an input stream is Transport Stream,\n" " Program Stream, or Elementary Stream, and if the latter, if it\n" " is H.262 or H.264 (i.e., MPEG-2 or MPEG-4/AVC respectively)." "\n" " The mechanisms used are fairly crude, assuming that:\n" " - data is byte aligned\n" " - for TS, the first byte in the file will be the start of a NAL unit,\n" " and PAT/PMT packets will be findable\n" " - for PS, the first packet starts immediately at the start of the\n" " file, and is a pack header\n" " - if the first 1000 packets could be H.262 *or* H.264, then the data\n" " is assumed to be H.264 (the program doesn't try to determine\n" " sensible sequences of H.262/H.264 packets, so this is a reasonable\n" " way of guessing)\n" "\n" " It is quite possible that data which is not relevant will be\n" " misidentified\n" "\n" " The program exit value is:\n" " * 10 if it detects Transport Stream,\n" " * 11 if it detects Program Stream,\n" " * 12 if it detects Elementary Stream containing H.262 (MPEG-2),\n" " * 14 if it detects Elementary Stream containing H.264 (MPEG-4/AVC),\n" " * 5 if it looks like it might be PES,\n" " * 9 if it really cannot decide, or\n" " * 0 if some error occurred\n" "\n" "Files:\n" " is the file to analyse\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" " -verbose, -v Output more detailed information about how it is\n" " making its decision\n" " -quiet, -q Only output error messages\n" ); } int main(int argc, char **argv) { char *input_name = NULL; int had_input_name = FALSE; int input = -1; int verbose = FALSE; int quiet = FALSE; int err = 0; int ii = 1; int decided = FALSE; int result = STREAM_IS_ERROR; 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 STREAM_IS_ERROR; } else if (!strcmp("-verbose",argv[ii]) || !strcmp("-v",argv[ii])) { verbose = TRUE; quiet = FALSE; } else if (!strcmp("-err",argv[ii])) { CHECKARG("stream_type",ii); if (!strcmp(argv[ii+1],"stderr")) redirect_output_stderr(); else if (!strcmp(argv[ii+1],"stdout")) redirect_output_stdout(); else { fprint_err("### stream_type: " "Unrecognised option '%s' to -err (not 'stdout' or" " 'stderr')\n",argv[ii+1]); return 1; } ii++; } else if (!strcmp("-quiet",argv[ii]) || !strcmp("-q",argv[ii])) { verbose = FALSE; quiet = TRUE; } else { fprint_err("### stream_type: " "Unrecognised command line switch '%s'\n",argv[ii]); return STREAM_IS_ERROR; } } else { if (had_input_name) { fprint_err("### stream_type: Unexpected '%s'\n",argv[ii]); return STREAM_IS_ERROR; } else { input_name = argv[ii]; had_input_name = TRUE; } } ii++; } if (!had_input_name) { print_err("### stream_type: No input file specified\n"); return STREAM_IS_ERROR; } input = open_binary_file(input_name,FALSE); if (input == -1) { fprint_err("### stream_type: Unable to open input file %s\n", input_name); return 1; } if (!quiet) fprint_msg("Reading from %s\n",input_name); // Try to guess err = determine_packet_type(input,verbose,&decided,&result); if (err) { print_err("### Unable to decide on stream type due to error\n"); return STREAM_IS_ERROR; } if (!quiet) { if (!decided) { print_msg("Unable to decide\n"); result = STREAM_IS_UNSURE; } else { switch (result) { case STREAM_IS_TS: print_msg("It appears to be Transport Stream\n"); break; case STREAM_IS_PS: print_msg("It appears to be Program Stream\n"); break; case STREAM_IS_H262: print_msg("It appears to be Elementary Stream, MPEG-2 (H.262)\n"); break; case STREAM_IS_H264: print_msg("It appears to be Elementary Stream, MPEG-4 (H.264)\n"); break; case STREAM_IS_AVS: print_msg("It appears to be Elementary Stream, AVS\n"); break; case STREAM_MAYBE_PES: print_msg("It looks likely to be PES\n"); break; case STREAM_IS_UNSURE: print_msg("It is not recognised\n"); break; default: fprint_msg("Unexpected decision value %d\n",result); result = STREAM_IS_ERROR; break; } } } return result; } // Local Variables: // tab-width: 8 // indent-tabs-mode: nil // c-basic-offset: 2 // End: // vim: set tabstop=8 shiftwidth=2 expandtab: