Loading media/libmedia/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -260,6 +260,7 @@ cc_library_shared { srcs: [ "AudioCapabilities.cpp", "CodecCapabilities.cpp", "EncoderCapabilities.cpp", "VideoCapabilities.cpp", "CodecCapabilitiesUtils.cpp", ], Loading media/libmedia/EncoderCapabilities.cpp 0 → 100644 +205 −0 Original line number Diff line number Diff line /* * Copyright 2024, The Android Open Source Project * * 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 LOG_NDEBUG 0 #define LOG_TAG "EncoderCapabilities" #include <android-base/strings.h> #include <media/CodecCapabilities.h> #include <media/EncoderCapabilities.h> #include <media/stagefright/MediaCodecConstants.h> namespace android { const Range<int>& EncoderCapabilities::getQualityRange() { return mQualityRange; } const Range<int>& EncoderCapabilities::getComplexityRange() { return mComplexityRange; } // static int EncoderCapabilities::ParseBitrateMode(std::string mode) { for (Feature feat: sBitrateModes) { if (base::EqualsIgnoreCase(feat.mName, mode)) { return feat.mValue; } } return 0; } bool EncoderCapabilities::isBitrateModeSupported(int mode) { for (Feature feat : sBitrateModes) { if (mode == feat.mValue) { return (mBitControl & (1 << mode)) != 0; } } return false; } // static std::shared_ptr<EncoderCapabilities> EncoderCapabilities::Create(std::string mediaType, std::vector<ProfileLevel> profLevs, const sp<AMessage> &format) { std::shared_ptr<EncoderCapabilities> caps(new EncoderCapabilities()); caps->init(mediaType, profLevs, format); return caps; } void EncoderCapabilities::init(std::string mediaType, std::vector<ProfileLevel> profLevs, const sp<AMessage> &format) { // no support for complexity or quality yet mMediaType = mediaType; mProfileLevels = profLevs; mComplexityRange = Range(0, 0); mQualityRange = Range(0, 0); mBitControl = (1 << BITRATE_MODE_VBR); applyLevelLimits(); parseFromInfo(format); } void EncoderCapabilities::applyLevelLimits() { if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_FLAC)) { mComplexityRange = Range(0, 8); mBitControl = (1 << BITRATE_MODE_CQ); } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_AMR_NB) || base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_AMR_WB) || base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_G711_ALAW) || base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_G711_MLAW) || base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_MSGSM)) { mBitControl = (1 << BITRATE_MODE_CBR); } } void EncoderCapabilities::parseFromInfo(const sp<AMessage> &format) { AString complexityRangeAStr; if (format->findString("complexity-range", &complexityRangeAStr)) { std::optional<Range<int>> complexityRangeOpt = Range<int32_t>::Parse(std::string(complexityRangeAStr.c_str())); mComplexityRange = complexityRangeOpt.value_or(mComplexityRange); // TODO should we limit this to level limits? } AString qualityRangeAStr; if (format->findString("quality-range", &qualityRangeAStr)) { std::optional<Range<int>> qualityRangeOpt = Range<int32_t>::Parse(std::string(qualityRangeAStr.c_str())); mQualityRange = qualityRangeOpt.value_or(mQualityRange); } AString bitrateModesAStr; if (format->findString("feature-bitrate-modes", &bitrateModesAStr)) { mBitControl = 0; for (std::string mode: base::Split(std::string(bitrateModesAStr.c_str()), ",")) { mBitControl |= (1 << ParseBitrateMode(mode)); } } format->findInt32("complexity-default", &mDefaultComplexity); format->findInt32("quality-default", &mDefaultQuality); AString qualityScaleAStr; if (format->findString("quality-scale", &qualityScaleAStr)) { mQualityScale = std::string(qualityScaleAStr.c_str()); } } bool EncoderCapabilities::supports( std::optional<int> complexity, std::optional<int> quality, std::optional<int> profile) { bool ok = true; if (complexity) { ok &= mComplexityRange.contains(complexity.value()); } if (quality) { ok &= mQualityRange.contains(quality.value()); } if (profile) { ok &= std::any_of(mProfileLevels.begin(), mProfileLevels.end(), [&profile](ProfileLevel pl){ return pl.mProfile == profile.value(); }); } return ok; } void EncoderCapabilities::getDefaultFormat(sp<AMessage> &format) { // don't list trivial quality/complexity as default for now if (mQualityRange.upper() != mQualityRange.lower() && mDefaultQuality != 0) { format->setInt32(KEY_QUALITY, mDefaultQuality); } if (mComplexityRange.upper() != mComplexityRange.lower() && mDefaultComplexity != 0) { format->setInt32(KEY_COMPLEXITY, mDefaultComplexity); } // bitrates are listed in order of preference for (Feature feat : sBitrateModes) { if ((mBitControl & (1 << feat.mValue)) != 0) { format->setInt32(KEY_BITRATE_MODE, feat.mValue); break; } } } bool EncoderCapabilities::supportsFormat(const sp<AMessage> &format) { int32_t mode; if (format->findInt32(KEY_BITRATE_MODE, &mode) && !isBitrateModeSupported(mode)) { return false; } int tmp; std::optional<int> complexity = std::nullopt; if (format->findInt32(KEY_COMPLEXITY, &tmp)) { complexity = tmp; } if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_FLAC)) { int flacComplexity; if (format->findInt32(KEY_FLAC_COMPRESSION_LEVEL, &flacComplexity)) { if (!complexity) { complexity = flacComplexity; } else if (flacComplexity != complexity.value()) { ALOGE("Conflicting values for complexity and flac-compression-level," " which are %d and %d", complexity.value(), flacComplexity); return false; } } } // other audio parameters std::optional<int> profile = std::nullopt; if (format->findInt32(KEY_PROFILE, &tmp)) { profile = tmp; } if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_AAC)) { int aacProfile; if (format->findInt32(KEY_AAC_PROFILE, &aacProfile)) { if (!profile) { profile = aacProfile; } else if (aacProfile != profile.value()) { ALOGE("Conflicting values for profile and aac-profile, which are %d and %d", profile.value(), aacProfile); return false; } } } std::optional<int> quality = std::nullopt; if (format->findInt32(KEY_QUALITY, &tmp)) { quality = tmp; } return supports(complexity, quality, profile); } } // namespace android No newline at end of file media/libmedia/include/media/CodecCapabilities.h +3 −1 Original line number Diff line number Diff line Loading @@ -19,8 +19,9 @@ #define CODEC_CAPABILITIES_H_ #include <media/AudioCapabilities.h> #include <media/VideoCapabilities.h> #include <media/CodecCapabilitiesUtils.h> #include <media/EncoderCapabilities.h> #include <media/VideoCapabilities.h> #include <media/stagefright/foundation/ABase.h> #include <media/stagefright/foundation/AMessage.h> #include <media/stagefright/foundation/AString.h> Loading Loading @@ -54,6 +55,7 @@ private: std::shared_ptr<AudioCapabilities> mAudioCaps; std::shared_ptr<VideoCapabilities> mVideoCaps; std::shared_ptr<EncoderCapabilities> mEncoderCaps; }; } // namespace android Loading media/libmedia/include/media/CodecCapabilitiesUtils.h +15 −0 Original line number Diff line number Diff line Loading @@ -42,6 +42,21 @@ struct ProfileLevel { } }; struct Feature { std::string mName; int mValue; bool mDefault; bool mInternal; Feature(std::string name, int value, bool def, bool internal) { mName = name; mValue = value; mDefault = def; mInternal = internal; } Feature(std::string name, int value, bool def) : Feature(name, value, def, false /* internal */) {} }; /** * Immutable class for describing the range of two numeric values. * Loading media/libmedia/include/media/EncoderCapabilities.h 0 → 100644 +105 −0 Original line number Diff line number Diff line /* * Copyright 2024, The Android Open Source Project * * 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. */ #ifndef ENCODER_CAPABILITIES_H_ #define ENCODER_CAPABILITIES_H_ #include <media/CodecCapabilitiesUtils.h> #include <media/stagefright/foundation/AMessage.h> #include <utils/StrongPointer.h> namespace android { /** * A class that supports querying the encoding capabilities of a codec. */ struct EncoderCapabilities { /** * Returns the supported range of quality values. * * Quality is implementation-specific. As a general rule, a higher quality * setting results in a better image quality and a lower compression ratio. */ const Range<int>& getQualityRange(); /** * Returns the supported range of encoder complexity values. * <p> * Some codecs may support multiple complexity levels, where higher * complexity values use more encoder tools (e.g. perform more * intensive calculations) to improve the quality or the compression * ratio. Use a lower value to save power and/or time. */ const Range<int>& getComplexityRange(); /** Constant quality mode */ inline static constexpr int BITRATE_MODE_CQ = 0; /** Variable bitrate mode */ inline static constexpr int BITRATE_MODE_VBR = 1; /** Constant bitrate mode */ inline static constexpr int BITRATE_MODE_CBR = 2; /** Constant bitrate mode with frame drops */ inline static constexpr int BITRATE_MODE_CBR_FD = 3; /** * Query whether a bitrate mode is supported. */ bool isBitrateModeSupported(int mode); /** @hide */ static std::shared_ptr<EncoderCapabilities> Create(std::string mediaType, std::vector<ProfileLevel> profLevs, const sp<AMessage> &format); /** @hide */ void getDefaultFormat(sp<AMessage> &format); /** @hide */ bool supportsFormat(const sp<AMessage> &format); private: inline static const Feature sBitrateModes[] = { Feature("VBR", BITRATE_MODE_VBR, true), Feature("CBR", BITRATE_MODE_CBR, false), Feature("CQ", BITRATE_MODE_CQ, false), Feature("CBR-FD", BITRATE_MODE_CBR_FD, false) }; static int ParseBitrateMode(std::string mode); std::string mMediaType; std::vector<ProfileLevel> mProfileLevels; Range<int> mQualityRange; Range<int> mComplexityRange; int mBitControl; int mDefaultComplexity; int mDefaultQuality; std::string mQualityScale; /* no public constructor */ EncoderCapabilities() {} void init(std::string mediaType, std::vector<ProfileLevel> profLevs, const sp<AMessage> &format); void applyLevelLimits(); void parseFromInfo(const sp<AMessage> &format); bool supports(std::optional<int> complexity, std::optional<int> quality, std::optional<int> profile); }; } // namespace android #endif // ENCODER_CAPABILITIES_H_ No newline at end of file Loading
media/libmedia/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -260,6 +260,7 @@ cc_library_shared { srcs: [ "AudioCapabilities.cpp", "CodecCapabilities.cpp", "EncoderCapabilities.cpp", "VideoCapabilities.cpp", "CodecCapabilitiesUtils.cpp", ], Loading
media/libmedia/EncoderCapabilities.cpp 0 → 100644 +205 −0 Original line number Diff line number Diff line /* * Copyright 2024, The Android Open Source Project * * 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 LOG_NDEBUG 0 #define LOG_TAG "EncoderCapabilities" #include <android-base/strings.h> #include <media/CodecCapabilities.h> #include <media/EncoderCapabilities.h> #include <media/stagefright/MediaCodecConstants.h> namespace android { const Range<int>& EncoderCapabilities::getQualityRange() { return mQualityRange; } const Range<int>& EncoderCapabilities::getComplexityRange() { return mComplexityRange; } // static int EncoderCapabilities::ParseBitrateMode(std::string mode) { for (Feature feat: sBitrateModes) { if (base::EqualsIgnoreCase(feat.mName, mode)) { return feat.mValue; } } return 0; } bool EncoderCapabilities::isBitrateModeSupported(int mode) { for (Feature feat : sBitrateModes) { if (mode == feat.mValue) { return (mBitControl & (1 << mode)) != 0; } } return false; } // static std::shared_ptr<EncoderCapabilities> EncoderCapabilities::Create(std::string mediaType, std::vector<ProfileLevel> profLevs, const sp<AMessage> &format) { std::shared_ptr<EncoderCapabilities> caps(new EncoderCapabilities()); caps->init(mediaType, profLevs, format); return caps; } void EncoderCapabilities::init(std::string mediaType, std::vector<ProfileLevel> profLevs, const sp<AMessage> &format) { // no support for complexity or quality yet mMediaType = mediaType; mProfileLevels = profLevs; mComplexityRange = Range(0, 0); mQualityRange = Range(0, 0); mBitControl = (1 << BITRATE_MODE_VBR); applyLevelLimits(); parseFromInfo(format); } void EncoderCapabilities::applyLevelLimits() { if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_FLAC)) { mComplexityRange = Range(0, 8); mBitControl = (1 << BITRATE_MODE_CQ); } else if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_AMR_NB) || base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_AMR_WB) || base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_G711_ALAW) || base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_G711_MLAW) || base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_MSGSM)) { mBitControl = (1 << BITRATE_MODE_CBR); } } void EncoderCapabilities::parseFromInfo(const sp<AMessage> &format) { AString complexityRangeAStr; if (format->findString("complexity-range", &complexityRangeAStr)) { std::optional<Range<int>> complexityRangeOpt = Range<int32_t>::Parse(std::string(complexityRangeAStr.c_str())); mComplexityRange = complexityRangeOpt.value_or(mComplexityRange); // TODO should we limit this to level limits? } AString qualityRangeAStr; if (format->findString("quality-range", &qualityRangeAStr)) { std::optional<Range<int>> qualityRangeOpt = Range<int32_t>::Parse(std::string(qualityRangeAStr.c_str())); mQualityRange = qualityRangeOpt.value_or(mQualityRange); } AString bitrateModesAStr; if (format->findString("feature-bitrate-modes", &bitrateModesAStr)) { mBitControl = 0; for (std::string mode: base::Split(std::string(bitrateModesAStr.c_str()), ",")) { mBitControl |= (1 << ParseBitrateMode(mode)); } } format->findInt32("complexity-default", &mDefaultComplexity); format->findInt32("quality-default", &mDefaultQuality); AString qualityScaleAStr; if (format->findString("quality-scale", &qualityScaleAStr)) { mQualityScale = std::string(qualityScaleAStr.c_str()); } } bool EncoderCapabilities::supports( std::optional<int> complexity, std::optional<int> quality, std::optional<int> profile) { bool ok = true; if (complexity) { ok &= mComplexityRange.contains(complexity.value()); } if (quality) { ok &= mQualityRange.contains(quality.value()); } if (profile) { ok &= std::any_of(mProfileLevels.begin(), mProfileLevels.end(), [&profile](ProfileLevel pl){ return pl.mProfile == profile.value(); }); } return ok; } void EncoderCapabilities::getDefaultFormat(sp<AMessage> &format) { // don't list trivial quality/complexity as default for now if (mQualityRange.upper() != mQualityRange.lower() && mDefaultQuality != 0) { format->setInt32(KEY_QUALITY, mDefaultQuality); } if (mComplexityRange.upper() != mComplexityRange.lower() && mDefaultComplexity != 0) { format->setInt32(KEY_COMPLEXITY, mDefaultComplexity); } // bitrates are listed in order of preference for (Feature feat : sBitrateModes) { if ((mBitControl & (1 << feat.mValue)) != 0) { format->setInt32(KEY_BITRATE_MODE, feat.mValue); break; } } } bool EncoderCapabilities::supportsFormat(const sp<AMessage> &format) { int32_t mode; if (format->findInt32(KEY_BITRATE_MODE, &mode) && !isBitrateModeSupported(mode)) { return false; } int tmp; std::optional<int> complexity = std::nullopt; if (format->findInt32(KEY_COMPLEXITY, &tmp)) { complexity = tmp; } if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_FLAC)) { int flacComplexity; if (format->findInt32(KEY_FLAC_COMPRESSION_LEVEL, &flacComplexity)) { if (!complexity) { complexity = flacComplexity; } else if (flacComplexity != complexity.value()) { ALOGE("Conflicting values for complexity and flac-compression-level," " which are %d and %d", complexity.value(), flacComplexity); return false; } } } // other audio parameters std::optional<int> profile = std::nullopt; if (format->findInt32(KEY_PROFILE, &tmp)) { profile = tmp; } if (base::EqualsIgnoreCase(mMediaType, MIMETYPE_AUDIO_AAC)) { int aacProfile; if (format->findInt32(KEY_AAC_PROFILE, &aacProfile)) { if (!profile) { profile = aacProfile; } else if (aacProfile != profile.value()) { ALOGE("Conflicting values for profile and aac-profile, which are %d and %d", profile.value(), aacProfile); return false; } } } std::optional<int> quality = std::nullopt; if (format->findInt32(KEY_QUALITY, &tmp)) { quality = tmp; } return supports(complexity, quality, profile); } } // namespace android No newline at end of file
media/libmedia/include/media/CodecCapabilities.h +3 −1 Original line number Diff line number Diff line Loading @@ -19,8 +19,9 @@ #define CODEC_CAPABILITIES_H_ #include <media/AudioCapabilities.h> #include <media/VideoCapabilities.h> #include <media/CodecCapabilitiesUtils.h> #include <media/EncoderCapabilities.h> #include <media/VideoCapabilities.h> #include <media/stagefright/foundation/ABase.h> #include <media/stagefright/foundation/AMessage.h> #include <media/stagefright/foundation/AString.h> Loading Loading @@ -54,6 +55,7 @@ private: std::shared_ptr<AudioCapabilities> mAudioCaps; std::shared_ptr<VideoCapabilities> mVideoCaps; std::shared_ptr<EncoderCapabilities> mEncoderCaps; }; } // namespace android Loading
media/libmedia/include/media/CodecCapabilitiesUtils.h +15 −0 Original line number Diff line number Diff line Loading @@ -42,6 +42,21 @@ struct ProfileLevel { } }; struct Feature { std::string mName; int mValue; bool mDefault; bool mInternal; Feature(std::string name, int value, bool def, bool internal) { mName = name; mValue = value; mDefault = def; mInternal = internal; } Feature(std::string name, int value, bool def) : Feature(name, value, def, false /* internal */) {} }; /** * Immutable class for describing the range of two numeric values. * Loading
media/libmedia/include/media/EncoderCapabilities.h 0 → 100644 +105 −0 Original line number Diff line number Diff line /* * Copyright 2024, The Android Open Source Project * * 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. */ #ifndef ENCODER_CAPABILITIES_H_ #define ENCODER_CAPABILITIES_H_ #include <media/CodecCapabilitiesUtils.h> #include <media/stagefright/foundation/AMessage.h> #include <utils/StrongPointer.h> namespace android { /** * A class that supports querying the encoding capabilities of a codec. */ struct EncoderCapabilities { /** * Returns the supported range of quality values. * * Quality is implementation-specific. As a general rule, a higher quality * setting results in a better image quality and a lower compression ratio. */ const Range<int>& getQualityRange(); /** * Returns the supported range of encoder complexity values. * <p> * Some codecs may support multiple complexity levels, where higher * complexity values use more encoder tools (e.g. perform more * intensive calculations) to improve the quality or the compression * ratio. Use a lower value to save power and/or time. */ const Range<int>& getComplexityRange(); /** Constant quality mode */ inline static constexpr int BITRATE_MODE_CQ = 0; /** Variable bitrate mode */ inline static constexpr int BITRATE_MODE_VBR = 1; /** Constant bitrate mode */ inline static constexpr int BITRATE_MODE_CBR = 2; /** Constant bitrate mode with frame drops */ inline static constexpr int BITRATE_MODE_CBR_FD = 3; /** * Query whether a bitrate mode is supported. */ bool isBitrateModeSupported(int mode); /** @hide */ static std::shared_ptr<EncoderCapabilities> Create(std::string mediaType, std::vector<ProfileLevel> profLevs, const sp<AMessage> &format); /** @hide */ void getDefaultFormat(sp<AMessage> &format); /** @hide */ bool supportsFormat(const sp<AMessage> &format); private: inline static const Feature sBitrateModes[] = { Feature("VBR", BITRATE_MODE_VBR, true), Feature("CBR", BITRATE_MODE_CBR, false), Feature("CQ", BITRATE_MODE_CQ, false), Feature("CBR-FD", BITRATE_MODE_CBR_FD, false) }; static int ParseBitrateMode(std::string mode); std::string mMediaType; std::vector<ProfileLevel> mProfileLevels; Range<int> mQualityRange; Range<int> mComplexityRange; int mBitControl; int mDefaultComplexity; int mDefaultQuality; std::string mQualityScale; /* no public constructor */ EncoderCapabilities() {} void init(std::string mediaType, std::vector<ProfileLevel> profLevs, const sp<AMessage> &format); void applyLevelLimits(); void parseFromInfo(const sp<AMessage> &format); bool supports(std::optional<int> complexity, std::optional<int> quality, std::optional<int> profile); }; } // namespace android #endif // ENCODER_CAPABILITIES_H_ No newline at end of file