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