blob: 0f5a158ffcb407469e22581a09f7db3996d1284c [file]
/**
* 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 <IVideoRendererNode.h>
#include <ImsMediaVideoRenderer.h>
#include <ImsMediaTrace.h>
#include <ImsMediaTimer.h>
#include <ImsMediaBitReader.h>
#include <VideoConfig.h>
#include <ImsMediaVideoUtil.h>
#include <VideoJitterBuffer.h>
#include <string.h>
using namespace android::telephony::imsmedia;
#define DEFAULT_UNDEFINED (-1)
IVideoRendererNode::IVideoRendererNode(BaseSessionCallback* callback) :
JitterBufferControlNode(callback, IMS_MEDIA_VIDEO)
{
std::unique_ptr<ImsMediaVideoRenderer> renderer(new ImsMediaVideoRenderer());
mVideoRenderer = std::move(renderer);
mVideoRenderer->SetSessionCallback(mCallback);
if (mJitterBuffer)
{
mJitterBuffer->SetSessionCallback(mCallback);
}
mWindow = nullptr;
mCondition.reset();
mCodecType = DEFAULT_UNDEFINED;
mWidth = 0;
mHeight = 0;
mSamplingRate = 0;
mCvoValue = 0;
memset(mConfigBuffer, 0, MAX_CONFIG_INDEX * MAX_CONFIG_LEN * sizeof(uint8_t));
memset(mConfigLen, 0, MAX_CONFIG_INDEX * sizeof(uint32_t));
mDeviceOrientation = 0;
mFirstFrame = false;
mSubtype = MEDIASUBTYPE_UNDEFINED;
mFramerate = 0;
mWaitIntraFrame = 0;
mLossDuration = 0;
mLossRateThreshold = 0;
}
IVideoRendererNode::~IVideoRendererNode() {}
kBaseNodeId IVideoRendererNode::GetNodeId()
{
return kNodeIdVideoRenderer;
}
ImsMediaResult IVideoRendererNode::Start()
{
IMLOGD1("[Start] codec[%d]", mCodecType);
if (mJitterBuffer)
{
VideoJitterBuffer* jitter = reinterpret_cast<VideoJitterBuffer*>(mJitterBuffer);
jitter->SetCodecType(mCodecType);
jitter->SetFramerate(mFramerate);
jitter->SetJitterBufferSize(15, 15, 25);
jitter->StartTimer(mLossDuration / 1000, mLossRateThreshold);
}
Reset();
std::lock_guard<std::mutex> guard(mMutex);
if (mVideoRenderer)
{
mVideoRenderer->SetCodec(mCodecType);
mVideoRenderer->SetResolution(mWidth, mHeight);
mVideoRenderer->SetDeviceOrientation(mDeviceOrientation);
mVideoRenderer->SetSurface(mWindow);
if (!mVideoRenderer->Start())
{
return RESULT_NOT_READY;
}
}
mFirstFrame = false;
mNodeState = kNodeStateRunning;
return RESULT_SUCCESS;
}
void IVideoRendererNode::Stop()
{
IMLOGD0("[Stop]");
std::lock_guard<std::mutex> guard(mMutex);
if (mVideoRenderer)
{
mVideoRenderer->Stop();
}
if (mJitterBuffer != nullptr)
{
VideoJitterBuffer* jitter = reinterpret_cast<VideoJitterBuffer*>(mJitterBuffer);
jitter->StopTimer();
}
mNodeState = kNodeStateStopped;
}
bool IVideoRendererNode::IsRunTime()
{
return false;
}
bool IVideoRendererNode::IsSourceNode()
{
return false;
}
void IVideoRendererNode::SetConfig(void* config)
{
if (config == nullptr)
{
return;
}
VideoConfig* pConfig = reinterpret_cast<VideoConfig*>(config);
mCodecType = ImsMediaVideoUtil::ConvertCodecType(pConfig->getCodecType());
mSamplingRate = pConfig->getSamplingRateKHz();
mWidth = pConfig->getResolutionWidth();
mHeight = pConfig->getResolutionHeight();
mCvoValue = pConfig->getCvoValue();
mDeviceOrientation = pConfig->getDeviceOrientationDegree();
mFramerate = pConfig->getFramerate();
}
bool IVideoRendererNode::IsSameConfig(void* config)
{
if (config == nullptr)
{
return true;
}
VideoConfig* pConfig = reinterpret_cast<VideoConfig*>(config);
return (mCodecType == ImsMediaVideoUtil::ConvertCodecType(pConfig->getCodecType()) &&
mWidth == pConfig->getResolutionWidth() && mHeight == pConfig->getResolutionHeight() &&
mCvoValue == pConfig->getCvoValue() &&
mDeviceOrientation == pConfig->getDeviceOrientationDegree() &&
mSamplingRate == pConfig->getSamplingRateKHz());
}
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;
uint32_t timestamp = 0;
uint32_t nBitstreamSize = 0;
ImsMediaSubType subtype = MEDIASUBTYPE_UNDEFINED;
uint32_t nInitialSeqNum = 0;
uint32_t nBufferOffset = 0;
ImsMediaSubType dataType;
while (GetData(&subtype, &pData, &nDataSize, &nTimeStamp, &bMark, &nSeqNum, &dataType))
{
IMLOGD_PACKET4(IM_PACKET_LOG_VIDEO,
"[ProcessData] subtype[%d], Size[%d], TimeStamp[%d] nBitstreamSize[%d]", subtype,
nDataSize, nTimeStamp, nBitstreamSize);
if (timestamp == 0)
{
timestamp = nTimeStamp;
}
else if (timestamp != nTimeStamp)
{
break;
}
if (nDataSize >= MAX_RTP_PAYLOAD_BUFFER_SIZE)
{
IMLOGE1("[ProcessData] exceed buffer size[%d]", nDataSize);
return;
}
memcpy(mBuffer + nBitstreamSize, pData, nDataSize);
nBitstreamSize += nDataSize;
if (nInitialSeqNum == 0)
{
nInitialSeqNum = nSeqNum;
}
DeleteData();
if (bMark)
{
break;
}
}
if (nBitstreamSize == 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))
{
SaveConfigFrame(pDataBuff + nBufferOffset, nDatabufferSize - nBufferOffset, kConfigSps);
SaveConfigFrame(pDataBuff + nBufferOffset, nDatabufferSize - nBufferOffset, kConfigPps);
if (mCodecType == kVideoCodecHevc)
{
SaveConfigFrame(pDataBuff + nBufferOffset, nDatabufferSize - nBufferOffset, kConfigVps);
}
if (IsSps(pDataBuff, nDatabufferSize, &nBufferOffset))
{
IMLOGD_PACKET1(
IM_PACKET_LOG_VIDEO, "[ProcessData] parse SPS - nOffset[%d]", nBufferOffset);
tCodecConfig codecConfig;
if (mCodecType == kVideoCodecAvc)
{
if (ImsMediaVideoUtil::ParseAvcSps(pDataBuff + nBufferOffset,
nDatabufferSize - nBufferOffset, &codecConfig))
{
CheckResolution(codecConfig.nWidth, codecConfig.nHeight);
}
}
else if (mCodecType == kVideoCodecHevc)
{
if (ImsMediaVideoUtil::ParseHevcSps(pDataBuff + nBufferOffset,
nDatabufferSize - nBufferOffset, &codecConfig))
{
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)
{
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");
}
mWaitIntraFrame--;
nDatabufferSize = 0; // drop non-DIR frame
IMLOGD1("[ProcessData] wait intra frame[%d]", mWaitIntraFrame);
}
}
if (!mFirstFrame)
{
IMLOGD0("[ProcessData] notify first frame");
mFirstFrame = true;
if (mCallback != nullptr)
{
mCallback->SendEvent(kImsMediaEventFirstPacketReceived);
}
}
// cvo
if (mCvoValue > 0)
{
if (mSubtype == MEDIASUBTYPE_UNDEFINED && subtype == MEDIASUBTYPE_UNDEFINED)
{
subtype = MEDIASUBTYPE_ROT0;
}
// rotation changed
if (mSubtype != subtype && (subtype >= MEDIASUBTYPE_ROT0 && subtype <= MEDIASUBTYPE_ROT270))
{
mSubtype = subtype;
int degree = 0;
switch (mSubtype)
{
default:
case MEDIASUBTYPE_ROT0:
degree = 0;
break;
case MEDIASUBTYPE_ROT90:
degree = 90;
break;
case MEDIASUBTYPE_ROT180:
degree = 180;
break;
case MEDIASUBTYPE_ROT270:
degree = 270;
break;
}
mVideoRenderer->UpdatePeerOrientation(degree);
NotifyPeerDimensionChanged();
}
}
// send sps/pps before send I frame
if (isIntraFrame)
{
QueueConfigFrame(timestamp);
}
mVideoRenderer->OnDataFrame(pDataBuff, nDatabufferSize, timestamp, false);
}
void IVideoRendererNode::UpdateSurface(ANativeWindow* window)
{
IMLOGD1("[UpdateSurface] surface[%p]", window);
mWindow = window;
}
void IVideoRendererNode::UpdateRoundTripTimeDelay(int32_t delay)
{
IMLOGD1("[UpdateRoundTripTimeDelay] delay[%d]", delay);
if (mJitterBuffer != nullptr)
{
VideoJitterBuffer* jitter = reinterpret_cast<VideoJitterBuffer*>(mJitterBuffer);
// calculate Response wait time : RWT = RTTD (mm) + 2 * frame duration
jitter->SetResponseWaitTime((delay / DEMON_NTP2MSEC) + 2 * (1000 / mFramerate));
}
}
void IVideoRendererNode::SetPacketLossParam(uint32_t time, uint32_t rate)
{
IMLOGD2("[SetPacketLossParam] time[%d], rate[%d]", time, rate);
mLossDuration = time;
mLossRateThreshold = rate;
}
bool IVideoRendererNode::IsIntraFrame(uint8_t* pbBuffer, uint32_t nBufferSize)
{
bool bIntraFrame = false;
if (nBufferSize <= 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)
{
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 bIntraFrame;
}
bool IVideoRendererNode::IsConfigFrame(
uint8_t* pbBuffer, uint32_t nBufferSize, uint32_t* nBufferOffset)
{
bool bConfigFrame = false;
if (nBufferSize <= 4)
return false;
IMLOGD_PACKET2(IM_PACKET_LOG_VIDEO, "[IsConfigFrame] 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)
{
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--;
}
break;
}
case kVideoCodecHevc:
{
uint32_t nOffset = 0;
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) == 32 || ((nCurrBuff[4] >> 1) & 0x3F) == 33 ||
((nCurrBuff[4] >> 1) & 0x3F) == 34))
{
bConfigFrame = true;
if (nBufferOffset)
{
*nBufferOffset = nOffset;
}
break;
}
nOffset++;
nCurrBuff++;
nCurrSize--;
}
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)
{
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--;
}
break;
}
case kVideoCodecHevc:
{
uint32_t nOffset = 0;
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) == 33)
{
bSPS = true;
if (nBufferOffset)
{
*nBufferOffset = nOffset;
}
break;
}
nOffset++;
nCurrBuff++;
nCurrSize--;
}
break;
}
default:
return false;
}
return bSPS;
}
void IVideoRendererNode::SaveConfigFrame(uint8_t* pbBuffer, uint32_t nBufferSize, uint32_t eMode)
{
bool bSPSString = false;
bool bPPSString = false;
if (nBufferSize <= 4)
{
return;
}
IMLOGD_PACKET3(IM_PACKET_LOG_VIDEO, "[SaveConfigFrame] mode[%d], size[%d], data[%s]", eMode,
nBufferSize,
ImsMediaTrace::IMTrace_Bin2String(
reinterpret_cast<const char*>(pbBuffer), nBufferSize > 52 ? 52 : nBufferSize));
switch (mCodecType)
{
case kVideoCodecAvc:
{
uint32_t nCurrSize = 0;
uint32_t nOffset = 0;
uint32_t nConfigSize = 0;
uint8_t* nCurrBuff = pbBuffer;
while (nCurrSize <= nBufferSize)
{
if (nCurrBuff[0] == 0x00 && nCurrBuff[1] == 0x00 && nCurrBuff[2] == 0x00 &&
nCurrBuff[3] == 0x01)
{
if (eMode == kConfigSps && !bSPSString && ((nCurrBuff[4] & 0x1F) == 7))
{
nOffset = nCurrSize;
bSPSString = true;
}
else if (eMode == kConfigPps && !bPPSString && ((nCurrBuff[4] & 0x1F) == 8))
{
nOffset = nCurrSize;
bPPSString = true;
}
else if (bSPSString || bPPSString)
{
nConfigSize = nCurrSize - nOffset;
break;
}
}
nCurrBuff++;
nCurrSize++;
}
if ((bSPSString || bPPSString) && nConfigSize == 0)
{
nConfigSize = nBufferSize - nOffset;
}
IMLOGD_PACKET3(IM_PACKET_LOG_VIDEO,
"[SaveConfigFrame] AVC Codec - bSps[%d], bPps[%d], size[%d]", bSPSString,
bPPSString, nConfigSize);
// save
if (bSPSString || bPPSString)
{
uint8_t* pConfigData = nullptr;
uint32_t nConfigIndex = 0;
if (eMode == kConfigSps)
{
nConfigIndex = 0;
}
else if (eMode == kConfigPps)
{
nConfigIndex = 1;
}
else
{
return;
}
pConfigData = mConfigBuffer[nConfigIndex];
if (0 != memcmp(pConfigData, pbBuffer + nOffset, nConfigSize))
{
memcpy(pConfigData, pbBuffer + nOffset, nConfigSize);
mConfigLen[nConfigIndex] = nConfigSize;
}
}
break;
}
case kVideoCodecHevc:
{
uint32_t nCurrSize = 0;
uint32_t nOffset = 0;
uint32_t nConfigSize = 0;
uint8_t* nCurrBuff = pbBuffer;
bool bVPSString = false;
while (nCurrSize <= nBufferSize)
{
if (nCurrBuff[0] == 0x00 && nCurrBuff[1] == 0x00 && nCurrBuff[2] == 0x00 &&
nCurrBuff[3] == 0x01)
{
if (eMode == kConfigVps && !bVPSString && (((nCurrBuff[4] >> 1) & 0x3F) == 32))
{
nOffset = nCurrSize;
bVPSString = true;
break;
}
else if (eMode == kConfigSps && !bSPSString &&
(((nCurrBuff[4] >> 1) & 0x3F) == 33))
{
nOffset = nCurrSize;
bSPSString = true;
break;
}
else if (eMode == kConfigPps && !bPPSString &&
(((nCurrBuff[4] >> 1) & 0x3F) == 34))
{
nOffset = nCurrSize;
bPPSString = true;
break;
}
}
nCurrBuff++;
nCurrSize++;
}
if (bVPSString || bSPSString || bPPSString)
{
if ((nBufferSize - nOffset) > 0)
{
nConfigSize = nBufferSize - nOffset;
}
}
IMLOGD_PACKET4(IM_PACKET_LOG_VIDEO,
"[SaveConfigFrame - H265] bVPS[%d], bSPS[%d], bPPS[%d], nConfigSize[%d]",
bVPSString, bSPSString, bPPSString, nConfigSize);
// save
if (bVPSString || bSPSString || bPPSString)
{
uint8_t* pConfigData = nullptr;
uint32_t nConfigIndex = 0;
if (eMode == kConfigVps)
{
nConfigIndex = 0;
}
else if (eMode == kConfigSps)
{
nConfigIndex = 1;
}
else if (eMode == kConfigPps)
{
nConfigIndex = 2;
}
else
{
return;
}
pConfigData = mConfigBuffer[nConfigIndex];
if (0 != memcmp(pConfigData, pbBuffer + nOffset, nConfigSize))
{
memcpy(pConfigData, pbBuffer + nOffset, nConfigSize);
mConfigLen[nConfigIndex] = nConfigSize;
}
}
break;
}
default:
return;
}
}
bool IVideoRendererNode::RemoveAUDNalUnit(
uint8_t* pInBuffer, uint32_t nInBufferSize, uint8_t** ppOutBuffer, uint32_t* pOutBufferSize)
{
bool bAUDUnit = false;
*ppOutBuffer = pInBuffer;
*pOutBufferSize = nInBufferSize;
if (nInBufferSize <= 4)
{
return false;
}
switch (mCodecType)
{
case kVideoCodecAvc:
{
uint32_t nCurrSize = nInBufferSize;
uint8_t* nCurrBuff = pInBuffer;
uint32_t nCnt = 0;
while (nCurrSize >= 5 && nCnt <= 12)
{
if (bAUDUnit &&
(nCurrBuff[0] == 0x00 && nCurrBuff[1] == 0x00 && nCurrBuff[2] == 0x00 &&
nCurrBuff[3] == 0x01))
{
*ppOutBuffer = nCurrBuff;
*pOutBufferSize = nCurrSize;
break;
}
if (nCurrBuff[0] == 0x00 && nCurrBuff[1] == 0x00 && nCurrBuff[2] == 0x00 &&
nCurrBuff[3] == 0x01 && nCurrBuff[4] == 0x09)
{
bAUDUnit = true;
}
nCurrBuff++;
nCurrSize--;
nCnt++;
}
}
break;
case kVideoCodecHevc:
default:
return false;
}
return bAUDUnit;
}
void IVideoRendererNode::CheckResolution(uint32_t nWidth, uint32_t nHeight)
{
if ((nWidth != 0 && nWidth != mWidth) || (nHeight != 0 && nHeight != mHeight))
{
IMLOGD4("[CheckResolution] resolution change[%dx%d] to [%dx%d]", mWidth, mHeight, nWidth,
nHeight);
mWidth = nWidth;
mHeight = nHeight;
NotifyPeerDimensionChanged();
}
}
void IVideoRendererNode::QueueConfigFrame(uint32_t timestamp)
{
uint32_t nNumOfConfigString = 0;
if (mCodecType == kVideoCodecAvc)
{
nNumOfConfigString = 2;
}
else if (mCodecType == kVideoCodecHevc)
{
nNumOfConfigString = 3;
}
for (int32_t i = 0; i < nNumOfConfigString; i++)
{
uint8_t* pConfigData = nullptr;
uint32_t nConfigLen = mConfigLen[i];
pConfigData = mConfigBuffer[i];
if (nConfigLen == 0 || mVideoRenderer == nullptr)
{
continue;
}
mVideoRenderer->OnDataFrame(pConfigData, nConfigLen, timestamp, true);
}
}
void IVideoRendererNode::NotifyPeerDimensionChanged()
{
if (mCallback == nullptr)
{
return;
}
// assume the device is portrait
if (mWidth > mHeight) // landscape
{
// local rotation
if (mDeviceOrientation == 0 || mDeviceOrientation == 180)
{
// peer rotation
if (mSubtype == MEDIASUBTYPE_ROT0 || mSubtype == MEDIASUBTYPE_ROT180)
{
mCallback->SendEvent(kImsMediaEventResolutionChanged, mWidth, mHeight);
}
else if (mSubtype == MEDIASUBTYPE_ROT90 || mSubtype == MEDIASUBTYPE_ROT270)
{
mCallback->SendEvent(kImsMediaEventResolutionChanged, mHeight, mWidth);
}
}
else
{
// peer rotation
if (mSubtype == MEDIASUBTYPE_ROT0 || mSubtype == MEDIASUBTYPE_ROT180)
{
mCallback->SendEvent(kImsMediaEventResolutionChanged, mHeight, mWidth);
}
else if (mSubtype == MEDIASUBTYPE_ROT90 || mSubtype == MEDIASUBTYPE_ROT270)
{
mCallback->SendEvent(kImsMediaEventResolutionChanged, mWidth, mHeight);
}
}
}
else // portrait
{
// peer rotation
if (mSubtype == MEDIASUBTYPE_ROT0 || mSubtype == MEDIASUBTYPE_ROT180)
{
mCallback->SendEvent(kImsMediaEventResolutionChanged, mWidth, mHeight);
}
else if (mSubtype == MEDIASUBTYPE_ROT90 || mSubtype == MEDIASUBTYPE_ROT270)
{
mCallback->SendEvent(kImsMediaEventResolutionChanged, mHeight, mWidth);
}
}
}