1 /**
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <IVideoRendererNode.h>
18 #include <ImsMediaVideoRenderer.h>
19 #include <ImsMediaTrace.h>
20 #include <ImsMediaTimer.h>
21 #include <ImsMediaBitReader.h>
22 #include <VideoConfig.h>
23 #include <ImsMediaVideoUtil.h>
24 #include <VideoJitterBuffer.h>
25 #include <string.h>
26 
27 using namespace android::telephony::imsmedia;
28 
29 #define DEFAULT_UNDEFINED (-1)
30 
IVideoRendererNode(BaseSessionCallback * callback)31 IVideoRendererNode::IVideoRendererNode(BaseSessionCallback* callback) :
32         JitterBufferControlNode(callback, IMS_MEDIA_VIDEO)
33 {
34     std::unique_ptr<ImsMediaVideoRenderer> renderer(new ImsMediaVideoRenderer());
35     mVideoRenderer = std::move(renderer);
36     mVideoRenderer->SetSessionCallback(mCallback);
37 
38     if (mJitterBuffer)
39     {
40         mJitterBuffer->SetSessionCallback(mCallback);
41     }
42 
43     mWindow = nullptr;
44     mCondition.reset();
45     mCodecType = DEFAULT_UNDEFINED;
46     mWidth = 0;
47     mHeight = 0;
48     mSamplingRate = 0;
49     mCvoValue = 0;
50     memset(mConfigBuffer, 0, MAX_CONFIG_INDEX * MAX_CONFIG_LEN * sizeof(uint8_t));
51     memset(mConfigLen, 0, MAX_CONFIG_INDEX * sizeof(uint32_t));
52     mDeviceOrientation = 0;
53     mFirstFrame = false;
54     mSubtype = MEDIASUBTYPE_UNDEFINED;
55     mFramerate = 0;
56     mLossDuration = 0;
57     mLossRateThreshold = 0;
58 }
59 
~IVideoRendererNode()60 IVideoRendererNode::~IVideoRendererNode() {}
61 
GetNodeId()62 kBaseNodeId IVideoRendererNode::GetNodeId()
63 {
64     return kNodeIdVideoRenderer;
65 }
66 
Start()67 ImsMediaResult IVideoRendererNode::Start()
68 {
69     IMLOGD1("[Start] codec[%d]", mCodecType);
70     if (mJitterBuffer)
71     {
72         VideoJitterBuffer* jitter = reinterpret_cast<VideoJitterBuffer*>(mJitterBuffer);
73         jitter->SetCodecType(mCodecType);
74         jitter->SetFramerate(mFramerate);
75         jitter->SetJitterBufferSize(15, 15, 25);
76         jitter->StartTimer(mLossDuration / 1000, mLossRateThreshold);
77     }
78 
79     Reset();
80     std::lock_guard<std::mutex> guard(mMutex);
81 
82     if (mVideoRenderer)
83     {
84         mVideoRenderer->SetCodec(mCodecType);
85         mVideoRenderer->SetResolution(mWidth, mHeight);
86         mVideoRenderer->SetDeviceOrientation(mDeviceOrientation);
87         mVideoRenderer->SetSurface(mWindow);
88 
89         if (!mVideoRenderer->Start())
90         {
91             return RESULT_NOT_READY;
92         }
93     }
94 
95     mFirstFrame = false;
96     mNodeState = kNodeStateRunning;
97     return RESULT_SUCCESS;
98 }
99 
Stop()100 void IVideoRendererNode::Stop()
101 {
102     IMLOGD0("[Stop]");
103     std::lock_guard<std::mutex> guard(mMutex);
104 
105     if (mVideoRenderer)
106     {
107         mVideoRenderer->Stop();
108     }
109 
110     if (mJitterBuffer != nullptr)
111     {
112         VideoJitterBuffer* jitter = reinterpret_cast<VideoJitterBuffer*>(mJitterBuffer);
113         jitter->StopTimer();
114     }
115 
116     mNodeState = kNodeStateStopped;
117 }
118 
IsRunTime()119 bool IVideoRendererNode::IsRunTime()
120 {
121     return false;
122 }
123 
IsSourceNode()124 bool IVideoRendererNode::IsSourceNode()
125 {
126     return false;
127 }
128 
SetConfig(void * config)129 void IVideoRendererNode::SetConfig(void* config)
130 {
131     if (config == nullptr)
132     {
133         return;
134     }
135 
136     VideoConfig* pConfig = reinterpret_cast<VideoConfig*>(config);
137     mCodecType = ImsMediaVideoUtil::ConvertCodecType(pConfig->getCodecType());
138     mSamplingRate = pConfig->getSamplingRateKHz();
139     mWidth = pConfig->getResolutionWidth();
140     mHeight = pConfig->getResolutionHeight();
141     mCvoValue = pConfig->getCvoValue();
142     mDeviceOrientation = pConfig->getDeviceOrientationDegree();
143     mFramerate = pConfig->getFramerate();
144 }
145 
IsSameConfig(void * config)146 bool IVideoRendererNode::IsSameConfig(void* config)
147 {
148     if (config == nullptr)
149     {
150         return true;
151     }
152 
153     VideoConfig* pConfig = reinterpret_cast<VideoConfig*>(config);
154     return (mCodecType == ImsMediaVideoUtil::ConvertCodecType(pConfig->getCodecType()) &&
155             mWidth == pConfig->getResolutionWidth() && mHeight == pConfig->getResolutionHeight() &&
156             mCvoValue == pConfig->getCvoValue() &&
157             mDeviceOrientation == pConfig->getDeviceOrientationDegree() &&
158             mSamplingRate == pConfig->getSamplingRateKHz());
159 }
160 
ProcessData()161 void IVideoRendererNode::ProcessData()
162 {
163     std::lock_guard<std::mutex> guard(mMutex);
164     uint8_t* data = nullptr;
165     uint32_t dataSize = 0;
166     uint32_t prevTimestamp = 0;
167     bool mark = false;
168     uint32_t seq = 0;
169     uint32_t timestamp = 0;
170     uint32_t frameSize = 0;
171     ImsMediaSubType subtype = MEDIASUBTYPE_UNDEFINED;
172     uint32_t initialSeq = 0;
173     ImsMediaSubType dataType;
174 
175     while (GetData(&subtype, &data, &dataSize, ×tamp, &mark, &seq, &dataType))
176     {
177         IMLOGD_PACKET4(IM_PACKET_LOG_VIDEO,
178                 "[ProcessData] subtype[%d], Size[%d], TS[%d] frameSize[%d]", subtype, dataSize,
179                 timestamp, frameSize);
180 
181         if (prevTimestamp == 0)
182         {
183             prevTimestamp = timestamp;
184         }
185         else if (timestamp != prevTimestamp || (frameSize != 0 && hasStartingCode(data, dataSize)))
186         {
187             // break when the timestamp is changed or next data has another starting code
188             break;
189         }
190 
191         if (dataSize >= MAX_RTP_PAYLOAD_BUFFER_SIZE)
192         {
193             IMLOGE1("[ProcessData] exceed buffer size[%d]", dataSize);
194             return;
195         }
196 
197         memcpy(mBuffer + frameSize, data, dataSize);
198         frameSize += dataSize;
199 
200         if (initialSeq == 0)
201         {
202             initialSeq = seq;
203         }
204 
205         DeleteData();
206 
207         if (mark)
208         {
209             break;
210         }
211     }
212 
213     if (frameSize == 0)
214     {
215         return;
216     }
217 
218     // remove AUD nal unit
219     uint32_t size = frameSize;
220     uint8_t* buffer = mBuffer;
221     RemoveAUDNalUnit(mBuffer, frameSize, &buffer, &size);
222 
223     FrameType frameType = GetFrameType(buffer, size);
224 
225     if (frameType == SPS)
226     {
227         SaveConfigFrame(buffer, size, kConfigSps);
228         tCodecConfig codecConfig;
229 
230         if (mCodecType == kVideoCodecAvc)
231         {
232             if (ImsMediaVideoUtil::ParseAvcSps(buffer, size, &codecConfig))
233             {
234                 CheckResolution(codecConfig.nWidth, codecConfig.nHeight);
235             }
236         }
237         else if (mCodecType == kVideoCodecHevc)
238         {
239             if (ImsMediaVideoUtil::ParseHevcSps(buffer, size, &codecConfig))
240             {
241                 CheckResolution(codecConfig.nWidth, codecConfig.nHeight);
242             }
243         }
244 
245         return;
246     }
247     else if (frameType == PPS)
248     {
249         SaveConfigFrame(buffer, size, kConfigPps);
250         return;
251     }
252     else if (frameType == VPS)
253     {
254         SaveConfigFrame(buffer, size, kConfigVps);
255         return;
256     }
257 
258     IMLOGD_PACKET2(IM_PACKET_LOG_VIDEO, "[ProcessData] frame type[%d] size[%d]", frameType, size);
259 
260     // TODO: Send PLI or FIR when I-frame wasn't received since beginning.
261 
262     if (!mFirstFrame)
263     {
264         IMLOGD0("[ProcessData] notify first frame");
265         mFirstFrame = true;
266 
267         if (mCallback != nullptr)
268         {
269             mCallback->SendEvent(kImsMediaEventFirstPacketReceived);
270 
271             if (mCvoValue <= 0)
272             {
273                 mCallback->SendEvent(kImsMediaEventResolutionChanged, mWidth, mHeight);
274             }
275         }
276     }
277 
278     // cvo
279     if (mCvoValue > 0)
280     {
281         if (mSubtype == MEDIASUBTYPE_UNDEFINED && subtype == MEDIASUBTYPE_UNDEFINED)
282         {
283             subtype = MEDIASUBTYPE_ROT0;
284         }
285 
286         // rotation changed
287         if (mSubtype != subtype && (subtype >= MEDIASUBTYPE_ROT0 && subtype <= MEDIASUBTYPE_ROT270))
288         {
289             mSubtype = subtype;
290             int degree = 0;
291 
292             switch (mSubtype)
293             {
294                 default:
295                 case MEDIASUBTYPE_ROT0:
296                     degree = 0;
297                     break;
298                 case MEDIASUBTYPE_ROT90:
299                     degree = 90;
300                     break;
301                 case MEDIASUBTYPE_ROT180:
302                     degree = 180;
303                     break;
304                 case MEDIASUBTYPE_ROT270:
305                     degree = 270;
306                     break;
307             }
308 
309             mVideoRenderer->UpdatePeerOrientation(degree);
310             NotifyPeerDimensionChanged();
311         }
312     }
313 
314     // send config frames before send I frame
315     if (frameType == IDR)
316     {
317         QueueConfigFrame(timestamp);
318     }
319 
320     mVideoRenderer->OnDataFrame(buffer, size, timestamp, false);
321 }
322 
UpdateSurface(ANativeWindow * window)323 void IVideoRendererNode::UpdateSurface(ANativeWindow* window)
324 {
325     IMLOGD1("[UpdateSurface] surface[%p]", window);
326     mWindow = window;
327 }
328 
UpdateRoundTripTimeDelay(int32_t delay)329 void IVideoRendererNode::UpdateRoundTripTimeDelay(int32_t delay)
330 {
331     IMLOGD1("[UpdateRoundTripTimeDelay] delay[%d]", delay);
332 
333     if (mJitterBuffer != nullptr)
334     {
335         VideoJitterBuffer* jitter = reinterpret_cast<VideoJitterBuffer*>(mJitterBuffer);
336 
337         // calculate Response wait time : RWT = RTTD (mm) + 2 * frame duration
338         jitter->SetResponseWaitTime((delay / DEMON_NTP2MSEC) + 2 * (1000 / mFramerate));
339     }
340 }
341 
SetPacketLossParam(uint32_t time,uint32_t rate)342 void IVideoRendererNode::SetPacketLossParam(uint32_t time, uint32_t rate)
343 {
344     IMLOGD2("[SetPacketLossParam] time[%d], rate[%d]", time, rate);
345 
346     mLossDuration = time;
347     mLossRateThreshold = rate;
348 }
349 
hasStartingCode(uint8_t * buffer,uint32_t bufferSize)350 bool IVideoRendererNode::hasStartingCode(uint8_t* buffer, uint32_t bufferSize)
351 {
352     if (bufferSize <= 4)
353     {
354         return false;
355     }
356 
357     // Check for NAL unit delimiter 0x00000001
358     if (buffer[0] == 0x00 && buffer[1] == 0x00 && buffer[2] == 0x00 && buffer[3] == 0x01)
359     {
360         return true;
361     }
362 
363     return false;
364 }
365 
GetFrameType(uint8_t * buffer,uint32_t bufferSize)366 FrameType IVideoRendererNode::GetFrameType(uint8_t* buffer, uint32_t bufferSize)
367 {
368     if (!hasStartingCode(buffer, bufferSize))
369     {
370         return UNKNOWN;
371     }
372 
373     uint8_t nalType = buffer[4];
374 
375     switch (mCodecType)
376     {
377         case kVideoCodecAvc:
378         {
379             if ((nalType & 0x1F) == 5)
380             {
381                 return IDR;
382             }
383             else if ((nalType & 0x1F) == 7)
384             {
385                 return SPS;
386             }
387             else if ((nalType & 0x1F) == 8)
388             {
389                 return PPS;
390             }
391             else
392             {
393                 return NonIDR;
394             }
395 
396             break;
397         }
398         case kVideoCodecHevc:
399         {
400             if (((nalType >> 1) & 0x3F) == 19 || ((nalType >> 1) & 0x3F) == 20)
401             {
402                 return IDR;
403             }
404             else if (((nalType >> 1) & 0x3F) == 32)
405             {
406                 return VPS;
407             }
408             else if (((nalType >> 1) & 0x3F) == 33)
409             {
410                 return SPS;
411             }
412             else if (((nalType >> 1) & 0x3F) == 34)
413             {
414                 return PPS;
415             }
416             else
417             {
418                 return NonIDR;
419             }
420 
421             break;
422         }
423         default:
424             IMLOGE1("[GetFrameType] Invalid video codec type %d", mCodecType);
425     }
426 
427     return UNKNOWN;
428 }
429 
SaveConfigFrame(uint8_t * pbBuffer,uint32_t nBufferSize,uint32_t eMode)430 void IVideoRendererNode::SaveConfigFrame(uint8_t* pbBuffer, uint32_t nBufferSize, uint32_t eMode)
431 {
432     bool bSPSString = false;
433     bool bPPSString = false;
434 
435     if (nBufferSize <= 4)
436     {
437         return;
438     }
439 
440     IMLOGD_PACKET3(IM_PACKET_LOG_VIDEO, "[SaveConfigFrame] mode[%d], size[%d], data[%s]", eMode,
441             nBufferSize,
442             ImsMediaTrace::IMTrace_Bin2String(
443                     reinterpret_cast<const char*>(pbBuffer), nBufferSize > 52 ? 52 : nBufferSize));
444 
445     switch (mCodecType)
446     {
447         case kVideoCodecAvc:
448         {
449             uint32_t nCurrSize = 0;
450             uint32_t nOffset = 0;
451             uint32_t nConfigSize = 0;
452             uint8_t* nCurrBuff = pbBuffer;
453 
454             while (nCurrSize <= nBufferSize)
455             {
456                 if (nCurrBuff[0] == 0x00 && nCurrBuff[1] == 0x00 && nCurrBuff[2] == 0x00 &&
457                         nCurrBuff[3] == 0x01)
458                 {
459                     if (eMode == kConfigSps && !bSPSString && ((nCurrBuff[4] & 0x1F) == 7))
460                     {
461                         nOffset = nCurrSize;
462                         bSPSString = true;
463                     }
464                     else if (eMode == kConfigPps && !bPPSString && ((nCurrBuff[4] & 0x1F) == 8))
465                     {
466                         nOffset = nCurrSize;
467                         bPPSString = true;
468                     }
469                     else if (bSPSString || bPPSString)
470                     {
471                         nConfigSize = nCurrSize - nOffset;
472                         break;
473                     }
474                 }
475 
476                 nCurrBuff++;
477                 nCurrSize++;
478             }
479 
480             if ((bSPSString || bPPSString) && nConfigSize == 0)
481             {
482                 nConfigSize = nBufferSize - nOffset;
483             }
484 
485             IMLOGD_PACKET3(IM_PACKET_LOG_VIDEO,
486                     "[SaveConfigFrame] AVC Codec - bSps[%d], bPps[%d], size[%d]", bSPSString,
487                     bPPSString, nConfigSize);
488 
489             // save
490             if (bSPSString || bPPSString)
491             {
492                 uint8_t* pConfigData = nullptr;
493                 uint32_t nConfigIndex = 0;
494 
495                 if (eMode == kConfigSps)
496                 {
497                     nConfigIndex = 0;
498                 }
499                 else if (eMode == kConfigPps)
500                 {
501                     nConfigIndex = 1;
502                 }
503                 else
504                 {
505                     return;
506                 }
507 
508                 pConfigData = mConfigBuffer[nConfigIndex];
509 
510                 if (0 != memcmp(pConfigData, pbBuffer + nOffset, nConfigSize))
511                 {
512                     memcpy(pConfigData, pbBuffer + nOffset, nConfigSize);
513                     mConfigLen[nConfigIndex] = nConfigSize;
514                 }
515             }
516             break;
517         }
518 
519         case kVideoCodecHevc:
520         {
521             uint32_t nCurrSize = 0;
522             uint32_t nOffset = 0;
523             uint32_t nConfigSize = 0;
524             uint8_t* nCurrBuff = pbBuffer;
525             bool bVPSString = false;
526 
527             while (nCurrSize <= nBufferSize)
528             {
529                 if (nCurrBuff[0] == 0x00 && nCurrBuff[1] == 0x00 && nCurrBuff[2] == 0x00 &&
530                         nCurrBuff[3] == 0x01)
531                 {
532                     if (eMode == kConfigVps && !bVPSString && (((nCurrBuff[4] >> 1) & 0x3F) == 32))
533                     {
534                         nOffset = nCurrSize;
535                         bVPSString = true;
536                         break;
537                     }
538                     else if (eMode == kConfigSps && !bSPSString &&
539                             (((nCurrBuff[4] >> 1) & 0x3F) == 33))
540                     {
541                         nOffset = nCurrSize;
542                         bSPSString = true;
543                         break;
544                     }
545                     else if (eMode == kConfigPps && !bPPSString &&
546                             (((nCurrBuff[4] >> 1) & 0x3F) == 34))
547                     {
548                         nOffset = nCurrSize;
549                         bPPSString = true;
550                         break;
551                     }
552                 }
553 
554                 nCurrBuff++;
555                 nCurrSize++;
556             }
557 
558             if (bVPSString || bSPSString || bPPSString)
559             {
560                 if ((nBufferSize - nOffset) > 0)
561                 {
562                     nConfigSize = nBufferSize - nOffset;
563                 }
564             }
565 
566             IMLOGD_PACKET4(IM_PACKET_LOG_VIDEO,
567                     "[SaveConfigFrame - H265] bVPS[%d], bSPS[%d], bPPS[%d], nConfigSize[%d]",
568                     bVPSString, bSPSString, bPPSString, nConfigSize);
569 
570             // save
571             if (bVPSString || bSPSString || bPPSString)
572             {
573                 uint8_t* pConfigData = nullptr;
574                 uint32_t nConfigIndex = 0;
575 
576                 if (eMode == kConfigVps)
577                 {
578                     nConfigIndex = 0;
579                 }
580                 else if (eMode == kConfigSps)
581                 {
582                     nConfigIndex = 1;
583                 }
584                 else if (eMode == kConfigPps)
585                 {
586                     nConfigIndex = 2;
587                 }
588                 else
589                 {
590                     return;
591                 }
592 
593                 pConfigData = mConfigBuffer[nConfigIndex];
594 
595                 if (0 != memcmp(pConfigData, pbBuffer + nOffset, nConfigSize))
596                 {
597                     memcpy(pConfigData, pbBuffer + nOffset, nConfigSize);
598                     mConfigLen[nConfigIndex] = nConfigSize;
599                 }
600             }
601             break;
602         }
603         default:
604             return;
605     }
606 }
607 
RemoveAUDNalUnit(uint8_t * inBuffer,uint32_t inBufferSize,uint8_t ** outBuffer,uint32_t * outBufferSize)608 bool IVideoRendererNode::RemoveAUDNalUnit(
609         uint8_t* inBuffer, uint32_t inBufferSize, uint8_t** outBuffer, uint32_t* outBufferSize)
610 {
611     bool IsAudUnit = false;
612     *outBuffer = inBuffer;
613     *outBufferSize = inBufferSize;
614 
615     if (inBufferSize <= 4)
616     {
617         return false;
618     }
619 
620     switch (mCodecType)
621     {
622         case kVideoCodecAvc:
623         {
624             uint32_t currSize = inBufferSize;
625             uint8_t* currBuffer = inBuffer;
626             uint32_t count = 0;
627 
628             while (currSize >= 5 && count <= 12)
629             {
630                 if (IsAudUnit &&
631                         (currBuffer[0] == 0x00 && currBuffer[1] == 0x00 && currBuffer[2] == 0x00 &&
632                                 currBuffer[3] == 0x01))
633                 {
634                     *outBuffer = currBuffer;
635                     *outBufferSize = currSize;
636                     break;
637                 }
638                 if (currBuffer[0] == 0x00 && currBuffer[1] == 0x00 && currBuffer[2] == 0x00 &&
639                         currBuffer[3] == 0x01 && currBuffer[4] == 0x09)
640                 {
641                     IsAudUnit = true;
642                 }
643 
644                 currBuffer++;
645                 currSize--;
646                 count++;
647             }
648         }
649         break;
650         case kVideoCodecHevc:
651         default:
652             return false;
653     }
654 
655     return IsAudUnit;
656 }
657 
CheckResolution(uint32_t nWidth,uint32_t nHeight)658 void IVideoRendererNode::CheckResolution(uint32_t nWidth, uint32_t nHeight)
659 {
660     if ((nWidth != 0 && nWidth != mWidth) || (nHeight != 0 && nHeight != mHeight))
661     {
662         IMLOGD4("[CheckResolution] resolution change[%dx%d] to [%dx%d]", mWidth, mHeight, nWidth,
663                 nHeight);
664         mWidth = nWidth;
665         mHeight = nHeight;
666 
667         NotifyPeerDimensionChanged();
668     }
669 }
670 
QueueConfigFrame(uint32_t timestamp)671 void IVideoRendererNode::QueueConfigFrame(uint32_t timestamp)
672 {
673     uint32_t nNumOfConfigString = 0;
674     if (mCodecType == kVideoCodecAvc)
675     {
676         nNumOfConfigString = 2;
677     }
678     else if (mCodecType == kVideoCodecHevc)
679     {
680         nNumOfConfigString = 3;
681     }
682 
683     for (int32_t i = 0; i < nNumOfConfigString; i++)
684     {
685         uint8_t* configFrame = nullptr;
686         uint32_t configLen = mConfigLen[i];
687         configFrame = mConfigBuffer[i];
688 
689         if (configLen == 0 || mVideoRenderer == nullptr)
690         {
691             continue;
692         }
693 
694         mVideoRenderer->OnDataFrame(configFrame, configLen, timestamp, true);
695     }
696 }
697 
NotifyPeerDimensionChanged()698 void IVideoRendererNode::NotifyPeerDimensionChanged()
699 {
700     if (mCallback == nullptr)
701     {
702         return;
703     }
704 
705     IMLOGD1("[NotifyPeerDimensionChanged] subtype[%d]", mSubtype);
706 
707     // assume the device is portrait
708     if (mWidth > mHeight)  // landscape
709     {
710         // local rotation
711         if (mDeviceOrientation == 0 || mDeviceOrientation == 180)
712         {
713             // peer rotation
714             if (mSubtype == MEDIASUBTYPE_ROT0 || mSubtype == MEDIASUBTYPE_ROT180)
715             {
716                 mCallback->SendEvent(kImsMediaEventResolutionChanged, mWidth, mHeight);
717             }
718             else if (mSubtype == MEDIASUBTYPE_ROT90 || mSubtype == MEDIASUBTYPE_ROT270)
719             {
720                 mCallback->SendEvent(kImsMediaEventResolutionChanged, mHeight, mWidth);
721             }
722         }
723         else
724         {
725             // peer rotation
726             if (mSubtype == MEDIASUBTYPE_ROT0 || mSubtype == MEDIASUBTYPE_ROT180)
727             {
728                 mCallback->SendEvent(kImsMediaEventResolutionChanged, mHeight, mWidth);
729             }
730             else if (mSubtype == MEDIASUBTYPE_ROT90 || mSubtype == MEDIASUBTYPE_ROT270)
731             {
732                 mCallback->SendEvent(kImsMediaEventResolutionChanged, mWidth, mHeight);
733             }
734         }
735     }
736     else  // portrait
737     {
738         // peer rotation
739         if (mSubtype == MEDIASUBTYPE_ROT0 || mSubtype == MEDIASUBTYPE_ROT180)
740         {
741             mCallback->SendEvent(kImsMediaEventResolutionChanged, mWidth, mHeight);
742         }
743         else if (mSubtype == MEDIASUBTYPE_ROT90 || mSubtype == MEDIASUBTYPE_ROT270)
744         {
745             mCallback->SendEvent(kImsMediaEventResolutionChanged, mHeight, mWidth);
746         }
747     }
748 }