1 /* 2 * Copyright (c) 2023-2023 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 32 namespace OHOS { 33 namespace Media { 34 namespace Plugins { 35 namespace HttpPlugin { 36 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, LOG_DOMAIN_STREAM_SOURCE, "HiStreamer" }; 37 38 enum struct DownloadStatus { 39 PARTTAL_DOWNLOAD, 40 }; 41 42 struct HeaderInfo { 43 char contentType[32]; // 32 chars 44 size_t fileContentLen {0}; 45 mutable size_t retryTimes {0}; 46 const static size_t maxRetryTimes {100}; 47 const static int sleepTime {10}; 48 long contentLen {0}; 49 bool isChunked {false}; 50 std::atomic<bool> isClosed {false}; 51 bool isServerAcceptRange {false}; 52 UpdateHeaderInfo53 void Update(const HeaderInfo* info) 54 { 55 NZERO_LOG(memcpy_s(contentType, sizeof(contentType), info->contentType, sizeof(contentType))); 56 fileContentLen = info->fileContentLen; 57 contentLen = info->contentLen; 58 isChunked = info->isChunked; 59 } 60 GetFileContentLengthHeaderInfo61 size_t GetFileContentLength() const 62 { 63 while (fileContentLen == 0 && !isChunked && !isClosed && retryTimes < maxRetryTimes) { 64 OSAL::SleepFor(sleepTime); // 10, wait for fileContentLen updated 65 retryTimes++; 66 } 67 return fileContentLen; 68 } 69 }; 70 71 // uint8_t* : the data should save 72 // uint32_t : length 73 using DataSaveFunc = std::function<bool(uint8_t*, uint32_t)>; 74 class Downloader; 75 class DownloadRequest; 76 using StatusCallbackFunc = std::function<void(DownloadStatus, std::shared_ptr<Downloader>&, 77 std::shared_ptr<DownloadRequest>&)>; 78 using DownloadDoneCbFunc = std::function<void(const std::string&, const std::string&)>; 79 80 class DownloadRequest { 81 public: 82 DownloadRequest(const std::string& url, DataSaveFunc saveData, StatusCallbackFunc statusCallback, 83 bool requestWholeFile = false); 84 DownloadRequest(const std::string& url, double duration, DataSaveFunc saveData, StatusCallbackFunc statusCallback, 85 bool requestWholeFile = false); 86 DownloadRequest(DataSaveFunc saveData, StatusCallbackFunc statusCallback, RequestInfo requestInfo, 87 bool requestWholeFile = false); 88 DownloadRequest(double duration, DataSaveFunc saveData, StatusCallbackFunc statusCallback, RequestInfo requestInfo, 89 bool requestWholeFile = false); 90 91 size_t GetFileContentLength() const; 92 size_t GetFileContentLengthNoWait() const; 93 void SaveHeader(const HeaderInfo* header); 94 Seekable IsChunked(bool isInterruptNeeded); 95 bool IsEos() const; 96 int GetRetryTimes() const; 97 int32_t GetClientError() const; 98 int32_t GetServerError() const; IsSame(const std::shared_ptr<DownloadRequest> & other)99 bool IsSame(const std::shared_ptr<DownloadRequest>& other) const 100 { 101 return url_ == other->url_ && startPos_ == other->startPos_; 102 } GetUrl()103 const std::string GetUrl() const 104 { 105 return url_; 106 } 107 bool IsClosed() const; 108 void Close(); 109 double GetDuration() const; 110 void SetStartTimePos(int64_t startTimePos); 111 void SetRangePos(int64_t startPos, int64_t endPos); 112 void SetDownloadDoneCb(DownloadDoneCbFunc downloadDoneCallback); 113 int64_t GetNowTime(); 114 uint32_t GetBitRate() const; 115 bool IsChunkedVod() const; 116 bool IsM3u8Request() const; 117 bool IsServerAcceptRange() const; 118 void GetLocation(std::string& location) const; 119 void SetIsM3u8Request(bool isM3u8Request); 120 private: 121 void WaitHeaderUpdated() const; 122 std::string url_; 123 double duration_ {0.0}; 124 DataSaveFunc saveData_; 125 StatusCallbackFunc statusCallback_; 126 DownloadDoneCbFunc downloadDoneCallback_; 127 128 HeaderInfo headerInfo_; 129 std::map<std::string, std::string> httpHeader_; 130 RequestInfo requestInfo_ {}; 131 132 bool isHeaderUpdated {false}; 133 bool isEos_ {false}; // file download finished 134 int64_t startPos_ {0}; 135 int64_t endPos_ {-1}; 136 int64_t startTimePos_ {0}; 137 bool isDownloading_ {false}; 138 bool requestWholeFile_ {false}; 139 int requestSize_ {0}; 140 int retryTimes_ {0}; 141 int32_t clientError_ {0}; 142 int32_t serverError_ {0}; 143 bool shouldSaveData_ {true}; 144 int64_t downloadStartTime_ {0}; 145 int64_t downloadDoneTime_ {0}; 146 int64_t realRecvContentLen_ {0}; 147 friend class Downloader; 148 std::string location_; 149 mutable size_t times_ {0}; 150 std::atomic<bool> isInterruptNeeded_{false}; 151 std::atomic<bool> retryOnGoing_ {false}; 152 int64_t dropedDataLen_ {0}; 153 std::atomic<bool> isFirstRangeRequestReady_ {false}; 154 bool isM3u8Request_ {false}; 155 }; 156 157 class Downloader { 158 public: 159 explicit Downloader(const std::string& name) noexcept; 160 virtual ~Downloader(); 161 162 bool Download(const std::shared_ptr<DownloadRequest>& request, int32_t waitMs); 163 void Start(); 164 void Pause(bool isAsync = false); 165 void Resume(); 166 void Stop(bool isAsync = false); 167 bool Seek(int64_t offset); 168 void Cancel(); 169 bool Retry(const std::shared_ptr<DownloadRequest>& request); 170 void SetRequestSize(size_t downloadRequestSize); 171 void GetIp(std::string &ip); 172 void SetAppUid(int32_t appUid); 173 const std::shared_ptr<DownloadRequest>& GetCurrentRequest(); 174 void SetInterruptState(bool isInterruptNeeded); 175 176 private: 177 bool BeginDownload(); 178 179 void HttpDownloadLoop(); 180 void RequestData(); 181 void HandlePlayingFinish(); 182 void HandleRetOK(); 183 static size_t RxBodyData(void* buffer, size_t size, size_t nitems, void* userParam); 184 static size_t RxHeaderData(void* buffer, size_t size, size_t nitems, void* userParam); 185 static bool HandleContentRange(HeaderInfo* info, char* key, char* next, size_t size, size_t nitems); 186 static bool HandleContentType(HeaderInfo* info, char* key, char* next, size_t size, size_t nitems); 187 static bool HandleContentEncode(HeaderInfo* info, char* key, char* next, size_t size, size_t nitems); 188 static bool HandleContentLength(HeaderInfo* info, char* key, char* next, Downloader* mediaDownloader); 189 static bool HandleRange(HeaderInfo* info, char* key, char* next, size_t size, size_t nitems); 190 static void UpdateHeaderInfo(Downloader* mediaDownloader); 191 static size_t DropRetryData(void* buffer, size_t dataLen, Downloader* mediaDownloader); 192 static bool IsDropDataRetryRequest(Downloader* mediaDownloader); 193 static void UpdateCurRequest(Downloader* mediaDownloader, HeaderInfo* header); 194 void PauseLoop(bool isAsync = false); 195 void WaitLoopPause(); 196 void NotifyLoopPause(); 197 198 std::string name_; 199 std::shared_ptr<NetworkClient> client_; 200 std::shared_ptr<BlockingQueue<std::shared_ptr<DownloadRequest>>> requestQue_; 201 FairMutex operatorMutex_{}; 202 std::shared_ptr<DownloadRequest> currentRequest_; 203 std::atomic<bool> shouldStartNextRequest {false}; 204 int32_t noTaskLoopTimes_ {0}; 205 size_t downloadRequestSize_ {0}; 206 std::shared_ptr<Task> task_; 207 std::atomic<bool> isDestructor_ {false}; 208 std::atomic<bool> isClientClose_ {false}; 209 std::atomic<bool> isInterruptNeeded_{false}; 210 211 enum struct LoopStatus { 212 IDLE, 213 START, 214 PAUSE, 215 }; 216 std::atomic<LoopStatus> loopStatus_ {LoopStatus::IDLE}; 217 FairMutex loopPauseMutex_ {}; 218 ConditionVariable loopPauseCond_; 219 }; 220 } 221 } 222 } 223 } 224 #endif