2008-04-14 04:09:29 +00:00
|
|
|
|
/*
|
|
|
|
|
* Datastructures and prototypes for reading AVS elementary streams.
|
|
|
|
|
*
|
|
|
|
|
* XXX Ignores the issue of the equivalent of AFD data. This *will* cause
|
|
|
|
|
* XXX problems if rewinding or filtering is to be done. However, what
|
|
|
|
|
* XXX needs to be done to fix this can probably be based on the code in
|
|
|
|
|
* XXX h262.c.
|
|
|
|
|
* XXX And, also, reversing is not yet supported for AVS, anyway.
|
|
|
|
|
*
|
|
|
|
|
* ***** 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 "avs_fns.h"
|
|
|
|
|
#include "es_fns.h"
|
|
|
|
|
#include "ts_fns.h"
|
|
|
|
|
#include "reverse_fns.h"
|
|
|
|
|
#include "misc_fns.h"
|
|
|
|
|
|
|
|
|
|
#define DEBUG 0
|
2008-04-14 07:24:06 +00:00
|
|
|
|
#define DEBUG_GET_NEXT_PICTURE 0
|
2008-04-14 04:09:29 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Return a string representing the start code
|
|
|
|
|
*/
|
|
|
|
|
extern const char *avs_start_code_str(byte start_code)
|
|
|
|
|
{
|
|
|
|
|
if (start_code < 0xB0) return "Slice";
|
|
|
|
|
switch (start_code)
|
|
|
|
|
{
|
|
|
|
|
// AVS start codes that we are interested in
|
|
|
|
|
case 0xB0: return "Video sequence start";
|
|
|
|
|
case 0xB1: return "Video sequence end";
|
|
|
|
|
case 0xB2: return "User data";
|
|
|
|
|
case 0xB3: return "I frame";
|
|
|
|
|
case 0xB4: return "Reserved";
|
|
|
|
|
case 0xB5: return "Extension start";
|
|
|
|
|
case 0xB6: return "P/B frame";
|
|
|
|
|
case 0xB7: return "Video edit";
|
|
|
|
|
default: return "Reserved";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------
|
|
|
|
|
// AVS item *data* stuff
|
|
|
|
|
// ------------------------------------------------------------
|
|
|
|
|
/*
|
|
|
|
|
* Build a new AVS frame reading context.
|
|
|
|
|
*
|
|
|
|
|
* This acts as a "jacket" around the ES context, and is used when reading
|
|
|
|
|
* AVS frames with get_next_avs_frame(). It "remembers" the last
|
|
|
|
|
* item read, which is the first item that was not part of the frame.
|
|
|
|
|
*
|
|
|
|
|
* Returns 0 if it succeeds, 1 if some error occurs.
|
|
|
|
|
*/
|
|
|
|
|
extern int build_avs_context(ES_p es,
|
|
|
|
|
avs_context_p *context)
|
|
|
|
|
{
|
|
|
|
|
avs_context_p new = malloc(SIZEOF_AVS_CONTEXT);
|
|
|
|
|
if (new == NULL)
|
|
|
|
|
{
|
2009-02-23 21:05:51 +00:00
|
|
|
|
print_err("### Unable to allocate AVS context datastructure\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
new->es = es;
|
|
|
|
|
new->frame_index = 0;
|
|
|
|
|
new->last_item = NULL;
|
|
|
|
|
new->reverse_data = NULL;
|
|
|
|
|
new->count_since_seq_hdr = 0;
|
|
|
|
|
|
|
|
|
|
*context = new;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Free an AVS frame 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_avs_context(avs_context_p *context)
|
|
|
|
|
{
|
|
|
|
|
avs_context_p cc = *context;
|
|
|
|
|
|
|
|
|
|
if (cc == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (cc->last_item != NULL)
|
|
|
|
|
free_ES_unit(&cc->last_item);
|
|
|
|
|
|
|
|
|
|
cc->reverse_data = NULL;
|
|
|
|
|
|
|
|
|
|
free(*context);
|
|
|
|
|
*context = NULL;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Rewind a file being read as AVS frames
|
|
|
|
|
*
|
|
|
|
|
* This is a wrapper for `seek_ES` that also knows to unset things appropriate
|
|
|
|
|
* to the AVS frame 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_avs_context(avs_context_p context)
|
|
|
|
|
{
|
|
|
|
|
ES_offset start_of_file = {0,0};
|
|
|
|
|
|
|
|
|
|
// First, forget where we are
|
|
|
|
|
if (context->last_item)
|
|
|
|
|
{
|
|
|
|
|
free_ES_unit(&context->last_item);
|
|
|
|
|
context->last_item = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
context->frame_index = 0; // no frames 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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------
|
|
|
|
|
// AVS "frames"
|
|
|
|
|
// ------------------------------------------------------------
|
|
|
|
|
/*
|
|
|
|
|
* Add (the information from) an AVS ES unit to the given frame.
|
|
|
|
|
*
|
|
|
|
|
* Note that since this takes a copy of the ES unit data,
|
|
|
|
|
* it is safe to free the original ES unit.
|
|
|
|
|
*
|
|
|
|
|
* Returns 0 if it succeeds, 1 if some error occurs.
|
|
|
|
|
*/
|
|
|
|
|
static int append_to_avs_frame(avs_frame_p frame,
|
|
|
|
|
ES_unit_p unit)
|
|
|
|
|
{
|
|
|
|
|
return append_to_ES_unit_list(frame->list,unit);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Determine the picture coding type of an AVS ES unit
|
|
|
|
|
*
|
|
|
|
|
* P/B frames are distinguished by their picture coding types. For I frames,
|
|
|
|
|
* we make one up...
|
|
|
|
|
*
|
|
|
|
|
* Returns an appropriate value (0 if none suitable)
|
|
|
|
|
*/
|
|
|
|
|
extern int avs_picture_coding_type(ES_unit_p unit)
|
|
|
|
|
{
|
|
|
|
|
if (unit->start_code == 0xB3)
|
|
|
|
|
return AVS_I_PICTURE_CODING; // strictly our invention
|
|
|
|
|
else if (unit->start_code == 0xB6)
|
|
|
|
|
{
|
|
|
|
|
byte picture_coding_type = (unit->data[6] & 0xC0) >> 6;
|
|
|
|
|
if (picture_coding_type == AVS_P_PICTURE_CODING ||
|
|
|
|
|
picture_coding_type == AVS_B_PICTURE_CODING)
|
|
|
|
|
return picture_coding_type;
|
|
|
|
|
else
|
|
|
|
|
{
|
2009-02-23 21:05:51 +00:00
|
|
|
|
fprint_err("AVS Picture coding type %d (in %02x)\n",
|
|
|
|
|
picture_coding_type,unit->data[3]);
|
2008-04-14 04:09:29 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2009-02-23 21:05:51 +00:00
|
|
|
|
fprint_err("AVS 'frame' with start code %02x does not have picture coding type\n",
|
|
|
|
|
unit->data[0]);
|
2008-04-14 04:09:29 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Build a new AVS "frame", 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_avs_frame(avs_context_p context,
|
|
|
|
|
avs_frame_p *frame,
|
|
|
|
|
ES_unit_p unit)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
byte *data = unit->data;
|
|
|
|
|
avs_frame_p new = malloc(SIZEOF_AVS_FRAME);
|
|
|
|
|
if (new == NULL)
|
|
|
|
|
{
|
2009-02-23 21:05:51 +00:00
|
|
|
|
print_err("### Unable to allocate AVS frame datastructure\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = build_ES_unit_list(&(new->list));
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
2009-02-23 21:05:51 +00:00
|
|
|
|
print_err("### Unable to allocate internal list for AVS frame\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
free(new);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Deduce what we can from the first unit of the "frame"
|
|
|
|
|
new->start_code = unit->start_code;
|
|
|
|
|
if (is_avs_frame_item(unit))
|
|
|
|
|
{
|
|
|
|
|
new->picture_coding_type = avs_picture_coding_type(unit);
|
|
|
|
|
new->is_frame = TRUE;
|
|
|
|
|
new->is_sequence_header = FALSE;
|
|
|
|
|
if (new->picture_coding_type != AVS_I_PICTURE_CODING)
|
|
|
|
|
new->picture_distance = (data[6] << 2) | ((data[7] & 0xC0) >> 6);
|
|
|
|
|
else
|
|
|
|
|
// I frames *do* have a picture_distance field, but I'm not interested
|
|
|
|
|
// and it takes more work to find it...
|
|
|
|
|
new->picture_distance = 0;
|
|
|
|
|
}
|
|
|
|
|
else if (is_avs_seq_header_item(unit))
|
|
|
|
|
{
|
|
|
|
|
new->is_frame = FALSE;
|
|
|
|
|
new->is_sequence_header = TRUE;
|
|
|
|
|
new->picture_coding_type = 0xFF; // Meaningless value, just in case
|
|
|
|
|
new->aspect_ratio = (data[10] & 0x3C) >> 2;
|
|
|
|
|
new->frame_rate_code = ((data[10] & 0x03) << 2) | ((data[11] & 0xC0) >> 4);
|
|
|
|
|
#if DEBUG
|
2009-02-23 21:05:51 +00:00
|
|
|
|
fprint_msg("aspect_ratio=%d, frame_rate_code=%d (%.2f)\n",new->aspect_ratio,
|
|
|
|
|
new->frame_rate_code,avs_frame_rate(new->frame_rate_code));
|
2008-04-14 04:09:29 +00:00
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
else if (is_avs_seq_end_item(unit))
|
|
|
|
|
{
|
|
|
|
|
new->is_frame = FALSE;
|
|
|
|
|
new->is_sequence_header = FALSE;
|
|
|
|
|
new->picture_coding_type = 0xFF; // Meaningless value, just in case
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2009-02-23 21:05:51 +00:00
|
|
|
|
fprint_err("!!! Building AVS frame that starts with a %s (%02x)\n",
|
|
|
|
|
avs_start_code_str(unit->start_code),unit->start_code);
|
2008-04-14 04:09:29 +00:00
|
|
|
|
new->is_frame = FALSE;
|
|
|
|
|
new->is_sequence_header = FALSE;
|
|
|
|
|
new->picture_coding_type = 0xFF; // Meaningless value, just in case
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = append_to_avs_frame(new,unit);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
2009-02-23 21:05:51 +00:00
|
|
|
|
fprint_err("### Error appending first ES unit to AVS %s\n",
|
|
|
|
|
avs_start_code_str(unit->start_code));
|
2008-04-14 04:09:29 +00:00
|
|
|
|
free_avs_frame(&new);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*frame = new;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Free an AVS "frame".
|
|
|
|
|
*
|
|
|
|
|
* Clears the datastructure, frees it, and returns `frame` as NULL.
|
|
|
|
|
*
|
|
|
|
|
* Does nothing if `frame` is already NULL.
|
|
|
|
|
*/
|
|
|
|
|
extern void free_avs_frame(avs_frame_p *frame)
|
|
|
|
|
{
|
|
|
|
|
avs_frame_p pic = *frame;
|
|
|
|
|
|
|
|
|
|
if (pic == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (pic->list != NULL)
|
|
|
|
|
free_ES_unit_list(&pic->list);
|
|
|
|
|
|
|
|
|
|
free(*frame);
|
|
|
|
|
*frame = NULL;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if DEBUG_GET_NEXT_PICTURE
|
|
|
|
|
/*
|
|
|
|
|
* Print a representation of an item for debugging
|
|
|
|
|
*/
|
|
|
|
|
static void _show_item(ES_unit_p unit)
|
|
|
|
|
{
|
2009-02-23 21:05:51 +00:00
|
|
|
|
print_msg("__ ");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
if (unit == NULL)
|
|
|
|
|
{
|
2009-02-23 21:05:51 +00:00
|
|
|
|
print_msg("<no ES unit>\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (is_avs_frame_item(unit))
|
2009-02-23 21:05:51 +00:00
|
|
|
|
fprint_msg("%s frame",AVS_PICTURE_CODING_STR(avs_picture_coding_type(unit)));
|
2008-04-14 04:09:29 +00:00
|
|
|
|
else
|
2009-02-23 21:05:51 +00:00
|
|
|
|
fprint_msg("%s",avs_start_code_str(unit->start_code));
|
|
|
|
|
fprint_msg(" at " OFFSET_T_FORMAT "/%d for %d\n",
|
2008-04-14 04:09:29 +00:00
|
|
|
|
unit->start_posn.infile,unit->start_posn.inpacket,unit->data_len);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Retrieve the the next AVS "frame".
|
|
|
|
|
*
|
|
|
|
|
* The AVS "frame" returned can be one of:
|
|
|
|
|
*
|
|
|
|
|
* 1. A frame, including its data.
|
|
|
|
|
* 2. A sequence header, including its sequence extension, if any.
|
|
|
|
|
* 3. A sequence end.
|
|
|
|
|
*
|
|
|
|
|
* - `context` is the AVS frame reading context.
|
|
|
|
|
* - if `verbose` is true, then extra information will be output
|
|
|
|
|
* - if `quiet` is true, then only errors will be reported
|
|
|
|
|
* - `frame` is the AVS "frame", containing a frame, 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.
|
|
|
|
|
*/
|
|
|
|
|
static int get_next_avs_single_frame(avs_context_p context,
|
|
|
|
|
int verbose,
|
|
|
|
|
avs_frame_p *frame)
|
|
|
|
|
{
|
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
|
|
int in_sequence_header = FALSE;
|
|
|
|
|
int in_sequence_end = FALSE;
|
|
|
|
|
int in_frame = FALSE;
|
|
|
|
|
int last_was_slice = FALSE;
|
|
|
|
|
|
|
|
|
|
ES_unit_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-02-23 21:05:51 +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 "frame"
|
|
|
|
|
for (;;)
|
|
|
|
|
{
|
|
|
|
|
if (item == NULL)
|
|
|
|
|
{
|
|
|
|
|
err = find_and_build_next_ES_unit(context->es,&item);
|
|
|
|
|
if (err) return err;
|
|
|
|
|
}
|
|
|
|
|
if (is_avs_frame_item(item))
|
|
|
|
|
{
|
|
|
|
|
in_frame = TRUE;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (is_avs_seq_header_item(item))
|
|
|
|
|
{
|
|
|
|
|
in_sequence_header = TRUE;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (is_avs_seq_end_item(item))
|
|
|
|
|
{
|
|
|
|
|
in_sequence_end = TRUE;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
#if DEBUG_GET_NEXT_PICTURE
|
|
|
|
|
else if (verbose)
|
|
|
|
|
_show_item(item);
|
|
|
|
|
#endif
|
|
|
|
|
free_ES_unit(&item);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if DEBUG_GET_NEXT_PICTURE
|
|
|
|
|
if (verbose)
|
|
|
|
|
{
|
2009-02-23 21:05:51 +00:00
|
|
|
|
print_msg("__ --------------------------------- <start frame>\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
_show_item(item);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
err = build_avs_frame(context,frame,item);
|
|
|
|
|
if (err) return 1;
|
|
|
|
|
|
|
|
|
|
free_ES_unit(&item);
|
|
|
|
|
|
|
|
|
|
if (in_sequence_end)
|
|
|
|
|
{
|
|
|
|
|
// A sequence end is a single item, so we're done
|
|
|
|
|
#if DEBUG_GET_NEXT_PICTURE
|
|
|
|
|
if (verbose)
|
2009-02-23 21:05:51 +00:00
|
|
|
|
print_msg("__ --------------------------------- <end frame>\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
#endif
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Now find all the rest of the frame/sequence header
|
|
|
|
|
for (;;)
|
|
|
|
|
{
|
|
|
|
|
err = find_and_build_next_ES_unit(context->es,&item);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
if (err != EOF)
|
|
|
|
|
free_avs_frame(frame);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (in_frame)
|
|
|
|
|
{
|
|
|
|
|
// Have we just finished a frame?
|
|
|
|
|
// We know we have if the last item was a slice, but this one isn't
|
|
|
|
|
if (last_was_slice && !is_avs_slice_item(item))
|
|
|
|
|
break;
|
|
|
|
|
last_was_slice = is_avs_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_avs_extension_start_item(item) &&
|
|
|
|
|
!is_avs_user_data_item(item))
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if DEBUG_GET_NEXT_PICTURE
|
|
|
|
|
if (verbose)
|
|
|
|
|
{
|
|
|
|
|
if (!had_slice)
|
|
|
|
|
_show_item(item);
|
|
|
|
|
if (is_avs_slice_item(item))
|
|
|
|
|
{
|
|
|
|
|
num_slices ++;
|
|
|
|
|
last_slice_start_code = item->start_code;
|
|
|
|
|
if (!had_slice)
|
|
|
|
|
had_slice = TRUE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// Don't forget to remember the actual item
|
|
|
|
|
err = append_to_avs_frame(*frame,item);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
2009-02-23 21:05:51 +00:00
|
|
|
|
print_err("### Error adding item to AVS sequence header\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
free_avs_frame(frame);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
free_ES_unit(&item);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (in_frame)
|
|
|
|
|
context->frame_index ++;
|
|
|
|
|
|
|
|
|
|
context->last_item = item;
|
|
|
|
|
#if DEBUG_GET_NEXT_PICTURE
|
|
|
|
|
if (verbose)
|
|
|
|
|
{
|
|
|
|
|
if (in_frame)
|
|
|
|
|
{
|
|
|
|
|
if (num_slices > 1)
|
|
|
|
|
{
|
|
|
|
|
ES_unit_p unit = &(*frame)->list->array[(*frame)->list->length-1];
|
2009-02-23 21:05:51 +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-02-23 21:05:51 +00:00
|
|
|
|
fprint_msg("__ (%2d slices)\n",num_slices);
|
2008-04-14 04:09:29 +00:00
|
|
|
|
}
|
2009-02-23 21:05:51 +00:00
|
|
|
|
print_msg("__ --------------------------------- <end frame>\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
if (in_frame || in_sequence_header)
|
|
|
|
|
_show_item(item);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Retrieve the the next AVS "frame".
|
|
|
|
|
*
|
|
|
|
|
* The AVS "frame" returned can be one of:
|
|
|
|
|
*
|
|
|
|
|
* 1. A frame, including its slices.
|
|
|
|
|
* 2. A sequence header, including its sequence extension, if any.
|
|
|
|
|
* 3. A sequence end.
|
|
|
|
|
*
|
|
|
|
|
* Specifically, the next AVS "frame" is retrieved from the input stream.
|
|
|
|
|
*
|
|
|
|
|
* If that "frame" represents a sequence header or a frame, it is returned.
|
|
|
|
|
*
|
|
|
|
|
* Note that if the context is associated with a reverse context,
|
|
|
|
|
* then appropriate frames/sequence headers will automatically be
|
|
|
|
|
* remembered therein.
|
|
|
|
|
*
|
|
|
|
|
* - `context` is the AVS frame reading context.
|
|
|
|
|
* - if `verbose` is true, then extra information will be output
|
|
|
|
|
* - if `quiet` is true, then only errors will be reported
|
|
|
|
|
* - `frame` is the AVS "frame", containing a frame, 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_avs_frame(avs_context_p context,
|
|
|
|
|
int verbose,
|
|
|
|
|
int quiet,
|
|
|
|
|
avs_frame_p *frame)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
err = get_next_avs_single_frame(context,verbose,frame);
|
|
|
|
|
if (err) return err;
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
if (context->reverse_data)
|
|
|
|
|
{
|
|
|
|
|
err = maybe_remember_this_frame(context,verbose,*frame);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
free_avs_frame(frame);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Write out an AVS frame as TS
|
|
|
|
|
*
|
|
|
|
|
* - `tswriter` is TS the output stream
|
|
|
|
|
* - `frame` is the frame 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_avs_frame_as_TS(TS_writer_p tswriter,
|
|
|
|
|
avs_frame_p frame,
|
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 (frame == NULL || frame->list == NULL)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
list = frame->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-02-23 21:05:51 +00:00
|
|
|
|
print_err("### Error writing out frame list to TS\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Write out an AVS frame as TS, with PTS timing in the first PES packet
|
|
|
|
|
* (and PCR timing in the first TS of the frame).
|
|
|
|
|
*
|
|
|
|
|
* - `frame` is the frame to write out
|
|
|
|
|
* - `tswriter` is the TS context to write with
|
|
|
|
|
* - `video_pid` is the PID to use to write the data
|
|
|
|
|
* - `got_pts` is TRUE if we have a PTS value, in which case
|
|
|
|
|
* - `pts` is said PTS value
|
|
|
|
|
* - `got_dts` is TRUE if we also have DTS, in which case
|
|
|
|
|
* - `dts` is said DTS value.
|
|
|
|
|
*
|
|
|
|
|
* If we are given a DTS (which must, by definition, always go up) we will also
|
|
|
|
|
* use it as the value for PCR.
|
|
|
|
|
*
|
|
|
|
|
* Returns 0 if it succeeds, 1 if some error occurs.
|
|
|
|
|
*/
|
|
|
|
|
extern int write_avs_frame_as_TS_with_pts_dts(avs_frame_p frame,
|
|
|
|
|
TS_writer_p tswriter,
|
2008-10-18 15:04:34 +00:00
|
|
|
|
uint32_t video_pid,
|
2008-04-14 04:09:29 +00:00
|
|
|
|
int got_pts,
|
2008-10-18 15:04:34 +00:00
|
|
|
|
uint64_t pts,
|
2008-04-14 04:09:29 +00:00
|
|
|
|
int got_dts,
|
2008-10-18 15:04:34 +00:00
|
|
|
|
uint64_t dts)
|
2008-04-14 04:09:29 +00:00
|
|
|
|
{
|
|
|
|
|
int ii;
|
|
|
|
|
ES_unit_list_p list;
|
|
|
|
|
|
|
|
|
|
if (frame == NULL || frame->list == NULL)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
list = frame->list;
|
|
|
|
|
|
|
|
|
|
for (ii = 0; ii < list->length; ii++)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
ES_unit_p unit = &(list->array[ii]);
|
|
|
|
|
|
|
|
|
|
// Only write the first PES packet out with PTS
|
|
|
|
|
if (ii == 0)
|
|
|
|
|
err = write_ES_as_TS_PES_packet_with_pts_dts(tswriter,
|
|
|
|
|
unit->data,
|
|
|
|
|
unit->data_len,
|
|
|
|
|
video_pid,
|
|
|
|
|
DEFAULT_VIDEO_STREAM_ID,
|
|
|
|
|
got_pts,pts,
|
|
|
|
|
got_dts,dts);
|
|
|
|
|
else
|
|
|
|
|
err = write_ES_as_TS_PES_packet(tswriter,
|
|
|
|
|
unit->data,unit->data_len,
|
|
|
|
|
video_pid,DEFAULT_VIDEO_STREAM_ID);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
2009-02-23 21:05:51 +00:00
|
|
|
|
print_err("### Error writing out frame list to TS\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Write out an AVS frame as TS, with PCR timing in the first TS of the
|
|
|
|
|
* frame.
|
|
|
|
|
*
|
|
|
|
|
* - `frame` is the frame to write out
|
|
|
|
|
* - `tswriter` is the TS context to write with
|
|
|
|
|
* - `video_pid` is the PID to use to write the data
|
|
|
|
|
* - `pcr_base` and `pcr_extn` encode the PCR value.
|
|
|
|
|
*
|
|
|
|
|
* Returns 0 if it succeeds, 1 if some error occurs.
|
|
|
|
|
*/
|
|
|
|
|
extern int write_avs_frame_as_TS_with_PCR(avs_frame_p frame,
|
|
|
|
|
TS_writer_p tswriter,
|
2008-10-18 15:04:34 +00:00
|
|
|
|
uint32_t video_pid,
|
|
|
|
|
uint64_t pcr_base,
|
|
|
|
|
uint32_t pcr_extn)
|
2008-04-14 04:09:29 +00:00
|
|
|
|
{
|
|
|
|
|
int ii;
|
|
|
|
|
ES_unit_list_p list;
|
|
|
|
|
|
|
|
|
|
if (frame == NULL || frame->list == NULL)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
list = frame->list;
|
|
|
|
|
|
|
|
|
|
for (ii = 0; ii < list->length; ii++)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
ES_unit_p unit = &(list->array[ii]);
|
|
|
|
|
|
|
|
|
|
// Only write the first PES packet out with PCR
|
|
|
|
|
if (ii == 0)
|
|
|
|
|
err = write_ES_as_TS_PES_packet_with_pcr(tswriter,
|
|
|
|
|
unit->data,
|
|
|
|
|
unit->data_len,
|
|
|
|
|
video_pid,
|
|
|
|
|
DEFAULT_VIDEO_STREAM_ID,
|
|
|
|
|
pcr_base,pcr_extn);
|
|
|
|
|
else
|
|
|
|
|
err = write_ES_as_TS_PES_packet(tswriter,
|
|
|
|
|
unit->data,unit->data_len,
|
|
|
|
|
video_pid,DEFAULT_VIDEO_STREAM_ID);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
2009-02-23 21:05:51 +00:00
|
|
|
|
print_err("### Error writing out frame list to TS\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Write out a frame (as stored in an ES unit list) as ES
|
|
|
|
|
*
|
|
|
|
|
* - `output` is the ES output file
|
|
|
|
|
* - `frame` is the frame to write out
|
|
|
|
|
*
|
|
|
|
|
* Returns 0 if it succeeds, 1 if some error occurs.
|
|
|
|
|
*/
|
|
|
|
|
extern int write_avs_frame_as_ES(FILE *output,
|
|
|
|
|
avs_frame_p frame)
|
|
|
|
|
{
|
|
|
|
|
int ii;
|
|
|
|
|
ES_unit_list_p list;
|
|
|
|
|
|
|
|
|
|
if (frame == NULL || frame->list == NULL)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
list = frame->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-02-23 21:05:51 +00:00
|
|
|
|
print_err("### Error writing out frame list to ES\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Report on an AVS frame's contents.
|
|
|
|
|
*
|
|
|
|
|
* - `stream` is where to write the information
|
|
|
|
|
* - `frame` is the frame 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_avs_frame(avs_frame_p frame,
|
2008-04-14 04:09:29 +00:00
|
|
|
|
int report_data)
|
|
|
|
|
{
|
|
|
|
|
if (frame->is_frame)
|
|
|
|
|
{
|
2009-02-23 21:05:51 +00:00
|
|
|
|
fprint_msg("%s #%02d",
|
|
|
|
|
AVS_PICTURE_CODING_STR(frame->picture_coding_type),
|
|
|
|
|
frame->picture_distance);
|
|
|
|
|
print_msg("\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
}
|
|
|
|
|
else if (frame->is_sequence_header)
|
|
|
|
|
{
|
2009-02-23 21:05:51 +00:00
|
|
|
|
print_msg("Sequence header: ");
|
|
|
|
|
fprint_msg(" frame rate %d (%.2f), aspect ratio %d (%s)",
|
|
|
|
|
frame->frame_rate_code,
|
|
|
|
|
avs_frame_rate(frame->frame_rate_code),
|
|
|
|
|
frame->aspect_ratio,
|
|
|
|
|
(frame->aspect_ratio==1?"SAR: 1.0":
|
|
|
|
|
frame->aspect_ratio==2?"4/3":
|
|
|
|
|
frame->aspect_ratio==3?"16/9":
|
|
|
|
|
frame->aspect_ratio==4?"2.21/1":"???"));
|
|
|
|
|
print_msg("\n");
|
2008-04-14 04:09:29 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2009-02-23 21:05:51 +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",frame->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:
|