• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2021 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 "StateMachine"
17 
18 #include "state_machine.h"
19 #include "utils/steady_clock.h"
20 
21 namespace OHOS {
22 namespace Media {
StateMachine(PlayExecutor & executor)23 StateMachine::StateMachine(PlayExecutor& executor)
24     : Task("StateMachine"),
25       intentSync_("fsmSync"),
26       curState_(std::make_shared<InitState>(StateId::INIT, executor)),
27       jobs_("StateMachineJobQue")
28 {
29     AddState(curState_);
30     AddState(std::make_shared<PreparingState>(StateId::PREPARING, executor));
31     AddState(std::make_shared<ReadyState>(StateId::READY, executor));
32     AddState(std::make_shared<PlayingState>(StateId::PLAYING, executor));
33     AddState(std::make_shared<PauseState>(StateId::PAUSE, executor));
34 }
35 
Stop()36 void StateMachine::Stop()
37 {
38     MEDIA_LOG_D("Stop called.");
39     jobs_.SetActive(false);
40     Task::Stop();
41 }
42 
SetStateCallback(StateChangeCallback * callback)43 void StateMachine::SetStateCallback(StateChangeCallback* callback)
44 {
45     callback_ = callback;
46 }
47 
GetCurrentState() const48 const std::string& StateMachine::GetCurrentState() const
49 {
50     return curState_->GetName();
51 }
52 
GetCurrentStateId() const53 StateId StateMachine::GetCurrentStateId() const
54 {
55     return curState_->GetStateId();
56 }
57 
SendEvent(Intent intent,const Plugin::Any & param) const58 ErrorCode StateMachine::SendEvent(Intent intent, const Plugin::Any& param) const
59 {
60     return const_cast<StateMachine*>(this)->SendEvent(intent, param);
61 }
62 
SendEvent(Intent intent,const Plugin::Any & param)63 ErrorCode StateMachine::SendEvent(Intent intent, const Plugin::Any& param)
64 {
65     constexpr int timeoutMs = 5000;
66     ErrorCode errorCode = ErrorCode::ERROR_TIMED_OUT;
67     if (!intentSync_.WaitFor(
68         intent, [this, intent, param] { SendEventAsync(intent, param); }, timeoutMs, errorCode)) {
69         MEDIA_LOG_E("SendEvent timeout, intent: %" PUBLIC_LOG "d", static_cast<int>(intent));
70     }
71     return errorCode;
72 }
73 
SendEventAsync(Intent intent,const Plugin::Any & param) const74 ErrorCode StateMachine::SendEventAsync(Intent intent, const Plugin::Any& param) const
75 {
76     return const_cast<StateMachine*>(this)->SendEventAsync(intent, param);
77 }
78 
SendEventAsync(Intent intent,const Plugin::Any & param)79 ErrorCode StateMachine::SendEventAsync(Intent intent, const Plugin::Any& param)
80 {
81     MEDIA_LOG_D("SendEventAsync, intent: %" PUBLIC_LOG "d", static_cast<int>(intent));
82     jobs_.Push([this, intent, param]() -> Action { return ProcessIntent(intent, param); });
83     return ErrorCode::SUCCESS;
84 }
85 
ProcessIntent(Intent intent,const Plugin::Any & param)86 Action StateMachine::ProcessIntent(Intent intent, const Plugin::Any& param)
87 {
88     MEDIA_LOG_D("ProcessIntent, curState: %" PUBLIC_LOG_S ", intent: %" PUBLIC_LOG_D32,
89                 curState_->GetName().c_str(), intent);
90     PROFILE_BEGIN("ProcessIntent, curState: %" PUBLIC_LOG_S ", intent: %" PUBLIC_LOG_D32,
91                   curState_->GetName().c_str(), intent);
92     OSAL::ScopedLock lock(mutex_);
93     lastIntent = intent;
94     ErrorCode rtv = ErrorCode::SUCCESS;
95     Action nextAction = Action::ACTION_BUTT;
96     std::tie(rtv, nextAction) = curState_->Execute(intent, param);
97     if (nextAction != Action::ACTION_BUTT) {
98         if (rtv == ErrorCode::SUCCESS) {
99             rtv = ProcAction(nextAction);
100         } else {
101             (void)ProcAction(nextAction);
102         }
103     }
104     OnIntentExecuted(intent, nextAction, rtv);
105     PROFILE_END("ProcessIntent, curState: %" PUBLIC_LOG_S ", intent: %" PUBLIC_LOG_D32,
106                 curState_->GetName().c_str(), intent);
107     return (rtv == ErrorCode::SUCCESS) ? nextAction : Action::ACTION_BUTT;
108 }
109 
DoTask()110 void StateMachine::DoTask()
111 {
112 #ifdef UNIT_TEST
113     constexpr int timeoutMs = 500;
114     auto job = jobs_.Pop(timeoutMs);
115 #else
116     auto job = jobs_.Pop();
117 #endif
118     if (!job) {
119         return;
120     }
121     auto action = job();
122     switch (action) {
123         case Action::ACTION_PENDING:
124             pendingJobs_.push(job);
125             break;
126         case Action::TRANS_TO_INIT:
127         case Action::TRANS_TO_READY:
128         case Action::TRANS_TO_PREPARING:
129         case Action::TRANS_TO_PLAYING:
130         case Action::TRANS_TO_PAUSE: {
131             if (!pendingJobs_.empty()) {
132                 job = pendingJobs_.front();
133                 pendingJobs_.pop();
134                 action = job();
135                 if (action == Action::ACTION_PENDING) {
136                     pendingJobs_.push(job);
137                 }
138             }
139             break;
140         }
141         case Action::ACTION_BUTT:
142             // fall through
143         default:
144             break;
145     }
146 }
147 
AddState(const std::shared_ptr<State> & state)148 void StateMachine::AddState(const std::shared_ptr<State>& state)
149 {
150     states_[state->GetStateId()] = state;
151 }
152 
ProcAction(Action nextAction)153 ErrorCode StateMachine::ProcAction(Action nextAction)
154 {
155     std::shared_ptr<State> nextState = nullptr;
156     switch (nextAction) {
157         case Action::TRANS_TO_INIT:
158             nextState = states_[StateId::INIT];
159             break;
160         case Action::TRANS_TO_PREPARING:
161             nextState = states_[StateId::PREPARING];
162             break;
163         case Action::TRANS_TO_READY:
164             nextState = states_[StateId::READY];
165             break;
166         case Action::TRANS_TO_PLAYING:
167             nextState = states_[StateId::PLAYING];
168             break;
169         case Action::TRANS_TO_PAUSE:
170             nextState = states_[StateId::PAUSE];
171             break;
172         default:
173             break;
174     }
175     ErrorCode ret = ErrorCode::SUCCESS;
176     if (nextState) {
177         ret = TransitionTo(nextState);
178     }
179     return ret;
180 }
181 
TransitionTo(const std::shared_ptr<State> & state)182 ErrorCode StateMachine::TransitionTo(const std::shared_ptr<State>& state)
183 {
184     if (state == nullptr) {
185         MEDIA_LOG_E("TransitionTo, nullptr for state");
186         return ErrorCode::ERROR_INVALID_PARAMETER_VALUE;
187     }
188     ErrorCode rtv = ErrorCode::SUCCESS;
189     if (state != curState_) {
190         curState_->Exit();
191         curState_ = state;
192         Action nextAction;
193         std::tie(rtv, nextAction) = curState_->Enter(lastIntent);
194         if (rtv == ErrorCode::SUCCESS) {
195             rtv = ProcAction(nextAction);
196         }
197         if (callback_) {
198             callback_->OnStateChanged(curState_->GetStateId());
199         }
200     }
201     return rtv;
202 }
203 
OnIntentExecuted(Intent intent,Action action,ErrorCode result)204 void StateMachine::OnIntentExecuted(Intent intent, Action action, ErrorCode result)
205 {
206     MEDIA_LOG_D("OnIntentExecuted, curState: %" PUBLIC_LOG "s, intent: %" PUBLIC_LOG "d, action: %" PUBLIC_LOG
207                 "d, result: %" PUBLIC_LOG "d", curState_->GetName().c_str(),
208                 static_cast<int>(intent), static_cast<int>(action), static_cast<int>(result));
209     if (action == Action::ACTION_PENDING) {
210         return;
211     }
212     if (intent == Intent::NOTIFY_READY && action == Action::TRANS_TO_PLAYING) {
213         intentSync_.Notify(Intent::PLAY, result);
214     } else {
215         intentSync_.Notify(intent, result);
216     }
217 }
218 } // namespace Media
219 } // namespace OHOS