/* * Copyright (c) 2021-2021 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 "Compatible_Check" #include "pipeline/core/compatible_check.h" #include #include #include #include "foundation/log.h" #include "plugin/common/plugin_attr_desc.h" namespace OHOS { namespace Media { namespace Pipeline { static constexpr uint8_t ALLOW_FIXED = 1 << 0; static constexpr uint8_t ALLOW_INTERVAL = 1 << 1; static constexpr uint8_t ALLOW_DISCRETE = 1 << 2; static inline bool IsFixedAllowed(uint8_t flags) { return ALLOW_FIXED & flags; } static inline bool IsIntervalAllowed(uint8_t flags) { return ALLOW_INTERVAL & flags; } static inline bool IsDiscreteAllowed(uint8_t flags) { return ALLOW_DISCRETE & flags; } template bool CapabilityValueCheck(CapabilityID key, std::pair inVals, uint8_t flags, std::function cmpFunc, Plugin::ValueType& outValue); template bool FixInvalDiscCapValCheck(CapabilityID key, const Plugin::ValueType& val1, const Plugin::ValueType& val2, Plugin::ValueType& outValue) { return CapabilityValueCheck(key, {val1, val2}, ALLOW_FIXED | ALLOW_INTERVAL | ALLOW_DISCRETE, [](T a, T b) { return a - b; }, outValue); } template bool FixDiscCapValCheck(CapabilityID key, const Plugin::ValueType& val1, const Plugin::ValueType& val2, Plugin::ValueType& outValue) { return CapabilityValueCheck(key, {val1, val2}, ALLOW_FIXED | ALLOW_DISCRETE, [](T a, T b) { return static_cast(a) - static_cast(b); }, outValue); } static std::vector g_allCapabilityId = { CapabilityID::AUDIO_SAMPLE_RATE, // 0 CapabilityID::AUDIO_CHANNELS, // 1 CapabilityID::AUDIO_CHANNEL_LAYOUT, // 2 CapabilityID::AUDIO_SAMPLE_FORMAT, // 3 CapabilityID::AUDIO_MPEG_VERSION, // 4 CapabilityID::AUDIO_MPEG_LAYER, // 5 CapabilityID::AUDIO_AAC_PROFILE, // 6 CapabilityID::AUDIO_AAC_LEVEL, // 7 CapabilityID::AUDIO_AAC_STREAM_FORMAT, // 8 CapabilityID::VIDEO_PIXEL_FORMAT, // 9 CapabilityID::VIDEO_BIT_STREAM_FORMAT, // 10 CapabilityID::MEDIA_BITRATE, // 11 }; template bool ExtractFixedCap(const Plugin::ValueType& value, Plugin::ValueType& fixedValue); static std::map> g_capExtrMap = { {g_allCapabilityId[0], ExtractFixedCap}, // 0 {g_allCapabilityId[1], ExtractFixedCap}, // 1 {g_allCapabilityId[2], ExtractFixedCap}, // 2 {g_allCapabilityId[3], ExtractFixedCap}, // 3 {g_allCapabilityId[4], ExtractFixedCap}, // 4 {g_allCapabilityId[5], ExtractFixedCap}, // 5 {g_allCapabilityId[6], ExtractFixedCap}, // 6 {g_allCapabilityId[7], ExtractFixedCap}, // 7 {g_allCapabilityId[8], ExtractFixedCap}, // 8 {g_allCapabilityId[9], ExtractFixedCap}, // 9 {g_allCapabilityId[10], ExtractFixedCap}, // 10 {g_allCapabilityId[11], ExtractFixedCap}, // 11 }; using CheckFunc = std::function; static std::map g_capabilityValueCheckMap = { {g_allCapabilityId[0], FixInvalDiscCapValCheck}, // 0 {g_allCapabilityId[1], FixInvalDiscCapValCheck}, // 1 {g_allCapabilityId[2], FixDiscCapValCheck}, // 2 {g_allCapabilityId[3], FixDiscCapValCheck}, // 3 {g_allCapabilityId[4], FixInvalDiscCapValCheck}, // 4 {g_allCapabilityId[5], FixInvalDiscCapValCheck}, // 5 {g_allCapabilityId[6], FixDiscCapValCheck}, // 6 {g_allCapabilityId[7], FixInvalDiscCapValCheck}, // 7 {g_allCapabilityId[8], FixDiscCapValCheck}, // 8 {g_allCapabilityId[9], FixDiscCapValCheck}, // 9 {g_allCapabilityId[10], FixDiscCapValCheck}, // 10 {g_allCapabilityId[11], FixInvalDiscCapValCheck}, // 11 }; static bool StringEqIgnoreCase(const std::string& s1, const std::string& s2) { if (s1.length() == s2.length()) { return std::equal(s1.begin(), s1.end(), s2.begin(), [](char a, char b) { return tolower(a) == tolower(b); }); } return false; } bool IsSubsetMime(const std::string& subset, const std::string& universe) { size_t devLinePosInMeta = subset.find_first_of('/'); if (devLinePosInMeta == 0 || devLinePosInMeta == std::string::npos) { MEDIA_LOG_E("wrong format of subset mime, must be xx/xxx"); return false; } if (universe == "*") { return true; } size_t devLinePosInCap = universe.find_first_of('/'); if (devLinePosInCap == 0 || devLinePosInCap == std::string::npos) { MEDIA_LOG_E("wrong format of universe mime, must be * or xx/* or xx/xxx"); return false; } // if media type is not the same, return false if (!StringEqIgnoreCase(subset.substr(0, devLinePosInMeta), universe.substr(0, devLinePosInCap))) { return false; } // if media type of capability is like audio/* video/* image/* etc. always return true if (universe.substr(devLinePosInCap + 1) == "*") { return true; } // left mime string compare if (!StringEqIgnoreCase(universe.substr(devLinePosInCap + 1), subset.substr(devLinePosInMeta + 1))) { return false; } return true; } template T Max(T val1, T val2, std::function compareFunc) { if (compareFunc(val1, val2) >= 0) { return val1; } return val2; } template T Min(T val1, T val2, std::function compareFunc) { if (compareFunc(val1, val2) <= 0) { return val1; } return val2; } template bool FFCapabilityCheck(const Plugin::FixedCapability& v1, const Plugin::FixedCapability& v2, std::function& cmpFunc, Plugin::ValueType& outValue) { if (cmpFunc(v1, v2) == 0) { outValue = v1; return true; } return false; } template bool FICapabilityCheck(const Plugin::FixedCapability& v1, const Plugin::IntervalCapability& v2, const std::function& cmpFunc, Plugin::ValueType& outValue) { T max = Max(v2.first, v2.second, cmpFunc); T min = Min(v2.first, v2.second, cmpFunc); if (cmpFunc(v1, min) >= 0 && cmpFunc(v1, max) <= 0) { outValue = v1; return true; } return false; } template bool FDCapabilityCheck(const Plugin::FixedCapability& v1, const Plugin::DiscreteCapability& v2, const std::function& cmpFunc, Plugin::ValueType& outValue) { if (std::any_of(v2.begin(), v2.end(), [&v1, &cmpFunc](const T& tmp){return cmpFunc(tmp, v1) == 0;})) { outValue = v1; return true; } return false; } template bool IICapabilityCheck(const Plugin::IntervalCapability& v1, const Plugin::IntervalCapability& v2, const std::function& cmpFunc, Plugin::ValueType& outValue) { T max1 = Max(v1.first, v1.second, cmpFunc); T min1 = Min(v1.first, v1.second, cmpFunc); T max2 = Max(v2.first, v2.second, cmpFunc); T min2 = Min(v2.first, v2.second, cmpFunc); T tmpMin = Max(min1, min2, cmpFunc); T tmpMax = Min(max1, max2, cmpFunc); auto compRes = cmpFunc(tmpMin, tmpMax); if (compRes > 0) { return false; } else if (compRes == 0) { outValue = Plugin::FixedCapability(tmpMin); } else { outValue = Plugin::IntervalCapability(tmpMin, tmpMax); } return true; } template bool IDCapabilityCheck(const Plugin::IntervalCapability& v1, const Plugin::DiscreteCapability& v2, const std::function& cmpFunc, Plugin::ValueType& outValue) { Plugin::DiscreteCapability tmpOut; for (const auto& oneValue : v2) { if (cmpFunc(oneValue, v1.first) >= 0 && cmpFunc(oneValue, v1.second) <= 0) { tmpOut.emplace_back(oneValue); } } if (tmpOut.empty()) { return false; } if (tmpOut.size() == 1) { outValue = Plugin::FixedCapability(tmpOut[0]); } else { outValue = Plugin::DiscreteCapability(tmpOut); } return true; } template bool DDCapabilityCheck(const Plugin::DiscreteCapability& v1, const Plugin::DiscreteCapability& v2, const std::function& cmpFunc, Plugin::ValueType& outValue) { Plugin::DiscreteCapability tmpOut; for (const auto& cap1 : v1) { if (std::any_of(v2.begin(), v2.end(), [&cap1, &cmpFunc](const T& tmp){return cmpFunc(cap1, tmp) == 0;})) { tmpOut.emplace_back(cap1); } } if (tmpOut.empty()) { return false; } if (tmpOut.size() == 1) { outValue = Plugin::FixedCapability(tmpOut[0]); } else { outValue = Plugin::DiscreteCapability(tmpOut); } return true; } void LogOutIncorrectType(CapabilityID key, uint8_t flags) { if (Plugin::HasTagInfo(static_cast(key))) { const auto& tuple = Plugin::g_tagInfoMap.at(static_cast(key)); const auto& typeName = std::get<2>(tuple); // secondary MEDIA_LOG_E("type of " PUBLIC_LOG_S " should be" " " PUBLIC_LOG_S "(" PUBLIC_LOG_C ")" " or Interval<" PUBLIC_LOG_S ">(" PUBLIC_LOG_C ")" " or Discrete<" PUBLIC_LOG_S ">(" PUBLIC_LOG_C ")", std::get<0>(tuple), typeName, IsFixedAllowed(flags)? 'o': 'x', typeName, IsFixedAllowed(flags)? 'o': 'x', typeName, IsFixedAllowed(flags)? 'o': 'x'); } else { MEDIA_LOG_E("capability " PUBLIC_LOG_D32 "is not in the map, may be update the map?", key); } } template bool FixedNumericalCapabilityCheck(CapabilityID key, const T& value2, const Plugin::ValueType& value1, uint8_t flags, std::function cmpFunc, Plugin::ValueType& outValue) { if (Plugin::Any::IsSameTypeWith(value1)) { return FFCapabilityCheck(value2, Plugin::AnyCast(value1), cmpFunc, outValue); } if (IsIntervalAllowed(flags) && Plugin::Any::IsSameTypeWith>(value1)) { return FICapabilityCheck(value2, Plugin::AnyCast>(value1), cmpFunc, outValue); } if (IsDiscreteAllowed(flags) && Plugin::Any::IsSameTypeWith>(value1)) { return FDCapabilityCheck(value2, Plugin::AnyCast>(value1), cmpFunc, outValue); } LogOutIncorrectType(key, flags); return false; } template bool IntervalNumericalCapabilityCheck(CapabilityID key, const Plugin::IntervalCapability& value2, const Plugin::ValueType& value1, uint8_t flags, std::function cmpFunc, Plugin::ValueType& outValue) { if (IsFixedAllowed(flags) && Plugin::Any::IsSameTypeWith(value1)) { return FICapabilityCheck(Plugin::AnyCast(value1), value2, cmpFunc, outValue); } if (Plugin::Any::IsSameTypeWith>(value1)) { return IICapabilityCheck(Plugin::AnyCast>(value1), value2, cmpFunc, outValue); } if (IsDiscreteAllowed(flags) && Plugin::Any::IsSameTypeWith>(value1)) { return IDCapabilityCheck(value2, Plugin::AnyCast>(value1), cmpFunc, outValue); } LogOutIncorrectType(key, flags); return false; } template bool DiscreteNumericalCapabilityCheck(CapabilityID key, const Plugin::DiscreteCapability& value2, const Plugin::ValueType& value1, uint8_t flags, std::function cmpFunc, Plugin::ValueType& outValue) { if (IsFixedAllowed(flags) && Plugin::Any::IsSameTypeWith(value1)) { return FDCapabilityCheck(Plugin::AnyCast(value1), value2, cmpFunc, outValue); } if (IsIntervalAllowed(flags) && Plugin::Any::IsSameTypeWith>(value1)) { return IDCapabilityCheck(Plugin::AnyCast>(value1), value2, cmpFunc, outValue); } if (Plugin::Any::IsSameTypeWith>(value1)) { return DDCapabilityCheck(Plugin::AnyCast>(value1), value2, cmpFunc, outValue); } LogOutIncorrectType(key, flags); return false; } template bool CapabilityValueCheck(CapabilityID key, std::pair inVals, uint8_t flags, std::function cmpFunc, Plugin::ValueType& outValue) { if (IsFixedAllowed(flags) && Plugin::Any::IsSameTypeWith>(inVals.first)) { return FixedNumericalCapabilityCheck(key, Plugin::AnyCast>(inVals.first), inVals.second, flags, cmpFunc,outValue); } if (IsIntervalAllowed(flags) && Plugin::Any::IsSameTypeWith>(inVals.first)) { return IntervalNumericalCapabilityCheck(key, Plugin::AnyCast>(inVals.first), inVals.second, flags, cmpFunc, outValue); } if (IsDiscreteAllowed(flags) && Plugin::Any::IsSameTypeWith>(inVals.first)) { return DiscreteNumericalCapabilityCheck(key, Plugin::AnyCast>(inVals.first), inVals.second, flags, cmpFunc, outValue); } LogOutIncorrectType(key, flags); return false; } bool MergeCapabilityKeys(const Capability& originCap, const Capability& otherCap, Capability& resCap) { resCap.keys.clear(); for (const auto& pairKey : originCap.keys) { auto oIte = otherCap.keys.find(pairKey.first); if (oIte == otherCap.keys.end()) { // if key is not in otherCap, then put into resCap resCap.keys.insert(pairKey); continue; } // if key is in otherCap, calculate the intersections if (g_capabilityValueCheckMap.count(pairKey.first) == 0) { MEDIA_LOG_W("capability " PUBLIC_LOG_S " cannot be applied, may be update the check map?", Plugin::Tag2String(static_cast(pairKey.first))); continue; } Plugin::ValueType tmp; if (g_capabilityValueCheckMap.at(pairKey.first)(pairKey.first, pairKey.second, oIte->second, tmp)) { resCap.keys[pairKey.first] = tmp; } else { // if no intersections return false resCap.keys.clear(); MEDIA_LOG_W("No intersections, originCap.mime: " PUBLIC_LOG_S ", tag: " PUBLIC_LOG_S, originCap.mime.c_str(), Plugin::Tag2String(static_cast(pairKey.first))); return false; } } // if key is otherCap but not in originCap, put into resCap for (const auto& pairKey : otherCap.keys) { if (resCap.keys.count(pairKey.first) == 0) { resCap.keys.insert(pairKey); } } return true; } bool MergeCapability(const Capability& originCap, const Capability& otherCap, Capability& resCap) { resCap.mime.clear(); resCap.keys.clear(); if (!IsSubsetMime(originCap.mime, otherCap.mime)) { return false; } if (!MergeCapabilityKeys(originCap, otherCap, resCap)) { return false; } resCap.mime = originCap.mime; return true; } bool ApplyCapabilitySet(const Capability& originCap, const CapabilitySet& capabilitySet, Capability& resCap) { Capability tmp; for (const auto& cap : capabilitySet) { if (MergeCapability(originCap, cap, resCap)) { return true; } } return false; } template bool ExtractFixedCap(const Plugin::ValueType& value, Plugin::ValueType& fixedValue) { if (Plugin::Any::IsSameTypeWith>(value)) { fixedValue = Plugin::AnyCast>(value); return true; } else if (Plugin::Any::IsSameTypeWith>(value)) { auto tmp = Plugin::AnyCast>(value); fixedValue = tmp.first; return true; } else if (Plugin::Any::IsSameTypeWith>(value)) { auto tmp = Plugin::AnyCast>(value); if (!tmp.empty()) { fixedValue = tmp[0]; return true; } } return false; } std::shared_ptr MetaToCapability(const Plugin::Meta& meta) { auto ret = std::make_shared(); std::string mime; if (meta.Get(mime)) { ret->mime = mime; } for (const auto& key : g_allCapabilityId) { Plugin::ValueType tmp; if (meta.GetData(static_cast(key), tmp)) { ret->keys[key] = tmp; } } return ret; } bool MergeMetaWithCapability(const Plugin::Meta& meta, const Capability& cap, Plugin::Meta& resMeta) { resMeta.Clear(); // change meta into capability firstly Capability metaCap; metaCap.mime = cap.mime; for (const auto& key : g_allCapabilityId) { Plugin::ValueType tmp; if (meta.GetData(static_cast(key), tmp)) { metaCap.keys[key] = tmp; } } Capability resCap; if (!MergeCapability(metaCap, cap, resCap)) { return false; } // merge capability resMeta = meta; resMeta.Set(cap.mime); for (const auto& oneCap : resCap.keys) { if (g_capExtrMap.count(oneCap.first) == 0) { continue; } auto func = g_capExtrMap[oneCap.first]; Plugin::ValueType tmp; if (func(oneCap.second, tmp)) { resMeta.SetData(static_cast(oneCap.first), tmp); } } return true; } } // namespace Pipeline } // namespace Media } // namespace OHOS