/*
 * Prototypes for handling H.264 elementary streams.
 *
 * ***** 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 _es_fns
#define _es_fns

#include "es_defns.h"

// ============================================================
// Elementary stream functions - basic
// ============================================================

/*
 * Open an ES file and build an elementary stream datastructure to read
 * it with.
 *
 * - `filename` is the ES files name
 *
 * Opens the file for read, builds the datastructure, and reads the first 3
 * bytes of the input file (this is done to prime the triple-byte search
 * mechanism).
 *
 * Use `close_elementary_stream` to close the stream and the file.
 *
 * Returns 0 if all goes well, 1 otherwise.
 */
extern int open_elementary_stream(char  *filename,
                                  ES_p  *es);

/*
 * Build an elementary stream datastructure attached to an input file.
 * This is intended for reading ES data files.
 *
 * - `input` is the file stream to read from.
 *
 * Builds the datastructure, and reads the first 3 bytes of the input
 * file (this is done to prime the triple-byte search mechanism).
 *
 * Use `free_elementary_stream` to release the ES context without closing
 * the associated file.
 *
 * Returns 0 if all goes well, 1 otherwise.
 */
extern int build_elementary_stream_file(int    input,
                                        ES_p  *es);


/*
 * Build an elementary stream datastructure for use with a PES reader.
 * Reads the first (or next) three bytes of the ES.
 *
 * This reads data from the PES video data, ignoring any audio data.
 *
 * - `reader` is the PES reader we want to use to read our TS or PS data.
 *
 * The caller must explicitly close the PES reader as well as closing the
 * elementary stream (closing the ES does not affect the PES reader).
 *
 * Returns 0 if all goes well, 1 otherwise.
 */
extern int build_elementary_stream_PES(PES_reader_p  reader,
                                       ES_p         *es);

/*
 * Tidy up the elementary stream datastructure after we've finished with it.
 *
 * Specifically:
 *
 * - free the datastructure
 * - set `es` to NULL
 *
 * No return status is given, since there's not much one can do if anything
 * *did* go wrong, and if something went wrong and the program is continuing,
 * it's bound to show up pretty soon.
 */
extern void free_elementary_stream(ES_p  *es);

/*
 * Tidy up the elementary stream datastructure after we've finished with it.
 *
 * Specifically:
 *
 * - close the input file (if its stream is set, and if it's not STDIN)
 * - call `free_elementary_stream()`
 *
 * No return status is given, since there's not much one can do if anything
 * *did* go wrong, and if something went wrong and the program is continuing,
 * it's bound to show up pretty soon.
 */
extern void close_elementary_stream(ES_p  *es);

/*
 * Ask an ES context if changed input is available.
 *
 * This is a convenience wrapper to save querying the ES context to see
 * if it is (a) reading from PES, (b) automatically writing the PES packets
 * out via a TS writer, and (c) if said TS writer has a changed command.
 *
 * Calls `tswrite_command_changed()` on the TS writer associated with this ES.
 *
 * Returns TRUE if there is a changed command.
 */
extern int es_command_changed(ES_p  es);


// ============================================================
// Elementary stream functions - item/unit reading
// ============================================================
/*
 * Prepare the contents of a (new) ES unit datastructure.
 *
 * Allocates a new data array, and unsets the counts.
 *
 * Returns 0 if it succeeds, 1 if some error occurs.
 */
extern int setup_ES_unit(ES_unit_p  unit);

/*
 * Tidy up an ES unit datastructure after we've finished with it.
 *
 * (Frees the internal data array, and unsets the counts)
 */
extern void clear_ES_unit(ES_unit_p  unit);

/*
 * Build a new ES unit datastructure.
 *
 * Returns 0 if it succeeds, 1 if some error occurs.
 */
extern int build_ES_unit(ES_unit_p  *unit);

/*
 * Build a new ES unit datastructure, from a given data array.
 *
 * Takes a copy of 'data'. Sets 'start_code' appropriately,
 * sets 'start_posn' to (0,0), and 'PES_had_PTS' to FALSE.
 *
 * Returns 0 if it succeeds, 1 if some error occurs.
 */
extern int build_ES_unit_from_data(ES_unit_p  *unit,
                                   byte       *data,
                                   uint32_t    data_len);

/*
 * Tidy up and free an ES unit datastructure after we've finished with it.
 *
 * Empties the ES unit datastructure, frees it, and sets `unit` to NULL.
 *
 * If `unit` is already NULL, does nothing.
 */
extern void free_ES_unit(ES_unit_p  *unit);

/*
 * Print out some information this ES unit, on normal or error output
 */
extern void report_ES_unit(int        is_msg,
                           ES_unit_p  unit);

/*
 * Retrieve ES data from the end of a PES packet. It is assumed (i.e, things
 * will go wrong if it is not true) that at least one ES unit has been read
 * from the PES data stream via the ES reader.
 *
 * - `es` is our ES reader. It must be reading ES from PES packets.
 * - `data` is the ES data remaining (to be read) in the current PES packet.
 *   It is up to the caller to free this data.
 * - `data_len` is the length of said data. If this is 0, then `data`
 *   will be NULL.
 *
 * Returns 0 if all goes well, 1 if an error occurs.
 */
extern int get_end_of_underlying_PES_packet(ES_p        es,
                                            byte      **data,
                                            int        *data_len);

/*
 * Find and read in the next ES unit.
 *
 * In general, unless there are compelling reasons, use
 * `find_and_build_next_ES_unit()` instead.
 *
 * - `es` is the elementary stream we're reading from.
 * - `unit` is the datastructure into which to read the ES unit
 *   - any previous content will be lost.
 *
 * Returns 0 if it succeeds, EOF if the end-of-file is read (i.e., there
 * is no next ES unit), otherwise 1 if some error occurs.
 */
extern int find_next_ES_unit(ES_p       es,
                             ES_unit_p  unit);

/*
 * Find and read the next ES unit into a new datastructure.
 *
 * - `es` is the elementary stream we're reading from.
 * - `count` is an integer to use as an id for this ES unit - typically
 *   its index in the input stream
 * - `unit` is the datastructure containing the ES unit found, or NULL
 *   if there was none.
 *
 * Returns 0 if it succeeds, EOF if the end-of-file is read (i.e., there
 * is no next ES unit), otherwise 1 if some error occurs.
 */
extern int find_and_build_next_ES_unit(ES_p        es,
                                       ES_unit_p  *unit);

/*
 * Write (copy) the current ES unit to the output stream.
 *
 * Note that it writes out all of the data for this ES unit,
 * including its 00 00 01 start code prefix.
 *
 * - `output` is the output stream (file descriptor) to write to
 * - `unit` is the ES unit to write
 *
 * Returns 0 if all went well, 1 if something went wrong.
 */
extern int write_ES_unit(FILE      *output,
                         ES_unit_p  unit);


// ------------------------------------------------------------
// Arbitrary reading from ES data
// ------------------------------------------------------------
/*
 * "Seek" to the given position in the ES data, which is assumed to
 * be an offset ready to read a 00 00 01 sequence.
 *
 * If the ES reader is using PES to read its data, then both fields
 * of `where` are significant, but if the underlying file *is* just a file,
 * only `where.infile` is used.
 *
 * Returns 0 if all went well, 1 is something went wrong
 */
extern int seek_ES(ES_p       es,
                   ES_offset  where);
/*
 * Read in some ES data from disk.
 *
 * Suitable for use when reading in a set of ES units whose bounds
 * (start offset and total number of bytes) have been remembered.
 *
 * "Seeks" to the given position in the ES data, which is assumed to
 * be an offset ready to read a 00 00 01 sequence, and reads data thereafter.
 *
 * After this function, the triple byte context is set to FF FF FF, and the
 * position of said bytes are undefined, but the next position to read a byte
 * from *is* defined.
 *
 * The intent is to allow the caller to have a data array (`data`) that
 * always contains the last data read, and is of the required size, and
 * need only be freed when no more data is needed.
 *
 * - `es` is where to read our data from
 * - `start_posn` is the file offset to start reading at
 * - `num_bytes` is how many bytes we want to read
 * - `data_len` may be NULL or a pointer to a value.
 *   If it is NULL, then the data array will be reallocated to size
 *   `num_bytes` regardless. If it is non-NULL, it should be passed *in*
 *    as the size that `data` *was*, and will be returned as the size
 *    that `data` is when the function returns.
 * - `data` is the data array to read into. If this is NULL, or if `num_bytes`
 *   is NULL, or if `num_bytes` is greater than `data_len`, then it will be
 *   reallocated to size `num_bytes`.
 *
 * Returns 0 if all went well, 1 if something went wrong.
 */
extern int read_ES_data(ES_p       es,
                        ES_offset  start_posn,
                        uint32_t   num_bytes,
                        uint32_t  *data_len,
                        byte     **data);


// ============================================================
// Lists of ES units
// ============================================================
/*
 * Build a new list-of-ES-units datastructure.
 *
 * Returns 0 if it succeeds, 1 if some error occurs.
 */
extern int build_ES_unit_list(ES_unit_list_p  *list);

/*
 * Add a copy of an ES unit to the end of the ES unit list
 *
 * Note that since this takes a copy of the ES unit's data, it is safe
 * to free the original ES unit.
 *
 * Returns 0 if it succeeds, 1 if some error occurs.
 */
extern int append_to_ES_unit_list(ES_unit_list_p  list,
                                  ES_unit_p       unit);

/*
 * Reset (empty) an ES unit list.
 */
extern void reset_ES_unit_list(ES_unit_list_p  list);

/*
 * Tidy up and free an ES unit list datastructure after we've finished with it.
 *
 * Clears the datastructure, frees it and returns `list` as NULL.
 *
 * Does nothing if `list` is already NULL.
 */
extern void free_ES_unit_list(ES_unit_list_p  *list);

/*
 * Report on an ES unit list's contents.
 *
 * - `name` is the name of the list (used in the header)
 * - `list` is the list to report on
 */
extern void report_ES_unit_list(char              *name,
                                ES_unit_list_p     list);

/*
 * Retrieve the bounds of this ES unit list in the file it was read from.
 *
 * - `list` is the ES unit list we're interested in
 * - `start` is its start position (i.e., the location at which to start
 *   reading to retrieve all of the data for the list)
 * - `length` is the total length of the ES units within this list
 *
 * Returns 0 if all goes well, 1 if the ES unit list has no content.
 */
extern int get_ES_unit_list_bounds(ES_unit_list_p   list,
                                   ES_offset       *start,
                                   uint32_t        *length);
/*
 * Compare two ES unit lists. The comparison does not include the start
 * position of the unit data, but just the actual data - i.e., two unit lists
 * read from different locations in the input stream may be considered the
 * same if their data content is identical.
 *
 * - `list1` and `list2` are the two ES unit lists to compare.
 *
 * Returns TRUE if the lists contain identical content, FALSE otherwise.
 */
extern int same_ES_unit_list(ES_unit_list_p  list1,
                             ES_unit_list_p  list2);

/*
 * Compare two ES offsets
 *
 * Returns -1 if offset1 < offset2, 0 if they are the same, and 1 if
 * offset1 > offset2.
 */
extern int compare_ES_offsets(ES_offset  offset1,
                              ES_offset  offset2);

// ============================================================
// Simple file type guessing
// ============================================================
/*
 * Look at the start of an elementary stream to try to determine its
 * video type.
 *
 * "Eats" the ES units that it looks at, and doesn't rewind the stream
 * afterwards.
 *
 * - `es` is the ES file
 * - if `print_dots` is true, print a dot for each ES unit that is inspected
 * - if `show_reasoning` is true, then output messages explaining how the
 *   decision is being made
 * - `video_type` is the final decision -- one of VIDEO_H264, VIDEO_H262,
 *   VIDEO_AVS, or VIDEO_UNKNOWN.
 *
 * Returns 0 if all goes well, 1 if something goes wrong
 */
extern int decide_ES_video_type(ES_p  es,
                                int   print_dots,
                                int   show_reasoning,
                                int  *video_type);
/*
 * Look at the start of an elementary stream to try to determine it's
 * video type.
 *
 * Note that it is easier to prove something is H.262 (or AVS) than to prove
 * that it is H.264, and that the result of this routine is a best-guess, not a
 * guarantee.
 *
 * Rewinds back to the original position in the file after it has finished.
 *
 * - `input` is the file to look at
 * - if `print_dots` is true, print a dot for each ES unit that is inspected
 * - if `show_reasoning` is true, then output messages explaining how the
 *   decision is being made
 * - `video_type` is the final decision -- one of VIDEO_H264, VIDEO_H262,
 *   VIDEO_AVS, or VIDEO_UNKNOWN.
 *
 * Returns 0 if all goes well, 1 if something goes wrong
 */
extern int decide_ES_file_video_type(int   input,
                                     int   print_dots,
                                     int   show_reasoning,
                                     int  *video_type);
#endif // _es_fns

// Local Variables:
// tab-width: 8
// indent-tabs-mode: nil
// c-basic-offset: 2
// End:
// vim: set tabstop=8 shiftwidth=2 expandtab:
