/* * Convert a Program Stream to Transport Stream. * * ***** 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 "pes_fns.h" #include "ps_fns.h" #include "ts_fns.h" #include "tswrite_fns.h" #include "misc_fns.h" #include "printing_fns.h" #include "version.h" static void print_usage() { print_msg( "Usage: ps2ts [switches] [] []\n" "\n" ); REPORT_VERSION("ps2ts"); print_msg( "\n" " Convert an H.222 program stream to H.222 transport stream.\n" "\n" " This program does not make use of any Program Stream Map packets\n" " in the data (mainly because I have yet to see data with any). This\n" " means that the program has to determine the stream type of the data\n" " based on the first few ES units.\n" "\n" " This program does not output more than one video and one audio\n" " stream. If the program stream data contains more than one of each,\n" " the first will be used, and the others ignored (with a message\n" " indicating this).\n" "\n" " It is assumed that the video stream will contain DTS values in its\n" " PES packets at reasonable intervals, which can be used as PCR values\n" " in the transport stream, and thus the video stream's PID can be used\n" " as the PCR PID in the transport stream.\n" "\n" "Files:\n" " is a file containing the program stream data\n" " (but see -stdin below)\n" " is a transport stream file\n" " (but see -stdout and -host below)\n" "\n" "Input switches:\n" " -stdin Take input from , instead of a named file\n" " -dvd The PS data is from a DVD. This is the default.\n" " This switch has no effect on MPEG-1 PS data.\n" " -notdvd, -nodvd The PS data is not from a DVD.\n" " The DVD specification stores AC-3 (Dolby), DTS and\n" " other audio in a specialised manner in private_stream_1.\n" " -vstream Take video from video stream (0..7).\n" " The default is the first video stream found.\n" " -astream Take audio from audio stream (0..31).\n" " The default is the first audio stream found\n" " (this includes private_stream_1 on non-DVD streams).\n" " -ac3stream Take audio from AC3 substream (0..7), from\n" " private_stream_1. This implies -dvd.\n" " (If audio is being taken from a substream, the user\n" " is assumed to have determined which one is wanted,\n" " e.g., using psreport)\n" "\n" "Output switches:\n" " -err stdout Write error messages to standard output (the default)\n" " -err stderr Write error messages to standard error (Unix traditional)\n" " -stdout Write output to , instead of a named file\n" " Forces -quiet and -err stderr.\n" " -host , -host :\n" " Writes output (over TCP/IP) to the named ,\n" " instead of to a named file. If is not\n" " specified, it defaults to 88.\n" " -vpid is the video PID to use for the data.\n" " Use '-vpid 0x' to specify a hex value.\n" " Defaults to 0x68.\n" " -apid is the audio PID to use for the data.\n" " Use '-apid 0x' to specify a hex value.\n" " Defaults to 0x67.\n" " -noaudio Don't output the audio data\n" " -pmt is the PMT PID to use.\n" " Use '-pmt 0x' to specify a hex value.\n" " Defaults to 0x66\n" " -prepeat Output the program data (PAT/PMT) after every \n" " PS packs. Defaults to 100.\n" " -pad Pad the start with filler TS packets, to allow\n" " a TS reader to synchronize with the datastream.\n" " Defaults to 8.\n" "\n" "General switches:\n" " -verbose, -v Print a 'v' for each video packet and an 'a' for \n" " each audio packet, as it is read\n" " -quiet, -q Only output error messages\n" " -max , -m Maximum number of PS packs to read\n" "\n" "Stream type:\n" " When the TS data is being output, it is flagged to indicate whether\n" " it conforms to H.262, H.264, etc. It is important to get this right, as\n" " it will affect interpretation of the TS data.\n" "\n" " If input is from a file, then the program will look at the start of\n" " the file to determine if the stream is H.264 or H.262 data. This\n" " process may occasionally come to the wrong conclusion, in which case\n" " the user can override the choice using the following switches.\n" "\n" " If input is from standard input (via -stdin), then it is not possible\n" " for the program to make its own decision on the input stream type.\n" " Instead, it defaults to H.262, and relies on the user indicating if\n" " this is wrong.\n" "\n" " -h264, -avc Force the program to treat the input as MPEG-4/AVC.\n" " -h262 Force the program to treat the input as MPEG-2.\n" " -mp42 Force the program to treat the input as MPEG-4/Part 2.\n" " -vtype Force the program to treat the input as video of\n" " stream type (e.g., 0x42 means AVS video). It is\n" " up to the user to specify a valid .\n" "\n" " If the audio stream being output is Dolby (AC-3), then the stream type\n" " used to output it differs for DVB (European) and ATSC (USA) data. It\n" " may be specified as follows:\n" "\n" " -dolby dvb Use stream type 0x06 (the default)\n" " -dolby atsc Use stream type 0x81\n" ); } int main(int argc, char **argv) { int use_stdin = FALSE; int use_stdout = FALSE; int use_tcpip = FALSE; int port = 88; // Useful default port number char *input_name = NULL; char *output_name = NULL; int had_input_name = FALSE; int had_output_name = FALSE; PS_reader_p ps = NULL; TS_writer_p output = NULL; int verbose = FALSE; int quiet = FALSE; int max = 0; uint32_t pmt_pid = 0x66; uint32_t video_pid = 0x68; uint32_t pcr_pid = video_pid; // Use PCRs from the video stream uint32_t audio_pid = 0x67; int keep_audio = TRUE; int repeat_program_every = 100; int pad_start = 8; int err = 0; int ii = 1; int video_type = VIDEO_H262; // hopefully a sensible default int force_stream_type = FALSE; int video_stream = -1; int audio_stream = -1; int want_ac3_audio = FALSE; int input_is_dvd = TRUE; int want_dolby_as_dvb = TRUE; if (argc < 2) { print_usage(); return 0; } while (ii < argc) { if (argv[ii][0] == '-') { if (!strcmp("--help",argv[ii]) || !strcmp("-help",argv[ii]) || !strcmp("-h",argv[ii])) { print_usage(); return 0; } else if (!strcmp("-avc",argv[ii]) || !strcmp("-h264",argv[ii])) { force_stream_type = TRUE; video_type = VIDEO_H264; } else if (!strcmp("-h262",argv[ii])) { force_stream_type = TRUE; video_type = VIDEO_H262; } else if (!strcmp("-vtype",argv[ii])) { CHECKARG("ps2ts",ii); err = int_value("ps2ts",argv[ii],argv[ii+1],TRUE,0, &video_type); if (err) return 1; ii++; force_stream_type = TRUE; } else if (!strcmp("-mp42",argv[ii])) { force_stream_type = TRUE; video_type = VIDEO_MPEG4_PART2; } else if (!strcmp("-dolby",argv[ii])) { CHECKARG("ps2ts",ii); if (!strcmp("dvb",argv[ii+1])) want_dolby_as_dvb = TRUE; else if (!strcmp("atsc",argv[ii+1])) want_dolby_as_dvb = FALSE; else { print_err("### ps2ts: -dolby must be followed by dvb or atsc\n"); return 1; } ii++; } else if (!strcmp("-stdin",argv[ii])) { had_input_name = TRUE; // more or less use_stdin = TRUE; } else if (!strcmp("-stdout",argv[ii])) { had_output_name = TRUE; // more or less use_stdout = TRUE; redirect_output_stderr(); } else if (!strcmp("-err",argv[ii])) { CHECKARG("ps2ts",ii); if (!strcmp(argv[ii+1],"stderr")) redirect_output_stderr(); else if (!strcmp(argv[ii+1],"stdout")) redirect_output_stdout(); else { fprint_err("### ps2ts: " "Unrecognised option '%s' to -err (not 'stdout' or" " 'stderr')\n",argv[ii+1]); return 1; } ii++; } else if (!strcmp("-dvd",argv[ii])) { input_is_dvd = TRUE; } else if (!strcmp("-notdvd",argv[ii]) || !strcmp("-nodvd",argv[ii])) { input_is_dvd = FALSE; } else if (!strcmp("-host",argv[ii])) { CHECKARG("ps2ts",ii); err = host_value("ps2ts",argv[ii],argv[ii+1],&output_name,&port); if (err) return 1; had_output_name = TRUE; // more or less use_tcpip = TRUE; ii++; } else if (!strcmp("-verbose",argv[ii]) || !strcmp("-v",argv[ii])) { verbose = TRUE; quiet = FALSE; } else if (!strcmp("-quiet",argv[ii]) || !strcmp("-q",argv[ii])) { verbose = FALSE; quiet = TRUE; } else if (!strcmp("-max",argv[ii]) || !strcmp("-m",argv[ii])) { CHECKARG("ps2ts",ii); err = int_value("ps2ts",argv[ii],argv[ii+1],TRUE,10,&max); if (err) return 1; ii++; } else if (!strcmp("-prepeat",argv[ii])) { CHECKARG("ps2ts",ii); err = int_value("ps2ts",argv[ii],argv[ii+1],TRUE,10, &repeat_program_every); if (err) return 1; ii++; } else if (!strcmp("-pad",argv[ii])) { CHECKARG("ps2ts",ii); err = int_value("ps2ts",argv[ii],argv[ii+1],TRUE,10,&pad_start); if (err) return 1; ii++; } else if (!strcmp("-vpid",argv[ii])) { CHECKARG("ps2ts",ii); err = unsigned_value("ps2ts",argv[ii],argv[ii+1],0,&video_pid); if (err) return 1; ii++; } else if (!strcmp("-apid",argv[ii])) { CHECKARG("ps2ts",ii); err = unsigned_value("ps2ts",argv[ii],argv[ii+1],0,&audio_pid); if (err) return 1; ii++; } else if (!strcmp("-pmt",argv[ii])) { CHECKARG("ps2ts",ii); err = unsigned_value("ps2ts",argv[ii],argv[ii+1],0,&pmt_pid); if (err) return 1; ii++; } else if (!strcmp("-noaudio",argv[ii])) { keep_audio = FALSE; } else if (!strcmp("-vstream",argv[ii])) { CHECKARG("ps2ts",ii); err = int_value_in_range("ps2ts",argv[ii],argv[ii+1],0,0xF,0, &video_stream); if (err) return 1; ii++; } else if (!strcmp("-astream",argv[ii])) { CHECKARG("ps2ts",ii); err = int_value_in_range("ps2ts",argv[ii],argv[ii+1],0,0x1F,0, &audio_stream); if (err) return 1; want_ac3_audio = FALSE; ii++; } else if (!strcmp("-ac3stream",argv[ii])) { CHECKARG("ps2ts",ii); err = int_value_in_range("ps2ts",argv[ii],argv[ii+1],0,0x7,0, &audio_stream); if (err) return 1; want_ac3_audio = TRUE; input_is_dvd = TRUE; ii++; } else { fprint_err("### ps2ts: " "Unrecognised command line switch '%s'\n",argv[ii]); return 1; } } else { if (had_input_name && had_output_name) { fprint_err("### ps2ts: Unexpected '%s'\n",argv[ii]); return 1; } else if (had_input_name) { output_name = argv[ii]; had_output_name = TRUE; } else { input_name = argv[ii]; had_input_name = TRUE; } } ii++; } if (!had_input_name) { print_err("### ps2ts: No input file specified\n"); return 1; } if (!had_output_name) { print_err("### ps2ts: No output file specified\n"); return 1; } // Try to stop extraneous data ending up in our output stream if (use_stdout) { verbose = FALSE; quiet = TRUE; } err = open_PS_file(input_name,quiet,&ps); if (err) { fprint_err("### ps2ts: Unable to open input %s\n", (use_stdin?"":input_name)); return 1; } if (!quiet) fprint_msg("Reading from %s\n",(use_stdin?"":input_name)); // Try to decide what sort of data stream we have if (force_stream_type || use_stdin) { if (!quiet) fprint_msg("Reading input as %s (0x%02x)\n", h222_stream_type_str(video_type),video_type); } else { err = determine_PS_video_type(ps,&video_type); if (err) return 1; if (!quiet) fprint_msg("Video appears to be %s (0x%02x)\n", h222_stream_type_str(video_type),video_type); } if (!quiet) { if (input_is_dvd) print_msg("Treating input as from DVD\n"); else print_msg("Treating input as NOT from DVD\n"); print_msg("Reading video from "); if (video_stream == -1) print_msg("first stream found"); else fprint_msg("stream %0#x (%d)",video_stream,video_stream); if (keep_audio) { print_msg(", audio from "); if (audio_stream == -1) fprint_msg("first %s found",(want_ac3_audio?"AC3 stream":"stream")); else fprint_msg("%s %0#x (%d)",(want_ac3_audio?"AC3 stream":"stream"), audio_stream,audio_stream); print_msg("\n"); } fprint_msg("Writing video with PID 0x%02x",video_pid); if (keep_audio) fprint_msg(", audio with PID 0x%02x,",audio_pid); fprint_msg(" PMT PID 0x%02x, PCR PID 0x%02x\n",pmt_pid,pcr_pid); if (max) fprint_msg("Stopping after %d program stream packets\n",max); } if (use_stdout) err = tswrite_open(TS_W_STDOUT,NULL,NULL,0,quiet,&output); else if (use_tcpip) err = tswrite_open(TS_W_TCP,output_name,NULL,port,quiet,&output); else err = tswrite_open(TS_W_FILE,output_name,NULL,0,quiet,&output); if (err) { fprint_err("### ps2ts: Unable to open %s\n",output_name); (void) close_PS_file(&ps); return 1; } err = ps_to_ts(ps,output,pad_start,repeat_program_every, video_type,input_is_dvd, video_stream,audio_stream,want_ac3_audio, want_dolby_as_dvb,pmt_pid,pcr_pid,video_pid, keep_audio,audio_pid,max,verbose,quiet); if (err) { print_err("### ps2ts: Error transferring data\n"); (void) close_PS_file(&ps); (void) tswrite_close(output,TRUE); return 1; } // And tidy up when we're finished err = tswrite_close(output,quiet); if (err) fprint_err("### ps2ts: Error closing output %s: %s\n",output_name, strerror(errno)); err = close_PS_file(&ps); if (err) fprint_err("### ps2ts: Error closing input %s\n", (use_stdin?"":input_name)); return 0; } // Local Variables: // tab-width: 8 // indent-tabs-mode: nil // c-basic-offset: 2 // End: // vim: set tabstop=8 shiftwidth=2 expandtab: