Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4ed0abda authored by Songyue Han's avatar Songyue Han
Browse files

CodecCapabilities: Port VideoCapabilities from Java to Native.

Test: atest VideoCapsHevcTest
Bug: b/306023029
Change-Id: If849e4bb6ded7c70af294c4b8fe8e3f79d378394
parent c07437e7
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -260,6 +260,7 @@ cc_library_shared {
    srcs: [
        "AudioCapabilities.cpp",
        "CodecCapabilities.cpp",
        "VideoCapabilities.cpp",
        "CodecCapabilitiesUtils.cpp",
    ],

+129 −2
Original line number Diff line number Diff line
@@ -16,19 +16,146 @@

//#define LOG_NDEBUG 0
#define LOG_TAG "CodecCapabilitiesUtils"

#include <android-base/properties.h>
#include <utils/Log.h>

#include <algorithm>
#include <cmath>
#include <regex>
#include <cstdlib>
#include <string>
#include <vector>

#include <media/CodecCapabilitiesUtils.h>

#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AUtils.h>

namespace android {

// VideoSize

VideoSize::VideoSize(int32_t width, int32_t height) : mWidth(width), mHeight(height) {}

VideoSize::VideoSize() : mWidth(0), mHeight(0) {}

int32_t VideoSize::getWidth() const { return mWidth; }

int32_t VideoSize::getHeight() const { return mHeight; }

bool VideoSize::equals(VideoSize other) const {
    return mWidth == other.mWidth && mHeight == other.mHeight;
}

bool VideoSize::empty() const {
    return mWidth <= 0 || mHeight <= 0;
}

std::string VideoSize::toString() const {
    return std::to_string(mWidth) + "x" + std::to_string(mHeight);
}

std::optional<VideoSize> VideoSize::ParseSize(std::string str) {
    if (str.empty()) {
        return std::nullopt;
    }

    std::regex regex("([0-9]+)([*x])([0-9]+)");
    std::smatch match;
    if (std::regex_match(str, match, regex)) {
        long int w = strtol(match[1].str().c_str(), NULL, 10);
        long int h = strtol(match[3].str().c_str(), NULL, 10);
        return std::make_optional(VideoSize(w, h));
    } else {
        ALOGW("could not parse size %s", str.c_str());
        return std::nullopt;
    }
}

std::optional<std::pair<VideoSize, VideoSize>> VideoSize::ParseSizeRange(const std::string str) {
    size_t ix = str.find_first_of('-');
    if (ix != std::string::npos) {
        std::optional<VideoSize> lowerOpt = VideoSize::ParseSize(str.substr(0, ix));
        std::optional<VideoSize> upperOpt = VideoSize::ParseSize(str.substr(ix + 1));
        if (!lowerOpt || !upperOpt) {
            return std::nullopt;
        }
        return std::make_optional(
                std::pair<VideoSize, VideoSize>(lowerOpt.value(), upperOpt.value()));
    } else {
        std::optional<VideoSize> opt = VideoSize::ParseSize(str);
        if (!opt) {
            return std::nullopt;
        }
        return std::make_optional(std::pair<VideoSize, VideoSize>(opt.value(), opt.value()));
    }
}

Range<int32_t> VideoSize::GetAllowedDimensionRange() {
#ifdef __LP64__
    return Range<int32_t>(1, 32768);
#else
    int32_t value = base::GetIntProperty("media.resolution.limit.32bit", (int32_t)4096);
    return Range<int32_t>(1, value);
#endif
}

// Rational

std::optional<Rational> Rational::Parse(std::string str) {
    if (str.compare("NaN") == 0) {
        return std::make_optional(NaN);
    } else if (str.compare("Infinity") == 0) {
        return std::make_optional(POSITIVE_INFINITY);
    } else if (str.compare("-Infinity") == 0) {
        return std::make_optional(NEGATIVE_INFINITY);
    }

    std::regex regex("([0-9]+)([:/])([0-9]+)");
    std::smatch match;
    if (std::regex_match(str, match, regex)) {
        long int numerator = strtol(match[1].str().c_str(), NULL, 10);
        long int denominator = strtol(match[3].str().c_str(), NULL, 10);
        return std::make_optional(Rational(numerator, denominator));
    } else {
        ALOGW("could not parse string: %s to Rational", str.c_str());
        return std::nullopt;
    }
}

Rational Rational::scale(int32_t num, int32_t den) {
    int32_t common = std::gcd(num, den);
    num /= common;
    den /= common;
    return Rational(
            (int32_t)(mNumerator * (double)num),     // saturate to int
            (int32_t)(mDenominator * (double)den));  // saturate to int
}

Range<Rational> Rational::ScaleRange(Range<Rational> range, int32_t num, int32_t den) {
    if (num == den) {
        return range;
    }
    return Range(
            range.lower().scale(num, den),
            range.upper().scale(num, den));
}

std::optional<Range<Rational>> Rational::ParseRange(const std::string str) {
    size_t ix = str.find_first_of('-');
    if (ix != std::string::npos) {
        std::optional<Rational> lower = Parse(str.substr(0, ix));
        std::optional<Rational> upper = Parse(str.substr(ix + 1));
        if (!lower || !upper) {
            return std::nullopt;
        }
        return std::make_optional<Range<Rational>>(lower.value(), upper.value());
    } else {
        std::optional<Rational> value = Parse(str);
        if (!value) {
            return std::nullopt;
        }
        return std::make_optional<Range<Rational>>(value.value(), value.value());
    }
}

}  // namespace android
 No newline at end of file
+1702 −0

File added.

Preview size limit exceeded, changes collapsed.

+2 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#define CODEC_CAPABILITIES_H_

#include <media/AudioCapabilities.h>
#include <media/VideoCapabilities.h>
#include <media/CodecCapabilitiesUtils.h>
#include <media/stagefright/foundation/ABase.h>
#include <media/stagefright/foundation/AMessage.h>
@@ -52,6 +53,7 @@ private:
    std::vector<ProfileLevel> mProfileLevels;

    std::shared_ptr<AudioCapabilities> mAudioCaps;
    std::shared_ptr<VideoCapabilities> mVideoCaps;
};

}  // namespace android
+497 −1
Original line number Diff line number Diff line
@@ -117,6 +117,39 @@ struct Range {
        return Range(std::max(lower_, lower), std::min(upper_, upper));
    }

    /**
     * Returns the smallest range that includes this range and
     * another range.
     *
     * E.g. if a < b < c < d, the
     * extension of [a, c] and [b, d] ranges is [a, d].
     * As the endpoints are object references, there is no guarantee
     * which specific endpoint reference is used from the input ranges:
     *
     * E.g. if a == a' < b < c, the
     * extension of [a, b] and [a', c] ranges could be either
     * [a, c] or ['a, c], where ['a, c] could be either the exact
     * input range, or a newly created range with the same endpoints.
     *
     * @param range a non-null Range<T> reference
     * @return the extension of this range and the other range.
     */
    Range<T> extend(Range<T> range) {
        return Range<T>(std::min(lower_, range.lower_), std::max(upper_, range.upper_));
    }

    Range<T> align(T align) {
        return this->intersect(
                divUp(lower_, align) * align, (upper_ / align) * align);
    }

    Range<T> factor(T factor) {
        if (factor == 1) {
            return *this;
        }
        return Range(divUp(this->lower(), factor), this->upper() / factor);
    }

    // parse a string into a range
    static std::optional<Range<T>> Parse(const std::string &str) {
        if (str.empty()) {
@@ -148,6 +181,10 @@ struct Range {
        return std::make_optional<Range<T>>((T)lower, (T)upper);
    }

    static Range<T> RangeFor(double v) {
        return Range((T)v, (T)ceil(v));
    }

private:
    T lower_;
    T upper_;
@@ -160,7 +197,7 @@ constexpr int ERROR_CAPABILITIES_UNRECOGNIZED = (1 << 0);
// found profile/level for which we don't have capability estimates
constexpr int ERROR_CAPABILITIES_UNSUPPORTED    = (1 << 1);
// have not found any profile/level for which we don't have capability estimate
// constexpr int ERROR_NONE_SUPPORTED = (1 << 2);
constexpr int ERROR_CAPABILITIES_NONE_SUPPORTED = (1 << 2);

/**
 * Sorts distinct (non-intersecting) range array in ascending order.
@@ -211,6 +248,465 @@ std::vector<Range<T>> intersectSortedDistinctRanges(
    return result;
}

/**
 * Immutable class for describing width and height dimensions in pixels.
 */
struct VideoSize {
    /**
     * Create a new immutable VideoSize instance.
     *
     * @param width The width of the size, in pixels
     * @param height The height of the size, in pixels
     */
    VideoSize(int32_t width, int32_t height);

    // default constructor
    VideoSize();

    /**
     * Get the width of the size (in pixels).
     * @return width
     */
    int32_t getWidth() const;

    /**
     * Get the height of the size (in pixels).
     * @return height
     */
    int32_t getHeight() const;

    /**
     * Check if this size is equal to another size.
     *
     * Two sizes are equal if and only if both their widths and heights are
     * equal.
     *
     * A size object is never equal to any other type of object.
     *
     * @return true if the objects were equal, false otherwise
     */
    bool equals(VideoSize other) const;

    bool empty() const;

    std::string toString() const;

    /**
     * Parses the specified string as a size value.
     *
     * The ASCII characters {@code \}{@code u002a} ('*') and
     * {@code \}{@code u0078} ('x') are recognized as separators between
     * the width and height.
     *
     * For any {@code VideoSize s}: {@code VideoSize::ParseSize(s.toString()).equals(s)}.
     * However, the method also handles sizes expressed in the
     * following forms:
     *
     * "<i>width</i>{@code x}<i>height</i>" or
     * "<i>width</i>{@code *}<i>height</i>" {@code => new VideoSize(width, height)},
     * where <i>width</i> and <i>height</i> are string integers potentially
     * containing a sign, such as "-10", "+7" or "5".
     *
     * <pre>{@code
     * VideoSize::ParseSize("3*+6").equals(new VideoSize(3, 6)) == true
     * VideoSize::ParseSize("-3x-6").equals(new VideoSize(-3, -6)) == true
     * VideoSize::ParseSize("4 by 3") => throws NumberFormatException
     * }</pre>
     *
     * @param string the string representation of a size value.
     * @return the size value represented by {@code string}.
     */
    static std::optional<VideoSize> ParseSize(std::string str);

    static std::optional<std::pair<VideoSize, VideoSize>> ParseSizeRange(const std::string str);

    static Range<int32_t> GetAllowedDimensionRange();

private:
    int32_t mWidth;
    int32_t mHeight;
};

// This is used for the std::map<VideoSize> in VideoCapabilities
struct VideoSizeCompare {
    bool operator() (const VideoSize& lhs, const VideoSize& rhs) const {
        if (lhs.getWidth() == rhs.getWidth()) {
            return lhs.getHeight() < rhs.getHeight();
        } else {
            return lhs.getWidth() < rhs.getWidth();
        }
    }
};

/**
 * An immutable data type representation a rational number.
 *
 * Contains a pair of ints representing the numerator and denominator of a
 * Rational number.
 */
struct Rational {
    /**
     * <p>Create a {@code Rational} with a given numerator and denominator.</p>
     *
     * <p>The signs of the numerator and the denominator may be flipped such that the denominator
     * is always positive. Both the numerator and denominator will be converted to their reduced
     * forms (see {@link #equals} for more details).</p>
     *
     * <p>For example,
     * <ul>
     * <li>a rational of {@code 2/4} will be reduced to {@code 1/2}.
     * <li>a rational of {@code 1/-1} will be flipped to {@code -1/1}
     * <li>a rational of {@code 5/0} will be reduced to {@code 1/0}
     * <li>a rational of {@code 0/5} will be reduced to {@code 0/1}
     * </ul>
     * </p>
     *
     * @param numerator the numerator of the rational
     * @param denominator the denominator of the rational
     *
     * @see #equals
     */
    Rational(int32_t numerator, int32_t denominator) {
        if (denominator < 0) {
            numerator = -numerator;
            denominator = -denominator;
        }

        // Convert to reduced form
        if (denominator == 0 && numerator > 0) {
            mNumerator = 1; // +Inf
            mDenominator = 0;
        } else if (denominator == 0 && numerator < 0) {
            mNumerator = -1; // -Inf
            mDenominator = 0;
        } else if (denominator == 0 && numerator == 0) {
            mNumerator = 0; // NaN
            mDenominator = 0;
        } else if (numerator == 0) {
            mNumerator = 0;
            mDenominator = 1;
        } else {
            int gcd = std::gcd(numerator, denominator);

            mNumerator = numerator / gcd;
            mDenominator = denominator / gcd;
        }
    }

    // default constructor;
    Rational() {
        Rational(0, 0);
    }

    /**
     * Gets the numerator of the rational.
     *
     * <p>The numerator will always return {@code 1} if this rational represents
     * infinity (that is, the denominator is {@code 0}).</p>
     */
    int32_t getNumerator() const {
        return mNumerator;
    }

    /**
     * Gets the denominator of the rational
     *
     * <p>The denominator may return {@code 0}, in which case the rational may represent
     * positive infinity (if the numerator was positive), negative infinity (if the numerator
     * was negative), or {@code NaN} (if the numerator was {@code 0}).</p>
     *
     * <p>The denominator will always return {@code 1} if the numerator is {@code 0}.
     */
    int32_t getDenominator() const {
        return mDenominator;
    }

    /**
     * Indicates whether this rational is a <em>Not-a-Number (NaN)</em> value.
     *
     * <p>A {@code NaN} value occurs when both the numerator and the denominator are {@code 0}.</p>
     *
     * @return {@code true} if this rational is a <em>Not-a-Number (NaN)</em> value;
     *         {@code false} if this is a (potentially infinite) number value
     */
    bool isNaN() const {
        return mDenominator == 0 && mNumerator == 0;
    }

    /**
     * Indicates whether this rational represents an infinite value.
     *
     * <p>An infinite value occurs when the denominator is {@code 0} (but the numerator is not).</p>
     *
     * @return {@code true} if this rational is a (positive or negative) infinite value;
     *         {@code false} if this is a finite number value (or {@code NaN})
     */
    bool isInfinite() const {
        return mNumerator != 0 && mDenominator == 0;
    }

    /**
     * Indicates whether this rational represents a finite value.
     *
     * <p>A finite value occurs when the denominator is not {@code 0}; in other words
     * the rational is neither infinity or {@code NaN}.</p>
     *
     * @return {@code true} if this rational is a (positive or negative) infinite value;
     *         {@code false} if this is a finite number value (or {@code NaN})
     */
    bool isFinite() const {
        return mDenominator != 0;
    }

    /**
     * Indicates whether this rational represents a zero value.
     *
     * <p>A zero value is a {@link #isFinite finite} rational with a numerator of {@code 0}.</p>
     *
     * @return {@code true} if this rational is finite zero value;
     *         {@code false} otherwise
     */
    bool isZero() const {
        return isFinite() && mNumerator == 0;
    }

    /**
     * Return a string representation of this rational, e.g. {@code "1/2"}.
     *
     * <p>The following rules of conversion apply:
     * <ul>
     * <li>{@code NaN} values will return {@code "NaN"}
     * <li>Positive infinity values will return {@code "Infinity"}
     * <li>Negative infinity values will return {@code "-Infinity"}
     * <li>All other values will return {@code "numerator/denominator"} where {@code numerator}
     * and {@code denominator} are substituted with the appropriate numerator and denominator
     * values.
     * </ul></p>
     */
    std::string toString() const {
        if (isNaN()) {
            return "NaN";
        } else if (isPosInf()) {
            return "Infinity";
        } else if (isNegInf()) {
            return "-Infinity";
        } else {
            return std::to_string(mNumerator) + "/" + std::to_string(mDenominator);
        }
    }

    /**
     * Returns the value of the specified number as a {@code double}.
     *
     * <p>The {@code double} is calculated by converting both the numerator and denominator
     * to a {@code double}; then returning the result of dividing the numerator by the
     * denominator.</p>
     *
     * @return the divided value of the numerator and denominator as a {@code double}.
     */
    double asDouble() const {
        double num = mNumerator;
        double den = mDenominator;

        return num / den;
    }

    /**
     * Returns the value of the specified number as a {@code float}.
     *
     * <p>The {@code float} is calculated by converting both the numerator and denominator
     * to a {@code float}; then returning the result of dividing the numerator by the
     * denominator.</p>
     *
     * @return the divided value of the numerator and denominator as a {@code float}.
     */
    float asfloat() const {
        float num = mNumerator;
        float den = mDenominator;

        return num / den;
    }

    /**
     * Returns the value of the specified number as a {@code int}.
     *
     * <p>{@link #isInfinite Finite} rationals are converted to an {@code int} value
     * by dividing the numerator by the denominator; conversion for non-finite values happens
     * identically to casting a floating point value to an {@code int}, in particular:
     *
     * @return the divided value of the numerator and denominator as a {@code int}.
     */
    int32_t asInt32() const {
        // Mimic float to int conversion rules from JLS 5.1.3

        if (isPosInf()) {
            return INT32_MAX;
        } else if (isNegInf()) {
            return INT32_MIN;
        } else if (isNaN()) {
            return 0;
        } else { // finite
            return mNumerator / mDenominator;
        }
    }

    /**
     * Returns the value of the specified number as a {@code long}.
     *
     * <p>{@link #isInfinite Finite} rationals are converted to an {@code long} value
     * by dividing the numerator by the denominator; conversion for non-finite values happens
     * identically to casting a floating point value to a {@code long}, in particular:
     *
     * @return the divided value of the numerator and denominator as a {@code long}.
     */
    int64_t asInt64() const {
        // Mimic float to long conversion rules from JLS 5.1.3

        if (isPosInf()) {
            return INT64_MAX;
        } else if (isNegInf()) {
            return INT64_MIN;
        } else if (isNaN()) {
            return 0;
        } else { // finite
            return mNumerator / mDenominator;
        }
    }

    /**
     * Returns the value of the specified number as a {@code short}.
     *
     * <p>{@link #isInfinite Finite} rationals are converted to a {@code short} value
     * identically to {@link #intValue}; the {@code int} result is then truncated to a
     * {@code short} before returning the value.</p>
     *
     * @return the divided value of the numerator and denominator as a {@code short}.
     */
    int16_t asInt16() const {
        return (int16_t) asInt32();
    }

    /**
     * Compare this rational to the specified rational to determine their natural order.
     *
     * Nan is considered to be equal to itself and greater than all other
     * Rational values. Otherwise, if the objects are not equal, then
     * the following rules apply:
     *
     * Positive infinity is greater than any other finite number (or negative infinity)
     * Negative infinity is less than any other finite number (or positive infinity)
     * The finite number represented by this rational is checked numerically
     * against the other finite number by converting both rationals to a common denominator multiple
     * and comparing their numerators.
     *
     * @param another the rational to be compared
     *
     * @return a negative integer, zero, or a positive integer as this object is less than,
     *         equal to, or greater than the specified rational.
     */
    // bool operator> (const Rational& another) {
    int compareTo(Rational another) const {
        if (equals(another)) {
            return 0;
        } else if (isNaN()) { // NaN is greater than the other non-NaN value
            return 1;
        } else if (another.isNaN()) { // the other NaN is greater than this non-NaN value
            return -1;
        } else if (isPosInf() || another.isNegInf()) {
            return 1; // positive infinity is greater than any non-NaN/non-posInf value
        } else if (isNegInf() || another.isPosInf()) {
            return -1; // negative infinity is less than any non-NaN/non-negInf value
        }

        // else both this and another are finite numbers

        // make the denominators the same, then compare numerators. int64_t to avoid overflow
        int64_t thisNumerator = ((int64_t)mNumerator) * another.mDenominator;
        int64_t otherNumerator = ((int64_t)another.mNumerator) * mDenominator;

        // avoid underflow from subtraction by doing comparisons
        if (thisNumerator < otherNumerator) {
            return -1;
        } else if (thisNumerator > otherNumerator) {
            return 1;
        } else {
            // This should be covered by #equals, but have this code path just in case
            return 0;
        }
    }

    bool operator > (const Rational& another) const {
        return compareTo(another) > 0;
    }

    bool operator >= (const Rational& another) const {
        return compareTo(another) >= 0;
    }

    bool operator < (const Rational& another) const {
        return compareTo(another) < 0;
    }

    bool operator <= (const Rational& another) const {
        return compareTo(another) <= 0;
    }

    bool operator == (const Rational& another) const {
        return equals(another);
    }

    static std::optional<Range<Rational>> ParseRange(const std::string str);

    static Range<Rational> ScaleRange(Range<Rational> range, int32_t num, int32_t den);

private:
    int32_t mNumerator;
    int32_t mDenominator;

    bool isPosInf() const {
        return mDenominator == 0 && mNumerator > 0;
    }

    bool isNegInf() const {
        return mDenominator == 0 && mNumerator < 0;
    }

    bool equals(Rational other) const {
        return (mNumerator == other.mNumerator && mDenominator == other.mDenominator);
    }

    Rational scale(int32_t num, int32_t den);

    /**
     * Parses the specified string as a rational value.
     * The ASCII characters {@code \}{@code u003a} (':') and
     * {@code \}{@code u002f} ('/') are recognized as separators between
     * the numerator and denominator.
     *
     * For any {@code Rational r}: {@code Rational::parseRational(r.toString()).equals(r)}.
     * However, the method also handles rational numbers expressed in the
     * following forms:
     *
     * "<i>num</i>{@code /}<i>den</i>" or
     * "<i>num</i>{@code :}<i>den</i>" {@code => new Rational(num, den);},
     * where <i>num</i> and <i>den</i> are string integers potentially
     * containing a sign, such as "-10", "+7" or "5".
     *
     * Rational::Parse("3:+6").equals(new Rational(1, 2)) == true
     * Rational::Parse("-3/-6").equals(new Rational(1, 2)) == true
     * Rational::Parse("4.56") => return std::nullopt
     *
     * @param str the string representation of a rational value.
     * @return the rational value wrapped by std::optional represented by str.
     */
    static std::optional<Rational> Parse(std::string str);
};

static const Rational NaN = Rational(0, 0);
static const Rational POSITIVE_INFINITY = Rational(1, 0);
static const Rational NEGATIVE_INFINITY = Rational(-1, 0);
static const Rational ZERO = Rational(0, 1);

}  // namespace android

#endif  // CODEC_CAPABILITIES__UTILS_H_
 No newline at end of file
Loading