/* * Support for writing out TS packets, to file, or over TCP/IP or UDP * * When writing asynchronously, provides automated producer/consumer * behaviour via a circular buffer, optionally taking timing from the * TS PCR entries. * * ***** 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 ***** */ #ifndef _tswrite_defns #define _tswrite_defns #include "compat.h" #include "ts_defns.h" #include "h222_defns.h" #ifdef _WIN32 #include // for definition of SOCKET #else typedef int SOCKET; // for compatibility with Windows #include // for struct termios #endif // ============================================================ // CIRCULAR BUFFER // ============================================================ // We default to using a "packet" of 7 transport stream packets because 7*188 = // 1316, but 8*188 = 1504, and we would like to output as much data as we can // that is guaranteed to fit into a single ethernet packet, size 1500. #define DEFAULT_TS_PACKETS_IN_ITEM 7 // For simplicity, we'll have a maximum on that (it allows us to have static // array sizes in some places). This should be a big enough size to more than // fill a jumbo packet on a gigabit network. #define MAX_TS_PACKETS_IN_ITEM 100 // ------------------------------------------------------------ // A circular buffer, usable as a queue // // We "waste" one buffer item so that we don't have to maintain a count // of items in the buffer // // To get an understanding of how it works, choose a small BUFFER_SIZE // (e.g., 11), enable DISPLAY_BUFFER, and select --visual - this will show the // reading/writing of the circular buffer in action, including the // "unused item". // // The data for the circular buffer // Each circular buffer item "contains" (up to) N TS packets (where N defaults // to 7, and is specified as `item_size` in the circular buffer header), and a // time (in microseconds) when we would like it to be output (relative to the // time for the first packet "sent"). // // Said data is stored at the address indicated by the circular buffer // "header", as `item_data`. // struct circular_buffer_item { uint32_t time; // when we would like this data output int discontinuity; // TRUE if our timeline has "broken" int length; // number of bytes of data in the array }; typedef struct circular_buffer_item *circular_buffer_item_p; #define SIZEOF_CIRCULAR_BUFFER_ITEM sizeof(struct circular_buffer_item) // ------------------------------------------------------------ // The header for the circular buffer // // Note that `start` is only ever written to by the child process, and this is // the only thing that the child process ever changes in the circular buffer. // // `maxnowait` is the maximum number of packets to send to the target host // without forcing an intermediate wait - required to stop us "swamping" the // target with too much data, and overrunning its buffers. struct circular_buffer { int start; // start of data "pointer" int end; // end of data "pointer" (you guessed) int size; // the actual length of the `item` array int TS_in_item; // max number of TS packets in a circular buffer item int item_size; // and thus the size of said item's data array int maxnowait; // max number consecutive packets to send with no wait int waitfor; // the number of microseconds to wait thereafter // The location of the packet data for the circular buffer items byte *item_data; // The "header" data for each circular buffer item struct circular_buffer_item item[]; }; typedef struct circular_buffer *circular_buffer_p; // Note that size doesn't include the final `item` #define SIZEOF_CIRCULAR_BUFFER sizeof(struct circular_buffer) #define DEFAULT_CIRCULAR_BUFFER_SIZE 1024 // used to be 100 // ============================================================ // BUFFERED OUTPUT // ============================================================ // Information about each TS packet in our circular buffer item struct TS_packet_info { int index; uint32_t pid; // do we need the PIDs? int got_pcr; uint64_t pcr; }; typedef struct TS_packet_info *TS_packet_info_p; #define SIZEOF_TS_PACKET_INFO sizeof(struct TS_packet_info); // If we're going to support output via our circular buffer in a manner // similar to that for output to a file or socket, then we need a structure // to maintain the relevant information. It seems a bit wasteful to burden // the circular buffer itself with this, particularly as only the writer // cares about this data, so it needn't be shared. struct buffered_TS_output { circular_buffer_p buffer; int which; // Which buffer index we're writing to int started; // TRUE if we've started writing therein // For each TS packet in the circular buffer, remember its `count` // within the input stream, whether it had a PCR, and if so what that // PCR was. To make it simpler to access these arrays, also keep a fill // index into them (the alternative would be to always re-zero the // `got_pcr` values whenever we start a new circular buffer entry, // which would be a pain...) int num_packets; // how many TS packets we've got struct TS_packet_info packet[MAX_TS_PACKETS_IN_ITEM]; // `rate` is the rate (in bytes per second) we would like to output data at uint32_t rate; // `pcr_scale` is a multiplier for PCRs - each PCR found gets its value // multiplied by this double pcr_scale; // `use_pcrs` indicates if we should use PCRs in the data to drive our // timing, rather than use the specified byte rate directly. The `priming` // values are only relevant if `use_pcrs` is true. int use_pcrs; // 'prime_size' is the amount of space/time to 'prime' the circular buffer // output timing mechanism with. This is effectively multiples of the // size of a circular buffer item. int prime_size; // Percentage "too fast" speedup for our priming rate int prime_speedup; }; typedef struct buffered_TS_output *buffered_TS_output_p; #define SIZEOF_BUFFERED_TS_OUTPUT sizeof(struct buffered_TS_output) // ============================================================ // EXTERNAL DATASTRUCTURES - these are *intended* for external use // ============================================================ // Our supported target types // On Unix-type systems, there is little distinction between file and // socket, but on Windows this becomes more interesting enum TS_writer_type { TS_W_UNDEFINED, TS_W_STDOUT, // standard output TS_W_FILE, // a file TS_W_TCP, // a socket, over TCP/IP TS_W_UDP, // a socket, over UDP }; typedef enum TS_writer_type TS_WRITER_TYPE; // ------------------------------------------------------------ // So, *is* it a file or a socket? union TS_writer_output { FILE *file; SOCKET socket; }; // ------------------------------------------------------------ // A datastructure to allow us to write to various different types of target // // When writing to a file, "how" will be TS_W_STDOUT or TS_W_FILE, and // "where" will be the appropriate file interface. "writer" is not necessary // (there's no point in putting a circular buffer and other stuff above // the file writes), and no child process is needed. // // When writing over UDP, "how" will be TS_W_UDP, and "where" will be the // socket that is being written to. For UDP, timing needs to be managed, and // thus the circular buffer support is necessary, so "writer" should be // set to a buffered output context. Since the circular buffer is being // used, there will also be a child process. // // When writing over TCP/IP, "how" will be TS_W_TCP, and "where" will be the // socket that is being written to. Timing is not an issue, so "writer" will // not be needed, and nor will there be a child process. However, it is // possible that we will want to respond to commands (over the same or another // socket (or, on Linux/BSD, file descriptor)), so "commander" may be set. struct TS_writer { enum TS_writer_type how; // what type of output we want union TS_writer_output where; // where it's going to buffered_TS_output_p writer; // our buffered output interface, if needed int count; // a count of how many TS packets written // Support for the child fork/thread, which actually does the writing when // buffered output is enabled. #ifdef _WIN32 HANDLE child; // the handle for the child thread (if any) #else // _WIN32 pid_t child; // the PID of the child process (if any) #endif // _WIN32 int quiet; // Should the child be as quiet as possible? // Support for "commands" being sent to us via a socket (or, on Linux/BSD, // from any other file descriptor). The "normal" way this is used is for // our application (tsserve) to act as a server, listening on a socket // for an incoming connection, and then both playing data to that // connection, and listening for commands from it. int server; // are we acting as a server? SOCKET command_socket; // where to read commands from/through // When the user sends a new command (a different character than is // currently in `command`), the underpinnings of tswrite_write() set // `command` to that command letter, and `command_changed` to TRUE. // Various key functions that write to TS check `command_changed`, and // return COMMAND_RETURN_CODE if it is true. // Note, however, that it is left up to the top level to *unset* // `command_changed` again. byte command; // A single character "command" for what to do int command_changed; // Has it changed? // Some commands (notably, the "skip" commands) want to be atomic - that // is, they should not be interrupted by the user "typing ahead". Since // the fast forward and reverse mechanisms (used for skipping as well) // call tswrite_command_changed() to tell if there is a new command that // should interrup them, we can provide a flag to say "don't do that"... int atomic_command; // Should some TS packets be thrown away every packets? This can be // useful for debugging other applications int drop_packets; // 0 to keep all packets, otherwise keep packets int drop_number; // and then drop this many }; typedef struct TS_writer *TS_writer_p; #define SIZEOF_TS_WRITER sizeof(struct TS_writer) // ------------------------------------------------------------ // Command letters #define COMMAND_NOT_A_COMMAND '_' // A guaranteed non-command letter #define COMMAND_QUIT 'q' // quit/exit #define COMMAND_NORMAL 'n' // normal playing speed #define COMMAND_PAUSE 'p' // pause until another command #define COMMAND_FAST 'f' // fast forward #define COMMAND_FAST_FAST 'F' // faster forward #define COMMAND_REVERSE 'r' // reverse/rewind #define COMMAND_FAST_REVERSE 'R' // faster reverse/rewind #define COMMAND_SKIP_FORWARD '>' // aim at 10s #define COMMAND_SKIP_BACKWARD '<' // ditto #define COMMAND_SKIP_FORWARD_LOTS ']' // aim at 100s #define COMMAND_SKIP_BACKWARD_LOTS '[' // ditto #define COMMAND_SELECT_FILE_0 '0' #define COMMAND_SELECT_FILE_1 '1' #define COMMAND_SELECT_FILE_2 '2' #define COMMAND_SELECT_FILE_3 '3' #define COMMAND_SELECT_FILE_4 '4' #define COMMAND_SELECT_FILE_5 '5' #define COMMAND_SELECT_FILE_6 '6' #define COMMAND_SELECT_FILE_7 '7' #define COMMAND_SELECT_FILE_8 '8' #define COMMAND_SELECT_FILE_9 '9' // And a "return code" that means "the command character has changed" #define COMMAND_RETURN_CODE -999 // ------------------------------------------------------------ // Context for use in decoding command line - see `tswrite_process_args()` struct TS_context { // Values used in setting up buffered output int circ_buf_size; // number of buffer entries (+1) for circular buffer int TS_in_item; // number of TS packets in each circular buffer item int maxnowait; // max number of packets to send without waiting int waitfor; // the number of microseconds to wait thereafter int bitrate; // suggested bit rate (byterate*8) - both are given int byterate; // suggested byte rate (bitrate/8) - for convenience int use_pcrs; // use PCRs for timing information? int prime_size; // initial priming size for buffered output int prime_speedup; // percentage of normal speed to prime with double pcr_scale; // multiplier for PCRs -- see buffered_TS_output }; typedef struct TS_context *TS_context_p; // Arguments processed by tswrite_process_args are set to: #define TSWRITE_PROCESSED "" #endif // _tswrite_defns // Local Variables: // tab-width: 8 // indent-tabs-mode: nil // c-basic-offset: 2 // End: // vim: set tabstop=8 shiftwidth=2 expandtab: