Remove manual parceling from Interpolator and VolumeShaper
Created stable parcelables to hold all the data that gets parceled.
Replaced manual parceling with a two-step approach, where we first
write into the auto-generated parcelable, then parcel it.
Test: Ran audio-related CTS tests
Bug: 160253486
Change-Id: I5a4d51fb1244ed49bcd70e446aaa26e2aa8ed7b1
diff --git a/include/media/Interpolator.h b/include/media/Interpolator.h
index 703cf77..2004acb 100644
--- a/include/media/Interpolator.h
+++ b/include/media/Interpolator.h
@@ -21,6 +21,7 @@
#include <sstream>
#include <unordered_map>
+#include <android/media/InterpolatorConfig.h>
#include <binder/Parcel.h>
#include <utils/RefBase.h>
@@ -39,17 +40,10 @@
class Interpolator : public std::map<S, T> {
public:
// Polynomial spline interpolators
- // Extend only at the end of enum, as this must match order in VolumeShapers.java.
- enum InterpolatorType : int32_t {
- INTERPOLATOR_TYPE_STEP, // Not continuous
- INTERPOLATOR_TYPE_LINEAR, // C0
- INTERPOLATOR_TYPE_CUBIC, // C1
- INTERPOLATOR_TYPE_CUBIC_MONOTONIC, // C1 (to provide locally monotonic curves)
- // INTERPOLATOR_TYPE_CUBIC_C2, // TODO - requires global computation / cache
- };
+ using InterpolatorType = media::InterpolatorType;
explicit Interpolator(
- InterpolatorType interpolatorType = INTERPOLATOR_TYPE_LINEAR,
+ InterpolatorType interpolatorType = InterpolatorType::LINEAR,
bool cache = true)
: mCache(cache)
, mFirstSlope(0)
@@ -82,13 +76,13 @@
// now that we have two adjacent points:
switch (mInterpolatorType) {
- case INTERPOLATOR_TYPE_STEP:
+ case InterpolatorType::STEP:
return high->first == x ? high->second : low->second;
- case INTERPOLATOR_TYPE_LINEAR:
+ case InterpolatorType::LINEAR:
return ((high->first - x) * low->second + (x - low->first) * high->second)
/ (high->first - low->first);
- case INTERPOLATOR_TYPE_CUBIC:
- case INTERPOLATOR_TYPE_CUBIC_MONOTONIC:
+ case InterpolatorType::CUBIC:
+ case InterpolatorType::CUBIC_MONOTONIC:
default: {
// See https://en.wikipedia.org/wiki/Cubic_Hermite_spline
@@ -116,7 +110,7 @@
// non catmullRom (finite difference) with regular cubic;
// the choices here minimize computation.
bool monotonic, catmullRom;
- if (mInterpolatorType == INTERPOLATOR_TYPE_CUBIC_MONOTONIC) {
+ if (mInterpolatorType == InterpolatorType::CUBIC_MONOTONIC) {
monotonic = true;
catmullRom = false;
} else {
@@ -202,11 +196,11 @@
status_t setInterpolatorType(InterpolatorType interpolatorType) {
switch (interpolatorType) {
- case INTERPOLATOR_TYPE_STEP: // Not continuous
- case INTERPOLATOR_TYPE_LINEAR: // C0
- case INTERPOLATOR_TYPE_CUBIC: // C1
- case INTERPOLATOR_TYPE_CUBIC_MONOTONIC: // C1 + other constraints
- // case INTERPOLATOR_TYPE_CUBIC_C2:
+ case InterpolatorType::STEP: // Not continuous
+ case InterpolatorType::LINEAR: // C0
+ case InterpolatorType::CUBIC: // C1
+ case InterpolatorType::CUBIC_MONOTONIC: // C1 + other constraints
+ // case InterpolatorType::CUBIC_C2:
mInterpolatorType = interpolatorType;
return NO_ERROR;
default:
@@ -235,49 +229,50 @@
mMemo.clear();
}
+ // TODO(ytai): remove this method once it is not used.
status_t writeToParcel(Parcel *parcel) const {
- if (parcel == nullptr) {
- return BAD_VALUE;
- }
- status_t res = parcel->writeInt32(mInterpolatorType)
- ?: parcel->writeFloat(mFirstSlope)
- ?: parcel->writeFloat(mLastSlope)
- ?: parcel->writeUint32((uint32_t)this->size()); // silent truncation
- if (res != NO_ERROR) {
- return res;
- }
- for (const auto &pt : *this) {
- res = parcel->writeFloat(pt.first)
- ?: parcel->writeFloat(pt.second);
- if (res != NO_ERROR) {
- return res;
- }
- }
- return NO_ERROR;
+ media::InterpolatorConfig config;
+ writeToConfig(&config);
+ return config.writeToParcel(parcel);
}
+ void writeToConfig(media::InterpolatorConfig *config) const {
+ config->type = mInterpolatorType;
+ config->firstSlope = mFirstSlope;
+ config->lastSlope = mLastSlope;
+ for (const auto &pt : *this) {
+ config->xy.push_back(pt.first);
+ config->xy.push_back(pt.second);
+ }
+ }
+
+ // TODO(ytai): remove this method once it is not used.
status_t readFromParcel(const Parcel &parcel) {
- this->clear();
- int32_t type;
- uint32_t size;
- status_t res = parcel.readInt32(&type)
- ?: parcel.readFloat(&mFirstSlope)
- ?: parcel.readFloat(&mLastSlope)
- ?: parcel.readUint32(&size)
- ?: setInterpolatorType((InterpolatorType)type);
+ media::InterpolatorConfig config;
+ status_t res = config.readFromParcel(&parcel);
if (res != NO_ERROR) {
return res;
}
+ return readFromConfig(config);
+ }
+
+ status_t readFromConfig(const media::InterpolatorConfig &config) {
+ this->clear();
+ setInterpolatorType(config.type);
+ if ((config.xy.size() & 1) != 0) {
+ // xy size must be even.
+ return BAD_VALUE;
+ }
+ uint32_t size = config.xy.size() / 2;
+ mFirstSlope = config.firstSlope;
+ mLastSlope = config.lastSlope;
+
// Note: We don't need to check size is within some bounds as
// the Parcel read will fail if size is incorrectly specified too large.
float lastx;
for (uint32_t i = 0; i < size; ++i) {
- float x, y;
- res = parcel.readFloat(&x)
- ?: parcel.readFloat(&y);
- if (res != NO_ERROR) {
- return res;
- }
+ float x = config.xy[i * 2];
+ float y = config.xy[i * 2 + 1];
if ((i > 0 && !(x > lastx)) /* handle nan */
|| y != y /* handle nan */) {
// This is a std::map object which imposes sorted order
diff --git a/include/media/VolumeShaper.h b/include/media/VolumeShaper.h
index fe519bb..f8ead2f 100644
--- a/include/media/VolumeShaper.h
+++ b/include/media/VolumeShaper.h
@@ -22,6 +22,11 @@
#include <math.h>
#include <sstream>
+#include <android/media/VolumeShaperConfiguration.h>
+#include <android/media/VolumeShaperConfigurationOptionFlag.h>
+#include <android/media/VolumeShaperOperation.h>
+#include <android/media/VolumeShaperOperationFlag.h>
+#include <android/media/VolumeShaperState.h>
#include <binder/Parcel.h>
#include <media/Interpolator.h>
#include <utils/Mutex.h>
@@ -284,30 +289,38 @@
clampVolume();
}
- // The parcel layout must match VolumeShaper.java
status_t writeToParcel(Parcel *parcel) const override {
- if (parcel == nullptr) return BAD_VALUE;
- return parcel->writeInt32((int32_t)mType)
- ?: parcel->writeInt32(mId)
- ?: mType == TYPE_ID
- ? NO_ERROR
- : parcel->writeInt32((int32_t)mOptionFlags)
- ?: parcel->writeDouble(mDurationMs)
- ?: Interpolator<S, T>::writeToParcel(parcel);
+ VolumeShaperConfiguration parcelable;
+ writeToParcelable(&parcelable);
+ return parcelable.writeToParcel(parcel);
}
- status_t readFromParcel(const Parcel *parcel) override {
- int32_t type, optionFlags;
- return parcel->readInt32(&type)
- ?: setType((Type)type)
- ?: parcel->readInt32(&mId)
- ?: mType == TYPE_ID
- ? NO_ERROR
- : parcel->readInt32(&optionFlags)
- ?: setOptionFlags((OptionFlag)optionFlags)
- ?: parcel->readDouble(&mDurationMs)
- ?: Interpolator<S, T>::readFromParcel(*parcel)
- ?: checkCurve();
+ void writeToParcelable(VolumeShaperConfiguration *parcelable) const {
+ parcelable->id = getId();
+ parcelable->type = getTypeAsAidl();
+ parcelable->optionFlags = 0;
+ if (mType != TYPE_ID) {
+ parcelable->optionFlags = getOptionFlagsAsAidl();
+ parcelable->durationMs = getDurationMs();
+ Interpolator<S, T>::writeToConfig(&parcelable->interpolatorConfig);
+ }
+ }
+
+ status_t readFromParcel(const Parcel* parcel) override {
+ VolumeShaperConfiguration data;
+ return data.readFromParcel(parcel)
+ ?: readFromParcelable(data);
+ }
+
+ status_t readFromParcelable(const VolumeShaperConfiguration& parcelable) {
+ setId(parcelable.id);
+ return setTypeFromAidl(parcelable.type)
+ ?: mType == TYPE_ID
+ ? NO_ERROR
+ : setOptionFlagsFromAidl(parcelable.optionFlags)
+ ?: setDurationMs(parcelable.durationMs)
+ ?: Interpolator<S, T>::readFromConfig(parcelable.interpolatorConfig)
+ ?: checkCurve();
}
// Returns a string for debug printing.
@@ -329,6 +342,51 @@
int32_t mId; // A valid id is >= 0.
OptionFlag mOptionFlags; // option flags for the configuration.
double mDurationMs; // duration, must be > 0; default is 1000 ms.
+
+ int32_t getOptionFlagsAsAidl() const {
+ int32_t result = 0;
+ if (getOptionFlags() & OPTION_FLAG_VOLUME_IN_DBFS) {
+ result |=
+ 1 << static_cast<int>(VolumeShaperConfigurationOptionFlag::VOLUME_IN_DBFS);
+ }
+ if (getOptionFlags() & OPTION_FLAG_CLOCK_TIME) {
+ result |= 1 << static_cast<int>(VolumeShaperConfigurationOptionFlag::CLOCK_TIME);
+ }
+ return result;
+ }
+
+ status_t setOptionFlagsFromAidl(int32_t aidl) {
+ std::underlying_type_t<OptionFlag> options = 0;
+ if (aidl & (1 << static_cast<int>(VolumeShaperConfigurationOptionFlag::VOLUME_IN_DBFS))) {
+ options |= OPTION_FLAG_VOLUME_IN_DBFS;
+ }
+ if (aidl & (1 << static_cast<int>(VolumeShaperConfigurationOptionFlag::CLOCK_TIME))) {
+ options |= OPTION_FLAG_CLOCK_TIME;
+ }
+ return setOptionFlags(static_cast<OptionFlag>(options));
+ }
+
+ status_t setTypeFromAidl(VolumeShaperConfigurationType aidl) {
+ switch (aidl) {
+ case VolumeShaperConfigurationType::ID:
+ return setType(TYPE_ID);
+ case VolumeShaperConfigurationType::SCALE:
+ return setType(TYPE_SCALE);
+ default:
+ return BAD_VALUE;
+ }
+ }
+
+ VolumeShaperConfigurationType getTypeAsAidl() const {
+ switch (getType()) {
+ case TYPE_ID:
+ return VolumeShaperConfigurationType::ID;
+ case TYPE_SCALE:
+ return VolumeShaperConfigurationType::SCALE;
+ default:
+ LOG_ALWAYS_FATAL("Shouldn't get here");
+ }
+ }
}; // Configuration
/* VolumeShaper::Operation expresses an operation to perform on the
@@ -420,19 +478,29 @@
return NO_ERROR;
}
- status_t writeToParcel(Parcel *parcel) const override {
+ status_t writeToParcel(Parcel* parcel) const override {
if (parcel == nullptr) return BAD_VALUE;
- return parcel->writeInt32((int32_t)mFlags)
- ?: parcel->writeInt32(mReplaceId)
- ?: parcel->writeFloat(mXOffset);
+ VolumeShaperOperation op;
+ writeToParcelable(&op);
+ return op.writeToParcel(parcel);
}
- status_t readFromParcel(const Parcel *parcel) override {
- int32_t flags;
- return parcel->readInt32(&flags)
- ?: parcel->readInt32(&mReplaceId)
- ?: parcel->readFloat(&mXOffset)
- ?: setFlags((Flag)flags);
+ void writeToParcelable(VolumeShaperOperation* op) const {
+ op->flags = getFlagsAsAidl();
+ op->replaceId = mReplaceId;
+ op->xOffset = mXOffset;
+ }
+
+ status_t readFromParcel(const Parcel* parcel) override {
+ VolumeShaperOperation op;
+ return op.readFromParcel(parcel)
+ ?: readFromParcelable(op);
+ }
+
+ status_t readFromParcelable(const VolumeShaperOperation& op) {
+ mReplaceId = op.replaceId;
+ mXOffset = op.xOffset;
+ return setFlagsFromAidl(op.flags);
}
std::string toString() const {
@@ -445,6 +513,48 @@
}
private:
+ status_t setFlagsFromAidl(int32_t aidl) {
+ std::underlying_type_t<Flag> flags = 0;
+ if (aidl & (1 << static_cast<int>(VolumeShaperOperationFlag::REVERSE))) {
+ flags |= FLAG_REVERSE;
+ }
+ if (aidl & (1 << static_cast<int>(VolumeShaperOperationFlag::TERMINATE))) {
+ flags |= FLAG_TERMINATE;
+ }
+ if (aidl & (1 << static_cast<int>(VolumeShaperOperationFlag::JOIN))) {
+ flags |= FLAG_JOIN;
+ }
+ if (aidl & (1 << static_cast<int>(VolumeShaperOperationFlag::DELAY))) {
+ flags |= FLAG_DELAY;
+ }
+ if (aidl & (1 << static_cast<int>(VolumeShaperOperationFlag::CREATE_IF_NECESSARY))) {
+ flags |= FLAG_CREATE_IF_NECESSARY;
+ }
+ return setFlags(static_cast<Flag>(flags));
+ }
+
+ int32_t getFlagsAsAidl() const {
+ int32_t aidl = 0;
+ std::underlying_type_t<Flag> flags = getFlags();
+ if (flags & FLAG_REVERSE) {
+ aidl |= (1 << static_cast<int>(VolumeShaperOperationFlag::REVERSE));
+ }
+ if (flags & FLAG_TERMINATE) {
+ aidl |= (1 << static_cast<int>(VolumeShaperOperationFlag::TERMINATE));
+ }
+ if (flags & FLAG_JOIN) {
+ aidl |= (1 << static_cast<int>(VolumeShaperOperationFlag::JOIN));
+ }
+ if (flags & FLAG_DELAY) {
+ aidl |= (1 << static_cast<int>(VolumeShaperOperationFlag::DELAY));
+ }
+ if (flags & FLAG_CREATE_IF_NECESSARY) {
+ aidl |= (1 << static_cast<int>(VolumeShaperOperationFlag::CREATE_IF_NECESSARY));
+ }
+ return aidl;
+ }
+
+ private:
Flag mFlags; // operation to do
int32_t mReplaceId; // if >= 0 the id to remove in a replace operation.
S mXOffset; // position in the curve to set if a valid number (not nan)
@@ -483,15 +593,28 @@
mXOffset = xOffset;
}
- status_t writeToParcel(Parcel *parcel) const override {
+ status_t writeToParcel(Parcel* parcel) const override {
if (parcel == nullptr) return BAD_VALUE;
- return parcel->writeFloat(mVolume)
- ?: parcel->writeFloat(mXOffset);
+ VolumeShaperState state;
+ writeToParcelable(&state);
+ return state.writeToParcel(parcel);
}
- status_t readFromParcel(const Parcel *parcel) override {
- return parcel->readFloat(&mVolume)
- ?: parcel->readFloat(&mXOffset);
+ void writeToParcelable(VolumeShaperState* parcelable) const {
+ parcelable->volume = mVolume;
+ parcelable->xOffset = mXOffset;
+ }
+
+ status_t readFromParcel(const Parcel* parcel) override {
+ VolumeShaperState state;
+ return state.readFromParcel(parcel)
+ ?: readFromParcelable(state);
+ }
+
+ status_t readFromParcelable(const VolumeShaperState& parcelable) {
+ mVolume = parcelable.volume;
+ mXOffset = parcelable.xOffset;
+ return OK;
}
std::string toString() const {