1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "daudio_echo_cannel_manager.h"
17
18 #include <dlfcn.h>
19
20 #include "daudio_constants.h"
21 #include "daudio_errorcode.h"
22 #include "daudio_log.h"
23 #include "daudio_util.h"
24
25 #include <securec.h>
26
27 #undef DH_LOG_TAG
28 #define DH_LOG_TAG "DAudioEchoCannelManager"
29
30 using namespace OHOS::AudioStandard;
31 namespace OHOS {
32 namespace DistributedHardware {
33 using AecEffectProcessorProvider = AecEffector *(*)();
34
35 const std::string ECHOCANNEL_SO_NAME = "libdaudio_aec_effect_processor.z.so";
36 const std::string GET_AEC_EFFECT_PROCESSOR_FUNC = "GetAecEffector";
37 const int32_t FRAME_SIZE_NORMAL = 3840;
38
DAudioEchoCannelManager()39 DAudioEchoCannelManager::DAudioEchoCannelManager()
40 {
41 DHLOGD("Distributed audio echo cannel manager constructed.");
42 }
43
~DAudioEchoCannelManager()44 DAudioEchoCannelManager::~DAudioEchoCannelManager()
45 {
46 DHLOGD("Distributed audio echo cannel manager destructed.");
47 }
48
SetUp(const AudioCommonParam param,const std::shared_ptr<IAudioDataTransCallback> & callback)49 int32_t DAudioEchoCannelManager::SetUp(const AudioCommonParam param,
50 const std::shared_ptr<IAudioDataTransCallback> &callback)
51 {
52 (void) param;
53 devCallback_ = callback;
54 DHLOGI("SetUp EchoCannel.");
55
56 if (!isCircuitStartRunning_.load()) {
57 isCircuitStartRunning_.store(true);
58 circuitStartThread_ = std::thread([this]() { this->CircuitStart(); });
59 circuitStartThread_.detach();
60 DHLOGI("circuitStartThread_ is on.");
61 }
62 DumpFileUtil::OpenDumpFile(DUMP_SERVER_PARA, DUMP_DAUDIO_AEC_REFERENCE_FILENAME, &dumpFileRef_);
63 DumpFileUtil::OpenDumpFile(DUMP_SERVER_PARA, DUMP_DAUDIO_AEC_RECORD_FILENAME, &dumpFileRec_);
64 DumpFileUtil::OpenDumpFile(DUMP_SERVER_PARA, DUMP_DAUDIO_AEC_CIRCUIT_FILENAME, &dumpFileCir_);
65 DumpFileUtil::OpenDumpFile(DUMP_SERVER_PARA, DUMP_DAUDIO_AEC_AFTER_PROCESS_FILENAME, &dumpFileAft_);
66 return DH_SUCCESS;
67 }
68
CircuitStart()69 void DAudioEchoCannelManager::CircuitStart()
70 {
71 DHLOGI("Start CircuitStart thread.");
72 int32_t ret = AudioCaptureSetUp();
73 CHECK_AND_RETURN_LOG(ret != DH_SUCCESS, "Init Get Reference error. ret: %{public}d.", ret);
74 ret = AudioCaptureStart();
75 CHECK_AND_RETURN_LOG(ret != DH_SUCCESS, "Start Get Reference error. ret: %{public}d.", ret);
76 ret = LoadAecProcessor();
77 CHECK_AND_RETURN_LOG(ret != DH_SUCCESS, "LoadAECProcessor error.");
78 ret = InitAecProcessor();
79 CHECK_AND_RETURN_LOG(ret != DH_SUCCESS, "Init Aec Processor error. ret: %{public}d.", ret);
80 ret = StartAecProcessor();
81 CHECK_AND_RETURN_LOG(ret != DH_SUCCESS, "Start Aec Processor error. ret: %{public}d.", ret);
82 DHLOGI("CircuitStart thread end success.");
83 }
84
Start()85 int32_t DAudioEchoCannelManager::Start()
86 {
87 DHLOGI("Start EchoCannel.");
88 int32_t ret = StartAecProcessor();
89 CHECK_AND_RETURN_RET_LOG(ret != DH_SUCCESS, ret, "Start Aec Processor error. ret: %{public}d.", ret);
90
91 isStarted.store(true);
92 DHLOGI("Start EchoCannel success.");
93 return DH_SUCCESS;
94 }
95
Stop()96 int32_t DAudioEchoCannelManager::Stop()
97 {
98 DHLOGI("Stop EchoCannel.");
99 isStarted.store(false);
100 int32_t ret = StopAecProcessor();
101 CHECK_AND_RETURN_RET_LOG(ret != DH_SUCCESS, ret, "Stop Aec Processor error. ret: %{public}d.", ret);
102 ret = AudioCaptureStop();
103 CHECK_AND_RETURN_RET_LOG(ret != DH_SUCCESS, ret, "Stop Get Reference error. ret: %{public}d.", ret);
104 DHLOGI("Stop EchoCannel success.");
105 return DH_SUCCESS;
106 }
107
Release()108 int32_t DAudioEchoCannelManager::Release()
109 {
110 DHLOGI("Release EchoCannel.");
111 int32_t ret = AudioCaptureRelease();
112 CHECK_AND_RETURN_RET_LOG(ret != DH_SUCCESS, ret, "Release Get Reference error. ret: %{public}d.", ret);
113
114 ret = ReleaseAecProcessor();
115 CHECK_AND_RETURN_RET_LOG(ret != DH_SUCCESS, ret, "Release Aec Processor error. ret: %{public}d.", ret);
116 UnLoadAecProcessor();
117 DumpFileUtil::CloseDumpFile(&dumpFileRef_);
118 DumpFileUtil::CloseDumpFile(&dumpFileRec_);
119 DumpFileUtil::CloseDumpFile(&dumpFileAft_);
120 DumpFileUtil::CloseDumpFile(&dumpFileCir_);
121 isStarted.store(false);
122 isCircuitStartRunning_.store(false);
123 return DH_SUCCESS;
124 }
125
OnMicDataReceived(const std::shared_ptr<AudioData> & pipeInData)126 int32_t DAudioEchoCannelManager::OnMicDataReceived(const std::shared_ptr<AudioData> &pipeInData)
127 {
128 DHLOGD("GetMicDataBeforeAec.");
129 CHECK_AND_RETURN_RET_LOG(devCallback_ == nullptr, ERR_DH_AUDIO_NULLPTR, "callback is nullptr.");
130 if (isStarted.load()) {
131 CHECK_AND_RETURN_RET_LOG(pipeInData == nullptr, ERR_DH_AUDIO_NULLPTR, "pipeInData is nullptr.");
132 auto micOutData = std::make_shared<AudioData>(pipeInData->Size());
133 int32_t ret = ProcessMicData(pipeInData, micOutData);
134 if (ret != DH_SUCCESS) {
135 DHLOGE("Mic data call processor error. ret : %{public}d.", ret);
136 devCallback_->OnDecodeTransDataDone(pipeInData);
137 return ERR_DH_AUDIO_FAILED;
138 }
139 DumpFileUtil::WriteDumpFile(dumpFileRec_, static_cast<void *>(pipeInData->Data()), pipeInData->Size());
140 DumpFileUtil::WriteDumpFile(dumpFileAft_, static_cast<void *>(micOutData->Data()), micOutData->Size());
141 devCallback_->OnDecodeTransDataDone(micOutData);
142 } else {
143 devCallback_->OnDecodeTransDataDone(pipeInData);
144 }
145 return DH_SUCCESS;
146 }
147
ProcessMicData(const std::shared_ptr<AudioData> & pipeInData,std::shared_ptr<AudioData> & micOutData)148 int32_t DAudioEchoCannelManager::ProcessMicData(const std::shared_ptr<AudioData> &pipeInData,
149 std::shared_ptr<AudioData> &micOutData)
150 {
151 DHLOGD("Process mic data.");
152 uint8_t *micOutDataExt = nullptr;
153 CHECK_AND_RETURN_RET_LOG(pipeInData == nullptr, ERR_DH_AUDIO_NULLPTR, "pipeInData is nullptr.");
154 CHECK_AND_RETURN_RET_LOG(micOutData == nullptr, ERR_DH_AUDIO_NULLPTR, "micOutData is nullptr.");
155 CHECK_AND_RETURN_RET_LOG(aecProcessor_ == nullptr, ERR_DH_AUDIO_NULLPTR, "aec processor is nullptr.");
156 int32_t ret = aecProcessor_->OnSendOriginData(aecProcessor_, pipeInData->Data(),
157 pipeInData->Size(), StreamType::MIC1, &micOutDataExt);
158 if (ret != DH_SUCCESS || micOutDataExt == nullptr) {
159 DHLOGI("aec effect process pipeInReferenceData fail. errocode:%{public}d", ret);
160 return ERR_DH_AUDIO_FAILED;
161 }
162 if (memcpy_s(micOutData->Data(), micOutData->Size(), micOutDataExt, pipeInData->Size()) != EOK) {
163 DHLOGE("copy mic data after aec error.");
164 ret = ERR_DH_AUDIO_FAILED;
165 } else {
166 ret = DH_SUCCESS;
167 }
168 if (micOutDataExt != nullptr) {
169 free(micOutDataExt);
170 micOutDataExt = nullptr;
171 }
172 return ret;
173 }
174
AecProcessData()175 void DAudioEchoCannelManager::AecProcessData()
176 {
177 DHLOGI("Start the aec process thread.");
178 if (pthread_setname_np(pthread_self(), AECTHREADNAME) != DH_SUCCESS) {
179 DHLOGE("aec process thread setname failed.");
180 }
181 DHLOGI("Begin the aec process thread. refDataQueueSize: %{public}zu.", refDataQueue_.size());
182 while (aecProcessor_ != nullptr && isAecRunning_.load()) {
183 std::shared_ptr<AudioData> refInData = nullptr;
184 uint8_t *refOutDataExt = nullptr;
185 {
186 std::unique_lock<std::mutex> refLck(refQueueMtx_);
187 refQueueCond_.wait_for(refLck, std::chrono::milliseconds(COND_WAIT_TIME_MS),
188 [this]() { return !refDataQueue_.empty(); });
189 if (refDataQueue_.empty()) {
190 DHLOGE("refDataQueue is Empty.");
191 continue;
192 }
193 refInData = refDataQueue_.front();
194 refDataQueue_.pop();
195 DHLOGD("Pop new echo ref data, ref dataqueue size: %{public}zu.", refDataQueue_.size());
196 }
197 DumpFileUtil::WriteDumpFile(dumpFileRef_, static_cast<void *>(refInData->Data()), refInData->Size());
198 int32_t ret = aecProcessor_->OnSendOriginData(aecProcessor_, refInData->Data(), refInData->Size(),
199 StreamType::REF, &refOutDataExt);
200 if (ret != DH_SUCCESS) {
201 DHLOGE("aec effect process pipeInReferenceData fail. errocode:%{public}d", ret);
202 }
203 if (!isStarted.load()) {
204 isStarted.store(true);
205 }
206 if (refOutDataExt != nullptr) {
207 free(refOutDataExt);
208 refOutDataExt = nullptr;
209 }
210 }
211 DHLOGI("the aec process thread exit.");
212 return;
213 }
214
OnReadData(size_t length)215 void DAudioEchoCannelManager::OnReadData(size_t length)
216 {
217 BufferDesc bufDesc;
218 if (audioCapturer_ == nullptr) {
219 DHLOGE("audioCapturer is nullptr.");
220 return;
221 }
222 int32_t ret = audioCapturer_->GetBufferDesc(bufDesc);
223 if (ret != 0 || bufDesc.buffer == nullptr || bufDesc.bufLength == 0) {
224 DHLOGE("Get buffer desc failed. On read data.");
225 return;
226 }
227 DHLOGD("Get echo ref data. size: %{public}zu.", bufDesc.bufLength);
228 std::shared_ptr<AudioData> audioData = std::make_shared<AudioData>(bufDesc.bufLength);
229 if (audioData->Capacity() != bufDesc.bufLength) {
230 DHLOGE("Audio data length is not equal to buflength. datalength: %{public}zu, bufLength: %{public}zu",
231 audioData->Capacity(), bufDesc.bufLength);
232 }
233 if (memcpy_s(audioData->Data(), audioData->Capacity(), bufDesc.buffer, bufDesc.bufLength) != EOK) {
234 DHLOGE("Copy audio data failed.");
235 }
236
237 audioCapturer_->Enqueue(bufDesc);
238 DumpFileUtil::WriteDumpFile(dumpFileCir_, static_cast<void *>(audioData->Data()), audioData->Size());
239 std::lock_guard<std::mutex> lock(refQueueMtx_);
240 while (refDataQueue_.size() > REF_QUEUE_MAX_SIZE) {
241 DHLOGE("Ref Data queue overflow. max size : 10");
242 refDataQueue_.pop();
243 }
244 refDataQueue_.push(audioData);
245 DHLOGI("Push new echo ref data, buf len: %{public}zu.", refDataQueue_.size());
246 refQueueCond_.notify_all();
247 }
248
AudioCaptureSetUp()249 int32_t DAudioEchoCannelManager::AudioCaptureSetUp()
250 {
251 if (audioCapturer_ != nullptr) {
252 DHLOGI("Audio capture has been created. no need to setup.");
253 return DH_SUCCESS;
254 }
255 AudioStandard::AudioCapturerOptions capturerOptions = {
256 {
257 AudioStandard::AudioSamplingRate::SAMPLE_RATE_48000,
258 AudioStandard::AudioEncodingType::ENCODING_PCM,
259 AudioStandard::AudioSampleFormat::SAMPLE_S16LE,
260 AudioStandard::AudioChannel::STEREO,
261 },
262 {
263 AudioStandard::SourceType::SOURCE_TYPE_PLAYBACK_CAPTURE,
264 AudioStandard::STREAM_FLAG_NORMAL,
265 }
266 };
267 capturerOptions.playbackCaptureConfig.filterOptions.usages.push_back(AudioStandard::
268 StreamUsage::STREAM_USAGE_MEDIA);
269 capturerOptions.playbackCaptureConfig.filterOptions.usages.push_back(AudioStandard::
270 StreamUsage::STREAM_USAGE_UNKNOWN);
271 capturerOptions.playbackCaptureConfig.filterOptions.usages.push_back(AudioStandard::
272 StreamUsage::STREAM_USAGE_VOICE_COMMUNICATION);
273 capturerOptions.playbackCaptureConfig.filterOptions.usages.push_back(AudioStandard::
274 StreamUsage::STREAM_USAGE_MOVIE);
275
276 audioCapturer_ = AudioStandard::AudioCapturer::Create(capturerOptions);
277 CHECK_AND_RETURN_RET_LOG(audioCapturer_ == nullptr, ERR_DH_AUDIO_FAILED, "Audio capture create failed.");
278
279 int32_t ret = audioCapturer_->SetCaptureMode(CAPTURE_MODE_CALLBACK);
280 CHECK_AND_RETURN_RET_LOG(ret != DH_SUCCESS, ret, "Set capture mode callback fail, ret %{public}d.", ret);
281 ret = audioCapturer_->SetCapturerReadCallback(shared_from_this());
282 CHECK_AND_RETURN_RET_LOG(ret != DH_SUCCESS, ret, "Set capture data callback fail, ret %{public}d.", ret);
283 DHLOGI("Audio capturer create success");
284 return DH_SUCCESS;
285 }
286
AudioCaptureStart()287 int32_t DAudioEchoCannelManager::AudioCaptureStart()
288 {
289 if (audioCapturer_ == nullptr) {
290 DHLOGE("Audio capturer is nullptr start.");
291 return ERR_DH_AUDIO_FAILED;
292 }
293 if (!audioCapturer_->Start()) {
294 DHLOGE("Audio capturer start failed.");
295 return ERR_DH_AUDIO_FAILED;
296 }
297 return DH_SUCCESS;
298 }
299
AudioCaptureStop()300 int32_t DAudioEchoCannelManager::AudioCaptureStop()
301 {
302 if (audioCapturer_ == nullptr) {
303 DHLOGE("Audio capturer is nullptr stop.");
304 return ERR_DH_AUDIO_FAILED;
305 }
306 if (!audioCapturer_->Stop()) {
307 DHLOGE("Audio capturer stop failed.");
308 return ERR_DH_AUDIO_FAILED;
309 }
310 return DH_SUCCESS;
311 }
312
AudioCaptureRelease()313 int32_t DAudioEchoCannelManager::AudioCaptureRelease()
314 {
315 if (audioCapturer_ != nullptr && !audioCapturer_->Release()) {
316 DHLOGE("Audio capturer release failed.");
317 }
318 audioCapturer_ = nullptr;
319 DHLOGI("Audio capturer release end.");
320 return DH_SUCCESS;
321 }
322
LoadAecProcessor()323 int32_t DAudioEchoCannelManager::LoadAecProcessor()
324 {
325 DHLOGI("LoadAecEffectProcessor enter");
326 if (ECHOCANNEL_SO_NAME.length() > PATH_MAX) {
327 DHLOGE("File open failed");
328 return ERR_DH_AUDIO_NULLPTR;
329 }
330 aecHandler_ = dlopen(ECHOCANNEL_SO_NAME.c_str(), RTLD_LAZY | RTLD_NODELETE);
331 CHECK_AND_RETURN_RET_LOG(aecHandler_ == nullptr, ERR_DH_AUDIO_NULLPTR, "dlOpen error.");
332 AecEffectProcessorProvider getAecEffectProcessorFunc = (AecEffectProcessorProvider)dlsym(aecHandler_,
333 GET_AEC_EFFECT_PROCESSOR_FUNC.c_str());
334 if (getAecEffectProcessorFunc == nullptr) {
335 DHLOGE("AecEffectProcessor function handler is null, failed reason : %s", dlerror());
336 dlclose(aecHandler_);
337 aecHandler_ = nullptr;
338 return ERR_DH_AUDIO_NULLPTR;
339 }
340 aecProcessor_ = getAecEffectProcessorFunc();
341 DHLOGI("LoadAecEffectProcessor exit");
342 return DH_SUCCESS;
343 }
344
UnLoadAecProcessor()345 void DAudioEchoCannelManager::UnLoadAecProcessor()
346 {
347 if (aecHandler_ != nullptr) {
348 dlclose(aecHandler_);
349 aecHandler_ = nullptr;
350 }
351 aecProcessor_ = nullptr;
352 }
353
InitAecProcessor()354 int32_t DAudioEchoCannelManager::InitAecProcessor()
355 {
356 AudioCommonParam param;
357 param.sampleRate = SAMPLE_RATE_48000;
358 param.channelMask = STEREO;
359 param.bitFormat = SAMPLE_S16LE;
360 param.frameSize = FRAME_SIZE_NORMAL;
361 if (aecProcessor_ == nullptr) {
362 DHLOGE("Aec processor is nullptr.");
363 return ERR_DH_AUDIO_NULLPTR;
364 }
365 int32_t ret = aecProcessor_->Init(aecProcessor_, param);
366 if (ret != DH_SUCCESS) {
367 DHLOGE("Aec effect processor init fail. errorcode: %{public}d", ret);
368 return ERR_DH_AUDIO_FAILED;
369 }
370 DHLOGI("Aec effect process init success.");
371 return DH_SUCCESS;
372 }
373
StartAecProcessor()374 int32_t DAudioEchoCannelManager::StartAecProcessor()
375 {
376 if (aecProcessor_ == nullptr) {
377 DHLOGE("Aec process is nullptr.");
378 return ERR_DH_AUDIO_NULLPTR;
379 }
380 int32_t ret = aecProcessor_->StartUp(aecProcessor_);
381 if (ret != DH_SUCCESS) {
382 DHLOGE("Aec effect process start fail. errorcode:%{public}d", ret);
383 return ERR_DH_AUDIO_FAILED;
384 }
385 if (!isAecRunning_.load()) {
386 isAecRunning_.store(true);
387 aecProcessThread_ = std::thread([this]() { this->AecProcessData(); });
388 }
389 DHLOGI("Aec effect process start success.");
390 return DH_SUCCESS;
391 }
392
StopAecProcessor()393 int32_t DAudioEchoCannelManager::StopAecProcessor()
394 {
395 if (aecProcessor_ == nullptr) {
396 DHLOGE("Aec processor is nullptr.");
397 return ERR_DH_AUDIO_NULLPTR;
398 }
399 int32_t ret = aecProcessor_->ShutDown(aecProcessor_);
400 if (ret != DH_SUCCESS) {
401 DHLOGE("Aec effect process stop fail. errorcode:%{public}d", ret);
402 return ERR_DH_AUDIO_FAILED;
403 }
404 if (isAecRunning_.load()) {
405 DHLOGI("Stop the aec process thread.");
406 isAecRunning_.store(false);
407 if (aecProcessThread_.joinable()) {
408 aecProcessThread_.join();
409 }
410 }
411 DHLOGI("Aec effect process stop success.");
412 return DH_SUCCESS;
413 }
414
ReleaseAecProcessor()415 int32_t DAudioEchoCannelManager::ReleaseAecProcessor()
416 {
417 if (isAecRunning_.load()) {
418 DHLOGI("Stop the aec process thread.");
419 isAecRunning_.store(false);
420 if (aecProcessThread_.joinable()) {
421 aecProcessThread_.join();
422 }
423 }
424 if (aecProcessor_ != nullptr) {
425 if (aecProcessor_->Release(aecProcessor_) != DH_SUCCESS) {
426 DHLOGE("Aec effect process release fail.");
427 }
428 aecProcessor_ = nullptr;
429 }
430 DHLOGI("Aec effect process release success.");
431 return DH_SUCCESS;
432 }
433 } // namespace DistributedHardware
434 } // namespace OHOS
435