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 #ifndef LOG_TAG
16 #define LOG_TAG "HpaeOffloadSinkOutputNode"
17 #endif
18
19 #include "hpae_offload_sinkoutput_node.h"
20 #include "audio_errors.h"
21 #include <iostream>
22 #include <cinttypes>
23
24 #include "hpae_format_convert.h"
25 #include "hpae_node_common.h"
26 #include "audio_volume.h"
27 #include "audio_common_utils.h"
28 #ifdef ENABLE_HOOK_PCM
29 #include "hpae_pcm_dumper.h"
30 #endif
31 #include "audio_engine_log.h"
32
33 namespace OHOS {
34 namespace AudioStandard {
35 namespace HPAE {
36 namespace {
37 constexpr uint32_t CACHE_FRAME_COUNT = 2;
38 constexpr uint32_t TIME_US_PER_MS = 1000;
39 constexpr uint32_t TIME_MS_PER_SEC = 1000;
40 constexpr uint32_t ERR_RETRY_COUNT = 20;
41 constexpr uint32_t FRAME_TIME_IN_MS = 20;
42 constexpr int32_t OFFLOAD_FULL = -1;
43 constexpr int32_t OFFLOAD_WRITE_FAILED = -2;
44 constexpr uint32_t OFFLOAD_HDI_CACHE_BACKGROUND_IN_MS = 7000;
45 constexpr uint32_t OFFLOAD_HDI_CACHE_FRONTGROUND_IN_MS = 200;
46 constexpr uint32_t OFFLOAD_HDI_CACHE_MOVIE_IN_MS = 500;
47 // hdi fallback, modify when hdi change
48 constexpr uint32_t OFFLOAD_FAD_INTERVAL_IN_US = 180000;
49 constexpr uint32_t OFFLOAD_SET_BUFFER_SIZE_NUM = 5;
50 constexpr uint32_t POLICY_STATE_DELAY_IN_SEC = 3;
51 static constexpr float EPSILON = 1e-6f;
52
53 const std::string DEVICE_CLASS_OFFLOAD = "offload";
54 }
HpaeOffloadSinkOutputNode(HpaeNodeInfo & nodeInfo)55 HpaeOffloadSinkOutputNode::HpaeOffloadSinkOutputNode(HpaeNodeInfo &nodeInfo)
56 : HpaeNode(nodeInfo),
57 renderFrameData_(0)
58 {
59 #ifdef ENABLE_HOOK_PCM
60 outputPcmDumper_ = std::make_unique<HpaePcmDumper>(
61 "HpaeOffloadSinkOutputNode_Out_bit_" + std::to_string(GetBitWidth()) + "_ch_" +
62 std::to_string(GetChannelCount()) + "_rate_" + std::to_string(GetSampleRate()) + ".pcm");
63 #endif
64 frameLenMs_ = nodeInfo.samplingRate ? nodeInfo.frameLen * TIME_MS_PER_SEC / nodeInfo.samplingRate : 0;
65 #ifdef ENABLE_HIDUMP_DFX
66 SetNodeName("hpaeOffloadSinkOutputNode");
67 if (auto callback = GetNodeStatusCallback().lock()) {
68 callback->OnNotifyDfxNodeInfo(true, 0, GetNodeInfo());
69 }
70 #endif
71 }
72
~HpaeOffloadSinkOutputNode()73 HpaeOffloadSinkOutputNode::~HpaeOffloadSinkOutputNode()
74 {
75 #ifdef ENABLE_HIDUMP_DFX
76 AUDIO_INFO_LOG("NodeId: %{public}u NodeName: %{public}s destructed.",
77 GetNodeId(), GetNodeName().c_str());
78 #endif
79 }
80
CheckIfSuspend()81 bool HpaeOffloadSinkOutputNode::CheckIfSuspend()
82 {
83 if (!GetPreOutNum()) {
84 suspendCount_++;
85 usleep(TIME_US_PER_MS * FRAME_TIME_IN_MS);
86 if (suspendCount_ > timeoutThdFrames_) {
87 RenderSinkStop();
88 }
89 return true;
90 } else {
91 suspendCount_ = 0;
92 return false;
93 }
94 }
95
DoProcess()96 void HpaeOffloadSinkOutputNode::DoProcess()
97 {
98 CHECK_AND_RETURN_LOG(audioRendererSink_, "audioRendererSink_ is nullptr sessionId: %{public}u", GetSessionId());
99 if (CheckIfSuspend()) {
100 return;
101 }
102 // if there are no enough frames in cache, read more data from pre-output
103 size_t frameSize = static_cast<size_t>(GetSizeFromFormat(GetBitWidth())) * GetFrameLen() * GetChannelCount();
104 while (renderFrameData_.size() < CACHE_FRAME_COUNT * frameSize) {
105 std::vector<HpaePcmBuffer *> &outputVec = inputStream_.ReadPreOutputData();
106 if (outputVec.size() && outputVec.front()->IsValid()) {
107 renderFrameData_.resize(renderFrameData_.size() + frameSize);
108 ConvertFromFloat(GetBitWidth(), GetChannelCount() * GetFrameLen(),
109 outputVec.front()->GetPcmDataBuffer(), renderFrameData_.data() + renderFrameData_.size() - frameSize);
110 } else {
111 break;
112 }
113 }
114 int32_t ret = ProcessRenderFrame();
115 // if renderframe faild, sleep and return directly
116 // if renderframe full, unlock the powerlock
117 retryCount_ = 1;
118 if (ret == OFFLOAD_FULL) {
119 if (hdiPolicyState_ == OFFLOAD_INACTIVE_BACKGROUND || GetStreamType() == STREAM_MOVIE) {
120 RunningLock(false);
121 }
122 isHdiFull_.store(true);
123 return;
124 } else if (ret != SUCCESS) {
125 usleep(std::min(retryCount_, FRAME_TIME_IN_MS) * TIME_US_PER_MS);
126 if (retryCount_ < ERR_RETRY_COUNT) {
127 retryCount_++;
128 }
129 return;
130 }
131 retryCount_ = 1;
132 return;
133 }
134
Reset()135 bool HpaeOffloadSinkOutputNode::Reset()
136 {
137 const auto preOutputMap = inputStream_.GetPreOutputMap();
138 for (const auto &preOutput : preOutputMap) {
139 OutputPort<HpaePcmBuffer *> *output = preOutput.first;
140 inputStream_.DisConnect(output);
141 }
142 return true;
143 }
144
ResetAll()145 bool HpaeOffloadSinkOutputNode::ResetAll()
146 {
147 const auto preOutputMap = inputStream_.GetPreOutputMap();
148 for (const auto &preOutput : preOutputMap) {
149 OutputPort<HpaePcmBuffer *> *output = preOutput.first;
150 std::shared_ptr<HpaeNode> hpaeNode = preOutput.second;
151 if (hpaeNode->ResetAll()) {
152 inputStream_.DisConnect(output);
153 }
154 }
155 return true;
156 }
157
Connect(const std::shared_ptr<OutputNode<HpaePcmBuffer * >> & preNode)158 void HpaeOffloadSinkOutputNode::Connect(const std::shared_ptr<OutputNode<HpaePcmBuffer *>> &preNode)
159 {
160 inputStream_.Connect(preNode->GetSharedInstance(), preNode->GetOutputPort());
161 #ifdef ENABLE_HIDUMP_DFX
162 if (auto callback = GetNodeStatusCallback().lock()) {
163 callback->OnNotifyDfxNodeInfo(true, GetNodeId(), preNode->GetSharedInstance()->GetNodeInfo());
164 }
165 #endif
166 }
167
DisConnect(const std::shared_ptr<OutputNode<HpaePcmBuffer * >> & preNode)168 void HpaeOffloadSinkOutputNode::DisConnect(const std::shared_ptr<OutputNode<HpaePcmBuffer *>> &preNode)
169 {
170 inputStream_.DisConnect(preNode->GetOutputPort());
171 #ifdef ENABLE_HIDUMP_DFX
172 if (auto callback = GetNodeStatusCallback().lock()) {
173 auto preNodeReal = preNode->GetSharedInstance();
174 callback->OnNotifyDfxNodeInfo(false, preNodeReal->GetNodeId(), preNodeReal->GetNodeInfo());
175 }
176 #endif
177 }
178
GetRenderSinkInstance(const std::string & deviceClass,const std::string & deviceNetworkId)179 int32_t HpaeOffloadSinkOutputNode::GetRenderSinkInstance(const std::string &deviceClass,
180 const std::string &deviceNetworkId)
181 {
182 std::string info = deviceNetworkId == "LocalDevice" ? HDI_ID_INFO_DEFAULT : deviceNetworkId;
183 renderId_ = HdiAdapterManager::GetInstance().GetRenderIdByDeviceClass(
184 deviceClass, info, true);
185 audioRendererSink_ = HdiAdapterManager::GetInstance().GetRenderSink(renderId_, true);
186 if (audioRendererSink_ == nullptr) {
187 AUDIO_ERR_LOG("get offload sink fail, deviceClass: %{public}s, renderId_: %{public}u",
188 deviceClass.c_str(), renderId_);
189 HdiAdapterManager::GetInstance().ReleaseId(renderId_);
190 return ERROR;
191 }
192 return SUCCESS;
193 }
194
OffloadReset()195 void HpaeOffloadSinkOutputNode::OffloadReset()
196 {
197 writePos_ = 0;
198 hdiPos_ = std::make_pair(0, std::chrono::high_resolution_clock::now());
199 firstWriteHdi_ = true;
200 isHdiFull_.store(false);
201 renderFrameData_.clear();
202 setPolicyStateTask_.flag = false; // unset the task when reset
203 }
204
RenderSinkInit(IAudioSinkAttr & attr)205 int32_t HpaeOffloadSinkOutputNode::RenderSinkInit(IAudioSinkAttr &attr)
206 {
207 CHECK_AND_RETURN_RET_LOG(audioRendererSink_, ERR_ILLEGAL_STATE,
208 "audioRendererSink_ is nullptr sessionId: %{public}u", GetSessionId());
209
210 sinkOutAttr_ = attr;
211 if (audioRendererSink_->IsInited()) {
212 SetSinkState(STREAM_MANAGER_IDLE);
213 AUDIO_WARNING_LOG("audioRenderSink already inited");
214 return SUCCESS;
215 }
216 #ifdef ENABLE_HOOK_PCM
217 HighResolutionTimer timer;
218 timer.Start();
219 #endif
220 int32_t ret = audioRendererSink_->Init(attr);
221 CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret,
222 "audioRendererSink_ init failed, errCode is %{public}d", ret);
223 SetSinkState(STREAM_MANAGER_IDLE);
224 #ifdef ENABLE_HOOK_PCM
225 timer.Stop();
226 int64_t interval = timer.Elapsed();
227 AUDIO_INFO_LOG("HpaeOffloadSinkOutputNode: name %{public}s, RenderSinkInit Elapsed: %{public}" PRId64 " ms",
228 sinkOutAttr_.adapterName.c_str(), interval);
229 #endif
230 return ret;
231 }
232
RenderSinkDeInit(void)233 int32_t HpaeOffloadSinkOutputNode::RenderSinkDeInit(void)
234 {
235 CHECK_AND_RETURN_RET_LOG(audioRendererSink_, ERR_ILLEGAL_STATE,
236 "audioRendererSink_ is nullptr sessionId: %{public}u", GetSessionId());
237 SetSinkState(STREAM_MANAGER_RELEASED);
238 #ifdef ENABLE_HOOK_PCM
239 HighResolutionTimer timer;
240 timer.Start();
241 #endif
242 audioRendererSink_->DeInit();
243 audioRendererSink_ = nullptr;
244 HdiAdapterManager::GetInstance().ReleaseId(renderId_);
245 #ifdef ENABLE_HOOK_PCM
246 timer.Stop();
247 int64_t interval = timer.Elapsed();
248 AUDIO_INFO_LOG("HpaeOffloadSinkOutputNode: name %{public}s, RenderSinkDeInit Elapsed: %{public}" PRId64 " ms",
249 sinkOutAttr_.adapterName.c_str(), interval);
250 #endif
251 return SUCCESS;
252 }
253
RenderSinkFlush(void)254 int32_t HpaeOffloadSinkOutputNode::RenderSinkFlush(void)
255 {
256 CHECK_AND_RETURN_RET_LOG(audioRendererSink_, ERR_ILLEGAL_STATE,
257 "audioRendererSink_ is nullptr sessionId: %{public}u", GetSessionId());
258 int32_t ret;
259 #ifdef ENABLE_HOOK_PCM
260 HighResolutionTimer timer;
261 timer.Start();
262 #endif
263 ret = audioRendererSink_->Flush();
264 CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret,
265 "audioRendererSink_ flush failed, errCode is %{public}d", ret);
266 #ifdef ENABLE_HOOK_PCM
267 timer.Stop();
268 int64_t interval = timer.Elapsed();
269 AUDIO_INFO_LOG("HpaeOffloadSinkOutputNode: name %{public}s, RenderSinkFlush Elapsed: %{public}" PRId64 " ms",
270 sinkOutAttr_.adapterName.c_str(), interval);
271 #endif
272 return ret;
273 }
274
RenderSinkStart(void)275 int32_t HpaeOffloadSinkOutputNode::RenderSinkStart(void)
276 {
277 CHECK_AND_RETURN_RET_LOG(audioRendererSink_, ERR_ILLEGAL_STATE,
278 "audioRendererSink_ is nullptr sessionId: %{public}u", GetSessionId());
279
280 int32_t ret;
281 #ifdef ENABLE_HOOK_PCM
282 HighResolutionTimer timer;
283 timer.Start();
284 #endif
285 ret = audioRendererSink_->Start();
286 CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret,
287 "audioRendererSink_ start failed, errCode is %{public}d", ret);
288 RegOffloadCallback();
289 // start need lock
290 RunningLock(true);
291 OffloadSetHdiVolume();
292 #ifdef ENABLE_HOOK_PCM
293 timer.Stop();
294 int64_t interval = timer.Elapsed();
295 AUDIO_INFO_LOG("HpaeOffloadSinkOutputNode: name %{public}s, RenderSinkStart Elapsed: %{public}" PRId64 " ms",
296 sinkOutAttr_.adapterName.c_str(), interval);
297 #endif
298 SetSinkState(STREAM_MANAGER_RUNNING);
299 return SUCCESS;
300 }
301
RenderSinkStop(void)302 int32_t HpaeOffloadSinkOutputNode::RenderSinkStop(void)
303 {
304 CHECK_AND_RETURN_RET_LOG(audioRendererSink_, ERR_ILLEGAL_STATE,
305 "audioRendererSink_ is nullptr sessionId: %{public}u", GetSessionId());
306 SetSinkState(STREAM_MANAGER_SUSPENDED);
307 int32_t ret;
308 #ifdef ENABLE_HOOK_PCM
309 HighResolutionTimer timer;
310 timer.Start();
311 #endif
312 ret = audioRendererSink_->Stop();
313 OffloadReset();
314 RunningLock(false);
315 CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret,
316 "audioRendererSink_ stop failed, errCode is %{public}d", ret);
317 #ifdef ENABLE_HOOK_PCM
318 timer.Stop();
319 int64_t interval = timer.Elapsed();
320 AUDIO_INFO_LOG("HpaeOffloadSinkOutputNode: name %{public}s, RenderSinkStop Elapsed: %{public}" PRId64 " ms",
321 sinkOutAttr_.adapterName.c_str(), interval);
322 #endif
323 return SUCCESS;
324 }
325
FlushStream()326 void HpaeOffloadSinkOutputNode::FlushStream()
327 {
328 renderFrameData_.clear();
329 }
330
GetPreOutNum()331 size_t HpaeOffloadSinkOutputNode::GetPreOutNum()
332 {
333 return inputStream_.GetPreOutputNum();
334 }
335
GetSinkState(void)336 StreamManagerState HpaeOffloadSinkOutputNode::GetSinkState(void)
337 {
338 return isHdiFull_.load() ? STREAM_MANAGER_SUSPENDED : state_;
339 }
340
SetSinkState(StreamManagerState sinkState)341 int32_t HpaeOffloadSinkOutputNode::SetSinkState(StreamManagerState sinkState)
342 {
343 AUDIO_INFO_LOG("Sink[%{public}s] state change:[%{public}s]-->[%{public}s]",
344 GetDeviceClass().c_str(), ConvertStreamManagerState2Str(state_).c_str(),
345 ConvertStreamManagerState2Str(sinkState).c_str());
346 state_ = sinkState;
347 return SUCCESS;
348 }
349
GetRenderFrameData(void)350 const char *HpaeOffloadSinkOutputNode::GetRenderFrameData(void)
351 {
352 return renderFrameData_.data();
353 }
354
StopStream()355 void HpaeOffloadSinkOutputNode::StopStream()
356 {
357 CHECK_AND_RETURN_LOG(audioRendererSink_, "audioRendererSink_ is nullptr sessionId: %{public}u", GetSessionId());
358 // flush hdi when disconnect
359 RunningLock(true);
360 UpdatePresentationPosition();
361 auto ret = RenderSinkFlush();
362 CHECK_AND_RETURN_LOG(ret == SUCCESS, "RenderSinkFlush failed");
363 uint64_t cacheLenInHdi = CalcOffloadCacheLenInHdi();
364 uint64_t fadeOutLen = static_cast<uint64_t>(OFFLOAD_FAD_INTERVAL_IN_US * speed_);
365 cacheLenInHdi = cacheLenInHdi > fadeOutLen ? cacheLenInHdi - fadeOutLen : 0;
366 uint64_t rewindTime = cacheLenInHdi + ConvertDatalenToUs(renderFrameData_.size(), GetNodeInfo());
367 AUDIO_DEBUG_LOG("OffloadRewindAndFlush rewind time in us %{public}" PRIu64, rewindTime);
368 auto callback = GetNodeInfo().statusCallback.lock();
369 CHECK_AND_RETURN_LOG(callback != nullptr, "HpaeOffloadSinkOutputNode::StopStream callback is null");
370 callback->OnRewindAndFlush(rewindTime, hdiRealPos_);
371 OffloadReset();
372 }
373
SetPolicyState(int32_t state)374 void HpaeOffloadSinkOutputNode::SetPolicyState(int32_t state)
375 {
376 if (setPolicyStateTask_.flag) {
377 if (state == hdiPolicyState_) {
378 AUDIO_INFO_LOG("HpaeOffloadSinkOutputNode: unset policy state task");
379 setPolicyStateTask_.flag = false;
380 }
381 return;
382 }
383 if (hdiPolicyState_ != state && state == OFFLOAD_INACTIVE_BACKGROUND) {
384 AUDIO_INFO_LOG("HpaeOffloadSinkOutputNode: set policy state task");
385 setPolicyStateTask_.flag = true;
386 setPolicyStateTask_.time = std::chrono::high_resolution_clock::now();
387 setPolicyStateTask_.state = OFFLOAD_INACTIVE_BACKGROUND;
388 return;
389 }
390 hdiPolicyState_ = static_cast<AudioOffloadType>(state);
391 SetBufferSize();
392 }
393
GetLatency()394 uint64_t HpaeOffloadSinkOutputNode::GetLatency()
395 {
396 return ConvertDatalenToUs(renderFrameData_.size(), GetNodeInfo());
397 }
398
SetTimeoutStopThd(uint32_t timeoutThdMs)399 int32_t HpaeOffloadSinkOutputNode::SetTimeoutStopThd(uint32_t timeoutThdMs)
400 {
401 if (frameLenMs_ != 0) {
402 timeoutThdFrames_ = timeoutThdMs / frameLenMs_;
403 }
404 AUDIO_INFO_LOG(
405 "SetTimeoutStopThd: timeoutThdFrames_:%{public}u, timeoutThdMs :%{public}u", timeoutThdFrames_, timeoutThdMs);
406 return SUCCESS;
407 }
408
SetOffloadRenderCallbackType(int32_t type)409 int32_t HpaeOffloadSinkOutputNode::SetOffloadRenderCallbackType(int32_t type)
410 {
411 AUDIO_INFO_LOG("SetOffloadRenderCallbackType type:%{public}d", type);
412 OffloadCallback(static_cast<RenderCallbackType>(type));
413 return SUCCESS;
414 }
415
SetSpeed(float speed)416 void HpaeOffloadSinkOutputNode::SetSpeed(float speed)
417 {
418 CHECK_AND_RETURN_LOG(audioRendererSink_, "audioRendererSink_ is nullptr sessionId: %{public}u", GetSessionId());
419 speed_ = speed;
420 audioRendererSink_->SetSpeed(speed);
421 }
422
RunningLock(bool islock)423 void HpaeOffloadSinkOutputNode::RunningLock(bool islock)
424 {
425 if (islock) {
426 audioRendererSink_->LockOffloadRunningLock();
427 } else if (!islock) {
428 audioRendererSink_->UnLockOffloadRunningLock();
429 }
430 }
431
SetBufferSizeWhileRenderFrame()432 void HpaeOffloadSinkOutputNode::SetBufferSizeWhileRenderFrame()
433 {
434 // 3s delay works, when change to BACKGROUND
435 if (setPolicyStateTask_.flag) {
436 auto now = std::chrono::high_resolution_clock::now();
437 if (std::chrono::duration_cast<std::chrono::seconds>(now - setPolicyStateTask_.time).count() >=
438 POLICY_STATE_DELAY_IN_SEC) {
439 AUDIO_INFO_LOG("HpaeOffloadSinkOutputNode: excute set policy state task");
440 setPolicyStateTask_.flag = false;
441 hdiPolicyState_ = setPolicyStateTask_.state;
442 SetBufferSize();
443 return; // no need to set buffer size twice at one process
444 }
445 }
446 // first start need to set buffer size 5 times
447 if (setHdiBufferSizeNum_ > 0) {
448 setHdiBufferSizeNum_--;
449 AUDIO_INFO_LOG("HpaeOffloadSinkOutputNode: set policy state cause first render");
450 SetBufferSize();
451 }
452 }
453
SetBufferSize()454 void HpaeOffloadSinkOutputNode::SetBufferSize()
455 {
456 uint32_t bufferSize = OFFLOAD_HDI_CACHE_FRONTGROUND_IN_MS;
457 if (GetStreamType() == STREAM_MOVIE) {
458 bufferSize = OFFLOAD_HDI_CACHE_MOVIE_IN_MS;
459 } else {
460 bufferSize = hdiPolicyState_ == OFFLOAD_INACTIVE_BACKGROUND ?
461 OFFLOAD_HDI_CACHE_BACKGROUND_IN_MS : OFFLOAD_HDI_CACHE_FRONTGROUND_IN_MS;
462 }
463 audioRendererSink_->SetBufferSize(bufferSize);
464 }
465
ProcessRenderFrame()466 int32_t HpaeOffloadSinkOutputNode::ProcessRenderFrame()
467 {
468 if (renderFrameData_.empty()) {
469 return OFFLOAD_WRITE_FAILED;
470 }
471 uint64_t writeLen = 0;
472 renderFrameDataTemp_ = renderFrameData_;
473 char *renderFrameData = (char *)renderFrameDataTemp_.data();
474 #ifdef ENABLE_HOOK_PCM
475 HighResolutionTimer timer;
476 timer.Start();
477 intervalTimer_.Stop();
478 int64_t interval = intervalTimer_.Elapsed();
479 AUDIO_DEBUG_LOG("HpaeOffloadSinkOutputNode: name %{public}s, RenderFrame interval: %{public}" PRId64 " ms",
480 sinkOutAttr_.adapterName.c_str(), interval);
481 #endif
482 auto now = std::chrono::high_resolution_clock::now();
483 auto ret = audioRendererSink_->RenderFrame(*renderFrameData, renderFrameData_.size(), writeLen);
484 if (ret == SUCCESS && writeLen == 0 && !firstWriteHdi_) {
485 AUDIO_INFO_LOG("HpaeOffloadSinkOutputNode: offload renderFrame full");
486 return OFFLOAD_FULL;
487 }
488 if (!(ret == SUCCESS && writeLen == renderFrameData_.size())) {
489 AUDIO_ERR_LOG("HpaeOffloadSinkOutputNode: offload renderFrame failed, errCode is %{public}d", ret);
490 return OFFLOAD_WRITE_FAILED;
491 }
492 // calc written data length
493 writePos_ += ConvertDatalenToUs(renderFrameData_.size(), GetNodeInfo());
494 // now is the time to first write hdi
495 if (firstWriteHdi_) {
496 firstWriteHdi_ = false;
497 hdiPos_ = std::make_pair(0, now);
498 setHdiBufferSizeNum_ = OFFLOAD_SET_BUFFER_SIZE_NUM;
499 // if the hdi is flushing, it will block the volume setting.
500 // so the render frame judge it.
501 OffloadSetHdiVolume();
502 SetSpeed(speed_);
503 AUDIO_INFO_LOG("offload write pos: %{public}" PRIu64 " hdi pos: %{public}" PRIu64 " ",
504 writePos_, hdiPos_.first);
505 }
506 // hdi fallback, dont modify
507 SetBufferSizeWhileRenderFrame();
508 #ifdef ENABLE_HOOK_PCM
509 if (outputPcmDumper_) {
510 outputPcmDumper_->Dump((int8_t *)renderFrameData, renderFrameData_.size());
511 }
512 timer.Stop();
513 int64_t elapsed = timer.Elapsed();
514 AUDIO_DEBUG_LOG("HpaeOffloadSinkOutputNode :name %{public}s, RenderFrame elapsed time: %{public}" PRId64 " ms",
515 sinkOutAttr_.adapterName.c_str(), elapsed);
516 intervalTimer_.Start();
517 #endif
518 renderFrameData_.clear();
519 return SUCCESS;
520 }
521
UpdatePresentationPosition()522 int32_t HpaeOffloadSinkOutputNode::UpdatePresentationPosition()
523 {
524 CHECK_AND_RETURN_RET_LOG(audioRendererSink_, ERR_ILLEGAL_STATE,
525 "audioRendererSink_ is nullptr sessionId: %{public}u", GetSessionId());
526 uint64_t frames;
527 int64_t timeSec;
528 int64_t timeNanoSec;
529 int ret = audioRendererSink_->ForceRefreshPresentationPosition(frames, hdiRealPos_, timeSec, timeNanoSec);
530 CHECK_AND_RETURN_RET_LOG(ret == SUCCESS, ret, "ForceRefreshPresentationPosition failed, ret is %{public}d", ret);
531 auto total_ns = std::chrono::seconds(timeSec) + std::chrono::nanoseconds(timeNanoSec);
532 hdiPos_ = std::make_pair(frames, TimePoint(total_ns));
533 return 0;
534 }
535
CalcOffloadCacheLenInHdi()536 uint64_t HpaeOffloadSinkOutputNode::CalcOffloadCacheLenInHdi()
537 {
538 auto now = std::chrono::high_resolution_clock::now();
539 uint64_t time = now > hdiPos_.second ?
540 static_cast<uint64_t>(std::chrono::duration_cast<std::chrono::microseconds>(now - hdiPos_.second).count()) : 0;
541 uint64_t hdiPos = hdiPos_.first + static_cast<uint64_t>(time * speed_);
542 uint64_t cacheLenInHdi = writePos_ > hdiPos ? (writePos_ - hdiPos) : 0;
543 AUDIO_DEBUG_LOG("offload latency: %{public}" PRIu64 " write pos: %{public}" PRIu64
544 " hdi pos: %{public}" PRIu64 " time: %{public}" PRIu64 " speed: %{public}f",
545 cacheLenInHdi, writePos_, hdiPos, time, speed_);
546 return cacheLenInHdi;
547 }
548
OffloadSetHdiVolume()549 void HpaeOffloadSinkOutputNode::OffloadSetHdiVolume()
550 {
551 struct VolumeValues volumes;
552 AudioStreamType volumeType = VolumeUtils::GetVolumeTypeFromStreamType(GetStreamType());
553 std::string deviceClass = GetDeviceClass();
554 std::string volumeDeviceClass = deviceClass == "remote_offload" ? "remote" : deviceClass;
555 float volumeEnd = AudioVolume::GetInstance()->GetVolume(GetSessionId(), volumeType, volumeDeviceClass, &volumes);
556 float volumeBeg = AudioVolume::GetInstance()->GetHistoryVolume(GetSessionId());
557 if (fabs(volumeBeg - volumeEnd) > EPSILON) {
558 AUDIO_INFO_LOG("HpaeOffloadSinkOutputNode::sessionID:%{public}u, volumeBeg:%{public}f, volumeEnd:%{public}f",
559 GetSessionId(), volumeBeg, volumeEnd);
560 AudioVolume::GetInstance()->SetHistoryVolume(GetSessionId(), volumeEnd);
561 AudioVolume::GetInstance()->Monitor(GetSessionId(), true);
562 }
563 CHECK_AND_RETURN_LOG(audioRendererSink_, "audioRendererSink_ is nullptr sessionId: %{public}u", GetSessionId());
564 audioRendererSink_->SetVolume(volumeEnd, volumeEnd);
565 }
566
OffloadCallback(const RenderCallbackType type)567 void HpaeOffloadSinkOutputNode::OffloadCallback(const RenderCallbackType type)
568 {
569 Trace trace("HpaeOffloadSinkOutputNode::OffloadCallback");
570 switch (type) {
571 case CB_NONBLOCK_WRITE_COMPLETED: {
572 if (isHdiFull_.load()) {
573 RunningLock(true);
574 UpdatePresentationPosition();
575 auto callback = GetNodeInfo().statusCallback.lock();
576 isHdiFull_.store(false);
577 if (callback) {
578 callback->OnNotifyQueue();
579 }
580 }
581 break;
582 }
583 case CB_RENDER_FULL: {
584 if (!isHdiFull_.load()) {
585 RunningLock(false);
586 isHdiFull_.store(true);
587 }
588 break;
589 }
590 default:
591 break;
592 }
593 }
594
RegOffloadCallback()595 void HpaeOffloadSinkOutputNode::RegOffloadCallback()
596 {
597 CHECK_AND_RETURN_LOG(audioRendererSink_, "audioRendererSink_ is nullptr sessionId: %{public}u", GetSessionId());
598 audioRendererSink_->RegistOffloadHdiCallback([this](const RenderCallbackType type) { OffloadCallback(type); });
599 }
600
UpdateAppsUid(const std::vector<int32_t> & appsUid)601 int32_t HpaeOffloadSinkOutputNode::UpdateAppsUid(const std::vector<int32_t> &appsUid)
602 {
603 CHECK_AND_RETURN_RET_LOG(audioRendererSink_ != nullptr, ERROR, "audioRendererSink_ is nullptr");
604 CHECK_AND_RETURN_RET_LOG(audioRendererSink_->IsInited(), ERR_ILLEGAL_STATE, "audioRendererSink_ not init");
605 return audioRendererSink_->UpdateAppsUid(appsUid);
606 }
607 } // namespace HPAE
608 } // namespace AudioStandard
609 } // namespace OHOS
610