F5OEO-tstools/nalunit.c

1984 wiersze
62 KiB
C

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

/*
* Utilities for working with NAL units in H.264 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 <string.h>
#include <math.h>
#include <errno.h>
#ifdef _WIN32
#include <io.h>
#else // _WIN32
#include <unistd.h>
#endif // _WIN32
#include "compat.h"
#include "printing_fns.h"
#include "es_fns.h"
#include "ts_fns.h"
#include "bitdata_fns.h"
#include "nalunit_fns.h"
#include "misc_fns.h"
#include "printing_fns.h"
#define DEBUG 0
#define REPORT_NAL_SHOWS_ADDRESS 0
/*
* Request details of the NAL unit contents as they are read
*/
extern void set_show_nal_reading_details(nal_unit_context_p context,
int show)
{
context->show_nal_details = show;
}
// ------------------------------------------------------------
// NAL unit context
// ------------------------------------------------------------
/*
* Build a new NAL unit context, for reading NAL units from an ES.
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int build_nal_unit_context(ES_p es,
nal_unit_context_p *context)
{
int err;
nal_unit_context_p new = malloc(SIZEOF_NAL_UNIT_CONTEXT);
if (new == NULL)
{
print_err("### Unable to allocate NAL unit context datastructure\n");
return 1;
}
new->es = es;
new->count = 0;
new->show_nal_details = FALSE;
err = build_param_dict(&new->seq_param_dict);
if (err)
{
free(new);
return err;
}
err = build_param_dict(&new->pic_param_dict);
if (err)
{
free_param_dict(&new->seq_param_dict);
free(new);
return err;
}
*context = new;
return 0;
}
/*
* Free a NAL unit context datastructure.
*
* Clears the datastructure, frees it, and returns `context` as NULL.
*
* Does nothing if `context` is already NULL.
*/
extern void free_nal_unit_context(nal_unit_context_p *context)
{
nal_unit_context_p cc = *context;
if (cc == NULL)
return;
free_param_dict(&cc->seq_param_dict);
free_param_dict(&cc->pic_param_dict);
free(*context);
*context = NULL;
return;
}
/*
* Rewind a file being read as NAL units.
*
* A thin jacket for `seek_ES`.
*
* Doesn't unset the sequence and picture parameter dictionaries
* that have been built up when reading the file - this may possibly
* not be the desired behaviour, but should be OK for well behaved files.
*
* Returns 0 if all goes well, 1 if something goes wrong.
*/
extern int rewind_nal_unit_context(nal_unit_context_p context)
{
ES_offset start_of_file = {0,0};
return seek_ES(context->es,start_of_file);
}
// ------------------------------------------------------------
// Basic NAL unit datastructure stuff
// ------------------------------------------------------------
/*
* Build a new NAL unit datastructure.
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int build_nal_unit(nal_unit_p *nal)
{
int err;
nal_unit_p new = malloc(SIZEOF_NAL_UNIT);
if (new == NULL)
{
print_err("### Unable to allocate NAL unit datastructure\n");
return 1;
}
err = setup_ES_unit(&(new->unit));
if (err)
{
print_err("### Unable to allocate NAL unit data buffer\n");
free(new);
return 1;
}
// However, we haven't yet got any actual data
new->data = NULL; // Only set to unit.data[3] when we *have* a NAL unit
new->data_len = 0;
new->rbsp = NULL;
new->rbsp_len = 0;
new->bit_data = NULL;
new->nal_unit_type = NAL_UNSPECIFIED;
new->starts_picture_decided = FALSE;
new->starts_picture = FALSE;
new->start_reason = NULL;
new->decoded = FALSE;
*nal = new;
return 0;
}
/*
* Tidy up a NAL unit datastructure after we've finished with it.
*/
static inline void clear_nal_unit(nal_unit_p nal)
{
clear_ES_unit(&(nal->unit));
nal->data = NULL;
nal->data_len = 0;
if (nal->rbsp != NULL)
{
free(nal->rbsp);
nal->rbsp_len = 0;
}
free_bitdata(&nal->bit_data);
}
/*
* Tidy up and free a NAL unit datastructure after we've finished with it.
*
* Empties the NAL unit datastructure, frees it, and sets `nal` to NULL.
*
* If `nal` is already NULL, does nothing.
*/
extern void free_nal_unit(nal_unit_p *nal)
{
if (*nal == NULL)
return;
clear_nal_unit(*nal);
free(*nal);
*nal = NULL;
}
// ------------------------------------------------------------
// Interpretive functions
// ------------------------------------------------------------
/*
* Process `data` to remove any emulation prevention bytes.
*
* That is, sort out 0x000003 sequences.
*
* Basically, if the data stream was *meant* to contain 0x0000xx,
* then it will instead contain 0x000003xx, and so we need to remove
* that extra 0x03 (in fact, there are also rules about what values
* 0xxx can take, but we shall assume that they have been followed
* by the encoder). See H.264 7.3.1
*
* - `data` is the NAL unit data array, including the first byte,
* which contains the nal_ref_idc and nal_unit_type.
* - `rbsp` is the processed data, not including said first byte.
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
static int remove_emulation_prevention(byte data[],
int data_len,
byte *rbsp[],
int *rbsp_len)
{
int ii;
int posn = 0;
byte prev1 = 27; // J. Random Number
byte prev2 = 27;
byte *tgt = NULL;
// We know we're going to produce data that is no longer than our input
tgt = malloc(data_len);
if (tgt == NULL)
{
print_err("### Cannot malloc RBSP target array\n");
return 1;
}
for (ii=1; ii<data_len; ii++) // NB: ignoring that first byte
{
if (prev2 == 0x00 && prev1 == 0x00 && data[ii] == 0x03)
; // ignore the emulation prevention 03 byte
else
tgt[posn++] = data[ii];
prev2 = prev1;
prev1 = data[ii];
}
*rbsp = tgt;
*rbsp_len = posn;
return 0;
}
/*
* Prepare for reading bit data from the RBSP.
*
* (Note that calling this more than once is safe.)
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
static inline int prepare_rbsp(nal_unit_p nal)
{
int err;
bitdata_p bd=NULL;
if (nal->bit_data != NULL)
return 0;
// Only remove the emulation 03 bytes when we think we need to
// (of course, we *could* do this as part of the bitdata byte
// reading code, but unless/until it's clear that the tradeoff
// in time/complexity is worth it, let's not bother).
if (nal->rbsp == NULL)
{
err = remove_emulation_prevention(nal->data,nal->data_len,
&(nal->rbsp),&(nal->rbsp_len));
if (err)
{
print_err("### Error removing emulation prevention bytes\n");
return 1;
}
}
err = build_bitdata(&bd,nal->rbsp,nal->rbsp_len);
if (err)
{
print_err("### Unable to build bitdata datastructure for NAL RBSP\n");
return 1;
}
nal->bit_data = bd;
return 0;
}
/*
* Look at the start of the slice header.
*
* Assumes that this *is* a NAL unit representing a slice.
*
* Don't call this directly - call read_rbsp_data() instead.
*
* If either of `seq_param_dict` or `pic_param_dict` is NULL, then
* we only read the first few entries in the RBSP (including the
* slice_type and the pic_parameter_set_id).
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
static int read_slice_data(nal_unit_p nal,
param_dict_p seq_param_dict,
param_dict_p pic_param_dict,
int show_nal_details)
{
int err;
bitdata_p bd = nal->bit_data;
uint32_t temp;
nal_slice_data_p data = &(nal->u.slice);
nal_seq_param_data_p seq_param_data = NULL;
nal_pic_param_data_p pic_param_data = NULL;
#define CHECK(name) \
if (err) \
{ \
fprint_err("### Error reading %s field from slice data\n",(name)); \
}
err = read_exp_golomb(bd,&data->first_mb_in_slice);
CHECK("first_mb_in_slice");
err = read_exp_golomb(bd,&data->slice_type);
CHECK("slice_type");
err = read_exp_golomb(bd,&temp);
CHECK("pic_parameter_set_id");
data->pic_parameter_set_id = temp;
if (show_nal_details)
{
fprint_msg("@@ NAL " OFFSET_T_FORMAT_08 "/%04d: size %d\n"
" nal_ref_idc %x nal_unit_type %02x (%s)\n",
nal->unit.start_posn.infile,nal->unit.start_posn.inpacket,
nal->data_len,
nal->nal_ref_idc,nal->nal_unit_type,
NAL_UNIT_TYPE_STR(nal->nal_unit_type));
fprint_msg(" first_mb_in_slice %u, slice_type %u (%s),"
" pic_parameter_set_id %d\n",data->first_mb_in_slice,
data->slice_type,NAL_SLICE_TYPE_STR(data->slice_type),
data->pic_parameter_set_id);
}
// If we don't have sequence/parameter sets, then we can't go any
// further. Assume the caller knew what they were doing...
if (seq_param_dict == NULL || pic_param_dict == NULL)
return 0;
// To read the frame number we need to know how long it is, which is
// determined by the value of log2_max_frame_num, which is defined in the
// relevant sequence parameter set, which is determined by the picture
// parameter set whose id we just read.
err = get_pic_param_data(pic_param_dict,data->pic_parameter_set_id,
&pic_param_data);
if (err) return 1;
err = get_seq_param_data(seq_param_dict,pic_param_data->seq_parameter_set_id,
&seq_param_data);
if (err) return 1;
// Whilst we've got the sequence parameter set to hand, it's convenient
// to remember the pic_order_cnt_type locally, so that we don't need to
// look it up again when trying to decide if this is the first VCL NAL
data->seq_param_set_pic_order_cnt_type = seq_param_data->pic_order_cnt_type;
if (show_nal_details)
fprint_msg(" seq_param_set->pic_order_cnt_type %u\n",
data->seq_param_set_pic_order_cnt_type);
err = read_bits(bd,seq_param_data->log2_max_frame_num,&data->frame_num);
CHECK("frame_num");
if (show_nal_details)
fprint_msg(" frame_num %u (%d bits)\n",data->frame_num,
seq_param_data->log2_max_frame_num);
data->field_pic_flag = 0; // value if not present - i.e., a frame
data->bottom_field_flag = 0;
data->bottom_field_flag_present = FALSE;
if (!seq_param_data->frame_mbs_only_flag)
{
err = read_bit(bd,&data->field_pic_flag);
CHECK("field_pic_flag");
if (show_nal_details)
fprint_msg(" field_pic_flag %d\n",data->field_pic_flag);
if (data->field_pic_flag)
{
data->bottom_field_flag_present = TRUE;
err = read_bit(bd,&data->bottom_field_flag);
CHECK("bottom_field_flag");
if (show_nal_details)
fprint_msg(" bottom_field_flag %d\n",data->bottom_field_flag);
}
}
if (nal->nal_unit_type == 5)
{
err = read_exp_golomb(bd,&data->idr_pic_id);
CHECK("idr_pic_id");
if (show_nal_details)
fprint_msg(" idr_pic_id %u\n",data->idr_pic_id);
}
data->delta_pic_order_cnt_bottom = 0; // value if not present
data->delta_pic_order_cnt[0] = 0;
data->delta_pic_order_cnt[1] = 0;
if (seq_param_data->pic_order_cnt_type == 0)
{
err = read_bits(bd,seq_param_data->log2_max_pic_order_cnt_lsb,
&data->pic_order_cnt_lsb);
CHECK("pic_order_cnt_lsb");
if (pic_param_data->pic_order_present_flag && !data->field_pic_flag)
{
err = read_signed_exp_golomb(bd,&data->delta_pic_order_cnt_bottom);
CHECK("delta_pic_order_cnt_bottom");
}
if (show_nal_details)
fprint_msg(" pic_order_cnt_lsb %u (%d bits)\n"
" delta_pic_order_cnt_bottom %d\n",
data->pic_order_cnt_lsb,
seq_param_data->log2_max_pic_order_cnt_lsb,
data->delta_pic_order_cnt_bottom);
}
else if (seq_param_data->pic_order_cnt_type == 1 &&
!seq_param_data->delta_pic_order_always_zero_flag)
{
err = read_signed_exp_golomb(bd,&data->delta_pic_order_cnt[0]);
CHECK("delta_pic_order_cnt[0]");
if (show_nal_details)
fprint_msg(" delta_pic_order_cnt[0] %d\n",data->delta_pic_order_cnt[0]);
if (pic_param_data->pic_order_present_flag && !data->field_pic_flag)
{
err = read_signed_exp_golomb(bd,&data->delta_pic_order_cnt[1]);
CHECK("delta_pic_order_cnt[1]");
if (show_nal_details)
fprint_msg(" delta_pic_order_cnt[1] %d\n",data->delta_pic_order_cnt[1]);
}
}
// Since we're not claiming to support redundant pictures, we could
// give up before reading the next value. However, if we *do* read
// it, we can grumble about/ignore redundant pictures if we get them,
// which seems a useful thing to be able to do.
data->redundant_pic_cnt = 0;
data->redundant_pic_cnt_present = FALSE;
if (pic_param_data->redundant_pic_cnt_present_flag)
{
data->redundant_pic_cnt_present = TRUE;
err = read_exp_golomb(bd,&data->redundant_pic_cnt);
CHECK("redundant_pic_cnt");
if (show_nal_details)
fprint_msg(" redundant_pic_cnt %u\n",data->redundant_pic_cnt);
}
nal->decoded = TRUE;
return 0;
}
/*
* Look at the start of the picture parameter set.
*
* Assumes that this *is* a NAL unit representing a picture parameter set.
*
* Don't call this directly - call read_rbsp_data() instead.
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
static int read_pic_param_set_data(nal_unit_p nal,
int show_nal_details)
{
int err;
bitdata_p bd = nal->bit_data;
nal_pic_param_data_p data = &(nal->u.pic);
uint32_t temp;
// Values that don't get saved into our NAL unit
uint32_t num_ref_idx_10_active;
uint32_t num_ref_idx_11_active;
byte weighted_pred_flag;
uint32_t weighted_bipred_idc;
int32_t pic_init_qp;
int32_t pic_init_qs;
int32_t chroma_qp_index_offset;
byte deblocking_filter_control_present_flag;
byte constrained_intra_pred_flag;
#undef CHECK
#define CHECK(name) \
if (err) \
{ \
fprint_err("### Error reading %s field from picture parameter set\n",\
(name)); \
}
// We need to know the id of this picture parameter set, and also
// which sequence parameter set it refers to
err = read_exp_golomb(bd,&temp);
CHECK("pic_parameter_set_id");
data->pic_parameter_set_id = temp;
err = read_exp_golomb(bd,&temp);
CHECK("seq_parameter_set_id");
data->seq_parameter_set_id = temp;
err = read_bit(bd,&data->entropy_coding_mode_flag);
CHECK("entropy_coding_mode_flag");
// We care about pic_order_present_flag
err = read_bit(bd,&data->pic_order_present_flag);
CHECK("pic_order_present_flag");
if (show_nal_details)
{
fprint_msg("@@ PPS " OFFSET_T_FORMAT_08 "/%04d: size %d\n"
" nal_ref_idc %x nal_unit_type %02x (%s)\n",
nal->unit.start_posn.infile,nal->unit.start_posn.inpacket,
nal->data_len,
nal->nal_ref_idc,nal->nal_unit_type,
NAL_UNIT_TYPE_STR(nal->nal_unit_type));
fprint_msg(" pic_parameter_set_id %d, seq_parameter_set_id %d\n",
data->pic_parameter_set_id,data->seq_parameter_set_id);
fprint_msg(" entropy_coding_mode_flag %d\n",
data->entropy_coding_mode_flag);
fprint_msg(" pic_order_present_flag %d\n",data->pic_order_present_flag);
}
// After this, we don't (at the moment) really need any of the rest.
// However, it is moderately useful (for paranoia's sake) to read the
// redundant_pic_cnt_present_flag at the very end, and we should not
// be interpreting the inside of a picture parameter set very often...
err = read_exp_golomb(bd,&data->num_slice_groups); // minus 1
CHECK("num_slice_groups");
data->num_slice_groups ++;
if (show_nal_details)
fprint_msg(" num_slice_groups %u\n",data->num_slice_groups);
if (data->num_slice_groups > 1)
{
err = read_exp_golomb(bd,&data->slice_group_map_type);
CHECK("slice_group_map_type");
if (show_nal_details)
fprint_msg(" slice_group_map_type %u\n",data->slice_group_map_type);
if (data->slice_group_map_type == 0)
{
// NB: 0..num_slice_groups-1, not 0..num_slice_groups-1 - 1
unsigned int igroup;
for (igroup=0; igroup < data->num_slice_groups; igroup++)
{
uint32_t ignint;
err = read_exp_golomb(bd,&ignint); // run_length_minus1[igroup]
CHECK("run_length_minus1[x]");
}
}
else if (data->slice_group_map_type == 2)
{
// But this time, 0..num_slice_groups-1 - 1
unsigned int igroup;
for (igroup=0; igroup < (data->num_slice_groups - 1); igroup++)
{
uint32_t ignint;
err = read_exp_golomb(bd,&ignint); // top_left[igroup]
CHECK("top_left[x]");
err = read_exp_golomb(bd,&ignint); // bottom_right[igroup]
CHECK("bottom_right[x]");
}
}
else if (data->slice_group_map_type == 3 ||
data->slice_group_map_type == 4 ||
data->slice_group_map_type == 5)
{
byte ignbyte;
uint32_t ignint;
err = read_bit(bd,&ignbyte); // slice_group_change_direction_flag
CHECK("slice_group_change_direction_flag");
err = read_exp_golomb(bd,&ignint); // slice_group_change_rate_minus1
CHECK("slice_group_change_rate_minus1");
}
else if (data->slice_group_map_type == 6)
{
uint32_t pic_size_in_map_units;
unsigned int ii;
int size;
err = read_exp_golomb(bd,&pic_size_in_map_units); // minus 1
pic_size_in_map_units ++;
CHECK("pic_size_in_map_units");
if (show_nal_details)
fprint_msg(" pic_size_in_map_units %u\n",pic_size_in_map_units);
size = (int) ceil(log2(data->num_slice_groups));
// Again, notice the range
for (ii=0; ii < pic_size_in_map_units; ii++)
{
uint32_t ignint;
err = read_bits(bd,size,&ignint); // slice_group_id[ii]
CHECK("slice_group_id[x]");
}
}
}
err = read_exp_golomb(bd,&num_ref_idx_10_active); // minus 1
CHECK("num_ref_idx_10_active");
num_ref_idx_10_active ++;
if (show_nal_details)
fprint_msg(" num_ref_idx_10_active %u\n",num_ref_idx_10_active);
err = read_exp_golomb(bd,&num_ref_idx_11_active); // minus 1
CHECK("num_ref_idx_11_active");
num_ref_idx_11_active ++;
if (show_nal_details)
fprint_msg(" num_ref_idx_11_active %u\n",num_ref_idx_11_active);
err = read_bit(bd,&weighted_pred_flag);
CHECK("weighted_pred_flag");
if (show_nal_details)
fprint_msg(" weighted_pred_flag %d\n",weighted_pred_flag);
err = read_bits(bd,2,&weighted_bipred_idc);
CHECK("weighted_bipred_idc");
if (show_nal_details)
fprint_msg(" weighted_bipred_idc %u\n",weighted_bipred_idc);
err = read_signed_exp_golomb(bd,&pic_init_qp); // minus 26
CHECK("pic_init_qp");
pic_init_qp += 26;
if (show_nal_details)
fprint_msg(" pic_init_qp %d\n",pic_init_qp);
err = read_signed_exp_golomb(bd,&pic_init_qs); // minus 26
CHECK("pic_init_qs");
pic_init_qs += 26;
if (show_nal_details)
fprint_msg(" pic_init_qs %d\n",pic_init_qs);
err = read_signed_exp_golomb(bd,&chroma_qp_index_offset);
CHECK("chroma_qp_index_offset");
if (show_nal_details)
fprint_msg(" chroma_qp_index_offset %d\n",chroma_qp_index_offset);
err = read_bit(bd,&deblocking_filter_control_present_flag);
CHECK("deblocking_filter_control_present_flag");
if (show_nal_details)
fprint_msg(" deblocking_filter_control_present_flag %d\n",
deblocking_filter_control_present_flag);
err = read_bit(bd,&constrained_intra_pred_flag);
CHECK("constrained_intra_pred_flag");
if (show_nal_details)
fprint_msg(" constrained_intra_pred_flag %d\n",constrained_intra_pred_flag);
// We (sort of) care about redundant_pic_cnt_present_flag
// in that we need to know it if we are going to read the later bits
// of a slice header
err = read_bit(bd,&data->redundant_pic_cnt_present_flag);
CHECK("redundant_pic_cnt_present_flag");
if (show_nal_details)
fprint_msg(" redundant_pic_cnt_present_flag %d\n",
data->redundant_pic_cnt_present_flag);
nal->decoded = TRUE;
return 0;
}
/*
* Look at the start of the sequence parameter set.
*
* Assumes that this *is* a NAL unit representing a sequence parameter set.
*
* Don't call this directly - call read_rbsp_data() instead.
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
static int read_seq_param_set_data(nal_unit_p nal,
int show_nal_details)
{
int err;
bitdata_p bd = nal->bit_data;
nal_seq_param_data_p data = &(nal->u.seq);
uint32_t temp;
// Values that don't get saved into our NAL unit
byte reserved_zero_5bits;
uint32_t num_ref_frames;
byte gaps_in_frame_num_value_allowed_flag;
uint32_t pic_width_in_mbs;
uint32_t pic_height_in_map_units;
#undef CHECK
#define CHECK(name) \
if (err) \
{ \
fprint_err("### Error reading %s field from sequence parameter set\n",\
(name)); \
}
err = read_bits_into_byte(bd,8,&data->profile_idc);
CHECK("profile_idc");
err = read_bit(bd,&data->constraint_set0_flag);
CHECK("constraint_set0_flag");
err = read_bit(bd,&data->constraint_set1_flag);
CHECK("constraint_set1_flag");
err = read_bit(bd,&data->constraint_set2_flag);
CHECK("constraint_set2_flag");
err = read_bits_into_byte(bd,5,&reserved_zero_5bits);
CHECK("reserved_zero_5bits");
if (reserved_zero_5bits != 0)
{
fprint_err("### reserved_zero_5bits not zero (%d) in sequence"
" parameter set NAL unit at " OFFSET_T_FORMAT "/%d\n",
reserved_zero_5bits,
nal->unit.start_posn.infile,nal->unit.start_posn.inpacket);
print_data(FALSE," Data",nal->bit_data->data,nal->bit_data->data_len,
20);
// Should we carry on or give up? On the whole, if this is broken
// we can't really trust the rest of its data...
return 1;
}
err = read_bits_into_byte(bd,8,&data->level_idc);
CHECK("level_idc");
if (show_nal_details)
{
fprint_msg("@@ SPS " OFFSET_T_FORMAT_08 "/%04d: size %d\n"
" nal_ref_idc %x nal_unit_type %02x (%s)\n",
nal->unit.start_posn.infile,nal->unit.start_posn.inpacket,
nal->data_len,
nal->nal_ref_idc,nal->nal_unit_type,
NAL_UNIT_TYPE_STR(nal->nal_unit_type));
fprint_msg(" profile_idc %u, constraint set flags: %d %d %d\n",
data->profile_idc,
data->constraint_set0_flag,
data->constraint_set1_flag,
data->constraint_set2_flag);
fprint_msg(" level_idc %u\n",data->level_idc);
}
err = read_exp_golomb(bd,&temp);
CHECK("seq_parameter_set_id");
data->seq_parameter_set_id = temp;
// We care about log2_max_frame_num_minus4
err = read_exp_golomb(bd,&data->log2_max_frame_num); // minus 4
CHECK("log2_max_frame_num");
data->log2_max_frame_num += 4;
// We care about pic_order_cnt_type
err = read_exp_golomb(bd,&data->pic_order_cnt_type);
CHECK("pic_order_cnt_type");
if (show_nal_details)
{
fprint_msg(" seq_parameter_set_id %u\n",data->seq_parameter_set_id);
fprint_msg(" log2_max_frame_num %u\n",data->log2_max_frame_num);
fprint_msg(" pic_order_cnt_type %u\n",data->pic_order_cnt_type);
}
if (data->pic_order_cnt_type == 0)
{
err = read_exp_golomb(bd,&data->log2_max_pic_order_cnt_lsb); // minus 4
CHECK("log2_max_pic_order_cnt_lsb");
data->log2_max_pic_order_cnt_lsb += 4;
if (show_nal_details)
fprint_msg(" log2_max_pic_order_cnt_lsb %u\n",
data->log2_max_pic_order_cnt_lsb);
}
else if (data->pic_order_cnt_type == 1)
{
unsigned int ii;
int32_t offset_for_non_ref_pic;
int32_t offset_for_top_to_bottom_field;
uint32_t num_ref_frames_in_pic_order_cnt_cycle;
err = read_bit(bd,&data->delta_pic_order_always_zero_flag);
CHECK("delta_pic_order_always_zero_flag");
if (show_nal_details)
fprint_msg(" delta_pic_order_always_zero_flag %d\n",
data->delta_pic_order_always_zero_flag);
err = read_signed_exp_golomb(bd,&offset_for_non_ref_pic);
CHECK("offset_for_non_ref_pic");
err = read_signed_exp_golomb(bd,&offset_for_top_to_bottom_field);
CHECK("offset_for_top_to_bottom_field");
err = read_exp_golomb(bd,&num_ref_frames_in_pic_order_cnt_cycle);
CHECK("num_ref_frames_in_pic_order_cnt_cycle");
// The standard says that num_ref_frames_in_pic_order_cnt_cycle
// shall be in the range 0..255
for (ii=0; ii < num_ref_frames_in_pic_order_cnt_cycle; ii++)
{
int32_t offset_for_ref_frame_XX;
err = read_signed_exp_golomb(bd,&offset_for_ref_frame_XX); // XX = [ii]
CHECK("offset_for_ref_frame_X");
}
}
err = read_exp_golomb(bd,&num_ref_frames);
CHECK("num_ref_frames");
if (show_nal_details)
fprint_msg(" num_ref_frames %u\n",num_ref_frames);
err = read_bit(bd,&gaps_in_frame_num_value_allowed_flag);
CHECK("gaps_in_frame_num_value_allowed_flag");
if (show_nal_details)
if (show_nal_details)
fprint_msg(" gaps_in_frame_num_value_allowed_flag %d\n",
gaps_in_frame_num_value_allowed_flag);
err = read_exp_golomb(bd,&pic_width_in_mbs); // minus 1
CHECK("pic_width_in_mbs");
pic_width_in_mbs ++;
if (show_nal_details)
fprint_msg(" pic_width_in_mbs %u\n",pic_width_in_mbs);
err = read_exp_golomb(bd,&pic_height_in_map_units); // minus 1
CHECK("pic_height_in_map_units");
pic_height_in_map_units ++;
if (show_nal_details)
fprint_msg(" pic_height_in_map_units %u\n",pic_height_in_map_units);
// We care about frame_mbs_only_flag
err = read_bit(bd,&data->frame_mbs_only_flag);
CHECK("frame_mbs_only_flag");
if (show_nal_details)
fprint_msg(" frame_mbs_only_flag %d\n",data->frame_mbs_only_flag);
nal->decoded = TRUE;
return 0;
}
/*
* Read the data for an SEI recovery point
*
* Returns 0 if it succeeds, 1 if some error occurs
*/
static int read_SEI_recovery_point(nal_unit_p nal,
int payloadSize,
int show_nal_details)
{
int err;
bitdata_p bd = nal->bit_data;
nal_SEI_recovery_data_p data = &(nal->u.sei_recovery);
uint32_t temp;
#undef CHECK
#define CHECK(name) \
if (err) \
{ \
fprint_err("### Error reading %s field from SEI\n", \
(name)); \
}
err = read_exp_golomb(bd,&temp);
CHECK("recovery_frame_cnt");
data->recovery_frame_cnt = temp;
err = read_bit(bd,&data->exact_match_flag);
CHECK("exact_match_flag");
err = read_bit(bd,&data->broken_link_flag);
CHECK("broken_link_flag");
err = read_bits(bd,2,&data->changing_slice_group_idc);
CHECK("changing_slice_group_idc");
nal->decoded = TRUE;
if (show_nal_details)
{
print_msg("@@ Recovery Point SEI\n");
fprint_msg(" recovery_frame_cnt %d\n exact_match_flag %d\n", data->recovery_frame_cnt, data->exact_match_flag);
fprint_msg(" broken_link_flag %d\n changing_slice_group_idc %d", data->broken_link_flag, data->changing_slice_group_idc);
}
return 0;
}
/*
* Look at the start of an SEI
*
* Assumes that this *is* a NAL unit representing an SEI
*
* Don't call this directly - call read_rbsp_data() instead.
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
static int read_SEI(nal_unit_p nal,
int show_nal_details)
{
int err;
int SEI_payloadType = 0;
int SEI_payloadSize = 0; // in byte
bitdata_p bd = nal->bit_data;
uint32_t temp = 0;
#undef CHECK
#define CHECK(name) \
if (err) \
{ \
fprint_err("### Error reading %s field from SEI\n", \
(name)); \
}
// read payloadtype (see H.264:7.3.2.3.1)
for (;;)
{
err = read_bits(bd,8,&temp);
CHECK("payloadType");
if (temp == 0xff)
SEI_payloadType += 0xff;
else
break;
}
SEI_payloadType += temp;
nal->u.sei_recovery.payloadType = SEI_payloadType;
// read payloadSize
for (;;)
{
err = read_bits(bd,8,&temp);
CHECK("payloadSize");
if (temp == 0xff)
SEI_payloadSize += 0xff;
else
break;
}
SEI_payloadSize += temp;
nal->u.sei_recovery.payloadSize = SEI_payloadSize;
if (SEI_payloadType == 6) // SEI recovery_point
err = read_SEI_recovery_point(nal, SEI_payloadSize, show_nal_details);
return 0;
}
/*
* Look at the start of the RBSP for a NAL unit
*
* Decodes some or all of the data for slices, sequence parameter sets
* and picture parameter sets.
*
* (Note that calling this more than once does not read the data
* more than once. Also note that the RBSP and bitdata datastructures
* in the NAL unit do not persist after this call.)
*
* Caveat: if either `seq_param_dict` or `pic_param_dict` is NULL, and the
* NAL unit being interpreted is an IDR or non-IDR unit (i.e., a slice),
* then only the first few values in the RBSP will be read (up to and
* including the slice_type and pic_parameter_set_id), and the RBSP will
* *not* be marked as decoded. Because of this, calling this
* function again later will cause the RBSP to be read again (strictly,
* to be re-extracted and read again).
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
static int read_rbsp_data(nal_unit_p nal,
param_dict_p seq_param_dict,
param_dict_p pic_param_dict,
int show_nal_details)
{
int err = 0;
if (nal->decoded)
return 0;
err = prepare_rbsp(nal);
if (err) return err;
if (nal->nal_unit_type == 1 || nal->nal_unit_type == 5) // Coded slice of a (non) IDR picture
err = read_slice_data(nal,seq_param_dict,pic_param_dict,show_nal_details);
else if (nal->nal_unit_type == 8) // Picture parameter set
err = read_pic_param_set_data(nal,show_nal_details);
else if (nal->nal_unit_type == 7) // Sequence parameter set
err = read_seq_param_set_data(nal,show_nal_details);
else if (nal->nal_unit_type == 6) // SEI
err = read_SEI(nal,show_nal_details);
else if (show_nal_details)
fprint_msg("@@ nal " OFFSET_T_FORMAT_08 "/%04d: size %d\n"
" nal_ref_idc %x nal_unit_type %02x (%s)\n",
nal->unit.start_posn.infile,nal->unit.start_posn.inpacket,
nal->data_len,
nal->nal_ref_idc,nal->nal_unit_type,
NAL_UNIT_TYPE_STR(nal->nal_unit_type));
if (err)
{
fprint_err("### Error reading RBSP data for %s NAL (ref idc %x,"
" unit type %x) at " OFFSET_T_FORMAT_08 "/%04d\n",
NAL_UNIT_TYPE_STR(nal->nal_unit_type),
nal->nal_ref_idc,
nal->nal_unit_type,
nal->unit.start_posn.infile,
nal->unit.start_posn.inpacket);
}
// At this point, we've finished with the actual RBSP data
// so we might as well free it and save some space.
if (nal->rbsp != NULL)
{
free(nal->rbsp);
nal->rbsp = NULL;
nal->rbsp_len = 0;
free_bitdata(&nal->bit_data);
}
return err;
}
/*
* Is this NAL unit a slice?
*
* Returns true if its ``nal_unit_type`` is 1 (coded slice of IDR picture)
* or 5 (coded slice of IDR picture).
*/
extern int nal_is_slice(nal_unit_p nal)
{
return nal->nal_unit_type == 1 || nal->nal_unit_type == 5;
}
/*
* Is this NAL unit a picture parameter set?
*
* Returns true if its ``nal_unit_type`` is 8.
*/
extern int nal_is_pic_param_set(nal_unit_p nal)
{
return nal->nal_unit_type == 8;
}
/*
* Is this NAL unit a sequence parameter set?
*
* Returns true if its ``nal_unit_type`` is 7.
*/
extern int nal_is_seq_param_set(nal_unit_p nal)
{
return nal->nal_unit_type == 7;
}
/*
* Is this NAL unit marked as part of a redundant picture?
*/
extern int nal_is_redundant(nal_unit_p nal)
{
return nal_is_slice(nal) && nal->u.slice.redundant_pic_cnt_present &&
nal->u.slice.redundant_pic_cnt;
}
/*
* Is this VCL NAL unit the first of a new primary coded picture?
*
* - `nal` is the NAL unit we need to decide about.
* - `last` is a slice NAL unit from the last primary coded picture
* (likely to be the first NAL unit therefrom, in fact)
*
* Both `nal` and `last` must be VCL NALs representing slices of a reference
* picture - i.e., with nal_unit_type 1 or 5 (if we were supporting type A
* slice data partitions, we would have to take them into account as well).
*
* Both `nal` and `last` must have had their innards decoded with
* `read_slice_data`, which should have occurred automatically if they are
* both appropriate NAL units for this process.
*
* Acording to H.264 7.4.1.2.4 (from the JVT-J010d7 draft):
*
* The first NAL unit of a new primary code picture can be detected
* because:
*
* - its frame number differs in value from that of the last slice (NB:
* IDR pictures always have frame_num == 0)
*
* - its field_pic_flag differs in value (i.e., one is a field slice, and
* the other a frame slice)
*
* - the bottom_field_flag is present in both (determined by
* frame_mbs_only_flag in the sequence parameter set, and by
* field_pic_flag) and differs (i.e., both are field slices, but one
* is top and the other bottom) [*]_
*
* - nal_ref_idc differs in value, and one of them has nal_ref_idc == 0
* (i.e., one is a reference picture and the other is not)
*
* - pic_order_cnt_type (found in the sequence parameter set) == 0 for
* both and either pic_order_cnt_lsb differs in value or
* delta_pic_order_cnt_bottom differs in value [*]_
*
* - pic_order_cnt_type == 1 for both and either delta_pic_order_cnt[0]
* or delta_pic_order_cnt[1] differs in value [*]_
*
* - nal_unit_type == 5 for one and not in the other (i.e., one is IDR
* and the other is not)
*
* - nal_unit_type == 5 for both (i.e., both are IDR), and idr_pic_id
* differs (i.e., they're not the same IDR)
*
* It is possible that later drafts may alter/augment these criteria -
* that has already happened between JVT-G050r1 and JVT-J010d7.
*
* .. [*] For these three items, we need to have decoded the active
* sequence parameter set (which, for now, I'll assume to be the last
* set we found with the appropriate id).
*/
extern int nal_is_first_VCL_NAL(nal_unit_p nal,
nal_unit_p last)
{
nal_slice_data_p this,that;
if (nal->starts_picture_decided)
return nal->starts_picture;
if (!nal->decoded)
{
print_err("### Cannot decide if NAL unit is first VCL NAL\n"
" its RBSP data has not been interpreted\n");
return FALSE;
}
// Since we intend to transmit all sequence and picture parameter
// sets, at least initially, we don't need to worry if they are
// "inside" a picture, at its start, or whatever.
// Since we're not supporting data partition slices A,B,C, we can
// ignore them as well (at least, so I hope)
if (nal->nal_unit_type != NAL_NON_IDR &&
nal->nal_unit_type != NAL_IDR)
{
nal->starts_picture = FALSE;
nal->starts_picture_decided = TRUE;
return FALSE;
}
nal->starts_picture = TRUE; // let's be optimistic...
nal->starts_picture_decided = TRUE;
if (last == NULL)
{
// With nothing else to compare to, we shall assume that we do
// "start" a picture
nal->start_reason = "First slice in data stream";
return TRUE;
}
this = &(nal->u.slice);
that = &(last->u.slice);
if (this->frame_num != that->frame_num)
nal->start_reason = "Frame number differs";
else if (this->field_pic_flag != that->field_pic_flag)
nal->start_reason = "One is field, the other frame";
else if (this->bottom_field_flag_present &&
that->bottom_field_flag_present &&
this->bottom_field_flag != that->bottom_field_flag)
{
nal->start_reason = "One is bottom field, the other top";
// In which case, we'll need to remember to output the OTHER
// half of the frame when we find it...
}
else if (nal->nal_ref_idc != last->nal_ref_idc &&
(nal->nal_ref_idc == 0 || last->nal_ref_idc == 0))
nal->start_reason = "One is reference picture, the other is not";
else if (this->seq_param_set_pic_order_cnt_type == 0 &&
that->seq_param_set_pic_order_cnt_type == 0 &&
(this->pic_order_cnt_lsb != that->pic_order_cnt_lsb ||
this->delta_pic_order_cnt_bottom !=
that->delta_pic_order_cnt_bottom))
nal->start_reason = "Picture order counts differ";
else if (this->seq_param_set_pic_order_cnt_type == 1 &&
that->seq_param_set_pic_order_cnt_type == 1 &&
(this->delta_pic_order_cnt[0] != that->delta_pic_order_cnt[0] ||
this->delta_pic_order_cnt[1] != that->delta_pic_order_cnt[1]))
nal->start_reason = "Picture delta counts differ";
else if ((nal->nal_unit_type == 5 || last->nal_unit_type == 5) &&
nal->nal_unit_type != last->nal_unit_type)
nal->start_reason = "One IDR, one not";
else if (nal->nal_unit_type == 5 && last->nal_unit_type == 5 &&
this->idr_pic_id != that->idr_pic_id)
nal->start_reason = "Different IDRs";
else
nal->starts_picture = FALSE;
return nal->starts_picture;
}
/*
* Print out useful information about this NAL unit, on the given stream.
*
* This is intended as a single line of information.
*/
extern void report_nal(int is_msg,
nal_unit_p nal)
{
if (nal == NULL)
fprint_msg_or_err(is_msg,".............: NAL unit <null>\n");
else if (nal_is_slice(nal) && (nal->nal_unit_type == NAL_IDR ||
nal->nal_unit_type == NAL_NON_IDR))
{
#define SARRAYSIZE 20
char what[SARRAYSIZE];
snprintf(what,SARRAYSIZE,"(%s)",NAL_UNIT_TYPE_STR(nal->nal_unit_type));
// On Windows, snprintf does not guarantee to write a terminating NULL
what[SARRAYSIZE-1] = '\0';
fprint_msg_or_err(is_msg,OFFSET_T_FORMAT_08 "/%04d: %x/%02x %-20s %u (%s) frame %u",
nal->unit.start_posn.infile,
nal->unit.start_posn.inpacket,
nal->nal_ref_idc,
nal->nal_unit_type,
what,
nal->u.slice.slice_type,
NAL_SLICE_TYPE_STR(nal->u.slice.slice_type),
nal->u.slice.frame_num);
if (nal->u.slice.field_pic_flag)
{
if (nal->u.slice.bottom_field_flag)
fprint_msg_or_err(is_msg," [bottom]");
else
fprint_msg_or_err(is_msg," [top]");
}
}
else if (nal_is_seq_param_set(nal))
{
fprint_msg_or_err(is_msg,OFFSET_T_FORMAT_08 "/%04d: %x/%02x (%s %u)",
nal->unit.start_posn.infile,
nal->unit.start_posn.inpacket,
nal->nal_ref_idc,
nal->nal_unit_type,
NAL_UNIT_TYPE_STR(nal->nal_unit_type),
nal->u.seq.seq_parameter_set_id);
}
else if (nal_is_pic_param_set(nal))
{
fprint_msg_or_err(is_msg,OFFSET_T_FORMAT_08 "/%04d: %x/%02x (%s %u)",
nal->unit.start_posn.infile,
nal->unit.start_posn.inpacket,
nal->nal_ref_idc,
nal->nal_unit_type,
NAL_UNIT_TYPE_STR(nal->nal_unit_type),
nal->u.pic.pic_parameter_set_id);
}
else
fprint_msg_or_err(is_msg,OFFSET_T_FORMAT_08 "/%04d: %x/%02x (%s)",
nal->unit.start_posn.infile,
nal->unit.start_posn.inpacket,
nal->nal_ref_idc,
nal->nal_unit_type,
NAL_UNIT_TYPE_STR(nal->nal_unit_type));
#if REPORT_NAL_SHOWS_ADDRESS
fprint_msg_or_err(is_msg," <%p>",nal);
#endif
fprint_msg_or_err(is_msg,"\n");
}
// ------------------------------------------------------------
// Check profile
// ------------------------------------------------------------
/*
* Issue a warning if the profile of this bitstream is "unsuitable".
*
* "suitable" bitstream declares itself as either conforming to
* the main profile, or as obeying the constraints of the main profile.
*
* This function should be called on the first sequence parameter set
* NAL unit in the bitstream, after its innards have been decoded.
*/
static void check_profile(nal_unit_p nal,
int show_nal_details)
{
struct nal_seq_param_data data;
char *name;
if (nal == NULL)
{
print_err("### Attempt to check profile on a NULL NAL unit\n");
return;
}
else if (nal->nal_unit_type != 7)
{
print_err("### Attempt to check profile on a NAL unit that is not a "
"sequence parameter set\n");
report_nal(FALSE,nal);
return;
}
else if (!nal->decoded)
{
// Note that we believe ourselves safe in passing NULLs for the
// parameter NAL units, since we are reading a sequence parameter set,
// which does not depend on anything else
int err = read_rbsp_data(nal,NULL,NULL,show_nal_details);
if (err)
{
print_err("### Error trying to decode RBSP for first sequence"
" parameter set\n");
return;
}
}
data = nal->u.seq;
name = (data.profile_idc==66?"baseline":
data.profile_idc==77?"main":
data.profile_idc==88?"extended":"<unknown>");
if (data.profile_idc == 77 || data.constraint_set1_flag == 1)
return;
else
{
int sum = data.constraint_set0_flag + data.constraint_set1_flag +
data.constraint_set2_flag;
print_err("\n");
fprint_err("Warning: This bitstream declares itself as %s profile (%d)",
name,data.profile_idc);
if (sum == 0)
print_err(".\n");
else
{
print_err(",\n");
print_err(" and as obeying the constraints of the");
if (data.constraint_set0_flag) print_err(" baseline");
if (data.constraint_set1_flag) print_err(" main");
if (data.constraint_set2_flag) print_err(" extended");
fprint_err(" profile%s.\n",(sum==1?"":"s"));
}
fprint_err(" This software does not support %s profile,\n",
name);
print_err(" and may give incorrect results or fail.\n\n");
return;
}
}
// ------------------------------------------------------------
// NAL unit *data* stuff
// ------------------------------------------------------------
/*
* Once we've read the *data* for a NAL unit, we can set up a bit more
* of the datastructure.
*
* - `verbose` is true if a brief report on the NAL unit should be given
* - `nal` is the NAL unit itself.
*
* CAVEAT: This function is declared external so that I can use it
* in `stream_type.c`, but it is not exported to the nalunit_defns.h file
* because I cannot see any sensible outside use for it...
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int setup_NAL_data(int verbose,
nal_unit_p nal)
{
int forbidden_zero_bit;
// Although we've read "all" the data (i.e., including the prefix),
// for most purposes of working on it it's more convenient to act
// as if those bytes aren't there...
nal->data = &(nal->unit.data[3]);
nal->data_len = nal->unit.data_len - 3;
// The first byte of our data tells us what sort of NAL unit it is
forbidden_zero_bit = nal->data[0] & 0x80;
if (forbidden_zero_bit)
{
fprint_err("### NAL forbidden_zero_bit is non-zero, at "
OFFSET_T_FORMAT "/%d\n",
nal->unit.start_posn.infile,nal->unit.start_posn.inpacket);
fprint_err(" First byte of NAL unit is %02x",nal->data[0]);
if (nal->data[0] == 0xB3)
print_err(", which is H.262 sequence header start code\n"
" Data may be MPEG-1 or MPEG-2");
print_err("\n");
return 1;
}
nal->nal_ref_idc = (nal->data[0] & 0x60) >> 5;
nal->nal_unit_type = (nal->data[0] & 0x1F);
if (verbose)
{
#define SARRAYSIZE2 20
char what[SARRAYSIZE2];
snprintf(what,SARRAYSIZE2,"(%s)",NAL_UNIT_TYPE_STR(nal->nal_unit_type));
// On Windows, snprintf does not guarantee to write a terminating NULL
what[SARRAYSIZE2-1] = '\0';
fprint_msg(OFFSET_T_FORMAT_08 "/%04d: NAL unit %d/%d %-20s",
nal->unit.start_posn.infile,
nal->unit.start_posn.inpacket,
nal->nal_ref_idc,nal->nal_unit_type,what);
// Show the start of the data bytes. This is a tailored form of what
// `print_data` would do, more suited to our purposes here (i.e.,
// wanting multiple rows of output to line up neatly in columns).
if (nal->data_len > 0)
{
int ii;
int show_len = (nal->data_len>10?10:nal->data_len);
fprint_msg(" %6d:",nal->data_len);
for (ii = 0; ii < show_len; ii++)
fprint_msg(" %02x",nal->data[ii]);
if (show_len < nal->data_len)
print_msg("...");
}
print_msg("\n");
}
return 0;
}
/*
* Find and read in the next NAL unit.
*
* - `context` is the NAL unit context we're reading from
* - `verbose` is true if a brief report on the NAL unit should be given
* - `nal` is the datastructure containing the NAL unit 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 NAL unit),
* * 2 if the NAL unit data does not make sense, so it should be ignored
* (specifically, if the NAL unit's RBSP data cannot be understood),
* * 1 if some other error occurs.
*/
extern int find_next_NAL_unit(nal_unit_context_p context,
int verbose,
nal_unit_p *nal)
{
static int need_first_seq_param_set = TRUE;
int err;
err = build_nal_unit(nal);
if (err) return 1;
err = find_next_ES_unit(context->es,&(*nal)->unit);
if (err) // 1 or EOF
{
free_nal_unit(nal);
return err;
}
(context->count) ++;
if (context->show_nal_details)
print_msg("\n");
err = setup_NAL_data(verbose,*nal);
if (err)
{
free_nal_unit(nal);
return err;
}
// By looking at the first sequence parameter set, we can
// decide whether the data matches what we claim to support.
// That also serves to bootstrap the decoding of other items
if (nal_is_seq_param_set(*nal))
{
if (need_first_seq_param_set)
{
check_profile(*nal,context->show_nal_details);
need_first_seq_param_set = FALSE;
}
}
// Once we know we've got the first sequence parameter set in hand
// (which we *assume* and hope is the first thing we find!), we can
// decode the innards of later things.
err = read_rbsp_data(*nal,context->seq_param_dict,context->pic_param_dict,
context->show_nal_details);
if (err)
{
free_nal_unit(nal);
return 2;
}
// If this is a picture parameter set, or a sequence parameter set,
// we should remember it for later on
if (nal_is_pic_param_set(*nal))
{
err = remember_param_data(context->pic_param_dict,
(*nal)->u.pic.pic_parameter_set_id,*nal);
if (err)
{
print_err("### Error remembering picture parameter set ");
report_nal(FALSE,*nal);
free_nal_unit(nal);
return 1;
}
}
else if (nal_is_seq_param_set(*nal))
{
err = remember_param_data(context->seq_param_dict,
(*nal)->u.seq.seq_parameter_set_id,*nal);
if (err)
{
print_err("### Error remembering sequence parameter set ");
report_nal(FALSE,*nal);
free_nal_unit(nal);
return 1;
}
}
return 0;
}
/*
* Write (copy) the current NAL unit to the ES output stream.
*
* - `output` is the output stream (file descriptor) to write to
* - `nal` is the NAL unit to write
*
* Returns 0 if all went well, 1 if something went wrong.
*/
extern int write_NAL_unit_as_ES(FILE *output,
nal_unit_p nal)
{
int err = write_ES_unit(output,&(nal->unit));
if (err)
{
print_err("### Error writing NAL unit as ES\n");
return err;
}
else
return 0;
}
/*
* Write (copy) the current NAL unit to the output stream, wrapped up in a
* PES within TS.
*
* - `output` is the TS writer to write to
* - `nal` is the NAL unit to write
* - `video_pid` is the video PID to use
*
* Returns 0 if all went well, 1 if something went wrong.
*/
extern int write_NAL_unit_as_TS(TS_writer_p tswriter,
nal_unit_p nal,
uint32_t video_pid)
{
// Note that we write out *all* of the data for this NAL unit,
// i.e., including its 00 00 01 prefix. Also note that we write
// out the data with its emulation prevention 03 bytes intact.
int err = write_ES_as_TS_PES_packet(tswriter,
nal->unit.data,nal->unit.data_len,
video_pid,DEFAULT_VIDEO_STREAM_ID);
if (err)
{
print_err("### Error writing NAL unit as TS\n");
return err;
}
else
return 0;
}
// ------------------------------------------------------------
// Picture and sequence parameter set support
// ------------------------------------------------------------
/*
* Create a new "dictionary" for remembering picture or sequence
* parameter sets.
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int build_param_dict(param_dict_p *param_dict)
{
param_dict_p new = malloc(SIZEOF_PARAM_DICT);
if (new == NULL)
{
print_err("### Unable to allocate parameter 'dictionary' datastructure\n");
return 1;
}
new->last_id = -1;
new->last_index = -1;
new->ids = malloc(sizeof(uint32_t)*NAL_PIC_PARAM_START_SIZE);
if (new->ids == NULL)
{
print_err("### Unable to allocate array within 'dictionary'"
" datastructure\n");
free(new);
return 1;
}
new->params = malloc(SIZEOF_NAL_INNARDS*NAL_PIC_PARAM_START_SIZE);
if (new->params == NULL)
{
print_err("### Unable to allocate array within 'dictionary'"
" datastructure\n");
free(new->ids);
free(new);
return 1;
}
new->posns = malloc(SIZEOF_ES_OFFSET*NAL_PIC_PARAM_START_SIZE);
if (new->posns == NULL)
{
print_err("### Unable to allocate array within 'dictionary'"
" datastructure\n");
free(new->params);
free(new->ids);
free(new);
return 1;
}
new->data_lens = malloc(sizeof(uint32_t)*NAL_PIC_PARAM_START_SIZE);
if (new->data_lens == NULL)
{
print_err("### Unable to allocate array within 'dictionary'"
" datastructure\n");
free(new->params);
free(new->ids);
free(new);
return 1;
}
new->size = NAL_PIC_PARAM_START_SIZE;
new->length = 0;
*param_dict = new;
return 0;
}
/*
* Tidy up and free a parameters "dictionary" datastructure after we've
* finished with it.
*
* Empties the datastructure, frees it, and sets `param_dict` to NULL.
*
* Does nothing if `param_dict` is already NULL.
*/
extern void free_param_dict(param_dict_p *param_dict)
{
if (*param_dict == NULL)
return;
free((*param_dict)->ids);
free((*param_dict)->params);
free((*param_dict)->posns);
free((*param_dict)->data_lens);
(*param_dict)->ids = NULL;
(*param_dict)->params = NULL;
(*param_dict)->posns = NULL;
(*param_dict)->data_lens = NULL;
free(*param_dict);
*param_dict = NULL;
}
/*
* Remember parameter set data in a "dictionary".
*
* - `param_dict` should be an appropriate "dictionary" - i.e., one
* being used to store picture or sequence parameter set data, as
* appropriate.
* - `param_id` is the id for this picture or sequence parameter set.
* - `nal` is the NAL unit containing the parameter set data.
* Note that a copy will be taken of the parameter set data, which
* means that the caller may free the NAL unit.
*
* Any previous data for this picture or sequence parameter set id will be
* forgotten (overwritten).
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int remember_param_data(param_dict_p param_dict,
uint32_t param_id,
nal_unit_p nal)
{
int ii;
if (param_id == param_dict->last_id)
{
param_dict->params[param_dict->last_index] = nal->u;
return 0;
}
for (ii=0; ii<param_dict->length; ii++)
{
if (param_dict->ids[ii] == param_id)
{
param_dict->params[ii] = nal->u;
param_dict->posns[ii] = nal->unit.start_posn;
param_dict->data_lens[ii] = nal->unit.data_len;
param_dict->last_id = param_id;
param_dict->last_index = ii;
return 0;
}
}
if (param_dict->length == param_dict->size)
{
int newsize = param_dict->size + NAL_PIC_PARAM_INCREMENT;
param_dict->ids = realloc(param_dict->ids,newsize*sizeof(uint32_t));
if (param_dict->ids == NULL)
{
print_err("### Unable to extend parameter set dictionary array\n");
return 1;
}
param_dict->params = realloc(param_dict->params,
newsize*SIZEOF_NAL_INNARDS);
if (param_dict->params == NULL)
{
print_err("### Unable to extend parameter set dictionary array\n");
return 1;
}
param_dict->posns = realloc(param_dict->params,newsize*SIZEOF_ES_OFFSET);
if (param_dict->posns == NULL)
{
print_err("### Unable to extend parameter set dictionary array\n");
return 1;
}
param_dict->data_lens = realloc(param_dict->params,
newsize*sizeof(uint32_t));
if (param_dict->data_lens == NULL)
{
print_err("### Unable to extend parameter set dictionary array\n");
return 1;
}
param_dict->size = newsize;
}
param_dict->ids[param_dict->length] = param_id;
param_dict->params[param_dict->length] = nal->u;
param_dict->posns[param_dict->length] = nal->unit.start_posn;
param_dict->data_lens[param_dict->length] = nal->unit.data_len;
param_dict->last_id = param_id;
param_dict->last_index = param_dict->length;
param_dict->length++;
return 0;
}
/*
* Look up a parameter set id in the "dictionary".
*
* - `param_dict` is a parameter "dictionary" of the appropriate type.
* - `param_id` is the id to look up.
* - `param_data` is the data for that id. Do not free this, it refers
* into the "dictionary" datastructure.
*
* Note that calls of `remember_param_data()` (i.e., altering the
* "dictionary") may cause the underlying datastructures to be realloc'ed,
* which in turn means that the address returned as `param_data` may not be
* valid after such a call.
*
* Returns 0 if it succeeds, 1 if the id is not present.
*/
static inline int lookup_param_data(param_dict_p param_dict,
uint32_t param_id,
nal_innards_p *param_data)
{
int ii;
for (ii=0; ii<param_dict->length; ii++)
{
if (param_dict->ids[ii] == param_id)
{
*param_data = &param_dict->params[ii];
param_dict->last_id = param_id;
param_dict->last_index = ii;
return 0;
}
}
return 1;
}
/*
* Retrieve the picture parameter set data for the given id.
*
* - `pic_param_dict` is a parameter "dictionary" of the appropriate type.
* - `pic_param_id` is the id to look up.
* - `pic_param_data` is the data for that id. Do not free this, it refers
* into the "dictionary" datastructure.
*
* Note that altering the "dictionary" (with `remember_param_data()`) may
* cause the underlying datastructures to be realloc'ed, which in turn means
* that the address returned as `pic_param_data` may not be valid after such
* an action.
*
* Returns 0 if it succeeds, 1 if the id is not recognised.
*/
extern int get_pic_param_data(param_dict_p pic_param_dict,
uint32_t pic_param_id,
nal_pic_param_data_p *pic_param_data)
{
nal_innards_p innards;
int absent = lookup_param_data(pic_param_dict,pic_param_id,&innards);
if (absent)
{
fprint_err("### Unable to find picture parameter set with id %u\n",
pic_param_id);
return 1;
}
*pic_param_data = &(innards->pic);
return 0;
}
/*
* Retrieve the sequence parameter set data for the given id.
*
* - `seq_param_dict` is a parameter "dictionary" of the appropriate type.
* - `seq_param_id` is the id to look up.
* - `seq_param_data` is the data for that id. Do not free this, it refers
* into the "dictionary" datastructure.
*
* Note that altering the "dictionary" (with `remember_param_data()`) may
* cause the underlying datastructures to be realloc'ed, which in turn means
* that the address returned as `seq_param_data` may not be valid after such
* an action.
*
* Returns 0 if it succeeds, 1 if the id is not recognised.
*/
extern int get_seq_param_data(param_dict_p seq_param_dict,
uint32_t seq_param_id,
nal_seq_param_data_p *seq_param_data)
{
nal_innards_p innards;
int absent = lookup_param_data(seq_param_dict,seq_param_id,&innards);
if (absent)
{
fprint_err("### Unable to find sequence parameter set with id %u\n",
seq_param_id);
return 1;
}
*seq_param_data = &(innards->seq);
return 0;
}
// ------------------------------------------------------------
// Lists of NAL units
//
// This duplicates the functionality provided by ES unit lists
// in es.c/h, but it works at the higher level of NAL units,
// which is useful if one wants to report on the content of the
// lists *as* NAL units.
// ------------------------------------------------------------
/*
* Build a new list-of-nal-units datastructure.
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int build_nal_unit_list(nal_unit_list_p *list)
{
nal_unit_list_p new = malloc(SIZEOF_NAL_UNIT_LIST);
if (new == NULL)
{
print_err("### Unable to allocate NAL unit list datastructure\n");
return 1;
}
new->length = 0;
new->size = NAL_UNIT_LIST_START_SIZE;
new->array = malloc(sizeof(nal_unit_p)*NAL_UNIT_LIST_START_SIZE);
if (new->array == NULL)
{
free(new);
print_err("### Unable to allocate array in NAL unit list datastructure\n");
return 1;
}
*list = new;
return 0;
}
/*
* Add a NAL unit to the end of the NAL unit list. Does not take a copy.
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int append_to_nal_unit_list(nal_unit_list_p list,
nal_unit_p nal)
{
if (list->length == list->size)
{
int newsize = list->size + NAL_UNIT_LIST_INCREMENT;
list->array = realloc(list->array,newsize*sizeof(nal_unit_p));
if (list->array == NULL)
{
print_err("### Unable to extend NAL unit list array\n");
return 1;
}
list->size = newsize;
}
list->array[list->length++] = nal;
return 0;
}
/*
* Tidy up a NAL unit list datastructure after we've finished with it.
*
* If `deep` is true, then any NAL units in the list will be freed
* as well (this will be a Bad Thing if anywhere else is using them).
*/
static inline void clear_nal_unit_list(nal_unit_list_p list,
int deep)
{
if (list->array != NULL)
{
int ii;
for (ii=0; ii<list->length; ii++)
{
if (deep)
{
nal_unit_p nal = list->array[ii];
if (nal != NULL)
{
clear_nal_unit(nal);
free(nal);
}
}
list->array[ii] = NULL;
}
free(list->array);
list->array = NULL;
}
list->length = 0;
list->size = 0;
}
/*
* Reset (empty) a NAL unit list.
*
* If `deep` is true, then any NAL units in the list will be freed
* as well (this will be a Bad Thing if anywhere else is using them).
*/
extern void reset_nal_unit_list(nal_unit_list_p list,
int deep)
{
if (list->array != NULL)
{
int ii;
for (ii=0; ii<list->length; ii++)
{
if (deep)
{
nal_unit_p nal = list->array[ii];
if (nal != NULL)
{
clear_nal_unit(nal);
free(nal);
}
}
list->array[ii] = NULL;
}
// We *could* also shrink it - as it is, it will never get smaller
// than its maximum size. Is that likely to be a problem?
}
list->length = 0;
}
/*
* Tidy up and free a NAL unit list datastructure after we've finished with it.
*
* Clears the datastructure, frees it and returns `list` as NULL.
*
* If `deep` is true, then any NAL units in the list will be freed
* as well (this will be a Bad Thing if anywhere else is using them).
*
* Does nothing if `list` is already NULL.
*/
extern void free_nal_unit_list(nal_unit_list_p *list,
int deep)
{
if (*list == NULL)
return;
clear_nal_unit_list(*list,deep);
free(*list);
*list = NULL;
}
/*
* Report on a NAL unit list's contents, to the given stream.
*/
extern void report_nal_unit_list(int is_msg,
char *prefix,
nal_unit_list_p list)
{
if (prefix == NULL)
prefix = "";
if (list->array == NULL)
fprint_msg_or_err(is_msg,"%s<empty>\n",prefix);
else
{
int ii;
for (ii=0; ii<list->length; ii++)
{
fprint_msg_or_err(is_msg,"%s",prefix);
report_nal(is_msg,list->array[ii]);
}
}
}
// Local Variables:
// tab-width: 8
// indent-tabs-mode: nil
// c-basic-offset: 2
// End:
// vim: set tabstop=8 shiftwidth=2 expandtab: