diff options
64 files changed, 3825 insertions, 1830 deletions
diff --git a/CleanSpec.mk b/CleanSpec.mk index 20da925..d0890fe 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -55,6 +55,10 @@ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libaudiopolicy $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libaudiopolicy.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libaudiopolicyservice_intermediates) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libaudiopolicymanager_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libaudiopolicyservice.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libaudiopolicymanager.so) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libaudiopolicyservice_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libaudiopolicymanager_intermediates) # ************************************************ # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST diff --git a/camera/CameraMetadata.cpp b/camera/CameraMetadata.cpp index 1567cd1..043437f 100644 --- a/camera/CameraMetadata.cpp +++ b/camera/CameraMetadata.cpp @@ -590,7 +590,8 @@ status_t CameraMetadata::writeToParcel(Parcel& data, const uintptr_t metadataStart = ALIGN_TO(blob.data(), alignment); offset = metadataStart - reinterpret_cast<uintptr_t>(blob.data()); ALOGV("%s: alignment is: %zu, metadata start: %p, offset: %zu", - __FUNCTION__, alignment, metadataStart, offset); + __FUNCTION__, alignment, + reinterpret_cast<const void *>(metadataStart), offset); copy_camera_metadata(reinterpret_cast<void*>(metadataStart), metadataSize, metadata); // Not too big of a problem since receiving side does hard validation diff --git a/camera/VendorTagDescriptor.cpp b/camera/VendorTagDescriptor.cpp index 59dce91..0dda6b6 100644 --- a/camera/VendorTagDescriptor.cpp +++ b/camera/VendorTagDescriptor.cpp @@ -213,7 +213,7 @@ status_t VendorTagDescriptor::createFromParcel(const Parcel* parcel, return res; } if (sectionCount < (maxSectionIndex + 1)) { - ALOGE("%s: Incorrect number of sections defined, received %d, needs %d.", + ALOGE("%s: Incorrect number of sections defined, received %zu, needs %d.", __FUNCTION__, sectionCount, (maxSectionIndex + 1)); return BAD_VALUE; } @@ -222,14 +222,16 @@ status_t VendorTagDescriptor::createFromParcel(const Parcel* parcel, for (size_t i = 0; i < sectionCount; ++i) { String8 sectionName = parcel->readString8(); if (sectionName.isEmpty()) { - ALOGE("%s: parcel section name was NULL for section %d.", __FUNCTION__, i); + ALOGE("%s: parcel section name was NULL for section %zu.", + __FUNCTION__, i); return NOT_ENOUGH_DATA; } desc->mSections.add(sectionName); } } - LOG_ALWAYS_FATAL_IF(tagCount != allTags.size(), "tagCount must be the same as allTags size"); + LOG_ALWAYS_FATAL_IF(static_cast<size_t>(tagCount) != allTags.size(), + "tagCount must be the same as allTags size"); // Set up reverse mapping for (size_t i = 0; i < static_cast<size_t>(tagCount); ++i) { uint32_t tag = allTags[i]; @@ -349,18 +351,18 @@ void VendorTagDescriptor::dump(int fd, int verbosity, int indentation) const { size_t size = mTagToNameMap.size(); if (size == 0) { - fdprintf(fd, "%*sDumping configured vendor tag descriptors: None set\n", + dprintf(fd, "%*sDumping configured vendor tag descriptors: None set\n", indentation, ""); return; } - fdprintf(fd, "%*sDumping configured vendor tag descriptors: %zu entries\n", + dprintf(fd, "%*sDumping configured vendor tag descriptors: %zu entries\n", indentation, "", size); for (size_t i = 0; i < size; ++i) { uint32_t tag = mTagToNameMap.keyAt(i); if (verbosity < 1) { - fdprintf(fd, "%*s0x%x\n", indentation + 2, "", tag); + dprintf(fd, "%*s0x%x\n", indentation + 2, "", tag); continue; } String8 name = mTagToNameMap.valueAt(i); @@ -369,7 +371,7 @@ void VendorTagDescriptor::dump(int fd, int verbosity, int indentation) const { int type = mTagToTypeMap.valueFor(tag); const char* typeName = (type >= 0 && type < NUM_TYPES) ? camera_metadata_type_names[type] : "UNKNOWN"; - fdprintf(fd, "%*s0x%x (%s) with type %d (%s) defined in section %s\n", indentation + 2, + dprintf(fd, "%*s0x%x (%s) with type %d (%s) defined in section %s\n", indentation + 2, "", tag, name.string(), type, typeName, sectionName.string()); } @@ -409,7 +411,7 @@ sp<VendorTagDescriptor> VendorTagDescriptor::getGlobalVendorTagDescriptor() { extern "C" { -int vendor_tag_descriptor_get_tag_count(const vendor_tag_ops_t* v) { +int vendor_tag_descriptor_get_tag_count(const vendor_tag_ops_t* /*v*/) { Mutex::Autolock al(sLock); if (sGlobalVendorTagDescriptor == NULL) { ALOGE("%s: Vendor tag descriptor not initialized.", __FUNCTION__); @@ -418,7 +420,7 @@ int vendor_tag_descriptor_get_tag_count(const vendor_tag_ops_t* v) { return sGlobalVendorTagDescriptor->getTagCount(); } -void vendor_tag_descriptor_get_all_tags(const vendor_tag_ops_t* v, uint32_t* tagArray) { +void vendor_tag_descriptor_get_all_tags(const vendor_tag_ops_t* /*v*/, uint32_t* tagArray) { Mutex::Autolock al(sLock); if (sGlobalVendorTagDescriptor == NULL) { ALOGE("%s: Vendor tag descriptor not initialized.", __FUNCTION__); @@ -427,7 +429,7 @@ void vendor_tag_descriptor_get_all_tags(const vendor_tag_ops_t* v, uint32_t* tag sGlobalVendorTagDescriptor->getTagArray(tagArray); } -const char* vendor_tag_descriptor_get_section_name(const vendor_tag_ops_t* v, uint32_t tag) { +const char* vendor_tag_descriptor_get_section_name(const vendor_tag_ops_t* /*v*/, uint32_t tag) { Mutex::Autolock al(sLock); if (sGlobalVendorTagDescriptor == NULL) { ALOGE("%s: Vendor tag descriptor not initialized.", __FUNCTION__); @@ -436,7 +438,7 @@ const char* vendor_tag_descriptor_get_section_name(const vendor_tag_ops_t* v, ui return sGlobalVendorTagDescriptor->getSectionName(tag); } -const char* vendor_tag_descriptor_get_tag_name(const vendor_tag_ops_t* v, uint32_t tag) { +const char* vendor_tag_descriptor_get_tag_name(const vendor_tag_ops_t* /*v*/, uint32_t tag) { Mutex::Autolock al(sLock); if (sGlobalVendorTagDescriptor == NULL) { ALOGE("%s: Vendor tag descriptor not initialized.", __FUNCTION__); @@ -445,7 +447,7 @@ const char* vendor_tag_descriptor_get_tag_name(const vendor_tag_ops_t* v, uint32 return sGlobalVendorTagDescriptor->getTagName(tag); } -int vendor_tag_descriptor_get_tag_type(const vendor_tag_ops_t* v, uint32_t tag) { +int vendor_tag_descriptor_get_tag_type(const vendor_tag_ops_t* /*v*/, uint32_t tag) { Mutex::Autolock al(sLock); if (sGlobalVendorTagDescriptor == NULL) { ALOGE("%s: Vendor tag descriptor not initialized.", __FUNCTION__); diff --git a/drm/libdrmframework/Android.mk b/drm/libdrmframework/Android.mk index 49c4f9b..33f9d3b 100644 --- a/drm/libdrmframework/Android.mk +++ b/drm/libdrmframework/Android.mk @@ -19,12 +19,14 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ DrmManagerClientImpl.cpp \ - DrmManagerClient.cpp + DrmManagerClient.cpp \ + NoOpDrmManagerClientImpl.cpp LOCAL_MODULE:= libdrmframework LOCAL_SHARED_LIBRARIES := \ libutils \ + libcutils \ liblog \ libbinder \ libdl diff --git a/drm/libdrmframework/DrmManagerClient.cpp b/drm/libdrmframework/DrmManagerClient.cpp index ea30d01..440dd91 100644 --- a/drm/libdrmframework/DrmManagerClient.cpp +++ b/drm/libdrmframework/DrmManagerClient.cpp @@ -29,7 +29,7 @@ DrmManagerClient::DrmManagerClient(): } DrmManagerClient::~DrmManagerClient() { - DrmManagerClientImpl::remove(mUniqueId); + mDrmManagerClientImpl->remove(mUniqueId); mDrmManagerClientImpl->removeClient(mUniqueId); mDrmManagerClientImpl->setOnInfoListener(mUniqueId, NULL); } diff --git a/drm/libdrmframework/DrmManagerClientImpl.cpp b/drm/libdrmframework/DrmManagerClientImpl.cpp index ffefd74..2d2c90e 100644 --- a/drm/libdrmframework/DrmManagerClientImpl.cpp +++ b/drm/libdrmframework/DrmManagerClientImpl.cpp @@ -21,8 +21,10 @@ #include <utils/String8.h> #include <utils/Vector.h> #include <binder/IServiceManager.h> +#include <cutils/properties.h> #include "DrmManagerClientImpl.h" +#include "NoOpDrmManagerClientImpl.h" using namespace android; @@ -35,9 +37,12 @@ const String8 DrmManagerClientImpl::EMPTY_STRING(""); DrmManagerClientImpl* DrmManagerClientImpl::create( int* pUniqueId, bool isNative) { - *pUniqueId = getDrmManagerService()->addUniqueId(isNative); - - return new DrmManagerClientImpl(); + sp<IDrmManagerService> service = getDrmManagerService(); + if (service != NULL) { + *pUniqueId = getDrmManagerService()->addUniqueId(isNative); + return new DrmManagerClientImpl(); + } + return new NoOpDrmManagerClientImpl(); } void DrmManagerClientImpl::remove(int uniqueId) { @@ -47,6 +52,12 @@ void DrmManagerClientImpl::remove(int uniqueId) { const sp<IDrmManagerService>& DrmManagerClientImpl::getDrmManagerService() { Mutex::Autolock lock(sMutex); if (NULL == sDrmManagerService.get()) { + char value[PROPERTY_VALUE_MAX]; + if (property_get("drm.service.enabled", value, NULL) == 0) { + // Drm is undefined for this device + return sDrmManagerService; + } + sp<IServiceManager> sm = defaultServiceManager(); sp<IBinder> binder; do { diff --git a/drm/libdrmframework/NoOpDrmManagerClientImpl.cpp b/drm/libdrmframework/NoOpDrmManagerClientImpl.cpp new file mode 100644 index 0000000..dab583d --- /dev/null +++ b/drm/libdrmframework/NoOpDrmManagerClientImpl.cpp @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2014 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 "NoOpDrmManagerClientImpl.h" + +namespace android { + +void NoOpDrmManagerClientImpl::remove(int uniqueId) { +} + +void NoOpDrmManagerClientImpl::addClient(int uniqueId) { +} + +void NoOpDrmManagerClientImpl::removeClient(int uniqueId) { +} + +status_t NoOpDrmManagerClientImpl::setOnInfoListener( + int uniqueId, const sp<DrmManagerClient::OnInfoListener>& infoListener) { + return UNKNOWN_ERROR; +} + +DrmConstraints* NoOpDrmManagerClientImpl::getConstraints(int uniqueId, const String8* path, const int action) { + return NULL; +} + +DrmMetadata* NoOpDrmManagerClientImpl::getMetadata(int uniqueId, const String8* path) { + return NULL; +} + +bool NoOpDrmManagerClientImpl::canHandle(int uniqueId, const String8& path, const String8& mimeType) { + return false; +} + +DrmInfoStatus* NoOpDrmManagerClientImpl::processDrmInfo(int uniqueId, const DrmInfo* drmInfo) { + return NULL; +} + +DrmInfo* NoOpDrmManagerClientImpl::acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest) { + return NULL; +} + +status_t NoOpDrmManagerClientImpl::saveRights(int uniqueId, const DrmRights& drmRights, + const String8& rightsPath, const String8& contentPath) { + return UNKNOWN_ERROR; +} + +String8 NoOpDrmManagerClientImpl::getOriginalMimeType(int uniqueId, const String8& path, int fd) { + return String8(); +} + +int NoOpDrmManagerClientImpl::getDrmObjectType(int uniqueId, const String8& path, const String8& mimeType) { + return -1; +} + +int NoOpDrmManagerClientImpl::checkRightsStatus(int uniqueId, const String8& path, int action) { + return -1; +} + +status_t NoOpDrmManagerClientImpl::consumeRights(int uniqueId, sp<DecryptHandle> &decryptHandle, int action, bool reserve) { + return UNKNOWN_ERROR; +} + +status_t NoOpDrmManagerClientImpl::setPlaybackStatus( + int uniqueId, sp<DecryptHandle> &decryptHandle, int playbackStatus, int64_t position) { + return UNKNOWN_ERROR; +} + +bool NoOpDrmManagerClientImpl::validateAction( + int uniqueId, const String8& path, int action, const ActionDescription& description) { + return false; +} + +status_t NoOpDrmManagerClientImpl::removeRights(int uniqueId, const String8& path) { + return UNKNOWN_ERROR; +} + +status_t NoOpDrmManagerClientImpl::removeAllRights(int uniqueId) { + return UNKNOWN_ERROR; +} + +int NoOpDrmManagerClientImpl::openConvertSession(int uniqueId, const String8& mimeType) { + return -1; +} + +DrmConvertedStatus* NoOpDrmManagerClientImpl::convertData(int uniqueId, int convertId, const DrmBuffer* inputData) { + return NULL; +} + +DrmConvertedStatus* NoOpDrmManagerClientImpl::closeConvertSession(int uniqueId, int convertId) { + return NULL; +} + +status_t NoOpDrmManagerClientImpl::getAllSupportInfo(int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray) { + return UNKNOWN_ERROR; +} + +sp<DecryptHandle> NoOpDrmManagerClientImpl::openDecryptSession( + int uniqueId, int fd, off64_t offset, off64_t length, const char* mime) { + return NULL; +} + +sp<DecryptHandle> NoOpDrmManagerClientImpl::openDecryptSession( + int uniqueId, const char* uri, const char* mime) { + return NULL; +} + +sp<DecryptHandle> NoOpDrmManagerClientImpl::openDecryptSession(int uniqueId, const DrmBuffer& buf, + const String8& mimeType) { + return NULL; +} + +status_t NoOpDrmManagerClientImpl::closeDecryptSession(int uniqueId, sp<DecryptHandle> &decryptHandle) { + return UNKNOWN_ERROR; +} + +status_t NoOpDrmManagerClientImpl::initializeDecryptUnit(int uniqueId, sp<DecryptHandle> &decryptHandle, + int decryptUnitId, const DrmBuffer* headerInfo) { + return UNKNOWN_ERROR; +} + +status_t NoOpDrmManagerClientImpl::decrypt(int uniqueId, sp<DecryptHandle> &decryptHandle, int decryptUnitId, + const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) { + return UNKNOWN_ERROR; +} + +status_t NoOpDrmManagerClientImpl::finalizeDecryptUnit(int uniqueId, sp<DecryptHandle> &decryptHandle, int decryptUnitId) { + return UNKNOWN_ERROR; +} + +ssize_t NoOpDrmManagerClientImpl::pread(int uniqueId, sp<DecryptHandle> &decryptHandle, + void* buffer, ssize_t numBytes, off64_t offset) { + return -1; +} + +status_t NoOpDrmManagerClientImpl::notify(const DrmInfoEvent& event) { + return UNKNOWN_ERROR; +} + +} diff --git a/drm/libdrmframework/include/DrmManagerClientImpl.h b/drm/libdrmframework/include/DrmManagerClientImpl.h index 3400cb1..3858675 100644 --- a/drm/libdrmframework/include/DrmManagerClientImpl.h +++ b/drm/libdrmframework/include/DrmManagerClientImpl.h @@ -34,30 +34,30 @@ class DrmInfoEvent; * */ class DrmManagerClientImpl : public BnDrmServiceListener { -private: +protected: DrmManagerClientImpl() { } public: static DrmManagerClientImpl* create(int* pUniqueId, bool isNative); - static void remove(int uniqueId); - virtual ~DrmManagerClientImpl() { } public: + virtual void remove(int uniqueId); + /** * Adds the client respective to given unique id. * * @param[in] uniqueId Unique identifier for a session */ - void addClient(int uniqueId); + virtual void addClient(int uniqueId); /** * Removes the client respective to given unique id. * * @param[in] uniqueId Unique identifier for a session */ - void removeClient(int uniqueId); + virtual void removeClient(int uniqueId); /** * Register a callback to be invoked when the caller required to @@ -68,7 +68,7 @@ public: * @return status_t * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure */ - status_t setOnInfoListener( + virtual status_t setOnInfoListener( int uniqueId, const sp<DrmManagerClient::OnInfoListener>& infoListener); /** @@ -83,7 +83,7 @@ public: * @note * In case of error, return NULL */ - DrmConstraints* getConstraints(int uniqueId, const String8* path, const int action); + virtual DrmConstraints* getConstraints(int uniqueId, const String8* path, const int action); /** * Get metadata information associated with input content. @@ -95,7 +95,7 @@ public: * @note * In case of error, return NULL */ - DrmMetadata* getMetadata(int uniqueId, const String8* path); + virtual DrmMetadata* getMetadata(int uniqueId, const String8* path); /** * Check whether the given mimetype or path can be handled @@ -106,7 +106,7 @@ public: * @return * True if DrmManager can handle given path or mime type. */ - bool canHandle(int uniqueId, const String8& path, const String8& mimeType); + virtual bool canHandle(int uniqueId, const String8& path, const String8& mimeType); /** * Executes given drm information based on its type @@ -116,7 +116,7 @@ public: * @return DrmInfoStatus * instance as a result of processing given input */ - DrmInfoStatus* processDrmInfo(int uniqueId, const DrmInfo* drmInfo); + virtual DrmInfoStatus* processDrmInfo(int uniqueId, const DrmInfo* drmInfo); /** * Retrieves necessary information for registration, unregistration or rights @@ -127,7 +127,7 @@ public: * @return DrmInfo * instance as a result of processing given input */ - DrmInfo* acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest); + virtual DrmInfo* acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest); /** * Save DRM rights to specified rights path @@ -140,7 +140,7 @@ public: * @return status_t * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure */ - status_t saveRights(int uniqueId, const DrmRights& drmRights, + virtual status_t saveRights(int uniqueId, const DrmRights& drmRights, const String8& rightsPath, const String8& contentPath); /** @@ -152,7 +152,7 @@ public: * @return String8 * Returns mime-type of the original content, such as "video/mpeg" */ - String8 getOriginalMimeType(int uniqueId, const String8& path, int fd); + virtual String8 getOriginalMimeType(int uniqueId, const String8& path, int fd); /** * Retrieves the type of the protected object (content, rights, etc..) @@ -165,7 +165,7 @@ public: * @return type of the DRM content, * such as DrmObjectType::CONTENT, DrmObjectType::RIGHTS_OBJECT */ - int getDrmObjectType(int uniqueId, const String8& path, const String8& mimeType); + virtual int getDrmObjectType(int uniqueId, const String8& path, const String8& mimeType); /** * Check whether the given content has valid rights or not @@ -176,7 +176,7 @@ public: * @return the status of the rights for the protected content, * such as RightsStatus::RIGHTS_VALID, RightsStatus::RIGHTS_EXPIRED, etc. */ - int checkRightsStatus(int uniqueId, const String8& path, int action); + virtual int checkRightsStatus(int uniqueId, const String8& path, int action); /** * Consumes the rights for a content. @@ -190,7 +190,7 @@ public: * @return status_t * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure */ - status_t consumeRights(int uniqueId, sp<DecryptHandle> &decryptHandle, int action, bool reserve); + virtual status_t consumeRights(int uniqueId, sp<DecryptHandle> &decryptHandle, int action, bool reserve); /** * Informs the DRM engine about the playback actions performed on the DRM files. @@ -203,7 +203,7 @@ public: * @return status_t * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure */ - status_t setPlaybackStatus( + virtual status_t setPlaybackStatus( int uniqueId, sp<DecryptHandle> &decryptHandle, int playbackStatus, int64_t position); /** @@ -215,7 +215,7 @@ public: * @param[in] description Detailed description of the action * @return true if the action is allowed. */ - bool validateAction( + virtual bool validateAction( int uniqueId, const String8& path, int action, const ActionDescription& description); /** @@ -226,7 +226,7 @@ public: * @return status_t * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure */ - status_t removeRights(int uniqueId, const String8& path); + virtual status_t removeRights(int uniqueId, const String8& path); /** * Removes all the rights information of each plug-in associated with @@ -236,7 +236,7 @@ public: * @return status_t * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure */ - status_t removeAllRights(int uniqueId); + virtual status_t removeAllRights(int uniqueId); /** * This API is for Forward Lock based DRM scheme. @@ -248,7 +248,7 @@ public: * @param[in] mimeType Description/MIME type of the input data packet * @return Return handle for the convert session */ - int openConvertSession(int uniqueId, const String8& mimeType); + virtual int openConvertSession(int uniqueId, const String8& mimeType); /** * Accepts and converts the input data which is part of DRM file. @@ -263,7 +263,7 @@ public: * the output converted data and offset. In this case the * application will ignore the offset information. */ - DrmConvertedStatus* convertData(int uniqueId, int convertId, const DrmBuffer* inputData); + virtual DrmConvertedStatus* convertData(int uniqueId, int convertId, const DrmBuffer* inputData); /** * Informs the Drm Agent when there is no more data which need to be converted @@ -279,7 +279,7 @@ public: * the application on which offset these signature data * should be appended. */ - DrmConvertedStatus* closeConvertSession(int uniqueId, int convertId); + virtual DrmConvertedStatus* closeConvertSession(int uniqueId, int convertId); /** * Retrieves all DrmSupportInfo instance that native DRM framework can handle. @@ -292,7 +292,7 @@ public: * @return status_t * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure */ - status_t getAllSupportInfo(int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray); + virtual status_t getAllSupportInfo(int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray); /** * Open the decrypt session to decrypt the given protected content @@ -305,7 +305,7 @@ public: * @return * Handle for the decryption session */ - sp<DecryptHandle> openDecryptSession( + virtual sp<DecryptHandle> openDecryptSession( int uniqueId, int fd, off64_t offset, off64_t length, const char* mime); /** @@ -317,7 +317,7 @@ public: * @return * Handle for the decryption session */ - sp<DecryptHandle> openDecryptSession( + virtual sp<DecryptHandle> openDecryptSession( int uniqueId, const char* uri, const char* mime); /** @@ -329,7 +329,7 @@ public: * @return * Handle for the decryption session */ - sp<DecryptHandle> openDecryptSession(int uniqueId, const DrmBuffer& buf, + virtual sp<DecryptHandle> openDecryptSession(int uniqueId, const DrmBuffer& buf, const String8& mimeType); /** @@ -340,7 +340,7 @@ public: * @return status_t * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure */ - status_t closeDecryptSession(int uniqueId, sp<DecryptHandle> &decryptHandle); + virtual status_t closeDecryptSession(int uniqueId, sp<DecryptHandle> &decryptHandle); /** * Initialize decryption for the given unit of the protected content @@ -352,7 +352,7 @@ public: * @return status_t * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure */ - status_t initializeDecryptUnit(int uniqueId, sp<DecryptHandle> &decryptHandle, + virtual status_t initializeDecryptUnit(int uniqueId, sp<DecryptHandle> &decryptHandle, int decryptUnitId, const DrmBuffer* headerInfo); /** @@ -372,7 +372,7 @@ public: * DRM_ERROR_SESSION_NOT_OPENED, DRM_ERROR_DECRYPT_UNIT_NOT_INITIALIZED, * DRM_ERROR_DECRYPT for failure. */ - status_t decrypt(int uniqueId, sp<DecryptHandle> &decryptHandle, int decryptUnitId, + virtual status_t decrypt(int uniqueId, sp<DecryptHandle> &decryptHandle, int decryptUnitId, const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV); /** @@ -384,7 +384,7 @@ public: * @return status_t * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure */ - status_t finalizeDecryptUnit(int uniqueId, sp<DecryptHandle> &decryptHandle, int decryptUnitId); + virtual status_t finalizeDecryptUnit(int uniqueId, sp<DecryptHandle> &decryptHandle, int decryptUnitId); /** * Reads the specified number of bytes from an open DRM file. @@ -397,7 +397,7 @@ public: * * @return Number of bytes read. Returns -1 for Failure. */ - ssize_t pread(int uniqueId, sp<DecryptHandle> &decryptHandle, + virtual ssize_t pread(int uniqueId, sp<DecryptHandle> &decryptHandle, void* buffer, ssize_t numBytes, off64_t offset); /** @@ -407,7 +407,7 @@ public: * @return status_t * Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure */ - status_t notify(const DrmInfoEvent& event); + virtual status_t notify(const DrmInfoEvent& event); private: Mutex mLock; diff --git a/drm/libdrmframework/include/NoOpDrmManagerClientImpl.h b/drm/libdrmframework/include/NoOpDrmManagerClientImpl.h new file mode 100644 index 0000000..e8e8f42 --- /dev/null +++ b/drm/libdrmframework/include/NoOpDrmManagerClientImpl.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2014 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 __NO_OP_DRM_MANAGER_CLIENT_IMPL_H__ +#define __NO_OP_DRM_MANAGER_CLIENT_IMPL_H__ + +#include "DrmManagerClientImpl.h" + +namespace android { + +class NoOpDrmManagerClientImpl : public DrmManagerClientImpl { +public: + NoOpDrmManagerClientImpl() { } + + void remove(int uniqueId); + void addClient(int uniqueId); + void removeClient(int uniqueId); + status_t setOnInfoListener( + int uniqueId, const sp<DrmManagerClient::OnInfoListener>& infoListener); + DrmConstraints* getConstraints(int uniqueId, const String8* path, const int action); + + DrmMetadata* getMetadata(int uniqueId, const String8* path); + bool canHandle(int uniqueId, const String8& path, const String8& mimeType); + DrmInfoStatus* processDrmInfo(int uniqueId, const DrmInfo* drmInfo); + DrmInfo* acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest); + status_t saveRights(int uniqueId, const DrmRights& drmRights, + const String8& rightsPath, const String8& contentPath); + String8 getOriginalMimeType(int uniqueId, const String8& path, int fd); + int getDrmObjectType(int uniqueId, const String8& path, const String8& mimeType); + int checkRightsStatus(int uniqueId, const String8& path, int action); + status_t consumeRights(int uniqueId, sp<DecryptHandle> &decryptHandle, int action, bool reserve); + status_t setPlaybackStatus( + int uniqueId, sp<DecryptHandle> &decryptHandle, int playbackStatus, int64_t position); + bool validateAction( + int uniqueId, const String8& path, int action, const ActionDescription& description); + status_t removeRights(int uniqueId, const String8& path); + status_t removeAllRights(int uniqueId); + int openConvertSession(int uniqueId, const String8& mimeType); + DrmConvertedStatus* convertData(int uniqueId, int convertId, const DrmBuffer* inputData); + DrmConvertedStatus* closeConvertSession(int uniqueId, int convertId); + status_t getAllSupportInfo(int uniqueId, int* length, DrmSupportInfo** drmSupportInfoArray); + sp<DecryptHandle> openDecryptSession( + int uniqueId, int fd, off64_t offset, off64_t length, const char* mime); + sp<DecryptHandle> openDecryptSession( + int uniqueId, const char* uri, const char* mime); + sp<DecryptHandle> openDecryptSession(int uniqueId, const DrmBuffer& buf, + const String8& mimeType); + status_t closeDecryptSession(int uniqueId, sp<DecryptHandle> &decryptHandle); + status_t initializeDecryptUnit(int uniqueId, sp<DecryptHandle> &decryptHandle, + int decryptUnitId, const DrmBuffer* headerInfo); + status_t decrypt(int uniqueId, sp<DecryptHandle> &decryptHandle, int decryptUnitId, + const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV); + status_t finalizeDecryptUnit(int uniqueId, sp<DecryptHandle> &decryptHandle, int decryptUnitId); + ssize_t pread(int uniqueId, sp<DecryptHandle> &decryptHandle, + void* buffer, ssize_t numBytes, off64_t offset); + status_t notify(const DrmInfoEvent& event); +}; + +} + +#endif // __NO_OP_DRM_MANAGER_CLIENT_IMPL_H diff --git a/include/soundtrigger/ISoundTrigger.h b/include/soundtrigger/ISoundTrigger.h new file mode 100644 index 0000000..5fd8eb2 --- /dev/null +++ b/include/soundtrigger/ISoundTrigger.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2014 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_ISOUNDTRIGGER_H +#define ANDROID_HARDWARE_ISOUNDTRIGGER_H + +#include <utils/RefBase.h> +#include <binder/IInterface.h> +#include <binder/Parcel.h> +#include <binder/IMemory.h> +#include <system/sound_trigger.h> + +namespace android { + +class ISoundTrigger : public IInterface +{ +public: + DECLARE_META_INTERFACE(SoundTrigger); + + virtual void detach() = 0; + + virtual status_t loadSoundModel(const sp<IMemory>& modelMemory, + sound_model_handle_t *handle) = 0; + + virtual status_t unloadSoundModel(sound_model_handle_t handle) = 0; + + virtual status_t startRecognition(sound_model_handle_t handle, + const sp<IMemory>& dataMemory) = 0; + virtual status_t stopRecognition(sound_model_handle_t handle) = 0; + +}; + +// ---------------------------------------------------------------------------- + +class BnSoundTrigger: public BnInterface<ISoundTrigger> +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +}; // namespace android + +#endif //ANDROID_HARDWARE_ISOUNDTRIGGER_H diff --git a/include/soundtrigger/ISoundTriggerClient.h b/include/soundtrigger/ISoundTriggerClient.h new file mode 100644 index 0000000..7f86d02 --- /dev/null +++ b/include/soundtrigger/ISoundTriggerClient.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2014 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_ISOUNDTRIGGER_CLIENT_H +#define ANDROID_HARDWARE_ISOUNDTRIGGER_CLIENT_H + +#include <utils/RefBase.h> +#include <binder/IInterface.h> +#include <binder/Parcel.h> + +namespace android { + +class ISoundTriggerClient : public IInterface +{ +public: + + DECLARE_META_INTERFACE(SoundTriggerClient); + + virtual void onRecognitionEvent(const sp<IMemory>& eventMemory) = 0; + +}; + +// ---------------------------------------------------------------------------- + +class BnSoundTriggerClient : public BnInterface<ISoundTriggerClient> +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +}; // namespace android + +#endif //ANDROID_HARDWARE_ISOUNDTRIGGER_CLIENT_H diff --git a/include/soundtrigger/ISoundTriggerHwService.h b/include/soundtrigger/ISoundTriggerHwService.h new file mode 100644 index 0000000..05a764a --- /dev/null +++ b/include/soundtrigger/ISoundTriggerHwService.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2014 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_ISOUNDTRIGGER_SERVICE_H +#define ANDROID_HARDWARE_ISOUNDTRIGGER_SERVICE_H + +#include <utils/RefBase.h> +#include <binder/IInterface.h> +#include <binder/Parcel.h> +#include <system/sound_trigger.h> + +namespace android { + +class ISoundTrigger; +class ISoundTriggerClient; + +class ISoundTriggerHwService : public IInterface +{ +public: + + DECLARE_META_INTERFACE(SoundTriggerHwService); + + virtual status_t listModules(struct sound_trigger_module_descriptor *modules, + uint32_t *numModules) = 0; + + virtual status_t attach(const sound_trigger_module_handle_t handle, + const sp<ISoundTriggerClient>& client, + sp<ISoundTrigger>& module) = 0; +}; + +// ---------------------------------------------------------------------------- + +class BnSoundTriggerHwService: public BnInterface<ISoundTriggerHwService> +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +}; // namespace android + +#endif //ANDROID_HARDWARE_ISOUNDTRIGGER_SERVICE_H diff --git a/include/soundtrigger/SoundTrigger.h b/include/soundtrigger/SoundTrigger.h new file mode 100644 index 0000000..1f7f286 --- /dev/null +++ b/include/soundtrigger/SoundTrigger.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2014 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_SOUNDTRIGGER_H +#define ANDROID_HARDWARE_SOUNDTRIGGER_H + +#include <binder/IBinder.h> +#include <soundtrigger/SoundTriggerCallback.h> +#include <soundtrigger/ISoundTrigger.h> +#include <soundtrigger/ISoundTriggerHwService.h> +#include <soundtrigger/ISoundTriggerClient.h> +#include <system/sound_trigger.h> + +namespace android { + +class MemoryDealer; + +class SoundTrigger : public BnSoundTriggerClient, + public IBinder::DeathRecipient +{ +public: + static status_t listModules(struct sound_trigger_module_descriptor *modules, + uint32_t *numModules); + static sp<SoundTrigger> attach(const sound_trigger_module_handle_t module, + const sp<SoundTriggerCallback>& callback); + + virtual ~SoundTrigger(); + + void detach(); + + status_t loadSoundModel(const sp<IMemory>& modelMemory, + sound_model_handle_t *handle); + + status_t unloadSoundModel(sound_model_handle_t handle); + + status_t startRecognition(sound_model_handle_t handle, const sp<IMemory>& dataMemory); + status_t stopRecognition(sound_model_handle_t handle); + + // BpSoundTriggerClient + virtual void onRecognitionEvent(const sp<IMemory>& eventMemory); + + //IBinder::DeathRecipient + virtual void binderDied(const wp<IBinder>& who); + + static status_t stringToGuid(const char *str, sound_trigger_uuid_t *guid); + static status_t guidToString(const sound_trigger_uuid_t *guid, + char *str, size_t maxLen); + +private: + SoundTrigger(sound_trigger_module_handle_t module, + const sp<SoundTriggerCallback>&); + static const sp<ISoundTriggerHwService>& getSoundTriggerHwService(); + + Mutex mLock; + sp<ISoundTrigger> mISoundTrigger; + const sound_trigger_module_handle_t mModule; + sp<SoundTriggerCallback> mCallback; +}; + +}; // namespace android + +#endif //ANDROID_HARDWARE_SOUNDTRIGGER_H diff --git a/include/soundtrigger/SoundTriggerCallback.h b/include/soundtrigger/SoundTriggerCallback.h new file mode 100644 index 0000000..8a5ba02 --- /dev/null +++ b/include/soundtrigger/SoundTriggerCallback.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2014 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_SOUNDTRIGGER_CALLBACK_H +#define ANDROID_HARDWARE_SOUNDTRIGGER_CALLBACK_H + +#include <utils/RefBase.h> +#include <system/sound_trigger.h> + +namespace android { + +class SoundTriggerCallback : public RefBase +{ +public: + + SoundTriggerCallback() {} + virtual ~SoundTriggerCallback() {} + + virtual void onRecognitionEvent(struct sound_trigger_recognition_event *event) = 0; + + virtual void onServiceDied() = 0; + +}; + +}; // namespace android + +#endif //ANDROID_HARDWARE_SOUNDTRIGGER_CALLBACK_H diff --git a/media/libcpustats/Android.mk b/media/libcpustats/Android.mk index b506353..ee283a6 100644 --- a/media/libcpustats/Android.mk +++ b/media/libcpustats/Android.mk @@ -1,4 +1,4 @@ -LOCAL_PATH:= $(call my-dir) +LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) @@ -8,4 +8,6 @@ LOCAL_SRC_FILES := \ LOCAL_MODULE := libcpustats +LOCAL_CFLAGS := -std=gnu++11 -Werror + include $(BUILD_STATIC_LIBRARY) diff --git a/media/libcpustats/ThreadCpuUsage.cpp b/media/libcpustats/ThreadCpuUsage.cpp index 637402a..cfdcb51 100644 --- a/media/libcpustats/ThreadCpuUsage.cpp +++ b/media/libcpustats/ThreadCpuUsage.cpp @@ -21,7 +21,6 @@ #include <stdlib.h> #include <time.h> -#include <utils/Debug.h> #include <utils/Log.h> #include <cpustats/ThreadCpuUsage.h> @@ -218,7 +217,7 @@ uint32_t ThreadCpuUsage::getCpukHz(int cpuNum) #define FREQ_SIZE 64 char freq_path[FREQ_SIZE]; #define FREQ_DIGIT 27 - COMPILE_TIME_ASSERT_FUNCTION_SCOPE(MAX_CPU <= 10); + static_assert(MAX_CPU <= 10, "MAX_CPU too large"); #define FREQ_PATH "/sys/devices/system/cpu/cpu?/cpufreq/scaling_cur_freq" strlcpy(freq_path, FREQ_PATH, sizeof(freq_path)); freq_path[FREQ_DIGIT] = cpuNum + '0'; diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp index a9820e0..194abbb 100644 --- a/media/libmediaplayerservice/MediaRecorderClient.cpp +++ b/media/libmediaplayerservice/MediaRecorderClient.cpp @@ -95,7 +95,8 @@ status_t MediaRecorderClient::setPreviewSurface(const sp<IGraphicBufferProducer> status_t MediaRecorderClient::setVideoSource(int vs) { ALOGV("setVideoSource(%d)", vs); - if (!checkPermission(cameraPermission)) { + // Check camera permission for sources other than SURFACE + if (vs != VIDEO_SOURCE_SURFACE && !checkPermission(cameraPermission)) { return PERMISSION_DENIED; } Mutex::Autolock lock(mLock); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index d8d939a..857e703 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -1376,16 +1376,15 @@ void NuPlayer::onSourceNotify(const sp<AMessage> &msg) { sp<NuPlayerDriver> driver = mDriver.promote(); if (driver != NULL) { - driver->notifyPrepareCompleted(err); - } - - int64_t durationUs; - if (mDriver != NULL && mSource->getDuration(&durationUs) == OK) { - sp<NuPlayerDriver> driver = mDriver.promote(); - if (driver != NULL) { + // notify duration first, so that it's definitely set when + // the app received the "prepare complete" callback. + int64_t durationUs; + if (mSource->getDuration(&durationUs) == OK) { driver->notifyDuration(durationUs); } + driver->notifyPrepareCompleted(err); } + break; } diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp index 469c9ca..cfbf282 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp @@ -37,6 +37,7 @@ NuPlayer::Decoder::Decoder( : mNotify(notify), mNativeWindow(nativeWindow), mBufferGeneration(0), + mPaused(true), mComponentName("decoder") { // Every decoder has its own looper because MediaCodec operations // are blocking, but NuPlayer needs asynchronous operations. @@ -112,6 +113,7 @@ void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) { mOutputBuffers.size()); requestCodecNotification(); + mPaused = false; } void NuPlayer::Decoder::requestCodecNotification() { @@ -352,6 +354,11 @@ void NuPlayer::Decoder::onFlush() { sp<AMessage> notify = mNotify->dup(); notify->setInt32("what", kWhatFlushCompleted); notify->post(); + mPaused = true; +} + +void NuPlayer::Decoder::onResume() { + mPaused = false; } void NuPlayer::Decoder::onShutdown() { @@ -380,6 +387,7 @@ void NuPlayer::Decoder::onShutdown() { sp<AMessage> notify = mNotify->dup(); notify->setInt32("what", kWhatShutdownCompleted); notify->post(); + mPaused = true; } void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) { @@ -397,7 +405,9 @@ void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) { case kWhatCodecNotify: { if (!isStaleReply(msg)) { - while (handleAnInputBuffer()) { + if (!mPaused) { + while (handleAnInputBuffer()) { + } } while (handleAnOutputBuffer()) { @@ -430,6 +440,12 @@ void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) { break; } + case kWhatResume: + { + onResume(); + break; + } + case kWhatShutdown: { onShutdown(); @@ -447,7 +463,7 @@ void NuPlayer::Decoder::signalFlush() { } void NuPlayer::Decoder::signalResume() { - // nothing to do + (new AMessage(kWhatResume, id()))->post(); } void NuPlayer::Decoder::initiateShutdown() { diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h index 94243fc..2892584 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h @@ -87,11 +87,13 @@ private: void onConfigure(const sp<AMessage> &format); void onFlush(); + void onResume(); void onInputBufferFilled(const sp<AMessage> &msg); void onRenderBuffer(const sp<AMessage> &msg); void onShutdown(); int32_t mBufferGeneration; + bool mPaused; AString mComponentName; bool supportsSeamlessAudioFormatChange(const sp<AMessage> &targetFormat) const; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp index e4850f0..280b5af 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp @@ -284,6 +284,10 @@ status_t NuPlayerDriver::seekTo(int msec) { case STATE_PREPARED: { mStartupSeekTimeUs = seekTimeUs; + // pretend that the seek completed. It will actually happen when starting playback. + // TODO: actually perform the seek here, so the player is ready to go at the new + // location + notifySeekComplete(); break; } diff --git a/media/libnbaio/NBLog.cpp b/media/libnbaio/NBLog.cpp index 4d9a1fa..4d14904 100644 --- a/media/libnbaio/NBLog.cpp +++ b/media/libnbaio/NBLog.cpp @@ -438,7 +438,7 @@ void NBLog::Reader::dump(int fd, size_t indent) void NBLog::Reader::dumpLine(const String8& timestamp, String8& body) { if (mFd >= 0) { - fdprintf(mFd, "%.*s%s %s\n", mIndent, "", timestamp.string(), body.string()); + dprintf(mFd, "%.*s%s %s\n", mIndent, "", timestamp.string(), body.string()); } else { ALOGI("%.*s%s %s", mIndent, "", timestamp.string(), body.string()); } diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index aabe0ec..297f4fc 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -2510,8 +2510,9 @@ status_t MPEG4Extractor::verifyTrack(Track *track) { } } - if (!track->sampleTable->isValid()) { + if (track->sampleTable == NULL || !track->sampleTable->isValid()) { // Make sure we have all the metadata we need. + ALOGE("stbl atom missing/invalid."); return ERROR_MALFORMED; } diff --git a/media/libstagefright/MediaBuffer.cpp b/media/libstagefright/MediaBuffer.cpp index 11b80bf..8af0880 100644 --- a/media/libstagefright/MediaBuffer.cpp +++ b/media/libstagefright/MediaBuffer.cpp @@ -27,7 +27,6 @@ #include <media/stagefright/MetaData.h> #include <ui/GraphicBuffer.h> -#include <sys/atomics.h> namespace android { @@ -92,7 +91,7 @@ void MediaBuffer::release() { return; } - int prevCount = __atomic_dec(&mRefCount); + int prevCount = __sync_fetch_and_sub(&mRefCount, 1); if (prevCount == 1) { if (mObserver == NULL) { delete this; @@ -112,7 +111,7 @@ void MediaBuffer::claim() { } void MediaBuffer::add_ref() { - (void) __atomic_inc(&mRefCount); + (void) __sync_fetch_and_add(&mRefCount, 1); } void *MediaBuffer::data() const { diff --git a/media/libstagefright/codecs/aacdec/Android.mk b/media/libstagefright/codecs/aacdec/Android.mk index 49ff238..afb00aa 100644 --- a/media/libstagefright/codecs/aacdec/Android.mk +++ b/media/libstagefright/codecs/aacdec/Android.mk @@ -3,7 +3,8 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ - SoftAAC2.cpp + SoftAAC2.cpp \ + DrcPresModeWrap.cpp LOCAL_C_INCLUDES := \ frameworks/av/media/libstagefright/include \ diff --git a/media/libstagefright/codecs/aacdec/DrcPresModeWrap.cpp b/media/libstagefright/codecs/aacdec/DrcPresModeWrap.cpp new file mode 100644 index 0000000..129ad65 --- /dev/null +++ b/media/libstagefright/codecs/aacdec/DrcPresModeWrap.cpp @@ -0,0 +1,372 @@ +/* + * Copyright (C) 2014 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 "DrcPresModeWrap.h" + +#include <assert.h> + +#define LOG_TAG "SoftAAC2_DrcWrapper" +//#define LOG_NDEBUG 0 +#include <utils/Log.h> + +//#define DRC_PRES_MODE_WRAP_DEBUG + +#define GPM_ENCODER_TARGET_LEVEL 64 +#define MAX_TARGET_LEVEL 64 + +CDrcPresModeWrapper::CDrcPresModeWrapper() +{ + mDataUpdate = true; + + /* Data from streamInfo. */ + /* Initialized to the same values as in the aac decoder */ + mStreamPRL = -1; + mStreamDRCPresMode = -1; + mStreamNrAACChan = 0; + mStreamNrOutChan = 0; + + /* Desired values (set by user). */ + /* Initialized to the same values as in the aac decoder */ + mDesTarget = -1; + mDesAttFactor = 0; + mDesBoostFactor = 0; + mDesHeavy = 0; + + mEncoderTarget = -1; + + /* Values from last time. */ + /* Initialized to the same values as the desired values */ + mLastTarget = -1; + mLastAttFactor = 0; + mLastBoostFactor = 0; + mLastHeavy = 0; +} + +CDrcPresModeWrapper::~CDrcPresModeWrapper() +{ +} + +void +CDrcPresModeWrapper::setDecoderHandle(const HANDLE_AACDECODER handle) +{ + mHandleDecoder = handle; +} + +void +CDrcPresModeWrapper::submitStreamData(CStreamInfo* pStreamInfo) +{ + assert(pStreamInfo); + + if (mStreamPRL != pStreamInfo->drcProgRefLev) { + mStreamPRL = pStreamInfo->drcProgRefLev; + mDataUpdate = true; +#ifdef DRC_PRES_MODE_WRAP_DEBUG + ALOGV("DRC presentation mode wrapper: drcProgRefLev is %d\n", mStreamPRL); +#endif + } + + if (mStreamDRCPresMode != pStreamInfo->drcPresMode) { + mStreamDRCPresMode = pStreamInfo->drcPresMode; + mDataUpdate = true; +#ifdef DRC_PRES_MODE_WRAP_DEBUG + ALOGV("DRC presentation mode wrapper: drcPresMode is %d\n", mStreamDRCPresMode); +#endif + } + + if (mStreamNrAACChan != pStreamInfo->aacNumChannels) { + mStreamNrAACChan = pStreamInfo->aacNumChannels; + mDataUpdate = true; +#ifdef DRC_PRES_MODE_WRAP_DEBUG + ALOGV("DRC presentation mode wrapper: aacNumChannels is %d\n", mStreamNrAACChan); +#endif + } + + if (mStreamNrOutChan != pStreamInfo->numChannels) { + mStreamNrOutChan = pStreamInfo->numChannels; + mDataUpdate = true; +#ifdef DRC_PRES_MODE_WRAP_DEBUG + ALOGV("DRC presentation mode wrapper: numChannels is %d\n", mStreamNrOutChan); +#endif + } + + + + if (mStreamNrOutChan<mStreamNrAACChan) { + mIsDownmix = true; + } else { + mIsDownmix = false; + } + + if (mIsDownmix && (mStreamNrOutChan == 1)) { + mIsMonoDownmix = true; + } else { + mIsMonoDownmix = false; + } + + if (mIsDownmix && mStreamNrOutChan == 2){ + mIsStereoDownmix = true; + } else { + mIsStereoDownmix = false; + } + +} + +void +CDrcPresModeWrapper::setParam(const DRC_PRES_MODE_WRAP_PARAM param, const int value) +{ + switch (param) { + case DRC_PRES_MODE_WRAP_DESIRED_TARGET: + mDesTarget = value; + break; + case DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR: + mDesAttFactor = value; + break; + case DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR: + mDesBoostFactor = value; + break; + case DRC_PRES_MODE_WRAP_DESIRED_HEAVY: + mDesHeavy = value; + break; + case DRC_PRES_MODE_WRAP_ENCODER_TARGET: + mEncoderTarget = value; + break; + default: + break; + } + mDataUpdate = true; +} + +void +CDrcPresModeWrapper::update() +{ + // Get Data from Decoder + int progRefLevel = mStreamPRL; + int drcPresMode = mStreamDRCPresMode; + + // by default, do as desired + int newTarget = mDesTarget; + int newAttFactor = mDesAttFactor; + int newBoostFactor = mDesBoostFactor; + int newHeavy = mDesHeavy; + + if (mDataUpdate) { + // sanity check + if (mDesTarget < MAX_TARGET_LEVEL){ + mDesTarget = MAX_TARGET_LEVEL; // limit target level to -16 dB or below + newTarget = MAX_TARGET_LEVEL; + } + + if (mEncoderTarget != -1) { + if (mDesTarget<124) { // if target level > -31 dB + if ((mIsStereoDownmix == false) && (mIsMonoDownmix == false)) { + // no stereo or mono downmixing, calculated scaling of light DRC + /* use as little compression as possible */ + newAttFactor = 0; + newBoostFactor = 0; + if (mDesTarget<progRefLevel) { // if target level > PRL + if (mEncoderTarget < mDesTarget) { // if mEncoderTarget > target level + // mEncoderTarget > target level > PRL + int calcFactor; + float calcFactor_norm; + // 0.0f < calcFactor_norm < 1.0f + calcFactor_norm = (float)(mDesTarget - progRefLevel) / + (float)(mEncoderTarget - progRefLevel); + calcFactor = (int)(calcFactor_norm*127.0f); // 0 <= calcFactor < 127 + // calcFactor is the lower limit + newAttFactor = (calcFactor>newAttFactor) ? calcFactor : newAttFactor; + // new AttFactor will be always = calcFactor, as it is set to 0 before. + newBoostFactor = newAttFactor; + } else { + /* target level > mEncoderTarget > PRL */ + // newTDLimiterEnable = 1; + // the time domain limiter must always be active in this case. + // It is assumed that the framework activates it by default + newAttFactor = 127; + newBoostFactor = 127; + } + } else { // target level <= PRL + // no restrictions required + // newAttFactor = newAttFactor; + } + } else { // downmixing + // if target level > -23 dB or mono downmix + if ( (mDesTarget<92) || mIsMonoDownmix ) { + newHeavy = 1; + } else { + // we perform a downmix, so, we need at least full light DRC + newAttFactor = 127; + } + } + } else { // target level <= -31 dB + // playback -31 dB: light DRC only needed if we perform downmixing + if (mIsDownmix) { // we do downmixing + newAttFactor = 127; + } + } + } + else { // handle other used encoder target levels + + // Sanity check: DRC presentation mode is only specified for max. 5.1 channels + if (mStreamNrAACChan > 6) { + drcPresMode = 0; + } + + switch (drcPresMode) { + case 0: + default: // presentation mode not indicated + { + + if (mDesTarget<124) { // if target level > -31 dB + // no stereo or mono downmixing + if ((mIsStereoDownmix == false) && (mIsMonoDownmix == false)) { + if (mDesTarget<progRefLevel) { // if target level > PRL + // newTDLimiterEnable = 1; + // the time domain limiter must always be active in this case. + // It is assumed that the framework activates it by default + newAttFactor = 127; // at least, use light compression + } else { // target level <= PRL + // no restrictions required + // newAttFactor = newAttFactor; + } + } else { // downmixing + // newTDLimiterEnable = 1; + // the time domain limiter must always be active in this case. + // It is assumed that the framework activates it by default + + // if target level > -23 dB or mono downmix + if ( (mDesTarget < 92) || mIsMonoDownmix ) { + newHeavy = 1; + } else{ + // we perform a downmix, so, we need at least full light DRC + newAttFactor = 127; + } + } + } else { // target level <= -31 dB + if (mIsDownmix) { // we do downmixing. + // newTDLimiterEnable = 1; + // the time domain limiter must always be active in this case. + // It is assumed that the framework activates it by default + newAttFactor = 127; + } + } + } + break; + + // Presentation mode 1 and 2 according to ETSI TS 101 154: + // Digital Video Broadcasting (DVB); Specification for the use of Video and Audio Coding + // in Broadcasting Applications based on the MPEG-2 Transport Stream, + // section C.5.4., "Decoding", and Table C.33 + // ISO DRC -> newHeavy = 0 (Use light compression, MPEG-style) + // Compression_value -> newHeavy = 1 (Use heavy compression, DVB-style) + // scaling restricted -> newAttFactor = 127 + + case 1: // presentation mode 1, Light:-31/Heavy:-23 + { + if (mDesTarget < 124) { // if target level > -31 dB + // playback up to -23 dB + newHeavy = 1; + } else { // target level <= -31 dB + // playback -31 dB + if (mIsDownmix) { // we do downmixing. + newAttFactor = 127; + } + } + } + break; + + case 2: // presentation mode 2, Light:-23/Heavy:-23 + { + if (mDesTarget < 124) { // if target level > -31 dB + // playback up to -23 dB + if (mIsMonoDownmix) { // if mono downmix + newHeavy = 1; + } else { + newHeavy = 0; + newAttFactor = 127; + } + } else { // target level <= -31 dB + // playback -31 dB + newHeavy = 0; + if (mIsDownmix) { // we do downmixing. + newAttFactor = 127; + } + } + } + break; + + } // switch() + } // if (mEncoderTarget == GPM_ENCODER_TARGET_LEVEL) + + // sanity again + if (newHeavy == 1) { + newBoostFactor=127; // not really needed as the same would be done by the decoder anyway + newAttFactor = 127; + } + + // update the decoder + if (newTarget != mLastTarget) { + aacDecoder_SetParam(mHandleDecoder, AAC_DRC_REFERENCE_LEVEL, newTarget); + mLastTarget = newTarget; +#ifdef DRC_PRES_MODE_WRAP_DEBUG + if (newTarget != mDesTarget) + ALOGV("DRC presentation mode wrapper: forced target level to %d (from %d)\n", newTarget, mDesTarget); + else + ALOGV("DRC presentation mode wrapper: set target level to %d\n", newTarget); +#endif + } + + if (newAttFactor != mLastAttFactor) { + aacDecoder_SetParam(mHandleDecoder, AAC_DRC_ATTENUATION_FACTOR, newAttFactor); + mLastAttFactor = newAttFactor; +#ifdef DRC_PRES_MODE_WRAP_DEBUG + if (newAttFactor != mDesAttFactor) + ALOGV("DRC presentation mode wrapper: forced attenuation factor to %d (from %d)\n", newAttFactor, mDesAttFactor); + else + ALOGV("DRC presentation mode wrapper: set attenuation factor to %d\n", newAttFactor); +#endif + } + + if (newBoostFactor != mLastBoostFactor) { + aacDecoder_SetParam(mHandleDecoder, AAC_DRC_BOOST_FACTOR, newBoostFactor); + mLastBoostFactor = newBoostFactor; +#ifdef DRC_PRES_MODE_WRAP_DEBUG + if (newBoostFactor != mDesBoostFactor) + ALOGV("DRC presentation mode wrapper: forced boost factor to %d (from %d)\n", + newBoostFactor, mDesBoostFactor); + else + ALOGV("DRC presentation mode wrapper: set boost factor to %d\n", newBoostFactor); +#endif + } + + if (newHeavy != mLastHeavy) { + aacDecoder_SetParam(mHandleDecoder, AAC_DRC_HEAVY_COMPRESSION, newHeavy); + mLastHeavy = newHeavy; +#ifdef DRC_PRES_MODE_WRAP_DEBUG + if (newHeavy != mDesHeavy) + ALOGV("DRC presentation mode wrapper: forced heavy compression to %d (from %d)\n", + newHeavy, mDesHeavy); + else + ALOGV("DRC presentation mode wrapper: set heavy compression to %d\n", newHeavy); +#endif + } + +#ifdef DRC_PRES_MODE_WRAP_DEBUG + ALOGV("DRC config: tgt_lev: %3d, cut: %3d, boost: %3d, heavy: %d\n", newTarget, + newAttFactor, newBoostFactor, newHeavy); +#endif + mDataUpdate = false; + + } // if (mDataUpdate) +} diff --git a/media/libstagefright/codecs/aacdec/DrcPresModeWrap.h b/media/libstagefright/codecs/aacdec/DrcPresModeWrap.h new file mode 100644 index 0000000..f0b6cf2 --- /dev/null +++ b/media/libstagefright/codecs/aacdec/DrcPresModeWrap.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2014 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 "aacdecoder_lib.h" + +typedef enum +{ + DRC_PRES_MODE_WRAP_DESIRED_TARGET = 0x0000, + DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR = 0x0001, + DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR = 0x0002, + DRC_PRES_MODE_WRAP_DESIRED_HEAVY = 0x0003, + DRC_PRES_MODE_WRAP_ENCODER_TARGET = 0x0004 +} DRC_PRES_MODE_WRAP_PARAM; + + +class CDrcPresModeWrapper { +public: + CDrcPresModeWrapper(); + ~CDrcPresModeWrapper(); + void setDecoderHandle(const HANDLE_AACDECODER handle); + void setParam(const DRC_PRES_MODE_WRAP_PARAM param, const int value); + void submitStreamData(CStreamInfo*); + void update(); + +protected: + HANDLE_AACDECODER mHandleDecoder; + int mDesTarget; + int mDesAttFactor; + int mDesBoostFactor; + int mDesHeavy; + + int mEncoderTarget; + + int mLastTarget; + int mLastAttFactor; + int mLastBoostFactor; + int mLastHeavy; + + SCHAR mStreamPRL; + SCHAR mStreamDRCPresMode; + INT mStreamNrAACChan; + INT mStreamNrOutChan; + + bool mIsDownmix; + bool mIsMonoDownmix; + bool mIsStereoDownmix; + + bool mDataUpdate; +}; diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp index 532e36f..64bf2b6 100644 --- a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp +++ b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp @@ -25,16 +25,22 @@ #include <media/stagefright/foundation/hexdump.h> #include <media/stagefright/MediaErrors.h> +#include <math.h> + #define FILEREAD_MAX_LAYERS 2 #define DRC_DEFAULT_MOBILE_REF_LEVEL 64 /* 64*-0.25dB = -16 dB below full scale for mobile conf */ #define DRC_DEFAULT_MOBILE_DRC_CUT 127 /* maximum compression of dynamic range for mobile conf */ #define DRC_DEFAULT_MOBILE_DRC_BOOST 127 /* maximum compression of dynamic range for mobile conf */ +#define DRC_DEFAULT_MOBILE_DRC_HEAVY 1 /* switch for heavy compression for mobile conf */ +#define DRC_DEFAULT_MOBILE_ENC_LEVEL -1 /* encoder target level; -1 => the value is unknown, otherwise dB step value (e.g. 64 for -16 dB) */ #define MAX_CHANNEL_COUNT 8 /* maximum number of audio channels that can be decoded */ // names of properties that can be used to override the default DRC settings #define PROP_DRC_OVERRIDE_REF_LEVEL "aac_drc_reference_level" #define PROP_DRC_OVERRIDE_CUT "aac_drc_cut" #define PROP_DRC_OVERRIDE_BOOST "aac_drc_boost" +#define PROP_DRC_OVERRIDE_HEAVY "aac_drc_heavy" +#define PROP_DRC_OVERRIDE_ENC_LEVEL "aac_drc_enc_target_level" namespace android { @@ -57,18 +63,19 @@ SoftAAC2::SoftAAC2( mStreamInfo(NULL), mIsADTS(false), mInputBufferCount(0), + mOutputBufferCount(0), mSignalledError(false), - mSawInputEos(false), - mSignalledOutputEos(false), - mAnchorTimeUs(0), - mNumSamplesOutput(0), mOutputPortSettingsChange(NONE) { + for (unsigned int i = 0; i < kNumDelayBlocksMax; i++) { + mAnchorTimeUs[i] = 0; + } initPorts(); CHECK_EQ(initDecoder(), (status_t)OK); } SoftAAC2::~SoftAAC2() { aacDecoder_Close(mAACDecoder); + delete mOutputDelayRingBuffer; } void SoftAAC2::initPorts() { @@ -121,36 +128,72 @@ status_t SoftAAC2::initDecoder() { status = OK; } } - mDecoderHasData = false; - // for streams that contain metadata, use the mobile profile DRC settings unless overridden - // by platform properties: + mEndOfInput = false; + mEndOfOutput = false; + mOutputDelayCompensated = 0; + mOutputDelayRingBufferSize = 2048 * MAX_CHANNEL_COUNT * kNumDelayBlocksMax; + mOutputDelayRingBuffer = new short[mOutputDelayRingBufferSize]; + mOutputDelayRingBufferWritePos = 0; + mOutputDelayRingBufferReadPos = 0; + + if (mAACDecoder == NULL) { + ALOGE("AAC decoder is null. TODO: Can not call aacDecoder_SetParam in the following code"); + } + + //aacDecoder_SetParam(mAACDecoder, AAC_PCM_LIMITER_ENABLE, 0); + + //init DRC wrapper + mDrcWrap.setDecoderHandle(mAACDecoder); + mDrcWrap.submitStreamData(mStreamInfo); + + // for streams that contain metadata, use the mobile profile DRC settings unless overridden by platform properties + // TODO: change the DRC settings depending on audio output device type (HDMI, loadspeaker, headphone) char value[PROPERTY_VALUE_MAX]; - // * AAC_DRC_REFERENCE_LEVEL + // DRC_PRES_MODE_WRAP_DESIRED_TARGET if (property_get(PROP_DRC_OVERRIDE_REF_LEVEL, value, NULL)) { unsigned refLevel = atoi(value); - ALOGV("AAC decoder using AAC_DRC_REFERENCE_LEVEL of %d instead of %d", - refLevel, DRC_DEFAULT_MOBILE_REF_LEVEL); - aacDecoder_SetParam(mAACDecoder, AAC_DRC_REFERENCE_LEVEL, refLevel); + ALOGV("AAC decoder using desired DRC target reference level of %d instead of %d", refLevel, + DRC_DEFAULT_MOBILE_REF_LEVEL); + mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_TARGET, refLevel); } else { - aacDecoder_SetParam(mAACDecoder, AAC_DRC_REFERENCE_LEVEL, DRC_DEFAULT_MOBILE_REF_LEVEL); + mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_TARGET, DRC_DEFAULT_MOBILE_REF_LEVEL); } - // * AAC_DRC_ATTENUATION_FACTOR + // DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR if (property_get(PROP_DRC_OVERRIDE_CUT, value, NULL)) { unsigned cut = atoi(value); - ALOGV("AAC decoder using AAC_DRC_ATTENUATION_FACTOR of %d instead of %d", - cut, DRC_DEFAULT_MOBILE_DRC_CUT); - aacDecoder_SetParam(mAACDecoder, AAC_DRC_ATTENUATION_FACTOR, cut); + ALOGV("AAC decoder using desired DRC attenuation factor of %d instead of %d", cut, + DRC_DEFAULT_MOBILE_DRC_CUT); + mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR, cut); } else { - aacDecoder_SetParam(mAACDecoder, AAC_DRC_ATTENUATION_FACTOR, DRC_DEFAULT_MOBILE_DRC_CUT); + mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR, DRC_DEFAULT_MOBILE_DRC_CUT); } - // * AAC_DRC_BOOST_FACTOR (note: no default, using cut) + // DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR if (property_get(PROP_DRC_OVERRIDE_BOOST, value, NULL)) { unsigned boost = atoi(value); - ALOGV("AAC decoder using AAC_DRC_BOOST_FACTOR of %d", boost); - aacDecoder_SetParam(mAACDecoder, AAC_DRC_BOOST_FACTOR, boost); + ALOGV("AAC decoder using desired DRC boost factor of %d instead of %d", boost, + DRC_DEFAULT_MOBILE_DRC_BOOST); + mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR, boost); + } else { + mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR, DRC_DEFAULT_MOBILE_DRC_BOOST); + } + // DRC_PRES_MODE_WRAP_DESIRED_HEAVY + if (property_get(PROP_DRC_OVERRIDE_HEAVY, value, NULL)) { + unsigned heavy = atoi(value); + ALOGV("AAC decoder using desried DRC heavy compression switch of %d instead of %d", heavy, + DRC_DEFAULT_MOBILE_DRC_HEAVY); + mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_HEAVY, heavy); } else { - aacDecoder_SetParam(mAACDecoder, AAC_DRC_BOOST_FACTOR, DRC_DEFAULT_MOBILE_DRC_BOOST); + mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_HEAVY, DRC_DEFAULT_MOBILE_DRC_HEAVY); + } + // DRC_PRES_MODE_WRAP_ENCODER_TARGET + if (property_get(PROP_DRC_OVERRIDE_ENC_LEVEL, value, NULL)) { + unsigned encoderRefLevel = atoi(value); + ALOGV("AAC decoder using encoder-side DRC reference level of %d instead of %d", + encoderRefLevel, DRC_DEFAULT_MOBILE_ENC_LEVEL); + mDrcWrap.setParam(DRC_PRES_MODE_WRAP_ENCODER_TARGET, encoderRefLevel); + } else { + mDrcWrap.setParam(DRC_PRES_MODE_WRAP_ENCODER_TARGET, DRC_DEFAULT_MOBILE_ENC_LEVEL); } return status; @@ -290,19 +333,101 @@ bool SoftAAC2::isConfigured() const { return mInputBufferCount > 0; } -void SoftAAC2::maybeConfigureDownmix() const { - if (mStreamInfo->numChannels > 2) { - char value[PROPERTY_VALUE_MAX]; - if (!(property_get("media.aac_51_output_enabled", value, NULL) && - (!strcmp(value, "1") || !strcasecmp(value, "true")))) { - ALOGI("Downmixing multichannel AAC to stereo"); - aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, 2); - mStreamInfo->numChannels = 2; - // By default, the decoder creates a 5.1 channel downmix signal - // for seven and eight channel input streams. To enable 6.1 and 7.1 channel output - // use aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, -1) +void SoftAAC2::configureDownmix() const { + char value[PROPERTY_VALUE_MAX]; + if (!(property_get("media.aac_51_output_enabled", value, NULL) + && (!strcmp(value, "1") || !strcasecmp(value, "true")))) { + ALOGI("limiting to stereo output"); + aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, 2); + // By default, the decoder creates a 5.1 channel downmix signal + // for seven and eight channel input streams. To enable 6.1 and 7.1 channel output + // use aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, -1) + } +} + +bool SoftAAC2::outputDelayRingBufferPutSamples(INT_PCM *samples, int32_t numSamples) { + if (mOutputDelayRingBufferWritePos + numSamples <= mOutputDelayRingBufferSize + && (mOutputDelayRingBufferReadPos <= mOutputDelayRingBufferWritePos + || mOutputDelayRingBufferReadPos > mOutputDelayRingBufferWritePos + numSamples)) { + // faster memcopy loop without checks, if the preconditions allow this + for (int32_t i = 0; i < numSamples; i++) { + mOutputDelayRingBuffer[mOutputDelayRingBufferWritePos++] = samples[i]; + } + + if (mOutputDelayRingBufferWritePos >= mOutputDelayRingBufferSize) { + mOutputDelayRingBufferWritePos -= mOutputDelayRingBufferSize; + } + if (mOutputDelayRingBufferWritePos == mOutputDelayRingBufferReadPos) { + ALOGE("RING BUFFER OVERFLOW"); + return false; + } + } else { + ALOGV("slow SoftAAC2::outputDelayRingBufferPutSamples()"); + + for (int32_t i = 0; i < numSamples; i++) { + mOutputDelayRingBuffer[mOutputDelayRingBufferWritePos] = samples[i]; + mOutputDelayRingBufferWritePos++; + if (mOutputDelayRingBufferWritePos >= mOutputDelayRingBufferSize) { + mOutputDelayRingBufferWritePos -= mOutputDelayRingBufferSize; + } + if (mOutputDelayRingBufferWritePos == mOutputDelayRingBufferReadPos) { + ALOGE("RING BUFFER OVERFLOW"); + return false; + } } } + return true; +} + +int32_t SoftAAC2::outputDelayRingBufferGetSamples(INT_PCM *samples, int32_t numSamples) { + if (mOutputDelayRingBufferReadPos + numSamples <= mOutputDelayRingBufferSize + && (mOutputDelayRingBufferWritePos < mOutputDelayRingBufferReadPos + || mOutputDelayRingBufferWritePos >= mOutputDelayRingBufferReadPos + numSamples)) { + // faster memcopy loop without checks, if the preconditions allow this + if (samples != 0) { + for (int32_t i = 0; i < numSamples; i++) { + samples[i] = mOutputDelayRingBuffer[mOutputDelayRingBufferReadPos++]; + } + } else { + mOutputDelayRingBufferReadPos += numSamples; + } + if (mOutputDelayRingBufferReadPos >= mOutputDelayRingBufferSize) { + mOutputDelayRingBufferReadPos -= mOutputDelayRingBufferSize; + } + } else { + ALOGV("slow SoftAAC2::outputDelayRingBufferGetSamples()"); + + for (int32_t i = 0; i < numSamples; i++) { + if (mOutputDelayRingBufferWritePos == mOutputDelayRingBufferReadPos) { + ALOGE("RING BUFFER UNDERRUN"); + return -1; + } + if (samples != 0) { + samples[i] = mOutputDelayRingBuffer[mOutputDelayRingBufferReadPos]; + } + mOutputDelayRingBufferReadPos++; + if (mOutputDelayRingBufferReadPos >= mOutputDelayRingBufferSize) { + mOutputDelayRingBufferReadPos -= mOutputDelayRingBufferSize; + } + } + } + return numSamples; +} + +int32_t SoftAAC2::outputDelayRingBufferSamplesAvailable() { + int32_t available = mOutputDelayRingBufferWritePos - mOutputDelayRingBufferReadPos; + if (available < 0) { + available += mOutputDelayRingBufferSize; + } + if (available < 0) { + ALOGE("FATAL RING BUFFER ERROR"); + return 0; + } + return available; +} + +int32_t SoftAAC2::outputDelayRingBufferSamplesLeft() { + return mOutputDelayRingBufferSize - outputDelayRingBufferSamplesAvailable(); } void SoftAAC2::onQueueFilled(OMX_U32 portIndex) { @@ -318,12 +443,11 @@ void SoftAAC2::onQueueFilled(OMX_U32 portIndex) { List<BufferInfo *> &outQueue = getPortQueue(1); if (portIndex == 0 && mInputBufferCount == 0) { - ++mInputBufferCount; - BufferInfo *info = *inQueue.begin(); - OMX_BUFFERHEADERTYPE *header = info->mHeader; + BufferInfo *inInfo = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; - inBuffer[0] = header->pBuffer + header->nOffset; - inBufferLength[0] = header->nFilledLen; + inBuffer[0] = inHeader->pBuffer + inHeader->nOffset; + inBufferLength[0] = inHeader->nFilledLen; AAC_DECODER_ERROR decoderErr = aacDecoder_ConfigRaw(mAACDecoder, @@ -331,19 +455,25 @@ void SoftAAC2::onQueueFilled(OMX_U32 portIndex) { inBufferLength); if (decoderErr != AAC_DEC_OK) { + ALOGW("aacDecoder_ConfigRaw decoderErr = 0x%4.4x", decoderErr); mSignalledError = true; notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL); return; } + mInputBufferCount++; + mOutputBufferCount++; // fake increase of outputBufferCount to keep the counters aligned + + inInfo->mOwnedByUs = false; inQueue.erase(inQueue.begin()); - info->mOwnedByUs = false; - notifyEmptyBufferDone(header); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + configureDownmix(); // Only send out port settings changed event if both sample rate // and numChannels are valid. if (mStreamInfo->sampleRate && mStreamInfo->numChannels) { - maybeConfigureDownmix(); ALOGI("Initially configuring decoder: %d Hz, %d channels", mStreamInfo->sampleRate, mStreamInfo->numChannels); @@ -355,202 +485,304 @@ void SoftAAC2::onQueueFilled(OMX_U32 portIndex) { return; } - while ((!inQueue.empty() || (mSawInputEos && !mSignalledOutputEos)) && !outQueue.empty()) { - BufferInfo *inInfo = NULL; - OMX_BUFFERHEADERTYPE *inHeader = NULL; + while ((!inQueue.empty() || mEndOfInput) && !outQueue.empty()) { if (!inQueue.empty()) { - inInfo = *inQueue.begin(); - inHeader = inInfo->mHeader; - } + INT_PCM tmpOutBuffer[2048 * MAX_CHANNEL_COUNT]; + BufferInfo *inInfo = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; - BufferInfo *outInfo = *outQueue.begin(); - OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; - outHeader->nFlags = 0; - - if (inHeader) { if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { - mSawInputEos = true; + mEndOfInput = true; + } else { + mEndOfInput = false; } - - if (inHeader->nOffset == 0 && inHeader->nFilledLen) { - mAnchorTimeUs = inHeader->nTimeStamp; - mNumSamplesOutput = 0; + if (inHeader->nOffset == 0) { // TODO: does nOffset != 0 happen? + mAnchorTimeUs[mInputBufferCount % kNumDelayBlocksMax] = + inHeader->nTimeStamp; } - if (mIsADTS && inHeader->nFilledLen) { - size_t adtsHeaderSize = 0; - // skip 30 bits, aac_frame_length follows. - // ssssssss ssssiiip ppffffPc ccohCCll llllllll lll????? + if (inHeader->nFilledLen == 0) { + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + } else { + if (mIsADTS) { + size_t adtsHeaderSize = 0; + // skip 30 bits, aac_frame_length follows. + // ssssssss ssssiiip ppffffPc ccohCCll llllllll lll????? - const uint8_t *adtsHeader = inHeader->pBuffer + inHeader->nOffset; + const uint8_t *adtsHeader = inHeader->pBuffer + inHeader->nOffset; - bool signalError = false; - if (inHeader->nFilledLen < 7) { - ALOGE("Audio data too short to contain even the ADTS header. " - "Got %d bytes.", inHeader->nFilledLen); - hexdump(adtsHeader, inHeader->nFilledLen); - signalError = true; - } else { - bool protectionAbsent = (adtsHeader[1] & 1); - - unsigned aac_frame_length = - ((adtsHeader[3] & 3) << 11) - | (adtsHeader[4] << 3) - | (adtsHeader[5] >> 5); - - if (inHeader->nFilledLen < aac_frame_length) { - ALOGE("Not enough audio data for the complete frame. " - "Got %d bytes, frame size according to the ADTS " - "header is %u bytes.", - inHeader->nFilledLen, aac_frame_length); + bool signalError = false; + if (inHeader->nFilledLen < 7) { + ALOGE("Audio data too short to contain even the ADTS header. " + "Got %d bytes.", inHeader->nFilledLen); hexdump(adtsHeader, inHeader->nFilledLen); signalError = true; } else { - adtsHeaderSize = (protectionAbsent ? 7 : 9); + bool protectionAbsent = (adtsHeader[1] & 1); + + unsigned aac_frame_length = + ((adtsHeader[3] & 3) << 11) + | (adtsHeader[4] << 3) + | (adtsHeader[5] >> 5); + + if (inHeader->nFilledLen < aac_frame_length) { + ALOGE("Not enough audio data for the complete frame. " + "Got %d bytes, frame size according to the ADTS " + "header is %u bytes.", + inHeader->nFilledLen, aac_frame_length); + hexdump(adtsHeader, inHeader->nFilledLen); + signalError = true; + } else { + adtsHeaderSize = (protectionAbsent ? 7 : 9); + + inBuffer[0] = (UCHAR *)adtsHeader + adtsHeaderSize; + inBufferLength[0] = aac_frame_length - adtsHeaderSize; + + inHeader->nOffset += adtsHeaderSize; + inHeader->nFilledLen -= adtsHeaderSize; + } + } + + if (signalError) { + mSignalledError = true; - inBuffer[0] = (UCHAR *)adtsHeader + adtsHeaderSize; - inBufferLength[0] = aac_frame_length - adtsHeaderSize; + notify(OMX_EventError, + OMX_ErrorStreamCorrupt, + ERROR_MALFORMED, + NULL); - inHeader->nOffset += adtsHeaderSize; - inHeader->nFilledLen -= adtsHeaderSize; + return; } + } else { + inBuffer[0] = inHeader->pBuffer + inHeader->nOffset; + inBufferLength[0] = inHeader->nFilledLen; } - if (signalError) { - mSignalledError = true; + // Fill and decode + bytesValid[0] = inBufferLength[0]; + + INT prevSampleRate = mStreamInfo->sampleRate; + INT prevNumChannels = mStreamInfo->numChannels; + + aacDecoder_Fill(mAACDecoder, + inBuffer, + inBufferLength, + bytesValid); - notify(OMX_EventError, - OMX_ErrorStreamCorrupt, - ERROR_MALFORMED, - NULL); + // run DRC check + mDrcWrap.submitStreamData(mStreamInfo); + mDrcWrap.update(); + AAC_DECODER_ERROR decoderErr = + aacDecoder_DecodeFrame(mAACDecoder, + tmpOutBuffer, + 2048 * MAX_CHANNEL_COUNT, + 0 /* flags */); + + if (decoderErr != AAC_DEC_OK) { + ALOGW("aacDecoder_DecodeFrame decoderErr = 0x%4.4x", decoderErr); + } + + if (decoderErr == AAC_DEC_NOT_ENOUGH_BITS) { + ALOGE("AAC_DEC_NOT_ENOUGH_BITS should never happen"); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); return; } - } else { - inBuffer[0] = inHeader->pBuffer + inHeader->nOffset; - inBufferLength[0] = inHeader->nFilledLen; - } - } else { - inBufferLength[0] = 0; - } - // Fill and decode - INT_PCM *outBuffer = reinterpret_cast<INT_PCM *>( - outHeader->pBuffer + outHeader->nOffset); - - bytesValid[0] = inBufferLength[0]; - - int prevSampleRate = mStreamInfo->sampleRate; - int prevNumChannels = mStreamInfo->numChannels; - - AAC_DECODER_ERROR decoderErr = AAC_DEC_NOT_ENOUGH_BITS; - while ((bytesValid[0] > 0 || mSawInputEos) && decoderErr == AAC_DEC_NOT_ENOUGH_BITS) { - mDecoderHasData |= (bytesValid[0] > 0); - aacDecoder_Fill(mAACDecoder, - inBuffer, - inBufferLength, - bytesValid); - - decoderErr = aacDecoder_DecodeFrame(mAACDecoder, - outBuffer, - outHeader->nAllocLen, - 0 /* flags */); - if (decoderErr == AAC_DEC_NOT_ENOUGH_BITS) { - if (mSawInputEos && bytesValid[0] <= 0) { - if (mDecoderHasData) { - // flush out the decoder's delayed data by calling DecodeFrame - // one more time, with the AACDEC_FLUSH flag set - decoderErr = aacDecoder_DecodeFrame(mAACDecoder, - outBuffer, - outHeader->nAllocLen, - AACDEC_FLUSH); - mDecoderHasData = false; + if (bytesValid[0] != 0) { + ALOGE("bytesValid[0] != 0 should never happen"); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + return; + } + + size_t numOutBytes = + mStreamInfo->frameSize * sizeof(int16_t) * mStreamInfo->numChannels; + + if (decoderErr == AAC_DEC_OK) { + if (!outputDelayRingBufferPutSamples(tmpOutBuffer, + mStreamInfo->frameSize * mStreamInfo->numChannels)) { + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL); + return; } - outHeader->nFlags = OMX_BUFFERFLAG_EOS; - mSignalledOutputEos = true; - break; + UINT inBufferUsedLength = inBufferLength[0] - bytesValid[0]; + inHeader->nFilledLen -= inBufferUsedLength; + inHeader->nOffset += inBufferUsedLength; } else { - ALOGW("Not enough bits, bytesValid %d", bytesValid[0]); - } - } - } + ALOGW("AAC decoder returned error 0x%4.4x, substituting silence", decoderErr); - size_t numOutBytes = - mStreamInfo->frameSize * sizeof(int16_t) * mStreamInfo->numChannels; + memset(tmpOutBuffer, 0, numOutBytes); // TODO: check for overflow - if (inHeader) { - if (decoderErr == AAC_DEC_OK) { - UINT inBufferUsedLength = inBufferLength[0] - bytesValid[0]; - inHeader->nFilledLen -= inBufferUsedLength; - inHeader->nOffset += inBufferUsedLength; - } else { - ALOGW("AAC decoder returned error %d, substituting silence", - decoderErr); + if (!outputDelayRingBufferPutSamples(tmpOutBuffer, + mStreamInfo->frameSize * mStreamInfo->numChannels)) { + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL); + return; + } - memset(outHeader->pBuffer + outHeader->nOffset, 0, numOutBytes); + // Discard input buffer. + inHeader->nFilledLen = 0; - // Discard input buffer. - inHeader->nFilledLen = 0; + aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1); - aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1); + // fall through + } - // fall through + /* + * AAC+/eAAC+ streams can be signalled in two ways: either explicitly + * or implicitly, according to MPEG4 spec. AAC+/eAAC+ is a dual + * rate system and the sampling rate in the final output is actually + * doubled compared with the core AAC decoder sampling rate. + * + * Explicit signalling is done by explicitly defining SBR audio object + * type in the bitstream. Implicit signalling is done by embedding + * SBR content in AAC extension payload specific to SBR, and hence + * requires an AAC decoder to perform pre-checks on actual audio frames. + * + * Thus, we could not say for sure whether a stream is + * AAC+/eAAC+ until the first data frame is decoded. + */ + if (mOutputBufferCount > 1) { + if (mStreamInfo->sampleRate != prevSampleRate || + mStreamInfo->numChannels != prevNumChannels) { + ALOGE("can not reconfigure AAC output"); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL); + return; + } + } + if (mInputBufferCount <= 2) { // TODO: <= 1 + if (mStreamInfo->sampleRate != prevSampleRate || + mStreamInfo->numChannels != prevNumChannels) { + ALOGI("Reconfiguring decoder: %d->%d Hz, %d->%d channels", + prevSampleRate, mStreamInfo->sampleRate, + prevNumChannels, mStreamInfo->numChannels); + + notify(OMX_EventPortSettingsChanged, 1, 0, NULL); + mOutputPortSettingsChange = AWAITING_DISABLED; + + if (inHeader->nFilledLen == 0) { + inInfo->mOwnedByUs = false; + mInputBufferCount++; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + } + return; + } + } else if (!mStreamInfo->sampleRate || !mStreamInfo->numChannels) { + ALOGW("Invalid AAC stream"); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL); + return; + } + if (inHeader->nFilledLen == 0) { + inInfo->mOwnedByUs = false; + mInputBufferCount++; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + } else { + ALOGW("inHeader->nFilledLen = %d", inHeader->nFilledLen); + } } + } - if (inHeader->nFilledLen == 0) { - inInfo->mOwnedByUs = false; - inQueue.erase(inQueue.begin()); - inInfo = NULL; - notifyEmptyBufferDone(inHeader); - inHeader = NULL; + int32_t outputDelay = mStreamInfo->outputDelay * mStreamInfo->numChannels; + + if (!mEndOfInput && mOutputDelayCompensated < outputDelay) { + // discard outputDelay at the beginning + int32_t toCompensate = outputDelay - mOutputDelayCompensated; + int32_t discard = outputDelayRingBufferSamplesAvailable(); + if (discard > toCompensate) { + discard = toCompensate; } + int32_t discarded = outputDelayRingBufferGetSamples(0, discard); + mOutputDelayCompensated += discarded; + continue; } - /* - * AAC+/eAAC+ streams can be signalled in two ways: either explicitly - * or implicitly, according to MPEG4 spec. AAC+/eAAC+ is a dual - * rate system and the sampling rate in the final output is actually - * doubled compared with the core AAC decoder sampling rate. - * - * Explicit signalling is done by explicitly defining SBR audio object - * type in the bitstream. Implicit signalling is done by embedding - * SBR content in AAC extension payload specific to SBR, and hence - * requires an AAC decoder to perform pre-checks on actual audio frames. - * - * Thus, we could not say for sure whether a stream is - * AAC+/eAAC+ until the first data frame is decoded. - */ - if (mInputBufferCount <= 2) { - if (mStreamInfo->sampleRate != prevSampleRate || - mStreamInfo->numChannels != prevNumChannels) { - maybeConfigureDownmix(); - ALOGI("Reconfiguring decoder: %d->%d Hz, %d->%d channels", - prevSampleRate, mStreamInfo->sampleRate, - prevNumChannels, mStreamInfo->numChannels); - - notify(OMX_EventPortSettingsChanged, 1, 0, NULL); - mOutputPortSettingsChange = AWAITING_DISABLED; - return; + if (mEndOfInput) { + while (mOutputDelayCompensated > 0) { + // a buffer big enough for MAX_CHANNEL_COUNT channels of decoded HE-AAC + INT_PCM tmpOutBuffer[2048 * MAX_CHANNEL_COUNT]; + + // run DRC check + mDrcWrap.submitStreamData(mStreamInfo); + mDrcWrap.update(); + + AAC_DECODER_ERROR decoderErr = + aacDecoder_DecodeFrame(mAACDecoder, + tmpOutBuffer, + 2048 * MAX_CHANNEL_COUNT, + AACDEC_FLUSH); + if (decoderErr != AAC_DEC_OK) { + ALOGW("aacDecoder_DecodeFrame decoderErr = 0x%4.4x", decoderErr); + } + + int32_t tmpOutBufferSamples = mStreamInfo->frameSize * mStreamInfo->numChannels; + if (tmpOutBufferSamples > mOutputDelayCompensated) { + tmpOutBufferSamples = mOutputDelayCompensated; + } + outputDelayRingBufferPutSamples(tmpOutBuffer, tmpOutBufferSamples); + mOutputDelayCompensated -= tmpOutBufferSamples; } - } else if (!mStreamInfo->sampleRate || !mStreamInfo->numChannels) { - ALOGW("Invalid AAC stream"); - mSignalledError = true; - notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL); - return; } - if (decoderErr == AAC_DEC_OK || mNumSamplesOutput > 0) { - // We'll only output data if we successfully decoded it or - // we've previously decoded valid data, in the latter case - // (decode failed) we'll output a silent frame. - outHeader->nFilledLen = numOutBytes; + while (!outQueue.empty() + && outputDelayRingBufferSamplesAvailable() + >= mStreamInfo->frameSize * mStreamInfo->numChannels) { + BufferInfo *outInfo = *outQueue.begin(); + OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; - outHeader->nTimeStamp = - mAnchorTimeUs - + (mNumSamplesOutput * 1000000ll) / mStreamInfo->sampleRate; + if (outHeader->nOffset != 0) { + ALOGE("outHeader->nOffset != 0 is not handled"); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + return; + } - mNumSamplesOutput += mStreamInfo->frameSize; + INT_PCM *outBuffer = + reinterpret_cast<INT_PCM *>(outHeader->pBuffer + outHeader->nOffset); + if (outHeader->nOffset + + mStreamInfo->frameSize * mStreamInfo->numChannels * sizeof(int16_t) + > outHeader->nAllocLen) { + ALOGE("buffer overflow"); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + return; + } + int32_t ns = outputDelayRingBufferGetSamples(outBuffer, + mStreamInfo->frameSize * mStreamInfo->numChannels); // TODO: check for overflow + if (ns != mStreamInfo->frameSize * mStreamInfo->numChannels) { + ALOGE("not a complete frame of samples available"); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + return; + } + + outHeader->nFilledLen = mStreamInfo->frameSize * mStreamInfo->numChannels + * sizeof(int16_t); + if (mEndOfInput && !outQueue.empty() && outputDelayRingBufferSamplesAvailable() == 0) { + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + mEndOfOutput = true; + } else { + outHeader->nFlags = 0; + } + + outHeader->nTimeStamp = mAnchorTimeUs[mOutputBufferCount + % kNumDelayBlocksMax]; + + mOutputBufferCount++; outInfo->mOwnedByUs = false; outQueue.erase(outQueue.begin()); outInfo = NULL; @@ -558,8 +790,48 @@ void SoftAAC2::onQueueFilled(OMX_U32 portIndex) { outHeader = NULL; } - if (decoderErr == AAC_DEC_OK) { - ++mInputBufferCount; + if (mEndOfInput) { + if (outputDelayRingBufferSamplesAvailable() > 0 + && outputDelayRingBufferSamplesAvailable() + < mStreamInfo->frameSize * mStreamInfo->numChannels) { + ALOGE("not a complete frame of samples available"); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + return; + } + + if (mEndOfInput && !outQueue.empty() && outputDelayRingBufferSamplesAvailable() == 0) { + if (!mEndOfOutput) { + // send empty block signaling EOS + mEndOfOutput = true; + BufferInfo *outInfo = *outQueue.begin(); + OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; + + if (outHeader->nOffset != 0) { + ALOGE("outHeader->nOffset != 0 is not handled"); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + return; + } + + INT_PCM *outBuffer = reinterpret_cast<INT_PCM *>(outHeader->pBuffer + + outHeader->nOffset); + int32_t ns = 0; + outHeader->nFilledLen = 0; + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + + outHeader->nTimeStamp = mAnchorTimeUs[mOutputBufferCount + % kNumDelayBlocksMax]; + + mOutputBufferCount++; + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + outInfo = NULL; + notifyFillBufferDone(outHeader); + outHeader = NULL; + } + break; // if outQueue not empty but no more output + } } } } @@ -570,38 +842,66 @@ void SoftAAC2::onPortFlushCompleted(OMX_U32 portIndex) { // depend on fragments from the last one decoded. // drain all existing data drainDecoder(); - // force decoder loop to drop the first decoded buffer by resetting these state variables, - // but only if initialization has already happened. - if (mInputBufferCount != 0) { - mInputBufferCount = 1; - mStreamInfo->sampleRate = 0; + } else { + while (outputDelayRingBufferSamplesAvailable() > 0) { + int32_t ns = outputDelayRingBufferGetSamples(0, + mStreamInfo->frameSize * mStreamInfo->numChannels); + if (ns != mStreamInfo->frameSize * mStreamInfo->numChannels) { + ALOGE("not a complete frame of samples available"); + } + mOutputBufferCount++; } + mOutputDelayRingBufferReadPos = mOutputDelayRingBufferWritePos; } } void SoftAAC2::drainDecoder() { - // a buffer big enough for 6 channels of decoded HE-AAC - short buf [2048*6]; - aacDecoder_DecodeFrame(mAACDecoder, - buf, sizeof(buf), AACDEC_FLUSH | AACDEC_CLRHIST | AACDEC_INTR); - aacDecoder_DecodeFrame(mAACDecoder, - buf, sizeof(buf), AACDEC_FLUSH | AACDEC_CLRHIST | AACDEC_INTR); - aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1); - mDecoderHasData = false; + int32_t outputDelay = mStreamInfo->outputDelay * mStreamInfo->numChannels; + + // flush decoder until outputDelay is compensated + while (mOutputDelayCompensated > 0) { + // a buffer big enough for MAX_CHANNEL_COUNT channels of decoded HE-AAC + INT_PCM tmpOutBuffer[2048 * MAX_CHANNEL_COUNT]; + + // run DRC check + mDrcWrap.submitStreamData(mStreamInfo); + mDrcWrap.update(); + + AAC_DECODER_ERROR decoderErr = + aacDecoder_DecodeFrame(mAACDecoder, + tmpOutBuffer, + 2048 * MAX_CHANNEL_COUNT, + AACDEC_FLUSH); + if (decoderErr != AAC_DEC_OK) { + ALOGW("aacDecoder_DecodeFrame decoderErr = 0x%4.4x", decoderErr); + } + + int32_t tmpOutBufferSamples = mStreamInfo->frameSize * mStreamInfo->numChannels; + if (tmpOutBufferSamples > mOutputDelayCompensated) { + tmpOutBufferSamples = mOutputDelayCompensated; + } + outputDelayRingBufferPutSamples(tmpOutBuffer, tmpOutBufferSamples); + + mOutputDelayCompensated -= tmpOutBufferSamples; + } } void SoftAAC2::onReset() { drainDecoder(); // reset the "configured" state mInputBufferCount = 0; - mNumSamplesOutput = 0; + mOutputBufferCount = 0; + mOutputDelayCompensated = 0; + mOutputDelayRingBufferWritePos = 0; + mOutputDelayRingBufferReadPos = 0; + mEndOfInput = false; + mEndOfOutput = false; + // To make the codec behave the same before and after a reset, we need to invalidate the // streaminfo struct. This does that: - mStreamInfo->sampleRate = 0; + mStreamInfo->sampleRate = 0; // TODO: mStreamInfo is read only mSignalledError = false; - mSawInputEos = false; - mSignalledOutputEos = false; mOutputPortSettingsChange = NONE; } diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.h b/media/libstagefright/codecs/aacdec/SoftAAC2.h index a7ea1e2..5cde03a 100644 --- a/media/libstagefright/codecs/aacdec/SoftAAC2.h +++ b/media/libstagefright/codecs/aacdec/SoftAAC2.h @@ -20,6 +20,7 @@ #include "SimpleSoftOMXComponent.h" #include "aacdecoder_lib.h" +#include "DrcPresModeWrap.h" namespace android { @@ -47,18 +48,19 @@ private: enum { kNumInputBuffers = 4, kNumOutputBuffers = 4, + kNumDelayBlocksMax = 8, }; HANDLE_AACDECODER mAACDecoder; CStreamInfo *mStreamInfo; bool mIsADTS; - bool mDecoderHasData; + bool mIsFirst; size_t mInputBufferCount; + size_t mOutputBufferCount; bool mSignalledError; - bool mSawInputEos; - bool mSignalledOutputEos; - int64_t mAnchorTimeUs; - int64_t mNumSamplesOutput; + int64_t mAnchorTimeUs[kNumDelayBlocksMax]; + + CDrcPresModeWrapper mDrcWrap; enum { NONE, @@ -69,9 +71,22 @@ private: void initPorts(); status_t initDecoder(); bool isConfigured() const; - void maybeConfigureDownmix() const; + void configureDownmix() const; void drainDecoder(); +// delay compensation + bool mEndOfInput; + bool mEndOfOutput; + int32_t mOutputDelayCompensated; + int32_t mOutputDelayRingBufferSize; + short *mOutputDelayRingBuffer; + int32_t mOutputDelayRingBufferWritePos; + int32_t mOutputDelayRingBufferReadPos; + bool outputDelayRingBufferPutSamples(INT_PCM *samples, int numSamples); + int32_t outputDelayRingBufferGetSamples(INT_PCM *samples, int numSamples); + int32_t outputDelayRingBufferSamplesAvailable(); + int32_t outputDelayRingBufferSamplesLeft(); + DISALLOW_EVIL_CONSTRUCTORS(SoftAAC2); }; diff --git a/media/libstagefright/codecs/hevcdec/Android.mk b/media/libstagefright/codecs/hevcdec/Android.mk index 2fe347b..960602f 100644 --- a/media/libstagefright/codecs/hevcdec/Android.mk +++ b/media/libstagefright/codecs/hevcdec/Android.mk @@ -1,3 +1,5 @@ +ifeq ($(if $(wildcard external/libhevc),1,0),1) + LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) @@ -20,3 +22,5 @@ LOCAL_SHARED_LIBRARIES += liblog include $(BUILD_SHARED_LIBRARY) + +endif diff --git a/media/libstagefright/codecs/mp3dec/src/asm/pvmp3_dct_9_arm.s b/media/libstagefright/codecs/mp3dec/src/asm/pvmp3_dct_9_arm.s deleted file mode 100644 index 3a6dd4f..0000000 --- a/media/libstagefright/codecs/mp3dec/src/asm/pvmp3_dct_9_arm.s +++ /dev/null @@ -1,210 +0,0 @@ -; ------------------------------------------------------------------ -; Copyright (C) 1998-2009 PacketVideo -; -; 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. -; ------------------------------------------------------------------- - -; -; -; Filename: pvmp3_dct_9.s -; -;------------------------------------------------------------------------------ -; REVISION HISTORY -; -; -; Who: Date: MM/DD/YYYY -; Description: -; -;------------------------------------------------------------------------------ - - AREA |.drectve|, DRECTVE - - DCB "-defaultlib:coredll.lib " - DCB "-defaultlib:corelibc.lib " - - IMPORT pvmp3_mdct_18 ; pvmp3_mdct_18.cpp - -;------------------------------------------------------------------------------ - - AREA |.rdata|, DATA, READONLY - % 4 - - -;------------------------------------------------------------------------------ - - AREA |.text|, CODE, READONLY - - -;------------------------------------------------------------------------------ - - EXPORT |pvmp3_dct_9| - -|pvmp3_dct_9| PROC - stmfd sp!,{r4-r10,lr} - ldr r2, [r0, #0x20] - ldr r3, [r0] - ldr r12,[r0, #4] - add r1,r2,r3 - sub lr,r2,r3 - ldr r3,[r0, #0x1c] - ldr r4,[r0, #0x18] - add r2,r3,r12 - ldr r5,[r0,#8] - sub r3,r3,r12 - add r12,r4,r5 - sub r4,r4,r5 - ldr r5,[r0, #0x14] - ldr r7,[r0, #0xc] - ldr r9,[r0, #0x10] - add r6,r5,r7 - sub r5,r5,r7 - add r7,r1,r12 - add r8,r9,r2 - add r7,r7,r6 - add r10,r7,r8 - rsb r7,r8,r7,asr #1 - str r7,[r0, #0x18] - rsb r2,r9,r2,asr #1 - str r10,[r0] - ldr r11,|cos_2pi_9| - rsb r7,r2,#0 - - mov r9,r1,lsl #1 - mov r1,r9 ;;;;;; !!!!!! - mov r8,r7 - -; vec[4] = fxp_mac32_Q32( vec[4], tmp0<<1, cos_2pi_9); - - smlal r1,r8,r11,r9 - ldr r10,|cos_4pi_9| - ldr r11,|cos_pi_9| - -; vec[8] = fxp_mac32_Q32( vec[8], tmp0<<1, cos_4pi_9); - - smlal r1,r7,r10,r9 - - - -; vec[2] = fxp_mac32_Q32( vec[2], tmp0<<1, cos_pi_9); - - smlal r9,r2,r11,r9 - mov r1,r12,lsl #1 - rsb r9,r10,#0 - ldr r11,|cos_5pi_9| - - smlal r12,r2,r9,r1 - - - -; vec[2] = fxp_mac32_Q32( vec[2], tmp2<<1, cos_5pi_9); - - ldr r9,|cos_2pi_9| - mov r12,r1 ;;;;;; !!!!!! - smlal r12,r8,r11,r1 - - -; vec[8] = fxp_mac32_Q32( vec[8], tmp2<<1, cos_2pi_9); - - smlal r1,r7,r9,r1 - mov r1,r6,lsl #1 - smlal r12,r7,r11,r1 - and r6,r10,r11,asr #14 - smlal r12,r8,r6,r1 - ldr r10,|cos_11pi_18| - add r12,r11,r6 - smlal r1,r2,r12,r1 - ldr r9,|cos_8pi_9| - str r2,[r0,#8] - mov r1,r5,lsl #1 - -; vec[8] = fxp_mac32_Q32( vec[8], tmp3<<1, cos_8pi_9); - - smull r2,r6,r9,r1 - str r7,[r0,#0x20] - mov r2,r4,lsl #1 - ldr r7,|cos_13pi_18| - smlal r12,r6,r10,r2 - - mov r3,r3,lsl #1 - -; vec[5] = fxp_mac32_Q32( vec[5], tmp8<<1, cos_13pi_18); - - smlal r12,r6,r7,r3 - add r4,r5,r4 - mov r12,lr,lsl #1 - sub lr,r4,lr - ldr r7,|cos_17pi_18| - str r8,[r0, #0x10] - ldr r4,|cos_pi_6| - - mov lr,lr,lsl #1 - -; vec[1] = fxp_mac32_Q32( vec[1], tmp8<<1, cos_17pi_18); - - smlal r8,r6,r7,r12 - -; vec[3] = fxp_mul32_Q32((tmp5 + tmp6 - tmp8)<<1, cos_pi_6); - - smull r5,lr,r4,lr - str r6,[r0, #4] - str lr,[r0, #0xc] - - -; vec[5] = fxp_mul32_Q32(tmp5<<1, cos_17pi_18); - smull r5,lr,r7,r1 - rsb r6,r9,#0 -; vec[5] = fxp_mac32_Q32( vec[5], tmp6<<1, cos_7pi_18); - smlal r5,lr,r6,r2 -; vec[5] = fxp_mac32_Q32( vec[5], tmp7<<1, cos_pi_6); - smlal r5,lr,r4,r3 -; vec[5] = fxp_mac32_Q32( vec[5], tmp8<<1, cos_13pi_18); - smlal r5,lr,r10,r12 - str lr,[r0, #0x14] - rsb lr,r10,#0 - -; vec[7] = fxp_mul32_Q32(tmp5<<1, cos_5pi_18); - smull r5,r1,lr,r1 -; vec[7] = fxp_mac32_Q32( vec[7], tmp6<<1, cos_17pi_18); - smlal r2,r1,r7,r2 -; vec[7] = fxp_mac32_Q32( vec[7], tmp7<<1, cos_pi_6); - smlal r3,r1,r4,r3 -; vec[7] = fxp_mac32_Q32( vec[7], tmp8<<1, cos_11pi_18); - smlal r12,r1,r9,r12 - str r1,[r0, #0x1c] - ldmfd sp!,{r4-r10,pc} -|cos_2pi_9| - DCD 0x620dbe80 -|cos_4pi_9| - DCD 0x163a1a80 -|cos_pi_9| - DCD 0x7847d900 -|cos_5pi_9| - DCD 0x87b82700 -|cos_8pi_9| - DCD 0xd438af00 -|cos_11pi_18| - DCD 0xadb92280 -|cos_13pi_18| - DCD 0x91261480 -|cos_17pi_18| - DCD 0x81f1d200 -|cos_pi_6| - DCD 0x6ed9eb80 - ENDP - - - - - - END diff --git a/media/libstagefright/codecs/mp3dec/src/asm/pvmp3_mdct_18_arm.s b/media/libstagefright/codecs/mp3dec/src/asm/pvmp3_mdct_18_arm.s deleted file mode 100644 index 9401d8c..0000000 --- a/media/libstagefright/codecs/mp3dec/src/asm/pvmp3_mdct_18_arm.s +++ /dev/null @@ -1,369 +0,0 @@ -; ------------------------------------------------------------------ -; Copyright (C) 1998-2009 PacketVideo -; -; 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. -; ------------------------------------------------------------------- - -; -; -; Filename: pvmp3_dct_18.s -; -;------------------------------------------------------------------------------ -; REVISION HISTORY -; -; -; Who: Date: MM/DD/YYYY -; Description: -; -;------------------------------------------------------------------------------ - - EXPORT pvmp3_mdct_18 - - IMPORT ||Lib$$Request$$armlib|| [WEAK] - IMPORT ||Lib$$Request$$cpplib|| [WEAK] - IMPORT pvmp3_dct_9 - - -;------------------------------------------------------------------------------ - - AREA |.text|, CODE, READONLY, ALIGN=2 - - -;------------------------------------------------------------------------------ - -|pvmp3_mdct_18| PROC - stmfd sp!,{r4-r10,lr} - mov r7,r2 - ldr r2,table - mov r6,r1 - add r3,r2,#0x24 - add r12,r3,#0x44 - add r1,r0,#0x44 - mov r5,r0 - -; for ( i=9; i!=0; i--) -; { - - mov r4,#9 -Loop_1 - -; tmp = *(pt_vec); -; tmp1 = *(pt_vec_o); - - ldr lr,[r0] ;; tmp == lr - ldr r8,[r3],#4 ;; tmp1 == r8 - -; tmp = fxp_mul32_Q32( tmp<<1, *(pt_cos++ )); -; tmp1 = fxp_mul32_Q27( tmp1, *(pt_cos_x--)); - - mov lr,lr,lsl #1 - smull r10,lr,r8,lr - ldr r8,[r12],#-4 - ldr r9,[r1] - subs r4,r4,#1 - smull r9,r10,r8,r9 - mov r8,r9,lsr #27 - add r8,r8,r10,lsl #5 - -; *(pt_vec++) = tmp + tmp1 ; -; *(pt_vec_o--) = fxp_mul32_Q28( (tmp - tmp1), *(pt_cos_split++)); - - add r9,lr,r8 - sub r8,lr,r8 - ldr lr,[r2],#4 - str r9,[r0],#4 - smull r8,r9,lr,r8 - mov lr,r8,lsr #28 - add lr,lr,r9,lsl #4 - str lr,[r1],#-4 - bne Loop_1 - -; } - - mov r0,r5 ;; r0 = vec - bl pvmp3_dct_9 - add r0,r5,#0x24 ;; r0 = &vec[9] - bl pvmp3_dct_9 - - ldr r0,[r5,#0x20] - ldr r2,[r5,#0x40] - str r0,[r5,#0x40] - ldr r0,[r5,#0x1c] - ldr r3,[r5,#0x38] - str r0,[r5,#0x38] - ldr r1,[r5,#0x18] - ldr r0,[r5,#0x30] - str r1,[r5,#0x30] - ldr r12,[r5,#0x14] - ldr r1,[r5,#0x28] - str r12,[r5,#0x28] - ldr r12,[r5,#0x10] - str r12,[r5,#0x20] - ldr r12,[r5,#0xc] - str r12,[r5,#0x18] - ldr r12,[r5,#8] - str r12,[r5,#0x10] - ldr r12,[r5,#4] - str r12,[r5,#8] - ldr r12,[r5,#0x24] - sub r12,r12,r1 - str r12,[r5,#4] - ldr r12,[r5,#0x2c] - sub r1,r12,r1 - str r1,[r5,#0xc] - sub r1,r12,r0 - str r1,[r5,#0x14] - ldr r1,[r5,#0x34] - sub r0,r1,r0 - str r0,[r5,#0x1c] - sub r0,r1,r3 - str r0,[r5,#0x24] - ldr r1,[r5,#0x3c] - sub r3,r1,r3 - sub r1,r1,r2 - str r1,[r5,#0x34] - str r3,[r5,#0x2c] - ldr r1,[r5,#0x44] - sub r1,r1,r2 - str r1,[r5,#0x3c] - ldr r12,[r5,#0] - -Loop_2 - add r1,r5,r4,lsl #2 - ldr r2,[r1,#0x28] - ldr r3,[r6,r4,lsl #2] - add r0,r0,r2 - str r0,[r1,#0x28] - ldr lr,[r7,r4,lsl #2] - ldr r1,[r1,#4] - smlal r0,r3,lr,r0 - mov r0,r2 - add r2,r12,r1 - rsb r2,r2,#0 - str r3,[r5,r4,lsl #2] - str r2,[r6,r4,lsl #2] - add r4,r4,#1 - cmp r4,#6 - mov r12,r1 - - blt Loop_2 - - ldr r1,[r5,#0x40] - ldr r2,[r6,#0x18] - add r3,r0,r1 - str r3,[r5,#0x40] - ldr lr,[r7,r4,lsl #2] - mov r3,r3,lsl #1 - ldr r0,[r5,#0x1c] - smlal r3,r2,lr,r3 - add r3,r12,r0 - str r2,[r5,#0x18] - ldr r2,[r6,#0x1c] - rsb r3,r3,#0 - str r3,[r6,#0x18] - ldr r3,[r5,#0x20] - add r0,r3,r0 - rsb r0,r0,#0 - str r0,[r6,#0x1c] - ldr r3,[r5,#0x44] - ldr r0,[r6,#0x20] - add r3,r3,r1 - mov r1,r2 - ldr r10,[r7,#0x1c] - mov r2,r3,lsl #1 - smlal r12,r1,r10,r2 - str r1,[r5,#0x1c] - ldr r1,[r5,#0x20] - ldr r3,[r5,#0x24] - add r1,r1,r3 - rsb r1,r1,#0 - str r1,[r6,#0x20] - ldr r1,[r5,#0x44] - ldr r3,[r7,#0x20] - mov r1,r1,lsl #1 - smlal r12,r0,r3,r1 - ldr lr,[r7,#0x24] - ldr r3,[r6,#0x24] - str r0,[r5,#0x20] - smlal r1,r3,lr,r1 - ldr r0,[r6,#0x40] - ldr r12,[r6,#0x44] - str r3,[r5,#0x24] - ldr r1,[r5,#0x28] - ldr r3,[r7,#0x44] - mov r1,r1,lsl #1 - smlal r1,r12,r3,r1 - ldr r1,[r5,#0x40] - str r12,[r5,#0x44] - rsb r8,r1,#0 - str r8,[r5,#0x28] - ldr r1,[r5,#0x2c] - ldr r3,[r7,#0x40] - mov r1,r1,lsl #1 - smlal r1,r0,r3,r1 - str r0,[r5,#0x40] - ldr r0,[r5,#0x3c] - ldr r1,[r6,#0x38] - ldr r3,[r6,#0x3c] - rsb r9,r0,#0 - str r9,[r5,#0x2c] - ldr r0,[r5,#0x30] - ldr r12,[r7,#0x3c] - mov r0,r0,lsl #1 - smlal r0,r3,r12,r0 - str r3,[r5,#0x3c] - ldr r0,[r5,#0x38] - rsb r0,r0,#0 - str r0,[r5,#0x30] - ldr r3,[r5,#0x34] - ldr r12,[r7,#0x38] - mov r3,r3,lsl #1 - smlal r3,r1,r12,r3 - mov r0,r0,lsl #1 - str r1,[r5,#0x38] - ldr r4,[r7,#0x34] - ldr r1,[r6,#0x34] - ldr r3,[r6,#0x30] - smlal r0,r1,r4,r0 - ldr r12,[r6,#0x2c] - ldr lr,[r6,#0x28] - str r1,[r5,#0x34] - ldr r1,[r7,#0x30] - mov r0,r9,lsl #1 - smlal r0,r3,r1,r0 - mov r0,r8,lsl #1 - ldr r1,[r7,#0x2c] - str r3,[r5,#0x30] - smlal r0,r12,r1,r0 - ldr r0,[r7,#0x28] - str r12,[r5,#0x2c] - smlal r2,lr,r0,r2 - str lr,[r5,#0x28] - ldr r1,[r6,#4] - ldr r12,[r7,#0x48] - mov r2,r1,lsl #1 - ldr r1,[r6,#0x20] - ldr r0,[r6] - mov r1,r1,lsl #1 - smull r4,lr,r12,r1 - ldr r3,[r6,#0x1c] - str lr,[r6] - ldr r12,[r7,#0x4c] - mov r3,r3,lsl #1 - smull r4,lr,r12,r3 - mov r0,r0,lsl #1 - ldr r12,[r7,#0x64] - str lr,[r6,#4] - smull r4,lr,r12,r2 - ldr r12,[r7,#0x68] - str lr,[r6,#0x1c] - smull r4,lr,r12,r0 - ldr r12,[r7,#0x6c] - str lr,[r6,#0x20] - smull lr,r0,r12,r0 - ldr r12,[r7,#0x70] - str r0,[r6,#0x24] - smull r0,r2,r12,r2 - ldr r0,[r7,#0x88] - str r2,[r6,#0x28] - smull r3,r2,r0,r3 - ldr r0,[r7,#0x8c] - str r2,[r6,#0x40] - smull r2,r1,r0,r1 - str r1,[r6,#0x44] - ldr r0,[r6,#0x18] - ldr lr,[r7,#0x50] - mov r1,r0,lsl #1 - ldr r0,[r6,#0x14] - smull r5,r4,lr,r1 - ldr r12,[r6,#0x10] - mov r3,r0,lsl #1 - ldr r0,[r6,#0xc] - mov r12,r12,lsl #1 - mov r2,r0,lsl #1 - ldr r0,[r6,#8] - str r4,[r6,#8] - ldr lr,[r7,#0x54] - mov r0,r0,lsl #1 - smull r5,r4,lr,r3 - ldr lr,[r7,#0x58] - str r4,[r6,#0xc] - smull r5,r4,lr,r12 - ldr lr,[r7,#0x5c] - str r4,[r6,#0x10] - smull r5,r4,lr,r2 - ldr lr,[r7,#0x60] - str r4,[r6,#0x14] - smull r5,r4,lr,r0 - ldr lr,[r7,#0x74] - str r4,[r6,#0x18] - smull r4,r0,lr,r0 - ldr lr,[r7,#0x78] - str r0,[r6,#0x2c] - smull r0,r2,lr,r2 - ldr r0,[r7,#0x7c] - str r2,[r6,#0x30] - smull r12,r2,r0,r12 - ldr r0,[r7,#0x80] - str r2,[r6,#0x34] - smull r3,r2,r0,r3 - ldr r0,[r7,#0x84] - str r2,[r6,#0x38] - smull r2,r1,r0,r1 - str r1,[r6,#0x3c] - ldmfd sp!,{r4-r10,pc} -table - DCD ||.constdata$1|| - ENDP - -;------------------------------------------------------------------------------ - - AREA |.constdata|, DATA, READONLY, ALIGN=2 - -;------------------------------------------------------------------------------ - -||.constdata$1|| -cosTerms_dct18 - DCD 0x0807d2b0 - DCD 0x08483ee0 - DCD 0x08d3b7d0 - DCD 0x09c42570 - DCD 0x0b504f30 - DCD 0x0df29440 - DCD 0x12edfb20 - DCD 0x1ee8dd40 - DCD 0x5bca2a00 -cosTerms_1_ov_cos_phi - DCD 0x400f9c00 - DCD 0x408d6080 - DCD 0x418dcb80 - DCD 0x431b1a00 - DCD 0x4545ea00 - DCD 0x48270680 - DCD 0x4be25480 - DCD 0x50ab9480 - DCD 0x56ce4d80 - DCD 0x05ebb630 - DCD 0x06921a98 - DCD 0x0771d3a8 - DCD 0x08a9a830 - DCD 0x0a73d750 - DCD 0x0d4d5260 - DCD 0x127b1ca0 - DCD 0x1ea52b40 - DCD 0x5bb3cc80 - - - - END diff --git a/media/libstagefright/codecs/mp3dec/src/asm/pvmp3_mdct_18_wm.asm b/media/libstagefright/codecs/mp3dec/src/asm/pvmp3_mdct_18_wm.asm deleted file mode 100644 index 5be75d4..0000000 --- a/media/libstagefright/codecs/mp3dec/src/asm/pvmp3_mdct_18_wm.asm +++ /dev/null @@ -1,366 +0,0 @@ -; ------------------------------------------------------------------ -; Copyright (C) 1998-2009 PacketVideo -; -; 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. -; ------------------------------------------------------------------- - -; -; -; Filename: pvmp3_dct_18.s -; -;------------------------------------------------------------------------------ -; REVISION HISTORY -; -; -; Who: Date: MM/DD/YYYY -; Description: -; -;------------------------------------------------------------------------------ - - EXPORT |pvmp3_mdct_18| - - IMPORT pvmp3_dct_9 - - -;------------------------------------------------------------------------------ - - AREA |.text|, CODE, READONLY, ALIGN=2 - - -;------------------------------------------------------------------------------ - -|pvmp3_mdct_18| PROC - stmfd sp!,{r4-r10,lr} - mov r7,r2 - ldr r2,table - mov r6,r1 - add r3,r2,#0x24 - add r12,r3,#0x44 - add r1,r0,#0x44 - mov r5,r0 - -; for ( i=9; i!=0; i--) -; { - - mov r4,#9 -Loop_1 - -; tmp = *(pt_vec); -; tmp1 = *(pt_vec_o); - - ldr lr,[r0] ;; tmp == lr - ldr r8,[r3],#4 ;; tmp1 == r8 - -; tmp = fxp_mul32_Q32( tmp<<1, *(pt_cos++ )); -; tmp1 = fxp_mul32_Q27( tmp1, *(pt_cos_x--)); - - mov lr,lr,lsl #1 - smull r10,lr,r8,lr - ldr r8,[r12],#-4 - ldr r9,[r1] - subs r4,r4,#1 - smull r9,r10,r8,r9 - mov r8,r9,lsr #27 - add r8,r8,r10,lsl #5 - -; *(pt_vec++) = tmp + tmp1 ; -; *(pt_vec_o--) = fxp_mul32_Q28( (tmp - tmp1), *(pt_cos_split++)); - - add r9,lr,r8 - sub r8,lr,r8 - ldr lr,[r2],#4 - str r9,[r0],#4 - smull r8,r9,lr,r8 - mov lr,r8,lsr #28 - add lr,lr,r9,lsl #4 - str lr,[r1],#-4 - bne Loop_1 - -; } - - mov r0,r5 ;; r0 = vec - bl pvmp3_dct_9 - add r0,r5,#0x24 ;; r0 = &vec[9] - bl pvmp3_dct_9 - - ldr r0,[r5,#0x20] - ldr r2,[r5,#0x40] - str r0,[r5,#0x40] - ldr r0,[r5,#0x1c] - ldr r3,[r5,#0x38] - str r0,[r5,#0x38] - ldr r1,[r5,#0x18] - ldr r0,[r5,#0x30] - str r1,[r5,#0x30] - ldr r12,[r5,#0x14] - ldr r1,[r5,#0x28] - str r12,[r5,#0x28] - ldr r12,[r5,#0x10] - str r12,[r5,#0x20] - ldr r12,[r5,#0xc] - str r12,[r5,#0x18] - ldr r12,[r5,#8] - str r12,[r5,#0x10] - ldr r12,[r5,#4] - str r12,[r5,#8] - ldr r12,[r5,#0x24] - sub r12,r12,r1 - str r12,[r5,#4] - ldr r12,[r5,#0x2c] - sub r1,r12,r1 - str r1,[r5,#0xc] - sub r1,r12,r0 - str r1,[r5,#0x14] - ldr r1,[r5,#0x34] - sub r0,r1,r0 - str r0,[r5,#0x1c] - sub r0,r1,r3 - str r0,[r5,#0x24] - ldr r1,[r5,#0x3c] - sub r3,r1,r3 - sub r1,r1,r2 - str r1,[r5,#0x34] - str r3,[r5,#0x2c] - ldr r1,[r5,#0x44] - sub r1,r1,r2 - str r1,[r5,#0x3c] - ldr r12,[r5,#0] - -Loop_2 - add r1,r5,r4,lsl #2 - ldr r2,[r1,#0x28] - ldr r3,[r6,r4,lsl #2] - add r0,r0,r2 - str r0,[r1,#0x28] - ldr lr,[r7,r4,lsl #2] - ldr r1,[r1,#4] - smlal r0,r3,lr,r0 - mov r0,r2 - add r2,r12,r1 - rsb r2,r2,#0 - str r3,[r5,r4,lsl #2] - str r2,[r6,r4,lsl #2] - add r4,r4,#1 - cmp r4,#6 - mov r12,r1 - - blt Loop_2 - - ldr r1,[r5,#0x40] - ldr r2,[r6,#0x18] - add r3,r0,r1 - str r3,[r5,#0x40] - ldr lr,[r7,r4,lsl #2] - mov r3,r3,lsl #1 - ldr r0,[r5,#0x1c] - smlal r3,r2,lr,r3 - add r3,r12,r0 - str r2,[r5,#0x18] - ldr r2,[r6,#0x1c] - rsb r3,r3,#0 - str r3,[r6,#0x18] - ldr r3,[r5,#0x20] - add r0,r3,r0 - rsb r0,r0,#0 - str r0,[r6,#0x1c] - ldr r3,[r5,#0x44] - ldr r0,[r6,#0x20] - add r3,r3,r1 - mov r1,r2 - ldr r10,[r7,#0x1c] - mov r2,r3,lsl #1 - smlal r12,r1,r10,r2 - str r1,[r5,#0x1c] - ldr r1,[r5,#0x20] - ldr r3,[r5,#0x24] - add r1,r1,r3 - rsb r1,r1,#0 - str r1,[r6,#0x20] - ldr r1,[r5,#0x44] - ldr r3,[r7,#0x20] - mov r1,r1,lsl #1 - smlal r12,r0,r3,r1 - ldr lr,[r7,#0x24] - ldr r3,[r6,#0x24] - str r0,[r5,#0x20] - smlal r1,r3,lr,r1 - ldr r0,[r6,#0x40] - ldr r12,[r6,#0x44] - str r3,[r5,#0x24] - ldr r1,[r5,#0x28] - ldr r3,[r7,#0x44] - mov r1,r1,lsl #1 - smlal r1,r12,r3,r1 - ldr r1,[r5,#0x40] - str r12,[r5,#0x44] - rsb r8,r1,#0 - str r8,[r5,#0x28] - ldr r1,[r5,#0x2c] - ldr r3,[r7,#0x40] - mov r1,r1,lsl #1 - smlal r1,r0,r3,r1 - str r0,[r5,#0x40] - ldr r0,[r5,#0x3c] - ldr r1,[r6,#0x38] - ldr r3,[r6,#0x3c] - rsb r9,r0,#0 - str r9,[r5,#0x2c] - ldr r0,[r5,#0x30] - ldr r12,[r7,#0x3c] - mov r0,r0,lsl #1 - smlal r0,r3,r12,r0 - str r3,[r5,#0x3c] - ldr r0,[r5,#0x38] - rsb r0,r0,#0 - str r0,[r5,#0x30] - ldr r3,[r5,#0x34] - ldr r12,[r7,#0x38] - mov r3,r3,lsl #1 - smlal r3,r1,r12,r3 - mov r0,r0,lsl #1 - str r1,[r5,#0x38] - ldr r4,[r7,#0x34] - ldr r1,[r6,#0x34] - ldr r3,[r6,#0x30] - smlal r0,r1,r4,r0 - ldr r12,[r6,#0x2c] - ldr lr,[r6,#0x28] - str r1,[r5,#0x34] - ldr r1,[r7,#0x30] - mov r0,r9,lsl #1 - smlal r0,r3,r1,r0 - mov r0,r8,lsl #1 - ldr r1,[r7,#0x2c] - str r3,[r5,#0x30] - smlal r0,r12,r1,r0 - ldr r0,[r7,#0x28] - str r12,[r5,#0x2c] - smlal r2,lr,r0,r2 - str lr,[r5,#0x28] - ldr r1,[r6,#4] - ldr r12,[r7,#0x48] - mov r2,r1,lsl #1 - ldr r1,[r6,#0x20] - ldr r0,[r6] - mov r1,r1,lsl #1 - smull r4,lr,r12,r1 - ldr r3,[r6,#0x1c] - str lr,[r6] - ldr r12,[r7,#0x4c] - mov r3,r3,lsl #1 - smull r4,lr,r12,r3 - mov r0,r0,lsl #1 - ldr r12,[r7,#0x64] - str lr,[r6,#4] - smull r4,lr,r12,r2 - ldr r12,[r7,#0x68] - str lr,[r6,#0x1c] - smull r4,lr,r12,r0 - ldr r12,[r7,#0x6c] - str lr,[r6,#0x20] - smull lr,r0,r12,r0 - ldr r12,[r7,#0x70] - str r0,[r6,#0x24] - smull r0,r2,r12,r2 - ldr r0,[r7,#0x88] - str r2,[r6,#0x28] - smull r3,r2,r0,r3 - ldr r0,[r7,#0x8c] - str r2,[r6,#0x40] - smull r2,r1,r0,r1 - str r1,[r6,#0x44] - ldr r0,[r6,#0x18] - ldr lr,[r7,#0x50] - mov r1,r0,lsl #1 - ldr r0,[r6,#0x14] - smull r5,r4,lr,r1 - ldr r12,[r6,#0x10] - mov r3,r0,lsl #1 - ldr r0,[r6,#0xc] - mov r12,r12,lsl #1 - mov r2,r0,lsl #1 - ldr r0,[r6,#8] - str r4,[r6,#8] - ldr lr,[r7,#0x54] - mov r0,r0,lsl #1 - smull r5,r4,lr,r3 - ldr lr,[r7,#0x58] - str r4,[r6,#0xc] - smull r5,r4,lr,r12 - ldr lr,[r7,#0x5c] - str r4,[r6,#0x10] - smull r5,r4,lr,r2 - ldr lr,[r7,#0x60] - str r4,[r6,#0x14] - smull r5,r4,lr,r0 - ldr lr,[r7,#0x74] - str r4,[r6,#0x18] - smull r4,r0,lr,r0 - ldr lr,[r7,#0x78] - str r0,[r6,#0x2c] - smull r0,r2,lr,r2 - ldr r0,[r7,#0x7c] - str r2,[r6,#0x30] - smull r12,r2,r0,r12 - ldr r0,[r7,#0x80] - str r2,[r6,#0x34] - smull r3,r2,r0,r3 - ldr r0,[r7,#0x84] - str r2,[r6,#0x38] - smull r2,r1,r0,r1 - str r1,[r6,#0x3c] - ldmfd sp!,{r4-r10,pc} -table - DCD cosTerms_dct18 - ENDP - -;------------------------------------------------------------------------------ - - AREA |.constdata|, DATA, READONLY, ALIGN=2 - -;------------------------------------------------------------------------------ - -cosTerms_dct18 - DCD 0x0807d2b0 - DCD 0x08483ee0 - DCD 0x08d3b7d0 - DCD 0x09c42570 - DCD 0x0b504f30 - DCD 0x0df29440 - DCD 0x12edfb20 - DCD 0x1ee8dd40 - DCD 0x5bca2a00 -cosTerms_1_ov_cos_phi - DCD 0x400f9c00 - DCD 0x408d6080 - DCD 0x418dcb80 - DCD 0x431b1a00 - DCD 0x4545ea00 - DCD 0x48270680 - DCD 0x4be25480 - DCD 0x50ab9480 - DCD 0x56ce4d80 - DCD 0x05ebb630 - DCD 0x06921a98 - DCD 0x0771d3a8 - DCD 0x08a9a830 - DCD 0x0a73d750 - DCD 0x0d4d5260 - DCD 0x127b1ca0 - DCD 0x1ea52b40 - DCD 0x5bb3cc80 - - - - END diff --git a/media/libstagefright/codecs/mp3dec/src/asm/pvmp3_polyphase_filter_window_arm.s b/media/libstagefright/codecs/mp3dec/src/asm/pvmp3_polyphase_filter_window_arm.s deleted file mode 100644 index abec599..0000000 --- a/media/libstagefright/codecs/mp3dec/src/asm/pvmp3_polyphase_filter_window_arm.s +++ /dev/null @@ -1,237 +0,0 @@ -; ------------------------------------------------------------------ -; Copyright (C) 1998-2009 PacketVideo -; -; 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. -; ------------------------------------------------------------------- - -; -; -; Filename: pvmp3_polyphase_filter_window.s -; -;------------------------------------------------------------------------------ -; REVISION HISTORY -; -; -; Who: Date: MM/DD/YYYY -; Description: -; -;------------------------------------------------------------------------------ - - EXPORT pvmp3_polyphase_filter_window - - IMPORT ||Lib$$Request$$armlib|| [WEAK] - IMPORT ||Lib$$Request$$cpplib|| [WEAK] - IMPORT pqmfSynthWin - - - -;------------------------------------------------------------------------------ - - AREA |.text|, CODE, READONLY, ALIGN=2 - - -;------------------------------------------------------------------------------ - -|pvmp3_polyphase_filter_window| PROC - - stmfd sp!,{r0-r2,r4-r11,lr} - - sub sp,sp,#4 - ldr r2,[sp,#0xc] - ldr r1,PolyPh_filter_coeff - - sub r2,r2,#1 - mov r10,#1 - str r2,[sp] - -; Accumulators r9, r11::> Initialization - -Loop_j - mov r9, #0x20 - mov r11, #0x20 - mov r4, #0x10 -Loop_i - add r2,r4,r10 - add r3,r0,r2,lsl #2 - sub r2,r4,r10 - ldr r5,[r3] - ldr lr,[r1] - add r12,r0,r2,lsl #2 - ldr r6,[r12,#0x780] - smlal r2,r9,lr,r5 - smlal r2,r11,lr,r6 - ldr r2,[r1,#4] - ldr r7,[r12,#0x80] - smlal r5,r11,r2,r5 - smull r6,r5,r2,r6 - sub r9,r9,r5 - ldr r5,[r1,#8] - ldr r8,[r3,#0x700] - add r4,r4,#0x200 - smlal r6,r9,r5,r7 - smull r6,r2,r5,r8 - ldr r5,[r1,#0xc] - sub r11,r11,r2 - smlal r8,r9,r5,r8 - smlal r7,r11,r5,r7 - ldr r5,[r3,#0x100] - ldr r2,[r1,#0x10] - ldr r6,[r12,#0x680] - smlal lr,r9,r2,r5 - smlal lr,r11,r2,r6 - ldr r2,[r1,#0x14] - ldr r7,[r12,#0x180] - smlal r5,r11,r2,r5 - smull r6,r5,r2,r6 - ldr r6,[r1,#0x18] - ldr r8,[r3,#0x600] - sub r9,r9,r5 - smlal r5,r9,r6,r7 - smull r2,r5,r6,r8 - ldr r6,[r1,#0x1c] - sub r11,r11,r5 - smlal r8,r9,r6,r8 - ldr r2,[r1,#0x20] - ldr r5,[r3,#0x200] - smlal r7,r11,r6,r7 - ldr r6,[r12,#0x580] - smlal lr,r9,r2,r5 - smlal lr,r11,r2,r6 - ldr r2,[r1,#0x24] - ldr r7,[r12,#0x280] - smlal r5,r11,r2,r5 - smull r6,r5,r2,r6 - ldr r6,[r1,#0x28] - ldr r8,[r3,#0x500] - sub r9,r9,r5 - smlal r5,r9,r6,r7 - smull r2,r5,r6,r8 - ldr r6,[r1,#0x2c] - sub r11,r11,r5 - - smlal r8,r9,r6,r8 - smlal r7,r11,r6,r7 - ldr r5,[r3,#0x300] - ldr r8,[r1,#0x30] - ldr r6,[r12,#0x480] - smlal r7,r9,r8,r5 - smlal r7,r11,r8,r6 - ldr r8,[r1,#0x34] - ldr r12,[r12,#0x380] - smlal r5,r11,r8,r5 - smull r6,r5,r8,r6 - ldr r6,[r1,#0x38] - - - ldr r3,[r3,#0x400] - sub r9,r9,r5 - smlal r7,r9,r6,r12 - smull r8,r7,r6,r3 - cmp r4,#0x210 - sub r11,r11,r7 - - ldr r2,[r1,#0x3c] - add r1,r1,#0x40 - smlal r3,r9,r2,r3 - smlal r12,r11,r2,r12 - - blt Loop_i - - mov r3,r9, asr #6 - mov r4,r3, asr #15 - teq r4,r3, asr #31 - ldr r12,LOW_16BITS - ldr r2,[sp] - eorne r3,r12,r3,asr #31 - ldr r4,[sp,#8] - mov r2,r10,lsl r2 - add r4,r4,r2,lsl #1 - strh r3,[r4] - - mov r3,r11,asr #6 - mov r4,r3,asr #15 - teq r4,r3,asr #31 - eorne r3,r12,r3,asr #31 - ldr r12,[sp,#0xc] - ldr r11,[sp,#8] - rsb r2,r2,r12,lsl #5 - add r2,r11,r2,lsl #1 - strh r3,[r2] - - add r10,r10,#1 - cmp r10,#0x10 - blt Loop_j - -; Accumulators r4, r5 Initialization - - mov r4,#0x20 - mov r5,#0x20 - mov r3,#0x10 -PolyPh_filter_loop2 - add r2,r0,r3,lsl #2 - ldr r12,[r2] - ldr r8,[r1] - ldr r6,[r2,#0x80] - smlal r12,r4,r8,r12 - ldr r12,[r1,#4] - ldr r7,[r2,#0x40] - smlal r6,r4,r12,r6 - - ldr r12,[r1,#8] - ldr r6,[r2,#0x180] - smlal r7,r5,r12,r7 - ldr r12,[r2,#0x100] - ldr r7,[r1,#0xc] - ldr r2,[r2,#0x140] - smlal r12,r4,r7,r12 - ldr r12,[r1,#0x10] - add r3,r3,#0x80 - smlal r6,r4,r12,r6 - ldr r6,[r1,#0x14] - cmp r3,#0x210 - smlal r2,r5,r6,r2 - add r1,r1,#0x18 - - blt PolyPh_filter_loop2 - mov r0,r4,asr #6 - mov r2,r0,asr #15 - teq r2,r0,asr #31 - ldrne r12,LOW_16BITS - ldr r1,[sp,#8] - eorne r0,r12,r0,asr #31 - strh r0,[r1,#0] - mov r0,r5,asr #6 - mov r2,r0,asr #15 - teq r2,r0,asr #31 - ldrne r12,LOW_16BITS - ldr r2,[sp] - mov r1,#0x10 - eorne r0,r12,r0,asr #31 - ldr r12,[sp,#8] - mov r1,r1,lsl r2 - add r1,r12,r1,lsl #1 - strh r0,[r1] - add sp,sp,#0x10 - ldmfd sp!,{r4-r11,pc} - - -PolyPh_filter_coeff - DCD pqmfSynthWin -LOW_16BITS - DCD 0x00007fff - - ENDP - - - END diff --git a/media/libstagefright/codecs/mp3dec/src/asm/pvmp3_polyphase_filter_window_wm.asm b/media/libstagefright/codecs/mp3dec/src/asm/pvmp3_polyphase_filter_window_wm.asm deleted file mode 100644 index f957267..0000000 --- a/media/libstagefright/codecs/mp3dec/src/asm/pvmp3_polyphase_filter_window_wm.asm +++ /dev/null @@ -1,231 +0,0 @@ -; ------------------------------------------------------------------ -; Copyright (C) 1998-2009 PacketVideo -; -; 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. -; ------------------------------------------------------------------- - -; -; -; Filename: pvmp3_polyphase_filter_window.s -; -;------------------------------------------------------------------------------ -; REVISION HISTORY -; -; -; Who: Date: MM/DD/YYYY -; Description: -; -;------------------------------------------------------------------------------ - - CODE32 - - AREA |.drectve|, DRECTVE - - EXPORT |pvmp3_polyphase_filter_window| - IMPORT |pqmfSynthWin| - - AREA |.pdata|, PDATA - - AREA |.text|, CODE, ARM - -|pvmp3_polyphase_filter_window| PROC - stmfd sp!,{r0-r2,r4-r11,lr} - - sub sp,sp,#4 - ldr r2,[sp,#0xc] - ldr r1,PolyPh_filter_coeff - - sub r2,r2,#1 - mov r10,#1 - str r2,[sp] - -; Accumulators r9, r11::> Initialization - -Loop_j - mov r9, #0x20 - mov r11, #0x20 - mov r4, #0x10 -Loop_i - add r2,r4,r10 - add r3,r0,r2,lsl #2 - sub r2,r4,r10 - ldr r5,[r3] - ldr lr,[r1] - add r12,r0,r2,lsl #2 - ldr r6,[r12,#0x780] - smlal r2,r9,lr,r5 - smlal r2,r11,lr,r6 - ldr r2,[r1,#4] - ldr r7,[r12,#0x80] - smlal r5,r11,r2,r5 - smull r6,r5,r2,r6 - sub r9,r9,r5 - ldr r5,[r1,#8] - ldr r8,[r3,#0x700] - add r4,r4,#0x200 - smlal r6,r9,r5,r7 - smull r6,r2,r5,r8 - ldr r5,[r1,#0xc] - sub r11,r11,r2 - smlal r8,r9,r5,r8 - smlal r7,r11,r5,r7 - ldr r5,[r3,#0x100] - ldr r2,[r1,#0x10] - ldr r6,[r12,#0x680] - smlal lr,r9,r2,r5 - smlal lr,r11,r2,r6 - ldr r2,[r1,#0x14] - ldr r7,[r12,#0x180] - smlal r5,r11,r2,r5 - smull r6,r5,r2,r6 - ldr r6,[r1,#0x18] - ldr r8,[r3,#0x600] - sub r9,r9,r5 - smlal r5,r9,r6,r7 - smull r2,r5,r6,r8 - ldr r6,[r1,#0x1c] - sub r11,r11,r5 - smlal r8,r9,r6,r8 - ldr r2,[r1,#0x20] - ldr r5,[r3,#0x200] - smlal r7,r11,r6,r7 - ldr r6,[r12,#0x580] - smlal lr,r9,r2,r5 - smlal lr,r11,r2,r6 - ldr r2,[r1,#0x24] - ldr r7,[r12,#0x280] - smlal r5,r11,r2,r5 - smull r6,r5,r2,r6 - ldr r6,[r1,#0x28] - ldr r8,[r3,#0x500] - sub r9,r9,r5 - smlal r5,r9,r6,r7 - smull r2,r5,r6,r8 - ldr r6,[r1,#0x2c] - sub r11,r11,r5 - - smlal r8,r9,r6,r8 - smlal r7,r11,r6,r7 - ldr r5,[r3,#0x300] - ldr r8,[r1,#0x30] - ldr r6,[r12,#0x480] - smlal r7,r9,r8,r5 - smlal r7,r11,r8,r6 - ldr r8,[r1,#0x34] - ldr r12,[r12,#0x380] - smlal r5,r11,r8,r5 - smull r6,r5,r8,r6 - ldr r6,[r1,#0x38] - - - ldr r3,[r3,#0x400] - sub r9,r9,r5 - smlal r7,r9,r6,r12 - smull r8,r7,r6,r3 - cmp r4,#0x210 - sub r11,r11,r7 - - ldr r2,[r1,#0x3c] - add r1,r1,#0x40 - smlal r3,r9,r2,r3 - smlal r12,r11,r2,r12 - - blt Loop_i - - mov r3,r9, asr #6 - mov r4,r3, asr #15 - teq r4,r3, asr #31 - ldr r12,LOW_16BITS - ldr r2,[sp] - eorne r3,r12,r3,asr #31 - ldr r4,[sp,#8] - mov r2,r10,lsl r2 - add r4,r4,r2,lsl #1 - strh r3,[r4] - - mov r3,r11,asr #6 - mov r4,r3,asr #15 - teq r4,r3,asr #31 - eorne r3,r12,r3,asr #31 - ldr r12,[sp,#0xc] - ldr r11,[sp,#8] - rsb r2,r2,r12,lsl #5 - add r2,r11,r2,lsl #1 - strh r3,[r2] - - add r10,r10,#1 - cmp r10,#0x10 - blt Loop_j - -; Accumulators r4, r5 Initialization - - mov r4,#0x20 - mov r5,#0x20 - mov r3,#0x10 -PolyPh_filter_loop2 - add r2,r0,r3,lsl #2 - ldr r12,[r2] - ldr r8,[r1] - ldr r6,[r2,#0x80] - smlal r12,r4,r8,r12 - ldr r12,[r1,#4] - ldr r7,[r2,#0x40] - smlal r6,r4,r12,r6 - - ldr r12,[r1,#8] - ldr r6,[r2,#0x180] - smlal r7,r5,r12,r7 - ldr r12,[r2,#0x100] - ldr r7,[r1,#0xc] - ldr r2,[r2,#0x140] - smlal r12,r4,r7,r12 - ldr r12,[r1,#0x10] - add r3,r3,#0x80 - smlal r6,r4,r12,r6 - ldr r6,[r1,#0x14] - cmp r3,#0x210 - smlal r2,r5,r6,r2 - add r1,r1,#0x18 - - blt PolyPh_filter_loop2 - mov r0,r4,asr #6 - mov r2,r0,asr #15 - teq r2,r0,asr #31 - ldrne r12,LOW_16BITS - ldr r1,[sp,#8] - eorne r0,r12,r0,asr #31 - strh r0,[r1,#0] - mov r0,r5,asr #6 - mov r2,r0,asr #15 - teq r2,r0,asr #31 - ldrne r12,LOW_16BITS - ldr r2,[sp] - mov r1,#0x10 - eorne r0,r12,r0,asr #31 - ldr r12,[sp,#8] - mov r1,r1,lsl r2 - add r1,r12,r1,lsl #1 - strh r0,[r1] - add sp,sp,#0x10 - ldmfd sp!,{r4-r11,pc} - - -PolyPh_filter_coeff - DCD pqmfSynthWin -LOW_16BITS - DCD 0x00007fff - - ENDP ; |pvmp3_polyphase_filter_window| - END - diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp index d1afd8b..338e899 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.cpp +++ b/media/libstagefright/mpeg2ts/ATSParser.cpp @@ -555,7 +555,9 @@ status_t ATSParser::Stream::parse( } #endif - return OK; + if (!payload_unit_start_indicator) { + return OK; + } } mExpectedContinuityCounter = (continuity_counter + 1) & 0x0f; diff --git a/media/mediaserver/Android.mk b/media/mediaserver/Android.mk index 786bf0d..3a280f0 100644 --- a/media/mediaserver/Android.mk +++ b/media/mediaserver/Android.mk @@ -25,7 +25,8 @@ LOCAL_SHARED_LIBRARIES := \ libmediaplayerservice \ libutils \ liblog \ - libbinder + libbinder \ + libsoundtriggerservice LOCAL_STATIC_LIBRARIES := \ libregistermsext @@ -36,7 +37,8 @@ LOCAL_C_INCLUDES := \ frameworks/av/services/audioflinger \ frameworks/av/services/audiopolicy \ frameworks/av/services/camera/libcameraservice \ - $(call include-path-for, audio-utils) + $(call include-path-for, audio-utils) \ + frameworks/av/services/soundtrigger LOCAL_MODULE:= mediaserver LOCAL_32_BIT_ONLY := true diff --git a/media/mediaserver/main_mediaserver.cpp b/media/mediaserver/main_mediaserver.cpp index a347951..af1c9e6 100644 --- a/media/mediaserver/main_mediaserver.cpp +++ b/media/mediaserver/main_mediaserver.cpp @@ -34,6 +34,7 @@ #include "MediaLogService.h" #include "MediaPlayerService.h" #include "AudioPolicyService.h" +#include "SoundTriggerHwService.h" using namespace android; @@ -128,6 +129,7 @@ int main(int argc __unused, char** argv) MediaPlayerService::instantiate(); CameraService::instantiate(); AudioPolicyService::instantiate(); + SoundTriggerHwService::instantiate(); registerExtensions(); ProcessState::self()->startThreadPool(); IPCThreadState::self()->joinThreadPool(); diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index a4cad4e..527fd65 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -431,7 +431,7 @@ status_t AudioFlinger::dump(int fd, const Vector<String16>& args) if (mLogMemoryDealer != 0) { sp<IBinder> binder = defaultServiceManager()->getService(String16("media.log")); if (binder != 0) { - fdprintf(fd, "\nmedia.log:\n"); + dprintf(fd, "\nmedia.log:\n"); Vector<String16> args; binder->dump(fd, args); } @@ -1266,7 +1266,7 @@ AudioFlinger::Client::Client(const sp<AudioFlinger>& audioFlinger, pid_t pid) : RefBase(), mAudioFlinger(audioFlinger), // FIXME should be a "k" constant not hard-coded, in .h or ro. property, see 4 lines below - mMemoryDealer(new MemoryDealer(4 * 1024*1024, "AudioFlinger::Client")), + mMemoryDealer(new MemoryDealer(1024*1024, "AudioFlinger::Client")), mPid(pid), mTimedTrackCount(0) { @@ -2609,7 +2609,7 @@ void AudioFlinger::dumpTee(int fd, const sp<NBAIO_Source>& source, audio_io_hand } } else { if (fd >= 0) { - fdprintf(fd, "unable to rotate tees in %s: %s\n", teePath, strerror(errno)); + dprintf(fd, "unable to rotate tees in %s: %s\n", teePath, strerror(errno)); } } char teeTime[16]; @@ -2663,11 +2663,11 @@ void AudioFlinger::dumpTee(int fd, const sp<NBAIO_Source>& source, audio_io_hand write(teeFd, &temp, sizeof(temp)); close(teeFd); if (fd >= 0) { - fdprintf(fd, "tee copied to %s\n", teePath); + dprintf(fd, "tee copied to %s\n", teePath); } } else { if (fd >= 0) { - fdprintf(fd, "unable to create tee %s: %s\n", teePath, strerror(errno)); + dprintf(fd, "unable to create tee %s: %s\n", teePath, strerror(errno)); } } } diff --git a/services/audioflinger/AudioResamplerDyn.cpp b/services/audioflinger/AudioResamplerDyn.cpp index a4446a4..318eb57 100644 --- a/services/audioflinger/AudioResamplerDyn.cpp +++ b/services/audioflinger/AudioResamplerDyn.cpp @@ -460,9 +460,15 @@ void AudioResamplerDyn<TC, TI, TO>::resample(TO* out, size_t outFrameCount, const uint32_t phaseIncrement = mPhaseIncrement; size_t outputIndex = 0; size_t outputSampleCount = outFrameCount * 2; // stereo output - size_t inFrameCount = getInFrameCountRequired(outFrameCount) + (phaseFraction != 0); - ALOG_ASSERT(0 < inFrameCount && inFrameCount < (1U << 31)); const uint32_t phaseWrapLimit = c.mL << c.mShift; + size_t inFrameCount = (phaseIncrement * (uint64_t)outFrameCount + phaseFraction) + / phaseWrapLimit; + // sanity check that inFrameCount is in signed 32 bit integer range. + ALOG_ASSERT(0 <= inFrameCount && inFrameCount < (1U << 31)); + + //ALOGV("inFrameCount:%d outFrameCount:%d" + // " phaseIncrement:%u phaseFraction:%u phaseWrapLimit:%u", + // inFrameCount, outFrameCount, phaseIncrement, phaseFraction, phaseWrapLimit); // NOTE: be very careful when modifying the code here. register // pressure is very high and a small change might cause the compiler @@ -472,10 +478,17 @@ void AudioResamplerDyn<TC, TI, TO>::resample(TO* out, size_t outFrameCount, // the following logic is a bit convoluted to keep the main processing loop // as tight as possible with register allocation. while (outputIndex < outputSampleCount) { - // buffer is empty, fetch a new one - while (mBuffer.frameCount == 0) { + //ALOGV("LOOP: inFrameCount:%d outputIndex:%d outFrameCount:%d" + // " phaseFraction:%u phaseWrapLimit:%u", + // inFrameCount, outputIndex, outFrameCount, phaseFraction, phaseWrapLimit); + + // check inputIndex overflow + ALOG_ASSERT(inputIndex <= mBuffer.frameCount, "inputIndex%d > frameCount%d", + inputIndex, mBuffer.frameCount); + // Buffer is empty, fetch a new one if necessary (inFrameCount > 0). + // We may not fetch a new buffer if the existing data is sufficient. + while (mBuffer.frameCount == 0 && inFrameCount > 0) { mBuffer.frameCount = inFrameCount; - ALOG_ASSERT(inFrameCount > 0); provider->getNextBuffer(&mBuffer, calculateOutputPTS(outputIndex / 2)); if (mBuffer.raw == NULL) { @@ -486,9 +499,9 @@ void AudioResamplerDyn<TC, TI, TO>::resample(TO* out, size_t outFrameCount, mInBuffer.template readAdvance<CHANNELS>( impulse, c.mHalfNumCoefs, reinterpret_cast<TI*>(mBuffer.raw), inputIndex); + inputIndex++; phaseFraction -= phaseWrapLimit; while (phaseFraction >= phaseWrapLimit) { - inputIndex++; if (inputIndex >= mBuffer.frameCount) { inputIndex = 0; provider->releaseBuffer(&mBuffer); @@ -497,6 +510,7 @@ void AudioResamplerDyn<TC, TI, TO>::resample(TO* out, size_t outFrameCount, mInBuffer.template readAdvance<CHANNELS>( impulse, c.mHalfNumCoefs, reinterpret_cast<TI*>(mBuffer.raw), inputIndex); + inputIndex++; phaseFraction -= phaseWrapLimit; } } @@ -507,9 +521,6 @@ void AudioResamplerDyn<TC, TI, TO>::resample(TO* out, size_t outFrameCount, const int halfNumCoefs = c.mHalfNumCoefs; const TO* const volumeSimd = mVolumeSimd; - // reread the last input in. - mInBuffer.template readAgain<CHANNELS>(impulse, halfNumCoefs, in, inputIndex); - // main processing loop while (CC_LIKELY(outputIndex < outputSampleCount)) { // caution: fir() is inlined and may be large. @@ -518,6 +529,10 @@ void AudioResamplerDyn<TC, TI, TO>::resample(TO* out, size_t outFrameCount, // from the input samples in impulse[-halfNumCoefs+1]... impulse[halfNumCoefs] // from the polyphase filter of (phaseFraction / phaseWrapLimit) in coefs. // + //ALOGV("LOOP2: inFrameCount:%d outputIndex:%d outFrameCount:%d" + // " phaseFraction:%u phaseWrapLimit:%u", + // inFrameCount, outputIndex, outFrameCount, phaseFraction, phaseWrapLimit); + ALOG_ASSERT(phaseFraction < phaseWrapLimit); fir<CHANNELS, LOCKED, STRIDE>( &out[outputIndex], phaseFraction, phaseWrapLimit, @@ -527,17 +542,20 @@ void AudioResamplerDyn<TC, TI, TO>::resample(TO* out, size_t outFrameCount, phaseFraction += phaseIncrement; while (phaseFraction >= phaseWrapLimit) { - inputIndex++; if (inputIndex >= frameCount) { goto done; // need a new buffer } mInBuffer.template readAdvance<CHANNELS>(impulse, halfNumCoefs, in, inputIndex); + inputIndex++; phaseFraction -= phaseWrapLimit; } } done: - // often arrives here when input buffer runs out - if (inputIndex >= frameCount) { + // We arrive here when we're finished or when the input buffer runs out. + // Regardless we need to release the input buffer if we've acquired it. + if (inputIndex > 0) { // we've acquired a buffer (alternatively could check frameCount) + ALOG_ASSERT(inputIndex == frameCount, "inputIndex(%d) != frameCount(%d)", + inputIndex, frameCount); // must have been fully read. inputIndex = 0; provider->releaseBuffer(&mBuffer); ALOG_ASSERT(mBuffer.frameCount == 0); @@ -545,14 +563,12 @@ done: } resample_exit: - // Release frames to avoid the count being inaccurate for pts timing. - // TODO: Avoid this extra check by making fetch count exact. This is tricky - // due to the overfetching mechanism which loads unnecessarily when - // mBuffer.frameCount == 0. - if (inputIndex) { - mBuffer.frameCount = inputIndex; - provider->releaseBuffer(&mBuffer); - } + // inputIndex must be zero in all three cases: + // (1) the buffer never was been acquired; (2) the buffer was + // released at "done:"; or (3) getNextBuffer() failed. + ALOG_ASSERT(inputIndex == 0, "Releasing: inputindex:%d frameCount:%d phaseFraction:%u", + inputIndex, mBuffer.frameCount, phaseFraction); + ALOG_ASSERT(mBuffer.frameCount == 0); // there must be no frames in the buffer mInBuffer.setImpulse(impulse); mPhaseFraction = phaseFraction; } diff --git a/services/audioflinger/AudioWatchdog.cpp b/services/audioflinger/AudioWatchdog.cpp index 93d185e..877e776 100644 --- a/services/audioflinger/AudioWatchdog.cpp +++ b/services/audioflinger/AudioWatchdog.cpp @@ -34,7 +34,7 @@ void AudioWatchdogDump::dump(int fd) } else { strcpy(buf, "N/A\n"); } - fdprintf(fd, "Watchdog: underruns=%u, logs=%u, most recent underrun log at %s", + dprintf(fd, "Watchdog: underruns=%u, logs=%u, most recent underrun log at %s", mUnderruns, mLogs, buf); } diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp index c9a3f10..c486630 100644 --- a/services/audioflinger/FastMixer.cpp +++ b/services/audioflinger/FastMixer.cpp @@ -26,7 +26,6 @@ #define ATRACE_TAG ATRACE_TAG_AUDIO #include "Configuration.h" -#include <sys/atomics.h> #include <time.h> #include <utils/Log.h> #include <utils/Trace.h> @@ -492,7 +491,7 @@ static int compare_uint32_t(const void *pa, const void *pb) void FastMixerDumpState::dump(int fd) const { if (mCommand == FastMixerState::INITIAL) { - fdprintf(fd, " FastMixer not initialized\n"); + dprintf(fd, " FastMixer not initialized\n"); return; } #define COMMAND_MAX 32 @@ -526,10 +525,10 @@ void FastMixerDumpState::dump(int fd) const double measuredWarmupMs = (mMeasuredWarmupTs.tv_sec * 1000.0) + (mMeasuredWarmupTs.tv_nsec / 1000000.0); double mixPeriodSec = (double) mFrameCount / (double) mSampleRate; - fdprintf(fd, " FastMixer command=%s writeSequence=%u framesWritten=%u\n" - " numTracks=%u writeErrors=%u underruns=%u overruns=%u\n" - " sampleRate=%u frameCount=%zu measuredWarmup=%.3g ms, warmupCycles=%u\n" - " mixPeriod=%.2f ms\n", + dprintf(fd, " FastMixer command=%s writeSequence=%u framesWritten=%u\n" + " numTracks=%u writeErrors=%u underruns=%u overruns=%u\n" + " sampleRate=%u frameCount=%zu measuredWarmup=%.3g ms, warmupCycles=%u\n" + " mixPeriod=%.2f ms\n", string, mWriteSequence, mFramesWritten, mNumTracks, mWriteErrors, mUnderruns, mOverruns, mSampleRate, mFrameCount, measuredWarmupMs, mWarmupCycles, @@ -581,26 +580,26 @@ void FastMixerDumpState::dump(int fd) const #endif } if (n) { - fdprintf(fd, " Simple moving statistics over last %.1f seconds:\n", - wall.n() * mixPeriodSec); - fdprintf(fd, " wall clock time in ms per mix cycle:\n" - " mean=%.2f min=%.2f max=%.2f stddev=%.2f\n", - wall.mean()*1e-6, wall.minimum()*1e-6, wall.maximum()*1e-6, - wall.stddev()*1e-6); - fdprintf(fd, " raw CPU load in us per mix cycle:\n" - " mean=%.0f min=%.0f max=%.0f stddev=%.0f\n", - loadNs.mean()*1e-3, loadNs.minimum()*1e-3, loadNs.maximum()*1e-3, - loadNs.stddev()*1e-3); + dprintf(fd, " Simple moving statistics over last %.1f seconds:\n", + wall.n() * mixPeriodSec); + dprintf(fd, " wall clock time in ms per mix cycle:\n" + " mean=%.2f min=%.2f max=%.2f stddev=%.2f\n", + wall.mean()*1e-6, wall.minimum()*1e-6, wall.maximum()*1e-6, + wall.stddev()*1e-6); + dprintf(fd, " raw CPU load in us per mix cycle:\n" + " mean=%.0f min=%.0f max=%.0f stddev=%.0f\n", + loadNs.mean()*1e-3, loadNs.minimum()*1e-3, loadNs.maximum()*1e-3, + loadNs.stddev()*1e-3); } else { - fdprintf(fd, " No FastMixer statistics available currently\n"); + dprintf(fd, " No FastMixer statistics available currently\n"); } #ifdef CPU_FREQUENCY_STATISTICS - fdprintf(fd, " CPU clock frequency in MHz:\n" - " mean=%.0f min=%.0f max=%.0f stddev=%.0f\n", - kHz.mean()*1e-3, kHz.minimum()*1e-3, kHz.maximum()*1e-3, kHz.stddev()*1e-3); - fdprintf(fd, " adjusted CPU load in MHz (i.e. normalized for CPU clock frequency):\n" - " mean=%.1f min=%.1f max=%.1f stddev=%.1f\n", - loadMHz.mean(), loadMHz.minimum(), loadMHz.maximum(), loadMHz.stddev()); + dprintf(fd, " CPU clock frequency in MHz:\n" + " mean=%.0f min=%.0f max=%.0f stddev=%.0f\n", + kHz.mean()*1e-3, kHz.minimum()*1e-3, kHz.maximum()*1e-3, kHz.stddev()*1e-3); + dprintf(fd, " adjusted CPU load in MHz (i.e. normalized for CPU clock frequency):\n" + " mean=%.1f min=%.1f max=%.1f stddev=%.1f\n", + loadMHz.mean(), loadMHz.minimum(), loadMHz.maximum(), loadMHz.stddev()); #endif if (tail != NULL) { qsort(tail, n, sizeof(uint32_t), compare_uint32_t); @@ -611,12 +610,12 @@ void FastMixerDumpState::dump(int fd) const left.sample(tail[i]); right.sample(tail[n - (i + 1)]); } - fdprintf(fd, " Distribution of mix cycle times in ms for the tails (> ~3 stddev outliers):\n" - " left tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n" - " right tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n", - left.mean()*1e-6, left.minimum()*1e-6, left.maximum()*1e-6, left.stddev()*1e-6, - right.mean()*1e-6, right.minimum()*1e-6, right.maximum()*1e-6, - right.stddev()*1e-6); + dprintf(fd, " Distribution of mix cycle times in ms for the tails (> ~3 stddev outliers):\n" + " left tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n" + " right tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n", + left.mean()*1e-6, left.minimum()*1e-6, left.maximum()*1e-6, left.stddev()*1e-6, + right.mean()*1e-6, right.minimum()*1e-6, right.maximum()*1e-6, + right.stddev()*1e-6); delete[] tail; } #endif @@ -626,9 +625,9 @@ void FastMixerDumpState::dump(int fd) const // Instead we always display all tracks, with an indication // of whether we think the track is active. uint32_t trackMask = mTrackMask; - fdprintf(fd, " Fast tracks: kMaxFastTracks=%u activeMask=%#x\n", + dprintf(fd, " Fast tracks: kMaxFastTracks=%u activeMask=%#x\n", FastMixerState::kMaxFastTracks, trackMask); - fdprintf(fd, " Index Active Full Partial Empty Recent Ready\n"); + dprintf(fd, " Index Active Full Partial Empty Recent Ready\n"); for (uint32_t i = 0; i < FastMixerState::kMaxFastTracks; ++i, trackMask >>= 1) { bool isActive = trackMask & 1; const FastTrackDump *ftDump = &mTracks[i]; @@ -648,7 +647,7 @@ void FastMixerDumpState::dump(int fd) const mostRecent = "?"; break; } - fdprintf(fd, " %5u %6s %4u %7u %5u %7s %5zu\n", i, isActive ? "yes" : "no", + dprintf(fd, " %5u %6s %4u %7u %5u %7s %5zu\n", i, isActive ? "yes" : "no", (underruns.mBitFields.mFull) & UNDERRUN_MASK, (underruns.mBitFields.mPartial) & UNDERRUN_MASK, (underruns.mBitFields.mEmpty) & UNDERRUN_MASK, diff --git a/services/audioflinger/StateQueue.cpp b/services/audioflinger/StateQueue.cpp index 48399c0..7e01c9f 100644 --- a/services/audioflinger/StateQueue.cpp +++ b/services/audioflinger/StateQueue.cpp @@ -28,12 +28,12 @@ namespace android { #ifdef STATE_QUEUE_DUMP void StateQueueObserverDump::dump(int fd) { - fdprintf(fd, "State queue observer: stateChanges=%u\n", mStateChanges); + dprintf(fd, "State queue observer: stateChanges=%u\n", mStateChanges); } void StateQueueMutatorDump::dump(int fd) { - fdprintf(fd, "State queue mutator: pushDirty=%u pushAck=%u blockedSequence=%u\n", + dprintf(fd, "State queue mutator: pushDirty=%u pushAck=%u blockedSequence=%u\n", mPushDirty, mPushAck, mBlockedSequence); } #endif diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index d6333be..d08c966 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -574,30 +574,30 @@ void AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args __u bool locked = AudioFlinger::dumpTryLock(mLock); if (!locked) { - fdprintf(fd, "thread %p maybe dead locked\n", this); + dprintf(fd, "thread %p maybe dead locked\n", this); } - fdprintf(fd, " I/O handle: %d\n", mId); - fdprintf(fd, " TID: %d\n", getTid()); - fdprintf(fd, " Standby: %s\n", mStandby ? "yes" : "no"); - fdprintf(fd, " Sample rate: %u\n", mSampleRate); - fdprintf(fd, " HAL frame count: %zu\n", mFrameCount); - fdprintf(fd, " HAL buffer size: %u bytes\n", mBufferSize); - fdprintf(fd, " Channel Count: %u\n", mChannelCount); - fdprintf(fd, " Channel Mask: 0x%08x (%s)\n", mChannelMask, + dprintf(fd, " I/O handle: %d\n", mId); + dprintf(fd, " TID: %d\n", getTid()); + dprintf(fd, " Standby: %s\n", mStandby ? "yes" : "no"); + dprintf(fd, " Sample rate: %u\n", mSampleRate); + dprintf(fd, " HAL frame count: %zu\n", mFrameCount); + dprintf(fd, " HAL buffer size: %u bytes\n", mBufferSize); + dprintf(fd, " Channel Count: %u\n", mChannelCount); + dprintf(fd, " Channel Mask: 0x%08x (%s)\n", mChannelMask, channelMaskToString(mChannelMask, mType != RECORD).string()); - fdprintf(fd, " Format: 0x%x (%s)\n", mFormat, formatToString(mFormat)); - fdprintf(fd, " Frame size: %zu\n", mFrameSize); - fdprintf(fd, " Pending config events:"); + dprintf(fd, " Format: 0x%x (%s)\n", mFormat, formatToString(mFormat)); + dprintf(fd, " Frame size: %zu\n", mFrameSize); + dprintf(fd, " Pending config events:"); size_t numConfig = mConfigEvents.size(); if (numConfig) { for (size_t i = 0; i < numConfig; i++) { mConfigEvents[i]->dump(buffer, SIZE); - fdprintf(fd, "\n %s", buffer); + dprintf(fd, "\n %s", buffer); } - fdprintf(fd, "\n"); + dprintf(fd, "\n"); } else { - fdprintf(fd, " none\n"); + dprintf(fd, " none\n"); } if (locked) { @@ -1260,15 +1260,15 @@ void AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16>& ar // These values are "raw"; they will wrap around. See prepareTracks_l() for a better way. FastTrackUnderruns underruns = getFastTrackUnderruns(0); - fdprintf(fd, " Normal mixer raw underrun counters: partial=%u empty=%u\n", + dprintf(fd, " Normal mixer raw underrun counters: partial=%u empty=%u\n", underruns.mBitFields.mPartial, underruns.mBitFields.mEmpty); size_t numtracks = mTracks.size(); size_t numactive = mActiveTracks.size(); - fdprintf(fd, " %d Tracks", numtracks); + dprintf(fd, " %d Tracks", numtracks); size_t numactiveseen = 0; if (numtracks) { - fdprintf(fd, " of which %d are active\n", numactive); + dprintf(fd, " of which %d are active\n", numactive); Track::appendDumpHeader(result); for (size_t i = 0; i < numtracks; ++i) { sp<Track> track = mTracks[i]; @@ -1300,22 +1300,21 @@ void AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16>& ar } write(fd, result.string(), result.size()); - } void AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String16>& args) { - fdprintf(fd, "\nOutput thread %p:\n", this); - fdprintf(fd, " Normal frame count: %zu\n", mNormalFrameCount); - fdprintf(fd, " Last write occurred (msecs): %llu\n", ns2ms(systemTime() - mLastWriteTime)); - fdprintf(fd, " Total writes: %d\n", mNumWrites); - fdprintf(fd, " Delayed writes: %d\n", mNumDelayedWrites); - fdprintf(fd, " Blocked in write: %s\n", mInWrite ? "yes" : "no"); - fdprintf(fd, " Suspend count: %d\n", mSuspended); - fdprintf(fd, " Sink buffer : %p\n", mSinkBuffer); - fdprintf(fd, " Mixer buffer: %p\n", mMixerBuffer); - fdprintf(fd, " Effect buffer: %p\n", mEffectBuffer); - fdprintf(fd, " Fast track availMask=%#x\n", mFastTrackAvailMask); + dprintf(fd, "\nOutput thread %p:\n", this); + dprintf(fd, " Normal frame count: %zu\n", mNormalFrameCount); + dprintf(fd, " Last write occurred (msecs): %llu\n", ns2ms(systemTime() - mLastWriteTime)); + dprintf(fd, " Total writes: %d\n", mNumWrites); + dprintf(fd, " Delayed writes: %d\n", mNumDelayedWrites); + dprintf(fd, " Blocked in write: %s\n", mInWrite ? "yes" : "no"); + dprintf(fd, " Suspend count: %d\n", mSuspended); + dprintf(fd, " Sink buffer : %p\n", mSinkBuffer); + dprintf(fd, " Mixer buffer: %p\n", mMixerBuffer); + dprintf(fd, " Effect buffer: %p\n", mEffectBuffer); + dprintf(fd, " Fast track availMask=%#x\n", mFastTrackAvailMask); dumpBase(fd, args); } @@ -3799,7 +3798,7 @@ void AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16>& ar PlaybackThread::dumpInternals(fd, args); - fdprintf(fd, " AudioMixer tracks: 0x%08x\n", mAudioMixer->trackNames()); + dprintf(fd, " AudioMixer tracks: 0x%08x\n", mAudioMixer->trackNames()); // Make a non-atomic copy of fast mixer dump state so it won't change underneath us const FastMixerDumpState copy(mFastMixerDumpState); @@ -5717,12 +5716,12 @@ void AudioFlinger::RecordThread::dump(int fd, const Vector<String16>& args) void AudioFlinger::RecordThread::dumpInternals(int fd, const Vector<String16>& args) { - fdprintf(fd, "\nInput thread %p:\n", this); + dprintf(fd, "\nInput thread %p:\n", this); if (mActiveTracks.size() > 0) { - fdprintf(fd, " Buffer size: %zu bytes\n", mBufferSize); + dprintf(fd, " Buffer size: %zu bytes\n", mBufferSize); } else { - fdprintf(fd, " No active record clients\n"); + dprintf(fd, " No active record clients\n"); } dprintf(fd, " Fast track available: %s\n", mFastTrackAvail ? "yes" : "no"); @@ -5738,9 +5737,9 @@ void AudioFlinger::RecordThread::dumpTracks(int fd, const Vector<String16>& args size_t numtracks = mTracks.size(); size_t numactive = mActiveTracks.size(); size_t numactiveseen = 0; - fdprintf(fd, " %d Tracks", numtracks); + dprintf(fd, " %d Tracks", numtracks); if (numtracks) { - fdprintf(fd, " of which %d are active\n", numactive); + dprintf(fd, " of which %d are active\n", numactive); RecordTrack::appendDumpHeader(result); for (size_t i = 0; i < numtracks ; ++i) { sp<RecordTrack> track = mTracks[i]; @@ -5754,7 +5753,7 @@ void AudioFlinger::RecordThread::dumpTracks(int fd, const Vector<String16>& args } } } else { - fdprintf(fd, "\n"); + dprintf(fd, "\n"); } if (numactiveseen != numactive) { diff --git a/services/audioflinger/tests/Android.mk b/services/audioflinger/tests/Android.mk new file mode 100644 index 0000000..874f18f --- /dev/null +++ b/services/audioflinger/tests/Android.mk @@ -0,0 +1,31 @@ +# Build the unit tests for audioflinger + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SHARED_LIBRARIES := \ + liblog \ + libutils \ + libcutils \ + libstlport \ + libaudioutils \ + libaudioresampler + +LOCAL_STATIC_LIBRARIES := \ + libgtest \ + libgtest_main + +LOCAL_C_INCLUDES := \ + bionic \ + bionic/libstdc++/include \ + external/gtest/include \ + external/stlport/stlport \ + frameworks/av/services/audioflinger + +LOCAL_SRC_FILES := \ + resampler_tests.cpp + +LOCAL_MODULE := resampler_tests +LOCAL_MODULE_TAGS := tests + +include $(BUILD_EXECUTABLE) diff --git a/services/audioflinger/tests/build_and_run_all_unit_tests.sh b/services/audioflinger/tests/build_and_run_all_unit_tests.sh new file mode 100755 index 0000000..2c453b0 --- /dev/null +++ b/services/audioflinger/tests/build_and_run_all_unit_tests.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +if [ -z "$ANDROID_BUILD_TOP" ]; then + echo "Android build environment not set" + exit -1 +fi + +# ensure we have mm +. $ANDROID_BUILD_TOP/build/envsetup.sh + +pushd $ANDROID_BUILD_TOP/frameworks/av/services/audioflinger/ +pwd +mm + +echo "waiting for device" +adb root && adb wait-for-device remount +adb push $OUT/system/lib/libaudioresampler.so /system/lib +adb push $OUT/system/bin/resampler_tests /system/bin + +sh $ANDROID_BUILD_TOP/frameworks/av/services/audioflinger/tests/run_all_unit_tests.sh + +popd diff --git a/services/audioflinger/tests/resampler_tests.cpp b/services/audioflinger/tests/resampler_tests.cpp new file mode 100644 index 0000000..8f9c270 --- /dev/null +++ b/services/audioflinger/tests/resampler_tests.cpp @@ -0,0 +1,471 @@ +/* + * Copyright (C) 2014 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 "audioflinger_resampler_tests" + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <errno.h> +#include <time.h> +#include <math.h> +#include <vector> +#include <utility> +#include <cutils/log.h> +#include <gtest/gtest.h> +#include <media/AudioBufferProvider.h> +#include "AudioResampler.h" + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +template<typename T, typename U> +struct is_same +{ + static const bool value = false; +}; + +template<typename T> +struct is_same<T, T> // partial specialization +{ + static const bool value = true; +}; + +template<typename T> +static inline T convertValue(double val) +{ + if (is_same<T, int16_t>::value) { + return floor(val * 32767.0 + 0.5); + } else if (is_same<T, int32_t>::value) { + return floor(val * (1UL<<31) + 0.5); + } + return val; // assume float or double +} + +/* Creates a type-independent audio buffer provider from + * a buffer base address, size, framesize, and input increment array. + * + * No allocation or deallocation of the provided buffer is done. + */ +class TestProvider : public android::AudioBufferProvider { +public: + TestProvider(const void* addr, size_t frames, size_t frameSize, + const std::vector<size_t>& inputIncr) + : mAddr(addr), + mNumFrames(frames), + mFrameSize(frameSize), + mNextFrame(0), mUnrel(0), mInputIncr(inputIncr), mNextIdx(0) + { + } + + virtual android::status_t getNextBuffer(Buffer* buffer, int64_t pts __unused = kInvalidPTS ) + { + size_t requestedFrames = buffer->frameCount; + if (requestedFrames > mNumFrames - mNextFrame) { + buffer->frameCount = mNumFrames - mNextFrame; + } + if (!mInputIncr.empty()) { + size_t provided = mInputIncr[mNextIdx++]; + ALOGV("getNextBuffer() mValue[%d]=%u not %u", + mNextIdx-1, provided, buffer->frameCount); + if (provided < buffer->frameCount) { + buffer->frameCount = provided; + } + if (mNextIdx >= mInputIncr.size()) { + mNextIdx = 0; + } + } + ALOGV("getNextBuffer() requested %u frames out of %u frames available" + " and returned %u frames\n", + requestedFrames, mNumFrames - mNextFrame, buffer->frameCount); + mUnrel = buffer->frameCount; + if (buffer->frameCount > 0) { + buffer->raw = (char *)mAddr + mFrameSize * mNextFrame; + return android::NO_ERROR; + } else { + buffer->raw = NULL; + return android::NOT_ENOUGH_DATA; + } + } + + virtual void releaseBuffer(Buffer* buffer) + { + if (buffer->frameCount > mUnrel) { + ALOGE("releaseBuffer() released %u frames but only %u available " + "to release\n", buffer->frameCount, mUnrel); + mNextFrame += mUnrel; + mUnrel = 0; + } else { + + ALOGV("releaseBuffer() released %u frames out of %u frames available " + "to release\n", buffer->frameCount, mUnrel); + mNextFrame += buffer->frameCount; + mUnrel -= buffer->frameCount; + } + buffer->frameCount = 0; + buffer->raw = NULL; + } + + void reset() + { + mNextFrame = 0; + } + + size_t getNumFrames() + { + return mNumFrames; + } + + void setIncr(const std::vector<size_t> inputIncr) + { + mNextIdx = 0; + mInputIncr = inputIncr; + } + +protected: + const void* mAddr; // base address + size_t mNumFrames; // total frames + int mFrameSize; // frame size (# channels * bytes per sample) + size_t mNextFrame; // index of next frame to provide + size_t mUnrel; // number of frames not yet released + std::vector<size_t> mInputIncr; // number of frames provided per call + size_t mNextIdx; // index of next entry in mInputIncr to use +}; + +/* Creates a buffer filled with a sine wave. + * + * Returns a pair consisting of the sine signal buffer and the number of frames. + * The caller must delete[] the buffer when no longer needed (no shared_ptr<>). + */ +template<typename T> +static std::pair<T*, size_t> createSine(size_t channels, + double freq, double samplingRate, double time) +{ + double tscale = 1. / samplingRate; + size_t frames = static_cast<size_t>(samplingRate * time); + T* buffer = new T[frames * channels]; + for (size_t i = 0; i < frames; ++i) { + double t = i * tscale; + double y = sin(2. * M_PI * freq * t); + T yt = convertValue<T>(y); + + for (size_t j = 0; j < channels; ++j) { + buffer[i*channels + j] = yt / (j + 1); + } + } + return std::make_pair(buffer, frames); +} + +/* Creates a buffer filled with a chirp signal (a sine wave sweep). + * + * Returns a pair consisting of the chirp signal buffer and the number of frames. + * The caller must delete[] the buffer when no longer needed (no shared_ptr<>). + * + * When creating the Chirp, note that the frequency is the true sinusoidal + * frequency not the sampling rate. + * + * http://en.wikipedia.org/wiki/Chirp + */ +template<typename T> +static std::pair<T*, size_t> createChirp(size_t channels, + double minfreq, double maxfreq, double samplingRate, double time) +{ + double tscale = 1. / samplingRate; + size_t frames = static_cast<size_t>(samplingRate * time); + T *buffer = new T[frames * channels]; + // note the chirp constant k has a divide-by-two. + double k = (maxfreq - minfreq) / (2. * time); + for (size_t i = 0; i < frames; ++i) { + double t = i * tscale; + double y = sin(2. * M_PI * (k * t + minfreq) * t); + T yt = convertValue<T>(y); + + for (size_t j = 0; j < channels; ++j) { + buffer[i*channels + j] = yt / (j + 1); + } + } + return std::make_pair(buffer, frames); +} + +/* This derived class creates a buffer provider of datatype T, + * consisting of an input signal, e.g. from createChirp(). + * The number of frames can be obtained from the base class + * TestProvider::getNumFrames(). + */ +template <typename T> +class SignalProvider : public TestProvider { +public: + SignalProvider(const std::pair<T*, size_t>& bufferInfo, size_t channels, + const std::vector<size_t>& values) + : TestProvider(bufferInfo.first, bufferInfo.second, channels * sizeof(T), values), + mManagedPtr(bufferInfo.first) + { + } + + virtual ~SignalProvider() + { + delete[] mManagedPtr; + } + +protected: + T* mManagedPtr; +}; + +void resample(void *output, size_t outputFrames, const std::vector<size_t> &outputIncr, + android::AudioBufferProvider *provider, android::AudioResampler *resampler) +{ + for (size_t i = 0, j = 0; i < outputFrames; ) { + size_t thisFrames = outputIncr[j++]; + if (j >= outputIncr.size()) { + j = 0; + } + if (thisFrames == 0 || thisFrames > outputFrames - i) { + thisFrames = outputFrames - i; + } + resampler->resample((int32_t*) output + 2*i, thisFrames, provider); + i += thisFrames; + } +} + +void buffercmp(const void *reference, const void *test, + size_t outputFrameSize, size_t outputFrames) +{ + for (size_t i = 0; i < outputFrames; ++i) { + int check = memcmp((const char*)reference + i * outputFrameSize, + (const char*)test + i * outputFrameSize, outputFrameSize); + if (check) { + ALOGE("Failure at frame %d", i); + ASSERT_EQ(check, 0); /* fails */ + } + } +} + +void testBufferIncrement(size_t channels, unsigned inputFreq, unsigned outputFreq, + enum android::AudioResampler::src_quality quality) +{ + // create the provider + std::vector<size_t> inputIncr; + SignalProvider<int16_t> provider(createChirp<int16_t>(channels, + 0., outputFreq/2., outputFreq, outputFreq/2000.), + channels, inputIncr); + + // calculate the output size + size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq; + size_t outputFrameSize = 2 * sizeof(int32_t); + size_t outputSize = outputFrameSize * outputFrames; + outputSize &= ~7; + + // create the resampler + const int volumePrecision = 12; /* typical unity gain */ + android::AudioResampler* resampler; + + resampler = android::AudioResampler::create(16, channels, outputFreq, quality); + resampler->setSampleRate(inputFreq); + resampler->setVolume(1 << volumePrecision, 1 << volumePrecision); + + // set up the reference run + std::vector<size_t> refIncr; + refIncr.push_back(outputFrames); + void* reference = malloc(outputSize); + resample(reference, outputFrames, refIncr, &provider, resampler); + + provider.reset(); + +#if 0 + /* this test will fail - API interface issue: reset() does not clear internal buffers */ + resampler->reset(); +#else + delete resampler; + resampler = android::AudioResampler::create(16, channels, outputFreq, quality); + resampler->setSampleRate(inputFreq); + resampler->setVolume(1 << volumePrecision, 1 << volumePrecision); +#endif + + // set up the test run + std::vector<size_t> outIncr; + outIncr.push_back(1); + outIncr.push_back(2); + outIncr.push_back(3); + void* test = malloc(outputSize); + resample(test, outputFrames, outIncr, &provider, resampler); + + // check + buffercmp(reference, test, outputFrameSize, outputFrames); + + free(reference); + free(test); + delete resampler; +} + +template <typename T> +inline double sqr(T v) +{ + double dv = static_cast<double>(v); + return dv * dv; +} + +template <typename T> +double signalEnergy(T *start, T *end, unsigned stride) +{ + double accum = 0; + + for (T *p = start; p < end; p += stride) { + accum += sqr(*p); + } + unsigned count = (end - start + stride - 1) / stride; + return accum / count; +} + +void testStopbandDownconversion(size_t channels, + unsigned inputFreq, unsigned outputFreq, + unsigned passband, unsigned stopband, + enum android::AudioResampler::src_quality quality) +{ + // create the provider + std::vector<size_t> inputIncr; + SignalProvider<int16_t> provider(createChirp<int16_t>(channels, + 0., inputFreq/2., inputFreq, inputFreq/2000.), + channels, inputIncr); + + // calculate the output size + size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq; + size_t outputFrameSize = 2 * sizeof(int32_t); + size_t outputSize = outputFrameSize * outputFrames; + outputSize &= ~7; + + // create the resampler + const int volumePrecision = 12; /* typical unity gain */ + android::AudioResampler* resampler; + + resampler = android::AudioResampler::create(16, channels, outputFreq, quality); + resampler->setSampleRate(inputFreq); + resampler->setVolume(1 << volumePrecision, 1 << volumePrecision); + + // set up the reference run + std::vector<size_t> refIncr; + refIncr.push_back(outputFrames); + void* reference = malloc(outputSize); + resample(reference, outputFrames, refIncr, &provider, resampler); + + int32_t *out = reinterpret_cast<int32_t *>(reference); + + // check signal energy in passband + const unsigned passbandFrame = passband * outputFreq / 1000.; + const unsigned stopbandFrame = stopband * outputFreq / 1000.; + + // check each channel separately + for (size_t i = 0; i < channels; ++i) { + double passbandEnergy = signalEnergy(out, out + passbandFrame * channels, channels); + double stopbandEnergy = signalEnergy(out + stopbandFrame * channels, + out + outputFrames * channels, channels); + double dbAtten = -10. * log10(stopbandEnergy / passbandEnergy); + ASSERT_GT(dbAtten, 60.); + +#if 0 + // internal verification + printf("if:%d of:%d pbf:%d sbf:%d sbe: %f pbe: %f db: %.2f\n", + provider.getNumFrames(), outputFrames, + passbandFrame, stopbandFrame, stopbandEnergy, passbandEnergy, dbAtten); + for (size_t i = 0; i < 10; ++i) { + printf("%d\n", out[i+passbandFrame*channels]); + } + for (size_t i = 0; i < 10; ++i) { + printf("%d\n", out[i+stopbandFrame*channels]); + } +#endif + } + + free(reference); + delete resampler; +} + +/* Buffer increment test + * + * We compare a reference output, where we consume and process the entire + * buffer at a time, and a test output, where we provide small chunks of input + * data and process small chunks of output (which may not be equivalent in size). + * + * Two subtests - fixed phase (3:2 down) and interpolated phase (147:320 up) + */ +TEST(audioflinger_resampler, bufferincrement_fixedphase) { + // all of these work + static const enum android::AudioResampler::src_quality kQualityArray[] = { + android::AudioResampler::LOW_QUALITY, + android::AudioResampler::MED_QUALITY, + android::AudioResampler::HIGH_QUALITY, + android::AudioResampler::VERY_HIGH_QUALITY, + android::AudioResampler::DYN_LOW_QUALITY, + android::AudioResampler::DYN_MED_QUALITY, + android::AudioResampler::DYN_HIGH_QUALITY, + }; + + for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { + testBufferIncrement(2, 48000, 32000, kQualityArray[i]); + } +} + +TEST(audioflinger_resampler, bufferincrement_interpolatedphase) { + // all of these work except low quality + static const enum android::AudioResampler::src_quality kQualityArray[] = { +// android::AudioResampler::LOW_QUALITY, + android::AudioResampler::MED_QUALITY, + android::AudioResampler::HIGH_QUALITY, + android::AudioResampler::VERY_HIGH_QUALITY, + android::AudioResampler::DYN_LOW_QUALITY, + android::AudioResampler::DYN_MED_QUALITY, + android::AudioResampler::DYN_HIGH_QUALITY, + }; + + for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { + testBufferIncrement(2, 22050, 48000, kQualityArray[i]); + } +} + +/* Simple aliasing test + * + * This checks stopband response of the chirp signal to make sure frequencies + * are properly suppressed. It uses downsampling because the stopband can be + * clearly isolated by input frequencies exceeding the output sample rate (nyquist). + */ +TEST(audioflinger_resampler, stopbandresponse) { + // not all of these may work (old resamplers fail on downsampling) + static const enum android::AudioResampler::src_quality kQualityArray[] = { + //android::AudioResampler::LOW_QUALITY, + //android::AudioResampler::MED_QUALITY, + //android::AudioResampler::HIGH_QUALITY, + //android::AudioResampler::VERY_HIGH_QUALITY, + android::AudioResampler::DYN_LOW_QUALITY, + android::AudioResampler::DYN_MED_QUALITY, + android::AudioResampler::DYN_HIGH_QUALITY, + }; + + // in this test we assume a maximum transition band between 12kHz and 20kHz. + // there must be at least 60dB relative attenuation between stopband and passband. + for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { + testStopbandDownconversion(2, 48000, 32000, 12000, 20000, kQualityArray[i]); + } + + // in this test we assume a maximum transition band between 7kHz and 15kHz. + // there must be at least 60dB relative attenuation between stopband and passband. + // (the weird ratio triggers interpolative resampling) + for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { + testStopbandDownconversion(2, 48000, 22101, 7000, 15000, kQualityArray[i]); + } +} diff --git a/services/audioflinger/tests/run_all_unit_tests.sh b/services/audioflinger/tests/run_all_unit_tests.sh new file mode 100755 index 0000000..ffae6ae --- /dev/null +++ b/services/audioflinger/tests/run_all_unit_tests.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +if [ -z "$ANDROID_BUILD_TOP" ]; then + echo "Android build environment not set" + exit -1 +fi + +echo "waiting for device" +adb root && adb wait-for-device remount + +adb shell /system/bin/resampler_tests diff --git a/services/audiopolicy/Android.mk b/services/audiopolicy/Android.mk index a22ad9d..cddc503 100644 --- a/services/audiopolicy/Android.mk +++ b/services/audiopolicy/Android.mk @@ -46,8 +46,8 @@ LOCAL_CFLAGS += -fvisibility=hidden include $(BUILD_SHARED_LIBRARY) + ifneq ($(USE_LEGACY_AUDIO_POLICY), 1) -ifneq ($(USE_CUSTOM_AUDIO_POLICY), 1) include $(CLEAR_VARS) @@ -62,6 +62,20 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_STATIC_LIBRARIES := \ libmedia_helper +LOCAL_MODULE:= libaudiopolicymanagerdefault + +include $(BUILD_SHARED_LIBRARY) + +ifneq ($(USE_CUSTOM_AUDIO_POLICY), 1) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + AudioPolicyFactory.cpp + +LOCAL_SHARED_LIBRARIES := \ + libaudiopolicymanagerdefault + LOCAL_MODULE:= libaudiopolicymanager include $(BUILD_SHARED_LIBRARY) diff --git a/services/audiopolicy/AudioPolicyFactory.cpp b/services/audiopolicy/AudioPolicyFactory.cpp new file mode 100644 index 0000000..2ae7bc1 --- /dev/null +++ b/services/audiopolicy/AudioPolicyFactory.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2014 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 "AudioPolicyManager.h" + +namespace android { + +extern "C" AudioPolicyInterface* createAudioPolicyManager( + AudioPolicyClientInterface *clientInterface) +{ + return new AudioPolicyManager(clientInterface); +} + +extern "C" void destroyAudioPolicyManager(AudioPolicyInterface *interface) +{ + delete interface; +} + +}; // namespace android diff --git a/services/audiopolicy/AudioPolicyManager.cpp b/services/audiopolicy/AudioPolicyManager.cpp index 61edac2..d4c9374 100644 --- a/services/audiopolicy/AudioPolicyManager.cpp +++ b/services/audiopolicy/AudioPolicyManager.cpp @@ -100,6 +100,7 @@ const StringToEnum sDeviceNameToEnumTable[] = { STRING_TO_ENUM(AUDIO_DEVICE_IN_TV_TUNER), STRING_TO_ENUM(AUDIO_DEVICE_IN_LINE), STRING_TO_ENUM(AUDIO_DEVICE_IN_SPDIF), + STRING_TO_ENUM(AUDIO_DEVICE_IN_BLUETOOTH_A2DP), }; const StringToEnum sFlagNameToEnumTable[] = { @@ -284,16 +285,8 @@ status_t AudioPolicyManager::setDeviceConnectionState(audio_devices_t device, 0); } - if (device == AUDIO_DEVICE_OUT_WIRED_HEADSET) { - device = AUDIO_DEVICE_IN_WIRED_HEADSET; - } else if (device == AUDIO_DEVICE_OUT_BLUETOOTH_SCO || - device == AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET || - device == AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT) { - device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET; - } else { - mpClientInterface->onAudioPortListUpdate(); - return NO_ERROR; - } + mpClientInterface->onAudioPortListUpdate(); + return NO_ERROR; } // end if is output device // handle input devices @@ -3809,6 +3802,12 @@ audio_devices_t AudioPolicyManager::getDeviceForInputSource(audio_source_t input case AUDIO_SOURCE_DEFAULT: case AUDIO_SOURCE_MIC: + if (availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_A2DP) { + device = AUDIO_DEVICE_IN_BLUETOOTH_A2DP; + break; + } + // FALL THROUGH + case AUDIO_SOURCE_VOICE_RECOGNITION: case AUDIO_SOURCE_HOTWORD: case AUDIO_SOURCE_VOICE_COMMUNICATION: @@ -3985,6 +3984,11 @@ const AudioPolicyManager::VolumeCurvePoint }; const AudioPolicyManager::VolumeCurvePoint + AudioPolicyManager::sSpeakerMediaVolumeCurveDrc[AudioPolicyManager::VOLCNT] = { + {1, -56.0f}, {20, -34.0f}, {86, -10.0f}, {100, 0.0f} +}; + +const AudioPolicyManager::VolumeCurvePoint AudioPolicyManager::sSpeakerSonificationVolumeCurve[AudioPolicyManager::VOLCNT] = { {1, -29.7f}, {33, -20.1f}, {66, -10.2f}, {100, 0.0f} }; @@ -4098,6 +4102,8 @@ void AudioPolicyManager::initializeVolumeCurves() sSpeakerSonificationVolumeCurveDrc; mStreams[AUDIO_STREAM_NOTIFICATION].mVolumeCurve[DEVICE_CATEGORY_SPEAKER] = sSpeakerSonificationVolumeCurveDrc; + mStreams[AUDIO_STREAM_MUSIC].mVolumeCurve[DEVICE_CATEGORY_SPEAKER] = + sSpeakerMediaVolumeCurveDrc; } } diff --git a/services/audiopolicy/AudioPolicyManager.h b/services/audiopolicy/AudioPolicyManager.h index adf1b33..1abeb6a 100644 --- a/services/audiopolicy/AudioPolicyManager.h +++ b/services/audiopolicy/AudioPolicyManager.h @@ -374,6 +374,7 @@ protected: static const VolumeCurvePoint sDefaultMediaVolumeCurve[AudioPolicyManager::VOLCNT]; // volume curve for media strategy on speakers static const VolumeCurvePoint sSpeakerMediaVolumeCurve[AudioPolicyManager::VOLCNT]; + static const VolumeCurvePoint sSpeakerMediaVolumeCurveDrc[AudioPolicyManager::VOLCNT]; // volume curve for sonification strategy on speakers static const VolumeCurvePoint sSpeakerSonificationVolumeCurve[AudioPolicyManager::VOLCNT]; static const VolumeCurvePoint sSpeakerSonificationVolumeCurveDrc[AudioPolicyManager::VOLCNT]; diff --git a/services/audiopolicy/AudioPolicyService.cpp b/services/audiopolicy/AudioPolicyService.cpp index a2a0461..93fab11 100644 --- a/services/audiopolicy/AudioPolicyService.cpp +++ b/services/audiopolicy/AudioPolicyService.cpp @@ -108,7 +108,7 @@ AudioPolicyService::AudioPolicyService() ALOGI("AudioPolicyService CSTOR in new mode"); mAudioPolicyClient = new AudioPolicyClient(this); - mAudioPolicyManager = new AudioPolicyManager(mAudioPolicyClient); + mAudioPolicyManager = createAudioPolicyManager(mAudioPolicyClient); #endif // load audio pre processing modules @@ -145,7 +145,7 @@ AudioPolicyService::~AudioPolicyService() audio_policy_dev_close(mpAudioPolicyDev); } #else - delete mAudioPolicyManager; + destroyAudioPolicyManager(mAudioPolicyManager); delete mAudioPolicyClient; #endif @@ -399,7 +399,8 @@ bool AudioPolicyService::AudioCommandThread::threadLoop() mLock.lock(); while (!exitPending()) { - while (!mAudioCommands.isEmpty()) { + sp<AudioPolicyService> svc; + while (!mAudioCommands.isEmpty() && !exitPending()) { nsecs_t curTime = systemTime(); // commands are sorted by increasing time stamp: execute them from index 0 and up if (mAudioCommands[0]->mTime <= curTime) { @@ -452,7 +453,7 @@ bool AudioPolicyService::AudioCommandThread::threadLoop() StopOutputData *data = (StopOutputData *)command->mParam.get(); ALOGV("AudioCommandThread() processing stop output %d", data->mIO); - sp<AudioPolicyService> svc = mService.promote(); + svc = mService.promote(); if (svc == 0) { break; } @@ -464,7 +465,7 @@ bool AudioPolicyService::AudioCommandThread::threadLoop() ReleaseOutputData *data = (ReleaseOutputData *)command->mParam.get(); ALOGV("AudioCommandThread() processing release output %d", data->mIO); - sp<AudioPolicyService> svc = mService.promote(); + svc = mService.promote(); if (svc == 0) { break; } @@ -494,7 +495,7 @@ bool AudioPolicyService::AudioCommandThread::threadLoop() } break; case UPDATE_AUDIOPORT_LIST: { ALOGV("AudioCommandThread() processing update audio port list"); - sp<AudioPolicyService> svc = mService.promote(); + svc = mService.promote(); if (svc == 0) { break; } @@ -504,7 +505,7 @@ bool AudioPolicyService::AudioCommandThread::threadLoop() }break; case UPDATE_AUDIOPATCH_LIST: { ALOGV("AudioCommandThread() processing update audio patch list"); - sp<AudioPolicyService> svc = mService.promote(); + svc = mService.promote(); if (svc == 0) { break; } @@ -542,9 +543,16 @@ bool AudioPolicyService::AudioCommandThread::threadLoop() if (mAudioCommands.isEmpty()) { release_wake_lock(mName.string()); } - ALOGV("AudioCommandThread() going to sleep"); - mWaitWorkCV.waitRelative(mLock, waitTime); - ALOGV("AudioCommandThread() waking up"); + // release mLock before releasing strong reference on the service as + // AudioPolicyService destructor calls AudioCommandThread::exit() which acquires mLock. + mLock.unlock(); + svc.clear(); + mLock.lock(); + if (!exitPending()) { + ALOGV("AudioCommandThread() going to sleep"); + mWaitWorkCV.waitRelative(mLock, waitTime); + ALOGV("AudioCommandThread() waking up"); + } } mLock.unlock(); return false; diff --git a/services/audiopolicy/AudioPolicyService.h b/services/audiopolicy/AudioPolicyService.h index 40f589b..66d9cad 100644 --- a/services/audiopolicy/AudioPolicyService.h +++ b/services/audiopolicy/AudioPolicyService.h @@ -539,7 +539,7 @@ private: sp<AudioCommandThread> mOutputCommandThread; // process stop and release output struct audio_policy_device *mpAudioPolicyDev; struct audio_policy *mpAudioPolicy; - AudioPolicyManager *mAudioPolicyManager; + AudioPolicyInterface *mAudioPolicyManager; AudioPolicyClient *mAudioPolicyClient; KeyedVector< audio_source_t, InputSourceDesc* > mInputSources; diff --git a/services/camera/libcameraservice/utils/CameraTraces.cpp b/services/camera/libcameraservice/utils/CameraTraces.cpp index 346e15f..374dc5e 100644 --- a/services/camera/libcameraservice/utils/CameraTraces.cpp +++ b/services/camera/libcameraservice/utils/CameraTraces.cpp @@ -74,10 +74,10 @@ status_t CameraTraces::dump(int fd, const Vector<String16> &args __attribute__(( return BAD_VALUE; } - fdprintf(fd, "Camera traces (%zu):\n", pcsList.size()); + dprintf(fd, "Camera traces (%zu):\n", pcsList.size()); if (pcsList.empty()) { - fdprintf(fd, " No camera traces collected.\n"); + dprintf(fd, " No camera traces collected.\n"); } // Print newest items first diff --git a/services/medialog/MediaLogService.cpp b/services/medialog/MediaLogService.cpp index 0c7fbbd..41dab1f 100644 --- a/services/medialog/MediaLogService.cpp +++ b/services/medialog/MediaLogService.cpp @@ -60,7 +60,7 @@ status_t MediaLogService::dump(int fd, const Vector<String16>& args __unused) static const String16 sDump("android.permission.DUMP"); if (!(IPCThreadState::self()->getCallingUid() == AID_MEDIA || PermissionCache::checkCallingPermission(sDump))) { - fdprintf(fd, "Permission Denial: can't dump media.log from pid=%d, uid=%d\n", + dprintf(fd, "Permission Denial: can't dump media.log from pid=%d, uid=%d\n", IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid()); return NO_ERROR; @@ -74,7 +74,7 @@ status_t MediaLogService::dump(int fd, const Vector<String16>& args __unused) for (size_t i = 0; i < namedReaders.size(); i++) { const NamedReader& namedReader = namedReaders[i]; if (fd >= 0) { - fdprintf(fd, "\n%s:\n", namedReader.name()); + dprintf(fd, "\n%s:\n", namedReader.name()); } else { ALOGI("%s:", namedReader.name()); } diff --git a/services/soundtrigger/Android.mk b/services/soundtrigger/Android.mk new file mode 100644 index 0000000..b7ccaab --- /dev/null +++ b/services/soundtrigger/Android.mk @@ -0,0 +1,41 @@ +# Copyright 2014 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. + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + + +ifeq ($(SOUND_TRIGGER_USE_STUB_MODULE), 1) + LOCAL_CFLAGS += -DSOUND_TRIGGER_USE_STUB_MODULE +endif + +LOCAL_SRC_FILES:= \ + SoundTriggerHwService.cpp + +LOCAL_SHARED_LIBRARIES:= \ + libui \ + liblog \ + libutils \ + libbinder \ + libcutils \ + libhardware \ + libsoundtrigger + +#LOCAL_C_INCLUDES += \ + + +LOCAL_MODULE:= libsoundtriggerservice + +include $(BUILD_SHARED_LIBRARY) diff --git a/services/soundtrigger/SoundTriggerHwService.cpp b/services/soundtrigger/SoundTriggerHwService.cpp new file mode 100644 index 0000000..fa59388 --- /dev/null +++ b/services/soundtrigger/SoundTriggerHwService.cpp @@ -0,0 +1,570 @@ +/* + * Copyright (C) 2014 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 "SoundTriggerHwService" +//#define LOG_NDEBUG 0 + +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <pthread.h> + +#include <binder/IServiceManager.h> +#include <binder/MemoryBase.h> +#include <binder/MemoryHeapBase.h> +#include <cutils/atomic.h> +#include <cutils/properties.h> +#include <hardware/hardware.h> +#include <utils/Errors.h> +#include <utils/Log.h> + +#include "SoundTriggerHwService.h" +#include <system/sound_trigger.h> +#include <hardware/sound_trigger.h> + +namespace android { + +#ifdef SOUND_TRIGGER_USE_STUB_MODULE +#define HW_MODULE_PREFIX "stub" +#else +#define HW_MODULE_PREFIX "primary" +#endif + +SoundTriggerHwService::SoundTriggerHwService() + : BnSoundTriggerHwService(), + mNextUniqueId(1) +{ +} + +void SoundTriggerHwService::onFirstRef() +{ + const hw_module_t *mod; + int rc; + sound_trigger_hw_device *dev; + + rc = hw_get_module_by_class(SOUND_TRIGGER_HARDWARE_MODULE_ID, HW_MODULE_PREFIX, &mod); + if (rc != 0) { + ALOGE("couldn't load sound trigger module %s.%s (%s)", + SOUND_TRIGGER_HARDWARE_MODULE_ID, "primary", strerror(-rc)); + return; + } + rc = sound_trigger_hw_device_open(mod, &dev); + if (rc != 0) { + ALOGE("couldn't open sound trigger hw device in %s.%s (%s)", + SOUND_TRIGGER_HARDWARE_MODULE_ID, "primary", strerror(-rc)); + return; + } + if (dev->common.version != SOUND_TRIGGER_DEVICE_API_VERSION_CURRENT) { + ALOGE("wrong sound trigger hw device version %04x", dev->common.version); + return; + } + + sound_trigger_module_descriptor descriptor; + rc = dev->get_properties(dev, &descriptor.properties); + if (rc != 0) { + ALOGE("could not read implementation properties"); + return; + } + descriptor.handle = + (sound_trigger_module_handle_t)android_atomic_inc(&mNextUniqueId); + ALOGI("loaded default module %s, handle %d", descriptor.properties.description, + descriptor.handle); + + sp<ISoundTriggerClient> client; + sp<Module> module = new Module(this, dev, descriptor, client); + mModules.add(descriptor.handle, module); + mCallbackThread = new CallbackThread(this); +} + +SoundTriggerHwService::~SoundTriggerHwService() +{ + if (mCallbackThread != 0) { + mCallbackThread->exit(); + } + for (size_t i = 0; i < mModules.size(); i++) { + sound_trigger_hw_device_close(mModules.valueAt(i)->hwDevice()); + } +} + +status_t SoundTriggerHwService::listModules(struct sound_trigger_module_descriptor *modules, + uint32_t *numModules) +{ + ALOGV("listModules"); + AutoMutex lock(mServiceLock); + if (numModules == NULL || (*numModules != 0 && modules == NULL)) { + return BAD_VALUE; + } + size_t maxModules = *numModules; + *numModules = mModules.size(); + for (size_t i = 0; i < mModules.size() && i < maxModules; i++) { + modules[i] = mModules.valueAt(i)->descriptor(); + } + return NO_ERROR; +} + +status_t SoundTriggerHwService::attach(const sound_trigger_module_handle_t handle, + const sp<ISoundTriggerClient>& client, + sp<ISoundTrigger>& moduleInterface) +{ + ALOGV("attach module %d", handle); + AutoMutex lock(mServiceLock); + moduleInterface.clear(); + if (client == 0) { + return BAD_VALUE; + } + ssize_t index = mModules.indexOfKey(handle); + if (index < 0) { + return BAD_VALUE; + } + sp<Module> module = mModules.valueAt(index); + + module->setClient(client); + client->asBinder()->linkToDeath(module); + moduleInterface = module; + + return NO_ERROR; +} + +void SoundTriggerHwService::detachModule(sp<Module> module) { + AutoMutex lock(mServiceLock); + ALOGV("detachModule"); + module->clearClient(); +} + +static const int kDumpLockRetries = 50; +static const int kDumpLockSleep = 60000; + +static bool tryLock(Mutex& mutex) +{ + bool locked = false; + for (int i = 0; i < kDumpLockRetries; ++i) { + if (mutex.tryLock() == NO_ERROR) { + locked = true; + break; + } + usleep(kDumpLockSleep); + } + return locked; +} + +status_t SoundTriggerHwService::dump(int fd, const Vector<String16>& args __unused) { + String8 result; + if (checkCallingPermission(String16("android.permission.DUMP")) == false) { + result.appendFormat("Permission Denial: can't dump SoundTriggerHwService"); + write(fd, result.string(), result.size()); + } else { + bool locked = tryLock(mServiceLock); + // failed to lock - SoundTriggerHwService is probably deadlocked + if (!locked) { + result.append("SoundTriggerHwService may be deadlocked\n"); + write(fd, result.string(), result.size()); + } + + if (locked) mServiceLock.unlock(); + } + return NO_ERROR; +} + +status_t SoundTriggerHwService::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { + return BnSoundTriggerHwService::onTransact(code, data, reply, flags); +} + + +// static +void SoundTriggerHwService::recognitionCallback(struct sound_trigger_recognition_event *event, + void *cookie) +{ + Module *module = (Module *)cookie; + if (module == NULL) { + return; + } + module->sendRecognitionEvent(event); +} + + +void SoundTriggerHwService::sendRecognitionEvent(const sp<RecognitionEvent>& event) +{ + mCallbackThread->sendRecognitionEvent(event); +} + +void SoundTriggerHwService::onRecognitionEvent(const sp<RecognitionEvent>& event) +{ + ALOGV("onRecognitionEvent"); + sp<Module> module; + { + AutoMutex lock(mServiceLock); + module = event->mModule.promote(); + if (module == 0) { + return; + } + } + module->onRecognitionEvent(event->mEventMemory); +} + +// static +void SoundTriggerHwService::soundModelCallback(struct sound_trigger_model_event *event __unused, + void *cookie) +{ + Module *module = (Module *)cookie; + +} + +#undef LOG_TAG +#define LOG_TAG "SoundTriggerHwService::CallbackThread" + +SoundTriggerHwService::CallbackThread::CallbackThread(const wp<SoundTriggerHwService>& service) + : mService(service) +{ +} + +SoundTriggerHwService::CallbackThread::~CallbackThread() +{ + mEventQueue.clear(); +} + +void SoundTriggerHwService::CallbackThread::onFirstRef() +{ + run("soundTrigger cbk", ANDROID_PRIORITY_URGENT_AUDIO); +} + +bool SoundTriggerHwService::CallbackThread::threadLoop() +{ + while (!exitPending()) { + sp<RecognitionEvent> event; + sp<SoundTriggerHwService> service; + { + Mutex::Autolock _l(mCallbackLock); + while (mEventQueue.isEmpty() && !exitPending()) { + ALOGV("CallbackThread::threadLoop() sleep"); + mCallbackCond.wait(mCallbackLock); + ALOGV("CallbackThread::threadLoop() wake up"); + } + if (exitPending()) { + break; + } + event = mEventQueue[0]; + mEventQueue.removeAt(0); + service = mService.promote(); + } + if (service != 0) { + service->onRecognitionEvent(event); + } + } + return false; +} + +void SoundTriggerHwService::CallbackThread::exit() +{ + Mutex::Autolock _l(mCallbackLock); + requestExit(); + mCallbackCond.broadcast(); +} + +void SoundTriggerHwService::CallbackThread::sendRecognitionEvent( + const sp<SoundTriggerHwService::RecognitionEvent>& event) +{ + AutoMutex lock(mCallbackLock); + mEventQueue.add(event); + mCallbackCond.signal(); +} + +SoundTriggerHwService::RecognitionEvent::RecognitionEvent( + sp<IMemory> eventMemory, + wp<Module> module) + : mEventMemory(eventMemory), mModule(module) +{ +} + +SoundTriggerHwService::RecognitionEvent::~RecognitionEvent() +{ +} + +#undef LOG_TAG +#define LOG_TAG "SoundTriggerHwService::Module" + +SoundTriggerHwService::Module::Module(const sp<SoundTriggerHwService>& service, + sound_trigger_hw_device* hwDevice, + sound_trigger_module_descriptor descriptor, + const sp<ISoundTriggerClient>& client) + : mService(service), mHwDevice(hwDevice), mDescriptor(descriptor), + mClient(client) +{ +} + +SoundTriggerHwService::Module::~Module() { +} + +void SoundTriggerHwService::Module::detach() { + ALOGV("detach()"); + { + AutoMutex lock(mLock); + for (size_t i = 0; i < mModels.size(); i++) { + sp<Model> model = mModels.valueAt(i); + ALOGV("detach() unloading model %d", model->mHandle); + if (model->mState == Model::STATE_ACTIVE) { + mHwDevice->stop_recognition(mHwDevice, model->mHandle); + model->deallocateMemory(); + } + mHwDevice->unload_sound_model(mHwDevice, model->mHandle); + } + mModels.clear(); + } + if (mClient != 0) { + mClient->asBinder()->unlinkToDeath(this); + } + sp<SoundTriggerHwService> service = mService.promote(); + if (service == 0) { + return; + } + service->detachModule(this); +} + +status_t SoundTriggerHwService::Module::loadSoundModel(const sp<IMemory>& modelMemory, + sound_model_handle_t *handle) +{ + ALOGV("loadSoundModel() handle"); + + if (modelMemory == 0 || modelMemory->pointer() == NULL) { + ALOGE("loadSoundModel() modelMemory is 0 or has NULL pointer()"); + return BAD_VALUE; + } + struct sound_trigger_sound_model *sound_model = + (struct sound_trigger_sound_model *)modelMemory->pointer(); + + AutoMutex lock(mLock); + status_t status = mHwDevice->load_sound_model(mHwDevice, + sound_model, + SoundTriggerHwService::soundModelCallback, + this, + handle); + if (status == NO_ERROR) { + mModels.replaceValueFor(*handle, new Model(*handle)); + } + + return status; +} + +status_t SoundTriggerHwService::Module::unloadSoundModel(sound_model_handle_t handle) +{ + ALOGV("unloadSoundModel() model handle %d", handle); + + AutoMutex lock(mLock); + ssize_t index = mModels.indexOfKey(handle); + if (index < 0) { + return BAD_VALUE; + } + sp<Model> model = mModels.valueAt(index); + mModels.removeItem(handle); + if (model->mState == Model::STATE_ACTIVE) { + mHwDevice->stop_recognition(mHwDevice, model->mHandle); + model->deallocateMemory(); + } + return mHwDevice->unload_sound_model(mHwDevice, handle); +} + +status_t SoundTriggerHwService::Module::startRecognition(sound_model_handle_t handle, + const sp<IMemory>& dataMemory) +{ + ALOGV("startRecognition() model handle %d", handle); + + if (dataMemory != 0 && dataMemory->pointer() == NULL) { + ALOGE("startRecognition() dataMemory is non-0 but has NULL pointer()"); + return BAD_VALUE; + + } + AutoMutex lock(mLock); + sp<Model> model = getModel(handle); + if (model == 0) { + return BAD_VALUE; + } + + if (model->mState == Model::STATE_ACTIVE) { + return INVALID_OPERATION; + } + model->mState = Model::STATE_ACTIVE; + + char *data = NULL; + unsigned int data_size = 0; + if (dataMemory != 0 && dataMemory->size() != 0) { + data_size = (unsigned int)dataMemory->size(); + data = (char *)dataMemory->pointer(); + ALOGV("startRecognition() data size %d data %d - %d", + data_size, data[0], data[data_size - 1]); + } + + //TODO: get capture handle and device from audio policy service + audio_io_handle_t capture_handle = 0; + return mHwDevice->start_recognition(mHwDevice, handle, capture_handle, AUDIO_DEVICE_NONE, + SoundTriggerHwService::recognitionCallback, + this, + data_size, + data); +} + +status_t SoundTriggerHwService::Module::stopRecognition(sound_model_handle_t handle) +{ + ALOGV("stopRecognition() model handle %d", handle); + + AutoMutex lock(mLock); + sp<Model> model = getModel(handle); + if (model == 0) { + return BAD_VALUE; + } + + if (model->mState != Model::STATE_ACTIVE) { + return INVALID_OPERATION; + } + mHwDevice->stop_recognition(mHwDevice, handle); + model->deallocateMemory(); + model->mState = Model::STATE_IDLE; + return NO_ERROR; +} + +void SoundTriggerHwService::Module::sendRecognitionEvent( + struct sound_trigger_recognition_event *event) +{ + sp<SoundTriggerHwService> service; + sp<IMemory> eventMemory; + ALOGV("sendRecognitionEvent for model %d", event->model); + { + AutoMutex lock(mLock); + sp<Model> model = getModel(event->model); + if (model == 0) { + return; + } + if (model->mState != Model::STATE_ACTIVE) { + ALOGV("sendRecognitionEvent model->mState %d != Model::STATE_ACTIVE", model->mState); + return; + } + if (mClient == 0) { + return; + } + service = mService.promote(); + if (service == 0) { + return; + } + + //sanitize event + switch (event->type) { + case SOUND_MODEL_TYPE_KEYPHRASE: + ALOGW_IF(event->data_offset != + sizeof(struct sound_trigger_phrase_recognition_event), + "sendRecognitionEvent(): invalid data offset %u for keyphrase event type", + event->data_offset); + event->data_offset = sizeof(struct sound_trigger_phrase_recognition_event); + break; + case SOUND_MODEL_TYPE_UNKNOWN: + ALOGW_IF(event->data_offset != + sizeof(struct sound_trigger_recognition_event), + "sendRecognitionEvent(): invalid data offset %u for unknown event type", + event->data_offset); + event->data_offset = sizeof(struct sound_trigger_recognition_event); + break; + default: + return; + } + + size_t size = event->data_offset + event->data_size; + eventMemory = model->allocateMemory(size); + if (eventMemory == 0 || eventMemory->pointer() == NULL) { + return; + } + memcpy(eventMemory->pointer(), event, size); + } + service->sendRecognitionEvent(new RecognitionEvent(eventMemory, this)); +} + +void SoundTriggerHwService::Module::onRecognitionEvent(sp<IMemory> eventMemory) +{ + ALOGV("Module::onRecognitionEvent"); + + AutoMutex lock(mLock); + + if (eventMemory == 0 || eventMemory->pointer() == NULL) { + return; + } + struct sound_trigger_recognition_event *event = + (struct sound_trigger_recognition_event *)eventMemory->pointer(); + + sp<Model> model = getModel(event->model); + if (model == 0) { + ALOGI("%s model == 0", __func__); + return; + } + if (model->mState != Model::STATE_ACTIVE) { + ALOGV("onRecognitionEvent model->mState %d != Model::STATE_ACTIVE", model->mState); + return; + } + if (mClient == 0) { + ALOGI("%s mClient == 0", __func__); + return; + } + mClient->onRecognitionEvent(eventMemory); + model->mState = Model::STATE_IDLE; + model->deallocateMemory(); +} + +sp<SoundTriggerHwService::Model> SoundTriggerHwService::Module::getModel( + sound_model_handle_t handle) +{ + sp<Model> model; + ssize_t index = mModels.indexOfKey(handle); + if (index >= 0) { + model = mModels.valueAt(index); + } + return model; +} + +void SoundTriggerHwService::Module::binderDied( + const wp<IBinder> &who __unused) { + ALOGW("client binder died for module %d", mDescriptor.handle); + detach(); +} + + +SoundTriggerHwService::Model::Model(sound_model_handle_t handle) : + mHandle(handle), mState(STATE_IDLE), mInputHandle(AUDIO_IO_HANDLE_NONE), + mCaptureSession(AUDIO_SESSION_ALLOCATE), + mMemoryDealer(new MemoryDealer(sizeof(struct sound_trigger_recognition_event), + "SoundTriggerHwService::Event")) +{ + +} + + +sp<IMemory> SoundTriggerHwService::Model::allocateMemory(size_t size) +{ + sp<IMemory> memory; + if (mMemoryDealer->getMemoryHeap()->getSize() < size) { + mMemoryDealer = new MemoryDealer(size, "SoundTriggerHwService::Event"); + } + memory = mMemoryDealer->allocate(size); + return memory; +} + +void SoundTriggerHwService::Model::deallocateMemory() +{ + mMemoryDealer->deallocate(0); +} + +status_t SoundTriggerHwService::Module::dump(int fd __unused, + const Vector<String16>& args __unused) { + String8 result; + return NO_ERROR; +} + +}; // namespace android diff --git a/services/soundtrigger/SoundTriggerHwService.h b/services/soundtrigger/SoundTriggerHwService.h new file mode 100644 index 0000000..377f2a1 --- /dev/null +++ b/services/soundtrigger/SoundTriggerHwService.h @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2008 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_SOUNDTRIGGER_HAL_SERVICE_H +#define ANDROID_HARDWARE_SOUNDTRIGGER_HAL_SERVICE_H + +#include <utils/Vector.h> +//#include <binder/AppOpsManager.h> +#include <binder/MemoryDealer.h> +#include <binder/BinderService.h> +#include <binder/IAppOpsCallback.h> +#include <soundtrigger/ISoundTriggerHwService.h> +#include <soundtrigger/ISoundTrigger.h> +#include <soundtrigger/ISoundTriggerClient.h> +#include <system/sound_trigger.h> +#include <hardware/sound_trigger.h> + +namespace android { + +class MemoryHeapBase; + +class SoundTriggerHwService : + public BinderService<SoundTriggerHwService>, + public BnSoundTriggerHwService +{ + friend class BinderService<SoundTriggerHwService>; +public: + class Module; + + static char const* getServiceName() { return "media.sound_trigger_hw"; } + + SoundTriggerHwService(); + virtual ~SoundTriggerHwService(); + + // ISoundTriggerHwService + virtual status_t listModules(struct sound_trigger_module_descriptor *modules, + uint32_t *numModules); + + virtual status_t attach(const sound_trigger_module_handle_t handle, + const sp<ISoundTriggerClient>& client, + sp<ISoundTrigger>& module); + + virtual status_t onTransact(uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags); + + virtual status_t dump(int fd, const Vector<String16>& args); + + class Model : public RefBase { + public: + + enum { + STATE_IDLE, + STATE_ACTIVE + }; + + Model(sound_model_handle_t handle); + ~Model() {} + + sp<IMemory> allocateMemory(size_t size); + void deallocateMemory(); + + sound_model_handle_t mHandle; + int mState; + audio_io_handle_t mInputHandle; + audio_session_t mCaptureSession; + sp<MemoryDealer> mMemoryDealer; + }; + + class Module : public virtual RefBase, + public BnSoundTrigger, + public IBinder::DeathRecipient { + public: + + Module(const sp<SoundTriggerHwService>& service, + sound_trigger_hw_device* hwDevice, + sound_trigger_module_descriptor descriptor, + const sp<ISoundTriggerClient>& client); + + virtual ~Module(); + + virtual void detach(); + + virtual status_t loadSoundModel(const sp<IMemory>& modelMemory, + sound_model_handle_t *handle); + + virtual status_t unloadSoundModel(sound_model_handle_t handle); + + virtual status_t startRecognition(sound_model_handle_t handle, + const sp<IMemory>& dataMemory); + virtual status_t stopRecognition(sound_model_handle_t handle); + + virtual status_t dump(int fd, const Vector<String16>& args); + + + sound_trigger_hw_device *hwDevice() const { return mHwDevice; } + struct sound_trigger_module_descriptor descriptor() { return mDescriptor; } + void setClient(sp<ISoundTriggerClient> client) { mClient = client; } + void clearClient() { mClient.clear(); } + sp<ISoundTriggerClient> client() { return mClient; } + + void sendRecognitionEvent(struct sound_trigger_recognition_event *event); + void onRecognitionEvent(sp<IMemory> eventMemory); + + sp<Model> getModel(sound_model_handle_t handle); + + // IBinder::DeathRecipient implementation + virtual void binderDied(const wp<IBinder> &who); + + private: + Mutex mLock; + wp<SoundTriggerHwService> mService; + struct sound_trigger_hw_device* mHwDevice; + struct sound_trigger_module_descriptor mDescriptor; + sp<ISoundTriggerClient> mClient; + DefaultKeyedVector< sound_model_handle_t, sp<Model> > mModels; + }; // class Module + + class RecognitionEvent : public RefBase { + public: + + RecognitionEvent(sp<IMemory> eventMemory, wp<Module> module); + + virtual ~RecognitionEvent(); + + sp<IMemory> mEventMemory; + wp<Module> mModule; + }; + + class CallbackThread : public Thread { + public: + + CallbackThread(const wp<SoundTriggerHwService>& service); + + virtual ~CallbackThread(); + + // Thread virtuals + virtual bool threadLoop(); + + // RefBase + virtual void onFirstRef(); + + void exit(); + void sendRecognitionEvent(const sp<RecognitionEvent>& event); + + private: + wp<SoundTriggerHwService> mService; + Condition mCallbackCond; + Mutex mCallbackLock; + Vector< sp<RecognitionEvent> > mEventQueue; + }; + + void detachModule(sp<Module> module); + + static void recognitionCallback(struct sound_trigger_recognition_event *event, void *cookie); + void sendRecognitionEvent(const sp<RecognitionEvent>& event); + void onRecognitionEvent(const sp<RecognitionEvent>& event); + + static void soundModelCallback(struct sound_trigger_model_event *event, void *cookie); + +private: + + virtual void onFirstRef(); + + Mutex mServiceLock; + volatile int32_t mNextUniqueId; + DefaultKeyedVector< sound_trigger_module_handle_t, sp<Module> > mModules; + sp<CallbackThread> mCallbackThread; +}; + +} // namespace android + +#endif // ANDROID_HARDWARE_SOUNDTRIGGER_HAL_SERVICE_H diff --git a/soundtrigger/Android.mk b/soundtrigger/Android.mk new file mode 100644 index 0000000..d91c4c2 --- /dev/null +++ b/soundtrigger/Android.mk @@ -0,0 +1,38 @@ +# Copyright 2014 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. + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + SoundTrigger.cpp \ + ISoundTrigger.cpp \ + ISoundTriggerClient.cpp \ + ISoundTriggerHwService.cpp + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + liblog \ + libbinder \ + libhardware + +#LOCAL_C_INCLUDES += \ + system/media/camera/include \ + system/media/private/camera/include + +LOCAL_MODULE:= libsoundtrigger + +include $(BUILD_SHARED_LIBRARY) diff --git a/soundtrigger/ISoundTrigger.cpp b/soundtrigger/ISoundTrigger.cpp new file mode 100644 index 0000000..42280d1 --- /dev/null +++ b/soundtrigger/ISoundTrigger.cpp @@ -0,0 +1,177 @@ +/* +** +** Copyright 2014, 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 "ISoundTrigger" +#include <utils/Log.h> +#include <utils/Errors.h> +#include <binder/IMemory.h> +#include <soundtrigger/ISoundTrigger.h> +#include <soundtrigger/ISoundTriggerHwService.h> +#include <soundtrigger/ISoundTriggerClient.h> +#include <system/sound_trigger.h> + +namespace android { + +enum { + DETACH = IBinder::FIRST_CALL_TRANSACTION, + LOAD_SOUND_MODEL, + UNLOAD_SOUND_MODEL, + START_RECOGNITION, + STOP_RECOGNITION, +}; + +class BpSoundTrigger: public BpInterface<ISoundTrigger> +{ +public: + BpSoundTrigger(const sp<IBinder>& impl) + : BpInterface<ISoundTrigger>(impl) + { + } + + void detach() + { + ALOGV("detach"); + Parcel data, reply; + data.writeInterfaceToken(ISoundTrigger::getInterfaceDescriptor()); + remote()->transact(DETACH, data, &reply); + } + + status_t loadSoundModel(const sp<IMemory>& modelMemory, + sound_model_handle_t *handle) + { + if (modelMemory == 0 || handle == NULL) { + return BAD_VALUE; + } + Parcel data, reply; + data.writeInterfaceToken(ISoundTrigger::getInterfaceDescriptor()); + data.writeStrongBinder(modelMemory->asBinder()); + status_t status = remote()->transact(LOAD_SOUND_MODEL, data, &reply); + if (status != NO_ERROR || + (status = (status_t)reply.readInt32()) != NO_ERROR) { + return status; + } + reply.read(handle, sizeof(sound_model_handle_t)); + return status; + } + + virtual status_t unloadSoundModel(sound_model_handle_t handle) + { + Parcel data, reply; + data.writeInterfaceToken(ISoundTrigger::getInterfaceDescriptor()); + data.write(&handle, sizeof(sound_model_handle_t)); + status_t status = remote()->transact(UNLOAD_SOUND_MODEL, data, &reply); + if (status != NO_ERROR) { + status = (status_t)reply.readInt32(); + } + return status; + } + + virtual status_t startRecognition(sound_model_handle_t handle, + const sp<IMemory>& dataMemory) + { + Parcel data, reply; + data.writeInterfaceToken(ISoundTrigger::getInterfaceDescriptor()); + data.write(&handle, sizeof(sound_model_handle_t)); + if (dataMemory == 0) { + data.writeInt32(0); + } else { + data.writeInt32(dataMemory->size()); + } + data.writeStrongBinder(dataMemory->asBinder()); + status_t status = remote()->transact(START_RECOGNITION, data, &reply); + if (status != NO_ERROR) { + status = (status_t)reply.readInt32(); + } + return status; + } + + virtual status_t stopRecognition(sound_model_handle_t handle) + { + Parcel data, reply; + data.writeInterfaceToken(ISoundTrigger::getInterfaceDescriptor()); + data.write(&handle, sizeof(sound_model_handle_t)); + status_t status = remote()->transact(STOP_RECOGNITION, data, &reply); + if (status != NO_ERROR) { + status = (status_t)reply.readInt32(); + } + return status; + } + +}; + +IMPLEMENT_META_INTERFACE(SoundTrigger, "android.hardware.ISoundTrigger"); + +// ---------------------------------------------------------------------- + +status_t BnSoundTrigger::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case DETACH: { + ALOGV("DETACH"); + CHECK_INTERFACE(ISoundTrigger, data, reply); + detach(); + return NO_ERROR; + } break; + case LOAD_SOUND_MODEL: { + CHECK_INTERFACE(ISoundTrigger, data, reply); + sp<IMemory> modelMemory = interface_cast<IMemory>( + data.readStrongBinder()); + sound_model_handle_t handle; + status_t status = loadSoundModel(modelMemory, &handle); + reply->writeInt32(status); + if (status == NO_ERROR) { + reply->write(&handle, sizeof(sound_model_handle_t)); + } + return NO_ERROR; + } + case UNLOAD_SOUND_MODEL: { + CHECK_INTERFACE(ISoundTrigger, data, reply); + sound_model_handle_t handle; + data.read(&handle, sizeof(sound_model_handle_t)); + status_t status = unloadSoundModel(handle); + reply->writeInt32(status); + return NO_ERROR; + } + case START_RECOGNITION: { + CHECK_INTERFACE(ISoundTrigger, data, reply); + sound_model_handle_t handle; + data.read(&handle, sizeof(sound_model_handle_t)); + sp<IMemory> dataMemory; + if (data.readInt32() != 0) { + dataMemory = interface_cast<IMemory>(data.readStrongBinder()); + } + status_t status = startRecognition(handle, dataMemory); + reply->writeInt32(status); + return NO_ERROR; + } + case STOP_RECOGNITION: { + CHECK_INTERFACE(ISoundTrigger, data, reply); + sound_model_handle_t handle; + data.read(&handle, sizeof(sound_model_handle_t)); + status_t status = stopRecognition(handle); + reply->writeInt32(status); + return NO_ERROR; + } + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/soundtrigger/ISoundTriggerClient.cpp b/soundtrigger/ISoundTriggerClient.cpp new file mode 100644 index 0000000..1d0c0ec --- /dev/null +++ b/soundtrigger/ISoundTriggerClient.cpp @@ -0,0 +1,75 @@ +/* +** +** Copyright 2014, 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 <stdint.h> +#include <sys/types.h> +#include <binder/IMemory.h> +#include <binder/Parcel.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <soundtrigger/ISoundTriggerClient.h> + +namespace android { + +enum { + ON_RECOGNITION_EVENT = IBinder::FIRST_CALL_TRANSACTION, +}; + +class BpSoundTriggerClient: public BpInterface<ISoundTriggerClient> +{ + +public: + BpSoundTriggerClient(const sp<IBinder>& impl) + : BpInterface<ISoundTriggerClient>(impl) + { + } + + virtual void onRecognitionEvent(const sp<IMemory>& eventMemory) + { + Parcel data, reply; + data.writeInterfaceToken(ISoundTriggerClient::getInterfaceDescriptor()); + data.writeStrongBinder(eventMemory->asBinder()); + remote()->transact(ON_RECOGNITION_EVENT, + data, + &reply); + } +}; + +IMPLEMENT_META_INTERFACE(SoundTriggerClient, + "android.hardware.ISoundTriggerClient"); + +// ---------------------------------------------------------------------- + +status_t BnSoundTriggerClient::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case ON_RECOGNITION_EVENT: { + CHECK_INTERFACE(ISoundTriggerClient, data, reply); + sp<IMemory> eventMemory = interface_cast<IMemory>( + data.readStrongBinder()); + onRecognitionEvent(eventMemory); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/soundtrigger/ISoundTriggerHwService.cpp b/soundtrigger/ISoundTriggerHwService.cpp new file mode 100644 index 0000000..c9a0c24 --- /dev/null +++ b/soundtrigger/ISoundTriggerHwService.cpp @@ -0,0 +1,150 @@ +/* +** +** Copyright 2014, 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 "BpSoundTriggerHwService" +//#define LOG_NDEBUG 0 + +#include <utils/Log.h> +#include <utils/Errors.h> + +#include <stdint.h> +#include <sys/types.h> +#include <binder/IMemory.h> +#include <binder/Parcel.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> + +#include <soundtrigger/ISoundTriggerHwService.h> +#include <soundtrigger/ISoundTrigger.h> +#include <soundtrigger/ISoundTriggerClient.h> + +namespace android { + +enum { + LIST_MODULES = IBinder::FIRST_CALL_TRANSACTION, + ATTACH, +}; + +class BpSoundTriggerHwService: public BpInterface<ISoundTriggerHwService> +{ +public: + BpSoundTriggerHwService(const sp<IBinder>& impl) + : BpInterface<ISoundTriggerHwService>(impl) + { + } + + virtual status_t listModules(struct sound_trigger_module_descriptor *modules, + uint32_t *numModules) + { + if (numModules == NULL || (*numModules != 0 && modules == NULL)) { + return BAD_VALUE; + } + Parcel data, reply; + data.writeInterfaceToken(ISoundTriggerHwService::getInterfaceDescriptor()); + unsigned int numModulesReq = (modules == NULL) ? 0 : *numModules; + data.writeInt32(numModulesReq); + status_t status = remote()->transact(LIST_MODULES, data, &reply); + if (status == NO_ERROR) { + status = (status_t)reply.readInt32(); + *numModules = (unsigned int)reply.readInt32(); + } + ALOGV("listModules() status %d got *numModules %d", status, *numModules); + if (status == NO_ERROR) { + if (numModulesReq > *numModules) { + numModulesReq = *numModules; + } + if (numModulesReq > 0) { + reply.read(modules, numModulesReq * sizeof(struct sound_trigger_module_descriptor)); + } + } + return status; + } + + virtual status_t attach(const sound_trigger_module_handle_t handle, + const sp<ISoundTriggerClient>& client, + sp<ISoundTrigger>& module) + { + Parcel data, reply; + data.writeInterfaceToken(ISoundTriggerHwService::getInterfaceDescriptor()); + data.write(&handle, sizeof(sound_trigger_module_handle_t)); + data.writeStrongBinder(client->asBinder()); + remote()->transact(ATTACH, data, &reply); + status_t status = reply.readInt32(); + if (reply.readInt32() != 0) { + module = interface_cast<ISoundTrigger>(reply.readStrongBinder()); + } + return status; + } + +}; + +IMPLEMENT_META_INTERFACE(SoundTriggerHwService, "android.hardware.ISoundTriggerHwService"); + +// ---------------------------------------------------------------------- + +status_t BnSoundTriggerHwService::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case LIST_MODULES: { + CHECK_INTERFACE(ISoundTriggerHwService, data, reply); + unsigned int numModulesReq = data.readInt32(); + unsigned int numModules = numModulesReq; + struct sound_trigger_module_descriptor *modules = + (struct sound_trigger_module_descriptor *)calloc(numModulesReq, + sizeof(struct sound_trigger_module_descriptor)); + status_t status = listModules(modules, &numModules); + reply->writeInt32(status); + reply->writeInt32(numModules); + ALOGV("LIST_MODULES status %d got numModules %d", status, numModules); + + if (status == NO_ERROR) { + if (numModulesReq > numModules) { + numModulesReq = numModules; + } + reply->write(modules, + numModulesReq * sizeof(struct sound_trigger_module_descriptor)); + } + free(modules); + return NO_ERROR; + } + + case ATTACH: { + CHECK_INTERFACE(ISoundTriggerHwService, data, reply); + sound_trigger_module_handle_t handle; + data.read(&handle, sizeof(sound_trigger_module_handle_t)); + sp<ISoundTriggerClient> client = + interface_cast<ISoundTriggerClient>(data.readStrongBinder()); + sp<ISoundTrigger> module; + status_t status = attach(handle, client, module); + reply->writeInt32(status); + if (module != 0) { + reply->writeInt32(1); + reply->writeStrongBinder(module->asBinder()); + } else { + reply->writeInt32(0); + } + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/soundtrigger/SoundTrigger.cpp b/soundtrigger/SoundTrigger.cpp new file mode 100644 index 0000000..e43acd0 --- /dev/null +++ b/soundtrigger/SoundTrigger.cpp @@ -0,0 +1,253 @@ +/* +** +** Copyright (C) 2014, 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 "SoundTrigger" +//#define LOG_NDEBUG 0 + +#include <utils/Log.h> +#include <utils/threads.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <binder/IMemory.h> + +#include <soundtrigger/SoundTrigger.h> +#include <soundtrigger/ISoundTrigger.h> +#include <soundtrigger/ISoundTriggerHwService.h> +#include <soundtrigger/ISoundTriggerClient.h> +#include <soundtrigger/SoundTriggerCallback.h> + +namespace android { + +namespace { + sp<ISoundTriggerHwService> gSoundTriggerHwService; + const int kSoundTriggerHwServicePollDelay = 500000; // 0.5s + const char* kSoundTriggerHwServiceName = "media.sound_trigger_hw"; + Mutex gLock; + + class DeathNotifier : public IBinder::DeathRecipient + { + public: + DeathNotifier() { + } + + virtual void binderDied(const wp<IBinder>& who __unused) { + ALOGV("binderDied"); + Mutex::Autolock _l(gLock); + gSoundTriggerHwService.clear(); + ALOGW("Sound trigger service died!"); + } + }; + + sp<DeathNotifier> gDeathNotifier; +}; // namespace anonymous + +const sp<ISoundTriggerHwService>& SoundTrigger::getSoundTriggerHwService() +{ + Mutex::Autolock _l(gLock); + if (gSoundTriggerHwService.get() == 0) { + sp<IServiceManager> sm = defaultServiceManager(); + sp<IBinder> binder; + do { + binder = sm->getService(String16(kSoundTriggerHwServiceName)); + if (binder != 0) { + break; + } + ALOGW("SoundTriggerHwService not published, waiting..."); + usleep(kSoundTriggerHwServicePollDelay); + } while(true); + if (gDeathNotifier == NULL) { + gDeathNotifier = new DeathNotifier(); + } + binder->linkToDeath(gDeathNotifier); + gSoundTriggerHwService = interface_cast<ISoundTriggerHwService>(binder); + } + ALOGE_IF(gSoundTriggerHwService == 0, "no SoundTriggerHwService!?"); + return gSoundTriggerHwService; +} + +// Static methods +status_t SoundTrigger::listModules(struct sound_trigger_module_descriptor *modules, + uint32_t *numModules) +{ + ALOGV("listModules()"); + const sp<ISoundTriggerHwService>& service = getSoundTriggerHwService(); + if (service == 0) { + return NO_INIT; + } + return service->listModules(modules, numModules); +} + +sp<SoundTrigger> SoundTrigger::attach(const sound_trigger_module_handle_t module, + const sp<SoundTriggerCallback>& callback) +{ + ALOGV("attach()"); + sp<SoundTrigger> soundTrigger; + const sp<ISoundTriggerHwService>& service = getSoundTriggerHwService(); + if (service == 0) { + return soundTrigger; + } + soundTrigger = new SoundTrigger(module, callback); + status_t status = service->attach(module, soundTrigger, soundTrigger->mISoundTrigger); + + if (status == NO_ERROR && soundTrigger->mISoundTrigger != 0) { + soundTrigger->mISoundTrigger->asBinder()->linkToDeath(soundTrigger); + } else { + ALOGW("Error %d connecting to sound trigger service", status); + soundTrigger.clear(); + } + return soundTrigger; +} + + +// SoundTrigger +SoundTrigger::SoundTrigger(sound_trigger_module_handle_t module, + const sp<SoundTriggerCallback>& callback) + : mModule(module), mCallback(callback) +{ +} + +SoundTrigger::~SoundTrigger() +{ + if (mISoundTrigger != 0) { + mISoundTrigger->detach(); + } +} + + +void SoundTrigger::detach() { + ALOGV("detach()"); + Mutex::Autolock _l(mLock); + mCallback.clear(); + if (mISoundTrigger != 0) { + mISoundTrigger->detach(); + mISoundTrigger->asBinder()->unlinkToDeath(this); + mISoundTrigger = 0; + } +} + +status_t SoundTrigger::loadSoundModel(const sp<IMemory>& modelMemory, + sound_model_handle_t *handle) +{ + Mutex::Autolock _l(mLock); + if (mISoundTrigger == 0) { + return NO_INIT; + } + + return mISoundTrigger->loadSoundModel(modelMemory, handle); +} + +status_t SoundTrigger::unloadSoundModel(sound_model_handle_t handle) +{ + Mutex::Autolock _l(mLock); + if (mISoundTrigger == 0) { + return NO_INIT; + } + return mISoundTrigger->unloadSoundModel(handle); +} + +status_t SoundTrigger::startRecognition(sound_model_handle_t handle, + const sp<IMemory>& dataMemory) +{ + Mutex::Autolock _l(mLock); + if (mISoundTrigger == 0) { + return NO_INIT; + } + return mISoundTrigger->startRecognition(handle, dataMemory); +} + +status_t SoundTrigger::stopRecognition(sound_model_handle_t handle) +{ + Mutex::Autolock _l(mLock); + if (mISoundTrigger == 0) { + return NO_INIT; + } + return mISoundTrigger->stopRecognition(handle); +} + +// BpSoundTriggerClient +void SoundTrigger::onRecognitionEvent(const sp<IMemory>& eventMemory) +{ + Mutex::Autolock _l(mLock); + if (eventMemory == 0 || eventMemory->pointer() == NULL) { + return; + } + + if (mCallback != 0) { + mCallback->onRecognitionEvent( + (struct sound_trigger_recognition_event *)eventMemory->pointer()); + } +} + + +//IBinder::DeathRecipient +void SoundTrigger::binderDied(const wp<IBinder>& who __unused) { + Mutex::Autolock _l(mLock); + ALOGW("SoundTrigger server binder Died "); + mISoundTrigger = 0; + if (mCallback != 0) { + mCallback->onServiceDied(); + } +} + +status_t SoundTrigger::stringToGuid(const char *str, sound_trigger_uuid_t *guid) +{ + if (str == NULL || guid == NULL) { + return BAD_VALUE; + } + + int tmp[10]; + + if (sscanf(str, "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", + tmp, tmp+1, tmp+2, tmp+3, tmp+4, tmp+5, tmp+6, tmp+7, tmp+8, tmp+9) < 10) { + return BAD_VALUE; + } + guid->timeLow = (uint32_t)tmp[0]; + guid->timeMid = (uint16_t)tmp[1]; + guid->timeHiAndVersion = (uint16_t)tmp[2]; + guid->clockSeq = (uint16_t)tmp[3]; + guid->node[0] = (uint8_t)tmp[4]; + guid->node[1] = (uint8_t)tmp[5]; + guid->node[2] = (uint8_t)tmp[6]; + guid->node[3] = (uint8_t)tmp[7]; + guid->node[4] = (uint8_t)tmp[8]; + guid->node[5] = (uint8_t)tmp[9]; + + return NO_ERROR; +} + +status_t SoundTrigger::guidToString(const sound_trigger_uuid_t *guid, char *str, size_t maxLen) +{ + if (guid == NULL || str == NULL) { + return BAD_VALUE; + } + + snprintf(str, maxLen, "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", + guid->timeLow, + guid->timeMid, + guid->timeHiAndVersion, + guid->clockSeq, + guid->node[0], + guid->node[1], + guid->node[2], + guid->node[3], + guid->node[4], + guid->node[5]); + + return NO_ERROR; +} + +}; // namespace android |