2008-04-14 04:09:29 +00:00
|
|
|
|
/*
|
|
|
|
|
* 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"
|
2009-02-23 21:05:51 +00:00
|
|
|
|
#include "printing_fns.h"
|
2008-04-14 04:09:29 +00:00
|
|
|
|
#include "h262_fns.h"
|
|
|
|
|
#include "es_fns.h"
|
|
|
|
|
#include "ts_fns.h"
|
|
|
|
|
#include "reverse_fns.h"
|
|
|
|
|
#include "misc_fns.h"
|
|
|
|
|
|
2008-04-14 07:24:06 +00:00
|
|
|
|
#define DEBUG_GET_NEXT_PICTURE 0
|
2008-04-14 04:09:29 +00:00
|
|
|
|
#define DEBUG_AFD 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2009-05-03 16:18:16 +00:00
|
|
|
|
* Print out information derived from the start code
|
2008-04-14 04:09:29 +00:00
|
|
|
|
*
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
2009-05-03 16:18:16 +00:00
|
|
|
|
extern void print_h262_start_code_str(byte start_code)
|
2008-04-14 04:09:29 +00:00
|
|
|
|
{
|
|
|
|
|
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)
|
2009-05-03 16:18:16 +00:00
|
|
|
|
print_msg(str);
|
2008-04-14 04:09:29 +00:00
|
|
|
|
else if (start_code == 0x47)
|
2009-05-03 16:18:16 +00:00
|
|
|
|
print_msg("TRANSPORT STREAM sync byte");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
else if (start_code >= 0x01 && start_code <= 0xAF)
|
2009-05-03 16:18:16 +00:00
|
|
|
|
fprint_msg("Slice, vertical posn %d",start_code);
|
2008-04-14 04:09:29 +00:00
|
|
|
|
else if (start_code >= 0xC0 && start_code <=0xDF)
|
|
|
|
|
{
|
|
|
|
|
number = start_code & 0x1F;
|
2009-05-03 16:18:16 +00:00
|
|
|
|
fprint_msg("SYSTEM START: Audio stream %02x",number);
|
2008-04-14 04:09:29 +00:00
|
|
|
|
}
|
|
|
|
|
else if (start_code >= 0xE0 && start_code <= 0xEF)
|
|
|
|
|
{
|
|
|
|
|
number = start_code & 0x0F;
|
2009-05-03 16:18:16 +00:00
|
|
|
|
fprint_msg("SYSTEM START: Video stream %x",number);
|
2008-04-14 04:09:29 +00:00
|
|
|
|
}
|
|
|
|
|
else if (start_code >= 0xFC && start_code <= 0xFE)
|
2009-05-03 16:18:16 +00:00
|
|
|
|
print_msg("SYSTEM START: Reserved data stream");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
else
|
2009-05-03 16:18:16 +00:00
|
|
|
|
print_msg("SYSTEM START: Unrecognised stream id");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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)
|
|
|
|
|
{
|
2009-05-03 16:18:16 +00:00
|
|
|
|
print_err("### Unable to allocate MPEG2 item datastructure\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
err = setup_ES_unit(&(new->unit));
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
2009-05-03 16:18:16 +00:00
|
|
|
|
print_err("### Unable to allocate MPEG2 item data buffer\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
2009-05-03 15:57:22 +00:00
|
|
|
|
* Print out useful information about this MPEG2 item
|
2008-04-14 04:09:29 +00:00
|
|
|
|
*/
|
2009-05-03 15:57:22 +00:00
|
|
|
|
extern void report_h262_item(h262_item_p item)
|
2008-04-14 04:09:29 +00:00
|
|
|
|
{
|
2009-05-03 15:57:22 +00:00
|
|
|
|
fprint_msg(OFFSET_T_FORMAT_08 "/%04d: MPEG2 item %02x (",
|
|
|
|
|
item->unit.start_posn.infile,
|
|
|
|
|
item->unit.start_posn.inpacket,item->unit.start_code);
|
2009-05-03 16:18:16 +00:00
|
|
|
|
print_h262_start_code_str(item->unit.start_code);
|
2009-05-03 15:57:22 +00:00
|
|
|
|
print_msg(")");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
if (item->unit.start_code == 0)
|
2009-05-03 15:57:22 +00:00
|
|
|
|
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");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------
|
|
|
|
|
// 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)
|
|
|
|
|
{
|
2009-05-03 16:18:16 +00:00
|
|
|
|
print_err("### Unable to allocate H.262 context datastructure\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
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)
|
|
|
|
|
{
|
2009-05-03 16:18:16 +00:00
|
|
|
|
print_err("### Unable to allocate H.262 picture datastructure\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = build_ES_unit_list(&(new->list));
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
2009-05-03 16:18:16 +00:00
|
|
|
|
print_err("### Unable to allocate internal list for H.262 picture\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
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
|
|
|
|
|
{
|
2009-05-03 16:18:16 +00:00
|
|
|
|
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);
|
2008-04-14 04:09:29 +00:00
|
|
|
|
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)
|
|
|
|
|
{
|
2009-05-03 16:18:16 +00:00
|
|
|
|
fprint_err("### Error appending first item to H.262 %s\n",
|
|
|
|
|
H262_START_CODE_STR(item->unit.start_code));
|
2008-04-14 04:09:29 +00:00
|
|
|
|
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)
|
|
|
|
|
{
|
2009-05-03 16:18:16 +00:00
|
|
|
|
print_err("### Error building 'fake' AFD for H.262 picture\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
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)
|
|
|
|
|
{
|
2009-05-03 16:18:16 +00:00
|
|
|
|
print_err("### Error appending 'fake' AFD to H.262 picture\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
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)
|
|
|
|
|
{
|
2009-05-03 16:18:16 +00:00
|
|
|
|
print_err("### Error merging two H.262 field pictures\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
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};
|
2008-10-18 15:04:34 +00:00
|
|
|
|
uint32_t num_bytes = 0;
|
2008-04-14 04:09:29 +00:00
|
|
|
|
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)
|
|
|
|
|
{
|
2009-05-03 16:18:16 +00:00
|
|
|
|
print_err("### Error working out position/size of H.262 picture\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
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)
|
|
|
|
|
{
|
2009-05-03 16:18:16 +00:00
|
|
|
|
print_err("### Error remembering reversing data for H.262 item\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
if (verbose)
|
2009-05-03 16:18:16 +00:00
|
|
|
|
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);
|
2008-04-14 04:09:29 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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)
|
|
|
|
|
{
|
2009-05-03 16:18:16 +00:00
|
|
|
|
print_err("### Error working out position/size of H.262"
|
|
|
|
|
" sequence header for reversing data\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
err = remember_reverse_h262_data(h262->reverse_data,0,
|
|
|
|
|
start_posn,num_bytes,0,0);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
2009-05-03 16:18:16 +00:00
|
|
|
|
print_err("### Error remembering reversing data for H.262 item\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
if (verbose)
|
2009-05-03 16:18:16 +00:00
|
|
|
|
fprint_msg("REMEMBER Sequence header at " OFFSET_T_FORMAT_08
|
|
|
|
|
"/%04d for %5d\n",
|
|
|
|
|
start_posn.infile,start_posn.inpacket,num_bytes);
|
2008-04-14 04:09:29 +00:00
|
|
|
|
}
|
|
|
|
|
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)
|
|
|
|
|
{
|
2009-05-03 16:18:16 +00:00
|
|
|
|
fprint_err("!!! AFD too short (only %d bytes - AFD missing)\n",
|
|
|
|
|
item->unit.data_len);
|
2008-04-14 04:09:29 +00:00
|
|
|
|
*afd = UNSET_AFD_BYTE;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
*afd = item->unit.data[9];
|
|
|
|
|
if ((item->unit.data[9] & 0xF0) != 0xF0)
|
|
|
|
|
{
|
2009-05-03 16:18:16 +00:00
|
|
|
|
fprint_err("### Bad AFD %02x (reserved bits not 1111)\n",
|
|
|
|
|
item->unit.data[9]);
|
2008-04-14 04:09:29 +00:00
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (item->unit.data[8] == 0x01)
|
|
|
|
|
{
|
|
|
|
|
*afd = UNSET_AFD_BYTE; // no explicit AFD - use the default
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2009-05-03 16:18:16 +00:00
|
|
|
|
fprint_err("### AFD datastructure malformed: flag byte is %02x"
|
|
|
|
|
" instead of 0x41 or 0x01\n",item->unit.data[8]);
|
2008-04-14 04:09:29 +00:00
|
|
|
|
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)
|
|
|
|
|
{
|
2009-05-03 16:18:16 +00:00
|
|
|
|
print_msg("__ ");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
if (item == NULL)
|
|
|
|
|
{
|
2009-05-03 16:18:16 +00:00
|
|
|
|
print_msg("<no item>\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (is_h262_picture_item(item))
|
2009-05-03 16:18:16 +00:00
|
|
|
|
fprint_msg("%s picture",H262_PICTURE_CODING_STR(item->picture_coding_type));
|
2008-04-14 04:09:29 +00:00
|
|
|
|
else if (is_h262_slice_item(item))
|
2009-05-03 16:18:16 +00:00
|
|
|
|
fprint_msg("slice %2x",item->unit.start_code);
|
2008-04-14 04:09:29 +00:00
|
|
|
|
else
|
2009-05-03 16:18:16 +00:00
|
|
|
|
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);
|
2008-04-14 04:09:29 +00:00
|
|
|
|
}
|
|
|
|
|
#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;
|
2009-05-03 16:18:16 +00:00
|
|
|
|
if (verbose && context->last_item) print_msg("__ reuse last item\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
#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)
|
|
|
|
|
{
|
2009-05-03 16:18:16 +00:00
|
|
|
|
print_msg("__ --------------------------------- <start picture>\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
_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)
|
2009-05-03 16:18:16 +00:00
|
|
|
|
print_msg("__ --------------------------------- <end picture>\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
#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)
|
2009-05-03 16:18:16 +00:00
|
|
|
|
fprint_err("!!! Assuming AFD %x at " OFFSET_T_FORMAT "/%d\n",
|
|
|
|
|
(*picture)->afd,
|
|
|
|
|
item->unit.start_posn.infile,item->unit.start_posn.inpacket);
|
2008-04-14 04:09:29 +00:00
|
|
|
|
(*picture)->is_real_afd = TRUE;
|
|
|
|
|
#if DEBUG_AFD
|
|
|
|
|
if ((*picture)->afd != context->last_afd)
|
|
|
|
|
{
|
2009-05-03 16:18:16 +00:00
|
|
|
|
print_msg("* ");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
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)
|
|
|
|
|
{
|
2009-05-03 16:18:16 +00:00
|
|
|
|
print_msg("__ fake AFD ");
|
2009-05-03 15:57:22 +00:00
|
|
|
|
print_bits(4,(*picture)->afd);
|
2009-05-03 16:18:16 +00:00
|
|
|
|
fprint_msg(", i.e., %s",SHORT_AFD_STR((*picture)->afd));
|
|
|
|
|
print_msg("\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
}
|
|
|
|
|
#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)
|
|
|
|
|
{
|
2009-05-03 16:18:16 +00:00
|
|
|
|
print_err("### Error adding item to H.262 sequence header\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
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];
|
2009-05-03 16:18:16 +00:00
|
|
|
|
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);
|
2008-04-14 04:09:29 +00:00
|
|
|
|
}
|
2009-05-03 16:18:16 +00:00
|
|
|
|
fprint_msg("__ (%2d slices)\n",num_slices);
|
2008-04-14 04:09:29 +00:00
|
|
|
|
}
|
2009-05-03 16:18:16 +00:00
|
|
|
|
print_msg("__ --------------------------------- <end picture>\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
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)
|
2009-05-03 16:18:16 +00:00
|
|
|
|
fprint_msg("@@ Looking for second field (%s time)\n",
|
|
|
|
|
(first_time?"first":"second"));
|
2008-04-14 04:09:29 +00:00
|
|
|
|
|
|
|
|
|
// 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)
|
2009-05-03 16:18:16 +00:00
|
|
|
|
print_err("### Trying to read second field\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!is_h262_field_picture(second))
|
|
|
|
|
{
|
|
|
|
|
// But it was either a frame or a sequence header - oh dear
|
|
|
|
|
if (!quiet)
|
2009-05-03 16:18:16 +00:00
|
|
|
|
fprint_err("!!! Field followed by a %s - ignoring the field\n",
|
|
|
|
|
(second->is_picture?"frame":"sequence header"));
|
2008-04-14 04:09:29 +00:00
|
|
|
|
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
|
2009-05-03 16:18:16 +00:00
|
|
|
|
if (verbose) print_msg("@@ Merging two fields\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
err = merge_fields(*picture,second);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
free_h262_picture(&second);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
free_h262_picture(&second);
|
|
|
|
|
}
|
|
|
|
|
else if (first_time)
|
|
|
|
|
{
|
|
|
|
|
if (!quiet)
|
2009-05-03 16:18:16 +00:00
|
|
|
|
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);
|
2008-04-14 04:09:29 +00:00
|
|
|
|
|
|
|
|
|
// Try again
|
|
|
|
|
free_h262_picture(picture);
|
|
|
|
|
*picture = second;
|
|
|
|
|
err = get_next_field_of_pair(context,verbose,quiet,FALSE,picture);
|
|
|
|
|
if (err) return 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2009-05-03 16:18:16 +00:00
|
|
|
|
print_err("### Adjacent fields do not share temporal references"
|
|
|
|
|
" - unable to match fields up\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
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,
|
2008-10-18 15:04:34 +00:00
|
|
|
|
uint32_t pid)
|
2008-04-14 04:09:29 +00:00
|
|
|
|
{
|
|
|
|
|
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)
|
|
|
|
|
{
|
2009-05-03 16:18:16 +00:00
|
|
|
|
print_err("### Error writing out picture list to TS\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
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)
|
|
|
|
|
{
|
2009-05-03 16:18:16 +00:00
|
|
|
|
print_err("### Error writing out picture list to ES\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
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
|
|
|
|
|
*/
|
2009-02-23 21:05:51 +00:00
|
|
|
|
extern void report_h262_picture(h262_picture_p picture,
|
2008-04-14 04:09:29 +00:00
|
|
|
|
int report_data)
|
|
|
|
|
{
|
|
|
|
|
if (picture->is_picture)
|
|
|
|
|
{
|
2009-05-03 16:18:16 +00:00
|
|
|
|
fprint_msg("%s %s #%02d",
|
|
|
|
|
H262_PICTURE_CODING_STR(picture->picture_coding_type),
|
|
|
|
|
H262_PICTURE_STRUCTURE_STR(picture->picture_structure),
|
|
|
|
|
picture->temporal_reference);
|
2008-04-14 04:09:29 +00:00
|
|
|
|
|
|
|
|
|
if (picture->was_two_fields)
|
2009-05-03 16:18:16 +00:00
|
|
|
|
print_msg(" (merged)");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
|
2009-05-03 16:18:16 +00:00
|
|
|
|
fprint_msg(" %s",H262_ASPECT_RATIO_INFO_STR(picture->aspect_ratio_info));
|
2009-02-23 21:05:51 +00:00
|
|
|
|
|
2008-04-14 04:09:29 +00:00
|
|
|
|
if (picture->is_real_afd)
|
2009-05-03 16:18:16 +00:00
|
|
|
|
print_msg(" AFD ");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
else
|
2009-05-03 16:18:16 +00:00
|
|
|
|
print_msg(" afd ");
|
2009-05-03 15:57:22 +00:00
|
|
|
|
print_bits(4,picture->afd);
|
2009-05-03 16:18:16 +00:00
|
|
|
|
fprint_msg(", i.e., %s",SHORT_AFD_STR(picture->afd));
|
|
|
|
|
print_msg("\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
}
|
|
|
|
|
else if (picture->is_sequence_header)
|
|
|
|
|
{
|
2009-05-03 16:18:16 +00:00
|
|
|
|
print_msg("Sequence header: ");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
|
|
|
|
|
switch (picture->progressive_sequence)
|
|
|
|
|
{
|
2009-05-03 16:18:16 +00:00
|
|
|
|
case 0: print_msg("frames and fields"); break;
|
|
|
|
|
case 1: print_msg("progressive frames only"); break;
|
2008-04-14 04:09:29 +00:00
|
|
|
|
default:
|
2009-05-03 16:18:16 +00:00
|
|
|
|
fprint_msg("progressive_sequence=%d",
|
|
|
|
|
picture->progressive_sequence);
|
2008-04-14 04:09:29 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
2009-05-03 16:18:16 +00:00
|
|
|
|
fprint_msg(", aspect ratio %s",
|
|
|
|
|
H262_ASPECT_RATIO_INFO_STR(picture->aspect_ratio_info));
|
|
|
|
|
print_msg("\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2009-05-03 16:18:16 +00:00
|
|
|
|
print_msg("Sequence end\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
}
|
|
|
|
|
if (report_data)
|
2009-02-23 21:05:51 +00:00
|
|
|
|
report_ES_unit_list("ES units",picture->list);
|
2008-04-14 04:09:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
2008-06-14 16:05:00 +00:00
|
|
|
|
// Local Variables:
|
|
|
|
|
// tab-width: 8
|
|
|
|
|
// indent-tabs-mode: nil
|
|
|
|
|
// c-basic-offset: 2
|
|
|
|
|
// End:
|
|
|
|
|
// vim: set tabstop=8 shiftwidth=2 expandtab:
|