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 #define HST_LOG_TAG "DownloadMonitor"
16
17 #include "download_monitor.h"
18 #include "foundation/cpp_ext/algorithm_ext.h"
19
20 namespace OHOS {
21 namespace Media {
22 namespace Plugin {
23 namespace HttpPlugin {
24 namespace {
25 constexpr int RETRY_TIMES_TO_REPORT_ERROR = 10;
26 }
DownloadMonitor(std::shared_ptr<MediaDownloader> downloader)27 DownloadMonitor::DownloadMonitor(std::shared_ptr<MediaDownloader> downloader) noexcept
28 : downloader_(std::move(downloader))
29 {
30 auto statusCallback = [this] (DownloadStatus&& status, std::shared_ptr<Downloader>& downloader,
31 std::shared_ptr<DownloadRequest>& request) {
32 OnDownloadStatus(std::forward<decltype(downloader)>(downloader), std::forward<decltype(request)>(request));
33 };
34 downloader_->SetStatusCallback(statusCallback);
35 task_ = std::make_shared<OSAL::Task>(std::string("HttpMonitor"));
36 task_->RegisterHandler([this] { HttpMonitorLoop(); });
37 task_->Start();
38 }
39
HttpMonitorLoop()40 void DownloadMonitor::HttpMonitorLoop()
41 {
42 if (isPlaying_) {
43 time_t nowTime;
44 time(&nowTime);
45 if ((lastReadTime_ != 0) && (nowTime - lastReadTime_ >= 60)) { // 60
46 MEDIA_LOG_D("HttpMonitorLoop : too long without reading data, paused");
47 Pause();
48 }
49 }
50 RetryRequest task;
51 {
52 OSAL::ScopedLock lock(taskMutex_);
53 if (!retryTasks_.empty()) {
54 task = retryTasks_.front();
55 retryTasks_.pop_front();
56 }
57 }
58 if (task.request && task.function) {
59 task.function();
60 }
61 OSAL::SleepFor(50); // 50
62 }
63
Open(const std::string & url)64 bool DownloadMonitor::Open(const std::string& url)
65 {
66 isPlaying_ = true;
67 retryTasks_.clear();
68 return downloader_->Open(url);
69 }
70
Pause()71 void DownloadMonitor::Pause()
72 {
73 downloader_->Pause();
74 isPlaying_ = false;
75 lastReadTime_ = 0;
76 }
77
Resume()78 void DownloadMonitor::Resume()
79 {
80 downloader_->Resume();
81 isPlaying_ = true;
82 }
83
Close()84 void DownloadMonitor::Close()
85 {
86 retryTasks_.clear();
87 task_->Stop();
88 downloader_->Close();
89 isPlaying_ = false;
90 }
91
Read(unsigned char * buff,unsigned int wantReadLength,unsigned int & realReadLength,bool & isEos)92 bool DownloadMonitor::Read(unsigned char *buff, unsigned int wantReadLength,
93 unsigned int &realReadLength, bool &isEos)
94 {
95 if (!isPlaying_) {
96 Resume();
97 }
98 bool ret = downloader_->Read(buff, wantReadLength, realReadLength, isEos);
99 time(&lastReadTime_);
100 return ret;
101 }
102
Seek(int offset)103 bool DownloadMonitor::Seek(int offset)
104 {
105 isPlaying_ = true;
106 retryTasks_.clear();
107 return downloader_->Seek(offset);
108 }
109
GetContentLength() const110 size_t DownloadMonitor::GetContentLength() const
111 {
112 return downloader_->GetContentLength();
113 }
114
GetDuration() const115 double DownloadMonitor::GetDuration() const
116 {
117 return downloader_->GetDuration();
118 }
119
GetSeekable() const120 Seekable DownloadMonitor::GetSeekable() const
121 {
122 return downloader_->GetSeekable();
123 }
124
SetCallback(Callback * cb)125 void DownloadMonitor::SetCallback(Callback* cb)
126 {
127 callback_ = cb;
128 downloader_->SetCallback(cb);
129 }
130
SetStatusCallback(StatusCallbackFunc cb)131 void DownloadMonitor::SetStatusCallback(StatusCallbackFunc cb)
132 {
133 }
134
GetStartedStatus()135 bool DownloadMonitor::GetStartedStatus()
136 {
137 return downloader_->GetStartedStatus();
138 }
139
NeedRetry(const std::shared_ptr<DownloadRequest> & request)140 bool DownloadMonitor::NeedRetry(const std::shared_ptr<DownloadRequest>& request)
141 {
142 auto clientError = request->GetClientError();
143 auto serverError = request->GetServerError();
144 auto retryTimes = request->GetRetryTimes();
145 if ((clientError != NetworkClientErrorCode::ERROR_OK && clientError != NetworkClientErrorCode::ERROR_NOT_RETRY)
146 || serverError != 0) {
147 MEDIA_LOG_I("NeedRetry: clientError = " PUBLIC_LOG_D32 ", serverError = " PUBLIC_LOG_D32
148 ", retryTimes = " PUBLIC_LOG_D32, clientError, serverError, retryTimes);
149 if (retryTimes > RETRY_TIMES_TO_REPORT_ERROR) { // Report error to upper layer
150 if (clientError != NetworkClientErrorCode::ERROR_OK) {
151 MEDIA_LOG_I("Send http client error, code " PUBLIC_LOG_D32, static_cast<int32_t>(clientError));
152 callback_->OnEvent({PluginEventType::CLIENT_ERROR, {clientError}, "http"});
153 }
154 if (serverError != 0) {
155 MEDIA_LOG_I("Send http server error, code " PUBLIC_LOG_D32, serverError);
156 callback_->OnEvent({PluginEventType::SERVER_ERROR, {serverError}, "http"});
157 }
158 if (!downloader_->GetStartedStatus()) {
159 task_->Stop();
160 downloader_->Close();
161 return false;
162 }
163 }
164 return true;
165 }
166 return false;
167 }
168
OnDownloadStatus(std::shared_ptr<Downloader> & downloader,std::shared_ptr<DownloadRequest> & request)169 void DownloadMonitor::OnDownloadStatus(std::shared_ptr<Downloader>& downloader,
170 std::shared_ptr<DownloadRequest>& request)
171 {
172 FALSE_RETURN_MSG(downloader != nullptr, "downloader is null, url is " PUBLIC_LOG_S, request->GetUrl().c_str());
173 if (NeedRetry(request)) {
174 OSAL::ScopedLock lock(taskMutex_);
175 bool exists = CppExt::AnyOf(retryTasks_.begin(), retryTasks_.end(), [&](const RetryRequest& item) {
176 return item.request->IsSame(request);
177 });
178 if (!exists) {
179 RetryRequest retryRequest {request, [downloader, request] { downloader->Retry(request); }};
180 retryTasks_.emplace_back(std::move(retryRequest));
181 }
182 }
183 }
184 }
185 }
186 }
187 }