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 }