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
__anon389889c30402null64 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: %{private}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 void* addr = bufData->GetWritableAddr(expectedLen);
197 if (addr == nullptr) {
198 return Status::ERROR_AGAIN;
199 }
200 auto size = std::fread(addr, sizeof(char), expectedLen, fp_);
201 bufData->UpdateDataSize(size);
202 position_ += bufData->GetSize();
203 MEDIA_LOG_DD("position_: " PUBLIC_LOG_U64 ", readSize: " PUBLIC_LOG_ZU, position_, bufData->GetSize());
204 return Status::OK;
205 }
206
GetSize(uint64_t & size)207 Status FileSourcePlugin::GetSize(uint64_t& size)
208 {
209 MEDIA_LOG_DD("IN");
210 if (!fp_) {
211 MEDIA_LOG_E("Need call SetSource() to open file first");
212 return Status::ERROR_WRONG_STATE;
213 }
214 size = fileSize_;
215 MEDIA_LOG_DD("FileSize_: " PUBLIC_LOG_U64, size);
216 return Status::OK;
217 }
218
GetSeekable()219 Seekable FileSourcePlugin::GetSeekable()
220 {
221 MEDIA_LOG_DD("IN");
222 return seekable_;
223 }
224
SeekTo(uint64_t offset)225 Status FileSourcePlugin::SeekTo(uint64_t offset)
226 {
227 if (!fp_ || (offset > fileSize_) || (position_ == offset)) {
228 MEDIA_LOG_E("Invalid operation");
229 return Status::ERROR_WRONG_STATE;
230 }
231 std::clearerr(fp_);
232 if (std::fseek(fp_, static_cast<long int>(offset), SEEK_SET) != 0) {
233 std::clearerr(fp_);
234 (void)std::fseek(fp_, static_cast<long int>(position_), SEEK_SET);
235 MEDIA_LOG_E("Seek to " PUBLIC_LOG_U64, offset);
236 return Status::ERROR_UNKNOWN;
237 }
238 position_ = offset;
239 if (std::feof(fp_)) {
240 MEDIA_LOG_I("It is the end of file!");
241 }
242 MEDIA_LOG_D("seek to position_: " PUBLIC_LOG_U64 " success", position_);
243 return Status::OK;
244 }
245
ParseFileName(const std::string & uri)246 Status FileSourcePlugin::ParseFileName(const std::string& uri)
247 {
248 if (uri.empty()) {
249 MEDIA_LOG_E("uri is empty");
250 return Status::ERROR_INVALID_PARAMETER;
251 }
252 if (uri.find("file:/") != std::string::npos) {
253 if (uri.find('#') != std::string::npos) {
254 MEDIA_LOG_E("Invalid file uri format");
255 return Status::ERROR_INVALID_PARAMETER;
256 }
257 auto pos = uri.find("file:");
258 if (pos == std::string::npos) {
259 MEDIA_LOG_E("Invalid file uri format");
260 return Status::ERROR_INVALID_PARAMETER;
261 }
262 pos += 5; // 5: offset
263 if (uri.find("///", pos) != std::string::npos) {
264 pos += 2; // 2: offset
265 } else if (uri.find("//", pos) != std::string::npos) {
266 pos += 2; // 2: offset
267 pos = uri.find('/', pos); // skip host name
268 if (pos == std::string::npos) {
269 MEDIA_LOG_E("Invalid file uri format");
270 return Status::ERROR_INVALID_PARAMETER;
271 }
272 pos++;
273 }
274 fileName_ = uri.substr(pos);
275 } else {
276 fileName_ = uri;
277 }
278 MEDIA_LOG_D("fileName_: " PUBLIC_LOG_S, fileName_.c_str());
279 return Status::OK;
280 }
281
CheckFileStat()282 Status FileSourcePlugin::CheckFileStat()
283 {
284 struct stat fileStat;
285 if (stat(fileName_.c_str(), &fileStat) < 0) {
286 MEDIA_LOG_E("Cannot get info from " PUBLIC_LOG_S, fileName_.c_str());
287 return Status::ERROR_NOT_EXISTED;
288 }
289 if (S_ISDIR(fileStat.st_mode)) {
290 MEDIA_LOG_E(PUBLIC_LOG_S " is directory", fileName_.c_str());
291 return Status::ERROR_UNSUPPORTED_FORMAT;
292 }
293 if (S_ISSOCK(fileStat.st_mode)) {
294 MEDIA_LOG_E(PUBLIC_LOG_S " is a socket", fileName_.c_str());
295 return Status::ERROR_UNSUPPORTED_FORMAT;
296 }
297 return Status::OK;
298 }
299
OpenFile()300 Status FileSourcePlugin::OpenFile()
301 {
302 MEDIA_LOG_D("IN");
303 auto ret = CheckFileStat();
304 if (ret != Status::OK) {
305 CloseFile();
306 return ret;
307 }
308 CloseFile();
309 fp_ = std::fopen(fileName_.c_str(), "rb");
310 if (fp_ == nullptr) {
311 MEDIA_LOG_E("Fail to load file from " PUBLIC_LOG_S, fileName_.c_str());
312 return Status::ERROR_UNKNOWN;
313 }
314 fileSize_ = GetFileSize(fileName_);
315 position_ = 0;
316 MEDIA_LOG_D("FileName_: " PUBLIC_LOG_S ", fileSize_: " PUBLIC_LOG_U64, fileName_.c_str(), fileSize_);
317 return Status::OK;
318 }
319
CloseFile()320 void FileSourcePlugin::CloseFile()
321 {
322 if (fp_) {
323 MEDIA_LOG_I("close file");
324 std::fclose(fp_);
325 fp_ = nullptr;
326 }
327 }
328
IsLocalFd()329 bool FileSourcePlugin::IsLocalFd()
330 {
331 return true;
332 }
333 } // namespace FileSource
334 } // namespace Plugin
335 } // namespace Media
336 } // namespace OHOS
337