• 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 "HpaeSinkOutputNode"
18 #endif
19 
20 #include <hpae_sink_output_node.h>
21 #include "audio_errors.h"
22 #include <iostream>
23 #include "hpae_format_convert.h"
24 #include "audio_utils.h"
25 #include "hpae_node_common.h"
26 #include "audio_engine_log.h"
27 
28 namespace OHOS {
29 namespace AudioStandard {
30 namespace HPAE {
31 namespace {
32 constexpr uint32_t SLEEP_TIME_IN_US = 20000;
33 static constexpr int64_t WAIT_CLOSE_PA_TIME = 4; // 4s
34 static constexpr int64_t MONITOR_CLOSE_PA_TIME = 5 * 60; // 5m
35 static constexpr int64_t TIME_IN_US = 1000000;
36 static constexpr uint64_t MS_PER_FRAME = 20; // 20ms
37 }
38 
HpaeSinkOutputNode(HpaeNodeInfo & nodeInfo)39 HpaeSinkOutputNode::HpaeSinkOutputNode(HpaeNodeInfo &nodeInfo)
40     : HpaeNode(nodeInfo),
41       renderFrameData_(nodeInfo.frameLen * nodeInfo.channels * GetSizeFromFormat(nodeInfo.format)),
42       interleveData_(nodeInfo.frameLen * nodeInfo.channels)
43 {
44     AUDIO_INFO_LOG("HpaeSinkOutputNode name is %{public}s", sinkOutAttr_.adapterName.c_str());
45 #ifdef ENABLE_HIDUMP_DFX
46     SetNodeName("hpaeSinkOutputNode");
47     if (auto callback = GetNodeStatusCallback().lock()) {
48         callback->OnNotifyDfxNodeInfo(true, 0, GetNodeInfo());
49     }
50 #endif
51 }
52 
~HpaeSinkOutputNode()53 HpaeSinkOutputNode::~HpaeSinkOutputNode()
54 {
55 #ifdef ENABLE_HIDUMP_DFX
56     AUDIO_INFO_LOG("NodeId: %{public}u NodeName: %{public}s destructed.",
57         GetNodeId(), GetNodeName().c_str());
58 #endif
59 }
60 
HandleRemoteTiming()61 void HpaeSinkOutputNode::HandleRemoteTiming()
62 {
63     CHECK_AND_RETURN(GetDeviceClass() == "remote");
64     auto now = std::chrono::high_resolution_clock::now();
65     remoteTimePoint_ += std::chrono::milliseconds(20);  // 20ms frameLen, need optimize
66     if (remoteTimePoint_ > now) {
67         remoteSleepTime_ = std::chrono::duration_cast<std::chrono::milliseconds>(remoteTimePoint_ - now);
68     } else {
69         remoteSleepTime_ = std::chrono::milliseconds(0);
70     }
71     std::this_thread::sleep_for(remoteSleepTime_);
72     AUDIO_DEBUG_LOG("remoteSleepTime_ %{public}lld", remoteSleepTime_.count());
73 }
74 
DoProcess()75 void HpaeSinkOutputNode::DoProcess()
76 {
77     Trace trace("HpaeSinkOutputNode::DoProcess " + GetTraceInfo());
78     if (audioRendererSink_ == nullptr) {
79         AUDIO_WARNING_LOG("audioRendererSink_ is nullptr sessionId: %{public}u", GetSessionId());
80         return;
81     }
82 
83     std::vector<HpaePcmBuffer *> &outputVec = inputStream_.ReadPreOutputData();
84     CHECK_AND_RETURN(!outputVec.empty());
85     HpaePcmBuffer *outputData = outputVec.front();
86     HandlePaPower(outputData);
87     ConvertFromFloat(
88         GetBitWidth(), GetChannelCount() * GetFrameLen(), outputData->GetPcmDataBuffer(), renderFrameData_.data());
89     uint64_t writeLen = 0;
90     char *renderFrameData = (char *)renderFrameData_.data();
91 
92 #ifdef ENABLE_HOOK_PCM
93     HighResolutionTimer timer;
94     timer.Start();
95     intervalTimer_.Stop();
96 #endif
97     HandleHapticParam(renderFrameTimes_);
98     renderFrameTimes_ += MS_PER_FRAME;
99     auto ret = audioRendererSink_->RenderFrame(*renderFrameData, renderFrameData_.size(), writeLen);
100     if (ret != SUCCESS || writeLen != renderFrameData_.size()) {
101         AUDIO_ERR_LOG("HpaeSinkOutputNode: RenderFrame failed");
102         if (GetDeviceClass() != "remote") {
103             periodTimer_.Stop();
104             uint64_t usedTimeUs = periodTimer_.Elapsed<std::chrono::microseconds>();
105             usleep(SLEEP_TIME_IN_US > usedTimeUs ? SLEEP_TIME_IN_US - usedTimeUs : 0);
106         }
107     }
108     periodTimer_.Start();
109     HandleRemoteTiming(); // used to control remote RenderFrame tempo.
110 #ifdef ENABLE_HOOK_PCM
111     timer.Stop();
112     int64_t elapsed = timer.Elapsed();
113     AUDIO_DEBUG_LOG("HpaeSinkOutputNode :name %{public}s, RenderFrame elapsed time: %{public}" PRId64 " ms",
114         sinkOutAttr_.adapterName.c_str(),
115         elapsed);
116     intervalTimer_.Start();
117 #endif
118     return;
119 }
120 
GetRenderFrameData(void)121 const char *HpaeSinkOutputNode::GetRenderFrameData(void)
122 {
123     return renderFrameData_.data();
124 }
125 
Reset()126 bool HpaeSinkOutputNode::Reset()
127 {
128     const auto preOutputMap = inputStream_.GetPreOutputMap();
129     for (const auto &preOutput : preOutputMap) {
130         OutputPort<HpaePcmBuffer *> *output = preOutput.first;
131         inputStream_.DisConnect(output);
132     }
133     return true;
134 }
135 
ResetAll()136 bool HpaeSinkOutputNode::ResetAll()
137 {
138     const auto preOutputMap = inputStream_.GetPreOutputMap();
139     for (const auto &preOutput : preOutputMap) {
140         OutputPort<HpaePcmBuffer *> *output = preOutput.first;
141         std::shared_ptr<HpaeNode> hpaeNode = preOutput.second;
142         if (hpaeNode->ResetAll()) {
143             inputStream_.DisConnect(output);
144         }
145     }
146     return true;
147 }
148 
Connect(const std::shared_ptr<OutputNode<HpaePcmBuffer * >> & preNode)149 void HpaeSinkOutputNode::Connect(const std::shared_ptr<OutputNode<HpaePcmBuffer *>> &preNode)
150 {
151     inputStream_.Connect(preNode->GetSharedInstance(), preNode->GetOutputPort());
152 #ifdef ENABLE_HIDUMP_DFX
153     if (auto callback = GetNodeStatusCallback().lock()) {
154         callback->OnNotifyDfxNodeInfo(true, GetNodeId(), preNode->GetSharedInstance()->GetNodeInfo());
155     }
156 #endif
157 }
158 
DisConnect(const std::shared_ptr<OutputNode<HpaePcmBuffer * >> & preNode)159 void HpaeSinkOutputNode::DisConnect(const std::shared_ptr<OutputNode<HpaePcmBuffer *>> &preNode)
160 {
161     inputStream_.DisConnect(preNode->GetOutputPort());
162 #ifdef ENABLE_HIDUMP_DFX
163     if (auto callback = GetNodeStatusCallback().lock()) {
164         auto preNodeReal = preNode->GetSharedInstance();
165         callback->OnNotifyDfxNodeInfo(false, preNodeReal->GetNodeId(), preNodeReal->GetNodeInfo());
166     }
167 #endif
168 }
169 
GetRenderSinkInstance(const std::string & deviceClass,const std::string & deviceNetId)170 int32_t HpaeSinkOutputNode::GetRenderSinkInstance(const std::string &deviceClass, const std::string &deviceNetId)
171 {
172     if (deviceNetId.empty()) {
173         renderId_ = HdiAdapterManager::GetInstance().GetRenderIdByDeviceClass(deviceClass, HDI_ID_INFO_DEFAULT, true);
174     } else {
175         renderId_ = HdiAdapterManager::GetInstance().GetRenderIdByDeviceClass(deviceClass, deviceNetId, true);
176     }
177     audioRendererSink_ = HdiAdapterManager::GetInstance().GetRenderSink(renderId_, true);
178     if (audioRendererSink_ == nullptr) {
179         AUDIO_ERR_LOG("get sink fail, deviceClass: %{public}s, deviceNetId: %{public}s, renderId_: %{public}u",
180             deviceClass.c_str(),
181             deviceNetId.c_str(),
182             renderId_);
183         HdiAdapterManager::GetInstance().ReleaseId(renderId_);
184         return ERROR;
185     }
186     return SUCCESS;
187 }
188 
RenderSinkInit(IAudioSinkAttr & attr)189 int32_t HpaeSinkOutputNode::RenderSinkInit(IAudioSinkAttr &attr)
190 {
191     CHECK_AND_RETURN_RET(audioRendererSink_ != nullptr, ERROR);
192 
193     sinkOutAttr_ = attr;
194     if (audioRendererSink_->IsInited()) {
195         AUDIO_WARNING_LOG("audioRenderSink already inited");
196         SetSinkState(STREAM_MANAGER_IDLE);
197         return SUCCESS;
198     }
199 #ifdef ENABLE_HOOK_PCM
200     HighResolutionTimer timer;
201     timer.Start();
202 #endif
203     int32_t ret = audioRendererSink_->Init(attr);
204     CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret,
205         "audioRendererSink_ init failed, errCode is %{public}d", ret);
206     SetSinkState(STREAM_MANAGER_IDLE);
207 #ifdef ENABLE_HOOK_PCM
208     timer.Stop();
209     int64_t interval = timer.Elapsed();
210     AUDIO_INFO_LOG("HpaeSinkOutputNode: name %{public}s, RenderSinkInit Elapsed: %{public}" PRId64
211                    " ms ret: %{public}d",
212         sinkOutAttr_.adapterName.c_str(),
213         interval,
214         ret);
215     std::string adapterName = sinkOutAttr_.adapterName;
216 #endif
217     return ret;
218 }
219 
RenderSinkDeInit(void)220 int32_t HpaeSinkOutputNode::RenderSinkDeInit(void)
221 {
222     CHECK_AND_RETURN_RET(audioRendererSink_ != nullptr, ERROR);
223     SetSinkState(STREAM_MANAGER_RELEASED);
224 #ifdef ENABLE_HOOK_PCM
225     HighResolutionTimer timer;
226     timer.Start();
227 #endif
228     audioRendererSink_->DeInit();
229     audioRendererSink_ = nullptr;
230     HdiAdapterManager::GetInstance().ReleaseId(renderId_);
231 #ifdef ENABLE_HOOK_PCM
232     timer.Stop();
233     int64_t interval = timer.Elapsed();
234     AUDIO_INFO_LOG("HpaeSinkOutputNode: name %{public}s, RenderSinkDeInit Elapsed: %{public}" PRId64 " ms",
235         sinkOutAttr_.adapterName.c_str(),
236         interval);
237 #endif
238     return SUCCESS;
239 }
240 
RenderSinkFlush(void)241 int32_t HpaeSinkOutputNode::RenderSinkFlush(void)
242 {
243     CHECK_AND_RETURN_RET(audioRendererSink_ != nullptr, ERROR);
244     return audioRendererSink_->Flush();
245 }
246 
RenderSinkPause(void)247 int32_t HpaeSinkOutputNode::RenderSinkPause(void)
248 {
249     CHECK_AND_RETURN_RET(audioRendererSink_ != nullptr, ERROR);
250     audioRendererSink_->Pause();
251     SetSinkState(STREAM_MANAGER_SUSPENDED);
252     return SUCCESS;
253 }
254 
RenderSinkReset(void)255 int32_t HpaeSinkOutputNode::RenderSinkReset(void)
256 {
257     CHECK_AND_RETURN_RET(audioRendererSink_ != nullptr, ERROR);
258     return audioRendererSink_->Reset();
259 }
260 
RenderSinkResume(void)261 int32_t HpaeSinkOutputNode::RenderSinkResume(void)
262 {
263     CHECK_AND_RETURN_RET(audioRendererSink_ != nullptr, ERROR);
264     int32_t ret = audioRendererSink_->Resume();
265     CHECK_AND_RETURN_RET(ret == SUCCESS, ret);
266     SetSinkState(STREAM_MANAGER_RUNNING);
267     return SUCCESS;
268 }
269 
RenderSinkStart(void)270 int32_t HpaeSinkOutputNode::RenderSinkStart(void)
271 {
272     CHECK_AND_RETURN_RET(audioRendererSink_ != nullptr, ERROR);
273     renderFrameTimes_ = 0;
274     int32_t ret;
275 #ifdef ENABLE_HOOK_PCM
276     HighResolutionTimer timer;
277     timer.Start();
278 #endif
279     ret = audioRendererSink_->Start();
280     CHECK_AND_RETURN_RET(ret == SUCCESS, ret);
281 #ifdef ENABLE_HOOK_PCM
282     timer.Stop();
283     int64_t interval = timer.Elapsed();
284     AUDIO_INFO_LOG("HpaeSinkOutputNode: name %{public}s, RenderSinkStart Elapsed: %{public}" PRId64 " ms",
285         sinkOutAttr_.adapterName.c_str(),
286         interval);
287 #endif
288     SetSinkState(STREAM_MANAGER_RUNNING);
289     if (GetDeviceClass() == "remote") {
290         remoteTimePoint_ = std::chrono::high_resolution_clock::now();
291     }
292     if (GetDeviceClass() == "primary") {
293         ret = audioRendererSink_->SetPaPower(true);
294         isOpenPaPower_ = true;
295         isDisplayPaPowerState_ = false;
296         silenceDataUs_ = 0;
297         AUDIO_INFO_LOG("Speaker sink started, open pa:[%{public}s] -- [%{public}s], ret:%{public}d",
298             GetDeviceClass().c_str(), (ret == 0 ? "success" : "failed"), ret);
299     }
300     periodTimer_.Start();
301     return SUCCESS;
302 }
303 
RenderSinkStop(void)304 int32_t HpaeSinkOutputNode::RenderSinkStop(void)
305 {
306     CHECK_AND_RETURN_RET(audioRendererSink_ != nullptr, ERROR);
307     SetSinkState(STREAM_MANAGER_SUSPENDED);
308     int32_t ret;
309 #ifdef ENABLE_HOOK_PCM
310     HighResolutionTimer timer;
311     timer.Start();
312 #endif
313     ret = audioRendererSink_->Stop();
314     CHECK_AND_RETURN_RET(ret == SUCCESS, ret);
315 #ifdef ENABLE_HOOK_PCM
316     timer.Stop();
317     int64_t interval = timer.Elapsed();
318     AUDIO_INFO_LOG("HpaeSinkOutputNode: name %{public}s, RenderSinkStop Elapsed: %{public}" PRId64 " ms",
319         sinkOutAttr_.adapterName.c_str(), interval);
320 #endif
321     return SUCCESS;
322 }
323 
GetSinkState(void)324 StreamManagerState HpaeSinkOutputNode::GetSinkState(void)
325 {
326     return state_;
327 }
328 
SetSinkState(StreamManagerState sinkState)329 int32_t HpaeSinkOutputNode::SetSinkState(StreamManagerState sinkState)
330 {
331     AUDIO_INFO_LOG("Sink[%{public}s] state change:[%{public}s]-->[%{public}s]",
332         GetDeviceClass().c_str(), ConvertStreamManagerState2Str(state_).c_str(),
333         ConvertStreamManagerState2Str(sinkState).c_str());
334         state_ = sinkState;
335         return SUCCESS;
336 }
337 
GetPreOutNum()338 size_t HpaeSinkOutputNode::GetPreOutNum()
339 {
340     return inputStream_.GetPreOutputNum();
341 }
342 
UpdateAppsUid(const std::vector<int32_t> & appsUid)343 int32_t HpaeSinkOutputNode::UpdateAppsUid(const std::vector<int32_t> &appsUid)
344 {
345     CHECK_AND_RETURN_RET_LOG(audioRendererSink_ != nullptr, ERROR, "audioRendererSink_ is nullptr");
346     CHECK_AND_RETURN_RET_LOG(audioRendererSink_->IsInited(), ERR_ILLEGAL_STATE, "audioRendererSink_ not init");
347     return audioRendererSink_->UpdateAppsUid(appsUid);
348 }
349 
HandlePaPower(HpaePcmBuffer * pcmBuffer)350 void HpaeSinkOutputNode::HandlePaPower(HpaePcmBuffer *pcmBuffer)
351 {
352     if (GetDeviceClass() != "primary" || !pcmBuffer->IsValid()) {
353         return;
354     }
355     if (pcmBuffer->IsSilence()) {
356         if (!isDisplayPaPowerState_) {
357             AUDIO_INFO_LOG("Timing begins, will close speaker after [%{public}" PRId64 "]s", WAIT_CLOSE_PA_TIME);
358             isDisplayPaPowerState_ = true;
359         }
360         silenceDataUs_ += static_cast<int64_t>(pcmBuffer->GetFrameLen()) * TIME_IN_US /
361             static_cast<int64_t>(pcmBuffer->GetSampleRate());
362         if (isOpenPaPower_ && silenceDataUs_ >= WAIT_CLOSE_PA_TIME * TIME_IN_US &&
363             audioRendererSink_->GetAudioScene() == 0) {
364             int32_t ret = audioRendererSink_->SetPaPower(false);
365             isOpenPaPower_ = false;
366             silenceDataUs_ = 0;
367             AUDIO_INFO_LOG("Speaker pa volume change to zero over [%{public}" PRId64
368                 "]s, close %{public}s pa [%{public}s], ret:%{public}d",
369                 WAIT_CLOSE_PA_TIME, GetDeviceClass().c_str(), (ret == 0 ? "success" : "failed"), ret);
370         } else if (!isOpenPaPower_ && silenceDataUs_ >= MONITOR_CLOSE_PA_TIME * TIME_IN_US) {
371             silenceDataUs_ = 0;
372             AUDIO_INFO_LOG("Speaker pa have closed [%{public}" PRId64 "]s.", MONITOR_CLOSE_PA_TIME);
373         }
374     } else {
375         if (isDisplayPaPowerState_) {
376             isDisplayPaPowerState_ = false;
377             AUDIO_INFO_LOG("Volume change to non zero, break the speaker closing.");
378         }
379         silenceDataUs_ = 0;
380         if (!isOpenPaPower_) {
381             int32_t ret = audioRendererSink_->SetPaPower(true);
382             isOpenPaPower_ = true;
383             AUDIO_INFO_LOG("Volume change to non zero, open closed pa:[%{public}s] -- [%{public}s], ret:%{public}d",
384                 GetDeviceClass().c_str(), (ret == 0 ? "success" : "failed"), ret);
385         }
386     }
387 }
388 
RenderSinkSetPriPaPower()389 int32_t HpaeSinkOutputNode::RenderSinkSetPriPaPower()
390 {
391     CHECK_AND_RETURN_RET_LOG(audioRendererSink_ != nullptr, ERROR, "audioRendererSink_ is nullptr");
392     int32_t ret = audioRendererSink_->SetPriPaPower();
393     AUDIO_INFO_LOG("Open pri pa:[%{public}s] -- [%{public}s], ret:%{public}d",
394         GetDeviceClass().c_str(), (ret == 0 ? "success" : "failed"), ret);
395     return ret;
396 }
397 
GetLatency()398 uint32_t HpaeSinkOutputNode::GetLatency()
399 {
400     if (audioRendererSink_ == nullptr) {
401         return ERROR;
402     }
403     audioRendererSink_->GetLatency(latency_);
404     return latency_;
405 }
406 
RenderSinkSetSyncId(int32_t syncId)407 int32_t HpaeSinkOutputNode::RenderSinkSetSyncId(int32_t syncId)
408 {
409     isSyncIdSet_ = true;
410     syncId_ = syncId;
411     return SUCCESS;
412 }
413 
HandleHapticParam(uint64_t syncTime)414 void HpaeSinkOutputNode::HandleHapticParam(uint64_t syncTime)
415 {
416     if (isSyncIdSet_) {
417         isSyncIdSet_ = false;
418         AudioParamKey key = NONE;
419         std::string condition = "haptic";
420         std::string param = "haptic_sessionid=" + std::to_string(syncId_) +
421             ";haptic_offset=" + std::to_string(syncTime);
422         audioRendererSink_->SetAudioParameter(key, condition, param);
423     }
424 }
425 }  // namespace HPAE
426 }  // namespace AudioStandard
427 }  // namespace OHOS
428