/* * 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 #include #include #include #include #ifdef _WIN32 #include #else // _WIN32 #include #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; iibit_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 \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":""); 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; iilength; 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; iilength; ii++) { if (param_dict->ids[ii] == param_id) { *param_data = ¶m_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; iilength; 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; iilength; 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\n",prefix); else { int ii; for (ii=0; iilength; 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: