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 #define HST_LOG_TAG "FileSourcePlugin"
17 
18 #include "file_source_plugin.h"
19 #include <sys/stat.h>
20 #include "common/log.h"
21 
22 namespace {
23 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, LOG_DOMAIN_SYSTEM_PLAYER, "HiStreamer" };
24 }
25 
26 namespace OHOS {
27 namespace Media {
28 namespace Plugins {
29 namespace FileSource {
30 namespace {
GetFileSize(const std::string & fileName)31 size_t GetFileSize(const std::string& fileName)
32 {
33     size_t fileSize = 0;
34     if (!fileName.empty()) {
35         struct stat fileStatus {};
36         if (stat(fileName.c_str(), &fileStatus) == 0) {
37             fileSize = static_cast<size_t>(fileStatus.st_size);
38         }
39     }
40     return fileSize;
41 }
42 }
FileSourcePluginCreator(const std::string & name)43 std::shared_ptr<SourcePlugin> FileSourcePluginCreator(const std::string& name)
44 {
45     return std::make_shared<FileSourcePlugin>(name);
46 }
47 
FileSourceRegister(const std::shared_ptr<Register> & reg)48 Status FileSourceRegister(const std::shared_ptr<Register>& reg)
49 {
50     SourcePluginDef definition;
51     definition.name = "FileSource";
52     definition.description = "File source";
53     definition.rank = 100; // 100: max rank
54     Capability capability;
55     capability.AppendFixedKey<std::vector<ProtocolType>>(Tag::MEDIA_PROTOCOL_TYPE, {ProtocolType::FILE});
56     definition.AddInCaps(capability);
57     auto func = [](const std::string& name) -> std::shared_ptr<SourcePlugin> {
58         return std::make_shared<FileSourcePlugin>(name);
59     };
60     definition.SetCreator(func);
61     return reg->AddPlugin(definition);
62 }
63 
__anona8e543bb0402null64 PLUGIN_DEFINITION(FileSource, LicenseType::APACHE_V2, FileSourceRegister, [] {});
65 
Alloc(size_t size)66 void* FileSourceAllocator::Alloc(size_t size)
67 {
68     if (size == 0) {
69         MEDIA_LOG_E("Invalid zero size");
70         return nullptr;
71     }
72     return static_cast<void*>(new (std::nothrow) uint8_t[size]);
73 }
74 
Free(void * ptr)75 void FileSourceAllocator::Free(void* ptr) // NOLINT: void*
76 {
77     if (ptr != nullptr) {
78         delete[](uint8_t*) ptr;
79     }
80 }
81 
FileSourcePlugin(std::string name)82 FileSourcePlugin::FileSourcePlugin(std::string name)
83     : SourcePlugin(std::move(name)), fp_(nullptr), fileSize_(0), seekable_(Seekable::SEEKABLE), position_(0)
84 {
85     MEDIA_LOG_D("IN");
86 }
87 
~FileSourcePlugin()88 FileSourcePlugin::~FileSourcePlugin()
89 {
90     MEDIA_LOG_D("IN");
91     if (fp_) {
92         std::fclose(fp_);
93         fp_ = nullptr;
94     }
95 }
96 
Init()97 Status FileSourcePlugin::Init()
98 {
99     MEDIA_LOG_D("IN");
100     mAllocator_ = std::make_shared<FileSourceAllocator>();
101     return Status::OK;
102 }
103 
Deinit()104 Status FileSourcePlugin::Deinit()
105 {
106     MEDIA_LOG_D("IN");
107     CloseFile();
108     return Status::OK;
109 }
110 
Prepare()111 Status FileSourcePlugin::Prepare()
112 {
113     MEDIA_LOG_D("IN");
114     return Status::OK;
115 }
116 
Reset()117 Status FileSourcePlugin::Reset()
118 {
119     MEDIA_LOG_D("IN");
120     CloseFile();
121     return Status::OK;
122 }
123 
GetParameter(std::shared_ptr<Meta> & meta)124 Status FileSourcePlugin::GetParameter(std::shared_ptr<Meta> &meta)
125 {
126     MEDIA_LOG_D("IN");
127     return Status::ERROR_UNIMPLEMENTED;
128 }
129 
SetParameter(const std::shared_ptr<Meta> & meta)130 Status FileSourcePlugin::SetParameter(const std::shared_ptr<Meta> &meta)
131 {
132     MEDIA_LOG_D("IN");
133     return Status::ERROR_UNIMPLEMENTED;
134 }
135 
GetAllocator()136 std::shared_ptr<Allocator> FileSourcePlugin::GetAllocator()
137 {
138     MEDIA_LOG_D("IN");
139     return mAllocator_;
140 }
141 
SetCallback(Callback * cb)142 Status FileSourcePlugin::SetCallback(Callback* cb)
143 {
144     MEDIA_LOG_D("IN");
145     return Status::ERROR_UNIMPLEMENTED;
146 }
147 
SetSource(std::shared_ptr<MediaSource> source)148 Status FileSourcePlugin::SetSource(std::shared_ptr<MediaSource> source)
149 {
150     MEDIA_LOG_D("IN");
151     auto err = ParseFileName(source->GetSourceUri());
152     if (err != Status::OK) {
153         MEDIA_LOG_E("Parse file name from uri fail, uri: " PUBLIC_LOG_S, source->GetSourceUri().c_str());
154         return err;
155     }
156     return OpenFile();
157 }
158 
Read(std::shared_ptr<Buffer> & buffer,uint64_t offset,size_t expectedLen)159 Status FileSourcePlugin::Read(std::shared_ptr<Buffer>& buffer, uint64_t offset, size_t expectedLen)
160 {
161     return Read(0, buffer, offset, expectedLen);
162 }
163 
Read(int32_t streamId,std::shared_ptr<Buffer> & buffer,uint64_t offset,size_t expectedLen)164 Status FileSourcePlugin::Read(int32_t streamId, std::shared_ptr<Buffer>& buffer, uint64_t offset, size_t expectedLen)
165 {
166     FALSE_RETURN_V_MSG_E(fp_ != nullptr, Status::ERROR_WRONG_STATE, "invalid fp");
167     (void)offset;
168     if (std::feof(fp_) || (fileSize_ == position_)) {
169         MEDIA_LOG_W("It is the end of file!");
170         return Status::END_OF_STREAM;
171     }
172     if (buffer == nullptr) {
173         buffer = std::make_shared<Buffer>();
174     }
175 
176     std::shared_ptr<Memory> bufData;
177 
178     // There is no buffer, so alloc it
179     if (buffer->IsEmpty()) {
180         bufData = buffer->AllocMemory(GetAllocator(), expectedLen);
181     } else {
182         bufData = buffer->GetMemory();
183     }
184     if (bufData == nullptr) {
185         return Status::ERROR_AGAIN;
186     }
187     expectedLen = std::min(static_cast<size_t>(fileSize_ - position_), expectedLen);
188     expectedLen = std::min(bufData->GetCapacity(), expectedLen);
189 
190     MEDIA_LOG_DD("buffer position " PUBLIC_LOG_U64 ", expectedLen " PUBLIC_LOG_ZU, position_, expectedLen);
191     auto bufDataAddr = bufData->GetWritableAddr(expectedLen);
192     if (bufDataAddr == nullptr) {
193         MEDIA_LOG_E("Read bufData GetWritableAddr fail");
194         return Status::ERROR_NO_MEMORY;
195     }
196     auto size = std::fread(bufDataAddr, sizeof(char), expectedLen, fp_);
197     bufData->UpdateDataSize(size);
198     position_ += bufData->GetSize();
199     MEDIA_LOG_DD("position_: " PUBLIC_LOG_U64 ", readSize: " PUBLIC_LOG_ZU, position_, bufData->GetSize());
200     return Status::OK;
201 }
202 
GetSize(uint64_t & size)203 Status FileSourcePlugin::GetSize(uint64_t& size)
204 {
205     MEDIA_LOG_DD("IN");
206     if (!fp_) {
207         MEDIA_LOG_E("Need call SetSource() to open file first");
208         return Status::ERROR_WRONG_STATE;
209     }
210     size = fileSize_;
211     MEDIA_LOG_DD("FileSize_: " PUBLIC_LOG_U64, size);
212     return Status::OK;
213 }
214 
GetSeekable()215 Seekable FileSourcePlugin::GetSeekable()
216 {
217     MEDIA_LOG_DD("IN");
218     return seekable_;
219 }
220 
SeekTo(uint64_t offset)221 Status FileSourcePlugin::SeekTo(uint64_t offset)
222 {
223     if (!fp_ || (offset > fileSize_) || (position_ == offset)) {
224         MEDIA_LOG_E("Invalid operation");
225         return Status::ERROR_WRONG_STATE;
226     }
227     std::clearerr(fp_);
228     if (std::fseek(fp_, static_cast<long int>(offset), SEEK_SET) != 0) {
229         std::clearerr(fp_);
230         (void)std::fseek(fp_, static_cast<long int>(position_), SEEK_SET);
231         MEDIA_LOG_E("Seek to " PUBLIC_LOG_U64, offset);
232         return Status::ERROR_UNKNOWN;
233     }
234     position_ = offset;
235     if (std::feof(fp_)) {
236         MEDIA_LOG_I("It is the end of file!");
237     }
238     MEDIA_LOG_D("seek to position_: " PUBLIC_LOG_U64 " success", position_);
239     return Status::OK;
240 }
241 
ParseFileName(const std::string & uri)242 Status FileSourcePlugin::ParseFileName(const std::string& uri)
243 {
244     if (uri.empty()) {
245         MEDIA_LOG_E("uri is empty");
246         return Status::ERROR_INVALID_PARAMETER;
247     }
248     if (uri.find("file:/") != std::string::npos) {
249         if (uri.find('#') != std::string::npos) {
250             MEDIA_LOG_E("Invalid file uri format");
251             return Status::ERROR_INVALID_PARAMETER;
252         }
253         auto pos = uri.find("file:");
254         if (pos == std::string::npos) {
255             MEDIA_LOG_E("Invalid file uri format");
256             return Status::ERROR_INVALID_PARAMETER;
257         }
258         pos += 5; // 5: offset
259         if (uri.find("///", pos) != std::string::npos) {
260             pos += 2; // 2: offset
261         } else if (uri.find("//", pos) != std::string::npos) {
262             pos += 2;                 // 2: offset
263             pos = uri.find('/', pos); // skip host name
264             if (pos == std::string::npos) {
265                 MEDIA_LOG_E("Invalid file uri format");
266                 return Status::ERROR_INVALID_PARAMETER;
267             }
268             pos++;
269         }
270         fileName_ = uri.substr(pos);
271     } else {
272         fileName_ = uri;
273     }
274     MEDIA_LOG_D("fileName_: " PUBLIC_LOG_S, fileName_.c_str());
275     return Status::OK;
276 }
277 
CheckFileStat()278 Status FileSourcePlugin::CheckFileStat()
279 {
280     struct stat fileStat;
281     if (stat(fileName_.c_str(), &fileStat) < 0) {
282         MEDIA_LOG_E("Cannot get info from " PUBLIC_LOG_S, fileName_.c_str());
283         return Status::ERROR_NOT_EXISTED;
284     }
285     if (S_ISDIR(fileStat.st_mode)) {
286         MEDIA_LOG_E(PUBLIC_LOG_S " is directory", fileName_.c_str());
287         return Status::ERROR_UNSUPPORTED_FORMAT;
288     }
289     if (S_ISSOCK(fileStat.st_mode)) {
290         MEDIA_LOG_E(PUBLIC_LOG_S " is a socket", fileName_.c_str());
291         return Status::ERROR_UNSUPPORTED_FORMAT;
292     }
293     return Status::OK;
294 }
295 
OpenFile()296 Status FileSourcePlugin::OpenFile()
297 {
298     MEDIA_LOG_D("IN");
299     auto ret = CheckFileStat();
300     if (ret != Status::OK) {
301         CloseFile();
302         return ret;
303     }
304     CloseFile();
305     fp_ = std::fopen(fileName_.c_str(), "rb");
306     if (fp_ == nullptr) {
307         MEDIA_LOG_E("Fail to load file from " PUBLIC_LOG_S, fileName_.c_str());
308         return Status::ERROR_UNKNOWN;
309     }
310     fileSize_ = GetFileSize(fileName_);
311     position_ = 0;
312     MEDIA_LOG_D("FileName_: " PUBLIC_LOG_S ", fileSize_: " PUBLIC_LOG_U64, fileName_.c_str(), fileSize_);
313     return Status::OK;
314 }
315 
CloseFile()316 void FileSourcePlugin::CloseFile()
317 {
318     if (fp_) {
319         MEDIA_LOG_I("close file");
320         std::fclose(fp_);
321         fp_ = nullptr;
322     }
323 }
324 } // namespace FileSource
325 } // namespace Plugin
326 } // namespace Media
327 } // namespace OHOS
328