| /** |
| * Copyright (C) 2022 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <media/NdkMediaCodec.h> |
| #include <media/NdkMediaFormat.h> |
| #include <ImsMediaVideoUtil.h> |
| #include <ImsMediaBitReader.h> |
| #include <ImsMediaBinaryFormat.h> |
| #include <ImsMediaTrace.h> |
| #include <VideoConfig.h> |
| #include <memory> |
| #include <string.h> |
| |
| #define START_CODE_PREFIX_LEN 4 |
| #define MAX_OUTPUT_BUFFER_READ_ATTEMPTS 5 |
| #define FRAME_TYPE_SPS 7 |
| #define FRAME_TYPE_PPS 8 |
| |
| ImsMediaVideoUtil::ImsMediaVideoUtil() {} |
| |
| ImsMediaVideoUtil::~ImsMediaVideoUtil() {} |
| |
| int32_t ImsMediaVideoUtil::ConvertCodecType(int32_t type) |
| { |
| switch (type) |
| { |
| default: |
| case VideoConfig::CODEC_AVC: |
| return kVideoCodecAvc; |
| case VideoConfig::CODEC_HEVC: |
| return kVideoCodecHevc; |
| } |
| } |
| |
| uint32_t ImsMediaVideoUtil::GetResolutionFromSize(uint32_t nWidth, uint32_t nHeight) |
| { |
| if (nWidth == 128 && nHeight == 96) |
| return kVideoResolutionSqcifLandscape; |
| else if (nWidth == 96 && nHeight == 128) |
| return kVideoResolutionSqcifPortrait; |
| else if (nWidth == 480 && nHeight == 640) |
| return kVideoResolutionVgaPortrait; |
| else if (nWidth == 640 && nHeight == 480) |
| return kVideoResolutionVgaLandscape; |
| else if (nWidth == 240 && nHeight == 320) |
| return kVideoResolutionQvgaPortrait; |
| else if (nWidth == 320 && nHeight == 240) |
| return kVideoResolutionQvgaLandscape; |
| else if (nWidth == 144 && nHeight == 176) |
| return kVideoResolutionQcifPortrait; |
| else if (nWidth == 176 && nHeight == 144) |
| return kVideoResolutionQcifLandscape; |
| else if (nWidth == 352 && nHeight == 288) |
| return kVideoResolutionCifLandscape; |
| else if (nWidth == 288 && nHeight == 352) |
| return kVideoResolutionCifPortrait; |
| else if (nWidth == 240 && nHeight == 352) |
| return kVideoResolutionSifPortrait; |
| else if (nWidth == 352 && nHeight == 240) |
| return kVideoResolutionSifLandscape; |
| else if (nWidth == 1280 && nHeight == 720) |
| return kVideoResolutionHdLandscape; |
| else if (nWidth == 720 && nHeight == 1280) |
| return kVideoResolutionHdPortrait; |
| else |
| return kVideoResolutionInvalid; |
| } |
| |
| ImsMediaResult ImsMediaVideoUtil::ParseAvcSpropParam(const char* szSpropparam, tCodecConfig* pInfo) |
| { |
| ImsMediaBitReader bitreader; |
| bool ret = false; |
| uint8_t pbSPSConfig[MAX_CONFIG_LEN] = {'\0'}; |
| char pSPSConfig[MAX_CONFIG_LEN] = {'\0'}; |
| uint32_t nSPSConfigSize = 0; |
| uint32_t chroma_format_idc = 0; |
| |
| memset(pSPSConfig, 0x00, MAX_CONFIG_LEN); |
| |
| for (int32_t i = 0; i < MAX_CONFIG_LEN; i++) |
| { |
| uint8_t Comma = ','; |
| char cmpConfig = *(szSpropparam + i); |
| if (Comma == cmpConfig) |
| { |
| memset(pSPSConfig, 0x00, MAX_CONFIG_LEN); |
| memcpy(pSPSConfig, szSpropparam, i); |
| } |
| } |
| |
| ret = ImsMediaBinaryFormat::Base00ToBinary( |
| pbSPSConfig, &nSPSConfigSize, MAX_CONFIG_LEN, pSPSConfig, BINARY_FORMAT_BASE64); |
| |
| if (ret == false) |
| { |
| IMLOGW0("[ParseAvcSpropParam] sps convert fail"); |
| return RESULT_INVALID_PARAM; |
| } |
| |
| uint8_t* pszSpropparam = reinterpret_cast<uint8_t*>(malloc(nSPSConfigSize)); |
| |
| if (pszSpropparam == nullptr) |
| { |
| pInfo->nProfile = 0; |
| pInfo->nLevel = 0; |
| pInfo->nHeight = 0; |
| pInfo->nWidth = 0; |
| |
| return RESULT_NO_MEMORY; |
| } |
| |
| memcpy(pszSpropparam, pbSPSConfig, nSPSConfigSize); |
| |
| bitreader.SetBuffer(pszSpropparam, nSPSConfigSize); |
| bitreader.Read(8); |
| |
| uint32_t Profile_idc = bitreader.Read(8); // Read profile_idc |
| |
| // Read constraint |
| bitreader.Read(1); // Constraint_set0 |
| bitreader.Read(1); // Constraint_set1 |
| bitreader.Read(1); // Constraint_set2 |
| bitreader.Read(1); // Constraint_set3 |
| bitreader.Read(1); // Constraint_set4 |
| bitreader.Read(3); // Read reserved_zeor_3bits |
| |
| uint32_t Level_idc = bitreader.Read(8); // Read level_idc |
| bitreader.ReadByUEMode(); // Read Seq_parameter_set_id |
| |
| if (Profile_idc == 100 || Profile_idc == 11 || Profile_idc == 122 || Profile_idc == 244 || |
| Profile_idc == 44 || Profile_idc == 83 || Profile_idc == 86 || Profile_idc == 118) |
| { |
| chroma_format_idc = bitreader.ReadByUEMode(); |
| |
| if (chroma_format_idc == 3) |
| { |
| bitreader.Read(1); // Separate_colour_plane |
| } |
| |
| bitreader.ReadByUEMode(); // Read bit_depth_luma_minus8 |
| bitreader.ReadByUEMode(); // Read bit_depth_chroma_minus8 |
| bitreader.Read(1); // Read qpprime_y_zero_transformypass |
| |
| // Read seq_scaling_matrix_present |
| if (bitreader.Read(1)) |
| { |
| uint32_t i = ((chroma_format_idc != 3) ? 8 : 12); |
| // Set scaling_list... not implement |
| bitreader.Read(i); |
| } |
| } |
| |
| pInfo->nProfile = Profile_idc; |
| pInfo->nLevel = Level_idc; |
| |
| bitreader.ReadByUEMode(); // Read log2_max_frame_num_minus4 |
| uint32_t pic_order_cnt_type = bitreader.ReadByUEMode(); // Read pic_order_cnt_type |
| |
| if (pic_order_cnt_type == 0) |
| { |
| bitreader.ReadByUEMode(); // Read log2_max_pic_order_cnt_lsb_minus4 |
| } |
| else if (pic_order_cnt_type == 1) |
| { |
| bitreader.Read(1); // Read delta_pic_order_always_zero_flag |
| bitreader.ReadByUEMode(); // Reda offset_for_non_ref_pic... |
| bitreader.ReadByUEMode(); // Read offset_for_top_to_bottom_field |
| uint32_t num_ref_frames_in_pic_order_cnt_cycle = |
| bitreader.ReadByUEMode(); // Read num_ref_frames_in_pic_order_cnt_cycle |
| for (int32_t i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++) |
| { |
| bitreader.ReadByUEMode(); // Read offset_for_ref_frame[i]; |
| } |
| } |
| |
| bitreader.ReadByUEMode(); // Read max_num_ref_frames |
| bitreader.Read(1); // Read gaps_in_frame_num_value_allowed |
| |
| uint32_t pic_width_in_mbs_minus1 = bitreader.ReadByUEMode(); // Read pic_width_in_mbs_minus1 |
| uint32_t pic_height_in_map_units_minus1 = |
| bitreader.ReadByUEMode(); // Read pic_height_in_map_units_minus1 |
| uint32_t frame_mbs_only = bitreader.Read(1); |
| |
| pInfo->nWidth = pic_width_in_mbs_minus1 * 16 + 16; |
| pInfo->nHeight = (2 - frame_mbs_only) * (pic_height_in_map_units_minus1 * 16 + 16); |
| |
| if (!frame_mbs_only) |
| { |
| bitreader.Read(1); // Read mb_adaptive_frame_field |
| } |
| |
| bitreader.Read(1); // Read direct_8x8_inferencce_flag |
| uint32_t frame_cropping = bitreader.Read(1); // Read frame_cropping_flag |
| |
| if (frame_cropping) |
| { |
| uint32_t frame_crop_left_offset = bitreader.ReadByUEMode(); // Read frame_crop_left_offset |
| uint32_t frame_crop_right_offset = bitreader.ReadByUEMode(); // Read |
| // frame_crop_right_offset |
| uint32_t frame_crop_top_offset = bitreader.ReadByUEMode(); // Read frame_crop_top_offset |
| uint32_t frame_crop_bottom_offset = |
| bitreader.ReadByUEMode(); // Read frame_crop_bottom_offset |
| uint32_t cropX, cropY = 0; |
| |
| if (chroma_format_idc == 0 /* Monochrome */) |
| { |
| cropX = 1; |
| cropY = 2 - frame_mbs_only; |
| } |
| else |
| { |
| uint32_t subWidthC = (chroma_format_idc == 3) ? 1 : 2; |
| uint32_t subHeightC = (chroma_format_idc == 1) ? 2 : 1; |
| cropX = subWidthC; |
| cropY = subHeightC * (2 - frame_mbs_only); |
| } |
| |
| pInfo->nWidth -= (frame_crop_left_offset + frame_crop_right_offset) * cropX; |
| pInfo->nHeight -= (frame_crop_top_offset + frame_crop_bottom_offset) * cropY; |
| } |
| |
| free(pszSpropparam); |
| |
| IMLOGD4("[ParseAvcSpropParam] width[%d],height[%d],nProfile[%d],nLevel[%d]", pInfo->nWidth, |
| pInfo->nHeight, pInfo->nProfile, pInfo->nLevel); |
| |
| return RESULT_SUCCESS; |
| } |
| |
| ImsMediaResult ImsMediaVideoUtil::ParseHevcSpropParam(const char* szSpropparam, tCodecConfig* pInfo) |
| { |
| uint32_t nSize = strlen(szSpropparam); |
| |
| if (nSize == 0) |
| { |
| return RESULT_INVALID_PARAM; |
| } |
| |
| IMLOGD1("[ParseHevcSpropParam] szSpropparam[%s]", szSpropparam); |
| |
| uint8_t pbSPSConfig[MAX_CONFIG_LEN] = {'\0'}; |
| char pSPSConfig[MAX_CONFIG_LEN] = {'\0'}; |
| uint32_t nSPSConfigSize = 0; |
| |
| memset(pSPSConfig, 0x00, MAX_CONFIG_LEN); |
| strlcpy(pSPSConfig, szSpropparam, MAX_CONFIG_LEN); |
| |
| uint8_t* pszSpropparam = nullptr; |
| |
| if (ImsMediaBinaryFormat::Base00ToBinary(pbSPSConfig, &nSPSConfigSize, MAX_CONFIG_LEN, |
| pSPSConfig, BINARY_FORMAT_BASE64) == false) |
| { |
| IMLOGW0("[ParseAvcSpropParam] sps convert fail"); |
| return RESULT_INVALID_PARAM; |
| } |
| |
| if (nSPSConfigSize == 0) |
| return RESULT_INVALID_PARAM; |
| |
| pszSpropparam = reinterpret_cast<uint8_t*>(malloc(nSPSConfigSize)); |
| |
| if (pszSpropparam == nullptr) |
| { |
| pInfo->nProfile = 0; |
| pInfo->nLevel = 0; |
| pInfo->nHeight = 0; |
| pInfo->nWidth = 0; |
| |
| return RESULT_INVALID_PARAM; |
| } |
| |
| memcpy(pszSpropparam, pbSPSConfig, nSPSConfigSize); |
| |
| // Check binary |
| ImsMediaTrace::IMLOGD_BINARY("[ParseHevcSpropParam] sps=", |
| reinterpret_cast<const char*>(pszSpropparam), nSPSConfigSize); |
| |
| uint32_t nOffset = 0; |
| |
| for (int32_t i = 0; i < nSPSConfigSize - 6; i++) |
| { |
| // NAL unit header offset |
| if (pszSpropparam[i] == 0x00 && pszSpropparam[i + 1] == 0x00 && |
| pszSpropparam[i + 2] == 0x00 && pszSpropparam[i + 3] == 0x01 && |
| pszSpropparam[i + 4] == 0x42 && pszSpropparam[i + 5] == 0x01) |
| { |
| nOffset = i + 6; |
| break; |
| } |
| } |
| |
| IMLOGD2("[ParseHevcSpropParam] nSPSConfigSize[%d], offset[%d]", nSPSConfigSize, nOffset); |
| |
| ImsMediaBitReader objBitReader; |
| objBitReader.SetBuffer(pszSpropparam + nOffset, nSPSConfigSize - nOffset); |
| |
| objBitReader.Read(4); // sps_video_parameter_set_id; |
| uint32_t sps_max_sub_layers_minus1 = objBitReader.Read(3); |
| |
| IMLOGD1("[ParseHevcSpropParam] sps_max_sub_layers_minus1[%d]", sps_max_sub_layers_minus1); |
| |
| objBitReader.Read(1); // sps_temporal_id_nesting_flag; |
| |
| /*-----------profile_tier_level start-----------------------*/ |
| objBitReader.Read(3); // general_profile_spac, general_tier_flag |
| uint32_t general_profile_idc = objBitReader.Read(5); |
| |
| IMLOGD1("[ParseHevcSpropParam] general_profile_idc[%d]", general_profile_idc); |
| |
| // skip 13byte - flags, not handle |
| objBitReader.Read(24); |
| objBitReader.Read(24); |
| objBitReader.Read(24); |
| objBitReader.Read(24); |
| objBitReader.Read(8); |
| |
| uint32_t general_level_idc = objBitReader.Read(8); |
| |
| IMLOGD1("[ParseHevcSpropParam] general_level_idc[%d]", general_level_idc); |
| |
| uint8_t sub_layer_profile_present_flag[sps_max_sub_layers_minus1]; |
| uint8_t sub_layer_level_present_flag[sps_max_sub_layers_minus1]; |
| |
| for (int32_t i = 0; i < sps_max_sub_layers_minus1; i++) |
| { |
| sub_layer_profile_present_flag[i] = objBitReader.Read(1); |
| sub_layer_level_present_flag[i] = objBitReader.Read(1); |
| } |
| |
| if (sps_max_sub_layers_minus1 > 0) |
| { |
| for (int32_t j = sps_max_sub_layers_minus1; j < 8; j++) |
| { |
| objBitReader.Read(2); |
| } |
| } |
| |
| for (int32_t i = 0; i < sps_max_sub_layers_minus1; i++) |
| { |
| if (sub_layer_profile_present_flag[i]) |
| { |
| objBitReader.Read(2); // sub_layer_profile_space |
| objBitReader.Read(1); // sub_layer_tier_flag |
| objBitReader.Read(5); // sub_layer_profile_idc |
| objBitReader.Read(24); // sub_layer_profile_compatibility_flag |
| objBitReader.Read(8); // sub_layer_profile_compatibility_flag |
| objBitReader.Read(24); // sub_layer_constraint_indicator_flags |
| objBitReader.Read(24); // sub_layer_constraint_indicator_flags |
| } |
| |
| if (sub_layer_level_present_flag[i]) |
| { |
| objBitReader.Read(8); // sub_layer_level_idc |
| } |
| } |
| |
| /*-----------profile_tier_level done-----------------------*/ |
| objBitReader.ReadByUEMode(); // sps_seq_parameter_set_id |
| |
| uint32_t chroma_format_idc; |
| chroma_format_idc = objBitReader.ReadByUEMode(); |
| |
| IMLOGD1("[ParseHevcSpropParam] chroma_format_idc[%d]", chroma_format_idc); |
| |
| if (chroma_format_idc == 3) |
| { |
| objBitReader.Read(1); // separate_colour_plane_flag |
| } |
| |
| int32_t pic_width_in_luma_samples = objBitReader.ReadByUEMode(); |
| int32_t pic_height_in_luma_samples = objBitReader.ReadByUEMode(); |
| |
| IMLOGD1("[ParseHevcSpropParam] pic_width_in_luma_samples[%d]", pic_width_in_luma_samples); |
| |
| IMLOGD1("[ParseHevcSpropParam] pic_height_in_luma_samples[%d]", pic_height_in_luma_samples); |
| |
| pInfo->nWidth = pic_width_in_luma_samples; |
| pInfo->nHeight = pic_height_in_luma_samples; |
| |
| uint8_t conformance_window_flag = objBitReader.Read(1); |
| |
| IMLOGD1("[ParseHevcSpropParam] conformance_window_flag[%d]", conformance_window_flag); |
| |
| if (conformance_window_flag) |
| { |
| uint32_t conf_win_left_offset = objBitReader.ReadByUEMode(); |
| uint32_t conf_win_right_offset = objBitReader.ReadByUEMode(); |
| uint32_t conf_win_top_offset = objBitReader.ReadByUEMode(); |
| uint32_t conf_win_bottom_offset = objBitReader.ReadByUEMode(); |
| |
| pInfo->nWidth -= conf_win_left_offset + conf_win_right_offset; |
| pInfo->nHeight -= conf_win_top_offset + conf_win_bottom_offset; |
| |
| IMLOGD4("[ParseHevcSpropParam] frame_crop = [%u, %u, %u, %u]", conf_win_left_offset, |
| conf_win_right_offset, conf_win_top_offset, conf_win_bottom_offset); |
| } |
| |
| // Round down by 16 unit |
| uint32_t nRoundDownWidth = (pInfo->nWidth) / 16 * 16; |
| uint32_t nRoundDownHeight = (pInfo->nHeight) / 16 * 16; |
| |
| pInfo->nWidth = nRoundDownWidth; |
| pInfo->nHeight = nRoundDownHeight; |
| free(pszSpropparam); |
| return RESULT_SUCCESS; |
| } |
| |
| bool ImsMediaVideoUtil::ParseAvcSps(uint8_t* pbBuffer, uint32_t nBufferSize, tCodecConfig* pInfo) |
| { |
| ImsMediaBitReader bitreader; |
| uint32_t chroma_format_idc = 0; |
| uint8_t* pszSPS = reinterpret_cast<uint8_t*>(malloc(nBufferSize)); |
| |
| if (pszSPS == nullptr) |
| { |
| pInfo->nProfile = 0; |
| pInfo->nLevel = 0; |
| pInfo->nHeight = 0; |
| pInfo->nWidth = 0; |
| return false; |
| } |
| |
| memcpy(pszSPS, pbBuffer, nBufferSize); |
| |
| IMLOGD_PACKET1(IM_PACKET_LOG_VIDEO, "[ParseAvcSps] pszSPS[%02X]", pszSPS[0]); |
| |
| bitreader.SetBuffer(pszSPS, nBufferSize); |
| bitreader.Read(8); |
| bitreader.Read(8); |
| bitreader.Read(8); |
| bitreader.Read(8); |
| bitreader.Read(8); |
| |
| uint32_t Profile_idc = bitreader.Read(8); // Read profile_idc |
| // Read constraint |
| uint32_t constraint_set0 = bitreader.Read(1); |
| uint32_t constraint_set1 = bitreader.Read(1); |
| uint32_t constraint_set2 = bitreader.Read(1); |
| uint32_t constraint_set3 = bitreader.Read(1); |
| uint32_t constraint_set4 = bitreader.Read(1); |
| bitreader.Read(3); // Read reserved_zeor_3bits |
| uint32_t Level_idc = bitreader.Read(8); // Read level_idc |
| bitreader.ReadByUEMode(); // Read Seq_parameter_set_id |
| |
| if (Profile_idc == 100 || Profile_idc == 110 || Profile_idc == 122 || Profile_idc == 244 || |
| Profile_idc == 44 || Profile_idc == 83 || Profile_idc == 86 || Profile_idc == 118) |
| { |
| chroma_format_idc = bitreader.ReadByUEMode(); |
| |
| if (chroma_format_idc == 3) |
| { |
| bitreader.Read(1); // separate_colour_plane |
| } |
| |
| bitreader.ReadByUEMode(); // Read bit_depth_luma_minus8 |
| bitreader.ReadByUEMode(); // Read bit_depth_chroma_minus8 |
| bitreader.Read(1); // Read qpprime_y_zero_transform_bypass |
| |
| // Read seq_scaling_matrix_present |
| if (bitreader.Read(1)) |
| { |
| uint32_t i = ((chroma_format_idc != 3) ? 8 : 12); |
| // set scaling_list... not implement |
| bitreader.Read(i); |
| } |
| } |
| else if (Profile_idc == 183) |
| { |
| chroma_format_idc = 0; |
| } |
| else |
| { |
| chroma_format_idc = 1; // if chroma_format_idc not present in SPS it should be set to 1; |
| } |
| pInfo->nProfile = Profile_idc; |
| pInfo->nLevel = Level_idc; |
| |
| IMLOGD_PACKET5(IM_PACKET_LOG_VIDEO, "[ParseAvcSps] constraint_set[%d][%d][%d][%d][%d]", |
| constraint_set0, constraint_set1, constraint_set2, constraint_set3, constraint_set4); |
| IMLOGD_PACKET2(IM_PACKET_LOG_VIDEO, "[ParseAvcSps] Profile_idc[%d], Level_idc[%d]", Profile_idc, |
| Level_idc); |
| |
| bitreader.ReadByUEMode(); // Read log2_max_frame_num_minus4 |
| uint32_t pic_order_cnt_type = bitreader.ReadByUEMode(); // Read pic_order_cnt_type |
| |
| if (pic_order_cnt_type == 0) |
| { |
| bitreader.ReadByUEMode(); // Read log2_max_pic_order_cnt_lsb_minus4 |
| } |
| else if (pic_order_cnt_type == 1) |
| { |
| bitreader.Read(1); // Read delta_pic_order_always_zero_flag |
| bitreader.ReadByUEMode(); // Reda offset_for_non_ref_pic... |
| bitreader.ReadByUEMode(); // Read offset_for_top_to_bottom_field |
| uint32_t num_ref_frames_in_pic_order_cnt_cycle = bitreader.ReadByUEMode(); |
| |
| for (int32_t i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++) |
| { |
| bitreader.ReadByUEMode(); // Read offset_for_ref_frame[i]; |
| } |
| } |
| |
| bitreader.ReadByUEMode(); // Read max_num_ref_frames |
| bitreader.Read(1); // Read gaps_in_frame_num_value_allowed |
| |
| uint32_t pic_width_in_mbs_minus1 = bitreader.ReadByUEMode(); |
| uint32_t pic_height_in_map_units_minus1 = bitreader.ReadByUEMode(); |
| uint32_t frame_mbs_only = bitreader.Read(1); |
| |
| pInfo->nWidth = pic_width_in_mbs_minus1 * 16 + 16; |
| pInfo->nHeight = (2 - frame_mbs_only) * (pic_height_in_map_units_minus1 * 16 + 16); |
| |
| if (!frame_mbs_only) |
| { |
| bitreader.Read(1); // Read mb_adaptive_frame_field |
| } |
| |
| bitreader.Read(1); // Read direct_8x8_inferencce_flag |
| uint32_t frame_cropping = bitreader.Read(1); // Read frame_cropping_flag |
| |
| if (frame_cropping) |
| { |
| uint32_t frame_crop_left_offset = bitreader.ReadByUEMode(); |
| uint32_t frame_crop_right_offset = bitreader.ReadByUEMode(); |
| uint32_t frame_crop_top_offset = bitreader.ReadByUEMode(); |
| uint32_t frame_crop_bottom_offset = bitreader.ReadByUEMode(); |
| uint32_t cropX, cropY = 0; |
| if (chroma_format_idc == 0 /* monochrome */) |
| { |
| cropX = 1; |
| cropY = 2 - frame_mbs_only; |
| } |
| else |
| { |
| uint32_t subWidthC = (chroma_format_idc == 3) ? 1 : 2; |
| uint32_t subHeightC = (chroma_format_idc == 1) ? 2 : 1; |
| cropX = subWidthC; |
| cropY = subHeightC * (2 - frame_mbs_only); |
| } |
| |
| pInfo->nWidth -= (frame_crop_left_offset + frame_crop_right_offset) * cropX; |
| pInfo->nHeight -= (frame_crop_top_offset + frame_crop_bottom_offset) * cropY; |
| } |
| |
| free(pszSPS); |
| |
| IMLOGD_PACKET2(IM_PACKET_LOG_VIDEO, "[ParseAvcSps] width[%d],height[%d]", pInfo->nWidth, |
| pInfo->nHeight); |
| IMLOGD_PACKET2(IM_PACKET_LOG_VIDEO, "[ParseAvcSps] nProfile[%d],nLevel[%d]", pInfo->nProfile, |
| pInfo->nLevel); |
| return true; |
| } |
| |
| bool ImsMediaVideoUtil::ParseHevcSps(uint8_t* pbBuffer, uint32_t nBufferSize, tCodecConfig* pInfo) |
| { |
| if (pbBuffer == nullptr || nBufferSize == 0) |
| { |
| pInfo->nProfile = 0; |
| pInfo->nLevel = 0; |
| pInfo->nHeight = 0; |
| pInfo->nWidth = 0; |
| return false; |
| } |
| |
| uint32_t nOffset = 0; |
| |
| for (int32_t i = 0; i < nBufferSize - 6; i++) |
| { |
| // NAL unit header offset |
| if (pbBuffer[i] == 0x00 && pbBuffer[i + 1] == 0x00 && pbBuffer[i + 2] == 0x00 && |
| pbBuffer[i + 3] == 0x01 && pbBuffer[i + 4] == 0x42 && pbBuffer[i + 5] == 0x01) |
| { |
| nOffset = i + 6; |
| break; |
| } |
| } |
| |
| ImsMediaBitReader objBitReader; |
| objBitReader.SetBuffer(pbBuffer + nOffset, nBufferSize - nOffset); |
| objBitReader.Read(4); // sps_video_parameter_set_id; |
| uint32_t sps_max_sub_layers_minus1 = objBitReader.Read(3); |
| objBitReader.Read(1); // sps_temporal_id_nesting_flag; |
| |
| /*-----------profile_tier_level start-----------------------*/ |
| objBitReader.Read(3); // general_profile_spac, general_tier_flag |
| objBitReader.Read(5); // general_profile_idc |
| |
| // skip 13byte - flags, not handle |
| objBitReader.Read(24); |
| objBitReader.Read(24); |
| objBitReader.Read(24); |
| objBitReader.Read(24); |
| objBitReader.Read(8); |
| |
| objBitReader.Read(8); // general_level_idc |
| |
| uint8_t sub_layer_profile_present_flag[sps_max_sub_layers_minus1]; |
| uint8_t sub_layer_level_present_flag[sps_max_sub_layers_minus1]; |
| |
| for (int32_t i = 0; i < sps_max_sub_layers_minus1; i++) |
| { |
| sub_layer_profile_present_flag[i] = objBitReader.Read(1); |
| sub_layer_level_present_flag[i] = objBitReader.Read(1); |
| } |
| |
| if (sps_max_sub_layers_minus1 > 0) |
| { |
| for (int32_t j = sps_max_sub_layers_minus1; j < 8; j++) |
| { |
| objBitReader.Read(2); |
| } |
| } |
| |
| for (int32_t i = 0; i < sps_max_sub_layers_minus1; i++) |
| { |
| if (sub_layer_profile_present_flag[i]) |
| { |
| objBitReader.Read(2); // sub_layer_profile_space |
| objBitReader.Read(1); // sub_layer_tier_flag |
| objBitReader.Read(5); // sub_layer_profile_idc |
| objBitReader.Read(24); // sub_layer_profile_compatibility_flag |
| objBitReader.Read(8); // sub_layer_profile_compatibility_flag |
| objBitReader.Read(24); // sub_layer_constraint_indicator_flags |
| objBitReader.Read(24); // sub_layer_constraint_indicator_flags |
| } |
| |
| if (sub_layer_level_present_flag[i]) |
| { |
| objBitReader.Read(8); // sub_layer_level_idc |
| } |
| } |
| |
| objBitReader.ReadByUEMode(); // sps_seq_parameter_set_id |
| |
| uint32_t chroma_format_idc; |
| chroma_format_idc = objBitReader.ReadByUEMode(); |
| |
| if (chroma_format_idc == 3) |
| { |
| objBitReader.Read(1); // separate_colour_plane_flag |
| } |
| |
| int32_t pic_width_in_luma_samples = objBitReader.ReadByUEMode(); |
| int32_t pic_height_in_luma_samples = objBitReader.ReadByUEMode(); |
| |
| pInfo->nWidth = pic_width_in_luma_samples; |
| pInfo->nHeight = pic_height_in_luma_samples; |
| |
| uint8_t conformance_window_flag = objBitReader.Read(1); |
| |
| if (conformance_window_flag) |
| { |
| uint32_t conf_win_left_offset = objBitReader.ReadByUEMode(); |
| uint32_t conf_win_right_offset = objBitReader.ReadByUEMode(); |
| uint32_t conf_win_top_offset = objBitReader.ReadByUEMode(); |
| uint32_t conf_win_bottom_offset = objBitReader.ReadByUEMode(); |
| |
| pInfo->nWidth -= conf_win_left_offset + conf_win_right_offset; |
| pInfo->nHeight -= conf_win_top_offset + conf_win_bottom_offset; |
| } |
| |
| // Round down by 16 unit |
| uint32_t nRoundDownWidth = (pInfo->nWidth) / 16 * 16; |
| uint32_t nRoundDownHeight = (pInfo->nHeight) / 16 * 16; |
| |
| pInfo->nWidth = nRoundDownWidth; |
| pInfo->nHeight = nRoundDownHeight; |
| |
| return true; |
| } |
| |
| uint8_t* FindAvcStartCode(uint8_t* pData, uint32_t nDataSize, uint32_t* pnSkipSize) |
| { |
| uint8_t* pCurDataPos = pData; |
| uint32_t nSkipSize = 0; |
| |
| // Remove leading zero bytes and start code prefix |
| while (nDataSize >= START_CODE_PREFIX_LEN) |
| { |
| // Start Code 00 00 00 01 case. |
| if (pCurDataPos[0] == 0x00 && pCurDataPos[1] == 0x00 && pCurDataPos[2] == 0x00 && |
| pCurDataPos[3] == 0x01) |
| { |
| if (pnSkipSize) |
| *pnSkipSize = nSkipSize; |
| return pCurDataPos; |
| } |
| else |
| { |
| pCurDataPos += 1; |
| nDataSize -= 1; |
| nSkipSize += 1; |
| } |
| } |
| |
| if (pnSkipSize) |
| *pnSkipSize = nSkipSize; |
| return nullptr; |
| } |
| |
| char* ImsMediaVideoUtil::GenerateVideoSprop(VideoConfig* pVideoConfig) |
| { |
| if (pVideoConfig == nullptr) |
| { |
| IMLOGE0("[GenerateVideoSprop] pVideoConfig is null"); |
| return nullptr; |
| } |
| |
| // Configure Encoder |
| AMediaFormat* pFormat = AMediaFormat_new(); |
| AMediaFormat_setInt32(pFormat, AMEDIAFORMAT_KEY_WIDTH, pVideoConfig->getResolutionWidth()); |
| AMediaFormat_setInt32(pFormat, AMEDIAFORMAT_KEY_HEIGHT, pVideoConfig->getResolutionHeight()); |
| |
| char kMimeType[128] = {'\0'}; |
| sprintf(kMimeType, "video/avc"); |
| if (pVideoConfig->getCodecType() == VideoConfig::CODEC_HEVC) |
| { |
| sprintf(kMimeType, "video/hevc"); |
| } |
| |
| IMLOGD1("[GenerateVideoSprop] MimeType[%s]", kMimeType); |
| IMLOGD2("[GenerateVideoSprop] Resolution[%dx%d].", pVideoConfig->getResolutionWidth(), |
| pVideoConfig->getResolutionHeight()); |
| IMLOGD2("[GenerateVideoSprop] Profile[%d] level[%d]", pVideoConfig->getCodecProfile(), |
| pVideoConfig->getCodecLevel()); |
| IMLOGD2("[GenerateVideoSprop] Bitrate[%d], FrameRate[%d]", pVideoConfig->getBitrate(), |
| pVideoConfig->getFramerate()); |
| |
| AMediaFormat_setString(pFormat, AMEDIAFORMAT_KEY_MIME, kMimeType); |
| AMediaFormat_setInt32(pFormat, AMEDIAFORMAT_KEY_COLOR_FORMAT, |
| 0x00000015); // COLOR_FormatYUV420SemiPlanar |
| |
| AMediaFormat_setInt32(pFormat, AMEDIAFORMAT_KEY_BIT_RATE, pVideoConfig->getBitrate() * 1000); |
| AMediaFormat_setInt32(pFormat, AMEDIAFORMAT_KEY_PROFILE, pVideoConfig->getCodecProfile()); |
| AMediaFormat_setInt32(pFormat, AMEDIAFORMAT_KEY_LEVEL, pVideoConfig->getCodecLevel()); |
| AMediaFormat_setInt32(pFormat, AMEDIAFORMAT_KEY_BITRATE_MODE, 2); // #2 : BITRATE_MODE_CBR |
| AMediaFormat_setFloat(pFormat, AMEDIAFORMAT_KEY_FRAME_RATE, pVideoConfig->getFramerate()); |
| AMediaFormat_setInt32( |
| pFormat, AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, pVideoConfig->getIntraFrameInterval()); |
| AMediaFormat_setInt32(pFormat, AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, |
| pVideoConfig->getResolutionWidth() * pVideoConfig->getResolutionHeight() * 10); |
| |
| AMediaCodec* pCodec = AMediaCodec_createEncoderByType(kMimeType); |
| if (pCodec == nullptr) |
| { |
| IMLOGE1("[GenerateVideoSprop] Unable to create encoder. MimeType[%s]", kMimeType); |
| return nullptr; |
| } |
| |
| media_status_t err = AMediaCodec_configure( |
| pCodec, pFormat, nullptr, nullptr, AMEDIACODEC_CONFIGURE_FLAG_ENCODE); |
| if (err != AMEDIA_OK) |
| { |
| IMLOGE1("[GenerateVideoSprop] Encoder configure error[%d]", err); |
| AMediaCodec_delete(pCodec); |
| pCodec = nullptr; |
| AMediaFormat_delete(pFormat); |
| pFormat = nullptr; |
| return nullptr; |
| } |
| |
| err = AMediaCodec_start(pCodec); |
| if (err != AMEDIA_OK) |
| { |
| IMLOGE1("[GenerateVideoSprop] Encoder start Err[%d]", err); |
| AMediaCodec_delete(pCodec); |
| pCodec = nullptr; |
| AMediaFormat_delete(pFormat); |
| pFormat = nullptr; |
| return nullptr; |
| } |
| |
| bool bSpsRead = false, bPpsRead = false; |
| int8_t nMaxBufferReads = MAX_OUTPUT_BUFFER_READ_ATTEMPTS; |
| char* pSpropStr = reinterpret_cast<char*>(malloc(MAX_CONFIG_LEN)); |
| pSpropStr[0] = '\0'; |
| |
| while (!bSpsRead || !bPpsRead) |
| { |
| // Get output buffer |
| AMediaCodecBufferInfo info; |
| auto index = AMediaCodec_dequeueOutputBuffer(pCodec, &info, 100000); |
| IMLOGD2("[GenerateVideoSprop] dequeueOutputBuffer returned index[%d] frameSize[%d]", index, |
| info.size); |
| if (index >= 0 && info.size > 0) |
| { |
| size_t buffCapacity = 0; |
| uint8_t* buf = AMediaCodec_getOutputBuffer(pCodec, index, &buffCapacity); |
| IMLOGD1("[GenerateVideoSprop] getOutputBuffer returned buffCapacity[%d]", buffCapacity); |
| |
| buf += info.offset; |
| buffCapacity = info.size; |
| |
| while (buf != nullptr && buffCapacity > 0) |
| { |
| uint32_t skipLen = 0; |
| uint8_t* tempBufPtr = FindAvcStartCode(buf, buffCapacity, &skipLen); |
| if (tempBufPtr != nullptr) |
| { |
| buf = tempBufPtr; |
| buffCapacity -= skipLen; |
| } |
| |
| if (buf == nullptr || buffCapacity < START_CODE_PREFIX_LEN) |
| continue; |
| |
| // Remove start sequence |
| buf += START_CODE_PREFIX_LEN; |
| buffCapacity -= START_CODE_PREFIX_LEN; |
| |
| uint8_t frameType = buf[0] & 0x1F; |
| |
| // Extract frame |
| int frameLen = buffCapacity; |
| tempBufPtr = FindAvcStartCode(buf, buffCapacity, &skipLen); |
| if (tempBufPtr != nullptr) |
| { |
| frameLen = tempBufPtr - buf; |
| } |
| |
| int16_t SpropStrlen = strlen(pSpropStr); |
| bool ret = ImsMediaBinaryFormat::BinaryToBase00(pSpropStr + SpropStrlen, |
| MAX_CONFIG_LEN, buf, frameLen, BINARY_FORMAT_BASE64); |
| if (ret == false) |
| { |
| IMLOGE0("[GenerateVideoSprop] BinaryToBase64 failed"); |
| free(pSpropStr); |
| pSpropStr = nullptr; |
| AMediaCodec_releaseOutputBuffer(pCodec, index, false); |
| goto JP_Exit_GenerateSprop; |
| } |
| |
| if (frameType == FRAME_TYPE_SPS) // Check if SPS |
| { |
| strlcat(pSpropStr, ",", MAX_CONFIG_LEN); |
| bSpsRead = true; |
| } |
| else if (frameType == FRAME_TYPE_PPS) // Check if PPS |
| { |
| bPpsRead = true; |
| } |
| |
| buffCapacity -= frameLen; |
| buf += frameLen; |
| } |
| |
| AMediaCodec_releaseOutputBuffer(pCodec, index, false); |
| } |
| else |
| { |
| IMLOGE2("[GenerateVideoSprop] dequeueOutputBuffer returned invalid index[%d] " |
| "info.size[%d]", |
| index, info.size); |
| } |
| |
| if (--nMaxBufferReads <= 0) |
| { |
| free(pSpropStr); |
| pSpropStr = nullptr; |
| goto JP_Exit_GenerateSprop; |
| } |
| } |
| |
| JP_Exit_GenerateSprop: |
| // Stop Encoder |
| if (pCodec != nullptr) |
| { |
| IMLOGD0("[GenerateVideoSprop] Stop encoder"); |
| AMediaCodec_signalEndOfInputStream(pCodec); |
| AMediaCodec_stop(pCodec); |
| AMediaCodec_delete(pCodec); |
| } |
| |
| if (pFormat != nullptr) |
| { |
| AMediaFormat_delete(pFormat); |
| } |
| |
| IMLOGD1("[GenerateVideoSprop] Returning sprop[%s]", pSpropStr); |
| return pSpropStr; |
| } |
| |
| void ImsMediaVideoUtil::ConvertBitrateToPower( |
| const uint32_t nInputBitrate, uint32_t& nOutExp, uint32_t& nOutMantissa) |
| { |
| if (nInputBitrate == 0) |
| { |
| return; |
| } |
| |
| nOutExp = 0; |
| nOutMantissa = (nInputBitrate % 2 == 0) ? (nInputBitrate) : (nInputBitrate + 1); |
| |
| while (nOutMantissa % 2 == 0) |
| { |
| nOutExp++; |
| nOutMantissa = nOutMantissa >> 1; |
| |
| if (nOutMantissa >= 0x1ffff) |
| { |
| nOutMantissa = (nOutMantissa % 2 == 0) ? (nOutMantissa) : (nOutMantissa + 1); |
| } |
| } |
| } |