diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-04-21 19:56:55 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-04-21 19:56:55 +0000 |
commit | ddd0cff3eb5acdc581c8f6f5b36b1bf8b90c8bed (patch) | |
tree | 77daebc4c4c41dae651a3425504a52dd1ccc21b1 | |
parent | cad0938b23a933110df171fb7c9f96721f606cd2 (diff) | |
parent | 1879ee137ede0d62b88cd16da3d46138ca540dfa (diff) | |
download | ImsMedia-ddd0cff3eb5acdc581c8f6f5b36b1bf8b90c8bed.tar.gz |
Snap for 9989322 from 1879ee137ede0d62b88cd16da3d46138ca540dfa to mainline-rkpd-release
Change-Id: I35046c7ba53f650c5d42314f226f1df5b7cd8dc1
117 files changed, 3949 insertions, 1512 deletions
diff --git a/framework/src/android/telephony/imsmedia/AudioSessionCallback.java b/framework/src/android/telephony/imsmedia/AudioSessionCallback.java index 7bf74de3..03863175 100644 --- a/framework/src/android/telephony/imsmedia/AudioSessionCallback.java +++ b/framework/src/android/telephony/imsmedia/AudioSessionCallback.java @@ -268,20 +268,11 @@ public class AudioSessionCallback extends ImsMediaManager.SessionCallback { } /** - * Notifies media inactivity observed as per thresholds set by - * setMediaQualityThreshold() API - * - * @param packetType either RTP or RTCP - */ - public void notifyMediaInactivity(final int packetType) { - // Base Implementation - } - - /** * Notifies media quality status observed as per thresholds set by * setMediaQualityThreshold() API * - * @param packetType either RTP or RTCP + * @param status The object of MediaQualityStatus with the rtp and + * the rtcp statistics. */ public void notifyMediaQualityStatus(final MediaQualityStatus status) { // Base Implementation diff --git a/framework/src/android/telephony/imsmedia/IImsVideoSessionCallback.aidl b/framework/src/android/telephony/imsmedia/IImsVideoSessionCallback.aidl index 8de33a39..b48e560d 100644 --- a/framework/src/android/telephony/imsmedia/IImsVideoSessionCallback.aidl +++ b/framework/src/android/telephony/imsmedia/IImsVideoSessionCallback.aidl @@ -35,6 +35,6 @@ oneway interface IImsVideoSessionCallback { void onPeerDimensionChanged(in int width, in int height); void onHeaderExtensionReceived(in List<RtpHeaderExtension> extensions); void notifyMediaInactivity(int packetType); - void notifyPacketLoss(int packetLossPercentage); + void notifyBitrate(int bitate); void notifyVideoDataUsage(long bytes); } diff --git a/framework/src/android/telephony/imsmedia/MediaQualityThreshold.java b/framework/src/android/telephony/imsmedia/MediaQualityThreshold.java index 3f38e89a..42024a87 100644 --- a/framework/src/android/telephony/imsmedia/MediaQualityThreshold.java +++ b/framework/src/android/telephony/imsmedia/MediaQualityThreshold.java @@ -38,6 +38,7 @@ public final class MediaQualityThreshold implements Parcelable { private final int[] mRtpPacketLossRate; private final int[] mRtpJitterMillis; private final boolean mNotifyCurrentStatus; + private final int mVideoBitrateBps; /** @hide **/ public MediaQualityThreshold(Parcel in) { @@ -60,6 +61,7 @@ public final class MediaQualityThreshold implements Parcelable { mRtpJitterMillis[i] = in.readInt(); } mNotifyCurrentStatus = in.readBoolean(); + mVideoBitrateBps = in.readInt(); } /** @hide **/ @@ -74,6 +76,7 @@ public final class MediaQualityThreshold implements Parcelable { mRtpJitterMillis = Arrays.copyOf(builder.mRtpJitterMillis, builder.mRtpJitterMillis.length); mNotifyCurrentStatus = builder.mNotifyCurrentStatus; + mVideoBitrateBps = builder.mVideoBitrateBps; } /** @hide **/ @@ -111,6 +114,10 @@ public final class MediaQualityThreshold implements Parcelable { return mNotifyCurrentStatus; } + public int getVideoBitrateBps() { + return mVideoBitrateBps; + } + @NonNull @Override public String toString() { @@ -122,6 +129,7 @@ public final class MediaQualityThreshold implements Parcelable { + ", mRtpPacketLossRate=" + Arrays.toString(mRtpPacketLossRate) + ", mRtpJitterMillis=" + Arrays.toString(mRtpJitterMillis) + ", mNotifyCurrentStatus=" + mNotifyCurrentStatus + + ", mVideoBitrateBps=" + mVideoBitrateBps + " }"; } @@ -130,7 +138,7 @@ public final class MediaQualityThreshold implements Parcelable { return Objects.hash(Arrays.hashCode(mRtpInactivityTimerMillis), mRtcpInactivityTimerMillis, mRtpHysteresisTimeInMillis, mRtpPacketLossDurationMillis, Arrays.hashCode(mRtpPacketLossRate), Arrays.hashCode(mRtpJitterMillis), - mNotifyCurrentStatus); + mNotifyCurrentStatus, mVideoBitrateBps); } @Override @@ -151,7 +159,8 @@ public final class MediaQualityThreshold implements Parcelable { && mRtpPacketLossDurationMillis == s.mRtpPacketLossDurationMillis && Arrays.equals(mRtpPacketLossRate, s.mRtpPacketLossRate) && Arrays.equals(mRtpJitterMillis, s.mRtpJitterMillis) - && mNotifyCurrentStatus == s.mNotifyCurrentStatus); + && mNotifyCurrentStatus == s.mNotifyCurrentStatus + && mVideoBitrateBps == s.mVideoBitrateBps); } /** @@ -172,6 +181,7 @@ public final class MediaQualityThreshold implements Parcelable { dest.writeIntArray(mRtpPacketLossRate); dest.writeIntArray(mRtpJitterMillis); dest.writeBoolean(mNotifyCurrentStatus); + dest.writeInt(mVideoBitrateBps); } public static final @NonNull Parcelable.Creator<MediaQualityThreshold> @@ -198,6 +208,7 @@ public final class MediaQualityThreshold implements Parcelable { private int[] mRtpPacketLossRate; private int[] mRtpJitterMillis; private boolean mNotifyCurrentStatus; + private int mVideoBitrateBps; /** * Default constructor for Builder. @@ -209,6 +220,7 @@ public final class MediaQualityThreshold implements Parcelable { mRtpPacketLossDurationMillis = 0; mRtpPacketLossRate = new int[0]; mRtpJitterMillis = new int[0]; + mVideoBitrateBps = 0; } /** @@ -303,6 +315,16 @@ public final class MediaQualityThreshold implements Parcelable { } /** + * The receiving bitrate threshold in bps for video call. If it is not zero, bitrate + * notification event is triggered when the receiving frame bitrate is less than the + * threshold. + */ + public @NonNull Builder setVideoBitrateBps(final int bitrate) { + this.mVideoBitrateBps = bitrate; + return this; + } + + /** * Build the MediaQualityThreshold. * * @return the MediaQualityThreshold object. diff --git a/framework/src/android/telephony/imsmedia/VideoSessionCallback.java b/framework/src/android/telephony/imsmedia/VideoSessionCallback.java index cf66ee56..17748bbb 100644 --- a/framework/src/android/telephony/imsmedia/VideoSessionCallback.java +++ b/framework/src/android/telephony/imsmedia/VideoSessionCallback.java @@ -151,12 +151,12 @@ public class VideoSessionCallback extends ImsMediaManager.SessionCallback { } @Override - public void notifyPacketLoss(final int packetLossPercentage) { + public void notifyBitrate(final int bitrate) { if (mLocalCallback == null) return; final long callingIdentity = Binder.clearCallingIdentity(); try { - mExecutor.execute(() -> mLocalCallback.notifyPacketLoss(packetLossPercentage)); + mExecutor.execute(() -> mLocalCallback.notifyBitrate(bitrate)); } finally { restoreCallingIdentity(callingIdentity); } @@ -234,13 +234,12 @@ public class VideoSessionCallback extends ImsMediaManager.SessionCallback { } /** - * Notifies RTP packet loss observed as per thresholds set by + * Notifies when the video bitrate decreased below the threshold set by * setMediaQualityThreshold() API * - * @param packetLossPercentage percentage of packet loss calculated - * over the duration + * @param bitrate The bitrate of sending video packets in bps unit */ - public void notifyPacketLoss(final int packetLossPercentage) { + public void notifyBitrate(final int bitrate) { // Base Implementation } diff --git a/service/src/com/android/telephony/imsmedia/AudioSession.java b/service/src/com/android/telephony/imsmedia/AudioSession.java index 603780c2..dae20c41 100644 --- a/service/src/com/android/telephony/imsmedia/AudioSession.java +++ b/service/src/com/android/telephony/imsmedia/AudioSession.java @@ -103,13 +103,14 @@ public final class AudioSession extends IImsAudioSession.Stub implements IMediaS @VisibleForTesting AudioSession(final int sessionId, - final @NonNull IImsAudioSessionCallback callback, - final @Nullable AudioService audioService, - final @Nullable AudioLocalSession localSession, - final @Nullable AudioOffloadService offloadService) { + @NonNull final IImsAudioSessionCallback callback, + @Nullable final AudioService audioService, + @Nullable final AudioLocalSession localSession, + @Nullable final AudioOffloadService offloadService, + Looper looper) { mSessionId = sessionId; mCallback = callback; - mHandler = new AudioSessionHandler(Looper.getMainLooper()); + mHandler = new AudioSessionHandler(looper); mAudioService = audioService; mLocalSession = localSession; mAudioListener = new AudioListener(mHandler); diff --git a/service/src/com/android/telephony/imsmedia/TextSession.java b/service/src/com/android/telephony/imsmedia/TextSession.java index b0895d8e..dd069a03 100644 --- a/service/src/com/android/telephony/imsmedia/TextSession.java +++ b/service/src/com/android/telephony/imsmedia/TextSession.java @@ -73,10 +73,10 @@ public final class TextSession extends IImsTextSession.Stub implements IMediaSes TextSession(final int sessionId, final @NonNull IImsTextSessionCallback callback, final @Nullable TextService textService, - final @Nullable TextLocalSession localSession) { + final @Nullable TextLocalSession localSession, Looper looper) { mSessionId = sessionId; mCallback = callback; - mHandler = new TextSessionHandler(Looper.getMainLooper()); + mHandler = new TextSessionHandler(looper); mTextService = textService; mLocalSession = localSession; mTextListener = new TextListener(mHandler); diff --git a/service/src/com/android/telephony/imsmedia/Utils.java b/service/src/com/android/telephony/imsmedia/Utils.java index 651413b5..5459e6c8 100644 --- a/service/src/com/android/telephony/imsmedia/Utils.java +++ b/service/src/com/android/telephony/imsmedia/Utils.java @@ -392,6 +392,7 @@ public final class Utils { .setRtpPacketLossRate(in.rtpPacketLossRate) .setRtpJitterMillis(in.rtpJitterMillis) .setNotifyCurrentStatus(in.notifyCurrentStatus) + .setVideoBitrateBps(0) .build(); } diff --git a/service/src/com/android/telephony/imsmedia/VideoListener.java b/service/src/com/android/telephony/imsmedia/VideoListener.java index ce70114b..32122974 100644 --- a/service/src/com/android/telephony/imsmedia/VideoListener.java +++ b/service/src/com/android/telephony/imsmedia/VideoListener.java @@ -92,7 +92,7 @@ public class VideoListener implements JNIImsMediaListener { //TODO: add implementation break; case VideoSession.EVENT_MEDIA_INACTIVITY_IND: - case VideoSession.EVENT_PACKET_LOSS_IND: + case VideoSession.EVENT_NOTIFY_BITRATE_IND: Utils.sendMessage(mHandler, event, parcel.readInt(), Utils.UNUSED); break; case VideoSession.EVENT_VIDEO_DATA_USAGE_IND: diff --git a/service/src/com/android/telephony/imsmedia/VideoSession.java b/service/src/com/android/telephony/imsmedia/VideoSession.java index a2351df9..02d47ad9 100644 --- a/service/src/com/android/telephony/imsmedia/VideoSession.java +++ b/service/src/com/android/telephony/imsmedia/VideoSession.java @@ -59,7 +59,7 @@ public final class VideoSession extends IImsVideoSession.Stub implements IMediaS public static final int EVENT_PEER_DIMENSION_CHANGED = 205; public static final int EVENT_RTP_HEADER_EXTENSION_IND = 206; public static final int EVENT_MEDIA_INACTIVITY_IND = 207; - public static final int EVENT_PACKET_LOSS_IND = 208; + public static final int EVENT_NOTIFY_BITRATE_IND = 208; public static final int EVENT_VIDEO_DATA_USAGE_IND = 209; public static final int EVENT_SESSION_CLOSED = 210; @@ -85,10 +85,10 @@ public final class VideoSession extends IImsVideoSession.Stub implements IMediaS VideoSession(final int sessionId, final @NonNull IImsVideoSessionCallback callback, final @Nullable VideoService videoService, - final @Nullable VideoLocalSession localSession) { + final @Nullable VideoLocalSession localSession, Looper looper) { mSessionId = sessionId; mCallback = callback; - mHandler = new VideoSessionHandler(Looper.getMainLooper()); + mHandler = new VideoSessionHandler(looper); mVideoService = videoService; mLocalSession = localSession; mVideoListener = new VideoListener(mHandler); @@ -233,8 +233,8 @@ public final class VideoSession extends IImsVideoSession.Stub implements IMediaS case EVENT_MEDIA_INACTIVITY_IND: handleNotifyMediaInactivityInd(msg.arg1); break; - case EVENT_PACKET_LOSS_IND: - handleNotifyPacketLossInd(msg.arg1); + case EVENT_NOTIFY_BITRATE_IND: + handleNotifyBitrateInd(msg.arg1); break; case EVENT_VIDEO_DATA_USAGE_IND: handleNotifyVideoDataUsage((long) msg.obj); @@ -344,9 +344,9 @@ public final class VideoSession extends IImsVideoSession.Stub implements IMediaS } } - private void handleNotifyPacketLossInd(int percentage) { + private void handleNotifyBitrateInd(int percentage) { try { - mCallback.notifyPacketLoss(percentage); + mCallback.notifyBitrate(percentage); } catch (RemoteException e) { Log.e(TAG, "Failed to notify packet loss: " + e); } diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/Android.bp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/Android.bp index de09bc98..c92b7a19 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/Android.bp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/Android.bp @@ -23,7 +23,7 @@ soong_config_module_type { module_type: "cc_defaults", config_namespace: "audio_lib", variables: ["audio_type"], - properties: ["static_libs","header_libs"], + properties: ["defaults","static_libs","header_libs"], } soong_config_string_variable { @@ -36,6 +36,9 @@ audio_cc_defaults { soong_config_variables: { audio_type: { oem_audio: { + defaults: [ + "libimsmedia_oem_audio_codec_defaults", + ], static_libs: [ "libimsmedia_oem_audio_codec", ], diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/config/include/MediaQualityThreshold.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/config/include/MediaQualityThreshold.h index ecb33980..d7b7ced3 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/config/include/MediaQualityThreshold.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/config/include/MediaQualityThreshold.h @@ -58,6 +58,8 @@ public: std::vector<int32_t> getRtpJitterMillis() const; void setNotifyCurrentStatus(bool status); bool getNotifyCurrentStatus() const; + void setVideoBitrateBps(int32_t bitrate); + int32_t getVideoBitrateBps() const; private: /** The timer in milliseconds for monitoring RTP inactivity */ @@ -85,6 +87,13 @@ private: * of the current status. */ bool mNotifyCurrentStatus; + + /** + * The receiving bitrate threshold in bps for video call. If it is not zero, bitrate + * notification event is triggered when the receiving frame bitrate is less than the + * threshold. + */ + int mVideoBitrateBps; }; } // namespace imsmedia diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/config/include/RtcpConfig.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/config/include/RtcpConfig.h index c742d883..566497e3 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/config/include/RtcpConfig.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/config/include/RtcpConfig.h @@ -50,7 +50,7 @@ public: * RTCP XR type Loss RLE Report Block as specified in * RFC 3611 section 4.1 */ - FLAG_RTCPXR_LOSS_RLE_REPORT_BLOCK = 1 < 0, + FLAG_RTCPXR_LOSS_RLE_REPORT_BLOCK = 1 << 0, /** * RTCP XR type Duplicate RLE Report Block as specified in * RFC 3611 section 4.2 diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/config/src/MediaQualityThreshold.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/config/src/MediaQualityThreshold.cpp index f0ca1473..c6ecefdd 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/config/src/MediaQualityThreshold.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/config/src/MediaQualityThreshold.cpp @@ -34,6 +34,7 @@ MediaQualityThreshold::MediaQualityThreshold() mRtpPacketLossRate.clear(); mRtpJitterMillis.clear(); mNotifyCurrentStatus = false; + mVideoBitrateBps = 0; } MediaQualityThreshold::MediaQualityThreshold(const MediaQualityThreshold& threshold) @@ -45,6 +46,7 @@ MediaQualityThreshold::MediaQualityThreshold(const MediaQualityThreshold& thresh mRtpPacketLossRate = threshold.mRtpPacketLossRate; mRtpJitterMillis = threshold.mRtpJitterMillis; mNotifyCurrentStatus = threshold.mNotifyCurrentStatus; + mVideoBitrateBps = threshold.mVideoBitrateBps; } MediaQualityThreshold::~MediaQualityThreshold() {} @@ -60,6 +62,7 @@ MediaQualityThreshold& MediaQualityThreshold::operator=(const MediaQualityThresh mRtpPacketLossRate = threshold.mRtpPacketLossRate; mRtpJitterMillis = threshold.mRtpJitterMillis; mNotifyCurrentStatus = threshold.mNotifyCurrentStatus; + mVideoBitrateBps = threshold.mVideoBitrateBps; } return *this; } @@ -72,7 +75,8 @@ bool MediaQualityThreshold::operator==(const MediaQualityThreshold& threshold) c mRtpPacketLossDurationMillis == threshold.mRtpPacketLossDurationMillis && mRtpPacketLossRate == threshold.mRtpPacketLossRate && mRtpJitterMillis == threshold.mRtpJitterMillis && - mNotifyCurrentStatus == threshold.mNotifyCurrentStatus); + mNotifyCurrentStatus == threshold.mNotifyCurrentStatus && + mVideoBitrateBps == threshold.mVideoBitrateBps); } bool MediaQualityThreshold::operator!=(const MediaQualityThreshold& threshold) const @@ -83,7 +87,8 @@ bool MediaQualityThreshold::operator!=(const MediaQualityThreshold& threshold) c mRtpPacketLossDurationMillis != threshold.mRtpPacketLossDurationMillis || mRtpPacketLossRate != threshold.mRtpPacketLossRate || mRtpJitterMillis != threshold.mRtpJitterMillis || - mNotifyCurrentStatus != threshold.mNotifyCurrentStatus); + mNotifyCurrentStatus != threshold.mNotifyCurrentStatus || + mVideoBitrateBps != threshold.mVideoBitrateBps); } status_t MediaQualityThreshold::writeToParcel(Parcel* out) const @@ -113,6 +118,7 @@ status_t MediaQualityThreshold::writeToParcel(Parcel* out) const } out->writeInt32(mNotifyCurrentStatus ? 1 : 0); + out->writeInt32(mVideoBitrateBps); return NO_ERROR; } @@ -149,7 +155,7 @@ status_t MediaQualityThreshold::readFromParcel(const Parcel* in) int32_t value; in->readInt32(&value); value == 1 ? mNotifyCurrentStatus = true : mNotifyCurrentStatus = false; - + in->readInt32(&mVideoBitrateBps); return NO_ERROR; } @@ -223,6 +229,16 @@ bool MediaQualityThreshold::getNotifyCurrentStatus() const return mNotifyCurrentStatus; } +void MediaQualityThreshold::setVideoBitrateBps(int32_t bitrate) +{ + mVideoBitrateBps = bitrate; +} + +int32_t MediaQualityThreshold::getVideoBitrateBps() const +{ + return mVideoBitrateBps; +} + } // namespace imsmedia } // namespace telephony diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/BaseJitterBuffer.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/BaseJitterBuffer.cpp index c039e690..5242fb34 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/BaseJitterBuffer.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/BaseJitterBuffer.cpp @@ -44,7 +44,13 @@ void BaseJitterBuffer::SetSessionCallback(BaseSessionCallback* callback) void BaseJitterBuffer::SetSsrc(uint32_t ssrc) { - IMLOGD1("[SetSsrc] ssrc[%x]", ssrc); + IMLOGI1("[SetSsrc] ssrc[%x]", ssrc); + + if (mSsrc != 0 && ssrc != mSsrc) + { + Reset(); + } + mSsrc = ssrc; } @@ -76,6 +82,7 @@ uint32_t BaseJitterBuffer::GetCount() void BaseJitterBuffer::Reset() { + mFirstFrameReceived = false; mNewInputData = false; mLastPlayedSeqNum = 0; mLastPlayedTimestamp = 0; diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/JitterNetworkAnalyser.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/JitterNetworkAnalyser.cpp index 85757c84..065b013f 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/JitterNetworkAnalyser.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/JitterNetworkAnalyser.cpp @@ -48,6 +48,8 @@ void JitterNetworkAnalyser::Reset() mNetworkStatus = NETWORK_STATUS_NORMAL; mGoodStatusEnteringTime = 0; mBadStatusChangedTime = 0; + + std::lock_guard<std::mutex> guard(mMutex); mListJitters.clear(); } @@ -79,6 +81,8 @@ int32_t JitterNetworkAnalyser::CalculateTransitTimeDifference( int32_t inputTimestampGap = timestamp - mBasePacketTime; int32_t inputTimeGap = arrivalTime - mBaseArrivalTime; int32_t jitter = inputTimeGap - inputTimestampGap; + + std::lock_guard<std::mutex> guard(mMutex); mListJitters.push_back(jitter); if (mListJitters.size() > MAX_JITTER_LIST_SIZE) @@ -91,6 +95,8 @@ int32_t JitterNetworkAnalyser::CalculateTransitTimeDifference( double JitterNetworkAnalyser::CalculateDeviation(double* pMean) { + std::lock_guard<std::mutex> guard(mMutex); + if (mListJitters.empty()) { *pMean = 0; @@ -114,6 +120,8 @@ double JitterNetworkAnalyser::CalculateDeviation(double* pMean) int32_t JitterNetworkAnalyser::GetMaxJitterValue() { + std::lock_guard<std::mutex> guard(mMutex); + if (mListJitters.empty()) { return 0; diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/StreamScheduler.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/StreamScheduler.cpp index d71707c7..9184212c 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/StreamScheduler.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/StreamScheduler.cpp @@ -19,11 +19,12 @@ #include <stdint.h> #include <chrono> #include <thread> +#include <algorithm> using namespace std::chrono; -#define RUN_WAIT_TIMEOUT 6 -#define STOP_WAIT_TIMEOUT 1000 +#define RUN_WAIT_TIMEOUT_MS 1 +#define STOP_WAIT_TIMEOUT_MS 1000 StreamScheduler::StreamScheduler() {} @@ -85,7 +86,7 @@ void StreamScheduler::Stop() { StopThread(); Awake(); - mConditionExit.wait_timeout(STOP_WAIT_TIMEOUT); + mConditionExit.wait_timeout(STOP_WAIT_TIMEOUT_MS); } IMLOGD1("[Stop] [%p] exit", this); @@ -96,53 +97,48 @@ void StreamScheduler::Awake() mConditionMain.signal(); } -BaseNode* StreamScheduler::DetermineProcessingNode() +void StreamScheduler::RunRegisteredNode() { - if (IsThreadStopped()) - { - return nullptr; - } - - BaseNode* pRetNode = nullptr; - uint32_t nMaxDataInNode = 0; + // the list to contain non-source type node + std::list<BaseNode*> listNodesToRun; for (auto& node : mlistRegisteredNode) { - if (node != nullptr && !node->IsRunTime()) + if (node != nullptr && node->GetState() == kNodeStateRunning && !node->IsRunTime()) { - uint32_t nDataInNode = node->GetDataCount(); - - if (nDataInNode > 0 && nDataInNode >= nMaxDataInNode) + if (node->IsSourceNode()) // process the source node + { + node->ProcessData(); + } + else if (node->GetDataCount() > 0) { - pRetNode = node; - nMaxDataInNode = nDataInNode; + listNodesToRun.push_back(node); // store node to run } } } - return pRetNode; -} - -void StreamScheduler::RunRegisteredNode() -{ - for (;;) + while (!listNodesToRun.empty()) { - BaseNode* pNode = DetermineProcessingNode(); - - if (pNode == nullptr) + std::list<BaseNode*>::iterator maxNode = + std::max_element(listNodesToRun.begin(), listNodesToRun.end(), + [=](BaseNode* a, BaseNode* b) + { + return a->GetDataCount() < b->GetDataCount(); + }); + + if (maxNode == listNodesToRun.end()) { break; } - if (pNode->GetState() == kNodeStateRunning) - { - pNode->ProcessData(); - } + (*maxNode)->ProcessData(); // process the non runtime node if (IsThreadStopped()) { break; } + + listNodesToRun.remove(*maxNode); }; } @@ -178,10 +174,10 @@ void* StreamScheduler::run() break; } - mConditionMain.wait_timeout(RUN_WAIT_TIMEOUT / 2); + mConditionMain.wait_timeout(RUN_WAIT_TIMEOUT_MS); } mConditionExit.signal(); IMLOGD1("[run] [%p] exit", this); return nullptr; -} +}
\ No newline at end of file diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioJitterBuffer.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioJitterBuffer.cpp index 7332af54..98bf8014 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioJitterBuffer.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioJitterBuffer.cpp @@ -45,10 +45,11 @@ AudioJitterBuffer::~AudioJitterBuffer() {} void AudioJitterBuffer::Reset() { - mNextJitterBufferSize = mCurrJitterBufferSize; + mFirstFrameReceived = false; + mNewInputData = false; mLastPlayedSeqNum = 0; mLastPlayedTimestamp = 0; - mFirstFrameReceived = false; + mNextJitterBufferSize = mCurrJitterBufferSize; mDtxOn = false; mSIDCount = 0; mWaiting = true; @@ -59,10 +60,20 @@ void AudioJitterBuffer::Reset() mCheckUpdateJitterPacketCnt = 0; mEnforceUpdate = false; mNeedToUpdateBasePacket = false; - BaseJitterBuffer::Reset(); + + mMutex.lock(); + DataEntry* entry = nullptr; + + while (mDataQueue.Get(&entry)) + { + CollectRxRtpStatus(entry->nSeqNum, kRtpStatusDiscarded); + mDataQueue.Delete(); + } + + mMutex.unlock(); + mJitterAnalyzer.Reset(); mJitterAnalyzer.SetMinMaxJitterBufferSize(mMinJitterBufferSize, mMaxJitterBufferSize); - mCurrPlayingSeq = 0; } void AudioJitterBuffer::SetJitterBufferSize(uint32_t nInit, uint32_t nMin, uint32_t nMax) @@ -125,15 +136,7 @@ void AudioJitterBuffer::Add(ImsMediaSubType subtype, uint8_t* pbBuffer, uint32_t if (mCannotGetCount > mMaxJitterBufferSize) { - IMLOGD0("[Add] refreshed"); - DataEntry* resetData = nullptr; - - while (mDataQueue.Get(&resetData)) - { - CollectRxRtpStatus(resetData->nSeqNum, kRtpStatusDiscarded); - Delete(); - } - + IMLOGD0("[Add] reset"); Reset(); } @@ -145,7 +148,7 @@ void AudioJitterBuffer::Add(ImsMediaSubType subtype, uint8_t* pbBuffer, uint32_t mJitterAnalyzer.UpdateBaseTimestamp(mBaseTimestamp, mBaseArrivalTime); } // TODO: remove mBufferIgnoreSIDPacket logic and the statements - else if (mBufferIgnoreSIDPacket && !IsSID(currEntry.pbBuffer, currEntry.nBufferSize)) + else if (mBufferIgnoreSIDPacket && !IsSID(currEntry.nBufferSize)) { // first packet delay compensation if ((mBaseTimestamp == 0 && mBaseArrivalTime == 0) || mNeedToUpdateBasePacket) @@ -194,8 +197,8 @@ void AudioJitterBuffer::Add(ImsMediaSubType subtype, uint8_t* pbBuffer, uint32_t } else { - IsSID(currEntry.pbBuffer, currEntry.nBufferSize) ? packet->rtpDataType = kRtpDataTypeSid - : packet->rtpDataType = kRtpDataTypeNormal; + IsSID(currEntry.nBufferSize) ? packet->rtpDataType = kRtpDataTypeSid + : packet->rtpDataType = kRtpDataTypeNormal; } packet->ssrc = mSsrc; @@ -281,14 +284,20 @@ bool AudioJitterBuffer::Get(ImsMediaSubType* psubtype, uint8_t** ppData, uint32_ if (mDataQueue.GetCount() == 0) { - IMLOGD_PACKET0(IM_PACKET_LOG_JITTER, "[Get] wait - empty"); + IMLOGD_PACKET0(IM_PACKET_LOG_JITTER, "[Get] fail - empty"); + + if (!mWaiting) + { + mCurrPlayingTS += FRAME_INTERVAL; + } + return false; } else if (mDataQueue.Get(&pEntry) && mWaiting) { uint32_t jitterDelay = currentTime - pEntry->arrivalTime; - if (jitterDelay < (mCurrJitterBufferSize - 1) * FRAME_INTERVAL + ALLOWABLE_ERROR) + if (jitterDelay <= (mCurrJitterBufferSize - 1) * FRAME_INTERVAL) { if (psubtype) *psubtype = MEDIASUBTYPE_UNDEFINED; @@ -306,16 +315,22 @@ bool AudioJitterBuffer::Get(ImsMediaSubType* psubtype, uint8_t** ppData, uint32_ IMLOGD_PACKET4(IM_PACKET_LOG_JITTER, "[Get] Wait - seq[%u], CurrJBSize[%u], delay[%u], QueueCount[%u]", pEntry->nSeqNum, mCurrJitterBufferSize, jitterDelay, mDataQueue.GetCount()); - mCannotGetCount++; return false; } else { - mWaiting = false; // once waiting is reset, it will not set true until resync - - // the first frame of voice term - mCurrPlayingTS = pEntry->nTimestamp; - mCurrPlayingSeq = pEntry->nSeqNum; + // resync until the frame delay is lower than current jitter buffer size + if (Resync(currentTime)) + { + mWaiting = false; + } + else + { + IMLOGD_PACKET4(IM_PACKET_LOG_JITTER, + "[Get] Wait - seq[%u], CurrJBSize[%u], delay[%u], QueueCount[%u]", + pEntry->nSeqNum, mCurrJitterBufferSize, jitterDelay, mDataQueue.GetCount()); + return false; + } } } @@ -325,9 +340,8 @@ bool AudioJitterBuffer::Get(ImsMediaSubType* psubtype, uint8_t** ppData, uint32_ (pEntry->nTimestamp < (mCurrPlayingTS + ALLOWABLE_ERROR))) { mCurrPlayingTS = pEntry->nTimestamp; - mCurrPlayingSeq = pEntry->nSeqNum; IMLOGD_PACKET2(IM_PACKET_LOG_JITTER, "[Get] sync playing TS[%u], seq[%d]", mCurrPlayingTS, - mCurrPlayingSeq); + pEntry->nSeqNum); } while (mDataQueue.Get(&pEntry)) @@ -341,13 +355,11 @@ bool AudioJitterBuffer::Get(ImsMediaSubType* psubtype, uint8_t** ppData, uint32_ if (mDataQueue.GetCount() >= nTempBuferSize) { mCurrPlayingTS = pEntry->nTimestamp; - mCurrPlayingSeq = pEntry->nSeqNum; } else { mCurrPlayingTS = pEntry->nTimestamp - (nTempBuferSize - mDataQueue.GetCount()) * FRAME_INTERVAL; - mCurrPlayingSeq = 0; } mNeedToUpdateBasePacket = true; @@ -372,7 +384,7 @@ bool AudioJitterBuffer::Get(ImsMediaSubType* psubtype, uint8_t** ppData, uint32_ } else // late arrival { - if (IsSID(pEntry->pbBuffer, pEntry->nBufferSize)) + if (IsSID(pEntry->nBufferSize)) { mSIDCount++; mDtxOn = true; @@ -392,7 +404,7 @@ bool AudioJitterBuffer::Get(ImsMediaSubType* psubtype, uint8_t** ppData, uint32_ // decrease jitter buffer if (mDtxOn && mSIDCount > 4 && mDataQueue.GetCount() > mCurrJitterBufferSize) { - if (mDataQueue.Get(&pEntry) && IsSID(pEntry->pbBuffer, pEntry->nBufferSize)) + if (mDataQueue.Get(&pEntry) && IsSID(pEntry->nBufferSize)) { IMLOGD_PACKET5(IM_PACKET_LOG_JITTER, "[Get] delete SID - seq[%d], mark[%d], TS[%u], currTS[%u], queue[%d]", @@ -429,7 +441,7 @@ bool AudioJitterBuffer::Get(ImsMediaSubType* psubtype, uint8_t** ppData, uint32_ pEntry->nSeqNum, pEntry->bMark, pEntry->nTimestamp, mCurrPlayingTS, mDataQueue.GetCount()); - if (IsSID(pEntry->pbBuffer, pEntry->nBufferSize)) + if (IsSID(pEntry->nBufferSize)) { mSIDCount++; mDtxOn = true; @@ -487,7 +499,7 @@ bool AudioJitterBuffer::Get(ImsMediaSubType* psubtype, uint8_t** ppData, uint32_ if (pnSeqNum) *pnSeqNum = pEntry->nSeqNum; - if (IsSID(pEntry->pbBuffer, pEntry->nBufferSize)) + if (IsSID(pEntry->nBufferSize)) { mSIDCount++; mDtxOn = true; @@ -512,12 +524,11 @@ bool AudioJitterBuffer::Get(ImsMediaSubType* psubtype, uint8_t** ppData, uint32_ } IMLOGD_PACKET7(IM_PACKET_LOG_JITTER, - "[Get] OK - dtx[%d], curTS[%u], seq[%d], mark[%d], TS[%u], size[%d], queue[%d]", - mDtxOn, mCurrPlayingTS, pEntry->nSeqNum, pEntry->bMark, pEntry->nTimestamp, - pEntry->nBufferSize, mDataQueue.GetCount()); + "[Get] OK - dtx[%d], curTS[%u], seq[%u], TS[%u], size[%u], delay[%u], queue[%u]", + mDtxOn, mCurrPlayingTS, pEntry->nSeqNum, pEntry->nTimestamp, pEntry->nBufferSize, + currentTime - pEntry->arrivalTime, mDataQueue.GetCount()); mCurrPlayingTS = pEntry->nTimestamp + FRAME_INTERVAL; - mCurrPlayingSeq = pEntry->nSeqNum + 1; mFirstFrameReceived = true; mLastPlayedSeqNum = pEntry->nSeqNum; mCannotGetCount = 0; @@ -552,28 +563,24 @@ bool AudioJitterBuffer::Get(ImsMediaSubType* psubtype, uint8_t** ppData, uint32_ mCurrPlayingTS); mCurrPlayingTS += FRAME_INTERVAL; - mCurrPlayingSeq++; return false; } return false; } -bool AudioJitterBuffer::IsSID(uint8_t* /*pbBuffer*/, uint32_t nBufferSize) +bool AudioJitterBuffer::IsSID(uint32_t frameSize) { switch (mCodecType) { case kAudioCodecAmr: case kAudioCodecAmrWb: - if (nBufferSize == 5) - return true; - else - return false; case kAudioCodecEvs: - if ((nBufferSize == 6) || (nBufferSize == 5)) + if (frameSize == 6 || frameSize == 5) + { return true; - else - return false; + } + break; case kAudioCodecPcmu: case kAudioCodecPcma: return false; @@ -581,6 +588,39 @@ bool AudioJitterBuffer::IsSID(uint8_t* /*pbBuffer*/, uint32_t nBufferSize) IMLOGE1("[IsSID] DTX detect method is not defined for[%u] codec", mCodecType); return false; } + + return false; +} + +bool AudioJitterBuffer::Resync(uint32_t currentTime) +{ + IMLOGD0("[Resync]"); + DataEntry* entry = nullptr; + + while (mDataQueue.Get(&entry)) + { + uint32_t timeDiff = currentTime - entry->arrivalTime; + + if (timeDiff > mCurrJitterBufferSize * FRAME_INTERVAL + ALLOWABLE_ERROR) + { + CollectRxRtpStatus(entry->nSeqNum, kRtpStatusDiscarded); + mDataQueue.Delete(); + } + else + { + if (!IsSID(entry->nBufferSize) || + timeDiff > (mCurrJitterBufferSize - 1) * FRAME_INTERVAL) + { + mCurrPlayingTS = entry->nTimestamp; + IMLOGD2("[Resync] currTs[%d], delay[%d]", mCurrPlayingTS, timeDiff); + return true; + } + + break; + } + } + + return false; } void AudioJitterBuffer::CollectRxRtpStatus(int32_t seq, kRtpPacketStatus status) 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 49efef54..e957cc3b 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 @@ -23,9 +23,18 @@ using namespace android; AudioManager* AudioManager::sManager = nullptr; -AudioManager::AudioManager() {} +AudioManager::AudioManager() +{ + mRequestHandler.Init("AUDIO_REQUEST_EVENT"); + mResponseHandler.Init("AUDIO_RESPONSE_EVENT"); +} -AudioManager::~AudioManager() {} +AudioManager::~AudioManager() +{ + mRequestHandler.Deinit(); + mResponseHandler.Deinit(); + sManager = nullptr; +} AudioManager* AudioManager::getInstance() { @@ -203,6 +212,22 @@ void AudioManager::setMediaQualityThreshold(int sessionId, MediaQualityThreshold } } +void AudioManager::SendInternalEvent( + uint32_t event, uint64_t sessionId, uint64_t paramA, uint64_t paramB) +{ + auto session = mSessions.find(sessionId); + IMLOGI1("[SendInternalEvent] sessionId[%d]", sessionId); + + if (session != mSessions.end()) + { + (session->second)->SendInternalEvent(event, paramA, paramB); + } + else + { + IMLOGE1("[SendInternalEvent] no session id[%d]", sessionId); + } +} + void AudioManager::sendMessage(const int sessionId, const android::Parcel& parcel) { int nMsg = parcel.readInt32(); @@ -283,35 +308,19 @@ void AudioManager::sendMessage(const int sessionId, const android::Parcel& parce } } -void AudioManager::SendInternalEvent( - uint32_t event, uint64_t sessionId, uint64_t paramA, uint64_t paramB) -{ - auto session = mSessions.find(sessionId); - IMLOGI1("[SendInternalEvent] sessionId[%d]", sessionId); - - if (session != mSessions.end()) - { - (session->second)->SendInternalEvent(event, paramA, paramB); - } - else - { - IMLOGE1("[SendInternalEvent] no session id[%d]", sessionId); - } -} - -AudioManager::RequestHandler::RequestHandler() : - ImsMediaEventHandler("AUDIO_REQUEST_EVENT") -{ -} - -AudioManager::RequestHandler::~RequestHandler() {} - void AudioManager::RequestHandler::processEvent( uint32_t event, uint64_t sessionId, uint64_t paramA, uint64_t paramB) { IMLOGI4("[processEvent] event[%d], sessionId[%d], paramA[%d], paramB[%d]", event, sessionId, paramA, paramB); ImsMediaResult result = RESULT_SUCCESS; + + if (sManager == nullptr) + { + IMLOGE0("[processEvent] not ready"); + return; + } + switch (event) { case kAudioOpenSession: @@ -320,7 +329,7 @@ void AudioManager::RequestHandler::processEvent( if (param != nullptr) { AudioConfig* pConfig = reinterpret_cast<AudioConfig*>(param->mConfig); - result = AudioManager::getInstance()->openSession( + result = sManager->openSession( static_cast<int>(sessionId), param->rtpFd, param->rtcpFd, pConfig); if (result == RESULT_SUCCESS) @@ -349,8 +358,7 @@ void AudioManager::RequestHandler::processEvent( } break; case kAudioCloseSession: - if (AudioManager::getInstance()->closeSession(static_cast<int>(sessionId)) == - RESULT_SUCCESS) + if (sManager->closeSession(static_cast<int>(sessionId)) == RESULT_SUCCESS) { ImsMediaEventHandler::SendEvent( "AUDIO_RESPONSE_EVENT", kAudioSessionClosed, sessionId, 0, 0); @@ -359,8 +367,7 @@ void AudioManager::RequestHandler::processEvent( case kAudioModifySession: { AudioConfig* config = reinterpret_cast<AudioConfig*>(paramA); - result = - AudioManager::getInstance()->modifySession(static_cast<int>(sessionId), config); + result = sManager->modifySession(static_cast<int>(sessionId), config); ImsMediaEventHandler::SendEvent( "AUDIO_RESPONSE_EVENT", kAudioModifySessionResponse, sessionId, result, paramA); } @@ -368,7 +375,7 @@ void AudioManager::RequestHandler::processEvent( case kAudioAddConfig: { AudioConfig* config = reinterpret_cast<AudioConfig*>(paramA); - result = AudioManager::getInstance()->addConfig(static_cast<int>(sessionId), config); + result = sManager->addConfig(static_cast<int>(sessionId), config); ImsMediaEventHandler::SendEvent( "AUDIO_RESPONSE_EVENT", kAudioAddConfigResponse, sessionId, result, paramA); } @@ -376,8 +383,7 @@ void AudioManager::RequestHandler::processEvent( case kAudioConfirmConfig: { AudioConfig* config = reinterpret_cast<AudioConfig*>(paramA); - result = - AudioManager::getInstance()->confirmConfig(static_cast<int>(sessionId), config); + result = sManager->confirmConfig(static_cast<int>(sessionId), config); ImsMediaEventHandler::SendEvent( "AUDIO_RESPONSE_EVENT", kAudioConfirmConfigResponse, sessionId, result, paramA); } @@ -387,7 +393,7 @@ void AudioManager::RequestHandler::processEvent( AudioConfig* config = reinterpret_cast<AudioConfig*>(paramA); if (config != nullptr) { - AudioManager::getInstance()->deleteConfig(static_cast<int>(sessionId), config); + sManager->deleteConfig(static_cast<int>(sessionId), config); delete config; } } @@ -397,8 +403,7 @@ void AudioManager::RequestHandler::processEvent( EventParamDtmf* param = reinterpret_cast<EventParamDtmf*>(paramA); if (param != nullptr) { - AudioManager::getInstance()->sendDtmf( - static_cast<int>(sessionId), param->digit, param->duration); + sManager->sendDtmf(static_cast<int>(sessionId), param->digit, param->duration); delete param; } } @@ -410,8 +415,7 @@ void AudioManager::RequestHandler::processEvent( if (listExtension != nullptr) { - AudioManager::getInstance()->sendRtpHeaderExtension( - static_cast<int>(sessionId), listExtension); + sManager->sendRtpHeaderExtension(static_cast<int>(sessionId), listExtension); delete listExtension; } } @@ -421,34 +425,32 @@ void AudioManager::RequestHandler::processEvent( MediaQualityThreshold* threshold = reinterpret_cast<MediaQualityThreshold*>(paramA); if (threshold != nullptr) { - AudioManager::getInstance()->setMediaQualityThreshold( - static_cast<int>(sessionId), threshold); + sManager->setMediaQualityThreshold(static_cast<int>(sessionId), threshold); delete threshold; } } break; case kRequestAudioCmr: case kRequestSendRtcpXrReport: - AudioManager::getInstance()->SendInternalEvent( - event, static_cast<int>(sessionId), paramA, paramB); + sManager->SendInternalEvent(event, static_cast<int>(sessionId), paramA, paramB); break; default: break; } } -AudioManager::ResponseHandler::ResponseHandler() : - ImsMediaEventHandler("AUDIO_RESPONSE_EVENT") -{ -} - -AudioManager::ResponseHandler::~ResponseHandler() {} - void AudioManager::ResponseHandler::processEvent( uint32_t event, uint64_t sessionId, uint64_t paramA, uint64_t paramB) { IMLOGI4("[processEvent] event[%d], sessionId[%d], paramA[%d], paramB[%d]", event, sessionId, paramA, paramB); + + if (sManager == nullptr) + { + IMLOGE0("[processEvent] not ready"); + return; + } + android::Parcel parcel; switch (event) { @@ -461,7 +463,7 @@ void AudioManager::ResponseHandler::processEvent( // fail reason parcel.writeInt32(static_cast<int>(paramA)); } - AudioManager::getInstance()->sendResponse(sessionId, parcel); + sManager->sendResponse(sessionId, parcel); break; case kAudioModifySessionResponse: // fall through case kAudioAddConfigResponse: // fall through @@ -473,7 +475,7 @@ void AudioManager::ResponseHandler::processEvent( if (config != nullptr) { config->writeToParcel(&parcel); - AudioManager::getInstance()->sendResponse(sessionId, parcel); + sManager->sendResponse(sessionId, parcel); delete config; } } @@ -485,7 +487,7 @@ void AudioManager::ResponseHandler::processEvent( if (config != nullptr) { config->writeToParcel(&parcel); - AudioManager::getInstance()->sendResponse(sessionId, parcel); + sManager->sendResponse(sessionId, parcel); delete config; } } @@ -505,7 +507,7 @@ void AudioManager::ResponseHandler::processEvent( extension.writeToParcel(&parcel); } - AudioManager::getInstance()->sendResponse(sessionId, parcel); + sManager->sendResponse(sessionId, parcel); delete listExtension; } } @@ -517,7 +519,7 @@ void AudioManager::ResponseHandler::processEvent( if (status != nullptr) { status->writeToParcel(&parcel); - AudioManager::getInstance()->sendResponse(sessionId, parcel); + sManager->sendResponse(sessionId, parcel); delete status; } } @@ -529,7 +531,7 @@ void AudioManager::ResponseHandler::processEvent( parcel.writeInt32(event); parcel.writeByte(static_cast<uint8_t>(paramA)); parcel.writeInt32(static_cast<int>(paramB)); - AudioManager::getInstance()->sendResponse(sessionId, parcel); + sManager->sendResponse(sessionId, parcel); break; case kAudioCallQualityChangedInd: { @@ -538,7 +540,7 @@ void AudioManager::ResponseHandler::processEvent( if (quality != nullptr) { quality->writeToParcel(&parcel); - AudioManager::getInstance()->sendResponse(sessionId, parcel); + sManager->sendResponse(sessionId, parcel); delete quality; } } @@ -546,7 +548,7 @@ void AudioManager::ResponseHandler::processEvent( case kAudioSessionClosed: parcel.writeInt32(event); parcel.writeInt32(static_cast<int>(sessionId)); - AudioManager::getInstance()->sendResponse(sessionId, parcel); + sManager->sendResponse(sessionId, parcel); break; default: break; 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 13367521..f872d730 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 @@ -33,6 +33,8 @@ AudioSession::~AudioSession() { IMLOGD0("[~AudioSession]"); + mMediaQualityAnalyzer->stop(); + while (mListGraphRtpTx.size() > 0) { AudioStreamGraphRtpTx* graph = mListGraphRtpTx.front(); @@ -71,8 +73,6 @@ AudioSession::~AudioSession() mListGraphRtcp.pop_front(); delete graph; } - - mMediaQualityAnalyzer->stop(); } SessionState AudioSession::getState() @@ -125,10 +125,8 @@ ImsMediaResult AudioSession::startGraph(RtpConfig* config) IMLOGI1("[startGraph] state[%d]", getState()); - if (mMediaQualityAnalyzer != nullptr && - !mMediaQualityAnalyzer->isSameConfig(reinterpret_cast<AudioConfig*>(config))) + if (mMediaQualityAnalyzer != nullptr) { - mMediaQualityAnalyzer->stop(); mMediaQualityAnalyzer->setConfig(reinterpret_cast<AudioConfig*>(config)); mMediaQualityAnalyzer->start(); } diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioStreamGraphRtcp.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioStreamGraphRtcp.cpp index e238f068..fa42bffd 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioStreamGraphRtcp.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioStreamGraphRtcp.cpp @@ -92,7 +92,7 @@ ImsMediaResult AudioStreamGraphRtcp::update(RtpConfig* config) if (*reinterpret_cast<AudioConfig*>(mConfig) == *pConfig) { - IMLOGD0("[update] no update"); + IMLOGI0("[update] no update"); return RESULT_SUCCESS; } @@ -104,7 +104,7 @@ ImsMediaResult AudioStreamGraphRtcp::update(RtpConfig* config) if (mConfig->getMediaDirection() == RtpConfig::MEDIA_DIRECTION_NO_FLOW) { - IMLOGD0("[update] pause RTCP"); + IMLOGI0("[update] pause RTCP"); return stop(); } @@ -138,6 +138,22 @@ ImsMediaResult AudioStreamGraphRtcp::update(RtpConfig* config) return ret; } +ImsMediaResult AudioStreamGraphRtcp::start() +{ + if (mConfig == nullptr) + { + return RESULT_NOT_READY; + } + + if (mConfig->getMediaDirection() != RtpConfig::MEDIA_DIRECTION_NO_FLOW) + { + return BaseStreamGraph::start(); + } + + // not started + return RESULT_SUCCESS; +} + bool AudioStreamGraphRtcp::OnEvent(int32_t type, uint64_t param1, uint64_t param2) { IMLOGI3("[onEvent] type[%d], param1[%d], param2[%d]", type, param1, param2); diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioStreamGraphRtpRx.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioStreamGraphRtpRx.cpp index 70963147..233284f5 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioStreamGraphRtpRx.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioStreamGraphRtpRx.cpp @@ -88,7 +88,7 @@ ImsMediaResult AudioStreamGraphRtpRx::update(RtpConfig* config) if (*mConfig == *pConfig) { - IMLOGD0("[update] no update"); + IMLOGI0("[update] no update"); return RESULT_SUCCESS; } 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 ae8624a5..f797c117 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 @@ -95,7 +95,7 @@ ImsMediaResult AudioStreamGraphRtpTx::update(RtpConfig* config) if (*reinterpret_cast<AudioConfig*>(mConfig) == *pConfig) { - IMLOGD0("[update] no update"); + IMLOGI0("[update] no update"); return RESULT_SUCCESS; } diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/MediaQualityAnalyzer.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/MediaQualityAnalyzer.cpp index 55c2568b..4661ee0e 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/MediaQualityAnalyzer.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/MediaQualityAnalyzer.cpp @@ -24,6 +24,9 @@ #include <AudioConfig.h> #include <stdlib.h> #include <algorithm> +#include <numeric> + +using namespace android::telephony::imsmedia; #define DEFAULT_PARAM (-1) #define DEFAULT_INACTIVITY_TIME_FOR_CALL_QUALITY (4) @@ -33,12 +36,17 @@ #define TIMER_INTERVAL (1000) // 1 sec #define STOP_TIMEOUT (1000) // 1 sec #define MESSAGE_PROCESSING_INTERVAL (20000) // 20 msec +#define MEDIA_DIRECTION_CONTAINS_RECEIVE(a) \ + ((a) == RtpConfig::MEDIA_DIRECTION_SEND_RECEIVE || \ + (a) == RtpConfig::MEDIA_DIRECTION_RECEIVE_ONLY) MediaQualityAnalyzer::MediaQualityAnalyzer() { mTimeStarted = 0; mCodecType = 0; mCodecAttribute = 0; + mIsRxRtpEnabled = false; + mIsRtcpEnabled = false; mCallback = nullptr; std::unique_ptr<RtcpXrEncoder> analyzer(new RtcpXrEncoder()); mRtcpXrEncoder = std::move(analyzer); @@ -66,10 +74,17 @@ MediaQualityAnalyzer::~MediaQualityAnalyzer() void MediaQualityAnalyzer::setConfig(AudioConfig* config) { + if (!isSameConfig(config)) + { + reset(); + } + + mIsRxRtpEnabled = MEDIA_DIRECTION_CONTAINS_RECEIVE(config->getMediaDirection()); mCodecType = config->getCodecType(); mCodecAttribute = config->getEvsParams().getEvsBandwidth(); - IMLOGD2("[setCodecType] type[%d], bandwidth[%d]", mCodecType, mCodecAttribute); + mCallQuality.setCodecType(convertAudioCodecType( + mCodecType, ImsMediaAudioUtil::FindMaxEvsBandwidthFromRange(mCodecAttribute))); if (mCodecType == AudioConfig::CODEC_AMR) { @@ -79,6 +94,20 @@ void MediaQualityAnalyzer::setConfig(AudioConfig* config) { mRtcpXrEncoder->setSamplingRate(16); } + + // Enable RTCP if both interval and direction is valid + bool isRtcpEnabled = (config->getRtcpConfig().getIntervalSec() > 0 && + config->getMediaDirection() != RtpConfig::MEDIA_DIRECTION_NO_FLOW); + + if (mIsRtcpEnabled != isRtcpEnabled) + { + mIsRtcpEnabled = isRtcpEnabled; + mCountRtcpInactivity = 0; + mNumRtcpPacketReceived = 0; + } + + IMLOGI4("[setConfig] codec type[%d], bandwidth[%d], rxRtp[%d], rtcp[%d]", mCodecType, + mCodecAttribute, mIsRxRtpEnabled, mIsRtcpEnabled); } void MediaQualityAnalyzer::setCallback(BaseSessionCallback* callback) @@ -111,16 +140,18 @@ void MediaQualityAnalyzer::setMediaQualityThreshold(const MediaQualityThreshold& bool MediaQualityAnalyzer::isSameConfig(AudioConfig* config) { return (mCodecType == config->getCodecType() && - mCodecAttribute == config->getEvsParams().getEvsBandwidth()); + mCodecAttribute == config->getEvsParams().getEvsBandwidth() && + mIsRxRtpEnabled == MEDIA_DIRECTION_CONTAINS_RECEIVE(config->getMediaDirection())); } void MediaQualityAnalyzer::start() { - IMLOGD0("[start]"); - mCallQuality.setCodecType(convertAudioCodecType( - mCodecType, ImsMediaAudioUtil::FindMaxEvsBandwidthFromRange(mCodecAttribute))); - mTimeStarted = ImsMediaTimer::GetTimeInMilliSeconds(); - StartThread(); + if (IsThreadStopped()) + { + IMLOGD0("[start]"); + mTimeStarted = ImsMediaTimer::GetTimeInMilliSeconds(); + StartThread(); + } } void MediaQualityAnalyzer::stop() @@ -155,12 +186,7 @@ void MediaQualityAnalyzer::collectInfo(const int32_t streamType, RtpPacket* pack } else if (streamType == kStreamRtpRx && packet != nullptr) { - if (mSSRC != DEFAULT_PARAM && mSSRC != packet->ssrc) - { - IMLOGW0("[collectInfo] ssrc changed"); - } - - // for call qualty report + // for call quality report mCallQuality.setNumRtpPacketsReceived(mCallQuality.getNumRtpPacketsReceived() + 1); mCallQualitySumRelativeJitter += packet->jitter; @@ -186,8 +212,8 @@ void MediaQualityAnalyzer::collectInfo(const int32_t streamType, RtpPacket* pack break; } - // for loss rate, jitter check - if (mSSRC == DEFAULT_PARAM) // stream is reset + // for jitter check + if (mSSRC != packet->ssrc) // stream is reset { mJitterRxPacket = std::abs(packet->jitter); // update rtcp-xr params @@ -396,7 +422,7 @@ void MediaQualityAnalyzer::processData(const int32_t timeCount) void MediaQualityAnalyzer::processMediaQuality() { // media quality rtp inactivity - if (mNumRxPacket == 0) + if (mNumRxPacket == 0 && mIsRxRtpEnabled) { mCountRtpInactivity += 1000; } @@ -408,7 +434,7 @@ void MediaQualityAnalyzer::processMediaQuality() } // media quality rtcp inactivity - if (mNumRtcpPacketReceived == 0) + if (mNumRtcpPacketReceived == 0 && mIsRtcpEnabled) { mCountRtcpInactivity += 1000; } @@ -424,7 +450,7 @@ void MediaQualityAnalyzer::processMediaQuality() if (mPacketLossDuration != 0 && !mListLostPacket.empty()) { - // calculate loss in duration + // counts received packets for the duration int32_t numReceivedPacketsInDuration = std::count_if(mListRxPacket.begin(), mListRxPacket.end(), [=](RtpPacket* packet) @@ -433,12 +459,21 @@ void MediaQualityAnalyzer::processMediaQuality() mPacketLossDuration); }); + // cumulates the number of lost packets for the duration + std::list<LostPacket*> listLostPacketInDuration; + std::copy_if(mListLostPacket.begin(), mListLostPacket.end(), + std::back_inserter(listLostPacketInDuration), + [=](LostPacket* packet) + { + return (ImsMediaTimer::GetTimeInMilliSeconds() - packet->markedTime <= + mPacketLossDuration); + }); + int32_t numLostPacketsInDuration = - std::count_if(mListLostPacket.begin(), mListLostPacket.end(), - [=](LostPacket* packet) + std::accumulate(begin(listLostPacketInDuration), end(listLostPacketInDuration), 0, + [=](int i, const LostPacket* packet) { - return (ImsMediaTimer::GetTimeInMilliSeconds() - packet->markedTime <= - mPacketLossDuration); + return packet->numLoss + i; }); if (numLostPacketsInDuration == 0 || numReceivedPacketsInDuration == 0) @@ -450,8 +485,8 @@ void MediaQualityAnalyzer::processMediaQuality() int32_t lossRate = numLostPacketsInDuration * 100 / (numReceivedPacketsInDuration + numLostPacketsInDuration); - IMLOGD3("[processData] mediaQualtyStatus lossRate[%d], received[%d], lost[%d]", - lossRate, numReceivedPacketsInDuration, numLostPacketsInDuration); + IMLOGD3("[processMediaQuality] lossRate[%d], received[%d], lost[%d]", lossRate, + numReceivedPacketsInDuration, numLostPacketsInDuration); mQualityStatus.setRtpPacketLossRate(lossRate); } } @@ -460,6 +495,27 @@ void MediaQualityAnalyzer::processMediaQuality() mQualityStatus.setRtpPacketLossRate(0); } + bool shouldNotify = false; + + // check jitter notification + if (!mJitterThreshold.empty() && mIsRxRtpEnabled) + { + if (mJitterChecker.checkNotifiable(mJitterThreshold, mQualityStatus.getRtpJitterMillis())) + { + shouldNotify = true; + } + } + + // check packet loss notification + if (!mPacketLossThreshold.empty() && mIsRxRtpEnabled) + { + if (mPacketLossChecker.checkNotifiable( + mPacketLossThreshold, mQualityStatus.getRtpPacketLossRate())) + { + shouldNotify = true; + } + } + IMLOGD_PACKET4(IM_PACKET_LOG_RTP, "[processMediaQuality] rtpInactivity[%d], rtcpInactivity[%d], lossRate[%d], " "jitter[%d]", @@ -474,7 +530,7 @@ void MediaQualityAnalyzer::processMediaQuality() return; } - if (!mCurrentRtpInactivityTimes.empty()) + if (!mCurrentRtpInactivityTimes.empty() && mIsRxRtpEnabled) { std::vector<int32_t>::iterator rtpIter = std::find_if(mCurrentRtpInactivityTimes.begin(), mCurrentRtpInactivityTimes.end(), @@ -492,32 +548,16 @@ void MediaQualityAnalyzer::processMediaQuality() } } - if (mRtcpInactivityTime != 0 && mCountRtcpInactivity == mRtcpInactivityTime) + if (mRtcpInactivityTime != 0 && mCountRtcpInactivity == mRtcpInactivityTime && mIsRtcpEnabled) { notifyMediaQualityStatus(); mCountRtcpInactivity = 0; return; } - // check jitter notification - if (!mJitterThreshold.empty()) + if (shouldNotify) { - if (mJitterChecker.checkNotifiable(mJitterThreshold, mQualityStatus.getRtpJitterMillis())) - { - notifyMediaQualityStatus(); - return; - } - } - - // check packet loss notification - if (!mPacketLossThreshold.empty()) - { - if (mPacketLossChecker.checkNotifiable( - mPacketLossThreshold, mQualityStatus.getRtpPacketLossRate())) - { - notifyMediaQualityStatus(); - return; - } + notifyMediaQualityStatus(); } } @@ -585,7 +625,11 @@ uint32_t MediaQualityAnalyzer::getTxPacketSize() uint32_t MediaQualityAnalyzer::getLostPacketSize() { - return mListLostPacket.size(); + return std::accumulate(begin(mListLostPacket), end(mListLostPacket), 0, + [](int i, const LostPacket* packet) + { + return packet->numLoss + i; + }); } void MediaQualityAnalyzer::SendEvent(uint32_t event, uint64_t paramA, uint64_t paramB) @@ -740,6 +784,17 @@ void MediaQualityAnalyzer::reset() mNumRxPacket = 0; mNumLostPacket = 0; mJitterRxPacket = 0.0; + + // rtp and rtcp inactivity + mCountRtpInactivity = 0; + mCountRtcpInactivity = 0; + mNumRtcpPacketReceived = 0; + + // reset the status + mQualityStatus = MediaQualityStatus(); + + mPacketLossChecker.initialize(mRtpHysteresisTime); + mJitterChecker.initialize(mRtpHysteresisTime); } void MediaQualityAnalyzer::clearPacketList(std::list<RtpPacket*>& list, const int32_t seq) diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/android/ImsMediaAudioPlayer.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/android/ImsMediaAudioPlayer.cpp index 2f71f13e..99ba77cc 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/android/ImsMediaAudioPlayer.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/android/ImsMediaAudioPlayer.cpp @@ -40,10 +40,19 @@ ImsMediaAudioPlayer::ImsMediaAudioPlayer() { mAudioStream = nullptr; mCodec = nullptr; + mFormat = nullptr; + mCodecType = 0; + mCodecMode = 0; mSamplingRate = DEFAULT_SAMPLING_RATE; + mEvsChAwOffset = 0; + mEvsBandwidth = kEvsBandwidthNone; + memset(mBuffer, 0, sizeof(mBuffer)); + mEvsBitRate = 0; mEvsCodecHeaderMode = kRtpPyaloadHeaderModeEvsHeaderFull; - mFirstFrame = false; + mIsFirstFrame = false; mIsEvsInitialized = false; + mIsOctetAligned = false; + mIsDtxEnabled = false; } ImsMediaAudioPlayer::~ImsMediaAudioPlayer() {} @@ -85,6 +94,16 @@ void ImsMediaAudioPlayer::SetCodecMode(uint32_t mode) mCodecMode = mode; } +void ImsMediaAudioPlayer::SetDtxEnabled(bool isDtxEnabled) +{ + mIsDtxEnabled = isDtxEnabled; +} + +void ImsMediaAudioPlayer::SetOctetAligned(bool isOctetAligned) +{ + mIsOctetAligned = isOctetAligned; +} + bool ImsMediaAudioPlayer::Start() { char kMimeType[128] = {'\0'}; @@ -368,10 +387,10 @@ bool ImsMediaAudioPlayer::decodeEvs(uint8_t* buffer, uint32_t size) mIsEvsInitialized = true; } - if (!mFirstFrame) + if (!mIsFirstFrame) { IMLOGD0("[decodeEvs] First frame has been decoded"); - mFirstFrame = true; + mIsFirstFrame = true; } AAudioStream_write(mAudioStream, output, (decodeSize / 2), 0); diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/android/ImsMediaAudioSource.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/android/ImsMediaAudioSource.cpp index 9117e0ad..d383cfd1 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/android/ImsMediaAudioSource.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/android/ImsMediaAudioSource.cpp @@ -50,6 +50,8 @@ ImsMediaAudioSource::ImsMediaAudioSource() mEvsChAwOffset = 0; mIsEvsInitialized = false; mMediaDirection = 0; + mIsDtxEnabled = false; + mIsOctetAligned = false; } ImsMediaAudioSource::~ImsMediaAudioSource() {} @@ -104,6 +106,16 @@ void ImsMediaAudioSource::SetMediaDirection(int32_t direction) mMediaDirection = direction; } +void ImsMediaAudioSource::SetDtxEnabled(bool isDtxEnabled) +{ + mIsDtxEnabled = isDtxEnabled; +} + +void ImsMediaAudioSource::SetOctetAligned(bool isOctetAligned) +{ + mIsOctetAligned = isOctetAligned; +} + bool ImsMediaAudioSource::Start() { char kMimeType[128] = {'\0'}; diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/android/include/ImsMediaAudioPlayer.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/android/include/ImsMediaAudioPlayer.h index 1b19e342..92c12d9d 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/android/include/ImsMediaAudioPlayer.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/android/include/ImsMediaAudioPlayer.h @@ -82,6 +82,21 @@ public: void SetEvsPayloadHeaderMode(int32_t EvsPayloadHeaderMode); /** + * @brief Set Whether discontinuous transmission is enabled or not + * + * @params isDtxEnabled, if set to true then enable discontinuous transmission + */ + void SetDtxEnabled(bool isDtxEnabled); + + /** + * @brief Setting octet-align for AMR/AMR-WB + * + * @params isOctetAligned, If it's set to true then all fields in the AMR/AMR-WB header + * shall be aligned to octet boundaries by adding padding bits. + */ + void SetOctetAligned(bool isOctetAligned); + + /** * @brief Starts audio player to play the decoded audio frame and ndk audio decoder to decode * the given data * @@ -127,8 +142,10 @@ private: std::mutex mMutex; int32_t mEvsBitRate; kRtpPyaloadHeaderMode mEvsCodecHeaderMode; - bool mFirstFrame; + bool mIsFirstFrame; bool mIsEvsInitialized; + bool mIsDtxEnabled; + bool mIsOctetAligned; }; #endif diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/android/include/ImsMediaAudioSource.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/android/include/ImsMediaAudioSource.h index 63ed1ad7..d838a8cc 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/android/include/ImsMediaAudioSource.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/android/include/ImsMediaAudioSource.h @@ -102,6 +102,21 @@ public: void SetMediaDirection(int32_t direction); /** + * @brief Set Whether discontinuous transmission is enabled or not + * + * @params isDtxEnabled, if set to true then enable discontinuous transmission + */ + void SetDtxEnabled(bool isDtxEnabled); + + /** + * @brief Setting octet-align for AMR/AMR-WB + * + * @params isOctetAligned, If it's set to true then all fields in the AMR/AMR-WB header + * shall be aligned to octet boundaries by adding padding bits. + */ + void SetOctetAligned(bool isOctetAligned); + + /** * @brief Starts aaudio and ndk audio codec to get the audio frame and encode the audio frames * with given configuration * @@ -149,6 +164,8 @@ private: ImsMediaCondition mConditionExit; bool mIsEvsInitialized; int32_t mMediaDirection; + bool mIsDtxEnabled; + bool mIsOctetAligned; }; #endif diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/nodes/AudioRtpPayloadDecoderNode.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/nodes/AudioRtpPayloadDecoderNode.cpp index 2525acfd..279557fc 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/nodes/AudioRtpPayloadDecoderNode.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/nodes/AudioRtpPayloadDecoderNode.cpp @@ -49,7 +49,7 @@ ImsMediaResult AudioRtpPayloadDecoderNode::Start() mEvsMode = (kEvsBitrate)ImsMediaAudioUtil::GetMaximumEvsMode(mCoreEvsMode); mEvsCodecMode = (kEvsCodecMode)ImsMediaAudioUtil::ConvertEvsCodecMode(mEvsMode); - mPrevCMR = 15; + mPrevCMR = mCodecType == kAudioCodecEvs ? 127 : 15; mListFrameType.clear(); mNodeState = kNodeStateRunning; return RESULT_SUCCESS; @@ -133,7 +133,7 @@ void AudioRtpPayloadDecoderNode::OnDataFromFrontNode(ImsMediaSubType subtype, ui { case kAudioCodecAmr: case kAudioCodecAmrWb: - DecodePayloadAmr(pData, nDataSize, nTimestamp, bMark, nSeqNum, arrivalTime); + DecodePayloadAmr(pData, nDataSize, nTimestamp, nSeqNum, arrivalTime); break; case kAudioCodecPcmu: case kAudioCodecPcma: @@ -152,14 +152,13 @@ void AudioRtpPayloadDecoderNode::OnDataFromFrontNode(ImsMediaSubType subtype, ui } void AudioRtpPayloadDecoderNode::DecodePayloadAmr(uint8_t* pData, uint32_t nDataSize, - uint32_t nTimestamp, bool bMark, uint32_t nSeqNum, uint32_t arrivalTime) + uint32_t nTimestamp, uint32_t nSeqNum, uint32_t arrivalTime) { if (pData == nullptr || nDataSize == 0) { return; } - (void)bMark; uint32_t timestamp = nTimestamp; uint32_t eRate; uint32_t f; @@ -167,9 +166,9 @@ void AudioRtpPayloadDecoderNode::DecodePayloadAmr(uint8_t* pData, uint32_t nData uint32_t QbitPos; // Q_Speech_Sid_Bad IMLOGD_PACKET5(IM_PACKET_LOG_PH, - "[DecodePayloadAmr] GetCodectype[%d], octetAligned[%d], nSeqNum[%d], TS[%u], " + "[DecodePayloadAmr] codec type[%d], octetAligned[%d], size[%d], TS[%u], " "arrivalTime[%u]", - mCodecType, mOctetAligned, nSeqNum, timestamp, arrivalTime); + mCodecType, mOctetAligned, nDataSize, timestamp, arrivalTime); mBitReader.SetBuffer(pData, nDataSize); // read cmr @@ -236,19 +235,20 @@ void AudioRtpPayloadDecoderNode::DecodePayloadAmr(uint8_t* pData, uint32_t nData mListFrameType.pop_front(); mBitWriter.SetBuffer(mPayload, MAX_AUDIO_PAYLOAD_SIZE); - // set payload header + uint32_t bufferSize = (nDataBitSize + 7) >> 3; + // set TOC mBitWriter.Write(f, 1); mBitWriter.Write(eRate, 4); mBitWriter.Write(QbitPos, 1); mBitWriter.Write(0, 2); mBitReader.ReadByteBuffer(mPayload + 1, nDataBitSize); - // add payload header to payload size - uint32_t nBufferSize = ((nDataBitSize + 7) >> 3) + 1; + bufferSize++; + IMLOGD_PACKET6(IM_PACKET_LOG_PH, "[DecodePayloadAmr] result = %02X %02X %02X %02X, len[%d], eRate[%d]", mPayload[0], - mPayload[1], mPayload[2], mPayload[3], nBufferSize, eRate); + mPayload[1], mPayload[2], mPayload[3], bufferSize, eRate); // send remaining packet number in bundle as bMark value - SendDataToRearNode(MEDIASUBTYPE_RTPPAYLOAD, mPayload, nBufferSize, timestamp, + SendDataToRearNode(MEDIASUBTYPE_RTPPAYLOAD, mPayload, bufferSize, timestamp, mListFrameType.size(), nSeqNum, MEDIASUBTYPE_UNDEFINED, arrivalTime); timestamp += 20; @@ -263,10 +263,9 @@ void AudioRtpPayloadDecoderNode::DecodePayloadEvs(uint8_t* pData, uint32_t nData return; } - IMLOGD_PACKET5(IM_PACKET_LOG_PH, - "[DecodePayloadEvs] GetCodectype[%d], octetAligned[%d], nSeqNum[%d], TS[%u], " - "arrivalTime[%u]", - mCodecType, mOctetAligned, nSeqNum, nTimeStamp, arrivalTime); + IMLOGD_PACKET4(IM_PACKET_LOG_PH, + "[DecodePayloadEvs] codec type[%d], size[%d], TS[%u], arrivalTime[%u]", mCodecType, + nDataSize, nTimeStamp, arrivalTime); kRtpPyaloadHeaderMode eEVSReceivedPHFormat = kRtpPyaloadHeaderModeEvsCompact; kEvsCodecMode kEvsCodecMode = kEvsCodecModePrimary; @@ -321,7 +320,7 @@ void AudioRtpPayloadDecoderNode::DecodePayloadEvs(uint8_t* pData, uint32_t nData mBitReader.ReadByteBuffer(mPayload, nDataBitSize); - IMLOGD6("[DecodePayloadEvs] Result =%02X %02X %02X %02X, len=%d,nFrameType=%d", + IMLOGD6("[DecodePayloadEvs] Result=%02X %02X %02X %02X, len=%d,nFrameType=%d", mPayload[0], mPayload[1], mPayload[2], mPayload[3], nDataSize, nFrameType); SendDataToRearNode(MEDIASUBTYPE_RTPPAYLOAD, mPayload, nDataSize, timestamp, bMark, @@ -608,7 +607,6 @@ void AudioRtpPayloadDecoderNode::DecodePayloadEvs(uint8_t* pData, uint32_t nData } else // ToC byte { - IMLOGD0("[DecodePayloadEvs] Decoding TOC header"); toc_f = mBitReader.Read(1); toc_ft_m = mBitReader.Read(1); toc_ft_q = mBitReader.Read(1); @@ -636,14 +634,16 @@ void AudioRtpPayloadDecoderNode::DecodePayloadEvs(uint8_t* pData, uint32_t nData } mBitWriter.SetBuffer(mPayload, MAX_AUDIO_PAYLOAD_SIZE); + uint32_t bufferSize = (nDataBitSize + 7) >> 3; + // set TOC mBitWriter.Write(h, 1); mBitWriter.Write(toc_f, 1); mBitWriter.Write(toc_ft_m, 1); mBitWriter.Write(toc_ft_q, 1); mBitWriter.Write(toc_ft_b, 4); - mBitReader.ReadByteBuffer(mPayload + 1, nDataBitSize); + bufferSize++; // remove padding bit { @@ -653,10 +653,10 @@ void AudioRtpPayloadDecoderNode::DecodePayloadEvs(uint8_t* pData, uint32_t nData } IMLOGD6("[DecodePayloadEvs] result = %02X %02X %02X %02X, len=%d, eRate=%d", - mPayload[0], mPayload[1], mPayload[2], mPayload[3], nDataSize, toc_ft_b); + mPayload[0], mPayload[1], mPayload[2], mPayload[3], bufferSize, toc_ft_b); - SendDataToRearNode(MEDIASUBTYPE_RTPPAYLOAD, mPayload, (((nDataBitSize + 7) >> 3)), - timestamp, mListFrameType.size(), nSeqNum, MEDIASUBTYPE_UNDEFINED, arrivalTime); + SendDataToRearNode(MEDIASUBTYPE_RTPPAYLOAD, mPayload, bufferSize, timestamp, + mListFrameType.size(), nSeqNum, MEDIASUBTYPE_UNDEFINED, arrivalTime); timestamp += 20; } diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/nodes/AudioRtpPayloadEncoderNode.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/nodes/AudioRtpPayloadEncoderNode.cpp index 028eba2d..cffcb2c8 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/nodes/AudioRtpPayloadEncoderNode.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/nodes/AudioRtpPayloadEncoderNode.cpp @@ -51,11 +51,13 @@ kBaseNodeId AudioRtpPayloadEncoderNode::GetNodeId() ImsMediaResult AudioRtpPayloadEncoderNode::Start() { - IMLOGD2("[Start] codecType[%d], mode[%d]", mCodecType, mOctetAligned); mMaxNumOfFrame = mPtime / 20; mEvsMode = (kEvsBitrate)ImsMediaAudioUtil::GetMaximumEvsMode(mCoreEvsMode); mEvsCodecMode = (kEvsCodecMode)ImsMediaAudioUtil::ConvertEvsCodecMode(mEvsMode); + IMLOGD5("[Start] codecType[%d], mode[%d], num of frames[%d], evs bitrate[%d], evs mode[%d]", + mCodecType, mOctetAligned, mMaxNumOfFrame, mEvsMode, mEvsCodecMode); + if (mMaxNumOfFrame == 0 || mMaxNumOfFrame > MAX_FRAME_IN_PACKET) { IMLOGE1("[Start] Invalid ptime [%d]", mPtime); @@ -86,16 +88,15 @@ bool AudioRtpPayloadEncoderNode::IsSourceNode() return false; } -void AudioRtpPayloadEncoderNode::OnDataFromFrontNode(ImsMediaSubType subtype, uint8_t* pData, +void AudioRtpPayloadEncoderNode::OnDataFromFrontNode(ImsMediaSubType /*subtype*/, uint8_t* pData, uint32_t nDataSize, uint32_t nTimestamp, bool bMark, uint32_t nSeqNum, ImsMediaSubType nDataType, uint32_t arrivalTime) { - (void)subtype; switch (mCodecType) { case kAudioCodecAmr: case kAudioCodecAmrWb: - EncodePayloadAmr(pData, nDataSize, nTimestamp, bMark); + EncodePayloadAmr(pData, nDataSize, nTimestamp); break; case kAudioCodecPcmu: case kAudioCodecPcma: @@ -166,16 +167,15 @@ bool AudioRtpPayloadEncoderNode::IsSameConfig(void* config) } void AudioRtpPayloadEncoderNode::EncodePayloadAmr( - uint8_t* pData, uint32_t nDataSize, uint32_t nTimestamp, bool bMark) + uint8_t* pData, uint32_t nDataSize, uint32_t nTimestamp) { - (void)bMark; uint32_t nCmr = 15; uint32_t f, ft, q, nDataBitSize; -#ifndef LEGACY_AUDIO_ENABLED // for ap audio test + // remove TOC from the encoder pData++; nDataSize -= 1; -#endif + if (nDataSize > 4) { IMLOGD_PACKET5(IM_PACKET_LOG_PH, "[EncodePayloadAmr] src = %02X %02X %02X %02X, len[%d]", @@ -277,28 +277,18 @@ void AudioRtpPayloadEncoderNode::EncodePayloadAmr( void AudioRtpPayloadEncoderNode::EncodePayloadEvs( uint8_t* pData, uint32_t nDataSize, uint32_t nTimeStamp) { - kRtpPyaloadHeaderMode eEVSPayloadFormat = kRtpPyaloadHeaderModeEvsHeaderFull; - kEvsCodecMode kEvsCodecMode; - - // 0111 1111 is no request. - uint32_t nEVSBW = 0x07; - uint32_t nEVSBR = 0x0f; - uint32_t nFrameType = 0; - if (nDataSize == 0) { return; } - eEVSPayloadFormat = mEvsPayloadHeaderMode; + uint32_t nFrameType = 0; // 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. mCurrNumOfFrame++; - if (eEVSPayloadFormat == kRtpPyaloadHeaderModeEvsCompact) + if (mEvsPayloadHeaderMode == kRtpPyaloadHeaderModeEvsCompact) { memset(mPayload, 0, MAX_AUDIO_PAYLOAD_SIZE); mBWHeader.SetBuffer(mPayload, MAX_AUDIO_PAYLOAD_SIZE); @@ -306,7 +296,7 @@ void AudioRtpPayloadEncoderNode::EncodePayloadEvs( mTimestamp = nTimeStamp; // exactly one coded frame without any additional EVS RTP payload header - if (kEvsCodecMode == kEvsCodecModePrimary) + if (mEvsCodecMode == kEvsCodecModePrimary) { // calculate nDataBitSize from nDataSize nFrameType = (uint32_t)ImsMediaAudioUtil::ConvertLenToEVSAudioMode(nDataSize); @@ -354,9 +344,8 @@ void AudioRtpPayloadEncoderNode::EncodePayloadEvs( mFirstFrame = false; } // one 3-bit CMR field, one coded frame, and zero-padding bits if necessary - else if (kEvsCodecMode == kEvsCodecModeAmrIo) + else if (mEvsCodecMode == kEvsCodecModeAmrIo) { - IMLOGE0("[EncodePayloadEvs] COMPACT and AMR_WB_IO"); // calculate nDataBitSize from nDataSize nFrameType = (uint32_t)ImsMediaAudioUtil::ConvertLenToAmrWbMode(nDataSize); uint32_t nDataBitSize = ImsMediaAudioUtil::ConvertAmrWbModeToBitLen(nFrameType); @@ -447,17 +436,23 @@ void AudioRtpPayloadEncoderNode::EncodePayloadEvs( return; } } - else if (eEVSPayloadFormat == kRtpPyaloadHeaderModeEvsHeaderFull) + else if (mEvsPayloadHeaderMode == kRtpPyaloadHeaderModeEvsHeaderFull) { + // 0111 1111 is no request. + uint32_t nEVSBW = 0x07; + uint32_t nEVSBR = 0x0f; + + // remove 1 byte toc field from the codec + pData++; + nDataSize--; + uint32_t cmr_h, cmr_t, cmr_d = 0; // CMR byte memset(mPayload, 0, MAX_AUDIO_PAYLOAD_SIZE); mBWHeader.SetBuffer(mPayload, MAX_AUDIO_PAYLOAD_SIZE); mBWPayload.SetBuffer(mPayload, MAX_AUDIO_PAYLOAD_SIZE); - if (kEvsCodecMode == kEvsCodecModePrimary) + if (mEvsCodecMode == kEvsCodecModePrimary) { - IMLOGE0("[EncodePayloadEvs] HF and PRI"); - if (nFrameType == kImsAudioEvsPrimaryModeSID || mSendCMR) // CMR value { // Header Type identification bit(1bit) - always set to 1 @@ -537,9 +532,8 @@ void AudioRtpPayloadEncoderNode::EncodePayloadEvs( mFirstFrame = false; } } - else if (kEvsCodecMode == kEvsCodecModeAmrIo) + else if (mEvsCodecMode == kEvsCodecModeAmrIo) { - IMLOGE0("[EncodePayloadEvs] HF and AMR_WB_IO"); // set CMR byte // at EVS AMR WB IO Mode, CMR field shall include. // Header Type identification bit(1bit) - always set to 1 @@ -624,8 +618,7 @@ void AudioRtpPayloadEncoderNode::EncodePayloadEvs( IMLOGE0("[EncodePayloadEvs] invalid codec mode"); return; } - - } // end of if(eEVSPayloadFormat == kRtpPyaloadHeaderModeEvsHeaderFull) + } else { IMLOGE0("[EncodePayloadEvs] invalid payload format"); @@ -637,19 +630,17 @@ void AudioRtpPayloadEncoderNode::EncodePayloadEvs( uint32_t AudioRtpPayloadEncoderNode::CheckPaddingNecessity(uint32_t nTotalSize) { - kEvsCodecMode kEvsCodecMode; + kEvsCodecMode evsCodecMode; uint32_t nEVSCompactId; uint32_t nSize = nTotalSize; // check EVS compact size while (nSize != 0 && - ImsMediaAudioUtil::ConvertEVSPayloadMode(nSize, &kEvsCodecMode, &nEVSCompactId) == + ImsMediaAudioUtil::ConvertEVSPayloadMode(nSize, &evsCodecMode, &nEVSCompactId) == kRtpPyaloadHeaderModeEvsCompact) { mPayload[nSize] = 0; nSize++; - - IMLOGD1("[CheckPaddingNecessity] Add Padding - size[%d]", nSize); } return nSize; diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/nodes/IAudioPlayerNode.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/nodes/IAudioPlayerNode.cpp index c37fba98..14acf360 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/nodes/IAudioPlayerNode.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/nodes/IAudioPlayerNode.cpp @@ -29,6 +29,8 @@ IAudioPlayerNode::IAudioPlayerNode(BaseSessionCallback* callback) : std::unique_ptr<ImsMediaAudioPlayer> track(new ImsMediaAudioPlayer()); mAudioPlayer = std::move(track); mConfig = nullptr; + mIsOctetAligned = false; + mIsDtxEnabled = false; } IAudioPlayerNode::~IAudioPlayerNode() @@ -59,6 +61,8 @@ ImsMediaResult IAudioPlayerNode::ProcessStart() { mAudioPlayer->SetCodec(mCodecType); mAudioPlayer->SetSamplingRate(mSamplingRate * 1000); + mAudioPlayer->SetDtxEnabled(mIsDtxEnabled); + mAudioPlayer->SetOctetAligned(mIsOctetAligned); if (mCodecType == kAudioCodecEvs) { @@ -76,7 +80,6 @@ ImsMediaResult IAudioPlayerNode::ProcessStart() IMLOGE0("[IAudioPlayer] Not able to start AudioPlayer"); } - mFirstFrame = false; mNodeState = kNodeStateRunning; StartThread(); return RESULT_SUCCESS; @@ -130,6 +133,7 @@ void IAudioPlayerNode::SetConfig(void* config) if (mCodecType == kAudioCodecAmr || mCodecType == kAudioCodecAmrWb) { mMode = mConfig->getAmrParams().getAmrMode(); + mIsOctetAligned = mConfig->getAmrParams().getOctetAligned(); } else if (mCodecType == kAudioCodecEvs) { @@ -141,7 +145,8 @@ void IAudioPlayerNode::SetConfig(void* config) } mSamplingRate = mConfig->getSamplingRateKHz(); - SetJitterBufferSize(4, 4, 9); + mIsDtxEnabled = mConfig->getDtxEnabled(); + SetJitterBufferSize(3, 3, 9); SetJitterOptions( 80, 1, (double)25 / 10, false /** TODO: when enable DTX, set this true on condition*/ ); @@ -161,7 +166,9 @@ bool IAudioPlayerNode::IsSameConfig(void* config) if (mCodecType == kAudioCodecAmr || mCodecType == kAudioCodecAmrWb) { return (mMode == pConfig->getAmrParams().getAmrMode() && - mSamplingRate == pConfig->getSamplingRateKHz()); + mSamplingRate == pConfig->getSamplingRateKHz() && + mIsDtxEnabled == pConfig->getDtxEnabled() && + mIsOctetAligned == pConfig->getAmrParams().getOctetAligned()); } else if (mCodecType == kAudioCodecEvs) { @@ -171,7 +178,8 @@ bool IAudioPlayerNode::IsSameConfig(void* config) pConfig->getEvsParams().getEvsBandwidth()) && mEvsChannelAwOffset == pConfig->getEvsParams().getChannelAwareMode() && mSamplingRate == pConfig->getSamplingRateKHz() && - mEvsPayloadHeaderMode == pConfig->getEvsParams().getUseHeaderFullOnly()); + mEvsPayloadHeaderMode == pConfig->getEvsParams().getUseHeaderFullOnly() && + mIsDtxEnabled == pConfig->getDtxEnabled()); } } @@ -189,6 +197,7 @@ void* IAudioPlayerNode::run() bool bMark = false; uint32_t nSeqNum = 0; uint64_t nNextTime = ImsMediaTimer::GetTimeInMicroSeconds(); + bool isFirstFrameReceived = false; while (true) { @@ -201,24 +210,28 @@ void* IAudioPlayerNode::run() if (GetData(&subtype, &pData, &nDataSize, &nTimestamp, &bMark, &nSeqNum, &datatype) == true) { + IMLOGD_PACKET2(IM_PACKET_LOG_AUDIO, "[run] write buffer size[%d], TS[%u]", nDataSize, + nTimestamp); if (nDataSize != 0) { - IMLOGD_PACKET2(IM_PACKET_LOG_AUDIO, "[run] write buffer size[%d], TS[%u]", - nDataSize, nTimestamp); if (mAudioPlayer->onDataFrame(pData, nDataSize)) { // send buffering complete message to client - if (mFirstFrame == false) + if (isFirstFrameReceived == false) { mCallback->SendEvent(kImsMediaEventFirstPacketReceived, reinterpret_cast<uint64_t>(new AudioConfig(*mConfig))); - mFirstFrame = true; + isFirstFrameReceived = true; } } } - DeleteData(); } + else if (isFirstFrameReceived) + { + IMLOGE0("[run] GetData returned 0 bytes"); + mAudioPlayer->onDataFrame(nullptr, 0); + } nNextTime += 20000; uint64_t nCurrTime = ImsMediaTimer::GetTimeInMicroSeconds(); diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/nodes/IAudioSourceNode.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/nodes/IAudioSourceNode.cpp index 8bedca46..3eff8305 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/nodes/IAudioSourceNode.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/nodes/IAudioSourceNode.cpp @@ -33,6 +33,8 @@ IAudioSourceNode::IAudioSourceNode(BaseSessionCallback* callback) : mRunningCodecMode = 0; mFirstFrame = false; mMediaDirection = 0; + mIsOctetAligned = false; + mIsDtxEnabled = false; } IAudioSourceNode::~IAudioSourceNode() {} @@ -54,6 +56,8 @@ ImsMediaResult IAudioSourceNode::ProcessStart() mAudioSource->SetPtime(mPtime); mAudioSource->SetSamplingRate(mSamplingRate * 1000); mAudioSource->SetMediaDirection(mMediaDirection); + mAudioSource->SetDtxEnabled(mIsDtxEnabled); + mAudioSource->SetOctetAligned(mIsOctetAligned); if (mCodecType == kAudioCodecEvs) { @@ -120,6 +124,7 @@ void IAudioSourceNode::SetConfig(void* config) if (mCodecType == kAudioCodecAmr || mCodecType == kAudioCodecAmrWb) { mCodecMode = pConfig->getAmrParams().getAmrMode(); + mIsOctetAligned = pConfig->getAmrParams().getOctetAligned(); } else if (mCodecType == kAudioCodecEvs) { @@ -132,6 +137,7 @@ void IAudioSourceNode::SetConfig(void* config) mMediaDirection = pConfig->getMediaDirection(); mSamplingRate = pConfig->getSamplingRateKHz(); mPtime = pConfig->getPtimeMillis(); + mIsDtxEnabled = pConfig->getDtxEnabled(); } bool IAudioSourceNode::IsSameConfig(void* config) @@ -149,7 +155,9 @@ bool IAudioSourceNode::IsSameConfig(void* config) { return (mCodecMode == pConfig->getAmrParams().getAmrMode() && mSamplingRate == pConfig->getSamplingRateKHz() && - mMediaDirection == pConfig->getMediaDirection()); + mMediaDirection == pConfig->getMediaDirection() && + mIsDtxEnabled == pConfig->getDtxEnabled() && + mIsOctetAligned == pConfig->getAmrParams().getOctetAligned()); } else if (mCodecType == kAudioCodecEvs) { @@ -159,7 +167,8 @@ bool IAudioSourceNode::IsSameConfig(void* config) pConfig->getEvsParams().getEvsBandwidth()) && mEvsChAwOffset == pConfig->getEvsParams().getChannelAwareMode() && mSamplingRate == pConfig->getSamplingRateKHz() && - mMediaDirection == pConfig->getMediaDirection()); + mMediaDirection == pConfig->getMediaDirection() && + mIsDtxEnabled == pConfig->getDtxEnabled()); } } 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 045051c1..75d70238 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 @@ -52,6 +52,7 @@ enum kImsMediaEventType kImsMediaEventResolutionChanged, kImsMediaEventNotifyVideoDataUsage, kImsMediaEventNotifyRttReceived, + kImsMediaEventNotifyVideoLowestBitrate, }; // Internal Request Event @@ -331,7 +332,7 @@ enum ImsMediaVideoMsgResponse kVideoPeerDimensionChanged, kVideoRtpHeaderExtensionInd, kVideoMediaInactivityInd, - kVideoPacketLossInd, + kVideoBitrateInd, kVideoDataUsageInd, kVideoSessionClosed, }; @@ -360,7 +361,6 @@ enum ImsMediaTextMsgResponse #define RTT_MAX_CHAR_PER_SEC (30) // ATIS_GTT : 30 characters per second #define RTT_MAX_UNICODE_UTF8 (4) #define MAX_RTT_LEN (RTT_MAX_CHAR_PER_SEC * RTT_MAX_UNICODE_UTF8) -#define T140_MAX_CHUNK (1) #define PAYLOADENCODER_TEXT_MAX_REDUNDANT_INTERVAL (16383) struct EventParamOpenSession diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/JitterNetworkAnalyser.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/JitterNetworkAnalyser.h index 341d4043..e328a21a 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/JitterNetworkAnalyser.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/JitterNetworkAnalyser.h @@ -19,6 +19,7 @@ #include <stdint.h> #include <list> +#include <mutex> enum NETWORK_STATUS { @@ -67,6 +68,7 @@ private: double CalculateDeviation(double* pMean); int32_t GetMaxJitterValue(); + std::mutex mMutex; uint32_t mMinJitterBufferSize; uint32_t mMaxJitterBufferSize; uint32_t mBasePacketTime; diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/StreamScheduler.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/StreamScheduler.h index b083096a..f9612c5d 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/StreamScheduler.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/StreamScheduler.h @@ -37,7 +37,6 @@ public: virtual void* run(); private: - BaseNode* DetermineProcessingNode(); void RunRegisteredNode(); std::list<BaseNode*> mlistRegisteredNode; ImsMediaCondition mConditionMain; diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/AudioJitterBuffer.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/AudioJitterBuffer.h index 001588fc..2c592514 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/AudioJitterBuffer.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/AudioJitterBuffer.h @@ -36,7 +36,8 @@ public: uint32_t* pnTimestamp, bool* pbMark, uint32_t* pnSeqNum, uint32_t currentTime); private: - bool IsSID(uint8_t* pbBuffer, uint32_t nBufferSize); + bool IsSID(uint32_t nBufferSize); + bool Resync(uint32_t currentTime); void CollectRxRtpStatus(int32_t seq, kRtpPacketStatus status); void CollectJitterBufferStatus(int32_t currSize, int32_t maxSize); @@ -48,7 +49,6 @@ private: bool mEnforceUpdate; uint32_t mCannotGetCount; uint32_t mCurrPlayingTS; - uint16_t mCurrPlayingSeq; uint32_t mBaseTimestamp; uint32_t mBaseArrivalTime; uint32_t mCheckUpdateJitterPacketCnt; 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 231b4141..d2a0e441 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 @@ -37,10 +37,6 @@ public: */ class RequestHandler : public ImsMediaEventHandler { - public: - RequestHandler(); - virtual ~RequestHandler(); - protected: virtual void processEvent( uint32_t event, uint64_t sessionId, uint64_t paramA, uint64_t paramB); @@ -51,10 +47,6 @@ public: */ class ResponseHandler : public ImsMediaEventHandler { - public: - ResponseHandler(); - virtual ~ResponseHandler(); - protected: virtual void processEvent( uint32_t event, uint64_t sessionId, uint64_t paramA, uint64_t paramB); @@ -63,20 +55,22 @@ public: static AudioManager* getInstance(); virtual int getState(int sessionId); virtual void sendMessage(const int sessionId, const android::Parcel& parcel); - void SendInternalEvent(uint32_t event, uint64_t sessionId, uint64_t paramA, uint64_t paramB); -private: +protected: AudioManager(); virtual ~AudioManager(); ImsMediaResult openSession(int sessionId, int rtpFd, int rtcpFd, AudioConfig* config); ImsMediaResult closeSession(int sessionId); ImsMediaResult modifySession(int sessionId, AudioConfig* config); ImsMediaResult addConfig(int sessionId, AudioConfig* config); - ImsMediaResult deleteConfig(int sessionId, AudioConfig* config); + virtual ImsMediaResult deleteConfig(int sessionId, AudioConfig* config); ImsMediaResult confirmConfig(int sessionId, AudioConfig* config); - void sendDtmf(int sessionId, char dtmfDigit, int duration); - void sendRtpHeaderExtension(int sessionId, std::list<RtpHeaderExtension>* listExtension); - void setMediaQualityThreshold(int sessionId, MediaQualityThreshold* threshold); + virtual void sendDtmf(int sessionId, char dtmfDigit, int duration); + virtual void sendRtpHeaderExtension( + int sessionId, std::list<RtpHeaderExtension>* listExtension); + virtual void setMediaQualityThreshold(int sessionId, MediaQualityThreshold* threshold); + virtual void SendInternalEvent( + uint32_t event, uint64_t sessionId, uint64_t paramA, uint64_t paramB); static AudioManager* sManager; std::unordered_map<int, std::unique_ptr<AudioSession>> mSessions; diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/AudioStreamGraphRtcp.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/AudioStreamGraphRtcp.h index 9691826c..7253b035 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/AudioStreamGraphRtcp.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/AudioStreamGraphRtcp.h @@ -27,6 +27,7 @@ public: virtual ~AudioStreamGraphRtcp(); virtual ImsMediaResult create(RtpConfig* config); virtual ImsMediaResult update(RtpConfig* config); + virtual ImsMediaResult start(); virtual bool OnEvent(int32_t type, uint64_t param1, uint64_t param2); }; diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/MediaQualityAnalyzer.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/MediaQualityAnalyzer.h index f83ea088..feff4892 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/MediaQualityAnalyzer.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/MediaQualityAnalyzer.h @@ -256,6 +256,10 @@ protected: int32_t mCodecType; /** The codec attribute of the audio session, it could be bandwidth in evs codec */ int32_t mCodecAttribute; + /** Whether RTP is activated for the receiver or not */ + bool mIsRxRtpEnabled; + /** Whether RTCP is activated for both sender and receiver */ + bool mIsRtcpEnabled; /** The begin of the rx rtp packet sequence number for Rtcp-Xr report */ int32_t mBeginSeq; /** The end of the rx rtp packet sequence number for Rtcp-Xr report */ diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/nodes/AudioRtpPayloadDecoderNode.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/nodes/AudioRtpPayloadDecoderNode.h index 469ea586..c074f0be 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/nodes/AudioRtpPayloadDecoderNode.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/nodes/AudioRtpPayloadDecoderNode.h @@ -45,8 +45,8 @@ public: uint32_t arrivalTime = 0); private: - void DecodePayloadAmr(uint8_t* pData, uint32_t nDataSize, uint32_t nTimestamp, bool bMark, - uint32_t nSeqNum, uint32_t arrivalTime); + void DecodePayloadAmr(uint8_t* pData, uint32_t nDataSize, uint32_t nTimestamp, uint32_t nSeqNum, + uint32_t arrivalTime); void DecodePayloadEvs(uint8_t* pData, uint32_t nDataSize, uint32_t nTimeStamp, bool bMark, uint32_t nSeqNum, uint32_t arrivalTime); bool ProcessCMRForEVS(kRtpPyaloadHeaderMode eEVSPayloadHeaderMode, kEvsCmrCodeType cmr_t, diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/nodes/AudioRtpPayloadEncoderNode.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/nodes/AudioRtpPayloadEncoderNode.h index 28adc783..cc3be7f3 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/nodes/AudioRtpPayloadEncoderNode.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/nodes/AudioRtpPayloadEncoderNode.h @@ -38,7 +38,7 @@ public: virtual bool IsSameConfig(void* config); private: - void EncodePayloadAmr(uint8_t* pData, uint32_t nDataSize, uint32_t nTimestamp, bool bMark); + void EncodePayloadAmr(uint8_t* pData, uint32_t nDataSize, uint32_t nTimestamp); void EncodePayloadEvs(uint8_t* pData, uint32_t nDataSize, uint32_t nTimeStamp); uint32_t CheckPaddingNecessity(uint32_t nTotalSize); diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/nodes/IAudioPlayerNode.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/nodes/IAudioPlayerNode.h index cb41c0c6..afea1a56 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/nodes/IAudioPlayerNode.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/nodes/IAudioPlayerNode.h @@ -49,11 +49,12 @@ private: int32_t mCodecType; uint32_t mMode; ImsMediaCondition mCondition; - bool mFirstFrame; int8_t mEvsChannelAwOffset; kEvsBandwidth mEvsBandwidth; int8_t mSamplingRate; int32_t mEvsPayloadHeaderMode; + bool mIsDtxEnabled; + bool mIsOctetAligned; }; #endif diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/nodes/IAudioSourceNode.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/nodes/IAudioSourceNode.h index 1f5d8d6e..741556b0 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/nodes/IAudioSourceNode.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/nodes/IAudioSourceNode.h @@ -68,6 +68,8 @@ public: int8_t mSamplingRate; int8_t mEvsChAwOffset; int32_t mMediaDirection; + bool mIsDtxEnabled; + bool mIsOctetAligned; }; #endif diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/nodes/BaseNode.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/nodes/BaseNode.h index 9ca79518..c7270c6b 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/nodes/BaseNode.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/nodes/BaseNode.h @@ -237,6 +237,26 @@ public: uint32_t* arrivalTime = nullptr); /** + * @brief This method is to add data frame to the queue in the node + * + * @param data The data buffer + * @param size The size of data + * @param timestamp The timestamp of data, it can be milliseconds unit or rtp timestamp unit + * @param mark It is true when the data has marker bit set + * @param seq The sequence number of data. it is 0 when there is no valid sequence number set + * @param subtype The subtype of data stored in the queue. It can be various subtype according + * to the characteristics of the given data + * @param dataType The additional data type for the video frames + * @param arrivalTime The arrival time of the packet + * @param index The index of the queue to add, if it is not set, add the frame to the end of + * the queue + */ + virtual void AddData(uint8_t* data, uint32_t size, uint32_t timestamp, bool mark, uint32_t seq, + ImsMediaSubType subtype = ImsMediaSubType::MEDIASUBTYPE_UNDEFINED, + ImsMediaSubType dataType = ImsMediaSubType::MEDIASUBTYPE_UNDEFINED, + uint32_t arrivalTime = 0, int32_t index = -1); + + /** * @brief Deletes the data stored in the front of the data queue * */ diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/nodes/RtcpEncoderNode.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/nodes/RtcpEncoderNode.h index 5693f2c9..692e1e76 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/nodes/RtcpEncoderNode.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/nodes/RtcpEncoderNode.h @@ -45,7 +45,7 @@ public: /** * @brief The methods operates when the timer is expired */ - void ProcessTimer(); + virtual void ProcessTimer(); /** * @brief Set the local ip address and port number 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 54253073..def41205 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 @@ -83,6 +83,7 @@ private: int8_t mRtpTxDtmfPayload; int8_t mRtpRxDtmfPayload; int8_t mDtmfSamplingRate; + int32_t mDtmfTimestamp; int32_t mCvoValue; int8_t mRedundantPayload; int8_t mRedundantLevel; diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/text/TextManager.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/text/TextManager.h index 66201daa..67b24ed3 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/text/TextManager.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/text/TextManager.h @@ -37,10 +37,6 @@ public: */ class RequestHandler : public ImsMediaEventHandler { - public: - RequestHandler(); - virtual ~RequestHandler(); - protected: virtual void processEvent( uint32_t event, uint64_t sessionId, uint64_t paramA, uint64_t paramB); @@ -51,10 +47,6 @@ public: */ class ResponseHandler : public ImsMediaEventHandler { - public: - ResponseHandler(); - virtual ~ResponseHandler(); - protected: virtual void processEvent( uint32_t event, uint64_t sessionId, uint64_t paramA, uint64_t paramB); diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/text/TextStreamGraphRtcp.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/text/TextStreamGraphRtcp.h index 9832f036..2e5155a1 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/text/TextStreamGraphRtcp.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/text/TextStreamGraphRtcp.h @@ -27,6 +27,7 @@ public: virtual ~TextStreamGraphRtcp(); virtual ImsMediaResult create(RtpConfig* config); virtual ImsMediaResult update(RtpConfig* config); + virtual ImsMediaResult start(); virtual bool setMediaQualityThreshold(MediaQualityThreshold* threshold); }; diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/text/nodes/TextSourceNode.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/text/nodes/TextSourceNode.h index 5b61a77e..5b690c90 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/text/nodes/TextSourceNode.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/text/nodes/TextSourceNode.h @@ -45,7 +45,7 @@ public: void SendRtt(const android::String8* text); private: - void SendBOM(); + void SendBom(); int32_t mCodecType; int8_t mRedundantLevel; @@ -54,9 +54,6 @@ private: int32_t mBitrate; bool mBomEnabled; bool mSentBOM; - std::list<uint32_t> mListTextSourceSize; - std::list<uint8_t*> mListTextSource; - uint8_t mTextToSend[MAX_RTT_LEN]; std::mutex mMutex; }; diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/utils/ImsMediaBitWriter.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/utils/ImsMediaBitWriter.h index e4aa8ea9..693b2f5c 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/utils/ImsMediaBitWriter.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/utils/ImsMediaBitWriter.h @@ -25,9 +25,9 @@ public: ImsMediaBitWriter(); ~ImsMediaBitWriter(); void SetBuffer(uint8_t* pbBuffer, uint32_t nBufferSize); - void Write(uint32_t nValue, uint32_t nSize); - void WriteByteBuffer(uint8_t* pbSrc, uint32_t nBitSize); - void WriteByteBuffer(uint32_t value); + bool Write(uint32_t nValue, uint32_t nSize); + bool WriteByteBuffer(uint8_t* pbSrc, uint32_t nBitSize); + bool WriteByteBuffer(uint32_t value); void Seek(uint32_t nSize); void AddPadding(); uint32_t GetBufferSize(); diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/utils/ImsMediaEventHandler.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/utils/ImsMediaEventHandler.h index 4e5dc7f0..e01761da 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/utils/ImsMediaEventHandler.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/utils/ImsMediaEventHandler.h @@ -46,8 +46,10 @@ private: std::mutex mMutexEvent; public: - ImsMediaEventHandler(const char* strName); + ImsMediaEventHandler(); virtual ~ImsMediaEventHandler(); + void Init(const char* strName); + void Deinit(); static void SendEvent(const char* strEventHandlerName, uint32_t event, uint64_t paramA, uint64_t paramB = 0, uint64_t paramC = 0); char* getName(); diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/utils/ImsMediaImageRotate.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/utils/ImsMediaImageRotate.h index 939cd670..a0d46ddf 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/utils/ImsMediaImageRotate.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/utils/ImsMediaImageRotate.h @@ -24,7 +24,7 @@ class ImsMediaImageRotate public: /** * @brief Rotates YUVImage_420_Planar Image by 90 degrees and flips. - * Supports zero pixel and zero row stride. + * Supports input row stride equal to width. * * Source Image Destination Image * + - - - - + + - - - - + @@ -33,17 +33,18 @@ public: * | 7 8 9 | | 7 4 1 | * + - - - - + + - - - - + * - * @param pbDst Destination buffer with size nDstWidth*nDstHeight*1.5. + * @param pOutBuffer Pointer to output buffer with size nDstWidth*nDstHeight*1.5. * @param pbSrc Source buffer with size nDstWidth*nDstHeight*1.5. * @param nSrcWidth Source Image width. * @param nSrcHeight Source Image height. */ static void YUV420_Planar_Rotate90_Flip( - uint8_t* pbDst, uint8_t* pbSrc, uint16_t nSrcWidth, uint16_t nSrcHeight); + uint8_t* pOutBuffer, uint8_t* pbSrc, uint16_t nSrcWidth, uint16_t nSrcHeight); /** * @brief Rotates YUVImage_420_888 Image by 90 degrees. - * Supports zero pixel stride and zero row stride. + * Supports input row stride equal to width and adds padding when outputStride is not same + * as output image width. * * Source Image Destination Image * + - - - - + + - - - - + @@ -52,18 +53,22 @@ public: * | 7 8 9 | | 9 6 3 | * + - - - - + + - - - - + * - * @param pbDst Destination buffer with size nDstWidth*nDstHeight*1.5. + * @param pOutBuffer Pointer to output buffer with size outputStride*nDstHeight*1.5. + * @param nOutBufSize size of output buffer. + * @param outputStride Stride of the output image >= nDstWidth. * @param pYPlane Y-Plane data of size nDstWidth*nDstHeight. * @param pUVPlane UV-Plane data of size (nDstWidth*nDstHeight)/2. * @param nSrcWidth Source Image width. * @param nSrcHeight Source Image height. + * + * @return -1 on error and 0 on success. */ - static void YUV420_SP_Rotate90(uint8_t* pbDst, uint8_t* pYPlane, uint8_t* pUVPlane, - uint16_t nSrcWidth, uint16_t nSrcHeight); + static int YUV420_SP_Rotate90(uint8_t* pOutBuffer, size_t nOutBufSize, uint16_t outputStride, + uint8_t* pYPlane, uint8_t* pUVPlane, uint16_t nSrcWidth, uint16_t nSrcHeight); /** * @brief Rotates YUVImage_420_888 Image by 90 degrees and flip. - * Supports zero pixel stride and zero row stride. + * Supports input row stride equal to width. * * Source Image Destination Image * + - - - - + + - - - - + @@ -72,18 +77,19 @@ public: * | 7 8 9 | | 7 4 1 | * + - - - - + + - - - - + * - * @param pbDst Destination buffer with size nDstWidth*nDstHeight*1.5. + * @param pOutBuffer Pointer to output buffer with size nDstWidth*nDstHeight*1.5. * @param pYPlane Y-Plane data of size nDstWidth*nDstHeight. * @param pUVPlane UV-Plane data of size (nDstWidth*nDstHeight)/2. * @param nSrcWidth Source Image width. * @param nSrcHeight Source Image height. */ - static void YUV420_SP_Rotate90_Flip(uint8_t* pbDst, uint8_t* pYPlane, uint8_t* pUVPlane, + static void YUV420_SP_Rotate90_Flip(uint8_t* pOutBuffer, uint8_t* pYPlane, uint8_t* pUVPlane, uint16_t nSrcWidth, uint16_t nSrcHeight); /** * @brief Rotates YUVImage_420_888 Image by 270 degrees. - * Supports zero pixel stride and zero row stride. + * Supports input row stride equal to width and adds padding when outputStride is not same + * as output image width. * * Source Image Destination Image * + - - - - + + - - - - + @@ -92,14 +98,18 @@ public: * | 7 8 9 | | 1 4 7 | * + - - - - + + - - - - + * - * @param pbDst Destination buffer with size nDstWidth*nDstHeight*1.5. + * @param pOutBuffer Pointer to output buffer with size nDstWidth*nDstHeight*1.5. + * @param nOutBufSize size of output buffer. + * @param outputStride Stride of the output image >= nDstWidth. * @param pYPlane Y-Plane data of size nDstWidth*nDstHeight. * @param pUVPlane UV-Plane data of size (nDstWidth*nDstHeight)/2. * @param nSrcWidth Source Image width. * @param nSrcHeight Source Image height. + * + * @return -1 on error and 0 on success. */ - static void YUV420_SP_Rotate270(uint8_t* pbDst, uint8_t* pYPlane, uint8_t* pUVPlane, - uint16_t nSrcWidth, uint16_t nSrcHeight); + static int YUV420_SP_Rotate270(uint8_t* pOutBuffer, size_t nOutBufSize, uint16_t outputStride, + uint8_t* pYPlane, uint8_t* pUVPlane, uint16_t nSrcWidth, uint16_t nSrcHeight); }; #endif // IMS_MEDIA_IMAGE_ROTATE
\ No newline at end of file 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 7c780fd0..91c2747b 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 @@ -37,10 +37,6 @@ public: */ class RequestHandler : public ImsMediaEventHandler { - public: - RequestHandler(); - virtual ~RequestHandler(); - protected: virtual void processEvent( uint32_t event, uint64_t sessionId, uint64_t paramA, uint64_t paramB); @@ -51,10 +47,6 @@ public: */ class ResponseHandler : public ImsMediaEventHandler { - public: - ResponseHandler(); - virtual ~ResponseHandler(); - protected: virtual void processEvent( uint32_t event, uint64_t sessionId, uint64_t paramA, uint64_t paramB); diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/video/VideoStreamGraphRtcp.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/video/VideoStreamGraphRtcp.h index 2ef9a5b6..f1c6d013 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/video/VideoStreamGraphRtcp.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/video/VideoStreamGraphRtcp.h @@ -27,6 +27,7 @@ public: virtual ~VideoStreamGraphRtcp(); virtual ImsMediaResult create(RtpConfig* config); virtual ImsMediaResult update(RtpConfig* config); + virtual ImsMediaResult start(); virtual bool setMediaQualityThreshold(MediaQualityThreshold* threshold); virtual bool OnEvent(int32_t type, uint64_t param1, uint64_t param2); }; diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/video/VideoStreamGraphRtpTx.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/video/VideoStreamGraphRtpTx.h index 1255d194..0fd1104a 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/video/VideoStreamGraphRtpTx.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/video/VideoStreamGraphRtpTx.h @@ -29,6 +29,7 @@ public: virtual ImsMediaResult create(RtpConfig* config); virtual ImsMediaResult update(RtpConfig* config); virtual ImsMediaResult start(); + virtual bool setMediaQualityThreshold(MediaQualityThreshold* threshold); void setSurface(ANativeWindow* surface); virtual bool OnEvent(int32_t type, uint64_t param1, uint64_t param2); diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/video/android/ImsMediaCamera.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/video/android/ImsMediaCamera.h index b77bc135..0a7728a1 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/video/android/ImsMediaCamera.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/video/android/ImsMediaCamera.h @@ -101,14 +101,12 @@ public: sessionOutputs.clear(); targets.clear(); request = nullptr; - sessionSequenceId = -1; } std::vector<ANativeWindow*> outputNativeWindows; std::vector<ACaptureSessionOutput*> sessionOutputs; std::vector<ACameraOutputTarget*> targets; ACaptureRequest* request; ACameraDevice_request_template requestTemplate; - int sessionSequenceId; }; enum kCameraMode diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/video/android/ImsMediaPauseImageSource.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/video/android/ImsMediaPauseImageSource.h index 723499b9..66022fc2 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/video/android/ImsMediaPauseImageSource.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/video/android/ImsMediaPauseImageSource.h @@ -31,10 +31,11 @@ public: * * @param width width of the video frames. * @param height height of the video frames. + * @param stride stride of the video frames. * * @return returns true if the image is loaded and false in case of failure. */ - bool Initialize(int width, int height); + bool Initialize(int width, int height, int stride); /** * @brief Image YUV buffer loaded in memory is freed. @@ -58,7 +59,7 @@ private: AAsset* getImageAsset(); const char* getImageFilePath(); - int8_t* ConvertRgbaToYuv(int8_t* pixels, int width, int height); + int8_t* ConvertRgbaToYuv(int8_t* pixels, int width, int height, int stride); }; #endif // IMSMEDIA_JPEG_SOURCE_H_INCLUDED diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/video/android/ImsMediaVideoSource.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/video/android/ImsMediaVideoSource.h index 3bc63092..b1adce53 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/video/android/ImsMediaVideoSource.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/video/android/ImsMediaVideoSource.h @@ -137,7 +137,7 @@ public: * * @param bitrate The bitrate in bps units */ - void changeBitrate(const uint32_t bitrate); + bool changeBitrate(const uint32_t bitrate); /** * @brief Request a new IDR frame to the codec output streaming @@ -153,11 +153,10 @@ private: ANativeWindow* mWindow; AMediaCodec* mCodec; AMediaFormat* mFormat; - ANativeWindow* mRecordingSurface; ANativeWindow* mImageReaderSurface; AImageReader* mImageReader; std::mutex mMutex; - std::mutex mImageReaderMutex; + ImsMediaCondition mConditionExit; IVideoSourceCallback* mListener; ImsMediaPauseImageSource mPauseImageSource; int32_t mCodecType; @@ -168,6 +167,7 @@ private: uint32_t mCameraZoom; uint32_t mWidth; uint32_t mHeight; + int32_t mCodecStride; uint32_t mFramerate; uint32_t mBitrate; uint32_t mIntraInterval; diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/video/nodes/IVideoRendererNode.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/video/nodes/IVideoRendererNode.h index caa54322..3e4483e7 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/video/nodes/IVideoRendererNode.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/video/nodes/IVideoRendererNode.h @@ -29,6 +29,16 @@ #define USE_JITTER_BUFFER // off this definition ONLY for test purpose. #define DEMON_NTP2MSEC 65.55 +enum FrameType +{ + UNKNOWN, + SPS, + PPS, + VPS, + IDR, + NonIDR, +}; + /** * @brief This class describes an interface between depacketization module and audio device */ @@ -69,25 +79,25 @@ public: void SetPacketLossParam(uint32_t time, uint32_t rate); private: - bool IsIntraFrame(uint8_t* pbBuffer, uint32_t nBufferSize); - bool IsConfigFrame(uint8_t* pbBuffer, uint32_t nBufferSize, uint32_t* nBufferOffset = nullptr); - bool IsSps(uint8_t* pbBuffer, uint32_t nBufferSize, uint32_t* nBufferOffset = nullptr); - void SaveConfigFrame(uint8_t* pbBuffer, uint32_t nBufferSize, uint32_t type); + bool hasStartingCode(uint8_t* buffer, uint32_t bufferSize); + FrameType GetFrameType(uint8_t* buffer, uint32_t bufferSize); + void SaveConfigFrame(uint8_t* buffer, uint32_t bufferSize, uint32_t type); + /** * @brief Remove Access Uint Delimiter Nal Unit. * - * @param pInBuffer - * @param nInBufferSize - * @param pOutBuffer - * @param pOutBufferSize + * @param inBuffer + * @param ibufferSize + * @param outBuffer + * @param outBufferSize * @return true * @return false */ - bool RemoveAUDNalUnit(uint8_t* pInBuffer, uint32_t nInBufferSize, uint8_t** pOutBuffer, - uint32_t* pOutBufferSize); + bool RemoveAUDNalUnit( + uint8_t* inBuffer, uint32_t ibufferSize, uint8_t** outBuffer, uint32_t* outBufferSize); void CheckResolution(uint32_t nWidth, uint32_t nHeight); - ImsMediaResult ParseAvcSps(uint8_t* pbBuffer, uint32_t nBufferSize, tCodecConfig* pInfo); - ImsMediaResult ParseHevcSps(uint8_t* pbBuffer, uint32_t nBufferSize, tCodecConfig* pInfo); + ImsMediaResult ParseAvcSps(uint8_t* buffer, uint32_t bufferSize, tCodecConfig* config); + ImsMediaResult ParseHevcSps(uint8_t* buffer, uint32_t bufferSize, tCodecConfig* config); void QueueConfigFrame(uint32_t timestamp); void NotifyPeerDimensionChanged(); @@ -107,7 +117,6 @@ private: bool mFirstFrame; ImsMediaSubType mSubtype; uint32_t mFramerate; - uint32_t mWaitIntraFrame; uint32_t mLossDuration; uint32_t mLossRateThreshold; }; diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/video/nodes/IVideoSourceNode.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/video/nodes/IVideoSourceNode.h index bcc43d5b..17acd172 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/video/nodes/IVideoSourceNode.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/video/nodes/IVideoSourceNode.h @@ -46,6 +46,14 @@ public: * @param window surface buffer to update */ void UpdateSurface(ANativeWindow* window); + + /** + * @brief Set the bitrate threshold to notify the indication when the encoding video bitrate is + * less than the threshold values + * + * @param bitrate The video encoding bitrate in bps unit + */ + void SetBitrateThreshold(int32_t bitrate); // callback from ImsMediaVideoSource virtual void OnUplinkEvent(uint8_t* pBitstream, uint32_t nSize, int64_t pstUsec, uint32_t flag); virtual void OnEvent(int32_t type, int32_t param1, int32_t param2); @@ -68,6 +76,8 @@ protected: android::String8 mImagePath; uint32_t mDeviceOrientation; ANativeWindow* mWindow; + int32_t mMinBitrateThreshold; + bool mBitrateNotified; }; #endif
\ No newline at end of file diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/nodes/BaseNode.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/nodes/BaseNode.cpp index 6ee0e6b5..70986fac 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/nodes/BaseNode.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/nodes/BaseNode.cpp @@ -268,6 +268,21 @@ bool BaseNode::GetData(ImsMediaSubType* psubtype, uint8_t** ppData, uint32_t* pn } } +void BaseNode::AddData(uint8_t* data, uint32_t size, uint32_t timestamp, bool mark, uint32_t seq, + ImsMediaSubType subtype, ImsMediaSubType dataType, uint32_t arrivalTime, int32_t index) +{ + DataEntry entry = DataEntry(); + entry.pbBuffer = data; + entry.nBufferSize = size; + entry.nTimestamp = timestamp; + entry.bMark = mark; + entry.nSeqNum = seq; + entry.eDataType = dataType; + entry.subtype = subtype; + entry.arrivalTime = arrivalTime; + index == -1 ? mDataQueue.Add(&entry) : mDataQueue.InsertAt(index, &entry); +} + void BaseNode::DeleteData() { mDataQueue.Delete(); diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/nodes/RtcpDecoderNode.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/nodes/RtcpDecoderNode.cpp index 7682bfc7..e104c2cb 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/nodes/RtcpDecoderNode.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/nodes/RtcpDecoderNode.cpp @@ -18,6 +18,10 @@ #include <ImsMediaTrace.h> #include <ImsMediaVideoUtil.h> +#ifdef DEBUG_BITRATE_CHANGE_SIMULATION +static int32_t gTestBitrate = 384000; +#endif + RtcpDecoderNode::RtcpDecoderNode(BaseSessionCallback* callback) : BaseNode(callback) { @@ -84,7 +88,10 @@ void RtcpDecoderNode::OnDataFromFrontNode(ImsMediaSubType subtype, uint8_t* pDat IMLOGD_PACKET6(IM_PACKET_LOG_RTCP, "[OnMediaDataInd] media[%d] subtype[%d], Size[%d], TS[%u], Mark[%d], Seq[%d]", mMediaType, subtype, nDataSize, nTimeStamp, bMark, nSeqNum); - mRtpSession->ProcRtcpPacket(pData, nDataSize); + if (mRtpSession != nullptr) + { + mRtpSession->ProcRtcpPacket(pData, nDataSize); + } } bool RtcpDecoderNode::IsRunTime() @@ -142,6 +149,13 @@ void RtcpDecoderNode::OnRtcpInd(tRtpSvc_IndicationFromStack type, void* data) { mCallback->SendEvent(kCollectPacketInfo, kStreamRtcp); } +#ifdef DEBUG_BITRATE_CHANGE_SIMULATION + else if (mMediaType == IMS_MEDIA_VIDEO) + { + gTestBitrate *= 0.8; + mCallback->SendEvent(kRequestVideoBitrateChange, gTestBitrate); + } +#endif } break; case RTPSVC_RECEIVE_RTCP_RR_IND: @@ -154,6 +168,13 @@ void RtcpDecoderNode::OnRtcpInd(tRtpSvc_IndicationFromStack type, void* data) { mCallback->SendEvent(kCollectPacketInfo, kStreamRtcp); } +#ifdef DEBUG_BITRATE_CHANGE_SIMULATION + else if (mMediaType == IMS_MEDIA_VIDEO) + { + gTestBitrate *= 0.8; + mCallback->SendEvent(kRequestVideoBitrateChange, gTestBitrate); + } +#endif } break; case RTPSVC_RECEIVE_RTCP_FB_IND: @@ -274,7 +295,7 @@ void RtcpDecoderNode::ReceiveTmmbr(const tRtpSvcIndSt_ReceiveRtcpFeedbackInd* pa ImsMediaVideoUtil::ConvertBitrateToPower(bitrate, exp, mantissa); InternalRequestEventParam* pParam = new InternalRequestEventParam( - kRtpFbTmmbr, TmmbrParams(receivedSsrc, exp, mantissa, receivedOverhead)); + kRtpFbTmmbn, TmmbrParams(receivedSsrc, exp, mantissa, receivedOverhead)); mCallback->SendEvent(kRequestVideoSendTmmbn, reinterpret_cast<uint64_t>(pParam)); } 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 bcd35a6d..cd4ca421 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 @@ -36,6 +36,7 @@ RtpEncoderNode::RtpEncoderNode(BaseSessionCallback* callback) : mRtpTxDtmfPayload = 0; mRtpRxDtmfPayload = 0; mDtmfSamplingRate = 0; + mDtmfTimestamp = 0; mCvoValue = CVO_DEFINE_NONE; mRedundantLevel = 0; mRedundantPayload = 0; @@ -289,7 +290,7 @@ bool RtpEncoderNode::SetCvoExtension(const int64_t facing, const int64_t orienta IMLOGD3("[SetCvoExtension] cvoValue[%d], facing[%ld], orientation[%ld]", mCvoValue, facing, orientation); - if (mCvoValue != -1) + if (mCvoValue > 0) { uint32_t rotation = 0; uint32_t cameraId = 0; @@ -337,6 +338,7 @@ bool RtpEncoderNode::SetCvoExtension(const int64_t facing, const int64_t orienta extensionData[2] = 0; // padding extensionData[3] = 0; // padding + mListRtpExtension.clear(); mListRtpExtension.push_back(RtpHeaderExtensionInfo( RtpHeaderExtensionInfo::kBitPatternForOneByteHeader, 1, extensionData, 4)); return true; @@ -445,18 +447,16 @@ bool RtpEncoderNode::ProcessAudioData(ImsMediaSubType subtype, uint8_t* data, ui return false; } + mMark ? mDtmfTimestamp = currentTimestamp : timeDiff = 0; mPrevTimestamp = currentTimestamp; timestampDiff = timeDiff * mSamplingRate; - IMLOGD_PACKET2(IM_PACKET_LOG_RTP, "[ProcessAudioData] dtmf payload, size[%u], TS[%u]", - size, currentTimestamp); + IMLOGD_PACKET3(IM_PACKET_LOG_RTP, + "[ProcessAudioData] dtmf payload, size[%u], TS[%u], diff[%d]", size, + mDtmfTimestamp, timestampDiff); mRtpSession->SendRtpPacket( - mRtpTxDtmfPayload, data, size, currentTimestamp, mMark, timestampDiff); - - if (mMark) - { - mMark = false; - } + mRtpTxDtmfPayload, data, size, mDtmfTimestamp, mMark, timestampDiff); + mMark = false; } } else // MEDIASUBTYPE_RTPPAYLOAD @@ -480,8 +480,6 @@ bool RtpEncoderNode::ProcessAudioData(ImsMediaSubType subtype, uint8_t* data, ui } else if (timeDiff == 0) { - IMLOGD_PACKET2(IM_PACKET_LOG_RTP, "[ProcessAudioData] skip, prev[%u] curr[%u]", - mPrevTimestamp, currentTimestamp); return false; } else @@ -496,8 +494,8 @@ bool RtpEncoderNode::ProcessAudioData(ImsMediaSubType subtype, uint8_t* data, ui kCollectPacketInfo, kStreamRtpTx, reinterpret_cast<uint64_t>(packet)); timestampDiff = timeDiff * mSamplingRate; - IMLOGD_PACKET3(IM_PACKET_LOG_RTP, "[ProcessAudioData] PayloadTx[%d], Size[%d], TS[%d]", - mRtpPayloadTx, size, currentTimestamp); + IMLOGD_PACKET3(IM_PACKET_LOG_RTP, "[ProcessAudioData] size[%u], TS[%u], diff[%d]", size, + currentTimestamp, timestampDiff); if (!mListRtpExtension.empty()) { @@ -524,13 +522,23 @@ bool RtpEncoderNode::ProcessAudioData(ImsMediaSubType subtype, uint8_t* data, ui void RtpEncoderNode::ProcessVideoData( ImsMediaSubType subtype, uint8_t* data, uint32_t size, uint32_t timestamp, bool mark) { - IMLOGD_PACKET2( - IM_PACKET_LOG_RTP, "[ProcessVideoData] nSize[%d], timestamp[%u]", size, timestamp); + IMLOGD_PACKET4(IM_PACKET_LOG_RTP, "[ProcessVideoData] subtype[%d], size[%d], TS[%u], mark[%d]", + subtype, size, timestamp, mark); + +#ifdef SIMULATE_VIDEO_CVO_UPDATE + const int64_t kCameraFacing = kCameraFacingFront; + static int64_t sDeviceOrientation = 0; + static int64_t sCount = 0; + if ((++sCount % 100) == 0) + { + SetCvoExtension(kCameraFacing, (sDeviceOrientation += 90) % 360); + } +#endif if (mCvoValue > 0 && mark && subtype == MEDIASUBTYPE_VIDEO_IDR_FRAME) { - mRtpSession->SendRtpPacket( - mRtpPayloadTx, data, size, timestamp, mark, 0, &mListRtpExtension.front()); + mRtpSession->SendRtpPacket(mRtpPayloadTx, data, size, timestamp, mark, 0, + mListRtpExtension.empty() ? nullptr : &mListRtpExtension.front()); } else { diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/TextJitterBuffer.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/TextJitterBuffer.cpp index beb1d015..9ed07c0a 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/TextJitterBuffer.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/TextJitterBuffer.cpp @@ -32,11 +32,9 @@ void TextJitterBuffer::Reset() } void TextJitterBuffer::Add(ImsMediaSubType subtype, uint8_t* buffer, uint32_t size, - uint32_t timestamp, bool mark, uint32_t seqNum, ImsMediaSubType dataType, + uint32_t timestamp, bool mark, uint32_t seqNum, ImsMediaSubType /*dataType*/, uint32_t arrivalTime) { - (void)dataType; - IMLOGD_PACKET6(IM_PACKET_LOG_JITTER, "[Add] seq[%u], mark[%u], TS[%u], size[%u], lastPlayedSeq[%u], arrivalTime[%u]", seqNum, mark, timestamp, size, mLastPlayedSeqNum, arrivalTime); @@ -64,11 +62,6 @@ void TextJitterBuffer::Add(ImsMediaSubType subtype, uint8_t* buffer, uint32_t si if (mDataQueue.GetCount() == 0) // jitter buffer is empty { mDataQueue.Add(&currEntry); - - if (!mFirstFrameReceived) - { - mFirstFrameReceived = true; - } } else { @@ -169,6 +162,11 @@ void TextJitterBuffer::Delete() return; } + if (!mFirstFrameReceived) + { + mFirstFrameReceived = true; + } + mLastPlayedSeqNum = pEntry->nSeqNum; mLastPlayedTimestamp = pEntry->nTimestamp; mDataQueue.Delete(); diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/TextManager.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/TextManager.cpp index f50a0ba8..33ad7a20 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/TextManager.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/TextManager.cpp @@ -21,9 +21,18 @@ using namespace android; TextManager* TextManager::manager; -TextManager::TextManager() {} +TextManager::TextManager() +{ + mRequestHandler.Init("TEXT_REQUEST_EVENT"); + mResponseHandler.Init("TEXT_RESPONSE_EVENT"); +} -TextManager::~TextManager() {} +TextManager::~TextManager() +{ + mRequestHandler.Deinit(); + mResponseHandler.Deinit(); + manager = nullptr; +} TextManager* TextManager::getInstance() { @@ -200,13 +209,6 @@ void TextManager::sendMessage(const int sessionId, const android::Parcel& parcel } } -TextManager::RequestHandler::RequestHandler() : - ImsMediaEventHandler("TEXT_REQUEST_EVENT") -{ -} - -TextManager::RequestHandler::~RequestHandler() {} - void TextManager::RequestHandler::processEvent( uint32_t event, uint64_t sessionId, uint64_t paramA, uint64_t paramB) { @@ -294,13 +296,6 @@ void TextManager::RequestHandler::processEvent( } } -TextManager::ResponseHandler::ResponseHandler() : - ImsMediaEventHandler("TEXT_RESPONSE_EVENT") -{ -} - -TextManager::ResponseHandler::~ResponseHandler() {} - void TextManager::ResponseHandler::processEvent( uint32_t event, uint64_t sessionId, uint64_t paramA, uint64_t paramB) { diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/TextStreamGraphRtcp.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/TextStreamGraphRtcp.cpp index 922981e6..db36beda 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/TextStreamGraphRtcp.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/TextStreamGraphRtcp.cpp @@ -94,7 +94,7 @@ ImsMediaResult TextStreamGraphRtcp::update(RtpConfig* config) if (*mConfig == *pConfig) { - IMLOGD0("[update] no update"); + IMLOGI0("[update] no update"); return RESULT_SUCCESS; } @@ -105,7 +105,7 @@ ImsMediaResult TextStreamGraphRtcp::update(RtpConfig* config) mConfig = new TextConfig(pConfig); - if (mConfig->getRtcpConfig().getIntervalSec() == 0) + if (mConfig->getMediaDirection() == RtpConfig::MEDIA_DIRECTION_NO_FLOW) { IMLOGI0("[update] pause RTCP"); return stop(); @@ -132,7 +132,8 @@ ImsMediaResult TextStreamGraphRtcp::update(RtpConfig* config) } } - if (mGraphState == kStreamStateCreated && mConfig->getRtcpConfig().getIntervalSec() != 0) + if (mGraphState == kStreamStateCreated && + mConfig->getMediaDirection() != RtpConfig::MEDIA_DIRECTION_NO_FLOW) { IMLOGI0("[update] resume RTCP"); return start(); @@ -147,6 +148,24 @@ ImsMediaResult TextStreamGraphRtcp::update(RtpConfig* config) return ret; } +ImsMediaResult TextStreamGraphRtcp::start() +{ + IMLOGI1("[start] state[%d]", mGraphState); + + if (mConfig == nullptr) + { + return RESULT_INVALID_PARAM; + } + + if (mConfig->getMediaDirection() != RtpConfig::MEDIA_DIRECTION_NO_FLOW) + { + return BaseStreamGraph::start(); + } + + // not started + return RESULT_SUCCESS; +} + bool TextStreamGraphRtcp::setMediaQualityThreshold(MediaQualityThreshold* threshold) { if (threshold != nullptr) diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/TextStreamGraphRtpRx.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/TextStreamGraphRtpRx.cpp index fa7eafa8..a4456320 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/TextStreamGraphRtpRx.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/TextStreamGraphRtpRx.cpp @@ -89,7 +89,7 @@ ImsMediaResult TextStreamGraphRtpRx::update(RtpConfig* config) if (*mConfig == *pConfig) { - IMLOGD0("[update] no update"); + IMLOGI0("[update] no update"); return RESULT_SUCCESS; } diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/TextStreamGraphRtpTx.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/TextStreamGraphRtpTx.cpp index 5976bb00..902709b3 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/TextStreamGraphRtpTx.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/TextStreamGraphRtpTx.cpp @@ -98,7 +98,7 @@ ImsMediaResult TextStreamGraphRtpTx::update(RtpConfig* config) if (*mConfig == *pConfig) { - IMLOGD0("[update] no update"); + IMLOGI0("[update] no update"); return RESULT_SUCCESS; } diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/nodes/TextRendererNode.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/nodes/TextRendererNode.cpp index 628248cc..254081d1 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/nodes/TextRendererNode.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/nodes/TextRendererNode.cpp @@ -114,20 +114,6 @@ void TextRendererNode::ProcessData() "[ProcessData] size[%u], TS[%u], mark[%u], seq[%u], last seq[%u]", size, timestamp, mark, seq, mLastPlayedSeq); - // ignore empty t.140 - if (size == 0) - { - mLastPlayedSeq = (uint16_t)seq; - DeleteData(); - break; - } - - if (data == nullptr) - { - IMLOGD0("[ProcessData] invalid data"); - break; - } - if (mFirstFrameReceived) { // detect lost packet diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/nodes/TextRtpPayloadDecoderNode.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/nodes/TextRtpPayloadDecoderNode.cpp index 284311e2..901f88c6 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/nodes/TextRtpPayloadDecoderNode.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/nodes/TextRtpPayloadDecoderNode.cpp @@ -118,10 +118,10 @@ void TextRtpPayloadDecoderNode::DecodeT140(uint8_t* data, uint32_t size, ImsMedi if (subtype == MEDIASUBTYPE_BITSTREAM_T140 || subtype == MEDIASUBTYPE_BITSTREAM_T140_RED) { - std::list<uint32_t> lstTSOffset; - std::list<uint32_t> lstLength; - uint32_t nReadByte = 0; - uint32_t nRedCount = 0; + std::list<uint32_t> listTimestampOffset; + std::list<uint32_t> listLength; + uint32_t readByte = 0; + uint16_t redundantCount = 0; mBitReader.SetBuffer(data, size); @@ -136,7 +136,6 @@ void TextRtpPayloadDecoderNode::DecodeT140(uint8_t* data, uint32_t size, ImsMedi */ // Primary Data Only - // Red header + primary header = 5 byte if (subtype == MEDIASUBTYPE_BITSTREAM_T140) { SendDataToRearNode(MEDIASUBTYPE_BITSTREAM_T140, data, size, timestamp, mark, seq); @@ -144,69 +143,54 @@ void TextRtpPayloadDecoderNode::DecodeT140(uint8_t* data, uint32_t size, ImsMedi } // Redundant data included - while (mBitReader.Read(1) == 1) // Rendundancy bit + while (mBitReader.Read(1) == 1) // redundant flag bit { - uint32_t nPT = mBitReader.Read(7); // T140 payload type - uint32_t nTSOffset = mBitReader.Read(14); - uint32_t nLen = mBitReader.Read(10); + uint32_t payloadType = mBitReader.Read(7); // T140 payload type + uint32_t timestampOffset = mBitReader.Read(14); + uint32_t length = mBitReader.Read(10); - lstTSOffset.push_back(nTSOffset); // timestamp offset - lstLength.push_back(nLen); // block length + listTimestampOffset.push_back(timestampOffset); // timestamp offset + listLength.push_back(length); // block length - IMLOGD_PACKET3(IM_PACKET_LOG_PH, "[DecodeT140] nPT[%u], nTSOffset[%u], nLen[%u]", nPT, - nTSOffset, nLen); - nReadByte += 4; - nRedCount++; + IMLOGD_PACKET3(IM_PACKET_LOG_PH, "[DecodeT140] PT[%u], TSOffset[%u], size[%u]", + payloadType, timestampOffset, length); + readByte += 4; + redundantCount++; } mBitReader.Read(7); // T140 payload type (111) - nReadByte += 1; + readByte += 1; // redundant data - while (lstTSOffset.size() > 0) + while (listTimestampOffset.size() > 0) { - uint32_t nRedTimestamp = 0; - uint32_t nRedLength = 0; - - nRedTimestamp = lstTSOffset.front(); - nRedLength = lstLength.front(); - - if (nRedLength > 0) - { - uint32_t nRedSeqNum = 0; - // here should compare mPayload size red length - mBitReader.ReadByteBuffer(mPayload, nRedLength * 8); - nReadByte += nRedLength; - - if (seq < nRedCount) - { - nRedSeqNum = seq + 0xffff - nRedCount; // round trip - } - else - { - nRedSeqNum = seq - nRedCount; - } - - IMLOGD_PACKET3(IM_PACKET_LOG_PH, - "[DecodeT140] nRedTimestamp[%u], nRedLength[%u], nRedSeqNum[%u]", - timestamp - nRedTimestamp, nRedLength, nRedSeqNum); - SendDataToRearNode(MEDIASUBTYPE_BITSTREAM_T140, mPayload, nRedLength, - timestamp - nRedTimestamp, mark, nRedSeqNum); - } - - nRedCount--; - lstTSOffset.pop_front(); - lstLength.pop_front(); + uint32_t redundantTimestamp = listTimestampOffset.front(); + uint32_t redundantLength = listLength.front(); + + // read redundant payload + mBitReader.ReadByteBuffer(mPayload, redundantLength * 8); + readByte += redundantLength; + + uint16_t redundantSeqNum = seq - redundantCount; + + IMLOGD_PACKET3(IM_PACKET_LOG_PH, "[DecodeT140] red TS[%u], size[%u], seq[%u]", + timestamp - redundantTimestamp, redundantLength, redundantSeqNum); + SendDataToRearNode(MEDIASUBTYPE_BITSTREAM_T140, mPayload, redundantLength, + timestamp - redundantTimestamp, mark, redundantSeqNum); + + redundantCount--; + listTimestampOffset.pop_front(); + listLength.pop_front(); } // primary data - if (size - nReadByte > 0) + if (size - readByte > 0) { - mBitReader.ReadByteBuffer(mPayload, (size - nReadByte) * 8); + mBitReader.ReadByteBuffer(mPayload, (size - readByte) * 8); } SendDataToRearNode( - MEDIASUBTYPE_BITSTREAM_T140, mPayload, (size - nReadByte), timestamp, mark, seq); + MEDIASUBTYPE_BITSTREAM_T140, mPayload, (size - readByte), timestamp, mark, seq); } else { diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/nodes/TextSourceNode.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/nodes/TextSourceNode.cpp index 82fc1b58..20a01154 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/nodes/TextSourceNode.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/nodes/TextSourceNode.cpp @@ -30,17 +30,7 @@ TextSourceNode::TextSourceNode(BaseSessionCallback* callback) : mSentBOM = false; } -TextSourceNode::~TextSourceNode() -{ - while (mListTextSource.size() > 0) - { - uint8_t* text = mListTextSource.front(); - delete text; - mListTextSource.pop_front(); - } - - mListTextSourceSize.clear(); -} +TextSourceNode::~TextSourceNode() {} kBaseNodeId TextSourceNode::GetNodeId() { @@ -49,7 +39,7 @@ kBaseNodeId TextSourceNode::GetNodeId() ImsMediaResult TextSourceNode::Start() { - IMLOGD2("[Start] codec[%d], Redundant Level[%d]", mCodecType, mRedundantLevel); + IMLOGD2("[Start] codec[%d], redundant level[%d]", mCodecType, mRedundantLevel); if (mCodecType == TextConfig::TEXT_CODEC_NONE) { @@ -117,52 +107,27 @@ void TextSourceNode::ProcessData() return; } - std::lock_guard<std::mutex> guard(mMutex); - - if (mBomEnabled == true && mSentBOM == false) + if (mBomEnabled && !mSentBOM) { - SendBOM(); + SendBom(); mSentBOM = true; } - uint32_t nLoopCount = 1; - - if (mListTextSource.size() >= T140_MAX_CHUNK) - { - nLoopCount = T140_MAX_CHUNK; - } - - uint32_t nSendingDataSize = 0; - memset(mTextToSend, 0, MAX_RTT_LEN); - - for (uint32_t i = 0; - (i < nLoopCount && !mListTextSource.empty() && !mListTextSourceSize.empty()); i++) - { - // get first node data - uint8_t* pData = mListTextSource.front(); - uint32_t nDataSize = mListTextSourceSize.front(); - - if (pData == nullptr || nDataSize == 0) - { - continue; - } + std::lock_guard<std::mutex> guard(mMutex); - memcpy(mTextToSend + nSendingDataSize, pData, nDataSize); - nSendingDataSize += nDataSize; + ImsMediaSubType subtype = MEDIASUBTYPE_UNDEFINED; + ImsMediaSubType datatype = MEDIASUBTYPE_UNDEFINED; + uint8_t* data = NULL; + uint32_t size = 0; + uint32_t timestamp = 0; + bool mark = false; + uint32_t seq = 0; - free(pData); - mListTextSourceSize.pop_front(); - mListTextSource.pop_front(); - } - - if (nSendingDataSize > 0) + if (GetData(&subtype, &data, &size, ×tamp, &mark, &seq, &datatype)) { - // send it one char - IMLOGD_PACKET1(IM_PACKET_LOG_TEXT, "[ProcessData] send[%s]", mTextToSend); - mTimeLastSent = ImsMediaTimer::GetTimeInMilliSeconds(); - SendDataToRearNode(MEDIASUBTYPE_BITSTREAM_T140, mTextToSend, nSendingDataSize, - mTimeLastSent, false, 0); + SendDataToRearNode(MEDIASUBTYPE_BITSTREAM_T140, data, size, mTimeLastSent, false, 0); + DeleteData(); mRedundantCount = mRedundantLevel; @@ -178,7 +143,7 @@ void TextSourceNode::ProcessData() * after the selected buffering time, an empty T140block SHOULD be transmitted. This * situation is regarded as the beginning of an idle period. */ - IMLOGD1("[ProcessData] send empty, nRedCount[%d]", mRedundantCount); + IMLOGD1("[ProcessData] send empty, redundant count[%d]", mRedundantCount); // send default if there is no data to send SendDataToRearNode(MEDIASUBTYPE_BITSTREAM_T140, nullptr, 0, ImsMediaTimer::GetTimeInMilliSeconds(), false, 0); @@ -188,72 +153,26 @@ void TextSourceNode::ProcessData() void TextSourceNode::SendRtt(const android::String8* text) { - if (text == nullptr || text->length() == 0) + if (text == NULL || text->length() == 0 || text->length() > MAX_RTT_LEN) { IMLOGE0("[SendRtt] invalid data"); return; } - IMLOGD2("[SendRtt] size[%u], listSize[%d]", text->length(), mListTextSource.size()); - std::lock_guard<std::mutex> guard(mMutex); + IMLOGD2("[SendRtt] size[%u], listSize[%d]", text->length(), mDataQueue.GetCount()); - uint8_t* pData = (uint8_t*)text->string(); - uint32_t nSize = text->length(); - uint32_t nChunkSize = 0; + uint8_t tempBuffer[MAX_RTT_LEN] = {'\0'}; + memcpy(tempBuffer, text->string(), text->length()); - while (nSize > 0) - { - // split with UTF-8 - if (pData[0] >= 0xC2 && pData[0] <= 0xDF && nSize >= 2) - { - nChunkSize = 2; - } - else if (pData[0] >= 0xE0 && pData[0] <= 0xEF && nSize >= 3) - { - nChunkSize = 3; - } - else if (pData[0] >= 0xF0 && pData[0] <= 0xFF && nSize >= 4) - { - nChunkSize = 4; - } - else // 1byte - { - nChunkSize = 1; - } - - uint8_t* buffer = (uint8_t*)calloc(nChunkSize, sizeof(uint8_t)); - - if (buffer == nullptr) - { - IMLOGE0("[SendRtt] allocated data is null"); - return; - } - - memcpy(buffer, pData, nChunkSize); - mListTextSource.push_back(buffer); - mListTextSourceSize.push_back(nChunkSize); - pData += nChunkSize; - nSize -= nChunkSize; - } + std::lock_guard<std::mutex> guard(mMutex); + AddData(tempBuffer, text->length(), 0, false, 0); } -void TextSourceNode::SendBOM() +void TextSourceNode::SendBom() { - const uint32_t sizeofBom = 3; - uint8_t* buffer = (uint8_t*)calloc(sizeofBom, sizeof(uint8_t)); - - if (buffer == nullptr) - { - IMLOGE0("[SendBOM] allocated data is null"); - return; - } - - // send BOM - The BOM character shall be 0xEFBBBF for UTF-8 - buffer[0] = 0xef; - buffer[1] = 0xbb; - buffer[2] = 0xbf; - IMLOGD0("[ProcessData] send BOM"); - mListTextSource.push_front(buffer); - mListTextSourceSize.push_front(sizeofBom); + uint8_t bom[3] = {0xEF, 0xBB, 0xBF}; + + std::lock_guard<std::mutex> guard(mMutex); + AddData(bom, sizeof(bom), 0, false, 0, MEDIASUBTYPE_UNDEFINED, MEDIASUBTYPE_UNDEFINED, 0, 0); }
\ No newline at end of file diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/utils/ImsMediaBitWriter.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/utils/ImsMediaBitWriter.cpp index cba37f55..91d69581 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/utils/ImsMediaBitWriter.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/utils/ImsMediaBitWriter.cpp @@ -40,15 +40,17 @@ void ImsMediaBitWriter::SetBuffer(uint8_t* pbBuffer, uint32_t nBufferSize) mMaxBufferSize = nBufferSize; } -void ImsMediaBitWriter::Write(uint32_t nValue, uint32_t nSize) +bool ImsMediaBitWriter::Write(uint32_t nValue, uint32_t nSize) { if (nSize == 0) - return; + { + return false; + } if (mBuffer == nullptr || nSize > 24 || mBufferFull) { IMLOGE2("[Write] nSize[%d], BufferFull[%d]", nSize, mBufferFull); - return; + return false; } // write to bit buffer @@ -67,9 +69,11 @@ void ImsMediaBitWriter::Write(uint32_t nValue, uint32_t nSize) { mBufferFull = true; } + + return true; } -void ImsMediaBitWriter::WriteByteBuffer(uint8_t* pbSrc, uint32_t nBitSize) +bool ImsMediaBitWriter::WriteByteBuffer(uint8_t* pbSrc, uint32_t nBitSize) { uint32_t nByteSize; uint32_t nRemainBitSize; @@ -87,7 +91,10 @@ void ImsMediaBitWriter::WriteByteBuffer(uint8_t* pbSrc, uint32_t nBitSize) for (i = 0; i < nByteSize; i++) { - Write(pbSrc[i], 8); + if (!Write(pbSrc[i], 8)) + { + return false; + } } } @@ -95,11 +102,17 @@ void ImsMediaBitWriter::WriteByteBuffer(uint8_t* pbSrc, uint32_t nBitSize) { uint32_t v = pbSrc[nByteSize]; v >>= (8 - nRemainBitSize); - Write(v, nRemainBitSize); + + if (!Write(v, nRemainBitSize)) + { + return false; + } } + + return true; } -void ImsMediaBitWriter::WriteByteBuffer(uint32_t value) +bool ImsMediaBitWriter::WriteByteBuffer(uint32_t value) { uint32_t nRemainBitSize = 32; @@ -107,8 +120,14 @@ void ImsMediaBitWriter::WriteByteBuffer(uint32_t value) { nRemainBitSize -= 8; uint8_t v = (value >> nRemainBitSize) & 0x00ff; - Write(v, 8); + + if (!Write(v, 8)) + { + return false; + } } + + return true; } void ImsMediaBitWriter::Seek(uint32_t nSize) diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/utils/ImsMediaEventHandler.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/utils/ImsMediaEventHandler.cpp index b3d165ab..336f2aa5 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/utils/ImsMediaEventHandler.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/utils/ImsMediaEventHandler.cpp @@ -22,24 +22,29 @@ std::list<ImsMediaEventHandler*> ImsMediaEventHandler::gListEventHandler; std::mutex ImsMediaEventHandler::mMutex; -ImsMediaEventHandler::ImsMediaEventHandler(const char* strName) +ImsMediaEventHandler::ImsMediaEventHandler() {} + +ImsMediaEventHandler::~ImsMediaEventHandler() {} + +void ImsMediaEventHandler::Init(const char* strName) { strncpy(mName, strName, MAX_EVENTHANDLER_NAME); mbTerminate = false; gListEventHandler.push_back(this); - IMLOGD1("[ImsMediaEventHandler] %s", mName); + IMLOGD1("[Init] %s", mName); StartThread(); } -ImsMediaEventHandler::~ImsMediaEventHandler() +void ImsMediaEventHandler::Deinit() { - IMLOGD1("[~ImsMediaEventHandler] %s", mName); + IMLOGD2("[Deinit] %s, queue size[%d]", mName, mListevent.size()); + std::lock_guard<std::mutex> guard(mMutexEvent); + StopThread(); gListEventHandler.remove(this); mListevent.clear(); mListParamA.clear(); mListParamB.clear(); mListParamC.clear(); - StopThread(); mCondition.signal(); mConditionExit.wait(); } @@ -73,13 +78,12 @@ char* ImsMediaEventHandler::getName() void ImsMediaEventHandler::AddEvent( uint32_t event, uint64_t paramA, uint64_t paramB, uint64_t paramC) { + std::lock_guard<std::mutex> guard(mMutexEvent); IMLOGD3("[AddEvent] %s, event[%d], size[%d]", mName, event, mListevent.size()); - mMutexEvent.lock(); mListevent.push_back(event); mListParamA.push_back(paramA); mListParamB.push_back(paramB); mListParamC.push_back(paramC); - mMutexEvent.unlock(); mCondition.signal(); } @@ -94,17 +98,22 @@ void* ImsMediaEventHandler::run() for (;;) { - // lock + if (IsThreadStopped()) + { + break; + } + mMutexEvent.lock(); - if (IsThreadStopped() || mListevent.size() == 0) + + if (mListevent.size() == 0) { mMutexEvent.unlock(); break; } - mMutexEvent.unlock(); + processEvent(mListevent.front(), mListParamA.front(), mListParamB.front(), mListParamC.front()); - mMutexEvent.lock(); + mListevent.pop_front(); mListParamA.pop_front(); mListParamB.pop_front(); diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/utils/ImsMediaImageRotate.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/utils/ImsMediaImageRotate.cpp index 90947c19..46d01ceb 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/utils/ImsMediaImageRotate.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/utils/ImsMediaImageRotate.cpp @@ -15,13 +15,14 @@ */ #include "ImsMediaImageRotate.h" +#include <ImsMediaTrace.h> void ImsMediaImageRotate::YUV420_Planar_Rotate90_Flip( uint8_t* pbDst, uint8_t* pbSrc, uint16_t nSrcWidth, uint16_t nSrcHeight) { uint16_t x, y; uint64_t srcIdx, dstIdx; - const uint64_t size = nSrcWidth * nSrcHeight; + const size_t size = nSrcWidth * nSrcHeight; dstIdx = size - 1; // Rotate Y buffer @@ -57,41 +58,59 @@ void ImsMediaImageRotate::YUV420_Planar_Rotate90_Flip( } } -void ImsMediaImageRotate::YUV420_SP_Rotate90(uint8_t* pbDst, uint8_t* pYPlane, uint8_t* pUVPlane, - uint16_t nSrcWidth, uint16_t nSrcHeight) +int ImsMediaImageRotate::YUV420_SP_Rotate90(uint8_t* pOutBuffer, size_t nOutBufSize, + uint16_t outputStride, uint8_t* pYPlane, uint8_t* pUVPlane, uint16_t nSrcWidth, + uint16_t nSrcHeight) { - uint16_t x, y; - uint64_t srcIdx, dstIdx; - const uint64_t size = nSrcWidth * nSrcHeight; - dstIdx = size - 1; + uint16_t x, y, nDstWidth = nSrcHeight, nDstHt = nSrcWidth, nPadWidth = outputStride - nDstWidth; + uint64_t srcIdx, dstIdx = (outputStride * nDstHt) - 1; + const size_t dstSize = outputStride * nDstHt * 1.5f; + + if (nOutBufSize < (dstSize - nPadWidth)) + { + IMLOGE4("Output buffer size is not sufficient. \ + Required(outputStride[%d] * outputHeight[%d] * 1.5 = %d) but passed[%d]", + outputStride, nDstHt, dstSize, nOutBufSize); + return -1; + } + + if (nDstWidth > outputStride) + { + IMLOGE2("Destination width[%d] cannot be bigger than stride[%d]", nDstWidth, outputStride); + return -1; + } // Rotate Y buffer for (y = 0; y < nSrcWidth; y++) { + dstIdx -= nPadWidth; srcIdx = nSrcWidth - y - 1; for (x = 0; x < nSrcHeight; x++) { - pbDst[dstIdx] = pYPlane[srcIdx]; // Y + pOutBuffer[dstIdx] = pYPlane[srcIdx]; // Y srcIdx += nSrcWidth; dstIdx--; } } - dstIdx = (size * 1.5f) - 1; + dstIdx = dstSize - 1; nSrcWidth /= 2; nSrcHeight /= 2; // Rotate UV buffer for (y = 0; y < nSrcWidth; y++) { + dstIdx -= nPadWidth; srcIdx = (nSrcWidth - y - 1) * 2; for (x = 0; x < nSrcHeight; x++) { - pbDst[dstIdx--] = pUVPlane[srcIdx + 1]; // V - pbDst[dstIdx--] = pUVPlane[srcIdx]; // U + pOutBuffer[dstIdx--] = pUVPlane[srcIdx + 1]; // V + pOutBuffer[dstIdx--] = pUVPlane[srcIdx]; // U srcIdx += nSrcWidth * 2; } } + + return 0; } void ImsMediaImageRotate::YUV420_SP_Rotate90_Flip(uint8_t* pbDst, uint8_t* pYPlane, @@ -99,7 +118,7 @@ void ImsMediaImageRotate::YUV420_SP_Rotate90_Flip(uint8_t* pbDst, uint8_t* pYPla { uint16_t x, y; uint64_t srcIdx, dstIdx; - const uint64_t size = nSrcWidth * nSrcHeight; + const size_t size = nSrcWidth * nSrcHeight; dstIdx = size - 1; @@ -132,40 +151,58 @@ void ImsMediaImageRotate::YUV420_SP_Rotate90_Flip(uint8_t* pbDst, uint8_t* pYPla } } -void ImsMediaImageRotate::YUV420_SP_Rotate270(uint8_t* pbDst, uint8_t* pYPlane, uint8_t* pUVPlane, - uint16_t nSrcWidth, uint16_t nSrcHeight) +int ImsMediaImageRotate::YUV420_SP_Rotate270(uint8_t* pOutBuffer, size_t nOutBufSize, + uint16_t outputStride, uint8_t* pYPlane, uint8_t* pUVPlane, uint16_t nSrcWidth, + uint16_t nSrcHeight) { - uint16_t x, y; - uint64_t srcIdx, dstIdx; - const uint64_t size = nSrcWidth * nSrcHeight; + uint16_t x, y, nDstWth = nSrcHeight, nDstHt = nSrcWidth, nPadWidth = outputStride - nDstWth; + uint64_t srcIdx, dstIdx = outputStride * nDstHt - 1; + const size_t size = nSrcWidth * nSrcHeight; + const size_t dstSize = outputStride * nDstHt * 1.5f; - dstIdx = size - 1; + if (nOutBufSize < (dstSize - nPadWidth)) + { + IMLOGE4("Output buffer size is not sufficient. \ + Required(outputStride[%d] * outputHeight[%d] * 1.5 = %d) but passed[%d]", + outputStride, nDstHt, dstSize, nOutBufSize); + return -1; + } + + if (nDstWth > outputStride) + { + IMLOGE2("Destination width[%d] cannot be bigger than stride[%d]", nDstWth, outputStride); + return -1; + } // Rotate Y buffer for (y = 0; y < nSrcWidth; y++) { + dstIdx -= nPadWidth; srcIdx = size - nSrcWidth + y; for (x = 0; x < nSrcHeight; x++) { - pbDst[dstIdx] = pYPlane[srcIdx]; // Y + pOutBuffer[dstIdx] = pYPlane[srcIdx]; // Y srcIdx -= nSrcWidth; dstIdx--; } } - dstIdx = (size * 1.5f) - 1; + dstIdx = dstSize - 1; nSrcWidth /= 2; nSrcHeight /= 2; // Rotate UV buffer for (y = 0; y < nSrcWidth; y++) { + dstIdx -= nPadWidth; srcIdx = (size / 2) - (nSrcWidth - y) * 2; for (x = 0; x < nSrcHeight; x++) { - pbDst[dstIdx--] = pUVPlane[srcIdx + 1]; // V - pbDst[dstIdx--] = pUVPlane[srcIdx]; // U + pOutBuffer[dstIdx--] = pUVPlane[srcIdx + 1]; // V + pOutBuffer[dstIdx--] = pUVPlane[srcIdx]; // U srcIdx -= nSrcWidth * 2; } } + + return 0; }
\ No newline at end of file diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/ImsMediaVideoUtil.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/ImsMediaVideoUtil.cpp index 817a1153..4dea4783 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/ImsMediaVideoUtil.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/ImsMediaVideoUtil.cpp @@ -590,22 +590,20 @@ bool ImsMediaVideoUtil::ParseHevcSps(uint8_t* pbBuffer, uint32_t nBufferSize, tC ImsMediaBitReader objBitReader; objBitReader.SetBuffer(pbBuffer + nOffset, nBufferSize - nOffset); - objBitReader.Read(4); // sps_video_parameter_set_id; - uint32_t sps_max_sub_layers_minus1 = objBitReader.Read(3); - objBitReader.Read(1); // sps_temporal_id_nesting_flag; + objBitReader.Read(4); // sps_video_parameter_set_id; + uint32_t sps_max_sub_layers_minus1 = objBitReader.Read(3); // 0 + objBitReader.Read(1); // sps_temporal_id_nesting_flag; - /*-----------profile_tier_level start-----------------------*/ - objBitReader.Read(3); // general_profile_spac, general_tier_flag - objBitReader.Read(5); // general_profile_idc + objBitReader.Read(3); // general_profile_spac, general_tier_flag + pInfo->nProfile = objBitReader.Read(5); // general_profile_idc - // skip 13byte - flags, not handle - objBitReader.Read(24); + // skip 10byte - flags, not handle objBitReader.Read(24); objBitReader.Read(24); objBitReader.Read(24); objBitReader.Read(8); - objBitReader.Read(8); // general_level_idc + pInfo->nLevel = objBitReader.Read(8); // general_level_idc uint8_t sub_layer_profile_present_flag[sps_max_sub_layers_minus1]; uint8_t sub_layer_level_present_flag[sps_max_sub_layers_minus1]; @@ -645,19 +643,15 @@ bool ImsMediaVideoUtil::ParseHevcSps(uint8_t* pbBuffer, uint32_t nBufferSize, tC objBitReader.ReadByUEMode(); // sps_seq_parameter_set_id - uint32_t chroma_format_idc; - chroma_format_idc = objBitReader.ReadByUEMode(); + uint32_t chroma_format_idc = objBitReader.ReadByUEMode(); if (chroma_format_idc == 3) { objBitReader.Read(1); // separate_colour_plane_flag } - int32_t pic_width_in_luma_samples = objBitReader.ReadByUEMode(); - int32_t pic_height_in_luma_samples = objBitReader.ReadByUEMode(); - - pInfo->nWidth = pic_width_in_luma_samples; - pInfo->nHeight = pic_height_in_luma_samples; + pInfo->nWidth = objBitReader.ReadByUEMode(); + pInfo->nHeight = objBitReader.ReadByUEMode(); uint8_t conformance_window_flag = objBitReader.Read(1); 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 9d98be37..4652511a 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 @@ -21,9 +21,18 @@ using namespace android; VideoManager* VideoManager::manager; -VideoManager::VideoManager() {} +VideoManager::VideoManager() +{ + mRequestHandler.Init("VIDEO_REQUEST_EVENT"); + mResponseHandler.Init("VIDEO_RESPONSE_EVENT"); +} -VideoManager::~VideoManager() {} +VideoManager::~VideoManager() +{ + mRequestHandler.Deinit(); + mResponseHandler.Deinit(); + manager = nullptr; +} VideoManager* VideoManager::getInstance() { @@ -245,13 +254,6 @@ void VideoManager::SendInternalEvent( } } -VideoManager::RequestHandler::RequestHandler() : - ImsMediaEventHandler("VIDEO_REQUEST_EVENT") -{ -} - -VideoManager::RequestHandler::~RequestHandler() {} - void VideoManager::RequestHandler::processEvent( uint32_t event, uint64_t sessionId, uint64_t paramA, uint64_t paramB) { @@ -351,13 +353,6 @@ void VideoManager::RequestHandler::processEvent( } } -VideoManager::ResponseHandler::ResponseHandler() : - ImsMediaEventHandler("VIDEO_RESPONSE_EVENT") -{ -} - -VideoManager::ResponseHandler::~ResponseHandler() {} - void VideoManager::ResponseHandler::processEvent( uint32_t event, uint64_t sessionId, uint64_t paramA, uint64_t paramB) { @@ -407,12 +402,7 @@ void VideoManager::ResponseHandler::processEvent( // TODO : add implementation break; case kVideoMediaInactivityInd: - parcel.writeInt32(event); - parcel.writeInt32(static_cast<int>(paramA)); // type - parcel.writeInt32(static_cast<int>(paramB)); // duration - VideoManager::getInstance()->sendResponse(sessionId, parcel); - break; - case kVideoPacketLossInd: + case kVideoBitrateInd: parcel.writeInt32(event); parcel.writeInt32(static_cast<int>(paramA)); VideoManager::getInstance()->sendResponse(sessionId, parcel); diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/VideoSession.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/VideoSession.cpp index 074e37cf..ba43d0a0 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/VideoSession.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/VideoSession.cpp @@ -106,6 +106,7 @@ ImsMediaResult VideoSession::startGraph(RtpConfig* config) if (mGraphRtpTx != nullptr) { + mGraphRtpTx->setMediaQualityThreshold(&mThreshold); ret = mGraphRtpTx->update(config); if (ret != RESULT_SUCCESS) @@ -135,6 +136,7 @@ ImsMediaResult VideoSession::startGraph(RtpConfig* config) if (pConfig->getMediaDirection() == RtpConfig::MEDIA_DIRECTION_SEND_ONLY || pConfig->getMediaDirection() == RtpConfig::MEDIA_DIRECTION_SEND_RECEIVE) { + mGraphRtpTx->setMediaQualityThreshold(&mThreshold); ret = mGraphRtpTx->start(); } } @@ -265,6 +267,10 @@ void VideoSession::onEvent(int32_t type, uint64_t param1, uint64_t param2) ImsMediaEventHandler::SendEvent( "VIDEO_RESPONSE_EVENT", kVideoDataUsageInd, mSessionId, param1, param2); break; + case kImsMediaEventNotifyVideoLowestBitrate: + ImsMediaEventHandler::SendEvent( + "VIDEO_RESPONSE_EVENT", kVideoBitrateInd, mSessionId, param1, param2); + break; case kRequestVideoCvoUpdate: case kRequestVideoBitrateChange: case kRequestVideoIdrFrame: diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/VideoStreamGraphRtcp.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/VideoStreamGraphRtcp.cpp index 28ef246a..03a6f728 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/VideoStreamGraphRtcp.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/VideoStreamGraphRtcp.cpp @@ -94,7 +94,7 @@ ImsMediaResult VideoStreamGraphRtcp::update(RtpConfig* config) if (*reinterpret_cast<VideoConfig*>(mConfig) == *pConfig) { - IMLOGD0("[update] no update"); + IMLOGI0("[update] no update"); return RESULT_SUCCESS; } @@ -105,6 +105,12 @@ ImsMediaResult VideoStreamGraphRtcp::update(RtpConfig* config) mConfig = new VideoConfig(pConfig); + if (mConfig->getMediaDirection() == RtpConfig::MEDIA_DIRECTION_NO_FLOW) + { + IMLOGI0("[update] pause RTCP"); + return stop(); + } + ImsMediaResult ret = ImsMediaResult::RESULT_NOT_READY; // stop scheduler if (mGraphState == kStreamStateRunning) @@ -125,7 +131,8 @@ ImsMediaResult VideoStreamGraphRtcp::update(RtpConfig* config) } } - if (mGraphState == kStreamStateCreated && mConfig->getRtcpConfig().getIntervalSec() != 0) + if (mGraphState == kStreamStateCreated && + mConfig->getMediaDirection() != RtpConfig::MEDIA_DIRECTION_NO_FLOW) { IMLOGI0("[update] resume RTCP"); return start(); @@ -140,6 +147,24 @@ ImsMediaResult VideoStreamGraphRtcp::update(RtpConfig* config) return ret; } +ImsMediaResult VideoStreamGraphRtcp::start() +{ + IMLOGI1("[start] state[%d]", mGraphState); + + if (mConfig == nullptr) + { + return RESULT_INVALID_PARAM; + } + + if (mConfig->getMediaDirection() != RtpConfig::MEDIA_DIRECTION_NO_FLOW) + { + return BaseStreamGraph::start(); + } + + // not started + return RESULT_SUCCESS; +} + bool VideoStreamGraphRtcp::setMediaQualityThreshold(MediaQualityThreshold* threshold) { if (threshold != nullptr) diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/VideoStreamGraphRtpRx.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/VideoStreamGraphRtpRx.cpp index c93bf83a..30912f9e 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/VideoStreamGraphRtpRx.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/VideoStreamGraphRtpRx.cpp @@ -90,7 +90,7 @@ ImsMediaResult VideoStreamGraphRtpRx::update(RtpConfig* config) if (*reinterpret_cast<VideoConfig*>(mConfig) == *pConfig) { - IMLOGD0("[update] no update"); + IMLOGI0("[update] no update"); return RESULT_SUCCESS; } diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/VideoStreamGraphRtpTx.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/VideoStreamGraphRtpTx.cpp index ea3d4654..bc7c7dd0 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/VideoStreamGraphRtpTx.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/VideoStreamGraphRtpTx.cpp @@ -106,7 +106,7 @@ ImsMediaResult VideoStreamGraphRtpTx::update(RtpConfig* config) if (*reinterpret_cast<VideoConfig*>(mConfig) == *pConfig) { - IMLOGD0("[update] no update"); + IMLOGI0("[update] no update"); return RESULT_SUCCESS; } @@ -234,6 +234,23 @@ ImsMediaResult VideoStreamGraphRtpTx::start() return RESULT_SUCCESS; } +bool VideoStreamGraphRtpTx::setMediaQualityThreshold(MediaQualityThreshold* threshold) +{ + if (threshold != nullptr) + { + BaseNode* node = findNode(kNodeIdVideoSource); + + if (node != nullptr) + { + IVideoSourceNode* source = reinterpret_cast<IVideoSourceNode*>(node); + source->SetBitrateThreshold(threshold->getVideoBitrateBps()); + return true; + } + } + + return false; +} + void VideoStreamGraphRtpTx::setSurface(ANativeWindow* surface) { IMLOGI1("[setSurface] state[%d]", mGraphState); diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/android/ImsMediaCamera.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/android/ImsMediaCamera.cpp index 36a4f1eb..afca7820 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/android/ImsMediaCamera.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/android/ImsMediaCamera.cpp @@ -191,7 +191,6 @@ bool ImsMediaCamera::OpenCamera() return false; } - // Create back facing camera device camera_status_t status = ACameraManager_openCamera(mManager, mActiveCameraId.c_str(), GetDeviceListener(), &gCameraIds[mActiveCameraId].mDevice); @@ -220,6 +219,7 @@ bool ImsMediaCamera::OpenCamera() if (status == ACAMERA_OK) { mExposureRange.min = val.data.i64[0]; + if (mExposureRange.min < kMinExposureTime) { mExposureRange.min = kMinExposureTime; @@ -285,7 +285,7 @@ bool ImsMediaCamera::CreateSession(ANativeWindow* preview, ANativeWindow* record return false; } - if (MatchCaptureSizeRequest(preview) == false) + if (!MatchCaptureSizeRequest(preview)) { IMLOGE0("[CreateSession] resolution is not matched"); return false; @@ -339,7 +339,7 @@ bool ImsMediaCamera::CreateSession(ANativeWindow* preview, ANativeWindow* record } } - if (gCameraIds[mActiveCameraId].mAvailable == false) + if (!gCameraIds[mActiveCameraId].mAvailable) { gCondition.wait_timeout(MAX_WAIT_CAMERA); } @@ -395,39 +395,41 @@ bool ImsMediaCamera::DeleteSession() mCaptureSession = nullptr; } - for (int idxTarget = 0; idxTarget < mCaptureRequest.outputNativeWindows.size(); idxTarget++) + if (mCaptureRequest.request != nullptr) { - if (mCaptureRequest.outputNativeWindows[idxTarget] == nullptr) + for (int idxTarget = 0; idxTarget < mCaptureRequest.outputNativeWindows.size(); idxTarget++) { - continue; - } + if (mCaptureRequest.outputNativeWindows[idxTarget] == nullptr) + { + continue; + } - status = ACaptureRequest_removeTarget( - mCaptureRequest.request, mCaptureRequest.targets[idxTarget]); + status = ACaptureRequest_removeTarget( + mCaptureRequest.request, mCaptureRequest.targets[idxTarget]); - if (status != ACAMERA_OK) - { - IMLOGE1("[DeleteSession] error ACaptureRequest_removeTarget[%s]", GetErrorStr(status)); - } + if (status != ACAMERA_OK) + { + IMLOGE1("[DeleteSession] error ACaptureRequest_removeTarget[%s]", + GetErrorStr(status)); + } - ACameraOutputTarget_free(mCaptureRequest.targets[idxTarget]); - status = ACaptureSessionOutputContainer_remove( - mSessionOutputContainer, mCaptureRequest.sessionOutputs[idxTarget]); + ACameraOutputTarget_free(mCaptureRequest.targets[idxTarget]); + status = ACaptureSessionOutputContainer_remove( + mSessionOutputContainer, mCaptureRequest.sessionOutputs[idxTarget]); - if (status != ACAMERA_OK) - { - IMLOGE1("[DeleteSession] error ACaptureSessionOutputContainer_remove[%s]", - GetErrorStr(status)); - } + if (status != ACAMERA_OK) + { + IMLOGE1("[DeleteSession] error ACaptureSessionOutputContainer_remove[%s]", + GetErrorStr(status)); + } - ACaptureSessionOutput_free(mCaptureRequest.sessionOutputs[idxTarget]); - ANativeWindow_release(mCaptureRequest.outputNativeWindows[idxTarget]); - } + ACaptureSessionOutput_free(mCaptureRequest.sessionOutputs[idxTarget]); + ANativeWindow_release(mCaptureRequest.outputNativeWindows[idxTarget]); + } - if (mCaptureRequest.request != nullptr) - { IMLOGD0("[DeleteSession] free request"); ACaptureRequest_free(mCaptureRequest.request); + mCaptureRequest.request = nullptr; } mCaptureRequest.outputNativeWindows.resize(0); @@ -438,6 +440,7 @@ bool ImsMediaCamera::DeleteSession() { IMLOGD0("[DeleteSession] free container"); ACaptureSessionOutputContainer_free(mSessionOutputContainer); + mSessionOutputContainer = nullptr; } return true; @@ -575,7 +578,6 @@ void ImsMediaCamera::OnDeviceState(ACameraDevice* dev) IMLOGW1("[OnDeviceState] device %s is disconnected", id.c_str()); gCameraIds[id].mAvailable = false; ACameraDevice_close(gCameraIds[id].mDevice); - gCameraIds.erase(id); } /* diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/android/ImsMediaPauseImageSource.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/android/ImsMediaPauseImageSource.cpp index 29a472aa..58c2f9fa 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/android/ImsMediaPauseImageSource.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/android/ImsMediaPauseImageSource.cpp @@ -63,9 +63,10 @@ void ImsMediaPauseImageSource::Uninitialize() } } -bool ImsMediaPauseImageSource::Initialize(int width, int height) +bool ImsMediaPauseImageSource::Initialize(int width, int height, int stride) { - IMLOGD2("[ImsMediaPauseImageSource] Init(width:%d, height:%d)", width, height); + IMLOGD3("[ImsMediaPauseImageSource] Init(width:%d, height:%d, stride:%d)", width, height, + stride); mWidth = width; mHeight = height; @@ -105,12 +106,12 @@ bool ImsMediaPauseImageSource::Initialize(int width, int height) * (AndroidBitmapFormat)AImageDecoderHeaderInfo_getAndroidBitmapFormat(info); */ - size_t stride = AImageDecoder_getMinimumStride(decoder); // Image decoder does not + size_t decStride = AImageDecoder_getMinimumStride(decoder); // Image decoder does not // use padding by default - size_t size = height * stride; + size_t size = height * decStride; int8_t* pixels = reinterpret_cast<int8_t*>(malloc(size)); - result = AImageDecoder_decodeImage(decoder, pixels, stride, size); + result = AImageDecoder_decodeImage(decoder, pixels, decStride, size); if (result != ANDROID_IMAGE_DECODER_SUCCESS) { IMLOGE0("[ImsMediaPauseImageSource] error occurred, and the file could not be decoded."); @@ -119,7 +120,7 @@ bool ImsMediaPauseImageSource::Initialize(int width, int height) return false; } - mYuvImageBuffer = ConvertRgbaToYuv(pixels, width, height); + mYuvImageBuffer = ConvertRgbaToYuv(pixels, width, height, stride); AImageDecoder_delete(decoder); free(pixels); @@ -141,7 +142,8 @@ size_t ImsMediaPauseImageSource::GetYuvImage(uint8_t* buffer, size_t len) return mBufferSize; } - IMLOGE0("[ImsMediaPauseImageSource] buffer size is smaller. Cannot copy"); + IMLOGE2("[ImsMediaPauseImageSource] buffer size is smaller. Expected Bufsize[%d], passed[%d]", + mBufferSize, len); return 0; } @@ -200,31 +202,27 @@ const char* ImsMediaPauseImageSource::getImageFilePath() return nullptr; } -int8_t* ImsMediaPauseImageSource::ConvertRgbaToYuv(int8_t* pixels, int width, int height) +int8_t* ImsMediaPauseImageSource::ConvertRgbaToYuv( + int8_t* pixels, int width, int height, int stride) { // src array must be integer array, data have no padding alignment int32_t* pSrcArray = reinterpret_cast<int32_t*>(pixels); - mBufferSize = width * height * 1.5; + mBufferSize = stride * height * 1.5; int8_t* pDstArray = reinterpret_cast<int8_t*>(malloc(mBufferSize)); int32_t nYIndex = 0; - int32_t nUVIndex = width * height; - int32_t r, g, b; + int32_t nUVIndex = stride * height; + int32_t r, g, b, padLen = stride - width; double y, u, v; for (int32_t j = 0; j < height; j++) { + int32_t nIndex = width * j; for (int32_t i = 0; i < width; i++) { - int32_t nIndex = width * j + i; - - /* - * TODO: Decode alpha - * a = (pSrcArray[nIndex] & 0xff000000) >> 24; - */ - r = (pSrcArray[nIndex] & 0xff0000) >> 16; g = (pSrcArray[nIndex] & 0xff00) >> 8; b = (pSrcArray[nIndex] & 0xff) >> 0; + nIndex++; // rgb to yuv y = 0.257 * r + 0.504 * g + 0.098 * b + 16; @@ -240,7 +238,19 @@ int8_t* ImsMediaPauseImageSource::ConvertRgbaToYuv(int8_t* pixels, int width, in pDstArray[nUVIndex++] = (uint8_t)((u < 0) ? 0 : ((u > 255) ? 255 : u)); } } + + // Add padding if stride > width + if (padLen > 0) + { + nYIndex += padLen; + + if (j % 2 == 0) + { + nUVIndex += padLen; + } + } } + mBufferSize -= padLen; return pDstArray; } diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/android/ImsMediaVideoRenderer.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/android/ImsMediaVideoRenderer.cpp index a4fe774d..475425be 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/android/ImsMediaVideoRenderer.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/android/ImsMediaVideoRenderer.cpp @@ -210,10 +210,12 @@ void ImsMediaVideoRenderer::processBuffers() } auto index = AMediaCodec_dequeueInputBuffer(mCodec, CODEC_TIMEOUT_NANO); + if (index >= 0) { size_t bufferSize = 0; uint8_t* inputBuffer = AMediaCodec_getInputBuffer(mCodec, index, &bufferSize); + if (inputBuffer != nullptr) { FrameData* frame = mFrameDatas.front(); @@ -223,17 +225,8 @@ void ImsMediaVideoRenderer::processBuffers() "config[%d]", index, frame->size, frame->timestamp, frame->isConfig); - media_status_t err; - if (frame->isConfig) - { - err = AMediaCodec_queueInputBuffer(mCodec, index, 0, frame->size, - frame->timestamp * 1000, AMEDIACODEC_BUFFER_FLAG_CODEC_CONFIG); - } - else - { - err = AMediaCodec_queueInputBuffer( - mCodec, index, 0, frame->size, frame->timestamp * 1000, 0); - } + media_status_t err = AMediaCodec_queueInputBuffer( + mCodec, index, 0, frame->size, frame->timestamp * 1000, 0); if (err != AMEDIA_OK) { diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/android/ImsMediaVideoSource.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/android/ImsMediaVideoSource.cpp index c93d99a4..38b71771 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/android/ImsMediaVideoSource.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/android/ImsMediaVideoSource.cpp @@ -30,7 +30,6 @@ ImsMediaVideoSource::ImsMediaVideoSource() mWindow = nullptr; mCodec = nullptr; mFormat = nullptr; - mRecordingSurface = nullptr; mImageReaderSurface = nullptr; mImageReader = nullptr; mCodecType = -1; @@ -164,7 +163,6 @@ void ImsMediaVideoSource::SetDeviceOrientation(const uint32_t degree) bool ImsMediaVideoSource::Start() { IMLOGD1("[Start], VideoMode[%d]", mVideoMode); - mRecordingSurface = nullptr; if (mVideoMode == kVideoModeRecording || mVideoMode == kVideoModePauseImage) { @@ -214,27 +212,30 @@ bool ImsMediaVideoSource::Start() return false; } - if (mWidth > mHeight) // Is Landscape Mode - { - err = AMediaCodec_createInputSurface(mCodec, &mRecordingSurface); + mImageReaderSurface = CreateImageReader(mWidth, mHeight); - if (err != AMEDIA_OK) - { - IMLOGE1("[Start] create input surface error[%d]", err); - AMediaCodec_delete(mCodec); - mCodec = nullptr; - AMediaFormat_delete(mFormat); - mFormat = nullptr; - return false; - } - } - else + if (mImageReaderSurface == nullptr) { - mImageReaderSurface = CreateImageReader(mWidth, mHeight); + IMLOGE0("[Start] create image reader failed"); + return false; + } - if (mImageReaderSurface == nullptr) + mCodecStride = mWidth; + AMediaFormat* encoderInputFormat = AMediaCodec_getInputFormat(mCodec); + + if (encoderInputFormat != nullptr) + { + // Check if encoder is initialized with the expected configurations. + int32_t width = 0, height = 0; + AMediaFormat_getInt32(encoderInputFormat, AMEDIAFORMAT_KEY_WIDTH, &width); + AMediaFormat_getInt32(encoderInputFormat, AMEDIAFORMAT_KEY_HEIGHT, &height); + AMediaFormat_getInt32(encoderInputFormat, AMEDIAFORMAT_KEY_STRIDE, &mCodecStride); + AMediaFormat_delete(encoderInputFormat); + + // TODO: More configuration checks should be added + if (mWidth != width || mHeight != height || width > mCodecStride) { - IMLOGE0("[Start] create image reader failed"); + IMLOGE0("Encoder doesn't support requested configuration."); return false; } } @@ -268,9 +269,7 @@ bool ImsMediaVideoSource::Start() return false; } - ANativeWindow* recording = mRecordingSurface ? mRecordingSurface : mImageReaderSurface; - - if (mCamera->CreateSession(mWindow, recording) == false) + if (mCamera->CreateSession(mWindow, mImageReaderSurface) == false) { IMLOGE0("[Start] error create camera session"); AMediaCodec_delete(mCodec); @@ -294,15 +293,14 @@ bool ImsMediaVideoSource::Start() } else if (mVideoMode == kVideoModePauseImage) { - mPauseImageSource.Initialize(mWidth, mHeight); - } - - // start encoder output thread - if (mCodec != nullptr) - { - mStopped = false; - std::thread t1(&ImsMediaVideoSource::processOutputBuffer, this); - t1.detach(); + mPauseImageSource.Initialize(mWidth, mHeight, mCodecStride); + // start encoder output thread + if (mCodec != nullptr) + { + mStopped = false; + std::thread t1(&ImsMediaVideoSource::EncodePauseImage, this); + t1.detach(); + } } mDeviceOrientation = -1; @@ -314,19 +312,19 @@ void ImsMediaVideoSource::Stop() { IMLOGD0("[Stop]"); - mMutex.lock(); + std::lock_guard<std::mutex> guard(mMutex); mStopped = true; - mMutex.unlock(); - if (mCamera != nullptr) + if (mImageReader != nullptr) { - mCamera->StopSession(); + AImageReader_delete(mImageReader); + mImageReader = nullptr; + mImageReaderSurface = nullptr; } - IMLOGD0("[Stop] deinitialize camera"); - if (mCamera != nullptr) { + mCamera->StopSession(); mCamera->DeleteSession(); mCamera->DeInitialize(); mCamera = nullptr; @@ -334,19 +332,12 @@ void ImsMediaVideoSource::Stop() if (mCodec != nullptr) { - if (mRecordingSurface != nullptr) + if (mVideoMode == kVideoModePauseImage) { - AMediaCodec_signalEndOfInputStream(mCodec); + mConditionExit.wait_timeout(mFramerate != 0 ? 1000 / mFramerate : 66); } AMediaCodec_stop(mCodec); - - if (mRecordingSurface != nullptr) - { - ANativeWindow_release(mRecordingSurface); - mRecordingSurface = nullptr; - } - AMediaCodec_delete(mCodec); mCodec = nullptr; } @@ -357,15 +348,10 @@ void ImsMediaVideoSource::Stop() mFormat = nullptr; } - if (mImageReader != nullptr) + if (mVideoMode == kVideoModePauseImage) { - std::lock_guard<std::mutex> guard(mImageReaderMutex); - AImageReader_delete(mImageReader); - mImageReader = nullptr; - mImageReaderSurface = nullptr; + mPauseImageSource.Uninitialize(); } - - mPauseImageSource.Uninitialize(); } bool ImsMediaVideoSource::IsStopped() @@ -376,7 +362,7 @@ bool ImsMediaVideoSource::IsStopped() void ImsMediaVideoSource::onCameraFrame(AImage* pImage) { - std::lock_guard<std::mutex> guard(mImageReaderMutex); + std::lock_guard<std::mutex> guard(mMutex); if (mImageReader == nullptr || pImage == nullptr) { @@ -389,7 +375,6 @@ void ImsMediaVideoSource::onCameraFrame(AImage* pImage) { size_t buffCapacity = 0; uint8_t* encoderBuf = AMediaCodec_getInputBuffer(mCodec, index, &buffCapacity); - if (!encoderBuf || !buffCapacity) { IMLOGE1("[onCameraFrame] returned null buffer pointer or buffCapacity[%d]", @@ -397,69 +382,93 @@ void ImsMediaVideoSource::onCameraFrame(AImage* pImage) return; } - int32_t width, height, ylen, uvlen; + int32_t width, height, ylen, uvlen, result = 0; uint8_t *yPlane, *uvPlane; AImage_getWidth(pImage, &width); AImage_getHeight(pImage, &height); AImage_getPlaneData(pImage, 0, &yPlane, &ylen); AImage_getPlaneData(pImage, 1, &uvPlane, &uvlen); - int32_t facing, sensorOrientation; - mCamera->GetSensorOrientation(mCameraId, &facing, &sensorOrientation); - - switch (facing) + if (mWidth > mHeight) // landscape mode, copy without rotate { - case ACAMERA_LENS_FACING_FRONT: - { - ImsMediaImageRotate::YUV420_SP_Rotate270( - encoderBuf, yPlane, uvPlane, width, height); - } - break; + memcpy(encoderBuf, yPlane, ylen); + memcpy(encoderBuf + ylen, uvPlane, uvlen); + } + else + { + int32_t facing, sensorOrientation; + mCamera->GetSensorOrientation(mCameraId, &facing, &sensorOrientation); - case ACAMERA_LENS_FACING_BACK: + switch (facing) { - ImsMediaImageRotate::YUV420_SP_Rotate90(encoderBuf, yPlane, uvPlane, width, height); - } - break; + case ACAMERA_LENS_FACING_FRONT: + { + result = ImsMediaImageRotate::YUV420_SP_Rotate270( + encoderBuf, buffCapacity, mCodecStride, yPlane, uvPlane, width, height); + } + break; - case ACAMERA_LENS_FACING_EXTERNAL: - { - uint32_t size = width * height; - memcpy(encoderBuf, yPlane, size); - memcpy(encoderBuf + size, uvPlane, size / 2); + case ACAMERA_LENS_FACING_BACK: + { + result = ImsMediaImageRotate::YUV420_SP_Rotate90( + encoderBuf, buffCapacity, mCodecStride, yPlane, uvPlane, width, height); + } + break; + + case ACAMERA_LENS_FACING_EXTERNAL: + { + uint32_t size = width * height; + memcpy(encoderBuf, yPlane, size); + memcpy(encoderBuf + size, uvPlane, size / 2); + } + break; } - break; } IMLOGD_PACKET1(IM_PACKET_LOG_VIDEO, "[onCameraFrame] queue buffer size[%d]", ylen + uvlen); - AMediaCodec_queueInputBuffer( - mCodec, index, 0, ylen + uvlen, ImsMediaTimer::GetTimeInMicroSeconds(), 0); + if (result == 0) + { + AMediaCodec_queueInputBuffer( + mCodec, index, 0, ylen + uvlen, ImsMediaTimer::GetTimeInMicroSeconds(), 0); + } + else + { + IMLOGE5("Camera image resolution[%dx%d]. Encoder resolution[%dx%d] buffer size[%d]", + width, height, mWidth, mHeight, buffCapacity); + AMediaCodec_queueInputBuffer(mCodec, index, 0, 0, 0, 0); + return; + } } else { IMLOGE1("[onCameraFrame] dequeueInputBuffer returned index[%d]", index); } + + processOutputBuffer(); } -void ImsMediaVideoSource::changeBitrate(const uint32_t bitrate) +bool ImsMediaVideoSource::changeBitrate(const uint32_t bitrate) { IMLOGD1("[changeBitrate] bitrate[%d]", bitrate); std::lock_guard<std::mutex> guard(mMutex); if (mStopped) { - return; + return false; } AMediaFormat* params = AMediaFormat_new(); - AMediaFormat_setInt32(params, AMEDIAFORMAT_KEY_BIT_RATE, bitrate); + AMediaFormat_setInt32(params, AMEDIACODEC_KEY_VIDEO_BITRATE, bitrate); media_status_t status = AMediaCodec_setParameters(mCodec, params); if (status != AMEDIA_OK) { IMLOGE1("[changeBitrate] error[%d]", status); + return false; } + + return true; } void ImsMediaVideoSource::requestIdrFrame() @@ -484,29 +493,8 @@ void ImsMediaVideoSource::requestIdrFrame() void ImsMediaVideoSource::EncodePauseImage() { - auto index = AMediaCodec_dequeueInputBuffer(mCodec, CODEC_TIMEOUT_NANO); - if (index >= 0) - { - size_t buffCapacity = 0; - uint8_t* encoderBuf = AMediaCodec_getInputBuffer(mCodec, index, &buffCapacity); - if (!encoderBuf || !buffCapacity) - { - IMLOGE1("[EncodePauseImage] returned null buffer pointer or buffCapacity[%d]", - buffCapacity); - return; - } - size_t len = mPauseImageSource.GetYuvImage(encoderBuf, buffCapacity); - AMediaCodec_queueInputBuffer( - mCodec, index, 0, len, ImsMediaTimer::GetTimeInMicroSeconds(), 0); - } - else - { - IMLOGE1("[EncodePauseImage] dequeueInputBuffer returned index[%d]", index); - } -} + IMLOGD0("[EncodePauseImage] start"); -void ImsMediaVideoSource::processOutputBuffer() -{ uint32_t nextTime = ImsMediaTimer::GetTimeInMilliSeconds(); uint32_t timeInterval = 66; @@ -515,74 +503,37 @@ void ImsMediaVideoSource::processOutputBuffer() timeInterval = 1000 / mFramerate; } - IMLOGD2("[processOutputBuffer] interval[%d] CameraId[%d]", timeInterval, mCameraId); - - for (;;) + while (!IsStopped()) { - if (IsStopped()) - { - IMLOGD0("[processOutputBuffer] terminated"); - break; - } - - if (mVideoMode == kVideoModePauseImage) - { - EncodePauseImage(); - } - - AMediaCodecBufferInfo info; - auto index = AMediaCodec_dequeueOutputBuffer(mCodec, &info, CODEC_TIMEOUT_NANO); + mMutex.lock(); + auto index = AMediaCodec_dequeueInputBuffer(mCodec, CODEC_TIMEOUT_NANO); if (index >= 0) { - IMLOGD_PACKET5(IM_PACKET_LOG_VIDEO, - "[processOutputBuffer] index[%d], size[%d], offset[%d], time[%ld], flags[%d]", - index, info.size, info.offset, info.presentationTimeUs, info.flags); - - if (info.size > 0) - { - size_t buffCapacity; - uint8_t* buf = AMediaCodec_getOutputBuffer(mCodec, index, &buffCapacity); - - if (IsStopped()) - { - break; - } - - if (buf != nullptr && buffCapacity > 0) - { - if (mListener != nullptr) - { - mListener->OnUplinkEvent( - buf + info.offset, info.size, info.presentationTimeUs, info.flags); - } - } - - AMediaCodec_releaseOutputBuffer(mCodec, index, false); - } - } - else if (index == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) - { - IMLOGI0("[processOutputBuffer] Encoder output buffer changed"); - } - else if (index == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) - { - if (mFormat != nullptr) + size_t buffCapacity = 0; + uint8_t* encoderBuf = AMediaCodec_getInputBuffer(mCodec, index, &buffCapacity); + if (!encoderBuf || !buffCapacity) { - AMediaFormat_delete(mFormat); + IMLOGE1("[EncodePauseImage] returned null buffer pointer or buffCapacity[%d]", + buffCapacity); + return; } - mFormat = AMediaCodec_getOutputFormat(mCodec); - IMLOGI1("[processOutputBuffer] Encoder format changed, format[%s]", - AMediaFormat_toString(mFormat)); + size_t len = mPauseImageSource.GetYuvImage(encoderBuf, buffCapacity); + AMediaCodec_queueInputBuffer( + mCodec, index, 0, len, ImsMediaTimer::GetTimeInMicroSeconds(), 0); } - else if (index == AMEDIACODEC_INFO_TRY_AGAIN_LATER) + else { - IMLOGD_PACKET0(IM_PACKET_LOG_VIDEO, "[processOutputBuffer] no output buffer"); + IMLOGE1("[EncodePauseImage] dequeueInputBuffer returned index[%d]", index); } - else + + processOutputBuffer(); + mMutex.unlock(); + + if (IsStopped()) { - IMLOGI1("[processOutputBuffer] unexpected index[%d]", index); + break; } nextTime += timeInterval; @@ -591,28 +542,77 @@ void ImsMediaVideoSource::processOutputBuffer() if (nextTime > nCurrTime) { uint32_t timeDiff = nextTime - nCurrTime; - IMLOGD_PACKET1(IM_PACKET_LOG_VIDEO, "[processOutputBuffer] timeDiff[%u]", timeDiff); + IMLOGD_PACKET1(IM_PACKET_LOG_VIDEO, "[EncodePauseImage] timeDiff[%u]", timeDiff); ImsMediaTimer::Sleep(timeDiff); } } - IMLOGD0("[processOutputBuffer] exit"); + IMLOGD0("[EncodePauseImage] end"); + mConditionExit.signal(); } -static void ImageCallback(void* context, AImageReader* reader) +void ImsMediaVideoSource::processOutputBuffer() { - if (context == nullptr) + AMediaCodecBufferInfo info; + auto index = AMediaCodec_dequeueOutputBuffer(mCodec, &info, CODEC_TIMEOUT_NANO); + + if (index >= 0) { - return; + IMLOGD_PACKET5(IM_PACKET_LOG_VIDEO, + "[processOutputBuffer] index[%d], size[%d], offset[%d], time[%ld], flags[%d]", + index, info.size, info.offset, info.presentationTimeUs, info.flags); + + if (info.size > 0) + { + size_t buffCapacity; + uint8_t* buf = AMediaCodec_getOutputBuffer(mCodec, index, &buffCapacity); + + if (buf != nullptr && buffCapacity > 0) + { + if (mListener != nullptr) + { + mListener->OnUplinkEvent( + buf + info.offset, info.size, info.presentationTimeUs, info.flags); + } + } + + AMediaCodec_releaseOutputBuffer(mCodec, index, false); + } + } + else if (index == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) + { + IMLOGI0("[processOutputBuffer] Encoder output buffer changed"); } + else if (index == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) + { + if (mFormat != nullptr) + { + AMediaFormat_delete(mFormat); + } - ImsMediaVideoSource* pVideoSource = static_cast<ImsMediaVideoSource*>(context); + mFormat = AMediaCodec_getOutputFormat(mCodec); + IMLOGI1("[processOutputBuffer] Encoder format changed, format[%s]", + AMediaFormat_toString(mFormat)); + } + else if (index == AMEDIACODEC_INFO_TRY_AGAIN_LATER) + { + IMLOGD_PACKET0(IM_PACKET_LOG_VIDEO, "[processOutputBuffer] no output buffer"); + } + else + { + IMLOGI1("[processOutputBuffer] unexpected index[%d]", index); + } +} - if (pVideoSource->IsStopped()) +static void ImageCallback(void* context, AImageReader* reader) +{ + if (context == nullptr) { return; } + ImsMediaVideoSource* pVideoSource = static_cast<ImsMediaVideoSource*>(context); + AImage* image = nullptr; auto status = AImageReader_acquireNextImage(reader, &image); diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/nodes/IVideoRendererNode.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/nodes/IVideoRendererNode.cpp index 88713fd1..9cff5794 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/nodes/IVideoRendererNode.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/nodes/IVideoRendererNode.cpp @@ -53,7 +53,6 @@ IVideoRendererNode::IVideoRendererNode(BaseSessionCallback* callback) : mFirstFrame = false; mSubtype = MEDIASUBTYPE_UNDEFINED; mFramerate = 0; - mWaitIntraFrame = 0; mLossDuration = 0; mLossRateThreshold = 0; } @@ -87,7 +86,7 @@ ImsMediaResult IVideoRendererNode::Start() mVideoRenderer->SetDeviceOrientation(mDeviceOrientation); mVideoRenderer->SetSurface(mWindow); - if (mVideoRenderer->Start() == false) + if (!mVideoRenderer->Start()) { return RESULT_NOT_READY; } @@ -162,134 +161,105 @@ bool IVideoRendererNode::IsSameConfig(void* config) void IVideoRendererNode::ProcessData() { std::lock_guard<std::mutex> guard(mMutex); - uint8_t* pData = nullptr; - uint32_t nDataSize = 0; - uint32_t nTimeStamp = 0; - bool bMark = false; - uint32_t nSeqNum = 0; + uint8_t* data = nullptr; + uint32_t dataSize = 0; + uint32_t prevTimestamp = 0; + bool mark = false; + uint32_t seq = 0; uint32_t timestamp = 0; - uint32_t nBitstreamSize = 0; + uint32_t frameSize = 0; ImsMediaSubType subtype = MEDIASUBTYPE_UNDEFINED; - uint32_t nInitialSeqNum = 0; - uint32_t nBufferOffset = 0; + uint32_t initialSeq = 0; ImsMediaSubType dataType; - while (GetData(&subtype, &pData, &nDataSize, &nTimeStamp, &bMark, &nSeqNum, &dataType)) + while (GetData(&subtype, &data, &dataSize, ×tamp, &mark, &seq, &dataType)) { IMLOGD_PACKET4(IM_PACKET_LOG_VIDEO, - "[ProcessData] subtype[%d], Size[%d], TimeStamp[%d] nBitstreamSize[%d]", subtype, - nDataSize, nTimeStamp, nBitstreamSize); + "[ProcessData] subtype[%d], Size[%d], TS[%d] frameSize[%d]", subtype, dataSize, + timestamp, frameSize); - if (timestamp == 0) + if (prevTimestamp == 0) { - timestamp = nTimeStamp; + prevTimestamp = timestamp; } - else if (timestamp != nTimeStamp) + else if (timestamp != prevTimestamp || (frameSize != 0 && hasStartingCode(data, dataSize))) { + // break when the timestamp is changed or next data has another starting code break; } - if (nDataSize >= MAX_RTP_PAYLOAD_BUFFER_SIZE) + if (dataSize >= MAX_RTP_PAYLOAD_BUFFER_SIZE) { - IMLOGE1("[ProcessData] exceed buffer size[%d]", nDataSize); + IMLOGE1("[ProcessData] exceed buffer size[%d]", dataSize); return; } - memcpy(mBuffer + nBitstreamSize, pData, nDataSize); - nBitstreamSize += nDataSize; + memcpy(mBuffer + frameSize, data, dataSize); + frameSize += dataSize; - if (nInitialSeqNum == 0) + if (initialSeq == 0) { - nInitialSeqNum = nSeqNum; + initialSeq = seq; } DeleteData(); - if (bMark) + if (mark) { break; } } - if (nBitstreamSize == 0) + if (frameSize == 0) { return; } // remove AUD nal unit - uint32_t nDatabufferSize = nBitstreamSize; - uint8_t* pDataBuff = mBuffer; - RemoveAUDNalUnit(mBuffer, nBitstreamSize, &pDataBuff, &nDatabufferSize); - - // check Config String for updating config frame - nBufferOffset = 0; - if ((mCodecType == kVideoCodecHevc || mCodecType == kVideoCodecAvc) && - IsConfigFrame(pDataBuff, nDatabufferSize, &nBufferOffset) == true) + uint32_t size = frameSize; + uint8_t* buffer = mBuffer; + RemoveAUDNalUnit(mBuffer, frameSize, &buffer, &size); + + FrameType frameType = GetFrameType(buffer, size); + + if (frameType == SPS) { - SaveConfigFrame(pDataBuff + nBufferOffset, nDatabufferSize - nBufferOffset, kConfigSps); - SaveConfigFrame(pDataBuff + nBufferOffset, nDatabufferSize - nBufferOffset, kConfigPps); + SaveConfigFrame(buffer, size, kConfigSps); + tCodecConfig codecConfig; - if (mCodecType == kVideoCodecHevc) + if (mCodecType == kVideoCodecAvc) { - SaveConfigFrame(pDataBuff + nBufferOffset, nDatabufferSize - nBufferOffset, kConfigVps); + if (ImsMediaVideoUtil::ParseAvcSps(buffer, size, &codecConfig)) + { + CheckResolution(codecConfig.nWidth, codecConfig.nHeight); + } } - - if (IsSps(pDataBuff, nDatabufferSize, &nBufferOffset) == true) + else if (mCodecType == kVideoCodecHevc) { - IMLOGD_PACKET1( - IM_PACKET_LOG_VIDEO, "[ProcessData] parse SPS - nOffset[%d]", nBufferOffset); - tCodecConfig codecConfig; - - if (mCodecType == kVideoCodecAvc) + if (ImsMediaVideoUtil::ParseHevcSps(buffer, size, &codecConfig)) { - if (ImsMediaVideoUtil::ParseAvcSps(pDataBuff + nBufferOffset, - nDatabufferSize - nBufferOffset, &codecConfig) == true) - { - CheckResolution(codecConfig.nWidth, codecConfig.nHeight); - } - } - else if (mCodecType == kVideoCodecHevc) - { - if (ImsMediaVideoUtil::ParseHevcSps(pDataBuff + nBufferOffset, - nDatabufferSize - nBufferOffset, &codecConfig) == true) - { - CheckResolution(codecConfig.nWidth, codecConfig.nHeight); - } + CheckResolution(codecConfig.nWidth, codecConfig.nHeight); } } return; } - - IMLOGD_PACKET2(IM_PACKET_LOG_VIDEO, "[ProcessData] nBitstreamSize[%d] nDatabufferSize[%d]", - nBitstreamSize, nDatabufferSize); - - bool isIntraFrame = IsIntraFrame(pDataBuff, nDatabufferSize); - - // drop non-idr frame when idr frame is not received - if (mWaitIntraFrame > 0 && nDatabufferSize > 0) + else if (frameType == PPS) { - if (isIntraFrame) - { - mWaitIntraFrame = 0; - } - else - { - // Send FIR when I-frame wasn't received - if ((mWaitIntraFrame % mFramerate) == 0) // every 1 sec - { - // TODO: send PLI event - IMLOGD0("[ProcessData] request Send PLI"); - } + SaveConfigFrame(buffer, size, kConfigPps); + return; + } + else if (frameType == VPS) + { + SaveConfigFrame(buffer, size, kConfigVps); + return; + } - mWaitIntraFrame--; - nDatabufferSize = 0; // drop non-DIR frame + IMLOGD_PACKET2(IM_PACKET_LOG_VIDEO, "[ProcessData] frame type[%d] size[%d]", frameType, size); - IMLOGD1("[ProcessData] wait intra frame[%d]", mWaitIntraFrame); - } - } + // TODO: Send PLI or FIR when I-frame wasn't received since beginning. - if (mFirstFrame == false) + if (!mFirstFrame) { IMLOGD0("[ProcessData] notify first frame"); mFirstFrame = true; @@ -297,6 +267,11 @@ void IVideoRendererNode::ProcessData() if (mCallback != nullptr) { mCallback->SendEvent(kImsMediaEventFirstPacketReceived); + + if (mCvoValue <= 0) + { + mCallback->SendEvent(kImsMediaEventResolutionChanged, mWidth, mHeight); + } } } @@ -308,7 +283,8 @@ void IVideoRendererNode::ProcessData() subtype = MEDIASUBTYPE_ROT0; } - if (mSubtype != subtype) + // rotation changed + if (mSubtype != subtype && (subtype >= MEDIASUBTYPE_ROT0 && subtype <= MEDIASUBTYPE_ROT270)) { mSubtype = subtype; int degree = 0; @@ -335,13 +311,13 @@ void IVideoRendererNode::ProcessData() } } - // send sps/pps before send I frame - if (isIntraFrame) + // send config frames before send I frame + if (frameType == IDR) { QueueConfigFrame(timestamp); } - mVideoRenderer->OnDataFrame(pDataBuff, nDatabufferSize, timestamp, false); + mVideoRenderer->OnDataFrame(buffer, size, timestamp, false); } void IVideoRendererNode::UpdateSurface(ANativeWindow* window) @@ -371,214 +347,84 @@ void IVideoRendererNode::SetPacketLossParam(uint32_t time, uint32_t rate) mLossRateThreshold = rate; } -bool IVideoRendererNode::IsIntraFrame(uint8_t* pbBuffer, uint32_t nBufferSize) +bool IVideoRendererNode::hasStartingCode(uint8_t* buffer, uint32_t bufferSize) { - bool bIntraFrame = false; - - if (nBufferSize <= 4) + if (bufferSize <= 4) { return false; } - IMLOGD_PACKET2(IM_PACKET_LOG_VIDEO, "[IsIntraFrame] size[%d], data[%s]", nBufferSize, - ImsMediaTrace::IMTrace_Bin2String( - reinterpret_cast<const char*>(pbBuffer), nBufferSize > 16 ? 16 : nBufferSize)); - - switch (mCodecType) + // Check for NAL unit delimiter 0x00000001 + if (buffer[0] == 0x00 && buffer[1] == 0x00 && buffer[2] == 0x00 && buffer[3] == 0x01) { - case kVideoCodecAvc: - { - uint32_t nCurrSize = nBufferSize; - uint8_t* nCurrBuff = pbBuffer; - - while (nCurrSize >= 5) - { - if (nCurrBuff[0] == 0x00 && nCurrBuff[1] == 0x00 && nCurrBuff[2] == 0x00 && - nCurrBuff[3] == 0x01 && (nCurrBuff[4] & 0x1F) == 5) - { - bIntraFrame = true; - break; - } - nCurrBuff++; - nCurrSize--; - } - - break; - } - case kVideoCodecHevc: - { - uint32_t nCurrSize = nBufferSize; - uint8_t* nCurrBuff = pbBuffer; - while (nCurrSize >= 5) - { - if (nCurrBuff[0] == 0x00 && nCurrBuff[1] == 0x00 && nCurrBuff[2] == 0x00 && - nCurrBuff[3] == 0x01 && - (((nCurrBuff[4] >> 1) & 0x3F) == 19 || ((nCurrBuff[4] >> 1) & 0x3F) == 20)) - { - bIntraFrame = true; - break; - } - nCurrBuff++; - nCurrSize--; - } - break; - } - default: - IMLOGE1("[IsIntraFrame] Invalid video codec type %d", mCodecType); - return true; + return true; } - return bIntraFrame; + return false; } -bool IVideoRendererNode::IsConfigFrame( - uint8_t* pbBuffer, uint32_t nBufferSize, uint32_t* nBufferOffset) +FrameType IVideoRendererNode::GetFrameType(uint8_t* buffer, uint32_t bufferSize) { - bool bConfigFrame = false; - - if (nBufferSize <= 4) - return false; + if (!hasStartingCode(buffer, bufferSize)) + { + return UNKNOWN; + } - IMLOGD_PACKET2(IM_PACKET_LOG_VIDEO, "[IsConfigFrame] size[%d], data[%s]", nBufferSize, - ImsMediaTrace::IMTrace_Bin2String( - reinterpret_cast<const char*>(pbBuffer), nBufferSize > 16 ? 16 : nBufferSize)); + uint8_t nalType = buffer[4]; switch (mCodecType) { case kVideoCodecAvc: { - uint32_t nOffset = 0; - uint32_t nCurrSize = nBufferSize; - uint8_t* nCurrBuff = pbBuffer; - - while (nCurrSize >= 5) + if ((nalType & 0x1F) == 5) { - if (nCurrBuff[0] == 0x00 && nCurrBuff[1] == 0x00 && nCurrBuff[2] == 0x00 && - nCurrBuff[3] == 0x01 && - ((nCurrBuff[4] & 0x1F) == 7 || ((nCurrBuff[4] & 0x1F) == 8))) - { - bConfigFrame = true; - - if (nBufferOffset) - { - *nBufferOffset = nOffset; - } - break; - } - - nOffset++; - nCurrBuff++; - nCurrSize--; + return IDR; } - break; - } - case kVideoCodecHevc: - { - uint32_t nOffset = 0; - uint32_t nCurrSize = nBufferSize; - uint8_t* nCurrBuff = pbBuffer; - - while (nCurrSize >= 5) + else if ((nalType & 0x1F) == 7) { - if (nCurrBuff[0] == 0x00 && nCurrBuff[1] == 0x00 && nCurrBuff[2] == 0x00 && - nCurrBuff[3] == 0x01 && - (((nCurrBuff[4] >> 1) & 0x3F) == 32 || ((nCurrBuff[4] >> 1) & 0x3F) == 33 || - ((nCurrBuff[4] >> 1) & 0x3F) == 34)) - { - bConfigFrame = true; - if (nBufferOffset) - { - *nBufferOffset = nOffset; - } - break; - } - nOffset++; - nCurrBuff++; - nCurrSize--; + return SPS; } - break; - } - default: - return false; - } - - return bConfigFrame; -} - -bool IVideoRendererNode::IsSps(uint8_t* pbBuffer, uint32_t nBufferSize, uint32_t* nBufferOffset) -{ - bool bSPS = false; - if (nBufferSize <= 4) - { - return false; - } - - IMLOGD_PACKET2(IM_PACKET_LOG_VIDEO, "[IsSps] size[%d], data[%s]", nBufferSize, - ImsMediaTrace::IMTrace_Bin2String( - reinterpret_cast<const char*>(pbBuffer), nBufferSize > 16 ? 16 : nBufferSize)); - - switch (mCodecType) - { - case kVideoCodecAvc: - { - uint32_t nOffset = 0; - uint32_t nCurrSize = nBufferSize; - uint8_t* nCurrBuff = pbBuffer; - - while (nCurrSize >= 5) + else if ((nalType & 0x1F) == 8) { - if (nCurrBuff[0] == 0x00 && nCurrBuff[1] == 0x00 && nCurrBuff[2] == 0x00 && - nCurrBuff[3] == 0x01 && (nCurrBuff[4] & 0x1F) == 7) - { - bSPS = true; - - if (nBufferOffset) - { - *nBufferOffset = nOffset; - } - - break; - } - - nOffset++; - nCurrBuff++; - nCurrSize--; + return PPS; + } + else + { + return NonIDR; } break; } case kVideoCodecHevc: { - uint32_t nOffset = 0; - uint32_t nCurrSize = nBufferSize; - uint8_t* nCurrBuff = pbBuffer; - - while (nCurrSize >= 5) + if (((nalType >> 1) & 0x3F) == 19 || ((nalType >> 1) & 0x3F) == 20) { - if (nCurrBuff[0] == 0x00 && nCurrBuff[1] == 0x00 && nCurrBuff[2] == 0x00 && - nCurrBuff[3] == 0x01 && ((nCurrBuff[4] >> 1) & 0x3F) == 33) - { - bSPS = true; - - if (nBufferOffset) - { - *nBufferOffset = nOffset; - } - - break; - } - - nOffset++; - nCurrBuff++; - nCurrSize--; + return IDR; + } + else if (((nalType >> 1) & 0x3F) == 32) + { + return VPS; + } + else if (((nalType >> 1) & 0x3F) == 33) + { + return SPS; + } + else if (((nalType >> 1) & 0x3F) == 34) + { + return PPS; + } + else + { + return NonIDR; } + break; } default: - return false; + IMLOGE1("[GetFrameType] Invalid video codec type %d", mCodecType); } - return bSPS; + return UNKNOWN; } void IVideoRendererNode::SaveConfigFrame(uint8_t* pbBuffer, uint32_t nBufferSize, uint32_t eMode) @@ -610,18 +456,17 @@ void IVideoRendererNode::SaveConfigFrame(uint8_t* pbBuffer, uint32_t nBufferSize if (nCurrBuff[0] == 0x00 && nCurrBuff[1] == 0x00 && nCurrBuff[2] == 0x00 && nCurrBuff[3] == 0x01) { - if (eMode == kConfigSps && bSPSString == false && ((nCurrBuff[4] & 0x1F) == 7)) + if (eMode == kConfigSps && !bSPSString && ((nCurrBuff[4] & 0x1F) == 7)) { nOffset = nCurrSize; bSPSString = true; } - else if (eMode == kConfigPps && bPPSString == false && - ((nCurrBuff[4] & 0x1F) == 8)) + else if (eMode == kConfigPps && !bPPSString && ((nCurrBuff[4] & 0x1F) == 8)) { nOffset = nCurrSize; bPPSString = true; } - else if (bSPSString == true || bPPSString == true) + else if (bSPSString || bPPSString) { nConfigSize = nCurrSize - nOffset; break; @@ -684,21 +529,20 @@ void IVideoRendererNode::SaveConfigFrame(uint8_t* pbBuffer, uint32_t nBufferSize if (nCurrBuff[0] == 0x00 && nCurrBuff[1] == 0x00 && nCurrBuff[2] == 0x00 && nCurrBuff[3] == 0x01) { - if (eMode == kConfigVps && bVPSString == false && - (((nCurrBuff[4] >> 1) & 0x3F) == 32)) + if (eMode == kConfigVps && !bVPSString && (((nCurrBuff[4] >> 1) & 0x3F) == 32)) { nOffset = nCurrSize; bVPSString = true; break; } - else if (eMode == kConfigSps && bSPSString == false && + else if (eMode == kConfigSps && !bSPSString && (((nCurrBuff[4] >> 1) & 0x3F) == 33)) { nOffset = nCurrSize; bSPSString = true; break; } - else if (eMode == kConfigPps && bPPSString == false && + else if (eMode == kConfigPps && !bPPSString && (((nCurrBuff[4] >> 1) & 0x3F) == 34)) { nOffset = nCurrSize; @@ -711,7 +555,7 @@ void IVideoRendererNode::SaveConfigFrame(uint8_t* pbBuffer, uint32_t nBufferSize nCurrSize++; } - if (bVPSString == true || bSPSString == true || bPPSString == true) + if (bVPSString || bSPSString || bPPSString) { if ((nBufferSize - nOffset) > 0) { @@ -762,13 +606,13 @@ void IVideoRendererNode::SaveConfigFrame(uint8_t* pbBuffer, uint32_t nBufferSize } bool IVideoRendererNode::RemoveAUDNalUnit( - uint8_t* pInBuffer, uint32_t nInBufferSize, uint8_t** ppOutBuffer, uint32_t* pOutBufferSize) + uint8_t* inBuffer, uint32_t inBufferSize, uint8_t** outBuffer, uint32_t* outBufferSize) { - bool bAUDUnit = false; - *ppOutBuffer = pInBuffer; - *pOutBufferSize = nInBufferSize; + bool IsAudUnit = false; + *outBuffer = inBuffer; + *outBufferSize = inBufferSize; - if (nInBufferSize <= 4) + if (inBufferSize <= 4) { return false; } @@ -777,29 +621,29 @@ bool IVideoRendererNode::RemoveAUDNalUnit( { case kVideoCodecAvc: { - uint32_t nCurrSize = nInBufferSize; - uint8_t* nCurrBuff = pInBuffer; - uint32_t nCnt = 0; + uint32_t currSize = inBufferSize; + uint8_t* currBuffer = inBuffer; + uint32_t count = 0; - while (nCurrSize >= 5 && nCnt <= 12) + while (currSize >= 5 && count <= 12) { - if (bAUDUnit == true && - (nCurrBuff[0] == 0x00 && nCurrBuff[1] == 0x00 && nCurrBuff[2] == 0x00 && - nCurrBuff[3] == 0x01)) + if (IsAudUnit && + (currBuffer[0] == 0x00 && currBuffer[1] == 0x00 && currBuffer[2] == 0x00 && + currBuffer[3] == 0x01)) { - *ppOutBuffer = nCurrBuff; - *pOutBufferSize = nCurrSize; + *outBuffer = currBuffer; + *outBufferSize = currSize; break; } - if (nCurrBuff[0] == 0x00 && nCurrBuff[1] == 0x00 && nCurrBuff[2] == 0x00 && - nCurrBuff[3] == 0x01 && nCurrBuff[4] == 0x09) + if (currBuffer[0] == 0x00 && currBuffer[1] == 0x00 && currBuffer[2] == 0x00 && + currBuffer[3] == 0x01 && currBuffer[4] == 0x09) { - bAUDUnit = true; + IsAudUnit = true; } - nCurrBuff++; - nCurrSize--; - nCnt++; + currBuffer++; + currSize--; + count++; } } break; @@ -808,7 +652,7 @@ bool IVideoRendererNode::RemoveAUDNalUnit( return false; } - return bAUDUnit; + return IsAudUnit; } void IVideoRendererNode::CheckResolution(uint32_t nWidth, uint32_t nHeight) @@ -838,16 +682,16 @@ void IVideoRendererNode::QueueConfigFrame(uint32_t timestamp) for (int32_t i = 0; i < nNumOfConfigString; i++) { - uint8_t* pConfigData = nullptr; - uint32_t nConfigLen = mConfigLen[i]; - pConfigData = mConfigBuffer[i]; + uint8_t* configFrame = nullptr; + uint32_t configLen = mConfigLen[i]; + configFrame = mConfigBuffer[i]; - if (nConfigLen == 0 || mVideoRenderer == nullptr) + if (configLen == 0 || mVideoRenderer == nullptr) { continue; } - mVideoRenderer->OnDataFrame(pConfigData, nConfigLen, timestamp, true); + mVideoRenderer->OnDataFrame(configFrame, configLen, timestamp, true); } } @@ -858,6 +702,8 @@ void IVideoRendererNode::NotifyPeerDimensionChanged() return; } + IMLOGD1("[NotifyPeerDimensionChanged] subtype[%d]", mSubtype); + // assume the device is portrait if (mWidth > mHeight) // landscape { @@ -869,7 +715,7 @@ void IVideoRendererNode::NotifyPeerDimensionChanged() { mCallback->SendEvent(kImsMediaEventResolutionChanged, mWidth, mHeight); } - else + else if (mSubtype == MEDIASUBTYPE_ROT90 || mSubtype == MEDIASUBTYPE_ROT270) { mCallback->SendEvent(kImsMediaEventResolutionChanged, mHeight, mWidth); } @@ -881,7 +727,7 @@ void IVideoRendererNode::NotifyPeerDimensionChanged() { mCallback->SendEvent(kImsMediaEventResolutionChanged, mHeight, mWidth); } - else + else if (mSubtype == MEDIASUBTYPE_ROT90 || mSubtype == MEDIASUBTYPE_ROT270) { mCallback->SendEvent(kImsMediaEventResolutionChanged, mWidth, mHeight); } @@ -894,7 +740,7 @@ void IVideoRendererNode::NotifyPeerDimensionChanged() { mCallback->SendEvent(kImsMediaEventResolutionChanged, mWidth, mHeight); } - else + else if (mSubtype == MEDIASUBTYPE_ROT90 || mSubtype == MEDIASUBTYPE_ROT270) { mCallback->SendEvent(kImsMediaEventResolutionChanged, mHeight, mWidth); } diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/nodes/IVideoSourceNode.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/nodes/IVideoSourceNode.cpp index 65b429e2..c95f6240 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/nodes/IVideoSourceNode.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/nodes/IVideoSourceNode.cpp @@ -45,6 +45,8 @@ IVideoSourceNode::IVideoSourceNode(BaseSessionCallback* callback) : mImagePath = ""; mDeviceOrientation = 0; mWindow = nullptr; + mMinBitrateThreshold = 0; + mBitrateNotified = false; } IVideoSourceNode::~IVideoSourceNode() {} @@ -57,7 +59,6 @@ kBaseNodeId IVideoSourceNode::GetNodeId() ImsMediaResult IVideoSourceNode::Start() { IMLOGD3("[Start] codec[%d], mode[%d], cameraId[%d]", mCodecType, mVideoMode, mCameraId); - std::lock_guard<std::mutex> guard(mMutex); if (mVideoSource) { @@ -84,12 +85,13 @@ ImsMediaResult IVideoSourceNode::Start() mVideoSource->SetSurface(mWindow); - if (mVideoSource->Start() == false) + if (!mVideoSource->Start()) { return RESULT_NOT_READY; } mVideoSource->SetDeviceOrientation(mDeviceOrientation); + mBitrateNotified = false; } mNodeState = kNodeStateRunning; @@ -99,7 +101,6 @@ ImsMediaResult IVideoSourceNode::Start() void IVideoSourceNode::Stop() { IMLOGD0("[Stop]"); - std::lock_guard<std::mutex> guard(mMutex); if (mVideoSource) { @@ -263,9 +264,9 @@ void IVideoSourceNode::UpdateSurface(ANativeWindow* window) mWindow = window; } -void IVideoSourceNode::OnUplinkEvent(uint8_t* data, uint32_t size, int64_t timestamp, uint32_t flag) +void IVideoSourceNode::OnUplinkEvent( + uint8_t* data, uint32_t size, int64_t timestamp, uint32_t /*flag*/) { - (void)flag; IMLOGD_PACKET2( IM_PACKET_LOG_VIDEO, "[OnUplinkEvent] size[%zu], timestamp[%ld]", size, timestamp); std::lock_guard<std::mutex> guard(mMutex); @@ -277,6 +278,12 @@ void IVideoSourceNode::OnUplinkEvent(uint8_t* data, uint32_t size, int64_t times } } +void IVideoSourceNode::SetBitrateThreshold(int32_t bitrate) +{ + IMLOGD1("[SetBitrateThreshold] bitrate[%d]", bitrate); + mMinBitrateThreshold = bitrate; +} + void IVideoSourceNode::OnEvent(int32_t type, int32_t param1, int32_t param2) { IMLOGD3("[OnEvent] type[%d], param1[%d], param2[%d]", type, param1, param2); @@ -298,7 +305,15 @@ void IVideoSourceNode::OnEvent(int32_t type, int32_t param1, int32_t param2) case kRequestVideoBitrateChange: if (mVideoSource != nullptr) { - mVideoSource->changeBitrate(param1); + if (mVideoSource->changeBitrate(param1)) + { + if (mMinBitrateThreshold != 0 && param1 <= mMinBitrateThreshold && + mCallback != nullptr && !mBitrateNotified) + { + mCallback->SendEvent(kImsMediaEventNotifyVideoLowestBitrate, param1); + mBitrateNotified = true; + } + } } break; case kRequestVideoIdrFrame: diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/nodes/VideoRtpPayloadEncoderNode.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/nodes/VideoRtpPayloadEncoderNode.cpp index 6ec6a3cd..e601c16c 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/nodes/VideoRtpPayloadEncoderNode.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/nodes/VideoRtpPayloadEncoderNode.cpp @@ -502,7 +502,7 @@ void VideoRtpPayloadEncoderNode::EncodeHevc( memset(mPPS, 0, MAX_CONFIG_LEN); memcpy(mPPS, pCurDataPos, nCurDataSize); mPpsSize = nCurDataSize; - IMLOGD1("[EncodeHevc] PPS Size [%d]", mSpsSize); + IMLOGD1("[EncodeHevc] PPS Size [%d]", mPpsSize); } if (nDataSize < nCurDataSize + 4) diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/protocol/rtp/core/RtcpPacket.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/protocol/rtp/core/RtcpPacket.cpp index b67fde10..2f11d8f1 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/protocol/rtp/core/RtcpPacket.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/protocol/rtp/core/RtcpPacket.cpp @@ -213,8 +213,8 @@ eRTP_STATUS_CODE RtcpPacket::decodeRtcpPacket(IN RtpBuffer* pobjRtcpPktBuf, usPktLen -= RTP_WORD_SIZE; if (usPktLen > iTrackCompLen) { - RTP_TRACE_ERROR( - "[DecodeRtcpPacket] RTCP packet length is Invalid.", usPktLen, iTrackCompLen); + RTP_TRACE_ERROR("[DecodeRtcpPacket] Report length is Invalid. ReportLen:%d, RtcpLen:%d", + usPktLen, iTrackCompLen); return RTP_INVALID_MSG; } @@ -265,6 +265,9 @@ eRTP_STATUS_CODE RtcpPacket::decodeRtcpPacket(IN RtpBuffer* pobjRtcpPktBuf, case RTCP_SDES: { RTP_TRACE_MESSAGE("[DecodeRtcpPacket] Decoding RTCP_SDES", 0, 0); + if (m_pobjSdesPkt != nullptr) + delete m_pobjSdesPkt; + m_pobjSdesPkt = new RtcpSdesPacket(); if (m_pobjSdesPkt == nullptr) { @@ -279,6 +282,9 @@ eRTP_STATUS_CODE RtcpPacket::decodeRtcpPacket(IN RtpBuffer* pobjRtcpPktBuf, case RTCP_BYE: { RTP_TRACE_MESSAGE("[DecodeRtcpPacket] Decoding RTCP_BYE", 0, 0); + if (m_pobjByePkt != nullptr) + delete m_pobjByePkt; + m_pobjByePkt = new RtcpByePacket(); if (m_pobjByePkt == nullptr) { @@ -293,6 +299,9 @@ eRTP_STATUS_CODE RtcpPacket::decodeRtcpPacket(IN RtpBuffer* pobjRtcpPktBuf, case RTCP_APP: { RTP_TRACE_MESSAGE("[DecodeRtcpPacket] Decoding RTCP_APP", 0, 0); + if (m_pobjAppPkt != nullptr) + delete m_pobjAppPkt; + m_pobjAppPkt = new RtcpAppPacket(); if (m_pobjAppPkt == nullptr) { @@ -321,11 +330,30 @@ eRTP_STATUS_CODE RtcpPacket::decodeRtcpPacket(IN RtpBuffer* pobjRtcpPktBuf, bFbPkt = eRTP_TRUE; break; } // RTCP_RTPFB || RTCP_PSFB + case RTCP_XR: + { + RTP_TRACE_MESSAGE("[DecodeRtcpPacket] Decoding RTCP_XR", 0, 0); + if (m_pobjRtcpXrPkt != nullptr) + delete m_pobjRtcpXrPkt; + + m_pobjRtcpXrPkt = new RtcpXrPacket(); + if (m_pobjRtcpXrPkt == nullptr) + { + RTP_TRACE_ERROR("[Memory Error] new returned NULL.", RTP_ZERO, RTP_ZERO); + return RTP_MEMORY_FAIL; + } + m_pobjRtcpXrPkt->setRtcpHdrInfo(m_objHeader); + eDecodeRes = m_pobjRtcpXrPkt->decodeRtcpXrPacket(pucBuffer, usPktLen, uiPktType); + bOtherPkt = eRTP_TRUE; + break; + } // RTCP_XR default: { - RTP_TRACE_WARNING( - "[DecodeRtcpPacket], Invalid RTCP MSG type received", RTP_ZERO, RTP_ZERO); - return RTP_INVALID_MSG; + RTP_TRACE_WARNING("[DecodeRtcpPacket], Invalid RTCP MSG type[%d] received", + uiPktType, RTP_ZERO); + // Instead of returning failure, ignore unknown report block and continue to decode + // next report block. + eDecodeRes = RTP_SUCCESS; } // default }; // switch diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/protocol/rtp/core/RtcpXrPacket.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/protocol/rtp/core/RtcpXrPacket.cpp index 819f13b6..406f04f0 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/protocol/rtp/core/RtcpXrPacket.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/protocol/rtp/core/RtcpXrPacket.cpp @@ -58,9 +58,11 @@ eRTP_STATUS_CODE RtcpXrPacket::decodeRtcpXrPacket( (RtpDt_Void) pucRtcpXrBuf; (RtpDt_Void) usRtcpXrLen; (RtpDt_Void) ucPktType; - RTP_TRACE_ERROR("decodeRtcpXrPacket not implemented.", RTP_ZERO, RTP_ZERO); + RTP_TRACE_WARNING("decodeRtcpXrPacket not implemented.", RTP_ZERO, RTP_ZERO); - return RTP_FAILURE; + /* TODO: Currently, there is no requirement to handle XR packets. Returning success to avoid + RTCP decoding issues. */ + return RTP_SUCCESS; } eRTP_STATUS_CODE RtcpXrPacket::formRtcpXrPacket(OUT RtpBuffer* pobjRtcpPktBuf) 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 f68076d5..f31d68b3 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 @@ -102,8 +102,8 @@ public class MainActivity extends AppCompatActivity { private static final int DTMF_SAMPLING_RATE_KHZ = 16; private static final int DTMF_DURATION = 140; private static final int IDR_INTERVAL = 1; - private static final int RESOLUTION_WIDTH = 640; - private static final int RESOLUTION_HEIGHT = 480; + private static final int RESOLUTION_WIDTH = 480; + private static final int RESOLUTION_HEIGHT = 640; private static final String IMAGE = "data/user_de/0/com.android.telephony.imsmedia/test.jpg"; private static final float DISABLED_ALPHA = 0.3f; private static final float ENABLED_ALPHA = 1.0f; @@ -119,6 +119,7 @@ public class MainActivity extends AppCompatActivity { private static final int[] PACKET_LOSS_RATE = { 1, 3 }; private static final int[] JITTER_THRESHOLD = { 100, 200 }; private static final boolean NOTIFY_STATUS = false; + private static final int VIDEO_BITRATE_THRESHOLD_BPS = 100000; private Set<Integer> mSelectedCodecTypes = new HashSet<>(); private Set<Integer> mSelectedAmrModes = new HashSet<>(); @@ -134,6 +135,7 @@ public class MainActivity extends AppCompatActivity { private int mSelectedCameraZoom = CAMERA_ZOOM; private int mSelectedDeviceOrientationDegree = 0; private int mSelectedCvoValue = -1; + private String mSelectedVideoResolution = "VGA_PR"; private Set<Integer> mSelectedRtcpFbTypes = new HashSet<>(); // The order of these values determines the priority in which they would be @@ -569,6 +571,34 @@ public class MainActivity extends AppCompatActivity { } } + public String[] mVideoResolutionStrings = new String[] { + "HD_PR", "HD_LS", "VGA_PR", "VGA_LS", "QVGA_PR", "QVGA_LS", "SIF_PR", "SIF_LS", "CIF_PR", + "CIF_LS", "QCIF_PR", "QCIF_LS", + }; + + public int[][] mVideoResolution = { + {720, 1280}, {1280, 720}, {480, 640}, {640, 480}, {240, 320}, {320, 240}, {240, 352}, + {352, 240}, {288, 352}, {352, 288}, {176, 144}, {144, 176}, + }; + + public int getResolutionWidth(String resolution) { + for (int i = 0; i < mVideoResolutionStrings.length; i++) { + if (mVideoResolutionStrings[i].equals(resolution)) { + return mVideoResolution[i][0]; + } + } + return RESOLUTION_WIDTH; + } + + public int getResolutionHeight(String resolution) { + for (int i = 0; i < mVideoResolutionStrings.length; i++) { + if (mVideoResolutionStrings[i].equals(resolution)) { + return mVideoResolution[i][1]; + } + } + return RESOLUTION_HEIGHT; + } + /** * Enum of the different states the application can be in. Mainly used to decide * how @@ -790,6 +820,28 @@ public class MainActivity extends AppCompatActivity { mVideoSession = (ImsVideoSession) session; Log.d(TAG, "onOpenSessionSuccess: id=" + mVideoSession.getSessionId()); mIsVideoSessionOpened = true; + + MediaQualityThreshold threshold = createMediaQualityThreshold(RTP_TIMEOUT, + RTCP_TIMEOUT, RTP_HYSTERESIS_TIME, RTP_PACKET_LOSS_DURATION, PACKET_LOSS_RATE, + JITTER_THRESHOLD, NOTIFY_STATUS); + mVideoSession.setMediaQualityThreshold(threshold); + + int rtcpfbTypes = 0; + for (int types : mSelectedRtcpFbTypes) { + rtcpfbTypes |= types; + } + + mVideoConfig = createVideoConfig(mSelectedVideoCodec, mSelectedVideoMode, + mSelectedFramerate, mSelectedBitrate, mSelectedCodecProfile, + mSelectedCodecLevel, mSelectedCameraId, mSelectedCameraZoom, + mSelectedDeviceOrientationDegree, + mSelectedCvoValue, rtcpfbTypes, + getResolutionWidth(mSelectedVideoResolution), + getResolutionHeight(mSelectedVideoResolution)); + + Log.d(TAG, "VideoConfig: " + mVideoConfig.toString()); + mVideoSession.modifySession(mVideoConfig); + runOnUiThread(() -> { if (mIsPreviewSurfaceSet) { mVideoSession.setPreviewSurface(mPreviewSurface); @@ -799,6 +851,22 @@ public class MainActivity extends AppCompatActivity { } }); } + + @Override + public void onModifySessionResponse(VideoConfig config, + final @ImsMediaSession.SessionOperationResult int result) { + Log.d(TAG, "onModifySessionResponse"); + } + + @Override + public void onPeerDimensionChanged(final int width, final int height) { + Log.d(TAG, "onPeerDimensionChanged - width=" + width + ", height=" + height); + } + + @Override + public void notifyBitrate(final int bitrate) { + Log.d(TAG, "notifyBitrate - bitrate=" + bitrate); + } } private class RtpTextSessionCallback extends TextSessionCallback { @@ -1425,7 +1493,7 @@ public class MainActivity extends AppCompatActivity { private VideoConfig createVideoConfig(InetSocketAddress remoteRtpAddress, RtcpConfig rtcpConfig, int codecType, int videoMode, int framerate, int bitrate, int profile, int level, int cameraId, int cameraZoom, int deviceOrientation, int cvo, - int rtcpFbTypes) { + int rtcpFbTypes, int width, int height) { VideoConfig config = new VideoConfig.Builder() .setMediaDirection(RtpConfig.MEDIA_DIRECTION_SEND_RECEIVE) .setAccessNetwork(AccessNetworkType.EUTRAN) @@ -1446,8 +1514,8 @@ public class MainActivity extends AppCompatActivity { .setPacketizationMode(VideoConfig.MODE_NON_INTERLEAVED) .setCameraId(cameraId) .setCameraZoom(cameraZoom) - .setResolutionWidth(RESOLUTION_WIDTH) - .setResolutionHeight(RESOLUTION_HEIGHT) + .setResolutionWidth(width) + .setResolutionHeight(height) .setPauseImagePath(IMAGE) .setDeviceOrientationDegree(deviceOrientation) .setCvoValue(cvo) @@ -1488,6 +1556,7 @@ public class MainActivity extends AppCompatActivity { .setRtpPacketLossRate(rtpPacketLossRate) .setRtpJitterMillis(rtpJitterMillis) .setNotifyCurrentStatus(notifyCurrentStatus) + .setVideoBitrateBps(VIDEO_BITRATE_THRESHOLD_BPS) .build(); } @@ -1641,7 +1710,7 @@ public class MainActivity extends AppCompatActivity { */ private VideoConfig createVideoConfig(int codecType, int videoMode, int framerate, int bitrate, int profile, int level, int cameraId, int cameraZoom, int deviceOrientation, int cvo, - int rtcpFbTypes) { + int rtcpFbTypes, int width, int height) { VideoConfig videoConfig = null; switch (codecType) { @@ -1649,7 +1718,8 @@ public class MainActivity extends AppCompatActivity { case VideoConfig.VIDEO_CODEC_HEVC: videoConfig = createVideoConfig(getRemoteVideoSocketAddress(), getRemoteVideoRtcpConfig(), codecType, videoMode, framerate, bitrate, - profile, level, cameraId, cameraZoom, deviceOrientation, cvo, rtcpFbTypes); + profile, level, cameraId, cameraZoom, deviceOrientation, cvo, rtcpFbTypes, + width, height); break; } @@ -1765,27 +1835,37 @@ public class MainActivity extends AppCompatActivity { PopupMenu mediaDirectionMenu = new PopupMenu(this, findViewById(R.id.mediaDirectionButton)); mediaDirectionMenu.getMenuInflater() .inflate(R.menu.media_direction_menu, mediaDirectionMenu.getMenu()); + int[] direction = { 0 }; mediaDirectionMenu.setOnMenuItemClickListener(item -> { switch (item.getItemId()) { case R.id.noFlowDirectionMenuItem: - mAudioConfig.setMediaDirection(AudioConfig.MEDIA_DIRECTION_NO_FLOW); + direction[0] = RtpConfig.MEDIA_DIRECTION_NO_FLOW; break; case R.id.sendReceiveDirectionMenuItem: - mAudioConfig.setMediaDirection(AudioConfig.MEDIA_DIRECTION_SEND_RECEIVE); + direction[0] = RtpConfig.MEDIA_DIRECTION_SEND_RECEIVE; break; case R.id.receiveOnlyDirectionMenuItem: - mAudioConfig.setMediaDirection(AudioConfig.MEDIA_DIRECTION_RECEIVE_ONLY); + direction[0] = RtpConfig.MEDIA_DIRECTION_RECEIVE_ONLY; break; case R.id.sendOnlyDirectionMenuItem: - mAudioConfig.setMediaDirection(AudioConfig.MEDIA_DIRECTION_SEND_ONLY); + direction[0] = RtpConfig.MEDIA_DIRECTION_SEND_ONLY; break; case R.id.inactiveDirectionMenuItem: - mAudioConfig.setMediaDirection(AudioConfig.MEDIA_DIRECTION_INACTIVE); + direction[0] = RtpConfig.MEDIA_DIRECTION_INACTIVE; break; default: return false; } + mAudioConfig.setMediaDirection(direction[0]); mAudioSession.modifySession(mAudioConfig); + if (mIsVideoSessionOpened) { + mVideoConfig.setMediaDirection(direction[0]); + mVideoSession.modifySession(mVideoConfig); + } + if (mIsTextSessionOpened) { + mTextConfig.setMediaDirection(direction[0]); + mTextSession.modifySession(mTextConfig); + } return true; }); mediaDirectionMenu.show(); @@ -1817,11 +1897,6 @@ public class MainActivity extends AppCompatActivity { mAudioConfig = determineAudioConfig(mLocalDeviceInfo, mRemoteDeviceInfo); Log.d(TAG, "AudioConfig: " + mAudioConfig.toString()); - int rtcpfbTypes = 0; - for (int types : mSelectedRtcpFbTypes) { - rtcpfbTypes |= types; - } - RtpAudioSessionCallback sessionAudioCallback = new RtpAudioSessionCallback(); mImsMediaManager.openSession(mAudioRtp, mAudioRtcp, ImsMediaSession.SESSION_TYPE_AUDIO, @@ -1830,17 +1905,10 @@ public class MainActivity extends AppCompatActivity { + mRemoteDeviceInfo.getAudioRtpPort()); if (mVideoEnabled) { - mVideoConfig = createVideoConfig(mSelectedVideoCodec, mSelectedVideoMode, - mSelectedFramerate, mSelectedBitrate, mSelectedCodecProfile, - mSelectedCodecLevel, mSelectedCameraId, mSelectedCameraZoom, - mSelectedDeviceOrientationDegree, - mSelectedCvoValue, rtcpfbTypes); - Log.d(TAG, "VideoConfig: " + mVideoConfig.toString()); - RtpVideoSessionCallback sessionVideoCallback = new RtpVideoSessionCallback(); mImsMediaManager.openSession(mVideoRtp, mVideoRtcp, ImsMediaSession.SESSION_TYPE_VIDEO, - mVideoConfig, mExecutor, sessionVideoCallback); + null, mExecutor, sessionVideoCallback); Log.d(TAG, "openSession(): video=" + mRemoteDeviceInfo.getInetAddress() + ":" + mRemoteDeviceInfo.getVideoRtpPort()); } @@ -1896,6 +1964,7 @@ public class MainActivity extends AppCompatActivity { Spinner videoBitrateSpinner = findViewById(R.id.spinnerVideoBitrates); Spinner videoDeviceOrientationSpinner = findViewById(R.id.spinnerVideoDeviceOrientations); Spinner videoCvoValueSpinner = findViewById(R.id.spinnerVideoCvoValues); + Spinner videoResolutionSpinner = (Spinner) findViewById(R.id.spinnerVideoResolution); mSelectedVideoCodec = ((VideoCodecEnum) videoCodecSpinner.getSelectedItem()).getValue(); @@ -1919,6 +1988,7 @@ public class MainActivity extends AppCompatActivity { .getValue(); mSelectedCvoValue = ((VideoCvoValueEnum) videoCvoValueSpinner.getSelectedItem()) .getValue(); + mSelectedVideoResolution = (String) videoResolutionSpinner.getSelectedItem(); Toast.makeText(getApplicationContext(), R.string.save_button_action_toast, Toast.LENGTH_SHORT).show(); } @@ -2074,7 +2144,7 @@ public class MainActivity extends AppCompatActivity { setupCodecSelectionOnClickListeners(); } - private int getSpinnerIndex(Spinner spinner, int value) { + private int getSpinnerIndex(Spinner spinner, Object value) { int index = 0; for (int i = 0; i < spinner.getCount(); i++) { if (spinner.getItemAtPosition(i).equals(value)) { @@ -2097,8 +2167,7 @@ public class MainActivity extends AppCompatActivity { } /** - * Gets the saved user selections for the audio codec settings and updates the - * UI's lists to + * Gets the saved user selections for the audio codec settings and updates the UI's lists to * match. */ private void setupAudioCodecSelectionLists() { @@ -2226,6 +2295,15 @@ public class MainActivity extends AppCompatActivity { videoCvoValueSpinner.setAdapter(videoCvoValueAdaptor); videoCvoValueSpinner.setSelection(getSpinnerIndex(videoCvoValueSpinner, mSelectedCvoValue)); + + Spinner videoResolutionSpinner = (Spinner) findViewById(R.id.spinnerVideoResolution); + ArrayAdapter<String> videoResolutionAdapter = new ArrayAdapter<String>(this, + android.R.layout.simple_spinner_item, mVideoResolutionStrings); + videoResolutionAdapter.setDropDownViewResource( + android.R.layout.simple_spinner_dropdown_item); + videoResolutionSpinner.setAdapter(videoResolutionAdapter); + videoResolutionSpinner.setSelection(getSpinnerIndex(videoResolutionSpinner, + mSelectedVideoResolution)); } /** @@ -2621,8 +2699,8 @@ public class MainActivity extends AppCompatActivity { .setFramerate(10) .setIntraFrameIntervalSec(1) .setPacketizationMode(VideoConfig.MODE_NON_INTERLEAVED) - .setResolutionWidth(480) - .setResolutionHeight(640) + .setResolutionWidth(RESOLUTION_WIDTH) + .setResolutionHeight(RESOLUTION_HEIGHT) .setVideoMode(VideoConfig.VIDEO_MODE_RECORDING) .setMaxMtuBytes(1500); diff --git a/test/app/ImsMediaTestingApp/app/src/main/res/layout/settings_video.xml b/test/app/ImsMediaTestingApp/app/src/main/res/layout/settings_video.xml index 89f103ae..ec502bec 100644 --- a/test/app/ImsMediaTestingApp/app/src/main/res/layout/settings_video.xml +++ b/test/app/ImsMediaTestingApp/app/src/main/res/layout/settings_video.xml @@ -254,6 +254,26 @@ android:layout_toEndOf="@id/videoCvoValueTitle" android:layout_below="@id/videoPauseImagePathTitle" /> + <TextView + android:id="@+id/videoResolutionTitle" + android:textStyle="bold" + android:layout_width="150dp" + android:layout_height="wrap_content" + android:layout_marginTop="30dp" + android:layout_below="@id/videoCvoValueTitle" + android:layout_alignStart="@id/videoCodecTitle" + android:text="@string/video_resolution" + android:textAlignment="textStart" /> + + <Spinner + android:id="@+id/spinnerVideoResolution" + android:layout_width="200dp" + android:layout_height="wrap_content" + android:layout_marginTop="30dp" + android:layout_marginStart="15dp" + android:layout_toEndOf="@id/videoResolutionTitle" + android:layout_below="@id/videoCvoValueTitle" /> + <Button android:id="@+id/saveVideoSettingsButton" android:layout_width="wrap_content" 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 37d6b5a7..7dd2cf86 100644 --- a/test/app/ImsMediaTestingApp/app/src/main/res/values/strings.xml +++ b/test/app/ImsMediaTestingApp/app/src/main/res/values/strings.xml @@ -64,6 +64,7 @@ <string name="video_device_orientation">Device Orientation</string> <string name="video_pause_image_path">Pause Image Path</string> <string name="video_cvo_value">CVO Value</string> + <string name="video_resolution">Resolution</string> <!-- Save button text --> <string name="save">SAVE</string> diff --git a/tests/native/Android.bp b/tests/native/Android.bp index 5000dee4..4171f753 100644 --- a/tests/native/Android.bp +++ b/tests/native/Android.bp @@ -22,6 +22,7 @@ cc_library_headers { name: "libimsmedia_tests_headers", export_include_dirs: [ "service/src/com/android/telephony/imsmedia/lib/libimsmedia/include", + "service/src/com/android/telephony/imsmedia/lib/libimsmedia/include/core/audio", ], } diff --git a/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/config/MediaQualityThresholdTest.cpp b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/config/MediaQualityThresholdTest.cpp index 1ab1ff01..33494468 100644 --- a/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/config/MediaQualityThresholdTest.cpp +++ b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/config/MediaQualityThresholdTest.cpp @@ -26,6 +26,7 @@ const int32_t kRtpPacketLossDurationMillis = 5000; const std::vector<int32_t> kRtpPacketLossRate = {3, 5}; const std::vector<int32_t> kRtpJitterMillis = {100, 200}; const bool kNotifyCurrentStatus = false; +const int32_t kVideoBitrateBps = 100000; class MediaQualityThresholdTest : public ::testing::Test { @@ -42,6 +43,7 @@ protected: threshold.setRtpPacketLossRate(kRtpPacketLossRate); threshold.setRtpJitterMillis(kRtpJitterMillis); threshold.setNotifyCurrentStatus(kNotifyCurrentStatus); + threshold.setVideoBitrateBps(kVideoBitrateBps); } virtual void TearDown() override {} @@ -56,6 +58,7 @@ TEST_F(MediaQualityThresholdTest, TestGetterSetter) EXPECT_EQ(threshold.getRtpPacketLossRate(), kRtpPacketLossRate); EXPECT_EQ(threshold.getRtpJitterMillis(), kRtpJitterMillis); EXPECT_EQ(threshold.getNotifyCurrentStatus(), kNotifyCurrentStatus); + EXPECT_EQ(threshold.getVideoBitrateBps(), kVideoBitrateBps); } TEST_F(MediaQualityThresholdTest, TestParcel) @@ -85,6 +88,7 @@ TEST_F(MediaQualityThresholdTest, TestEqual) threshold2.setRtpPacketLossRate(kRtpPacketLossRate); threshold2.setRtpJitterMillis(kRtpJitterMillis); threshold2.setNotifyCurrentStatus(kNotifyCurrentStatus); + threshold2.setVideoBitrateBps(kVideoBitrateBps); EXPECT_EQ(threshold, threshold2); } @@ -98,6 +102,7 @@ TEST_F(MediaQualityThresholdTest, TestNotEqual) threshold2.setRtpPacketLossRate(kRtpPacketLossRate); threshold2.setRtpJitterMillis(kRtpJitterMillis); threshold2.setNotifyCurrentStatus(kNotifyCurrentStatus); + threshold2.setVideoBitrateBps(kVideoBitrateBps); MediaQualityThreshold threshold3; threshold3.setRtpInactivityTimerMillis(kRtpInactivityTimerMillis); @@ -107,6 +112,7 @@ TEST_F(MediaQualityThresholdTest, TestNotEqual) threshold3.setRtpPacketLossRate(std::vector<int32_t>{5, 10}); threshold3.setRtpJitterMillis(kRtpJitterMillis); threshold3.setNotifyCurrentStatus(kNotifyCurrentStatus); + threshold3.setVideoBitrateBps(kVideoBitrateBps); EXPECT_NE(threshold, threshold2); EXPECT_NE(threshold, threshold3); diff --git a/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioManagerTest.cpp b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioManagerTest.cpp new file mode 100644 index 00000000..9f8cc4c5 --- /dev/null +++ b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioManagerTest.cpp @@ -0,0 +1,683 @@ +/** + * 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 <gtest/gtest.h> +#include <ImsMediaNetworkUtil.h> +#include <AudioConfig.h> +#include <MockAudioManager.h> +#include <ImsMediaCondition.h> +#include <unordered_map> +#include <algorithm> + +using namespace android::telephony::imsmedia; + +using ::testing::_; +using ::testing::Eq; +using ::testing::Pointee; +using ::testing::Ref; +using ::testing::Return; + +// RtpConfig +const int32_t kMediaDirection = RtpConfig::MEDIA_DIRECTION_SEND_RECEIVE; +const android::String8 kRemoteAddress("127.0.0.1"); +const int32_t kRemotePort = 10000; +const int8_t kDscp = 0; +const int8_t kRxPayload = 96; +const int8_t kTxPayload = 96; +const int8_t kSamplingRate = 16; + +// RtcpConfig +const android::String8 kCanonicalName("name"); +const int32_t kTransmitPort = 1001; +const int32_t kIntervalSec = 5; +const int32_t kRtcpXrBlockTypes = RtcpConfig::FLAG_RTCPXR_STATISTICS_SUMMARY_REPORT_BLOCK | + RtcpConfig::FLAG_RTCPXR_VOIP_METRICS_REPORT_BLOCK; + +// AudioConfig +const int8_t kPTimeMillis = 20; +const int32_t kMaxPtimeMillis = 100; +const bool kDtxEnabled = true; +const int32_t kCodecType = AudioConfig::CODEC_AMR_WB; +const int8_t kDtmfTxPayloadTypeNumber = 100; +const int8_t kDtmfRxPayloadTypeNumber = 101; +const int8_t kDtmfsamplingRateKHz = 16; + +// AmrParam +const int32_t kAmrMode = 8; +const bool kOctetAligned = false; +const int32_t kMaxRedundancyMillis = 240; + +// EvsParam +const int32_t kEvsBandwidth = EvsParams::EVS_BAND_NONE; +const int32_t kEvsMode = 8; +const int8_t kChannelAwareMode = 3; +const bool kUseHeaderFullOnly = false; +const int8_t kcodecModeRequest = 15; + +int32_t kSessionId = 0; + +static ImsMediaCondition gCondition; + +class AudioManagerCallback +{ +public: + int32_t resSessionId; + int32_t response; + AudioConfig resConfig; + ImsMediaResult result; + std::list<RtpHeaderExtension> extensions; + MediaQualityStatus mediaQualityStatus; + char receivedDtmfDigit; + int32_t receivedDtmfDuration; + CallQuality callQuality; + + void resetRespond() + { + resSessionId = -1; + response = -1; + result = RESULT_NOT_READY; + } + + void onCallback(const int id, const int event, const ImsMediaResult res) + { + resSessionId = id; + response = event; + result = res; + } + + void onCallbackConfig( + const int id, const int event, const ImsMediaResult res, const AudioConfig& config) + { + resSessionId = id; + response = event; + resConfig = config; + result = res; + } + + void onCallbackHeaderExtension( + const int id, const int event, const std::list<RtpHeaderExtension>& list) + { + extensions.clear(); + resSessionId = id; + response = event; + std::copy(list.begin(), list.end(), std::back_inserter(extensions)); + } + + void onCallbackMediaQualityStatus( + const int id, const int event, const MediaQualityStatus& status) + { + resSessionId = id; + response = event; + mediaQualityStatus = status; + } + + void onCallbackDtmfReceived(const int id, const int event, char digit, int32_t duration) + { + resSessionId = id; + response = event; + receivedDtmfDigit = digit; + receivedDtmfDuration = duration; + } + + void onCallbackCallQuality(const int id, const int event, const CallQuality& status) + { + resSessionId = id; + response = event; + callQuality = status; + } +}; + +static std::unordered_map<int, AudioManagerCallback*> gMapCallback; + +class AudioManagerTest : public ::testing::Test +{ +public: + MockAudioManager manager; + AudioConfig config; + RtcpConfig rtcp; + AmrParams amr; + EvsParams evs; + int socketRtpFd; + int socketRtcpFd; + AudioManagerCallback callback; + + AudioManagerTest() + { + socketRtpFd = -1; + socketRtcpFd = -1; + callback.resetRespond(); + gCondition.reset(); + } + ~AudioManagerTest() {} + +protected: + virtual void SetUp() override + { + rtcp.setCanonicalName(kCanonicalName); + rtcp.setTransmitPort(kTransmitPort); + rtcp.setIntervalSec(kIntervalSec); + rtcp.setRtcpXrBlockTypes(kRtcpXrBlockTypes); + + amr.setAmrMode(kAmrMode); + amr.setOctetAligned(kOctetAligned); + amr.setMaxRedundancyMillis(kMaxRedundancyMillis); + + evs.setEvsBandwidth(kEvsBandwidth); + evs.setEvsMode(kEvsMode); + evs.setChannelAwareMode(kChannelAwareMode); + evs.setUseHeaderFullOnly(kUseHeaderFullOnly); + evs.setCodecModeRequest(kcodecModeRequest); + + config.setMediaDirection(kMediaDirection); + config.setRemoteAddress(kRemoteAddress); + config.setRemotePort(kRemotePort); + config.setRtcpConfig(rtcp); + config.setDscp(kDscp); + config.setRxPayloadTypeNumber(kRxPayload); + config.setTxPayloadTypeNumber(kTxPayload); + config.setSamplingRateKHz(kSamplingRate); + config.setPtimeMillis(kPTimeMillis); + config.setMaxPtimeMillis(kMaxPtimeMillis); + config.setDtxEnabled(kDtxEnabled); + config.setCodecType(kCodecType); + config.setTxDtmfPayloadTypeNumber(kDtmfTxPayloadTypeNumber); + config.setRxDtmfPayloadTypeNumber(kDtmfRxPayloadTypeNumber); + config.setDtmfsamplingRateKHz(kDtmfsamplingRateKHz); + config.setAmrParams(amr); + config.setEvsParams(evs); + + manager.setCallback(&audioCallback); + gMapCallback.insert(std::make_pair(kSessionId, &callback)); + const char testIp[] = "127.0.0.1"; + unsigned int testPortRtp = 30000; + socketRtpFd = ImsMediaNetworkUtil::openSocket(testIp, testPortRtp, AF_INET); + EXPECT_NE(socketRtpFd, -1); + unsigned int testPortRtcp = 30001; + socketRtcpFd = ImsMediaNetworkUtil::openSocket(testIp, testPortRtcp, AF_INET); + EXPECT_NE(socketRtcpFd, -1); + gCondition.reset(); + } + + virtual void TearDown() override + { + if (socketRtpFd != -1) + { + ImsMediaNetworkUtil::closeSocket(socketRtpFd); + } + + if (socketRtcpFd != -1) + { + ImsMediaNetworkUtil::closeSocket(socketRtcpFd); + } + + gMapCallback.erase(kSessionId); + } + + void openSession(const int32_t sessionId) + { + callback.resetRespond(); + android::Parcel parcel; + parcel.writeInt32(kAudioOpenSession); + parcel.writeInt32(socketRtpFd); + parcel.writeInt32(socketRtcpFd); + parcel.setDataPosition(0); + gCondition.reset(); + manager.sendMessage(sessionId, parcel); + EXPECT_TRUE(!gCondition.wait_timeout(1000)); + EXPECT_EQ(callback.resSessionId, sessionId); + EXPECT_EQ(callback.response, kAudioOpenSessionSuccess); + } + + void closeSession(const int32_t sessionId) + { + callback.resetRespond(); + android::Parcel parcel; + parcel.writeInt32(kAudioCloseSession); + parcel.setDataPosition(0); + gCondition.reset(); + manager.sendMessage(sessionId, parcel); + EXPECT_TRUE(!gCondition.wait_timeout(1000)); + EXPECT_EQ(callback.resSessionId, sessionId); + EXPECT_EQ(callback.response, kAudioSessionClosed); + } + + void testEventResponse(const int32_t sessionId, const int32_t event, AudioConfig* config, + const int32_t response, const int32_t result) + { + callback.resetRespond(); + android::Parcel parcel; + parcel.writeInt32(event); + + if (config != nullptr) + { + config->writeToParcel(&parcel); + } + + parcel.setDataPosition(0); + gCondition.reset(); + manager.sendMessage(sessionId, parcel); + EXPECT_TRUE(!gCondition.wait_timeout(1000)); + EXPECT_EQ(callback.resSessionId, sessionId); + EXPECT_EQ(callback.response, response); + + if (callback.response >= kAudioOpenSessionFailure && + callback.response <= kAudioConfirmConfigResponse) + { + EXPECT_EQ(result, result); + + if (config != nullptr && callback.response >= kAudioModifySessionResponse && + callback.response <= kAudioConfirmConfigResponse) + { + EXPECT_EQ(callback.resConfig, *config); + } + } + } + + static int32_t audioCallback(int sessionId, const android::Parcel& parcel) + { + parcel.setDataPosition(0); + + int response = parcel.readInt32(); + ImsMediaResult result = RESULT_INVALID_PARAM; + + auto callback = gMapCallback.find(sessionId); + + if (callback != gMapCallback.end()) + { + if (response >= kAudioOpenSessionFailure && response <= kAudioConfirmConfigResponse) + { + result = static_cast<ImsMediaResult>(parcel.readInt32()); + } + + switch (response) + { + case kAudioModifySessionResponse: + case kAudioAddConfigResponse: + case kAudioConfirmConfigResponse: + { + AudioConfig resConfig; + resConfig.readFromParcel(&parcel); + (callback->second)->onCallbackConfig(sessionId, response, result, resConfig); + } + break; + case kAudioFirstMediaPacketInd: + { + AudioConfig resConfig; + resConfig.readFromParcel(&parcel); + (callback->second) + ->onCallbackConfig(sessionId, response, RESULT_SUCCESS, resConfig); + } + break; + case kAudioRtpHeaderExtensionInd: + { + std::list<RtpHeaderExtension> listExtension; + int32_t listSize = parcel.readInt32(); + + for (int32_t i = 0; i < listSize; i++) + { + RtpHeaderExtension extension; + extension.readFromParcel(&parcel); + listExtension.push_back(extension); + } + + (callback->second) + ->onCallbackHeaderExtension(sessionId, response, listExtension); + } + break; + case kAudioMediaQualityStatusInd: + { + MediaQualityStatus status; + status.readFromParcel(&parcel); + (callback->second)->onCallbackMediaQualityStatus(sessionId, response, status); + } + break; + case kAudioDtmfReceivedInd: + (callback->second) + ->onCallbackDtmfReceived( + sessionId, response, parcel.readByte(), parcel.readInt32()); + break; + case kAudioCallQualityChangedInd: + { + CallQuality quality; + quality.readFromParcel(&parcel); + (callback->second)->onCallbackCallQuality(sessionId, response, quality); + } + break; + default: + (callback->second)->onCallback(sessionId, response, result); + break; + } + } + + if (response != kAudioCallQualityChangedInd) + { + gCondition.signal(); + } + + return 0; + } +}; + +TEST_F(AudioManagerTest, testOpenCloseSession) +{ + EXPECT_EQ(manager.getState(kSessionId), kSessionStateClosed); + openSession(kSessionId); + closeSession(kSessionId); +} + +TEST_F(AudioManagerTest, testModifySession) +{ + testEventResponse(kSessionId, kAudioModifySession, nullptr, kAudioModifySessionResponse, + RESULT_INVALID_PARAM); + + openSession(kSessionId); + + testEventResponse(kSessionId, kAudioModifySession, nullptr, kAudioModifySessionResponse, + RESULT_INVALID_PARAM); + + testEventResponse( + kSessionId, kAudioModifySession, &config, kAudioModifySessionResponse, RESULT_SUCCESS); + + closeSession(kSessionId); +} + +TEST_F(AudioManagerTest, testAddConfig) +{ + testEventResponse( + kSessionId, kAudioAddConfig, nullptr, kAudioAddConfigResponse, RESULT_INVALID_PARAM); + + openSession(kSessionId); + + testEventResponse( + kSessionId, kAudioAddConfig, nullptr, kAudioAddConfigResponse, RESULT_INVALID_PARAM); + + testEventResponse( + kSessionId, kAudioAddConfig, &config, kAudioAddConfigResponse, RESULT_SUCCESS); + + closeSession(kSessionId); +} + +TEST_F(AudioManagerTest, testConfirmConfig) +{ + testEventResponse(kSessionId, kAudioConfirmConfig, nullptr, kAudioConfirmConfigResponse, + RESULT_INVALID_PARAM); + + openSession(kSessionId); + + testEventResponse(kSessionId, kAudioConfirmConfig, nullptr, kAudioConfirmConfigResponse, + RESULT_INVALID_PARAM); + + testEventResponse( + kSessionId, kAudioConfirmConfig, &config, kAudioConfirmConfigResponse, RESULT_SUCCESS); + + closeSession(kSessionId); +} + +TEST_F(AudioManagerTest, testDeleteConfig) +{ + openSession(kSessionId); + + android::Parcel parcel; + parcel.writeInt32(kAudioDeleteConfig); + + if (config != nullptr) + { + config.writeToParcel(&parcel); + } + + EXPECT_CALL(manager, deleteConfig(kSessionId, Pointee(Eq(config)))) + .Times(1) + .WillOnce(Return(RESULT_INVALID_PARAM)); + + parcel.setDataPosition(0); + manager.sendMessage(kSessionId, parcel); + + closeSession(kSessionId); +} + +TEST_F(AudioManagerTest, testSendDtmf) +{ + openSession(kSessionId); + + const char kDigit = '1'; + const int32_t kDuration = 100; + + android::Parcel parcel; + parcel.writeInt32(kAudioSendDtmf); + parcel.writeByte(kDigit); + parcel.writeInt32(kDuration); + parcel.setDataPosition(0); + + EXPECT_CALL(manager, sendDtmf(kSessionId, kDigit, kDuration)).Times(1).WillOnce(Return()); + + manager.sendMessage(kSessionId, parcel); + + closeSession(kSessionId); +} + +TEST_F(AudioManagerTest, testSendHeaderExtension) +{ + openSession(kSessionId); + + std::list<RtpHeaderExtension> extensions; + RtpHeaderExtension extension; + const uint8_t kExtensionData[] = {0x01, 0x02}; + const int32_t kExtensionDataSize = 2; + extension.setLocalIdentifier(15); + extension.setExtensionData(kExtensionData, kExtensionDataSize); + extensions.push_back(extension); + + android::Parcel parcel; + parcel.writeInt32(kAudioSendRtpHeaderExtension); + parcel.writeInt32(extensions.size()); + + for (auto& item : extensions) + { + item.writeToParcel(&parcel); + } + + parcel.setDataPosition(0); + + EXPECT_CALL(manager, sendRtpHeaderExtension(kSessionId, Pointee(Eq(extensions)))) + .Times(1) + .WillOnce(Return()); + + manager.sendMessage(kSessionId, parcel); + + closeSession(kSessionId); +} + +TEST_F(AudioManagerTest, testSetMediaQualityThreshold) +{ + openSession(kSessionId); + + const std::vector<int32_t> kRtpInactivityTimerMillis = {10000, 20000}; + const int32_t kRtcpInactivityTimerMillis = 20000; + const int32_t kRtpHysteresisTimeInMillis = 3000; + const int32_t kRtpPacketLossDurationMillis = 5000; + const std::vector<int32_t> kRtpPacketLossRate = {3, 5}; + const std::vector<int32_t> kRtpJitterMillis = {100, 200}; + const bool kNotifyCurrentStatus = false; + + MediaQualityThreshold threshold; + threshold.setRtpInactivityTimerMillis(kRtpInactivityTimerMillis); + threshold.setRtcpInactivityTimerMillis(kRtcpInactivityTimerMillis); + threshold.setRtpHysteresisTimeInMillis(kRtpHysteresisTimeInMillis); + threshold.setRtpPacketLossDurationMillis(kRtpPacketLossDurationMillis); + threshold.setRtpPacketLossRate(kRtpPacketLossRate); + threshold.setRtpJitterMillis(kRtpJitterMillis); + threshold.setNotifyCurrentStatus(kNotifyCurrentStatus); + + android::Parcel parcel; + parcel.writeInt32(kAudioSetMediaQualityThreshold); + threshold.writeToParcel(&parcel); + parcel.setDataPosition(0); + + EXPECT_CALL(manager, setMediaQualityThreshold(kSessionId, Pointee(Eq(threshold)))) + .Times(1) + .WillOnce(Return()); + + manager.sendMessage(kSessionId, parcel); + + closeSession(kSessionId); +} + +TEST_F(AudioManagerTest, testSendInternalEventCmr) +{ + openSession(kSessionId); + + const int32_t kCmrCode = 1; + const int32_t kCmrDefine = 7; + + EXPECT_CALL(manager, SendInternalEvent(kRequestAudioCmr, kSessionId, kCmrCode, kCmrDefine)) + .Times(1) + .WillOnce(Return()); + + ImsMediaEventHandler::SendEvent( + "AUDIO_REQUEST_EVENT", kRequestAudioCmr, kSessionId, kCmrCode, kCmrDefine); + + closeSession(kSessionId); +} + +TEST_F(AudioManagerTest, testSendInternalEventRtcpXr) +{ + openSession(kSessionId); + + const int32_t param1 = 10; + const int32_t param2 = 20; + + EXPECT_CALL(manager, SendInternalEvent(kRequestSendRtcpXrReport, kSessionId, param1, param2)) + .Times(1) + .WillOnce(Return()); + + ImsMediaEventHandler::SendEvent( + "AUDIO_REQUEST_EVENT", kRequestSendRtcpXrReport, kSessionId, param1, param2); + + closeSession(kSessionId); +} + +TEST_F(AudioManagerTest, testFirstMediaPacketInd) +{ + AudioConfig* param = new AudioConfig(config); + + ImsMediaEventHandler::SendEvent("AUDIO_RESPONSE_EVENT", kAudioFirstMediaPacketInd, kSessionId, + reinterpret_cast<uint64_t>(param), 0); + + gCondition.wait_timeout(20); + EXPECT_EQ(callback.resSessionId, kSessionId); + EXPECT_EQ(callback.response, kAudioFirstMediaPacketInd); + EXPECT_EQ(callback.resConfig, config); +} + +TEST_F(AudioManagerTest, testRtpHeaderExtensionInd) +{ + std::list<RtpHeaderExtension> extensions; + RtpHeaderExtension extension; + const uint8_t kExtensionData[] = {0x01, 0x02}; + const int32_t kExtensionDataSize = 2; + extension.setLocalIdentifier(15); + extension.setExtensionData(kExtensionData, kExtensionDataSize); + extensions.push_back(extension); + + std::list<RtpHeaderExtension>* param = new std::list<RtpHeaderExtension>(); + std::copy(extensions.begin(), extensions.end(), std::back_inserter(*param)); + + android::Parcel parcel; + parcel.writeInt32(param->size()); + + for (auto& item : *param) + { + item.writeToParcel(&parcel); + } + + ImsMediaEventHandler::SendEvent("AUDIO_RESPONSE_EVENT", kAudioRtpHeaderExtensionInd, kSessionId, + reinterpret_cast<uint64_t>(param), 0); + + gCondition.wait_timeout(20); + EXPECT_EQ(callback.resSessionId, kSessionId); + EXPECT_EQ(callback.response, kAudioRtpHeaderExtensionInd); + EXPECT_EQ(callback.extensions, extensions); +} + +TEST_F(AudioManagerTest, testMediaQualityStatusInd) +{ + MediaQualityStatus status; + status.setRtpInactivityTimeMillis(10000); + status.setRtcpInactivityTimeMillis(10000); + status.setRtpPacketLossRate(1); + status.setRtpJitterMillis(100); + + MediaQualityStatus* param = new MediaQualityStatus(status); + + ImsMediaEventHandler::SendEvent("AUDIO_RESPONSE_EVENT", kAudioMediaQualityStatusInd, kSessionId, + reinterpret_cast<uint64_t>(param), 0); + + gCondition.wait_timeout(20); + EXPECT_EQ(callback.resSessionId, kSessionId); + EXPECT_EQ(callback.response, kAudioMediaQualityStatusInd); + EXPECT_EQ(callback.mediaQualityStatus, status); +} + +TEST_F(AudioManagerTest, testDtmfReceivedInd) +{ + const char digit = 1; + const int32_t duration = 100; + + ImsMediaEventHandler::SendEvent( + "AUDIO_RESPONSE_EVENT", kAudioDtmfReceivedInd, kSessionId, digit, duration); + + gCondition.wait_timeout(20); + EXPECT_EQ(callback.resSessionId, kSessionId); + EXPECT_EQ(callback.response, kAudioDtmfReceivedInd); + EXPECT_EQ(callback.receivedDtmfDigit, digit); + EXPECT_EQ(callback.receivedDtmfDuration, duration); +} + +TEST_F(AudioManagerTest, testCallQualityInd) +{ + CallQuality quality; + quality.setDownlinkCallQualityLevel(0); + quality.setUplinkCallQualityLevel(0); + quality.setCallDuration(30000); + quality.setNumRtpPacketsTransmitted(1500); + quality.setNumRtpPacketsReceived(1500); + quality.setNumRtpPacketsTransmittedLost(1); + quality.setNumRtpPacketsNotReceived(2); + quality.setAverageRelativeJitter(50); + quality.setMaxRelativeJitter(150); + quality.setAverageRoundTripTime(60); + quality.setCodecType(AudioConfig::CODEC_AMR_WB); + quality.setRtpInactivityDetected(false); + quality.setRxSilenceDetected(false); + quality.setTxSilenceDetected(false); + quality.setNumVoiceFrames(1400); + quality.setNumNoDataFrames(0); + quality.setNumDroppedRtpPackets(0); + quality.setMinPlayoutDelayMillis(100); + quality.setMaxPlayoutDelayMillis(180); + quality.setNumRtpSidPacketsReceived(100); + quality.setNumRtpDuplicatePackets(1); + + CallQuality* param = new CallQuality(quality); + + ImsMediaEventHandler::SendEvent("AUDIO_RESPONSE_EVENT", kAudioCallQualityChangedInd, kSessionId, + reinterpret_cast<uint64_t>(param), 0); + + gCondition.wait_timeout(20); + EXPECT_EQ(callback.resSessionId, kSessionId); + EXPECT_EQ(callback.response, kAudioCallQualityChangedInd); + EXPECT_EQ(callback.callQuality, quality); +}
\ No newline at end of file diff --git a/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/MediaQualityAnalyzerTest.cpp b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/MediaQualityAnalyzerTest.cpp index a94b6654..ba468406 100644 --- a/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/MediaQualityAnalyzerTest.cpp +++ b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/MediaQualityAnalyzerTest.cpp @@ -19,7 +19,6 @@ #include <AudioConfig.h> #include <ImsMediaAudioUtil.h> #include <MediaQualityAnalyzer.h> -#include <ImsMediaCondition.h> #include <MockBaseSessionCallback.h> #include <ImsMediaTimer.h> @@ -27,7 +26,7 @@ using namespace android::telephony::imsmedia; using ::testing::_; // RtpConfig -const int32_t kMediaDirection = RtpConfig::MEDIA_DIRECTION_INACTIVE; +const int32_t kMediaDirection = RtpConfig::MEDIA_DIRECTION_SEND_RECEIVE; const android::String8 kRemoteAddress("127.0.0.1"); const int32_t kRemotePort = 10000; const int8_t kDscp = 0; @@ -100,13 +99,7 @@ public: } } - virtual void onEvent(int32_t type, uint64_t param1, uint64_t param2) - { - (void)type; - (void)param1; - (void)param2; - } - + virtual void onEvent(int32_t /* type */, uint64_t /* param1 */, uint64_t /* param2 */) {} CallQuality getCallQuality() { return mCallQuality; } MediaQualityStatus getMediaQualityStatus() { return mStatus; } @@ -115,6 +108,49 @@ private: MediaQualityStatus mStatus; }; +class FakeMediaQualityAnalyzer : public MediaQualityAnalyzer +{ +public: + FakeMediaQualityAnalyzer() : + MediaQualityAnalyzer() + { + counter = 0; + } + virtual ~FakeMediaQualityAnalyzer() {} + + virtual void start() + { + mCallQuality.setCodecType(convertAudioCodecType( + mCodecType, ImsMediaAudioUtil::FindMaxEvsBandwidthFromRange(mCodecAttribute))); + } + + virtual void stop() + { + notifyCallQuality(); + reset(); + counter = 0; + } + + void testProcessCycle(const int32_t numCycle) + { + for (int i = 0; i < numCycle; i++) + { + while (!mListevent.empty()) + { + processEvent(mListevent.front(), mListParamA.front(), mListParamB.front()); + mListevent.pop_front(); + mListParamA.pop_front(); + mListParamB.pop_front(); + } + + processData(++counter); + } + } + +private: + int32_t counter; +}; + class MediaQualityAnalyzerTest : public ::testing::Test { public: @@ -122,21 +158,20 @@ public: virtual ~MediaQualityAnalyzerTest() {} protected: - MediaQualityAnalyzer* mAnalyzer; + FakeMediaQualityAnalyzer* mAnalyzer; AudioConfig mConfig; RtcpConfig mRtcpConfig; AmrParams mAmrParam; EvsParams mEvsParam; FakeMediaQualityCallback mFakeCallback; MockBaseSessionCallback mCallback; - ImsMediaCondition mCondition; virtual void SetUp() override { mCallback.SetDelegate(&mFakeCallback); mCallback.DelegateToFake(); - mAnalyzer = new MediaQualityAnalyzer(); + mAnalyzer = new FakeMediaQualityAnalyzer(); mRtcpConfig.setCanonicalName(kCanonicalName); mRtcpConfig.setTransmitPort(kTransmitPort); mRtcpConfig.setIntervalSec(kIntervalSec); @@ -172,7 +207,6 @@ protected: mAnalyzer->setCallback(&mCallback); mAnalyzer->setConfig(&mConfig); - mCondition.reset(); } virtual void TearDown() override @@ -243,7 +277,8 @@ TEST_F(MediaQualityAnalyzerTest, TestCollectTxPackets) mAnalyzer->SendEvent(kCollectPacketInfo, kStreamRtpTx, reinterpret_cast<uint64_t>(packet)); } - mCondition.wait_timeout(1100); // 1.1 sec + mAnalyzer->testProcessCycle(1); + EXPECT_EQ(mAnalyzer->getTxPacketSize(), numPackets); EXPECT_EQ(mAnalyzer->getRxPacketSize(), 0); EXPECT_EQ(mAnalyzer->getLostPacketSize(), 0); @@ -259,17 +294,69 @@ TEST_F(MediaQualityAnalyzerTest, TestCollectTxPackets) TEST_F(MediaQualityAnalyzerTest, TestRtpInactivityNotRunning) { - EXPECT_CALL(mCallback, onEvent(kAudioCallQualityChangedInd, _, _)).Times(1); + EXPECT_CALL(mCallback, onEvent(kAudioCallQualityChangedInd, _, _)).Times(2); EXPECT_CALL(mCallback, onEvent(kImsMediaEventMediaQualityStatus, _, _)).Times(0); MediaQualityThreshold threshold; threshold.setRtpInactivityTimerMillis(std::vector<int32_t>{0}); mAnalyzer->setMediaQualityThreshold(threshold); mAnalyzer->start(); - mCondition.wait_timeout(2100); // 2.1 sec + mAnalyzer->testProcessCycle(2); + mAnalyzer->stop(); + + threshold.setRtpInactivityTimerMillis(std::vector<int32_t>{2000}); + mConfig.setMediaDirection(RtpConfig::MEDIA_DIRECTION_INACTIVE); + mAnalyzer->setConfig(&mConfig); + mAnalyzer->setMediaQualityThreshold(threshold); + mAnalyzer->start(); + mAnalyzer->testProcessCycle(2); + mAnalyzer->stop(); +} + +TEST_F(MediaQualityAnalyzerTest, TestRtpInactivityNoUpdateByDirection) +{ + EXPECT_CALL(mCallback, onEvent(kAudioCallQualityChangedInd, _, _)).Times(2); + EXPECT_CALL(mCallback, onEvent(kImsMediaEventMediaQualityStatus, _, _)).Times(1); + MediaQualityThreshold threshold; + threshold.setRtpInactivityTimerMillis(std::vector<int32_t>{4000}); + mAnalyzer->setMediaQualityThreshold(threshold); + mAnalyzer->testProcessCycle(2); + + mConfig.setMediaDirection(RtpConfig::MEDIA_DIRECTION_RECEIVE_ONLY); + + if (!mAnalyzer->isSameConfig(&mConfig)) + { + mAnalyzer->stop(); + mAnalyzer->start(); + } + + mAnalyzer->testProcessCycle(2); mAnalyzer->stop(); + MediaQualityStatus quality = mFakeCallback.getMediaQualityStatus(); + EXPECT_EQ(quality.getRtpInactivityTimeMillis(), 4000); } -TEST_F(MediaQualityAnalyzerTest, TestRtpInactivityRunning) +TEST_F(MediaQualityAnalyzerTest, TestRtpInactivityUpdateByDirection) +{ + EXPECT_CALL(mCallback, onEvent(kAudioCallQualityChangedInd, _, _)).Times(2); + EXPECT_CALL(mCallback, onEvent(kImsMediaEventMediaQualityStatus, _, _)).Times(1); + MediaQualityThreshold threshold; + threshold.setRtpInactivityTimerMillis(std::vector<int32_t>{2000}); + mAnalyzer->setMediaQualityThreshold(threshold); + mAnalyzer->testProcessCycle(2); + + mConfig.setMediaDirection(RtpConfig::MEDIA_DIRECTION_INACTIVE); + + if (!mAnalyzer->isSameConfig(&mConfig)) + { + mAnalyzer->stop(); + mAnalyzer->start(); + } + + mAnalyzer->testProcessCycle(2); + mAnalyzer->stop(); +} + +TEST_F(MediaQualityAnalyzerTest, TestRtpInactivityUpdate) { EXPECT_CALL(mCallback, onEvent(kAudioCallQualityChangedInd, _, _)).Times(2); EXPECT_CALL(mCallback, onEvent(kImsMediaEventMediaQualityStatus, _, _)).Times(3); @@ -277,13 +364,13 @@ TEST_F(MediaQualityAnalyzerTest, TestRtpInactivityRunning) threshold.setRtpInactivityTimerMillis(kRtpInactivityTimerMillis); mAnalyzer->setMediaQualityThreshold(threshold); mAnalyzer->start(); - mCondition.wait_timeout(2100); // 2.1 sec + mAnalyzer->testProcessCycle(2); // Check MediaQualityStatus value MediaQualityStatus quality1 = mFakeCallback.getMediaQualityStatus(); EXPECT_EQ(quality1.getRtpInactivityTimeMillis(), 2000); - mCondition.wait_timeout(2100); // 2.1 sec + mAnalyzer->testProcessCycle(2); // Check MediaQualityStatus value MediaQualityStatus quality2 = mFakeCallback.getMediaQualityStatus(); @@ -293,13 +380,43 @@ TEST_F(MediaQualityAnalyzerTest, TestRtpInactivityRunning) packet->seqNum = 0; mAnalyzer->SendEvent(kCollectPacketInfo, kStreamRtpRx, reinterpret_cast<uint64_t>(packet)); - mCondition.wait_timeout(3100); // 3.1 sec + mAnalyzer->testProcessCycle(3); MediaQualityStatus quality3 = mFakeCallback.getMediaQualityStatus(); EXPECT_EQ(quality3.getRtpInactivityTimeMillis(), 2000); mAnalyzer->stop(); } +TEST_F(MediaQualityAnalyzerTest, TestRtcpInactivityNotRunning) +{ + EXPECT_CALL(mCallback, onEvent(kAudioCallQualityChangedInd, _, _)).Times(3); + EXPECT_CALL(mCallback, onEvent(kImsMediaEventMediaQualityStatus, _, _)).Times(0); + MediaQualityThreshold threshold; + threshold.setRtcpInactivityTimerMillis(0); + mAnalyzer->setMediaQualityThreshold(threshold); + mAnalyzer->start(); + mAnalyzer->testProcessCycle(2); + mAnalyzer->stop(); + + threshold.setRtcpInactivityTimerMillis(2000); + mConfig.setMediaDirection(RtpConfig::MEDIA_DIRECTION_NO_FLOW); + mAnalyzer->setConfig(&mConfig); + mAnalyzer->setMediaQualityThreshold(threshold); + mAnalyzer->start(); + mAnalyzer->testProcessCycle(2); + mAnalyzer->stop(); + + threshold.setRtcpInactivityTimerMillis(2000); + mRtcpConfig.setIntervalSec(0); + mConfig.setMediaDirection(RtpConfig::MEDIA_DIRECTION_INACTIVE); + mConfig.setRtcpConfig(mRtcpConfig); + mAnalyzer->setConfig(&mConfig); + mAnalyzer->setMediaQualityThreshold(threshold); + mAnalyzer->start(); + mAnalyzer->testProcessCycle(2); + mAnalyzer->stop(); +} + TEST_F(MediaQualityAnalyzerTest, TestRtcpInactivity) { EXPECT_CALL(mCallback, onEvent(kAudioCallQualityChangedInd, _, _)).Times(2); @@ -308,21 +425,20 @@ TEST_F(MediaQualityAnalyzerTest, TestRtcpInactivity) threshold.setRtcpInactivityTimerMillis(kRtcpInactivityTimerMillis); mAnalyzer->setMediaQualityThreshold(threshold); mAnalyzer->start(); - mCondition.wait_timeout(2100); // 2.1 sec + mAnalyzer->testProcessCycle(2); // Check MediaQualityStatus value MediaQualityStatus quality1 = mFakeCallback.getMediaQualityStatus(); EXPECT_EQ(quality1.getRtcpInactivityTimeMillis(), 2000); - mCondition.wait_timeout(2100); // 2.1 sec + mAnalyzer->testProcessCycle(2); // Check MediaQualityStatus value MediaQualityStatus quality2 = mFakeCallback.getMediaQualityStatus(); EXPECT_EQ(quality2.getRtcpInactivityTimeMillis(), 2000); mAnalyzer->SendEvent(kCollectPacketInfo, kStreamRtcp); - - mCondition.wait_timeout(3100); // 3.1 sec + mAnalyzer->testProcessCycle(3); MediaQualityStatus quality3 = mFakeCallback.getMediaQualityStatus(); EXPECT_EQ(quality3.getRtcpInactivityTimeMillis(), 2000); @@ -333,7 +449,7 @@ TEST_F(MediaQualityAnalyzerTest, TestCallQualityInactivity) { EXPECT_CALL(mCallback, onEvent(kAudioCallQualityChangedInd, _, _)).Times(2); mAnalyzer->start(); - mCondition.wait_timeout(4100); // 4.1 sec + mAnalyzer->testProcessCycle(4); mAnalyzer->stop(); // Check CallQuality value @@ -369,7 +485,7 @@ TEST_F(MediaQualityAnalyzerTest, TestCallQualityLevelChanged) SessionCallbackParameter* param = new SessionCallbackParameter(kReportPacketLossGap, 5, 1); mAnalyzer->SendEvent(kCollectOptionalInfo, reinterpret_cast<uint64_t>(param), 0); - mCondition.wait_timeout(5100); // 5.1 sec + mAnalyzer->testProcessCycle(5); EXPECT_EQ(mAnalyzer->getTxPacketSize(), 0); EXPECT_EQ(mAnalyzer->getRxPacketSize(), numPackets - 1); @@ -409,7 +525,7 @@ TEST_F(MediaQualityAnalyzerTest, TestJitterInd) mAnalyzer->SendEvent(kCollectPacketInfo, kStreamRtpRx, reinterpret_cast<uint64_t>(packet)); } - mCondition.wait_timeout(1100); // 1.1 sec + mAnalyzer->testProcessCycle(1); EXPECT_EQ(mAnalyzer->getTxPacketSize(), 0); EXPECT_EQ(mAnalyzer->getRxPacketSize(), numPackets); @@ -452,7 +568,8 @@ TEST_F(MediaQualityAnalyzerTest, TestSsrcChange) mAnalyzer->SendEvent(kCollectPacketInfo, kStreamRtpRx, reinterpret_cast<uint64_t>(packet)); } - mCondition.wait_timeout(1100); // 1.1 sec + mAnalyzer->testProcessCycle(1); + EXPECT_EQ(mAnalyzer->getTxPacketSize(), 0); EXPECT_EQ(mAnalyzer->getRxPacketSize(), numPackets); EXPECT_EQ(mAnalyzer->getLostPacketSize(), 0); @@ -481,7 +598,7 @@ TEST_F(MediaQualityAnalyzerTest, TestPacketLossInd) { RtpPacket* packet = new RtpPacket(); - if (i == 5) // make 10% loss rate + if (i == 5 || i == 6) // make 20% loss rate { continue; } @@ -492,14 +609,14 @@ TEST_F(MediaQualityAnalyzerTest, TestPacketLossInd) mAnalyzer->SendEvent(kCollectPacketInfo, kStreamRtpRx, reinterpret_cast<uint64_t>(packet)); } - SessionCallbackParameter* param = new SessionCallbackParameter(kReportPacketLossGap, 5, 1); + SessionCallbackParameter* param = new SessionCallbackParameter(kReportPacketLossGap, 5, 2); mAnalyzer->SendEvent(kCollectOptionalInfo, reinterpret_cast<uint64_t>(param), 0); - mCondition.wait_timeout(1100); // 1.1 sec + mAnalyzer->testProcessCycle(1); EXPECT_EQ(mAnalyzer->getTxPacketSize(), 0); - EXPECT_EQ(mAnalyzer->getRxPacketSize(), numPackets - 1); - EXPECT_EQ(mAnalyzer->getLostPacketSize(), 1); + EXPECT_EQ(mAnalyzer->getRxPacketSize(), numPackets - 2); + EXPECT_EQ(mAnalyzer->getLostPacketSize(), 2); mAnalyzer->stop(); @@ -507,10 +624,10 @@ TEST_F(MediaQualityAnalyzerTest, TestPacketLossInd) EXPECT_EQ(mAnalyzer->getRxPacketSize(), 0); EXPECT_EQ(mAnalyzer->getLostPacketSize(), 0); - EXPECT_EQ(mFakeCallback.getCallQuality().getNumRtpPacketsNotReceived(), 1); + EXPECT_EQ(mFakeCallback.getCallQuality().getNumRtpPacketsNotReceived(), 2); MediaQualityStatus status = mFakeCallback.getMediaQualityStatus(); - EXPECT_EQ(status.getRtpPacketLossRate(), 10); + EXPECT_EQ(status.getRtpPacketLossRate(), 20); } TEST_F(MediaQualityAnalyzerTest, TestNotifyMediaQualityStatus) @@ -521,7 +638,6 @@ TEST_F(MediaQualityAnalyzerTest, TestNotifyMediaQualityStatus) threshold.setNotifyCurrentStatus(true); mAnalyzer->setMediaQualityThreshold(threshold); mAnalyzer->start(); - - mCondition.wait_timeout(2100); // 2.1 sec + mAnalyzer->testProcessCycle(2); mAnalyzer->stop(); }
\ No newline at end of file diff --git a/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/node/AudioRtpPayloadNodeTest.cpp b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/node/AudioRtpPayloadNodeTest.cpp new file mode 100644 index 00000000..0221f6fa --- /dev/null +++ b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/node/AudioRtpPayloadNodeTest.cpp @@ -0,0 +1,296 @@ +/** + * 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 <gtest/gtest.h> +#include <AudioConfig.h> +#include <AudioRtpPayloadEncoderNode.h> +#include <AudioRtpPayloadDecoderNode.h> +#include <string.h> + +using namespace android::telephony::imsmedia; +using namespace android; + +// RtpConfig +const int32_t kMediaDirection = RtpConfig::MEDIA_DIRECTION_SEND_RECEIVE; +const String8 kRemoteAddress("127.0.0.1"); +const int32_t kRemotePort = 10000; +const int8_t kDscp = 0; +const int8_t kRxPayload = 96; +const int8_t kTxPayload = 96; +const int8_t kSamplingRate = 16; + +// RtcpConfig +const String8 kCanonicalName("name"); +const int32_t kTransmitPort = 10001; +const int32_t kIntervalSec = 5; +const int32_t kRtcpXrBlockTypes = 0; + +// AudioConfig +const int8_t kPTimeMillis = 20; +const int32_t kMaxPtimeMillis = 100; +const bool kDtxEnabled = true; +const int8_t kDtmfPayloadTypeNumber = 103; +const int8_t kDtmfsamplingRateKHz = 16; + +// AmrParam +const int32_t kAmrMode = AmrParams::AMR_MODE_8; +const bool kOctetAligned = false; +const int32_t kMaxRedundancyMillis = 240; + +// EvsParam +const int32_t kEvsBandwidth = EvsParams::EVS_SUPER_WIDE_BAND; +const int32_t kEvsMode = EvsParams::EVS_MODE_13; +const int8_t kChannelAwareMode = 2; + +namespace +{ +class FakeNode : public BaseNode +{ +public: + explicit FakeNode(BaseSessionCallback* callback = nullptr) : + BaseNode(callback) + { + frameSize = 0; + memset(dataFrame, 0, sizeof(dataFrame)); + } + virtual ~FakeNode() {} + virtual ImsMediaResult Start() { return RESULT_SUCCESS; } + virtual void Stop() {} + virtual bool IsRunTime() { return true; } + virtual bool IsSourceNode() { return false; } + virtual void SetConfig(void* config) { (void)config; } + virtual void OnDataFromFrontNode(ImsMediaSubType /*IMS_MEDIA_AUDIO*/, uint8_t* data, + uint32_t size, uint32_t /*timestamp*/, bool /*mark*/, uint32_t /*seq*/, + ImsMediaSubType /*dataType*/, uint32_t /*arrivalTime*/) + { + if (data != nullptr && size > 0) + { + memset(dataFrame, 0, sizeof(dataFrame)); + memcpy(dataFrame, data, size); + frameSize = size; + } + } + + virtual kBaseNodeState GetState() { return kNodeStateRunning; } + + uint32_t GetFrameSize() { return frameSize; } + uint8_t* GetDataFrame() { return dataFrame; } + +private: + uint32_t frameSize; + uint8_t dataFrame[DEFAULT_MTU]; +}; + +class AudioRtpPayloadNodeTest : public ::testing::Test +{ +public: + AudioRtpPayloadNodeTest() + { + encoder = nullptr; + decoder = nullptr; + fakeNode = nullptr; + } + virtual ~AudioRtpPayloadNodeTest() {} + +protected: + AmrParams amr; + EvsParams evs; + RtcpConfig rtcp; + AudioConfig audioConfig; + AudioRtpPayloadEncoderNode* encoder; + AudioRtpPayloadDecoderNode* decoder; + FakeNode* fakeNode; + std::list<BaseNode*> nodes; + + virtual void SetUp() override + { + rtcp.setCanonicalName(kCanonicalName); + rtcp.setTransmitPort(kTransmitPort); + rtcp.setIntervalSec(kIntervalSec); + rtcp.setRtcpXrBlockTypes(kRtcpXrBlockTypes); + + setupAudioConfig(); + setupNodes(&audioConfig); + } + + virtual void TearDown() override + { + while (nodes.size() > 0) + { + BaseNode* node = nodes.front(); + node->Stop(); + delete node; + nodes.pop_front(); + } + } + +public: + void setupNodes(AudioConfig* config) + { + encoder = new AudioRtpPayloadEncoderNode(); + encoder->SetMediaType(IMS_MEDIA_AUDIO); + encoder->SetConfig(config); + nodes.push_back(encoder); + + decoder = new AudioRtpPayloadDecoderNode(); + decoder->SetMediaType(IMS_MEDIA_AUDIO); + decoder->SetConfig(config); + nodes.push_back(decoder); + encoder->ConnectRearNode(decoder); + + fakeNode = new FakeNode(); + fakeNode->SetMediaType(IMS_MEDIA_AUDIO); + fakeNode->SetConfig(config); + nodes.push_back(fakeNode); + decoder->ConnectRearNode(fakeNode); + } + + void setupAudioConfig() + { + amr.setAmrMode(kAmrMode); + amr.setOctetAligned(kOctetAligned); + amr.setMaxRedundancyMillis(kMaxRedundancyMillis); + + audioConfig.setMediaDirection(kMediaDirection); + audioConfig.setRemoteAddress(kRemoteAddress); + audioConfig.setRemotePort(kRemotePort); + audioConfig.setRtcpConfig(rtcp); + audioConfig.setDscp(kDscp); + audioConfig.setRxPayloadTypeNumber(kRxPayload); + audioConfig.setTxPayloadTypeNumber(kTxPayload); + audioConfig.setSamplingRateKHz(kSamplingRate); + audioConfig.setPtimeMillis(kPTimeMillis); + audioConfig.setMaxPtimeMillis(kMaxPtimeMillis); + audioConfig.setDtxEnabled(kDtxEnabled); + audioConfig.setCodecType(AudioConfig::CODEC_AMR_WB); + audioConfig.setTxDtmfPayloadTypeNumber(kDtmfPayloadTypeNumber); + audioConfig.setRxDtmfPayloadTypeNumber(kDtmfPayloadTypeNumber); + audioConfig.setDtmfsamplingRateKHz(kDtmfsamplingRateKHz); + audioConfig.setAmrParams(amr); + audioConfig.setEvsParams(evs); + } +}; + +TEST_F(AudioRtpPayloadNodeTest, startFail) +{ + audioConfig.setPtimeMillis(0); + encoder->SetConfig(&audioConfig); + EXPECT_EQ(encoder->Start(), RESULT_INVALID_PARAM); +} + +TEST_F(AudioRtpPayloadNodeTest, startAndUpdate) +{ + EXPECT_EQ(encoder->Start(), RESULT_SUCCESS); + EXPECT_EQ(decoder->Start(), RESULT_SUCCESS); + + // no update + EXPECT_EQ(encoder->UpdateConfig(&audioConfig), RESULT_SUCCESS); + EXPECT_EQ(decoder->UpdateConfig(&audioConfig), RESULT_SUCCESS); + + // update + audioConfig.setCodecType(AudioConfig::CODEC_AMR); + EXPECT_EQ(encoder->UpdateConfig(&audioConfig), RESULT_SUCCESS); + EXPECT_EQ(decoder->UpdateConfig(&audioConfig), RESULT_SUCCESS); +} + +TEST_F(AudioRtpPayloadNodeTest, testAmrBandwidthEfficientDataProcess) +{ + EXPECT_EQ(encoder->Start(), RESULT_SUCCESS); + EXPECT_EQ(decoder->Start(), RESULT_SUCCESS); + + // AMR-WB mode 8 audio frame with toc field + uint8_t testFrame[] = {0x44, 0xe6, 0x6e, 0x84, 0x8a, 0xa4, 0xda, 0xc8, 0xf2, 0x6c, 0xeb, 0x87, + 0xe4, 0x56, 0x0f, 0x49, 0x47, 0xfa, 0xdc, 0xa7, 0x9d, 0xbb, 0xcf, 0xda, 0xda, 0x67, + 0x80, 0xc2, 0x7f, 0x8d, 0x5b, 0xab, 0xd9, 0xbb, 0xd7, 0x1e, 0x60, 0x96, 0x5d, 0xdd, + 0x28, 0x65, 0x5f, 0x43, 0xf4, 0xb9, 0x0d, 0x7d, 0x05, 0x4e, 0x30, 0x50, 0xe1, 0x98, + 0x03, 0xed, 0xee, 0x8a, 0xa8, 0x34, 0x40}; + + encoder->OnDataFromFrontNode(MEDIASUBTYPE_UNDEFINED, testFrame, sizeof(testFrame), 0, false, 0); + EXPECT_EQ(fakeNode->GetFrameSize(), sizeof(testFrame)); + EXPECT_EQ(memcmp(fakeNode->GetDataFrame(), testFrame, fakeNode->GetFrameSize()), 0); +} + +TEST_F(AudioRtpPayloadNodeTest, testAmrOctetAlignedDataProcess) +{ + amr.setOctetAligned(true); + audioConfig.setAmrParams(amr); + encoder->SetConfig(&audioConfig); + decoder->SetConfig(&audioConfig); + EXPECT_EQ(encoder->Start(), RESULT_SUCCESS); + EXPECT_EQ(decoder->Start(), RESULT_SUCCESS); + + // AMR-WB mode 8 audio frame with toc field + uint8_t testFrame[] = {0x44, 0xe6, 0x6e, 0x84, 0x8a, 0xa4, 0xda, 0xc8, 0xf2, 0x6c, 0xeb, 0x87, + 0xe4, 0x56, 0x0f, 0x49, 0x47, 0xfa, 0xdc, 0xa7, 0x9d, 0xbb, 0xcf, 0xda, 0xda, 0x67, + 0x80, 0xc2, 0x7f, 0x8d, 0x5b, 0xab, 0xd9, 0xbb, 0xd7, 0x1e, 0x60, 0x96, 0x5d, 0xdd, + 0x28, 0x65, 0x5f, 0x43, 0xf4, 0xb9, 0x0d, 0x7d, 0x05, 0x4e, 0x30, 0x50, 0xe1, 0x98, + 0x03, 0xed, 0xee, 0x8a, 0xa8, 0x34, 0x40}; + + encoder->OnDataFromFrontNode(MEDIASUBTYPE_UNDEFINED, testFrame, sizeof(testFrame), 0, false, 0); + EXPECT_EQ(fakeNode->GetFrameSize(), sizeof(testFrame)); + EXPECT_EQ(memcmp(fakeNode->GetDataFrame(), testFrame, fakeNode->GetFrameSize()), 0); +} + +TEST_F(AudioRtpPayloadNodeTest, testEvsCompactModeDataProcess) +{ + evs.setEvsBandwidth(kEvsBandwidth); + evs.setEvsMode(kEvsMode); + evs.setChannelAwareMode(kChannelAwareMode); + evs.setUseHeaderFullOnly(false); + evs.setCodecModeRequest(-1); + + audioConfig.setEvsParams(evs); + audioConfig.setCodecType(AudioConfig::CODEC_EVS); + encoder->SetConfig(&audioConfig); + decoder->SetConfig(&audioConfig); + EXPECT_EQ(encoder->Start(), RESULT_SUCCESS); + EXPECT_EQ(decoder->Start(), RESULT_SUCCESS); + + // EVS mode 13.2 kbps frame without toc field + uint8_t testFrame[] = {0xce, 0x40, 0xf2, 0xb2, 0xa4, 0xce, 0x4f, 0xd9, 0xfa, 0xe9, 0x77, 0xdc, + 0x9b, 0xc0, 0xa8, 0x10, 0xc8, 0xc3, 0x0f, 0xc9, 0x52, 0xc1, 0xda, 0x45, 0x7e, 0x6c, + 0x55, 0x47, 0xff, 0xff, 0xff, 0xff, 0xe0}; + + encoder->OnDataFromFrontNode(MEDIASUBTYPE_UNDEFINED, testFrame, sizeof(testFrame), 0, false, 0); + EXPECT_EQ(fakeNode->GetFrameSize(), sizeof(testFrame)); + EXPECT_EQ(memcmp(fakeNode->GetDataFrame(), testFrame, fakeNode->GetFrameSize()), 0); +} + +TEST_F(AudioRtpPayloadNodeTest, testEvsHeaderFullModeDataProcess) +{ + evs.setEvsBandwidth(kEvsBandwidth); + evs.setEvsMode(kEvsMode); + evs.setChannelAwareMode(kChannelAwareMode); + evs.setUseHeaderFullOnly(true); + evs.setCodecModeRequest(-1); + + audioConfig.setEvsParams(evs); + audioConfig.setCodecType(AudioConfig::CODEC_EVS); + encoder->SetConfig(&audioConfig); + decoder->SetConfig(&audioConfig); + EXPECT_EQ(encoder->Start(), RESULT_SUCCESS); + EXPECT_EQ(decoder->Start(), RESULT_SUCCESS); + + // EVS mode 13.2 kbps frame with toc field + uint8_t testFrame[] = {0x04, 0xce, 0x40, 0xf2, 0xb2, 0xa4, 0xce, 0x4f, 0xd9, 0xfa, 0xe9, 0x77, + 0xdc, 0x9b, 0xc0, 0xa8, 0x10, 0xc8, 0xc3, 0x0f, 0xc9, 0x52, 0xc1, 0xda, 0x45, 0x7e, + 0x6c, 0x55, 0x47, 0xff, 0xff, 0xff, 0xff, 0xe0}; + + encoder->OnDataFromFrontNode(MEDIASUBTYPE_UNDEFINED, testFrame, sizeof(testFrame), 0, false, 0); + EXPECT_EQ(fakeNode->GetFrameSize(), sizeof(testFrame)); + EXPECT_EQ(memcmp(fakeNode->GetDataFrame(), testFrame, fakeNode->GetFrameSize()), 0); +} +} // namespace diff --git a/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/nodes/RtcpDecoderNodeTests.cpp b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/nodes/RtcpDecoderNodeTests.cpp new file mode 100644 index 00000000..05687f48 --- /dev/null +++ b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/nodes/RtcpDecoderNodeTests.cpp @@ -0,0 +1,303 @@ +/** + * 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 <gtest/gtest.h> +#include <condition_variable> +#include <mutex> +#include <RtcpConfig.h> +#include <AudioConfig.h> +#include <VideoConfig.h> +#include <TextConfig.h> +#include <RtcpDecoderNode.h> +#include <ImsMediaVideoUtil.h> +#include <ImsMediaTrace.h> + +using namespace android::telephony::imsmedia; +using namespace android; + +namespace +{ +// RtpConfig +const int32_t kMediaDirection = RtpConfig::MEDIA_DIRECTION_SEND_RECEIVE; +const String8 kRemoteAddress("127.0.0.1"); +const int32_t kRemotePort = 10000; +const int8_t kDscp = 0; +const int8_t kRxPayload = 96; +const int8_t kTxPayload = 96; +const int8_t kSamplingRate = 16; + +// RtcpConfig +const String8 kCanonicalName("name"); +const int32_t kTransmitPort = 10001; +const int32_t RTCP_INTERVAL = 1; +const int32_t kIntervalSec = RTCP_INTERVAL; +int32_t kRtcpXrBlockTypes = 0; + +// VideoConfig +const int32_t kVideoMode = VideoConfig::VIDEO_MODE_PREVIEW; +const int32_t kMtu = 1500; +const int32_t kFramerate = DEFAULT_FRAMERATE; +const int32_t kBitrate = DEFAULT_BITRATE; +const int32_t kCodecProfile = VideoConfig::AVC_PROFILE_BASELINE; +const int32_t kCodecLevel = VideoConfig::AVC_LEVEL_12; +const int32_t kIntraFrameIntervalSec = 1; +const int32_t kPacketizationMode = VideoConfig::MODE_NON_INTERLEAVED; +const int32_t kCameraId = 0; +const int32_t kCameraZoom = 10; +const int32_t kResolutionWidth = DEFAULT_RESOLUTION_WIDTH; +const int32_t kResolutionHeight = DEFAULT_RESOLUTION_HEIGHT; +const int32_t kDeviceOrientationDegree = 0; +const int32_t kCvoValue = 1; +const int32_t kRtcpFbTypes = VideoConfig::RTP_FB_NONE; + +static std::condition_variable cond; +static std::mutex timerMutex; + +class FakeNode : public BaseNode +{ +public: + bool mOnDataFromFrontNodeCalled = false; + virtual ~FakeNode() {} + void Stop() {} + bool IsRunTime() { return true; } + bool IsSourceNode() { return false; } + virtual kBaseNodeState GetState() { return kNodeStateRunning; } + void SetConfig(void* config) { (void)config; } + void OnDataFromFrontNode(ImsMediaSubType, uint8_t*, uint32_t, uint32_t, bool, uint32_t, + ImsMediaSubType, uint32_t) + { + IMLOGI0("FakeNode::OnDataFromFrontNode"); + mOnDataFromFrontNodeCalled = true; + cond.notify_all(); + } + + ImsMediaResult Start() { return RESULT_SUCCESS; } +}; + +class SessionCallback : public BaseSessionCallback +{ +public: + bool mOnEventCalled = false; + int32_t mType; + uint64_t mParam1, mParam2; + + virtual ~SessionCallback() {} + virtual void onEvent(int32_t type, uint64_t param1, uint64_t param2) + { + IMLOGI0("SessionCallback::onEvent"); + mOnEventCalled = true; + mType = type; + mParam1 = param1; + mParam2 = param2; + cond.notify_all(); + } +}; + +class RtcpDecoderNodeEx : public RtcpDecoderNode +{ +public: + bool mCallBaseClassMethod = false; + bool mOnRtcpIndCalled = false; + + RtcpDecoderNodeEx(BaseSessionCallback* callback = nullptr) : + RtcpDecoderNode(callback) + { + } + virtual ~RtcpDecoderNodeEx() {} +}; + +class RtcpDecoderNodeTests : public ::testing::Test +{ +public: + virtual ~RtcpDecoderNodeTests() {} + +protected: + RtcpDecoderNodeEx* pRtcpDecNode; + VideoConfig videoConfig; + FakeNode* pFakeRearNode; + SessionCallback* pCallback; + + virtual void SetUp() override + { + pCallback = new SessionCallback(); + pRtcpDecNode = new RtcpDecoderNodeEx(pCallback); + pRtcpDecNode->SetMediaType(IMS_MEDIA_VIDEO); + setupVideoConfig(videoConfig); + pRtcpDecNode->SetConfig(&videoConfig); + pFakeRearNode = connectNodes(pRtcpDecNode); + } + + virtual void TearDown() override + { + delete pRtcpDecNode; + delete pFakeRearNode; + delete pCallback; + } + + void setupRtcpConfig(RtcpConfig& rtcpConfig) + { + rtcpConfig.setCanonicalName(kCanonicalName); + rtcpConfig.setTransmitPort(kTransmitPort); + rtcpConfig.setIntervalSec(kIntervalSec); + rtcpConfig.setRtcpXrBlockTypes(kRtcpXrBlockTypes); + } + + // using video codec because RTCP has feedback implementation for video media type. + void setupVideoConfig(VideoConfig& videoConfig) + { + videoConfig.setMediaDirection(kMediaDirection); + videoConfig.setRemoteAddress(kRemoteAddress); + videoConfig.setRemotePort(kRemotePort); + RtcpConfig rtcpConfig; + setupRtcpConfig(rtcpConfig); + videoConfig.setRtcpConfig(rtcpConfig); + videoConfig.setMaxMtuBytes(kMtu); + videoConfig.setDscp(kDscp); + videoConfig.setRxPayloadTypeNumber(kRxPayload); + videoConfig.setTxPayloadTypeNumber(kTxPayload); + videoConfig.setSamplingRateKHz(kSamplingRate); + videoConfig.setVideoMode(kVideoMode); + videoConfig.setCodecType(VideoConfig::CODEC_AVC); + videoConfig.setFramerate(kFramerate); + videoConfig.setBitrate(kBitrate); + videoConfig.setCodecProfile(kCodecProfile); + videoConfig.setCodecLevel(kCodecLevel); + videoConfig.setIntraFrameInterval(kIntraFrameIntervalSec); + videoConfig.setPacketizationMode(kPacketizationMode); + videoConfig.setCameraId(kCameraId); + videoConfig.setCameraZoom(kCameraZoom); + videoConfig.setResolutionWidth(kResolutionWidth); + videoConfig.setResolutionHeight(kResolutionHeight); + videoConfig.setDeviceOrientationDegree(kDeviceOrientationDegree); + videoConfig.setCvoValue(kCvoValue); + videoConfig.setRtcpFbType(kRtcpFbTypes); + } + + FakeNode* connectNodes(RtcpDecoderNode* pRtcpDecNode) + { + FakeNode* pFakeNode = new FakeNode(); + pRtcpDecNode->ConnectRearNode(pFakeNode); + return pFakeNode; + } +}; + +TEST_F(RtcpDecoderNodeTests, TestInitState) +{ + EXPECT_EQ(pRtcpDecNode->GetNodeId(), kNodeIdRtcpDecoder); + EXPECT_EQ(pRtcpDecNode->IsRunTime(), true); + EXPECT_EQ(pRtcpDecNode->IsSourceNode(), false); +} + +TEST_F(RtcpDecoderNodeTests, TestConfigChange) +{ + VideoConfig videoConfig; + setupVideoConfig(videoConfig); + EXPECT_EQ(pRtcpDecNode->IsSameConfig(&videoConfig), true); +} + +TEST_F(RtcpDecoderNodeTests, TestStartStopSuccess) +{ + EXPECT_EQ(pRtcpDecNode->Start(), RESULT_SUCCESS); + EXPECT_EQ(pRtcpDecNode->GetState(), kNodeStateRunning); + + pRtcpDecNode->Stop(); + EXPECT_EQ(pRtcpDecNode->GetState(), kNodeStateStopped); +} + +TEST_F(RtcpDecoderNodeTests, TestOnRtcpSrInd) +{ + pRtcpDecNode->SetMediaType(IMS_MEDIA_AUDIO); + tNotifyReceiveRtcpSrInd payload; + memset(&payload, 0x00, sizeof(payload)); + pRtcpDecNode->OnRtcpInd(RTPSVC_RECEIVE_RTCP_SR_IND, &payload); + EXPECT_EQ(pCallback->mOnEventCalled, true); + EXPECT_EQ(pCallback->mType, kCollectPacketInfo); + EXPECT_EQ(pCallback->mParam1, kStreamRtcp); +} + +TEST_F(RtcpDecoderNodeTests, TestOnRtcpRrInd) +{ + pRtcpDecNode->SetMediaType(IMS_MEDIA_AUDIO); + tNotifyReceiveRtcpRrInd payload; + memset(&payload, 0x00, sizeof(payload)); + pRtcpDecNode->OnRtcpInd(RTPSVC_RECEIVE_RTCP_RR_IND, &payload); + EXPECT_EQ(pCallback->mOnEventCalled, true); + EXPECT_EQ(pCallback->mType, kCollectPacketInfo); + EXPECT_EQ(pCallback->mParam1, kStreamRtcp); +} + +TEST_F(RtcpDecoderNodeTests, TestOnRtcpFbInd) +{ + pRtcpDecNode->SetMediaType(IMS_MEDIA_AUDIO); + tRtpSvcIndSt_ReceiveRtcpFeedbackInd payload; + memset(&payload, 0x00, sizeof(payload)); + payload.wFmt = kRtpFbTmmbr; + uint8_t fbMsgData[64]; + payload.pMsg = fbMsgData; + pRtcpDecNode->OnRtcpInd(RTPSVC_RECEIVE_RTCP_FB_IND, &payload); + EXPECT_EQ(pCallback->mOnEventCalled, true); + EXPECT_EQ(pCallback->mType, kRequestVideoSendTmmbn); +} + +TEST_F(RtcpDecoderNodeTests, TestOnNumReceivedPacket) +{ + pRtcpDecNode->SetMediaType(IMS_MEDIA_AUDIO); + pRtcpDecNode->SetInactivityTimerSec(1); + pRtcpDecNode->OnNumReceivedPacket(0, 0); + EXPECT_EQ(pCallback->mOnEventCalled, true); + EXPECT_EQ(pCallback->mType, kImsMediaEventMediaInactivity); + EXPECT_EQ(pCallback->mParam1, kProtocolRtcp); + EXPECT_EQ(pCallback->mParam2, 1); + + pCallback->mOnEventCalled = false; + pRtcpDecNode->OnNumReceivedPacket(1, 0); + EXPECT_EQ(pCallback->mOnEventCalled, false); + + pCallback->mOnEventCalled = false; + pRtcpDecNode->OnNumReceivedPacket(0, 1); + EXPECT_EQ(pCallback->mOnEventCalled, false); + + pCallback->mOnEventCalled = false; + pRtcpDecNode->OnNumReceivedPacket(1, 1); + EXPECT_EQ(pCallback->mOnEventCalled, false); +} + +TEST_F(RtcpDecoderNodeTests, TestOnEvent) +{ + pRtcpDecNode->OnEvent(kRequestRoundTripTimeDelayUpdate, 100); + EXPECT_EQ(pCallback->mOnEventCalled, true); + EXPECT_EQ(pCallback->mType, kRequestRoundTripTimeDelayUpdate); +} + +TEST_F(RtcpDecoderNodeTests, TestReceiveTmmbr) +{ + pRtcpDecNode->SetMediaType(IMS_MEDIA_AUDIO); + tRtpSvcIndSt_ReceiveRtcpFeedbackInd payload; + memset(&payload, 0x00, sizeof(payload)); + uint8_t fbMsgData[64]; + payload.pMsg = fbMsgData; + pRtcpDecNode->ReceiveTmmbr(&payload); + EXPECT_EQ(pCallback->mOnEventCalled, true); + EXPECT_EQ(pCallback->mType, kRequestVideoSendTmmbn); +} + +TEST_F(RtcpDecoderNodeTests, TestRequestIdrFrame) +{ + pRtcpDecNode->RequestIdrFrame(); + EXPECT_EQ(pCallback->mOnEventCalled, true); + EXPECT_EQ(pCallback->mType, kRequestVideoIdrFrame); +} +} // namespace diff --git a/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/nodes/RtcpEncoderNodeTests.cpp b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/nodes/RtcpEncoderNodeTests.cpp new file mode 100644 index 00000000..94fd213d --- /dev/null +++ b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/nodes/RtcpEncoderNodeTests.cpp @@ -0,0 +1,349 @@ +/** + * 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 <gtest/gtest.h> +#include <condition_variable> +#include <mutex> +#include <RtcpConfig.h> +#include <AudioConfig.h> +#include <VideoConfig.h> +#include <TextConfig.h> +#include <RtcpEncoderNode.h> +#include <android/log.h> + +using namespace android::telephony::imsmedia; +using namespace android; + +namespace +{ +// RtpConfig +const int32_t kMediaDirection = RtpConfig::MEDIA_DIRECTION_SEND_RECEIVE; +const String8 kRemoteAddress("127.0.0.1"); +const int32_t kRemotePort = 10000; +const int8_t kDscp = 0; +const int8_t kRxPayload = 96; +const int8_t kTxPayload = 96; +const int8_t kSamplingRate = 16; + +// RtcpConfig +const String8 kCanonicalName("name"); +const int32_t kTransmitPort = 10001; +const int32_t RTCP_INTERVAL = 1; +const int32_t kIntervalSec = RTCP_INTERVAL; +int32_t kRtcpXrBlockTypes = 0; + +// VideoConfig +const int32_t kVideoMode = VideoConfig::VIDEO_MODE_PREVIEW; +const int32_t kMtu = 1500; +const int32_t kFramerate = DEFAULT_FRAMERATE; +const int32_t kBitrate = DEFAULT_BITRATE; +const int32_t kCodecProfile = VideoConfig::AVC_PROFILE_BASELINE; +const int32_t kCodecLevel = VideoConfig::AVC_LEVEL_12; +const int32_t kIntraFrameIntervalSec = 1; +const int32_t kPacketizationMode = VideoConfig::MODE_NON_INTERLEAVED; +const int32_t kCameraId = 0; +const int32_t kCameraZoom = 10; +const int32_t kResolutionWidth = DEFAULT_RESOLUTION_WIDTH; +const int32_t kResolutionHeight = DEFAULT_RESOLUTION_HEIGHT; +const android::String8 kPauseImagePath("data/user_de/0/com.android.telephony.imsmedia/test.jpg"); +const int32_t kDeviceOrientationDegree = 0; +const int32_t kCvoValue = 1; +const int32_t kRtcpFbTypes = VideoConfig::RTP_FB_NONE; + +static std::condition_variable cond; +static std::mutex timerMutex; + +class FakeNode : public BaseNode +{ +public: + bool mOnDataFromFrontNodeCalled = false; + virtual ~FakeNode() {} + void Stop() {} + bool IsRunTime() { return true; } + bool IsSourceNode() { return false; } + virtual kBaseNodeState GetState() { return kNodeStateRunning; } + void SetConfig(void* config) { (void)config; } + void OnDataFromFrontNode(ImsMediaSubType, uint8_t*, uint32_t, uint32_t, bool, uint32_t, + ImsMediaSubType, uint32_t) + { + mOnDataFromFrontNodeCalled = true; + cond.notify_all(); + } + + ImsMediaResult Start() { return RESULT_SUCCESS; } +}; + +class SessionCallback : public BaseSessionCallback +{ +public: + bool mOnEventCalled = false; + virtual ~SessionCallback() {} + virtual void onEvent(int32_t, uint64_t, uint64_t) + { + mOnEventCalled = true; + cond.notify_all(); + } +}; + +class RtcpEncoderNodeEx : public RtcpEncoderNode +{ +public: + bool mCallBaseClassMethod = false; + bool mProcessTimerMethodCalled = false; + + RtcpEncoderNodeEx(BaseSessionCallback* callback = nullptr) : + RtcpEncoderNode(callback) + { + } + virtual ~RtcpEncoderNodeEx() {} + + void ProcessTimer() + { + mProcessTimerMethodCalled = true; + cond.notify_all(); + + if (mCallBaseClassMethod) + { + RtcpEncoderNode::ProcessTimer(); + } + } +}; + +class RtcpEncoderNodeTests : public ::testing::Test +{ +public: + virtual ~RtcpEncoderNodeTests() {} + +protected: + void setupRtcpConfig(RtcpConfig& rtcpConfig) + { + rtcpConfig.setCanonicalName(kCanonicalName); + rtcpConfig.setTransmitPort(kTransmitPort); + rtcpConfig.setIntervalSec(kIntervalSec); + rtcpConfig.setRtcpXrBlockTypes(kRtcpXrBlockTypes); + } + + // using video codec because RTCP has feedback implementation for video media type. + void setupVideoConfig(VideoConfig& videoConfig) + { + videoConfig.setMediaDirection(kMediaDirection); + videoConfig.setRemoteAddress(kRemoteAddress); + videoConfig.setRemotePort(kRemotePort); + RtcpConfig rtcpConfig; + setupRtcpConfig(rtcpConfig); + videoConfig.setRtcpConfig(rtcpConfig); + videoConfig.setMaxMtuBytes(kMtu); + videoConfig.setDscp(kDscp); + videoConfig.setRxPayloadTypeNumber(kRxPayload); + videoConfig.setTxPayloadTypeNumber(kTxPayload); + videoConfig.setSamplingRateKHz(kSamplingRate); + videoConfig.setVideoMode(kVideoMode); + videoConfig.setCodecType(VideoConfig::CODEC_AVC); + videoConfig.setFramerate(kFramerate); + videoConfig.setBitrate(kBitrate); + videoConfig.setCodecProfile(kCodecProfile); + videoConfig.setCodecLevel(kCodecLevel); + videoConfig.setIntraFrameInterval(kIntraFrameIntervalSec); + videoConfig.setPacketizationMode(kPacketizationMode); + videoConfig.setCameraId(kCameraId); + videoConfig.setCameraZoom(kCameraZoom); + videoConfig.setResolutionWidth(kResolutionWidth); + videoConfig.setResolutionHeight(kResolutionHeight); + videoConfig.setPauseImagePath(kPauseImagePath); + videoConfig.setDeviceOrientationDegree(kDeviceOrientationDegree); + videoConfig.setCvoValue(kCvoValue); + videoConfig.setRtcpFbType(kRtcpFbTypes); + } + + FakeNode* connectNodes(RtcpEncoderNode* pRtcpEncNode) + { + FakeNode* pFakeNode = new FakeNode(); + pRtcpEncNode->ConnectRearNode(pFakeNode); + return pFakeNode; + } +}; + +TEST_F(RtcpEncoderNodeTests, TestInitState) +{ + RtcpEncoderNode* pRtcpEncNode = new RtcpEncoderNode(); + EXPECT_EQ(pRtcpEncNode->GetNodeId(), kNodeIdRtcpEncoder); + EXPECT_EQ(pRtcpEncNode->IsRunTime(), true); + EXPECT_EQ(pRtcpEncNode->IsSourceNode(), true); + delete pRtcpEncNode; +} + +TEST_F(RtcpEncoderNodeTests, TestConfigChange) +{ + RtcpEncoderNode* pRtcpEncNode = new RtcpEncoderNode(); + VideoConfig videoConfig; + setupVideoConfig(videoConfig); + pRtcpEncNode->SetConfig(&videoConfig); + EXPECT_EQ(pRtcpEncNode->IsSameConfig(&videoConfig), true); + delete pRtcpEncNode; +} + +TEST_F(RtcpEncoderNodeTests, TestStartStopSuccess) +{ + SessionCallback callback; + RtcpEncoderNodeEx* pRtcpEncNode = new RtcpEncoderNodeEx(&callback); + kRtcpXrBlockTypes = RtcpConfig::FLAG_RTCPXR_LOSS_RLE_REPORT_BLOCK; + VideoConfig videoConfig; + setupVideoConfig(videoConfig); + pRtcpEncNode->SetConfig(&videoConfig); + EXPECT_EQ(pRtcpEncNode->Start(), RESULT_SUCCESS); + + bool timeout = false; + constexpr std::chrono::duration<int> waittime = std::chrono::seconds(RTCP_INTERVAL + 1); + // Wait for RTCP timer expiry to confirm start success. + { + pRtcpEncNode->mCallBaseClassMethod = true; + std::unique_lock<std::mutex> lock(timerMutex); + if (cond.wait_for(lock, waittime) == std::cv_status::timeout) + timeout = true; + + EXPECT_EQ(timeout, false); + EXPECT_EQ(pRtcpEncNode->mProcessTimerMethodCalled, true); + } + + // Check if SendEvent is called. + { + timeout = false; + std::unique_lock<std::mutex> lock(timerMutex); + if (cond.wait_for(lock, waittime) == std::cv_status::timeout) + timeout = true; + + EXPECT_EQ(timeout, false); + EXPECT_EQ(callback.mOnEventCalled, true); + } + + // Call stop and make sure RTCP timer doesn't expire. + pRtcpEncNode->Stop(); + { + timeout = false; + pRtcpEncNode->mProcessTimerMethodCalled = false; + std::unique_lock<std::mutex> lock(timerMutex); + if (cond.wait_for(lock, waittime) == std::cv_status::timeout) + timeout = true; + + EXPECT_EQ(timeout, true); + EXPECT_EQ(pRtcpEncNode->mProcessTimerMethodCalled, false); + } + delete pRtcpEncNode; +} + +TEST_F(RtcpEncoderNodeTests, TestOnRtcpPacket) +{ + RtcpEncoderNode* pRtcpEncNode = new RtcpEncoderNode(); + FakeNode* pRearNode = connectNodes(pRtcpEncNode); + + unsigned char data[10]; + pRtcpEncNode->OnRtcpPacket(data, 10); + EXPECT_EQ(pRearNode->mOnDataFromFrontNodeCalled, true); + delete pRtcpEncNode; + delete pRearNode; +} + +TEST_F(RtcpEncoderNodeTests, TestSendNack) +{ + RtcpEncoderNode* pRtcpEncNode = new RtcpEncoderNode(); + pRtcpEncNode->SetMediaType(IMS_MEDIA_VIDEO); + + bool bRet = pRtcpEncNode->SendNack(nullptr); + EXPECT_EQ(bRet, false); + + NackParams param; + param.PID = 0; + param.BLP = 0; + param.nSecNackCnt = 0; + param.bNackReport = true; + + bRet = pRtcpEncNode->SendNack(¶m); + EXPECT_EQ(bRet, false); + + VideoConfig videoConfig; + setupVideoConfig(videoConfig); + videoConfig.setRtcpFbType(VideoConfig::RTP_FB_NACK); + pRtcpEncNode->SetConfig(&videoConfig); + EXPECT_EQ(pRtcpEncNode->Start(), RESULT_SUCCESS); + bRet = pRtcpEncNode->SendNack(¶m); + EXPECT_EQ(bRet, true); + pRtcpEncNode->Stop(); + delete pRtcpEncNode; +} + +TEST_F(RtcpEncoderNodeTests, TestSendPictureLost) +{ + RtcpEncoderNode* pRtcpEncNode = new RtcpEncoderNode(); + pRtcpEncNode->SetMediaType(IMS_MEDIA_VIDEO); + VideoConfig videoConfig; + setupVideoConfig(videoConfig); + videoConfig.setRtcpFbType(VideoConfig::PSFB_PLI); + pRtcpEncNode->SetConfig(&videoConfig); + EXPECT_EQ(pRtcpEncNode->Start(), RESULT_SUCCESS); + + bool bRet = pRtcpEncNode->SendPictureLost(kPsfbPli); + EXPECT_EQ(bRet, true); + + videoConfig.setRtcpFbType(VideoConfig::PSFB_FIR); + pRtcpEncNode->SetConfig(&videoConfig); + bRet = pRtcpEncNode->SendPictureLost(kPsfbFir); + EXPECT_EQ(bRet, true); + pRtcpEncNode->Stop(); + delete pRtcpEncNode; +} + +TEST_F(RtcpEncoderNodeTests, TestSendTmmbrn) +{ + RtcpEncoderNode* pRtcpEncNode = new RtcpEncoderNode(); + pRtcpEncNode->SetMediaType(IMS_MEDIA_VIDEO); + VideoConfig videoConfig; + setupVideoConfig(videoConfig); + videoConfig.setRtcpFbType(VideoConfig::RTP_FB_TMMBR); + pRtcpEncNode->SetConfig(&videoConfig); + EXPECT_EQ(pRtcpEncNode->Start(), RESULT_SUCCESS); + + TmmbrParams tmmbr; + tmmbr.ssrc = 0x1111; + tmmbr.exp = 0x2222; + tmmbr.mantissa = 0x3333; + tmmbr.overhead = 0x4444; + bool bRet = pRtcpEncNode->SendTmmbrn(kRtpFbTmmbr, &tmmbr); + EXPECT_EQ(bRet, true); + + videoConfig.setRtcpFbType(VideoConfig::RTP_FB_TMMBR); + pRtcpEncNode->SetConfig(&videoConfig); + bRet = pRtcpEncNode->SendTmmbrn(kRtpFbTmmbr, &tmmbr); + EXPECT_EQ(bRet, true); + pRtcpEncNode->Stop(); + delete pRtcpEncNode; +} + +TEST_F(RtcpEncoderNodeTests, SendRtcpXr) +{ + RtcpEncoderNode* pRtcpEncNode = new RtcpEncoderNode(); + pRtcpEncNode->SetMediaType(IMS_MEDIA_VIDEO); + EXPECT_EQ(pRtcpEncNode->Start(), RESULT_SUCCESS); + + bool bRet = pRtcpEncNode->SendRtcpXr(nullptr, 0); + EXPECT_EQ(bRet, false); + + uint8_t* pDummyRtcpXrPacket = new uint8_t[10]; + bRet = pRtcpEncNode->SendRtcpXr(pDummyRtcpXrPacket, 10); + EXPECT_EQ(bRet, true); + pRtcpEncNode->Stop(); + delete pRtcpEncNode; +} +} // namespace
\ No newline at end of file diff --git a/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/TextStreamGraphRtcpTest.cpp b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/TextStreamGraphRtcpTest.cpp index 4436e429..a292a5f5 100644 --- a/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/TextStreamGraphRtcpTest.cpp +++ b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/TextStreamGraphRtcpTest.cpp @@ -23,7 +23,7 @@ using namespace android::telephony::imsmedia; // RtpConfig -const int32_t kMediaDirection = RtpConfig::MEDIA_DIRECTION_NO_FLOW; +const int32_t kMediaDirection = RtpConfig::MEDIA_DIRECTION_INACTIVE; const android::String8 kRemoteAddress("127.0.0.1"); const int32_t kRemotePort = 10000; const int8_t kDscp = 0; @@ -130,13 +130,11 @@ TEST_F(TextStreamGraphRtcpTest, TestRtcpStreamAndUpdate) EXPECT_EQ(graph->update(&config), RESULT_SUCCESS); EXPECT_EQ(graph->getState(), kStreamStateRunning); - rtcp.setIntervalSec(0); - config.setRtcpConfig(rtcp); + config.setMediaDirection(RtpConfig::MEDIA_DIRECTION_NO_FLOW); EXPECT_EQ(graph->update(&config), RESULT_SUCCESS); EXPECT_EQ(graph->getState(), kStreamStateCreated); - rtcp.setIntervalSec(5); - config.setRtcpConfig(rtcp); + config.setMediaDirection(RtpConfig::MEDIA_DIRECTION_INACTIVE); EXPECT_EQ(graph->update(&config), RESULT_SUCCESS); EXPECT_EQ(graph->getState(), kStreamStateRunning); diff --git a/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/nodes/TextSourceNodeTest.cpp b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/nodes/TextSourceNodeTest.cpp index d4ae8902..69a17eb4 100644 --- a/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/nodes/TextSourceNodeTest.cpp +++ b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/nodes/TextSourceNodeTest.cpp @@ -15,17 +15,14 @@ */ #include <gtest/gtest.h> -#include <gmock/gmock.h> #include <TextConfig.h> #include <TextSourceNode.h> #include <ImsMediaCondition.h> -#include <MockBaseNode.h> +#include <string.h> using namespace android::telephony::imsmedia; using namespace android; -using ::testing::Return; - // RtpConfig const int32_t kMediaDirection = RtpConfig::MEDIA_DIRECTION_SEND_ONLY; const String8 kRemoteAddress("127.0.0.1"); @@ -48,41 +45,40 @@ const int8_t kRedundantPayload = 102; const int8_t kRedundantLevel = 3; const bool kKeepRedundantLevel = true; const int kTextInterval = 300; -const char* kBom = {"\xEF\xBB\xBF\0"}; +const uint8_t kBom[] = {0xEF, 0xBB, 0xBF}; -class FakeBaseNode : public BaseNode +class FakeTextNode : public BaseNode { public: - FakeBaseNode() { mEmptyFlag = false; } - virtual ~FakeBaseNode() {} + FakeTextNode() + { + mEmptyFlag = false; + memset(mData, 0, sizeof(mData)); + } + virtual ~FakeTextNode() {} virtual ImsMediaResult Start() { return RESULT_SUCCESS; } virtual void Stop() {} virtual bool IsRunTime() { return true; } virtual bool IsSourceNode() { return false; } virtual void SetConfig(void* config) { (void)config; } - virtual void OnDataFromFrontNode(ImsMediaSubType subtype, uint8_t* data, uint32_t size, - uint32_t timestamp, bool mark, uint32_t seq, ImsMediaSubType dataType, - uint32_t arrivalTime) + virtual void OnDataFromFrontNode(ImsMediaSubType /*subtype*/, uint8_t* data, uint32_t size, + uint32_t /*timestamp*/, bool /*mark*/, uint32_t /*seq*/, ImsMediaSubType /*dataType*/, + uint32_t /*arrivalTime*/) { - (void)subtype; - (void)timestamp; - (void)mark; - (void)seq; - (void)dataType; - (void)arrivalTime; - if (size != 0 && size <= MAX_RTT_LEN) { memset(mData, 0, sizeof(mData)); memcpy(mData, data, size); mEmptyFlag = false; } - else if (size == 0 && data == nullptr) + else if (data == nullptr) { mEmptyFlag = true; } } + virtual kBaseNodeState GetState() { return kNodeStateRunning; } + uint8_t* getData() { return mData; } bool getEmptyFlag() { return mEmptyFlag; } @@ -94,7 +90,11 @@ private: class TextSourceNodeTest : public ::testing::Test { public: - TextSourceNodeTest() {} + TextSourceNodeTest() + { + mNode = NULL; + mFakeNode = NULL; + } virtual ~TextSourceNodeTest() {} protected: @@ -102,8 +102,7 @@ protected: RtcpConfig mRtcp; ImsMediaCondition mCondition; TextSourceNode* mNode; - MockBaseNode* mMockNode; - FakeBaseNode mFakeNode; + FakeTextNode* mFakeNode; std::list<BaseNode*> mNodes; virtual void SetUp() override @@ -132,18 +131,12 @@ protected: mNode->SetConfig(&mConfig); mNodes.push_back(mNode); - mMockNode = new MockBaseNode(); - mMockNode->SetDelegate(&mFakeNode); - mMockNode->DelegateToFake(); - - mMockNode->SetMediaType(IMS_MEDIA_TEXT); - mMockNode->SetConfig(&mConfig); - mNodes.push_back(mMockNode); - - mNode->ConnectRearNode(mMockNode); - - ON_CALL(*mMockNode, GetState()).WillByDefault(Return(kNodeStateRunning)); - ON_CALL(*mMockNode, IsRunTime()).WillByDefault(Return(true)); + mFakeNode = new FakeTextNode(); + mFakeNode->SetMediaType(IMS_MEDIA_TEXT); + mFakeNode->SetConfig(&mConfig); + mNodes.push_back(mFakeNode); + mNode->ConnectRearNode(mFakeNode); + mCondition.reset(); } virtual void TearDown() override @@ -165,26 +158,46 @@ TEST_F(TextSourceNodeTest, startFail) EXPECT_EQ(mNode->Start(), RESULT_INVALID_PARAM); } +TEST_F(TextSourceNodeTest, sendRttDisableBom) +{ + mConfig.setKeepRedundantLevel(false); + mNode->SetConfig(&mConfig); + + EXPECT_EQ(mNode->Start(), RESULT_SUCCESS); + EXPECT_FALSE(mFakeNode->getEmptyFlag()); + + String8 testText1 = String8("a"); + mNode->SendRtt(&testText1); + + mNode->ProcessData(); + EXPECT_EQ(memcmp(mFakeNode->getData(), testText1.string(), testText1.length()), 0); + + mCondition.wait_timeout(kTextInterval); + mNode->ProcessData(); + // expect empty flag set + EXPECT_TRUE(mFakeNode->getEmptyFlag()); +} + TEST_F(TextSourceNodeTest, sendRttTestChunkSizeOne) { String8 testText1 = String8("a"); EXPECT_EQ(mNode->Start(), RESULT_SUCCESS); - EXPECT_FALSE(mFakeNode.getEmptyFlag()); + EXPECT_FALSE(mFakeNode->getEmptyFlag()); mNode->SendRtt(&testText1); mNode->ProcessData(); // expect BOM - EXPECT_EQ(String8((char*)mFakeNode.getData()), String8(kBom)); + EXPECT_EQ(memcmp(mFakeNode->getData(), kBom, sizeof(kBom)), 0); mCondition.wait_timeout(kTextInterval); mNode->ProcessData(); - EXPECT_EQ(String8((char*)mFakeNode.getData()), testText1); + EXPECT_EQ(memcmp(mFakeNode->getData(), testText1.string(), testText1.length()), 0); mCondition.wait_timeout(kTextInterval); mNode->ProcessData(); // expect empty flag set - EXPECT_TRUE(mFakeNode.getEmptyFlag()); + EXPECT_TRUE(mFakeNode->getEmptyFlag()); } TEST_F(TextSourceNodeTest, sendRttTestChunkSizeTwo) @@ -192,21 +205,21 @@ TEST_F(TextSourceNodeTest, sendRttTestChunkSizeTwo) String8 testText2 = String8("\xC2\xA9"); EXPECT_EQ(mNode->Start(), RESULT_SUCCESS); - EXPECT_FALSE(mFakeNode.getEmptyFlag()); + EXPECT_FALSE(mFakeNode->getEmptyFlag()); mNode->SendRtt(&testText2); mNode->ProcessData(); // expect BOM - EXPECT_EQ(String8((char*)mFakeNode.getData()), String8(kBom)); + EXPECT_EQ(memcmp(mFakeNode->getData(), kBom, sizeof(kBom)), 0); mCondition.wait_timeout(kTextInterval); mNode->ProcessData(); - EXPECT_EQ(String8((char*)mFakeNode.getData()), testText2); + EXPECT_EQ(memcmp(mFakeNode->getData(), testText2.string(), testText2.length()), 0); mCondition.wait_timeout(kTextInterval); mNode->ProcessData(); // expect empty flag set - EXPECT_TRUE(mFakeNode.getEmptyFlag()); + EXPECT_TRUE(mFakeNode->getEmptyFlag()); } TEST_F(TextSourceNodeTest, sendRttTestChunkSizeThree) @@ -214,21 +227,21 @@ TEST_F(TextSourceNodeTest, sendRttTestChunkSizeThree) String8 testText3 = String8("\xE2\x9C\x82"); EXPECT_EQ(mNode->Start(), RESULT_SUCCESS); - EXPECT_FALSE(mFakeNode.getEmptyFlag()); + EXPECT_FALSE(mFakeNode->getEmptyFlag()); mNode->SendRtt(&testText3); mNode->ProcessData(); // expect BOM - EXPECT_EQ(String8((char*)mFakeNode.getData()), String8(kBom)); + EXPECT_EQ(memcmp(mFakeNode->getData(), kBom, sizeof(kBom)), 0); mCondition.wait_timeout(kTextInterval); mNode->ProcessData(); - EXPECT_EQ(String8((char*)mFakeNode.getData()), testText3); + EXPECT_EQ(memcmp(mFakeNode->getData(), testText3.string(), testText3.length()), 0); mCondition.wait_timeout(kTextInterval); mNode->ProcessData(); // expect empty flag set - EXPECT_TRUE(mFakeNode.getEmptyFlag()); + EXPECT_TRUE(mFakeNode->getEmptyFlag()); } TEST_F(TextSourceNodeTest, sendRttTestChunkSizeFour) @@ -236,21 +249,21 @@ TEST_F(TextSourceNodeTest, sendRttTestChunkSizeFour) String8 testText4 = String8("\xF0\x9F\x9A\x80"); EXPECT_EQ(mNode->Start(), RESULT_SUCCESS); - EXPECT_FALSE(mFakeNode.getEmptyFlag()); + EXPECT_FALSE(mFakeNode->getEmptyFlag()); mNode->SendRtt(&testText4); mNode->ProcessData(); // expect BOM - EXPECT_EQ(String8((char*)mFakeNode.getData()), String8(kBom)); + EXPECT_EQ(memcmp(mFakeNode->getData(), kBom, sizeof(kBom)), 0); mCondition.wait_timeout(kTextInterval); mNode->ProcessData(); - EXPECT_EQ(String8((char*)mFakeNode.getData()), testText4); + EXPECT_EQ(memcmp(mFakeNode->getData(), testText4.string(), testText4.length()), 0); mCondition.wait_timeout(kTextInterval); mNode->ProcessData(); // expect empty flag set - EXPECT_TRUE(mFakeNode.getEmptyFlag()); + EXPECT_TRUE(mFakeNode->getEmptyFlag()); } TEST_F(TextSourceNodeTest, sendRttTestLongString) @@ -267,31 +280,19 @@ TEST_F(TextSourceNodeTest, sendRttTestLongString) testText5.append(testText4); EXPECT_EQ(mNode->Start(), RESULT_SUCCESS); - EXPECT_FALSE(mFakeNode.getEmptyFlag()); + EXPECT_FALSE(mFakeNode->getEmptyFlag()); mNode->SendRtt(&testText5); mNode->ProcessData(); // expect BOM - EXPECT_EQ(String8((char*)mFakeNode.getData()), String8(kBom)); - - mCondition.wait_timeout(kTextInterval); - mNode->ProcessData(); - EXPECT_EQ(String8((char*)mFakeNode.getData()), testText1); - - mCondition.wait_timeout(kTextInterval); - mNode->ProcessData(); - EXPECT_EQ(String8((char*)mFakeNode.getData()), testText2); - - mCondition.wait_timeout(kTextInterval); - mNode->ProcessData(); - EXPECT_EQ(String8((char*)mFakeNode.getData()), testText3); + EXPECT_EQ(memcmp(mFakeNode->getData(), kBom, sizeof(kBom)), 0); mCondition.wait_timeout(kTextInterval); mNode->ProcessData(); - EXPECT_EQ(String8((char*)mFakeNode.getData()), testText4); + EXPECT_EQ(memcmp(mFakeNode->getData(), testText5.string(), testText5.length()), 0); mCondition.wait_timeout(kTextInterval); mNode->ProcessData(); // expect empty flag set - EXPECT_TRUE(mFakeNode.getEmptyFlag()); + EXPECT_TRUE(mFakeNode->getEmptyFlag()); } diff --git a/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/utils/ImsMediaBitReaderTest.cpp b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/utils/ImsMediaBitReaderTest.cpp new file mode 100644 index 00000000..da66bcf8 --- /dev/null +++ b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/utils/ImsMediaBitReaderTest.cpp @@ -0,0 +1,75 @@ +/** + * 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 <gtest/gtest.h> +#include <ImsMediaBitReader.h> +#include <string.h> + +class ImsMediaBitReaderTest : public ::testing::Test +{ +public: +protected: + virtual void SetUp() override {} + + virtual void TearDown() override {} +}; + +TEST_F(ImsMediaBitReaderTest, SetBufferAndReadBitTest) +{ + uint8_t testBuffer[] = {1, 2, 4, 8, 16, 32, 64, 128}; + + ImsMediaBitReader reader; + EXPECT_EQ(reader.Read(24), 0); + reader.SetBuffer(testBuffer, sizeof(testBuffer)); + EXPECT_EQ(reader.Read(32), 0); + + for (int32_t i = 0; i < sizeof(testBuffer); i++) + { + EXPECT_EQ(reader.Read(8), testBuffer[i]); + } + + EXPECT_EQ(reader.Read(8), 0); +} + +TEST_F(ImsMediaBitReaderTest, SetBufferAndReadByteTest) +{ + uint8_t testBuffer[] = {1, 2, 4, 8, 16, 32, 64, 128}; + + ImsMediaBitReader reader; + reader.SetBuffer(testBuffer, sizeof(testBuffer)); + + uint8_t dstBuffer[8] = {0}; + + for (int32_t i = 0; i < sizeof(testBuffer); i++) + { + reader.ReadByteBuffer(dstBuffer + i, 8); + } + + EXPECT_EQ(memcmp(dstBuffer, testBuffer, sizeof(testBuffer)), 0); +} + +TEST_F(ImsMediaBitReaderTest, SetBufferAndReadUEModeTest) +{ + uint8_t testBuffer[] = {0xDA}; // 11011010 + + ImsMediaBitReader reader; + reader.SetBuffer(testBuffer, sizeof(testBuffer)); + + EXPECT_EQ(reader.ReadByUEMode(), 0); + EXPECT_EQ(reader.ReadByUEMode(), 0); + EXPECT_EQ(reader.ReadByUEMode(), 2); + EXPECT_EQ(reader.ReadByUEMode(), 1); +} diff --git a/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/utils/ImsMediaBitWriterTest.cpp b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/utils/ImsMediaBitWriterTest.cpp new file mode 100644 index 00000000..af10eaa2 --- /dev/null +++ b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/utils/ImsMediaBitWriterTest.cpp @@ -0,0 +1,77 @@ +/** + * 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 <gtest/gtest.h> +#include <ImsMediaBitWriter.h> +#include <string.h> + +class ImsMediaBitWriterTest : public ::testing::Test +{ +public: +protected: + virtual void SetUp() override {} + + virtual void TearDown() override {} +}; + +TEST_F(ImsMediaBitWriterTest, SetBufferAndWriteBitTest) +{ + uint8_t testBuffer[] = {1, 2, 4, 8, 16, 32, 64, 128}; + uint8_t dstBuffer[8] = {0}; + + ImsMediaBitWriter writer; + + EXPECT_EQ(writer.Write(0, 24), false); + writer.SetBuffer(dstBuffer, sizeof(dstBuffer)); + EXPECT_EQ(writer.Write(0, 32), false); + + for (int32_t i = 0; i < sizeof(testBuffer); i++) + { + EXPECT_EQ(writer.Write(testBuffer[i], 8), true); + } + + EXPECT_EQ(writer.Write(0, 8), false); + EXPECT_EQ(memcmp(dstBuffer, testBuffer, sizeof(testBuffer)), 0); +} + +TEST_F(ImsMediaBitWriterTest, SetBufferAndWriteByteTest) +{ + uint8_t testBuffer[] = {1, 2, 4, 8, 16, 32, 64, 128}; + uint8_t dstBuffer[8] = {0}; + + ImsMediaBitWriter writer; + writer.SetBuffer(dstBuffer, sizeof(dstBuffer)); + + for (int32_t i = 0; i < sizeof(testBuffer); i++) + { + EXPECT_EQ(writer.WriteByteBuffer(testBuffer + i, 8), true); + } + + EXPECT_EQ(memcmp(dstBuffer, testBuffer, sizeof(testBuffer)), 0); +} + +TEST_F(ImsMediaBitWriterTest, SetBufferAndSeekToWriteTest) +{ + uint8_t testBuffer[] = {1, 2, 4, 8, 16, 32, 64, 128}; + uint8_t dstBuffer[8] = {1, 2, 4, 8}; + + ImsMediaBitWriter writer; + writer.SetBuffer(dstBuffer, sizeof(dstBuffer)); + writer.Seek(32); + writer.WriteByteBuffer(testBuffer + 4, 32); + + EXPECT_EQ(memcmp(dstBuffer, testBuffer, sizeof(testBuffer)), 0); +} diff --git a/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/utils/ImsMediaImageRotateTest.cpp b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/utils/ImsMediaImageRotateTest.cpp index 60dc69fb..a5cbd157 100644 --- a/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/utils/ImsMediaImageRotateTest.cpp +++ b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/utils/ImsMediaImageRotateTest.cpp @@ -72,6 +72,76 @@ TEST_F(ImsMediaImageRotateTest, Rotate90FlipTest_ZeroImageSize) EXPECT_EQ(memcmp(output_img, exp_img, 0), 0); } +TEST_F(ImsMediaImageRotateTest, Rotate90Test) +{ + const uint16_t img_width = 4, img_height = 4; + const uint32_t img_buf_size = img_width * img_height * 1.5f; + + // Input image Y buffer + uint8_t input_img_y[] = {0, 1, 2, 3, 10, 11, 12, 13, 20, 21, 22, 23, 30, 31, 32, 33}; + + // Input image UV buffer + uint8_t input_img_uv[] = {80, 90, 81, 91, 82, 92, 83, 93}; + + // Expected output Y buffer + uint8_t exp_img[img_buf_size] = {30, 20, 10, 0, 31, 21, 11, 1, 32, 22, 12, 2, 33, 23, 13, 3, 82, + 92, 80, 90, 83, 93, 81, 91}; + + // Output image buffer to be verified + uint8_t output_img[img_buf_size] = {0}; + + ImsMediaImageRotate::YUV420_SP_Rotate90( + output_img, img_buf_size, img_height, input_img_y, input_img_uv, img_width, img_height); + + EXPECT_EQ(memcmp(output_img, exp_img, img_buf_size), 0); +} + +TEST_F(ImsMediaImageRotateTest, Rotate90WithOutputStrideTest) +{ + const uint16_t img_width = 4, img_height = 4, outimg_stride = 6; + const uint32_t img_buf_size = outimg_stride * img_width * 1.5f; + + // Input image Y buffer + uint8_t input_img_y[] = {0, 1, 2, 3, 10, 11, 12, 13, 20, 21, 22, 23, 30, 31, 32, 33}; + + // Input image UV buffer + uint8_t input_img_uv[] = {80, 90, 81, 91, 82, 92, 83, 93}; + + // Expected output Y buffer + uint8_t exp_img[img_buf_size] = {30, 20, 10, 0, 0, 0, 31, 21, 11, 1, 0, 0, 32, 22, 12, 2, 0, 0, + 33, 23, 13, 3, 0, 0, 82, 92, 80, 90, 0, 0, 83, 93, 81, 91, 0, 0}; + + // Output image buffer to be verified + uint8_t output_img[img_buf_size] = {0}; + + ImsMediaImageRotate::YUV420_SP_Rotate90(output_img, img_buf_size, outimg_stride, input_img_y, + input_img_uv, img_width, img_height); + + EXPECT_EQ(memcmp(output_img, exp_img, img_buf_size), 0); +} + +TEST_F(ImsMediaImageRotateTest, Rotate90Flip_ZeroImageSize) +{ + const uint16_t img_width = 0, img_height = 0; + + // Input image Y buffer + uint8_t input_img_y[0] = {}; + + // Input image UV buffer + uint8_t input_img_uv[0] = {}; + + // Expected output Y buffer + uint8_t exp_img[0] = {}; + + // Output image buffer to be verified + uint8_t output_img[0] = {}; + + ImsMediaImageRotate::YUV420_SP_Rotate90( + output_img, 0, img_height, input_img_y, input_img_uv, img_width, img_height); + + EXPECT_EQ(memcmp(output_img, exp_img, 0), 0); +} + TEST_F(ImsMediaImageRotateTest, Rotate270Test) { const uint16_t img_width = 4, img_height = 4; @@ -91,7 +161,31 @@ TEST_F(ImsMediaImageRotateTest, Rotate270Test) uint8_t output_img[img_buf_size] = {0}; ImsMediaImageRotate::YUV420_SP_Rotate270( - output_img, input_img_y, input_img_uv, img_width, img_height); + output_img, img_buf_size, img_height, input_img_y, input_img_uv, img_width, img_height); + + EXPECT_EQ(memcmp(output_img, exp_img, img_buf_size), 0); +} + +TEST_F(ImsMediaImageRotateTest, Rotate270WithOutStrideTest) +{ + const uint16_t img_width = 4, img_height = 4, outimg_stride = 6; + const uint32_t img_buf_size = outimg_stride * img_width * 1.5f; + + // Input image Y buffer + uint8_t input_img_y[] = {0, 1, 2, 3, 10, 11, 12, 13, 20, 21, 22, 23, 30, 31, 32, 33}; + + // Input image UV buffer + uint8_t input_img_uv[] = {80, 90, 81, 91, 82, 92, 83, 93}; + + // Expected output Y buffer + uint8_t exp_img[img_buf_size] = {3, 13, 23, 33, 0, 0, 2, 12, 22, 32, 0, 0, 1, 11, 21, 31, 0, 0, + 0, 10, 20, 30, 0, 0, 81, 91, 83, 93, 0, 0, 80, 90, 82, 92, 0, 0}; + + // Output image buffer to be verified + uint8_t output_img[img_buf_size] = {0}; + + ImsMediaImageRotate::YUV420_SP_Rotate270(output_img, img_buf_size, outimg_stride, input_img_y, + input_img_uv, img_width, img_height); EXPECT_EQ(memcmp(output_img, exp_img, img_buf_size), 0); } @@ -113,7 +207,7 @@ TEST_F(ImsMediaImageRotateTest, Rotate270Test_ZeroImageSize) uint8_t output_img[0] = {}; ImsMediaImageRotate::YUV420_SP_Rotate270( - output_img, input_img_y, input_img_uv, img_width, img_height); + output_img, 0, img_height, input_img_y, input_img_uv, img_width, img_height); EXPECT_EQ(memcmp(output_img, exp_img, 0), 0); } diff --git a/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/VideoStreamGraphRtcpTest.cpp b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/VideoStreamGraphRtcpTest.cpp index 327e42fc..0806fe7d 100644 --- a/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/VideoStreamGraphRtcpTest.cpp +++ b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/VideoStreamGraphRtcpTest.cpp @@ -24,7 +24,7 @@ using namespace android::telephony::imsmedia; // RtpConfig -const int32_t kMediaDirection = RtpConfig::MEDIA_DIRECTION_SEND_RECEIVE; +const int32_t kMediaDirection = RtpConfig::MEDIA_DIRECTION_INACTIVE; const android::String8 kRemoteAddress("127.0.0.1"); const int32_t kRemotePort = 10000; const int32_t kMtu = 1300; @@ -163,6 +163,14 @@ TEST_F(VideoStreamGraphRtcpTest, TestRtcpStreamAndUpdate) EXPECT_EQ(graph->update(&config), RESULT_SUCCESS); EXPECT_EQ(graph->getState(), kStreamStateRunning); + config.setMediaDirection(RtpConfig::MEDIA_DIRECTION_NO_FLOW); + EXPECT_EQ(graph->update(&config), RESULT_SUCCESS); + EXPECT_EQ(graph->getState(), kStreamStateCreated); + + config.setMediaDirection(RtpConfig::MEDIA_DIRECTION_INACTIVE); + EXPECT_EQ(graph->update(&config), RESULT_SUCCESS); + EXPECT_EQ(graph->getState(), kStreamStateRunning); + EXPECT_EQ(graph->stop(), RESULT_SUCCESS); EXPECT_EQ(graph->getState(), kStreamStateCreated); } diff --git a/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/VideoStreamGraphRtpTxTest.cpp b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/VideoStreamGraphRtpTxTest.cpp index 332041c4..20f368ba 100644 --- a/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/VideoStreamGraphRtpTxTest.cpp +++ b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/VideoStreamGraphRtpTxTest.cpp @@ -123,6 +123,12 @@ protected: 1, &previewReader), AMEDIA_OK); AImageReader_getWindow(previewReader, &previewSurface); + + /* + * TODO: Below line will skip all test under this class, need to remove to include it in + * atest + */ + GTEST_SKIP(); } virtual void TearDown() override diff --git a/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/include/core/audio/MockAudioManager.h b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/include/core/audio/MockAudioManager.h new file mode 100644 index 00000000..4a46634b --- /dev/null +++ b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/include/core/audio/MockAudioManager.h @@ -0,0 +1,39 @@ +/** + * 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 MOCK_AUDIO_MANAGER_H +#define MOCK_AUDIO_MANAGER_H + +#include <AudioManager.h> +#include <ImsMediaDefine.h> +#include <gmock/gmock.h> + +class MockAudioManager : public AudioManager +{ +public: + MockAudioManager() { sManager = this; } + virtual ~MockAudioManager() { sManager = nullptr; } + MOCK_METHOD(ImsMediaResult, deleteConfig, (int sessionId, AudioConfig* config), (override)); + MOCK_METHOD(void, sendDtmf, (int sessionId, char dtmfDigit, int duration), (override)); + MOCK_METHOD(void, sendRtpHeaderExtension, + (int sessionId, std::list<RtpHeaderExtension>* listExtension), (override)); + MOCK_METHOD(void, setMediaQualityThreshold, (int sessionId, MediaQualityThreshold* threshold), + (override)); + MOCK_METHOD(void, SendInternalEvent, + (uint32_t event, uint64_t sessionId, uint64_t paramA, uint64_t paramB), (override)); +}; + +#endif
\ No newline at end of file diff --git a/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/protocol/rtp/core/RtcpPacketTest.cpp b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/protocol/rtp/core/RtcpPacketTest.cpp index 37185728..1c95e03b 100644 --- a/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/protocol/rtp/core/RtcpPacketTest.cpp +++ b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/protocol/rtp/core/RtcpPacketTest.cpp @@ -671,6 +671,39 @@ TEST_F(RtcpPacketTest, DecodeOnlyRtcpSRHeader) EXPECT_EQ(pRtcpHeader.getLength(), 0 * RTP_WORD_SIZE); } +/** + * Test RTCP XR packet. + */ +TEST_F(RtcpPacketTest, TestDecodeRtcpXrPacket) +{ + RtcpPacket rtcpPacket; + + /* + * Real-time Transport Control Protocol (Sender Report) + * 10.. .... = Version: RFC 1889 Version (2) + * ..1. .... = Padding: False + * ...0 0001 = Report count: 1 + * Packet type: XR (207) + * Length: 5 (24 bytes) + * SSRC : 0xb1c8cb02 (2982726402) + * 0x00, 0x00, 0x00, 0x01, // XR block type: VoIP Metrics Report Block (207) + * 0x00, 0x0A, // Length of the XR block in 32-bit words: 10 + * 0x02, 0x01, // Loss rate (packets lost per million packets sent): 2 bytes; + * Type-specific: 1 0x00, 0x64, // Loss rate: 100 0x03, 0x01, // Delay + * since last report (milliseconds): 2 bytes; Type-specific: 1 0x00, 0x3C, // Delay: + * 60 milliseconds + */ + uint8_t bufPacket[] = {0xa1, 0xcf, 0x00, 0x05, 0xb1, 0xc8, 0xcb, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x0A, 0x02, 0x01, 0x00, 0x64, 0x03, 0x01, 0x00, 0x3C, 0x00, 0x02}; + + RtcpConfigInfo rtcpConfigInfo; + RtpBuffer rtpBuffer(24, bufPacket); + eRTP_STATUS_CODE res = rtcpPacket.decodeRtcpPacket(&rtpBuffer, 0, &rtcpConfigInfo); + EXPECT_EQ(res, RTP_SUCCESS); + + // TODO: After Rtcp-Xr decoder function is implemented, add checks for each files in XR report. +} + TEST_F(RtcpPacketTest, CheckAllGetSets) { RtcpPacket rtcpPacket; diff --git a/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/protocol/rtp/core/RtcpXrPacketTest.cpp b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/protocol/rtp/core/RtcpXrPacketTest.cpp index 1c9a1cb8..45624c71 100644 --- a/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/protocol/rtp/core/RtcpXrPacketTest.cpp +++ b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/protocol/rtp/core/RtcpXrPacketTest.cpp @@ -47,8 +47,8 @@ TEST(RtcpXrPacketTest, TestDecodeXrPacket) 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0xc8, 0x53, 0x81, 0xca, 0x00, 0x0a}; eRTP_STATUS_CODE res = objRtcpXrPacket.decodeRtcpXrPacket(reinterpret_cast<RtpDt_UChar*>(bufXrPacket), 24, 0); - // Result should be failure because decode XR packet is not supported or implemented. - EXPECT_EQ(res, RTP_FAILURE); + + EXPECT_EQ(res, RTP_SUCCESS); } TEST(RtcpXrPacketTest, TestFormXrPacket) diff --git a/tests/unit/src/com/android/telephony/imsmedia/AudioListenerTest.java b/tests/unit/src/com/android/telephony/imsmedia/AudioListenerTest.java index 353b6f7c..5dace993 100644 --- a/tests/unit/src/com/android/telephony/imsmedia/AudioListenerTest.java +++ b/tests/unit/src/com/android/telephony/imsmedia/AudioListenerTest.java @@ -22,6 +22,7 @@ import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import android.os.Looper; import android.os.Parcel; import android.os.RemoteException; import android.telephony.CallQuality; @@ -30,21 +31,22 @@ import android.telephony.imsmedia.AudioConfig; import android.telephony.imsmedia.IImsAudioSessionCallback; import android.telephony.imsmedia.ImsMediaSession; import android.telephony.imsmedia.MediaQualityStatus; +import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -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 { +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class AudioListenerTest extends ImsMediaTest { private static final int SESSION_ID = 1; private static final char DTMF_DIGIT = '7'; private static final int DTMF_DURATION = 120; @@ -58,30 +60,23 @@ public class AudioListenerTest { @Mock private IImsAudioSessionCallback mMockIImsAudioSessionCallback; private AudioConfig mAudioConfig; - private TestableLooper mLooper; @Before public void setUp() { MockitoAnnotations.initMocks(this); AudioSession audioSession = new AudioSession(SESSION_ID, mMockIImsAudioSessionCallback, - mAudioService, mMockAudioLocalSession, null); + mAudioService, mMockAudioLocalSession, null, Looper.myLooper()); AudioSession.AudioSessionHandler handler = audioSession.getAudioSessionHandler(); mAudioListener = new AudioListener(handler); mAudioListener.setMediaCallback(mMockCallback); mAudioConfig = AudioConfigTest.createAudioConfig(); - try { - mLooper = new TestableLooper(handler.getLooper()); - } catch (Exception e) { - throw new AssertionError("Unable to create TestableLooper", e); - } + mTestClass = AudioListenerTest.this; + super.setUp(); } @After public void tearDown() throws Exception { - if (mLooper != null) { - mLooper.destroy(); - mLooper = null; - } + super.tearDown(); } private Parcel createParcel(int event, int result, AudioConfig config) { @@ -238,10 +233,4 @@ public class AudioListenerTest { doNothing().when(mMockCallback).onSessionClosed(eq(SESSION_ID)); verify(mMockCallback, times(1)).onSessionClosed(eq(SESSION_ID)); } - - private void processAllMessages() { - while (!mLooper.getLooper().getQueue().isIdle()) { - mLooper.processAllMessages(); - } - } } diff --git a/tests/unit/src/com/android/telephony/imsmedia/AudioOffloadTest.java b/tests/unit/src/com/android/telephony/imsmedia/AudioOffloadTest.java index 5eb92175..c3013544 100644 --- a/tests/unit/src/com/android/telephony/imsmedia/AudioOffloadTest.java +++ b/tests/unit/src/com/android/telephony/imsmedia/AudioOffloadTest.java @@ -30,6 +30,7 @@ import android.hardware.radio.ims.media.IImsMedia; import android.hardware.radio.ims.media.IImsMediaSession; import android.hardware.radio.ims.media.RtpConfig; import android.hardware.radio.ims.media.RtpError; +import android.os.Looper; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.telephony.CallQuality; @@ -39,6 +40,7 @@ import android.telephony.imsmedia.IImsAudioSessionCallback; import android.telephony.imsmedia.ImsMediaSession; import android.telephony.imsmedia.MediaQualityStatus; import android.telephony.imsmedia.MediaQualityThreshold; +import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import com.android.telephony.imsmedia.AudioSession; @@ -49,7 +51,6 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -60,8 +61,9 @@ import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; -@RunWith(JUnit4.class) -public class AudioOffloadTest { +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class AudioOffloadTest extends ImsMediaTest { private static final int SESSION_ID = 1; private static final int DTMF_DURATION = 120; private static final int NO_RESOURCES = ImsMediaSession.RESULT_NO_RESOURCES; @@ -73,7 +75,6 @@ public class AudioOffloadTest { private AudioSession audioSession; private AudioOffloadListener offloadListener; private AudioSession.AudioSessionHandler handler; - private TestableLooper looper; @Mock private IImsAudioSessionCallback callback; @Mock @@ -88,24 +89,19 @@ public class AudioOffloadTest { MockitoAnnotations.initMocks(this); offloadService = spy(AudioOffloadService.getInstance()); doReturn(imsMedia).when(offloadService).getIImsMedia(); - audioSession = new AudioSession(SESSION_ID, callback, null, null, offloadService); + audioSession = new AudioSession(SESSION_ID, callback, null, null, offloadService, + Looper.myLooper()); handler = audioSession.getAudioSessionHandler(); audioSession.setAudioOffload(true); offloadListener = audioSession.getOffloadListener(); audioSession.onOpenSessionSuccess(imsMediaSession); - try { - looper = new TestableLooper(handler.getLooper()); - } catch (Exception e) { - throw new AssertionError("Unable to create TestableLooper", e); - } + mTestClass = AudioOffloadTest.this; + super.setUp(); } @After public void tearDown() throws Exception { - if (looper != null) { - looper.destroy(); - looper = null; - } + super.tearDown(); } @Test @@ -263,7 +259,8 @@ public class AudioOffloadTest { @Test public void testSetMediaQualityThreshold() { // Set Media Quality Threshold - MediaQualityThreshold threshold = MediaQualityThresholdTest.createMediaQualityThreshold(); + MediaQualityThreshold threshold = + MediaQualityThresholdTest.createMediaQualityThresholdForHal(); audioSession.setMediaQualityThreshold(threshold); processAllMessages(); try { @@ -397,10 +394,4 @@ public class AudioOffloadTest { fail("Failed to notify onCallQualityChanged: " + e); } } - - private void processAllMessages() { - while (!looper.getLooper().getQueue().isIdle()) { - looper.processAllMessages(); - } - } } diff --git a/tests/unit/src/com/android/telephony/imsmedia/AudioSessionTest.java b/tests/unit/src/com/android/telephony/imsmedia/AudioSessionTest.java index 2c9bf484..e51a46fb 100644 --- a/tests/unit/src/com/android/telephony/imsmedia/AudioSessionTest.java +++ b/tests/unit/src/com/android/telephony/imsmedia/AudioSessionTest.java @@ -21,6 +21,7 @@ import static org.mockito.Mockito.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import android.os.Looper; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.RemoteException; @@ -31,6 +32,7 @@ import android.telephony.imsmedia.IImsAudioSessionCallback; import android.telephony.imsmedia.ImsMediaSession; import android.telephony.imsmedia.MediaQualityStatus; import android.telephony.imsmedia.MediaQualityThreshold; +import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import com.android.telephony.imsmedia.AudioService; @@ -42,7 +44,6 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -50,8 +51,9 @@ import java.net.DatagramSocket; import java.net.SocketException; import java.util.ArrayList; -@RunWith(JUnit4.class) -public class AudioSessionTest { +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class AudioSessionTest extends ImsMediaTest { private static final int SESSION_ID = 1; private static final int DTMF_DURATION = 140; private static final int UNUSED = -1; @@ -69,28 +71,21 @@ public class AudioSessionTest { private AudioLocalSession audioLocalSession; @Mock private IImsAudioSessionCallback callback; - private TestableLooper looper; @Before public void setUp() { MockitoAnnotations.initMocks(this); audioSession = new AudioSession(SESSION_ID, callback, - audioService, audioLocalSession, null); + audioService, audioLocalSession, null, Looper.myLooper()); audioListener = audioSession.getAudioListener(); handler = audioSession.getAudioSessionHandler(); - try { - looper = new TestableLooper(handler.getLooper()); - } catch (Exception e) { - throw new AssertionError("Unable to create TestableLooper", e); - } + mTestClass = AudioSessionTest.this; + super.setUp(); } @After public void tearDown() throws Exception { - if (looper != null) { - looper.destroy(); - looper = null; - } + super.tearDown(); } private Parcel createParcel(int message, int result, AudioConfig config) { @@ -366,10 +361,4 @@ public class AudioSessionTest { fail("Failed to notify onSessionClosed: " + e); } } - - private void processAllMessages() { - while (!looper.getLooper().getQueue().isIdle()) { - looper.processAllMessages(); - } - } } diff --git a/tests/unit/src/com/android/telephony/imsmedia/ImsMediaTest.java b/tests/unit/src/com/android/telephony/imsmedia/ImsMediaTest.java new file mode 100644 index 00000000..759e6f32 --- /dev/null +++ b/tests/unit/src/com/android/telephony/imsmedia/ImsMediaTest.java @@ -0,0 +1,88 @@ +/* + * 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. + */ + +package com.android.telephony.imsmedia; + +import static org.junit.Assert.fail; + +import android.testing.TestableLooper; + +import org.junit.After; +import org.junit.Before; + +import java.util.ArrayList; +import java.util.List; + +public class ImsMediaTest { + protected List<TestableLooper> mTestableLoopers = new ArrayList<>(); + protected TestableLooper mTestableLooper; + protected Object mTestClass; + + @Before + public void setUp() { + mTestableLooper = TestableLooper.get(mTestClass); + if (mTestableLooper != null) { + monitorTestableLooper(mTestableLooper); + } + } + + @After + public void tearDown() throws Exception { + if (!mTestableLoopers.isEmpty()) { + for (TestableLooper looper : mTestableLoopers) { + looper.getLooper().quit(); + } + } + // Unmonitor TestableLooper for ImsMediaTest class + if (mTestableLooper != null) { + unmonitorTestableLooper(mTestableLooper); + } + // Destroy all newly created TestableLoopers so they can be reused + for (TestableLooper looper : mTestableLoopers) { + looper.destroy(); + } + TestableLooper.remove(mTestClass); + + } + + private void monitorTestableLooper(TestableLooper looper) { + if (!mTestableLoopers.contains(looper)) { + mTestableLoopers.add(looper); + } + } + + private void unmonitorTestableLooper(TestableLooper looper) { + if (mTestableLoopers.contains(looper)) { + mTestableLoopers.remove(looper); + } + } + + private boolean areAllTestableLoopersIdle() { + for (TestableLooper looper : mTestableLoopers) { + if (!looper.getLooper().getQueue().isIdle()) return false; + } + return true; + } + + public void processAllMessages() { + if (mTestableLoopers.isEmpty()) { + fail("mTestableLoopers is empty. Please make sure to add @RunWithLooper annotation"); + } + while (!areAllTestableLoopersIdle()) { + for (TestableLooper looper : mTestableLoopers) looper.processAllMessages(); + } + } +} diff --git a/tests/unit/src/com/android/telephony/imsmedia/MediaQualityThresholdTest.java b/tests/unit/src/com/android/telephony/imsmedia/MediaQualityThresholdTest.java index 40dfe866..d61e0f33 100644 --- a/tests/unit/src/com/android/telephony/imsmedia/MediaQualityThresholdTest.java +++ b/tests/unit/src/com/android/telephony/imsmedia/MediaQualityThresholdTest.java @@ -37,6 +37,7 @@ public class MediaQualityThresholdTest { private static final int[] PACKET_LOSS_RATE = { 1, 3 }; private static final int[] JITTER_THRESHOLD = { 100, 200 }; private static final boolean NOTIFY_STATUS = false; + private static final int VIDEO_BITRATE_BPS = 100000; @Test public void testConstructorAndGetters() { @@ -48,6 +49,7 @@ public class MediaQualityThresholdTest { assertThat(Arrays.equals(threshold.getRtpPacketLossRate(), PACKET_LOSS_RATE)).isTrue(); assertThat(Arrays.equals(threshold.getRtpJitterMillis(), JITTER_THRESHOLD)).isTrue(); assertThat(threshold.getNotifyCurrentStatus()).isEqualTo(NOTIFY_STATUS); + assertThat(threshold.getVideoBitrateBps()).isEqualTo(VIDEO_BITRATE_BPS); } @Test @@ -82,6 +84,7 @@ public class MediaQualityThresholdTest { .setRtpPacketLossRate(PACKET_LOSS_RATE) .setRtpJitterMillis(JITTER_THRESHOLD) .setNotifyCurrentStatus(NOTIFY_STATUS) + .setVideoBitrateBps(VIDEO_BITRATE_BPS) .build(); assertThat(threshold1).isNotEqualTo(threshold2); @@ -94,6 +97,7 @@ public class MediaQualityThresholdTest { .setRtpPacketLossRate(PACKET_LOSS_RATE) .setRtpJitterMillis(JITTER_THRESHOLD) .setNotifyCurrentStatus(NOTIFY_STATUS) + .setVideoBitrateBps(VIDEO_BITRATE_BPS) .build(); assertThat(threshold1).isNotEqualTo(threshold3); @@ -106,6 +110,7 @@ public class MediaQualityThresholdTest { .setRtpPacketLossRate(PACKET_LOSS_RATE) .setRtpJitterMillis(JITTER_THRESHOLD) .setNotifyCurrentStatus(NOTIFY_STATUS) + .setVideoBitrateBps(VIDEO_BITRATE_BPS) .build(); assertThat(threshold1).isNotEqualTo(threshold4); @@ -119,6 +124,7 @@ public class MediaQualityThresholdTest { .setRtpPacketLossRate(PACKET_LOSS_RATE) .setRtpJitterMillis(JITTER_THRESHOLD) .setNotifyCurrentStatus(NOTIFY_STATUS) + .setVideoBitrateBps(VIDEO_BITRATE_BPS) .build(); assertThat(threshold1).isNotEqualTo(threshold5); @@ -133,6 +139,20 @@ public class MediaQualityThresholdTest { .setRtpPacketLossRate(PACKET_LOSS_RATE) .setRtpJitterMillis(JITTER_THRESHOLD) .setNotifyCurrentStatus(NOTIFY_STATUS) + .setVideoBitrateBps(VIDEO_BITRATE_BPS) + .build(); + } + + static MediaQualityThreshold createMediaQualityThresholdForHal() { + return new MediaQualityThreshold.Builder() + .setRtpInactivityTimerMillis(RTP_TIMEOUT) + .setRtcpInactivityTimerMillis(RTCP_TIMEOUT) + .setRtpHysteresisTimeInMillis(RTP_HYSTERESIS_TIME) + .setRtpPacketLossDurationMillis(RTP_PACKET_LOSS_DURATION) + .setRtpPacketLossRate(PACKET_LOSS_RATE) + .setRtpJitterMillis(JITTER_THRESHOLD) + .setNotifyCurrentStatus(NOTIFY_STATUS) + .setVideoBitrateBps(0) .build(); } } diff --git a/tests/unit/src/com/android/telephony/imsmedia/TextListenerTest.java b/tests/unit/src/com/android/telephony/imsmedia/TextListenerTest.java index ce2520c1..a7f016f5 100644 --- a/tests/unit/src/com/android/telephony/imsmedia/TextListenerTest.java +++ b/tests/unit/src/com/android/telephony/imsmedia/TextListenerTest.java @@ -22,23 +22,25 @@ import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import android.os.Looper; import android.os.Parcel; import android.os.RemoteException; import android.telephony.imsmedia.IImsTextSessionCallback; import android.telephony.imsmedia.ImsMediaSession; import android.telephony.imsmedia.TextConfig; +import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -@RunWith(JUnit4.class) -public class TextListenerTest { +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class TextListenerTest extends ImsMediaTest { private static final int SESSION_ID = 1; private static final String TEXT_STREAM = "Hello"; private static final long NATIVE_OBJECT = 1234L; @@ -52,31 +54,24 @@ public class TextListenerTest { @Mock private IImsTextSessionCallback mMockIImsTextSessionCallback; private TextConfig mTextConfig; - private TestableLooper mLooper; @Before public void setUp() { MockitoAnnotations.initMocks(this); TextSession textSession = new TextSession(SESSION_ID, mMockIImsTextSessionCallback, - mTextService, mMockTextLocalSession); + mTextService, mMockTextLocalSession, Looper.myLooper()); TextSession.TextSessionHandler handler = textSession.getTextSessionHandler(); mTextListener = new TextListener(handler); mTextListener.setMediaCallback(mMockCallback); mTextListener.setNativeObject(NATIVE_OBJECT); mTextConfig = TextConfigTest.createTextConfig(); - try { - mLooper = new TestableLooper(handler.getLooper()); - } catch (Exception e) { - throw new AssertionError("Unable to create TestableLooper", e); - } + mTestClass = TextListenerTest.this; + super.setUp(); } @After - public void tearDown() { - if (mLooper != null) { - mLooper.destroy(); - mLooper = null; - } + public void tearDown() throws Exception { + super.tearDown(); } private Parcel createParcel(int event, int result, TextConfig config) { @@ -166,10 +161,4 @@ public class TextListenerTest { parcel.recycle(); verify(mMockCallback, times(1)).onSessionClosed(eq(SESSION_ID)); } - - private void processAllMessages() { - while (!mLooper.getLooper().getQueue().isIdle()) { - mLooper.processAllMessages(); - } - } } diff --git a/tests/unit/src/com/android/telephony/imsmedia/TextSessionTest.java b/tests/unit/src/com/android/telephony/imsmedia/TextSessionTest.java index 7319711e..0c150026 100644 --- a/tests/unit/src/com/android/telephony/imsmedia/TextSessionTest.java +++ b/tests/unit/src/com/android/telephony/imsmedia/TextSessionTest.java @@ -21,6 +21,7 @@ import static org.mockito.Mockito.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import android.os.Looper; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.RemoteException; @@ -28,6 +29,7 @@ import android.telephony.imsmedia.IImsTextSessionCallback; import android.telephony.imsmedia.ImsMediaSession; import android.telephony.imsmedia.MediaQualityThreshold; import android.telephony.imsmedia.TextConfig; +import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import com.android.telephony.imsmedia.Utils.OpenSessionParams; @@ -36,15 +38,15 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.net.DatagramSocket; import java.net.SocketException; -@RunWith(JUnit4.class) -public class TextSessionTest { +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class TextSessionTest extends ImsMediaTest { private static final int SESSION_ID = 1; private static final int SUCCESS = ImsMediaSession.RESULT_SUCCESS; private static final int NO_RESOURCES = ImsMediaSession.RESULT_NO_RESOURCES; @@ -60,27 +62,21 @@ public class TextSessionTest { private TextLocalSession mTextLocalSession; @Mock private IImsTextSessionCallback mCallback; - private TestableLooper mLooper; @Before public void setUp() { MockitoAnnotations.initMocks(this); - mTextSession = new TextSession(SESSION_ID, mCallback, mTextService, mTextLocalSession); + mTextSession = new TextSession(SESSION_ID, mCallback, mTextService, mTextLocalSession, + Looper.myLooper()); mTextListener = mTextSession.getTextListener(); mHandler = mTextSession.getTextSessionHandler(); - try { - mLooper = new TestableLooper(mHandler.getLooper()); - } catch (Exception e) { - throw new AssertionError("Unable to create TestableLooper", e); - } + mTestClass = TextSessionTest.this; + super.setUp(); } @After public void tearDown() throws Exception { - if (mLooper != null) { - mLooper.destroy(); - mLooper = null; - } + super.tearDown(); } private Parcel createParcel(int message, int result, TextConfig config) { @@ -241,10 +237,4 @@ public class TextSessionTest { fail("Failed to notify onSessionClosed: " + e); } } - - private void processAllMessages() { - while (!mLooper.getLooper().getQueue().isIdle()) { - mLooper.processAllMessages(); - } - } } diff --git a/tests/unit/src/com/android/telephony/imsmedia/VideoListenerTest.java b/tests/unit/src/com/android/telephony/imsmedia/VideoListenerTest.java index ad90f957..d00ccb1b 100644 --- a/tests/unit/src/com/android/telephony/imsmedia/VideoListenerTest.java +++ b/tests/unit/src/com/android/telephony/imsmedia/VideoListenerTest.java @@ -22,23 +22,25 @@ import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import android.os.Looper; import android.os.Parcel; import android.os.RemoteException; import android.telephony.imsmedia.IImsVideoSessionCallback; import android.telephony.imsmedia.ImsMediaSession; import android.telephony.imsmedia.VideoConfig; +import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -@RunWith(JUnit4.class) -public class VideoListenerTest { +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class VideoListenerTest extends ImsMediaTest { private static final int SESSION_ID = 1; private static final long VIDEO_DATA = 1024; private static final int RESOLUTION_WIDTH = 640; @@ -54,31 +56,24 @@ public class VideoListenerTest { @Mock private IImsVideoSessionCallback mMockIImsVideoSessionCallback; private VideoConfig mVideoConfig; - private TestableLooper mLooper; @Before public void setUp() { MockitoAnnotations.initMocks(this); VideoSession videoSession = new VideoSession(SESSION_ID, mMockIImsVideoSessionCallback, - mVideoService, mMockVideoLocalSession); + mVideoService, mMockVideoLocalSession, Looper.myLooper()); VideoSession.VideoSessionHandler handler = videoSession.getVideoSessionHandler(); mVideoListener = new VideoListener(handler); mVideoListener.setMediaCallback(mMockCallback); mVideoListener.setNativeObject(NATIVE_OBJECT); mVideoConfig = VideoConfigTest.createVideoConfig(); - try { - mLooper = new TestableLooper(handler.getLooper()); - } catch (Exception e) { - throw new AssertionError("Unable to create TestableLooper", e); - } + mTestClass = VideoListenerTest.this; + super.setUp(); } @After - public void tearDown() { - if (mLooper != null) { - mLooper.destroy(); - mLooper = null; - } + public void tearDown() throws Exception { + super.tearDown(); } private Parcel createParcel(int event, int result) { @@ -170,14 +165,14 @@ public class VideoListenerTest { } @Test - public void testEventPacketLossInd() throws RemoteException { - Parcel parcel = createParcel(VideoSession.EVENT_PACKET_LOSS_IND, + public void testEventNotifyBitrateInd() throws RemoteException { + Parcel parcel = createParcel(VideoSession.EVENT_NOTIFY_BITRATE_IND, ImsMediaSession.PACKET_TYPE_RTCP); mVideoListener.onMessage(parcel); processAllMessages(); parcel.recycle(); verify(mMockIImsVideoSessionCallback, - times(1)).notifyPacketLoss(eq(ImsMediaSession.PACKET_TYPE_RTCP)); + times(1)).notifyBitrate(eq(ImsMediaSession.PACKET_TYPE_RTCP)); } @Test @@ -201,10 +196,4 @@ public class VideoListenerTest { parcel.recycle(); verify(mMockCallback, times(1)).onSessionClosed(eq(SESSION_ID)); } - - private void processAllMessages() { - while (!mLooper.getLooper().getQueue().isIdle()) { - mLooper.processAllMessages(); - } - } } diff --git a/tests/unit/src/com/android/telephony/imsmedia/VideoSessionTest.java b/tests/unit/src/com/android/telephony/imsmedia/VideoSessionTest.java index 3f781886..d9e4b897 100644 --- a/tests/unit/src/com/android/telephony/imsmedia/VideoSessionTest.java +++ b/tests/unit/src/com/android/telephony/imsmedia/VideoSessionTest.java @@ -23,6 +23,7 @@ import static org.mockito.Mockito.verify; import android.graphics.ImageFormat; import android.media.ImageReader; +import android.os.Looper; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.RemoteException; @@ -31,6 +32,7 @@ import android.telephony.imsmedia.IImsVideoSessionCallback; import android.telephony.imsmedia.ImsMediaSession; import android.telephony.imsmedia.MediaQualityThreshold; import android.telephony.imsmedia.VideoConfig; +import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.Surface; @@ -40,7 +42,6 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -48,8 +49,9 @@ import java.net.DatagramSocket; import java.net.SocketException; import java.util.ArrayList; -@RunWith(JUnit4.class) -public class VideoSessionTest { +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class VideoSessionTest extends ImsMediaTest { private static final int SESSION_ID = 1; private static final int UNUSED = -1; private static final int SUCCESS = ImsMediaSession.RESULT_SUCCESS; @@ -79,22 +81,16 @@ public class VideoSessionTest { public void setUp() { MockitoAnnotations.initMocks(this); mVideoSession = new VideoSession(SESSION_ID, mCallback, - mVideoService, mVideoLocalSession); + mVideoService, mVideoLocalSession, Looper.myLooper()); mVideoListener = mVideoSession.getVideoListener(); mHandler = mVideoSession.getVideoSessionHandler(); - try { - mLooper = new TestableLooper(mHandler.getLooper()); - } catch (Exception e) { - throw new AssertionError("Unable to create TestableLooper", e); - } + mTestClass = VideoSessionTest.this; + super.setUp(); } @After public void tearDown() throws Exception { - if (mLooper != null) { - mLooper.destroy(); - mLooper = null; - } + super.tearDown(); } private Parcel createParcel(int message, int result, VideoConfig config) { @@ -289,12 +285,12 @@ public class VideoSessionTest { @Test public void testPacketLossInd() { // Receive Packet Loss - Utils.sendMessage(mHandler, VideoSession.EVENT_PACKET_LOSS_IND, PACKET_LOSS, UNUSED); + Utils.sendMessage(mHandler, VideoSession.EVENT_NOTIFY_BITRATE_IND, PACKET_LOSS, UNUSED); processAllMessages(); try { - verify(mCallback, times(1)).notifyPacketLoss(eq(PACKET_LOSS)); + verify(mCallback, times(1)).notifyBitrate(eq(PACKET_LOSS)); } catch (RemoteException e) { - fail("Failed to notify notifyPacketLoss: " + e); + fail("Failed to notify notifyBitrate: " + e); } } @@ -330,10 +326,4 @@ public class VideoSessionTest { fail("Failed to notify onSessionClosed: " + e); } } - - private void processAllMessages() { - while (!mLooper.getLooper().getQueue().isIdle()) { - mLooper.processAllMessages(); - } - } } |