/** * 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 #include #include #include #include #include #include #include #include #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(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(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(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(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(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); } } }