| // Copyright 2023 The ChromiumOS Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "hwdrm-videoproc-ta/h264_parser.h" | |
| #include <string.h> | |
| struct H264Bitstream { | |
| // Pointer to the next unread (not in curr_byte) byte in the stream. | |
| const uint8_t* data; | |
| // Bytes left in the stream (without the curr_byte). | |
| uint32_t bytes_left; | |
| // Contents of the current byte; first unread bit starting at position | |
| // 8 - bits_left_in_byte from MSB. | |
| uint8_t curr_byte; | |
| // Number of bits remaining in curr_byte. | |
| uint8_t bits_left_in_byte; | |
| // Used in emulation prevention three byte detection (see spec). | |
| // Initially set to 0xffff to accept all initial two-byte sequences. | |
| uint16_t prev_two_bytes; | |
| // Number of emulation prevent bytes read in stream. | |
| uint32_t emulation_prevention_bytes; | |
| }; | |
| static void InitBitReader(struct H264Bitstream* br, | |
| const uint8_t* data, | |
| uint32_t size) { | |
| br->data = data; | |
| br->bytes_left = size; | |
| br->bits_left_in_byte = 0; | |
| br->prev_two_bytes = 0xFFFF; | |
| br->emulation_prevention_bytes = 0; | |
| } | |
| static bool UpdateCurrByte(struct H264Bitstream* br) { | |
| if (br->bytes_left < 1) { | |
| return false; | |
| } | |
| // Emulation prevention three-byte detection. | |
| // If a sequence of 0x000003 is found, skip (ignore) the last byte (0x03). | |
| if (*br->data == 0x03 && (br->prev_two_bytes & 0xffff) == 0) { | |
| // Detected 0x000003, skip last byte. | |
| ++br->data; | |
| --br->bytes_left; | |
| ++br->emulation_prevention_bytes; | |
| // Need another full three bytes before we can detect the sequence again. | |
| br->prev_two_bytes = 0xffff; | |
| if (br->bytes_left < 1) { | |
| return false; | |
| } | |
| } | |
| // Load a new byte and advance pointers. | |
| br->curr_byte = *br->data++; | |
| --br->bytes_left; | |
| br->bits_left_in_byte = 8; | |
| br->prev_two_bytes = ((br->prev_two_bytes & 0xff) << 8) | br->curr_byte; | |
| return true; | |
| } | |
| // Read |num_bits| (1 to 31 inclusive) from the stream and return them | |
| // in |out|, with first bit in the stream as MSB in |out| at position | |
| // (|num_bits| - 1). | |
| static bool ReadBits(struct H264Bitstream* br, | |
| uint8_t num_bits, | |
| uint32_t* out) { | |
| if (num_bits >= 32) { | |
| return false; | |
| } | |
| int bits_left = num_bits; | |
| *out = 0; | |
| while (br->bits_left_in_byte < bits_left) { | |
| // Take all that's left in current byte, shift to make space for the rest. | |
| *out |= ((br->curr_byte & ((1u << br->bits_left_in_byte) - 1u)) | |
| << (bits_left - br->bits_left_in_byte)); | |
| bits_left -= br->bits_left_in_byte; | |
| if (!UpdateCurrByte(br)) { | |
| return false; | |
| } | |
| } | |
| *out |= (br->curr_byte >> (br->bits_left_in_byte - bits_left)); | |
| *out &= ((1u << num_bits) - 1u); | |
| br->bits_left_in_byte -= bits_left; | |
| return true; | |
| } | |
| static uint32_t NumBitsLeft(struct H264Bitstream* br) { | |
| return (br->bits_left_in_byte + br->bytes_left * 8); | |
| } | |
| static bool IsPSlice(struct H264SliceHeaderData* hdr) { | |
| return (hdr->slice_type % 5 == 0); | |
| } | |
| static bool IsBSlice(struct H264SliceHeaderData* hdr) { | |
| return (hdr->slice_type % 5 == 1); | |
| } | |
| static bool IsISlice(struct H264SliceHeaderData* hdr) { | |
| return (hdr->slice_type % 5 == 2); | |
| } | |
| static bool IsSPSlice(struct H264SliceHeaderData* hdr) { | |
| return (hdr->slice_type % 5 == 3); | |
| } | |
| static bool IsSISlice(struct H264SliceHeaderData* hdr) { | |
| return (hdr->slice_type % 5 == 4); | |
| } | |
| #define READ_BITS_OR_RETURN(num_bits, out) \ | |
| do { \ | |
| if (!ReadBits(br, num_bits, out)) { \ | |
| return false; \ | |
| } \ | |
| } while (0) | |
| #define TRUE_OR_RETURN(a) \ | |
| do { \ | |
| if (!(a)) { \ | |
| return false; \ | |
| } \ | |
| } while (0) | |
| #define IN_RANGE_OR_RETURN(val, min, max) \ | |
| do { \ | |
| if ((val) < (min) || (val) > (max)) { \ | |
| return false; \ | |
| } \ | |
| } while (0) | |
| // Exp-Golomb code parsing as specified in H.26x specifications. | |
| // Read one unsigned exp-Golomb code from the stream and return in |*out|. | |
| #define READ_UE_OR_RETURN(out) \ | |
| do { \ | |
| uint32_t _bit = 0; \ | |
| uint32_t _num_bits_processed = -1; \ | |
| do { \ | |
| READ_BITS_OR_RETURN(1, &_bit); \ | |
| _num_bits_processed++; \ | |
| } while (_bit == 0); \ | |
| if (_num_bits_processed > 31) { \ | |
| return false; \ | |
| } \ | |
| *out = (1u << _num_bits_processed) - 1u; \ | |
| uint32_t _rest; \ | |
| if (_num_bits_processed == 31) { \ | |
| READ_BITS_OR_RETURN(_num_bits_processed, &_rest); \ | |
| if (_rest == 0) { \ | |
| break; \ | |
| } else { \ | |
| return false; \ | |
| } \ | |
| } \ | |
| if (_num_bits_processed > 0) { \ | |
| READ_BITS_OR_RETURN(_num_bits_processed, &_rest); \ | |
| *out += _rest; \ | |
| } \ | |
| } while (0) | |
| // Read one signed exp-Golomb code from the stream and return in |*out|. | |
| #define READ_SE_OR_RETURN(out) \ | |
| do { \ | |
| uint32_t ue = 0; \ | |
| READ_UE_OR_RETURN(&ue); \ | |
| if (ue % 2 == 0) { \ | |
| *out = -(((int32_t)ue) / 2); \ | |
| } else { \ | |
| *out = ((int32_t)ue) / 2 + 1; \ | |
| } \ | |
| } while (0) | |
| static bool SkipRefPicListModification(struct H264Bitstream* br, | |
| uint32_t num_ref_idx_active_minus1) { | |
| if (num_ref_idx_active_minus1 >= 32) { | |
| return false; | |
| } | |
| for (int i = 0; i < 32; ++i) { | |
| int modification_of_pic_nums_idc; | |
| int data; | |
| READ_UE_OR_RETURN(&modification_of_pic_nums_idc); | |
| TRUE_OR_RETURN(modification_of_pic_nums_idc < 4); | |
| switch (modification_of_pic_nums_idc) { | |
| case 0: | |
| case 1: | |
| READ_UE_OR_RETURN(&data); // abs_diff_pic_num_minus1 | |
| break; | |
| case 2: | |
| READ_UE_OR_RETURN(&data); // long_term_pic_num | |
| break; | |
| case 3: | |
| // Per spec, list cannot be empty. | |
| if (i == 0) { | |
| return false; | |
| } | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| // If we got here, we didn't get loop end marker prematurely, | |
| // so make sure it is there for our client. | |
| int modification_of_pic_nums_idc; | |
| READ_UE_OR_RETURN(&modification_of_pic_nums_idc); | |
| TRUE_OR_RETURN(modification_of_pic_nums_idc == 3); | |
| return true; | |
| } | |
| static bool SkipWeightingFactors(struct H264Bitstream* br, | |
| int num_ref_idx_active_minus1, | |
| int chroma_array_type) { | |
| for (int i = 0; i < num_ref_idx_active_minus1 + 1; ++i) { | |
| uint32_t data; | |
| int32_t sdata; | |
| READ_BITS_OR_RETURN(1, &data); // luma_weight_flag | |
| if (data) { | |
| READ_SE_OR_RETURN(&sdata); // luma_weight[i] | |
| READ_SE_OR_RETURN(&sdata); // luma_offset[i] | |
| } | |
| if (chroma_array_type != 0) { | |
| READ_BITS_OR_RETURN(1, &data); // chroma_weight_flag | |
| if (data) { | |
| for (int j = 0; j < 2; ++j) { | |
| READ_SE_OR_RETURN(&sdata); // chroma_weight[i][j] | |
| READ_SE_OR_RETURN(&sdata); // chroma_offset[i][j] | |
| } | |
| } | |
| } | |
| } | |
| return true; | |
| } | |
| bool ParseSliceHeader(const uint8_t* slice_header, | |
| uint32_t header_size, | |
| const struct StreamDataForSliceHeader* stream_data, | |
| struct H264SliceHeaderData* hdr_out) { | |
| // Be very strict about bitstream conformance, we don't want this used as a | |
| // tool to extract data from anything else. | |
| if (header_size < 4) { | |
| return false; | |
| } | |
| if (slice_header[0] != 0 || slice_header[1] != 0 || slice_header[2] != 1) { | |
| return false; | |
| } | |
| // Initialize the reader, skip the 3 byte start code. | |
| struct H264Bitstream bitstream; | |
| struct H264Bitstream* br = &bitstream; | |
| InitBitReader(br, slice_header + 3, header_size - 3); | |
| memset(hdr_out, 0, sizeof(*hdr_out)); | |
| // Parse the NALU header. | |
| uint32_t data; | |
| // Forbidden zero bit. | |
| READ_BITS_OR_RETURN(1, &data); | |
| TRUE_OR_RETURN(data == 0); | |
| READ_BITS_OR_RETURN(2, &data); | |
| hdr_out->nal_ref_idc = (uint8_t)data; | |
| READ_BITS_OR_RETURN(5, &data); | |
| uint8_t nal_unit_type = (uint8_t)data; | |
| // It should only be a slice header NALU, nothing else is allowed here. | |
| if (nal_unit_type != 1 && nal_unit_type != 5) { | |
| return false; | |
| } | |
| hdr_out->idr_pic_flag = (nal_unit_type == 5); | |
| READ_UE_OR_RETURN(&data); // first_mb_in_slice | |
| READ_UE_OR_RETURN(&data); | |
| hdr_out->slice_type = (uint8_t)data; | |
| TRUE_OR_RETURN(hdr_out->slice_type < 10); | |
| READ_UE_OR_RETURN(&data); // pic_parameter_set_id | |
| TRUE_OR_RETURN(stream_data->log2_max_frame_num_minus4 < 13); | |
| READ_BITS_OR_RETURN(stream_data->log2_max_frame_num_minus4 + 4, | |
| &hdr_out->frame_num); | |
| if (!stream_data->frame_mbs_only_flag) { | |
| READ_BITS_OR_RETURN(1, &data); | |
| hdr_out->field_pic_flag = (uint8_t)data; | |
| } | |
| if (hdr_out->idr_pic_flag) { | |
| READ_UE_OR_RETURN(&hdr_out->idr_pic_id); | |
| } | |
| size_t bits_left_at_pic_order_cnt_start = NumBitsLeft(br); | |
| if (stream_data->pic_order_cnt_type == 0) { | |
| TRUE_OR_RETURN(stream_data->log2_max_pic_order_cnt_lsb_minus4 < 13); | |
| READ_BITS_OR_RETURN(stream_data->log2_max_pic_order_cnt_lsb_minus4 + 4, | |
| &hdr_out->pic_order_cnt_lsb); | |
| if (stream_data->bottom_field_pic_order_in_frame_present_flag && | |
| !hdr_out->field_pic_flag) { | |
| READ_SE_OR_RETURN(&hdr_out->delta_pic_order_cnt_bottom); | |
| } | |
| } | |
| if (stream_data->pic_order_cnt_type == 1 && | |
| !stream_data->delta_pic_order_always_zero_flag) { | |
| READ_SE_OR_RETURN(&hdr_out->delta_pic_order_cnt0); | |
| if (stream_data->bottom_field_pic_order_in_frame_present_flag && | |
| !hdr_out->field_pic_flag) { | |
| READ_SE_OR_RETURN(&hdr_out->delta_pic_order_cnt1); | |
| } | |
| } | |
| hdr_out->pic_order_cnt_bit_size = | |
| bits_left_at_pic_order_cnt_start - NumBitsLeft(br); | |
| if (stream_data->redundant_pic_cnt_present_flag) { | |
| READ_UE_OR_RETURN(&data); // redundant_pic_cnt | |
| TRUE_OR_RETURN(data < 128); | |
| } | |
| if (IsBSlice(hdr_out)) { | |
| READ_BITS_OR_RETURN(1, &data); // direct_spatial_mv_pred_flag | |
| } | |
| int num_ref_idx_l0_active_minus1 = 0; | |
| int num_ref_idx_l1_active_minus1 = 0; | |
| if (IsPSlice(hdr_out) || IsSPSlice(hdr_out) || IsBSlice(hdr_out)) { | |
| uint32_t num_ref_idx_active_override_flag; | |
| READ_BITS_OR_RETURN(1, &num_ref_idx_active_override_flag); | |
| if (num_ref_idx_active_override_flag) { | |
| READ_UE_OR_RETURN(&num_ref_idx_l0_active_minus1); | |
| if (IsBSlice(hdr_out)) { | |
| READ_UE_OR_RETURN(&num_ref_idx_l1_active_minus1); | |
| } | |
| } else { | |
| num_ref_idx_l0_active_minus1 = | |
| stream_data->num_ref_idx_l0_default_active_minus1; | |
| if (IsBSlice(hdr_out)) { | |
| num_ref_idx_l1_active_minus1 = | |
| stream_data->num_ref_idx_l1_default_active_minus1; | |
| } | |
| } | |
| } | |
| IN_RANGE_OR_RETURN(num_ref_idx_l0_active_minus1, 0, 31); | |
| IN_RANGE_OR_RETURN(num_ref_idx_l1_active_minus1, 0, 31); | |
| if (!IsISlice(hdr_out) && !IsSISlice(hdr_out)) { | |
| uint32_t ref_pic_list_modification_flag_l0; | |
| READ_BITS_OR_RETURN(1, &ref_pic_list_modification_flag_l0); | |
| if (ref_pic_list_modification_flag_l0) { | |
| if (!SkipRefPicListModification(br, num_ref_idx_l0_active_minus1)) { | |
| return false; | |
| } | |
| } | |
| } | |
| if (IsBSlice(hdr_out)) { | |
| uint32_t ref_pic_list_modification_flag_l1; | |
| READ_BITS_OR_RETURN(1, &ref_pic_list_modification_flag_l1); | |
| if (ref_pic_list_modification_flag_l1) { | |
| if (!SkipRefPicListModification(br, num_ref_idx_l1_active_minus1)) { | |
| return false; | |
| } | |
| } | |
| } | |
| if ((stream_data->weighted_pred_flag && | |
| (IsPSlice(hdr_out) || IsSPSlice(hdr_out))) || | |
| (stream_data->weighted_bipred_idc == 1 && IsBSlice(hdr_out))) { | |
| READ_UE_OR_RETURN(&data); // luma_log2_weight_denom | |
| if (stream_data->chroma_array_type != 0) { | |
| READ_UE_OR_RETURN(&data); // chroma_log2_weight_denom | |
| } | |
| if (!SkipWeightingFactors(br, num_ref_idx_l0_active_minus1, | |
| stream_data->chroma_array_type)) { | |
| return false; | |
| } | |
| if (IsBSlice(hdr_out)) { | |
| if (!SkipWeightingFactors(br, num_ref_idx_l1_active_minus1, | |
| stream_data->chroma_array_type)) { | |
| return false; | |
| } | |
| } | |
| } | |
| if (hdr_out->nal_ref_idc != 0) { | |
| size_t bits_left_at_start = NumBitsLeft(br); | |
| if (hdr_out->idr_pic_flag) { | |
| READ_BITS_OR_RETURN(1, &data); | |
| hdr_out->ref_pic_fields.bits.no_output_of_prior_pics_flag = data; | |
| READ_BITS_OR_RETURN(1, &data); | |
| hdr_out->ref_pic_fields.bits.long_term_reference_flag = data; | |
| } else { | |
| READ_BITS_OR_RETURN(1, &data); | |
| hdr_out->ref_pic_fields.bits.adaptive_ref_pic_marking_mode_flag = data; | |
| if (hdr_out->ref_pic_fields.bits.adaptive_ref_pic_marking_mode_flag) { | |
| size_t i; | |
| for (i = 0; i < 32; ++i) { | |
| READ_UE_OR_RETURN(&hdr_out->memory_management_control_operation[i]); | |
| if (hdr_out->memory_management_control_operation[i] == 0) { | |
| break; | |
| } | |
| hdr_out->ref_pic_fields.bits.dec_ref_pic_marking_count++; | |
| if (hdr_out->memory_management_control_operation[i] == 1 || | |
| hdr_out->memory_management_control_operation[i] == 3) { | |
| READ_UE_OR_RETURN(&hdr_out->difference_of_pic_nums_minus1[i]); | |
| } | |
| if (hdr_out->memory_management_control_operation[i] == 2) { | |
| READ_UE_OR_RETURN(&hdr_out->long_term_pic_num[i]); | |
| } | |
| if (hdr_out->memory_management_control_operation[i] == 3 || | |
| hdr_out->memory_management_control_operation[i] == 6) { | |
| READ_UE_OR_RETURN(&hdr_out->long_term_frame_idx[i]); | |
| } | |
| if (hdr_out->memory_management_control_operation[i] == 4) { | |
| READ_UE_OR_RETURN(&hdr_out->max_long_term_frame_idx_plus1[i]); | |
| } | |
| if (hdr_out->memory_management_control_operation[i] > 6) { | |
| return false; | |
| } | |
| } | |
| // We should break at the last field and never hit 32. | |
| if (i == 32) { | |
| return false; | |
| } | |
| } | |
| } | |
| hdr_out->dec_ref_pic_marking_bit_size = | |
| bits_left_at_start - NumBitsLeft(br); | |
| } | |
| return true; | |
| } |