Teamol
Teamol

Reputation: 819

H264 getting frame height and width from sequence parameter set (SPS) NAL unit

I've been trying to find out how to calculate width a height from SPS nal unit. I have H264 video which has these parameters

h264 (High), yuvj420p(pc), 1280x720 [SAR 1:1 DAR 16:9], 20 fps, 20 tbr, 1200k tbn, 40 tbc

I've been searching for a formula which would calculate width (1280) and height (720) but haven't found any which would help me. Right now I'm using this formula and it works for most H264 streams but in this case height and width is 80x48

if(frame_cropping_flag) {
    width = ((pic_width_in_mbs_minus1 +1)*16) - frame_crop_left_offset*2 - frame_crop_right_offset*2;
    height= ((2 - frame_mbs_only_flag)* (pic_height_in_map_units_minus1 +1) * 16) - (frame_crop_top_offset * 2) - (frame_crop_bottom_offset * 2);
}
else {
    width = ((pic_width_in_mbs_minus1 +1)*16);
    height= ((2 - frame_mbs_only_flag)* (pic_height_in_map_units_minus1 +1) * 16);
}

here is SPS as base64

Z2QAKa2EBUViuKxUdCAqKxXFYqOhAVFYrisVHQgKisVxWKjoQFRWK4rFR0ICorFcVio6ECSFITk8nyfk/k/J8nm5s00IEkKQnJ5Pk/J/J+T5PNzZprQCgC3YCqQAAAMB4AAASwGBAAH0AAADAjKAve+F4RCNQA==

here is SPS that I've parsed:

======= SPS =======
 profile_idc : 100
 constraint_set0_flag : 0
 constraint_set1_flag : 0
 constraint_set2_flag : 0
 constraint_set3_flag : 0
 constraint_set4_flag : 0
 constraint_set5_flag : 0
 reserved_zero_2bits : 0
 level_idc : 41
 seq_parameter_set_id : 0
 chroma_format_idc : 1
 separate_colour_plane_flag : 0
 bit_depth_luma_minus8 : 0
 bit_depth_chroma_minus8 : 0
 qpprime_y_zero_transform_bypass_flag : 0
 seq_scaling_matrix_present_flag : 1
 log2_max_frame_num_minus4 : 41
 pic_order_cnt_type : 4
   log2_max_pic_order_cnt_lsb_minus4 : 0
   delta_pic_order_always_zero_flag : 0
   offset_for_non_ref_pic : 0
   offset_for_top_to_bottom_field : 0
   num_ref_frames_in_pic_order_cnt_cycle : 0
 num_ref_frames : 2
 gaps_in_frame_num_value_allowed_flag : 0
 pic_width_in_mbs_minus1 : 4
 pic_height_in_map_units_minus1 : 2
 frame_mbs_only_flag : 1
 mb_adaptive_frame_field_flag : 0
 direct_8x8_inference_flag : 0
 frame_cropping_flag : 0
   frame_crop_left_offset : 0
   frame_crop_right_offset : 0
   frame_crop_top_offset : 0
   frame_crop_bottom_offset : 0
 vui_parameters_present_flag : 0
=== VUI ===
 aspect_ratio_info_present_flag : 0
   aspect_ratio_idc : 0
     sar_width : 0
     sar_height : 0
 overscan_info_present_flag : 0
   overscan_appropriate_flag : 0
 video_signal_type_present_flag : 0
   video_format : 0
   video_full_range_flag : 0
   colour_description_present_flag : 0
     colour_primaries : 0
   transfer_characteristics : 0
   matrix_coefficients : 0
 chroma_loc_info_present_flag : 0
   chroma_sample_loc_type_top_field : 0
   chroma_sample_loc_type_bottom_field : 0
 timing_info_present_flag : 0
   num_units_in_tick : 0
   time_scale : 0
   fixed_frame_rate_flag : 0
 nal_hrd_parameters_present_flag : 0
 vcl_hrd_parameters_present_flag : 0
   low_delay_hrd_flag : 0
 pic_struct_present_flag : 0
 bitstream_restriction_flag : 0
   motion_vectors_over_pic_boundaries_flag : 0
   max_bytes_per_pic_denom : 0
   max_bits_per_mb_denom : 0
   log2_max_mv_length_horizontal : 0
   log2_max_mv_length_vertical : 0
   num_reorder_frames : 0
   max_dec_frame_buffering : 0
=== HRD ===
 cpb_cnt_minus1 : 0
 bit_rate_scale : 0
 cpb_size_scale : 0
   bit_rate_value_minus1[0] : 0
   cpb_size_value_minus1[0] : 0
   cbr_flag[0] : 0
 initial_cpb_removal_delay_length_minus1 : 0
 cpb_removal_delay_length_minus1 : 0
 dpb_output_delay_length_minus1 : 0
 time_offset_length : 0

I guess it has something to do with luma and chroma macroblocks size I've been able to calculate SubWidthC\SubHeightC and MbWidthC\MbHeightC. But I'm still confused what to do next.

Upvotes: 7

Views: 6722

Answers (2)

alfredh
alfredh

Reputation: 86

we now have an H.264 SPS parser in librem:

https://github.com/creytiv/rem/blob/master/include/rem_h264.h#L52

it can be used like this, to extract the resolution:

    struct h264_sps sps;
    struct vidsz vidsz;

    h264_sps_decode(&sps, buf, len);

    h264_sps_resolution(&sps, vidsz);

    printf("resolution: %u x %u\n", vidsz.w, vidsz.h);

Upvotes: 1

Ivan Roubíček
Ivan Roubíček

Reputation: 369

Hello first of all you are parsing SPS incorrectly so you need to fix that. If you parse it correctly then you will have

pic_width_in_mbs_minus1 : 79
pic_height_in_map_units_minus1 : 44
frame_mbs_only_flag : 1
frame_cropping_flag : 0

If you calculate width and height using your formula then you will actualy have 1280x720

Anyway you should calculate height and width using SubWidth and SubHeight as follows:

int SubWidthC;
int SubHeightC;

if (sps->chroma_format_idc == 0 && sps->separate_colour_plane_flag == 0) { //monochrome
    SubWidthC = SubHeightC = 0;
}
else if (sps->chroma_format_idc == 1 && sps->separate_colour_plane_flag == 0) { //4:2:0 
    SubWidthC = SubHeightC = 2;
}
else if (sps->chroma_format_idc == 2 && sps->separate_colour_plane_flag == 0) { //4:2:2 
    SubWidthC = 2;
    SubHeightC = 1;
}
else if (sps->chroma_format_idc == 3) { //4:4:4
    if (sps->separate_colour_plane_flag == 0) {
        SubWidthC = SubHeightC = 1;
    }
    else if (sps->separate_colour_plane_flag == 1) {
        SubWidthC = SubHeightC = 0;
    }
}

int PicWidthInMbs = sps->pic_width_in_mbs_minus1 + 1;

int PicHeightInMapUnits = sps->pic_height_in_map_units_minus1 + 1;
int FrameHeightInMbs = (2 - sps->frame_mbs_only_flag) * PicHeightInMapUnits;

int crop_left = 0;
int crop_right = 0;
int crop_top = 0;
int crop_bottom = 0;

if (sps->frame_cropping_flag) {
    crop_left = sps->frame_crop_left_offset;
    crop_right = sps->frame_crop_right_offset;
    crop_top = sps->frame_crop_top_offset;
    crop_bottom = sps->frame_crop_bottom_offset;
}

int width = PicWidthInMbs * 16 - SubWidthC * (crop_left + crop_right);
int height = FrameHeightInMbs * 16 - SubHeightC * (2 - sps->frame_mbs_only_flag) * (crop_top + crop_bottom);

Upvotes: 11

Related Questions