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