| 83 | } // namespace |
| 84 | |
| 85 | H264Info H264::parseSPS(fl::span<const fl::u8> spsData, fl::string* error_message) { |
| 86 | H264Info info; |
| 87 | |
| 88 | if (spsData.size() < 4) { |
| 89 | if (error_message) *error_message = "SPS data too small"; |
| 90 | return info; |
| 91 | } |
| 92 | |
| 93 | BitReader br(spsData); |
| 94 | |
| 95 | // NAL unit header: forbidden_zero_bit(1) + nal_ref_idc(2) + nal_unit_type(5) |
| 96 | fl::u8 nalHeader = (fl::u8)br.readBits(8); |
| 97 | fl::u8 nalType = nalHeader & 0x1F; |
| 98 | FL_UNUSED(nalType); // should be 7 for SPS |
| 99 | |
| 100 | // SPS header |
| 101 | fl::u8 profile_idc = (fl::u8)br.readBits(8); |
| 102 | br.readBits(8); // constraint_set flags + reserved |
| 103 | fl::u8 level_idc = (fl::u8)br.readBits(8); |
| 104 | br.readUE(); // seq_parameter_set_id |
| 105 | |
| 106 | info.profile = profile_idc; |
| 107 | info.level = level_idc; |
| 108 | |
| 109 | // High profile extensions |
| 110 | fl::u32 chromaFormatIdc = 1; // default |
| 111 | if (profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || |
| 112 | profile_idc == 244 || profile_idc == 44 || profile_idc == 83 || |
| 113 | profile_idc == 86 || profile_idc == 118 || profile_idc == 128 || |
| 114 | profile_idc == 138 || profile_idc == 139 || profile_idc == 134 || |
| 115 | profile_idc == 135) { |
| 116 | chromaFormatIdc = br.readUE(); |
| 117 | if (chromaFormatIdc == 3) { |
| 118 | br.readBits(1); // separate_colour_plane_flag |
| 119 | } |
| 120 | br.readUE(); // bit_depth_luma_minus8 |
| 121 | br.readUE(); // bit_depth_chroma_minus8 |
| 122 | br.readBits(1); // qpprime_y_zero_transform_bypass_flag |
| 123 | |
| 124 | fl::u32 seqScalingMatrixPresent = br.readBits(1); |
| 125 | if (seqScalingMatrixPresent) { |
| 126 | int limit = (chromaFormatIdc != 3) ? 8 : 12; |
| 127 | for (int i = 0; i < limit; i++) { |
| 128 | if (br.readBits(1)) { // seq_scaling_list_present_flag |
| 129 | skipScalingList(br, (i < 6) ? 16 : 64); |
| 130 | } |
| 131 | } |
| 132 | } |
| 133 | } |
| 134 | |
| 135 | br.readUE(); // log2_max_frame_num_minus4 |
| 136 | fl::u32 picOrderCntType = br.readUE(); |
| 137 | if (picOrderCntType == 0) { |
| 138 | br.readUE(); // log2_max_pic_order_cnt_lsb_minus4 |
| 139 | } else if (picOrderCntType == 1) { |
| 140 | br.readBits(1); // delta_pic_order_always_zero_flag |
| 141 | br.readSE(); // offset_for_non_ref_pic |
| 142 | br.readSE(); // offset_for_top_to_bottom_field |
nothing calls this directly
no test coverage detected