• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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                 DHLOGD("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     DHLOGD("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     }
309     return DH_SUCCESS;
310 }
311 
AudioCaptureRelease()312 int32_t DAudioEchoCannelManager::AudioCaptureRelease()
313 {
314     if (audioCapturer_ != nullptr && !audioCapturer_->Release()) {
315         DHLOGE("Audio capturer release failed.");
316     }
317     audioCapturer_ = nullptr;
318     DHLOGI("Audio capturer release end.");
319     return DH_SUCCESS;
320 }
321 
LoadAecProcessor()322 int32_t DAudioEchoCannelManager::LoadAecProcessor()
323 {
324     DHLOGI("LoadAecEffectProcessor enter");
325     if (ECHOCANNEL_SO_NAME.length() > PATH_MAX) {
326         DHLOGE("File open failed");
327         return ERR_DH_AUDIO_NULLPTR;
328     }
329     aecHandler_ = dlopen(ECHOCANNEL_SO_NAME.c_str(), RTLD_LAZY | RTLD_NODELETE);
330     CHECK_AND_RETURN_RET_LOG(aecHandler_ == nullptr, ERR_DH_AUDIO_NULLPTR, "dlOpen error.");
331     AecEffectProcessorProvider getAecEffectProcessorFunc = (AecEffectProcessorProvider)dlsym(aecHandler_,
332         GET_AEC_EFFECT_PROCESSOR_FUNC.c_str());
333     if (getAecEffectProcessorFunc == nullptr) {
334         DHLOGE("AecEffectProcessor function handler is null, failed reason : %s", dlerror());
335         dlclose(aecHandler_);
336         aecHandler_ = nullptr;
337         return ERR_DH_AUDIO_NULLPTR;
338     }
339     aecProcessor_ = getAecEffectProcessorFunc();
340     DHLOGI("LoadAecEffectProcessor exit");
341     return DH_SUCCESS;
342 }
343 
UnLoadAecProcessor()344 void DAudioEchoCannelManager::UnLoadAecProcessor()
345 {
346     if (aecHandler_ != nullptr) {
347         dlclose(aecHandler_);
348         aecHandler_ = nullptr;
349     }
350     aecProcessor_ = nullptr;
351 }
352 
InitAecProcessor()353 int32_t DAudioEchoCannelManager::InitAecProcessor()
354 {
355     AudioCommonParam param;
356     param.sampleRate = SAMPLE_RATE_48000;
357     param.channelMask = STEREO;
358     param.bitFormat = SAMPLE_S16LE;
359     param.frameSize = FRAME_SIZE_NORMAL;
360     if (aecProcessor_ == nullptr) {
361         DHLOGE("Aec processor is nullptr.");
362         return ERR_DH_AUDIO_NULLPTR;
363     }
364     int32_t ret = aecProcessor_->Init(aecProcessor_, param);
365     if (ret != DH_SUCCESS) {
366         DHLOGE("Aec effect processor init fail. errorcode: %{public}d", ret);
367         return ERR_DH_AUDIO_FAILED;
368     }
369     DHLOGI("Aec effect process init success.");
370     return DH_SUCCESS;
371 }
372 
StartAecProcessor()373 int32_t DAudioEchoCannelManager::StartAecProcessor()
374 {
375     if (aecProcessor_ == nullptr) {
376         DHLOGE("Aec process is nullptr.");
377         return ERR_DH_AUDIO_NULLPTR;
378     }
379     int32_t ret = aecProcessor_->StartUp(aecProcessor_);
380     if (ret != DH_SUCCESS) {
381         DHLOGE("Aec effect process start fail. errorcode:%{public}d", ret);
382         return ERR_DH_AUDIO_FAILED;
383     }
384     if (!isAecRunning_.load()) {
385         isAecRunning_.store(true);
386         aecProcessThread_ = std::thread([this]() { this->AecProcessData(); });
387     }
388     DHLOGI("Aec effect process start success.");
389     return DH_SUCCESS;
390 }
391 
StopAecProcessor()392 int32_t DAudioEchoCannelManager::StopAecProcessor()
393 {
394     if (aecProcessor_ == nullptr) {
395         DHLOGE("Aec processor is nullptr.");
396         return ERR_DH_AUDIO_NULLPTR;
397     }
398     int32_t ret = aecProcessor_->ShutDown(aecProcessor_);
399     if (ret != DH_SUCCESS) {
400         DHLOGE("Aec effect process stop fail. errorcode:%{public}d", ret);
401         return ERR_DH_AUDIO_FAILED;
402     }
403     if (isAecRunning_.load()) {
404         DHLOGI("Stop the aec process thread.");
405         isAecRunning_.store(false);
406         if (aecProcessThread_.joinable()) {
407             aecProcessThread_.join();
408         }
409     }
410     DHLOGI("Aec effect process stop success.");
411     return DH_SUCCESS;
412 }
413 
ReleaseAecProcessor()414 int32_t DAudioEchoCannelManager::ReleaseAecProcessor()
415 {
416     if (isAecRunning_.load()) {
417         DHLOGI("Stop the aec process thread.");
418         isAecRunning_.store(false);
419         if (aecProcessThread_.joinable()) {
420             aecProcessThread_.join();
421         }
422     }
423     if (aecProcessor_ != nullptr) {
424         if (aecProcessor_->Release(aecProcessor_) != DH_SUCCESS) {
425             DHLOGE("Aec effect process release fail.");
426         }
427         aecProcessor_ = nullptr;
428     }
429     DHLOGI("Aec effect process release success.");
430     return DH_SUCCESS;
431 }
432 } // namespace DistributedHardware
433 } // namespace OHOS
434