1 /*
2  * Copyright (c) 2021-2021 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 "HttpLiteSourcePlugin"
16 #include "http_lite_source_plugin.h"
17 #include "foundation/log.h"
18 #include "foundation/osal/utils/util.h"
19 
20 namespace OHOS {
21 namespace Media {
22 namespace Plugin {
23 namespace HttpLitePlugin {
24 namespace {
25 constexpr int DEFAULT_BUFFER_SIZE  = 200 * 1024;
26 constexpr int DEFAULT_HTTP_PRORITY = 32;
27 constexpr int8_t RANK_MAX          = 100;
28 }
29 
HttpSourcePluginCreater(const std::string & name)30 std::shared_ptr<SourcePlugin> HttpSourcePluginCreater(const std::string &name)
31 {
32     return std::make_shared<HttpSourcePlugin>(name);
33 }
34 
HttpSourceRegister(std::shared_ptr<Register> reg)35 const Status HttpSourceRegister(std::shared_ptr<Register> reg)
36 {
37     SourcePluginDef definition;
38     definition.name = "HttpLiteSource";
39     definition.description = "Http lite source";
40     definition.rank = RANK_MAX;
41     definition.protocol.emplace_back(ProtocolType::HTTP);
42     definition.protocol.emplace_back(ProtocolType::HTTPS);
43     definition.protocol.emplace_back(ProtocolType::HLS);
44     definition.creator = HttpSourcePluginCreater;
45     return reg->AddPlugin(definition);
46 }
__anona3910c420202null47 PLUGIN_DEFINITION(HttpLiteSource, LicenseType::APACHE_V2, HttpSourceRegister, [] {});
48 
Alloc(size_t size)49 void* HttpSourceAllocator::Alloc(size_t size)
50 {
51     if (size == 0) {
52         return nullptr;
53     }
54     return reinterpret_cast<void*>(new (std::nothrow) uint8_t[size]); // NOLINT: cast
55 }
56 
Free(void * ptr)57 void HttpSourceAllocator::Free(void* ptr) // NOLINT: void*
58 {
59     if (ptr != nullptr) {
60         delete[]static_cast<uint8_t*>(ptr);
61     }
62 }
63 
HttpSourcePlugin(const std::string & name)64 HttpSourcePlugin::HttpSourcePlugin(const std::string& name) noexcept
65     : SourcePlugin(std::move(name)),
66       url_(""),
67       certFile_(""),
68       needExit_(false),
69       isStream_(false),
70       bufferSize_(DEFAULT_BUFFER_SIZE),
71       position_(0),
72       waterline_(0),
73       fileSize_(0),
74       httpHandle_(nullptr),
75       mAllocator_(nullptr),
76       httpMutex_()
77 {
78     MEDIA_LOG_I("HttpSourcePlugin IN");
79 }
80 
~HttpSourcePlugin()81 HttpSourcePlugin::~HttpSourcePlugin()
82 {
83     MEDIA_LOG_I("~HttpSourcePlugin IN");
84 }
85 
Init()86 Status HttpSourcePlugin::Init()
87 {
88     OSAL::ScopedLock lock(httpMutex_);
89     MEDIA_LOG_D("Init IN");
90     httpHandle_ = std::make_shared<HttpLiteManager>();
91     if (httpHandle_ == nullptr) {
92         MEDIA_LOG_E("httpHandle_ create error");
93         return Status::ERROR_UNKNOWN;
94     }
95     mAllocator_ = std::make_shared<HttpSourceAllocator>();
96     if (mAllocator_ == nullptr) {
97         MEDIA_LOG_E("mAllocator_ create error");
98         return Status::ERROR_UNKNOWN;
99     }
100     MEDIA_LOG_D("Init OUT");
101     return Status::OK;
102 }
103 
Deinit()104 Status HttpSourcePlugin::Deinit()
105 {
106     OSAL::ScopedLock lock(httpMutex_);
107     MEDIA_LOG_D("IN");
108     CloseUri();
109     return Status::OK;
110 }
111 
Prepare()112 Status HttpSourcePlugin::Prepare()
113 {
114     OSAL::ScopedLock lock(httpMutex_);
115     MEDIA_LOG_D("IN");
116     return Status::OK;
117 }
118 
Reset()119 Status HttpSourcePlugin::Reset()
120 {
121     needExit_ = true;
122     {
123         OSAL::ScopedLock lock(httpMutex_);
124         needExit_ = false;
125         MEDIA_LOG_D("IN");
126         CloseUri();
127         return Status::OK;
128     }
129 }
130 
Stop()131 Status HttpSourcePlugin::Stop()
132 {
133     needExit_ = true;
134     {
135         MEDIA_LOG_D("IN");
136         OSAL::ScopedLock lock(httpMutex_);
137         needExit_ = false;
138         if (httpHandle_ != nullptr) {
139             httpHandle_->HttpClose();
140             httpHandle_ = nullptr;
141         }
142         MEDIA_LOG_D("OUT");
143         return Status::ERROR_UNKNOWN;
144     }
145 }
146 
GetParameter(Tag tag,ValueType & value)147 Status HttpSourcePlugin::GetParameter(Tag tag, ValueType &value)
148 {
149     OSAL::ScopedLock lock(httpMutex_);
150     MEDIA_LOG_D("IN");
151     switch (tag) {
152         case Tag::BUFFERING_SIZE:
153             value = bufferSize_;
154             return Status::OK;
155         case Tag::WATERLINE_HIGH:
156             value = waterline_;
157             return Status::OK;
158         default:
159             return Status::ERROR_INVALID_PARAMETER;
160     }
161 }
162 
SetParameter(Tag tag,const ValueType & value)163 Status HttpSourcePlugin::SetParameter(Tag tag, const ValueType &value)
164 {
165     OSAL::ScopedLock lock(httpMutex_);
166     MEDIA_LOG_D("IN");
167     switch (tag) {
168         case Tag::BUFFERING_SIZE:
169             bufferSize_ = AnyCast<uint32_t>(value);
170             return Status::OK;
171         case Tag::WATERLINE_HIGH:
172             waterline_ = AnyCast<uint32_t>(value);
173             return Status::OK;
174         default:
175             return Status::ERROR_INVALID_PARAMETER;
176     }
177 }
178 
SetCallback(Callback * cb)179 Status HttpSourcePlugin::SetCallback(Callback* cb)
180 {
181     MEDIA_LOG_D("IN");
182     callback_ = cb;
183     return Status::OK;
184 }
185 
SetSource(std::shared_ptr<MediaSource> source)186 Status HttpSourcePlugin::SetSource(std::shared_ptr<MediaSource> source)
187 {
188     OSAL::ScopedLock lock(httpMutex_);
189     MEDIA_LOG_D("SetSource IN");
190     if (httpHandle_ == nullptr) {
191         MEDIA_LOG_E("httpHandle_ null error");
192         return Status::ERROR_UNKNOWN;
193     }
194     auto uri = source->GetSourceUri();
195     MEDIA_LOG_D(PUBLIC_LOG_S, uri.c_str());
196     Status ret = OpenUri(uri);
197     if (ret != Status::OK) {
198         MEDIA_LOG_E("OpenUri error");
199         return ret;
200     }
201     MEDIA_LOG_D("OpenUri success");
202     unsigned int downloadPos = 0;
203     httpHandle_->GetHttpBufferRange(&position_, &downloadPos);
204     MEDIA_LOG_D("position_ " PUBLIC_LOG_U32 "downloadPos " PUBLIC_LOG_U32, position_, downloadPos);
205     int8_t retryTimes = 0;
206     while (!needExit_ && position_ == downloadPos && retryTimes < 60) { // 60
207         OHOS::Media::OSAL::SleepFor(200); // 200
208         httpHandle_->GetHttpBufferRange(&position_, &downloadPos);
209         retryTimes++;
210     }
211     MEDIA_LOG_D("position_ " PUBLIC_LOG_U32 "downloadPos " PUBLIC_LOG_U32, position_, downloadPos);
212     if (position_ == downloadPos) {
213         MEDIA_LOG_D("position_ == downloadPos");
214         httpHandle_->HttpClose();
215         return Status::ERROR_UNKNOWN;
216     }
217     isStream_ = httpHandle_->IsStreaming();
218     waterline_ = 20; // 20
219     httpHandle_->SetWaterline(waterline_, 0);
220     fileSize_ = isStream_ ? 0 : httpHandle_->GetContentLength();
221     MEDIA_LOG_D("SetSource OUT fileSize_ " PUBLIC_LOG_ZU, fileSize_);
222     return Status::OK;
223 }
224 
GetAllocator()225 std::shared_ptr<Allocator> HttpSourcePlugin::GetAllocator()
226 {
227     MEDIA_LOG_D("GetAllocator IN");
228     return mAllocator_;
229 }
230 
OnError(int httpError,int localError,void * param,int supportRetry)231 void HttpSourcePlugin::OnError(int httpError, int localError, void *param, int supportRetry)
232 {
233     MEDIA_LOG_D("httpError " PUBLIC_LOG_D32 " localError " PUBLIC_LOG_D32, httpError, localError);
234     auto plugin = reinterpret_cast<HttpSourcePlugin *>(param);
235     if (plugin == nullptr) {
236         return;
237     }
238     plugin->needExit_ = true;
239     plugin->OnHttpEvent(param, httpError, localError);
240 }
241 
OnHttpEvent(void * priv,int errorType,int32_t errorCode)242 Status HttpSourcePlugin::OnHttpEvent(void *priv, int errorType, int32_t errorCode)
243 {
244     if (priv == nullptr) {
245         MEDIA_LOG_D("priv null error");
246         return Status::ERROR_UNKNOWN;
247     }
248     auto plugin = reinterpret_cast<HttpSourcePlugin *>(priv);
249     plugin->callback_->OnEvent(
250         PluginEvent{PluginEventType::OTHER_ERROR, errorCode, "http lite error"});
251     return Status::OK;
252 }
253 
Read(std::shared_ptr<Buffer> & buffer,size_t expectedLen)254 Status HttpSourcePlugin::Read(std::shared_ptr<Buffer> &buffer, size_t expectedLen)
255 {
256     MEDIA_LOG_D("Read in");
257     if (httpHandle_ == nullptr) {
258         MEDIA_LOG_D("Read error");
259         return Status::ERROR_INVALID_PARAMETER;
260     }
261     if (buffer == nullptr) {
262         buffer = std::make_shared<Buffer>();
263     }
264 
265     OSAL::ScopedLock lock(httpMutex_);
266     std::shared_ptr<Memory> bufData;
267 
268     if (buffer->IsEmpty()) {
269         bufData = buffer->AllocMemory(GetAllocator(), expectedLen);
270     } else {
271         bufData = buffer->GetMemory();
272     }
273     unsigned int read = 0;
274     unsigned int write = 0;
275     unsigned int realReadSize = 0;
276     int isEos = -1;
277     int retryReadTimes = 0;
278 
279     httpHandle_->GetHttpBufferRange(&read, &write);
280 
281     MEDIA_LOG_D("read pos " PUBLIC_LOG_U32 " write pos " PUBLIC_LOG_U32 " expectedLen " PUBLIC_LOG_ZU,
282                 read, write, expectedLen);
283 
284     do {
285         MEDIA_LOG_D("bufData->GetCapacity() " PUBLIC_LOG_ZU, bufData->GetCapacity());
286         httpHandle_->HttpRead(bufData->GetWritableAddr(expectedLen), expectedLen, realReadSize, isEos);
287         if (realReadSize == 0 && retryReadTimes <= 200) { // 200
288             OSAL::SleepFor(10); // 10
289             retryReadTimes++;
290             continue;
291         }
292         break;
293     } while (true);
294     bufData->UpdateDataSize(realReadSize);
295     httpHandle_->GetHttpBufferRange(&position_, &write);
296     MEDIA_LOG_D("position_ : " PUBLIC_LOG_U32 ", readSize = " PUBLIC_LOG_ZU ", isEos " PUBLIC_LOG_D32,
297                 position_, bufData->GetSize(), isEos);
298     return Status::OK;
299 }
300 
GetSize(uint64_t & size)301 Status HttpSourcePlugin::GetSize(uint64_t& size)
302 {
303     OSAL::ScopedLock lock(httpMutex_);
304     MEDIA_LOG_D("IN");
305     size = fileSize_;
306     return Status::OK;
307 }
308 
GetSeekable()309 Seekable HttpSourcePlugin::GetSeekable()
310 {
311     OSAL::ScopedLock lock(httpMutex_);
312     MEDIA_LOG_D("IN");
313     return !isStream_ ? Seekable::SEEKABLE : Seekable::UNSEEKABLE;
314 }
315 
SeekTo(uint64_t offset)316 Status HttpSourcePlugin::SeekTo(uint64_t offset)
317 {
318     OSAL::ScopedLock lock(httpMutex_);
319     unsigned int readPos = 0;
320     unsigned int writePos = 0;
321     if ((httpHandle_ == nullptr) || (isStream_) || (position_ == offset) || (offset > fileSize_)) {
322         MEDIA_LOG_E("Invalid operation");
323         return Status::ERROR_INVALID_PARAMETER;
324     }
325     if (!httpHandle_->HttpSeek(offset)) {
326         MEDIA_LOG_E("seek to position_ " PUBLIC_LOG_U32 " failed", position_);
327         return Status::ERROR_UNKNOWN;
328     }
329     position_ = static_cast<unsigned int>(offset);
330     httpHandle_->GetHttpBufferRange(&readPos, &writePos);
331     MEDIA_LOG_D("offset = " PUBLIC_LOG_U32 " , after SeekTo readPos = " PUBLIC_LOG_U32 ", writePos = " PUBLIC_LOG_U32,
332         static_cast<uint32_t>(offset), readPos, writePos);
333     MEDIA_LOG_D("seek to position_ " PUBLIC_LOG_U32 " success", position_);
334     return Status::OK;
335 }
336 
OpenUri(std::string & url)337 Status HttpSourcePlugin::OpenUri(std::string &url)
338 {
339     MEDIA_LOG_I("OpenUri IN");
340     if (httpHandle_ == nullptr) {
341         return Status::ERROR_UNIMPLEMENTED;
342     }
343     httpHandle_->HttpClose();
344     HttpLiteAttr httpAttr;
345     httpAttr.certFile = certFile_;
346     httpAttr.priority = DEFAULT_HTTP_PRORITY;
347     httpAttr.bufferSize = bufferSize_;
348     httpAttr.pluginHandle = this;
349     httpAttr.callbackFunc = OnError;
350     httpAttr.notVerifyCert = true;
351     return httpHandle_->HttpOpen(url, httpAttr) ? Status::OK : Status::ERROR_UNKNOWN;
352 }
353 
CloseUri()354 void HttpSourcePlugin::CloseUri()
355 {
356     if (httpHandle_ != nullptr) {
357         MEDIA_LOG_D("close uri");
358         httpHandle_->HttpClose();
359         httpHandle_ = nullptr;
360     }
361 }
362 } // namespace HttpLitePlugin
363 } // namespace Plugin
364 } // namespace Media
365 } // namespace OHOS