1 /*
2 * Copyright (c) 2022-2022 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 #define HST_LOG_TAG "CallbackLooper"
17
18 #include "hiplayer_callback_looper.h"
19 #include <utility>
20 #include "common/log.h"
21 #include "osal/task/autolock.h"
22 #include "osal/utils/steady_clock.h"
23
24 namespace OHOS {
25 namespace Media {
26 namespace {
27 constexpr int32_t WHAT_NONE = 0;
28 constexpr int32_t WHAT_MEDIA_PROGRESS = 1;
29 constexpr int32_t WHAT_INFO = 2;
30 constexpr int32_t WHAT_ERROR = 3;
31
32 constexpr int32_t TUPLE_POS_0 = 0;
33 constexpr int32_t TUPLE_POS_1 = 1;
34 constexpr int32_t TUPLE_POS_2 = 2;
35 }
HiPlayerCallbackLooper()36 HiPlayerCallbackLooper::HiPlayerCallbackLooper() : task_("callbackThread", OHOS::Media::TaskPriority::NORMAL)
37 {
38 task_.RegisterJob([this] {LoopOnce();});
39 }
40
~HiPlayerCallbackLooper()41 HiPlayerCallbackLooper::~HiPlayerCallbackLooper()
42 {
43 Stop();
44 }
45
IsStarted()46 bool HiPlayerCallbackLooper::IsStarted()
47 {
48 return taskStarted_;
49 }
50
Stop()51 void HiPlayerCallbackLooper::Stop()
52 {
53 if (taskStarted_) {
54 eventQueue_.Quit();
55 task_.Stop();
56 taskStarted_ = false;
57 }
58 }
59
StartWithPlayerEngineObs(const std::weak_ptr<IPlayerEngineObs> & obs)60 void HiPlayerCallbackLooper::StartWithPlayerEngineObs(const std::weak_ptr<IPlayerEngineObs>& obs)
61 {
62 OHOS::Media::AutoLock lock(loopMutex_);
63 obs_ = obs;
64 if (!taskStarted_) {
65 task_.Start();
66 taskStarted_ = true;
67 MEDIA_LOG_I("start callback looper");
68 }
69 }
SetPlayEngine(IPlayerEngine * engine)70 void HiPlayerCallbackLooper::SetPlayEngine(IPlayerEngine* engine)
71 {
72 OHOS::Media::AutoLock lock(loopMutex_);
73 playerEngine_ = engine;
74 }
75
StartReportMediaProgress(int64_t updateIntervalMs)76 void HiPlayerCallbackLooper::StartReportMediaProgress(int64_t updateIntervalMs)
77 {
78 reportProgressIntervalMs_ = updateIntervalMs;
79 if (reportMediaProgress_) { // already set
80 return;
81 }
82 reportMediaProgress_ = true;
83 eventQueue_.Enqueue(std::make_shared<Event>(WHAT_MEDIA_PROGRESS, SteadyClock::GetCurrentTimeMs(), Any()));
84 }
85
ManualReportMediaProgressOnce()86 void HiPlayerCallbackLooper::ManualReportMediaProgressOnce()
87 {
88 eventQueue_.Enqueue(std::make_shared<Event>(WHAT_MEDIA_PROGRESS, SteadyClock::GetCurrentTimeMs(), Any()));
89 }
90
StopReportMediaProgress()91 void HiPlayerCallbackLooper::StopReportMediaProgress()
92 {
93 reportMediaProgress_ = false;
94 }
95
DropMediaProgress()96 void HiPlayerCallbackLooper::DropMediaProgress()
97 {
98 isDropMediaProgress_ = true;
99 eventQueue_.RemoveMediaProgressEvent();
100 }
101
DoReportCompletedTime()102 void HiPlayerCallbackLooper::DoReportCompletedTime()
103 {
104 OHOS::Media::AutoLock lock(loopMutex_);
105 auto obs = obs_.lock();
106 if (obs) {
107 Format format;
108 int32_t currentPositionMs;
109 if (playerEngine_->GetDuration(currentPositionMs) == 0) {
110 MEDIA_LOG_DD("EVENT_AUDIO_PROGRESS completed position updated: " PUBLIC_LOG_D32, currentPositionMs);
111 obs->OnInfo(INFO_TYPE_POSITION_UPDATE, currentPositionMs, format);
112 } else {
113 MEDIA_LOG_W("get player engine current time error");
114 }
115 }
116 }
117
DoReportMediaProgress()118 void HiPlayerCallbackLooper::DoReportMediaProgress()
119 {
120 OHOS::Media::AutoLock lock(loopMutex_);
121 auto obs = obs_.lock();
122 if (obs && !isDropMediaProgress_) {
123 Format format;
124 int32_t currentPositionMs;
125 if (playerEngine_->GetCurrentTime(currentPositionMs) == 0) {
126 MEDIA_LOG_DD("EVENT_AUDIO_PROGRESS position updated: " PUBLIC_LOG_D32, currentPositionMs);
127 obs->OnInfo(INFO_TYPE_POSITION_UPDATE, currentPositionMs, format);
128 } else {
129 MEDIA_LOG_W("get player engine current time error");
130 }
131 }
132 isDropMediaProgress_ = false;
133 if (reportMediaProgress_) {
134 eventQueue_.Enqueue(std::make_shared<Event>(WHAT_MEDIA_PROGRESS,
135 SteadyClock::GetCurrentTimeMs() + reportProgressIntervalMs_, Any()));
136 }
137 }
138
OnError(PlayerErrorType errorType,int32_t errorCode)139 void HiPlayerCallbackLooper::OnError(PlayerErrorType errorType, int32_t errorCode)
140 {
141 eventQueue_.Enqueue(std::make_shared<HiPlayerCallbackLooper::Event>(WHAT_ERROR, SteadyClock::GetCurrentTimeMs(),
142 std::make_pair(errorType, errorCode)));
143 }
144
DoReportError(const Any & error)145 void HiPlayerCallbackLooper::DoReportError(const Any &error)
146 {
147 OHOS::Media::AutoLock lock(loopMutex_);
148 auto obs = obs_.lock();
149 if (obs != nullptr) {
150 auto ptr = AnyCast<std::pair<PlayerErrorType, int32_t>>(&error);
151 MEDIA_LOG_E("Report error, error type: " PUBLIC_LOG_D32 " error value: " PUBLIC_LOG_D32,
152 static_cast<int32_t>(ptr->first), static_cast<int32_t>(ptr->second));
153 obs->OnError(ptr->first, ptr->second);
154 }
155 }
156
OnInfo(PlayerOnInfoType type,int32_t extra,const Format & infoBody)157 void HiPlayerCallbackLooper::OnInfo(PlayerOnInfoType type, int32_t extra, const Format &infoBody)
158 {
159 eventQueue_.Enqueue(std::make_shared<HiPlayerCallbackLooper::Event>(WHAT_INFO, SteadyClock::GetCurrentTimeMs(),
160 std::make_tuple(type, extra, infoBody)));
161 }
162
DoReportInfo(const Any & info)163 void HiPlayerCallbackLooper::DoReportInfo(const Any& info)
164 {
165 auto obs = obs_.lock();
166 if (obs != nullptr) {
167 auto ptr = AnyCast<std::tuple<PlayerOnInfoType, int32_t, Format>>(&info);
168 MEDIA_LOG_I("Report info, info type: " PUBLIC_LOG_D32 " info value: " PUBLIC_LOG_D32,
169 static_cast<int32_t>(std::get<TUPLE_POS_0>(*ptr)), static_cast<int32_t>(std::get<TUPLE_POS_1>(*ptr)));
170 obs->OnInfo(std::get<TUPLE_POS_0>(*ptr), std::get<TUPLE_POS_1>(*ptr), std::get<TUPLE_POS_2>(*ptr));
171 }
172 }
173
LoopOnce()174 void HiPlayerCallbackLooper::LoopOnce()
175 {
176 auto item = eventQueue_.Next();
177 switch (item->what) {
178 case WHAT_MEDIA_PROGRESS:
179 DoReportMediaProgress();
180 break;
181 case WHAT_INFO:
182 DoReportInfo(item->detail);
183 break;
184 case WHAT_ERROR:
185 DoReportError(item->detail);
186 break;
187 default:
188 break;
189 }
190 }
191
Enqueue(const std::shared_ptr<HiPlayerCallbackLooper::Event> & event)192 void HiPlayerCallbackLooper::EventQueue::Enqueue(const std::shared_ptr<HiPlayerCallbackLooper::Event>& event)
193 {
194 if (event->what == WHAT_NONE) {
195 MEDIA_LOG_I("invalid event");
196 }
197 OHOS::Media::AutoLock lock(queueMutex_);
198 if (quit_) {
199 MEDIA_LOG_W("event already quit");
200 return;
201 }
202 auto ite = queue_.begin();
203 for (; ite != queue_.end(); ite++) {
204 if ((*ite)->whenMs > event->whenMs) {
205 break;
206 }
207 }
208 auto pos = queue_.insert(ite, event);
209 if (pos == queue_.begin()) {
210 queueHeadUpdatedCond_.NotifyOne();
211 }
212 }
213
Next()214 std::shared_ptr<HiPlayerCallbackLooper::Event> HiPlayerCallbackLooper::EventQueue::Next()
215 {
216 OHOS::Media::AutoLock lock(queueMutex_);
217 // not empty
218 while (queue_.empty() && !quit_) {
219 queueHeadUpdatedCond_.Wait(lock);
220 }
221
222 do {
223 if (quit_) {
224 return std::make_shared<HiPlayerCallbackLooper::Event>(WHAT_NONE, 0, Any());
225 }
226 auto wakenAtTime = (*queue_.begin())->whenMs;
227 auto leftTime = wakenAtTime - SteadyClock::GetCurrentTimeMs();
228 if (leftTime <= 0) {
229 auto first = *queue_.begin();
230 queue_.erase(queue_.begin());
231 return first;
232 }
233 queueHeadUpdatedCond_.WaitFor(lock, leftTime);
234 } while (1);
235 }
236
Quit()237 void HiPlayerCallbackLooper::EventQueue::Quit()
238 {
239 OHOS::Media::AutoLock lock(queueMutex_);
240 quit_ = true;
241 queueHeadUpdatedCond_.NotifyOne();
242 }
243
RemoveMediaProgressEvent()244 void HiPlayerCallbackLooper::EventQueue::RemoveMediaProgressEvent()
245 {
246 OHOS::Media::AutoLock lock(queueMutex_);
247 for (auto iter = queue_.begin(); iter != queue_.end(); iter++) {
248 if ((*iter)->what == WHAT_MEDIA_PROGRESS) {
249 (*iter)->what = WHAT_NONE;
250 }
251 }
252 }
253 } // namespace Media
254 } // namespace OHOS