Merge "Audioflinger: Dump "master mute" state in threads"
diff --git a/camera/ndk/impl/ACameraMetadata.cpp b/camera/ndk/impl/ACameraMetadata.cpp
index fc00a2d..d73f744 100644
--- a/camera/ndk/impl/ACameraMetadata.cpp
+++ b/camera/ndk/impl/ACameraMetadata.cpp
@@ -32,6 +32,10 @@
if (mType == ACM_CHARACTERISTICS) {
filterUnsupportedFeatures();
filterStreamConfigurations();
+ filterDurations(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS);
+ filterDurations(ANDROID_SCALER_AVAILABLE_STALL_DURATIONS);
+ filterDurations(ANDROID_DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS);
+ filterDurations(ANDROID_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS);
}
// TODO: filter request/result keys
}
@@ -82,6 +86,72 @@
void
+ACameraMetadata::filterDurations(uint32_t tag) {
+ const int STREAM_CONFIGURATION_SIZE = 4;
+ const int STREAM_FORMAT_OFFSET = 0;
+ const int STREAM_WIDTH_OFFSET = 1;
+ const int STREAM_HEIGHT_OFFSET = 2;
+ const int STREAM_DURATION_OFFSET = 3;
+ camera_metadata_entry entry = mData.find(tag);
+ if (entry.count == 0 || entry.count % 4 || entry.type != TYPE_INT64) {
+ ALOGE("%s: malformed duration key %d! count %zu, type %d",
+ __FUNCTION__, tag, entry.count, entry.type);
+ return;
+ }
+ Vector<int64_t> filteredDurations;
+ filteredDurations.setCapacity(entry.count * 2);
+
+ for (size_t i=0; i < entry.count; i += STREAM_CONFIGURATION_SIZE) {
+ int64_t format = entry.data.i64[i + STREAM_FORMAT_OFFSET];
+ int64_t width = entry.data.i64[i + STREAM_WIDTH_OFFSET];
+ int64_t height = entry.data.i64[i + STREAM_HEIGHT_OFFSET];
+ int64_t duration = entry.data.i32[i + STREAM_DURATION_OFFSET];
+
+ // Leave the unfiltered format in so apps depending on previous wrong
+ // filter behavior continue to work
+ filteredDurations.push_back(format);
+ filteredDurations.push_back(width);
+ filteredDurations.push_back(height);
+ filteredDurations.push_back(duration);
+
+ // Translate HAL formats to NDK format
+ switch (tag) {
+ case ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS:
+ case ANDROID_SCALER_AVAILABLE_STALL_DURATIONS:
+ if (format == HAL_PIXEL_FORMAT_BLOB) {
+ format = AIMAGE_FORMAT_JPEG;
+ filteredDurations.push_back(format);
+ filteredDurations.push_back(width);
+ filteredDurations.push_back(height);
+ filteredDurations.push_back(duration);
+ }
+ break;
+ case ANDROID_DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS:
+ case ANDROID_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS:
+ if (format == HAL_PIXEL_FORMAT_BLOB) {
+ format = AIMAGE_FORMAT_DEPTH_POINT_CLOUD;
+ filteredDurations.push_back(format);
+ filteredDurations.push_back(width);
+ filteredDurations.push_back(height);
+ filteredDurations.push_back(duration);
+ } else if (format == HAL_PIXEL_FORMAT_Y16) {
+ format = AIMAGE_FORMAT_DEPTH16;
+ filteredDurations.push_back(format);
+ filteredDurations.push_back(width);
+ filteredDurations.push_back(height);
+ filteredDurations.push_back(duration);
+ }
+ break;
+ default:
+ // Should not reach here
+ ALOGE("%s: Unkown tag 0x%x", __FUNCTION__, tag);
+ }
+ }
+
+ mData.update(tag, filteredDurations);
+}
+
+void
ACameraMetadata::filterStreamConfigurations() {
const int STREAM_CONFIGURATION_SIZE = 4;
const int STREAM_FORMAT_OFFSET = 0;
diff --git a/camera/ndk/impl/ACameraMetadata.h b/camera/ndk/impl/ACameraMetadata.h
index 0fd7efa..e76b80c 100644
--- a/camera/ndk/impl/ACameraMetadata.h
+++ b/camera/ndk/impl/ACameraMetadata.h
@@ -58,13 +58,16 @@
camera_status_t getTags(/*out*/int32_t* numTags,
/*out*/const uint32_t** tags) const;
+ const CameraMetadata& getInternalData() const;
+
+ private:
+
bool isNdkSupportedCapability(const int32_t capability);
static inline bool isVendorTag(const uint32_t tag);
static bool isCaptureRequestTag(const uint32_t tag);
void filterUnsupportedFeatures(); // Hide features not yet supported by NDK
void filterStreamConfigurations(); // Hide input streams, translate hal format to NDK formats
-
- const CameraMetadata& getInternalData() const;
+ void filterDurations(uint32_t tag); // translate hal format to NDK formats
template<typename INTERNAL_T, typename NDK_T>
camera_status_t updateImpl(uint32_t tag, uint32_t count, const NDK_T* data) {
@@ -96,7 +99,6 @@
}
}
- private:
// guard access of public APIs: get/update/getTags
mutable Mutex mLock;
CameraMetadata mData;
diff --git a/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp
index c559ff9..1b8e8d9 100644
--- a/cmds/screenrecord/screenrecord.cpp
+++ b/cmds/screenrecord/screenrecord.cpp
@@ -235,10 +235,10 @@
// Set the region of the layer stack we're interested in, which in our
// case is "all of it".
- Rect layerStackRect(mainDpyInfo.w, mainDpyInfo.h);
+ Rect layerStackRect(mainDpyInfo.viewportW, mainDpyInfo.viewportH);
// We need to preserve the aspect ratio of the display.
- float displayAspect = (float) mainDpyInfo.h / (float) mainDpyInfo.w;
+ float displayAspect = (float) mainDpyInfo.viewportH / (float) mainDpyInfo.viewportW;
// Set the way we map the output onto the display surface (which will
@@ -315,22 +315,6 @@
}
/*
- * Set the main display width and height to the actual width and height
- */
-static status_t getActualDisplaySize(const sp<IBinder>& mainDpy, DisplayInfo* mainDpyInfo) {
- Rect viewport;
- status_t err = SurfaceComposerClient::getDisplayViewport(mainDpy, &viewport);
- if (err != NO_ERROR) {
- fprintf(stderr, "ERROR: unable to get display viewport\n");
- return err;
- }
- mainDpyInfo->w = viewport.width();
- mainDpyInfo->h = viewport.height();
-
- return NO_ERROR;
-}
-
-/*
* Runs the MediaCodec encoder, sending the output to the MediaMuxer. The
* input frames are coming from the virtual display as fast as SurfaceFlinger
* wants to send them.
@@ -400,22 +384,14 @@
// useful stuff is hard to get at without a Dalvik VM.
err = SurfaceComposerClient::getDisplayInfo(mainDpy,
&mainDpyInfo);
- if (err == NO_ERROR) {
- err = getActualDisplaySize(mainDpy, &mainDpyInfo);
- if (err != NO_ERROR) {
- fprintf(stderr, "ERROR: unable to set actual display size\n");
- return err;
- }
-
- if (orientation != mainDpyInfo.orientation) {
- ALOGD("orientation changed, now %d", mainDpyInfo.orientation);
- SurfaceComposerClient::Transaction t;
- setDisplayProjection(t, virtualDpy, mainDpyInfo);
- t.apply();
- orientation = mainDpyInfo.orientation;
- }
- } else {
+ if (err != NO_ERROR) {
ALOGW("getDisplayInfo(main) failed: %d", err);
+ } else if (orientation != mainDpyInfo.orientation) {
+ ALOGD("orientation changed, now %d", mainDpyInfo.orientation);
+ SurfaceComposerClient::Transaction t;
+ setDisplayProjection(t, virtualDpy, mainDpyInfo);
+ t.apply();
+ orientation = mainDpyInfo.orientation;
}
}
@@ -589,25 +565,19 @@
return err;
}
- err = getActualDisplaySize(mainDpy, &mainDpyInfo);
- if (err != NO_ERROR) {
- fprintf(stderr, "ERROR: unable to set actual display size\n");
- return err;
- }
-
if (gVerbose) {
printf("Main display is %dx%d @%.2ffps (orientation=%u)\n",
- mainDpyInfo.w, mainDpyInfo.h, mainDpyInfo.fps,
+ mainDpyInfo.viewportW, mainDpyInfo.viewportH, mainDpyInfo.fps,
mainDpyInfo.orientation);
fflush(stdout);
}
// Encoder can't take odd number as config
if (gVideoWidth == 0) {
- gVideoWidth = floorToEven(mainDpyInfo.w);
+ gVideoWidth = floorToEven(mainDpyInfo.viewportW);
}
if (gVideoHeight == 0) {
- gVideoHeight = floorToEven(mainDpyInfo.h);
+ gVideoHeight = floorToEven(mainDpyInfo.viewportH);
}
// Configure and start the encoder.
diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp
index cf08610..ce9dc38 100644
--- a/drm/libmediadrm/DrmHal.cpp
+++ b/drm/libmediadrm/DrmHal.cpp
@@ -318,7 +318,6 @@
for (const auto &instance : registered) {
auto factory = drm::V1_0::IDrmFactory::getService(instance);
if (factory != NULL) {
- ALOGD("found drm@1.0 IDrmFactory %s", instance.c_str());
factories.push_back(factory);
}
}
@@ -329,7 +328,6 @@
for (const auto &instance : registered) {
auto factory = drm::V1_1::IDrmFactory::getService(instance);
if (factory != NULL) {
- ALOGD("found drm@1.1 IDrmFactory %s", instance.c_str());
factories.push_back(factory);
}
}
diff --git a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp
index 73ed8c3..1558e8b 100644
--- a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp
+++ b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp
@@ -118,9 +118,9 @@
status_t ClearKeyCasPlugin::closeSession(const CasSessionId &sessionId) {
ALOGV("closeSession: sessionId=%s", sessionIdToString(sessionId).string());
- sp<ClearKeyCasSession> session =
+ std::shared_ptr<ClearKeyCasSession> session =
ClearKeySessionLibrary::get()->findSession(sessionId);
- if (session == NULL) {
+ if (session.get() == nullptr) {
return ERROR_CAS_SESSION_NOT_OPENED;
}
@@ -132,9 +132,9 @@
const CasSessionId &sessionId, const CasData & /*data*/) {
ALOGV("setSessionPrivateData: sessionId=%s",
sessionIdToString(sessionId).string());
- sp<ClearKeyCasSession> session =
+ std::shared_ptr<ClearKeyCasSession> session =
ClearKeySessionLibrary::get()->findSession(sessionId);
- if (session == NULL) {
+ if (session.get() == nullptr) {
return ERROR_CAS_SESSION_NOT_OPENED;
}
return OK;
@@ -143,9 +143,9 @@
status_t ClearKeyCasPlugin::processEcm(
const CasSessionId &sessionId, const CasEcm& ecm) {
ALOGV("processEcm: sessionId=%s", sessionIdToString(sessionId).string());
- sp<ClearKeyCasSession> session =
+ std::shared_ptr<ClearKeyCasSession> session =
ClearKeySessionLibrary::get()->findSession(sessionId);
- if (session == NULL) {
+ if (session.get() == nullptr) {
return ERROR_CAS_SESSION_NOT_OPENED;
}
@@ -418,15 +418,15 @@
const CasSessionId &sessionId) {
ALOGV("setMediaCasSession: sessionId=%s", sessionIdToString(sessionId).string());
- sp<ClearKeyCasSession> session =
+ std::shared_ptr<ClearKeyCasSession> session =
ClearKeySessionLibrary::get()->findSession(sessionId);
- if (session == NULL) {
+ if (session.get() == nullptr) {
ALOGE("ClearKeyDescramblerPlugin: session not found");
return ERROR_CAS_SESSION_NOT_OPENED;
}
- mCASSession = session;
+ std::atomic_store(&mCASSession, session);
return OK;
}
@@ -447,12 +447,14 @@
subSamplesToString(subSamples, numSubSamples).string(),
srcPtr, dstPtr, srcOffset, dstOffset);
- if (mCASSession == NULL) {
+ std::shared_ptr<ClearKeyCasSession> session = std::atomic_load(&mCASSession);
+
+ if (session.get() == nullptr) {
ALOGE("Uninitialized CAS session!");
return ERROR_CAS_DECRYPT_UNIT_NOT_INITIALIZED;
}
- return mCASSession->decrypt(
+ return session->decrypt(
secure, scramblingControl,
numSubSamples, subSamples,
(uint8_t*)srcPtr + srcOffset,
diff --git a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.h b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.h
index 42cfb8f..389e172 100644
--- a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.h
+++ b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.h
@@ -120,7 +120,7 @@
AString *errorDetailMsg) override;
private:
- sp<ClearKeyCasSession> mCASSession;
+ std::shared_ptr<ClearKeyCasSession> mCASSession;
String8 subSamplesToString(
SubSample const *subSamples,
diff --git a/drm/mediacas/plugins/clearkey/ClearKeySessionLibrary.cpp b/drm/mediacas/plugins/clearkey/ClearKeySessionLibrary.cpp
index 4b4051d..3bb1176 100644
--- a/drm/mediacas/plugins/clearkey/ClearKeySessionLibrary.cpp
+++ b/drm/mediacas/plugins/clearkey/ClearKeySessionLibrary.cpp
@@ -56,7 +56,7 @@
Mutex::Autolock lock(mSessionsLock);
- sp<ClearKeyCasSession> session = new ClearKeyCasSession(plugin);
+ std::shared_ptr<ClearKeyCasSession> session(new ClearKeyCasSession(plugin));
uint8_t *byteArray = (uint8_t *) &mNextSessionId;
sessionId->push_back(byteArray[3]);
@@ -69,7 +69,7 @@
return OK;
}
-sp<ClearKeyCasSession> ClearKeySessionLibrary::findSession(
+std::shared_ptr<ClearKeyCasSession> ClearKeySessionLibrary::findSession(
const CasSessionId& sessionId) {
Mutex::Autolock lock(mSessionsLock);
@@ -88,7 +88,7 @@
return;
}
- sp<ClearKeyCasSession> session = mIDToSessionMap.valueAt(index);
+ std::shared_ptr<ClearKeyCasSession> session = mIDToSessionMap.valueAt(index);
mIDToSessionMap.removeItemsAt(index);
}
@@ -96,7 +96,7 @@
Mutex::Autolock lock(mSessionsLock);
for (ssize_t index = (ssize_t)mIDToSessionMap.size() - 1; index >= 0; index--) {
- sp<ClearKeyCasSession> session = mIDToSessionMap.valueAt(index);
+ std::shared_ptr<ClearKeyCasSession> session = mIDToSessionMap.valueAt(index);
if (session->getPlugin() == plugin) {
mIDToSessionMap.removeItemsAt(index);
}
diff --git a/drm/mediacas/plugins/clearkey/ClearKeySessionLibrary.h b/drm/mediacas/plugins/clearkey/ClearKeySessionLibrary.h
index 01f5f47..a537e63 100644
--- a/drm/mediacas/plugins/clearkey/ClearKeySessionLibrary.h
+++ b/drm/mediacas/plugins/clearkey/ClearKeySessionLibrary.h
@@ -32,6 +32,10 @@
class ClearKeyCasSession : public RefBase {
public:
+ explicit ClearKeyCasSession(CasPlugin *plugin);
+
+ virtual ~ClearKeyCasSession();
+
ssize_t decrypt(
bool secure,
DescramblerPlugin::ScramblingControl scramblingControl,
@@ -58,8 +62,6 @@
friend class ClearKeySessionLibrary;
- explicit ClearKeyCasSession(CasPlugin *plugin);
- virtual ~ClearKeyCasSession();
CasPlugin* getPlugin() const { return mPlugin; }
status_t decryptPayload(
const AES_KEY& key, size_t length, size_t offset, char* buffer) const;
@@ -73,7 +75,7 @@
status_t addSession(CasPlugin *plugin, CasSessionId *sessionId);
- sp<ClearKeyCasSession> findSession(const CasSessionId& sessionId);
+ std::shared_ptr<ClearKeyCasSession> findSession(const CasSessionId& sessionId);
void destroySession(const CasSessionId& sessionId);
@@ -85,7 +87,7 @@
Mutex mSessionsLock;
uint32_t mNextSessionId;
- KeyedVector<CasSessionId, sp<ClearKeyCasSession>> mIDToSessionMap;
+ KeyedVector<CasSessionId, std::shared_ptr<ClearKeyCasSession>> mIDToSessionMap;
ClearKeySessionLibrary();
DISALLOW_EVIL_CONSTRUCTORS(ClearKeySessionLibrary);
diff --git a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
index d51e29d..3b61085 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
@@ -245,11 +245,29 @@
setPlayPolicy();
std::vector<uint8_t> keySetId;
+ keySetId.clear();
+
Status status = session->provideKeyResponse(response);
if (status == Status::OK) {
- // This is for testing AMediaDrm_setOnEventListener only.
- sendEvent(EventType::VENDOR_DEFINED, 0, scope);
- keySetId.clear();
+ // Test calling AMediaDrm listeners.
+ sendEvent(EventType::VENDOR_DEFINED, toVector(scope), toVector(scope));
+
+ sendExpirationUpdate(toVector(scope), 100);
+
+ std::vector<KeyStatus> keysStatus;
+ KeyStatus keyStatus;
+
+ std::vector<uint8_t> keyId1 = { 0xA, 0xB, 0xC };
+ keyStatus.keyId = keyId1;
+ keyStatus.type = V1_0::KeyStatusType::USABLE;
+ keysStatus.push_back(keyStatus);
+
+ std::vector<uint8_t> keyId2 = { 0xD, 0xE, 0xF };
+ keyStatus.keyId = keyId2;
+ keyStatus.type = V1_0::KeyStatusType::EXPIRED;
+ keysStatus.push_back(keyStatus);
+
+ sendKeysChange(toVector(scope), keysStatus, true);
}
installSecureStop(scope);
diff --git a/media/bufferpool/2.0/Accessor.cpp b/media/bufferpool/2.0/Accessor.cpp
new file mode 100644
index 0000000..c1b62f8
--- /dev/null
+++ b/media/bufferpool/2.0/Accessor.cpp
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2018 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 "BufferPoolConnection"
+
+#include "Accessor.h"
+#include "AccessorImpl.h"
+#include "Connection.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V2_0 {
+namespace implementation {
+
+void ConnectionDeathRecipient::add(
+ int64_t connectionId,
+ const sp<Accessor> &accessor) {
+ std::lock_guard<std::mutex> lock(mLock);
+ if (mAccessors.find(connectionId) == mAccessors.end()) {
+ mAccessors.insert(std::make_pair(connectionId, accessor));
+ }
+}
+
+void ConnectionDeathRecipient::remove(int64_t connectionId) {
+ std::lock_guard<std::mutex> lock(mLock);
+ mAccessors.erase(connectionId);
+ auto it = mConnectionToCookie.find(connectionId);
+ if (it != mConnectionToCookie.end()) {
+ uint64_t cookie = it->second;
+ mConnectionToCookie.erase(it);
+ auto cit = mCookieToConnections.find(cookie);
+ if (cit != mCookieToConnections.end()) {
+ cit->second.erase(connectionId);
+ if (cit->second.size() == 0) {
+ mCookieToConnections.erase(cit);
+ }
+ }
+ }
+}
+
+void ConnectionDeathRecipient::addCookieToConnection(
+ uint64_t cookie,
+ int64_t connectionId) {
+ std::lock_guard<std::mutex> lock(mLock);
+ if (mAccessors.find(connectionId) == mAccessors.end()) {
+ return;
+ }
+ mConnectionToCookie.insert(std::make_pair(connectionId, cookie));
+ auto it = mCookieToConnections.find(cookie);
+ if (it != mCookieToConnections.end()) {
+ it->second.insert(connectionId);
+ } else {
+ mCookieToConnections.insert(std::make_pair(
+ cookie, std::set<int64_t>{connectionId}));
+ }
+}
+
+void ConnectionDeathRecipient::serviceDied(
+ uint64_t cookie,
+ const wp<::android::hidl::base::V1_0::IBase>& /* who */
+ ) {
+ std::map<int64_t, const wp<Accessor>> connectionsToClose;
+ {
+ std::lock_guard<std::mutex> lock(mLock);
+
+ auto it = mCookieToConnections.find(cookie);
+ if (it != mCookieToConnections.end()) {
+ for (auto conIt = it->second.begin(); conIt != it->second.end(); ++conIt) {
+ auto accessorIt = mAccessors.find(*conIt);
+ if (accessorIt != mAccessors.end()) {
+ connectionsToClose.insert(std::make_pair(*conIt, accessorIt->second));
+ mAccessors.erase(accessorIt);
+ }
+ mConnectionToCookie.erase(*conIt);
+ }
+ mCookieToConnections.erase(it);
+ }
+ }
+
+ if (connectionsToClose.size() > 0) {
+ sp<Accessor> accessor;
+ for (auto it = connectionsToClose.begin(); it != connectionsToClose.end(); ++it) {
+ accessor = it->second.promote();
+
+ if (accessor) {
+ accessor->close(it->first);
+ ALOGD("connection %lld closed on death", (long long)it->first);
+ }
+ }
+ }
+}
+
+namespace {
+static sp<ConnectionDeathRecipient> sConnectionDeathRecipient =
+ new ConnectionDeathRecipient();
+}
+
+sp<ConnectionDeathRecipient> Accessor::getConnectionDeathRecipient() {
+ return sConnectionDeathRecipient;
+}
+
+// Methods from ::android::hardware::media::bufferpool::V2_0::IAccessor follow.
+Return<void> Accessor::connect(connect_cb _hidl_cb) {
+ sp<Connection> connection;
+ ConnectionId connectionId;
+ const StatusDescriptor* fmqDesc;
+
+ ResultStatus status = connect(&connection, &connectionId, &fmqDesc, false);
+ if (status == ResultStatus::OK) {
+ _hidl_cb(status, connection, connectionId, *fmqDesc,
+ android::hardware::MQDescriptorSync<BufferInvalidationMessage>(
+ std::vector<android::hardware::GrantorDescriptor>(),
+ nullptr /* nhandle */, 0 /* size */));
+ } else {
+ _hidl_cb(status, nullptr, -1LL,
+ android::hardware::MQDescriptorSync<BufferStatusMessage>(
+ std::vector<android::hardware::GrantorDescriptor>(),
+ nullptr /* nhandle */, 0 /* size */),
+ android::hardware::MQDescriptorSync<BufferInvalidationMessage>(
+ std::vector<android::hardware::GrantorDescriptor>(),
+ nullptr /* nhandle */, 0 /* size */));
+ }
+ return Void();
+}
+
+Accessor::Accessor(const std::shared_ptr<BufferPoolAllocator> &allocator)
+ : mImpl(new Impl(allocator)) {}
+
+Accessor::~Accessor() {
+}
+
+bool Accessor::isValid() {
+ return (bool)mImpl;
+}
+
+ResultStatus Accessor::allocate(
+ ConnectionId connectionId,
+ const std::vector<uint8_t> ¶ms,
+ BufferId *bufferId, const native_handle_t** handle) {
+ if (mImpl) {
+ return mImpl->allocate(connectionId, params, bufferId, handle);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus Accessor::fetch(
+ ConnectionId connectionId, TransactionId transactionId,
+ BufferId bufferId, const native_handle_t** handle) {
+ if (mImpl) {
+ return mImpl->fetch(connectionId, transactionId, bufferId, handle);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus Accessor::connect(
+ sp<Connection> *connection, ConnectionId *pConnectionId,
+ const StatusDescriptor** fmqDescPtr, bool local) {
+ if (mImpl) {
+ ResultStatus status = mImpl->connect(this, connection, pConnectionId, fmqDescPtr);
+ if (!local && status == ResultStatus::OK) {
+ sp<Accessor> accessor(this);
+ sConnectionDeathRecipient->add(*pConnectionId, accessor);
+ }
+ return status;
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus Accessor::close(ConnectionId connectionId) {
+ if (mImpl) {
+ ResultStatus status = mImpl->close(connectionId);
+ sConnectionDeathRecipient->remove(connectionId);
+ return status;
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+void Accessor::cleanUp(bool clearCache) {
+ if (mImpl) {
+ mImpl->cleanUp(clearCache);
+ }
+}
+
+//IAccessor* HIDL_FETCH_IAccessor(const char* /* name */) {
+// return new Accessor();
+//}
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
diff --git a/media/bufferpool/2.0/Accessor.h b/media/bufferpool/2.0/Accessor.h
new file mode 100644
index 0000000..fa2cb1b
--- /dev/null
+++ b/media/bufferpool/2.0/Accessor.h
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_ACCESSOR_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_ACCESSOR_H
+
+#include <android/hardware/media/bufferpool/2.0/IAccessor.h>
+#include <bufferpool/BufferPoolTypes.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include "BufferStatus.h"
+
+#include <set>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V2_0 {
+namespace implementation {
+
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct Accessor;
+struct Connection;
+
+/**
+ * Receives death notifications from remote connections.
+ * On death notifications, the connections are closed and used resources
+ * are released.
+ */
+struct ConnectionDeathRecipient : public hardware::hidl_death_recipient {
+ /**
+ * Registers a newly connected connection from remote processes.
+ */
+ void add(int64_t connectionId, const sp<Accessor> &accessor);
+
+ /**
+ * Removes a connection.
+ */
+ void remove(int64_t connectionId);
+
+ void addCookieToConnection(uint64_t cookie, int64_t connectionId);
+
+ virtual void serviceDied(
+ uint64_t /* cookie */,
+ const wp<::android::hidl::base::V1_0::IBase>& /* who */
+ ) override;
+
+private:
+ std::mutex mLock;
+ std::map<uint64_t, std::set<int64_t>> mCookieToConnections;
+ std::map<int64_t, uint64_t> mConnectionToCookie;
+ std::map<int64_t, const wp<Accessor>> mAccessors;
+};
+
+/**
+ * A buffer pool accessor which enables a buffer pool to communicate with buffer
+ * pool clients. 1:1 correspondense holds between a buffer pool and an accessor.
+ */
+struct Accessor : public IAccessor {
+ // Methods from ::android::hardware::media::bufferpool::V2_0::IAccessor follow.
+ Return<void> connect(connect_cb _hidl_cb) override;
+
+ /**
+ * Creates a buffer pool accessor which uses the specified allocator.
+ *
+ * @param allocator buffer allocator.
+ */
+ explicit Accessor(const std::shared_ptr<BufferPoolAllocator> &allocator);
+
+ /** Destructs a buffer pool accessor. */
+ ~Accessor();
+
+ /** Returns whether the accessor is valid. */
+ bool isValid();
+
+ /** Allocates a buffer from a buffer pool.
+ *
+ * @param connectionId the connection id of the client.
+ * @param params the allocation parameters.
+ * @param bufferId the id of the allocated buffer.
+ * @param handle the native handle of the allocated buffer.
+ *
+ * @return OK when a buffer is successfully allocated.
+ * NO_MEMORY when there is no memory.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus allocate(
+ ConnectionId connectionId,
+ const std::vector<uint8_t>& params,
+ BufferId *bufferId,
+ const native_handle_t** handle);
+
+ /**
+ * Fetches a buffer for the specified transaction.
+ *
+ * @param connectionId the id of receiving connection(client).
+ * @param transactionId the id of the transfer transaction.
+ * @param bufferId the id of the buffer to be fetched.
+ * @param handle the native handle of the fetched buffer.
+ *
+ * @return OK when a buffer is successfully fetched.
+ * NO_MEMORY when there is no memory.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus fetch(
+ ConnectionId connectionId,
+ TransactionId transactionId,
+ BufferId bufferId,
+ const native_handle_t** handle);
+
+ /**
+ * Makes a connection to the buffer pool. The buffer pool client uses the
+ * created connection in order to communicate with the buffer pool. An
+ * FMQ for buffer status message is also created for the client.
+ *
+ * @param connection created connection
+ * @param pConnectionId the id of the created connection
+ * @param fmqDescPtr FMQ descriptor for shared buffer status message
+ * queue between a buffer pool and the client.
+ * @param local true when a connection request comes from local process,
+ * false otherwise.
+ *
+ * @return OK when a connection is successfully made.
+ * NO_MEMORY when there is no memory.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus connect(
+ sp<Connection> *connection, ConnectionId *pConnectionId,
+ const StatusDescriptor** fmqDescPtr, bool local);
+
+ /**
+ * Closes the specified connection to the client.
+ *
+ * @param connectionId the id of the connection.
+ *
+ * @return OK when the connection is closed.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus close(ConnectionId connectionId);
+
+ /**
+ * Processes pending buffer status messages and perfoms periodic cache
+ * cleaning.
+ *
+ * @param clearCache if clearCache is true, it frees all buffers waiting
+ * to be recycled.
+ */
+ void cleanUp(bool clearCache);
+
+ /**
+ * Gets a hidl_death_recipient for remote connection death.
+ */
+ static sp<ConnectionDeathRecipient> getConnectionDeathRecipient();
+
+private:
+ class Impl;
+ std::unique_ptr<Impl> mImpl;
+};
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_ACCESSOR_H
diff --git a/media/bufferpool/2.0/AccessorImpl.cpp b/media/bufferpool/2.0/AccessorImpl.cpp
new file mode 100644
index 0000000..ef62d03
--- /dev/null
+++ b/media/bufferpool/2.0/AccessorImpl.cpp
@@ -0,0 +1,543 @@
+/*
+ * Copyright (C) 2018 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 "BufferPoolAccessor"
+//#define LOG_NDEBUG 0
+
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <utils/Log.h>
+#include "AccessorImpl.h"
+#include "Connection.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V2_0 {
+namespace implementation {
+
+namespace {
+ static constexpr int64_t kCleanUpDurationUs = 500000; // TODO tune 0.5 sec
+ static constexpr int64_t kLogDurationUs = 5000000; // 5 secs
+
+ static constexpr size_t kMinAllocBytesForEviction = 1024*1024*15;
+ static constexpr size_t kMinBufferCountForEviction = 40;
+}
+
+// Buffer structure in bufferpool process
+struct InternalBuffer {
+ BufferId mId;
+ size_t mOwnerCount;
+ size_t mTransactionCount;
+ const std::shared_ptr<BufferPoolAllocation> mAllocation;
+ const size_t mAllocSize;
+ const std::vector<uint8_t> mConfig;
+
+ InternalBuffer(
+ BufferId id,
+ const std::shared_ptr<BufferPoolAllocation> &alloc,
+ const size_t allocSize,
+ const std::vector<uint8_t> &allocConfig)
+ : mId(id), mOwnerCount(0), mTransactionCount(0),
+ mAllocation(alloc), mAllocSize(allocSize), mConfig(allocConfig) {}
+
+ const native_handle_t *handle() {
+ return mAllocation->handle();
+ }
+};
+
+struct TransactionStatus {
+ TransactionId mId;
+ BufferId mBufferId;
+ ConnectionId mSender;
+ ConnectionId mReceiver;
+ BufferStatus mStatus;
+ int64_t mTimestampUs;
+ bool mSenderValidated;
+
+ TransactionStatus(const BufferStatusMessage &message, int64_t timestampUs) {
+ mId = message.transactionId;
+ mBufferId = message.bufferId;
+ mStatus = message.newStatus;
+ mTimestampUs = timestampUs;
+ if (mStatus == BufferStatus::TRANSFER_TO) {
+ mSender = message.connectionId;
+ mReceiver = message.targetConnectionId;
+ mSenderValidated = true;
+ } else {
+ mSender = -1LL;
+ mReceiver = message.connectionId;
+ mSenderValidated = false;
+ }
+ }
+};
+
+// Helper template methods for handling map of set.
+template<class T, class U>
+bool insert(std::map<T, std::set<U>> *mapOfSet, T key, U value) {
+ auto iter = mapOfSet->find(key);
+ if (iter == mapOfSet->end()) {
+ std::set<U> valueSet{value};
+ mapOfSet->insert(std::make_pair(key, valueSet));
+ return true;
+ } else if (iter->second.find(value) == iter->second.end()) {
+ iter->second.insert(value);
+ return true;
+ }
+ return false;
+}
+
+template<class T, class U>
+bool erase(std::map<T, std::set<U>> *mapOfSet, T key, U value) {
+ bool ret = false;
+ auto iter = mapOfSet->find(key);
+ if (iter != mapOfSet->end()) {
+ if (iter->second.erase(value) > 0) {
+ ret = true;
+ }
+ if (iter->second.size() == 0) {
+ mapOfSet->erase(iter);
+ }
+ }
+ return ret;
+}
+
+template<class T, class U>
+bool contains(std::map<T, std::set<U>> *mapOfSet, T key, U value) {
+ auto iter = mapOfSet->find(key);
+ if (iter != mapOfSet->end()) {
+ auto setIter = iter->second.find(value);
+ return setIter != iter->second.end();
+ }
+ return false;
+}
+
+int32_t Accessor::Impl::sPid = getpid();
+uint32_t Accessor::Impl::sSeqId = time(nullptr);
+
+Accessor::Impl::Impl(
+ const std::shared_ptr<BufferPoolAllocator> &allocator)
+ : mAllocator(allocator) {}
+
+Accessor::Impl::~Impl() {
+}
+
+ResultStatus Accessor::Impl::connect(
+ const sp<Accessor> &accessor, sp<Connection> *connection,
+ ConnectionId *pConnectionId, const StatusDescriptor** fmqDescPtr) {
+ sp<Connection> newConnection = new Connection();
+ ResultStatus status = ResultStatus::CRITICAL_ERROR;
+ {
+ std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
+ if (newConnection) {
+ ConnectionId id = (int64_t)sPid << 32 | sSeqId;
+ status = mBufferPool.mObserver.open(id, fmqDescPtr);
+ if (status == ResultStatus::OK) {
+ newConnection->initialize(accessor, id);
+ *connection = newConnection;
+ *pConnectionId = id;
+ ++sSeqId;
+ }
+ }
+ mBufferPool.processStatusMessages();
+ mBufferPool.cleanUp();
+ }
+ return status;
+}
+
+ResultStatus Accessor::Impl::close(ConnectionId connectionId) {
+ std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
+ mBufferPool.processStatusMessages();
+ mBufferPool.handleClose(connectionId);
+ mBufferPool.mObserver.close(connectionId);
+ // Since close# will be called after all works are finished, it is OK to
+ // evict unused buffers.
+ mBufferPool.cleanUp(true);
+ return ResultStatus::OK;
+}
+
+ResultStatus Accessor::Impl::allocate(
+ ConnectionId connectionId, const std::vector<uint8_t>& params,
+ BufferId *bufferId, const native_handle_t** handle) {
+ std::unique_lock<std::mutex> lock(mBufferPool.mMutex);
+ mBufferPool.processStatusMessages();
+ ResultStatus status = ResultStatus::OK;
+ if (!mBufferPool.getFreeBuffer(mAllocator, params, bufferId, handle)) {
+ lock.unlock();
+ std::shared_ptr<BufferPoolAllocation> alloc;
+ size_t allocSize;
+ status = mAllocator->allocate(params, &alloc, &allocSize);
+ lock.lock();
+ if (status == ResultStatus::OK) {
+ status = mBufferPool.addNewBuffer(alloc, allocSize, params, bufferId, handle);
+ }
+ ALOGV("create a buffer %d : %u %p",
+ status == ResultStatus::OK, *bufferId, *handle);
+ }
+ if (status == ResultStatus::OK) {
+ // TODO: handle ownBuffer failure
+ mBufferPool.handleOwnBuffer(connectionId, *bufferId);
+ }
+ mBufferPool.cleanUp();
+ return status;
+}
+
+ResultStatus Accessor::Impl::fetch(
+ ConnectionId connectionId, TransactionId transactionId,
+ BufferId bufferId, const native_handle_t** handle) {
+ std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
+ mBufferPool.processStatusMessages();
+ auto found = mBufferPool.mTransactions.find(transactionId);
+ if (found != mBufferPool.mTransactions.end() &&
+ contains(&mBufferPool.mPendingTransactions,
+ connectionId, transactionId)) {
+ if (found->second->mSenderValidated &&
+ found->second->mStatus == BufferStatus::TRANSFER_FROM &&
+ found->second->mBufferId == bufferId) {
+ found->second->mStatus = BufferStatus::TRANSFER_FETCH;
+ auto bufferIt = mBufferPool.mBuffers.find(bufferId);
+ if (bufferIt != mBufferPool.mBuffers.end()) {
+ mBufferPool.mStats.onBufferFetched();
+ *handle = bufferIt->second->handle();
+ return ResultStatus::OK;
+ }
+ }
+ }
+ mBufferPool.cleanUp();
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+void Accessor::Impl::cleanUp(bool clearCache) {
+ // transaction timeout, buffer cacheing TTL handling
+ std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
+ mBufferPool.processStatusMessages();
+ mBufferPool.cleanUp(clearCache);
+}
+
+Accessor::Impl::Impl::BufferPool::BufferPool()
+ : mTimestampUs(getTimestampNow()),
+ mLastCleanUpUs(mTimestampUs),
+ mLastLogUs(mTimestampUs),
+ mSeq(0) {}
+
+
+// Statistics helper
+template<typename T, typename S>
+int percentage(T base, S total) {
+ return int(total ? 0.5 + 100. * static_cast<S>(base) / total : 0);
+}
+
+Accessor::Impl::Impl::BufferPool::~BufferPool() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ ALOGD("Destruction - bufferpool %p "
+ "cached: %zu/%zuM, %zu/%d%% in use; "
+ "allocs: %zu, %d%% recycled; "
+ "transfers: %zu, %d%% unfetced",
+ this, mStats.mBuffersCached, mStats.mSizeCached >> 20,
+ mStats.mBuffersInUse, percentage(mStats.mBuffersInUse, mStats.mBuffersCached),
+ mStats.mTotalAllocations, percentage(mStats.mTotalRecycles, mStats.mTotalAllocations),
+ mStats.mTotalTransfers,
+ percentage(mStats.mTotalTransfers - mStats.mTotalFetches, mStats.mTotalTransfers));
+}
+
+bool Accessor::Impl::BufferPool::handleOwnBuffer(
+ ConnectionId connectionId, BufferId bufferId) {
+
+ bool added = insert(&mUsingBuffers, connectionId, bufferId);
+ if (added) {
+ auto iter = mBuffers.find(bufferId);
+ iter->second->mOwnerCount++;
+ }
+ insert(&mUsingConnections, bufferId, connectionId);
+ return added;
+}
+
+bool Accessor::Impl::BufferPool::handleReleaseBuffer(
+ ConnectionId connectionId, BufferId bufferId) {
+ bool deleted = erase(&mUsingBuffers, connectionId, bufferId);
+ if (deleted) {
+ auto iter = mBuffers.find(bufferId);
+ iter->second->mOwnerCount--;
+ if (iter->second->mOwnerCount == 0 &&
+ iter->second->mTransactionCount == 0) {
+ mStats.onBufferUnused(iter->second->mAllocSize);
+ mFreeBuffers.insert(bufferId);
+ }
+ }
+ erase(&mUsingConnections, bufferId, connectionId);
+ ALOGV("release buffer %u : %d", bufferId, deleted);
+ return deleted;
+}
+
+bool Accessor::Impl::BufferPool::handleTransferTo(const BufferStatusMessage &message) {
+ auto completed = mCompletedTransactions.find(
+ message.transactionId);
+ if (completed != mCompletedTransactions.end()) {
+ // already completed
+ mCompletedTransactions.erase(completed);
+ return true;
+ }
+ // the buffer should exist and be owned.
+ auto bufferIter = mBuffers.find(message.bufferId);
+ if (bufferIter == mBuffers.end() ||
+ !contains(&mUsingBuffers, message.connectionId, message.bufferId)) {
+ return false;
+ }
+ auto found = mTransactions.find(message.transactionId);
+ if (found != mTransactions.end()) {
+ // transfer_from was received earlier.
+ found->second->mSender = message.connectionId;
+ found->second->mSenderValidated = true;
+ return true;
+ }
+ // TODO: verify there is target connection Id
+ mStats.onBufferSent();
+ mTransactions.insert(std::make_pair(
+ message.transactionId,
+ std::make_unique<TransactionStatus>(message, mTimestampUs)));
+ insert(&mPendingTransactions, message.targetConnectionId,
+ message.transactionId);
+ bufferIter->second->mTransactionCount++;
+ return true;
+}
+
+bool Accessor::Impl::BufferPool::handleTransferFrom(const BufferStatusMessage &message) {
+ auto found = mTransactions.find(message.transactionId);
+ if (found == mTransactions.end()) {
+ // TODO: is it feasible to check ownership here?
+ mStats.onBufferSent();
+ mTransactions.insert(std::make_pair(
+ message.transactionId,
+ std::make_unique<TransactionStatus>(message, mTimestampUs)));
+ insert(&mPendingTransactions, message.connectionId,
+ message.transactionId);
+ auto bufferIter = mBuffers.find(message.bufferId);
+ bufferIter->second->mTransactionCount++;
+ } else {
+ if (message.connectionId == found->second->mReceiver) {
+ found->second->mStatus = BufferStatus::TRANSFER_FROM;
+ }
+ }
+ return true;
+}
+
+bool Accessor::Impl::BufferPool::handleTransferResult(const BufferStatusMessage &message) {
+ auto found = mTransactions.find(message.transactionId);
+ if (found != mTransactions.end()) {
+ bool deleted = erase(&mPendingTransactions, message.connectionId,
+ message.transactionId);
+ if (deleted) {
+ if (!found->second->mSenderValidated) {
+ mCompletedTransactions.insert(message.transactionId);
+ }
+ auto bufferIter = mBuffers.find(message.bufferId);
+ if (message.newStatus == BufferStatus::TRANSFER_OK) {
+ handleOwnBuffer(message.connectionId, message.bufferId);
+ }
+ bufferIter->second->mTransactionCount--;
+ if (bufferIter->second->mOwnerCount == 0
+ && bufferIter->second->mTransactionCount == 0) {
+ mStats.onBufferUnused(bufferIter->second->mAllocSize);
+ mFreeBuffers.insert(message.bufferId);
+ }
+ mTransactions.erase(found);
+ }
+ ALOGV("transfer finished %llu %u - %d", (unsigned long long)message.transactionId,
+ message.bufferId, deleted);
+ return deleted;
+ }
+ ALOGV("transfer not found %llu %u", (unsigned long long)message.transactionId,
+ message.bufferId);
+ return false;
+}
+
+void Accessor::Impl::BufferPool::processStatusMessages() {
+ std::vector<BufferStatusMessage> messages;
+ mObserver.getBufferStatusChanges(messages);
+ mTimestampUs = getTimestampNow();
+ for (BufferStatusMessage& message: messages) {
+ bool ret = false;
+ switch (message.newStatus) {
+ case BufferStatus::NOT_USED:
+ ret = handleReleaseBuffer(
+ message.connectionId, message.bufferId);
+ break;
+ case BufferStatus::USED:
+ // not happening
+ break;
+ case BufferStatus::TRANSFER_TO:
+ ret = handleTransferTo(message);
+ break;
+ case BufferStatus::TRANSFER_FROM:
+ ret = handleTransferFrom(message);
+ break;
+ case BufferStatus::TRANSFER_TIMEOUT:
+ // TODO
+ break;
+ case BufferStatus::TRANSFER_LOST:
+ // TODO
+ break;
+ case BufferStatus::TRANSFER_FETCH:
+ // not happening
+ break;
+ case BufferStatus::TRANSFER_OK:
+ case BufferStatus::TRANSFER_ERROR:
+ ret = handleTransferResult(message);
+ break;
+ }
+ if (ret == false) {
+ ALOGW("buffer status message processing failure - message : %d connection : %lld",
+ message.newStatus, (long long)message.connectionId);
+ }
+ }
+ messages.clear();
+}
+
+bool Accessor::Impl::BufferPool::handleClose(ConnectionId connectionId) {
+ // Cleaning buffers
+ auto buffers = mUsingBuffers.find(connectionId);
+ if (buffers != mUsingBuffers.end()) {
+ for (const BufferId& bufferId : buffers->second) {
+ bool deleted = erase(&mUsingConnections, bufferId, connectionId);
+ if (deleted) {
+ auto bufferIter = mBuffers.find(bufferId);
+ bufferIter->second->mOwnerCount--;
+ if (bufferIter->second->mOwnerCount == 0 &&
+ bufferIter->second->mTransactionCount == 0) {
+ // TODO: handle freebuffer insert fail
+ mStats.onBufferUnused(bufferIter->second->mAllocSize);
+ mFreeBuffers.insert(bufferId);
+ }
+ }
+ }
+ mUsingBuffers.erase(buffers);
+ }
+
+ // Cleaning transactions
+ auto pending = mPendingTransactions.find(connectionId);
+ if (pending != mPendingTransactions.end()) {
+ for (const TransactionId& transactionId : pending->second) {
+ auto iter = mTransactions.find(transactionId);
+ if (iter != mTransactions.end()) {
+ if (!iter->second->mSenderValidated) {
+ mCompletedTransactions.insert(transactionId);
+ }
+ BufferId bufferId = iter->second->mBufferId;
+ auto bufferIter = mBuffers.find(bufferId);
+ bufferIter->second->mTransactionCount--;
+ if (bufferIter->second->mOwnerCount == 0 &&
+ bufferIter->second->mTransactionCount == 0) {
+ // TODO: handle freebuffer insert fail
+ mStats.onBufferUnused(bufferIter->second->mAllocSize);
+ mFreeBuffers.insert(bufferId);
+ }
+ mTransactions.erase(iter);
+ }
+ }
+ }
+ return true;
+}
+
+bool Accessor::Impl::BufferPool::getFreeBuffer(
+ const std::shared_ptr<BufferPoolAllocator> &allocator,
+ const std::vector<uint8_t> ¶ms, BufferId *pId,
+ const native_handle_t** handle) {
+ auto bufferIt = mFreeBuffers.begin();
+ for (;bufferIt != mFreeBuffers.end(); ++bufferIt) {
+ BufferId bufferId = *bufferIt;
+ if (allocator->compatible(params, mBuffers[bufferId]->mConfig)) {
+ break;
+ }
+ }
+ if (bufferIt != mFreeBuffers.end()) {
+ BufferId id = *bufferIt;
+ mFreeBuffers.erase(bufferIt);
+ mStats.onBufferRecycled(mBuffers[id]->mAllocSize);
+ *handle = mBuffers[id]->handle();
+ *pId = id;
+ ALOGV("recycle a buffer %u %p", id, *handle);
+ return true;
+ }
+ return false;
+}
+
+ResultStatus Accessor::Impl::BufferPool::addNewBuffer(
+ const std::shared_ptr<BufferPoolAllocation> &alloc,
+ const size_t allocSize,
+ const std::vector<uint8_t> ¶ms,
+ BufferId *pId,
+ const native_handle_t** handle) {
+
+ BufferId bufferId = mSeq++;
+ if (mSeq == Connection::SYNC_BUFFERID) {
+ mSeq = 0;
+ }
+ std::unique_ptr<InternalBuffer> buffer =
+ std::make_unique<InternalBuffer>(
+ bufferId, alloc, allocSize, params);
+ if (buffer) {
+ auto res = mBuffers.insert(std::make_pair(
+ bufferId, std::move(buffer)));
+ if (res.second) {
+ mStats.onBufferAllocated(allocSize);
+ *handle = alloc->handle();
+ *pId = bufferId;
+ return ResultStatus::OK;
+ }
+ }
+ return ResultStatus::NO_MEMORY;
+}
+
+void Accessor::Impl::BufferPool::cleanUp(bool clearCache) {
+ if (clearCache || mTimestampUs > mLastCleanUpUs + kCleanUpDurationUs) {
+ mLastCleanUpUs = mTimestampUs;
+ if (mTimestampUs > mLastLogUs + kLogDurationUs) {
+ mLastLogUs = mTimestampUs;
+ ALOGD("bufferpool %p : %zu(%zu size) total buffers - "
+ "%zu(%zu size) used buffers - %zu/%zu (recycle/alloc) - "
+ "%zu/%zu (fetch/transfer)",
+ this, mStats.mBuffersCached, mStats.mSizeCached,
+ mStats.mBuffersInUse, mStats.mSizeInUse,
+ mStats.mTotalRecycles, mStats.mTotalAllocations,
+ mStats.mTotalFetches, mStats.mTotalTransfers);
+ }
+ for (auto freeIt = mFreeBuffers.begin(); freeIt != mFreeBuffers.end();) {
+ if (!clearCache && mStats.mSizeCached < kMinAllocBytesForEviction
+ && mBuffers.size() < kMinBufferCountForEviction) {
+ break;
+ }
+ auto it = mBuffers.find(*freeIt);
+ if (it != mBuffers.end() &&
+ it->second->mOwnerCount == 0 && it->second->mTransactionCount == 0) {
+ mStats.onBufferEvicted(it->second->mAllocSize);
+ mBuffers.erase(it);
+ freeIt = mFreeBuffers.erase(freeIt);
+ } else {
+ ++freeIt;
+ ALOGW("bufferpool inconsistent!");
+ }
+ }
+ }
+}
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
diff --git a/media/bufferpool/2.0/AccessorImpl.h b/media/bufferpool/2.0/AccessorImpl.h
new file mode 100644
index 0000000..1d33880
--- /dev/null
+++ b/media/bufferpool/2.0/AccessorImpl.h
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_ACCESSORIMPL_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_ACCESSORIMPL_H
+
+#include <map>
+#include <set>
+#include "Accessor.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V2_0 {
+namespace implementation {
+
+struct InternalBuffer;
+struct TransactionStatus;
+
+/**
+ * An implementation of a buffer pool accessor(or a buffer pool implementation.) */
+class Accessor::Impl {
+public:
+ Impl(const std::shared_ptr<BufferPoolAllocator> &allocator);
+
+ ~Impl();
+
+ ResultStatus connect(
+ const sp<Accessor> &accessor, sp<Connection> *connection,
+ ConnectionId *pConnectionId, const StatusDescriptor** fmqDescPtr);
+
+ ResultStatus close(ConnectionId connectionId);
+
+ ResultStatus allocate(ConnectionId connectionId,
+ const std::vector<uint8_t>& params,
+ BufferId *bufferId,
+ const native_handle_t** handle);
+
+ ResultStatus fetch(ConnectionId connectionId,
+ TransactionId transactionId,
+ BufferId bufferId,
+ const native_handle_t** handle);
+
+ void cleanUp(bool clearCache);
+
+private:
+ // ConnectionId = pid : (timestamp_created + seqId)
+ // in order to guarantee uniqueness for each connection
+ static uint32_t sSeqId;
+ static int32_t sPid;
+
+ const std::shared_ptr<BufferPoolAllocator> mAllocator;
+
+ /**
+ * Buffer pool implementation.
+ *
+ * Handles buffer status messages. Handles buffer allocation/recycling.
+ * Handles buffer transfer between buffer pool clients.
+ */
+ struct BufferPool {
+ private:
+ std::mutex mMutex;
+ int64_t mTimestampUs;
+ int64_t mLastCleanUpUs;
+ int64_t mLastLogUs;
+ BufferId mSeq;
+ BufferStatusObserver mObserver;
+
+ std::map<ConnectionId, std::set<BufferId>> mUsingBuffers;
+ std::map<BufferId, std::set<ConnectionId>> mUsingConnections;
+
+ std::map<ConnectionId, std::set<TransactionId>> mPendingTransactions;
+ // Transactions completed before TRANSFER_TO message arrival.
+ // Fetch does not occur for the transactions.
+ // Only transaction id is kept for the transactions in short duration.
+ std::set<TransactionId> mCompletedTransactions;
+ // Currently active(pending) transations' status & information.
+ std::map<TransactionId, std::unique_ptr<TransactionStatus>>
+ mTransactions;
+
+ std::map<BufferId, std::unique_ptr<InternalBuffer>> mBuffers;
+ std::set<BufferId> mFreeBuffers;
+
+ /// Buffer pool statistics which tracks allocation and transfer statistics.
+ struct Stats {
+ /// Total size of allocations which are used or available to use.
+ /// (bytes or pixels)
+ size_t mSizeCached;
+ /// # of cached buffers which are used or available to use.
+ size_t mBuffersCached;
+ /// Total size of allocations which are currently used. (bytes or pixels)
+ size_t mSizeInUse;
+ /// # of currently used buffers
+ size_t mBuffersInUse;
+
+ /// # of allocations called on bufferpool. (# of fetched from BlockPool)
+ size_t mTotalAllocations;
+ /// # of allocations that were served from the cache.
+ /// (# of allocator alloc prevented)
+ size_t mTotalRecycles;
+ /// # of buffer transfers initiated.
+ size_t mTotalTransfers;
+ /// # of transfers that had to be fetched.
+ size_t mTotalFetches;
+
+ Stats()
+ : mSizeCached(0), mBuffersCached(0), mSizeInUse(0), mBuffersInUse(0),
+ mTotalAllocations(0), mTotalRecycles(0), mTotalTransfers(0), mTotalFetches(0) {}
+
+ /// A new buffer is allocated on an allocation request.
+ void onBufferAllocated(size_t allocSize) {
+ mSizeCached += allocSize;
+ mBuffersCached++;
+
+ mSizeInUse += allocSize;
+ mBuffersInUse++;
+
+ mTotalAllocations++;
+ }
+
+ /// A buffer is evicted and destroyed.
+ void onBufferEvicted(size_t allocSize) {
+ mSizeCached -= allocSize;
+ mBuffersCached--;
+ }
+
+ /// A buffer is recycled on an allocation request.
+ void onBufferRecycled(size_t allocSize) {
+ mSizeInUse += allocSize;
+ mBuffersInUse++;
+
+ mTotalAllocations++;
+ mTotalRecycles++;
+ }
+
+ /// A buffer is available to be recycled.
+ void onBufferUnused(size_t allocSize) {
+ mSizeInUse -= allocSize;
+ mBuffersInUse--;
+ }
+
+ /// A buffer transfer is initiated.
+ void onBufferSent() {
+ mTotalTransfers++;
+ }
+
+ /// A buffer fetch is invoked by a buffer transfer.
+ void onBufferFetched() {
+ mTotalFetches++;
+ }
+ } mStats;
+
+ public:
+ /** Creates a buffer pool. */
+ BufferPool();
+
+ /** Destroys a buffer pool. */
+ ~BufferPool();
+
+ /**
+ * Processes all pending buffer status messages, and returns the result.
+ * Each status message is handled by methods with 'handle' prefix.
+ */
+ void processStatusMessages();
+
+ /**
+ * Handles a buffer being owned by a connection.
+ *
+ * @param connectionId the id of the buffer owning connection.
+ * @param bufferId the id of the buffer.
+ *
+ * @return {@code true} when the buffer is owned,
+ * {@code false} otherwise.
+ */
+ bool handleOwnBuffer(ConnectionId connectionId, BufferId bufferId);
+
+ /**
+ * Handles a buffer being released by a connection.
+ *
+ * @param connectionId the id of the buffer owning connection.
+ * @param bufferId the id of the buffer.
+ *
+ * @return {@code true} when the buffer ownership is released,
+ * {@code false} otherwise.
+ */
+ bool handleReleaseBuffer(ConnectionId connectionId, BufferId bufferId);
+
+ /**
+ * Handles a transfer transaction start message from the sender.
+ *
+ * @param message a buffer status message for the transaction.
+ *
+ * @result {@code true} when transfer_to message is acknowledged,
+ * {@code false} otherwise.
+ */
+ bool handleTransferTo(const BufferStatusMessage &message);
+
+ /**
+ * Handles a transfer transaction being acked by the receiver.
+ *
+ * @param message a buffer status message for the transaction.
+ *
+ * @result {@code true} when transfer_from message is acknowledged,
+ * {@code false} otherwise.
+ */
+ bool handleTransferFrom(const BufferStatusMessage &message);
+
+ /**
+ * Handles a transfer transaction result message from the receiver.
+ *
+ * @param message a buffer status message for the transaction.
+ *
+ * @result {@code true} when the exisitng transaction is finished,
+ * {@code false} otherwise.
+ */
+ bool handleTransferResult(const BufferStatusMessage &message);
+
+ /**
+ * Handles a connection being closed, and returns the result. All the
+ * buffers and transactions owned by the connection will be cleaned up.
+ * The related FMQ will be cleaned up too.
+ *
+ * @param connectionId the id of the connection.
+ *
+ * @result {@code true} when the connection existed,
+ * {@code false} otherwise.
+ */
+ bool handleClose(ConnectionId connectionId);
+
+ /**
+ * Recycles a existing free buffer if it is possible.
+ *
+ * @param allocator the buffer allocator
+ * @param params the allocation parameters.
+ * @param pId the id of the recycled buffer.
+ * @param handle the native handle of the recycled buffer.
+ *
+ * @return {@code true} when a buffer is recycled, {@code false}
+ * otherwise.
+ */
+ bool getFreeBuffer(
+ const std::shared_ptr<BufferPoolAllocator> &allocator,
+ const std::vector<uint8_t> ¶ms,
+ BufferId *pId, const native_handle_t **handle);
+
+ /**
+ * Adds a newly allocated buffer to bufferpool.
+ *
+ * @param alloc the newly allocated buffer.
+ * @param allocSize the size of the newly allocated buffer.
+ * @param params the allocation parameters.
+ * @param pId the buffer id for the newly allocated buffer.
+ * @param handle the native handle for the newly allocated buffer.
+ *
+ * @return OK when an allocation is successfully allocated.
+ * NO_MEMORY when there is no memory.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus addNewBuffer(
+ const std::shared_ptr<BufferPoolAllocation> &alloc,
+ const size_t allocSize,
+ const std::vector<uint8_t> ¶ms,
+ BufferId *pId,
+ const native_handle_t **handle);
+
+ /**
+ * Processes pending buffer status messages and performs periodic cache
+ * cleaning.
+ *
+ * @param clearCache if clearCache is true, it frees all buffers
+ * waiting to be recycled.
+ */
+ void cleanUp(bool clearCache = false);
+
+ friend class Accessor::Impl;
+ } mBufferPool;
+};
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace ufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_ACCESSORIMPL_H
diff --git a/media/bufferpool/2.0/Android.bp b/media/bufferpool/2.0/Android.bp
new file mode 100644
index 0000000..413125a
--- /dev/null
+++ b/media/bufferpool/2.0/Android.bp
@@ -0,0 +1,29 @@
+cc_library {
+ name: "libstagefright_bufferpool@2.0",
+ vendor_available: true,
+ srcs: [
+ "Accessor.cpp",
+ "AccessorImpl.cpp",
+ "BufferPoolClient.cpp",
+ "BufferStatus.cpp",
+ "ClientManager.cpp",
+ "Connection.cpp",
+ ],
+ export_include_dirs: [
+ "include",
+ ],
+ shared_libs: [
+ "libcutils",
+ "libfmq",
+ "libhidlbase",
+ "libhwbinder",
+ "libhidltransport",
+ "liblog",
+ "libutils",
+ "android.hardware.media.bufferpool@2.0",
+ ],
+ export_shared_lib_headers: [
+ "libfmq",
+ "android.hardware.media.bufferpool@2.0",
+ ],
+}
diff --git a/media/bufferpool/2.0/BufferPoolClient.cpp b/media/bufferpool/2.0/BufferPoolClient.cpp
new file mode 100644
index 0000000..4eeebb4
--- /dev/null
+++ b/media/bufferpool/2.0/BufferPoolClient.cpp
@@ -0,0 +1,710 @@
+/*
+ * Copyright (C) 2018 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 "BufferPoolClient"
+//#define LOG_NDEBUG 0
+
+#include <thread>
+#include <utils/Log.h>
+#include "BufferPoolClient.h"
+#include "Connection.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V2_0 {
+namespace implementation {
+
+static constexpr int64_t kReceiveTimeoutUs = 1000000; // 100ms
+static constexpr int kPostMaxRetry = 3;
+static constexpr int kCacheTtlUs = 1000000; // TODO: tune
+
+class BufferPoolClient::Impl
+ : public std::enable_shared_from_this<BufferPoolClient::Impl> {
+public:
+ explicit Impl(const sp<Accessor> &accessor);
+
+ explicit Impl(const sp<IAccessor> &accessor);
+
+ bool isValid() {
+ return mValid;
+ }
+
+ bool isLocal() {
+ return mValid && mLocal;
+ }
+
+ ConnectionId getConnectionId() {
+ return mConnectionId;
+ }
+
+ sp<IAccessor> &getAccessor() {
+ return mAccessor;
+ }
+
+ bool isActive(int64_t *lastTransactionUs, bool clearCache);
+
+ ResultStatus allocate(const std::vector<uint8_t> ¶ms,
+ native_handle_t **handle,
+ std::shared_ptr<BufferPoolData> *buffer);
+
+ ResultStatus receive(
+ TransactionId transactionId, BufferId bufferId,
+ int64_t timestampUs,
+ native_handle_t **handle, std::shared_ptr<BufferPoolData> *buffer);
+
+ void postBufferRelease(BufferId bufferId);
+
+ bool postSend(
+ BufferId bufferId, ConnectionId receiver,
+ TransactionId *transactionId, int64_t *timestampUs);
+private:
+
+ bool postReceive(
+ BufferId bufferId, TransactionId transactionId,
+ int64_t timestampUs);
+
+ bool postReceiveResult(
+ BufferId bufferId, TransactionId transactionId, bool result, bool *needsSync);
+
+ void trySyncFromRemote();
+
+ bool syncReleased();
+
+ void evictCaches(bool clearCache = false);
+
+ ResultStatus allocateBufferHandle(
+ const std::vector<uint8_t>& params, BufferId *bufferId,
+ native_handle_t **handle);
+
+ ResultStatus fetchBufferHandle(
+ TransactionId transactionId, BufferId bufferId,
+ native_handle_t **handle);
+
+ struct BlockPoolDataDtor;
+ struct ClientBuffer;
+
+ bool mLocal;
+ bool mValid;
+ sp<IAccessor> mAccessor;
+ sp<Connection> mLocalConnection;
+ sp<IConnection> mRemoteConnection;
+ uint32_t mSeqId;
+ ConnectionId mConnectionId;
+ int64_t mLastEvictCacheUs;
+
+ // CachedBuffers
+ struct BufferCache {
+ std::mutex mLock;
+ bool mCreating;
+ std::condition_variable mCreateCv;
+ std::map<BufferId, std::unique_ptr<ClientBuffer>> mBuffers;
+ int mActive;
+ int64_t mLastChangeUs;
+
+ BufferCache() : mCreating(false), mActive(0), mLastChangeUs(getTimestampNow()) {}
+
+ void incActive_l() {
+ ++mActive;
+ mLastChangeUs = getTimestampNow();
+ }
+
+ void decActive_l() {
+ --mActive;
+ mLastChangeUs = getTimestampNow();
+ }
+ } mCache;
+
+ // FMQ - release notifier
+ struct {
+ std::mutex mLock;
+ // TODO: use only one list?(using one list may dealy sending messages?)
+ std::list<BufferId> mReleasingIds;
+ std::list<BufferId> mReleasedIds;
+ std::unique_ptr<BufferStatusChannel> mStatusChannel;
+ } mReleasing;
+
+ // This lock is held during synchronization from remote side.
+ // In order to minimize remote calls and locking durtaion, this lock is held
+ // by best effort approach using try_lock().
+ std::mutex mRemoteSyncLock;
+};
+
+struct BufferPoolClient::Impl::BlockPoolDataDtor {
+ BlockPoolDataDtor(const std::shared_ptr<BufferPoolClient::Impl> &impl)
+ : mImpl(impl) {}
+
+ void operator()(BufferPoolData *buffer) {
+ BufferId id = buffer->mId;
+ delete buffer;
+
+ auto impl = mImpl.lock();
+ if (impl && impl->isValid()) {
+ impl->postBufferRelease(id);
+ }
+ }
+ const std::weak_ptr<BufferPoolClient::Impl> mImpl;
+};
+
+struct BufferPoolClient::Impl::ClientBuffer {
+private:
+ bool mInvalidated; // TODO: implement
+ int64_t mExpireUs;
+ bool mHasCache;
+ ConnectionId mConnectionId;
+ BufferId mId;
+ native_handle_t *mHandle;
+ std::weak_ptr<BufferPoolData> mCache;
+
+ void updateExpire() {
+ mExpireUs = getTimestampNow() + kCacheTtlUs;
+ }
+
+public:
+ ClientBuffer(
+ ConnectionId connectionId, BufferId id, native_handle_t *handle)
+ : mInvalidated(false), mHasCache(false),
+ mConnectionId(connectionId), mId(id), mHandle(handle) {
+ (void)mInvalidated;
+ mExpireUs = getTimestampNow() + kCacheTtlUs;
+ }
+
+ ~ClientBuffer() {
+ if (mHandle) {
+ native_handle_close(mHandle);
+ native_handle_delete(mHandle);
+ }
+ }
+
+ bool expire() const {
+ int64_t now = getTimestampNow();
+ return now >= mExpireUs;
+ }
+
+ bool hasCache() const {
+ return mHasCache;
+ }
+
+ std::shared_ptr<BufferPoolData> fetchCache(native_handle_t **pHandle) {
+ if (mHasCache) {
+ std::shared_ptr<BufferPoolData> cache = mCache.lock();
+ if (cache) {
+ *pHandle = mHandle;
+ }
+ return cache;
+ }
+ return nullptr;
+ }
+
+ std::shared_ptr<BufferPoolData> createCache(
+ const std::shared_ptr<BufferPoolClient::Impl> &impl,
+ native_handle_t **pHandle) {
+ if (!mHasCache) {
+ // Allocates a raw ptr in order to avoid sending #postBufferRelease
+ // from deleter, in case of native_handle_clone failure.
+ BufferPoolData *ptr = new BufferPoolData(mConnectionId, mId);
+ if (ptr) {
+ std::shared_ptr<BufferPoolData> cache(ptr, BlockPoolDataDtor(impl));
+ if (cache) {
+ mCache = cache;
+ mHasCache = true;
+ *pHandle = mHandle;
+ return cache;
+ }
+ }
+ if (ptr) {
+ delete ptr;
+ }
+ }
+ return nullptr;
+ }
+
+ bool onCacheRelease() {
+ if (mHasCache) {
+ // TODO: verify mCache is not valid;
+ updateExpire();
+ mHasCache = false;
+ return true;
+ }
+ return false;
+ }
+};
+
+BufferPoolClient::Impl::Impl(const sp<Accessor> &accessor)
+ : mLocal(true), mValid(false), mAccessor(accessor), mSeqId(0),
+ mLastEvictCacheUs(getTimestampNow()) {
+ const StatusDescriptor *fmqDesc;
+ ResultStatus status = accessor->connect(
+ &mLocalConnection, &mConnectionId, &fmqDesc, true);
+ if (status == ResultStatus::OK) {
+ mReleasing.mStatusChannel =
+ std::make_unique<BufferStatusChannel>(*fmqDesc);
+ mValid = mReleasing.mStatusChannel &&
+ mReleasing.mStatusChannel->isValid();
+ }
+}
+
+BufferPoolClient::Impl::Impl(const sp<IAccessor> &accessor)
+ : mLocal(false), mValid(false), mAccessor(accessor), mSeqId(0),
+ mLastEvictCacheUs(getTimestampNow()) {
+ bool valid = false;
+ sp<IConnection>& outConnection = mRemoteConnection;
+ ConnectionId& id = mConnectionId;
+ std::unique_ptr<BufferStatusChannel>& outChannel =
+ mReleasing.mStatusChannel;
+ Return<void> transResult = accessor->connect(
+ [&valid, &outConnection, &id, &outChannel]
+ (ResultStatus status, sp<IConnection> connection,
+ ConnectionId connectionId, const StatusDescriptor& desc,
+ const InvalidationDescriptor& invDesc) {
+ (void) invDesc;
+ if (status == ResultStatus::OK) {
+ outConnection = connection;
+ id = connectionId;
+ outChannel = std::make_unique<BufferStatusChannel>(desc);
+ if (outChannel && outChannel->isValid()) {
+ valid = true;
+ }
+ }
+ });
+ mValid = transResult.isOk() && valid;
+}
+
+bool BufferPoolClient::Impl::isActive(int64_t *lastTransactionUs, bool clearCache) {
+ bool active = false;
+ {
+ std::lock_guard<std::mutex> lock(mCache.mLock);
+ syncReleased();
+ evictCaches(clearCache);
+ *lastTransactionUs = mCache.mLastChangeUs;
+ active = mCache.mActive > 0;
+ }
+ if (mValid && mLocal && mLocalConnection) {
+ mLocalConnection->cleanUp(clearCache);
+ return true;
+ }
+ return active;
+}
+
+ResultStatus BufferPoolClient::Impl::allocate(
+ const std::vector<uint8_t> ¶ms,
+ native_handle_t **pHandle,
+ std::shared_ptr<BufferPoolData> *buffer) {
+ if (!mLocal || !mLocalConnection || !mValid) {
+ return ResultStatus::CRITICAL_ERROR;
+ }
+ BufferId bufferId;
+ native_handle_t *handle = nullptr;
+ buffer->reset();
+ ResultStatus status = allocateBufferHandle(params, &bufferId, &handle);
+ if (status == ResultStatus::OK) {
+ if (handle) {
+ std::unique_lock<std::mutex> lock(mCache.mLock);
+ syncReleased();
+ evictCaches();
+ auto cacheIt = mCache.mBuffers.find(bufferId);
+ if (cacheIt != mCache.mBuffers.end()) {
+ // TODO: verify it is recycled. (not having active ref)
+ mCache.mBuffers.erase(cacheIt);
+ }
+ auto clientBuffer = std::make_unique<ClientBuffer>(
+ mConnectionId, bufferId, handle);
+ if (clientBuffer) {
+ auto result = mCache.mBuffers.insert(std::make_pair(
+ bufferId, std::move(clientBuffer)));
+ if (result.second) {
+ *buffer = result.first->second->createCache(
+ shared_from_this(), pHandle);
+ if (*buffer) {
+ mCache.incActive_l();
+ }
+ }
+ }
+ }
+ if (!*buffer) {
+ ALOGV("client cache creation failure %d: %lld",
+ handle != nullptr, (long long)mConnectionId);
+ status = ResultStatus::NO_MEMORY;
+ postBufferRelease(bufferId);
+ }
+ }
+ return status;
+}
+
+ResultStatus BufferPoolClient::Impl::receive(
+ TransactionId transactionId, BufferId bufferId, int64_t timestampUs,
+ native_handle_t **pHandle,
+ std::shared_ptr<BufferPoolData> *buffer) {
+ if (!mValid) {
+ return ResultStatus::CRITICAL_ERROR;
+ }
+ if (timestampUs != 0) {
+ timestampUs += kReceiveTimeoutUs;
+ }
+ if (!postReceive(bufferId, transactionId, timestampUs)) {
+ return ResultStatus::CRITICAL_ERROR;
+ }
+ ResultStatus status = ResultStatus::CRITICAL_ERROR;
+ buffer->reset();
+ while(1) {
+ std::unique_lock<std::mutex> lock(mCache.mLock);
+ syncReleased();
+ evictCaches();
+ auto cacheIt = mCache.mBuffers.find(bufferId);
+ if (cacheIt != mCache.mBuffers.end()) {
+ if (cacheIt->second->hasCache()) {
+ *buffer = cacheIt->second->fetchCache(pHandle);
+ if (!*buffer) {
+ // check transfer time_out
+ lock.unlock();
+ std::this_thread::yield();
+ continue;
+ }
+ ALOGV("client receive from reference %lld", (long long)mConnectionId);
+ break;
+ } else {
+ *buffer = cacheIt->second->createCache(shared_from_this(), pHandle);
+ if (*buffer) {
+ mCache.incActive_l();
+ }
+ ALOGV("client receive from cache %lld", (long long)mConnectionId);
+ break;
+ }
+ } else {
+ if (!mCache.mCreating) {
+ mCache.mCreating = true;
+ lock.unlock();
+ native_handle_t* handle = nullptr;
+ status = fetchBufferHandle(transactionId, bufferId, &handle);
+ lock.lock();
+ if (status == ResultStatus::OK) {
+ if (handle) {
+ auto clientBuffer = std::make_unique<ClientBuffer>(
+ mConnectionId, bufferId, handle);
+ if (clientBuffer) {
+ auto result = mCache.mBuffers.insert(
+ std::make_pair(bufferId, std::move(
+ clientBuffer)));
+ if (result.second) {
+ *buffer = result.first->second->createCache(
+ shared_from_this(), pHandle);
+ if (*buffer) {
+ mCache.incActive_l();
+ }
+ }
+ }
+ }
+ if (!*buffer) {
+ status = ResultStatus::NO_MEMORY;
+ }
+ }
+ mCache.mCreating = false;
+ lock.unlock();
+ mCache.mCreateCv.notify_all();
+ break;
+ }
+ mCache.mCreateCv.wait(lock);
+ }
+ }
+ bool needsSync = false;
+ bool posted = postReceiveResult(bufferId, transactionId,
+ *buffer ? true : false, &needsSync);
+ ALOGV("client receive %lld - %u : %s (%d)", (long long)mConnectionId, bufferId,
+ *buffer ? "ok" : "fail", posted);
+ if (mValid && mLocal && mLocalConnection) {
+ mLocalConnection->cleanUp(false);
+ }
+ if (needsSync && mRemoteConnection) {
+ trySyncFromRemote();
+ }
+ if (*buffer) {
+ if (!posted) {
+ buffer->reset();
+ return ResultStatus::CRITICAL_ERROR;
+ }
+ return ResultStatus::OK;
+ }
+ return status;
+}
+
+
+void BufferPoolClient::Impl::postBufferRelease(BufferId bufferId) {
+ std::lock_guard<std::mutex> lock(mReleasing.mLock);
+ mReleasing.mReleasingIds.push_back(bufferId);
+ mReleasing.mStatusChannel->postBufferRelease(
+ mConnectionId, mReleasing.mReleasingIds, mReleasing.mReleasedIds);
+}
+
+// TODO: revise ad-hoc posting data structure
+bool BufferPoolClient::Impl::postSend(
+ BufferId bufferId, ConnectionId receiver,
+ TransactionId *transactionId, int64_t *timestampUs) {
+ bool ret = false;
+ bool needsSync = false;
+ {
+ std::lock_guard<std::mutex> lock(mReleasing.mLock);
+ *timestampUs = getTimestampNow();
+ *transactionId = (mConnectionId << 32) | mSeqId++;
+ // TODO: retry, add timeout, target?
+ ret = mReleasing.mStatusChannel->postBufferStatusMessage(
+ *transactionId, bufferId, BufferStatus::TRANSFER_TO, mConnectionId,
+ receiver, mReleasing.mReleasingIds, mReleasing.mReleasedIds);
+ needsSync = !mLocal && mReleasing.mStatusChannel->needsSync();
+ }
+ if (mValid && mLocal && mLocalConnection) {
+ mLocalConnection->cleanUp(false);
+ }
+ if (needsSync && mRemoteConnection) {
+ trySyncFromRemote();
+ }
+ return ret;
+}
+
+bool BufferPoolClient::Impl::postReceive(
+ BufferId bufferId, TransactionId transactionId, int64_t timestampUs) {
+ for (int i = 0; i < kPostMaxRetry; ++i) {
+ std::unique_lock<std::mutex> lock(mReleasing.mLock);
+ int64_t now = getTimestampNow();
+ if (timestampUs == 0 || now < timestampUs) {
+ bool result = mReleasing.mStatusChannel->postBufferStatusMessage(
+ transactionId, bufferId, BufferStatus::TRANSFER_FROM,
+ mConnectionId, -1, mReleasing.mReleasingIds,
+ mReleasing.mReleasedIds);
+ if (result) {
+ return true;
+ }
+ lock.unlock();
+ std::this_thread::yield();
+ } else {
+ mReleasing.mStatusChannel->postBufferStatusMessage(
+ transactionId, bufferId, BufferStatus::TRANSFER_TIMEOUT,
+ mConnectionId, -1, mReleasing.mReleasingIds,
+ mReleasing.mReleasedIds);
+ return false;
+ }
+ }
+ return false;
+}
+
+bool BufferPoolClient::Impl::postReceiveResult(
+ BufferId bufferId, TransactionId transactionId, bool result, bool *needsSync) {
+ std::lock_guard<std::mutex> lock(mReleasing.mLock);
+ // TODO: retry, add timeout
+ bool ret = mReleasing.mStatusChannel->postBufferStatusMessage(
+ transactionId, bufferId,
+ result ? BufferStatus::TRANSFER_OK : BufferStatus::TRANSFER_ERROR,
+ mConnectionId, -1, mReleasing.mReleasingIds,
+ mReleasing.mReleasedIds);
+ *needsSync = !mLocal && mReleasing.mStatusChannel->needsSync();
+ return ret;
+}
+
+void BufferPoolClient::Impl::trySyncFromRemote() {
+ if (mRemoteSyncLock.try_lock()) {
+ bool needsSync = false;
+ {
+ std::lock_guard<std::mutex> lock(mReleasing.mLock);
+ needsSync = mReleasing.mStatusChannel->needsSync();
+ }
+ if (needsSync) {
+ TransactionId transactionId = (mConnectionId << 32);
+ BufferId bufferId = Connection::SYNC_BUFFERID;
+ Return<void> transResult = mRemoteConnection->fetch(
+ transactionId, bufferId,
+ []
+ (ResultStatus outStatus, Buffer outBuffer) {
+ (void) outStatus;
+ (void) outBuffer;
+ });
+ }
+ mRemoteSyncLock.unlock();
+ }
+}
+
+// should have mCache.mLock
+bool BufferPoolClient::Impl::syncReleased() {
+ std::lock_guard<std::mutex> lock(mReleasing.mLock);
+ if (mReleasing.mReleasingIds.size() > 0) {
+ mReleasing.mStatusChannel->postBufferRelease(
+ mConnectionId, mReleasing.mReleasingIds,
+ mReleasing.mReleasedIds);
+ }
+ if (mReleasing.mReleasedIds.size() > 0) {
+ for (BufferId& id: mReleasing.mReleasedIds) {
+ ALOGV("client release buffer %lld - %u", (long long)mConnectionId, id);
+ auto found = mCache.mBuffers.find(id);
+ if (found != mCache.mBuffers.end()) {
+ if (found->second->onCacheRelease()) {
+ mCache.decActive_l();
+ } else {
+ // should not happen!
+ ALOGW("client %lld cache release status inconsitent!",
+ (long long)mConnectionId);
+ }
+ } else {
+ // should not happen!
+ ALOGW("client %lld cache status inconsitent!", (long long)mConnectionId);
+ }
+ }
+ mReleasing.mReleasedIds.clear();
+ return true;
+ }
+ return false;
+}
+
+// should have mCache.mLock
+void BufferPoolClient::Impl::evictCaches(bool clearCache) {
+ int64_t now = getTimestampNow();
+ if (now >= mLastEvictCacheUs + kCacheTtlUs || clearCache) {
+ size_t evicted = 0;
+ for (auto it = mCache.mBuffers.begin(); it != mCache.mBuffers.end();) {
+ if (!it->second->hasCache() && (it->second->expire() || clearCache)) {
+ it = mCache.mBuffers.erase(it);
+ ++evicted;
+ } else {
+ ++it;
+ }
+ }
+ ALOGV("cache count %lld : total %zu, active %d, evicted %zu",
+ (long long)mConnectionId, mCache.mBuffers.size(), mCache.mActive, evicted);
+ mLastEvictCacheUs = now;
+ }
+}
+
+ResultStatus BufferPoolClient::Impl::allocateBufferHandle(
+ const std::vector<uint8_t>& params, BufferId *bufferId,
+ native_handle_t** handle) {
+ if (mLocalConnection) {
+ const native_handle_t* allocHandle = nullptr;
+ ResultStatus status = mLocalConnection->allocate(
+ params, bufferId, &allocHandle);
+ if (status == ResultStatus::OK) {
+ *handle = native_handle_clone(allocHandle);
+ }
+ ALOGV("client allocate result %lld %d : %u clone %p",
+ (long long)mConnectionId, status == ResultStatus::OK,
+ *handle ? *bufferId : 0 , *handle);
+ return status;
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus BufferPoolClient::Impl::fetchBufferHandle(
+ TransactionId transactionId, BufferId bufferId,
+ native_handle_t **handle) {
+ sp<IConnection> connection;
+ if (mLocal) {
+ connection = mLocalConnection;
+ } else {
+ connection = mRemoteConnection;
+ }
+ ResultStatus status;
+ Return<void> transResult = connection->fetch(
+ transactionId, bufferId,
+ [&status, &handle]
+ (ResultStatus outStatus, Buffer outBuffer) {
+ status = outStatus;
+ if (status == ResultStatus::OK) {
+ *handle = native_handle_clone(
+ outBuffer.buffer.getNativeHandle());
+ }
+ });
+ return transResult.isOk() ? status : ResultStatus::CRITICAL_ERROR;
+}
+
+
+BufferPoolClient::BufferPoolClient(const sp<Accessor> &accessor) {
+ mImpl = std::make_shared<Impl>(accessor);
+}
+
+BufferPoolClient::BufferPoolClient(const sp<IAccessor> &accessor) {
+ mImpl = std::make_shared<Impl>(accessor);
+}
+
+BufferPoolClient::~BufferPoolClient() {
+ // TODO: how to handle orphaned buffers?
+}
+
+bool BufferPoolClient::isValid() {
+ return mImpl && mImpl->isValid();
+}
+
+bool BufferPoolClient::isLocal() {
+ return mImpl && mImpl->isLocal();
+}
+
+bool BufferPoolClient::isActive(int64_t *lastTransactionUs, bool clearCache) {
+ if (!isValid()) {
+ *lastTransactionUs = 0;
+ return false;
+ }
+ return mImpl->isActive(lastTransactionUs, clearCache);
+}
+
+ConnectionId BufferPoolClient::getConnectionId() {
+ if (isValid()) {
+ return mImpl->getConnectionId();
+ }
+ return -1;
+}
+
+ResultStatus BufferPoolClient::getAccessor(sp<IAccessor> *accessor) {
+ if (isValid()) {
+ *accessor = mImpl->getAccessor();
+ return ResultStatus::OK;
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus BufferPoolClient::allocate(
+ const std::vector<uint8_t> ¶ms,
+ native_handle_t **handle,
+ std::shared_ptr<BufferPoolData> *buffer) {
+ if (isValid()) {
+ return mImpl->allocate(params, handle, buffer);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus BufferPoolClient::receive(
+ TransactionId transactionId, BufferId bufferId, int64_t timestampUs,
+ native_handle_t **handle, std::shared_ptr<BufferPoolData> *buffer) {
+ if (isValid()) {
+ return mImpl->receive(transactionId, bufferId, timestampUs, handle, buffer);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus BufferPoolClient::postSend(
+ ConnectionId receiverId,
+ const std::shared_ptr<BufferPoolData> &buffer,
+ TransactionId *transactionId,
+ int64_t *timestampUs) {
+ if (isValid()) {
+ bool result = mImpl->postSend(
+ buffer->mId, receiverId, transactionId, timestampUs);
+ return result ? ResultStatus::OK : ResultStatus::CRITICAL_ERROR;
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
diff --git a/media/bufferpool/2.0/BufferPoolClient.h b/media/bufferpool/2.0/BufferPoolClient.h
new file mode 100644
index 0000000..00d6839
--- /dev/null
+++ b/media/bufferpool/2.0/BufferPoolClient.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_BUFFERPOOLCLIENT_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_BUFFERPOOLCLIENT_H
+
+#include <memory>
+#include <android/hardware/media/bufferpool/2.0/IAccessor.h>
+#include <android/hardware/media/bufferpool/2.0/IConnection.h>
+#include <bufferpool/BufferPoolTypes.h>
+#include <cutils/native_handle.h>
+#include "Accessor.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V2_0 {
+namespace implementation {
+
+using ::android::hardware::media::bufferpool::V2_0::IAccessor;
+using ::android::hardware::media::bufferpool::V2_0::IConnection;
+using ::android::hardware::media::bufferpool::V2_0::ResultStatus;
+using ::android::sp;
+
+/**
+ * A buffer pool client for a buffer pool. For a specific buffer pool, at most
+ * one buffer pool client exists per process. This class will not be exposed
+ * outside. A buffer pool client will be used via ClientManager.
+ */
+class BufferPoolClient {
+public:
+ /**
+ * Creates a buffer pool client from a local buffer pool
+ * (via ClientManager#create).
+ */
+ explicit BufferPoolClient(const sp<Accessor> &accessor);
+
+ /**
+ * Creates a buffer pool client from a remote buffer pool
+ * (via ClientManager#registerSender).
+ * Note: A buffer pool client created with remote buffer pool cannot
+ * allocate a buffer.
+ */
+ explicit BufferPoolClient(const sp<IAccessor> &accessor);
+
+ /** Destructs a buffer pool client. */
+ ~BufferPoolClient();
+
+private:
+ bool isValid();
+
+ bool isLocal();
+
+ bool isActive(int64_t *lastTransactionUs, bool clearCache);
+
+ ConnectionId getConnectionId();
+
+ ResultStatus getAccessor(sp<IAccessor> *accessor);
+
+ ResultStatus allocate(const std::vector<uint8_t> ¶ms,
+ native_handle_t **handle,
+ std::shared_ptr<BufferPoolData> *buffer);
+
+ ResultStatus receive(TransactionId transactionId,
+ BufferId bufferId,
+ int64_t timestampUs,
+ native_handle_t **handle,
+ std::shared_ptr<BufferPoolData> *buffer);
+
+ ResultStatus postSend(ConnectionId receiver,
+ const std::shared_ptr<BufferPoolData> &buffer,
+ TransactionId *transactionId,
+ int64_t *timestampUs);
+
+ class Impl;
+ std::shared_ptr<Impl> mImpl;
+
+ friend struct ClientManager;
+};
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_BUFFERPOOLCLIENT_H
diff --git a/media/bufferpool/2.0/BufferStatus.cpp b/media/bufferpool/2.0/BufferStatus.cpp
new file mode 100644
index 0000000..0d3f5a3f
--- /dev/null
+++ b/media/bufferpool/2.0/BufferStatus.cpp
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2018 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 "BufferPoolStatus"
+//#define LOG_NDEBUG 0
+
+#include <time.h>
+#include "BufferStatus.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V2_0 {
+namespace implementation {
+
+int64_t getTimestampNow() {
+ int64_t stamp;
+ struct timespec ts;
+ // TODO: CLOCK_MONOTONIC_COARSE?
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ stamp = ts.tv_nsec / 1000;
+ stamp += (ts.tv_sec * 1000000LL);
+ return stamp;
+}
+
+static constexpr int kNumElementsInQueue = 1024*16;
+static constexpr int kMinElementsToSyncInQueue = 128;
+
+ResultStatus BufferStatusObserver::open(
+ ConnectionId id, const StatusDescriptor** fmqDescPtr) {
+ if (mBufferStatusQueues.find(id) != mBufferStatusQueues.end()) {
+ // TODO: id collision log?
+ return ResultStatus::CRITICAL_ERROR;
+ }
+ std::unique_ptr<BufferStatusQueue> queue =
+ std::make_unique<BufferStatusQueue>(kNumElementsInQueue);
+ if (!queue || queue->isValid() == false) {
+ *fmqDescPtr = nullptr;
+ return ResultStatus::NO_MEMORY;
+ } else {
+ *fmqDescPtr = queue->getDesc();
+ }
+ auto result = mBufferStatusQueues.insert(
+ std::make_pair(id, std::move(queue)));
+ if (!result.second) {
+ *fmqDescPtr = nullptr;
+ return ResultStatus::NO_MEMORY;
+ }
+ return ResultStatus::OK;
+}
+
+ResultStatus BufferStatusObserver::close(ConnectionId id) {
+ if (mBufferStatusQueues.find(id) == mBufferStatusQueues.end()) {
+ return ResultStatus::CRITICAL_ERROR;
+ }
+ mBufferStatusQueues.erase(id);
+ return ResultStatus::OK;
+}
+
+void BufferStatusObserver::getBufferStatusChanges(std::vector<BufferStatusMessage> &messages) {
+ for (auto it = mBufferStatusQueues.begin(); it != mBufferStatusQueues.end(); ++it) {
+ BufferStatusMessage message;
+ size_t avail = it->second->availableToRead();
+ while (avail > 0) {
+ if (!it->second->read(&message, 1)) {
+ // Since avaliable # of reads are already confirmed,
+ // this should not happen.
+ // TODO: error handling (spurious client?)
+ ALOGW("FMQ message cannot be read from %lld", (long long)it->first);
+ return;
+ }
+ message.connectionId = it->first;
+ messages.push_back(message);
+ --avail;
+ }
+ }
+}
+
+BufferStatusChannel::BufferStatusChannel(
+ const StatusDescriptor &fmqDesc) {
+ std::unique_ptr<BufferStatusQueue> queue =
+ std::make_unique<BufferStatusQueue>(fmqDesc);
+ if (!queue || queue->isValid() == false) {
+ mValid = false;
+ return;
+ }
+ mValid = true;
+ mBufferStatusQueue = std::move(queue);
+}
+
+bool BufferStatusChannel::isValid() {
+ return mValid;
+}
+
+bool BufferStatusChannel::needsSync() {
+ if (mValid) {
+ size_t avail = mBufferStatusQueue->availableToWrite();
+ return avail + kMinElementsToSyncInQueue < kNumElementsInQueue;
+ }
+ return false;
+}
+
+void BufferStatusChannel::postBufferRelease(
+ ConnectionId connectionId,
+ std::list<BufferId> &pending, std::list<BufferId> &posted) {
+ if (mValid && pending.size() > 0) {
+ size_t avail = mBufferStatusQueue->availableToWrite();
+ avail = std::min(avail, pending.size());
+ BufferStatusMessage message;
+ for (size_t i = 0 ; i < avail; ++i) {
+ BufferId id = pending.front();
+ message.newStatus = BufferStatus::NOT_USED;
+ message.bufferId = id;
+ message.connectionId = connectionId;
+ if (!mBufferStatusQueue->write(&message, 1)) {
+ // Since avaliable # of writes are already confirmed,
+ // this should not happen.
+ // TODO: error handing?
+ ALOGW("FMQ message cannot be sent from %lld", (long long)connectionId);
+ return;
+ }
+ pending.pop_front();
+ posted.push_back(id);
+ }
+ }
+}
+
+bool BufferStatusChannel::postBufferStatusMessage(
+ TransactionId transactionId, BufferId bufferId,
+ BufferStatus status, ConnectionId connectionId, ConnectionId targetId,
+ std::list<BufferId> &pending, std::list<BufferId> &posted) {
+ if (mValid) {
+ size_t avail = mBufferStatusQueue->availableToWrite();
+ size_t numPending = pending.size();
+ if (avail >= numPending + 1) {
+ BufferStatusMessage release, message;
+ for (size_t i = 0; i < numPending; ++i) {
+ BufferId id = pending.front();
+ release.newStatus = BufferStatus::NOT_USED;
+ release.bufferId = id;
+ release.connectionId = connectionId;
+ if (!mBufferStatusQueue->write(&release, 1)) {
+ // Since avaliable # of writes are already confirmed,
+ // this should not happen.
+ // TODO: error handling?
+ ALOGW("FMQ message cannot be sent from %lld", (long long)connectionId);
+ return false;
+ }
+ pending.pop_front();
+ posted.push_back(id);
+ }
+ message.transactionId = transactionId;
+ message.bufferId = bufferId;
+ message.newStatus = status;
+ message.connectionId = connectionId;
+ message.targetConnectionId = targetId;
+ // TODO : timesatamp
+ message.timestampUs = 0;
+ if (!mBufferStatusQueue->write(&message, 1)) {
+ // Since avaliable # of writes are already confirmed,
+ // this should not happen.
+ ALOGW("FMQ message cannot be sent from %lld", (long long)connectionId);
+ return false;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
+
diff --git a/media/bufferpool/2.0/BufferStatus.h b/media/bufferpool/2.0/BufferStatus.h
new file mode 100644
index 0000000..777a320
--- /dev/null
+++ b/media/bufferpool/2.0/BufferStatus.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_BUFFERSTATUS_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_BUFFERSTATUS_H
+
+#include <android/hardware/media/bufferpool/2.0/types.h>
+#include <bufferpool/BufferPoolTypes.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <memory>
+#include <mutex>
+#include <vector>
+#include <list>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V2_0 {
+namespace implementation {
+
+/** Returns monotonic timestamp in Us since fixed point in time. */
+int64_t getTimestampNow();
+
+/**
+ * A collection of FMQ for a buffer pool. buffer ownership/status change
+ * messages are sent via the FMQs from the clients.
+ */
+class BufferStatusObserver {
+private:
+ std::map<ConnectionId, std::unique_ptr<BufferStatusQueue>>
+ mBufferStatusQueues;
+
+public:
+ /** Creates an FMQ for the specified connection(client).
+ *
+ * @param connectionId connection Id of the specified client.
+ * @param fmqDescPtr double ptr of created FMQ's descriptor.
+ *
+ * @return OK if FMQ is created successfully.
+ * NO_MEMORY when there is no memory.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus open(ConnectionId id, const StatusDescriptor** fmqDescPtr);
+
+ /** Closes an FMQ for the specified connection(client).
+ *
+ * @param connectionId connection Id of the specified client.
+ *
+ * @return OK if the specified connection is closed successfully.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus close(ConnectionId id);
+
+ /** Retrieves all pending FMQ buffer status messages from clients.
+ *
+ * @param messages retrieved pending messages.
+ */
+ void getBufferStatusChanges(std::vector<BufferStatusMessage> &messages);
+};
+
+/**
+ * An FMQ for a buffer pool client. Buffer ownership/status change messages
+ * are sent via the fmq to the buffer pool.
+ */
+class BufferStatusChannel {
+private:
+ bool mValid;
+ std::unique_ptr<BufferStatusQueue> mBufferStatusQueue;
+
+public:
+ /**
+ * Connects to an FMQ from a descriptor of the created FMQ.
+ *
+ * @param fmqDesc Descriptor of the created FMQ.
+ */
+ BufferStatusChannel(const StatusDescriptor &fmqDesc);
+
+ /** Returns whether the FMQ is connected successfully. */
+ bool isValid();
+
+ /** Returns whether the FMQ needs to be synced from the buffer pool */
+ bool needsSync();
+
+ /**
+ * Posts a buffer release message to the buffer pool.
+ *
+ * @param connectionId connection Id of the client.
+ * @param pending currently pending buffer release messages.
+ * @param posted posted buffer release messages.
+ */
+ void postBufferRelease(
+ ConnectionId connectionId,
+ std::list<BufferId> &pending, std::list<BufferId> &posted);
+
+ /**
+ * Posts a buffer status message regarding the specified buffer
+ * transfer transaction.
+ *
+ * @param transactionId Id of the specified transaction.
+ * @param bufferId buffer Id of the specified transaction.
+ * @param status new status of the buffer.
+ * @param connectionId connection Id of the client.
+ * @param targetId connection Id of the receiver(only when the sender
+ * posts a status message).
+ * @param pending currently pending buffer release messages.
+ * @param posted posted buffer release messages.
+ *
+ * @return {@code true} when the specified message is posted,
+ * {@code false} otherwise.
+ */
+ bool postBufferStatusMessage(
+ TransactionId transactionId,
+ BufferId bufferId,
+ BufferStatus status,
+ ConnectionId connectionId,
+ ConnectionId targetId,
+ std::list<BufferId> &pending, std::list<BufferId> &posted);
+};
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_BUFFERSTATUS_H
diff --git a/media/bufferpool/2.0/ClientManager.cpp b/media/bufferpool/2.0/ClientManager.cpp
new file mode 100644
index 0000000..eeaf093
--- /dev/null
+++ b/media/bufferpool/2.0/ClientManager.cpp
@@ -0,0 +1,504 @@
+/*
+ * Copyright (C) 2018 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 "BufferPoolManager"
+//#define LOG_NDEBUG 0
+
+#include <bufferpool/ClientManager.h>
+#include <hidl/HidlTransportSupport.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <utils/Log.h>
+#include "BufferPoolClient.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V2_0 {
+namespace implementation {
+
+static constexpr int64_t kRegisterTimeoutUs = 500000; // 0.5 sec
+static constexpr int64_t kCleanUpDurationUs = 1000000; // TODO: 1 sec tune
+static constexpr int64_t kClientTimeoutUs = 5000000; // TODO: 5 secs tune
+
+/**
+ * The holder of the cookie of remote IClientManager.
+ * The cookie is process locally unique for each IClientManager.
+ * (The cookie is used to notify death of clients to bufferpool process.)
+ */
+class ClientManagerCookieHolder {
+public:
+ /**
+ * Creates a cookie holder for remote IClientManager(s).
+ */
+ ClientManagerCookieHolder();
+
+ /**
+ * Gets a cookie for a remote IClientManager.
+ *
+ * @param manager the specified remote IClientManager.
+ * @param added true when the specified remote IClientManager is added
+ * newly, false otherwise.
+ *
+ * @return the process locally unique cookie for the specified IClientManager.
+ */
+ uint64_t getCookie(const sp<IClientManager> &manager, bool *added);
+
+private:
+ uint64_t mSeqId;
+ std::mutex mLock;
+ std::list<std::pair<const wp<IClientManager>, uint64_t>> mManagers;
+};
+
+ClientManagerCookieHolder::ClientManagerCookieHolder() : mSeqId(0){}
+
+uint64_t ClientManagerCookieHolder::getCookie(
+ const sp<IClientManager> &manager,
+ bool *added) {
+ std::lock_guard<std::mutex> lock(mLock);
+ for (auto it = mManagers.begin(); it != mManagers.end();) {
+ const sp<IClientManager> key = it->first.promote();
+ if (key) {
+ if (interfacesEqual(key, manager)) {
+ *added = false;
+ return it->second;
+ }
+ ++it;
+ } else {
+ it = mManagers.erase(it);
+ }
+ }
+ uint64_t id = mSeqId++;
+ *added = true;
+ mManagers.push_back(std::make_pair(manager, id));
+ return id;
+}
+
+class ClientManager::Impl {
+public:
+ Impl();
+
+ // BnRegisterSender
+ ResultStatus registerSender(const sp<IAccessor> &accessor,
+ ConnectionId *pConnectionId);
+
+ // BpRegisterSender
+ ResultStatus registerSender(const sp<IClientManager> &receiver,
+ ConnectionId senderId,
+ ConnectionId *receiverId);
+
+ ResultStatus create(const std::shared_ptr<BufferPoolAllocator> &allocator,
+ ConnectionId *pConnectionId);
+
+ ResultStatus close(ConnectionId connectionId);
+
+ ResultStatus allocate(ConnectionId connectionId,
+ const std::vector<uint8_t> ¶ms,
+ native_handle_t **handle,
+ std::shared_ptr<BufferPoolData> *buffer);
+
+ ResultStatus receive(ConnectionId connectionId,
+ TransactionId transactionId,
+ BufferId bufferId,
+ int64_t timestampUs,
+ native_handle_t **handle,
+ std::shared_ptr<BufferPoolData> *buffer);
+
+ ResultStatus postSend(ConnectionId receiverId,
+ const std::shared_ptr<BufferPoolData> &buffer,
+ TransactionId *transactionId,
+ int64_t *timestampUs);
+
+ ResultStatus getAccessor(ConnectionId connectionId,
+ sp<IAccessor> *accessor);
+
+ void cleanUp(bool clearCache = false);
+
+private:
+ // In order to prevent deadlock between multiple locks,
+ // always lock ClientCache.lock before locking ActiveClients.lock.
+ struct ClientCache {
+ // This lock is held for brief duration.
+ // Blocking operation is not performed while holding the lock.
+ std::mutex mMutex;
+ std::list<std::pair<const wp<IAccessor>, const std::weak_ptr<BufferPoolClient>>>
+ mClients;
+ std::condition_variable mConnectCv;
+ bool mConnecting;
+ int64_t mLastCleanUpUs;
+
+ ClientCache() : mConnecting(false), mLastCleanUpUs(getTimestampNow()) {}
+ } mCache;
+
+ // Active clients which can be retrieved via ConnectionId
+ struct ActiveClients {
+ // This lock is held for brief duration.
+ // Blocking operation is not performed holding the lock.
+ std::mutex mMutex;
+ std::map<ConnectionId, const std::shared_ptr<BufferPoolClient>>
+ mClients;
+ } mActive;
+
+ ClientManagerCookieHolder mRemoteClientCookies;
+};
+
+ClientManager::Impl::Impl() {}
+
+ResultStatus ClientManager::Impl::registerSender(
+ const sp<IAccessor> &accessor, ConnectionId *pConnectionId) {
+ cleanUp();
+ int64_t timeoutUs = getTimestampNow() + kRegisterTimeoutUs;
+ do {
+ std::unique_lock<std::mutex> lock(mCache.mMutex);
+ for (auto it = mCache.mClients.begin(); it != mCache.mClients.end(); ++it) {
+ sp<IAccessor> sAccessor = it->first.promote();
+ if (sAccessor && interfacesEqual(sAccessor, accessor)) {
+ const std::shared_ptr<BufferPoolClient> client = it->second.lock();
+ if (client) {
+ std::lock_guard<std::mutex> lock(mActive.mMutex);
+ *pConnectionId = client->getConnectionId();
+ if (mActive.mClients.find(*pConnectionId) != mActive.mClients.end()) {
+ ALOGV("register existing connection %lld", (long long)*pConnectionId);
+ return ResultStatus::ALREADY_EXISTS;
+ }
+ }
+ mCache.mClients.erase(it);
+ break;
+ }
+ }
+ if (!mCache.mConnecting) {
+ mCache.mConnecting = true;
+ lock.unlock();
+ ResultStatus result = ResultStatus::OK;
+ const std::shared_ptr<BufferPoolClient> client =
+ std::make_shared<BufferPoolClient>(accessor);
+ lock.lock();
+ if (!client) {
+ result = ResultStatus::NO_MEMORY;
+ } else if (!client->isValid()) {
+ result = ResultStatus::CRITICAL_ERROR;
+ }
+ if (result == ResultStatus::OK) {
+ // TODO: handle insert fail. (malloc fail)
+ const std::weak_ptr<BufferPoolClient> wclient = client;
+ mCache.mClients.push_back(std::make_pair(accessor, wclient));
+ ConnectionId conId = client->getConnectionId();
+ {
+ std::lock_guard<std::mutex> lock(mActive.mMutex);
+ mActive.mClients.insert(std::make_pair(conId, client));
+ }
+ *pConnectionId = conId;
+ ALOGV("register new connection %lld", (long long)*pConnectionId);
+ }
+ mCache.mConnecting = false;
+ lock.unlock();
+ mCache.mConnectCv.notify_all();
+ return result;
+ }
+ mCache.mConnectCv.wait_for(
+ lock, std::chrono::microseconds(kRegisterTimeoutUs));
+ } while (getTimestampNow() < timeoutUs);
+ // TODO: return timeout error
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus ClientManager::Impl::registerSender(
+ const sp<IClientManager> &receiver,
+ ConnectionId senderId,
+ ConnectionId *receiverId) {
+ sp<IAccessor> accessor;
+ bool local = false;
+ {
+ std::lock_guard<std::mutex> lock(mActive.mMutex);
+ auto it = mActive.mClients.find(senderId);
+ if (it == mActive.mClients.end()) {
+ return ResultStatus::NOT_FOUND;
+ }
+ it->second->getAccessor(&accessor);
+ local = it->second->isLocal();
+ }
+ ResultStatus rs = ResultStatus::CRITICAL_ERROR;
+ if (accessor) {
+ Return<void> transResult = receiver->registerSender(
+ accessor,
+ [&rs, receiverId](
+ ResultStatus status,
+ int64_t connectionId) {
+ rs = status;
+ *receiverId = connectionId;
+ });
+ if (!transResult.isOk()) {
+ return ResultStatus::CRITICAL_ERROR;
+ } else if (local && rs == ResultStatus::OK) {
+ sp<ConnectionDeathRecipient> recipient = Accessor::getConnectionDeathRecipient();
+ if (recipient) {
+ ALOGV("client death recipient registered %lld", (long long)*receiverId);
+ bool added;
+ uint64_t cookie = mRemoteClientCookies.getCookie(receiver, &added);
+ recipient->addCookieToConnection(cookie, *receiverId);
+ if (added) {
+ Return<bool> transResult = receiver->linkToDeath(recipient, cookie);
+ }
+ }
+ }
+ }
+ return rs;
+}
+
+ResultStatus ClientManager::Impl::create(
+ const std::shared_ptr<BufferPoolAllocator> &allocator,
+ ConnectionId *pConnectionId) {
+ const sp<Accessor> accessor = new Accessor(allocator);
+ if (!accessor || !accessor->isValid()) {
+ return ResultStatus::CRITICAL_ERROR;
+ }
+ std::shared_ptr<BufferPoolClient> client =
+ std::make_shared<BufferPoolClient>(accessor);
+ if (!client || !client->isValid()) {
+ return ResultStatus::CRITICAL_ERROR;
+ }
+ // Since a new bufferpool is created, evict memories which are used by
+ // existing bufferpools and clients.
+ cleanUp(true);
+ {
+ // TODO: handle insert fail. (malloc fail)
+ std::lock_guard<std::mutex> lock(mCache.mMutex);
+ const std::weak_ptr<BufferPoolClient> wclient = client;
+ mCache.mClients.push_back(std::make_pair(accessor, wclient));
+ ConnectionId conId = client->getConnectionId();
+ {
+ std::lock_guard<std::mutex> lock(mActive.mMutex);
+ mActive.mClients.insert(std::make_pair(conId, client));
+ }
+ *pConnectionId = conId;
+ ALOGV("create new connection %lld", (long long)*pConnectionId);
+ }
+ return ResultStatus::OK;
+}
+
+ResultStatus ClientManager::Impl::close(ConnectionId connectionId) {
+ std::lock_guard<std::mutex> lock1(mCache.mMutex);
+ std::lock_guard<std::mutex> lock2(mActive.mMutex);
+ auto it = mActive.mClients.find(connectionId);
+ if (it != mActive.mClients.end()) {
+ sp<IAccessor> accessor;
+ it->second->getAccessor(&accessor);
+ mActive.mClients.erase(connectionId);
+ for (auto cit = mCache.mClients.begin(); cit != mCache.mClients.end();) {
+ // clean up dead client caches
+ sp<IAccessor> cAccessor = cit->first.promote();
+ if (!cAccessor || (accessor && interfacesEqual(cAccessor, accessor))) {
+ cit = mCache.mClients.erase(cit);
+ } else {
+ cit++;
+ }
+ }
+ return ResultStatus::OK;
+ }
+ return ResultStatus::NOT_FOUND;
+}
+
+ResultStatus ClientManager::Impl::allocate(
+ ConnectionId connectionId, const std::vector<uint8_t> ¶ms,
+ native_handle_t **handle, std::shared_ptr<BufferPoolData> *buffer) {
+ std::shared_ptr<BufferPoolClient> client;
+ {
+ std::lock_guard<std::mutex> lock(mActive.mMutex);
+ auto it = mActive.mClients.find(connectionId);
+ if (it == mActive.mClients.end()) {
+ return ResultStatus::NOT_FOUND;
+ }
+ client = it->second;
+ }
+ return client->allocate(params, handle, buffer);
+}
+
+ResultStatus ClientManager::Impl::receive(
+ ConnectionId connectionId, TransactionId transactionId,
+ BufferId bufferId, int64_t timestampUs,
+ native_handle_t **handle, std::shared_ptr<BufferPoolData> *buffer) {
+ std::shared_ptr<BufferPoolClient> client;
+ {
+ std::lock_guard<std::mutex> lock(mActive.mMutex);
+ auto it = mActive.mClients.find(connectionId);
+ if (it == mActive.mClients.end()) {
+ return ResultStatus::NOT_FOUND;
+ }
+ client = it->second;
+ }
+ return client->receive(transactionId, bufferId, timestampUs, handle, buffer);
+}
+
+ResultStatus ClientManager::Impl::postSend(
+ ConnectionId receiverId, const std::shared_ptr<BufferPoolData> &buffer,
+ TransactionId *transactionId, int64_t *timestampUs) {
+ ConnectionId connectionId = buffer->mConnectionId;
+ std::shared_ptr<BufferPoolClient> client;
+ {
+ std::lock_guard<std::mutex> lock(mActive.mMutex);
+ auto it = mActive.mClients.find(connectionId);
+ if (it == mActive.mClients.end()) {
+ return ResultStatus::NOT_FOUND;
+ }
+ client = it->second;
+ }
+ return client->postSend(receiverId, buffer, transactionId, timestampUs);
+}
+
+ResultStatus ClientManager::Impl::getAccessor(
+ ConnectionId connectionId, sp<IAccessor> *accessor) {
+ std::shared_ptr<BufferPoolClient> client;
+ {
+ std::lock_guard<std::mutex> lock(mActive.mMutex);
+ auto it = mActive.mClients.find(connectionId);
+ if (it == mActive.mClients.end()) {
+ return ResultStatus::NOT_FOUND;
+ }
+ client = it->second;
+ }
+ return client->getAccessor(accessor);
+}
+
+void ClientManager::Impl::cleanUp(bool clearCache) {
+ int64_t now = getTimestampNow();
+ int64_t lastTransactionUs;
+ std::lock_guard<std::mutex> lock1(mCache.mMutex);
+ if (clearCache || mCache.mLastCleanUpUs + kCleanUpDurationUs < now) {
+ std::lock_guard<std::mutex> lock2(mActive.mMutex);
+ int cleaned = 0;
+ for (auto it = mActive.mClients.begin(); it != mActive.mClients.end();) {
+ if (!it->second->isActive(&lastTransactionUs, clearCache)) {
+ if (lastTransactionUs + kClientTimeoutUs < now) {
+ sp<IAccessor> accessor;
+ it->second->getAccessor(&accessor);
+ it = mActive.mClients.erase(it);
+ ++cleaned;
+ continue;
+ }
+ }
+ ++it;
+ }
+ for (auto cit = mCache.mClients.begin(); cit != mCache.mClients.end();) {
+ // clean up dead client caches
+ sp<IAccessor> cAccessor = cit->first.promote();
+ if (!cAccessor) {
+ cit = mCache.mClients.erase(cit);
+ } else {
+ ++cit;
+ }
+ }
+ ALOGV("# of cleaned connections: %d", cleaned);
+ mCache.mLastCleanUpUs = now;
+ }
+}
+
+// Methods from ::android::hardware::media::bufferpool::V2_0::IClientManager follow.
+Return<void> ClientManager::registerSender(const sp<::android::hardware::media::bufferpool::V2_0::IAccessor>& bufferPool, registerSender_cb _hidl_cb) {
+ if (mImpl) {
+ ConnectionId connectionId = -1;
+ ResultStatus status = mImpl->registerSender(bufferPool, &connectionId);
+ _hidl_cb(status, connectionId);
+ } else {
+ _hidl_cb(ResultStatus::CRITICAL_ERROR, -1);
+ }
+ return Void();
+}
+
+// Methods for local use.
+sp<ClientManager> ClientManager::sInstance;
+std::mutex ClientManager::sInstanceLock;
+
+sp<ClientManager> ClientManager::getInstance() {
+ std::lock_guard<std::mutex> lock(sInstanceLock);
+ if (!sInstance) {
+ sInstance = new ClientManager();
+ }
+ return sInstance;
+}
+
+ClientManager::ClientManager() : mImpl(new Impl()) {}
+
+ClientManager::~ClientManager() {
+}
+
+ResultStatus ClientManager::create(
+ const std::shared_ptr<BufferPoolAllocator> &allocator,
+ ConnectionId *pConnectionId) {
+ if (mImpl) {
+ return mImpl->create(allocator, pConnectionId);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus ClientManager::registerSender(
+ const sp<IClientManager> &receiver,
+ ConnectionId senderId,
+ ConnectionId *receiverId) {
+ if (mImpl) {
+ return mImpl->registerSender(receiver, senderId, receiverId);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus ClientManager::close(ConnectionId connectionId) {
+ if (mImpl) {
+ return mImpl->close(connectionId);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus ClientManager::allocate(
+ ConnectionId connectionId, const std::vector<uint8_t> ¶ms,
+ native_handle_t **handle, std::shared_ptr<BufferPoolData> *buffer) {
+ if (mImpl) {
+ return mImpl->allocate(connectionId, params, handle, buffer);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus ClientManager::receive(
+ ConnectionId connectionId, TransactionId transactionId,
+ BufferId bufferId, int64_t timestampUs,
+ native_handle_t **handle, std::shared_ptr<BufferPoolData> *buffer) {
+ if (mImpl) {
+ return mImpl->receive(connectionId, transactionId, bufferId,
+ timestampUs, handle, buffer);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+ResultStatus ClientManager::postSend(
+ ConnectionId receiverId, const std::shared_ptr<BufferPoolData> &buffer,
+ TransactionId *transactionId, int64_t* timestampUs) {
+ if (mImpl && buffer) {
+ return mImpl->postSend(receiverId, buffer, transactionId, timestampUs);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+void ClientManager::cleanUp() {
+ if (mImpl) {
+ mImpl->cleanUp(true);
+ }
+}
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
diff --git a/media/bufferpool/2.0/Connection.cpp b/media/bufferpool/2.0/Connection.cpp
new file mode 100644
index 0000000..cd837a1
--- /dev/null
+++ b/media/bufferpool/2.0/Connection.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Connection.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V2_0 {
+namespace implementation {
+
+// Methods from ::android::hardware::media::bufferpool::V2_0::IConnection follow.
+Return<void> Connection::fetch(uint64_t transactionId, uint32_t bufferId, fetch_cb _hidl_cb) {
+ ResultStatus status = ResultStatus::CRITICAL_ERROR;
+ if (mInitialized && mAccessor) {
+ if (bufferId != SYNC_BUFFERID) {
+ const native_handle_t *handle = nullptr;
+ status = mAccessor->fetch(
+ mConnectionId, transactionId, bufferId, &handle);
+ if (status == ResultStatus::OK) {
+ _hidl_cb(status, Buffer{bufferId, handle});
+ return Void();
+ }
+ } else {
+ mAccessor->cleanUp(false);
+ }
+ }
+ _hidl_cb(status, Buffer{0, nullptr});
+ return Void();
+}
+
+Connection::Connection() : mInitialized(false), mConnectionId(-1LL) {}
+
+Connection::~Connection() {
+ if (mInitialized && mAccessor) {
+ mAccessor->close(mConnectionId);
+ }
+}
+
+void Connection::initialize(
+ const sp<Accessor>& accessor, ConnectionId connectionId) {
+ if (!mInitialized) {
+ mAccessor = accessor;
+ mConnectionId = connectionId;
+ mInitialized = true;
+ }
+}
+
+ResultStatus Connection::allocate(
+ const std::vector<uint8_t> ¶ms, BufferId *bufferId,
+ const native_handle_t **handle) {
+ if (mInitialized && mAccessor) {
+ return mAccessor->allocate(mConnectionId, params, bufferId, handle);
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+void Connection::cleanUp(bool clearCache) {
+ if (mInitialized && mAccessor) {
+ mAccessor->cleanUp(clearCache);
+ }
+}
+
+// Methods from ::android::hidl::base::V1_0::IBase follow.
+
+//IConnection* HIDL_FETCH_IConnection(const char* /* name */) {
+// return new Connection();
+//}
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
diff --git a/media/bufferpool/2.0/Connection.h b/media/bufferpool/2.0/Connection.h
new file mode 100644
index 0000000..e2b47f1
--- /dev/null
+++ b/media/bufferpool/2.0/Connection.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_CONNECTION_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_CONNECTION_H
+
+#include <android/hardware/media/bufferpool/2.0/IConnection.h>
+#include <bufferpool/BufferPoolTypes.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include "Accessor.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V2_0 {
+namespace implementation {
+
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::media::bufferpool::V2_0::implementation::Accessor;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct Connection : public IConnection {
+ // Methods from ::android::hardware::media::bufferpool::V2_0::IConnection follow.
+ Return<void> fetch(uint64_t transactionId, uint32_t bufferId, fetch_cb _hidl_cb) override;
+
+ /**
+ * Allocates a buffer using the specified parameters. Recycles a buffer if
+ * it is possible. The returned buffer can be transferred to other remote
+ * clients(Connection).
+ *
+ * @param params allocation parameters.
+ * @param bufferId Id of the allocated buffer.
+ * @param handle native handle of the allocated buffer.
+ *
+ * @return OK if a buffer is successfully allocated.
+ * NO_MEMORY when there is no memory.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus allocate(const std::vector<uint8_t> ¶ms,
+ BufferId *bufferId, const native_handle_t **handle);
+
+ /**
+ * Processes pending buffer status messages and performs periodic cache cleaning
+ * from bufferpool.
+ *
+ * @param clearCache if clearCache is true, bufferpool frees all buffers
+ * waiting to be recycled.
+ */
+ void cleanUp(bool clearCache);
+
+ /** Destructs a connection. */
+ ~Connection();
+
+ /** Creates a connection. */
+ Connection();
+
+ /**
+ * Initializes with the specified buffer pool and the connection id.
+ * The connection id should be unique in the whole system.
+ *
+ * @param accessor the specified buffer pool.
+ * @param connectionId Id.
+ */
+ void initialize(const sp<Accessor> &accessor, ConnectionId connectionId);
+
+ enum : uint32_t {
+ SYNC_BUFFERID = UINT32_MAX,
+ };
+
+private:
+ bool mInitialized;
+ sp<Accessor> mAccessor;
+ ConnectionId mConnectionId;
+};
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_CONNECTION_H
diff --git a/media/bufferpool/2.0/include/bufferpool/BufferPoolTypes.h b/media/bufferpool/2.0/include/bufferpool/BufferPoolTypes.h
new file mode 100644
index 0000000..eb845e1
--- /dev/null
+++ b/media/bufferpool/2.0/include/bufferpool/BufferPoolTypes.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_BUFFERPOOLTYPES_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_BUFFERPOOLTYPES_H
+
+#include <android/hardware/media/bufferpool/2.0/types.h>
+#include <cutils/native_handle.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+
+struct BufferPoolData {
+ // For local use, to specify a bufferpool (client connection) for buffers.
+ // Return value from connect#IAccessor(android.hardware.media.bufferpool@2.0).
+ int64_t mConnectionId;
+ // BufferId
+ uint32_t mId;
+
+ BufferPoolData() : mConnectionId(0), mId(0) {}
+
+ BufferPoolData(
+ int64_t connectionId, uint32_t id)
+ : mConnectionId(connectionId), mId(id) {}
+
+ ~BufferPoolData() {}
+};
+
+namespace V2_0 {
+namespace implementation {
+
+using ::android::hardware::kSynchronizedReadWrite;
+
+typedef uint32_t BufferId;
+typedef uint64_t TransactionId;
+typedef int64_t ConnectionId;
+
+enum : ConnectionId {
+ INVALID_CONNECTIONID = 0,
+};
+
+typedef android::hardware::MessageQueue<BufferStatusMessage, kSynchronizedReadWrite> BufferStatusQueue;
+typedef BufferStatusQueue::Descriptor StatusDescriptor;
+
+typedef android::hardware::MessageQueue<BufferInvalidationMessage, kSynchronizedReadWrite>
+ BufferInvalidationQueue;
+typedef BufferInvalidationQueue::Descriptor InvalidationDescriptor;
+
+/**
+ * Allocation wrapper class for buffer pool.
+ */
+struct BufferPoolAllocation {
+ const native_handle_t *mHandle;
+
+ const native_handle_t *handle() {
+ return mHandle;
+ }
+
+ BufferPoolAllocation(const native_handle_t *handle) : mHandle(handle) {}
+
+ ~BufferPoolAllocation() {};
+};
+
+/**
+ * Allocator wrapper class for buffer pool.
+ */
+class BufferPoolAllocator {
+public:
+
+ /**
+ * Allocate an allocation(buffer) for buffer pool.
+ *
+ * @param params allocation parameters
+ * @param alloc created allocation
+ * @param allocSize size of created allocation
+ *
+ * @return OK when an allocation is created successfully.
+ */
+ virtual ResultStatus allocate(
+ const std::vector<uint8_t> ¶ms,
+ std::shared_ptr<BufferPoolAllocation> *alloc,
+ size_t *allocSize) = 0;
+
+ /**
+ * Returns whether allocation parameters of an old allocation are
+ * compatible with new allocation parameters.
+ */
+ virtual bool compatible(const std::vector<uint8_t> &newParams,
+ const std::vector<uint8_t> &oldParams) = 0;
+
+protected:
+ BufferPoolAllocator() = default;
+
+ virtual ~BufferPoolAllocator() = default;
+};
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_BUFFERPOOLTYPES_H
diff --git a/media/bufferpool/2.0/include/bufferpool/ClientManager.h b/media/bufferpool/2.0/include/bufferpool/ClientManager.h
new file mode 100644
index 0000000..cfc3bc3
--- /dev/null
+++ b/media/bufferpool/2.0/include/bufferpool/ClientManager.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_CLIENTMANAGER_H
+#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_CLIENTMANAGER_H
+
+#include <android/hardware/media/bufferpool/2.0/IClientManager.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <memory>
+#include "BufferPoolTypes.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace bufferpool {
+namespace V2_0 {
+namespace implementation {
+
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::media::bufferpool::V2_0::IAccessor;
+using ::android::hardware::media::bufferpool::V2_0::ResultStatus;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct ClientManager : public IClientManager {
+ // Methods from ::android::hardware::media::bufferpool::V2_0::IClientManager follow.
+ Return<void> registerSender(const sp<::android::hardware::media::bufferpool::V2_0::IAccessor>& bufferPool, registerSender_cb _hidl_cb) override;
+
+ /** Gets an instance. */
+ static sp<ClientManager> getInstance();
+
+ /**
+ * Creates a local connection with a newly created buffer pool.
+ *
+ * @param allocator for new buffer allocation.
+ * @param pConnectionId Id of the created connection. This is
+ * system-wide unique.
+ *
+ * @return OK when a buffer pool and a local connection is successfully
+ * created.
+ * NO_MEMORY when there is no memory.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus create(const std::shared_ptr<BufferPoolAllocator> &allocator,
+ ConnectionId *pConnectionId);
+
+ /**
+ * Register a created connection as sender for remote process.
+ *
+ * @param receiver The remote receiving process.
+ * @param senderId A local connection which will send buffers to.
+ * @param receiverId Id of the created receiving connection on the receiver
+ * process.
+ *
+ * @return OK when the receiving connection is successfully created on the
+ * receiver process.
+ * NOT_FOUND when the sender connection was not found.
+ * ALREADY_EXISTS the receiving connection is already made.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus registerSender(const sp<IClientManager> &receiver,
+ ConnectionId senderId,
+ ConnectionId *receiverId);
+
+ /**
+ * Closes the specified connection.
+ *
+ * @param connectionId The id of the connection.
+ *
+ * @return OK when the connection is closed.
+ * NOT_FOUND when the specified connection was not found.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus close(ConnectionId connectionId);
+
+ /**
+ * Allocates a buffer from the specified connection.
+ *
+ * @param connectionId The id of the connection.
+ * @param params The allocation parameters.
+ * @param handle The native handle to the allocated buffer. handle
+ * should be cloned before use.
+ * @param buffer The allocated buffer.
+ *
+ * @return OK when a buffer was allocated successfully.
+ * NOT_FOUND when the specified connection was not found.
+ * NO_MEMORY when there is no memory.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus allocate(ConnectionId connectionId,
+ const std::vector<uint8_t> ¶ms,
+ native_handle_t **handle,
+ std::shared_ptr<BufferPoolData> *buffer);
+
+ /**
+ * Receives a buffer for the transaction.
+ *
+ * @param connectionId The id of the receiving connection.
+ * @param transactionId The id for the transaction.
+ * @param bufferId The id for the buffer.
+ * @param timestampUs The timestamp of the buffer is being sent.
+ * @param handle The native handle to the allocated buffer. handle
+ * should be cloned before use.
+ * @param buffer The received buffer.
+ *
+ * @return OK when a buffer was received successfully.
+ * NOT_FOUND when the specified connection was not found.
+ * NO_MEMORY when there is no memory.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus receive(ConnectionId connectionId,
+ TransactionId transactionId,
+ BufferId bufferId,
+ int64_t timestampUs,
+ native_handle_t **handle,
+ std::shared_ptr<BufferPoolData> *buffer);
+
+ /**
+ * Posts a buffer transfer transaction to the buffer pool. Sends a buffer
+ * to other remote clients(connection) after this call has been succeeded.
+ *
+ * @param receiverId The id of the receiving connection.
+ * @param buffer to transfer
+ * @param transactionId Id of the transfer transaction.
+ * @param timestampUs The timestamp of the buffer transaction is being
+ * posted.
+ *
+ * @return OK when a buffer transaction was posted successfully.
+ * NOT_FOUND when the sending connection was not found.
+ * CRITICAL_ERROR otherwise.
+ */
+ ResultStatus postSend(ConnectionId receiverId,
+ const std::shared_ptr<BufferPoolData> &buffer,
+ TransactionId *transactionId,
+ int64_t *timestampUs);
+
+ /**
+ * Time out inactive lingering connections and close.
+ */
+ void cleanUp();
+
+ /** Destructs the manager of buffer pool clients. */
+ ~ClientManager();
+private:
+ static sp<ClientManager> sInstance;
+ static std::mutex sInstanceLock;
+
+ class Impl;
+ const std::unique_ptr<Impl> mImpl;
+
+ ClientManager();
+};
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace bufferpool
+} // namespace media
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V2_0_CLIENTMANAGER_H
diff --git a/media/bufferpool/2.0/tests/Android.bp b/media/bufferpool/2.0/tests/Android.bp
new file mode 100644
index 0000000..8b44f61
--- /dev/null
+++ b/media/bufferpool/2.0/tests/Android.bp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+cc_test {
+ name: "VtsVndkHidlBufferpoolV2_0TargetSingleTest",
+ defaults: ["VtsHalTargetTestDefaults"],
+ srcs: [
+ "allocator.cpp",
+ "single.cpp",
+ ],
+ static_libs: [
+ "android.hardware.media.bufferpool@2.0",
+ "libcutils",
+ "libstagefright_bufferpool@2.0",
+ ],
+ shared_libs: [
+ "libfmq",
+ ],
+ compile_multilib: "both",
+}
+
+cc_test {
+ name: "VtsVndkHidlBufferpoolV2_0TargetMultiTest",
+ defaults: ["VtsHalTargetTestDefaults"],
+ srcs: [
+ "allocator.cpp",
+ "multi.cpp",
+ ],
+ static_libs: [
+ "android.hardware.media.bufferpool@2.0",
+ "libcutils",
+ "libstagefright_bufferpool@2.0",
+ ],
+ shared_libs: [
+ "libfmq",
+ ],
+ compile_multilib: "both",
+}
diff --git a/media/bufferpool/2.0/tests/OWNERS b/media/bufferpool/2.0/tests/OWNERS
new file mode 100644
index 0000000..6733e0c
--- /dev/null
+++ b/media/bufferpool/2.0/tests/OWNERS
@@ -0,0 +1,9 @@
+# Media team
+lajos@google.com
+pawin@google.com
+taklee@google.com
+wonsik@google.com
+
+# VTS team
+yim@google.com
+zhuoyao@google.com
diff --git a/media/bufferpool/2.0/tests/allocator.cpp b/media/bufferpool/2.0/tests/allocator.cpp
new file mode 100644
index 0000000..843f7ea
--- /dev/null
+++ b/media/bufferpool/2.0/tests/allocator.cpp
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cutils/ashmem.h>
+#include <sys/mman.h>
+#include "allocator.h"
+
+union Params {
+ struct {
+ uint32_t capacity;
+ } data;
+ uint8_t array[0];
+ Params() : data{0} {}
+ Params(uint32_t size)
+ : data{size} {}
+};
+
+
+namespace {
+
+struct HandleAshmem : public native_handle_t {
+ HandleAshmem(int ashmemFd, size_t size)
+ : native_handle_t(cHeader),
+ mFds{ ashmemFd },
+ mInts{ int (size & 0xFFFFFFFF), int((uint64_t(size) >> 32) & 0xFFFFFFFF), kMagic } {}
+
+ int ashmemFd() const { return mFds.mAshmem; }
+ size_t size() const {
+ return size_t(unsigned(mInts.mSizeLo))
+ | size_t(uint64_t(unsigned(mInts.mSizeHi)) << 32);
+ }
+
+ static bool isValid(const native_handle_t * const o);
+
+protected:
+ struct {
+ int mAshmem;
+ } mFds;
+ struct {
+ int mSizeLo;
+ int mSizeHi;
+ int mMagic;
+ } mInts;
+
+private:
+ enum {
+ kMagic = 'ahm\x00',
+ numFds = sizeof(mFds) / sizeof(int),
+ numInts = sizeof(mInts) / sizeof(int),
+ version = sizeof(native_handle_t)
+ };
+ const static native_handle_t cHeader;
+};
+
+const native_handle_t HandleAshmem::cHeader = {
+ HandleAshmem::version,
+ HandleAshmem::numFds,
+ HandleAshmem::numInts,
+ {}
+};
+
+bool HandleAshmem::isValid(const native_handle_t * const o) {
+ if (!o || memcmp(o, &cHeader, sizeof(cHeader))) {
+ return false;
+ }
+ const HandleAshmem *other = static_cast<const HandleAshmem*>(o);
+ return other->mInts.mMagic == kMagic;
+}
+
+class AllocationAshmem {
+private:
+ AllocationAshmem(int ashmemFd, size_t capacity, bool res)
+ : mHandle(ashmemFd, capacity),
+ mInit(res) {}
+
+public:
+ static AllocationAshmem *Alloc(size_t size) {
+ constexpr static const char *kAllocationTag = "bufferpool_test";
+ int ashmemFd = ashmem_create_region(kAllocationTag, size);
+ return new AllocationAshmem(ashmemFd, size, ashmemFd >= 0);
+ }
+
+ ~AllocationAshmem() {
+ if (mInit) {
+ native_handle_close(&mHandle);
+ }
+ }
+
+ const HandleAshmem *handle() {
+ return &mHandle;
+ }
+
+private:
+ HandleAshmem mHandle;
+ bool mInit;
+ // TODO: mapping and map fd
+};
+
+struct AllocationDtor {
+ AllocationDtor(const std::shared_ptr<AllocationAshmem> &alloc)
+ : mAlloc(alloc) {}
+
+ void operator()(BufferPoolAllocation *poolAlloc) { delete poolAlloc; }
+
+ const std::shared_ptr<AllocationAshmem> mAlloc;
+};
+
+}
+
+
+ResultStatus TestBufferPoolAllocator::allocate(
+ const std::vector<uint8_t> ¶ms,
+ std::shared_ptr<BufferPoolAllocation> *alloc,
+ size_t *allocSize) {
+ Params ashmemParams;
+ memcpy(&ashmemParams, params.data(), std::min(sizeof(Params), params.size()));
+
+ std::shared_ptr<AllocationAshmem> ashmemAlloc =
+ std::shared_ptr<AllocationAshmem>(
+ AllocationAshmem::Alloc(ashmemParams.data.capacity));
+ if (ashmemAlloc) {
+ BufferPoolAllocation *ptr = new BufferPoolAllocation(ashmemAlloc->handle());
+ if (ptr) {
+ *alloc = std::shared_ptr<BufferPoolAllocation>(ptr, AllocationDtor(ashmemAlloc));
+ if (*alloc) {
+ *allocSize = ashmemParams.data.capacity;
+ return ResultStatus::OK;
+ }
+ delete ptr;
+ return ResultStatus::NO_MEMORY;
+ }
+ }
+ return ResultStatus::CRITICAL_ERROR;
+}
+
+bool TestBufferPoolAllocator::compatible(const std::vector<uint8_t> &newParams,
+ const std::vector<uint8_t> &oldParams) {
+ size_t newSize = newParams.size();
+ size_t oldSize = oldParams.size();
+ if (newSize == oldSize) {
+ for (size_t i = 0; i < newSize; ++i) {
+ if (newParams[i] != oldParams[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+bool TestBufferPoolAllocator::Fill(const native_handle_t *handle, const unsigned char val) {
+ if (!HandleAshmem::isValid(handle)) {
+ return false;
+ }
+ const HandleAshmem *o = static_cast<const HandleAshmem*>(handle);
+ unsigned char *ptr = (unsigned char *)mmap(
+ NULL, o->size(), PROT_READ|PROT_WRITE, MAP_SHARED, o->ashmemFd(), 0);
+
+ if (ptr != MAP_FAILED) {
+ for (size_t i = 0; i < o->size(); ++i) {
+ ptr[i] = val;
+ }
+ munmap(ptr, o->size());
+ return true;
+ }
+ return false;
+}
+
+bool TestBufferPoolAllocator::Verify(const native_handle_t *handle, const unsigned char val) {
+ if (!HandleAshmem::isValid(handle)) {
+ return false;
+ }
+ const HandleAshmem *o = static_cast<const HandleAshmem*>(handle);
+ unsigned char *ptr = (unsigned char *)mmap(
+ NULL, o->size(), PROT_READ, MAP_SHARED, o->ashmemFd(), 0);
+
+ if (ptr != MAP_FAILED) {
+ bool res = true;
+ for (size_t i = 0; i < o->size(); ++i) {
+ if (ptr[i] != val) {
+ res = false;
+ break;
+ }
+ }
+ munmap(ptr, o->size());
+ return res;
+ }
+ return false;
+}
+
+void getTestAllocatorParams(std::vector<uint8_t> *params) {
+ constexpr static int kAllocationSize = 1024 * 10;
+ Params ashmemParams(kAllocationSize);
+
+ params->assign(ashmemParams.array, ashmemParams.array + sizeof(ashmemParams));
+}
diff --git a/media/bufferpool/2.0/tests/allocator.h b/media/bufferpool/2.0/tests/allocator.h
new file mode 100644
index 0000000..5281dc3
--- /dev/null
+++ b/media/bufferpool/2.0/tests/allocator.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 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 VNDK_HIDL_BUFFERPOOL_V2_0_ALLOCATOR_H
+#define VNDK_HIDL_BUFFERPOOL_V2_0_ALLOCATOR_H
+
+#include <bufferpool/BufferPoolTypes.h>
+
+using android::hardware::media::bufferpool::V2_0::ResultStatus;
+using android::hardware::media::bufferpool::V2_0::implementation::
+ BufferPoolAllocation;
+using android::hardware::media::bufferpool::V2_0::implementation::
+ BufferPoolAllocator;
+
+// buffer allocator for the tests
+class TestBufferPoolAllocator : public BufferPoolAllocator {
+ public:
+ TestBufferPoolAllocator() {}
+
+ ~TestBufferPoolAllocator() override {}
+
+ ResultStatus allocate(const std::vector<uint8_t> ¶ms,
+ std::shared_ptr<BufferPoolAllocation> *alloc,
+ size_t *allocSize) override;
+
+ bool compatible(const std::vector<uint8_t> &newParams,
+ const std::vector<uint8_t> &oldParams) override;
+
+ static bool Fill(const native_handle_t *handle, const unsigned char val);
+
+ static bool Verify(const native_handle_t *handle, const unsigned char val);
+
+};
+
+// retrieve buffer allocator paramters
+void getTestAllocatorParams(std::vector<uint8_t> *params);
+
+#endif // VNDK_HIDL_BUFFERPOOL_V2_0_ALLOCATOR_H
diff --git a/media/bufferpool/2.0/tests/multi.cpp b/media/bufferpool/2.0/tests/multi.cpp
new file mode 100644
index 0000000..68b6992
--- /dev/null
+++ b/media/bufferpool/2.0/tests/multi.cpp
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2018 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 "buffferpool_unit_test"
+
+#include <gtest/gtest.h>
+
+#include <android-base/logging.h>
+#include <binder/ProcessState.h>
+#include <bufferpool/ClientManager.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/HidlTransportSupport.h>
+#include <hidl/LegacySupport.h>
+#include <hidl/Status.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <iostream>
+#include <memory>
+#include <vector>
+#include "allocator.h"
+
+using android::hardware::configureRpcThreadpool;
+using android::hardware::hidl_handle;
+using android::hardware::media::bufferpool::V2_0::IClientManager;
+using android::hardware::media::bufferpool::V2_0::ResultStatus;
+using android::hardware::media::bufferpool::V2_0::implementation::BufferId;
+using android::hardware::media::bufferpool::V2_0::implementation::ClientManager;
+using android::hardware::media::bufferpool::V2_0::implementation::ConnectionId;
+using android::hardware::media::bufferpool::V2_0::implementation::TransactionId;
+using android::hardware::media::bufferpool::BufferPoolData;
+
+namespace {
+
+// communication message types between processes.
+enum PipeCommand : int32_t {
+ INIT_OK = 0,
+ INIT_ERROR,
+ SEND,
+ RECEIVE_OK,
+ RECEIVE_ERROR,
+};
+
+// communication message between processes.
+union PipeMessage {
+ struct {
+ int32_t command;
+ BufferId bufferId;
+ ConnectionId connectionId;
+ TransactionId transactionId;
+ int64_t timestampUs;
+ } data;
+ char array[0];
+};
+
+// media.bufferpool test setup
+class BufferpoolMultiTest : public ::testing::Test {
+ public:
+ virtual void SetUp() override {
+ ResultStatus status;
+ mReceiverPid = -1;
+ mConnectionValid = false;
+
+ ASSERT_TRUE(pipe(mCommandPipeFds) == 0);
+ ASSERT_TRUE(pipe(mResultPipeFds) == 0);
+
+ mReceiverPid = fork();
+ ASSERT_TRUE(mReceiverPid >= 0);
+
+ if (mReceiverPid == 0) {
+ doReceiver();
+ // In order to ignore gtest behaviour, wait for being killed from
+ // tearDown
+ pause();
+ }
+
+ mManager = ClientManager::getInstance();
+ ASSERT_NE(mManager, nullptr);
+
+ mAllocator = std::make_shared<TestBufferPoolAllocator>();
+ ASSERT_TRUE((bool)mAllocator);
+
+ status = mManager->create(mAllocator, &mConnectionId);
+ ASSERT_TRUE(status == ResultStatus::OK);
+ mConnectionValid = true;
+ }
+
+ virtual void TearDown() override {
+ if (mReceiverPid > 0) {
+ kill(mReceiverPid, SIGKILL);
+ int wstatus;
+ wait(&wstatus);
+ }
+
+ if (mConnectionValid) {
+ mManager->close(mConnectionId);
+ }
+ }
+
+ protected:
+ static void description(const std::string& description) {
+ RecordProperty("description", description);
+ }
+
+ android::sp<ClientManager> mManager;
+ std::shared_ptr<BufferPoolAllocator> mAllocator;
+ bool mConnectionValid;
+ ConnectionId mConnectionId;
+ pid_t mReceiverPid;
+ int mCommandPipeFds[2];
+ int mResultPipeFds[2];
+
+ bool sendMessage(int *pipes, const PipeMessage &message) {
+ int ret = write(pipes[1], message.array, sizeof(PipeMessage));
+ return ret == sizeof(PipeMessage);
+ }
+
+ bool receiveMessage(int *pipes, PipeMessage *message) {
+ int ret = read(pipes[0], message->array, sizeof(PipeMessage));
+ return ret == sizeof(PipeMessage);
+ }
+
+ void doReceiver() {
+ configureRpcThreadpool(1, false);
+ PipeMessage message;
+ mManager = ClientManager::getInstance();
+ if (!mManager) {
+ message.data.command = PipeCommand::INIT_ERROR;
+ sendMessage(mResultPipeFds, message);
+ return;
+ }
+ android::status_t status = mManager->registerAsService();
+ if (status != android::OK) {
+ message.data.command = PipeCommand::INIT_ERROR;
+ sendMessage(mResultPipeFds, message);
+ return;
+ }
+ message.data.command = PipeCommand::INIT_OK;
+ sendMessage(mResultPipeFds, message);
+
+ receiveMessage(mCommandPipeFds, &message);
+ {
+ native_handle_t *rhandle = nullptr;
+ std::shared_ptr<BufferPoolData> rbuffer;
+ ResultStatus status = mManager->receive(
+ message.data.connectionId, message.data.transactionId,
+ message.data.bufferId, message.data.timestampUs, &rhandle, &rbuffer);
+ mManager->close(message.data.connectionId);
+ if (status != ResultStatus::OK) {
+ if (!TestBufferPoolAllocator::Verify(rhandle, 0x77)) {
+ message.data.command = PipeCommand::RECEIVE_ERROR;
+ sendMessage(mResultPipeFds, message);
+ return;
+ }
+ }
+ }
+ message.data.command = PipeCommand::RECEIVE_OK;
+ sendMessage(mResultPipeFds, message);
+ }
+};
+
+// Buffer transfer test between processes.
+TEST_F(BufferpoolMultiTest, TransferBuffer) {
+ ResultStatus status;
+ PipeMessage message;
+
+ ASSERT_TRUE(receiveMessage(mResultPipeFds, &message));
+
+ android::sp<IClientManager> receiver = IClientManager::getService();
+ ConnectionId receiverId;
+ ASSERT_TRUE((bool)receiver);
+
+ status = mManager->registerSender(receiver, mConnectionId, &receiverId);
+ ASSERT_TRUE(status == ResultStatus::OK);
+ {
+ native_handle_t *shandle = nullptr;
+ std::shared_ptr<BufferPoolData> sbuffer;
+ TransactionId transactionId;
+ int64_t postUs;
+ std::vector<uint8_t> vecParams;
+
+ getTestAllocatorParams(&vecParams);
+ status = mManager->allocate(mConnectionId, vecParams, &shandle, &sbuffer);
+ ASSERT_TRUE(status == ResultStatus::OK);
+
+ ASSERT_TRUE(TestBufferPoolAllocator::Fill(shandle, 0x77));
+
+ status = mManager->postSend(receiverId, sbuffer, &transactionId, &postUs);
+ ASSERT_TRUE(status == ResultStatus::OK);
+
+ message.data.command = PipeCommand::SEND;
+ message.data.bufferId = sbuffer->mId;
+ message.data.connectionId = receiverId;
+ message.data.transactionId = transactionId;
+ message.data.timestampUs = postUs;
+ sendMessage(mCommandPipeFds, message);
+ }
+ EXPECT_TRUE(receiveMessage(mResultPipeFds, &message));
+}
+
+} // anonymous namespace
+
+int main(int argc, char** argv) {
+ setenv("TREBLE_TESTING_OVERRIDE", "true", true);
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = RUN_ALL_TESTS();
+ LOG(INFO) << "Test result = " << status;
+ return status;
+}
diff --git a/media/bufferpool/2.0/tests/single.cpp b/media/bufferpool/2.0/tests/single.cpp
new file mode 100644
index 0000000..777edcf
--- /dev/null
+++ b/media/bufferpool/2.0/tests/single.cpp
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2018 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 "buffferpool_unit_test"
+
+#include <gtest/gtest.h>
+
+#include <android-base/logging.h>
+#include <binder/ProcessState.h>
+#include <bufferpool/ClientManager.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/HidlTransportSupport.h>
+#include <hidl/LegacySupport.h>
+#include <hidl/Status.h>
+#include <unistd.h>
+#include <iostream>
+#include <memory>
+#include <vector>
+#include "allocator.h"
+
+using android::hardware::hidl_handle;
+using android::hardware::media::bufferpool::V2_0::ResultStatus;
+using android::hardware::media::bufferpool::V2_0::implementation::BufferId;
+using android::hardware::media::bufferpool::V2_0::implementation::ClientManager;
+using android::hardware::media::bufferpool::V2_0::implementation::ConnectionId;
+using android::hardware::media::bufferpool::V2_0::implementation::TransactionId;
+using android::hardware::media::bufferpool::BufferPoolData;
+
+namespace {
+
+// Number of iteration for buffer allocation test.
+constexpr static int kNumAllocationTest = 3;
+
+// Number of iteration for buffer recycling test.
+constexpr static int kNumRecycleTest = 3;
+
+// media.bufferpool test setup
+class BufferpoolSingleTest : public ::testing::Test {
+ public:
+ virtual void SetUp() override {
+ ResultStatus status;
+ mConnectionValid = false;
+
+ mManager = ClientManager::getInstance();
+ ASSERT_NE(mManager, nullptr);
+
+ mAllocator = std::make_shared<TestBufferPoolAllocator>();
+ ASSERT_TRUE((bool)mAllocator);
+
+ status = mManager->create(mAllocator, &mConnectionId);
+ ASSERT_TRUE(status == ResultStatus::OK);
+
+ mConnectionValid = true;
+
+ status = mManager->registerSender(mManager, mConnectionId, &mReceiverId);
+ ASSERT_TRUE(status == ResultStatus::ALREADY_EXISTS &&
+ mReceiverId == mConnectionId);
+ }
+
+ virtual void TearDown() override {
+ if (mConnectionValid) {
+ mManager->close(mConnectionId);
+ }
+ }
+
+ protected:
+ static void description(const std::string& description) {
+ RecordProperty("description", description);
+ }
+
+ android::sp<ClientManager> mManager;
+ std::shared_ptr<BufferPoolAllocator> mAllocator;
+ bool mConnectionValid;
+ ConnectionId mConnectionId;
+ ConnectionId mReceiverId;
+
+};
+
+// Buffer allocation test.
+// Check whether each buffer allocation is done successfully with
+// unique buffer id.
+TEST_F(BufferpoolSingleTest, AllocateBuffer) {
+ ResultStatus status;
+ std::vector<uint8_t> vecParams;
+ getTestAllocatorParams(&vecParams);
+
+ std::shared_ptr<BufferPoolData> buffer[kNumAllocationTest];
+ native_handle_t *allocHandle = nullptr;
+ for (int i = 0; i < kNumAllocationTest; ++i) {
+ status = mManager->allocate(mConnectionId, vecParams, &allocHandle, &buffer[i]);
+ ASSERT_TRUE(status == ResultStatus::OK);
+ }
+ for (int i = 0; i < kNumAllocationTest; ++i) {
+ for (int j = i + 1; j < kNumAllocationTest; ++j) {
+ ASSERT_TRUE(buffer[i]->mId != buffer[j]->mId);
+ }
+ }
+ EXPECT_TRUE(kNumAllocationTest > 1);
+}
+
+// Buffer recycle test.
+// Check whether de-allocated buffers are recycled.
+TEST_F(BufferpoolSingleTest, RecycleBuffer) {
+ ResultStatus status;
+ std::vector<uint8_t> vecParams;
+ getTestAllocatorParams(&vecParams);
+
+ BufferId bid[kNumRecycleTest];
+ for (int i = 0; i < kNumRecycleTest; ++i) {
+ std::shared_ptr<BufferPoolData> buffer;
+ native_handle_t *allocHandle = nullptr;
+ status = mManager->allocate(mConnectionId, vecParams, &allocHandle, &buffer);
+ ASSERT_TRUE(status == ResultStatus::OK);
+ bid[i] = buffer->mId;
+ }
+ for (int i = 1; i < kNumRecycleTest; ++i) {
+ ASSERT_TRUE(bid[i - 1] == bid[i]);
+ }
+ EXPECT_TRUE(kNumRecycleTest > 1);
+}
+
+// Buffer transfer test.
+// Check whether buffer is transferred to another client successfully.
+TEST_F(BufferpoolSingleTest, TransferBuffer) {
+ ResultStatus status;
+ std::vector<uint8_t> vecParams;
+ getTestAllocatorParams(&vecParams);
+ std::shared_ptr<BufferPoolData> sbuffer, rbuffer;
+ native_handle_t *allocHandle = nullptr;
+ native_handle_t *recvHandle = nullptr;
+
+ TransactionId transactionId;
+ int64_t postUs;
+
+ status = mManager->allocate(mConnectionId, vecParams, &allocHandle, &sbuffer);
+ ASSERT_TRUE(status == ResultStatus::OK);
+ ASSERT_TRUE(TestBufferPoolAllocator::Fill(allocHandle, 0x77));
+ status = mManager->postSend(mReceiverId, sbuffer, &transactionId, &postUs);
+ ASSERT_TRUE(status == ResultStatus::OK);
+ status = mManager->receive(mReceiverId, transactionId, sbuffer->mId, postUs,
+ &recvHandle, &rbuffer);
+ EXPECT_TRUE(status == ResultStatus::OK);
+ ASSERT_TRUE(TestBufferPoolAllocator::Verify(recvHandle, 0x77));
+}
+
+} // anonymous namespace
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = RUN_ALL_TESTS();
+ LOG(INFO) << "Test result = " << status;
+ return status;
+}
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index c02d2fb..f52d451 100644
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -393,7 +393,9 @@
}
mPssh.clear();
- delete mCachedSource;
+ if (mCachedSource != mDataSource) {
+ delete mCachedSource;
+ }
delete mDataSource;
}
diff --git a/media/libaaudio/src/Android.bp b/media/libaaudio/src/Android.bp
index b9e28a0..4090286 100644
--- a/media/libaaudio/src/Android.bp
+++ b/media/libaaudio/src/Android.bp
@@ -6,6 +6,7 @@
"client",
"core",
"fifo",
+ "flowgraph",
"legacy",
"utility",
],
@@ -25,9 +26,9 @@
"utility/FixedBlockAdapter.cpp",
"utility/FixedBlockReader.cpp",
"utility/FixedBlockWriter.cpp",
- "utility/LinearRamp.cpp",
"fifo/FifoBuffer.cpp",
"fifo/FifoControllerBase.cpp",
+ "client/AAudioFlowGraph.cpp",
"client/AudioEndpoint.cpp",
"client/AudioStreamInternal.cpp",
"client/AudioStreamInternalCapture.cpp",
@@ -42,6 +43,16 @@
"binding/RingBufferParcelable.cpp",
"binding/SharedMemoryParcelable.cpp",
"binding/SharedRegionParcelable.cpp",
+ "flowgraph/AudioProcessorBase.cpp",
+ "flowgraph/ClipToRange.cpp",
+ "flowgraph/MonoToMultiConverter.cpp",
+ "flowgraph/RampLinear.cpp",
+ "flowgraph/SinkFloat.cpp",
+ "flowgraph/SinkI16.cpp",
+ "flowgraph/SinkI24.cpp",
+ "flowgraph/SourceFloat.cpp",
+ "flowgraph/SourceI16.cpp",
+ "flowgraph/SourceI24.cpp",
],
cflags: [
diff --git a/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp b/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
index 959db61..3d1bc9b 100644
--- a/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
+++ b/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+#define LOG_TAG "AAudioStreamConfiguration"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
#include <stdint.h>
#include <sys/mman.h>
@@ -36,6 +40,7 @@
status_t AAudioStreamConfiguration::writeToParcel(Parcel* parcel) const {
status_t status;
+
status = parcel->writeInt32(getDeviceId());
if (status != NO_ERROR) goto error;
status = parcel->writeInt32(getSampleRate());
@@ -46,6 +51,7 @@
if (status != NO_ERROR) goto error;
status = parcel->writeInt32((int32_t) getFormat());
if (status != NO_ERROR) goto error;
+
status = parcel->writeInt32((int32_t) getDirection());
if (status != NO_ERROR) goto error;
status = parcel->writeInt32(getBufferCapacity());
@@ -60,7 +66,7 @@
if (status != NO_ERROR) goto error;
return NO_ERROR;
error:
- ALOGE("AAudioStreamConfiguration.writeToParcel(): write failed = %d", status);
+ ALOGE("%s(): write failed = %d", __func__, status);
return status;
}
@@ -80,7 +86,8 @@
setSharingMode((aaudio_sharing_mode_t) value);
status = parcel->readInt32(&value);
if (status != NO_ERROR) goto error;
- setFormat((aaudio_format_t) value);
+ setFormat((audio_format_t) value);
+
status = parcel->readInt32(&value);
if (status != NO_ERROR) goto error;
setDirection((aaudio_direction_t) value);
@@ -99,8 +106,9 @@
status = parcel->readInt32(&value);
if (status != NO_ERROR) goto error;
setSessionId(value);
+
return NO_ERROR;
error:
- ALOGE("AAudioStreamConfiguration.readFromParcel(): read failed = %d", status);
+ ALOGE("%s(): read failed = %d", __func__, status);
return status;
}
diff --git a/media/libaaudio/src/binding/SharedMemoryParcelable.cpp b/media/libaaudio/src/binding/SharedMemoryParcelable.cpp
index 0b0cf77..67955e8 100644
--- a/media/libaaudio/src/binding/SharedMemoryParcelable.cpp
+++ b/media/libaaudio/src/binding/SharedMemoryParcelable.cpp
@@ -43,7 +43,7 @@
void SharedMemoryParcelable::setup(const unique_fd& fd, int32_t sizeInBytes) {
mFd.reset(dup(fd.get())); // store a duplicate fd
- ALOGV("setup(%d -> %d, %d) this = %p\n", fd.get(), mFd.get(), sizeInBytes, this);
+ ALOGV("setup(fd = %d -> %d, size = %d) this = %p\n", fd.get(), mFd.get(), sizeInBytes, this);
mSizeInBytes = sizeInBytes;
}
@@ -104,7 +104,8 @@
mResolvedAddress = (uint8_t *) mmap(0, mSizeInBytes, PROT_READ | PROT_WRITE,
MAP_SHARED, fd.get(), 0);
if (mResolvedAddress == MMAP_UNRESOLVED_ADDRESS) {
- ALOGE("mmap() failed for fd = %d, errno = %s", fd.get(), strerror(errno));
+ ALOGE("mmap() failed for fd = %d, nBytes = %d, errno = %s",
+ fd.get(), mSizeInBytes, strerror(errno));
return AAUDIO_ERROR_INTERNAL;
}
return AAUDIO_OK;
diff --git a/media/libaaudio/src/binding/SharedMemoryParcelable.h b/media/libaaudio/src/binding/SharedMemoryParcelable.h
index 82c2240..4ec38c5 100644
--- a/media/libaaudio/src/binding/SharedMemoryParcelable.h
+++ b/media/libaaudio/src/binding/SharedMemoryParcelable.h
@@ -70,8 +70,8 @@
aaudio_result_t resolveSharedMemory(const android::base::unique_fd& fd);
android::base::unique_fd mFd;
- int32_t mSizeInBytes = 0;
- uint8_t *mResolvedAddress = MMAP_UNRESOLVED_ADDRESS;
+ int32_t mSizeInBytes = 0;
+ uint8_t *mResolvedAddress = MMAP_UNRESOLVED_ADDRESS;
private:
diff --git a/media/libaaudio/src/client/AAudioFlowGraph.cpp b/media/libaaudio/src/client/AAudioFlowGraph.cpp
new file mode 100644
index 0000000..3e43c6b
--- /dev/null
+++ b/media/libaaudio/src/client/AAudioFlowGraph.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2018 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 "AAudioFlowGraph"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include "AAudioFlowGraph.h"
+
+#include <flowgraph/ClipToRange.h>
+#include <flowgraph/MonoToMultiConverter.h>
+#include <flowgraph/RampLinear.h>
+#include <flowgraph/SinkFloat.h>
+#include <flowgraph/SinkI16.h>
+#include <flowgraph/SinkI24.h>
+#include <flowgraph/SourceFloat.h>
+#include <flowgraph/SourceI16.h>
+#include <flowgraph/SourceI24.h>
+
+using namespace flowgraph;
+
+aaudio_result_t AAudioFlowGraph::configure(audio_format_t sourceFormat,
+ int32_t sourceChannelCount,
+ audio_format_t sinkFormat,
+ int32_t sinkChannelCount) {
+ AudioFloatOutputPort *lastOutput = nullptr;
+
+ ALOGD("%s() source format = 0x%08x, channels = %d, sink format = 0x%08x, channels = %d",
+ __func__, sourceFormat, sourceChannelCount, sinkFormat, sinkChannelCount);
+
+ switch (sourceFormat) {
+ case AUDIO_FORMAT_PCM_FLOAT:
+ mSource = std::make_unique<SourceFloat>(sourceChannelCount);
+ break;
+ case AUDIO_FORMAT_PCM_16_BIT:
+ mSource = std::make_unique<SourceI16>(sourceChannelCount);
+ break;
+ case AUDIO_FORMAT_PCM_24_BIT_PACKED:
+ mSource = std::make_unique<SourceI24>(sourceChannelCount);
+ break;
+ default: // TODO add I32
+ ALOGE("%s() Unsupported source format = %d", __func__, sourceFormat);
+ return AAUDIO_ERROR_UNIMPLEMENTED;
+ }
+ lastOutput = &mSource->output;
+
+ // Apply volume as a ramp to avoid pops.
+ mVolumeRamp = std::make_unique<RampLinear>(sourceChannelCount);
+ lastOutput->connect(&mVolumeRamp->input);
+ lastOutput = &mVolumeRamp->output;
+
+ // For a pure float graph, there is chance that the data range may be very large.
+ // So we should clip to a reasonable value that allows a little headroom.
+ if (sourceFormat == AUDIO_FORMAT_PCM_FLOAT && sinkFormat == AUDIO_FORMAT_PCM_FLOAT) {
+ mClipper = std::make_unique<ClipToRange>(sourceChannelCount);
+ lastOutput->connect(&mClipper->input);
+ lastOutput = &mClipper->output;
+ }
+
+ // Expand the number of channels if required.
+ if (sourceChannelCount == 1 && sinkChannelCount > 1) {
+ mChannelConverter = std::make_unique<MonoToMultiConverter>(sinkChannelCount);
+ lastOutput->connect(&mChannelConverter->input);
+ lastOutput = &mChannelConverter->output;
+ } else if (sourceChannelCount != sinkChannelCount) {
+ ALOGE("%s() Channel reduction not supported.", __func__);
+ return AAUDIO_ERROR_UNIMPLEMENTED;
+ }
+
+ switch (sinkFormat) {
+ case AUDIO_FORMAT_PCM_FLOAT:
+ mSink = std::make_unique<SinkFloat>(sinkChannelCount);
+ break;
+ case AUDIO_FORMAT_PCM_16_BIT:
+ mSink = std::make_unique<SinkI16>(sinkChannelCount);
+ break;
+ case AUDIO_FORMAT_PCM_24_BIT_PACKED:
+ mSink = std::make_unique<SinkI24>(sinkChannelCount);
+ break;
+ default: // TODO add I32
+ ALOGE("%s() Unsupported sink format = %d", __func__, sinkFormat);
+ return AAUDIO_ERROR_UNIMPLEMENTED;
+ }
+ lastOutput->connect(&mSink->input);
+
+ return AAUDIO_OK;
+}
+
+void AAudioFlowGraph::process(const void *source, void *destination, int32_t numFrames) {
+ mSource->setData(source, numFrames);
+ mSink->read(destination, numFrames);
+}
+
+/**
+ * @param volume between 0.0 and 1.0
+ */
+void AAudioFlowGraph::setTargetVolume(float volume) {
+ mVolumeRamp->setTarget(volume);
+}
+
+void AAudioFlowGraph::setRampLengthInFrames(int32_t numFrames) {
+ mVolumeRamp->setLengthInFrames(numFrames);
+}
diff --git a/media/libaaudio/src/client/AAudioFlowGraph.h b/media/libaaudio/src/client/AAudioFlowGraph.h
new file mode 100644
index 0000000..a49f64e
--- /dev/null
+++ b/media/libaaudio/src/client/AAudioFlowGraph.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2018 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_AAUDIO_FLOW_GRAPH_H
+#define ANDROID_AAUDIO_FLOW_GRAPH_H
+
+#include <memory>
+#include <stdint.h>
+#include <sys/types.h>
+#include <system/audio.h>
+
+#include <aaudio/AAudio.h>
+#include <flowgraph/ClipToRange.h>
+#include <flowgraph/MonoToMultiConverter.h>
+#include <flowgraph/RampLinear.h>
+
+class AAudioFlowGraph {
+public:
+ /** Connect several modules together to convert from source to sink.
+ * This should only be called once for each instance.
+ *
+ * @param sourceFormat
+ * @param sourceChannelCount
+ * @param sinkFormat
+ * @param sinkChannelCount
+ * @return
+ */
+ aaudio_result_t configure(audio_format_t sourceFormat,
+ int32_t sourceChannelCount,
+ audio_format_t sinkFormat,
+ int32_t sinkChannelCount);
+
+ void process(const void *source, void *destination, int32_t numFrames);
+
+ /**
+ * @param volume between 0.0 and 1.0
+ */
+ void setTargetVolume(float volume);
+
+ void setRampLengthInFrames(int32_t numFrames);
+
+private:
+ std::unique_ptr<flowgraph::AudioSource> mSource;
+ std::unique_ptr<flowgraph::RampLinear> mVolumeRamp;
+ std::unique_ptr<flowgraph::ClipToRange> mClipper;
+ std::unique_ptr<flowgraph::MonoToMultiConverter> mChannelConverter;
+ std::unique_ptr<flowgraph::AudioSink> mSink;
+};
+
+
+#endif //ANDROID_AAUDIO_FLOW_GRAPH_H
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 9204824..0a8021a 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -39,7 +39,6 @@
#include "core/AudioStreamBuilder.h"
#include "fifo/FifoBuffer.h"
#include "utility/AudioClock.h"
-#include "utility/LinearRamp.h"
#include "AudioStreamInternal.h"
@@ -92,11 +91,11 @@
}
// We have to do volume scaling. So we prefer FLOAT format.
- if (getFormat() == AAUDIO_FORMAT_UNSPECIFIED) {
- setFormat(AAUDIO_FORMAT_PCM_FLOAT);
+ if (getFormat() == AUDIO_FORMAT_DEFAULT) {
+ setFormat(AUDIO_FORMAT_PCM_FLOAT);
}
// Request FLOAT for the shared mixer.
- request.getConfiguration().setFormat(AAUDIO_FORMAT_PCM_FLOAT);
+ request.getConfiguration().setFormat(AUDIO_FORMAT_PCM_FLOAT);
// Build the request to send to the server.
request.setUserId(getuid());
@@ -126,7 +125,7 @@
// if that failed then try switching from mono to stereo if OUTPUT.
// Only do this in the client. Otherwise we end up with a mono mixer in the service
// that writes to a stereo MMAP stream.
- ALOGD("%s - openStream() returned %d, try switching from MONO to STEREO",
+ ALOGD("%s() - openStream() returned %d, try switching from MONO to STEREO",
__func__, mServiceStreamHandle);
request.getConfiguration().setSamplesPerFrame(2); // stereo
mServiceStreamHandle = mServiceInterface.openStream(request, configurationOutput);
@@ -212,9 +211,7 @@
mCallbackFrames = mFramesPerBurst;
}
- int32_t bytesPerFrame = getSamplesPerFrame()
- * AAudioConvert_formatToSizeInBytes(getFormat());
- int32_t callbackBufferSize = mCallbackFrames * bytesPerFrame;
+ const int32_t callbackBufferSize = mCallbackFrames * getBytesPerFrame();
mCallbackBuffer = new uint8_t[callbackBufferSize];
}
diff --git a/media/libaaudio/src/client/AudioStreamInternal.h b/media/libaaudio/src/client/AudioStreamInternal.h
index 0425cd5..3bb9e1e 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.h
+++ b/media/libaaudio/src/client/AudioStreamInternal.h
@@ -27,7 +27,6 @@
#include "client/AudioEndpoint.h"
#include "core/AudioStream.h"
#include "utility/AudioClock.h"
-#include "utility/LinearRamp.h"
using android::sp;
using android::IAAudioService;
@@ -193,6 +192,8 @@
int64_t mServiceLatencyNanos = 0;
+ // Sometimes the hardware is operating with a different channel count from the app.
+ // Then we require conversion in AAudio.
int32_t mDeviceChannelCount = 0;
};
diff --git a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
index 0719fe1..4a0e6da 100644
--- a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
@@ -20,6 +20,7 @@
#include <utils/Log.h>
#include <algorithm>
+#include <audio_utils/primitives.h>
#include <aaudio/AAudio.h>
#include "client/AudioStreamInternalCapture.h"
@@ -165,35 +166,36 @@
// Read data in one or two parts.
for (int partIndex = 0; framesLeft > 0 && partIndex < WrappingBuffer::SIZE; partIndex++) {
int32_t framesToProcess = framesLeft;
- int32_t framesAvailable = wrappingBuffer.numFrames[partIndex];
+ const int32_t framesAvailable = wrappingBuffer.numFrames[partIndex];
if (framesAvailable <= 0) break;
if (framesToProcess > framesAvailable) {
framesToProcess = framesAvailable;
}
- int32_t numBytes = getBytesPerFrame() * framesToProcess;
- int32_t numSamples = framesToProcess * getSamplesPerFrame();
+ const int32_t numBytes = getBytesPerFrame() * framesToProcess;
+ const int32_t numSamples = framesToProcess * getSamplesPerFrame();
+ const audio_format_t sourceFormat = getDeviceFormat();
+ const audio_format_t destinationFormat = getFormat();
// TODO factor this out into a utility function
- if (getDeviceFormat() == getFormat()) {
+ if (sourceFormat == destinationFormat) {
memcpy(destination, wrappingBuffer.data[partIndex], numBytes);
- } else if (getDeviceFormat() == AAUDIO_FORMAT_PCM_I16
- && getFormat() == AAUDIO_FORMAT_PCM_FLOAT) {
- AAudioConvert_pcm16ToFloat(
- (const int16_t *) wrappingBuffer.data[partIndex],
+ } else if (sourceFormat == AUDIO_FORMAT_PCM_16_BIT
+ && destinationFormat == AUDIO_FORMAT_PCM_FLOAT) {
+ memcpy_to_float_from_i16(
(float *) destination,
- numSamples,
- 1.0f);
- } else if (getDeviceFormat() == AAUDIO_FORMAT_PCM_FLOAT
- && getFormat() == AAUDIO_FORMAT_PCM_I16) {
- AAudioConvert_floatToPcm16(
- (const float *) wrappingBuffer.data[partIndex],
+ (const int16_t *) wrappingBuffer.data[partIndex],
+ numSamples);
+ } else if (sourceFormat == AUDIO_FORMAT_PCM_FLOAT
+ && destinationFormat == AUDIO_FORMAT_PCM_16_BIT) {
+ memcpy_to_i16_from_float(
(int16_t *) destination,
- numSamples,
- 1.0f);
+ (const float *) wrappingBuffer.data[partIndex],
+ numSamples);
} else {
- ALOGE("Format conversion not supported!");
+ ALOGE("%s() - Format conversion not supported! audio_format_t source = %u, dest = %u",
+ __func__, sourceFormat, destinationFormat);
return AAUDIO_ERROR_INVALID_FORMAT;
}
destination += numBytes;
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
index 795ba2c..2ae37a5 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
@@ -43,9 +43,17 @@
aaudio_result_t AudioStreamInternalPlay::open(const AudioStreamBuilder &builder) {
aaudio_result_t result = AudioStreamInternal::open(builder);
if (result == AAUDIO_OK) {
+ result = mFlowGraph.configure(getFormat(),
+ getSamplesPerFrame(),
+ getDeviceFormat(),
+ getDeviceChannelCount());
+
+ if (result != AAUDIO_OK) {
+ close();
+ }
// Sample rate is constrained to common values by now and should not overflow.
int32_t numFrames = kRampMSec * getSampleRate() / AAUDIO_MILLIS_PER_SECOND;
- mVolumeRamp.setLengthInFrames(numFrames);
+ mFlowGraph.setRampLengthInFrames(numFrames);
}
return result;
}
@@ -216,22 +224,10 @@
}
int32_t numBytes = getBytesPerFrame() * framesToWrite;
- // Data conversion.
- float levelFrom;
- float levelTo;
- mVolumeRamp.nextSegment(framesToWrite, &levelFrom, &levelTo);
- AAudioDataConverter::FormattedData source(
- (void *)byteBuffer,
- getFormat(),
- getSamplesPerFrame());
- AAudioDataConverter::FormattedData destination(
- wrappingBuffer.data[partIndex],
- getDeviceFormat(),
- getDeviceChannelCount());
-
- AAudioDataConverter::convert(source, destination, framesToWrite,
- levelFrom, levelTo);
+ mFlowGraph.process((void *)byteBuffer,
+ wrappingBuffer.data[partIndex],
+ framesToWrite);
byteBuffer += numBytes;
framesLeft -= framesToWrite;
@@ -313,6 +309,6 @@
float combinedVolume = mStreamVolume * getDuckAndMuteVolume();
ALOGD("%s() mStreamVolume * duckAndMuteVolume = %f * %f = %f",
__func__, mStreamVolume, getDuckAndMuteVolume(), combinedVolume);
- mVolumeRamp.setTarget(combinedVolume);
+ mFlowGraph.setTargetVolume(combinedVolume);
return android::NO_ERROR;
}
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.h b/media/libaaudio/src/client/AudioStreamInternalPlay.h
index 977a909..cab2942 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.h
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.h
@@ -21,6 +21,7 @@
#include <aaudio/AAudio.h>
#include "binding/AAudioServiceInterface.h"
+#include "client/AAudioFlowGraph.h"
#include "client/AudioStreamInternal.h"
using android::sp;
@@ -93,7 +94,7 @@
int64_t mLastFramesRead = 0; // used to prevent retrograde motion
- LinearRamp mVolumeRamp;
+ AAudioFlowGraph mFlowGraph;
};
diff --git a/media/libaaudio/src/core/AAudioAudio.cpp b/media/libaaudio/src/core/AAudioAudio.cpp
index df0db79..8dc31d0 100644
--- a/media/libaaudio/src/core/AAudioAudio.cpp
+++ b/media/libaaudio/src/core/AAudioAudio.cpp
@@ -167,7 +167,9 @@
aaudio_format_t format)
{
AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
- streamBuilder->setFormat(format);
+ // Use audio_format_t everywhere internally.
+ const audio_format_t internalFormat = AAudioConvert_aaudioToAndroidDataFormat(format);
+ streamBuilder->setFormat(internalFormat);
}
AAUDIO_API void AAudioStreamBuilder_setSharingMode(AAudioStreamBuilder* builder,
@@ -408,7 +410,9 @@
AAUDIO_API aaudio_format_t AAudioStream_getFormat(AAudioStream* stream)
{
AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
- return audioStream->getFormat();
+ // Use audio_format_t internally.
+ audio_format_t internalFormat = audioStream->getFormat();
+ return AAudioConvert_androidToAAudioDataFormat(internalFormat);
}
AAUDIO_API aaudio_result_t AAudioStream_setBufferSizeInFrames(AAudioStream* stream,
diff --git a/media/libaaudio/src/core/AAudioStreamParameters.cpp b/media/libaaudio/src/core/AAudioStreamParameters.cpp
index d56701b..bd42697 100644
--- a/media/libaaudio/src/core/AAudioStreamParameters.cpp
+++ b/media/libaaudio/src/core/AAudioStreamParameters.cpp
@@ -48,6 +48,20 @@
mInputPreset = other.mInputPreset;
}
+static aaudio_result_t isFormatValid(audio_format_t format) {
+ switch (format) {
+ case AUDIO_FORMAT_DEFAULT:
+ case AUDIO_FORMAT_PCM_16_BIT:
+ case AUDIO_FORMAT_PCM_FLOAT:
+ break; // valid
+ default:
+ ALOGE("audioFormat not valid, audio_format_t = 0x%08x", format);
+ return AAUDIO_ERROR_INVALID_FORMAT;
+ // break;
+ }
+ return AAUDIO_OK;
+}
+
aaudio_result_t AAudioStreamParameters::validate() const {
if (mSamplesPerFrame != AAUDIO_UNSPECIFIED
&& (mSamplesPerFrame < SAMPLES_PER_FRAME_MIN || mSamplesPerFrame > SAMPLES_PER_FRAME_MAX)) {
@@ -79,16 +93,8 @@
// break;
}
- switch (mAudioFormat) {
- case AAUDIO_FORMAT_UNSPECIFIED:
- case AAUDIO_FORMAT_PCM_I16:
- case AAUDIO_FORMAT_PCM_FLOAT:
- break; // valid
- default:
- ALOGE("audioFormat not valid = %d", mAudioFormat);
- return AAUDIO_ERROR_INVALID_FORMAT;
- // break;
- }
+ aaudio_result_t result = isFormatValid (mAudioFormat);
+ if (result != AAUDIO_OK) return result;
if (mSampleRate != AAUDIO_UNSPECIFIED
&& (mSampleRate < SAMPLE_RATE_HZ_MIN || mSampleRate > SAMPLE_RATE_HZ_MAX)) {
diff --git a/media/libaaudio/src/core/AAudioStreamParameters.h b/media/libaaudio/src/core/AAudioStreamParameters.h
index ce5dacd..6beb4b2 100644
--- a/media/libaaudio/src/core/AAudioStreamParameters.h
+++ b/media/libaaudio/src/core/AAudioStreamParameters.h
@@ -56,11 +56,11 @@
mSamplesPerFrame = samplesPerFrame;
}
- aaudio_format_t getFormat() const {
+ audio_format_t getFormat() const {
return mAudioFormat;
}
- void setFormat(aaudio_format_t audioFormat) {
+ void setFormat(audio_format_t audioFormat) {
mAudioFormat = audioFormat;
}
@@ -120,8 +120,11 @@
mSessionId = sessionId;
}
+ /**
+ * @return bytes per frame of getFormat()
+ */
int32_t calculateBytesPerFrame() const {
- return getSamplesPerFrame() * AAudioConvert_formatToSizeInBytes(getFormat());
+ return getSamplesPerFrame() * audio_bytes_per_sample(getFormat());
}
/**
@@ -139,7 +142,7 @@
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;
+ audio_format_t mAudioFormat = AUDIO_FORMAT_DEFAULT;
aaudio_direction_t mDirection = AAUDIO_DIRECTION_OUTPUT;
aaudio_usage_t mUsage = AAUDIO_UNSPECIFIED;
aaudio_content_type_t mContentType = AAUDIO_UNSPECIFIED;
diff --git a/media/libaaudio/src/core/AudioStream.h b/media/libaaudio/src/core/AudioStream.h
index 31b895c..60200b2 100644
--- a/media/libaaudio/src/core/AudioStream.h
+++ b/media/libaaudio/src/core/AudioStream.h
@@ -192,7 +192,7 @@
return mSampleRate;
}
- aaudio_format_t getFormat() const {
+ audio_format_t getFormat() const {
return mFormat;
}
@@ -249,21 +249,14 @@
* This is only valid after setFormat() has been called.
*/
int32_t getBytesPerSample() const {
- return AAudioConvert_formatToSizeInBytes(mFormat);
+ return audio_bytes_per_sample(mFormat);
}
/**
* This is only valid after setSamplesPerFrame() and setDeviceFormat() have been called.
*/
int32_t getBytesPerDeviceFrame() const {
- return mSamplesPerFrame * getBytesPerDeviceSample();
- }
-
- /**
- * This is only valid after setDeviceFormat() has been called.
- */
- int32_t getBytesPerDeviceSample() const {
- return AAudioConvert_formatToSizeInBytes(getDeviceFormat());
+ return getSamplesPerFrame() * audio_bytes_per_sample(getDeviceFormat());
}
virtual int64_t getFramesWritten() = 0;
@@ -478,18 +471,18 @@
/**
* This should not be called after the open() call.
*/
- void setFormat(aaudio_format_t format) {
+ void setFormat(audio_format_t format) {
mFormat = format;
}
/**
* This should not be called after the open() call.
*/
- void setDeviceFormat(aaudio_format_t format) {
+ void setDeviceFormat(audio_format_t format) {
mDeviceFormat = format;
}
- aaudio_format_t getDeviceFormat() const {
+ audio_format_t getDeviceFormat() const {
return mDeviceFormat;
}
@@ -565,7 +558,7 @@
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;
+ audio_format_t mFormat = AUDIO_FORMAT_DEFAULT;
aaudio_stream_state_t mState = AAUDIO_STREAM_STATE_UNINITIALIZED;
aaudio_performance_mode_t mPerformanceMode = AAUDIO_PERFORMANCE_MODE_NONE;
@@ -577,7 +570,7 @@
// Sometimes the hardware is operating with a different format from the app.
// Then we require conversion in AAudio.
- aaudio_format_t mDeviceFormat = AAUDIO_FORMAT_UNSPECIFIED;
+ audio_format_t mDeviceFormat = AUDIO_FORMAT_INVALID;
// callback ----------------------------------
diff --git a/media/libaaudio/src/fifo/FifoControllerBase.cpp b/media/libaaudio/src/fifo/FifoControllerBase.cpp
index 14a2be1..9885cb0 100644
--- a/media/libaaudio/src/fifo/FifoControllerBase.cpp
+++ b/media/libaaudio/src/fifo/FifoControllerBase.cpp
@@ -59,5 +59,10 @@
}
void FifoControllerBase::setThreshold(fifo_frames_t threshold) {
+ if (threshold > mCapacity) {
+ threshold = mCapacity;
+ } else if (threshold < 0) {
+ threshold = 0;
+ }
mThreshold = threshold;
}
diff --git a/media/libaaudio/src/fifo/FifoControllerBase.h b/media/libaaudio/src/fifo/FifoControllerBase.h
index 64af777..1edb8a3 100644
--- a/media/libaaudio/src/fifo/FifoControllerBase.h
+++ b/media/libaaudio/src/fifo/FifoControllerBase.h
@@ -102,6 +102,9 @@
/**
* You can request that the buffer not be filled above a maximum
* number of frames.
+ *
+ * The threshold will be clipped between zero and the buffer capacity.
+ *
* @param threshold effective size of the buffer
*/
void setThreshold(fifo_frames_t threshold);
diff --git a/media/libaaudio/src/flowgraph/AudioProcessorBase.cpp b/media/libaaudio/src/flowgraph/AudioProcessorBase.cpp
new file mode 100644
index 0000000..5667fdb
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/AudioProcessorBase.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+#include <sys/types.h>
+#include "AudioProcessorBase.h"
+
+using namespace flowgraph;
+
+/***************************************************************************/
+int32_t AudioProcessorBase::pullData(int64_t framePosition, int32_t numFrames) {
+ if (framePosition > mLastFramePosition) {
+ mLastFramePosition = framePosition;
+ mFramesValid = onProcess(framePosition, numFrames);
+ }
+ return mFramesValid;
+}
+
+/***************************************************************************/
+AudioFloatBlockPort::AudioFloatBlockPort(AudioProcessorBase &parent,
+ int32_t samplesPerFrame,
+ int32_t framesPerBlock)
+ : AudioPort(parent, samplesPerFrame)
+ , mFramesPerBlock(framesPerBlock)
+ , mSampleBlock(NULL) {
+ int32_t numFloats = framesPerBlock * getSamplesPerFrame();
+ mSampleBlock = new float[numFloats]{0.0f};
+}
+
+AudioFloatBlockPort::~AudioFloatBlockPort() {
+ delete[] mSampleBlock;
+}
+
+/***************************************************************************/
+int32_t AudioFloatOutputPort::pullData(int64_t framePosition, int32_t numFrames) {
+ numFrames = std::min(getFramesPerBlock(), numFrames);
+ return mParent.pullData(framePosition, numFrames);
+}
+
+// These need to be in the .cpp file because of forward cross references.
+void AudioFloatOutputPort::connect(AudioFloatInputPort *port) {
+ port->connect(this);
+}
+
+void AudioFloatOutputPort::disconnect(AudioFloatInputPort *port) {
+ port->disconnect(this);
+}
+
+/***************************************************************************/
+int32_t AudioFloatInputPort::pullData(int64_t framePosition, int32_t numFrames) {
+ return (mConnected == NULL)
+ ? std::min(getFramesPerBlock(), numFrames)
+ : mConnected->pullData(framePosition, numFrames);
+}
+
+float *AudioFloatInputPort::getBlock() {
+ if (mConnected == NULL) {
+ return AudioFloatBlockPort::getBlock(); // loaded using setValue()
+ } else {
+ return mConnected->getBlock();
+ }
+}
+
+/***************************************************************************/
+int32_t AudioSink::pull(int32_t numFrames) {
+ int32_t actualFrames = input.pullData(mFramePosition, numFrames);
+ mFramePosition += actualFrames;
+ return actualFrames;
+}
\ No newline at end of file
diff --git a/media/libaaudio/src/flowgraph/AudioProcessorBase.h b/media/libaaudio/src/flowgraph/AudioProcessorBase.h
new file mode 100644
index 0000000..eda46ae
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/AudioProcessorBase.h
@@ -0,0 +1,293 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * AudioProcessorBase.h
+ *
+ * Audio processing node and ports that can be used in a simple data flow graph.
+ */
+
+#ifndef FLOWGRAPH_AUDIO_PROCESSOR_BASE_H
+#define FLOWGRAPH_AUDIO_PROCESSOR_BASE_H
+
+#include <cassert>
+#include <cstring>
+#include <math.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+// TODO consider publishing all header files under "include/libaaudio/FlowGraph.h"
+
+namespace flowgraph {
+
+// Default block size that can be overridden when the AudioFloatBlockPort is created.
+// If it is too small then we will have too much overhead from switching between nodes.
+// If it is too high then we will thrash the caches.
+constexpr int kDefaultBlockSize = 8; // arbitrary
+
+class AudioFloatInputPort;
+
+/***************************************************************************/
+class AudioProcessorBase {
+public:
+ virtual ~AudioProcessorBase() = default;
+
+ /**
+ * Perform custom function.
+ *
+ * @param framePosition index of first frame to be processed
+ * @param numFrames maximum number of frames requested for processing
+ * @return number of frames actually processed
+ */
+ virtual int32_t onProcess(int64_t framePosition, int32_t numFrames) = 0;
+
+ /**
+ * If the framePosition is at or after the last frame position then call onProcess().
+ * This prevents infinite recursion in case of cyclic graphs.
+ * It also prevents nodes upstream from a branch from being executed twice.
+ *
+ * @param framePosition
+ * @param numFrames
+ * @return
+ */
+ int32_t pullData(int64_t framePosition, int32_t numFrames);
+
+protected:
+ int64_t mLastFramePosition = -1; // Start at -1 so that the first pull works.
+
+private:
+ int32_t mFramesValid = 0; // num valid frames in the block
+};
+
+/***************************************************************************/
+/**
+ * This is a connector that allows data to flow between modules.
+ */
+class AudioPort {
+public:
+ AudioPort(AudioProcessorBase &parent, int32_t samplesPerFrame)
+ : mParent(parent)
+ , mSamplesPerFrame(samplesPerFrame) {
+ }
+
+ // Ports are often declared public. So let's make them non-copyable.
+ AudioPort(const AudioPort&) = delete;
+ AudioPort& operator=(const AudioPort&) = delete;
+
+ int32_t getSamplesPerFrame() const {
+ return mSamplesPerFrame;
+ }
+
+protected:
+ AudioProcessorBase &mParent;
+
+private:
+ const int32_t mSamplesPerFrame = 1;
+};
+
+/***************************************************************************/
+/**
+ * This port contains a float type buffer.
+ * The size is framesPerBlock * samplesPerFrame).
+ */
+class AudioFloatBlockPort : public AudioPort {
+public:
+ AudioFloatBlockPort(AudioProcessorBase &mParent,
+ int32_t samplesPerFrame,
+ int32_t framesPerBlock = kDefaultBlockSize
+ );
+
+ virtual ~AudioFloatBlockPort();
+
+ int32_t getFramesPerBlock() const {
+ return mFramesPerBlock;
+ }
+
+protected:
+
+ /**
+ * @return buffer internal to the port or from a connected port
+ */
+ virtual float *getBlock() {
+ return mSampleBlock;
+ }
+
+
+private:
+ const int32_t mFramesPerBlock = 1;
+ float *mSampleBlock = nullptr; // allocated in constructor
+};
+
+/***************************************************************************/
+/**
+ * The results of a module are stored in the buffer of the output ports.
+ */
+class AudioFloatOutputPort : public AudioFloatBlockPort {
+public:
+ AudioFloatOutputPort(AudioProcessorBase &parent, int32_t samplesPerFrame)
+ : AudioFloatBlockPort(parent, samplesPerFrame) {
+ }
+
+ virtual ~AudioFloatOutputPort() = default;
+
+ using AudioFloatBlockPort::getBlock;
+
+ /**
+ * Call the parent module's onProcess() method.
+ * That may pull data from its inputs and recursively
+ * process the entire graph.
+ * @return number of frames actually pulled
+ */
+ int32_t pullData(int64_t framePosition, int32_t numFrames);
+
+ /**
+ * Connect to the input of another module.
+ * An input port can only have one connection.
+ * An output port can have multiple connections.
+ * If you connect a second output port to an input port
+ * then it overwrites the previous connection.
+ *
+ * This not thread safe. Do not modify the graph topology form another thread while running.
+ */
+ void connect(AudioFloatInputPort *port);
+
+ /**
+ * Disconnect from the input of another module.
+ * This not thread safe.
+ */
+ void disconnect(AudioFloatInputPort *port);
+};
+
+/***************************************************************************/
+class AudioFloatInputPort : public AudioFloatBlockPort {
+public:
+ AudioFloatInputPort(AudioProcessorBase &parent, int32_t samplesPerFrame)
+ : AudioFloatBlockPort(parent, samplesPerFrame) {
+ }
+
+ virtual ~AudioFloatInputPort() = default;
+
+ /**
+ * If connected to an output port then this will return
+ * that output ports buffers.
+ * If not connected then it returns the input ports own buffer
+ * which can be loaded using setValue().
+ */
+ float *getBlock() override;
+
+ /**
+ * Pull data from any output port that is connected.
+ */
+ int32_t pullData(int64_t framePosition, int32_t numFrames);
+
+ /**
+ * Write every value of the float buffer.
+ * This value will be ignored if an output port is connected
+ * to this port.
+ */
+ void setValue(float value) {
+ int numFloats = kDefaultBlockSize * getSamplesPerFrame();
+ float *buffer = getBlock();
+ for (int i = 0; i < numFloats; i++) {
+ *buffer++ = value;
+ }
+ }
+
+ /**
+ * Connect to the output of another module.
+ * An input port can only have one connection.
+ * An output port can have multiple connections.
+ * This not thread safe.
+ */
+ void connect(AudioFloatOutputPort *port) {
+ assert(getSamplesPerFrame() == port->getSamplesPerFrame());
+ mConnected = port;
+ }
+
+ void disconnect(AudioFloatOutputPort *port) {
+ assert(mConnected == port);
+ (void) port;
+ mConnected = nullptr;
+ }
+
+ void disconnect() {
+ mConnected = nullptr;
+ }
+
+private:
+ AudioFloatOutputPort *mConnected = nullptr;
+};
+
+/***************************************************************************/
+class AudioSource : public AudioProcessorBase {
+public:
+ explicit AudioSource(int32_t channelCount)
+ : output(*this, channelCount) {
+ }
+
+ virtual ~AudioSource() = default;
+
+ AudioFloatOutputPort output;
+
+ void setData(const void *data, int32_t numFrames) {
+ mData = data;
+ mSizeInFrames = numFrames;
+ mFrameIndex = 0;
+ }
+
+protected:
+ const void *mData = nullptr;
+ int32_t mSizeInFrames = 0; // number of frames in mData
+ int32_t mFrameIndex = 0; // index of next frame to be processed
+};
+
+/***************************************************************************/
+class AudioSink : public AudioProcessorBase {
+public:
+ explicit AudioSink(int32_t channelCount)
+ : input(*this, channelCount) {
+ }
+
+ virtual ~AudioSink() = default;
+
+ AudioFloatInputPort input;
+
+ /**
+ * Dummy processor. The work happens in the read() method.
+ *
+ * @param framePosition index of first frame to be processed
+ * @param numFrames
+ * @return number of frames actually processed
+ */
+ int32_t onProcess(int64_t framePosition, int32_t numFrames) override {
+ (void) framePosition;
+ (void) numFrames;
+ return 0;
+ };
+
+ virtual int32_t read(void *data, int32_t numFrames) = 0;
+
+protected:
+ int32_t pull(int32_t numFrames);
+
+private:
+ int64_t mFramePosition = 0;
+};
+
+} /* namespace flowgraph */
+
+#endif /* FLOWGRAPH_AUDIO_PROCESSOR_BASE_H */
diff --git a/media/libaaudio/src/flowgraph/ClipToRange.cpp b/media/libaaudio/src/flowgraph/ClipToRange.cpp
new file mode 100644
index 0000000..bd9c22a
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/ClipToRange.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+#include <unistd.h>
+#include "AudioProcessorBase.h"
+#include "ClipToRange.h"
+
+using namespace flowgraph;
+
+ClipToRange::ClipToRange(int32_t channelCount)
+ : input(*this, channelCount)
+ , output(*this, channelCount) {
+}
+
+int32_t ClipToRange::onProcess(int64_t framePosition, int32_t numFrames) {
+ int32_t framesToProcess = input.pullData(framePosition, numFrames);
+ const float *inputBuffer = input.getBlock();
+ float *outputBuffer = output.getBlock();
+
+ int32_t numSamples = framesToProcess * output.getSamplesPerFrame();
+ for (int32_t i = 0; i < numSamples; i++) {
+ *outputBuffer++ = std::min(mMaximum, std::max(mMinimum, *inputBuffer++));
+ }
+
+ return framesToProcess;
+}
diff --git a/media/libaaudio/src/flowgraph/ClipToRange.h b/media/libaaudio/src/flowgraph/ClipToRange.h
new file mode 100644
index 0000000..9eef254
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/ClipToRange.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FLOWGRAPH_CLIP_TO_RANGE_H
+#define FLOWGRAPH_CLIP_TO_RANGE_H
+
+#include <atomic>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "AudioProcessorBase.h"
+
+namespace flowgraph {
+
+// This is 3 dB, (10^(3/20)), to match the maximum headroom in AudioTrack for float data.
+// It is designed to allow occasional transient peaks.
+constexpr float kDefaultMaxHeadroom = 1.41253754f;
+constexpr float kDefaultMinHeadroom = -kDefaultMaxHeadroom;
+
+class ClipToRange : public AudioProcessorBase {
+public:
+ explicit ClipToRange(int32_t channelCount);
+
+ virtual ~ClipToRange() = default;
+
+ int32_t onProcess(int64_t framePosition, int32_t numFrames) override;
+
+ void setMinimum(float min) {
+ mMinimum = min;
+ }
+
+ float getMinimum() const {
+ return mMinimum;
+ }
+
+ void setMaximum(float min) {
+ mMaximum = min;
+ }
+
+ float getMaximum() const {
+ return mMaximum;
+ }
+
+ AudioFloatInputPort input;
+ AudioFloatOutputPort output;
+
+private:
+ float mMinimum = kDefaultMinHeadroom;
+ float mMaximum = kDefaultMaxHeadroom;
+};
+
+} /* namespace flowgraph */
+
+#endif //FLOWGRAPH_CLIP_TO_RANGE_H
diff --git a/media/libaaudio/src/flowgraph/MonoToMultiConverter.cpp b/media/libaaudio/src/flowgraph/MonoToMultiConverter.cpp
new file mode 100644
index 0000000..78aad52
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/MonoToMultiConverter.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include <unistd.h>
+#include "AudioProcessorBase.h"
+#include "MonoToMultiConverter.h"
+
+using namespace flowgraph;
+
+MonoToMultiConverter::MonoToMultiConverter(int32_t channelCount)
+ : input(*this, 1)
+ , output(*this, channelCount) {
+}
+
+MonoToMultiConverter::~MonoToMultiConverter() { }
+
+int32_t MonoToMultiConverter::onProcess(int64_t framePosition, int32_t numFrames) {
+ int32_t framesToProcess = input.pullData(framePosition, numFrames);
+
+ const float *inputBuffer = input.getBlock();
+ float *outputBuffer = output.getBlock();
+ int32_t channelCount = output.getSamplesPerFrame();
+ // TODO maybe move to audio_util as audio_mono_to_multi()
+ for (int i = 0; i < framesToProcess; i++) {
+ // read one, write many
+ float sample = *inputBuffer++;
+ for (int channel = 0; channel < channelCount; channel++) {
+ *outputBuffer++ = sample;
+ }
+ }
+ return framesToProcess;
+}
+
diff --git a/media/libaaudio/src/flowgraph/MonoToMultiConverter.h b/media/libaaudio/src/flowgraph/MonoToMultiConverter.h
new file mode 100644
index 0000000..34d53c7
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/MonoToMultiConverter.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef FLOWGRAPH_MONO_TO_MULTI_CONVERTER_H
+#define FLOWGRAPH_MONO_TO_MULTI_CONVERTER_H
+
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "AudioProcessorBase.h"
+
+namespace flowgraph {
+
+class MonoToMultiConverter : public AudioProcessorBase {
+public:
+ explicit MonoToMultiConverter(int32_t channelCount);
+
+ virtual ~MonoToMultiConverter();
+
+ int32_t onProcess(int64_t framePosition, int32_t numFrames) override;
+
+ AudioFloatInputPort input;
+ AudioFloatOutputPort output;
+};
+
+} /* namespace flowgraph */
+
+#endif //FLOWGRAPH_MONO_TO_MULTI_CONVERTER_H
diff --git a/media/libaaudio/src/flowgraph/RampLinear.cpp b/media/libaaudio/src/flowgraph/RampLinear.cpp
new file mode 100644
index 0000000..a260828
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/RampLinear.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "RampLinear"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <algorithm>
+#include <unistd.h>
+#include "AudioProcessorBase.h"
+#include "RampLinear.h"
+
+using namespace flowgraph;
+
+RampLinear::RampLinear(int32_t channelCount)
+ : input(*this, channelCount)
+ , output(*this, channelCount) {
+ mTarget.store(1.0f);
+}
+
+void RampLinear::setLengthInFrames(int32_t frames) {
+ mLengthInFrames = frames;
+}
+
+void RampLinear::setTarget(float target) {
+ mTarget.store(target);
+}
+
+float RampLinear::interpolateCurrent() {
+ return mLevelTo - (mRemaining * mScaler);
+}
+
+int32_t RampLinear::onProcess(int64_t framePosition, int32_t numFrames) {
+ int32_t framesToProcess = input.pullData(framePosition, numFrames);
+ const float *inputBuffer = input.getBlock();
+ float *outputBuffer = output.getBlock();
+ int32_t channelCount = output.getSamplesPerFrame();
+
+ float target = getTarget();
+ if (target != mLevelTo) {
+ // Start new ramp. Continue from previous level.
+ mLevelFrom = interpolateCurrent();
+ mLevelTo = target;
+ mRemaining = mLengthInFrames;
+ ALOGV("%s() mLevelFrom = %f, mLevelTo = %f, mRemaining = %d, mScaler = %f",
+ __func__, mLevelFrom, mLevelTo, mRemaining, mScaler);
+ mScaler = (mLevelTo - mLevelFrom) / mLengthInFrames; // for interpolation
+ }
+
+ int32_t framesLeft = framesToProcess;
+
+ if (mRemaining > 0) { // Ramping? This doesn't happen very often.
+ int32_t framesToRamp = std::min(framesLeft, mRemaining);
+ framesLeft -= framesToRamp;
+ while (framesToRamp > 0) {
+ float currentLevel = interpolateCurrent();
+ for (int ch = 0; ch < channelCount; ch++) {
+ *outputBuffer++ = *inputBuffer++ * currentLevel;
+ }
+ mRemaining--;
+ framesToRamp--;
+ }
+ }
+
+ // Process any frames after the ramp.
+ int32_t samplesLeft = framesLeft * channelCount;
+ for (int i = 0; i < samplesLeft; i++) {
+ *outputBuffer++ = *inputBuffer++ * mLevelTo;
+ }
+
+ return framesToProcess;
+}
diff --git a/media/libaaudio/src/flowgraph/RampLinear.h b/media/libaaudio/src/flowgraph/RampLinear.h
new file mode 100644
index 0000000..bdc8f41
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/RampLinear.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FLOWGRAPH_RAMP_LINEAR_H
+#define FLOWGRAPH_RAMP_LINEAR_H
+
+#include <atomic>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "AudioProcessorBase.h"
+
+namespace flowgraph {
+
+class RampLinear : public AudioProcessorBase {
+public:
+ explicit RampLinear(int32_t channelCount);
+
+ virtual ~RampLinear() = default;
+
+ int32_t onProcess(int64_t framePosition, int32_t numFrames) override;
+
+ /**
+ * This is used for the next ramp.
+ * Calling this does not affect a ramp that is in progress.
+ */
+ void setLengthInFrames(int32_t frames);
+
+ int32_t getLengthInFrames() const {
+ return mLengthInFrames;
+ }
+
+ /**
+ * This may be safely called by another thread.
+ * @param target
+ */
+ void setTarget(float target);
+
+ float getTarget() const {
+ return mTarget.load();
+ }
+
+ /**
+ * Force the nextSegment to start from this level.
+ *
+ * WARNING: this can cause a discontinuity if called while the ramp is being used.
+ * Only call this when setting the initial ramp.
+ *
+ * @param level
+ */
+ void forceCurrent(float level) {
+ mLevelFrom = level;
+ mLevelTo = level;
+ }
+
+ AudioFloatInputPort input;
+ AudioFloatOutputPort output;
+
+private:
+
+ float interpolateCurrent();
+
+ std::atomic<float> mTarget;
+
+ int32_t mLengthInFrames = 48000.0f / 100.0f ; // 10 msec at 48000 Hz;
+ int32_t mRemaining = 0;
+ float mScaler = 0.0f;
+ float mLevelFrom = 0.0f;
+ float mLevelTo = 0.0f;
+};
+
+} /* namespace flowgraph */
+
+#endif //FLOWGRAPH_RAMP_LINEAR_H
diff --git a/media/libaaudio/src/flowgraph/SinkFloat.cpp b/media/libaaudio/src/flowgraph/SinkFloat.cpp
new file mode 100644
index 0000000..fb3dcbc
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SinkFloat.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+#include <unistd.h>
+#include "AudioProcessorBase.h"
+#include "SinkFloat.h"
+
+using namespace flowgraph;
+
+SinkFloat::SinkFloat(int32_t channelCount)
+ : AudioSink(channelCount) {
+}
+
+int32_t SinkFloat::read(void *data, int32_t numFrames) {
+ float *floatData = (float *) data;
+ int32_t channelCount = input.getSamplesPerFrame();
+
+ int32_t framesLeft = numFrames;
+ while (framesLeft > 0) {
+ // Run the graph and pull data through the input port.
+ int32_t framesRead = pull(framesLeft);
+ if (framesRead <= 0) {
+ break;
+ }
+ const float *signal = input.getBlock();
+ int32_t numSamples = framesRead * channelCount;
+ memcpy(floatData, signal, numSamples * sizeof(float));
+ floatData += numSamples;
+ framesLeft -= framesRead;
+ }
+ return numFrames - framesLeft;
+}
diff --git a/media/libaaudio/src/flowgraph/SinkFloat.h b/media/libaaudio/src/flowgraph/SinkFloat.h
new file mode 100644
index 0000000..7775c08
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SinkFloat.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2018 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 FLOWGRAPH_SINK_FLOAT_H
+#define FLOWGRAPH_SINK_FLOAT_H
+
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "AudioProcessorBase.h"
+
+namespace flowgraph {
+
+class SinkFloat : public AudioSink {
+public:
+ explicit SinkFloat(int32_t channelCount);
+
+ int32_t read(void *data, int32_t numFrames) override;
+
+};
+
+} /* namespace flowgraph */
+
+#endif //FLOWGRAPH_SINK_FLOAT_H
diff --git a/media/libaaudio/src/flowgraph/SinkI16.cpp b/media/libaaudio/src/flowgraph/SinkI16.cpp
new file mode 100644
index 0000000..ffec8f5
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SinkI16.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+#include <unistd.h>
+
+#ifdef __ANDROID__
+#include <audio_utils/primitives.h>
+#endif
+
+#include "AudioProcessorBase.h"
+#include "SinkI16.h"
+
+using namespace flowgraph;
+
+SinkI16::SinkI16(int32_t channelCount)
+ : AudioSink(channelCount) {}
+
+int32_t SinkI16::read(void *data, int32_t numFrames) {
+ int16_t *shortData = (int16_t *) data;
+ const int32_t channelCount = input.getSamplesPerFrame();
+
+ int32_t framesLeft = numFrames;
+ while (framesLeft > 0) {
+ // Run the graph and pull data through the input port.
+ int32_t framesRead = pull(framesLeft);
+ if (framesRead <= 0) {
+ break;
+ }
+ const float *signal = input.getBlock();
+ int32_t numSamples = framesRead * channelCount;
+#ifdef __ANDROID__
+ memcpy_to_i16_from_float(shortData, signal, numSamples);
+ shortData += numSamples;
+ signal += numSamples;
+#else
+ for (int i = 0; i < numSamples; i++) {
+ int32_t n = (int32_t) (*signal++ * 32768.0f);
+ *shortData++ = std::min(INT16_MAX, std::max(INT16_MIN, n)); // clip
+ }
+#endif
+ framesLeft -= framesRead;
+ }
+ return numFrames - framesLeft;
+}
diff --git a/media/libaaudio/src/flowgraph/SinkI16.h b/media/libaaudio/src/flowgraph/SinkI16.h
new file mode 100644
index 0000000..6d86266
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SinkI16.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2018 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 FLOWGRAPH_SINK_I16_H
+#define FLOWGRAPH_SINK_I16_H
+
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "AudioProcessorBase.h"
+
+namespace flowgraph {
+
+class SinkI16 : public AudioSink {
+public:
+ explicit SinkI16(int32_t channelCount);
+
+ int32_t read(void *data, int32_t numFrames) override;
+};
+
+} /* namespace flowgraph */
+
+#endif //FLOWGRAPH_SINK_I16_H
diff --git a/media/libaaudio/src/flowgraph/SinkI24.cpp b/media/libaaudio/src/flowgraph/SinkI24.cpp
new file mode 100644
index 0000000..6592828
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SinkI24.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+#include <unistd.h>
+
+#ifdef __ANDROID__
+#include <audio_utils/primitives.h>
+#endif
+
+#include "AudioProcessorBase.h"
+#include "SinkI24.h"
+
+using namespace flowgraph;
+
+
+SinkI24::SinkI24(int32_t channelCount)
+ : AudioSink(channelCount) {}
+
+int32_t SinkI24::read(void *data, int32_t numFrames) {
+ uint8_t *byteData = (uint8_t *) data;
+ const int32_t channelCount = input.getSamplesPerFrame();
+
+ int32_t framesLeft = numFrames;
+ while (framesLeft > 0) {
+ // Run the graph and pull data through the input port.
+ int32_t framesRead = pull(framesLeft);
+ if (framesRead <= 0) {
+ break;
+ }
+ const float *floatData = input.getBlock();
+ int32_t numSamples = framesRead * channelCount;
+#ifdef __ANDROID__
+ memcpy_to_p24_from_float(byteData, floatData, numSamples);
+ static const int kBytesPerI24Packed = 3;
+ byteData += numSamples * kBytesPerI24Packed;
+ floatData += numSamples;
+#else
+ const int32_t kI24PackedMax = 0x007FFFFF;
+ const int32_t kI24PackedMin = 0xFF800000;
+ for (int i = 0; i < numSamples; i++) {
+ int32_t n = (int32_t) (*floatData++ * 0x00800000);
+ n = std::min(kI24PackedMax, std::max(kI24PackedMin, n)); // clip
+ // Write as a packed 24-bit integer in Little Endian format.
+ *byteData++ = (uint8_t) n;
+ *byteData++ = (uint8_t) (n >> 8);
+ *byteData++ = (uint8_t) (n >> 16);
+ }
+#endif
+ framesLeft -= framesRead;
+ }
+ return numFrames - framesLeft;
+}
diff --git a/media/libaaudio/src/flowgraph/SinkI24.h b/media/libaaudio/src/flowgraph/SinkI24.h
new file mode 100644
index 0000000..5b9b505
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SinkI24.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2018 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 FLOWGRAPH_SINK_I24_H
+#define FLOWGRAPH_SINK_I24_H
+
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "AudioProcessorBase.h"
+
+namespace flowgraph {
+
+class SinkI24 : public AudioSink {
+public:
+ explicit SinkI24(int32_t channelCount);
+
+ int32_t read(void *data, int32_t numFrames) override;
+};
+
+} /* namespace flowgraph */
+
+#endif //FLOWGRAPH_SINK_I24_H
diff --git a/media/libaaudio/src/flowgraph/SourceFloat.cpp b/media/libaaudio/src/flowgraph/SourceFloat.cpp
new file mode 100644
index 0000000..4bb674f
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SourceFloat.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+#include <unistd.h>
+#include "AudioProcessorBase.h"
+#include "SourceFloat.h"
+
+using namespace flowgraph;
+
+SourceFloat::SourceFloat(int32_t channelCount)
+ : AudioSource(channelCount) {
+}
+
+int32_t SourceFloat::onProcess(int64_t framePosition, int32_t numFrames) {
+
+ float *outputBuffer = output.getBlock();
+ int32_t channelCount = output.getSamplesPerFrame();
+
+ int32_t framesLeft = mSizeInFrames - mFrameIndex;
+ int32_t framesToProcess = std::min(numFrames, framesLeft);
+ int32_t numSamples = framesToProcess * channelCount;
+
+ const float *floatBase = (float *) mData;
+ const float *floatData = &floatBase[mFrameIndex * channelCount];
+ memcpy(outputBuffer, floatData, numSamples * sizeof(float));
+ mFrameIndex += framesToProcess;
+ return framesToProcess;
+}
+
diff --git a/media/libaaudio/src/flowgraph/SourceFloat.h b/media/libaaudio/src/flowgraph/SourceFloat.h
new file mode 100644
index 0000000..e6eed9f
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SourceFloat.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2018 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 FLOWGRAPH_SOURCE_FLOAT_H
+#define FLOWGRAPH_SOURCE_FLOAT_H
+
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "AudioProcessorBase.h"
+
+namespace flowgraph {
+
+class SourceFloat : public AudioSource {
+public:
+ explicit SourceFloat(int32_t channelCount);
+
+ int32_t onProcess(int64_t framePosition, int32_t numFrames) override;
+};
+
+} /* namespace flowgraph */
+
+#endif //FLOWGRAPH_SOURCE_FLOAT_H
diff --git a/media/libaaudio/src/flowgraph/SourceI16.cpp b/media/libaaudio/src/flowgraph/SourceI16.cpp
new file mode 100644
index 0000000..c3fcec2
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SourceI16.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+#include <unistd.h>
+
+#ifdef __ANDROID__
+#include <audio_utils/primitives.h>
+#endif
+
+#include "AudioProcessorBase.h"
+#include "SourceI16.h"
+
+using namespace flowgraph;
+
+SourceI16::SourceI16(int32_t channelCount)
+ : AudioSource(channelCount) {
+}
+
+int32_t SourceI16::onProcess(int64_t framePosition, int32_t numFrames) {
+ float *floatData = output.getBlock();
+ int32_t channelCount = output.getSamplesPerFrame();
+
+ int32_t framesLeft = mSizeInFrames - mFrameIndex;
+ int32_t framesToProcess = std::min(numFrames, framesLeft);
+ int32_t numSamples = framesToProcess * channelCount;
+
+ const int16_t *shortBase = static_cast<const int16_t *>(mData);
+ const int16_t *shortData = &shortBase[mFrameIndex * channelCount];
+
+#ifdef __ANDROID__
+ memcpy_to_float_from_i16(floatData, shortData, numSamples);
+#else
+ for (int i = 0; i < numSamples; i++) {
+ *floatData++ = *shortData++ * (1.0f / 32768);
+ }
+#endif
+
+ mFrameIndex += framesToProcess;
+ return framesToProcess;
+}
\ No newline at end of file
diff --git a/media/libaaudio/src/flowgraph/SourceI16.h b/media/libaaudio/src/flowgraph/SourceI16.h
new file mode 100644
index 0000000..2b116cf
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SourceI16.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2018 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 FLOWGRAPH_SOURCE_I16_H
+#define FLOWGRAPH_SOURCE_I16_H
+
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "AudioProcessorBase.h"
+
+namespace flowgraph {
+
+class SourceI16 : public AudioSource {
+public:
+ explicit SourceI16(int32_t channelCount);
+
+ int32_t onProcess(int64_t framePosition, int32_t numFrames) override;
+};
+
+} /* namespace flowgraph */
+
+#endif //FLOWGRAPH_SOURCE_I16_H
diff --git a/media/libaaudio/src/flowgraph/SourceI24.cpp b/media/libaaudio/src/flowgraph/SourceI24.cpp
new file mode 100644
index 0000000..f319880
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SourceI24.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+#include <unistd.h>
+
+#ifdef __ANDROID__
+#include <audio_utils/primitives.h>
+#endif
+
+#include "AudioProcessorBase.h"
+#include "SourceI24.h"
+
+using namespace flowgraph;
+
+constexpr int kBytesPerI24Packed = 3;
+
+SourceI24::SourceI24(int32_t channelCount)
+ : AudioSource(channelCount) {
+}
+
+int32_t SourceI24::onProcess(int64_t framePosition, int32_t numFrames) {
+ float *floatData = output.getBlock();
+ int32_t channelCount = output.getSamplesPerFrame();
+
+ int32_t framesLeft = mSizeInFrames - mFrameIndex;
+ int32_t framesToProcess = std::min(numFrames, framesLeft);
+ int32_t numSamples = framesToProcess * channelCount;
+
+ const uint8_t *byteBase = (uint8_t *) mData;
+ const uint8_t *byteData = &byteBase[mFrameIndex * channelCount * kBytesPerI24Packed];
+
+#ifdef __ANDROID__
+ memcpy_to_float_from_p24(floatData, byteData, numSamples);
+#else
+ static const float scale = 1. / (float)(1UL << 31);
+ for (int i = 0; i < numSamples; i++) {
+ // Assemble the data assuming Little Endian format.
+ int32_t pad = byteData[2];
+ pad <<= 8;
+ pad |= byteData[1];
+ pad <<= 8;
+ pad |= byteData[0];
+ pad <<= 8; // Shift to 32 bit data so the sign is correct.
+ byteData += kBytesPerI24Packed;
+ *floatData++ = pad * scale; // scale to range -1.0 to 1.0
+ }
+#endif
+
+ mFrameIndex += framesToProcess;
+ return framesToProcess;
+}
\ No newline at end of file
diff --git a/media/libaaudio/src/flowgraph/SourceI24.h b/media/libaaudio/src/flowgraph/SourceI24.h
new file mode 100644
index 0000000..39f14da
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SourceI24.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2018 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 FLOWGRAPH_SOURCE_I24_H
+#define FLOWGRAPH_SOURCE_I24_H
+
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "AudioProcessorBase.h"
+
+namespace flowgraph {
+
+class SourceI24 : public AudioSource {
+public:
+ explicit SourceI24(int32_t channelCount);
+
+ int32_t onProcess(int64_t framePosition, int32_t numFrames) override;
+};
+
+} /* namespace flowgraph */
+
+#endif //FLOWGRAPH_SOURCE_I24_H
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.h b/media/libaaudio/src/legacy/AudioStreamLegacy.h
index 494edbc..8e78554 100644
--- a/media/libaaudio/src/legacy/AudioStreamLegacy.h
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.h
@@ -77,7 +77,6 @@
virtual int64_t incrementClientFrameCounter(int32_t frames) = 0;
-
virtual int64_t getFramesWritten() override {
return mFramesWritten.get();
}
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
index 505f2ee..dbf00a9 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
@@ -81,8 +81,8 @@
}
// Preserve behavior of API 26
- if (getFormat() == AAUDIO_FORMAT_UNSPECIFIED) {
- setFormat(AAUDIO_FORMAT_PCM_FLOAT);
+ if (getFormat() == AUDIO_FORMAT_DEFAULT) {
+ setFormat(AUDIO_FORMAT_PCM_FLOAT);
}
// Maybe change device format to get a FAST path.
@@ -99,12 +99,12 @@
// We just may not get a FAST track.
// But we wouldn't have anyway without this hack.
constexpr int32_t kMostLikelySampleRateForFast = 48000;
- if (getFormat() == AAUDIO_FORMAT_PCM_FLOAT
+ if (getFormat() == AUDIO_FORMAT_PCM_FLOAT
&& perfMode == AAUDIO_PERFORMANCE_MODE_LOW_LATENCY
&& (samplesPerFrame <= 2) // FAST only for mono and stereo
&& (getSampleRate() == kMostLikelySampleRateForFast
|| getSampleRate() == AAUDIO_UNSPECIFIED)) {
- setDeviceFormat(AAUDIO_FORMAT_PCM_I16);
+ setDeviceFormat(AUDIO_FORMAT_PCM_16_BIT);
} else {
setDeviceFormat(getFormat());
}
@@ -147,8 +147,7 @@
// ----------- open the AudioRecord ---------------------
// Might retry, but never more than once.
for (int i = 0; i < 2; i ++) {
- audio_format_t requestedInternalFormat =
- AAudioConvert_aaudioToAndroidDataFormat(getDeviceFormat());
+ const audio_format_t requestedInternalFormat = getDeviceFormat();
mAudioRecord = new AudioRecord(
mOpPackageName // const String16& opPackageName TODO does not compile
@@ -214,8 +213,8 @@
}
// Allocate format conversion buffer if needed.
- if (getDeviceFormat() == AAUDIO_FORMAT_PCM_I16
- && getFormat() == AAUDIO_FORMAT_PCM_FLOAT) {
+ if (getDeviceFormat() == AUDIO_FORMAT_PCM_16_BIT
+ && getFormat() == AUDIO_FORMAT_PCM_FLOAT) {
if (builder.getDataCallbackProc() != nullptr) {
// If we have a callback then we need to convert the data into an internal float
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
index 505cd77..1572f0d 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
@@ -88,9 +88,9 @@
int32_t notificationFrames = 0;
- audio_format_t format = (getFormat() == AAUDIO_FORMAT_UNSPECIFIED)
+ const audio_format_t format = (getFormat() == AUDIO_FORMAT_DEFAULT)
? AUDIO_FORMAT_PCM_FLOAT
- : AAudioConvert_aaudioToAndroidDataFormat(getFormat());
+ : getFormat();
// Setup the callback if there is one.
AudioTrack::callback_t callback = nullptr;
@@ -178,10 +178,8 @@
// Get the actual values from the AudioTrack.
setSamplesPerFrame(mAudioTrack->channelCount());
- aaudio_format_t aaudioFormat =
- AAudioConvert_androidToAAudioDataFormat(mAudioTrack->format());
- setFormat(aaudioFormat);
- setDeviceFormat(aaudioFormat);
+ setFormat(mAudioTrack->format());
+ setDeviceFormat(mAudioTrack->format());
int32_t actualSampleRate = mAudioTrack->getSampleRate();
ALOGW_IF(actualSampleRate != getSampleRate(),
diff --git a/media/libaaudio/src/utility/AAudioUtilities.cpp b/media/libaaudio/src/utility/AAudioUtilities.cpp
index 40ebb76..f5b3ad4 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.cpp
+++ b/media/libaaudio/src/utility/AAudioUtilities.cpp
@@ -33,395 +33,6 @@
using namespace android;
-// This is 3 dB, (10^(3/20)), to match the maximum headroom in AudioTrack for float data.
-// It is designed to allow occasional transient peaks.
-#define MAX_HEADROOM (1.41253754f)
-#define MIN_HEADROOM (0 - MAX_HEADROOM)
-
-int32_t AAudioConvert_formatToSizeInBytes(aaudio_format_t format) {
- int32_t size = AAUDIO_ERROR_ILLEGAL_ARGUMENT;
- switch (format) {
- case AAUDIO_FORMAT_PCM_I16:
- size = sizeof(int16_t);
- break;
- case AAUDIO_FORMAT_PCM_FLOAT:
- size = sizeof(float);
- break;
- default:
- break;
- }
- return size;
-}
-
-// TODO expose and call clamp16_from_float function in primitives.h
-static inline int16_t clamp16_from_float(float f) {
- static const float scale = 1 << 15;
- return (int16_t) roundf(fmaxf(fminf(f * scale, scale - 1.f), -scale));
-}
-
-// Clip to valid range of a float sample to prevent excessive volume.
-// By using fmin and fmax we also protect against NaN.
-static float clipToMinMaxHeadroom(float input) {
- return fmin(MAX_HEADROOM, fmax(MIN_HEADROOM, input));
-}
-
-static float clipAndClampFloatToPcm16(float sample, float scaler) {
- // Clip to valid range of a float sample to prevent excessive volume.
- sample = clipToMinMaxHeadroom(sample);
-
- // Scale and convert to a short.
- float fval = sample * scaler;
- return clamp16_from_float(fval);
-}
-
-void AAudioConvert_floatToPcm16(const float *source,
- int16_t *destination,
- int32_t numSamples,
- float amplitude) {
- const float scaler = amplitude;
- for (int i = 0; i < numSamples; i++) {
- float sample = *source++;
- *destination++ = clipAndClampFloatToPcm16(sample, scaler);
- }
-}
-
-void AAudioConvert_floatToPcm16(const float *source,
- int16_t *destination,
- int32_t numFrames,
- int32_t samplesPerFrame,
- float amplitude1,
- float amplitude2) {
- float scaler = amplitude1;
- // divide by numFrames so that we almost reach amplitude2
- float delta = (amplitude2 - amplitude1) / numFrames;
- for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
- for (int sampleIndex = 0; sampleIndex < samplesPerFrame; sampleIndex++) {
- float sample = *source++;
- *destination++ = clipAndClampFloatToPcm16(sample, scaler);
- }
- scaler += delta;
- }
-}
-
-#define SHORT_SCALE 32768
-
-void AAudioConvert_pcm16ToFloat(const int16_t *source,
- float *destination,
- int32_t numSamples,
- float amplitude) {
- const float scaler = amplitude / SHORT_SCALE;
- for (int i = 0; i < numSamples; i++) {
- destination[i] = source[i] * scaler;
- }
-}
-
-// This code assumes amplitude1 and amplitude2 are between 0.0 and 1.0
-void AAudioConvert_pcm16ToFloat(const int16_t *source,
- float *destination,
- int32_t numFrames,
- int32_t samplesPerFrame,
- float amplitude1,
- float amplitude2) {
- float scaler = amplitude1 / SHORT_SCALE;
- const float delta = (amplitude2 - amplitude1) / (SHORT_SCALE * (float) numFrames);
- for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
- for (int sampleIndex = 0; sampleIndex < samplesPerFrame; sampleIndex++) {
- *destination++ = *source++ * scaler;
- }
- scaler += delta;
- }
-}
-
-
-// This code assumes amplitude1 and amplitude2 are between 0.0 and 1.0
-void AAudio_linearRamp(const float *source,
- float *destination,
- int32_t numFrames,
- int32_t samplesPerFrame,
- float amplitude1,
- float amplitude2) {
- float scaler = amplitude1;
- const float delta = (amplitude2 - amplitude1) / numFrames;
- for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
- for (int sampleIndex = 0; sampleIndex < samplesPerFrame; sampleIndex++) {
- float sample = *source++;
- // Clip to valid range of a float sample to prevent excessive volume.
- sample = clipToMinMaxHeadroom(sample);
-
- *destination++ = sample * scaler;
- }
- scaler += delta;
- }
-}
-
-// This code assumes amplitude1 and amplitude2 are between 0.0 and 1.0
-void AAudio_linearRamp(const int16_t *source,
- int16_t *destination,
- int32_t numFrames,
- int32_t samplesPerFrame,
- float amplitude1,
- float amplitude2) {
- // Because we are converting from int16 to 1nt16, we do not have to scale by 1/32768.
- float scaler = amplitude1;
- const float delta = (amplitude2 - amplitude1) / numFrames;
- for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
- for (int sampleIndex = 0; sampleIndex < samplesPerFrame; sampleIndex++) {
- // No need to clip because int16_t range is inherently limited.
- float sample = *source++ * scaler;
- *destination++ = (int16_t) roundf(sample);
- }
- scaler += delta;
- }
-}
-
-// *************************************************************************************
-// Convert Mono To Stereo at the same time as converting format.
-void AAudioConvert_formatMonoToStereo(const float *source,
- int16_t *destination,
- int32_t numFrames,
- float amplitude) {
- const float scaler = amplitude;
- for (int i = 0; i < numFrames; i++) {
- float sample = *source++;
- int16_t sample16 = clipAndClampFloatToPcm16(sample, scaler);
- *destination++ = sample16;
- *destination++ = sample16;
- }
-}
-
-void AAudioConvert_formatMonoToStereo(const float *source,
- int16_t *destination,
- int32_t numFrames,
- float amplitude1,
- float amplitude2) {
- // divide by numFrames so that we almost reach amplitude2
- const float delta = (amplitude2 - amplitude1) / numFrames;
- for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
- const float scaler = amplitude1 + (frameIndex * delta);
- const float sample = *source++;
- int16_t sample16 = clipAndClampFloatToPcm16(sample, scaler);
- *destination++ = sample16;
- *destination++ = sample16;
- }
-}
-
-void AAudioConvert_formatMonoToStereo(const int16_t *source,
- float *destination,
- int32_t numFrames,
- float amplitude) {
- const float scaler = amplitude / SHORT_SCALE;
- for (int i = 0; i < numFrames; i++) {
- float sample = source[i] * scaler;
- *destination++ = sample;
- *destination++ = sample;
- }
-}
-
-// This code assumes amplitude1 and amplitude2 are between 0.0 and 1.0
-void AAudioConvert_formatMonoToStereo(const int16_t *source,
- float *destination,
- int32_t numFrames,
- float amplitude1,
- float amplitude2) {
- const float scaler1 = amplitude1 / SHORT_SCALE;
- const float delta = (amplitude2 - amplitude1) / (SHORT_SCALE * (float) numFrames);
- for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
- float scaler = scaler1 + (frameIndex * delta);
- float sample = source[frameIndex] * scaler;
- *destination++ = sample;
- *destination++ = sample;
- }
-}
-
-// This code assumes amplitude1 and amplitude2 are between 0.0 and 1.0
-void AAudio_linearRampMonoToStereo(const float *source,
- float *destination,
- int32_t numFrames,
- float amplitude1,
- float amplitude2) {
- const float delta = (amplitude2 - amplitude1) / numFrames;
- for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
- float sample = *source++;
-
- // Clip to valid range of a float sample to prevent excessive volume.
- sample = clipToMinMaxHeadroom(sample);
-
- const float scaler = amplitude1 + (frameIndex * delta);
- float sampleScaled = sample * scaler;
- *destination++ = sampleScaled;
- *destination++ = sampleScaled;
- }
-}
-
-// This code assumes amplitude1 and amplitude2 are between 0.0 and 1.0
-void AAudio_linearRampMonoToStereo(const int16_t *source,
- int16_t *destination,
- int32_t numFrames,
- float amplitude1,
- float amplitude2) {
- // Because we are converting from int16 to 1nt16, we do not have to scale by 1/32768.
- const float delta = (amplitude2 - amplitude1) / numFrames;
- for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
- const float scaler = amplitude1 + (frameIndex * delta);
- // No need to clip because int16_t range is inherently limited.
- const float sample = *source++ * scaler;
- int16_t sample16 = (int16_t) roundf(sample);
- *destination++ = sample16;
- *destination++ = sample16;
- }
-}
-
-// *************************************************************************************
-void AAudioDataConverter::convert(
- const FormattedData &source,
- const FormattedData &destination,
- int32_t numFrames,
- float levelFrom,
- float levelTo) {
-
- if (source.channelCount == 1 && destination.channelCount == 2) {
- convertMonoToStereo(source,
- destination,
- numFrames,
- levelFrom,
- levelTo);
- } else {
- // We only support mono to stereo conversion. Otherwise source and destination
- // must match.
- assert(source.channelCount == destination.channelCount);
- convertChannelsMatch(source,
- destination,
- numFrames,
- levelFrom,
- levelTo);
- }
-}
-
-void AAudioDataConverter::convertMonoToStereo(
- const FormattedData &source,
- const FormattedData &destination,
- int32_t numFrames,
- float levelFrom,
- float levelTo) {
-
- // The formats are validated when the stream is opened so we do not have to
- // check for illegal combinations here.
- if (source.format == AAUDIO_FORMAT_PCM_FLOAT) {
- if (destination.format == AAUDIO_FORMAT_PCM_FLOAT) {
- AAudio_linearRampMonoToStereo(
- (const float *) source.data,
- (float *) destination.data,
- numFrames,
- levelFrom,
- levelTo);
- } else if (destination.format == AAUDIO_FORMAT_PCM_I16) {
- if (levelFrom != levelTo) {
- AAudioConvert_formatMonoToStereo(
- (const float *) source.data,
- (int16_t *) destination.data,
- numFrames,
- levelFrom,
- levelTo);
- } else {
- AAudioConvert_formatMonoToStereo(
- (const float *) source.data,
- (int16_t *) destination.data,
- numFrames,
- levelTo);
- }
- }
- } else if (source.format == AAUDIO_FORMAT_PCM_I16) {
- if (destination.format == AAUDIO_FORMAT_PCM_FLOAT) {
- if (levelFrom != levelTo) {
- AAudioConvert_formatMonoToStereo(
- (const int16_t *) source.data,
- (float *) destination.data,
- numFrames,
- levelFrom,
- levelTo);
- } else {
- AAudioConvert_formatMonoToStereo(
- (const int16_t *) source.data,
- (float *) destination.data,
- numFrames,
- levelTo);
- }
- } else if (destination.format == AAUDIO_FORMAT_PCM_I16) {
- AAudio_linearRampMonoToStereo(
- (const int16_t *) source.data,
- (int16_t *) destination.data,
- numFrames,
- levelFrom,
- levelTo);
- }
- }
-}
-
-void AAudioDataConverter::convertChannelsMatch(
- const FormattedData &source,
- const FormattedData &destination,
- int32_t numFrames,
- float levelFrom,
- float levelTo) {
- const int32_t numSamples = numFrames * source.channelCount;
-
- // The formats are validated when the stream is opened so we do not have to
- // check for illegal combinations here.
- if (source.format == AAUDIO_FORMAT_PCM_FLOAT) {
- if (destination.format == AAUDIO_FORMAT_PCM_FLOAT) {
- AAudio_linearRamp(
- (const float *) source.data,
- (float *) destination.data,
- numFrames,
- source.channelCount,
- levelFrom,
- levelTo);
- } else if (destination.format == AAUDIO_FORMAT_PCM_I16) {
- if (levelFrom != levelTo) {
- AAudioConvert_floatToPcm16(
- (const float *) source.data,
- (int16_t *) destination.data,
- numFrames,
- source.channelCount,
- levelFrom,
- levelTo);
- } else {
- AAudioConvert_floatToPcm16(
- (const float *) source.data,
- (int16_t *) destination.data,
- numSamples,
- levelTo);
- }
- }
- } else if (source.format == AAUDIO_FORMAT_PCM_I16) {
- if (destination.format == AAUDIO_FORMAT_PCM_FLOAT) {
- if (levelFrom != levelTo) {
- AAudioConvert_pcm16ToFloat(
- (const int16_t *) source.data,
- (float *) destination.data,
- numFrames,
- source.channelCount,
- levelFrom,
- levelTo);
- } else {
- AAudioConvert_pcm16ToFloat(
- (const int16_t *) source.data,
- (float *) destination.data,
- numSamples,
- levelTo);
- }
- } else if (destination.format == AAUDIO_FORMAT_PCM_I16) {
- AAudio_linearRamp(
- (const int16_t *) source.data,
- (int16_t *) destination.data,
- numFrames,
- source.channelCount,
- levelFrom,
- levelTo);
- }
- }
-}
-
status_t AAudioConvert_aaudioToAndroidStatus(aaudio_result_t result) {
// This covers the case for AAUDIO_OK and for positive results.
if (result >= 0) {
@@ -513,6 +124,9 @@
audio_format_t AAudioConvert_aaudioToAndroidDataFormat(aaudio_format_t aaudioFormat) {
audio_format_t androidFormat;
switch (aaudioFormat) {
+ case AAUDIO_FORMAT_UNSPECIFIED:
+ androidFormat = AUDIO_FORMAT_DEFAULT;
+ break;
case AAUDIO_FORMAT_PCM_I16:
androidFormat = AUDIO_FORMAT_PCM_16_BIT;
break;
@@ -520,16 +134,19 @@
androidFormat = AUDIO_FORMAT_PCM_FLOAT;
break;
default:
- androidFormat = AUDIO_FORMAT_DEFAULT;
- ALOGE("AAudioConvert_aaudioToAndroidDataFormat 0x%08X unrecognized", aaudioFormat);
+ androidFormat = AUDIO_FORMAT_INVALID;
+ ALOGE("%s() 0x%08X unrecognized", __func__, aaudioFormat);
break;
}
return androidFormat;
}
aaudio_format_t AAudioConvert_androidToAAudioDataFormat(audio_format_t androidFormat) {
- aaudio_format_t aaudioFormat = AAUDIO_FORMAT_INVALID;
+ aaudio_format_t aaudioFormat;
switch (androidFormat) {
+ case AUDIO_FORMAT_DEFAULT:
+ aaudioFormat = AAUDIO_FORMAT_UNSPECIFIED;
+ break;
case AUDIO_FORMAT_PCM_16_BIT:
aaudioFormat = AAUDIO_FORMAT_PCM_I16;
break;
@@ -538,7 +155,7 @@
break;
default:
aaudioFormat = AAUDIO_FORMAT_INVALID;
- ALOGE("AAudioConvert_androidToAAudioDataFormat 0x%08X unrecognized", androidFormat);
+ ALOGE("%s() 0x%08X unrecognized", __func__, androidFormat);
break;
}
return aaudioFormat;
diff --git a/media/libaaudio/src/utility/AAudioUtilities.h b/media/libaaudio/src/utility/AAudioUtilities.h
index 4b975e8..dc2b198 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.h
+++ b/media/libaaudio/src/utility/AAudioUtilities.h
@@ -45,156 +45,6 @@
audio_session_t AAudioConvert_aaudioToAndroidSessionId(aaudio_session_id_t sessionId);
/**
- * Convert an array of floats to an array of int16_t.
- *
- * @param source
- * @param destination
- * @param numSamples number of values in the array
- * @param amplitude level between 0.0 and 1.0
- */
-void AAudioConvert_floatToPcm16(const float *source,
- int16_t *destination,
- int32_t numSamples,
- float amplitude);
-
-/**
- * Convert floats to int16_t and scale by a linear ramp.
- *
- * The ramp stops just short of reaching amplitude2 so that the next
- * ramp can start at amplitude2 without causing a discontinuity.
- *
- * @param source
- * @param destination
- * @param numFrames
- * @param samplesPerFrame AKA number of channels
- * @param amplitude1 level at start of ramp, between 0.0 and 1.0
- * @param amplitude2 level past end of ramp, between 0.0 and 1.0
- */
-void AAudioConvert_floatToPcm16(const float *source,
- int16_t *destination,
- int32_t numFrames,
- int32_t samplesPerFrame,
- float amplitude1,
- float amplitude2);
-
-/**
- * Convert int16_t array to float array ranging from -1.0 to +1.0.
- * @param source
- * @param destination
- * @param numSamples
- */
-//void AAudioConvert_pcm16ToFloat(const int16_t *source, int32_t numSamples,
-// float *destination);
-
-/**
- *
- * Convert int16_t array to float array ranging from +/- amplitude.
- * @param source
- * @param destination
- * @param numSamples
- * @param amplitude
- */
-void AAudioConvert_pcm16ToFloat(const int16_t *source,
- float *destination,
- int32_t numSamples,
- float amplitude);
-
-/**
- * Convert floats to int16_t and scale by a linear ramp.
- *
- * The ramp stops just short of reaching amplitude2 so that the next
- * ramp can start at amplitude2 without causing a discontinuity.
- *
- * @param source
- * @param destination
- * @param numFrames
- * @param samplesPerFrame AKA number of channels
- * @param amplitude1 level at start of ramp, between 0.0 and 1.0
- * @param amplitude2 level at end of ramp, between 0.0 and 1.0
- */
-void AAudioConvert_pcm16ToFloat(const int16_t *source,
- float *destination,
- int32_t numFrames,
- int32_t samplesPerFrame,
- float amplitude1,
- float amplitude2);
-
-/**
- * Scale floats by a linear ramp.
- *
- * The ramp stops just short of reaching amplitude2 so that the next
- * ramp can start at amplitude2 without causing a discontinuity.
- *
- * @param source
- * @param destination
- * @param numFrames
- * @param samplesPerFrame
- * @param amplitude1
- * @param amplitude2
- */
-void AAudio_linearRamp(const float *source,
- float *destination,
- int32_t numFrames,
- int32_t samplesPerFrame,
- float amplitude1,
- float amplitude2);
-
-/**
- * Scale int16_t's by a linear ramp.
- *
- * The ramp stops just short of reaching amplitude2 so that the next
- * ramp can start at amplitude2 without causing a discontinuity.
- *
- * @param source
- * @param destination
- * @param numFrames
- * @param samplesPerFrame
- * @param amplitude1
- * @param amplitude2
- */
-void AAudio_linearRamp(const int16_t *source,
- int16_t *destination,
- int32_t numFrames,
- int32_t samplesPerFrame,
- float amplitude1,
- float amplitude2);
-
-class AAudioDataConverter {
-public:
-
- struct FormattedData {
-
- FormattedData(void *data, aaudio_format_t format, int32_t channelCount)
- : data(data)
- , format(format)
- , channelCount(channelCount) {}
-
- const void *data = nullptr;
- const aaudio_format_t format = AAUDIO_FORMAT_UNSPECIFIED;
- const int32_t channelCount = 1;
- };
-
- static void convert(const FormattedData &source,
- const FormattedData &destination,
- int32_t numFrames,
- float levelFrom,
- float levelTo);
-
-private:
- static void convertMonoToStereo(const FormattedData &source,
- const FormattedData &destination,
- int32_t numFrames,
- float levelFrom,
- float levelTo);
-
- static void convertChannelsMatch(const FormattedData &source,
- const FormattedData &destination,
- int32_t numFrames,
- float levelFrom,
- float levelTo);
-};
-
-/**
* Calculate the number of bytes and prevent numeric overflow.
* The *sizeInBytes will be set to zero if there is an error.
*
@@ -234,12 +84,6 @@
*/
audio_source_t AAudioConvert_inputPresetToAudioSource(aaudio_input_preset_t preset);
-/**
- * @return the size of a sample of the given format in bytes or AAUDIO_ERROR_ILLEGAL_ARGUMENT
- */
-int32_t AAudioConvert_formatToSizeInBytes(aaudio_format_t format);
-
-
// Note that this code may be replaced by Settings or by some other system configuration tool.
#define AAUDIO_PROP_MMAP_POLICY "aaudio.mmap_policy"
diff --git a/media/libaaudio/src/utility/LinearRamp.cpp b/media/libaaudio/src/utility/LinearRamp.cpp
deleted file mode 100644
index 1714bbf..0000000
--- a/media/libaaudio/src/utility/LinearRamp.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "LinearRamp.h"
-
-bool LinearRamp::isRamping() {
- float target = mTarget.load();
- if (target != mLevelTo) {
- // Update target. Continue from previous level.
- mLevelTo = target;
- mRemaining = mLengthInFrames;
- return true;
- } else {
- return mRemaining > 0;
- }
-}
-
-bool LinearRamp::nextSegment(int32_t frames, float *levelFrom, float *levelTo) {
- bool ramping = isRamping();
- *levelFrom = mLevelFrom;
- if (ramping) {
- float level;
- if (frames >= mRemaining) {
- level = mLevelTo;
- mRemaining = 0;
- } else {
- // Interpolate to a point along the full ramp.
- level = mLevelFrom + (frames * (mLevelTo - mLevelFrom) / mRemaining);
- mRemaining -= frames;
- }
- mLevelFrom = level; // for next ramp
- *levelTo = level;
- } else {
- *levelTo = mLevelTo;
- }
- return ramping;
-}
\ No newline at end of file
diff --git a/media/libaaudio/src/utility/LinearRamp.h b/media/libaaudio/src/utility/LinearRamp.h
deleted file mode 100644
index 2b1b8e0..0000000
--- a/media/libaaudio/src/utility/LinearRamp.h
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef AAUDIO_LINEAR_RAMP_H
-#define AAUDIO_LINEAR_RAMP_H
-
-#include <atomic>
-#include <stdint.h>
-
-/**
- * Generate segments along a linear ramp.
- * The ramp target can be updated from another thread.
- * When the target is updated, a new ramp is started from the current position.
- *
- * The first ramp starts at 0.0.
- *
- */
-class LinearRamp {
-public:
- LinearRamp() {
- mTarget.store(1.0f);
- }
-
- void setLengthInFrames(int32_t frames) {
- mLengthInFrames = frames;
- }
-
- int32_t getLengthInFrames() {
- return mLengthInFrames;
- }
-
- /**
- * This may be called by another thread.
- * @param target
- */
- void setTarget(float target) {
- mTarget.store(target);
- }
-
- float getTarget() {
- return mTarget.load();
- }
-
- /**
- * Force the nextSegment to start from this level.
- *
- * WARNING: this can cause a discontinuity if called while the ramp is being used.
- * Only call this when setting the initial ramp.
- *
- * @param level
- */
- void forceCurrent(float level) {
- mLevelFrom = level;
- mLevelTo = level; // forces a ramp if it does not match target
- }
-
- float getCurrent() {
- return mLevelFrom;
- }
-
- /**
- * Get levels for next ramp segment.
- *
- * @param frames number of frames in the segment
- * @param levelFrom pointer to starting amplitude
- * @param levelTo pointer to ending amplitude
- * @return true if ramp is still moving towards the target
- */
- bool nextSegment(int32_t frames, float *levelFrom, float *levelTo);
-
-private:
-
- bool isRamping();
-
- std::atomic<float> mTarget;
-
- int32_t mLengthInFrames = 48000 / 100; // 10 msec at 48000 Hz
- int32_t mRemaining = 0;
- float mLevelFrom = 0.0f;
- float mLevelTo = 0.0f;
-};
-
-
-#endif //AAUDIO_LINEAR_RAMP_H
diff --git a/media/libaaudio/tests/Android.bp b/media/libaaudio/tests/Android.bp
index 68194db..ef272b0 100644
--- a/media/libaaudio/tests/Android.bp
+++ b/media/libaaudio/tests/Android.bp
@@ -34,13 +34,6 @@
}
cc_test {
- name: "test_linear_ramp",
- defaults: ["libaaudio_tests_defaults"],
- srcs: ["test_linear_ramp.cpp"],
- shared_libs: ["libaaudio"],
-}
-
-cc_test {
name: "test_open_params",
defaults: ["libaaudio_tests_defaults"],
srcs: ["test_open_params.cpp"],
@@ -167,3 +160,15 @@
srcs: ["test_atomic_fifo.cpp"],
shared_libs: ["libaaudio"],
}
+
+cc_test {
+ name: "test_flowgraph",
+ defaults: ["libaaudio_tests_defaults"],
+ srcs: ["test_flowgraph.cpp"],
+ shared_libs: [
+ "libaaudio",
+ "libbinder",
+ "libcutils",
+ "libutils",
+ ],
+}
diff --git a/media/libaaudio/tests/test_flowgraph.cpp b/media/libaaudio/tests/test_flowgraph.cpp
new file mode 100644
index 0000000..d563a7e
--- /dev/null
+++ b/media/libaaudio/tests/test_flowgraph.cpp
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2018 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.
+ */
+
+/*
+ * Test FlowGraph
+ */
+
+#include <iostream>
+
+#include <gtest/gtest.h>
+
+#include "flowgraph/ClipToRange.h"
+#include "flowgraph/MonoToMultiConverter.h"
+#include "flowgraph/SourceFloat.h"
+#include "flowgraph/RampLinear.h"
+#include "flowgraph/SinkFloat.h"
+#include "flowgraph/SinkI16.h"
+#include "flowgraph/SinkI24.h"
+#include "flowgraph/SourceI16.h"
+#include "flowgraph/SourceI24.h"
+
+using namespace flowgraph;
+
+constexpr int kBytesPerI24Packed = 3;
+
+TEST(test_flowgraph, module_sinki16) {
+ static const float input[] = {1.0f, 0.5f, -0.25f, -1.0f, 0.0f, 53.9f, -87.2f};
+ static const int16_t expected[] = {32767, 16384, -8192, -32768, 0, 32767, -32768};
+ int16_t output[20];
+ SourceFloat sourceFloat{1};
+ SinkI16 sinkI16{1};
+
+ int numInputFrames = sizeof(input) / sizeof(input[0]);
+ sourceFloat.setData(input, numInputFrames);
+ sourceFloat.output.connect(&sinkI16.input);
+
+ int numOutputFrames = sizeof(output) / sizeof(int16_t);
+ int32_t numRead = sinkI16.read(output, numOutputFrames);
+ ASSERT_EQ(numInputFrames, numRead);
+ for (int i = 0; i < numRead; i++) {
+ EXPECT_EQ(expected[i], output[i]);
+ }
+}
+
+TEST(test_flowgraph, module_mono_to_stereo) {
+ static const float input[] = {1.0f, 2.0f, 3.0f};
+ float output[100] = {};
+ SourceFloat sourceFloat{1};
+ MonoToMultiConverter monoToStereo{2};
+ SinkFloat sinkFloat{2};
+
+ sourceFloat.setData(input, 3);
+
+ sourceFloat.output.connect(&monoToStereo.input);
+ monoToStereo.output.connect(&sinkFloat.input);
+
+ int32_t numRead = sinkFloat.read(output, 8);
+ ASSERT_EQ(3, numRead);
+ EXPECT_EQ(input[0], output[0]);
+ EXPECT_EQ(input[0], output[1]);
+ EXPECT_EQ(input[1], output[2]);
+ EXPECT_EQ(input[1], output[3]);
+}
+
+TEST(test_flowgraph, module_ramp_linear) {
+ constexpr int rampSize = 5;
+ constexpr int numOutput = 100;
+ constexpr float value = 1.0f;
+ constexpr float target = 100.0f;
+ float output[numOutput] = {};
+ RampLinear rampLinear{1};
+ SinkFloat sinkFloat{1};
+
+ rampLinear.input.setValue(value);
+ rampLinear.setLengthInFrames(rampSize);
+ rampLinear.setTarget(target);
+ rampLinear.forceCurrent(0.0f);
+
+ rampLinear.output.connect(&sinkFloat.input);
+
+ int32_t numRead = sinkFloat.read(output, numOutput);
+ ASSERT_EQ(numOutput, numRead);
+ constexpr float tolerance = 0.0001f; // arbitrary
+ int i = 0;
+ for (; i < rampSize; i++) {
+ float expected = i * value * target / rampSize;
+ EXPECT_NEAR(expected, output[i], tolerance);
+ }
+ for (; i < numOutput; i++) {
+ float expected = value * target;
+ EXPECT_NEAR(expected, output[i], tolerance);
+ }
+}
+
+// It is easiest to represent packed 24-bit data as a byte array.
+// This test will read from input, convert to float, then write
+// back to output as bytes.
+TEST(test_flowgraph, module_packed_24) {
+ static const uint8_t input[] = {0x01, 0x23, 0x45,
+ 0x67, 0x89, 0xAB,
+ 0xCD, 0xEF, 0x5A};
+ uint8_t output[99] = {};
+ SourceI24 sourceI24{1};
+ SinkI24 sinkI24{1};
+
+ int numInputFrames = sizeof(input) / kBytesPerI24Packed;
+ sourceI24.setData(input, numInputFrames);
+ sourceI24.output.connect(&sinkI24.input);
+
+ int32_t numRead = sinkI24.read(output, sizeof(output) / kBytesPerI24Packed);
+ ASSERT_EQ(numInputFrames, numRead);
+ for (size_t i = 0; i < sizeof(input); i++) {
+ EXPECT_EQ(input[i], output[i]);
+ }
+}
+
+TEST(test_flowgraph, module_clip_to_range) {
+ constexpr float myMin = -2.0f;
+ constexpr float myMax = 1.5f;
+
+ static const float input[] = {-9.7, 0.5f, -0.25, 1.0f, 12.3};
+ static const float expected[] = {myMin, 0.5f, -0.25, 1.0f, myMax};
+ float output[100];
+ SourceFloat sourceFloat{1};
+ ClipToRange clipper{1};
+ SinkFloat sinkFloat{1};
+
+ int numInputFrames = sizeof(input) / sizeof(input[0]);
+ sourceFloat.setData(input, numInputFrames);
+
+ clipper.setMinimum(myMin);
+ clipper.setMaximum(myMax);
+
+ sourceFloat.output.connect(&clipper.input);
+ clipper.output.connect(&sinkFloat.input);
+
+ int numOutputFrames = sizeof(output) / sizeof(output[0]);
+ int32_t numRead = sinkFloat.read(output, numOutputFrames);
+ ASSERT_EQ(numInputFrames, numRead);
+ constexpr float tolerance = 0.000001f; // arbitrary
+ for (int i = 0; i < numRead; i++) {
+ EXPECT_NEAR(expected[i], output[i], tolerance);
+ }
+}
diff --git a/media/libaaudio/tests/test_linear_ramp.cpp b/media/libaaudio/tests/test_linear_ramp.cpp
deleted file mode 100644
index 93226ba..0000000
--- a/media/libaaudio/tests/test_linear_ramp.cpp
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <iostream>
-#include <math.h>
-
-#include <gtest/gtest.h>
-
-#include "utility/AAudioUtilities.h"
-#include "utility/LinearRamp.h"
-
-TEST(test_linear_ramp, linear_ramp_segments) {
- LinearRamp ramp;
- const float source[4] = {1.0f, 1.0f, 1.0f, 1.0f };
- float destination[4] = {1.0f, 1.0f, 1.0f, 1.0f };
-
- float levelFrom = -1.0f;
- float levelTo = -1.0f;
- ramp.setLengthInFrames(8);
- ramp.setTarget(8.0f);
-
- EXPECT_EQ(8, ramp.getLengthInFrames());
-
- bool ramping = ramp.nextSegment(4, &levelFrom, &levelTo);
- EXPECT_EQ(1, ramping);
- EXPECT_EQ(0.0f, levelFrom);
- EXPECT_EQ(4.0f, levelTo);
-
- AAudio_linearRamp(source, destination, 4, 1, levelFrom, levelTo);
- EXPECT_EQ(0.0f, destination[0]);
- EXPECT_EQ(1.0f, destination[1]);
- EXPECT_EQ(2.0f, destination[2]);
- EXPECT_EQ(3.0f, destination[3]);
-
- ramping = ramp.nextSegment(4, &levelFrom, &levelTo);
- EXPECT_EQ(1, ramping);
- EXPECT_EQ(4.0f, levelFrom);
- EXPECT_EQ(8.0f, levelTo);
-
- AAudio_linearRamp(source, destination, 4, 1, levelFrom, levelTo);
- EXPECT_EQ(4.0f, destination[0]);
- EXPECT_EQ(5.0f, destination[1]);
- EXPECT_EQ(6.0f, destination[2]);
- EXPECT_EQ(7.0f, destination[3]);
-
- ramping = ramp.nextSegment(4, &levelFrom, &levelTo);
- EXPECT_EQ(0, ramping);
- EXPECT_EQ(8.0f, levelFrom);
- EXPECT_EQ(8.0f, levelTo);
-
- AAudio_linearRamp(source, destination, 4, 1, levelFrom, levelTo);
- EXPECT_EQ(8.0f, destination[0]);
- EXPECT_EQ(8.0f, destination[1]);
- EXPECT_EQ(8.0f, destination[2]);
- EXPECT_EQ(8.0f, destination[3]);
-
-};
-
-
-TEST(test_linear_ramp, linear_ramp_forced) {
- LinearRamp ramp;
- const float source[4] = {1.0f, 1.0f, 1.0f, 1.0f };
- float destination[4] = {1.0f, 1.0f, 1.0f, 1.0f };
-
- float levelFrom = -1.0f;
- float levelTo = -1.0f;
- ramp.setLengthInFrames(4);
- ramp.setTarget(8.0f);
- ramp.forceCurrent(4.0f);
- EXPECT_EQ(4.0f, ramp.getCurrent());
-
- bool ramping = ramp.nextSegment(4, &levelFrom, &levelTo);
- EXPECT_EQ(1, ramping);
- EXPECT_EQ(4.0f, levelFrom);
- EXPECT_EQ(8.0f, levelTo);
-
- AAudio_linearRamp(source, destination, 4, 1, levelFrom, levelTo);
- EXPECT_EQ(4.0f, destination[0]);
- EXPECT_EQ(5.0f, destination[1]);
- EXPECT_EQ(6.0f, destination[2]);
- EXPECT_EQ(7.0f, destination[3]);
-
- ramping = ramp.nextSegment(4, &levelFrom, &levelTo);
- EXPECT_EQ(0, ramping);
- EXPECT_EQ(8.0f, levelFrom);
- EXPECT_EQ(8.0f, levelTo);
-
- AAudio_linearRamp(source, destination, 4, 1, levelFrom, levelTo);
- EXPECT_EQ(8.0f, destination[0]);
- EXPECT_EQ(8.0f, destination[1]);
- EXPECT_EQ(8.0f, destination[2]);
- EXPECT_EQ(8.0f, destination[3]);
-
-};
-
-constexpr int16_t kMaxI16 = INT16_MAX;
-constexpr int16_t kMinI16 = INT16_MIN;
-constexpr int16_t kHalfI16 = 16384;
-constexpr int16_t kTenthI16 = 3277;
-
-//void AAudioConvert_floatToPcm16(const float *source,
-// int16_t *destination,
-// int32_t numSamples,
-// float amplitude);
-TEST(test_linear_ramp, float_to_i16) {
- const float source[] = {12345.6f, 1.0f, 0.5f, 0.1f, 0.0f, -0.1f, -0.5f, -1.0f, -12345.6f};
- constexpr size_t count = sizeof(source) / sizeof(source[0]);
- int16_t destination[count];
- const int16_t expected[count] = {kMaxI16, kMaxI16, kHalfI16, kTenthI16, 0,
- -kTenthI16, -kHalfI16, kMinI16, kMinI16};
-
- AAudioConvert_floatToPcm16(source, destination, count, 1.0f);
- for (size_t i = 0; i < count; i++) {
- EXPECT_EQ(expected[i], destination[i]);
- }
-
-}
-
-//void AAudioConvert_pcm16ToFloat(const int16_t *source,
-// float *destination,
-// int32_t numSamples,
-// float amplitude);
-TEST(test_linear_ramp, i16_to_float) {
- const int16_t source[] = {kMaxI16, kHalfI16, kTenthI16, 0,
- -kTenthI16, -kHalfI16, kMinI16};
- constexpr size_t count = sizeof(source) / sizeof(source[0]);
- float destination[count];
- const float expected[count] = {(32767.0f / 32768.0f), 0.5f, 0.1f, 0.0f, -0.1f, -0.5f, -1.0f};
-
- AAudioConvert_pcm16ToFloat(source, destination, count, 1.0f);
- for (size_t i = 0; i < count; i++) {
- EXPECT_NEAR(expected[i], destination[i], 0.0001f);
- }
-
-}
-
-//void AAudio_linearRamp(const int16_t *source,
-// int16_t *destination,
-// int32_t numFrames,
-// int32_t samplesPerFrame,
-// float amplitude1,
-// float amplitude2);
-TEST(test_linear_ramp, ramp_i16_to_i16) {
- const int16_t source[] = {1, 1, 1, 1, 1, 1, 1, 1};
- constexpr size_t count = sizeof(source) / sizeof(source[0]);
- int16_t destination[count];
- // Ramp will sweep from -1 to almost +1
- const int16_t expected[count] = {
- -1, // from -1.00
- -1, // from -0.75
- -1, // from -0.55, round away from zero
- 0, // from -0.25, round up to zero
- 0, // from 0.00
- 0, // from 0.25, round down to zero
- 1, // from 0.50, round away from zero
- 1 // from 0.75
- };
-
- // sweep across zero to test symmetry
- constexpr float amplitude1 = -1.0;
- constexpr float amplitude2 = 1.0;
- AAudio_linearRamp(source, destination, count, 1, amplitude1, amplitude2);
- for (size_t i = 0; i < count; i++) {
- EXPECT_EQ(expected[i], destination[i]);
- }
-
-}
diff --git a/media/libaudioprocessing/AudioMixer.cpp b/media/libaudioprocessing/AudioMixer.cpp
index f3ea826..af54b21 100644
--- a/media/libaudioprocessing/AudioMixer.cpp
+++ b/media/libaudioprocessing/AudioMixer.cpp
@@ -337,6 +337,7 @@
void AudioMixer::Track::reconfigureBufferProviders()
{
+ // configure from upstream to downstream buffer providers.
bufferProvider = mInputBufferProvider;
if (mReformatBufferProvider.get() != nullptr) {
mReformatBufferProvider->setBufferProvider(bufferProvider);
@@ -813,14 +814,15 @@
if (track->mInputBufferProvider == bufferProvider) {
return; // don't reset any buffer providers if identical.
}
- if (track->mReformatBufferProvider.get() != nullptr) {
- track->mReformatBufferProvider->reset();
- } else if (track->mDownmixerBufferProvider != nullptr) {
- track->mDownmixerBufferProvider->reset();
+ // reset order from downstream to upstream buffer providers.
+ if (track->mTimestretchBufferProvider.get() != nullptr) {
+ track->mTimestretchBufferProvider->reset();
} else if (track->mPostDownmixReformatBufferProvider.get() != nullptr) {
track->mPostDownmixReformatBufferProvider->reset();
- } else if (track->mTimestretchBufferProvider.get() != nullptr) {
- track->mTimestretchBufferProvider->reset();
+ } else if (track->mDownmixerBufferProvider != nullptr) {
+ track->mDownmixerBufferProvider->reset();
+ } else if (track->mReformatBufferProvider.get() != nullptr) {
+ track->mReformatBufferProvider->reset();
}
track->mInputBufferProvider = bufferProvider;
diff --git a/media/libmediaplayer2/Android.bp b/media/libmediaplayer2/Android.bp
index 0fb5abc..0672ca9 100644
--- a/media/libmediaplayer2/Android.bp
+++ b/media/libmediaplayer2/Android.bp
@@ -56,9 +56,11 @@
static_libs: [
"libmedia_helper",
+ "libmediaplayer2-protos",
+ "libprotobuf-cpp-lite",
"libstagefright_nuplayer2",
"libstagefright_rtsp",
- "libstagefright_timedtext",
+ "libstagefright_timedtext2",
],
export_include_dirs: [
diff --git a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h
index a6bf543..55d812b 100644
--- a/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h
+++ b/media/libmediaplayer2/include/mediaplayer2/MediaPlayer2Interface.h
@@ -33,6 +33,10 @@
#include <media/stagefright/foundation/AHandler.h>
#include <mediaplayer2/MediaPlayer2Types.h>
+#include "mediaplayer2.pb.h"
+
+using android::media::MediaPlayer2Proto::PlayerMessage;
+
// Fwd decl to make sure everyone agrees that the scope of struct sockaddr_in is
// global, and not in android::
struct sockaddr_in;
@@ -56,7 +60,8 @@
class MediaPlayer2InterfaceListener: public RefBase
{
public:
- virtual void notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj) = 0;
+ virtual void notify(int64_t srcId, int msg, int ext1, int ext2,
+ const PlayerMessage *obj) = 0;
};
class MediaPlayer2Interface : public AHandler {
@@ -217,7 +222,7 @@
// data sent by the java layer.
// @param[out] reply Parcel to hold the reply data. Cannot be null.
// @return OK if the call was successful.
- virtual status_t invoke(const Parcel& request, Parcel *reply) = 0;
+ virtual status_t invoke(const PlayerMessage &request, PlayerMessage *reply) = 0;
// The Client in the MetadataPlayerService calls this method on
// the native player to retrieve all or a subset of metadata.
@@ -236,7 +241,7 @@
mListener = listener;
}
- void sendEvent(int64_t srcId, int msg, int ext1=0, int ext2=0, const Parcel *obj=NULL) {
+ void sendEvent(int64_t srcId, int msg, int ext1=0, int ext2=0, const PlayerMessage *obj=NULL) {
sp<MediaPlayer2InterfaceListener> listener;
{
Mutex::Autolock autoLock(mListenerLock);
diff --git a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
index 43fba23..e03b499 100644
--- a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
+++ b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
@@ -42,7 +42,8 @@
class MediaPlayer2Listener: virtual public RefBase
{
public:
- virtual void notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj) = 0;
+ virtual void notify(int64_t srcId, int msg, int ext1, int ext2,
+ const PlayerMessage *obj = NULL) = 0;
};
class MediaPlayer2 : public MediaPlayer2InterfaceListener
@@ -89,8 +90,8 @@
bool isLooping();
status_t setVolume(float leftVolume, float rightVolume);
void notify(int64_t srcId, int msg, int ext1, int ext2,
- const Parcel *obj = NULL);
- status_t invoke(const Parcel& request, Parcel *reply);
+ const PlayerMessage *obj = NULL);
+ status_t invoke(const PlayerMessage &request, PlayerMessage *reply);
status_t setMetadataFilter(const Parcel& filter);
status_t getMetadata(bool update_only, bool apply_filter, Parcel *metadata);
status_t setAudioSessionId(audio_session_t sessionId);
diff --git a/media/libmediaplayer2/mediaplayer2.cpp b/media/libmediaplayer2/mediaplayer2.cpp
index f0ea59e..8d0df8a 100644
--- a/media/libmediaplayer2/mediaplayer2.cpp
+++ b/media/libmediaplayer2/mediaplayer2.cpp
@@ -223,7 +223,8 @@
~proxyListener() { };
- virtual void notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj) override {
+ virtual void notify(int64_t srcId, int msg, int ext1, int ext2,
+ const PlayerMessage *obj) override {
sp<MediaPlayer2> player = mPlayer.promote();
if (player != NULL) {
player->notify(srcId, msg, ext1, ext2, obj);
@@ -565,16 +566,15 @@
return mPlayer->playNextDataSource(srcId);
}
-status_t MediaPlayer2::invoke(const Parcel& request, Parcel *reply) {
+status_t MediaPlayer2::invoke(const PlayerMessage &request, PlayerMessage *reply) {
Mutex::Autolock _l(mLock);
const bool hasBeenInitialized =
(mCurrentState != MEDIA_PLAYER2_STATE_ERROR) &&
((mCurrentState & MEDIA_PLAYER2_IDLE) != MEDIA_PLAYER2_IDLE);
if ((mPlayer == NULL) || !hasBeenInitialized) {
- ALOGE("invoke failed: wrong state %X, mPlayer(%p)", mCurrentState, mPlayer.get());
+ ALOGE("invoke() failed: wrong state %X, mPlayer(%p)", mCurrentState, mPlayer.get());
return INVALID_OPERATION;
}
- ALOGV("invoke %zu", request.dataSize());
return mPlayer->invoke(request, reply);
}
@@ -1272,7 +1272,7 @@
}
}
-void MediaPlayer2::notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj) {
+void MediaPlayer2::notify(int64_t srcId, int msg, int ext1, int ext2, const PlayerMessage *obj) {
ALOGV("message received srcId=%lld, msg=%d, ext1=%d, ext2=%d",
(long long)srcId, msg, ext1, ext2);
diff --git a/media/libmediaplayer2/nuplayer2/Android.bp b/media/libmediaplayer2/nuplayer2/Android.bp
index 1634f35..dd91628 100644
--- a/media/libmediaplayer2/nuplayer2/Android.bp
+++ b/media/libmediaplayer2/nuplayer2/Android.bp
@@ -54,6 +54,7 @@
static_libs: [
"libmedia_helper",
+ "libmediaplayer2-protos",
],
name: "libstagefright_nuplayer2",
diff --git a/media/libmediaplayer2/nuplayer2/GenericSource2.cpp b/media/libmediaplayer2/nuplayer2/GenericSource2.cpp
index e317e23..4ce1a88 100644
--- a/media/libmediaplayer2/nuplayer2/GenericSource2.cpp
+++ b/media/libmediaplayer2/nuplayer2/GenericSource2.cpp
@@ -1590,15 +1590,20 @@
return OK; // source without DRM info
}
- sp<ABuffer> drmInfoBuffer = NuPlayer2Drm::retrieveDrmInfo(psshInfo);
- ALOGV("checkDrmInfo: MEDIA_DRM_INFO PSSH drm info size: %d", (int)drmInfoBuffer->size());
+ PlayerMessage playerMsg;
+ status_t ret = NuPlayer2Drm::retrieveDrmInfo(psshInfo, &playerMsg);
+ ALOGV("checkDrmInfo: MEDIA_DRM_INFO PSSH drm info size: %d", (int)playerMsg.ByteSize());
- if (drmInfoBuffer->size() == 0) {
- ALOGE("checkDrmInfo: Unexpected parcel size: 0");
+ if (ret != OK) {
+ ALOGE("checkDrmInfo: failed to retrive DrmInfo %d", ret);
return UNKNOWN_ERROR;
}
- notifyDrmInfo(drmInfoBuffer);
+ int size = playerMsg.ByteSize();
+ sp<ABuffer> drmInfoBuf = new ABuffer(size);
+ playerMsg.SerializeToArray(drmInfoBuf->data(), size);
+ drmInfoBuf->setRange(0, size);
+ notifyDrmInfo(drmInfoBuf);
return OK;
}
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
index b6b9b78..2f90825 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.cpp
@@ -33,7 +33,7 @@
#include "NuPlayer2Source.h"
#include "RTSPSource2.h"
#include "GenericSource2.h"
-#include "TextDescriptions.h"
+#include "TextDescriptions2.h"
#include "ATSParser.h"
@@ -584,9 +584,8 @@
msg->post();
}
-
void NuPlayer2::writeTrackInfo(
- Parcel* reply, const sp<AMessage>& format) const {
+ PlayerMessage* reply, const sp<AMessage>& format) const {
if (format == NULL) {
ALOGE("NULL format");
return;
@@ -619,10 +618,9 @@
return;
}
- reply->writeInt32(2); // write something non-zero
- reply->writeInt32(trackType);
- reply->writeString16(String16(mime.c_str()));
- reply->writeString16(String16(lang.c_str()));
+ reply->add_values()->set_int32_value(trackType);
+ reply->add_values()->set_string_value(mime.c_str());
+ reply->add_values()->set_string_value(lang.c_str());
if (trackType == MEDIA_TRACK_TYPE_SUBTITLE) {
int32_t isAuto, isDefault, isForced;
@@ -630,9 +628,9 @@
CHECK(format->findInt32("default", &isDefault));
CHECK(format->findInt32("forced", &isForced));
- reply->writeInt32(isAuto);
- reply->writeInt32(isDefault);
- reply->writeInt32(isForced);
+ reply->add_values()->set_int32_value(isAuto);
+ reply->add_values()->set_int32_value(isDefault);
+ reply->add_values()->set_int32_value(isForced);
}
}
@@ -764,7 +762,7 @@
sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
- Parcel* reply;
+ PlayerMessage* reply;
CHECK(msg->findPointer("reply", (void**)&reply));
size_t inbandTracks = 0;
@@ -778,7 +776,7 @@
}
// total track count
- reply->writeInt32(inbandTracks + ccTracks);
+ reply->add_values()->set_int32_value(inbandTracks + ccTracks);
// write inband tracks
for (size_t i = 0; i < inbandTracks; ++i) {
@@ -806,9 +804,9 @@
media_track_type type = (media_track_type)type32;
ssize_t selectedTrack = mSource->getSelectedTrack(type);
- Parcel* reply;
+ PlayerMessage* reply;
CHECK(msg->findPointer("reply", (void**)&reply));
- reply->writeInt32(selectedTrack);
+ reply->add_values()->set_int32_value(selectedTrack);
}
sp<AMessage> response = new AMessage;
@@ -2147,7 +2145,8 @@
displayHeight);
}
-void NuPlayer2::notifyListener(int64_t srcId, int msg, int ext1, int ext2, const Parcel *in) {
+void NuPlayer2::notifyListener(
+ int64_t srcId, int msg, int ext1, int ext2, const PlayerMessage *in) {
if (mDriver == NULL) {
return;
}
@@ -2231,7 +2230,7 @@
return OK;
}
-status_t NuPlayer2::getTrackInfo(Parcel* reply) const {
+status_t NuPlayer2::getTrackInfo(PlayerMessage* reply) const {
sp<AMessage> msg = new AMessage(kWhatGetTrackInfo, this);
msg->setPointer("reply", reply);
@@ -2240,7 +2239,7 @@
return err;
}
-status_t NuPlayer2::getSelectedTrack(int32_t type, Parcel* reply) const {
+status_t NuPlayer2::getSelectedTrack(int32_t type, PlayerMessage* reply) const {
sp<AMessage> msg = new AMessage(kWhatGetSelectedTrack, this);
msg->setPointer("reply", reply);
msg->setInt32("type", type);
@@ -2618,15 +2617,15 @@
// Modular DRM
case Source::kWhatDrmInfo:
{
- Parcel parcel;
+ PlayerMessage playerMsg;
sp<ABuffer> drmInfo;
CHECK(msg->findBuffer("drmInfo", &drmInfo));
- parcel.setData(drmInfo->data(), drmInfo->size());
+ playerMsg.ParseFromArray(drmInfo->data(), drmInfo->size());
- ALOGV("onSourceNotify() kWhatDrmInfo MEDIA2_DRM_INFO drmInfo: %p parcel size: %zu",
- drmInfo.get(), parcel.dataSize());
+ ALOGV("onSourceNotify() kWhatDrmInfo MEDIA2_DRM_INFO drmInfo: %p playerMsg size: %d",
+ drmInfo.get(), playerMsg.ByteSize());
- notifyListener(srcId, MEDIA2_DRM_INFO, 0 /* ext1 */, 0 /* ext2 */, &parcel);
+ notifyListener(srcId, MEDIA2_DRM_INFO, 0 /* ext1 */, 0 /* ext2 */, &playerMsg);
break;
}
@@ -2847,35 +2846,31 @@
CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
CHECK(buffer->meta()->findInt64("durationUs", &durationUs));
- Parcel in;
- in.writeInt32(trackIndex + baseIndex);
- in.writeInt64(timeUs);
- in.writeInt64(durationUs);
- in.writeInt32(buffer->size());
- in.writeInt32(buffer->size());
- in.write(buffer->data(), buffer->size());
+ PlayerMessage playerMsg;
+ playerMsg.add_values()->set_int32_value(trackIndex + baseIndex);
+ playerMsg.add_values()->set_int64_value(timeUs);
+ playerMsg.add_values()->set_int64_value(durationUs);
+ playerMsg.add_values()->set_bytes_value(buffer->data(), buffer->size());
- notifyListener(mSrcId, MEDIA2_SUBTITLE_DATA, 0, 0, &in);
+ notifyListener(mSrcId, MEDIA2_SUBTITLE_DATA, 0, 0, &playerMsg);
}
void NuPlayer2::sendTimedMetaData(const sp<ABuffer> &buffer) {
int64_t timeUs;
CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
- Parcel in;
- in.writeInt64(timeUs);
- in.writeInt32(buffer->size());
- in.writeInt32(buffer->size());
- in.write(buffer->data(), buffer->size());
+ PlayerMessage playerMsg;
+ playerMsg.add_values()->set_int64_value(timeUs);
+ playerMsg.add_values()->set_bytes_value(buffer->data(), buffer->size());
- notifyListener(mSrcId, MEDIA2_META_DATA, 0, 0, &in);
+ notifyListener(mSrcId, MEDIA2_META_DATA, 0, 0, &playerMsg);
}
void NuPlayer2::sendTimedTextData(const sp<ABuffer> &buffer) {
const void *data;
size_t size = 0;
int64_t timeUs;
- int32_t flag = TextDescriptions::IN_BAND_TEXT_3GPP;
+ int32_t flag = TextDescriptions2::IN_BAND_TEXT_3GPP;
AString mime;
CHECK(buffer->meta()->findString("mime", &mime));
@@ -2884,21 +2879,21 @@
data = buffer->data();
size = buffer->size();
- Parcel parcel;
+ PlayerMessage playerMsg;
if (size > 0) {
CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
int32_t global = 0;
if (buffer->meta()->findInt32("global", &global) && global) {
- flag |= TextDescriptions::GLOBAL_DESCRIPTIONS;
+ flag |= TextDescriptions2::GLOBAL_DESCRIPTIONS;
} else {
- flag |= TextDescriptions::LOCAL_DESCRIPTIONS;
+ flag |= TextDescriptions2::LOCAL_DESCRIPTIONS;
}
- TextDescriptions::getParcelOfDescriptions(
- (const uint8_t *)data, size, flag, timeUs / 1000, &parcel);
+ TextDescriptions2::getPlayerMessageOfDescriptions(
+ (const uint8_t *)data, size, flag, timeUs / 1000, &playerMsg);
}
- if ((parcel.dataSize() > 0)) {
- notifyListener(mSrcId, MEDIA2_TIMED_TEXT, 0, 0, &parcel);
+ if (playerMsg.values_size() > 0) {
+ notifyListener(mSrcId, MEDIA2_TIMED_TEXT, 0, 0, &playerMsg);
} else { // send an empty timed text
notifyListener(mSrcId, MEDIA2_TIMED_TEXT, 0, 0);
}
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2.h b/media/libmediaplayer2/nuplayer2/NuPlayer2.h
index 96f85f9..77845ac 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2.h
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2.h
@@ -23,6 +23,10 @@
#include <mediaplayer2/MediaPlayer2Interface.h>
+#include "mediaplayer2.pb.h"
+
+using android::media::MediaPlayer2Proto::PlayerMessage;
+
namespace android {
struct ABuffer;
@@ -77,8 +81,8 @@
bool needNotify = false);
status_t setVideoScalingMode(int32_t mode);
- status_t getTrackInfo(Parcel* reply) const;
- status_t getSelectedTrack(int32_t type, Parcel* reply) const;
+ status_t getTrackInfo(PlayerMessage* reply) const;
+ status_t getSelectedTrack(int32_t type, PlayerMessage* reply) const;
status_t selectTrack(size_t trackIndex, bool select, int64_t timeUs);
status_t getCurrentPosition(int64_t *mediaUs);
void getStats(Vector<sp<AMessage> > *mTrackStats);
@@ -292,7 +296,7 @@
const sp<AMessage> &inputFormat,
const sp<AMessage> &outputFormat = NULL);
- void notifyListener(int64_t srcId, int msg, int ext1, int ext2, const Parcel *in = NULL);
+ void notifyListener(int64_t srcId, int msg, int ext1, int ext2, const PlayerMessage *in = NULL);
void handleFlushComplete(bool audio, bool isDecoder);
void finishFlushIfPossible();
@@ -333,7 +337,7 @@
void sendTimedMetaData(const sp<ABuffer> &buffer);
void sendTimedTextData(const sp<ABuffer> &buffer);
- void writeTrackInfo(Parcel* reply, const sp<AMessage>& format) const;
+ void writeTrackInfo(PlayerMessage* reply, const sp<AMessage>& format) const;
status_t onPrepareDrm(const sp<AMessage> &msg);
status_t onReleaseDrm();
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp
index f85e3a2..4d799b8 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp
@@ -36,45 +36,47 @@
#include <media/IMediaAnalyticsService.h>
+using google::protobuf::RepeatedPtrField;
+using android::media::MediaPlayer2Proto::Value;
+
static const int kDumpLockRetries = 50;
static const int kDumpLockSleepUs = 20000;
namespace android {
-struct ParcelWrapper : public RefBase {
- static sp<ParcelWrapper> Create(const Parcel *p) {
+struct PlayerMessageWrapper : public RefBase {
+ static sp<PlayerMessageWrapper> Create(const PlayerMessage *p) {
if (p != NULL) {
- sp<ParcelWrapper> pw = new ParcelWrapper();
- if (pw->appendFrom(p) == OK) {
- return pw;
- }
+ sp<PlayerMessageWrapper> pw = new PlayerMessageWrapper();
+ pw->copyFrom(p);
+ return pw;
}
return NULL;
}
- const Parcel *getParcel() {
- return mParcel;
+ const PlayerMessage *getPlayerMessage() {
+ return mPlayerMessage;
}
protected:
- virtual ~ParcelWrapper() {
- if (mParcel != NULL) {
- delete mParcel;
+ virtual ~PlayerMessageWrapper() {
+ if (mPlayerMessage != NULL) {
+ delete mPlayerMessage;
}
}
private:
- ParcelWrapper()
- : mParcel(NULL) { }
+ PlayerMessageWrapper()
+ : mPlayerMessage(NULL) { }
- status_t appendFrom(const Parcel *p) {
- if (mParcel == NULL) {
- mParcel = new Parcel;
+ void copyFrom(const PlayerMessage *p) {
+ if (mPlayerMessage == NULL) {
+ mPlayerMessage = new PlayerMessage;
}
- return mParcel->appendFrom(p, 0 /* start */, p->dataSize());
+ mPlayerMessage->CopyFrom(*p);
}
- Parcel *mParcel;
+ PlayerMessage *mPlayerMessage;
};
// key for media statistics
@@ -591,34 +593,30 @@
return OK;
}
-status_t NuPlayer2Driver::invoke(const Parcel &request, Parcel *reply) {
- if (reply == NULL) {
+status_t NuPlayer2Driver::invoke(const PlayerMessage &request, PlayerMessage *response) {
+ if (response == NULL) {
ALOGE("reply is a NULL pointer");
return BAD_VALUE;
}
- int32_t methodId;
- status_t ret = request.readInt32(&methodId);
- if (ret != OK) {
- ALOGE("Failed to retrieve the requested method to invoke, err(%d)", ret);
- return ret;
- }
+ RepeatedPtrField<const Value>::const_iterator it = request.values().cbegin();
+ int32_t methodId = (it++)->int32_value();
switch (methodId) {
case MEDIA_PLAYER2_INVOKE_ID_SET_VIDEO_SCALING_MODE:
{
- int mode = request.readInt32();
+ int mode = (it++)->int32_value();
return mPlayer->setVideoScalingMode(mode);
}
case MEDIA_PLAYER2_INVOKE_ID_GET_TRACK_INFO:
{
- return mPlayer->getTrackInfo(reply);
+ return mPlayer->getTrackInfo(response);
}
case MEDIA_PLAYER2_INVOKE_ID_SELECT_TRACK:
{
- int trackIndex = request.readInt32();
+ int trackIndex = (it++)->int32_value();
int64_t msec = 0;
// getCurrentPosition should always return OK
getCurrentPosition(&msec);
@@ -627,14 +625,14 @@
case MEDIA_PLAYER2_INVOKE_ID_UNSELECT_TRACK:
{
- int trackIndex = request.readInt32();
+ int trackIndex = (it++)->int32_value();
return mPlayer->selectTrack(trackIndex, false /* select */, 0xdeadbeef /* not used */);
}
case MEDIA_PLAYER2_INVOKE_ID_GET_SELECTED_TRACK:
{
- int32_t type = request.readInt32();
- return mPlayer->getSelectedTrack(type, reply);
+ int32_t type = (it++)->int32_value();
+ return mPlayer->getSelectedTrack(type, response);
}
default:
@@ -829,12 +827,12 @@
CHECK(msg->findInt32("messageId", &msgId));
msg->findInt32("ext1", &ext1);
msg->findInt32("ext2", &ext2);
- sp<ParcelWrapper> in;
+ sp<PlayerMessageWrapper> in;
sp<RefBase> obj;
- if (msg->findObject("parcel", &obj) && obj != NULL) {
- in = static_cast<ParcelWrapper *>(obj.get());
+ if (msg->findObject("obj", &obj) && obj != NULL) {
+ in = static_cast<PlayerMessageWrapper *>(obj.get());
}
- sendEvent(srcId, msgId, ext1, ext2, (in == NULL ? NULL : in->getParcel()));
+ sendEvent(srcId, msgId, ext1, ext2, (in == NULL ? NULL : in->getPlayerMessage()));
break;
}
default:
@@ -843,16 +841,16 @@
}
void NuPlayer2Driver::notifyListener(
- int64_t srcId, int msg, int ext1, int ext2, const Parcel *in) {
+ int64_t srcId, int msg, int ext1, int ext2, const PlayerMessage *in) {
Mutex::Autolock autoLock(mLock);
notifyListener_l(srcId, msg, ext1, ext2, in);
}
void NuPlayer2Driver::notifyListener_l(
- int64_t srcId, int msg, int ext1, int ext2, const Parcel *in) {
+ int64_t srcId, int msg, int ext1, int ext2, const PlayerMessage *in) {
ALOGD("notifyListener_l(%p), (%lld, %d, %d, %d, %d), loop setting(%d, %d)",
this, (long long)srcId, msg, ext1, ext2,
- (in == NULL ? -1 : (int)in->dataSize()), mAutoLoop, mLooping);
+ (in == NULL ? -1 : (int)in->ByteSize()), mAutoLoop, mLooping);
if (srcId == mSrcId) {
switch (msg) {
case MEDIA2_PLAYBACK_COMPLETE:
@@ -919,7 +917,7 @@
notify->setInt32("messageId", msg);
notify->setInt32("ext1", ext1);
notify->setInt32("ext2", ext2);
- notify->setObject("parcel", ParcelWrapper::Create(in));
+ notify->setObject("obj", PlayerMessageWrapper::Create((PlayerMessage*)in));
notify->post();
}
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h
index 6d5a007..50ee173 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.h
@@ -56,7 +56,7 @@
virtual status_t reset() override;
virtual status_t notifyAt(int64_t mediaTimeUs) override;
virtual status_t setLooping(int loop) override;
- virtual status_t invoke(const Parcel &request, Parcel *reply) override;
+ virtual status_t invoke(const PlayerMessage &request, PlayerMessage *response) override;
virtual void setAudioSink(const sp<AudioSink> &audioSink) override;
virtual status_t setParameter(int key, const Parcel &request) override;
virtual status_t getParameter(int key, Parcel *reply) override;
@@ -78,7 +78,7 @@
void notifyRebufferingWhenExit(int64_t srcId, bool status);
void notifySeekComplete(int64_t srcId);
void notifyListener(int64_t srcId, int msg, int ext1 = 0, int ext2 = 0,
- const Parcel *in = NULL);
+ const PlayerMessage *in = NULL);
void notifyFlagsChanged(int64_t srcId, uint32_t flags);
// Modular DRM
@@ -145,7 +145,7 @@
status_t start_l();
void notifyListener_l(int64_t srcId, int msg, int ext1 = 0, int ext2 = 0,
- const Parcel *in = NULL);
+ const PlayerMessage *in = NULL);
DISALLOW_EVIL_CONSTRUCTORS(NuPlayer2Driver);
};
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.cpp
index 4853ae1..57d1147 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.cpp
@@ -133,9 +133,8 @@
return drmInfoBuffer;
}
-sp<ABuffer> NuPlayer2Drm::retrieveDrmInfo(PsshInfo *psshInfo)
+status_t NuPlayer2Drm::retrieveDrmInfo(PsshInfo *psshInfo, PlayerMessage *playerMsg)
{
-
std::ostringstream pssh, drmInfo;
// 0) Generate PSSH bytes
@@ -153,22 +152,20 @@
ALOGV("retrieveDrmInfo: MEDIA_DRM_INFO PSSH: size: %u %s", psshSize, psshHex);
// 1) Write PSSH bytes
- drmInfo.write(reinterpret_cast<const char *>(&psshSize), sizeof(psshSize));
- drmInfo.write(reinterpret_cast<const char *>(pssh.str().c_str()), psshSize);
+ playerMsg->add_values()->set_bytes_value(
+ reinterpret_cast<const char *>(pssh.str().c_str()), psshSize);
// 2) Write supportedDRMs
uint32_t numentries = psshInfo->numentries;
- drmInfo.write(reinterpret_cast<const char *>(&numentries), sizeof(numentries));
+ playerMsg->add_values()->set_int32_value(numentries);
for (size_t i = 0; i < numentries; i++) {
PsshEntry *entry = &psshInfo->entries[i];
- drmInfo.write(reinterpret_cast<const char *>(&entry->uuid), sizeof(entry->uuid));
+ playerMsg->add_values()->set_bytes_value(
+ reinterpret_cast<const char *>(&entry->uuid), sizeof(entry->uuid));
ALOGV("retrieveDrmInfo: MEDIA_DRM_INFO supportedScheme[%zu] %s", i,
DrmUUID::arrayToHex((const uint8_t*)&entry->uuid, sizeof(AMediaUUID)).string());
}
-
- sp<ABuffer> drmInfoBuf = ABuffer::CreateAsCopy(drmInfo.str().c_str(), drmInfo.tellp());
- drmInfoBuf->setRange(0, drmInfo.tellp());
- return drmInfoBuf;
+ return OK;
}
} // namespace android
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.h b/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.h
index 99d2415..968d1be 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.h
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Drm.h
@@ -23,6 +23,10 @@
#include <utils/String8.h>
#include <utils/Vector.h>
+#include "mediaplayer2.pb.h"
+
+using android::media::MediaPlayer2Proto::PlayerMessage;
+
namespace android {
struct DrmUUID {
@@ -80,7 +84,7 @@
public:
static sp<ABuffer> retrieveDrmInfo(const void *pssh, uint32_t psshsize);
- static sp<ABuffer> retrieveDrmInfo(PsshInfo *);
+ static status_t retrieveDrmInfo(PsshInfo *, PlayerMessage *);
}; // NuPlayer2Drm
diff --git a/media/libnblog/Android.bp b/media/libnblog/Android.bp
index 74aaf77..4a4aac1 100644
--- a/media/libnblog/Android.bp
+++ b/media/libnblog/Android.bp
@@ -13,9 +13,14 @@
"libbinder",
"libcutils",
"liblog",
+ "libmediametrics",
"libutils",
],
+ static_libs: [
+ "libjsoncpp",
+ ],
+
cflags: [
"-Werror",
"-Wall",
diff --git a/media/libnblog/NBLog.cpp b/media/libnblog/NBLog.cpp
index d659445..aee0ea3 100644
--- a/media/libnblog/NBLog.cpp
+++ b/media/libnblog/NBLog.cpp
@@ -21,6 +21,7 @@
#include <algorithm>
#include <climits>
#include <math.h>
+#include <memory>
#include <unordered_set>
#include <vector>
#include <stdarg.h>
@@ -31,12 +32,14 @@
#include <time.h>
#include <new>
#include <audio_utils/roundup.h>
+#include <json/json.h>
#include <media/nblog/NBLog.h>
#include <media/nblog/PerformanceAnalysis.h>
#include <media/nblog/ReportPerformance.h>
#include <utils/CallStack.h>
#include <utils/Log.h>
#include <utils/String8.h>
+#include <utils/Timers.h>
#include <queue>
#include <utility>
@@ -68,7 +71,7 @@
}
const uint8_t type = EntryIterator(ptr)->type;
switch (type) {
- case EVENT_START_FMT:
+ case EVENT_FMT_START:
return std::make_unique<FormatEntry>(FormatEntry(ptr));
case EVENT_AUDIO_STATE:
case EVENT_HISTOGRAM_ENTRY_TS:
@@ -107,7 +110,7 @@
++it; // skip timestamp
++it; // skip hash
// Skip author if present
- if (it->type == EVENT_AUTHOR) {
+ if (it->type == EVENT_FMT_AUTHOR) {
++it;
}
return it;
@@ -138,7 +141,7 @@
++it; // skip timestamp
++it; // skip hash
// if there is an author entry, return it, return -1 otherwise
- return it->type == EVENT_AUTHOR ? it.payload<int>() : -1;
+ return it->type == EVENT_FMT_AUTHOR ? it.payload<int>() : -1;
}
NBLog::EntryIterator NBLog::FormatEntry::copyWithAuthor(
@@ -151,14 +154,14 @@
// insert author entry
size_t authorEntrySize = Entry::kOverhead + sizeof(author);
uint8_t authorEntry[authorEntrySize];
- authorEntry[offsetof(entry, type)] = EVENT_AUTHOR;
+ authorEntry[offsetof(entry, type)] = EVENT_FMT_AUTHOR;
authorEntry[offsetof(entry, length)] =
authorEntry[authorEntrySize + Entry::kPreviousLengthOffset] =
sizeof(author);
*(int*) (&authorEntry[offsetof(entry, data)]) = author;
dst->write(authorEntry, authorEntrySize);
// copy rest of entries
- while ((++it)->type != EVENT_END_FMT) {
+ while ((++it)->type != EVENT_FMT_END) {
it.copyTo(dst);
}
it.copyTo(dst);
@@ -394,46 +397,12 @@
if (!mEnabled) {
return;
}
- int64_t ts = get_monotonic_ns();
- if (ts > 0) {
+ struct timespec ts;
+ if (!clock_gettime(CLOCK_MONOTONIC, &ts)) {
log(EVENT_TIMESTAMP, &ts, sizeof(ts));
- } else {
- ALOGE("Failed to get timestamp");
}
}
-void NBLog::Writer::logTimestamp(const int64_t ts)
-{
- if (!mEnabled) {
- return;
- }
- log(EVENT_TIMESTAMP, &ts, sizeof(ts));
-}
-
-void NBLog::Writer::logInteger(const int x)
-{
- if (!mEnabled) {
- return;
- }
- log(EVENT_INTEGER, &x, sizeof(x));
-}
-
-void NBLog::Writer::logFloat(const float x)
-{
- if (!mEnabled) {
- return;
- }
- log(EVENT_FLOAT, &x, sizeof(x));
-}
-
-void NBLog::Writer::logPID()
-{
- if (!mEnabled) {
- return;
- }
- log(EVENT_PID, mPidTag, mPidTagSize);
-}
-
void NBLog::Writer::logStart(const char *fmt)
{
if (!mEnabled) {
@@ -443,24 +412,20 @@
if (length > Entry::kMaxLength) {
length = Entry::kMaxLength;
}
- log(EVENT_START_FMT, fmt, length);
+ log(EVENT_FMT_START, fmt, length);
}
-void NBLog::Writer::logEnd()
+void NBLog::Writer::logTimestampFormat()
{
if (!mEnabled) {
return;
}
- Entry entry = Entry(EVENT_END_FMT, NULL, 0);
- log(entry, true);
-}
-
-void NBLog::Writer::logHash(log_hash_t hash)
-{
- if (!mEnabled) {
- return;
+ const nsecs_t ts = systemTime();
+ if (ts > 0) {
+ log(EVENT_FMT_TIMESTAMP, &ts, sizeof(ts));
+ } else {
+ ALOGE("Failed to get timestamp");
}
- log(EVENT_HASH, &hash, sizeof(hash));
}
void NBLog::Writer::logEventHistTs(Event event, log_hash_t hash)
@@ -470,7 +435,7 @@
}
HistTsEntry data;
data.hash = hash;
- data.ts = get_monotonic_ns();
+ data.ts = systemTime();
if (data.ts > 0) {
log(event, &data, sizeof(data));
} else {
@@ -478,14 +443,6 @@
}
}
-void NBLog::Writer::logMonotonicCycleTime(uint32_t monotonicNs)
-{
- if (!mEnabled) {
- return;
- }
- log(EVENT_MONOTONIC_CYCLE_TIME, &monotonicNs, sizeof(&monotonicNs));
-}
-
void NBLog::Writer::logFormat(const char *fmt, log_hash_t hash, ...)
{
if (!mEnabled) {
@@ -504,11 +461,13 @@
}
Writer::logStart(fmt);
int i;
- double f;
+ double d;
+ float f;
char* s;
+ size_t length;
int64_t t;
- Writer::logTimestamp();
- Writer::logHash(hash);
+ Writer::logTimestampFormat();
+ log(EVENT_FMT_HASH, &hash, sizeof(hash));
for (const char *p = fmt; *p != '\0'; p++) {
// TODO: implement more complex formatting such as %.3f
if (*p != '%') {
@@ -517,26 +476,31 @@
switch(*++p) {
case 's': // string
s = va_arg(argp, char *);
- Writer::log(s);
+ length = strlen(s);
+ if (length > Entry::kMaxLength) {
+ length = Entry::kMaxLength;
+ }
+ log(EVENT_FMT_STRING, s, length);
break;
case 't': // timestamp
t = va_arg(argp, int64_t);
- Writer::logTimestamp(t);
+ log(EVENT_FMT_TIMESTAMP, &t, sizeof(t));
break;
case 'd': // integer
i = va_arg(argp, int);
- Writer::logInteger(i);
+ log(EVENT_FMT_INTEGER, &i, sizeof(i));
break;
case 'f': // float
- f = va_arg(argp, double); // float arguments are promoted to double in vararg lists
- Writer::logFloat((float)f);
+ d = va_arg(argp, double); // float arguments are promoted to double in vararg lists
+ f = (float)d;
+ log(EVENT_FMT_FLOAT, &f, sizeof(f));
break;
case 'p': // pid
- Writer::logPID();
+ log(EVENT_FMT_PID, mPidTag, mPidTagSize);
break;
// the "%\0" case finishes parsing
@@ -552,7 +516,8 @@
break;
}
}
- Writer::logEnd();
+ Entry etr(EVENT_FMT_END, nullptr, 0);
+ log(etr, true);
}
void NBLog::Writer::log(Event event, const void *data, size_t length)
@@ -622,79 +587,6 @@
{
}
-void NBLog::LockedWriter::log(const char *string)
-{
- Mutex::Autolock _l(mLock);
- Writer::log(string);
-}
-
-void NBLog::LockedWriter::logf(const char *fmt, ...)
-{
- // FIXME should not take the lock until after formatting is done
- Mutex::Autolock _l(mLock);
- va_list ap;
- va_start(ap, fmt);
- Writer::logvf(fmt, ap);
- va_end(ap);
-}
-
-void NBLog::LockedWriter::logvf(const char *fmt, va_list ap)
-{
- // FIXME should not take the lock until after formatting is done
- Mutex::Autolock _l(mLock);
- Writer::logvf(fmt, ap);
-}
-
-void NBLog::LockedWriter::logTimestamp()
-{
- // FIXME should not take the lock until after the clock_gettime() syscall
- Mutex::Autolock _l(mLock);
- Writer::logTimestamp();
-}
-
-void NBLog::LockedWriter::logTimestamp(const int64_t ts)
-{
- Mutex::Autolock _l(mLock);
- Writer::logTimestamp(ts);
-}
-
-void NBLog::LockedWriter::logInteger(const int x)
-{
- Mutex::Autolock _l(mLock);
- Writer::logInteger(x);
-}
-
-void NBLog::LockedWriter::logFloat(const float x)
-{
- Mutex::Autolock _l(mLock);
- Writer::logFloat(x);
-}
-
-void NBLog::LockedWriter::logPID()
-{
- Mutex::Autolock _l(mLock);
- Writer::logPID();
-}
-
-void NBLog::LockedWriter::logStart(const char *fmt)
-{
- Mutex::Autolock _l(mLock);
- Writer::logStart(fmt);
-}
-
-
-void NBLog::LockedWriter::logEnd()
-{
- Mutex::Autolock _l(mLock);
- Writer::logEnd();
-}
-
-void NBLog::LockedWriter::logHash(log_hash_t hash)
-{
- Mutex::Autolock _l(mLock);
- Writer::logHash(hash);
-}
-
bool NBLog::LockedWriter::isEnabled() const
{
Mutex::Autolock _l(mLock);
@@ -707,19 +599,47 @@
return Writer::setEnabled(enabled);
}
+void NBLog::LockedWriter::log(const Entry &entry, bool trusted) {
+ Mutex::Autolock _l(mLock);
+ Writer::log(entry, trusted);
+}
+
// ---------------------------------------------------------------------------
-const std::unordered_set<NBLog::Event> NBLog::Reader::startingTypes {
- NBLog::Event::EVENT_START_FMT,
- NBLog::Event::EVENT_HISTOGRAM_ENTRY_TS,
- NBLog::Event::EVENT_AUDIO_STATE,
- NBLog::Event::EVENT_MONOTONIC_CYCLE_TIME
+// We make a set of the invalid types rather than the valid types when aligning
+// Snapshot EntryIterators to valid entries during log corruption checking.
+// This is done in order to avoid the maintenance overhead of adding a new NBLog::Event
+// type to the two sets below whenever a new NBLog::Event type is created, as it is
+// very likely that new types added will be valid types.
+// Currently, invalidBeginTypes and invalidEndTypes are used to handle the special
+// case of a Format Entry, which consists of a variable number of simple log entries.
+// If a new NBLog::Event is added that consists of a variable number of simple log entries,
+// then these sets need to be updated.
+
+// We want the beginning of a Snapshot to point to an entry that is not in
+// the middle of a formatted entry and not an FMT_END.
+const std::unordered_set<NBLog::Event> NBLog::Reader::invalidBeginTypes {
+ NBLog::Event::EVENT_FMT_TIMESTAMP,
+ NBLog::Event::EVENT_FMT_HASH,
+ NBLog::Event::EVENT_FMT_STRING,
+ NBLog::Event::EVENT_FMT_INTEGER,
+ NBLog::Event::EVENT_FMT_FLOAT,
+ NBLog::Event::EVENT_FMT_PID,
+ NBLog::Event::EVENT_FMT_AUTHOR,
+ NBLog::Event::EVENT_FMT_END
};
-const std::unordered_set<NBLog::Event> NBLog::Reader::endingTypes {
- NBLog::Event::EVENT_END_FMT,
- NBLog::Event::EVENT_HISTOGRAM_ENTRY_TS,
- NBLog::Event::EVENT_AUDIO_STATE,
- NBLog::Event::EVENT_MONOTONIC_CYCLE_TIME
+
+// We want the end of a Snapshot to point to an entry that is not in
+// the middle of a formatted entry and not a FMT_START.
+const std::unordered_set<NBLog::Event> NBLog::Reader::invalidEndTypes {
+ NBLog::Event::EVENT_FMT_START,
+ NBLog::Event::EVENT_FMT_TIMESTAMP,
+ NBLog::Event::EVENT_FMT_HASH,
+ NBLog::Event::EVENT_FMT_STRING,
+ NBLog::Event::EVENT_FMT_INTEGER,
+ NBLog::Event::EVENT_FMT_FLOAT,
+ NBLog::Event::EVENT_FMT_PID,
+ NBLog::Event::EVENT_FMT_AUTHOR
};
NBLog::Reader::Reader(const void *shared, size_t size, const std::string &name)
@@ -744,17 +664,22 @@
delete mFifo;
}
-const uint8_t *NBLog::Reader::findLastEntryOfTypes(const uint8_t *front, const uint8_t *back,
- const std::unordered_set<Event> &types) {
+const uint8_t *NBLog::Reader::findLastValidEntry(const uint8_t *front, const uint8_t *back,
+ const std::unordered_set<Event> &invalidTypes) {
+ if (front == nullptr || back == nullptr) {
+ return nullptr;
+ }
while (back + Entry::kPreviousLengthOffset >= front) {
const uint8_t *prev = back - back[Entry::kPreviousLengthOffset] - Entry::kOverhead;
- if (prev < front || prev + prev[offsetof(entry, length)] +
- Entry::kOverhead != back) {
-
+ const Event type = (const Event)prev[offsetof(entry, type)];
+ if (prev < front
+ || prev + prev[offsetof(entry, length)] + Entry::kOverhead != back
+ || type <= NBLog::EVENT_RESERVED || type >= NBLog::EVENT_UPPER_BOUND) {
// prev points to an out of limits or inconsistent entry
return nullptr;
}
- if (types.find((const Event) prev[offsetof(entry, type)]) != types.end()) {
+ // if invalidTypes does not contain the type, then the type is valid.
+ if (invalidTypes.find(type) == invalidTypes.end()) {
return prev;
}
back = prev;
@@ -768,7 +693,7 @@
std::unique_ptr<NBLog::Snapshot> NBLog::Reader::getSnapshot()
{
if (mFifoReader == NULL) {
- return std::make_unique<Snapshot>();
+ return std::unique_ptr<Snapshot>(new Snapshot());
}
// This emulates the behaviour of audio_utils_fifo_reader::read, but without incrementing the
@@ -798,9 +723,20 @@
if (availToRead <= 0) {
ALOGW_IF(availToRead < 0, "NBLog Reader %s failed to catch up with Writer", mName.c_str());
- return std::make_unique<Snapshot>();
+ return std::unique_ptr<Snapshot>(new Snapshot());
}
+ // Change to #if 1 for debugging. This statement is useful for checking buffer fullness levels
+ // (as seen by reader) and how much data was lost. If you find that the fullness level is
+ // getting close to full, or that data loss is happening to often, then you should
+ // probably try some of the following:
+ // - log less data
+ // - log less often
+ // - increase the initial shared memory allocation for the buffer
+#if 0
+ ALOGD("getSnapshot name=%s, availToRead=%zd, capacity=%zu, fullness=%.3f, lost=%zu",
+ name().c_str(), availToRead, capacity, (double)availToRead / (double)capacity, lost);
+#endif
std::unique_ptr<Snapshot> snapshot(new Snapshot(availToRead));
memcpy(snapshot->mData, (const char *) mFifo->buffer() + iovec[0].mOffset, iovec[0].mLength);
if (iovec[1].mLength > 0) {
@@ -811,28 +747,28 @@
// Handle corrupted buffer
// Potentially, a buffer has corrupted data on both beginning (due to overflow) and end
// (due to incomplete format entry). But even if the end format entry is incomplete,
- // it ends in a complete entry (which is not an END_FMT). So is safe to traverse backwards.
+ // it ends in a complete entry (which is not an FMT_END). So is safe to traverse backwards.
// TODO: handle client corruption (in the middle of a buffer)
const uint8_t *back = snapshot->mData + availToRead;
const uint8_t *front = snapshot->mData;
- // Find last END_FMT. <back> is sitting on an entry which might be the middle of a FormatEntry.
- // We go backwards until we find an EVENT_END_FMT.
- const uint8_t *lastEnd = findLastEntryOfTypes(front, back, endingTypes);
+ // Find last FMT_END. <back> is sitting on an entry which might be the middle of a FormatEntry.
+ // We go backwards until we find an EVENT_FMT_END.
+ const uint8_t *lastEnd = findLastValidEntry(front, back, invalidEndTypes);
if (lastEnd == nullptr) {
snapshot->mEnd = snapshot->mBegin = EntryIterator(front);
} else {
- // end of snapshot points to after last END_FMT entry
+ // end of snapshot points to after last FMT_END entry
snapshot->mEnd = EntryIterator(lastEnd).next();
- // find first START_FMT
+ // find first FMT_START
const uint8_t *firstStart = nullptr;
const uint8_t *firstStartTmp = snapshot->mEnd;
- while ((firstStartTmp = findLastEntryOfTypes(front, firstStartTmp, startingTypes))
+ while ((firstStartTmp = findLastValidEntry(front, firstStartTmp, invalidBeginTypes))
!= nullptr) {
firstStart = firstStartTmp;
}
- // firstStart is null if no START_FMT entry was found before lastEnd
+ // firstStart is null if no FMT_START entry was found before lastEnd
if (firstStart == nullptr) {
snapshot->mBegin = snapshot->mEnd;
} else {
@@ -847,36 +783,58 @@
return snapshot;
}
+// TODO separate this method from the rest of NBLog
// Takes raw content of the local merger FIFO, processes log entries, and
// writes the data to a map of class PerformanceAnalysis, based on their thread ID.
-void NBLog::MergeReader::getAndProcessSnapshot(NBLog::Snapshot &snapshot, int author)
+void NBLog::MergeReader::processSnapshot(NBLog::Snapshot &snapshot, int author)
{
- for (const entry &etr : snapshot) {
- switch (etr.type) {
+ ReportPerformance::PerformanceData& data = mThreadPerformanceData[author];
+ // We don't do "auto it" because it reduces readability in this case.
+ for (EntryIterator it = snapshot.begin(); it != snapshot.end(); ++it) {
+ switch (it->type) {
case EVENT_HISTOGRAM_ENTRY_TS: {
- HistTsEntry *data = (HistTsEntry *) (etr.data);
- // TODO This memcpies are here to avoid unaligned memory access crash.
- // There's probably a more efficient way to do it
- log_hash_t hash;
- memcpy(&hash, &(data->hash), sizeof(hash));
- int64_t ts;
- memcpy(&ts, &data->ts, sizeof(ts));
+ const HistTsEntry payload = it.payload<HistTsEntry>();
// TODO: hash for histogram ts and audio state need to match
// and correspond to audio production source file location
- mThreadPerformanceAnalysis[author][0 /*hash*/].logTsEntry(ts);
+ mThreadPerformanceAnalysis[author][0 /*hash*/].logTsEntry(payload.ts);
} break;
case EVENT_AUDIO_STATE: {
- HistTsEntry *data = (HistTsEntry *) (etr.data);
- // TODO This memcpies are here to avoid unaligned memory access crash.
- // There's probably a more efficient way to do it
- log_hash_t hash;
- memcpy(&hash, &(data->hash), sizeof(hash));
mThreadPerformanceAnalysis[author][0 /*hash*/].handleStateChange();
} break;
- case EVENT_END_FMT:
+ case EVENT_THREAD_INFO: {
+ const thread_info_t info = it.payload<thread_info_t>();
+ // TODO make PerformanceData hold a type of thread_info_t.
+ // Currently, thread_info_t is defined in NBLog.h, which includes
+ // PerformanceAnalysis.h. PerformanceData is defined in PerformanceAnalysis.h,
+ // where including NBLog.h would result in circular includes. The organization
+ // of files will need to change to avoid this problem.
+ data.type = info.type;
+ data.frameCount = info.frameCount;
+ data.sampleRate = info.sampleRate;
+ } break;
+ case EVENT_LATENCY: {
+ const double latencyMs = it.payload<double>();
+ data.latencyHist.add(latencyMs);
+ } break;
+ case EVENT_WORK_TIME: {
+ const int64_t monotonicNs = it.payload<int64_t>();
+ const double monotonicMs = monotonicNs * 1e-6;
+ data.workHist.add(monotonicMs);
+ data.active += monotonicNs;
+ } break;
+ case EVENT_WARMUP_TIME: {
+ const double timeMs = it.payload<double>();
+ data.warmupHist.add(timeMs);
+ } break;
+ case EVENT_UNDERRUN:
+ data.underruns++;
+ break;
+ case EVENT_OVERRUN:
+ data.overruns++;
+ break;
case EVENT_RESERVED:
case EVENT_UPPER_BOUND:
- ALOGW("warning: unexpected event %d", etr.type);
+ ALOGW("warning: unexpected event %d", it->type);
default:
break;
}
@@ -895,7 +853,20 @@
// TODO unlock lock here
for (size_t i = 0; i < nLogs; i++) {
if (snapshots[i] != nullptr) {
- getAndProcessSnapshot(*(snapshots[i]), i);
+ processSnapshot(*(snapshots[i]), i);
+ }
+ }
+ checkPushToMediaMetrics();
+}
+
+void NBLog::MergeReader::checkPushToMediaMetrics()
+{
+ const nsecs_t now = systemTime();
+ for (auto& item : mThreadPerformanceData) {
+ ReportPerformance::PerformanceData& data = item.second;
+ if (now - data.start >= kPeriodicMediaMetricsPush) {
+ (void)ReportPerformance::sendToMediaMetrics(data);
+ data.reset(); // data is persistent per thread
}
}
}
@@ -904,6 +875,19 @@
{
// TODO: add a mutex around media.log dump
ReportPerformance::dump(fd, indent, mThreadPerformanceAnalysis);
+ Json::Value root(Json::arrayValue);
+ for (const auto& item : mThreadPerformanceData) {
+ const ReportPerformance::PerformanceData& data = item.second;
+ std::unique_ptr<Json::Value> threadData = ReportPerformance::dumpToJson(data);
+ if (threadData == nullptr) {
+ continue;
+ }
+ (*threadData)["threadNum"] = item.first;
+ root.append(*threadData);
+ }
+ Json::StyledWriter writer;
+ std::string rootStr = writer.write(root);
+ dprintf(fd, "%s", rootStr.c_str());
}
// TODO for future compatibility, would prefer to have a dump() go to string, and then go
@@ -918,17 +902,30 @@
String8 timestamp, body;
// TODO all logged types should have a printable format.
- for (auto it = snapshot->begin(); it != snapshot->end(); ++it) {
+ for (EntryIterator it = snapshot->begin(); it != snapshot->end(); ++it) {
switch (it->type) {
- case EVENT_START_FMT:
+ case EVENT_FMT_START:
it = handleFormat(FormatEntry(it), ×tamp, &body);
break;
- case EVENT_MONOTONIC_CYCLE_TIME: {
- uint32_t monotonicNs;
- memcpy(&monotonicNs, it->data, sizeof(monotonicNs));
- body.appendFormat("Thread cycle took %u ns", monotonicNs);
+ case EVENT_WORK_TIME: {
+ const int64_t monotonicNs = it.payload<int64_t>();
+ body.appendFormat("Thread cycle: %ld ns", (long)monotonicNs);
} break;
- case EVENT_END_FMT:
+ case EVENT_LATENCY: {
+ const double latencyMs = it.payload<double>();
+ body.appendFormat("latency: %.3f ms", latencyMs);
+ } break;
+ case EVENT_WARMUP_TIME: {
+ const double timeMs = it.payload<double>();
+ body.appendFormat("warmup time: %.3f ms", timeMs);
+ } break;
+ case EVENT_UNDERRUN:
+ body.appendFormat("underrun");
+ break;
+ case EVENT_OVERRUN:
+ body.appendFormat("overrun");
+ break;
+ case EVENT_FMT_END:
case EVENT_RESERVED:
case EVENT_UPPER_BOUND:
body.appendFormat("warning: unexpected event %d", it->type);
@@ -1012,7 +1009,7 @@
String8 *body)
{
// log timestamp
- int64_t ts = fmtEntry.timestamp();
+ const int64_t ts = fmtEntry.timestamp();
timestamp->clear();
timestamp->appendFormat("[%d.%03d]", (int) (ts / (1000 * 1000 * 1000)),
(int) ((ts / (1000 * 1000)) % 1000));
@@ -1051,7 +1048,7 @@
// TODO check length for event type is correct
- if (event == EVENT_END_FMT) {
+ if (event == EVENT_FMT_END) {
break;
}
@@ -1060,31 +1057,31 @@
switch(fmt[fmt_offset])
{
case 's': // string
- ALOGW_IF(event != EVENT_STRING,
+ ALOGW_IF(event != EVENT_FMT_STRING,
"NBLog Reader incompatible event for string specifier: %d", event);
body->append((const char*) datum, length);
break;
case 't': // timestamp
- ALOGW_IF(event != EVENT_TIMESTAMP,
+ ALOGW_IF(event != EVENT_FMT_TIMESTAMP,
"NBLog Reader incompatible event for timestamp specifier: %d", event);
appendTimestamp(body, datum);
break;
case 'd': // integer
- ALOGW_IF(event != EVENT_INTEGER,
+ ALOGW_IF(event != EVENT_FMT_INTEGER,
"NBLog Reader incompatible event for integer specifier: %d", event);
appendInt(body, datum);
break;
case 'f': // float
- ALOGW_IF(event != EVENT_FLOAT,
+ ALOGW_IF(event != EVENT_FMT_FLOAT,
"NBLog Reader incompatible event for float specifier: %d", event);
appendFloat(body, datum);
break;
case 'p': // pid
- ALOGW_IF(event != EVENT_PID,
+ ALOGW_IF(event != EVENT_FMT_PID,
"NBLog Reader incompatible event for pid specifier: %d", event);
appendPID(body, datum, length);
break;
@@ -1094,7 +1091,7 @@
}
++arg;
}
- ALOGW_IF(arg->type != EVENT_END_FMT, "Expected end of format, got %d", arg->type);
+ ALOGW_IF(arg->type != EVENT_FMT_END, "Expected end of format, got %d", arg->type);
return arg;
}
diff --git a/media/libnblog/PerformanceAnalysis.cpp b/media/libnblog/PerformanceAnalysis.cpp
index 3418dc0..ca9bc2c 100644
--- a/media/libnblog/PerformanceAnalysis.cpp
+++ b/media/libnblog/PerformanceAnalysis.cpp
@@ -17,13 +17,15 @@
#define LOG_TAG "PerformanceAnalysis"
// #define LOG_NDEBUG 0
+// #define WRITE_TO_FILE
#include <algorithm>
#include <climits>
#include <deque>
-#include <iostream>
#include <math.h>
#include <numeric>
+#include <sstream>
+#include <string>
#include <vector>
#include <stdarg.h>
#include <stdint.h>
@@ -39,6 +41,7 @@
#include <media/nblog/ReportPerformance.h>
#include <utils/Log.h>
#include <utils/String8.h>
+#include <utils/Timers.h>
#include <queue>
#include <utility>
@@ -47,6 +50,65 @@
namespace ReportPerformance {
+void Histogram::add(double value)
+{
+ // TODO Handle domain and range error exceptions?
+ const int binIndex = lround((value - mLow) / mBinSize);
+ if (binIndex < 0) {
+ mLowCount++;
+ } else if (binIndex >= mNumBins) {
+ mHighCount++;
+ } else {
+ mBins[binIndex]++;
+ }
+ mTotalCount++;
+}
+
+void Histogram::clear()
+{
+ std::fill(mBins.begin(), mBins.end(), 0);
+ mLowCount = 0;
+ mHighCount = 0;
+ mTotalCount = 0;
+}
+
+uint64_t Histogram::totalCount() const
+{
+ return mTotalCount;
+}
+
+std::string Histogram::toString() const {
+ std::stringstream ss;
+ static constexpr char kDivider = '|';
+ ss << mBinSize << "," << mNumBins << "," << mLow << ",{";
+ bool first = true;
+ if (mLowCount != 0) {
+ ss << "-1" << kDivider << mLowCount;
+ first = false;
+ }
+ for (size_t i = 0; i < mNumBins; i++) {
+ if (mBins[i] != 0) {
+ if (!first) {
+ ss << ",";
+ }
+ ss << i << kDivider << mBins[i];
+ first = false;
+ }
+ }
+ if (mHighCount != 0) {
+ if (!first) {
+ ss << ",";
+ }
+ ss << mNumBins << kDivider << mHighCount;
+ first = false;
+ }
+ ss << "}";
+
+ return ss.str();
+}
+
+//------------------------------------------------------------------------------
+
// Given an audio processing wakeup timestamp, buckets the time interval
// since the previous timestamp into a histogram, searches for
// outliers, analyzes the outlier series for unexpectedly
@@ -277,7 +339,9 @@
// writes summary of performance into specified file descriptor
void dump(int fd, int indent, PerformanceAnalysisMap &threadPerformanceAnalysis) {
String8 body;
+#ifdef WRITE_TO_FILE
const char* const kDirectory = "/data/misc/audioserver/";
+#endif
for (auto & thread : threadPerformanceAnalysis) {
for (auto & hash: thread.second) {
PerformanceAnalysis& curr = hash.second;
@@ -287,9 +351,11 @@
dumpLine(fd, indent, body);
body.clear();
}
- // write to file
+#ifdef WRITE_TO_FILE
+ // write to file. Enable by uncommenting macro at top of file.
writeToFile(curr.mHists, curr.mOutlierData, curr.mPeakTimestamps,
kDirectory, false, thread.first, hash.first);
+#endif
}
}
}
diff --git a/media/libnblog/ReportPerformance.cpp b/media/libnblog/ReportPerformance.cpp
index 827e731..aab67c6 100644
--- a/media/libnblog/ReportPerformance.cpp
+++ b/media/libnblog/ReportPerformance.cpp
@@ -18,6 +18,7 @@
#include <fstream>
#include <iostream>
+#include <memory>
#include <queue>
#include <stdarg.h>
#include <stdint.h>
@@ -27,6 +28,8 @@
#include <sys/prctl.h>
#include <sys/time.h>
#include <utility>
+#include <json/json.h>
+#include <media/MediaAnalyticsItem.h>
#include <media/nblog/NBLog.h>
#include <media/nblog/PerformanceAnalysis.h>
#include <media/nblog/ReportPerformance.h>
@@ -37,13 +40,93 @@
namespace ReportPerformance {
+std::unique_ptr<Json::Value> dumpToJson(const PerformanceData& data)
+{
+ std::unique_ptr<Json::Value> rootPtr = std::make_unique<Json::Value>(Json::objectValue);
+ Json::Value& root = *rootPtr;
+ root["type"] = data.type;
+ root["frameCount"] = (Json::Value::Int)data.frameCount;
+ root["sampleRate"] = (Json::Value::Int)data.sampleRate;
+ root["workMsHist"] = data.workHist.toString();
+ root["latencyMsHist"] = data.latencyHist.toString();
+ root["warmupMsHist"] = data.warmupHist.toString();
+ root["underruns"] = (Json::Value::Int64)data.underruns;
+ root["overruns"] = (Json::Value::Int64)data.overruns;
+ root["activeMs"] = (Json::Value::Int64)ns2ms(data.active);
+ root["durationMs"] = (Json::Value::Int64)ns2ms(systemTime() - data.start);
+ return rootPtr;
+}
+
+bool sendToMediaMetrics(const PerformanceData& data)
+{
+ // See documentation for these metrics here:
+ // docs.google.com/document/d/11--6dyOXVOpacYQLZiaOY5QVtQjUyqNx2zT9cCzLKYE/edit?usp=sharing
+ static constexpr char kThreadType[] = "android.media.audiothread.type";
+ static constexpr char kThreadFrameCount[] = "android.media.audiothread.framecount";
+ static constexpr char kThreadSampleRate[] = "android.media.audiothread.samplerate";
+ static constexpr char kThreadWorkHist[] = "android.media.audiothread.workMs.hist";
+ static constexpr char kThreadLatencyHist[] = "android.media.audiothread.latencyMs.hist";
+ static constexpr char kThreadWarmupHist[] = "android.media.audiothread.warmupMs.hist";
+ static constexpr char kThreadUnderruns[] = "android.media.audiothread.underruns";
+ static constexpr char kThreadOverruns[] = "android.media.audiothread.overruns";
+ static constexpr char kThreadActive[] = "android.media.audiothread.activeMs";
+ static constexpr char kThreadDuration[] = "android.media.audiothread.durationMs";
+
+ std::unique_ptr<MediaAnalyticsItem> item(new MediaAnalyticsItem("audiothread"));
+
+ const Histogram &workHist = data.workHist;
+ if (workHist.totalCount() > 0) {
+ item->setCString(kThreadWorkHist, workHist.toString().c_str());
+ }
+
+ const Histogram &latencyHist = data.latencyHist;
+ if (latencyHist.totalCount() > 0) {
+ item->setCString(kThreadLatencyHist, latencyHist.toString().c_str());
+ }
+
+ const Histogram &warmupHist = data.warmupHist;
+ if (warmupHist.totalCount() > 0) {
+ item->setCString(kThreadWarmupHist, warmupHist.toString().c_str());
+ }
+
+ if (data.underruns > 0) {
+ item->setInt64(kThreadUnderruns, data.underruns);
+ }
+
+ if (data.overruns > 0) {
+ item->setInt64(kThreadOverruns, data.overruns);
+ }
+
+ // Send to Media Metrics if the record is not empty.
+ // The thread and time info are added inside the if statement because
+ // we want to send them only if there are performance metrics to send.
+ if (item->count() > 0) {
+ // Add thread info fields.
+ const int type = data.type;
+ // TODO have a int-to-string mapping defined somewhere else for other thread types.
+ if (type == 2) {
+ item->setCString(kThreadType, "FASTMIXER");
+ } else {
+ item->setCString(kThreadType, "UNKNOWN");
+ }
+ item->setInt32(kThreadFrameCount, data.frameCount);
+ item->setInt32(kThreadSampleRate, data.sampleRate);
+ // Add time info fields.
+ item->setInt64(kThreadActive, data.active / 1000000);
+ item->setInt64(kThreadDuration, (systemTime() - data.start) / 1000000);
+ return item->selfrecord();
+ }
+ return false;
+}
+
+//------------------------------------------------------------------------------
// TODO: use a function like this to extract logic from writeToFile
// https://stackoverflow.com/a/9279620
// Writes outlier intervals, timestamps, and histograms spanning long time intervals to file.
// TODO: write data in binary format
-void writeToFile(const std::deque<std::pair<timestamp, Histogram>> &hists,
+void writeToFile(const std::deque<std::pair<timestamp, Hist>> &hists,
const std::deque<std::pair<msInterval, timestamp>> &outlierData,
const std::deque<timestamp> &peakTimestamps,
const char * directory, bool append, int author, log_hash_t hash) {
diff --git a/media/libnblog/include/media/nblog/NBLog.h b/media/libnblog/include/media/nblog/NBLog.h
index c9bfaae..e43b026 100644
--- a/media/libnblog/include/media/nblog/NBLog.h
+++ b/media/libnblog/include/media/nblog/NBLog.h
@@ -20,6 +20,8 @@
#define ANDROID_MEDIA_NBLOG_H
#include <map>
+#include <memory>
+#include <type_traits>
#include <unordered_set>
#include <vector>
@@ -29,6 +31,7 @@
#include <media/nblog/ReportPerformance.h>
#include <utils/Mutex.h>
#include <utils/threads.h>
+#include <utils/Timers.h>
namespace android {
@@ -38,40 +41,85 @@
public:
- using log_hash_t = ReportPerformance::log_hash_t;
+ using log_hash_t = uint64_t;
// FIXME Everything needed for client (writer API and registration) should be isolated
// from the rest of the implementation.
class Writer;
class Reader;
+ // TODO have a comment somewhere explaining the whole process for adding a new EVENT_
+
+ // NBLog Event types. The Events are named to provide contextual meaning for what is logged.
+ // If adding a new standalone Event here, update the event-to-type mapping by adding a
+ // MAP_EVENT_TO_TYPE statement below.
enum Event : uint8_t {
EVENT_RESERVED,
EVENT_STRING, // ASCII string, not NUL-terminated
// TODO: make timestamp optional
EVENT_TIMESTAMP, // clock_gettime(CLOCK_MONOTONIC)
- EVENT_INTEGER, // integer value entry
- EVENT_FLOAT, // floating point value entry
- EVENT_PID, // process ID and process name
- EVENT_AUTHOR, // author index (present in merged logs) tracks entry's
- // original log
- EVENT_START_FMT, // logFormat start event: entry includes format string,
+
+ // Types for Format Entry, i.e. formatted entry
+ EVENT_FMT_START, // logFormat start event: entry includes format string,
// following entries contain format arguments
- EVENT_HASH, // unique HASH of log origin, originates from hash of file name
+ // format arguments
+ EVENT_FMT_TIMESTAMP, // timestamp value entry
+ EVENT_FMT_HASH, // unique HASH of log origin, originates from hash of file name
// and line number
+ EVENT_FMT_STRING, // string value entry
+ EVENT_FMT_INTEGER, // integer value entry
+ EVENT_FMT_FLOAT, // floating point value entry
+ EVENT_FMT_PID, // process ID and process name
+ EVENT_FMT_AUTHOR, // author index (present in merged logs) tracks entry's
+ // original log
+ // end of format arguments
+ EVENT_FMT_END, // end of logFormat argument list
+
+ // Types for wakeup timestamp histograms
EVENT_HISTOGRAM_ENTRY_TS, // single datum for timestamp histogram
EVENT_AUDIO_STATE, // audio on/off event: logged on FastMixer::onStateChange call
- EVENT_END_FMT, // end of logFormat argument list
// Types representing audio performance metrics
- EVENT_LATENCY, // TODO classify specifically what this is
- EVENT_CPU_FREQUENCY, // instantaneous CPU frequency in kHz
- EVENT_MONOTONIC_CYCLE_TIME, // thread per-cycle monotonic time
- EVENT_CPU_CYCLE_TIME, // thread per-cycle cpu time
+ EVENT_THREAD_INFO, // thread type, frameCount and sampleRate, which give context
+ // for the metrics below.
+ EVENT_LATENCY, // difference between frames presented by HAL and frames
+ // written to HAL output sink, divided by sample rate.
+ EVENT_WORK_TIME, // the time a thread takes to do work, e.g. read, write, etc.
+ EVENT_WARMUP_TIME, // thread warmup time
+ EVENT_UNDERRUN, // predicted thread underrun event timestamp
+ EVENT_OVERRUN, // predicted thread overrun event timestamp
EVENT_UPPER_BOUND, // to check for invalid events
};
+ // NBLog custom-defined structs. Some NBLog Event types map to these structs.
+
+ // mapped from EVENT_THREAD_INFO
+ struct thread_info_t {
+ // TODO make type an enum
+ int type; // Thread type: 0 for MIXER, 1 for CAPTURE,
+ // 2 for FASTMIXER, 3 for FASTCAPTURE
+ size_t frameCount; // number of frames per read or write buffer
+ unsigned sampleRate; // in frames per second
+ };
+
+ template <Event E> struct get_mapped;
+#define MAP_EVENT_TO_TYPE(E, T) \
+ template<> struct get_mapped<E> { \
+ static_assert(std::is_trivially_copyable<T>::value \
+ && !std::is_pointer<T>::value, \
+ "NBLog::Event must map to trivially copyable, non-pointer type."); \
+ typedef T type; \
+ }
+
+ // Maps an NBLog Event type to a C++ POD type.
+ MAP_EVENT_TO_TYPE(EVENT_THREAD_INFO, thread_info_t);
+ MAP_EVENT_TO_TYPE(EVENT_LATENCY, double);
+ MAP_EVENT_TO_TYPE(EVENT_WORK_TIME, int64_t);
+ MAP_EVENT_TO_TYPE(EVENT_WARMUP_TIME, double);
+ MAP_EVENT_TO_TYPE(EVENT_UNDERRUN, int64_t);
+ MAP_EVENT_TO_TYPE(EVENT_OVERRUN, int64_t);
+
private:
// ---------------------------------------------------------------------------
@@ -119,10 +167,24 @@
void copyTo(std::unique_ptr<audio_utils_fifo_writer> &dst) const;
void copyData(uint8_t *dst) const;
+ // memcpy preferred to reinterpret_cast to avoid potentially unsupported
+ // unaligned memory access.
+#if 0
template<typename T>
inline const T& payload() {
return *reinterpret_cast<const T *>(mPtr + offsetof(entry, data));
}
+#else
+ template<typename T>
+ inline T payload() const {
+ static_assert(std::is_trivially_copyable<T>::value
+ && !std::is_pointer<T>::value,
+ "NBLog::EntryIterator payload must be trivially copyable, non-pointer type.");
+ T payload;
+ memcpy(&payload, mPtr + offsetof(entry, data), sizeof(payload));
+ return payload;
+ }
+#endif
inline operator const uint8_t*() const {
return mPtr;
@@ -169,14 +231,14 @@
// API for handling format entry operations
// a formatted entry has the following structure:
- // * START_FMT entry, containing the format string
+ // * FMT_START entry, containing the format string
// * TIMESTAMP entry
// * HASH entry
// * author entry of the thread that generated it (optional, present in merged log)
// * format arg1
// * format arg2
// * ...
- // * END_FMT entry
+ // * FMT_END entry
class FormatEntry : public AbstractEntry {
public:
// explicit FormatEntry(const EntryIterator &it);
@@ -262,6 +324,7 @@
offsetof(ending, length);
};
+ // TODO move these somewhere else
struct HistTsEntry {
log_hash_t hash;
int64_t ts;
@@ -320,6 +383,8 @@
};
// ---------------------------------------------------------------------------
+ // NBLog Writer API
+ // ---------------------------------------------------------------------------
// Writer is thread-safe with respect to Reader, but not with respect to multiple threads
// calling Writer methods. If you need multi-thread safety for writing, use LockedWriter.
@@ -335,23 +400,22 @@
virtual ~Writer();
// FIXME needs comments, and some should be private
- virtual void log(const char *string);
- virtual void logf(const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
- virtual void logvf(const char *fmt, va_list ap);
- virtual void logTimestamp();
- virtual void logTimestamp(const int64_t ts);
- virtual void logInteger(const int x);
- virtual void logFloat(const float x);
- virtual void logPID();
- virtual void logStart(const char *fmt);
- virtual void logEnd();
- virtual void logHash(log_hash_t hash);
- // The functions below are not in LockedWriter yet.
- virtual void logFormat(const char *fmt, log_hash_t hash, ...);
- virtual void logVFormat(const char *fmt, log_hash_t hash, va_list ap);
- virtual void logEventHistTs(Event event, log_hash_t hash);
- virtual void logMonotonicCycleTime(uint32_t monotonicNs);
- // End of functions that are not in LockedWriter yet.
+ void log(const char *string);
+ void logf(const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
+ void logTimestamp();
+ void logFormat(const char *fmt, log_hash_t hash, ...);
+ void logEventHistTs(Event event, log_hash_t hash);
+
+ // Log data related to Event E. See the event-to-type mapping for the type of data
+ // corresponding to the event. For example, if you see a mapping statement:
+ // MAP_TYPE_TO_EVENT(E, T);
+ // then the usage of this method would be:
+ // T data = doComputation();
+ // tlNBLogWriter->log<NBLog::E>(data);
+ template<Event E>
+ void log(typename get_mapped<E>::type data) {
+ log(E, &data, sizeof(data));
+ }
virtual bool isEnabled() const;
@@ -362,12 +426,25 @@
sp<IMemory> getIMemory() const { return mIMemory; }
+ // Public logging function implementations should always use one of the
+ // two log() function calls below to write to shared memory.
+ protected:
+ // Writes a single Entry to the FIFO if the writer is enabled.
+ // This is protected and virtual because LockedWriter uses a lock to protect
+ // writing to the FIFO before writing to this function.
+ virtual void log(const Entry &entry, bool trusted = false);
+
private:
// 0 <= length <= kMaxLength
- // writes a single Entry to the FIFO
+ // Log a single Entry with corresponding event, data, and length.
void log(Event event, const void *data, size_t length);
- // checks validity of an event before calling log above this one
- void log(const Entry &entry, bool trusted = false);
+
+ void logvf(const char *fmt, va_list ap);
+
+ // helper functions for logging parts of a formatted entry
+ void logStart(const char *fmt);
+ void logTimestampFormat();
+ void logVFormat(const char *fmt, log_hash_t hash, va_list ap);
Shared* const mShared; // raw pointer to shared memory
sp<IMemory> mIMemory; // ref-counted version, initialized in constructor
@@ -392,54 +469,20 @@
LockedWriter();
LockedWriter(void *shared, size_t size);
- virtual void log(const char *string);
- virtual void logf(const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
- virtual void logvf(const char *fmt, va_list ap);
- virtual void logTimestamp();
- virtual void logTimestamp(const int64_t ts);
- virtual void logInteger(const int x);
- virtual void logFloat(const float x);
- virtual void logPID();
- virtual void logStart(const char *fmt);
- virtual void logEnd();
- virtual void logHash(log_hash_t hash);
-
- virtual bool isEnabled() const;
- virtual bool setEnabled(bool enabled);
+ bool isEnabled() const override;
+ bool setEnabled(bool enabled) override;
private:
+ // Lock needs to be obtained before writing to FIFO.
+ void log(const Entry &entry, bool trusted = false) override;
mutable Mutex mLock;
};
// ---------------------------------------------------------------------------
+ // NBLog Reader API
+ // ---------------------------------------------------------------------------
- // A snapshot of a readers buffer
- // This is raw data. No analysis has been done on it
- class Snapshot {
- public:
- Snapshot() = default;
-
- explicit Snapshot(size_t bufferSize) : mData(new uint8_t[bufferSize]) {}
-
- ~Snapshot() { delete[] mData; }
-
- // amount of data lost (given by audio_utils_fifo_reader)
- size_t lost() const { return mLost; }
-
- // iterator to beginning of readable segment of snapshot
- // data between begin and end has valid entries
- EntryIterator begin() const { return mBegin; }
-
- // iterator to end of readable segment of snapshot
- EntryIterator end() const { return mEnd; }
-
- private:
- friend class Reader;
- uint8_t * const mData{};
- size_t mLost{0};
- EntryIterator mBegin;
- EntryIterator mEnd;
- };
+ class Snapshot; // Forward declaration needed for Reader::getSnapshot()
class Reader : public RefBase {
public:
@@ -455,10 +498,14 @@
const std::string &name() const { return mName; }
private:
+ // Amount of tries for reader to catch up with writer in getSnapshot().
static constexpr int kMaxObtainTries = 3;
- // startingTypes and endingTypes are used to check for log corruption.
- static const std::unordered_set<Event> startingTypes;
- static const std::unordered_set<Event> endingTypes;
+
+ // invalidBeginTypes and invalidEndTypes are used to align the Snapshot::begin() and
+ // Snapshot::end() EntryIterators to valid entries.
+ static const std::unordered_set<Event> invalidBeginTypes;
+ static const std::unordered_set<Event> invalidEndTypes;
+
// declared as const because audio_utils_fifo() constructor
sp<IMemory> mIMemory; // ref-counted version, assigned only in constructor
@@ -469,12 +516,39 @@
audio_utils_fifo_reader * const mFifoReader; // used to read from FIFO,
// non-NULL unless constructor fails
- // Searches for the last entry of type <type> in the range [front, back)
+ // Searches for the last valid entry in the range [front, back)
// back has to be entry-aligned. Returns nullptr if none enconuntered.
- static const uint8_t *findLastEntryOfTypes(const uint8_t *front, const uint8_t *back,
- const std::unordered_set<Event> &types);
+ static const uint8_t *findLastValidEntry(const uint8_t *front, const uint8_t *back,
+ const std::unordered_set<Event> &invalidTypes);
};
+ // A snapshot of a readers buffer
+ // This is raw data. No analysis has been done on it
+ class Snapshot {
+ public:
+ ~Snapshot() { delete[] mData; }
+
+ // amount of data lost (given by audio_utils_fifo_reader)
+ size_t lost() const { return mLost; }
+
+ // iterator to beginning of readable segment of snapshot
+ // data between begin and end has valid entries
+ EntryIterator begin() const { return mBegin; }
+
+ // iterator to end of readable segment of snapshot
+ EntryIterator end() const { return mEnd; }
+
+ private:
+ Snapshot() = default;
+ explicit Snapshot(size_t bufferSize) : mData(new uint8_t[bufferSize]) {}
+ friend std::unique_ptr<Snapshot> Reader::getSnapshot();
+ uint8_t * const mData = nullptr;
+ size_t mLost = 0;
+ EntryIterator mBegin;
+ EntryIterator mEnd;
+ };
+
+ // TODO move this to MediaLogService?
class DumpReader : public Reader {
public:
DumpReader(const void *shared, size_t size, const std::string &name)
@@ -490,12 +564,14 @@
static void appendFloat(String8 *body, const void *data);
static void appendPID(String8 *body, const void *data, size_t length);
static void appendTimestamp(String8 *body, const void *data);
- //static size_t fmtEntryLength(const uint8_t *data); // TODO Eric remove if not used
+
+ // The bufferDump functions are used for debugging only.
static String8 bufferDump(const uint8_t *buffer, size_t size);
static String8 bufferDump(const EntryIterator &it);
};
// ---------------------------------------------------------------------------
+ // TODO move Merger, MergeReader, and MergeThread to a separate file.
// This class is used to read data from each thread's individual FIFO in shared memory
// and write it to a single FIFO in local memory.
@@ -531,11 +607,17 @@
MergeReader(const void *shared, size_t size, Merger &merger);
void dump(int fd, int indent = 0);
+
// process a particular snapshot of the reader
- void getAndProcessSnapshot(Snapshot &snap, int author);
+ void processSnapshot(Snapshot &snap, int author);
+
// call getSnapshot of the content of the reader's buffer and process the data
void getAndProcessSnapshot();
+ // check for periodic push of performance data to media metrics, and perform
+ // the send if it is time to do so.
+ void checkPushToMediaMetrics();
+
private:
// FIXME Needs to be protected by a lock,
// because even though our use of it is read-only there may be asynchronous updates
@@ -547,6 +629,13 @@
// location within each author
ReportPerformance::PerformanceAnalysisMap mThreadPerformanceAnalysis;
+ // compresses and stores audio performance data from each thread's buffers.
+ // first parameter is author, i.e. thread index.
+ std::map<int, ReportPerformance::PerformanceData> mThreadPerformanceData;
+
+ // how often to push data to Media Metrics
+ static constexpr nsecs_t kPeriodicMediaMetricsPush = s2ns((nsecs_t)2 * 60 * 60); // 2 hours
+
// handle author entry by looking up the author's name and appending it to the body
// returns number of bytes read from fmtEntry
void handleAuthor(const AbstractEntry &fmtEntry, String8 *body);
@@ -594,15 +683,6 @@
}; // class NBLog
-// TODO put somewhere else
-static inline int64_t get_monotonic_ns() {
- timespec ts;
- if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
- return (uint64_t) ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec;
- }
- return 0; // should not happen.
-}
-
} // namespace android
#endif // ANDROID_MEDIA_NBLOG_H
diff --git a/media/libnblog/include/media/nblog/PerformanceAnalysis.h b/media/libnblog/include/media/nblog/PerformanceAnalysis.h
index 56e0ea6..80eddb1 100644
--- a/media/libnblog/include/media/nblog/PerformanceAnalysis.h
+++ b/media/libnblog/include/media/nblog/PerformanceAnalysis.h
@@ -19,9 +19,11 @@
#include <deque>
#include <map>
+#include <utility>
#include <vector>
#include <media/nblog/ReportPerformance.h>
+#include <utils/Timers.h>
namespace android {
@@ -29,6 +31,145 @@
namespace ReportPerformance {
+// TODO make this a templated class and put it in a separate file.
+// The templated parameters would be bin size and low limit.
+/*
+ * Histogram provides a way to store numeric data in histogram format and read it as a serialized
+ * string. The terms "bin" and "bucket" are used interchangeably.
+ *
+ * This class is not thread-safe.
+ */
+class Histogram {
+public:
+ struct Config {
+ const double binSize; // TODO template type
+ const size_t numBins;
+ const double low; // TODO template type
+ };
+
+ // Histograms are constructed with fixed configuration numbers. Dynamic configuration based
+ // the data is possible but complex because
+ // - data points are added one by one, not processed as a batch.
+ // - Histograms with different configuration parameters are tricky to aggregate, and they
+ // will need to be aggregated at the Media Metrics cloud side.
+ // - not providing limits theoretically allows for infinite number of buckets.
+
+ /**
+ * \brief Creates a Histogram object.
+ *
+ * \param binSize the width of each bin of the histogram.
+ * Units are whatever data the caller decides to store.
+ * \param numBins the number of bins desired in the histogram range.
+ * \param low the lower bound of the histogram bucket values.
+ * Units are whatever data the caller decides to store.
+ * Note that the upper bound can be calculated by the following:
+ * upper = lower + binSize * numBins.
+ */
+ Histogram(double binSize, size_t numBins, double low = 0.)
+ : mBinSize(binSize), mNumBins(numBins), mLow(low), mBins(mNumBins) {}
+
+ Histogram(const Config &c)
+ : Histogram(c.binSize, c.numBins, c.low) {}
+
+ /**
+ * \brief Add a data point to the histogram. The value of the data point
+ * is rounded to the nearest multiple of the bin size (before accounting
+ * for the lower bound offset, which may not be a multiple of the bin size).
+ *
+ * \param value the value of the data point to add.
+ */
+ void add(double value);
+
+ /**
+ * \brief Removes all data points from the histogram.
+ */
+ void clear();
+
+ /**
+ * \brief Returns the total number of data points added to the histogram.
+ *
+ * \return the total number of data points in the histogram.
+ */
+ uint64_t totalCount() const;
+
+ /**
+ * \brief Serializes the histogram into a string. The format is chosen to be compatible with
+ * the histogram representation to send to the Media Metrics service.
+ *
+ * The string is as follows:
+ * binSize,numBins,low,{-1|lowCount,...,binIndex|count,...,numBins|highCount}
+ *
+ * - binIndex is an integer with 0 <= binIndex < numBins.
+ * - count is the number of occurrences of the (rounded) value
+ * low + binSize * bucketIndex.
+ * - lowCount is the number of (rounded) values less than low.
+ * - highCount is the number of (rounded) values greater than or equal to
+ * low + binSize * numBins.
+ * - a binIndex may be skipped if its count is 0.
+ *
+ * \return the histogram serialized as a string.
+ */
+ std::string toString() const;
+
+private:
+ const double mBinSize; // Size of each bucket
+ const size_t mNumBins; // Number of buckets in histogram range
+ const double mLow; // Lower bound of values
+ std::vector<int> mBins; // Data structure to store the actual histogram
+
+ int mLowCount = 0; // Number of values less than mLow
+ int mHighCount = 0; // Number of values >= mLow + mBinSize * mNumBins
+ uint64_t mTotalCount = 0; // Total number of values recorded
+};
+
+// This is essentially the same as class PerformanceAnalysis, but PerformanceAnalysis
+// also does some additional analyzing of data, while the purpose of this struct is
+// to hold data.
+struct PerformanceData {
+ // Values based on mUnderrunNs and mOverrunNs in FastMixer.cpp for frameCount = 192
+ // and mSampleRate = 48000, which correspond to 2 and 7 seconds.
+ static constexpr Histogram::Config kWorkConfig = { 0.25, 20, 2.};
+
+ // Values based on trial and error logging. Need a better way to determine
+ // bin size and lower/upper limits.
+ static constexpr Histogram::Config kLatencyConfig = { 2., 10, 10.};
+
+ // Values based on trial and error logging. Need a better way to determine
+ // bin size and lower/upper limits.
+ static constexpr Histogram::Config kWarmupConfig = { 5., 10, 10.};
+
+ // Thread Info
+ // TODO make type an enum
+ int type = -1; // Thread type: 0 for MIXER, 1 for CAPTURE,
+ // 2 for FASTMIXER, 3 for FASTCAPTURE
+ size_t frameCount = 0;
+ unsigned sampleRate = 0;
+
+ // Performance Data
+ Histogram workHist{kWorkConfig};
+ Histogram latencyHist{kLatencyConfig};
+ Histogram warmupHist{kWarmupConfig};
+ int64_t underruns = 0;
+ int64_t overruns = 0;
+ nsecs_t active = 0;
+ nsecs_t start{systemTime()};
+
+ // Reset the performance data. This does not represent a thread state change.
+ // Thread info is not reset here because the data is meant to be a continuation of the thread
+ // that struct PerformanceData is associated with.
+ void reset() {
+ workHist.clear();
+ latencyHist.clear();
+ warmupHist.clear();
+ underruns = 0;
+ overruns = 0;
+ active = 0;
+ start = systemTime();
+ }
+};
+
+//------------------------------------------------------------------------------
+
class PerformanceAnalysis;
// a map of PerformanceAnalysis instances
@@ -82,7 +223,7 @@
std::deque<timestamp> mPeakTimestamps;
// stores buffer period histograms with timestamp of first sample
- std::deque<std::pair<timestamp, Histogram>> mHists;
+ std::deque<std::pair<timestamp, Hist>> mHists;
// Parameters used when detecting outliers
struct BufferPeriod {
diff --git a/media/libnblog/include/media/nblog/ReportPerformance.h b/media/libnblog/include/media/nblog/ReportPerformance.h
index 1b11197..09bb2a0 100644
--- a/media/libnblog/include/media/nblog/ReportPerformance.h
+++ b/media/libnblog/include/media/nblog/ReportPerformance.h
@@ -19,19 +19,38 @@
#include <deque>
#include <map>
+#include <memory>
#include <vector>
+namespace Json {
+class Value;
+}
+
namespace android {
namespace ReportPerformance {
+struct PerformanceData;
+
+// Dumps performance data to a JSON format.
+// Return by pointer instead of by value to avoid dependency side effects of including
+// the header of an external library.
+std::unique_ptr<Json::Value> dumpToJson(const PerformanceData& data);
+
+// Send one thread's data to media metrics, if the performance data is nontrivial (i.e. not
+// all zero values). Return true if data was sent, false if there is nothing to write
+// or an error occurred while writing.
+bool sendToMediaMetrics(const PerformanceData& data);
+
+//------------------------------------------------------------------------------
+
constexpr int kMsPerSec = 1000;
constexpr int kSecPerMin = 60;
constexpr int kJiffyPerMs = 10; // time unit for histogram as a multiple of milliseconds
// stores a histogram: key: observed buffer period (multiple of jiffy). value: count
-using Histogram = std::map<int, int>;
+using Hist = std::map<int, int>;
using msInterval = double;
using jiffyInterval = double;
@@ -54,7 +73,7 @@
}
// Writes outlier intervals, timestamps, peaks timestamps, and histograms to a file.
-void writeToFile(const std::deque<std::pair<timestamp, Histogram>> &hists,
+void writeToFile(const std::deque<std::pair<timestamp, Hist>> &hists,
const std::deque<std::pair<msInterval, timestamp>> &outlierData,
const std::deque<timestamp> &peakTimestamps,
const char * kDirectory, bool append, int author, log_hash_t hash);
diff --git a/media/libstagefright/foundation/AHierarchicalStateMachine.cpp b/media/libstagefright/AHierarchicalStateMachine.cpp
similarity index 97%
rename from media/libstagefright/foundation/AHierarchicalStateMachine.cpp
rename to media/libstagefright/AHierarchicalStateMachine.cpp
index b837f66..f89b8b0 100644
--- a/media/libstagefright/foundation/AHierarchicalStateMachine.cpp
+++ b/media/libstagefright/AHierarchicalStateMachine.cpp
@@ -18,7 +18,7 @@
#define LOG_TAG "AHierarchicalStateMachine"
#include <utils/Log.h>
-#include <media/stagefright/foundation/AHierarchicalStateMachine.h>
+#include <media/stagefright/AHierarchicalStateMachine.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index fec40b6..ae2f61b 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -90,9 +90,10 @@
name: "libstagefright",
srcs: [
+ "AACWriter.cpp",
"ACodec.cpp",
"ACodecBufferChannel.cpp",
- "AACWriter.cpp",
+ "AHierarchicalStateMachine.cpp",
"AMRWriter.cpp",
"AudioPlayer.cpp",
"AudioPresentationInfo.cpp",
diff --git a/media/libstagefright/MediaExtractorFactory.cpp b/media/libstagefright/MediaExtractorFactory.cpp
index 72ddb71..a0a3a75 100644
--- a/media/libstagefright/MediaExtractorFactory.cpp
+++ b/media/libstagefright/MediaExtractorFactory.cpp
@@ -319,12 +319,6 @@
#endif
"/extractors", *newList);
- RegisterExtractorsInSystem("/vendor/lib"
-#ifdef __LP64__
- "64"
-#endif
- "/extractors", *newList);
-
if (newUpdateApkPath != nullptr) {
RegisterExtractorsInApk(newUpdateApkPath, *newList);
}
diff --git a/media/libstagefright/TEST_MAPPING b/media/libstagefright/TEST_MAPPING
new file mode 100644
index 0000000..96818eb
--- /dev/null
+++ b/media/libstagefright/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "postsubmit": [
+ {
+ "name": "CtsMediaTestCases",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.RequiresDevice"
+ }
+ ]
+ }
+ ]
+}
diff --git a/media/libstagefright/bqhelper/Conversion.cpp b/media/libstagefright/bqhelper/Conversion.cpp
index ffed005..91d7c74 100644
--- a/media/libstagefright/bqhelper/Conversion.cpp
+++ b/media/libstagefright/bqhelper/Conversion.cpp
@@ -98,6 +98,17 @@
*/
/**
+ * \brief Convert `Return<void>` to `status_t`. This is for legacy binder calls.
+ *
+ * \param[in] t The source `Return<void>`.
+ * \return The corresponding `status_t`.
+ */
+// convert: Return<void> -> status_t
+status_t toStatusT(Return<void> const& t) {
+ return t.isOk() ? OK : (t.isDeadObject() ? DEAD_OBJECT : UNKNOWN_ERROR);
+}
+
+/**
* \brief Convert `Return<void>` to `binder::Status`.
*
* \param[in] t The source `Return<void>`.
@@ -107,22 +118,11 @@
::android::binder::Status toBinderStatus(
Return<void> const& t) {
return ::android::binder::Status::fromExceptionCode(
- t.isOk() ? OK : UNKNOWN_ERROR,
+ toStatusT(t),
t.description().c_str());
}
/**
- * \brief Convert `Return<void>` to `status_t`. This is for legacy binder calls.
- *
- * \param[in] t The source `Return<void>`.
- * \return The corresponding `status_t`.
- */
-// convert: Return<void> -> status_t
-status_t toStatusT(Return<void> const& t) {
- return t.isOk() ? OK : UNKNOWN_ERROR;
-}
-
-/**
* \brief Wrap `native_handle_t*` in `hidl_handle`.
*
* \param[in] nh The source `native_handle_t*`.
diff --git a/media/libstagefright/foundation/ANetworkSession.cpp b/media/libstagefright/foundation/ANetworkSession.cpp
deleted file mode 100644
index eafdc37..0000000
--- a/media/libstagefright/foundation/ANetworkSession.cpp
+++ /dev/null
@@ -1,1401 +0,0 @@
-/*
- * Copyright 2012, 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 "NetworkSession"
-#include <utils/Log.h>
-
-#include "ANetworkSession.h"
-#include "ParsedMessage.h"
-
-#include <arpa/inet.h>
-#include <fcntl.h>
-#include <linux/tcp.h>
-#include <net/if.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-
-#include <media/stagefright/foundation/ABuffer.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/foundation/ByteUtils.h>
-#include <media/stagefright/foundation/hexdump.h>
-
-namespace android {
-
-static const size_t kMaxUDPSize = 1500;
-static const int32_t kMaxUDPRetries = 200;
-
-struct ANetworkSession::NetworkThread : public Thread {
- explicit NetworkThread(ANetworkSession *session);
-
-protected:
- virtual ~NetworkThread();
-
-private:
- ANetworkSession *mSession;
-
- virtual bool threadLoop();
-
- DISALLOW_EVIL_CONSTRUCTORS(NetworkThread);
-};
-
-struct ANetworkSession::Session : public RefBase {
- enum Mode {
- MODE_RTSP,
- MODE_DATAGRAM,
- MODE_WEBSOCKET,
- };
-
- enum State {
- CONNECTING,
- CONNECTED,
- LISTENING_RTSP,
- LISTENING_TCP_DGRAMS,
- DATAGRAM,
- };
-
- Session(int32_t sessionID,
- State state,
- int s,
- const sp<AMessage> ¬ify);
-
- int32_t sessionID() const;
- int socket() const;
- sp<AMessage> getNotificationMessage() const;
-
- bool isRTSPServer() const;
- bool isTCPDatagramServer() const;
-
- bool wantsToRead();
- bool wantsToWrite();
-
- status_t readMore();
- status_t writeMore();
-
- status_t sendRequest(
- const void *data, ssize_t size, bool timeValid, int64_t timeUs);
-
- void setMode(Mode mode);
-
- status_t switchToWebSocketMode();
-
-protected:
- virtual ~Session();
-
-private:
- enum {
- FRAGMENT_FLAG_TIME_VALID = 1,
- };
- struct Fragment {
- uint32_t mFlags;
- int64_t mTimeUs;
- sp<ABuffer> mBuffer;
- };
-
- int32_t mSessionID;
- State mState;
- Mode mMode;
- int mSocket;
- sp<AMessage> mNotify;
- bool mSawReceiveFailure, mSawSendFailure;
- int32_t mUDPRetries;
-
- List<Fragment> mOutFragments;
-
- AString mInBuffer;
-
- int64_t mLastStallReportUs;
-
- void notifyError(bool send, status_t err, const char *detail);
- void notify(NotificationReason reason);
-
- void dumpFragmentStats(const Fragment &frag);
-
- DISALLOW_EVIL_CONSTRUCTORS(Session);
-};
-////////////////////////////////////////////////////////////////////////////////
-
-ANetworkSession::NetworkThread::NetworkThread(ANetworkSession *session)
- : mSession(session) {
-}
-
-ANetworkSession::NetworkThread::~NetworkThread() {
-}
-
-bool ANetworkSession::NetworkThread::threadLoop() {
- mSession->threadLoop();
-
- return true;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-ANetworkSession::Session::Session(
- int32_t sessionID,
- State state,
- int s,
- const sp<AMessage> ¬ify)
- : mSessionID(sessionID),
- mState(state),
- mMode(MODE_DATAGRAM),
- mSocket(s),
- mNotify(notify),
- mSawReceiveFailure(false),
- mSawSendFailure(false),
- mUDPRetries(kMaxUDPRetries),
- mLastStallReportUs(-1ll) {
- if (mState == CONNECTED) {
- struct sockaddr_in localAddr;
- socklen_t localAddrLen = sizeof(localAddr);
-
- int res = getsockname(
- mSocket, (struct sockaddr *)&localAddr, &localAddrLen);
- CHECK_GE(res, 0);
-
- struct sockaddr_in remoteAddr;
- socklen_t remoteAddrLen = sizeof(remoteAddr);
-
- res = getpeername(
- mSocket, (struct sockaddr *)&remoteAddr, &remoteAddrLen);
- CHECK_GE(res, 0);
-
- in_addr_t addr = ntohl(localAddr.sin_addr.s_addr);
- AString localAddrString = AStringPrintf(
- "%d.%d.%d.%d",
- (addr >> 24),
- (addr >> 16) & 0xff,
- (addr >> 8) & 0xff,
- addr & 0xff);
-
- addr = ntohl(remoteAddr.sin_addr.s_addr);
- AString remoteAddrString = AStringPrintf(
- "%d.%d.%d.%d",
- (addr >> 24),
- (addr >> 16) & 0xff,
- (addr >> 8) & 0xff,
- addr & 0xff);
-
- sp<AMessage> msg = mNotify->dup();
- msg->setInt32("sessionID", mSessionID);
- msg->setInt32("reason", kWhatClientConnected);
- msg->setString("server-ip", localAddrString.c_str());
- msg->setInt32("server-port", ntohs(localAddr.sin_port));
- msg->setString("client-ip", remoteAddrString.c_str());
- msg->setInt32("client-port", ntohs(remoteAddr.sin_port));
- msg->post();
- }
-}
-
-ANetworkSession::Session::~Session() {
- ALOGV("Session %d gone", mSessionID);
-
- close(mSocket);
- mSocket = -1;
-}
-
-int32_t ANetworkSession::Session::sessionID() const {
- return mSessionID;
-}
-
-int ANetworkSession::Session::socket() const {
- return mSocket;
-}
-
-void ANetworkSession::Session::setMode(Mode mode) {
- mMode = mode;
-}
-
-status_t ANetworkSession::Session::switchToWebSocketMode() {
- if (mState != CONNECTED || mMode != MODE_RTSP) {
- return INVALID_OPERATION;
- }
-
- mMode = MODE_WEBSOCKET;
-
- return OK;
-}
-
-sp<AMessage> ANetworkSession::Session::getNotificationMessage() const {
- return mNotify;
-}
-
-bool ANetworkSession::Session::isRTSPServer() const {
- return mState == LISTENING_RTSP;
-}
-
-bool ANetworkSession::Session::isTCPDatagramServer() const {
- return mState == LISTENING_TCP_DGRAMS;
-}
-
-bool ANetworkSession::Session::wantsToRead() {
- return !mSawReceiveFailure && mState != CONNECTING;
-}
-
-bool ANetworkSession::Session::wantsToWrite() {
- return !mSawSendFailure
- && (mState == CONNECTING
- || (mState == CONNECTED && !mOutFragments.empty())
- || (mState == DATAGRAM && !mOutFragments.empty()));
-}
-
-status_t ANetworkSession::Session::readMore() {
- if (mState == DATAGRAM) {
- CHECK_EQ(mMode, MODE_DATAGRAM);
-
- status_t err;
- do {
- sp<ABuffer> buf = new ABuffer(kMaxUDPSize);
-
- struct sockaddr_in remoteAddr;
- socklen_t remoteAddrLen = sizeof(remoteAddr);
-
- ssize_t n;
- do {
- n = recvfrom(
- mSocket, buf->data(), buf->capacity(), 0,
- (struct sockaddr *)&remoteAddr, &remoteAddrLen);
- } while (n < 0 && errno == EINTR);
-
- err = OK;
- if (n < 0) {
- err = -errno;
- } else if (n == 0) {
- err = -ECONNRESET;
- } else {
- buf->setRange(0, n);
-
- int64_t nowUs = ALooper::GetNowUs();
- buf->meta()->setInt64("arrivalTimeUs", nowUs);
-
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("sessionID", mSessionID);
- notify->setInt32("reason", kWhatDatagram);
-
- uint32_t ip = ntohl(remoteAddr.sin_addr.s_addr);
- notify->setString(
- "fromAddr",
- AStringPrintf(
- "%u.%u.%u.%u",
- ip >> 24,
- (ip >> 16) & 0xff,
- (ip >> 8) & 0xff,
- ip & 0xff).c_str());
-
- notify->setInt32("fromPort", ntohs(remoteAddr.sin_port));
-
- notify->setBuffer("data", buf);
- notify->post();
- }
- } while (err == OK);
-
- if (err == -EAGAIN) {
- err = OK;
- }
-
- if (err != OK) {
- if (!mUDPRetries) {
- notifyError(false /* send */, err, "Recvfrom failed.");
- mSawReceiveFailure = true;
- } else {
- mUDPRetries--;
- ALOGE("Recvfrom failed, %d/%d retries left",
- mUDPRetries, kMaxUDPRetries);
- err = OK;
- }
- } else {
- mUDPRetries = kMaxUDPRetries;
- }
-
- return err;
- }
-
- char tmp[512];
- ssize_t n;
- do {
- n = recv(mSocket, tmp, sizeof(tmp), 0);
- } while (n < 0 && errno == EINTR);
-
- status_t err = OK;
-
- if (n > 0) {
- mInBuffer.append(tmp, n);
-
-#if 0
- ALOGI("in:");
- hexdump(tmp, n);
-#endif
- } else if (n < 0) {
- err = -errno;
- } else {
- err = -ECONNRESET;
- }
-
- if (mMode == MODE_DATAGRAM) {
- // TCP stream carrying 16-bit length-prefixed datagrams.
-
- while (mInBuffer.size() >= 2) {
- size_t packetSize = U16_AT((const uint8_t *)mInBuffer.c_str());
-
- if (mInBuffer.size() < packetSize + 2) {
- break;
- }
-
- sp<ABuffer> packet = new ABuffer(packetSize);
- memcpy(packet->data(), mInBuffer.c_str() + 2, packetSize);
-
- int64_t nowUs = ALooper::GetNowUs();
- packet->meta()->setInt64("arrivalTimeUs", nowUs);
-
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("sessionID", mSessionID);
- notify->setInt32("reason", kWhatDatagram);
- notify->setBuffer("data", packet);
- notify->post();
-
- mInBuffer.erase(0, packetSize + 2);
- }
- } else if (mMode == MODE_RTSP) {
- for (;;) {
- size_t length;
-
- if (mInBuffer.size() > 0 && mInBuffer.c_str()[0] == '$') {
- if (mInBuffer.size() < 4) {
- break;
- }
-
- length = U16_AT((const uint8_t *)mInBuffer.c_str() + 2);
-
- if (mInBuffer.size() < 4 + length) {
- break;
- }
-
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("sessionID", mSessionID);
- notify->setInt32("reason", kWhatBinaryData);
- notify->setInt32("channel", mInBuffer.c_str()[1]);
-
- sp<ABuffer> data = new ABuffer(length);
- memcpy(data->data(), mInBuffer.c_str() + 4, length);
-
- int64_t nowUs = ALooper::GetNowUs();
- data->meta()->setInt64("arrivalTimeUs", nowUs);
-
- notify->setBuffer("data", data);
- notify->post();
-
- mInBuffer.erase(0, 4 + length);
- continue;
- }
-
- sp<ParsedMessage> msg =
- ParsedMessage::Parse(
- mInBuffer.c_str(), mInBuffer.size(), err != OK, &length);
-
- if (msg == NULL) {
- break;
- }
-
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("sessionID", mSessionID);
- notify->setInt32("reason", kWhatData);
- notify->setObject("data", msg);
- notify->post();
-
-#if 1
- // XXX The (old) dongle sends the wrong content length header on a
- // SET_PARAMETER request that signals a "wfd_idr_request".
- // (17 instead of 19).
- const char *content = msg->getContent();
- if (content
- && !memcmp(content, "wfd_idr_request\r\n", 17)
- && length >= 19
- && mInBuffer.c_str()[length] == '\r'
- && mInBuffer.c_str()[length + 1] == '\n') {
- length += 2;
- }
-#endif
-
- mInBuffer.erase(0, length);
-
- if (err != OK) {
- break;
- }
- }
- } else {
- CHECK_EQ(mMode, MODE_WEBSOCKET);
-
- const uint8_t *data = (const uint8_t *)mInBuffer.c_str();
- // hexdump(data, mInBuffer.size());
-
- while (mInBuffer.size() >= 2) {
- size_t offset = 2;
-
- uint64_t payloadLen = data[1] & 0x7f;
- if (payloadLen == 126) {
- if (offset + 2 > mInBuffer.size()) {
- break;
- }
-
- payloadLen = U16_AT(&data[offset]);
- offset += 2;
- } else if (payloadLen == 127) {
- if (offset + 8 > mInBuffer.size()) {
- break;
- }
-
- payloadLen = U64_AT(&data[offset]);
- offset += 8;
- }
-
- uint32_t mask = 0;
- if (data[1] & 0x80) {
- // MASK==1
- if (offset + 4 > mInBuffer.size()) {
- break;
- }
-
- mask = U32_AT(&data[offset]);
- offset += 4;
- }
-
- if (payloadLen > mInBuffer.size() || offset > mInBuffer.size() - payloadLen) {
- break;
- }
-
- // We have the full message.
-
- sp<ABuffer> packet = new ABuffer(payloadLen);
- memcpy(packet->data(), &data[offset], payloadLen);
-
- if (mask != 0) {
- for (size_t i = 0; i < payloadLen; ++i) {
- packet->data()[i] =
- data[offset + i]
- ^ ((mask >> (8 * (3 - (i % 4)))) & 0xff);
- }
- }
-
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("sessionID", mSessionID);
- notify->setInt32("reason", kWhatWebSocketMessage);
- notify->setBuffer("data", packet);
- notify->setInt32("headerByte", data[0]);
- notify->post();
-
- mInBuffer.erase(0, offset + payloadLen);
- }
- }
-
- if (err != OK) {
- notifyError(false /* send */, err, "Recv failed.");
- mSawReceiveFailure = true;
- }
-
- return err;
-}
-
-void ANetworkSession::Session::dumpFragmentStats(const Fragment & /* frag */) {
-#if 0
- int64_t nowUs = ALooper::GetNowUs();
- int64_t delayMs = (nowUs - frag.mTimeUs) / 1000ll;
-
- static const int64_t kMinDelayMs = 0;
- static const int64_t kMaxDelayMs = 300;
-
- const char *kPattern = "########################################";
- size_t kPatternSize = strlen(kPattern);
-
- int n = (kPatternSize * (delayMs - kMinDelayMs))
- / (kMaxDelayMs - kMinDelayMs);
-
- if (n < 0) {
- n = 0;
- } else if ((size_t)n > kPatternSize) {
- n = kPatternSize;
- }
-
- ALOGI("[%lld]: (%4lld ms) %s\n",
- frag.mTimeUs / 1000,
- delayMs,
- kPattern + kPatternSize - n);
-#endif
-}
-
-status_t ANetworkSession::Session::writeMore() {
- if (mState == DATAGRAM) {
- CHECK(!mOutFragments.empty());
-
- status_t err;
- do {
- const Fragment &frag = *mOutFragments.begin();
- const sp<ABuffer> &datagram = frag.mBuffer;
-
- int n;
- do {
- n = send(mSocket, datagram->data(), datagram->size(), 0);
- } while (n < 0 && errno == EINTR);
-
- err = OK;
-
- if (n > 0) {
- if (frag.mFlags & FRAGMENT_FLAG_TIME_VALID) {
- dumpFragmentStats(frag);
- }
-
- mOutFragments.erase(mOutFragments.begin());
- } else if (n < 0) {
- err = -errno;
- } else if (n == 0) {
- err = -ECONNRESET;
- }
- } while (err == OK && !mOutFragments.empty());
-
- if (err == -EAGAIN) {
- if (!mOutFragments.empty()) {
- ALOGI("%zu datagrams remain queued.", mOutFragments.size());
- }
- err = OK;
- }
-
- if (err != OK) {
- if (!mUDPRetries) {
- notifyError(true /* send */, err, "Send datagram failed.");
- mSawSendFailure = true;
- } else {
- mUDPRetries--;
- ALOGE("Send datagram failed, %d/%d retries left",
- mUDPRetries, kMaxUDPRetries);
- err = OK;
- }
- } else {
- mUDPRetries = kMaxUDPRetries;
- }
-
- return err;
- }
-
- if (mState == CONNECTING) {
- int err;
- socklen_t optionLen = sizeof(err);
- CHECK_EQ(getsockopt(mSocket, SOL_SOCKET, SO_ERROR, &err, &optionLen), 0);
- CHECK_EQ(optionLen, (socklen_t)sizeof(err));
-
- if (err != 0) {
- notifyError(kWhatError, -err, "Connection failed");
- mSawSendFailure = true;
-
- return -err;
- }
-
- mState = CONNECTED;
- notify(kWhatConnected);
-
- return OK;
- }
-
- CHECK_EQ(mState, CONNECTED);
- CHECK(!mOutFragments.empty());
-
- ssize_t n = -1;
- while (!mOutFragments.empty()) {
- const Fragment &frag = *mOutFragments.begin();
-
- do {
- n = send(mSocket, frag.mBuffer->data(), frag.mBuffer->size(), 0);
- } while (n < 0 && errno == EINTR);
-
- if (n <= 0) {
- break;
- }
-
- frag.mBuffer->setRange(
- frag.mBuffer->offset() + n, frag.mBuffer->size() - n);
-
- if (frag.mBuffer->size() > 0) {
- break;
- }
-
- if (frag.mFlags & FRAGMENT_FLAG_TIME_VALID) {
- dumpFragmentStats(frag);
- }
-
- mOutFragments.erase(mOutFragments.begin());
- }
-
- status_t err = OK;
-
- if (n < 0) {
- err = -errno;
- } else if (n == 0) {
- err = -ECONNRESET;
- }
-
- if (err != OK) {
- notifyError(true /* send */, err, "Send failed.");
- mSawSendFailure = true;
- }
-
-#if 0
- int numBytesQueued;
- int res = ioctl(mSocket, SIOCOUTQ, &numBytesQueued);
- if (res == 0 && numBytesQueued > 50 * 1024) {
- if (numBytesQueued > 409600) {
- ALOGW("!!! numBytesQueued = %d", numBytesQueued);
- }
-
- int64_t nowUs = ALooper::GetNowUs();
-
- if (mLastStallReportUs < 0ll
- || nowUs > mLastStallReportUs + 100000ll) {
- sp<AMessage> msg = mNotify->dup();
- msg->setInt32("sessionID", mSessionID);
- msg->setInt32("reason", kWhatNetworkStall);
- msg->setSize("numBytesQueued", numBytesQueued);
- msg->post();
-
- mLastStallReportUs = nowUs;
- }
- }
-#endif
-
- return err;
-}
-
-status_t ANetworkSession::Session::sendRequest(
- const void *data, ssize_t size, bool timeValid, int64_t timeUs) {
- CHECK(mState == CONNECTED || mState == DATAGRAM);
-
- if (size < 0) {
- size = strlen((const char *)data);
- }
-
- if (size == 0) {
- return OK;
- }
-
- sp<ABuffer> buffer;
-
- if (mState == CONNECTED && mMode == MODE_DATAGRAM) {
- CHECK_LE(size, 65535);
-
- buffer = new ABuffer(size + 2);
- buffer->data()[0] = size >> 8;
- buffer->data()[1] = size & 0xff;
- memcpy(buffer->data() + 2, data, size);
- } else if (mState == CONNECTED && mMode == MODE_WEBSOCKET) {
- static const bool kUseMask = false; // Chromium doesn't like it.
-
- size_t numHeaderBytes = 2 + (kUseMask ? 4 : 0);
- if (size > 65535) {
- numHeaderBytes += 8;
- } else if (size > 125) {
- numHeaderBytes += 2;
- }
-
- buffer = new ABuffer(numHeaderBytes + size);
- buffer->data()[0] = 0x81; // FIN==1 | opcode=1 (text)
- buffer->data()[1] = kUseMask ? 0x80 : 0x00;
-
- if (size > 65535) {
- buffer->data()[1] |= 127;
- buffer->data()[2] = 0x00;
- buffer->data()[3] = 0x00;
- buffer->data()[4] = 0x00;
- buffer->data()[5] = 0x00;
- buffer->data()[6] = (size >> 24) & 0xff;
- buffer->data()[7] = (size >> 16) & 0xff;
- buffer->data()[8] = (size >> 8) & 0xff;
- buffer->data()[9] = size & 0xff;
- } else if (size > 125) {
- buffer->data()[1] |= 126;
- buffer->data()[2] = (size >> 8) & 0xff;
- buffer->data()[3] = size & 0xff;
- } else {
- buffer->data()[1] |= size;
- }
-
- if (kUseMask) {
- uint32_t mask = rand();
-
- buffer->data()[numHeaderBytes - 4] = (mask >> 24) & 0xff;
- buffer->data()[numHeaderBytes - 3] = (mask >> 16) & 0xff;
- buffer->data()[numHeaderBytes - 2] = (mask >> 8) & 0xff;
- buffer->data()[numHeaderBytes - 1] = mask & 0xff;
-
- for (size_t i = 0; i < (size_t)size; ++i) {
- buffer->data()[numHeaderBytes + i] =
- ((const uint8_t *)data)[i]
- ^ ((mask >> (8 * (3 - (i % 4)))) & 0xff);
- }
- } else {
- memcpy(buffer->data() + numHeaderBytes, data, size);
- }
- } else {
- buffer = new ABuffer(size);
- memcpy(buffer->data(), data, size);
- }
-
- Fragment frag;
-
- frag.mFlags = 0;
- if (timeValid) {
- frag.mFlags = FRAGMENT_FLAG_TIME_VALID;
- frag.mTimeUs = timeUs;
- }
-
- frag.mBuffer = buffer;
-
- mOutFragments.push_back(frag);
-
- return OK;
-}
-
-void ANetworkSession::Session::notifyError(
- bool send, status_t err, const char *detail) {
- sp<AMessage> msg = mNotify->dup();
- msg->setInt32("sessionID", mSessionID);
- msg->setInt32("reason", kWhatError);
- msg->setInt32("send", send);
- msg->setInt32("err", err);
- msg->setString("detail", detail);
- msg->post();
-}
-
-void ANetworkSession::Session::notify(NotificationReason reason) {
- sp<AMessage> msg = mNotify->dup();
- msg->setInt32("sessionID", mSessionID);
- msg->setInt32("reason", reason);
- msg->post();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-ANetworkSession::ANetworkSession()
- : mNextSessionID(1) {
- mPipeFd[0] = mPipeFd[1] = -1;
-}
-
-ANetworkSession::~ANetworkSession() {
- stop();
-}
-
-status_t ANetworkSession::start() {
- if (mThread != NULL) {
- return INVALID_OPERATION;
- }
-
- int res = pipe(mPipeFd);
- if (res != 0) {
- mPipeFd[0] = mPipeFd[1] = -1;
- return -errno;
- }
-
- mThread = new NetworkThread(this);
-
- status_t err = mThread->run("ANetworkSession", ANDROID_PRIORITY_AUDIO);
-
- if (err != OK) {
- mThread.clear();
-
- close(mPipeFd[0]);
- close(mPipeFd[1]);
- mPipeFd[0] = mPipeFd[1] = -1;
-
- return err;
- }
-
- return OK;
-}
-
-status_t ANetworkSession::stop() {
- if (mThread == NULL) {
- return INVALID_OPERATION;
- }
-
- mThread->requestExit();
- interrupt();
- mThread->requestExitAndWait();
-
- mThread.clear();
-
- close(mPipeFd[0]);
- close(mPipeFd[1]);
- mPipeFd[0] = mPipeFd[1] = -1;
-
- return OK;
-}
-
-status_t ANetworkSession::createRTSPClient(
- const char *host, unsigned port, const sp<AMessage> ¬ify,
- int32_t *sessionID) {
- return createClientOrServer(
- kModeCreateRTSPClient,
- NULL /* addr */,
- 0 /* port */,
- host,
- port,
- notify,
- sessionID);
-}
-
-status_t ANetworkSession::createRTSPServer(
- const struct in_addr &addr, unsigned port,
- const sp<AMessage> ¬ify, int32_t *sessionID) {
- return createClientOrServer(
- kModeCreateRTSPServer,
- &addr,
- port,
- NULL /* remoteHost */,
- 0 /* remotePort */,
- notify,
- sessionID);
-}
-
-status_t ANetworkSession::createUDPSession(
- unsigned localPort, const sp<AMessage> ¬ify, int32_t *sessionID) {
- return createUDPSession(localPort, NULL, 0, notify, sessionID);
-}
-
-status_t ANetworkSession::createUDPSession(
- unsigned localPort,
- const char *remoteHost,
- unsigned remotePort,
- const sp<AMessage> ¬ify,
- int32_t *sessionID) {
- return createClientOrServer(
- kModeCreateUDPSession,
- NULL /* addr */,
- localPort,
- remoteHost,
- remotePort,
- notify,
- sessionID);
-}
-
-status_t ANetworkSession::createTCPDatagramSession(
- const struct in_addr &addr, unsigned port,
- const sp<AMessage> ¬ify, int32_t *sessionID) {
- return createClientOrServer(
- kModeCreateTCPDatagramSessionPassive,
- &addr,
- port,
- NULL /* remoteHost */,
- 0 /* remotePort */,
- notify,
- sessionID);
-}
-
-status_t ANetworkSession::createTCPDatagramSession(
- unsigned localPort,
- const char *remoteHost,
- unsigned remotePort,
- const sp<AMessage> ¬ify,
- int32_t *sessionID) {
- return createClientOrServer(
- kModeCreateTCPDatagramSessionActive,
- NULL /* addr */,
- localPort,
- remoteHost,
- remotePort,
- notify,
- sessionID);
-}
-
-status_t ANetworkSession::destroySession(int32_t sessionID) {
- Mutex::Autolock autoLock(mLock);
-
- ssize_t index = mSessions.indexOfKey(sessionID);
-
- if (index < 0) {
- return -ENOENT;
- }
-
- mSessions.removeItemsAt(index);
-
- interrupt();
-
- return OK;
-}
-
-// static
-status_t ANetworkSession::MakeSocketNonBlocking(int s) {
- int flags = fcntl(s, F_GETFL, 0);
- if (flags < 0) {
- flags = 0;
- }
-
- int res = fcntl(s, F_SETFL, flags | O_NONBLOCK);
- if (res < 0) {
- return -errno;
- }
-
- return OK;
-}
-
-status_t ANetworkSession::createClientOrServer(
- Mode mode,
- const struct in_addr *localAddr,
- unsigned port,
- const char *remoteHost,
- unsigned remotePort,
- const sp<AMessage> ¬ify,
- int32_t *sessionID) {
- Mutex::Autolock autoLock(mLock);
-
- *sessionID = 0;
- status_t err = OK;
- int s, res;
- sp<Session> session;
-
- s = socket(
- AF_INET,
- (mode == kModeCreateUDPSession) ? SOCK_DGRAM : SOCK_STREAM,
- 0);
-
- if (s < 0) {
- err = -errno;
- goto bail;
- }
-
- if (mode == kModeCreateRTSPServer
- || mode == kModeCreateTCPDatagramSessionPassive) {
- const int yes = 1;
- res = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
-
- if (res < 0) {
- err = -errno;
- goto bail2;
- }
- }
-
- if (mode == kModeCreateUDPSession) {
- int size = 256 * 1024;
-
- res = setsockopt(s, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
-
- if (res < 0) {
- err = -errno;
- goto bail2;
- }
-
- res = setsockopt(s, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
-
- if (res < 0) {
- err = -errno;
- goto bail2;
- }
- } else if (mode == kModeCreateTCPDatagramSessionActive) {
- int flag = 1;
- res = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag));
-
- if (res < 0) {
- err = -errno;
- goto bail2;
- }
-
- int tos = 224; // VOICE
- res = setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(tos));
-
- if (res < 0) {
- err = -errno;
- goto bail2;
- }
- }
-
- err = MakeSocketNonBlocking(s);
-
- if (err != OK) {
- goto bail2;
- }
-
- struct sockaddr_in addr;
- memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
- addr.sin_family = AF_INET;
-
- if (mode == kModeCreateRTSPClient
- || mode == kModeCreateTCPDatagramSessionActive) {
- struct hostent *ent= gethostbyname(remoteHost);
- if (ent == NULL) {
- err = -h_errno;
- goto bail2;
- }
-
- addr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr;
- addr.sin_port = htons(remotePort);
- } else if (localAddr != NULL) {
- addr.sin_addr = *localAddr;
- addr.sin_port = htons(port);
- } else {
- addr.sin_addr.s_addr = htonl(INADDR_ANY);
- addr.sin_port = htons(port);
- }
-
- if (mode == kModeCreateRTSPClient
- || mode == kModeCreateTCPDatagramSessionActive) {
- in_addr_t x = ntohl(addr.sin_addr.s_addr);
- ALOGI("connecting socket %d to %d.%d.%d.%d:%d",
- s,
- (x >> 24),
- (x >> 16) & 0xff,
- (x >> 8) & 0xff,
- x & 0xff,
- ntohs(addr.sin_port));
-
- res = connect(s, (const struct sockaddr *)&addr, sizeof(addr));
-
- CHECK_LT(res, 0);
- if (errno == EINPROGRESS) {
- res = 0;
- }
- } else {
- res = bind(s, (const struct sockaddr *)&addr, sizeof(addr));
-
- if (res == 0) {
- if (mode == kModeCreateRTSPServer
- || mode == kModeCreateTCPDatagramSessionPassive) {
- res = listen(s, 4);
- } else {
- CHECK_EQ(mode, kModeCreateUDPSession);
-
- if (remoteHost != NULL) {
- struct sockaddr_in remoteAddr;
- memset(remoteAddr.sin_zero, 0, sizeof(remoteAddr.sin_zero));
- remoteAddr.sin_family = AF_INET;
- remoteAddr.sin_port = htons(remotePort);
-
- struct hostent *ent= gethostbyname(remoteHost);
- if (ent == NULL) {
- err = -h_errno;
- goto bail2;
- }
-
- remoteAddr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr;
-
- res = connect(
- s,
- (const struct sockaddr *)&remoteAddr,
- sizeof(remoteAddr));
- }
- }
- }
- }
-
- if (res < 0) {
- err = -errno;
- goto bail2;
- }
-
- Session::State state;
- switch (mode) {
- case kModeCreateRTSPClient:
- state = Session::CONNECTING;
- break;
-
- case kModeCreateTCPDatagramSessionActive:
- state = Session::CONNECTING;
- break;
-
- case kModeCreateTCPDatagramSessionPassive:
- state = Session::LISTENING_TCP_DGRAMS;
- break;
-
- case kModeCreateRTSPServer:
- state = Session::LISTENING_RTSP;
- break;
-
- default:
- CHECK_EQ(mode, kModeCreateUDPSession);
- state = Session::DATAGRAM;
- break;
- }
-
- session = new Session(
- mNextSessionID++,
- state,
- s,
- notify);
-
- if (mode == kModeCreateTCPDatagramSessionActive) {
- session->setMode(Session::MODE_DATAGRAM);
- } else if (mode == kModeCreateRTSPClient) {
- session->setMode(Session::MODE_RTSP);
- }
-
- mSessions.add(session->sessionID(), session);
-
- interrupt();
-
- *sessionID = session->sessionID();
-
- goto bail;
-
-bail2:
- close(s);
- s = -1;
-
-bail:
- return err;
-}
-
-status_t ANetworkSession::connectUDPSession(
- int32_t sessionID, const char *remoteHost, unsigned remotePort) {
- Mutex::Autolock autoLock(mLock);
-
- ssize_t index = mSessions.indexOfKey(sessionID);
-
- if (index < 0) {
- return -ENOENT;
- }
-
- const sp<Session> session = mSessions.valueAt(index);
- int s = session->socket();
-
- struct sockaddr_in remoteAddr;
- memset(remoteAddr.sin_zero, 0, sizeof(remoteAddr.sin_zero));
- remoteAddr.sin_family = AF_INET;
- remoteAddr.sin_port = htons(remotePort);
-
- status_t err = OK;
- struct hostent *ent = gethostbyname(remoteHost);
- if (ent == NULL) {
- err = -h_errno;
- } else {
- remoteAddr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr;
-
- int res = connect(
- s,
- (const struct sockaddr *)&remoteAddr,
- sizeof(remoteAddr));
-
- if (res < 0) {
- err = -errno;
- }
- }
-
- return err;
-}
-
-status_t ANetworkSession::sendRequest(
- int32_t sessionID, const void *data, ssize_t size,
- bool timeValid, int64_t timeUs) {
- Mutex::Autolock autoLock(mLock);
-
- ssize_t index = mSessions.indexOfKey(sessionID);
-
- if (index < 0) {
- return -ENOENT;
- }
-
- const sp<Session> session = mSessions.valueAt(index);
-
- status_t err = session->sendRequest(data, size, timeValid, timeUs);
-
- interrupt();
-
- return err;
-}
-
-status_t ANetworkSession::switchToWebSocketMode(int32_t sessionID) {
- Mutex::Autolock autoLock(mLock);
-
- ssize_t index = mSessions.indexOfKey(sessionID);
-
- if (index < 0) {
- return -ENOENT;
- }
-
- const sp<Session> session = mSessions.valueAt(index);
- return session->switchToWebSocketMode();
-}
-
-void ANetworkSession::interrupt() {
- static const char dummy = 0;
-
- ssize_t n;
- do {
- n = write(mPipeFd[1], &dummy, 1);
- } while (n < 0 && errno == EINTR);
-
- if (n < 0) {
- ALOGW("Error writing to pipe (%s)", strerror(errno));
- }
-}
-
-void ANetworkSession::threadLoop() {
- fd_set rs, ws;
- FD_ZERO(&rs);
- FD_ZERO(&ws);
-
- FD_SET(mPipeFd[0], &rs);
- int maxFd = mPipeFd[0];
-
- {
- Mutex::Autolock autoLock(mLock);
-
- for (size_t i = 0; i < mSessions.size(); ++i) {
- const sp<Session> &session = mSessions.valueAt(i);
-
- int s = session->socket();
-
- if (s < 0) {
- continue;
- }
-
- if (session->wantsToRead()) {
- FD_SET(s, &rs);
- if (s > maxFd) {
- maxFd = s;
- }
- }
-
- if (session->wantsToWrite()) {
- FD_SET(s, &ws);
- if (s > maxFd) {
- maxFd = s;
- }
- }
- }
- }
-
- int res = select(maxFd + 1, &rs, &ws, NULL, NULL /* tv */);
-
- if (res == 0) {
- return;
- }
-
- if (res < 0) {
- if (errno == EINTR) {
- return;
- }
-
- ALOGE("select failed w/ error %d (%s)", errno, strerror(errno));
- return;
- }
-
- if (FD_ISSET(mPipeFd[0], &rs)) {
- char c;
- ssize_t n;
- do {
- n = read(mPipeFd[0], &c, 1);
- } while (n < 0 && errno == EINTR);
-
- if (n < 0) {
- ALOGW("Error reading from pipe (%s)", strerror(errno));
- }
-
- --res;
- }
-
- {
- Mutex::Autolock autoLock(mLock);
-
- List<sp<Session> > sessionsToAdd;
-
- for (size_t i = mSessions.size(); res > 0 && i > 0;) {
- i--;
- const sp<Session> &session = mSessions.valueAt(i);
-
- int s = session->socket();
-
- if (s < 0) {
- continue;
- }
-
- if (FD_ISSET(s, &rs) || FD_ISSET(s, &ws)) {
- --res;
- }
-
- if (FD_ISSET(s, &rs)) {
- if (session->isRTSPServer() || session->isTCPDatagramServer()) {
- struct sockaddr_in remoteAddr;
- socklen_t remoteAddrLen = sizeof(remoteAddr);
-
- int clientSocket = accept(
- s, (struct sockaddr *)&remoteAddr, &remoteAddrLen);
-
- if (clientSocket >= 0) {
- status_t err = MakeSocketNonBlocking(clientSocket);
-
- if (err != OK) {
- ALOGE("Unable to make client socket non blocking, "
- "failed w/ error %d (%s)",
- err, strerror(-err));
-
- close(clientSocket);
- clientSocket = -1;
- } else {
- in_addr_t addr = ntohl(remoteAddr.sin_addr.s_addr);
-
- ALOGI("incoming connection from %d.%d.%d.%d:%d "
- "(socket %d)",
- (addr >> 24),
- (addr >> 16) & 0xff,
- (addr >> 8) & 0xff,
- addr & 0xff,
- ntohs(remoteAddr.sin_port),
- clientSocket);
-
- sp<Session> clientSession =
- new Session(
- mNextSessionID++,
- Session::CONNECTED,
- clientSocket,
- session->getNotificationMessage());
-
- clientSession->setMode(
- session->isRTSPServer()
- ? Session::MODE_RTSP
- : Session::MODE_DATAGRAM);
-
- sessionsToAdd.push_back(clientSession);
- }
- } else {
- ALOGE("accept returned error %d (%s)",
- errno, strerror(errno));
- }
- } else {
- status_t err = session->readMore();
- if (err != OK) {
- ALOGE("readMore on socket %d failed w/ error %d (%s)",
- s, err, strerror(-err));
- }
- }
- }
-
- if (FD_ISSET(s, &ws)) {
- status_t err = session->writeMore();
- if (err != OK) {
- ALOGE("writeMore on socket %d failed w/ error %d (%s)",
- s, err, strerror(-err));
- }
- }
- }
-
- while (!sessionsToAdd.empty()) {
- sp<Session> session = *sessionsToAdd.begin();
- sessionsToAdd.erase(sessionsToAdd.begin());
-
- mSessions.add(session->sessionID(), session);
-
- ALOGI("added clientSession %d", session->sessionID());
- }
- }
-}
-
-} // namespace android
diff --git a/media/libstagefright/foundation/Android.bp b/media/libstagefright/foundation/Android.bp
index 6b384c0..5b7961d 100644
--- a/media/libstagefright/foundation/Android.bp
+++ b/media/libstagefright/foundation/Android.bp
@@ -56,18 +56,15 @@
"ABuffer.cpp",
"ADebug.cpp",
"AHandler.cpp",
- "AHierarchicalStateMachine.cpp",
"ALooper.cpp",
"ALooperRoster.cpp",
"AMessage.cpp",
- "ANetworkSession.cpp",
"AString.cpp",
"AStringUtils.cpp",
"ByteUtils.cpp",
"ColorUtils.cpp",
"MediaDefs.cpp",
"MediaKeys.cpp",
- "ParsedMessage.cpp",
"avc_utils.cpp",
"base64.cpp",
"hexdump.cpp",
diff --git a/media/libstagefright/foundation/MediaDefs.cpp b/media/libstagefright/foundation/MediaDefs.cpp
index a32cf08..28bb10a 100644
--- a/media/libstagefright/foundation/MediaDefs.cpp
+++ b/media/libstagefright/foundation/MediaDefs.cpp
@@ -55,7 +55,7 @@
const char *MEDIA_MIMETYPE_CONTAINER_MPEG4 = "video/mp4";
const char *MEDIA_MIMETYPE_CONTAINER_WAV = "audio/x-wav";
-const char *MEDIA_MIMETYPE_CONTAINER_OGG = "application/ogg";
+const char *MEDIA_MIMETYPE_CONTAINER_OGG = "audio/ogg";
const char *MEDIA_MIMETYPE_CONTAINER_MATROSKA = "video/x-matroska";
const char *MEDIA_MIMETYPE_CONTAINER_MPEG2TS = "video/mp2ts";
const char *MEDIA_MIMETYPE_CONTAINER_AVI = "video/avi";
diff --git a/media/libstagefright/foundation/ParsedMessage.cpp b/media/libstagefright/foundation/ParsedMessage.cpp
deleted file mode 100644
index 049c9ad..0000000
--- a/media/libstagefright/foundation/ParsedMessage.cpp
+++ /dev/null
@@ -1,302 +0,0 @@
-/*
- * Copyright 2012, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ParsedMessage.h"
-
-#include <ctype.h>
-#include <media/stagefright/foundation/ABuffer.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/hexdump.h>
-
-namespace android {
-
-// static
-sp<ParsedMessage> ParsedMessage::Parse(
- const char *data, size_t size, bool noMoreData, size_t *length) {
- sp<ParsedMessage> msg = new ParsedMessage;
- ssize_t res = msg->parse(data, size, noMoreData);
-
- if (res < 0) {
- *length = 0;
- return NULL;
- }
-
- *length = res;
- return msg;
-}
-
-ParsedMessage::ParsedMessage() {
-}
-
-ParsedMessage::~ParsedMessage() {
-}
-
-bool ParsedMessage::findString(const char *name, AString *value) const {
- AString key = name;
- key.tolower();
-
- ssize_t index = mDict.indexOfKey(key);
-
- if (index < 0) {
- value->clear();
-
- return false;
- }
-
- *value = mDict.valueAt(index);
- return true;
-}
-
-bool ParsedMessage::findInt32(const char *name, int32_t *value) const {
- AString stringValue;
-
- if (!findString(name, &stringValue)) {
- return false;
- }
-
- char *end;
- *value = strtol(stringValue.c_str(), &end, 10);
-
- if (end == stringValue.c_str() || *end != '\0') {
- *value = 0;
- return false;
- }
-
- return true;
-}
-
-const char *ParsedMessage::getContent() const {
- return mContent.c_str();
-}
-
-ssize_t ParsedMessage::parse(const char *data, size_t size, bool noMoreData) {
- if (size == 0) {
- return -1;
- }
-
- ssize_t lastDictIndex = -1;
-
- size_t offset = 0;
- bool headersComplete = false;
- while (offset < size) {
- size_t lineEndOffset = offset;
- while (lineEndOffset + 1 < size
- && (data[lineEndOffset] != '\r'
- || data[lineEndOffset + 1] != '\n')) {
- ++lineEndOffset;
- }
-
- if (lineEndOffset + 1 >= size) {
- return -1;
- }
-
- AString line(&data[offset], lineEndOffset - offset);
-
- if (offset == 0) {
- // Special handling for the request/status line.
-
- mDict.add(AString("_"), line);
- offset = lineEndOffset + 2;
-
- continue;
- }
-
- if (lineEndOffset == offset) {
- // An empty line separates headers from body.
- headersComplete = true;
- offset += 2;
- break;
- }
-
- if (line.c_str()[0] == ' ' || line.c_str()[0] == '\t') {
- // Support for folded header values.
-
- if (lastDictIndex >= 0) {
- // Otherwise it's malformed since the first header line
- // cannot continue anything...
-
- AString &value = mDict.editValueAt(lastDictIndex);
- value.append(line);
- }
-
- offset = lineEndOffset + 2;
- continue;
- }
-
- ssize_t colonPos = line.find(":");
- if (colonPos >= 0) {
- AString key(line, 0, colonPos);
- key.trim();
- key.tolower();
-
- line.erase(0, colonPos + 1);
-
- lastDictIndex = mDict.add(key, line);
- }
-
- offset = lineEndOffset + 2;
- }
-
- if (!headersComplete && (!noMoreData || offset == 0)) {
- // We either saw the empty line separating headers from body
- // or we saw at least the status line and know that no more data
- // is going to follow.
- return -1;
- }
-
- for (size_t i = 0; i < mDict.size(); ++i) {
- mDict.editValueAt(i).trim();
- }
-
- int32_t contentLength;
- if (!findInt32("content-length", &contentLength) || contentLength < 0) {
- contentLength = 0;
- }
-
- size_t totalLength = offset + contentLength;
-
- if (size < totalLength) {
- return -1;
- }
-
- mContent.setTo(&data[offset], contentLength);
-
- return totalLength;
-}
-
-bool ParsedMessage::getRequestField(size_t index, AString *field) const {
- AString line;
- CHECK(findString("_", &line));
-
- size_t prevOffset = 0;
- size_t offset = 0;
- for (size_t i = 0; i <= index; ++i) {
- if (offset >= line.size()) {
- return false;
- }
-
- ssize_t spacePos = line.find(" ", offset);
-
- if (spacePos < 0) {
- spacePos = line.size();
- }
-
- prevOffset = offset;
- offset = spacePos + 1;
- }
-
- field->setTo(line, prevOffset, offset - prevOffset - 1);
-
- return true;
-}
-
-bool ParsedMessage::getStatusCode(int32_t *statusCode) const {
- AString statusCodeString;
- if (!getRequestField(1, &statusCodeString)) {
- *statusCode = 0;
- return false;
- }
-
- char *end;
- *statusCode = strtol(statusCodeString.c_str(), &end, 10);
-
- if (*end != '\0' || end == statusCodeString.c_str()
- || (*statusCode) < 100 || (*statusCode) > 999) {
- *statusCode = 0;
- return false;
- }
-
- return true;
-}
-
-AString ParsedMessage::debugString() const {
- AString line;
- CHECK(findString("_", &line));
-
- line.append("\n");
-
- for (size_t i = 0; i < mDict.size(); ++i) {
- const AString &key = mDict.keyAt(i);
- const AString &value = mDict.valueAt(i);
-
- if (key == AString("_")) {
- continue;
- }
-
- line.append(key);
- line.append(": ");
- line.append(value);
- line.append("\n");
- }
-
- line.append("\n");
- line.append(mContent);
-
- return line;
-}
-
-// static
-bool ParsedMessage::GetAttribute(
- const char *s, const char *key, AString *value) {
- value->clear();
-
- size_t keyLen = strlen(key);
-
- for (;;) {
- while (isspace(*s)) {
- ++s;
- }
-
- const char *colonPos = strchr(s, ';');
-
- size_t len =
- (colonPos == NULL) ? strlen(s) : colonPos - s;
-
- if (len >= keyLen + 1 && s[keyLen] == '=' && !strncmp(s, key, keyLen)) {
- value->setTo(&s[keyLen + 1], len - keyLen - 1);
- return true;
- }
-
- if (colonPos == NULL) {
- return false;
- }
-
- s = colonPos + 1;
- }
-}
-
-// static
-bool ParsedMessage::GetInt32Attribute(
- const char *s, const char *key, int32_t *value) {
- AString stringValue;
- if (!GetAttribute(s, key, &stringValue)) {
- *value = 0;
- return false;
- }
-
- char *end;
- *value = strtol(stringValue.c_str(), &end, 10);
-
- if (end == stringValue.c_str() || *end != '\0') {
- *value = 0;
- return false;
- }
-
- return true;
-}
-
-} // namespace android
-
diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/ANetworkSession.h b/media/libstagefright/foundation/include/media/stagefright/foundation/ANetworkSession.h
deleted file mode 100644
index fd3ebaa..0000000
--- a/media/libstagefright/foundation/include/media/stagefright/foundation/ANetworkSession.h
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright 2012, 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 A_NETWORK_SESSION_H_
-
-#define A_NETWORK_SESSION_H_
-
-#include <media/stagefright/foundation/ABase.h>
-#include <utils/KeyedVector.h>
-#include <utils/RefBase.h>
-#include <utils/Thread.h>
-
-#include <netinet/in.h>
-
-namespace android {
-
-struct AMessage;
-
-// Helper class to manage a number of live sockets (datagram and stream-based)
-// on a single thread. Clients are notified about activity through AMessages.
-struct ANetworkSession : public RefBase {
- ANetworkSession();
-
- status_t start();
- status_t stop();
-
- status_t createRTSPClient(
- const char *host, unsigned port, const sp<AMessage> ¬ify,
- int32_t *sessionID);
-
- status_t createRTSPServer(
- const struct in_addr &addr, unsigned port,
- const sp<AMessage> ¬ify, int32_t *sessionID);
-
- status_t createUDPSession(
- unsigned localPort, const sp<AMessage> ¬ify, int32_t *sessionID);
-
- status_t createUDPSession(
- unsigned localPort,
- const char *remoteHost,
- unsigned remotePort,
- const sp<AMessage> ¬ify,
- int32_t *sessionID);
-
- status_t connectUDPSession(
- int32_t sessionID, const char *remoteHost, unsigned remotePort);
-
- // passive
- status_t createTCPDatagramSession(
- const struct in_addr &addr, unsigned port,
- const sp<AMessage> ¬ify, int32_t *sessionID);
-
- // active
- status_t createTCPDatagramSession(
- unsigned localPort,
- const char *remoteHost,
- unsigned remotePort,
- const sp<AMessage> ¬ify,
- int32_t *sessionID);
-
- status_t destroySession(int32_t sessionID);
-
- status_t sendRequest(
- int32_t sessionID, const void *data, ssize_t size = -1,
- bool timeValid = false, int64_t timeUs = -1ll);
-
- status_t switchToWebSocketMode(int32_t sessionID);
-
- enum NotificationReason {
- kWhatError,
- kWhatConnected,
- kWhatClientConnected,
- kWhatData,
- kWhatDatagram,
- kWhatBinaryData,
- kWhatWebSocketMessage,
- kWhatNetworkStall,
- };
-
-protected:
- virtual ~ANetworkSession();
-
-private:
- struct NetworkThread;
- struct Session;
-
- Mutex mLock;
- sp<Thread> mThread;
-
- int32_t mNextSessionID;
-
- int mPipeFd[2];
-
- KeyedVector<int32_t, sp<Session> > mSessions;
-
- enum Mode {
- kModeCreateUDPSession,
- kModeCreateTCPDatagramSessionPassive,
- kModeCreateTCPDatagramSessionActive,
- kModeCreateRTSPServer,
- kModeCreateRTSPClient,
- };
- status_t createClientOrServer(
- Mode mode,
- const struct in_addr *addr,
- unsigned port,
- const char *remoteHost,
- unsigned remotePort,
- const sp<AMessage> ¬ify,
- int32_t *sessionID);
-
- void threadLoop();
- void interrupt();
-
- static status_t MakeSocketNonBlocking(int s);
-
- DISALLOW_EVIL_CONSTRUCTORS(ANetworkSession);
-};
-
-} // namespace android
-
-#endif // A_NETWORK_SESSION_H_
diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/ParsedMessage.h b/media/libstagefright/foundation/include/media/stagefright/foundation/ParsedMessage.h
deleted file mode 100644
index 9d43a93..0000000
--- a/media/libstagefright/foundation/include/media/stagefright/foundation/ParsedMessage.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright 2012, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <media/stagefright/foundation/ABase.h>
-#include <media/stagefright/foundation/AString.h>
-#include <utils/KeyedVector.h>
-#include <utils/RefBase.h>
-
-namespace android {
-
-// Encapsulates an "HTTP/RTSP style" response, i.e. a status line,
-// key/value pairs making up the headers and an optional body/content.
-struct ParsedMessage : public RefBase {
- static sp<ParsedMessage> Parse(
- const char *data, size_t size, bool noMoreData, size_t *length);
-
- bool findString(const char *name, AString *value) const;
- bool findInt32(const char *name, int32_t *value) const;
-
- const char *getContent() const;
-
- bool getRequestField(size_t index, AString *field) const;
- bool getStatusCode(int32_t *statusCode) const;
-
- AString debugString() const;
-
- static bool GetAttribute(const char *s, const char *key, AString *value);
-
- static bool GetInt32Attribute(
- const char *s, const char *key, int32_t *value);
-
-
-protected:
- virtual ~ParsedMessage();
-
-private:
- KeyedVector<AString, AString> mDict;
- AString mContent;
-
- ParsedMessage();
-
- ssize_t parse(const char *data, size_t size, bool noMoreData);
-
- DISALLOW_EVIL_CONSTRUCTORS(ParsedMessage);
-};
-
-} // namespace android
diff --git a/media/libstagefright/include/media/stagefright/ACodec.h b/media/libstagefright/include/media/stagefright/ACodec.h
index 1137cf1..73f93d1 100644
--- a/media/libstagefright/include/media/stagefright/ACodec.h
+++ b/media/libstagefright/include/media/stagefright/ACodec.h
@@ -22,7 +22,7 @@
#include <media/hardware/MetadataBufferType.h>
#include <media/MediaCodecInfo.h>
#include <media/IOMX.h>
-#include <media/stagefright/foundation/AHierarchicalStateMachine.h>
+#include <media/stagefright/AHierarchicalStateMachine.h>
#include <media/stagefright/CodecBase.h>
#include <media/stagefright/FrameRenderTracker.h>
#include <media/stagefright/MediaDefs.h>
diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/AHierarchicalStateMachine.h b/media/libstagefright/include/media/stagefright/AHierarchicalStateMachine.h
similarity index 100%
rename from media/libstagefright/foundation/include/media/stagefright/foundation/AHierarchicalStateMachine.h
rename to media/libstagefright/include/media/stagefright/AHierarchicalStateMachine.h
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h b/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h
index a9fce55..0f229f7 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h
+++ b/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h
@@ -190,7 +190,12 @@
*/
// convert: Status -> status_t
inline status_t toStatusT(Return<Status> const& t) {
- return t.isOk() ? toStatusT(static_cast<Status>(t)) : UNKNOWN_ERROR;
+ if (t.isOk()) {
+ return toStatusT(static_cast<Status>(t));
+ } else if (t.isDeadObject()) {
+ return DEAD_OBJECT;
+ }
+ return UNKNOWN_ERROR;
}
/**
diff --git a/media/libstagefright/timedtext/Android.bp b/media/libstagefright/timedtext/Android.bp
index a5ad6c6..7c51333 100644
--- a/media/libstagefright/timedtext/Android.bp
+++ b/media/libstagefright/timedtext/Android.bp
@@ -25,3 +25,36 @@
shared_libs: ["libmedia"],
}
+
+cc_library_static {
+ name: "libstagefright_timedtext2",
+
+ srcs: ["TextDescriptions2.cpp"],
+
+ static_libs: [
+ "libmediaplayer2-protos",
+ "libprotobuf-cpp-lite",
+ ],
+
+ cflags: [
+ "-Wno-multichar",
+ "-Werror",
+ "-Wall",
+ ],
+
+ sanitize: {
+ misc_undefined: [
+ "signed-integer-overflow",
+ ],
+ cfi: true,
+ diag: {
+ cfi: true,
+ },
+ },
+
+ include_dirs: [
+ "frameworks/av/media/libstagefright",
+ ],
+
+ shared_libs: ["libmedia"],
+}
diff --git a/media/libstagefright/timedtext/TextDescriptions2.cpp b/media/libstagefright/timedtext/TextDescriptions2.cpp
new file mode 100644
index 0000000..f48eacc
--- /dev/null
+++ b/media/libstagefright/timedtext/TextDescriptions2.cpp
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TextDescriptions2.h"
+#include <media/stagefright/foundation/ByteUtils.h>
+#include <media/stagefright/MediaErrors.h>
+
+namespace android {
+
+TextDescriptions2::TextDescriptions2() {
+}
+
+status_t TextDescriptions2::getPlayerMessageOfDescriptions(
+ const uint8_t *data, ssize_t size,
+ uint32_t flags, int timeMs, PlayerMessage *playerMsg) {
+ if (flags & IN_BAND_TEXT_3GPP) {
+ if (flags & GLOBAL_DESCRIPTIONS) {
+ return extract3GPPGlobalDescriptions(data, size, playerMsg);
+ } else if (flags & LOCAL_DESCRIPTIONS) {
+ return extract3GPPLocalDescriptions(data, size, timeMs, playerMsg);
+ }
+ } else if (flags & OUT_OF_BAND_TEXT_SRT) {
+ if (flags & LOCAL_DESCRIPTIONS) {
+ return extractSRTLocalDescriptions(data, size, timeMs, playerMsg);
+ }
+ }
+
+ return ERROR_UNSUPPORTED;
+}
+
+// Parse the SRT text sample, and store the timing and text sample in a PlayerMessage.
+// The PlayerMessage will be sent to MediaPlayer2.java through event, and will be
+// parsed in TimedText.java.
+status_t TextDescriptions2::extractSRTLocalDescriptions(
+ const uint8_t *data, ssize_t size, int timeMs, PlayerMessage *playerMsg) {
+ playerMsg->add_values()->set_int32_value(KEY_LOCAL_SETTING);
+ playerMsg->add_values()->set_int32_value(KEY_START_TIME);
+ playerMsg->add_values()->set_int32_value(timeMs);
+
+ playerMsg->add_values()->set_int32_value(KEY_STRUCT_TEXT);
+ playerMsg->add_values()->set_bytes_value(data, size);
+
+ return OK;
+}
+
+// Extract the local 3GPP display descriptions. 3GPP local descriptions
+// are appended to the text sample if any.
+status_t TextDescriptions2::extract3GPPLocalDescriptions(
+ const uint8_t *data, ssize_t size,
+ int timeMs, PlayerMessage *playerMsg) {
+
+ playerMsg->add_values()->set_int32_value(KEY_LOCAL_SETTING);
+
+ // write start time to display this text sample
+ playerMsg->add_values()->set_int32_value(KEY_START_TIME);
+ playerMsg->add_values()->set_int32_value(timeMs);
+
+ if (size < 2) {
+ return OK;
+ }
+ ssize_t textLen = (*data) << 8 | (*(data + 1));
+
+ if (size < textLen + 2) {
+ return OK;
+ }
+
+ // write text sample length and text sample itself
+ playerMsg->add_values()->set_int32_value(KEY_STRUCT_TEXT);
+ playerMsg->add_values()->set_bytes_value(data + 2, textLen);
+
+ if (size > textLen + 2) {
+ data += (textLen + 2);
+ size -= (textLen + 2);
+ } else {
+ return OK;
+ }
+
+ while (size >= 8) {
+ const uint8_t *tmpData = data;
+ ssize_t chunkSize = U32_AT(tmpData); // size includes size and type
+ uint32_t chunkType = U32_AT(tmpData + 4);
+
+ if (chunkSize <= 8 || chunkSize > size) {
+ return OK;
+ }
+
+ size_t remaining = chunkSize - 8;
+
+ tmpData += 8;
+
+ switch(chunkType) {
+ // 'tbox' box to indicate the position of the text with values
+ // of top, left, bottom and right
+ case FOURCC('t', 'b', 'o', 'x'):
+ {
+ if (remaining < 8) {
+ return OK;
+ }
+ playerMsg->add_values()->set_int32_value(KEY_STRUCT_TEXT_POS);
+ playerMsg->add_values()->set_int32_value(U16_AT(tmpData));
+ playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 2));
+ playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 4));
+ playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 6));
+
+ tmpData += 8;
+ remaining -= 8;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ data += chunkSize;
+ size -= chunkSize;
+ }
+
+ return OK;
+}
+
+// To extract box 'tx3g' defined in 3GPP TS 26.245, and store it in a PlayerMessage
+status_t TextDescriptions2::extract3GPPGlobalDescriptions(
+ const uint8_t *data, ssize_t size, PlayerMessage *playerMsg) {
+
+ playerMsg->add_values()->set_int32_value(KEY_GLOBAL_SETTING);
+
+ while (size >= 8) {
+ ssize_t chunkSize = U32_AT(data);
+ uint32_t chunkType = U32_AT(data + 4);
+ const uint8_t *tmpData = data;
+ tmpData += 8;
+ size_t remaining = size - 8;
+
+ if (size < chunkSize) {
+ return OK;
+ }
+ switch(chunkType) {
+ case FOURCC('t', 'x', '3', 'g'):
+ {
+ if (remaining < 18) {
+ return OK;
+ }
+ // Skip DISPLAY_FLAGS, STRUCT_JUSTIFICATION, and BACKGROUND_COLOR_RGBA
+ tmpData += 18;
+ remaining -= 18;
+
+ if (remaining < 8) {
+ return OK;
+ }
+ playerMsg->add_values()->set_int32_value(KEY_STRUCT_TEXT_POS);
+ playerMsg->add_values()->set_int32_value(U16_AT(tmpData));
+ playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 2));
+ playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 4));
+ playerMsg->add_values()->set_int32_value(U16_AT(tmpData + 6));
+
+ tmpData += 8;
+ remaining -= 18;
+ // Ignore remaining data.
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ data += chunkSize;
+ size -= chunkSize;
+ }
+
+ return OK;
+}
+
+} // namespace android
diff --git a/media/libstagefright/timedtext/TextDescriptions2.h b/media/libstagefright/timedtext/TextDescriptions2.h
new file mode 100644
index 0000000..7c7d2d0
--- /dev/null
+++ b/media/libstagefright/timedtext/TextDescriptions2.h
@@ -0,0 +1,88 @@
+ /*
+ * Copyright (C) 2018 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 TEXT_DESCRIPTIONS2_H_
+
+#define TEXT_DESCRIPTIONS2_H_
+
+#include <binder/Parcel.h>
+#include <media/stagefright/foundation/ABase.h>
+
+#include "mediaplayer2.pb.h"
+
+using android::media::MediaPlayer2Proto::PlayerMessage;
+
+namespace android {
+
+class TextDescriptions2 {
+public:
+ enum {
+ IN_BAND_TEXT_3GPP = 0x01,
+ OUT_OF_BAND_TEXT_SRT = 0x02,
+
+ GLOBAL_DESCRIPTIONS = 0x100,
+ LOCAL_DESCRIPTIONS = 0x200,
+ };
+
+ static status_t getPlayerMessageOfDescriptions(
+ const uint8_t *data, ssize_t size,
+ uint32_t flags, int timeMs, PlayerMessage *playerMsg);
+private:
+ TextDescriptions2();
+
+ enum {
+ // These keys must be in sync with the keys in TimedText.java
+ KEY_DISPLAY_FLAGS = 1, // int
+ KEY_STYLE_FLAGS = 2, // int
+ KEY_BACKGROUND_COLOR_RGBA = 3, // int
+ KEY_HIGHLIGHT_COLOR_RGBA = 4, // int
+ KEY_SCROLL_DELAY = 5, // int
+ KEY_WRAP_TEXT = 6, // int
+ KEY_START_TIME = 7, // int
+ KEY_STRUCT_BLINKING_TEXT_LIST = 8, // List<CharPos>
+ KEY_STRUCT_FONT_LIST = 9, // List<Font>
+ KEY_STRUCT_HIGHLIGHT_LIST = 10, // List<CharPos>
+ KEY_STRUCT_HYPER_TEXT_LIST = 11, // List<HyperText>
+ KEY_STRUCT_KARAOKE_LIST = 12, // List<Karaoke>
+ KEY_STRUCT_STYLE_LIST = 13, // List<Style>
+ KEY_STRUCT_TEXT_POS = 14, // TextPos
+ KEY_STRUCT_JUSTIFICATION = 15, // Justification
+ KEY_STRUCT_TEXT = 16, // Text
+
+ KEY_GLOBAL_SETTING = 101,
+ KEY_LOCAL_SETTING = 102,
+ KEY_START_CHAR = 103,
+ KEY_END_CHAR = 104,
+ KEY_FONT_ID = 105,
+ KEY_FONT_SIZE = 106,
+ KEY_TEXT_COLOR_RGBA = 107,
+ };
+
+ static status_t extractSRTLocalDescriptions(
+ const uint8_t *data, ssize_t size,
+ int timeMs, PlayerMessage *playerMsg);
+ static status_t extract3GPPGlobalDescriptions(
+ const uint8_t *data, ssize_t size,
+ PlayerMessage *playerMsg);
+ static status_t extract3GPPLocalDescriptions(
+ const uint8_t *data, ssize_t size,
+ int timeMs, PlayerMessage *playerMsg);
+
+ DISALLOW_EVIL_CONSTRUCTORS(TextDescriptions2);
+};
+
+} // namespace android
+#endif // TEXT_DESCRIPTIONS2_H_
diff --git a/media/ndk/NdkMediaDrm.cpp b/media/ndk/NdkMediaDrm.cpp
index 5597488..2552073 100644
--- a/media/ndk/NdkMediaDrm.cpp
+++ b/media/ndk/NdkMediaDrm.cpp
@@ -17,6 +17,8 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "NdkMediaDrm"
+#include <inttypes.h>
+
#include <media/NdkMediaDrm.h>
#include <cutils/properties.h>
@@ -40,10 +42,32 @@
{
private:
AMediaDrm *mObj;
- AMediaDrmEventListener mListener;
+ AMediaDrmEventListener mEventListener;
+ AMediaDrmExpirationUpdateListener mExpirationUpdateListener;
+ AMediaDrmKeysChangeListener mKeysChangeListener;
public:
- DrmListener(AMediaDrm *obj, AMediaDrmEventListener listener) : mObj(obj), mListener(listener) {}
+ DrmListener(AMediaDrm *obj, AMediaDrmEventListener listener) : mObj(obj),
+ mEventListener(listener), mExpirationUpdateListener(NULL), mKeysChangeListener(NULL) {}
+
+ DrmListener(AMediaDrm *obj, AMediaDrmExpirationUpdateListener listener) : mObj(obj),
+ mEventListener(NULL), mExpirationUpdateListener(listener), mKeysChangeListener(NULL) {}
+
+ DrmListener(AMediaDrm *obj, AMediaDrmKeysChangeListener listener) : mObj(obj),
+ mEventListener(NULL), mExpirationUpdateListener(NULL), mKeysChangeListener(listener) {}
+
+ void setEventListener(AMediaDrmEventListener listener) {
+ mEventListener = listener;
+ }
+
+ void setExpirationUpdateListener(AMediaDrmExpirationUpdateListener listener) {
+ mExpirationUpdateListener = listener;
+ }
+
+ void setKeysChangeListener(AMediaDrmKeysChangeListener listener) {
+ mKeysChangeListener = listener;
+ }
+
void notify(DrmPlugin::EventType eventType, int extra, const Parcel *obj);
};
@@ -62,27 +86,75 @@
};
void DrmListener::notify(DrmPlugin::EventType eventType, int extra, const Parcel *obj) {
- if (!mListener) {
+ if (!mEventListener && !mExpirationUpdateListener && !mKeysChangeListener) {
+ ALOGE("No listeners are specified");
return;
}
+ obj->setDataPosition(0);
+
AMediaDrmSessionId sessionId = {NULL, 0};
int32_t sessionIdSize = obj->readInt32();
- if (sessionIdSize) {
- uint8_t *sessionIdData = new uint8_t[sessionIdSize];
- sessionId.ptr = sessionIdData;
- sessionId.length = sessionIdSize;
- obj->read(sessionIdData, sessionId.length);
+ if (sessionIdSize <= 0) {
+ ALOGE("Invalid session id size");
+ return;
}
- int32_t dataSize = obj->readInt32();
- uint8_t *data = NULL;
- if (dataSize) {
- data = new uint8_t[dataSize];
- obj->read(data, dataSize);
+ std::unique_ptr<uint8_t[]> sessionIdData(new uint8_t[sessionIdSize]);
+ sessionId.ptr = sessionIdData.get();
+ sessionId.length = sessionIdSize;
+ status_t err = obj->read(sessionIdData.get(), sessionId.length);
+ if (err != OK) {
+ ALOGE("Failed to read session id, error=%d", err);
+ return;
}
- // translate DrmPlugin event types into their NDK equivalents
+ if (DrmPlugin::kDrmPluginEventExpirationUpdate == eventType) {
+ int64_t expiryTimeInMS = obj->readInt64();
+ if (expiryTimeInMS >= 0) {
+ (*mExpirationUpdateListener)(mObj, &sessionId, expiryTimeInMS);
+ } else {
+ ALOGE("Failed to read expiry time, status=%" PRId64 "", expiryTimeInMS);
+ }
+ return;
+ } else if (DrmPlugin::kDrmPluginEventKeysChange == eventType) {
+ int32_t numKeys = 0;
+ err = obj->readInt32(&numKeys);
+ if (err != OK) {
+ ALOGE("Failed to read number of keys status, error=%d", err);
+ return;
+ }
+
+ Vector<AMediaDrmKeyStatus> keysStatus;
+ std::vector<std::unique_ptr<uint8_t[]> > dataPointers;
+ AMediaDrmKeyStatus keyStatus;
+
+ for (size_t i = 0; i < numKeys; ++i) {
+ keyStatus.keyId.ptr = nullptr;
+ keyStatus.keyId.length = 0;
+ int32_t idSize = obj->readInt32();
+ if (idSize > 0) {
+ std::unique_ptr<uint8_t[]> data(new uint8_t[idSize]);
+ err = obj->read(data.get(), idSize);
+ if (err != OK) {
+ ALOGE("Failed to read key data, error=%d", err);
+ return;
+ }
+ keyStatus.keyId.ptr = data.get();
+ keyStatus.keyId.length = idSize;
+ dataPointers.push_back(std::move(data));
+ }
+ keyStatus.keyType = static_cast<AMediaDrmKeyStatusType>(obj->readInt32());
+ keysStatus.push(keyStatus);
+ }
+
+ bool hasNewUsableKey = obj->readInt32();
+ (*mKeysChangeListener)(mObj, &sessionId, keysStatus.array(), numKeys, hasNewUsableKey);
+ return;
+ }
+
+ // Handles AMediaDrmEventListener below:
+ // translates DrmPlugin event types into their NDK equivalents
AMediaDrmEventType ndkEventType;
switch(eventType) {
case DrmPlugin::kDrmPluginEventProvisionRequired:
@@ -97,19 +169,30 @@
case DrmPlugin::kDrmPluginEventVendorDefined:
ndkEventType = EVENT_VENDOR_DEFINED;
break;
+ case DrmPlugin::kDrmPluginEventSessionReclaimed:
+ ndkEventType = EVENT_SESSION_RECLAIMED;
+ break;
default:
ALOGE("Invalid event DrmPlugin::EventType %d, ignored", (int)eventType);
- goto cleanup;
+ return;
}
- (*mListener)(mObj, &sessionId, ndkEventType, extra, data, dataSize);
-
- cleanup:
- delete [] sessionId.ptr;
- delete [] data;
+ int32_t dataSize = obj->readInt32();
+ uint8_t *data = NULL;
+ if (dataSize > 0) {
+ data = new uint8_t[dataSize];
+ err = obj->read(data, dataSize);
+ if (err == OK) {
+ (*mEventListener)(mObj, &sessionId, ndkEventType, extra, data, dataSize);
+ } else {
+ ALOGE("Failed to read event data, error=%d", err);
+ }
+ delete [] data;
+ } else {
+ ALOGE("Error reading parcel: invalid event data size=%d", dataSize);
+ }
}
-
extern "C" {
static media_status_t translateStatus(status_t status) {
@@ -198,6 +281,8 @@
AMediaDrm* AMediaDrm_createByUUID(const AMediaUUID uuid) {
AMediaDrm *mObj = new AMediaDrm();
mObj->mDrm = CreateDrmFromUUID(uuid);
+
+ mObj->mListener.clear();
return mObj;
}
@@ -216,11 +301,47 @@
if (!mObj || mObj->mDrm == NULL) {
return AMEDIA_ERROR_INVALID_OBJECT;
}
- mObj->mListener = new DrmListener(mObj, listener);
+
+ if (mObj->mListener.get()) {
+ mObj->mListener->setEventListener(listener);
+ } else {
+ mObj->mListener = new DrmListener(mObj, listener);
+ }
mObj->mDrm->setListener(mObj->mListener);
return AMEDIA_OK;
}
+EXPORT
+media_status_t AMediaDrm_setOnExpirationUpdateListener(AMediaDrm *mObj,
+ AMediaDrmExpirationUpdateListener listener) {
+ if (!mObj || mObj->mDrm == NULL) {
+ return AMEDIA_ERROR_INVALID_OBJECT;
+ }
+
+ if (mObj->mListener.get()) {
+ mObj->mListener->setExpirationUpdateListener(listener);
+ } else {
+ mObj->mListener = new DrmListener(mObj, listener);
+ }
+ mObj->mDrm->setListener(mObj->mListener);
+ return AMEDIA_OK;
+}
+
+EXPORT
+media_status_t AMediaDrm_setOnKeysChangeListener(AMediaDrm *mObj,
+ AMediaDrmKeysChangeListener listener) {
+ if (!mObj || mObj->mDrm == NULL) {
+ return AMEDIA_ERROR_INVALID_OBJECT;
+ }
+
+ if (mObj->mListener.get()) {
+ mObj->mListener->setKeysChangeListener(listener);
+ } else {
+ mObj->mListener = new DrmListener(mObj, listener);
+ }
+ mObj->mDrm->setListener(mObj->mListener);
+ return AMEDIA_OK;
+}
static bool findId(AMediaDrm *mObj, const AMediaDrmByteArray &id, List<idvec_t>::iterator &iter) {
for (iter = mObj->mIds.begin(); iter != mObj->mIds.end(); ++iter) {
diff --git a/media/ndk/NdkMediaExtractor.cpp b/media/ndk/NdkMediaExtractor.cpp
index 8c1ac59..c66cd50 100644
--- a/media/ndk/NdkMediaExtractor.cpp
+++ b/media/ndk/NdkMediaExtractor.cpp
@@ -84,8 +84,17 @@
EXPORT
media_status_t AMediaExtractor_setDataSource(AMediaExtractor *mData, const char *location) {
- ALOGV("setDataSource(%s)", location);
- // TODO: add header support
+ return AMediaExtractor_setDataSourceWithHeaders(mData, location, 0, NULL, NULL);
+}
+
+EXPORT
+media_status_t AMediaExtractor_setDataSourceWithHeaders(AMediaExtractor *mData,
+ const char *uri,
+ int numheaders,
+ const char * const *keys,
+ const char * const *values) {
+
+ ALOGV("setDataSource(%s)", uri);
JNIEnv *env = AndroidRuntime::getJNIEnv();
jobject service = NULL;
@@ -109,7 +118,7 @@
return AMEDIA_ERROR_UNSUPPORTED;
}
- jstring jloc = env->NewStringUTF(location);
+ jstring jloc = env->NewStringUTF(uri);
service = env->CallStaticObjectMethod(mediahttpclass, mediaHttpCreateMethod, jloc);
env->DeleteLocalRef(jloc);
@@ -120,7 +129,15 @@
httpService = interface_cast<IMediaHTTPService>(binder);
}
- status_t err = mData->mImpl->setDataSource(httpService, location, NULL);
+ KeyedVector<String8, String8> headers;
+ for (int i = 0; i < numheaders; ++i) {
+ String8 key8(keys[i]);
+ String8 value8(values[i]);
+ headers.add(key8, value8);
+ }
+
+ status_t err;
+ err = mData->mImpl->setDataSource(httpService, uri, numheaders > 0 ? &headers : NULL);
env->ExceptionClear();
return translate_error(err);
}
diff --git a/media/ndk/include/media/NdkMediaDrm.h b/media/ndk/include/media/NdkMediaDrm.h
index 0209681..2e438d9 100644
--- a/media/ndk/include/media/NdkMediaDrm.h
+++ b/media/ndk/include/media/NdkMediaDrm.h
@@ -56,6 +56,7 @@
typedef AMediaDrmByteArray AMediaDrmScope;
typedef AMediaDrmByteArray AMediaDrmKeySetId;
typedef AMediaDrmByteArray AMediaDrmSecureStop;
+typedef AMediaDrmByteArray AMediaDrmKeyId;
typedef enum AMediaDrmEventType {
/**
@@ -81,12 +82,89 @@
* This event may indicate some specific vendor-defined condition, see your
* DRM provider documentation for details
*/
- EVENT_VENDOR_DEFINED = 4
+ EVENT_VENDOR_DEFINED = 4,
+
+ /**
+ * This event indicates that a session opened by the app has been reclaimed
+ * by the resource manager.
+ */
+ EVENT_SESSION_RECLAIMED = 5,
} AMediaDrmEventType;
+typedef enum AMediaDrmKeyType {
+ /**
+ * This key request type specifies that the keys will be for online use, they will
+ * not be saved to the device for subsequent use when the device is not connected
+ * to a network.
+ */
+ KEY_TYPE_STREAMING = 1,
+
+ /**
+ * This key request type specifies that the keys will be for offline use, they
+ * will be saved to the device for use when the device is not connected to a network.
+ */
+ KEY_TYPE_OFFLINE = 2,
+
+ /**
+ * This key request type specifies that previously saved offline keys should be released.
+ */
+ KEY_TYPE_RELEASE = 3
+} AMediaDrmKeyType;
+
+/**
+ * Data type containing {key, value} pair
+ */
+typedef struct AMediaDrmKeyValuePair {
+ const char *mKey;
+ const char *mValue;
+} AMediaDrmKeyValue;
+
+typedef enum AMediaKeyStatusType {
+ /**
+ * The key is currently usable to decrypt media data.
+ */
+ KEY_STATUS_TYPE_USABLE,
+
+ /**
+ * The key is no longer usable to decrypt media data because its expiration
+ * time has passed.
+ */
+ KEY_STATUS_TYPE_EXPIRED,
+
+ /**
+ * The key is not currently usable to decrypt media data because its output
+ * requirements cannot currently be met.
+ */
+ KEY_STATUS_TYPE_OUTPUTNOTALLOWED,
+
+ /**
+ * The status of the key is not yet known and is being determined.
+ */
+ KEY_STATUS_TYPE_STATUSPENDING,
+
+ /**
+ * The key is not currently usable to decrypt media data because of an
+ * internal error in processing unrelated to input parameters.
+ */
+ KEY_STATUS_TYPE_INTERNALERROR,
+
+} AMediaDrmKeyStatusType;
+
+typedef struct AMediaDrmKeyStatus {
+ AMediaDrmKeyId keyId;
+ AMediaDrmKeyStatusType keyType;
+} AMediaDrmKeyStatus;
+
typedef void (*AMediaDrmEventListener)(AMediaDrm *, const AMediaDrmSessionId *sessionId,
AMediaDrmEventType eventType, int extra, const uint8_t *data, size_t dataSize);
+typedef void (*AMediaDrmExpirationUpdateListener)(AMediaDrm *,
+ const AMediaDrmSessionId *sessionId, int64_t expiryTimeInMS);
+
+typedef void (*AMediaDrmKeysChangeListener)(AMediaDrm *,
+ const AMediaDrmSessionId *sessionId, const AMediaDrmKeyStatus *keyStatus,
+ size_t numKeys, bool hasNewUsableKey);
+
#if __ANDROID_API__ >= 21
/**
@@ -120,6 +198,22 @@
AMediaDrmEventListener listener) __INTRODUCED_IN(21);
/**
+ * Register a callback to be invoked when an expiration update event occurs
+ *
+ * listener is the callback that will be invoked on event
+ */
+media_status_t AMediaDrm_setOnExpirationUpdateListener(AMediaDrm *,
+ AMediaDrmExpirationUpdateListener listener) __INTRODUCED_IN(29);
+
+/**
+ * Register a callback to be invoked when a key status change event occurs
+ *
+ * listener is the callback that will be invoked on event
+ */
+media_status_t AMediaDrm_setOnKeysChangeListener(AMediaDrm *,
+ AMediaDrmKeysChangeListener listener) __INTRODUCED_IN(29);
+
+/**
* Open a new session with the MediaDrm object. A session ID is returned.
*
* returns MEDIADRM_NOT_PROVISIONED_ERROR if provisioning is needed
@@ -135,34 +229,6 @@
media_status_t AMediaDrm_closeSession(AMediaDrm *,
const AMediaDrmSessionId *sessionId) __INTRODUCED_IN(21);
-typedef enum AMediaDrmKeyType {
- /**
- * This key request type species that the keys will be for online use, they will
- * not be saved to the device for subsequent use when the device is not connected
- * to a network.
- */
- KEY_TYPE_STREAMING = 1,
-
- /**
- * This key request type specifies that the keys will be for offline use, they
- * will be saved to the device for use when the device is not connected to a network.
- */
- KEY_TYPE_OFFLINE = 2,
-
- /**
- * This key request type specifies that previously saved offline keys should be released.
- */
- KEY_TYPE_RELEASE = 3
-} AMediaDrmKeyType;
-
-/**
- * Data type containing {key, value} pair
- */
-typedef struct AMediaDrmKeyValuePair {
- const char *mKey;
- const char *mValue;
-} AMediaDrmKeyValue;
-
/**
* A key request/response exchange occurs between the app and a license server
* to obtain or release keys used to decrypt encrypted content.
diff --git a/media/ndk/include/media/NdkMediaExtractor.h b/media/ndk/include/media/NdkMediaExtractor.h
index 6a1796f..413bc1a 100644
--- a/media/ndk/include/media/NdkMediaExtractor.h
+++ b/media/ndk/include/media/NdkMediaExtractor.h
@@ -72,7 +72,27 @@
*/
media_status_t AMediaExtractor_setDataSource(AMediaExtractor*,
const char *location) __INTRODUCED_IN(21);
- // TODO support headers
+
+#if __ANDROID_API__ >= 29
+/**
+ * Set the |uri| from which the extractor will read,
+ * plus additional http headers when initiating the request.
+ *
+ * Headers will contain corresponding items from |keys| & |values|
+ * from indices 0 (inclusive) to numheaders-1 (inclusive):
+ *
+ * keys[0]:values[0]
+ * ...
+ * keys[numheaders - 1]:values[numheaders - 1]
+ *
+ */
+media_status_t AMediaExtractor_setDataSourceWithHeaders(AMediaExtractor*,
+ const char *uri,
+ int numheaders,
+ const char * const *keys,
+ const char * const *values) __INTRODUCED_IN(29);
+
+#endif /* __ANDROID_API__ >= 29 */
#if __ANDROID_API__ >= 28
diff --git a/media/ndk/libmediandk.map.txt b/media/ndk/libmediandk.map.txt
index 0751a55..1fd69a2 100644
--- a/media/ndk/libmediandk.map.txt
+++ b/media/ndk/libmediandk.map.txt
@@ -184,6 +184,7 @@
AMediaExtractor_setDataSource;
AMediaExtractor_setDataSourceCustom; # introduced=28
AMediaExtractor_setDataSourceFd;
+ AMediaExtractor_setDataSourceWithHeaders; # introduced=29
AMediaExtractor_unselectTrack;
AMediaFormat_clear; # introduced=29
AMediaFormat_copy; # introduced=29
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index c4c742e..6cab441 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -1157,7 +1157,8 @@
{
sp<ThreadBase> thread = mThread.promote();
if (thread != 0 &&
- (thread->type() == ThreadBase::OFFLOAD || thread->type() == ThreadBase::DIRECT)) {
+ (thread->type() == ThreadBase::OFFLOAD || thread->type() == ThreadBase::DIRECT) &&
+ !isNonOffloadableEnabled_l()) {
PlaybackThread *t = (PlaybackThread *)thread.get();
float vol_l = (float)left / (1 << 24);
float vol_r = (float)right / (1 << 24);
@@ -2552,6 +2553,11 @@
bool AudioFlinger::EffectChain::isNonOffloadableEnabled()
{
Mutex::Autolock _l(mLock);
+ return isNonOffloadableEnabled_l();
+}
+
+bool AudioFlinger::EffectChain::isNonOffloadableEnabled_l()
+{
size_t size = mEffects.size();
for (size_t i = 0; i < size; i++) {
if (mEffects[i]->isEnabled() && !mEffects[i]->isOffloadable()) {
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index e04ee8e..15a26ea 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -378,6 +378,7 @@
// At least one non offloadable effect in the chain is enabled
bool isNonOffloadableEnabled();
+ bool isNonOffloadableEnabled_l();
void syncHalEffectsState();
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index a42d6b3..f650b66 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -195,6 +195,9 @@
// to avoid blocking here and to prevent possible priority inversion
mMixer = new AudioMixer(frameCount, mSampleRate);
// FIXME See the other FIXME at FastMixer::setNBLogWriter()
+ // TODO define an int to thread type mapping for the "2" below.
+ const NBLog::thread_info_t info = { 2 /*FastMixer*/, frameCount, mSampleRate };
+ LOG_THREAD_INFO(info);
const size_t mixerFrameSize = mSinkChannelCount
* audio_bytes_per_sample(mMixerBufferFormat);
mMixerBufferSize = mixerFrameSize * frameCount;
@@ -340,10 +343,19 @@
FastMixerDumpState * const dumpState = (FastMixerDumpState *) mDumpState;
if (mIsWarm) {
+ // Logging timestamps for FastMixer is currently disabled to make memory room for logging
+ // other statistics in FastMixer.
+ // To re-enable, delete the #ifdef FASTMIXER_LOG_HIST_TS lines (and the #endif lines).
+#ifdef FASTMIXER_LOG_HIST_TS
LOG_HIST_TS();
+#endif
+ //ALOGD("Eric FastMixer::onWork() mIsWarm");
} else {
dumpState->mTimestampVerifier.discontinuity();
+ // See comment in if block.
+#ifdef FASTMIXER_LOG_HIST_TS
LOG_AUDIO_STATE();
+#endif
}
const FastMixerState::Command command = mCommand;
const size_t frameCount = current->mFrameCount;
@@ -498,8 +510,10 @@
timestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL];
// We don't compensate for server - kernel time difference and
// only update latency if we have valid info.
- dumpState->mLatencyMs =
+ const double latencyMs =
(double)mNativeFramesWrittenButNotPresented * 1000 / mSampleRate;
+ dumpState->mLatencyMs = latencyMs;
+ LOG_LATENCY(latencyMs);
} else {
// HAL reported that more frames were presented than were written
mNativeFramesWrittenButNotPresented = 0;
diff --git a/services/audioflinger/FastThread.cpp b/services/audioflinger/FastThread.cpp
index e587026..09101d9 100644
--- a/services/audioflinger/FastThread.cpp
+++ b/services/audioflinger/FastThread.cpp
@@ -22,6 +22,7 @@
#include "Configuration.h"
#include <linux/futex.h>
#include <sys/syscall.h>
+#include <audio_utils/clock.h>
#include <cutils/atomic.h>
#include <utils/Log.h>
#include <utils/Trace.h>
@@ -260,6 +261,9 @@
mIsWarm = true;
mDumpState->mMeasuredWarmupTs = mMeasuredWarmupTs;
mDumpState->mWarmupCycles = mWarmupCycles;
+ const double measuredWarmupMs = (mMeasuredWarmupTs.tv_sec * 1e3) +
+ (mMeasuredWarmupTs.tv_nsec * 1e-6);
+ LOG_WARMUP_TIME(measuredWarmupMs);
}
}
mSleepNs = -1;
@@ -270,6 +274,7 @@
ALOGV("underrun: time since last cycle %d.%03ld sec",
(int) sec, nsec / 1000000L);
mDumpState->mUnderruns++;
+ LOG_UNDERRUN(audio_utils_ns_from_timespec(&newTs));
mIgnoreNextOverrun = true;
} else if (nsec < mOverrunNs) {
if (mIgnoreNextOverrun) {
@@ -279,6 +284,7 @@
ALOGV("overrun: time since last cycle %d.%03ld sec",
(int) sec, nsec / 1000000L);
mDumpState->mOverruns++;
+ LOG_OVERRUN(audio_utils_ns_from_timespec(&newTs));
}
// This forces a minimum cycle time. It:
// - compensates for an audio HAL with jitter due to sample rate conversion
@@ -339,7 +345,7 @@
// these stores #1, #2, #3 are not atomic with respect to each other,
// or with respect to store #4 below
mDumpState->mMonotonicNs[i] = monotonicNs;
- LOG_MONOTONIC_CYCLE_TIME(monotonicNs);
+ LOG_WORK_TIME(monotonicNs);
mDumpState->mLoadNs[i] = loadNs;
#ifdef CPU_FREQUENCY_STATISTICS
mDumpState->mCpukHz[i] = kHz;
diff --git a/services/audioflinger/TypedLogger.h b/services/audioflinger/TypedLogger.h
index 0fea42a..dd2e7c9 100644
--- a/services/audioflinger/TypedLogger.h
+++ b/services/audioflinger/TypedLogger.h
@@ -97,10 +97,32 @@
#define LOG_AUDIO_STATE() do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \
x->logEventHistTs(NBLog::EVENT_AUDIO_STATE, hash(__FILE__, __LINE__)); } while(0)
-// Record a typed entry that represents a thread's cycle time in nanoseconds.
+// Record a typed entry that represents a thread's work time in nanoseconds.
// Parameter ns should be of type uint32_t.
-#define LOG_MONOTONIC_CYCLE_TIME(ns) do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \
- x->logMonotonicCycleTime(ns); } while (0)
+#define LOG_WORK_TIME(ns) do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \
+ x->log<NBLog::EVENT_WORK_TIME>(ns); } while (0)
+
+// Log the difference bewteen frames presented by HAL and frames written to HAL output sink,
+// divided by the sample rate. Parameter ms is of type double.
+#define LOG_LATENCY(ms) do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \
+ x->log<NBLog::EVENT_LATENCY>(ms); } while (0)
+
+// Record thread warmup time in milliseconds. Parameter ms is of type double.
+#define LOG_WARMUP_TIME(ms) do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \
+ x->log<NBLog::EVENT_WARMUP_TIME>(ms); } while (0)
+
+// Record thread underrun event nanosecond timestamp. Parameter ns is an int64_t.
+#define LOG_UNDERRUN(ns) do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \
+ x->log<NBLog::EVENT_UNDERRUN>(ns); } while (0)
+
+// Record thread overrun event nanosecond timestamp. Parameter ns is an int64_t.
+#define LOG_OVERRUN(ns) do { NBLog::Writer *x = tlNBLogWriter; if (x != nullptr) \
+ x->log<NBLog::EVENT_OVERRUN>(ns); } while (0)
+
+// Record thread info. This currently includes type, frameCount, and sampleRate.
+// Parameter type is thread_info_t as defined in NBLog.h.
+#define LOG_THREAD_INFO(info) do { NBLog::Writer *x = tlNBLogWriter; \
+ if (x != nullptr) x->log<NBLog::EVENT_THREAD_INFO>(info); } while (0)
namespace android {
extern "C" {
diff --git a/services/audiopolicy/common/include/RoutingStrategy.h b/services/audiopolicy/common/include/RoutingStrategy.h
index d38967e..f8a1cd6 100644
--- a/services/audiopolicy/common/include/RoutingStrategy.h
+++ b/services/audiopolicy/common/include/RoutingStrategy.h
@@ -23,6 +23,7 @@
#define SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY 5000
enum routing_strategy {
+ STRATEGY_NONE = -1,
STRATEGY_MEDIA,
STRATEGY_PHONE,
STRATEGY_SONIFICATION,
diff --git a/services/audiopolicy/common/managerdefinitions/Android.mk b/services/audiopolicy/common/managerdefinitions/Android.mk
index 9b8f095..bacb780 100644
--- a/services/audiopolicy/common/managerdefinitions/Android.mk
+++ b/services/audiopolicy/common/managerdefinitions/Android.mk
@@ -17,10 +17,8 @@
src/AudioCollections.cpp \
src/EffectDescriptor.cpp \
src/SoundTriggerSession.cpp \
- src/SessionRoute.cpp \
src/VolumeCurve.cpp \
src/TypeConverter.cpp \
- src/AudioSession.cpp \
src/ClientDescriptor.cpp
LOCAL_SHARED_LIBRARIES := \
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
index 44662e5..72d5a8c 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
@@ -16,19 +16,19 @@
#pragma once
-#include "AudioIODescriptorInterface.h"
-#include "AudioPort.h"
-#include "AudioSession.h"
-#include "ClientDescriptor.h"
-#include <utils/Errors.h>
#include <system/audio.h>
+#include <utils/Errors.h>
#include <utils/SortedVector.h>
#include <utils/KeyedVector.h>
+#include "AudioIODescriptorInterface.h"
+#include "AudioPort.h"
+#include "ClientDescriptor.h"
namespace android {
class IOProfile;
class AudioMix;
+class AudioPolicyClientInterface;
// descriptor for audio inputs. Used to maintain current configuration of each opened audio input
// and keep track of the usage of this input.
@@ -39,7 +39,6 @@
AudioPolicyClientInterface *clientInterface);
audio_port_handle_t getId() const;
audio_module_handle_t getModuleHandle() const;
- uint32_t getOpenRefCount() const;
status_t dump(int fd);
@@ -56,19 +55,13 @@
SortedVector<audio_session_t> getPreemptedSessions() const;
bool hasPreemptedSession(audio_session_t session) const;
void clearPreemptedSessions();
- bool isActive() const;
+ bool isActive() const { return mGlobalActiveCount > 0; }
bool isSourceActive(audio_source_t source) const;
audio_source_t inputSource(bool activeOnly = false) const;
bool isSoundTrigger() const;
- status_t addAudioSession(audio_session_t session,
- const sp<AudioSession>& audioSession);
- status_t removeAudioSession(audio_session_t session);
- sp<AudioSession> getAudioSession(audio_session_t session) const;
- AudioSessionCollection getAudioSessions(bool activeOnly) const;
- size_t getAudioSessionCount(bool activeOnly) const;
audio_source_t getHighestPrioritySource(bool activeOnly) const;
- void changeRefCount(audio_session_t session, int delta);
-
+ void setClientActive(const sp<RecordClientDescriptor>& client, bool active);
+ int32_t activeCount() { return mGlobalActiveCount; }
// implementation of AudioIODescriptorInterface
audio_config_base_t getConfig() const override;
@@ -82,24 +75,24 @@
audio_input_flags_t flags,
audio_io_handle_t *input);
// Called when a stream is about to be started.
- // Note: called after changeRefCount(session, 1)
+ // Note: called after setClientActive(client, true)
status_t start();
// Called after a stream is stopped
- // Note: called after changeRefCount(session, -1)
+ // Note: called after setClientActive(client, false)
void stop();
void close();
- RecordClientMap& clients() { return mClients; }
+ RecordClientMap& clientsMap() { return mClients; }
RecordClientVector getClientsForSession(audio_session_t session);
+ RecordClientVector clientsList(bool activeOnly = false,
+ audio_source_t source = AUDIO_SOURCE_DEFAULT, bool preferredDeviceOnly = false) const;
private:
- void updateSessionRecordingConfiguration(int event, const sp<AudioSession>& audioSession);
+ void updateClientRecordingConfiguration(int event, const sp<RecordClientDescriptor>& client);
audio_patch_handle_t mPatchHandle;
audio_port_handle_t mId;
- // audio sessions attached to this input
- AudioSessionCollection mSessions;
// Because a preemptible capture session can preempt another one, we end up in an endless loop
// situation were each session is allowed to restart after being preempted,
// thus preempting the other one which restarts and so on.
@@ -108,7 +101,7 @@
// We also inherit sessions from the preempted input to avoid a 3 way preemption loop etc...
SortedVector<audio_session_t> mPreemptedSessions;
AudioPolicyClientInterface *mClientInterface;
- uint32_t mGlobalRefCount; // non-session-specific ref count
+ int32_t mGlobalActiveCount; // non-client-specific activity ref count
RecordClientMap mClients;
};
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
index ff0201a..27b1c93 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
@@ -59,7 +59,10 @@
audio_devices_t device,
uint32_t delayMs,
bool force);
- virtual void changeRefCount(audio_stream_type_t stream, int delta);
+ virtual void changeStreamActiveCount(audio_stream_type_t stream, int delta);
+ uint32_t streamActiveCount(audio_stream_type_t stream) const
+ { return mActiveCount[stream]; }
+ void setClientActive(const sp<TrackClientDescriptor>& client, bool active);
bool isActive(uint32_t inPastMs = 0) const;
bool isStreamActive(audio_stream_type_t stream,
@@ -78,19 +81,23 @@
audio_patch_handle_t getPatchHandle() const override;
void setPatchHandle(audio_patch_handle_t handle) override;
- TrackClientMap& clients() { return mClients; }
+ TrackClientMap& clientsMap() { return mClients; }
+ TrackClientVector clientsList(bool activeOnly = false,
+ routing_strategy strategy = STRATEGY_NONE, bool preferredDeviceOnly = false) const;
sp<AudioPort> mPort;
audio_devices_t mDevice; // current device this output is routed to
- uint32_t mRefCount[AUDIO_STREAM_CNT]; // number of streams of each type using this output
nsecs_t mStopTime[AUDIO_STREAM_CNT];
float mCurVolume[AUDIO_STREAM_CNT]; // current stream volume in dB
int mMuteCount[AUDIO_STREAM_CNT]; // mute request counter
bool mStrategyMutedByDevice[NUM_STRATEGIES]; // strategies muted because of incompatible
// device selection. See checkDeviceMuteStrategies()
AudioPolicyClientInterface *mClientInterface;
+ AudioMix *mPolicyMix; // non NULL when used by a dynamic policy
protected:
+ uint32_t mActiveCount[AUDIO_STREAM_CNT]; // number of streams of each type active on this output
+ uint32_t mGlobalActiveCount; // non-client-specific active count
audio_patch_handle_t mPatchHandle;
audio_port_handle_t mId;
TrackClientMap mClients;
@@ -114,7 +121,7 @@
virtual bool isFixedVolume(audio_devices_t device);
virtual sp<AudioOutputDescriptor> subOutput1() { return mOutput1; }
virtual sp<AudioOutputDescriptor> subOutput2() { return mOutput2; }
- virtual void changeRefCount(audio_stream_type_t stream, int delta);
+ virtual void changeStreamActiveCount(audio_stream_type_t stream, int delta);
virtual bool setVolume(float volume,
audio_stream_type_t stream,
audio_devices_t device,
@@ -132,10 +139,10 @@
audio_output_flags_t flags,
audio_io_handle_t *output);
// Called when a stream is about to be started
- // Note: called before changeRefCount(1);
+ // Note: called before setClientActive(true);
status_t start();
// Called after a stream is stopped.
- // Note: called after changeRefCount(-1);
+ // Note: called after setClientActive(false);
void stop();
void close();
status_t openDuplicating(const sp<SwAudioOutputDescriptor>& output1,
@@ -146,12 +153,10 @@
audio_io_handle_t mIoHandle; // output handle
uint32_t mLatency; //
audio_output_flags_t mFlags; //
- AudioMix *mPolicyMix; // non NULL when used by a dynamic policy
sp<SwAudioOutputDescriptor> mOutput1; // used by duplicated outputs: first output
sp<SwAudioOutputDescriptor> mOutput2; // used by duplicated outputs: second output
uint32_t mDirectOpenCount; // number of clients using this output (direct outputs only)
audio_session_t mDirectClientSession; // session id of the direct output client
- uint32_t mGlobalRefCount; // non-stream-specific ref count
};
// Audio output driven by an input device directly.
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h
index f861b95..78e7ec9 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyConfig.h
@@ -28,7 +28,6 @@
#include <AudioPolicyMix.h>
#include <EffectDescriptor.h>
#include <SoundTriggerSession.h>
-#include <SessionRoute.h>
namespace android {
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioSession.h b/services/audiopolicy/common/managerdefinitions/include/AudioSession.h
deleted file mode 100644
index 1636d3a..0000000
--- a/services/audiopolicy/common/managerdefinitions/include/AudioSession.h
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <system/audio.h>
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-#include <utils/Errors.h>
-#include <utils/KeyedVector.h>
-#include <media/AudioPolicy.h>
-#include <media/IAudioPolicyServiceClient.h>
-#include "AudioIODescriptorInterface.h"
-
-namespace android {
-
-class AudioPolicyClientInterface;
-
-class AudioSession : public RefBase
-{
-public:
- AudioSession(audio_session_t session,
- audio_source_t inputSource,
- audio_format_t format,
- uint32_t sampleRate,
- audio_channel_mask_t channelMask,
- audio_input_flags_t flags,
- uid_t uid,
- bool isSoundTrigger);
-
- status_t dump(int fd, int spaces, int index) const;
-
- audio_session_t session() const { return mRecordClientInfo.session; }
- audio_source_t inputSource()const { return mRecordClientInfo.source; }
- audio_format_t format() const { return mConfig.format; }
- uint32_t sampleRate() const { return mConfig.sample_rate; }
- audio_channel_mask_t channelMask() const { return mConfig.channel_mask; }
- audio_config_base config() const { return mConfig; }
- record_client_info_t recordClientInfo() const { return mRecordClientInfo; }
- audio_input_flags_t flags() const { return mFlags; }
- uid_t uid() const { return mRecordClientInfo.uid; }
- void setUid(uid_t uid) { mRecordClientInfo.uid = uid; }
- bool matches(const sp<AudioSession> &other) const;
- bool isSoundTrigger() const { return mIsSoundTrigger; }
- void setSilenced(bool silenced) { mSilenced = silenced; }
- bool isSilenced() const { return mSilenced; }
- uint32_t openCount() const { return mOpenCount; } ;
- uint32_t activeCount() const { return mActiveCount; } ;
-
- uint32_t changeOpenCount(int delta);
- uint32_t changeActiveCount(int delta);
-
-private:
- record_client_info_t mRecordClientInfo;
- const struct audio_config_base mConfig;
- const audio_input_flags_t mFlags;
- bool mIsSoundTrigger;
- bool mSilenced;
- uint32_t mOpenCount;
- uint32_t mActiveCount;
-};
-
-class AudioSessionCollection :
- public DefaultKeyedVector<audio_session_t, sp<AudioSession> >
-{
-public:
- status_t addSession(audio_session_t session,
- const sp<AudioSession>& audioSession);
-
- status_t removeSession(audio_session_t session);
-
- uint32_t getOpenCount() const;
-
- AudioSessionCollection getActiveSessions() const;
- size_t getActiveSessionCount() const;
- bool hasActiveSession() const;
- bool isSourceActive(audio_source_t source) const;
- audio_source_t getHighestPrioritySource(bool activeOnly) const;
-
- status_t dump(int fd, int spaces) const;
-};
-
-} // namespace android
diff --git a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
index 9efe57f..1a3300d 100644
--- a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
@@ -27,6 +27,7 @@
#include <utils/RefBase.h>
#include <utils/String8.h>
#include "AudioPatch.h"
+#include "RoutingStrategy.h"
namespace android {
@@ -53,8 +54,14 @@
audio_attributes_t attributes() const { return mAttributes; }
audio_config_base_t config() const { return mConfig; }
audio_port_handle_t preferredDeviceId() const { return mPreferredDeviceId; };
+ void setPreferredDeviceId(audio_port_handle_t preferredDeviceId) {
+ mPreferredDeviceId = preferredDeviceId;
+ };
void setActive(bool active) { mActive = active; }
bool active() const { return mActive; }
+ bool hasPreferredDevice(bool activeOnly = false) const {
+ return mPreferredDeviceId != AUDIO_PORT_HANDLE_NONE && (!activeOnly || mActive);
+ }
private:
const audio_port_handle_t mPortId; // unique Id for this client
@@ -62,7 +69,7 @@
const audio_session_t mSessionId; // audio session ID
const audio_attributes_t mAttributes; // usage...
const audio_config_base_t mConfig;
- const audio_port_handle_t mPreferredDeviceId; // selected input device port ID
+ audio_port_handle_t mPreferredDeviceId; // selected input device port ID
bool mActive;
protected:
@@ -75,10 +82,10 @@
public:
TrackClientDescriptor(audio_port_handle_t portId, uid_t uid, audio_session_t sessionId,
audio_attributes_t attributes, audio_config_base_t config,
- audio_port_handle_t preferredDeviceId,
- audio_stream_type_t stream, audio_output_flags_t flags) :
+ audio_port_handle_t preferredDeviceId, audio_stream_type_t stream,
+ routing_strategy strategy, audio_output_flags_t flags) :
ClientDescriptor(portId, uid, sessionId, attributes, config, preferredDeviceId),
- mStream(stream), mFlags(flags) {}
+ mStream(stream), mStrategy(strategy), mFlags(flags) {}
~TrackClientDescriptor() override = default;
using ClientDescriptor::dump;
@@ -86,9 +93,11 @@
audio_output_flags_t flags() const { return mFlags; }
audio_stream_type_t stream() const { return mStream; }
+ routing_strategy strategy() const { return mStrategy; }
private:
const audio_stream_type_t mStream;
+ const routing_strategy mStrategy;
const audio_output_flags_t mFlags;
};
@@ -98,9 +107,9 @@
RecordClientDescriptor(audio_port_handle_t portId, uid_t uid, audio_session_t sessionId,
audio_attributes_t attributes, audio_config_base_t config,
audio_port_handle_t preferredDeviceId,
- audio_source_t source, audio_input_flags_t flags) :
+ audio_source_t source, audio_input_flags_t flags, bool isSoundTrigger) :
ClientDescriptor(portId, uid, sessionId, attributes, config, preferredDeviceId),
- mSource(source), mFlags(flags) {}
+ mSource(source), mFlags(flags), mIsSoundTrigger(isSoundTrigger), mSilenced(false) {}
~RecordClientDescriptor() override = default;
using ClientDescriptor::dump;
@@ -108,10 +117,15 @@
audio_source_t source() const { return mSource; }
audio_input_flags_t flags() const { return mFlags; }
+ bool isSoundTrigger() const { return mIsSoundTrigger; }
+ void setSilenced(bool silenced) { mSilenced = silenced; }
+ bool isSilenced() const { return mSilenced; }
private:
const audio_source_t mSource;
const audio_input_flags_t mFlags;
+ const bool mIsSoundTrigger;
+ bool mSilenced;
};
class SourceClientDescriptor: public TrackClientDescriptor
@@ -119,7 +133,7 @@
public:
SourceClientDescriptor(audio_port_handle_t portId, uid_t uid, audio_attributes_t attributes,
const sp<AudioPatch>& patchDesc, const sp<DeviceDescriptor>& srcDevice,
- audio_stream_type_t stream);
+ audio_stream_type_t stream, routing_strategy strategy);
~SourceClientDescriptor() override = default;
sp<AudioPatch> patchDesc() const { return mPatchDesc; }
diff --git a/services/audiopolicy/common/managerdefinitions/include/SessionRoute.h b/services/audiopolicy/common/managerdefinitions/include/SessionRoute.h
deleted file mode 100644
index 32b4440..0000000
--- a/services/audiopolicy/common/managerdefinitions/include/SessionRoute.h
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <system/audio.h>
-#include <utils/KeyedVector.h>
-#include <utils/RefBase.h>
-#include <utils/Errors.h>
-
-namespace android {
-
-class DeviceDescriptor;
-class DeviceVector;
-
-class SessionRoute : public RefBase
-{
-public:
- // For Input (Source) routes, use STREAM_TYPE_NA ("NA" = "not applicable)for the
- // streamType argument
- static const audio_stream_type_t STREAM_TYPE_NA = AUDIO_STREAM_DEFAULT;
-
- // For Output (Sink) routes, use SOURCE_TYPE_NA ("NA" = "not applicable") for the
- // source argument
-
- static const audio_source_t SOURCE_TYPE_NA = AUDIO_SOURCE_DEFAULT;
-
- SessionRoute(audio_session_t session,
- audio_stream_type_t streamType,
- audio_source_t source,
- sp<DeviceDescriptor> deviceDescriptor,
- uid_t uid)
- : mUid(uid),
- mSession(session),
- mDeviceDescriptor(deviceDescriptor),
- mRefCount(0),
- mActivityCount(0),
- mChanged(false),
- mStreamType(streamType),
- mSource(source)
- {}
-
- void log(const char* prefix);
-
- bool isActiveOrChanged() {
- return (mDeviceDescriptor != 0) && (mChanged || (mActivityCount > 0));
- }
-
- uid_t mUid;
- audio_session_t mSession;
- sp<DeviceDescriptor> mDeviceDescriptor;
-
- // "reference" counting
- int mRefCount; // +/- on references
- int mActivityCount; // +/- on start/stop
- bool mChanged;
- // for outputs
- const audio_stream_type_t mStreamType;
- // for inputs
- const audio_source_t mSource;
-};
-
-class SessionRouteMap: public KeyedVector<audio_session_t, sp<SessionRoute> >
-{
-public:
- // These constants identify the SessionRoutMap as holding EITHER input routes,
- // or output routes. An error will occur if an attempt is made to add a SessionRoute
- // object with mStreamType == STREAM_TYPE_NA (i.e. an input SessionRoute) to a
- // SessionRoutMap that is marked for output (i.e. mMapType == SESSION_ROUTE_MAP_OUTPUT)
- // and similarly for output SessionRoutes and Input SessionRouteMaps.
- typedef enum
- {
- MAPTYPE_INPUT = 0,
- MAPTYPE_OUTPUT = 1
- } session_route_map_type_t;
-
- explicit SessionRouteMap(session_route_map_type_t mapType) :
- mMapType(mapType)
- {}
-
- bool hasRoute(audio_session_t session);
-
- void removeRoute(audio_session_t session);
-
- int incRouteActivity(audio_session_t session);
- int decRouteActivity(audio_session_t session);
- bool getAndClearRouteChanged(audio_session_t session); // also clears the changed flag
- void log(const char* caption);
- audio_devices_t getActiveDeviceForStream(audio_stream_type_t streamType,
- const DeviceVector& availableDevices);
- // Specify an Output(Sink) route by passing SessionRoute::SOURCE_TYPE_NA in the
- // source argument.
- // Specify an Input(Source) rout by passing SessionRoute::AUDIO_STREAM_DEFAULT
- // in the streamType argument.
- void addRoute(audio_session_t session,
- audio_stream_type_t streamType,
- audio_source_t source,
- const sp<DeviceDescriptor>& deviceDescriptor,
- uid_t uid);
-
-private:
- // Used to mark a SessionRoute as for either inputs (mMapType == kSessionRouteMap_Input)
- // or outputs (mMapType == kSessionRouteMap_Output)
- const session_route_map_type_t mMapType;
-};
-
-} // namespace android
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
index 2770e74..e25b0fe 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
@@ -17,13 +17,13 @@
#define LOG_TAG "APM::AudioInputDescriptor"
//#define LOG_NDEBUG 0
+#include <media/AudioPolicy.h>
+#include <policy.h>
#include <AudioPolicyInterface.h>
#include "AudioInputDescriptor.h"
#include "IOProfile.h"
#include "AudioGain.h"
#include "HwModule.h"
-#include <media/AudioPolicy.h>
-#include <policy.h>
namespace android {
@@ -32,7 +32,7 @@
: mIoHandle(0),
mDevice(AUDIO_DEVICE_NONE), mPolicyMix(NULL),
mProfile(profile), mPatchHandle(AUDIO_PATCH_HANDLE_NONE), mId(0),
- mClientInterface(clientInterface), mGlobalRefCount(0)
+ mClientInterface(clientInterface), mGlobalActiveCount(0)
{
if (profile != NULL) {
profile->pickAudioProfile(mSamplingRate, mChannelMask, mFormat);
@@ -50,11 +50,6 @@
return mProfile->getModuleHandle();
}
-uint32_t AudioInputDescriptor::getOpenRefCount() const
-{
- return mSessions.getOpenCount();
-}
-
audio_port_handle_t AudioInputDescriptor::getId() const
{
return mId;
@@ -118,57 +113,45 @@
mPreemptedSessions.clear();
}
-bool AudioInputDescriptor::isActive() const {
- return mSessions.hasActiveSession();
-}
-
bool AudioInputDescriptor::isSourceActive(audio_source_t source) const
{
- return mSessions.isSourceActive(source);
+ for (const auto &client : mClients) {
+ if (client.second->active() &&
+ ((client.second->source() == source) ||
+ ((source == AUDIO_SOURCE_VOICE_RECOGNITION) &&
+ (client.second->source() == AUDIO_SOURCE_HOTWORD) &&
+ client.second->isSoundTrigger()))) {
+ return true;
+ }
+ }
+ return false;
}
audio_source_t AudioInputDescriptor::getHighestPrioritySource(bool activeOnly) const
{
+ audio_source_t source = AUDIO_SOURCE_DEFAULT;
+ int32_t priority = -1;
- return mSessions.getHighestPrioritySource(activeOnly);
+ for (const auto &client : mClients) {
+ if (activeOnly && !client.second->active() ) {
+ continue;
+ }
+ int32_t curPriority = source_priority(client.second->source());
+ if (curPriority > priority) {
+ priority = curPriority;
+ source = client.second->source();
+ }
+ }
+ return source;
}
bool AudioInputDescriptor::isSoundTrigger() const {
- // sound trigger and non sound trigger sessions are not mixed
- // on a given input
- return mSessions.valueAt(0)->isSoundTrigger();
-}
-
-sp<AudioSession> AudioInputDescriptor::getAudioSession(
- audio_session_t session) const {
- return mSessions.valueFor(session);
-}
-
-AudioSessionCollection AudioInputDescriptor::getAudioSessions(bool activeOnly) const
-{
- if (activeOnly) {
- return mSessions.getActiveSessions();
- } else {
- return mSessions;
+ // sound trigger and non sound trigger clients are not mixed on a given input
+ // so check only first client
+ if (mClients.size() == 0) {
+ return false;
}
-}
-
-size_t AudioInputDescriptor::getAudioSessionCount(bool activeOnly) const
-{
- if (activeOnly) {
- return mSessions.getActiveSessionCount();
- } else {
- return mSessions.size();
- }
-}
-
-status_t AudioInputDescriptor::addAudioSession(audio_session_t session,
- const sp<AudioSession>& audioSession) {
- return mSessions.addSession(session, audioSession);
-}
-
-status_t AudioInputDescriptor::removeAudioSession(audio_session_t session) {
- return mSessions.removeSession(session);
+ return mClients.cbegin()->second->isSoundTrigger();
}
audio_patch_handle_t AudioInputDescriptor::getPatchHandle() const
@@ -179,9 +162,9 @@
void AudioInputDescriptor::setPatchHandle(audio_patch_handle_t handle)
{
mPatchHandle = handle;
- for (size_t i = 0; i < mSessions.size(); i++) {
- if (mSessions[i]->activeCount() > 0) {
- updateSessionRecordingConfiguration(RECORD_CONFIG_EVENT_START, mSessions[i]);
+ for (const auto &client : mClients) {
+ if (client.second->active()) {
+ updateClientRecordingConfiguration(RECORD_CONFIG_EVENT_START, client.second);
}
}
}
@@ -243,7 +226,7 @@
status_t AudioInputDescriptor::start()
{
- if (getAudioSessionCount(true/*activeOnly*/) == 1) {
+ if (mGlobalActiveCount == 1) {
if (!mProfile->canStartNewIo()) {
ALOGI("%s mProfile->curActiveCount %d", __func__, mProfile->curActiveCount);
return INVALID_OPERATION;
@@ -270,7 +253,7 @@
LOG_ALWAYS_FATAL_IF(mProfile->curOpenCount < 1, "%s profile open count %u",
__FUNCTION__, mProfile->curOpenCount);
// do not call stop() here as stop() is supposed to be called after
- // changeRefCount(session, -1) and we don't know how many sessions
+ // setClientActive(client, false) and we don't know how many clients
// are still active at this time
if (isActive()) {
mProfile->curActiveCount--;
@@ -280,27 +263,28 @@
}
}
-void AudioInputDescriptor::changeRefCount(audio_session_t session, int delta)
+void AudioInputDescriptor::setClientActive(const sp<RecordClientDescriptor>& client, bool active)
{
- sp<AudioSession> audioSession = mSessions.valueFor(session);
- if (audioSession == 0) {
+ if (mClients.find(client->portId()) == mClients.end()
+ || active == client->active()) {
return;
}
- // handle session-independent ref count
- uint32_t oldGlobalRefCount = mGlobalRefCount;
- if ((delta + (int)mGlobalRefCount) < 0) {
- ALOGW("changeRefCount() invalid delta %d globalRefCount %d", delta, mGlobalRefCount);
- delta = -((int)mGlobalRefCount);
+
+ // Handle non-client-specific activity ref count
+ int32_t oldGlobalActiveCount = mGlobalActiveCount;
+ if (!active && mGlobalActiveCount < 1) {
+ ALOGW("%s invalid deactivation with globalRefCount %d", __FUNCTION__, mGlobalActiveCount);
+ mGlobalActiveCount = 1;
}
- mGlobalRefCount += delta;
- if ((oldGlobalRefCount == 0) && (mGlobalRefCount > 0)) {
+ mGlobalActiveCount += active ? 1 : -1;
+
+ if ((oldGlobalActiveCount == 0) && (mGlobalActiveCount > 0)) {
if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0))
{
mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress,
MIX_STATE_MIXING);
}
-
- } else if ((oldGlobalRefCount > 0) && (mGlobalRefCount == 0)) {
+ } else if ((oldGlobalActiveCount > 0) && (mGlobalActiveCount == 0)) {
if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0))
{
mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress,
@@ -308,32 +292,18 @@
}
}
- uint32_t oldActiveCount = audioSession->activeCount();
- if ((delta + (int)oldActiveCount) < 0) {
- ALOGW("changeRefCount() invalid delta %d for sesion %d active count %d",
- delta, session, oldActiveCount);
- delta = -((int)oldActiveCount);
- }
+ client->setActive(active);
- audioSession->changeActiveCount(delta);
-
- int event = RECORD_CONFIG_EVENT_NONE;
- if ((oldActiveCount == 0) && (audioSession->activeCount() > 0)) {
- event = RECORD_CONFIG_EVENT_START;
- } else if ((oldActiveCount > 0) && (audioSession->activeCount() == 0)) {
- event = RECORD_CONFIG_EVENT_STOP;
- }
- if (event != RECORD_CONFIG_EVENT_NONE) {
- updateSessionRecordingConfiguration(event, audioSession);
- }
+ int event = active ? RECORD_CONFIG_EVENT_START : RECORD_CONFIG_EVENT_STOP;
+ updateClientRecordingConfiguration(event, client);
}
-void AudioInputDescriptor::updateSessionRecordingConfiguration(
- int event, const sp<AudioSession>& audioSession) {
-
- const audio_config_base_t sessionConfig = audioSession->config();
- const record_client_info_t recordClientInfo = audioSession->recordClientInfo();
+void AudioInputDescriptor::updateClientRecordingConfiguration(
+ int event, const sp<RecordClientDescriptor>& client)
+{
+ const audio_config_base_t sessionConfig = client->config();
+ const record_client_info_t recordClientInfo{client->uid(), client->session(), client->source()};
const audio_config_base_t config = getConfig();
mClientInterface->onRecordingConfigurationUpdate(event,
&recordClientInfo, &sessionConfig,
@@ -352,6 +322,20 @@
return clients;
}
+RecordClientVector AudioInputDescriptor::clientsList(bool activeOnly, audio_source_t source,
+ bool preferredDeviceOnly) const
+{
+ RecordClientVector clients;
+ for (const auto &client : mClients) {
+ if ((!activeOnly || client.second->active())
+ && (source == AUDIO_SOURCE_DEFAULT || source == client.second->source())
+ && (!preferredDeviceOnly || client.second->hasPreferredDevice())) {
+ clients.push_back(client.second);
+ }
+ }
+ return clients;
+}
+
status_t AudioInputDescriptor::dump(int fd)
{
const size_t SIZE = 256;
@@ -371,8 +355,6 @@
write(fd, result.string(), result.size());
- mSessions.dump(fd, 1);
-
size_t index = 0;
result = " AudioRecord clients:\n";
for (const auto& client: mClients) {
@@ -446,7 +428,7 @@
{
for (size_t i = 0; i < size(); i++) {
sp<AudioInputDescriptor> inputDesc = valueAt(i);
- for (const auto& client : inputDesc->clients()) {
+ for (const auto& client : inputDesc->clientsMap()) {
if (client.second->portId() == portId) {
return inputDesc;
}
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
index 39fce4d..b6ff6ea 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
@@ -34,12 +34,12 @@
AudioOutputDescriptor::AudioOutputDescriptor(const sp<AudioPort>& port,
AudioPolicyClientInterface *clientInterface)
- : mPort(port), mDevice(AUDIO_DEVICE_NONE),
- mClientInterface(clientInterface), mPatchHandle(AUDIO_PATCH_HANDLE_NONE), mId(0)
+ : mPort(port), mDevice(AUDIO_DEVICE_NONE), mClientInterface(clientInterface),
+ mPolicyMix(NULL), mGlobalActiveCount(0), mPatchHandle(AUDIO_PATCH_HANDLE_NONE), mId(0)
{
// clear usage count for all stream types
for (int i = 0; i < AUDIO_STREAM_CNT; i++) {
- mRefCount[i] = 0;
+ mActiveCount[i] = 0;
mCurVolume[i] = -1.0;
mMuteCount[i] = 0;
mStopTime[i] = 0;
@@ -103,17 +103,51 @@
}
}
-void AudioOutputDescriptor::changeRefCount(audio_stream_type_t stream,
+void AudioOutputDescriptor::changeStreamActiveCount(audio_stream_type_t stream,
int delta)
{
- if ((delta + (int)mRefCount[stream]) < 0) {
- ALOGW("changeRefCount() invalid delta %d for stream %d, refCount %d",
- delta, stream, mRefCount[stream]);
- mRefCount[stream] = 0;
+ if ((delta + (int)mActiveCount[stream]) < 0) {
+ ALOGW("%s invalid delta %d for stream %d, active count %d",
+ __FUNCTION__, delta, stream, mActiveCount[stream]);
+ mActiveCount[stream] = 0;
return;
}
- mRefCount[stream] += delta;
- ALOGV("changeRefCount() stream %d, count %d", stream, mRefCount[stream]);
+ mActiveCount[stream] += delta;
+ ALOGV("%s stream %d, count %d", __FUNCTION__, stream, mActiveCount[stream]);
+}
+
+void AudioOutputDescriptor::setClientActive(const sp<TrackClientDescriptor>& client, bool active)
+{
+ if (mClients.find(client->portId()) == mClients.end()
+ || active == client->active()) {
+ return;
+ }
+
+ changeStreamActiveCount(client->stream(), active ? 1 : -1);
+
+ // Handle non-client-specific activity ref count
+ int32_t oldGlobalActiveCount = mGlobalActiveCount;
+ if (!active && mGlobalActiveCount < 1) {
+ ALOGW("%s invalid deactivation with globalRefCount %d", __FUNCTION__, mGlobalActiveCount);
+ mGlobalActiveCount = 1;
+ }
+ mGlobalActiveCount += active ? 1 : -1;
+
+ if ((oldGlobalActiveCount == 0) && (mGlobalActiveCount > 0)) {
+ if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0))
+ {
+ mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress,
+ MIX_STATE_MIXING);
+ }
+ } else if ((oldGlobalActiveCount > 0) && (mGlobalActiveCount == 0)) {
+ if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0))
+ {
+ mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress,
+ MIX_STATE_IDLE);
+ }
+ }
+
+ client->setActive(active);
}
bool AudioOutputDescriptor::isActive(uint32_t inPastMs) const
@@ -137,7 +171,7 @@
uint32_t inPastMs,
nsecs_t sysTime) const
{
- if (mRefCount[stream] != 0) {
+ if (mActiveCount[stream] != 0) {
return true;
}
if (inPastMs == 0) {
@@ -201,6 +235,20 @@
port->ext.mix.hw_module = getModuleHandle();
}
+TrackClientVector AudioOutputDescriptor::clientsList(bool activeOnly, routing_strategy strategy,
+ bool preferredDeviceOnly) const
+{
+ TrackClientVector clients;
+ for (const auto &client : mClients) {
+ if ((!activeOnly || client.second->active())
+ && (strategy == STRATEGY_NONE || strategy == client.second->strategy())
+ && (!preferredDeviceOnly || client.second->hasPreferredDevice())) {
+ clients.push_back(client.second);
+ }
+ }
+ return clients;
+}
+
status_t AudioOutputDescriptor::dump(int fd)
{
const size_t SIZE = 256;
@@ -217,11 +265,11 @@
result.append(buffer);
snprintf(buffer, SIZE, " Devices %08x\n", device());
result.append(buffer);
- snprintf(buffer, SIZE, " Stream volume refCount muteCount\n");
+ snprintf(buffer, SIZE, " Stream volume activeCount muteCount\n");
result.append(buffer);
for (int i = 0; i < (int)AUDIO_STREAM_CNT; i++) {
- snprintf(buffer, SIZE, " %02d %.03f %02d %02d\n",
- i, mCurVolume[i], mRefCount[i], mMuteCount[i]);
+ snprintf(buffer, SIZE, " %02d %.03f %02d %02d\n",
+ i, mCurVolume[i], streamActiveCount((audio_stream_type_t)i), mMuteCount[i]);
result.append(buffer);
}
@@ -247,9 +295,9 @@
AudioPolicyClientInterface *clientInterface)
: AudioOutputDescriptor(profile, clientInterface),
mProfile(profile), mIoHandle(AUDIO_IO_HANDLE_NONE), mLatency(0),
- mFlags((audio_output_flags_t)0), mPolicyMix(NULL),
+ mFlags((audio_output_flags_t)0),
mOutput1(0), mOutput2(0), mDirectOpenCount(0),
- mDirectClientSession(AUDIO_SESSION_NONE), mGlobalRefCount(0)
+ mDirectClientSession(AUDIO_SESSION_NONE)
{
if (profile != NULL) {
mFlags = (audio_output_flags_t)profile->getFlags();
@@ -313,41 +361,17 @@
}
}
-void SwAudioOutputDescriptor::changeRefCount(audio_stream_type_t stream,
+void SwAudioOutputDescriptor::changeStreamActiveCount(audio_stream_type_t stream,
int delta)
{
// forward usage count change to attached outputs
if (isDuplicated()) {
- mOutput1->changeRefCount(stream, delta);
- mOutput2->changeRefCount(stream, delta);
+ mOutput1->changeStreamActiveCount(stream, delta);
+ mOutput2->changeStreamActiveCount(stream, delta);
}
- AudioOutputDescriptor::changeRefCount(stream, delta);
-
- // handle stream-independent ref count
- uint32_t oldGlobalRefCount = mGlobalRefCount;
- if ((delta + (int)mGlobalRefCount) < 0) {
- ALOGW("changeRefCount() invalid delta %d globalRefCount %d", delta, mGlobalRefCount);
- mGlobalRefCount = 0;
- } else {
- mGlobalRefCount += delta;
- }
- if ((oldGlobalRefCount == 0) && (mGlobalRefCount > 0)) {
- if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0))
- {
- mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress,
- MIX_STATE_MIXING);
- }
-
- } else if ((oldGlobalRefCount > 0) && (mGlobalRefCount == 0)) {
- if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0))
- {
- mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress,
- MIX_STATE_IDLE);
- }
- }
+ AudioOutputDescriptor::changeStreamActiveCount(stream, delta);
}
-
bool SwAudioOutputDescriptor::isFixedVolume(audio_devices_t device)
{
// unit gain if rerouting to external policy
@@ -523,7 +547,7 @@
LOG_ALWAYS_FATAL_IF(mProfile->curOpenCount < 1, "%s profile open count %u",
__FUNCTION__, mProfile->curOpenCount);
- // do not call stop() here as stop() is supposed to be called after changeRefCount(-1)
+ // do not call stop() here as stop() is supposed to be called after setClientActive(false)
// and we don't know how many streams are still active at this time
if (isActive()) {
mProfile->curActiveCount--;
@@ -723,7 +747,7 @@
}
for (size_t i = 0; i < size(); i++) {
const sp<SwAudioOutputDescriptor> outputDesc = valueAt(i);
- if (outputDesc->mRefCount[s] != 0) {
+ if (outputDesc->streamActiveCount((audio_stream_type_t)s)!= 0) {
return true;
}
}
@@ -742,7 +766,7 @@
{
for (size_t i = 0; i < size(); i++) {
sp<SwAudioOutputDescriptor> outputDesc = valueAt(i);
- for (const auto& client : outputDesc->clients()) {
+ for (const auto& client : outputDesc->clientsMap()) {
if (client.second->portId() == portId) {
return outputDesc;
}
@@ -788,7 +812,7 @@
}
for (size_t i = 0; i < size(); i++) {
const sp<HwAudioOutputDescriptor> outputDesc = valueAt(i);
- if (outputDesc->mRefCount[s] != 0) {
+ if (outputDesc->streamActiveCount((audio_stream_type_t)s) != 0) {
return true;
}
}
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp
deleted file mode 100644
index 5ea4c92..0000000
--- a/services/audiopolicy/common/managerdefinitions/src/AudioSession.cpp
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "APM::AudioSession"
-//#define LOG_NDEBUG 0
-
-#include <AudioPolicyInterface.h>
-#include "policy.h"
-#include "AudioSession.h"
-#include "AudioGain.h"
-#include "TypeConverter.h"
-
-#include <log/log.h>
-#include <utils/String8.h>
-
-namespace android {
-
-AudioSession::AudioSession(audio_session_t session,
- audio_source_t inputSource,
- audio_format_t format,
- uint32_t sampleRate,
- audio_channel_mask_t channelMask,
- audio_input_flags_t flags,
- uid_t uid,
- bool isSoundTrigger) :
- mRecordClientInfo({ .uid = uid, .session = session, .source = inputSource}),
- mConfig({ .format = format, .sample_rate = sampleRate, .channel_mask = channelMask}),
- mFlags(flags), mIsSoundTrigger(isSoundTrigger),
- mOpenCount(1), mActiveCount(0)
-{
-}
-
-uint32_t AudioSession::changeOpenCount(int delta)
-{
- if ((delta + (int)mOpenCount) < 0) {
- ALOGW("%s invalid delta %d, open count %d",
- __FUNCTION__, delta, mOpenCount);
- mOpenCount = (uint32_t)(-delta);
- }
- mOpenCount += delta;
- ALOGV("%s open count %d", __FUNCTION__, mOpenCount);
- return mOpenCount;
-}
-
-uint32_t AudioSession::changeActiveCount(int delta)
-{
- if ((delta + (int)mActiveCount) < 0) {
- ALOGW("%s invalid delta %d, active count %d",
- __FUNCTION__, delta, mActiveCount);
- mActiveCount = (uint32_t)(-delta);
- }
- mActiveCount += delta;
- ALOGV("%s active count %d", __FUNCTION__, mActiveCount);
-
- return mActiveCount;
-}
-
-bool AudioSession::matches(const sp<AudioSession> &other) const
-{
- if (other->session() == mRecordClientInfo.session &&
- other->inputSource() == mRecordClientInfo.source &&
- other->format() == mConfig.format &&
- other->sampleRate() == mConfig.sample_rate &&
- other->channelMask() == mConfig.channel_mask &&
- other->flags() == mFlags &&
- other->uid() == mRecordClientInfo.uid) {
- return true;
- }
- return false;
-}
-
-status_t AudioSession::dump(int fd, int spaces, int index) const
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
-
- snprintf(buffer, SIZE, "%*sAudio session %d:\n", spaces, "", index+1);
- result.append(buffer);
- snprintf(buffer, SIZE, "%*s- session: %2d\n", spaces, "", mRecordClientInfo.session);
- result.append(buffer);
- snprintf(buffer, SIZE, "%*s- owner uid: %2d\n", spaces, "", mRecordClientInfo.uid);
- result.append(buffer);
- snprintf(buffer, SIZE, "%*s- input source: %d\n", spaces, "", mRecordClientInfo.source);
- result.append(buffer);
- snprintf(buffer, SIZE, "%*s- format: %08x\n", spaces, "", mConfig.format);
- result.append(buffer);
- snprintf(buffer, SIZE, "%*s- sample: %d\n", spaces, "", mConfig.sample_rate);
- result.append(buffer);
- snprintf(buffer, SIZE, "%*s- channel mask: %08x\n",
- spaces, "", mConfig.channel_mask);
- result.append(buffer);
- snprintf(buffer, SIZE, "%*s- is soundtrigger: %s\n",
- spaces, "", mIsSoundTrigger ? "true" : "false");
- result.append(buffer);
- snprintf(buffer, SIZE, "%*s- open count: %d\n", spaces, "", mOpenCount);
- result.append(buffer);
- snprintf(buffer, SIZE, "%*s- active count: %d\n", spaces, "", mActiveCount);
- result.append(buffer);
-
- write(fd, result.string(), result.size());
- return NO_ERROR;
-}
-
-status_t AudioSessionCollection::addSession(audio_session_t session,
- const sp<AudioSession>& audioSession)
-{
- ssize_t index = indexOfKey(session);
-
- if (index >= 0) {
- ALOGW("addSession() session %d already in", session);
- return ALREADY_EXISTS;
- }
- add(session, audioSession);
- ALOGV("addSession() session %d client %d source %d",
- session, audioSession->uid(), audioSession->inputSource());
- return NO_ERROR;
-}
-
-status_t AudioSessionCollection::removeSession(audio_session_t session)
-{
- ssize_t index = indexOfKey(session);
-
- if (index < 0) {
- ALOGW("removeSession() session %d not in", session);
- return ALREADY_EXISTS;
- }
- ALOGV("removeSession() session %d", session);
- removeItemsAt(index);
- return NO_ERROR;
-}
-
-uint32_t AudioSessionCollection::getOpenCount() const
-{
- uint32_t openCount = 0;
- for (size_t i = 0; i < size(); i++) {
- openCount += valueAt(i)->openCount();
- }
- return openCount;
-}
-
-AudioSessionCollection AudioSessionCollection::getActiveSessions() const
-{
- AudioSessionCollection activeSessions;
- for (size_t i = 0; i < size(); i++) {
- if (valueAt(i)->activeCount() != 0) {
- activeSessions.add(valueAt(i)->session(), valueAt(i));
- }
- }
- return activeSessions;
-}
-
-size_t AudioSessionCollection::getActiveSessionCount() const
-{
- size_t activeCount = 0;
- for (size_t i = 0; i < size(); i++) {
- if (valueAt(i)->activeCount() != 0) {
- activeCount++;
- }
- }
- return activeCount;
-}
-
-bool AudioSessionCollection::hasActiveSession() const
-{
- return getActiveSessionCount() != 0;
-}
-
-bool AudioSessionCollection::isSourceActive(audio_source_t source) const
-{
- for (size_t i = 0; i < size(); i++) {
- const sp<AudioSession> audioSession = valueAt(i);
- // AUDIO_SOURCE_HOTWORD is equivalent to AUDIO_SOURCE_VOICE_RECOGNITION only if it
- // corresponds to an active capture triggered by a hardware hotword recognition
- if (audioSession->activeCount() > 0 &&
- ((audioSession->inputSource() == source) ||
- ((source == AUDIO_SOURCE_VOICE_RECOGNITION) &&
- (audioSession->inputSource() == AUDIO_SOURCE_HOTWORD) &&
- audioSession->isSoundTrigger()))) {
- return true;
- }
- }
- return false;
-}
-
-audio_source_t AudioSessionCollection::getHighestPrioritySource(bool activeOnly) const
-{
- audio_source_t source = AUDIO_SOURCE_DEFAULT;
- int32_t priority = -1;
-
- for (size_t i = 0; i < size(); i++) {
- const sp<AudioSession> audioSession = valueAt(i);
- if (activeOnly && audioSession->activeCount() == 0) {
- continue;
- }
- int32_t curPriority = source_priority(audioSession->inputSource());
- if (curPriority > priority) {
- priority = curPriority;
- source = audioSession->inputSource();
- }
- }
- return source;
-}
-
-status_t AudioSessionCollection::dump(int fd, int spaces) const
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- snprintf(buffer, SIZE, "%*sAudio Sessions:\n", spaces, "");
- write(fd, buffer, strlen(buffer));
- for (size_t i = 0; i < size(); i++) {
- valueAt(i)->dump(fd, spaces + 2, i);
- }
- return NO_ERROR;
-}
-
-} // namespace android
diff --git a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp
index 5aca3cc..0d65a31 100644
--- a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp
@@ -76,9 +76,11 @@
SourceClientDescriptor::SourceClientDescriptor(audio_port_handle_t portId, uid_t uid,
audio_attributes_t attributes, const sp<AudioPatch>& patchDesc,
- const sp<DeviceDescriptor>& srcDevice, audio_stream_type_t stream) :
+ const sp<DeviceDescriptor>& srcDevice, audio_stream_type_t stream,
+ routing_strategy strategy) :
TrackClientDescriptor::TrackClientDescriptor(portId, uid, AUDIO_SESSION_NONE, attributes,
- AUDIO_CONFIG_BASE_INITIALIZER, AUDIO_PORT_HANDLE_NONE, stream, AUDIO_OUTPUT_FLAG_NONE),
+ AUDIO_CONFIG_BASE_INITIALIZER, AUDIO_PORT_HANDLE_NONE,
+ stream, strategy, AUDIO_OUTPUT_FLAG_NONE),
mPatchDesc(patchDesc), mSrcDevice(srcDevice)
{
}
diff --git a/services/audiopolicy/common/managerdefinitions/src/SessionRoute.cpp b/services/audiopolicy/common/managerdefinitions/src/SessionRoute.cpp
deleted file mode 100644
index 440a4e7..0000000
--- a/services/audiopolicy/common/managerdefinitions/src/SessionRoute.cpp
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "APM_SessionRoute"
-//#define LOG_NDEBUG 0
-
-#include "SessionRoute.h"
-#include "HwModule.h"
-#include "AudioGain.h"
-#include "DeviceDescriptor.h"
-#include <utils/Log.h>
-
-namespace android {
-
-// --- SessionRoute class implementation
-void SessionRoute::log(const char* prefix)
-{
- ALOGI("%s[SessionRoute strm:0x%X, src:%d, sess:0x%X, dev:0x%X refs:%d act:%d",
- prefix, mStreamType, mSource, mSession,
- mDeviceDescriptor != 0 ? mDeviceDescriptor->type() : AUDIO_DEVICE_NONE,
- mRefCount, mActivityCount);
-}
-
-// --- SessionRouteMap class implementation
-bool SessionRouteMap::hasRoute(audio_session_t session)
-{
- return indexOfKey(session) >= 0 && valueFor(session)->mDeviceDescriptor != 0;
-}
-
-bool SessionRouteMap::getAndClearRouteChanged(audio_session_t session)
-{
- if (indexOfKey(session) >= 0) {
- if (valueFor(session)->mChanged) {
- valueFor(session)->mChanged = false;
- return true;
- }
- }
- return false;
-}
-
-void SessionRouteMap::removeRoute(audio_session_t session)
-{
- sp<SessionRoute> route = indexOfKey(session) >= 0 ? valueFor(session) : 0;
- if (route != 0) {
- ALOG_ASSERT(route->mRefCount > 0);
- --route->mRefCount;
- if (route->mRefCount <= 0) {
- removeItem(session);
- }
- }
-}
-
-int SessionRouteMap::incRouteActivity(audio_session_t session)
-{
- sp<SessionRoute> route = indexOfKey(session) >= 0 ? valueFor(session) : 0;
- return route != 0 ? ++(route->mActivityCount) : -1;
-}
-
-int SessionRouteMap::decRouteActivity(audio_session_t session)
-{
- sp<SessionRoute> route = indexOfKey(session) >= 0 ? valueFor(session) : 0;
- if (route != 0 && route->mActivityCount > 0) {
- return --(route->mActivityCount);
- } else {
- return -1;
- }
-}
-
-void SessionRouteMap::log(const char* caption)
-{
- ALOGI("%s ----", caption);
- for (size_t index = 0; index < size(); index++) {
- valueAt(index)->log(" ");
- }
-}
-
-void SessionRouteMap::addRoute(audio_session_t session,
- audio_stream_type_t streamType,
- audio_source_t source,
- const sp<DeviceDescriptor>& descriptor,
- uid_t uid)
-{
- if (mMapType == MAPTYPE_INPUT && streamType != SessionRoute::STREAM_TYPE_NA) {
- ALOGE("Adding Output Route to InputRouteMap");
- return;
- } else if (mMapType == MAPTYPE_OUTPUT && source != SessionRoute::SOURCE_TYPE_NA) {
- ALOGE("Adding Input Route to OutputRouteMap");
- return;
- }
-
- sp<SessionRoute> route = indexOfKey(session) >= 0 ? valueFor(session) : 0;
-
- if (route != 0) {
- if (descriptor != 0 || route->mDeviceDescriptor != 0) {
- route->mChanged = true;
- }
- route->mRefCount++;
- route->mDeviceDescriptor = descriptor;
- } else {
- route = new SessionRoute(session, streamType, source, descriptor, uid);
- route->mRefCount++;
- if (descriptor != 0) {
- route->mChanged = true;
- }
- add(session, route);
- }
-}
-
-audio_devices_t SessionRouteMap::getActiveDeviceForStream(audio_stream_type_t streamType,
- const DeviceVector& availableDevices)
-{
- for (size_t index = 0; index < size(); index++) {
- sp<SessionRoute> route = valueAt(index);
- if (streamType == route->mStreamType && route->isActiveOrChanged()
- && route->mDeviceDescriptor != 0) {
- audio_devices_t device = route->mDeviceDescriptor->type();
- if (!availableDevices.getDevicesFromTypeMask(device).isEmpty()) {
- return device;
- }
- }
- }
- return AUDIO_DEVICE_NONE;
-}
-
-} // namespace android
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 2a8b397..60bce9c 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -517,7 +517,7 @@
// symmetric to the one in startInput()
for (const auto& activeDesc : mInputs.getActiveInputs()) {
if (activeDesc->hasSameHwModuleAs(txSourceDeviceDesc)) {
- closeSessions(activeDesc, true /*activeOnly*/);
+ closeActiveClients(activeDesc);
}
}
}
@@ -788,9 +788,14 @@
DeviceVector outputDevices;
routing_strategy strategy;
audio_devices_t device;
- audio_port_handle_t requestedDeviceId = *selectedDeviceId;
+ const audio_port_handle_t requestedDeviceId = *selectedDeviceId;
audio_devices_t msdDevice = getMsdAudioOutDeviceTypes();
+ // The supplied portId must be AUDIO_PORT_HANDLE_NONE
+ if (*portId != AUDIO_PORT_HANDLE_NONE) {
+ return INVALID_OPERATION;
+ }
+
if (attr != NULL) {
if (!isValidAttributes(attr)) {
ALOGE("getOutputForAttr() invalid attributes: usage=%d content=%d flags=0x%x tags=[%s]",
@@ -808,19 +813,19 @@
}
ALOGV("getOutputForAttr() usage=%d, content=%d, tag=%s flags=%08x"
- " session %d selectedDeviceId %d",
- attributes.usage, attributes.content_type, attributes.tags, attributes.flags,
- session, *selectedDeviceId);
+ " session %d selectedDeviceId %d",
+ attributes.usage, attributes.content_type, attributes.tags, attributes.flags,
+ session, requestedDeviceId);
- // TODO: check for existing client for this port ID
- if (*portId == AUDIO_PORT_HANDLE_NONE) {
- *portId = AudioPort::getNextUniqueId();
- }
+ *stream = streamTypefromAttributesInt(&attributes);
+
+ strategy = getStrategyForAttr(&attributes);
// First check for explicit routing (eg. setPreferredDevice)
- sp<DeviceDescriptor> deviceDesc;
- if (*selectedDeviceId != AUDIO_PORT_HANDLE_NONE) {
- deviceDesc = mAvailableOutputDevices.getDeviceFromId(*selectedDeviceId);
+ if (requestedDeviceId != AUDIO_PORT_HANDLE_NONE) {
+ sp<DeviceDescriptor> deviceDesc =
+ mAvailableOutputDevices.getDeviceFromId(requestedDeviceId);
+ device = deviceDesc->type();
} else {
// If no explict route, is there a matching dynamic policy that applies?
sp<SwAudioOutputDescriptor> desc;
@@ -831,6 +836,10 @@
}
*stream = streamTypefromAttributesInt(&attributes);
*output = desc->mIoHandle;
+ AudioMix *mix = desc->mPolicyMix;
+ sp<DeviceDescriptor> deviceDesc =
+ mAvailableOutputDevices.getDevice(mix->mDeviceType, mix->mDeviceAddress);
+ *selectedDeviceId = deviceDesc != 0 ? deviceDesc->getId() : AUDIO_PORT_HANDLE_NONE;
ALOGV("getOutputForAttr() returns output %d", *output);
goto exit;
}
@@ -840,25 +849,9 @@
ALOGW("getOutputForAttr() no policy mix found for usage AUDIO_USAGE_VIRTUAL_SOURCE");
return BAD_VALUE;
}
+ device = getDeviceForStrategy(strategy, false /*fromCache*/);
}
- // Virtual sources must always be dynamicaly or explicitly routed
- if (attributes.usage == AUDIO_USAGE_VIRTUAL_SOURCE) {
- ALOGW("getOutputForAttr() no policy mix found for usage AUDIO_USAGE_VIRTUAL_SOURCE");
- return BAD_VALUE;
- }
-
- *stream = streamTypefromAttributesInt(&attributes);
-
- // TODO: Should this happen only if an explicit route is active?
- // the previous code structure meant that this would always happen which
- // would appear to result in adding a null deviceDesc when not using an
- // explicit route. Is that the intended and necessary behavior?
- mOutputRoutes.addRoute(session, *stream, SessionRoute::SOURCE_TYPE_NA, deviceDesc, uid);
-
- strategy = (routing_strategy) getStrategyForAttr(&attributes);
- device = getDeviceForStrategy(strategy, false /*fromCache*/);
-
if ((attributes.flags & AUDIO_FLAG_HW_AV_SYNC) != 0) {
*flags = (audio_output_flags_t)(*flags | AUDIO_OUTPUT_FLAG_HW_AV_SYNC);
}
@@ -871,9 +864,10 @@
*stream == AUDIO_STREAM_MUSIC &&
audio_is_linear_pcm(config->format) &&
isInCall()) {
- if (*selectedDeviceId != AUDIO_PORT_HANDLE_NONE) {
+ if (requestedDeviceId != AUDIO_PORT_HANDLE_NONE) {
*flags = (audio_output_flags_t)AUDIO_OUTPUT_FLAG_INCALL_MUSIC;
} else {
+ // Get the devce type directly from the engine to bypass preferred route logic
device = mEngine->getDeviceForStrategy(strategy);
}
}
@@ -897,7 +891,6 @@
*output = getOutputForDevice(device, session, *stream, config, flags);
}
if (*output == AUDIO_IO_HANDLE_NONE) {
- mOutputRoutes.removeRoute(session);
return INVALID_OPERATION;
}
@@ -909,11 +902,15 @@
audio_config_base_t clientConfig = {.sample_rate = config->sample_rate,
.format = config->format,
.channel_mask = config->channel_mask };
+ *portId = AudioPort::getNextUniqueId();
+
sp<TrackClientDescriptor> clientDesc =
- new TrackClientDescriptor(*portId, uid, session,
- attributes, clientConfig, requestedDeviceId, *stream, *flags);
+ new TrackClientDescriptor(*portId, uid, session, attributes, clientConfig,
+ requestedDeviceId, *stream,
+ getStrategyForAttr(&attributes),
+ *flags);
sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueFor(*output);
- outputDesc->clients().emplace(*portId, clientDesc);
+ outputDesc->clientsMap().emplace(*portId, clientDesc);
ALOGV(" getOutputForAttr() returns output %d selectedDeviceId %d for port ID %d",
*output, *selectedDeviceId, *portId);
@@ -1037,8 +1034,6 @@
}
return AUDIO_IO_HANDLE_NONE;
}
- outputDesc->mRefCount[stream] = 0;
- outputDesc->mStopTime[stream] = 0;
outputDesc->mDirectOpenCount = 1;
outputDesc->mDirectClientSession = session;
@@ -1325,60 +1320,23 @@
ALOGW("startOutput() no output for client %d", portId);
return BAD_VALUE;
}
- sp<TrackClientDescriptor> client = outputDesc->clients()[portId];
- audio_stream_type_t stream = client->stream();
- audio_session_t session = client->session();
+ sp<TrackClientDescriptor> client = outputDesc->clientsMap()[portId];
ALOGV("startOutput() output %d, stream %d, session %d",
- outputDesc->mIoHandle, stream, session);
+ outputDesc->mIoHandle, client->stream(), client->session());
status_t status = outputDesc->start();
if (status != NO_ERROR) {
return status;
}
- // Routing?
- mOutputRoutes.incRouteActivity(session);
-
- audio_devices_t newDevice;
- AudioMix *policyMix = NULL;
- const char *address = NULL;
- if (outputDesc->mPolicyMix != NULL) {
- policyMix = outputDesc->mPolicyMix;
- address = policyMix->mDeviceAddress.string();
- if ((policyMix->mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) {
- newDevice = policyMix->mDeviceType;
- } else {
- newDevice = AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
- }
- } else if (mOutputRoutes.getAndClearRouteChanged(session)) {
- newDevice = getNewOutputDevice(outputDesc, false /*fromCache*/);
- if (newDevice != outputDesc->device()) {
- checkStrategyRoute(getStrategy(stream), outputDesc->mIoHandle);
- }
- } else {
- newDevice = AUDIO_DEVICE_NONE;
- }
-
- uint32_t delayMs = 0;
-
- status = startSource(outputDesc, stream, newDevice, address, &delayMs);
+ uint32_t delayMs;
+ status = startSource(outputDesc, client, &delayMs);
if (status != NO_ERROR) {
- mOutputRoutes.decRouteActivity(session);
outputDesc->stop();
return status;
}
- // Automatically enable the remote submix input when output is started on a re routing mix
- // of type MIX_TYPE_RECORDERS
- if (audio_is_remote_submix_device(newDevice) && policyMix != NULL &&
- policyMix->mMixType == MIX_TYPE_RECORDERS) {
- setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX,
- AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
- address,
- "remote-submix");
- }
-
if (delayMs != 0) {
usleep(delayMs * 1000);
}
@@ -1386,16 +1344,15 @@
return status;
}
-status_t AudioPolicyManager::startSource(const sp<AudioOutputDescriptor>& outputDesc,
- audio_stream_type_t stream,
- audio_devices_t device,
- const char *address,
- uint32_t *delayMs)
+status_t AudioPolicyManager::startSource(const sp<SwAudioOutputDescriptor>& outputDesc,
+ const sp<TrackClientDescriptor>& client,
+ uint32_t *delayMs)
{
// cannot start playback of STREAM_TTS if any other output is being used
uint32_t beaconMuteLatency = 0;
*delayMs = 0;
+ audio_stream_type_t stream = client->stream();
if (stream == AUDIO_STREAM_TTS) {
ALOGV("\t found BEACON stream");
if (!mTtsOutputAvailable && mOutputs.isAnyOutputActive(AUDIO_STREAM_TTS /*streamToIgnore*/)) {
@@ -1413,6 +1370,19 @@
bool force = !outputDesc->isActive() &&
(outputDesc->getPatchHandle() == AUDIO_PATCH_HANDLE_NONE);
+ audio_devices_t device = AUDIO_DEVICE_NONE;
+ AudioMix *policyMix = NULL;
+ const char *address = NULL;
+ if (outputDesc->mPolicyMix != NULL) {
+ policyMix = outputDesc->mPolicyMix;
+ address = policyMix->mDeviceAddress.string();
+ if ((policyMix->mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) {
+ device = policyMix->mDeviceType;
+ } else {
+ device = AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
+ }
+ }
+
// requiresMuteCheck is false when we can bypass mute strategy.
// It covers a common case when there is no materially active audio
// and muting would result in unnecessary delay and dropped audio.
@@ -1422,13 +1392,20 @@
// increment usage count for this stream on the requested output:
// NOTE that the usage count is the same for duplicated output and hardware output which is
// necessary for a correct control of hardware output routing by startOutput() and stopOutput()
- outputDesc->changeRefCount(stream, 1);
+ outputDesc->setClientActive(client, true);
+
+ if (client->hasPreferredDevice(true)) {
+ device = getNewOutputDevice(outputDesc, false /*fromCache*/);
+ if (device != outputDesc->device()) {
+ checkStrategyRoute(getStrategy(stream), outputDesc->mIoHandle);
+ }
+ }
if (stream == AUDIO_STREAM_MUSIC) {
selectOutputForMusicEffects();
}
- if (outputDesc->mRefCount[stream] == 1 || device != AUDIO_DEVICE_NONE) {
+ if (outputDesc->streamActiveCount(stream) == 1 || device != AUDIO_DEVICE_NONE) {
// starting an output being rerouted?
if (device == AUDIO_DEVICE_NONE) {
device = getNewOutputDevice(outputDesc, false /*fromCache*/);
@@ -1514,6 +1491,16 @@
setStrategyMute(STRATEGY_SONIFICATION, true, outputDesc);
}
+ // Automatically enable the remote submix input when output is started on a re routing mix
+ // of type MIX_TYPE_RECORDERS
+ if (audio_is_remote_submix_device(device) && policyMix != NULL &&
+ policyMix->mMixType == MIX_TYPE_RECORDERS) {
+ setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX,
+ AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
+ address,
+ "remote-submix");
+ }
+
return NO_ERROR;
}
@@ -1526,37 +1513,12 @@
ALOGW("stopOutput() no output for client %d", portId);
return BAD_VALUE;
}
- sp<TrackClientDescriptor> client = outputDesc->clients()[portId];
- audio_stream_type_t stream = client->stream();
- audio_session_t session = client->session();
+ sp<TrackClientDescriptor> client = outputDesc->clientsMap()[portId];
- ALOGV("stopOutput() output %d, stream %d, session %d", outputDesc->mIoHandle, stream, session);
+ ALOGV("stopOutput() output %d, stream %d, session %d",
+ outputDesc->mIoHandle, client->stream(), client->session());
- if (outputDesc->mRefCount[stream] == 1) {
- // Automatically disable the remote submix input when output is stopped on a
- // re routing mix of type MIX_TYPE_RECORDERS
- if (audio_is_remote_submix_device(outputDesc->mDevice) &&
- outputDesc->mPolicyMix != NULL &&
- outputDesc->mPolicyMix->mMixType == MIX_TYPE_RECORDERS) {
- setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX,
- AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
- outputDesc->mPolicyMix->mDeviceAddress,
- "remote-submix");
- }
- }
-
- // Routing?
- bool forceDeviceUpdate = false;
- if (outputDesc->mRefCount[stream] > 0) {
- int activityCount = mOutputRoutes.decRouteActivity(session);
- forceDeviceUpdate = (mOutputRoutes.hasRoute(session) && (activityCount == 0));
-
- if (forceDeviceUpdate) {
- checkStrategyRoute(getStrategy(stream), AUDIO_IO_HANDLE_NONE);
- }
- }
-
- status_t status = stopSource(outputDesc, stream, forceDeviceUpdate);
+ status_t status = stopSource(outputDesc, client);
if (status == NO_ERROR ) {
outputDesc->stop();
@@ -1564,19 +1526,38 @@
return status;
}
-status_t AudioPolicyManager::stopSource(const sp<AudioOutputDescriptor>& outputDesc,
- audio_stream_type_t stream,
- bool forceDeviceUpdate)
+status_t AudioPolicyManager::stopSource(const sp<SwAudioOutputDescriptor>& outputDesc,
+ const sp<TrackClientDescriptor>& client)
{
// always handle stream stop, check which stream type is stopping
+ audio_stream_type_t stream = client->stream();
+
handleEventForBeacon(stream == AUDIO_STREAM_TTS ? STOPPING_BEACON : STOPPING_OUTPUT);
- if (outputDesc->mRefCount[stream] > 0) {
+ if (outputDesc->streamActiveCount(stream) > 0) {
+ if (outputDesc->streamActiveCount(stream) == 1) {
+ // Automatically disable the remote submix input when output is stopped on a
+ // re routing mix of type MIX_TYPE_RECORDERS
+ if (audio_is_remote_submix_device(outputDesc->mDevice) &&
+ outputDesc->mPolicyMix != NULL &&
+ outputDesc->mPolicyMix->mMixType == MIX_TYPE_RECORDERS) {
+ setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX,
+ AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
+ outputDesc->mPolicyMix->mDeviceAddress,
+ "remote-submix");
+ }
+ }
+ bool forceDeviceUpdate = false;
+ if (client->hasPreferredDevice(true)) {
+ checkStrategyRoute(getStrategy(stream), AUDIO_IO_HANDLE_NONE);
+ forceDeviceUpdate = true;
+ }
+
// decrement usage count of this stream on the output
- outputDesc->changeRefCount(stream, -1);
+ outputDesc->setClientActive(client, false);
// store time at which the stream was stopped - see isStreamActive()
- if (outputDesc->mRefCount[stream] == 0 || forceDeviceUpdate) {
+ if (outputDesc->streamActiveCount(stream) == 0 || forceDeviceUpdate) {
outputDesc->mStopTime[stream] = systemTime();
audio_devices_t newDevice = getNewOutputDevice(outputDesc, false /*fromCache*/);
// delay the device switch by twice the latency because stopOutput() is executed when
@@ -1636,14 +1617,10 @@
ALOGW("releaseOutput() no output for client %d", portId);
return;
}
- sp<TrackClientDescriptor> client = outputDesc->clients()[portId];
- audio_session_t session = client->session();
+ sp<TrackClientDescriptor> client = outputDesc->clientsMap()[portId];
ALOGV("releaseOutput() %d", outputDesc->mIoHandle);
- // Routing
- mOutputRoutes.removeRoute(session);
-
if (outputDesc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) {
if (outputDesc->mDirectOpenCount <= 0) {
ALOGW("releaseOutput() invalid open count %d for output %d",
@@ -1655,7 +1632,7 @@
mpClientInterface->onAudioPortListUpdate();
}
}
- outputDesc->clients().erase(portId);
+ outputDesc->clientsMap().erase(portId);
}
@@ -1683,6 +1660,13 @@
sp<AudioInputDescriptor> inputDesc;
sp<RecordClientDescriptor> clientDesc;
audio_port_handle_t requestedDeviceId = *selectedDeviceId;
+ bool isSoundTrigger;
+ audio_devices_t device;
+
+ // The supplied portId must be AUDIO_PORT_HANDLE_NONE
+ if (*portId != AUDIO_PORT_HANDLE_NONE) {
+ return INVALID_OPERATION;
+ }
if (inputSource == AUDIO_SOURCE_DEFAULT) {
inputSource = AUDIO_SOURCE_MIC;
@@ -1693,7 +1677,6 @@
if (*selectedDeviceId != AUDIO_PORT_HANDLE_NONE) {
deviceDesc = mAvailableInputDevices.getDeviceFromId(*selectedDeviceId);
}
- mInputRoutes.addRoute(session, SessionRoute::STREAM_TYPE_NA, inputSource, deviceDesc, uid);
// special case for mmap capture: if an input IO handle is specified, we reuse this input if
// possible
@@ -1706,8 +1689,8 @@
goto error;
}
sp<AudioInputDescriptor> inputDesc = mInputs.valueAt(index);
- sp<AudioSession> audioSession = inputDesc->getAudioSession(session);
- if (audioSession == 0) {
+ RecordClientVector clients = inputDesc->getClientsForSession(session);
+ if (clients.size() == 0) {
ALOGW("getInputForAttr() unknown session %d on input %d", session, *input);
status = BAD_VALUE;
goto error;
@@ -1716,28 +1699,27 @@
// The second call is for the first active client and sets the UID. Any further call
// corresponds to a new client and is only permitted from the same UID.
// If the first UID is silenced, allow a new UID connection and replace with new UID
- if (audioSession->openCount() == 1) {
- audioSession->setUid(uid);
- } else if (audioSession->uid() != uid) {
- if (!audioSession->isSilenced()) {
- ALOGW("getInputForAttr() bad uid %d for session %d uid %d",
- uid, session, audioSession->uid());
- status = INVALID_OPERATION;
- goto error;
+ if (clients.size() > 1) {
+ for (const auto& client : clients) {
+ // The client map is ordered by key values (portId) and portIds are allocated
+ // incrementaly. So the first client in this list is the one opened by audio flinger
+ // when the mmap stream is created and should be ignored as it does not correspond
+ // to an actual client
+ if (client == *clients.cbegin()) {
+ continue;
+ }
+ if (uid != client->uid() && !client->isSilenced()) {
+ ALOGW("getInputForAttr() bad uid %d for client %d uid %d",
+ uid, client->portId(), client->uid());
+ status = INVALID_OPERATION;
+ goto error;
+ }
}
- audioSession->setUid(uid);
- audioSession->setSilenced(false);
}
- audioSession->changeOpenCount(1);
*inputType = API_INPUT_LEGACY;
- if (*portId == AUDIO_PORT_HANDLE_NONE) {
- *portId = AudioPort::getNextUniqueId();
- }
- inputDevices = mAvailableInputDevices.getDevicesFromTypeMask(inputDesc->mDevice);
- *selectedDeviceId = inputDevices.size() > 0 ? inputDevices.itemAt(0)->getId()
- : AUDIO_PORT_HANDLE_NONE;
- ALOGI("%s reusing MMAP input %d for session %d", __FUNCTION__, *input, session);
+ device = inputDesc->mDevice;
+ ALOGI("%s reusing MMAP input %d for session %d", __FUNCTION__, *input, session);
goto exit;
}
@@ -1746,13 +1728,6 @@
halInputSource = inputSource;
- // TODO: check for existing client for this port ID
- if (*portId == AUDIO_PORT_HANDLE_NONE) {
- *portId = AudioPort::getNextUniqueId();
- }
-
- audio_devices_t device;
-
if (inputSource == AUDIO_SOURCE_REMOTE_SUBMIX &&
strncmp(attr->tags, "addr=", strlen("addr=")) == 0) {
status = mPolicyMixes.getInputMixForAttr(*attr, &policyMix);
@@ -1763,7 +1738,11 @@
device = AUDIO_DEVICE_IN_REMOTE_SUBMIX;
address = String8(attr->tags + strlen("addr="));
} else {
- device = getDeviceAndMixForInputSource(inputSource, &policyMix);
+ if (deviceDesc != 0) {
+ device = deviceDesc->type();
+ } else {
+ device = getDeviceAndMixForInputSource(inputSource, &policyMix);
+ }
if (device == AUDIO_DEVICE_NONE) {
ALOGW("getInputForAttr() could not find device for source %d", inputSource);
status = BAD_VALUE;
@@ -1792,7 +1771,7 @@
}
- *input = getInputForDevice(device, address, session, uid, inputSource,
+ *input = getInputForDevice(device, address, session, inputSource,
config, flags,
policyMix);
if (*input == AUDIO_IO_HANDLE_NONE) {
@@ -1800,15 +1779,21 @@
goto error;
}
+exit:
+
inputDevices = mAvailableInputDevices.getDevicesFromTypeMask(device);
*selectedDeviceId = inputDevices.size() > 0 ? inputDevices.itemAt(0)->getId()
- : AUDIO_PORT_HANDLE_NONE;
+ : AUDIO_PORT_HANDLE_NONE;
-exit:
+ isSoundTrigger = inputSource == AUDIO_SOURCE_HOTWORD &&
+ mSoundTriggerSessions.indexOfKey(session) > 0;
+ *portId = AudioPort::getNextUniqueId();
+
clientDesc = new RecordClientDescriptor(*portId, uid, session,
- *attr, *config, requestedDeviceId, inputSource, flags);
+ *attr, *config, requestedDeviceId,
+ inputSource,flags, isSoundTrigger);
inputDesc = mInputs.valueFor(*input);
- inputDesc->clients().emplace(*portId, clientDesc);
+ inputDesc->clientsMap().emplace(*portId, clientDesc);
ALOGV("getInputForAttr() returns input %d type %d selectedDeviceId %d for port ID %d",
*input, *inputType, *selectedDeviceId, *portId);
@@ -1816,7 +1801,6 @@
return NO_ERROR;
error:
- mInputRoutes.removeRoute(session);
return status;
}
@@ -1824,7 +1808,6 @@
audio_io_handle_t AudioPolicyManager::getInputForDevice(audio_devices_t device,
String8 address,
audio_session_t session,
- uid_t uid,
audio_source_t inputSource,
const audio_config_base_t *config,
audio_input_flags_t flags,
@@ -1886,15 +1869,6 @@
return input;
}
- sp<AudioSession> audioSession = new AudioSession(session,
- inputSource,
- config->format,
- samplingRate,
- config->channel_mask,
- flags,
- uid,
- isSoundTrigger);
-
if (!profile->canOpenNewIo()) {
return AUDIO_IO_HANDLE_NONE;
}
@@ -1930,7 +1904,6 @@
}
inputDesc->mPolicyMix = policyMix;
- inputDesc->addAudioSession(session, audioSession);
addInput(input, inputDesc);
mpClientInterface->onAudioPortListUpdate();
@@ -1946,39 +1919,6 @@
(source == AUDIO_SOURCE_FM_TUNER);
}
-bool AudioPolicyManager::isConcurentCaptureAllowed(const sp<AudioInputDescriptor>& inputDesc,
- const sp<AudioSession>& audioSession)
-{
- // Do not allow capture if an active voice call is using a software patch and
- // the call TX source device is on the same HW module.
- // FIXME: would be better to refine to only inputs whose profile connects to the
- // call TX device but this information is not in the audio patch
- if (mCallTxPatch != 0 &&
- inputDesc->getModuleHandle() == mCallTxPatch->mPatch.sources[0].ext.device.hw_module) {
- return false;
- }
-
- // starting concurrent capture is enabled if:
- // 1) capturing for re-routing
- // 2) capturing for HOTWORD source
- // 3) capturing for FM TUNER source
- // 3) All other active captures are either for re-routing or HOTWORD
-
- if (is_virtual_input_device(inputDesc->mDevice) ||
- isConcurrentSource(audioSession->inputSource())) {
- return true;
- }
-
- for (const auto& activeInput : mInputs.getActiveInputs()) {
- if (!isConcurrentSource(activeInput->inputSource(true)) &&
- !is_virtual_input_device(activeInput->mDevice)) {
- return false;
- }
- }
-
- return true;
-}
-
// FIXME: remove when concurrent capture is ready. This is a hack to work around bug b/63083537.
bool AudioPolicyManager::soundTriggerSupportsConcurrentCapture() {
if (!mHasComputedSoundTriggerSupportsConcurrentCapture) {
@@ -2024,22 +1964,21 @@
sp<AudioInputDescriptor> inputDesc = mInputs.getInputForClient(portId);
if (inputDesc == 0) {
- ALOGW("startInput() no input for client %d", portId);
+ ALOGW("%s no input for client %d", __FUNCTION__, portId);
return BAD_VALUE;
}
- sp<RecordClientDescriptor> client = inputDesc->clients()[portId];
- audio_session_t session = client->session();
audio_io_handle_t input = inputDesc->mIoHandle;
-
- ALOGV("AudioPolicyManager::startInput(input:%d, session:%d, silenced:%d, concurrency:%d)",
- input, session, silenced, *concurrency);
-
- sp<AudioSession> audioSession = inputDesc->getAudioSession(session);
- if (audioSession == 0) {
- ALOGW("startInput() unknown session %d on input %d", session, input);
- return BAD_VALUE;
+ sp<RecordClientDescriptor> client = inputDesc->clientsMap()[portId];
+ if (client->active()) {
+ ALOGW("%s input %d client %d already started", __FUNCTION__, input, client->portId());
+ return INVALID_OPERATION;
}
+ audio_session_t session = client->session();
+
+ ALOGV("%s input:%d, session:%d, silenced:%d, concurrency:%d)",
+ __FUNCTION__, input, session, silenced, *concurrency);
+
if (!is_virtual_input_device(inputDesc->mDevice)) {
if (mCallTxPatch != 0 &&
inputDesc->getModuleHandle() == mCallTxPatch->mPatch.sources[0].ext.device.hw_module) {
@@ -2055,47 +1994,48 @@
// favor of the new one - "There can be only one" TM
if (!silenced) {
for (const auto& activeDesc : activeInputs) {
- if ((audioSession->flags() & AUDIO_INPUT_FLAG_MMAP_NOIRQ) != 0 &&
+ if ((activeDesc->getAudioPort()->getFlags() & AUDIO_INPUT_FLAG_MMAP_NOIRQ) != 0 &&
activeDesc->getId() == inputDesc->getId()) {
continue;
}
- AudioSessionCollection activeSessions = activeDesc->getAudioSessions(
- true /*activeOnly*/);
- sp<AudioSession> activeSession = activeSessions.valueAt(0);
- if (activeSession->isSilenced()) {
- closeSession(activeDesc, activeSession);
- ALOGV("startInput() session %d stopping silenced session %d", session, activeSession->session());
- activeInputs = mInputs.getActiveInputs();
+ RecordClientVector activeClients = activeDesc->clientsList(true /*activeOnly*/);
+ for (const auto& activeClient : activeClients) {
+ if (activeClient->isSilenced()) {
+ closeClient(activeClient->portId());
+ ALOGV("%s client %d stopping silenced client %d", __FUNCTION__,
+ portId, activeClient->portId());
+ activeInputs = mInputs.getActiveInputs();
+ }
}
}
}
for (const auto& activeDesc : activeInputs) {
- if ((audioSession->flags() & AUDIO_INPUT_FLAG_MMAP_NOIRQ) != 0 &&
+ if ((client->flags() & AUDIO_INPUT_FLAG_MMAP_NOIRQ) != 0 &&
activeDesc->getId() == inputDesc->getId()) {
continue;
}
audio_source_t activeSource = activeDesc->inputSource(true);
- if (audioSession->inputSource() == AUDIO_SOURCE_HOTWORD) {
+ if (client->source() == AUDIO_SOURCE_HOTWORD) {
if (activeSource == AUDIO_SOURCE_HOTWORD) {
if (activeDesc->hasPreemptedSession(session)) {
- ALOGW("startInput(%d) failed for HOTWORD: "
- "other input %d already started for HOTWORD",
+ ALOGW("%s input %d failed for HOTWORD: "
+ "other input %d already started for HOTWORD", __FUNCTION__,
input, activeDesc->mIoHandle);
*concurrency |= API_INPUT_CONCURRENCY_HOTWORD;
return INVALID_OPERATION;
}
} else {
- ALOGV("startInput(%d) failed for HOTWORD: other input %d already started",
- input, activeDesc->mIoHandle);
+ ALOGV("%s input %d failed for HOTWORD: other input %d already started",
+ __FUNCTION__, input, activeDesc->mIoHandle);
*concurrency |= API_INPUT_CONCURRENCY_CAPTURE;
return INVALID_OPERATION;
}
} else {
if (activeSource != AUDIO_SOURCE_HOTWORD) {
- ALOGW("startInput(%d) failed: other input %d already started",
+ ALOGW("%s input %d failed: other input %d already started", __FUNCTION__,
input, activeDesc->mIoHandle);
*concurrency |= API_INPUT_CONCURRENCY_CAPTURE;
return INVALID_OPERATION;
@@ -2114,80 +2054,75 @@
if (allowConcurrentWithSoundTrigger && activeDesc->isSoundTrigger()) {
continue;
}
-
- audio_source_t activeSource = activeDesc->inputSource(true);
- if (activeSource == AUDIO_SOURCE_HOTWORD) {
- AudioSessionCollection activeSessions =
- activeDesc->getAudioSessions(true /*activeOnly*/);
- sp<AudioSession> activeSession = activeSessions[0];
+ RecordClientVector activeHotwordClients =
+ activeDesc->clientsList(true, AUDIO_SOURCE_HOTWORD);
+ if (activeHotwordClients.size() > 0) {
SortedVector<audio_session_t> sessions = activeDesc->getPreemptedSessions();
- *concurrency |= API_INPUT_CONCURRENCY_PREEMPT;
- sessions.add(activeSession->session());
+
+ for (const auto& activeClient : activeHotwordClients) {
+ *concurrency |= API_INPUT_CONCURRENCY_PREEMPT;
+ sessions.add(activeClient->session());
+ closeClient(activeClient->portId());
+ ALOGV("%s input %d for HOTWORD preempting HOTWORD input %d", __FUNCTION__,
+ input, activeDesc->mIoHandle);
+ }
+
inputDesc->setPreemptedSessions(sessions);
- closeSession(inputDesc, activeSession);
- ALOGV("startInput(%d) for HOTWORD preempting HOTWORD input %d",
- input, activeDesc->mIoHandle);
}
}
}
// Make sure we start with the correct silence state
- audioSession->setSilenced(silenced);
+ client->setSilenced(silenced);
// increment activity count before calling getNewInputDevice() below as only active sessions
// are considered for device selection
- inputDesc->changeRefCount(session, 1);
+ inputDesc->setClientActive(client, true);
- // Routing?
- mInputRoutes.incRouteActivity(session);
+ // indicate active capture to sound trigger service if starting capture from a mic on
+ // primary HW module
+ audio_devices_t device = getNewInputDevice(inputDesc);
+ setInputDevice(input, device, true /* force */);
- if (audioSession->activeCount() == 1 || mInputRoutes.getAndClearRouteChanged(session)) {
- // indicate active capture to sound trigger service if starting capture from a mic on
- // primary HW module
- audio_devices_t device = getNewInputDevice(inputDesc);
- setInputDevice(input, device, true /* force */);
+ status_t status = inputDesc->start();
+ if (status != NO_ERROR) {
+ inputDesc->setClientActive(client, false);
+ return status;
+ }
- status_t status = inputDesc->start();
- if (status != NO_ERROR) {
- mInputRoutes.decRouteActivity(session);
- inputDesc->changeRefCount(session, -1);
- return status;
+ if (inputDesc->activeCount() == 1) {
+ // if input maps to a dynamic policy with an activity listener, notify of state change
+ if ((inputDesc->mPolicyMix != NULL)
+ && ((inputDesc->mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) {
+ mpClientInterface->onDynamicPolicyMixStateUpdate(inputDesc->mPolicyMix->mDeviceAddress,
+ MIX_STATE_MIXING);
}
- if (inputDesc->getAudioSessionCount(true/*activeOnly*/) == 1) {
- // if input maps to a dynamic policy with an activity listener, notify of state change
- if ((inputDesc->mPolicyMix != NULL)
- && ((inputDesc->mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) {
- mpClientInterface->onDynamicPolicyMixStateUpdate(inputDesc->mPolicyMix->mDeviceAddress,
- MIX_STATE_MIXING);
- }
+ audio_devices_t primaryInputDevices = availablePrimaryInputDevices();
+ if (((device & primaryInputDevices & ~AUDIO_DEVICE_BIT_IN) != 0) &&
+ mInputs.activeInputsCountOnDevices(primaryInputDevices) == 1) {
+ SoundTrigger::setCaptureState(true);
+ }
- audio_devices_t primaryInputDevices = availablePrimaryInputDevices();
- if (((device & primaryInputDevices & ~AUDIO_DEVICE_BIT_IN) != 0) &&
- mInputs.activeInputsCountOnDevices(primaryInputDevices) == 1) {
- SoundTrigger::setCaptureState(true);
+ // automatically enable the remote submix output when input is started if not
+ // used by a policy mix of type MIX_TYPE_RECORDERS
+ // For remote submix (a virtual device), we open only one input per capture request.
+ if (audio_is_remote_submix_device(inputDesc->mDevice)) {
+ String8 address = String8("");
+ if (inputDesc->mPolicyMix == NULL) {
+ address = String8("0");
+ } else if (inputDesc->mPolicyMix->mMixType == MIX_TYPE_PLAYERS) {
+ address = inputDesc->mPolicyMix->mDeviceAddress;
}
-
- // automatically enable the remote submix output when input is started if not
- // used by a policy mix of type MIX_TYPE_RECORDERS
- // For remote submix (a virtual device), we open only one input per capture request.
- if (audio_is_remote_submix_device(inputDesc->mDevice)) {
- String8 address = String8("");
- if (inputDesc->mPolicyMix == NULL) {
- address = String8("0");
- } else if (inputDesc->mPolicyMix->mMixType == MIX_TYPE_PLAYERS) {
- address = inputDesc->mPolicyMix->mDeviceAddress;
- }
- if (address != "") {
- setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
- AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
- address, "remote-submix");
- }
+ if (address != "") {
+ setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
+ AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
+ address, "remote-submix");
}
}
}
- ALOGV("AudioPolicyManager::startInput() input source = %d", audioSession->inputSource());
+ ALOGV("%s input %d source = %d exit", __FUNCTION__, input, client->source());
return NO_ERROR;
}
@@ -2198,67 +2133,56 @@
sp<AudioInputDescriptor> inputDesc = mInputs.getInputForClient(portId);
if (inputDesc == 0) {
- ALOGW("stopInput() no input for client %d", portId);
+ ALOGW("%s no input for client %d", __FUNCTION__, portId);
return BAD_VALUE;
}
- sp<RecordClientDescriptor> client = inputDesc->clients()[portId];
- audio_session_t session = client->session();
audio_io_handle_t input = inputDesc->mIoHandle;
-
- ALOGV("stopInput() input %d", input);
-
- sp<AudioSession> audioSession = inputDesc->getAudioSession(session);
-
- if (audioSession->activeCount() == 0) {
- ALOGW("stopInput() input %d already stopped", input);
+ sp<RecordClientDescriptor> client = inputDesc->clientsMap()[portId];
+ if (!client->active()) {
+ ALOGW("%s input %d client %d already stopped", __FUNCTION__, input, client->portId());
return INVALID_OPERATION;
}
- inputDesc->changeRefCount(session, -1);
+ inputDesc->setClientActive(client, false);
- // Routing?
- mInputRoutes.decRouteActivity(session);
-
- if (audioSession->activeCount() == 0) {
- inputDesc->stop();
- if (inputDesc->isActive()) {
- setInputDevice(input, getNewInputDevice(inputDesc), false /* force */);
- } else {
- // if input maps to a dynamic policy with an activity listener, notify of state change
- if ((inputDesc->mPolicyMix != NULL)
- && ((inputDesc->mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) {
- mpClientInterface->onDynamicPolicyMixStateUpdate(inputDesc->mPolicyMix->mDeviceAddress,
- MIX_STATE_IDLE);
- }
-
- // automatically disable the remote submix output when input is stopped if not
- // used by a policy mix of type MIX_TYPE_RECORDERS
- if (audio_is_remote_submix_device(inputDesc->mDevice)) {
- String8 address = String8("");
- if (inputDesc->mPolicyMix == NULL) {
- address = String8("0");
- } else if (inputDesc->mPolicyMix->mMixType == MIX_TYPE_PLAYERS) {
- address = inputDesc->mPolicyMix->mDeviceAddress;
- }
- if (address != "") {
- setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
- AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
- address, "remote-submix");
- }
- }
-
- audio_devices_t device = inputDesc->mDevice;
- resetInputDevice(input);
-
- // indicate inactive capture to sound trigger service if stopping capture from a mic on
- // primary HW module
- audio_devices_t primaryInputDevices = availablePrimaryInputDevices();
- if (((device & primaryInputDevices & ~AUDIO_DEVICE_BIT_IN) != 0) &&
- mInputs.activeInputsCountOnDevices(primaryInputDevices) == 0) {
- SoundTrigger::setCaptureState(false);
- }
- inputDesc->clearPreemptedSessions();
+ inputDesc->stop();
+ if (inputDesc->isActive()) {
+ setInputDevice(input, getNewInputDevice(inputDesc), false /* force */);
+ } else {
+ // if input maps to a dynamic policy with an activity listener, notify of state change
+ if ((inputDesc->mPolicyMix != NULL)
+ && ((inputDesc->mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) {
+ mpClientInterface->onDynamicPolicyMixStateUpdate(inputDesc->mPolicyMix->mDeviceAddress,
+ MIX_STATE_IDLE);
}
+
+ // automatically disable the remote submix output when input is stopped if not
+ // used by a policy mix of type MIX_TYPE_RECORDERS
+ if (audio_is_remote_submix_device(inputDesc->mDevice)) {
+ String8 address = String8("");
+ if (inputDesc->mPolicyMix == NULL) {
+ address = String8("0");
+ } else if (inputDesc->mPolicyMix->mMixType == MIX_TYPE_PLAYERS) {
+ address = inputDesc->mPolicyMix->mDeviceAddress;
+ }
+ if (address != "") {
+ setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
+ AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
+ address, "remote-submix");
+ }
+ }
+
+ audio_devices_t device = inputDesc->mDevice;
+ resetInputDevice(input);
+
+ // indicate inactive capture to sound trigger service if stopping capture from a mic on
+ // primary HW module
+ audio_devices_t primaryInputDevices = availablePrimaryInputDevices();
+ if (((device & primaryInputDevices & ~AUDIO_DEVICE_BIT_IN) != 0) &&
+ mInputs.activeInputsCountOnDevices(primaryInputDevices) == 0) {
+ SoundTrigger::setCaptureState(false);
+ }
+ inputDesc->clearPreemptedSessions();
}
return NO_ERROR;
}
@@ -2269,64 +2193,40 @@
sp<AudioInputDescriptor> inputDesc = mInputs.getInputForClient(portId);
if (inputDesc == 0) {
- ALOGW("releaseInput() no input for client %d", portId);
+ ALOGW("%s no input for client %d", __FUNCTION__, portId);
return;
}
- sp<RecordClientDescriptor> client = inputDesc->clients()[portId];
- audio_session_t session = client->session();
+ sp<RecordClientDescriptor> client = inputDesc->clientsMap()[portId];
audio_io_handle_t input = inputDesc->mIoHandle;
- ALOGV("releaseInput() %d", input);
+ ALOGV("%s %d", __FUNCTION__, input);
- // Routing
- mInputRoutes.removeRoute(session);
+ inputDesc->clientsMap().erase(portId);
- sp<AudioSession> audioSession = inputDesc->getAudioSession(session);
- if (audioSession == 0) {
- ALOGW("releaseInput() unknown session %d on input %d", session, input);
- return;
- }
-
- if (audioSession->openCount() == 0) {
- ALOGW("releaseInput() invalid open count %d on session %d",
- audioSession->openCount(), session);
- return;
- }
-
- if (audioSession->changeOpenCount(-1) == 0) {
- inputDesc->removeAudioSession(session);
- }
-
- if (inputDesc->getOpenRefCount() > 0) {
- ALOGV("releaseInput() exit > 0");
+ if (inputDesc->clientsMap().size() > 0) {
+ ALOGV("%s %zu clients remaining", __FUNCTION__, inputDesc->clientsMap().size());
return;
}
closeInput(input);
- inputDesc->clients().erase(portId);
mpClientInterface->onAudioPortListUpdate();
- ALOGV("releaseInput() exit");
+ ALOGV("%s exit", __FUNCTION__);
}
-void AudioPolicyManager::closeSessions(const sp<AudioInputDescriptor>& input, bool activeOnly)
+void AudioPolicyManager::closeActiveClients(const sp<AudioInputDescriptor>& input)
{
- AudioSessionCollection sessions = input->getAudioSessions(activeOnly /*activeOnly*/);
- for (size_t i = 0; i < sessions.size(); i++) {
- closeSession(input, sessions[i]);
- }
-}
-
-void AudioPolicyManager::closeSession(const sp<AudioInputDescriptor>& input,
- const sp<AudioSession>& session)
-{
- RecordClientVector clients = input->getClientsForSession(session->session());
+ RecordClientVector clients = input->clientsList(true);
for (const auto& client : clients) {
- stopInput(client->portId());
- releaseInput(client->portId());
+ closeClient(client->portId());
}
}
+void AudioPolicyManager::closeClient(audio_port_handle_t portId)
+{
+ stopInput(portId);
+ releaseInput(portId);
+}
void AudioPolicyManager::closeAllInputs() {
bool patchRemoved = false;
@@ -2342,7 +2242,6 @@
}
inputDesc->close();
}
- mInputRoutes.clear();
mInputs.clear();
SoundTrigger::setCaptureState(false);
nextAudioPortGeneration();
@@ -2619,12 +2518,13 @@
sp<HwModule> rSubmixModule;
// examine each mix's route type
for (size_t i = 0; i < mixes.size(); i++) {
+ AudioMix mix = mixes[i];
// we only support MIX_ROUTE_FLAG_LOOP_BACK or MIX_ROUTE_FLAG_RENDER, not the combination
- if ((mixes[i].mRouteFlags & MIX_ROUTE_FLAG_ALL) == MIX_ROUTE_FLAG_ALL) {
+ if ((mix.mRouteFlags & MIX_ROUTE_FLAG_ALL) == MIX_ROUTE_FLAG_ALL) {
res = INVALID_OPERATION;
break;
}
- if ((mixes[i].mRouteFlags & MIX_ROUTE_FLAG_LOOP_BACK) == MIX_ROUTE_FLAG_LOOP_BACK) {
+ if ((mix.mRouteFlags & MIX_ROUTE_FLAG_LOOP_BACK) == MIX_ROUTE_FLAG_LOOP_BACK) {
ALOGV("registerPolicyMixes() mix %zu of %zu is LOOP_BACK", i, mixes.size());
if (rSubmixModule == 0) {
rSubmixModule = mHwModules.getModuleFromName(
@@ -2637,15 +2537,20 @@
}
}
- String8 address = mixes[i].mDeviceAddress;
+ String8 address = mix.mDeviceAddress;
+ if (mix.mMixType == MIX_TYPE_PLAYERS) {
+ mix.mDeviceType = AUDIO_DEVICE_IN_REMOTE_SUBMIX;
+ } else {
+ mix.mDeviceType = AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
+ }
- if (mPolicyMixes.registerMix(address, mixes[i], 0 /*output desc*/) != NO_ERROR) {
+ if (mPolicyMixes.registerMix(address, mix, 0 /*output desc*/) != NO_ERROR) {
ALOGE(" Error registering mix %zu for address %s", i, address.string());
res = INVALID_OPERATION;
break;
}
- audio_config_t outputConfig = mixes[i].mFormat;
- audio_config_t inputConfig = mixes[i].mFormat;
+ audio_config_t outputConfig = mix.mFormat;
+ audio_config_t inputConfig = mix.mFormat;
// NOTE: audio flinger mixer does not support mono output: configure remote submix HAL in
// stereo and let audio flinger do the channel conversion if needed.
outputConfig.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
@@ -2655,7 +2560,7 @@
rSubmixModule->addInputProfile(address, &inputConfig,
AUDIO_DEVICE_IN_REMOTE_SUBMIX, address);
- if (mixes[i].mMixType == MIX_TYPE_PLAYERS) {
+ if (mix.mMixType == MIX_TYPE_PLAYERS) {
setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX,
AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
address.string(), "remote-submix");
@@ -2664,9 +2569,9 @@
AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
address.string(), "remote-submix");
}
- } else if ((mixes[i].mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) {
- String8 address = mixes[i].mDeviceAddress;
- audio_devices_t device = mixes[i].mDeviceType;
+ } else if ((mix.mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) {
+ String8 address = mix.mDeviceAddress;
+ audio_devices_t device = mix.mDeviceType;
ALOGV(" registerPolicyMixes() mix %zu of %zu is RENDER, dev=0x%X addr=%s",
i, mixes.size(), device, address.string());
@@ -2679,7 +2584,7 @@
&& (patch->mPatch.sinks[0].ext.device.type == device)
&& (strncmp(patch->mPatch.sinks[0].ext.device.address, address.string(),
AUDIO_DEVICE_MAX_ADDRESS_LEN) == 0)) {
- if (mPolicyMixes.registerMix(address, mixes[i], desc) != NO_ERROR) {
+ if (mPolicyMixes.registerMix(address, mix, desc) != NO_ERROR) {
res = INVALID_OPERATION;
} else {
foundOutput = true;
@@ -2811,12 +2716,9 @@
}
// Check if offload has been disabled
- char propValue[PROPERTY_VALUE_MAX];
- if (property_get("audio.offload.disable", propValue, "0")) {
- if (atoi(propValue) != 0) {
- ALOGV("offload disabled by audio.offload.disable=%s", propValue );
- return false;
- }
+ if (property_get_bool("audio.offload.disable", false /* default_value */)) {
+ ALOGV("offload disabled by audio.offload.disable");
+ return false;
}
// Check if stream type is music, then only allow offload as of now.
@@ -2835,9 +2737,12 @@
}
//If duration is less than minimum value defined in property, return false
- if (property_get("audio.offload.min.duration.secs", propValue, NULL)) {
- if (offloadInfo.duration_us < (atoi(propValue) * 1000000 )) {
- ALOGV("Offload denied by duration < audio.offload.min.duration.secs(=%s)", propValue);
+ const int min_duration_secs = property_get_int32(
+ "audio.offload.min.duration.secs", -1 /* default_value */);
+ if (min_duration_secs >= 0) {
+ if (offloadInfo.duration_us < min_duration_secs * 1000000LL) {
+ ALOGV("Offload denied by duration < audio.offload.min.duration.secs(=%d)",
+ min_duration_secs);
return false;
}
} else if (offloadInfo.duration_us < OFFLOAD_DEFAULT_MIN_DURATION_SECS * 1000000) {
@@ -3391,12 +3296,13 @@
{
// remove output routes associated with this uid
SortedVector<routing_strategy> affectedStrategies;
- for (ssize_t i = (ssize_t)mOutputRoutes.size() - 1; i >= 0; i--) {
- sp<SessionRoute> route = mOutputRoutes.valueAt(i);
- if (route->mUid == uid) {
- mOutputRoutes.removeItemsAt(i);
- if (route->mDeviceDescriptor != 0) {
- affectedStrategies.add(getStrategy(route->mStreamType));
+ for (size_t i = 0; i < mOutputs.size(); i++) {
+ sp<AudioOutputDescriptor> outputDesc = mOutputs.valueAt(i);
+ TrackClientMap clients = outputDesc->clientsMap();
+ for (const auto& client : clients) {
+ if (client.second->hasPreferredDevice() && client.second->uid() == uid) {
+ client.second->setPreferredDeviceId(AUDIO_PORT_HANDLE_NONE);
+ affectedStrategies.add(getStrategy(client.second->stream()));
}
}
}
@@ -3407,12 +3313,13 @@
// remove input routes associated with this uid
SortedVector<audio_source_t> affectedSources;
- for (ssize_t i = (ssize_t)mInputRoutes.size() - 1; i >= 0; i--) {
- sp<SessionRoute> route = mInputRoutes.valueAt(i);
- if (route->mUid == uid) {
- mInputRoutes.removeItemsAt(i);
- if (route->mDeviceDescriptor != 0) {
- affectedSources.add(route->mSource);
+ for (size_t i = 0; i < mInputs.size(); i++) {
+ sp<AudioInputDescriptor> inputDesc = mInputs.valueAt(i);
+ RecordClientMap clients = inputDesc->clientsMap();
+ for (const auto& client : clients) {
+ if (client.second->hasPreferredDevice() && client.second->uid() == uid) {
+ client.second->setPreferredDeviceId(AUDIO_PORT_HANDLE_NONE);
+ affectedSources.add(client.second->source());
}
}
}
@@ -3486,7 +3393,8 @@
sp<SourceClientDescriptor> sourceDesc =
new SourceClientDescriptor(*portId, uid, *attributes, patchDesc, srcDeviceDesc,
- streamTypefromAttributesInt(attributes));
+ streamTypefromAttributesInt(attributes),
+ getStrategyForAttr(attributes));
status_t status = connectAudioSource(sourceDesc);
if (status == NO_ERROR) {
@@ -3503,7 +3411,7 @@
disconnectAudioSource(sourceDesc);
audio_attributes_t attributes = sourceDesc->attributes();
- routing_strategy strategy = (routing_strategy) getStrategyForAttr(&attributes);
+ routing_strategy strategy = getStrategyForAttr(&attributes);
audio_stream_type_t stream = sourceDesc->stream();
sp<DeviceDescriptor> srcDeviceDesc = sourceDesc->srcDevice();
@@ -3557,7 +3465,7 @@
return INVALID_OPERATION;
}
uint32_t delayMs = 0;
- status = startSource(outputDesc, stream, sinkDevice, NULL, &delayMs);
+ status = startSource(outputDesc, sourceDesc, &delayMs);
if (status != NO_ERROR) {
mpClientInterface->releaseAudioPatch(sourceDesc->patchDesc()->mAfPatchHandle, 0);
@@ -3909,11 +3817,10 @@
Vector<sp<AudioInputDescriptor> > activeInputs = mInputs.getActiveInputs();
for (size_t i = 0; i < activeInputs.size(); i++) {
sp<AudioInputDescriptor> activeDesc = activeInputs[i];
- AudioSessionCollection activeSessions = activeDesc->getAudioSessions(true);
- for (size_t j = 0; j < activeSessions.size(); j++) {
- sp<AudioSession> activeSession = activeSessions.valueAt(j);
- if (activeSession->uid() == uid) {
- activeSession->setSilenced(silenced);
+ RecordClientVector clients = activeDesc->clientsList(true /*activeOnly*/);
+ for (const auto& client : clients) {
+ if (uid == client->uid()) {
+ client->setSilenced(silenced);
}
}
}
@@ -3931,10 +3838,9 @@
}
removeAudioPatch(sourceDesc->patchDesc()->mHandle);
- audio_stream_type_t stream = sourceDesc->stream();
sp<SwAudioOutputDescriptor> swOutputDesc = sourceDesc->swOutput().promote();
if (swOutputDesc != 0) {
- status_t status = stopSource(swOutputDesc, stream, false);
+ status_t status = stopSource(swOutputDesc, sourceDesc);
if (status == NO_ERROR) {
swOutputDesc->stop();
}
@@ -3958,8 +3864,7 @@
for (size_t i = 0; i < mAudioSources.size(); i++) {
sp<SourceClientDescriptor> sourceDesc = mAudioSources.valueAt(i);
audio_attributes_t attributes = sourceDesc->attributes();
- routing_strategy sourceStrategy =
- (routing_strategy) getStrategyForAttr(&attributes);
+ routing_strategy sourceStrategy = getStrategyForAttr(&attributes);
sp<SwAudioOutputDescriptor> outputDesc = sourceDesc->swOutput().promote();
if (sourceStrategy == strategy && outputDesc != 0 && outputDesc->mIoHandle == output) {
source = sourceDesc;
@@ -4712,8 +4617,8 @@
// the other output.
bool wasActive = outputDesc2->isActive();
for (int j = 0; j < AUDIO_STREAM_CNT; j++) {
- int refCount = dupOutputDesc->mRefCount[j];
- outputDesc2->changeRefCount((audio_stream_type_t)j,-refCount);
+ int activeCount = dupOutputDesc->streamActiveCount((audio_stream_type_t)j);
+ outputDesc2->changeStreamActiveCount((audio_stream_type_t)j,-activeCount);
}
// stop() will be a no op if the output is still active but is needed in case all
// active streams refcounts where cleared above
@@ -4936,11 +4841,41 @@
}
}
+template <class IoDescriptor, class Filter>
+sp<DeviceDescriptor> AudioPolicyManager::findPreferredDevice(
+ IoDescriptor& desc, Filter filter, bool& active, const DeviceVector& devices)
+{
+ auto activeClients = desc->clientsList(true /*activeOnly*/);
+ auto activeClientsWithRoute =
+ desc->clientsList(true /*activeOnly*/, filter, true /*preferredDevice*/);
+ active = activeClients.size() > 0;
+ if (active && activeClients.size() == activeClientsWithRoute.size()) {
+ return devices.getDeviceFromId(activeClientsWithRoute[0]->preferredDeviceId());
+ }
+ return nullptr;
+}
+
+template <class IoCollection, class Filter>
+sp<DeviceDescriptor> AudioPolicyManager::findPreferredDevice(
+ IoCollection& ioCollection, Filter filter, const DeviceVector& devices)
+{
+ sp<DeviceDescriptor> device;
+ for (size_t i = 0; i < ioCollection.size(); i++) {
+ auto desc = ioCollection.valueAt(i);
+ bool active;
+ sp<DeviceDescriptor> curDevice = findPreferredDevice(desc, filter, active, devices);
+ if (active && curDevice == nullptr) {
+ return nullptr;
+ } else if (curDevice != nullptr) {
+ device = curDevice;
+ }
+ }
+ return device;
+}
+
audio_devices_t AudioPolicyManager::getNewOutputDevice(const sp<AudioOutputDescriptor>& outputDesc,
bool fromCache)
{
- audio_devices_t device = AUDIO_DEVICE_NONE;
-
ssize_t index = mAudioPatches.indexOfKey(outputDesc->getPatchHandle());
if (index >= 0) {
sp<AudioPatch> patchDesc = mAudioPatches.valueAt(index);
@@ -4951,18 +4886,13 @@
}
}
- // Check if an explicit routing request exists for an active stream on this output and
- // use it in priority before any other rule
- for (int stream = 0; stream < AUDIO_STREAM_FOR_POLICY_CNT; stream++) {
- if (outputDesc->isStreamActive((audio_stream_type_t)stream)) {
- audio_devices_t forcedDevice =
- mOutputRoutes.getActiveDeviceForStream(
- (audio_stream_type_t)stream, mAvailableOutputDevices);
-
- if (forcedDevice != AUDIO_DEVICE_NONE) {
- return forcedDevice;
- }
- }
+ // Honor explicit routing requests only if no client using default routing is active on this
+ // input: a specific app can not force routing for other apps by setting a preferred device.
+ bool active; // unused
+ sp<DeviceDescriptor> deviceDesc =
+ findPreferredDevice(outputDesc, STRATEGY_NONE, active, mAvailableOutputDevices);
+ if (deviceDesc != nullptr) {
+ return deviceDesc->type();
}
// check the following by order of priority to request a routing change if necessary:
@@ -4988,6 +4918,7 @@
// FIXME: extend use of isStrategyActiveOnSameModule() to all strategies
// with a refined rule considering mutually exclusive devices (using same backend)
// as opposed to all streams on the same audio HAL module.
+ audio_devices_t device = AUDIO_DEVICE_NONE;
if (isStrategyActive(outputDesc, STRATEGY_ENFORCED_AUDIBLE) &&
mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) {
device = getDeviceForStrategy(STRATEGY_ENFORCED_AUDIBLE, fromCache);
@@ -5030,6 +4961,15 @@
}
}
+ // Honor explicit routing requests only if no client using default routing is active on this
+ // input: a specific app can not force routing for other apps by setting a preferred device.
+ bool active;
+ sp<DeviceDescriptor> deviceDesc =
+ findPreferredDevice(inputDesc, AUDIO_SOURCE_DEFAULT, active, mAvailableInputDevices);
+ if (deviceDesc != nullptr) {
+ return deviceDesc->type();
+ }
+
// If we are not in call and no client is active on this input, this methods returns
// AUDIO_DEVICE_NONE, causing the patch on the input stream to be released.
audio_source_t source = inputDesc->getHighestPrioritySource(true /*activeOnly*/);
@@ -5059,6 +4999,7 @@
if (stream < (audio_stream_type_t) 0 || stream >= AUDIO_STREAM_PUBLIC_CNT) {
return AUDIO_DEVICE_NONE;
}
+ audio_devices_t activeDevices = AUDIO_DEVICE_NONE;
audio_devices_t devices = AUDIO_DEVICE_NONE;
for (int curStream = 0; curStream < AUDIO_STREAM_FOR_POLICY_CNT; curStream++) {
if (!streamsMatchForvolume(stream, (audio_stream_type_t)curStream)) {
@@ -5067,15 +5008,20 @@
routing_strategy curStrategy = getStrategy((audio_stream_type_t)curStream);
audio_devices_t curDevices =
getDeviceForStrategy((routing_strategy)curStrategy, false /*fromCache*/);
+ devices |= curDevices;
for (audio_io_handle_t output : getOutputsForDevice(curDevices, mOutputs)) {
sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
if (outputDesc->isStreamActive((audio_stream_type_t)curStream)) {
- curDevices |= outputDesc->device();
+ activeDevices |= outputDesc->device();
}
}
- devices |= curDevices;
}
+ // Favor devices selected on active streams if any to report correct device in case of
+ // explicit device selection
+ if (activeDevices != AUDIO_DEVICE_NONE) {
+ devices = activeDevices;
+ }
/*Filter SPEAKER_SAFE out of results, as AudioService doesn't know about it
and doesn't really need to.*/
if (devices & AUDIO_DEVICE_OUT_SPEAKER_SAFE) {
@@ -5091,16 +5037,16 @@
return mEngine->getStrategyForStream(stream);
}
-uint32_t AudioPolicyManager::getStrategyForAttr(const audio_attributes_t *attr) {
+routing_strategy AudioPolicyManager::getStrategyForAttr(const audio_attributes_t *attr) {
// flags to strategy mapping
if ((attr->flags & AUDIO_FLAG_BEACON) == AUDIO_FLAG_BEACON) {
- return (uint32_t) STRATEGY_TRANSMITTED_THROUGH_SPEAKER;
+ return STRATEGY_TRANSMITTED_THROUGH_SPEAKER;
}
if ((attr->flags & AUDIO_FLAG_AUDIBILITY_ENFORCED) == AUDIO_FLAG_AUDIBILITY_ENFORCED) {
- return (uint32_t) STRATEGY_ENFORCED_AUDIBLE;
+ return STRATEGY_ENFORCED_AUDIBLE;
}
// usage to strategy mapping
- return static_cast<uint32_t>(mEngine->getStrategyForUsage(attr->usage));
+ return mEngine->getStrategyForUsage(attr->usage);
}
void AudioPolicyManager::handleNotificationRoutingForStream(audio_stream_type_t stream) {
@@ -5176,17 +5122,11 @@
audio_devices_t AudioPolicyManager::getDeviceForStrategy(routing_strategy strategy,
bool fromCache)
{
- // Check if an explicit routing request exists for a stream type corresponding to the
- // specified strategy and use it in priority over default routing rules.
- for (int stream = 0; stream < AUDIO_STREAM_FOR_POLICY_CNT; stream++) {
- if (getStrategy((audio_stream_type_t)stream) == strategy) {
- audio_devices_t forcedDevice =
- mOutputRoutes.getActiveDeviceForStream(
- (audio_stream_type_t)stream, mAvailableOutputDevices);
- if (forcedDevice != AUDIO_DEVICE_NONE) {
- return forcedDevice;
- }
- }
+ // Honor explicit routing requests only if all active clients have a preferred route in which
+ // case the last active client route is used
+ sp<DeviceDescriptor> deviceDesc = findPreferredDevice(mOutputs, strategy, mAvailableOutputDevices);
+ if (deviceDesc != nullptr) {
+ return deviceDesc->type();
}
if (fromCache) {
@@ -5530,6 +5470,15 @@
audio_devices_t AudioPolicyManager::getDeviceAndMixForInputSource(audio_source_t inputSource,
AudioMix **policyMix)
{
+ // Honor explicit routing requests only if all active clients have a preferred route in which
+ // case the last active client route is used
+ sp<DeviceDescriptor> deviceDesc =
+ findPreferredDevice(mInputs, inputSource, mAvailableInputDevices);
+ if (deviceDesc != nullptr) {
+ return deviceDesc->type();
+ }
+
+
audio_devices_t availableDeviceTypes = mAvailableInputDevices.types() & ~AUDIO_DEVICE_BIT_IN;
audio_devices_t selectedDeviceFromMix =
mPolicyMixes.getDeviceAndMixForInputSource(inputSource, availableDeviceTypes, policyMix);
@@ -5542,20 +5491,7 @@
audio_devices_t AudioPolicyManager::getDeviceForInputSource(audio_source_t inputSource)
{
- // Routing
- // Scan the whole RouteMap to see if we have an explicit route:
- // if the input source in the RouteMap is the same as the argument above,
- // and activity count is non-zero and the device in the route descriptor is available
- // then select this device.
- for (size_t routeIndex = 0; routeIndex < mInputRoutes.size(); routeIndex++) {
- sp<SessionRoute> route = mInputRoutes.valueAt(routeIndex);
- if ((inputSource == route->mSource) && route->isActiveOrChanged() &&
- (mAvailableInputDevices.indexOf(route->mDeviceDescriptor) >= 0)) {
- return route->mDeviceDescriptor->type();
- }
- }
-
- return mEngine->getDeviceForInputSource(inputSource);
+ return mEngine->getDeviceForInputSource(inputSource);
}
float AudioPolicyManager::computeVolume(audio_stream_type_t stream,
@@ -5860,7 +5796,7 @@
}
for (int i = 0; i < (int)AUDIO_STREAM_FOR_POLICY_CNT; i++) {
if (((getStrategy((audio_stream_type_t)i) == strategy) ||
- (NUM_STRATEGIES == strategy)) &&
+ (STRATEGY_NONE == strategy)) &&
outputDesc->isStreamActive((audio_stream_type_t)i, inPastMs, sysTime)) {
return true;
}
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 9436767..6f4cce1 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -49,7 +49,6 @@
#include <AudioPolicyMix.h>
#include <EffectDescriptor.h>
#include <SoundTriggerSession.h>
-#include <SessionRoute.h>
#include <VolumeCurve.h>
namespace android {
@@ -156,7 +155,7 @@
// return the strategy corresponding to a given stream type
virtual uint32_t getStrategyForStream(audio_stream_type_t stream);
// return the strategy corresponding to the given audio attributes
- virtual uint32_t getStrategyForAttr(const audio_attributes_t *attr);
+ virtual routing_strategy getStrategyForAttr(const audio_attributes_t *attr);
// return the enabled output devices for the given stream type
virtual audio_devices_t getDevicesForStream(audio_stream_type_t stream);
@@ -366,7 +365,7 @@
bool on,
const sp<AudioOutputDescriptor>& outputDesc,
int delayMs = 0,
- audio_devices_t device = (audio_devices_t)0);
+ audio_devices_t device = AUDIO_DEVICE_NONE);
// Mute or unmute the stream on the specified output
void setStreamMute(audio_stream_type_t stream,
@@ -422,6 +421,14 @@
// manages A2DP output suspend/restore according to phone state and BT SCO usage
void checkA2dpSuspend();
+ template <class IoDescriptor, class Filter>
+ sp<DeviceDescriptor> findPreferredDevice(IoDescriptor& desc, Filter filter,
+ bool& active, const DeviceVector& devices);
+
+ template <class IoCollection, class Filter>
+ sp<DeviceDescriptor> findPreferredDevice(IoCollection& ioCollection, Filter filter,
+ const DeviceVector& devices);
+
// selects the most appropriate device on output for current state
// must be called every time a condition that affects the device choice for a given output is
// changed: connected device, phone state, force use, output start, output stop..
@@ -508,16 +515,11 @@
sp<DeviceDescriptor> findDevice(
const DeviceVector& devices, audio_devices_t device) const;
- // if argument "device" is different from AUDIO_DEVICE_NONE, startSource() will force
- // the re-evaluation of the output device.
- status_t startSource(const sp<AudioOutputDescriptor>& outputDesc,
- audio_stream_type_t stream,
- audio_devices_t device,
- const char *address,
+ status_t startSource(const sp<SwAudioOutputDescriptor>& outputDesc,
+ const sp<TrackClientDescriptor>& client,
uint32_t *delayMs);
- status_t stopSource(const sp<AudioOutputDescriptor>& outputDesc,
- audio_stream_type_t stream,
- bool forceDeviceUpdate);
+ status_t stopSource(const sp<SwAudioOutputDescriptor>& outputDesc,
+ const sp<TrackClientDescriptor>& client);
void clearAudioPatches(uid_t uid);
void clearSessionRoutes(uid_t uid);
@@ -537,15 +539,11 @@
static bool isConcurrentSource(audio_source_t source);
- bool isConcurentCaptureAllowed(const sp<AudioInputDescriptor>& inputDesc,
- const sp<AudioSession>& audioSession);
-
static bool streamsMatchForvolume(audio_stream_type_t stream1,
audio_stream_type_t stream2);
- void closeSessions(const sp<AudioInputDescriptor>& input, bool activeOnly);
- void closeSession(const sp<AudioInputDescriptor>& input,
- const sp<AudioSession>& session);
+ void closeActiveClients(const sp<AudioInputDescriptor>& input);
+ void closeClient(audio_port_handle_t portId);
const uid_t mUidCached; // AID_AUDIOSERVER
AudioPolicyClientInterface *mpClientInterface; // audio policy client interface
@@ -561,9 +559,6 @@
DeviceVector mAvailableOutputDevices; // all available output devices
DeviceVector mAvailableInputDevices; // all available input devices
- SessionRouteMap mOutputRoutes = SessionRouteMap(SessionRouteMap::MAPTYPE_OUTPUT);
- SessionRouteMap mInputRoutes = SessionRouteMap(SessionRouteMap::MAPTYPE_INPUT);
-
bool mLimitRingtoneVolume; // limit ringtone volume to music volume if headset connected
audio_devices_t mDeviceForStrategy[NUM_STRATEGIES];
float mLastVoiceVolume; // last voice volume value sent to audio HAL
@@ -668,7 +663,6 @@
audio_io_handle_t getInputForDevice(audio_devices_t device,
String8 address,
audio_session_t session,
- uid_t uid,
audio_source_t inputSource,
const audio_config_base_t *config,
audio_input_flags_t flags,
diff --git a/services/audiopolicy/service/AudioPolicyEffects.cpp b/services/audiopolicy/service/AudioPolicyEffects.cpp
index b3d564a..29b0561 100644
--- a/services/audiopolicy/service/AudioPolicyEffects.cpp
+++ b/services/audiopolicy/service/AudioPolicyEffects.cpp
@@ -656,12 +656,13 @@
len = strnlen(node->value, EFFECT_STRING_LEN_MAX);
if (*curSize + len + 1 > *totSize) {
*totSize = *curSize + len + 1;
- *param = (char *)realloc(*param, *totSize);
- if (*param == NULL) {
+ char *newParam = (char *)realloc(*param, *totSize);
+ if (newParam == NULL) {
len = 0;
ALOGE("%s realloc error for string len %zu", __func__, *totSize);
goto exit;
}
+ *param = newParam;
}
strncpy(*param + *curSize, node->value, len);
*curSize += len;
diff --git a/services/camera/libcameraservice/CameraFlashlight.cpp b/services/camera/libcameraservice/CameraFlashlight.cpp
index 471c77d..e629cdd 100644
--- a/services/camera/libcameraservice/CameraFlashlight.cpp
+++ b/services/camera/libcameraservice/CameraFlashlight.cpp
@@ -125,7 +125,7 @@
status_t res;
std::vector<String8> cameraIds;
- std::vector<std::string> ids = mProviderManager->getAPI1CompatibleCameraDeviceIds();
+ std::vector<std::string> ids = mProviderManager->getCameraDeviceIds();
int numberOfCameras = static_cast<int>(ids.size());
cameraIds.resize(numberOfCameras);
// No module, must be provider
@@ -217,7 +217,7 @@
if (mOpenedCameraIds.size() == 0) {
// notify torch unavailable for all cameras with a flash
- std::vector<std::string> ids = mProviderManager->getAPI1CompatibleCameraDeviceIds();
+ std::vector<std::string> ids = mProviderManager->getCameraDeviceIds();
int numCameras = static_cast<int>(ids.size());
for (int i = 0; i < numCameras; i++) {
String8 id8(ids[i].c_str());
@@ -263,7 +263,7 @@
if (isBackwardCompatibleMode(cameraId)) {
// notify torch available for all cameras with a flash
- std::vector<std::string> ids = mProviderManager->getAPI1CompatibleCameraDeviceIds();
+ std::vector<std::string> ids = mProviderManager->getCameraDeviceIds();
int numCameras = static_cast<int>(ids.size());
for (int i = 0; i < numCameras; i++) {
String8 id8(ids[i].c_str());
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 7656407..cbcc85b 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -2742,13 +2742,14 @@
status_t Camera3Device::registerInFlight(uint32_t frameNumber,
int32_t numBuffers, CaptureResultExtras resultExtras, bool hasInput,
bool hasAppCallback, nsecs_t maxExpectedDuration,
- std::set<String8>& physicalCameraIds, bool isStillCapture) {
+ std::set<String8>& physicalCameraIds, bool isStillCapture,
+ bool isZslCapture) {
ATRACE_CALL();
Mutex::Autolock l(mInFlightLock);
ssize_t res;
res = mInFlightMap.add(frameNumber, InFlightRequest(numBuffers, resultExtras, hasInput,
- hasAppCallback, maxExpectedDuration, physicalCameraIds, isStillCapture));
+ hasAppCallback, maxExpectedDuration, physicalCameraIds, isStillCapture, isZslCapture));
if (res < 0) return res;
if (mInFlightMap.size() == 1) {
@@ -2766,11 +2767,12 @@
void Camera3Device::returnOutputBuffers(
const camera3_stream_buffer_t *outputBuffers, size_t numBuffers,
- nsecs_t timestamp) {
+ nsecs_t timestamp, bool timestampIncreasing) {
+
for (size_t i = 0; i < numBuffers; i++)
{
Camera3Stream *stream = Camera3Stream::cast(outputBuffers[i].stream);
- status_t res = stream->returnBuffer(outputBuffers[i], timestamp);
+ status_t res = stream->returnBuffer(outputBuffers[i], timestamp, timestampIncreasing);
// Note: stream may be deallocated at this point, if this buffer was
// the last reference to it.
if (res != OK) {
@@ -3214,8 +3216,9 @@
request.pendingOutputBuffers.appendArray(result->output_buffers,
result->num_output_buffers);
} else {
+ bool timestampIncreasing = !(request.zslCapture || request.hasInputBuffer);
returnOutputBuffers(result->output_buffers,
- result->num_output_buffers, shutterTimestamp);
+ result->num_output_buffers, shutterTimestamp, timestampIncreasing);
}
if (result->result != NULL && !isPartialResult) {
@@ -3421,8 +3424,9 @@
r.collectedPartialResult, msg.frame_number,
r.hasInputBuffer, r.physicalMetadatas);
}
+ bool timestampIncreasing = !(r.zslCapture || r.hasInputBuffer);
returnOutputBuffers(r.pendingOutputBuffers.array(),
- r.pendingOutputBuffers.size(), r.shutterTimestamp);
+ r.pendingOutputBuffers.size(), r.shutterTimestamp, timestampIncreasing);
r.pendingOutputBuffers.clear();
removeInFlightRequestIfReadyLocked(idx);
@@ -4948,6 +4952,7 @@
hasCallback = false;
}
bool isStillCapture = false;
+ bool isZslCapture = false;
if (!mNextRequests[0].captureRequest->mSettingsList.begin()->metadata.isEmpty()) {
camera_metadata_ro_entry_t e = camera_metadata_ro_entry_t();
find_camera_metadata_ro_entry(halRequest->settings, ANDROID_CONTROL_CAPTURE_INTENT, &e);
@@ -4955,13 +4960,18 @@
isStillCapture = true;
ATRACE_ASYNC_BEGIN("still capture", mNextRequests[i].halRequest.frame_number);
}
+
+ find_camera_metadata_ro_entry(halRequest->settings, ANDROID_CONTROL_ENABLE_ZSL, &e);
+ if ((e.count > 0) && (e.data.u8[0] == ANDROID_CONTROL_ENABLE_ZSL_TRUE)) {
+ isZslCapture = true;
+ }
}
res = parent->registerInFlight(halRequest->frame_number,
totalNumBuffers, captureRequest->mResultExtras,
/*hasInput*/halRequest->input_buffer != NULL,
hasCallback,
calculateMaxExpectedDuration(halRequest->settings),
- requestedPhysicalCameras, isStillCapture);
+ requestedPhysicalCameras, isStillCapture, isZslCapture);
ALOGVV("%s: registered in flight requestId = %" PRId32 ", frameNumber = %" PRId64
", burstId = %" PRId32 ".",
__FUNCTION__,
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 85f9614..159f2ca 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -999,6 +999,9 @@
// Indicates a still capture request.
bool stillCapture;
+ // Indicates a ZSL capture request
+ bool zslCapture;
+
// Default constructor needed by KeyedVector
InFlightRequest() :
shutterTimestamp(0),
@@ -1010,12 +1013,14 @@
hasCallback(true),
maxExpectedDuration(kDefaultExpectedDuration),
skipResultMetadata(false),
- stillCapture(false) {
+ stillCapture(false),
+ zslCapture(false) {
}
InFlightRequest(int numBuffers, CaptureResultExtras extras, bool hasInput,
bool hasAppCallback, nsecs_t maxDuration,
- const std::set<String8>& physicalCameraIdSet, bool isStillCapture) :
+ const std::set<String8>& physicalCameraIdSet, bool isStillCapture,
+ bool isZslCapture) :
shutterTimestamp(0),
sensorTimestamp(0),
requestStatus(OK),
@@ -1027,7 +1032,8 @@
maxExpectedDuration(maxDuration),
skipResultMetadata(false),
physicalCameraIds(physicalCameraIdSet),
- stillCapture(isStillCapture) {
+ stillCapture(isStillCapture),
+ zslCapture(isZslCapture) {
}
};
@@ -1044,7 +1050,7 @@
status_t registerInFlight(uint32_t frameNumber,
int32_t numBuffers, CaptureResultExtras resultExtras, bool hasInput,
bool callback, nsecs_t maxExpectedDuration, std::set<String8>& physicalCameraIds,
- bool isStillCapture);
+ bool isStillCapture, bool isZslCapture);
/**
* Returns the maximum expected time it'll take for all currently in-flight
@@ -1154,7 +1160,7 @@
// helper function to return the output buffers to the streams.
void returnOutputBuffers(const camera3_stream_buffer_t *outputBuffers,
- size_t numBuffers, nsecs_t timestamp);
+ size_t numBuffers, nsecs_t timestamp, bool timestampIncreasing = true);
// Send a partial capture result.
void sendPartialCaptureResult(const camera_metadata_t * partialResult,
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index 2c020a2..ecbcf76 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -237,12 +237,14 @@
/**
* Return buffer back to ANativeWindow
*/
- if (buffer.status == CAMERA3_BUFFER_STATUS_ERROR || mDropBuffers) {
+ if (buffer.status == CAMERA3_BUFFER_STATUS_ERROR || mDropBuffers || timestamp == 0) {
// Cancel buffer
if (mDropBuffers) {
ALOGV("%s: Dropping a frame for stream %d.", __FUNCTION__, mId);
- } else {
+ } else if (buffer.status == CAMERA3_BUFFER_STATUS_ERROR) {
ALOGW("%s: A frame is dropped for stream %d due to buffer error.", __FUNCTION__, mId);
+ } else {
+ ALOGE("%s: Stream %d: timestamp shouldn't be 0", __FUNCTION__, mId);
}
res = currentConsumer->cancelBuffer(currentConsumer.get(),
diff --git a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
index 2bb9ff7..fb3ce4c 100644
--- a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
@@ -60,9 +60,8 @@
}
}
- android::PixelFormat format = isFormatOverridden() ? getOriginalFormat() : getFormat();
res = mStreamSplitter->connect(initialSurfaces, usage, mUsage, camera3_stream::max_buffers,
- getWidth(), getHeight(), format, &mConsumer);
+ getWidth(), getHeight(), getFormat(), &mConsumer);
if (res != OK) {
ALOGE("%s: Failed to connect to stream splitter: %s(%d)",
__FUNCTION__, strerror(-res), res);
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index 6030d15..9238590 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -66,7 +66,8 @@
mBufferLimitLatency(kBufferLimitLatencyBinSize),
mFormatOverridden(false),
mOriginalFormat(-1),
- mPhysicalCameraId(physicalCameraId) {
+ mPhysicalCameraId(physicalCameraId),
+ mLastTimestamp(0) {
camera3_stream::stream_type = type;
camera3_stream::width = width;
@@ -649,7 +650,7 @@
}
status_t Camera3Stream::returnBuffer(const camera3_stream_buffer &buffer,
- nsecs_t timestamp) {
+ nsecs_t timestamp, bool timestampIncreasing) {
ATRACE_CALL();
Mutex::Autolock l(mLock);
@@ -661,6 +662,15 @@
removeOutstandingBuffer(buffer);
+ // Buffer status may be changed, so make a copy of the stream_buffer struct.
+ camera3_stream_buffer b = buffer;
+ if (timestampIncreasing && timestamp != 0 && timestamp <= mLastTimestamp) {
+ ALOGE("%s: Stream %d: timestamp %" PRId64 " is not increasing. Prev timestamp %" PRId64,
+ __FUNCTION__, mId, timestamp, mLastTimestamp);
+ b.status = CAMERA3_BUFFER_STATUS_ERROR;
+ }
+ mLastTimestamp = timestamp;
+
/**
* TODO: Check that the state is valid first.
*
@@ -669,9 +679,9 @@
*
* Do this for getBuffer as well.
*/
- status_t res = returnBufferLocked(buffer, timestamp);
+ status_t res = returnBufferLocked(b, timestamp);
if (res == OK) {
- fireBufferListenersLocked(buffer, /*acquired*/false, /*output*/true);
+ fireBufferListenersLocked(b, /*acquired*/false, /*output*/true);
}
// Even if returning the buffer failed, we still want to signal whoever is waiting for the
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index 4ddcf1a..e30fc59 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -320,7 +320,7 @@
* For bidirectional streams, this method applies to the output-side buffers
*/
status_t returnBuffer(const camera3_stream_buffer &buffer,
- nsecs_t timestamp);
+ nsecs_t timestamp, bool timestampIncreasing);
/**
* Fill in the camera3_stream_buffer with the next valid buffer for this
@@ -559,6 +559,7 @@
android_dataspace mOriginalDataSpace;
String8 mPhysicalCameraId;
+ nsecs_t mLastTimestamp;
}; // class Camera3Stream
}; // namespace camera3
diff --git a/services/camera/libcameraservice/device3/Camera3StreamInterface.h b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
index 9ed7184..bddd2e7 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
@@ -246,7 +246,7 @@
* For bidirectional streams, this method applies to the output-side buffers
*/
virtual status_t returnBuffer(const camera3_stream_buffer &buffer,
- nsecs_t timestamp) = 0;
+ nsecs_t timestamp, bool timestampIncreasing = true) = 0;
/**
* Fill in the camera3_stream_buffer with the next valid buffer for this
diff --git a/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp b/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
index 8a9402e..8bc208d 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
+++ b/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
@@ -182,12 +182,19 @@
return BAD_VALUE;
}
- status_t res = native_window_set_buffers_dimensions(outputQueue.get(),
+ status_t res = native_window_set_buffers_dimensions(outputQueue.get(),
mWidth, mHeight);
if (res != NO_ERROR) {
SP_LOGE("addOutput: failed to set buffer dimensions (%d)", res);
return res;
}
+ res = native_window_set_buffers_format(outputQueue.get(),
+ mFormat);
+ if (res != OK) {
+ ALOGE("%s: Unable to configure stream buffer format %#x for surfaceId %zu",
+ __FUNCTION__, mFormat, surfaceId);
+ return res;
+ }
sp<IGraphicBufferProducer> gbp = outputQueue->getIGraphicBufferProducer();
// Connect to the buffer producer
diff --git a/services/mediaanalytics/MediaAnalyticsService.cpp b/services/mediaanalytics/MediaAnalyticsService.cpp
index 4b05395..45da56a 100644
--- a/services/mediaanalytics/MediaAnalyticsService.cpp
+++ b/services/mediaanalytics/MediaAnalyticsService.cpp
@@ -483,6 +483,7 @@
{
"audiopolicy",
"audiorecord",
+ "audiothread",
"audiotrack",
"codec",
"extractor",
diff --git a/services/mediacodec/seccomp_policy/mediacodec-x86.policy b/services/mediacodec/seccomp_policy/mediacodec-x86.policy
index 4031b11..966e214 100644
--- a/services/mediacodec/seccomp_policy/mediacodec-x86.policy
+++ b/services/mediacodec/seccomp_policy/mediacodec-x86.policy
@@ -49,6 +49,8 @@
set_tid_address: 1
write: 1
nanosleep: 1
+sched_setscheduler: 1
+uname: 1
# Required by AddressSanitizer
gettid: 1
diff --git a/services/oboeservice/AAudioEndpointManager.cpp b/services/oboeservice/AAudioEndpointManager.cpp
index 04fee13..b1854bf 100644
--- a/services/oboeservice/AAudioEndpointManager.cpp
+++ b/services/oboeservice/AAudioEndpointManager.cpp
@@ -140,9 +140,8 @@
}
sp<AAudioServiceEndpoint> AAudioEndpointManager::openEndpoint(AAudioService &audioService,
- const aaudio::AAudioStreamRequest &request,
- aaudio_sharing_mode_t sharingMode) {
- if (sharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE) {
+ const aaudio::AAudioStreamRequest &request) {
+ if (request.getConstantConfiguration().getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE) {
return openExclusiveEndpoint(audioService, request);
} else {
return openSharedEndpoint(audioService, request);
diff --git a/services/oboeservice/AAudioEndpointManager.h b/services/oboeservice/AAudioEndpointManager.h
index 193bdee..ba17853 100644
--- a/services/oboeservice/AAudioEndpointManager.h
+++ b/services/oboeservice/AAudioEndpointManager.h
@@ -56,8 +56,7 @@
* @return endpoint or null
*/
android::sp<AAudioServiceEndpoint> openEndpoint(android::AAudioService &audioService,
- const aaudio::AAudioStreamRequest &request,
- aaudio_sharing_mode_t sharingMode);
+ const aaudio::AAudioStreamRequest &request);
void closeEndpoint(android::sp<AAudioServiceEndpoint> serviceEndpoint);
diff --git a/services/oboeservice/AAudioService.cpp b/services/oboeservice/AAudioService.cpp
index 94440b1..53ee145 100644
--- a/services/oboeservice/AAudioService.cpp
+++ b/services/oboeservice/AAudioService.cpp
@@ -118,11 +118,16 @@
}
}
- // if SHARED requested or if EXCLUSIVE failed
- if (sharingMode == AAUDIO_SHARING_MODE_SHARED
- || (serviceStream.get() == nullptr && !sharingModeMatchRequired)) {
+ // If SHARED requested or if EXCLUSIVE failed.
+ if (sharingMode == AAUDIO_SHARING_MODE_SHARED) {
serviceStream = new AAudioServiceStreamShared(*this);
result = serviceStream->open(request);
+ } else if (serviceStream.get() == nullptr && !sharingModeMatchRequired) {
+ aaudio::AAudioStreamRequest modifiedRequest = request;
+ // Overwrite the original EXCLUSIVE mode with SHARED.
+ modifiedRequest.getConfiguration().setSharingMode(AAUDIO_SHARING_MODE_SHARED);
+ serviceStream = new AAudioServiceStreamShared(*this);
+ result = serviceStream->open(modifiedRequest);
}
if (result != AAUDIO_OK) {
diff --git a/services/oboeservice/AAudioServiceEndpoint.cpp b/services/oboeservice/AAudioServiceEndpoint.cpp
index 0349034..24ab65e 100644
--- a/services/oboeservice/AAudioServiceEndpoint.cpp
+++ b/services/oboeservice/AAudioServiceEndpoint.cpp
@@ -59,6 +59,7 @@
result << " Device Id: " << getDeviceId() << "\n";
result << " Sample Rate: " << getSampleRate() << "\n";
result << " Channel Count: " << getSamplesPerFrame() << "\n";
+ result << " Format: " << getFormat() << "\n";
result << " Frames Per Burst: " << mFramesPerBurst << "\n";
result << " Usage: " << getUsage() << "\n";
result << " ContentType: " << getContentType() << "\n";
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.cpp b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
index f30f9bb..18fcd35 100644
--- a/services/oboeservice/AAudioServiceEndpointMMAP.cpp
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
@@ -111,11 +111,11 @@
mRequestedDeviceId = deviceId = getDeviceId();
// Fill in config
- aaudio_format_t aaudioFormat = getFormat();
- if (aaudioFormat == AAUDIO_UNSPECIFIED || aaudioFormat == AAUDIO_FORMAT_PCM_FLOAT) {
- aaudioFormat = AAUDIO_FORMAT_PCM_I16;
+ audio_format_t audioFormat = getFormat();
+ if (audioFormat == AUDIO_FORMAT_DEFAULT || audioFormat == AUDIO_FORMAT_PCM_FLOAT) {
+ audioFormat = AUDIO_FORMAT_PCM_16_BIT;
}
- config.format = AAudioConvert_aaudioToAndroidDataFormat(aaudioFormat);
+ config.format = audioFormat;
int32_t aaudioSampleRate = getSampleRate();
if (aaudioSampleRate == AAUDIO_UNSPECIFIED) {
@@ -210,9 +210,6 @@
uid_t audioServiceUid = getuid();
if ((mMmapClient.clientUid != audioServiceUid) &&
getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE) {
- // Fallback is handled by caller but indicate what is possible in case
- // this is used in the future
- setSharingMode(AAUDIO_SHARING_MODE_SHARED);
ALOGW("%s() - exclusive FD cannot be used by client", __func__);
result = AAUDIO_ERROR_UNAVAILABLE;
goto error;
@@ -233,7 +230,7 @@
goto error;
}
mFramesPerBurst = mMmapBufferinfo.burst_size_frames;
- setFormat(AAudioConvert_androidToAAudioDataFormat(config.format));
+ setFormat(config.format);
setSampleRate(config.sample_rate);
// Scale up the burst size to meet the minimum equivalent in microseconds.
@@ -253,6 +250,9 @@
", deviceId = %d, capacity = %d\n",
__func__, getSampleRate(), getSamplesPerFrame(), deviceId, getBufferCapacity());
+ ALOGD("%s() format = =x%08x, frame size = %d",
+ __func__, getFormat(), calculateBytesPerFrame());
+
return result;
error:
diff --git a/services/oboeservice/AAudioServiceEndpointShared.cpp b/services/oboeservice/AAudioServiceEndpointShared.cpp
index 63b9983..a7cd128 100644
--- a/services/oboeservice/AAudioServiceEndpointShared.cpp
+++ b/services/oboeservice/AAudioServiceEndpointShared.cpp
@@ -78,6 +78,7 @@
setSamplesPerFrame(mStreamInternal->getSamplesPerFrame());
setDeviceId(mStreamInternal->getDeviceId());
setSessionId(mStreamInternal->getSessionId());
+ setFormat(AUDIO_FORMAT_PCM_FLOAT); // force for mixer
mFramesPerBurst = mStreamInternal->getFramesPerBurst();
return result;
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index 9af8af3..12be4a3 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -81,8 +81,7 @@
return result.str();
}
-aaudio_result_t AAudioServiceStreamBase::open(const aaudio::AAudioStreamRequest &request,
- aaudio_sharing_mode_t sharingMode) {
+aaudio_result_t AAudioServiceStreamBase::open(const aaudio::AAudioStreamRequest &request) {
AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance();
aaudio_result_t result = AAUDIO_OK;
@@ -109,8 +108,7 @@
// referenced until the service returns a handle to the client.
// So only one thread can open a stream.
mServiceEndpoint = mEndpointManager.openEndpoint(mAudioService,
- request,
- sharingMode);
+ request);
if (mServiceEndpoint == nullptr) {
ALOGE("%s() openEndpoint() failed", __func__);
result = AAUDIO_ERROR_UNAVAILABLE;
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.cpp b/services/oboeservice/AAudioServiceStreamMMAP.cpp
index c845309..9377945 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.cpp
+++ b/services/oboeservice/AAudioServiceStreamMMAP.cpp
@@ -64,8 +64,13 @@
sp<AAudioServiceStreamMMAP> keep(this);
- aaudio_result_t result = AAudioServiceStreamBase::open(request,
- AAUDIO_SHARING_MODE_EXCLUSIVE);
+ if (request.getConstantConfiguration().getSharingMode() != AAUDIO_SHARING_MODE_EXCLUSIVE) {
+ ALOGE("%s() sharingMode mismatch %d", __func__,
+ request.getConstantConfiguration().getSharingMode());
+ return AAUDIO_ERROR_INTERNAL;
+ }
+
+ aaudio_result_t result = AAudioServiceStreamBase::open(request);
if (result != AAUDIO_OK) {
return result;
}
diff --git a/services/oboeservice/AAudioServiceStreamShared.cpp b/services/oboeservice/AAudioServiceStreamShared.cpp
index 05c5735..dbc2c2e 100644
--- a/services/oboeservice/AAudioServiceStreamShared.cpp
+++ b/services/oboeservice/AAudioServiceStreamShared.cpp
@@ -120,7 +120,13 @@
sp<AAudioServiceStreamShared> keep(this);
- aaudio_result_t result = AAudioServiceStreamBase::open(request, AAUDIO_SHARING_MODE_SHARED);
+ if (request.getConstantConfiguration().getSharingMode() != AAUDIO_SHARING_MODE_SHARED) {
+ ALOGE("%s() sharingMode mismatch %d", __func__,
+ request.getConstantConfiguration().getSharingMode());
+ return AAUDIO_ERROR_INTERNAL;
+ }
+
+ aaudio_result_t result = AAudioServiceStreamBase::open(request);
if (result != AAUDIO_OK) {
ALOGE("%s() returned %d", __func__, result);
return result;
@@ -136,10 +142,10 @@
// Is the request compatible with the shared endpoint?
setFormat(configurationInput.getFormat());
- if (getFormat() == AAUDIO_FORMAT_UNSPECIFIED) {
- setFormat(AAUDIO_FORMAT_PCM_FLOAT);
- } else if (getFormat() != AAUDIO_FORMAT_PCM_FLOAT) {
- ALOGD("%s() mAudioFormat = %d, need FLOAT", __func__, getFormat());
+ if (getFormat() == AUDIO_FORMAT_DEFAULT) {
+ setFormat(AUDIO_FORMAT_PCM_FLOAT);
+ } else if (getFormat() != AUDIO_FORMAT_PCM_FLOAT) {
+ ALOGE("%s() audio_format_t mAudioFormat = %d, need FLOAT", __func__, getFormat());
result = AAUDIO_ERROR_INVALID_FORMAT;
goto error;
}
@@ -148,7 +154,7 @@
if (getSampleRate() == AAUDIO_UNSPECIFIED) {
setSampleRate(endpoint->getSampleRate());
} else if (getSampleRate() != endpoint->getSampleRate()) {
- ALOGD("%s() mSampleRate = %d, need %d",
+ ALOGE("%s() mSampleRate = %d, need %d",
__func__, getSampleRate(), endpoint->getSampleRate());
result = AAUDIO_ERROR_INVALID_RATE;
goto error;
@@ -158,7 +164,7 @@
if (getSamplesPerFrame() == AAUDIO_UNSPECIFIED) {
setSamplesPerFrame(endpoint->getSamplesPerFrame());
} else if (getSamplesPerFrame() != endpoint->getSamplesPerFrame()) {
- ALOGD("%s() mSamplesPerFrame = %d, need %d",
+ ALOGE("%s() mSamplesPerFrame = %d, need %d",
__func__, getSamplesPerFrame(), endpoint->getSamplesPerFrame());
result = AAUDIO_ERROR_OUT_OF_RANGE;
goto error;
@@ -185,8 +191,8 @@
}
}
- ALOGD("AAudioServiceStreamShared::open() actual rate = %d, channels = %d, deviceId = %d",
- getSampleRate(), getSamplesPerFrame(), endpoint->getDeviceId());
+ ALOGD("%s() actual rate = %d, channels = %d, deviceId = %d",
+ __func__, getSampleRate(), getSamplesPerFrame(), endpoint->getDeviceId());
result = endpoint->registerStream(keep);
if (result != AAUDIO_OK) {