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