#include "Cafe/OS/libs/h264_avc/parser/H264Parser.h" void parse_hrd_parameters(h264ParserState_t* h264ParserState, RBSPInputBitstream& nalStream) { uint32 cpb_cnt_minus1 = nalStream.readUV_E(); uint8 bit_rate_scale = nalStream.readBits<4>(); uint8 cpb_size_scale = nalStream.readBits<4>(); for (uint8 schedSelIdx = 0; schedSelIdx <= cpb_cnt_minus1; schedSelIdx++) { uint32 bit_rate_value_minus1 = nalStream.readUV_E(); uint32 cpb_size_value_minus1 = nalStream.readUV_E(); uint8 cbr_flag = nalStream.readBit(); } uint8 initial_cpb_removal_delay_length_minus1 = nalStream.readBits<5>(); uint8 cpb_removal_delay_length_minus1 = nalStream.readBits<5>(); uint8 dpb_output_delay_length_minus1 = nalStream.readBits<5>(); uint8 time_offset_length = nalStream.readBits<5>(); } void parseNAL_scaling_list4x4(RBSPInputBitstream& rbspStream, h264_scaling_matrix4x4_t& scalingMatrix4x4) { if (rbspStream.readBit() == 0) { scalingMatrix4x4.isPresent = 0; return; } scalingMatrix4x4.isPresent = 1; cemu_assert_debug(false); // needs testing sint32 lastScale = 8; sint32 nextScale = 8; for (sint32 j = 0; j < 4 * 4; j++) { if (nextScale != 0) { sint32 delta_scale = rbspStream.readSV_E(); nextScale = (lastScale + delta_scale + 256) % 256; scalingMatrix4x4.UseDefaultScalingMatrix = (j == 0 && nextScale == 0); } scalingMatrix4x4.list[j] = (nextScale == 0) ? lastScale : nextScale; lastScale = scalingMatrix4x4.list[j]; } } void parseNAL_scaling_list8x8(RBSPInputBitstream& rbspStream, h264_scaling_matrix8x8_t& scalingMatrix8x8) { if (rbspStream.readBit() == 0) { scalingMatrix8x8.isPresent = 0; return; } scalingMatrix8x8.isPresent = 1; cemu_assert_debug(false); // needs testing sint32 lastScale = 8; sint32 nextScale = 8; for (sint32 j = 0; j < 8 * 8; j++) { if (nextScale != 0) { sint32 delta_scale = rbspStream.readSV_E(); nextScale = (lastScale + delta_scale + 256) % 256; scalingMatrix8x8.UseDefaultScalingMatrix = (j == 0 && nextScale == 0); } scalingMatrix8x8.list[j] = (nextScale == 0) ? lastScale : nextScale; lastScale = scalingMatrix8x8.list[j]; } } void parseNAL_pps_scaling_lists(h264ParserState_t* h264ParserState, RBSPInputBitstream& nalStream) { parseNAL_scaling_list4x4(nalStream, h264ParserState->pps.ScalingList4x4[0]); parseNAL_scaling_list4x4(nalStream, h264ParserState->pps.ScalingList4x4[1]); parseNAL_scaling_list4x4(nalStream, h264ParserState->pps.ScalingList4x4[2]); parseNAL_scaling_list4x4(nalStream, h264ParserState->pps.ScalingList4x4[3]); parseNAL_scaling_list4x4(nalStream, h264ParserState->pps.ScalingList4x4[4]); parseNAL_scaling_list4x4(nalStream, h264ParserState->pps.ScalingList4x4[5]); if (h264ParserState->pps.transform_8x8_mode_flag) { parseNAL_scaling_list8x8(nalStream, h264ParserState->pps.ScalingList8x8[0]); parseNAL_scaling_list8x8(nalStream, h264ParserState->pps.ScalingList8x8[1]); if (h264ParserState->sps.chroma_format_idc == 3) cemu_assert(false); // todo - more scaling lists to parse } } void parseNAL_sps_scaling_lists(h264ParserState_t* h264ParserState, RBSPInputBitstream& nalStream) { parseNAL_scaling_list4x4(nalStream, h264ParserState->sps.ScalingList4x4[0]); parseNAL_scaling_list4x4(nalStream, h264ParserState->sps.ScalingList4x4[1]); parseNAL_scaling_list4x4(nalStream, h264ParserState->sps.ScalingList4x4[2]); parseNAL_scaling_list4x4(nalStream, h264ParserState->sps.ScalingList4x4[3]); parseNAL_scaling_list4x4(nalStream, h264ParserState->sps.ScalingList4x4[4]); parseNAL_scaling_list4x4(nalStream, h264ParserState->sps.ScalingList4x4[5]); parseNAL_scaling_list8x8(nalStream, h264ParserState->sps.ScalingList8x8[0]); parseNAL_scaling_list8x8(nalStream, h264ParserState->sps.ScalingList8x8[1]); if (h264ParserState->sps.chroma_format_idc == 3) cemu_assert(false); // todo - more scaling lists to parse } bool parseNAL_seq_parameter_set_rbsp(h264ParserState_t* h264ParserState, h264ParserOutput_t* output, RBSPInputBitstream& nalStream) { memset(&h264ParserState->sps, 0, sizeof(h264State_seq_parameter_set_t)); h264ParserState->sps.profile_idc = nalStream.readU8(); // 0x64 = high profile h264ParserState->sps.constraint = nalStream.readU8(); // 6 flags + 2 reserved bits h264ParserState->sps.level_idc = nalStream.readU8(); // 0x29 = level 4.1 // some default values in case flags are not set h264ParserState->sps.separate_colour_plane_flag = 0; h264ParserState->sps.chroma_format_idc = 1; // Spec 7.4.2.1.1 h264ParserState->sps.qpprime_y_zero_transform_bypass_flag = 0; h264ParserState->sps.seq_scaling_matrix_present_flag = 0; //h264ParserState->sps.mb_adaptive_frame_field_flag = 0; uint32 seq_parameter_set_id = nalStream.readUV_E(); if (h264ParserState->sps.profile_idc == 100 || h264ParserState->sps.profile_idc == 110 || h264ParserState->sps.profile_idc == 122 || h264ParserState->sps.profile_idc == 244 || h264ParserState->sps.profile_idc == 44 || h264ParserState->sps.profile_idc == 83 || h264ParserState->sps.profile_idc == 86 || h264ParserState->sps.profile_idc == 118 || h264ParserState->sps.profile_idc == 128 || h264ParserState->sps.profile_idc == 138 || h264ParserState->sps.profile_idc == 139 || h264ParserState->sps.profile_idc == 134 || h264ParserState->sps.profile_idc == 135) { h264ParserState->sps.chroma_format_idc = nalStream.readUV_E(); if (h264ParserState->sps.chroma_format_idc == 3) { h264ParserState->sps.separate_colour_plane_flag = nalStream.readBit(); } h264ParserState->sps.bit_depth_luma_minus8 = nalStream.readUV_E(); h264ParserState->sps.bit_depth_chroma_minus8 = nalStream.readUV_E(); h264ParserState->sps.qpprime_y_zero_transform_bypass_flag = nalStream.readBit(); h264ParserState->sps.seq_scaling_matrix_present_flag = nalStream.readBit(); if (h264ParserState->sps.seq_scaling_matrix_present_flag) { parseNAL_sps_scaling_lists(h264ParserState, nalStream); } } h264ParserState->sps.log2_max_frame_num_minus4 = nalStream.readUV_E(); h264ParserState->sps.pic_order_cnt_type = nalStream.readUV_E(); if (h264ParserState->sps.pic_order_cnt_type == 0) { h264ParserState->sps.log2_max_pic_order_cnt_lsb_minus4 = nalStream.readUV_E(); } else if (h264ParserState->sps.pic_order_cnt_type == 2) { // nothing to parse } else { // todo - parse fields cemu_assert_debug(false); } h264ParserState->sps.num_ref_frames = nalStream.readUV_E(); h264ParserState->sps.gaps_in_frame_num_value_allowed_flag = nalStream.readBit(); h264ParserState->sps.pic_width_in_mbs_minus1 = nalStream.readUV_E(); h264ParserState->sps.pic_height_in_map_units_minus1 = nalStream.readUV_E(); h264ParserState->sps.frame_mbs_only_flag = nalStream.readBit(); if (h264ParserState->sps.frame_mbs_only_flag == 0) { h264ParserState->sps.mb_adaptive_frame_field_flag = nalStream.readBit(); cemu_assert_debug(false); } else h264ParserState->sps.mb_adaptive_frame_field_flag = 0; // default is zero? h264ParserState->sps.direct_8x8_inference_flag = nalStream.readBit(); if (h264ParserState->sps.frame_mbs_only_flag == 0 && h264ParserState->sps.direct_8x8_inference_flag != 1) { cemu_assert_debug(false); // not allowed } h264ParserState->sps.frame_cropping_flag = nalStream.readBit(); if (h264ParserState->sps.frame_cropping_flag) { h264ParserState->sps.frame_crop_left_offset = nalStream.readUV_E(); h264ParserState->sps.frame_crop_right_offset = nalStream.readUV_E(); h264ParserState->sps.frame_crop_top_offset = nalStream.readUV_E(); h264ParserState->sps.frame_crop_bottom_offset = nalStream.readUV_E(); } uint8 vui_parameters_present_flag = nalStream.readBit(); if (vui_parameters_present_flag) { // vui_parameters uint8 aspect_ratio_info_present_flag = nalStream.readBit(); if (aspect_ratio_info_present_flag) { uint32 aspect_ratio_idc = nalStream.readBits<8>(); if (aspect_ratio_idc == 255) // Extended_SAR { uint16 sar_width = nalStream.readBits<16>(); uint16 sar_height = nalStream.readBits<16>(); } } uint8 overscan_info_present_flag = nalStream.readBit(); if (overscan_info_present_flag) { cemu_assert_debug(false); } uint8 video_signal_type_present_flag = nalStream.readBit(); if (video_signal_type_present_flag) { uint8 video_format = nalStream.readBits<3>(); uint8 video_full_range_flag = nalStream.readBit(); uint8 colour_description_present_flag = nalStream.readBit(); if (colour_description_present_flag) { uint8 colour_primaries = nalStream.readBits<8>(); uint8 transfer_characteristics = nalStream.readBits<8>(); uint8 matrix_coefficients = nalStream.readBits<8>(); } } uint8 chroma_loc_info_present_flag = nalStream.readBit(); if (chroma_loc_info_present_flag) { uint32 chroma_sample_loc_type_top_field = nalStream.readUV_E(); uint32 chroma_sample_loc_type_bottom_field = nalStream.readUV_E(); } uint8 timing_info_present_flag = nalStream.readBit(); if (timing_info_present_flag) { uint32 num_units_in_tick = nalStream.readBits<32>(); uint32 time_scale = nalStream.readBits<32>(); uint8 fixed_frame_rate_flag = nalStream.readBits<1>(); } uint8 nal_hrd_parameters_present_flag = nalStream.readBit(); if (nal_hrd_parameters_present_flag) { parse_hrd_parameters(h264ParserState, nalStream); } uint8 vcl_hrd_parameters_present_flag = nalStream.readBit(); if (vcl_hrd_parameters_present_flag) { parse_hrd_parameters(h264ParserState, nalStream); } if (nal_hrd_parameters_present_flag || vcl_hrd_parameters_present_flag) { uint8 low_delay_hrd_flag = nalStream.readBit(); } uint8 pic_struct_present_flag = nalStream.readBit(); uint8 bitstream_restriction_flag = nalStream.readBit(); if (bitstream_restriction_flag) { uint8 motion_vectors_over_pic_boundaries_flag = nalStream.readBit(); uint32 max_bytes_per_pic_denom = nalStream.readUV_E(); uint32 max_bits_per_mb_denom = nalStream.readUV_E(); uint32 log2_max_mv_length_horizontal = nalStream.readUV_E(); uint32 log2_max_mv_length_vertical = nalStream.readUV_E(); uint32 max_num_reorder_frames = nalStream.readUV_E(); uint32 max_dec_frame_buffering = nalStream.readUV_E(); } } // trailing bits bool nalValid = true; if (nalStream.readTrailingRBSPBits() == false) nalValid = false; if (nalValid) { if(output) output->hasSPS = true; h264ParserState->hasSPS = true; } return true; } bool parseNAL_pic_parameter_set_rbsp(h264ParserState_t* h264ParserState, h264ParserOutput_t* output, RBSPInputBitstream& nalStream) { memset(&h264ParserState->pps, 0, sizeof(h264State_pic_parameter_set_t)); h264ParserState->pps.pic_parameter_set_id = nalStream.readUV_E(); h264ParserState->pps.seq_parameter_set_id = nalStream.readUV_E(); h264ParserState->pps.entropy_coding_mode_flag = nalStream.readBit(); h264ParserState->pps.bottom_field_pic_order_in_frame_present_flag = nalStream.readBit(); h264ParserState->pps.num_slice_groups_minus1 = nalStream.readUV_E(); if (h264ParserState->pps.num_slice_groups_minus1 > 0) { cemu_assert_debug(false); return false; } h264ParserState->pps.num_ref_idx_l0_default_active_minus1 = nalStream.readUV_E(); h264ParserState->pps.num_ref_idx_l1_default_active_minus1 = nalStream.readUV_E(); h264ParserState->pps.weighted_pred_flag = nalStream.readBit(); h264ParserState->pps.weighted_bipred_idc = nalStream.readBits<2>(); h264ParserState->pps.pic_init_qp_minus26 = nalStream.readSV_E(); h264ParserState->pps.pic_init_qs_minus26 = nalStream.readSV_E(); h264ParserState->pps.chroma_qp_index_offset = nalStream.readSV_E(); h264ParserState->pps.deblocking_filter_control_present_flag = nalStream.readBit(); h264ParserState->pps.constrained_intra_pred_flag = nalStream.readBit(); h264ParserState->pps.redundant_pic_cnt_present_flag = nalStream.readBit(); if (nalStream.more_rbsp_data()) { h264ParserState->pps.transform_8x8_mode_flag = nalStream.readBit(); h264ParserState->pps.pic_scaling_matrix_present_flag = nalStream.readBit(); if (h264ParserState->pps.pic_scaling_matrix_present_flag) { parseNAL_pps_scaling_lists(h264ParserState, nalStream); } h264ParserState->pps.second_chroma_qp_index_offset = nalStream.readSV_E(); } // trailing bits bool nalValid = true; if (nalStream.readTrailingRBSPBits() == false) nalValid = false; if (nalValid) { if(output) output->hasPPS = true; h264ParserState->hasPPS = true; } return true; } void parseNAL_ref_pic_list_modification(const h264State_seq_parameter_set_t& sps, const h264State_pic_parameter_set_t& pps, RBSPInputBitstream& nalStream, nal_slice_header_t* sliceHeader) { if (!sliceHeader->slice_type.isSliceTypeI() && !sliceHeader->slice_type.isSliceTypeSI()) { uint8 ref_pic_list_modification_flag_l0 = nalStream.readBit(); if (ref_pic_list_modification_flag_l0) { sliceHeader->pic_list_modification0Count = 0; uint32 modType; while(true) { if (sliceHeader->pic_list_modification0Count >= 32) { cemu_assert_debug(false); return; } modType = nalStream.readUV_E(); sliceHeader->pic_list_modification0Array[sliceHeader->pic_list_modification0Count].type = modType; if (modType == 0 || modType == 1) { sliceHeader->pic_list_modification0Array[sliceHeader->pic_list_modification0Count].abs_diff_pic_num_minus1 = nalStream.readUV_E(); } else if (modType == 2) { sliceHeader->pic_list_modification0Array[sliceHeader->pic_list_modification0Count].long_term_pic_num = nalStream.readUV_E(); } else if (modType == 3) { // end of list break; } else { cemu_assert_debug(false); // invalid type return; } sliceHeader->pic_list_modification0Count++; } } } if (sliceHeader->slice_type.isSliceTypeB()) { uint8 ref_pic_list_modification_flag_l1 = nalStream.readBit(); if (ref_pic_list_modification_flag_l1) { cemu_assert_debug(false); // testing required while (true) { if (sliceHeader->pic_list_modification1Count >= 32) { cemu_assert_debug(false); return; } uint32 modType = nalStream.readUV_E(); // aka modification_of_pic_nums_idc sliceHeader->pic_list_modification1Array[sliceHeader->pic_list_modification1Count].type = modType; if (modType == 0 || modType == 1) { sliceHeader->pic_list_modification1Array[sliceHeader->pic_list_modification1Count].abs_diff_pic_num_minus1 = nalStream.readUV_E(); } else if (modType == 2) { sliceHeader->pic_list_modification1Array[sliceHeader->pic_list_modification1Count].long_term_pic_num = nalStream.readUV_E(); } else if(modType == 3) { break; } else { cemu_assert_debug(false); // invalid mode break; } sliceHeader->pic_list_modification0Count++; } if (sliceHeader->pic_list_modification1Count > 0) { cemuLog_logDebug(LogType::Force, "sliceHeader->pic_list_modification1Count non-zero is not supported"); cemu_assert_unimplemented(); } } } } void parseNAL_dec_ref_pic_marking(const h264State_seq_parameter_set_t& sps, const h264State_pic_parameter_set_t& pps, RBSPInputBitstream& nalStream, nal_slice_header_t* sliceHeader) { sliceHeader->memory_management_control_operation_num = 0; if (sliceHeader->IdrPicFlag) { uint8 no_output_of_prior_pics_flag = nalStream.readBit(); if (no_output_of_prior_pics_flag) cemu_assert_debug(false); uint8 long_term_reference_flag = nalStream.readBit(); if (long_term_reference_flag) cemu_assert_debug(false); sliceHeader->adaptive_ref_pic_marking_mode_flag = 1; } else { sliceHeader->adaptive_ref_pic_marking_mode_flag = nalStream.readBit(); if (sliceHeader->adaptive_ref_pic_marking_mode_flag) { while (true) { uint32 memory_management_control_operation = nalStream.readUV_E(); if (memory_management_control_operation == nal_slice_header_t::MEMOP_END) break; if (sliceHeader->memory_management_control_operation_num >= 16) { cemu_assert_debug(false); return; } sliceHeader->memory_management_control_operation[sliceHeader->memory_management_control_operation_num].op = memory_management_control_operation; if (memory_management_control_operation == nal_slice_header_t::MEMOP_REMOVE_REF_FROM_SHORT_TERM || memory_management_control_operation == nal_slice_header_t::MEMOP_MAKE_LONG_TERM_REF) { sliceHeader->memory_management_control_operation[sliceHeader->memory_management_control_operation_num].difference_of_pic_nums_minus1 = nalStream.readUV_E(); } else if (memory_management_control_operation == nal_slice_header_t::MEMOP_REMOVE_REF_FROM_LONG_TERM) { sliceHeader->memory_management_control_operation[sliceHeader->memory_management_control_operation_num].long_term_pic_num = nalStream.readUV_E(); } if (memory_management_control_operation == nal_slice_header_t::MEMOP_MAKE_LONG_TERM_REF || memory_management_control_operation == nal_slice_header_t::MEMOP_MAKE_CURRENT_LONG_TERM_REF) { sliceHeader->memory_management_control_operation[sliceHeader->memory_management_control_operation_num].long_term_frame_idx = nalStream.readUV_E(); } if (memory_management_control_operation == nal_slice_header_t::MEMOP_MAX_LONG_TERM_INDEX) { sliceHeader->memory_management_control_operation[sliceHeader->memory_management_control_operation_num].max_long_term_frame_idx_plus1 = nalStream.readUV_E(); } sliceHeader->memory_management_control_operation_num++; } } } } void parseNAL_pred_weight_table(const h264State_seq_parameter_set_t& sps, const h264State_pic_parameter_set_t& pps, RBSPInputBitstream& nalStream, nal_slice_header_t* sliceHeader) { uint8 luma_log2_weight_denom = nalStream.readUV_E(); uint32 ChromaArrayType; if (sps.separate_colour_plane_flag == 0) ChromaArrayType = sps.chroma_format_idc; else ChromaArrayType = 0; if (ChromaArrayType != 0) { uint32 chroma_log2_weight_denom = nalStream.readUV_E(); } for (uint32 i = 0; i <= sliceHeader->num_ref_idx_l0_active_minus1; i++) { uint8 luma_weight_l0_flag = nalStream.readBit(); if (luma_weight_l0_flag) { uint32 luma_weight_l0 = nalStream.readSV_E(); uint32 luma_offset_l0 = nalStream.readSV_E(); } if (ChromaArrayType != 0) { uint8 chroma_weight_l0_flag = nalStream.readBit(); if (chroma_weight_l0_flag) { for (sint32 j = 0; j < 2; j++) { uint32 chroma_weight_l0 = nalStream.readSV_E(); uint32 chroma_offset_l0 = nalStream.readSV_E(); } } } } if (sliceHeader->slice_type.isSliceTypeB()) { for (uint32 i = 0; i <= sliceHeader->num_ref_idx_l1_active_minus1; i++) { uint8 luma_weight_l1_flag = nalStream.readBit(); if (luma_weight_l1_flag) { cemu_assert_debug(false); //luma_weight_l1[i] //luma_offset_l1[i] } if (ChromaArrayType != 0) { cemu_assert_debug(false); //chroma_weight_l1_flag //if (chroma_weight_l1_flag) //{ // for (j = 0; j < 2; j++) // { // chroma_weight_l1[i][j] // chroma_offset_l1[i][j] // } //} } } } } void parseNAL_slice_header(const h264State_seq_parameter_set_t& sps, const h264State_pic_parameter_set_t& pps, RBSPInputBitstream& nalStream, uint8 nal_unit_type, uint8 nal_ref_idc, nal_slice_header_t* sliceHeader) { bool IdrPicFlag = nal_unit_type == 5; memset(sliceHeader, 0, sizeof(nal_slice_header_t)); sliceHeader->nal_ref_idc = nal_ref_idc; sliceHeader->nal_unit_type = nal_unit_type; sliceHeader->first_mb_in_slice = nalStream.readUV_E(); // address of first macroblock in slice sliceHeader->slice_type = { nalStream.readUV_E() }; sliceHeader->pic_parameter_set_id = nalStream.readUV_E(); if (sps.separate_colour_plane_flag == 1) { cemu_assert_debug(false); return; } sliceHeader->frame_num = nalStream.readBits(sps.log2_max_frame_num_minus4 + 4); if (sps.frame_mbs_only_flag == 0) { sliceHeader->field_pic_flag = nalStream.readBit(); if (sliceHeader->field_pic_flag) { sliceHeader->bottom_field_flag = nalStream.readBit(); } } sliceHeader->IdrPicFlag = IdrPicFlag?1:0; if (IdrPicFlag) { sliceHeader->idr_pic_id = nalStream.readUV_E(); } if (sps.pic_order_cnt_type == 0) { sliceHeader->pic_order_cnt_lsb = nalStream.readBits(sps.log2_max_pic_order_cnt_lsb_minus4 + 4); if (pps.bottom_field_pic_order_in_frame_present_flag && sliceHeader->field_pic_flag == 0) sliceHeader->delta_pic_order_cnt_bottom = nalStream.readSV_E(); } else if (sps.pic_order_cnt_type == 1) { cemu_assert(false); } if (pps.redundant_pic_cnt_present_flag) { sliceHeader->redundant_pic_cnt = nalStream.readUV_E(); } if (sliceHeader->slice_type.isSliceTypeB()) { sliceHeader->direct_spatial_mv_pred_flag = nalStream.readBit(); } sliceHeader->num_ref_idx_l0_active_minus1 = pps.num_ref_idx_l0_default_active_minus1; sliceHeader->num_ref_idx_l1_active_minus1 = pps.num_ref_idx_l1_default_active_minus1; if (sliceHeader->slice_type.isSliceTypeP() || sliceHeader->slice_type.isSliceTypeSP() || sliceHeader->slice_type.isSliceTypeB()) { sliceHeader->num_ref_idx_active_override_flag = nalStream.readBit(); if (sliceHeader->num_ref_idx_active_override_flag) { sliceHeader->num_ref_idx_l0_active_minus1 = nalStream.readUV_E(); if (sliceHeader->slice_type.isSliceTypeB()) { sliceHeader->num_ref_idx_l1_active_minus1 = nalStream.readUV_E(); } } } // todo - ref_pic_list_mvc_modification etc if (nal_unit_type == 20 || nal_unit_type == 21) { cemu_assert_debug(false); } else { parseNAL_ref_pic_list_modification(sps, pps, nalStream, sliceHeader); } if ((pps.weighted_pred_flag && (sliceHeader->slice_type.isSliceTypeP() || sliceHeader->slice_type.isSliceTypeSP())) || (pps.weighted_bipred_idc == 1 && sliceHeader->slice_type.isSliceTypeB())) { parseNAL_pred_weight_table(sps, pps, nalStream, sliceHeader); } if (sliceHeader->nal_ref_idc != 0) { parseNAL_dec_ref_pic_marking(sps, pps, nalStream, sliceHeader); } if (pps.entropy_coding_mode_flag && !sliceHeader->slice_type.isSliceTypeI() && !sliceHeader->slice_type.isSliceTypeSI()) { uint32 cabac_init_idc = nalStream.readUV_E(); cemu_assert_debug(cabac_init_idc <= 2); // invalid value } sint32 slice_qp_delta = nalStream.readSV_E(); if (sliceHeader->slice_type.isSliceTypeSP() || sliceHeader->slice_type.isSliceTypeSI()) { if (sliceHeader->slice_type.isSliceTypeSP()) { uint8 sp_for_switch_flag = nalStream.readBit(); } sint32 slice_qs_delta = nalStream.readSV_E(); } if (pps.deblocking_filter_control_present_flag) { uint32 disable_deblocking_filter_idc = nalStream.readUV_E(); if (disable_deblocking_filter_idc != 1) { sint32 slice_alpha_c0_offset_div2 = nalStream.readSV_E(); sint32 slice_beta_offset_div2 = nalStream.readSV_E(); } } if (pps.num_slice_groups_minus1 > 0 && pps.slice_group_map_type >= 3 && pps.slice_group_map_type <= 5) { cemu_assert_debug(false); // todo } } void _calculateFrameOrder(h264ParserState_t* h264ParserState, const h264State_seq_parameter_set_t& sps, const h264State_pic_parameter_set_t& pps, nal_slice_header_t* sliceHeader) { if (sps.pic_order_cnt_type == 0) { // calculate TopFieldOrderCnt uint32 prevPicOrderCntMsb; uint32 prevPicOrderCntLsb; if (sliceHeader->IdrPicFlag) { sliceHeader->calculated.TopFieldOrderCnt = 0; prevPicOrderCntMsb = 0; prevPicOrderCntLsb = 0; } else { uint32 prevRefPic_PicOrderCntMsb = h264ParserState->picture_order.prevPicOrderCntMsb; uint32 prevRefPic_pic_order_cnt_lsb = h264ParserState->picture_order.prevPicOrderCntLsb; // todo prevPicOrderCntMsb = prevRefPic_PicOrderCntMsb; prevPicOrderCntLsb = prevRefPic_pic_order_cnt_lsb; } uint32 MaxPicOrderCntLsb = 1 << (sps.log2_max_pic_order_cnt_lsb_minus4 + 4); // todo - verify uint32 PicOrderCntMsb; if ((sliceHeader->pic_order_cnt_lsb < prevPicOrderCntLsb) && (((sint32)prevPicOrderCntLsb - (sint32)sliceHeader->pic_order_cnt_lsb) >= (sint32)(MaxPicOrderCntLsb / 2))) PicOrderCntMsb = prevPicOrderCntMsb + MaxPicOrderCntLsb; else if ((sliceHeader->pic_order_cnt_lsb > prevPicOrderCntLsb) && (((sint32)sliceHeader->pic_order_cnt_lsb - (sint32)prevPicOrderCntLsb) > (sint32)(MaxPicOrderCntLsb / 2))) PicOrderCntMsb = prevPicOrderCntMsb - MaxPicOrderCntLsb; else PicOrderCntMsb = prevPicOrderCntMsb; uint32 TopFieldOrderCnt = PicOrderCntMsb + sliceHeader->pic_order_cnt_lsb; if (sliceHeader->IdrPicFlag != 0 && TopFieldOrderCnt != 0) cemu_assert(false); sliceHeader->calculated.TopFieldOrderCnt = TopFieldOrderCnt; h264ParserState->picture_order.prevPicOrderCntMsb = PicOrderCntMsb; h264ParserState->picture_order.prevPicOrderCntLsb = sliceHeader->pic_order_cnt_lsb; } else if (sps.pic_order_cnt_type == 2) { // display order matches decode order uint32 prevFrameNum = h264ParserState->picture_order.prevFrameNum; ; uint32 FrameNumOffset; if (sliceHeader->IdrPicFlag) { FrameNumOffset = 0; } else { // todo - check for memory_management_control_operation 5 // prevFrameNumOffset is set equal to the value of FrameNumOffset of the previous picture in decoding order. uint32 prevFrameNumOffset = h264ParserState->picture_order.prevFrameNumOffset; if (prevFrameNum > sliceHeader->frame_num) FrameNumOffset = prevFrameNumOffset + sps.getMaxFrameNum(); else FrameNumOffset = prevFrameNumOffset; } uint32 tempPicOrderCnt; if (sliceHeader->IdrPicFlag == 1) tempPicOrderCnt = 0; else if (sliceHeader->nal_ref_idc == 0) tempPicOrderCnt = 2 * (FrameNumOffset + sliceHeader->frame_num) - 1; else tempPicOrderCnt = 2 * (FrameNumOffset + sliceHeader->frame_num); uint32 TopFieldOrderCnt = 0; uint32 BottomFieldOrderCnt = 0; if (sliceHeader->field_pic_flag == 0) { TopFieldOrderCnt = tempPicOrderCnt; BottomFieldOrderCnt = tempPicOrderCnt; } else if (sliceHeader->bottom_field_flag != 0) { cemu_assert_debug(false); // fields aren't supported BottomFieldOrderCnt = tempPicOrderCnt; } else { cemu_assert_debug(false); // fields aren't supported TopFieldOrderCnt = tempPicOrderCnt; } sliceHeader->calculated.TopFieldOrderCnt = TopFieldOrderCnt; h264ParserState->picture_order.prevFrameNum = sliceHeader->frame_num; h264ParserState->picture_order.prevFrameNumOffset = FrameNumOffset; } else { cemu_assert_debug(false); } } void parseNAL_slice_layer_without_partitioning_rbsp(h264ParserState_t* h264ParserState, h264ParserOutput_t* output, sint32 streamSubOffset, sint32 streamSubLength, RBSPInputBitstream& nalStream, uint8 nal_ref_idc, uint8 nal_unit_type) { nal_slice_t slice = {}; cemu_assert_debug(h264ParserState->hasPPS && h264ParserState->hasSPS); parseNAL_slice_header(h264ParserState->sps, h264ParserState->pps, nalStream, nal_unit_type, nal_ref_idc, &slice.slice_header); _calculateFrameOrder(h264ParserState, h264ParserState->sps, h264ParserState->pps, &slice.slice_header); if (output->sliceCount >= output->sliceInfo.size()) { cemu_assert_debug(false); // internal slice buffer full return; } output->sliceInfo[output->sliceCount].header = slice.slice_header; output->sliceInfo[output->sliceCount].streamSubOffset = streamSubOffset; output->sliceInfo[output->sliceCount].streamSubSize = streamSubLength; output->sliceCount++; } void h264Parse(h264ParserState_t* h264ParserState, h264ParserOutput_t* output, uint8* data, uint32 length, bool parseSlices) { memset(output, 0, sizeof(h264ParserOutput_t)); NALInputBitstream nalStream(data, length); if (nalStream.hasError()) { cemu_assert_debug(false); return; } // parse NAL data while (true) { if (nalStream.isEndOfStream()) break; RBSPInputBitstream rbspStream; if (nalStream.getNextRBSP(rbspStream) == false) break; sint32 streamSubOffset = (sint32)(rbspStream.getBasePtr() - data); sint32 streamSubLength = rbspStream.getBaseLength(); // parse NAL header uint8 nalHeaderByte = rbspStream.readU8(); if ((nalHeaderByte & 0x80) != 0) { // MSB must be zero cemu_assert_debug(false); continue; } uint8 nal_ref_idc = (nalHeaderByte >> 5) & 0x3; uint8 nal_unit_type = (nalHeaderByte >> 0) & 0x1f; // nal_ref_idc -> If not 0, reference picture. Contains sequence set ?? // nal_unit_type -> RBSP type // nal_unit_type: // 0 - unspecified // 1 - Coded slice of a non-IDR picture slice_layer_without_partitioning_rbsp() // 2 - Coded slice data partition A slice_data_partition_a_layer_rbsp() // 3 - Coded slice data partition B slice_data_partition_b_layer_rbsp() // 4 - Coded slice data partition C slice_data_partition_c_layer_rbsp() // 5 - Coded slice of an IDR picture slice_layer_without_partitioning_rbsp() // 6 - Supplemental enhancement information (SEI) sei_rbsp() // 7 - Sequence parameter set seq_parameter_set_rbsp() // 8 - Picture parameter set pic_parameter_set_rbsp() // 9 - Access unit delimiter access_unit_delimiter_rbsp() // 10 - End of sequence end_of_seq_rbsp() // 11 - End of stream end_of_stream_rbsp() // 12 - Filler data filler_data_rbsp() // 13 to 23 - reserved // 24 to 31 - unspecified // VCL NAL -> nal_unit_type 1,2,3,4,5 // non-VCL NAL -> All other nal_unit_type values if (nal_unit_type == 14 || nal_unit_type == 20 || nal_unit_type == 21) { cemu_assert_debug(false); // todo - there are fields to parse here } switch (nal_unit_type) { case 1: if (parseSlices) parseNAL_slice_layer_without_partitioning_rbsp(h264ParserState, output, streamSubOffset, streamSubLength, rbspStream, nal_ref_idc, nal_unit_type); break; case 5: if (parseSlices) parseNAL_slice_layer_without_partitioning_rbsp(h264ParserState, output, streamSubOffset, streamSubLength, rbspStream, nal_ref_idc, nal_unit_type); break; case 6: // SEI break; case 7: cemu_assert_debug(parseNAL_seq_parameter_set_rbsp(h264ParserState, output, rbspStream)); break; case 8: cemu_assert_debug(parseNAL_pic_parameter_set_rbsp(h264ParserState, output, rbspStream)); break; case 9: // access unit delimiter break; case 10: // end of sequence break; case 12: // filler data // seen in Cocoto Magic Circus 2 intro video break; default: cemuLog_logDebug(LogType::Force, "Unsupported NAL unit type {}", nal_unit_type); cemu_assert_debug(false); // todo break; } } } sint32 h264GetUnitLength(h264ParserState_t* h264ParserState, uint8* data, uint32 length) { NALInputBitstream nalStream(data, length); if (nalStream.hasError()) { cemu_assert_debug(false); return -1; } // search for start code sint32 startCodeOffset = 0; bool hasStartcode = false; while (startCodeOffset < (sint32)(length - 3)) { if (data[startCodeOffset + 0] == 0x00 && data[startCodeOffset + 1] == 0x00 && data[startCodeOffset + 2] == 0x01) { hasStartcode = true; break; } startCodeOffset++; } if (hasStartcode == false) return -1; data += startCodeOffset; length -= startCodeOffset; // parse NAL data while (true) { if (nalStream.isEndOfStream()) break; RBSPInputBitstream rbspStream; if (nalStream.getNextRBSP(rbspStream, true) == false) break; sint32 streamSubOffset = (sint32)(rbspStream.getBasePtr() - data); sint32 streamSubLength = rbspStream.getBaseLength(); // parse NAL header uint8 nalHeaderByte = rbspStream.readU8(); if ((nalHeaderByte & 0x80) != 0) { // MSB must be zero cemu_assert_debug(false); continue; } uint8 nal_ref_idc = (nalHeaderByte >> 5) & 0x3; uint8 nal_unit_type = (nalHeaderByte >> 0) & 0x1f; if (nal_unit_type == 14 || nal_unit_type == 20 || nal_unit_type == 21) { cemu_assert_debug(false); continue; } switch (nal_unit_type) { case 1: case 5: { // note: We cant parse the slice data because we dont have SPS and PPS data reliably available // // currently we just assume there is 1 slice per unit return (sint32)((rbspStream.getBasePtr() + rbspStream.getBaseLength()) - data) + startCodeOffset; break; } case 6: // SEI break; case 7: cemu_assert_debug(parseNAL_seq_parameter_set_rbsp(h264ParserState, nullptr, rbspStream)); break; case 8: cemu_assert_debug(parseNAL_pic_parameter_set_rbsp(h264ParserState, nullptr, rbspStream)); break; case 9: // access unit delimiter break; case 10: // end of sequence break; default: cemuLog_logDebug(LogType::Force, "Unsupported NAL unit type {}", nal_unit_type); assert_dbg(); // todo - NAL 10 is used in DKC TF // todo break; } } return -1; } static const unsigned char h264_default_4x4_Intra[16] = { 6, 13, 13, 20, 20, 20, 28, 28, 28, 28, 32, 32, 32, 37, 37, 42 }; static const unsigned char h264_default_4x4_Inter[16] = { 10, 14, 14, 20, 20, 20, 24, 24, 24, 24, 27, 27, 27, 30, 30, 34 }; static const unsigned char h264_default_8x8_Intra[64] = { 6, 10, 10, 13, 11, 13, 16, 16, 16, 16, 18, 18, 18, 18, 18, 23, 23, 23, 23, 23, 23, 25, 25, 25, 25, 25, 25, 25, 27, 27, 27, 27, 27, 27, 27, 27, 29, 29, 29, 29, 29, 29, 29, 31, 31, 31, 31, 31, 31, 33, 33, 33, 33, 33, 36, 36, 36, 36, 38, 38, 38, 40, 40, 42 }; static const unsigned char h264_default_8x8_Inter[64] = { 9, 13, 13, 15, 13, 15, 17, 17, 17, 17, 19, 19, 19, 19, 19, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 30, 30, 30, 30, 32, 32, 32, 33, 33, 35 }; void h264Parser_getScalingMatrix4x4(h264State_seq_parameter_set_t* sps, h264State_pic_parameter_set_t* pps, nal_slice_header_t* sliceHeader, sint32 index, uint8* matrix4x4) { // use scaling lists from PPS first if (pps->pic_scaling_matrix_present_flag) { if (pps->ScalingList4x4[index].isPresent) { cemu_assert(false); // testing needed (what about UseDefaultScalingMatrix?) memcpy(matrix4x4, pps->ScalingList4x4[index].list, 4 * 4 * sizeof(uint8)); } else { if (index < 3) memcpy(matrix4x4, h264_default_4x4_Intra, 4 * 4 * sizeof(uint8)); else memcpy(matrix4x4, h264_default_4x4_Inter, 4 * 4 * sizeof(uint8)); } return; } // use scaling lists from SPS if (sps->seq_scaling_matrix_present_flag) { if (sps->ScalingList4x4[index].isPresent) { cemu_assert(false); // testing needed (what about UseDefaultScalingMatrix?) memcpy(matrix4x4, sps->ScalingList4x4[index].list, 4 * 4 * sizeof(uint8)); } else { if (index < 3) memcpy(matrix4x4, h264_default_4x4_Intra, 4 * 4 * sizeof(uint8)); else memcpy(matrix4x4, h264_default_4x4_Inter, 4 * 4 * sizeof(uint8)); } return; } // default (Flat_4x4_16) memset(matrix4x4, 16, 4 * 4 * sizeof(uint8)); } void h264Parser_getScalingMatrix8x8(h264State_seq_parameter_set_t* sps, h264State_pic_parameter_set_t* pps, nal_slice_header_t* sliceHeader, sint32 index, uint8* matrix8x8) { // use scaling lists from PPS first if (pps->pic_scaling_matrix_present_flag) { if (pps->ScalingList8x8[index].isPresent) { cemu_assert(false); // testing needed (what about UseDefaultScalingMatrix?) memcpy(matrix8x8, pps->ScalingList8x8[index].list, 8 * 8 * sizeof(uint8)); } else { if ((index&1) == 0) memcpy(matrix8x8, h264_default_8x8_Intra, 8 * 8 * sizeof(uint8)); else memcpy(matrix8x8, h264_default_8x8_Inter, 8 * 8 * sizeof(uint8)); } return; } // use scaling lists from SPS if (sps->seq_scaling_matrix_present_flag) { if (sps->ScalingList8x8[index].isPresent) { cemu_assert(false); // testing needed (what about UseDefaultScalingMatrix?) memcpy(matrix8x8, sps->ScalingList8x8[index].list, 8 * 8 * sizeof(uint8)); } else { if ((index & 1) == 0) memcpy(matrix8x8, h264_default_8x8_Intra, 8 * 8 * sizeof(uint8)); else memcpy(matrix8x8, h264_default_8x8_Inter, 8 * 8 * sizeof(uint8)); } return; } // default (Flat_8x8_16) memset(matrix8x8, 16, 8 * 8 * sizeof(uint8)); }