• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2025 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 #ifndef LOG_TAG
17 #define LOG_TAG "HpaeSoftLink"
18 #endif
19 
20 #include "hpae_soft_link.h"
21 #ifdef ENABLE_HOOK_PCM
22 #include <thread>
23 #endif
24 #include "audio_errors.h"
25 #include "audio_utils.h"
26 #include "audio_volume.h"
27 #include "i_hpae_manager.h"
28 #include "audio_engine_log.h"
29 
30 namespace OHOS {
31 namespace AudioStandard {
32 namespace HPAE {
33 static constexpr uint32_t FIRST_SESSIONID = 90000;
34 static constexpr uint32_t MAX_VALID_SESSIONID = 99999;
35 static constexpr uint32_t OPERATION_TIMEOUT_IN_MS = 1000; // 1000ms
36 static constexpr uint32_t DEFAULT_FRAME_LEN_MS = 20;
37 static constexpr uint32_t MS_PER_SECOND = 1000;
38 static constexpr uint32_t DEFAULT_RING_BUFFER_NUM = 4;
39 static constexpr int32_t MAX_OVERFLOW_UNDERRUN_COUNT = 50; // 1s
40 uint32_t HpaeSoftLink::g_sessionId = FIRST_SESSIONID; // begin at 90000
CreateSoftLink(uint32_t sinkIdx,uint32_t sourceIdx,SoftLinkMode mode)41 std::shared_ptr<IHpaeSoftLink> IHpaeSoftLink::CreateSoftLink(uint32_t sinkIdx, uint32_t sourceIdx, SoftLinkMode mode)
42 {
43     std::shared_ptr<IHpaeSoftLink> softLink = std::make_shared<HpaeSoftLink>(sinkIdx, sourceIdx, mode);
44     CHECK_AND_RETURN_RET_LOG(softLink != nullptr, nullptr, "new HpaeSoftLink failed");
45     CHECK_AND_RETURN_RET_LOG(softLink->Init() == SUCCESS, nullptr, "HpaeSoftLink init failed");
46     return softLink;
47 }
48 
GenerateSessionId()49 uint32_t HpaeSoftLink::GenerateSessionId()
50 {
51     std::lock_guard<std::mutex> lock(sessionIdMutex_);
52     uint32_t sessionId = g_sessionId++;
53     AUDIO_INFO_LOG("hpae softlink sessionId: %{public}u", sessionId);
54     if (g_sessionId > MAX_VALID_SESSIONID) {
55         AUDIO_WARNING_LOG("sessionId is too large, reset it!");
56         g_sessionId = FIRST_SESSIONID;
57     }
58     return sessionId;
59 }
60 
HpaeSoftLink(uint32_t sinkIdx,uint32_t sourceIdx,SoftLinkMode mode)61 HpaeSoftLink::HpaeSoftLink(uint32_t sinkIdx, uint32_t sourceIdx, SoftLinkMode mode)
62     : sinkIdx_(sinkIdx), sourceIdx_(sourceIdx), linkMode_(mode), state_(HpaeSoftLinkState::NEW)
63 {
64     sinkInfo_.sinkId = sinkIdx;
65     sourceInfo_.sourceId = sourceIdx;
66 }
67 
~HpaeSoftLink()68 HpaeSoftLink::~HpaeSoftLink()
69 {
70     AUDIO_INFO_LOG("~HpaeSoftLink");
71     Release();
72 }
73 
Init()74 int32_t HpaeSoftLink::Init()
75 {
76     Trace trace("HpaeSoftLink::Init");
77     AUDIO_INFO_LOG("init in");
78     CHECK_AND_RETURN_RET_LOG(state_ != HpaeSoftLinkState::PREPARED, SUCCESS, "softlink already inited");
79     CHECK_AND_RETURN_RET_LOG(state_ == HpaeSoftLinkState::NEW, ERR_ILLEGAL_STATE, "init error state");
80     CHECK_AND_RETURN_RET_LOG(sinkIdx_ != HDI_INVALID_ID && sourceIdx_ != HDI_INVALID_ID, ERR_INVALID_PARAM,
81         "invalid sinkIdx or capturerIdx");
82 
83     int32_t ret = GetDeviceInfo();
84     CHECK_AND_RETURN_RET(ret == SUCCESS, ERR_OPERATION_FAILED);
85 
86     size_t frameBytes = sinkInfo_.channels * static_cast<size_t>(GetSizeFromFormat(sinkInfo_.format)) *
87         DEFAULT_FRAME_LEN_MS * sinkInfo_.samplingRate / MS_PER_SECOND;
88     size_t size = DEFAULT_RING_BUFFER_NUM * frameBytes;
89     bufferQueue_ = AudioRingCache::Create(size);
90     CHECK_AND_RETURN_RET_LOG(bufferQueue_ != nullptr, ERR_OPERATION_FAILED, "bufferQueue create error");
91     tempBuffer_.resize(frameBytes);
92     ret = CreateStream();
93     if (ret == SUCCESS) {
94         state_ = HpaeSoftLinkState::PREPARED;
95     }
96     return ret;
97 }
98 
GetDeviceInfo()99 int32_t HpaeSoftLink::GetDeviceInfo()
100 {
101     Trace trace("HpaeSoftLink::GetDeviceInfo");
102     AUDIO_INFO_LOG("get device info");
103     std::unique_lock<std::mutex> lock(callbackMutex_);
104     isDeviceOperationFinish_ = 0;
105     int32_t retGetSink = ERROR;
106     IHpaeManager::GetHpaeManager().GetSinkInfoByIdx(sinkIdx_,
107         [this, &retGetSink](const HpaeSinkInfo &sinkInfo, int32_t result) {
108             sinkInfo_ = sinkInfo;
109             retGetSink = result;
110             OnDeviceInfoReceived(SOFTLINK_SINK_OPERATION);
111     });
112     int32_t retGetSource = ERROR;
113     IHpaeManager::GetHpaeManager().GetSourceInfoByIdx(sourceIdx_,
114         [this, &retGetSource](const HpaeSourceInfo &sourceInfo, int32_t result) {
115             sourceInfo_ = sourceInfo;
116             retGetSource = result;
117             OnDeviceInfoReceived(SOFTLINK_SOURCE_OPERATION);
118     });
119     bool stopWaiting = callbackCV_.wait_for(lock, std::chrono::milliseconds(OPERATION_TIMEOUT_IN_MS), [this] {
120         return (isDeviceOperationFinish_ & SOFTLINK_SINK_OPERATION) &&
121             (isDeviceOperationFinish_ & SOFTLINK_SOURCE_OPERATION);
122     });
123     CHECK_AND_RETURN_RET_LOG(stopWaiting, ERROR, "get device info timeout");
124     CHECK_AND_RETURN_RET_LOG(retGetSink == SUCCESS, ERROR, "get sink info failed");
125     CHECK_AND_RETURN_RET_LOG(retGetSource == SUCCESS, ERROR, "get source info failed");
126     return SUCCESS;
127 }
128 
OnDeviceInfoReceived(const HpaeSoftLinkDeviceOperation & operation)129 void HpaeSoftLink::OnDeviceInfoReceived(const HpaeSoftLinkDeviceOperation &operation)
130 {
131     std::unique_lock<std::mutex> lock(callbackMutex_);
132     isDeviceOperationFinish_ |= operation;
133     AUDIO_INFO_LOG("GetDeviceInfo callback, operation: %{public}d", operation);
134     callbackCV_.notify_all();
135 }
136 
TransSinkInfoToStreamInfo(HpaeStreamInfo & info,const HpaeStreamClassType & streamClassType)137 void HpaeSoftLink::TransSinkInfoToStreamInfo(HpaeStreamInfo &info, const HpaeStreamClassType &streamClassType)
138 {
139     info.channels = sinkInfo_.channels;
140     info.samplingRate = sinkInfo_.samplingRate;
141     info.format = sinkInfo_.format;
142     info.channelLayout = sinkInfo_.channelLayout;
143     info.frameLen = DEFAULT_FRAME_LEN_MS * static_cast<uint32_t>(sinkInfo_.samplingRate) / MS_PER_SECOND;
144     info.streamClassType = streamClassType;
145     info.isMoveAble = false;
146     info.sessionId = GenerateSessionId();
147     if (streamClassType == HPAE_STREAM_CLASS_TYPE_PLAY) {
148         info.streamType = STREAM_VOICE_CALL;
149         info.deviceName = sinkInfo_.deviceName;
150         info.sourceType = SOURCE_TYPE_INVALID;
151         info.fadeType = DEFAULT_FADE;
152         info.effectInfo.effectMode = EFFECT_DEFAULT;
153         info.effectInfo.effectScene = SCENE_VOIP_DOWN;
154         info.effectInfo.systemVolumeType = STREAM_VOICE_CALL;
155         info.effectInfo.streamUsage = STREAM_USAGE_VOICE_COMMUNICATION;
156     } else {
157         info.streamType = STREAM_SOURCE_VOICE_CALL;
158         info.deviceName = sourceInfo_.deviceName;
159         info.sourceType = SOURCE_TYPE_MIC;
160     }
161 }
162 
CreateStream()163 int32_t HpaeSoftLink::CreateStream()
164 {
165     TransSinkInfoToStreamInfo(rendererStreamInfo_, HPAE_STREAM_CLASS_TYPE_PLAY);
166     TransSinkInfoToStreamInfo(capturerStreamInfo_, HPAE_STREAM_CLASS_TYPE_RECORD);
167 
168     uint32_t &rendererSessionId = rendererStreamInfo_.sessionId;
169     IHpaeManager::GetHpaeManager().CreateStream(rendererStreamInfo_);
170     IHpaeManager::GetHpaeManager().RegisterStatusCallback(HPAE_STREAM_CLASS_TYPE_PLAY, rendererSessionId,
171         shared_from_this());
172     IHpaeManager::GetHpaeManager().RegisterWriteCallback(rendererSessionId, shared_from_this());
173     StreamVolumeParams streamVolumeParams = {
174         rendererSessionId, rendererStreamInfo_.streamType,
175         rendererStreamInfo_.effectInfo.streamUsage, -1, -1,
176         false, AUDIOSTREAM_VOLUMEMODE_SYSTEM_GLOBAL, false
177     };
178     AudioVolume::GetInstance()->AddStreamVolume(streamVolumeParams);
179     streamStateMap_[rendererStreamInfo_.sessionId] = HpaeSoftLinkState::PREPARED;
180 
181     uint32_t &capturerSessionId = capturerStreamInfo_.sessionId;
182     IHpaeManager::GetHpaeManager().CreateStream(capturerStreamInfo_);
183     IHpaeManager::GetHpaeManager().RegisterStatusCallback(HPAE_STREAM_CLASS_TYPE_RECORD, capturerSessionId,
184         shared_from_this());
185     IHpaeManager::GetHpaeManager().RegisterReadCallback(capturerSessionId, shared_from_this());
186     streamStateMap_[capturerStreamInfo_.sessionId] = HpaeSoftLinkState::PREPARED;
187     return SUCCESS;
188 }
189 
SetVolume(float volume)190 int32_t HpaeSoftLink::SetVolume(float volume)
191 {
192     CHECK_AND_RETURN_RET_LOG(state_ != HpaeSoftLinkState::NEW, ERR_ILLEGAL_STATE, "softlink not prepared");
193     CHECK_AND_RETURN_RET_LOG(state_ != HpaeSoftLinkState::RELEASED, ERR_ILLEGAL_STATE, "softlink already release");
194     AudioVolume::GetInstance()->SetStreamVolume(rendererStreamInfo_.sessionId, volume);
195     return SUCCESS;
196 }
197 
Start()198 int32_t HpaeSoftLink::Start()
199 {
200     Trace trace("HpaeSoftLink::Start");
201     AUDIO_INFO_LOG("Start in");
202     CHECK_AND_RETURN_RET_LOG(state_ != HpaeSoftLinkState::RUNNING, SUCCESS, "softlink already start");
203     CHECK_AND_RETURN_RET_LOG(state_ == HpaeSoftLinkState::PREPARED || state_ == HpaeSoftLinkState::STOPPED,
204         ERR_ILLEGAL_STATE, "softlink not init");
205     std::unique_lock<std::mutex> lock(callbackMutex_);
206     isStreamOperationFinish_ = 0;
207     IHpaeManager::GetHpaeManager().Start(HPAE_STREAM_CLASS_TYPE_PLAY, rendererStreamInfo_.sessionId);
208     IHpaeManager::GetHpaeManager().Start(HPAE_STREAM_CLASS_TYPE_RECORD, capturerStreamInfo_.sessionId);
209     bool  stopWaiting = callbackCV_.wait_for(lock, std::chrono::milliseconds(OPERATION_TIMEOUT_IN_MS), [this] {
210         return (isStreamOperationFinish_ & SOFTLINK_RENDERER_OPERATION) &&
211             (isStreamOperationFinish_ & SOFTLINK_CAPTURER_OPERATION);
212     });
213 
214     CHECK_AND_RETURN_RET_LOG(stopWaiting, ERROR, "start timeout");
215     {
216         std::lock_guard<std::mutex> stateLock(stateMutex_);
217         CHECK_AND_RETURN_RET_LOG(streamStateMap_[rendererStreamInfo_.sessionId] == HpaeSoftLinkState::RUNNING,
218             ERROR, "start renderer[%{public}d] failed", rendererStreamInfo_.sessionId);
219         CHECK_AND_RETURN_RET_LOG(streamStateMap_[capturerStreamInfo_.sessionId] == HpaeSoftLinkState::RUNNING,
220             ERROR, "start capturer[%{public}d] failed", capturerStreamInfo_.sessionId);
221         state_ = HpaeSoftLinkState::RUNNING;
222     }
223     return SUCCESS;
224 }
225 
Stop()226 int32_t HpaeSoftLink::Stop()
227 {
228     Trace trace("HpaeSoftLink::Stop");
229     CHECK_AND_RETURN_RET_LOG(state_ != HpaeSoftLinkState::STOPPED, SUCCESS, "softlink already stop");
230     CHECK_AND_RETURN_RET_LOG(state_ == HpaeSoftLinkState::RUNNING, ERR_ILLEGAL_STATE, "softlink not init");
231     IHpaeManager::GetHpaeManager().Stop(HPAE_STREAM_CLASS_TYPE_PLAY, rendererStreamInfo_.sessionId);
232     IHpaeManager::GetHpaeManager().Stop(HPAE_STREAM_CLASS_TYPE_RECORD, capturerStreamInfo_.sessionId);
233     std::lock_guard<std::mutex> lock(stateMutex_);
234     state_ = HpaeSoftLinkState::STOPPED;
235     return SUCCESS;
236 }
237 
Release()238 int32_t HpaeSoftLink::Release()
239 {
240     Trace trace("HpaeSoftLink::Release");
241     CHECK_AND_RETURN_RET_LOG(state_ != HpaeSoftLinkState::RELEASED, SUCCESS, "softlink already release");
242     if (state_ == HpaeSoftLinkState::RUNNING) {
243         AUDIO_INFO_LOG("softlink not stop, stop before release");
244         Stop();
245     }
246     IHpaeManager::GetHpaeManager().Release(HPAE_STREAM_CLASS_TYPE_PLAY, rendererStreamInfo_.sessionId);
247     AudioVolume::GetInstance()->RemoveStreamVolume(rendererStreamInfo_.sessionId);
248     IHpaeManager::GetHpaeManager().Release(HPAE_STREAM_CLASS_TYPE_RECORD, capturerStreamInfo_.sessionId);
249     std::lock_guard<std::mutex> lock(stateMutex_);
250     state_ = HpaeSoftLinkState::RELEASED;
251     return SUCCESS;
252 }
253 
FlushRingCache()254 void HpaeSoftLink::FlushRingCache()
255 {
256     CHECK_AND_RETURN_LOG(bufferQueue_ != nullptr, "ring cache is null");
257     bufferQueue_->ResetBuffer();
258 }
259 
OnStatusUpdate(IOperation operation,uint32_t streamIndex)260 void HpaeSoftLink::OnStatusUpdate(IOperation operation, uint32_t streamIndex)
261 {
262     AUDIO_INFO_LOG("stream %{public}u recv operation:%{public}d", streamIndex, operation);
263     CHECK_AND_RETURN_LOG(operation != OPERATION_RELEASED, "stream already released");
264     CHECK_AND_RETURN_LOG(streamIndex == rendererStreamInfo_.sessionId || streamIndex == capturerStreamInfo_.sessionId,
265         "invalid streamIndex");
266     {
267         std::lock_guard<std::mutex> lock(stateMutex_);
268         if (operation == OPERATION_STARTED) {
269             streamStateMap_[streamIndex] = HpaeSoftLinkState::RUNNING;
270         } else if (operation == OPERATION_STOPPED) {
271             streamStateMap_[streamIndex] = HpaeSoftLinkState::STOPPED;
272             if (streamIndex == capturerStreamInfo_.sessionId) {
273                 FlushRingCache();
274             }
275         } else if (operation == OPERATION_RELEASED) {
276             streamStateMap_[streamIndex] = HpaeSoftLinkState::RELEASED;
277         } else {
278             return;
279         }
280     }
281     std::lock_guard<std::mutex> lock(callbackMutex_);
282     isStreamOperationFinish_ |=
283         (streamIndex == rendererStreamInfo_.sessionId ? SOFTLINK_RENDERER_OPERATION : SOFTLINK_CAPTURER_OPERATION);
284     callbackCV_.notify_all();
285 }
286 
OnStreamData(AudioCallBackStreamInfo & callbackStreamInfo)287 int32_t HpaeSoftLink::OnStreamData(AudioCallBackStreamInfo& callbackStreamInfo)
288 {
289     Trace trace("HpaeSoftLink::OnStreamData, [" +std::to_string(rendererStreamInfo_.sessionId) + "]OnWriteData");
290 #ifdef ENABLE_HOOK_PCM
291     if (sinkInfo_.adapterName == "file_io") {
292         std::this_thread::sleep_for(std::chrono::milliseconds(20)); // 20s for file_io sleep
293     }
294 #endif
295     int8_t *inputData = callbackStreamInfo.inputData;
296     size_t requestDataLen = callbackStreamInfo.requestDataLen;
297     OptResult result = bufferQueue_->GetReadableSize();
298     CHECK_AND_RETURN_RET_LOG(result.ret == OPERATION_SUCCESS, ERROR,
299         "ringBuffer get readable invalid size: %{public}zu", result.size);
300     if (result.size == 0 || result.size < requestDataLen) {
301         ++underRunCount_;
302         AUDIO_INFO_LOG("underrun[%{public}d]!, readable size is invalid, result.size[%{public}zu],"
303             "requestDataLen[%{public}zu]", underRunCount_, result.size, requestDataLen);
304     } else {
305         underRunCount_ = 0;
306     }
307     if (underRunCount_ >= MAX_OVERFLOW_UNDERRUN_COUNT) {
308         IHpaeManager::GetHpaeManager().Stop(HPAE_STREAM_CLASS_TYPE_PLAY, rendererStreamInfo_.sessionId);
309         AUDIO_WARNING_LOG("renderer[%{public}u] will stop", rendererStreamInfo_.sessionId);
310         underRunCount_ = 0;
311         return ERROR;
312     }
313     AUDIO_DEBUG_LOG("readable size: %{public}zu, requestDataLen: %{public}zu", result.size, requestDataLen);
314     result = bufferQueue_->Dequeue({reinterpret_cast<uint8_t *>(inputData), requestDataLen});
315     CHECK_AND_RETURN_RET_LOG(result.ret == OPERATION_SUCCESS, ERROR, "ringBuffer dequeue failed");
316     return SUCCESS;
317 }
318 
CopyRightToLeft(uint8_t * data,size_t size,const AudioSampleFormat & format)319 static void CopyRightToLeft(uint8_t *data, size_t size, const AudioSampleFormat &format)
320 {
321     CHECK_AND_RETURN_LOG(data != nullptr && size > 0, "error param");
322     const size_t bytesPerSample = static_cast<size_t>(GetSizeFromFormat(format));
323     const size_t frameSize = bytesPerSample * 2;
324     CHECK_AND_RETURN_LOG(size > frameSize, "invalid data size");
325     uint8_t *left = nullptr;
326     uint8_t *right = nullptr;
327     for (size_t i = 0; i < size - frameSize + 1; i += frameSize) {
328         left = data + i;
329         right = left + bytesPerSample;
330         CHECK_AND_RETURN_LOG(memcpy_s(left, bytesPerSample, right, bytesPerSample) == 0, "memcpy_s failed");
331     }
332 }
333 
OnStreamData(AudioCallBackCapturerStreamInfo & callbackStreamInfo)334 int32_t HpaeSoftLink::OnStreamData(AudioCallBackCapturerStreamInfo& callbackStreamInfo)
335 {
336     Trace trace("HpaeSoftLink::OnStreamData, [" + std::to_string(capturerStreamInfo_.sessionId) + "]OnReadData");
337 #ifdef ENABLE_HOOK_PCM
338     if (sourceInfo_.adapterName == "file_io") {
339         std::this_thread::sleep_for(std::chrono::milliseconds(20)); // 20s for file_io sleep
340     }
341 #endif
342     int8_t *outputData = callbackStreamInfo.outputData;
343     size_t requestDataLen = callbackStreamInfo.requestDataLen;
344     // todo : channel select
345     if (linkMode_ == SoftLinkMode::HEARING_AID && sinkInfo_.channels == STEREO) {
346         CopyRightToLeft(reinterpret_cast<uint8_t *>(outputData), requestDataLen, sinkInfo_.format);
347     }
348     OptResult result = bufferQueue_->GetWritableSize();
349     CHECK_AND_RETURN_RET_LOG(result.ret == OPERATION_SUCCESS, ERR_READ_FAILED,
350         "ringBuffer get writeable invalid size: %{public}zu", result.size);
351     if (result.size == 0 || result.size < requestDataLen) {
352         ++overFlowCount_;
353         AUDIO_INFO_LOG("overflow[%{public}d]!, writable size is invalid, result.size[%{public}zu],"
354             "requestDataLen[%{public}zu]", overFlowCount_, result.size, requestDataLen);
355         bufferQueue_->Dequeue({reinterpret_cast<uint8_t *>(tempBuffer_.data()), requestDataLen});
356     } else {
357         overFlowCount_ = 0;
358     }
359     if (overFlowCount_ >= MAX_OVERFLOW_UNDERRUN_COUNT) {
360         IHpaeManager::GetHpaeManager().Stop(HPAE_STREAM_CLASS_TYPE_RECORD, capturerStreamInfo_.sessionId);
361         AUDIO_WARNING_LOG("capturer[%{public}u] will stop", capturerStreamInfo_.sessionId);
362         overFlowCount_ = 0;
363         return ERROR;
364     }
365     AUDIO_DEBUG_LOG("writable size: %{public}zu, requestDataLen: %{public}zu", result.size, requestDataLen);
366     result = bufferQueue_->Enqueue({reinterpret_cast<uint8_t *>(outputData), requestDataLen});
367     CHECK_AND_RETURN_RET_LOG(result.ret == OPERATION_SUCCESS, ERROR, "ringBuffer enqueue failed");
368     return SUCCESS;
369 }
370 
371 // for test
GetStreamStateById(uint32_t sessionId)372 HpaeSoftLinkState HpaeSoftLink::GetStreamStateById(uint32_t sessionId)
373 {
374     std::lock_guard<std::mutex> lock(stateMutex_);
375     CHECK_AND_RETURN_RET_LOG(streamStateMap_.find(sessionId) != streamStateMap_.end(), HpaeSoftLinkState::INVALID,
376         "invalid param");
377     return streamStateMap_[sessionId];
378 }
379 } // namespace HPAE
380 } // namespace AudioStandard
381 } // namespace OHOS
382