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 <TextRtpPayloadEncoderNode.h>
18 #include <TextConfig.h>
19 #include <ImsMediaTrace.h>
20
TextRtpPayloadEncoderNode(BaseSessionCallback * callback)21 TextRtpPayloadEncoderNode::TextRtpPayloadEncoderNode(BaseSessionCallback* callback) :
22 BaseNode(callback)
23 {
24 mCodecType = TextConfig::TEXT_CODEC_NONE;
25 mLastTimestampSent = 0;
26 mLastMarkedTime = 0;
27 }
28
~TextRtpPayloadEncoderNode()29 TextRtpPayloadEncoderNode::~TextRtpPayloadEncoderNode()
30 {
31 mBufferQueue.Clear();
32 }
33
GetNodeId()34 kBaseNodeId TextRtpPayloadEncoderNode::GetNodeId()
35 {
36 return kNodeIdTextPayloadEncoder;
37 }
38
Start()39 ImsMediaResult TextRtpPayloadEncoderNode::Start()
40 {
41 IMLOGD1("[Start] codec[%d]", mCodecType);
42
43 if (mCodecType == TextConfig::TEXT_CODEC_NONE)
44 {
45 return RESULT_INVALID_PARAM;
46 }
47
48 mNodeState = kNodeStateRunning;
49 return RESULT_SUCCESS;
50 }
51
Stop()52 void TextRtpPayloadEncoderNode::Stop()
53 {
54 IMLOGD0("[Stop]");
55 mNodeState = kNodeStateStopped;
56 }
57
IsRunTime()58 bool TextRtpPayloadEncoderNode::IsRunTime()
59 {
60 return true;
61 }
62
IsSourceNode()63 bool TextRtpPayloadEncoderNode::IsSourceNode()
64 {
65 return false;
66 }
67
OnDataFromFrontNode(ImsMediaSubType,uint8_t * data,uint32_t size,uint32_t timestamp,bool mark,uint32_t,ImsMediaSubType,uint32_t)68 void TextRtpPayloadEncoderNode::OnDataFromFrontNode(ImsMediaSubType /*subtype*/, uint8_t* data,
69 uint32_t size, uint32_t timestamp, bool mark, uint32_t /*seq*/,
70 ImsMediaSubType /*dataType*/, uint32_t /*arrivalTime*/)
71 {
72 switch (mCodecType)
73 {
74 case TextConfig::TEXT_T140:
75 case TextConfig::TEXT_T140_RED:
76 EncodeT140(data, size, timestamp, mark);
77 break;
78 default:
79 IMLOGE1("[ProcessData] invalid codec type[%d]", mCodecType);
80 break;
81 }
82 }
83
SetConfig(void * config)84 void TextRtpPayloadEncoderNode::SetConfig(void* config)
85 {
86 if (config == nullptr)
87 {
88 return;
89 }
90
91 TextConfig* pConfig = reinterpret_cast<TextConfig*>(config);
92 mCodecType = pConfig->getCodecType();
93 mRedundantPayload = pConfig->getRedundantPayload();
94 mRedundantLevel = pConfig->getRedundantLevel();
95 mKeepRedundantLevel = pConfig->getKeepRedundantLevel();
96 }
97
IsSameConfig(void * config)98 bool TextRtpPayloadEncoderNode::IsSameConfig(void* config)
99 {
100 if (config == nullptr)
101 {
102 return true;
103 }
104
105 TextConfig* pConfig = reinterpret_cast<TextConfig*>(config);
106
107 return (mCodecType == pConfig->getCodecType() &&
108 mRedundantPayload == pConfig->getRedundantPayload() &&
109 mRedundantLevel == pConfig->getRedundantLevel() &&
110 mKeepRedundantLevel == pConfig->getKeepRedundantLevel());
111 }
112
EncodeT140(uint8_t * data,uint32_t size,uint32_t timestamp,bool mark)113 void TextRtpPayloadEncoderNode::EncodeT140(
114 uint8_t* data, uint32_t size, uint32_t timestamp, bool mark)
115 {
116 (void)mark;
117
118 bool bNewMark = false;
119
120 /** The RFC 4103 defines idle period as 300 ms or more of inactivity, and also requires
121 * the RTP Marker bit to be set to one only in the first RTP packet sent after an idle period
122 * (must be set to zero in any other RTP packet for sending text).*/
123
124 if (size > 0 && timestamp > mLastTimestampSent + T140_BUFFERING_TIME)
125 {
126 bNewMark = true; // Mark will be added only after Buffering time
127 }
128
129 uint32_t codecType = mCodecType;
130
131 IMLOGD_PACKET6(IM_PACKET_LOG_PH,
132 "[EncodeT140] Size[%u], Mark[%d], RedLevel[%d], TS[%u], LastTS[%u], queue size[%u]",
133 size, bNewMark, mRedundantLevel, timestamp, mLastTimestampSent,
134 mBufferQueue.GetCount());
135
136 // check RFC4103 5.2 - send an empty T.140 at the beginning of idle period
137 if (size == 0 && mBufferQueue.GetCount() == 0)
138 {
139 codecType = TextConfig::TEXT_T140;
140 }
141
142 // Queueing and sending a text data if it's T140 Redundancy mode
143 if (codecType == TextConfig::TEXT_T140_RED)
144 {
145 // Remove overflowed data
146 while (mBufferQueue.GetCount() > mRedundantLevel)
147 {
148 mBufferQueue.Delete();
149 }
150
151 // Remove very old redundant data
152 DataEntry* pEntry = nullptr;
153
154 while (mBufferQueue.Get(&pEntry) && pEntry != nullptr)
155 {
156 /** [RFC 4103] 10.1. Registration of MIME Media Type text/t140 Required parameters:
157 * rate: The RTP timestamp clock rate, which is equal to the sampling rate. The only
158 * valid value is 1000*/
159 uint32_t nTSInterval = timestamp - pEntry->nTimestamp;
160
161 // Remove a very old redundant data
162 IMLOGD_PACKET4(IM_PACKET_LOG_PH,
163 "[EncodeT140] timestamp[%u], pEntry->timestamp[%u], nTSInterval[%d], "
164 "arrivalTime[%u]",
165 timestamp, pEntry->nTimestamp, nTSInterval, pEntry->arrivalTime);
166
167 // Check time interval and the number of remained use
168 if (nTSInterval >= PAYLOADENCODER_TEXT_MAX_REDUNDANT_INTERVAL ||
169 pEntry->arrivalTime == 0)
170 {
171 pEntry = nullptr;
172 mBufferQueue.Delete();
173 }
174 else
175 {
176 break;
177 }
178 }
179
180 // Check non-redundant data is exist
181 bool bRealData = false;
182
183 for (uint32_t i = 0; i < mBufferQueue.GetCount(); i++)
184 {
185 if (mBufferQueue.GetAt(i, &pEntry) && pEntry != nullptr)
186 {
187 if (pEntry->nBufferSize > 0)
188 {
189 IMLOGD_PACKET2(IM_PACKET_LOG_PH, "[EncodeT140] Found Data[%d/%d]", i,
190 mBufferQueue.GetCount());
191 bRealData = true;
192 break;
193 }
194 }
195 }
196
197 if (bNewMark == true)
198 {
199 if (bRealData == false)
200 {
201 // If there are redundant data only and mark is false (after IDLE period), clear
202 // buffer
203 mBufferQueue.Clear();
204 }
205 else
206 {
207 // If there are non-redundant (real) data and mark is true, set Mark False
208 // (exception handling)
209 IMLOGD0("[EncodeT140] reset marker");
210 bNewMark = false;
211 }
212 }
213
214 /** At the beginning of a TTY call, or after an idle period during a TTY call, when the
215 * device needs to send new / non-redundant text data only, it shall send it using an RTP
216 * packet that has a payload type (PT) of 111 (i.e., send in a T.140 RTP packet).*/
217 if (mKeepRedundantLevel == true)
218 {
219 // add empty redundant payload
220 uint32_t nTempRedCount = mBufferQueue.GetCount();
221 for (uint32_t indexRed = 0; indexRed < mRedundantLevel - nTempRedCount; indexRed++)
222 {
223 DataEntry nullRED = DataEntry();
224 nullRED.subtype = MEDIASUBTYPE_RTPPAYLOAD;
225 nullRED.pbBuffer = nullptr;
226 nullRED.nBufferSize = 0;
227 nullRED.nTimestamp = timestamp;
228 nullRED.arrivalTime = 1; // Remained time to be retransmitted
229 mBufferQueue.InsertAt(0, &nullRED);
230 IMLOGD0("[EncodeT140] add null red");
231 }
232 }
233
234 // Set Buffer
235 memset(mPayload, 0, MAX_RTT_LEN);
236 mBWHeader.SetBuffer(mPayload, MAX_RTT_LEN);
237 mBWPayload.SetBuffer(mPayload, MAX_RTT_LEN);
238
239 // Set a payload starting point
240 if (mBufferQueue.GetCount() > 0)
241 {
242 mBWPayload.Seek(8 + mBufferQueue.GetCount() * 32);
243 }
244
245 // Write redundant header & data to payload
246 pEntry = nullptr;
247
248 for (uint32_t i = 0; i < mBufferQueue.GetCount(); i++)
249 {
250 if (mBufferQueue.GetAt(i, &pEntry) && pEntry != nullptr)
251 {
252 uint32_t nTSInterval = timestamp - pEntry->nTimestamp;
253 mBWHeader.Write(1, 1);
254 mBWHeader.Write(mRedundantPayload, 7);
255 mBWHeader.Write(nTSInterval, 14);
256 mBWHeader.Write(pEntry->nBufferSize, 10);
257 pEntry->arrivalTime -= 1;
258
259 IMLOGD_PACKET6(IM_PACKET_LOG_PH,
260 "[EncodeT140] RED payload [%d/%d] - RemaindTime[%d], RED payload[%d], "
261 "offset[%d], data size[%d]",
262 i, mBufferQueue.GetCount(), pEntry->arrivalTime, mRedundantPayload,
263 nTSInterval, pEntry->nBufferSize);
264
265 if (pEntry->nBufferSize > 0 && pEntry->pbBuffer != nullptr)
266 {
267 mBWPayload.WriteByteBuffer(pEntry->pbBuffer, pEntry->nBufferSize * 8);
268 }
269 }
270 }
271
272 // Write primary header & data
273 {
274 mBWHeader.Write(0, 1);
275 mBWHeader.Write(mRedundantPayload, 7);
276 }
277
278 /** At the beginning of a TTY call, or after an idle period during a TTY call, when the
279 * device needs to send new / non-redundant text data only, it shall send it using an RTP
280 * packet that has a payload type (PT) of 111 (i.e., send in a T.140 RTP packet).*/
281
282 if (size > 0 && data != nullptr)
283 {
284 mBWPayload.WriteByteBuffer(data, size * 8);
285 }
286
287 IMLOGD_PACKET3(IM_PACKET_LOG_PH, "[EncodeT140] data size[%d], timestamp[%u], mark[%d]",
288 mBWPayload.GetBufferSize(), timestamp, bNewMark);
289
290 if (mBufferQueue.GetCount() > 0)
291 {
292 SendDataToRearNode(MEDIASUBTYPE_BITSTREAM_T140_RED, mPayload,
293 mBWPayload.GetBufferSize(), timestamp, bNewMark, 0);
294 }
295 else
296 {
297 SendDataToRearNode(MEDIASUBTYPE_BITSTREAM_T140, mPayload, mBWPayload.GetBufferSize(),
298 timestamp, bNewMark, 0);
299 }
300
301 // Queueing a primary data
302 DataEntry newEntry = DataEntry();
303 newEntry.subtype = MEDIASUBTYPE_RTPPAYLOAD;
304 newEntry.pbBuffer = data;
305 newEntry.nBufferSize = size;
306 newEntry.nTimestamp = timestamp;
307 newEntry.arrivalTime = static_cast<uint32_t>(std::make_unsigned_t<int8_t>(
308 mRedundantLevel)); // Remained time to be retransmitted
309 mBufferQueue.Add(&newEntry);
310 }
311 else // TEXT_T140
312 {
313 SendDataToRearNode(MEDIASUBTYPE_BITSTREAM_T140, data, size, timestamp, bNewMark, 0);
314 }
315
316 mLastTimestampSent = timestamp;
317 }