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