blob: 0ab5134fdda56d56e686a3db37fc8f0bdefe3389 [file] [log] [blame]
/**
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <AudioJitterBuffer.h>
#include <ImsMediaDataQueue.h>
#include <ImsMediaTimer.h>
#include <ImsMediaTrace.h>
#if 1
#define AUDIO_JITTER_BUFFER_SIZE 5
#define AUDIO_G711_JITTER_BUFFER_SIZE 6
#define AUDIO_JITTER_BUFFER_MIN_SIZE 3
#define AUDIO_JITTER_BUFFER_MAX_SIZE 9
#define AUDIO_JITTER_BUFFER_START_SIZE 4
#else
#define AUDIO_JITTER_BUFFER_SIZE 3
#define AUDIO_G711_JITTER_BUFFER_SIZE 4
#endif
#if defined(FEATURE_IMS_MEDIA_ATT) || defined(FEATURE_IMS_MEDIA_CMCC)
#define JITTERBUFFER_UPDATE_DURATION 9
#else
#define JITTERBUFFER_UPDATE_DURATION 0
#endif
#define CODECFILTER_AUDIO_FMC_MAX_SEQUENCE 0xffff
#define CODECFILTER_AUDIO_PACKETRECEIVE_COUNTER_MAX 50
#define CODECFILTER_AUDIO_PACKETINSERT_COUNTER_MAX 5
#define CODECFILTER_AUDIO_SKIP_READCOUNTER 100
#define GET_SEQ_GAP(a, b) ((uint16_t)a - (uint16_t)b)
AudioJitterBuffer::AudioJitterBuffer()
{
mInitJitterBufferSize = AUDIO_JITTER_BUFFER_START_SIZE;
mMinJitterBufferSize = AUDIO_JITTER_BUFFER_MIN_SIZE;
mMaxJitterBufferSize = AUDIO_JITTER_BUFFER_MAX_SIZE;
AudioJitterBuffer::Reset();
mBufferIgnoreSIDPacket = false;
mBufferImprovement = false;
}
AudioJitterBuffer::~AudioJitterBuffer() {}
void AudioJitterBuffer::Reset()
{
mNextJitterBufferSize = mCurrJitterBufferSize;
mLastPlayedSeqNum = 0;
mLastPlayedTimestamp = 0;
mIsReceivedFirst = false;
mDtxOn = false;
mSIDCount = 0;
mWaiting = false;
mDeleteCount = 0;
mBaseTimestamp = 0;
mBaseArrivalTime = 0;
mUpdateJitterBufferSize = 0;
mCheckUpdateJitterPacketCnt = 0;
mFourceToUpdateJitterBuffer = false;
mNeedToUpdateBasePacket = false;
mCanNotGetCount = 0;
mNullDataCount = 0;
BaseJitterBuffer::Reset();
mJitterAnalyser.Reset();
mJitterAnalyser.SetMinMaxJitterBufferSize(mMinJitterBufferSize, mMaxJitterBufferSize);
mCurrPlayingSeq = 0;
mBufferUpdateDuration = 0;
}
void AudioJitterBuffer::SetJitterBufferSize(uint32_t nInit, uint32_t nMin, uint32_t nMax)
{
IMLOGD3("[SetJitterBufferSize] %02x, %02x, %02x", nInit, nMin, nMax);
if (nMin > 0)
{
mMinJitterBufferSize = nMin;
}
if (nMax > 0)
{
mMaxJitterBufferSize = nMax;
}
if (nInit > 0)
{
if (nInit < mMinJitterBufferSize)
{
nInit = mMinJitterBufferSize;
}
if (nInit > mMaxJitterBufferSize)
{
nInit = mMaxJitterBufferSize;
}
mInitJitterBufferSize = nInit;
mCurrJitterBufferSize = mInitJitterBufferSize;
mNextJitterBufferSize = mInitJitterBufferSize;
}
mJitterAnalyser.SetMinMaxJitterBufferSize(mMinJitterBufferSize, mMaxJitterBufferSize);
}
void AudioJitterBuffer::SetJitterOptions(
uint32_t nReduceTH, uint32_t nStepSize, double zValue, bool bIgnoreSID, bool bImprovement)
{
// do it later
mBufferIgnoreSIDPacket = bIgnoreSID;
mBufferImprovement = bImprovement;
mJitterAnalyser.SetJitterOptions(nReduceTH, nStepSize, zValue, bImprovement);
}
void AudioJitterBuffer::Add(ImsMediaSubType subtype, uint8_t* pbBuffer, uint32_t nBufferSize,
uint32_t nTimestamp, bool bMark, uint32_t nSeqNum, ImsMediaSubType nDataType,
uint32_t arrivalTime)
{
(void)nDataType;
DataEntry currEntry;
memset(&currEntry, 0, sizeof(DataEntry));
currEntry.subtype = subtype;
currEntry.pbBuffer = pbBuffer;
currEntry.nBufferSize = nBufferSize;
currEntry.nTimestamp = nTimestamp;
currEntry.bMark = bMark;
currEntry.nSeqNum = nSeqNum;
currEntry.bHeader = true;
currEntry.bValid = true;
currEntry.arrivalTime = arrivalTime;
// non-voice issue when min buffer size is 10 (VT case)
if (mBufferImprovement == false && mCanNotGetCount > 500)
{
IMLOGD0("[Add] Refreshed");
Reset();
}
else if (mBufferImprovement)
{
if ((mCanNotGetCount > mMaxJitterBufferSize) && (mCanNotGetCount > mMinJitterBufferSize))
{
IMLOGD0("[Add] Refreshed");
CollectRxRtpStatus(nSeqNum, kRtpStatusDiscarded);
Reset();
}
}
int32_t jitter = 0;
if (mBufferIgnoreSIDPacket == false)
{
jitter = mJitterAnalyser.calculateTransitTimeDifference(nTimestamp, arrivalTime);
mBaseTimestamp = currEntry.nTimestamp;
mBaseArrivalTime = currEntry.arrivalTime;
mJitterAnalyser.BasePacketChange(mBaseTimestamp, mBaseArrivalTime);
}
// TODO: remove mBufferIgnoreSIDPacket logic and the statements
else if ((mBufferIgnoreSIDPacket == true) && !IsSID(currEntry.pbBuffer, currEntry.nBufferSize))
{
// first packet delay compensation
if ((mBaseTimestamp == 0 && mBaseArrivalTime == 0) || (mNeedToUpdateBasePacket == true))
{
mBaseTimestamp = currEntry.nTimestamp;
mBaseArrivalTime = currEntry.arrivalTime;
mJitterAnalyser.BasePacketChange(mBaseTimestamp, mBaseArrivalTime);
mNeedToUpdateBasePacket = false;
}
else if (mBaseTimestamp > currEntry.nTimestamp || mBaseArrivalTime > currEntry.arrivalTime)
{
// rounding case (more consider case)
mBaseTimestamp = currEntry.nTimestamp;
mBaseArrivalTime = currEntry.arrivalTime;
mJitterAnalyser.BasePacketChange(mBaseTimestamp, mBaseArrivalTime);
}
else
{
// update case
if (currEntry.nTimestamp - mBaseTimestamp > currEntry.arrivalTime - mBaseArrivalTime)
{
mBaseTimestamp = currEntry.nTimestamp;
mBaseArrivalTime = currEntry.arrivalTime;
mJitterAnalyser.BasePacketChange(mBaseTimestamp, mBaseArrivalTime);
}
else
{ // compensation case
uint32_t temp = currEntry.arrivalTime;
IMLOGD_PACKET2(IM_PACKET_LOG_JITTER, "Before compensation[%u], nSeqNum[%d]", temp,
currEntry.nSeqNum);
currEntry.arrivalTime = mBaseArrivalTime + (currEntry.nTimestamp - mBaseTimestamp);
IMLOGD_PACKET2(IM_PACKET_LOG_JITTER, "After compensation[%u], delay[%u]",
currEntry.arrivalTime, temp - currEntry.arrivalTime);
}
}
jitter = mJitterAnalyser.calculateTransitTimeDifference(nTimestamp, arrivalTime);
}
RtpPacket* packet = new RtpPacket();
if (nBufferSize == 0)
{
packet->rtpDataType = kRtpDataTypeNoData;
}
else
{
IsSID(currEntry.pbBuffer, currEntry.nBufferSize) ? packet->rtpDataType = kRtpDataTypeSid
: packet->rtpDataType = kRtpDataTypeNormal;
}
packet->ssrc = mSsrc;
packet->seqNum = nSeqNum;
packet->jitter = jitter;
packet->delay = ImsMediaTimer::GetTimeInMilliSeconds();
mCallback->SendEvent(kCollectPacketInfo, kStreamRtpRx, reinterpret_cast<uint64_t>(packet));
if (nBufferSize == 0)
{
return;
}
std::lock_guard<std::mutex> guard(mMutex);
IMLOGD_PACKET7(IM_PACKET_LOG_JITTER,
"[Add] seq[%d], bMark[%d], TS[%d], size[%d] subtype[%d] queueSize[%d], arrivalTime[%u]",
nSeqNum, bMark, nTimestamp, nBufferSize, subtype, mDataQueue.GetCount() + 1,
arrivalTime);
if (mDataQueue.GetCount() == 0)
{ // jitter buffer is empty
mDataQueue.Add(&currEntry);
}
else
{
DataEntry* pEntry;
mDataQueue.GetLast(&pEntry);
if (pEntry == NULL)
{
return;
}
// current data is the latest data
if (USHORT_SEQ_ROUND_COMPARE(nSeqNum, pEntry->nSeqNum))
{
mDataQueue.Add(&currEntry);
}
else
{
// find the position of current data and insert current data to the correct position
bool bIsLateArrival = false;
mDataQueue.SetReadPosFirst();
for (int32_t i = 0; mDataQueue.GetNext(&pEntry); i++)
{
// late arrival packet
if (!USHORT_SEQ_ROUND_COMPARE(nSeqNum, pEntry->nSeqNum))
{
mDataQueue.InsertAt(i, &currEntry);
bIsLateArrival = true;
break;
}
}
}
}
// Count Duplicated SeqNum packets not to delete bundled packets (for maxptime 240 support)
/*
uint32_t nMaxBundledCount = 0;
uint32_t nTempBundledSeqNum = 0;
uint32_t nTempBundledCount = 0;
DataEntry* pEntry;
mDataQueue.SetReadPosFirst();
while (mDataQueue.GetNext(&pEntry) && pEntry != NULL)
{
if (nTempBundledSeqNum != pEntry->nSeqNum)
{
nTempBundledSeqNum = pEntry->nSeqNum;
nTempBundledCount = 1;
}
else
{
nTempBundledCount++;
}
if (nMaxBundledCount < nTempBundledCount)
{
nMaxBundledCount = nTempBundledCount; // count maximum
}
}
IMLOGD_PACKET1(IM_PACKET_LOG_JITTER, "[Add] bundled seq count[%d]", nMaxBundledCount);
if (mBufferImprovement == false)
{
while (mDataQueue.GetCount() >
mMaxJitterBufferSize + mMinJitterBufferSize + nMaxBundledCount - 1)
{
IMLOGD_PACKET1(IM_PACKET_LOG_JITTER, "[Add] delete seq[%d]", pEntry->nSeqNum);
CollectRxRtpStatus(pEntry->nSeqNum, kRtpStatusLate);
mDataQueue.Delete();
mDeleteCount++;
}
}
else
{
if (mIsReceivedFirst == false)
{
while (mDataQueue.GetCount() > mCurrJitterBufferSize + nMaxBundledCount)
{
IMLOGD_PACKET7(IM_PACKET_LOG_JITTER,
"[Add] early received - seq[%d], bMark[%d], TS[%d], size[%d] "
"subtype[%d] queueSize[%d] arrivalTime[%u]",
nSeqNum, bMark, nTimestamp, nBufferSize, subtype, mDataQueue.GetCount() + 1,
currEntry.arrivalTime);
IMLOGD_PACKET1(IM_PACKET_LOG_JITTER, "[Add] delete seq[%d]", nSeqNum);
CollectRxRtpStatus(nSeqNum, kRtpStatusDiscarded);
mDataQueue.Delete();
mDeleteCount++;
}
}
else
{
while (mDataQueue.GetCount() >
mMaxJitterBufferSize + mMinJitterBufferSize + nMaxBundledCount - 1)
{
IMLOGD_PACKET1(IM_PACKET_LOG_JITTER, "[Add] delete seq[%d]", pEntry->nSeqNum);
CollectRxRtpStatus(pEntry->nSeqNum, kRtpStatusLate);
mDataQueue.Delete();
mDeleteCount++;
}
}
}*/
}
bool AudioJitterBuffer::Get(ImsMediaSubType* psubtype, uint8_t** ppData, uint32_t* pnDataSize,
uint32_t* pnTimestamp, bool* pbMark, uint32_t* pnSeqNum, uint32_t* pnChecker)
{
if (mNullDataCount < 4)
{
mNullDataCount++;
IMLOGD1("[Get] mNullDataCount is [%d].", mNullDataCount);
if (psubtype)
*psubtype = MEDIASUBTYPE_UNDEFINED;
if (ppData)
*ppData = NULL;
if (pnDataSize)
*pnDataSize = 0;
if (pnTimestamp)
*pnTimestamp = 0;
if (pbMark)
*pbMark = false;
if (pnSeqNum)
*pnSeqNum = 0;
if (pnChecker)
*pnChecker = 0;
return false;
}
std::lock_guard<std::mutex> guard(mMutex);
bool bWait = false;
DataEntry* pEntry = NULL;
bool bForceToPlay = false;
mUpdateJitterBufferSize++; // add code for no dtx
mCheckUpdateJitterPacketCnt++;
int32_t nTimediff = 0;
// determin bWait
if (mIsReceivedFirst == false)
{
bWait = true;
}
else if (mDtxOn && mDataQueue.GetCount() > 0)
{
if (mDataQueue.Get(&pEntry) && IsSID(pEntry->pbBuffer, pEntry->nBufferSize) == false)
{
IMLOGD_PACKET0(IM_PACKET_LOG_JITTER, "[Get] bWait = True");
bWait = true;
}
}
// set current jitter buffer size
// when dtx occur, calculate nextJitterBufferSize.
if (mBufferImprovement == false)
{
if ((mWaiting == false && bWait == true) || mUpdateJitterBufferSize > 2 * 50)
{
uint32_t nPrev_jitterbufferSize = mCurrJitterBufferSize;
mCurrJitterBufferSize = mJitterAnalyser.GetJitterBufferSize(mCurrJitterBufferSize);
if (mUpdateJitterBufferSize > 2 * 50)
{ // 2s
nTimediff = ((int32_t)mCurrJitterBufferSize - (int32_t)nPrev_jitterbufferSize) * 20;
IMLOGD1("[Get] mFourceToUpdateJitterBuffer set. nTimediff[%d]", nTimediff);
mFourceToUpdateJitterBuffer = true;
}
mUpdateJitterBufferSize = 0;
}
mWaiting = bWait;
}
else
{
if ((mWaiting == false && bWait == true) || mUpdateJitterBufferSize > 500)
{
// update mCurrJitterBufferSize
if (mCheckUpdateJitterPacketCnt > 50 * mBufferUpdateDuration)
{
mCurrJitterBufferSize = mJitterAnalyser.GetJitterBufferSize(mCurrJitterBufferSize);
mCheckUpdateJitterPacketCnt = 0;
}
if (mUpdateJitterBufferSize > 500)
{
IMLOGD_PACKET0(IM_PACKET_LOG_JITTER, "[Get] mFourceToUpdateJitterBuffer set");
mFourceToUpdateJitterBuffer = true;
}
mUpdateJitterBufferSize = 0;
}
mWaiting = bWait;
}
// Wait case
if (bWait)
{
if (mDataQueue.Get(&pEntry))
{
IMLOGD_PACKET5(IM_PACKET_LOG_JITTER,
"[Get] time[%u], arrivalTime[%u], nTS[%d], mBaseTimestamp[%u], "
"mBaseArrivalTime[%u]",
ImsMediaTimer::GetTimeInMilliSeconds(), pEntry->arrivalTime, pEntry->nTimestamp,
mBaseTimestamp, mBaseArrivalTime);
}
if (mDataQueue.GetCount() == 0 ||
((mDataQueue.Get(&pEntry) &&
(ImsMediaTimer::GetTimeInMilliSeconds() - pEntry->arrivalTime) <
((mCurrJitterBufferSize - 1) * 20 + 10)) &&
(mDataQueue.GetCount() <= mCurrJitterBufferSize)))
{
if (psubtype)
*psubtype = MEDIASUBTYPE_UNDEFINED;
if (ppData)
*ppData = NULL;
if (pnDataSize)
*pnDataSize = 0;
if (pnTimestamp)
*pnTimestamp = 0;
if (pbMark)
*pbMark = false;
if (pnSeqNum)
*pnSeqNum = 0;
if (pnChecker)
*pnChecker = 0;
if (mDataQueue.GetCount() > 0)
{
IMLOGD_PACKET5(IM_PACKET_LOG_JITTER,
"[Get] Wait - Seq[%u], CurrJBSize[%u], curr[%u], th[%u], QueueCount[%u]",
pEntry->nSeqNum, mCurrJitterBufferSize,
(uint32_t)(ImsMediaTimer::GetTimeInMilliSeconds() - pEntry->arrivalTime),
((mCurrJitterBufferSize - 1) * 20 + 10), mDataQueue.GetCount());
mCanNotGetCount++;
}
else
{
IMLOGD_PACKET0(IM_PACKET_LOG_JITTER, "[Get] Wait - empty");
}
return false;
}
// the first frame of voice term
mCurrPlayingTS = pEntry->nTimestamp;
mCurrPlayingSeq = pEntry->nSeqNum;
}
if (mDataQueue.Get(&pEntry) == true && pEntry->nTimestamp != mCurrPlayingTS &&
((mCurrPlayingTS - 10) < pEntry->nTimestamp) &&
(pEntry->nTimestamp < (mCurrPlayingTS + 10)))
{
IMLOGD_PACKET0(IM_PACKET_LOG_JITTER, "[Get] CurrPlaying nTimestamp Reset!");
mCurrPlayingTS = pEntry->nTimestamp;
mCurrPlayingSeq = pEntry->nSeqNum;
}
// delete very old data
while (mDataQueue.GetCount() > 0)
{
mDataQueue.Get(&pEntry);
if (pEntry == NULL)
{
return false;
}
if (mDeleteCount > 4 && mDataQueue.GetCount() < mCurrJitterBufferSize + 1)
{
IMLOGD0("[Get] Resync");
if (mBufferImprovement == false)
{
uint32_t nTempBuferSize = (mCurrJitterBufferSize + mMinJitterBufferSize) / 2;
if (mDataQueue.GetCount() > nTempBuferSize)
{
mCurrPlayingTS = pEntry->nTimestamp;
mCurrPlayingSeq = pEntry->nSeqNum;
}
else
{
mCurrPlayingTS =
pEntry->nTimestamp - (nTempBuferSize - mDataQueue.GetCount()) * 20;
mCurrPlayingSeq = 0;
}
}
else
{
uint32_t nTempBuferSize =
(mCurrJitterBufferSize + AUDIO_JITTER_BUFFER_MIN_SIZE) / 2;
if (mDataQueue.GetCount() >= nTempBuferSize)
{
mCurrPlayingTS = pEntry->nTimestamp;
mCurrPlayingSeq = pEntry->nSeqNum;
}
else
{
mCurrPlayingTS =
pEntry->nTimestamp - (nTempBuferSize - mDataQueue.GetCount()) * 20;
mCurrPlayingSeq = 0;
}
}
mNeedToUpdateBasePacket = true;
mDeleteCount = 0;
break;
}
if (USHORT_TS_ROUND_COMPARE(pEntry->nTimestamp, mCurrPlayingTS) == false)
{
if (IsSID(pEntry->pbBuffer, pEntry->nBufferSize))
{
mSIDCount++;
mDtxOn = true;
IMLOGD_PACKET0(IM_PACKET_LOG_JITTER, "[Get] Dtx On");
}
else
{
mSIDCount = 0;
// mDtxOn = false;
IMLOGD_PACKET0(IM_PACKET_LOG_JITTER, "[Get] Dtx Off");
}
mDeleteCount++;
if (mDtxOn == true)
{
CollectRxRtpStatus(pEntry->nSeqNum, kRtpStatusNormal);
CollectJitterBufferStatus(mCurrJitterBufferSize * 20, mMaxJitterBufferSize * 20);
}
else
{
CollectRxRtpStatus(pEntry->nSeqNum, kRtpStatusLate);
}
mDataQueue.Delete();
IMLOGD_PACKET0(IM_PACKET_LOG_JITTER, "[Get] delete nTimestamp < mCurrPlayingTS)");
}
else
{
uint32_t timediff = pEntry->nTimestamp - mCurrPlayingTS;
mDeleteCount = 0;
// timestamp compensation logic
if ((timediff > 0) && (timediff < 20))
{
IMLOGD2("[Get] Re sync2 - PacketTS[%d], PlayTS[%d]", pEntry->nTimestamp,
mCurrPlayingTS);
bForceToPlay = true;
}
break;
}
IMLOGD_PACKET4(IM_PACKET_LOG_JITTER, "[Get] D [ %d / %u / %u / %d ]", pEntry->nSeqNum,
pEntry->nTimestamp, mCurrPlayingTS, mDataQueue.GetCount());
}
// decrease jitter buffer
if (mDtxOn && mSIDCount > 4 && mDataQueue.GetCount() > mCurrJitterBufferSize)
{
mDataQueue.Get(&pEntry);
if (pEntry && IsSID(pEntry->pbBuffer, pEntry->nBufferSize))
{
IMLOGD_PACKET5(IM_PACKET_LOG_JITTER,
"[Get] Delete SID - seq[%d], bMark[%d], nTS[%d], curTS[%d], size[%d]",
pEntry->nSeqNum, pEntry->bMark, pEntry->nTimestamp, mCurrPlayingTS,
mDataQueue.GetCount());
mSIDCount++;
mDtxOn = true;
IMLOGD_PACKET0(IM_PACKET_LOG_JITTER, "[Get] Dtx On");
CollectRxRtpStatus(pEntry->nSeqNum, kRtpStatusNormal);
CollectJitterBufferStatus(mCurrJitterBufferSize * 20, mMaxJitterBufferSize * 20);
mDataQueue.Delete();
bForceToPlay = true;
}
}
// add condition in case of changing Seq# & TS
if ((mCanNotGetCount > 10) && (mDataQueue.Get(&pEntry) == true) &&
(pEntry->nTimestamp - mCurrPlayingTS > TS_ROUND_QUARD))
{
IMLOGD4("[Get] TS changing case [ %d / %u / %u / %d ]", pEntry->nSeqNum, pEntry->nTimestamp,
mCurrPlayingTS, mDataQueue.GetCount());
bForceToPlay = true;
}
if (mFourceToUpdateJitterBuffer == true)
{
// removing delete packet in min JitterBuffer size
if (mDataQueue.GetCount() > mCurrJitterBufferSize + 1)
{
mDataQueue.Get(&pEntry);
if (pEntry)
{
IMLOGD_PACKET5(IM_PACKET_LOG_JITTER,
"[Get] Delete Packets - seq[%d], bMark[%d], nTS[%d], curTS[%d], size[%d]",
pEntry->nSeqNum, pEntry->bMark, pEntry->nTimestamp, mCurrPlayingTS,
mDataQueue.GetCount());
if (IsSID(pEntry->pbBuffer, pEntry->nBufferSize))
{
mSIDCount++;
mDtxOn = true;
IMLOGD_PACKET0(IM_PACKET_LOG_JITTER, "[Get] Dtx On");
}
else
{
mSIDCount = 0;
mDtxOn = false;
IMLOGD_PACKET0(IM_PACKET_LOG_JITTER, "[Get] Dtx Off");
}
if (mDtxOn == true)
{
CollectRxRtpStatus(pEntry->nSeqNum, kRtpStatusNormal);
CollectJitterBufferStatus(
mCurrJitterBufferSize * 20, mMaxJitterBufferSize * 20);
}
else
{
CollectRxRtpStatus(pEntry->nSeqNum, kRtpStatusDiscarded);
}
mDataQueue.Delete();
bForceToPlay = true;
}
}
if (mBufferImprovement == false)
{
// increase case
if ((mDataQueue.GetCount() < 2) || (mDataQueue.GetCount() < mCurrJitterBufferSize))
{
IMLOGD3("[Get] increase queue size[%d], curr buffer[%d], nTimediff[%d]",
mDataQueue.GetCount(), mCurrJitterBufferSize, nTimediff);
if (nTimediff > 0)
{
mCurrPlayingTS -= nTimediff;
mCurrPlayingSeq -= nTimediff / 20;
IMLOGD1("[Get] increase queue size [%d]", nTimediff / 20);
}
}
// reset
mFourceToUpdateJitterBuffer = false;
mUpdateJitterBufferSize = 0;
}
else
{
mFourceToUpdateJitterBuffer = false;
mUpdateJitterBufferSize = 0;
if ((mDataQueue.GetCount() < 2) ||
(mDataQueue.GetCount() < mCurrJitterBufferSize - mMinJitterBufferSize))
{
IMLOGD_PACKET0(IM_PACKET_LOG_JITTER, "[Get] increase packet");
return false;
}
}
}
// get data and check the duplication
if (mDataQueue.Get(&pEntry) && pEntry->nSeqNum == mLastPlayedSeqNum)
{
IMLOGD_PACKET6(IM_PACKET_LOG_JITTER,
"[Get] duplicate - curTS[%d], seq[%d], Mark[%d], TS[%d], buffer[%d], size[%d]",
mCurrPlayingTS, pEntry->nSeqNum, pEntry->bMark, pEntry->nTimestamp,
pEntry->nBufferSize, mDataQueue.GetCount());
CollectRxRtpStatus(pEntry->nSeqNum, kRtpStatusDuplicated);
mDataQueue.Delete();
mDeleteCount++;
}
if (mDataQueue.Get(&pEntry) && pEntry != NULL &&
(pEntry->nTimestamp == mCurrPlayingTS || bForceToPlay ||
(pEntry->nTimestamp < TS_ROUND_QUARD && mCurrPlayingTS > 0xFFFF)))
{
if (psubtype)
*psubtype = pEntry->subtype;
if (ppData)
*ppData = pEntry->pbBuffer;
if (pnDataSize)
*pnDataSize = pEntry->nBufferSize;
if (pnTimestamp)
*pnTimestamp = pEntry->nTimestamp;
if (pbMark)
*pbMark = pEntry->bMark;
if (pnSeqNum)
*pnSeqNum = pEntry->nSeqNum;
mIsReceivedFirst = true;
if (IsSID(pEntry->pbBuffer, pEntry->nBufferSize))
{
mSIDCount++;
mDtxOn = true;
IMLOGD_PACKET0(IM_PACKET_LOG_JITTER, "[Get] Dtx On");
}
else
{
mSIDCount = 0;
mDtxOn = false;
IMLOGD_PACKET0(IM_PACKET_LOG_JITTER, "[Get] Dtx Off");
}
IMLOGD_PACKET7(IM_PACKET_LOG_JITTER,
"[Get] OK - dtx mode[%d], curTS[%d], seq[%d], Mark[%d], TS[%d], buffer[%d], "
"size[%d]",
mDtxOn, mCurrPlayingTS, pEntry->nSeqNum, pEntry->bMark, pEntry->nTimestamp,
pEntry->nBufferSize, mDataQueue.GetCount());
mCurrPlayingTS = pEntry->nTimestamp + 20;
mCurrPlayingSeq = pEntry->nSeqNum + 1;
if (mLastPlayedSeqNum > 0)
{
/** Report the loss gap if the loss gap is over 0 */
uint16_t lostGap = GET_SEQ_GAP(pEntry->nSeqNum, mLastPlayedSeqNum);
if (lostGap > 1)
{
uint16_t lostSeq = mLastPlayedSeqNum + 1;
SessionCallbackParameter* param =
new SessionCallbackParameter(kReportPacketLossGap, lostSeq, lostGap - 1);
mCallback->SendEvent(kCollectOptionalInfo, reinterpret_cast<uint64_t>(param), 0);
}
}
mLastPlayedSeqNum = pEntry->nSeqNum;
mCanNotGetCount = 0;
CollectRxRtpStatus(pEntry->nSeqNum, kRtpStatusNormal);
CollectJitterBufferStatus(mCurrJitterBufferSize * 20, mMaxJitterBufferSize * 20);
return true;
}
else
{
// check EVS redundancy
{
if ((mCodecType == kAudioCodecEvs) && (mDtxOn == false) && (mRedundancyOffSet > 0) &&
(mDataQueue.GetCount() > 0))
{
// check partial redundancy packet
bool ret = false;
ret = CheckPartialRedundancyFrame(
psubtype, ppData, pnDataSize, pnTimestamp, pbMark, pnSeqNum, pnChecker);
if (ret == true)
{
mCurrPlayingTS += 20;
mCurrPlayingSeq += 1;
return true;
}
}
}
if (mDtxOn == false)
{
mCanNotGetCount++;
}
if (psubtype)
*psubtype = MEDIASUBTYPE_UNDEFINED;
if (ppData)
*ppData = NULL;
if (pnDataSize)
*pnDataSize = 0;
if (pnTimestamp)
*pnTimestamp = 0;
if (pbMark)
*pbMark = false;
if (pnSeqNum)
*pnSeqNum = 0;
IMLOGD_PACKET2(IM_PACKET_LOG_JITTER, "[Get] fail - dtx mode[%d], curTS[%d]", mDtxOn,
mCurrPlayingTS);
mCurrPlayingTS += 20;
mCurrPlayingSeq += 1;
return false;
}
return false;
}
bool AudioJitterBuffer::CheckPartialRedundancyFrame(ImsMediaSubType* psubtype, uint8_t** ppData,
uint32_t* pnDataSize, uint32_t* pnTimestamp, bool* pbMark, uint32_t* pnSeqNum,
uint32_t* pnChecker)
{
if (mDataQueue.GetCount() == 0)
{
return false;
}
DataEntry* pEntry = NULL;
uint32_t nFindPartialRedundancyFrameSeq = mCurrPlayingSeq + mRedundancyOffSet;
bool nFindPartialFrame = false;
// Find redundancy frame from the DataQueue using CAM offset(mRedundancyOffSet)
for (int32_t i = 0; i < mDataQueue.GetCount(); i++)
{
mDataQueue.GetAt(i, &pEntry);
if ((pEntry != NULL) && (pEntry->nSeqNum == nFindPartialRedundancyFrameSeq))
{
if (psubtype)
*psubtype = pEntry->subtype;
if (ppData)
*ppData = pEntry->pbBuffer;
if (pnDataSize)
*pnDataSize = pEntry->nBufferSize;
if (pnTimestamp)
*pnTimestamp = pEntry->nTimestamp;
if (pbMark)
*pbMark = pEntry->bMark;
if (pnSeqNum)
*pnSeqNum = pEntry->nSeqNum;
nFindPartialFrame = true;
break;
}
}
if (pEntry == NULL)
{
return false;
}
// Check bitrate using dataSize (13.2kbps : data size -> 33 byte)
if ((nFindPartialFrame != true) || (pEntry->nBufferSize != 33))
{
IMLOGD1("[CheckPartialRedundancyFrame] not found or not adjust CAM -- PartialFrame[%d]",
nFindPartialFrame);
return false;
}
int16_t nPartialFrameOffset = 0;
int16_t nPartialFlag = 0;
// If PartialRedundancyFrame is useable, send ppData and set nChecker variable to 1, after that,
// return true. if not, return false.
if ((nPartialFlag == 1) && (mRedundancyOffSet == (uint32_t)nPartialFrameOffset))
{
IMLOGD2("[CheckPartialRedundancyFrame] adjust CAM -- redundancyOffSet[%d], adjust seq[%d]",
mRedundancyOffSet, pEntry->nSeqNum);
if (pnChecker)
{
*pnChecker = 1;
}
return true;
}
else
{
IMLOGD3("[CheckPartialRedundancyFrame] partial frame at seq[%d], flag[%d], offset[%d]",
pEntry->nSeqNum, nPartialFlag, nPartialFrameOffset);
return false;
}
return false;
}
bool AudioJitterBuffer::IsSID(uint8_t* pbBuffer, uint32_t nBufferSize)
{
// remove warning
(void)pbBuffer;
switch (mCodecType)
{
case kAudioCodecAmr:
case kAudioCodecAmrWb:
if (nBufferSize == 5)
return true;
else
return false;
case kAudioCodecEvs:
if ((nBufferSize == 6) || (nBufferSize == 5))
return true;
else
return false;
case kAudioCodecPcmu:
case kAudioCodecPcma:
return false;
default:
IMLOGE1("[IsSID] DTX detect method is not defined for[%u] codec", mCodecType);
return false;
}
}
void AudioJitterBuffer::CollectRxRtpStatus(int32_t seq, kRtpPacketStatus status)
{
IMLOGD_PACKET2(IM_PACKET_LOG_JITTER, "[CollectRxRtpStatus] seq[%d], status[%d]", seq, status);
if (mCallback != NULL)
{
mCallback->SendEvent(kCollectRxRtpStatus, seq, status);
}
}
void AudioJitterBuffer::CollectJitterBufferStatus(int32_t currSize, int32_t maxSize)
{
IMLOGD_PACKET2(IM_PACKET_LOG_JITTER, "[CollectJitterBufferStatus] currSize[%d], maxSize[%d]",
currSize, maxSize);
if (mCallback != NULL)
{
mCallback->SendEvent(kCollectJitterBufferSize, currSize, maxSize);
}
}