• 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 #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