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