kopia lustrzana https://github.com/F5OEO/tstools
881 wiersze
24 KiB
C
881 wiersze
24 KiB
C
/*
|
||
* 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"
|
||
#include "avs_fns.h"
|
||
#include "es_fns.h"
|
||
#include "ts_fns.h"
|
||
#include "reverse_fns.h"
|
||
#include "misc_fns.h"
|
||
|
||
#define DEBUG 0
|
||
#define DEBUG_GET_NEXT_PICTURE 0
|
||
|
||
|
||
/*
|
||
* 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";
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Print out information derived from the start code, to the given stream.
|
||
*/
|
||
extern void print_avs_start_code_str(FILE *stream,
|
||
byte start_code)
|
||
{
|
||
if (stream != NULL)
|
||
{
|
||
if (start_code < 0xB0)
|
||
fprintf(stream,"Slice %02x\n",start_code & 0xAF);
|
||
else
|
||
fprintf(stream,"%s",avs_start_code_str(start_code));
|
||
}
|
||
}
|
||
|
||
// ------------------------------------------------------------
|
||
// 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)
|
||
{
|
||
fprintf(stderr,"### Unable to allocate AVS context datastructure\n");
|
||
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
|
||
{
|
||
fprintf(stderr,"AVS Picture coding type %d (in %02x)\n",
|
||
picture_coding_type,unit->data[3]);
|
||
return 0;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
fprintf(stderr,"AVS 'frame' with start code %02x does not have picture coding type\n",
|
||
unit->data[0]);
|
||
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)
|
||
{
|
||
fprintf(stderr,"### Unable to allocate AVS frame datastructure\n");
|
||
return 1;
|
||
}
|
||
|
||
err = build_ES_unit_list(&(new->list));
|
||
if (err)
|
||
{
|
||
fprintf(stderr,"### Unable to allocate internal list for AVS frame\n");
|
||
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
|
||
printf("aspect_ratio=%d, frame_rate_code=%d (%.2f)\n",new->aspect_ratio,
|
||
new->frame_rate_code,avs_frame_rate(new->frame_rate_code));
|
||
#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
|
||
{
|
||
fprintf(stderr,
|
||
"!!! Building AVS frame that starts with a %s (%02x)\n",
|
||
avs_start_code_str(unit->start_code),unit->start_code);
|
||
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)
|
||
{
|
||
fprintf(stderr,"### Error appending first ES unit to AVS %s\n",
|
||
avs_start_code_str(unit->start_code));
|
||
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 0
|
||
/*
|
||
* Remember a frame for future reversing, if it's an I frame or a
|
||
* sequence header
|
||
*
|
||
* Returns 0 if it succeeds, 1 if some error occurs.
|
||
*/
|
||
static int maybe_remember_this_frame(avs_context_p avs,
|
||
int verbose,
|
||
avs_frame_p this_frame)
|
||
{
|
||
int err;
|
||
ES_offset start_posn = {0,0};
|
||
u_int32 num_bytes = 0;
|
||
if (this_frame->is_frame)
|
||
{
|
||
if (this_frame->picture_coding_type == AVS_I_PICTURE_CODING)
|
||
{
|
||
// It's an I frame - we want to remember it in our reverse list
|
||
(avs->count_since_seq_hdr) ++;
|
||
|
||
err = get_ES_unit_list_bounds(this_frame->list,&start_posn,&num_bytes);
|
||
if (err)
|
||
{
|
||
fprintf(stderr,
|
||
"### Error working out position/size of AVS frame\n");
|
||
return 1;
|
||
}
|
||
|
||
err = remember_reverse_avs_data(avs->reverse_data,avs->frame_index,
|
||
start_posn,num_bytes,
|
||
avs->count_since_seq_hdr,0);
|
||
if (err)
|
||
{
|
||
fprintf(stderr,
|
||
"### Error remembering reversing data for AVS item\n");
|
||
return 1;
|
||
}
|
||
if (verbose)
|
||
printf("REMEMBER I frame %5d at " OFFSET_T_FORMAT_08
|
||
"/%04d for %5d\n",avs->frame_index,
|
||
start_posn.infile,start_posn.inpacket,num_bytes);
|
||
}
|
||
}
|
||
else if (this_frame->is_sequence_header)
|
||
{
|
||
// It's a sequence header - remember it for the next frame
|
||
avs->count_since_seq_hdr = 0;
|
||
err = get_ES_unit_list_bounds(this_frame->list,&start_posn,&num_bytes);
|
||
if (err)
|
||
{
|
||
fprintf(stderr,"### Error working out position/size of AVS"
|
||
" sequence header for reversing data\n");
|
||
return 1;
|
||
}
|
||
err = remember_reverse_avs_data(avs->reverse_data,0,
|
||
start_posn,num_bytes,0);
|
||
if (err)
|
||
{
|
||
fprintf(stderr,"### Error remembering reversing data for AVS 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;
|
||
}
|
||
#endif
|
||
|
||
#if DEBUG_GET_NEXT_PICTURE
|
||
/*
|
||
* Print a representation of an item for debugging
|
||
*/
|
||
static void _show_item(ES_unit_p unit)
|
||
{
|
||
printf("__ ");
|
||
if (unit == NULL)
|
||
{
|
||
printf("<no ES unit>\n");
|
||
return;
|
||
}
|
||
if (is_avs_frame_item(unit))
|
||
printf("%s frame",AVS_PICTURE_CODING_STR(avs_picture_coding_type(unit)));
|
||
else
|
||
printf("%s",avs_start_code_str(unit->start_code));
|
||
printf(" at " OFFSET_T_FORMAT "/%d for %d\n",
|
||
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;
|
||
if (verbose && context->last_item) printf("__ reuse last item\n");
|
||
#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)
|
||
{
|
||
printf("__ --------------------------------- <start frame>\n");
|
||
_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)
|
||
printf("__ --------------------------------- <end frame>\n");
|
||
#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)
|
||
{
|
||
fprintf(stderr,"### Error adding item to AVS sequence header\n");
|
||
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];
|
||
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 frame>\n");
|
||
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,
|
||
u_int32 pid)
|
||
{
|
||
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)
|
||
{
|
||
fprintf(stderr,"### Error writing out frame list to TS\n");
|
||
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,
|
||
u_int32 video_pid,
|
||
int got_pts,
|
||
u_int64 pts,
|
||
int got_dts,
|
||
u_int64 dts)
|
||
{
|
||
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)
|
||
{
|
||
fprintf(stderr,"### Error writing out frame list to TS\n");
|
||
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,
|
||
u_int32 video_pid,
|
||
u_int64 pcr_base,
|
||
u_int32 pcr_extn)
|
||
{
|
||
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)
|
||
{
|
||
fprintf(stderr,"### Error writing out frame list to TS\n");
|
||
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)
|
||
{
|
||
fprintf(stderr,"### Error writing out frame list to ES\n");
|
||
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
|
||
*/
|
||
extern void report_avs_frame(FILE *stream,
|
||
avs_frame_p frame,
|
||
int report_data)
|
||
{
|
||
if (frame->is_frame)
|
||
{
|
||
printf("%s #%02d",
|
||
AVS_PICTURE_CODING_STR(frame->picture_coding_type),
|
||
frame->picture_distance);
|
||
printf("\n");
|
||
}
|
||
else if (frame->is_sequence_header)
|
||
{
|
||
printf("Sequence header: ");
|
||
printf(" 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":"???"));
|
||
printf("\n");
|
||
}
|
||
else
|
||
{
|
||
printf("Sequence end\n");
|
||
}
|
||
if (report_data)
|
||
report_ES_unit_list(stream,"ES units",frame->list);
|
||
}
|
||
|
||
// Local Variables:
|
||
// tab-width: 8
|
||
// indent-tabs-mode: nil
|
||
// c-basic-offset: 2
|
||
// End:
|
||
// vim: set tabstop=8 shiftwidth=2 expandtab:
|