1 /*
2 * Copyright (c) 2022-2022 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 "HttpMediaDownloader"
16
17 #include "http_media_downloader.h"
18
19 namespace OHOS {
20 namespace Media {
21 namespace Plugin {
22 namespace HttpPlugin {
23 namespace {
24 #ifdef OHOS_LITE
25 constexpr int RING_BUFFER_SIZE = 5 * 48 * 1024;
26 constexpr int WATER_LINE = RING_BUFFER_SIZE / 30; //30 WATER_LINE:8192
27 #else
28 constexpr int RING_BUFFER_SIZE = 5 * 1024 * 1024;
29 constexpr int WATER_LINE = 8192; // WATER_LINE:8192
30 #endif
31 }
32
HttpMediaDownloader()33 HttpMediaDownloader::HttpMediaDownloader() noexcept
34 {
35 buffer_ = std::make_shared<RingBuffer>(RING_BUFFER_SIZE);
36 buffer_->Init();
37
38 downloader_ = std::make_shared<Downloader>("http");
39 }
40
~HttpMediaDownloader()41 HttpMediaDownloader::~HttpMediaDownloader()
42 {
43 MEDIA_LOG_I("~HttpMediaDownloader dtor");
44 Close(false);
45 }
46
Open(const std::string & url)47 bool HttpMediaDownloader::Open(const std::string& url)
48 {
49 MEDIA_LOG_I("Open download " PUBLIC_LOG_S, url.c_str());
50 auto saveData = [this] (uint8_t*&& data, uint32_t&& len) {
51 return SaveData(std::forward<decltype(data)>(data), std::forward<decltype(len)>(len));
52 };
53 FALSE_RETURN_V(statusCallback_ != nullptr, false);
54 auto realStatusCallback = [this] (DownloadStatus&& status, std::shared_ptr<Downloader>& downloader,
55 std::shared_ptr<DownloadRequest>& request) {
56 statusCallback_(status, downloader_, std::forward<decltype(request)>(request));
57 };
58 downloadRequest_ = std::make_shared<DownloadRequest>(url, saveData, realStatusCallback);
59 downloader_->Download(downloadRequest_, -1); // -1
60 buffer_->SetMediaOffset(0);
61 downloader_->Start();
62 return true;
63 }
64
Close(bool isAsync)65 void HttpMediaDownloader::Close(bool isAsync)
66 {
67 buffer_->SetActive(false);
68 downloader_->Stop(isAsync);
69 cvReadWrite_.NotifyOne();
70 }
71
Pause()72 void HttpMediaDownloader::Pause()
73 {
74 bool cleanData = GetSeekable() != Seekable::SEEKABLE;
75 buffer_->SetActive(false, cleanData);
76 downloader_->Pause();
77 cvReadWrite_.NotifyOne();
78 }
79
Resume()80 void HttpMediaDownloader::Resume()
81 {
82 buffer_->SetActive(true);
83 downloader_->Resume();
84 cvReadWrite_.NotifyOne();
85 }
86
Read(unsigned char * buff,unsigned int wantReadLength,unsigned int & realReadLength,bool & isEos)87 bool HttpMediaDownloader::Read(unsigned char* buff, unsigned int wantReadLength,
88 unsigned int& realReadLength, bool& isEos)
89 {
90 FALSE_RETURN_V(buffer_ != nullptr, false);
91 isEos = false;
92 while (buffer_->GetSize() == 0) {
93 isEos = downloadRequest_->IsEos();
94 bool isClosed = downloadRequest_->IsClosed();
95 if (isEos || isClosed) {
96 MEDIA_LOG_D("HttpMediaDownloader read return, isEos: " PUBLIC_LOG_D32 ", isClosed: "
97 PUBLIC_LOG_D32, isEos, isClosed);
98 realReadLength = 0;
99 return false;
100 }
101 OSAL::SleepFor(5); // 5
102 }
103 if (buffer_->GetMediaOffset() + wantReadLength <= downloadRequest_->GetFileContentLength() &&
104 (buffer_->GetSize() < wantReadLength)) {
105 MEDIA_LOG_D("wantReadLength larger than available, wait for write");
106 OSAL::ScopedLock lock(mutex_);
107 cvReadWrite_.Wait(lock, [this, wantReadLength] {
108 return buffer_->GetSize() >= wantReadLength || downloadRequest_->IsEos() || downloadRequest_->IsClosed();
109 });
110 }
111 realReadLength = buffer_->ReadBuffer(buff, wantReadLength, 2); // wait 2 times
112 MEDIA_LOG_D("Read: wantReadLength " PUBLIC_LOG_D32 ", realReadLength " PUBLIC_LOG_D32 ", isEos "
113 PUBLIC_LOG_D32, wantReadLength, realReadLength, isEos);
114 return true;
115 }
116
Seek(int offset)117 bool HttpMediaDownloader::Seek(int offset)
118 {
119 FALSE_RETURN_V(buffer_ != nullptr, false);
120 MEDIA_LOG_I("Seek: buffer size " PUBLIC_LOG_ZU ", offset " PUBLIC_LOG_D32, buffer_->GetSize(), offset);
121 if (buffer_->Seek(offset)) {
122 return true;
123 }
124 buffer_->SetActive(false); // First clear buffer, avoid no available buffer then task pause never exit.
125 downloader_->Pause();
126 buffer_->Clear();
127 buffer_->SetActive(true);
128 bool result = downloader_->Seek(offset);
129 if (result) {
130 buffer_->SetMediaOffset(offset);
131 }
132 downloader_->Resume();
133 return result;
134 }
135
GetContentLength() const136 size_t HttpMediaDownloader::GetContentLength() const
137 {
138 return downloadRequest_->GetFileContentLength();
139 }
140
GetDuration() const141 double HttpMediaDownloader::GetDuration() const
142 {
143 return 0;
144 }
145
GetSeekable() const146 Seekable HttpMediaDownloader::GetSeekable() const
147 {
148 return downloadRequest_->IsChunked() ? Seekable::UNSEEKABLE : Seekable::SEEKABLE;
149 }
150
SetCallback(Callback * cb)151 void HttpMediaDownloader::SetCallback(Callback* cb)
152 {
153 callback_ = cb;
154 }
155
SetStatusCallback(StatusCallbackFunc cb)156 void HttpMediaDownloader::SetStatusCallback(StatusCallbackFunc cb)
157 {
158 statusCallback_ = cb;
159 }
160
GetStartedStatus()161 bool HttpMediaDownloader::GetStartedStatus()
162 {
163 return startedPlayStatus_;
164 }
165
SaveData(uint8_t * data,uint32_t len)166 bool HttpMediaDownloader::SaveData(uint8_t* data, uint32_t len)
167 {
168 FALSE_RETURN_V(buffer_->WriteBuffer(data, len), false);
169 cvReadWrite_.NotifyOne();
170 size_t bufferSize = buffer_->GetSize();
171 double ratio = (static_cast<double>(bufferSize)) / RING_BUFFER_SIZE;
172 if ((bufferSize >= WATER_LINE ||
173 bufferSize >= downloadRequest_->GetFileContentLength() / 2) && !aboveWaterline_) { // 2
174 aboveWaterline_ = true;
175 MEDIA_LOG_I("Send http aboveWaterline event, ringbuffer ratio " PUBLIC_LOG_F, ratio);
176 if (callback_ != nullptr) {
177 callback_->OnEvent({PluginEventType::ABOVE_LOW_WATERLINE, {ratio}, "http"});
178 }
179 startedPlayStatus_ = true;
180 } else if (bufferSize < WATER_LINE && aboveWaterline_) {
181 aboveWaterline_ = false;
182 MEDIA_LOG_I("Send http belowWaterline event, ringbuffer ratio " PUBLIC_LOG_F, ratio);
183 if (callback_ != nullptr) {
184 callback_->OnEvent({PluginEventType::BELOW_LOW_WATERLINE, {ratio}, "http"});
185 }
186 }
187 return true;
188 }
189 }
190 }
191 }
192 }