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 }