diff options
author | bodamnam <bodamnam@google.com> | 2023-02-22 10:25:14 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2023-02-22 10:25:14 +0000 |
commit | df138e15d38f53901f2a79d9ebff24cafce034e5 (patch) | |
tree | 2995612be064e9ade68d8eaed067758a1b9d7396 | |
parent | 042d9aac09e69904208833a6af8d31d977d611ef (diff) | |
parent | 0e6c7d339c5feac51992f7c5204ded40d873fd32 (diff) | |
download | ImsMedia-df138e15d38f53901f2a79d9ebff24cafce034e5.tar.gz |
Implementation of RtpHeaderExtension am: 0e6c7d339c
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/modules/ImsMedia/+/21515131
Change-Id: I578cc114a7bf924e4d12f6dbf63153e36d8a96aa
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
33 files changed, 1001 insertions, 199 deletions
diff --git a/service/src/com/android/telephony/imsmedia/AudioListener.java b/service/src/com/android/telephony/imsmedia/AudioListener.java index 738f4083..f1afc46b 100644 --- a/service/src/com/android/telephony/imsmedia/AudioListener.java +++ b/service/src/com/android/telephony/imsmedia/AudioListener.java @@ -19,10 +19,14 @@ package com.android.telephony.imsmedia; import android.os.Handler; import android.os.Parcel; import android.telephony.CallQuality; +import android.telephony.ims.RtpHeaderExtension; import android.telephony.imsmedia.AudioConfig; import android.telephony.imsmedia.MediaQualityStatus; import android.util.Log; +import java.util.ArrayList; +import java.util.List; + /** * Audio listener to process JNI messages from local AP based RTP stack */ @@ -91,7 +95,14 @@ public class AudioListener implements JNIImsMediaListener { } break; case AudioSession.EVENT_RTP_HEADER_EXTENSION_IND: - Utils.sendMessage(mHandler, event); + { + final List<RtpHeaderExtension> extensions = new ArrayList<RtpHeaderExtension>(); + final int listSize = parcel.readInt(); + for (int i = 0; i < listSize; i++) { + extensions.add(RtpHeaderExtension.CREATOR.createFromParcel(parcel)); + } + Utils.sendMessage(mHandler, event, extensions); + } break; case AudioSession.EVENT_MEDIA_QUALITY_STATUS_IND: { diff --git a/service/src/com/android/telephony/imsmedia/AudioLocalSession.java b/service/src/com/android/telephony/imsmedia/AudioLocalSession.java index fc5f1927..2955f952 100644 --- a/service/src/com/android/telephony/imsmedia/AudioLocalSession.java +++ b/service/src/com/android/telephony/imsmedia/AudioLocalSession.java @@ -158,8 +158,14 @@ public class AudioLocalSession { * @param extensions List of RTP header extensions to be transmitted */ public void sendHeaderExtension(final List<RtpHeaderExtension> extensions) { - Log.d(TAG, "sendHeaderExtension"); - // TODO: add implementation + Log.d(TAG, "sendHeaderExtension, extension=" + extensions); + Parcel parcel = Parcel.obtain(); + parcel.writeInt(AudioSession.CMD_SEND_RTP_HDR_EXTN); + parcel.writeInt(extensions.size()); + for (RtpHeaderExtension item : extensions) { + item.writeToParcel(parcel, 0); + } + sendRequest(mSessionId, parcel); } /** diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/config/include/RtpHeaderExtension.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/config/include/RtpHeaderExtension.h new file mode 100644 index 00000000..f3c52272 --- /dev/null +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/config/include/RtpHeaderExtension.h @@ -0,0 +1,71 @@ +/** + * Copyright (C) 2023 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. + */ + +#ifndef RTPEXTENSION_H +#define RTPEXTENSION_H + +#include <binder/Parcel.h> +#include <binder/Parcelable.h> +#include <binder/Status.h> +#include <stdint.h> + +namespace android +{ + +namespace telephony +{ + +namespace imsmedia +{ + +/** Native representation of android.telephony.imsmedia.RtpHeaderExtension */ + +/** + * The class to encapsulate RTP header extension. + * Per RFC8285, an RTP header extension consists of both a local identifier in the range 1-14, an + * 8-bit length indicator and a number of extension data bytes equivalent to the stated length. + */ +class RtpHeaderExtension : public Parcelable +{ +public: + RtpHeaderExtension(); + RtpHeaderExtension(const RtpHeaderExtension& extension); + virtual ~RtpHeaderExtension(); + RtpHeaderExtension& operator=(const RtpHeaderExtension& extension); + bool operator==(const RtpHeaderExtension& extension) const; + bool operator!=(const RtpHeaderExtension& extension) const; + virtual status_t writeToParcel(Parcel* parcel) const; + virtual status_t readFromParcel(const Parcel* in); + int32_t getLocalIdentifier(); + void setLocalIdentifier(int32_t id); + uint8_t* getExtensionData() const; + void setExtensionData(const uint8_t* data, const int32_t size); + int32_t getExtensionDataSize(); + void setExtensionDataSize(int32_t size); + +protected: + // The local identifier for this RTP header extension. + int32_t mLocalIdentifier; + // The data for this RTP header extension. + uint8_t* mExtensionData; + // The length of the mExtensionData + int32_t mExtensionDataSize; +}; +} // namespace imsmedia +} // namespace telephony +} // namespace android + +#endif
\ No newline at end of file diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/config/include/VideoConfig.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/config/include/VideoConfig.h index eff588ff..8a6f1f57 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/config/include/VideoConfig.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/config/include/VideoConfig.h @@ -252,8 +252,8 @@ protected: int32_t deviceOrientationDegree; /* The value to identify CVO RTP header extension features is enabled by the SDP negotiation. * When the flag is set, MediaStack sends CVO RTP extension byte in the RTP header when the - * sendHeaderExtension is invoked and the Video IDR frame is sent. if this value is -1, - * CVO is disabled, and non zero means CVO enabled with specified offset. Check RFC 5285 */ + * Video IDR frame is sent. if this value is -1, CVO is disabled, and non zero means CVO enabled + * with specified offset. Check RFC 5285 */ int32_t cvoValue; /* The RTPFB, PSFB configuration with RTCP Protocol */ int32_t rtcpFbTypes; diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/config/src/RtpHeaderExtension.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/config/src/RtpHeaderExtension.cpp new file mode 100644 index 00000000..e65bd752 --- /dev/null +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/config/src/RtpHeaderExtension.cpp @@ -0,0 +1,199 @@ +/** + * Copyright (C) 2023 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 <RtpHeaderExtension.h> + +namespace android +{ + +namespace telephony +{ + +namespace imsmedia +{ + +RtpHeaderExtension::RtpHeaderExtension() : + mLocalIdentifier(0), + mExtensionData(nullptr), + mExtensionDataSize(0) +{ +} + +RtpHeaderExtension::RtpHeaderExtension(const RtpHeaderExtension& extension) : + mLocalIdentifier(extension.mLocalIdentifier), + mExtensionData(nullptr), + mExtensionDataSize(extension.mExtensionDataSize) +{ + if (mExtensionDataSize > 0) + { + mExtensionData = new uint8_t[mExtensionDataSize]; + memcpy(mExtensionData, extension.mExtensionData, mExtensionDataSize); + } +} + +RtpHeaderExtension::~RtpHeaderExtension() +{ + if (mExtensionData != nullptr) + { + delete[] mExtensionData; + mExtensionData = nullptr; + } +} + +RtpHeaderExtension& RtpHeaderExtension::operator=(const RtpHeaderExtension& extension) +{ + if (this != &extension) + { + mLocalIdentifier = extension.mLocalIdentifier; + this->setExtensionData(extension.mExtensionData, extension.mExtensionDataSize); + } + + return *this; +} + +bool RtpHeaderExtension::operator==(const RtpHeaderExtension& extension) const +{ + return (mLocalIdentifier == extension.mLocalIdentifier && + memcmp(mExtensionData, extension.mExtensionData, mExtensionDataSize) == 0); +} + +bool RtpHeaderExtension::operator!=(const RtpHeaderExtension& extension) const +{ + return (mLocalIdentifier != extension.mLocalIdentifier || + memcmp(mExtensionData, extension.mExtensionData, mExtensionDataSize) != 0); +} + +status_t RtpHeaderExtension::writeToParcel(Parcel* parcel) const +{ + status_t err; + + if (parcel == nullptr) + { + return BAD_VALUE; + } + + err = parcel->writeInt32(mLocalIdentifier); + + if (err != NO_ERROR) + { + return err; + } + + err = parcel->writeInt32(mExtensionDataSize); + + if (err != NO_ERROR) + { + return err; + } + + void* dest = parcel->writeInplace(mExtensionDataSize); + + if (dest == nullptr) + { + return NO_MEMORY; + } + + memcpy(dest, mExtensionData, mExtensionDataSize); + return NO_ERROR; +} + +status_t RtpHeaderExtension::readFromParcel(const Parcel* parcel) +{ + status_t err; + + if (parcel == nullptr) + { + return BAD_VALUE; + } + + err = parcel->readInt32(&mLocalIdentifier); + + if (err != NO_ERROR) + { + return err; + } + + err = parcel->readInt32(&mExtensionDataSize); + + if (err != NO_ERROR) + { + return err; + } + + if (mExtensionDataSize != 0) + { + if (mExtensionData != nullptr) + { + delete[] mExtensionData; + mExtensionData = nullptr; + } + + mExtensionData = new uint8_t[mExtensionDataSize]; + const void* data = parcel->readInplace(mExtensionDataSize); + + if (data != nullptr) + { + memcpy(mExtensionData, data, mExtensionDataSize); + } + } + + return NO_ERROR; +} + +int32_t RtpHeaderExtension::getLocalIdentifier() +{ + return mLocalIdentifier; +} + +void RtpHeaderExtension::setLocalIdentifier(const int32_t id) +{ + mLocalIdentifier = id; +} + +uint8_t* RtpHeaderExtension::getExtensionData() const +{ + return mExtensionData; +} + +void RtpHeaderExtension::setExtensionData(const uint8_t* data, const int32_t size) +{ + if (mExtensionData != nullptr) + { + delete[] mExtensionData; + mExtensionData = nullptr; + mExtensionDataSize = 0; + } + + if (data != nullptr) + { + mExtensionDataSize = size; + mExtensionData = new uint8_t[mExtensionDataSize]; + memcpy(mExtensionData, data, mExtensionDataSize); + } +} + +int32_t RtpHeaderExtension::getExtensionDataSize() +{ + return mExtensionDataSize; +} +void RtpHeaderExtension::setExtensionDataSize(int32_t size) +{ + mExtensionDataSize = size; +} + +} // namespace imsmedia +} // namespace telephony +} // namespace android
\ No newline at end of file diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/IRtpSession.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/IRtpSession.cpp index f2a97fc4..11647e48 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/IRtpSession.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/IRtpSession.cpp @@ -298,40 +298,40 @@ void IRtpSession::StopRtcp() } } -bool IRtpSession::SendRtpPacket(uint32_t nPayloadType, uint8_t* pData, uint32_t nDataSize, - uint32_t nTimestamp, bool bMark, uint32_t nTimeDiff, bool bExtension, - tRtpHeaderExtensionInfo* pExtensionInfo) +bool IRtpSession::SendRtpPacket(uint32_t payloadType, uint8_t* data, uint32_t dataSize, + uint32_t timestamp, bool mark, uint32_t timeDiff, RtpHeaderExtensionInfo* extensionInfo) { - tRtpSvc_SendRtpPacketParm stRtpPacketParam; - memset(&stRtpPacketParam, 0, sizeof(tRtpSvc_SendRtpPacketParm)); + tRtpSvc_SendRtpPacketParam stRtpPacketParam; + memset(&stRtpPacketParam, 0, sizeof(tRtpSvc_SendRtpPacketParam)); IMLOGD_PACKET5(IM_PACKET_LOG_RTP, - "SendRtpPacket, payloadType[%u], size[%u], nTS[%u], bMark[%d], bExtension[%d]", - nPayloadType, nDataSize, nTimestamp, bMark, bExtension); - stRtpPacketParam.bMbit = bMark ? eRTP_TRUE : eRTP_FALSE; - stRtpPacketParam.byPayLoadType = nPayloadType; - stRtpPacketParam.diffFromLastRtpTimestamp = nTimeDiff; - stRtpPacketParam.bXbit = bExtension ? eRTP_TRUE : eRTP_FALSE; - - if (bExtension && pExtensionInfo != nullptr) + "SendRtpPacket, payloadType[%u], size[%u], TS[%u], mark[%d], extension[%d]", + payloadType, dataSize, timestamp, mark, extensionInfo != nullptr); + stRtpPacketParam.bMbit = mark ? eRTP_TRUE : eRTP_FALSE; + stRtpPacketParam.byPayLoadType = payloadType; + stRtpPacketParam.diffFromLastRtpTimestamp = timeDiff; + stRtpPacketParam.bXbit = extensionInfo != nullptr ? eRTP_TRUE : eRTP_FALSE; + + if (extensionInfo != nullptr) { - stRtpPacketParam.nDefinedByProfile = pExtensionInfo->nDefinedByProfile; - stRtpPacketParam.nLength = pExtensionInfo->nLength; - stRtpPacketParam.nExtensionData = pExtensionInfo->nExtensionData; + stRtpPacketParam.wDefinedByProfile = extensionInfo->definedByProfile; + stRtpPacketParam.wExtLen = extensionInfo->length; + stRtpPacketParam.pExtData = extensionInfo->extensionData; + stRtpPacketParam.nExtDataSize = extensionInfo->extensionDataSize; } - if (mPrevTimestamp == nTimestamp) + if (mPrevTimestamp == timestamp) { stRtpPacketParam.bUseLastTimestamp = eRTP_TRUE; } else { stRtpPacketParam.bUseLastTimestamp = eRTP_FALSE; - mPrevTimestamp = nTimestamp; + mPrevTimestamp = timestamp; } mNumRtpDataToSend++; IMS_RtpSvc_SendRtpPacket( - this, mRtpSessionId, reinterpret_cast<char*>(pData), nDataSize, &stRtpPacketParam); + this, mRtpSessionId, reinterpret_cast<char*>(data), dataSize, &stRtpPacketParam); return true; } @@ -421,26 +421,16 @@ void IRtpSession::OnPeerInd(tRtpSvc_IndicationFromStack type, void* pMsg) { tRtpSvcIndSt_ReceiveRtpInd* pstRtp = reinterpret_cast<tRtpSvcIndSt_ReceiveRtpInd*>(pMsg); - uint32_t nSSRC = 0; - - if (pstRtp->wMsgHdrLen >= 12) - { - nSSRC = pstRtp->pMsgHdr[8]; - nSSRC <<= 8; - nSSRC += pstRtp->pMsgHdr[9]; - nSSRC <<= 8; - nSSRC += pstRtp->pMsgHdr[10]; - nSSRC <<= 8; - nSSRC += pstRtp->pMsgHdr[11]; - } if ((mEnableDTMF == false || mRtpDtmfPayloadType != pstRtp->dwPayloadType) && pstRtp->dwPayloadType != 20) { + RtpHeaderExtensionInfo extensionInfo(pstRtp->wDefinedByProfile, pstRtp->wExtLen, + reinterpret_cast<int8_t*>(pstRtp->pExtData), pstRtp->wExtDataSize); + mRtpDecoderListener->OnMediaDataInd(pstRtp->pMsgBody, pstRtp->wMsgBodyLen, pstRtp->dwTimestamp, pstRtp->bMbit, pstRtp->dwSeqNum, - pstRtp->dwPayloadType, nSSRC, pstRtp->bExtension, - pstRtp->extensionData); + pstRtp->dwPayloadType, pstRtp->dwSsrc, extensionInfo); } } break; diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioManager.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioManager.cpp index 357a1d71..49efef54 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioManager.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioManager.cpp @@ -174,10 +174,20 @@ void AudioManager::sendDtmf(int sessionId, char dtmfDigit, int duration) } } -/*void AudioManager::sendHeaderExtension(int sessionId, RtpHeaderExtension* data) { - (void)sessionId; - (void)data; -}*/ +void AudioManager::sendRtpHeaderExtension( + int sessionId, std::list<RtpHeaderExtension>* listExtension) +{ + auto session = mSessions.find(sessionId); + IMLOGI1("[sendRtpHeaderExtension] sessionId[%d]", sessionId); + if (session != mSessions.end()) + { + (session->second)->sendRtpHeaderExtension(listExtension); + } + else + { + IMLOGE1("[sendRtpHeaderExtension] no session id[%d]", sessionId); + } +} void AudioManager::setMediaQualityThreshold(int sessionId, MediaQualityThreshold* threshold) { @@ -225,7 +235,7 @@ void AudioManager::sendMessage(const int sessionId, const android::Parcel& parce case kAudioDeleteConfig: { AudioConfig* config = new AudioConfig(); - config->readFromParcel(&parcel); + err = config->readFromParcel(&parcel); if (err != NO_ERROR) { IMLOGE1("[sendMessage] error readFromParcel[%d]", err); @@ -241,9 +251,25 @@ void AudioManager::sendMessage(const int sessionId, const android::Parcel& parce "AUDIO_REQUEST_EVENT", nMsg, sessionId, reinterpret_cast<uint64_t>(param)); } break; - case kAudioSendHeaderExtension: - // TO DO - break; + case kAudioSendRtpHeaderExtension: + { + std::list<RtpHeaderExtension>* listExtension = new std::list<RtpHeaderExtension>(); + int listSize = parcel.readInt32(); + + for (int32_t i = 0; i < listSize; i++) + { + RtpHeaderExtension extension; + + if (extension.readFromParcel(&parcel) == NO_ERROR) + { + listExtension->push_back(extension); + } + } + + ImsMediaEventHandler::SendEvent("AUDIO_REQUEST_EVENT", nMsg, sessionId, + reinterpret_cast<uint64_t>(listExtension)); + } + break; case kAudioSetMediaQualityThreshold: { MediaQualityThreshold* threshold = new MediaQualityThreshold(); @@ -377,9 +403,19 @@ void AudioManager::RequestHandler::processEvent( } } break; - case kAudioSendHeaderExtension: - // TO DO : add implementation - break; + case kAudioSendRtpHeaderExtension: + { + std::list<RtpHeaderExtension>* listExtension = + reinterpret_cast<std::list<RtpHeaderExtension>*>(paramA); + + if (listExtension != nullptr) + { + AudioManager::getInstance()->sendRtpHeaderExtension( + static_cast<int>(sessionId), listExtension); + delete listExtension; + } + } + break; case kAudioSetMediaQualityThreshold: { MediaQualityThreshold* threshold = reinterpret_cast<MediaQualityThreshold*>(paramA); @@ -455,8 +491,25 @@ void AudioManager::ResponseHandler::processEvent( } break; case kAudioRtpHeaderExtensionInd: - // TODO : add implementation - break; + { + parcel.writeInt32(event); + std::list<RtpHeaderExtension>* listExtension = + reinterpret_cast<std::list<RtpHeaderExtension>*>(paramA); + + if (listExtension != nullptr) + { + parcel.writeInt32(listExtension->size()); + + for (auto& extension : *listExtension) + { + extension.writeToParcel(&parcel); + } + + AudioManager::getInstance()->sendResponse(sessionId, parcel); + delete listExtension; + } + } + break; case kAudioMediaQualityStatusInd: { parcel.writeInt32(event); diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioSession.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioSession.cpp index e17112f6..5262cd15 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioSession.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioSession.cpp @@ -478,8 +478,8 @@ void AudioSession::onEvent(int32_t type, uint64_t param1, uint64_t param2) "AUDIO_RESPONSE_EVENT", kAudioFirstMediaPacketInd, mSessionId, param1, param2); break; case kImsMediaEventHeaderExtensionReceived: - ImsMediaEventHandler::SendEvent( - "AUDIO_RESPONSE_EVENT", kAudioRtpHeaderExtensionInd, 0, 0); + ImsMediaEventHandler::SendEvent("AUDIO_RESPONSE_EVENT", kAudioRtpHeaderExtensionInd, + mSessionId, param1, param2); break; case kImsMediaEventMediaQualityStatus: ImsMediaEventHandler::SendEvent("AUDIO_RESPONSE_EVENT", kAudioMediaQualityStatusInd, @@ -578,10 +578,19 @@ uint32_t AudioSession::getGraphSize(ImsMediaStreamType type) return 0; } -void AudioSession::SendInternalEvent(int32_t type, uint64_t param1, uint64_t param2) +void AudioSession::sendRtpHeaderExtension(std::list<RtpHeaderExtension>* listExtension) { - (void)param2; + for (auto& graph : mListGraphRtpTx) + { + if (graph != nullptr) + { + graph->sendRtpHeaderExtension(listExtension); + } + } +} +void AudioSession::SendInternalEvent(int32_t type, uint64_t param1, uint64_t param2) +{ switch (type) { case kRequestAudioCmr: diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioStreamGraphRtpTx.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioStreamGraphRtpTx.cpp index 4a991320..ae8624a5 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioStreamGraphRtpTx.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioStreamGraphRtpTx.cpp @@ -244,3 +244,13 @@ void AudioStreamGraphRtpTx::processCmr(const uint32_t cmr) (reinterpret_cast<IAudioSourceNode*>(node))->ProcessCmr(cmr); } } + +void AudioStreamGraphRtpTx::sendRtpHeaderExtension(std::list<RtpHeaderExtension>* listExtension) +{ + BaseNode* node = findNode(kNodeIdRtpEncoder); + + if (node != nullptr) + { + (reinterpret_cast<RtpEncoderNode*>(node))->SetRtpHeaderExtension(listExtension); + } +}
\ No newline at end of file diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/IRtpSession.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/IRtpSession.h index bb667867..3075dd4d 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/IRtpSession.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/IRtpSession.h @@ -55,9 +55,9 @@ class IRtpDecoderListener public: IRtpDecoderListener() {} virtual ~IRtpDecoderListener() {} - virtual void OnMediaDataInd(unsigned char* pData, uint32_t nDataSize, uint32_t nTimestamp, - bool bMark, uint16_t nSeqNum, uint32_t nPayloadType, uint32_t nSSRC, bool bExtension, - uint16_t nExtensionData) = 0; + virtual void OnMediaDataInd(unsigned char* data, uint32_t dataSize, uint32_t timestamp, + bool mark, uint16_t seqNum, uint32_t payloadType, uint32_t ssrc, + const RtpHeaderExtensionInfo& extensionInfo) = 0; virtual void OnNumReceivedPacket(uint32_t nNumRtpPacket) = 0; }; @@ -103,9 +103,8 @@ public: void StopRtp(); void StartRtcp(bool bSendRtcpBye = false); void StopRtcp(); - bool SendRtpPacket(uint32_t nPayloadType, uint8_t* pData, uint32_t nDataSize, - uint32_t timestamp, bool mark, uint32_t nTimeDiff, bool bExtension = false, - tRtpHeaderExtensionInfo* pExtensionInfo = nullptr); + bool SendRtpPacket(uint32_t payloadType, uint8_t* data, uint32_t dataSize, uint32_t timestamp, + bool mark, uint32_t nTimeDiff, RtpHeaderExtensionInfo* extensionInfo = nullptr); bool ProcRtpPacket(uint8_t* pData, uint32_t nDataSize); bool ProcRtcpPacket(uint8_t* pData, uint32_t nDataSize); void OnTimer(); diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/ImsMediaDefine.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/ImsMediaDefine.h index 9006f64d..a445059e 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/ImsMediaDefine.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/ImsMediaDefine.h @@ -30,6 +30,7 @@ #define USHORT_TS_ROUND_COMPARE(a, b) \ (((a) >= (b) && (b) >= TS_ROUND_QUARD) || ((a) <= 0xffff - TS_ROUND_QUARD) || \ ((a) <= TS_ROUND_QUARD && (b) >= 0xffff - TS_ROUND_QUARD)) +#define IMS_MEDIA_WORD_SIZE 4 using namespace android::telephony::imsmedia; @@ -293,7 +294,7 @@ enum ImsMediaAudioMsgRequest kAudioDeleteConfig, kAudioConfirmConfig, kAudioSendDtmf, - kAudioSendHeaderExtension, + kAudioSendRtpHeaderExtension, kAudioSetMediaQualityThreshold, }; @@ -320,7 +321,7 @@ enum ImsMediaVideoMsgRequest kVideoModifySession, kVideoSetPreviewSurface, kVideoSetDisplaySurface, - kVideoSendHeaderExtension, + kVideoSendRtpHeaderExtension, kVideoSetMediaQualityThreshold, kVideoRequestDataUsage, }; @@ -550,28 +551,77 @@ public: uint32_t option; }; -struct tRtpHeaderExtensionInfo +struct RtpHeaderExtensionInfo { - uint16_t nDefinedByProfile; - uint16_t nLength; - uint16_t nExtensionData; - tRtpHeaderExtensionInfo(uint16_t profile = 0, uint16_t length = 0, uint16_t data = 0) +public: + enum + { + // RFC 8285#section-4.2, The bit pattern for one byte header + kBitPatternForOneByteHeader = 0xBEDE, + // RFC 8285#section-4.3, The bit pattern for two byte header + kBitPatternForTwoByteHeader = 0x1000, + }; + + uint16_t definedByProfile; + uint16_t length; // length in word unit + int8_t* extensionData; + uint16_t extensionDataSize; + + RtpHeaderExtensionInfo( + uint16_t profile = 0, uint16_t len = 0, int8_t* data = nullptr, uint16_t size = 0) + { + definedByProfile = profile; + length = len; + extensionData = nullptr; + extensionDataSize = 0; + setExtensionData(data, size); + } + + RtpHeaderExtensionInfo(const RtpHeaderExtensionInfo& extension) { - nDefinedByProfile = profile; - nLength = length; - nExtensionData = data; + definedByProfile = extension.definedByProfile; + extensionData = nullptr; + length = extension.length; + setExtensionData(extension.extensionData, extension.extensionDataSize); } - tRtpHeaderExtensionInfo& operator=(const tRtpHeaderExtensionInfo& extension) + + ~RtpHeaderExtensionInfo() + { + if (extensionData != nullptr) + { + delete[] extensionData; + extensionData = nullptr; + } + } + + RtpHeaderExtensionInfo& operator=(const RtpHeaderExtensionInfo& extension) { if (this != &extension) { - nDefinedByProfile = extension.nDefinedByProfile; - nLength = extension.nLength; - nExtensionData = extension.nExtensionData; + definedByProfile = extension.definedByProfile; + length = extension.length; + setExtensionData(extension.extensionData, extension.extensionDataSize); } return *this; } + + void setExtensionData(int8_t* data, uint16_t dataSize) + { + if (extensionData != nullptr) + { + delete[] extensionData; + extensionData = nullptr; + extensionDataSize = 0; + } + + if (data != nullptr) + { + extensionDataSize = dataSize; + extensionData = new int8_t[extensionDataSize]; + memcpy(extensionData, data, extensionDataSize); + } + } }; #define MAX_IP_LEN 128 diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/AudioManager.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/AudioManager.h index a7b69c63..231b4141 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/AudioManager.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/AudioManager.h @@ -23,6 +23,7 @@ #include <AudioSession.h> #include <AudioConfig.h> #include <MediaQualityThreshold.h> +#include <RtpHeaderExtension.h> #include <unordered_map> using namespace std; @@ -74,7 +75,7 @@ private: ImsMediaResult deleteConfig(int sessionId, AudioConfig* config); ImsMediaResult confirmConfig(int sessionId, AudioConfig* config); void sendDtmf(int sessionId, char dtmfDigit, int duration); - // void sendHeaderExtension(int sessionId, RtpHeaderExtension* data); + void sendRtpHeaderExtension(int sessionId, std::list<RtpHeaderExtension>* listExtension); void setMediaQualityThreshold(int sessionId, MediaQualityThreshold* threshold); static AudioManager* sManager; diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/AudioSession.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/AudioSession.h index 10d8b916..ac29bfde 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/AudioSession.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/AudioSession.h @@ -24,6 +24,7 @@ #include <AudioStreamGraphRtcp.h> #include <RtpConfig.h> #include <MediaQualityAnalyzer.h> +#include <RtpHeaderExtension.h> #include <list> class AudioSession : public BaseSession @@ -103,6 +104,13 @@ public: */ uint32_t getGraphSize(ImsMediaStreamType type); + /** + * @brief Send rtp header extension to the audio rtp + * + * @param listExtension The list of rtp header extension data + */ + void sendRtpHeaderExtension(std::list<RtpHeaderExtension>* listExtension); + private: std::list<AudioStreamGraphRtpTx*> mListGraphRtpTx; std::list<AudioStreamGraphRtpRx*> mListGraphRtpRx; diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/AudioStreamGraphRtpTx.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/AudioStreamGraphRtpTx.h index 753774f1..0e9f00d6 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/AudioStreamGraphRtpTx.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/AudioStreamGraphRtpTx.h @@ -19,6 +19,7 @@ #include <ImsMediaDefine.h> #include <AudioStreamGraph.h> +#include <RtpHeaderExtension.h> class AudioStreamGraphRtpTx : public AudioStreamGraph { @@ -57,6 +58,13 @@ public: */ void processCmr(const uint32_t cmr); + /** + * @brief Send rtp header extension to the audio rtp + * + * @param listExtension The list of rtp header extension data + */ + void sendRtpHeaderExtension(std::list<RtpHeaderExtension>* listExtension); + private: std::list<BaseNode*> mListDtmfNodes; }; diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/nodes/RtpDecoderNode.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/nodes/RtpDecoderNode.h index cf285891..efa933e6 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/nodes/RtpDecoderNode.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/nodes/RtpDecoderNode.h @@ -19,6 +19,7 @@ #include <BaseNode.h> #include <IRtpSession.h> +#include <RtpHeaderExtension.h> // #define DEBUG_JITTER_GEN_SIMULATION_DELAY // #define DEBUG_JITTER_GEN_SIMULATION_REORDER @@ -46,9 +47,9 @@ public: virtual void OnDataFromFrontNode(ImsMediaSubType subtype, uint8_t* pData, uint32_t nDataSize, uint32_t timestamp, bool mark, uint32_t nSeqNum, ImsMediaSubType nDataType, uint32_t arrivalTime = 0); - virtual void OnMediaDataInd(unsigned char* pData, uint32_t nDataSize, uint32_t timestamp, - bool mark, uint16_t nSeqNum, uint32_t nPayloadType, uint32_t nSSRC, bool bExtension, - uint16_t nExtensionData); + virtual void OnMediaDataInd(unsigned char* data, uint32_t dataSize, uint32_t timestamp, + bool mark, uint16_t seqNum, uint32_t payloadType, uint32_t ssrc, + const RtpHeaderExtensionInfo& extensionInfo); // IRtpDecoderListener virtual void OnNumReceivedPacket(uint32_t nNumRtpPacket); @@ -69,6 +70,8 @@ public: private: void processDtmf(uint8_t* data); + std::list<RtpHeaderExtension>* DecodeRtpHeaderExtension( + const RtpHeaderExtensionInfo& extensionInfo); IRtpSession* mRtpSession; RtpAddress mLocalAddress; diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/nodes/RtpEncoderNode.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/nodes/RtpEncoderNode.h index 788f227e..54253073 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/nodes/RtpEncoderNode.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/nodes/RtpEncoderNode.h @@ -19,6 +19,7 @@ #include <BaseNode.h> #include <IRtpSession.h> +#include <RtpHeaderExtension.h> #include <mutex> class RtpEncoderNode : public BaseNode, public IRtpEncoderListener @@ -49,13 +50,18 @@ public: /** * @brief Set the camera facing and device orientation parameter for cvo extension in rtp header + * + * @param facing The facing of camera define in kCameraFacing in ImsMediaVideoUtil.h + * @param orientation The orientation value to send in degree unit + * @return true Return true when the extension data set properly + * @return false Return false when the cvo configuration is disabled */ bool SetCvoExtension(const int64_t facing, const int64_t orientation); /** - * @brief Set the rtp header extension parameter + * @brief Convert list of the RtpHeaderExtension to Rtp header extension payload */ - void SetRtpHeaderExtension(tRtpHeaderExtensionInfo& tExtension); + void SetRtpHeaderExtension(std::list<RtpHeaderExtension>* listExtension); private: bool ProcessAudioData(ImsMediaSubType subtype, uint8_t* pData, uint32_t nDataSize); @@ -80,7 +86,7 @@ private: int32_t mCvoValue; int8_t mRedundantPayload; int8_t mRedundantLevel; - tRtpHeaderExtensionInfo mRtpExtension; + std::list<RtpHeaderExtensionInfo> mListRtpExtension; }; #endif diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/video/VideoManager.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/video/VideoManager.h index 6095335c..7c780fd0 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/video/VideoManager.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/video/VideoManager.h @@ -98,7 +98,6 @@ private: ImsMediaResult setPreviewSurfaceToSession(const int sessionId, ANativeWindow* surface); ImsMediaResult setDisplaySurfaceToSession(const int sessionId, ANativeWindow* surface); ImsMediaResult modifySession(const int sessionId, VideoConfig* config); - // void sendHeaderExtension(int sessionId, RtpHeaderExtension* data); void setMediaQualityThreshold(const int sessionId, MediaQualityThreshold* threshold); static VideoManager* manager; diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/nodes/RtpDecoderNode.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/nodes/RtpDecoderNode.cpp index 66e06141..a163d6ad 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/nodes/RtpDecoderNode.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/nodes/RtpDecoderNode.cpp @@ -386,13 +386,14 @@ bool RtpDecoderNode::IsSameConfig(void* config) } void RtpDecoderNode::OnMediaDataInd(unsigned char* data, uint32_t datasize, uint32_t timestamp, - bool mark, uint16_t seq, uint32_t payloadType, uint32_t ssrc, bool extension, - uint16_t nExtensionData) + bool mark, uint16_t seq, uint32_t payloadType, uint32_t ssrc, + const RtpHeaderExtensionInfo& extensionInfo) { IMLOGD_PACKET8(IM_PACKET_LOG_RTP, "[OnMediaDataInd] media[%d] size[%d], TS[%d], mark[%d], seq[%d], payloadType[%d] " - "sampling[%d], ext[%d]", - mMediaType, datasize, timestamp, mark, seq, payloadType, mSamplingRate, extension); + "sampling[%d], extensionSize[%d]", + mMediaType, datasize, timestamp, mark, seq, payloadType, mSamplingRate, + extensionInfo.length); if (mMediaType == IMS_MEDIA_AUDIO && mRtpPayloadRx != payloadType && mRtpPayloadTx != payloadType && payloadType != mRtpRxDtmfPayload && @@ -423,46 +424,53 @@ void RtpDecoderNode::OnMediaDataInd(unsigned char* data, uint32_t datasize, uint return; } - if (extension && mMediaType == IMS_MEDIA_VIDEO && mCvoValue != CVO_DEFINE_NONE) + if (extensionInfo.length > 0 && mMediaType == IMS_MEDIA_AUDIO) { - uint16_t nExtensionID; - uint16_t nCamID; - uint16_t nRotation; - nExtensionID = nExtensionData; - nExtensionID = nExtensionID >> 12; + std::list<RtpHeaderExtension>* extensions = DecodeRtpHeaderExtension(extensionInfo); - nCamID = nExtensionData; // 0: Front-facing camera, 1: Back-facing camera - nCamID = nCamID << 12; - nCamID = nCamID >> 15; + if (mCallback != nullptr && extensions != nullptr) + { + mCallback->SendEvent( + kImsMediaEventHeaderExtensionReceived, reinterpret_cast<uint64_t>(extensions)); + } + } - nRotation = nExtensionData; - nRotation = nRotation << 13; - nRotation = nRotation >> 13; + if (extensionInfo.extensionData != nullptr && extensionInfo.extensionDataSize >= 2 && + mMediaType == IMS_MEDIA_VIDEO && mCvoValue != CVO_DEFINE_NONE) + { + uint16_t extensionId = extensionInfo.extensionData[0] >> 4; - switch (nRotation) + if (extensionId == mCvoValue) { - case 0: // No rotation (Rotated 0CW/CCW = To rotate 0CW/CCW) - case 4: // + Horizontal Flip, but it's treated as same as above - mSubtype = MEDIASUBTYPE_ROT0; - break; - case 1: // Rotated 270CW(90CCW) = To rotate 90CW(270CCW) - case 5: // + Horizontal Flip, but it's treated as same as above - mSubtype = MEDIASUBTYPE_ROT90; - break; - case 2: // Rotated 180CW = To rotate 180CW - case 6: // + Horizontal Flip, but it's treated as same as above - mSubtype = MEDIASUBTYPE_ROT180; - break; - case 3: // Rotated 90CW(270CCW) = To rotate 270CW(90CCW) - case 7: // + Horizontal Flip, but it's treated as same as above - mSubtype = MEDIASUBTYPE_ROT270; - break; - default: - break; - } + // 0: Front-facing camera, 1: Back-facing camera + uint16_t cameraId = extensionInfo.extensionData[1] >> 3; + uint16_t rotation = extensionInfo.extensionData[1] & 0x07; - IMLOGD4("[OnMediaDataInd] extensionId[%d], camId[%d], rot[%d], subtype[%d]", nExtensionID, - nCamID, nRotation, mSubtype); + switch (rotation) + { + case 0: // No rotation (Rotated 0CW/CCW = To rotate 0CW/CCW) + case 4: // + Horizontal Flip, but it's treated as same as above + mSubtype = MEDIASUBTYPE_ROT0; + break; + case 1: // Rotated 270CW(90CCW) = To rotate 90CW(270CCW) + case 5: // + Horizontal Flip, but it's treated as same as above + mSubtype = MEDIASUBTYPE_ROT90; + break; + case 2: // Rotated 180CW = To rotate 180CW + case 6: // + Horizontal Flip, but it's treated as same as above + mSubtype = MEDIASUBTYPE_ROT180; + break; + case 3: // Rotated 90CW(270CCW) = To rotate 270CW(90CCW) + case 7: // + Horizontal Flip, but it's treated as same as above + mSubtype = MEDIASUBTYPE_ROT270; + break; + default: + break; + } + + IMLOGD4("[OnMediaDataInd] extensionId[%d], cameraId[%d], rotation[%d], subtype[%d]", + extensionId, cameraId, rotation, mSubtype); + } } if (mMediaType == IMS_MEDIA_TEXT) @@ -564,4 +572,76 @@ void RtpDecoderNode::processDtmf(uint8_t* data) // mark true when the new event started mDtmfEndBit = true; } +} + +std::list<RtpHeaderExtension>* RtpDecoderNode::DecodeRtpHeaderExtension( + const RtpHeaderExtensionInfo& extensionInfo) +{ + if (extensionInfo.length == 0 || extensionInfo.extensionData == nullptr || + extensionInfo.extensionDataSize == 0) + { + return nullptr; + } + + std::list<RtpHeaderExtension>* extensions = new std::list<RtpHeaderExtension>(); + + // header + bool useTwoByteHeader = + (extensionInfo.definedByProfile == RtpHeaderExtensionInfo::kBitPatternForTwoByteHeader); + uint32_t length = extensionInfo.length; // word size + IMLOGD2("[DecodeRtpHeaderExtension] twoByteHeader[%d], len[%d]", useTwoByteHeader, length); + + uint32_t offset = 0; + int32_t remainingSize = extensionInfo.extensionDataSize; + + while (remainingSize > 0) + { + RtpHeaderExtension extension; + + if (useTwoByteHeader) + { + // header + extension.setLocalIdentifier(extensionInfo.extensionData[offset++]); + int8_t dataSize = extensionInfo.extensionData[offset++]; // add header + + // payload + if (dataSize > 0) + { + extension.setExtensionData( + reinterpret_cast<const uint8_t*>(extensionInfo.extensionData + offset), + dataSize); + } + + offset += dataSize; + remainingSize -= (dataSize + 2); // remove two byte header too + } + else // one byte header + { + // header + extension.setLocalIdentifier(extensionInfo.extensionData[offset] >> 4); + int8_t dataSize = (extensionInfo.extensionData[offset] & 0x0F) + 1; // data + header + offset++; + + // payload + if (dataSize > 0) + { + extension.setExtensionData( + reinterpret_cast<const uint8_t*>(extensionInfo.extensionData + offset), + dataSize); + } + + offset += dataSize; + remainingSize -= (dataSize + 1); // remove one byte header too + } + + extensions->push_back(extension); + + while (remainingSize > 0 && extensionInfo.extensionData[offset] == 0x00) // ignore padding + { + offset++; + remainingSize--; + } + } + + return extensions; }
\ No newline at end of file diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/nodes/RtpEncoderNode.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/nodes/RtpEncoderNode.cpp index d89d5bed..bcd35a6d 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/nodes/RtpEncoderNode.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/nodes/RtpEncoderNode.cpp @@ -328,24 +328,91 @@ bool RtpEncoderNode::SetCvoExtension(const int64_t facing, const int64_t orienta } } - uint16_t extensionData; + int8_t extensionData[4]; // 32bit IMLOGD3("[SetCvoExtension] cvoValue[%d], facing[%d], orientation[%d]", mCvoValue, cameraId, rotation); - extensionData = ((mCvoValue << 12) | (1 << 8)) | ((cameraId << 3) | rotation); - mRtpExtension.nDefinedByProfile = 0xBEDE; - mRtpExtension.nLength = 1; - mRtpExtension.nExtensionData = extensionData; + extensionData[0] = (mCvoValue << 4) | 1; // local identifier and data length + extensionData[1] = (cameraId << 3) | rotation; + extensionData[2] = 0; // padding + extensionData[3] = 0; // padding + mListRtpExtension.push_back(RtpHeaderExtensionInfo( + RtpHeaderExtensionInfo::kBitPatternForOneByteHeader, 1, extensionData, 4)); return true; } return false; } -void RtpEncoderNode::SetRtpHeaderExtension(tRtpHeaderExtensionInfo& tExtension) +void RtpEncoderNode::SetRtpHeaderExtension(std::list<RtpHeaderExtension>* listExtension) { - mRtpExtension = tExtension; + if (listExtension == nullptr || listExtension->empty()) + { + return; + } + + /** + * Check number of byte of the header. Based on RFC8285 4.2, one byte header has a local + * identifier in range of 1 to 14. Two byte header has is a range of 1 to 255. + */ + bool useTwoByteHeader = false; + int32_t totalPayloadLength = 0; // accumulate payload length except the header size + + for (auto extension : *listExtension) + { + if (extension.getLocalIdentifier() > 15) + { + useTwoByteHeader = true; + } + + totalPayloadLength += extension.getExtensionDataSize(); + } + + // accumulate header size + useTwoByteHeader ? totalPayloadLength += 2 * listExtension->size() + : totalPayloadLength += listExtension->size(); + + // padding size + int32_t paddingSize = totalPayloadLength % IMS_MEDIA_WORD_SIZE == 0 + ? 0 + : IMS_MEDIA_WORD_SIZE - totalPayloadLength % IMS_MEDIA_WORD_SIZE; + totalPayloadLength += paddingSize; + + int8_t* extensionData = new int8_t[totalPayloadLength]; + int offset = 0; + + for (auto extension : *listExtension) + { + if (useTwoByteHeader) + { + extensionData[offset++] = extension.getLocalIdentifier(); + extensionData[offset++] = extension.getExtensionDataSize(); + } + else + { + extensionData[offset++] = + extension.getLocalIdentifier() << 4 | (extension.getExtensionDataSize() - 1); + } + + memcpy(extensionData + offset, extension.getExtensionData(), + extension.getExtensionDataSize()); + offset += extension.getExtensionDataSize(); + } + + // add padding + memset(extensionData + offset, 0, paddingSize); + + IMLOGD3("[SetRtpHeaderExtension] twoByte[%d], size[%d], list size[%d]", useTwoByteHeader, + totalPayloadLength, listExtension->size()); + + int16_t defineByProfile = useTwoByteHeader + ? RtpHeaderExtensionInfo::kBitPatternForTwoByteHeader + : RtpHeaderExtensionInfo::kBitPatternForOneByteHeader; + mListRtpExtension.push_back(RtpHeaderExtensionInfo( + defineByProfile, totalPayloadLength / 4, extensionData, totalPayloadLength)); + + delete[] extensionData; } bool RtpEncoderNode::ProcessAudioData(ImsMediaSubType subtype, uint8_t* data, uint32_t size) @@ -431,8 +498,18 @@ bool RtpEncoderNode::ProcessAudioData(ImsMediaSubType subtype, uint8_t* data, ui timestampDiff = timeDiff * mSamplingRate; IMLOGD_PACKET3(IM_PACKET_LOG_RTP, "[ProcessAudioData] PayloadTx[%d], Size[%d], TS[%d]", mRtpPayloadTx, size, currentTimestamp); - mRtpSession->SendRtpPacket( - mRtpPayloadTx, data, size, currentTimestamp, mMark, timestampDiff); + + if (!mListRtpExtension.empty()) + { + mRtpSession->SendRtpPacket(mRtpPayloadTx, data, size, currentTimestamp, mMark, + timestampDiff, &mListRtpExtension.front()); + mListRtpExtension.pop_front(); + } + else + { + mRtpSession->SendRtpPacket( + mRtpPayloadTx, data, size, currentTimestamp, mMark, timestampDiff); + } if (mMark) { @@ -453,11 +530,11 @@ void RtpEncoderNode::ProcessVideoData( if (mCvoValue > 0 && mark && subtype == MEDIASUBTYPE_VIDEO_IDR_FRAME) { mRtpSession->SendRtpPacket( - mRtpPayloadTx, data, size, timestamp, mark, 0, true, &mRtpExtension); + mRtpPayloadTx, data, size, timestamp, mark, 0, &mListRtpExtension.front()); } else { - mRtpSession->SendRtpPacket(mRtpPayloadTx, data, size, timestamp, mark, 0, false, nullptr); + mRtpSession->SendRtpPacket(mRtpPayloadTx, data, size, timestamp, mark, 0); } } @@ -483,19 +560,16 @@ void RtpEncoderNode::ProcessTextData( { if (mRedundantLevel > 1 && mRedundantPayload > 0) { - mRtpSession->SendRtpPacket( - mRedundantPayload, data, size, timestamp, mark, timeDiff, 0, nullptr); + mRtpSession->SendRtpPacket(mRedundantPayload, data, size, timestamp, mark, timeDiff); } else { - mRtpSession->SendRtpPacket( - mRtpPayloadRx, data, size, timestamp, mark, timeDiff, 0, nullptr); + mRtpSession->SendRtpPacket(mRtpPayloadRx, data, size, timestamp, mark, timeDiff); } } else if (subtype == MEDIASUBTYPE_BITSTREAM_T140_RED) { - mRtpSession->SendRtpPacket( - mRtpPayloadTx, data, size, timestamp, mark, timeDiff, 0, nullptr); + mRtpSession->SendRtpPacket(mRtpPayloadTx, data, size, timestamp, mark, timeDiff); } mMark = false; diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/VideoManager.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/VideoManager.cpp index bec6234f..9d98be37 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/VideoManager.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/VideoManager.cpp @@ -199,7 +199,7 @@ void VideoManager::sendMessage(const int sessionId, const android::Parcel& parce "VIDEO_REQUEST_EVENT", nMsg, sessionId, reinterpret_cast<uint64_t>(config)); } break; - case kVideoSendHeaderExtension: + case kVideoSendRtpHeaderExtension: // TO DO break; case kVideoSetMediaQualityThreshold: @@ -321,7 +321,7 @@ void VideoManager::RequestHandler::processEvent( "VIDEO_RESPONSE_EVENT", kVideoModifySessionResponse, sessionId, result, paramA); } break; - case kVideoSendHeaderExtension: + case kVideoSendRtpHeaderExtension: /** TODO: add implementation */ break; case kVideoSetMediaQualityThreshold: diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/protocol/interface/rtp/RtpService.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/protocol/interface/rtp/RtpService.h index f4b62bc7..bf5e0025 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/protocol/interface/rtp/RtpService.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/protocol/interface/rtp/RtpService.h @@ -128,7 +128,7 @@ GLOBAL eRtp_Bool IMS_RtpSvc_DeleteSession(IN RTPSESSIONID hRtpSession); */ GLOBAL eRtp_Bool IMS_RtpSvc_SendRtpPacket(IN RtpServiceListener* pobjRtpServiceListener, IN RTPSESSIONID hRtpSession, IN RtpDt_Char* pBuffer, IN RtpDt_UInt16 wBufferLength, - IN tRtpSvc_SendRtpPacketParm* pstRtpParam); + IN tRtpSvc_SendRtpPacketParam* pstRtpParam); /** * This API processes the received RTP packet. Processed information is sent using diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/protocol/interface/rtp/RtpServiceTypes.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/protocol/interface/rtp/RtpServiceTypes.h index ea3796be..9c4c119e 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/protocol/interface/rtp/RtpServiceTypes.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/protocol/interface/rtp/RtpServiceTypes.h @@ -72,12 +72,13 @@ typedef struct eRtp_Bool bUseLastTimestamp; RtpDt_UInt32 diffFromLastRtpTimestamp; - // [CVO] for extension header + // Rtp extension header eRtp_Bool bXbit; - RtpDt_UInt16 nDefinedByProfile; - RtpDt_UInt16 nLength; - RtpDt_UInt16 nExtensionData; -} tRtpSvc_SendRtpPacketParm; + RtpDt_UInt16 wDefinedByProfile; + RtpDt_UInt16 wExtLen; + RtpDt_Int8* pExtData; + RtpDt_Int32 nExtDataSize; +} tRtpSvc_SendRtpPacketParam; /* typedef struct @@ -94,6 +95,7 @@ typedef struct RtpDt_UInt32 dwTimestamp; RtpDt_UInt32 dwPayloadType; RtpDt_UInt16 dwSeqNum; + RtpDt_UInt32 dwSsrc; RtpDt_UInt16 wMsgHdrLen; RtpDt_UChar* pMsgHdr; @@ -101,10 +103,11 @@ typedef struct RtpDt_UInt16 wMsgBodyLen; RtpDt_UChar* pMsgBody; - /* [CVO] for RTP Header Extension, seokhwan.chung@, 2013.05.22 */ - RtpDt_UInt16 extensionData; - eRtp_Bool bExtension; - + /* RTP Header extension */ + RtpDt_UInt16 wDefinedByProfile; + RtpDt_UInt16 wExtLen; + RtpDt_UChar* pExtData; + RtpDt_UInt16 wExtDataSize; } tRtpSvcIndSt_ReceiveRtpInd; typedef struct diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/protocol/rtp/RtpService.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/protocol/rtp/RtpService.cpp index 0b502bc1..16599644 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/protocol/rtp/RtpService.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/protocol/rtp/RtpService.cpp @@ -44,21 +44,29 @@ RtpDt_Void populateReceiveRtpIndInfo( pstRtpIndMsg->dwTimestamp = pobjRtpHeader->getRtpTimestamp(); pstRtpIndMsg->dwPayloadType = pobjRtpHeader->getPayloadType(); pstRtpIndMsg->dwSeqNum = pobjRtpHeader->getSequenceNumber(); + pstRtpIndMsg->dwSsrc = pobjRtpHeader->getRtpSsrc(); // Header length pstRtpIndMsg->wMsgHdrLen = RTP_FIXED_HDR_LEN; pstRtpIndMsg->wMsgHdrLen += RTP_WORD_SIZE * pobjRtpHeader->getCsrcCount(); + if (pobjRtpPkt->getExtHeader()) { pstRtpIndMsg->wMsgHdrLen += pobjRtpPkt->getExtHeader()->getLength(); - pstRtpIndMsg->bExtension = eRTP_TRUE; - RtpDt_UChar* pbuf = pobjRtpPkt->getExtHeader()->getBuffer(); - pstRtpIndMsg->extensionData = ((((unsigned)*pbuf) << 8) & 0xff00) | ((pbuf[1] & 0x00ff)); + RtpDt_UChar* pExtHdrBuffer = pobjRtpPkt->getExtHeader()->getBuffer(); + RtpDt_Int32 uiByte4Data = + RtpOsUtil::Ntohl(*(reinterpret_cast<RtpDt_UInt32*>(pExtHdrBuffer))); + pstRtpIndMsg->wDefinedByProfile = uiByte4Data >> 16; + pstRtpIndMsg->wExtLen = uiByte4Data & 0x00FF; + pstRtpIndMsg->pExtData = pExtHdrBuffer + 4; + pstRtpIndMsg->wExtDataSize = pobjRtpPkt->getExtHeader()->getLength() - 4; } else { - pstRtpIndMsg->bExtension = eRTP_FALSE; - pstRtpIndMsg->extensionData = 0; + pstRtpIndMsg->wDefinedByProfile = 0; + pstRtpIndMsg->wExtLen = 0; + pstRtpIndMsg->pExtData = nullptr; + pstRtpIndMsg->wExtDataSize = 0; } // End Header length // play the payload @@ -204,27 +212,33 @@ RtpDt_Void populateRtpProfile(OUT RtpStackProfile* pobjStackProfile) pobjStackProfile->setTermNumber(RTP_CONF_SSRC_SEED); } -RtpBuffer* GetCVOXHdr(IN tRtpSvc_SendRtpPacketParm* pstRtpParam) +RtpBuffer* SetRtpHeaderExtension(IN tRtpSvc_SendRtpPacketParam* pstRtpParam) { RtpBuffer* pobjXHdr = new RtpBuffer(); // HDR extension if (pstRtpParam->bXbit) { - RtpDt_UChar* pBuf = new RtpDt_UChar[8]; - pBuf[0] = (((unsigned)pstRtpParam->nDefinedByProfile) >> 8) & 0x00ff; - pBuf[1] = pstRtpParam->nDefinedByProfile & 0x00ff; + const RtpDt_Int32 headerSize = 4; + RtpDt_Int32 nBufferSize = headerSize + pstRtpParam->wExtLen * sizeof(int32_t); + RtpDt_UChar* pBuf = new RtpDt_UChar[nBufferSize]; - pBuf[2] = (((unsigned)pstRtpParam->nLength) >> 8) & 0x00ff; - pBuf[3] = (pstRtpParam->nLength) & 0x00ff; + if (pstRtpParam->wExtLen * sizeof(int32_t) != pstRtpParam->nExtDataSize) + { + RTP_TRACE_WARNING("SetRtpHeaderExtension invalid data size len[%d], size[%d]", + pstRtpParam->wExtLen, pstRtpParam->nExtDataSize); + } - pBuf[4] = (((unsigned)pstRtpParam->nExtensionData) >> 8) & 0x00ff; - pBuf[5] = (pstRtpParam->nExtensionData) & 0x00ff; + // define by profile + pBuf[0] = (((unsigned)pstRtpParam->wDefinedByProfile) >> 8) & 0x00ff; + pBuf[1] = pstRtpParam->wDefinedByProfile & 0x00ff; - pBuf[6] = 0; - pBuf[7] = 0; + // number of the extension data set + pBuf[2] = (((unsigned)pstRtpParam->wExtLen) >> 8) & 0x00ff; + pBuf[3] = (pstRtpParam->wExtLen) & 0x00ff; - pobjXHdr->setBufferInfo(8, pBuf); + memcpy(pBuf + 4, pstRtpParam->pExtData, pstRtpParam->nExtDataSize); + pobjXHdr->setBufferInfo(nBufferSize, pBuf); } else { @@ -234,7 +248,7 @@ RtpBuffer* GetCVOXHdr(IN tRtpSvc_SendRtpPacketParm* pstRtpParam) return pobjXHdr; } -RtpDt_UInt16 GetCVOXHdrLen(eRtp_Bool bEnableCVO) +RtpDt_UInt16 GetRtpHeaderExtensionSize(eRtp_Bool bEnableCVO) { if (bEnableCVO) return RTP_CVO_XHDR_LEN; @@ -351,7 +365,7 @@ GLOBAL eRtp_Bool IMS_RtpSvc_SetPayload(IN RTPSESSIONID hRtpSession, } eRTP_STATUS_CODE eInitSta = - pobjRtpSession->setPayload(pobjlPayloadInfo, GetCVOXHdrLen(bEnableXHdr)); + pobjRtpSession->setPayload(pobjlPayloadInfo, GetRtpHeaderExtensionSize(bEnableXHdr)); delete pobjlPayloadInfo; if (eInitSta != RTP_SUCCESS) { @@ -392,7 +406,7 @@ GLOBAL eRtp_Bool IMS_RtpSvc_DeleteSession(IN RTPSESSIONID hRtpSession) GLOBAL eRtp_Bool IMS_RtpSvc_SendRtpPacket(IN RtpServiceListener* pobjRtpServiceListener, IN RTPSESSIONID hRtpSession, IN RtpDt_Char* pBuffer, IN RtpDt_UInt16 wBufferLength, - IN tRtpSvc_SendRtpPacketParm* pstRtpParam) + IN tRtpSvc_SendRtpPacketParam* pstRtpParam) { RtpSession* pobjRtpSession = reinterpret_cast<RtpSession*>(hRtpSession); @@ -425,6 +439,7 @@ GLOBAL eRtp_Bool IMS_RtpSvc_SendRtpPacket(IN RtpServiceListener* pobjRtpServiceL // Create RTP packet // 1. Set Marker bit eRtp_Bool bMbit = eRTP_FALSE; + if (pstRtpParam->bMbit == eRTP_TRUE) { bMbit = eRTP_TRUE; @@ -435,7 +450,7 @@ GLOBAL eRtp_Bool IMS_RtpSvc_SendRtpPacket(IN RtpServiceListener* pobjRtpServiceL eRtp_Bool bUseLastTimestamp = pstRtpParam->bUseLastTimestamp ? eRTP_TRUE : eRTP_FALSE; eRTP_STATUS_CODE eRtpCreateStat = pobjRtpSession->createRtpPacket(pobjRtpPayload, bMbit, pstRtpParam->byPayLoadType, bUseLastTimestamp, pstRtpParam->diffFromLastRtpTimestamp, - GetCVOXHdr(pstRtpParam), pobjRtpBuf); + SetRtpHeaderExtension(pstRtpParam), pobjRtpBuf); // 3. de-init and free the temp variable both in success and failure case pobjRtpPayload->setBufferInfo(RTP_ZERO, nullptr); @@ -498,6 +513,7 @@ GLOBAL eRtp_Bool IMS_RtpSvc_ProcRtpPacket(IN RtpServiceListener* pvIRtpSession, RtpBuffer objRtpBuf; objRtpBuf.setBufferInfo(uiMsgLength, pMsg); + RtpDt_UInt32 uiTransLen = strlen(reinterpret_cast<const RtpDt_Char*>(pPeerIp)); RtpBuffer objRmtAddr; objRmtAddr.setBufferInfo(uiTransLen + 1, reinterpret_cast<RtpDt_UChar*>(pPeerIp)); diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/protocol/rtp/core/RtpPacket.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/protocol/rtp/core/RtpPacket.cpp index 7ee0e408..23fa9378 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/protocol/rtp/core/RtpPacket.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/protocol/rtp/core/RtpPacket.cpp @@ -149,34 +149,24 @@ eRtp_Bool RtpPacket::decodePacket(IN RtpBuffer* pobjRtpPktBuf) // extension header if (m_objRtpHeader.getExtension()) { - RTP_TRACE_MESSAGE("[XHdr] Extension Header detected", 0, 0); m_pobjExt = new RtpBuffer(); - if (m_pobjExt == nullptr) - { - return eRTP_FAILURE; - } // Get XHdr type and length RtpDt_UInt32 uiByte4Data = RtpOsUtil::Ntohl(*(reinterpret_cast<RtpDt_UInt32*>(pcRtpBuf))); - pcRtpBuf = pcRtpBuf + RTP_WORD_SIZE; - uiRtpBufPos = uiRtpBufPos + RTP_WORD_SIZE; - RtpDt_UInt16 uXHdrLen = (RtpDt_UInt16)(uiByte4Data & RTP_HEX_16_BIT_MAX); - // RtpDt_UInt16 uXHdrType = (RtpDt_UInt16)(uiByte4Data >> RTP_SIXTEEN); + RtpDt_UInt16 uXHdrLen = + (RtpDt_UInt16)(uiByte4Data & RTP_HEX_16_BIT_MAX); // add header size + + uXHdrLen += 1; // add a word for header type info + uXHdrLen *= RTP_WORD_SIZE; // convert word to byte - uXHdrLen += RTP_WORD_SIZE - (uXHdrLen % RTP_WORD_SIZE); // word align if ((uXHdrLen <= 0) || ((uiRtpBufPos + uXHdrLen) > uiRtpUtlBufLen)) { - RTP_TRACE_ERROR("[XHdr]Invalid Header Extension.Xbit-1 but XHdr len <= 0 OR xhdr len " - "> rtp buffer len", - 0, 0); + RTP_TRACE_ERROR("[decodePacket] Invalid Header Extension len[%d]", uXHdrLen, 0); return eRTP_FAILURE; } RtpDt_UChar* pRtpExtData = new RtpDt_UChar[uXHdrLen]; - if (pRtpExtData == nullptr) - { - return eRTP_FAILURE; - } + memcpy(pRtpExtData, pcRtpBuf, uXHdrLen); m_pobjExt->setBufferInfo(uXHdrLen, pRtpExtData); diff --git a/test/app/ImsMediaTestingApp/app/src/main/java/com/example/imsmediatestingapp/MainActivity.java b/test/app/ImsMediaTestingApp/app/src/main/java/com/example/imsmediatestingapp/MainActivity.java index e6800b57..f68076d5 100644 --- a/test/app/ImsMediaTestingApp/app/src/main/java/com/example/imsmediatestingapp/MainActivity.java +++ b/test/app/ImsMediaTestingApp/app/src/main/java/com/example/imsmediatestingapp/MainActivity.java @@ -17,6 +17,7 @@ import android.net.wifi.WifiManager; import android.os.Bundle; import android.telephony.AccessNetworkConstants.AccessNetworkType; import android.telephony.CallQuality; +import android.telephony.ims.RtpHeaderExtension; import android.telephony.imsmedia.AmrParams; import android.telephony.imsmedia.AudioConfig; import android.telephony.imsmedia.AudioSessionCallback; @@ -752,6 +753,12 @@ public class MainActivity extends AppCompatActivity { } @Override + public void onHeaderExtensionReceived(final List<RtpHeaderExtension> extensions) { + Log.d(TAG, "onHeaderExtensionReceived, list size=" + extensions.size() + + "list=" + extensions); + } + + @Override public void triggerAnbrQuery(AudioConfig config) { Log.d(TAG, "triggerAnbrQuery"); } @@ -2627,4 +2634,20 @@ public class MainActivity extends AppCompatActivity { Log.d(TAG, e.toString()); } } + + public void sendHeaderExtension(View btn) { + if (mAudioSession != null) { + List<RtpHeaderExtension> extensions = new ArrayList<>(); + byte[] testBytes1 = new byte[1]; + byte[] testBytes2 = new byte[1]; + testBytes1[0] = 5; + testBytes2[0] = 10; + RtpHeaderExtension extension1 = new RtpHeaderExtension(1, testBytes1); + RtpHeaderExtension extension2 = new RtpHeaderExtension(2, testBytes2); + extensions.add(extension1); + extensions.add(extension2); + Log.d(TAG, "[sendHeaderExtension] extension size=" + extensions.size()); + mAudioSession.sendHeaderExtension(extensions); + } + } } diff --git a/test/app/ImsMediaTestingApp/app/src/main/res/layout/activity_main.xml b/test/app/ImsMediaTestingApp/app/src/main/res/layout/activity_main.xml index 28189c98..fb2025a7 100644 --- a/test/app/ImsMediaTestingApp/app/src/main/res/layout/activity_main.xml +++ b/test/app/ImsMediaTestingApp/app/src/main/res/layout/activity_main.xml @@ -171,6 +171,7 @@ android:textColor="@color/black" /> <Button + android:id="@+id/getSpropButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="20dp" @@ -184,6 +185,20 @@ android:autoSizeTextType="uniform" android:textColor="@color/black" /> + <Button + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="20dp" + android:layout_marginEnd="15dp" + android:layout_alignParentEnd="true" + android:layout_below="@+id/getSpropButton" + android:backgroundTint="@drawable/selected_green" + android:enabled="true" + android:onClick="sendHeaderExtension" + android:text="@string/send_header_extension_button_text" + android:autoSizeTextType="uniform" + android:textColor="@color/black" /> + <Spinner android:id="@+id/spinnerAdditionalMedia" android:layout_width="150dp" diff --git a/test/app/ImsMediaTestingApp/app/src/main/res/values/strings.xml b/test/app/ImsMediaTestingApp/app/src/main/res/values/strings.xml index efdc710c..37d6b5a7 100644 --- a/test/app/ImsMediaTestingApp/app/src/main/res/values/strings.xml +++ b/test/app/ImsMediaTestingApp/app/src/main/res/values/strings.xml @@ -40,6 +40,8 @@ <string name="connected_call_toast_text">Connected!</string> <string name="connection_failed_toast_text">Connection failed!</string> + <!-- Send Header extension button text --> + <string name="send_header_extension_button_text">Send RtpHdrExt</string> <!-- Settings page Strings --> <string name="settings_remote_device_title">Remote Device</string> diff --git a/test/imsmediahal/src/com/android/telephony/testimsmediahal/AudioListenerProxy.java b/test/imsmediahal/src/com/android/telephony/testimsmediahal/AudioListenerProxy.java index 4917c3cc..2832c4d2 100644 --- a/test/imsmediahal/src/com/android/telephony/testimsmediahal/AudioListenerProxy.java +++ b/test/imsmediahal/src/com/android/telephony/testimsmediahal/AudioListenerProxy.java @@ -129,11 +129,14 @@ class AudioListenerProxy implements JNIImsMediaListener { } break; case AudioSession.EVENT_RTP_HEADER_EXTENSION_IND: - final List<RtpHeaderExtension> rtpHeaderExt = new ArrayList<RtpHeaderExtension>(); - parcel.readList(rtpHeaderExt, RtpHeaderExtension.class.getClassLoader()); + final List<RtpHeaderExtension> extensions = new ArrayList<RtpHeaderExtension>(); + final int listSize = parcel.readInt(); + for (int i = 0; i < listSize; i++) { + extensions.add(RtpHeaderExtension.CREATOR.createFromParcel(parcel)); + } try { - mMediaSessionListener.onHeaderExtensionReceived(rtpHeaderExt); + mMediaSessionListener.onHeaderExtensionReceived(extensions); } catch(RemoteException e) { Log.e(TAG, "Failed to notify rtp header extension: " + e); } diff --git a/test/imsmediahal/src/com/android/telephony/testimsmediahal/IImsMediaSessionImpl.java b/test/imsmediahal/src/com/android/telephony/testimsmediahal/IImsMediaSessionImpl.java index 1ea35ad9..8d84c325 100644 --- a/test/imsmediahal/src/com/android/telephony/testimsmediahal/IImsMediaSessionImpl.java +++ b/test/imsmediahal/src/com/android/telephony/testimsmediahal/IImsMediaSessionImpl.java @@ -118,8 +118,9 @@ public class IImsMediaSessionImpl extends IImsMediaSession.Stub { Log.d(TAG, "sendHeaderExtension: " + data); Parcel parcel = Parcel.obtain(); parcel.writeInt(AudioSession.CMD_SEND_RTP_HDR_EXTN); - if (data != null) { - parcel.writeList(data); + parcel.writeInt(data.size()); + for (RtpHeaderExtension item : data) { + item.writeToParcel(parcel, 0); } connector.sendRequest(mSessionId, parcel); } diff --git a/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/config/RtpHeaderExtensionTest.cpp b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/config/RtpHeaderExtensionTest.cpp new file mode 100644 index 00000000..926e917f --- /dev/null +++ b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/config/RtpHeaderExtensionTest.cpp @@ -0,0 +1,100 @@ +/** + * Copyright (C) 2023 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 <RtpHeaderExtension.h> +#include <gtest/gtest.h> + +using namespace android::telephony::imsmedia; + +const int32_t kIdentifier = 15; +const uint8_t kExtensionData[] = {0x01, 0x02}; +const int32_t kExtensionDataSize = 2; + +class RtpHeaderExtensionTest : public ::testing::Test +{ +public: + RtpHeaderExtensionTest() { extension = nullptr; } + virtual ~RtpHeaderExtensionTest() {} + +protected: + RtpHeaderExtension* extension; + + virtual void SetUp() override + { + extension = new RtpHeaderExtension(); + extension->setExtensionData(kExtensionData, kExtensionDataSize); + extension->setLocalIdentifier(kIdentifier); + extension->setExtensionDataSize(kExtensionDataSize); + } + + virtual void TearDown() override { delete extension; } +}; + +TEST_F(RtpHeaderExtensionTest, TestGetterSetter) +{ + EXPECT_EQ(memcmp(extension->getExtensionData(), kExtensionData, kExtensionDataSize), 0); + EXPECT_EQ(extension->getLocalIdentifier(), kIdentifier); + EXPECT_EQ(extension->getExtensionDataSize(), kExtensionDataSize); +} + +TEST_F(RtpHeaderExtensionTest, TestParcel) +{ + android::Parcel parcel; + extension->writeToParcel(&parcel); + parcel.setDataPosition(0); + + RtpHeaderExtension* extension2 = new RtpHeaderExtension(); + extension2->readFromParcel(&parcel); + EXPECT_EQ(*extension2, *extension); + delete extension2; +} + +TEST_F(RtpHeaderExtensionTest, TestAssign) +{ + RtpHeaderExtension extension2; + extension2 = *extension; + EXPECT_EQ(*extension, extension2); +} + +TEST_F(RtpHeaderExtensionTest, TestEqual) +{ + RtpHeaderExtension* extension2 = new RtpHeaderExtension(); + extension2->setExtensionData(kExtensionData, kExtensionDataSize); + extension2->setLocalIdentifier(kIdentifier); + extension2->setExtensionDataSize(kExtensionDataSize); + EXPECT_EQ(*extension, *extension2); + delete extension2; +} + +TEST_F(RtpHeaderExtensionTest, TestNotEqual) +{ + RtpHeaderExtension* extension2 = new RtpHeaderExtension(); + const uint8_t data[] = {0x03, 0x04}; + extension2->setExtensionData(data, 2); + extension2->setLocalIdentifier(kIdentifier); + extension2->setExtensionDataSize(kExtensionDataSize); + + RtpHeaderExtension* extension3 = new RtpHeaderExtension(); + extension3->setExtensionData(kExtensionData, kExtensionDataSize); + extension3->setLocalIdentifier(9999); + extension3->setExtensionDataSize(kExtensionDataSize); + + EXPECT_NE(*extension, *extension2); + EXPECT_NE(*extension, *extension3); + + delete extension2; + delete extension3; +}
\ No newline at end of file diff --git a/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/nodes/RtpDecoderNodeTests.cpp b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/nodes/RtpDecoderNodeTests.cpp index dfa293e7..90c23056 100644 --- a/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/nodes/RtpDecoderNodeTests.cpp +++ b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/nodes/RtpDecoderNodeTests.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2023 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. @@ -89,8 +89,15 @@ public: { dtmfDigit = 0; dtmfDuration = 0; + listExtensions = nullptr; + } + virtual ~FakeRtpDecoderCallback() + { + if (listExtensions != nullptr) + { + delete listExtensions; + } } - virtual ~FakeRtpDecoderCallback() {} virtual void onEvent(int32_t type, uint64_t param1, uint64_t param2) { if (type == kAudioDtmfReceivedInd) @@ -98,13 +105,24 @@ public: dtmfDigit = static_cast<uint8_t>(param1); dtmfDuration = static_cast<uint32_t>(param2); } + else if (type == kImsMediaEventHeaderExtensionReceived) + { + if (listExtensions != nullptr) + { + delete listExtensions; + } + + listExtensions = reinterpret_cast<std::list<RtpHeaderExtension>*>(param1); + } } uint8_t GetDtmfDigit() { return dtmfDigit; } uint32_t GetDtmfDuration() { return dtmfDuration; } + std::list<RtpHeaderExtension>* GetListExtension() { return listExtensions; } private: uint8_t dtmfDigit; uint32_t dtmfDuration; + std::list<RtpHeaderExtension>* listExtensions; }; class FakeRtpDecoderNode : public BaseNode @@ -370,6 +388,46 @@ TEST_F(RtpDecoderNodeTest, testAudioDtmfDataProcess) EXPECT_EQ(callback.GetDtmfDuration(), 100); } +TEST_F(RtpDecoderNodeTest, testAudioRtpExtension) +{ + setupAudioConfig(); + EXPECT_EQ(encoder->Start(), RESULT_SUCCESS); + EXPECT_EQ(decoder->Start(), RESULT_SUCCESS); + + // AMR mode 6 payload frame + uint8_t testFrame[] = {0x1c, 0x51, 0x06, 0x40, 0x32, 0xba, 0x8e, 0xc1, 0x25, 0x42, 0x2f, 0xc7, + 0xaf, 0x6e, 0xe0, 0xbb, 0xb2, 0x91, 0x09, 0xa5, 0xa6, 0x08, 0x18, 0x6f, 0x08, 0x1c, + 0x1c, 0x44, 0xd8, 0xe0, 0x48, 0x8c, 0x7c, 0xf8, 0x4c, 0x22, 0xd0}; + + const uint8_t testExtension1[] = {0xFF, 0xF2}; + const uint8_t testExtension2[] = {0xFF, 0xF2}; + + std::list<RtpHeaderExtension> listExtension; + + RtpHeaderExtension extension1; + extension1.setLocalIdentifier(1); + extension1.setExtensionData(testExtension1, 2); + listExtension.push_back(extension1); + + RtpHeaderExtension extension2; + extension2.setLocalIdentifier(2); + extension2.setExtensionData(testExtension2, 2); + listExtension.push_back(extension2); + + encoder->SetRtpHeaderExtension(&listExtension); + encoder->OnDataFromFrontNode(MEDIASUBTYPE_UNDEFINED, testFrame, sizeof(testFrame), 0, false, 0); + encoder->ProcessData(); + + std::list<RtpHeaderExtension>* receivedExtension = callback.GetListExtension(); + ASSERT_TRUE(receivedExtension != nullptr); + + for (auto& extension : *receivedExtension) + { + EXPECT_EQ(extension, listExtension.front()); + listExtension.pop_front(); + } +} + TEST_F(RtpDecoderNodeTest, startVideoAndUpdate) { setupVideoConfig(); diff --git a/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/protocol/rtp/core/RtpPacketTest.cpp b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/protocol/rtp/core/RtpPacketTest.cpp index 17c758cb..319a2fe0 100644 --- a/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/protocol/rtp/core/RtpPacketTest.cpp +++ b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/protocol/rtp/core/RtpPacketTest.cpp @@ -90,7 +90,7 @@ TEST(RtpPacketTest, TestDecodePacket) RtpBuffer* pobjRtpExtHdr = rtpPacket.getExtHeader(); ASSERT_TRUE(pobjRtpExtHdr != nullptr); - uint8_t pRtpExtHdr[] = {0x41, 0x78, 0x42, 0x00}; + uint8_t pRtpExtHdr[] = {0xbe, 0xde, 0x00, 0x01, 0x41, 0x78, 0x42, 0x00}; EXPECT_EQ(memcmp(pRtpExtHdr, pobjRtpExtHdr->getBuffer(), sizeof(pRtpExtHdr)), 0); EXPECT_EQ(pobjRtpExtHdr->getLength(), sizeof(pRtpExtHdr)); diff --git a/tests/unit/src/com/android/telephony/imsmedia/AudioListenerTest.java b/tests/unit/src/com/android/telephony/imsmedia/AudioListenerTest.java index a59a1646..353b6f7c 100644 --- a/tests/unit/src/com/android/telephony/imsmedia/AudioListenerTest.java +++ b/tests/unit/src/com/android/telephony/imsmedia/AudioListenerTest.java @@ -1,4 +1,4 @@ -/* +/** * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -25,6 +25,7 @@ import static org.mockito.Mockito.verify; import android.os.Parcel; import android.os.RemoteException; import android.telephony.CallQuality; +import android.telephony.ims.RtpHeaderExtension; import android.telephony.imsmedia.AudioConfig; import android.telephony.imsmedia.IImsAudioSessionCallback; import android.telephony.imsmedia.ImsMediaSession; @@ -39,6 +40,9 @@ import org.junit.runners.JUnit4; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.ArrayList; +import java.util.List; + @RunWith(JUnit4.class) public class AudioListenerTest { private static final int SESSION_ID = 1; @@ -160,11 +164,21 @@ public class AudioListenerTest { @Test public void testEventRtpHeaderExtensionInd() throws RemoteException { - mAudioListener.onMessage(createParcel(AudioSession.EVENT_RTP_HEADER_EXTENSION_IND, - SESSION_ID, mAudioConfig)); + List<RtpHeaderExtension> extensions = new ArrayList<>(); + extensions.add(new RtpHeaderExtension(1, new byte[]{5})); + extensions.add(new RtpHeaderExtension(2, new byte[]{10})); + Parcel parcel = Parcel.obtain(); + parcel.writeInt(AudioSession.EVENT_RTP_HEADER_EXTENSION_IND); + parcel.writeInt(extensions.size()); + for (RtpHeaderExtension item : extensions) { + item.writeToParcel(parcel, 0); + } + parcel.setDataPosition(0); + mAudioListener.onMessage(parcel); + parcel.recycle(); processAllMessages(); verify(mMockIImsAudioSessionCallback, - times(1)).onHeaderExtensionReceived(any()); + times(1)).onHeaderExtensionReceived(eq(extensions)); } @Test |