/* * Copyright (c) 2023-2023 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define HST_LOG_TAG "FileSourcePlugin" #include "file_source_plugin.h" #include #include "common/log.h" namespace { constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, LOG_DOMAIN_SYSTEM_PLAYER, "HiStreamer" }; } namespace OHOS { namespace Media { namespace Plugins { namespace FileSource { namespace { size_t GetFileSize(const std::string& fileName) { size_t fileSize = 0; if (!fileName.empty()) { struct stat fileStatus {}; if (stat(fileName.c_str(), &fileStatus) == 0) { fileSize = static_cast(fileStatus.st_size); } } return fileSize; } } std::shared_ptr FileSourcePluginCreator(const std::string& name) { return std::make_shared(name); } Status FileSourceRegister(const std::shared_ptr& reg) { SourcePluginDef definition; definition.name = "FileSource"; definition.description = "File source"; definition.rank = 100; // 100: max rank Capability capability; capability.AppendFixedKey>(Tag::MEDIA_PROTOCOL_TYPE, {ProtocolType::FILE}); definition.AddInCaps(capability); auto func = [](const std::string& name) -> std::shared_ptr { return std::make_shared(name); }; definition.SetCreator(func); return reg->AddPlugin(definition); } PLUGIN_DEFINITION(FileSource, LicenseType::APACHE_V2, FileSourceRegister, [] {}); void* FileSourceAllocator::Alloc(size_t size) { if (size == 0) { MEDIA_LOG_E("Invalid zero size"); return nullptr; } return static_cast(new (std::nothrow) uint8_t[size]); } void FileSourceAllocator::Free(void* ptr) // NOLINT: void* { if (ptr != nullptr) { delete[](uint8_t*) ptr; } } FileSourcePlugin::FileSourcePlugin(std::string name) : SourcePlugin(std::move(name)), fp_(nullptr), fileSize_(0), seekable_(Seekable::SEEKABLE), position_(0) { MEDIA_LOG_D("IN"); } FileSourcePlugin::~FileSourcePlugin() { MEDIA_LOG_D("IN"); if (fp_) { std::fclose(fp_); fp_ = nullptr; } } Status FileSourcePlugin::Init() { MEDIA_LOG_D("IN"); mAllocator_ = std::make_shared(); return Status::OK; } Status FileSourcePlugin::Deinit() { MEDIA_LOG_D("IN"); CloseFile(); return Status::OK; } Status FileSourcePlugin::Prepare() { MEDIA_LOG_D("IN"); return Status::OK; } Status FileSourcePlugin::Reset() { MEDIA_LOG_D("IN"); CloseFile(); return Status::OK; } Status FileSourcePlugin::GetParameter(std::shared_ptr &meta) { MEDIA_LOG_D("IN"); return Status::ERROR_UNIMPLEMENTED; } Status FileSourcePlugin::SetParameter(const std::shared_ptr &meta) { MEDIA_LOG_D("IN"); return Status::ERROR_UNIMPLEMENTED; } std::shared_ptr FileSourcePlugin::GetAllocator() { MEDIA_LOG_D("IN"); return mAllocator_; } Status FileSourcePlugin::SetCallback(Callback* cb) { MEDIA_LOG_D("IN"); return Status::ERROR_UNIMPLEMENTED; } Status FileSourcePlugin::SetSource(std::shared_ptr source) { MEDIA_LOG_D("IN"); auto err = ParseFileName(source->GetSourceUri()); if (err != Status::OK) { MEDIA_LOG_E("Parse file name from uri fail, uri: " PUBLIC_LOG_S, source->GetSourceUri().c_str()); return err; } return OpenFile(); } Status FileSourcePlugin::Read(std::shared_ptr& buffer, uint64_t offset, size_t expectedLen) { return Read(0, buffer, offset, expectedLen); } Status FileSourcePlugin::Read(int32_t streamId, std::shared_ptr& buffer, uint64_t offset, size_t expectedLen) { FALSE_RETURN_V_MSG_E(fp_ != nullptr, Status::ERROR_WRONG_STATE, "invalid fp"); (void)offset; if (std::feof(fp_) || (fileSize_ == position_)) { MEDIA_LOG_W("It is the end of file!"); return Status::END_OF_STREAM; } if (buffer == nullptr) { buffer = std::make_shared(); } std::shared_ptr bufData; // There is no buffer, so alloc it if (buffer->IsEmpty()) { bufData = buffer->AllocMemory(GetAllocator(), expectedLen); } else { bufData = buffer->GetMemory(); } if (bufData == nullptr) { return Status::ERROR_AGAIN; } expectedLen = std::min(static_cast(fileSize_ - position_), expectedLen); expectedLen = std::min(bufData->GetCapacity(), expectedLen); MEDIA_LOG_DD("buffer position " PUBLIC_LOG_U64 ", expectedLen " PUBLIC_LOG_ZU, position_, expectedLen); auto bufDataAddr = bufData->GetWritableAddr(expectedLen); if (bufDataAddr == nullptr) { MEDIA_LOG_E("Read bufData GetWritableAddr fail"); return Status::ERROR_NO_MEMORY; } auto size = std::fread(bufDataAddr, sizeof(char), expectedLen, fp_); bufData->UpdateDataSize(size); position_ += bufData->GetSize(); MEDIA_LOG_DD("position_: " PUBLIC_LOG_U64 ", readSize: " PUBLIC_LOG_ZU, position_, bufData->GetSize()); return Status::OK; } Status FileSourcePlugin::GetSize(uint64_t& size) { MEDIA_LOG_DD("IN"); if (!fp_) { MEDIA_LOG_E("Need call SetSource() to open file first"); return Status::ERROR_WRONG_STATE; } size = fileSize_; MEDIA_LOG_DD("FileSize_: " PUBLIC_LOG_U64, size); return Status::OK; } Seekable FileSourcePlugin::GetSeekable() { MEDIA_LOG_DD("IN"); return seekable_; } Status FileSourcePlugin::SeekTo(uint64_t offset) { if (!fp_ || (offset > fileSize_) || (position_ == offset)) { MEDIA_LOG_E("Invalid operation"); return Status::ERROR_WRONG_STATE; } std::clearerr(fp_); if (std::fseek(fp_, static_cast(offset), SEEK_SET) != 0) { std::clearerr(fp_); (void)std::fseek(fp_, static_cast(position_), SEEK_SET); MEDIA_LOG_E("Seek to " PUBLIC_LOG_U64, offset); return Status::ERROR_UNKNOWN; } position_ = offset; if (std::feof(fp_)) { MEDIA_LOG_I("It is the end of file!"); } MEDIA_LOG_D("seek to position_: " PUBLIC_LOG_U64 " success", position_); return Status::OK; } Status FileSourcePlugin::ParseFileName(const std::string& uri) { if (uri.empty()) { MEDIA_LOG_E("uri is empty"); return Status::ERROR_INVALID_PARAMETER; } if (uri.find("file:/") != std::string::npos) { if (uri.find('#') != std::string::npos) { MEDIA_LOG_E("Invalid file uri format"); return Status::ERROR_INVALID_PARAMETER; } auto pos = uri.find("file:"); if (pos == std::string::npos) { MEDIA_LOG_E("Invalid file uri format"); return Status::ERROR_INVALID_PARAMETER; } pos += 5; // 5: offset if (uri.find("///", pos) != std::string::npos) { pos += 2; // 2: offset } else if (uri.find("//", pos) != std::string::npos) { pos += 2; // 2: offset pos = uri.find('/', pos); // skip host name if (pos == std::string::npos) { MEDIA_LOG_E("Invalid file uri format"); return Status::ERROR_INVALID_PARAMETER; } pos++; } fileName_ = uri.substr(pos); } else { fileName_ = uri; } MEDIA_LOG_D("fileName_: " PUBLIC_LOG_S, fileName_.c_str()); return Status::OK; } Status FileSourcePlugin::CheckFileStat() { struct stat fileStat; if (stat(fileName_.c_str(), &fileStat) < 0) { MEDIA_LOG_E("Cannot get info from " PUBLIC_LOG_S, fileName_.c_str()); return Status::ERROR_NOT_EXISTED; } if (S_ISDIR(fileStat.st_mode)) { MEDIA_LOG_E(PUBLIC_LOG_S " is directory", fileName_.c_str()); return Status::ERROR_UNSUPPORTED_FORMAT; } if (S_ISSOCK(fileStat.st_mode)) { MEDIA_LOG_E(PUBLIC_LOG_S " is a socket", fileName_.c_str()); return Status::ERROR_UNSUPPORTED_FORMAT; } return Status::OK; } Status FileSourcePlugin::OpenFile() { MEDIA_LOG_D("IN"); auto ret = CheckFileStat(); if (ret != Status::OK) { CloseFile(); return ret; } CloseFile(); fp_ = std::fopen(fileName_.c_str(), "rb"); if (fp_ == nullptr) { MEDIA_LOG_E("Fail to load file from " PUBLIC_LOG_S, fileName_.c_str()); return Status::ERROR_UNKNOWN; } fileSize_ = GetFileSize(fileName_); position_ = 0; MEDIA_LOG_D("FileName_: " PUBLIC_LOG_S ", fileSize_: " PUBLIC_LOG_U64, fileName_.c_str(), fileSize_); return Status::OK; } void FileSourcePlugin::CloseFile() { if (fp_) { MEDIA_LOG_I("close file"); std::fclose(fp_); fp_ = nullptr; } } } // namespace FileSource } // namespace Plugin } // namespace Media } // namespace OHOS