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 16 #ifndef HISTREAMER_DOWNLOADER_H 17 #define HISTREAMER_DOWNLOADER_H 18 19 #include <functional> 20 #include <memory> 21 #include <string> 22 #include "osal/task/task.h" 23 #include "osal/task/mutex.h" 24 #include "osal/task/condition_variable.h" 25 #include "osal/task/blocking_queue.h" 26 #include "osal/utils/util.h" 27 #include "network/network_client.h" 28 #include "network/network_typs.h" 29 #include <chrono> 30 #include "securec.h" 31 #include "common/media_source.h" 32 #include "media_source_loading_request.h" 33 34 namespace OHOS { 35 namespace Media { 36 namespace Plugins { 37 namespace HttpPlugin { 38 39 enum struct DownloadStatus { 40 PARTTAL_DOWNLOAD, 41 }; 42 43 struct HeaderInfo { 44 char contentType[32] = {}; // 32 chars 45 size_t fileContentLen {0}; 46 mutable size_t retryTimes {0}; 47 const static size_t maxRetryTimes {100}; 48 const static int sleepTime {10}; 49 long contentLen {0}; 50 bool isChunked {false}; 51 std::atomic<bool> isClosed {false}; 52 bool isServerAcceptRange {false}; 53 UpdateHeaderInfo54 void Update(const HeaderInfo* info) 55 { 56 NZERO_LOG(memcpy_s(contentType, sizeof(contentType), info->contentType, sizeof(contentType))); 57 fileContentLen = info->fileContentLen; 58 contentLen = info->contentLen; 59 isChunked = info->isChunked; 60 } 61 GetFileContentLengthHeaderInfo62 size_t GetFileContentLength() const 63 { 64 while (fileContentLen == 0 && !isChunked && !isClosed && retryTimes < maxRetryTimes) { 65 OSAL::SleepFor(sleepTime); // 10, wait for fileContentLen updated 66 retryTimes++; 67 } 68 return fileContentLen; 69 } 70 }; 71 72 // uint8_t* : the data should save 73 // uint32_t : length 74 using DataSaveFunc = std::function<uint32_t(uint8_t*, uint32_t, bool)>; 75 class Downloader; 76 class DownloadRequest; 77 using StatusCallbackFunc = std::function<void(DownloadStatus, std::shared_ptr<Downloader>&, 78 std::shared_ptr<DownloadRequest>&)>; 79 using DownloadDoneCbFunc = std::function<void(const std::string&, const std::string&)>; 80 81 enum class RequestProtocolType : int32_t { 82 HTTP = 0, 83 HLS = 1, 84 DASH = 2, 85 }; 86 87 class DownloadRequest { 88 public: 89 DownloadRequest(const std::string& url, DataSaveFunc saveData, StatusCallbackFunc statusCallback, 90 bool requestWholeFile = false); 91 DownloadRequest(const std::string& url, double duration, DataSaveFunc saveData, StatusCallbackFunc statusCallback, 92 bool requestWholeFile = false); 93 DownloadRequest(DataSaveFunc saveData, StatusCallbackFunc statusCallback, RequestInfo requestInfo, 94 bool requestWholeFile = false); 95 DownloadRequest(double duration, DataSaveFunc saveData, StatusCallbackFunc statusCallback, RequestInfo requestInfo, 96 bool requestWholeFile = false); 97 ~DownloadRequest(); 98 size_t GetFileContentLength() const; 99 size_t GetFileContentLengthNoWait() const; 100 void SaveHeader(const HeaderInfo* header); 101 Seekable IsChunked(bool isInterruptNeeded); 102 bool IsEos() const; 103 int GetRetryTimes() const; 104 int32_t GetClientError() const; 105 int32_t GetServerError() const; IsSame(const std::shared_ptr<DownloadRequest> & other)106 bool IsSame(const std::shared_ptr<DownloadRequest>& other) const 107 { 108 return url_ == other->url_ && startPos_ == other->startPos_; 109 } GetUrl()110 const std::string GetUrl() const 111 { 112 return url_; 113 } GetHttpHeader()114 const std::map<std::string, std::string>& GetHttpHeader() const 115 { 116 return httpHeader_; 117 } SetUrl(const std::string & url)118 void SetUrl(const std::string& url) 119 { 120 url_ = url; 121 } SetIsAuthRequest(bool isAuthRequest)122 void SetIsAuthRequest(bool isAuthRequest) 123 { 124 isAuthRequest_ = isAuthRequest; 125 } IsAuthRequest()126 bool IsAuthRequest() const 127 { 128 return isAuthRequest_; 129 } SetIsIndexM3u8Request(bool isIndexM3u8Request)130 void SetIsIndexM3u8Request(bool isIndexM3u8Request) 131 { 132 isIndexM3u8Request_ = isIndexM3u8Request; 133 } IsIndexM3u8Request()134 bool IsIndexM3u8Request() const 135 { 136 return isIndexM3u8Request_; 137 } 138 bool IsClosed() const; 139 void Close(); 140 double GetDuration() const; 141 void SetStartTimePos(int64_t startTimePos); 142 void SetRangePos(int64_t startPos, int64_t endPos); 143 void SetDownloadDoneCb(DownloadDoneCbFunc downloadDoneCallback); 144 int64_t GetNowTime(); 145 uint32_t GetBitRate() const; 146 bool IsChunkedVod() const; 147 bool IsM3u8Request() const; 148 bool IsServerAcceptRange() const; 149 void GetLocation(std::string& location) const; 150 void SetRequestProtocolType(RequestProtocolType protocolType); 151 void SetIsM3u8Request(bool isM3u8Request); 152 std::atomic<bool> isHeaderUpdated_ {false}; 153 std::atomic<bool> haveRedirectRetry_ {false}; 154 private: 155 void WaitHeaderUpdated() const; 156 std::string url_; 157 double duration_ {0.0}; 158 DataSaveFunc saveData_; 159 StatusCallbackFunc statusCallback_; 160 DownloadDoneCbFunc downloadDoneCallback_; 161 mutable std::atomic<bool> isHeaderUpdating_ {false}; 162 HeaderInfo headerInfo_; 163 std::map<std::string, std::string> httpHeader_; 164 RequestInfo requestInfo_ {}; 165 bool isEos_ {false}; // file download finished 166 int64_t startPos_ {0}; 167 int64_t endPos_ {-1}; 168 int64_t startTimePos_ {0}; 169 bool isDownloading_ {false}; 170 bool requestWholeFile_ {false}; 171 int requestSize_ {0}; 172 int preRequestSize_ {0}; 173 int retryTimes_ {0}; 174 int32_t clientError_ {0}; 175 int32_t serverError_ {0}; 176 bool shouldSaveData_ {true}; 177 int64_t downloadStartTime_ {0}; 178 int64_t downloadDoneTime_ {0}; 179 int64_t realRecvContentLen_ {0}; 180 friend class Downloader; 181 std::string location_; 182 mutable std::atomic<size_t> times_ {0}; 183 std::atomic<bool> isInterruptNeeded_{false}; 184 std::atomic<bool> retryOnGoing_ {false}; 185 int64_t dropedDataLen_ {0}; 186 std::atomic<bool> isFirstRangeRequestReady_ {false}; 187 bool isM3u8Request_ {false}; 188 bool isIndexM3u8Request_ {false}; 189 bool isAuthRequest_ {false}; 190 RequestProtocolType protocolType_ {RequestProtocolType::HTTP}; 191 }; 192 193 class Downloader { 194 public: 195 explicit Downloader(const std::string& name) noexcept; 196 explicit Downloader(const std::string& name, std::shared_ptr<MediaSourceLoaderCombinations> sourceLoader) noexcept; 197 virtual ~Downloader(); 198 199 bool Download(const std::shared_ptr<DownloadRequest>& request, int32_t waitMs); 200 void Start(); 201 void Pause(bool isAsync = false); 202 void Resume(); 203 void Stop(bool isAsync = false); 204 bool Seek(int64_t offset); 205 void Cancel(); 206 bool Retry(const std::shared_ptr<DownloadRequest>& request); 207 void SetRequestSize(size_t downloadRequestSize); 208 void GetIp(std::string &ip); 209 void SetAppUid(int32_t appUid); 210 const std::shared_ptr<DownloadRequest>& GetCurrentRequest(); 211 void SetInterruptState(bool isInterruptNeeded); 212 void SetAppState(bool isAppBackground); 213 void StopBufferring(); 214 std::string GetContentType(); 215 void ReStart(); 216 217 private: 218 bool BeginDownload(); 219 void HttpDownloadLoop(); 220 void RequestData(); 221 void HandlePlayingFinish(); 222 void HandleRetOK(); 223 void HandleResponseCb(int32_t clientCode, int32_t serverCode, Status& ret); 224 static size_t RxBodyData(void* buffer, size_t size, size_t nitems, void* userParam); 225 static size_t RxHeaderData(void* buffer, size_t size, size_t nitems, void* userParam); 226 static bool HandleContentRange(HeaderInfo* info, char* key, char* next, size_t size, size_t nitems); 227 static bool HandleContentType(HeaderInfo* info, char* key, char* next, size_t headerSize, 228 Downloader* mediaDownloader); 229 static bool HandleContentEncode(HeaderInfo* info, char* key, char* next, size_t size, size_t nitems); 230 static bool HandleContentLength(HeaderInfo* info, char* key, char* next, Downloader* mediaDownloader); 231 static bool HandleRange(HeaderInfo* info, char* key, char* next, size_t size, size_t nitems); 232 static void UpdateHeaderInfo(Downloader* mediaDownloader); 233 static size_t DropRetryData(void* buffer, size_t dataLen, Downloader* mediaDownloader); 234 static bool IsDropDataRetryRequest(Downloader* mediaDownloader); 235 static void UpdateCurRequest(Downloader* mediaDownloader, HeaderInfo* header); 236 static void HandleFileContentLen(HeaderInfo* header); 237 static void UpdateRequestSize(Downloader* mediaDownloader); 238 static void ToLower(char* str); 239 void PauseLoop(bool isAsync = false); 240 void WaitLoopPause(); 241 void NotifyLoopPause(); 242 void HandleRetErrorCode(); 243 void DonwloaderInit(const std::string& name); 244 void OpenAppUri(); 245 void HandleRedirect(Status& ret); 246 247 std::string name_; 248 std::shared_ptr<NetworkClient> client_; 249 std::shared_ptr<BlockingQueue<std::shared_ptr<DownloadRequest>>> requestQue_; 250 FairMutex operatorMutex_{}; 251 std::shared_ptr<DownloadRequest> currentRequest_; 252 std::atomic<bool> shouldStartNextRequest {false}; 253 int32_t noTaskLoopTimes_ {0}; 254 size_t downloadRequestSize_ {0}; 255 std::shared_ptr<Task> task_; 256 std::atomic<bool> isDestructor_ {false}; 257 std::atomic<bool> isClientClose_ {false}; 258 std::atomic<bool> isInterruptNeeded_{false}; 259 260 enum struct LoopStatus { 261 IDLE, 262 START, 263 PAUSE, 264 }; 265 std::atomic<LoopStatus> loopStatus_ {LoopStatus::IDLE}; 266 FairMutex loopPauseMutex_ {}; 267 ConditionVariable loopPauseCond_; 268 std::atomic<bool> isAppBackground_ {false}; 269 270 int64_t uuid_ {0}; 271 FairMutex closeMutex_ {}; 272 std::shared_ptr<MediaSourceLoaderCombinations> sourceLoader_; 273 std::shared_ptr<IMediaSourceLoadingRequest> loadingReques_; 274 bool isNotBlock_ {false}; 275 std::string appPreviousRequestUrl_ {}; 276 std::string contentType_; 277 bool isContentTypeUpdated_{false}; 278 ConditionVariable sleepCond_; 279 FairMutex sleepMutex_; 280 }; 281 } 282 } 283 } 284 } 285 #endif