/* * Datastructures and prototypes for reading H.262 (MPEG-2) 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 ***** */ #include #include #include #include #include "compat.h" #include "printing_fns.h" #include "h262_fns.h" #include "es_fns.h" #include "ts_fns.h" #include "reverse_fns.h" #include "misc_fns.h" #define DEBUG_GET_NEXT_PICTURE 0 #define DEBUG_AFD 0 /* * Print out information derived from the start code * * Note that if a "SYSTEM START" code is reported, then the data is * likely to be PES or Transport Stream data, not Elementary Stream. * * Similarly, if a "TRANSPORT STREAM sync byte" is reported, then * the stream is probably Transport Stream. * * If the stream is *not* Elementary Stream data, then it is possible * that some of the apparent start code prefixes are actually false * detections. */ extern void print_h262_start_code_str(byte start_code) { byte number; char *str = NULL; switch (start_code) { // H.262 start codes case 0x00: str = "Picture"; break; case 0xB0: str = "Reserved"; break; case 0xB1: str = "Reserved"; break; case 0xB2: str = "User data"; break; case 0xB3: str = "SEQUENCE HEADER"; break; case 0xB4: str = "Sequence error"; break; case 0xB5: str = "Extension start"; break; case 0xB6: str = "Reserved"; break; case 0xB7: str = "SEQUENCE END"; break; case 0xB8: str = "Group start"; break; // System start codes - 13818-1 p32 Table 2-18 stream_id // If these occur, then we're seeing PES headers // - maybe we're looking at transport stream data? case 0xBC: str = "SYSTEM START: Program stream map"; break; case 0xBD: str = "SYSTEM START: Private stream 1"; break; case 0xBE: str = "SYSTEM START: Padding stream"; break; case 0xBF: str = "SYSTEM START: Private stream 2"; break; case 0xF0: str = "SYSTEM START: ECM stream"; break; case 0xF1: str = "SYSTEM START: EMM stream"; break; case 0xF2: str = "SYSTEM START: DSMCC stream"; break; case 0xF3: str = "SYSTEM START: 13522 stream"; break; case 0xF4: str = "SYSTEM START: H.222 A stream"; break; case 0xF5: str = "SYSTEM START: H.222 B stream"; break; case 0xF6: str = "SYSTEM START: H.222 C stream"; break; case 0xF7: str = "SYSTEM START: H.222 D stream"; break; case 0xF8: str = "SYSTEM START: H.222 E stream"; break; case 0xF9: str = "SYSTEM START: Ancillary stream"; break; case 0xFF: str = "SYSTEM START: Program stream directory"; break; default: str = NULL; break; } if (str != NULL) print_msg(str); else if (start_code == 0x47) print_msg("TRANSPORT STREAM sync byte"); else if (start_code >= 0x01 && start_code <= 0xAF) fprint_msg("Slice, vertical posn %d",start_code); else if (start_code >= 0xC0 && start_code <=0xDF) { number = start_code & 0x1F; fprint_msg("SYSTEM START: Audio stream %02x",number); } else if (start_code >= 0xE0 && start_code <= 0xEF) { number = start_code & 0x0F; fprint_msg("SYSTEM START: Video stream %x",number); } else if (start_code >= 0xFC && start_code <= 0xFE) print_msg("SYSTEM START: Reserved data stream"); else print_msg("SYSTEM START: Unrecognised stream id"); } /* * Build a new MPEG2 item datastructure. * * Returns 0 if it succeeds, 1 if some error occurs. */ extern int build_h262_item(h262_item_p *item) { int err; h262_item_p new = malloc(SIZEOF_H262_ITEM); if (new == NULL) { print_err("### Unable to allocate MPEG2 item datastructure\n"); return 1; } err = setup_ES_unit(&(new->unit)); if (err) { print_err("### Unable to allocate MPEG2 item data buffer\n"); free(new); return 1; } *item = new; return 0; } /* * Tidy up and free an MPEG2 item datastructure after we've finished with it. * * Empties the MPEG2 item datastructure, frees it, and sets `item` to NULL. * * If `item` is already NULL, does nothing. */ extern void free_h262_item(h262_item_p *item) { if (*item == NULL) return; clear_ES_unit(&(*item)->unit); free(*item); *item = NULL; } /* * Print out useful information about this MPEG2 item */ extern void report_h262_item(h262_item_p item) { fprint_msg(OFFSET_T_FORMAT_08 "/%04d: MPEG2 item %02x (", item->unit.start_posn.infile, item->unit.start_posn.inpacket,item->unit.start_code); print_h262_start_code_str(item->unit.start_code); print_msg(")"); if (item->unit.start_code == 0) fprint_msg(" %d (%s)",item->picture_coding_type, H262_PICTURE_CODING_STR(item->picture_coding_type)); fprint_msg(" size %d",item->unit.data_len); print_msg("\n"); } // ------------------------------------------------------------ // MPEG2 item *data* stuff // ------------------------------------------------------------ /* * Find and read in the next MPEG2 item. * * Be careful if using this in conjunction with reading H.262 pictures * via an `h262_context_p`, as it does not maintain the "last item read" * information therein. * * - `es` is the elementary stream we're reading from. * - `item` is the datastructure containing the MPEG2 item 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 MPEG2 item), otherwise 1 if some error occurs. */ extern int find_next_h262_item(ES_p es, h262_item_p *item) { int err; err = build_h262_item(item); if (err) return 1; err = find_next_ES_unit(es,&(*item)->unit); if (err) // 1 or EOF { free_h262_item(item); return err; } // If this is a picture, we can do a little more if ((*item)->unit.start_code == 0) { (*item)->picture_coding_type = ((*item)->unit.data[5] & 0x38) >> 3; } return 0; } /* * Build a new H.262 picture reading context. * * This acts as a "jacket" around the ES context, and is used when reading * H.262 pictures with get_next_h262_picture(). It "remembers" the last * item read, which is the first item that was not part of the picture. * * Returns 0 if it succeeds, 1 if some error occurs. */ extern int build_h262_context(ES_p es, h262_context_p *context) { h262_context_p new = malloc(SIZEOF_H262_CONTEXT); if (new == NULL) { print_err("### Unable to allocate H.262 context datastructure\n"); return 1; } new->es = es; new->picture_index = 0; new->last_item = NULL; new->reverse_data = NULL; new->count_since_seq_hdr = 0; new->last_aspect_ratio_info = H262_UNSET_ASPECT_RATIO_INFO; new->last_afd = UNSET_AFD_BYTE; new->add_fake_afd = FALSE; *context = new; return 0; } /* * Free an H.262 picture reading context. * * Clears the datastructure, frees it, and returns `context` as NULL. * * Does not free any `reverse_data` datastructure. * * Does nothing if `context` is already NULL. */ extern void free_h262_context(h262_context_p *context) { h262_context_p cc = *context; if (cc == NULL) return; if (cc->last_item != NULL) free_h262_item(&cc->last_item); cc->reverse_data = NULL; free(*context); *context = NULL; return; } /* * Rewind a file being read as H.262 pictures * * This is a wrapper for `seek_ES` that also knows to unset things appropriate * to the H.262 picture reading context. * * If a reverse context is attached to this context, it also will * be "rewound" appropriately. * * Returns 0 if all goes well, 1 if something goes wrong. */ extern int rewind_h262_context(h262_context_p context) { ES_offset start_of_file = {0,0}; // First, forget where we are if (context->last_item) free_h262_item(&context->last_item); context->picture_index = 0; // no pictures read from this file yet // Next, take care of rewinding if (context->reverse_data) { context->reverse_data->last_posn_added = -1; // next entry to be 0 context->count_since_seq_hdr = 0; // what else can we do? } // And then, do the relocation itself return seek_ES(context->es,start_of_file); } // ------------------------------------------------------------ // MPEG2 "pictures" // ------------------------------------------------------------ /* * Add (the information from) an H.262 item to the given picture. * * Note that since this takes a copy of the ES unit data from within the item, * it is safe to free the original item. * * Returns 0 if it succeeds, 1 if some error occurs. */ static int append_to_h262_picture(h262_picture_p picture, h262_item_p item) { ES_unit_p unit = &(item->unit); if (is_h262_extension_start_item(item)) { byte *data = unit->data; int extension_start_code_id = (data[4] & 0xF0) >> 4; if (extension_start_code_id == 1) // sequence extension { picture->progressive_sequence = data[5] & 0x08; } else if (extension_start_code_id == 8) // picture coding extension { picture->picture_structure = data[6] & 0x03; } } return append_to_ES_unit_list(picture->list,unit); } /* * Build a new H.262 "picture", starting with the given item (which is * copied, so may be freed after this call). * * Returns 0 if it succeeds, 1 if some error occurs. */ static int build_h262_picture(h262_context_p context, h262_picture_p *picture, h262_item_p item) { int err; ES_unit_p unit = &(item->unit); byte *data = unit->data; h262_picture_p new = malloc(SIZEOF_H262_PICTURE); if (new == NULL) { print_err("### Unable to allocate H.262 picture datastructure\n"); return 1; } err = build_ES_unit_list(&(new->list)); if (err) { print_err("### Unable to allocate internal list for H.262 picture\n"); free(new); return 1; } // Deduce what we can from the first item of the "picture" if (is_h262_picture_item(item)) { new->picture_coding_type = item->picture_coding_type; new->is_picture = TRUE; new->is_sequence_header = FALSE; new->temporal_reference = (data[4] << 2) | ((data[5] & 0xC0) >> 6); // Assume that our picture is a frame, until we're told otherwise // (MPEG-1 data will never tell us otherwise) new->picture_structure = 3; new->was_two_fields = FALSE; // Assume the last AFD and aspect ratio info until told otherwise new->afd = context->last_afd; new->aspect_ratio_info = context->last_aspect_ratio_info; new->is_real_afd = FALSE; } else if (is_h262_seq_header_item(item)) { new->is_picture = FALSE; new->is_sequence_header = TRUE; new->picture_coding_type = 0; // Forbidden value, just in case new->aspect_ratio_info = (data[7] & 0xF0) >> 4; // Assume that we are only allowed progressive frames, until we're told // otherwise (MPEG-1 data will never tell us otherwise) new->progressive_sequence = 1; } else if (is_h262_seq_end_item(item)) { new->is_picture = FALSE; new->is_sequence_header = FALSE; new->picture_coding_type = 0; // Forbidden value, just in case } else { fprint_err("!!! Building H.262 picture that starts with a %s (%02x)\n", H262_START_CODE_STR(item->unit.start_code),item->unit.start_code); new->is_picture = FALSE; new->is_sequence_header = FALSE; new->picture_coding_type = 0; // Forbidden value, just in case } err = append_to_h262_picture(new,item); if (err) { fprint_err("### Error appending first item to H.262 %s\n", H262_START_CODE_STR(item->unit.start_code)); free_h262_picture(&new); return 1; } *picture = new; return 0; } /* * Build a "pretend" H262 item containing an AFD user data field, and * append it to the given picture. * * Returns 0 if it succeeds, 1 if some error occurs. */ static int append_fake_afd(h262_picture_p picture, byte afd) { int err; static h262_item_p item = NULL; if (item == NULL) { err = build_h262_item(&item); if (err) { print_err("### Error building 'fake' AFD for H.262 picture\n"); return 1; } item->unit.data[0] = 0x00; item->unit.data[1] = 0x00; item->unit.data[2] = 0x01; item->unit.data[3] = 0xb2; item->unit.data[4] = 0x44; item->unit.data[5] = 0x54; item->unit.data[6] = 0x47; item->unit.data[7] = 0x31; item->unit.data[8] = 0x41; item->unit.data[9] = afd; item->unit.data_len = 10; item->unit.start_code = 0xb2; } else item->unit.data[9] = afd; // Remember, this *copies* the item, so we can use it again later on err = append_to_h262_picture(picture,item); if (err) { print_err("### Error appending 'fake' AFD to H.262 picture\n"); return 1; } picture->afd = afd; picture->is_real_afd = FALSE; return 0; } /* * Merge two fields into one (frame) picture. * * - `picture1` is the first field. * - `picture2` is the second field, which will be merged into the first * (thus `picture2` may be freed after this function succeeds). * * Returns 0 if it succeeds, 1 if some error occurs. */ static int merge_fields(h262_picture_p picture1, h262_picture_p picture2) { int ii; for (ii = 0; ii < picture2->list->length; ii++) { int err = append_to_ES_unit_list(picture1->list, &picture2->list->array[ii]); if (err) { print_err("### Error merging two H.262 field pictures\n"); return 1; } } picture1->was_two_fields = TRUE; return 0; } /* * Free an H.262 "picture". * * Clears the datastructure, frees it, and returns `picture` as NULL. * * Does nothing if `picture` is already NULL. */ extern void free_h262_picture(h262_picture_p *picture) { h262_picture_p pic = *picture; if (pic == NULL) return; if (pic->list != NULL) free_ES_unit_list(&pic->list); free(*picture); *picture = NULL; return; } /* * Compare two H.262 pictures. The comparison does not include the start * position of the picture, but just the actual data - i.e., two pictures * read from different locations in the input stream may be considered the * same if their data content is identical. * * Returns TRUE if the lists contain identical content, FALSE otherwise. */ extern int same_h262_picture(h262_picture_p picture1, h262_picture_p picture2) { if (picture1 == picture2) return TRUE; else if (picture1 == NULL || picture2 == NULL) return FALSE; else return same_ES_unit_list(picture1->list,picture2->list); } /* * Remember a picture for future reversing, if it's an I picture or a * sequence header * * Returns 0 if it succeeds, 1 if some error occurs. */ static int maybe_remember_this_picture(h262_context_p h262, int verbose, h262_picture_p this_picture) { int err; ES_offset start_posn = {0,0}; uint32_t num_bytes = 0; if (this_picture->is_picture) { if (this_picture->picture_coding_type == 1) { // It's an I picture - we want to remember it in our reverse list (h262->count_since_seq_hdr) ++; err = get_ES_unit_list_bounds(this_picture->list,&start_posn,&num_bytes); if (err) { print_err("### Error working out position/size of H.262 picture\n"); return 1; } err = remember_reverse_h262_data(h262->reverse_data,h262->picture_index, start_posn,num_bytes, h262->count_since_seq_hdr, this_picture->afd); if (err) { print_err("### Error remembering reversing data for H.262 item\n"); return 1; } if (verbose) fprint_msg("REMEMBER I picture %5d at " OFFSET_T_FORMAT_08 "/%04d for %5d\n",h262->picture_index, start_posn.infile,start_posn.inpacket,num_bytes); } } else if (this_picture->is_sequence_header) { // It's a sequence header - remember it for the next picture h262->count_since_seq_hdr = 0; err = get_ES_unit_list_bounds(this_picture->list,&start_posn,&num_bytes); if (err) { print_err("### Error working out position/size of H.262" " sequence header for reversing data\n"); return 1; } err = remember_reverse_h262_data(h262->reverse_data,0, start_posn,num_bytes,0,0); if (err) { print_err("### Error remembering reversing data for H.262 item\n"); return 1; } if (verbose) fprint_msg("REMEMBER Sequence header at " OFFSET_T_FORMAT_08 "/%04d for %5d\n", start_posn.infile,start_posn.inpacket,num_bytes); } return 0; } /* * Given an MPEG-2 user data item containing an AFD (as indicated by the * ``is_h262_AFD_user_data_item`` macro), extract the actual AFD. * * NB: the whole byte containing the AFD is returned, including the top * '1111' bits. * * Returns 0 if all goes well, 1 if the AFD user data item is malformed * (in which case a message will have been written out to ``stderr``, but * the "apparent" AFD value will still be returned). */ static int extract_AFD(h262_item_p item, byte *afd) { if (item->unit.data[8] == 0x41) { // AFD flag set if (item->unit.data_len < 10) { fprint_err("!!! AFD too short (only %d bytes - AFD missing)\n", item->unit.data_len); *afd = UNSET_AFD_BYTE; return 1; } *afd = item->unit.data[9]; if ((item->unit.data[9] & 0xF0) != 0xF0) { fprint_err("### Bad AFD %02x (reserved bits not 1111)\n", item->unit.data[9]); return 1; } } else if (item->unit.data[8] == 0x01) { *afd = UNSET_AFD_BYTE; // no explicit AFD - use the default } else { fprint_err("### AFD datastructure malformed: flag byte is %02x" " instead of 0x41 or 0x01\n",item->unit.data[8]); if (item->unit.data_len == 9) *afd = UNSET_AFD_BYTE; else *afd = item->unit.data[9]; return 1; } return 0; } #if DEBUG_GET_NEXT_PICTURE /* * Print a representation of an item for debugging */ static void _show_item(h262_item_p item) { print_msg("__ "); if (item == NULL) { print_msg("\n"); return; } if (is_h262_picture_item(item)) fprint_msg("%s picture",H262_PICTURE_CODING_STR(item->picture_coding_type)); else if (is_h262_slice_item(item)) fprint_msg("slice %2x",item->unit.start_code); else fprint_msg("%s",H262_START_CODE_STR(item->unit.start_code)); fprint_msg(" at " OFFSET_T_FORMAT "/%d for %d\n", item->unit.start_posn.infile,item->unit.start_posn.inpacket, item->unit.data_len); } #endif /* * Retrieve the the next H.262 "picture". * * The H.262 "picture" returned can be one of: * * 1. A field or frame, including its slices. * 2. A sequence header, including its sequence extension, if any. * 3. A sequence end. * * - `context` is the H.262 picture reading context. * - if `verbose` is true, then extra information will be output * - if `quiet` is true, then only errors will be reported * - `picture` is the H.262 "picture", containing a field or frame picture, * a sequence header or a sequence end * * Returns 0 if it succeeds, EOF if we reach the end of file, or 1 if some * error occurs. */ extern int get_next_h262_single_picture(h262_context_p context, int verbose, h262_picture_p *picture) { int err = 0; int in_sequence_header = FALSE; int in_sequence_end = FALSE; int in_picture = FALSE; int last_was_slice = FALSE; int had_afd = FALSE; h262_item_p item = context->last_item; #if DEBUG_GET_NEXT_PICTURE int num_slices = 0; int had_slice = FALSE; int last_slice_start_code = 0; if (verbose && context->last_item) print_msg("__ reuse last item\n"); #endif context->last_item = NULL; // Find the first item of our next "picture" for (;;) { if (item == NULL) { err = find_next_h262_item(context->es,&item); if (err) return err; } if (is_h262_picture_item(item)) { in_picture = TRUE; break; } else if (is_h262_seq_header_item(item)) { in_sequence_header = TRUE; break; } else if (is_h262_seq_end_item(item)) { in_sequence_end = TRUE; break; } #if DEBUG_GET_NEXT_PICTURE else if (verbose) _show_item(item); #endif free_h262_item(&item); } #if DEBUG_GET_NEXT_PICTURE if (verbose) { print_msg("__ --------------------------------- \n"); _show_item(item); } #endif err = build_h262_picture(context,picture,item); if (err) return 1; free_h262_item(&item); if (in_sequence_end) { // A sequence end is a single item, so we're done #if DEBUG_GET_NEXT_PICTURE if (verbose) print_msg("__ --------------------------------- \n"); #endif return 0; } // Now find all the rest of the picture/sequence header for (;;) { err = find_next_h262_item(context->es,&item); if (err) { if (err != EOF) free_h262_picture(picture); return err; } if (in_picture) { // Have we just finished a picture? // We know we have if the last item was a slice, but this one isn't if (last_was_slice && !is_h262_slice_item(item)) break; last_was_slice = is_h262_slice_item(item); } else if (in_sequence_header) { // Have we just finished a sequence header and its friends? // We know we have if we've hit something that isn't an // extension start or user data start code (perhaps we could // get away with just keeping the (in MPEG-2) sequence_extension, // but it's safer (and simpler) to keep the lot if (!is_h262_extension_start_item(item) && !is_h262_user_data_item(item)) break; } if (in_picture) { if (is_h262_AFD_user_data_item(item)) { // We found a *real* AFD - remember it err = extract_AFD(item,&(*picture)->afd); if (err) fprint_err("!!! Assuming AFD %x at " OFFSET_T_FORMAT "/%d\n", (*picture)->afd, item->unit.start_posn.infile,item->unit.start_posn.inpacket); (*picture)->is_real_afd = TRUE; #if DEBUG_AFD if ((*picture)->afd != context->last_afd) { print_msg("* "); report_h262_picture(stdout,*picture,FALSE); } #endif context->last_afd = (*picture)->afd; had_afd = TRUE; } else if (context->add_fake_afd && !had_afd && is_h262_slice_item(item)) { // We've been asked to fake AFDs for pictures that don't have them, // and this is the first slice of a picture, so now (i.e., before // said first slice) is the time to add in that faked AFD err = append_fake_afd(*picture,context->last_afd); if (err) { free_h262_picture(picture); return 1; } had_afd = TRUE; // well, sort of #if DEBUG_GET_NEXT_PICTURE if (verbose) { print_msg("__ fake AFD "); print_bits(4,(*picture)->afd); fprint_msg(", i.e., %s",SHORT_AFD_STR((*picture)->afd)); print_msg("\n"); } #endif } } #if DEBUG_GET_NEXT_PICTURE if (verbose) { if (!had_slice) _show_item(item); if (is_h262_slice_item(item)) { num_slices ++; last_slice_start_code = item->unit.start_code; if (!had_slice) had_slice = TRUE; } } #endif // Don't forget to remember the actual item err = append_to_h262_picture(*picture,item); if (err) { print_err("### Error adding item to H.262 sequence header\n"); free_h262_picture(picture); return 1; } free_h262_item(&item); } if (in_picture) context->picture_index ++; else context->last_aspect_ratio_info = (*picture)->aspect_ratio_info; context->last_item = item; #if DEBUG_GET_NEXT_PICTURE if (verbose) { if (in_picture) { if (num_slices > 1) { ES_unit_p unit = &(*picture)->list->array[(*picture)->list->length-1]; print_msg("__ ...\n"); fprint_msg("__ slice %2x",last_slice_start_code); fprint_msg(" at " OFFSET_T_FORMAT "/%d for %d\n", unit->start_posn.infile,unit->start_posn.inpacket, unit->data_len); } fprint_msg("__ (%2d slices)\n",num_slices); } print_msg("__ --------------------------------- \n"); if (in_picture || in_sequence_header) _show_item(item); } #endif return 0; } /* * Try for the next field of a pair, and return a frame formed thereof * * - `context` is the H.262 picture reading context. * - if `verbose` is true, then extra information will be output * - if `quiet` is true, then only errors will be reported * - if `first_time` is true, then we will try to match a second field * with a third, if the second field has a different temporal reference * than the first. If it is false, we will not (thus stopping us from * trying forever...) * - `picture` starts out at the first field of our (hoped for) pair, and * will end up as the merged result of our two fields. If the input stream * is awry (or we are misaligned with respect to it), this might instead be * replaced by a "proper" frame, or even a sequence header. * * Returns 0 if it succeeds, EOF if we reach the end of file or have * read the sequence_end_code, or 1 if some error occurs. */ static int get_next_field_of_pair(h262_context_p context, int verbose, int quiet, int first_time, h262_picture_p *picture) { int err; h262_picture_p second; if (verbose) fprint_msg("@@ Looking for second field (%s time)\n", (first_time?"first":"second")); // We assume (hope) the next picture will be our second half err = get_next_h262_single_picture(context,verbose,&second); if (err) { if (err != EOF) print_err("### Trying to read second field\n"); return err; } if (!is_h262_field_picture(second)) { // But it was either a frame or a sequence header - oh dear if (!quiet) fprint_err("!!! Field followed by a %s - ignoring the field\n", (second->is_picture?"frame":"sequence header")); free_h262_picture(picture); *picture = second; // and pretend to success } else if ((*picture)->temporal_reference == second->temporal_reference) { // They appear to be matching fields - make a frame from them if (verbose) print_msg("@@ Merging two fields\n"); err = merge_fields(*picture,second); if (err) { free_h262_picture(&second); return 1; } free_h262_picture(&second); } else if (first_time) { if (!quiet) fprint_err("!!! Field with temporal ref %d (%x) followed by" " field with temporal ref %d (%x) - ignoring first field\n", (*picture)->temporal_reference,(*picture)->temporal_reference, second->temporal_reference,second->temporal_reference); // Try again free_h262_picture(picture); *picture = second; err = get_next_field_of_pair(context,verbose,quiet,FALSE,picture); if (err) return 1; } else { print_err("### Adjacent fields do not share temporal references" " - unable to match fields up\n"); return 1; } return 0; } /* * Retrieve the the next H.262 "picture". * * The H.262 "picture" returned can be one of: * * 1. A frame, including its slices. This may be the concatenation of two * adjacent field pictures. * 2. A sequence header, including its sequence extension, if any. * 3. A sequence end. * * Specifically, the next H.262 "picture" is retrieved from the input stream. * * If that "picture" represents a sequence header or a frame, it is returned. * * If it represents a field, then the *following* "picture" is retrieved, and * if that is the second field of its frame, it is merged into the first, * and the resultant frame is returned. * * If a field with temporal reference A is followed by a field with temporal * reference B, it is assumed that synchronisation has been lost. In this * case, the first field (frame A) will be discarded, and an attempt made to * read the second field of frame B. * * Similarly, if a frame or sequence header is found instead of the second * field, the first field will be discarded and the frame returned. * * Note that if the context is associated with a reverse context, * then appropriate frames/sequence headers will automatically be * remembered therein. * * Also note that it is assumed that the AFD for adjacent fields will be * the same. * * - `context` is the H.262 picture reading context. * - if `verbose` is true, then extra information will be output * - if `quiet` is true, then only errors will be reported * - `picture` is the H.262 "picture", containing a frame picture, * a sequence header or a sequence end * * Returns 0 if it succeeds, EOF if we reach the end of file, or 1 if some * error occurs. */ extern int get_next_h262_frame(h262_context_p context, int verbose, int quiet, h262_picture_p *picture) { int err; err = get_next_h262_single_picture(context,verbose,picture); if (err) return err; if (is_h262_field_picture(*picture)) { // We assume (hope) the next picture will be our second half // - let's try to get it, and merge it into our current picture err = get_next_field_of_pair(context,verbose,quiet,TRUE,picture); if (err) { free_h262_picture(picture); return 1; } } if (context->reverse_data) { err = maybe_remember_this_picture(context,verbose,*picture); if (err) { free_h262_picture(picture); return 1; } } return 0; } /* * Write out an H.262 picture as TS * * - `tswriter` is TS the output stream * - `picture` is the picture to write out * - `pid` is the PID to use for the TS packets * * Returns 0 if it succeeds, 1 if some error occurs. */ extern int write_h262_picture_as_TS(TS_writer_p tswriter, h262_picture_p picture, uint32_t pid) { int ii; ES_unit_list_p list; if (picture == NULL || picture->list == NULL) return 0; list = picture->list; for (ii = 0; ii < list->length; ii++) { int err; ES_unit_p unit = &(list->array[ii]); err = write_ES_as_TS_PES_packet(tswriter,unit->data,unit->data_len,pid, DEFAULT_VIDEO_STREAM_ID); if (err) { print_err("### Error writing out picture list to TS\n"); return err; } } return 0; } /* * Write out a picture (as stored in an ES unit list) as ES * * - `output` is the ES output file * - `picture` is the picture to write out * * Returns 0 if it succeeds, 1 if some error occurs. */ extern int write_h262_picture_as_ES(FILE *output, h262_picture_p picture) { int ii; ES_unit_list_p list; if (picture == NULL || picture->list == NULL) return 0; list = picture->list; for (ii = 0; ii < list->length; ii++) { int err; ES_unit_p unit = &(list->array[ii]); err = write_ES_unit(output,unit); if (err) { print_err("### Error writing out picture list to ES\n"); return err; } } return 0; } /* * Report on an H.262 picture's contents. * * - `stream` is where to write the information * - `picture` is the picture to report on * - if `report_data`, then the component ES units will be printed out as well */ extern void report_h262_picture(h262_picture_p picture, int report_data) { if (picture->is_picture) { fprint_msg("%s %s #%02d", H262_PICTURE_CODING_STR(picture->picture_coding_type), H262_PICTURE_STRUCTURE_STR(picture->picture_structure), picture->temporal_reference); if (picture->was_two_fields) print_msg(" (merged)"); fprint_msg(" %s",H262_ASPECT_RATIO_INFO_STR(picture->aspect_ratio_info)); if (picture->is_real_afd) print_msg(" AFD "); else print_msg(" afd "); print_bits(4,picture->afd); fprint_msg(", i.e., %s",SHORT_AFD_STR(picture->afd)); print_msg("\n"); } else if (picture->is_sequence_header) { print_msg("Sequence header: "); switch (picture->progressive_sequence) { case 0: print_msg("frames and fields"); break; case 1: print_msg("progressive frames only"); break; default: fprint_msg("progressive_sequence=%d", picture->progressive_sequence); break; } fprint_msg(", aspect ratio %s", H262_ASPECT_RATIO_INFO_STR(picture->aspect_ratio_info)); print_msg("\n"); } else { print_msg("Sequence end\n"); } if (report_data) report_ES_unit_list("ES units",picture->list); } // Local Variables: // tab-width: 8 // indent-tabs-mode: nil // c-basic-offset: 2 // End: // vim: set tabstop=8 shiftwidth=2 expandtab: