// Copyright 2025 Google LLC. All Rights Reserved. This file and proprietary // source code may only be used and distributed under the Widevine License // Agreement. #ifndef WVCDM_UTIL_HLS_ATTRIBUTE_LIST_H_ #define WVCDM_UTIL_HLS_ATTRIBUTE_LIST_H_ #include #include #include #include #include #include #include "wv_class_utils.h" namespace wvutil { // An HLS Attribute List is a loosely defined HLS tag value // type representing a dictionary of attribute name-value pairs. // No attribute name may appear twice in a valid HLS Attribute // List. // // When serialized, an HLS attribute list appears as a comma // separated list of = pairs, without any line breaks, // and whitespace only within quoted string attribute values. // // The HLS specification defines them as context sensitive-types, // as the format of certain value types are ambiguous with other // value types (ex. something that looks like a hex sequence could // actually be an enum string). The exact value types of attributes // depends on the HLS tag. // // This implementation is intended to be context free when parsing. // Internally, value will be assigned a type based on their // appearance to the most restrictive type; however, accessing as a // particular type will be allowed so long as they match the format // that type. The only exception to this is quoted strings, which // are fully unambiguous with all other types. // // The standard HLS attribute list allows for UTF-8 encoded unicode // characters; however, for Widevine's use case, we only allow // basic ASCII. // // This class is based on RFC 8216 section 4.2. class HlsAttributeList { public: enum ValueType { kUnsetType = 0, // HLS integers are a sequence of 1 to 20 base10 digits // values, which must fit into an unsigned 64-bit integer. // Note: // All integers can appear as an enum string. // All integers can appear as floats; however, precision // restrictions may limit ability to parse accurately. kIntegerType, // HLS hex sequence are a sequence of 1 or more upper case // hexadecimal digits, with the prefix "0x" or "0X". // Note: // All hex sequences can appear as an enum string. // Certain hex sequences can appear as a resolution. kHexSequenceType, // HLS float and sign float are a sequence of base10 digits // with a possible leading negative sign ('-') and at most one // decimal point ('.'). // Note: // All floats can appear as an enum string. // Certain floats can appear are an integer. kFloatType, // For simplicity, signed and unsigned floats are internally // treated as the same. kSignedFloatType = kFloatType, // Aliasing // HLS quoted strings are a sequence of printable characters // (except a double quote), or space characters, contained within // a pair of double quotes. // Note: Quote string are unambiguous with all other types. kQuotedStringType, // HLS enum strings are a sequence of any printable characters // (except quotes or commas) and do not contain whitespace. // Note: // Certain enum strings can appear as integers, floats, hex // sequences or resolutions. kEnumStringType, // HLS resolutions are a pair of base10 integers (similar to // integer types) separated by an 'x' character. // Note: // All resolutions can appear as enum strings // Certain resolutions can appear as hex sequences. kResolutionType, }; static const char* ValueTypeToString(ValueType type); // Checks if the provided |name| is valid HLS attribute name. static bool IsValidName(const std::string& name); // Checks if the provided |value| is a valid HLS enumerated // string. static bool IsValidEnumStringValue(const std::string& value); // Checks if the provided |value| is an allowed content // of a quote string (i.e., |value| is the portion that is // to be contained within the double quotes). static bool IsValidQuotedStringValue(const std::string& value); // Validator for an HLS unsigned integer representation. // Checks that the value representation |value_rep| conforms // to HLS requirements for a serialized integer. static bool IsValidIntegerRep(const std::string& value_rep); // Parses the provided |integer_rep| as an HLS integer, assign // the parsed integer to |value|. static bool ParseInteger(const std::string& integer_rep, uint64_t* value); HlsAttributeList() = default; WVCDM_DEFAULT_COPY_AND_MOVE(HlsAttributeList); // == Basic Accessors == bool IsEmpty() const { return members_.empty(); } size_t Count() const { return members_.size(); } void Clear() { members_.clear(); } // == Value Getters == // Returns a list of attribute names. std::vector GetNames() const; // Returns true if the provided attribute |name| is contained // within list. bool Contains(const std::string& name) const; // Checks if the provided attribute |name| could be the // specified |type|. // Certain value types are ambiguous; so they may be marked // internally as one type, but are still valid forms of a // different type. bool IsType(const std::string& name, ValueType type) const; // Gets the attribute value for the provided attribute |name|, // assigning the deserialized value to the output parameter(s). // // If a type is internally stored as a different type, but the format // matches the requested type, it will be allowed. // // Returns true if the attribute value was successfully obtained; // false otherwise (attribute does not exist, or the value could // not be parsed as the specified type). bool GetEnumString(const std::string& name, std::string* value) const; bool GetQuotedString(const std::string& name, std::string* value) const; bool GetHexSequence(const std::string& name, std::string* value) const; bool GetHexSequence(const std::string& name, std::vector* value) const; bool GetInteger(const std::string& name, uint64_t* value) const; bool GetFloat(const std::string& name, double* value) const; bool GetResolution(const std::string& name, uint64_t* width, uint64_t* height) const; // == Value Setters == // The value setters attempt to serializes the provided value // into an HLS attribute value format of the type indicated // by the method name. These methods will overwrite any existing // attribute of the same name. // Setting will be rejected if |name| is not a valid HLS attribute // name, or if the provided |value| is valid (only applicable to // certain types). bool SetEnumString(const std::string& name, const std::string& value); bool SetQuotedString(const std::string& name, const std::string& value); // Note: This implementation will use lower "0x" prefix for // hex sequences. bool SetHexSequence(const std::string& name, const std::string& value); bool SetHexSequence(const std::string& name, const std::vector& value); bool SetInteger(const std::string& name, uint64_t value); bool SetFloat(const std::string& name, double value); bool SetResolution(const std::string& name, uint64_t width, uint64_t height); // Removes the specified attribute. Returns true if the attribute // was removed; false otherwise. bool Remove(const std::string& name); // == Parsing / Serialization == // Attempts to parse the provided HLS Attribute List. // Always clears the existing contents, even if the attribute // list cannot be parsed. // // Internally, values are not parsed, but checked to see which // type they resemble. This does not restrict accessing them // as different types. // // Returns true if successfully parsed; false otherwise. bool Parse(const std::string& hls_attr_list_rep); // Serializes the contents of the HLS attribute list into a // valid HLS attribute list form. std::string Serialize() const; bool SerializeToStream(std::ostream* out) const; // Internally, values are stored in their serialized form // in case their value is ambiguous between two or more types. // The assigned ValueType is intended to be a hint for improve // access speeds. using ValueInfo = std::pair; private: // Internal utility to obtain the value info. // Silently returns null if the name does not exist. const ValueInfo* GetInfo(const std::string& name) const; // Values in |members_| will always be validated before assigned. std::map members_; }; // class HlsAttributeList } // namespace wvutil #endif // WVCDM_UTIL_HLS_ATTRIBUTE_LIST_H_