Merge "Remove VTS reference to non-shipping OMX/xaac plugin" into rvc-dev
diff --git a/media/codec2/components/vpx/C2SoftVpxEnc.cpp b/media/codec2/components/vpx/C2SoftVpxEnc.cpp
index ebc7a8f..74e105e 100644
--- a/media/codec2/components/vpx/C2SoftVpxEnc.cpp
+++ b/media/codec2/components/vpx/C2SoftVpxEnc.cpp
@@ -67,8 +67,9 @@
mLastTimestamp(0x7FFFFFFFFFFFFFFFull),
mSignalledOutputEos(false),
mSignalledError(false) {
- memset(mTemporalLayerBitrateRatio, 0, sizeof(mTemporalLayerBitrateRatio));
- mTemporalLayerBitrateRatio[0] = 100;
+ for (int i = 0; i < MAXTEMPORALLAYERS; i++) {
+ mTemporalLayerBitrateRatio[i] = 1.0f;
+ }
}
C2SoftVpxEnc::~C2SoftVpxEnc() {
@@ -123,7 +124,8 @@
mFrameRate = mIntf->getFrameRate_l();
mIntraRefresh = mIntf->getIntraRefresh_l();
mRequestSync = mIntf->getRequestSync_l();
- mTemporalLayers = mIntf->getTemporalLayers_l()->m.layerCount;
+ mLayering = mIntf->getTemporalLayers_l();
+ mTemporalLayers = mLayering->m.layerCount;
}
switch (mBitrateMode->value) {
@@ -225,6 +227,7 @@
mTemporalPattern[5] = kTemporalUpdateGoldenRefAltRef;
mTemporalPattern[6] = kTemporalUpdateLastRefAltRef;
mTemporalPattern[7] = kTemporalUpdateNone;
+ mTemporalLayerBitrateRatio[0] = mLayering->m.bitrateRatios[0];
mTemporalPatternLength = 8;
break;
case 3:
@@ -245,6 +248,8 @@
mTemporalPattern[5] = kTemporalUpdateNone;
mTemporalPattern[6] = kTemporalUpdateGoldenRefAltRef;
mTemporalPattern[7] = kTemporalUpdateNone;
+ mTemporalLayerBitrateRatio[0] = mLayering->m.bitrateRatios[0];
+ mTemporalLayerBitrateRatio[1] = mLayering->m.bitrateRatios[1];
mTemporalPatternLength = 8;
break;
default:
@@ -255,7 +260,7 @@
for (size_t i = 0; i < mCodecConfiguration->ts_number_layers; i++) {
mCodecConfiguration->ts_target_bitrate[i] =
mCodecConfiguration->rc_target_bitrate *
- mTemporalLayerBitrateRatio[i] / 100;
+ mTemporalLayerBitrateRatio[i];
}
if (mIntf->getSyncFramePeriod() >= 0) {
mCodecConfiguration->kf_max_dist = mIntf->getSyncFramePeriod();
diff --git a/media/codec2/components/vpx/C2SoftVpxEnc.h b/media/codec2/components/vpx/C2SoftVpxEnc.h
index 62ccd1b..5e34b8a 100644
--- a/media/codec2/components/vpx/C2SoftVpxEnc.h
+++ b/media/codec2/components/vpx/C2SoftVpxEnc.h
@@ -180,7 +180,7 @@
size_t mTemporalLayers;
// Temporal layer bitrare ratio in percentage
- uint32_t mTemporalLayerBitrateRatio[MAXTEMPORALLAYERS];
+ float_t mTemporalLayerBitrateRatio[MAXTEMPORALLAYERS];
// Temporal pattern type
TemporalPatternType mTemporalPatternType;
@@ -218,6 +218,7 @@
std::shared_ptr<C2StreamBitrateInfo::output> mBitrate;
std::shared_ptr<C2StreamBitrateModeTuning::output> mBitrateMode;
std::shared_ptr<C2StreamRequestSyncFrameTuning::output> mRequestSync;
+ std::shared_ptr<C2StreamTemporalLayeringTuning::output> mLayering;
C2_DO_NOT_COPY(C2SoftVpxEnc);
};
diff --git a/media/codec2/vndk/util/C2InterfaceUtils.cpp b/media/codec2/vndk/util/C2InterfaceUtils.cpp
index 61ec911..0c1729b 100644
--- a/media/codec2/vndk/util/C2InterfaceUtils.cpp
+++ b/media/codec2/vndk/util/C2InterfaceUtils.cpp
@@ -216,9 +216,14 @@
if (limit.contains(minMask) && contains(minMask)) {
values[0] = minMask;
// keep only flags that are covered by limit
- std::remove_if(values.begin(), values.end(), [&limit, minMask](const C2Value::Primitive &v) -> bool {
- T value = v.ref<ValueType>() | minMask;
- return value == minMask || !limit.contains(value); });
+ values.erase(std::remove_if(values.begin(), values.end(),
+ [&limit, minMask](
+ const C2Value::Primitive &v) -> bool {
+ T value = v.ref<ValueType>() | minMask;
+ return value == minMask ||
+ !limit.contains(value);
+ }),
+ values.end());
// we also need to do it vice versa
for (const C2Value::Primitive &v : _mValues) {
T value = v.ref<ValueType>() | minMask;
@@ -264,24 +269,33 @@
template<typename T>
C2SupportedValueSet<T> C2SupportedValueSet<T>::limitedTo(const C2SupportedValueSet<T> &limit) const {
std::vector<C2Value::Primitive> values = _mValues; // make a copy
- std::remove_if(values.begin(), values.end(), [&limit](const C2Value::Primitive &v) -> bool {
- return !limit.contains(v.ref<ValueType>()); });
+ values.erase(std::remove_if(values.begin(), values.end(),
+ [&limit](const C2Value::Primitive &v) -> bool {
+ return !limit.contains(v.ref<ValueType>());
+ }),
+ values.end());
return C2SupportedValueSet(std::move(values));
}
template<typename T>
C2SupportedValueSet<T> C2SupportedValueSet<T>::limitedTo(const C2SupportedRange<T> &limit) const {
std::vector<C2Value::Primitive> values = _mValues; // make a copy
- std::remove_if(values.begin(), values.end(), [&limit](const C2Value::Primitive &v) -> bool {
- return !limit.contains(v.ref<ValueType>()); });
+ values.erase(std::remove_if(values.begin(), values.end(),
+ [&limit](const C2Value::Primitive &v) -> bool {
+ return !limit.contains(v.ref<ValueType>());
+ }),
+ values.end());
return C2SupportedValueSet(std::move(values));
}
template<typename T>
C2SupportedValueSet<T> C2SupportedValueSet<T>::limitedTo(const C2SupportedFlags<T> &limit) const {
std::vector<C2Value::Primitive> values = _mValues; // make a copy
- std::remove_if(values.begin(), values.end(), [&limit](const C2Value::Primitive &v) -> bool {
- return !limit.contains(v.ref<ValueType>()); });
+ values.erase(std::remove_if(values.begin(), values.end(),
+ [&limit](const C2Value::Primitive &v) -> bool {
+ return !limit.contains(v.ref<ValueType>());
+ }),
+ values.end());
return C2SupportedValueSet(std::move(values));
}
diff --git a/media/libaaudio/src/client/AudioEndpoint.cpp b/media/libaaudio/src/client/AudioEndpoint.cpp
index 214f888..06f66d3 100644
--- a/media/libaaudio/src/client/AudioEndpoint.cpp
+++ b/media/libaaudio/src/client/AudioEndpoint.cpp
@@ -32,19 +32,12 @@
#define RIDICULOUSLY_LARGE_FRAME_SIZE 4096
AudioEndpoint::AudioEndpoint()
- : mUpCommandQueue(nullptr)
- , mDataQueue(nullptr)
- , mFreeRunning(false)
+ : mFreeRunning(false)
, mDataReadCounter(0)
, mDataWriteCounter(0)
{
}
-AudioEndpoint::~AudioEndpoint() {
- delete mDataQueue;
- delete mUpCommandQueue;
-}
-
// TODO Consider moving to a method in RingBufferDescriptor
static aaudio_result_t AudioEndpoint_validateQueueDescriptor(const char *type,
const RingBufferDescriptor *descriptor) {
@@ -144,7 +137,7 @@
return AAUDIO_ERROR_INTERNAL;
}
- mUpCommandQueue = new FifoBuffer(
+ mUpCommandQueue = std::make_unique<FifoBuffer>(
descriptor->bytesPerFrame,
descriptor->capacityInFrames,
descriptor->readCounterAddress,
@@ -173,7 +166,7 @@
? &mDataWriteCounter
: descriptor->writeCounterAddress;
- mDataQueue = new FifoBuffer(
+ mDataQueue = std::make_unique<FifoBuffer>(
descriptor->bytesPerFrame,
descriptor->capacityInFrames,
readCounterAddress,
@@ -194,18 +187,15 @@
return mDataQueue->getEmptyRoomAvailable(wrappingBuffer);
}
-int32_t AudioEndpoint::getEmptyFramesAvailable()
-{
+int32_t AudioEndpoint::getEmptyFramesAvailable() {
return mDataQueue->getEmptyFramesAvailable();
}
-int32_t AudioEndpoint::getFullFramesAvailable(WrappingBuffer *wrappingBuffer)
-{
+int32_t AudioEndpoint::getFullFramesAvailable(WrappingBuffer *wrappingBuffer) {
return mDataQueue->getFullDataAvailable(wrappingBuffer);
}
-int32_t AudioEndpoint::getFullFramesAvailable()
-{
+int32_t AudioEndpoint::getFullFramesAvailable() {
return mDataQueue->getFullFramesAvailable();
}
@@ -217,29 +207,24 @@
mDataQueue->advanceReadIndex(deltaFrames);
}
-void AudioEndpoint::setDataReadCounter(fifo_counter_t framesRead)
-{
+void AudioEndpoint::setDataReadCounter(fifo_counter_t framesRead) {
mDataQueue->setReadCounter(framesRead);
}
-fifo_counter_t AudioEndpoint::getDataReadCounter()
-{
+fifo_counter_t AudioEndpoint::getDataReadCounter() const {
return mDataQueue->getReadCounter();
}
-void AudioEndpoint::setDataWriteCounter(fifo_counter_t framesRead)
-{
+void AudioEndpoint::setDataWriteCounter(fifo_counter_t framesRead) {
mDataQueue->setWriteCounter(framesRead);
}
-fifo_counter_t AudioEndpoint::getDataWriteCounter()
-{
+fifo_counter_t AudioEndpoint::getDataWriteCounter() const {
return mDataQueue->getWriteCounter();
}
int32_t AudioEndpoint::setBufferSizeInFrames(int32_t requestedFrames,
- int32_t *actualFrames)
-{
+ int32_t *actualFrames) {
if (requestedFrames < ENDPOINT_DATA_QUEUE_SIZE_MIN) {
requestedFrames = ENDPOINT_DATA_QUEUE_SIZE_MIN;
}
@@ -248,19 +233,17 @@
return AAUDIO_OK;
}
-int32_t AudioEndpoint::getBufferSizeInFrames() const
-{
+int32_t AudioEndpoint::getBufferSizeInFrames() const {
return mDataQueue->getThreshold();
}
-int32_t AudioEndpoint::getBufferCapacityInFrames() const
-{
+int32_t AudioEndpoint::getBufferCapacityInFrames() const {
return (int32_t)mDataQueue->getBufferCapacityInFrames();
}
void AudioEndpoint::dump() const {
- ALOGD("data readCounter = %lld", (long long) mDataQueue->getReadCounter());
- ALOGD("data writeCounter = %lld", (long long) mDataQueue->getWriteCounter());
+ ALOGD("data readCounter = %lld", (long long) getDataReadCounter());
+ ALOGD("data writeCounter = %lld", (long long) getDataWriteCounter());
}
void AudioEndpoint::eraseDataMemory() {
diff --git a/media/libaaudio/src/client/AudioEndpoint.h b/media/libaaudio/src/client/AudioEndpoint.h
index f5b67e8..484d917 100644
--- a/media/libaaudio/src/client/AudioEndpoint.h
+++ b/media/libaaudio/src/client/AudioEndpoint.h
@@ -35,7 +35,6 @@
public:
AudioEndpoint();
- virtual ~AudioEndpoint();
/**
* Configure based on the EndPointDescriptor_t.
@@ -67,11 +66,11 @@
*/
void setDataReadCounter(android::fifo_counter_t framesRead);
- android::fifo_counter_t getDataReadCounter();
+ android::fifo_counter_t getDataReadCounter() const;
void setDataWriteCounter(android::fifo_counter_t framesWritten);
- android::fifo_counter_t getDataWriteCounter();
+ android::fifo_counter_t getDataWriteCounter() const;
/**
* The result is not valid until after configure() is called.
@@ -94,8 +93,8 @@
void dump() const;
private:
- android::FifoBuffer *mUpCommandQueue;
- android::FifoBuffer *mDataQueue;
+ std::unique_ptr<android::FifoBuffer> mUpCommandQueue;
+ std::unique_ptr<android::FifoBuffer> mDataQueue;
bool mFreeRunning;
android::fifo_counter_t mDataReadCounter; // only used if free-running
android::fifo_counter_t mDataWriteCounter; // only used if free-running
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 076c92d..f89cde7 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -58,7 +58,6 @@
AudioStreamInternal::AudioStreamInternal(AAudioServiceInterface &serviceInterface, bool inService)
: AudioStream()
, mClockModel()
- , mAudioEndpoint()
, mServiceStreamHandle(AAUDIO_HANDLE_INVALID)
, mInService(inService)
, mServiceInterface(serviceInterface)
@@ -74,7 +73,6 @@
aaudio_result_t AudioStreamInternal::open(const AudioStreamBuilder &builder) {
aaudio_result_t result = AAUDIO_OK;
- int32_t capacity;
int32_t framesPerBurst;
int32_t framesPerHardwareBurst;
AAudioStreamRequest request;
@@ -173,7 +171,8 @@
}
// Configure endpoint based on descriptor.
- result = mAudioEndpoint.configure(&mEndpointDescriptor, getDirection());
+ mAudioEndpoint = std::make_unique<AudioEndpoint>();
+ result = mAudioEndpoint->configure(&mEndpointDescriptor, getDirection());
if (result != AAUDIO_OK) {
goto error;
}
@@ -201,9 +200,10 @@
}
mFramesPerBurst = framesPerBurst; // only save good value
- capacity = mEndpointDescriptor.dataQueueDescriptor.capacityInFrames;
- if (capacity < mFramesPerBurst || capacity > MAX_BUFFER_CAPACITY_IN_FRAMES) {
- ALOGE("%s - bufferCapacity out of range = %d", __func__, capacity);
+ mBufferCapacityInFrames = mEndpointDescriptor.dataQueueDescriptor.capacityInFrames;
+ if (mBufferCapacityInFrames < mFramesPerBurst
+ || mBufferCapacityInFrames > MAX_BUFFER_CAPACITY_IN_FRAMES) {
+ ALOGE("%s - bufferCapacity out of range = %d", __func__, mBufferCapacityInFrames);
result = AAUDIO_ERROR_OUT_OF_RANGE;
goto error;
}
@@ -239,7 +239,7 @@
// You can use this offset to reduce glitching.
// You can also use this offset to force glitching. By iterating over multiple
// values you can reveal the distribution of the hardware timing jitter.
- if (mAudioEndpoint.isFreeRunning()) { // MMAP?
+ if (mAudioEndpoint->isFreeRunning()) { // MMAP?
int32_t offsetMicros = (getDirection() == AAUDIO_DIRECTION_OUTPUT)
? AAudioProperty_getOutputMMapOffsetMicros()
: AAudioProperty_getInputMMapOffsetMicros();
@@ -251,7 +251,7 @@
mTimeOffsetNanos = offsetMicros * AAUDIO_NANOS_PER_MICROSECOND;
}
- setBufferSize(capacity / 2); // Default buffer size to match Q
+ setBufferSize(mBufferCapacityInFrames / 2); // Default buffer size to match Q
setState(AAUDIO_STREAM_STATE_OPEN);
@@ -280,6 +280,11 @@
mServiceInterface.closeStream(serviceStreamHandle);
mCallbackBuffer.reset();
+
+ // Update local frame counters so we can query them after releasing the endpoint.
+ getFramesRead();
+ getFramesWritten();
+ mAudioEndpoint.reset();
result = mEndPointParcelable.close();
aaudio_result_t result2 = AudioStream::release_l();
return (result != AAUDIO_OK) ? result : result2;
@@ -538,7 +543,7 @@
case AAUDIO_SERVICE_EVENT_DISCONNECTED:
// Prevent hardware from looping on old data and making buzzing sounds.
if (getDirection() == AAUDIO_DIRECTION_OUTPUT) {
- mAudioEndpoint.eraseDataMemory();
+ mAudioEndpoint->eraseDataMemory();
}
result = AAUDIO_ERROR_DISCONNECTED;
setState(AAUDIO_STREAM_STATE_DISCONNECTED);
@@ -564,7 +569,10 @@
while (result == AAUDIO_OK) {
AAudioServiceMessage message;
- if (mAudioEndpoint.readUpCommand(&message) != 1) {
+ if (!mAudioEndpoint) {
+ break;
+ }
+ if (mAudioEndpoint->readUpCommand(&message) != 1) {
break; // no command this time, no problem
}
switch (message.what) {
@@ -592,7 +600,10 @@
while (result == AAUDIO_OK) {
AAudioServiceMessage message;
- if (mAudioEndpoint.readUpCommand(&message) != 1) {
+ if (!mAudioEndpoint) {
+ break;
+ }
+ if (mAudioEndpoint->readUpCommand(&message) != 1) {
break; // no command this time, no problem
}
switch (message.what) {
@@ -625,7 +636,7 @@
const char * fifoName = "aaRdy";
ATRACE_BEGIN(traceName);
if (ATRACE_ENABLED()) {
- int32_t fullFrames = mAudioEndpoint.getFullFramesAvailable();
+ int32_t fullFrames = mAudioEndpoint->getFullFramesAvailable();
ATRACE_INT(fifoName, fullFrames);
}
@@ -654,7 +665,7 @@
if (timeoutNanoseconds == 0) {
break; // don't block
} else if (wakeTimeNanos != 0) {
- if (!mAudioEndpoint.isFreeRunning()) {
+ if (!mAudioEndpoint->isFreeRunning()) {
// If there is software on the other end of the FIFO then it may get delayed.
// So wake up just a little after we expect it to be ready.
wakeTimeNanos += mWakeupDelayNanos;
@@ -679,12 +690,12 @@
ALOGW("processData(): past deadline by %d micros",
(int)((wakeTimeNanos - deadlineNanos) / AAUDIO_NANOS_PER_MICROSECOND));
mClockModel.dump();
- mAudioEndpoint.dump();
+ mAudioEndpoint->dump();
break;
}
if (ATRACE_ENABLED()) {
- int32_t fullFrames = mAudioEndpoint.getFullFramesAvailable();
+ int32_t fullFrames = mAudioEndpoint->getFullFramesAvailable();
ATRACE_INT(fifoName, fullFrames);
int64_t sleepForNanos = wakeTimeNanos - currentTimeNanos;
ATRACE_INT("aaSlpNs", (int32_t)sleepForNanos);
@@ -696,7 +707,7 @@
}
if (ATRACE_ENABLED()) {
- int32_t fullFrames = mAudioEndpoint.getFullFramesAvailable();
+ int32_t fullFrames = mAudioEndpoint->getFullFramesAvailable();
ATRACE_INT(fifoName, fullFrames);
}
@@ -730,11 +741,15 @@
adjustedFrames = std::min(maximumSize, adjustedFrames);
}
- // Clip against the actual size from the endpoint.
- int32_t actualFrames = 0;
- mAudioEndpoint.setBufferSizeInFrames(maximumSize, &actualFrames);
- // actualFrames should be <= actual maximum size of endpoint
- adjustedFrames = std::min(actualFrames, adjustedFrames);
+ if (mAudioEndpoint) {
+ // Clip against the actual size from the endpoint.
+ int32_t actualFrames = 0;
+ // Set to maximum size so we can write extra data when ready in order to reduce glitches.
+ // The amount we keep in the buffer is controlled by mBufferSizeInFrames.
+ mAudioEndpoint->setBufferSizeInFrames(maximumSize, &actualFrames);
+ // actualFrames should be <= actual maximum size of endpoint
+ adjustedFrames = std::min(actualFrames, adjustedFrames);
+ }
mBufferSizeInFrames = adjustedFrames;
ALOGV("%s(%d) returns %d", __func__, requestedFrames, adjustedFrames);
@@ -746,7 +761,7 @@
}
int32_t AudioStreamInternal::getBufferCapacity() const {
- return mAudioEndpoint.getBufferCapacityInFrames();
+ return mBufferCapacityInFrames;
}
int32_t AudioStreamInternal::getFramesPerBurst() const {
@@ -759,5 +774,5 @@
}
bool AudioStreamInternal::isClockModelInControl() const {
- return isActive() && mAudioEndpoint.isFreeRunning() && mClockModel.isRunning();
+ return isActive() && mAudioEndpoint->isFreeRunning() && mClockModel.isRunning();
}
diff --git a/media/libaaudio/src/client/AudioStreamInternal.h b/media/libaaudio/src/client/AudioStreamInternal.h
index 42f2889..61591b3 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.h
+++ b/media/libaaudio/src/client/AudioStreamInternal.h
@@ -155,7 +155,8 @@
IsochronousClockModel mClockModel; // timing model for chasing the HAL
- AudioEndpoint mAudioEndpoint; // source for reads or sink for writes
+ std::unique_ptr<AudioEndpoint> mAudioEndpoint; // source for reads or sink for writes
+
aaudio_handle_t mServiceStreamHandle; // opaque handle returned from service
int32_t mFramesPerBurst = MIN_FRAMES_PER_BURST; // frames per HAL transfer
@@ -178,6 +179,9 @@
float mStreamVolume = 1.0f;
+ int64_t mLastFramesWritten = 0;
+ int64_t mLastFramesRead = 0;
+
private:
/*
* Asynchronous write with data conversion.
@@ -207,6 +211,8 @@
int32_t mDeviceChannelCount = 0;
int32_t mBufferSizeInFrames = 0; // local threshold to control latency
+ int32_t mBufferCapacityInFrames = 0;
+
};
diff --git a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
index 32cf368..9fa2e40 100644
--- a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
@@ -42,8 +42,8 @@
AudioStreamInternalCapture::~AudioStreamInternalCapture() {}
void AudioStreamInternalCapture::advanceClientToMatchServerPosition() {
- int64_t readCounter = mAudioEndpoint.getDataReadCounter();
- int64_t writeCounter = mAudioEndpoint.getDataWriteCounter();
+ int64_t readCounter = mAudioEndpoint->getDataReadCounter();
+ int64_t writeCounter = mAudioEndpoint->getDataWriteCounter();
// Bump offset so caller does not see the retrograde motion in getFramesRead().
int64_t offset = readCounter - writeCounter;
@@ -53,7 +53,7 @@
// Force readCounter to match writeCounter.
// This is because we cannot change the write counter in the hardware.
- mAudioEndpoint.setDataReadCounter(writeCounter);
+ mAudioEndpoint->setDataReadCounter(writeCounter);
}
// Write the data, block if needed and timeoutMillis > 0
@@ -86,7 +86,7 @@
}
// If we have gotten this far then we have at least one timestamp from server.
- if (mAudioEndpoint.isFreeRunning()) {
+ if (mAudioEndpoint->isFreeRunning()) {
//ALOGD("AudioStreamInternalCapture::processDataNow() - update remote counter");
// Update data queue based on the timing model.
// Jitter in the DSP can cause late writes to the FIFO.
@@ -95,7 +95,7 @@
// that the DSP could have written the data.
int64_t estimatedRemoteCounter = mClockModel.convertLatestTimeToPosition(currentNanoTime);
// TODO refactor, maybe use setRemoteCounter()
- mAudioEndpoint.setDataWriteCounter(estimatedRemoteCounter);
+ mAudioEndpoint->setDataWriteCounter(estimatedRemoteCounter);
}
// This code assumes that we have already received valid timestamps.
@@ -108,8 +108,8 @@
// If the capture buffer is full beyond capacity then consider it an overrun.
// For shared streams, the xRunCount is passed up from the service.
- if (mAudioEndpoint.isFreeRunning()
- && mAudioEndpoint.getFullFramesAvailable() > mAudioEndpoint.getBufferCapacityInFrames()) {
+ if (mAudioEndpoint->isFreeRunning()
+ && mAudioEndpoint->getFullFramesAvailable() > mAudioEndpoint->getBufferCapacityInFrames()) {
mXRunCount++;
if (ATRACE_ENABLED()) {
ATRACE_INT("aaOverRuns", mXRunCount);
@@ -143,7 +143,7 @@
// Calculate frame position based off of the readCounter because
// the writeCounter might have just advanced in the background,
// causing us to sleep until a later burst.
- int64_t nextPosition = mAudioEndpoint.getDataReadCounter() + mFramesPerBurst;
+ int64_t nextPosition = mAudioEndpoint->getDataReadCounter() + mFramesPerBurst;
wakeTime = mClockModel.convertPositionToLatestTime(nextPosition);
}
break;
@@ -166,7 +166,7 @@
uint8_t *destination = (uint8_t *) buffer;
int32_t framesLeft = numFrames;
- mAudioEndpoint.getFullFramesAvailable(&wrappingBuffer);
+ mAudioEndpoint->getFullFramesAvailable(&wrappingBuffer);
// Read data in one or two parts.
for (int partIndex = 0; framesLeft > 0 && partIndex < WrappingBuffer::SIZE; partIndex++) {
@@ -208,26 +208,29 @@
}
int32_t framesProcessed = numFrames - framesLeft;
- mAudioEndpoint.advanceReadIndex(framesProcessed);
+ mAudioEndpoint->advanceReadIndex(framesProcessed);
//ALOGD("readNowWithConversion() returns %d", framesProcessed);
return framesProcessed;
}
int64_t AudioStreamInternalCapture::getFramesWritten() {
- const int64_t framesWrittenHardware = isClockModelInControl()
- ? mClockModel.convertTimeToPosition(AudioClock::getNanoseconds())
- : mAudioEndpoint.getDataWriteCounter();
- // Add service offset and prevent retrograde motion.
- mLastFramesWritten = std::max(mLastFramesWritten,
- framesWrittenHardware + mFramesOffsetFromService);
+ if (mAudioEndpoint) {
+ const int64_t framesWrittenHardware = isClockModelInControl()
+ ? mClockModel.convertTimeToPosition(AudioClock::getNanoseconds())
+ : mAudioEndpoint->getDataWriteCounter();
+ // Add service offset and prevent retrograde motion.
+ mLastFramesWritten = std::max(mLastFramesWritten,
+ framesWrittenHardware + mFramesOffsetFromService);
+ }
return mLastFramesWritten;
}
int64_t AudioStreamInternalCapture::getFramesRead() {
- int64_t frames = mAudioEndpoint.getDataReadCounter() + mFramesOffsetFromService;
- //ALOGD("getFramesRead() returns %lld", (long long)frames);
- return frames;
+ if (mAudioEndpoint) {
+ mLastFramesRead = mAudioEndpoint->getDataReadCounter() + mFramesOffsetFromService;
+ }
+ return mLastFramesRead;
}
// Read data from the stream and pass it to the callback for processing.
diff --git a/media/libaaudio/src/client/AudioStreamInternalCapture.h b/media/libaaudio/src/client/AudioStreamInternalCapture.h
index 294dbaf..6436a53 100644
--- a/media/libaaudio/src/client/AudioStreamInternalCapture.h
+++ b/media/libaaudio/src/client/AudioStreamInternalCapture.h
@@ -68,8 +68,6 @@
* @return frames written or negative error
*/
aaudio_result_t readNowWithConversion(void *buffer, int32_t numFrames);
-
- int64_t mLastFramesWritten = 0; // used to prevent retrograde motion
};
} /* namespace aaudio */
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
index b50a512..1303daf 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
@@ -87,8 +87,8 @@
}
void AudioStreamInternalPlay::advanceClientToMatchServerPosition() {
- int64_t readCounter = mAudioEndpoint.getDataReadCounter();
- int64_t writeCounter = mAudioEndpoint.getDataWriteCounter();
+ int64_t readCounter = mAudioEndpoint->getDataReadCounter();
+ int64_t writeCounter = mAudioEndpoint->getDataWriteCounter();
// Bump offset so caller does not see the retrograde motion in getFramesRead().
int64_t offset = writeCounter - readCounter;
@@ -98,7 +98,7 @@
// Force writeCounter to match readCounter.
// This is because we cannot change the read counter in the hardware.
- mAudioEndpoint.setDataWriteCounter(readCounter);
+ mAudioEndpoint->setDataWriteCounter(readCounter);
}
void AudioStreamInternalPlay::onFlushFromServer() {
@@ -135,11 +135,11 @@
// If we have gotten this far then we have at least one timestamp from server.
// If a DMA channel or DSP is reading the other end then we have to update the readCounter.
- if (mAudioEndpoint.isFreeRunning()) {
+ if (mAudioEndpoint->isFreeRunning()) {
// Update data queue based on the timing model.
int64_t estimatedReadCounter = mClockModel.convertTimeToPosition(currentNanoTime);
// ALOGD("AudioStreamInternal::processDataNow() - estimatedReadCounter = %d", (int)estimatedReadCounter);
- mAudioEndpoint.setDataReadCounter(estimatedReadCounter);
+ mAudioEndpoint->setDataReadCounter(estimatedReadCounter);
}
if (mNeedCatchUp.isRequested()) {
@@ -151,7 +151,7 @@
// If the read index passed the write index then consider it an underrun.
// For shared streams, the xRunCount is passed up from the service.
- if (mAudioEndpoint.isFreeRunning() && mAudioEndpoint.getFullFramesAvailable() < 0) {
+ if (mAudioEndpoint->isFreeRunning() && mAudioEndpoint->getFullFramesAvailable() < 0) {
mXRunCount++;
if (ATRACE_ENABLED()) {
ATRACE_INT("aaUnderRuns", mXRunCount);
@@ -170,7 +170,7 @@
// Sleep if there is too much data in the buffer.
// Calculate an ideal time to wake up.
if (wakeTimePtr != nullptr
- && (mAudioEndpoint.getFullFramesAvailable() >= getBufferSize())) {
+ && (mAudioEndpoint->getFullFramesAvailable() >= getBufferSize())) {
// 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();
@@ -188,7 +188,7 @@
{
// Sleep until the readCounter catches up and we only have
// the getBufferSize() frames of data sitting in the buffer.
- int64_t nextReadPosition = mAudioEndpoint.getDataWriteCounter() - getBufferSize();
+ int64_t nextReadPosition = mAudioEndpoint->getDataWriteCounter() - getBufferSize();
wakeTime = mClockModel.convertPositionToTime(nextReadPosition);
}
break;
@@ -210,7 +210,7 @@
uint8_t *byteBuffer = (uint8_t *) buffer;
int32_t framesLeft = numFrames;
- mAudioEndpoint.getEmptyFramesAvailable(&wrappingBuffer);
+ mAudioEndpoint->getEmptyFramesAvailable(&wrappingBuffer);
// Write data in one or two parts.
int partIndex = 0;
@@ -236,24 +236,28 @@
partIndex++;
}
int32_t framesWritten = numFrames - framesLeft;
- mAudioEndpoint.advanceWriteIndex(framesWritten);
+ mAudioEndpoint->advanceWriteIndex(framesWritten);
return framesWritten;
}
int64_t AudioStreamInternalPlay::getFramesRead() {
- const int64_t framesReadHardware = isClockModelInControl()
- ? mClockModel.convertTimeToPosition(AudioClock::getNanoseconds())
- : mAudioEndpoint.getDataReadCounter();
- // Add service offset and prevent retrograde motion.
- mLastFramesRead = std::max(mLastFramesRead, framesReadHardware + mFramesOffsetFromService);
+ if (mAudioEndpoint) {
+ const int64_t framesReadHardware = isClockModelInControl()
+ ? mClockModel.convertTimeToPosition(AudioClock::getNanoseconds())
+ : mAudioEndpoint->getDataReadCounter();
+ // Add service offset and prevent retrograde motion.
+ mLastFramesRead = std::max(mLastFramesRead, framesReadHardware + mFramesOffsetFromService);
+ }
return mLastFramesRead;
}
int64_t AudioStreamInternalPlay::getFramesWritten() {
- const int64_t framesWritten = mAudioEndpoint.getDataWriteCounter()
- + mFramesOffsetFromService;
- return framesWritten;
+ if (mAudioEndpoint) {
+ mLastFramesWritten = mAudioEndpoint->getDataWriteCounter()
+ + mFramesOffsetFromService;
+ }
+ return mLastFramesWritten;
}
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.h b/media/libaaudio/src/client/AudioStreamInternalPlay.h
index cab2942..2e93157 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.h
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.h
@@ -92,8 +92,6 @@
aaudio_result_t writeNowWithConversion(const void *buffer,
int32_t numFrames);
- int64_t mLastFramesRead = 0; // used to prevent retrograde motion
-
AAudioFlowGraph mFlowGraph;
};
diff --git a/media/libaaudio/src/core/AudioStream.cpp b/media/libaaudio/src/core/AudioStream.cpp
index f51db70..4252dfd 100644
--- a/media/libaaudio/src/core/AudioStream.cpp
+++ b/media/libaaudio/src/core/AudioStream.cpp
@@ -111,6 +111,33 @@
return AAUDIO_ERROR_INVALID_STATE;
}
+ switch (getState()) {
+ // Is this a good time to start?
+ case AAUDIO_STREAM_STATE_OPEN:
+ case AAUDIO_STREAM_STATE_PAUSING:
+ case AAUDIO_STREAM_STATE_PAUSED:
+ case AAUDIO_STREAM_STATE_STOPPING:
+ case AAUDIO_STREAM_STATE_STOPPED:
+ case AAUDIO_STREAM_STATE_FLUSHED:
+ break; // Proceed with starting.
+
+ // Already started?
+ case AAUDIO_STREAM_STATE_STARTING:
+ case AAUDIO_STREAM_STATE_STARTED:
+ ALOGW("%s() stream was already started, state = %s", __func__,
+ AudioGlobal_convertStreamStateToText(getState()));
+ return AAUDIO_ERROR_INVALID_STATE;
+
+ // Don't start when the stream is dead!
+ case AAUDIO_STREAM_STATE_DISCONNECTED:
+ case AAUDIO_STREAM_STATE_CLOSING:
+ case AAUDIO_STREAM_STATE_CLOSED:
+ default:
+ ALOGW("%s() stream is dead, state = %s", __func__,
+ AudioGlobal_convertStreamStateToText(getState()));
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
+
aaudio_result_t result = requestStart();
if (result == AAUDIO_OK) {
// We only call this for logging in "dumpsys audio". So ignore return code.
@@ -156,8 +183,8 @@
case AAUDIO_STREAM_STATE_CLOSING:
case AAUDIO_STREAM_STATE_CLOSED:
default:
- ALOGW("safePause() stream not running, state = %s",
- AudioGlobal_convertStreamStateToText(getState()));
+ ALOGW("%s() stream not running, state = %s",
+ __func__, AudioGlobal_convertStreamStateToText(getState()));
return AAUDIO_ERROR_INVALID_STATE;
}
@@ -268,6 +295,11 @@
if (mState == AAUDIO_STREAM_STATE_CLOSED) {
ALOGE("%s(%d) tried to set to %d but already CLOSED", __func__, getId(), state);
+ // Once CLOSING, we can only move to CLOSED state.
+ } else if (mState == AAUDIO_STREAM_STATE_CLOSING
+ && state != AAUDIO_STREAM_STATE_CLOSED) {
+ ALOGE("%s(%d) tried to set to %d but already CLOSING", __func__, getId(), state);
+
// Once DISCONNECTED, we can only move to CLOSING or CLOSED state.
} else if (mState == AAUDIO_STREAM_STATE_DISCONNECTED
&& !(state == AAUDIO_STREAM_STATE_CLOSING
diff --git a/media/libaaudio/tests/test_various.cpp b/media/libaaudio/tests/test_various.cpp
index 41693e8..1c26615 100644
--- a/media/libaaudio/tests/test_various.cpp
+++ b/media/libaaudio/tests/test_various.cpp
@@ -75,12 +75,33 @@
EXPECT_EQ(AAUDIO_OK, AAudioStream_requestStop(aaudioStream));
EXPECT_EQ(AAUDIO_OK, AAudioStream_release(aaudioStream));
- aaudio_stream_state_t state = AAudioStream_getState(aaudioStream);
- EXPECT_EQ(AAUDIO_STREAM_STATE_CLOSING, state);
+ EXPECT_EQ(AAUDIO_STREAM_STATE_CLOSING, AAudioStream_getState(aaudioStream));
// We should be able to call this again without crashing.
EXPECT_EQ(AAUDIO_OK, AAudioStream_release(aaudioStream));
- state = AAudioStream_getState(aaudioStream);
+ EXPECT_EQ(AAUDIO_STREAM_STATE_CLOSING, AAudioStream_getState(aaudioStream));
+
+ // We expect these not to crash.
+ AAudioStream_setBufferSizeInFrames(aaudioStream, 0);
+ AAudioStream_setBufferSizeInFrames(aaudioStream, 99999999);
+
+ // We should NOT be able to start or change a stream after it has been released.
+ EXPECT_EQ(AAUDIO_ERROR_INVALID_STATE, AAudioStream_requestStart(aaudioStream));
+ EXPECT_EQ(AAUDIO_STREAM_STATE_CLOSING, AAudioStream_getState(aaudioStream));
+ EXPECT_EQ(AAUDIO_ERROR_INVALID_STATE, AAudioStream_requestPause(aaudioStream));
+ EXPECT_EQ(AAUDIO_STREAM_STATE_CLOSING, AAudioStream_getState(aaudioStream));
+ EXPECT_EQ(AAUDIO_ERROR_INVALID_STATE, AAudioStream_requestStop(aaudioStream));
+ EXPECT_EQ(AAUDIO_STREAM_STATE_CLOSING, AAudioStream_getState(aaudioStream));
+
+ // Does this crash?
+ EXPECT_LT(0, AAudioStream_getFramesRead(aaudioStream));
+ EXPECT_LT(0, AAudioStream_getFramesWritten(aaudioStream));
+
+ // Verify Closing State. Does this crash?
+ aaudio_stream_state_t state = AAUDIO_STREAM_STATE_UNKNOWN;
+ EXPECT_EQ(AAUDIO_OK, AAudioStream_waitForStateChange(aaudioStream,
+ AAUDIO_STREAM_STATE_UNKNOWN, &state,
+ 500 * NANOS_PER_MILLISECOND));
EXPECT_EQ(AAUDIO_STREAM_STATE_CLOSING, state);
EXPECT_EQ(AAUDIO_OK, AAudioStream_close(aaudioStream));
diff --git a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
index 1cb81a6..39f5bb6 100644
--- a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
+++ b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
@@ -1906,11 +1906,15 @@
//ALOGV("\tReverb_command cmdCode Case: "
// "EFFECT_CMD_GET_PARAM start");
effect_param_t *p = (effect_param_t *)pCmdData;
+ if (pCmdData == nullptr) {
+ ALOGW("\tLVM_ERROR : pCmdData is NULL");
+ return -EINVAL;
+ }
if (SIZE_MAX - sizeof(effect_param_t) < (size_t)p->psize) {
android_errorWriteLog(0x534e4554, "26347509");
return -EINVAL;
}
- if (pCmdData == NULL || cmdSize < sizeof(effect_param_t) ||
+ if (cmdSize < sizeof(effect_param_t) ||
cmdSize < (sizeof(effect_param_t) + p->psize) ||
pReplyData == NULL || replySize == NULL ||
*replySize < (sizeof(effect_param_t) + p->psize)) {
diff --git a/media/libmediametrics/Android.bp b/media/libmediametrics/Android.bp
index 0cd5194..03068c7 100644
--- a/media/libmediametrics/Android.bp
+++ b/media/libmediametrics/Android.bp
@@ -22,9 +22,11 @@
export_include_dirs: ["include"],
cflags: [
- "-Werror",
- "-Wno-error=deprecated-declarations",
"-Wall",
+ "-Werror",
+ "-Wextra",
+ "-Wthread-safety",
+ "-Wunreachable-code",
],
sanitize: {
diff --git a/media/libstagefright/codecs/mp3dec/src/pvmp3_framedecoder.cpp b/media/libstagefright/codecs/mp3dec/src/pvmp3_framedecoder.cpp
index df6cd03..a5c7f5e 100644
--- a/media/libstagefright/codecs/mp3dec/src/pvmp3_framedecoder.cpp
+++ b/media/libstagefright/codecs/mp3dec/src/pvmp3_framedecoder.cpp
@@ -659,20 +659,12 @@
huffcodetab *pHuff;
pVars = (tmp3dec_file *)pMem;
-
- pVars->num_channels = 0;
+ memset(pVars, 0, sizeof(*pVars));
pExt->totalNumberOfBitsUsed = 0;
pExt->inputBufferCurrentLength = 0;
pExt->inputBufferUsedLength = 0;
- pVars->mainDataStream.offset = 0;
-
- pv_memset((void*)pVars->mainDataBuffer,
- 0,
- BUFSIZE*sizeof(*pVars->mainDataBuffer));
-
-
pVars->inputStream.pBuffer = pExt->pInputBuffer;
/*
diff --git a/media/libstagefright/id3/ID3.cpp b/media/libstagefright/id3/ID3.cpp
index 425468f..e97f6eb 100644
--- a/media/libstagefright/id3/ID3.cpp
+++ b/media/libstagefright/id3/ID3.cpp
@@ -107,20 +107,6 @@
}
}
-ID3::ID3(DataSourceBase *source, bool ignoreV1, off64_t offset)
- : mIsValid(false),
- mData(NULL),
- mSize(0),
- mFirstFrameOffset(0),
- mVersion(ID3_UNKNOWN),
- mRawSize(0) {
- mIsValid = parseV2(source, offset);
-
- if (!mIsValid && !ignoreV1) {
- mIsValid = parseV1(source);
- }
-}
-
ID3::ID3(const uint8_t *data, size_t size, bool ignoreV1)
: mIsValid(false),
mData(NULL),
@@ -247,44 +233,14 @@
return false;
}
- if (header.version_major == 4) {
- void *copy = malloc(size);
- if (copy == NULL) {
- free(mData);
- mData = NULL;
- ALOGE("b/24623447, no more memory");
- return false;
- }
-
- memcpy(copy, mData, size);
-
- bool success = removeUnsynchronizationV2_4(false /* iTunesHack */);
- if (!success) {
- memcpy(mData, copy, size);
- mSize = size;
-
- success = removeUnsynchronizationV2_4(true /* iTunesHack */);
-
- if (success) {
- ALOGV("Had to apply the iTunes hack to parse this ID3 tag");
- }
- }
-
- free(copy);
- copy = NULL;
-
- if (!success) {
- free(mData);
- mData = NULL;
-
- return false;
- }
- } else if (header.flags & 0x80) {
+ // first handle global unsynchronization
+ if (header.flags & 0x80) {
ALOGV("removing unsynchronization");
removeUnsynchronization();
}
+ // handle extended header, if present
mFirstFrameOffset = 0;
if (header.version_major == 3 && (header.flags & 0x40)) {
// Version 2.3 has an optional extended header.
@@ -296,6 +252,7 @@
return false;
}
+ // v2.3 does not have syncsafe integers
size_t extendedHeaderSize = U32_AT(&mData[0]);
if (extendedHeaderSize > SIZE_MAX - 4) {
free(mData);
@@ -367,6 +324,48 @@
mFirstFrameOffset = ext_size;
}
+ // Handle any v2.4 per-frame unsynchronization
+ // The id3 spec isn't clear about what should happen if the global
+ // unsynchronization flag is combined with per-frame unsynchronization,
+ // or whether that's even allowed, so this code assumes id3 writing
+ // tools do the right thing and not apply double-unsynchronization,
+ // but will honor the flags if they are set.
+ if (header.version_major == 4) {
+ void *copy = malloc(size);
+ if (copy == NULL) {
+ free(mData);
+ mData = NULL;
+ ALOGE("b/24623447, no more memory");
+ return false;
+ }
+
+ memcpy(copy, mData, size);
+
+ bool success = removeUnsynchronizationV2_4(false /* iTunesHack */);
+ if (!success) {
+ memcpy(mData, copy, size);
+ mSize = size;
+
+ success = removeUnsynchronizationV2_4(true /* iTunesHack */);
+
+ if (success) {
+ ALOGV("Had to apply the iTunes hack to parse this ID3 tag");
+ }
+ }
+
+ free(copy);
+ copy = NULL;
+
+ if (!success) {
+ free(mData);
+ mData = NULL;
+
+ return false;
+ }
+ }
+
+
+
if (header.version_major == 2) {
mVersion = ID3_V2_2;
} else if (header.version_major == 3) {
@@ -411,7 +410,7 @@
bool ID3::removeUnsynchronizationV2_4(bool iTunesHack) {
size_t oldSize = mSize;
- size_t offset = 0;
+ size_t offset = mFirstFrameOffset;
while (mSize >= 10 && offset <= mSize - 10) {
if (!memcmp(&mData[offset], "\0\0\0\0", 4)) {
break;
@@ -445,7 +444,7 @@
}
if ((flags & 2) && (dataSize >= 2)) {
- // This file has "unsynchronization", so we have to replace occurrences
+ // This frame has "unsynchronization", so we have to replace occurrences
// of 0xff 0x00 with just 0xff in order to get the real data.
size_t readOffset = offset + 11;
diff --git a/media/libstagefright/id3/test/ID3Test.cpp b/media/libstagefright/id3/test/ID3Test.cpp
index a8f1470..cd5cd9e 100644
--- a/media/libstagefright/id3/test/ID3Test.cpp
+++ b/media/libstagefright/id3/test/ID3Test.cpp
@@ -24,6 +24,7 @@
#include <datasource/FileSource.h>
#include <media/stagefright/foundation/hexdump.h>
+#include <media/MediaExtractorPluginHelper.h>
#include <ID3.h>
#include "ID3TestEnvironment.h"
@@ -42,7 +43,8 @@
string path = gEnv->getRes() + GetParam();
sp<FileSource> file = new FileSource(path.c_str());
ASSERT_EQ(file->initCheck(), (status_t)OK) << "File initialization failed! \n";
- ID3 tag(file.get());
+ DataSourceHelper helper(file->wrap());
+ ID3 tag(&helper);
ASSERT_TRUE(tag.isValid()) << "No valid ID3 tag found for " << path.c_str() << "\n";
ID3::Iterator it(tag, nullptr);
@@ -61,7 +63,8 @@
sp<android::FileSource> file = new FileSource(path.c_str());
ASSERT_EQ(file->initCheck(), (status_t)OK) << "File initialization failed! \n";
- ID3 tag(file.get());
+ DataSourceHelper helper(file->wrap());
+ ID3 tag(&helper);
ASSERT_TRUE(tag.isValid()) << "No valid ID3 tag found for " << path.c_str() << "\n";
ASSERT_TRUE(tag.version() >= versionNumber)
<< "Expected version: " << tag.version() << " Found version: " << versionNumber;
@@ -73,7 +76,8 @@
sp<android::FileSource> file = new FileSource(path.c_str());
ASSERT_EQ(file->initCheck(), (status_t)OK) << "File initialization failed! \n";
- ID3 tag(file.get());
+ DataSourceHelper helper(file->wrap());
+ ID3 tag(&helper);
ASSERT_TRUE(tag.isValid()) << "No valid ID3 tag found for " << path.c_str() << "\n";
int countTextFrames = 0;
ID3::Iterator it(tag, nullptr);
@@ -99,7 +103,8 @@
sp<android::FileSource> file = new FileSource(path.c_str());
ASSERT_EQ(file->initCheck(), (status_t)OK) << "File initialization failed! \n";
- ID3 tag(file.get());
+ DataSourceHelper helper(file->wrap());
+ ID3 tag(&helper);
ASSERT_TRUE(tag.isValid()) << "No valid ID3 tag found for " << path.c_str() << "\n";
size_t dataSize;
String8 mime;
@@ -124,7 +129,8 @@
sp<android::FileSource> file = new FileSource(path.c_str());
ASSERT_EQ(file->initCheck(), (status_t)OK) << "File initialization failed! \n";
- ID3 tag(file.get());
+ DataSourceHelper helper(file->wrap());
+ ID3 tag(&helper);
ASSERT_TRUE(tag.isValid()) << "No valid ID3 tag found for " << path.c_str() << "\n";
int count = 0;
ID3::Iterator it(tag, nullptr);
diff --git a/media/libstagefright/id3/testid3.cpp b/media/libstagefright/id3/testid3.cpp
index 9984d85..5cd51cf 100644
--- a/media/libstagefright/id3/testid3.cpp
+++ b/media/libstagefright/id3/testid3.cpp
@@ -24,6 +24,7 @@
#include <binder/ProcessState.h>
#include <datasource/FileSource.h>
#include <media/stagefright/foundation/ADebug.h>
+#include <media/MediaExtractorPluginHelper.h>
#define MAXPATHLEN 256
@@ -72,7 +73,8 @@
sp<FileSource> file = new FileSource(path);
CHECK_EQ(file->initCheck(), (status_t)OK);
- ID3 tag(file.get());
+ DataSourceHelper helper(file->wrap());
+ ID3 tag(&helper);
if (!tag.isValid()) {
printf("FAIL %s\n", path);
} else {
diff --git a/media/libstagefright/include/ID3.h b/media/libstagefright/include/ID3.h
index 2843a7a..0be5896 100644
--- a/media/libstagefright/include/ID3.h
+++ b/media/libstagefright/include/ID3.h
@@ -37,7 +37,6 @@
};
explicit ID3(DataSourceHelper *source, bool ignoreV1 = false, off64_t offset = 0);
- explicit ID3(DataSourceBase *source, bool ignoreV1 = false, off64_t offset = 0);
ID3(const uint8_t *data, size_t size, bool ignoreV1 = false);
~ID3();
diff --git a/media/libstagefright/tests/extractorFactory/Android.bp b/media/libstagefright/tests/extractorFactory/Android.bp
new file mode 100644
index 0000000..e3e61d7
--- /dev/null
+++ b/media/libstagefright/tests/extractorFactory/Android.bp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+cc_test {
+ name: "ExtractorFactoryTest",
+ gtest: true,
+
+ srcs: [
+ "ExtractorFactoryTest.cpp",
+ ],
+
+ shared_libs: [
+ "liblog",
+ "libbase",
+ "libutils",
+ "libmedia",
+ "libbinder",
+ "libcutils",
+ "libdl_android",
+ "libdatasource",
+ "libmediametrics",
+ ],
+
+ static_libs: [
+ "libstagefright",
+ "libstagefright_foundation",
+ ],
+
+ include_dirs: [
+ "frameworks/av/media/libstagefright",
+ ],
+
+ // TODO: (b/150181583)
+ compile_multilib: "first",
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+ sanitize: {
+ cfi: true,
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ },
+}
diff --git a/media/libstagefright/tests/extractorFactory/AndroidTest.xml b/media/libstagefright/tests/extractorFactory/AndroidTest.xml
new file mode 100644
index 0000000..3aa6392
--- /dev/null
+++ b/media/libstagefright/tests/extractorFactory/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Test module config for extractor factory unit tests">
+ <option name="test-suite-tag" value="ExtractorFactoryTest" />
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="ExtractorFactoryTest->/data/local/tmp/ExtractorFactoryTest" />
+ <option name="push-file"
+ key="https://storage.googleapis.com/android_media/frameworks/av/media/extractors/tests/extractor.zip?unzip=true"
+ value="/data/local/tmp/ExtractorFactoryTestRes/" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="ExtractorFactoryTest" />
+ <option name="native-test-flag" value="-P /data/local/tmp/ExtractorFactoryTestRes/" />
+ </test>
+</configuration>
diff --git a/media/libstagefright/tests/extractorFactory/ExtractorFactoryTest.cpp b/media/libstagefright/tests/extractorFactory/ExtractorFactoryTest.cpp
new file mode 100644
index 0000000..d155caa
--- /dev/null
+++ b/media/libstagefright/tests/extractorFactory/ExtractorFactoryTest.cpp
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ExtractorFactoryTest"
+#include <utils/Log.h>
+
+#include <binder/ProcessState.h>
+
+#include <datasource/FileSource.h>
+#include <media/stagefright/MediaExtractorFactory.h>
+#include <media/stagefright/foundation/MediaDefs.h>
+
+#include "ExtractorFactoryTestEnvironment.h"
+
+#define OUTPUT_FILE_NAME "/data/local/tmp/exFactoryLogs"
+
+using namespace android;
+
+static ExtractorFactoryTestEnvironment *gEnv = nullptr;
+
+class ExtractorFactoryTest : public ::testing::TestWithParam<pair<string, string>> {
+ public:
+ ExtractorFactoryTest() : mDataSource(nullptr), mExtractor(nullptr) {}
+
+ ~ExtractorFactoryTest() {
+ if (mDataSource) {
+ mDataSource.clear();
+ mDataSource = nullptr;
+ }
+ if (mExtractor) {
+ mExtractor.clear();
+ mExtractor = nullptr;
+ }
+ }
+
+ int32_t createDataSource(string inputFileName);
+ int32_t createExtractor(bool createFromService, string inputMime);
+
+ sp<DataSource> mDataSource;
+ sp<IMediaExtractor> mExtractor;
+};
+
+int32_t ExtractorFactoryTest::createDataSource(string inputFileName) {
+ FILE *mInputFp = fopen(inputFileName.c_str(), "rb");
+ if (!mInputFp) {
+ ALOGE("Unable to open input file : %s for reading", inputFileName.c_str());
+ return -1;
+ }
+ struct stat buf;
+ int32_t status = stat(inputFileName.c_str(), &buf);
+ if (status != 0) {
+ ALOGE("Failed to read file properties for input file : %s", inputFileName.c_str());
+ return -1;
+ }
+ int32_t fd = fileno(mInputFp);
+ if (fd < 0) {
+ ALOGE("Invalid file descriptor for input file : %s", inputFileName.c_str());
+ return -1;
+ }
+ mDataSource = new FileSource(dup(fd), 0, buf.st_size);
+ if (!mDataSource) return -1;
+ return 0;
+}
+
+int32_t ExtractorFactoryTest::createExtractor(bool createFromService, string inputMime) {
+ ALOGV("Creating extractor for mime : %s", inputMime.c_str());
+ if (createFromService) {
+ mExtractor = MediaExtractorFactory::CreateFromService(mDataSource, inputMime.c_str());
+ } else {
+ mExtractor = MediaExtractorFactory::Create(mDataSource);
+ }
+ if (mExtractor == nullptr) return -1;
+ return 0;
+}
+
+TEST_F(ExtractorFactoryTest, ListExtractorsTest) {
+ MediaExtractorFactory::LoadExtractors();
+ vector<std::string> supportedTypes = MediaExtractorFactory::getSupportedTypes();
+ ASSERT_GT(supportedTypes.size(), 0) << " MediaExtractorFactory doesn't suuport any extractor";
+
+ FILE *outputLog = fopen(OUTPUT_FILE_NAME, "wb");
+ ASSERT_NE(outputLog, nullptr) << "Unable to open output file - " << OUTPUT_FILE_NAME
+ << " for writing";
+
+ int32_t fd = fileno(outputLog);
+ ASSERT_GE(fd, 0);
+
+ Vector<String16> args;
+ int32_t status = MediaExtractorFactory::dump(fd, args);
+ ASSERT_EQ(status, OK) << "MediaExtractorFactory dump failed";
+ fclose(outputLog);
+}
+
+TEST_P(ExtractorFactoryTest, ExtractorFactoryApiTest) {
+ string inputMime = GetParam().second;
+ string inputFileName = gEnv->getRes() + GetParam().first;
+
+ MediaExtractorFactory::LoadExtractors();
+ bool createMode[] = {true, false};
+ for (bool createFromService : createMode) {
+ int32_t status = createDataSource(inputFileName);
+ ASSERT_EQ(status, 0) << "create data source failed";
+
+ status = createExtractor(createFromService, inputMime);
+ ASSERT_EQ(status, 0) << "Extractor creation failed for input: " << inputFileName;
+
+ int32_t numTracks = mExtractor->countTracks();
+ ASSERT_GT(numTracks, 0) << "Extractor didn't find any track for the given clip";
+
+ sp<MetaData> meta = mExtractor->getMetaData();
+ ASSERT_NE(meta, nullptr) << "getMetaData returned null";
+
+ const char *mime;
+ bool valueFound = meta->findCString(kKeyMIMEType, &mime);
+ ASSERT_TRUE(valueFound) << "Extractor did not provide MIME type";
+ ASSERT_EQ(mime, inputMime) << "Extractor factory returned invalid mime type";
+ mExtractor.clear();
+ mDataSource.clear();
+ }
+}
+
+// TODO: (b/150111966)
+// Replace mime strings with appropriate definitions
+INSTANTIATE_TEST_SUITE_P(
+ ExtractorFactoryTestAll, ExtractorFactoryTest,
+ ::testing::Values(make_pair("loudsoftaac.aac", MEDIA_MIMETYPE_AUDIO_AAC_ADTS),
+ make_pair("testamr.amr", "audio/amr"),
+ make_pair("amrwb.wav", MEDIA_MIMETYPE_AUDIO_AMR_WB),
+ make_pair("john_cage.ogg", MEDIA_MIMETYPE_CONTAINER_OGG),
+ make_pair("monotestgsm.wav", MEDIA_MIMETYPE_CONTAINER_WAV),
+ make_pair("segment000001.ts", MEDIA_MIMETYPE_CONTAINER_MPEG2TS),
+ make_pair("sinesweepflac.flac", MEDIA_MIMETYPE_AUDIO_FLAC),
+ make_pair("testopus.opus", MEDIA_MIMETYPE_CONTAINER_OGG),
+ make_pair("midi_a.mid", MEDIA_MIMETYPE_AUDIO_MIDI),
+ make_pair("sinesweepvorbis.mkv", MEDIA_MIMETYPE_CONTAINER_MATROSKA),
+ make_pair("sinesweepoggmp4.mp4", "audio/mp4"),
+ make_pair("sinesweepmp3lame.mp3", MEDIA_MIMETYPE_AUDIO_MPEG),
+ make_pair("swirl_144x136_vp9.webm", "video/webm"),
+ make_pair("swirl_144x136_vp8.webm", "video/webm"),
+ make_pair("swirl_132x130_mpeg4.mp4", MEDIA_MIMETYPE_CONTAINER_MPEG4)));
+
+int main(int argc, char **argv) {
+ ProcessState::self()->startThreadPool();
+ gEnv = new ExtractorFactoryTestEnvironment();
+ ::testing::AddGlobalTestEnvironment(gEnv);
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = gEnv->initFromOptions(argc, argv);
+ if (status == 0) {
+ status = RUN_ALL_TESTS();
+ ALOGV("Test result = %d\n", status);
+ }
+ return status;
+}
diff --git a/media/libstagefright/tests/extractorFactory/ExtractorFactoryTestEnvironment.h b/media/libstagefright/tests/extractorFactory/ExtractorFactoryTestEnvironment.h
new file mode 100644
index 0000000..0fad4d3
--- /dev/null
+++ b/media/libstagefright/tests/extractorFactory/ExtractorFactoryTestEnvironment.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __EXTRACTOR_FACTORY_TEST_ENVIRONMENT_H__
+#define __EXTRACTOR_FACTORY_TEST_ENVIRONMENT_H__
+
+#include <gtest/gtest.h>
+
+#include <getopt.h>
+
+using namespace std;
+
+class ExtractorFactoryTestEnvironment : public ::testing::Environment {
+ public:
+ ExtractorFactoryTestEnvironment() : res("/data/local/tmp/") {}
+
+ // Parses the command line arguments
+ int initFromOptions(int argc, char **argv);
+
+ void setRes(const char *_res) { res = _res; }
+
+ const string getRes() const { return res; }
+
+ private:
+ string res;
+};
+
+int ExtractorFactoryTestEnvironment::initFromOptions(int argc, char **argv) {
+ static struct option options[] = {{"res", required_argument, 0, 'P'}, {0, 0, 0, 0}};
+
+ while (true) {
+ int index = 0;
+ int c = getopt_long(argc, argv, "P:", options, &index);
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 'P':
+ setRes(optarg);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (optind < argc) {
+ fprintf(stderr,
+ "unrecognized option: %s\n\n"
+ "usage: %s <gtest options> <test options>\n\n"
+ "test options are:\n\n"
+ "-P, --path: Resource files directory location\n",
+ argv[optind ?: 1], argv[0]);
+ return 2;
+ }
+ return 0;
+}
+
+#endif // __EXTRACTOR_FACTORY_TEST_ENVIRONMENT_H__
diff --git a/media/libstagefright/tests/extractorFactory/README.md b/media/libstagefright/tests/extractorFactory/README.md
new file mode 100644
index 0000000..aaa71aa
--- /dev/null
+++ b/media/libstagefright/tests/extractorFactory/README.md
@@ -0,0 +1,37 @@
+## Media Testing ##
+---
+#### Writer :
+The Writer Test Suite validates the writers available in libstagefright.
+
+Run the following steps to build the test suite:
+```
+mmm frameworks/av/media/libstagefright/tests/writer/
+```
+
+The 32-bit binaries will be created in the following path : ${OUT}/data/nativetest/
+The 64-bit binaries will be created in the following path : ${OUT}/data/nativetest64/
+
+To test 64-bit binary push binaries from nativetest64.
+
+adb push ${OUT}/data/nativetest64/ExtractorFactoryTest/ExtractorFactoryTest /data/local/tmp/
+
+To test 32-bit binary push binaries from nativetest.
+
+adb push ${OUT}/data/nativetest/ExtractorFactoryTest/ExtractorFactoryTest /data/local/tmp/
+
+The resource file for the tests is taken from [here](https://storage.googleapis.com/android_media/frameworks/av/media/extractors/tests/extractor.zip).
+Download, unzip and push these files into device for testing.
+
+```
+adb push extractor /data/local/tmp/
+```
+
+usage: ExtractorFactoryTest -P \<path_to_res_folder\>
+```
+adb shell /data/local/tmp/ExtractorFactoryTest -P /data/local/tmp/extractor/
+```
+Alternatively, the test can also be run using atest command.
+
+```
+atest ExtractorFactoryTest -- --enable-module-dynamic-download=true
+```
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
index aaa28bc..d5272bc 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
@@ -152,10 +152,16 @@
bool AudioOutputDescriptor::setVolume(float volumeDb,
VolumeSource volumeSource,
const StreamTypeVector &/*streams*/,
- const DeviceTypeSet& /*deviceTypes*/,
+ const DeviceTypeSet& deviceTypes,
uint32_t delayMs,
bool force)
{
+
+ if (!supportedDevices().containsDeviceAmongTypes(deviceTypes)) {
+ ALOGV("%s output ID %d unsupported device %s",
+ __func__, getId(), toString(deviceTypes).c_str());
+ return false;
+ }
// We actually change the volume if:
// - the float value returned by computeVolume() changed
// - the force flag is set
diff --git a/services/mediametrics/AnalyticsActions.h b/services/mediametrics/AnalyticsActions.h
index 5568c91..0151134 100644
--- a/services/mediametrics/AnalyticsActions.h
+++ b/services/mediametrics/AnalyticsActions.h
@@ -16,6 +16,7 @@
#pragma once
+#include <android-base/thread_annotations.h>
#include <media/MediaMetricsItem.h>
#include <mutex>
@@ -144,7 +145,7 @@
}
mutable std::mutex mLock;
- std::map<Trigger, Action> mFilters; // GUARDED_BY mLock
+ std::map<Trigger, Action> mFilters GUARDED_BY(mLock);
};
} // namespace android::mediametrics
diff --git a/services/mediametrics/Android.bp b/services/mediametrics/Android.bp
index 58f3ea8..fb4022e 100644
--- a/services/mediametrics/Android.bp
+++ b/services/mediametrics/Android.bp
@@ -27,6 +27,7 @@
"-Wall",
"-Werror",
"-Wextra",
+ "-Wthread-safety",
],
}
diff --git a/services/mediametrics/MediaMetricsService.cpp b/services/mediametrics/MediaMetricsService.cpp
index b9703ba..4b84bea 100644
--- a/services/mediametrics/MediaMetricsService.cpp
+++ b/services/mediametrics/MediaMetricsService.cpp
@@ -282,8 +282,8 @@
} else {
result.appendFormat("Dump of the %s process:\n", kServiceName);
const char *prefixptr = prefix.size() > 0 ? prefix.c_str() : nullptr;
- dumpHeaders_l(result, sinceNs, prefixptr);
- dumpQueue_l(result, sinceNs, prefixptr);
+ dumpHeaders(result, sinceNs, prefixptr);
+ dumpQueue(result, sinceNs, prefixptr);
// TODO: maybe consider a better way of dumping audio analytics info.
const int32_t linesToDump = all ? INT32_MAX : 1000;
@@ -312,7 +312,7 @@
}
// dump headers
-void MediaMetricsService::dumpHeaders_l(String8 &result, int64_t sinceNs, const char* prefix)
+void MediaMetricsService::dumpHeaders(String8 &result, int64_t sinceNs, const char* prefix)
{
if (mediametrics::Item::isEnabled()) {
result.append("Metrics gathering: enabled\n");
@@ -337,7 +337,7 @@
}
// TODO: should prefix be a set<string>?
-void MediaMetricsService::dumpQueue_l(String8 &result, int64_t sinceNs, const char* prefix)
+void MediaMetricsService::dumpQueue(String8 &result, int64_t sinceNs, const char* prefix)
{
if (mItems.empty()) {
result.append("empty\n");
@@ -364,7 +364,7 @@
// if item != NULL, it's the item we just inserted
// true == more items eligible to be recovered
-bool MediaMetricsService::expirations_l(const std::shared_ptr<const mediametrics::Item>& item)
+bool MediaMetricsService::expirations(const std::shared_ptr<const mediametrics::Item>& item)
{
bool more = false;
@@ -419,7 +419,7 @@
do {
sleep(1);
std::lock_guard _l(mLock);
- more = expirations_l(nullptr);
+ more = expirations(nullptr);
} while (more);
}
@@ -429,7 +429,7 @@
// we assume the items are roughly in time order.
mItems.emplace_back(item);
++mItemsFinalized;
- if (expirations_l(item)
+ if (expirations(item)
&& (!mExpireFuture.valid()
|| mExpireFuture.wait_for(std::chrono::seconds(0)) == std::future_status::ready)) {
mExpireFuture = std::async(std::launch::async, [this] { processExpirations(); });
diff --git a/services/mediametrics/MediaMetricsService.h b/services/mediametrics/MediaMetricsService.h
index a93f7fb..faba197 100644
--- a/services/mediametrics/MediaMetricsService.h
+++ b/services/mediametrics/MediaMetricsService.h
@@ -23,6 +23,7 @@
#include <unordered_map>
// IMediaMetricsService must include Vector, String16, Errors
+#include <android-base/thread_annotations.h>
#include <media/IMediaMetricsService.h>
#include <mediautils/ServiceUtilities.h>
#include <utils/String8.h>
@@ -81,12 +82,11 @@
bool isRateLimited(mediametrics::Item *) const;
void saveItem(const std::shared_ptr<const mediametrics::Item>& item);
- // The following methods are GUARDED_BY(mLock)
- bool expirations_l(const std::shared_ptr<const mediametrics::Item>& item);
+ bool expirations(const std::shared_ptr<const mediametrics::Item>& item) REQUIRES(mLock);
// support for generating output
- void dumpQueue_l(String8 &result, int64_t sinceNs, const char* prefix);
- void dumpHeaders_l(String8 &result, int64_t sinceNs, const char* prefix);
+ void dumpQueue(String8 &result, int64_t sinceNs, const char* prefix) REQUIRES(mLock);
+ void dumpHeaders(String8 &result, int64_t sinceNs, const char* prefix) REQUIRES(mLock);
// The following variables accessed without mLock
@@ -102,22 +102,22 @@
mediautils::UidInfo mUidInfo; // mUidInfo can be accessed without lock (locked internally)
- mediametrics::AudioAnalytics mAudioAnalytics;
+ mediametrics::AudioAnalytics mAudioAnalytics; // mAudioAnalytics is locked internally.
std::mutex mLock;
// statistics about our analytics
- int64_t mItemsFinalized = 0; // GUARDED_BY(mLock)
- int64_t mItemsDiscarded = 0; // GUARDED_BY(mLock)
- int64_t mItemsDiscardedExpire = 0; // GUARDED_BY(mLock)
- int64_t mItemsDiscardedCount = 0; // GUARDED_BY(mLock)
+ int64_t mItemsFinalized GUARDED_BY(mLock) = 0;
+ int64_t mItemsDiscarded GUARDED_BY(mLock) = 0;
+ int64_t mItemsDiscardedExpire GUARDED_BY(mLock) = 0;
+ int64_t mItemsDiscardedCount GUARDED_BY(mLock) = 0;
// If we have a worker thread to garbage collect
- std::future<void> mExpireFuture; // GUARDED_BY(mLock)
+ std::future<void> mExpireFuture GUARDED_BY(mLock);
// Our item queue, generally (oldest at front)
// TODO: Make separate class, use segmented queue, write lock only end.
// Note: Another analytics module might have ownership of an item longer than the log.
- std::deque<std::shared_ptr<const mediametrics::Item>> mItems; // GUARDED_BY(mLock)
+ std::deque<std::shared_ptr<const mediametrics::Item>> mItems GUARDED_BY(mLock);
};
} // namespace android
diff --git a/services/mediametrics/TimeMachine.h b/services/mediametrics/TimeMachine.h
index 29adeae..b138233 100644
--- a/services/mediametrics/TimeMachine.h
+++ b/services/mediametrics/TimeMachine.h
@@ -23,6 +23,7 @@
#include <variant>
#include <vector>
+#include <android-base/thread_annotations.h>
#include <media/MediaMetricsItem.h>
#include <utils/Timers.h>
@@ -92,7 +93,8 @@
}
template <typename T>
- status_t getValue(const std::string &property, T* value, int64_t time = 0) const {
+ status_t getValue(const std::string &property, T* value, int64_t time = 0) const
+ REQUIRES(mPseudoKeyHistoryLock) {
if (time == 0) time = systemTime(SYSTEM_TIME_REALTIME);
const auto tsptr = mPropertyMap.find(property);
if (tsptr == mPropertyMap.end()) return BAD_VALUE;
@@ -108,20 +110,22 @@
}
template <typename T>
- status_t getValue(const std::string &property, T defaultValue, int64_t time = 0) const {
+ status_t getValue(const std::string &property, T defaultValue, int64_t time = 0) const
+ REQUIRES(mPseudoKeyHistoryLock){
T value;
return getValue(property, &value, time) != NO_ERROR ? defaultValue : value;
}
void putProp(
- const std::string &name, const mediametrics::Item::Prop &prop, int64_t time = 0) {
+ const std::string &name, const mediametrics::Item::Prop &prop, int64_t time = 0)
+ REQUIRES(mPseudoKeyHistoryLock) {
//alternatively: prop.visit([&](auto value) { putValue(name, value, time); });
putValue(name, prop.get(), time);
}
template <typename T>
- void putValue(const std::string &property,
- T&& e, int64_t time = 0) {
+ void putValue(const std::string &property, T&& e, int64_t time = 0)
+ REQUIRES(mPseudoKeyHistoryLock) {
if (time == 0) time = systemTime(SYSTEM_TIME_REALTIME);
mLastModificationTime = time;
if (mPropertyMap.size() >= kKeyMaxProperties &&
@@ -144,7 +148,8 @@
}
}
- std::pair<std::string, int32_t> dump(int32_t lines, int64_t time) const {
+ std::pair<std::string, int32_t> dump(int32_t lines, int64_t time) const
+ REQUIRES(mPseudoKeyHistoryLock) {
std::stringstream ss;
int32_t ll = lines;
for (auto& tsPair : mPropertyMap) {
@@ -158,7 +163,9 @@
return { ss.str(), lines - ll };
}
- int64_t getLastModificationTime() const { return mLastModificationTime; }
+ int64_t getLastModificationTime() const REQUIRES(mPseudoKeyHistoryLock) {
+ return mLastModificationTime;
+ }
private:
static std::string dump(
@@ -267,7 +274,7 @@
if (it == mHistory.end()) {
if (!isTrusted) return PERMISSION_DENIED;
- (void)gc_l(garbage);
+ (void)gc(garbage);
// no keylock needed here as we are sole owner
// until placed on mHistory.
@@ -435,7 +442,8 @@
private:
// Obtains the lock for a KeyHistory.
- std::mutex &getLockForKey(const std::string &key) const {
+ std::mutex &getLockForKey(const std::string &key) const
+ RETURN_CAPABILITY(mPseudoKeyHistoryLock) {
return mKeyLocks[std::hash<std::string>{}(key) % std::size(mKeyLocks)];
}
@@ -459,7 +467,6 @@
return it->second;
}
- // GUARDED_BY mLock
/**
* Garbage collects if the TimeMachine size exceeds the high water mark.
*
@@ -471,7 +478,7 @@
*
* \return true if garbage collection was done.
*/
- bool gc_l(std::vector<std::any>& garbage) {
+ bool gc(std::vector<std::any>& garbage) REQUIRES(mLock) {
// TODO: something better than this for garbage collection.
if (mHistory.size() < mKeyHighWaterMark) return false;
@@ -540,12 +547,17 @@
*/
mutable std::mutex mLock; // Lock for mHistory
- History mHistory; // GUARDED_BY mLock
+ History mHistory GUARDED_BY(mLock);
// KEY_LOCKS is the number of mutexes for keys.
// It need not be a power of 2, but faster that way.
static inline constexpr size_t KEY_LOCKS = 256;
mutable std::mutex mKeyLocks[KEY_LOCKS]; // Hash-striped lock for KeyHistory based on key.
+
+ // Used for thread-safety analysis, we create a fake mutex object to represent
+ // the hash stripe lock mechanism, which is then tracked by the compiler.
+ class CAPABILITY("mutex") PseudoLock {};
+ static inline PseudoLock mPseudoKeyHistoryLock;
};
} // namespace android::mediametrics
diff --git a/services/mediametrics/TransactionLog.h b/services/mediametrics/TransactionLog.h
index 4f09bb0..d64acf3 100644
--- a/services/mediametrics/TransactionLog.h
+++ b/services/mediametrics/TransactionLog.h
@@ -21,6 +21,7 @@
#include <sstream>
#include <string>
+#include <android-base/thread_annotations.h>
#include <media/MediaMetricsItem.h>
namespace android::mediametrics {
@@ -92,7 +93,7 @@
std::vector<std::any> garbage; // objects destroyed after lock.
std::lock_guard lock(mLock);
- (void)gc_l(garbage);
+ (void)gc(garbage);
mLog.emplace(time, item);
mItemMap[key].emplace(time, item);
return NO_ERROR; // no errors for now.
@@ -104,7 +105,7 @@
std::vector<std::shared_ptr<const mediametrics::Item>> get(
int64_t startTime = 0, int64_t endTime = INT64_MAX) const {
std::lock_guard lock(mLock);
- return getItemsInRange_l(mLog, startTime, endTime);
+ return getItemsInRange(mLog, startTime, endTime);
}
/**
@@ -116,7 +117,7 @@
std::lock_guard lock(mLock);
auto mapIt = mItemMap.find(key);
if (mapIt == mItemMap.end()) return {};
- return getItemsInRange_l(mapIt->second, startTime, endTime);
+ return getItemsInRange(mapIt->second, startTime, endTime);
}
/**
@@ -204,7 +205,6 @@
return { ss.str(), lines - ll };
}
- // GUARDED_BY mLock
/**
* Garbage collects if the TimeMachine size exceeds the high water mark.
*
@@ -213,7 +213,7 @@
*
* \return true if garbage collection was done.
*/
- bool gc_l(std::vector<std::any>& garbage) {
+ bool gc(std::vector<std::any>& garbage) REQUIRES(mLock) {
if (mLog.size() < mHighWaterMark) return false;
ALOGD("%s: garbage collection", __func__);
@@ -268,7 +268,7 @@
return true;
}
- static std::vector<std::shared_ptr<const mediametrics::Item>> getItemsInRange_l(
+ static std::vector<std::shared_ptr<const mediametrics::Item>> getItemsInRange(
const MapTimeItem& map,
int64_t startTime = 0, int64_t endTime = INT64_MAX) {
auto it = map.lower_bound(startTime);
@@ -289,11 +289,8 @@
mutable std::mutex mLock;
- // GUARDED_BY mLock
- MapTimeItem mLog;
-
- // GUARDED_BY mLock
- std::map<std::string /* item_key */, MapTimeItem> mItemMap;
+ MapTimeItem mLog GUARDED_BY(mLock);
+ std::map<std::string /* item_key */, MapTimeItem> mItemMap GUARDED_BY(mLock);
};
} // namespace android::mediametrics