Merge "Add non-treble IOMXStore interface"
diff --git a/drm/libmediadrm/Android.bp b/drm/libmediadrm/Android.bp
index 1dd5139..f906564 100644
--- a/drm/libmediadrm/Android.bp
+++ b/drm/libmediadrm/Android.bp
@@ -13,16 +13,23 @@
"IDrm.cpp",
"IDrmClient.cpp",
"IMediaDrmService.cpp",
+ "PluginMetricsReporting.cpp",
"SharedLibrary.cpp",
"DrmHal.cpp",
"CryptoHal.cpp",
+ "protos/plugin_metrics.proto",
],
+ proto: {
+ type: "lite",
+ },
+
shared_libs: [
"libbinder",
"libcutils",
"libdl",
"liblog",
+ "libmediametrics",
"libmediautils",
"libstagefright_foundation",
"libutils",
diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp
index 386546f..3150e3c 100644
--- a/drm/libmediadrm/DrmHal.cpp
+++ b/drm/libmediadrm/DrmHal.cpp
@@ -30,6 +30,7 @@
#include <media/DrmHal.h>
#include <media/DrmSessionClientInterface.h>
#include <media/DrmSessionManager.h>
+#include <media/PluginMetricsReporting.h>
#include <media/drm/DrmAPI.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AString.h>
@@ -194,7 +195,18 @@
mInitCheck((mFactories.size() == 0) ? ERROR_UNSUPPORTED : NO_INIT) {
}
+void DrmHal::closeOpenSessions() {
+ if (mPlugin != NULL) {
+ for (size_t i = 0; i < mOpenSessions.size(); i++) {
+ mPlugin->closeSession(toHidlVec(mOpenSessions[i]));
+ DrmSessionManager::Instance()->removeSession(mOpenSessions[i]);
+ }
+ }
+ mOpenSessions.clear();
+}
+
DrmHal::~DrmHal() {
+ closeOpenSessions();
DrmSessionManager::Instance()->removeDrm(mDrmSessionClient);
}
@@ -405,11 +417,12 @@
status_t DrmHal::destroyPlugin() {
Mutex::Autolock autoLock(mLock);
-
if (mInitCheck != OK) {
return mInitCheck;
}
+ closeOpenSessions();
+ reportMetrics();
setListener(NULL);
if (mPlugin != NULL) {
mPlugin->setListener(NULL);
@@ -461,6 +474,7 @@
if (err == OK) {
DrmSessionManager::Instance()->addSession(getCallingPid(),
mDrmSessionClient, sessionId);
+ mOpenSessions.push(sessionId);
}
return err;
}
@@ -475,7 +489,14 @@
Status status = mPlugin->closeSession(toHidlVec(sessionId));
if (status == Status::OK) {
DrmSessionManager::Instance()->removeSession(sessionId);
+ for (size_t i = 0; i < mOpenSessions.size(); i++) {
+ if (mOpenSessions[i] == sessionId) {
+ mOpenSessions.removeAt(i);
+ break;
+ }
+ }
}
+ reportMetrics();
return toStatusT(status);
}
@@ -727,6 +748,12 @@
status_t DrmHal::getPropertyString(String8 const &name, String8 &value ) const {
Mutex::Autolock autoLock(mLock);
+ return getPropertyStringInternal(name, value);
+}
+
+status_t DrmHal::getPropertyStringInternal(String8 const &name, String8 &value) const {
+ // This function is internal to the class and should only be called while
+ // mLock is already held.
if (mInitCheck != OK) {
return mInitCheck;
@@ -748,6 +775,12 @@
status_t DrmHal::getPropertyByteArray(String8 const &name, Vector<uint8_t> &value ) const {
Mutex::Autolock autoLock(mLock);
+ return getPropertyByteArrayInternal(name, value);
+}
+
+status_t DrmHal::getPropertyByteArrayInternal(String8 const &name, Vector<uint8_t> &value ) const {
+ // This function is internal to the class and should only be called while
+ // mLock is already held.
if (mInitCheck != OK) {
return mInitCheck;
@@ -962,6 +995,7 @@
void DrmHal::binderDied(const wp<IBinder> &the_late_who __unused)
{
Mutex::Autolock autoLock(mLock);
+ closeOpenSessions();
setListener(NULL);
if (mPlugin != NULL) {
mPlugin->setListener(NULL);
@@ -980,4 +1014,20 @@
}
}
+void DrmHal::reportMetrics() const
+{
+ Vector<uint8_t> metrics;
+ String8 vendor;
+ String8 description;
+ if (getPropertyStringInternal(String8("vendor"), vendor) == OK &&
+ getPropertyStringInternal(String8("description"), description) == OK &&
+ getPropertyByteArrayInternal(String8("metrics"), metrics) == OK) {
+ status_t res = android::reportDrmPluginMetrics(
+ metrics, vendor, description);
+ if (res != OK) {
+ ALOGE("Metrics were retrieved but could not be reported: %i", res);
+ }
+ }
+}
+
} // namespace android
diff --git a/drm/libmediadrm/PluginMetricsReporting.cpp b/drm/libmediadrm/PluginMetricsReporting.cpp
new file mode 100644
index 0000000..a9302ea
--- /dev/null
+++ b/drm/libmediadrm/PluginMetricsReporting.cpp
@@ -0,0 +1,122 @@
+/*
+ * 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 "PluginMetricsReporting"
+#include <utils/Log.h>
+
+#include <media/PluginMetricsReporting.h>
+
+#include <media/MediaAnalyticsItem.h>
+
+#include "protos/plugin_metrics.pb.h"
+
+namespace android {
+
+namespace {
+
+using android::drm_metrics::MetricsGroup;
+using android::drm_metrics::MetricsGroup_Metric;
+using android::drm_metrics::MetricsGroup_Metric_MetricValue;
+
+const char* const kParentAttribute = "/parent/external";
+
+status_t reportMetricsGroup(const MetricsGroup& metricsGroup,
+ const String8& batchName,
+ const int64_t* parentId) {
+ MediaAnalyticsItem analyticsItem(batchName.c_str());
+ analyticsItem.generateSessionID();
+ int64_t sessionId = analyticsItem.getSessionID();
+ if (parentId != NULL) {
+ analyticsItem.setInt64(kParentAttribute, *parentId);
+ }
+
+ for (int i = 0; i < metricsGroup.metric_size(); ++i) {
+ const MetricsGroup_Metric& metric = metricsGroup.metric(i);
+ if (!metric.has_name()) {
+ ALOGE("Metric with no name.");
+ return BAD_VALUE;
+ }
+
+ if (!metric.has_value()) {
+ ALOGE("Metric with no value.");
+ return BAD_VALUE;
+ }
+
+ const MetricsGroup_Metric_MetricValue& value = metric.value();
+ if (value.has_int_value()) {
+ analyticsItem.setInt64(metric.name().c_str(),
+ value.int_value());
+ } else if (value.has_double_value()) {
+ analyticsItem.setDouble(metric.name().c_str(),
+ value.double_value());
+ } else if (value.has_string_value()) {
+ analyticsItem.setCString(metric.name().c_str(),
+ value.string_value().c_str());
+ } else {
+ ALOGE("Metric Value with no actual value.");
+ return BAD_VALUE;
+ }
+ }
+
+ analyticsItem.setFinalized(true);
+ analyticsItem.selfrecord();
+
+ for (int i = 0; i < metricsGroup.metric_sub_group_size(); ++i) {
+ const MetricsGroup& subGroup = metricsGroup.metric_sub_group(i);
+ status_t res = reportMetricsGroup(subGroup, batchName, &sessionId);
+ if (res != OK) {
+ return res;
+ }
+ }
+
+ return OK;
+}
+
+String8 sanitize(const String8& input) {
+ // Filters the input string down to just alphanumeric characters.
+ String8 output;
+ for (size_t i = 0; i < input.size(); ++i) {
+ char candidate = input[i];
+ if ((candidate >= 'a' && candidate <= 'z') ||
+ (candidate >= 'A' && candidate <= 'Z') ||
+ (candidate >= '0' && candidate <= '9')) {
+ output.append(&candidate, 1);
+ }
+ }
+ return output;
+}
+
+} // namespace
+
+status_t reportDrmPluginMetrics(const Vector<uint8_t>& serializedMetrics,
+ const String8& vendor,
+ const String8& description) {
+ MetricsGroup root_metrics_group;
+ if (!root_metrics_group.ParseFromArray(serializedMetrics.array(),
+ serializedMetrics.size())) {
+ ALOGE("Failure to parse.");
+ return BAD_VALUE;
+ }
+
+ String8 name = String8::format("drm.vendor.%s.%s",
+ sanitize(vendor).c_str(),
+ sanitize(description).c_str());
+
+ return reportMetricsGroup(root_metrics_group, name, NULL);
+}
+
+} // namespace android
diff --git a/drm/libmediadrm/protos/plugin_metrics.proto b/drm/libmediadrm/protos/plugin_metrics.proto
new file mode 100644
index 0000000..2d26f14
--- /dev/null
+++ b/drm/libmediadrm/protos/plugin_metrics.proto
@@ -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.
+ */
+
+syntax = "proto2";
+
+package android.drm_metrics;
+
+// need this if we are using libprotobuf-cpp-2.3.0-lite
+option optimize_for = LITE_RUNTIME;
+
+// The MetricsGroup is a collection of metric name/value pair instances
+// that can be serialized and provided to a caller.
+message MetricsGroup {
+ message Metric {
+ message MetricValue {
+ // Exactly one of the following values must be set.
+ optional int64 int_value = 1;
+ optional double double_value = 2;
+ optional string string_value = 3;
+ }
+
+ // The name of the metric. Must be valid UTF-8. Required.
+ optional string name = 1;
+
+ // The value of the metric. Required.
+ optional MetricValue value = 2;
+ }
+
+ // The list of name/value pairs of metrics.
+ repeated Metric metric = 1;
+
+ // Allow multiple sub groups of metrics.
+ repeated MetricsGroup metric_sub_group = 2;
+}
diff --git a/drm/mediacas/plugins/clearkey/tests/Android.mk b/drm/mediacas/plugins/clearkey/tests/Android.mk
index 5418c1d..e1545af 100644
--- a/drm/mediacas/plugins/clearkey/tests/Android.mk
+++ b/drm/mediacas/plugins/clearkey/tests/Android.mk
@@ -21,6 +21,7 @@
ClearKeyFetcherTest.cpp
LOCAL_MODULE := ClearKeyFetcherTest
+LOCAL_VENDOR_MODULE := true
# LOCAL_LDFLAGS is needed here for the test to use the plugin, because
# the plugin is not in standard library search path. Without this .so
diff --git a/drm/mediadrm/plugins/clearkey/tests/Android.bp b/drm/mediadrm/plugins/clearkey/tests/Android.bp
index ac57d65..0fcfc64 100644
--- a/drm/mediadrm/plugins/clearkey/tests/Android.bp
+++ b/drm/mediadrm/plugins/clearkey/tests/Android.bp
@@ -34,4 +34,5 @@
"libstagefright_foundation",
"libutils",
],
+ header_libs: ["media_plugin_headers"],
}
diff --git a/include/OWNERS b/include/OWNERS
index 0ec7529..3cb6d9c 100644
--- a/include/OWNERS
+++ b/include/OWNERS
@@ -1,5 +1,6 @@
elaurent@google.com
gkasten@android.com
hunga@google.com
+jtinker@google.com
lajos@google.com
marcone@google.com
diff --git a/include/media/PluginMetricsReporting.h b/include/media/PluginMetricsReporting.h
new file mode 120000
index 0000000..7d9a7a0
--- /dev/null
+++ b/include/media/PluginMetricsReporting.h
@@ -0,0 +1 @@
+../../media/libmedia/include/media/PluginMetricsReporting.h
\ No newline at end of file
diff --git a/media/OWNERS b/media/OWNERS
index cd4afb4..1605efd 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -1,5 +1,18 @@
+chz@google.com
+dwkang@google.com
+elaurent@google.com
+essick@google.com
hunga@google.com
+jmtrivi@google.com
+krocard@google.com
lajos@google.com
marcone@google.com
+mnaganov@google.com
+pawin@google.com
+philburk@google.com
+pmclean@google.com
+rachad@google.com
+rago@google.com
robertshih@google.com
wjia@google.com
+wonsik@google.com
diff --git a/media/libaaudio/OWNERS b/media/libaaudio/OWNERS
new file mode 100644
index 0000000..f4d51f9
--- /dev/null
+++ b/media/libaaudio/OWNERS
@@ -0,0 +1 @@
+philburk@google.com
diff --git a/media/libaaudio/examples/loopback/src/loopback.cpp b/media/libaaudio/examples/loopback/src/loopback.cpp
index 45a3beb..57d45cd 100644
--- a/media/libaaudio/examples/loopback/src/loopback.cpp
+++ b/media/libaaudio/examples/loopback/src/loopback.cpp
@@ -17,6 +17,7 @@
// Play an impulse and then record it.
// Measure the round trip latency.
+#include <algorithm>
#include <assert.h>
#include <cctype>
#include <math.h>
@@ -25,11 +26,13 @@
#include <unistd.h>
#include <aaudio/AAudio.h>
+#include <aaudio/AAudioTesting.h>
-#define INPUT_PEAK_THRESHOLD 0.1f
-#define SILENCE_FRAMES 10000
+// Tag for machine readable results as property = value pairs
+#define RESULT_TAG "RESULT: "
#define SAMPLE_RATE 48000
-#define NUM_SECONDS 7
+#define NUM_SECONDS 5
+#define NUM_INPUT_CHANNELS 1
#define FILENAME "/data/oboe_input.raw"
#define NANOS_PER_MICROSECOND ((int64_t)1000)
@@ -37,12 +40,172 @@
#define MILLIS_PER_SECOND 1000
#define NANOS_PER_SECOND (NANOS_PER_MILLISECOND * MILLIS_PER_SECOND)
-class AudioRecorder
+#define MAX_ZEROTH_PARTIAL_BINS 40
+
+static const float s_Impulse[] = {
+ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, // silence on each side of the impulse
+ 0.5f, 0.9f, 0.0f, -0.9f, -0.5f, // bipolar
+ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
+
+
+static double calculateCorrelation(const float *a,
+ const float *b,
+ int windowSize)
+{
+ double correlation = 0.0;
+ double sumProducts = 0.0;
+ double sumSquares = 0.0;
+
+ // Correlate a against b.
+ for (int i = 0; i < windowSize; i++) {
+ float s1 = a[i];
+ float s2 = b[i];
+ // Use a normalized cross-correlation.
+ sumProducts += s1 * s2;
+ sumSquares += ((s1 * s1) + (s2 * s2));
+ }
+
+ if (sumSquares >= 0.00000001) {
+ correlation = (float) (2.0 * sumProducts / sumSquares);
+ }
+ return correlation;
+}
+
+static int calculateCorrelations(const float *haystack, int haystackSize,
+ const float *needle, int needleSize,
+ float *results, int resultSize)
+{
+ int ic;
+ int maxCorrelations = haystackSize - needleSize;
+ int numCorrelations = std::min(maxCorrelations, resultSize);
+
+ for (ic = 0; ic < numCorrelations; ic++) {
+ double correlation = calculateCorrelation(&haystack[ic], needle, needleSize);
+ results[ic] = correlation;
+ }
+
+ return numCorrelations;
+}
+
+/*==========================================================================================*/
+/**
+ * Scan until we get a correlation of a single scan that goes over the tolerance level,
+ * peaks then drops back down.
+ */
+static double findFirstMatch(const float *haystack, int haystackSize,
+ const float *needle, int needleSize, double threshold )
+{
+ int ic;
+ // How many correlations can we calculate?
+ int numCorrelations = haystackSize - needleSize;
+ double maxCorrelation = 0.0;
+ int peakIndex = -1;
+ double location = -1.0;
+
+ for (ic = 0; ic < numCorrelations; ic++) {
+ double correlation = calculateCorrelation(&haystack[ic], needle, needleSize);
+
+ if( (correlation > maxCorrelation) ) {
+ maxCorrelation = correlation;
+ peakIndex = ic;
+ }
+
+ //printf("PaQa_FindFirstMatch: ic = %4d, correlation = %8f, maxSum = %8f\n",
+ // ic, correlation, maxSum );
+ // Are we past what we were looking for?
+ if((maxCorrelation > threshold) && (correlation < 0.5 * maxCorrelation)) {
+ location = peakIndex;
+ break;
+ }
+ }
+
+ return location;
+}
+
+typedef struct LatencyReport_s {
+ double latencyInFrames;
+ double confidence;
+} LatencyReport;
+
+// Apply a technique similar to Harmonic Product Spectrum Analysis to find echo fundamental.
+// Using first echo instead of the original impulse for a better match.
+int measureLatencyFromEchos(const float *haystack, int haystackSize,
+ const float *needle, int needleSize,
+ LatencyReport *report) {
+ double threshold = 0.1;
+
+ // Find first peak
+ int first = (int) (findFirstMatch(haystack,
+ haystackSize,
+ needle,
+ needleSize,
+ threshold) + 0.5);
+
+ // Use first echo as the needle for the other echos because
+ // it will be more similar.
+ needle = &haystack[first];
+ int again = (int) (findFirstMatch(haystack,
+ haystackSize,
+ needle,
+ needleSize,
+ threshold) + 0.5);
+
+ printf("first = %d, again at %d\n", first, again);
+ first = again;
+
+ // Allocate results array
+ int remaining = haystackSize - first;
+ int generous = 48000 * 2;
+ int numCorrelations = std::min(remaining, generous);
+ float *correlations = new float[numCorrelations];
+ float *harmonicSums = new float[numCorrelations](); // cleared to zero
+
+ // Generate correlation for every position.
+ numCorrelations = calculateCorrelations(&haystack[first], remaining,
+ needle, needleSize,
+ correlations, numCorrelations);
+
+ // Add higher harmonics mapped onto lower harmonics.
+ // This reinforces the "fundamental" echo.
+ const int numEchoes = 10;
+ for (int partial = 1; partial < numEchoes; partial++) {
+ for (int i = 0; i < numCorrelations; i++) {
+ harmonicSums[i / partial] += correlations[i] / partial;
+ }
+ }
+
+ // Find highest peak in correlation array.
+ float maxCorrelation = 0.0;
+ float sumOfPeaks = 0.0;
+ int peakIndex = 0;
+ const int skip = MAX_ZEROTH_PARTIAL_BINS; // skip low bins
+ for (int i = skip; i < numCorrelations; i++) {
+ if (harmonicSums[i] > maxCorrelation) {
+ maxCorrelation = harmonicSums[i];
+ sumOfPeaks += maxCorrelation;
+ peakIndex = i;
+ printf("maxCorrelation = %f at %d\n", maxCorrelation, peakIndex);
+ }
+ }
+
+ report->latencyInFrames = peakIndex;
+ if (sumOfPeaks < 0.0001) {
+ report->confidence = 0.0;
+ } else {
+ report->confidence = maxCorrelation / sumOfPeaks;
+ }
+
+ delete[] correlations;
+ delete[] harmonicSums;
+ return 0;
+}
+
+class AudioRecording
{
public:
- AudioRecorder() {
+ AudioRecording() {
}
- ~AudioRecorder() {
+ ~AudioRecording() {
delete[] mData;
}
@@ -52,7 +215,8 @@
mMaxFrames = maxFrames;
}
- void record(int16_t *inputData, int inputChannelCount, int numFrames) {
+ // Write SHORT data from the first channel.
+ int write(int16_t *inputData, int inputChannelCount, int numFrames) {
// stop at end of buffer
if ((mFrameCounter + numFrames) > mMaxFrames) {
numFrames = mMaxFrames - mFrameCounter;
@@ -60,9 +224,11 @@
for (int i = 0; i < numFrames; i++) {
mData[mFrameCounter++] = inputData[i * inputChannelCount] * (1.0f / 32768);
}
+ return numFrames;
}
- void record(float *inputData, int inputChannelCount, int numFrames) {
+ // Write FLOAT data from the first channel.
+ int write(float *inputData, int inputChannelCount, int numFrames) {
// stop at end of buffer
if ((mFrameCounter + numFrames) > mMaxFrames) {
numFrames = mMaxFrames - mFrameCounter;
@@ -70,162 +236,253 @@
for (int i = 0; i < numFrames; i++) {
mData[mFrameCounter++] = inputData[i * inputChannelCount];
}
+ return numFrames;
}
- int save(const char *fileName) {
+ int size() {
+ return mFrameCounter;
+ }
+
+ float *getData() {
+ return mData;
+ }
+
+ int save(const char *fileName, bool writeShorts = true) {
+ int written = 0;
+ const int chunkSize = 64;
FILE *fid = fopen(fileName, "wb");
if (fid == NULL) {
- return errno;
+ return -errno;
}
- int written = fwrite(mData, sizeof(float), mFrameCounter, fid);
+
+ if (writeShorts) {
+ int16_t buffer[chunkSize];
+ int32_t framesLeft = mFrameCounter;
+ int32_t cursor = 0;
+ while (framesLeft) {
+ int32_t framesToWrite = framesLeft < chunkSize ? framesLeft : chunkSize;
+ for (int i = 0; i < framesToWrite; i++) {
+ buffer[i] = (int16_t) (mData[cursor++] * 32767);
+ }
+ written += fwrite(buffer, sizeof(int16_t), framesToWrite, fid);
+ framesLeft -= framesToWrite;
+ }
+ } else {
+ written = fwrite(mData, sizeof(float), mFrameCounter, fid);
+ }
fclose(fid);
return written;
}
private:
- float *mData = NULL;
+ float *mData = nullptr;
int32_t mFrameCounter = 0;
int32_t mMaxFrames = 0;
};
// ====================================================================================
-// ========================= Loopback Processor =======================================
-// ====================================================================================
class LoopbackProcessor {
public:
+ virtual ~LoopbackProcessor() = default;
- // Calculate mean and standard deviation.
- double calculateAverageLatency(double *deviation) {
- if (mLatencyCount <= 0) {
- return -1.0;
- }
- double sum = 0.0;
- for (int i = 0; i < mLatencyCount; i++) {
- sum += mLatencyArray[i];
- }
- double average = sum / mLatencyCount;
- sum = 0.0;
- for (int i = 0; i < mLatencyCount; i++) {
- double error = average - mLatencyArray[i];
- sum += error * error; // squared
- }
- *deviation = sqrt(sum / mLatencyCount);
- return average;
+ virtual void process(float *inputData, int inputChannelCount,
+ float *outputData, int outputChannelCount,
+ int numFrames) = 0;
+
+
+ virtual void report() = 0;
+
+ void setSampleRate(int32_t sampleRate) {
+ mSampleRate = sampleRate;
}
- float getMaxAmplitude() const { return mMaxAmplitude; }
- int getMeasurementCount() const { return mLatencyCount; }
- float getAverageAmplitude() const { return mAmplitudeTotal / mAmplitudeCount; }
-
- // TODO Convert this to a feedback circuit and then use auto-correlation to measure the period.
- void process(float *inputData, int inputChannelCount,
- float *outputData, int outputChannelCount,
- int numFrames) {
- (void) outputChannelCount;
-
- // Measure peak and average amplitude.
- for (int i = 0; i < numFrames; i++) {
- float sample = inputData[i * inputChannelCount];
- if (sample > mMaxAmplitude) {
- mMaxAmplitude = sample;
- }
- if (sample < 0) {
- sample = 0 - sample;
- }
- mAmplitudeTotal += sample;
- mAmplitudeCount++;
- }
-
- // Clear output.
- memset(outputData, 0, numFrames * outputChannelCount * sizeof(float));
-
- // Wait a while between hearing the pulse and starting a new one.
- if (mState == STATE_SILENT) {
- mCounter += numFrames;
- if (mCounter > SILENCE_FRAMES) {
- //printf("LoopbackProcessor send impulse, burst #%d\n", mBurstCounter);
- // copy impulse
- for (float sample : mImpulse) {
- *outputData = sample;
- outputData += outputChannelCount;
- }
- mState = STATE_LISTENING;
- mCounter = 0;
- }
- }
- // Start listening as soon as we send the impulse.
- if (mState == STATE_LISTENING) {
- for (int i = 0; i < numFrames; i++) {
- float sample = inputData[i * inputChannelCount];
- if (sample >= INPUT_PEAK_THRESHOLD) {
- mLatencyArray[mLatencyCount++] = mCounter;
- if (mLatencyCount >= MAX_LATENCY_VALUES) {
- mState = STATE_DONE;
- } else {
- mState = STATE_SILENT;
- }
- mCounter = 0;
- break;
- } else {
- mCounter++;
- }
- }
- }
+ int32_t getSampleRate() {
+ return mSampleRate;
}
- void echo(float *inputData, int inputChannelCount,
- float *outputData, int outputChannelCount,
- int numFrames) {
- int channelsValid = (inputChannelCount < outputChannelCount)
- ? inputChannelCount : outputChannelCount;
- for (int i = 0; i < numFrames; i++) {
- int ic;
- for (ic = 0; ic < channelsValid; ic++) {
- outputData[ic] = inputData[ic];
- }
- for (ic = 0; ic < outputChannelCount; ic++) {
- outputData[ic] = 0;
- }
- inputData += inputChannelCount;
- outputData += outputChannelCount;
- }
- }
private:
- enum {
- STATE_SILENT,
- STATE_LISTENING,
- STATE_DONE
- };
-
- enum {
- MAX_LATENCY_VALUES = 64
- };
-
- int mState = STATE_SILENT;
- int32_t mCounter = 0;
- int32_t mLatencyArray[MAX_LATENCY_VALUES];
- int32_t mLatencyCount = 0;
- float mMaxAmplitude = 0;
- float mAmplitudeTotal = 0;
- int32_t mAmplitudeCount = 0;
- static const float mImpulse[5];
+ int32_t mSampleRate = SAMPLE_RATE;
};
-const float LoopbackProcessor::mImpulse[5] = {0.5f, 0.9f, 0.0f, -0.9f, -0.5f};
+
+// ====================================================================================
+class EchoAnalyzer : public LoopbackProcessor {
+public:
+
+ EchoAnalyzer() : LoopbackProcessor() {
+ audioRecorder.allocate(NUM_SECONDS * SAMPLE_RATE);
+ }
+
+ void setGain(float gain) {
+ mGain = gain;
+ }
+
+ float getGain() {
+ return mGain;
+ }
+
+ void report() override {
+
+ const float *needle = s_Impulse;
+ int needleSize = (int)(sizeof(s_Impulse) / sizeof(float));
+ float *haystack = audioRecorder.getData();
+ int haystackSize = audioRecorder.size();
+ int result = measureLatencyFromEchos(haystack, haystackSize,
+ needle, needleSize,
+ &latencyReport);
+ if (latencyReport.confidence < 0.01) {
+ printf(" ERROR - confidence too low = %f\n", latencyReport.confidence);
+ } else {
+ double latencyMillis = 1000.0 * latencyReport.latencyInFrames / getSampleRate();
+ printf(RESULT_TAG "latency.frames = %8.2f\n", latencyReport.latencyInFrames);
+ printf(RESULT_TAG "latency.msec = %8.2f\n", latencyMillis);
+ printf(RESULT_TAG "latency.confidence = %8.6f\n", latencyReport.confidence);
+ }
+ }
+
+ void process(float *inputData, int inputChannelCount,
+ float *outputData, int outputChannelCount,
+ int numFrames) override {
+ int channelsValid = std::min(inputChannelCount, outputChannelCount);
+
+ audioRecorder.write(inputData, inputChannelCount, numFrames);
+
+ if (mLoopCounter < mLoopStart) {
+ // Output silence at the beginning.
+ for (int i = 0; i < numFrames; i++) {
+ int ic;
+ for (ic = 0; ic < outputChannelCount; ic++) {
+ outputData[ic] = 0;
+ }
+ inputData += inputChannelCount;
+ outputData += outputChannelCount;
+ }
+ } else if (mLoopCounter == mLoopStart) {
+ // Send a bipolar impulse that we can easily detect.
+ for (float sample : s_Impulse) {
+ *outputData = sample;
+ outputData += outputChannelCount;
+ }
+ } else {
+ // Echo input to output.
+ for (int i = 0; i < numFrames; i++) {
+ int ic;
+ for (ic = 0; ic < channelsValid; ic++) {
+ outputData[ic] = inputData[ic] * mGain;
+ }
+ for (; ic < outputChannelCount; ic++) {
+ outputData[ic] = 0;
+ }
+ inputData += inputChannelCount;
+ outputData += outputChannelCount;
+ }
+ }
+
+ mLoopCounter++;
+ }
+
+private:
+ int mLoopCounter = 0;
+ int mLoopStart = 1000;
+ float mGain = 1.0f;
+
+ AudioRecording audioRecorder;
+ LatencyReport latencyReport;
+};
+
+
+// ====================================================================================
+class SineAnalyzer : public LoopbackProcessor {
+public:
+
+ void report() override {
+ double magnitude = calculateMagnitude();
+ printf("sine magnitude = %7.5f\n", magnitude);
+ printf("sine frames = %7d\n", mFrameCounter);
+ printf("sine frequency = %7.1f Hz\n", mFrequency);
+ }
+
+ double calculateMagnitude(double *phasePtr = NULL) {
+ if (mFrameCounter == 0) {
+ return 0.0;
+ }
+ double sinMean = mSinAccumulator / mFrameCounter;
+ double cosMean = mCosAccumulator / mFrameCounter;
+ double magnitude = 2.0 * sqrt( (sinMean * sinMean) + (cosMean * cosMean ));
+ if( phasePtr != NULL )
+ {
+ double phase = atan2( sinMean, cosMean );
+ *phasePtr = phase;
+ }
+ return magnitude;
+ }
+
+ void process(float *inputData, int inputChannelCount,
+ float *outputData, int outputChannelCount,
+ int numFrames) override {
+ double phaseIncrement = 2.0 * M_PI * mFrequency / getSampleRate();
+
+ for (int i = 0; i < numFrames; i++) {
+ // Multiply input by sine/cosine
+ float sample = inputData[i * inputChannelCount];
+ float sinOut = sinf(mPhase);
+ mSinAccumulator += sample * sinOut;
+ mCosAccumulator += sample * cosf(mPhase);
+ // Advance and wrap phase
+ mPhase += phaseIncrement;
+ if (mPhase > (2.0 * M_PI)) {
+ mPhase -= (2.0 * M_PI);
+ }
+
+ // Output sine wave so we can measure it.
+ outputData[i * outputChannelCount] = sinOut;
+ }
+ mFrameCounter += numFrames;
+
+ double magnitude = calculateMagnitude();
+ if (mWaiting) {
+ if (magnitude < 0.001) {
+ // discard silence
+ mFrameCounter = 0;
+ mSinAccumulator = 0.0;
+ mCosAccumulator = 0.0;
+ } else {
+ mWaiting = false;
+ }
+ }
+ };
+
+ void setFrequency(int32_t frequency) {
+ mFrequency = frequency;
+ }
+
+ int32_t getFrequency() {
+ return mFrequency;
+ }
+
+private:
+ double mFrequency = 300.0;
+ double mPhase = 0.0;
+ int32_t mFrameCounter = 0;
+ double mSinAccumulator = 0.0;
+ double mCosAccumulator = 0.0;
+ bool mWaiting = true;
+};
// TODO make this a class that manages its own buffer allocation
struct LoopbackData {
- AAudioStream *inputStream = nullptr;
- int32_t inputFramesMaximum = 0;
- int16_t *inputData = nullptr;
- float *conversionBuffer = nullptr;
- int32_t actualInputChannelCount = 0;
- int32_t actualOutputChannelCount = 0;
- int32_t inputBuffersToDiscard = 10;
+ AAudioStream *inputStream = nullptr;
+ int32_t inputFramesMaximum = 0;
+ int16_t *inputData = nullptr;
+ float *conversionBuffer = nullptr;
+ int32_t actualInputChannelCount = 0;
+ int32_t actualOutputChannelCount = 0;
+ int32_t inputBuffersToDiscard = 10;
- aaudio_result_t inputError;
- LoopbackProcessor loopbackProcessor;
- AudioRecorder audioRecorder;
+ aaudio_result_t inputError;
+ SineAnalyzer sineAnalyzer;
+ EchoAnalyzer echoAnalyzer;
+ LoopbackProcessor *loopbackProcessor;
};
static void convertPcm16ToFloat(const int16_t *source,
@@ -248,6 +505,7 @@
int32_t numFrames
) {
(void) outputStream;
+ aaudio_data_callback_result_t result = AAUDIO_CALLBACK_RESULT_CONTINUE;
LoopbackData *myData = (LoopbackData *) userData;
float *outputData = (float *) audioData;
@@ -266,6 +524,7 @@
numFrames, 0);
if (framesRead < 0) {
myData->inputError = framesRead;
+ result = AAUDIO_CALLBACK_RESULT_STOP;
} else if (framesRead > 0) {
myData->inputBuffersToDiscard--;
}
@@ -275,16 +534,13 @@
numFrames, 0);
if (framesRead < 0) {
myData->inputError = framesRead;
+ result = AAUDIO_CALLBACK_RESULT_STOP;
} else if (framesRead > 0) {
- // Process valid input data.
- myData->audioRecorder.record(myData->inputData,
- myData->actualInputChannelCount,
- framesRead);
int32_t numSamples = framesRead * myData->actualInputChannelCount;
convertPcm16ToFloat(myData->inputData, myData->conversionBuffer, numSamples);
- myData->loopbackProcessor.process(myData->conversionBuffer,
+ myData->loopbackProcessor->process(myData->conversionBuffer,
myData->actualInputChannelCount,
outputData,
myData->actualOutputChannelCount,
@@ -292,17 +548,25 @@
}
}
- return AAUDIO_CALLBACK_RESULT_CONTINUE;
+ return result;
}
+
static void usage() {
- printf("loopback: -b{burstsPerBuffer} -p{outputPerfMode} -P{inputPerfMode}\n");
- printf(" -b{burstsPerBuffer} for example 2 for double buffered\n");
- printf(" -p{outputPerfMode} set output AAUDIO_PERFORMANCE_MODE*\n");
- printf(" -P{inputPerfMode} set input AAUDIO_PERFORMANCE_MODE*\n");
+ printf("loopback: -n{numBursts} -p{outPerf} -P{inPerf} -t{test} -g{gain} -f{freq}\n");
+ printf(" -c{inputChannels}\n");
+ printf(" -f{freq} sine frequency\n");
+ printf(" -g{gain} recirculating loopback gain\n");
+ printf(" -m enable MMAP mode\n");
+ printf(" -n{numBursts} buffer size, for example 2 for double buffered\n");
+ printf(" -p{outPerf} set output AAUDIO_PERFORMANCE_MODE*\n");
+ printf(" -P{inPerf} set input AAUDIO_PERFORMANCE_MODE*\n");
printf(" n for _NONE\n");
printf(" l for _LATENCY\n");
printf(" p for _POWER_SAVING;\n");
+ printf(" -t{test} select test mode\n");
+ printf(" m for sine magnitude\n");
+ printf(" e for echo latency (default)\n");
printf("For example: loopback -b2 -pl -Pn\n");
}
@@ -320,12 +584,34 @@
mode = AAUDIO_PERFORMANCE_MODE_POWER_SAVING;
break;
default:
- printf("ERROR invalue performance mode %c\n", c);
+ printf("ERROR in value performance mode %c\n", c);
break;
}
return mode;
}
+enum {
+ TEST_SINE_MAGNITUDE = 0,
+ TEST_ECHO_LATENCY,
+};
+
+static int parseTestMode(char c) {
+ int testMode = TEST_ECHO_LATENCY;
+ c = tolower(c);
+ switch (c) {
+ case 'm':
+ testMode = TEST_SINE_MAGNITUDE;
+ break;
+ case 'e':
+ testMode = TEST_ECHO_LATENCY;
+ break;
+ default:
+ printf("ERROR in value test mode %c\n", c);
+ break;
+ }
+ return testMode;
+}
+
// ====================================================================================
// TODO break up this large main() function into smaller functions
int main(int argc, const char **argv)
@@ -334,7 +620,7 @@
LoopbackData loopbackData;
AAudioStream *outputStream = nullptr;
- const int requestedInputChannelCount = 1;
+ int requestedInputChannelCount = NUM_INPUT_CHANNELS;
const int requestedOutputChannelCount = AAUDIO_UNSPECIFIED;
const int requestedSampleRate = SAMPLE_RATE;
int actualSampleRate = 0;
@@ -342,6 +628,9 @@
const aaudio_format_t requestedOutputFormat = AAUDIO_FORMAT_PCM_FLOAT;
aaudio_format_t actualInputFormat;
aaudio_format_t actualOutputFormat;
+ int testMode = TEST_ECHO_LATENCY;
+ double frequency = 1000.0;
+ double gain = 1.0;
const aaudio_sharing_mode_t requestedSharingMode = AAUDIO_SHARING_MODE_EXCLUSIVE;
//const aaudio_sharing_mode_t requestedSharingMode = AAUDIO_SHARING_MODE_SHARED;
@@ -363,7 +652,19 @@
if (arg[0] == '-') {
char option = arg[1];
switch (option) {
- case 'b':
+ case 'c':
+ requestedInputChannelCount = atoi(&arg[2]);
+ break;
+ case 'f':
+ frequency = atof(&arg[2]);
+ break;
+ case 'g':
+ gain = atof(&arg[2]);
+ break;
+ case 'm':
+ AAudio_setMMapPolicy(AAUDIO_POLICY_AUTO);
+ break;
+ case 'n':
burstsPerBuffer = atoi(&arg[2]);
break;
case 'p':
@@ -372,16 +673,35 @@
case 'P':
inputPerformanceLevel = parsePerformanceMode(arg[2]);
break;
+ case 't':
+ testMode = parseTestMode(arg[2]);
+ break;
default:
usage();
+ exit(0);
break;
}
} else {
+ usage();
+ exit(0);
break;
}
}
- loopbackData.audioRecorder.allocate(NUM_SECONDS * SAMPLE_RATE);
+
+ switch(testMode) {
+ case TEST_SINE_MAGNITUDE:
+ loopbackData.sineAnalyzer.setFrequency(frequency);
+ loopbackData.loopbackProcessor = &loopbackData.sineAnalyzer;
+ break;
+ case TEST_ECHO_LATENCY:
+ loopbackData.echoAnalyzer.setGain(gain);
+ loopbackData.loopbackProcessor = &loopbackData.echoAnalyzer;
+ break;
+ default:
+ exit(1);
+ break;
+ }
// Make printf print immediately so that debug info is not stuck
// in a buffer if we hang or crash.
@@ -431,47 +751,68 @@
printf(" channelCount: requested = %d, actual = %d\n", requestedInputChannelCount,
loopbackData.actualInputChannelCount);
printf(" framesPerBurst = %d\n", AAudioStream_getFramesPerBurst(loopbackData.inputStream));
+ printf(" bufferSize = %d\n",
+ AAudioStream_getBufferSizeInFrames(loopbackData.inputStream));
+ printf(" bufferCapacity = %d\n",
+ AAudioStream_getBufferCapacityInFrames(loopbackData.inputStream));
+
+ actualSharingMode = AAudioStream_getSharingMode(loopbackData.inputStream);
+ printf(" sharingMode: requested = %d, actual = %d\n",
+ requestedSharingMode, actualSharingMode);
actualInputFormat = AAudioStream_getFormat(loopbackData.inputStream);
- printf(" dataFormat: requested = %d, actual = %d\n", requestedInputFormat, actualInputFormat);
+ printf(" dataFormat: requested = %d, actual = %d\n",
+ requestedInputFormat, actualInputFormat);
assert(actualInputFormat == AAUDIO_FORMAT_PCM_I16);
+ printf(" is MMAP used? = %s\n", AAudioStream_isMMapUsed(loopbackData.inputStream)
+ ? "yes" : "no");
+
+
printf("Stream OUTPUT ---------------------\n");
// Check to see what kind of stream we actually got.
actualSampleRate = AAudioStream_getSampleRate(outputStream);
printf(" sampleRate: requested = %d, actual = %d\n", requestedSampleRate, actualSampleRate);
+ loopbackData.echoAnalyzer.setSampleRate(actualSampleRate);
loopbackData.actualOutputChannelCount = AAudioStream_getChannelCount(outputStream);
printf(" channelCount: requested = %d, actual = %d\n", requestedOutputChannelCount,
loopbackData.actualOutputChannelCount);
actualSharingMode = AAudioStream_getSharingMode(outputStream);
- printf(" sharingMode: requested = %d, actual = %d\n", requestedSharingMode, actualSharingMode);
+ printf(" sharingMode: requested = %d, actual = %d\n",
+ requestedSharingMode, 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(outputStream);
printf(" framesPerBurst = %d\n", framesPerBurst);
- printf(" bufferCapacity = %d\n", AAudioStream_getBufferCapacityInFrames(outputStream));
-
- actualOutputFormat = AAudioStream_getFormat(outputStream);
- printf(" dataFormat: requested = %d, actual = %d\n", requestedOutputFormat, actualOutputFormat);
- assert(actualOutputFormat == AAUDIO_FORMAT_PCM_FLOAT);
-
- // Allocate a buffer for the audio data.
- loopbackData.inputFramesMaximum = 32 * framesPerBurst;
-
- loopbackData.inputData = new int16_t[loopbackData.inputFramesMaximum * loopbackData.actualInputChannelCount];
- loopbackData.conversionBuffer = new float[loopbackData.inputFramesMaximum *
- loopbackData.actualInputChannelCount];
-
result = AAudioStream_setBufferSizeInFrames(outputStream, burstsPerBuffer * framesPerBurst);
if (result < 0) { // may be positive buffer size
fprintf(stderr, "ERROR - AAudioStream_setBufferSize() returned %d\n", result);
goto finish;
}
- printf("AAudioStream_setBufferSize() actual = %d\n",result);
+ printf(" bufferSize = %d\n", AAudioStream_getBufferSizeInFrames(outputStream));
+ printf(" bufferCapacity = %d\n", AAudioStream_getBufferCapacityInFrames(outputStream));
+
+ actualOutputFormat = AAudioStream_getFormat(outputStream);
+ printf(" dataFormat: requested = %d, actual = %d\n",
+ requestedOutputFormat, actualOutputFormat);
+ assert(actualOutputFormat == AAUDIO_FORMAT_PCM_FLOAT);
+
+ printf(" is MMAP used? = %s\n", AAudioStream_isMMapUsed(outputStream)
+ ? "yes" : "no");
+
+ // Allocate a buffer for the audio data.
+ loopbackData.inputFramesMaximum = 32 * framesPerBurst;
+ loopbackData.inputBuffersToDiscard = 100;
+
+ loopbackData.inputData = new int16_t[loopbackData.inputFramesMaximum
+ * loopbackData.actualInputChannelCount];
+ loopbackData.conversionBuffer = new float[loopbackData.inputFramesMaximum *
+ loopbackData.actualInputChannelCount];
+
// Start output first so input stream runs low.
result = AAudioStream_requestStart(outputStream);
@@ -500,18 +841,13 @@
printf("framesRead = %d\n", (int) AAudioStream_getFramesRead(outputStream));
printf("framesWritten = %d\n", (int) AAudioStream_getFramesWritten(outputStream));
- latency = loopbackData.loopbackProcessor.calculateAverageLatency(&deviation);
- printf("measured peak = %8.5f\n", loopbackData.loopbackProcessor.getMaxAmplitude());
- printf("threshold = %8.5f\n", INPUT_PEAK_THRESHOLD);
- printf("measured average = %8.5f\n", loopbackData.loopbackProcessor.getAverageAmplitude());
- printf("# latency measurements = %d\n", loopbackData.loopbackProcessor.getMeasurementCount());
- printf("measured latency = %8.2f +/- %4.5f frames\n", latency, deviation);
- printf("measured latency = %8.2f msec <===== !!\n", (1000.0 * latency / actualSampleRate));
+ loopbackData.loopbackProcessor->report();
- {
- int written = loopbackData.audioRecorder.save(FILENAME);
- printf("wrote %d samples to %s\n", written, FILENAME);
- }
+// {
+// int written = loopbackData.audioRecorder.save(FILENAME);
+// printf("wrote %d mono samples to %s on Android device\n", written, FILENAME);
+// }
+
finish:
AAudioStream_close(outputStream);
@@ -521,7 +857,13 @@
delete[] outputData;
AAudioStreamBuilder_delete(builder);
- printf("exiting - AAudio result = %d = %s\n", result, AAudio_convertResultToText(result));
- return (result != AAUDIO_OK) ? EXIT_FAILURE : EXIT_SUCCESS;
+ printf(RESULT_TAG "error = %d = %s\n", result, AAudio_convertResultToText(result));
+ if ((result != AAUDIO_OK)) {
+ printf("error %d = %s\n", result, AAudio_convertResultToText(result));
+ return EXIT_FAILURE;
+ } else {
+ printf("SUCCESS\n");
+ return EXIT_SUCCESS;
+ }
}
diff --git a/media/libaaudio/examples/utils/dummy.cpp b/media/libaaudio/examples/utils/dummy.cpp
new file mode 100644
index 0000000..8ef7e36
--- /dev/null
+++ b/media/libaaudio/examples/utils/dummy.cpp
@@ -0,0 +1,5 @@
+/**
+ * Dummy file needed to get Android Studio to scan this folder.
+ */
+
+int g_DoNotUseThisVariable = 0;
diff --git a/media/libaaudio/src/Android.mk b/media/libaaudio/src/Android.mk
index 7131c6c..cfcf27a 100644
--- a/media/libaaudio/src/Android.mk
+++ b/media/libaaudio/src/Android.mk
@@ -32,6 +32,7 @@
core/AudioStream.cpp \
core/AudioStreamBuilder.cpp \
core/AAudioAudio.cpp \
+ core/AAudioStreamParameters.cpp \
legacy/AudioStreamLegacy.cpp \
legacy/AudioStreamRecord.cpp \
legacy/AudioStreamTrack.cpp \
@@ -90,6 +91,7 @@
LOCAL_SRC_FILES = core/AudioStream.cpp \
core/AudioStreamBuilder.cpp \
core/AAudioAudio.cpp \
+ core/AAudioStreamParameters.cpp \
legacy/AudioStreamLegacy.cpp \
legacy/AudioStreamRecord.cpp \
legacy/AudioStreamTrack.cpp \
diff --git a/media/libaaudio/src/binding/AAudioBinderClient.cpp b/media/libaaudio/src/binding/AAudioBinderClient.cpp
index 6464334..a268e49 100644
--- a/media/libaaudio/src/binding/AAudioBinderClient.cpp
+++ b/media/libaaudio/src/binding/AAudioBinderClient.cpp
@@ -57,7 +57,7 @@
, Singleton<AAudioBinderClient>() {
mAAudioClient = new AAudioClient(this);
- ALOGD("AAudioBinderClient() created mAAudioClient = %p", mAAudioClient.get());
+ ALOGV("AAudioBinderClient() created mAAudioClient = %p", mAAudioClient.get());
}
AAudioBinderClient::~AAudioBinderClient() {
@@ -90,8 +90,11 @@
if (binder != 0) {
// Ask for notification if the service dies.
status_t status = binder->linkToDeath(mAAudioClient);
- ALOGD("getAAudioService: linkToDeath(mAAudioClient = %p) returned %d",
- mAAudioClient.get(), status);
+ // TODO review what we should do if this fails
+ if (status != NO_ERROR) {
+ ALOGE("getAAudioService: linkToDeath(mAAudioClient = %p) returned %d",
+ mAAudioClient.get(), status);
+ }
mAAudioService = interface_cast<IAAudioService>(binder);
needToRegister = true;
// Make sure callbacks can be received by mAAudioClient
diff --git a/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp b/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
index 44edb1d..e763934 100644
--- a/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
+++ b/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
@@ -36,17 +36,17 @@
status_t AAudioStreamConfiguration::writeToParcel(Parcel* parcel) const {
status_t status;
- status = parcel->writeInt32(mDeviceId);
+ status = parcel->writeInt32(getDeviceId());
if (status != NO_ERROR) goto error;
- status = parcel->writeInt32(mSampleRate);
+ status = parcel->writeInt32(getSampleRate());
if (status != NO_ERROR) goto error;
- status = parcel->writeInt32(mSamplesPerFrame);
+ status = parcel->writeInt32(getSamplesPerFrame());
if (status != NO_ERROR) goto error;
- status = parcel->writeInt32((int32_t) mSharingMode);
+ status = parcel->writeInt32((int32_t) getSharingMode());
if (status != NO_ERROR) goto error;
- status = parcel->writeInt32((int32_t) mAudioFormat);
+ status = parcel->writeInt32((int32_t) getFormat());
if (status != NO_ERROR) goto error;
- status = parcel->writeInt32(mBufferCapacity);
+ status = parcel->writeInt32(getBufferCapacity());
if (status != NO_ERROR) goto error;
return NO_ERROR;
error:
@@ -55,57 +55,27 @@
}
status_t AAudioStreamConfiguration::readFromParcel(const Parcel* parcel) {
- status_t status = parcel->readInt32(&mDeviceId);
+ int32_t value;
+ status_t status = parcel->readInt32(&value);
if (status != NO_ERROR) goto error;
- status = parcel->readInt32(&mSampleRate);
+ setDeviceId(value);
+ status = parcel->readInt32(&value);
if (status != NO_ERROR) goto error;
- status = parcel->readInt32(&mSamplesPerFrame);
+ setSampleRate(value);
+ status = parcel->readInt32(&value);
if (status != NO_ERROR) goto error;
- status = parcel->readInt32(&mSharingMode);
+ setSamplesPerFrame(value);
+ status = parcel->readInt32(&value);
if (status != NO_ERROR) goto error;
- status = parcel->readInt32(&mAudioFormat);
+ setSharingMode(value);
+ status = parcel->readInt32(&value);
if (status != NO_ERROR) goto error;
- status = parcel->readInt32(&mBufferCapacity);
+ setFormat(value);
+ status = parcel->readInt32(&value);
if (status != NO_ERROR) goto error;
+ setBufferCapacity(value);
return NO_ERROR;
error:
ALOGE("AAudioStreamConfiguration.readFromParcel(): read failed = %d", status);
return status;
-}
-
-aaudio_result_t AAudioStreamConfiguration::validate() const {
- // Validate results of the open.
- if (mSampleRate < 0 || mSampleRate >= 8 * 48000) { // TODO review limits
- ALOGE("AAudioStreamConfiguration.validate(): invalid sampleRate = %d", mSampleRate);
- return AAUDIO_ERROR_INTERNAL;
- }
-
- if (mSamplesPerFrame < 1 || mSamplesPerFrame >= 32) { // TODO review limits
- ALOGE("AAudioStreamConfiguration.validate() invalid samplesPerFrame = %d", mSamplesPerFrame);
- return AAUDIO_ERROR_INTERNAL;
- }
-
- switch (mAudioFormat) {
- case AAUDIO_FORMAT_PCM_I16:
- case AAUDIO_FORMAT_PCM_FLOAT:
- break;
- default:
- ALOGE("AAudioStreamConfiguration.validate() invalid audioFormat = %d", mAudioFormat);
- return AAUDIO_ERROR_INTERNAL;
- }
-
- if (mBufferCapacity < 0) {
- ALOGE("AAudioStreamConfiguration.validate() invalid mBufferCapacity = %d", mBufferCapacity);
- return AAUDIO_ERROR_INTERNAL;
- }
- return AAUDIO_OK;
-}
-
-void AAudioStreamConfiguration::dump() const {
- ALOGD("AAudioStreamConfiguration mDeviceId = %d", mDeviceId);
- ALOGD("AAudioStreamConfiguration mSampleRate = %d", mSampleRate);
- ALOGD("AAudioStreamConfiguration mSamplesPerFrame = %d", mSamplesPerFrame);
- ALOGD("AAudioStreamConfiguration mSharingMode = %d", (int)mSharingMode);
- ALOGD("AAudioStreamConfiguration mAudioFormat = %d", (int)mAudioFormat);
- ALOGD("AAudioStreamConfiguration mBufferCapacity = %d", mBufferCapacity);
-}
+}
\ No newline at end of file
diff --git a/media/libaaudio/src/binding/AAudioStreamConfiguration.h b/media/libaaudio/src/binding/AAudioStreamConfiguration.h
index 144595a..b324896 100644
--- a/media/libaaudio/src/binding/AAudioStreamConfiguration.h
+++ b/media/libaaudio/src/binding/AAudioStreamConfiguration.h
@@ -22,6 +22,7 @@
#include <aaudio/AAudio.h>
#include <binder/Parcel.h>
#include <binder/Parcelable.h>
+#include "core/AAudioStreamParameters.h"
using android::status_t;
using android::Parcel;
@@ -29,74 +30,14 @@
namespace aaudio {
-class AAudioStreamConfiguration : public Parcelable {
+class AAudioStreamConfiguration : public AAudioStreamParameters, public Parcelable {
public:
AAudioStreamConfiguration();
virtual ~AAudioStreamConfiguration();
- int32_t getDeviceId() const {
- return mDeviceId;
- }
-
- void setDeviceId(int32_t deviceId) {
- mDeviceId = deviceId;
- }
-
- int32_t getSampleRate() const {
- return mSampleRate;
- }
-
- void setSampleRate(int32_t sampleRate) {
- mSampleRate = sampleRate;
- }
-
- int32_t getSamplesPerFrame() const {
- return mSamplesPerFrame;
- }
-
- void setSamplesPerFrame(int32_t samplesPerFrame) {
- mSamplesPerFrame = samplesPerFrame;
- }
-
- aaudio_format_t getAudioFormat() const {
- return mAudioFormat;
- }
-
- void setAudioFormat(aaudio_format_t audioFormat) {
- mAudioFormat = audioFormat;
- }
-
- aaudio_sharing_mode_t getSharingMode() const {
- return mSharingMode;
- }
-
- void setSharingMode(aaudio_sharing_mode_t sharingMode) {
- mSharingMode = sharingMode;
- }
-
- int32_t getBufferCapacity() const {
- return mBufferCapacity;
- }
-
- void setBufferCapacity(int32_t frames) {
- mBufferCapacity = frames;
- }
-
virtual status_t writeToParcel(Parcel* parcel) const override;
virtual status_t readFromParcel(const Parcel* parcel) override;
-
- aaudio_result_t validate() const;
-
- void dump() const;
-
-private:
- int32_t mDeviceId = AAUDIO_UNSPECIFIED;
- int32_t mSampleRate = AAUDIO_UNSPECIFIED;
- int32_t mSamplesPerFrame = AAUDIO_UNSPECIFIED;
- aaudio_sharing_mode_t mSharingMode = AAUDIO_SHARING_MODE_SHARED;
- aaudio_format_t mAudioFormat = AAUDIO_FORMAT_UNSPECIFIED;
- int32_t mBufferCapacity = AAUDIO_UNSPECIFIED;
};
} /* namespace aaudio */
diff --git a/media/libaaudio/src/binding/IAAudioService.cpp b/media/libaaudio/src/binding/IAAudioService.cpp
index 97fbaaa..b3c4934 100644
--- a/media/libaaudio/src/binding/IAAudioService.cpp
+++ b/media/libaaudio/src/binding/IAAudioService.cpp
@@ -264,13 +264,19 @@
case OPEN_STREAM: {
CHECK_INTERFACE(IAAudioService, data, reply);
request.readFromParcel(&data);
- //ALOGD("BnAAudioService::client openStream request dump --------------------");
- //request.dump();
- // Override the uid and pid from the client in case they are incorrect.
- request.setUserId(IPCThreadState::self()->getCallingUid());
- request.setProcessId(IPCThreadState::self()->getCallingPid());
- streamHandle = openStream(request, configuration);
- //ALOGD("BnAAudioService::onTransact OPEN_STREAM server handle = 0x%08X", streamHandle);
+ result = request.validate();
+ if (result != AAUDIO_OK) {
+ streamHandle = result;
+ } else {
+ //ALOGD("BnAAudioService::client openStream request dump --------------------");
+ //request.dump();
+ // Override the uid and pid from the client in case they are incorrect.
+ request.setUserId(IPCThreadState::self()->getCallingUid());
+ request.setProcessId(IPCThreadState::self()->getCallingPid());
+ streamHandle = openStream(request, configuration);
+ //ALOGD("BnAAudioService::onTransact OPEN_STREAM server handle = 0x%08X",
+ // streamHandle);
+ }
reply->writeInt32(streamHandle);
configuration.writeToParcel(reply);
return NO_ERROR;
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 7b01e44..8b14922 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -86,7 +86,7 @@
setFormat(AAUDIO_FORMAT_PCM_FLOAT);
}
// Request FLOAT for the shared mixer.
- request.getConfiguration().setAudioFormat(AAUDIO_FORMAT_PCM_FLOAT);
+ request.getConfiguration().setFormat(AAUDIO_FORMAT_PCM_FLOAT);
// Build the request to send to the server.
request.setUserId(getuid());
@@ -119,7 +119,7 @@
setSharingMode(configuration.getSharingMode());
// Save device format so we can do format conversion and volume scaling together.
- mDeviceFormat = configuration.getAudioFormat();
+ mDeviceFormat = configuration.getFormat();
result = mServiceInterface.getStreamDescription(mServiceStreamHandle, mEndPointParcelable);
if (result != AAUDIO_OK) {
diff --git a/media/libaaudio/src/client/IsochronousClockModel.cpp b/media/libaaudio/src/client/IsochronousClockModel.cpp
index 73f4c1d..6ea1172 100644
--- a/media/libaaudio/src/client/IsochronousClockModel.cpp
+++ b/media/libaaudio/src/client/IsochronousClockModel.cpp
@@ -16,7 +16,7 @@
#define LOG_TAG "AAudio"
//#define LOG_NDEBUG 0
-#include <utils/Log.h>
+#include <log/log.h>
#include <stdint.h>
@@ -25,7 +25,6 @@
#define MIN_LATENESS_NANOS (10 * AAUDIO_NANOS_PER_MICROSECOND)
-using namespace android;
using namespace aaudio;
IsochronousClockModel::IsochronousClockModel()
diff --git a/media/libaaudio/src/core/AAudioStreamParameters.cpp b/media/libaaudio/src/core/AAudioStreamParameters.cpp
new file mode 100644
index 0000000..65c2b46
--- /dev/null
+++ b/media/libaaudio/src/core/AAudioStreamParameters.cpp
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+
+
+#define LOG_TAG "AAudio"
+#include <utils/Log.h>
+#include <hardware/audio.h>
+
+#include "AAudioStreamParameters.h"
+
+using namespace aaudio;
+
+// TODO These defines should be moved to a central place in audio.
+#define SAMPLES_PER_FRAME_MIN 1
+// TODO Remove 8 channel limitation.
+#define SAMPLES_PER_FRAME_MAX FCC_8
+#define SAMPLE_RATE_HZ_MIN 8000
+// HDMI supports up to 32 channels at 1536000 Hz.
+#define SAMPLE_RATE_HZ_MAX 1600000
+
+AAudioStreamParameters::AAudioStreamParameters() {}
+AAudioStreamParameters::~AAudioStreamParameters() {}
+
+aaudio_result_t AAudioStreamParameters::validate() const {
+ if (mSamplesPerFrame != AAUDIO_UNSPECIFIED
+ && (mSamplesPerFrame < SAMPLES_PER_FRAME_MIN || mSamplesPerFrame > SAMPLES_PER_FRAME_MAX)) {
+ ALOGE("AAudioStreamParameters: channelCount out of range = %d", mSamplesPerFrame);
+ return AAUDIO_ERROR_OUT_OF_RANGE;
+ }
+
+ if (mDeviceId < 0) {
+ ALOGE("AAudioStreamParameters: deviceId out of range = %d", mDeviceId);
+ return AAUDIO_ERROR_OUT_OF_RANGE;
+ }
+
+ switch (mSharingMode) {
+ case AAUDIO_SHARING_MODE_EXCLUSIVE:
+ case AAUDIO_SHARING_MODE_SHARED:
+ break;
+ default:
+ ALOGE("AAudioStreamParameters: illegal sharingMode = %d", mSharingMode);
+ return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
+ // break;
+ }
+
+ switch (mAudioFormat) {
+ case AAUDIO_FORMAT_UNSPECIFIED:
+ case AAUDIO_FORMAT_PCM_I16:
+ case AAUDIO_FORMAT_PCM_FLOAT:
+ break; // valid
+ default:
+ ALOGE("AAudioStreamParameters: audioFormat not valid = %d", mAudioFormat);
+ return AAUDIO_ERROR_INVALID_FORMAT;
+ // break;
+ }
+
+ if (mSampleRate != AAUDIO_UNSPECIFIED
+ && (mSampleRate < SAMPLE_RATE_HZ_MIN || mSampleRate > SAMPLE_RATE_HZ_MAX)) {
+ ALOGE("AAudioStreamParameters: sampleRate out of range = %d", mSampleRate);
+ return AAUDIO_ERROR_INVALID_RATE;
+ }
+
+ if (mBufferCapacity < 0) {
+ ALOGE("AAudioStreamParameters: bufferCapacity out of range = %d", mBufferCapacity);
+ return AAUDIO_ERROR_OUT_OF_RANGE;
+ }
+
+ return AAUDIO_OK;
+}
+
+void AAudioStreamParameters::dump() const {
+ ALOGD("AAudioStreamParameters mDeviceId = %d", mDeviceId);
+ ALOGD("AAudioStreamParameters mSampleRate = %d", mSampleRate);
+ ALOGD("AAudioStreamParameters mSamplesPerFrame = %d", mSamplesPerFrame);
+ ALOGD("AAudioStreamParameters mSharingMode = %d", (int)mSharingMode);
+ ALOGD("AAudioStreamParameters mAudioFormat = %d", (int)mAudioFormat);
+ ALOGD("AAudioStreamParameters mBufferCapacity = %d", mBufferCapacity);
+}
\ No newline at end of file
diff --git a/media/libaaudio/src/core/AAudioStreamParameters.h b/media/libaaudio/src/core/AAudioStreamParameters.h
new file mode 100644
index 0000000..97379cc
--- /dev/null
+++ b/media/libaaudio/src/core/AAudioStreamParameters.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_STREAM_PARAMETERS_H
+#define AAUDIO_STREAM_PARAMETERS_H
+
+#include <stdint.h>
+
+#include <aaudio/AAudio.h>
+
+namespace aaudio {
+
+class AAudioStreamParameters {
+public:
+ AAudioStreamParameters();
+ virtual ~AAudioStreamParameters();
+
+ int32_t getDeviceId() const {
+ return mDeviceId;
+ }
+
+ void setDeviceId(int32_t deviceId) {
+ mDeviceId = deviceId;
+ }
+
+ int32_t getSampleRate() const {
+ return mSampleRate;
+ }
+
+ void setSampleRate(int32_t sampleRate) {
+ mSampleRate = sampleRate;
+ }
+
+ int32_t getSamplesPerFrame() const {
+ return mSamplesPerFrame;
+ }
+
+ /**
+ * This is also known as channelCount.
+ */
+ void setSamplesPerFrame(int32_t samplesPerFrame) {
+ mSamplesPerFrame = samplesPerFrame;
+ }
+
+ aaudio_format_t getFormat() const {
+ return mAudioFormat;
+ }
+
+ void setFormat(aaudio_format_t audioFormat) {
+ mAudioFormat = audioFormat;
+ }
+
+ aaudio_sharing_mode_t getSharingMode() const {
+ return mSharingMode;
+ }
+
+ void setSharingMode(aaudio_sharing_mode_t sharingMode) {
+ mSharingMode = sharingMode;
+ }
+
+ int32_t getBufferCapacity() const {
+ return mBufferCapacity;
+ }
+
+ void setBufferCapacity(int32_t frames) {
+ mBufferCapacity = frames;
+ }
+
+ virtual aaudio_result_t validate() const;
+
+ void dump() const;
+
+private:
+ int32_t mSamplesPerFrame = AAUDIO_UNSPECIFIED;
+ int32_t mSampleRate = AAUDIO_UNSPECIFIED;
+ int32_t mDeviceId = AAUDIO_UNSPECIFIED;
+ aaudio_sharing_mode_t mSharingMode = AAUDIO_SHARING_MODE_SHARED;
+ aaudio_format_t mAudioFormat = AAUDIO_FORMAT_UNSPECIFIED;
+ int32_t mBufferCapacity = AAUDIO_UNSPECIFIED;
+};
+
+} /* namespace aaudio */
+
+#endif //AAUDIO_STREAM_PARAMETERS_H
\ No newline at end of file
diff --git a/media/libaaudio/src/core/AudioStreamBuilder.cpp b/media/libaaudio/src/core/AudioStreamBuilder.cpp
index 6c4aa59..43a1ef1 100644
--- a/media/libaaudio/src/core/AudioStreamBuilder.cpp
+++ b/media/libaaudio/src/core/AudioStreamBuilder.cpp
@@ -179,36 +179,9 @@
// Check for values that are ridiculously out of range to prevent math overflow exploits.
// The service will do a better check.
- if (mSamplesPerFrame != AAUDIO_UNSPECIFIED
- && (mSamplesPerFrame < SAMPLES_PER_FRAME_MIN || mSamplesPerFrame > SAMPLES_PER_FRAME_MAX)) {
- ALOGE("AudioStreamBuilder: channelCount out of range = %d", mSamplesPerFrame);
- return AAUDIO_ERROR_OUT_OF_RANGE;
- }
-
- if (mDeviceId < 0) {
- ALOGE("AudioStreamBuilder: deviceId out of range = %d", mDeviceId);
- return AAUDIO_ERROR_OUT_OF_RANGE;
- }
-
- switch (mSharingMode) {
- case AAUDIO_SHARING_MODE_EXCLUSIVE:
- case AAUDIO_SHARING_MODE_SHARED:
- break;
- default:
- ALOGE("AudioStreamBuilder: illegal sharingMode = %d", mSharingMode);
- return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
- // break;
- }
-
- switch (mFormat) {
- case AAUDIO_FORMAT_UNSPECIFIED:
- case AAUDIO_FORMAT_PCM_I16:
- case AAUDIO_FORMAT_PCM_FLOAT:
- break; // valid
- default:
- ALOGE("AudioStreamBuilder: audioFormat not valid = %d", mFormat);
- return AAUDIO_ERROR_INVALID_FORMAT;
- // break;
+ aaudio_result_t result = AAudioStreamParameters::validate();
+ if (result != AAUDIO_OK) {
+ return result;
}
switch (mDirection) {
@@ -221,17 +194,6 @@
// break;
}
- if (mSampleRate != AAUDIO_UNSPECIFIED
- && (mSampleRate < SAMPLE_RATE_HZ_MIN || mSampleRate > SAMPLE_RATE_HZ_MAX)) {
- ALOGE("AudioStreamBuilder: sampleRate out of range = %d", mSampleRate);
- return AAUDIO_ERROR_INVALID_RATE;
- }
-
- if (mBufferCapacity < 0) {
- ALOGE("AudioStreamBuilder: bufferCapacity out of range = %d", mBufferCapacity);
- return AAUDIO_ERROR_OUT_OF_RANGE;
- }
-
switch (mPerformanceMode) {
case AAUDIO_PERFORMANCE_MODE_NONE:
case AAUDIO_PERFORMANCE_MODE_POWER_SAVING:
diff --git a/media/libaaudio/src/core/AudioStreamBuilder.h b/media/libaaudio/src/core/AudioStreamBuilder.h
index d757592..6e548b1 100644
--- a/media/libaaudio/src/core/AudioStreamBuilder.h
+++ b/media/libaaudio/src/core/AudioStreamBuilder.h
@@ -21,6 +21,7 @@
#include <aaudio/AAudio.h>
+#include "AAudioStreamParameters.h"
#include "AudioStream.h"
namespace aaudio {
@@ -28,24 +29,12 @@
/**
* Factory class for an AudioStream.
*/
-class AudioStreamBuilder {
+class AudioStreamBuilder : public AAudioStreamParameters {
public:
AudioStreamBuilder();
~AudioStreamBuilder();
- int getSamplesPerFrame() const {
- return mSamplesPerFrame;
- }
-
- /**
- * This is also known as channelCount.
- */
- AudioStreamBuilder* setSamplesPerFrame(int samplesPerFrame) {
- mSamplesPerFrame = samplesPerFrame;
- return this;
- }
-
aaudio_direction_t getDirection() const {
return mDirection;
}
@@ -55,33 +44,6 @@
return this;
}
- int32_t getSampleRate() const {
- return mSampleRate;
- }
-
- AudioStreamBuilder* setSampleRate(int32_t sampleRate) {
- mSampleRate = sampleRate;
- return this;
- }
-
- aaudio_format_t getFormat() const {
- return mFormat;
- }
-
- AudioStreamBuilder *setFormat(aaudio_format_t format) {
- mFormat = format;
- return this;
- }
-
- aaudio_sharing_mode_t getSharingMode() const {
- return mSharingMode;
- }
-
- AudioStreamBuilder* setSharingMode(aaudio_sharing_mode_t sharingMode) {
- mSharingMode = sharingMode;
- return this;
- }
-
bool isSharingModeMatchRequired() const {
return mSharingModeMatchRequired;
}
@@ -91,15 +53,6 @@
return this;
}
- int32_t getBufferCapacity() const {
- return mBufferCapacity;
- }
-
- AudioStreamBuilder* setBufferCapacity(int32_t frames) {
- mBufferCapacity = frames;
- return this;
- }
-
int32_t getPerformanceMode() const {
return mPerformanceMode;
}
@@ -109,15 +62,6 @@
return this;
}
- int32_t getDeviceId() const {
- return mDeviceId;
- }
-
- AudioStreamBuilder* setDeviceId(int32_t deviceId) {
- mDeviceId = deviceId;
- return this;
- }
-
AAudioStream_dataCallback getDataCallbackProc() const {
return mDataCallbackProc;
}
@@ -165,17 +109,11 @@
aaudio_result_t build(AudioStream **streamPtr);
- aaudio_result_t validate() const;
+ virtual aaudio_result_t validate() const override;
private:
- int32_t mSamplesPerFrame = AAUDIO_UNSPECIFIED;
- 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_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
diff --git a/media/libaaudio/src/utility/AAudioUtilities.cpp b/media/libaaudio/src/utility/AAudioUtilities.cpp
index 2d8ac6e..a3198d7 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.cpp
+++ b/media/libaaudio/src/utility/AAudioUtilities.cpp
@@ -229,10 +229,13 @@
case AAUDIO_ERROR_NULL:
status = UNEXPECTED_NULL;
break;
+ case AAUDIO_ERROR_UNAVAILABLE:
+ status = NOT_ENOUGH_DATA;
+ break;
+
// TODO translate these result codes
case AAUDIO_ERROR_INTERNAL:
case AAUDIO_ERROR_UNIMPLEMENTED:
- case AAUDIO_ERROR_UNAVAILABLE:
case AAUDIO_ERROR_NO_FREE_HANDLES:
case AAUDIO_ERROR_NO_MEMORY:
case AAUDIO_ERROR_TIMEOUT:
@@ -268,6 +271,9 @@
case WOULD_BLOCK:
result = AAUDIO_ERROR_WOULD_BLOCK;
break;
+ case NOT_ENOUGH_DATA:
+ result = AAUDIO_ERROR_UNAVAILABLE;
+ break;
default:
result = AAUDIO_ERROR_INTERNAL;
break;
diff --git a/media/libaudioclient/AudioTrackShared.cpp b/media/libaudioclient/AudioTrackShared.cpp
index 97f0dfd..e49945b 100644
--- a/media/libaudioclient/AudioTrackShared.cpp
+++ b/media/libaudioclient/AudioTrackShared.cpp
@@ -36,7 +36,7 @@
// a value between "other" + 1 and "other" + INT32_MAX, the choice of
// which needs to be the "least recently used" sequence value for "self".
// In general, this means (new_self) returned is max(self, other) + 1.
-
+__attribute__((no_sanitize("integer")))
static uint32_t incrementSequence(uint32_t self, uint32_t other) {
int32_t diff = (int32_t) self - (int32_t) other;
if (diff >= 0 && diff < INT32_MAX) {
@@ -389,6 +389,7 @@
// ---------------------------------------------------------------------------
+__attribute__((no_sanitize("integer")))
void AudioTrackClientProxy::flush()
{
// This works for mFrameCountP2 <= 2^30
@@ -862,6 +863,7 @@
return old;
}
+__attribute__((no_sanitize("integer")))
void AudioTrackServerProxy::tallyUnderrunFrames(uint32_t frameCount)
{
audio_track_cblk_t* cblk = mCblk;
@@ -1017,6 +1019,7 @@
return (ssize_t) mState.mPosition;
}
+__attribute__((no_sanitize("integer")))
status_t StaticAudioTrackServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush)
{
if (mIsShutdown) {
@@ -1061,6 +1064,7 @@
return NO_ERROR;
}
+__attribute__((no_sanitize("integer")))
void StaticAudioTrackServerProxy::releaseBuffer(Buffer* buffer)
{
size_t stepCount = buffer->mFrameCount;
diff --git a/media/libaudioclient/OWNERS b/media/libaudioclient/OWNERS
new file mode 100644
index 0000000..482b9fb
--- /dev/null
+++ b/media/libaudioclient/OWNERS
@@ -0,0 +1,3 @@
+gkasten@google.com
+jmtrivi@google.com
+mnaganov@google.com
diff --git a/media/libaudiohal/OWNERS b/media/libaudiohal/OWNERS
new file mode 100644
index 0000000..1456ab6
--- /dev/null
+++ b/media/libaudiohal/OWNERS
@@ -0,0 +1,2 @@
+krocard@google.com
+mnaganov@google.com
diff --git a/media/libeffects/OWNERS b/media/libeffects/OWNERS
new file mode 100644
index 0000000..7e3de13
--- /dev/null
+++ b/media/libeffects/OWNERS
@@ -0,0 +1,3 @@
+krocard@google.com
+mnaganov@google.com
+rago@google.com
diff --git a/media/libeffects/config/Android.bp b/media/libeffects/config/Android.bp
index 5666d14..4398a91 100644
--- a/media/libeffects/config/Android.bp
+++ b/media/libeffects/config/Android.bp
@@ -10,5 +10,8 @@
"libtinyxml2",
],
+ header_libs: ["libaudio_system_headers"],
+ export_header_lib_headers: ["libaudio_system_headers"],
+
export_include_dirs: ["include"],
}
diff --git a/media/libeffects/factory/Android.bp b/media/libeffects/factory/Android.bp
index 21be587..ddbfdd8 100644
--- a/media/libeffects/factory/Android.bp
+++ b/media/libeffects/factory/Android.bp
@@ -10,10 +10,11 @@
cc_library_shared {
name: "libeffects",
vendor: true,
- srcs: ["EffectsFactory.c",
- "EffectsConfigLoader.c",
- "EffectsFactoryState.c",
- "EffectsXmlConfigLoader.cpp",
+ srcs: [
+ "EffectsFactory.c",
+ "EffectsConfigLoader.c",
+ "EffectsFactoryState.c",
+ "EffectsXmlConfigLoader.cpp",
],
shared_libs: [
@@ -24,11 +25,12 @@
],
cflags: ["-fvisibility=hidden"],
- include_dirs: ["system/media/audio_effects/include"],
-
local_include_dirs:["include/media"],
- header_libs: ["libeffects_headers"],
+ header_libs: [
+ "libaudioeffects",
+ "libeffects_headers",
+ ],
export_header_lib_headers: ["libeffects_headers"],
}
diff --git a/media/libeffects/factory/EffectsConfigLoader.c b/media/libeffects/factory/EffectsConfigLoader.c
index 5c973b4..fcef36f 100644
--- a/media/libeffects/factory/EffectsConfigLoader.c
+++ b/media/libeffects/factory/EffectsConfigLoader.c
@@ -19,6 +19,7 @@
#include <dlfcn.h>
#include <stdlib.h>
+#include <unistd.h>
#include <cutils/config_utils.h>
#include <cutils/misc.h>
diff --git a/media/libeffects/factory/EffectsFactory.h b/media/libeffects/factory/EffectsFactory.h
index cdba362..29dbc9c 100644
--- a/media/libeffects/factory/EffectsFactory.h
+++ b/media/libeffects/factory/EffectsFactory.h
@@ -20,7 +20,6 @@
#include <dirent.h>
#include <pthread.h>
-#include <android/log.h>
#include <cutils/compiler.h>
#include <hardware/audio_effect.h>
diff --git a/media/libeffects/factory/EffectsXmlConfigLoader.cpp b/media/libeffects/factory/EffectsXmlConfigLoader.cpp
index 42e00f8..438b787 100644
--- a/media/libeffects/factory/EffectsXmlConfigLoader.cpp
+++ b/media/libeffects/factory/EffectsXmlConfigLoader.cpp
@@ -281,7 +281,7 @@
++nbSkippedElement;
continue;
}
- listPush(&effectLoadResult.effectDesc, subEffectList);
+ listPush(effectLoadResult.effectDesc.get(), subEffectList);
// Since we return a dummy descriptor for the proxy during
// get_descriptor call, we replace it with the corresponding
diff --git a/media/libmedia/include/media/DrmHal.h b/media/libmedia/include/media/DrmHal.h
index e031765..5d25e4d 100644
--- a/media/libmedia/include/media/DrmHal.h
+++ b/media/libmedia/include/media/DrmHal.h
@@ -39,6 +39,11 @@
struct DrmSessionClientInterface;
+inline bool operator==(const Vector<uint8_t> &l, const Vector<uint8_t> &r) {
+ if (l.size() != r.size()) return false;
+ return memcmp(l.array(), r.array(), l.size()) == 0;
+}
+
struct DrmHal : public BnDrm,
public IBinder::DeathRecipient,
public IDrmPluginListener {
@@ -161,6 +166,9 @@
const Vector<sp<IDrmFactory>> mFactories;
sp<IDrmPlugin> mPlugin;
+ Vector<Vector<uint8_t>> mOpenSessions;
+ void closeOpenSessions();
+
/**
* mInitCheck is:
* NO_INIT if a plugin hasn't been created yet
@@ -175,6 +183,11 @@
void writeByteArray(Parcel &obj, const hidl_vec<uint8_t>& array);
+ void reportMetrics() const;
+ status_t getPropertyStringInternal(String8 const &name, String8 &value) const;
+ status_t getPropertyByteArrayInternal(String8 const &name,
+ Vector<uint8_t> &value) const;
+
DISALLOW_EVIL_CONSTRUCTORS(DrmHal);
};
diff --git a/media/libmedia/include/media/PluginMetricsReporting.h b/media/libmedia/include/media/PluginMetricsReporting.h
new file mode 100644
index 0000000..4a5a363
--- /dev/null
+++ b/media/libmedia/include/media/PluginMetricsReporting.h
@@ -0,0 +1,33 @@
+/*
+ * 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 PLUGIN_METRICS_REPORTING_H_
+
+#define PLUGIN_METRICS_REPORTING_H_
+
+#include <utils/Errors.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+status_t reportDrmPluginMetrics(const Vector<uint8_t>& serializedMetrics,
+ const String8& vendorName,
+ const String8& description);
+
+} // namespace android
+
+#endif // PLUGIN_METRICS_REPORTING_H_
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 18fd857..77f0cab 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -83,6 +83,9 @@
#include "HTTPBase.h"
#include "RemoteDisplay.h"
+static const int kDumpLockRetries = 50;
+static const int kDumpLockSleepUs = 20000;
+
namespace {
using android::media::Metadata;
using android::status_t;
@@ -392,12 +395,32 @@
snprintf(buffer, 255, " pid(%d), connId(%d), status(%d), looping(%s)\n",
mPid, mConnId, mStatus, mLoop?"true": "false");
result.append(buffer);
- write(fd, result.string(), result.size());
- if (mPlayer != NULL) {
- mPlayer->dump(fd, args);
+
+ sp<MediaPlayerBase> p;
+ sp<AudioOutput> audioOutput;
+ bool locked = false;
+ for (int i = 0; i < kDumpLockRetries; ++i) {
+ if (mLock.tryLock() == NO_ERROR) {
+ locked = true;
+ break;
+ }
+ usleep(kDumpLockSleepUs);
}
- if (mAudioOutput != 0) {
- mAudioOutput->dump(fd, args);
+
+ if (locked) {
+ p = mPlayer;
+ audioOutput = mAudioOutput;
+ mLock.unlock();
+ } else {
+ result.append(" lock is taken, no dump from player and audio output\n");
+ }
+ write(fd, result.string(), result.size());
+
+ if (p != NULL) {
+ p->dump(fd, args);
+ }
+ if (audioOutput != 0) {
+ audioOutput->dump(fd, args);
}
write(fd, "\n", 1);
return NO_ERROR;
@@ -577,7 +600,10 @@
MediaPlayerService::Client::~Client()
{
ALOGV("Client(%d) destructor pid = %d", mConnId, mPid);
- mAudioOutput.clear();
+ {
+ Mutex::Autolock l(mLock);
+ mAudioOutput.clear();
+ }
wp<Client> client(this);
disconnect();
mService->removeClient(client);
@@ -597,10 +623,9 @@
Mutex::Autolock l(mLock);
p = mPlayer;
mClient.clear();
+ mPlayer.clear();
}
- mPlayer.clear();
-
// clear the notification to prevent callbacks to dead client
// and reset the player. We assume the player will serialize
// access to itself if necessary.
@@ -621,7 +646,7 @@
sp<MediaPlayerBase> MediaPlayerService::Client::createPlayer(player_type playerType)
{
// determine if we have the right player type
- sp<MediaPlayerBase> p = mPlayer;
+ sp<MediaPlayerBase> p = getPlayer();
if ((p != NULL) && (p->playerType() != playerType)) {
ALOGV("delete player");
p.clear();
@@ -772,6 +797,7 @@
}
if (mStatus == OK) {
+ Mutex::Autolock l(mLock);
mPlayer = p;
}
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 758db1f..483a9ff 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -840,7 +840,7 @@
return;
}
- notifyEOS(true /* audio */, ERROR_END_OF_STREAM);
+ notifyEOS_l(true /* audio */, ERROR_END_OF_STREAM);
}
size_t NuPlayer::Renderer::fillAudioBuffer(void *buffer, size_t size) {
@@ -917,10 +917,10 @@
if (mAudioSink->needsTrailingPadding()) {
postEOSDelayUs = getPendingAudioPlayoutDurationUs(ALooper::GetNowUs());
}
- ALOGV("fillAudioBuffer: notifyEOS "
+ ALOGV("fillAudioBuffer: notifyEOS_l "
"mNumFramesWritten:%u finalResult:%d postEOSDelay:%lld",
mNumFramesWritten, entry->mFinalResult, (long long)postEOSDelayUs);
- notifyEOS(true /* audio */, entry->mFinalResult, postEOSDelayUs);
+ notifyEOS_l(true /* audio */, entry->mFinalResult, postEOSDelayUs);
}
}
return sizeCopied;
@@ -1408,6 +1408,11 @@
}
void NuPlayer::Renderer::notifyEOS(bool audio, status_t finalResult, int64_t delayUs) {
+ Mutex::Autolock autoLock(mLock);
+ notifyEOS_l(audio, finalResult, delayUs);
+}
+
+void NuPlayer::Renderer::notifyEOS_l(bool audio, status_t finalResult, int64_t delayUs) {
if (audio && delayUs > 0) {
sp<AMessage> msg = new AMessage(kWhatEOS, this);
msg->setInt32("audioEOSGeneration", mAudioEOSGeneration);
@@ -1420,6 +1425,11 @@
notify->setInt32("audio", static_cast<int32_t>(audio));
notify->setInt32("finalResult", finalResult);
notify->post(delayUs);
+
+ if (audio) {
+ // Video might outlive audio. Clear anchor to enable video only case.
+ mAnchorTimeMediaUs = -1;
+ }
}
void NuPlayer::Renderer::notifyAudioTearDown(AudioTearDownReason reason) {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
index e6850b5..f58b79c 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
@@ -275,6 +275,7 @@
void onChangeAudioFormat(const sp<AMessage> &meta, const sp<AMessage> ¬ify);
void notifyEOS(bool audio, status_t finalResult, int64_t delayUs = 0);
+ void notifyEOS_l(bool audio, status_t finalResult, int64_t delayUs = 0);
void notifyFlushComplete(bool audio);
void notifyPosition();
void notifyVideoLateBy(int64_t lateByUs);
diff --git a/media/libnbaio/Android.bp b/media/libnbaio/Android.bp
index 02f7529..4220b77 100644
--- a/media/libnbaio/Android.bp
+++ b/media/libnbaio/Android.bp
@@ -45,6 +45,7 @@
"PerformanceAnalysis.cpp",
"Pipe.cpp",
"PipeReader.cpp",
+ "ReportPerformance.cpp",
"SourceAudioBufferProvider.cpp",
],
diff --git a/media/libnbaio/NBLog.cpp b/media/libnbaio/NBLog.cpp
index 2b5b4ff..2f639d2 100644
--- a/media/libnbaio/NBLog.cpp
+++ b/media/libnbaio/NBLog.cpp
@@ -24,8 +24,8 @@
* Calls LOG_HIST_TS
* LOG_HIST_TS
* Hashes file name and line number, and writes single timestamp to buffer
-* calls NBLOG::Writer::logHistTS once
-* NBLOG::Writer::logHistTS
+* calls NBLOG::Writer::logEventHistTS once
+* NBLOG::Writer::logEventHistTS
* calls NBLOG::Writer::log on hash and current timestamp
* time is in CLOCK_MONOTONIC converted to ns
* NBLOG::Writer::log(Event, const void*, size_t)
@@ -44,6 +44,8 @@
* ssize_t audio_utils_fifo_reader::obtain
* Determines readable buffer section via pointer arithmetic on reader
* and writer pointers
+* Similarly, LOG_AUDIO_STATE() is called by onStateChange whenever audio is
+* turned on or off, and writes this notification to the FIFO.
*
* 2) reading the data from shared memory
* Thread::threadloop()
@@ -106,6 +108,7 @@
#include <audio_utils/roundup.h>
#include <media/nbaio/NBLog.h>
#include <media/nbaio/PerformanceAnalysis.h>
+#include <media/nbaio/ReportPerformance.h>
// #include <utils/CallStack.h> // used to print callstack
#include <utils/Log.h>
#include <utils/String8.h>
@@ -138,6 +141,7 @@
switch (type) {
case EVENT_START_FMT:
return std::make_unique<FormatEntry>(FormatEntry(ptr));
+ case EVENT_AUDIO_STATE:
case EVENT_HISTOGRAM_ENTRY_TS:
return std::make_unique<HistogramEntry>(HistogramEntry(ptr));
default:
@@ -516,7 +520,7 @@
log(EVENT_HASH, &hash, sizeof(hash));
}
-void NBLog::Writer::logHistTS(log_hash_t hash)
+void NBLog::Writer::logEventHistTs(Event event, log_hash_t hash)
{
if (!mEnabled) {
return;
@@ -525,7 +529,7 @@
data.hash = hash;
data.ts = get_monotonic_ns();
if (data.ts > 0) {
- log(EVENT_HISTOGRAM_ENTRY_TS, &data, sizeof(data));
+ log(event, &data, sizeof(data));
} else {
ALOGE("Failed to get timestamp");
}
@@ -758,7 +762,9 @@
const std::set<NBLog::Event> NBLog::Reader::startingTypes {NBLog::Event::EVENT_START_FMT,
NBLog::Event::EVENT_HISTOGRAM_ENTRY_TS};
const std::set<NBLog::Event> NBLog::Reader::endingTypes {NBLog::Event::EVENT_END_FMT,
- NBLog::Event::EVENT_HISTOGRAM_ENTRY_TS};
+ NBLog::Event::EVENT_HISTOGRAM_ENTRY_TS,
+ NBLog::Event::EVENT_AUDIO_STATE};
+
NBLog::Reader::Reader(const void *shared, size_t size)
: mShared((/*const*/ Shared *) shared), /*mIMemory*/
mFd(-1), mIndent(0),
@@ -864,42 +870,17 @@
}
-// writes sample deltas to file, either truncating or appending
-inline void writeHistToFile(const std::vector<int64_t> &samples, bool append) {
- // name of file on audioserver
- static const char* const kName = (char *)"/data/misc/audioserver/sample_results.txt";
- // stores deltas between the samples
- std::vector<int64_t> intervals;
- if (samples.size() == 0) return;
- for (size_t i = 1; i < samples.size(); ++i) {
- intervals.push_back(deltaMs(samples[i - 1], samples[i]));
- }
- // Deletes maximum value in a histogram. Temp quick fix.
- // FIXME: need to find root cause of approx. 35th element from the end
- // consistently being an outlier in the first histogram of a flush
- // ALOGW("%" PRId64 "before", (int64_t) *(std::max_element(intervals.begin(), intervals.end())));
- intervals.erase(std::max_element(intervals.begin(), intervals.end()));
- // ALOGW("%" PRId64 "after", (int64_t) *(std::max_element(intervals.begin(), intervals.end())));
- std::ofstream ofs;
- ofs.open(kName, append ? std::ios::app : std::ios::trunc);
- if (!ofs) {
- ALOGW("couldn't open file %s", kName);
- return;
- }
- for (size_t i = 0; i < intervals.size(); ++i) {
- ofs << intervals[i] << "\n";
- }
- ofs.close();
-}
-
+// TODO: move this to PerformanceAnalysis
+// TODO: make call to dump periodic so that data in shared FIFO does not get overwritten
void NBLog::Reader::dump(int fd, size_t indent, NBLog::Reader::Snapshot &snapshot)
{
- // CallStack cs(LOG_TAG);
mFd = fd;
mIndent = indent;
String8 timestamp, body;
// FIXME: this is not thread safe
- static PerformanceAnalysis performanceAnalysis; // used to store data and to call analysis functions
+ // TODO: need a separate instance of performanceAnalysis for each thread
+ // used to store data and to call analysis functions
+ static ReportPerformance::PerformanceAnalysis performanceAnalysis;
size_t lost = snapshot.lost() + (snapshot.begin() - EntryIterator(snapshot.data()));
if (lost > 0) {
body.appendFormat("warning: lost %zu bytes worth of events", lost);
@@ -921,7 +902,12 @@
memcpy(&hash, &(data->hash), sizeof(hash));
int64_t ts;
memcpy(&ts, &data->ts, sizeof(ts));
- performanceAnalysis.logTsEntry(data->author, ts);
+ performanceAnalysis.logTsEntry(ts);
+ ++entry;
+ break;
+ }
+ case EVENT_AUDIO_STATE: {
+ performanceAnalysis.handleStateChange();
++entry;
break;
}
diff --git a/media/libnbaio/OWNERS b/media/libnbaio/OWNERS
new file mode 100644
index 0000000..f9cb567
--- /dev/null
+++ b/media/libnbaio/OWNERS
@@ -0,0 +1 @@
+gkasten@google.com
diff --git a/media/libnbaio/PerformanceAnalysis.cpp b/media/libnbaio/PerformanceAnalysis.cpp
index fa8f47e..fb3bddc 100644
--- a/media/libnbaio/PerformanceAnalysis.cpp
+++ b/media/libnbaio/PerformanceAnalysis.cpp
@@ -21,7 +21,6 @@
#include <algorithm>
#include <climits>
#include <deque>
-#include <fstream>
#include <iostream>
#include <math.h>
#include <numeric>
@@ -36,6 +35,7 @@
#include <audio_utils/roundup.h>
#include <media/nbaio/NBLog.h>
#include <media/nbaio/PerformanceAnalysis.h>
+#include <media/nbaio/ReportPerformance.h>
// #include <utils/CallStack.h> // used to print callstack
#include <utils/Log.h>
#include <utils/String8.h>
@@ -45,6 +45,8 @@
namespace android {
+namespace ReportPerformance {
+
PerformanceAnalysis::PerformanceAnalysis() {
// These variables will be (FIXME) learned from the data
kPeriodMs = 4; // typical buffer period (mode)
@@ -71,39 +73,92 @@
return width;
}
-// Takes a single buffer period timestamp entry with author information and stores it
-// in a temporary series of timestamps. Once the series is full, the data is analyzed,
+// Given a series of audio processing wakeup timestamps,
+// buckets the time intervals into a histogram, searches for
+// outliers, analyzes the outlier series for unexpectedly
+// small or large values and stores these as peaks, and flushes
+// the timestamp series from memory.
+void PerformanceAnalysis::processAndFlushTimeStampSeries() {
+ // 1) analyze the series to store all outliers and their exact timestamps:
+ storeOutlierData(mTimeStampSeries);
+
+ // 2) detect peaks in the outlier series
+ detectPeaks();
+
+ // 3) compute its histogram, append to mRecentHists and clear the time series
+ mRecentHists.emplace_back(static_cast<timestamp>(mTimeStampSeries[0]),
+ buildBuckets(mTimeStampSeries));
+ // do not let mRecentHists exceed capacity
+ // ALOGD("mRecentHists size: %d", static_cast<int>(mRecentHists.size()));
+ if (mRecentHists.size() >= kRecentHistsCapacity) {
+ // ALOGD("popped back mRecentHists");
+ mRecentHists.pop_front();
+ }
+ mTimeStampSeries.clear();
+}
+
+// forces short-term histogram storage to avoid adding idle audio time interval
+// to buffer period data
+void PerformanceAnalysis::handleStateChange() {
+ ALOGD("handleStateChange");
+ processAndFlushTimeStampSeries();
+ return;
+}
+
+// Takes a single buffer period timestamp entry information and stores it in a
+// temporary series of timestamps. Once the series is full, the data is analyzed,
// stored, and emptied.
-// TODO: decide whether author or file location information is more important to store
-// for now, only stores author (thread)
-void PerformanceAnalysis::logTsEntry(int author, int64_t ts) {
+void PerformanceAnalysis::logTsEntry(int64_t ts) {
// TODO might want to filter excessively high outliers, which are usually caused
// by the thread being inactive.
// Store time series data for each reader in order to bucket it once there
// is enough data. Then, write to recentHists as a histogram.
- mTimeStampSeries[author].push_back(ts);
- // if length of the time series has reached kShortHistSize samples, do 1) and 2):
- if (mTimeStampSeries[author].size() >= kShortHistSize) {
- // 1) analyze the series to store all outliers and their exact timestamps:
- storeOutlierData(mTimeStampSeries[author]);
- // 2) detect peaks in the outlier series
- detectPeaks();
- // 3) compute its histogram, append this to mRecentHists and erase the time series
- // FIXME: need to store the timestamp of the beginning of each histogram
- // FIXME: Restore LOG_HIST_FLUSH to separate histograms at every end-of-stream event
- // A histogram should not span data between audio off/on timespans
- mRecentHists.emplace_back(author,
- buildBuckets(mTimeStampSeries[author]));
- // do not let mRecentHists exceed capacity
- // ALOGD("mRecentHists size: %d", static_cast<int>(mRecentHists.size()));
- if (mRecentHists.size() >= kRecentHistsCapacity) {
- // ALOGD("popped back mRecentHists");
- mRecentHists.pop_front();
- }
- mTimeStampSeries[author].clear();
+ mTimeStampSeries.push_back(ts);
+ // if length of the time series has reached kShortHistSize samples,
+ // analyze the data and flush the timestamp series from memory
+ if (mTimeStampSeries.size() >= kShortHistSize) {
+ processAndFlushTimeStampSeries();
}
}
+// When the short-term histogram array mRecentHists has reached capacity,
+// merge histograms for data compression and store them in mLongTermHists
+// clears mRecentHists
+// TODO: have logTsEntry write directly to mLongTermHists, discard mRecentHists,
+// start a new histogram when a peak occurs
+void PerformanceAnalysis::processAndFlushRecentHists() {
+
+ // Buckets is used to aggregate short-term histograms.
+ Histogram buckets;
+ timestamp startingTs = mRecentHists[0].first;
+
+ for (const auto &shortHist: mRecentHists) {
+ // If the time between starting and ending timestamps has reached the maximum,
+ // add the current histogram (buckets) to the long-term histogram buffer,
+ // clear buckets, and start a new long-term histogram aggregation process.
+ if (deltaMs(startingTs, shortHist.first) >= kMaxHistTimespanMs) {
+ mLongTermHists.emplace_back(startingTs, std::move(buckets));
+ buckets.clear();
+ startingTs = shortHist.first;
+ // When memory is full, delete oldest histogram
+ // TODO use a circular buffer
+ if (mLongTermHists.size() >= kLongTermHistsCapacity) {
+ mLongTermHists.pop_front();
+ }
+ }
+
+ // add current histogram to buckets
+ for (const auto &countPair : shortHist.second) {
+ buckets[countPair.first] += countPair.second;
+ }
+ }
+ mRecentHists.clear();
+ // TODO: decide when/where to call writeToFile
+ // TODO: add a thread-specific extension to the file name
+ static const char* const kName = (const char *) "/data/misc/audioserver/sample_results.txt";
+ writeToFile(mOutlierData, mLongTermHists, kName, false);
+}
+
// Given a series of outlier intervals (mOutlier data),
// looks for changes in distribution (peaks), which can be either positive or negative.
// The function sets the mean to the starting value and sigma to 0, and updates
@@ -112,7 +167,6 @@
// are set to the peak value and 0.
void PerformanceAnalysis::detectPeaks() {
if (mOutlierData.empty()) {
- ALOGD("peak detector called on empty array");
return;
}
@@ -147,14 +201,12 @@
mPeakDetectorSd = sqrt(std::accumulate(start, it + 1, 0.0,
[=](auto &a, auto &b){ return a + sqr(b.first - mPeakDetectorMean);})) /
((kN > 1)? kN - 1 : kN); // kN - 1: mean is correlated with variance
- // ALOGD("value, mean, sd: %f, %f, %f", static_cast<double>(it->first), mean, sd);
}
// surprising value: store peak timestamp and reset mean, sd, and start iterator
else {
mPeakTimestamps.emplace_back(it->second);
// TODO: remove pop_front once a circular buffer is in place
- if (mPeakTimestamps.size() >= kShortHistSize) {
- ALOGD("popped back mPeakTimestamps");
+ if (mPeakTimestamps.size() >= kPeakSeriesSize) {
mPeakTimestamps.pop_front();
}
mPeakDetectorMean = static_cast<double>(it->first);
@@ -162,9 +214,6 @@
start = it;
}
}
- //for (const auto &it : mPeakTimestamps) {
- // ALOGE("mPeakTimestamps %f", static_cast<double>(it));
- //}
return;
}
@@ -175,7 +224,6 @@
// This function is applied to the time series before it is converted into a histogram.
void PerformanceAnalysis::storeOutlierData(const std::vector<int64_t> ×tamps) {
if (timestamps.size() < 1) {
- ALOGE("storeOutlierData called on empty vector");
return;
}
// first pass: need to initialize
@@ -188,10 +236,9 @@
mOutlierData.emplace_back(mElapsed, static_cast<uint64_t>(mPrevNs));
// Remove oldest value if the vector is full
// TODO: remove pop_front once circular buffer is in place
- // FIXME: change kShortHistSize to some other constant. Make sure it is large
- // enough that data will never be lost before being written to a long-term FIFO
- if (mOutlierData.size() >= kShortHistSize) {
- ALOGD("popped back mOutlierData");
+ // FIXME: make sure kShortHistSize is large enough that that data will never be lost
+ // before being written to file or to a FIFO
+ if (mOutlierData.size() >= kOutlierSeriesSize) {
mOutlierData.pop_front();
}
mElapsed = 0;
@@ -217,9 +264,8 @@
}
// TODO Make it return a std::string instead of modifying body --> is this still relevant?
-// FIXME: as can be seen when printing the values, the outlier timestamps typically occur
-// in the first histogram 35 to 38 indices from the end (most often 35).
// TODO consider changing all ints to uint32_t or uint64_t
+// TODO: move this to ReportPerformance, probably make it a friend function of PerformanceAnalysis
void PerformanceAnalysis::reportPerformance(String8 *body, int maxHeight) {
if (mRecentHists.size() < 1) {
ALOGD("reportPerformance: mRecentHists is empty");
@@ -323,4 +369,6 @@
return;
}
+} // namespace ReportPerformance
+
} // namespace android
diff --git a/media/libnbaio/ReportPerformance.cpp b/media/libnbaio/ReportPerformance.cpp
new file mode 100644
index 0000000..dc50ada
--- /dev/null
+++ b/media/libnbaio/ReportPerformance.cpp
@@ -0,0 +1,74 @@
+/*
+ * 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 "ReportPerformance"
+
+#include <fstream>
+#include <iostream>
+#include <queue>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <utility>
+#include <media/nbaio/NBLog.h>
+#include <media/nbaio/PerformanceAnalysis.h>
+#include <media/nbaio/ReportPerformance.h>
+// #include <utils/CallStack.h> // used to print callstack
+#include <utils/Log.h>
+#include <utils/String8.h>
+
+namespace android {
+
+namespace ReportPerformance {
+
+// Writes outlier intervals, timestamps, and histograms spanning long time intervals to a file.
+// TODO: format the data efficiently and write different types of data to different files
+void writeToFile(std::deque<std::pair<outlierInterval, timestamp>> &outlierData,
+ std::deque<std::pair<timestamp, Histogram>> &hists,
+ const char * kName,
+ bool append) {
+ ALOGD("writing performance data to file");
+ if (outlierData.empty() || hists.empty()) {
+ return;
+ }
+
+ std::ofstream ofs;
+ ofs.open(kName, append ? std::ios::app : std::ios::trunc);
+ if (!ofs.is_open()) {
+ ALOGW("couldn't open file %s", kName);
+ return;
+ }
+ ofs << "Outlier data: interval and timestamp\n";
+ for (const auto &outlier : outlierData) {
+ ofs << outlier.first << ": " << outlier.second << "\n";
+ }
+ ofs << "Histogram data\n";
+ for (const auto &hist : hists) {
+ ofs << "\ttimestamp\n";
+ ofs << hist.first << "\n";
+ ofs << "\tbuckets and counts\n";
+ for (const auto &bucket : hist.second) {
+ ofs << bucket.first << ": " << bucket.second << "\n";
+ }
+ }
+ ofs.close();
+}
+
+} // namespace ReportPerformance
+
+} // namespace android
diff --git a/media/libnbaio/include/media/nbaio/NBLog.h b/media/libnbaio/include/media/nbaio/NBLog.h
index 0b5e24d..3e48ee1 100644
--- a/media/libnbaio/include/media/nbaio/NBLog.h
+++ b/media/libnbaio/include/media/nbaio/NBLog.h
@@ -44,8 +44,6 @@
class Writer;
class Reader;
-private:
-
enum Event : uint8_t {
EVENT_RESERVED,
EVENT_STRING, // ASCII string, not NUL-terminated
@@ -60,11 +58,13 @@
EVENT_HASH, // unique HASH of log origin, originates from hash of file name
// and line number
EVENT_HISTOGRAM_ENTRY_TS, // single datum for timestamp histogram
+ EVENT_AUDIO_STATE, // audio on/off event: logged upon FastMixer::onStateChange() call
EVENT_END_FMT, // end of logFormat argument list
EVENT_UPPER_BOUND, // to check for invalid events
};
+private:
// ---------------------------------------------------------------------------
// API for handling format entry operations
@@ -248,6 +248,8 @@
int author;
}; //TODO __attribute__((packed));
+using StateTsEntryWithAuthor = HistTsEntryWithAuthor;
+
struct HistIntEntry {
log_hash_t hash;
int value;
@@ -341,7 +343,7 @@
virtual void logStart(const char *fmt);
virtual void logEnd();
virtual void logHash(log_hash_t hash);
- virtual void logHistTS(log_hash_t hash);
+ virtual void logEventHistTs(Event event, log_hash_t hash);
virtual bool isEnabled() const;
diff --git a/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h b/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h
index 89699bf..b0dc148 100644
--- a/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h
+++ b/media/libnbaio/include/media/nbaio/PerformanceAnalysis.h
@@ -23,10 +23,11 @@
#include <deque>
#include <vector>
#include "NBLog.h"
+#include "ReportPerformance.h"
namespace android {
-class String8;
+namespace ReportPerformance {
class PerformanceAnalysis {
// This class stores and analyzes audio processing wakeup timestamps from NBLog
@@ -37,31 +38,25 @@
PerformanceAnalysis();
- // FIXME: decide whether to use 64 or 32 bits
- typedef uint64_t log_hash_t;
+ // Given a series of audio processing wakeup timestamps,
+ // compresses and and analyzes the data, and flushes
+ // the timestamp series from memory.
+ void processAndFlushTimeStampSeries();
- // stores a short-term histogram of size determined by kShortHistSize
- // key: observed buffer period. value: count
- // TODO: unsigned, unsigned
- // TODO: change this name to histogram
- using short_histogram = std::map<int, int>;
+ // Called when an audio on/off event is read from the buffer,
+ // e.g. EVENT_AUDIO_STATE.
+ // calls flushTimeStampSeries on the data up to the event,
+ // effectively discarding the idle audio time interval
+ void handleStateChange();
- using outlierInterval = uint64_t;
- // int64_t timestamps are converted to uint64_t in PerformanceAnalysis::storeOutlierData,
- // and all further analysis functions use uint64_t.
- using timestamp = uint64_t;
- using timestamp_raw = int64_t;
+ // When the short-term histogram array mRecentHists has reached capacity,
+ // merges histograms for data compression and stores them in mLongTermHists
+ void processAndFlushRecentHists();
// Writes wakeup timestamp entry to log and runs analysis
- // author is the thread ID
- // TODO: check. if the thread has multiple histograms, is author info correct
- // FIXME: remove author from arglist. Want to call these function separately on
- // each thread’s data.
- // FIXME: decide whether to store the hash (source file location) instead
- // FIXME: If thread has multiple histograms, check that code works and correct
- // author is stored (test with multiple threads). Need to check that the current
- // code is not receiving data from multiple threads. This could cause odd values.
- void logTsEntry(int author, timestamp_raw ts);
+ // TODO: make this thread safe. Each thread should have its own instance
+ // of PerformanceAnalysis.
+ void logTsEntry(timestamp_raw ts);
// FIXME: make peakdetector and storeOutlierData a single function
// Input: mOutlierData. Looks at time elapsed between outliers
@@ -84,9 +79,7 @@
// This function used to detect glitches in a time series
// TODO incorporate this into the analysis (currently unused)
- void alertIfGlitch(const std::vector<timestamp_raw> &samples);
-
- ~PerformanceAnalysis() {}
+ void alertIfGlitch(const std::vector<timestamp_raw> &samples);
private:
@@ -97,23 +90,24 @@
// a peak is a moment at which the average outlier interval changed significantly
std::deque<timestamp> mPeakTimestamps;
+ // TODO: turn these into circular buffers for better data flow
// FIFO of small histograms
- // stores fixed-size short buffer period histograms with hash and thread data
- // TODO: Turn it into a circular buffer for better data flow
- std::deque<std::pair<int, short_histogram>> mRecentHists;
+ // stores fixed-size short buffer period histograms with timestamp of first sample
+ std::deque<std::pair<timestamp, Histogram>> mRecentHists;
- // map from author to vector of timestamps, collected from NBLog
- // when a vector reaches its maximum size, analysis is run and the data is deleted
- std::map<int, std::vector<timestamp_raw>> mTimeStampSeries;
+ // FIFO of small histograms
+ // stores fixed-size long-term buffer period histograms with timestamp of first sample
+ std::deque<std::pair<timestamp, Histogram>> mLongTermHists;
- // TODO: measure these from the data (e.g., mode) as they may change.
- // const int kGlitchThreshMs = 7;
- // const int kMsPerSec = 1000;
+ // vector of timestamps, collected from NBLog for a (TODO) specific thread
+ // when a vector reaches its maximum size, the data is processed and flushed
+ std::vector<timestamp_raw> mTimeStampSeries;
+
+ static const int kMsPerSec = 1000;
// Parameters used when detecting outliers
// TODO: learn some of these from the data, delete unused ones
// FIXME: decide whether to make kPeriodMs static.
- // The non-const values are (TODO: will be) learned from the data
static const int kNumBuff = 3; // number of buffers considered in local history
int kPeriodMs; // current period length is ideally 4 ms
static const int kOutlierMs = 7; // values greater or equal to this cause glitches
@@ -124,8 +118,15 @@
// Peak detection: number of standard deviations from mean considered a significant change
static const int kStddevThreshold = 5;
+ // capacity allocated to data structures
+ // TODO: adjust all of these values
static const int kRecentHistsCapacity = 100; // number of short-term histograms stored in memory
static const int kShortHistSize = 50; // number of samples in a short-term histogram
+ static const int kOutlierSeriesSize = 100; // number of values stored in outlier array
+ static const int kPeakSeriesSize = 100; // number of values stored in peak array
+ static const int kLongTermHistsCapacity = 20; // number of long-term histogram stored in memory
+ // maximum elapsed time between first and last timestamp of a long-term histogram
+ static const int kMaxHistTimespanMs = 5 * kMsPerSec;
// these variables are stored in-class to ensure continuity while analyzing the timestamp
// series one short sequence at a time: the variables are not re-initialized every time.
@@ -138,14 +139,7 @@
};
-static inline int deltaMs(int64_t ns1, int64_t ns2) {
- return (ns2 - ns1) / (1000 * 1000);
-}
-
-static inline uint32_t log2(uint32_t x) {
- // This works for x > 0
- return 31 - __builtin_clz(x);
-}
+} // namespace ReportPerformance
} // namespace android
diff --git a/media/libnbaio/include/media/nbaio/ReportPerformance.h b/media/libnbaio/include/media/nbaio/ReportPerformance.h
new file mode 100644
index 0000000..27d2810
--- /dev/null
+++ b/media/libnbaio/include/media/nbaio/ReportPerformance.h
@@ -0,0 +1,66 @@
+/*
+ * 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_MEDIA_REPORTPERFORMANCE_H
+#define ANDROID_MEDIA_REPORTPERFORMANCE_H
+
+#include <deque>
+#include <map>
+#include <vector>
+
+namespace android {
+
+// This class is used by reportPerformance function
+// TODO move reportPerformance function to ReportPerformance.cpp
+class String8;
+
+namespace ReportPerformance {
+
+// stores a histogram: key: observed buffer period. value: count
+// TODO: unsigned, unsigned
+using Histogram = std::map<int, int>;
+
+using outlierInterval = uint64_t;
+// int64_t timestamps are converted to uint64_t in PerformanceAnalysis::storeOutlierData,
+// and all analysis functions use uint64_t.
+using timestamp = uint64_t;
+using timestamp_raw = int64_t;
+
+// FIXME: decide whether to use 64 or 32 bits
+// TODO: the code has a mix of typedef and using. Standardize to one or the other.
+typedef uint64_t log_hash_t;
+
+static inline int deltaMs(int64_t ns1, int64_t ns2) {
+ return (ns2 - ns1) / (1000 * 1000);
+}
+
+static inline uint32_t log2(uint32_t x) {
+ // This works for x > 0
+ return 31 - __builtin_clz(x);
+}
+
+// Writes outlier intervals, timestamps, and histograms spanning long time
+// intervals to a file.
+void writeToFile(std::deque<std::pair<outlierInterval, timestamp>> &outlierData,
+ std::deque<std::pair<timestamp, Histogram>> &hists,
+ const char * kName,
+ bool append);
+
+} // namespace ReportPerformance
+
+} // namespace android
+
+#endif // ANDROID_MEDIA_REPORTPERFORMANCE_H
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 27c121f..92399f1 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -311,8 +311,14 @@
int64_t mMinCttsOffsetTicks;
int64_t mMaxCttsOffsetTicks;
- // Save the last 10 frames' timestamp for debug.
- std::list<std::pair<int64_t, int64_t>> mTimestampDebugHelper;
+ // Save the last 10 frames' timestamp and frame type for debug.
+ struct TimestampDebugHelperEntry {
+ int64_t pts;
+ int64_t dts;
+ std::string frameType;
+ };
+
+ std::list<TimestampDebugHelperEntry> mTimestampDebugHelper;
// Sequence parameter set or picture parameter set
struct AVCParamSet {
@@ -2543,12 +2549,12 @@
}
void MPEG4Writer::Track::dumpTimeStamps() {
- ALOGE("Dumping %s track's last 10 frames timestamp ", getTrackType());
+ ALOGE("Dumping %s track's last 10 frames timestamp and frame type ", getTrackType());
std::string timeStampString;
- for (std::list<std::pair<int64_t, int64_t>>::iterator num = mTimestampDebugHelper.begin();
- num != mTimestampDebugHelper.end(); ++num) {
- timeStampString += "(" + std::to_string(num->first)+
- "us, " + std::to_string(num->second) + "us) ";
+ for (std::list<TimestampDebugHelperEntry>::iterator entry = mTimestampDebugHelper.begin();
+ entry != mTimestampDebugHelper.end(); ++entry) {
+ timeStampString += "(" + std::to_string(entry->pts)+
+ "us, " + std::to_string(entry->dts) + "us " + entry->frameType + ") ";
}
ALOGE("%s", timeStampString.c_str());
}
@@ -2758,9 +2764,9 @@
previousPausedDurationUs += pausedDurationUs - lastDurationUs;
mResumed = false;
}
- std::pair<int64_t, int64_t> timestampPair;
+ TimestampDebugHelperEntry timestampDebugEntry;
timestampUs -= previousPausedDurationUs;
- timestampPair.first = timestampUs;
+ timestampDebugEntry.pts = timestampUs;
if (WARN_UNLESS(timestampUs >= 0ll, "for %s track", trackName)) {
copy->release();
mSource->stop();
@@ -2790,6 +2796,14 @@
}
mLastDecodingTimeUs = decodingTimeUs;
+ timestampDebugEntry.dts = decodingTimeUs;
+ timestampDebugEntry.frameType = isSync ? "Key frame" : "Non-Key frame";
+ // Insert the timestamp into the mTimestampDebugHelper
+ if (mTimestampDebugHelper.size() >= kTimestampDebugCount) {
+ mTimestampDebugHelper.pop_front();
+ }
+ mTimestampDebugHelper.push_back(timestampDebugEntry);
+
cttsOffsetTimeUs =
timestampUs + kMaxCttsOffsetTimeUs - decodingTimeUs;
if (WARN_UNLESS(cttsOffsetTimeUs >= 0ll, "for %s track", trackName)) {
@@ -2919,12 +2933,6 @@
lastDurationUs = timestampUs - lastTimestampUs;
lastDurationTicks = currDurationTicks;
lastTimestampUs = timestampUs;
- timestampPair.second = timestampUs;
- // Insert the timestamp into the mTimestampDebugHelper
- if (mTimestampDebugHelper.size() >= kTimestampDebugCount) {
- mTimestampDebugHelper.pop_front();
- }
- mTimestampDebugHelper.push_back(timestampPair);
if (isSync != 0) {
addOneStssTableEntry(mStszTableEntries->count());
diff --git a/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp b/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp
index 6e70ded..9a69226 100644
--- a/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp
+++ b/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp
@@ -89,10 +89,10 @@
}
-static size_t getMinTimestampIdx(OMX_S64 *pNTimeStamp, bool *pIsTimeStampValid) {
+static ssize_t getMinTimestampIdx(OMX_S64 *pNTimeStamp, bool *pIsTimeStampValid) {
OMX_S64 minTimeStamp = LLONG_MAX;
- int idx = -1;
- for (size_t i = 0; i < MAX_TIME_STAMPS; i++) {
+ ssize_t idx = -1;
+ for (ssize_t i = 0; i < MAX_TIME_STAMPS; i++) {
if (pIsTimeStampValid[i]) {
if (pNTimeStamp[i] < minTimeStamp) {
minTimeStamp = pNTimeStamp[i];
@@ -788,10 +788,15 @@
}
if (s_dec_op.u4_output_present) {
- size_t timeStampIdx;
+ ssize_t timeStampIdx;
outHeader->nFilledLen = (mWidth * mHeight * 3) / 2;
timeStampIdx = getMinTimestampIdx(mTimeStamps, mTimeStampsValid);
+ if (timeStampIdx < 0) {
+ ALOGE("b/62872863, Invalid timestamp index!");
+ android_errorWriteLog(0x534e4554, "62872863");
+ return;
+ }
outHeader->nTimeStamp = mTimeStamps[timeStampIdx];
mTimeStampsValid[timeStampIdx] = false;
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index f1b44ae..1cf9744 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -892,6 +892,11 @@
bits.skipBits(2);
unsigned aac_frame_length = bits.getBits(13);
+ if (aac_frame_length == 0){
+ ALOGE("b/62673179, Invalid AAC frame length!");
+ android_errorWriteLog(0x534e4554, "62673179");
+ return NULL;
+ }
bits.skipBits(11); // adts_buffer_fullness
diff --git a/media/libstagefright/omx/SoftOMXPlugin.cpp b/media/libstagefright/omx/SoftOMXPlugin.cpp
index fccb12b..0bc65e1 100644
--- a/media/libstagefright/omx/SoftOMXPlugin.cpp
+++ b/media/libstagefright/omx/SoftOMXPlugin.cpp
@@ -85,7 +85,21 @@
libName.append(kComponents[i].mLibNameSuffix);
libName.append(".so");
- void *libHandle = dlopen(libName.c_str(), RTLD_NOW);
+ // RTLD_NODELETE means we keep the shared library around forever.
+ // this eliminates thrashing during sequences like loading soundpools.
+ // It also leaves the rest of the logic around the dlopen()/dlclose()
+ // calls in this file unchanged.
+ //
+ // Implications of the change:
+ // -- the codec process (where this happens) will have a slightly larger
+ // long-term memory footprint as it accumulates the loaded shared libraries.
+ // This is expected to be a small amount of memory.
+ // -- plugin codecs can no longer (and never should have) depend on a
+ // free reset of any static data as the library would have crossed
+ // a dlclose/dlopen cycle.
+ //
+
+ void *libHandle = dlopen(libName.c_str(), RTLD_NOW|RTLD_NODELETE);
if (libHandle == NULL) {
ALOGE("unable to dlopen %s: %s", libName.c_str(), dlerror());
diff --git a/media/ndk/include/media/NdkMediaCodec.h b/media/ndk/include/media/NdkMediaCodec.h
index 637bf9b..7e7e81e 100644
--- a/media/ndk/include/media/NdkMediaCodec.h
+++ b/media/ndk/include/media/NdkMediaCodec.h
@@ -27,6 +27,7 @@
#ifndef _NDK_MEDIA_CODEC_H
#define _NDK_MEDIA_CODEC_H
+#include <stdint.h>
#include <sys/cdefs.h>
#include "NdkMediaCrypto.h"
@@ -130,17 +131,45 @@
*/
ssize_t AMediaCodec_dequeueInputBuffer(AMediaCodec*, int64_t timeoutUs);
-/**
- * Send the specified buffer to the codec for processing.
+/*
+ * __USE_FILE_OFFSET64 changes the type of off_t in LP32, which changes the ABI
+ * of these declarations to not match the platform. In that case, define these
+ * APIs in terms of int32_t instead. Passing an off_t in this situation will
+ * result in silent truncation unless the user builds with -Wconversion, but the
+ * only alternative it to not expose them at all for this configuration, which
+ * makes the whole API unusable.
+ *
+ * https://github.com/android-ndk/ndk/issues/459
*/
-media_status_t AMediaCodec_queueInputBuffer(AMediaCodec*,
- size_t idx, off_t offset, size_t size, uint64_t time, uint32_t flags);
+#if defined(__USE_FILE_OFFSET64) && !defined(__LP64__)
+#define _off_t_compat int32_t
+#else
+#define _off_t_compat off_t
+#endif /* defined(__USE_FILE_OFFSET64) && !defined(__LP64__) */
+
+#if (defined(__cplusplus) && __cplusplus >= 201103L) || \
+ __STDC_VERSION__ >= 201112L
+static_assert(sizeof(_off_t_compat) == sizeof(long),
+ "_off_t_compat does not match the NDK ABI. See "
+ "https://github.com/android-ndk/ndk/issues/459.");
+#endif
/**
* Send the specified buffer to the codec for processing.
*/
-media_status_t AMediaCodec_queueSecureInputBuffer(AMediaCodec*,
- size_t idx, off_t offset, AMediaCodecCryptoInfo*, uint64_t time, uint32_t flags);
+media_status_t AMediaCodec_queueInputBuffer(AMediaCodec*, size_t idx,
+ _off_t_compat offset, size_t size,
+ uint64_t time, uint32_t flags);
+
+/**
+ * Send the specified buffer to the codec for processing.
+ */
+media_status_t AMediaCodec_queueSecureInputBuffer(AMediaCodec*, size_t idx,
+ _off_t_compat offset,
+ AMediaCodecCryptoInfo*,
+ uint64_t time, uint32_t flags);
+
+#undef _off_t_compat
/**
* Get the index of the next available buffer of processed data.
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 38c9687..0df9a39 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1193,25 +1193,10 @@
String8 value;
if (param.get(String8(AudioParameter::keyBtNrec), value) == NO_ERROR) {
bool btNrecIsOff = (value == AudioParameter::valueOff);
- if (mBtNrecIsOff != btNrecIsOff) {
+ if (mBtNrecIsOff.exchange(btNrecIsOff) != btNrecIsOff) {
for (size_t i = 0; i < mRecordThreads.size(); i++) {
- sp<RecordThread> thread = mRecordThreads.valueAt(i);
- audio_devices_t device = thread->inDevice();
- bool suspend = audio_is_bluetooth_sco_device(device) && btNrecIsOff;
- // collect all of the thread's session IDs
- KeyedVector<audio_session_t, bool> ids = thread->sessionIds();
- // suspend effects associated with those session IDs
- for (size_t j = 0; j < ids.size(); ++j) {
- audio_session_t sessionId = ids.keyAt(j);
- thread->setEffectSuspended(FX_IID_AEC,
- suspend,
- sessionId);
- thread->setEffectSuspended(FX_IID_NS,
- suspend,
- sessionId);
- }
+ mRecordThreads.valueAt(i)->checkBtNrec();
}
- mBtNrecIsOff = btNrecIsOff;
}
}
String8 screenState;
@@ -3214,6 +3199,11 @@
status_t AudioFlinger::putOrphanEffectChain_l(const sp<AudioFlinger::EffectChain>& chain)
{
+ // clear possible suspended state before parking the chain so that it starts in default state
+ // when attached to a new record thread
+ chain->setEffectSuspended_l(FX_IID_AEC, false);
+ chain->setEffectSuspended_l(FX_IID_NS, false);
+
audio_session_t session = chain->sessionId();
ssize_t index = mOrphanEffectChains.indexOfKey(session);
ALOGV("putOrphanEffectChain_l session %d index %zd", session, index);
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 8a96c1d..9023b2d 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -350,12 +350,13 @@
sync_event_callback_t callBack,
const wp<RefBase>& cookie);
+ bool btNrecIsOff() const { return mBtNrecIsOff.load(); }
+
+
private:
audio_mode_t getMode() const { return mMode; }
- bool btNrecIsOff() const { return mBtNrecIsOff; }
-
AudioFlinger() ANDROID_API;
virtual ~AudioFlinger();
@@ -781,7 +782,7 @@
volatile atomic_uint_fast32_t mNextUniqueIds[AUDIO_UNIQUE_ID_USE_MAX];
audio_mode_t mMode;
- bool mBtNrecIsOff;
+ std::atomic_bool mBtNrecIsOff;
// protected by mLock
Vector<AudioSessionRef*> mAudioSessionRefs;
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index f1a55f1..f2c1c4f 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -21,10 +21,12 @@
#include "Configuration.h"
#include <utils/Log.h>
+#include <system/audio_effects/effect_aec.h>
+#include <system/audio_effects/effect_ns.h>
+#include <system/audio_effects/effect_visualizer.h>
#include <audio_utils/primitives.h>
#include <media/audiohal/EffectHalInterface.h>
#include <media/audiohal/EffectsFactoryHalInterface.h>
-#include <system/audio_effects/effect_visualizer.h>
#include "AudioFlinger.h"
#include "ServiceUtilities.h"
@@ -1333,6 +1335,24 @@
ALOGVV("command(), cmdCode: %d, mHasControl: %d, mEffect: %p",
cmdCode, mHasControl, mEffect.unsafe_get());
+ // reject commands reserved for internal use by audio framework if coming from outside
+ // of audioserver
+ switch(cmdCode) {
+ case EFFECT_CMD_ENABLE:
+ case EFFECT_CMD_DISABLE:
+ case EFFECT_CMD_SET_PARAM:
+ case EFFECT_CMD_SET_PARAM_DEFERRED:
+ case EFFECT_CMD_SET_PARAM_COMMIT:
+ case EFFECT_CMD_GET_PARAM:
+ break;
+ default:
+ if (cmdCode >= EFFECT_CMD_FIRST_PROPRIETARY) {
+ break;
+ }
+ android_errorWriteLog(0x534e4554, "62019992");
+ return BAD_VALUE;
+ }
+
if (cmdCode == EFFECT_CMD_ENABLE) {
if (*replySize < sizeof(int)) {
android_errorWriteLog(0x534e4554, "32095713");
@@ -1791,6 +1811,7 @@
idx_insert);
}
effect->configure();
+
return NO_ERROR;
}
@@ -2012,6 +2033,7 @@
mSuspendedEffects.add(type->timeLow, desc);
ALOGV("setEffectSuspended_l() add entry for %08x", type->timeLow);
}
+
if (desc->mRefCount++ == 0) {
sp<EffectModule> effect = getEffectIfEnabled(type);
if (effect != 0) {
@@ -2027,7 +2049,8 @@
desc = mSuspendedEffects.valueAt(index);
if (desc->mRefCount <= 0) {
ALOGW("setEffectSuspended_l() restore refcount should not be 0 %d", desc->mRefCount);
- desc->mRefCount = 1;
+ desc->mRefCount = 0;
+ return;
}
if (--desc->mRefCount == 0) {
ALOGV("setEffectSuspended_l() remove entry for %08x", mSuspendedEffects.keyAt(index));
@@ -2105,6 +2128,17 @@
const effect_uuid_t * const SL_IID_VOLUME = &SL_IID_VOLUME_;
#endif //OPENSL_ES_H_
+/* static */
+bool AudioFlinger::EffectChain::isEffectEligibleForBtNrecSuspend(const effect_uuid_t *type)
+{
+ // Only NS and AEC are suspended when BtNRec is off
+ if ((memcmp(type, FX_IID_AEC, sizeof(effect_uuid_t)) == 0) ||
+ (memcmp(type, FX_IID_NS, sizeof(effect_uuid_t)) == 0)) {
+ return true;
+ }
+ return false;
+}
+
bool AudioFlinger::EffectChain::isEffectEligibleForSuspend(const effect_descriptor_t& desc)
{
// auxiliary effects and visualizer are never suspended on output mix
@@ -2159,7 +2193,7 @@
ALOGV("checkSuspendOnEffectEnabled() enable suspending fx %08x",
effect->desc().type.timeLow);
sp<SuspendedEffectDesc> desc = mSuspendedEffects.valueAt(index);
- // if effect is requested to suspended but was not yet enabled, supend it now.
+ // if effect is requested to suspended but was not yet enabled, suspend it now.
if (desc->mEffect == 0) {
desc->mEffect = effect;
effect->setEnabled(false);
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index e37529e..e29798b 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -331,7 +331,8 @@
void setStrategy(uint32_t strategy)
{ mStrategy = strategy; }
- // suspend effect of the given type
+ // suspend or restore effects of the specified type. The number of suspend requests is counted
+ // and restore occurs once all suspend requests are cancelled.
void setEffectSuspended_l(const effect_uuid_t *type,
bool suspend);
// suspend all eligible effects
@@ -372,7 +373,7 @@
public:
SuspendedEffectDesc() : mRefCount(0) {}
- int mRefCount;
+ int mRefCount; // > 0 when suspended
effect_uuid_t mType;
wp<EffectModule> mEffect;
};
@@ -388,6 +389,8 @@
// types or implementations from the suspend/restore mechanism.
bool isEffectEligibleForSuspend(const effect_descriptor_t& desc);
+ static bool isEffectEligibleForBtNrecSuspend(const effect_uuid_t *type);
+
void clearInputBuffer_l(const sp<ThreadBase>& thread);
void setThread(const sp<ThreadBase>& thread);
@@ -414,6 +417,6 @@
// mSuspendedEffects lists all effects currently suspended in the chain.
// Use effect type UUID timelow field as key. There is no real risk of identical
// timeLow fields among effect type UUIDs.
- // Updated by updateSuspendedSessions_l() only.
+ // Updated by setEffectSuspended_l() and setEffectSuspendedAll_l() only.
KeyedVector< int, sp<SuspendedEffectDesc> > mSuspendedEffects;
};
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index c4a0c0d..c10fa05 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -138,6 +138,8 @@
void FastMixer::onStateChange()
{
+ // log that audio was turned on/off
+ LOG_AUDIO_STATE();
const FastMixerState * const current = (const FastMixerState *) mCurrent;
const FastMixerState * const previous = (const FastMixerState *) mPrevious;
FastMixerDumpState * const dumpState = (FastMixerDumpState *) mDumpState;
diff --git a/services/audioflinger/OWNERS b/services/audioflinger/OWNERS
new file mode 100644
index 0000000..703e4d2
--- /dev/null
+++ b/services/audioflinger/OWNERS
@@ -0,0 +1,3 @@
+hunga@google.com
+jmtrivi@google.com
+mnaganov@google.com
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 7871ae6..8efcce6 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -991,13 +991,6 @@
ALOGW("power manager service died !!!");
}
-void AudioFlinger::ThreadBase::setEffectSuspended(
- const effect_uuid_t *type, bool suspend, audio_session_t sessionId)
-{
- Mutex::Autolock _l(mLock);
- setEffectSuspended_l(type, suspend, sessionId);
-}
-
void AudioFlinger::ThreadBase::setEffectSuspended_l(
const effect_uuid_t *type, bool suspend, audio_session_t sessionId)
{
@@ -1176,6 +1169,11 @@
return BAD_VALUE;
}
+ // always allow effects without processing load or latency
+ if ((desc->flags & EFFECT_FLAG_NO_PROCESS_MASK) == EFFECT_FLAG_NO_PROCESS) {
+ return NO_ERROR;
+ }
+
switch (mType) {
case MIXER: {
// Reject any effect on mixer multichannel sinks.
@@ -1206,10 +1204,6 @@
}
}
- // always allow effects without processing load or latency
- if ((desc->flags & EFFECT_FLAG_NO_PROCESS_MASK) == EFFECT_FLAG_NO_PROCESS) {
- break;
- }
if (flags & AUDIO_OUTPUT_FLAG_RAW) {
ALOGW("checkEffectCompatibility_l(): effect %s on playback thread in raw mode",
desc->name);
@@ -1451,6 +1445,7 @@
effect->setDevice(mInDevice);
effect->setMode(mAudioFlinger->getMode());
effect->setAudioSource(mAudioSource);
+
return NO_ERROR;
}
@@ -5956,6 +5951,7 @@
// mPipeMemory
// mFastCaptureNBLogWriter
, mFastTrackAvail(false)
+ , mBtNrecSuspended(false)
{
snprintf(mThreadName, kThreadNameLength, "AudioIn_%X", id);
mNBLogWriter = audioFlinger->newWriter_l(kLogSize, mThreadName);
@@ -6721,12 +6717,6 @@
}
mTracks.add(track);
- // disable AEC and NS if the device is a BT SCO headset supporting those pre processings
- bool suspend = audio_is_bluetooth_sco_device(mInDevice) &&
- mAudioFlinger->btNrecIsOff();
- setEffectSuspended_l(FX_IID_AEC, suspend, sessionId);
- setEffectSuspended_l(FX_IID_NS, suspend, sessionId);
-
if ((*flags & AUDIO_INPUT_FLAG_FAST) && (tid != -1)) {
pid_t callingPid = IPCThreadState::self()->getCallingPid();
// we don't have CAP_SYS_NICE, nor do we want to have it as it's too powerful,
@@ -7100,6 +7090,26 @@
buffer->frameCount = 0;
}
+void AudioFlinger::RecordThread::checkBtNrec()
+{
+ Mutex::Autolock _l(mLock);
+ checkBtNrec_l();
+}
+
+void AudioFlinger::RecordThread::checkBtNrec_l()
+{
+ // disable AEC and NS if the device is a BT SCO headset supporting those
+ // pre processings
+ bool suspend = audio_is_bluetooth_sco_device(mInDevice) &&
+ mAudioFlinger->btNrecIsOff();
+ if (mBtNrecSuspended.exchange(suspend) != suspend) {
+ for (size_t i = 0; i < mEffectChains.size(); i++) {
+ setEffectSuspended_l(FX_IID_AEC, suspend, mEffectChains[i]->sessionId());
+ setEffectSuspended_l(FX_IID_NS, suspend, mEffectChains[i]->sessionId());
+ }
+ }
+}
+
bool AudioFlinger::RecordThread::checkForNewParameter_l(const String8& keyValuePair,
status_t& status)
@@ -7172,17 +7182,7 @@
if (value != AUDIO_DEVICE_NONE) {
mPrevInDevice = value;
}
- // disable AEC and NS if the device is a BT SCO headset supporting those
- // pre processings
- if (mTracks.size() > 0) {
- bool suspend = audio_is_bluetooth_sco_device(mInDevice) &&
- mAudioFlinger->btNrecIsOff();
- for (size_t i = 0; i < mTracks.size(); i++) {
- sp<RecordTrack> track = mTracks[i];
- setEffectSuspended_l(FX_IID_AEC, suspend, track->sessionId());
- setEffectSuspended_l(FX_IID_NS, suspend, track->sessionId());
- }
- }
+ checkBtNrec_l();
}
}
if (param.getInt(String8(AudioParameter::keyInputSource), value) == NO_ERROR &&
@@ -7415,17 +7415,7 @@
mEffectChains[i]->setDevice_l(mInDevice);
}
- // disable AEC and NS if the device is a BT SCO headset supporting those
- // pre processings
- if (mTracks.size() > 0) {
- bool suspend = audio_is_bluetooth_sco_device(mInDevice) &&
- mAudioFlinger->btNrecIsOff();
- for (size_t i = 0; i < mTracks.size(); i++) {
- sp<RecordTrack> track = mTracks[i];
- setEffectSuspended_l(FX_IID_AEC, suspend, track->sessionId());
- setEffectSuspended_l(FX_IID_NS, suspend, track->sessionId());
- }
- }
+ checkBtNrec_l();
// store new source and send to effects
if (mAudioSource != patch->sinks[0].ext.mix.usecase.source) {
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 22d78eb..32f2bc4 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -361,11 +361,6 @@
virtual uint32_t getStrategyForSession_l(audio_session_t sessionId __unused)
{ return 0; }
- // suspend or restore effect according to the type of effect passed. a NULL
- // type pointer means suspend all effects in the session
- void setEffectSuspended(const effect_uuid_t *type,
- bool suspend,
- audio_session_t sessionId = AUDIO_SESSION_OUTPUT_MIX);
// check if some effects must be suspended/restored when an effect is enabled
// or disabled
void checkSuspendOnEffectEnabled(const sp<EffectModule>& effect,
@@ -417,10 +412,13 @@
void releaseWakeLock_l();
void updateWakeLockUids_l(const SortedVector<uid_t> &uids);
void getPowerManager_l();
+ // suspend or restore effects of the specified type (or all if type is NULL)
+ // on a given session. The number of suspend requests is counted and restore
+ // occurs when all suspend requests are cancelled.
void setEffectSuspended_l(const effect_uuid_t *type,
bool suspend,
audio_session_t sessionId);
- // updated mSuspendedSessions when an effect suspended or restored
+ // updated mSuspendedSessions when an effect is suspended or restored
void updateSuspendedSessions_l(const effect_uuid_t *type,
bool suspend,
audio_session_t sessionId);
@@ -484,6 +482,7 @@
const sp<PMDeathRecipient> mDeathRecipient;
// list of suspended effects per session and per type. The first (outer) vector is
// keyed by session ID, the second (inner) by type UUID timeLow field
+ // Updated by updateSuspendedSessions_l() only.
KeyedVector< audio_session_t, KeyedVector< int, sp<SuspendedSessionDesc> > >
mSuspendedSessions;
static const size_t kLogSize = 4 * 1024;
@@ -1391,6 +1390,8 @@
}
virtual bool isOutput() const override { return false; }
+ void checkBtNrec();
+
private:
// Enter standby if not already in standby, and set mStandby flag
void standbyIfNotAlreadyInStandby();
@@ -1398,6 +1399,8 @@
// Call the HAL standby method unconditionally, and don't change mStandby flag
void inputStandBy();
+ void checkBtNrec_l();
+
AudioStreamIn *mInput;
SortedVector < sp<RecordTrack> > mTracks;
// mActiveTracks has dual roles: it indicates the current active track(s), and
@@ -1457,6 +1460,8 @@
sp<NBLog::Writer> mFastCaptureNBLogWriter;
bool mFastTrackAvail; // true if fast track available
+ // common state to all record threads
+ std::atomic_bool mBtNrecSuspended;
};
class MmapThread : public ThreadBase
diff --git a/services/audioflinger/TypedLogger.h b/services/audioflinger/TypedLogger.h
index 83aa6a1..909af09 100644
--- a/services/audioflinger/TypedLogger.h
+++ b/services/audioflinger/TypedLogger.h
@@ -88,7 +88,11 @@
// Write histogram timestamp entry
#define LOG_HIST_TS() do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \
- x->logHistTS(hash(__FILE__, __LINE__)); } while(0)
+ x->logEventHistTs(NBLog::EVENT_HISTOGRAM_ENTRY_TS, hash(__FILE__, __LINE__)); } while(0)
+
+// Record that audio was turned on/off
+#define LOG_AUDIO_STATE() do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \
+ x->logEventHistTs(NBLog::EVENT_AUDIO_STATE, hash(__FILE__, __LINE__)); } while(0)
namespace android {
extern "C" {
diff --git a/services/audiopolicy/OWNERS b/services/audiopolicy/OWNERS
new file mode 100644
index 0000000..a8483fa
--- /dev/null
+++ b/services/audiopolicy/OWNERS
@@ -0,0 +1,3 @@
+jmtrivi@google.com
+krocard@google.com
+mnaganov@google.com
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index e022057..49423a0 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -138,13 +138,15 @@
requestQueueRet.description().c_str());
return DEAD_OBJECT;
}
+
+ std::unique_ptr<ResultMetadataQueue>& resQueue = mResultMetadataQueue;
auto resultQueueRet = session->getCaptureResultMetadataQueue(
- [&queue = mResultMetadataQueue](const auto& descriptor) {
- queue = std::make_unique<ResultMetadataQueue>(descriptor);
- if (!queue->isValid() || queue->availableToWrite() <= 0) {
+ [&resQueue](const auto& descriptor) {
+ resQueue = std::make_unique<ResultMetadataQueue>(descriptor);
+ if (!resQueue->isValid() || resQueue->availableToWrite() <= 0) {
ALOGE("HAL returns empty result metadata fmq, not use it");
- queue = nullptr;
- // Don't use the queue onwards.
+ resQueue = nullptr;
+ // Don't use the resQueue onwards.
}
});
if (!resultQueueRet.isOk()) {
@@ -153,7 +155,7 @@
return DEAD_OBJECT;
}
- mInterface = std::make_unique<HalInterface>(session, queue);
+ mInterface = new HalInterface(session, queue);
std::string providerType;
mVendorTagId = manager->getProviderTagIdLocked(mId.string());
@@ -182,7 +184,7 @@
mTagMonitor.initialize(mVendorTagId);
/** Start up request queue thread */
- mRequestThread = new RequestThread(this, mStatusTracker, mInterface.get());
+ mRequestThread = new RequestThread(this, mStatusTracker, mInterface);
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)",
@@ -237,7 +239,7 @@
ALOGI("%s: E", __FUNCTION__);
status_t res = OK;
-
+ std::vector<wp<Camera3StreamInterface>> streams;
{
Mutex::Autolock l(mLock);
if (mStatus == STATUS_UNINITIALIZED) return res;
@@ -269,8 +271,13 @@
mRequestThread->requestExit();
}
- mOutputStreams.clear();
- mInputStream.clear();
+ streams.reserve(mOutputStreams.size() + (mInputStream != nullptr ? 1 : 0));
+ for (size_t i = 0; i < mOutputStreams.size(); i++) {
+ streams.push_back(mOutputStreams[i]);
+ }
+ if (mInputStream != nullptr) {
+ streams.push_back(mInputStream);
+ }
}
// Joining done without holding mLock, otherwise deadlocks may ensue
@@ -289,11 +296,8 @@
HalInterface* interface;
{
Mutex::Autolock l(mLock);
-
mRequestThread.clear();
mStatusTracker.clear();
- mBufferManager.clear();
-
interface = mInterface.get();
}
@@ -301,12 +305,25 @@
// wait on assorted callbacks,etc, to complete before it can return.
interface->close();
+ flushInflightRequests();
+
{
Mutex::Autolock l(mLock);
mInterface->clear();
+ mOutputStreams.clear();
+ mInputStream.clear();
+ mBufferManager.clear();
internalUpdateStatusLocked(STATUS_UNINITIALIZED);
}
+ for (auto& weakStream : streams) {
+ sp<Camera3StreamInterface> stream = weakStream.promote();
+ if (stream != nullptr) {
+ ALOGE("%s: Stream %d leaked! strong reference (%d)!",
+ __FUNCTION__, stream->getId(), stream->getStrongCount() - 1);
+ }
+ }
+
ALOGI("%s: X", __FUNCTION__);
return res;
}
@@ -834,6 +851,15 @@
hardware::Return<void> Camera3Device::processCaptureResult(
const hardware::hidl_vec<
hardware::camera::device::V3_2::CaptureResult>& results) {
+ // Ideally we should grab mLock, but that can lead to deadlock, and
+ // it's not super important to get up to date value of mStatus for this
+ // warning print, hence skipping the lock here
+ if (mStatus == STATUS_ERROR) {
+ // Per API contract, HAL should act as closed after device error
+ // But mStatus can be set to error by framework as well, so just log
+ // a warning here.
+ ALOGW("%s: received capture result in error state.", __FUNCTION__);
+ }
if (mProcessCaptureResultLock.tryLock() != OK) {
// This should never happen; it indicates a wrong client implementation
@@ -965,6 +991,16 @@
hardware::Return<void> Camera3Device::notify(
const hardware::hidl_vec<hardware::camera::device::V3_2::NotifyMsg>& msgs) {
+ // Ideally we should grab mLock, but that can lead to deadlock, and
+ // it's not super important to get up to date value of mStatus for this
+ // warning print, hence skipping the lock here
+ if (mStatus == STATUS_ERROR) {
+ // Per API contract, HAL should act as closed after device error
+ // But mStatus can be set to error by framework as well, so just log
+ // a warning here.
+ ALOGW("%s: received notify message in error state.", __FUNCTION__);
+ }
+
for (const auto& msg : msgs) {
notify(msg);
}
@@ -2434,6 +2470,53 @@
}
}
+void Camera3Device::flushInflightRequests() {
+ { // First return buffers cached in mInFlightMap
+ Mutex::Autolock l(mInFlightLock);
+ for (size_t idx = 0; idx < mInFlightMap.size(); idx++) {
+ const InFlightRequest &request = mInFlightMap.valueAt(idx);
+ returnOutputBuffers(request.pendingOutputBuffers.array(),
+ request.pendingOutputBuffers.size(), 0);
+ }
+ mInFlightMap.clear();
+ }
+
+ // Then return all inflight buffers not returned by HAL
+ std::vector<std::pair<int32_t, int32_t>> inflightKeys;
+ mInterface->getInflightBufferKeys(&inflightKeys);
+
+ int32_t inputStreamId = (mInputStream != nullptr) ? mInputStream->getId() : -1;
+ for (auto& pair : inflightKeys) {
+ int32_t frameNumber = pair.first;
+ int32_t streamId = pair.second;
+ buffer_handle_t* buffer;
+ status_t res = mInterface->popInflightBuffer(frameNumber, streamId, &buffer);
+ if (res != OK) {
+ ALOGE("%s: Frame %d: No in-flight buffer for stream %d",
+ __FUNCTION__, frameNumber, streamId);
+ continue;
+ }
+
+ camera3_stream_buffer_t streamBuffer;
+ streamBuffer.buffer = buffer;
+ streamBuffer.status = CAMERA3_BUFFER_STATUS_ERROR;
+ streamBuffer.acquire_fence = -1;
+ streamBuffer.release_fence = -1;
+ if (streamId == inputStreamId) {
+ streamBuffer.stream = mInputStream->asHalStream();
+ res = mInputStream->returnInputBuffer(streamBuffer);
+ if (res != OK) {
+ ALOGE("%s: Can't return input buffer for frame %d to"
+ " its stream:%s (%d)", __FUNCTION__,
+ frameNumber, strerror(-res), res);
+ }
+ } else {
+ streamBuffer.stream = mOutputStreams.valueFor(streamId)->asHalStream();
+ returnOutputBuffers(&streamBuffer, /*size*/1, /*timestamp*/ 0);
+ }
+ }
+}
+
void Camera3Device::insertResultLocked(CaptureResult *result,
uint32_t frameNumber) {
if (result == nullptr) return;
@@ -3349,6 +3432,20 @@
return res;
}
+void Camera3Device::HalInterface::getInflightBufferKeys(
+ std::vector<std::pair<int32_t, int32_t>>* out) {
+ std::lock_guard<std::mutex> lock(mInflightLock);
+ out->clear();
+ out->reserve(mInflightBufferMap.size());
+ for (auto& pair : mInflightBufferMap) {
+ uint64_t key = pair.first;
+ int32_t streamId = key & 0xFFFFFFFF;
+ int32_t frameNumber = (key >> 32) & 0xFFFFFFFF;
+ out->push_back(std::make_pair(frameNumber, streamId));
+ }
+ return;
+}
+
status_t Camera3Device::HalInterface::pushInflightBufferLocked(
int32_t frameNumber, int32_t streamId, buffer_handle_t *buffer, int acquireFence) {
uint64_t key = static_cast<uint64_t>(frameNumber) << 32 | static_cast<uint64_t>(streamId);
@@ -3423,7 +3520,7 @@
Camera3Device::RequestThread::RequestThread(wp<Camera3Device> parent,
sp<StatusTracker> statusTracker,
- HalInterface* interface) :
+ sp<HalInterface> interface) :
Thread(/*canCallJava*/false),
mParent(parent),
mStatusTracker(statusTracker),
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 5549dd1..0251d62 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -265,6 +265,10 @@
status_t popInflightBuffer(int32_t frameNumber, int32_t streamId,
/*out*/ buffer_handle_t **buffer);
+ // Get a vector of (frameNumber, streamId) pair of currently inflight
+ // buffers
+ void getInflightBufferKeys(std::vector<std::pair<int32_t, int32_t>>* out);
+
private:
camera3_device_t *mHal3Device;
sp<hardware::camera::device::V3_2::ICameraDeviceSession> mHidlSession;
@@ -333,7 +337,7 @@
std::vector<std::pair<int, uint64_t>> mFreedBuffers;
};
- std::unique_ptr<HalInterface> mInterface;
+ sp<HalInterface> mInterface;
CameraMetadata mDeviceInfo;
@@ -624,7 +628,7 @@
RequestThread(wp<Camera3Device> parent,
sp<camera3::StatusTracker> statusTracker,
- HalInterface* interface);
+ sp<HalInterface> interface);
~RequestThread();
void setNotificationListener(wp<NotificationListener> listener);
@@ -774,7 +778,7 @@
wp<Camera3Device> mParent;
wp<camera3::StatusTracker> mStatusTracker;
- HalInterface* mInterface;
+ sp<HalInterface> mInterface;
wp<NotificationListener> mListener;
@@ -1023,6 +1027,10 @@
// Remove the in-flight request of the given index from mInFlightMap
// if it's no longer needed. It must only be called with mInFlightLock held.
void removeInFlightRequestIfReadyLocked(int idx);
+ // Remove all in-flight requests and return all buffers.
+ // This is used after HAL interface is closed to cleanup any request/buffers
+ // not returned by HAL.
+ void flushInflightRequests();
/**** End scope for mInFlightLock ****/
diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.cpp b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
index 4eb15ad..35096eb 100644
--- a/services/camera/libcameraservice/device3/Camera3InputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
@@ -293,8 +293,9 @@
void Camera3InputStream::onBufferFreed(const wp<GraphicBuffer>& gb) {
const sp<GraphicBuffer> buffer = gb.promote();
if (buffer != nullptr) {
- if (mBufferFreedListener != nullptr) {
- mBufferFreedListener->onBufferFreed(mId, buffer->handle);
+ sp<Camera3StreamBufferFreedListener> callback = mBufferFreedListener.promote();
+ if (callback != nullptr) {
+ callback->onBufferFreed(mId, buffer->handle);
}
} else {
ALOGE("%s: GraphicBuffer is freed before onBufferFreed callback finishes!", __FUNCTION__);
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index e15aa43..b02cd6a 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -727,7 +727,7 @@
void Camera3OutputStream::onBuffersRemovedLocked(
const std::vector<sp<GraphicBuffer>>& removedBuffers) {
- Camera3StreamBufferFreedListener* callback = mBufferFreedListener;
+ sp<Camera3StreamBufferFreedListener> callback = mBufferFreedListener.promote();
if (callback != nullptr) {
for (auto gb : removedBuffers) {
callback->onBufferFreed(mId, gb->handle);
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index 9297ac8..e77421a 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -523,6 +523,8 @@
return BAD_VALUE;
}
+ removeOutstandingBuffer(buffer);
+
/**
* TODO: Check that the state is valid first.
*
@@ -540,7 +542,6 @@
// buffer to be returned.
mOutputBufferReturnedSignal.signal();
- removeOutstandingBuffer(buffer);
return res;
}
@@ -591,13 +592,14 @@
return BAD_VALUE;
}
+ removeOutstandingBuffer(buffer);
+
status_t res = returnInputBufferLocked(buffer);
if (res == OK) {
fireBufferListenersLocked(buffer, /*acquired*/false, /*output*/false);
mInputBufferReturnedSignal.signal();
}
- removeOutstandingBuffer(buffer);
return res;
}
@@ -744,7 +746,7 @@
}
void Camera3Stream::setBufferFreedListener(
- Camera3StreamBufferFreedListener* listener) {
+ wp<Camera3StreamBufferFreedListener> listener) {
Mutex::Autolock l(mLock);
// Only allow set listener during stream configuration because stream is guaranteed to be IDLE
// at this state, so setBufferFreedListener won't collide with onBufferFreed callbacks
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index b6c8396..0940d62 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -371,7 +371,7 @@
// Setting listener will remove previous listener (if exists)
virtual void setBufferFreedListener(
- Camera3StreamBufferFreedListener* listener) override;
+ wp<Camera3StreamBufferFreedListener> listener) override;
/**
* Return if the buffer queue of the stream is abandoned.
@@ -416,7 +416,7 @@
android_dataspace dataSpace, camera3_stream_rotation_t rotation,
int setId);
- Camera3StreamBufferFreedListener* mBufferFreedListener;
+ wp<Camera3StreamBufferFreedListener> mBufferFreedListener;
/**
* Interface to be implemented by derived classes
diff --git a/services/camera/libcameraservice/device3/Camera3StreamBufferFreedListener.h b/services/camera/libcameraservice/device3/Camera3StreamBufferFreedListener.h
index 478a752..104cd22 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamBufferFreedListener.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamBufferFreedListener.h
@@ -24,7 +24,7 @@
namespace camera3 {
-class Camera3StreamBufferFreedListener {
+class Camera3StreamBufferFreedListener : public virtual RefBase {
public:
// onBufferFreed is called when a buffer is no longer being managed
// by this stream. This will not be called in events when all
diff --git a/services/camera/libcameraservice/device3/Camera3StreamInterface.h b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
index c695a10..0544a1b 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
@@ -298,7 +298,7 @@
* Client is responsible to keep the listener object alive throughout the lifecycle of this
* Camera3Stream.
*/
- virtual void setBufferFreedListener(Camera3StreamBufferFreedListener* listener) = 0;
+ virtual void setBufferFreedListener(wp<Camera3StreamBufferFreedListener> listener) = 0;
};
} // namespace camera3
diff --git a/services/oboeservice/AAudioClientTracker.cpp b/services/oboeservice/AAudioClientTracker.cpp
index c0bea13..75392bd 100644
--- a/services/oboeservice/AAudioClientTracker.cpp
+++ b/services/oboeservice/AAudioClientTracker.cpp
@@ -64,7 +64,7 @@
// Create a tracker for the client.
aaudio_result_t AAudioClientTracker::registerClient(pid_t pid,
const sp<IAAudioClient>& client) {
- ALOGD("AAudioClientTracker::registerClient(), calling pid = %d, getpid() = %d\n",
+ ALOGV("AAudioClientTracker::registerClient(), calling pid = %d, getpid() = %d\n",
pid, getpid());
std::lock_guard<std::mutex> lock(mLock);
@@ -84,7 +84,7 @@
}
void AAudioClientTracker::unregisterClient(pid_t pid) {
- ALOGD("AAudioClientTracker::unregisterClient(), calling pid = %d, getpid() = %d\n",
+ ALOGV("AAudioClientTracker::unregisterClient(), calling pid = %d, getpid() = %d\n",
pid, getpid());
std::lock_guard<std::mutex> lock(mLock);
mNotificationClients.erase(pid);
@@ -103,12 +103,12 @@
aaudio_result_t
AAudioClientTracker::registerClientStream(pid_t pid, sp<AAudioServiceStreamBase> serviceStream) {
aaudio_result_t result = AAUDIO_OK;
- ALOGD("AAudioClientTracker::registerClientStream(%d, %p)\n", pid, serviceStream.get());
+ ALOGV("AAudioClientTracker::registerClientStream(%d, %p)\n", pid, serviceStream.get());
std::lock_guard<std::mutex> lock(mLock);
sp<NotificationClient> notificationClient = mNotificationClients[pid];
if (notificationClient == 0) {
// This will get called the first time the audio server registers an internal stream.
- ALOGD("AAudioClientTracker::registerClientStream(%d,) unrecognized pid\n", pid);
+ ALOGV("AAudioClientTracker::registerClientStream(%d,) unrecognized pid\n", pid);
notificationClient = new NotificationClient(pid);
mNotificationClients[pid] = notificationClient;
}
@@ -120,11 +120,11 @@
aaudio_result_t
AAudioClientTracker::unregisterClientStream(pid_t pid,
sp<AAudioServiceStreamBase> serviceStream) {
- ALOGD("AAudioClientTracker::unregisterClientStream(%d, %p)\n", pid, serviceStream.get());
+ ALOGV("AAudioClientTracker::unregisterClientStream(%d, %p)\n", pid, serviceStream.get());
std::lock_guard<std::mutex> lock(mLock);
auto it = mNotificationClients.find(pid);
if (it != mNotificationClients.end()) {
- ALOGD("AAudioClientTracker::unregisterClientStream(%d, %p) found NotificationClient\n",
+ ALOGV("AAudioClientTracker::unregisterClientStream(%d, %p) found NotificationClient\n",
pid, serviceStream.get());
it->second->unregisterClientStream(serviceStream);
} else {
@@ -158,11 +158,7 @@
aaudio_result_t AAudioClientTracker::NotificationClient::unregisterClientStream(
sp<AAudioServiceStreamBase> serviceStream) {
std::lock_guard<std::mutex> lock(mLock);
- ALOGD("AAudioClientTracker::NotificationClient() before erase() count = %d\n",
- (int)mStreams.size());
mStreams.erase(serviceStream);
- ALOGD("AAudioClientTracker::NotificationClient() after erase() count = %d\n",
- (int)mStreams.size());
return AAUDIO_OK;
}
@@ -176,8 +172,6 @@
{
std::lock_guard<std::mutex> lock(mLock);
- ALOGV("AAudioClientTracker::binderDied() pid = %d, # streams = %d\n",
- mProcessId, (int) mStreams.size());
for (auto serviceStream : mStreams) {
streamsToClose.insert(serviceStream);
}
diff --git a/services/oboeservice/AAudioServiceEndpoint.cpp b/services/oboeservice/AAudioServiceEndpoint.cpp
index 5895974..0f863fe 100644
--- a/services/oboeservice/AAudioServiceEndpoint.cpp
+++ b/services/oboeservice/AAudioServiceEndpoint.cpp
@@ -84,7 +84,7 @@
// Don't fall back to SHARED because that would cause recursion.
builder.setSharingModeMatchRequired(true);
builder.setDeviceId(mRequestedDeviceId);
- builder.setFormat(configuration.getAudioFormat());
+ builder.setFormat(configuration.getFormat());
builder.setSampleRate(configuration.getSampleRate());
builder.setSamplesPerFrame(configuration.getSamplesPerFrame());
builder.setDirection(getDirection());
@@ -112,10 +112,10 @@
}
aaudio_result_t AAudioServiceEndpoint::startStream(sp<AAudioServiceStreamShared> sharedStream) {
- // TODO use real-time technique to avoid mutex, eg. atomic command FIFO
aaudio_result_t result = AAUDIO_OK;
- std::lock_guard<std::mutex> lock(mLockStreams);
if (++mRunningStreams == 1) {
+ // TODO use real-time technique to avoid mutex, eg. atomic command FIFO
+ std::lock_guard<std::mutex> lock(mLockStreams);
result = getStreamInternal()->requestStart();
startSharingThread_l();
}
@@ -123,13 +123,8 @@
}
aaudio_result_t AAudioServiceEndpoint::stopStream(sp<AAudioServiceStreamShared> sharedStream) {
- int numRunningStreams = 0;
- {
- std::lock_guard<std::mutex> lock(mLockStreams);
- numRunningStreams = --mRunningStreams;
- }
- if (numRunningStreams == 0) {
- // Don't call this under a lock because the callbackLoop also uses the lock.
+ // Don't lock here because the disconnectRegisteredStreams also uses the lock.
+ if (--mRunningStreams == 0) { // atomic
stopSharingThread();
getStreamInternal()->requestStop();
}
diff --git a/services/oboeservice/AAudioServiceEndpoint.h b/services/oboeservice/AAudioServiceEndpoint.h
index ed995e5..e40a670 100644
--- a/services/oboeservice/AAudioServiceEndpoint.h
+++ b/services/oboeservice/AAudioServiceEndpoint.h
@@ -73,13 +73,13 @@
virtual AudioStreamInternal *getStreamInternal() = 0;
- std::atomic<bool> mCallbackEnabled;
+ std::atomic<bool> mCallbackEnabled{false};
mutable std::mutex mLockStreams;
std::vector<android::sp<AAudioServiceStreamShared>> mRegisteredStreams;
- size_t mRunningStreams = 0;
+ std::atomic<int> mRunningStreams{0};
private:
aaudio_result_t startSharingThread_l();
diff --git a/services/oboeservice/AAudioServiceEndpointPlay.cpp b/services/oboeservice/AAudioServiceEndpointPlay.cpp
index 86ccde0..e609ab5 100644
--- a/services/oboeservice/AAudioServiceEndpointPlay.cpp
+++ b/services/oboeservice/AAudioServiceEndpointPlay.cpp
@@ -57,7 +57,6 @@
mLatencyTuningEnabled = true;
burstsPerBuffer = BURSTS_PER_BUFFER_DEFAULT;
}
- ALOGD("AAudioServiceEndpoint(): burstsPerBuffer = %d", burstsPerBuffer);
int32_t desiredBufferSize = burstsPerBuffer * getStreamInternal()->getFramesPerBurst();
getStreamInternal()->setBufferSize(desiredBufferSize);
}
@@ -66,7 +65,6 @@
// Mix data from each application stream and write result to the shared MMAP stream.
void *AAudioServiceEndpointPlay::callbackLoop() {
- ALOGD("AAudioServiceEndpointPlay(): callbackLoop() entering");
int32_t underflowCount = 0;
aaudio_result_t result = AAUDIO_OK;
int64_t timeoutNanos = getStreamInternal()->calculateReasonableTimeout();
@@ -102,6 +100,8 @@
}
}
- ALOGD("AAudioServiceEndpointPlay(): callbackLoop() exiting, %d underflows", underflowCount);
+ ALOGW_IF((underflowCount > 0),
+ "AAudioServiceEndpointPlay(): callbackLoop() had %d underflows", underflowCount);
+
return NULL; // TODO review
}
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index e0f6ad4..5f7d179 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -219,6 +219,8 @@
// (long long) command.timestamp.timestamp);
command.what = AAudioServiceMessage::code::TIMESTAMP;
result = writeUpMessageQueue(&command);
+ } else if (result == AAUDIO_ERROR_UNAVAILABLE) {
+ result = AAUDIO_OK; // just not available yet, try again later
}
return result;
}
diff --git a/services/oboeservice/AAudioServiceStreamBase.h b/services/oboeservice/AAudioServiceStreamBase.h
index 93a522e..cebefec 100644
--- a/services/oboeservice/AAudioServiceStreamBase.h
+++ b/services/oboeservice/AAudioServiceStreamBase.h
@@ -163,6 +163,11 @@
aaudio_result_t sendCurrentTimestamp();
+ /**
+ * @param positionFrames
+ * @param timeNanos
+ * @return AAUDIO_OK or AAUDIO_ERROR_UNAVAILABLE or other negative error
+ */
virtual aaudio_result_t getFreeRunningPosition(int64_t *positionFrames, int64_t *timeNanos) = 0;
virtual aaudio_result_t getDownDataDescription(AudioEndpointParcelable &parcelable) = 0;
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.cpp b/services/oboeservice/AAudioServiceStreamMMAP.cpp
index 68be3c3..afa2ff0 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.cpp
+++ b/services/oboeservice/AAudioServiceStreamMMAP.cpp
@@ -95,7 +95,7 @@
aaudio_direction_t direction = request.getDirection();
// Fill in config
- aaudio_format_t aaudioFormat = configurationInput.getAudioFormat();
+ aaudio_format_t aaudioFormat = configurationInput.getFormat();
if (aaudioFormat == AAUDIO_UNSPECIFIED || aaudioFormat == AAUDIO_FORMAT_PCM_FLOAT) {
aaudioFormat = AAUDIO_FORMAT_PCM_I16;
}
@@ -154,9 +154,9 @@
status);
return AAUDIO_ERROR_UNAVAILABLE;
} else {
- ALOGD("createMmapBuffer status %d shared_address = %p buffer_size %d burst_size %d"
- "Sharable FD: %s",
- status, mMmapBufferinfo.shared_memory_address,
+ ALOGD("createMmapBuffer status %d, buffer_size, %d burst_size %d"
+ ", Sharable FD: %s",
+ status,
abs(mMmapBufferinfo.buffer_size_frames),
mMmapBufferinfo.burst_size_frames,
mMmapBufferinfo.buffer_size_frames < 0 ? "Yes" : "No");
@@ -210,7 +210,7 @@
// Fill in AAudioStreamConfiguration
configurationOutput.setSampleRate(mSampleRate);
configurationOutput.setSamplesPerFrame(mSamplesPerFrame);
- configurationOutput.setAudioFormat(mAudioFormat);
+ configurationOutput.setFormat(mAudioFormat);
configurationOutput.setDeviceId(deviceId);
setState(AAUDIO_STREAM_STATE_OPEN);
@@ -297,16 +297,18 @@
return AAUDIO_ERROR_NULL;
}
status_t status = mMmapStream->getMmapPosition(&position);
- if (status != OK) {
- ALOGE("sendCurrentTimestamp(): getMmapPosition() returned %d", status);
+ aaudio_result_t result = AAudioConvert_androidToAAudioResult(status);
+ if (result == AAUDIO_ERROR_UNAVAILABLE) {
+ ALOGW("sendCurrentTimestamp(): getMmapPosition() has no position data yet");
+ } else if (result != AAUDIO_OK) {
+ ALOGE("sendCurrentTimestamp(): getMmapPosition() returned status %d", status);
disconnect();
- return AAudioConvert_androidToAAudioResult(status);
} else {
mFramesRead.update32(position.position_frames);
*positionFrames = mFramesRead.get();
*timeNanos = position.time_nanoseconds;
}
- return AAUDIO_OK;
+ return result;
}
void AAudioServiceStreamMMAP::onTearDown() {
diff --git a/services/oboeservice/AAudioServiceStreamShared.cpp b/services/oboeservice/AAudioServiceStreamShared.cpp
index fe488cb..5654113 100644
--- a/services/oboeservice/AAudioServiceStreamShared.cpp
+++ b/services/oboeservice/AAudioServiceStreamShared.cpp
@@ -112,7 +112,7 @@
}
// Is the request compatible with the shared endpoint?
- mAudioFormat = configurationInput.getAudioFormat();
+ mAudioFormat = configurationInput.getFormat();
if (mAudioFormat == AAUDIO_FORMAT_UNSPECIFIED) {
mAudioFormat = AAUDIO_FORMAT_PCM_FLOAT;
} else if (mAudioFormat != AAUDIO_FORMAT_PCM_FLOAT) {
@@ -169,7 +169,7 @@
// Fill in configuration for client.
configurationOutput.setSampleRate(mSampleRate);
configurationOutput.setSamplesPerFrame(mSamplesPerFrame);
- configurationOutput.setAudioFormat(mAudioFormat);
+ configurationOutput.setFormat(mAudioFormat);
configurationOutput.setDeviceId(mServiceEndpoint->getDeviceId());
result = mServiceEndpoint->registerStream(keep);
diff --git a/services/oboeservice/OWNERS b/services/oboeservice/OWNERS
new file mode 100644
index 0000000..f4d51f9
--- /dev/null
+++ b/services/oboeservice/OWNERS
@@ -0,0 +1 @@
+philburk@google.com
diff --git a/services/oboeservice/SharedMemoryProxy.cpp b/services/oboeservice/SharedMemoryProxy.cpp
index fc4532c..c31557e 100644
--- a/services/oboeservice/SharedMemoryProxy.cpp
+++ b/services/oboeservice/SharedMemoryProxy.cpp
@@ -16,12 +16,11 @@
#define LOG_TAG "AAudioService"
//#define LOG_NDEBUG 0
-#include <utils/Log.h>
+#include <log/log.h>
#include <aaudio/AAudio.h>
#include "SharedMemoryProxy.h"
-using namespace android;
using namespace aaudio;
SharedMemoryProxy::~SharedMemoryProxy()