kopia lustrzana https://github.com/F5OEO/tstools
1205 wiersze
34 KiB
C
1205 wiersze
34 KiB
C
/*
|
||
* 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 <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <errno.h>
|
||
#include <string.h>
|
||
|
||
#include "compat.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, to the given stream.
|
||
*
|
||
* 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(FILE *stream,
|
||
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)
|
||
fprintf(stream,str);
|
||
else if (start_code == 0x47)
|
||
fprintf(stream,"TRANSPORT STREAM sync byte");
|
||
else if (start_code >= 0x01 && start_code <= 0xAF)
|
||
fprintf(stream,"Slice, vertical posn %d",start_code);
|
||
else if (start_code >= 0xC0 && start_code <=0xDF)
|
||
{
|
||
number = start_code & 0x1F;
|
||
fprintf(stream,"SYSTEM START: Audio stream %02x",number);
|
||
}
|
||
else if (start_code >= 0xE0 && start_code <= 0xEF)
|
||
{
|
||
number = start_code & 0x0F;
|
||
fprintf(stream,"SYSTEM START: Video stream %x",number);
|
||
}
|
||
else if (start_code >= 0xFC && start_code <= 0xFE)
|
||
fprintf(stream,"SYSTEM START: Reserved data stream");
|
||
else
|
||
fprintf(stream,"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)
|
||
{
|
||
fprintf(stderr,"### Unable to allocate MPEG2 item datastructure\n");
|
||
return 1;
|
||
}
|
||
err = setup_ES_unit(&(new->unit));
|
||
if (err)
|
||
{
|
||
fprintf(stderr,"### 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, on the given stream.
|
||
*/
|
||
extern void report_h262_item(FILE *stream,
|
||
h262_item_p item)
|
||
{
|
||
fprintf(stream,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(stream,item->unit.start_code);
|
||
fprintf(stream,")");
|
||
if (item->unit.start_code == 0)
|
||
fprintf(stream," %d (%s)",item->picture_coding_type,
|
||
H262_PICTURE_CODING_STR(item->picture_coding_type));
|
||
fprintf(stream," size %d",item->unit.data_len);
|
||
fprintf(stream,"\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)
|
||
{
|
||
fprintf(stderr,"### 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)
|
||
{
|
||
fprintf(stderr,"### Unable to allocate H.262 picture datastructure\n");
|
||
return 1;
|
||
}
|
||
|
||
err = build_ES_unit_list(&(new->list));
|
||
if (err)
|
||
{
|
||
fprintf(stderr,"### 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
|
||
{
|
||
fprintf(stderr,
|
||
"!!! 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)
|
||
{
|
||
fprintf(stderr,"### 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)
|
||
{
|
||
fprintf(stderr,"### 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)
|
||
{
|
||
fprintf(stderr,"### 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)
|
||
{
|
||
fprintf(stderr,"### 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};
|
||
u_int32 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)
|
||
{
|
||
fprintf(stderr,
|
||
"### 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)
|
||
{
|
||
fprintf(stderr,
|
||
"### Error remembering reversing data for H.262 item\n");
|
||
return 1;
|
||
}
|
||
if (verbose)
|
||
printf("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)
|
||
{
|
||
fprintf(stderr,"### 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)
|
||
{
|
||
fprintf(stderr,"### Error remembering reversing data for H.262 item\n");
|
||
return 1;
|
||
}
|
||
if (verbose)
|
||
printf("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)
|
||
{
|
||
fprintf(stderr,"!!! 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)
|
||
{
|
||
fprintf(stderr,"### 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
|
||
{
|
||
fprintf(stderr,"### 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)
|
||
{
|
||
printf("__ ");
|
||
if (item == NULL)
|
||
{
|
||
printf("<no item>\n");
|
||
return;
|
||
}
|
||
if (is_h262_picture_item(item))
|
||
printf("%s picture",H262_PICTURE_CODING_STR(item->picture_coding_type));
|
||
else if (is_h262_slice_item(item))
|
||
printf("slice %2x",item->unit.start_code);
|
||
else
|
||
printf("%s",H262_START_CODE_STR(item->unit.start_code));
|
||
printf(" 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) printf("__ 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)
|
||
{
|
||
printf("__ --------------------------------- <start picture>\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)
|
||
printf("__ --------------------------------- <end picture>\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)
|
||
fprintf(stderr,"!!! 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)
|
||
{
|
||
printf("* ");
|
||
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)
|
||
{
|
||
printf("__ fake AFD ");
|
||
print_bits(stdout,4,(*picture)->afd);
|
||
printf(", i.e., %s",SHORT_AFD_STR((*picture)->afd));
|
||
printf("\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)
|
||
{
|
||
fprintf(stderr,"### 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];
|
||
printf("__ ...\n");
|
||
printf("__ slice %2x",last_slice_start_code);
|
||
printf(" at " OFFSET_T_FORMAT "/%d for %d\n",
|
||
unit->start_posn.infile,unit->start_posn.inpacket,
|
||
unit->data_len);
|
||
}
|
||
printf("__ (%2d slices)\n",num_slices);
|
||
}
|
||
printf("__ --------------------------------- <end picture>\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)
|
||
printf("@@ 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)
|
||
fprintf(stderr,"### 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)
|
||
fprintf(stderr,"!!! 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) printf("@@ 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)
|
||
fprintf(stderr,"!!! 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
|
||
{
|
||
fprintf(stderr,"### 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,
|
||
u_int32 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)
|
||
{
|
||
fprintf(stderr,"### 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)
|
||
{
|
||
fprintf(stderr,"### 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(FILE *stream,
|
||
h262_picture_p picture,
|
||
int report_data)
|
||
{
|
||
if (picture->is_picture)
|
||
{
|
||
printf("%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)
|
||
printf(" (merged)");
|
||
|
||
printf(" %s",H262_ASPECT_RATIO_INFO_STR(picture->aspect_ratio_info));
|
||
|
||
if (picture->is_real_afd)
|
||
printf(" AFD ");
|
||
else
|
||
printf(" afd ");
|
||
print_bits(stdout,4,picture->afd);
|
||
printf(", i.e., %s",SHORT_AFD_STR(picture->afd));
|
||
printf("\n");
|
||
}
|
||
else if (picture->is_sequence_header)
|
||
{
|
||
printf("Sequence header: ");
|
||
|
||
switch (picture->progressive_sequence)
|
||
{
|
||
case 0: printf("frames and fields"); break;
|
||
case 1: printf("progressive frames only"); break;
|
||
default:
|
||
printf("progressive_sequence=%d",
|
||
picture->progressive_sequence);
|
||
break;
|
||
}
|
||
printf(", aspect ratio %s",
|
||
H262_ASPECT_RATIO_INFO_STR(picture->aspect_ratio_info));
|
||
printf("\n");
|
||
}
|
||
else
|
||
{
|
||
printf("Sequence end\n");
|
||
}
|
||
if (report_data)
|
||
report_ES_unit_list(stream,"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:
|