Merge "media: Write maker and model information into recorded videos." into oc-dev
diff --git a/camera/include/camera/ndk/NdkCameraMetadataTags.h b/camera/include/camera/ndk/NdkCameraMetadataTags.h
index ced6034..8b76cdf 100644
--- a/camera/include/camera/ndk/NdkCameraMetadataTags.h
+++ b/camera/include/camera/ndk/NdkCameraMetadataTags.h
@@ -1542,7 +1542,12 @@
* request A.</p>
* <p>Note that when enableZsl is <code>true</code>, it is not guaranteed to get output images captured in the
* past for requests with STILL_CAPTURE capture intent.</p>
- * <p>The value of enableZsl in capture templates is always <code>false</code> if present.</p>
+ * <p>For applications targeting SDK versions O and newer, the value of enableZsl in
+ * TEMPLATE_STILL_CAPTURE template may be <code>true</code>. The value in other templates is always
+ * <code>false</code> if present.</p>
+ * <p>For applications targeting SDK versions older than O, the value of enableZsl in all
+ * capture templates is always <code>false</code> if present.</p>
+ * <p>For application-operated ZSL, use CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG template.</p>
*
* @see ACAMERA_CONTROL_CAPTURE_INTENT
* @see ACAMERA_SENSOR_TIMESTAMP
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 80aad2f..dfd5df7 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -1048,6 +1048,10 @@
bool haveVideo = false;
for (size_t i = 0; i < numTracks; ++i) {
sp<IMediaSource> source = extractor->getTrack(i);
+ if (source == nullptr) {
+ fprintf(stderr, "skip NULL track %zu, track count %zu.\n", i, numTracks);
+ continue;
+ }
const char *mime;
CHECK(source->getFormat()->findCString(
@@ -1110,6 +1114,10 @@
}
mediaSource = extractor->getTrack(i);
+ if (mediaSource == nullptr) {
+ fprintf(stderr, "skip NULL track %zu, total tracks %zu.\n", i, numTracks);
+ return -1;
+ }
}
}
diff --git a/cmds/stagefright/stream.cpp b/cmds/stagefright/stream.cpp
index 8f9333a..2e1d240 100644
--- a/cmds/stagefright/stream.cpp
+++ b/cmds/stagefright/stream.cpp
@@ -171,7 +171,8 @@
mWriter = new MPEG2TSWriter(
this, &MyConvertingStreamSource::WriteDataWrapper);
- for (size_t i = 0; i < extractor->countTracks(); ++i) {
+ size_t numTracks = extractor->countTracks();
+ for (size_t i = 0; i < numTracks; ++i) {
const sp<MetaData> &meta = extractor->getTrackMetaData(i);
const char *mime;
@@ -181,7 +182,12 @@
continue;
}
- CHECK_EQ(mWriter->addSource(extractor->getTrack(i)), (status_t)OK);
+ sp<IMediaSource> track = extractor->getTrack(i);
+ if (track == nullptr) {
+ fprintf(stderr, "skip NULL track %zu, total tracks %zu\n", i, numTracks);
+ continue;
+ }
+ CHECK_EQ(mWriter->addSource(track), (status_t)OK);
}
CHECK_EQ(mWriter->start(), (status_t)OK);
diff --git a/include/media/Interpolator.h b/include/media/Interpolator.h
index 1b26b87..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,10 +294,21 @@
std::string toString() const {
std::stringstream ss;
- ss << "mInterpolatorType: " << mInterpolatorType << 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();
}
@@ -322,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 f5a74d8..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,19 +121,21 @@
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.)
, mId(-1) {
}
- Configuration(const Configuration &configuration)
+ 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);
@@ -236,6 +285,7 @@
clampVolume();
}
+ // The parcel layout must match VolumeShaper.java
status_t writeToParcel(Parcel *parcel) const {
if (parcel == nullptr) return BAD_VALUE;
return parcel->writeInt32((int32_t)mType)
@@ -261,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),
@@ -300,15 +358,19 @@
: Operation(FLAG_NONE, -1 /* replaceId */) {
}
- explicit Operation(Flag flags, int replaceId)
+ Operation(Flag flags, int replaceId)
: Operation(flags, replaceId, std::numeric_limits<S>::quiet_NaN() /* xOffset */) {
}
- Operation(const Operation &operation)
+ explicit Operation(const Operation &operation)
: Operation(operation.mFlags, operation.mReplaceId, operation.mXOffset) {
}
- explicit Operation(Flag flags, int replaceId, S xOffset)
+ explicit Operation(const sp<Operation> &operation)
+ : Operation(*operation.get()) {
+ }
+
+ Operation(Flag flags, int replaceId, S xOffset)
: mFlags(flags)
, mReplaceId(replaceId)
, mXOffset(xOffset) {
@@ -327,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);
@@ -360,28 +438,35 @@
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:
- explicit State(T volume, S xOffset)
+ State(T volume, S xOffset)
: mVolume(volume)
, mXOffset(xOffset) {
}
State()
- : State(-1.f, -1.f) { }
+ : State(NAN, NAN) { }
T getVolume() const {
return mVolume;
@@ -412,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:
@@ -452,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();
}
@@ -477,59 +565,73 @@
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.
- explicit VolumeShaper(
+ /* 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)
: mConfiguration(configuration) // we do not make a copy
, 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;
@@ -537,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;
@@ -628,12 +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);
@@ -656,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);
@@ -685,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:
@@ -697,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;
@@ -706,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;
@@ -746,51 +901,78 @@
return it->getState();
}
+ /* 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;
}
- return std::make_pair(volume, activeCount != 0);
+ 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.
+ */
+ void setStarted() {
+ (void)getVolume(mLastFrame); // getVolume() will start the individual VolumeShapers.
+ }
+
+ std::pair<T /* volume */, bool /* active */> getLastVolume() const {
+ AutoMutex _l(mLock);
+ return mLastVolume;
}
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();
}
- void forall(const std::function<VolumeShaper::Status (
- const sp<VolumeShaper::Configuration> &configuration,
- const sp<VolumeShaper::Operation> &operation)> &lambda) {
+ void forall(const std::function<VolumeShaper::Status (const VolumeShaper &)> &lambda) {
AutoMutex _l(mLock);
+ VS_LOG("forall: mVolumeShapers.size() %zu", mVolumeShapers.size());
for (const auto &shaper : mVolumeShapers) {
- VS_LOG("forall applying lambda");
- (void)lambda(shaper.mConfiguration, shaper.mOperation);
+ VolumeShaper::Status status = lambda(shaper);
+ VS_LOG("forall applying lambda on shaper (%p): %d", &shaper, (int)status);
}
}
void reset() {
AutoMutex _l(mLock);
mVolumeShapers.clear();
- mLastFrame = -1;
+ mLastFrame = 0;
// 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();
@@ -799,7 +981,7 @@
AutoMutex _l(mLock);
while (true) {
if (mVolumeShaperIdCounter == INT32_MAX) {
- mVolumeShaperIdCounter = VolumeShaper::kSystemIdMax;
+ mVolumeShaperIdCounter = VolumeShaper::kSystemVolumeShapersMax;
} else {
++mVolumeShaperIdCounter;
}
@@ -825,10 +1007,19 @@
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
+ int64_t mLastFrame; // logging purpose only, 0 on start
int32_t mVolumeShaperIdCounter; // a counter to return a unique volume shaper id.
+ std::pair<T /* volume */, bool /* active */> mLastVolume;
std::list<VolumeShaper> mVolumeShapers; // list provides stable iterators on erase
}; // VolumeHandler
diff --git a/include/media/omx/1.0/WGraphicBufferSource.h b/include/media/omx/1.0/WGraphicBufferSource.h
index 0ca5f44..397e576 100644
--- a/include/media/omx/1.0/WGraphicBufferSource.h
+++ b/include/media/omx/1.0/WGraphicBufferSource.h
@@ -67,14 +67,11 @@
struct LWGraphicBufferSource : public BnGraphicBufferSource {
sp<TGraphicBufferSource> mBase;
LWGraphicBufferSource(sp<TGraphicBufferSource> const& base);
- BnStatus configure(
- const sp<IOMXNode>& omxNode, int32_t dataSpace) override;
+ BnStatus configure(const sp<IOMXNode>& omxNode, int32_t dataSpace) override;
BnStatus setSuspend(bool suspend, int64_t timeUs) override;
- BnStatus setRepeatPreviousFrameDelayUs(
- int64_t repeatAfterUs) override;
+ BnStatus setRepeatPreviousFrameDelayUs(int64_t repeatAfterUs) override;
BnStatus setMaxFps(float maxFps) override;
- BnStatus setTimeLapseConfig(
- int64_t timePerFrameUs, int64_t timePerCaptureUs) override;
+ BnStatus setTimeLapseConfig(double fps, double captureFps) override;
BnStatus setStartTimeUs(int64_t startTimeUs) override;
BnStatus setStopTimeUs(int64_t stopTimeUs) override;
BnStatus setColorAspects(int32_t aspects) override;
diff --git a/include/ndk/NdkImage.h b/include/ndk/NdkImage.h
index 3d1bacc..66005cb 100644
--- a/include/ndk/NdkImage.h
+++ b/include/ndk/NdkImage.h
@@ -136,7 +136,7 @@
* <p>
* Corresponding formats:
* <ul>
- * <li>AHardwareBuffer: AHARDWAREBUFFER_FORMAT_R16G16B16A16_SFLOAT</li>
+ * <li>AHardwareBuffer: AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT</li>
* <li>Vulkan: VK_FORMAT_R16G16B16A16_SFLOAT</li>
* <li>OpenGL ES: GL_RGBA16F</li>
* </ul>
diff --git a/include/ndk/NdkImageReader.h b/include/ndk/NdkImageReader.h
index a158da9..e3600c2 100644
--- a/include/ndk/NdkImageReader.h
+++ b/include/ndk/NdkImageReader.h
@@ -307,22 +307,38 @@
* for the consumer usage. All other parameters and the return values are identical to those passed
* to {@line AImageReader_new}.
*
- * @param usage0 specifies how the consumer will access the AImage, using combination of the
- * AHARDWAREBUFFER_USAGE0 flags described in {@link hardware_buffer.h}.
- * Passing {@link AHARDWAREBUFFER_USAGE0_CPU_READ_OFTEN} is equivalent to calling
- * {@link AImageReader_new} with the same parameters. Note that consumers that do not
- * require CPU access to the buffer should omit {@link
- * AHARDWAREBUFFER_USAGE0_CPU_READ_OFTEN} to improve performance.
- * @param usage1 specifies how the consumer will access the AImage, using combination of the
- * AHARDWAREBUFFER_USAGE1 flags described in {@link hardware_buffer.h}.
+ * @param usage specifies how the consumer will access the AImage, using combination of the
+ * AHARDWAREBUFFER_USAGE flags described in {@link hardware_buffer.h}.
+ * Passing {@link AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN} is equivalent to calling
+ * {@link AImageReader_new} with the same parameters.
+ *
+ * Note that not all format and usage flag combination is supported by the {@link AImageReader}.
+ * Below are the combinations supported by the {@link AImageReader}.
+ * <table>
+ * <tr>
+ * <th>Format</th>
+ * <th>Compatible usage flags</th>
+ * </tr>
+ * <tr>
+ * <td>non-{@link AIMAGE_FORMAT_PRIVATE PRIVATE} formats defined in {@link AImage.h}
+ * </td>
+ * <td>{@link AHARDWAREBUFFER_USAGE_CPU_READ_RARELY} or
+ * {@link AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN}</td>
+ * </tr>
+ * <tr>
+ * <td>{@link AIMAGE_FORMAT_RGBA_8888}</td>
+ * <td>{@link AHARDWAREBUFFER_USAGE_VIDEO_ENCODE} or
+ * {@link AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE}, or combined</td>
+ * </tr>
+ * </table>
*
* @see AImage
* @see AImageReader_new
* @see AHardwareBuffer
*/
media_status_t AImageReader_newWithUsage(
- int32_t width, int32_t height, int32_t format, uint64_t usage0,
- uint64_t usage1, int32_t maxImages, /*out*/ AImageReader** reader);
+ int32_t width, int32_t height, int32_t format, uint64_t usage, int32_t maxImages,
+ /*out*/ AImageReader** reader);
/*
* Acquire the next {@link AImage} from the image reader's queue asynchronously.
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/examples/write_sine/jni/Android.mk b/media/libaaudio/examples/write_sine/jni/Android.mk
index 5a884e1..0bda008 100644
--- a/media/libaaudio/examples/write_sine/jni/Android.mk
+++ b/media/libaaudio/examples/write_sine/jni/Android.mk
@@ -4,7 +4,8 @@
LOCAL_MODULE_TAGS := tests
LOCAL_C_INCLUDES := \
$(call include-path-for, audio-utils) \
- frameworks/av/media/libaaudio/include
+ frameworks/av/media/libaaudio/include \
+ frameworks/av/media/libaaudio/src
# NDK recommends using this kind of relative path instead of an absolute path.
LOCAL_SRC_FILES:= ../src/write_sine.cpp
diff --git a/media/libaaudio/examples/write_sine/src/write_sine.cpp b/media/libaaudio/examples/write_sine/src/write_sine.cpp
index d8e5ec1..57a5273 100644
--- a/media/libaaudio/examples/write_sine/src/write_sine.cpp
+++ b/media/libaaudio/examples/write_sine/src/write_sine.cpp
@@ -22,12 +22,16 @@
#include <aaudio/AAudio.h>
#include "SineGenerator.h"
-#define SAMPLE_RATE 48000
-#define NUM_SECONDS 10
+#define SAMPLE_RATE 48000
+#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)
+#define REQUESTED_FORMAT AAUDIO_FORMAT_PCM_I16
+#define REQUESTED_SHARING_MODE AAUDIO_SHARING_MODE_SHARED
+//#define REQUESTED_SHARING_MODE AAUDIO_SHARING_MODE_EXCLUSIVE
+
static const char *getSharingModeText(aaudio_sharing_mode_t mode) {
const char *modeText = "unknown";
switch (mode) {
@@ -59,27 +63,25 @@
aaudio_result_t result = AAUDIO_OK;
- const int requestedSamplesPerFrame = 2;
- int actualSamplesPerFrame = 0;
+ const int requestedChannelCount = 2;
+ int actualChannelCount = 0;
const int requestedSampleRate = SAMPLE_RATE;
int actualSampleRate = 0;
- const aaudio_audio_format_t requestedDataFormat = AAUDIO_FORMAT_PCM_I16;
- aaudio_audio_format_t actualDataFormat = AAUDIO_FORMAT_PCM_I16;
+ aaudio_audio_format_t actualDataFormat = AAUDIO_FORMAT_UNSPECIFIED;
- //const aaudio_sharing_mode_t requestedSharingMode = AAUDIO_SHARING_MODE_EXCLUSIVE;
- const aaudio_sharing_mode_t requestedSharingMode = AAUDIO_SHARING_MODE_SHARED;
aaudio_sharing_mode_t actualSharingMode = AAUDIO_SHARING_MODE_SHARED;
AAudioStreamBuilder *aaudioBuilder = nullptr;
AAudioStream *aaudioStream = nullptr;
aaudio_stream_state_t state = AAUDIO_STREAM_STATE_UNINITIALIZED;
- int32_t framesPerBurst = 0;
- int32_t framesPerWrite = 0;
- int32_t bufferCapacity = 0;
- int32_t framesToPlay = 0;
- int32_t framesLeft = 0;
- int32_t xRunCount = 0;
- int16_t *data = nullptr;
+ int32_t framesPerBurst = 0;
+ int32_t framesPerWrite = 0;
+ int32_t bufferCapacity = 0;
+ int32_t framesToPlay = 0;
+ int32_t framesLeft = 0;
+ int32_t xRunCount = 0;
+ float *floatData = nullptr;
+ int16_t *shortData = nullptr;
SineGenerator sineOsc1;
SineGenerator sineOsc2;
@@ -98,9 +100,13 @@
// Request stream properties.
AAudioStreamBuilder_setSampleRate(aaudioBuilder, requestedSampleRate);
- AAudioStreamBuilder_setSamplesPerFrame(aaudioBuilder, requestedSamplesPerFrame);
- AAudioStreamBuilder_setFormat(aaudioBuilder, requestedDataFormat);
- AAudioStreamBuilder_setSharingMode(aaudioBuilder, requestedSharingMode);
+ AAudioStreamBuilder_setChannelCount(aaudioBuilder, requestedChannelCount);
+ AAudioStreamBuilder_setFormat(aaudioBuilder, REQUESTED_FORMAT);
+ AAudioStreamBuilder_setSharingMode(aaudioBuilder, REQUESTED_SHARING_MODE);
+
+ AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, AAUDIO_PERFORMANCE_MODE_NONE);
+ //AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
+ //AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, AAUDIO_PERFORMANCE_MODE_POWER_SAVING);
// Create an AAudioStream using the Builder.
result = AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream);
@@ -118,21 +124,21 @@
sineOsc1.setup(440.0, actualSampleRate);
sineOsc2.setup(660.0, actualSampleRate);
- actualSamplesPerFrame = AAudioStream_getSamplesPerFrame(aaudioStream);
- printf("SamplesPerFrame: requested = %d, actual = %d\n",
- requestedSamplesPerFrame, actualSamplesPerFrame);
+ actualChannelCount = AAudioStream_getChannelCount(aaudioStream);
+ printf("ChannelCount: requested = %d, actual = %d\n",
+ requestedChannelCount, actualChannelCount);
actualSharingMode = AAudioStream_getSharingMode(aaudioStream);
printf("SharingMode: requested = %s, actual = %s\n",
- getSharingModeText(requestedSharingMode),
+ getSharingModeText(REQUESTED_SHARING_MODE),
getSharingModeText(actualSharingMode));
// This is the number of frames that are read in one chunk by a DMA controller
// or a DSP or a mixer.
framesPerBurst = AAudioStream_getFramesPerBurst(aaudioStream);
- printf("DataFormat: framesPerBurst = %d\n",framesPerBurst);
+ printf("Buffer: bufferSize = %d\n", AAudioStream_getBufferSizeInFrames(aaudioStream));
bufferCapacity = AAudioStream_getBufferCapacityInFrames(aaudioStream);
- printf("DataFormat: bufferCapacity = %d, remainder = %d\n",
+ printf("Buffer: bufferCapacity = %d, remainder = %d\n",
bufferCapacity, bufferCapacity % framesPerBurst);
// Some DMA might use very short bursts of 16 frames. We don't need to write such small
@@ -141,17 +147,22 @@
while (framesPerWrite < 48) {
framesPerWrite *= 2;
}
- printf("DataFormat: framesPerWrite = %d\n",framesPerWrite);
+ printf("Buffer: framesPerBurst = %d\n",framesPerBurst);
+ printf("Buffer: framesPerWrite = %d\n",framesPerWrite);
actualDataFormat = AAudioStream_getFormat(aaudioStream);
- printf("DataFormat: requested = %d, actual = %d\n", requestedDataFormat, actualDataFormat);
+ printf("DataFormat: requested = %d, actual = %d\n", REQUESTED_FORMAT, actualDataFormat);
// TODO handle other data formats
+ printf("PerformanceMode: %d\n", AAudioStream_getPerformanceMode(aaudioStream));
+
// Allocate a buffer for the audio data.
- data = new int16_t[framesPerWrite * actualSamplesPerFrame];
- if (data == nullptr) {
- fprintf(stderr, "ERROR - could not allocate data buffer\n");
- result = AAUDIO_ERROR_NO_MEMORY;
+ if (actualDataFormat == AAUDIO_FORMAT_PCM_FLOAT) {
+ floatData = new float[framesPerWrite * actualChannelCount];
+ } else if (actualDataFormat == AAUDIO_FORMAT_PCM_I16) {
+ shortData = new int16_t[framesPerWrite * actualChannelCount];
+ } else {
+ printf("ERROR Unsupported data format!\n");
goto finish;
}
@@ -170,26 +181,41 @@
framesToPlay = actualSampleRate * NUM_SECONDS;
framesLeft = framesToPlay;
while (framesLeft > 0) {
- // Render sine waves to left and right channels.
- sineOsc1.render(&data[0], actualSamplesPerFrame, framesPerWrite);
- if (actualSamplesPerFrame > 1) {
- sineOsc2.render(&data[1], actualSamplesPerFrame, framesPerWrite);
+
+ if (actualDataFormat == AAUDIO_FORMAT_PCM_FLOAT) {
+ // Render sine waves to left and right channels.
+ sineOsc1.render(&floatData[0], actualChannelCount, framesPerWrite);
+ if (actualChannelCount > 1) {
+ sineOsc2.render(&floatData[1], actualChannelCount, framesPerWrite);
+ }
+ } else if (actualDataFormat == AAUDIO_FORMAT_PCM_I16) {
+ // Render sine waves to left and right channels.
+ sineOsc1.render(&shortData[0], actualChannelCount, framesPerWrite);
+ if (actualChannelCount > 1) {
+ sineOsc2.render(&shortData[1], actualChannelCount, framesPerWrite);
+ }
}
// Write audio data to the stream.
- int64_t timeoutNanos = 100 * NANOS_PER_MILLISECOND;
- int minFrames = (framesToPlay < framesPerWrite) ? framesToPlay : framesPerWrite;
- int actual = AAudioStream_write(aaudioStream, data, minFrames, timeoutNanos);
+ int64_t timeoutNanos = 1000 * NANOS_PER_MILLISECOND;
+ int32_t minFrames = (framesToPlay < framesPerWrite) ? framesToPlay : framesPerWrite;
+ int32_t actual = 0;
+ if (actualDataFormat == AAUDIO_FORMAT_PCM_FLOAT) {
+ actual = AAudioStream_write(aaudioStream, floatData, minFrames, timeoutNanos);
+ } else if (actualDataFormat == AAUDIO_FORMAT_PCM_I16) {
+ actual = AAudioStream_write(aaudioStream, shortData, minFrames, timeoutNanos);
+ }
if (actual < 0) {
- fprintf(stderr, "ERROR - AAudioStream_write() returned %zd\n", actual);
+ fprintf(stderr, "ERROR - AAudioStream_write() returned %d\n", actual);
goto finish;
} else if (actual == 0) {
- fprintf(stderr, "WARNING - AAudioStream_write() returned %zd\n", actual);
+ fprintf(stderr, "WARNING - AAudioStream_write() returned %d\n", actual);
goto finish;
}
framesLeft -= actual;
// Use timestamp to estimate latency.
+ /*
{
int64_t presentationFrame;
int64_t presentationTime;
@@ -208,13 +234,15 @@
printf("estimatedLatencyMillis %d\n", (int)estimatedLatencyMillis);
}
}
+ */
}
xRunCount = AAudioStream_getXRunCount(aaudioStream);
printf("AAudioStream_getXRunCount %d\n", xRunCount);
finish:
- delete[] data;
+ delete[] floatData;
+ delete[] shortData;
AAudioStream_close(aaudioStream);
AAudioStreamBuilder_delete(aaudioBuilder);
printf("exiting - AAudio result = %d = %s\n", result, AAudio_convertResultToText(result));
diff --git a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
index 9414236..1a66f35 100644
--- a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
+++ b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
@@ -31,8 +31,6 @@
//#define SHARING_MODE AAUDIO_SHARING_MODE_EXCLUSIVE
#define SHARING_MODE AAUDIO_SHARING_MODE_SHARED
-#define CALLBACK_SIZE_FRAMES 128
-
// TODO refactor common code into a single SimpleAAudio class
/**
* Simple wrapper for AAudio that opens a default stream and then calls
@@ -67,11 +65,11 @@
/**
* Only call this after open() has been called.
*/
- int32_t getSamplesPerFrame() {
+ int32_t getChannelCount() {
if (mStream == nullptr) {
return AAUDIO_ERROR_INVALID_STATE;
}
- return AAudioStream_getSamplesPerFrame(mStream);;
+ return AAudioStream_getChannelCount(mStream);;
}
/**
@@ -87,8 +85,12 @@
AAudioStreamBuilder_setSharingMode(mBuilder, mRequestedSharingMode);
AAudioStreamBuilder_setDataCallback(mBuilder, dataProc, userContext);
AAudioStreamBuilder_setFormat(mBuilder, AAUDIO_FORMAT_PCM_FLOAT);
- AAudioStreamBuilder_setFramesPerDataCallback(mBuilder, CALLBACK_SIZE_FRAMES);
- // AAudioStreamBuilder_setBufferCapacityInFrames(mBuilder, CALLBACK_SIZE_FRAMES * 4);
+ // AAudioStreamBuilder_setFramesPerDataCallback(mBuilder, CALLBACK_SIZE_FRAMES);
+ AAudioStreamBuilder_setBufferCapacityInFrames(mBuilder, 48 * 8);
+
+ //AAudioStreamBuilder_setPerformanceMode(mBuilder, AAUDIO_PERFORMANCE_MODE_NONE);
+ AAudioStreamBuilder_setPerformanceMode(mBuilder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
+ //AAudioStreamBuilder_setPerformanceMode(mBuilder, AAUDIO_PERFORMANCE_MODE_POWER_SAVING);
// Open an AAudioStream using the Builder.
result = AAudioStreamBuilder_openStream(mBuilder, &mStream);
@@ -100,7 +102,6 @@
AAudioStream_getBufferSizeInFrames(mStream));
printf("AAudioStream_getBufferCapacityInFrames() = %d\n",
AAudioStream_getBufferCapacityInFrames(mStream));
- return result;
finish1:
AAudioStreamBuilder_delete(mBuilder);
@@ -121,7 +122,7 @@
// Write zero data to fill up the buffer and prevent underruns.
aaudio_result_t prime() {
- int32_t samplesPerFrame = AAudioStream_getSamplesPerFrame(mStream);
+ int32_t samplesPerFrame = AAudioStream_getChannelCount(mStream);
const int numFrames = 32;
float zeros[numFrames * samplesPerFrame];
memset(zeros, 0, sizeof(zeros));
@@ -136,7 +137,7 @@
aaudio_result_t start() {
aaudio_result_t result = AAudioStream_requestStart(mStream);
if (result != AAUDIO_OK) {
- fprintf(stderr, "ERROR - AAudioStream_requestStart() returned %d %s\n",
+ printf("ERROR - AAudioStream_requestStart() returned %d %s\n",
result, AAudio_convertResultToText(result));
}
return result;
@@ -146,7 +147,7 @@
aaudio_result_t stop() {
aaudio_result_t result = AAudioStream_requestStop(mStream);
if (result != AAUDIO_OK) {
- fprintf(stderr, "ERROR - AAudioStream_requestStop() returned %d %s\n",
+ printf("ERROR - AAudioStream_requestStop() returned %d %s\n",
result, AAudio_convertResultToText(result));
}
int32_t xRunCount = AAudioStream_getXRunCount(mStream);
@@ -169,9 +170,6 @@
typedef struct SineThreadedData_s {
SineGenerator sineOsc1;
SineGenerator sineOsc2;
- // Remove these variables used for testing.
- int32_t numFrameCounts;
- int32_t frameCounts[MAX_FRAME_COUNT_RECORDS];
int scheduler;
bool schedulerChecked;
} SineThreadedData_t;
@@ -186,16 +184,12 @@
SineThreadedData_t *sineData = (SineThreadedData_t *) userData;
- if (sineData->numFrameCounts < MAX_FRAME_COUNT_RECORDS) {
- sineData->frameCounts[sineData->numFrameCounts++] = numFrames;
- }
-
if (!sineData->schedulerChecked) {
sineData->scheduler = sched_getscheduler(gettid());
sineData->schedulerChecked = true;
}
- int32_t samplesPerFrame = AAudioStream_getSamplesPerFrame(stream);
+ int32_t samplesPerFrame = AAudioStream_getChannelCount(stream);
// This code only plays on the first one or two channels.
// TODO Support arbitrary number of channels.
switch (AAudioStream_getFormat(stream)) {
@@ -240,7 +234,6 @@
player.setSharingMode(SHARING_MODE);
- myData.numFrameCounts = 0;
myData.schedulerChecked = false;
result = player.open(MyDataCallbackProc, &myData);
@@ -249,7 +242,7 @@
goto error;
}
printf("player.getFramesPerSecond() = %d\n", player.getFramesPerSecond());
- printf("player.getSamplesPerFrame() = %d\n", player.getSamplesPerFrame());
+ printf("player.getChannelCount() = %d\n", player.getChannelCount());
myData.sineOsc1.setup(440.0, 48000);
myData.sineOsc1.setSweep(300.0, 600.0, 5.0);
myData.sineOsc2.setup(660.0, 48000);
@@ -288,22 +281,21 @@
printf("Stream state is %d %s!\n", state, AAudio_convertStreamStateToText(state));
break;
}
+ printf("framesWritten = %d\n", (int) AAudioStream_getFramesWritten(player.getStream()));
}
printf("Woke up now.\n");
+ printf("call stop()\n");
result = player.stop();
if (result != AAUDIO_OK) {
goto error;
}
+ printf("call close()\n");
result = player.close();
if (result != AAUDIO_OK) {
goto error;
}
- // Report data gathered in the callback.
- for (int i = 0; i < myData.numFrameCounts; i++) {
- printf("numFrames[%4d] = %4d\n", i, myData.frameCounts[i]);
- }
if (myData.schedulerChecked) {
printf("scheduler = 0x%08x, SCHED_FIFO = 0x%08X\n",
myData.scheduler,
diff --git a/media/libaaudio/examples/write_sine/static/Android.mk b/media/libaaudio/examples/write_sine/static/Android.mk
index e4da6a8..3fee08a 100644
--- a/media/libaaudio/examples/write_sine/static/Android.mk
+++ b/media/libaaudio/examples/write_sine/static/Android.mk
@@ -4,6 +4,7 @@
LOCAL_MODULE_TAGS := examples
LOCAL_C_INCLUDES := \
$(call include-path-for, audio-utils) \
+ frameworks/av/media/libaaudio/src \
frameworks/av/media/libaaudio/include
# NDK recommends using this kind of relative path instead of an absolute path.
diff --git a/media/libaaudio/include/aaudio/AAudio.h b/media/libaaudio/include/aaudio/AAudio.h
index 4c1ea55..c21caa4 100644
--- a/media/libaaudio/include/aaudio/AAudio.h
+++ b/media/libaaudio/include/aaudio/AAudio.h
@@ -125,6 +125,25 @@
};
typedef int32_t aaudio_sharing_mode_t;
+
+enum {
+ /**
+ * No particular performance needs. Default.
+ */
+ AAUDIO_PERFORMANCE_MODE_NONE = 10,
+
+ /**
+ * Extending battery life is most important.
+ */
+ AAUDIO_PERFORMANCE_MODE_POWER_SAVING,
+
+ /**
+ * Reducing latency is most important.
+ */
+ AAUDIO_PERFORMANCE_MODE_LOW_LATENCY
+};
+typedef int32_t aaudio_performance_mode_t;
+
typedef struct AAudioStreamStruct AAudioStream;
typedef struct AAudioStreamBuilderStruct AAudioStreamBuilder;
@@ -206,20 +225,28 @@
int32_t sampleRate);
/**
- * Request a number of samples per frame.
+ * Request a number of channels for the stream.
*
* The stream may be opened with a different value.
* So the application should query for the actual value after the stream is opened.
*
* The default, if you do not call this function, is AAUDIO_UNSPECIFIED.
*
- * Note, this quantity is sometimes referred to as "channel count".
+ * Note, this quantity is sometimes referred to as "samples per frame".
*
* @param builder reference provided by AAudio_createStreamBuilder()
- * @param samplesPerFrame Number of samples in one frame, ie. numChannels.
+ * @param channelCount Number of channels desired.
*/
+AAUDIO_API void AAudioStreamBuilder_setChannelCount(AAudioStreamBuilder* builder,
+ int32_t channelCount);
+
+/**
+ *
+ * @deprecated use AAudioStreamBuilder_setChannelCount()
+ */
+// TODO remove as soon as the NDK and OS are in sync, before RC1
AAUDIO_API void AAudioStreamBuilder_setSamplesPerFrame(AAudioStreamBuilder* builder,
- int32_t samplesPerFrame);
+ int32_t samplesPerFrame);
/**
* Request a sample data format, for example AAUDIO_FORMAT_PCM_I16.
@@ -271,6 +298,18 @@
*/
AAUDIO_API void AAudioStreamBuilder_setBufferCapacityInFrames(AAudioStreamBuilder* builder,
int32_t numFrames);
+
+/**
+ * Set the requested performance mode.
+ *
+ * The default, if you do not call this function, is AAUDIO_PERFORMANCE_MODE_NONE.
+ *
+ * @param builder reference provided by AAudio_createStreamBuilder()
+ * @param mode the desired performance mode, eg. AAUDIO_PERFORMANCE_MODE_LOW_LATENCY
+ */
+AAUDIO_API void AAudioStreamBuilder_setPerformanceMode(AAudioStreamBuilder* builder,
+ aaudio_performance_mode_t mode);
+
/**
* Return one of these values from the data callback function.
*/
@@ -468,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.
*/
@@ -480,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.
*/
@@ -665,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
*/
@@ -677,8 +724,18 @@
AAUDIO_API int32_t AAudioStream_getSampleRate(AAudioStream* stream);
/**
+ * A stream has one or more channels of data.
+ * A frame will contain one sample for each channel.
+ *
+ * @param stream reference provided by AAudioStreamBuilder_openStream()
+ * @return actual number of channels
+ */
+AAUDIO_API int32_t AAudioStream_getChannelCount(AAudioStream* stream);
+
+/**
* The samplesPerFrame is also known as channelCount.
*
+ * @deprecated use AAudioStream_getChannelCount()
* @param stream reference provided by AAudioStreamBuilder_openStream()
* @return actual samples per frame
*/
@@ -704,6 +761,13 @@
AAUDIO_API aaudio_sharing_mode_t AAudioStream_getSharingMode(AAudioStream* stream);
/**
+ * Get the performance mode used by the stream.
+ *
+ * @param stream reference provided by AAudioStreamBuilder_openStream()
+ */
+AAUDIO_API aaudio_performance_mode_t AAudioStream_getPerformanceMode(AAudioStream* stream);
+
+/**
* @param stream reference provided by AAudioStreamBuilder_openStream()
* @return direction
*/
diff --git a/media/libaaudio/libaaudio.map.txt b/media/libaaudio/libaaudio.map.txt
index 1024e1f..8f74800 100644
--- a/media/libaaudio/libaaudio.map.txt
+++ b/media/libaaudio/libaaudio.map.txt
@@ -3,12 +3,14 @@
AAudio_convertResultToText;
AAudio_convertStreamStateToText;
AAudio_createStreamBuilder;
+ AAudioStreamBuilder_setPerformanceMode;
AAudioStreamBuilder_setDeviceId;
AAudioStreamBuilder_setDataCallback;
AAudioStreamBuilder_setErrorCallback;
AAudioStreamBuilder_setFramesPerDataCallback;
AAudioStreamBuilder_setSampleRate;
AAudioStreamBuilder_setSamplesPerFrame;
+ AAudioStreamBuilder_setChannelCount;
AAudioStreamBuilder_setFormat;
AAudioStreamBuilder_setSharingMode;
AAudioStreamBuilder_setDirection;
@@ -32,6 +34,8 @@
AAudioStream_getXRunCount;
AAudioStream_getSampleRate;
AAudioStream_getSamplesPerFrame;
+ AAudioStream_getChannelCount;
+ AAudioStream_getPerformanceMode;
AAudioStream_getDeviceId;
AAudioStream_getFormat;
AAudioStream_getSharingMode;
diff --git a/media/libaaudio/src/Android.mk b/media/libaaudio/src/Android.mk
index b5bb75f..f43c0ad 100644
--- a/media/libaaudio/src/Android.mk
+++ b/media/libaaudio/src/Android.mk
@@ -39,6 +39,7 @@
utility/FixedBlockAdapter.cpp \
utility/FixedBlockReader.cpp \
utility/FixedBlockWriter.cpp \
+ utility/LinearRamp.cpp \
fifo/FifoBuffer.cpp \
fifo/FifoControllerBase.cpp \
client/AudioEndpoint.cpp \
@@ -93,6 +94,7 @@
utility/FixedBlockAdapter.cpp \
utility/FixedBlockReader.cpp \
utility/FixedBlockWriter.cpp \
+ utility/LinearRamp.cpp \
fifo/FifoBuffer.cpp \
fifo/FifoControllerBase.cpp \
client/AudioEndpoint.cpp \
diff --git a/media/libaaudio/src/binding/AAudioBinderClient.cpp b/media/libaaudio/src/binding/AAudioBinderClient.cpp
index 8315c40..3f1bba3 100644
--- a/media/libaaudio/src/binding/AAudioBinderClient.cpp
+++ b/media/libaaudio/src/binding/AAudioBinderClient.cpp
@@ -75,6 +75,10 @@
return gAAudioService;
}
+static void dropAAudioService() {
+ Mutex::Autolock _l(gServiceLock);
+ gAAudioService.clear(); // force a reconnect
+}
AAudioBinderClient::AAudioBinderClient()
: AAudioServiceInterface() {}
@@ -88,14 +92,26 @@
*/
aaudio_handle_t AAudioBinderClient::openStream(const AAudioStreamRequest &request,
AAudioStreamConfiguration &configurationOutput) {
+ aaudio_handle_t stream;
+ for (int i = 0; i < 2; i++) {
+ const sp<IAAudioService> &service = getAAudioService();
+ if (service == 0) {
+ return AAUDIO_ERROR_NO_SERVICE;
+ }
- const sp<IAAudioService> &service = getAAudioService();
- if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
- return service->openStream(request, configurationOutput);
+ stream = service->openStream(request, configurationOutput);
+
+ if (stream == AAUDIO_ERROR_NO_SERVICE) {
+ ALOGE("AAudioBinderClient: lost connection to AAudioService.");
+ dropAAudioService(); // force a reconnect
+ } else {
+ break;
+ }
+ }
+ return stream;
}
aaudio_result_t AAudioBinderClient::closeStream(aaudio_handle_t streamHandle) {
-
const sp<IAAudioService> &service = getAAudioService();
if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
return service->closeStream(streamHandle);
@@ -106,37 +122,33 @@
*/
aaudio_result_t AAudioBinderClient::getStreamDescription(aaudio_handle_t streamHandle,
AudioEndpointParcelable &parcelable) {
-
const sp<IAAudioService> &service = getAAudioService();
if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
return service->getStreamDescription(streamHandle, parcelable);
}
-/**
-* Start the flow of data.
-*/
aaudio_result_t AAudioBinderClient::startStream(aaudio_handle_t streamHandle) {
const sp<IAAudioService> &service = getAAudioService();
if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
return service->startStream(streamHandle);
}
-/**
-* Stop the flow of data such that start() can resume without loss of data.
-*/
aaudio_result_t AAudioBinderClient::pauseStream(aaudio_handle_t streamHandle) {
const sp<IAAudioService> &service = getAAudioService();
if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
- return service->startStream(streamHandle);
+ return service->pauseStream(streamHandle);
}
-/**
-* Discard any data held by the underlying HAL or Service.
-*/
+aaudio_result_t AAudioBinderClient::stopStream(aaudio_handle_t streamHandle) {
+ const sp<IAAudioService> &service = getAAudioService();
+ if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
+ return service->stopStream(streamHandle);
+}
+
aaudio_result_t AAudioBinderClient::flushStream(aaudio_handle_t streamHandle) {
const sp<IAAudioService> &service = getAAudioService();
if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
- return service->startStream(streamHandle);
+ return service->flushStream(streamHandle);
}
/**
@@ -163,5 +175,3 @@
clientProcessId,
clientThreadId);
}
-
-
diff --git a/media/libaaudio/src/binding/AAudioBinderClient.h b/media/libaaudio/src/binding/AAudioBinderClient.h
index 1497177..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"
@@ -66,6 +66,8 @@
*/
aaudio_result_t pauseStream(aaudio_handle_t streamHandle) override;
+ aaudio_result_t stopStream(aaudio_handle_t streamHandle) override;
+
/**
* Discard any data held by the underlying HAL or Service.
* This is asynchronous. When complete, the service will send a FLUSHED event.
@@ -89,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/AAudioServiceDefinitions.h b/media/libaaudio/src/binding/AAudioServiceDefinitions.h
index 0d5bae5..2de560b 100644
--- a/media/libaaudio/src/binding/AAudioServiceDefinitions.h
+++ b/media/libaaudio/src/binding/AAudioServiceDefinitions.h
@@ -35,6 +35,7 @@
GET_STREAM_DESCRIPTION,
START_STREAM,
PAUSE_STREAM,
+ STOP_STREAM,
FLUSH_STREAM,
REGISTER_AUDIO_THREAD,
UNREGISTER_AUDIO_THREAD
diff --git a/media/libaaudio/src/binding/AAudioServiceInterface.h b/media/libaaudio/src/binding/AAudioServiceInterface.h
index 62fd894..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"
@@ -63,6 +63,11 @@
virtual aaudio_result_t pauseStream(aaudio_handle_t streamHandle) = 0;
/**
+ * Stop the flow of data after data currently inthe buffer has played.
+ */
+ virtual aaudio_result_t stopStream(aaudio_handle_t streamHandle) = 0;
+
+ /**
* Discard any data held by the underlying HAL or Service.
*/
virtual aaudio_result_t flushStream(aaudio_handle_t streamHandle) = 0;
@@ -82,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 19d6d52..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>
@@ -35,6 +35,7 @@
typedef enum aaudio_service_event_e : uint32_t {
AAUDIO_SERVICE_EVENT_STARTED,
AAUDIO_SERVICE_EVENT_PAUSED,
+ AAUDIO_SERVICE_EVENT_STOPPED,
AAUDIO_SERVICE_EVENT_FLUSHED,
AAUDIO_SERVICE_EVENT_CLOSED,
AAUDIO_SERVICE_EVENT_DISCONNECTED,
@@ -63,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.cpp b/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
index c2bcd05..09eaa42 100644
--- a/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
+++ b/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
@@ -43,7 +43,6 @@
status = parcel->writeInt32(mSamplesPerFrame);
if (status != NO_ERROR) goto error;
status = parcel->writeInt32((int32_t) mSharingMode);
- ALOGD("AAudioStreamConfiguration.writeToParcel(): mSharingMode = %d", mSharingMode);
if (status != NO_ERROR) goto error;
status = parcel->writeInt32((int32_t) mAudioFormat);
if (status != NO_ERROR) goto error;
@@ -66,7 +65,6 @@
status = parcel->readInt32(&temp);
if (status != NO_ERROR) goto error;
mSharingMode = (aaudio_sharing_mode_t) temp;
- ALOGD("AAudioStreamConfiguration.readFromParcel(): mSharingMode = %d", mSharingMode);
status = parcel->readInt32(&temp);
if (status != NO_ERROR) goto error;
mAudioFormat = (aaudio_audio_format_t) temp;
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.cpp b/media/libaaudio/src/binding/AAudioStreamRequest.cpp
index ec21f8a..a5c27b9 100644
--- a/media/libaaudio/src/binding/AAudioStreamRequest.cpp
+++ b/media/libaaudio/src/binding/AAudioStreamRequest.cpp
@@ -49,6 +49,10 @@
if (status != NO_ERROR) goto error;
status = parcel->writeInt32((int32_t) mDirection);
if (status != NO_ERROR) goto error;
+
+ status = parcel->writeBool(mSharingModeMatchRequired);
+ if (status != NO_ERROR) goto error;
+
status = mConfiguration.writeToParcel(parcel);
if (status != NO_ERROR) goto error;
return NO_ERROR;
@@ -63,12 +67,18 @@
status_t status = parcel->readInt32(&temp);
if (status != NO_ERROR) goto error;
mUserId = (uid_t) temp;
+
status = parcel->readInt32(&temp);
if (status != NO_ERROR) goto error;
mProcessId = (pid_t) temp;
+
status = parcel->readInt32(&temp);
if (status != NO_ERROR) goto error;
mDirection = (aaudio_direction_t) temp;
+
+ status = parcel->readBool(&mSharingModeMatchRequired);
+ if (status != NO_ERROR) goto error;
+
status = mConfiguration.readFromParcel(parcel);
if (status != NO_ERROR) goto error;
return NO_ERROR;
diff --git a/media/libaaudio/src/binding/AAudioStreamRequest.h b/media/libaaudio/src/binding/AAudioStreamRequest.h
index 992e978..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>
@@ -60,6 +60,15 @@
mDirection = direction;
}
+ bool isSharingModeMatchRequired() const {
+ return mSharingModeMatchRequired;
+ }
+
+ void setSharingModeMatchRequired(bool required) {
+ mSharingModeMatchRequired = required;
+ }
+
+
const AAudioStreamConfiguration &getConstantConfiguration() const {
return mConfiguration;
}
@@ -81,8 +90,9 @@
uid_t mUserId;
pid_t mProcessId;
aaudio_direction_t mDirection;
+ bool mSharingModeMatchRequired = false;
};
} /* 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..993075c 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.cpp b/media/libaaudio/src/binding/IAAudioService.cpp
index 03fc088..b8ef611 100644
--- a/media/libaaudio/src/binding/IAAudioService.cpp
+++ b/media/libaaudio/src/binding/IAAudioService.cpp
@@ -45,16 +45,25 @@
Parcel data, reply;
// send command
data.writeInterfaceToken(IAAudioService::getInterfaceDescriptor());
- ALOGE("BpAAudioService::client openStream request dump --------------------");
- request.dump();
+ ALOGV("BpAAudioService::client openStream --------------------");
+ // request.dump();
request.writeToParcel(&data);
status_t err = remote()->transact(OPEN_STREAM, data, &reply);
+ ALOGV("BpAAudioService::client openStream returned %d", err);
if (err != NO_ERROR) {
+ ALOGE("BpAAudioService::client openStream transact failed %d", err);
return AAudioConvert_androidToAAudioResult(err);
}
// parse reply
aaudio_handle_t stream;
- reply.readInt32(&stream);
+ err = reply.readInt32(&stream);
+ if (err != NO_ERROR) {
+ ALOGE("BpAAudioService::client transact(OPEN_STREAM) readInt %d", err);
+ return AAudioConvert_androidToAAudioResult(err);
+ } else if (stream < 0) {
+ ALOGE("BpAAudioService::client OPEN_STREAM passed stream %d", stream);
+ return stream;
+ }
err = configurationOutput.readFromParcel(&reply);
if (err != NO_ERROR) {
ALOGE("BpAAudioService::client openStream readFromParcel failed %d", err);
@@ -71,6 +80,7 @@
data.writeInt32(streamHandle);
status_t err = remote()->transact(CLOSE_STREAM, data, &reply);
if (err != NO_ERROR) {
+ ALOGE("BpAAudioService::client closeStream transact failed %d", err);
return AAudioConvert_androidToAAudioResult(err);
}
// parse reply
@@ -145,6 +155,21 @@
return res;
}
+ virtual aaudio_result_t stopStream(aaudio_handle_t streamHandle) override {
+ Parcel data, reply;
+ // send command
+ data.writeInterfaceToken(IAAudioService::getInterfaceDescriptor());
+ data.writeInt32(streamHandle);
+ status_t err = remote()->transact(STOP_STREAM, data, &reply);
+ if (err != NO_ERROR) {
+ return AAudioConvert_androidToAAudioResult(err);
+ }
+ // parse reply
+ aaudio_result_t res;
+ reply.readInt32(&res);
+ return res;
+ }
+
virtual aaudio_result_t flushStream(aaudio_handle_t streamHandle) override {
Parcel data, reply;
// send command
@@ -226,11 +251,11 @@
case OPEN_STREAM: {
request.readFromParcel(&data);
- ALOGD("BnAAudioService::client openStream request dump --------------------");
- request.dump();
+ //ALOGD("BnAAudioService::client openStream request dump --------------------");
+ //request.dump();
stream = openStream(request, configuration);
- ALOGV("BnAAudioService::onTransact OPEN_STREAM server handle = 0x%08X", stream);
+ //ALOGD("BnAAudioService::onTransact OPEN_STREAM server handle = 0x%08X", stream);
reply->writeInt32(stream);
configuration.writeToParcel(reply);
return NO_ERROR;
@@ -238,18 +263,17 @@
case CLOSE_STREAM: {
data.readInt32(&stream);
- ALOGV("BnAAudioService::onTransact CLOSE_STREAM 0x%08X", stream);
result = closeStream(stream);
+ //ALOGD("BnAAudioService::onTransact CLOSE_STREAM 0x%08X, result = %d",
+ // stream, result);
reply->writeInt32(result);
return NO_ERROR;
} break;
case GET_STREAM_DESCRIPTION: {
data.readInt32(&stream);
- ALOGI("BnAAudioService::onTransact GET_STREAM_DESCRIPTION 0x%08X", stream);
aaudio::AudioEndpointParcelable parcelable;
result = getStreamDescription(stream, parcelable);
- ALOGI("BnAAudioService::onTransact getStreamDescription() returns %d", result);
if (result != AAUDIO_OK) {
return AAudioConvert_aaudioToAndroidStatus(result);
}
@@ -277,7 +301,16 @@
data.readInt32(&stream);
result = pauseStream(stream);
ALOGV("BnAAudioService::onTransact PAUSE_STREAM 0x%08X, result = %d",
- stream, result);
+ stream, result);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ } break;
+
+ case STOP_STREAM: {
+ data.readInt32(&stream);
+ result = stopStream(stream);
+ ALOGV("BnAAudioService::onTransact STOP_STREAM 0x%08X, result = %d",
+ stream, result);
reply->writeInt32(result);
return NO_ERROR;
} break;
diff --git a/media/libaaudio/src/binding/IAAudioService.h b/media/libaaudio/src/binding/IAAudioService.h
index ab7fd1b..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>
@@ -69,6 +69,12 @@
virtual aaudio_result_t pauseStream(aaudio::aaudio_handle_t streamHandle) = 0;
/**
+ * Stop the flow of data such that the data currently in the buffer is played.
+ * This is asynchronous. When complete, the service will send a STOPPED event.
+ */
+ virtual aaudio_result_t stopStream(aaudio::aaudio_handle_t streamHandle) = 0;
+
+ /**
* Discard any data held by the underlying HAL or Service.
* This is asynchronous. When complete, the service will send a FLUSHED event.
*/
@@ -97,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 649c884..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,30 +62,37 @@
return status;
}
if (mSizeInBytes > 0) {
-// FIXME mFd = dup(parcel->readFileDescriptor());
- // Why is the ALSA resource not getting freed?!
- mFd = fcntl(parcel->readFileDescriptor(), 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;
}
aaudio_result_t SharedMemoryParcelable::close() {
- if (mResolvedAddress != nullptr) {
+ if (mResolvedAddress != MMAP_UNRESOLVED_ADDRESS) {
int err = munmap(mResolvedAddress, mSizeInBytes);
if (err < 0) {
ALOGE("SharedMemoryParcelable::close() munmap() failed %d", err);
return AAudioConvert_androidToAAudioResult(err);
}
- mResolvedAddress = nullptr;
+ 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;
}
@@ -100,16 +108,12 @@
offsetInBytes, sizeInBytes, mSizeInBytes);
return AAUDIO_ERROR_OUT_OF_RANGE;
}
- if (mResolvedAddress == nullptr) {
- /* TODO remove
- int fd = fcntl(mFd, F_DUPFD_CLOEXEC, 0);
- ALOGE_IF(fd==-1, "cannot dup fd=%d, size=%zd, (%s)",
- mFd, mSizeInBytes, strerror(errno));
- */
+ if (mResolvedAddress == MMAP_UNRESOLVED_ADDRESS) {
mResolvedAddress = (uint8_t *) mmap(0, mSizeInBytes, PROT_READ|PROT_WRITE,
MAP_SHARED, mFd, 0);
- if (mResolvedAddress == nullptr) {
- ALOGE("SharedMemoryParcelable mmap failed for fd = %d", mFd);
+ if (mResolvedAddress == MMAP_UNRESOLVED_ADDRESS) {
+ ALOGE("SharedMemoryParcelable mmap failed for fd = %d, errno = %s",
+ mFd, strerror(errno));
return AAUDIO_ERROR_INTERNAL;
}
}
diff --git a/media/libaaudio/src/binding/SharedMemoryParcelable.h b/media/libaaudio/src/binding/SharedMemoryParcelable.h
index 22e16f0..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>
@@ -64,11 +64,15 @@
void dump();
protected:
- int mFd = -1;
- int32_t mSizeInBytes = 0;
- uint8_t *mResolvedAddress = nullptr;
+
+#define MMAP_UNRESOLVED_ADDRESS reinterpret_cast<uint8_t*>(MAP_FAILED)
+
+ 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 fe049b2..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,50 +48,58 @@
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;
}
- ALOGD("AudioEndpoint_validateQueueDescriptor %s, dataAddress at %p ====================",
+ ALOGV("AudioEndpoint_validateQueueDescriptor %s, dataAddress at %p ====================",
type,
descriptor->dataAddress);
- ALOGD("AudioEndpoint_validateQueueDescriptor readCounter at %p, writeCounter at %p",
+ ALOGV("AudioEndpoint_validateQueueDescriptor readCounter at %p, writeCounter at %p",
descriptor->readCounterAddress,
descriptor->writeCounterAddress);
// Try to READ from the data area.
// This code will crash if the mmap failed.
uint8_t value = descriptor->dataAddress[0];
- ALOGD("AudioEndpoint_validateQueueDescriptor() dataAddress[0] = %d, then try to write",
+ ALOGV("AudioEndpoint_validateQueueDescriptor() dataAddress[0] = %d, then try to write",
(int) value);
// Try to WRITE to the data area.
descriptor->dataAddress[0] = value * 3;
- ALOGD("AudioEndpoint_validateQueueDescriptor() wrote successfully");
+ ALOGV("AudioEndpoint_validateQueueDescriptor() wrote successfully");
if (descriptor->readCounterAddress) {
fifo_counter_t counter = *descriptor->readCounterAddress;
- ALOGD("AudioEndpoint_validateQueueDescriptor() *readCounterAddress = %d, now write",
+ ALOGV("AudioEndpoint_validateQueueDescriptor() *readCounterAddress = %d, now write",
(int) counter);
*descriptor->readCounterAddress = counter;
- ALOGD("AudioEndpoint_validateQueueDescriptor() wrote readCounterAddress successfully");
+ ALOGV("AudioEndpoint_validateQueueDescriptor() wrote readCounterAddress successfully");
}
+
if (descriptor->writeCounterAddress) {
fifo_counter_t counter = *descriptor->writeCounterAddress;
- ALOGD("AudioEndpoint_validateQueueDescriptor() *writeCounterAddress = %d, now write",
+ ALOGV("AudioEndpoint_validateQueueDescriptor() *writeCounterAddress = %d, now write",
(int) counter);
*descriptor->writeCounterAddress = counter;
- ALOGD("AudioEndpoint_validateQueueDescriptor() wrote writeCounterAddress successfully");
+ ALOGV("AudioEndpoint_validateQueueDescriptor() wrote writeCounterAddress successfully");
}
+
return AAUDIO_OK;
}
@@ -104,18 +115,26 @@
aaudio_result_t AudioEndpoint::configure(const EndpointDescriptor *pEndpointDescriptor)
{
- // TODO maybe remove after debugging
aaudio_result_t result = AudioEndpoint_validateDescriptor(pEndpointDescriptor);
if (result != AAUDIO_OK) {
- ALOGD("AudioEndpoint_validateQueueDescriptor returned %d %s",
+ ALOGE("AudioEndpoint_validateQueueDescriptor returned %d %s",
result, AAudio_convertResultToText(result));
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,29 +142,13 @@
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);
- ALOGD("AudioEndpoint::configure() data framesPerBurst = %d", descriptor->framesPerBurst);
- ALOGD("AudioEndpoint::configure() data readCounterAddress = %p", descriptor->readCounterAddress);
+ ALOGV("AudioEndpoint::configure() data framesPerBurst = %d", descriptor->framesPerBurst);
+ ALOGV("AudioEndpoint::configure() data readCounterAddress = %p", descriptor->readCounterAddress);
mOutputFreeRunning = descriptor->readCounterAddress == nullptr;
- ALOGD("AudioEndpoint::configure() mOutputFreeRunning = %d", mOutputFreeRunning ? 1 : 0);
+ ALOGV("AudioEndpoint::configure() mOutputFreeRunning = %d", mOutputFreeRunning ? 1 : 0);
int64_t *readCounterAddress = (descriptor->readCounterAddress == nullptr)
? &mDataReadCounter
: descriptor->readCounterAddress;
@@ -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 7304205..eee860e 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -32,17 +32,15 @@
#include "binding/AAudioStreamConfiguration.h"
#include "binding/IAAudioService.h"
#include "binding/AAudioServiceMessage.h"
-#include "fifo/FifoBuffer.h"
-
#include "core/AudioStreamBuilder.h"
+#include "fifo/FifoBuffer.h"
+#include "utility/LinearRamp.h"
+
#include "AudioStreamInternal.h"
#define LOG_TIMESTAMPS 0
using android::String16;
-using android::IServiceManager;
-using android::defaultServiceManager;
-using android::interface_cast;
using android::Mutex;
using android::WrappingBuffer;
@@ -53,7 +51,10 @@
// Wait at least this many times longer than the operation should take.
#define MIN_TIMEOUT_OPERATIONS 4
-#define ALOG_CONDITION (mInService == false)
+//static int64_t s_logCounter = 0;
+//#define MYLOG_CONDITION (mInService == true && s_logCounter++ < 500)
+//#define MYLOG_CONDITION (s_logCounter++ < 500000)
+#define MYLOG_CONDITION (1)
AudioStreamInternal::AudioStreamInternal(AAudioServiceInterface &serviceInterface, bool inService)
: AudioStream()
@@ -62,8 +63,7 @@
, mServiceStreamHandle(AAUDIO_HANDLE_INVALID)
, mFramesPerBurst(16)
, mServiceInterface(serviceInterface)
- , mInService(inService)
-{
+ , mInService(inService) {
}
AudioStreamInternal::~AudioStreamInternal() {
@@ -84,27 +84,26 @@
if (getFormat() == AAUDIO_UNSPECIFIED) {
setFormat(AAUDIO_FORMAT_PCM_FLOAT);
}
+ // Request FLOAT for the shared mixer.
+ request.getConfiguration().setAudioFormat(AAUDIO_FORMAT_PCM_FLOAT);
// Build the request to send to the server.
request.setUserId(getuid());
request.setProcessId(getpid());
request.setDirection(getDirection());
+ request.setSharingModeMatchRequired(isSharingModeMatchRequired());
request.getConfiguration().setDeviceId(getDeviceId());
request.getConfiguration().setSampleRate(getSampleRate());
request.getConfiguration().setSamplesPerFrame(getSamplesPerFrame());
- request.getConfiguration().setAudioFormat(getFormat());
- aaudio_sharing_mode_t sharingMode = getSharingMode();
- ALOGE("AudioStreamInternal.open(): sharingMode %d", sharingMode);
- request.getConfiguration().setSharingMode(sharingMode);
+ request.getConfiguration().setSharingMode(getSharingMode());
+
request.getConfiguration().setBufferCapacity(builder.getBufferCapacity());
mServiceStreamHandle = mServiceInterface.openStream(request, configuration);
- ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal.open(): openStream returned mServiceStreamHandle = 0x%08X",
- (unsigned int)mServiceStreamHandle);
if (mServiceStreamHandle < 0) {
result = mServiceStreamHandle;
- ALOGE("AudioStreamInternal.open(): openStream() returned %d", result);
+ ALOGE("AudioStreamInternal.open(): %s openStream() returned %d", getLocationName(), result);
} else {
result = configuration.validate();
if (result != AAUDIO_OK) {
@@ -120,10 +119,9 @@
mDeviceFormat = configuration.getAudioFormat();
result = mServiceInterface.getStreamDescription(mServiceStreamHandle, mEndPointParcelable);
- ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal.open(): getStreamDescriptor(0x%08X) returns %d",
- mServiceStreamHandle, result);
if (result != AAUDIO_OK) {
- ALOGE("AudioStreamInternal.open(): getStreamDescriptor returns %d", result);
+ ALOGE("AudioStreamInternal.open(): %s getStreamDescriptor returns %d",
+ getLocationName(), result);
mServiceInterface.closeStream(mServiceStreamHandle);
return result;
}
@@ -140,8 +138,19 @@
mAudioEndpoint.configure(&mEndpointDescriptor);
mFramesPerBurst = mEndpointDescriptor.downDataQueueDescriptor.framesPerBurst;
- assert(mFramesPerBurst >= 16);
- assert(mEndpointDescriptor.downDataQueueDescriptor.capacityInFrames < 10 * 1024);
+ int32_t capacity = mEndpointDescriptor.downDataQueueDescriptor.capacityInFrames;
+
+ ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal.open() %s framesPerBurst = %d, capacity = %d",
+ getLocationName(), mFramesPerBurst, capacity);
+ // Validate result from server.
+ if (mFramesPerBurst < 16 || mFramesPerBurst > 16 * 1024) {
+ ALOGE("AudioStream::open(): framesPerBurst out of range = %d", mFramesPerBurst);
+ return AAUDIO_ERROR_OUT_OF_RANGE;
+ }
+ if (capacity < mFramesPerBurst || capacity > 32 * 1024) {
+ ALOGE("AudioStream::open(): bufferCapacity out of range = %d", capacity);
+ return AAUDIO_ERROR_OUT_OF_RANGE;
+ }
mClockModel.setSampleRate(getSampleRate());
mClockModel.setFramesPerBurst(mFramesPerBurst);
@@ -149,7 +158,8 @@
if (getDataCallbackProc()) {
mCallbackFrames = builder.getFramesPerDataCallback();
if (mCallbackFrames > getBufferCapacity() / 2) {
- ALOGE("AudioStreamInternal.open(): framesPerCallback too large");
+ ALOGE("AudioStreamInternal.open(): framesPerCallback too large = %d, capacity = %d",
+ mCallbackFrames, getBufferCapacity());
mServiceInterface.closeStream(mServiceStreamHandle);
return AAUDIO_ERROR_OUT_OF_RANGE;
@@ -175,7 +185,8 @@
}
aaudio_result_t AudioStreamInternal::close() {
- ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal.close(): mServiceStreamHandle = 0x%08X", mServiceStreamHandle);
+ ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal.close(): mServiceStreamHandle = 0x%08X",
+ mServiceStreamHandle);
if (mServiceStreamHandle != AAUDIO_HANDLE_INVALID) {
aaudio_handle_t serviceStreamHandle = mServiceStreamHandle;
mServiceStreamHandle = AAUDIO_HANDLE_INVALID;
@@ -233,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)
@@ -250,7 +261,7 @@
aaudio_result_t AudioStreamInternal::requestStart()
{
int64_t startTime;
- ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal(): start()");
+ ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal(): start()");
if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
return AAUDIO_ERROR_INVALID_STATE;
}
@@ -275,8 +286,10 @@
int64_t AudioStreamInternal::calculateReasonableTimeout(int32_t framesPerOperation) {
// Wait for at least a second or some number of callbacks to join the thread.
- int64_t timeoutNanoseconds = (MIN_TIMEOUT_OPERATIONS * framesPerOperation * AAUDIO_NANOS_PER_SECOND)
- / getSampleRate();
+ int64_t timeoutNanoseconds = (MIN_TIMEOUT_OPERATIONS
+ * framesPerOperation
+ * AAUDIO_NANOS_PER_SECOND)
+ / getSampleRate();
if (timeoutNanoseconds < MIN_TIMEOUT_NANOS) { // arbitrary number of seconds
timeoutNanoseconds = MIN_TIMEOUT_NANOS;
}
@@ -295,28 +308,34 @@
aaudio_result_t AudioStreamInternal::requestPauseInternal()
{
- ALOGD("AudioStreamInternal(): pause()");
if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
+ ALOGE("AudioStreamInternal(): requestPauseInternal() mServiceStreamHandle invalid = 0x%08X",
+ mServiceStreamHandle);
return AAUDIO_ERROR_INVALID_STATE;
}
mClockModel.stop(AudioClock::getNanoseconds());
setState(AAUDIO_STREAM_STATE_PAUSING);
- return mServiceInterface.startStream(mServiceStreamHandle);
+ return mServiceInterface.pauseStream(mServiceStreamHandle);
}
aaudio_result_t AudioStreamInternal::requestPause()
{
+ ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal(): %s requestPause()", getLocationName());
aaudio_result_t result = stopCallback();
if (result != AAUDIO_OK) {
return result;
}
- return requestPauseInternal();
+ result = requestPauseInternal();
+ ALOGD("AudioStreamInternal(): requestPause() returns %d", result);
+ return result;
}
aaudio_result_t AudioStreamInternal::requestFlush() {
- ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal(): flush()");
+ ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal(): requestFlush()");
if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
+ ALOGE("AudioStreamInternal(): requestFlush() mServiceStreamHandle invalid = 0x%08X",
+ mServiceStreamHandle);
return AAUDIO_ERROR_INVALID_STATE;
}
@@ -325,35 +344,45 @@
}
void AudioStreamInternal::onFlushFromServer() {
- ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal(): onFlushFromServer()");
+ ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal(): onFlushFromServer()");
int64_t readCounter = mAudioEndpoint.getDownDataReadCounter();
int64_t writeCounter = mAudioEndpoint.getDownDataWriteCounter();
+
// Bump offset so caller does not see the retrograde motion in getFramesRead().
int64_t framesFlushed = writeCounter - readCounter;
mFramesOffsetFromService += framesFlushed;
+
// Flush written frames by forcing writeCounter to readCounter.
// This is because we cannot move the read counter in the hardware.
mAudioEndpoint.setDownDataWriteCounter(readCounter);
}
+aaudio_result_t AudioStreamInternal::requestStopInternal()
+{
+ if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
+ ALOGE("AudioStreamInternal(): requestStopInternal() mServiceStreamHandle invalid = 0x%08X",
+ mServiceStreamHandle);
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
+
+ mClockModel.stop(AudioClock::getNanoseconds());
+ setState(AAUDIO_STREAM_STATE_STOPPING);
+ return mServiceInterface.stopStream(mServiceStreamHandle);
+}
+
aaudio_result_t AudioStreamInternal::requestStop()
{
- // TODO better implementation of requestStop()
- aaudio_result_t result = requestPause();
- if (result == AAUDIO_OK) {
- aaudio_stream_state_t state;
- result = waitForStateChange(AAUDIO_STREAM_STATE_PAUSING,
- &state,
- 500 * AAUDIO_NANOS_PER_MILLISECOND);// TODO temporary code
- if (result == AAUDIO_OK) {
- result = requestFlush();
- }
+ ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal(): %s requestStop()", getLocationName());
+ aaudio_result_t result = stopCallback();
+ if (result != AAUDIO_OK) {
+ return result;
}
+ result = requestStopInternal();
+ ALOGD("AudioStreamInternal(): requestStop() returns %d", result);
return result;
}
aaudio_result_t AudioStreamInternal::registerThread() {
- ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal(): registerThread()");
if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
return AAUDIO_ERROR_INVALID_STATE;
}
@@ -364,7 +393,6 @@
}
aaudio_result_t AudioStreamInternal::unregisterThread() {
- ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal(): unregisterThread()");
if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
return AAUDIO_ERROR_INVALID_STATE;
}
@@ -374,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
@@ -394,16 +422,16 @@
static int64_t oldTime = 0;
int64_t framePosition = command.timestamp.position;
int64_t nanoTime = command.timestamp.timestamp;
- ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal() timestamp says framePosition = %08lld at nanoTime %llu",
+ ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal() timestamp says framePosition = %08lld at nanoTime %llu",
(long long) framePosition,
(long long) nanoTime);
int64_t nanosDelta = nanoTime - oldTime;
if (nanosDelta > 0 && oldTime > 0) {
int64_t framesDelta = framePosition - oldPosition;
int64_t rate = (framesDelta * AAUDIO_NANOS_PER_SECOND) / nanosDelta;
- ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal() - framesDelta = %08lld", (long long) framesDelta);
- ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal() - nanosDelta = %08lld", (long long) nanosDelta);
- ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal() - measured rate = %llu", (unsigned long long) rate);
+ ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal() - framesDelta = %08lld", (long long) framesDelta);
+ ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal() - nanosDelta = %08lld", (long long) nanosDelta);
+ ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal() - measured rate = %llu", (unsigned long long) rate);
}
oldPosition = framePosition;
oldTime = nanoTime;
@@ -422,23 +450,27 @@
aaudio_result_t AudioStreamInternal::onEventFromServer(AAudioServiceMessage *message) {
aaudio_result_t result = AAUDIO_OK;
- ALOGD_IF(ALOG_CONDITION, "processCommands() got event %d", message->event.event);
+ ALOGD_IF(MYLOG_CONDITION, "processCommands() got event %d", message->event.event);
switch (message->event.event) {
case AAUDIO_SERVICE_EVENT_STARTED:
- ALOGD_IF(ALOG_CONDITION, "processCommands() got AAUDIO_SERVICE_EVENT_STARTED");
+ ALOGD_IF(MYLOG_CONDITION, "processCommands() got AAUDIO_SERVICE_EVENT_STARTED");
setState(AAUDIO_STREAM_STATE_STARTED);
break;
case AAUDIO_SERVICE_EVENT_PAUSED:
- ALOGD_IF(ALOG_CONDITION, "processCommands() got AAUDIO_SERVICE_EVENT_PAUSED");
+ ALOGD_IF(MYLOG_CONDITION, "processCommands() got AAUDIO_SERVICE_EVENT_PAUSED");
setState(AAUDIO_STREAM_STATE_PAUSED);
break;
+ case AAUDIO_SERVICE_EVENT_STOPPED:
+ ALOGD_IF(MYLOG_CONDITION, "processCommands() got AAUDIO_SERVICE_EVENT_STOPPED");
+ setState(AAUDIO_STREAM_STATE_STOPPED);
+ break;
case AAUDIO_SERVICE_EVENT_FLUSHED:
- ALOGD_IF(ALOG_CONDITION, "processCommands() got AAUDIO_SERVICE_EVENT_FLUSHED");
+ ALOGD_IF(MYLOG_CONDITION, "processCommands() got AAUDIO_SERVICE_EVENT_FLUSHED");
setState(AAUDIO_STREAM_STATE_FLUSHED);
onFlushFromServer();
break;
case AAUDIO_SERVICE_EVENT_CLOSED:
- ALOGD_IF(ALOG_CONDITION, "processCommands() got AAUDIO_SERVICE_EVENT_CLOSED");
+ ALOGD_IF(MYLOG_CONDITION, "processCommands() got AAUDIO_SERVICE_EVENT_CLOSED");
setState(AAUDIO_STREAM_STATE_CLOSED);
break;
case AAUDIO_SERVICE_EVENT_DISCONNECTED:
@@ -447,8 +479,9 @@
ALOGW("WARNING - processCommands() AAUDIO_SERVICE_EVENT_DISCONNECTED");
break;
case AAUDIO_SERVICE_EVENT_VOLUME:
- mVolume = message->event.dataDouble;
- ALOGD_IF(ALOG_CONDITION, "processCommands() AAUDIO_SERVICE_EVENT_VOLUME %f", mVolume);
+ mVolumeRamp.setTarget((float) message->event.dataDouble);
+ ALOGD_IF(MYLOG_CONDITION, "processCommands() AAUDIO_SERVICE_EVENT_VOLUME %f",
+ message->event.dataDouble);
break;
default:
ALOGW("WARNING - processCommands() Unrecognized event = %d",
@@ -463,7 +496,7 @@
aaudio_result_t result = AAUDIO_OK;
while (result == AAUDIO_OK) {
- //ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::processCommands() - looping, %d", result);
+ //ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal::processCommands() - looping, %d", result);
AAudioServiceMessage message;
if (mAudioEndpoint.readUpCommand(&message) != 1) {
break; // no command this time, no problem
@@ -478,7 +511,7 @@
break;
default:
- ALOGW("WARNING - AudioStreamInternal::processCommands() Unrecognized what = %d",
+ ALOGE("WARNING - AudioStreamInternal::processCommands() Unrecognized what = %d",
(int) message.what);
result = AAUDIO_ERROR_UNEXPECTED_VALUE;
break;
@@ -497,19 +530,13 @@
int64_t currentTimeNanos = AudioClock::getNanoseconds();
int64_t deadlineNanos = currentTimeNanos + timeoutNanoseconds;
int32_t framesLeft = numFrames;
- //ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::write(%p, %d) at time %08llu , mState = %s",
- // buffer, numFrames, (unsigned long long) currentTimeNanos,
- // AAudio_convertStreamStateToText(getState()));
// Write until all the data has been written or until a timeout occurs.
while (framesLeft > 0) {
- //ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::write() loop: framesLeft = %d, loopCount = %d =====",
- // framesLeft, loopCount++);
// The call to writeNow() will not block. It will just write as much as it can.
int64_t wakeTimeNanos = 0;
aaudio_result_t framesWritten = writeNow(source, framesLeft,
currentTimeNanos, &wakeTimeNanos);
- //ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::write() loop: framesWritten = %d", framesWritten);
if (framesWritten < 0) {
ALOGE("AudioStreamInternal::write() loop: writeNow returned %d", framesWritten);
result = framesWritten;
@@ -522,7 +549,6 @@
if (timeoutNanoseconds == 0) {
break; // don't block
} else if (framesLeft > 0) {
- //ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal:: original wakeTimeNanos %lld", (long long) wakeTimeNanos);
// clip the wake time to something reasonable
if (wakeTimeNanos < currentTimeNanos) {
wakeTimeNanos = currentTimeNanos;
@@ -534,16 +560,13 @@
break;
}
- //ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal:: sleep until %lld, dur = %lld", (long long) wakeTimeNanos,
- // (long long) (wakeTimeNanos - currentTimeNanos));
- AudioClock::sleepForNanos(wakeTimeNanos - currentTimeNanos);
+ int64_t sleepForNanos = wakeTimeNanos - currentTimeNanos;
+ AudioClock::sleepForNanos(sleepForNanos);
currentTimeNanos = AudioClock::getNanoseconds();
}
}
// return error or framesWritten
- //ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::write() result = %d, framesLeft = %d, #%d",
- // result, framesLeft, loopCount);
(void) loopCount;
return (result < 0) ? result : numFrames - framesLeft;
}
@@ -551,18 +574,13 @@
// 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) {
-
- //ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::writeNow(%p) - enter", buffer);
- {
- aaudio_result_t result = processCommands();
- //ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::writeNow() - processCommands() returned %d", result);
- if (result != AAUDIO_OK) {
- return result;
- }
+ aaudio_result_t result = processCommands();
+ if (result != AAUDIO_OK) {
+ return result;
}
if (mAudioEndpoint.isOutputFreeRunning()) {
- ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::writeNow() - update read counter");
+ //ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal::writeNow() - update read counter");
// Update data queue based on the timing model.
int64_t estimatedReadCounter = mClockModel.convertTimeToPosition(currentNanoTime);
mAudioEndpoint.setDownDataReadCounter(estimatedReadCounter);
@@ -575,9 +593,9 @@
}
// Write some data to the buffer.
- //ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::writeNow() - writeNowWithConversion(%d)", numFrames);
+ //ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal::writeNow() - writeNowWithConversion(%d)", numFrames);
int32_t framesWritten = writeNowWithConversion(buffer, numFrames);
- //ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::writeNow() - tried to write %d frames, wrote %d",
+ //ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal::writeNow() - tried to write %d frames, wrote %d",
// numFrames, framesWritten);
// Calculate an ideal time to wake up.
@@ -585,7 +603,7 @@
// By default wake up a few milliseconds from now. // TODO review
int64_t wakeTime = currentNanoTime + (1 * AAUDIO_NANOS_PER_MILLISECOND);
aaudio_stream_state_t state = getState();
- //ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::writeNow() - wakeTime based on %s",
+ //ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal::writeNow() - wakeTime based on %s",
// AAudio_convertStreamStateToText(state));
switch (state) {
case AAUDIO_STREAM_STATE_OPEN:
@@ -612,7 +630,7 @@
*wakeTimePtr = wakeTime;
}
-// ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::writeNow finished: now = %llu, read# = %llu, wrote# = %llu",
+// ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal::writeNow finished: now = %llu, read# = %llu, wrote# = %llu",
// (unsigned long long)currentNanoTime,
// (unsigned long long)mAudioEndpoint.getDownDataReadCounter(),
// (unsigned long long)mAudioEndpoint.getDownDataWriteCounter());
@@ -620,12 +638,11 @@
}
-// TODO this function needs a major cleanup.
aaudio_result_t AudioStreamInternal::writeNowWithConversion(const void *buffer,
int32_t numFrames) {
- // ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::writeNowWithConversion(%p, %d)", buffer, numFrames);
+ // ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal::writeNowWithConversion(%p, %d)",
+ // buffer, numFrames);
WrappingBuffer wrappingBuffer;
- mAudioEndpoint.getEmptyRoomAvailable(&wrappingBuffer);
uint8_t *source = (uint8_t *) buffer;
int32_t framesLeft = numFrames;
@@ -640,27 +657,72 @@
if (framesToWrite > framesAvailable) {
framesToWrite = framesAvailable;
}
- int32_t numBytes = getBytesPerFrame();
- // TODO handle volume scaling
- if (getFormat() == mDeviceFormat) {
- // Copy straight through.
- memcpy(wrappingBuffer.data[partIndex], source, numBytes);
- } else if (getFormat() == AAUDIO_FORMAT_PCM_FLOAT
- && mDeviceFormat == AAUDIO_FORMAT_PCM_I16) {
- // Data conversion.
- AAudioConvert_floatToPcm16(
- (const float *) source,
- framesToWrite * getSamplesPerFrame(),
- (int16_t *) wrappingBuffer.data[partIndex]);
- } else {
- // TODO handle more conversions
- ALOGE("AudioStreamInternal::writeNowWithConversion() unsupported formats: %d, %d",
- getFormat(), mDeviceFormat);
- return AAUDIO_ERROR_UNEXPECTED_VALUE;
+ int32_t numBytes = getBytesPerFrame() * framesToWrite;
+ int32_t numSamples = framesToWrite * getSamplesPerFrame();
+ // Data conversion.
+ float levelFrom;
+ float levelTo;
+ bool ramping = mVolumeRamp.nextSegment(framesToWrite * getSamplesPerFrame(),
+ &levelFrom, &levelTo);
+ // The formats are validated when the stream is opened so we do not have to
+ // check for illegal combinations here.
+ if (getFormat() == AAUDIO_FORMAT_PCM_FLOAT) {
+ if (mDeviceFormat == AAUDIO_FORMAT_PCM_FLOAT) {
+ AAudio_linearRamp(
+ (const float *) source,
+ (float *) wrappingBuffer.data[partIndex],
+ framesToWrite,
+ getSamplesPerFrame(),
+ levelFrom,
+ levelTo);
+ } else if (mDeviceFormat == AAUDIO_FORMAT_PCM_I16) {
+ if (ramping) {
+ AAudioConvert_floatToPcm16(
+ (const float *) source,
+ (int16_t *) wrappingBuffer.data[partIndex],
+ framesToWrite,
+ getSamplesPerFrame(),
+ levelFrom,
+ levelTo);
+ } else {
+ AAudioConvert_floatToPcm16(
+ (const float *) source,
+ (int16_t *) wrappingBuffer.data[partIndex],
+ numSamples,
+ levelTo);
+ }
+ }
+ } else if (getFormat() == AAUDIO_FORMAT_PCM_I16) {
+ if (mDeviceFormat == AAUDIO_FORMAT_PCM_FLOAT) {
+ if (ramping) {
+ AAudioConvert_pcm16ToFloat(
+ (const int16_t *) source,
+ (float *) wrappingBuffer.data[partIndex],
+ framesToWrite,
+ getSamplesPerFrame(),
+ levelFrom,
+ levelTo);
+ } else {
+ AAudioConvert_pcm16ToFloat(
+ (const int16_t *) source,
+ (float *) wrappingBuffer.data[partIndex],
+ numSamples,
+ levelTo);
+ }
+ } else if (mDeviceFormat == AAUDIO_FORMAT_PCM_I16) {
+ AAudio_linearRamp(
+ (const int16_t *) source,
+ (int16_t *) wrappingBuffer.data[partIndex],
+ framesToWrite,
+ getSamplesPerFrame(),
+ levelFrom,
+ levelTo);
+ }
}
-
source += numBytes;
framesLeft -= framesToWrite;
+ } else {
+ break;
}
partIndex++;
}
@@ -670,7 +732,7 @@
if (framesWritten > 0) {
incrementFramesWritten(framesWritten);
}
- // ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::writeNowWithConversion() returns %d", framesWritten);
+ // ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal::writeNowWithConversion() returns %d", framesWritten);
return framesWritten;
}
@@ -680,7 +742,15 @@
aaudio_result_t AudioStreamInternal::setBufferSize(int32_t requestedFrames) {
int32_t actualFrames = 0;
+ // Round to the next highest burst size.
+ if (getFramesPerBurst() > 0) {
+ int32_t numBursts = (requestedFrames + getFramesPerBurst() - 1) / getFramesPerBurst();
+ requestedFrames = numBursts * getFramesPerBurst();
+ }
+
aaudio_result_t result = mAudioEndpoint.setBufferSizeInFrames(requestedFrames, &actualFrames);
+ ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal::setBufferSize() %s req = %d => %d",
+ getLocationName(), requestedFrames, actualFrames);
if (result < 0) {
return result;
} else {
@@ -714,8 +784,14 @@
} else {
mLastFramesRead = framesRead;
}
- ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::getFramesRead() returns %lld", (long long)framesRead);
+ ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal::getFramesRead() returns %lld", (long long)framesRead);
return framesRead;
}
-// TODO implement getTimestamp
+int64_t AudioStreamInternal::getFramesWritten()
+{
+ int64_t getFramesWritten = mAudioEndpoint.getDownDataWriteCounter()
+ + mFramesOffsetFromService;
+ ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal::getFramesWritten() returns %lld", (long long)getFramesWritten);
+ return getFramesWritten;
+}
diff --git a/media/libaaudio/src/client/AudioStreamInternal.h b/media/libaaudio/src/client/AudioStreamInternal.h
index 1aa3b0f..ee602c1 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.h
+++ b/media/libaaudio/src/client/AudioStreamInternal.h
@@ -14,19 +14,19 @@
* 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>
#include "binding/IAAudioService.h"
#include "binding/AudioEndpointParcelable.h"
+#include "binding/AAudioServiceInterface.h"
#include "client/IsochronousClockModel.h"
#include "client/AudioEndpoint.h"
#include "core/AudioStream.h"
-
-#include "binding/AAudioServiceInterface.h"
+#include "utility/LinearRamp.h"
using android::sp;
using android::IAAudioService;
@@ -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;
@@ -77,6 +73,7 @@
int32_t getFramesPerBurst() const override;
int64_t getFramesRead() override;
+ int64_t getFramesWritten() override;
int32_t getXRunCount() const override {
return mXRunCount;
@@ -89,11 +86,17 @@
// Called internally from 'C'
void *callbackLoop();
+
+ bool isMMap() override {
+ return true;
+ }
+
protected:
aaudio_result_t processCommands();
aaudio_result_t requestPauseInternal();
+ aaudio_result_t requestStopInternal();
aaudio_result_t stopCallback();
@@ -129,6 +132,11 @@
int32_t numFrames);
void processTimestamp(uint64_t position, int64_t time);
+
+ const char *getLocationName() const {
+ return mInService ? "SERVICE" : "CLIENT";
+ }
+
// Adjust timing model based on timestamp from service.
IsochronousClockModel mClockModel; // timing model for chasing the HAL
@@ -148,7 +156,7 @@
int64_t mLastFramesRead = 0; // used to prevent retrograde motion
int32_t mFramesPerBurst; // frames per HAL transfer
int32_t mXRunCount = 0; // how many underrun events?
- float mVolume = 1.0; // volume that the server told us to use
+ LinearRamp mVolumeRamp;
AAudioServiceInterface &mServiceInterface; // abstract interface to the service
@@ -158,4 +166,4 @@
} /* namespace aaudio */
-#endif //AAUDIO_AUDIOSTREAMINTERNAL_H
+#endif //ANDROID_AAUDIO_AUDIO_STREAM_INTERNAL_H
diff --git a/media/libaaudio/src/client/IsochronousClockModel.cpp b/media/libaaudio/src/client/IsochronousClockModel.cpp
index c278c8b..21e3e70 100644
--- a/media/libaaudio/src/client/IsochronousClockModel.cpp
+++ b/media/libaaudio/src/client/IsochronousClockModel.cpp
@@ -101,13 +101,13 @@
// or we may be drifting due to a slow HW clock.
mMarkerFramePosition = framePosition;
mMarkerNanoTime = nanoTime;
- ALOGI("processTimestamp() - STATE_RUNNING - %d < %d micros - EARLY",
+ ALOGV("processTimestamp() - STATE_RUNNING - %d < %d micros - EARLY",
(int) (nanosDelta / 1000), (int)(expectedNanosDelta / 1000));
} else if (nanosDelta > (expectedNanosDelta + mMaxLatenessInNanos)) {
// Later than expected timestamp.
mMarkerFramePosition = framePosition;
mMarkerNanoTime = nanoTime - mMaxLatenessInNanos;
- ALOGI("processTimestamp() - STATE_RUNNING - %d > %d + %d micros - LATE",
+ ALOGV("processTimestamp() - STATE_RUNNING - %d > %d + %d micros - LATE",
(int) (nanosDelta / 1000), (int)(expectedNanosDelta / 1000),
(int) (mMaxLatenessInNanos / 1000));
}
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/AAudioAudio.cpp b/media/libaaudio/src/core/AAudioAudio.cpp
index 9d69423..59032d5 100644
--- a/media/libaaudio/src/core/AAudioAudio.cpp
+++ b/media/libaaudio/src/core/AAudioAudio.cpp
@@ -114,8 +114,15 @@
return AAUDIO_OK;
}
+AAUDIO_API void AAudioStreamBuilder_setPerformanceMode(AAudioStreamBuilder* builder,
+ aaudio_performance_mode_t mode)
+{
+ AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
+ streamBuilder->setPerformanceMode(mode);
+}
+
AAUDIO_API void AAudioStreamBuilder_setDeviceId(AAudioStreamBuilder* builder,
- int32_t deviceId)
+ int32_t deviceId)
{
AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
streamBuilder->setDeviceId(deviceId);
@@ -128,8 +135,15 @@
streamBuilder->setSampleRate(sampleRate);
}
+AAUDIO_API void AAudioStreamBuilder_setChannelCount(AAudioStreamBuilder* builder,
+ int32_t channelCount)
+{
+ AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
+ streamBuilder->setSamplesPerFrame(channelCount);
+}
+
AAUDIO_API void AAudioStreamBuilder_setSamplesPerFrame(AAudioStreamBuilder* builder,
- int32_t samplesPerFrame)
+ int32_t samplesPerFrame)
{
AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
streamBuilder->setSamplesPerFrame(samplesPerFrame);
@@ -168,16 +182,15 @@
void *userData)
{
AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
- ALOGD("AAudioStreamBuilder_setCallback(): userData = %p", userData);
streamBuilder->setDataCallbackProc(callback);
streamBuilder->setDataCallbackUserData(userData);
}
+
AAUDIO_API void AAudioStreamBuilder_setErrorCallback(AAudioStreamBuilder* builder,
AAudioStream_errorCallback callback,
void *userData)
{
AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
- ALOGD("AAudioStreamBuilder_setCallback(): userData = %p", userData);
streamBuilder->setErrorCallbackProc(callback);
streamBuilder->setErrorCallbackUserData(userData);
}
@@ -186,29 +199,24 @@
int32_t frames)
{
AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
- ALOGD("%s: frames = %d", __func__, frames);
streamBuilder->setFramesPerDataCallback(frames);
}
-static aaudio_result_t AAudioInternal_openStream(AudioStreamBuilder *streamBuilder,
- AAudioStream** streamPtr)
-{
- AudioStream *audioStream = nullptr;
- aaudio_result_t result = streamBuilder->build(&audioStream);
- if (result != AAUDIO_OK) {
- return result;
- } else {
- *streamPtr = (AAudioStream*) audioStream;
- return AAUDIO_OK;
- }
-}
-
AAUDIO_API aaudio_result_t AAudioStreamBuilder_openStream(AAudioStreamBuilder* builder,
AAudioStream** streamPtr)
{
- ALOGD("AAudioStreamBuilder_openStream(): builder = %p", builder);
+ AudioStream *audioStream = nullptr;
+ ALOGD("AAudioStreamBuilder_openStream() ----------------------------------------------");
AudioStreamBuilder *streamBuilder = COMMON_GET_FROM_BUILDER_OR_RETURN(streamPtr);
- return AAudioInternal_openStream(streamBuilder, streamPtr);
+ aaudio_result_t result = streamBuilder->build(&audioStream);
+ ALOGD("AAudioStreamBuilder_openStream() returns %d -----------------------------------",
+ result);
+ if (result == AAUDIO_OK) {
+ *streamPtr = (AAudioStream*) audioStream;
+ } else {
+ *streamPtr = nullptr;
+ }
+ return result;
}
AAUDIO_API aaudio_result_t AAudioStreamBuilder_delete(AAudioStreamBuilder* builder)
@@ -228,6 +236,7 @@
if (audioStream != nullptr) {
audioStream->close();
delete audioStream;
+ ALOGD("AAudioStream_close() ----------------------------------------------");
return AAUDIO_OK;
}
return AAUDIO_ERROR_INVALID_HANDLE;
@@ -334,6 +343,12 @@
return audioStream->getSampleRate();
}
+AAUDIO_API int32_t AAudioStream_getChannelCount(AAudioStream* stream)
+{
+ AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
+ return audioStream->getSamplesPerFrame();
+}
+
AAUDIO_API int32_t AAudioStream_getSamplesPerFrame(AAudioStream* stream)
{
AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
@@ -395,6 +410,12 @@
return audioStream->getXRunCount();
}
+AAUDIO_API aaudio_performance_mode_t AAudioStream_getPerformanceMode(AAudioStream* stream)
+{
+ AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
+ return audioStream->getPerformanceMode();
+}
+
AAUDIO_API int32_t AAudioStream_getDeviceId(AAudioStream* stream)
{
AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
diff --git a/media/libaaudio/src/core/AudioStream.cpp b/media/libaaudio/src/core/AudioStream.cpp
index 7c0b5ae..d1698bf 100644
--- a/media/libaaudio/src/core/AudioStream.cpp
+++ b/media/libaaudio/src/core/AudioStream.cpp
@@ -38,7 +38,6 @@
aaudio_result_t AudioStream::open(const AudioStreamBuilder& builder)
{
-
// Copy parameters from the Builder because the Builder may be deleted after this call.
mSamplesPerFrame = builder.getSamplesPerFrame();
mSampleRate = builder.getSampleRate();
@@ -46,23 +45,58 @@
mFormat = builder.getFormat();
mDirection = builder.getDirection();
mSharingMode = builder.getSharingMode();
+ mSharingModeMatchRequired = builder.isSharingModeMatchRequired();
+
+ mPerformanceMode = builder.getPerformanceMode();
// callbacks
mFramesPerDataCallback = builder.getFramesPerDataCallback();
mDataCallbackProc = builder.getDataCallbackProc();
mErrorCallbackProc = builder.getErrorCallbackProc();
mDataCallbackUserData = builder.getDataCallbackUserData();
+ mErrorCallbackUserData = builder.getErrorCallbackUserData();
- // TODO validate more parameters.
- if (mErrorCallbackProc != nullptr && mDataCallbackProc == nullptr) {
- ALOGE("AudioStream::open(): disconnect callback cannot be used without a data callback.");
- return AAUDIO_ERROR_UNEXPECTED_VALUE;
+ // This is very helpful for debugging in the future.
+ ALOGI("AudioStream.open(): rate = %d, channels = %d, format = %d, sharing = %d",
+ mSampleRate, mSamplesPerFrame, mFormat, mSharingMode);
+
+ // Check for values that are ridiculously out of range to prevent math overflow exploits.
+ // The service will do a better check.
+ if (mSamplesPerFrame < 0 || mSamplesPerFrame > 128) {
+ 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;
}
if (mDirection != AAUDIO_DIRECTION_INPUT && mDirection != AAUDIO_DIRECTION_OUTPUT) {
ALOGE("AudioStream::open(): illegal direction %d", mDirection);
return AAUDIO_ERROR_UNEXPECTED_VALUE;
}
+ switch(mPerformanceMode) {
+ case AAUDIO_PERFORMANCE_MODE_NONE:
+ case AAUDIO_PERFORMANCE_MODE_POWER_SAVING:
+ case AAUDIO_PERFORMANCE_MODE_LOW_LATENCY:
+ break;
+ default:
+ ALOGE("AudioStream::open(): illegal performanceMode %d", mPerformanceMode);
+ return AAUDIO_ERROR_UNEXPECTED_VALUE;
+ }
+
return AAUDIO_OK;
}
@@ -70,27 +104,6 @@
close();
}
-aaudio_result_t AudioStream::waitForStateTransition(aaudio_stream_state_t startingState,
- aaudio_stream_state_t endingState,
- int64_t timeoutNanoseconds)
-{
- aaudio_stream_state_t state = getState();
- aaudio_stream_state_t nextState = state;
- if (state == startingState && state != endingState) {
- aaudio_result_t result = waitForStateChange(state, &nextState, timeoutNanoseconds);
- if (result != AAUDIO_OK) {
- return result;
- }
- }
-// It's OK if the expected transition has already occurred.
-// But if we reach an unexpected state then that is an error.
- if (nextState != endingState) {
- return AAUDIO_ERROR_UNEXPECTED_STATE;
- } else {
- return AAUDIO_OK;
- }
-}
-
aaudio_result_t AudioStream::waitForStateChange(aaudio_stream_state_t currentState,
aaudio_stream_state_t *nextState,
int64_t timeoutNanoseconds)
@@ -100,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) {
@@ -123,16 +135,15 @@
return (state == currentState) ? AAUDIO_ERROR_TIMEOUT : AAUDIO_OK;
}
-// This registers the app's background audio thread with the server before
+// This registers the callback thread with the server before
// passing control to the app. This gives the server an opportunity to boost
// the thread's performance characteristics.
void* AudioStream::wrapUserThread() {
void* procResult = nullptr;
mThreadRegistrationResult = registerThread();
if (mThreadRegistrationResult == AAUDIO_OK) {
- // Call application procedure. This may take a very long time.
+ // Run callback loop. This may take a very long time.
procResult = mThreadProc(mThreadArg);
- ALOGD("AudioStream::mThreadProc() returned");
mThreadRegistrationResult = unregisterThread();
}
return procResult;
@@ -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/core/AudioStream.h b/media/libaaudio/src/core/AudioStream.h
index 073b9a1..c49b46b 100644
--- a/media/libaaudio/src/core/AudioStream.h
+++ b/media/libaaudio/src/core/AudioStream.h
@@ -134,6 +134,10 @@
return mState == AAUDIO_STREAM_STATE_STARTING || mState == AAUDIO_STREAM_STATE_STARTED;
}
+ virtual bool isMMap() {
+ return false;
+ }
+
aaudio_result_t getSampleRate() const {
return mSampleRate;
}
@@ -146,6 +150,14 @@
return mSamplesPerFrame;
}
+ virtual int32_t getPerformanceMode() const {
+ return mPerformanceMode;
+ }
+
+ void setPerformanceMode(aaudio_performance_mode_t performanceMode) {
+ mPerformanceMode = performanceMode;
+ }
+
int32_t getDeviceId() const {
return mDeviceId;
}
@@ -154,6 +166,10 @@
return mSharingMode;
}
+ bool isSharingModeMatchRequired() const {
+ return mSharingModeMatchRequired;
+ }
+
aaudio_direction_t getDirection() const {
return mDirection;
}
@@ -219,24 +235,14 @@
protected:
virtual int64_t incrementFramesWritten(int32_t frames) {
- return static_cast<int64_t>(mFramesWritten.increment(frames));
+ return mFramesWritten.increment(frames);
}
virtual int64_t incrementFramesRead(int32_t frames) {
- return static_cast<int64_t>(mFramesRead.increment(frames));
+ return mFramesRead.increment(frames);
}
/**
- * Wait for a transition from one state to another.
- * @return AAUDIO_OK if the endingState was observed, or AAUDIO_ERROR_UNEXPECTED_STATE
- * if any state that was not the startingState or endingState was observed
- * or AAUDIO_ERROR_TIMEOUT
- */
- virtual aaudio_result_t waitForStateTransition(aaudio_stream_state_t startingState,
- aaudio_stream_state_t endingState,
- int64_t timeoutNanoseconds);
-
- /**
* This should not be called after the open() call.
*/
void setSampleRate(int32_t sampleRate) {
@@ -294,10 +300,13 @@
int32_t mSampleRate = AAUDIO_UNSPECIFIED;
int32_t mDeviceId = AAUDIO_UNSPECIFIED;
aaudio_sharing_mode_t mSharingMode = AAUDIO_SHARING_MODE_SHARED;
+ bool mSharingModeMatchRequired = false; // must match sharing mode requested
aaudio_audio_format_t mFormat = AAUDIO_FORMAT_UNSPECIFIED;
aaudio_direction_t mDirection = AAUDIO_DIRECTION_OUTPUT;
aaudio_stream_state_t mState = AAUDIO_STREAM_STATE_UNINITIALIZED;
+ aaudio_performance_mode_t mPerformanceMode = AAUDIO_PERFORMANCE_MODE_NONE;
+
// callback ----------------------------------
AAudioStream_dataCallback mDataCallbackProc = nullptr; // external callback functions
diff --git a/media/libaaudio/src/core/AudioStreamBuilder.cpp b/media/libaaudio/src/core/AudioStreamBuilder.cpp
index b135a4b..9bc2ef2 100644
--- a/media/libaaudio/src/core/AudioStreamBuilder.cpp
+++ b/media/libaaudio/src/core/AudioStreamBuilder.cpp
@@ -30,9 +30,10 @@
#include "legacy/AudioStreamRecord.h"
#include "legacy/AudioStreamTrack.h"
-// Enable a mixer in AAudio service that will mix stream to an ALSA MMAP buffer.
+// Enable a mixer in AAudio service that will mix streams to an ALSA MMAP buffer.
#define MMAP_SHARED_ENABLED 0
-// Enable AAUDIO_SHARING_MODE_EXCLUSIVE that uses an ALSA MMAP buffer.
+
+// Enable AAUDIO_SHARING_MODE_EXCLUSIVE that uses an ALSA MMAP buffer directly.
#define MMAP_EXCLUSIVE_ENABLED 0
using namespace aaudio;
@@ -46,66 +47,80 @@
AudioStreamBuilder::~AudioStreamBuilder() {
}
+static aaudio_result_t builder_createStream(aaudio_direction_t direction,
+ aaudio_sharing_mode_t sharingMode,
+ bool tryMMap,
+ AudioStream **audioStreamPtr) {
+ *audioStreamPtr = nullptr;
+ aaudio_result_t result = AAUDIO_OK;
+ switch (direction) {
+
+ case AAUDIO_DIRECTION_INPUT:
+ if (sharingMode == AAUDIO_SHARING_MODE_SHARED) {
+ *audioStreamPtr = new AudioStreamRecord();
+ } else {
+ ALOGE("AudioStreamBuilder(): bad sharing mode = %d for input", sharingMode);
+ result = AAUDIO_ERROR_ILLEGAL_ARGUMENT;
+ }
+ break;
+
+ case AAUDIO_DIRECTION_OUTPUT:
+ if (tryMMap) {
+ // TODO use a singleton for the AAudioBinderClient
+ AAudioBinderClient *aaudioClient = new AAudioBinderClient();
+ *audioStreamPtr = new AudioStreamInternal(*aaudioClient, false);
+ } else {
+ *audioStreamPtr = new AudioStreamTrack();
+ }
+ break;
+
+ default:
+ ALOGE("AudioStreamBuilder(): bad direction = %d", direction);
+ result = AAUDIO_ERROR_ILLEGAL_ARGUMENT;
+ }
+ return result;
+}
+
aaudio_result_t AudioStreamBuilder::build(AudioStream** streamPtr) {
- AudioStream* audioStream = nullptr;
- AAudioBinderClient *aaudioClient = nullptr;
- const aaudio_sharing_mode_t sharingMode = getSharingMode();
- ALOGD("AudioStreamBuilder.build() sharingMode = %d", sharingMode);
- switch (getDirection()) {
+ aaudio_sharing_mode_t sharingMode = getSharingMode();
+ if ((sharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE) && (MMAP_EXCLUSIVE_ENABLED == 0)) {
+ ALOGE("AudioStreamBuilder(): EXCLUSIVE sharing mode not supported");
+ return AAUDIO_ERROR_UNAVAILABLE;
+ }
- case AAUDIO_DIRECTION_INPUT:
- switch (sharingMode) {
- case AAUDIO_SHARING_MODE_SHARED:
- audioStream = new(std::nothrow) AudioStreamRecord();
- break;
- default:
- ALOGE("AudioStreamBuilder(): bad sharing mode = %d", sharingMode);
- return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
- break;
+ AudioStream *audioStream = nullptr;
+ *streamPtr = nullptr;
+
+ bool tryMMap = (sharingMode == AAUDIO_SHARING_MODE_SHARED) && MMAP_SHARED_ENABLED;
+ aaudio_result_t result = builder_createStream(getDirection(), sharingMode,
+ tryMMap, &audioStream);
+ if (result == AAUDIO_OK) {
+ // Open the stream using the parameters from the builder.
+ result = audioStream->open(*this);
+ if (result == AAUDIO_OK) {
+ *streamPtr = audioStream;
+ } else {
+ bool isMMap = audioStream->isMMap();
+ delete audioStream;
+ audioStream = nullptr;
+
+ if (isMMap) {
+ ALOGD("AudioStreamBuilder.build() MMAP stream did not open so try Legacy path");
+ // If MMAP stream failed to open then TRY using a legacy stream.
+ result = builder_createStream(getDirection(), sharingMode,
+ false, &audioStream);
+ if (result == AAUDIO_OK) {
+ result = audioStream->open(*this);
+ if (result == AAUDIO_OK) {
+ *streamPtr = audioStream;
+ } else {
+ delete audioStream;
+ }
+ }
+ }
}
- break;
-
- case AAUDIO_DIRECTION_OUTPUT:
- switch (sharingMode) {
- case AAUDIO_SHARING_MODE_SHARED:
-#if MMAP_SHARED_ENABLED
- aaudioClient = new AAudioBinderClient();
- audioStream = new(std::nothrow) AudioStreamInternal(*aaudioClient, false);
-#else
- audioStream = new(std::nothrow) AudioStreamTrack();
-#endif
- break;
-#if MMAP_EXCLUSIVE_ENABLED
- case AAUDIO_SHARING_MODE_EXCLUSIVE:
- aaudioClient = new AAudioBinderClient();
- audioStream = new(std::nothrow) AudioStreamInternal(*aaudioClient, false);
- break;
-#endif
- default:
- ALOGE("AudioStreamBuilder(): bad sharing mode = %d", sharingMode);
- return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
- break;
- }
- break;
-
- default:
- ALOGE("AudioStreamBuilder(): bad direction = %d", getDirection());
- return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
- break;
}
- if (audioStream == nullptr) {
- delete aaudioClient;
- return AAUDIO_ERROR_NO_MEMORY;
- }
- ALOGD("AudioStreamBuilder(): created audioStream = %p", audioStream);
- // TODO maybe move this out of build and pass the builder to the constructors
- // Open the stream using the parameters from the builder.
- const aaudio_result_t result = audioStream->open(*this);
- if (result != AAUDIO_OK) {
- delete audioStream;
- } else {
- *streamPtr = audioStream;
- }
+ ALOGD("AudioStreamBuilder(): returned %d", result);
return result;
}
diff --git a/media/libaaudio/src/core/AudioStreamBuilder.h b/media/libaaudio/src/core/AudioStreamBuilder.h
index c0ee6fe..569ca63 100644
--- a/media/libaaudio/src/core/AudioStreamBuilder.h
+++ b/media/libaaudio/src/core/AudioStreamBuilder.h
@@ -82,6 +82,15 @@
return this;
}
+ bool isSharingModeMatchRequired() const {
+ return mSharingModeMatchRequired;
+ }
+
+ AudioStreamBuilder* setSharingModeMatchRequired(bool required) {
+ mSharingModeMatchRequired = required;
+ return this;
+ }
+
int32_t getBufferCapacity() const {
return mBufferCapacity;
}
@@ -91,6 +100,15 @@
return this;
}
+ int32_t getPerformanceMode() const {
+ return mPerformanceMode;
+ }
+
+ AudioStreamBuilder* setPerformanceMode(aaudio_performance_mode_t performanceMode) {
+ mPerformanceMode = performanceMode;
+ return this;
+ }
+
int32_t getDeviceId() const {
return mDeviceId;
}
@@ -109,7 +127,6 @@
return this;
}
-
void *getDataCallbackUserData() const {
return mDataCallbackUserData;
}
@@ -149,13 +166,15 @@
aaudio_result_t build(AudioStream **streamPtr);
private:
- int32_t mSamplesPerFrame = AAUDIO_UNSPECIFIED;
- int32_t mSampleRate = AAUDIO_UNSPECIFIED;
- int32_t mDeviceId = AAUDIO_DEVICE_UNSPECIFIED;
- aaudio_sharing_mode_t mSharingMode = AAUDIO_SHARING_MODE_SHARED;
- aaudio_audio_format_t mFormat = AAUDIO_FORMAT_UNSPECIFIED;
- aaudio_direction_t mDirection = AAUDIO_DIRECTION_OUTPUT;
- int32_t mBufferCapacity = AAUDIO_UNSPECIFIED;
+ int32_t mSamplesPerFrame = AAUDIO_UNSPECIFIED;
+ int32_t mSampleRate = AAUDIO_UNSPECIFIED;
+ int32_t mDeviceId = AAUDIO_DEVICE_UNSPECIFIED;
+ aaudio_sharing_mode_t mSharingMode = AAUDIO_SHARING_MODE_SHARED;
+ bool mSharingModeMatchRequired = false; // must match sharing mode requested
+ aaudio_audio_format_t mFormat = AAUDIO_FORMAT_UNSPECIFIED;
+ aaudio_direction_t mDirection = AAUDIO_DIRECTION_OUTPUT;
+ int32_t mBufferCapacity = AAUDIO_UNSPECIFIED;
+ aaudio_performance_mode_t mPerformanceMode = AAUDIO_PERFORMANCE_MODE_NONE;
AAudioStream_dataCallback mDataCallbackProc = nullptr; // external callback functions
void *mDataCallbackUserData = nullptr;
diff --git a/media/libaaudio/src/fifo/FifoBuffer.cpp b/media/libaaudio/src/fifo/FifoBuffer.cpp
index 857780c..6b4a772 100644
--- a/media/libaaudio/src/fifo/FifoBuffer.cpp
+++ b/media/libaaudio/src/fifo/FifoBuffer.cpp
@@ -60,14 +60,11 @@
, mFramesUnderrunCount(0)
, mUnderrunCount(0)
{
- // TODO Handle possible failures to allocate. Move out of constructor?
mFifo = new FifoControllerIndirect(capacityInFrames,
capacityInFrames,
readIndexAddress,
writeIndexAddress);
mStorageOwned = false;
- ALOGD("FifoProcessor: capacityInFrames = %d, bytesPerFrame = %d",
- capacityInFrames, bytesPerFrame);
}
FifoBuffer::~FifoBuffer() {
@@ -132,8 +129,6 @@
while (framesLeft > 0 && partIndex < WrappingBuffer::SIZE) {
fifo_frames_t framesToRead = framesLeft;
fifo_frames_t framesAvailable = wrappingBuffer.numFrames[partIndex];
- //ALOGD("FifoProcessor::read() framesAvailable = %d, partIndex = %d",
- // framesAvailable, partIndex);
if (framesAvailable > 0) {
if (framesToRead > framesAvailable) {
framesToRead = framesAvailable;
@@ -143,6 +138,8 @@
destination += numBytes;
framesLeft -= framesToRead;
+ } else {
+ break;
}
partIndex++;
}
@@ -172,6 +169,8 @@
source += numBytes;
framesLeft -= framesToWrite;
+ } else {
+ break;
}
partIndex++;
}
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
index baa24c9..f89234a 100644
--- a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
@@ -83,6 +83,7 @@
}
if (callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE) {
audioBuffer->size = audioBuffer->frameCount * getBytesPerFrame();
+ incrementClientFrameCounter(audioBuffer->frameCount);
} else {
audioBuffer->size = 0;
}
@@ -108,3 +109,23 @@
}
}
+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 c109ee7..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>
@@ -70,7 +71,15 @@
// Implement FixedBlockProcessor
int32_t onProcessFixedBlock(uint8_t *buffer, int32_t numBytes) override;
+ 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/AudioStreamRecord.h b/media/libaaudio/src/legacy/AudioStreamRecord.h
index 897a5b3..f4a78e1 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.h
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.h
@@ -68,6 +68,10 @@
// This is public so it can be called from the C callback function.
void processCallback(int event, void *info) override;
+ int64_t incrementClientFrameCounter(int32_t frames) override {
+ return incrementFramesRead(frames);
+ }
+
private:
android::sp<android::AudioRecord> mAudioRecord;
// adapts between variable sized blocks and fixed size blocks
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
index 1bb9e53..a7c7673 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
@@ -61,22 +61,36 @@
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);
ALOGD("AudioStreamTrack::open(), samplesPerFrame = %d, channelMask = 0x%08x",
samplesPerFrame, channelMask);
- // TODO add more performance options
- audio_output_flags_t flags = (audio_output_flags_t) AUDIO_OUTPUT_FLAG_FAST;
+ audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE;
+ switch(getPerformanceMode()) {
+ case AAUDIO_PERFORMANCE_MODE_LOW_LATENCY:
+ // Bypass the normal mixer and go straight to the FAST mixer.
+ flags = (audio_output_flags_t)(AUDIO_OUTPUT_FLAG_FAST | AUDIO_OUTPUT_FLAG_RAW);
+ break;
+
+ case AAUDIO_PERFORMANCE_MODE_POWER_SAVING:
+ // This uses a mixer that wakes up less often than the FAST mixer.
+ flags = AUDIO_OUTPUT_FLAG_DEEP_BUFFER;
+ break;
+
+ case AAUDIO_PERFORMANCE_MODE_NONE:
+ default:
+ // No flags. Use a normal mixer in front of the FAST mixer.
+ break;
+ }
int32_t frameCount = builder.getBufferCapacity();
ALOGD("AudioStreamTrack::open(), requested buffer capacity %d", frameCount);
int32_t notificationFrames = 0;
- // TODO implement an unspecified AudioTrack format then use that.
audio_format_t format = (getFormat() == AAUDIO_FORMAT_UNSPECIFIED)
? AUDIO_FORMAT_PCM_FLOAT
: AAudioConvert_aaudioToAndroidDataFormat(getFormat());
@@ -149,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();
@@ -276,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);
}
}
@@ -354,6 +367,8 @@
case AAUDIO_STREAM_STATE_STARTING:
case AAUDIO_STREAM_STATE_STARTED:
case AAUDIO_STREAM_STATE_STOPPING:
+ case AAUDIO_STREAM_STATE_PAUSING:
+ case AAUDIO_STREAM_STATE_PAUSED:
result = mAudioTrack->getPosition(&position);
if (result == OK) {
mFramesRead.update32(position);
@@ -373,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/legacy/AudioStreamTrack.h b/media/libaaudio/src/legacy/AudioStreamTrack.h
index 29f5d15..186a08e 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.h
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.h
@@ -68,6 +68,10 @@
// This is public so it can be called from the C callback function.
void processCallback(int event, void *info) override;
+ int64_t incrementClientFrameCounter(int32_t frames) override {
+ return incrementFramesWritten(frames);
+ }
+
private:
android::sp<android::AudioTrack> mAudioTrack;
diff --git a/media/libaaudio/src/utility/AAudioUtilities.cpp b/media/libaaudio/src/utility/AAudioUtilities.cpp
index 87b8b0d..be2bd10 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.cpp
+++ b/media/libaaudio/src/utility/AAudioUtilities.cpp
@@ -27,6 +27,11 @@
using namespace android;
+// This is 3 dB, (10^(3/20)), to match the maximum headroom in AudioTrack for float data.
+// It is designed to allow occasional transient peaks.
+#define MAX_HEADROOM (1.41253754f)
+#define MIN_HEADROOM (0 - MAX_HEADROOM)
+
int32_t AAudioConvert_formatToSizeInBytes(aaudio_audio_format_t format) {
int32_t size = AAUDIO_ERROR_ILLEGAL_ARGUMENT;
switch (format) {
@@ -42,24 +47,153 @@
return size;
}
-// TODO This similar to a function in audio_utils. Consider using that instead.
-void AAudioConvert_floatToPcm16(const float *source, int32_t numSamples, int16_t *destination) {
+
+// 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
+ * is used to multiply by 32768.
+ */
+ static const float offset = (float)(3 << (22 - 15));
+ /* zero = (0x10f << 22) = 0x43c00000 (not directly used) */
+ static const int32_t limneg = (0x10f << 22) /*zero*/ - 32768; /* 0x43bf8000 */
+ static const int32_t limpos = (0x10f << 22) /*zero*/ + 32767; /* 0x43c07fff */
+
+ union {
+ float f;
+ int32_t i;
+ } u;
+
+ u.f = f + offset; /* recenter valid range */
+ /* Now the valid range is represented as integers between [limneg, limpos].
+ * Clamp using the fact that float representation (as an integer) is an ordered set.
+ */
+ if (u.i < limneg)
+ u.i = -32768;
+ else if (u.i > limpos)
+ u.i = 32767;
+ return u.i; /* Return lower 16 bits, the part of interest in the significand. */
+}
+
+// Same but without clipping.
+// Convert -1.0f to +1.0f to -32768 to +32767
+static inline int16_t floatToInt16(float f) {
+ static const float offset = (float)(3 << (22 - 15));
+ union {
+ float f;
+ int32_t i;
+ } u;
+ u.f = f + offset; /* recenter valid range */
+ return u.i; /* Return lower 16 bits, the part of interest in the significand. */
+}
+
+static float clipAndClampFloatToPcm16(float sample, float scaler) {
+ // Clip to valid range of a float sample to prevent excessive volume.
+ if (sample > MAX_HEADROOM) sample = MAX_HEADROOM;
+ else if (sample < MIN_HEADROOM) sample = MIN_HEADROOM;
+
+ // Scale and convert to a short.
+ float fval = sample * scaler;
+ return clamp16_from_float(fval);
+}
+
+void AAudioConvert_floatToPcm16(const float *source,
+ int16_t *destination,
+ int32_t numSamples,
+ float amplitude) {
+ float scaler = amplitude;
for (int i = 0; i < numSamples; i++) {
- float fval = source[i];
- fval += 1.0; // to avoid discontinuity at 0.0 caused by truncation
- fval *= 32768.0f;
- int32_t sample = (int32_t) fval;
- // clip to 16-bit range
- if (sample < 0) sample = 0;
- else if (sample > 0x0FFFF) sample = 0x0FFFF;
- sample -= 32768; // center at zero
- destination[i] = (int16_t) sample;
+ float sample = *source++;
+ *destination++ = clipAndClampFloatToPcm16(sample, scaler);
}
}
-void AAudioConvert_pcm16ToFloat(const float *source, int32_t numSamples, int16_t *destination) {
+void AAudioConvert_floatToPcm16(const float *source,
+ int16_t *destination,
+ int32_t numFrames,
+ int32_t samplesPerFrame,
+ float amplitude1,
+ float amplitude2) {
+ float scaler = amplitude1;
+ // divide by numFrames so that we almost reach amplitude2
+ float delta = (amplitude2 - amplitude1) / numFrames;
+ for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
+ for (int sampleIndex = 0; sampleIndex < samplesPerFrame; sampleIndex++) {
+ float sample = *source++;
+ *destination++ = clipAndClampFloatToPcm16(sample, scaler);
+ }
+ scaler += delta;
+ }
+}
+
+#define SHORT_SCALE 32768
+
+void AAudioConvert_pcm16ToFloat(const int16_t *source,
+ float *destination,
+ int32_t numSamples,
+ float amplitude) {
+ float scaler = amplitude / SHORT_SCALE;
for (int i = 0; i < numSamples; i++) {
- destination[i] = source[i] * (1.0f / 32768.0f);
+ destination[i] = source[i] * scaler;
+ }
+}
+
+// This code assumes amplitude1 and amplitude2 are between 0.0 and 1.0
+void AAudioConvert_pcm16ToFloat(const int16_t *source,
+ float *destination,
+ int32_t numFrames,
+ int32_t samplesPerFrame,
+ float amplitude1,
+ float amplitude2) {
+ float scaler = amplitude1 / SHORT_SCALE;
+ float delta = (amplitude2 - amplitude1) / (SHORT_SCALE * (float) numFrames);
+ for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
+ for (int sampleIndex = 0; sampleIndex < samplesPerFrame; sampleIndex++) {
+ *destination++ = *source++ * scaler;
+ }
+ scaler += delta;
+ }
+}
+
+// This code assumes amplitude1 and amplitude2 are between 0.0 and 1.0
+void AAudio_linearRamp(const float *source,
+ float *destination,
+ int32_t numFrames,
+ int32_t samplesPerFrame,
+ float amplitude1,
+ float amplitude2) {
+ float scaler = amplitude1;
+ float delta = (amplitude2 - amplitude1) / numFrames;
+ for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
+ for (int sampleIndex = 0; sampleIndex < samplesPerFrame; sampleIndex++) {
+ float sample = *source++;
+
+ // Clip to valid range of a float sample to prevent excessive volume.
+ if (sample > MAX_HEADROOM) sample = MAX_HEADROOM;
+ else if (sample < MIN_HEADROOM) sample = MIN_HEADROOM;
+
+ *destination++ = sample * scaler;
+ }
+ scaler += delta;
+ }
+}
+
+// This code assumes amplitude1 and amplitude2 are between 0.0 and 1.0
+void AAudio_linearRamp(const int16_t *source,
+ int16_t *destination,
+ int32_t numFrames,
+ int32_t samplesPerFrame,
+ float amplitude1,
+ float amplitude2) {
+ float scaler = amplitude1 / SHORT_SCALE;
+ float delta = (amplitude2 - amplitude1) / (SHORT_SCALE * (float) numFrames);
+ for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
+ for (int sampleIndex = 0; sampleIndex < samplesPerFrame; sampleIndex++) {
+ // No need to clip because int16_t range is inherently limited.
+ float sample = *source++ * scaler;
+ *destination++ = floatToInt16(sample);
+ }
+ scaler += delta;
}
}
@@ -75,16 +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;
@@ -103,18 +253,20 @@
result = AAUDIO_ERROR_INVALID_HANDLE;
break;
case DEAD_OBJECT:
- result = AAUDIO_ERROR_DISCONNECTED;
+ result = AAUDIO_ERROR_NO_SERVICE;
break;
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/AAudioUtilities.h b/media/libaaudio/src/utility/AAudioUtilities.h
index 3dc501e..0078cbb 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.h
+++ b/media/libaaudio/src/utility/AAudioUtilities.h
@@ -35,9 +35,120 @@
*/
aaudio_result_t AAudioConvert_androidToAAudioResult(android::status_t status);
-void AAudioConvert_floatToPcm16(const float *source, int32_t numSamples, int16_t *destination);
+/**
+ * Convert an array of floats to an array of int16_t.
+ *
+ * @param source
+ * @param destination
+ * @param numSamples number of values in the array
+ * @param amplitude level between 0.0 and 1.0
+ */
+void AAudioConvert_floatToPcm16(const float *source,
+ int16_t *destination,
+ int32_t numSamples,
+ float amplitude);
-void AAudioConvert_pcm16ToFloat(const int16_t *source, int32_t numSamples, float *destination);
+/**
+ * Convert floats to int16_t and scale by a linear ramp.
+ *
+ * The ramp stops just short of reaching amplitude2 so that the next
+ * ramp can start at amplitude2 without causing a discontinuity.
+ *
+ * @param source
+ * @param destination
+ * @param numFrames
+ * @param samplesPerFrame AKA number of channels
+ * @param amplitude1 level at start of ramp, between 0.0 and 1.0
+ * @param amplitude2 level past end of ramp, between 0.0 and 1.0
+ */
+void AAudioConvert_floatToPcm16(const float *source,
+ int16_t *destination,
+ int32_t numFrames,
+ int32_t samplesPerFrame,
+ float amplitude1,
+ float amplitude2);
+
+/**
+ * Convert int16_t array to float array ranging from -1.0 to +1.0.
+ * @param source
+ * @param destination
+ * @param numSamples
+ */
+//void AAudioConvert_pcm16ToFloat(const int16_t *source, int32_t numSamples,
+// float *destination);
+
+/**
+ *
+ * Convert int16_t array to float array ranging from +/- amplitude.
+ * @param source
+ * @param destination
+ * @param numSamples
+ * @param amplitude
+ */
+void AAudioConvert_pcm16ToFloat(const int16_t *source,
+ float *destination,
+ int32_t numSamples,
+ float amplitude);
+
+/**
+ * Convert floats to int16_t and scale by a linear ramp.
+ *
+ * The ramp stops just short of reaching amplitude2 so that the next
+ * ramp can start at amplitude2 without causing a discontinuity.
+ *
+ * @param source
+ * @param destination
+ * @param numFrames
+ * @param samplesPerFrame AKA number of channels
+ * @param amplitude1 level at start of ramp, between 0.0 and 1.0
+ * @param amplitude2 level at end of ramp, between 0.0 and 1.0
+ */
+void AAudioConvert_pcm16ToFloat(const int16_t *source,
+ float *destination,
+ int32_t numFrames,
+ int32_t samplesPerFrame,
+ float amplitude1,
+ float amplitude2);
+
+/**
+ * Scale floats by a linear ramp.
+ *
+ * The ramp stops just short of reaching amplitude2 so that the next
+ * ramp can start at amplitude2 without causing a discontinuity.
+ *
+ * @param source
+ * @param destination
+ * @param numFrames
+ * @param samplesPerFrame
+ * @param amplitude1
+ * @param amplitude2
+ */
+void AAudio_linearRamp(const float *source,
+ float *destination,
+ int32_t numFrames,
+ int32_t samplesPerFrame,
+ float amplitude1,
+ float amplitude2);
+
+/**
+ * Scale int16_t's by a linear ramp.
+ *
+ * The ramp stops just short of reaching amplitude2 so that the next
+ * ramp can start at amplitude2 without causing a discontinuity.
+ *
+ * @param source
+ * @param destination
+ * @param numFrames
+ * @param samplesPerFrame
+ * @param amplitude1
+ * @param amplitude2
+ */
+void AAudio_linearRamp(const int16_t *source,
+ int16_t *destination,
+ int32_t numFrames,
+ int32_t samplesPerFrame,
+ float amplitude1,
+ float amplitude2);
/**
* Calculate the number of bytes and prevent numeric overflow.
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/libaaudio/src/utility/LinearRamp.cpp b/media/libaaudio/src/utility/LinearRamp.cpp
new file mode 100644
index 0000000..1714bbf
--- /dev/null
+++ b/media/libaaudio/src/utility/LinearRamp.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LinearRamp.h"
+
+bool LinearRamp::isRamping() {
+ float target = mTarget.load();
+ if (target != mLevelTo) {
+ // Update target. Continue from previous level.
+ mLevelTo = target;
+ mRemaining = mLengthInFrames;
+ return true;
+ } else {
+ return mRemaining > 0;
+ }
+}
+
+bool LinearRamp::nextSegment(int32_t frames, float *levelFrom, float *levelTo) {
+ bool ramping = isRamping();
+ *levelFrom = mLevelFrom;
+ if (ramping) {
+ float level;
+ if (frames >= mRemaining) {
+ level = mLevelTo;
+ mRemaining = 0;
+ } else {
+ // Interpolate to a point along the full ramp.
+ level = mLevelFrom + (frames * (mLevelTo - mLevelFrom) / mRemaining);
+ mRemaining -= frames;
+ }
+ mLevelFrom = level; // for next ramp
+ *levelTo = level;
+ } else {
+ *levelTo = mLevelTo;
+ }
+ return ramping;
+}
\ No newline at end of file
diff --git a/media/libaaudio/src/utility/LinearRamp.h b/media/libaaudio/src/utility/LinearRamp.h
new file mode 100644
index 0000000..ff09dce
--- /dev/null
+++ b/media/libaaudio/src/utility/LinearRamp.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AAUDIO_LINEAR_RAMP_H
+#define AAUDIO_LINEAR_RAMP_H
+
+#include <atomic>
+#include <stdint.h>
+
+/**
+ * Generate segments along a linear ramp.
+ * The ramp target can be updated from another thread.
+ * When the target is updated, a new ramp is started from the current position.
+ *
+ * The first ramp starts at 0.0.
+ *
+ */
+class LinearRamp {
+public:
+ LinearRamp() {
+ mTarget.store(1.0f);
+ }
+
+ void setLengthInFrames(int32_t frames) {
+ mLengthInFrames = frames;
+ }
+
+ int32_t getLengthInFrames() {
+ return mLengthInFrames;
+ }
+
+ /**
+ * This may be called by another thread.
+ * @param target
+ */
+ void setTarget(float target) {
+ mTarget.store(target);
+ }
+
+ float getTarget() {
+ return mTarget.load();
+ }
+
+ /**
+ * Force the nextSegment to start from this level.
+ *
+ * WARNING: this can cause a discontinuity if called while the ramp is being used.
+ * Only call this when setting the initial ramp.
+ *
+ * @param level
+ */
+ void forceCurrent(float level) {
+ mLevelFrom = level;
+ mLevelTo = level; // forces a ramp if it does not match target
+ }
+
+ float getCurrent() {
+ return mLevelFrom;
+ }
+
+ /**
+ * Get levels for next ramp segment.
+ *
+ * @param frames number of frames in the segment
+ * @param levelFrom pointer to starting amplitude
+ * @param levelTo pointer to ending amplitude
+ * @return true if ramp is still moving towards the target
+ */
+ bool nextSegment(int32_t frames, float *levelFrom, float *levelTo);
+
+private:
+
+ bool isRamping();
+
+ std::atomic<float> mTarget;
+
+ int32_t mLengthInFrames = 48000 / 50; // 20 msec at 48000 Hz
+ int32_t mRemaining = 0;
+ float mLevelFrom = 0.0f;
+ float mLevelTo = 0.0f;
+};
+
+
+#endif //AAUDIO_LINEAR_RAMP_H
diff --git a/media/libaaudio/tests/Android.mk b/media/libaaudio/tests/Android.mk
index 06c9364..01360b1 100644
--- a/media/libaaudio/tests/Android.mk
+++ b/media/libaaudio/tests/Android.mk
@@ -35,3 +35,15 @@
LOCAL_STATIC_LIBRARIES := libaaudio
LOCAL_MODULE := test_block_adapter
include $(BUILD_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+LOCAL_C_INCLUDES := \
+ $(call include-path-for, audio-utils) \
+ frameworks/av/media/libaaudio/include \
+ frameworks/av/media/libaaudio/src
+LOCAL_SRC_FILES:= test_linear_ramp.cpp
+LOCAL_SHARED_LIBRARIES := libaudioclient libaudioutils libbinder \
+ libcutils liblog libmedia libutils
+LOCAL_STATIC_LIBRARIES := libaaudio
+LOCAL_MODULE := test_linear_ramp
+include $(BUILD_NATIVE_TEST)
diff --git a/media/libaaudio/tests/test_linear_ramp.cpp b/media/libaaudio/tests/test_linear_ramp.cpp
new file mode 100644
index 0000000..5c53982
--- /dev/null
+++ b/media/libaaudio/tests/test_linear_ramp.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <iostream>
+
+#include <gtest/gtest.h>
+
+#include "utility/AAudioUtilities.h"
+#include "utility/LinearRamp.h"
+
+
+TEST(test_linear_ramp, linear_ramp_segments) {
+ LinearRamp ramp;
+ const float source[4] = {1.0f, 1.0f, 1.0f, 1.0f };
+ float destination[4] = {1.0f, 1.0f, 1.0f, 1.0f };
+
+ float levelFrom = -1.0f;
+ float levelTo = -1.0f;
+ ramp.setLengthInFrames(8);
+ ramp.setTarget(8.0f);
+
+ ASSERT_EQ(8, ramp.getLengthInFrames());
+
+ bool ramping = ramp.nextSegment(4, &levelFrom, &levelTo);
+ ASSERT_EQ(1, ramping);
+ ASSERT_EQ(0.0f, levelFrom);
+ ASSERT_EQ(4.0f, levelTo);
+
+ AAudio_linearRamp(source, destination, 4, 1, levelFrom, levelTo);
+ ASSERT_EQ(0.0f, destination[0]);
+ ASSERT_EQ(1.0f, destination[1]);
+ ASSERT_EQ(2.0f, destination[2]);
+ ASSERT_EQ(3.0f, destination[3]);
+
+ ramping = ramp.nextSegment(4, &levelFrom, &levelTo);
+ ASSERT_EQ(1, ramping);
+ ASSERT_EQ(4.0f, levelFrom);
+ ASSERT_EQ(8.0f, levelTo);
+
+ AAudio_linearRamp(source, destination, 4, 1, levelFrom, levelTo);
+ ASSERT_EQ(4.0f, destination[0]);
+ ASSERT_EQ(5.0f, destination[1]);
+ ASSERT_EQ(6.0f, destination[2]);
+ ASSERT_EQ(7.0f, destination[3]);
+
+ ramping = ramp.nextSegment(4, &levelFrom, &levelTo);
+ ASSERT_EQ(0, ramping);
+ ASSERT_EQ(8.0f, levelFrom);
+ ASSERT_EQ(8.0f, levelTo);
+
+ AAudio_linearRamp(source, destination, 4, 1, levelFrom, levelTo);
+ ASSERT_EQ(8.0f, destination[0]);
+ ASSERT_EQ(8.0f, destination[1]);
+ ASSERT_EQ(8.0f, destination[2]);
+ ASSERT_EQ(8.0f, destination[3]);
+
+};
+
+
+TEST(test_linear_ramp, linear_ramp_forced) {
+ LinearRamp ramp;
+ const float source[4] = {1.0f, 1.0f, 1.0f, 1.0f };
+ float destination[4] = {1.0f, 1.0f, 1.0f, 1.0f };
+
+ float levelFrom = -1.0f;
+ float levelTo = -1.0f;
+ ramp.setLengthInFrames(4);
+ ramp.setTarget(8.0f);
+ ramp.forceCurrent(4.0f);
+ ASSERT_EQ(4.0f, ramp.getCurrent());
+
+ bool ramping = ramp.nextSegment(4, &levelFrom, &levelTo);
+ ASSERT_EQ(1, ramping);
+ ASSERT_EQ(4.0f, levelFrom);
+ ASSERT_EQ(8.0f, levelTo);
+
+ AAudio_linearRamp(source, destination, 4, 1, levelFrom, levelTo);
+ ASSERT_EQ(4.0f, destination[0]);
+ ASSERT_EQ(5.0f, destination[1]);
+ ASSERT_EQ(6.0f, destination[2]);
+ ASSERT_EQ(7.0f, destination[3]);
+
+ ramping = ramp.nextSegment(4, &levelFrom, &levelTo);
+ ASSERT_EQ(0, ramping);
+ ASSERT_EQ(8.0f, levelFrom);
+ ASSERT_EQ(8.0f, levelTo);
+
+ AAudio_linearRamp(source, destination, 4, 1, levelFrom, levelTo);
+ ASSERT_EQ(8.0f, destination[0]);
+ ASSERT_EQ(8.0f, destination[1]);
+ ASSERT_EQ(8.0f, destination[2]);
+ ASSERT_EQ(8.0f, destination[3]);
+
+};
+
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index 3a0ce5e..38d90bc 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -652,6 +652,9 @@
get_sched_policy(0, &mPreviousSchedulingGroup);
androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO);
}
+
+ // Start our local VolumeHandler for restoration purposes.
+ mVolumeHandler->setStarted();
} else {
ALOGE("start() status %d", status);
mState = previousState;
@@ -2254,17 +2257,20 @@
}
}
// restore volume handler
- mVolumeHandler->forall([this](const sp<VolumeShaper::Configuration> &configuration,
- const sp<VolumeShaper::Operation> &operation) -> VolumeShaper::Status {
- sp<VolumeShaper::Operation> operationToEnd = new VolumeShaper::Operation(*operation);
+ mVolumeHandler->forall([this](const VolumeShaper &shaper) -> VolumeShaper::Status {
+ sp<VolumeShaper::Operation> operationToEnd =
+ new VolumeShaper::Operation(shaper.mOperation);
// TODO: Ideally we would restore to the exact xOffset position
// as returned by getVolumeShaperState(), but we don't have that
// information when restoring at the client unless we periodically poll
// the server or create shared memory state.
//
- // For now, we simply advance to the end of the VolumeShaper effect.
- operationToEnd->setXOffset(1.f);
- return mAudioTrack->applyVolumeShaper(configuration, operationToEnd);
+ // For now, we simply advance to the end of the VolumeShaper effect
+ // if it has been started.
+ if (shaper.isStarted()) {
+ operationToEnd->setNormalizedTime(1.f);
+ }
+ return mAudioTrack->applyVolumeShaper(shaper.mConfiguration, operationToEnd);
});
if (mState == STATE_ACTIVE) {
@@ -2334,19 +2340,36 @@
AutoMutex lock(mLock);
mVolumeHandler->setIdIfNecessary(configuration);
VolumeShaper::Status status = mAudioTrack->applyVolumeShaper(configuration, operation);
+
+ if (status == DEAD_OBJECT) {
+ if (restoreTrack_l("applyVolumeShaper") == OK) {
+ status = mAudioTrack->applyVolumeShaper(configuration, operation);
+ }
+ }
if (status >= 0) {
// save VolumeShaper for restore
mVolumeHandler->applyVolumeShaper(configuration, operation);
+ if (mState == STATE_ACTIVE || mState == STATE_STOPPING) {
+ mVolumeHandler->setStarted();
+ }
+ } else {
+ // warn only if not an expected restore failure.
+ ALOGW_IF(!((isOffloadedOrDirect_l() || mDoNotReconnect) && status == DEAD_OBJECT),
+ "applyVolumeShaper failed: %d", status);
}
return status;
}
sp<VolumeShaper::State> AudioTrack::getVolumeShaperState(int id)
{
- // TODO: To properly restore the AudioTrack
- // we will need to save the last state in AudioTrackShared.
AutoMutex lock(mLock);
- return mAudioTrack->getVolumeShaperState(id);
+ sp<VolumeShaper::State> state = mAudioTrack->getVolumeShaperState(id);
+ if (state.get() == nullptr && (mCblk->mFlags & CBLK_INVALID) != 0) {
+ if (restoreTrack_l("getVolumeShaperState") == OK) {
+ state = mAudioTrack->getVolumeShaperState(id);
+ }
+ }
+ return state;
}
status_t AudioTrack::getTimestamp(ExtendedTimestamp *timestamp)
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/libaudiohal/Android.mk b/media/libaudiohal/Android.mk
index 68a1f7b..e592169 100644
--- a/media/libaudiohal/Android.mk
+++ b/media/libaudiohal/Android.mk
@@ -5,30 +5,37 @@
LOCAL_SHARED_LIBRARIES := \
libcutils \
liblog \
- libutils
+ libutils \
+ libhardware
+
+LOCAL_SRC_FILES := \
+ DeviceHalLocal.cpp \
+ DevicesFactoryHalHybrid.cpp \
+ DevicesFactoryHalLocal.cpp \
+ StreamHalLocal.cpp
+
+LOCAL_CFLAGS := -Wall -Werror
ifeq ($(USE_LEGACY_LOCAL_AUDIO_HAL), true)
# Use audiohal directly w/o hwbinder middleware.
# This is for performance comparison and debugging only.
-LOCAL_SRC_FILES := \
- DeviceHalLocal.cpp \
- DevicesFactoryHalLocal.cpp \
+LOCAL_SRC_FILES += \
EffectBufferHalLocal.cpp \
- EffectHalLocal.cpp \
EffectsFactoryHalLocal.cpp \
- StreamHalLocal.cpp
+ EffectHalLocal.cpp
LOCAL_SHARED_LIBRARIES += \
- libeffects \
- libhardware
+ libeffects
+
+LOCAL_CFLAGS += -DUSE_LEGACY_LOCAL_AUDIO_HAL
else # if !USE_LEGACY_LOCAL_AUDIO_HAL
-LOCAL_SRC_FILES := \
+LOCAL_SRC_FILES += \
ConversionHelperHidl.cpp \
- HalDeathHandlerHidl.cpp \
+ HalDeathHandlerHidl.cpp \
DeviceHalHidl.cpp \
DevicesFactoryHalHidl.cpp \
EffectBufferHalHidl.cpp \
@@ -60,6 +67,4 @@
LOCAL_MODULE := libaudiohal
-LOCAL_CFLAGS := -Wall -Werror
-
include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libaudiohal/DevicesFactoryHalHidl.cpp b/media/libaudiohal/DevicesFactoryHalHidl.cpp
index fc2645e..31da263 100644
--- a/media/libaudiohal/DevicesFactoryHalHidl.cpp
+++ b/media/libaudiohal/DevicesFactoryHalHidl.cpp
@@ -33,11 +33,6 @@
namespace android {
-// static
-sp<DevicesFactoryHalInterface> DevicesFactoryHalInterface::create() {
- return new DevicesFactoryHalHidl();
-}
-
DevicesFactoryHalHidl::DevicesFactoryHalHidl() {
mDevicesFactory = IDevicesFactory::getService();
if (mDevicesFactory != 0) {
diff --git a/media/libaudiohal/DevicesFactoryHalHidl.h b/media/libaudiohal/DevicesFactoryHalHidl.h
index a26dec1..e2f1ad1 100644
--- a/media/libaudiohal/DevicesFactoryHalHidl.h
+++ b/media/libaudiohal/DevicesFactoryHalHidl.h
@@ -36,7 +36,7 @@
virtual status_t openDevice(const char *name, sp<DeviceHalInterface> *device);
private:
- friend class DevicesFactoryHalInterface;
+ friend class DevicesFactoryHalHybrid;
sp<IDevicesFactory> mDevicesFactory;
diff --git a/media/libaudiohal/DevicesFactoryHalHybrid.cpp b/media/libaudiohal/DevicesFactoryHalHybrid.cpp
new file mode 100644
index 0000000..454b03b
--- /dev/null
+++ b/media/libaudiohal/DevicesFactoryHalHybrid.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "DevicesFactoryHalHybrid"
+//#define LOG_NDEBUG 0
+
+#include "DevicesFactoryHalHybrid.h"
+#include "DevicesFactoryHalLocal.h"
+#ifndef USE_LEGACY_LOCAL_AUDIO_HAL
+#include "DevicesFactoryHalHidl.h"
+#endif
+
+namespace android {
+
+// static
+sp<DevicesFactoryHalInterface> DevicesFactoryHalInterface::create() {
+ return new DevicesFactoryHalHybrid();
+}
+
+DevicesFactoryHalHybrid::DevicesFactoryHalHybrid()
+ : mLocalFactory(new DevicesFactoryHalLocal()),
+ mHidlFactory(
+#ifdef USE_LEGACY_LOCAL_AUDIO_HAL
+ nullptr
+#else
+ new DevicesFactoryHalHidl()
+#endif
+ ) {
+}
+
+DevicesFactoryHalHybrid::~DevicesFactoryHalHybrid() {
+}
+
+status_t DevicesFactoryHalHybrid::openDevice(const char *name, sp<DeviceHalInterface> *device) {
+ if (mHidlFactory != 0 && strcmp(AUDIO_HARDWARE_MODULE_ID_A2DP, name) != 0) {
+ return mHidlFactory->openDevice(name, device);
+ }
+ return mLocalFactory->openDevice(name, device);
+}
+
+} // namespace android
diff --git a/media/libaudiohal/DevicesFactoryHalHybrid.h b/media/libaudiohal/DevicesFactoryHalHybrid.h
new file mode 100644
index 0000000..abd57d6
--- /dev/null
+++ b/media/libaudiohal/DevicesFactoryHalHybrid.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HYBRID_H
+#define ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HYBRID_H
+
+#include <media/audiohal/DevicesFactoryHalInterface.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+class DevicesFactoryHalHybrid : public DevicesFactoryHalInterface
+{
+ public:
+ // Opens a device with the specified name. To close the device, it is
+ // necessary to release references to the returned object.
+ virtual status_t openDevice(const char *name, sp<DeviceHalInterface> *device);
+
+ private:
+ friend class DevicesFactoryHalInterface;
+
+ // Can not be constructed directly by clients.
+ DevicesFactoryHalHybrid();
+
+ virtual ~DevicesFactoryHalHybrid();
+
+ sp<DevicesFactoryHalInterface> mLocalFactory;
+ sp<DevicesFactoryHalInterface> mHidlFactory;
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HYBRID_H
diff --git a/media/libaudiohal/DevicesFactoryHalLocal.cpp b/media/libaudiohal/DevicesFactoryHalLocal.cpp
index cd9a9e7..13a9acd 100644
--- a/media/libaudiohal/DevicesFactoryHalLocal.cpp
+++ b/media/libaudiohal/DevicesFactoryHalLocal.cpp
@@ -27,11 +27,6 @@
namespace android {
-// static
-sp<DevicesFactoryHalInterface> DevicesFactoryHalInterface::create() {
- return new DevicesFactoryHalLocal();
-}
-
static status_t load_audio_interface(const char *if_name, audio_hw_device_t **dev)
{
const hw_module_t *mod;
diff --git a/media/libaudiohal/DevicesFactoryHalLocal.h b/media/libaudiohal/DevicesFactoryHalLocal.h
index 58ce4ff..b9d18ab 100644
--- a/media/libaudiohal/DevicesFactoryHalLocal.h
+++ b/media/libaudiohal/DevicesFactoryHalLocal.h
@@ -33,7 +33,7 @@
virtual status_t openDevice(const char *name, sp<DeviceHalInterface> *device);
private:
- friend class DevicesFactoryHalInterface;
+ friend class DevicesFactoryHalHybrid;
// Can not be constructed directly by clients.
DevicesFactoryHalLocal() {}
diff --git a/media/libaudiohal/EffectBufferHalHidl.cpp b/media/libaudiohal/EffectBufferHalHidl.cpp
index d6a41a2..8b5201b 100644
--- a/media/libaudiohal/EffectBufferHalHidl.cpp
+++ b/media/libaudiohal/EffectBufferHalHidl.cpp
@@ -47,7 +47,7 @@
status_t EffectBufferHalInterface::mirror(
void* external, size_t size, sp<EffectBufferHalInterface>* buffer) {
sp<EffectBufferHalInterface> tempBuffer = new EffectBufferHalHidl(size);
- status_t result = reinterpret_cast<EffectBufferHalHidl*>(tempBuffer.get())->init();
+ status_t result = static_cast<EffectBufferHalHidl*>(tempBuffer.get())->init();
if (result == OK) {
tempBuffer->setExternalData(external);
*buffer = tempBuffer;
@@ -56,7 +56,8 @@
}
EffectBufferHalHidl::EffectBufferHalHidl(size_t size)
- : mBufferSize(size), mExternalData(nullptr), mAudioBuffer{0, {nullptr}} {
+ : mBufferSize(size), mFrameCountChanged(false),
+ mExternalData(nullptr), mAudioBuffer{0, {nullptr}} {
mHidlBuffer.id = makeUniqueId();
mHidlBuffer.frameCount = 0;
}
@@ -107,6 +108,13 @@
void EffectBufferHalHidl::setFrameCount(size_t frameCount) {
mHidlBuffer.frameCount = frameCount;
mAudioBuffer.frameCount = frameCount;
+ mFrameCountChanged = true;
+}
+
+bool EffectBufferHalHidl::checkFrameCountChange() {
+ bool result = mFrameCountChanged;
+ mFrameCountChanged = false;
+ return result;
}
void EffectBufferHalHidl::setExternalData(void* external) {
diff --git a/media/libaudiohal/EffectBufferHalHidl.h b/media/libaudiohal/EffectBufferHalHidl.h
index 6e9fd0b..66a81c2 100644
--- a/media/libaudiohal/EffectBufferHalHidl.h
+++ b/media/libaudiohal/EffectBufferHalHidl.h
@@ -37,6 +37,7 @@
virtual void setExternalData(void* external);
virtual void setFrameCount(size_t frameCount);
+ virtual bool checkFrameCountChange();
virtual void update();
virtual void commit();
@@ -51,6 +52,7 @@
static uint64_t makeUniqueId();
const size_t mBufferSize;
+ bool mFrameCountChanged;
void* mExternalData;
AudioBuffer mHidlBuffer;
sp<IMemory> mMemory;
diff --git a/media/libaudiohal/EffectBufferHalLocal.cpp b/media/libaudiohal/EffectBufferHalLocal.cpp
index 9fe2c7b..7951c8e 100644
--- a/media/libaudiohal/EffectBufferHalLocal.cpp
+++ b/media/libaudiohal/EffectBufferHalLocal.cpp
@@ -39,13 +39,13 @@
EffectBufferHalLocal::EffectBufferHalLocal(size_t size)
: mOwnBuffer(new uint8_t[size]),
- mBufferSize(size),
+ mBufferSize(size), mFrameCountChanged(false),
mAudioBuffer{0, {mOwnBuffer.get()}} {
}
EffectBufferHalLocal::EffectBufferHalLocal(void* external, size_t size)
: mOwnBuffer(nullptr),
- mBufferSize(size),
+ mBufferSize(size), mFrameCountChanged(false),
mAudioBuffer{0, {external}} {
}
@@ -62,6 +62,7 @@
void EffectBufferHalLocal::setFrameCount(size_t frameCount) {
mAudioBuffer.frameCount = frameCount;
+ mFrameCountChanged = true;
}
void EffectBufferHalLocal::setExternalData(void* external) {
@@ -69,6 +70,12 @@
mAudioBuffer.raw = external;
}
+bool EffectBufferHalLocal::checkFrameCountChange() {
+ bool result = mFrameCountChanged;
+ mFrameCountChanged = false;
+ return result;
+}
+
void EffectBufferHalLocal::update() {
}
diff --git a/media/libaudiohal/EffectBufferHalLocal.h b/media/libaudiohal/EffectBufferHalLocal.h
index 202d878..d2b624b 100644
--- a/media/libaudiohal/EffectBufferHalLocal.h
+++ b/media/libaudiohal/EffectBufferHalLocal.h
@@ -32,6 +32,7 @@
virtual void setExternalData(void* external);
virtual void setFrameCount(size_t frameCount);
+ virtual bool checkFrameCountChange();
virtual void update();
virtual void commit();
@@ -43,6 +44,7 @@
std::unique_ptr<uint8_t[]> mOwnBuffer;
const size_t mBufferSize;
+ bool mFrameCountChanged;
audio_buffer_t mAudioBuffer;
// Can not be constructed directly by clients.
diff --git a/media/libaudiohal/EffectHalHidl.cpp b/media/libaudiohal/EffectHalHidl.cpp
index 0babfda..b49b975 100644
--- a/media/libaudiohal/EffectHalHidl.cpp
+++ b/media/libaudiohal/EffectHalHidl.cpp
@@ -168,13 +168,20 @@
return OK;
}
+bool EffectHalHidl::needToResetBuffers() {
+ if (mBuffersChanged) return true;
+ bool inBufferFrameCountUpdated = mInBuffer->checkFrameCountChange();
+ bool outBufferFrameCountUpdated = mOutBuffer->checkFrameCountChange();
+ return inBufferFrameCountUpdated || outBufferFrameCountUpdated;
+}
+
status_t EffectHalHidl::processImpl(uint32_t mqFlag) {
if (mEffect == 0 || mInBuffer == 0 || mOutBuffer == 0) return NO_INIT;
status_t status;
if (!mStatusMQ && (status = prepareForProcessing()) != OK) {
return status;
}
- if (mBuffersChanged && (status = setProcessBuffers()) != OK) {
+ if (needToResetBuffers() && (status = setProcessBuffers()) != OK) {
return status;
}
// The data is already in the buffers, just need to flush it and wake up the server side.
@@ -202,8 +209,8 @@
status_t EffectHalHidl::setProcessBuffers() {
Return<Result> ret = mEffect->setProcessBuffers(
- reinterpret_cast<EffectBufferHalHidl*>(mInBuffer.get())->hidlBuffer(),
- reinterpret_cast<EffectBufferHalHidl*>(mOutBuffer.get())->hidlBuffer());
+ static_cast<EffectBufferHalHidl*>(mInBuffer.get())->hidlBuffer(),
+ static_cast<EffectBufferHalHidl*>(mOutBuffer.get())->hidlBuffer());
if (ret.isOk() && ret == Result::OK) {
mBuffersChanged = false;
return OK;
diff --git a/media/libaudiohal/EffectHalHidl.h b/media/libaudiohal/EffectHalHidl.h
index c8db36f..6ffdaf1 100644
--- a/media/libaudiohal/EffectHalHidl.h
+++ b/media/libaudiohal/EffectHalHidl.h
@@ -58,6 +58,9 @@
// Free resources on the remote side.
virtual status_t close();
+ // Whether it's a local implementation.
+ virtual bool isLocal() const { return false; }
+
uint64_t effectId() const { return mEffectId; }
static void effectDescriptorToHal(
@@ -92,6 +95,7 @@
status_t getConfigImpl(uint32_t cmdCode, uint32_t *replySize, void *pReplyData);
status_t prepareForProcessing();
+ bool needToResetBuffers();
status_t processImpl(uint32_t mqFlag);
status_t setConfigImpl(
uint32_t cmdCode, uint32_t cmdSize, void *pCmdData,
diff --git a/media/libaudiohal/EffectHalLocal.h b/media/libaudiohal/EffectHalLocal.h
index b499462..693fb50 100644
--- a/media/libaudiohal/EffectHalLocal.h
+++ b/media/libaudiohal/EffectHalLocal.h
@@ -48,6 +48,9 @@
// Free resources on the remote side.
virtual status_t close();
+ // Whether it's a local implementation.
+ virtual bool isLocal() const { return true; }
+
effect_handle_t handle() const { return mHandle; }
private:
diff --git a/media/libaudiohal/StreamHalLocal.cpp b/media/libaudiohal/StreamHalLocal.cpp
index b25e518..05800a0 100644
--- a/media/libaudiohal/StreamHalLocal.cpp
+++ b/media/libaudiohal/StreamHalLocal.cpp
@@ -79,11 +79,13 @@
}
status_t StreamHalLocal::addEffect(sp<EffectHalInterface> effect) {
+ LOG_ALWAYS_FATAL_IF(!effect->isLocal(), "Only local effects can be added for a local stream");
return mStream->add_audio_effect(mStream,
static_cast<EffectHalLocal*>(effect.get())->handle());
}
status_t StreamHalLocal::removeEffect(sp<EffectHalInterface> effect) {
+ LOG_ALWAYS_FATAL_IF(!effect->isLocal(), "Only local effects can be removed for a local stream");
return mStream->remove_audio_effect(mStream,
static_cast<EffectHalLocal*>(effect.get())->handle());
}
@@ -162,7 +164,7 @@
// correctly the case when the callback is invoked while StreamOutHalLocal's destructor is
// already running, because the destructor is invoked after the refcount has been atomically
// decremented.
- wp<StreamOutHalLocal> weakSelf(reinterpret_cast<StreamOutHalLocal*>(cookie));
+ wp<StreamOutHalLocal> weakSelf(static_cast<StreamOutHalLocal*>(cookie));
sp<StreamOutHalLocal> self = weakSelf.promote();
if (self == 0) return 0;
sp<StreamOutHalInterfaceCallback> callback = self->mCallback.promote();
diff --git a/media/libaudiohal/include/EffectBufferHalInterface.h b/media/libaudiohal/include/EffectBufferHalInterface.h
index 6fa7940..e862f6e 100644
--- a/media/libaudiohal/include/EffectBufferHalInterface.h
+++ b/media/libaudiohal/include/EffectBufferHalInterface.h
@@ -39,6 +39,8 @@
virtual void setExternalData(void* external) = 0;
virtual void setFrameCount(size_t frameCount) = 0;
+ virtual bool checkFrameCountChange() = 0; // returns whether frame count has been updated
+ // since the last call to this method
virtual void update() = 0; // copies data from the external buffer, noop for allocated buffers
virtual void commit() = 0; // copies data to the external buffer, noop for allocated buffers
diff --git a/media/libaudiohal/include/EffectHalInterface.h b/media/libaudiohal/include/EffectHalInterface.h
index 7f9a6fd..92622aa 100644
--- a/media/libaudiohal/include/EffectHalInterface.h
+++ b/media/libaudiohal/include/EffectHalInterface.h
@@ -52,6 +52,9 @@
// Free resources on the remote side.
virtual status_t close() = 0;
+ // Whether it's a local implementation.
+ virtual bool isLocal() const = 0;
+
protected:
// Subclasses can not be constructed directly by clients.
EffectHalInterface() {}
diff --git a/media/libaudioprocessing/AudioMixer.cpp b/media/libaudioprocessing/AudioMixer.cpp
index a7d9f0f..05b726a 100644
--- a/media/libaudioprocessing/AudioMixer.cpp
+++ b/media/libaudioprocessing/AudioMixer.cpp
@@ -302,6 +302,12 @@
void AudioMixer::track_t::unprepareForDownmix() {
ALOGV("AudioMixer::unprepareForDownmix(%p)", this);
+ if (mPostDownmixReformatBufferProvider != nullptr) {
+ // release any buffers held by the mPostDownmixReformatBufferProvider
+ // before deallocating the downmixerBufferProvider.
+ mPostDownmixReformatBufferProvider->reset();
+ }
+
mDownmixRequiresFormat = AUDIO_FORMAT_INVALID;
if (downmixerBufferProvider != NULL) {
// this track had previously been configured with a downmixer, delete it
diff --git a/media/libmedia/aidl/android/IGraphicBufferSource.aidl b/media/libmedia/aidl/android/IGraphicBufferSource.aidl
index 325c631..f3c7abc 100644
--- a/media/libmedia/aidl/android/IGraphicBufferSource.aidl
+++ b/media/libmedia/aidl/android/IGraphicBufferSource.aidl
@@ -28,10 +28,10 @@
void setSuspend(boolean suspend, long suspendTimeUs);
void setRepeatPreviousFrameDelayUs(long repeatAfterUs);
void setMaxFps(float maxFps);
- void setTimeLapseConfig(long timePerFrameUs, long timePerCaptureUs);
+ void setTimeLapseConfig(double fps, double captureFps);
void setStartTimeUs(long startTimeUs);
void setStopTimeUs(long stopTimeUs);
void setColorAspects(int aspects);
void setTimeOffsetUs(long timeOffsetsUs);
void signalEndOfInputStream();
-}
\ No newline at end of file
+}
diff --git a/media/libmedia/include/media/IMediaExtractor.h b/media/libmedia/include/media/IMediaExtractor.h
index cf1b9fb..ab40f53 100644
--- a/media/libmedia/include/media/IMediaExtractor.h
+++ b/media/libmedia/include/media/IMediaExtractor.h
@@ -34,6 +34,9 @@
DECLARE_META_INTERFACE(MediaExtractor);
virtual size_t countTracks() = 0;
+ // This function could return NULL IMediaSource even when index is within the
+ // track count returned by countTracks, since it's possible the track is malformed
+ // and it's not detected during countTracks call.
virtual sp<IMediaSource> getTrack(size_t index) = 0;
enum GetTrackMetaDataFlags {
diff --git a/media/libmedia/omx/1.0/WGraphicBufferSource.cpp b/media/libmedia/omx/1.0/WGraphicBufferSource.cpp
index b4e2975..4c543fa 100644
--- a/media/libmedia/omx/1.0/WGraphicBufferSource.cpp
+++ b/media/libmedia/omx/1.0/WGraphicBufferSource.cpp
@@ -53,9 +53,8 @@
}
BnStatus LWGraphicBufferSource::setTimeLapseConfig(
- int64_t timePerFrameUs, int64_t timePerCaptureUs) {
- return toBinderStatus(mBase->setTimeLapseConfig(
- timePerFrameUs, timePerCaptureUs));
+ double fps, double captureFps) {
+ return toBinderStatus(mBase->setTimeLapseConfig(fps, captureFps));
}
BnStatus LWGraphicBufferSource::setStartTimeUs(
diff --git a/media/libmediametrics/MediaAnalyticsItem.cpp b/media/libmediametrics/MediaAnalyticsItem.cpp
index 229e747..43881b3 100644
--- a/media/libmediametrics/MediaAnalyticsItem.cpp
+++ b/media/libmediametrics/MediaAnalyticsItem.cpp
@@ -34,6 +34,7 @@
#include <binder/IServiceManager.h>
#include <media/IMediaAnalyticsService.h>
#include <media/MediaAnalyticsItem.h>
+#include <private/android_filesystem_config.h>
namespace android {
@@ -776,6 +777,24 @@
return NULL;
}
+ // completely skip logging from certain UIDs. We do this here
+ // to avoid the multi-second timeouts while we learn that
+ // sepolicy will not let us find the service.
+ // We do this only for a select set of UIDs
+ // The sepolicy protection is still in place, we just want a faster
+ // response from this specific, small set of uids.
+ {
+ uid_t uid = getuid();
+ switch (uid) {
+ case AID_RADIO: // telephony subsystem, RIL
+ return NULL;
+ break;
+ default:
+ // let sepolicy deny access if appropriate
+ break;
+ }
+ }
+
{
Mutex::Autolock _l(sInitMutex);
const char *badness = "";
diff --git a/media/libmediametrics/include/MediaAnalyticsItem.h b/media/libmediametrics/include/MediaAnalyticsItem.h
index f050e7f..dc501b2 100644
--- a/media/libmediametrics/include/MediaAnalyticsItem.h
+++ b/media/libmediametrics/include/MediaAnalyticsItem.h
@@ -41,6 +41,7 @@
friend class MediaAnalyticsService;
friend class IMediaAnalyticsService;
friend class MediaMetricsJNI;
+ friend class MetricsSummarizer;
public:
@@ -231,7 +232,6 @@
size_t mPropCount;
size_t mPropSize;
Prop *mProps;
-
};
} // namespace android
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index b082654..18fd857 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -275,20 +275,6 @@
ALOGV("MediaPlayerService created");
mNextConnId = 1;
- mBatteryAudio.refCount = 0;
- for (int i = 0; i < NUM_AUDIO_DEVICES; i++) {
- mBatteryAudio.deviceOn[i] = 0;
- mBatteryAudio.lastTime[i] = 0;
- mBatteryAudio.totalTime[i] = 0;
- }
- // speaker is on by default
- mBatteryAudio.deviceOn[SPEAKER] = 1;
-
- // reset battery stats
- // if the mediaserver has crashed, battery stats could be left
- // in bad state, reset the state upon service start.
- BatteryNotifier::getInstance().noteResetVideo();
-
MediaPlayerFactory::registerBuiltinFactories();
}
@@ -2029,12 +2015,23 @@
ALOGV("setVolume");
t->setVolume(mLeftVolume, mRightVolume);
- // Dispatch any queued VolumeShapers when the track was not open.
- mVolumeHandler->forall([&t](const sp<VolumeShaper::Configuration> &configuration,
- const sp<VolumeShaper::Operation> &operation) -> VolumeShaper::Status {
- return t->applyVolumeShaper(configuration, operation);
+ // Restore VolumeShapers for the MediaPlayer in case the track was recreated
+ // due to an output sink error (e.g. offload to non-offload switch).
+ mVolumeHandler->forall([&t](const VolumeShaper &shaper) -> VolumeShaper::Status {
+ sp<VolumeShaper::Operation> operationToEnd =
+ new VolumeShaper::Operation(shaper.mOperation);
+ // TODO: Ideally we would restore to the exact xOffset position
+ // as returned by getVolumeShaperState(), but we don't have that
+ // information when restoring at the client unless we periodically poll
+ // the server or create shared memory state.
+ //
+ // For now, we simply advance to the end of the VolumeShaper effect
+ // if it has been started.
+ if (shaper.isStarted()) {
+ operationToEnd->setNormalizedTime(1.f);
+ }
+ return t->applyVolumeShaper(shaper.mConfiguration, operationToEnd);
});
- mVolumeHandler->reset(); // After dispatching, clear VolumeShaper queue.
mSampleRateHz = sampleRate;
mFlags = flags;
@@ -2075,7 +2072,11 @@
if (mTrack != 0) {
mTrack->setVolume(mLeftVolume, mRightVolume);
mTrack->setAuxEffectSendLevel(mSendLevel);
- return mTrack->start();
+ status_t status = mTrack->start();
+ if (status == NO_ERROR) {
+ mVolumeHandler->setStarted();
+ }
+ return status;
}
return NO_INIT;
}
@@ -2279,13 +2280,21 @@
Mutex::Autolock lock(mLock);
ALOGV("AudioOutput::applyVolumeShaper");
- // We take ownership of the VolumeShaper if set before the track is created.
mVolumeHandler->setIdIfNecessary(configuration);
+
+ VolumeShaper::Status status;
if (mTrack != 0) {
- return mTrack->applyVolumeShaper(configuration, operation);
+ status = mTrack->applyVolumeShaper(configuration, operation);
+ if (status >= 0) {
+ (void)mVolumeHandler->applyVolumeShaper(configuration, operation);
+ if (mTrack->isPlaying()) { // match local AudioTrack to properly restore.
+ mVolumeHandler->setStarted();
+ }
+ }
} else {
- return mVolumeHandler->applyVolumeShaper(configuration, operation);
+ status = mVolumeHandler->applyVolumeShaper(configuration, operation);
}
+ return status;
}
sp<VolumeShaper::State> MediaPlayerService::AudioOutput::getVolumeShaperState(int id)
@@ -2463,7 +2472,31 @@
////////////////////////////////////////////////////////////////////////////////
-void MediaPlayerService::addBatteryData(uint32_t params)
+void MediaPlayerService::addBatteryData(uint32_t params) {
+ mBatteryTracker.addBatteryData(params);
+}
+
+status_t MediaPlayerService::pullBatteryData(Parcel* reply) {
+ return mBatteryTracker.pullBatteryData(reply);
+}
+
+MediaPlayerService::BatteryTracker::BatteryTracker() {
+ mBatteryAudio.refCount = 0;
+ for (int i = 0; i < NUM_AUDIO_DEVICES; i++) {
+ mBatteryAudio.deviceOn[i] = 0;
+ mBatteryAudio.lastTime[i] = 0;
+ mBatteryAudio.totalTime[i] = 0;
+ }
+ // speaker is on by default
+ mBatteryAudio.deviceOn[SPEAKER] = 1;
+
+ // reset battery stats
+ // if the mediaserver has crashed, battery stats could be left
+ // in bad state, reset the state upon service start.
+ BatteryNotifier::getInstance().noteResetVideo();
+}
+
+void MediaPlayerService::BatteryTracker::addBatteryData(uint32_t params)
{
Mutex::Autolock lock(mLock);
@@ -2603,7 +2636,7 @@
}
}
-status_t MediaPlayerService::pullBatteryData(Parcel* reply) {
+status_t MediaPlayerService::BatteryTracker::pullBatteryData(Parcel* reply) {
Mutex::Autolock lock(mLock);
// audio output devices usage
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index 009fe73..06b9cad 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -246,51 +246,62 @@
CAMERA_PROCESS_DEATH = 4
};
- // For battery usage tracking purpose
- struct BatteryUsageInfo {
- // how many streams are being played by one UID
- int refCount;
- // a temp variable to store the duration(ms) of audio codecs
- // when we start a audio codec, we minus the system time from audioLastTime
- // when we pause it, we add the system time back to the audioLastTime
- // so after the pause, audioLastTime = pause time - start time
- // if multiple audio streams are played (or recorded), then audioLastTime
- // = the total playing time of all the streams
- int32_t audioLastTime;
- // when all the audio streams are being paused, we assign audioLastTime to
- // this variable, so this value could be provided to the battery app
- // in the next pullBatteryData call
- int32_t audioTotalTime;
-
- int32_t videoLastTime;
- int32_t videoTotalTime;
- };
- KeyedVector<int, BatteryUsageInfo> mBatteryData;
-
- enum {
- SPEAKER,
- OTHER_AUDIO_DEVICE,
- SPEAKER_AND_OTHER,
- NUM_AUDIO_DEVICES
- };
-
- struct BatteryAudioFlingerUsageInfo {
- int refCount; // how many audio streams are being played
- int deviceOn[NUM_AUDIO_DEVICES]; // whether the device is currently used
- int32_t lastTime[NUM_AUDIO_DEVICES]; // in ms
- // totalTime[]: total time of audio output devices usage
- int32_t totalTime[NUM_AUDIO_DEVICES]; // in ms
- };
-
- // This varialble is used to record the usage of audio output device
- // for battery app
- BatteryAudioFlingerUsageInfo mBatteryAudio;
-
// Collect info of the codec usage from media player and media recorder
virtual void addBatteryData(uint32_t params);
// API for the Battery app to pull the data of codecs usage
virtual status_t pullBatteryData(Parcel* reply);
private:
+ struct BatteryTracker {
+ BatteryTracker();
+ // Collect info of the codec usage from media player and media recorder
+ void addBatteryData(uint32_t params);
+ // API for the Battery app to pull the data of codecs usage
+ status_t pullBatteryData(Parcel* reply);
+
+ private:
+ // For battery usage tracking purpose
+ struct BatteryUsageInfo {
+ // how many streams are being played by one UID
+ int refCount;
+ // a temp variable to store the duration(ms) of audio codecs
+ // when we start a audio codec, we minus the system time from audioLastTime
+ // when we pause it, we add the system time back to the audioLastTime
+ // so after the pause, audioLastTime = pause time - start time
+ // if multiple audio streams are played (or recorded), then audioLastTime
+ // = the total playing time of all the streams
+ int32_t audioLastTime;
+ // when all the audio streams are being paused, we assign audioLastTime to
+ // this variable, so this value could be provided to the battery app
+ // in the next pullBatteryData call
+ int32_t audioTotalTime;
+
+ int32_t videoLastTime;
+ int32_t videoTotalTime;
+ };
+ KeyedVector<int, BatteryUsageInfo> mBatteryData;
+
+ enum {
+ SPEAKER,
+ OTHER_AUDIO_DEVICE,
+ SPEAKER_AND_OTHER,
+ NUM_AUDIO_DEVICES
+ };
+
+ struct BatteryAudioFlingerUsageInfo {
+ int refCount; // how many audio streams are being played
+ int deviceOn[NUM_AUDIO_DEVICES]; // whether the device is currently used
+ int32_t lastTime[NUM_AUDIO_DEVICES]; // in ms
+ // totalTime[]: total time of audio output devices usage
+ int32_t totalTime[NUM_AUDIO_DEVICES]; // in ms
+ };
+
+ // This varialble is used to record the usage of audio output device
+ // for battery app
+ BatteryAudioFlingerUsageInfo mBatteryAudio;
+
+ mutable Mutex mLock;
+ };
+ BatteryTracker mBatteryTracker;
class Client : public BnMediaPlayer {
// IMediaPlayer interface
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 3b7c61b..e1d762f 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -163,7 +163,7 @@
// TBD mTrackEveryTimeDurationUs = 0;
mAnalyticsItem->setInt32(kRecorderCaptureFpsEnable, mCaptureFpsEnable);
mAnalyticsItem->setDouble(kRecorderCaptureFps, mCaptureFps);
- // TBD mTimeBetweenCaptureUs = -1;
+ // TBD mCaptureFps = -1.0;
// TBD mCameraSourceTimeLapse = NULL;
// TBD mMetaDataStoredInVideoBuffers = kMetadataBufferTypeInvalid;
// TBD mEncoderProfiles = MediaProfiles::getInstance();
@@ -709,18 +709,11 @@
status_t StagefrightRecorder::setParamCaptureFps(double fps) {
ALOGV("setParamCaptureFps: %.2f", fps);
- // FPS value is from Java layer where double follows IEEE-754, which is not
- // necessarily true for double here.
- constexpr double kIeee754Epsilon = 2.220446049250313e-16;
- constexpr double kEpsilon = std::max(std::numeric_limits<double>::epsilon(), kIeee754Epsilon);
- // Not allowing fps less than 1 frame / day minus epsilon.
- if (fps < 1.0 / 86400 - kEpsilon) {
- ALOGE("fps (%lf) is out of range (>= 1 frame / day)", fps);
+ if (!(fps >= 1.0 / 86400)) {
+ ALOGE("FPS is too small");
return BAD_VALUE;
}
-
mCaptureFps = fps;
- mTimeBetweenCaptureUs = std::llround(1e6 / fps);
return OK;
}
@@ -1574,16 +1567,15 @@
videoSize.width = mVideoWidth;
videoSize.height = mVideoHeight;
if (mCaptureFpsEnable) {
- if (mTimeBetweenCaptureUs < 0) {
- ALOGE("Invalid mTimeBetweenTimeLapseFrameCaptureUs value: %lld",
- (long long)mTimeBetweenCaptureUs);
+ if (!(mCaptureFps > 0.)) {
+ ALOGE("Invalid mCaptureFps value: %lf", mCaptureFps);
return BAD_VALUE;
}
mCameraSourceTimeLapse = CameraSourceTimeLapse::CreateFromCamera(
mCamera, mCameraProxy, mCameraId, mClientName, mClientUid, mClientPid,
videoSize, mFrameRate, mPreviewSurface,
- mTimeBetweenCaptureUs);
+ std::llround(1e6 / mCaptureFps));
*cameraSource = mCameraSourceTimeLapse;
} else {
*cameraSource = CameraSource::CreateFromCamera(
@@ -1679,12 +1671,11 @@
// set up time lapse/slow motion for surface source
if (mCaptureFpsEnable) {
- if (mTimeBetweenCaptureUs <= 0) {
- ALOGE("Invalid mTimeBetweenCaptureUs value: %lld",
- (long long)mTimeBetweenCaptureUs);
+ if (!(mCaptureFps > 0.)) {
+ ALOGE("Invalid mCaptureFps value: %lf", mCaptureFps);
return BAD_VALUE;
}
- format->setInt64("time-lapse", mTimeBetweenCaptureUs);
+ format->setDouble("time-lapse-fps", mCaptureFps);
}
}
@@ -2075,8 +2066,7 @@
mMaxFileSizeBytes = 0;
mTrackEveryTimeDurationUs = 0;
mCaptureFpsEnable = false;
- mCaptureFps = 0.0;
- mTimeBetweenCaptureUs = -1;
+ mCaptureFps = -1.0;
mCameraSourceTimeLapse = NULL;
mMetaDataStoredInVideoBuffers = kMetadataBufferTypeInvalid;
mEncoderProfiles = MediaProfiles::getInstance();
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index aedca3f..22b09d4 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -156,6 +156,7 @@
extractor = MediaExtractor::Create(mDataSource, NULL);
if (extractor == NULL) {
+ ALOGE("initFromDataSource, cannot create extractor!");
return UNKNOWN_ERROR;
}
@@ -171,6 +172,7 @@
size_t numtracks = extractor->countTracks();
if (numtracks == 0) {
+ ALOGE("initFromDataSource, source has no track!");
return UNKNOWN_ERROR;
}
@@ -377,6 +379,11 @@
source.get(), mFd, (long long)mOffset, (long long)mLength);
if (source.get() != nullptr) {
mDataSource = DataSource::CreateFromIDataSource(source);
+ if (mDataSource != nullptr) {
+ // Close the local file descriptor as it is not needed anymore.
+ close(mFd);
+ mFd = -1;
+ }
} else {
ALOGW("extractor service cannot make data source");
}
@@ -388,7 +395,9 @@
ALOGD("FileSource local");
mDataSource = new FileSource(mFd, mOffset, mLength);
}
-
+ // TODO: close should always be done on mFd, see the lines following
+ // DataSource::CreateFromIDataSource above,
+ // and the FileSource constructor should dup the mFd argument as needed.
mFd = -1;
}
@@ -1952,12 +1961,12 @@
size_t psshsize;
if (!mFileMeta->findData(kKeyPssh, &type, &pssh, &psshsize)) {
- ALOGE("checkDrmInfo: No PSSH");
+ ALOGV("checkDrmInfo: No PSSH");
return OK; // source without DRM info
}
Parcel parcel;
- NuPlayerDrm::retrieveDrmInfo(pssh, psshsize, mMimes, &parcel);
+ NuPlayerDrm::retrieveDrmInfo(pssh, psshsize, &parcel);
ALOGV("checkDrmInfo: MEDIA_DRM_INFO PSSH size: %d Parcel size: %d objects#: %d",
(int)psshsize, (int)parcel.dataSize(), (int)parcel.objectsCount());
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index d048777..0d4c730 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -202,7 +202,8 @@
mPaused(false),
mPausedByClient(true),
mPausedForBuffering(false),
- mIsDrmProtected(false) {
+ mIsDrmProtected(false),
+ mDataSourceType(DATA_SOURCE_TYPE_NONE) {
clearFlushComplete();
}
@@ -225,6 +226,7 @@
msg->setObject("source", new StreamingSource(notify, source));
msg->post();
+ mDataSourceType = DATA_SOURCE_TYPE_STREAM;
}
static bool IsHTTPLiveURL(const char *url) {
@@ -258,10 +260,12 @@
if (IsHTTPLiveURL(url)) {
source = new HTTPLiveSource(notify, httpService, url, headers);
ALOGV("setDataSourceAsync HTTPLiveSource %s", url);
+ mDataSourceType = DATA_SOURCE_TYPE_HTTP_LIVE;
} else if (!strncasecmp(url, "rtsp://", 7)) {
source = new RTSPSource(
notify, httpService, url, headers, mUIDValid, mUID);
ALOGV("setDataSourceAsync RTSPSource %s", url);
+ mDataSourceType = DATA_SOURCE_TYPE_RTSP;
} else if ((!strncasecmp(url, "http://", 7)
|| !strncasecmp(url, "https://", 8))
&& ((len >= 4 && !strcasecmp(".sdp", &url[len - 4]))
@@ -269,6 +273,7 @@
source = new RTSPSource(
notify, httpService, url, headers, mUIDValid, mUID, true);
ALOGV("setDataSourceAsync RTSPSource http/https/.sdp %s", url);
+ mDataSourceType = DATA_SOURCE_TYPE_RTSP;
} else {
ALOGV("setDataSourceAsync GenericSource %s", url);
@@ -282,6 +287,9 @@
} else {
ALOGE("Failed to set data source!");
}
+
+ // regardless of success/failure
+ mDataSourceType = DATA_SOURCE_TYPE_GENERIC_URL;
}
msg->setObject("source", source);
msg->post();
@@ -307,6 +315,7 @@
msg->setObject("source", source);
msg->post();
+ mDataSourceType = DATA_SOURCE_TYPE_GENERIC_FD;
}
void NuPlayer::setDataSourceAsync(const sp<DataSource> &dataSource) {
@@ -323,6 +332,7 @@
msg->setObject("source", source);
msg->post();
+ mDataSourceType = DATA_SOURCE_TYPE_MEDIA;
}
status_t NuPlayer::getDefaultBufferingSettings(
@@ -2651,6 +2661,32 @@
}
}
+const char *NuPlayer::getDataSourceType() {
+ switch (mDataSourceType) {
+ case DATA_SOURCE_TYPE_HTTP_LIVE:
+ return "HTTPLive";
+
+ case DATA_SOURCE_TYPE_RTSP:
+ return "RTSP";
+
+ case DATA_SOURCE_TYPE_GENERIC_URL:
+ return "GenURL";
+
+ case DATA_SOURCE_TYPE_GENERIC_FD:
+ return "GenFD";
+
+ case DATA_SOURCE_TYPE_MEDIA:
+ return "Media";
+
+ case DATA_SOURCE_TYPE_STREAM:
+ return "Stream";
+
+ case DATA_SOURCE_TYPE_NONE:
+ default:
+ return "None";
+ }
+ }
+
// Modular DRM begin
status_t NuPlayer::prepareDrm(const uint8_t uuid[16], const Vector<uint8_t> &drmSessionId)
{
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index d542749..c69835f 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -93,6 +93,8 @@
status_t prepareDrm(const uint8_t uuid[16], const Vector<uint8_t> &drmSessionId);
status_t releaseDrm();
+ const char *getDataSourceType();
+
protected:
virtual ~NuPlayer();
@@ -236,6 +238,18 @@
sp<ICrypto> mCrypto;
bool mIsDrmProtected;
+ typedef enum {
+ DATA_SOURCE_TYPE_NONE,
+ DATA_SOURCE_TYPE_HTTP_LIVE,
+ DATA_SOURCE_TYPE_RTSP,
+ DATA_SOURCE_TYPE_GENERIC_URL,
+ DATA_SOURCE_TYPE_GENERIC_FD,
+ DATA_SOURCE_TYPE_MEDIA,
+ DATA_SOURCE_TYPE_STREAM,
+ } DATA_SOURCE_TYPE;
+
+ std::atomic<DATA_SOURCE_TYPE> mDataSourceType;
+
inline const sp<DecoderBase> &getDecoder(bool audio) {
return audio ? mAudioDecoder : mVideoDecoder;
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 9e579f9..a4a5861 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -95,7 +95,11 @@
}
NuPlayer::Decoder::~Decoder() {
- mCodec->release();
+ // Need to stop looper first since mCodec could be accessed on the mDecoderLooper.
+ stopLooper();
+ if (mCodec != NULL) {
+ mCodec->release();
+ }
releaseAndResetMediaBuffers();
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp
index 1210dc9..d0de7b0 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp
@@ -43,8 +43,7 @@
}
NuPlayer::DecoderBase::~DecoderBase() {
- mDecoderLooper->unregisterHandler(id());
- mDecoderLooper->stop();
+ stopLooper();
}
static
@@ -73,6 +72,11 @@
mDecoderLooper->registerHandler(this);
}
+void NuPlayer::DecoderBase::stopLooper() {
+ mDecoderLooper->unregisterHandler(id());
+ mDecoderLooper->stop();
+}
+
void NuPlayer::DecoderBase::setParameters(const sp<AMessage> ¶ms) {
sp<AMessage> msg = new AMessage(kWhatSetParameters, this);
msg->setMessage("params", params);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
index dcdfcaf..d44c396 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
@@ -69,6 +69,8 @@
virtual ~DecoderBase();
+ void stopLooper();
+
virtual void onMessageReceived(const sp<AMessage> &msg);
virtual void onConfigure(const sp<AMessage> &format) = 0;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index 01008b4..0c06976 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -53,6 +53,7 @@
static const char *kPlayerPlaying = "android.media.mediaplayer.playingMs";
static const char *kPlayerError = "android.media.mediaplayer.err";
static const char *kPlayerErrorCode = "android.media.mediaplayer.errcode";
+static const char *kPlayerDataSourceType = "android.media.mediaplayer.dataSource";
NuPlayerDriver::NuPlayerDriver(pid_t pid)
@@ -570,6 +571,8 @@
mAnalyticsItem->setInt64(kPlayerDuration, duration_ms);
mAnalyticsItem->setInt64(kPlayerPlaying, (mPlayingTimeUs+500)/1000 );
+
+ mAnalyticsItem->setCString(kPlayerDataSourceType, mPlayer->getDataSourceType());
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDrm.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDrm.cpp
index ce6cedc..b7c9db7 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDrm.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDrm.cpp
@@ -205,26 +205,8 @@
}
// Parcel has only private copy constructor so passing it in rather than returning
-void NuPlayerDrm::retrieveDrmInfo(const void *pssh, size_t psshsize,
- const Vector<String8> &mimes_in, Parcel *parcel)
+void NuPlayerDrm::retrieveDrmInfo(const void *pssh, size_t psshsize, Parcel *parcel)
{
- // 0) Make mimes a vector of unique items while keeping the original order; video first
- Vector<String8> mimes;
- for (size_t j = 0; j < mimes_in.size(); j++) {
- String8 mime = mimes_in[j];
- bool exists = false;
- for (size_t i = 0; i < mimes.size() && !exists; i++) {
- if (mimes[i] == mime) {
- exists = true;
- }
- } // for i
-
- if (!exists) {
- mimes.add(mime);
- }
- } // for j
-
-
// 1) PSSH bytes
parcel->writeUint32(psshsize);
parcel->writeByteArray(psshsize, (const uint8_t*)pssh);
@@ -242,18 +224,6 @@
ALOGV("retrieveDrmInfo: MEDIA_DRM_INFO supportedScheme[%zu] %s", i,
uuid.toHexString().string());
}
-
- // TODO: remove mimes after it's removed from Java DrmInfo
- // 3) mimes
- parcel->writeUint32(mimes.size());
- for (size_t i = 0; i < mimes.size(); i++) {
- // writing as String16 so the Java framework side can unpack it to Java String
- String16 mime(mimes[i]);
- parcel->writeString16(mime);
-
- ALOGV("retrieveDrmInfo: MEDIA_DRM_INFO MIME[%zu] %s",
- i, mimes[i].string());
- }
}
////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDrm.h b/media/libmediaplayerservice/nuplayer/NuPlayerDrm.h
index 6704bd1..6b8a2d9 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDrm.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDrm.h
@@ -84,8 +84,7 @@
static sp<ICrypto> createCryptoAndPlugin(const uint8_t uuid[16],
const Vector<uint8_t> &drmSessionId, status_t &status);
// Parcel has only private copy constructor so passing it in rather than returning
- static void retrieveDrmInfo(const void *pssh, size_t psshsize,
- const Vector<String8> &mimes_in, Parcel *parcel);
+ static void retrieveDrmInfo(const void *pssh, size_t psshsize, Parcel *parcel);
////////////////////////////////////////////////////////////////////////////////////////////
/// Helpers for NuPlayerDecoder
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 63b9571..9f1be22 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -251,6 +251,7 @@
virtual PortMode getPortMode(OMX_U32 portIndex);
+ virtual void stateExited();
virtual bool onMessageReceived(const sp<AMessage> &msg);
virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
@@ -546,13 +547,14 @@
mRepeatFrameDelayUs(-1ll),
mMaxPtsGapUs(-1ll),
mMaxFps(-1),
- mTimePerFrameUs(-1ll),
- mTimePerCaptureUs(-1ll),
+ mFps(-1.0),
+ mCaptureFps(-1.0),
mCreateInputBuffersSuspended(false),
mLatency(0),
mTunneled(false),
mDescribeColorAspectsIndex((OMX_INDEXTYPE)0),
mDescribeHDRStaticInfoIndex((OMX_INDEXTYPE)0),
+ mStateGeneration(0),
mVendorExtensionsStatus(kExtensionsUnchecked) {
mUninitializedState = new UninitializedState(this);
mLoadedState = new LoadedState(this);
@@ -1061,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) {
@@ -1104,7 +1134,8 @@
if (err == OK) {
err = setupNativeWindowSizeFormatAndUsage(
- mNativeWindow.get(), &mNativeWindowUsageBits, preregister /* reconnect */);
+ mNativeWindow.get(), &mNativeWindowUsageBits,
+ preregister && !mTunneled /* reconnect */);
}
if (err != OK) {
mNativeWindowUsageBits = 0;
@@ -1802,8 +1833,8 @@
mMaxFps = -1;
}
- if (!msg->findInt64("time-lapse", &mTimePerCaptureUs)) {
- mTimePerCaptureUs = -1ll;
+ if (!msg->findDouble("time-lapse-fps", &mCaptureFps)) {
+ mCaptureFps = -1.0;
}
if (!msg->findInt32(
@@ -3739,17 +3770,18 @@
def.nBufferSize = (video_def->nStride * video_def->nSliceHeight * 3) / 2;
- float frameRate;
- if (!msg->findFloat("frame-rate", &frameRate)) {
+ float framerate;
+ if (!msg->findFloat("frame-rate", &framerate)) {
int32_t tmp;
if (!msg->findInt32("frame-rate", &tmp)) {
return INVALID_OPERATION;
}
- frameRate = (float)tmp;
- mTimePerFrameUs = (int64_t) (1000000.0f / frameRate);
+ mFps = (double)tmp;
+ } else {
+ mFps = (double)framerate;
}
- video_def->xFramerate = (OMX_U32)(frameRate * 65536.0f);
+ video_def->xFramerate = (OMX_U32)(mFps * 65536);
video_def->eCompressionFormat = OMX_VIDEO_CodingUnused;
// this is redundant as it was already set up in setVideoPortFormatType
// FIXME for now skip this only for flexible YUV formats
@@ -5321,6 +5353,10 @@
return KEEP_BUFFERS;
}
+void ACodec::BaseState::stateExited() {
+ ++mCodec->mStateGeneration;
+}
+
bool ACodec::BaseState::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatInputBufferFilled:
@@ -5394,6 +5430,14 @@
ALOGE_IF("[%s] failed to release codec instance: err=%d",
mCodec->mComponentName.c_str(), err);
mCodec->mCallback->onReleaseCompleted();
+
+ mCodec->changeState(mCodec->mUninitializedState);
+ break;
+ }
+
+ case ACodec::kWhatForceStateTransition:
+ {
+ ALOGV("Already transitioned --- ignore");
break;
}
@@ -6597,11 +6641,10 @@
}
}
- if (mCodec->mTimePerCaptureUs > 0ll
- && mCodec->mTimePerFrameUs > 0ll) {
+ if (mCodec->mCaptureFps > 0. && mCodec->mFps > 0.) {
err = statusFromBinderStatus(
mCodec->mGraphicBufferSource->setTimeLapseConfig(
- mCodec->mTimePerFrameUs, mCodec->mTimePerCaptureUs));
+ mCodec->mFps, mCodec->mCaptureFps));
if (err != OK) {
ALOGE("[%s] Unable to configure time lapse (err %d)",
@@ -6988,7 +7031,6 @@
void ACodec::ExecutingState::stateEntered() {
ALOGV("[%s] Now Executing", mCodec->mComponentName.c_str());
-
mCodec->mRenderTracker.clear(systemTime(CLOCK_MONOTONIC));
mCodec->processDeferredMessages();
}
@@ -7614,6 +7656,30 @@
mCallback->onSignaledInputEOS(err);
}
+void ACodec::forceStateTransition(int generation) {
+ if (generation != mStateGeneration) {
+ ALOGV("Ignoring stale force state transition message: #%d (now #%d)",
+ generation, mStateGeneration);
+ return;
+ }
+ ALOGE("State machine stuck");
+ // Error must have already been signalled to the client.
+
+ // Deferred messages will be handled at LoadedState at the end of the
+ // transition.
+ mShutdownInProgress = true;
+ // No shutdown complete callback at the end of the transition.
+ mExplicitShutdown = false;
+ mKeepComponentAllocated = true;
+
+ status_t err = mOMXNode->sendCommand(OMX_CommandStateSet, OMX_StateIdle);
+ if (err != OK) {
+ // TODO: do some recovery here.
+ } else {
+ changeState(mExecutingToIdleState);
+ }
+}
+
bool ACodec::ExecutingState::onOMXFrameRendered(int64_t mediaTimeUs, nsecs_t systemNano) {
mCodec->onFrameRendered(mediaTimeUs, systemNano);
return true;
@@ -7680,7 +7746,14 @@
switch (msg->what()) {
case kWhatFlush:
- case kWhatShutdown:
+ case kWhatShutdown: {
+ if (mCodec->mFatalError) {
+ sp<AMessage> msg = new AMessage(ACodec::kWhatForceStateTransition, mCodec);
+ msg->setInt32("generation", mCodec->mStateGeneration);
+ msg->post(3000000);
+ }
+ // fall-through
+ }
case kWhatResume:
case kWhatSetParameters:
{
@@ -7693,6 +7766,16 @@
break;
}
+ case kWhatForceStateTransition:
+ {
+ int32_t generation = 0;
+ CHECK(msg->findInt32("generation", &generation));
+ mCodec->forceStateTransition(generation);
+
+ handled = true;
+ break;
+ }
+
default:
handled = BaseState::onMessageReceived(msg);
break;
@@ -7752,15 +7835,7 @@
if (err != OK) {
mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err));
-
- // This is technically not correct, but appears to be
- // the only way to free the component instance.
- // Controlled transitioning from excecuting->idle
- // and idle->loaded seem impossible probably because
- // the output port never finishes re-enabling.
- mCodec->mShutdownInProgress = true;
- mCodec->mKeepComponentAllocated = false;
- mCodec->changeState(mCodec->mLoadedState);
+ ALOGE("Error occurred while disabling the output port");
}
return true;
@@ -7785,7 +7860,7 @@
}
default:
- return false;
+ return BaseState::onOMXEvent(event, data1, data2);
}
}
@@ -7987,6 +8062,11 @@
case kWhatShutdown:
{
mCodec->deferMessage(msg);
+ if (mCodec->mFatalError) {
+ sp<AMessage> msg = new AMessage(ACodec::kWhatForceStateTransition, mCodec);
+ msg->setInt32("generation", mCodec->mStateGeneration);
+ msg->post(3000000);
+ }
break;
}
@@ -7997,6 +8077,16 @@
break;
}
+ case kWhatForceStateTransition:
+ {
+ int32_t generation = 0;
+ CHECK(msg->findInt32("generation", &generation));
+ mCodec->forceStateTransition(generation);
+
+ handled = true;
+ break;
+ }
+
default:
handled = BaseState::onMessageReceived(msg);
break;
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 61b8f9d..372b11a 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -78,6 +78,7 @@
libaudioutils \
libbinder \
libcamera_client \
+ libcrypto \
libcutils \
libdl \
libdrmframework \
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
index 9965462..c91c82b 100644
--- a/media/libstagefright/MediaExtractor.cpp
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -158,7 +158,7 @@
if (mime == NULL) {
float confidence;
if (!sniff(source, &tmp, &confidence, &meta)) {
- ALOGV("FAILED to autodetect media content.");
+ ALOGW("FAILED to autodetect media content.");
return NULL;
}
diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp
index e3ca516..51f1ba3 100644
--- a/media/libstagefright/NuMediaExtractor.cpp
+++ b/media/libstagefright/NuMediaExtractor.cpp
@@ -297,6 +297,10 @@
sp<IMediaSource> source = mImpl->getTrack(index);
+ if (source == nullptr) {
+ return ERROR_MALFORMED;
+ }
+
status_t ret = source->start();
if (ret != OK) {
return ret;
diff --git a/media/libstagefright/OMXClient.cpp b/media/libstagefright/OMXClient.cpp
index 02d275b..5f9aa01 100644
--- a/media/libstagefright/OMXClient.cpp
+++ b/media/libstagefright/OMXClient.cpp
@@ -37,6 +37,10 @@
OMXClient::OMXClient() {
}
+status_t OMXClient::connect() {
+ return connect(nullptr);
+}
+
status_t OMXClient::connect(bool* trebleFlag) {
if (property_get_bool("persist.media.treble_omx", true)) {
if (trebleFlag != nullptr) {
diff --git a/media/libstagefright/SurfaceUtils.cpp b/media/libstagefright/SurfaceUtils.cpp
index b6b315d..b7c1598 100644
--- a/media/libstagefright/SurfaceUtils.cpp
+++ b/media/libstagefright/SurfaceUtils.cpp
@@ -91,9 +91,19 @@
return err;
}
- // Check if the ANativeWindow uses hardware protected buffers.
- if (queuesToNativeWindow != 1 && !(consumerUsage & GRALLOC_USAGE_PROTECTED)) {
- ALOGE("native window could not be authenticated");
+ // Check if the consumer end of the ANativeWindow can handle protected content.
+ int isConsumerProtected = 0;
+ err = nativeWindow->query(
+ nativeWindow, NATIVE_WINDOW_CONSUMER_IS_PROTECTED, &isConsumerProtected);
+ if (err != NO_ERROR) {
+ ALOGE("error query native window: %s (%d)", strerror(-err), -err);
+ return err;
+ }
+
+ // Deny queuing into native window if neither condition is satisfied.
+ if (queuesToNativeWindow != 1 && isConsumerProtected != 1) {
+ ALOGE("native window cannot handle protected buffers: the consumer should either be "
+ "a hardware composer or support hardware protection");
return PERMISSION_DENIED;
}
}
diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp
index bbcea51..00cf142 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.cpp
+++ b/media/libstagefright/httplive/PlaylistFetcher.cpp
@@ -26,6 +26,7 @@
#include "include/avc_utils.h"
#include "include/ID3.h"
#include "mpeg2ts/AnotherPacketSource.h"
+#include "mpeg2ts/HlsSampleDecryptor.h"
#include <media/stagefright/foundation/ABitReader.h>
#include <media/stagefright/foundation/ABuffer.h>
@@ -36,7 +37,6 @@
#include <ctype.h>
#include <inttypes.h>
-#include <openssl/aes.h>
#define FLOGV(fmt, ...) ALOGV("[fetcher-%d] " fmt, mFetcherID, ##__VA_ARGS__)
#define FSLOGV(stream, fmt, ...) ALOGV("[fetcher-%d] [%s] " fmt, mFetcherID, \
@@ -167,11 +167,15 @@
mFirstPTSValid(false),
mFirstTimeUs(-1ll),
mVideoBuffer(new AnotherPacketSource(NULL)),
+ mSampleAesKeyItemChanged(false),
mThresholdRatio(-1.0f),
mDownloadState(new DownloadState()),
mHasMetadata(false) {
memset(mPlaylistHash, 0, sizeof(mPlaylistHash));
mHTTPDownloader = mSession->getHTTPDownloader();
+
+ memset(mKeyData, 0, sizeof(mKeyData));
+ memset(mAESInitVec, 0, sizeof(mAESInitVec));
}
PlaylistFetcher::~PlaylistFetcher() {
@@ -306,6 +310,15 @@
}
}
+ // TODO: Revise this when we add support for KEYFORMAT
+ // If method has changed (e.g., -> NONE); sufficient to check at the segment boundary
+ if (mSampleAesKeyItem != NULL && first && found && method != "SAMPLE-AES") {
+ ALOGI("decryptBuffer: resetting mSampleAesKeyItem(%p) with method %s",
+ mSampleAesKeyItem.get(), method.c_str());
+ mSampleAesKeyItem = NULL;
+ mSampleAesKeyItemChanged = true;
+ }
+
if (!found) {
method = "NONE";
}
@@ -313,6 +326,8 @@
if (method == "NONE") {
return OK;
+ } else if (method == "SAMPLE-AES") {
+ ALOGV("decryptBuffer: Non-Widevine SAMPLE-AES is supported now.");
} else if (!(method == "AES-128")) {
ALOGE("Unsupported cipher method '%s'", method.c_str());
return ERROR_UNSUPPORTED;
@@ -345,6 +360,79 @@
mAESKeyForURI.add(keyURI, key);
}
+ if (first) {
+ // If decrypting the first block in a file, read the iv from the manifest
+ // or derive the iv from the file's sequence number.
+
+ unsigned char AESInitVec[AES_BLOCK_SIZE];
+ AString iv;
+ if (itemMeta->findString("cipher-iv", &iv)) {
+ if ((!iv.startsWith("0x") && !iv.startsWith("0X"))
+ || iv.size() > 16 * 2 + 2) {
+ ALOGE("malformed cipher IV '%s'.", iv.c_str());
+ return ERROR_MALFORMED;
+ }
+
+ while (iv.size() < 16 * 2 + 2) {
+ iv.insert("0", 1, 2);
+ }
+
+ memset(AESInitVec, 0, sizeof(AESInitVec));
+ for (size_t i = 0; i < 16; ++i) {
+ char c1 = tolower(iv.c_str()[2 + 2 * i]);
+ char c2 = tolower(iv.c_str()[3 + 2 * i]);
+ if (!isxdigit(c1) || !isxdigit(c2)) {
+ ALOGE("malformed cipher IV '%s'.", iv.c_str());
+ return ERROR_MALFORMED;
+ }
+ uint8_t nibble1 = isdigit(c1) ? c1 - '0' : c1 - 'a' + 10;
+ uint8_t nibble2 = isdigit(c2) ? c2 - '0' : c2 - 'a' + 10;
+
+ AESInitVec[i] = nibble1 << 4 | nibble2;
+ }
+ } else {
+ memset(AESInitVec, 0, sizeof(AESInitVec));
+ AESInitVec[15] = mSeqNumber & 0xff;
+ AESInitVec[14] = (mSeqNumber >> 8) & 0xff;
+ AESInitVec[13] = (mSeqNumber >> 16) & 0xff;
+ AESInitVec[12] = (mSeqNumber >> 24) & 0xff;
+ }
+
+ bool newKey = memcmp(mKeyData, key->data(), AES_BLOCK_SIZE) != 0;
+ bool newInitVec = memcmp(mAESInitVec, AESInitVec, AES_BLOCK_SIZE) != 0;
+ bool newSampleAesKeyItem = newKey || newInitVec;
+ ALOGV("decryptBuffer: SAMPLE-AES newKeyItem %d/%d (Key %d initVec %d)",
+ mSampleAesKeyItemChanged, newSampleAesKeyItem, newKey, newInitVec);
+
+ if (newSampleAesKeyItem) {
+ memcpy(mKeyData, key->data(), AES_BLOCK_SIZE);
+ memcpy(mAESInitVec, AESInitVec, AES_BLOCK_SIZE);
+
+ if (method == "SAMPLE-AES") {
+ mSampleAesKeyItemChanged = true;
+
+ sp<ABuffer> keyDataBuffer = ABuffer::CreateAsCopy(mKeyData, sizeof(mKeyData));
+ sp<ABuffer> initVecBuffer = ABuffer::CreateAsCopy(mAESInitVec, sizeof(mAESInitVec));
+
+ // always allocating a new one rather than updating the old message
+ // lower layer might still have a reference to the old message
+ mSampleAesKeyItem = new AMessage();
+ mSampleAesKeyItem->setBuffer("keyData", keyDataBuffer);
+ mSampleAesKeyItem->setBuffer("initVec", initVecBuffer);
+
+ ALOGV("decryptBuffer: New SampleAesKeyItem: Key: %s IV: %s",
+ HlsSampleDecryptor::aesBlockToStr(mKeyData).c_str(),
+ HlsSampleDecryptor::aesBlockToStr(mAESInitVec).c_str());
+ } // SAMPLE-AES
+ } // newSampleAesKeyItem
+ } // first
+
+ if (method == "SAMPLE-AES") {
+ ALOGV("decryptBuffer: skipping full-seg decrypt for SAMPLE-AES");
+ return OK;
+ }
+
+
AES_KEY aes_key;
if (AES_set_decrypt_key(key->data(), 128, &aes_key) != 0) {
ALOGE("failed to set AES decryption key.");
@@ -361,44 +449,6 @@
return ERROR_MALFORMED;
}
- if (first) {
- // If decrypting the first block in a file, read the iv from the manifest
- // or derive the iv from the file's sequence number.
-
- AString iv;
- if (itemMeta->findString("cipher-iv", &iv)) {
- if ((!iv.startsWith("0x") && !iv.startsWith("0X"))
- || iv.size() > 16 * 2 + 2) {
- ALOGE("malformed cipher IV '%s'.", iv.c_str());
- return ERROR_MALFORMED;
- }
-
- while (iv.size() < 16 * 2 + 2) {
- iv.insert("0", 1, 2);
- }
-
- memset(mAESInitVec, 0, sizeof(mAESInitVec));
- for (size_t i = 0; i < 16; ++i) {
- char c1 = tolower(iv.c_str()[2 + 2 * i]);
- char c2 = tolower(iv.c_str()[3 + 2 * i]);
- if (!isxdigit(c1) || !isxdigit(c2)) {
- ALOGE("malformed cipher IV '%s'.", iv.c_str());
- return ERROR_MALFORMED;
- }
- uint8_t nibble1 = isdigit(c1) ? c1 - '0' : c1 - 'a' + 10;
- uint8_t nibble2 = isdigit(c2) ? c2 - '0' : c2 - 'a' + 10;
-
- mAESInitVec[i] = nibble1 << 4 | nibble2;
- }
- } else {
- memset(mAESInitVec, 0, sizeof(mAESInitVec));
- mAESInitVec[15] = mSeqNumber & 0xff;
- mAESInitVec[14] = (mSeqNumber >> 8) & 0xff;
- mAESInitVec[13] = (mSeqNumber >> 16) & 0xff;
- mAESInitVec[12] = (mSeqNumber >> 24) & 0xff;
- }
- }
-
AES_cbc_encrypt(
buffer->data(), buffer->data(), buffer->size(),
&aes_key, mAESInitVec, AES_DECRYPT);
@@ -409,7 +459,7 @@
status_t PlaylistFetcher::checkDecryptPadding(const sp<ABuffer> &buffer) {
AString method;
CHECK(buffer->meta()->findString("cipher-method", &method));
- if (method == "NONE") {
+ if (method == "NONE" || method == "SAMPLE-AES") {
return OK;
}
@@ -1656,6 +1706,11 @@
mNextPTSTimeUs = -1ll;
}
+ if (mSampleAesKeyItemChanged) {
+ mTSParser->signalNewSampleAesKey(mSampleAesKeyItem);
+ mSampleAesKeyItemChanged = false;
+ }
+
size_t offset = 0;
while (offset + 188 <= buffer->size()) {
status_t err = mTSParser->feedTSPacket(buffer->data() + offset, 188);
@@ -2038,10 +2093,24 @@
}
}
+ sp<HlsSampleDecryptor> sampleDecryptor = NULL;
+ if (mSampleAesKeyItem != NULL) {
+ ALOGV("extractAndQueueAccessUnits[%d] SampleAesKeyItem: Key: %s IV: %s",
+ mSeqNumber,
+ HlsSampleDecryptor::aesBlockToStr(mKeyData).c_str(),
+ HlsSampleDecryptor::aesBlockToStr(mAESInitVec).c_str());
+
+ sampleDecryptor = new HlsSampleDecryptor(mSampleAesKeyItem);
+ }
+
+ int frameId = 0;
+
size_t offset = 0;
while (offset < buffer->size()) {
const uint8_t *adtsHeader = buffer->data() + offset;
CHECK_LT(offset + 5, buffer->size());
+ // non-const pointer for decryption if needed
+ uint8_t *adtsFrame = buffer->data() + offset;
unsigned aac_frame_length =
((adtsHeader[3] & 3) << 11)
@@ -2099,6 +2168,18 @@
}
}
+ if (sampleDecryptor != NULL) {
+ bool protection_absent = (adtsHeader[1] & 0x1);
+ size_t headerSize = protection_absent ? 7 : 9;
+ if (frameId == 0) {
+ ALOGV("extractAndQueueAAC[%d] protection_absent %d (%02x) headerSize %zu",
+ mSeqNumber, protection_absent, adtsHeader[1], headerSize);
+ }
+
+ sampleDecryptor->processAAC(headerSize, adtsFrame, aac_frame_length);
+ }
+ frameId++;
+
sp<ABuffer> unit = new ABuffer(aac_frame_length);
memcpy(unit->data(), adtsHeader, aac_frame_length);
diff --git a/media/libstagefright/httplive/PlaylistFetcher.h b/media/libstagefright/httplive/PlaylistFetcher.h
index ee7d3a1..d7db54a 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.h
+++ b/media/libstagefright/httplive/PlaylistFetcher.h
@@ -19,6 +19,7 @@
#define PLAYLIST_FETCHER_H_
#include <media/stagefright/foundation/AHandler.h>
+#include <openssl/aes.h>
#include "mpeg2ts/ATSParser.h"
#include "LiveSession.h"
@@ -175,7 +176,10 @@
// Stores the initialization vector to decrypt the next block of cipher text, which can
// either be derived from the sequence number, read from the manifest, or copied from
// the last block of cipher text (cipher-block chaining).
- unsigned char mAESInitVec[16];
+ unsigned char mAESInitVec[AES_BLOCK_SIZE];
+ unsigned char mKeyData[AES_BLOCK_SIZE];
+ bool mSampleAesKeyItemChanged;
+ sp<AMessage> mSampleAesKeyItem;
Mutex mThresholdLock;
float mThresholdRatio;
diff --git a/media/libstagefright/include/ACodec.h b/media/libstagefright/include/ACodec.h
index 6c1a5c6..22b8657 100644
--- a/media/libstagefright/include/ACodec.h
+++ b/media/libstagefright/include/ACodec.h
@@ -150,6 +150,7 @@
kWhatSubmitOutputMetadataBufferIfEOS = 'subm',
kWhatOMXDied = 'OMXd',
kWhatReleaseCodecInstance = 'relC',
+ kWhatForceStateTransition = 'fstt',
};
enum {
@@ -293,8 +294,8 @@
int64_t mRepeatFrameDelayUs;
int64_t mMaxPtsGapUs;
float mMaxFps;
- int64_t mTimePerFrameUs;
- int64_t mTimePerCaptureUs;
+ double mFps;
+ double mCaptureFps;
bool mCreateInputBuffersSuspended;
uint32_t mLatency;
@@ -305,6 +306,8 @@
std::shared_ptr<ACodecBufferChannel> mBufferChannel;
+ int32_t mStateGeneration;
+
enum {
kExtensionsUnchecked,
kExtensionsNone,
@@ -569,6 +572,9 @@
// Send EOS on input stream.
void onSignalEndOfInputStream();
+ // Force EXEC->IDLE->LOADED shutdown sequence if not stale.
+ void forceStateTransition(int generation);
+
DISALLOW_EVIL_CONSTRUCTORS(ACodec);
};
diff --git a/media/libstagefright/include/OMXClient.h b/media/libstagefright/include/OMXClient.h
index 315f19b..203a181 100644
--- a/media/libstagefright/include/OMXClient.h
+++ b/media/libstagefright/include/OMXClient.h
@@ -26,7 +26,9 @@
public:
OMXClient();
- status_t connect(bool* trebleFlag = nullptr);
+ status_t connect();
+ status_t connect(bool* trebleFlag);
+
status_t connectLegacy();
status_t connectTreble();
void disconnect();
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 8099edb..31edb21 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -105,6 +105,8 @@
void updateCasSessions();
+ void signalNewSampleAesKey(const sp<AMessage> &keyItem);
+
private:
struct StreamInfo {
unsigned mType;
@@ -119,6 +121,7 @@
bool mFirstPTSValid;
uint64_t mFirstPTS;
int64_t mLastRecoveredPTS;
+ sp<AMessage> mSampleAesKeyItem;
status_t parseProgramMap(ABitReader *br);
int64_t recoverPTS(uint64_t PTS_33bit);
@@ -168,6 +171,8 @@
bool isVideo() const;
bool isMeta() const;
+ void signalNewSampleAesKey(const sp<AMessage> &keyItem);
+
protected:
virtual ~Stream();
@@ -194,6 +199,8 @@
ElementaryStreamQueue *mQueue;
bool mScrambled;
+ bool mSampleEncrypted;
+ sp<AMessage> mSampleAesKeyItem;
sp<IMemory> mMem;
sp<MemoryDealer> mDealer;
sp<ABuffer> mDescrambledBuffer;
@@ -586,6 +593,10 @@
sp<Stream> stream = new Stream(
this, info.mPID, info.mType, PCR_PID, info.mCASystemId);
+ if (mSampleAesKeyItem != NULL) {
+ stream->signalNewSampleAesKey(mSampleAesKeyItem);
+ }
+
isAddingScrambledStream |= info.mCASystemId >= 0;
mStreams.add(info.mPID, stream);
}
@@ -710,22 +721,32 @@
mPrevPTS(0),
mQueue(NULL),
mScrambled(CA_system_ID >= 0) {
- ALOGV("new stream PID 0x%02x, type 0x%02x, scrambled %d",
- elementaryPID, streamType, mScrambled);
- uint32_t flags = (isVideo() && mScrambled) ?
- ElementaryStreamQueue::kFlag_ScrambledData : 0;
+ mSampleEncrypted =
+ mStreamType == STREAMTYPE_H264_ENCRYPTED ||
+ mStreamType == STREAMTYPE_AAC_ENCRYPTED ||
+ mStreamType == STREAMTYPE_AC3_ENCRYPTED;
+
+ ALOGV("new stream PID 0x%02x, type 0x%02x, scrambled %d, SampleEncrypted: %d",
+ elementaryPID, streamType, mScrambled, mSampleEncrypted);
+
+ uint32_t flags =
+ (isVideo() && mScrambled) ? ElementaryStreamQueue::kFlag_ScrambledData :
+ (mSampleEncrypted) ? ElementaryStreamQueue::kFlag_SampleEncryptedData :
+ 0;
ElementaryStreamQueue::Mode mode = ElementaryStreamQueue::INVALID;
switch (mStreamType) {
case STREAMTYPE_H264:
+ case STREAMTYPE_H264_ENCRYPTED:
mode = ElementaryStreamQueue::H264;
flags |= (mProgram->parserFlags() & ALIGNED_VIDEO_DATA) ?
ElementaryStreamQueue::kFlag_AlignedData : 0;
break;
case STREAMTYPE_MPEG2_AUDIO_ADTS:
+ case STREAMTYPE_AAC_ENCRYPTED:
mode = ElementaryStreamQueue::AAC;
break;
@@ -745,6 +766,7 @@
case STREAMTYPE_LPCM_AC3:
case STREAMTYPE_AC3:
+ case STREAMTYPE_AC3_ENCRYPTED:
mode = ElementaryStreamQueue::AC3;
break;
@@ -761,6 +783,10 @@
mQueue = new ElementaryStreamQueue(mode, flags);
if (mQueue != NULL) {
+ if (mSampleAesKeyItem != NULL) {
+ mQueue->signalNewSampleAesKey(mSampleAesKeyItem);
+ }
+
ensureBufferCapacity(kInitialStreamBufferSize);
if (mScrambled && (isAudio() || isVideo())) {
@@ -913,6 +939,7 @@
bool ATSParser::Stream::isVideo() const {
switch (mStreamType) {
case STREAMTYPE_H264:
+ case STREAMTYPE_H264_ENCRYPTED:
case STREAMTYPE_MPEG1_VIDEO:
case STREAMTYPE_MPEG2_VIDEO:
case STREAMTYPE_MPEG4_VIDEO:
@@ -930,6 +957,8 @@
case STREAMTYPE_MPEG2_AUDIO_ADTS:
case STREAMTYPE_LPCM_AC3:
case STREAMTYPE_AC3:
+ case STREAMTYPE_AAC_ENCRYPTED:
+ case STREAMTYPE_AC3_ENCRYPTED:
return true;
default:
@@ -1454,7 +1483,7 @@
mPrevPTS = PTS;
#endif
- ALOGV("onPayloadData mStreamType=0x%02x", mStreamType);
+ ALOGV("onPayloadData mStreamType=0x%02x size: %zu", mStreamType, size);
int64_t timeUs = 0ll; // no presentation timestamp available.
if (PTS_DTS_flags == 2 || PTS_DTS_flags == 3) {
@@ -1492,6 +1521,8 @@
}
mSource = new AnotherPacketSource(meta);
mSource->queueAccessUnit(accessUnit);
+ ALOGV("onPayloadData: created AnotherPacketSource PID 0x%08x of type 0x%02x",
+ mElementaryPID, mStreamType);
}
} else if (mQueue->getFormat() != NULL) {
// After a discontinuity we invalidate the queue's format
@@ -1730,6 +1761,9 @@
if (!found) {
mPrograms.push(
new Program(this, program_number, programMapPID, mLastRecoveredPTS));
+ if (mSampleAesKeyItem != NULL) {
+ mPrograms.top()->signalNewSampleAesKey(mSampleAesKeyItem);
+ }
}
if (mPSISections.indexOfKey(programMapPID) < 0) {
@@ -2228,4 +2262,40 @@
ALOGV("crc: %08x\n", crc);
return (crc == 0);
}
+
+// SAMPLE_AES key handling
+// TODO: Merge these to their respective class after Widevine-HLS
+void ATSParser::signalNewSampleAesKey(const sp<AMessage> &keyItem) {
+ ALOGD("signalNewSampleAesKey: %p", keyItem.get());
+
+ mSampleAesKeyItem = keyItem;
+
+ // a NULL key item will propagate to existing ElementaryStreamQueues
+ for (size_t i = 0; i < mPrograms.size(); ++i) {
+ mPrograms[i]->signalNewSampleAesKey(keyItem);
+ }
+}
+
+void ATSParser::Program::signalNewSampleAesKey(const sp<AMessage> &keyItem) {
+ ALOGD("Program::signalNewSampleAesKey: %p", keyItem.get());
+
+ mSampleAesKeyItem = keyItem;
+
+ // a NULL key item will propagate to existing ElementaryStreamQueues
+ for (size_t i = 0; i < mStreams.size(); ++i) {
+ mStreams[i]->signalNewSampleAesKey(keyItem);
+ }
+}
+
+void ATSParser::Stream::signalNewSampleAesKey(const sp<AMessage> &keyItem) {
+ ALOGD("Stream::signalNewSampleAesKey: 0x%04x size = %zu keyItem: %p",
+ mElementaryPID, mBuffer->size(), keyItem.get());
+
+ // a NULL key item will propagate to existing ElementaryStreamQueues
+ mSampleAesKeyItem = keyItem;
+
+ flush(NULL);
+ mQueue->signalNewSampleAesKey(keyItem);
+}
+
} // namespace android
diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h
index 4a88713..374e011 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.h
+++ b/media/libstagefright/mpeg2ts/ATSParser.h
@@ -131,6 +131,8 @@
int64_t getFirstPTSTimeUs();
+ void signalNewSampleAesKey(const sp<AMessage> &keyItem);
+
enum {
// From ISO/IEC 13818-1: 2000 (E), Table 2-29
STREAMTYPE_RESERVED = 0x00,
@@ -149,6 +151,11 @@
// Stream type 0x83 is non-standard,
// it could be LPCM or TrueHD AC3
STREAMTYPE_LPCM_AC3 = 0x83,
+
+ //Sample Encrypted types
+ STREAMTYPE_H264_ENCRYPTED = 0xDB,
+ STREAMTYPE_AAC_ENCRYPTED = 0xCF,
+ STREAMTYPE_AC3_ENCRYPTED = 0xC1,
};
protected:
@@ -181,6 +188,8 @@
size_t mNumTSPacketsParsed;
+ sp<AMessage> mSampleAesKeyItem;
+
void parseProgramAssociationTable(ABitReader *br);
void parseProgramMap(ABitReader *br);
// Parse PES packet where br is pointing to. If the PES contains a sync
diff --git a/media/libstagefright/mpeg2ts/Android.mk b/media/libstagefright/mpeg2ts/Android.mk
index 5140e66..20acfe7 100644
--- a/media/libstagefright/mpeg2ts/Android.mk
+++ b/media/libstagefright/mpeg2ts/Android.mk
@@ -7,6 +7,7 @@
ATSParser.cpp \
CasManager.cpp \
ESQueue.cpp \
+ HlsSampleDecryptor.cpp \
MPEG2PSExtractor.cpp \
MPEG2TSExtractor.cpp \
@@ -18,7 +19,9 @@
LOCAL_SANITIZE := unsigned-integer-overflow signed-integer-overflow cfi
LOCAL_SANITIZE_DIAG := cfi
-LOCAL_SHARED_LIBRARIES := libmedia
+LOCAL_SHARED_LIBRARIES := \
+ libcrypto \
+ libmedia \
LOCAL_MODULE:= libstagefright_mpeg2ts
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index ae7ec77..f1b44ae 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -42,7 +42,15 @@
: mMode(mode),
mFlags(flags),
mEOSReached(false),
- mCASystemId(0) {
+ mCASystemId(0),
+ mAUIndex(0) {
+
+ ALOGV("ElementaryStreamQueue(%p) mode %x flags %x isScrambled %d isSampleEncrypted %d",
+ this, mode, flags, isScrambled(), isSampleEncrypted());
+
+ // Create the decryptor anyway since we don't know the use-case unless key is provided
+ // Won't decrypt if key info not available (e.g., scanner/extractor just parsing ts files)
+ mSampleDecryptor = isSampleEncrypted() ? new HlsSampleDecryptor : NULL;
}
sp<MetaData> ElementaryStreamQueue::getFormat() {
@@ -659,6 +667,9 @@
unsigned syncStartPos = 0; // in bytes
unsigned payloadSize = 0;
sp<MetaData> format = new MetaData;
+
+ ALOGV("dequeueAccessUnit_AC3[%d]: mBuffer %p(%zu)", mAUIndex, mBuffer->data(), mBuffer->size());
+
while (true) {
if (syncStartPos + 2 >= mBuffer->size()) {
return NULL;
@@ -671,6 +682,10 @@
if (payloadSize > 0) {
break;
}
+
+ ALOGV("dequeueAccessUnit_AC3[%d]: syncStartPos %u payloadSize %u",
+ mAUIndex, syncStartPos, payloadSize);
+
++syncStartPos;
}
@@ -683,14 +698,22 @@
mFormat = format;
}
- sp<ABuffer> accessUnit = new ABuffer(syncStartPos + payloadSize);
- memcpy(accessUnit->data(), mBuffer->data(), syncStartPos + payloadSize);
int64_t timeUs = fetchTimestamp(syncStartPos + payloadSize);
if (timeUs < 0ll) {
ALOGE("negative timeUs");
return NULL;
}
+
+ // Not decrypting if key info not available (e.g., scanner/extractor parsing ts files)
+ if (mSampleDecryptor != NULL) {
+ mSampleDecryptor->processAC3(mBuffer->data() + syncStartPos, payloadSize);
+ }
+ mAUIndex++;
+
+ sp<ABuffer> accessUnit = new ABuffer(syncStartPos + payloadSize);
+ memcpy(accessUnit->data(), mBuffer->data(), syncStartPos + payloadSize);
+
accessUnit->meta()->setInt64("timeUs", timeUs);
accessUnit->meta()->setInt32("isSync", 1);
@@ -791,6 +814,17 @@
return NULL;
}
+ ALOGV("dequeueAccessUnit_AAC[%d]: mBuffer %zu info.mLength %zu",
+ mAUIndex, mBuffer->size(), info.mLength);
+
+ struct ADTSPosition {
+ size_t offset;
+ size_t headerSize;
+ size_t length;
+ };
+
+ Vector<ADTSPosition> frames;
+
// The idea here is consume all AAC frames starting at offsets before
// info.mLength so we can assign a meaningful timestamp without
// having to interpolate.
@@ -811,7 +845,7 @@
return NULL;
}
bits.skipBits(3); // ID, layer
- bool protection_absent __unused = bits.getBits(1) != 0;
+ bool protection_absent = bits.getBits(1) != 0;
if (mFormat == NULL) {
unsigned profile = bits.getBits(2);
@@ -873,11 +907,36 @@
return NULL;
}
- size_t headerSize __unused = protection_absent ? 7 : 9;
+ size_t headerSize = protection_absent ? 7 : 9;
+
+ // tracking the frame positions first then decrypt only if an accessUnit to be generated
+ if (mSampleDecryptor != NULL) {
+ ADTSPosition frame = {
+ .offset = offset,
+ .headerSize = headerSize,
+ .length = aac_frame_length
+ };
+
+ frames.push(frame);
+ }
offset += aac_frame_length;
}
+ // Decrypting only if the loop didn't exit early and an accessUnit is about to be generated
+ // Not decrypting if key info not available (e.g., scanner/extractor parsing ts files)
+ if (mSampleDecryptor != NULL) {
+ for (size_t frameId = 0; frameId < frames.size(); frameId++) {
+ const ADTSPosition &frame = frames.itemAt(frameId);
+
+ mSampleDecryptor->processAAC(frame.headerSize,
+ mBuffer->data() + frame.offset, frame.length);
+// ALOGV("dequeueAccessUnitAAC[%zu]: while offset %zu headerSize %zu frame_len %zu",
+// frameId, frame.offset, frame.headerSize, frame.length);
+ }
+ }
+ mAUIndex++;
+
int64_t timeUs = fetchTimestamp(offset);
sp<ABuffer> accessUnit = new ABuffer(offset);
@@ -970,6 +1029,9 @@
size_t nalSize;
bool foundSlice = false;
bool foundIDR = false;
+
+ ALOGV("dequeueAccessUnit_H264[%d] %p/%zu", mAUIndex, data, size);
+
while ((err = getNextNALUnit(&data, &size, &nalStart, &nalSize)) == OK) {
if (nalSize == 0) continue;
@@ -981,6 +1043,7 @@
foundIDR = true;
}
if (foundSlice) {
+ //TODO: Shouldn't this have been called with nalSize-1?
ABitReader br(nalStart + 1, nalSize);
unsigned first_mb_in_slice = parseUE(&br);
@@ -1021,6 +1084,7 @@
size_t dstOffset = 0;
size_t seiIndex = 0;
+ size_t shrunkBytes = 0;
for (size_t i = 0; i < nals.size(); ++i) {
const NALPosition &pos = nals.itemAt(i);
@@ -1047,11 +1111,30 @@
memcpy(accessUnit->data() + dstOffset, "\x00\x00\x00\x01", 4);
- memcpy(accessUnit->data() + dstOffset + 4,
- mBuffer->data() + pos.nalOffset,
- pos.nalSize);
+ if (mSampleDecryptor != NULL && (nalType == 1 || nalType == 5)) {
+ uint8_t *nalData = mBuffer->data() + pos.nalOffset;
+ size_t newSize = mSampleDecryptor->processNal(nalData, pos.nalSize);
+ // Note: the data can shrink due to unescaping
+ memcpy(accessUnit->data() + dstOffset + 4,
+ nalData,
+ newSize);
+ dstOffset += newSize + 4;
- dstOffset += pos.nalSize + 4;
+ size_t thisShrunkBytes = pos.nalSize - newSize;
+ //ALOGV("dequeueAccessUnitH264[%d]: nalType: %d -> %zu (%zu)",
+ // nalType, (int)pos.nalSize, newSize, thisShrunkBytes);
+
+ shrunkBytes += thisShrunkBytes;
+ }
+ else {
+ memcpy(accessUnit->data() + dstOffset + 4,
+ mBuffer->data() + pos.nalOffset,
+ pos.nalSize);
+
+ dstOffset += pos.nalSize + 4;
+ //ALOGV("dequeueAccessUnitH264 [%d] %d @%d",
+ // nalType, (int)pos.nalSize, (int)pos.nalOffset);
+ }
}
#if !LOG_NDEBUG
@@ -1082,6 +1165,18 @@
mFormat = MakeAVCCodecSpecificData(accessUnit);
}
+ if (mSampleDecryptor != NULL && shrunkBytes > 0) {
+ size_t adjustedSize = accessUnit->size() - shrunkBytes;
+ ALOGV("dequeueAccessUnitH264[%d]: AU size adjusted %zu -> %zu",
+ mAUIndex, accessUnit->size(), adjustedSize);
+ accessUnit->setRange(0, adjustedSize);
+ }
+
+ ALOGV("dequeueAccessUnitH264[%d]: AU %p(%zu) dstOffset:%zu, nals:%zu, totalSize:%zu ",
+ mAUIndex, accessUnit->data(), accessUnit->size(),
+ dstOffset, nals.size(), totalSize);
+ mAUIndex++;
+
return accessUnit;
}
@@ -1612,4 +1707,15 @@
return accessUnit;
}
+void ElementaryStreamQueue::signalNewSampleAesKey(const sp<AMessage> &keyItem) {
+ if (mSampleDecryptor == NULL) {
+ ALOGE("signalNewSampleAesKey: Stream %x is not encrypted; keyItem: %p",
+ mMode, keyItem.get());
+ return;
+ }
+
+ mSampleDecryptor->signalNewSampleAesKey(keyItem);
+}
+
+
} // namespace android
diff --git a/media/libstagefright/mpeg2ts/ESQueue.h b/media/libstagefright/mpeg2ts/ESQueue.h
index 11e1af7..ffcb502 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.h
+++ b/media/libstagefright/mpeg2ts/ESQueue.h
@@ -19,11 +19,14 @@
#define ES_QUEUE_H_
#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AMessage.h>
#include <utils/Errors.h>
#include <utils/List.h>
#include <utils/RefBase.h>
#include <vector>
+#include "HlsSampleDecryptor.h"
+
namespace android {
struct ABuffer;
@@ -46,6 +49,7 @@
// Data appended to the queue is always at access unit boundaries.
kFlag_AlignedData = 1,
kFlag_ScrambledData = 2,
+ kFlag_SampleEncryptedData = 4,
};
explicit ElementaryStreamQueue(Mode mode, uint32_t flags = 0);
@@ -69,6 +73,8 @@
void setCasInfo(int32_t systemId, const std::vector<uint8_t> &sessionId);
+ void signalNewSampleAesKey(const sp<AMessage> &keyItem);
+
private:
struct RangeInfo {
int64_t mTimestampUs;
@@ -100,6 +106,13 @@
sp<MetaData> mFormat;
+ sp<HlsSampleDecryptor> mSampleDecryptor;
+ int mAUIndex;
+
+ bool isSampleEncrypted() const {
+ return (mFlags & kFlag_SampleEncryptedData) != 0;
+ }
+
sp<ABuffer> dequeueAccessUnitH264();
sp<ABuffer> dequeueAccessUnitAAC();
sp<ABuffer> dequeueAccessUnitAC3();
diff --git a/media/libstagefright/mpeg2ts/HlsSampleDecryptor.cpp b/media/libstagefright/mpeg2ts/HlsSampleDecryptor.cpp
new file mode 100644
index 0000000..e32f676
--- /dev/null
+++ b/media/libstagefright/mpeg2ts/HlsSampleDecryptor.cpp
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "HlsSampleDecryptor"
+
+#include "HlsSampleDecryptor.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/Utils.h>
+
+
+namespace android {
+
+HlsSampleDecryptor::HlsSampleDecryptor()
+ : mValidKeyInfo(false) {
+}
+
+HlsSampleDecryptor::HlsSampleDecryptor(const sp<AMessage> &sampleAesKeyItem)
+ : mValidKeyInfo(false) {
+
+ signalNewSampleAesKey(sampleAesKeyItem);
+}
+
+void HlsSampleDecryptor::signalNewSampleAesKey(const sp<AMessage> &sampleAesKeyItem) {
+
+ if (sampleAesKeyItem == NULL) {
+ mValidKeyInfo = false;
+ ALOGW("signalNewSampleAesKey: sampleAesKeyItem is NULL");
+ return;
+ }
+
+ sp<ABuffer> keyDataBuffer, initVecBuffer;
+ sampleAesKeyItem->findBuffer("keyData", &keyDataBuffer);
+ sampleAesKeyItem->findBuffer("initVec", &initVecBuffer);
+
+ if (keyDataBuffer != NULL && keyDataBuffer->size() == AES_BLOCK_SIZE &&
+ initVecBuffer != NULL && initVecBuffer->size() == AES_BLOCK_SIZE) {
+
+ ALOGV("signalNewSampleAesKey: Key: %s IV: %s",
+ aesBlockToStr(keyDataBuffer->data()).c_str(),
+ aesBlockToStr(initVecBuffer->data()).c_str());
+
+ uint8_t KeyData[AES_BLOCK_SIZE];
+ memcpy(KeyData, keyDataBuffer->data(), AES_BLOCK_SIZE);
+ memcpy(mAESInitVec, initVecBuffer->data(), AES_BLOCK_SIZE);
+
+ mValidKeyInfo = (AES_set_decrypt_key(KeyData, 8*AES_BLOCK_SIZE/*128*/, &mAesKey) == 0);
+ if (!mValidKeyInfo) {
+ ALOGE("signalNewSampleAesKey: failed to set AES decryption key.");
+ }
+
+ } else {
+ // Media scanner might try extract/parse the TS files without knowing the key.
+ // Otherwise, shouldn't get here (unless an invalid playlist has swaped SAMPLE-AES with
+ // NONE method while still sample-encrypted stream is parsed).
+
+ mValidKeyInfo = false;
+ ALOGE("signalNewSampleAesKey Can't decrypt; keyDataBuffer: %p(%zu) initVecBuffer: %p(%zu)",
+ keyDataBuffer.get(), (keyDataBuffer.get() == NULL)? -1 : keyDataBuffer->size(),
+ initVecBuffer.get(), (initVecBuffer.get() == NULL)? -1 : initVecBuffer->size());
+ }
+}
+
+size_t HlsSampleDecryptor::processNal(uint8_t *nalData, size_t nalSize) {
+
+ unsigned nalType = nalData[0] & 0x1f;
+ if (!mValidKeyInfo) {
+ ALOGV("processNal[%d]: (%p)/%zu Skipping due to invalid key", nalType, nalData, nalSize);
+ return nalSize;
+ }
+
+ bool isEncrypted = (nalSize > VIDEO_CLEAR_LEAD + AES_BLOCK_SIZE);
+ ALOGV("processNal[%d]: (%p)/%zu isEncrypted: %d", nalType, nalData, nalSize, isEncrypted);
+
+ if (isEncrypted) {
+ // Encrypted NALUs have extra start code emulation prevention that must be
+ // stripped out before we can decrypt it.
+ size_t newSize = unescapeStream(nalData, nalSize);
+
+ ALOGV("processNal:unescapeStream[%d]: %zu -> %zu", nalType, nalSize, newSize);
+ nalSize = newSize;
+
+ //Encrypted_nal_unit () {
+ // nal_unit_type_byte // 1 byte
+ // unencrypted_leader // 31 bytes
+ // while (bytes_remaining() > 0) {
+ // if (bytes_remaining() > 16) {
+ // encrypted_block // 16 bytes
+ // }
+ // unencrypted_block // MIN(144, bytes_remaining()) bytes
+ // }
+ //}
+
+ size_t offset = VIDEO_CLEAR_LEAD;
+ size_t remainingBytes = nalSize - VIDEO_CLEAR_LEAD;
+
+ // a copy of initVec as decryptBlock updates it
+ unsigned char AESInitVec[AES_BLOCK_SIZE];
+ memcpy(AESInitVec, mAESInitVec, AES_BLOCK_SIZE);
+
+ while (remainingBytes > 0) {
+ // encrypted_block: protected block uses 10% skip encryption
+ if (remainingBytes > AES_BLOCK_SIZE) {
+ uint8_t *encrypted = nalData + offset;
+ status_t ret = decryptBlock(encrypted, AES_BLOCK_SIZE, AESInitVec);
+ if (ret != OK) {
+ ALOGE("processNal failed with %d", ret);
+ return nalSize; // revisit this
+ }
+
+ offset += AES_BLOCK_SIZE;
+ remainingBytes -= AES_BLOCK_SIZE;
+ }
+
+ // unencrypted_block
+ size_t clearBytes = std::min(remainingBytes, (size_t)(9 * AES_BLOCK_SIZE));
+
+ offset += clearBytes;
+ remainingBytes -= clearBytes;
+ } // while
+
+ } else { // isEncrypted == false
+ ALOGV("processNal[%d]: Unencrypted NALU (%p)/%zu", nalType, nalData, nalSize);
+ }
+
+ return nalSize;
+}
+
+void HlsSampleDecryptor::processAAC(size_t adtsHdrSize, uint8_t *data, size_t size) {
+
+ if (!mValidKeyInfo) {
+ ALOGV("processAAC: (%p)/%zu Skipping due to invalid key", data, size);
+ return;
+ }
+
+ // ADTS header is included in the size
+ size_t offset = adtsHdrSize;
+ size_t remainingBytes = size - adtsHdrSize;
+
+ bool isEncrypted = (remainingBytes >= AUDIO_CLEAR_LEAD + AES_BLOCK_SIZE);
+ ALOGV("processAAC: header: %zu data: %p(%zu) isEncrypted: %d",
+ adtsHdrSize, data, size, isEncrypted);
+
+ //Encrypted_AAC_Frame () {
+ // ADTS_Header // 7 or 9 bytes
+ // unencrypted_leader // 16 bytes
+ // while (bytes_remaining() >= 16) {
+ // encrypted_block // 16 bytes
+ // }
+ // unencrypted_trailer // 0-15 bytes
+ //}
+
+ // with lead bytes
+ if (remainingBytes >= AUDIO_CLEAR_LEAD) {
+ offset += AUDIO_CLEAR_LEAD;
+ remainingBytes -= AUDIO_CLEAR_LEAD;
+
+ // encrypted_block
+ if (remainingBytes >= AES_BLOCK_SIZE) {
+
+ size_t encryptedBytes = (remainingBytes / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
+ unsigned char AESInitVec[AES_BLOCK_SIZE];
+ memcpy(AESInitVec, mAESInitVec, AES_BLOCK_SIZE);
+
+ // decrypting all blocks at once
+ uint8_t *encrypted = data + offset;
+ status_t ret = decryptBlock(encrypted, encryptedBytes, AESInitVec);
+ if (ret != OK) {
+ ALOGE("processAAC: decryptBlock failed with %d", ret);
+ return;
+ }
+
+ offset += encryptedBytes;
+ remainingBytes -= encryptedBytes;
+ } // encrypted
+
+ // unencrypted_trailer
+ size_t clearBytes = remainingBytes;
+ if (clearBytes > 0) {
+ CHECK(clearBytes < AES_BLOCK_SIZE);
+ }
+
+ } else { // without lead bytes
+ ALOGV("processAAC: Unencrypted frame (without lead bytes) size %zu = %zu (hdr) + %zu (rem)",
+ size, adtsHdrSize, remainingBytes);
+ }
+
+}
+
+void HlsSampleDecryptor::processAC3(uint8_t *data, size_t size) {
+
+ if (!mValidKeyInfo) {
+ ALOGV("processAC3: (%p)/%zu Skipping due to invalid key", data, size);
+ return;
+ }
+
+ bool isEncrypted = (size >= AUDIO_CLEAR_LEAD + AES_BLOCK_SIZE);
+ ALOGV("processAC3 %p(%zu) isEncrypted: %d", data, size, isEncrypted);
+
+ //Encrypted_AC3_Frame () {
+ // unencrypted_leader // 16 bytes
+ // while (bytes_remaining() >= 16) {
+ // encrypted_block // 16 bytes
+ // }
+ // unencrypted_trailer // 0-15 bytes
+ //}
+
+ if (size >= AUDIO_CLEAR_LEAD) {
+ // unencrypted_leader
+ size_t offset = AUDIO_CLEAR_LEAD;
+ size_t remainingBytes = size - AUDIO_CLEAR_LEAD;
+
+ if (remainingBytes >= AES_BLOCK_SIZE) {
+
+ size_t encryptedBytes = (remainingBytes / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
+
+ // encrypted_block
+ unsigned char AESInitVec[AES_BLOCK_SIZE];
+ memcpy(AESInitVec, mAESInitVec, AES_BLOCK_SIZE);
+
+ // decrypting all blocks at once
+ uint8_t *encrypted = data + offset;
+ status_t ret = decryptBlock(encrypted, encryptedBytes, AESInitVec);
+ if (ret != OK) {
+ ALOGE("processAC3: decryptBlock failed with %d", ret);
+ return;
+ }
+
+ offset += encryptedBytes;
+ remainingBytes -= encryptedBytes;
+ } // encrypted
+
+ // unencrypted_trailer
+ size_t clearBytes = remainingBytes;
+ if (clearBytes > 0) {
+ CHECK(clearBytes < AES_BLOCK_SIZE);
+ }
+
+ } else {
+ ALOGV("processAC3: Unencrypted frame (without lead bytes) size %zu", size);
+ }
+}
+
+// Unescapes data replacing occurrences of [0, 0, 3] with [0, 0] and returns the new size
+size_t HlsSampleDecryptor::unescapeStream(uint8_t *data, size_t limit) const {
+ Vector<size_t> scratchEscapePositions;
+ size_t position = 0;
+
+ while (position < limit) {
+ position = findNextUnescapeIndex(data, position, limit);
+ if (position < limit) {
+ scratchEscapePositions.add(position);
+ position += 3;
+ }
+ }
+
+ size_t scratchEscapeCount = scratchEscapePositions.size();
+ size_t escapedPosition = 0; // The position being read from.
+ size_t unescapedPosition = 0; // The position being written to.
+ for (size_t i = 0; i < scratchEscapeCount; i++) {
+ size_t nextEscapePosition = scratchEscapePositions[i];
+ //TODO: add 2 and get rid of the later = 0 assignments
+ size_t copyLength = nextEscapePosition - escapedPosition;
+ memmove(data+unescapedPosition, data+escapedPosition, copyLength);
+ unescapedPosition += copyLength;
+ data[unescapedPosition++] = 0;
+ data[unescapedPosition++] = 0;
+ escapedPosition += copyLength + 3;
+ }
+
+ size_t unescapedLength = limit - scratchEscapeCount;
+ size_t remainingLength = unescapedLength - unescapedPosition;
+ memmove(data+unescapedPosition, data+escapedPosition, remainingLength);
+
+ return unescapedLength;
+}
+
+size_t HlsSampleDecryptor::findNextUnescapeIndex(uint8_t *data, size_t offset, size_t limit) const {
+ for (size_t i = offset; i < limit - 2; i++) {
+ //TODO: speed
+ if (data[i] == 0x00 && data[i + 1] == 0x00 && data[i + 2] == 0x03) {
+ return i;
+ }
+ }
+ return limit;
+}
+
+status_t HlsSampleDecryptor::decryptBlock(uint8_t *buffer, size_t size,
+ uint8_t AESInitVec[AES_BLOCK_SIZE]) {
+ if (size == 0) {
+ return OK;
+ }
+
+ if ((size % AES_BLOCK_SIZE) != 0) {
+ ALOGE("decryptBlock: size (%zu) not a multiple of block size", size);
+ return ERROR_MALFORMED;
+ }
+
+ ALOGV("decryptBlock: %p (%zu)", buffer, size);
+
+ AES_cbc_encrypt(buffer, buffer, size, &mAesKey, AESInitVec, AES_DECRYPT);
+
+ return OK;
+}
+
+AString HlsSampleDecryptor::aesBlockToStr(uint8_t block[AES_BLOCK_SIZE]) {
+ AString result;
+
+ if (block == NULL) {
+ result = AString("null");
+ } else {
+ result = AStringPrintf("0x%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
+ block[0], block[1], block[2], block[3], block[4], block[5], block[6], block[7],
+ block[8], block[9], block[10], block[11], block[12], block[13], block[14], block[15]);
+ }
+
+ return result;
+}
+
+
+} // namespace android
diff --git a/media/libstagefright/mpeg2ts/HlsSampleDecryptor.h b/media/libstagefright/mpeg2ts/HlsSampleDecryptor.h
new file mode 100644
index 0000000..2c76620
--- /dev/null
+++ b/media/libstagefright/mpeg2ts/HlsSampleDecryptor.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SAMPLE_AES_PROCESSOR_H_
+
+#define SAMPLE_AES_PROCESSOR_H_
+
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/AString.h>
+
+#include <openssl/aes.h>
+
+#include <utils/Errors.h>
+#include <utils/List.h>
+#include <utils/RefBase.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+struct HlsSampleDecryptor : RefBase {
+
+ HlsSampleDecryptor();
+ explicit HlsSampleDecryptor(const sp<AMessage> &sampleAesKeyItem);
+
+ void signalNewSampleAesKey(const sp<AMessage> &sampleAesKeyItem);
+
+ size_t processNal(uint8_t *nalData, size_t nalSize);
+ void processAAC(size_t adtsHdrSize, uint8_t *data, size_t size);
+ void processAC3(uint8_t *data, size_t size);
+
+ static AString aesBlockToStr(uint8_t block[AES_BLOCK_SIZE]);
+
+private:
+ size_t unescapeStream(uint8_t *data, size_t limit) const;
+ size_t findNextUnescapeIndex(uint8_t *data, size_t offset, size_t limit) const;
+ status_t decryptBlock(uint8_t *buffer, size_t size, uint8_t AESInitVec[AES_BLOCK_SIZE]);
+
+ static const int VIDEO_CLEAR_LEAD = 32;
+ static const int AUDIO_CLEAR_LEAD = 16;
+
+ AES_KEY mAesKey;
+ uint8_t mAESInitVec[AES_BLOCK_SIZE];
+ bool mValidKeyInfo;
+
+ DISALLOW_EVIL_CONSTRUCTORS(HlsSampleDecryptor);
+};
+
+} // namespace android
+
+#endif // SAMPLE_AES_PROCESSOR_H_
diff --git a/media/libstagefright/omx/1.0/Omx.cpp b/media/libstagefright/omx/1.0/Omx.cpp
index 365ea5d..64b2c08 100644
--- a/media/libstagefright/omx/1.0/Omx.cpp
+++ b/media/libstagefright/omx/1.0/Omx.cpp
@@ -180,26 +180,22 @@
return OK;
}
- wp<IBase> observer;
{
Mutex::Autolock autoLock(mLock);
ssize_t observerIndex = mNode2Observer.indexOfKey(instance.get());
- if (observerIndex < 0) {
- return OK;
- }
- observer = mNode2Observer.valueAt(observerIndex);
- ssize_t nodeIndex = mLiveNodes.indexOfKey(observer);
- if (nodeIndex < 0) {
- return OK;
- }
- mNode2Observer.removeItemsAt(observerIndex);
- mLiveNodes.removeItemsAt(nodeIndex);
- }
-
- {
- sp<IBase> sObserver = observer.promote();
- if (sObserver != nullptr) {
- sObserver->unlinkToDeath(this);
+ if (observerIndex >= 0) {
+ wp<IBase> observer = mNode2Observer.valueAt(observerIndex);
+ ssize_t nodeIndex = mLiveNodes.indexOfKey(observer);
+ if (nodeIndex >= 0) {
+ mNode2Observer.removeItemsAt(observerIndex);
+ mLiveNodes.removeItemsAt(nodeIndex);
+ sp<IBase> sObserver = observer.promote();
+ if (sObserver != nullptr) {
+ sObserver->unlinkToDeath(this);
+ }
+ } else {
+ LOG(WARNING) << "Inconsistent observer record";
+ }
}
}
diff --git a/media/libstagefright/omx/1.0/OmxStore.cpp b/media/libstagefright/omx/1.0/OmxStore.cpp
new file mode 100644
index 0000000..0e37af9
--- /dev/null
+++ b/media/libstagefright/omx/1.0/OmxStore.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ios>
+#include <list>
+
+#include <android-base/logging.h>
+
+#include "Conversion.h"
+#include "OmxStore.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace omx {
+namespace V1_0 {
+namespace implementation {
+
+OmxStore::OmxStore() {
+}
+
+OmxStore::~OmxStore() {
+}
+
+Return<void> OmxStore::listServiceAttributes(listServiceAttributes_cb _hidl_cb) {
+ _hidl_cb(toStatus(NO_ERROR), hidl_vec<ServiceAttribute>());
+ return Void();
+}
+
+Return<void> OmxStore::getNodePrefix(getNodePrefix_cb _hidl_cb) {
+ _hidl_cb(hidl_string());
+ return Void();
+}
+
+Return<void> OmxStore::listRoles(listRoles_cb _hidl_cb) {
+ _hidl_cb(hidl_vec<RoleInfo>());
+ return Void();
+}
+
+Return<sp<IOmx>> OmxStore::getOmx(hidl_string const& omxName) {
+ return IOmx::tryGetService(omxName);
+}
+
+// Methods from ::android::hidl::base::V1_0::IBase follow.
+
+IOmxStore* HIDL_FETCH_IOmxStore(const char* /* name */) {
+ return new OmxStore();
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace omx
+} // namespace media
+} // namespace hardware
+} // namespace android
diff --git a/media/libstagefright/omx/1.0/OmxStore.h b/media/libstagefright/omx/1.0/OmxStore.h
new file mode 100644
index 0000000..f377f5a
--- /dev/null
+++ b/media/libstagefright/omx/1.0/OmxStore.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_MEDIA_OMX_V1_0_OMXSTORE_H
+#define ANDROID_HARDWARE_MEDIA_OMX_V1_0_OMXSTORE_H
+
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+#include <android/hardware/media/omx/1.0/IOmxStore.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace omx {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::media::omx::V1_0::IOmxStore;
+using ::android::hardware::media::omx::V1_0::IOmx;
+using ::android::hardware::media::omx::V1_0::Status;
+using ::android::hidl::base::V1_0::IBase;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+using ::android::wp;
+
+struct OmxStore : public IOmxStore {
+ OmxStore();
+ virtual ~OmxStore();
+
+ // Methods from IOmx
+ Return<void> listServiceAttributes(listServiceAttributes_cb) override;
+ Return<void> getNodePrefix(getNodePrefix_cb) override;
+ Return<void> listRoles(listRoles_cb) override;
+ Return<sp<IOmx>> getOmx(hidl_string const&) override;
+};
+
+extern "C" IOmxStore* HIDL_FETCH_IOmxStore(const char* name);
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace omx
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_MEDIA_OMX_V1_0_OMXSTORE_H
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/1.0/WGraphicBufferSource.cpp b/media/libstagefright/omx/1.0/WGraphicBufferSource.cpp
index 3c2face..e876306 100644
--- a/media/libstagefright/omx/1.0/WGraphicBufferSource.cpp
+++ b/media/libstagefright/omx/1.0/WGraphicBufferSource.cpp
@@ -192,8 +192,8 @@
}
Return<Status> TWGraphicBufferSource::setTimeLapseConfig(
- int64_t timePerFrameUs, int64_t timePerCaptureUs) {
- return toStatus(mBase->setTimeLapseConfig(timePerFrameUs, timePerCaptureUs));
+ double fps, double captureFps) {
+ return toStatus(mBase->setTimeLapseConfig(fps, captureFps));
}
Return<Status> TWGraphicBufferSource::setStartTimeUs(int64_t startTimeUs) {
@@ -204,6 +204,13 @@
return toStatus(mBase->setStopTimeUs(stopTimeUs));
}
+Return<void> TWGraphicBufferSource::getStopTimeOffsetUs(
+ getStopTimeOffsetUs_cb _hidl_cb) {
+ // TODO: Implement this when needed.
+ _hidl_cb(Status::OK, 0);
+ return Void();
+}
+
Return<Status> TWGraphicBufferSource::setColorAspects(
const ColorAspects& aspects) {
return toStatus(mBase->setColorAspects(toCompactColorAspects(aspects)));
diff --git a/media/libstagefright/omx/1.0/WGraphicBufferSource.h b/media/libstagefright/omx/1.0/WGraphicBufferSource.h
index 73b86b8..4549c97 100644
--- a/media/libstagefright/omx/1.0/WGraphicBufferSource.h
+++ b/media/libstagefright/omx/1.0/WGraphicBufferSource.h
@@ -78,10 +78,10 @@
Return<Status> setSuspend(bool suspend, int64_t timeUs) override;
Return<Status> setRepeatPreviousFrameDelayUs(int64_t repeatAfterUs) override;
Return<Status> setMaxFps(float maxFps) override;
- Return<Status> setTimeLapseConfig(
- int64_t timePerFrameUs, int64_t timePerCaptureUs) override;
+ Return<Status> setTimeLapseConfig(double fps, double captureFps) override;
Return<Status> setStartTimeUs(int64_t startTimeUs) override;
Return<Status> setStopTimeUs(int64_t stopTimeUs) override;
+ Return<void> getStopTimeOffsetUs(getStopTimeOffsetUs_cb _hidl_cb) override;
Return<Status> setColorAspects(const ColorAspects& aspects) override;
Return<Status> setTimeOffsetUs(int64_t timeOffsetUs) override;
Return<Status> signalEndOfInputStream() override;
diff --git a/media/libstagefright/omx/Android.mk b/media/libstagefright/omx/Android.mk
index 90333ef..29e2ccc 100644
--- a/media/libstagefright/omx/Android.mk
+++ b/media/libstagefright/omx/Android.mk
@@ -15,6 +15,7 @@
SoftVideoDecoderOMXComponent.cpp \
SoftVideoEncoderOMXComponent.cpp \
1.0/Omx.cpp \
+ 1.0/OmxStore.cpp \
1.0/WGraphicBufferProducer.cpp \
1.0/WProducerListener.cpp \
1.0/WGraphicBufferSource.cpp \
diff --git a/media/libstagefright/omx/BWGraphicBufferSource.cpp b/media/libstagefright/omx/BWGraphicBufferSource.cpp
index 4e0f6dd..f2a454f 100644
--- a/media/libstagefright/omx/BWGraphicBufferSource.cpp
+++ b/media/libstagefright/omx/BWGraphicBufferSource.cpp
@@ -145,9 +145,9 @@
}
::android::binder::Status BWGraphicBufferSource::setTimeLapseConfig(
- int64_t timePerFrameUs, int64_t timePerCaptureUs) {
+ double fps, double captureFps) {
return Status::fromStatusT(mBase->setTimeLapseConfig(
- timePerFrameUs, timePerCaptureUs));
+ fps, captureFps));
}
::android::binder::Status BWGraphicBufferSource::setStartTimeUs(
diff --git a/media/libstagefright/omx/BWGraphicBufferSource.h b/media/libstagefright/omx/BWGraphicBufferSource.h
index f1ce2af..43763c2 100644
--- a/media/libstagefright/omx/BWGraphicBufferSource.h
+++ b/media/libstagefright/omx/BWGraphicBufferSource.h
@@ -50,7 +50,7 @@
int64_t repeatAfterUs) override;
Status setMaxFps(float maxFps) override;
Status setTimeLapseConfig(
- int64_t timePerFrameUs, int64_t timePerCaptureUs) override;
+ double fps, double captureFps) override;
Status setStartTimeUs(int64_t startTimeUs) override;
Status setStopTimeUs(int64_t stopTimeUs) override;
Status setColorAspects(int32_t aspects) override;
diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp
index 5257b50..103a3e9 100644
--- a/media/libstagefright/omx/GraphicBufferSource.cpp
+++ b/media/libstagefright/omx/GraphicBufferSource.cpp
@@ -42,6 +42,7 @@
#include <functional>
#include <memory>
+#include <cmath>
namespace android {
@@ -270,8 +271,11 @@
mRepeatLastFrameGeneration(0),
mOutstandingFrameRepeatCount(0),
mFrameRepeatBlockedOnCodecBuffer(false),
- mTimePerCaptureUs(-1ll),
- mTimePerFrameUs(-1ll),
+ mFps(-1.0),
+ mCaptureFps(-1.0),
+ mBaseCaptureUs(-1ll),
+ mBaseFrameUs(-1ll),
+ mFrameCount(0),
mPrevCaptureUs(-1ll),
mPrevFrameUs(-1ll),
mInputBufferTimeOffsetUs(0ll) {
@@ -713,26 +717,31 @@
int64_t timeUs = bufferTimeNs / 1000;
timeUs += mInputBufferTimeOffsetUs;
- if (mTimePerCaptureUs > 0ll
- && (mTimePerCaptureUs > 2 * mTimePerFrameUs
- || mTimePerFrameUs > 2 * mTimePerCaptureUs)) {
+ if (mCaptureFps > 0.
+ && (mFps > 2 * mCaptureFps
+ || mCaptureFps > 2 * mFps)) {
// Time lapse or slow motion mode
if (mPrevCaptureUs < 0ll) {
// first capture
- mPrevCaptureUs = timeUs;
+ mPrevCaptureUs = mBaseCaptureUs = timeUs;
// adjust the first sample timestamp.
- mPrevFrameUs = (timeUs * mTimePerFrameUs) / mTimePerCaptureUs;
+ mPrevFrameUs = mBaseFrameUs =
+ std::llround((timeUs * mCaptureFps) / mFps);
+ mFrameCount = 0;
} else {
// snap to nearest capture point
- int64_t nFrames = (timeUs + mTimePerCaptureUs / 2 - mPrevCaptureUs)
- / mTimePerCaptureUs;
+ int64_t nFrames = std::llround(
+ (timeUs - mPrevCaptureUs) * mCaptureFps);
if (nFrames <= 0) {
// skip this frame as it's too close to previous capture
ALOGV("skipping frame, timeUs %lld", static_cast<long long>(timeUs));
return false;
}
- mPrevCaptureUs += mTimePerCaptureUs * nFrames;
- mPrevFrameUs += mTimePerFrameUs * nFrames;
+ mFrameCount += nFrames;
+ mPrevCaptureUs = mBaseCaptureUs + std::llround(
+ mFrameCount / mCaptureFps);
+ mPrevFrameUs = mBaseFrameUs + std::llround(
+ mFrameCount / mFps);
}
ALOGV("timeUs %lld, captureUs %lld, frameUs %lld",
@@ -757,7 +766,6 @@
status_t GraphicBufferSource::submitBuffer_l(const VideoBuffer &item) {
CHECK(!mFreeCodecBuffers.empty());
IOMX::buffer_id codecBufferId = *mFreeCodecBuffers.begin();
- mFreeCodecBuffers.erase(mFreeCodecBuffers.begin());
ALOGV("submitBuffer_l [slot=%d, bufferId=%d]", item.mBuffer->getSlot(), codecBufferId);
@@ -788,6 +796,8 @@
return err;
}
+ mFreeCodecBuffers.erase(mFreeCodecBuffers.begin());
+
ssize_t cbix = mSubmittedCodecBuffers.add(codecBufferId, buffer);
ALOGV("emptyGraphicBuffer succeeded, bufferId=%u@%zd bufhandle=%p",
codecBufferId, cbix, graphicBuffer->handle);
@@ -806,7 +816,6 @@
return;
}
IOMX::buffer_id codecBufferId = *mFreeCodecBuffers.begin();
- mFreeCodecBuffers.erase(mFreeCodecBuffers.begin());
// We reject any additional incoming graphic buffers. There is no acquired buffer used for EOS
status_t err = mOMXNode->emptyBuffer(
@@ -814,6 +823,7 @@
if (err != OK) {
ALOGW("emptyDirectBuffer EOS failed: 0x%x", err);
} else {
+ mFreeCodecBuffers.erase(mFreeCodecBuffers.begin());
ssize_t cbix = mSubmittedCodecBuffers.add(codecBufferId, nullptr);
ALOGV("submitEndOfInputStream_l: buffer submitted, bufferId=%u@%zd", codecBufferId, cbix);
mEndOfStreamSent = true;
@@ -888,7 +898,7 @@
++mNumAvailableUnacquiredBuffers;
// For BufferQueue we cannot acquire a buffer if we cannot immediately feed it to the codec
- // OR we are discarding this buffer (acquiring and immediately releasing it), which makes
+ // UNLESS we are discarding this buffer (acquiring and immediately releasing it), which makes
// this an ugly logic.
// NOTE: We could also rely on our debug counter but that is meant only as a debug counter.
if (!areWeDiscardingAvailableBuffers_l() && mFreeCodecBuffers.empty()) {
@@ -897,6 +907,7 @@
ALOGV("onFrameAvailable: cannot acquire buffer right now, do it later");
++mRepeatLastFrameGeneration; // cancel any pending frame repeat
+ return;
}
VideoBuffer buffer;
@@ -1054,10 +1065,13 @@
mOutstandingFrameRepeatCount = 0;
mLatestBuffer.mBuffer.reset();
mFrameRepeatBlockedOnCodecBuffer = false;
- mTimePerCaptureUs = -1ll;
- mTimePerFrameUs = -1ll;
+ mFps = -1.0;
+ mCaptureFps = -1.0;
+ mBaseCaptureUs = -1ll;
+ mBaseFrameUs = -1ll;
mPrevCaptureUs = -1ll;
mPrevFrameUs = -1ll;
+ mFrameCount = 0;
mInputBufferTimeOffsetUs = 0;
mStopTimeUs = -1;
mActionQueue.clear();
@@ -1202,18 +1216,18 @@
return OK;
}
-status_t GraphicBufferSource::setTimeLapseConfig(int64_t timePerFrameUs, int64_t timePerCaptureUs) {
- ALOGV("setTimeLapseConfig: timePerFrameUs=%lld, timePerCaptureUs=%lld",
- (long long)timePerFrameUs, (long long)timePerCaptureUs);
+status_t GraphicBufferSource::setTimeLapseConfig(double fps, double captureFps) {
+ ALOGV("setTimeLapseConfig: fps=%lg, captureFps=%lg",
+ fps, captureFps);
Mutex::Autolock autoLock(mMutex);
- if (mExecuting || timePerFrameUs <= 0ll || timePerCaptureUs <= 0ll) {
+ if (mExecuting || !(fps > 0) || !(captureFps > 0)) {
return INVALID_OPERATION;
}
- mTimePerFrameUs = timePerFrameUs;
- mTimePerCaptureUs = timePerCaptureUs;
+ mFps = fps;
+ mCaptureFps = captureFps;
return OK;
}
diff --git a/media/libstagefright/omx/GraphicBufferSource.h b/media/libstagefright/omx/GraphicBufferSource.h
index 635cfd6..3df1aa1 100644
--- a/media/libstagefright/omx/GraphicBufferSource.h
+++ b/media/libstagefright/omx/GraphicBufferSource.h
@@ -162,7 +162,7 @@
// Sets the time lapse (or slow motion) parameters.
// When set, the sample's timestamp will be modified to playback framerate,
// and capture timestamp will be modified to capture rate.
- status_t setTimeLapseConfig(int64_t timePerFrameUs, int64_t timePerCaptureUs);
+ status_t setTimeLapseConfig(double fps, double captureFps);
// Sets the start time us (in system time), samples before which should
// be dropped and not submitted to encoder
@@ -417,27 +417,39 @@
// Time lapse / slow motion configuration
// --------------------------------------
- // desired time interval between captured frames (capture interval) - value <= 0 if undefined
- int64_t mTimePerCaptureUs;
+ // desired frame rate for encoding - value <= 0 if undefined
+ double mFps;
- // desired time interval between encoded frames (media time interval) - value <= 0 if undefined
- int64_t mTimePerFrameUs;
+ // desired frame rate for capture - value <= 0 if undefined
+ double mCaptureFps;
- // Time lapse mode is enabled if capture interval is defined and it is more than twice the
- // media time interval (if defined). In this mode frames that come in between the capture
- // interval are dropped and the media timestamp is adjusted to have exactly the desired
- // media time interval.
+ // Time lapse mode is enabled if the capture frame rate is defined and it is
+ // smaller than half the encoding frame rate (if defined). In this mode,
+ // frames that come in between the capture interval (the reciprocal of the
+ // capture frame rate) are dropped and the encoding timestamp is adjusted to
+ // match the desired encoding frame rate.
//
- // Slow motion mode is enabled if both media and capture intervals are defined and the media
- // time interval is more than twice the capture interval. In this mode frames that come in
- // between the capture interval are dropped (though there isn't expected to be any, but there
- // could eventually be a frame drop if the actual capture interval is smaller than the
- // configured capture interval). The media timestamp is adjusted to have exactly the desired
- // media time interval.
+ // Slow motion mode is enabled if both encoding and capture frame rates are
+ // defined and the encoding frame rate is less than half the capture frame
+ // rate. In this mode, the source is expected to produce frames with an even
+ // timestamp interval (after rounding) with the configured capture fps. The
+ // first source timestamp is used as the source base time. Afterwards, the
+ // timestamp of each source frame is snapped to the nearest expected capture
+ // timestamp and scaled to match the configured encoding frame rate.
// These modes must be enabled before using this source.
- // adjusted capture timestamp for previous frame
+ // adjusted capture timestamp of the base frame
+ int64_t mBaseCaptureUs;
+
+ // adjusted encoding timestamp of the base frame
+ int64_t mBaseFrameUs;
+
+ // number of frames from the base time
+ int64_t mFrameCount;
+
+ // adjusted capture timestamp for previous frame (negative if there were
+ // none)
int64_t mPrevCaptureUs;
// adjusted media timestamp for previous frame (negative if there were none)
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/media/ndk/NdkImage.cpp b/media/ndk/NdkImage.cpp
index 95fdf36..6d28d1b 100644
--- a/media/ndk/NdkImage.cpp
+++ b/media/ndk/NdkImage.cpp
@@ -31,12 +31,10 @@
#define ALIGN(x, mask) ( ((x) + (mask) - 1) & ~((mask) - 1) )
-AImage::AImage(AImageReader* reader, int32_t format, uint64_t usage0, uint64_t usage1,
- BufferItem* buffer, int64_t timestamp,
- int32_t width, int32_t height, int32_t numPlanes) :
- mReader(reader), mFormat(format), mUsage0(usage0), mUsage1(usage1),
- mBuffer(buffer), mLockedBuffer(nullptr), mTimestamp(timestamp),
- mWidth(width), mHeight(height), mNumPlanes(numPlanes) {
+AImage::AImage(AImageReader* reader, int32_t format, uint64_t usage, BufferItem* buffer,
+ int64_t timestamp, int32_t width, int32_t height, int32_t numPlanes) :
+ mReader(reader), mFormat(format), mUsage(usage), mBuffer(buffer), mLockedBuffer(nullptr),
+ mTimestamp(timestamp), mWidth(width), mHeight(height), mNumPlanes(numPlanes) {
}
// Can only be called by free() with mLock hold
@@ -178,9 +176,9 @@
return AMEDIA_ERROR_INVALID_OBJECT;
}
- if ((mUsage0 & AHARDWAREBUFFER_USAGE0_CPU_READ_OFTEN) == 0) {
+ if ((mUsage & AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN) == 0) {
ALOGE("%s: AImage %p does not have any software read usage bits set, usage=%" PRIu64 "",
- __FUNCTION__, this, mUsage0);
+ __FUNCTION__, this, mUsage);
return AMEDIA_IMGREADER_CANNOT_LOCK_IMAGE;
}
@@ -191,13 +189,10 @@
auto lockedBuffer = std::make_unique<CpuConsumer::LockedBuffer>();
- uint64_t producerUsage;
- uint64_t consumerUsage;
- android_hardware_HardwareBuffer_convertToGrallocUsageBits(
- &producerUsage, &consumerUsage, mUsage0, mUsage1);
+ uint64_t grallocUsage = android_hardware_HardwareBuffer_convertToGrallocUsageBits(mUsage);
status_t ret =
- lockImageFromBuffer(mBuffer, consumerUsage, mBuffer->mFence->dup(), lockedBuffer.get());
+ lockImageFromBuffer(mBuffer, grallocUsage, mBuffer->mFence->dup(), lockedBuffer.get());
if (ret != OK) {
ALOGE("%s: AImage %p failed to lock, error=%d", __FUNCTION__, this, ret);
return AMEDIA_IMGREADER_CANNOT_LOCK_IMAGE;
diff --git a/media/ndk/NdkImagePriv.h b/media/ndk/NdkImagePriv.h
index 1fcb495..e9073d5 100644
--- a/media/ndk/NdkImagePriv.h
+++ b/media/ndk/NdkImagePriv.h
@@ -32,9 +32,8 @@
// TODO: this only supports ImageReader
struct AImage {
- AImage(AImageReader* reader, int32_t format, uint64_t usage0, uint64_t usage1,
- BufferItem* buffer, int64_t timestamp,
- int32_t width, int32_t height, int32_t numPlanes);
+ AImage(AImageReader* reader, int32_t format, uint64_t usage, BufferItem* buffer,
+ int64_t timestamp, int32_t width, int32_t height, int32_t numPlanes);
// free all resources while keeping object alive. Caller must obtain reader lock
void close() { close(-1); }
@@ -75,8 +74,7 @@
// When reader is close, AImage will only accept close API call
wp<AImageReader> mReader;
const int32_t mFormat;
- const uint64_t mUsage0; // AHARDWAREBUFFER_USAGE0* flags.
- const uint64_t mUsage1; // AHARDWAREBUFFER_USAGE1* flags.
+ const uint64_t mUsage; // AHARDWAREBUFFER_USAGE_* flags.
BufferItem* mBuffer;
std::unique_ptr<CpuConsumer::LockedBuffer> mLockedBuffer;
const int64_t mTimestamp;
diff --git a/media/ndk/NdkImageReader.cpp b/media/ndk/NdkImageReader.cpp
index c449611..5d1a20b 100644
--- a/media/ndk/NdkImageReader.cpp
+++ b/media/ndk/NdkImageReader.cpp
@@ -238,14 +238,12 @@
AImageReader::AImageReader(int32_t width,
int32_t height,
int32_t format,
- uint64_t usage0,
- uint64_t usage1,
+ uint64_t usage,
int32_t maxImages)
: mWidth(width),
mHeight(height),
mFormat(format),
- mUsage0(usage0),
- mUsage1(usage1),
+ mUsage(usage),
mMaxImages(maxImages),
mNumPlanes(getNumPlanesForFormat(format)),
mFrameListener(new FrameListener(this)),
@@ -256,20 +254,14 @@
PublicFormat publicFormat = static_cast<PublicFormat>(mFormat);
mHalFormat = android_view_Surface_mapPublicFormatToHalFormat(publicFormat);
mHalDataSpace = android_view_Surface_mapPublicFormatToHalDataspace(publicFormat);
-
- uint64_t producerUsage;
- uint64_t consumerUsage;
- android_hardware_HardwareBuffer_convertToGrallocUsageBits(
- &producerUsage, &consumerUsage, mUsage0, mUsage1);
- // Strip out producerUsage here.
- mHalUsage = android_convertGralloc1To0Usage(0, consumerUsage);
+ mHalUsage = android_hardware_HardwareBuffer_convertToGrallocUsageBits(mUsage);
sp<IGraphicBufferProducer> gbProducer;
sp<IGraphicBufferConsumer> gbConsumer;
BufferQueue::createBufferQueue(&gbProducer, &gbConsumer);
- String8 consumerName = String8::format("ImageReader-%dx%df%xu%" PRIu64 "u%" PRIu64 "m%d-%d-%d",
- mWidth, mHeight, mFormat, mUsage0, mUsage1, mMaxImages, getpid(),
+ String8 consumerName = String8::format("ImageReader-%dx%df%xu%" PRIu64 "m%d-%d-%d",
+ mWidth, mHeight, mFormat, mUsage, mMaxImages, getpid(),
createProcessUniqueId());
mBufferItemConsumer =
@@ -445,10 +437,10 @@
}
if (mHalFormat == HAL_PIXEL_FORMAT_BLOB) {
- *image = new AImage(this, mFormat, mUsage0, mUsage1, buffer, buffer->mTimestamp,
+ *image = new AImage(this, mFormat, mUsage, buffer, buffer->mTimestamp,
readerWidth, readerHeight, mNumPlanes);
} else {
- *image = new AImage(this, mFormat, mUsage0, mUsage1, buffer, buffer->mTimestamp,
+ *image = new AImage(this, mFormat, mUsage, buffer, buffer->mTimestamp,
bufferWidth, bufferHeight, mNumPlanes);
}
mAcquiredImages.push_back(*image);
@@ -587,12 +579,12 @@
/*out*/AImageReader** reader) {
ALOGV("%s", __FUNCTION__);
return AImageReader_newWithUsage(
- width, height, format, AHARDWAREBUFFER_USAGE0_CPU_READ_OFTEN, 0, maxImages, reader);
+ width, height, format, AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, maxImages, reader);
}
EXPORT
media_status_t AImageReader_newWithUsage(
- int32_t width, int32_t height, int32_t format, uint64_t usage0, uint64_t usage1,
+ int32_t width, int32_t height, int32_t format, uint64_t usage,
int32_t maxImages, /*out*/ AImageReader** reader) {
ALOGV("%s", __FUNCTION__);
@@ -626,7 +618,7 @@
}
AImageReader* tmpReader = new AImageReader(
- width, height, format, usage0, usage1, maxImages);
+ width, height, format, usage, maxImages);
if (tmpReader == nullptr) {
ALOGE("%s: AImageReader allocation failed", __FUNCTION__);
return AMEDIA_ERROR_UNKNOWN;
diff --git a/media/ndk/NdkImageReaderPriv.h b/media/ndk/NdkImageReaderPriv.h
index a233ec8..35af169 100644
--- a/media/ndk/NdkImageReaderPriv.h
+++ b/media/ndk/NdkImageReaderPriv.h
@@ -55,8 +55,7 @@
AImageReader(int32_t width,
int32_t height,
int32_t format,
- uint64_t usage0,
- uint64_t usage1,
+ uint64_t usage,
int32_t maxImages);
~AImageReader();
@@ -117,8 +116,7 @@
const int32_t mWidth;
const int32_t mHeight;
const int32_t mFormat;
- const uint64_t mUsage0; // AHARDWAREBUFFER_USAGE0* flags.
- const uint64_t mUsage1; // AHARDWAREBUFFER_USAGE1* flags.
+ const uint64_t mUsage; // AHARDWAREBUFFER_USAGE_* flags.
const int32_t mMaxImages;
// TODO(jwcai) Seems completely unused in AImageReader class.
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 42e9c6b..9f19dfd 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -2676,12 +2676,14 @@
// checkPlaybackThread_l() must be called with AudioFlinger::mLock held
AudioFlinger::VolumeInterface *AudioFlinger::getVolumeInterface_l(audio_io_handle_t output) const
{
- VolumeInterface *volumeInterface = (VolumeInterface *)mPlaybackThreads.valueFor(output).get();
+ VolumeInterface *volumeInterface = mPlaybackThreads.valueFor(output).get();
if (volumeInterface == nullptr) {
MmapThread *mmapThread = mMmapThreads.valueFor(output).get();
if (mmapThread != nullptr) {
if (mmapThread->isOutput()) {
- volumeInterface = (VolumeInterface *)mmapThread;
+ MmapPlaybackThread *mmapPlaybackThread =
+ static_cast<MmapPlaybackThread *>(mmapThread);
+ volumeInterface = mmapPlaybackThread;
}
}
}
@@ -2692,11 +2694,13 @@
{
Vector <VolumeInterface *> volumeInterfaces;
for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
- volumeInterfaces.add((VolumeInterface *)mPlaybackThreads.valueAt(i).get());
+ volumeInterfaces.add(mPlaybackThreads.valueAt(i).get());
}
for (size_t i = 0; i < mMmapThreads.size(); i++) {
if (mMmapThreads.valueAt(i)->isOutput()) {
- volumeInterfaces.add((VolumeInterface *)mMmapThreads.valueAt(i).get());
+ MmapPlaybackThread *mmapPlaybackThread =
+ static_cast<MmapPlaybackThread *>(mMmapThreads.valueAt(i).get());
+ volumeInterfaces.add(mmapPlaybackThread);
}
}
return volumeInterfaces;
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 3665875..a6857fe 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -2962,6 +2962,10 @@
// and then that string will be logged at the next convenient opportunity.
const char *logString = NULL;
+ // Estimated time for next buffer to be written to hal. This is used only on
+ // suspended mode (for now) to help schedule the wait time until next iteration.
+ nsecs_t timeLoopNextNs = 0;
+
checkSilentMode_l();
#if 0
int z = 0; // used in logFormat example
@@ -3327,6 +3331,29 @@
} else {
ATRACE_BEGIN("sleep");
Mutex::Autolock _l(mLock);
+ // suspended requires accurate metering of sleep time.
+ if (isSuspended()) {
+ // advance by expected sleepTime
+ timeLoopNextNs += microseconds((nsecs_t)mSleepTimeUs);
+ const nsecs_t nowNs = systemTime();
+
+ // compute expected next time vs current time.
+ // (negative deltas are treated as delays).
+ nsecs_t deltaNs = timeLoopNextNs - nowNs;
+ if (deltaNs < -kMaxNextBufferDelayNs) {
+ // Delays longer than the max allowed trigger a reset.
+ ALOGV("DelayNs: %lld, resetting timeLoopNextNs", (long long) deltaNs);
+ deltaNs = microseconds((nsecs_t)mSleepTimeUs);
+ timeLoopNextNs = nowNs + deltaNs;
+ } else if (deltaNs < 0) {
+ // Delays within the max delay allowed: zero the delta/sleepTime
+ // to help the system catch up in the next iteration(s)
+ ALOGV("DelayNs: %lld, catching-up", (long long) deltaNs);
+ deltaNs = 0;
+ }
+ // update sleep time (which is >= 0)
+ mSleepTimeUs = deltaNs / 1000;
+ }
if (!mSignalPending && mConfigEvents.isEmpty() && !exitPending()) {
mWaitWorkCV.waitRelative(mLock, microseconds((nsecs_t)mSleepTimeUs));
}
@@ -8296,22 +8323,24 @@
mEffectChains[0]->setVolume_l(&vol, &vol);
volume = (float)vol / (1 << 24);
}
-
- mOutput->stream->setVolume(volume, volume);
-
- sp<MmapStreamCallback> callback = mCallback.promote();
- if (callback != 0) {
- int channelCount;
- if (isOutput()) {
- channelCount = audio_channel_count_from_out_mask(mChannelMask);
+ // Try to use HW volume control and fall back to SW control if not implemented
+ if (mOutput->stream->setVolume(volume, volume) != NO_ERROR) {
+ sp<MmapStreamCallback> callback = mCallback.promote();
+ if (callback != 0) {
+ int channelCount;
+ if (isOutput()) {
+ channelCount = audio_channel_count_from_out_mask(mChannelMask);
+ } else {
+ channelCount = audio_channel_count_from_in_mask(mChannelMask);
+ }
+ Vector<float> values;
+ for (int i = 0; i < channelCount; i++) {
+ values.add(volume);
+ }
+ callback->onVolumeChanged(mChannelMask, values);
} else {
- channelCount = audio_channel_count_from_in_mask(mChannelMask);
+ ALOGW("Could not set MMAP stream volume: no volume callback!");
}
- Vector<float> values;
- for (int i = 0; i < channelCount; i++) {
- values.add(volume);
- }
- callback->onVolumeChanged(mChannelMask, values);
}
}
}
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 7469710..80b368e 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -623,6 +623,12 @@
// 14 tracks max per client allows for 2 misbehaving application leaving 4 available tracks.
static const uint32_t kMaxTracksPerUid = 14;
+ // Maximum delay (in nanoseconds) for upcoming buffers in suspend mode, otherwise
+ // if delay is greater, the estimated time for timeLoopNextNs is reset.
+ // This allows for catch-up to be done for small delays, while resetting the estimate
+ // for initial conditions or large delays.
+ static const nsecs_t kMaxNextBufferDelayNs = 100000000;
+
PlaybackThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
audio_io_handle_t id, audio_devices_t device, type_t type, bool systemReady);
virtual ~PlaybackThread();
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 5e07e3b..8fefb36 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -489,7 +489,7 @@
/*static*/ void AudioFlinger::PlaybackThread::Track::appendDumpHeader(String8& result)
{
result.append(" Name Active Client Type Fmt Chn mask Session fCount S F SRate "
- "L dB R dB Server Main buf Aux buf Flags UndFrmCnt Flushed\n");
+ "L dB R dB VS dB Server Main buf Aux buf Flags UndFrmCnt Flushed\n");
}
void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size, bool active)
@@ -555,8 +555,11 @@
nowInUnderrun = '?';
break;
}
- snprintf(&buffer[8], size-8, " %6s %6u %4u %08X %08X %7u %6zu %1c %1d %5u %5.2g %5.2g "
- "%08X %08zX %08zX 0x%03X %9u%c %7u\n",
+
+ std::pair<float /* volume */, bool /* active */> vsVolume = mVolumeHandler->getLastVolume();
+ snprintf(&buffer[8], size - 8, " %6s %6u %4u %08X %08X %7u %6zu %1c %1d %5u "
+ "%5.2g %5.2g %5.2g%c "
+ "%08X %08zX %08zX 0x%03X %9u%c %7u\n",
active ? "yes" : "no",
(mClient == 0) ? getpid_cached : mClient->pid(),
mStreamType,
@@ -569,6 +572,8 @@
mAudioTrackServerProxy->getSampleRate(),
20.0 * log10(float_from_gain(gain_minifloat_unpack_left(vlr))),
20.0 * log10(float_from_gain(gain_minifloat_unpack_right(vlr))),
+ 20.0 * log10(vsVolume.first), // VolumeShaper(s) total volume
+ vsVolume.second ? 'A' : ' ', // if any VolumeShapers active
mCblk->mServer,
(size_t)mMainBuffer, // use %zX as %p appends 0x
(size_t)mAuxBuffer, // use %zX as %p appends 0x
@@ -592,7 +597,9 @@
status_t status = mServerProxy->obtainBuffer(&buf);
buffer->frameCount = buf.mFrameCount;
buffer->raw = buf.mRaw;
- if (buf.mFrameCount == 0) {
+ if (buf.mFrameCount == 0 && !isStopping() && !isStopped() && !isPaused()) {
+ ALOGV("underrun, framesReady(%zu) < framesDesired(%zd), state: %d",
+ buf.mFrameCount, desiredFrames, mState);
mAudioTrackServerProxy->tallyUnderrunFrames(desiredFrames);
} else {
mAudioTrackServerProxy->tallyUnderrunFrames(0);
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index e3a23f9..aaa6134 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1007,11 +1007,14 @@
if (offloadInfo != NULL) {
config.offload_info = *offloadInfo;
}
+ DeviceVector outputDevices = mAvailableOutputDevices.getDevicesFromType(device);
+ String8 address = outputDevices.size() > 0 ? outputDevices.itemAt(0)->mAddress
+ : String8("");
status = mpClientInterface->openOutput(profile->getModuleHandle(),
&output,
&config,
&outputDesc->mDevice,
- String8(""),
+ address,
&outputDesc->mLatency,
outputDesc->mFlags);
@@ -1699,6 +1702,12 @@
config.channel_mask = profileChannelMask;
config.format = profileFormat;
+ if (address == "") {
+ DeviceVector inputDevices = mAvailableInputDevices.getDevicesFromType(device);
+ // the inputs vector must be of size 1, but we don't want to crash here
+ address = inputDevices.size() > 0 ? inputDevices.itemAt(0)->mAddress : String8("");
+ }
+
status_t status = mpClientInterface->openInput(profile->getModuleHandle(),
&input,
&config,
diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk
index 2d78f99..ef2f3b3 100644
--- a/services/camera/libcameraservice/Android.mk
+++ b/services/camera/libcameraservice/Android.mk
@@ -27,7 +27,6 @@
CameraFlashlight.cpp \
common/Camera2ClientBase.cpp \
common/CameraDeviceBase.cpp \
- common/CameraModule.cpp \
common/CameraProviderManager.cpp \
common/FrameProcessorBase.cpp \
api1/CameraClient.cpp \
diff --git a/services/camera/libcameraservice/CameraFlashlight.cpp b/services/camera/libcameraservice/CameraFlashlight.cpp
index 4537ae6..836972a 100644
--- a/services/camera/libcameraservice/CameraFlashlight.cpp
+++ b/services/camera/libcameraservice/CameraFlashlight.cpp
@@ -36,16 +36,9 @@
// CameraFlashlight implementation begins
// used by camera service to control flashflight.
/////////////////////////////////////////////////////////////////////
-CameraFlashlight::CameraFlashlight(CameraModule* cameraModule,
- camera_module_callbacks_t* callbacks) :
- mCameraModule(cameraModule),
- mCallbacks(callbacks),
- mFlashlightMapInitialized(false) {
-}
CameraFlashlight::CameraFlashlight(sp<CameraProviderManager> providerManager,
camera_module_callbacks_t* callbacks) :
- mCameraModule(nullptr),
mProviderManager(providerManager),
mCallbacks(callbacks),
mFlashlightMapInitialized(false) {
@@ -61,52 +54,12 @@
return INVALID_OPERATION;
}
- status_t res = OK;
-
- if (mCameraModule == nullptr) {
- if (mProviderManager->supportSetTorchMode(cameraId.string())) {
- mFlashControl = new ProviderFlashControl(mProviderManager);
- } else {
- // Only HAL1 devices do not support setTorchMode
- mFlashControl =
- new CameraHardwareInterfaceFlashControl(mProviderManager, *mCallbacks);
- }
- } else if (mCameraModule->getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_4) {
- mFlashControl = new ModuleFlashControl(*mCameraModule);
- if (mFlashControl == NULL) {
- ALOGV("%s: cannot create flash control for module api v2.4+",
- __FUNCTION__);
- return NO_MEMORY;
- }
+ if (mProviderManager->supportSetTorchMode(cameraId.string())) {
+ mFlashControl = new ProviderFlashControl(mProviderManager);
} else {
- uint32_t deviceVersion = CAMERA_DEVICE_API_VERSION_1_0;
-
- if (mCameraModule->getModuleApiVersion() >=
- CAMERA_MODULE_API_VERSION_2_0) {
- camera_info info;
- res = mCameraModule->getCameraInfo(
- atoi(cameraId.string()), &info);
- if (res) {
- ALOGE("%s: failed to get camera info for camera %s",
- __FUNCTION__, cameraId.string());
- return res;
- }
- deviceVersion = info.device_version;
- }
-
- if (deviceVersion >= CAMERA_DEVICE_API_VERSION_3_0) {
- CameraDeviceClientFlashControl *flashControl =
- new CameraDeviceClientFlashControl(*mCameraModule,
- *mCallbacks);
- if (!flashControl) {
- return NO_MEMORY;
- }
-
- mFlashControl = flashControl;
- } else {
- mFlashControl =
- new CameraHardwareInterfaceFlashControl(mCameraModule, *mCallbacks);
- }
+ // Only HAL1 devices do not support setTorchMode
+ mFlashControl =
+ new CameraHardwareInterfaceFlashControl(mProviderManager, *mCallbacks);
}
return OK;
@@ -170,11 +123,7 @@
}
int CameraFlashlight::getNumberOfCameras() {
- if (mCameraModule) {
- return mCameraModule->getNumberOfCameras();
- } else {
- return mProviderManager->getStandardCameraCount();
- }
+ return mProviderManager->getAPI1CompatibleCameraCount();
}
status_t CameraFlashlight::findFlashUnits() {
@@ -184,16 +133,10 @@
std::vector<String8> cameraIds;
int numberOfCameras = getNumberOfCameras();
cameraIds.resize(numberOfCameras);
- if (mCameraModule) {
- for (size_t i = 0; i < cameraIds.size(); i++) {
- cameraIds[i] = String8::format("%zu", i);
- }
- } else {
- // No module, must be provider
- std::vector<std::string> ids = mProviderManager->getStandardCameraDeviceIds();
- for (size_t i = 0; i < cameraIds.size(); i++) {
- cameraIds[i] = String8(ids[i].c_str());
- }
+ // No module, must be provider
+ std::vector<std::string> ids = mProviderManager->getAPI1CompatibleCameraDeviceIds();
+ for (size_t i = 0; i < cameraIds.size(); i++) {
+ cameraIds[i] = String8(ids[i].c_str());
}
mHasFlashlightMap.clear();
@@ -251,9 +194,7 @@
bool CameraFlashlight::isBackwardCompatibleMode(const String8& cameraId) {
bool backwardCompatibleMode = false;
- if (mCameraModule && mCameraModule->getModuleApiVersion() < CAMERA_MODULE_API_VERSION_2_4) {
- backwardCompatibleMode = true;
- } else if (mProviderManager != nullptr &&
+ if (mProviderManager != nullptr &&
!mProviderManager->supportSetTorchMode(cameraId.string())) {
backwardCompatibleMode = true;
}
@@ -368,366 +309,13 @@
// ProviderFlashControl implementation ends
/////////////////////////////////////////////////////////////////////
-// ModuleFlashControl implementation begins
-// Flash control for camera module v2.4 and above.
-/////////////////////////////////////////////////////////////////////
-ModuleFlashControl::ModuleFlashControl(CameraModule& cameraModule) :
- mCameraModule(&cameraModule) {
-}
-
-ModuleFlashControl::~ModuleFlashControl() {
-}
-
-status_t ModuleFlashControl::hasFlashUnit(const String8& cameraId, bool *hasFlash) {
- if (!hasFlash) {
- return BAD_VALUE;
- }
-
- *hasFlash = false;
- Mutex::Autolock l(mLock);
-
- camera_info info;
- status_t res = mCameraModule->getCameraInfo(atoi(cameraId.string()),
- &info);
- if (res != 0) {
- return res;
- }
-
- CameraMetadata metadata;
- metadata = info.static_camera_characteristics;
- camera_metadata_entry flashAvailable =
- metadata.find(ANDROID_FLASH_INFO_AVAILABLE);
- if (flashAvailable.count == 1 && flashAvailable.data.u8[0] == 1) {
- *hasFlash = true;
- }
-
- return OK;
-}
-
-status_t ModuleFlashControl::setTorchMode(const String8& cameraId, bool enabled) {
- ALOGV("%s: set camera %s torch mode to %d", __FUNCTION__,
- cameraId.string(), enabled);
-
- Mutex::Autolock l(mLock);
- return mCameraModule->setTorchMode(cameraId.string(), enabled);
-}
-// ModuleFlashControl implementation ends
-
-/////////////////////////////////////////////////////////////////////
-// CameraDeviceClientFlashControl implementation begins
-// Flash control for camera module <= v2.3 and camera HAL v2-v3
-/////////////////////////////////////////////////////////////////////
-CameraDeviceClientFlashControl::CameraDeviceClientFlashControl(
- CameraModule& cameraModule,
- const camera_module_callbacks_t& callbacks) :
- mCameraModule(&cameraModule),
- mCallbacks(&callbacks),
- mTorchEnabled(false),
- mMetadata(NULL),
- mStreaming(false) {
-}
-
-CameraDeviceClientFlashControl::~CameraDeviceClientFlashControl() {
- disconnectCameraDevice();
- if (mMetadata) {
- delete mMetadata;
- }
-
- mSurface.clear();
- mSurfaceTexture.clear();
- mProducer.clear();
- mConsumer.clear();
-
- if (mTorchEnabled) {
- if (mCallbacks) {
- ALOGV("%s: notify the framework that torch was turned off",
- __FUNCTION__);
- mCallbacks->torch_mode_status_change(mCallbacks,
- mCameraId.string(), TORCH_MODE_STATUS_AVAILABLE_OFF);
- }
- }
-}
-
-status_t CameraDeviceClientFlashControl::initializeSurface(
- sp<CameraDeviceBase> &device, int32_t width, int32_t height) {
- status_t res;
- BufferQueue::createBufferQueue(&mProducer, &mConsumer);
-
- mSurfaceTexture = new GLConsumer(mConsumer, 0, GLConsumer::TEXTURE_EXTERNAL,
- true, true);
- if (mSurfaceTexture == NULL) {
- return NO_MEMORY;
- }
-
- int32_t format = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
- res = mSurfaceTexture->setDefaultBufferSize(width, height);
- if (res) {
- return res;
- }
- res = mSurfaceTexture->setDefaultBufferFormat(format);
- if (res) {
- return res;
- }
-
- mSurface = new Surface(mProducer, /*useAsync*/ true);
- if (mSurface == NULL) {
- return NO_MEMORY;
- }
- res = device->createStream(mSurface, width, height, format,
- HAL_DATASPACE_UNKNOWN, CAMERA3_STREAM_ROTATION_0, &mStreamId);
- if (res) {
- return res;
- }
-
- res = device->configureStreams();
- if (res) {
- return res;
- }
-
- return res;
-}
-
-status_t CameraDeviceClientFlashControl::getSmallestSurfaceSize(
- const camera_info& info, int32_t *width, int32_t *height) {
- if (!width || !height) {
- return BAD_VALUE;
- }
-
- int32_t w = INT32_MAX;
- int32_t h = 1;
-
- CameraMetadata metadata;
- metadata = info.static_camera_characteristics;
- camera_metadata_entry streamConfigs =
- metadata.find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
- for (size_t i = 0; i < streamConfigs.count; i += 4) {
- int32_t fmt = streamConfigs.data.i32[i];
- if (fmt == ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED) {
- int32_t ww = streamConfigs.data.i32[i + 1];
- int32_t hh = streamConfigs.data.i32[i + 2];
-
- if (w * h > ww * hh) {
- w = ww;
- h = hh;
- }
- }
- }
-
- // if stream configuration is not found, try available processed sizes.
- if (streamConfigs.count == 0) {
- camera_metadata_entry availableProcessedSizes =
- metadata.find(ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES);
- for (size_t i = 0; i < availableProcessedSizes.count; i += 2) {
- int32_t ww = availableProcessedSizes.data.i32[i];
- int32_t hh = availableProcessedSizes.data.i32[i + 1];
- if (w * h > ww * hh) {
- w = ww;
- h = hh;
- }
- }
- }
-
- if (w == INT32_MAX) {
- return NAME_NOT_FOUND;
- }
-
- *width = w;
- *height = h;
-
- return OK;
-}
-
-status_t CameraDeviceClientFlashControl::connectCameraDevice(
- const String8& cameraId) {
- camera_info info;
- status_t res = mCameraModule->getCameraInfo(atoi(cameraId.string()), &info);
- if (res != 0) {
- ALOGE("%s: failed to get camera info for camera %s", __FUNCTION__,
- cameraId.string());
- return res;
- }
-
- sp<CameraDeviceBase> device =
- new Camera3Device(cameraId);
- if (device == NULL) {
- return NO_MEMORY;
- }
-
- res = device->initialize(mCameraModule);
- if (res) {
- return res;
- }
-
- int32_t width, height;
- res = getSmallestSurfaceSize(info, &width, &height);
- if (res) {
- return res;
- }
- res = initializeSurface(device, width, height);
- if (res) {
- return res;
- }
-
- mCameraId = cameraId;
- mStreaming = (info.device_version <= CAMERA_DEVICE_API_VERSION_3_1);
- mDevice = device;
-
- return OK;
-}
-
-status_t CameraDeviceClientFlashControl::disconnectCameraDevice() {
- if (mDevice != NULL) {
- mDevice->disconnect();
- mDevice.clear();
- }
-
- return OK;
-}
-
-
-
-status_t CameraDeviceClientFlashControl::hasFlashUnit(const String8& cameraId,
- bool *hasFlash) {
- ALOGV("%s: checking if camera %s has a flash unit", __FUNCTION__,
- cameraId.string());
-
- Mutex::Autolock l(mLock);
- return hasFlashUnitLocked(cameraId, hasFlash);
-
-}
-
-status_t CameraDeviceClientFlashControl::hasFlashUnitLocked(
- const String8& cameraId, bool *hasFlash) {
- if (!hasFlash) {
- return BAD_VALUE;
- }
-
- camera_info info;
- status_t res = mCameraModule->getCameraInfo(
- atoi(cameraId.string()), &info);
- if (res != 0) {
- ALOGE("%s: failed to get camera info for camera %s", __FUNCTION__,
- cameraId.string());
- return res;
- }
-
- CameraMetadata metadata;
- metadata = info.static_camera_characteristics;
- camera_metadata_entry flashAvailable =
- metadata.find(ANDROID_FLASH_INFO_AVAILABLE);
- if (flashAvailable.count == 1 && flashAvailable.data.u8[0] == 1) {
- *hasFlash = true;
- }
-
- return OK;
-}
-
-status_t CameraDeviceClientFlashControl::submitTorchEnabledRequest() {
- status_t res;
-
- if (mMetadata == NULL) {
- mMetadata = new CameraMetadata();
- if (mMetadata == NULL) {
- return NO_MEMORY;
- }
- res = mDevice->createDefaultRequest(
- CAMERA3_TEMPLATE_PREVIEW, mMetadata);
- if (res) {
- return res;
- }
- }
-
- uint8_t torchOn = ANDROID_FLASH_MODE_TORCH;
- mMetadata->update(ANDROID_FLASH_MODE, &torchOn, 1);
- mMetadata->update(ANDROID_REQUEST_OUTPUT_STREAMS, &mStreamId, 1);
-
- uint8_t aeMode = ANDROID_CONTROL_AE_MODE_ON;
- mMetadata->update(ANDROID_CONTROL_AE_MODE, &aeMode, 1);
-
- int32_t requestId = 0;
- mMetadata->update(ANDROID_REQUEST_ID, &requestId, 1);
-
- if (mStreaming) {
- res = mDevice->setStreamingRequest(*mMetadata);
- } else {
- res = mDevice->capture(*mMetadata);
- }
- return res;
-}
-
-
-
-
-status_t CameraDeviceClientFlashControl::setTorchMode(
- const String8& cameraId, bool enabled) {
- bool hasFlash = false;
-
- Mutex::Autolock l(mLock);
- status_t res = hasFlashUnitLocked(cameraId, &hasFlash);
-
- // pre-check
- if (enabled) {
- // invalid camera?
- if (res) {
- return -EINVAL;
- }
- // no flash unit?
- if (!hasFlash) {
- return -ENOSYS;
- }
- // already opened for a different device?
- if (mDevice != NULL && cameraId != mCameraId) {
- return BAD_INDEX;
- }
- } else if (mDevice == NULL || cameraId != mCameraId) {
- // disabling the torch mode of an un-opened or different device.
- return OK;
- } else {
- // disabling the torch mode of currently opened device
- disconnectCameraDevice();
- mTorchEnabled = false;
- mCallbacks->torch_mode_status_change(mCallbacks,
- cameraId.string(), TORCH_MODE_STATUS_AVAILABLE_OFF);
- return OK;
- }
-
- if (mDevice == NULL) {
- res = connectCameraDevice(cameraId);
- if (res) {
- return res;
- }
- }
-
- res = submitTorchEnabledRequest();
- if (res) {
- return res;
- }
-
- mTorchEnabled = true;
- mCallbacks->torch_mode_status_change(mCallbacks,
- cameraId.string(), TORCH_MODE_STATUS_AVAILABLE_ON);
- return OK;
-}
-// CameraDeviceClientFlashControl implementation ends
-
-
-/////////////////////////////////////////////////////////////////////
// CameraHardwareInterfaceFlashControl implementation begins
// Flash control for camera module <= v2.3 and camera HAL v1
/////////////////////////////////////////////////////////////////////
-CameraHardwareInterfaceFlashControl::CameraHardwareInterfaceFlashControl(
- CameraModule* cameraModule,
- const camera_module_callbacks_t& callbacks) :
- mCameraModule(cameraModule),
- mProviderManager(nullptr),
- mCallbacks(&callbacks),
- mTorchEnabled(false) {
-}
CameraHardwareInterfaceFlashControl::CameraHardwareInterfaceFlashControl(
sp<CameraProviderManager> manager,
const camera_module_callbacks_t& callbacks) :
- mCameraModule(nullptr),
mProviderManager(manager),
mCallbacks(&callbacks),
mTorchEnabled(false) {
@@ -931,12 +519,7 @@
sp<CameraHardwareInterface> device =
new CameraHardwareInterface(cameraId.string());
- status_t res;
- if (mCameraModule != nullptr) {
- res = device->initialize(mCameraModule);
- } else {
- res = device->initialize(mProviderManager);
- }
+ status_t res = device->initialize(mProviderManager);
if (res) {
ALOGE("%s: initializing camera %s failed", __FUNCTION__,
cameraId.string());
diff --git a/services/camera/libcameraservice/CameraFlashlight.h b/services/camera/libcameraservice/CameraFlashlight.h
index 98f269a..c86ee85 100644
--- a/services/camera/libcameraservice/CameraFlashlight.h
+++ b/services/camera/libcameraservice/CameraFlashlight.h
@@ -23,7 +23,6 @@
#include <utils/KeyedVector.h>
#include <utils/SortedVector.h>
#include "common/CameraProviderManager.h"
-#include "common/CameraModule.h"
#include "common/CameraDeviceBase.h"
#include "device1/CameraHardwareInterface.h"
@@ -55,8 +54,6 @@
*/
class CameraFlashlight : public virtual VirtualLightRefBase {
public:
- CameraFlashlight(CameraModule* cameraModule,
- camera_module_callbacks_t* callbacks);
CameraFlashlight(sp<CameraProviderManager> providerManager,
camera_module_callbacks_t* callbacks);
virtual ~CameraFlashlight();
@@ -100,7 +97,6 @@
sp<FlashControlBase> mFlashControl;
- CameraModule *mCameraModule;
sp<CameraProviderManager> mProviderManager;
const camera_module_callbacks_t *mCallbacks;
@@ -132,86 +128,11 @@
};
/**
- * Flash control for camera module v2.4 and above.
- */
-class ModuleFlashControl : public FlashControlBase {
- public:
- ModuleFlashControl(CameraModule& cameraModule);
- virtual ~ModuleFlashControl();
-
- // FlashControlBase
- status_t hasFlashUnit(const String8& cameraId, bool *hasFlash);
- status_t setTorchMode(const String8& cameraId, bool enabled);
-
- private:
- CameraModule *mCameraModule;
-
- Mutex mLock;
-};
-
-/**
- * Flash control for camera module <= v2.3 and camera HAL v2-v3
- */
-class CameraDeviceClientFlashControl : public FlashControlBase {
- public:
- CameraDeviceClientFlashControl(CameraModule& cameraModule,
- const camera_module_callbacks_t& callbacks);
- virtual ~CameraDeviceClientFlashControl();
-
- // FlashControlBase
- status_t setTorchMode(const String8& cameraId, bool enabled);
- status_t hasFlashUnit(const String8& cameraId, bool *hasFlash);
-
- private:
- // connect to a camera device
- status_t connectCameraDevice(const String8& cameraId);
- // disconnect and free mDevice
- status_t disconnectCameraDevice();
-
- // initialize a surface
- status_t initializeSurface(sp<CameraDeviceBase>& device, int32_t width,
- int32_t height);
-
- // submit a request to enable the torch mode
- status_t submitTorchEnabledRequest();
-
- // get the smallest surface size of IMPLEMENTATION_DEFINED
- status_t getSmallestSurfaceSize(const camera_info& info, int32_t *width,
- int32_t *height);
-
- // protected by mLock
- status_t hasFlashUnitLocked(const String8& cameraId, bool *hasFlash);
-
- CameraModule *mCameraModule;
- const camera_module_callbacks_t *mCallbacks;
- String8 mCameraId;
- bool mTorchEnabled;
- CameraMetadata *mMetadata;
- // WORKAROUND: will be set to true for HAL v2 devices where
- // setStreamingRequest() needs to be call for torch mode settings to
- // take effect.
- bool mStreaming;
-
- sp<CameraDeviceBase> mDevice;
-
- sp<IGraphicBufferProducer> mProducer;
- sp<IGraphicBufferConsumer> mConsumer;
- sp<GLConsumer> mSurfaceTexture;
- sp<Surface> mSurface;
- int32_t mStreamId;
-
- Mutex mLock;
-};
-
-/**
* Flash control for camera module <= v2.3 and camera HAL v1
*/
class CameraHardwareInterfaceFlashControl : public FlashControlBase {
public:
CameraHardwareInterfaceFlashControl(
- CameraModule* cameraModule,
- const camera_module_callbacks_t& callbacks);
- CameraHardwareInterfaceFlashControl(
sp<CameraProviderManager> manager,
const camera_module_callbacks_t& callbacks);
virtual ~CameraHardwareInterfaceFlashControl();
@@ -244,7 +165,6 @@
// function, keepDeviceOpen is ignored.
status_t hasFlashUnitLocked(const String8& cameraId, bool *hasFlash, bool keepDeviceOpen);
- CameraModule *mCameraModule;
sp<CameraProviderManager> mProviderManager;
const camera_module_callbacks_t *mCallbacks;
sp<CameraHardwareInterface> mDevice;
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 39351e7..0031441 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -167,7 +167,7 @@
CameraService::CameraService() :
mEventLog(DEFAULT_EVENT_LOG_LENGTH),
mNumberOfCameras(0), mNumberOfNormalCameras(0),
- mSoundRef(0), mInitialized(false), mModule(nullptr) {
+ mSoundRef(0), mInitialized(false) {
ALOGI("CameraService started (pid=%d)", getpid());
this->camera_device_status_change = android::camera_device_status_change;
@@ -189,13 +189,7 @@
status_t res = INVALID_OPERATION;
- bool disableTreble = property_get_bool("camera.disable_treble", false);
- if (disableTreble) {
- ALOGI("Treble disabled - using legacy path");
- res = loadLegacyHalModule();
- } else {
- res = enumerateProviders();
- }
+ res = enumerateProviders();
if (res == OK) {
mInitialized = true;
}
@@ -203,108 +197,6 @@
CameraService::pingCameraServiceProxy();
}
-status_t CameraService::loadLegacyHalModule() {
- camera_module_t *rawModule;
- int err = hw_get_module(CAMERA_HARDWARE_MODULE_ID,
- (const hw_module_t **)&rawModule);
- if (err < 0) {
- ALOGE("Could not load camera HAL module: %d (%s)", err, strerror(-err));
- logServiceError("Could not load camera HAL module", err);
- return INVALID_OPERATION;
- }
-
- mModule = new CameraModule(rawModule);
- err = mModule->init();
- if (err != OK) {
- ALOGE("Could not initialize camera HAL module: %d (%s)", err,
- strerror(-err));
- logServiceError("Could not initialize camera HAL module", err);
-
- delete mModule;
- mModule = nullptr;
- return INVALID_OPERATION;
- }
- ALOGI("Loaded \"%s\" camera module", mModule->getModuleName());
-
- mNumberOfCameras = mModule->getNumberOfCameras();
- mNumberOfNormalCameras = mNumberOfCameras;
-
- // Setup vendor tags before we call get_camera_info the first time
- // because HAL might need to setup static vendor keys in get_camera_info
- VendorTagDescriptor::clearGlobalVendorTagDescriptor();
- if (mModule->getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_2) {
- setUpVendorTags();
- }
-
- mFlashlight = new CameraFlashlight(mModule, this);
- status_t res = mFlashlight->findFlashUnits();
- if (res) {
- // impossible because we haven't open any camera devices.
- ALOGE("Failed to find flash units.");
- }
-
- int latestStrangeCameraId = INT_MAX;
- for (int i = 0; i < mNumberOfCameras; i++) {
- String8 cameraId = String8::format("%d", i);
-
- // Get camera info
-
- struct camera_info info;
- bool haveInfo = true;
- status_t rc = mModule->getCameraInfo(i, &info);
- if (rc != NO_ERROR) {
- ALOGE("%s: Received error loading camera info for device %d, cost and"
- " conflicting devices fields set to defaults for this device.",
- __FUNCTION__, i);
- haveInfo = false;
- }
-
- // Check for backwards-compatibility support
- if (haveInfo) {
- if (checkCameraCapabilities(i, info, &latestStrangeCameraId) != OK) {
- delete mModule;
- mModule = nullptr;
- return INVALID_OPERATION;
- }
- }
-
- // Defaults to use for cost and conflicting devices
- int cost = 100;
- char** conflicting_devices = nullptr;
- size_t conflicting_devices_length = 0;
-
- // If using post-2.4 module version, query the cost + conflicting devices from the HAL
- if (mModule->getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_4 && haveInfo) {
- cost = info.resource_cost;
- conflicting_devices = info.conflicting_devices;
- conflicting_devices_length = info.conflicting_devices_length;
- }
-
- std::set<String8> conflicting;
- for (size_t i = 0; i < conflicting_devices_length; i++) {
- conflicting.emplace(String8(conflicting_devices[i]));
- }
-
- // Initialize state for each camera device
- {
- Mutex::Autolock lock(mCameraStatesLock);
- mCameraStates.emplace(cameraId, std::make_shared<CameraState>(cameraId, cost,
- conflicting));
- }
-
- if (mFlashlight->hasFlashUnit(cameraId)) {
- mTorchStatusMap.add(cameraId,
- TorchModeStatus::AVAILABLE_OFF);
- }
- }
-
- if (mModule->getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_1) {
- mModule->setCallbacks(this);
- }
-
- return OK;
-}
-
status_t CameraService::enumerateProviders() {
mCameraProviderManager = new CameraProviderManager();
status_t res;
@@ -316,7 +208,8 @@
}
mNumberOfCameras = mCameraProviderManager->getCameraCount();
- mNumberOfNormalCameras = mCameraProviderManager->getStandardCameraCount();
+ mNumberOfNormalCameras =
+ mCameraProviderManager->getAPI1CompatibleCameraCount();
// Setup vendor tags before we call get_camera_info the first time
// because HAL might need to setup static vendor keys in get_camera_info
@@ -380,10 +273,6 @@
}
CameraService::~CameraService() {
- if (mModule) {
- delete mModule;
- mModule = nullptr;
- }
VendorTagDescriptor::clearGlobalVendorTagDescriptor();
}
@@ -548,28 +437,13 @@
}
Status ret = Status::ok();
- if (mModule != nullptr) {
- struct camera_info info;
- ret = filterGetInfoErrorCode(mModule->getCameraInfo(cameraId, &info));
-
- if (ret.isOk()) {
- cameraInfo->facing = info.facing;
- cameraInfo->orientation = info.orientation;
- // CameraInfo is for android.hardware.Camera which does not
- // support external camera facing. The closest approximation would be
- // front camera.
- if (cameraInfo->facing == CAMERA_FACING_EXTERNAL) {
- cameraInfo->facing = hardware::CAMERA_FACING_FRONT;
- }
- }
- } else {
- status_t err = mCameraProviderManager->getCameraInfo(std::to_string(cameraId), cameraInfo);
- if (err != OK) {
- ret = STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,
- "Error retrieving camera info from device %d: %s (%d)", cameraId,
- strerror(-err), err);
- }
+ status_t err = mCameraProviderManager->getCameraInfo(std::to_string(cameraId), cameraInfo);
+ if (err != OK) {
+ ret = STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,
+ "Error retrieving camera info from device %d: %s (%d)", cameraId,
+ strerror(-err), err);
}
+
return ret;
}
@@ -598,34 +472,12 @@
Status ret{};
- if (mModule != nullptr) {
- int id = cameraIdToInt(String8(cameraId));
-
- if (id < 0 || id >= mNumberOfCameras) {
- ALOGE("%s: Invalid camera id: %d", __FUNCTION__, id);
- return STATUS_ERROR_FMT(ERROR_ILLEGAL_ARGUMENT,
- "Invalid camera id: %d", id);
- }
-
- int version = getDeviceVersion(String8(cameraId));
- if (version < CAMERA_DEVICE_API_VERSION_3_0) {
- return STATUS_ERROR_FMT(ERROR_ILLEGAL_ARGUMENT, "Can't get camera characteristics"
- " for devices with HAL version < 3.0, %d is version %x", id, version);
- }
-
- struct camera_info info;
- ret = filterGetInfoErrorCode(mModule->getCameraInfo(id, &info));
- if (ret.isOk()) {
- *cameraInfo = info.static_camera_characteristics;
- }
- } else {
- status_t res = mCameraProviderManager->getCameraCharacteristics(
- String8(cameraId).string(), cameraInfo);
- if (res != OK) {
- return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION, "Unable to retrieve camera "
- "characteristics for device %s: %s (%d)", String8(cameraId).string(),
- strerror(-res), res);
- }
+ status_t res = mCameraProviderManager->getCameraCharacteristics(
+ String8(cameraId).string(), cameraInfo);
+ if (res != OK) {
+ return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION, "Unable to retrieve camera "
+ "characteristics for device %s: %s (%d)", String8(cameraId).string(),
+ strerror(-res), res);
}
return ret;
@@ -682,39 +534,20 @@
int deviceVersion = 0;
- if (mModule != nullptr) {
- int id = cameraIdToInt(cameraId);
- if (id < 0) return -1;
+ status_t res;
+ hardware::hidl_version maxVersion{0,0};
+ res = mCameraProviderManager->getHighestSupportedVersion(cameraId.string(),
+ &maxVersion);
+ if (res != OK) return -1;
+ deviceVersion = HARDWARE_DEVICE_API_VERSION(maxVersion.get_major(), maxVersion.get_minor());
- struct camera_info info;
- if (mModule->getCameraInfo(id, &info) != OK) {
- return -1;
- }
-
- if (mModule->getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_0) {
- deviceVersion = info.device_version;
- } else {
- deviceVersion = CAMERA_DEVICE_API_VERSION_1_0;
- }
-
- if (facing) {
- *facing = info.facing;
- }
- } else {
- status_t res;
- hardware::hidl_version maxVersion{0,0};
- res = mCameraProviderManager->getHighestSupportedVersion(cameraId.string(),
- &maxVersion);
+ hardware::CameraInfo info;
+ if (facing) {
+ res = mCameraProviderManager->getCameraInfo(cameraId.string(), &info);
if (res != OK) return -1;
- deviceVersion = HARDWARE_DEVICE_API_VERSION(maxVersion.get_major(), maxVersion.get_minor());
-
- hardware::CameraInfo info;
- if (facing) {
- res = mCameraProviderManager->getCameraInfo(cameraId.string(), &info);
- if (res != OK) return -1;
- *facing = info.facing;
- }
+ *facing = info.facing;
}
+
return deviceVersion;
}
@@ -735,45 +568,6 @@
}
}
-bool CameraService::setUpVendorTags() {
- ATRACE_CALL();
- if (mModule == nullptr) return false;
-
- vendor_tag_ops_t vOps = vendor_tag_ops_t();
-
- // Check if vendor operations have been implemented
- if (!mModule->isVendorTagDefined()) {
- ALOGI("%s: No vendor tags defined for this device.", __FUNCTION__);
- return false;
- }
-
- mModule->getVendorTagOps(&vOps);
-
- // Ensure all vendor operations are present
- if (vOps.get_tag_count == NULL || vOps.get_all_tags == NULL ||
- vOps.get_section_name == NULL || vOps.get_tag_name == NULL ||
- vOps.get_tag_type == NULL) {
- ALOGE("%s: Vendor tag operations not fully defined. Ignoring definitions."
- , __FUNCTION__);
- return false;
- }
-
- // Read all vendor tag definitions into a descriptor
- sp<VendorTagDescriptor> desc;
- status_t res;
- if ((res = VendorTagDescriptor::createDescriptorFromOps(&vOps, /*out*/desc))
- != OK) {
- ALOGE("%s: Could not generate descriptor from vendor tag operations,"
- "received error %s (%d). Camera clients will not be able to use"
- "vendor tags", __FUNCTION__, strerror(res), res);
- return false;
- }
-
- // Set the global descriptor to use with camera metadata
- VendorTagDescriptor::setAsGlobalVendorTagDescriptor(desc);
- return true;
-}
-
Status CameraService::makeClient(const sp<CameraService>& cameraService,
const sp<IInterface>& cameraCb, const String16& packageName, const String8& cameraId,
int facing, int clientPid, uid_t clientUid, int servicePid, bool legacyMode,
@@ -1403,25 +1197,6 @@
ATRACE_CALL();
String8 id = String8::format("%d", cameraId);
- if (mModule != nullptr) {
- int apiVersion = mModule->getModuleApiVersion();
- if (halVersion != CAMERA_HAL_API_VERSION_UNSPECIFIED &&
- apiVersion < CAMERA_MODULE_API_VERSION_2_3) {
- /*
- * Either the HAL version is unspecified in which case this just creates
- * a camera client selected by the latest device version, or
- * it's a particular version in which case the HAL must supported
- * the open_legacy call
- */
- String8 msg = String8::format("Camera HAL module version %x too old for connectLegacy!",
- apiVersion);
- ALOGE("%s: %s",
- __FUNCTION__, msg.string());
- logRejected(id, getCallingPid(), String8(clientPackageName),
- msg);
- return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, msg.string());
- }
- }
Status ret = Status::ok();
sp<Client> client = nullptr;
@@ -1564,12 +1339,7 @@
LOG_ALWAYS_FATAL_IF(client.get() == nullptr, "%s: CameraService in invalid state",
__FUNCTION__);
- if (mModule != nullptr) {
- err = client->initialize(mModule);
- } else {
- err = client->initialize(mCameraProviderManager);
- }
-
+ err = client->initialize(mCameraProviderManager);
if (err != OK) {
ALOGE("%s: Could not initialize client from HAL.", __FUNCTION__);
// Errors could be from the HAL module open call or from AppOpsManager
@@ -1988,83 +1758,6 @@
return ret;
}
-
-/**
- * Check camera capabilities, such as support for basic color operation
- * Also check that the device HAL version is still in support
- */
-int CameraService::checkCameraCapabilities(int id, camera_info info, int *latestStrangeCameraId) {
- if (mModule == nullptr) return NO_INIT;
-
- // device_version undefined in CAMERA_MODULE_API_VERSION_1_0,
- // All CAMERA_MODULE_API_VERSION_1_0 devices are backward-compatible
- if (mModule->getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_0) {
- // Verify the device version is in the supported range
- switch (info.device_version) {
- case CAMERA_DEVICE_API_VERSION_1_0:
- case CAMERA_DEVICE_API_VERSION_3_0:
- case CAMERA_DEVICE_API_VERSION_3_1:
- case CAMERA_DEVICE_API_VERSION_3_2:
- case CAMERA_DEVICE_API_VERSION_3_3:
- case CAMERA_DEVICE_API_VERSION_3_4:
- // in support
- break;
- case CAMERA_DEVICE_API_VERSION_2_0:
- case CAMERA_DEVICE_API_VERSION_2_1:
- // no longer supported
- default:
- ALOGE("%s: Device %d has HAL version %x, which is not supported",
- __FUNCTION__, id, info.device_version);
- String8 msg = String8::format(
- "Unsupported device HAL version %x for device %d",
- info.device_version, id);
- logServiceError(msg.string(), NO_INIT);
- return NO_INIT;
- }
- }
-
- // Assume all devices pre-v3.3 are backward-compatible
- bool isBackwardCompatible = true;
- if (mModule->getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_0
- && info.device_version >= CAMERA_DEVICE_API_VERSION_3_3) {
- isBackwardCompatible = false;
- status_t res;
- camera_metadata_ro_entry_t caps;
- res = find_camera_metadata_ro_entry(
- info.static_camera_characteristics,
- ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
- &caps);
- if (res != 0) {
- ALOGW("%s: Unable to find camera capabilities for camera device %d",
- __FUNCTION__, id);
- caps.count = 0;
- }
- for (size_t i = 0; i < caps.count; i++) {
- if (caps.data.u8[i] ==
- ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE) {
- isBackwardCompatible = true;
- break;
- }
- }
- }
-
- if (!isBackwardCompatible) {
- mNumberOfNormalCameras--;
- *latestStrangeCameraId = id;
- } else {
- if (id > *latestStrangeCameraId) {
- ALOGE("%s: Normal camera ID %d higher than strange camera ID %d. "
- "This is not allowed due backward-compatibility requirements",
- __FUNCTION__, id, *latestStrangeCameraId);
- logServiceError("Invalid order of camera devices", NO_INIT);
- mNumberOfCameras = 0;
- mNumberOfNormalCameras = 0;
- return NO_INIT;
- }
- }
- return OK;
-}
-
std::shared_ptr<CameraService::CameraState> CameraService::getCameraState(
const String8& cameraId) const {
std::shared_ptr<CameraState> state;
@@ -2815,61 +2508,13 @@
cameraId.string());
}
- if (mModule != nullptr) {
- dprintf(fd, "== Camera HAL device %s static information: ==\n", cameraId.string());
-
- camera_info info;
- status_t rc = mModule->getCameraInfo(cameraIdToInt(cameraId), &info);
- int deviceVersion = -1;
- if (rc != OK) {
- dprintf(fd, " Error reading static information!\n");
- } else {
- dprintf(fd, " Facing: %s\n",
- info.facing == CAMERA_FACING_BACK ? "BACK" :
- info.facing == CAMERA_FACING_FRONT ? "FRONT" : "EXTERNAL");
- dprintf(fd, " Orientation: %d\n", info.orientation);
-
- if (mModule->getModuleApiVersion() < CAMERA_MODULE_API_VERSION_2_0) {
- deviceVersion = CAMERA_DEVICE_API_VERSION_1_0;
- } else {
- deviceVersion = info.device_version;
- }
- }
-
- auto conflicting = state.second->getConflicting();
- dprintf(fd, " Resource Cost: %d\n", state.second->getCost());
- dprintf(fd, " Conflicting Devices:");
- for (auto& id : conflicting) {
- dprintf(fd, " %s", id.string());
- }
- if (conflicting.size() == 0) {
- dprintf(fd, " NONE");
- }
- dprintf(fd, "\n");
-
- dprintf(fd, " Device version: %#x\n", deviceVersion);
- if (deviceVersion >= CAMERA_DEVICE_API_VERSION_3_0) {
- dprintf(fd, " Device static metadata:\n");
- dump_indented_camera_metadata(info.static_camera_characteristics,
- fd, /*verbosity*/2, /*indentation*/4);
- }
- }
-
}
if (stateLocked) mCameraStatesLock.unlock();
if (locked) mServiceLock.unlock();
- if (mModule == nullptr) {
- mCameraProviderManager->dump(fd, args);
- } else {
- dprintf(fd, "\n== Camera Module HAL static info: ==\n");
- dprintf(fd, "Camera module HAL API version: 0x%x\n", mModule->getHalApiVersion());
- dprintf(fd, "Camera module API version: 0x%x\n", mModule->getModuleApiVersion());
- dprintf(fd, "Camera module name: %s\n", mModule->getModuleName());
- dprintf(fd, "Camera module author: %s\n", mModule->getModuleAuthor());
- }
+ mCameraProviderManager->dump(fd, args);
dprintf(fd, "\n== Vendor tags: ==\n\n");
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index e49fe62..7d81993 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -37,7 +37,6 @@
#include "CameraFlashlight.h"
-#include "common/CameraModule.h"
#include "common/CameraProviderManager.h"
#include "media/RingBuffer.h"
#include "utils/AutoConditionLock.h"
@@ -198,7 +197,6 @@
class BasicClient : public virtual RefBase {
public:
- virtual status_t initialize(CameraModule *module) = 0;
virtual status_t initialize(sp<CameraProviderManager> manager) = 0;
virtual binder::Status disconnect();
@@ -508,9 +506,6 @@
// Delay-load the Camera HAL module
virtual void onFirstRef();
- // Load the legacy HAL module
- status_t loadLegacyHalModule();
-
// Eumerate all camera providers in the system
status_t enumerateProviders();
@@ -568,11 +563,6 @@
std::set<userid_t> mAllowedUsers;
/**
- * Check camera capabilities, such as support for basic color operation
- */
- int checkCameraCapabilities(int id, camera_info info, int *latestStrangeCameraId);
-
- /**
* Get the camera state for a given camera id.
*
* This acquires mCameraStatesLock.
@@ -687,7 +677,6 @@
// Basic flag on whether the camera subsystem is in a usable state
bool mInitialized;
- CameraModule* mModule;
sp<CameraProviderManager> mCameraProviderManager;
// Guarded by mStatusListenerMutex
@@ -745,10 +734,6 @@
// IBinder::DeathRecipient implementation
virtual void binderDied(const wp<IBinder> &who);
- // Helpers
-
- bool setUpVendorTags();
-
/**
* Initialize and cache the metadata used by the HAL1 shim for a given cameraId.
*
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index 335e999..a28518e 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -68,10 +68,6 @@
mLegacyMode = legacyMode;
}
-status_t Camera2Client::initialize(CameraModule *module) {
- return initializeImpl(module);
-}
-
status_t Camera2Client::initialize(sp<CameraProviderManager> manager) {
return initializeImpl(manager);
}
diff --git a/services/camera/libcameraservice/api1/Camera2Client.h b/services/camera/libcameraservice/api1/Camera2Client.h
index 9738aca..72315d4 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.h
+++ b/services/camera/libcameraservice/api1/Camera2Client.h
@@ -100,7 +100,6 @@
virtual ~Camera2Client();
- virtual status_t initialize(CameraModule *module) override;
virtual status_t initialize(sp<CameraProviderManager> manager) override;
virtual status_t dump(int fd, const Vector<String16>& args);
diff --git a/services/camera/libcameraservice/api1/CameraClient.cpp b/services/camera/libcameraservice/api1/CameraClient.cpp
index df8726e..075c2e3 100644
--- a/services/camera/libcameraservice/api1/CameraClient.cpp
+++ b/services/camera/libcameraservice/api1/CameraClient.cpp
@@ -62,16 +62,7 @@
LOG1("CameraClient::CameraClient X (pid %d, id %d)", callingPid, cameraId);
}
-status_t CameraClient::initialize(CameraModule *module) {
- return initializeImpl<CameraModule*>(module);
-}
-
status_t CameraClient::initialize(sp<CameraProviderManager> manager) {
- return initializeImpl<sp<CameraProviderManager>>(manager);
-}
-
-template<typename TProviderPtr>
-status_t CameraClient::initializeImpl(TProviderPtr providerPtr) {
int callingPid = getCallingPid();
status_t res;
@@ -87,7 +78,7 @@
snprintf(camera_device_name, sizeof(camera_device_name), "%d", mCameraId);
mHardware = new CameraHardwareInterface(camera_device_name);
- res = mHardware->initialize(providerPtr);
+ res = mHardware->initialize(manager);
if (res != OK) {
ALOGE("%s: Camera %d: unable to initialize device: %s (%d)",
__FUNCTION__, mCameraId, strerror(-res), res);
@@ -478,14 +469,23 @@
// stop recording mode
void CameraClient::stopRecording() {
LOG1("stopRecording (pid %d)", getCallingPid());
- Mutex::Autolock lock(mLock);
- if (checkPidAndHardware() != NO_ERROR) return;
+ {
+ Mutex::Autolock lock(mLock);
+ if (checkPidAndHardware() != NO_ERROR) return;
- disableMsgType(CAMERA_MSG_VIDEO_FRAME);
- mHardware->stopRecording();
- sCameraService->playSound(CameraService::SOUND_RECORDING_STOP);
+ disableMsgType(CAMERA_MSG_VIDEO_FRAME);
+ mHardware->stopRecording();
+ sCameraService->playSound(CameraService::SOUND_RECORDING_STOP);
- mPreviewBuffer.clear();
+ mPreviewBuffer.clear();
+ }
+
+ {
+ Mutex::Autolock l(mAvailableCallbackBuffersLock);
+ if (!mAvailableCallbackBuffers.empty()) {
+ mAvailableCallbackBuffers.clear();
+ }
+ }
}
// release a recording frame
diff --git a/services/camera/libcameraservice/api1/CameraClient.h b/services/camera/libcameraservice/api1/CameraClient.h
index 1073384..7f93fef 100644
--- a/services/camera/libcameraservice/api1/CameraClient.h
+++ b/services/camera/libcameraservice/api1/CameraClient.h
@@ -72,7 +72,6 @@
bool legacyMode = false);
~CameraClient();
- virtual status_t initialize(CameraModule *module) override;
virtual status_t initialize(sp<CameraProviderManager> manager) override;
virtual status_t dump(int fd, const Vector<String16>& args);
@@ -81,9 +80,6 @@
private:
- template<typename TProviderPtr>
- status_t initializeImpl(TProviderPtr providerPtr);
-
// check whether the calling process matches mClientPid.
status_t checkPid() const;
status_t checkPidAndHardware() const; // also check mHardware != 0
diff --git a/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp b/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp
index 733a78e..6e21126 100644
--- a/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp
@@ -41,11 +41,7 @@
{
SharedParameters::Lock l(client->getParameters());
- if (client->getCameraDeviceVersion() >= CAMERA_DEVICE_API_VERSION_3_2) {
- mUsePartialResult = (mNumPartialResults > 1);
- } else {
- mUsePartialResult = l.mParameters.quirks.partialResults;
- }
+ mUsePartialResult = (mNumPartialResults > 1);
// Initialize starting 3A state
m3aState.afTriggerId = l.mParameters.afTriggerCounter;
@@ -76,16 +72,7 @@
bool isPartialResult = false;
if (mUsePartialResult) {
- if (client->getCameraDeviceVersion() >= CAMERA_DEVICE_API_VERSION_3_2) {
- isPartialResult = frame.mResultExtras.partialResultCount < mNumPartialResults;
- } else {
- camera_metadata_entry_t entry;
- entry = frame.mMetadata.find(ANDROID_QUIRKS_PARTIAL_RESULT);
- if (entry.count > 0 &&
- entry.data.u8[0] == ANDROID_QUIRKS_PARTIAL_RESULT_PARTIAL) {
- isPartialResult = true;
- }
- }
+ isPartialResult = frame.mResultExtras.partialResultCount < mNumPartialResults;
}
if (!isPartialResult && processFaceDetect(frame.mMetadata, client) != OK) {
@@ -291,16 +278,8 @@
gotAllStates &= updatePendingState<uint8_t>(metadata, ANDROID_CONTROL_AWB_STATE,
&pendingState.awbState, frameNumber, cameraId);
- if (client->getCameraDeviceVersion() >= CAMERA_DEVICE_API_VERSION_3_2) {
- pendingState.afTriggerId = frame.mResultExtras.afTriggerId;
- pendingState.aeTriggerId = frame.mResultExtras.precaptureTriggerId;
- } else {
- gotAllStates &= updatePendingState<int32_t>(metadata,
- ANDROID_CONTROL_AF_TRIGGER_ID, &pendingState.afTriggerId, frameNumber, cameraId);
-
- gotAllStates &= updatePendingState<int32_t>(metadata,
- ANDROID_CONTROL_AE_PRECAPTURE_ID, &pendingState.aeTriggerId, frameNumber, cameraId);
- }
+ pendingState.afTriggerId = frame.mResultExtras.afTriggerId;
+ pendingState.aeTriggerId = frame.mResultExtras.precaptureTriggerId;
if (!gotAllStates) {
// If not all states are received, put the pending state to mPending3AStates.
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp
index 1c78a08..a305bc7 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.cpp
+++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp
@@ -2854,32 +2854,14 @@
}
sizes->clear();
- if (mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2) {
- Vector<StreamConfiguration> scs = getStreamConfigurations();
- for (size_t i=0; i < scs.size(); i++) {
- const StreamConfiguration &sc = scs[i];
- if (sc.isInput == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT &&
- sc.format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED &&
- sc.width <= limit.width && sc.height <= limit.height) {
- Size sz = {sc.width, sc.height};
- sizes->push(sz);
- }
- }
- } else {
- const size_t SIZE_COUNT = sizeof(Size) / sizeof(int);
- camera_metadata_ro_entry_t availableProcessedSizes =
- staticInfo(ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES, SIZE_COUNT);
- if (availableProcessedSizes.count < SIZE_COUNT) return BAD_VALUE;
-
- Size filteredSize;
- for (size_t i = 0; i < availableProcessedSizes.count; i += SIZE_COUNT) {
- filteredSize.width = availableProcessedSizes.data.i32[i];
- filteredSize.height = availableProcessedSizes.data.i32[i+1];
- // Need skip the preview sizes that are too large.
- if (filteredSize.width <= limit.width &&
- filteredSize.height <= limit.height) {
- sizes->push(filteredSize);
- }
+ Vector<StreamConfiguration> scs = getStreamConfigurations();
+ for (size_t i=0; i < scs.size(); i++) {
+ const StreamConfiguration &sc = scs[i];
+ if (sc.isInput == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT &&
+ sc.format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED &&
+ sc.width <= limit.width && sc.height <= limit.height) {
+ Size sz = {sc.width, sc.height};
+ sizes->push(sz);
}
}
@@ -2934,10 +2916,6 @@
const int STREAM_HEIGHT_OFFSET = 2;
const int STREAM_IS_INPUT_OFFSET = 3;
Vector<StreamConfiguration> scs;
- if (mDeviceVersion < CAMERA_DEVICE_API_VERSION_3_2) {
- ALOGE("StreamConfiguration is only valid after device HAL 3.2!");
- return scs;
- }
camera_metadata_ro_entry_t availableStreamConfigs =
staticInfo(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
@@ -2953,37 +2931,10 @@
}
int64_t Parameters::getJpegStreamMinFrameDurationNs(Parameters::Size size) {
- if (mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2) {
- return getMinFrameDurationNs(size, HAL_PIXEL_FORMAT_BLOB);
- } else {
- Vector<Size> availableJpegSizes = getAvailableJpegSizes();
- size_t streamIdx = availableJpegSizes.size();
- for (size_t i = 0; i < availableJpegSizes.size(); i++) {
- if (availableJpegSizes[i].width == size.width &&
- availableJpegSizes[i].height == size.height) {
- streamIdx = i;
- break;
- }
- }
- if (streamIdx != availableJpegSizes.size()) {
- camera_metadata_ro_entry_t jpegMinDurations =
- staticInfo(ANDROID_SCALER_AVAILABLE_JPEG_MIN_DURATIONS);
- if (streamIdx < jpegMinDurations.count) {
- return jpegMinDurations.data.i64[streamIdx];
- }
- }
- }
- ALOGE("%s: cannot find min frame duration for jpeg size %dx%d",
- __FUNCTION__, size.width, size.height);
- return -1;
+ return getMinFrameDurationNs(size, HAL_PIXEL_FORMAT_BLOB);
}
int64_t Parameters::getMinFrameDurationNs(Parameters::Size size, int fmt) {
- if (mDeviceVersion < CAMERA_DEVICE_API_VERSION_3_2) {
- ALOGE("Min frame duration for HAL 3.1 or lower is not supported");
- return -1;
- }
-
const int STREAM_DURATION_SIZE = 4;
const int STREAM_FORMAT_OFFSET = 0;
const int STREAM_WIDTH_OFFSET = 1;
@@ -3005,11 +2956,6 @@
}
bool Parameters::isFpsSupported(const Vector<Size> &sizes, int format, int32_t fps) {
- // Skip the check for older HAL version, as the min duration is not supported.
- if (mDeviceVersion < CAMERA_DEVICE_API_VERSION_3_2) {
- return true;
- }
-
// Get min frame duration for each size and check if the given fps range can be supported.
for (size_t i = 0 ; i < sizes.size(); i++) {
int64_t minFrameDuration = getMinFrameDurationNs(sizes[i], format);
@@ -3030,48 +2976,29 @@
SortedVector<int32_t> Parameters::getAvailableOutputFormats() {
SortedVector<int32_t> outputFormats; // Non-duplicated output formats
- if (mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2) {
- Vector<StreamConfiguration> scs = getStreamConfigurations();
- for (size_t i = 0; i < scs.size(); i++) {
- const StreamConfiguration &sc = scs[i];
- if (sc.isInput == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT) {
- outputFormats.add(sc.format);
- }
- }
- } else {
- camera_metadata_ro_entry_t availableFormats = staticInfo(ANDROID_SCALER_AVAILABLE_FORMATS);
- for (size_t i = 0; i < availableFormats.count; i++) {
- outputFormats.add(availableFormats.data.i32[i]);
+ Vector<StreamConfiguration> scs = getStreamConfigurations();
+ for (size_t i = 0; i < scs.size(); i++) {
+ const StreamConfiguration &sc = scs[i];
+ if (sc.isInput == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT) {
+ outputFormats.add(sc.format);
}
}
+
return outputFormats;
}
Vector<Parameters::Size> Parameters::getAvailableJpegSizes() {
Vector<Parameters::Size> jpegSizes;
- if (mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2) {
- Vector<StreamConfiguration> scs = getStreamConfigurations();
- for (size_t i = 0; i < scs.size(); i++) {
- const StreamConfiguration &sc = scs[i];
- if (sc.isInput == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT &&
- sc.format == HAL_PIXEL_FORMAT_BLOB) {
- Size sz = {sc.width, sc.height};
- jpegSizes.add(sz);
- }
- }
- } else {
- const int JPEG_SIZE_ENTRY_COUNT = 2;
- const int WIDTH_OFFSET = 0;
- const int HEIGHT_OFFSET = 1;
- camera_metadata_ro_entry_t availableJpegSizes =
- staticInfo(ANDROID_SCALER_AVAILABLE_JPEG_SIZES);
- for (size_t i = 0; i < availableJpegSizes.count; i+= JPEG_SIZE_ENTRY_COUNT) {
- int width = availableJpegSizes.data.i32[i + WIDTH_OFFSET];
- int height = availableJpegSizes.data.i32[i + HEIGHT_OFFSET];
- Size sz = {width, height};
+ Vector<StreamConfiguration> scs = getStreamConfigurations();
+ for (size_t i = 0; i < scs.size(); i++) {
+ const StreamConfiguration &sc = scs[i];
+ if (sc.isInput == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT &&
+ sc.format == HAL_PIXEL_FORMAT_BLOB) {
+ Size sz = {sc.width, sc.height};
jpegSizes.add(sz);
}
}
+
return jpegSizes;
}
diff --git a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
index bf92a2b..d79e430 100644
--- a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
@@ -114,16 +114,11 @@
}
// Use CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG for ZSL streaming case.
- if (client->getCameraDeviceVersion() >= CAMERA_DEVICE_API_VERSION_3_0) {
- if (params.useZeroShutterLag() && !params.recordingHint) {
- res = device->createDefaultRequest(CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG,
- &mPreviewRequest);
- } else {
- res = device->createDefaultRequest(CAMERA3_TEMPLATE_PREVIEW,
- &mPreviewRequest);
- }
+ if (params.useZeroShutterLag() && !params.recordingHint) {
+ res = device->createDefaultRequest(
+ CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG, &mPreviewRequest);
} else {
- res = device->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW,
+ res = device->createDefaultRequest(CAMERA3_TEMPLATE_PREVIEW,
&mPreviewRequest);
}
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index f2e8df8..0429e7f 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -82,10 +82,6 @@
ALOGI("CameraDeviceClient %s: Opened", cameraId.string());
}
-status_t CameraDeviceClient::initialize(CameraModule *module) {
- return initializeImpl(module);
-}
-
status_t CameraDeviceClient::initialize(sp<CameraProviderManager> manager) {
return initializeImpl(manager);
}
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index 2a95c88..2bf73a0 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -149,7 +149,6 @@
int servicePid);
virtual ~CameraDeviceClient();
- virtual status_t initialize(CameraModule *module) override;
virtual status_t initialize(sp<CameraProviderManager> manager) override;
virtual status_t dump(int fd, const Vector<String16>& args);
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.cpp b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
index 93a584b..32ee273 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.cpp
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
@@ -79,11 +79,6 @@
}
template <typename TClientBase>
-status_t Camera2ClientBase<TClientBase>::initialize(CameraModule *module) {
- return initializeImpl(module);
-}
-
-template <typename TClientBase>
status_t Camera2ClientBase<TClientBase>::initialize(sp<CameraProviderManager> manager) {
return initializeImpl(manager);
}
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.h b/services/camera/libcameraservice/common/Camera2ClientBase.h
index a4c08ef..e898d5d 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.h
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.h
@@ -18,7 +18,6 @@
#define ANDROID_SERVERS_CAMERA_CAMERA2CLIENT_BASE_H
#include "common/CameraDeviceBase.h"
-#include "common/CameraModule.h"
#include "camera/CaptureResult.h"
namespace android {
@@ -56,7 +55,6 @@
int servicePid);
virtual ~Camera2ClientBase();
- virtual status_t initialize(CameraModule *module);
virtual status_t initialize(sp<CameraProviderManager> manager);
virtual status_t dumpClient(int fd, const Vector<String16>& args);
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index 4f788ae..d9059f3 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -30,7 +30,6 @@
#include "hardware/camera3.h"
#include "camera/CameraMetadata.h"
#include "camera/CaptureResult.h"
-#include "common/CameraModule.h"
#include "gui/IGraphicBufferProducer.h"
#include "device3/Camera3StreamInterface.h"
#include "binder/Status.h"
@@ -55,7 +54,6 @@
*/
virtual const String8& getId() const = 0;
- virtual status_t initialize(CameraModule *module) = 0;
virtual status_t initialize(sp<CameraProviderManager> manager) = 0;
virtual status_t disconnect() = 0;
@@ -307,11 +305,6 @@
virtual status_t prepare(int maxCount, int streamId) = 0;
/**
- * Get the HAL device version.
- */
- virtual uint32_t getDeviceVersion() = 0;
-
- /**
* Set the deferred consumer surface and finish the rest of the stream configuration.
*/
virtual status_t setConsumerSurfaces(int streamId,
diff --git a/services/camera/libcameraservice/common/CameraModule.cpp b/services/camera/libcameraservice/common/CameraModule.cpp
deleted file mode 100644
index 073144c..0000000
--- a/services/camera/libcameraservice/common/CameraModule.cpp
+++ /dev/null
@@ -1,403 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "CameraModule"
-#define ATRACE_TAG ATRACE_TAG_CAMERA
-//#define LOG_NDEBUG 0
-
-#include <utils/Trace.h>
-
-#include "CameraModule.h"
-
-namespace android {
-
-void CameraModule::deriveCameraCharacteristicsKeys(
- uint32_t deviceVersion, CameraMetadata &chars) {
- ATRACE_CALL();
-
- Vector<int32_t> derivedCharKeys;
- Vector<int32_t> derivedRequestKeys;
- Vector<int32_t> derivedResultKeys;
- // Keys added in HAL3.3
- if (deviceVersion < CAMERA_DEVICE_API_VERSION_3_3) {
- Vector<uint8_t> controlModes;
- uint8_t data = ANDROID_CONTROL_AE_LOCK_AVAILABLE_TRUE;
- chars.update(ANDROID_CONTROL_AE_LOCK_AVAILABLE, &data, /*count*/1);
- data = ANDROID_CONTROL_AWB_LOCK_AVAILABLE_TRUE;
- chars.update(ANDROID_CONTROL_AWB_LOCK_AVAILABLE, &data, /*count*/1);
- controlModes.push(ANDROID_CONTROL_MODE_AUTO);
- camera_metadata_entry entry = chars.find(ANDROID_CONTROL_AVAILABLE_SCENE_MODES);
- if (entry.count > 1 || entry.data.u8[0] != ANDROID_CONTROL_SCENE_MODE_DISABLED) {
- controlModes.push(ANDROID_CONTROL_MODE_USE_SCENE_MODE);
- }
-
- // Only advertise CONTROL_OFF mode if 3A manual controls are supported.
- bool isManualAeSupported = false;
- bool isManualAfSupported = false;
- bool isManualAwbSupported = false;
- entry = chars.find(ANDROID_CONTROL_AE_AVAILABLE_MODES);
- if (entry.count > 0) {
- for (size_t i = 0; i < entry.count; i++) {
- if (entry.data.u8[i] == ANDROID_CONTROL_AE_MODE_OFF) {
- isManualAeSupported = true;
- break;
- }
- }
- }
- entry = chars.find(ANDROID_CONTROL_AF_AVAILABLE_MODES);
- if (entry.count > 0) {
- for (size_t i = 0; i < entry.count; i++) {
- if (entry.data.u8[i] == ANDROID_CONTROL_AF_MODE_OFF) {
- isManualAfSupported = true;
- break;
- }
- }
- }
- entry = chars.find(ANDROID_CONTROL_AWB_AVAILABLE_MODES);
- if (entry.count > 0) {
- for (size_t i = 0; i < entry.count; i++) {
- if (entry.data.u8[i] == ANDROID_CONTROL_AWB_MODE_OFF) {
- isManualAwbSupported = true;
- break;
- }
- }
- }
- if (isManualAeSupported && isManualAfSupported && isManualAwbSupported) {
- controlModes.push(ANDROID_CONTROL_MODE_OFF);
- }
-
- chars.update(ANDROID_CONTROL_AVAILABLE_MODES, controlModes);
-
- entry = chars.find(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS);
- // HAL3.2 devices passing existing CTS test should all support all LSC modes and LSC map
- bool lensShadingModeSupported = false;
- if (entry.count > 0) {
- for (size_t i = 0; i < entry.count; i++) {
- if (entry.data.i32[i] == ANDROID_SHADING_MODE) {
- lensShadingModeSupported = true;
- break;
- }
- }
- }
- Vector<uint8_t> lscModes;
- Vector<uint8_t> lscMapModes;
- lscModes.push(ANDROID_SHADING_MODE_FAST);
- lscModes.push(ANDROID_SHADING_MODE_HIGH_QUALITY);
- lscMapModes.push(ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF);
- if (lensShadingModeSupported) {
- lscModes.push(ANDROID_SHADING_MODE_OFF);
- lscMapModes.push(ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_ON);
- }
- chars.update(ANDROID_SHADING_AVAILABLE_MODES, lscModes);
- chars.update(ANDROID_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES, lscMapModes);
-
- derivedCharKeys.push(ANDROID_CONTROL_AE_LOCK_AVAILABLE);
- derivedCharKeys.push(ANDROID_CONTROL_AWB_LOCK_AVAILABLE);
- derivedCharKeys.push(ANDROID_CONTROL_AVAILABLE_MODES);
- derivedCharKeys.push(ANDROID_SHADING_AVAILABLE_MODES);
- derivedCharKeys.push(ANDROID_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES);
-
- // Need update android.control.availableHighSpeedVideoConfigurations since HAL3.3
- // adds batch size to this array.
- entry = chars.find(ANDROID_CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS);
- if (entry.count > 0) {
- Vector<int32_t> highSpeedConfig;
- for (size_t i = 0; i < entry.count; i += 4) {
- highSpeedConfig.add(entry.data.i32[i]); // width
- highSpeedConfig.add(entry.data.i32[i + 1]); // height
- highSpeedConfig.add(entry.data.i32[i + 2]); // fps_min
- highSpeedConfig.add(entry.data.i32[i + 3]); // fps_max
- highSpeedConfig.add(1); // batchSize_max. default to 1 for HAL3.2
- }
- chars.update(ANDROID_CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS,
- highSpeedConfig);
- }
- }
-
- // Keys added in HAL3.4
- if (deviceVersion < CAMERA_DEVICE_API_VERSION_3_4) {
- // Check if HAL supports RAW_OPAQUE output
- camera_metadata_entry entry = chars.find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
- bool supportRawOpaque = false;
- bool supportAnyRaw = false;
- const int STREAM_CONFIGURATION_SIZE = 4;
- const int STREAM_FORMAT_OFFSET = 0;
- const int STREAM_WIDTH_OFFSET = 1;
- const int STREAM_HEIGHT_OFFSET = 2;
- const int STREAM_IS_INPUT_OFFSET = 3;
- Vector<int32_t> rawOpaqueSizes;
-
- for (size_t i=0; i < entry.count; i += STREAM_CONFIGURATION_SIZE) {
- int32_t format = entry.data.i32[i + STREAM_FORMAT_OFFSET];
- int32_t width = entry.data.i32[i + STREAM_WIDTH_OFFSET];
- int32_t height = entry.data.i32[i + STREAM_HEIGHT_OFFSET];
- int32_t isInput = entry.data.i32[i + STREAM_IS_INPUT_OFFSET];
- if (isInput == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT &&
- format == HAL_PIXEL_FORMAT_RAW_OPAQUE) {
- supportRawOpaque = true;
- rawOpaqueSizes.push(width);
- rawOpaqueSizes.push(height);
- // 2 bytes per pixel. This rough estimation is only used when
- // HAL does not fill in the opaque raw size
- rawOpaqueSizes.push(width * height *2);
- }
- if (isInput == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT &&
- (format == HAL_PIXEL_FORMAT_RAW16 ||
- format == HAL_PIXEL_FORMAT_RAW10 ||
- format == HAL_PIXEL_FORMAT_RAW12 ||
- format == HAL_PIXEL_FORMAT_RAW_OPAQUE)) {
- supportAnyRaw = true;
- }
- }
-
- if (supportRawOpaque) {
- entry = chars.find(ANDROID_SENSOR_OPAQUE_RAW_SIZE);
- if (entry.count == 0) {
- // Fill in estimated value if HAL does not list it
- chars.update(ANDROID_SENSOR_OPAQUE_RAW_SIZE, rawOpaqueSizes);
- derivedCharKeys.push(ANDROID_SENSOR_OPAQUE_RAW_SIZE);
- }
- }
-
- // Check if HAL supports any RAW output, if so, fill in postRawSensitivityBoost range
- if (supportAnyRaw) {
- int32_t defaultRange[2] = {100, 100};
- entry = chars.find(ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST_RANGE);
- if (entry.count == 0) {
- // Fill in default value (100, 100)
- chars.update(
- ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST_RANGE,
- defaultRange, 2);
- derivedCharKeys.push(ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST_RANGE);
- // Actual request/results will be derived by camera device.
- derivedRequestKeys.push(ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST);
- derivedResultKeys.push(ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST);
- }
- }
- }
-
- // Always add a default for the pre-correction active array if the vendor chooses to omit this
- camera_metadata_entry entry = chars.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
- if (entry.count == 0) {
- Vector<int32_t> preCorrectionArray;
- entry = chars.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
- preCorrectionArray.appendArray(entry.data.i32, entry.count);
- chars.update(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE, preCorrectionArray);
- derivedCharKeys.push(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
- }
-
- // Add those newly added keys to AVAILABLE_CHARACTERISTICS_KEYS
- // This has to be done at this end of this function.
- if (derivedCharKeys.size() > 0) {
- appendAvailableKeys(
- chars, ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS, derivedCharKeys);
- }
- if (derivedRequestKeys.size() > 0) {
- appendAvailableKeys(
- chars, ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS, derivedRequestKeys);
- }
- if (derivedResultKeys.size() > 0) {
- appendAvailableKeys(
- chars, ANDROID_REQUEST_AVAILABLE_RESULT_KEYS, derivedResultKeys);
- }
- return;
-}
-
-void CameraModule::appendAvailableKeys(CameraMetadata &chars,
- int32_t keyTag, const Vector<int32_t>& appendKeys) {
- camera_metadata_entry entry = chars.find(keyTag);
- Vector<int32_t> availableKeys;
- availableKeys.setCapacity(entry.count + appendKeys.size());
- for (size_t i = 0; i < entry.count; i++) {
- availableKeys.push(entry.data.i32[i]);
- }
- for (size_t i = 0; i < appendKeys.size(); i++) {
- availableKeys.push(appendKeys[i]);
- }
- chars.update(keyTag, availableKeys);
-}
-
-CameraModule::CameraModule(camera_module_t *module) {
- if (module == NULL) {
- ALOGE("%s: camera hardware module must not be null", __FUNCTION__);
- assert(0);
- }
- mModule = module;
-}
-
-CameraModule::~CameraModule()
-{
- while (mCameraInfoMap.size() > 0) {
- camera_info cameraInfo = mCameraInfoMap.editValueAt(0);
- if (cameraInfo.static_camera_characteristics != NULL) {
- free_camera_metadata(
- const_cast<camera_metadata_t*>(cameraInfo.static_camera_characteristics));
- }
- mCameraInfoMap.removeItemsAt(0);
- }
-}
-
-int CameraModule::init() {
- ATRACE_CALL();
- int res = OK;
- if (getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_4 &&
- mModule->init != NULL) {
- ATRACE_BEGIN("camera_module->init");
- res = mModule->init();
- ATRACE_END();
- }
- mCameraInfoMap.setCapacity(getNumberOfCameras());
- return res;
-}
-
-int CameraModule::getCameraInfo(int cameraId, struct camera_info *info) {
- ATRACE_CALL();
- Mutex::Autolock lock(mCameraInfoLock);
- if (cameraId < 0) {
- ALOGE("%s: Invalid camera ID %d", __FUNCTION__, cameraId);
- return -EINVAL;
- }
-
- // Only override static_camera_characteristics for API2 devices
- int apiVersion = mModule->common.module_api_version;
- if (apiVersion < CAMERA_MODULE_API_VERSION_2_0) {
- int ret;
- ATRACE_BEGIN("camera_module->get_camera_info");
- ret = mModule->get_camera_info(cameraId, info);
- // Fill in this so CameraService won't be confused by
- // possibly 0 device_version
- info->device_version = CAMERA_DEVICE_API_VERSION_1_0;
- ATRACE_END();
- return ret;
- }
-
- ssize_t index = mCameraInfoMap.indexOfKey(cameraId);
- if (index == NAME_NOT_FOUND) {
- // Get camera info from raw module and cache it
- camera_info rawInfo, cameraInfo;
- ATRACE_BEGIN("camera_module->get_camera_info");
- int ret = mModule->get_camera_info(cameraId, &rawInfo);
- ATRACE_END();
- if (ret != 0) {
- return ret;
- }
- int deviceVersion = rawInfo.device_version;
- if (deviceVersion < CAMERA_DEVICE_API_VERSION_3_0) {
- // static_camera_characteristics is invalid
- *info = rawInfo;
- return ret;
- }
- CameraMetadata m;
- m = rawInfo.static_camera_characteristics;
- deriveCameraCharacteristicsKeys(rawInfo.device_version, m);
- cameraInfo = rawInfo;
- cameraInfo.static_camera_characteristics = m.release();
- index = mCameraInfoMap.add(cameraId, cameraInfo);
- }
-
- assert(index != NAME_NOT_FOUND);
- // return the cached camera info
- *info = mCameraInfoMap[index];
- return OK;
-}
-
-int CameraModule::open(const char* id, struct hw_device_t** device) {
- int res;
- ATRACE_BEGIN("camera_module->open");
- res = filterOpenErrorCode(mModule->common.methods->open(&mModule->common, id, device));
- ATRACE_END();
- return res;
-}
-
-int CameraModule::openLegacy(
- const char* id, uint32_t halVersion, struct hw_device_t** device) {
- int res;
- ATRACE_BEGIN("camera_module->open_legacy");
- res = mModule->open_legacy(&mModule->common, id, halVersion, device);
- ATRACE_END();
- return res;
-}
-
-int CameraModule::getNumberOfCameras() {
- int numCameras;
- ATRACE_BEGIN("camera_module->get_number_of_cameras");
- numCameras = mModule->get_number_of_cameras();
- ATRACE_END();
- return numCameras;
-}
-
-int CameraModule::setCallbacks(const camera_module_callbacks_t *callbacks) {
- int res;
- ATRACE_BEGIN("camera_module->set_callbacks");
- res = mModule->set_callbacks(callbacks);
- ATRACE_END();
- return res;
-}
-
-bool CameraModule::isVendorTagDefined() {
- return mModule->get_vendor_tag_ops != NULL;
-}
-
-void CameraModule::getVendorTagOps(vendor_tag_ops_t* ops) {
- if (mModule->get_vendor_tag_ops) {
- ATRACE_BEGIN("camera_module->get_vendor_tag_ops");
- mModule->get_vendor_tag_ops(ops);
- ATRACE_END();
- }
-}
-
-int CameraModule::setTorchMode(const char* camera_id, bool enable) {
- int res;
- ATRACE_BEGIN("camera_module->set_torch_mode");
- res = mModule->set_torch_mode(camera_id, enable);
- ATRACE_END();
- return res;
-}
-
-status_t CameraModule::filterOpenErrorCode(status_t err) {
- switch(err) {
- case NO_ERROR:
- case -EBUSY:
- case -EINVAL:
- case -EUSERS:
- return err;
- default:
- break;
- }
- return -ENODEV;
-}
-
-uint16_t CameraModule::getModuleApiVersion() {
- return mModule->common.module_api_version;
-}
-
-const char* CameraModule::getModuleName() {
- return mModule->common.name;
-}
-
-uint16_t CameraModule::getHalApiVersion() {
- return mModule->common.hal_api_version;
-}
-
-const char* CameraModule::getModuleAuthor() {
- return mModule->common.author;
-}
-
-void* CameraModule::getDso() {
- return mModule->common.dso;
-}
-
-}; // namespace android
diff --git a/services/camera/libcameraservice/common/CameraModule.h b/services/camera/libcameraservice/common/CameraModule.h
deleted file mode 100644
index d131a26..0000000
--- a/services/camera/libcameraservice/common/CameraModule.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_SERVERS_CAMERA_CAMERAMODULE_H
-#define ANDROID_SERVERS_CAMERA_CAMERAMODULE_H
-
-#include <hardware/camera.h>
-#include <camera/CameraMetadata.h>
-#include <utils/Mutex.h>
-#include <utils/KeyedVector.h>
-
-namespace android {
-/**
- * A wrapper class for HAL camera module.
- *
- * This class wraps camera_module_t returned from HAL to provide a wrapped
- * get_camera_info implementation which CameraService generates some
- * camera characteristics keys defined in newer HAL version on an older HAL.
- */
-class CameraModule {
-public:
- explicit CameraModule(camera_module_t *module);
- virtual ~CameraModule();
-
- // Must be called after construction
- // Returns OK on success, NO_INIT on failure
- int init();
-
- int getCameraInfo(int cameraId, struct camera_info *info);
- int getNumberOfCameras(void);
- int open(const char* id, struct hw_device_t** device);
- int openLegacy(const char* id, uint32_t halVersion, struct hw_device_t** device);
- int setCallbacks(const camera_module_callbacks_t *callbacks);
- bool isVendorTagDefined();
- void getVendorTagOps(vendor_tag_ops_t* ops);
- int setTorchMode(const char* camera_id, bool enable);
- uint16_t getModuleApiVersion();
- const char* getModuleName();
- uint16_t getHalApiVersion();
- const char* getModuleAuthor();
- // Only used by CameraModuleFixture native test. Do NOT use elsewhere.
- void *getDso();
-
-private:
- // Derive camera characteristics keys defined after HAL device version
- static void deriveCameraCharacteristicsKeys(uint32_t deviceVersion, CameraMetadata &chars);
- // Helper function to append available[request|result|chars]Keys
- static void appendAvailableKeys(CameraMetadata &chars,
- int32_t keyTag, const Vector<int32_t>& appendKeys);
- status_t filterOpenErrorCode(status_t err);
- camera_module_t *mModule;
- KeyedVector<int, camera_info> mCameraInfoMap;
- Mutex mCameraInfoLock;
-};
-
-} // namespace android
-
-#endif
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index f3a81cb..38fe1b6 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -82,12 +82,16 @@
return count;
}
-int CameraProviderManager::getStandardCameraCount() const {
+int CameraProviderManager::getAPI1CompatibleCameraCount() const {
std::lock_guard<std::mutex> lock(mInterfaceMutex);
int count = 0;
for (auto& provider : mProviders) {
if (kStandardProviderTypes.find(provider->getType()) != std::string::npos) {
- count += provider->mUniqueDeviceCount;
+ for (auto& device : provider->mDevices) {
+ if (device->isAPI1Compatible()) {
+ count++;
+ }
+ }
}
}
return count;
@@ -104,13 +108,15 @@
return deviceIds;
}
-std::vector<std::string> CameraProviderManager::getStandardCameraDeviceIds() const {
+std::vector<std::string> CameraProviderManager::getAPI1CompatibleCameraDeviceIds() const {
std::lock_guard<std::mutex> lock(mInterfaceMutex);
std::vector<std::string> deviceIds;
for (auto& provider : mProviders) {
if (kStandardProviderTypes.find(provider->getType()) != std::string::npos) {
- for (auto& id : provider->mUniqueCameraIds) {
- deviceIds.push_back(id);
+ for (auto& device : provider->mDevices) {
+ if (device->isAPI1Compatible()) {
+ deviceIds.push_back(device->mId);
+ }
}
}
}
@@ -983,6 +989,20 @@
return OK;
}
+bool CameraProviderManager::ProviderInfo::DeviceInfo3::isAPI1Compatible() const {
+ bool isBackwardCompatible = false;
+ camera_metadata_ro_entry_t caps = mCameraCharacteristics.find(
+ ANDROID_REQUEST_AVAILABLE_CAPABILITIES);
+ for (size_t i = 0; i < caps.count; i++) {
+ if (caps.data.u8[i] ==
+ ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE) {
+ isBackwardCompatible = true;
+ break;
+ }
+ }
+
+ return isBackwardCompatible;
+}
status_t CameraProviderManager::ProviderInfo::DeviceInfo3::getCameraCharacteristics(
CameraMetadata *characteristics) const {
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h
index 2df4fd5..3afc1d9 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.h
+++ b/services/camera/libcameraservice/common/CameraProviderManager.h
@@ -125,16 +125,16 @@
int getCameraCount() const;
/**
- * Retrieve the number of 'standard' cameras; these are internal and
+ * Retrieve the number of API1 compatible cameras; these are internal and
* backwards-compatible. This is the set of cameras that will be
* accessible via the old camera API, with IDs in range of
- * [0, getStandardCameraCount()-1]. This value is not expected to change dynamically.
+ * [0, getAPI1CompatibleCameraCount()-1]. This value is not expected to change dynamically.
*/
- int getStandardCameraCount() const;
+ int getAPI1CompatibleCameraCount() const;
std::vector<std::string> getCameraDeviceIds() const;
- std::vector<std::string> getStandardCameraDeviceIds() const;
+ std::vector<std::string> getAPI1CompatibleCameraDeviceIds() const;
/**
* Return true if a device with a given ID and major version exists
@@ -291,6 +291,7 @@
bool hasFlashUnit() const { return mHasFlashUnit; }
virtual status_t setTorchMode(bool enabled) = 0;
virtual status_t getCameraInfo(hardware::CameraInfo *info) const = 0;
+ virtual bool isAPI1Compatible() const = 0;
virtual status_t getCameraCharacteristics(CameraMetadata *characteristics) const {
(void) characteristics;
return INVALID_OPERATION;
@@ -321,7 +322,8 @@
virtual status_t setTorchMode(bool enabled) override;
virtual status_t getCameraInfo(hardware::CameraInfo *info) const override;
-
+ //In case of Device1Info assume that we are always API1 compatible
+ virtual bool isAPI1Compatible() const override { return true; }
DeviceInfo1(const std::string& name, const metadata_vendor_id_t tagId,
const std::string &id, uint16_t minorVersion,
const hardware::camera::common::V1_0::CameraResourceCost& resourceCost,
@@ -338,6 +340,7 @@
virtual status_t setTorchMode(bool enabled) override;
virtual status_t getCameraInfo(hardware::CameraInfo *info) const override;
+ virtual bool isAPI1Compatible() const override;
virtual status_t getCameraCharacteristics(
CameraMetadata *characteristics) const override;
diff --git a/services/camera/libcameraservice/common/FrameProcessorBase.cpp b/services/camera/libcameraservice/common/FrameProcessorBase.cpp
index 9e78f88..41c953b 100644
--- a/services/camera/libcameraservice/common/FrameProcessorBase.cpp
+++ b/services/camera/libcameraservice/common/FrameProcessorBase.cpp
@@ -32,8 +32,7 @@
mDevice(device),
mNumPartialResults(1) {
sp<CameraDeviceBase> cameraDevice = device.promote();
- if (cameraDevice != 0 &&
- cameraDevice->getDeviceVersion() >= CAMERA_DEVICE_API_VERSION_3_2) {
+ if (cameraDevice != 0) {
CameraMetadata staticInfo = cameraDevice->info();
camera_metadata_entry_t entry = staticInfo.find(ANDROID_REQUEST_PARTIAL_RESULT_COUNT);
if (entry.count > 0) {
@@ -171,18 +170,8 @@
camera_metadata_ro_entry_t entry;
// Check if this result is partial.
- bool isPartialResult = false;
- if (device->getDeviceVersion() >= CAMERA_DEVICE_API_VERSION_3_2) {
- isPartialResult = result.mResultExtras.partialResultCount < mNumPartialResults;
- } else {
- entry = result.mMetadata.find(ANDROID_QUIRKS_PARTIAL_RESULT);
- if (entry.count != 0 &&
- entry.data.u8[0] == ANDROID_QUIRKS_PARTIAL_RESULT_PARTIAL) {
- ALOGV("%s: Camera %s: This is a partial result",
- __FUNCTION__, device->getId().string());
- isPartialResult = true;
- }
- }
+ bool isPartialResult =
+ result.mResultExtras.partialResultCount < mNumPartialResults;
// TODO: instead of getting requestID from CameraMetadata, we should get it
// from CaptureResultExtras. This will require changing Camera2Device.
diff --git a/services/camera/libcameraservice/device1/CameraHardwareInterface.cpp b/services/camera/libcameraservice/device1/CameraHardwareInterface.cpp
index 9df7cd4..469c86c 100644
--- a/services/camera/libcameraservice/device1/CameraHardwareInterface.cpp
+++ b/services/camera/libcameraservice/device1/CameraHardwareInterface.cpp
@@ -41,37 +41,6 @@
}
}
-status_t CameraHardwareInterface::initialize(CameraModule *module)
-{
- if (mHidlDevice != nullptr) {
- ALOGE("%s: camera hardware interface has been initialized to HIDL path!", __FUNCTION__);
- return INVALID_OPERATION;
- }
- ALOGI("Opening camera %s", mName.string());
- camera_info info;
- status_t res = module->getCameraInfo(atoi(mName.string()), &info);
- if (res != OK) {
- return res;
- }
-
- int rc = OK;
- if (module->getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_3 &&
- info.device_version > CAMERA_DEVICE_API_VERSION_1_0) {
- // Open higher version camera device as HAL1.0 device.
- rc = module->openLegacy(mName.string(),
- CAMERA_DEVICE_API_VERSION_1_0,
- (hw_device_t **)&mDevice);
- } else {
- rc = module->open(mName.string(), (hw_device_t **)&mDevice);
- }
- if (rc != OK) {
- ALOGE("Could not open camera %s: %d", mName.string(), rc);
- return rc;
- }
- initHalPreviewWindow();
- return rc;
-}
-
status_t CameraHardwareInterface::initialize(sp<CameraProviderManager> manager) {
if (mDevice) {
ALOGE("%s: camera hardware interface has been initialized to libhardware path!",
diff --git a/services/camera/libcameraservice/device1/CameraHardwareInterface.h b/services/camera/libcameraservice/device1/CameraHardwareInterface.h
index 907065f..1c38d00 100644
--- a/services/camera/libcameraservice/device1/CameraHardwareInterface.h
+++ b/services/camera/libcameraservice/device1/CameraHardwareInterface.h
@@ -28,7 +28,6 @@
#include <system/window.h>
#include <hardware/camera.h>
-#include <common/CameraModule.h>
#include <common/CameraProviderManager.h>
namespace android {
@@ -107,7 +106,6 @@
~CameraHardwareInterface();
- status_t initialize(CameraModule *module);
status_t initialize(sp<CameraProviderManager> manager);
/** Set the ANativeWindow to which preview frames are sent */
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index b64488c..4706319 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -94,82 +94,6 @@
return mId;
}
-/**
- * CameraDeviceBase interface
- */
-
-status_t Camera3Device::initialize(CameraModule *module)
-{
- ATRACE_CALL();
- Mutex::Autolock il(mInterfaceLock);
- Mutex::Autolock l(mLock);
-
- ALOGV("%s: Initializing device for camera %s", __FUNCTION__, mId.string());
- if (mStatus != STATUS_UNINITIALIZED) {
- CLOGE("Already initialized!");
- return INVALID_OPERATION;
- }
-
- /** Open HAL device */
-
- status_t res;
-
- camera3_device_t *device;
-
- ATRACE_BEGIN("CameraHal::open");
- res = module->open(mId.string(),
- reinterpret_cast<hw_device_t**>(&device));
- ATRACE_END();
-
- if (res != OK) {
- SET_ERR_L("Could not open camera: %s (%d)", strerror(-res), res);
- return res;
- }
-
- /** Cross-check device version */
- if (device->common.version < CAMERA_DEVICE_API_VERSION_3_2) {
- SET_ERR_L("Could not open camera: "
- "Camera device should be at least %x, reports %x instead",
- CAMERA_DEVICE_API_VERSION_3_2,
- device->common.version);
- device->common.close(&device->common);
- return BAD_VALUE;
- }
-
- camera_info info;
- res = module->getCameraInfo(atoi(mId), &info);
- if (res != OK) return res;
-
- if (info.device_version != device->common.version) {
- SET_ERR_L("HAL reporting mismatched camera_info version (%x)"
- " and device version (%x).",
- info.device_version, device->common.version);
- device->common.close(&device->common);
- return BAD_VALUE;
- }
-
- /** Initialize device with callback functions */
-
- ATRACE_BEGIN("CameraHal::initialize");
- res = device->ops->initialize(device, this);
- ATRACE_END();
-
- if (res != OK) {
- SET_ERR_L("Unable to initialize HAL device: %s (%d)",
- strerror(-res), res);
- device->common.close(&device->common);
- return res;
- }
-
- /** Everything is good to go */
-
- mDeviceVersion = device->common.version;
- mDeviceInfo = info.static_camera_characteristics;
- mInterface = std::make_unique<HalInterface>(device);
-
- return initializeCommonLocked();
-}
-
status_t Camera3Device::initialize(sp<CameraProviderManager> manager) {
ATRACE_CALL();
Mutex::Autolock il(mInterfaceLock);
@@ -212,8 +136,7 @@
if (!requestQueueRet.isOk()) {
ALOGE("Transaction error when getting request metadata fmq: %s, not use it",
requestQueueRet.description().c_str());
- queue = nullptr;
- // Don't use the queue onwards.
+ return DEAD_OBJECT;
}
auto resultQueueRet = session->getCaptureResultMetadataQueue(
[&queue = mResultMetadataQueue](const auto& descriptor) {
@@ -227,13 +150,9 @@
if (!resultQueueRet.isOk()) {
ALOGE("Transaction error when getting result metadata queue from camera session: %s",
resultQueueRet.description().c_str());
- mResultMetadataQueue = nullptr;
- // Don't use the queue onwards.
+ return DEAD_OBJECT;
}
- // TODO: camera service will absorb 3_2/3_3/3_4 differences in the future
- // for now use 3_4 to keep legacy devices working
- mDeviceVersion = CAMERA_DEVICE_API_VERSION_3_4;
mInterface = std::make_unique<HalInterface>(session, queue);
std::string providerType;
mVendorTagId = manager->getProviderTagIdLocked(mId.string());
@@ -262,17 +181,8 @@
mTagMonitor.initialize(mVendorTagId);
- bool aeLockAvailable = false;
- camera_metadata_entry aeLockAvailableEntry = mDeviceInfo.find(
- ANDROID_CONTROL_AE_LOCK_AVAILABLE);
- if (aeLockAvailableEntry.count > 0) {
- aeLockAvailable = (aeLockAvailableEntry.data.u8[0] ==
- ANDROID_CONTROL_AE_LOCK_AVAILABLE_TRUE);
- }
-
/** Start up request queue thread */
- mRequestThread = new RequestThread(this, mStatusTracker, mInterface.get(), mDeviceVersion,
- aeLockAvailable);
+ mRequestThread = new RequestThread(this, mStatusTracker, mInterface.get());
res = mRequestThread->run(String8::format("C3Dev-%s-ReqQueue", mId.string()).string());
if (res != OK) {
SET_ERR_L("Unable to start request queue thread: %s (%d)",
@@ -284,13 +194,6 @@
mPreparerThread = new PreparerThread();
- // Determine whether we need to derive sensitivity boost values for older devices.
- // If post-RAW sensitivity boost range is listed, so should post-raw sensitivity control
- // be listed (as the default value 100)
- if (mDeviceInfo.exists(ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST_RANGE)) {
- mDerivePostRawSensKey = true;
- }
-
internalUpdateStatusLocked(STATUS_UNCONFIGURED);
mNextStreamId = 0;
mDummyStreamId = NO_STREAM;
@@ -306,19 +209,11 @@
}
// Will the HAL be sending in early partial result metadata?
- if (mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2) {
- camera_metadata_entry partialResultsCount =
- mDeviceInfo.find(ANDROID_REQUEST_PARTIAL_RESULT_COUNT);
- if (partialResultsCount.count > 0) {
- mNumPartialResults = partialResultsCount.data.i32[0];
- mUsePartialResult = (mNumPartialResults > 1);
- }
- } else {
- camera_metadata_entry partialResultsQuirk =
- mDeviceInfo.find(ANDROID_QUIRKS_USE_PARTIAL_RESULT);
- if (partialResultsQuirk.count > 0 && partialResultsQuirk.data.u8[0] == 1) {
- mUsePartialResult = true;
- }
+ camera_metadata_entry partialResultsCount =
+ mDeviceInfo.find(ANDROID_REQUEST_PARTIAL_RESULT_COUNT);
+ if (partialResultsCount.count > 0) {
+ mNumPartialResults = partialResultsCount.data.i32[0];
+ mUsePartialResult = (mNumPartialResults > 1);
}
camera_metadata_entry configs =
@@ -434,48 +329,32 @@
Camera3Device::Size Camera3Device::getMaxJpegResolution() const {
int32_t maxJpegWidth = 0, maxJpegHeight = 0;
- if (mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2) {
- const int STREAM_CONFIGURATION_SIZE = 4;
- const int STREAM_FORMAT_OFFSET = 0;
- const int STREAM_WIDTH_OFFSET = 1;
- const int STREAM_HEIGHT_OFFSET = 2;
- const int STREAM_IS_INPUT_OFFSET = 3;
- camera_metadata_ro_entry_t availableStreamConfigs =
- mDeviceInfo.find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
- if (availableStreamConfigs.count == 0 ||
- availableStreamConfigs.count % STREAM_CONFIGURATION_SIZE != 0) {
- return Size(0, 0);
- }
+ const int STREAM_CONFIGURATION_SIZE = 4;
+ const int STREAM_FORMAT_OFFSET = 0;
+ const int STREAM_WIDTH_OFFSET = 1;
+ const int STREAM_HEIGHT_OFFSET = 2;
+ const int STREAM_IS_INPUT_OFFSET = 3;
+ camera_metadata_ro_entry_t availableStreamConfigs =
+ mDeviceInfo.find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
+ if (availableStreamConfigs.count == 0 ||
+ availableStreamConfigs.count % STREAM_CONFIGURATION_SIZE != 0) {
+ return Size(0, 0);
+ }
- // Get max jpeg size (area-wise).
- for (size_t i=0; i < availableStreamConfigs.count; i+= STREAM_CONFIGURATION_SIZE) {
- int32_t format = availableStreamConfigs.data.i32[i + STREAM_FORMAT_OFFSET];
- int32_t width = availableStreamConfigs.data.i32[i + STREAM_WIDTH_OFFSET];
- int32_t height = availableStreamConfigs.data.i32[i + STREAM_HEIGHT_OFFSET];
- int32_t isInput = availableStreamConfigs.data.i32[i + STREAM_IS_INPUT_OFFSET];
- if (isInput == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT
- && format == HAL_PIXEL_FORMAT_BLOB &&
- (width * height > maxJpegWidth * maxJpegHeight)) {
- maxJpegWidth = width;
- maxJpegHeight = height;
- }
- }
- } else {
- camera_metadata_ro_entry availableJpegSizes =
- mDeviceInfo.find(ANDROID_SCALER_AVAILABLE_JPEG_SIZES);
- if (availableJpegSizes.count == 0 || availableJpegSizes.count % 2 != 0) {
- return Size(0, 0);
- }
-
- // Get max jpeg size (area-wise).
- for (size_t i = 0; i < availableJpegSizes.count; i += 2) {
- if ((availableJpegSizes.data.i32[i] * availableJpegSizes.data.i32[i + 1])
- > (maxJpegWidth * maxJpegHeight)) {
- maxJpegWidth = availableJpegSizes.data.i32[i];
- maxJpegHeight = availableJpegSizes.data.i32[i + 1];
- }
+ // Get max jpeg size (area-wise).
+ for (size_t i=0; i < availableStreamConfigs.count; i+= STREAM_CONFIGURATION_SIZE) {
+ int32_t format = availableStreamConfigs.data.i32[i + STREAM_FORMAT_OFFSET];
+ int32_t width = availableStreamConfigs.data.i32[i + STREAM_WIDTH_OFFSET];
+ int32_t height = availableStreamConfigs.data.i32[i + STREAM_HEIGHT_OFFSET];
+ int32_t isInput = availableStreamConfigs.data.i32[i + STREAM_IS_INPUT_OFFSET];
+ if (isInput == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT
+ && format == HAL_PIXEL_FORMAT_BLOB &&
+ (width * height > maxJpegWidth * maxJpegHeight)) {
+ maxJpegWidth = width;
+ maxJpegHeight = height;
}
}
+
return Size(maxJpegWidth, maxJpegHeight);
}
@@ -497,31 +376,6 @@
return measured;
}
-/**
- * Map Android N dataspace definitions back to Android M definitions, for
- * use with HALv3.3 or older.
- *
- * Only map where correspondences exist, and otherwise preserve the value.
- */
-android_dataspace Camera3Device::mapToLegacyDataspace(android_dataspace dataSpace) {
- switch (dataSpace) {
- case HAL_DATASPACE_V0_SRGB_LINEAR:
- return HAL_DATASPACE_SRGB_LINEAR;
- case HAL_DATASPACE_V0_SRGB:
- return HAL_DATASPACE_SRGB;
- case HAL_DATASPACE_V0_JFIF:
- return HAL_DATASPACE_JFIF;
- case HAL_DATASPACE_V0_BT601_625:
- return HAL_DATASPACE_BT601_625;
- case HAL_DATASPACE_V0_BT601_525:
- return HAL_DATASPACE_BT601_525;
- case HAL_DATASPACE_V0_BT709:
- return HAL_DATASPACE_BT709;
- default:
- return dataSpace;
- }
-}
-
hardware::graphics::common::V1_0::PixelFormat Camera3Device::mapToPixelFormat(
int frameworkFormat) {
return (hardware::graphics::common::V1_0::PixelFormat) frameworkFormat;
@@ -1365,32 +1219,17 @@
assert(mStatus != STATUS_ACTIVE);
sp<Camera3OutputStream> newStream;
- // Overwrite stream set id to invalid for HAL3.2 or lower, as buffer manager does support
- // such devices.
- if (mDeviceVersion <= CAMERA_DEVICE_API_VERSION_3_2) {
- streamSetId = CAMERA3_STREAM_SET_ID_INVALID;
- }
if (consumers.size() == 0 && !hasDeferredConsumer) {
ALOGE("%s: Number of consumers cannot be smaller than 1", __FUNCTION__);
return BAD_VALUE;
}
- // HAL3.1 doesn't support deferred consumer stream creation as it requires buffer registration
- // which requires a consumer surface to be available.
- if (hasDeferredConsumer && mDeviceVersion < CAMERA_DEVICE_API_VERSION_3_2) {
- ALOGE("HAL3.1 doesn't support deferred consumer stream creation");
- return BAD_VALUE;
- }
if (hasDeferredConsumer && format != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
ALOGE("Deferred consumer stream creation only support IMPLEMENTATION_DEFINED format");
return BAD_VALUE;
}
- // Use legacy dataspace values for older HALs
- if (mDeviceVersion <= CAMERA_DEVICE_API_VERSION_3_3) {
- dataSpace = mapToLegacyDataspace(dataSpace);
- }
if (format == HAL_PIXEL_FORMAT_BLOB) {
ssize_t blobBufferSize;
if (dataSpace != HAL_DATASPACE_DEPTH) {
@@ -1433,15 +1272,7 @@
}
newStream->setStatusTracker(mStatusTracker);
- /**
- * Camera3 Buffer manager is only supported by HAL3.3 onwards, as the older HALs ( < HAL3.2)
- * requires buffers to be statically allocated for internal static buffer registration, while
- * the buffers provided by buffer manager are really dynamically allocated. For HAL3.2, because
- * not all HAL implementation supports dynamic buffer registeration, exlude it as well.
- */
- if (mDeviceVersion > CAMERA_DEVICE_API_VERSION_3_2) {
- newStream->setBufferManager(mBufferManager);
- }
+ newStream->setBufferManager(mBufferManager);
res = mOutputStreams.add(mNextStreamId, newStream);
if (res < 0) {
@@ -1659,15 +1490,6 @@
set_camera_metadata_vendor_id(rawRequest, mVendorTagId);
mRequestTemplateCache[templateId].acquire(rawRequest);
- // Derive some new keys for backward compatibility
- if (mDerivePostRawSensKey && !mRequestTemplateCache[templateId].exists(
- ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST)) {
- int32_t defaultBoost[1] = {100};
- mRequestTemplateCache[templateId].update(
- ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST,
- defaultBoost, 1);
- }
-
*request = mRequestTemplateCache[templateId];
return OK;
}
@@ -1919,15 +1741,7 @@
mRequestThread->clear(/*out*/frameNumber);
}
- status_t res;
- if (mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_1) {
- res = mRequestThread->flush();
- } else {
- Mutex::Autolock l(mLock);
- res = waitUntilDrainedLocked();
- }
-
- return res;
+ return mRequestThread->flush();
}
status_t Camera3Device::prepare(int streamId) {
@@ -1968,14 +1782,6 @@
Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
- // Teardown can only be accomplished on devices that don't require register_stream_buffers,
- // since we cannot call register_stream_buffers except right after configure_streams.
- if (mDeviceVersion < CAMERA_DEVICE_API_VERSION_3_2) {
- ALOGE("%s: Unable to tear down streams on device HAL v%x",
- __FUNCTION__, mDeviceVersion);
- return NO_INIT;
- }
-
sp<Camera3StreamInterface> stream;
ssize_t outputStreamIdx = mOutputStreams.indexOfKey(streamId);
if (outputStreamIdx == NAME_NOT_FOUND) {
@@ -2013,12 +1819,6 @@
return OK;
}
-uint32_t Camera3Device::getDeviceVersion() {
- ATRACE_CALL();
- Mutex::Autolock il(mInterfaceLock);
- return mDeviceVersion;
-}
-
/**
* Methods called by subclasses
*/
@@ -2511,14 +2311,13 @@
status_t Camera3Device::registerInFlight(uint32_t frameNumber,
int32_t numBuffers, CaptureResultExtras resultExtras, bool hasInput,
- const AeTriggerCancelOverride_t &aeTriggerCancelOverride,
bool hasAppCallback) {
ATRACE_CALL();
Mutex::Autolock l(mInFlightLock);
ssize_t res;
res = mInFlightMap.add(frameNumber, InFlightRequest(numBuffers, resultExtras, hasInput,
- aeTriggerCancelOverride, hasAppCallback));
+ hasAppCallback));
if (res < 0) return res;
if (mInFlightMap.size() == 1) {
@@ -2603,8 +2402,8 @@
}
}
-void Camera3Device::insertResultLocked(CaptureResult *result, uint32_t frameNumber,
- const AeTriggerCancelOverride_t &aeTriggerCancelOverride) {
+void Camera3Device::insertResultLocked(CaptureResult *result,
+ uint32_t frameNumber) {
if (result == nullptr) return;
camera_metadata_t *meta = const_cast<camera_metadata_t *>(
@@ -2623,8 +2422,6 @@
return;
}
- overrideResultForPrecaptureCancel(&result->mMetadata, aeTriggerCancelOverride);
-
// Valid result, insert into queue
List<CaptureResult>::iterator queuedResult =
mResultQueue.insert(mResultQueue.end(), CaptureResult(*result));
@@ -2639,15 +2436,14 @@
void Camera3Device::sendPartialCaptureResult(const camera_metadata_t * partialResult,
- const CaptureResultExtras &resultExtras, uint32_t frameNumber,
- const AeTriggerCancelOverride_t &aeTriggerCancelOverride) {
+ const CaptureResultExtras &resultExtras, uint32_t frameNumber) {
Mutex::Autolock l(mOutputLock);
CaptureResult captureResult;
captureResult.mResultExtras = resultExtras;
captureResult.mMetadata = partialResult;
- insertResultLocked(&captureResult, frameNumber, aeTriggerCancelOverride);
+ insertResultLocked(&captureResult, frameNumber);
}
@@ -2655,8 +2451,7 @@
CaptureResultExtras &resultExtras,
CameraMetadata &collectedPartialResult,
uint32_t frameNumber,
- bool reprocess,
- const AeTriggerCancelOverride_t &aeTriggerCancelOverride) {
+ bool reprocess) {
if (pendingMetadata.isEmpty())
return;
@@ -2690,15 +2485,6 @@
captureResult.mMetadata.append(collectedPartialResult);
}
- // Derive some new keys for backward compaibility
- if (mDerivePostRawSensKey && !captureResult.mMetadata.exists(
- ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST)) {
- int32_t defaultBoost[1] = {100};
- captureResult.mMetadata.update(
- ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST,
- defaultBoost, 1);
- }
-
captureResult.mMetadata.sort();
// Check that there's a timestamp in the result metadata
@@ -2712,7 +2498,7 @@
mTagMonitor.monitorMetadata(TagMonitor::RESULT,
frameNumber, timestamp.data.i64[0], captureResult.mMetadata);
- insertResultLocked(&captureResult, frameNumber, aeTriggerCancelOverride);
+ insertResultLocked(&captureResult, frameNumber);
}
/**
@@ -2732,10 +2518,7 @@
return;
}
- // For HAL3.2 or above, If HAL doesn't support partial, it must always set
- // partial_result to 1 when metadata is included in this result.
if (!mUsePartialResult &&
- mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2 &&
result->result != NULL &&
result->partial_result != 1) {
SET_ERR("Result is malformed for frame %d: partial_result %u must be 1"
@@ -2780,39 +2563,21 @@
// Check if this result carries only partial metadata
if (mUsePartialResult && result->result != NULL) {
- if (mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2) {
- if (result->partial_result > mNumPartialResults || result->partial_result < 1) {
- SET_ERR("Result is malformed for frame %d: partial_result %u must be in"
- " the range of [1, %d] when metadata is included in the result",
- frameNumber, result->partial_result, mNumPartialResults);
- return;
- }
- isPartialResult = (result->partial_result < mNumPartialResults);
- if (isPartialResult) {
- request.collectedPartialResult.append(result->result);
- }
- } else {
- camera_metadata_ro_entry_t partialResultEntry;
- res = find_camera_metadata_ro_entry(result->result,
- ANDROID_QUIRKS_PARTIAL_RESULT, &partialResultEntry);
- if (res != NAME_NOT_FOUND &&
- partialResultEntry.count > 0 &&
- partialResultEntry.data.u8[0] ==
- ANDROID_QUIRKS_PARTIAL_RESULT_PARTIAL) {
- // A partial result. Flag this as such, and collect this
- // set of metadata into the in-flight entry.
- isPartialResult = true;
- request.collectedPartialResult.append(
- result->result);
- request.collectedPartialResult.erase(
- ANDROID_QUIRKS_PARTIAL_RESULT);
- }
+ if (result->partial_result > mNumPartialResults || result->partial_result < 1) {
+ SET_ERR("Result is malformed for frame %d: partial_result %u must be in"
+ " the range of [1, %d] when metadata is included in the result",
+ frameNumber, result->partial_result, mNumPartialResults);
+ return;
+ }
+ isPartialResult = (result->partial_result < mNumPartialResults);
+ if (isPartialResult) {
+ request.collectedPartialResult.append(result->result);
}
if (isPartialResult && request.hasCallback) {
// Send partial capture result
- sendPartialCaptureResult(result->result, request.resultExtras, frameNumber,
- request.aeTriggerCancelOverride);
+ sendPartialCaptureResult(result->result, request.resultExtras,
+ frameNumber);
}
}
@@ -2877,8 +2642,8 @@
CameraMetadata metadata;
metadata = result->result;
sendCaptureResult(metadata, request.resultExtras,
- collectedPartialResult, frameNumber, hasInputBufferInRequest,
- request.aeTriggerCancelOverride);
+ collectedPartialResult, frameNumber,
+ hasInputBufferInRequest);
}
}
@@ -3058,7 +2823,7 @@
// send pending result and buffers
sendCaptureResult(r.pendingMetadata, r.resultExtras,
r.collectedPartialResult, msg.frame_number,
- r.hasInputBuffer, r.aeTriggerCancelOverride);
+ r.hasInputBuffer);
}
returnOutputBuffers(r.pendingOutputBuffers.array(),
r.pendingOutputBuffers.size(), r.shutterTimestamp);
@@ -3096,9 +2861,6 @@
* HalInterface inner class methods
*/
-Camera3Device::HalInterface::HalInterface(camera3_device_t *device) :
- mHal3Device(device) {}
-
Camera3Device::HalInterface::HalInterface(
sp<ICameraDeviceSession> &session,
std::shared_ptr<RequestMetadataQueue> queue) :
@@ -3171,7 +2933,7 @@
// Unknown template ID
return BAD_VALUE;
}
- mHidlSession->constructDefaultRequestSettings(id,
+ auto err = mHidlSession->constructDefaultRequestSettings(id,
[&status, &requestTemplate]
(common::V1_0::Status s, const device::V3_2::CameraMetadata& request) {
status = s;
@@ -3193,7 +2955,12 @@
}
}
});
- res = CameraProviderManager::mapToStatusT(status);
+ if (!err.isOk()) {
+ ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());
+ res = DEAD_OBJECT;
+ } else {
+ res = CameraProviderManager::mapToStatusT(status);
+ }
}
return res;
}
@@ -3267,12 +3034,17 @@
HalStreamConfiguration finalConfiguration;
common::V1_0::Status status;
- mHidlSession->configureStreams(requestedConfiguration,
+ auto err = mHidlSession->configureStreams(requestedConfiguration,
[&status, &finalConfiguration]
(common::V1_0::Status s, const HalStreamConfiguration& halConfiguration) {
finalConfiguration = halConfiguration;
status = s;
});
+ if (!err.isOk()) {
+ ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());
+ return DEAD_OBJECT;
+ }
+
if (status != common::V1_0::Status::OK ) {
return CameraProviderManager::mapToStatusT(status);
}
@@ -3458,12 +3230,15 @@
captureRequest->fmqSettingsSize = 0u;
}
}
- mHidlSession->processCaptureRequest(captureRequests, cachesToRemove,
+ auto err = mHidlSession->processCaptureRequest(captureRequests, cachesToRemove,
[&status, &numRequestProcessed] (auto s, uint32_t n) {
status = s;
*numRequestProcessed = n;
});
-
+ if (!err.isOk()) {
+ ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());
+ return DEAD_OBJECT;
+ }
if (status == common::V1_0::Status::OK && *numRequestProcessed != batchSize) {
ALOGE("%s: processCaptureRequest returns OK but processed %d/%zu requests",
__FUNCTION__, *numRequestProcessed, batchSize);
@@ -3501,7 +3276,13 @@
if (mHal3Device != nullptr) {
res = mHal3Device->ops->flush(mHal3Device);
} else {
- res = CameraProviderManager::mapToStatusT(mHidlSession->flush());
+ auto err = mHidlSession->flush();
+ if (!err.isOk()) {
+ ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());
+ res = DEAD_OBJECT;
+ } else {
+ res = CameraProviderManager::mapToStatusT(err);
+ }
}
return res;
}
@@ -3527,7 +3308,11 @@
if (mHal3Device != nullptr) {
mHal3Device->common.close(&mHal3Device->common);
} else {
- mHidlSession->close();
+ auto err = mHidlSession->close();
+ // Interface will be dead shortly anyway, so don't log errors
+ if (!err.isOk()) {
+ res = DEAD_OBJECT;
+ }
}
return res;
}
@@ -3606,14 +3391,11 @@
Camera3Device::RequestThread::RequestThread(wp<Camera3Device> parent,
sp<StatusTracker> statusTracker,
- HalInterface* interface,
- uint32_t deviceVersion,
- bool aeLockAvailable) :
+ HalInterface* interface) :
Thread(/*canCallJava*/false),
mParent(parent),
mStatusTracker(statusTracker),
mInterface(interface),
- mDeviceVersion(deviceVersion),
mListener(nullptr),
mId(getId(parent)),
mReconfigured(false),
@@ -3625,7 +3407,6 @@
mCurrentPreCaptureTriggerId(0),
mRepeatingLastFrameNumber(
hardware::camera2::ICameraDeviceUser::NO_IN_FLIGHT_REPEATING_FRAMES),
- mAeLockAvailable(aeLockAvailable),
mPrepareVideoStream(false) {
mStatusId = statusTracker->addComponent();
}
@@ -3820,11 +3601,7 @@
ATRACE_CALL();
Mutex::Autolock l(mFlushLock);
- if (mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_1) {
- return mInterface->flush();
- }
-
- return -ENOTSUP;
+ return mInterface->flush();
}
void Camera3Device::RequestThread::setPaused(bool paused) {
@@ -3857,65 +3634,6 @@
mRequestSignal.signal();
}
-
-/**
- * For devices <= CAMERA_DEVICE_API_VERSION_3_2, AE_PRECAPTURE_TRIGGER_CANCEL is not supported so
- * we need to override AE_PRECAPTURE_TRIGGER_CANCEL to AE_PRECAPTURE_TRIGGER_IDLE and AE_LOCK_OFF
- * to AE_LOCK_ON to start cancelling AE precapture. If AE lock is not available, it still overrides
- * AE_PRECAPTURE_TRIGGER_CANCEL to AE_PRECAPTURE_TRIGGER_IDLE but doesn't add AE_LOCK_ON to the
- * request.
- */
-void Camera3Device::RequestThread::handleAePrecaptureCancelRequest(const sp<CaptureRequest>& request) {
- request->mAeTriggerCancelOverride.applyAeLock = false;
- request->mAeTriggerCancelOverride.applyAePrecaptureTrigger = false;
-
- if (mDeviceVersion > CAMERA_DEVICE_API_VERSION_3_2) {
- return;
- }
-
- camera_metadata_entry_t aePrecaptureTrigger =
- request->mSettings.find(ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER);
- if (aePrecaptureTrigger.count > 0 &&
- aePrecaptureTrigger.data.u8[0] == ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL) {
- // Always override CANCEL to IDLE
- uint8_t aePrecaptureTrigger = ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_IDLE;
- request->mSettings.update(ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER, &aePrecaptureTrigger, 1);
- request->mAeTriggerCancelOverride.applyAePrecaptureTrigger = true;
- request->mAeTriggerCancelOverride.aePrecaptureTrigger =
- ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL;
-
- if (mAeLockAvailable == true) {
- camera_metadata_entry_t aeLock = request->mSettings.find(ANDROID_CONTROL_AE_LOCK);
- if (aeLock.count == 0 || aeLock.data.u8[0] == ANDROID_CONTROL_AE_LOCK_OFF) {
- uint8_t aeLock = ANDROID_CONTROL_AE_LOCK_ON;
- request->mSettings.update(ANDROID_CONTROL_AE_LOCK, &aeLock, 1);
- request->mAeTriggerCancelOverride.applyAeLock = true;
- request->mAeTriggerCancelOverride.aeLock = ANDROID_CONTROL_AE_LOCK_OFF;
- }
- }
- }
-}
-
-/**
- * Override result metadata for cancelling AE precapture trigger applied in
- * handleAePrecaptureCancelRequest().
- */
-void Camera3Device::overrideResultForPrecaptureCancel(
- CameraMetadata *result, const AeTriggerCancelOverride_t &aeTriggerCancelOverride) {
- if (aeTriggerCancelOverride.applyAeLock) {
- // Only devices <= v3.2 should have this override
- assert(mDeviceVersion <= CAMERA_DEVICE_API_VERSION_3_2);
- result->update(ANDROID_CONTROL_AE_LOCK, &aeTriggerCancelOverride.aeLock, 1);
- }
-
- if (aeTriggerCancelOverride.applyAePrecaptureTrigger) {
- // Only devices <= v3.2 should have this override
- assert(mDeviceVersion <= CAMERA_DEVICE_API_VERSION_3_2);
- result->update(ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
- &aeTriggerCancelOverride.aePrecaptureTrigger, 1);
- }
-}
-
void Camera3Device::RequestThread::checkAndStopRepeatingRequest() {
bool surfaceAbandoned = false;
int64_t lastFrameNumber = 0;
@@ -4283,7 +4001,6 @@
res = parent->registerInFlight(halRequest->frame_number,
totalNumBuffers, captureRequest->mResultExtras,
/*hasInput*/halRequest->input_buffer != NULL,
- captureRequest->mAeTriggerCancelOverride,
hasCallback);
ALOGVV("%s: registered in flight requestId = %" PRId32 ", frameNumber = %" PRId64
", burstId = %" PRId32 ".",
@@ -4542,8 +4259,6 @@
}
}
- handleAePrecaptureCancelRequest(nextRequest);
-
return nextRequest;
}
@@ -4629,9 +4344,7 @@
request->mResultExtras.afTriggerId = triggerId;
mCurrentAfTriggerId = triggerId;
}
- if (parent->mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2) {
- continue; // Trigger ID tag is deprecated since device HAL 3.2
- }
+ continue;
}
camera_metadata_entry entry = metadata.find(tag);
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index eaffad8..1ca6811 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -87,7 +87,6 @@
const String8& getId() const override;
// Transitions to idle state on success.
- status_t initialize(CameraModule *module) override;
status_t initialize(sp<CameraProviderManager> manager) override;
status_t disconnect() override;
status_t dump(int fd, const Vector<String16> &args) override;
@@ -166,8 +165,6 @@
status_t prepare(int maxCount, int streamId) override;
- uint32_t getDeviceVersion() override;
-
ssize_t getJpegBufferSize(uint32_t width, uint32_t height) const override;
ssize_t getPointCloudBufferSize() const;
ssize_t getRawOpaqueBufferSize(int32_t width, int32_t height) const;
@@ -235,7 +232,6 @@
*/
class HalInterface : public camera3::Camera3StreamBufferFreedListener {
public:
- HalInterface(camera3_device_t *device);
HalInterface(sp<hardware::camera::device::V3_2::ICameraDeviceSession> &session,
std::shared_ptr<RequestMetadataQueue> queue);
HalInterface(const HalInterface &other);
@@ -294,9 +290,7 @@
size_t result = 1;
result = 31 * result + buf->numFds;
- result = 31 * result + buf->numInts;
- int length = buf->numFds + buf->numInts;
- for (int i = 0; i < length; i++) {
+ for (int i = 0; i < buf->numFds; i++) {
result = 31 * result + buf->data[i];
}
return result;
@@ -305,9 +299,8 @@
struct BufferComparator {
bool operator()(const buffer_handle_t& buf1, const buffer_handle_t& buf2) const {
- if (buf1->numFds == buf2->numFds && buf1->numInts == buf2->numInts) {
- int length = buf1->numFds + buf1->numInts;
- for (int i = 0; i < length; i++) {
+ if (buf1->numFds == buf2->numFds) {
+ for (int i = 0; i < buf1->numFds; i++) {
if (buf1->data[i] != buf2->data[i]) {
return false;
}
@@ -345,12 +338,6 @@
CameraMetadata mRequestTemplateCache[CAMERA3_TEMPLATE_COUNT];
- uint32_t mDeviceVersion;
-
- // whether Camera3Device should derive ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST for
- // backward compatibility. Should not be changed after initialization.
- bool mDerivePostRawSensKey = false;
-
struct Size {
uint32_t width;
uint32_t height;
@@ -408,13 +395,6 @@
// words, camera device shouldn't be open during CPU suspend.
nsecs_t mTimestampOffset;
- typedef struct AeTriggerCancelOverride {
- bool applyAeLock;
- uint8_t aeLock;
- bool applyAePrecaptureTrigger;
- uint8_t aePrecaptureTrigger;
- } AeTriggerCancelOverride_t;
-
class CaptureRequest : public LightRefBase<CaptureRequest> {
public:
CameraMetadata mSettings;
@@ -424,9 +404,6 @@
mOutputStreams;
SurfaceMap mOutputSurfaces;
CaptureResultExtras mResultExtras;
- // Used to cancel AE precapture trigger for devices doesn't support
- // CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL
- AeTriggerCancelOverride_t mAeTriggerCancelOverride;
// The number of requests that should be submitted to HAL at a time.
// For example, if batch size is 8, this request and the following 7
// requests will be submitted to HAL at a time. The batch size for
@@ -602,11 +579,6 @@
static nsecs_t getMonoToBoottimeOffset();
/**
- * Helper function to map between legacy and new dataspace enums
- */
- static android_dataspace mapToLegacyDataspace(android_dataspace dataSpace);
-
- /**
* Helper functions to map between framework and HIDL values
*/
static hardware::graphics::common::V1_0::PixelFormat mapToPixelFormat(int frameworkFormat);
@@ -651,9 +623,7 @@
RequestThread(wp<Camera3Device> parent,
sp<camera3::StatusTracker> statusTracker,
- HalInterface* interface,
- uint32_t deviceVersion,
- bool aeLockAvailable);
+ HalInterface* interface);
~RequestThread();
void setNotificationListener(wp<NotificationListener> listener);
@@ -787,9 +757,6 @@
// If the input request is in mRepeatingRequests. Must be called with mRequestLock hold
bool isRepeatingRequestLocked(const sp<CaptureRequest>&);
- // Handle AE precapture trigger cancel for devices <= CAMERA_DEVICE_API_VERSION_3_2.
- void handleAePrecaptureCancelRequest(const sp<CaptureRequest>& request);
-
// Clear repeating requests. Must be called with mRequestLock held.
status_t clearRepeatingRequestsLocked(/*out*/ int64_t *lastFrameNumber = NULL);
@@ -802,7 +769,6 @@
wp<Camera3Device> mParent;
wp<camera3::StatusTracker> mStatusTracker;
HalInterface* mInterface;
- uint32_t mDeviceVersion;
wp<NotificationListener> mListener;
@@ -852,9 +818,6 @@
int64_t mRepeatingLastFrameNumber;
- // Whether the device supports AE lock
- bool mAeLockAvailable;
-
// Flag indicating if we should prepare video stream for video requests.
bool mPrepareVideoStream;
};
@@ -895,10 +858,6 @@
// the shutter event.
Vector<camera3_stream_buffer_t> pendingOutputBuffers;
- // Used to cancel AE precapture trigger for devices doesn't support
- // CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL
- AeTriggerCancelOverride_t aeTriggerCancelOverride;
-
// Whether this inflight request's shutter and result callback are to be
// called. The policy is that if the request is the last one in the constrained
// high speed recording request list, this flag will be true. If the request list
@@ -913,12 +872,11 @@
haveResultMetadata(false),
numBuffersLeft(0),
hasInputBuffer(false),
- aeTriggerCancelOverride({false, 0, false, 0}),
hasCallback(true) {
}
InFlightRequest(int numBuffers, CaptureResultExtras extras, bool hasInput,
- AeTriggerCancelOverride aeTriggerCancelOverride, bool hasAppCallback) :
+ bool hasAppCallback) :
shutterTimestamp(0),
sensorTimestamp(0),
requestStatus(OK),
@@ -926,7 +884,6 @@
numBuffersLeft(numBuffers),
resultExtras(extras),
hasInputBuffer(hasInput),
- aeTriggerCancelOverride(aeTriggerCancelOverride),
hasCallback(hasAppCallback) {
}
};
@@ -940,14 +897,7 @@
status_t registerInFlight(uint32_t frameNumber,
int32_t numBuffers, CaptureResultExtras resultExtras, bool hasInput,
- const AeTriggerCancelOverride_t &aeTriggerCancelOverride, bool callback);
-
- /**
- * Override result metadata for cancelling AE precapture trigger applied in
- * handleAePrecaptureCancelRequest().
- */
- void overrideResultForPrecaptureCancel(CameraMetadata* result,
- const AeTriggerCancelOverride_t &aeTriggerCancelOverride);
+ bool callback);
/**
* Tracking for idle detection
@@ -1042,21 +992,19 @@
// Send a partial capture result.
void sendPartialCaptureResult(const camera_metadata_t * partialResult,
- const CaptureResultExtras &resultExtras, uint32_t frameNumber,
- const AeTriggerCancelOverride_t &aeTriggerCancelOverride);
+ const CaptureResultExtras &resultExtras, uint32_t frameNumber);
// Send a total capture result given the pending metadata and result extras,
// partial results, and the frame number to the result queue.
void sendCaptureResult(CameraMetadata &pendingMetadata,
CaptureResultExtras &resultExtras,
CameraMetadata &collectedPartialResult, uint32_t frameNumber,
- bool reprocess, const AeTriggerCancelOverride_t &aeTriggerCancelOverride);
+ bool reprocess);
// Insert the result to the result queue after updating frame number and overriding AE
// trigger cancel.
// mOutputLock must be held when calling this function.
- void insertResultLocked(CaptureResult *result, uint32_t frameNumber,
- const AeTriggerCancelOverride_t &aeTriggerCancelOverride);
+ void insertResultLocked(CaptureResult *result, uint32_t frameNumber);
/**** Scope for mInFlightLock ****/
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index 2b1a899..b45ef77 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -663,89 +663,6 @@
}
}
-status_t Camera3Stream::registerBuffersLocked(camera3_device *hal3Device) {
- ATRACE_CALL();
-
- /**
- * >= CAMERA_DEVICE_API_VERSION_3_2:
- *
- * camera3_device_t->ops->register_stream_buffers() is not called and must
- * be NULL.
- */
- if (hal3Device->common.version >= CAMERA_DEVICE_API_VERSION_3_2) {
- ALOGV("%s: register_stream_buffers unused as of HAL3.2", __FUNCTION__);
-
- if (hal3Device->ops->register_stream_buffers != NULL) {
- ALOGE("%s: register_stream_buffers is deprecated in HAL3.2; "
- "must be set to NULL in camera3_device::ops", __FUNCTION__);
- return INVALID_OPERATION;
- }
-
- return OK;
- }
-
- ALOGV("%s: register_stream_buffers using deprecated code path", __FUNCTION__);
-
- status_t res;
-
- size_t bufferCount = getBufferCountLocked();
-
- Vector<buffer_handle_t*> buffers;
- buffers.insertAt(/*prototype_item*/NULL, /*index*/0, bufferCount);
-
- camera3_stream_buffer_set bufferSet = camera3_stream_buffer_set();
- bufferSet.stream = this;
- bufferSet.num_buffers = bufferCount;
- bufferSet.buffers = buffers.editArray();
-
- Vector<camera3_stream_buffer_t> streamBuffers;
- streamBuffers.insertAt(camera3_stream_buffer_t(), /*index*/0, bufferCount);
-
- // Register all buffers with the HAL. This means getting all the buffers
- // from the stream, providing them to the HAL with the
- // register_stream_buffers() method, and then returning them back to the
- // stream in the error state, since they won't have valid data.
- //
- // Only registered buffers can be sent to the HAL.
-
- uint32_t bufferIdx = 0;
- for (; bufferIdx < bufferCount; bufferIdx++) {
- res = getBufferLocked( &streamBuffers.editItemAt(bufferIdx) );
- if (res != OK) {
- ALOGE("%s: Unable to get buffer %d for registration with HAL",
- __FUNCTION__, bufferIdx);
- // Skip registering, go straight to cleanup
- break;
- }
-
- sp<Fence> fence = new Fence(streamBuffers[bufferIdx].acquire_fence);
- fence->waitForever("Camera3Stream::registerBuffers");
-
- buffers.editItemAt(bufferIdx) = streamBuffers[bufferIdx].buffer;
- }
- if (bufferIdx == bufferCount) {
- // Got all buffers, register with HAL
- ALOGV("%s: Registering %zu buffers with camera HAL",
- __FUNCTION__, bufferCount);
- ATRACE_BEGIN("camera3->register_stream_buffers");
- res = hal3Device->ops->register_stream_buffers(hal3Device,
- &bufferSet);
- ATRACE_END();
- }
-
- // Return all valid buffers to stream, in ERROR state to indicate
- // they weren't filled.
- for (size_t i = 0; i < bufferIdx; i++) {
- streamBuffers.editItemAt(i).release_fence = -1;
- streamBuffers.editItemAt(i).status = CAMERA3_BUFFER_STATUS_ERROR;
- returnBufferLocked(streamBuffers[i], 0);
- }
-
- mPrepared = true;
-
- return res;
-}
-
status_t Camera3Stream::getBufferLocked(camera3_stream_buffer *,
const std::vector<size_t>&) {
ALOGE("%s: This type of stream does not support output", __FUNCTION__);
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index 27ef86d..9cdc1b3 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -474,9 +474,6 @@
Condition mInputBufferReturnedSignal;
static const nsecs_t kWaitForBufferDuration = 3000000000LL; // 3000 ms
- // Gets all buffers from endpoint and registers them with the HAL.
- status_t registerBuffersLocked(camera3_device *hal3Device);
-
void fireBufferListenersLocked(const camera3_stream_buffer& buffer,
bool acquired, bool output);
List<wp<Camera3StreamBufferListener> > mBufferListenerList;
diff --git a/services/mediaanalytics/Android.mk b/services/mediaanalytics/Android.mk
index f7197af..9e2813e 100644
--- a/services/mediaanalytics/Android.mk
+++ b/services/mediaanalytics/Android.mk
@@ -5,7 +5,12 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- main_mediametrics.cpp \
+ main_mediametrics.cpp \
+ MetricsSummarizerCodec.cpp \
+ MetricsSummarizerExtractor.cpp \
+ MetricsSummarizerPlayer.cpp \
+ MetricsSummarizerRecorder.cpp \
+ MetricsSummarizer.cpp \
MediaAnalyticsService.cpp
LOCAL_SHARED_LIBRARIES := \
diff --git a/services/mediaanalytics/MediaAnalyticsService.cpp b/services/mediaanalytics/MediaAnalyticsService.cpp
index 35c1f5b..876c685 100644
--- a/services/mediaanalytics/MediaAnalyticsService.cpp
+++ b/services/mediaanalytics/MediaAnalyticsService.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@
#define LOG_TAG "MediaAnalyticsService"
#include <utils/Log.h>
+#include <stdint.h>
#include <inttypes.h>
#include <sys/types.h>
#include <sys/stat.h>
@@ -70,11 +71,28 @@
#include "MediaAnalyticsService.h"
+#include "MetricsSummarizer.h"
+#include "MetricsSummarizerCodec.h"
+#include "MetricsSummarizerExtractor.h"
+#include "MetricsSummarizerPlayer.h"
+#include "MetricsSummarizerRecorder.h"
+
namespace android {
-#define DEBUG_QUEUE 0
+
+// summarized records
+// up to 48 sets, each covering an hour -- at least 2 days of coverage
+// (will be longer if there are hours without any media action)
+static const nsecs_t kNewSetIntervalNs = 3600*(1000*1000*1000ll);
+static const int kMaxRecordSets = 48;
+// individual records kept in memory
+static const int kMaxRecords = 100;
+
+
+static const char *kServiceName = "media.metrics";
+
//using android::status_t;
//using android::OK;
@@ -85,18 +103,67 @@
void MediaAnalyticsService::instantiate() {
defaultServiceManager()->addService(
- String16("media.metrics"), new MediaAnalyticsService());
+ String16(kServiceName), new MediaAnalyticsService());
}
-// XXX: add dynamic controls for mMaxRecords
+// handle sets of summarizers
+MediaAnalyticsService::SummarizerSet::SummarizerSet() {
+ mSummarizers = new List<MetricsSummarizer *>();
+}
+MediaAnalyticsService::SummarizerSet::~SummarizerSet() {
+ // empty the list
+ List<MetricsSummarizer *> *l = mSummarizers;
+ while (l->size() > 0) {
+ MetricsSummarizer *summarizer = *(l->begin());
+ l->erase(l->begin());
+ delete summarizer;
+ }
+}
+
+void MediaAnalyticsService::newSummarizerSet() {
+ ALOGD("MediaAnalyticsService::newSummarizerSet");
+ MediaAnalyticsService::SummarizerSet *set = new MediaAnalyticsService::SummarizerSet();
+ nsecs_t now = systemTime(SYSTEM_TIME_REALTIME);
+ set->setStarted(now);
+
+ set->appendSummarizer(new MetricsSummarizerExtractor("extractor"));
+ set->appendSummarizer(new MetricsSummarizerCodec("codec"));
+ set->appendSummarizer(new MetricsSummarizerPlayer("nuplayer"));
+ set->appendSummarizer(new MetricsSummarizerRecorder("recorder"));
+
+ // ALWAYS at the end, since it catches everything
+ set->appendSummarizer(new MetricsSummarizer(NULL));
+
+ // inject this set at the BACK of the list.
+ mSummarizerSets->push_back(set);
+ mCurrentSet = set;
+
+ // limit the # that we have
+ if (mMaxRecordSets > 0) {
+ List<SummarizerSet *> *l = mSummarizerSets;
+ while (l->size() > (size_t) mMaxRecordSets) {
+ ALOGD("Deleting oldest record set....");
+ MediaAnalyticsService::SummarizerSet *oset = *(l->begin());
+ l->erase(l->begin());
+ delete oset;
+ mSetsDiscarded++;
+ }
+ }
+}
+
MediaAnalyticsService::MediaAnalyticsService()
- : mMaxRecords(100) {
+ : mMaxRecords(kMaxRecords),
+ mMaxRecordSets(kMaxRecordSets),
+ mNewSetInterval(kNewSetIntervalNs) {
ALOGD("MediaAnalyticsService created");
// clear our queues
mOpen = new List<MediaAnalyticsItem *>();
mFinalized = new List<MediaAnalyticsItem *>();
+ mSummarizerSets = new List<MediaAnalyticsService::SummarizerSet *>();
+ newSummarizerSet();
+
mItemsSubmitted = 0;
mItemsFinalized = 0;
mItemsDiscarded = 0;
@@ -109,7 +176,13 @@
MediaAnalyticsService::~MediaAnalyticsService() {
ALOGD("MediaAnalyticsService destroyed");
- // XXX: clean out mOpen and mFinalized
+ // clean out mOpen and mFinalized
+ delete mOpen;
+ mOpen = NULL;
+ delete mFinalized;
+ mFinalized = NULL;
+
+ // XXX: clean out the summaries
}
@@ -145,7 +218,7 @@
case AID_MEDIA_EX:
case AID_MEDIA_DRM:
// trusted source, only override default values
- isTrusted = true;
+ isTrusted = true;
if (uid_given == (-1)) {
item->setUid(uid);
}
@@ -197,10 +270,12 @@
oitem = NULL;
} else {
oitem->setFinalized(true);
+ summarize(oitem);
saveItem(mFinalized, oitem, 0);
}
// new record could itself be marked finalized...
if (finalizing) {
+ summarize(item);
saveItem(mFinalized, item, 0);
mItemsFinalized++;
} else {
@@ -211,6 +286,7 @@
// combine the records, send it to finalized if appropriate
oitem->merge(item);
if (finalizing) {
+ summarize(oitem);
saveItem(mFinalized, oitem, 0);
mItemsFinalized++;
}
@@ -229,6 +305,7 @@
delete item;
item = NULL;
} else {
+ summarize(item);
saveItem(mFinalized, item, 0);
mItemsFinalized++;
}
@@ -239,26 +316,6 @@
return id;
}
-List<MediaAnalyticsItem *> *MediaAnalyticsService::getMediaAnalyticsItemList(bool finished, nsecs_t ts) {
- // this might never get called; the binder interface maps to the full parm list
- // on the client side before making the binder call.
- // but this lets us be sure...
- List<MediaAnalyticsItem*> *list;
- list = getMediaAnalyticsItemList(finished, ts, MediaAnalyticsItem::kKeyAny);
- return list;
-}
-
-List<MediaAnalyticsItem *> *MediaAnalyticsService::getMediaAnalyticsItemList(bool , nsecs_t , MediaAnalyticsItem::Key ) {
-
- // XXX: implement the get-item-list semantics
-
- List<MediaAnalyticsItem *> *list = NULL;
- // set up our query on the persistent data
- // slurp in all of the pieces
- // return that
- return list;
-}
-
status_t MediaAnalyticsService::dump(int fd, const Vector<String16>& args)
{
const size_t SIZE = 512;
@@ -277,15 +334,21 @@
// crack any parameters
bool clear = false;
+ bool summary = false;
nsecs_t ts_since = 0;
+ String16 summaryOption("-summary");
String16 clearOption("-clear");
String16 sinceOption("-since");
String16 helpOption("-help");
+ String16 onlyOption("-only");
+ const char *only = NULL;
int n = args.size();
for (int i = 0; i < n; i++) {
String8 myarg(args[i]);
if (args[i] == clearOption) {
clear = true;
+ } else if (args[i] == summaryOption) {
+ summary = true;
} else if (args[i] == sinceOption) {
i++;
if (i < n) {
@@ -301,12 +364,27 @@
}
// command line is milliseconds; internal units are nano-seconds
ts_since *= 1000*1000;
+ } else if (args[i] == onlyOption) {
+ i++;
+ if (i < n) {
+ String8 value(args[i]);
+ const char *p = value.string();
+ char *q = strdup(p);
+ if (q != NULL) {
+ if (only != NULL) {
+ free((void*)only);
+ }
+ only = q;
+ }
+ }
} else if (args[i] == helpOption) {
result.append("Recognized parameters:\n");
result.append("-help this help message\n");
+ result.append("-summary show summary info\n");
result.append("-clear clears out saved records\n");
- result.append("-since XXX include records since XXX\n");
- result.append(" (XXX is milliseconds since the UNIX epoch)\n");
+ result.append("-only X process records for component X\n");
+ result.append("-since X include records since X\n");
+ result.append(" (X is milliseconds since the UNIX epoch)\n");
write(fd, result.string(), result.size());
return NO_ERROR;
}
@@ -314,9 +392,42 @@
Mutex::Autolock _l(mLock);
- snprintf(buffer, SIZE, "Dump of the mediametrics process:\n");
+ // we ALWAYS dump this piece
+ snprintf(buffer, SIZE, "Dump of the %s process:\n", kServiceName);
result.append(buffer);
+ dumpHeaders(result, ts_since);
+
+ // only want 1, to avoid confusing folks that parse the output
+ if (summary) {
+ dumpSummaries(result, ts_since, only);
+ } else {
+ dumpRecent(result, ts_since, only);
+ }
+
+
+ if (clear) {
+ // remove everything from the finalized queue
+ while (mFinalized->size() > 0) {
+ MediaAnalyticsItem * oitem = *(mFinalized->begin());
+ mFinalized->erase(mFinalized->begin());
+ delete oitem;
+ mItemsDiscarded++;
+ }
+
+ // shall we clear the summary data too?
+
+ }
+
+ write(fd, result.string(), result.size());
+ return NO_ERROR;
+}
+
+// dump headers
+void MediaAnalyticsService::dumpHeaders(String8 &result, nsecs_t ts_since) {
+ const size_t SIZE = 512;
+ char buffer[SIZE];
+
int enabled = MediaAnalyticsItem::isEnabled();
if (enabled) {
snprintf(buffer, SIZE, "Metrics gathering: enabled\n");
@@ -331,50 +442,71 @@
" Discarded: %" PRId64 "\n",
mItemsSubmitted, mItemsFinalized, mItemsDiscarded);
result.append(buffer);
+ snprintf(buffer, SIZE,
+ "Summary Sets Discarded: %" PRId64 "\n", mSetsDiscarded);
+ result.append(buffer);
if (ts_since != 0) {
snprintf(buffer, SIZE,
"Dumping Queue entries more recent than: %" PRId64 "\n",
(int64_t) ts_since);
result.append(buffer);
}
+}
+
+// dump summary info
+void MediaAnalyticsService::dumpSummaries(String8 &result, nsecs_t ts_since, const char *only) {
+ const size_t SIZE = 512;
+ char buffer[SIZE];
+ int slot = 0;
+
+ snprintf(buffer, SIZE, "\nSummarized Metrics:\n");
+ result.append(buffer);
+
+ // have each of the distillers dump records
+ if (mSummarizerSets != NULL) {
+ List<SummarizerSet *>::iterator itSet = mSummarizerSets->begin();
+ for (; itSet != mSummarizerSets->end(); itSet++) {
+ nsecs_t when = (*itSet)->getStarted();
+ if (when < ts_since) {
+ continue;
+ }
+ List<MetricsSummarizer *> *list = (*itSet)->getSummarizers();
+ List<MetricsSummarizer *>::iterator it = list->begin();
+ for (; it != list->end(); it++) {
+ if (only != NULL && strcmp(only, (*it)->getKey()) != 0) {
+ ALOGV("Told to omit '%s'", (*it)->getKey());
+ }
+ AString distilled = (*it)->dumpSummary(slot, only);
+ result.append(distilled.c_str());
+ }
+ }
+ }
+}
+
+// the recent, detailed queues
+void MediaAnalyticsService::dumpRecent(String8 &result, nsecs_t ts_since, const char * only) {
+ const size_t SIZE = 512;
+ char buffer[SIZE];
// show the recently recorded records
snprintf(buffer, sizeof(buffer), "\nFinalized Metrics (oldest first):\n");
result.append(buffer);
- result.append(this->dumpQueue(mFinalized, ts_since));
+ result.append(this->dumpQueue(mFinalized, ts_since, only));
snprintf(buffer, sizeof(buffer), "\nIn-Progress Metrics (newest first):\n");
result.append(buffer);
- result.append(this->dumpQueue(mOpen, ts_since));
+ result.append(this->dumpQueue(mOpen, ts_since, only));
// show who is connected and injecting records?
// talk about # records fed to the 'readers'
// talk about # records we discarded, perhaps "discarded w/o reading" too
-
- if (clear) {
- // remove everything from the finalized queue
- while (mFinalized->size() > 0) {
- MediaAnalyticsItem * oitem = *(mFinalized->begin());
- if (DEBUG_QUEUE) {
- ALOGD("zap old record: key %s sessionID %" PRId64 " ts %" PRId64 "",
- oitem->getKey().c_str(), oitem->getSessionID(),
- oitem->getTimestamp());
- }
- mFinalized->erase(mFinalized->begin());
- mItemsDiscarded++;
- }
- }
-
- write(fd, result.string(), result.size());
- return NO_ERROR;
}
-
// caller has locked mLock...
String8 MediaAnalyticsService::dumpQueue(List<MediaAnalyticsItem *> *theList) {
- return dumpQueue(theList, (nsecs_t) 0);
+ return dumpQueue(theList, (nsecs_t) 0, NULL);
}
-String8 MediaAnalyticsService::dumpQueue(List<MediaAnalyticsItem *> *theList, nsecs_t ts_since) {
+String8 MediaAnalyticsService::dumpQueue(List<MediaAnalyticsItem *> *theList, nsecs_t ts_since, const char * only) {
String8 result;
int slot = 0;
@@ -387,6 +519,11 @@
if (when < ts_since) {
continue;
}
+ if (only != NULL &&
+ strcmp(only, (*it)->getKey().c_str()) != 0) {
+ ALOGV("Omit '%s', it's not '%s'", (*it)->getKey().c_str(), only);
+ continue;
+ }
AString entry = (*it)->toString();
result.appendFormat("%5d: %s\n", slot, entry.c_str());
slot++;
@@ -405,13 +542,6 @@
Mutex::Autolock _l(mLock);
- if (DEBUG_QUEUE) {
- ALOGD("Inject a record: session %" PRId64 " ts %" PRId64 "",
- item->getSessionID(), item->getTimestamp());
- String8 before = dumpQueue(l);
- ALOGD("Q before insert: %s", before.string());
- }
-
// adding at back of queue (fifo order)
if (front) {
l->push_front(item);
@@ -419,30 +549,15 @@
l->push_back(item);
}
- if (DEBUG_QUEUE) {
- String8 after = dumpQueue(l);
- ALOGD("Q after insert: %s", after.string());
- }
-
// keep removing old records the front until we're in-bounds
if (mMaxRecords > 0) {
while (l->size() > (size_t) mMaxRecords) {
MediaAnalyticsItem * oitem = *(l->begin());
- if (DEBUG_QUEUE) {
- ALOGD("zap old record: key %s sessionID %" PRId64 " ts %" PRId64 "",
- oitem->getKey().c_str(), oitem->getSessionID(),
- oitem->getTimestamp());
- }
l->erase(l->begin());
delete oitem;
mItemsDiscarded++;
}
}
-
- if (DEBUG_QUEUE) {
- String8 after = dumpQueue(l);
- ALOGD("Q after cleanup: %s", after.string());
- }
}
// are they alike enough that nitem can be folded into oitem?
@@ -515,29 +630,14 @@
Mutex::Autolock _l(mLock);
- if(DEBUG_QUEUE) {
- String8 before = dumpQueue(l);
- ALOGD("Q before delete: %s", before.string());
- }
-
for (List<MediaAnalyticsItem *>::iterator it = l->begin();
it != l->end(); it++) {
if ((*it)->getSessionID() != item->getSessionID())
continue;
-
- if (DEBUG_QUEUE) {
- ALOGD(" --- removing record for SessionID %" PRId64 "", item->getSessionID());
- ALOGD("drop record at %s:%d", __FILE__, __LINE__);
- }
delete *it;
l->erase(it);
break;
}
-
- if (DEBUG_QUEUE) {
- String8 after = dumpQueue(l);
- ALOGD("Q after delete: %s", after.string());
- }
}
static AString allowedKeys[] =
@@ -579,5 +679,43 @@
return false;
}
+// insert into the appropriate summarizer.
+// we make our own copy to save/summarize
+void MediaAnalyticsService::summarize(MediaAnalyticsItem *item) {
+
+ ALOGV("MediaAnalyticsService::summarize()");
+
+ if (item == NULL) {
+ return;
+ }
+
+ nsecs_t now = systemTime(SYSTEM_TIME_REALTIME);
+ if (mCurrentSet == NULL
+ || (mCurrentSet->getStarted() + mNewSetInterval < now)) {
+ newSummarizerSet();
+ }
+
+ if (mCurrentSet == NULL) {
+ return;
+ }
+
+ List<MetricsSummarizer *> *summarizers = mCurrentSet->getSummarizers();
+ List<MetricsSummarizer *>::iterator it = summarizers->begin();
+ for (; it != summarizers->end(); it++) {
+ if ((*it)->isMine(*item)) {
+ break;
+ }
+ }
+ if (it == summarizers->end()) {
+ ALOGD("no handler for type %s", item->getKey().c_str());
+ return; // no handler
+ }
+
+ // invoke the summarizer. summarizer will make whatever copies
+ // it wants; the caller retains ownership of item.
+
+ (*it)->handleRecord(item);
+
+}
} // namespace android
diff --git a/services/mediaanalytics/MediaAnalyticsService.h b/services/mediaanalytics/MediaAnalyticsService.h
index d2b0f09..6685967 100644
--- a/services/mediaanalytics/MediaAnalyticsService.h
+++ b/services/mediaanalytics/MediaAnalyticsService.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,6 +28,8 @@
#include <media/IMediaAnalyticsService.h>
+#include "MetricsSummarizer.h"
+
namespace android {
@@ -39,12 +41,6 @@
// on this side, caller surrenders ownership
virtual int64_t submit(MediaAnalyticsItem *item, bool forcenew);
- virtual List<MediaAnalyticsItem *>
- *getMediaAnalyticsItemList(bool finished, int64_t ts);
- virtual List<MediaAnalyticsItem *>
- *getMediaAnalyticsItemList(bool finished, int64_t ts, MediaAnalyticsItem::Key key);
-
-
static void instantiate();
virtual status_t dump(int fd, const Vector<String16>& args);
@@ -58,6 +54,7 @@
int64_t mItemsSubmitted;
int64_t mItemsFinalized;
int64_t mItemsDiscarded;
+ int64_t mSetsDiscarded;
MediaAnalyticsItem::SessionID_t mLastSessionID;
// partitioned a bit so we don't over serialize
@@ -67,6 +64,10 @@
// the most we hold in memory
// up to this many in each queue (open, finalized)
int32_t mMaxRecords;
+ // # of sets of summaries
+ int32_t mMaxRecordSets;
+ // nsecs until we start a new record set
+ nsecs_t mNewSetInterval;
// input validation after arrival from client
bool contentValid(MediaAnalyticsItem *item, bool isTrusted);
@@ -82,12 +83,47 @@
MediaAnalyticsItem *findItem(List<MediaAnalyticsItem *> *,
MediaAnalyticsItem *, bool removeit);
+ // summarizers
+ void summarize(MediaAnalyticsItem *item);
+ class SummarizerSet {
+ nsecs_t mStarted;
+ List<MetricsSummarizer *> *mSummarizers;
+
+ public:
+ void appendSummarizer(MetricsSummarizer *s) {
+ if (s) {
+ mSummarizers->push_back(s);
+ }
+ };
+ nsecs_t getStarted() { return mStarted;}
+ void setStarted(nsecs_t started) {mStarted = started;}
+ List<MetricsSummarizer *> *getSummarizers() { return mSummarizers;}
+
+ SummarizerSet();
+ ~SummarizerSet();
+ };
+ void newSummarizerSet();
+ List<SummarizerSet *> *mSummarizerSets;
+ SummarizerSet *mCurrentSet;
+ List<MetricsSummarizer *> *getFirstSet() {
+ List<SummarizerSet *>::iterator first = mSummarizerSets->begin();
+ if (first != mSummarizerSets->end()) {
+ return (*first)->getSummarizers();
+ }
+ return NULL;
+ }
+
void saveItem(MediaAnalyticsItem);
void saveItem(List<MediaAnalyticsItem *> *, MediaAnalyticsItem *, int);
void deleteItem(List<MediaAnalyticsItem *> *, MediaAnalyticsItem *);
+ // support for generating output
String8 dumpQueue(List<MediaAnalyticsItem*> *);
- String8 dumpQueue(List<MediaAnalyticsItem*> *, nsecs_t);
+ String8 dumpQueue(List<MediaAnalyticsItem*> *, nsecs_t, const char *only);
+
+ void dumpHeaders(String8 &result, nsecs_t ts_since);
+ void dumpSummaries(String8 &result, nsecs_t ts_since, const char * only);
+ void dumpRecent(String8 &result, nsecs_t ts_since, const char * only);
};
diff --git a/services/mediaanalytics/MetricsSummarizer.cpp b/services/mediaanalytics/MetricsSummarizer.cpp
new file mode 100644
index 0000000..6d5787e
--- /dev/null
+++ b/services/mediaanalytics/MetricsSummarizer.cpp
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MetricsSummarizer"
+#include <utils/Log.h>
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
+
+#include <utils/threads.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/List.h>
+
+#include <media/IMediaAnalyticsService.h>
+
+#include "MetricsSummarizer.h"
+
+
+namespace android {
+
+#define DEBUG_SORT 0
+#define DEBUG_QUEUE 0
+
+
+MetricsSummarizer::MetricsSummarizer(const char *key)
+ : mIgnorables(NULL)
+{
+ ALOGV("MetricsSummarizer::MetricsSummarizer");
+
+ if (key == NULL) {
+ mKey = key;
+ } else {
+ mKey = strdup(key);
+ }
+
+ mSummaries = new List<MediaAnalyticsItem *>();
+}
+
+MetricsSummarizer::~MetricsSummarizer()
+{
+ ALOGV("MetricsSummarizer::~MetricsSummarizer");
+ if (mKey) {
+ free((void *)mKey);
+ mKey = NULL;
+ }
+
+ // clear the list of items we have saved
+ while (mSummaries->size() > 0) {
+ MediaAnalyticsItem * oitem = *(mSummaries->begin());
+ if (DEBUG_QUEUE) {
+ ALOGD("zap old record: key %s sessionID %" PRId64 " ts %" PRId64 "",
+ oitem->getKey().c_str(), oitem->getSessionID(),
+ oitem->getTimestamp());
+ }
+ mSummaries->erase(mSummaries->begin());
+ delete oitem;
+ }
+}
+
+// so we know what summarizer we were using
+const char *MetricsSummarizer::getKey() {
+ const char *value = mKey;
+ if (value == NULL) {
+ value = "unknown";
+ }
+ return value;
+}
+
+// should the record be given to this summarizer
+bool MetricsSummarizer::isMine(MediaAnalyticsItem &item)
+{
+ if (mKey == NULL)
+ return true;
+ AString itemKey = item.getKey();
+ if (strcmp(mKey, itemKey.c_str()) != 0) {
+ return false;
+ }
+ return true;
+}
+
+AString MetricsSummarizer::dumpSummary(int &slot)
+{
+ return dumpSummary(slot, NULL);
+}
+
+AString MetricsSummarizer::dumpSummary(int &slot, const char *only)
+{
+ AString value = "";
+
+ List<MediaAnalyticsItem *>::iterator it = mSummaries->begin();
+ if (it != mSummaries->end()) {
+ char buf[16]; // enough for "#####: "
+ for (; it != mSummaries->end(); it++) {
+ if (only != NULL && strcmp(only, (*it)->getKey().c_str()) != 0) {
+ continue;
+ }
+ AString entry = (*it)->toString();
+ snprintf(buf, sizeof(buf), "%5d: ", slot);
+ value.append(buf);
+ value.append(entry.c_str());
+ value.append("\n");
+ slot++;
+ }
+ }
+ return value;
+}
+
+void MetricsSummarizer::setIgnorables(const char **ignorables) {
+ mIgnorables = ignorables;
+}
+
+const char **MetricsSummarizer::getIgnorables() {
+ return mIgnorables;
+}
+
+void MetricsSummarizer::handleRecord(MediaAnalyticsItem *item) {
+
+ ALOGV("MetricsSummarizer::handleRecord() for %s",
+ item == NULL ? "<nothing>" : item->toString().c_str());
+
+ if (item == NULL) {
+ return;
+ }
+
+ List<MediaAnalyticsItem *>::iterator it = mSummaries->begin();
+ for (; it != mSummaries->end(); it++) {
+ bool good = sameAttributes((*it), item, getIgnorables());
+ ALOGV("Match against %s says %d",
+ (*it)->toString().c_str(), good);
+ if (good)
+ break;
+ }
+ if (it == mSummaries->end()) {
+ ALOGV("save new record");
+ item = item->dup();
+ if (item == NULL) {
+ ALOGE("unable to save MediaMetrics record");
+ }
+ sortProps(item);
+ item->setInt32("aggregated",1);
+ mSummaries->push_back(item);
+ } else {
+ ALOGV("increment existing record");
+ (*it)->addInt32("aggregated",1);
+ mergeRecord(*(*it), *item);
+ }
+}
+
+void MetricsSummarizer::mergeRecord(MediaAnalyticsItem &/*have*/, MediaAnalyticsItem &/*item*/) {
+ // default is no further massaging.
+ ALOGV("MetricsSummarizer::mergeRecord() [default]");
+ return;
+}
+
+
+//
+// Comparators
+//
+
+// testing that all of 'single' is in 'summ'
+// and that the values match.
+// 'summ' may have extra fields.
+// 'ignorable' is a set of things that we don't worry about matching up
+// (usually time- or count-based values we'll sum elsewhere)
+bool MetricsSummarizer::sameAttributes(MediaAnalyticsItem *summ, MediaAnalyticsItem *single, const char **ignorable) {
+
+ if (single == NULL || summ == NULL) {
+ return false;
+ }
+ ALOGV("MetricsSummarizer::sameAttributes(): summ %s", summ->toString().c_str());
+ ALOGV("MetricsSummarizer::sameAttributes(): single %s", single->toString().c_str());
+
+ // this can be made better.
+ for(size_t i=0;i<single->mPropCount;i++) {
+ MediaAnalyticsItem::Prop *prop1 = &(single->mProps[i]);
+ const char *attrName = prop1->mName;
+ ALOGV("compare on attr '%s'", attrName);
+
+ // is it something we should ignore
+ if (ignorable != NULL) {
+ const char **ig = ignorable;
+ while (*ig) {
+ if (strcmp(*ig, attrName) == 0) {
+ break;
+ }
+ ig++;
+ }
+ if (*ig) {
+ ALOGV("we don't mind that it has attr '%s'", attrName);
+ continue;
+ }
+ }
+
+ MediaAnalyticsItem::Prop *prop2 = summ->findProp(attrName);
+ if (prop2 == NULL) {
+ ALOGV("summ doesn't have this attr");
+ return false;
+ }
+ if (prop1->mType != prop2->mType) {
+ ALOGV("mismatched attr types");
+ return false;
+ }
+ switch (prop1->mType) {
+ case MediaAnalyticsItem::kTypeInt32:
+ if (prop1->u.int32Value != prop2->u.int32Value)
+ return false;
+ break;
+ case MediaAnalyticsItem::kTypeInt64:
+ if (prop1->u.int64Value != prop2->u.int64Value)
+ return false;
+ break;
+ case MediaAnalyticsItem::kTypeDouble:
+ // XXX: watch out for floating point comparisons!
+ if (prop1->u.doubleValue != prop2->u.doubleValue)
+ return false;
+ break;
+ case MediaAnalyticsItem::kTypeCString:
+ if (strcmp(prop1->u.CStringValue, prop2->u.CStringValue) != 0)
+ return false;
+ break;
+ case MediaAnalyticsItem::kTypeRate:
+ if (prop1->u.rate.count != prop2->u.rate.count)
+ return false;
+ if (prop1->u.rate.duration != prop2->u.rate.duration)
+ return false;
+ break;
+ default:
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool MetricsSummarizer::sameAttributesId(MediaAnalyticsItem *summ, MediaAnalyticsItem *single, const char **ignorable) {
+
+ // verify same user
+ if (summ->mPid != single->mPid)
+ return false;
+
+ // and finally do the more expensive validation of the attributes
+ return sameAttributes(summ, single, ignorable);
+}
+
+int MetricsSummarizer::PropSorter(const void *a, const void *b) {
+ MediaAnalyticsItem::Prop *ai = (MediaAnalyticsItem::Prop *)a;
+ MediaAnalyticsItem::Prop *bi = (MediaAnalyticsItem::Prop *)b;
+ return strcmp(ai->mName, bi->mName);
+}
+
+// we sort in the summaries so that it looks pretty in the dumpsys
+void MetricsSummarizer::sortProps(MediaAnalyticsItem *item) {
+ if (item->mPropCount != 0) {
+ if (DEBUG_SORT) {
+ ALOGD("sortProps(pre): %s", item->toString().c_str());
+ }
+ qsort(item->mProps, item->mPropCount,
+ sizeof(MediaAnalyticsItem::Prop), MetricsSummarizer::PropSorter);
+ if (DEBUG_SORT) {
+ ALOGD("sortProps(pst): %s", item->toString().c_str());
+ }
+ }
+}
+
+} // namespace android
diff --git a/services/mediaanalytics/MetricsSummarizer.h b/services/mediaanalytics/MetricsSummarizer.h
new file mode 100644
index 0000000..0b64eac
--- /dev/null
+++ b/services/mediaanalytics/MetricsSummarizer.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_METRICSSUMMARIZER_H
+#define ANDROID_METRICSSUMMARIZER_H
+
+#include <utils/threads.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/List.h>
+
+#include <media/IMediaAnalyticsService.h>
+
+
+namespace android {
+
+class MetricsSummarizer
+{
+
+ public:
+
+ MetricsSummarizer(const char *key);
+ virtual ~MetricsSummarizer();
+
+ // show the key
+ const char * getKey();
+
+ // should the record be given to this summarizer
+ bool isMine(MediaAnalyticsItem &item);
+
+ // hand the record to this summarizer
+ void handleRecord(MediaAnalyticsItem *item);
+
+ virtual void mergeRecord(MediaAnalyticsItem &have, MediaAnalyticsItem &incoming);
+
+ // dump the summarized records (for dumpsys)
+ AString dumpSummary(int &slot);
+ AString dumpSummary(int &slot, const char *only);
+
+ void setIgnorables(const char **);
+ const char **getIgnorables();
+
+ protected:
+
+ // various comparators
+ // "do these records have same attributes and values in those attrs"
+ // ditto, but watch for "error" fields
+ bool sameAttributes(MediaAnalyticsItem *summ, MediaAnalyticsItem *single, const char **ignoreables);
+ // attributes + from the same app/userid
+ bool sameAttributesId(MediaAnalyticsItem *summ, MediaAnalyticsItem *single, const char **ignoreables);
+
+ static int PropSorter(const void *a, const void *b);
+ void sortProps(MediaAnalyticsItem *item);
+
+ private:
+ const char *mKey;
+ const char **mIgnorables;
+ List<MediaAnalyticsItem *> *mSummaries;
+
+
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_METRICSSUMMARIZER_H
diff --git a/services/mediaanalytics/MetricsSummarizerCodec.cpp b/services/mediaanalytics/MetricsSummarizerCodec.cpp
new file mode 100644
index 0000000..8c74782
--- /dev/null
+++ b/services/mediaanalytics/MetricsSummarizerCodec.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MetricsSummarizerCodec"
+#include <utils/Log.h>
+
+
+#include <utils/threads.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/List.h>
+
+#include <media/IMediaAnalyticsService.h>
+
+#include "MetricsSummarizer.h"
+#include "MetricsSummarizerCodec.h"
+
+
+
+
+namespace android {
+
+MetricsSummarizerCodec::MetricsSummarizerCodec(const char *key)
+ : MetricsSummarizer(key)
+{
+ ALOGV("MetricsSummarizerCodec::MetricsSummarizerCodec");
+}
+
+
+} // namespace android
diff --git a/services/mediaanalytics/MetricsSummarizerCodec.h b/services/mediaanalytics/MetricsSummarizerCodec.h
new file mode 100644
index 0000000..c01196f
--- /dev/null
+++ b/services/mediaanalytics/MetricsSummarizerCodec.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_METRICSSUMMARIZERCODEC_H
+#define ANDROID_METRICSSUMMARIZERCODEC_H
+
+#include <utils/threads.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/List.h>
+
+#include <media/IMediaAnalyticsService.h>
+#include "MetricsSummarizer.h"
+
+
+namespace android {
+
+class MetricsSummarizerCodec : public MetricsSummarizer
+{
+
+ public:
+
+ MetricsSummarizerCodec(const char *key);
+ virtual ~MetricsSummarizerCodec() {};
+
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_METRICSSUMMARIZERCODEC_H
diff --git a/services/mediaanalytics/MetricsSummarizerExtractor.cpp b/services/mediaanalytics/MetricsSummarizerExtractor.cpp
new file mode 100644
index 0000000..190f87d
--- /dev/null
+++ b/services/mediaanalytics/MetricsSummarizerExtractor.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MetricsSummarizerExtractor"
+#include <utils/Log.h>
+
+#include <utils/threads.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/List.h>
+
+#include <media/IMediaAnalyticsService.h>
+
+#include "MetricsSummarizer.h"
+#include "MetricsSummarizerExtractor.h"
+
+
+
+
+namespace android {
+
+MetricsSummarizerExtractor::MetricsSummarizerExtractor(const char *key)
+ : MetricsSummarizer(key)
+{
+ ALOGV("MetricsSummarizerExtractor::MetricsSummarizerExtractor");
+}
+
+} // namespace android
diff --git a/services/mediaanalytics/MetricsSummarizerExtractor.h b/services/mediaanalytics/MetricsSummarizerExtractor.h
new file mode 100644
index 0000000..eee052b
--- /dev/null
+++ b/services/mediaanalytics/MetricsSummarizerExtractor.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_METRICSSUMMARIZEREXTRACTOR_H
+#define ANDROID_METRICSSUMMARIZEREXTRACTOR_H
+
+#include <utils/threads.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/List.h>
+
+#include <media/IMediaAnalyticsService.h>
+#include "MetricsSummarizer.h"
+
+
+namespace android {
+
+class MetricsSummarizerExtractor : public MetricsSummarizer
+{
+
+ public:
+
+ MetricsSummarizerExtractor(const char *key);
+ virtual ~MetricsSummarizerExtractor() {};
+
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_METRICSSUMMARIZEREXTRACTOR_H
diff --git a/services/mediaanalytics/MetricsSummarizerPlayer.cpp b/services/mediaanalytics/MetricsSummarizerPlayer.cpp
new file mode 100644
index 0000000..5162059
--- /dev/null
+++ b/services/mediaanalytics/MetricsSummarizerPlayer.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MetricsSummarizerPlayer"
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <inttypes.h>
+
+#include <utils/threads.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/List.h>
+
+#include <media/IMediaAnalyticsService.h>
+
+#include "MetricsSummarizer.h"
+#include "MetricsSummarizerPlayer.h"
+
+
+
+
+namespace android {
+
+static const char *player_ignorable[] = {
+ "android.media.mediaplayer.durationMs",
+ "android.media.mediaplayer.playingMs",
+ "android.media.mediaplayer.frames",
+ "android.media.mediaplayer.dropped",
+ 0
+};
+
+MetricsSummarizerPlayer::MetricsSummarizerPlayer(const char *key)
+ : MetricsSummarizer(key)
+{
+ ALOGV("MetricsSummarizerPlayer::MetricsSummarizerPlayer");
+ setIgnorables(player_ignorable);
+}
+
+void MetricsSummarizerPlayer::mergeRecord(MediaAnalyticsItem &summation, MediaAnalyticsItem &item) {
+
+ ALOGV("MetricsSummarizerPlayer::mergeRecord()");
+
+ //
+ // we sum time & frames.
+ // be careful about our special "-1" values that indicate 'unknown'
+ // treat those as 0 [basically, not summing them into the totals].
+ int64_t duration = 0;
+ if (item.getInt64("android.media.mediaplayer.durationMs", &duration)) {
+ ALOGV("found durationMs of %" PRId64, duration);
+ summation.addInt64("android.media.mediaplayer.durationMs",duration);
+ }
+ int64_t playing = 0;
+ if (item.getInt64("android.media.mediaplayer.playingMs", &playing))
+ ALOGV("found playingMs of %" PRId64, playing);
+ if (playing >= 0) {
+ summation.addInt64("android.media.mediaplayer.playingMs",playing);
+ }
+ int64_t frames = 0;
+ if (item.getInt64("android.media.mediaplayer.frames", &frames))
+ ALOGV("found framess of %" PRId64, frames);
+ if (frames >= 0) {
+ summation.addInt64("android.media.mediaplayer.frames",frames);
+ }
+ int64_t dropped = 0;
+ if (item.getInt64("android.media.mediaplayer.dropped", &dropped))
+ ALOGV("found dropped of %" PRId64, dropped);
+ if (dropped >= 0) {
+ summation.addInt64("android.media.mediaplayer.dropped",dropped);
+ }
+}
+
+} // namespace android
diff --git a/services/mediaanalytics/MetricsSummarizerPlayer.h b/services/mediaanalytics/MetricsSummarizerPlayer.h
new file mode 100644
index 0000000..ad1bf74
--- /dev/null
+++ b/services/mediaanalytics/MetricsSummarizerPlayer.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_METRICSSUMMARIZERPLAYER_H
+#define ANDROID_METRICSSUMMARIZERPLAYER_H
+
+#include <utils/threads.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/List.h>
+
+#include <media/IMediaAnalyticsService.h>
+#include "MetricsSummarizer.h"
+
+
+namespace android {
+
+class MetricsSummarizerPlayer : public MetricsSummarizer
+{
+
+ public:
+
+ MetricsSummarizerPlayer(const char *key);
+ virtual ~MetricsSummarizerPlayer() {};
+
+ virtual void mergeRecord(MediaAnalyticsItem &have, MediaAnalyticsItem &incoming);
+
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_METRICSSUMMARIZERPLAYER_H
diff --git a/services/mediaanalytics/MetricsSummarizerRecorder.cpp b/services/mediaanalytics/MetricsSummarizerRecorder.cpp
new file mode 100644
index 0000000..c2919c3
--- /dev/null
+++ b/services/mediaanalytics/MetricsSummarizerRecorder.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MetricsSummarizerRecorder"
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <inttypes.h>
+
+#include <utils/threads.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/List.h>
+
+#include <media/IMediaAnalyticsService.h>
+
+#include "MetricsSummarizer.h"
+#include "MetricsSummarizerRecorder.h"
+
+
+
+
+namespace android {
+
+MetricsSummarizerRecorder::MetricsSummarizerRecorder(const char *key)
+ : MetricsSummarizer(key)
+{
+ ALOGV("MetricsSummarizerRecorder::MetricsSummarizerRecorder");
+}
+
+} // namespace android
diff --git a/services/mediaanalytics/MetricsSummarizerRecorder.h b/services/mediaanalytics/MetricsSummarizerRecorder.h
new file mode 100644
index 0000000..963baab
--- /dev/null
+++ b/services/mediaanalytics/MetricsSummarizerRecorder.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_METRICSSUMMARIZERRECORDER_H
+#define ANDROID_METRICSSUMMARIZERRECORDER_H
+
+#include <utils/threads.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/List.h>
+
+#include <media/IMediaAnalyticsService.h>
+#include "MetricsSummarizer.h"
+
+
+namespace android {
+
+class MetricsSummarizerRecorder : public MetricsSummarizer
+{
+
+ public:
+
+ MetricsSummarizerRecorder(const char *key);
+ virtual ~MetricsSummarizerRecorder() {};
+
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_METRICSSUMMARIZERRECORDER_H
diff --git a/services/mediacodec/main_codecservice.cpp b/services/mediacodec/main_codecservice.cpp
index c4e4cff..c59944a 100644
--- a/services/mediacodec/main_codecservice.cpp
+++ b/services/mediacodec/main_codecservice.cpp
@@ -31,9 +31,9 @@
#include "MediaCodecService.h"
#include "minijail.h"
-#include <android/hardware/media/omx/1.0/IOmx.h>
#include <hidl/HidlTransportSupport.h>
#include <omx/1.0/Omx.h>
+#include <omx/1.0/OmxStore.h>
using namespace android;
@@ -61,17 +61,23 @@
if (treble) {
using namespace ::android::hardware::media::omx::V1_0;
+ sp<IOmxStore> omxStore = new implementation::OmxStore();
+ if (omxStore == nullptr) {
+ LOG(ERROR) << "Cannot create IOmxStore HAL service.";
+ } else if (omxStore->registerAsService() != OK) {
+ LOG(ERROR) << "Cannot register IOmxStore HAL service.";
+ }
sp<IOmx> omx = new implementation::Omx();
if (omx == nullptr) {
- LOG(ERROR) << "Cannot create a Treble IOmx service.";
+ LOG(ERROR) << "Cannot create IOmx HAL service.";
} else if (omx->registerAsService() != OK) {
- LOG(ERROR) << "Cannot register a Treble IOmx service.";
+ LOG(ERROR) << "Cannot register IOmx HAL service.";
} else {
- LOG(INFO) << "Treble IOmx service created.";
+ LOG(INFO) << "Treble OMX service created.";
}
} else {
MediaCodecService::instantiate();
- LOG(INFO) << "Non-Treble IOMX service created.";
+ LOG(INFO) << "Non-Treble OMX service created.";
}
ProcessState::self()->startThreadPool();
diff --git a/services/mediacodec/seccomp_policy/mediacodec-arm.policy b/services/mediacodec/seccomp_policy/mediacodec-arm.policy
index 890d777..b8a5e90 100644
--- a/services/mediacodec/seccomp_policy/mediacodec-arm.policy
+++ b/services/mediacodec/seccomp_policy/mediacodec-arm.policy
@@ -29,6 +29,7 @@
setpriority: 1
getuid32: 1
fstat64: 1
+fstatfs64: 1
pread64: 1
faccessat: 1
readlinkat: 1
diff --git a/services/mediadrm/Android.mk b/services/mediadrm/Android.mk
index 1d5fa07..fa3a02b 100644
--- a/services/mediadrm/Android.mk
+++ b/services/mediadrm/Android.mk
@@ -43,8 +43,9 @@
# TODO: Some legacy DRM plugins only support 32-bit. They need to be migrated to
# 64-bit. (b/18948909) Once all of a device's legacy DRM plugins support 64-bit,
-# that device can turn on ENABLE_MEDIADRM_64 to build this service as 64-bit.
-ifneq ($(ENABLE_MEDIADRM_64), true)
+# that device can turn on TARGET_ENABLE_MEDIADRM_64 to build this service as
+# 64-bit.
+ifneq ($(TARGET_ENABLE_MEDIADRM_64), true)
LOCAL_32_BIT_ONLY := true
endif
diff --git a/services/mediaextractor/seccomp_policy/mediaextractor-arm.policy b/services/mediaextractor/seccomp_policy/mediaextractor-arm.policy
index 4e4ce30..7e8af1a 100644
--- a/services/mediaextractor/seccomp_policy/mediaextractor-arm.policy
+++ b/services/mediaextractor/seccomp_policy/mediaextractor-arm.policy
@@ -20,6 +20,7 @@
lseek: 1
writev: 1
fstatat64: 1
+fstatfs64: 1
fstat64: 1
restart_syscall: 1
exit: 1
diff --git a/services/mediaextractor/seccomp_policy/mediaextractor-arm64.policy b/services/mediaextractor/seccomp_policy/mediaextractor-arm64.policy
index 1683adb..aa8be5b 100644
--- a/services/mediaextractor/seccomp_policy/mediaextractor-arm64.policy
+++ b/services/mediaextractor/seccomp_policy/mediaextractor-arm64.policy
@@ -14,6 +14,7 @@
madvise: 1
getuid: 1
fstat: 1
+fstatfs: 1
read: 1
setpriority: 1
sigaltstack: 1
diff --git a/services/mediaextractor/seccomp_policy/mediaextractor-x86.policy b/services/mediaextractor/seccomp_policy/mediaextractor-x86.policy
index 83725cd..b5a6503 100644
--- a/services/mediaextractor/seccomp_policy/mediaextractor-x86.policy
+++ b/services/mediaextractor/seccomp_policy/mediaextractor-x86.policy
@@ -22,6 +22,7 @@
setpriority: 1
sigaltstack: 1
fstatat64: 1
+fstatfs64: 1
fstat64: 1
restart_syscall: 1
exit: 1
diff --git a/services/oboeservice/AAudioEndpointManager.cpp b/services/oboeservice/AAudioEndpointManager.cpp
index 84fa227..65b17bc 100644
--- a/services/oboeservice/AAudioEndpointManager.cpp
+++ b/services/oboeservice/AAudioEndpointManager.cpp
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+#define LOG_TAG "AAudioService"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
#include <assert.h>
#include <map>
#include <mutex>
@@ -28,13 +32,18 @@
ANDROID_SINGLETON_STATIC_INSTANCE(AAudioEndpointManager);
AAudioEndpointManager::AAudioEndpointManager()
- : Singleton<AAudioEndpointManager>() {
+ : Singleton<AAudioEndpointManager>()
+ , mInputs()
+ , mOutputs() {
}
-AAudioServiceEndpoint *AAudioEndpointManager::findEndpoint(AAudioService &audioService, int32_t deviceId,
+AAudioServiceEndpoint *AAudioEndpointManager::openEndpoint(AAudioService &audioService, int32_t deviceId,
aaudio_direction_t direction) {
AAudioServiceEndpoint *endpoint = nullptr;
std::lock_guard<std::mutex> lock(mLock);
+
+ // Try to find an existing endpoint.
+ ALOGD("AAudioEndpointManager::openEndpoint(), device = %d, dir = %d", deviceId, direction);
switch (direction) {
case AAUDIO_DIRECTION_INPUT:
endpoint = mInputs[deviceId];
@@ -48,11 +57,11 @@
}
// If we can't find an existing one then open one.
- ALOGD("AAudioEndpointManager::findEndpoint(), found %p", endpoint);
+ ALOGD("AAudioEndpointManager::openEndpoint(), found %p", endpoint);
if (endpoint == nullptr) {
endpoint = new AAudioServiceEndpoint(audioService);
if (endpoint->open(deviceId, direction) != AAUDIO_OK) {
- ALOGD("AAudioEndpointManager::findEndpoint(), open failed");
+ ALOGE("AAudioEndpointManager::findEndpoint(), open failed");
delete endpoint;
endpoint = nullptr;
} else {
@@ -66,22 +75,37 @@
}
}
}
+
+ if (endpoint != nullptr) {
+ // Increment the reference count under this lock.
+ endpoint->setReferenceCount(endpoint->getReferenceCount() + 1);
+ }
+
return endpoint;
}
-// FIXME add reference counter for serviceEndpoints and removed on last use.
-
-void AAudioEndpointManager::removeEndpoint(AAudioServiceEndpoint *serviceEndpoint) {
- aaudio_direction_t direction = serviceEndpoint->getDirection();
- int32_t deviceId = serviceEndpoint->getDeviceId();
-
+void AAudioEndpointManager::closeEndpoint(AAudioServiceEndpoint *serviceEndpoint) {
std::lock_guard<std::mutex> lock(mLock);
- switch(direction) {
- case AAUDIO_DIRECTION_INPUT:
- mInputs.erase(deviceId);
- break;
- case AAUDIO_DIRECTION_OUTPUT:
- mOutputs.erase(deviceId);
- break;
+ if (serviceEndpoint == nullptr) {
+ return;
}
-}
\ No newline at end of file
+
+ // Decrement the reference count under this lock.
+ int32_t newRefCount = serviceEndpoint->getReferenceCount() - 1;
+ serviceEndpoint->setReferenceCount(newRefCount);
+ if (newRefCount <= 0) {
+ aaudio_direction_t direction = serviceEndpoint->getDirection();
+ int32_t deviceId = serviceEndpoint->getDeviceId();
+
+ switch (direction) {
+ case AAUDIO_DIRECTION_INPUT:
+ mInputs.erase(deviceId);
+ break;
+ case AAUDIO_DIRECTION_OUTPUT:
+ mOutputs.erase(deviceId);
+ break;
+ }
+ serviceEndpoint->close();
+ delete serviceEndpoint;
+ }
+}
diff --git a/services/oboeservice/AAudioEndpointManager.h b/services/oboeservice/AAudioEndpointManager.h
index 48b27f0..bbcfc1d 100644
--- a/services/oboeservice/AAudioEndpointManager.h
+++ b/services/oboeservice/AAudioEndpointManager.h
@@ -39,11 +39,11 @@
* @param direction
* @return endpoint or nullptr
*/
- AAudioServiceEndpoint *findEndpoint(android::AAudioService &audioService,
+ AAudioServiceEndpoint *openEndpoint(android::AAudioService &audioService,
int32_t deviceId,
aaudio_direction_t direction);
- void removeEndpoint(AAudioServiceEndpoint *serviceEndpoint);
+ void closeEndpoint(AAudioServiceEndpoint *serviceEndpoint);
private:
diff --git a/services/oboeservice/AAudioMixer.cpp b/services/oboeservice/AAudioMixer.cpp
index 70da339..43203d4 100644
--- a/services/oboeservice/AAudioMixer.cpp
+++ b/services/oboeservice/AAudioMixer.cpp
@@ -41,7 +41,7 @@
memset(mOutputBuffer, 0, mBufferSizeInBytes);
}
-void AAudioMixer::mix(FifoBuffer *fifo, float volume) {
+bool AAudioMixer::mix(FifoBuffer *fifo, float volume) {
WrappingBuffer wrappingBuffer;
float *destination = mOutputBuffer;
fifo_frames_t framesLeft = mFramesPerBurst;
@@ -67,9 +67,10 @@
}
fifo->getFifoControllerBase()->advanceReadIndex(mFramesPerBurst - framesLeft);
if (framesLeft > 0) {
- ALOGW("AAudioMixer::mix() UNDERFLOW by %d / %d frames ----- UNDERFLOW !!!!!!!!!!",
- framesLeft, mFramesPerBurst);
+ //ALOGW("AAudioMixer::mix() UNDERFLOW by %d / %d frames ----- UNDERFLOW !!!!!!!!!!",
+ // framesLeft, mFramesPerBurst);
}
+ return (framesLeft > 0); // did not get all the frames we needed, ie. "underflow"
}
void AAudioMixer::mixPart(float *destination, float *source, int32_t numFrames, float volume) {
diff --git a/services/oboeservice/AAudioMixer.h b/services/oboeservice/AAudioMixer.h
index 2191183..9155fec 100644
--- a/services/oboeservice/AAudioMixer.h
+++ b/services/oboeservice/AAudioMixer.h
@@ -31,7 +31,13 @@
void clear();
- void mix(android::FifoBuffer *fifo, float volume);
+ /**
+ * Mix from this FIFO
+ * @param fifo
+ * @param volume
+ * @return true if underflowed
+ */
+ bool mix(android::FifoBuffer *fifo, float volume);
void mixPart(float *destination, float *source, int32_t numFrames, float volume);
diff --git a/services/oboeservice/AAudioService.cpp b/services/oboeservice/AAudioService.cpp
index 723ef63..816d5ab 100644
--- a/services/oboeservice/AAudioService.cpp
+++ b/services/oboeservice/AAudioService.cpp
@@ -54,8 +54,8 @@
aaudio_result_t result = AAUDIO_OK;
AAudioServiceStreamBase *serviceStream = nullptr;
const AAudioStreamConfiguration &configurationInput = request.getConstantConfiguration();
+ bool sharingModeMatchRequired = request.isSharingModeMatchRequired();
aaudio_sharing_mode_t sharingMode = configurationInput.getSharingMode();
- ALOGE("AAudioService::openStream(): sharingMode = %d", sharingMode);
if (sharingMode != AAUDIO_SHARING_MODE_EXCLUSIVE && sharingMode != AAUDIO_SHARING_MODE_SHARED) {
ALOGE("AAudioService::openStream(): unrecognized sharing mode = %d", sharingMode);
@@ -77,8 +77,9 @@
}
// if SHARED requested or if EXCLUSIVE failed
- if (serviceStream == nullptr) {
- ALOGD("AAudioService::openStream(), sharingMode = AAUDIO_SHARING_MODE_SHARED");
+ if (sharingMode == AAUDIO_SHARING_MODE_SHARED
+ || (serviceStream == nullptr && !sharingModeMatchRequired)) {
+ ALOGD("AAudioService::openStream(), try AAUDIO_SHARING_MODE_SHARED");
serviceStream = new AAudioServiceStreamShared(*this);
result = serviceStream->open(request, configurationOutput);
configurationOutput.setSharingMode(AAUDIO_SHARING_MODE_SHARED);
@@ -126,9 +127,7 @@
ALOGE("AAudioService::getStreamDescription(), illegal stream handle = 0x%0x", streamHandle);
return AAUDIO_ERROR_INVALID_HANDLE;
}
- ALOGD("AAudioService::getStreamDescription(), handle = 0x%08x", streamHandle);
aaudio_result_t result = serviceStream->getDescription(parcelable);
- ALOGD("AAudioService::getStreamDescription(), result = %d", result);
// parcelable.dump();
return result;
}
@@ -140,7 +139,6 @@
return AAUDIO_ERROR_INVALID_HANDLE;
}
aaudio_result_t result = serviceStream->start();
- ALOGD("AAudioService::startStream(), serviceStream->start() returned %d", result);
return result;
}
@@ -154,6 +152,16 @@
return result;
}
+aaudio_result_t AAudioService::stopStream(aaudio_handle_t streamHandle) {
+ AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
+ if (serviceStream == nullptr) {
+ ALOGE("AAudioService::pauseStream(), illegal stream handle = 0x%0x", streamHandle);
+ return AAUDIO_ERROR_INVALID_HANDLE;
+ }
+ aaudio_result_t result = serviceStream->stop();
+ return result;
+}
+
aaudio_result_t AAudioService::flushStream(aaudio_handle_t streamHandle) {
AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
if (serviceStream == nullptr) {
@@ -168,7 +176,6 @@
pid_t clientThreadId,
int64_t periodNanoseconds) {
AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
- ALOGD("AAudioService::registerAudioThread(), serviceStream = %p", serviceStream);
if (serviceStream == nullptr) {
ALOGE("AAudioService::registerAudioThread(), illegal stream handle = 0x%0x", streamHandle);
return AAUDIO_ERROR_INVALID_HANDLE;
@@ -193,7 +200,6 @@
pid_t clientProcessId,
pid_t clientThreadId) {
AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
- ALOGI("AAudioService::unregisterAudioThread(), serviceStream = %p", serviceStream);
if (serviceStream == nullptr) {
ALOGE("AAudioService::unregisterAudioThread(), illegal stream handle = 0x%0x",
streamHandle);
diff --git a/services/oboeservice/AAudioService.h b/services/oboeservice/AAudioService.h
index 5a7a2b6..f5a7d2f 100644
--- a/services/oboeservice/AAudioService.h
+++ b/services/oboeservice/AAudioService.h
@@ -57,6 +57,8 @@
virtual aaudio_result_t pauseStream(aaudio_handle_t streamHandle);
+ virtual aaudio_result_t stopStream(aaudio_handle_t streamHandle);
+
virtual aaudio_result_t flushStream(aaudio_handle_t streamHandle);
virtual aaudio_result_t registerAudioThread(aaudio_handle_t streamHandle,
diff --git a/services/oboeservice/AAudioServiceEndpoint.cpp b/services/oboeservice/AAudioServiceEndpoint.cpp
index 80551c9..d3e182a 100644
--- a/services/oboeservice/AAudioServiceEndpoint.cpp
+++ b/services/oboeservice/AAudioServiceEndpoint.cpp
@@ -14,6 +14,17 @@
* limitations under the License.
*/
+#define LOG_TAG "AAudioService"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <assert.h>
+#include <map>
+#include <mutex>
+#include <utils/Singleton.h>
+
+#include "AAudioEndpointManager.h"
+#include "AAudioServiceEndpoint.h"
#include <algorithm>
#include <mutex>
#include <vector>
@@ -30,6 +41,13 @@
// Wait at least this many times longer than the operation should take.
#define MIN_TIMEOUT_OPERATIONS 4
+// This is the maximum size in frames. The effective size can be tuned smaller at runtime.
+#define DEFAULT_BUFFER_CAPACITY (48 * 8)
+
+// Use 2 for "double buffered"
+#define BUFFER_SIZE_IN_BURSTS 2
+#define BURSTS_PER_MIX_LOOP 1
+
// The mStreamInternal will use a service interface that does not go through Binder.
AAudioServiceEndpoint::AAudioServiceEndpoint(AAudioService &audioService)
: mStreamInternal(audioService, true)
@@ -43,11 +61,18 @@
aaudio_result_t AAudioServiceEndpoint::open(int32_t deviceId, aaudio_direction_t direction) {
AudioStreamBuilder builder;
builder.setSharingMode(AAUDIO_SHARING_MODE_EXCLUSIVE);
+ // Don't fall back to SHARED because that would cause recursion.
+ builder.setSharingModeMatchRequired(true);
builder.setDeviceId(deviceId);
builder.setDirection(direction);
+ builder.setBufferCapacity(DEFAULT_BUFFER_CAPACITY);
+
aaudio_result_t result = mStreamInternal.open(builder);
if (result == AAUDIO_OK) {
mMixer.allocate(mStreamInternal.getSamplesPerFrame(), mStreamInternal.getFramesPerBurst());
+
+ int32_t desiredBufferSize = BUFFER_SIZE_IN_BURSTS * mStreamInternal.getFramesPerBurst();
+ mStreamInternal.setBufferSize(desiredBufferSize);
}
return result;
}
@@ -58,15 +83,12 @@
// TODO, maybe use an interface to reduce exposure
aaudio_result_t AAudioServiceEndpoint::registerStream(AAudioServiceStreamShared *sharedStream) {
- ALOGD("AAudioServiceEndpoint::registerStream(%p)", sharedStream);
- // TODO use real-time technique to avoid mutex, eg. atomic command FIFO
std::lock_guard<std::mutex> lock(mLockStreams);
mRegisteredStreams.push_back(sharedStream);
return AAUDIO_OK;
}
aaudio_result_t AAudioServiceEndpoint::unregisterStream(AAudioServiceStreamShared *sharedStream) {
- ALOGD("AAudioServiceEndpoint::unregisterStream(%p)", sharedStream);
std::lock_guard<std::mutex> lock(mLockStreams);
mRegisteredStreams.erase(std::remove(mRegisteredStreams.begin(), mRegisteredStreams.end(), sharedStream),
mRegisteredStreams.end());
@@ -75,7 +97,6 @@
aaudio_result_t AAudioServiceEndpoint::startStream(AAudioServiceStreamShared *sharedStream) {
// TODO use real-time technique to avoid mutex, eg. atomic command FIFO
- ALOGD("AAudioServiceEndpoint(): startStream() entering");
std::lock_guard<std::mutex> lock(mLockStreams);
mRunningStreams.push_back(sharedStream);
if (mRunningStreams.size() == 1) {
@@ -106,13 +127,10 @@
// Render audio in the application callback and then write the data to the stream.
void *AAudioServiceEndpoint::callbackLoop() {
- aaudio_result_t result = AAUDIO_OK;
-
ALOGD("AAudioServiceEndpoint(): callbackLoop() entering");
+ int32_t underflowCount = 0;
- result = mStreamInternal.requestStart();
- ALOGD("AAudioServiceEndpoint(): callbackLoop() after requestStart() %d, isPlaying() = %d",
- result, (int) mStreamInternal.isPlaying());
+ aaudio_result_t result = mStreamInternal.requestStart();
// result might be a frame count
while (mCallbackEnabled.load() && mStreamInternal.isPlaying() && (result >= 0)) {
@@ -123,12 +141,14 @@
for(AAudioServiceStreamShared *sharedStream : mRunningStreams) {
FifoBuffer *fifo = sharedStream->getDataFifoBuffer();
float volume = 0.5; // TODO get from system
- mMixer.mix(fifo, volume);
+ bool underflowed = mMixer.mix(fifo, volume);
+ underflowCount += underflowed ? 1 : 0;
+ // TODO log underflows in each stream
+ sharedStream->markTransferTime(AudioClock::getNanoseconds());
}
}
// Write audio data to stream using a blocking write.
- ALOGD("AAudioServiceEndpoint(): callbackLoop() write(%d)", getFramesPerBurst());
int64_t timeoutNanos = calculateReasonableTimeout(mStreamInternal.getFramesPerBurst());
result = mStreamInternal.write(mMixer.getOutputBuffer(), getFramesPerBurst(), timeoutNanos);
if (result == AAUDIO_ERROR_DISCONNECTED) {
@@ -141,11 +161,9 @@
}
}
- ALOGD("AAudioServiceEndpoint(): callbackLoop() exiting, result = %d, isPlaying() = %d",
- result, (int) mStreamInternal.isPlaying());
-
result = mStreamInternal.requestStop();
+ ALOGD("AAudioServiceEndpoint(): callbackLoop() exiting, %d underflows", underflowCount);
return NULL; // TODO review
}
diff --git a/services/oboeservice/AAudioServiceEndpoint.h b/services/oboeservice/AAudioServiceEndpoint.h
index 020d38a..a4ceae6 100644
--- a/services/oboeservice/AAudioServiceEndpoint.h
+++ b/services/oboeservice/AAudioServiceEndpoint.h
@@ -56,6 +56,16 @@
void *callbackLoop();
+ // This should only be called from the AAudioEndpointManager under a mutex.
+ int32_t getReferenceCount() const {
+ return mReferenceCount;
+ }
+
+ // This should only be called from the AAudioEndpointManager under a mutex.
+ void setReferenceCount(int32_t count) {
+ mReferenceCount = count;
+ }
+
private:
aaudio_result_t startMixer_l();
aaudio_result_t stopMixer_l();
@@ -64,13 +74,14 @@
AudioStreamInternal mStreamInternal;
AAudioMixer mMixer;
- AAudioServiceStreamMMAP mStreamMMAP;
std::atomic<bool> mCallbackEnabled;
+ int32_t mReferenceCount = 0;
std::mutex mLockStreams;
std::vector<AAudioServiceStreamShared *> mRegisteredStreams;
std::vector<AAudioServiceStreamShared *> mRunningStreams;
+
};
} /* namespace aaudio */
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index b15043d..8248f8b 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -59,10 +59,12 @@
std::lock_guard<std::mutex> lock(mLockUpMessageQueue);
delete mUpMessageQueue;
mUpMessageQueue = nullptr;
+
return AAUDIO_OK;
}
aaudio_result_t AAudioServiceStreamBase::start() {
+ ALOGD("AAudioServiceStreamBase::start() send AAUDIO_SERVICE_EVENT_STARTED");
sendServiceEvent(AAUDIO_SERVICE_EVENT_STARTED);
mState = AAUDIO_STREAM_STATE_STARTED;
mThreadEnabled.store(true);
@@ -78,14 +80,37 @@
processError();
return result;
}
+ ALOGD("AAudioServiceStreamBase::pause() send AAUDIO_SERVICE_EVENT_PAUSED");
sendServiceEvent(AAUDIO_SERVICE_EVENT_PAUSED);
mState = AAUDIO_STREAM_STATE_PAUSED;
return result;
}
+aaudio_result_t AAudioServiceStreamBase::stop() {
+ // TODO wait for data to be played out
+ sendCurrentTimestamp();
+ mThreadEnabled.store(false);
+ aaudio_result_t result = mAAudioThread.stop();
+ if (result != AAUDIO_OK) {
+ processError();
+ return result;
+ }
+ ALOGD("AAudioServiceStreamBase::stop() send AAUDIO_SERVICE_EVENT_STOPPED");
+ sendServiceEvent(AAUDIO_SERVICE_EVENT_STOPPED);
+ mState = AAUDIO_STREAM_STATE_STOPPED;
+ return result;
+}
+
+aaudio_result_t AAudioServiceStreamBase::flush() {
+ ALOGD("AAudioServiceStreamBase::flush() send AAUDIO_SERVICE_EVENT_FLUSHED");
+ sendServiceEvent(AAUDIO_SERVICE_EVENT_FLUSHED);
+ mState = AAUDIO_STREAM_STATE_FLUSHED;
+ return AAUDIO_OK;
+}
+
// implement Runnable
void AAudioServiceStreamBase::run() {
- ALOGD("AAudioServiceStreamMMAP::run() entering ----------------");
+ ALOGD("AAudioServiceStreamBase::run() entering ----------------");
TimestampScheduler timestampScheduler;
timestampScheduler.setBurstPeriod(mFramesPerBurst, mSampleRate);
timestampScheduler.start(AudioClock::getNanoseconds());
@@ -102,7 +127,7 @@
AudioClock::sleepUntilNanoTime(nextTime);
}
}
- ALOGD("AAudioServiceStreamMMAP::run() exiting ----------------");
+ ALOGD("AAudioServiceStreamBase::run() exiting ----------------");
}
void AAudioServiceStreamBase::processError() {
@@ -122,6 +147,10 @@
aaudio_result_t AAudioServiceStreamBase::writeUpMessageQueue(AAudioServiceMessage *command) {
std::lock_guard<std::mutex> lock(mLockUpMessageQueue);
+ if (mUpMessageQueue == nullptr) {
+ ALOGE("writeUpMessageQueue(): mUpMessageQueue null! - stream not open");
+ return AAUDIO_ERROR_NULL;
+ }
int32_t count = mUpMessageQueue->getFifoBuffer()->write(command, 1);
if (count != 1) {
ALOGE("writeUpMessageQueue(): Queue full. Did client die?");
@@ -133,9 +162,11 @@
aaudio_result_t AAudioServiceStreamBase::sendCurrentTimestamp() {
AAudioServiceMessage command;
+ //ALOGD("sendCurrentTimestamp() called");
aaudio_result_t result = getFreeRunningPosition(&command.timestamp.position,
&command.timestamp.timestamp);
if (result == AAUDIO_OK) {
+ //ALOGD("sendCurrentTimestamp(): position %d", (int) command.timestamp.position);
command.what = AAudioServiceMessage::code::TIMESTAMP;
result = writeUpMessageQueue(&command);
}
diff --git a/services/oboeservice/AAudioServiceStreamBase.h b/services/oboeservice/AAudioServiceStreamBase.h
index 91eec35..9318c2e 100644
--- a/services/oboeservice/AAudioServiceStreamBase.h
+++ b/services/oboeservice/AAudioServiceStreamBase.h
@@ -17,6 +17,7 @@
#ifndef AAUDIO_AAUDIO_SERVICE_STREAM_BASE_H
#define AAUDIO_AAUDIO_SERVICE_STREAM_BASE_H
+#include <assert.h>
#include <mutex>
#include "fifo/FifoBuffer.h"
@@ -60,17 +61,22 @@
/**
* Start the flow of data.
*/
- virtual aaudio_result_t start() = 0;
+ virtual aaudio_result_t start();
/**
* Stop the flow of data such that start() can resume with loss of data.
*/
- virtual aaudio_result_t pause() = 0;
+ virtual aaudio_result_t pause();
+
+ /**
+ * Stop the flow of data after data in buffer has played.
+ */
+ virtual aaudio_result_t stop();
/**
* Discard any data held by the underlying HAL or Service.
*/
- virtual aaudio_result_t flush() = 0;
+ virtual aaudio_result_t flush();
// -------------------------------------------------------------------
@@ -127,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 b70c625..cadc2a4 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.cpp
+++ b/services/oboeservice/AAudioServiceStreamMMAP.cpp
@@ -55,6 +55,18 @@
aaudio_result_t AAudioServiceStreamMMAP::close() {
ALOGD("AAudioServiceStreamMMAP::close() called, %p", mMmapStream.get());
mMmapStream.clear(); // TODO review. Is that all we have to do?
+ // Apparently the above close is asynchronous. An attempt to open a new device
+ // right after a close can fail. Also some callbacks may still be in flight!
+ // 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();
}
@@ -79,8 +91,8 @@
const AAudioStreamConfiguration &configurationInput = request.getConstantConfiguration();
audio_port_handle_t deviceId = configurationInput.getDeviceId();
- ALOGI("open request dump()");
- request.dump();
+ // ALOGI("open request dump()");
+ // request.dump();
mMmapClient.clientUid = request.getUserId();
mMmapClient.clientPid = request.getProcessId();
@@ -120,6 +132,9 @@
MmapStreamInterface::stream_direction_t streamDirection = (direction == AAUDIO_DIRECTION_OUTPUT)
? MmapStreamInterface::DIRECTION_OUTPUT : MmapStreamInterface::DIRECTION_INPUT;
+ ALOGD("AAudioServiceStreamMMAP::open() request devId = %d, sRate = %d",
+ deviceId, config.sample_rate);
+
// Open HAL stream.
status_t status = MmapStreamInterface::openMmapStream(streamDirection,
&attributes,
@@ -156,11 +171,16 @@
: 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);
mSampleRate = config.sample_rate;
+ ALOGD("AAudioServiceStreamMMAP::open() got devId = %d, sRate = %d",
+ deviceId, config.sample_rate);
+
// Fill in AAudioStreamConfiguration
configurationOutput.setSampleRate(mSampleRate);
configurationOutput.setSamplesPerFrame(mSamplesPerFrame);
@@ -198,16 +218,25 @@
return (result1 != AAUDIO_OK) ? result1 : result2;
}
+aaudio_result_t AAudioServiceStreamMMAP::stop() {
+ if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
+
+ aaudio_result_t result1 = AAudioServiceStreamBase::stop();
+ aaudio_result_t result2 = mMmapStream->stop(mPortHandle);
+ mFramesRead.reset32();
+ return (result1 != AAUDIO_OK) ? result1 : result2;
+}
+
/**
* Discard any data held by the underlying HAL or Service.
*/
aaudio_result_t AAudioServiceStreamMMAP::flush() {
if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
// TODO how do we flush an MMAP/NOIRQ buffer? sync pointers?
- ALOGD("AAudioServiceStreamMMAP::pause() send AAUDIO_SERVICE_EVENT_FLUSHED");
+ ALOGD("AAudioServiceStreamMMAP::flush() send AAUDIO_SERVICE_EVENT_FLUSHED");
sendServiceEvent(AAUDIO_SERVICE_EVENT_FLUSHED);
mState = AAUDIO_STREAM_STATE_FLUSHED;
- return AAUDIO_OK;
+ return AAudioServiceStreamBase::flush();;
}
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.h b/services/oboeservice/AAudioServiceStreamMMAP.h
index f121c5c..fe75a10 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.h
+++ b/services/oboeservice/AAudioServiceStreamMMAP.h
@@ -66,6 +66,8 @@
*/
aaudio_result_t pause() override;
+ aaudio_result_t stop() override;
+
/**
* Discard any data held by the underlying HAL or Service.
*
@@ -125,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 cd9336b..713d1f8 100644
--- a/services/oboeservice/AAudioServiceStreamShared.cpp
+++ b/services/oboeservice/AAudioServiceStreamShared.cpp
@@ -61,7 +61,7 @@
ALOGD("AAudioServiceStreamShared::open(), direction = %d", direction);
AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance();
- mServiceEndpoint = mEndpointManager.findEndpoint(mAudioService, deviceId, direction);
+ mServiceEndpoint = mEndpointManager.openEndpoint(mAudioService, deviceId, direction);
ALOGD("AAudioServiceStreamShared::open(), mServiceEndPoint = %p", mServiceEndpoint);
if (mServiceEndpoint == nullptr) {
return AAUDIO_ERROR_UNAVAILABLE;
@@ -72,6 +72,7 @@
if (mAudioFormat == AAUDIO_FORMAT_UNSPECIFIED) {
mAudioFormat = AAUDIO_FORMAT_PCM_FLOAT;
} else if (mAudioFormat != AAUDIO_FORMAT_PCM_FLOAT) {
+ ALOGE("AAudioServiceStreamShared::open(), mAudioFormat = %d, need FLOAT", mAudioFormat);
return AAUDIO_ERROR_INVALID_FORMAT;
}
@@ -79,6 +80,8 @@
if (mSampleRate == AAUDIO_FORMAT_UNSPECIFIED) {
mSampleRate = mServiceEndpoint->getSampleRate();
} else if (mSampleRate != mServiceEndpoint->getSampleRate()) {
+ ALOGE("AAudioServiceStreamShared::open(), mAudioFormat = %d, need %d",
+ mSampleRate, mServiceEndpoint->getSampleRate());
return AAUDIO_ERROR_INVALID_RATE;
}
@@ -86,17 +89,22 @@
if (mSamplesPerFrame == AAUDIO_FORMAT_UNSPECIFIED) {
mSamplesPerFrame = mServiceEndpoint->getSamplesPerFrame();
} else if (mSamplesPerFrame != mServiceEndpoint->getSamplesPerFrame()) {
+ ALOGE("AAudioServiceStreamShared::open(), mSamplesPerFrame = %d, need %d",
+ mSamplesPerFrame, mServiceEndpoint->getSamplesPerFrame());
return AAUDIO_ERROR_OUT_OF_RANGE;
}
// Determine this stream's shared memory buffer capacity.
mFramesPerBurst = mServiceEndpoint->getFramesPerBurst();
int32_t minCapacityFrames = configurationInput.getBufferCapacity();
- int32_t numBursts = (minCapacityFrames + mFramesPerBurst - 1) / mFramesPerBurst;
- if (numBursts < MIN_BURSTS_PER_BUFFER) {
- numBursts = MIN_BURSTS_PER_BUFFER;
- } else if (numBursts > MAX_BURSTS_PER_BUFFER) {
- numBursts = MAX_BURSTS_PER_BUFFER;
+ int32_t numBursts = MAX_BURSTS_PER_BUFFER;
+ if (minCapacityFrames != AAUDIO_UNSPECIFIED) {
+ numBursts = (minCapacityFrames + mFramesPerBurst - 1) / mFramesPerBurst;
+ if (numBursts < MIN_BURSTS_PER_BUFFER) {
+ numBursts = MIN_BURSTS_PER_BUFFER;
+ } else if (numBursts > MAX_BURSTS_PER_BUFFER) {
+ numBursts = MAX_BURSTS_PER_BUFFER;
+ }
}
mCapacityInFrames = numBursts * mFramesPerBurst;
ALOGD("AAudioServiceStreamShared::open(), mCapacityInFrames = %d", mCapacityInFrames);
@@ -122,8 +130,12 @@
* An AAUDIO_SERVICE_EVENT_STARTED will be sent to the client when complete.
*/
aaudio_result_t AAudioServiceStreamShared::start() {
+ AAudioServiceEndpoint *endpoint = mServiceEndpoint;
+ if (endpoint == nullptr) {
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
// Add this stream to the mixer.
- aaudio_result_t result = mServiceEndpoint->startStream(this);
+ aaudio_result_t result = endpoint->startStream(this);
if (result != AAUDIO_OK) {
ALOGE("AAudioServiceStreamShared::start() mServiceEndpoint returned %d", result);
processError();
@@ -139,15 +151,31 @@
* An AAUDIO_SERVICE_EVENT_PAUSED will be sent to the client when complete.
*/
aaudio_result_t AAudioServiceStreamShared::pause() {
+ AAudioServiceEndpoint *endpoint = mServiceEndpoint;
+ if (endpoint == nullptr) {
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
// Add this stream to the mixer.
- aaudio_result_t result = mServiceEndpoint->stopStream(this);
+ aaudio_result_t result = endpoint->stopStream(this);
+ if (result != AAUDIO_OK) {
+ ALOGE("AAudioServiceStreamShared::pause() mServiceEndpoint returned %d", result);
+ processError();
+ }
+ return AAudioServiceStreamBase::pause();
+}
+
+aaudio_result_t AAudioServiceStreamShared::stop() {
+ AAudioServiceEndpoint *endpoint = mServiceEndpoint;
+ if (endpoint == nullptr) {
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
+ // Add this stream to the mixer.
+ aaudio_result_t result = endpoint->stopStream(this);
if (result != AAUDIO_OK) {
ALOGE("AAudioServiceStreamShared::stop() mServiceEndpoint returned %d", result);
processError();
- } else {
- result = AAudioServiceStreamBase::start();
}
- return AAUDIO_OK;
+ return AAudioServiceStreamBase::stop();
}
/**
@@ -157,15 +185,25 @@
*/
aaudio_result_t AAudioServiceStreamShared::flush() {
// TODO make sure we are paused
- return AAUDIO_OK;
+ // TODO actually flush the data
+ return AAudioServiceStreamBase::flush() ;
}
aaudio_result_t AAudioServiceStreamShared::close() {
pause();
// TODO wait for pause() to synchronize
- mServiceEndpoint->unregisterStream(this);
- mServiceEndpoint->close();
- mServiceEndpoint = nullptr;
+ AAudioServiceEndpoint *endpoint = mServiceEndpoint;
+ if (endpoint != nullptr) {
+ endpoint->unregisterStream(this);
+
+ AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance();
+ mEndpointManager.closeEndpoint(endpoint);
+ mServiceEndpoint = nullptr;
+ }
+ if (mAudioDataQueue != nullptr) {
+ delete mAudioDataQueue;
+ mAudioDataQueue = nullptr;
+ }
return AAudioServiceStreamBase::close();
}
@@ -189,10 +227,15 @@
mServiceEndpoint = nullptr;
}
+void AAudioServiceStreamShared::markTransferTime(int64_t nanoseconds) {
+ mMarkedPosition = mAudioDataQueue->getFifoBuffer()->getReadCounter();
+ mMarkedTime = nanoseconds;
+}
aaudio_result_t AAudioServiceStreamShared::getFreeRunningPosition(int64_t *positionFrames,
int64_t *timeNanos) {
- *positionFrames = mAudioDataQueue->getFifoBuffer()->getReadCounter();
- *timeNanos = AudioClock::getNanoseconds();
+ // TODO get these two numbers as an atomic pair
+ *positionFrames = mMarkedPosition;
+ *timeNanos = mMarkedTime;
return AAUDIO_OK;
}
diff --git a/services/oboeservice/AAudioServiceStreamShared.h b/services/oboeservice/AAudioServiceStreamShared.h
index f6df7ce..b981387 100644
--- a/services/oboeservice/AAudioServiceStreamShared.h
+++ b/services/oboeservice/AAudioServiceStreamShared.h
@@ -66,6 +66,11 @@
aaudio_result_t pause() override;
/**
+ * Stop the flow of data after data in buffer has played.
+ */
+ aaudio_result_t stop() override;
+
+ /**
* Discard any data held by the underlying HAL or Service.
*
* This is not guaranteed to be synchronous but it currently is.
@@ -77,6 +82,11 @@
android::FifoBuffer *getDataFifoBuffer() { return mAudioDataQueue->getFifoBuffer(); }
+ /* Keep a record of when a buffer transfer completed.
+ * This allows for a more accurate timing model.
+ */
+ void markTransferTime(int64_t nanoseconds);
+
void onStop();
void onDisconnect();
@@ -91,6 +101,9 @@
android::AAudioService &mAudioService;
AAudioServiceEndpoint *mServiceEndpoint = nullptr;
SharedRingBuffer *mAudioDataQueue;
+
+ int64_t mMarkedPosition = 0;
+ int64_t mMarkedTime = 0;
};
} /* namespace aaudio */
diff --git a/services/oboeservice/Android.mk b/services/oboeservice/Android.mk
index a9c80ae..afb477e 100644
--- a/services/oboeservice/Android.mk
+++ b/services/oboeservice/Android.mk
@@ -48,8 +48,7 @@
libcutils \
libmediautils \
libutils \
- liblog \
- libtinyalsa
+ liblog
include $(BUILD_SHARED_LIBRARY)
diff --git a/services/oboeservice/SharedRingBuffer.cpp b/services/oboeservice/SharedRingBuffer.cpp
index efcc9d6..6b3fb4c 100644
--- a/services/oboeservice/SharedRingBuffer.cpp
+++ b/services/oboeservice/SharedRingBuffer.cpp
@@ -18,6 +18,8 @@
//#define LOG_NDEBUG 0
#include <utils/Log.h>
+#include <sys/mman.h>
+
#include "binding/RingBufferParcelable.h"
#include "binding/AudioEndpointParcelable.h"
@@ -31,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,
@@ -44,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);
@@ -73,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,