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