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 <TextSourceNode.h>
18 #include <TextConfig.h>
19 #include <ImsMediaTimer.h>
20 #include <ImsMediaTrace.h>
21 
22 #define RTT_MAX_CHAR_PER_BUFFERING (RTT_MAX_CHAR_PER_SEC / 3)
23 
TextSourceNode(BaseSessionCallback * callback)24 TextSourceNode::TextSourceNode(BaseSessionCallback* callback) :
25         BaseNode(callback)
26 {
27     mCodecType = TextConfig::TEXT_CODEC_NONE;
28     mRedundantLevel = 0;
29     mRedundantCount = 0;
30     mTimeLastSent = 0;
31     mSentBOM = false;
32 }
33 
~TextSourceNode()34 TextSourceNode::~TextSourceNode() {}
35 
GetNodeId()36 kBaseNodeId TextSourceNode::GetNodeId()
37 {
38     return kNodeIdTextSource;
39 }
40 
Start()41 ImsMediaResult TextSourceNode::Start()
42 {
43     IMLOGD2("[Start] codec[%d], redundant level[%d]", mCodecType, mRedundantLevel);
44 
45     if (mCodecType == TextConfig::TEXT_CODEC_NONE)
46     {
47         return RESULT_INVALID_PARAM;
48     }
49 
50     mRedundantCount = 0;
51     mTimeLastSent = 0;
52     mSentBOM = false;
53     mNodeState = kNodeStateRunning;
54     return RESULT_SUCCESS;
55 }
56 
Stop()57 void TextSourceNode::Stop()
58 {
59     IMLOGD0("[Stop]");
60     ClearDataQueue();
61     mNodeState = kNodeStateStopped;
62 }
63 
IsRunTime()64 bool TextSourceNode::IsRunTime()
65 {
66     return false;
67 }
68 
IsSourceNode()69 bool TextSourceNode::IsSourceNode()
70 {
71     return true;
72 }
73 
SetConfig(void * config)74 void TextSourceNode::SetConfig(void* config)
75 {
76     if (config == nullptr)
77     {
78         return;
79     }
80 
81     TextConfig* pConfig = reinterpret_cast<TextConfig*>(config);
82     mCodecType = pConfig->getCodecType();
83     mRedundantLevel = pConfig->getRedundantLevel();
84     mBitrate = pConfig->getBitrate();
85 }
86 
IsSameConfig(void * config)87 bool TextSourceNode::IsSameConfig(void* config)
88 {
89     if (config == nullptr)
90     {
91         return true;
92     }
93 
94     TextConfig* pConfig = reinterpret_cast<TextConfig*>(config);
95 
96     return (mCodecType == pConfig->getCodecType() &&
97             mRedundantLevel == pConfig->getRedundantLevel() && mBitrate == pConfig->getBitrate());
98 }
99 
ProcessData()100 void TextSourceNode::ProcessData()
101 {
102     // RFC 4103 recommended T.140 buffering time is 300ms
103     if (mTimeLastSent != 0 &&
104             ImsMediaTimer::GetTimeInMilliSeconds() - mTimeLastSent < T140_BUFFERING_TIME)
105     {
106         return;
107     }
108 
109     std::lock_guard<std::mutex> guard(mMutex);
110 
111     if (GetDataCount() > 0 && !mSentBOM)
112     {
113         SendBom();
114         mSentBOM = true;
115         return;
116     }
117 
118     ImsMediaSubType subtype = MEDIASUBTYPE_UNDEFINED;
119     ImsMediaSubType datatype = MEDIASUBTYPE_UNDEFINED;
120     uint8_t* data = NULL;
121     uint32_t size = 0;
122     uint32_t timestamp = 0;
123     bool mark = false;
124     uint32_t seq = 0;
125 
126     if (GetDataCount() > 0)
127     {
128         uint32_t stackedSize = 0;
129         uint32_t numCharacter = 0;
130         memset(mPayload, 0, sizeof(mPayload));
131 
132         while (GetData(&subtype, &data, &size, &timestamp, &mark, &seq, &datatype) &&
133                 (numCharacter < RTT_MAX_CHAR_PER_BUFFERING) &&
134                 (stackedSize + size < sizeof(mPayload)))
135         {
136             memcpy(mPayload + stackedSize, data, size);
137             stackedSize += size;
138             numCharacter++;
139             DeleteData();
140         }
141 
142         mTimeLastSent = ImsMediaTimer::GetTimeInMilliSeconds();
143         IMLOGD_PACKET3(IM_PACKET_LOG_TEXT, "[ProcessData] sent length[%u], queue[%d], TS[%d]",
144                 stackedSize, mDataQueue.GetCount(), mTimeLastSent);
145         SendDataToRearNode(
146                 MEDIASUBTYPE_BITSTREAM_T140, mPayload, stackedSize, mTimeLastSent, false, 0);
147         mRedundantCount = mRedundantLevel + 1;
148     }
149     else if (mRedundantCount > 0)
150     {
151         /** RFC 4103. 5.2 : In the absence of new T.140 data for transmission, an empty T140block
152          * should be sent after the selected buffering time, marking the start of an idle period
153          */
154         IMLOGD1("[ProcessData] send empty, redundant count[%d]", mRedundantCount);
155         mTimeLastSent = ImsMediaTimer::GetTimeInMilliSeconds();
156         // send default if there is no data to send
157         SendDataToRearNode(MEDIASUBTYPE_BITSTREAM_T140, nullptr, 0, mTimeLastSent, false, 0);
158         mRedundantCount--;
159     }
160 }
161 
SendRtt(const android::String8 * text)162 void TextSourceNode::SendRtt(const android::String8* text)
163 {
164     if (text == NULL || text->length() == 0)
165     {
166         IMLOGE0("[SendRtt] invalid data");
167         return;
168     }
169 
170     uint8_t* data = (uint8_t*)text->c_str();
171     uint32_t size = text->length();
172     uint32_t chunkSize = 0;
173 
174     while (size > 0)
175     {
176         // split with UTF-8
177         if (data[0] >= 0xC2 && data[0] <= 0xDF && size >= 2)
178         {
179             chunkSize = 2;
180         }
181         else if (data[0] >= 0xE0 && data[0] <= 0xEF && size >= 3)
182         {
183             chunkSize = 3;
184         }
185         else if (data[0] >= 0xF0 && data[0] <= 0xFF && size >= 4)
186         {
187             chunkSize = 4;
188         }
189         else  // 1byte
190         {
191             chunkSize = 1;
192         }
193 
194         {
195             std::lock_guard<std::mutex> guard(mMutex);
196             AddData(data, chunkSize, 0, false, 0);
197             data += chunkSize;
198             size -= chunkSize;
199         }
200     }
201 
202     IMLOGD2("[SendRtt] size[%u], queue[%d]", text->length(), mDataQueue.GetCount());
203 }
204 
SendBom()205 void TextSourceNode::SendBom()
206 {
207     IMLOGD0("[ProcessData] send BOM");
208     uint8_t bom[3] = {0xEF, 0xBB, 0xBF};
209 
210     mTimeLastSent = ImsMediaTimer::GetTimeInMilliSeconds();
211     SendDataToRearNode(MEDIASUBTYPE_BITSTREAM_T140, bom, sizeof(bom), mTimeLastSent, false, 0);
212     mRedundantCount = mRedundantLevel + 1;
213 }