Merge "stagefright: put battery update under a separate lock" into oc-dev
diff --git a/include/media/Interpolator.h b/include/media/Interpolator.h
index 120a074..45a0585 100644
--- a/include/media/Interpolator.h
+++ b/include/media/Interpolator.h
@@ -152,8 +152,8 @@
: mLastSlope;
} else {
// finite difference spline
- m0 = (sec0 + sec) * 0.5;
- m1 = (sec1 + sec) * 0.5;
+ m0 = (sec0 + sec) * 0.5f;
+ m1 = (sec1 + sec) * 0.5f;
}
if (monotonic) {
@@ -294,12 +294,21 @@
std::string toString() const {
std::stringstream ss;
- ss << "mInterpolatorType: " << mInterpolatorType << std::endl;
- ss << "mFirstSlope: " << mFirstSlope << std::endl;
- ss << "mLastSlope: " << mLastSlope << std::endl;
+ ss << "Interpolator{mInterpolatorType=" << static_cast<int32_t>(mInterpolatorType);
+ ss << ", mFirstSlope=" << mFirstSlope;
+ ss << ", mLastSlope=" << mLastSlope;
+ ss << ", {";
+ bool first = true;
for (const auto &pt : *this) {
- ss << pt.first << " " << pt.second << std::endl;
+ if (first) {
+ first = false;
+ ss << "{";
+ } else {
+ ss << ", {";
+ }
+ ss << pt.first << ", " << pt.second << "}";
}
+ ss << "}}";
return ss.str();
}
@@ -324,7 +333,7 @@
// spline cubic polynomial coefficient cache
std::unordered_map<S, std::tuple<S /* c1 */, S /* c2 */, S /* c3 */>> mMemo;
-};
+}; // Interpolator
} // namespace android
diff --git a/include/media/VolumeShaper.h b/include/media/VolumeShaper.h
index 1282124..e4c0b5b 100644
--- a/include/media/VolumeShaper.h
+++ b/include/media/VolumeShaper.h
@@ -51,31 +51,68 @@
class VolumeShaper {
public:
- using S = float;
- using T = float;
+ // S and T are like template typenames (matching the Interpolator<S, T>)
+ using S = float; // time type
+ using T = float; // volume type
- static const int kSystemIdMax = 16;
+// Curve and dimension information
+// TODO: member static const or constexpr float initialization not permitted in C++11
+#define MIN_CURVE_TIME 0.f // type S: start of VolumeShaper curve (normalized)
+#define MAX_CURVE_TIME 1.f // type S: end of VolumeShaper curve (normalized)
+#define MIN_LINEAR_VOLUME 0.f // type T: silence / mute audio
+#define MAX_LINEAR_VOLUME 1.f // type T: max volume, unity gain
+#define MAX_LOG_VOLUME 0.f // type T: max volume, unity gain in dBFS
- // VolumeShaper::Status is equivalent to status_t if negative
- // but if non-negative represents the id operated on.
- // It must be expressible as an int32_t for binder purposes.
+ /* kSystemVolumeShapersMax is the maximum number of system VolumeShapers.
+ * Each system VolumeShapers has a predefined Id, which ranges from 0
+ * to kSystemVolumeShapersMax - 1 and is unique for its usage.
+ *
+ * "1" is reserved for system ducking.
+ */
+ static const int kSystemVolumeShapersMax = 16;
+
+ /* kUserVolumeShapersMax is the maximum number of application
+ * VolumeShapers for a player/track. Application VolumeShapers are
+ * assigned on creation by the client, and have Ids ranging
+ * from kSystemVolumeShapersMax to INT32_MAX.
+ *
+ * The number of user/application volume shapers is independent to the
+ * system volume shapers. If an application tries to create more than
+ * kUserVolumeShapersMax to a player, then the apply() will fail.
+ * This prevents exhausting server side resources by a potentially malicious
+ * application.
+ */
+ static const int kUserVolumeShapersMax = 16;
+
+ /* VolumeShaper::Status is equivalent to status_t if negative
+ * but if non-negative represents the id operated on.
+ * It must be expressible as an int32_t for binder purposes.
+ */
using Status = status_t;
+ // Local definition for clamp as std::clamp is included in C++17 only.
+ // TODO: use the std::clamp version when Android build uses C++17.
+ template<typename R>
+ static constexpr const R &clamp(const R &v, const R &lo, const R &hi) {
+ return (v < lo) ? lo : (hi < v) ? hi : v;
+ }
+
+ /* VolumeShaper.Configuration derives from the Interpolator class and adds
+ * parameters relating to the volume shape.
+ *
+ * This parallels the Java implementation and the enums must match.
+ * See "frameworks/base/media/java/android/media/VolumeShaper.java" for
+ * details on the Java implementation.
+ */
class Configuration : public Interpolator<S, T>, public RefBase {
public:
- /* VolumeShaper.Configuration derives from the Interpolator class and adds
- * parameters relating to the volume shape.
- */
-
- // TODO document as per VolumeShaper.java flags.
-
- // must match with VolumeShaper.java in frameworks/base
+ // Must match with VolumeShaper.java in frameworks/base.
enum Type : int32_t {
TYPE_ID,
TYPE_SCALE,
};
- // must match with VolumeShaper.java in frameworks/base
+ // Must match with VolumeShaper.java in frameworks/base.
enum OptionFlag : int32_t {
OPTION_FLAG_NONE = 0,
OPTION_FLAG_VOLUME_IN_DBFS = (1 << 0),
@@ -84,11 +121,12 @@
OPTION_FLAG_ALL = (OPTION_FLAG_VOLUME_IN_DBFS | OPTION_FLAG_CLOCK_TIME),
};
- // bring to derived class; must match with VolumeShaper.java in frameworks/base
+ // Bring from base class; must match with VolumeShaper.java in frameworks/base.
using InterpolatorType = Interpolator<S, T>::InterpolatorType;
Configuration()
: Interpolator<S, T>()
+ , RefBase()
, mType(TYPE_SCALE)
, mOptionFlags(OPTION_FLAG_NONE)
, mDurationMs(1000.)
@@ -97,6 +135,7 @@
explicit Configuration(const Configuration &configuration)
: Interpolator<S, T>(*static_cast<const Interpolator<S, T> *>(&configuration))
+ , RefBase()
, mType(configuration.mType)
, mOptionFlags(configuration.mOptionFlags)
, mDurationMs(configuration.mDurationMs)
@@ -136,8 +175,13 @@
return mDurationMs;
}
- void setDurationMs(double durationMs) {
- mDurationMs = durationMs;
+ status_t setDurationMs(double durationMs) {
+ if (durationMs > 0.) {
+ mDurationMs = durationMs;
+ return NO_ERROR;
+ }
+ // zero, negative, or nan. These values not possible from Java.
+ return BAD_VALUE;
}
int32_t getId() const {
@@ -145,45 +189,46 @@
}
void setId(int32_t id) {
+ // We permit a negative id here (representing invalid).
mId = id;
}
+ /* Adjust the volume to be in linear range from MIN_LINEAR_VOLUME to MAX_LINEAR_VOLUME
+ * and compensate for log dbFS volume as needed.
+ */
T adjustVolume(T volume) const {
if ((getOptionFlags() & OPTION_FLAG_VOLUME_IN_DBFS) != 0) {
- const T out = powf(10.f, volume / 10.);
+ const T out = powf(10.f, volume / 10.f);
VS_LOG("in: %f out: %f", volume, out);
volume = out;
}
- // clamp
- if (volume < 0.f) {
- volume = 0.f;
- } else if (volume > 1.f) {
- volume = 1.f;
- }
- return volume;
+ return clamp(volume, MIN_LINEAR_VOLUME /* lo */, MAX_LINEAR_VOLUME /* hi */);
}
- status_t checkCurve() {
+ /* Check if the existing curve is valid.
+ */
+ status_t checkCurve() const {
if (mType == TYPE_ID) return NO_ERROR;
if (this->size() < 2) {
ALOGE("curve must have at least 2 points");
return BAD_VALUE;
}
- if (first().first != 0.f || last().first != 1.f) {
- ALOGE("curve must start at 0.f and end at 1.f");
+ if (first().first != MIN_CURVE_TIME || last().first != MAX_CURVE_TIME) {
+ ALOGE("curve must start at MIN_CURVE_TIME and end at MAX_CURVE_TIME");
return BAD_VALUE;
}
if ((getOptionFlags() & OPTION_FLAG_VOLUME_IN_DBFS) != 0) {
for (const auto &pt : *this) {
- if (!(pt.second <= 0.f) /* handle nan */) {
+ if (!(pt.second <= MAX_LOG_VOLUME) /* handle nan */) {
ALOGE("positive volume dbFS");
return BAD_VALUE;
}
}
} else {
for (const auto &pt : *this) {
- if (!(pt.second >= 0.f) || !(pt.second <= 1.f) /* handle nan */) {
- ALOGE("volume < 0.f or > 1.f");
+ if (!(pt.second >= MIN_LINEAR_VOLUME)
+ || !(pt.second <= MAX_LINEAR_VOLUME) /* handle nan */) {
+ ALOGE("volume < MIN_LINEAR_VOLUME or > MAX_LINEAR_VOLUME");
return BAD_VALUE;
}
}
@@ -191,19 +236,22 @@
return NO_ERROR;
}
+ /* Clamps the volume curve in the configuration to
+ * the valid range for log or linear scale.
+ */
void clampVolume() {
if ((mOptionFlags & OPTION_FLAG_VOLUME_IN_DBFS) != 0) {
for (auto it = this->begin(); it != this->end(); ++it) {
- if (!(it->second <= 0.f) /* handle nan */) {
- it->second = 0.f;
+ if (!(it->second <= MAX_LOG_VOLUME) /* handle nan */) {
+ it->second = MAX_LOG_VOLUME;
}
}
} else {
for (auto it = this->begin(); it != this->end(); ++it) {
- if (!(it->second >= 0.f) /* handle nan */) {
- it->second = 0.f;
- } else if (!(it->second <= 1.f)) {
- it->second = 1.f;
+ if (!(it->second >= MIN_LINEAR_VOLUME) /* handle nan */) {
+ it->second = MIN_LINEAR_VOLUME;
+ } else if (!(it->second <= MAX_LINEAR_VOLUME)) {
+ it->second = MAX_LINEAR_VOLUME;
}
}
}
@@ -224,8 +272,9 @@
if (endVolume == startVolume) {
// match with linear ramp
const T offset = volume - startVolume;
+ static const T scale = 1.f / (MAX_CURVE_TIME - MIN_CURVE_TIME); // nominally 1.f
for (auto it = this->begin(); it != this->end(); ++it) {
- it->second = it->second + offset * (1.f - it->first);
+ it->second = it->second + offset * (MAX_CURVE_TIME - it->first) * scale;
}
} else {
const T scale = (volume - endVolume) / (startVolume - endVolume);
@@ -262,32 +311,40 @@
?: checkCurve();
}
+ // Returns a string for debug printing.
std::string toString() const {
std::stringstream ss;
- ss << "mType: " << mType << std::endl;
- ss << "mId: " << mId << std::endl;
+ ss << "VolumeShaper::Configuration{mType=" << static_cast<int32_t>(mType);
+ ss << ", mId=" << mId;
if (mType != TYPE_ID) {
- ss << "mOptionFlags: " << mOptionFlags << std::endl;
- ss << "mDurationMs: " << mDurationMs << std::endl;
- ss << Interpolator<S, T>::toString().c_str();
+ ss << ", mOptionFlags=" << static_cast<int32_t>(mOptionFlags);
+ ss << ", mDurationMs=" << mDurationMs;
+ ss << ", " << Interpolator<S, T>::toString().c_str();
}
+ ss << "}";
return ss.str();
}
private:
- Type mType;
- int32_t mId;
- OptionFlag mOptionFlags;
- double mDurationMs;
+ Type mType; // type of configuration
+ 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.
}; // Configuration
- // must match with VolumeShaper.java in frameworks/base
- // TODO document per VolumeShaper.java flags.
+ /* VolumeShaper::Operation expresses an operation to perform on the
+ * configuration (either explicitly specified or an id).
+ *
+ * This parallels the Java implementation and the enums must match.
+ * See "frameworks/base/media/java/android/media/VolumeShaper.java" for
+ * details on the Java implementation.
+ */
class Operation : public RefBase {
public:
+ // Must match with VolumeShaper.java.
enum Flag : int32_t {
FLAG_NONE = 0,
- FLAG_REVERSE = (1 << 0),
+ FLAG_REVERSE = (1 << 0), // the absence of this indicates "play"
FLAG_TERMINATE = (1 << 1),
FLAG_JOIN = (1 << 2),
FLAG_DELAY = (1 << 3),
@@ -332,13 +389,29 @@
}
void setXOffset(S xOffset) {
- mXOffset = xOffset;
+ mXOffset = clamp(xOffset, MIN_CURVE_TIME /* lo */, MAX_CURVE_TIME /* hi */);
}
Flag getFlags() const {
return mFlags;
}
+ /* xOffset is the position on the volume curve and may go backwards
+ * if you are in reverse mode. This must be in the range from
+ * [MIN_CURVE_TIME, MAX_CURVE_TIME].
+ *
+ * normalizedTime always increases as time or framecount increases.
+ * normalizedTime is nominally from MIN_CURVE_TIME to MAX_CURVE_TIME when
+ * running through the curve, but could be outside this range afterwards.
+ * If you are reversing, this means the position on the curve, or xOffset,
+ * is computed as MAX_CURVE_TIME - normalizedTime, clamped to
+ * [MIN_CURVE_TIME, MAX_CURVE_TIME].
+ */
+ void setNormalizedTime(S normalizedTime) {
+ setXOffset((mFlags & FLAG_REVERSE) != 0
+ ? MAX_CURVE_TIME - normalizedTime : normalizedTime);
+ }
+
status_t setFlags(Flag flags) {
if ((flags & ~FLAG_ALL) != 0) {
ALOGE("flags has invalid bits: %#x", flags);
@@ -365,19 +438,26 @@
std::string toString() const {
std::stringstream ss;
- ss << "mFlags: " << mFlags << std::endl;
- ss << "mReplaceId: " << mReplaceId << std::endl;
- ss << "mXOffset: " << mXOffset << std::endl;
+ ss << "VolumeShaper::Operation{mFlags=" << static_cast<int32_t>(mFlags) ;
+ ss << ", mReplaceId=" << mReplaceId;
+ ss << ", mXOffset=" << mXOffset;
+ ss << "}";
return ss.str();
}
private:
- Flag mFlags;
- int32_t mReplaceId;
- S mXOffset;
+ 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)
}; // Operation
- // must match with VolumeShaper.java in frameworks/base
+ /* VolumeShaper.State is returned when requesting the last
+ * state of the VolumeShaper.
+ *
+ * This parallels the Java implementation.
+ * See "frameworks/base/media/java/android/media/VolumeShaper.java" for
+ * details on the Java implementation.
+ */
class State : public RefBase {
public:
State(T volume, S xOffset)
@@ -386,7 +466,7 @@
}
State()
- : State(-1.f, -1.f) { }
+ : State(NAN, NAN) { }
T getVolume() const {
return mVolume;
@@ -417,16 +497,18 @@
std::string toString() const {
std::stringstream ss;
- ss << "mVolume: " << mVolume << std::endl;
- ss << "mXOffset: " << mXOffset << std::endl;
+ ss << "VolumeShaper::State{mVolume=" << mVolume;
+ ss << ", mXOffset=" << mXOffset;
+ ss << "}";
return ss.str();
}
private:
- T mVolume;
- S mXOffset;
+ T mVolume; // linear volume in the range MIN_LINEAR_VOLUME to MAX_LINEAR_VOLUME
+ S mXOffset; // position on curve expressed from MIN_CURVE_TIME to MAX_CURVE_TIME
}; // State
+ // Internal helper class to do an affine transform for time and amplitude scaling.
template <typename R>
class Translate {
public:
@@ -457,8 +539,9 @@
std::string toString() const {
std::stringstream ss;
- ss << "mOffset: " << mOffset << std::endl;
- ss << "mScale: " << mScale << std::endl;
+ ss << "VolumeShaper::Translate{mOffset=" << mOffset;
+ ss << ", mScale=" << mScale;
+ ss << "}";
return ss.str();
}
@@ -482,9 +565,14 @@
return convertTimespecToUs(tv);
}
- // TODO: Since we pass configuration and operation as shared pointers
- // there is a potential risk that the caller may modify these after
- // delivery. Currently, we don't require copies made here.
+ /* Native implementation of VolumeShaper. This is NOT mirrored
+ * on the Java side, so we don't need to mimic Java side layout
+ * and data; furthermore, this isn't refcounted as a "RefBase" object.
+ *
+ * Since we pass configuration and operation as shared pointers (like
+ * Java) there is a potential risk that the caller may modify
+ * these after delivery.
+ */
VolumeShaper(
const sp<VolumeShaper::Configuration> &configuration,
const sp<VolumeShaper::Operation> &operation)
@@ -492,53 +580,58 @@
, mOperation(operation) // ditto
, mStartFrame(-1)
, mLastVolume(T(1))
- , mLastXOffset(0.f)
- , mDelayXOffset(std::numeric_limits<S>::quiet_NaN()) {
+ , mLastXOffset(MIN_CURVE_TIME)
+ , mDelayXOffset(MIN_CURVE_TIME) {
if (configuration.get() != nullptr
&& (getFlags() & VolumeShaper::Operation::FLAG_DELAY) == 0) {
mLastVolume = configuration->first().second;
}
}
- void updatePosition(int64_t startFrame, double sampleRate) {
- double scale = (mConfiguration->last().first - mConfiguration->first().first)
- / (mConfiguration->getDurationMs() * 0.001 * sampleRate);
- const double minScale = 1. / INT64_MAX;
- scale = std::max(scale, minScale);
- const S xOffset = std::isnan(mDelayXOffset) ? mConfiguration->first().first : mDelayXOffset;
- VS_LOG("update position: scale %lf frameCount:%lld, sampleRate:%lf, xOffset:%f",
- scale, (long long) startFrame, sampleRate, xOffset);
-
- mXTranslate.setOffset(startFrame - xOffset / scale);
- mXTranslate.setScale(scale);
- VS_LOG("translate: %s", mXTranslate.toString().c_str());
- }
-
// We allow a null operation here, though VolumeHandler always provides one.
VolumeShaper::Operation::Flag getFlags() const {
return mOperation == nullptr
- ? VolumeShaper::Operation::FLAG_NONE :mOperation->getFlags();
+ ? VolumeShaper::Operation::FLAG_NONE : mOperation->getFlags();
}
+ /* Returns the last volume and xoffset reported to the AudioFlinger.
+ * If the VolumeShaper has not been started, compute what the volume
+ * should be based on the initial offset specified.
+ */
sp<VolumeShaper::State> getState() const {
- return new VolumeShaper::State(mLastVolume, mLastXOffset);
+ if (!isStarted()) {
+ const T volume = computeVolumeFromXOffset(mDelayXOffset);
+ VS_LOG("delayed VolumeShaper, using cached offset:%f for volume:%f",
+ mDelayXOffset, volume);
+ return new VolumeShaper::State(volume, mDelayXOffset);
+ } else {
+ return new VolumeShaper::State(mLastVolume, mLastXOffset);
+ }
+ }
+
+ S getDelayXOffset() const {
+ return mDelayXOffset;
}
void setDelayXOffset(S xOffset) {
- mDelayXOffset = xOffset;
+ mDelayXOffset = clamp(xOffset, MIN_CURVE_TIME /* lo */, MAX_CURVE_TIME /* hi */);
}
bool isStarted() const {
return mStartFrame >= 0;
}
+ /* getVolume() updates the last volume/xoffset state so it is not
+ * const, even though logically it may be viewed as const.
+ */
std::pair<T /* volume */, bool /* active */> getVolume(
int64_t trackFrameCount, double trackSampleRate) {
if ((getFlags() & VolumeShaper::Operation::FLAG_DELAY) != 0) {
- VS_LOG("delayed VolumeShaper, ignoring");
- mLastVolume = T(1);
- mLastXOffset = 0.;
- return std::make_pair(T(1), false);
+ // We haven't had PLAY called yet, so just return the value
+ // as if PLAY were called just now.
+ VS_LOG("delayed VolumeShaper, using cached offset %f", mDelayXOffset);
+ const T volume = computeVolumeFromXOffset(mDelayXOffset);
+ return std::make_pair(volume, false);
}
const bool clockTime = (mConfiguration->getOptionFlags()
& VolumeShaper::Configuration::OPTION_FLAG_CLOCK_TIME) != 0;
@@ -546,84 +639,110 @@
const double sampleRate = clockTime ? 1000000 : trackSampleRate;
if (mStartFrame < 0) {
- updatePosition(frameCount, sampleRate);
+ updatePosition(frameCount, sampleRate, mDelayXOffset);
mStartFrame = frameCount;
}
VS_LOG("frameCount: %lld", (long long)frameCount);
- S x = mXTranslate((T)frameCount);
- VS_LOG("translation: %f", x);
+ const S x = mXTranslate((T)frameCount);
+ VS_LOG("translation to normalized time: %f", x);
- // handle reversal of position
- if (getFlags() & VolumeShaper::Operation::FLAG_REVERSE) {
- x = 1.f - x;
- VS_LOG("reversing to %f", x);
- if (x < mConfiguration->first().first) {
- mLastXOffset = 1.f;
- const T volume = mConfiguration->adjustVolume(
- mConfiguration->first().second); // persist last value
- VS_LOG("persisting volume %f", volume);
- mLastVolume = volume;
- return std::make_pair(volume, false);
- }
- if (x > mConfiguration->last().first) {
- mLastXOffset = 0.f;
- mLastVolume = 1.f;
- return std::make_pair(T(1), true); // too early
- }
- } else {
- if (x < mConfiguration->first().first) {
- mLastXOffset = 0.f;
- mLastVolume = 1.f;
- return std::make_pair(T(1), true); // too early
- }
- if (x > mConfiguration->last().first) {
- mLastXOffset = 1.f;
- const T volume = mConfiguration->adjustVolume(
- mConfiguration->last().second); // persist last value
- VS_LOG("persisting volume %f", volume);
- mLastVolume = volume;
- return std::make_pair(volume, false);
- }
- }
- mLastXOffset = x;
- // x contains the location on the volume curve to use.
- const T unscaledVolume = mConfiguration->findY(x);
- const T volume = mConfiguration->adjustVolume(unscaledVolume); // handle log scale
- VS_LOG("volume: %f unscaled: %f", volume, unscaledVolume);
- mLastVolume = volume;
- return std::make_pair(volume, true);
+ std::tuple<T /* volume */, S /* position */, bool /* active */> vt =
+ computeStateFromNormalizedTime(x);
+
+ mLastVolume = std::get<0>(vt);
+ mLastXOffset = std::get<1>(vt);
+ const bool active = std::get<2>(vt);
+ VS_LOG("rescaled time:%f volume:%f xOffset:%f active:%s",
+ x, mLastVolume, mLastXOffset, active ? "true" : "false");
+ return std::make_pair(mLastVolume, active);
}
std::string toString() const {
std::stringstream ss;
- ss << "StartFrame: " << mStartFrame << std::endl;
- ss << mXTranslate.toString().c_str();
- if (mConfiguration.get() == nullptr) {
- ss << "VolumeShaper::Configuration: nullptr" << std::endl;
- } else {
- ss << "VolumeShaper::Configuration:" << std::endl;
- ss << mConfiguration->toString().c_str();
- }
- if (mOperation.get() == nullptr) {
- ss << "VolumeShaper::Operation: nullptr" << std::endl;
- } else {
- ss << "VolumeShaper::Operation:" << std::endl;
- ss << mOperation->toString().c_str();
- }
+ ss << "VolumeShaper{mStartFrame=" << mStartFrame;
+ ss << ", mXTranslate=" << mXTranslate.toString().c_str();
+ ss << ", mConfiguration=" <<
+ (mConfiguration.get() == nullptr
+ ? "nullptr" : mConfiguration->toString().c_str());
+ ss << ", mOperation=" <<
+ (mOperation.get() == nullptr
+ ? "nullptr" : mOperation->toString().c_str());
+ ss << "}";
return ss.str();
}
- Translate<S> mXTranslate; // x axis translation from frames (in usec for clock time)
+ Translate<S> mXTranslate; // translation from frames (usec for clock time) to normalized time.
sp<VolumeShaper::Configuration> mConfiguration;
sp<VolumeShaper::Operation> mOperation;
+
+private:
int64_t mStartFrame; // starting frame, non-negative when started (in usec for clock time)
T mLastVolume; // last computed interpolated volume (y-axis)
S mLastXOffset; // last computed interpolated xOffset/time (x-axis)
- S mDelayXOffset; // delay xOffset on first volumeshaper start.
+ S mDelayXOffset; // xOffset to use for first invocation of VolumeShaper.
+
+ // Called internally to adjust mXTranslate for first time start.
+ void updatePosition(int64_t startFrame, double sampleRate, S xOffset) {
+ double scale = (mConfiguration->last().first - mConfiguration->first().first)
+ / (mConfiguration->getDurationMs() * 0.001 * sampleRate);
+ const double minScale = 1. / static_cast<double>(INT64_MAX);
+ scale = std::max(scale, minScale);
+ VS_LOG("update position: scale %lf frameCount:%lld, sampleRate:%lf, xOffset:%f",
+ scale, (long long) startFrame, sampleRate, xOffset);
+
+ S normalizedTime = (getFlags() & VolumeShaper::Operation::FLAG_REVERSE) != 0 ?
+ MAX_CURVE_TIME - xOffset : xOffset;
+ mXTranslate.setOffset(static_cast<float>(static_cast<double>(startFrame)
+ - static_cast<double>(normalizedTime) / scale));
+ mXTranslate.setScale(static_cast<float>(scale));
+ VS_LOG("translate: %s", mXTranslate.toString().c_str());
+ }
+
+ T computeVolumeFromXOffset(S xOffset) const {
+ const T unscaledVolume = mConfiguration->findY(xOffset);
+ const T volume = mConfiguration->adjustVolume(unscaledVolume); // handle log scale
+ VS_LOG("computeVolumeFromXOffset %f -> %f -> %f", xOffset, unscaledVolume, volume);
+ return volume;
+ }
+
+ std::tuple<T /* volume */, S /* position */, bool /* active */>
+ computeStateFromNormalizedTime(S x) const {
+ bool active = true;
+ // handle reversal of position
+ if (getFlags() & VolumeShaper::Operation::FLAG_REVERSE) {
+ x = MAX_CURVE_TIME - x;
+ VS_LOG("reversing to %f", x);
+ if (x < MIN_CURVE_TIME) {
+ x = MIN_CURVE_TIME;
+ active = false; // at the end
+ } else if (x > MAX_CURVE_TIME) {
+ x = MAX_CURVE_TIME; //early
+ }
+ } else {
+ if (x < MIN_CURVE_TIME) {
+ x = MIN_CURVE_TIME; // early
+ } else if (x > MAX_CURVE_TIME) {
+ x = MAX_CURVE_TIME;
+ active = false; // at end
+ }
+ }
+ const S xOffset = x;
+ const T volume = computeVolumeFromXOffset(xOffset);
+ return std::make_tuple(volume, xOffset, active);
+ }
}; // VolumeShaper
-// VolumeHandler combines the volume factors of multiple VolumeShapers and handles
-// multiple thread access by synchronizing all public methods.
+/* VolumeHandler combines the volume factors of multiple VolumeShapers associated
+ * with a player. It is thread safe by synchronizing all public methods.
+ *
+ * This is a native-only implementation.
+ *
+ * The server side VolumeHandler is used to maintain a list of volume handlers,
+ * keep state, and obtain volume.
+ *
+ * The client side VolumeHandler is used to maintain a list of volume handlers,
+ * keep some partial state, and restore if the server dies.
+ */
class VolumeHandler : public RefBase {
public:
using S = float;
@@ -637,13 +756,15 @@
explicit VolumeHandler(uint32_t sampleRate)
: mSampleRate((double)sampleRate)
, mLastFrame(0)
- , mVolumeShaperIdCounter(VolumeShaper::kSystemIdMax)
+ , mVolumeShaperIdCounter(VolumeShaper::kSystemVolumeShapersMax)
, mLastVolume(1.f, false) {
}
VolumeShaper::Status applyVolumeShaper(
const sp<VolumeShaper::Configuration> &configuration,
- const sp<VolumeShaper::Operation> &operation) {
+ const sp<VolumeShaper::Operation> &operation_in) {
+ // make a local copy of operation, as we modify it.
+ sp<VolumeShaper::Operation> operation(new VolumeShaper::Operation(operation_in));
VS_LOG("applyVolumeShaper:configuration: %s", configuration->toString().c_str());
VS_LOG("applyVolumeShaper:operation: %s", operation->toString().c_str());
AutoMutex _l(mLock);
@@ -666,14 +787,16 @@
case VolumeShaper::Configuration::TYPE_SCALE: {
const int replaceId = operation->getReplaceId();
if (replaceId >= 0) {
+ VS_LOG("replacing %d", replaceId);
auto replaceIt = findId_l(replaceId);
if (replaceIt == mVolumeShapers.end()) {
ALOGW("cannot find replace id: %d", replaceId);
} else {
- if ((replaceIt->getFlags() & VolumeShaper::Operation::FLAG_JOIN) != 0) {
+ if ((operation->getFlags() & VolumeShaper::Operation::FLAG_JOIN) != 0) {
// For join, we scale the start volume of the current configuration
// to match the last-used volume of the replacing VolumeShaper.
auto state = replaceIt->getState();
+ ALOGD("join: state:%s", state->toString().c_str());
if (state->getXOffset() >= 0) { // valid
const T volume = state->getVolume();
ALOGD("join: scaling start volume to %f", volume);
@@ -695,8 +818,22 @@
ALOGW("duplicate id, removing old %d", id);
(void)mVolumeShapers.erase(oldIt);
}
- // create new VolumeShaper
- mVolumeShapers.emplace_back(configuration, operation);
+
+ /* Check if too many application VolumeShapers (with id >= kSystemVolumeShapersMax).
+ * We check on the server side to ensure synchronization and robustness.
+ *
+ * This shouldn't fail on a replace command unless the replaced id is
+ * already invalid (which *should* be checked in the Java layer).
+ */
+ if (id >= VolumeShaper::kSystemVolumeShapersMax
+ && numberOfUserVolumeShapers_l() >= VolumeShaper::kUserVolumeShapersMax) {
+ ALOGW("Too many app VolumeShapers, cannot add to VolumeHandler");
+ return VolumeShaper::Status(INVALID_OPERATION);
+ }
+
+ // create new VolumeShaper with default behavior.
+ mVolumeShapers.emplace_back(configuration, new VolumeShaper::Operation());
+ VS_LOG("after adding, number of volumeShapers:%zu", mVolumeShapers.size());
}
// fall through to handle the operation
HANDLE_TYPE_ID:
@@ -707,7 +844,7 @@
VS_LOG("couldn't find id: %d", id);
return VolumeShaper::Status(INVALID_OPERATION);
}
- if ((it->getFlags() & VolumeShaper::Operation::FLAG_TERMINATE) != 0) {
+ if ((operation->getFlags() & VolumeShaper::Operation::FLAG_TERMINATE) != 0) {
VS_LOG("terminate id: %d", id);
mVolumeShapers.erase(it);
break;
@@ -716,29 +853,37 @@
& VolumeShaper::Configuration::OPTION_FLAG_CLOCK_TIME) != 0;
if ((it->getFlags() & VolumeShaper::Operation::FLAG_REVERSE) !=
(operation->getFlags() & VolumeShaper::Operation::FLAG_REVERSE)) {
- const int64_t frameCount = clockTime ? VolumeShaper::getNowUs() : mLastFrame;
- const S x = it->mXTranslate((T)frameCount);
- VS_LOG("reverse translation: %f", x);
- // reflect position
- S target = 1.f - x;
- if (target < it->mConfiguration->first().first) {
- VS_LOG("clamp to start - begin immediately");
- target = 0.;
+ if (it->isStarted()) {
+ const int64_t frameCount = clockTime ? VolumeShaper::getNowUs() : mLastFrame;
+ const S x = it->mXTranslate((T)frameCount);
+ VS_LOG("reverse normalizedTime: %f", x);
+ // reflect position
+ S target = MAX_CURVE_TIME - x;
+ if (target < MIN_CURVE_TIME) {
+ VS_LOG("clamp to start - begin immediately");
+ target = MIN_CURVE_TIME;
+ }
+ VS_LOG("reverse normalizedTime target: %f", target);
+ it->mXTranslate.setOffset(it->mXTranslate.getOffset()
+ + (x - target) / it->mXTranslate.getScale());
}
- VS_LOG("target reverse: %f", target);
- it->mXTranslate.setOffset(it->mXTranslate.getOffset()
- + (x - target) / it->mXTranslate.getScale());
+ // if not started, the delay offset doesn't change.
}
const S xOffset = operation->getXOffset();
if (!std::isnan(xOffset)) {
- const int64_t frameCount = clockTime ? VolumeShaper::getNowUs() : mLastFrame;
- const S x = it->mXTranslate((T)frameCount);
- VS_LOG("xOffset translation: %f", x);
- const S target = xOffset; // offset
- VS_LOG("xOffset target x offset: %f", target);
- it->mXTranslate.setOffset(it->mXTranslate.getOffset()
- + (x - target) / it->mXTranslate.getScale());
- it->setDelayXOffset(xOffset);
+ if (it->isStarted()) {
+ const int64_t frameCount = clockTime ? VolumeShaper::getNowUs() : mLastFrame;
+ const S x = it->mXTranslate((T)frameCount);
+ VS_LOG("normalizedTime translation: %f", x);
+ const S target =
+ (operation->getFlags() & VolumeShaper::Operation::FLAG_REVERSE) != 0 ?
+ MAX_CURVE_TIME - xOffset : xOffset;
+ VS_LOG("normalizedTime target x offset: %f", target);
+ it->mXTranslate.setOffset(it->mXTranslate.getOffset()
+ + (x - target) / it->mXTranslate.getScale());
+ } else {
+ it->setDelayXOffset(xOffset);
+ }
}
it->mOperation = operation; // replace the operation
} break;
@@ -756,28 +901,31 @@
return it->getState();
}
- // getVolume() is not const, as it updates internal state.
- // Once called, any VolumeShapers not already started begin running.
+ /* getVolume() is not const, as it updates internal state.
+ * Once called, any VolumeShapers not already started begin running.
+ */
std::pair<T /* volume */, bool /* active */> getVolume(int64_t trackFrameCount) {
AutoMutex _l(mLock);
mLastFrame = trackFrameCount;
T volume(1);
size_t activeCount = 0;
for (auto it = mVolumeShapers.begin(); it != mVolumeShapers.end();) {
- std::pair<T, bool> shaperVolume =
+ const std::pair<T, bool> shaperVolume =
it->getVolume(trackFrameCount, mSampleRate);
volume *= shaperVolume.first;
activeCount += shaperVolume.second;
++it;
}
mLastVolume = std::make_pair(volume, activeCount != 0);
+ VS_LOG("getVolume: <%f, %s>", mLastVolume.first, mLastVolume.second ? "true" : "false");
return mLastVolume;
}
- // Used by a client side VolumeHandler to ensure all the VolumeShapers
- // indicate that they have been started. Upon a change in audioserver
- // output sink, this information is used for restoration of the server side
- // VolumeHandler.
+ /* Used by a client side VolumeHandler to ensure all the VolumeShapers
+ * indicate that they have been started. Upon a change in audioserver
+ * output sink, this information is used for restoration of the server side
+ * VolumeHandler.
+ */
void setStarted() {
(void)getVolume(mLastFrame); // getVolume() will start the individual VolumeShapers.
}
@@ -790,11 +938,19 @@
std::string toString() const {
AutoMutex _l(mLock);
std::stringstream ss;
- ss << "mSampleRate: " << mSampleRate << std::endl;
- ss << "mLastFrame: " << mLastFrame << std::endl;
+ ss << "VolumeHandler{mSampleRate=" << mSampleRate;
+ ss << ", mLastFrame=" << mLastFrame;
+ ss << ", mVolumeShapers={";
+ bool first = true;
for (const auto &shaper : mVolumeShapers) {
+ if (first) {
+ first = false;
+ } else {
+ ss << ", ";
+ }
ss << shaper.toString().c_str();
}
+ ss << "}}";
return ss.str();
}
@@ -814,8 +970,9 @@
// keep mVolumeShaperIdCounter as is.
}
- // Sets the configuration id if necessary - This is based on the counter
- // internal to the VolumeHandler.
+ /* Sets the configuration id if necessary - This is based on the counter
+ * internal to the VolumeHandler.
+ */
void setIdIfNecessary(const sp<VolumeShaper::Configuration> &configuration) {
if (configuration->getType() == VolumeShaper::Configuration::TYPE_SCALE) {
const int id = configuration->getId();
@@ -824,7 +981,7 @@
AutoMutex _l(mLock);
while (true) {
if (mVolumeShaperIdCounter == INT32_MAX) {
- mVolumeShaperIdCounter = VolumeShaper::kSystemIdMax;
+ mVolumeShaperIdCounter = VolumeShaper::kSystemVolumeShapersMax;
} else {
++mVolumeShaperIdCounter;
}
@@ -850,6 +1007,14 @@
return it;
}
+ size_t numberOfUserVolumeShapers_l() const {
+ size_t count = 0;
+ for (const auto &shaper : mVolumeShapers) {
+ count += (shaper.mConfiguration->getId() >= VolumeShaper::kSystemVolumeShapersMax);
+ }
+ return count;
+ }
+
mutable Mutex mLock;
double mSampleRate; // in samples (frames) per second
int64_t mLastFrame; // logging purpose only, 0 on start
diff --git a/media/libaaudio/examples/input_monitor/src/input_monitor_callback.cpp b/media/libaaudio/examples/input_monitor/src/input_monitor_callback.cpp
index b482e93..7c34252 100644
--- a/media/libaaudio/examples/input_monitor/src/input_monitor_callback.cpp
+++ b/media/libaaudio/examples/input_monitor/src/input_monitor_callback.cpp
@@ -24,7 +24,7 @@
#include <time.h>
#include <aaudio/AAudio.h>
-#define NUM_SECONDS 10
+#define NUM_SECONDS 5
#define NANOS_PER_MICROSECOND ((int64_t)1000)
#define NANOS_PER_MILLISECOND (NANOS_PER_MICROSECOND * 1000)
#define NANOS_PER_SECOND (NANOS_PER_MILLISECOND * 1000)
@@ -33,13 +33,13 @@
#define SHARING_MODE AAUDIO_SHARING_MODE_SHARED
/**
- * Simple wrapper for AAudio that opens a default stream and then calls
- * a callback function to fill the output buffers.
+ * Simple wrapper for AAudio that opens an input stream and then calls
+ * a callback function to process the input data.
*/
-class SimpleAAudioPlayer {
+class SimpleAAudioRecorder {
public:
- SimpleAAudioPlayer() {}
- ~SimpleAAudioPlayer() {
+ SimpleAAudioRecorder() {}
+ ~SimpleAAudioRecorder() {
close();
};
@@ -71,6 +71,15 @@
}
return AAudioStream_getSamplesPerFrame(mStream);;
}
+ /**
+ * Only call this after open() has been called.
+ */
+ int64_t getFramesRead() {
+ if (mStream == nullptr) {
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
+ return AAudioStream_getFramesRead(mStream);;
+ }
/**
* Open a stream
@@ -85,7 +94,7 @@
AAudioStreamBuilder_setDirection(mBuilder, AAUDIO_DIRECTION_INPUT);
AAudioStreamBuilder_setSharingMode(mBuilder, mRequestedSharingMode);
AAudioStreamBuilder_setDataCallback(mBuilder, proc, userContext);
- AAudioStreamBuilder_setFormat(mBuilder, AAUDIO_FORMAT_PCM_I16);
+ AAudioStreamBuilder_setFormat(mBuilder, AAUDIO_FORMAT_PCM_FLOAT);
// Open an AAudioStream using the Builder.
result = AAudioStreamBuilder_openStream(mBuilder, &mStream);
@@ -121,11 +130,10 @@
}
// Write zero data to fill up the buffer and prevent underruns.
- // Assume format is PCM_I16. TODO use floats.
aaudio_result_t prime() {
int32_t samplesPerFrame = AAudioStream_getSamplesPerFrame(mStream);
const int numFrames = 32; // arbitrary
- int16_t zeros[numFrames * samplesPerFrame];
+ float zeros[numFrames * samplesPerFrame];
memset(zeros, 0, sizeof(zeros));
aaudio_result_t result = numFrames;
while (result == numFrames) {
@@ -151,8 +159,16 @@
fprintf(stderr, "ERROR - AAudioStream_requestStop() returned %d %s\n",
result, AAudio_convertResultToText(result));
}
- int32_t xRunCount = AAudioStream_getXRunCount(mStream);
- printf("AAudioStream_getXRunCount %d\n", xRunCount);
+ return result;
+ }
+
+ // Pause the stream. AAudio will stop calling your callback function.
+ aaudio_result_t pause() {
+ aaudio_result_t result = AAudioStream_requestPause(mStream);
+ if (result != AAUDIO_OK) {
+ fprintf(stderr, "ERROR - AAudioStream_requestPause() returned %d %s\n",
+ result, AAudio_convertResultToText(result));
+ }
return result;
}
@@ -227,7 +243,7 @@
int main(int argc, char **argv)
{
(void)argc; // unused
- SimpleAAudioPlayer player;
+ SimpleAAudioRecorder recorder;
PeakTrackerData_t myData = {0.0};
aaudio_result_t result;
const int displayRateHz = 20; // arbitrary
@@ -238,37 +254,60 @@
setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
printf("%s - Display audio input using an AAudio callback\n", argv[0]);
- player.setSharingMode(SHARING_MODE);
+ recorder.setSharingMode(SHARING_MODE);
- result = player.open(MyDataCallbackProc, &myData);
+ result = recorder.open(MyDataCallbackProc, &myData);
if (result != AAUDIO_OK) {
- fprintf(stderr, "ERROR - player.open() returned %d\n", result);
+ fprintf(stderr, "ERROR - recorder.open() returned %d\n", result);
goto error;
}
- printf("player.getFramesPerSecond() = %d\n", player.getFramesPerSecond());
- printf("player.getSamplesPerFrame() = %d\n", player.getSamplesPerFrame());
+ printf("recorder.getFramesPerSecond() = %d\n", recorder.getFramesPerSecond());
+ printf("recorder.getSamplesPerFrame() = %d\n", recorder.getSamplesPerFrame());
- result = player.start();
+ result = recorder.start();
if (result != AAUDIO_OK) {
- fprintf(stderr, "ERROR - player.start() returned %d\n", result);
+ fprintf(stderr, "ERROR - recorder.start() returned %d\n", result);
goto error;
}
- printf("Sleep for %d seconds while audio plays in a callback thread.\n", NUM_SECONDS);
- for (int i = 0; i < loopsNeeded; i++)
+ printf("Sleep for %d seconds while audio record in a callback thread.\n", NUM_SECONDS);
+ for (int i = 0; i < loopsNeeded; i++)
{
const struct timespec request = { .tv_sec = 0,
.tv_nsec = NANOS_PER_SECOND / displayRateHz };
(void) clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, &request, NULL /*remain*/);
+ printf("%08d: ", (int)recorder.getFramesRead());
+ displayPeakLevel(myData.peakLevel);
+ }
+ printf("Woke up. Stop for a moment.\n");
+
+ result = recorder.stop();
+ if (result != AAUDIO_OK) {
+ goto error;
+ }
+ sleep(1);
+ result = recorder.start();
+ if (result != AAUDIO_OK) {
+ fprintf(stderr, "ERROR - recorder.start() returned %d\n", result);
+ goto error;
+ }
+
+ printf("Sleep for %d seconds while audio records in a callback thread.\n", NUM_SECONDS);
+ for (int i = 0; i < loopsNeeded; i++)
+ {
+ const struct timespec request = { .tv_sec = 0,
+ .tv_nsec = NANOS_PER_SECOND / displayRateHz };
+ (void) clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, &request, NULL /*remain*/);
+ printf("%08d: ", (int)recorder.getFramesRead());
displayPeakLevel(myData.peakLevel);
}
printf("Woke up now.\n");
- result = player.stop();
+ result = recorder.stop();
if (result != AAUDIO_OK) {
goto error;
}
- result = player.close();
+ result = recorder.close();
if (result != AAUDIO_OK) {
goto error;
}
@@ -276,7 +315,7 @@
printf("SUCCESS\n");
return EXIT_SUCCESS;
error:
- player.close();
+ recorder.close();
printf("exiting - AAudio result = %d = %s\n", result, AAudio_convertResultToText(result));
return EXIT_FAILURE;
}
diff --git a/media/libaaudio/include/aaudio/AAudio.h b/media/libaaudio/include/aaudio/AAudio.h
index 8498950..c21caa4 100644
--- a/media/libaaudio/include/aaudio/AAudio.h
+++ b/media/libaaudio/include/aaudio/AAudio.h
@@ -507,6 +507,9 @@
* Use AAudioStream_Start() to resume playback after a pause.
* After this call the state will be in AAUDIO_STREAM_STATE_PAUSING or AAUDIO_STREAM_STATE_PAUSED.
*
+ * This will return AAUDIO_ERROR_UNIMPLEMENTED for input streams.
+ * For input streams use AAudioStream_requestStop().
+ *
* @param stream reference provided by AAudioStreamBuilder_openStream()
* @return AAUDIO_OK or a negative error.
*/
@@ -519,6 +522,8 @@
* Frame counters are not reset by a flush. They may be advanced.
* After this call the state will be in AAUDIO_STREAM_STATE_FLUSHING or AAUDIO_STREAM_STATE_FLUSHED.
*
+ * This will return AAUDIO_ERROR_UNIMPLEMENTED for input streams.
+ *
* @param stream reference provided by AAudioStreamBuilder_openStream()
* @return AAUDIO_OK or a negative error.
*/
@@ -704,6 +709,9 @@
*
* An underrun or overrun can cause an audible "pop" or "glitch".
*
+ * Note that some INPUT devices may not support this function.
+ * In that case a 0 will always be returned.
+ *
* @param stream reference provided by AAudioStreamBuilder_openStream()
* @return the underrun or overrun count
*/
diff --git a/media/libaaudio/src/binding/AAudioBinderClient.h b/media/libaaudio/src/binding/AAudioBinderClient.h
index f7f2808..ca2da29 100644
--- a/media/libaaudio/src/binding/AAudioBinderClient.h
+++ b/media/libaaudio/src/binding/AAudioBinderClient.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef AAUDIO_AAUDIO_BINDER_CLIENT_H
-#define AAUDIO_AAUDIO_BINDER_CLIENT_H
+#ifndef ANDROID_AAUDIO_AAUDIO_BINDER_CLIENT_H
+#define ANDROID_AAUDIO_AAUDIO_BINDER_CLIENT_H
#include <aaudio/AAudio.h>
#include "AAudioServiceDefinitions.h"
@@ -91,4 +91,4 @@
} /* namespace aaudio */
-#endif //AAUDIO_AAUDIO_BINDER_CLIENT_H
+#endif //ANDROID_AAUDIO_AAUDIO_BINDER_CLIENT_H
diff --git a/media/libaaudio/src/binding/AAudioServiceInterface.h b/media/libaaudio/src/binding/AAudioServiceInterface.h
index b565499..824e5bc 100644
--- a/media/libaaudio/src/binding/AAudioServiceInterface.h
+++ b/media/libaaudio/src/binding/AAudioServiceInterface.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef AAUDIO_BINDING_AAUDIO_SERVICE_INTERFACE_H
-#define AAUDIO_BINDING_AAUDIO_SERVICE_INTERFACE_H
+#ifndef ANDROID_AAUDIO_BINDING_AAUDIO_SERVICE_INTERFACE_H
+#define ANDROID_AAUDIO_BINDING_AAUDIO_SERVICE_INTERFACE_H
#include "binding/AAudioServiceDefinitions.h"
#include "binding/AAudioStreamRequest.h"
@@ -87,4 +87,4 @@
} /* namespace aaudio */
-#endif //AAUDIO_BINDING_AAUDIO_SERVICE_INTERFACE_H
+#endif //ANDROID_AAUDIO_BINDING_AAUDIO_SERVICE_INTERFACE_H
diff --git a/media/libaaudio/src/binding/AAudioServiceMessage.h b/media/libaaudio/src/binding/AAudioServiceMessage.h
index d75aa32..b4377fb 100644
--- a/media/libaaudio/src/binding/AAudioServiceMessage.h
+++ b/media/libaaudio/src/binding/AAudioServiceMessage.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef AAUDIO_AAUDIO_SERVICE_MESSAGE_H
-#define AAUDIO_AAUDIO_SERVICE_MESSAGE_H
+#ifndef ANDROID_AAUDIO_AAUDIO_SERVICE_MESSAGE_H
+#define ANDROID_AAUDIO_AAUDIO_SERVICE_MESSAGE_H
#include <stdint.h>
@@ -64,4 +64,4 @@
} /* namespace aaudio */
-#endif //AAUDIO_AAUDIO_SERVICE_MESSAGE_H
+#endif //ANDROID_AAUDIO_AAUDIO_SERVICE_MESSAGE_H
diff --git a/media/libaaudio/src/binding/AAudioStreamConfiguration.h b/media/libaaudio/src/binding/AAudioStreamConfiguration.h
index 78c4983..b1e4a7d 100644
--- a/media/libaaudio/src/binding/AAudioStreamConfiguration.h
+++ b/media/libaaudio/src/binding/AAudioStreamConfiguration.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef BINDING_AAUDIO_STREAM_CONFIGURATION_H
-#define BINDING_AAUDIO_STREAM_CONFIGURATION_H
+#ifndef ANDROID_BINDING_AAUDIO_STREAM_CONFIGURATION_H
+#define ANDROID_BINDING_AAUDIO_STREAM_CONFIGURATION_H
#include <stdint.h>
@@ -101,4 +101,4 @@
} /* namespace aaudio */
-#endif //BINDING_AAUDIO_STREAM_CONFIGURATION_H
+#endif //ANDROID_BINDING_AAUDIO_STREAM_CONFIGURATION_H
diff --git a/media/libaaudio/src/binding/AAudioStreamRequest.h b/media/libaaudio/src/binding/AAudioStreamRequest.h
index d4bfbe1..77138da 100644
--- a/media/libaaudio/src/binding/AAudioStreamRequest.h
+++ b/media/libaaudio/src/binding/AAudioStreamRequest.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef BINDING_AAUDIO_STREAM_REQUEST_H
-#define BINDING_AAUDIO_STREAM_REQUEST_H
+#ifndef ANDROID_BINDING_AAUDIO_STREAM_REQUEST_H
+#define ANDROID_BINDING_AAUDIO_STREAM_REQUEST_H
#include <stdint.h>
@@ -95,4 +95,4 @@
} /* namespace aaudio */
-#endif //BINDING_AAUDIO_STREAM_REQUEST_H
+#endif //ANDROID_BINDING_AAUDIO_STREAM_REQUEST_H
diff --git a/media/libaaudio/src/binding/AudioEndpointParcelable.h b/media/libaaudio/src/binding/AudioEndpointParcelable.h
index 4a1cb72..993075c7 100644
--- a/media/libaaudio/src/binding/AudioEndpointParcelable.h
+++ b/media/libaaudio/src/binding/AudioEndpointParcelable.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef BINDING_AUDIOENDPOINTPARCELABLE_H
-#define BINDING_AUDIOENDPOINTPARCELABLE_H
+#ifndef ANDROID_BINDING_AUDIO_ENDPOINT_PARCELABLE_H
+#define ANDROID_BINDING_AUDIO_ENDPOINT_PARCELABLE_H
#include <stdint.h>
@@ -75,4 +75,4 @@
} /* namespace aaudio */
-#endif //BINDING_AUDIOENDPOINTPARCELABLE_H
+#endif //ANDROID_BINDING_AUDIO_ENDPOINT_PARCELABLE_H
diff --git a/media/libaaudio/src/binding/IAAudioService.h b/media/libaaudio/src/binding/IAAudioService.h
index 2cee651..44a5e12 100644
--- a/media/libaaudio/src/binding/IAAudioService.h
+++ b/media/libaaudio/src/binding/IAAudioService.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef BINDING_IAAUDIOSERVICE_H
-#define BINDING_IAAUDIOSERVICE_H
+#ifndef ANDROID_AAUDIO_IAAUDIO_SERVICE_H
+#define ANDROID_AAUDIO_IAAUDIO_SERVICE_H
#include <stdint.h>
#include <utils/RefBase.h>
@@ -103,4 +103,4 @@
} /* namespace android */
-#endif //BINDING_IAAUDIOSERVICE_H
+#endif //ANDROID_AAUDIO_IAAUDIO_SERVICE_H
diff --git a/media/libaaudio/src/binding/RingBufferParcelable.cpp b/media/libaaudio/src/binding/RingBufferParcelable.cpp
index 05451f9..6b74b21 100644
--- a/media/libaaudio/src/binding/RingBufferParcelable.cpp
+++ b/media/libaaudio/src/binding/RingBufferParcelable.cpp
@@ -79,29 +79,49 @@
* The read and write must be symmetric.
*/
status_t RingBufferParcelable::writeToParcel(Parcel* parcel) const {
- parcel->writeInt32(mCapacityInFrames);
+ status_t status = parcel->writeInt32(mCapacityInFrames);
+ if (status != NO_ERROR) goto error;
if (mCapacityInFrames > 0) {
- parcel->writeInt32(mBytesPerFrame);
- parcel->writeInt32(mFramesPerBurst);
- parcel->writeInt32(mFlags);
- mReadCounterParcelable.writeToParcel(parcel);
- mWriteCounterParcelable.writeToParcel(parcel);
- mDataParcelable.writeToParcel(parcel);
+ status = parcel->writeInt32(mBytesPerFrame);
+ if (status != NO_ERROR) goto error;
+ status = parcel->writeInt32(mFramesPerBurst);
+ if (status != NO_ERROR) goto error;
+ status = parcel->writeInt32(mFlags);
+ if (status != NO_ERROR) goto error;
+ status = mReadCounterParcelable.writeToParcel(parcel);
+ if (status != NO_ERROR) goto error;
+ status = mWriteCounterParcelable.writeToParcel(parcel);
+ if (status != NO_ERROR) goto error;
+ status = mDataParcelable.writeToParcel(parcel);
+ if (status != NO_ERROR) goto error;
}
- return NO_ERROR; // TODO check for errors above
+ return NO_ERROR;
+error:
+ ALOGE("RingBufferParcelable::writeToParcel() error = %d", status);
+ return status;
}
status_t RingBufferParcelable::readFromParcel(const Parcel* parcel) {
- parcel->readInt32(&mCapacityInFrames);
+ status_t status = parcel->readInt32(&mCapacityInFrames);
+ if (status != NO_ERROR) goto error;
if (mCapacityInFrames > 0) {
- parcel->readInt32(&mBytesPerFrame);
- parcel->readInt32(&mFramesPerBurst);
- parcel->readInt32((int32_t *)&mFlags);
- mReadCounterParcelable.readFromParcel(parcel);
- mWriteCounterParcelable.readFromParcel(parcel);
- mDataParcelable.readFromParcel(parcel);
+ status = parcel->readInt32(&mBytesPerFrame);
+ if (status != NO_ERROR) goto error;
+ status = parcel->readInt32(&mFramesPerBurst);
+ if (status != NO_ERROR) goto error;
+ status = parcel->readInt32((int32_t *)&mFlags);
+ if (status != NO_ERROR) goto error;
+ status = mReadCounterParcelable.readFromParcel(parcel);
+ if (status != NO_ERROR) goto error;
+ status = mWriteCounterParcelable.readFromParcel(parcel);
+ if (status != NO_ERROR) goto error;
+ status = mDataParcelable.readFromParcel(parcel);
+ if (status != NO_ERROR) goto error;
}
- return NO_ERROR; // TODO check for errors above
+ return NO_ERROR;
+error:
+ ALOGE("RingBufferParcelable::readFromParcel() error = %d", status);
+ return status;
}
aaudio_result_t RingBufferParcelable::resolve(SharedMemoryParcelable *memoryParcels, RingBufferDescriptor *descriptor) {
@@ -141,7 +161,7 @@
ALOGE("RingBufferParcelable invalid mBytesPerFrame = %d", mBytesPerFrame);
return AAUDIO_ERROR_INTERNAL;
}
- if (mFramesPerBurst < 0 || mFramesPerBurst >= 1024) {
+ if (mFramesPerBurst < 0 || mFramesPerBurst >= 16 * 1024) {
ALOGE("RingBufferParcelable invalid mFramesPerBurst = %d", mFramesPerBurst);
return AAUDIO_ERROR_INTERNAL;
}
diff --git a/media/libaaudio/src/binding/RingBufferParcelable.h b/media/libaaudio/src/binding/RingBufferParcelable.h
index 5fc5d00..bd562f2 100644
--- a/media/libaaudio/src/binding/RingBufferParcelable.h
+++ b/media/libaaudio/src/binding/RingBufferParcelable.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef BINDING_RINGBUFFER_PARCELABLE_H
-#define BINDING_RINGBUFFER_PARCELABLE_H
+#ifndef ANDROID_AAUDIO_RINGBUFFER_PARCELABLE_H
+#define ANDROID_AAUDIO_RINGBUFFER_PARCELABLE_H
#include <stdint.h>
@@ -82,4 +82,4 @@
} /* namespace aaudio */
-#endif //BINDING_RINGBUFFER_PARCELABLE_H
+#endif //ANDROID_AAUDIO_RINGBUFFER_PARCELABLE_H
diff --git a/media/libaaudio/src/binding/SharedMemoryParcelable.cpp b/media/libaaudio/src/binding/SharedMemoryParcelable.cpp
index 7e77ca0..1094d9e 100644
--- a/media/libaaudio/src/binding/SharedMemoryParcelable.cpp
+++ b/media/libaaudio/src/binding/SharedMemoryParcelable.cpp
@@ -50,7 +50,8 @@
if (status != NO_ERROR) return status;
if (mSizeInBytes > 0) {
status = parcel->writeDupFileDescriptor(mFd);
- ALOGE_IF(status != NO_ERROR, "SharedMemoryParcelable writeDupFileDescriptor failed : %d", status);
+ ALOGE_IF(status != NO_ERROR, "SharedMemoryParcelable writeDupFileDescriptor failed : %d",
+ status);
}
return status;
}
@@ -61,11 +62,13 @@
return status;
}
if (mSizeInBytes > 0) {
- int originalFD = parcel->readFileDescriptor();
- mFd = fcntl(originalFD, F_DUPFD_CLOEXEC, 0);
+ mOriginalFd = parcel->readFileDescriptor();
+ ALOGV("SharedMemoryParcelable::readFromParcel() LEAK? mOriginalFd = %d\n", mOriginalFd);
+ mFd = fcntl(mOriginalFd, F_DUPFD_CLOEXEC, 0);
+ ALOGV("SharedMemoryParcelable::readFromParcel() LEAK? mFd = %d\n", mFd);
if (mFd == -1) {
status = -errno;
- ALOGE("SharedMemoryParcelable readFileDescriptor fcntl() failed : %d", status);
+ ALOGE("SharedMemoryParcelable readFromParcel fcntl() failed : %d", status);
}
}
return status;
@@ -81,9 +84,15 @@
mResolvedAddress = MMAP_UNRESOLVED_ADDRESS;
}
if (mFd != -1) {
+ ALOGV("SharedMemoryParcelable::close() LEAK? mFd = %d\n", mFd);
::close(mFd);
mFd = -1;
}
+ if (mOriginalFd != -1) {
+ ALOGV("SharedMemoryParcelable::close() LEAK? mOriginalFd = %d\n", mOriginalFd);
+ ::close(mOriginalFd);
+ mOriginalFd = -1;
+ }
return AAUDIO_OK;
}
diff --git a/media/libaaudio/src/binding/SharedMemoryParcelable.h b/media/libaaudio/src/binding/SharedMemoryParcelable.h
index c4feb48..4b94b46 100644
--- a/media/libaaudio/src/binding/SharedMemoryParcelable.h
+++ b/media/libaaudio/src/binding/SharedMemoryParcelable.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef BINDING_SHAREDMEMORYPARCELABLE_H
-#define BINDING_SHAREDMEMORYPARCELABLE_H
+#ifndef ANDROID_AAUDIO_SHARED_MEMORY_PARCELABLE_H
+#define ANDROID_AAUDIO_SHARED_MEMORY_PARCELABLE_H
#include <stdint.h>
@@ -67,11 +67,12 @@
#define MMAP_UNRESOLVED_ADDRESS reinterpret_cast<uint8_t*>(MAP_FAILED)
- int mFd = -1;
- int32_t mSizeInBytes = 0;
+ int mFd = -1;
+ int mOriginalFd = -1;
+ int32_t mSizeInBytes = 0;
uint8_t *mResolvedAddress = MMAP_UNRESOLVED_ADDRESS;
};
} /* namespace aaudio */
-#endif //BINDING_SHAREDMEMORYPARCELABLE_H
+#endif //ANDROID_AAUDIO_SHARED_MEMORY_PARCELABLE_H
diff --git a/media/libaaudio/src/binding/SharedRegionParcelable.h b/media/libaaudio/src/binding/SharedRegionParcelable.h
index e9f5785..f6babfd 100644
--- a/media/libaaudio/src/binding/SharedRegionParcelable.h
+++ b/media/libaaudio/src/binding/SharedRegionParcelable.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef BINDING_SHAREDREGIONPARCELABLE_H
-#define BINDING_SHAREDREGIONPARCELABLE_H
+#ifndef ANDROID_AAUDIO_SHARED_REGION_PARCELABLE_H
+#define ANDROID_AAUDIO_SHARED_REGION_PARCELABLE_H
#include <stdint.h>
@@ -59,4 +59,4 @@
} /* namespace aaudio */
-#endif //BINDING_SHAREDREGIONPARCELABLE_H
+#endif //ANDROID_AAUDIO_SHARED_REGION_PARCELABLE_H
diff --git a/media/libaaudio/src/client/AudioEndpoint.cpp b/media/libaaudio/src/client/AudioEndpoint.cpp
index 6f87df6..027d66d 100644
--- a/media/libaaudio/src/client/AudioEndpoint.cpp
+++ b/media/libaaudio/src/client/AudioEndpoint.cpp
@@ -28,6 +28,9 @@
using namespace android;
using namespace aaudio;
+#define RIDICULOUSLY_LARGE_BUFFER_CAPACITY (256 * 1024)
+#define RIDICULOUSLY_LARGE_FRAME_SIZE 4096
+
AudioEndpoint::AudioEndpoint()
: mOutputFreeRunning(false)
, mDataReadCounter(0)
@@ -45,16 +48,22 @@
ALOGE("AudioEndpoint_validateQueueDescriptor() NULL descriptor");
return AAUDIO_ERROR_NULL;
}
- if (descriptor->capacityInFrames <= 0) {
+
+ if (descriptor->capacityInFrames < 1
+ || descriptor->capacityInFrames > RIDICULOUSLY_LARGE_BUFFER_CAPACITY) {
ALOGE("AudioEndpoint_validateQueueDescriptor() bad capacityInFrames = %d",
descriptor->capacityInFrames);
return AAUDIO_ERROR_OUT_OF_RANGE;
}
- if (descriptor->bytesPerFrame <= 1) {
+
+ // Reject extreme values to catch bugs and prevent numeric overflows.
+ if (descriptor->bytesPerFrame < 1
+ || descriptor->bytesPerFrame > RIDICULOUSLY_LARGE_FRAME_SIZE) {
ALOGE("AudioEndpoint_validateQueueDescriptor() bad bytesPerFrame = %d",
descriptor->bytesPerFrame);
return AAUDIO_ERROR_OUT_OF_RANGE;
}
+
if (descriptor->dataAddress == nullptr) {
ALOGE("AudioEndpoint_validateQueueDescriptor() NULL dataAddress");
return AAUDIO_ERROR_NULL;
@@ -82,6 +91,7 @@
*descriptor->readCounterAddress = counter;
ALOGV("AudioEndpoint_validateQueueDescriptor() wrote readCounterAddress successfully");
}
+
if (descriptor->writeCounterAddress) {
fifo_counter_t counter = *descriptor->writeCounterAddress;
ALOGV("AudioEndpoint_validateQueueDescriptor() *writeCounterAddress = %d, now write",
@@ -89,6 +99,7 @@
*descriptor->writeCounterAddress = counter;
ALOGV("AudioEndpoint_validateQueueDescriptor() wrote writeCounterAddress successfully");
}
+
return AAUDIO_OK;
}
@@ -104,7 +115,6 @@
aaudio_result_t AudioEndpoint::configure(const EndpointDescriptor *pEndpointDescriptor)
{
- // TODO maybe remove after debugging
aaudio_result_t result = AudioEndpoint_validateDescriptor(pEndpointDescriptor);
if (result != AAUDIO_OK) {
ALOGE("AudioEndpoint_validateQueueDescriptor returned %d %s",
@@ -112,10 +122,19 @@
return result;
}
+ // ============================ up message queue =============================
const RingBufferDescriptor *descriptor = &pEndpointDescriptor->upMessageQueueDescriptor;
- assert(descriptor->bytesPerFrame == sizeof(AAudioServiceMessage));
- assert(descriptor->readCounterAddress != nullptr);
- assert(descriptor->writeCounterAddress != nullptr);
+ if(descriptor->bytesPerFrame != sizeof(AAudioServiceMessage)) {
+ ALOGE("AudioEndpoint::configure() bytesPerFrame != sizeof(AAudioServiceMessage) = %d",
+ descriptor->bytesPerFrame);
+ return AAUDIO_ERROR_INTERNAL;
+ }
+
+ if(descriptor->readCounterAddress == nullptr || descriptor->writeCounterAddress == nullptr) {
+ ALOGE("AudioEndpoint_validateQueueDescriptor() NULL counter address");
+ return AAUDIO_ERROR_NULL;
+ }
+
mUpCommandQueue = new FifoBuffer(
descriptor->bytesPerFrame,
descriptor->capacityInFrames,
@@ -123,25 +142,9 @@
descriptor->writeCounterAddress,
descriptor->dataAddress
);
- /* TODO mDownCommandQueue
- if (descriptor->capacityInFrames > 0) {
- descriptor = &pEndpointDescriptor->downMessageQueueDescriptor;
- mDownCommandQueue = new FifoBuffer(
- descriptor->capacityInFrames,
- descriptor->bytesPerFrame,
- descriptor->readCounterAddress,
- descriptor->writeCounterAddress,
- descriptor->dataAddress
- );
- }
- */
+
+ // ============================ down data queue =============================
descriptor = &pEndpointDescriptor->downDataQueueDescriptor;
- assert(descriptor->capacityInFrames > 0);
- assert(descriptor->bytesPerFrame > 1);
- assert(descriptor->bytesPerFrame < 4 * 16); // FIXME just for initial debugging
- assert(descriptor->framesPerBurst > 0);
- assert(descriptor->framesPerBurst < 8 * 1024); // FIXME just for initial debugging
- assert(descriptor->dataAddress != nullptr);
ALOGV("AudioEndpoint::configure() data framesPerBurst = %d", descriptor->framesPerBurst);
ALOGV("AudioEndpoint::configure() data readCounterAddress = %p", descriptor->readCounterAddress);
mOutputFreeRunning = descriptor->readCounterAddress == nullptr;
@@ -172,8 +175,6 @@
aaudio_result_t AudioEndpoint::writeDataNow(const void *buffer, int32_t numFrames)
{
- // TODO Make it easier for the AAudioStreamInternal to scale floats and write shorts
- // TODO Similar to block adapter write through technique. Add a DataConverter.
return mDownDataQueue->write(buffer, numFrames);
}
diff --git a/media/libaaudio/src/client/AudioEndpoint.h b/media/libaaudio/src/client/AudioEndpoint.h
index a24a705..46a3fc5 100644
--- a/media/libaaudio/src/client/AudioEndpoint.h
+++ b/media/libaaudio/src/client/AudioEndpoint.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef AAUDIO_AUDIO_ENDPOINT_H
-#define AAUDIO_AUDIO_ENDPOINT_H
+#ifndef ANDROID_AAUDIO_AUDIO_ENDPOINT_H
+#define ANDROID_AAUDIO_AUDIO_ENDPOINT_H
#include <aaudio/AAudio.h>
@@ -93,4 +93,4 @@
} // namespace aaudio
-#endif //AAUDIO_AUDIO_ENDPOINT_H
+#endif //ANDROID_AAUDIO_AUDIO_ENDPOINT_H
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 5214db8..eee860e 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -244,7 +244,7 @@
ALOGD("AudioStreamInternal(): callbackLoop() exiting, result = %d, isPlaying() = %d",
result, (int) isPlaying());
- return NULL; // TODO review
+ return NULL;
}
static void *aaudio_callback_thread_proc(void *context)
@@ -402,7 +402,7 @@
aaudio_result_t AudioStreamInternal::getTimestamp(clockid_t clockId,
int64_t *framePosition,
int64_t *timeNanoseconds) {
- // TODO implement using real HAL
+ // TODO Generate in server and pass to client. Return latest.
int64_t time = AudioClock::getNanoseconds();
*framePosition = mClockModel.convertTimeToPosition(time);
*timeNanoseconds = time + (10 * AAUDIO_NANOS_PER_MILLISECOND); // Fake hardware delay
@@ -574,12 +574,9 @@
// Write as much data as we can without blocking.
aaudio_result_t AudioStreamInternal::writeNow(const void *buffer, int32_t numFrames,
int64_t currentNanoTime, int64_t *wakeTimePtr) {
-
- {
- aaudio_result_t result = processCommands();
- if (result != AAUDIO_OK) {
- return result;
- }
+ aaudio_result_t result = processCommands();
+ if (result != AAUDIO_OK) {
+ return result;
}
if (mAudioEndpoint.isOutputFreeRunning()) {
diff --git a/media/libaaudio/src/client/AudioStreamInternal.h b/media/libaaudio/src/client/AudioStreamInternal.h
index a43f6c5..ee602c1 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.h
+++ b/media/libaaudio/src/client/AudioStreamInternal.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef AAUDIO_AUDIOSTREAMINTERNAL_H
-#define AAUDIO_AUDIOSTREAMINTERNAL_H
+#ifndef ANDROID_AAUDIO_AUDIO_STREAM_INTERNAL_H
+#define ANDROID_AAUDIO_AUDIO_STREAM_INTERNAL_H
#include <stdint.h>
#include <aaudio/AAudio.h>
@@ -49,15 +49,11 @@
aaudio_result_t requestStop() override;
- // TODO use aaudio_clockid_t all the way down to AudioClock
aaudio_result_t getTimestamp(clockid_t clockId,
int64_t *framePosition,
int64_t *timeNanoseconds) override;
-
-
virtual aaudio_result_t updateStateWhileWaiting() override;
-
// =========== End ABSTRACT methods ===========================
aaudio_result_t open(const AudioStreamBuilder &builder) override;
@@ -170,4 +166,4 @@
} /* namespace aaudio */
-#endif //AAUDIO_AUDIOSTREAMINTERNAL_H
+#endif //ANDROID_AAUDIO_AUDIO_STREAM_INTERNAL_H
diff --git a/media/libaaudio/src/client/IsochronousClockModel.h b/media/libaaudio/src/client/IsochronousClockModel.h
index 205c341..0314f55 100644
--- a/media/libaaudio/src/client/IsochronousClockModel.h
+++ b/media/libaaudio/src/client/IsochronousClockModel.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef AAUDIO_ISOCHRONOUS_CLOCK_MODEL_H
-#define AAUDIO_ISOCHRONOUS_CLOCK_MODEL_H
+#ifndef ANDROID_AAUDIO_ISOCHRONOUS_CLOCK_MODEL_H
+#define ANDROID_AAUDIO_ISOCHRONOUS_CLOCK_MODEL_H
#include <stdint.h>
@@ -106,4 +106,4 @@
} /* namespace aaudio */
-#endif //AAUDIO_ISOCHRONOUS_CLOCK_MODEL_H
+#endif //ANDROID_AAUDIO_ISOCHRONOUS_CLOCK_MODEL_H
diff --git a/media/libaaudio/src/core/AudioStream.cpp b/media/libaaudio/src/core/AudioStream.cpp
index c3cf27f..d1698bf 100644
--- a/media/libaaudio/src/core/AudioStream.cpp
+++ b/media/libaaudio/src/core/AudioStream.cpp
@@ -66,6 +66,18 @@
ALOGE("AudioStream::open(): samplesPerFrame out of range = %d", mSamplesPerFrame);
return AAUDIO_ERROR_OUT_OF_RANGE;
}
+
+ switch(mFormat) {
+ case AAUDIO_FORMAT_UNSPECIFIED:
+ case AAUDIO_FORMAT_PCM_I16:
+ case AAUDIO_FORMAT_PCM_FLOAT:
+ break; // valid
+ default:
+ ALOGE("AudioStream::open(): audioFormat not valid = %d", mFormat);
+ return AAUDIO_ERROR_INVALID_FORMAT;
+ // break;
+ }
+
if (mSampleRate < 0 || mSampleRate > 1000000) {
ALOGE("AudioStream::open(): mSampleRate out of range = %d", mSampleRate);
return AAUDIO_ERROR_INVALID_RATE;
@@ -101,7 +113,6 @@
return result;
}
- // TODO replace this when similar functionality added to AudioTrack.cpp
int64_t durationNanos = 20 * AAUDIO_NANOS_PER_MILLISECOND; // arbitrary
aaudio_stream_state_t state = getState();
while (state == currentState && timeoutNanoseconds > 0) {
@@ -145,6 +156,8 @@
return audioStream->wrapUserThread();
}
+// This is not exposed in the API.
+// But it is still used internally to implement callbacks for MMAP mode.
aaudio_result_t AudioStream::createThread(int64_t periodNanoseconds,
aaudio_audio_thread_proc_t threadProc,
void* threadArg)
@@ -161,8 +174,7 @@
setPeriodNanoseconds(periodNanoseconds);
int err = pthread_create(&mThread, nullptr, AudioStream_internalThreadProc, this);
if (err != 0) {
- // TODO convert errno to aaudio_result_t
- return AAUDIO_ERROR_INTERNAL;
+ return AAudioConvert_androidToAAudioResult(-errno);
} else {
mHasThread = true;
return AAUDIO_OK;
@@ -182,7 +194,6 @@
int err = pthread_join(mThread, returnArg);
#endif
mHasThread = false;
- // TODO convert errno to aaudio_result_t
- return err ? AAUDIO_ERROR_INTERNAL : mThreadRegistrationResult;
+ return err ? AAudioConvert_androidToAAudioResult(-errno) : mThreadRegistrationResult;
}
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
index 59d754a..f89234a 100644
--- a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
@@ -108,3 +108,24 @@
break;
}
}
+
+aaudio_result_t AudioStreamLegacy::getBestTimestamp(clockid_t clockId,
+ int64_t *framePosition,
+ int64_t *timeNanoseconds,
+ ExtendedTimestamp *extendedTimestamp) {
+ int timebase;
+ switch (clockId) {
+ case CLOCK_BOOTTIME:
+ timebase = ExtendedTimestamp::TIMEBASE_BOOTTIME;
+ break;
+ case CLOCK_MONOTONIC:
+ timebase = ExtendedTimestamp::TIMEBASE_MONOTONIC;
+ break;
+ default:
+ ALOGE("getTimestamp() - Unrecognized clock type %d", (int) clockId);
+ return AAUDIO_ERROR_UNEXPECTED_VALUE;
+ break;
+ }
+ status_t status = extendedTimestamp->getBestTimestamp(framePosition, timeNanoseconds, timebase);
+ return AAudioConvert_androidToAAudioResult(status);
+}
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.h b/media/libaaudio/src/legacy/AudioStreamLegacy.h
index d5a3ede..38f1a56 100644
--- a/media/libaaudio/src/legacy/AudioStreamLegacy.h
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.h
@@ -17,6 +17,7 @@
#ifndef LEGACY_AUDIO_STREAM_LEGACY_H
#define LEGACY_AUDIO_STREAM_LEGACY_H
+#include <media/AudioTimestamp.h>
#include <aaudio/AAudio.h>
@@ -73,6 +74,12 @@
virtual int64_t incrementClientFrameCounter(int32_t frames) = 0;
protected:
+
+ aaudio_result_t getBestTimestamp(clockid_t clockId,
+ int64_t *framePosition,
+ int64_t *timeNanoseconds,
+ android::ExtendedTimestamp *extendedTimestamp);
+
FixedBlockAdapter *mBlockAdapter = nullptr;
aaudio_wrapping_frames_t mPositionWhenStarting = 0;
int32_t mCallbackBufferSize = 0;
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
index f0a6ceb..eb6bfd5 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
@@ -177,10 +177,13 @@
aaudio_result_t AudioStreamRecord::requestPause()
{
+ // This does not make sense for an input stream.
+ // There is no real difference between pause() and stop().
return AAUDIO_ERROR_UNIMPLEMENTED;
}
aaudio_result_t AudioStreamRecord::requestFlush() {
+ // This does not make sense for an input stream.
return AAUDIO_ERROR_UNIMPLEMENTED;
}
@@ -259,7 +262,7 @@
int32_t AudioStreamRecord::getXRunCount() const
{
- return AAUDIO_ERROR_UNIMPLEMENTED; // TODO implement when AudioRecord supports it
+ return 0; // TODO implement when AudioRecord supports it
}
int32_t AudioStreamRecord::getFramesPerBurst() const
@@ -275,20 +278,5 @@
if (status != NO_ERROR) {
return AAudioConvert_androidToAAudioResult(status);
}
- // TODO Merge common code into AudioStreamLegacy after rebasing.
- int timebase;
- switch(clockId) {
- case CLOCK_BOOTTIME:
- timebase = ExtendedTimestamp::TIMEBASE_BOOTTIME;
- break;
- case CLOCK_MONOTONIC:
- timebase = ExtendedTimestamp::TIMEBASE_MONOTONIC;
- break;
- default:
- ALOGE("getTimestamp() - Unrecognized clock type %d", (int) clockId);
- return AAUDIO_ERROR_UNEXPECTED_VALUE;
- break;
- }
- status = extendedTimestamp.getBestTimestamp(framePosition, timeNanoseconds, timebase);
- return AAudioConvert_androidToAAudioResult(status);
+ return getBestTimestamp(clockId, framePosition, timeNanoseconds, &extendedTimestamp);
}
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
index b4b1c04..a7c7673 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
@@ -61,7 +61,7 @@
ALOGD("AudioStreamTrack::open = %p", this);
// Try to create an AudioTrack
- // TODO Support UNSPECIFIED in AudioTrack. For now, use stereo if unspecified.
+ // Use stereo if unspecified.
int32_t samplesPerFrame = (getSamplesPerFrame() == AAUDIO_UNSPECIFIED)
? 2 : getSamplesPerFrame();
audio_channel_mask_t channelMask = audio_channel_out_mask_from_count(samplesPerFrame);
@@ -163,9 +163,8 @@
aaudio_result_t AudioStreamTrack::close()
{
- // TODO maybe add close() or release() to AudioTrack API then call it from here
if (getState() != AAUDIO_STREAM_STATE_CLOSED) {
- mAudioTrack.clear(); // TODO is this right?
+ mAudioTrack.clear();
setState(AAUDIO_STREAM_STATE_CLOSED);
}
mFixedBlockReader.close();
@@ -290,7 +289,7 @@
if (err != OK) {
return AAudioConvert_androidToAAudioResult(err);
} else if (position == 0) {
- // Advance frames read to match written.
+ // TODO Advance frames read to match written.
setState(AAUDIO_STREAM_STATE_FLUSHED);
}
}
@@ -389,20 +388,5 @@
if (status != NO_ERROR) {
return AAudioConvert_androidToAAudioResult(status);
}
- // TODO Merge common code into AudioStreamLegacy after rebasing.
- int timebase;
- switch (clockId) {
- case CLOCK_BOOTTIME:
- timebase = ExtendedTimestamp::TIMEBASE_BOOTTIME;
- break;
- case CLOCK_MONOTONIC:
- timebase = ExtendedTimestamp::TIMEBASE_MONOTONIC;
- break;
- default:
- ALOGE("getTimestamp() - Unrecognized clock type %d", (int) clockId);
- return AAUDIO_ERROR_UNEXPECTED_VALUE;
- break;
- }
- status = extendedTimestamp.getBestTimestamp(framePosition, timeNanoseconds, timebase);
- return AAudioConvert_androidToAAudioResult(status);
+ return getBestTimestamp(clockId, framePosition, timeNanoseconds, &extendedTimestamp);
}
diff --git a/media/libaaudio/src/utility/AAudioUtilities.cpp b/media/libaaudio/src/utility/AAudioUtilities.cpp
index 5fa228a..be2bd10 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.cpp
+++ b/media/libaaudio/src/utility/AAudioUtilities.cpp
@@ -48,7 +48,7 @@
}
-// TODO call clamp16_from_float function in primitives.h
+// TODO expose and call clamp16_from_float function in primitives.h
static inline int16_t clamp16_from_float(float f) {
/* Offset is used to expand the valid range of [-1.0, 1.0) into the 16 lsbs of the
* floating point significand. The normal shift is 3<<22, but the -15 offset
@@ -209,18 +209,32 @@
status = DEAD_OBJECT;
break;
case AAUDIO_ERROR_INVALID_STATE:
+ case AAUDIO_ERROR_UNEXPECTED_STATE:
status = INVALID_OPERATION;
break;
- case AAUDIO_ERROR_UNEXPECTED_VALUE: // TODO redundant?
+ case AAUDIO_ERROR_UNEXPECTED_VALUE:
case AAUDIO_ERROR_INVALID_RATE:
case AAUDIO_ERROR_INVALID_FORMAT:
case AAUDIO_ERROR_ILLEGAL_ARGUMENT:
+ case AAUDIO_ERROR_OUT_OF_RANGE:
status = BAD_VALUE;
break;
case AAUDIO_ERROR_WOULD_BLOCK:
status = WOULD_BLOCK;
break;
- // TODO add more result codes
+ case AAUDIO_ERROR_NULL:
+ status = UNEXPECTED_NULL;
+ break;
+ // TODO translate these result codes
+ case AAUDIO_ERROR_INCOMPATIBLE:
+ case AAUDIO_ERROR_INTERNAL:
+ case AAUDIO_ERROR_INVALID_QUERY:
+ case AAUDIO_ERROR_UNIMPLEMENTED:
+ case AAUDIO_ERROR_UNAVAILABLE:
+ case AAUDIO_ERROR_NO_FREE_HANDLES:
+ case AAUDIO_ERROR_NO_MEMORY:
+ case AAUDIO_ERROR_TIMEOUT:
+ case AAUDIO_ERROR_NO_SERVICE:
default:
status = UNKNOWN_ERROR;
break;
@@ -244,13 +258,15 @@
case INVALID_OPERATION:
result = AAUDIO_ERROR_INVALID_STATE;
break;
- case BAD_VALUE:
- result = AAUDIO_ERROR_UNEXPECTED_VALUE;
- break;
+ case UNEXPECTED_NULL:
+ result = AAUDIO_ERROR_NULL;
+ break;
+ case BAD_VALUE:
+ result = AAUDIO_ERROR_UNEXPECTED_VALUE;
+ break;
case WOULD_BLOCK:
result = AAUDIO_ERROR_WOULD_BLOCK;
break;
- // TODO add more status codes
default:
result = AAUDIO_ERROR_INTERNAL;
break;
diff --git a/media/libaaudio/src/utility/FixedBlockAdapter.cpp b/media/libaaudio/src/utility/FixedBlockAdapter.cpp
index f4666af..63495f0 100644
--- a/media/libaaudio/src/utility/FixedBlockAdapter.cpp
+++ b/media/libaaudio/src/utility/FixedBlockAdapter.cpp
@@ -25,7 +25,7 @@
int32_t FixedBlockAdapter::open(int32_t bytesPerFixedBlock)
{
mSize = bytesPerFixedBlock;
- mStorage = new uint8_t[bytesPerFixedBlock]; // TODO use std::nothrow
+ mStorage = new uint8_t[bytesPerFixedBlock];
mPosition = 0;
return 0;
}
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index 4baf253..38d90bc 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -2268,7 +2268,7 @@
// For now, we simply advance to the end of the VolumeShaper effect
// if it has been started.
if (shaper.isStarted()) {
- operationToEnd->setXOffset(1.f);
+ operationToEnd->setNormalizedTime(1.f);
}
return mAudioTrack->applyVolumeShaper(shaper.mConfiguration, operationToEnd);
});
diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h
index 16eb225..a4c8d53 100644
--- a/media/libaudioclient/include/media/AudioTrack.h
+++ b/media/libaudioclient/include/media/AudioTrack.h
@@ -874,6 +874,10 @@
*/
bool hasStarted(); // not const
+ bool isPlaying() {
+ AutoMutex lock(mLock);
+ return mState == STATE_ACTIVE || mState == STATE_STOPPING;
+ }
protected:
/* copying audio tracks is not allowed */
AudioTrack(const AudioTrack& other);
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index cc0ea1e..18fd857 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -2028,7 +2028,7 @@
// For now, we simply advance to the end of the VolumeShaper effect
// if it has been started.
if (shaper.isStarted()) {
- operationToEnd->setXOffset(1.f);
+ operationToEnd->setNormalizedTime(1.f);
}
return t->applyVolumeShaper(shaper.mConfiguration, operationToEnd);
});
@@ -2287,8 +2287,9 @@
status = mTrack->applyVolumeShaper(configuration, operation);
if (status >= 0) {
(void)mVolumeHandler->applyVolumeShaper(configuration, operation);
- // TODO: start on exact AudioTrack state (STATE_ACTIVE || STATE_STOPPING)
- mVolumeHandler->setStarted();
+ if (mTrack->isPlaying()) { // match local AudioTrack to properly restore.
+ mVolumeHandler->setStarted();
+ }
}
} else {
status = mVolumeHandler->applyVolumeShaper(configuration, operation);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 9fe61703..5775b43 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -1010,6 +1010,13 @@
}
// EOS
+ if (mPaused) {
+ // Do not notify EOS when paused.
+ // This is needed to avoid switch to next clip while in pause.
+ ALOGV("onDrainAudioQueue(): Do not notify EOS when paused");
+ return false;
+ }
+
int64_t postEOSDelayUs = 0;
if (mAudioSink->needsTrailingPadding()) {
postEOSDelayUs = getPendingAudioPlayoutDurationUs(ALooper::GetNowUs());
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 88de2e5..9f1be22 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -1063,6 +1063,34 @@
return err;
}
+ OMX_INDEXTYPE index;
+ err = mOMXNode->getExtensionIndex(
+ "OMX.google.android.index.AndroidNativeBufferConsumerUsage",
+ &index);
+
+ if (err != OK) {
+ // allow failure
+ err = OK;
+ } else {
+ int usageBits = 0;
+ if (nativeWindow->query(
+ nativeWindow,
+ NATIVE_WINDOW_CONSUMER_USAGE_BITS,
+ &usageBits) == OK) {
+ OMX_PARAM_U32TYPE params;
+ InitOMXParams(¶ms);
+ params.nPortIndex = kPortIndexOutput;
+ params.nU32 = (OMX_U32)usageBits;
+
+ err = mOMXNode->setParameter(index, ¶ms, sizeof(params));
+
+ if (err != OK) {
+ ALOGE("Fail to set AndroidNativeBufferConsumerUsage: %d", err);
+ return err;
+ }
+ }
+ }
+
OMX_U32 usage = 0;
err = mOMXNode->getGraphicBufferUsage(kPortIndexOutput, &usage);
if (err != 0) {
@@ -1106,7 +1134,8 @@
if (err == OK) {
err = setupNativeWindowSizeFormatAndUsage(
- mNativeWindow.get(), &mNativeWindowUsageBits, preregister /* reconnect */);
+ mNativeWindow.get(), &mNativeWindowUsageBits,
+ preregister && !mTunneled /* reconnect */);
}
if (err != OK) {
mNativeWindowUsageBits = 0;
diff --git a/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp b/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp
index 36bd624..650db8e 100644
--- a/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp
+++ b/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp
@@ -248,7 +248,7 @@
Return<int32_t> TWGraphicBufferProducer::setSidebandStream(const hidl_handle& stream) {
return static_cast<int32_t>(mBase->setSidebandStream(NativeHandle::create(
- native_handle_clone(stream), true)));
+ stream ? native_handle_clone(stream) : NULL, true)));
}
Return<void> TWGraphicBufferProducer::allocateBuffers(
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index d0f64ca..db99ef2 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -1861,7 +1861,7 @@
}
void OMXNodeInstance::codecBufferFilled(omx_message &msg) {
- Mutex::Autolock autoLock(mBufferIDLock);
+ Mutex::Autolock autoLock(mLock);
if (mMaxTimestampGapUs <= 0ll || mRestorePtsFailed) {
return;
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index d8882c9..8248f8b 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -59,6 +59,7 @@
std::lock_guard<std::mutex> lock(mLockUpMessageQueue);
delete mUpMessageQueue;
mUpMessageQueue = nullptr;
+
return AAUDIO_OK;
}
diff --git a/services/oboeservice/AAudioServiceStreamBase.h b/services/oboeservice/AAudioServiceStreamBase.h
index d6b6ee3..9318c2e 100644
--- a/services/oboeservice/AAudioServiceStreamBase.h
+++ b/services/oboeservice/AAudioServiceStreamBase.h
@@ -133,9 +133,6 @@
// This is used by one thread to tell another thread to exit. So it must be atomic.
std::atomic<bool> mThreadEnabled;
-
- int mAudioDataFileDescriptor = -1;
-
aaudio_audio_format_t mAudioFormat = AAUDIO_FORMAT_UNSPECIFIED;
int32_t mFramesPerBurst = 0;
int32_t mSamplesPerFrame = AAUDIO_UNSPECIFIED;
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.cpp b/services/oboeservice/AAudioServiceStreamMMAP.cpp
index 5e4a9b1..cadc2a4 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.cpp
+++ b/services/oboeservice/AAudioServiceStreamMMAP.cpp
@@ -60,6 +60,13 @@
// FIXME Make closing synchronous.
AudioClock::sleepForNanos(100 * AAUDIO_NANOS_PER_MILLISECOND);
+ if (mAudioDataFileDescriptor != -1) {
+ ALOGV("AAudioServiceStreamMMAP: LEAK? close(mAudioDataFileDescriptor = %d)\n",
+ mAudioDataFileDescriptor);
+ ::close(mAudioDataFileDescriptor);
+ mAudioDataFileDescriptor = -1;
+ }
+
return AAudioServiceStreamBase::close();
}
@@ -164,6 +171,8 @@
: audio_channel_count_from_in_mask(config.channel_mask);
mAudioDataFileDescriptor = mMmapBufferinfo.shared_memory_fd;
+ ALOGV("AAudioServiceStreamMMAP::open LEAK? mAudioDataFileDescriptor = %d\n",
+ mAudioDataFileDescriptor);
mFramesPerBurst = mMmapBufferinfo.burst_size_frames;
mCapacityInFrames = mMmapBufferinfo.buffer_size_frames;
mAudioFormat = AAudioConvert_androidToAAudioDataFormat(config.format);
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.h b/services/oboeservice/AAudioServiceStreamMMAP.h
index a8e63a6..fe75a10 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.h
+++ b/services/oboeservice/AAudioServiceStreamMMAP.h
@@ -127,6 +127,7 @@
MonotonicCounter mFramesWritten;
MonotonicCounter mFramesRead;
int32_t mPreviousFrameCounter = 0; // from HAL
+ int mAudioDataFileDescriptor = -1;
// Interface to the AudioFlinger MMAP support.
android::sp<android::MmapStreamInterface> mMmapStream;
diff --git a/services/oboeservice/AAudioServiceStreamShared.cpp b/services/oboeservice/AAudioServiceStreamShared.cpp
index b5d9927..713d1f8 100644
--- a/services/oboeservice/AAudioServiceStreamShared.cpp
+++ b/services/oboeservice/AAudioServiceStreamShared.cpp
@@ -200,6 +200,10 @@
mEndpointManager.closeEndpoint(endpoint);
mServiceEndpoint = nullptr;
}
+ if (mAudioDataQueue != nullptr) {
+ delete mAudioDataQueue;
+ mAudioDataQueue = nullptr;
+ }
return AAudioServiceStreamBase::close();
}
diff --git a/services/oboeservice/SharedRingBuffer.cpp b/services/oboeservice/SharedRingBuffer.cpp
index 03c160d..6b3fb4c 100644
--- a/services/oboeservice/SharedRingBuffer.cpp
+++ b/services/oboeservice/SharedRingBuffer.cpp
@@ -33,9 +33,13 @@
if (mSharedMemory != nullptr) {
delete mFifoBuffer;
munmap(mSharedMemory, mSharedMemorySizeInBytes);
- close(mFileDescriptor);
mSharedMemory = nullptr;
}
+ if (mFileDescriptor != -1) {
+ ALOGV("SharedRingBuffer: LEAK? close(mFileDescriptor = %d)\n", mFileDescriptor);
+ close(mFileDescriptor);
+ mFileDescriptor = -1;
+ }
}
aaudio_result_t SharedRingBuffer::allocate(fifo_frames_t bytesPerFrame,
@@ -46,10 +50,12 @@
mDataMemorySizeInBytes = bytesPerFrame * capacityInFrames;
mSharedMemorySizeInBytes = mDataMemorySizeInBytes + (2 * (sizeof(fifo_counter_t)));
mFileDescriptor = ashmem_create_region("AAudioSharedRingBuffer", mSharedMemorySizeInBytes);
+ ALOGV("SharedRingBuffer::allocate() LEAK? mFileDescriptor = %d\n", mFileDescriptor);
if (mFileDescriptor < 0) {
ALOGE("SharedRingBuffer::allocate() ashmem_create_region() failed %d", errno);
return AAUDIO_ERROR_INTERNAL;
}
+
int err = ashmem_set_prot_region(mFileDescriptor, PROT_READ|PROT_WRITE); // TODO error handling?
if (err < 0) {
ALOGE("SharedRingBuffer::allocate() ashmem_set_prot_region() failed %d", errno);
@@ -75,9 +81,9 @@
(fifo_counter_t *) &mSharedMemory[SHARED_RINGBUFFER_WRITE_OFFSET];
uint8_t *dataAddress = &mSharedMemory[SHARED_RINGBUFFER_DATA_OFFSET];
- mFifoBuffer = new(std::nothrow) FifoBuffer(bytesPerFrame, capacityInFrames,
+ mFifoBuffer = new FifoBuffer(bytesPerFrame, capacityInFrames,
readCounterAddress, writeCounterAddress, dataAddress);
- return (mFifoBuffer == nullptr) ? AAUDIO_ERROR_NO_MEMORY : AAUDIO_OK;
+ return AAUDIO_OK;
}
void SharedRingBuffer::fillParcelable(AudioEndpointParcelable &endpointParcelable,