1 /*
2 * Copyright (c) 2024-2024 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 "PlayListDownloader"
16 #include "playlist_downloader.h"
17 #include "securec.h"
18 #include <unistd.h>
19 #include <fstream>
20 #include <iostream>
21 #include <regex>
22 #include "network/network_typs.h"
23 #include "osal/filesystem/file_system.h"
24
25 namespace OHOS {
26 namespace Media {
27 namespace Plugins {
28 namespace HttpPlugin {
29 constexpr int FDPOS = 2;
30 constexpr int PLAYLIST_UPDATE_RATE = 1000 * 1000;
31 constexpr int MIN_PRE_PARSE_CONTENT_LEN = 5 * 1024; // 5k
32 constexpr int RETRY_DELTA_TIME_TO_REPORT_ERROR = 5 * 1000; // 5s
33 constexpr int RETRY_TIME_TO_REPORT_ERROR = 10; // 10
34
isNumber(const std::string & str)35 static bool isNumber(const std::string& str)
36 {
37 return str.find_first_not_of("0123456789") == std::string::npos;
38 }
39
PlayListDownloaderInit()40 void PlayListDownloader::PlayListDownloaderInit()
41 {
42 dataSave_ = [this] (uint8_t*&& data, uint32_t&& len) {
43 return SaveData(std::forward<decltype(data)>(data), std::forward<decltype(len)>(len));
44 };
45 // this is default callback
46 statusCallback_ = [this] (DownloadStatus&& status, std::shared_ptr<Downloader> d,
47 std::shared_ptr<DownloadRequest>& request) {
48 OnDownloadStatus(std::forward<decltype(status)>(status), downloader_,
49 std::forward<decltype(request)>(request));
50 };
51 updateTask_ = std::make_shared<Task>(std::string("OS_FragmentListUpdate"), "", TaskType::SINGLETON);
52 updateTask_->RegisterJob([this] {
53 UpdateManifest();
54 return PLAYLIST_UPDATE_RATE;
55 });
56 }
57
PlayListDownloader(const std::map<std::string,std::string> & httpHeader)58 PlayListDownloader::PlayListDownloader(const std::map<std::string, std::string>& httpHeader) noexcept
59 {
60 downloader_ = std::make_shared<Downloader>("hlsPlayList");
61 httpHeader_ = httpHeader;
62 PlayListDownloaderInit();
63 }
64
PlayListDownloader(std::shared_ptr<Downloader> downloader,const std::map<std::string,std::string> & httpHeader)65 PlayListDownloader::PlayListDownloader(std::shared_ptr<Downloader> downloader,
66 const std::map<std::string, std::string>& httpHeader) noexcept
67 {
68 downloader_ = downloader;
69 httpHeader_ = httpHeader;
70 PlayListDownloaderInit();
71 }
72
SaveHttpHeader(const std::map<std::string,std::string> & httpHeader)73 void PlayListDownloader::SaveHttpHeader(const std::map<std::string, std::string>& httpHeader)
74 {
75 httpHeader_ = httpHeader;
76 }
77
DoOpen(const std::string & url)78 void PlayListDownloader::DoOpen(const std::string& url)
79 {
80 auto realStatusCallback = [this] (DownloadStatus&& status, std::shared_ptr<Downloader>& downloader,
81 std::shared_ptr<DownloadRequest>& request) {
82 if (retryStartTime_ == 0) {
83 retryStartTime_ = request->GetNowTime();
84 }
85 int64_t nowTime = request->GetNowTime();
86 bool isNeedReportError = (nowTime - retryStartTime_) >= RETRY_DELTA_TIME_TO_REPORT_ERROR
87 || request->GetRetryTimes() >= RETRY_TIME_TO_REPORT_ERROR;
88 if (isNeedReportError && eventCallback_ != nullptr) {
89 MEDIA_LOG_E("fail to download m3u8.");
90 eventCallback_->OnEvent({PluginEventType::CLIENT_ERROR,
91 {NetworkClientErrorCode::ERROR_TIME_OUT}, "download m3u8"});
92
93 return;
94 }
95 statusCallback_(status, downloader_, std::forward<decltype(request)>(request));
96 };
97
98 RequestInfo requestInfo;
99 requestInfo.url = url;
100 requestInfo.httpHeader = httpHeader_;
101 downloadRequest_ = std::make_shared<DownloadRequest>(dataSave_, realStatusCallback, requestInfo, true);
102 if (downloadRequest_ == nullptr) {
103 MEDIA_LOG_E("no enough memory downloadRequest_ is nullptr");
104 return;
105 }
106 auto downloadDoneCallback = [this] (const std::string& url, const std::string& location) {
107 UpdateDownloadFinished(url, location);
108 };
109 downloadRequest_->SetIsM3u8Request(true);
110 downloadRequest_->SetDownloadDoneCb(downloadDoneCallback);
111 if (downloader_ != nullptr) {
112 downloader_->Download(downloadRequest_, -1); // -1
113 downloader_->Start();
114 }
115 }
116
DoOpenNative(const std::string & url)117 void PlayListDownloader::DoOpenNative(const std::string& url)
118 {
119 std::string uri = url;
120 ParseUriInfo(uri);
121 char buf[size_ + 1];
122
123 auto ret = read(fd_, buf, size_);
124 buf[size_] = '\0';
125 std::string m3u8(buf);
126 if (ret < 0) {
127 MEDIA_LOG_E("Failed to read, errno " PUBLIC_LOG_D32, static_cast<int32_t>(errno));
128 return;
129 }
130 MEDIA_LOG_I("Read success.");
131 playList_ = m3u8;
132 ParseManifest(playList_);
133 }
134
ParseUriInfo(const std::string & uri)135 bool PlayListDownloader::ParseUriInfo(const std::string& uri)
136 {
137 if (uri.empty()) {
138 MEDIA_LOG_E("uri is empty");
139 return false;
140 }
141 MEDIA_LOG_D("uri: ");
142 std::smatch fdUriMatch;
143 FALSE_RETURN_V_MSG_E(std::regex_match(uri, fdUriMatch, std::regex("^fd://(.*)\\?offset=(.*)&size=(.*)")) ||
144 std::regex_match(uri, fdUriMatch, std::regex("^fd://(.*)")),
145 false, "Invalid fd uri format");
146 FALSE_RETURN_V_MSG_E(fdUriMatch.size() >= FDPOS && isNumber(fdUriMatch[1].str()),
147 false, "Invalid fd uri format");
148 fd_ = std::stoi(fdUriMatch[1].str()); // 1: sub match fd subscript
149 FALSE_RETURN_V_MSG_E(fd_ != -1 && FileSystem::IsRegularFile(fd_),
150 false, "Invalid fd: " PUBLIC_LOG_D32, fd_);
151 fileSize_ = GetFileSize(fd_);
152 if (fdUriMatch.size() == 4) { // 4:4 sub match
153 offset_ = std::stoll(fdUriMatch[2].str()); // 2: sub match offset subscript
154 if (static_cast<uint64_t>(offset_) > fileSize_) {
155 offset_ = static_cast<int64_t>(fileSize_);
156 }
157 size_ = static_cast<uint64_t>(std::stoll(fdUriMatch[3].str())); // 3: sub match size subscript
158 uint64_t remainingSize = fileSize_ - static_cast<uint64_t>(offset_);
159 if (size_ > remainingSize) {
160 size_ = remainingSize;
161 }
162 } else {
163 size_ = fileSize_;
164 offset_ = 0;
165 }
166 position_ = static_cast<uint64_t>(offset_);
167 seekable_ = FileSystem::IsSeekable(fd_) ? Seekable::SEEKABLE : Seekable::UNSEEKABLE;
168 if (seekable_ == Seekable::SEEKABLE) {
169 SeekTo(0);
170 }
171 MEDIA_LOG_D("Fd: " PUBLIC_LOG_D32 ", offset: " PUBLIC_LOG_D64 ", size: " PUBLIC_LOG_U64, fd_, offset_, size_);
172 return true;
173 }
174
GetFileSize(int32_t fd)175 uint64_t PlayListDownloader::GetFileSize(int32_t fd)
176 {
177 uint64_t fileSize = 0;
178 struct stat s {};
179 if (fstat(fd, &s) == 0) {
180 fileSize = static_cast<uint64_t>(s.st_size);
181 return fileSize;
182 }
183 return fileSize;
184 }
185
SeekTo(uint64_t offset)186 bool PlayListDownloader::SeekTo(uint64_t offset)
187 {
188 if (fd_ == -1) {
189 return false;
190 }
191 int32_t ret = lseek(fd_, offset + static_cast<uint64_t>(offset_), SEEK_SET);
192 if (ret == -1) {
193 MEDIA_LOG_E("seek to " PUBLIC_LOG_U64 " failed due to " PUBLIC_LOG_S, offset, strerror(errno));
194 return false;
195 }
196 position_ = offset + static_cast<uint64_t>(offset_);
197 MEDIA_LOG_D("now seek to " PUBLIC_LOG_D32, ret);
198 return true;
199 }
200
GetPlayListDownloadStatus()201 bool PlayListDownloader::GetPlayListDownloadStatus()
202 {
203 return startedDownloadStatus_;
204 }
205
SaveData(uint8_t * data,uint32_t len)206 bool PlayListDownloader::SaveData(uint8_t* data, uint32_t len)
207 {
208 if (data == nullptr || len == 0) {
209 return false;
210 }
211 playList_.reserve(playList_.size() + len);
212 playList_.append(reinterpret_cast<const char*>(data), len);
213 startedDownloadStatus_ = true;
214 FALSE_RETURN_V_MSG_E(downloader_ != nullptr, false, "downloader nullptr");
215 int32_t contentlen = static_cast<int32_t>(downloader_->GetCurrentRequest()->GetFileContentLengthNoWait());
216 std::string location;
217 downloader_->GetCurrentRequest()->GetLocation(location);
218 if (contentlen > MIN_PRE_PARSE_CONTENT_LEN) {
219 PreParseManifest(location);
220 }
221 return true;
222 }
223
UpdateDownloadFinished(const std::string & url,const std::string & location)224 void PlayListDownloader::UpdateDownloadFinished(const std::string& url, const std::string& location)
225 {
226 ParseManifest(location);
227 playList_.clear();
228 retryStartTime_ = 0;
229 }
230
OnDownloadStatus(DownloadStatus status,std::shared_ptr<Downloader> &,std::shared_ptr<DownloadRequest> & request)231 void PlayListDownloader::OnDownloadStatus(DownloadStatus status, std::shared_ptr<Downloader>&,
232 std::shared_ptr<DownloadRequest>& request)
233 {
234 // This should not be called normally
235 MEDIA_LOG_D("Should not call this OnDownloadStatus, should call monitor.");
236 if (request->GetClientError() != 0 || request->GetServerError() != 0) {
237 MEDIA_LOG_E("OnDownloadStatus " PUBLIC_LOG_D32, status);
238 }
239 }
240
SetStatusCallback(StatusCallbackFunc cb)241 void PlayListDownloader::SetStatusCallback(StatusCallbackFunc cb)
242 {
243 statusCallback_ = cb;
244 }
245
ParseManifest(const std::string & location,bool isPreParse)246 void PlayListDownloader::ParseManifest(const std::string& location, bool isPreParse)
247 {
248 MEDIA_LOG_E("Should not call this ParseManifest");
249 }
250
Resume()251 void PlayListDownloader::Resume()
252 {
253 MEDIA_LOG_I("PlayListDownloader::Resume.");
254 if (IsLive() && updateTask_ != nullptr) {
255 MEDIA_LOG_I("updateTask_ Start.");
256 updateTask_->Start();
257 }
258 }
259
Pause(bool isAsync)260 void PlayListDownloader::Pause(bool isAsync)
261 {
262 MEDIA_LOG_I("PlayListDownloader::Pause.");
263 if (updateTask_ == nullptr) {
264 return;
265 }
266 if (IsLive()) {
267 MEDIA_LOG_I("updateTask_ Pause.");
268 if (isAsync) {
269 updateTask_->PauseAsync();
270 } else {
271 updateTask_->Pause();
272 }
273 }
274 }
275
Close()276 void PlayListDownloader::Close()
277 {
278 if (IsLive()) {
279 MEDIA_LOG_I("updateTask_ Close.");
280 updateTask_->StopAsync();
281 }
282 Stop();
283 }
284
Stop()285 void PlayListDownloader::Stop()
286 {
287 if (downloader_ != nullptr) {
288 MEDIA_LOG_I("PlayListDownloader::Stop.");
289 downloader_->Stop(true);
290 }
291 }
292
Start()293 void PlayListDownloader::Start()
294 {
295 MEDIA_LOG_I("PlayListDownloader::Start.");
296 if (downloader_ != nullptr) {
297 downloader_->Start();
298 }
299 }
300
Cancel()301 void PlayListDownloader::Cancel()
302 {
303 MEDIA_LOG_I("PlayListDownloader::Cancel.");
304 if (downloader_ != nullptr) {
305 downloader_->Cancel();
306 }
307 playList_.clear();
308 }
309
GetHttpHeader()310 std::map<std::string, std::string> PlayListDownloader::GetHttpHeader()
311 {
312 return httpHeader_;
313 }
314
SetCallback(Callback * cb)315 void PlayListDownloader::SetCallback(Callback* cb)
316 {
317 eventCallback_ = cb;
318 }
319
SetInterruptState(bool isInterruptNeeded)320 void PlayListDownloader::SetInterruptState(bool isInterruptNeeded)
321 {
322 isInterruptNeeded_ = isInterruptNeeded;
323 if (downloader_ != nullptr) {
324 downloader_->SetInterruptState(isInterruptNeeded);
325 }
326 }
327
SetAppUid(int32_t appUid)328 void PlayListDownloader::SetAppUid(int32_t appUid)
329 {
330 if (downloader_) {
331 downloader_->SetAppUid(appUid);
332 }
333 }
334
335 }
336 }
337 }
338 }