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, ×tamp, &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 }