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 "TypeFinder"
17 
18 #include "type_finder.h"
19 
20 #include <algorithm>
21 #include "avcodec_trace.h"
22 #include "common/log.h"
23 #include "meta/any.h"
24 #include "osal/utils/util.h"
25 #include "plugin/plugin_info.h"
26 #include "plugin/plugin_manager_v2.h"
27 
28 namespace {
29 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, LOG_DOMAIN_DEMUXER, "TypeFinder" };
30 }
31 
32 namespace OHOS {
33 namespace Media {
34 using namespace Plugins;
35 namespace {
GetUriSuffix(const std::string & uri)36 std::string GetUriSuffix(const std::string& uri)
37 {
38     std::string suffix {""};
39     auto const pos = uri.find_last_of('.');
40     if (pos != std::string::npos) {
41         suffix = uri.substr(pos + 1);
42     }
43     return suffix;
44 }
45 
IsPluginSupportedExtension(Plugins::PluginInfo & pluginInfo,const std::string & extension)46 bool IsPluginSupportedExtension(Plugins::PluginInfo& pluginInfo, const std::string& extension)
47 {
48     if (pluginInfo.pluginType != Plugins::PluginType::DEMUXER) {
49         return false;
50     }
51     bool rtv = false;
52     auto info = pluginInfo.extra[PLUGIN_INFO_EXTRA_EXTENSIONS];
53     if (info.HasValue() && Any::IsSameTypeWith<std::vector<std::string>>(info)) {
54         for (const auto& ext : AnyCast<std::vector<std::string>&>(info)) {
55             if (ext == extension) {
56                 rtv = true;
57                 break;
58             }
59         }
60     }
61     return rtv;
62 }
63 
ToLower(std::string & str)64 void ToLower(std::string& str)
65 {
66     std::transform(str.begin(), str.end(), str.begin(), [](unsigned char ch) { return std::tolower(ch); });
67 }
68 } // namespace
69 
TypeFinder()70 TypeFinder::TypeFinder()
71     : sniffNeeded_(true),
72       uri_(),
73       mediaDataSize_(0),
74       pluginName_(),
75       plugins_(),
76       pluginRegistryChanged_(true),
77       task_(nullptr),
78       checkRange_(),
79       peekRange_(),
80       typeFound_()
81 {
82     MEDIA_LOG_D("In");
83 }
84 
~TypeFinder()85 TypeFinder::~TypeFinder()
86 {
87     MEDIA_LOG_D("In");
88     if (task_) {
89         task_->Stop();
90     }
91 }
92 
IsSniffNeeded(std::string uri)93 bool TypeFinder::IsSniffNeeded(std::string uri)
94 {
95     if (uri.empty() || pluginRegistryChanged_) {
96         return true;
97     }
98 
99     ToLower(uri);
100     return uri_ != uri;
101 }
102 
Init(std::string uri,uint64_t mediaDataSize,std::function<Status (int32_t,uint64_t,size_t)> checkRange,std::function<Status (int32_t,uint64_t,size_t,std::shared_ptr<Buffer> &)> peekRange,int32_t streamId)103 void TypeFinder::Init(std::string uri, uint64_t mediaDataSize,
104     std::function<Status(int32_t, uint64_t, size_t)> checkRange,
105     std::function<Status(int32_t, uint64_t, size_t, std::shared_ptr<Buffer>&)> peekRange, int32_t streamId)
106 {
107     streamID_ = streamId;
108     mediaDataSize_ = mediaDataSize;
109     checkRange_ = std::move(checkRange);
110     peekRange_ = std::move(peekRange);
111     sniffNeeded_ = IsSniffNeeded(uri);
112     if (sniffNeeded_) {
113         uri_.swap(uri);
114         pluginName_.clear();
115     }
116 }
117 
118 /**
119  * FindMediaType for seekable source, is a sync interface.
120  * @return plugin names for the found media type.
121  */
FindMediaType()122 std::string TypeFinder::FindMediaType()
123 {
124     MediaAVCodec::AVCodecTrace trace("TypeFinder::FindMediaType");
125     if (sniffNeeded_) {
126         pluginName_ = SniffMediaType();
127         if (!pluginName_.empty()) {
128             sniffNeeded_ = false;
129         }
130     }
131     return pluginName_;
132 }
133 
134 /**
135  * FindMediaTypeAsync for non-seekable source
136  * @param typeFound is a callback called when media type found.
137  */
FindMediaTypeAsync(std::function<void (std::string)> typeFound)138 void TypeFinder::FindMediaTypeAsync(std::function<void(std::string)> typeFound)
139 {
140     typeFound_ = std::move(typeFound);
141     task_ = std::make_shared<Task>("TypeFinder");
142     task_->RegisterJob([this]() {
143         DoTask();
144         return 0;
145     });
146     task_->Start();
147 }
148 
ReadAt(int64_t offset,std::shared_ptr<Buffer> & buffer,size_t expectedLen)149 Status TypeFinder::ReadAt(int64_t offset, std::shared_ptr<Buffer>& buffer, size_t expectedLen)
150 {
151     if (!buffer || expectedLen == 0 || !IsOffsetValid(offset)) {
152         MEDIA_LOG_E("Buffer empty: " PUBLIC_LOG_D32 ", expectedLen: " PUBLIC_LOG_ZU ", offset: "
153             PUBLIC_LOG_D64, !buffer, expectedLen, offset);
154         return Status::ERROR_INVALID_PARAMETER;
155     }
156 
157     const int maxTryTimes = 3;
158     int i = 0;
159     while ((checkRange_(streamID_, offset, expectedLen) != Status::OK) && (i < maxTryTimes)) {
160         i++;
161         OSAL::SleepFor(5); // 5 ms
162     }
163     if (i == maxTryTimes) {
164         MEDIA_LOG_E("ReadAt failed try 5 times");
165         return Status::ERROR_NOT_ENOUGH_DATA;
166     }
167     FALSE_LOG_MSG(peekRange_(streamID_, static_cast<uint64_t>(offset), expectedLen, buffer) == Status::OK,
168         "PeekRange failed");
169     return Status::OK;
170 }
171 
GetSize(uint64_t & size)172 Status TypeFinder::GetSize(uint64_t& size)
173 {
174     size = mediaDataSize_;
175     return (mediaDataSize_ > 0) ? Status::OK : Status::ERROR_UNKNOWN;
176 }
177 
GetSeekable()178 Plugins::Seekable TypeFinder::GetSeekable()
179 {
180     return Plugins::Seekable::INVALID;
181 }
182 
DoTask()183 void TypeFinder::DoTask()
184 {
185     if (sniffNeeded_) {
186         pluginName_ = SniffMediaType();
187         if (pluginName_.empty()) {
188             pluginName_ = GuessMediaType();
189         }
190         sniffNeeded_ = false;
191     }
192     task_->StopAsync();
193     typeFound_(pluginName_);
194 }
195 
SniffMediaType()196 std::string TypeFinder::SniffMediaType()
197 {
198     std::string pluginName;
199     auto dataSource = shared_from_this();
200     pluginName = Plugins::PluginManagerV2::Instance().SnifferPlugin(PluginType::DEMUXER, dataSource);
201     return pluginName;
202 }
203 
GuessMediaType() const204 std::string TypeFinder::GuessMediaType() const
205 {
206     std::string pluginName;
207     std::string uriSuffix = GetUriSuffix(uri_);
208     if (uriSuffix.empty()) {
209         return "";
210     }
211     for (const auto& pluginInfo : plugins_) {
212         if (IsPluginSupportedExtension(*pluginInfo, uriSuffix)) {
213             pluginName = pluginInfo->name;
214             break;
215         }
216     }
217     return pluginName;
218 }
219 
IsOffsetValid(int64_t offset) const220 bool TypeFinder::IsOffsetValid(int64_t offset) const
221 {
222     return (mediaDataSize_ == 0) || (static_cast<int64_t>(mediaDataSize_) == -1) ||
223         offset < static_cast<int64_t>(mediaDataSize_);
224 }
225 
SortPlugins(const std::string & uriSuffix)226 void TypeFinder::SortPlugins(const std::string& uriSuffix)
227 {
228     if (uriSuffix.empty()) {
229         return;
230     }
231     std::stable_sort(
232         plugins_.begin(), plugins_.end(),
233         [&uriSuffix](const std::shared_ptr<Plugins::PluginInfo>& lhs,
234             const std::shared_ptr<Plugins::PluginInfo>& rhs) {
235             if (IsPluginSupportedExtension(*lhs, uriSuffix)) {
236                 return (lhs->rank >= rhs->rank) || !IsPluginSupportedExtension(*rhs, uriSuffix);
237             } else {
238                 return (lhs->rank >= rhs->rank) && !IsPluginSupportedExtension(*rhs, uriSuffix);
239             }
240         });
241 }
242 
GetStreamID()243 int32_t TypeFinder::GetStreamID()
244 {
245     return streamID_;
246 }
247 } // namespace Media
248 } // namespace OHOS
249