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 <TextRendererNode.h>
18 #include <TextConfig.h>
19 #include <ImsMediaTimer.h>
20 #include <ImsMediaTrace.h>
21 
22 /** Maximum waiting time when packet loss found */
23 #define TEXT_LOSS_MAX_WAITING_TIME (1000)
24 
TextRendererNode(BaseSessionCallback * callback)25 TextRendererNode::TextRendererNode(BaseSessionCallback* callback) :
26         JitterBufferControlNode(callback, IMS_MEDIA_TEXT)
27 {
28     mCodecType = TextConfig::TEXT_CODEC_NONE;
29     mBOMReceived = false;
30     mLastPlayedSeq = 0;
31     mLossWaitTime = 0;
32     mFirstFrameReceived = false;
33 }
34 
~TextRendererNode()35 TextRendererNode::~TextRendererNode() {}
36 
GetNodeId()37 kBaseNodeId TextRendererNode::GetNodeId()
38 {
39     return kNodeIdTextRenderer;
40 }
41 
Start()42 ImsMediaResult TextRendererNode::Start()
43 {
44     IMLOGD1("[Start] codec[%d]", mCodecType);
45 
46     if (mCodecType == TextConfig::TEXT_CODEC_NONE)
47     {
48         return RESULT_INVALID_PARAM;
49     }
50 
51     mBOMReceived = false;
52     mLastPlayedSeq = 0;
53     mLossWaitTime = 0;
54     mFirstFrameReceived = false;
55     mNodeState = kNodeStateRunning;
56     return RESULT_SUCCESS;
57 }
58 
Stop()59 void TextRendererNode::Stop()
60 {
61     IMLOGD0("[Stop]");
62     mNodeState = kNodeStateStopped;
63 }
64 
IsRunTime()65 bool TextRendererNode::IsRunTime()
66 {
67     return false;
68 }
69 
IsSourceNode()70 bool TextRendererNode::IsSourceNode()
71 {
72     return false;
73 }
74 
SetConfig(void * config)75 void TextRendererNode::SetConfig(void* config)
76 {
77     if (config == nullptr)
78     {
79         return;
80     }
81 
82     TextConfig* pConfig = reinterpret_cast<TextConfig*>(config);
83     mCodecType = pConfig->getCodecType();
84 }
85 
IsSameConfig(void * config)86 bool TextRendererNode::IsSameConfig(void* config)
87 {
88     if (config == nullptr)
89     {
90         return true;
91     }
92 
93     TextConfig* pConfig = reinterpret_cast<TextConfig*>(config);
94 
95     return (mCodecType == pConfig->getCodecType());
96 }
97 
OnDataFromFrontNode(ImsMediaSubType subtype,uint8_t * data,uint32_t size,uint32_t timestamp,bool mark,uint32_t seq,ImsMediaSubType dataType,uint32_t arrivalTime)98 void TextRendererNode::OnDataFromFrontNode(ImsMediaSubType subtype, uint8_t* data, uint32_t size,
99         uint32_t timestamp, bool mark, uint32_t seq, ImsMediaSubType dataType, uint32_t arrivalTime)
100 {
101     if (subtype == MEDIASUBTYPE_REFRESHED)
102     {
103         mFirstFrameReceived = false;
104         mBOMReceived = false;
105         mLastPlayedSeq = 0;
106         mLossWaitTime = 0;
107     }
108 
109     JitterBufferControlNode::OnDataFromFrontNode(
110             subtype, data, size, timestamp, mark, seq, dataType, arrivalTime);
111 }
112 
ProcessData()113 void TextRendererNode::ProcessData()
114 {
115     static const char* CHAR_REPLACEMENT = "\xEf\xbf\xbd";
116     uint8_t* data;
117     uint32_t size;
118     uint32_t timestamp;
119     uint32_t seq;
120     bool mark;
121     ImsMediaSubType subtype;
122     ImsMediaSubType dataType;
123 
124     while (GetData(&subtype, &data, &size, &timestamp, &mark, &seq, &dataType))
125     {
126         IMLOGD_PACKET5(IM_PACKET_LOG_TEXT,
127                 "[ProcessData] size[%u], TS[%u], mark[%u], seq[%u], last seq[%u]", size, timestamp,
128                 mark, seq, mLastPlayedSeq);
129 
130         if (mFirstFrameReceived)
131         {
132             // detect lost packet
133             uint16_t seqDiff = (uint16_t)seq - mLastPlayedSeq;
134 
135             if (seqDiff > 1)  // lost found
136             {
137                 // Wait 1000 sec - RFC4103: 5.4 - Compensation for Packets Out of Order
138                 uint32_t nCurTime = ImsMediaTimer::GetTimeInMilliSeconds();
139 
140                 if (mLossWaitTime == 0)
141                 {
142                     mLossWaitTime = nCurTime;
143                 }
144 
145                 if (nCurTime - mLossWaitTime <= TEXT_LOSS_MAX_WAITING_TIME)
146                 {
147                     return;
148                 }
149 
150                 int32_t lostCount = seqDiff - 1;
151                 IMLOGD1("[ProcessData] lostCount[%d]", lostCount);
152 
153                 // Send a lost T140 as question mark
154                 for (int32_t nIndex = 1; nIndex <= lostCount; nIndex++)
155                 {
156                     // send replacement character in case of lost packet detected
157                     android::String8* text = new android::String8(CHAR_REPLACEMENT);
158                     mCallback->SendEvent(
159                             kImsMediaEventNotifyRttReceived, reinterpret_cast<uint64_t>(text), 0);
160 
161                     uint16_t lostSeq = mLastPlayedSeq + (uint16_t)nIndex;
162                     IMLOGD_PACKET1(IM_PACKET_LOG_TEXT, "[ProcessData] LostSeq[%u]", lostSeq);
163                 }
164             }
165 
166             mLossWaitTime = 0;  // reset loss wait
167         }
168 
169         int32_t offset = size;
170         uint8_t* dataPtr = data;
171 
172         // remove BOM from the string and send event
173         while (offset > 0)
174         {
175             // remain last null data
176             uint32_t transSize = 0;
177             offset > MAX_RTT_LEN ? transSize = MAX_RTT_LEN : transSize = offset;
178 
179             if (mBOMReceived == false && offset >= 3 && dataPtr[0] == 0xef && dataPtr[1] == 0xbb &&
180                     dataPtr[2] == 0xbf)
181             {
182                 IMLOGD0("[ProcessData] got byte order mark");
183                 mBOMReceived = true;
184                 dataPtr += 3;
185                 transSize -= 3;
186                 offset -= 3;
187             }
188 
189             // send event to notify to transfer rtt data received
190             if (transSize > 0)
191             {
192                 memset(mBuffer, 0, sizeof(mBuffer));
193                 memcpy(mBuffer, dataPtr, transSize);
194                 android::String8* text = new android::String8(mBuffer);
195                 mCallback->SendEvent(
196                         kImsMediaEventNotifyRttReceived, reinterpret_cast<uint64_t>(text), 0);
197                 IMLOGD_PACKET2(
198                         IM_PACKET_LOG_TEXT, "[ProcessData] size[%d] text[%s]", transSize, mBuffer);
199             }
200 
201             dataPtr += transSize;
202             offset -= transSize;
203         }
204 
205         mFirstFrameReceived = true;
206         mLastPlayedSeq = (uint16_t)seq;
207         DeleteData();
208     }
209 }