| /** |
| * 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 <AudioRtpPayloadEncoderNode.h> |
| #include <ImsMediaAudioUtil.h> |
| #include <ImsMediaTrace.h> |
| #include <AudioConfig.h> |
| #include <EvsParams.h> |
| |
| AudioRtpPayloadEncoderNode::AudioRtpPayloadEncoderNode(BaseSessionCallback* callback) : |
| BaseNode(callback) |
| { |
| mPtime = 0; |
| mFirstFrame = false; |
| mMaxNumOfFrame = 0; |
| mCurrNumOfFrame = 0; |
| mCurrFramePos = 0; |
| mTotalPayloadSize = 0; |
| } |
| |
| AudioRtpPayloadEncoderNode::~AudioRtpPayloadEncoderNode() {} |
| |
| kBaseNodeId AudioRtpPayloadEncoderNode::GetNodeId() |
| { |
| return kNodeIdAudioPayloadEncoder; |
| } |
| |
| ImsMediaResult AudioRtpPayloadEncoderNode::Start() |
| { |
| IMLOGD2("[Start] codecType[%d], mode[%d]", mCodecType, mOctetAligned); |
| std::lock_guard<std::mutex> guard(mMutexExit); |
| mMaxNumOfFrame = mPtime / 20; |
| mEvsMode = (kEvsBitrate)ImsMediaAudioUtil::GetMaximumEvsMode(mCoreEvsMode); |
| mEvsCodecMode = (kEvsCodecMode)ImsMediaAudioUtil::ConvertEvsCodecMode(mEvsMode); |
| mEvsBitRate = ImsMediaAudioUtil::ConvertEVSModeToBitRate(mEvsMode); |
| |
| if (mMaxNumOfFrame == 0 || mMaxNumOfFrame > MAX_FRAME_IN_PACKET) |
| { |
| IMLOGE1("[Start] Invalid ptime [%d]", mPtime); |
| return RESULT_INVALID_PARAM; |
| } |
| |
| mCurrNumOfFrame = 0; |
| mCurrFramePos = 0; |
| mFirstFrame = true; |
| mTotalPayloadSize = 0; |
| mNodeState = kNodeStateRunning; |
| return RESULT_SUCCESS; |
| } |
| |
| void AudioRtpPayloadEncoderNode::Stop() |
| { |
| IMLOGD0("[Stop]"); |
| std::lock_guard<std::mutex> guard(mMutexExit); |
| mNodeState = kNodeStateStopped; |
| } |
| |
| bool AudioRtpPayloadEncoderNode::IsRunTime() |
| { |
| return true; |
| } |
| |
| bool AudioRtpPayloadEncoderNode::IsSourceNode() |
| { |
| return false; |
| } |
| |
| void AudioRtpPayloadEncoderNode::OnDataFromFrontNode(ImsMediaSubType subtype, uint8_t* pData, |
| uint32_t nDataSize, uint32_t nTimestamp, bool bMark, uint32_t nSeqNum, |
| ImsMediaSubType nDataType) |
| { |
| (void)subtype; |
| switch (mCodecType) |
| { |
| case kAudioCodecAmr: |
| case kAudioCodecAmrWb: |
| EncodePayloadAmr(pData, nDataSize, nTimestamp, bMark); |
| break; |
| case kAudioCodecPcmu: |
| case kAudioCodecPcma: |
| SendDataToRearNode(MEDIASUBTYPE_RTPPAYLOAD, pData, nDataSize, nTimestamp, bMark, |
| nSeqNum, nDataType); |
| break; |
| case kAudioCodecEvs: |
| EncodePayloadEvs(pData, nDataSize, nTimestamp); |
| break; |
| default: |
| IMLOGE1("[OnDataFromFrontNode] invalid codec type[%d]", mCodecType); |
| SendDataToRearNode(MEDIASUBTYPE_RTPPAYLOAD, pData, nDataSize, nTimestamp, bMark, |
| nSeqNum, nDataType); |
| break; |
| } |
| } |
| |
| void AudioRtpPayloadEncoderNode::SetConfig(void* config) |
| { |
| AudioConfig* pConfig = reinterpret_cast<AudioConfig*>(config); |
| if (pConfig != NULL) |
| { |
| mCodecType = ImsMediaAudioUtil::ConvertCodecType(pConfig->getCodecType()); |
| if (mCodecType == kAudioCodecAmr || mCodecType == kAudioCodecAmrWb) |
| { |
| mOctetAligned = pConfig->getAmrParams().getOctetAligned(); |
| } |
| else if (mCodecType == kAudioCodecEvs) |
| { |
| mEvsBandwidth = (kEvsBandwidth)pConfig->getEvsParams().getEvsBandwidth(); |
| mEvsPayloadHeaderMode = |
| (kRtpPyaloadHeaderMode)pConfig->getEvsParams().getUseHeaderFullOnly(); |
| mCoreEvsMode = pConfig->getEvsParams().getEvsMode(); |
| mEvsOffset = pConfig->getEvsParams().getChannelAwareMode(); |
| mSendCMR = pConfig->getCodecModeRequest(); |
| } |
| |
| mPtime = pConfig->getPtimeMillis(); |
| } |
| } |
| |
| bool AudioRtpPayloadEncoderNode::IsSameConfig(void* config) |
| { |
| if (config == NULL) |
| return true; |
| AudioConfig* pConfig = reinterpret_cast<AudioConfig*>(config); |
| |
| if (mCodecType == ImsMediaAudioUtil::ConvertCodecType(pConfig->getCodecType())) |
| { |
| if (mCodecType == kAudioCodecAmr || mCodecType == kAudioCodecAmrWb) |
| { |
| return (mOctetAligned == pConfig->getAmrParams().getOctetAligned()); |
| } |
| else if (mCodecType == kAudioCodecEvs) |
| { |
| return (mEvsBandwidth == (kEvsBandwidth)pConfig->getEvsParams().getEvsBandwidth() && |
| mEvsPayloadHeaderMode == |
| (kRtpPyaloadHeaderMode)pConfig->getEvsParams().getUseHeaderFullOnly() && |
| mCoreEvsMode == |
| ImsMediaAudioUtil::GetMaximumEvsMode( |
| pConfig->getEvsParams().getEvsMode()) && |
| mEvsOffset == pConfig->getEvsParams().getChannelAwareMode()); |
| } |
| } |
| |
| return false; |
| } |
| |
| void AudioRtpPayloadEncoderNode::EncodePayloadAmr( |
| uint8_t* pData, uint32_t nDataSize, uint32_t nTimestamp, bool bMark) |
| { |
| (void)bMark; |
| uint32_t nCmr = 15; |
| uint32_t f, ft, q, nDataBitSize; |
| uint32_t nTotalSize; |
| std::lock_guard<std::mutex> guard(mMutexExit); |
| |
| #ifndef LEGACY_AUDIO_ENABLED // for ap audio test |
| pData++; |
| nDataSize -= 1; |
| #endif |
| if (nDataSize > 4) |
| { |
| IMLOGD_PACKET5(IM_PACKET_LOG_PH, "[EncodePayloadAmr] src = %02X %02X %02X %02X, len[%d]", |
| pData[0], pData[1], pData[2], pData[3], nDataSize); |
| } |
| |
| IMLOGD_PACKET2(IM_PACKET_LOG_PH, "[EncodePayloadAmr] codectype[%d], octetAligned[%d]", |
| mCodecType, mOctetAligned); |
| |
| mCurrNumOfFrame++; |
| f = (mCurrNumOfFrame == mMaxNumOfFrame) ? 0 : 1; |
| |
| if (mCodecType == kAudioCodecAmr) |
| { |
| nCmr = 0x0F; |
| ft = ImsMediaAudioUtil::ConvertLenToAmrMode(nDataSize); |
| nDataBitSize = ImsMediaAudioUtil::ConvertAmrModeToBitLen(ft); |
| } |
| else |
| { |
| nCmr = 0x0F; |
| ft = ImsMediaAudioUtil::ConvertLenToAmrWbMode(nDataSize); |
| nDataBitSize = ImsMediaAudioUtil::ConvertAmrWbModeToBitLen(ft); |
| } |
| |
| q = 1; |
| #ifdef USE_CMR_TEST |
| // generate cmr |
| static int32_t sAMRCount = 0; |
| static int32_t bAMRUpdate = 0; |
| static int32_t nAMRNextMode = 7; |
| static int32_t nAMRInc = -1; |
| sAMRCount++; |
| |
| if ((sAMRCount & 0x7F) == 0) |
| { |
| bAMRUpdate = 1; |
| } |
| |
| nCmr = nAMRNextMode; |
| |
| if (bAMRUpdate == 1 && nDataSize > 0) |
| { |
| int32_t max_mode; |
| |
| if (mCodecType == kAudioCodecAmr) |
| { |
| max_mode = 7; |
| } |
| else |
| { |
| max_mode = 8; |
| } |
| |
| nAMRNextMode += nAMRInc; |
| |
| if (nAMRNextMode < 0) |
| { |
| nAMRNextMode = 1; |
| nAMRInc = 1; |
| } |
| else if (nAMRNextMode > max_mode) |
| { |
| nAMRNextMode = max_mode - 1; |
| nAMRInc = -1; |
| } |
| |
| bAMRUpdate = 0; |
| } |
| #endif |
| // the first paylaod |
| if (mCurrNumOfFrame == 1) |
| { |
| memset(mPayload, 0, MAX_AUDIO_PAYLOAD_SIZE); |
| mBWHeader.SetBuffer(mPayload, MAX_AUDIO_PAYLOAD_SIZE); |
| mBWPayload.SetBuffer(mPayload, MAX_AUDIO_PAYLOAD_SIZE); |
| mBWHeader.Write(nCmr, 4); |
| |
| if (mOctetAligned == true) |
| { |
| mBWHeader.Write(0, 4); |
| mBWPayload.Seek(8 + mMaxNumOfFrame * 8); |
| } |
| else |
| { |
| mBWPayload.Seek(4 + mMaxNumOfFrame * 6); |
| } |
| |
| mTimestamp = nTimestamp; |
| } |
| |
| // Payload ToC |
| mBWHeader.Write(f, 1); |
| mBWHeader.Write(ft, 4); |
| mBWHeader.Write(q, 1); |
| |
| if (mOctetAligned == true) |
| { |
| mBWHeader.AddPadding(); |
| } |
| |
| IMLOGD_PACKET2(IM_PACKET_LOG_PH, "[EncodePayloadAmr] nDataBitSize[%d], nDataSize[%d]", |
| nDataBitSize, nDataSize); |
| |
| // Speech Frame |
| mBWPayload.WriteByteBuffer(pData, nDataBitSize); |
| |
| if (mOctetAligned == true) |
| { |
| mBWPayload.AddPadding(); |
| } |
| |
| mTotalPayloadSize += nDataSize; |
| |
| if (mCurrNumOfFrame == mMaxNumOfFrame) |
| { |
| mBWHeader.Flush(); |
| mBWPayload.AddPadding(); |
| mBWPayload.Flush(); |
| nTotalSize = mBWPayload.GetBufferSize(); |
| |
| IMLOGD_PACKET7(IM_PACKET_LOG_PH, |
| "[EncodePayloadAmr] result = %02X %02X %02X %02X %02X %02X, len[%d]", mPayload[0], |
| mPayload[1], mPayload[2], mPayload[3], mPayload[4], mPayload[5], nTotalSize); |
| |
| if (mTotalPayloadSize > 0) |
| { |
| SendDataToRearNode( |
| MEDIASUBTYPE_RTPPAYLOAD, mPayload, nTotalSize, mTimestamp, mFirstFrame, 0); |
| } |
| |
| mCurrNumOfFrame = 0; |
| mTotalPayloadSize = 0; |
| |
| if (mFirstFrame) |
| { |
| mFirstFrame = false; |
| } |
| } |
| } |
| |
| void AudioRtpPayloadEncoderNode::EncodePayloadEvs( |
| uint8_t* pData, uint32_t nDataSize, uint32_t nTimeStamp) |
| { |
| kRtpPyaloadHeaderMode eEVSPayloadFormat = kRtpPyaloadHeaderModeEvsHeaderFull; |
| kEvsCodecMode kEvsCodecMode; |
| |
| // Converting bits to bytes. |
| nDataSize /= 8; |
| |
| // 0111 1111 is no request. |
| uint32_t nEVSBW = 0x07; |
| uint32_t nEVSBR = 0x0f; |
| |
| uint32_t nFrameType = 0; |
| uint32_t nDataBitSize = 0; // bit unit |
| uint32_t nTotalSize = 0; // byte unit |
| |
| std::lock_guard<std::mutex> guard(mMutexExit); |
| if (nDataSize == 0) |
| return; // don't send 'NO DATA' packet |
| |
| eEVSPayloadFormat = mEvsPayloadHeaderMode; |
| // compact or header-full format, default is compact formats |
| kEvsCodecMode = mEvsCodecMode; |
| |
| // primary or amr-wb io mode, default is primary mode |
| // primary or amr-wb io mode base on frameSize. |
| // kEvsCodecMode = ImsMediaAudioUtil::CheckEVSCodecMode(nDataSize); |
| mCurrNumOfFrame++; |
| |
| if (eEVSPayloadFormat == kRtpPyaloadHeaderModeEvsCompact) |
| { |
| memset(mPayload, 0, MAX_AUDIO_PAYLOAD_SIZE); |
| mBWHeader.SetBuffer(mPayload, MAX_AUDIO_PAYLOAD_SIZE); |
| mBWPayload.SetBuffer(mPayload, MAX_AUDIO_PAYLOAD_SIZE); |
| |
| mTimestamp = nTimeStamp; |
| // exactly one coded frame without any additional EVS RTP payload header |
| if (kEvsCodecMode == kEvsCodecModePrimary) |
| { |
| // calculate nDataBitSize from nDataSize |
| nFrameType = (uint32_t)ImsMediaAudioUtil::ConvertLenToEVSAudioMode(nDataSize); |
| nDataBitSize = |
| ImsMediaAudioUtil::ConvertEVSAudioModeToBitLen((kImsAudioEvsMode)nFrameType); |
| |
| if (nDataBitSize == 0) |
| { |
| return; |
| } |
| |
| // special case, EVS Primary 2.8 kbps frame in Compact format |
| if (nFrameType == 0) |
| { |
| // First data bit d(0) of the EVS Primary 2.8 kbps is always set to '0' |
| pData[0] = pData[0] & 0x7f; |
| } |
| |
| // write speech Frame |
| mBWPayload.WriteByteBuffer(pData, nDataBitSize); |
| mTotalPayloadSize += nDataSize; |
| |
| mBWHeader.AddPadding(); |
| mBWHeader.Flush(); |
| |
| mBWPayload.AddPadding(); |
| mBWPayload.Flush(); |
| |
| nTotalSize = mBWPayload.GetBufferSize(); |
| |
| IMLOGD_PACKET7(IM_PACKET_LOG_PH, "[EncodePayloadEvs] result =\ |
| %02X %02X %02X %02X %02X %02X, len[%d]", |
| mPayload[0], mPayload[1], mPayload[2], mPayload[3], mPayload[4], mPayload[5], |
| nTotalSize); |
| |
| if (mTotalPayloadSize > 0) |
| { |
| SendDataToRearNode( |
| MEDIASUBTYPE_RTPPAYLOAD, mPayload, nTotalSize, mTimestamp, mFirstFrame, 0); |
| } |
| |
| mCurrNumOfFrame = 0; |
| mTotalPayloadSize = 0; |
| if (mFirstFrame) |
| mFirstFrame = false; |
| } |
| // one 3-bit CMR field, one coded frame, and zero-padding bits if necessary |
| else if (kEvsCodecMode == kEvsCodecModeAmrIo) |
| { |
| IMLOGE0("[EncodePayloadEvs] COMPACT and AMR_WB_IO"); |
| // calculate nDataBitSize from nDataSize |
| nFrameType = (uint32_t)ImsMediaAudioUtil::ConvertLenToAmrWbMode(nDataSize); |
| nDataBitSize = ImsMediaAudioUtil::ConvertAmrWbModeToBitLen(nFrameType); |
| |
| // 0: 6.6, 1: 8.85, 2: 12.65, 3: 15.85, 4: 18.25, 5: 23.05, 6: 23.85, 7: none |
| // 0111(7) is no request. |
| uint32_t nCmr = 0x07; |
| |
| // write CMR except SID |
| // at EVS AMR WB IO Mode, SID packet does not include cmr field...and no processing |
| if (nFrameType != kImsAudioAmrWbModeSID) |
| { |
| #ifdef USE_CMR_TEST |
| uint32_t cmr_dummy = 0; |
| EVSCMRGeneratorForTest(nDataSize); |
| #endif |
| if (mSendCMR) |
| { |
| nCmr = GenerateCMRForEVS(eEVSPayloadFormat); |
| } |
| |
| mBWHeader.Write(nCmr, 3); |
| mBWPayload.Seek(3); |
| |
| // append a speech data bit(0) after the last speech data bit |
| uint8_t nDataBit0 = 0; |
| uint32_t i = 0; |
| uint32_t remain = 0; |
| |
| nDataBit0 = pData[0] >> 7; |
| for (i = 0; i < (nDataSize - 1); i++) |
| { |
| pData[i] = pData[i] << 1; |
| pData[i] = pData[i] + (pData[i + 1] >> 7); |
| } |
| |
| // set the last speech data byte |
| remain = nDataBitSize % 8; |
| if (remain == 0) |
| remain = 8; |
| pData[nDataSize - 1] = pData[nDataSize - 1] << 1; |
| nDataBit0 = nDataBit0 << (8 - remain); |
| pData[nDataSize - 1] = pData[nDataSize - 1] + nDataBit0; |
| } |
| else // kImsAudioAmrWbModeSID case |
| { |
| // EVS amr io mode's SID is used HF format. |
| // set cmr |
| if (mSendCMR) |
| { |
| nCmr = GenerateCMRForEVS(kRtpPyaloadHeaderModeEvsHeaderFull); |
| } |
| else |
| { |
| nCmr = 0xff; // no request - 0xff |
| } |
| |
| mBWHeader.Write(nCmr, 8); |
| mBWPayload.Seek(8); |
| |
| // set ToC |
| // Header Type identification bit(1bit) - always set to 0 |
| uint32_t toc_h = 0; |
| // (1bit - always set to 0 in compact AMR WB IO mode) |
| uint32_t toc_f = 0; |
| // 1 1 1001 - EVS AMR IO Mode, Q bit set 1, 1001 indicate SID packet |
| uint32_t ft = 0x39; |
| |
| mBWHeader.Write(toc_h, 1); |
| mBWHeader.Write(toc_f, 1); |
| mBWHeader.Write(ft, 6); |
| mBWPayload.Seek(8); |
| } |
| |
| // write speech Frame |
| mBWPayload.WriteByteBuffer(pData, nDataBitSize); |
| mTotalPayloadSize += nDataSize; |
| |
| mBWHeader.Flush(); |
| |
| mBWPayload.AddPadding(); |
| mBWPayload.Flush(); |
| |
| nTotalSize = mBWPayload.GetBufferSize(); |
| |
| IMLOGD_PACKET7(IM_PACKET_LOG_PH, |
| "[EncodePayloadEvs] Result = %02X %02X %02X %02X %02X %02X, len[%d]", |
| mPayload[0], mPayload[1], mPayload[2], mPayload[3], mPayload[4], mPayload[5], |
| nTotalSize); |
| |
| if (mTotalPayloadSize > 0) |
| { |
| SendDataToRearNode( |
| MEDIASUBTYPE_RTPPAYLOAD, mPayload, nTotalSize, mTimestamp, mFirstFrame, 0); |
| } |
| |
| mCurrNumOfFrame = 0; |
| mTotalPayloadSize = 0; |
| if (mFirstFrame) |
| mFirstFrame = false; |
| } |
| else |
| { |
| IMLOGE0("[EncodePayloadEvs] Invalid codec mode"); |
| return; |
| } |
| } |
| else if (eEVSPayloadFormat == kRtpPyaloadHeaderModeEvsHeaderFull) |
| { |
| uint8_t nCmr = 0; |
| |
| uint32_t m_nCMR = 0; // CMR value |
| m_nCMR = mSendCMR; |
| |
| uint32_t cmr_h, cmr_t, cmr_d = 0; // CMR byte |
| // ToC byte |
| uint32_t toc_h, toc_f = 0; |
| uint32_t toc_ft_m, toc_ft_q, toc_ft_b = 0; // ToC frame type index |
| |
| memset(mPayload, 0, MAX_AUDIO_PAYLOAD_SIZE); |
| mBWHeader.SetBuffer(mPayload, MAX_AUDIO_PAYLOAD_SIZE); |
| mBWPayload.SetBuffer(mPayload, MAX_AUDIO_PAYLOAD_SIZE); |
| |
| if (kEvsCodecMode == kEvsCodecModePrimary) |
| { |
| IMLOGE0("[EncodePayloadEvs] HF and PRI"); |
| if (nFrameType == kImsAudioEvsPrimaryModeSID || m_nCMR == 1) // CMR value |
| { |
| // Header Type identification bit(1bit) - always set to 1 |
| cmr_h = 1; |
| // Type of Request(3bits) - NB(000), IO(001), FB(100), WB(101), SWB(110) |
| cmr_t = nEVSBW; |
| // codec mode request(4bits) |
| cmr_d = nEVSBR; |
| } |
| |
| // set ToC byte |
| toc_h = 0; // Header Type identification bit(1bit) - always set to 0 |
| toc_f = (mCurrNumOfFrame == mMaxNumOfFrame) ? 0 : 1; // (1bit) |
| toc_ft_m = 0; // EVS mode(1bit), Primary mode is 0 |
| toc_ft_q = 0; // Q bit(1bit) - zero for kEvsCodecModePrimary |
| toc_ft_b = |
| ImsMediaAudioUtil::ConvertLenToEVSAudioMode(nDataSize); // EVS bit rate(4bits) |
| nDataBitSize = |
| ImsMediaAudioUtil::ConvertEVSAudioModeToBitLen((kImsAudioEvsMode)toc_ft_b); |
| |
| // write CMR and seek the position of the first paylaod |
| if (mCurrNumOfFrame == 1) |
| { |
| // set CMR byte - it's optional field... |
| if (mSendCMR) |
| { |
| nCmr = GenerateCMRForEVS(eEVSPayloadFormat); |
| mBWHeader.Write(nCmr, 8); |
| mBWPayload.Seek(8); |
| } |
| else if (nFrameType == kImsAudioEvsPrimaryModeSID || m_nCMR == 1) |
| { |
| // check writing CMR or not |
| // write CMR byte |
| mBWHeader.Write(cmr_h, 1); |
| mBWHeader.Write(cmr_t, 3); |
| mBWHeader.Write(cmr_d, 4); |
| |
| mBWPayload.Seek(8); |
| } |
| |
| // ToC field. |
| mBWPayload.Seek(mMaxNumOfFrame * 8); // jump ToC bytes |
| mTimestamp = nTimeStamp; // set timestamp as the first frame |
| } |
| |
| // write ToC |
| mBWHeader.Write(toc_h, 1); |
| mBWHeader.Write(toc_f, 1); |
| mBWHeader.Write(toc_ft_m, 1); |
| mBWHeader.Write(toc_ft_q, 1); |
| mBWHeader.Write(toc_ft_b, 4); |
| |
| // write Speech Frame |
| mBWPayload.WriteByteBuffer(pData, nDataBitSize); |
| mBWPayload.AddPadding(); |
| |
| mTotalPayloadSize += nDataSize; |
| |
| if (mCurrNumOfFrame == mMaxNumOfFrame) |
| { |
| // mBWHeader.AddPadding(); |
| mBWHeader.Flush(); |
| |
| mBWPayload.AddPadding(); |
| mBWPayload.Flush(); |
| |
| nTotalSize = mBWPayload.GetBufferSize(); |
| |
| IMLOGD_PACKET7(IM_PACKET_LOG_PH, |
| "[EncodePayloadEvs] Result = %02X %02X %02X %02X %02X %02X, len[%d]", |
| mPayload[0], mPayload[1], mPayload[2], mPayload[3], mPayload[4], |
| mPayload[5], nTotalSize); |
| |
| if (mTotalPayloadSize > 0) |
| { |
| nTotalSize = CheckPaddingNecessity(nTotalSize); |
| |
| SendDataToRearNode(MEDIASUBTYPE_RTPPAYLOAD, mPayload, nTotalSize, mTimestamp, |
| mFirstFrame, 0); |
| } |
| |
| mCurrNumOfFrame = 0; |
| mTotalPayloadSize = 0; |
| if (mFirstFrame) |
| mFirstFrame = false; |
| } |
| } |
| else if (kEvsCodecMode == kEvsCodecModeAmrIo) |
| { |
| IMLOGE0("[EncodePayloadEvs] HF and AMR_WB_IO"); |
| // set CMR byte |
| // at EVS AMR WB IO Mode, CMR field shall include. |
| #ifdef USE_CMR_TEST |
| EVSCMRGeneratorForTest(nDataSize); |
| #endif |
| uint32_t cmr_h, cmr_t, cmr_d; |
| // Header Type identification bit(1bit) - always set to 1 |
| cmr_h = 1; |
| /* Type of Request(3bits) - NB(000), IO(001), WB(010), SWB(011), FB(100), WB 13.2 |
| * channel-aware(101), SWB 13.2 channel-aware(110), reserved(111) */ |
| cmr_t = nEVSBW; |
| // codec mode request(4bits) 1111 is no request. |
| cmr_d = nEVSBR; |
| |
| // set ToC byte |
| // Header Type identification bit(1bit) - always set to 0 |
| toc_h = 0; |
| // (1bit) |
| toc_f = (mCurrNumOfFrame == mMaxNumOfFrame) ? 0 : 1; |
| // EVS mode(1bit), AMR-WB IO mode is 1 |
| toc_ft_m = 1; |
| // Q bit(1bit) - 1 for AMR_WB_IO |
| // for ORG EVS to avoid the issue -#EURAVOLTE-567 |
| toc_ft_q = 1; |
| // EVS AMR WB IO bit rate(4bits) |
| toc_ft_b = (uint32_t)ImsMediaAudioUtil::ConvertLenToAmrWbMode(nDataSize); |
| nDataBitSize = ImsMediaAudioUtil::ConvertAmrWbModeToBitLen(toc_ft_b); |
| |
| // write CMR and seek the position of the first paylaod |
| if (mCurrNumOfFrame == 1) |
| { |
| // write CMR byte |
| mBWHeader.Write(cmr_h, 1); |
| mBWHeader.Write(cmr_t, 3); |
| mBWHeader.Write(cmr_d, 4); |
| |
| // seek the position of the first paylaod |
| // add speech data after CMR and ToC |
| mBWPayload.Seek(8 + mMaxNumOfFrame * 8); |
| |
| mTimestamp = nTimeStamp; // set timestamp as the first frame |
| } |
| |
| // write ToC |
| mBWHeader.Write(toc_h, 1); |
| mBWHeader.Write(toc_f, 1); |
| mBWHeader.Write(toc_ft_m, 1); |
| mBWHeader.Write(toc_ft_q, 1); |
| mBWHeader.Write(toc_ft_b, 4); |
| |
| // write Speech Frame |
| mBWPayload.WriteByteBuffer(pData, nDataBitSize); |
| mBWPayload.AddPadding(); |
| |
| mTotalPayloadSize += nDataSize; |
| |
| if (mCurrNumOfFrame == mMaxNumOfFrame) |
| { |
| // mBWHeader.AddPadding(); |
| mBWHeader.Flush(); |
| |
| mBWPayload.AddPadding(); |
| mBWPayload.Flush(); |
| |
| nTotalSize = mBWPayload.GetBufferSize(); |
| |
| IMLOGD_PACKET7(IM_PACKET_LOG_PH, |
| "[EncodePayloadEvs] result = %02X %02X %02X %02X %02X %02X, len[%d]", |
| mPayload[0], mPayload[1], mPayload[2], mPayload[3], mPayload[4], |
| mPayload[5], nTotalSize); |
| |
| if (mTotalPayloadSize > 0) |
| { |
| nTotalSize = CheckPaddingNecessity(nTotalSize); |
| |
| SendDataToRearNode(MEDIASUBTYPE_RTPPAYLOAD, mPayload, nTotalSize, mTimestamp, |
| mFirstFrame, 0); |
| } |
| |
| mCurrNumOfFrame = 0; |
| mTotalPayloadSize = 0; |
| if (mFirstFrame) |
| mFirstFrame = false; |
| } |
| } |
| else |
| { |
| IMLOGE0("[EncodePayloadEvs] invalid codec mode"); |
| return; |
| } |
| |
| } // end of if(eEVSPayloadFormat == kRtpPyaloadHeaderModeEvsHeaderFull) |
| else |
| { |
| IMLOGE0("[EncodePayloadEvs] invalid payload format"); |
| return; |
| } |
| |
| return; |
| } |
| |
| uint8_t AudioRtpPayloadEncoderNode::GenerateCMRForEVS(kRtpPyaloadHeaderMode eEVSPayloadFormat) |
| { |
| uint8_t nCmrT = 0; |
| uint8_t nCmrD = 0; |
| uint8_t nCmr = 0; |
| |
| uint32_t nOriginBR = ImsMediaAudioUtil::FindMaxEVSBitrate(mEvsBitRate, mEvsCodecMode); |
| uint32_t nOriginBW = ImsMediaAudioUtil::FindMaxEVSBandwidth(mEvsBandwidth); |
| |
| if (eEVSPayloadFormat == kRtpPyaloadHeaderModeEvsCompact) |
| { |
| // 0: 6.6, 1: 8.85, 2: 12.65, 3: 15.85, 4: 18.25, 5: 23.05, 6: 23.85, 7: none |
| nCmr = nOriginBR; |
| } |
| else |
| { |
| if (nOriginBR == kEvsPrimaryModeBitrate01320) // channel aware mode |
| { |
| nCmrD = mEvsOffset; |
| |
| switch (nOriginBW) |
| { |
| case kEvsBandwidthWB: |
| nCmrT = kEvsCmrCodeTypeWbCha; |
| break; |
| case kEvsBandwidthSWB: |
| nCmrT = kEvsCmrCodeTypeSwbCha; |
| break; |
| case kEvsBandwidthFB: |
| case kEvsBandwidthNB: |
| default: |
| IMLOGE0("[GenerateCMRForEVS] error"); |
| break; |
| } |
| } |
| else |
| { |
| if (mEvsCodecMode == kEvsCodecModeAmrIo) |
| { |
| nCmrT = kEvsCmrCodeTypeAmrIO; |
| nCmrD = nOriginBR; |
| } |
| else |
| { |
| nCmrD = nOriginBR - 9; // start : kEvsPrimaryModeBitrate00590 = 9 to 0 |
| |
| switch (nOriginBW) |
| { |
| case kEvsBandwidthWB: |
| nCmrT = kEvsCmrCodeTypeWb; |
| break; |
| case kEvsBandwidthSWB: |
| nCmrT = kEvsCmrCodeTypeSwb; |
| break; |
| case kEvsBandwidthFB: |
| nCmrT = kEvsCmrCodeTypeFb; |
| break; |
| case kEvsBandwidthNB: |
| default: |
| nCmrT = kEvsCmrCodeTypeNb; |
| break; |
| } |
| } |
| } |
| |
| nCmr = 0x8 | nCmrT; |
| nCmr = (nCmr << 4) | nCmrD; |
| } |
| |
| IMLOGD_PACKET3(IM_PACKET_LOG_PH, "[GenerateCMRForEVS] nCmrD[%u], nCmrT[%d], nCMR[%d]", nCmrT, |
| nCmrD, nCmr); |
| |
| return nCmr; |
| } |
| |
| uint32_t AudioRtpPayloadEncoderNode::CheckPaddingNecessity(uint32_t nTotalSize) |
| { |
| kEvsCodecMode kEvsCodecMode; |
| uint32_t nEVSCompactId; |
| uint32_t nSize = nTotalSize; |
| |
| // check EVS compact size |
| while (nSize != 0 && |
| ImsMediaAudioUtil::ConvertEVSPayloadMode(nSize, &kEvsCodecMode, &nEVSCompactId) == |
| kRtpPyaloadHeaderModeEvsCompact) |
| { |
| mPayload[nSize] = 0; |
| nSize++; |
| |
| IMLOGD1("[CheckPaddingNecessity] Add Padding - size[%d]", nSize); |
| } |
| |
| return nSize; |
| } |
| |
| #ifdef USE_CMR_TEST |
| void AudioRtpPayloadEncoderNode::EVSCMRGeneratorForTest(uint32_t datasize) |
| { |
| // cmr test code start |
| // generate cmr |
| static int32_t sCount = 0; |
| static int32_t bUpdate = 0; |
| static int32_t nNextMode = 0; |
| static int32_t nInc = -1; |
| static int32_t sCodeType = 0; |
| static int32_t sCodeDefine = 0; |
| static int32_t sNextType = 1; |
| |
| sCount++; |
| |
| // cmr test code end |
| if (mEvsPayloadHeaderMode == kRtpPyaloadHeaderModeEvsCompact) |
| { |
| if (mEvsCodecMode == kEvsCodecModeAmrIo) |
| { |
| if ((sCount & 0x7f) == 0) |
| { |
| bUpdate = 1; |
| } |
| |
| if (bUpdate == 1 && datasize > 0) |
| { |
| sCodeDefine = nNextMode; |
| |
| nNextMode += nInc; |
| |
| if (nNextMode < 0) |
| { |
| nNextMode = 1; |
| nInc = 1; |
| } |
| else if (nNextMode > 7) |
| { |
| nNextMode = 6; |
| nInc = -1; |
| } |
| |
| bUpdate = 0; |
| } |
| } |
| } |
| else // headerfull format. |
| { |
| int32_t nMaxCodeDefine = 0; |
| int32_t nMinCodeDefine = 0; |
| |
| switch (sCodeType) |
| { |
| case 0: |
| nMinCodeDefine = 0; |
| nMaxCodeDefine = 6; |
| break; // NB |
| case 1: |
| nMinCodeDefine = 0; |
| nMaxCodeDefine = 8; |
| break; // AMR IO Mode |
| case 2: |
| nMinCodeDefine = 0; |
| nMaxCodeDefine = 6; |
| break; // WB |
| case 3: |
| nMinCodeDefine = 3; |
| nMaxCodeDefine = 6; |
| break; // SWB |
| case 5: |
| nMinCodeDefine = 0; |
| nMaxCodeDefine = 8; |
| break; // WB ChA |
| case 6: |
| nMinCodeDefine = 0; |
| nMaxCodeDefine = 8; |
| break; // SWB ChA |
| case 7: |
| nMinCodeDefine = 15; |
| nMaxCodeDefine = 15; |
| break; // no request |
| default: |
| return; |
| } |
| |
| if ((sCount & 0x7f) == 0) |
| { |
| bUpdate = 1; |
| } |
| |
| if (bUpdate == 1 && datasize > 0) |
| { |
| sCodeDefine = nNextMode; |
| nNextMode += 1; |
| |
| if ((nNextMode - 1) > nMaxCodeDefine) |
| { |
| sCodeType = sNextType; |
| sNextType += 1; |
| |
| if (sNextType == 4) // fb case - skipped |
| sNextType += 1; |
| |
| if (sCodeType == 3) |
| { |
| sCodeDefine = 3; |
| nNextMode = 4; |
| } |
| else if (sCodeType == 7) |
| { |
| sCodeDefine = 15; |
| nNextMode = 16; |
| } |
| else |
| { |
| sCodeDefine = 0; |
| nNextMode = 1; |
| } |
| |
| if (sNextType > 7) // rounding case.(no request) goto NB CMR case. |
| { |
| sNextType = 0; |
| nNextMode = 0; |
| } |
| } |
| |
| bUpdate = 0; |
| } |
| } |
| |
| mCodecType = sCodeType; |
| mEvsMode = ImsMediaAudioUtil::FindMaxEVSBitrate( |
| ImsMediaAudioUtil::ConvertEVSModeToBitRate(sCodeDefine), (kEvsCodecMode)sCodeDefine); |
| } |
| #endif |