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