1304 lines
47 KiB
C++
1304 lines
47 KiB
C++
/*
|
|
* Copyright 2020 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 ANDROID_AUDIO_METADATA_H
|
|
#define ANDROID_AUDIO_METADATA_H
|
|
|
|
#include <stdint.h>
|
|
#include <sys/cdefs.h>
|
|
#include <unistd.h>
|
|
|
|
#ifdef __cplusplus
|
|
|
|
#include <any>
|
|
#include <map>
|
|
#include <string>
|
|
#include <tuple>
|
|
#include <vector>
|
|
|
|
/**
|
|
* Audio Metadata: a C++ Object based map.
|
|
*
|
|
* Data is a map of strings to Datum Objects.
|
|
*
|
|
* Datum is a C++ "Object", a direct instance of std::any, but limited
|
|
* to only the following allowed types:
|
|
*
|
|
* Native Java equivalent
|
|
* int32 (int)
|
|
* int64 (long)
|
|
* float (float)
|
|
* double (double)
|
|
* std::string (String)
|
|
* Data (std::map<std::string, Datum>) (Map<String, Object>)
|
|
*
|
|
* Metadata code supports advanced automatic parceling.
|
|
* TEST ONLY:
|
|
* std::vector<Datum> (Object[]) --> vector of Objects
|
|
* std::pair<Datum, Datum> (Pair<Object, Object>) --> pair of Objects
|
|
* std::vector<std::vector<std::pair<std::string, short>>> --> recursive containers
|
|
* struct { int i0; std::vector<int> v1; std::pair<int, int> p2; } --> struct parceling
|
|
*
|
|
* The Data map accepts typed Keys, which designate the type T of the
|
|
* value associated with the Key<T> in the template parameter.
|
|
*
|
|
* CKey<T> is the constexpr version suitable for fixed compile-time constants.
|
|
* Key<T> is the non-constexpr version.
|
|
*
|
|
* Notes: for future extensibility:
|
|
*
|
|
* In order to add a new type in.
|
|
*
|
|
* 1) Add the new type to the END of the metadata_types lists below.
|
|
*
|
|
* 2) The new type can be a primitive, or make use of containers std::map, std::vector,
|
|
* std::pair, or be simple structs (see below).
|
|
*
|
|
* 3) Simple structs contain no pointers and all public data. The members can be based
|
|
* on existing types.
|
|
* a) If trivially copyable (packed) primitive data,
|
|
* add to primitive_metadata_types.
|
|
* b) If the struct requires member-wise parceling, add to structural_metadata_types
|
|
* (current limit is 4 members).
|
|
*
|
|
* 4) The type system is recursive.
|
|
*
|
|
* Design notes:
|
|
* 1) Tuple is intentionally not implemented as it isn't that readable. This can
|
|
* be revisited if the need comes up. If you have more than a couple of elements,
|
|
* we suggest embedding in a Data typed map or a Simple struct.
|
|
*
|
|
* 2) Each custom type e.g. vector<int>, pair<short, char> takes one
|
|
* slot in the type index. A full type description language is not implemented
|
|
* here for brevity and clarity.
|
|
*/
|
|
|
|
namespace android::audio_utils::metadata {
|
|
|
|
// Determine if a type is a specialization of a templated type
|
|
// Example: is_specialization_v<T, std::vector>
|
|
// https://stackoverflow.com/questions/16337610/how-to-know-if-a-type-is-a-specialization-of-stdvector
|
|
|
|
template <typename Test, template <typename...> class Ref>
|
|
struct is_specialization : std::false_type {};
|
|
|
|
template <template <typename...> class Ref, typename... Args>
|
|
struct is_specialization<Ref<Args...>, Ref>: std::true_type {};
|
|
|
|
template <typename Test, template <typename...> class Ref>
|
|
inline constexpr bool is_specialization_v = is_specialization<Test, Ref>::value;
|
|
|
|
// For static assert(false) we need a template version to avoid early failure.
|
|
// See: https://stackoverflow.com/questions/51523965/template-dependent-false
|
|
template <typename T>
|
|
inline constexpr bool dependent_false_v = false;
|
|
|
|
// Determine the number of arguments required for structured binding.
|
|
// See the discussion here and follow the links:
|
|
// https://isocpp.org/blog/2016/08/cpp17-structured-bindings-convert-struct-to-a-tuple-simple-reflection
|
|
struct any_type {
|
|
template<class T>
|
|
constexpr operator T(); // non explicit
|
|
};
|
|
|
|
template <typename T, typename... TArgs>
|
|
decltype(void(T{std::declval<TArgs>()...}), std::true_type{}) test_is_braces_constructible(int);
|
|
|
|
template <typename, typename...>
|
|
std::false_type test_is_braces_constructible(...);
|
|
|
|
template <typename T, typename... TArgs>
|
|
using is_braces_constructible = decltype(test_is_braces_constructible<T, TArgs...>(0));
|
|
|
|
// Set up type comparison system
|
|
// see std::variant for the how the type_index() may be used.
|
|
|
|
/*
|
|
* returns the index of type T in the type parameter list Ts.
|
|
*/
|
|
template <typename T, typename... Ts>
|
|
inline constexpr ssize_t type_index() {
|
|
constexpr bool checks[] = {std::is_same_v<std::decay_t<T>, std::decay_t<Ts>>...};
|
|
for (size_t i = 0; i < sizeof...(Ts); ++i) {
|
|
if (checks[i]) return i; // the index in Ts.
|
|
}
|
|
return -1; // none found.
|
|
}
|
|
|
|
// compound_type is a holder of types. There are concatenation tricks of type lists
|
|
// but we don't need them here.
|
|
template <typename... Ts>
|
|
struct compound_type {
|
|
inline static constexpr size_t size_v = sizeof...(Ts);
|
|
template <typename T>
|
|
inline static constexpr bool contains_v = type_index<T, Ts...>() >= 0;
|
|
template <typename T>
|
|
inline static constexpr ssize_t index_of() { return type_index<T, Ts...>(); }
|
|
|
|
// create a tupe equivalent of the compound type. This is useful for
|
|
// finding the nth type by std::tuple_element
|
|
using tuple_t = std::tuple<Ts...>;
|
|
|
|
/**
|
|
* Applies function f to a datum pointer a.
|
|
*
|
|
* \param f is the function to apply. It should take one argument,
|
|
* which is a (typed) pointer to the value stored in a.
|
|
* \param a is the Datum object (derived from std::any).
|
|
* \param result if non-null stores the return value of f (if f has a return value).
|
|
* result may be nullptr if one does not care about the return value of f.
|
|
* \return true on success, false if there is no applicable data stored in a.
|
|
*/
|
|
template <typename F, typename A>
|
|
static bool apply(F f, A *a, std::any *result = nullptr) {
|
|
return apply_impl<F, A, Ts...>(f, a, result);
|
|
}
|
|
|
|
// helper
|
|
// Linear search in the number of types because of non-cached std:any_cast
|
|
// lookup. See std::visit for std::variant for constant time implementation.
|
|
template <typename F, typename A, typename T, typename... Ts2>
|
|
static bool apply_impl(F f, A *a, std::any *result) {
|
|
auto t = std::any_cast<T>(a); // may be const ptr or not.
|
|
if (t == nullptr) {
|
|
return apply_impl<F, A, Ts2...>(f, a, result);
|
|
}
|
|
|
|
// only save result if the function has a non-void return type.
|
|
// and result is not nullptr.
|
|
using result_type = std::invoke_result_t<F, T*>;
|
|
if constexpr (!std::is_same_v<result_type, void>) {
|
|
if (result != nullptr) {
|
|
*result = (result_type)f(t);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
f(t); // discard the result
|
|
return true;
|
|
}
|
|
|
|
// helper base class
|
|
template <typename F, typename A>
|
|
static bool apply_impl(F f __attribute__((unused)), A *a __attribute__((unused)),
|
|
std::any *result __attribute__((unused))) {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
#ifdef METADATA_TESTING
|
|
|
|
// This is a helper struct to verify that we are moving Datums instead
|
|
// of copying them.
|
|
struct MoveCount {
|
|
int32_t mMoveCount = 0;
|
|
int32_t mCopyCount = 0;
|
|
|
|
MoveCount() = default;
|
|
MoveCount(MoveCount&& other) {
|
|
mMoveCount = other.mMoveCount + 1;
|
|
mCopyCount = other.mCopyCount;
|
|
}
|
|
MoveCount(const MoveCount& other) {
|
|
mMoveCount = other.mMoveCount;
|
|
mCopyCount = other.mCopyCount + 1;
|
|
}
|
|
MoveCount &operator=(MoveCount&& other) {
|
|
mMoveCount = other.mMoveCount + 1;
|
|
mCopyCount = other.mCopyCount;
|
|
return *this;
|
|
}
|
|
MoveCount &operator=(const MoveCount& other) {
|
|
mMoveCount = other.mMoveCount;
|
|
mCopyCount = other.mCopyCount + 1;
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
// We can automatically parcel this "Arbitrary" struct
|
|
// since it has no pointers and all public members.
|
|
struct Arbitrary {
|
|
int i0;
|
|
std::vector<int> v1;
|
|
std::pair<int, int> p2;
|
|
};
|
|
|
|
#endif
|
|
|
|
class Data;
|
|
class Datum;
|
|
|
|
// The order of this list must be maintained for binary compatibility
|
|
using metadata_types = compound_type<
|
|
int32_t,
|
|
int64_t,
|
|
float,
|
|
double,
|
|
std::string,
|
|
Data /* std::map<std::string, Datum> */
|
|
// OK to add at end.
|
|
#ifdef METADATA_TESTING
|
|
, std::vector<Datum> // another complex object for testing
|
|
, std::pair<Datum, Datum> // another complex object for testing
|
|
, std::vector<std::vector<std::pair<std::string, short>>> // complex object
|
|
, MoveCount
|
|
, Arbitrary
|
|
#endif
|
|
>;
|
|
|
|
// A subset of the metadata types may be directly copied as bytes
|
|
using primitive_metadata_types = compound_type<int32_t, int64_t, float, double
|
|
#ifdef METADATA_TESTING
|
|
, MoveCount
|
|
#endif
|
|
>;
|
|
|
|
// A subset of metadata types which are a struct-based.
|
|
using structural_metadata_types = compound_type<
|
|
#ifdef METADATA_TESTING
|
|
Arbitrary
|
|
#endif
|
|
>;
|
|
|
|
template <typename T>
|
|
inline constexpr bool is_primitive_metadata_type_v =
|
|
primitive_metadata_types::contains_v<T>;
|
|
|
|
template <typename T>
|
|
inline constexpr bool is_structural_metadata_type_v =
|
|
structural_metadata_types::contains_v<T>;
|
|
|
|
template <typename T>
|
|
inline constexpr bool is_metadata_type_v =
|
|
metadata_types::contains_v<T>;
|
|
|
|
/**
|
|
* Datum is the C++ version of Object, based on std::any
|
|
* to be portable to other Data Object systems on std::any. For C++, there
|
|
* are two forms of generalized Objects, std::variant and std::any.
|
|
*
|
|
* What is a variant?
|
|
* std::variant is like a std::pair<type_index, union>, where the types
|
|
* are kept in the template parameter list, and you only need to store
|
|
* the type_index of the current value's type in the template parameter
|
|
* list to find the value's type (to access data in the union).
|
|
*
|
|
* What is an any?
|
|
* std::any is a std::pair<type_func, pointer> (though the standard encourages
|
|
* small buffer optimization of the pointer for small data types,
|
|
* so the pointer might actually be data). The type_func is cleverly
|
|
* implemented templated, so that one type_func exists per type.
|
|
*
|
|
* For datum, we use std::any, which is different than mediametrics::Item
|
|
* (which uses std::variant).
|
|
*
|
|
* std::any is the C++ version of Java's Object. One benefit of std::any
|
|
* over std::variant is that it is portable outside of this package as a
|
|
* std::any, to another C++ Object system based on std::any
|
|
* (as we any_cast to discover the type). std::variant does not have this
|
|
* portability (without copy conversion) because it requires an explicit
|
|
* type list to be known in the template, so you can't exchange them freely
|
|
* as the union size and the type/type ordering will be different in general
|
|
* between two variant-based Object systems.
|
|
*
|
|
* std::any may work better with some recursive types than variants,
|
|
* as it uses pointers so that physical size need not be known for type
|
|
* definition.
|
|
*
|
|
* This is a design choice: mediametrics::Item as a closed system,
|
|
* metadata::Datum as an open system.
|
|
*
|
|
* CAUTION:
|
|
* For efficiency, prefer the use of std::any_cast<T>(std::any *)
|
|
* which returns a pointer to T (no extra copies.)
|
|
*
|
|
* std::any_cast<T>(std::any) returns an instance of T (copy constructor).
|
|
* std::get<N>(std::variant) returns a reference (no extra copies).
|
|
*
|
|
* The Data map operations are optimized to return references to
|
|
* avoid unnecessary copies.
|
|
*/
|
|
|
|
class Datum : public std::any {
|
|
public:
|
|
// Don't add any virtual functions or non-static member variables.
|
|
|
|
Datum() = default;
|
|
|
|
// Do not make these explicit
|
|
// Force type of std::any to exactly the values we permit to be parceled.
|
|
template <typename T, typename = std::enable_if_t<is_metadata_type_v<T>>>
|
|
Datum(T && t) : std::any(std::forward<T>(t)) {};
|
|
|
|
template <typename T, typename = std::enable_if_t<is_metadata_type_v<T>>>
|
|
Datum& operator=(T&& t) {
|
|
static_cast<std::any *>(this)->operator=(std::forward<T>(t));
|
|
return *this;
|
|
}
|
|
|
|
Datum(const char *t) : std::any(std::string(t)) {}; // special string handling
|
|
};
|
|
|
|
// PREVENT INCORRECT MODIFICATIONS
|
|
// Datum is a helping wrapper on std::any
|
|
// Don't add any non-static members
|
|
static_assert(sizeof(Datum) == sizeof(std::any));
|
|
// Nothing is virtual
|
|
static_assert(!std::is_polymorphic_v<Datum>);
|
|
|
|
/**
|
|
* Keys
|
|
*
|
|
* Audio Metadata keys are typed. Similar to variant's template typenames,
|
|
* which directly indicate possible types in the union, the Audio Metadata
|
|
* Keys contain the Value's Type in the Key's template type parameter.
|
|
*
|
|
* Example:
|
|
*
|
|
* inline constexpr CKey<int64_t> MY_BIGINT("bigint_is_mine");
|
|
* inline constexpr CKey<Data> TABLE("table");
|
|
*
|
|
* Thus if we have a Data object d:
|
|
*
|
|
* decltype(d[TABLE]) is Data
|
|
* decltype(d[MY_BIGINT) is int64_t
|
|
*/
|
|
|
|
/**
|
|
* Key is a non-constexpr key which has local storage in a string.
|
|
*/
|
|
template <typename T, typename = std::enable_if_t<is_metadata_type_v<T>>>
|
|
class Key : private std::string {
|
|
public:
|
|
using std::string::string; // base constructor
|
|
const char *getName() const { return c_str(); }
|
|
};
|
|
|
|
/**
|
|
* CKey is a constexpr key, which is preferred.
|
|
*
|
|
* inline constexpr CKey<int64_t> MY_BIGINT("bigint_is_mine");
|
|
*/
|
|
template <typename T, typename = std::enable_if_t<is_metadata_type_v<T>>>
|
|
class CKey {
|
|
const char * const mName;
|
|
public:
|
|
explicit constexpr CKey(const char *name) : mName(name) {}
|
|
CKey(const Key<T> &key) : mName(key.getName()) {}
|
|
const char *getName() const { return mName; }
|
|
};
|
|
|
|
/**
|
|
* Data is the storage for our Datums.
|
|
*
|
|
* It is implemented on top of std::map<std::string, Datum>
|
|
* but we augment it with typed Key
|
|
* getters and setters, as well as operator[] overloads.
|
|
*/
|
|
class Data : public std::map<std::string, Datum> {
|
|
public:
|
|
// Don't add any virtual functions or non-static member variables.
|
|
|
|
// We supplement the raw form of map with
|
|
// the following typed form using Key.
|
|
|
|
// Intentionally there is no get(), we suggest *get_ptr()
|
|
template <template <typename /* T */, typename... /* enable-ifs */> class K, typename T>
|
|
T* get_ptr(const K<T>& key, bool allocate = false) {
|
|
auto it = find(key.getName());
|
|
if (it == this->end()) {
|
|
if (!allocate) return nullptr;
|
|
it = emplace(key.getName(), T{}).first;
|
|
}
|
|
return std::any_cast<T>(&it->second);
|
|
}
|
|
|
|
template <template <typename, typename...> class K, typename T>
|
|
const T* get_ptr(const K<T>& key) const {
|
|
auto it = find(key.getName());
|
|
if (it == this->end()) return nullptr;
|
|
return std::any_cast<T>(&it->second);
|
|
}
|
|
|
|
template <template <typename, typename...> class K, typename T>
|
|
void put(const K<T>& key, T && t) {
|
|
(*this)[key.getName()] = std::forward<T>(t);
|
|
}
|
|
|
|
template <template <typename, typename...> class K>
|
|
void put(const K<std::string>& key, const char *value) {
|
|
(*this)[key.getName()] = value;
|
|
}
|
|
|
|
// We overload our operator[] so we unhide the one in the base class.
|
|
using std::map<std::string, Datum>::operator[];
|
|
|
|
template <template <typename, typename...> class K, typename T>
|
|
T& operator[](const K<T> &key) {
|
|
return *get_ptr(key, /* allocate */ true);
|
|
}
|
|
|
|
template <template <typename, typename...> class K, typename T>
|
|
const T& operator[](const K<T> &key) const {
|
|
return *get_ptr(key);
|
|
}
|
|
};
|
|
|
|
// PREVENT INCORRECT MODIFICATIONS
|
|
// Data is a helping wrapper on std::map
|
|
// Don't add any non-static members
|
|
static_assert(sizeof(Data) == sizeof(std::map<std::string, Datum>));
|
|
// Nothing is virtual
|
|
static_assert(!std::is_polymorphic_v<Data>);
|
|
|
|
/**
|
|
* Parceling of Datum by recursive descent to a ByteString
|
|
*
|
|
* Parceling Format:
|
|
* All values are native endian order.
|
|
*
|
|
* Datum = {
|
|
* (type_size_t) Type (the type index from type_as_value<T>.)
|
|
* (datum_size_t) Size (size of Payload)
|
|
* (byte string) Payload<Type>
|
|
* }
|
|
*
|
|
* Payload<Primitive_Type> = { bytes in native endian order }
|
|
*
|
|
* Payload<String> = { (index_size_t) number of elements (not including zero termination)
|
|
* bytes of string data.
|
|
* }
|
|
*
|
|
* Vector, Map, Container types:
|
|
* Payload<Type> = { (index_size_t) number of elements
|
|
* (byte string) Payload<Element_Type> * number
|
|
* }
|
|
*
|
|
* Pair container types:
|
|
* Payload<Type> = { (byte string) Payload<first>,
|
|
* (byte string) Payload<second>
|
|
* }
|
|
*
|
|
* Note: Data is a std::map<std::string, Datum>
|
|
*
|
|
* Design notes:
|
|
*
|
|
* 1) The size of each datum allows skipping of unknown types for compatibility
|
|
* of older code with newer Datums.
|
|
*
|
|
* Examples:
|
|
* Payload<Int32> of 123
|
|
* [ value of 123 ] = 0x7b 0x00 0x00 0x00 123
|
|
*
|
|
* Example of Payload<String> of std::string("hi"):
|
|
* [ (index_size_t) length ] = 0x02 0x00 0x00 0x00 2 strlen("hi")
|
|
* [ raw bytes "hi" ] = 0x68 0x69 "hi"
|
|
*
|
|
* Payload<Data>
|
|
* [ (index_size_t) entries ]
|
|
* [ raw bytes (entry 1) Key (Payload<String>)
|
|
* Value (Datum)
|
|
* ... (until #entries) ]
|
|
*
|
|
* Example of Payload<Data> of {{"hello", "world"},
|
|
* {"value", (int32_t)1000}};
|
|
* [ (index_size_t) #entries ] = 0x02 0x00 0x00 0x00 2 entries
|
|
* Key (Payload<String>)
|
|
* [ index_size_t length ] = 0x05 0x00 0x00 0x00 5 strlen("hello")
|
|
* [ raw bytes "hello" ] = 0x68 0x65 0x6c 0x6c 0x6f "hello"
|
|
* Value (Datum)
|
|
* [ (type_size_t) type ] = 0x05 0x00 0x00 0x00 5 (TYPE_STRING)
|
|
* [ (datum_size_t) size ] = 0x09 0x00 0x00 0x00 sizeof(index_size_t) +
|
|
* strlen("world")
|
|
* Payload<String>
|
|
* [ (index_size_t) length ] = 0x05 0x00 0x00 0x00 5 strlen("world")
|
|
* [ raw bytes "world" ] = 0x77 0x6f 0x72 0x6c 0x64 "world"
|
|
* Key (Payload<String>)
|
|
* [ index_size_t length ] = 0x05 0x00 0x00 0x00 5 strlen("value")
|
|
* [ raw bytes "value" ] = 0x76 0x61 0x6c 0x75 0x65 "value"
|
|
* Value (Datum)
|
|
* [ (type_size_t) type ] = 0x01 0x00 0x00 0x00 1 (TYPE_INT32)
|
|
* [ (datum_size_t) size ] = 0x04 0x00 0x00 0x00 4 sizeof(int32_t)
|
|
* Payload<Int32>
|
|
* [ raw bytes 1000 ] = 0xe8 0x03 0x00 0x00 1000
|
|
*
|
|
* Metadata is passed as a Payload<Data>.
|
|
* An implementation dependent detail is that the Keys are always
|
|
* stored sorted, so the byte string representation generated is unique.
|
|
*/
|
|
|
|
// Platform Apex compatibility note:
|
|
// type_size_t may not change.
|
|
using type_size_t = uint32_t;
|
|
|
|
// Platform Apex compatibility note:
|
|
// index_size_t must not change.
|
|
using index_size_t = uint32_t;
|
|
|
|
// Platform Apex compatibility note:
|
|
// datum_size_t must not change.
|
|
using datum_size_t = uint32_t;
|
|
|
|
// The particular implementation of ByteString may change
|
|
// without affecting compatibility.
|
|
using ByteString = std::basic_string<uint8_t>;
|
|
|
|
/*
|
|
These should correspond to the Java AudioMetadata.java
|
|
|
|
Permitted type indexes:
|
|
|
|
TYPE_NONE = 0,
|
|
TYPE_INT32 = 1,
|
|
TYPE_INT64 = 2,
|
|
TYPE_FLOAT = 3,
|
|
TYPE_DOUBLE = 4,
|
|
TYPE_STRING = 5,
|
|
TYPE_DATA = 6,
|
|
*/
|
|
|
|
template <typename T>
|
|
inline constexpr type_size_t get_type_as_value() {
|
|
return (type_size_t)(metadata_types::index_of<T>() + 1);
|
|
}
|
|
|
|
template <typename T>
|
|
inline constexpr type_size_t type_as_value = get_type_as_value<T>();
|
|
|
|
// forward decl for recursion - do not remove.
|
|
bool copyToByteString(const Datum& datum, ByteString &bs);
|
|
|
|
template <template <typename ...> class V, typename... Args>
|
|
bool copyToByteString(const V<Args...>& v, ByteString&bs);
|
|
// end forward decl
|
|
|
|
// primitives handled here
|
|
template <typename T>
|
|
std::enable_if_t<
|
|
is_primitive_metadata_type_v<T> || std::is_arithmetic_v<std::decay_t<T>>,
|
|
bool
|
|
>
|
|
copyToByteString(const T& t, ByteString& bs) {
|
|
bs.append((uint8_t*)&t, sizeof(t));
|
|
return true;
|
|
}
|
|
|
|
// pairs handled here
|
|
template <typename A, typename B>
|
|
bool copyToByteString(const std::pair<A, B>& p, ByteString& bs) {
|
|
return copyToByteString(p.first, bs) && copyToByteString(p.second, bs);
|
|
}
|
|
|
|
// containers
|
|
template <template <typename ...> class V, typename... Args>
|
|
bool copyToByteString(const V<Args...>& v, ByteString& bs) {
|
|
if (v.size() > std::numeric_limits<index_size_t>::max()) return false;
|
|
index_size_t size = v.size();
|
|
if (!copyToByteString(size, bs)) return false;
|
|
if constexpr (std::is_same_v<std::decay_t<V<Args...>>, std::string>) {
|
|
bs.append((uint8_t*)v.c_str());
|
|
} else /* constexpr */ {
|
|
for (const auto &d : v) { // handles std::vector and std::map
|
|
if (!copyToByteString(d, bs)) return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// simple struct data (use structured binding to extract members)
|
|
template <typename T>
|
|
std::enable_if_t<
|
|
is_structural_metadata_type_v<T>,
|
|
bool
|
|
>
|
|
copyToByteString(const T& t, ByteString& bs) {
|
|
using type = std::decay_t<T>;
|
|
if constexpr (is_braces_constructible<type, any_type, any_type, any_type, any_type>{}) {
|
|
const auto& [e1, e2, e3, e4] = t;
|
|
return copyToByteString(e1, bs)
|
|
&& copyToByteString(e2, bs)
|
|
&& copyToByteString(e3, bs)
|
|
&& copyToByteString(e4, bs);
|
|
} else if constexpr (is_braces_constructible<type, any_type, any_type, any_type>{}) {
|
|
const auto& [e1, e2, e3] = t;
|
|
return copyToByteString(e1, bs)
|
|
&& copyToByteString(e2, bs)
|
|
&& copyToByteString(e3, bs);
|
|
} else if constexpr (is_braces_constructible<type, any_type, any_type>{}) {
|
|
const auto& [e1, e2] = t;
|
|
return copyToByteString(e1, bs)
|
|
&& copyToByteString(e2, bs);
|
|
} else if constexpr(is_braces_constructible<type, any_type>{}) {
|
|
const auto& [e1] = t;
|
|
return copyToByteString(e1, bs);
|
|
} else if constexpr (is_braces_constructible<type>{}) {
|
|
return true; // like std::monostate - no members
|
|
} else /* constexpr */ {
|
|
static_assert(dependent_false_v<T>);
|
|
}
|
|
}
|
|
|
|
// TODO Consider moving to .cpp, but one advantage of keeping in the header
|
|
// is that C++ invocations don't need to link with the shared library.
|
|
|
|
// Datum
|
|
inline
|
|
bool copyToByteString(const Datum& datum, ByteString &bs) {
|
|
bool success = false;
|
|
return metadata_types::apply([&bs, &success](auto ptr) {
|
|
// save type
|
|
const type_size_t type = type_as_value<decltype(*ptr)>;
|
|
if (!copyToByteString(type, bs)) return;
|
|
|
|
// get current location
|
|
const size_t idx = bs.size();
|
|
|
|
// save size (replaced later)
|
|
datum_size_t datum_size = 0;
|
|
if (!copyToByteString(datum_size, bs)) return;
|
|
|
|
// copy data
|
|
if (!copyToByteString(*ptr, bs)) return;
|
|
|
|
// save correct size
|
|
const size_t diff = bs.size() - idx - sizeof(datum_size);
|
|
if (diff > std::numeric_limits<datum_size_t>::max()) return;
|
|
datum_size = diff;
|
|
bs.replace(idx, sizeof(datum_size), (uint8_t*)&datum_size, sizeof(datum_size));
|
|
success = true;
|
|
}, &datum) && success;
|
|
}
|
|
|
|
/**
|
|
* Obtaining the Datum back from ByteString
|
|
*/
|
|
|
|
// A container that lists all the unknown types found during parsing.
|
|
using ByteStringUnknowns = std::vector<type_size_t>;
|
|
|
|
// forward decl for recursion - do not remove.
|
|
bool copyFromByteString(Datum *datum, const ByteString &bs, size_t& idx,
|
|
ByteStringUnknowns *unknowns);
|
|
|
|
template <template <typename ...> class V, typename... Args>
|
|
bool copyFromByteString(V<Args...> *v, const ByteString& bs, size_t& idx,
|
|
ByteStringUnknowns *unknowns);
|
|
|
|
// primitive
|
|
template <typename T>
|
|
std::enable_if_t<
|
|
is_primitive_metadata_type_v<T> ||
|
|
std::is_arithmetic_v<std::decay_t<T>>,
|
|
bool
|
|
>
|
|
copyFromByteString(T *dest, const ByteString& bs, size_t& idx,
|
|
ByteStringUnknowns *unknowns __attribute__((unused))) {
|
|
if (idx + sizeof(T) > bs.size()) return false;
|
|
bs.copy((uint8_t*)dest, sizeof(T), idx);
|
|
idx += sizeof(T);
|
|
return true;
|
|
}
|
|
|
|
// pairs
|
|
template <typename A, typename B>
|
|
bool copyFromByteString(std::pair<A, B>* p, const ByteString& bs, size_t& idx,
|
|
ByteStringUnknowns *unknowns) {
|
|
return copyFromByteString(&p->first, bs, idx, unknowns)
|
|
&& copyFromByteString(&p->second, bs, idx, unknowns);
|
|
}
|
|
|
|
// containers
|
|
template <template <typename ...> class V, typename... Args>
|
|
bool copyFromByteString(V<Args...> *v, const ByteString& bs, size_t& idx,
|
|
ByteStringUnknowns *unknowns) {
|
|
index_size_t size;
|
|
if (!copyFromByteString(&size, bs, idx, unknowns)) return false;
|
|
|
|
if constexpr (std::is_same_v<std::decay_t<V<Args...>>, std::string>) {
|
|
if (size > bs.size() - idx) return false;
|
|
v->resize(size);
|
|
for (index_size_t i = 0; i < size; ++i) {
|
|
(*v)[i] = bs[idx++];
|
|
}
|
|
} else if constexpr (is_specialization_v<std::decay_t<V<Args...>>, std::vector>) {
|
|
for (index_size_t i = 0; i < size; ++i) {
|
|
std::decay_t<decltype(*v->begin())> value{};
|
|
if (!copyFromByteString(&value, bs, idx, unknowns)) {
|
|
return false;
|
|
}
|
|
if constexpr (std::is_same_v<std::decay_t<decltype(value)>, Datum>) {
|
|
if (!value.has_value()) {
|
|
continue; // ignore empty datum values in a vector.
|
|
}
|
|
}
|
|
v->emplace_back(std::move(value));
|
|
}
|
|
} else if constexpr (is_specialization_v<std::decay_t<V<Args...>>, std::map>) {
|
|
for (index_size_t i = 0; i < size; ++i) {
|
|
// we can't directly use pair because there may be internal const decls.
|
|
std::decay_t<decltype(v->begin()->first)> key{};
|
|
std::decay_t<decltype(v->begin()->second)> value{};
|
|
if (!copyFromByteString(&key, bs, idx, unknowns) ||
|
|
!copyFromByteString(&value, bs, idx, unknowns)) {
|
|
return false;
|
|
}
|
|
if constexpr (std::is_same_v<std::decay_t<decltype(value)>, Datum>) {
|
|
if (!value.has_value()) {
|
|
continue; // ignore empty datum values in a map.
|
|
}
|
|
}
|
|
v->emplace(std::move(key), std::move(value));
|
|
}
|
|
} else /* constexpr */ {
|
|
for (index_size_t i = 0; i < size; ++i) {
|
|
std::decay_t<decltype(*v->begin())> value{};
|
|
if (!copyFromByteString(&value, bs, idx, unknowns)) {
|
|
return false;
|
|
}
|
|
v->emplace(std::move(value));
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// simple structs (use structured binding to extract members)
|
|
template <typename T>
|
|
typename std::enable_if_t<is_structural_metadata_type_v<T>, bool>
|
|
copyFromByteString(T *t, const ByteString& bs, size_t& idx,
|
|
ByteStringUnknowns *unknowns) {
|
|
using type = std::decay_t<T>;
|
|
if constexpr (is_braces_constructible<type, any_type, any_type, any_type, any_type>{}) {
|
|
auto& [e1, e2, e3, e4] = *t;
|
|
return copyFromByteString(&e1, bs, idx, unknowns)
|
|
&& copyFromByteString(&e2, bs, idx, unknowns)
|
|
&& copyFromByteString(&e3, bs, idx, unknowns)
|
|
&& copyFromByteString(&e4, bs, idx, unknowns);
|
|
} else if constexpr (is_braces_constructible<type, any_type, any_type, any_type>{}) {
|
|
auto& [e1, e2, e3] = *t;
|
|
return copyFromByteString(&e1, bs, idx, unknowns)
|
|
&& copyFromByteString(&e2, bs, idx, unknowns)
|
|
&& copyFromByteString(&e3, bs, idx, unknowns);
|
|
} else if constexpr (is_braces_constructible<type, any_type, any_type>{}) {
|
|
auto& [e1, e2] = *t;
|
|
return copyFromByteString(&e1, bs, idx, unknowns)
|
|
&& copyFromByteString(&e2, bs, idx, unknowns);
|
|
} else if constexpr (is_braces_constructible<type, any_type>{}) {
|
|
auto& [e1] = *t;
|
|
return copyFromByteString(&e1, bs, idx, unknowns);
|
|
} else if constexpr (is_braces_constructible<type>{}) {
|
|
return true; // like std::monostate - no members
|
|
} else /* constexpr */ {
|
|
static_assert(dependent_false_v<T>);
|
|
}
|
|
}
|
|
|
|
namespace tedious_details {
|
|
//
|
|
// We build a function table at compile time to lookup the proper copyFromByteString method.
|
|
// See:
|
|
// https://stackoverflow.com/questions/36785345/void-to-the-nth-element-of-stdtuple-at-runtime
|
|
// Constant time implementation of std::visit (variant)
|
|
|
|
template <typename CompoundT, size_t Index>
|
|
bool copyFromByteString(Datum *datum, const ByteString &bs, size_t &idx, size_t endIdx,
|
|
ByteStringUnknowns *unknowns) {
|
|
using T = std::tuple_element_t<Index, typename CompoundT::tuple_t>;
|
|
T value;
|
|
if (!android::audio_utils::metadata::copyFromByteString(
|
|
&value, bs, idx, unknowns)) return false; // have we parsed correctly?
|
|
if (idx != endIdx) return false; // have we consumed the correct number of bytes?
|
|
*datum = std::move(value);
|
|
return true;
|
|
}
|
|
|
|
template <typename CompoundT, size_t... Indexes>
|
|
constexpr bool copyFromByteString(Datum *datum, const ByteString &bs,
|
|
size_t &idx, size_t endIdx, ByteStringUnknowns *unknowns,
|
|
size_t typeIndex, std::index_sequence<Indexes...>)
|
|
{
|
|
using function_type =
|
|
bool (*)(Datum*, const ByteString&, size_t&, size_t, ByteStringUnknowns*);
|
|
function_type constexpr ptrs[] = {
|
|
©FromByteString<CompoundT, Indexes>...
|
|
};
|
|
return ptrs[typeIndex](datum, bs, idx, endIdx, unknowns);
|
|
}
|
|
|
|
template <typename CompoundT>
|
|
__attribute__((noinline))
|
|
constexpr bool copyFromByteString(Datum *datum, const ByteString &bs,
|
|
size_t &idx, size_t endIdx, ByteStringUnknowns *unknowns, size_t typeIndex) {
|
|
return copyFromByteString<CompoundT>(
|
|
datum, bs, idx, endIdx, unknowns,
|
|
typeIndex, std::make_index_sequence<CompoundT::size_v>());
|
|
}
|
|
|
|
} // namespace tedious_details
|
|
|
|
// TODO Ditto about moving to .cpp.
|
|
|
|
inline
|
|
bool copyFromByteString(Datum *datum, const ByteString &bs, size_t& idx,
|
|
ByteStringUnknowns *unknowns) {
|
|
type_size_t type;
|
|
if (!copyFromByteString(&type, bs, idx, unknowns)) return false;
|
|
|
|
datum_size_t datum_size;
|
|
if (!copyFromByteString(&datum_size, bs, idx, unknowns)) return false;
|
|
if (datum_size > bs.size() - idx) return false;
|
|
const size_t endIdx = idx + datum_size;
|
|
|
|
if (type == 0 || type > metadata_types::size_v) {
|
|
idx = endIdx; // skip unrecognized type.
|
|
if (unknowns != nullptr) {
|
|
unknowns->push_back(type);
|
|
return true; // allow further recursion.
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// use special trick to instantiate all the types for copyFromByteString
|
|
// in a table and find the right method from table lookup.
|
|
return tedious_details::copyFromByteString<metadata_types>(
|
|
datum, bs, idx, endIdx, unknowns, type - 1);
|
|
}
|
|
|
|
// Handy helpers - these are the most efficient ways to parcel Data.
|
|
/**
|
|
* Returns the Data map from a byte string.
|
|
*
|
|
* If unknowns is nullptr, then any unknown entries during parsing will cause
|
|
* an empty map to be returned.
|
|
*
|
|
* If unknowns is non-null, then it contains all of the unknown types
|
|
* encountered during parsing, and a partial map will be returned excluding all
|
|
* unknown types encountered.
|
|
*/
|
|
inline
|
|
Data dataFromByteString(const ByteString &bs,
|
|
ByteStringUnknowns *unknowns = nullptr) {
|
|
Data d;
|
|
size_t idx = 0;
|
|
if (!copyFromByteString(&d, bs, idx, unknowns)) {
|
|
return {};
|
|
}
|
|
return d; // copy elision
|
|
}
|
|
|
|
inline
|
|
ByteString byteStringFromData(const Data &data) {
|
|
ByteString bs;
|
|
copyToByteString(data, bs);
|
|
return bs; // copy elision
|
|
}
|
|
|
|
/**
|
|
* \brief Returns the length of the byte string buffer from the raw pointer.
|
|
*
|
|
* The raw pointer comes from the Data object's ByteString.data()
|
|
* or from the C API byte_string_from_audio_metadata().
|
|
* This is a helper method for C implementations which may pass the raw
|
|
* byte string buffer pointer (which does not directly contain the length).
|
|
* C++ methods should always use the ByteString object.
|
|
*
|
|
* \param byteString byte string buffer raw pointer.
|
|
* \return size in bytes of metadata in the buffer or 0 if something went wrong.
|
|
*/
|
|
|
|
inline size_t dataByteStringLen(const uint8_t *ptr) {
|
|
index_size_t elements;
|
|
const uint8_t * const origPtr = ptr;
|
|
memcpy(&elements, ptr, sizeof(elements));
|
|
ptr += sizeof(elements);
|
|
for (index_size_t i = 0; i < elements; ++i) {
|
|
// get key (string)
|
|
index_size_t keyLen;
|
|
memcpy(&keyLen, ptr, sizeof(keyLen));
|
|
ptr += keyLen + sizeof(keyLen);
|
|
// get type
|
|
type_size_t type;
|
|
memcpy(&type, ptr, sizeof(type));
|
|
ptr += sizeof(type_size_t);
|
|
// Note: could check type validity.
|
|
// payload size
|
|
datum_size_t datumSize;
|
|
memcpy(&datumSize, ptr, sizeof(datumSize));
|
|
ptr += datumSize + sizeof(datumSize);
|
|
}
|
|
const ptrdiff_t size = ptr - origPtr;
|
|
return size < 0 ? 0 : size;
|
|
}
|
|
|
|
} // namespace android::audio_utils::metadata
|
|
|
|
#endif // __cplusplus
|
|
|
|
// C API (see C++ API above for details)
|
|
|
|
/** \cond */
|
|
__BEGIN_DECLS
|
|
/** \endcond */
|
|
|
|
typedef struct audio_metadata_t audio_metadata_t;
|
|
|
|
// Used by audio_metadata_put_unknown() and audio_metadata_get_unknown(), but not part of public API
|
|
// The name and data structure representation discourage accidental use.
|
|
typedef struct { char c; } audio_metadata_unknown_t;
|
|
|
|
/**
|
|
* \brief Creates a metadata object
|
|
*
|
|
* \return the metadata object or NULL on failure. Caller must call
|
|
* audio_metadata_destroy to free memory.
|
|
*/
|
|
audio_metadata_t *audio_metadata_create();
|
|
|
|
/**
|
|
* \brief Put key value pair where the value type is int32_t to audio metadata.
|
|
*
|
|
* \param metadata the audio metadata object.
|
|
* \param key the key of the element to be put.
|
|
* \param value the value of the element to be put.
|
|
* \return 0 if the key value pair is put successfully into the audio metadata.
|
|
* -EINVAL if metadata or key is null.
|
|
*/
|
|
int audio_metadata_put_int32(audio_metadata_t *metadata, const char *key, int32_t value);
|
|
|
|
/**
|
|
* \brief Put key value pair where the value type is int64_t to audio metadata.
|
|
*
|
|
* \param metadata the audio metadata object.
|
|
* \param key the key of the element to be put.
|
|
* \param value the value of the element to be put.
|
|
* \return 0 if the key value pair is put successfully into the audio metadata.
|
|
* -EINVAL if metadata or key is null.
|
|
*/
|
|
int audio_metadata_put_int64(audio_metadata_t *metadata, const char *key, int64_t value);
|
|
|
|
/**
|
|
* \brief Put key value pair where the value type is float to audio metadata.
|
|
*
|
|
* \param metadata the audio metadata object.
|
|
* \param key the key of the element to be put.
|
|
* \param value the value of the element to be put.
|
|
* \return 0 if the key value pair is put successfully into the audio metadata.
|
|
* -EINVAL if metadata or key is null.
|
|
*/
|
|
int audio_metadata_put_float(audio_metadata_t *metadata, const char *key, float value);
|
|
|
|
/**
|
|
* \brief Put key value pair where the value type is double to audio metadata.
|
|
*
|
|
* \param metadata the audio metadata object.
|
|
* \param key the key of the element to be put.
|
|
* \param value the value of the element to be put.
|
|
* \return 0 if the key value pair is put successfully into the audio metadata.
|
|
* -EINVAL if metadata or key is null.
|
|
*/
|
|
int audio_metadata_put_double(audio_metadata_t *metadata, const char *key, double value);
|
|
|
|
/**
|
|
* \brief Put key value pair where the value type is `const char *` to audio metadata.
|
|
*
|
|
* \param metadata the audio metadata object.
|
|
* \param key the key of the element to be put.
|
|
* \param value the value of the element to be put.
|
|
* \return 0 if the key value pair is put successfully into the audio metadata.
|
|
* -EINVAL if metadata, key or value is null.
|
|
*/
|
|
int audio_metadata_put_string(audio_metadata_t *metadata, const char *key, const char *value);
|
|
|
|
/**
|
|
* \brief Put key value pair where the value type is audio_metadata_t to audio metadata.
|
|
*
|
|
* \param metadata the audio metadata object.
|
|
* \param key the key of the element to be put.
|
|
* \param value the value of the element to be put.
|
|
* \return 0 if the key value pair is put successfully into the audio metadata.
|
|
* -EINVAL if metadata, key or value is null.
|
|
*/
|
|
int audio_metadata_put_data(audio_metadata_t *metadata, const char *key, audio_metadata_t *value);
|
|
|
|
/**
|
|
* \brief Declared but not implemented, as any potential caller won't supply a correct value.
|
|
*/
|
|
int audio_metadata_put_unknown(audio_metadata_t *metadata, const char *key,
|
|
audio_metadata_unknown_t value);
|
|
|
|
#ifndef __cplusplus // Only C11 has _Generic; C++ uses overloaded declarations instead
|
|
|
|
// use C Generics to provide interfaces for put/get functions
|
|
// See: https://en.cppreference.com/w/c/language/generic
|
|
|
|
/**
|
|
* A generic interface to put key value pair into the audio metadata.
|
|
* Fails at compile-time if type isn't supported.
|
|
*/
|
|
#define audio_metadata_put(metadata, key, value) _Generic((value), \
|
|
int32_t: audio_metadata_put_int32, \
|
|
int64_t: audio_metadata_put_int64, \
|
|
float: audio_metadata_put_float, \
|
|
double: audio_metadata_put_double, \
|
|
/* https://stackoverflow.com/questions/18857056/c11-generic-how-to-deal-with-string-literals */ \
|
|
const char*: audio_metadata_put_string, \
|
|
char*: audio_metadata_put_string, \
|
|
audio_metadata_t*: audio_metadata_put_data, \
|
|
default: audio_metadata_put_unknown \
|
|
)(metadata, key, value)
|
|
|
|
#endif // !__cplusplus
|
|
|
|
/**
|
|
* \brief Get mapped value whose type is int32_t by a given key from audio metadata.
|
|
*
|
|
* \param metadata the audio metadata object.
|
|
* \param key the key value to get value.
|
|
* \param value the mapped value to be written.
|
|
* \return -EINVAL when 1) metadata is null, 2) key is null, or 3) value is null.
|
|
* -ENOENT when 1) key is found in the audio metadata,
|
|
* 2) the type of mapped value is not int32_t.
|
|
* 0 if successfully find the mapped value.
|
|
*/
|
|
int audio_metadata_get_int32(audio_metadata_t *metadata, const char *key, int32_t *value);
|
|
|
|
/**
|
|
* \brief Get mapped value whose type is int64_t by a given key from audio metadata.
|
|
*
|
|
* \param metadata the audio metadata object.
|
|
* \param key the key value to get value.
|
|
* \param value the mapped value to be written.
|
|
* \return -EINVAL when 1) metadata is null, 2) key is null, or 3) value is null.
|
|
* -ENOENT when 1) key is found in the audio metadata,
|
|
* 2) the type of mapped value is not int32_t.
|
|
* 0 if successfully find the mapped value.
|
|
*/
|
|
int audio_metadata_get_int64(audio_metadata_t *metadata, const char *key, int64_t *value);
|
|
|
|
/**
|
|
* \brief Get mapped value whose type is float by a given key from audio metadata.
|
|
*
|
|
* \param metadata the audio metadata object.
|
|
* \param key the key value to get value.
|
|
* \param value the mapped value to be written.
|
|
* \return -EINVAL when 1) metadata is null, 2) key is null, or 3) value is null.
|
|
* -ENOENT when 1) key is found in the audio metadata,
|
|
* 2) the type of mapped value is not float.
|
|
* 0 if successfully find the mapped value.
|
|
*/
|
|
int audio_metadata_get_float(audio_metadata_t *metadata, const char *key, float *value);
|
|
|
|
/**
|
|
* \brief Get mapped value whose type is double by a given key from audio metadata.
|
|
*
|
|
* \param metadata the audio metadata object.
|
|
* \param key the key value to get value.
|
|
* \param value the mapped value to be written.
|
|
* \return -EINVAL when 1) metadata is null, 2) key is null, or 3) value is null.
|
|
* -ENOENT when 1) key is found in the audio metadata,
|
|
* 2) the type of mapped value is not double.
|
|
* 0 if successfully find the mapped value.
|
|
*/
|
|
int audio_metadata_get_double(audio_metadata_t *metadata, const char *key, double *value);
|
|
|
|
/**
|
|
* \brief Get mapped value whose type is std::string by a given key from audio metadata.
|
|
*
|
|
* \param metadata the audio metadata object.
|
|
* \param key the key value to get value.
|
|
* \param value the mapped value to be written. The memory will be allocated in the
|
|
* function, which must be freed by caller.
|
|
* \return -EINVAL when 1) metadata is null, 2) key is null, or 3) value is null.
|
|
* -ENOENT when 1) key is found in the audio metadata,
|
|
* 2) the type of mapped value is not std::string.
|
|
* -ENOMEM when fails allocating memory for value.
|
|
* 0 if successfully find the mapped value.
|
|
*/
|
|
int audio_metadata_get_string(audio_metadata_t *metadata, const char *key, char **value);
|
|
|
|
/**
|
|
* \brief Get mapped value whose type is audio_metadata_t by a given key from audio metadata.
|
|
*
|
|
* \param metadata the audio metadata object.
|
|
* \param key the key value to get value.
|
|
* \param value the mapped value to be written. The memory will be allocated in the
|
|
* function, which should be free by caller via audio_metadata_destroy.
|
|
* \return -EINVAL when 1) metadata is null, 2) key is null, or 3) value is null.
|
|
* -ENOENT when 1) key is found in the audio metadata,
|
|
* 2) the type of mapped value is not audio_utils::metadata::Data.
|
|
* -ENOMEM when fails allocating memory for value.
|
|
* 0 if successfully find the mapped value.
|
|
*/
|
|
int audio_metadata_get_data(audio_metadata_t *metadata, const char *key, audio_metadata_t **value);
|
|
|
|
/**
|
|
* \brief Declared but not implemented, as any potential caller won't supply a correct value.
|
|
*/
|
|
int audio_metadata_get_unknown(audio_metadata_t *metadata, const char *key,
|
|
audio_metadata_unknown_t *value);
|
|
|
|
#ifndef __cplusplus // Only C11 has _Generic; C++ uses overloaded declarations instead
|
|
|
|
/**
|
|
* A generic interface to get mapped value by a given key from audio metadata. The value object
|
|
* will remain the same if the key is not found in the audio metadata.
|
|
* Fails at compile-time if type isn't supported.
|
|
*/
|
|
#define audio_metadata_get(metadata, key, value) _Generic((value), \
|
|
int32_t*: audio_metadata_get_int32, \
|
|
int64_t*: audio_metadata_get_int64, \
|
|
float*: audio_metadata_get_float, \
|
|
double*: audio_metadata_get_double, \
|
|
char**: audio_metadata_get_string, \
|
|
audio_metadata_t**: audio_metadata_get_data, \
|
|
default: audio_metadata_get_unknown \
|
|
)(metadata, key, value)
|
|
|
|
#endif // !__cplusplus
|
|
|
|
/**
|
|
* \brief Remove item from audio metadata.
|
|
*
|
|
* \param metadata the audio metadata object.
|
|
* \param key the key of the item that is going to be removed.
|
|
* \return -EINVAL if metadata or key is null. Otherwise, return the number of elements erased.
|
|
*/
|
|
ssize_t audio_metadata_erase(audio_metadata_t *metadata, const char *key);
|
|
|
|
/**
|
|
* \brief Destroys the metadata object
|
|
*
|
|
* \param metadata object returned by create, if NULL nothing happens.
|
|
*/
|
|
void audio_metadata_destroy(audio_metadata_t *metadata);
|
|
|
|
/**
|
|
* \brief Unpack byte string into a given audio metadata
|
|
*
|
|
* \param byteString a byte string that contains data to convert to audio metadata.
|
|
* \param length the length of the byte string
|
|
* \return the audio metadata object that contains the converted data. Caller must call
|
|
* audio_metadata_destroy to free the memory.
|
|
*/
|
|
audio_metadata_t *audio_metadata_from_byte_string(const uint8_t *byteString, size_t length);
|
|
|
|
/**
|
|
* \brief Pack the audio metadata into a byte string
|
|
*
|
|
* \param metadata the audio metadata object to be converted.
|
|
* \param byteString the buffer to write data to. The memory will be allocated
|
|
* in the function, which must be freed by caller via free().
|
|
* \return -EINVAL if metadata or byteString is null.
|
|
* -ENOMEM if fails to allocate memory for byte string.
|
|
* The length of the byte string.
|
|
*/
|
|
ssize_t byte_string_from_audio_metadata(audio_metadata_t *metadata, uint8_t **byteString);
|
|
|
|
/**
|
|
* \brief Return the size in bytes of the metadata byte string
|
|
*
|
|
* Note: strlen() cannot be used as there are embedded 0's in the byte string.
|
|
*
|
|
* \param byteString a valid byte string buffer from byte_string_from_audio_metadata().
|
|
* \return size in bytes of metadata in the buffer or 0 if something went wrong.
|
|
*/
|
|
size_t audio_metadata_byte_string_len(const uint8_t *byteString);
|
|
|
|
/** \cond */
|
|
__END_DECLS
|
|
/** \endcond */
|
|
|
|
#ifdef __cplusplus
|
|
|
|
inline
|
|
int audio_metadata_put(audio_metadata_t *metadata, const char *key, int32_t value)
|
|
{
|
|
return audio_metadata_put_int32(metadata, key, value);
|
|
}
|
|
|
|
inline
|
|
int audio_metadata_put(audio_metadata_t *metadata, const char *key, int64_t value)
|
|
{
|
|
return audio_metadata_put_int64(metadata, key, value);
|
|
}
|
|
|
|
inline
|
|
int audio_metadata_put(audio_metadata_t *metadata, const char *key, float value)
|
|
{
|
|
return audio_metadata_put_float(metadata, key, value);
|
|
}
|
|
|
|
inline
|
|
int audio_metadata_put(audio_metadata_t *metadata, const char *key, double value)
|
|
{
|
|
return audio_metadata_put_double(metadata, key, value);
|
|
}
|
|
|
|
inline
|
|
int audio_metadata_put(audio_metadata_t *metadata, const char *key, const char *value)
|
|
{
|
|
return audio_metadata_put_string(metadata, key, value);
|
|
}
|
|
|
|
inline
|
|
int audio_metadata_put(audio_metadata_t *metadata, const char *key, audio_metadata_t *value)
|
|
{
|
|
return audio_metadata_put_data(metadata, key, value);
|
|
}
|
|
|
|
// No overload for default type
|
|
|
|
inline
|
|
int audio_metadata_get(audio_metadata_t *metadata, const char *key, int32_t *value)
|
|
{
|
|
return audio_metadata_get_int32(metadata, key, value);
|
|
}
|
|
|
|
inline
|
|
int audio_metadata_get(audio_metadata_t *metadata, const char *key, int64_t *value)
|
|
{
|
|
return audio_metadata_get_int64(metadata, key, value);
|
|
}
|
|
|
|
inline
|
|
int audio_metadata_get(audio_metadata_t *metadata, const char *key, float *value)
|
|
{
|
|
return audio_metadata_get_float(metadata, key, value);
|
|
}
|
|
|
|
inline
|
|
int audio_metadata_get(audio_metadata_t *metadata, const char *key, double *value)
|
|
{
|
|
return audio_metadata_get_double(metadata, key, value);
|
|
}
|
|
|
|
inline
|
|
int audio_metadata_get(audio_metadata_t *metadata, const char *key, char **value)
|
|
{
|
|
return audio_metadata_get_string(metadata, key, value);
|
|
}
|
|
|
|
inline
|
|
int audio_metadata_get(audio_metadata_t *metadata, const char *key, audio_metadata_t **value)
|
|
{
|
|
return audio_metadata_get_data(metadata, key, value);
|
|
}
|
|
|
|
// No overload for default type
|
|
|
|
#endif // __cplusplus
|
|
|
|
#endif // !ANDROID_AUDIO_METADATA_H
|