Merge changes I70ebe6bc,I9a6d6401,Icbde26fe,I40299d95,Ifd8eb281, ...
* changes:
Create DeviceDescriptorBase.
Copy DeviceDescriptor to libaudiofoundation.
Rename AudioPort and PolicyAudioPort accordingly.
Make audio port in policy not derive from audio port base
Rename AudioProfileVector.h as AudioProfileVectorHelper.h
Leave AudioProfileVector only in libaudiofoundation.
diff --git a/apex/manifest.json b/apex/manifest.json
index 3011ee8..ddd642e 100644
--- a/apex/manifest.json
+++ b/apex/manifest.json
@@ -1,4 +1,4 @@
{
"name": "com.android.media",
- "version": 290000000
+ "version": 300000000
}
diff --git a/apex/manifest_codec.json b/apex/manifest_codec.json
index 83a5178..2320fd7 100644
--- a/apex/manifest_codec.json
+++ b/apex/manifest_codec.json
@@ -1,4 +1,4 @@
{
"name": "com.android.media.swcodec",
- "version": 290000000
+ "version": 300000000
}
diff --git a/camera/Camera.cpp b/camera/Camera.cpp
index 19849f8..84d1d93 100644
--- a/camera/Camera.cpp
+++ b/camera/Camera.cpp
@@ -347,13 +347,20 @@
return c->setPreviewCallbackTarget(callbackProducer);
}
-int32_t Camera::setAudioRestriction(int32_t mode)
+status_t Camera::setAudioRestriction(int32_t mode)
{
sp <::android::hardware::ICamera> c = mCamera;
if (c == 0) return NO_INIT;
return c->setAudioRestriction(mode);
}
+int32_t Camera::getGlobalAudioRestriction()
+{
+ sp <::android::hardware::ICamera> c = mCamera;
+ if (c == 0) return NO_INIT;
+ return c->getGlobalAudioRestriction();
+}
+
// callback from camera service
void Camera::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2)
{
diff --git a/camera/ICamera.cpp b/camera/ICamera.cpp
index 060e8e0..b83edf7 100644
--- a/camera/ICamera.cpp
+++ b/camera/ICamera.cpp
@@ -57,6 +57,7 @@
RELEASE_RECORDING_FRAME_HANDLE,
RELEASE_RECORDING_FRAME_HANDLE_BATCH,
SET_AUDIO_RESTRICTION,
+ GET_GLOBAL_AUDIO_RESTRICTION,
};
class BpCamera: public BpInterface<ICamera>
@@ -192,7 +193,7 @@
}
}
- int32_t setAudioRestriction(int32_t mode) {
+ status_t setAudioRestriction(int32_t mode) {
Parcel data, reply;
data.writeInterfaceToken(ICamera::getInterfaceDescriptor());
data.writeInt32(mode);
@@ -200,6 +201,13 @@
return reply.readInt32();
}
+ int32_t getGlobalAudioRestriction() {
+ Parcel data, reply;
+ data.writeInterfaceToken(ICamera::getInterfaceDescriptor());
+ remote()->transact(GET_GLOBAL_AUDIO_RESTRICTION, data, &reply);
+ return reply.readInt32();
+ }
+
status_t setVideoBufferMode(int32_t videoBufferMode)
{
ALOGV("setVideoBufferMode: %d", videoBufferMode);
@@ -509,6 +517,11 @@
reply->writeInt32(setAudioRestriction(mode));
return NO_ERROR;
} break;
+ case GET_GLOBAL_AUDIO_RESTRICTION: {
+ CHECK_INTERFACE(ICamera, data, reply);
+ reply->writeInt32(getGlobalAudioRestriction());
+ return NO_ERROR;
+ } break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl b/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl
index 5987b42..93549e0 100644
--- a/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl
+++ b/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl
@@ -168,7 +168,13 @@
*
* @param mode the audio restriction mode ID as above
*
- * @return the resulting system-wide audio restriction mode
*/
- int setCameraAudioRestriction(int mode);
+ void setCameraAudioRestriction(int mode);
+
+ /**
+ * Get global audio restriction mode for all camera clients.
+ *
+ * @return the currently applied system-wide audio restriction mode
+ */
+ int getGlobalAudioRestriction();
}
diff --git a/camera/include/camera/Camera.h b/camera/include/camera/Camera.h
index 9800bb7..2cdb617 100644
--- a/camera/include/camera/Camera.h
+++ b/camera/include/camera/Camera.h
@@ -167,7 +167,8 @@
sp<ICameraRecordingProxy> getRecordingProxy();
- int32_t setAudioRestriction(int32_t mode);
+ status_t setAudioRestriction(int32_t mode);
+ int32_t getGlobalAudioRestriction();
// ICameraClient interface
virtual void notifyCallback(int32_t msgType, int32_t ext, int32_t ext2);
diff --git a/camera/include/camera/android/hardware/ICamera.h b/camera/include/camera/android/hardware/ICamera.h
index eba9efe..ec19e5d 100644
--- a/camera/include/camera/android/hardware/ICamera.h
+++ b/camera/include/camera/android/hardware/ICamera.h
@@ -142,7 +142,10 @@
const sp<IGraphicBufferProducer>& bufferProducer) = 0;
// Set the audio restriction mode
- virtual int32_t setAudioRestriction(int32_t mode) = 0;
+ virtual status_t setAudioRestriction(int32_t mode) = 0;
+
+ // Get the global audio restriction mode
+ virtual int32_t getGlobalAudioRestriction() = 0;
};
// ----------------------------------------------------------------------------
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index bf36be0..cf9efe0 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -989,7 +989,7 @@
failed = false;
printf("getFrameAtTime(%s) => OK\n", filename);
- VideoFrame *frame = (VideoFrame *)mem->pointer();
+ VideoFrame *frame = (VideoFrame *)mem->unsecurePointer();
CHECK_EQ(writeJpegFile("/sdcard/out.jpg",
frame->getFlattenedData(),
diff --git a/cmds/stagefright/stream.cpp b/cmds/stagefright/stream.cpp
index 35bdbc0..59b5f9f 100644
--- a/cmds/stagefright/stream.cpp
+++ b/cmds/stagefright/stream.cpp
@@ -116,7 +116,7 @@
sp<IMemory> mem = mBuffers.itemAt(index);
- ssize_t n = read(mFd, mem->pointer(), mem->size());
+ ssize_t n = read(mFd, mem->unsecurePointer(), mem->size());
if (n <= 0) {
mListener->issueCommand(IStreamListener::EOS, false /* synchronous */);
} else {
@@ -238,7 +238,7 @@
copy = mem->size() - mCurrentBufferOffset;
}
- memcpy((uint8_t *)mem->pointer() + mCurrentBufferOffset, data, copy);
+ memcpy((uint8_t *)mem->unsecurePointer() + mCurrentBufferOffset, data, copy);
mCurrentBufferOffset += copy;
if (mCurrentBufferOffset == mem->size()) {
diff --git a/drm/drmserver/Android.bp b/drm/drmserver/Android.bp
index c25a0a1..fd71837 100644
--- a/drm/drmserver/Android.bp
+++ b/drm/drmserver/Android.bp
@@ -25,6 +25,7 @@
shared_libs: [
"libmedia",
+ "libmediametrics",
"libutils",
"liblog",
"libbinder",
diff --git a/drm/drmserver/DrmManager.cpp b/drm/drmserver/DrmManager.cpp
index afbcb39..66610b3 100644
--- a/drm/drmserver/DrmManager.cpp
+++ b/drm/drmserver/DrmManager.cpp
@@ -19,7 +19,10 @@
#include "utils/Log.h"
#include <utils/String8.h>
+
+#include <binder/IPCThreadState.h>
#include <drm/DrmInfo.h>
+
#include <drm/DrmInfoEvent.h>
#include <drm/DrmRights.h>
#include <drm/DrmConstraints.h>
@@ -28,6 +31,7 @@
#include <drm/DrmInfoRequest.h>
#include <drm/DrmSupportInfo.h>
#include <drm/DrmConvertedStatus.h>
+#include <media/MediaAnalyticsItem.h>
#include <IDrmEngine.h>
#include "DrmManager.h"
@@ -50,6 +54,40 @@
}
+void DrmManager::reportEngineMetrics(
+ const char func[], const String8& plugInId, const String8& mimeType) {
+ IDrmEngine& engine = mPlugInManager.getPlugIn(plugInId);
+
+ std::unique_ptr<MediaAnalyticsItem> item(MediaAnalyticsItem::create("drmmanager"));
+ item->generateSessionID();
+ item->setUid(IPCThreadState::self()->getCallingUid());
+ item->setCString("function_name", func);
+ item->setCString("plugin_id", plugInId.getPathLeaf().getBasePath().c_str());
+
+ std::unique_ptr<DrmSupportInfo> info(engine.getSupportInfo(0));
+ if (NULL != info) {
+ item->setCString("description", info->getDescription().c_str());
+ }
+
+ if (!mimeType.isEmpty()) {
+ item->setCString("mime_types", mimeType.c_str());
+ } else if (NULL != info) {
+ DrmSupportInfo::MimeTypeIterator mimeIter = info->getMimeTypeIterator();
+ String8 mimes;
+ while (mimeIter.hasNext()) {
+ mimes += mimeIter.next();
+ if (mimeIter.hasNext()) {
+ mimes += ",";
+ }
+ }
+ item->setCString("mime_types", mimes.c_str());
+ }
+
+ if (!item->selfrecord()) {
+ ALOGE("Failed to record metrics");
+ }
+}
+
int DrmManager::addUniqueId(bool isNative) {
Mutex::Autolock _l(mLock);
@@ -147,27 +185,36 @@
for (size_t index = 0; index < plugInIdList.size(); index++) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInIdList.itemAt(index));
rDrmEngine.terminate(uniqueId);
+ reportEngineMetrics(__func__, plugInIdList[index]);
}
}
DrmConstraints* DrmManager::getConstraints(int uniqueId, const String8* path, const int action) {
Mutex::Autolock _l(mLock);
+ DrmConstraints *constraints = NULL;
const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, *path);
if (EMPTY_STRING != plugInId) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
- return rDrmEngine.getConstraints(uniqueId, path, action);
+ constraints = rDrmEngine.getConstraints(uniqueId, path, action);
}
- return NULL;
+ if (NULL != constraints) {
+ reportEngineMetrics(__func__, plugInId);
+ }
+ return constraints;
}
DrmMetadata* DrmManager::getMetadata(int uniqueId, const String8* path) {
Mutex::Autolock _l(mLock);
+ DrmMetadata *meta = NULL;
const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, *path);
if (EMPTY_STRING != plugInId) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
- return rDrmEngine.getMetadata(uniqueId, path);
+ meta = rDrmEngine.getMetadata(uniqueId, path);
}
- return NULL;
+ if (NULL != meta) {
+ reportEngineMetrics(__func__, plugInId);
+ }
+ return meta;
}
bool DrmManager::canHandle(int uniqueId, const String8& path, const String8& mimeType) {
@@ -175,6 +222,10 @@
const String8 plugInId = getSupportedPlugInId(mimeType);
bool result = (EMPTY_STRING != plugInId) ? true : false;
+ if (result) {
+ reportEngineMetrics(__func__, plugInId, mimeType);
+ }
+
if (0 < path.length()) {
if (result) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
@@ -191,12 +242,17 @@
DrmInfoStatus* DrmManager::processDrmInfo(int uniqueId, const DrmInfo* drmInfo) {
Mutex::Autolock _l(mLock);
- const String8 plugInId = getSupportedPlugInId(drmInfo->getMimeType());
+ DrmInfoStatus *infoStatus = NULL;
+ const String8 mimeType = drmInfo->getMimeType();
+ const String8 plugInId = getSupportedPlugInId(mimeType);
if (EMPTY_STRING != plugInId) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
- return rDrmEngine.processDrmInfo(uniqueId, drmInfo);
+ infoStatus = rDrmEngine.processDrmInfo(uniqueId, drmInfo);
}
- return NULL;
+ if (NULL != infoStatus) {
+ reportEngineMetrics(__func__, plugInId, mimeType);
+ }
+ return infoStatus;
}
bool DrmManager::canHandle(int uniqueId, const String8& path) {
@@ -208,6 +264,7 @@
result = rDrmEngine.canHandle(uniqueId, path);
if (result) {
+ reportEngineMetrics(__func__, plugInPathList[i]);
break;
}
}
@@ -216,54 +273,75 @@
DrmInfo* DrmManager::acquireDrmInfo(int uniqueId, const DrmInfoRequest* drmInfoRequest) {
Mutex::Autolock _l(mLock);
- const String8 plugInId = getSupportedPlugInId(drmInfoRequest->getMimeType());
+ DrmInfo *info = NULL;
+ const String8 mimeType = drmInfoRequest->getMimeType();
+ const String8 plugInId = getSupportedPlugInId(mimeType);
if (EMPTY_STRING != plugInId) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
- return rDrmEngine.acquireDrmInfo(uniqueId, drmInfoRequest);
+ info = rDrmEngine.acquireDrmInfo(uniqueId, drmInfoRequest);
}
- return NULL;
+ if (NULL != info) {
+ reportEngineMetrics(__func__, plugInId, mimeType);
+ }
+ return info;
}
status_t DrmManager::saveRights(int uniqueId, const DrmRights& drmRights,
const String8& rightsPath, const String8& contentPath) {
Mutex::Autolock _l(mLock);
- const String8 plugInId = getSupportedPlugInId(drmRights.getMimeType());
+ const String8 mimeType = drmRights.getMimeType();
+ const String8 plugInId = getSupportedPlugInId(mimeType);
status_t result = DRM_ERROR_UNKNOWN;
if (EMPTY_STRING != plugInId) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
result = rDrmEngine.saveRights(uniqueId, drmRights, rightsPath, contentPath);
}
+ if (DRM_NO_ERROR == result) {
+ reportEngineMetrics(__func__, plugInId, mimeType);
+ }
return result;
}
String8 DrmManager::getOriginalMimeType(int uniqueId, const String8& path, int fd) {
Mutex::Autolock _l(mLock);
+ String8 mimeType(EMPTY_STRING);
const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, path);
if (EMPTY_STRING != plugInId) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
- return rDrmEngine.getOriginalMimeType(uniqueId, path, fd);
+ mimeType = rDrmEngine.getOriginalMimeType(uniqueId, path, fd);
}
- return EMPTY_STRING;
+ if (!mimeType.isEmpty()) {
+ reportEngineMetrics(__func__, plugInId, mimeType);
+ }
+ return mimeType;
}
int DrmManager::getDrmObjectType(int uniqueId, const String8& path, const String8& mimeType) {
Mutex::Autolock _l(mLock);
+ int type = DrmObjectType::UNKNOWN;
const String8 plugInId = getSupportedPlugInId(uniqueId, path, mimeType);
if (EMPTY_STRING != plugInId) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
- return rDrmEngine.getDrmObjectType(uniqueId, path, mimeType);
+ type = rDrmEngine.getDrmObjectType(uniqueId, path, mimeType);
}
- return DrmObjectType::UNKNOWN;
+ if (DrmObjectType::UNKNOWN != type) {
+ reportEngineMetrics(__func__, plugInId, mimeType);
+ }
+ return type;
}
int DrmManager::checkRightsStatus(int uniqueId, const String8& path, int action) {
Mutex::Autolock _l(mLock);
+ int rightsStatus = RightsStatus::RIGHTS_INVALID;
const String8 plugInId = getSupportedPlugInIdFromPath(uniqueId, path);
if (EMPTY_STRING != plugInId) {
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
- return rDrmEngine.checkRightsStatus(uniqueId, path, action);
+ rightsStatus = rDrmEngine.checkRightsStatus(uniqueId, path, action);
}
- return RightsStatus::RIGHTS_INVALID;
+ if (RightsStatus::RIGHTS_INVALID != rightsStatus) {
+ reportEngineMetrics(__func__, plugInId);
+ }
+ return rightsStatus;
}
status_t DrmManager::consumeRights(
@@ -307,6 +385,9 @@
IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
result = rDrmEngine.removeRights(uniqueId, path);
}
+ if (DRM_NO_ERROR == result) {
+ reportEngineMetrics(__func__, plugInId);
+ }
return result;
}
@@ -319,6 +400,7 @@
if (DRM_NO_ERROR != result) {
break;
}
+ reportEngineMetrics(__func__, plugInIdList[index]);
}
return result;
}
@@ -335,6 +417,7 @@
++mConvertId;
convertId = mConvertId;
mConvertSessionMap.add(convertId, &rDrmEngine);
+ reportEngineMetrics(__func__, plugInId, mimeType);
}
}
return convertId;
@@ -415,6 +498,7 @@
if (DRM_NO_ERROR == result) {
++mDecryptSessionId;
mDecryptSessionMap.add(mDecryptSessionId, &rDrmEngine);
+ reportEngineMetrics(__func__, plugInId, String8(mime));
break;
}
}
@@ -443,6 +527,7 @@
if (DRM_NO_ERROR == result) {
++mDecryptSessionId;
mDecryptSessionMap.add(mDecryptSessionId, &rDrmEngine);
+ reportEngineMetrics(__func__, plugInId, String8(mime));
break;
}
}
@@ -472,6 +557,7 @@
if (DRM_NO_ERROR == result) {
++mDecryptSessionId;
mDecryptSessionMap.add(mDecryptSessionId, &rDrmEngine);
+ reportEngineMetrics(__func__, plugInId, mimeType);
break;
}
}
diff --git a/drm/drmserver/DrmManager.h b/drm/drmserver/DrmManager.h
index 26222bc..75fc1a8 100644
--- a/drm/drmserver/DrmManager.h
+++ b/drm/drmserver/DrmManager.h
@@ -143,6 +143,9 @@
bool canHandle(int uniqueId, const String8& path);
+ void reportEngineMetrics(const char func[],
+ const String8& plugInId, const String8& mimeType = String8(""));
+
private:
enum {
kMaxNumUniqueIds = 0x1000,
diff --git a/drm/libmediadrm/CryptoHal.cpp b/drm/libmediadrm/CryptoHal.cpp
index 954608f..58110d4 100644
--- a/drm/libmediadrm/CryptoHal.cpp
+++ b/drm/libmediadrm/CryptoHal.cpp
@@ -321,7 +321,11 @@
}
// memory must be within the address space of the heap
- if (memory->pointer() != static_cast<uint8_t *>(heap->getBase()) + memory->offset() ||
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ if (memory->unsecurePointer() != static_cast<uint8_t *>(heap->getBase()) + memory->offset() ||
heap->getSize() < memory->offset() + memory->size() ||
SIZE_MAX - memory->offset() < memory->size()) {
android_errorWriteLog(0x534e4554, "76221123");
diff --git a/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp b/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp
index 23a35e5..f164f28 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp
@@ -62,10 +62,8 @@
secure, keyId, iv, mode, pattern, subSamples, source, offset, destination,
[&](Status_V1_2 hStatus, uint32_t hBytesWritten, hidl_string hDetailedError) {
status = toStatus_1_0(hStatus);
- if (status == Status::OK) {
- bytesWritten = hBytesWritten;
- detailedError = hDetailedError;
- }
+ bytesWritten = hBytesWritten;
+ detailedError = hDetailedError;
}
);
@@ -109,6 +107,10 @@
"destination decrypt buffer base not set");
return Void();
}
+ } else {
+ _hidl_cb(Status_V1_2::ERROR_DRM_CANNOT_HANDLE, 0,
+ "destination type not supported");
+ return Void();
}
sp<IMemory> sourceBase = mSharedBufferMap[source.bufferId];
@@ -126,38 +128,45 @@
(static_cast<void *>(sourceBase->getPointer()));
uint8_t* srcPtr = static_cast<uint8_t *>(base + source.offset + offset);
void* destPtr = NULL;
- if (destination.type == BufferType::SHARED_MEMORY) {
- const SharedBuffer& destBuffer = destination.nonsecureMemory;
- sp<IMemory> destBase = mSharedBufferMap[destBuffer.bufferId];
- if (destBase == nullptr) {
- _hidl_cb(Status_V1_2::ERROR_DRM_CANNOT_HANDLE, 0, "destination is a nullptr");
- return Void();
- }
-
- if (destBuffer.offset + destBuffer.size > destBase->getSize()) {
- _hidl_cb(Status_V1_2::ERROR_DRM_FRAME_TOO_LARGE, 0, "invalid buffer size");
- return Void();
- }
- destPtr = static_cast<void *>(base + destination.nonsecureMemory.offset);
- } else if (destination.type == BufferType::NATIVE_HANDLE) {
- native_handle_t *handle = const_cast<native_handle_t *>(
- destination.secureMemory.getNativeHandle());
- destPtr = static_cast<void *>(handle);
+ // destination.type == BufferType::SHARED_MEMORY
+ const SharedBuffer& destBuffer = destination.nonsecureMemory;
+ sp<IMemory> destBase = mSharedBufferMap[destBuffer.bufferId];
+ if (destBase == nullptr) {
+ _hidl_cb(Status_V1_2::ERROR_DRM_CANNOT_HANDLE, 0, "destination is a nullptr");
+ return Void();
}
+ if (destBuffer.offset + destBuffer.size > destBase->getSize()) {
+ _hidl_cb(Status_V1_2::ERROR_DRM_FRAME_TOO_LARGE, 0, "invalid buffer size");
+ return Void();
+ }
+ destPtr = static_cast<void *>(base + destination.nonsecureMemory.offset);
+
+
// Calculate the output buffer size and determine if any subsamples are
// encrypted.
size_t destSize = 0;
bool haveEncryptedSubsamples = false;
for (size_t i = 0; i < subSamples.size(); i++) {
const SubSample &subSample = subSamples[i];
- destSize += subSample.numBytesOfClearData;
- destSize += subSample.numBytesOfEncryptedData;
+ if (__builtin_add_overflow(destSize, subSample.numBytesOfClearData, &destSize)) {
+ _hidl_cb(Status_V1_2::ERROR_DRM_FRAME_TOO_LARGE, 0, "subsample clear size overflow");
+ return Void();
+ }
+ if (__builtin_add_overflow(destSize, subSample.numBytesOfEncryptedData, &destSize)) {
+ _hidl_cb(Status_V1_2::ERROR_DRM_FRAME_TOO_LARGE, 0, "subsample encrypted size overflow");
+ return Void();
+ }
if (subSample.numBytesOfEncryptedData > 0) {
haveEncryptedSubsamples = true;
}
}
+ if (destSize > destBuffer.size) {
+ _hidl_cb(Status_V1_2::ERROR_DRM_FRAME_TOO_LARGE, 0, "subsample sum too large");
+ return Void();
+ }
+
if (mode == Mode::UNENCRYPTED) {
if (haveEncryptedSubsamples) {
_hidl_cb(Status_V1_2::ERROR_DRM_CANNOT_HANDLE, 0,
diff --git a/media/bufferpool/2.0/Android.bp b/media/bufferpool/2.0/Android.bp
index 4ae3b24..97f114a 100644
--- a/media/bufferpool/2.0/Android.bp
+++ b/media/bufferpool/2.0/Android.bp
@@ -1,9 +1,5 @@
-cc_library {
- name: "libstagefright_bufferpool@2.0",
- vendor_available: true,
- vndk: {
- enabled: true,
- },
+cc_defaults {
+ name: "libstagefright_bufferpool@2.0-default",
srcs: [
"Accessor.cpp",
"AccessorImpl.cpp",
@@ -29,3 +25,23 @@
"android.hardware.media.bufferpool@2.0",
],
}
+
+cc_library {
+ name: "libstagefright_bufferpool@2.0.1",
+ defaults: ["libstagefright_bufferpool@2.0-default"],
+ vendor_available: true,
+ cflags: [
+ "-DBUFFERPOOL_CLONE_HANDLES",
+ ],
+}
+
+// Deprecated. Do not use. Use libstagefright_bufferpool@2.0.1 instead.
+cc_library {
+ name: "libstagefright_bufferpool@2.0",
+ defaults: ["libstagefright_bufferpool@2.0-default"],
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
+}
+
diff --git a/media/bufferpool/2.0/ClientManager.cpp b/media/bufferpool/2.0/ClientManager.cpp
index 48c2da4..87ee4e8 100644
--- a/media/bufferpool/2.0/ClientManager.cpp
+++ b/media/bufferpool/2.0/ClientManager.cpp
@@ -351,6 +351,7 @@
}
client = it->second;
}
+#ifdef BUFFERPOOL_CLONE_HANDLES
native_handle_t *origHandle;
ResultStatus res = client->allocate(params, &origHandle, buffer);
if (res != ResultStatus::OK) {
@@ -362,6 +363,9 @@
return ResultStatus::NO_MEMORY;
}
return ResultStatus::OK;
+#else
+ return client->allocate(params, handle, buffer);
+#endif
}
ResultStatus ClientManager::Impl::receive(
@@ -377,6 +381,7 @@
}
client = it->second;
}
+#ifdef BUFFERPOOL_CLONE_HANDLES
native_handle_t *origHandle;
ResultStatus res = client->receive(
transactionId, bufferId, timestampUs, &origHandle, buffer);
@@ -389,6 +394,9 @@
return ResultStatus::NO_MEMORY;
}
return ResultStatus::OK;
+#else
+ return client->receive(transactionId, bufferId, timestampUs, handle, buffer);
+#endif
}
ResultStatus ClientManager::Impl::postSend(
diff --git a/media/codec2/components/avc/C2SoftAvcDec.cpp b/media/codec2/components/avc/C2SoftAvcDec.cpp
index 3f015b4..fa98178 100644
--- a/media/codec2/components/avc/C2SoftAvcDec.cpp
+++ b/media/codec2/components/avc/C2SoftAvcDec.cpp
@@ -33,7 +33,8 @@
namespace {
constexpr char COMPONENT_NAME[] = "c2.android.avc.decoder";
-
+constexpr uint32_t kDefaultOutputDelay = 8;
+constexpr uint32_t kMaxOutputDelay = 16;
} // namespace
class C2SoftAvcDec::IntfImpl : public SimpleInterface<void>::BaseParams {
@@ -54,7 +55,9 @@
// TODO: Proper support for reorder depth.
addParameter(
DefineParam(mActualOutputDelay, C2_PARAMKEY_OUTPUT_DELAY)
- .withConstValue(new C2PortActualDelayTuning::output(8u))
+ .withDefault(new C2PortActualDelayTuning::output(kDefaultOutputDelay))
+ .withFields({C2F(mActualOutputDelay, value).inRange(0, kMaxOutputDelay)})
+ .withSetter(Setter<decltype(*mActualOutputDelay)>::StrictValueWithNoDeps)
.build());
// TODO: output latency and reordering
@@ -196,7 +199,6 @@
0u, HAL_PIXEL_FORMAT_YCBCR_420_888))
.build());
}
-
static C2R SizeSetter(bool mayBlock, const C2P<C2StreamPictureSizeInfo::output> &oldMe,
C2P<C2StreamPictureSizeInfo::output> &me) {
(void)mayBlock;
@@ -333,6 +335,7 @@
mDecHandle(nullptr),
mOutBufferFlush(nullptr),
mIvColorFormat(IV_YUV_420P),
+ mOutputDelay(kDefaultOutputDelay),
mWidth(320),
mHeight(240),
mHeaderDecoded(false),
@@ -882,6 +885,26 @@
work->result = C2_CORRUPTED;
return;
}
+ if (s_decode_op.i4_reorder_depth >= 0 && mOutputDelay != s_decode_op.i4_reorder_depth) {
+ mOutputDelay = s_decode_op.i4_reorder_depth;
+ ALOGV("New Output delay %d ", mOutputDelay);
+
+ C2PortActualDelayTuning::output outputDelay(mOutputDelay);
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+ c2_status_t err =
+ mIntf->config({&outputDelay}, C2_MAY_BLOCK, &failures);
+ if (err == OK) {
+ work->worklets.front()->output.configUpdate.push_back(
+ C2Param::Copy(outputDelay));
+ } else {
+ ALOGE("Cannot set output delay");
+ mSignalledError = true;
+ work->workletsProcessed = 1u;
+ work->result = C2_CORRUPTED;
+ return;
+ }
+ continue;
+ }
if (0 < s_decode_op.u4_pic_wd && 0 < s_decode_op.u4_pic_ht) {
if (mHeaderDecoded == false) {
mHeaderDecoded = true;
diff --git a/media/codec2/components/avc/C2SoftAvcDec.h b/media/codec2/components/avc/C2SoftAvcDec.h
index 72ee583..4414a26 100644
--- a/media/codec2/components/avc/C2SoftAvcDec.h
+++ b/media/codec2/components/avc/C2SoftAvcDec.h
@@ -157,7 +157,7 @@
size_t mNumCores;
IV_COLOR_FORMAT_T mIvColorFormat;
-
+ uint32_t mOutputDelay;
uint32_t mWidth;
uint32_t mHeight;
uint32_t mStride;
diff --git a/media/codec2/components/hevc/C2SoftHevcDec.cpp b/media/codec2/components/hevc/C2SoftHevcDec.cpp
index 7232572..df677c2 100644
--- a/media/codec2/components/hevc/C2SoftHevcDec.cpp
+++ b/media/codec2/components/hevc/C2SoftHevcDec.cpp
@@ -33,7 +33,8 @@
namespace {
constexpr char COMPONENT_NAME[] = "c2.android.hevc.decoder";
-
+constexpr uint32_t kDefaultOutputDelay = 8;
+constexpr uint32_t kMaxOutputDelay = 16;
} // namespace
class C2SoftHevcDec::IntfImpl : public SimpleInterface<void>::BaseParams {
@@ -54,7 +55,9 @@
// TODO: Proper support for reorder depth.
addParameter(
DefineParam(mActualOutputDelay, C2_PARAMKEY_OUTPUT_DELAY)
- .withConstValue(new C2PortActualDelayTuning::output(8u))
+ .withDefault(new C2PortActualDelayTuning::output(kDefaultOutputDelay))
+ .withFields({C2F(mActualOutputDelay, value).inRange(0, kMaxOutputDelay)})
+ .withSetter(Setter<decltype(*mActualOutputDelay)>::StrictValueWithNoDeps)
.build());
addParameter(
@@ -327,6 +330,7 @@
mDecHandle(nullptr),
mOutBufferFlush(nullptr),
mIvColorformat(IV_YUV_420P),
+ mOutputDelay(kDefaultOutputDelay),
mWidth(320),
mHeight(240),
mHeaderDecoded(false),
@@ -877,6 +881,26 @@
work->result = C2_CORRUPTED;
return;
}
+ if (s_decode_op.i4_reorder_depth >= 0 && mOutputDelay != s_decode_op.i4_reorder_depth) {
+ mOutputDelay = s_decode_op.i4_reorder_depth;
+ ALOGV("New Output delay %d ", mOutputDelay);
+
+ C2PortActualDelayTuning::output outputDelay(mOutputDelay);
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+ c2_status_t err =
+ mIntf->config({&outputDelay}, C2_MAY_BLOCK, &failures);
+ if (err == OK) {
+ work->worklets.front()->output.configUpdate.push_back(
+ C2Param::Copy(outputDelay));
+ } else {
+ ALOGE("Cannot set output delay");
+ mSignalledError = true;
+ work->workletsProcessed = 1u;
+ work->result = C2_CORRUPTED;
+ return;
+ }
+ continue;
+ }
if (0 < s_decode_op.u4_pic_wd && 0 < s_decode_op.u4_pic_ht) {
if (mHeaderDecoded == false) {
mHeaderDecoded = true;
diff --git a/media/codec2/components/hevc/C2SoftHevcDec.h b/media/codec2/components/hevc/C2SoftHevcDec.h
index b7664e6..ce63a6c 100644
--- a/media/codec2/components/hevc/C2SoftHevcDec.h
+++ b/media/codec2/components/hevc/C2SoftHevcDec.h
@@ -115,7 +115,7 @@
size_t mNumCores;
IV_COLOR_FORMAT_T mIvColorformat;
-
+ uint32_t mOutputDelay;
uint32_t mWidth;
uint32_t mHeight;
uint32_t mStride;
diff --git a/media/codec2/hidl/1.0/utils/Android.bp b/media/codec2/hidl/1.0/utils/Android.bp
index 9db85d5..bdff29a 100644
--- a/media/codec2/hidl/1.0/utils/Android.bp
+++ b/media/codec2/hidl/1.0/utils/Android.bp
@@ -24,7 +24,7 @@
"libgui",
"libhidlbase",
"liblog",
- "libstagefright_bufferpool@2.0",
+ "libstagefright_bufferpool@2.0.1",
"libui",
"libutils",
],
@@ -37,7 +37,7 @@
"android.hardware.media.c2@1.0",
"libcodec2",
"libgui",
- "libstagefright_bufferpool@2.0",
+ "libstagefright_bufferpool@2.0.1",
"libui",
],
}
@@ -81,7 +81,7 @@
"libcutils",
"libhidlbase",
"liblog",
- "libstagefright_bufferpool@2.0",
+ "libstagefright_bufferpool@2.0.1",
"libstagefright_bufferqueue_helper",
"libui",
"libutils",
@@ -96,7 +96,7 @@
"libcodec2",
"libcodec2_vndk",
"libhidlbase",
- "libstagefright_bufferpool@2.0",
+ "libstagefright_bufferpool@2.0.1",
"libui",
],
}
diff --git a/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp b/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp
index 6469735..a8a552c 100644
--- a/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp
@@ -547,6 +547,10 @@
if (mCompName == raw) {
bitStreamInfo[0] = 8000;
bitStreamInfo[1] = 1;
+ } else if (mCompName == g711alaw || mCompName == g711mlaw) {
+ // g711 test data is all 1-channel and has no embedded config info.
+ bitStreamInfo[0] = 8000;
+ bitStreamInfo[1] = 1;
} else {
ASSERT_NO_FATAL_FAILURE(
getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
diff --git a/media/codec2/hidl/client/Android.bp b/media/codec2/hidl/client/Android.bp
index 672369c..89c1c4a 100644
--- a/media/codec2/hidl/client/Android.bp
+++ b/media/codec2/hidl/client/Android.bp
@@ -18,7 +18,7 @@
"libgui",
"libhidlbase",
"liblog",
- "libstagefright_bufferpool@2.0",
+ "libstagefright_bufferpool@2.0.1",
"libui",
"libutils",
],
diff --git a/media/codec2/hidl/client/client.cpp b/media/codec2/hidl/client/client.cpp
index 5ed54f1..c620bad 100644
--- a/media/codec2/hidl/client/client.cpp
+++ b/media/codec2/hidl/client/client.cpp
@@ -883,66 +883,72 @@
Codec2Client::CreateComponentByName(
const char* componentName,
const std::shared_ptr<Listener>& listener,
- std::shared_ptr<Codec2Client>* owner) {
- std::shared_ptr<Component> component;
- c2_status_t status = ForAllServices(
- componentName,
- [owner, &component, componentName, &listener](
- const std::shared_ptr<Codec2Client> &client)
- -> c2_status_t {
- c2_status_t status = client->createComponent(componentName,
- listener,
- &component);
- if (status == C2_OK) {
- if (owner) {
- *owner = client;
+ std::shared_ptr<Codec2Client>* owner,
+ size_t numberOfAttempts) {
+ while (true) {
+ std::shared_ptr<Component> component;
+ c2_status_t status = ForAllServices(
+ componentName,
+ [owner, &component, componentName, &listener](
+ const std::shared_ptr<Codec2Client> &client)
+ -> c2_status_t {
+ c2_status_t status = client->createComponent(componentName,
+ listener,
+ &component);
+ if (status == C2_OK) {
+ if (owner) {
+ *owner = client;
+ }
+ } else if (status != C2_NOT_FOUND) {
+ LOG(DEBUG) << "IComponentStore("
+ << client->getServiceName()
+ << ")::createComponent(\"" << componentName
+ << "\") returned status = "
+ << status << ".";
}
- } else if (status != C2_NOT_FOUND) {
- LOG(DEBUG) << "IComponentStore("
- << client->getServiceName()
- << ")::createComponent(\"" << componentName
- << "\") returned status = "
- << status << ".";
- }
- return status;
- });
- if (status != C2_OK) {
- LOG(DEBUG) << "Could not create component \"" << componentName << "\". "
- "Status = " << status << ".";
+ return status;
+ });
+ if (numberOfAttempts > 0 && status == C2_TRANSACTION_FAILED) {
+ --numberOfAttempts;
+ continue;
+ }
+ return component;
}
- return component;
}
std::shared_ptr<Codec2Client::Interface>
Codec2Client::CreateInterfaceByName(
const char* interfaceName,
- std::shared_ptr<Codec2Client>* owner) {
- std::shared_ptr<Interface> interface;
- c2_status_t status = ForAllServices(
- interfaceName,
- [owner, &interface, interfaceName](
- const std::shared_ptr<Codec2Client> &client)
- -> c2_status_t {
- c2_status_t status = client->createInterface(interfaceName,
- &interface);
- if (status == C2_OK) {
- if (owner) {
- *owner = client;
+ std::shared_ptr<Codec2Client>* owner,
+ size_t numberOfAttempts) {
+ while (true) {
+ std::shared_ptr<Interface> interface;
+ c2_status_t status = ForAllServices(
+ interfaceName,
+ [owner, &interface, interfaceName](
+ const std::shared_ptr<Codec2Client> &client)
+ -> c2_status_t {
+ c2_status_t status = client->createInterface(interfaceName,
+ &interface);
+ if (status == C2_OK) {
+ if (owner) {
+ *owner = client;
+ }
+ } else if (status != C2_NOT_FOUND) {
+ LOG(DEBUG) << "IComponentStore("
+ << client->getServiceName()
+ << ")::createInterface(\"" << interfaceName
+ << "\") returned status = "
+ << status << ".";
}
- } else if (status != C2_NOT_FOUND) {
- LOG(DEBUG) << "IComponentStore("
- << client->getServiceName()
- << ")::createInterface(\"" << interfaceName
- << "\") returned status = "
- << status << ".";
- }
- return status;
- });
- if (status != C2_OK) {
- LOG(DEBUG) << "Could not create interface \"" << interfaceName << "\". "
- "Status = " << status << ".";
+ return status;
+ });
+ if (numberOfAttempts > 0 && status == C2_TRANSACTION_FAILED) {
+ --numberOfAttempts;
+ continue;
+ }
+ return interface;
}
- return interface;
}
std::vector<C2Component::Traits> const& Codec2Client::ListComponents() {
diff --git a/media/codec2/hidl/client/include/codec2/hidl/client.h b/media/codec2/hidl/client/include/codec2/hidl/client.h
index b8a7fb5..848901d 100644
--- a/media/codec2/hidl/client/include/codec2/hidl/client.h
+++ b/media/codec2/hidl/client/include/codec2/hidl/client.h
@@ -179,17 +179,21 @@
static std::vector<std::shared_ptr<Codec2Client>> CreateFromAllServices();
// Try to create a component with a given name from all known
- // IComponentStore services.
+ // IComponentStore services. numberOfAttempts determines the number of times
+ // to retry the HIDL call if the transaction fails.
static std::shared_ptr<Component> CreateComponentByName(
char const* componentName,
std::shared_ptr<Listener> const& listener,
- std::shared_ptr<Codec2Client>* owner = nullptr);
+ std::shared_ptr<Codec2Client>* owner = nullptr,
+ size_t numberOfAttempts = 10);
// Try to create a component interface with a given name from all known
- // IComponentStore services.
+ // IComponentStore services. numberOfAttempts determines the number of times
+ // to retry the HIDL call if the transaction fails.
static std::shared_ptr<Interface> CreateInterfaceByName(
char const* interfaceName,
- std::shared_ptr<Codec2Client>* owner = nullptr);
+ std::shared_ptr<Codec2Client>* owner = nullptr,
+ size_t numberOfAttempts = 10);
// List traits from all known IComponentStore services.
static std::vector<C2Component::Traits> const& ListComponents();
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 1883225..d61b751 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -225,7 +225,7 @@
mFirstValidFrameIndex(0u),
mMetaMode(MODE_NONE),
mInputMetEos(false) {
- mOutputSurface.lock()->maxDequeueBuffers = 2 * kSmoothnessFactor + kRenderingDepth;
+ mOutputSurface.lock()->maxDequeueBuffers = kSmoothnessFactor + kRenderingDepth;
{
Mutexed<Input>::Locked input(mInput);
input->buffers.reset(new DummyInputBuffers(""));
@@ -949,8 +949,11 @@
uint32_t outputGeneration;
{
Mutexed<OutputSurface>::Locked output(mOutputSurface);
- output->maxDequeueBuffers = numOutputSlots + numInputSlots +
+ output->maxDequeueBuffers = numOutputSlots +
reorderDepth.value + kRenderingDepth;
+ if (!secure) {
+ output->maxDequeueBuffers += numInputSlots;
+ }
outputSurface = output->surface ?
output->surface->getIGraphicBufferProducer() : nullptr;
if (outputSurface) {
@@ -1330,14 +1333,18 @@
case C2PortReorderBufferDepthTuning::CORE_INDEX: {
C2PortReorderBufferDepthTuning::output reorderDepth;
if (reorderDepth.updateFrom(*param)) {
+ bool secure = mComponent->getName().find(".secure") != std::string::npos;
mReorderStash.lock()->setDepth(reorderDepth.value);
ALOGV("[%s] onWorkDone: updated reorder depth to %u",
mName, reorderDepth.value);
size_t numOutputSlots = mOutput.lock()->numSlots;
size_t numInputSlots = mInput.lock()->numSlots;
Mutexed<OutputSurface>::Locked output(mOutputSurface);
- output->maxDequeueBuffers = numOutputSlots + numInputSlots +
+ output->maxDequeueBuffers = numOutputSlots +
reorderDepth.value + kRenderingDepth;
+ if (!secure) {
+ output->maxDequeueBuffers += numInputSlots;
+ }
if (output->surface) {
output->surface->setMaxDequeuedBufferCount(output->maxDequeueBuffers);
}
@@ -1381,6 +1388,7 @@
if (outputDelay.updateFrom(*param)) {
ALOGV("[%s] onWorkDone: updating output delay %u",
mName, outputDelay.value);
+ bool secure = mComponent->getName().find(".secure") != std::string::npos;
(void)mPipelineWatcher.lock()->outputDelay(outputDelay.value);
bool outputBuffersChanged = false;
@@ -1410,8 +1418,10 @@
uint32_t depth = mReorderStash.lock()->depth();
Mutexed<OutputSurface>::Locked output(mOutputSurface);
- output->maxDequeueBuffers = numOutputSlots + numInputSlots +
- depth + kRenderingDepth;
+ output->maxDequeueBuffers = numOutputSlots + depth + kRenderingDepth;
+ if (!secure) {
+ output->maxDequeueBuffers += numInputSlots;
+ }
if (output->surface) {
output->surface->setMaxDequeuedBufferCount(output->maxDequeueBuffers);
}
diff --git a/media/codec2/sfplugin/Codec2Buffer.cpp b/media/codec2/sfplugin/Codec2Buffer.cpp
index 5c8ad56..b339a92 100644
--- a/media/codec2/sfplugin/Codec2Buffer.cpp
+++ b/media/codec2/sfplugin/Codec2Buffer.cpp
@@ -764,7 +764,11 @@
const std::shared_ptr<C2LinearBlock> &block,
const sp<IMemory> &memory,
int32_t heapSeqNum)
- : Codec2Buffer(format, new ABuffer(memory->pointer(), memory->size())),
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ : Codec2Buffer(format, new ABuffer(memory->unsecurePointer(), memory->size())),
mBlock(block),
mMemory(memory),
mHeapSeqNum(heapSeqNum) {
@@ -800,7 +804,7 @@
if (view.size() < length) {
return false;
}
- memcpy(view.data(), decrypted->pointer(), length);
+ memcpy(view.data(), decrypted->unsecurePointer(), length);
return true;
}
diff --git a/media/codec2/vndk/Android.bp b/media/codec2/vndk/Android.bp
index b6ddfab..52cc7ad 100644
--- a/media/codec2/vndk/Android.bp
+++ b/media/codec2/vndk/Android.bp
@@ -66,7 +66,7 @@
"liblog",
"libnativewindow",
"libstagefright_foundation",
- "libstagefright_bufferpool@2.0",
+ "libstagefright_bufferpool@2.0.1",
"libui",
"libutils",
],
diff --git a/media/libaudioclient/AudioEffect.cpp b/media/libaudioclient/AudioEffect.cpp
index cf11936..1cc5fe6 100644
--- a/media/libaudioclient/AudioEffect.cpp
+++ b/media/libaudioclient/AudioEffect.cpp
@@ -159,7 +159,11 @@
mIEffect = iEffect;
mCblkMemory = cblk;
- mCblk = static_cast<effect_param_cblk_t*>(cblk->pointer());
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ mCblk = static_cast<effect_param_cblk_t*>(cblk->unsecurePointer());
int bufOffset = ((sizeof(effect_param_cblk_t) - 1) / sizeof(int) + 1) * sizeof(int);
mCblk->buffer = (uint8_t *)mCblk + bufOffset;
diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp
index a1b04ca..0f2d48e 100644
--- a/media/libaudioclient/AudioRecord.cpp
+++ b/media/libaudioclient/AudioRecord.cpp
@@ -759,7 +759,11 @@
status = NO_INIT;
goto exit;
}
- iMemPointer = output.cblk ->pointer();
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ iMemPointer = output.cblk ->unsecurePointer();
if (iMemPointer == NULL) {
ALOGE("%s(%d): Could not get control block pointer", __func__, mPortId);
status = NO_INIT;
@@ -774,7 +778,11 @@
if (output.buffers == 0) {
buffers = cblk + 1;
} else {
- buffers = output.buffers->pointer();
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ buffers = output.buffers->unsecurePointer();
if (buffers == NULL) {
ALOGE("%s(%d): Could not get buffer pointer", __func__, mPortId);
status = NO_INIT;
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index 4a80cd3..e8d7b60 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -406,7 +406,7 @@
mDoNotReconnect = doNotReconnect;
ALOGV_IF(sharedBuffer != 0, "%s(): sharedBuffer: %p, size: %zu",
- __func__, sharedBuffer->pointer(), sharedBuffer->size());
+ __func__, sharedBuffer->unsecurePointer(), sharedBuffer->size());
ALOGV("%s(): streamType %d frameCount %zu flags %04x",
__func__, streamType, frameCount, flags);
@@ -1508,7 +1508,11 @@
status = NO_INIT;
goto exit;
}
- void *iMemPointer = iMem->pointer();
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ void *iMemPointer = iMem->unsecurePointer();
if (iMemPointer == NULL) {
ALOGE("%s(%d): Could not get control block pointer", __func__, mPortId);
status = NO_INIT;
@@ -1563,7 +1567,11 @@
if (mSharedBuffer == 0) {
buffers = cblk + 1;
} else {
- buffers = mSharedBuffer->pointer();
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ buffers = mSharedBuffer->unsecurePointer();
if (buffers == NULL) {
ALOGE("%s(%d): Could not get buffer pointer", __func__, mPortId);
status = NO_INIT;
diff --git a/media/libaudioclient/IAudioTrack.cpp b/media/libaudioclient/IAudioTrack.cpp
index 83a568a..6219e7a 100644
--- a/media/libaudioclient/IAudioTrack.cpp
+++ b/media/libaudioclient/IAudioTrack.cpp
@@ -62,7 +62,7 @@
status_t status = remote()->transact(GET_CBLK, data, &reply);
if (status == NO_ERROR) {
cblk = interface_cast<IMemory>(reply.readStrongBinder());
- if (cblk != 0 && cblk->pointer() == NULL) {
+ if (cblk != 0 && cblk->unsecurePointer() == NULL) {
cblk.clear();
}
}
diff --git a/media/libaudioclient/IEffect.cpp b/media/libaudioclient/IEffect.cpp
index ce72dae..5d47dff 100644
--- a/media/libaudioclient/IEffect.cpp
+++ b/media/libaudioclient/IEffect.cpp
@@ -122,7 +122,7 @@
status_t status = remote()->transact(GET_CBLK, data, &reply);
if (status == NO_ERROR) {
cblk = interface_cast<IMemory>(reply.readStrongBinder());
- if (cblk != 0 && cblk->pointer() == NULL) {
+ if (cblk != 0 && cblk->unsecurePointer() == NULL) {
cblk.clear();
}
}
diff --git a/media/libaudioclient/include/media/IAudioFlinger.h b/media/libaudioclient/include/media/IAudioFlinger.h
index db09ddf..b580a88 100644
--- a/media/libaudioclient/include/media/IAudioFlinger.h
+++ b/media/libaudioclient/include/media/IAudioFlinger.h
@@ -70,8 +70,12 @@
return DEAD_OBJECT;
}
if (parcel->readInt32() != 0) {
+ // TODO: Using unsecurePointer() has some associated security
+ // pitfalls (see declaration for details).
+ // Either document why it is safe in this case or address
+ // the issue (e.g. by copying).
sharedBuffer = interface_cast<IMemory>(parcel->readStrongBinder());
- if (sharedBuffer == 0 || sharedBuffer->pointer() == NULL) {
+ if (sharedBuffer == 0 || sharedBuffer->unsecurePointer() == NULL) {
return BAD_VALUE;
}
}
@@ -269,13 +273,21 @@
(void)parcel->read(&inputId, sizeof(audio_io_handle_t));
if (parcel->readInt32() != 0) {
cblk = interface_cast<IMemory>(parcel->readStrongBinder());
- if (cblk == 0 || cblk->pointer() == NULL) {
+ // TODO: Using unsecurePointer() has some associated security
+ // pitfalls (see declaration for details).
+ // Either document why it is safe in this case or address
+ // the issue (e.g. by copying).
+ if (cblk == 0 || cblk->unsecurePointer() == NULL) {
return BAD_VALUE;
}
}
if (parcel->readInt32() != 0) {
buffers = interface_cast<IMemory>(parcel->readStrongBinder());
- if (buffers == 0 || buffers->pointer() == NULL) {
+ // TODO: Using unsecurePointer() has some associated security
+ // pitfalls (see declaration for details).
+ // Either document why it is safe in this case or address
+ // the issue (e.g. by copying).
+ if (buffers == 0 || buffers->unsecurePointer() == NULL) {
return BAD_VALUE;
}
}
diff --git a/media/libheif/HeifDecoderImpl.cpp b/media/libheif/HeifDecoderImpl.cpp
index bad4210..33ea1ca 100644
--- a/media/libheif/HeifDecoderImpl.cpp
+++ b/media/libheif/HeifDecoderImpl.cpp
@@ -173,7 +173,7 @@
// copy from cache if the request falls entirely in cache
if (offset + size <= mCachedOffset + mCachedSize) {
- memcpy(mMemory->pointer(), mCache.get() + offset - mCachedOffset, size);
+ memcpy(mMemory->unsecurePointer(), mCache.get() + offset - mCachedOffset, size);
return size;
}
@@ -271,7 +271,7 @@
if (bytesAvailable < (int64_t)size) {
size = bytesAvailable;
}
- memcpy(mMemory->pointer(), mCache.get() + offset - mCachedOffset, size);
+ memcpy(mMemory->unsecurePointer(), mCache.get() + offset - mCachedOffset, size);
return size;
}
@@ -360,12 +360,16 @@
sp<IMemory> sharedMem = mRetriever->getImageAtIndex(
-1, mOutputColor, true /*metaOnly*/);
- if (sharedMem == nullptr || sharedMem->pointer() == nullptr) {
+ if (sharedMem == nullptr || sharedMem->unsecurePointer() == nullptr) {
ALOGE("init: videoFrame is a nullptr");
return false;
}
- VideoFrame* videoFrame = static_cast<VideoFrame*>(sharedMem->pointer());
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ VideoFrame* videoFrame = static_cast<VideoFrame*>(sharedMem->unsecurePointer());
ALOGV("Image dimension %dx%d, display %dx%d, angle %d, iccSize %d",
videoFrame->mWidth,
@@ -391,12 +395,17 @@
MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC,
mOutputColor, true /*metaOnly*/);
- if (sharedMem == nullptr || sharedMem->pointer() == nullptr) {
+ if (sharedMem == nullptr || sharedMem->unsecurePointer() == nullptr) {
ALOGE("init: videoFrame is a nullptr");
return false;
}
- VideoFrame* videoFrame = static_cast<VideoFrame*>(sharedMem->pointer());
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ VideoFrame* videoFrame = static_cast<VideoFrame*>(
+ sharedMem->unsecurePointer());
ALOGV("Sequence dimension %dx%d, display %dx%d, angle %d, iccSize %d",
videoFrame->mWidth,
@@ -487,7 +496,7 @@
{
Mutex::Autolock autolock(mLock);
- if (frameMemory == nullptr || frameMemory->pointer() == nullptr) {
+ if (frameMemory == nullptr || frameMemory->unsecurePointer() == nullptr) {
mAsyncDecodeDone = true;
mScanlineReady.signal();
break;
@@ -529,12 +538,16 @@
sp<IMemory> frameMemory = mRetriever->getImageRectAtIndex(
-1, mOutputColor, 0, 0, mImageInfo.mWidth, mSliceHeight);
- if (frameMemory == nullptr || frameMemory->pointer() == nullptr) {
+ if (frameMemory == nullptr || frameMemory->unsecurePointer() == nullptr) {
ALOGE("decode: metadata is a nullptr");
return false;
}
- VideoFrame* videoFrame = static_cast<VideoFrame*>(frameMemory->pointer());
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ VideoFrame* videoFrame = static_cast<VideoFrame*>(frameMemory->unsecurePointer());
if (frameInfo != nullptr) {
initFrameInfo(frameInfo, videoFrame);
@@ -563,12 +576,16 @@
MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC, mOutputColor);
}
- if (mFrameMemory == nullptr || mFrameMemory->pointer() == nullptr) {
+ if (mFrameMemory == nullptr || mFrameMemory->unsecurePointer() == nullptr) {
ALOGE("decode: videoFrame is a nullptr");
return false;
}
- VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->pointer());
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->unsecurePointer());
if (videoFrame->mSize == 0 ||
mFrameMemory->size() < videoFrame->getFlattenedSize()) {
ALOGE("decode: videoFrame size is invalid");
@@ -613,12 +630,16 @@
mTotalScanline = mSequenceInfo.mHeight;
mFrameMemory = mRetriever->getFrameAtIndex(frameIndex, mOutputColor);
- if (mFrameMemory == nullptr || mFrameMemory->pointer() == nullptr) {
+ if (mFrameMemory == nullptr || mFrameMemory->unsecurePointer() == nullptr) {
ALOGE("decode: videoFrame is a nullptr");
return false;
}
- VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->pointer());
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->unsecurePointer());
if (videoFrame->mSize == 0 ||
mFrameMemory->size() < videoFrame->getFlattenedSize()) {
ALOGE("decode: videoFrame size is invalid");
@@ -641,10 +662,14 @@
}
bool HeifDecoderImpl::getScanlineInner(uint8_t* dst) {
- if (mFrameMemory == nullptr || mFrameMemory->pointer() == nullptr) {
+ if (mFrameMemory == nullptr || mFrameMemory->unsecurePointer() == nullptr) {
return false;
}
- VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->pointer());
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->unsecurePointer());
uint8_t* src = videoFrame->getFlattenedData() + videoFrame->mRowBytes * mCurScanline++;
memcpy(dst, src, videoFrame->mBytesPerPixel * videoFrame->mWidth);
return true;
diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp
index 8b97fbe..8de6381 100644
--- a/media/libmedia/Android.bp
+++ b/media/libmedia/Android.bp
@@ -1,10 +1,3 @@
-cc_defaults {
- name: "libmedia_defaults",
- include_dirs: [
- "bionic/libc/private",
- ],
-}
-
cc_library_headers {
name: "libmedia_headers",
vendor_available: true,
@@ -227,8 +220,6 @@
cc_library {
name: "libmedia",
- defaults: [ "libmedia_defaults" ],
-
srcs: [
"IDataSource.cpp",
"BufferingSettings.cpp",
@@ -274,6 +265,7 @@
},
header_libs: [
+ "bionic_libc_platform_headers",
"libstagefright_headers",
"media_ndk_headers",
],
@@ -339,8 +331,6 @@
cc_library_static {
name: "libmedia_player2_util",
- defaults: [ "libmedia_defaults" ],
-
srcs: [
"AudioParameter.cpp",
"BufferingSettings.cpp",
diff --git a/media/libmedia/IMediaHTTPConnection.cpp b/media/libmedia/IMediaHTTPConnection.cpp
index 1bb8d67..8cbb4c2 100644
--- a/media/libmedia/IMediaHTTPConnection.cpp
+++ b/media/libmedia/IMediaHTTPConnection.cpp
@@ -128,12 +128,12 @@
ALOGE("readAt got a NULL buffer");
return UNKNOWN_ERROR;
}
- if (mMemory->pointer() == NULL) {
- ALOGE("readAt got a NULL mMemory->pointer()");
+ if (mMemory->unsecurePointer() == NULL) {
+ ALOGE("readAt got a NULL mMemory->unsecurePointer()");
return UNKNOWN_ERROR;
}
- memcpy(buffer, mMemory->pointer(), len);
+ memcpy(buffer, mMemory->unsecurePointer(), len);
return len;
}
diff --git a/media/libmedia/MediaUtils.cpp b/media/libmedia/MediaUtils.cpp
index 31972fa..2efb30e 100644
--- a/media/libmedia/MediaUtils.cpp
+++ b/media/libmedia/MediaUtils.cpp
@@ -22,7 +22,7 @@
#include <sys/resource.h>
#include <unistd.h>
-#include <bionic_malloc.h>
+#include <bionic/malloc.h>
#include "MediaUtils.h"
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
index 4a3c65e..b44eb43 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.cpp
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
@@ -292,7 +292,7 @@
delete albumArt;
return NULL;
}
- MediaAlbumArt::init((MediaAlbumArt *) mAlbumArt->pointer(),
+ MediaAlbumArt::init((MediaAlbumArt *) mAlbumArt->unsecurePointer(),
albumArt->size(), albumArt->data());
delete albumArt; // We've taken our copy.
return mAlbumArt;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 39be40d..c30f048 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -33,6 +33,7 @@
#include <media/stagefright/Utils.h>
#include <media/stagefright/VideoFrameScheduler.h>
#include <media/MediaCodecBuffer.h>
+#include <utils/SystemClock.h>
#include <inttypes.h>
@@ -156,6 +157,7 @@
CHECK(mediaClock != NULL);
mPlaybackRate = mPlaybackSettings.mSpeed;
mMediaClock->setPlaybackRate(mPlaybackRate);
+ (void)mSyncFlag.test_and_set();
}
NuPlayer::Renderer::~Renderer() {
@@ -326,9 +328,27 @@
mSyncQueues = false;
}
+ // Wait until the current job in the message queue is done, to make sure
+ // buffer processing from the old generation is finished. After the current
+ // job is finished, access to buffers are protected by generation.
+ Mutex::Autolock syncLock(mSyncLock);
+ int64_t syncCount = mSyncCount;
+ mSyncFlag.clear();
+
+ // Make sure message queue is not empty after mSyncFlag is cleared.
sp<AMessage> msg = new AMessage(kWhatFlush, this);
msg->setInt32("audio", static_cast<int32_t>(audio));
msg->post();
+
+ int64_t uptimeMs = uptimeMillis();
+ while (mSyncCount == syncCount) {
+ (void)mSyncCondition.waitRelative(mSyncLock, ms2ns(1000));
+ if (uptimeMillis() - uptimeMs > 1000) {
+ ALOGW("flush(): no wake-up from sync point for 1s; stop waiting to "
+ "prevent being stuck indefinitely.");
+ break;
+ }
+ }
}
void NuPlayer::Renderer::signalTimeDiscontinuity() {
@@ -781,6 +801,11 @@
TRESPASS();
break;
}
+ if (!mSyncFlag.test_and_set()) {
+ Mutex::Autolock syncLock(mSyncLock);
+ ++mSyncCount;
+ mSyncCondition.broadcast();
+ }
}
void NuPlayer::Renderer::postDrainAudioQueue_l(int64_t delayUs) {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
index a521f62..3d2b033 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
@@ -18,6 +18,8 @@
#define NUPLAYER_RENDERER_H_
+#include <atomic>
+
#include <media/AudioResamplerPublic.h>
#include <media/AVSyncSettings.h>
@@ -220,6 +222,11 @@
sp<AWakeLock> mWakeLock;
+ std::atomic_flag mSyncFlag = ATOMIC_FLAG_INIT;
+ Mutex mSyncLock;
+ Condition mSyncCondition;
+ int64_t mSyncCount{0};
+
status_t getCurrentPositionOnLooper(int64_t *mediaUs);
status_t getCurrentPositionOnLooper(
int64_t *mediaUs, int64_t nowUs, bool allowPastQueuedVideo = false);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp
index ee70306..b5142ed 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp
@@ -154,7 +154,7 @@
}
memcpy(data,
- (const uint8_t *)mem->pointer()
+ (const uint8_t *)mem->unsecurePointer()
+ entry->mOffset,
copy);
diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
index afdcd37..f21d2b3 100644
--- a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
@@ -130,29 +130,32 @@
} else if (n < 0) {
break;
} else {
- if (buffer[0] == 0x00) {
+ if (buffer[0] == 0x00) { // OK to access buffer[0] since n must be > 0 here
// XXX legacy
if (extra == NULL) {
extra = new AMessage;
}
- uint8_t type = buffer[1];
+ uint8_t type = 0;
+ if (n > 1) {
+ type = buffer[1];
- if (type & 2) {
- int64_t mediaTimeUs;
- memcpy(&mediaTimeUs, &buffer[2], sizeof(mediaTimeUs));
+ if ((type & 2) && (n >= 2 + sizeof(int64_t))) {
+ int64_t mediaTimeUs;
+ memcpy(&mediaTimeUs, &buffer[2], sizeof(mediaTimeUs));
- extra->setInt64(kATSParserKeyMediaTimeUs, mediaTimeUs);
+ extra->setInt64(kATSParserKeyMediaTimeUs, mediaTimeUs);
+ }
}
mTSParser->signalDiscontinuity(
((type & 1) == 0)
- ? ATSParser::DISCONTINUITY_TIME
- : ATSParser::DISCONTINUITY_FORMATCHANGE,
+ ? ATSParser::DISCONTINUITY_TIME
+ : ATSParser::DISCONTINUITY_FORMATCHANGE,
extra);
} else {
- status_t err = mTSParser->feedTSPacket(buffer, sizeof(buffer));
+ status_t err = mTSParser->feedTSPacket(buffer, n);
if (err != OK) {
ALOGE("TS Parser returned error %d", err);
diff --git a/media/libnblog/Reader.cpp b/media/libnblog/Reader.cpp
index f556e37..67d028d 100644
--- a/media/libnblog/Reader.cpp
+++ b/media/libnblog/Reader.cpp
@@ -45,7 +45,12 @@
}
Reader::Reader(const sp<IMemory>& iMemory, size_t size, const std::string &name)
- : Reader(iMemory != 0 ? (Shared *) iMemory->pointer() : NULL, size, name)
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ : Reader(iMemory != 0 ? (Shared *) iMemory->unsecurePointer() : NULL, size,
+ name)
{
mIMemory = iMemory;
}
@@ -156,7 +161,8 @@
bool Reader::isIMemory(const sp<IMemory>& iMemory) const
{
- return iMemory != 0 && mIMemory != 0 && iMemory->pointer() == mIMemory->pointer();
+ return iMemory != 0 && mIMemory != 0 &&
+ iMemory->unsecurePointer() == mIMemory->unsecurePointer();
}
// We make a set of the invalid types rather than the valid types when aligning
diff --git a/media/libnblog/Writer.cpp b/media/libnblog/Writer.cpp
index da3bd52..86d3b98 100644
--- a/media/libnblog/Writer.cpp
+++ b/media/libnblog/Writer.cpp
@@ -56,7 +56,11 @@
}
Writer::Writer(const sp<IMemory>& iMemory, size_t size)
- : Writer(iMemory != 0 ? (Shared *) iMemory->pointer() : NULL, size)
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ : Writer(iMemory != 0 ? (Shared *) iMemory->unsecurePointer() : NULL, size)
{
mIMemory = iMemory;
}
diff --git a/media/libstagefright/ACodecBufferChannel.cpp b/media/libstagefright/ACodecBufferChannel.cpp
index 266a240..dd6f7b4 100644
--- a/media/libstagefright/ACodecBufferChannel.cpp
+++ b/media/libstagefright/ACodecBufferChannel.cpp
@@ -153,7 +153,8 @@
}
if (destination.mType == ICrypto::kDestinationTypeSharedMemory) {
- memcpy(it->mCodecBuffer->base(), destination.mSharedMemory->pointer(), result);
+ memcpy(it->mCodecBuffer->base(),
+ destination.mSharedMemory->unsecurePointer(), result);
}
} else {
// Here we cast CryptoPlugin::SubSample to hardware::cas::native::V1_0::SubSample
@@ -219,7 +220,8 @@
if (dstBuffer.type == BufferType::SHARED_MEMORY) {
memcpy(it->mCodecBuffer->base(),
- (uint8_t*)it->mSharedEncryptedBuffer->pointer(), result);
+ (uint8_t*)it->mSharedEncryptedBuffer->unsecurePointer(),
+ result);
}
}
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index 7eab230..50a3c63 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -219,7 +219,7 @@
],
header_libs:[
- "libnativeloader-dummy-headers",
+ "libnativeloader-headers",
"libstagefright_xmlparser_headers",
"media_ndk_headers",
],
diff --git a/media/libstagefright/BufferImpl.cpp b/media/libstagefright/BufferImpl.cpp
index b760273..68440e6 100644
--- a/media/libstagefright/BufferImpl.cpp
+++ b/media/libstagefright/BufferImpl.cpp
@@ -32,7 +32,11 @@
// SharedMemoryBuffer
SharedMemoryBuffer::SharedMemoryBuffer(const sp<AMessage> &format, const sp<IMemory> &mem)
- : MediaCodecBuffer(format, new ABuffer(mem->pointer(), mem->size())),
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ : MediaCodecBuffer(format, new ABuffer(mem->unsecurePointer(), mem->size())),
mMemory(mem) {
}
diff --git a/media/libstagefright/CallbackDataSource.cpp b/media/libstagefright/CallbackDataSource.cpp
index 92e6eb9..2f8e6af 100644
--- a/media/libstagefright/CallbackDataSource.cpp
+++ b/media/libstagefright/CallbackDataSource.cpp
@@ -81,7 +81,8 @@
return ERROR_OUT_OF_RANGE;
}
CHECK(numRead >= 0 && (size_t)numRead <= bufferSize);
- memcpy(((uint8_t*)data) + totalNumRead, mMemory->pointer(), numRead);
+ memcpy(((uint8_t*)data) + totalNumRead, mMemory->unsecurePointer(),
+ numRead);
numLeft -= numRead;
totalNumRead += numRead;
}
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 41f5db0..9b3f420 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -89,7 +89,7 @@
void CameraSourceListener::postData(int32_t msgType, const sp<IMemory> &dataPtr,
camera_frame_metadata_t * /* metadata */) {
ALOGV("postData(%d, ptr:%p, size:%zu)",
- msgType, dataPtr->pointer(), dataPtr->size());
+ msgType, dataPtr->unsecurePointer(), dataPtr->size());
sp<CameraSource> source = mSource.promote();
if (source.get() != NULL) {
@@ -966,8 +966,12 @@
// Check if frame contains a VideoNativeHandleMetadata.
if (frame->size() == sizeof(VideoNativeHandleMetadata)) {
- VideoNativeHandleMetadata *metadata =
- (VideoNativeHandleMetadata*)(frame->pointer());
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ VideoNativeHandleMetadata *metadata =
+ (VideoNativeHandleMetadata*)(frame->unsecurePointer());
if (metadata->eType == kMetadataBufferTypeNativeHandleSource) {
handle = metadata->pHandle;
}
@@ -1047,7 +1051,7 @@
Mutex::Autolock autoLock(mLock);
for (List<sp<IMemory> >::iterator it = mFramesBeingEncoded.begin();
it != mFramesBeingEncoded.end(); ++it) {
- if ((*it)->pointer() == buffer->data()) {
+ if ((*it)->unsecurePointer() == buffer->data()) {
releaseOneRecordingFrame((*it));
mFramesBeingEncoded.erase(it);
++mNumFramesEncoded;
@@ -1102,7 +1106,11 @@
frameTime = *mFrameTimes.begin();
mFrameTimes.erase(mFrameTimes.begin());
mFramesBeingEncoded.push_back(frame);
- *buffer = new MediaBuffer(frame->pointer(), frame->size());
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ *buffer = new MediaBuffer(frame->unsecurePointer(), frame->size());
(*buffer)->setObserver(this);
(*buffer)->add_ref();
(*buffer)->meta_data().setInt64(kKeyTime, frameTime);
@@ -1248,7 +1256,7 @@
mMemoryBases.erase(mMemoryBases.begin());
// Wrap native handle in sp<IMemory> so it can be pushed to mFramesReceived.
- VideoNativeHandleMetadata *metadata = (VideoNativeHandleMetadata*)(data->pointer());
+ VideoNativeHandleMetadata *metadata = (VideoNativeHandleMetadata*)(data->unsecurePointer());
metadata->eType = kMetadataBufferTypeNativeHandleSource;
metadata->pHandle = handle;
@@ -1296,7 +1304,11 @@
mMemoryBases.erase(mMemoryBases.begin());
// Wrap native handle in sp<IMemory> so it can be pushed to mFramesReceived.
- VideoNativeHandleMetadata *metadata = (VideoNativeHandleMetadata*)(data->pointer());
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ VideoNativeHandleMetadata *metadata = (VideoNativeHandleMetadata*)(data->unsecurePointer());
metadata->eType = kMetadataBufferTypeNativeHandleSource;
metadata->pHandle = handle;
diff --git a/media/libstagefright/CameraSourceTimeLapse.cpp b/media/libstagefright/CameraSourceTimeLapse.cpp
index 2a819ad..e0a6eb3 100644
--- a/media/libstagefright/CameraSourceTimeLapse.cpp
+++ b/media/libstagefright/CameraSourceTimeLapse.cpp
@@ -245,11 +245,11 @@
ALOGV("createIMemoryCopy");
size_t source_size = source_data->size();
- void* source_pointer = source_data->pointer();
+ void* source_pointer = source_data->unsecurePointer();
sp<MemoryHeapBase> newMemoryHeap = new MemoryHeapBase(source_size);
sp<MemoryBase> newMemory = new MemoryBase(newMemoryHeap, 0, source_size);
- memcpy(newMemory->pointer(), source_pointer, source_size);
+ memcpy(newMemory->unsecurePointer(), source_pointer, source_size);
return newMemory;
}
diff --git a/media/libstagefright/FrameDecoder.cpp b/media/libstagefright/FrameDecoder.cpp
index f99dd1c..a5a07d6 100644
--- a/media/libstagefright/FrameDecoder.cpp
+++ b/media/libstagefright/FrameDecoder.cpp
@@ -99,7 +99,7 @@
ALOGE("not enough memory for VideoFrame size=%zu", size);
return NULL;
}
- VideoFrame* frameCopy = static_cast<VideoFrame*>(frameMem->pointer());
+ VideoFrame* frameCopy = static_cast<VideoFrame*>(frameMem->unsecurePointer());
frameCopy->init(frame, iccData, iccSize);
return frameMem;
@@ -206,7 +206,7 @@
// try to fill sequence meta's duration based on average frame rate,
// default to 33ms if frame rate is unavailable.
int32_t frameRate;
- VideoFrame* meta = static_cast<VideoFrame*>(metaMem->pointer());
+ VideoFrame* meta = static_cast<VideoFrame*>(metaMem->unsecurePointer());
if (trackMeta->findInt32(kKeyFrameRate, &frameRate) && frameRate > 0) {
meta->mDurationUs = 1000000LL / frameRate;
} else {
@@ -614,7 +614,7 @@
0,
dstBpp(),
mSurfaceControl != nullptr /*allocRotated*/);
- mFrame = static_cast<VideoFrame*>(frameMem->pointer());
+ mFrame = static_cast<VideoFrame*>(frameMem->unsecurePointer());
setFrame(frameMem);
}
@@ -895,7 +895,7 @@
if (mFrame == NULL) {
sp<IMemory> frameMem = allocVideoFrame(
trackMeta(), mWidth, mHeight, mTileWidth, mTileHeight, dstBpp());
- mFrame = static_cast<VideoFrame*>(frameMem->pointer());
+ mFrame = static_cast<VideoFrame*>(frameMem->unsecurePointer());
setFrame(frameMem);
}
diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp
index cf4edae..ce73676 100644
--- a/media/libstagefright/StagefrightMediaScanner.cpp
+++ b/media/libstagefright/StagefrightMediaScanner.cpp
@@ -158,7 +158,11 @@
if (mRetriever->setDataSource(fd, 0, size) == OK) {
sp<IMemory> mem = mRetriever->extractAlbumArt();
if (mem != NULL) {
- MediaAlbumArt *art = static_cast<MediaAlbumArt *>(mem->pointer());
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ MediaAlbumArt *art = static_cast<MediaAlbumArt *>(mem->unsecurePointer());
return art->clone();
}
}
diff --git a/media/libstagefright/bqhelper/GraphicBufferSource.cpp b/media/libstagefright/bqhelper/GraphicBufferSource.cpp
index 59317e7..591b9e9 100644
--- a/media/libstagefright/bqhelper/GraphicBufferSource.cpp
+++ b/media/libstagefright/bqhelper/GraphicBufferSource.cpp
@@ -29,6 +29,7 @@
#include <media/stagefright/foundation/ColorUtils.h>
#include <media/stagefright/foundation/FileDescriptor.h>
+#include <android-base/properties.h>
#include <media/hardware/MetadataBufferType.h>
#include <ui/GraphicBuffer.h>
#include <gui/BufferItem.h>
@@ -800,6 +801,9 @@
}
}
+#ifdef __clang__
+__attribute__((no_sanitize("integer")))
+#endif
bool GraphicBufferSource::calculateCodecTimestamp_l(
nsecs_t bufferTimeNs, int64_t *codecTimeUs) {
int64_t timeUs = bufferTimeNs / 1000;
@@ -816,14 +820,15 @@
mPrevFrameUs = mBaseFrameUs =
std::llround((timeUs * mCaptureFps) / mFps);
mFrameCount = 0;
- } else {
- // snap to nearest capture point
+ } else if (mSnapTimestamps) {
double nFrames = (timeUs - mPrevCaptureUs) * mCaptureFps / 1000000;
if (nFrames < 0.5 - kTimestampFluctuation) {
// skip this frame as it's too close to previous capture
- ALOGD("skipping frame, timeUs %lld", static_cast<long long>(timeUs));
+ ALOGD("skipping frame, timeUs %lld",
+ static_cast<long long>(timeUs));
return false;
}
+ // snap to nearest capture point
if (nFrames <= 1.0) {
nFrames = 1.0;
}
@@ -832,6 +837,22 @@
mFrameCount * 1000000 / mCaptureFps);
mPrevFrameUs = mBaseFrameUs + std::llround(
mFrameCount * 1000000 / mFps);
+ } else {
+ if (timeUs <= mPrevCaptureUs) {
+ if (mFrameDropper != NULL && mFrameDropper->disabled()) {
+ // Warn only, client has disabled frame drop logic possibly for image
+ // encoding cases where camera's ZSL mode could send out of order frames.
+ ALOGW("Received frame that's going backward in time");
+ } else {
+ // Drop the frame if it's going backward in time. Bad timestamp
+ // could disrupt encoder's rate control completely.
+ ALOGW("Dropping frame that's going backward in time");
+ return false;
+ }
+ }
+ mPrevCaptureUs = timeUs;
+ mPrevFrameUs = mBaseFrameUs + std::llround(
+ (timeUs - mBaseCaptureUs) * (mCaptureFps / mFps));
}
ALOGV("timeUs %lld, captureUs %lld, frameUs %lld",
@@ -1359,6 +1380,12 @@
mFps = fps;
mCaptureFps = captureFps;
+ if (captureFps > fps) {
+ mSnapTimestamps = 1 == base::GetIntProperty(
+ "debug.stagefright.snap_timestamps", int64_t(1));
+ } else {
+ mSnapTimestamps = false;
+ }
return OK;
}
diff --git a/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/GraphicBufferSource.h b/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/GraphicBufferSource.h
index b4c4d4a..ed5d7cb 100644
--- a/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/GraphicBufferSource.h
+++ b/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/GraphicBufferSource.h
@@ -461,12 +461,33 @@
// Slow motion mode is enabled if both encoding and capture frame rates are
// defined and the encoding frame rate is less than half the capture frame
// rate. In this mode, the source is expected to produce frames with an even
- // timestamp interval (after rounding) with the configured capture fps. The
- // first source timestamp is used as the source base time. Afterwards, the
- // timestamp of each source frame is snapped to the nearest expected capture
- // timestamp and scaled to match the configured encoding frame rate.
+ // timestamp interval (after rounding) with the configured capture fps.
+ //
+ // These modes must be configured by calling setTimeLapseConfig() before
+ // using this source.
+ //
+ // Timestamp snapping for slow motion recording
+ // ============================================
+ //
+ // When the slow motion mode is configured with setTimeLapseConfig(), the
+ // property "debug.stagefright.snap_timestamps" will be checked. If the
+ // value of the property is set to any value other than 1, mSnapTimestamps
+ // will be set to false. Otherwise, mSnapTimestamps will be set to true.
+ // (mSnapTimestamps will be false for time lapse recording regardless of the
+ // value of the property.)
+ //
+ // If mSnapTimestamps is true, i.e., timestamp snapping is enabled, the
+ // first source timestamp will be used as the source base time; afterwards,
+ // the timestamp of each source frame will be snapped to the nearest
+ // expected capture timestamp and scaled to match the configured encoding
+ // frame rate.
+ //
+ // If timestamp snapping is disabled, the timestamp of source frames will
+ // be scaled to match the ratio between the configured encoding frame rate
+ // and the configured capture frame rate.
- // These modes must be enabled before using this source.
+ // whether timestamps will be snapped
+ bool mSnapTimestamps{true};
// adjusted capture timestamp of the base frame
int64_t mBaseCaptureUs;
diff --git a/media/libstagefright/foundation/AString.cpp b/media/libstagefright/foundation/AString.cpp
index fb51cc5..a8adff5 100644
--- a/media/libstagefright/foundation/AString.cpp
+++ b/media/libstagefright/foundation/AString.cpp
@@ -365,8 +365,6 @@
// static
AString AString::FromParcel(const Parcel &parcel) {
size_t size = static_cast<size_t>(parcel.readInt32());
- // The static analyzer incorrectly reports a false-positive here in c++17.
- // https://bugs.llvm.org/show_bug.cgi?id=38176 . NOLINTNEXTLINE
return AString(static_cast<const char *>(parcel.readInplace(size)), size);
}
diff --git a/media/libstagefright/foundation/MediaBuffer.cpp b/media/libstagefright/foundation/MediaBuffer.cpp
index 9beac05..8e245dc 100644
--- a/media/libstagefright/foundation/MediaBuffer.cpp
+++ b/media/libstagefright/foundation/MediaBuffer.cpp
@@ -72,7 +72,7 @@
}
} else {
getSharedControl()->clear();
- mData = (uint8_t *)mMemory->pointer() + sizeof(SharedControl);
+ mData = (uint8_t *)mMemory->unsecurePointer() + sizeof(SharedControl);
ALOGV("Allocated shared mem buffer of size %zu @ %p", size, mData);
}
}
diff --git a/media/libstagefright/foundation/MediaBufferGroup.cpp b/media/libstagefright/foundation/MediaBufferGroup.cpp
index 84ff9a6..3c25047 100644
--- a/media/libstagefright/foundation/MediaBufferGroup.cpp
+++ b/media/libstagefright/foundation/MediaBufferGroup.cpp
@@ -75,7 +75,7 @@
for (size_t i = 0; i < buffers; ++i) {
sp<IMemory> mem = memoryDealer->allocate(augmented_size);
- if (mem.get() == nullptr || mem->pointer() == nullptr) {
+ if (mem.get() == nullptr || mem->unsecurePointer() == nullptr) {
ALOGW("Only allocated %zu shared buffers of size %zu", i, buffer_size);
break;
}
diff --git a/media/libstagefright/include/media/stagefright/MediaBuffer.h b/media/libstagefright/include/media/stagefright/MediaBuffer.h
index ace63ae..9145b63 100644
--- a/media/libstagefright/include/media/stagefright/MediaBuffer.h
+++ b/media/libstagefright/include/media/stagefright/MediaBuffer.h
@@ -48,7 +48,11 @@
explicit MediaBuffer(const sp<ABuffer> &buffer);
#ifndef NO_IMEMORY
MediaBuffer(const sp<IMemory> &mem) :
- MediaBuffer((uint8_t *)mem->pointer() + sizeof(SharedControl), mem->size()) {
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ MediaBuffer((uint8_t *)mem->unsecurePointer() + sizeof(SharedControl), mem->size()) {
// delegate and override mMemory
mMemory = mem;
}
@@ -94,9 +98,13 @@
virtual int remoteRefcount() const {
#ifndef NO_IMEMORY
- if (mMemory.get() == nullptr || mMemory->pointer() == nullptr) return 0;
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ if (mMemory.get() == nullptr || mMemory->unsecurePointer() == nullptr) return 0;
int32_t remoteRefcount =
- reinterpret_cast<SharedControl *>(mMemory->pointer())->getRemoteRefcount();
+ reinterpret_cast<SharedControl *>(mMemory->unsecurePointer())->getRemoteRefcount();
// Sanity check so that remoteRefCount() is non-negative.
return remoteRefcount >= 0 ? remoteRefcount : 0; // do not allow corrupted data.
#else
@@ -107,8 +115,12 @@
// returns old value
int addRemoteRefcount(int32_t value) {
#ifndef NO_IMEMORY
- if (mMemory.get() == nullptr || mMemory->pointer() == nullptr) return 0;
- return reinterpret_cast<SharedControl *>(mMemory->pointer())->addRemoteRefcount(value);
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ if (mMemory.get() == nullptr || mMemory->unsecurePointer() == nullptr) return 0;
+ return reinterpret_cast<SharedControl *>(mMemory->unsecurePointer())->addRemoteRefcount(value);
#else
(void) value;
return 0;
@@ -121,8 +133,12 @@
static bool isDeadObject(const sp<IMemory> &memory) {
#ifndef NO_IMEMORY
- if (memory.get() == nullptr || memory->pointer() == nullptr) return false;
- return reinterpret_cast<SharedControl *>(memory->pointer())->isDeadObject();
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ if (memory.get() == nullptr || memory->unsecurePointer() == nullptr) return false;
+ return reinterpret_cast<SharedControl *>(memory->unsecurePointer())->isDeadObject();
#else
(void) memory;
return false;
@@ -220,7 +236,11 @@
inline SharedControl *getSharedControl() const {
#ifndef NO_IMEMORY
- return reinterpret_cast<SharedControl *>(mMemory->pointer());
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ return reinterpret_cast<SharedControl *>(mMemory->unsecurePointer());
#else
return nullptr;
#endif
diff --git a/media/libstagefright/include/media/stagefright/RemoteDataSource.h b/media/libstagefright/include/media/stagefright/RemoteDataSource.h
index e191e6a..b634505 100644
--- a/media/libstagefright/include/media/stagefright/RemoteDataSource.h
+++ b/media/libstagefright/include/media/stagefright/RemoteDataSource.h
@@ -48,7 +48,7 @@
if (size > kBufferSize) {
size = kBufferSize;
}
- return mSource->readAt(offset, mMemory->pointer(), size);
+ return mSource->readAt(offset, mMemory->unsecurePointer(), size);
}
virtual status_t getSize(off64_t *size) {
return mSource->getSize(size);
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index ddb4ba0..e1c3916 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -128,7 +128,7 @@
}
OMX_U8 *getPointer() {
- return mMem.get() ? static_cast<OMX_U8*>(mMem->pointer()) :
+ return mMem.get() ? static_cast<OMX_U8*>(mMem->unsecurePointer()) :
mHidlMemory.get() ? static_cast<OMX_U8*>(
static_cast<void*>(mHidlMemory->getPointer())) : nullptr;
}
@@ -1173,7 +1173,11 @@
return BAD_VALUE;
}
if (params != NULL) {
- paramsPointer = params->pointer();
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ paramsPointer = params->unsecurePointer();
paramsSize = params->size();
} else if (hParams != NULL) {
paramsPointer = hParams->getPointer();
diff --git a/media/ndk/NdkMediaCodec.cpp b/media/ndk/NdkMediaCodec.cpp
index c23f19b..e041533 100644
--- a/media/ndk/NdkMediaCodec.cpp
+++ b/media/ndk/NdkMediaCodec.cpp
@@ -221,7 +221,13 @@
break;
}
- AMediaFormat *aMediaFormat = AMediaFormat_fromMsg(&format);
+ // Here format is MediaCodec's internal copy of output format.
+ // Make a copy since the client might modify it.
+ sp<AMessage> copy;
+ if (format != nullptr) {
+ copy = format->dup();
+ }
+ AMediaFormat *aMediaFormat = AMediaFormat_fromMsg(©);
Mutex::Autolock _l(mCodec->mAsyncCallbackLock);
if (mCodec->mAsyncCallbackUserData != NULL
diff --git a/media/ndk/NdkMediaDrm.cpp b/media/ndk/NdkMediaDrm.cpp
index 85dbffe..cd5a23a 100644
--- a/media/ndk/NdkMediaDrm.cpp
+++ b/media/ndk/NdkMediaDrm.cpp
@@ -89,7 +89,7 @@
};
void DrmListener::notify(DrmPlugin::EventType eventType, int extra, const Parcel *obj) {
- if (!mEventListener && !mExpirationUpdateListener && !mKeysChangeListener) {
+ if (!mEventListener || !mExpirationUpdateListener || !mKeysChangeListener) {
ALOGE("No listeners are specified");
return;
}
diff --git a/media/tests/benchmark/.clang-format b/media/tests/benchmark/.clang-format
new file mode 100644
index 0000000..bf1e355
--- /dev/null
+++ b/media/tests/benchmark/.clang-format
@@ -0,0 +1,13 @@
+BasedOnStyle: Google
+Standard: Cpp11
+AccessModifierOffset: -2
+AllowShortFunctionsOnASingleLine: Inline
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+DerivePointerAlignment: false
+IncludeBlocks: Preserve
+IndentWidth: 4
+ContinuationIndentWidth: 8
+PointerAlignment: Right
+TabWidth: 4
+UseTab: Never
diff --git a/media/tests/benchmark/Android.bp b/media/tests/benchmark/Android.bp
new file mode 100644
index 0000000..8a7a59f
--- /dev/null
+++ b/media/tests/benchmark/Android.bp
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+subdirs = [
+ "src",
+ "tests",
+]
\ No newline at end of file
diff --git a/media/tests/benchmark/README.md b/media/tests/benchmark/README.md
new file mode 100644
index 0000000..0acfab8
--- /dev/null
+++ b/media/tests/benchmark/README.md
@@ -0,0 +1,50 @@
+# Benchmark tests
+
+Run the following steps to build the test suite:
+```
+mmm frameworks/av/media/tests/benchmark/
+```
+
+The binaries will be created in the following path : ${OUT}/data/nativetest64/
+
+adb push $(OUT)/data/nativetest64/* /data/local/tmp/
+
+Eg. adb push $(OUT)/data/nativetest64/extractorTest/extractorTest /data/local/tmp/
+
+To run the binary, follow the commands mentioned below under each module.
+
+The resource files for the tests are taken from [here](https://drive.google.com/open?id=1ghMr17BBJ7n0pqbm7oREiTN_MNemJUqy)
+
+## Extractor
+
+The test extracts elementary stream and benchmarks the extractors available in NDK.
+
+Push the resource files to /sdcard/res on the device.
+
+You can use a different location, but you have to modify the rest of the instructions to replace /sdcard/res with wherever you chose to put the files.
+
+The path to these files on the device is required to be given for the test.
+
+```
+adb shell /data/local/tmp/extractorTest -P /sdcard/res/
+```
+
+## Decoder
+
+The test decodes input stream and benchmarks the decoders available in NDK.
+
+Setup steps are same as extractor.
+
+```
+adb shell /data/local/tmp/decoderTest -P /sdcard/res/
+```
+
+## Muxer
+
+The test muxes elementary stream and benchmarks the muxers available in NDK.
+
+Setup steps are same as extractor.
+
+```
+adb shell /data/local/tmp/muxerTest -P /sdcard/res/
+```
diff --git a/media/tests/benchmark/src/native/common/Android.bp b/media/tests/benchmark/src/native/common/Android.bp
index e4f9ab6..64ea1ee 100644
--- a/media/tests/benchmark/src/native/common/Android.bp
+++ b/media/tests/benchmark/src/native/common/Android.bp
@@ -22,6 +22,7 @@
],
srcs: [
+ "BenchmarkCommon.cpp",
"Timer.cpp",
],
diff --git a/media/tests/benchmark/src/native/common/BenchmarkCommon.cpp b/media/tests/benchmark/src/native/common/BenchmarkCommon.cpp
new file mode 100644
index 0000000..bbc8790
--- /dev/null
+++ b/media/tests/benchmark/src/native/common/BenchmarkCommon.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2019 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 "BenchmarkCommon"
+
+#include "BenchmarkCommon.h"
+#include <iostream>
+
+void CallBackHandle::ioThread() {
+ ALOGV("In %s mIsDone : %d, mSawError : %d ", __func__, mIsDone, mSawError);
+ while (!mIsDone && !mSawError) {
+ auto task = mIOQueue.pop();
+ task();
+ }
+}
+
+void OnInputAvailableCB(AMediaCodec *codec, void *userdata, int32_t index) {
+ ALOGV("OnInputAvailableCB: index(%d)", index);
+ CallBackHandle *self = (CallBackHandle *)userdata;
+ self->getTimer()->addInputTime();
+ self->mIOQueue.push([self, codec, index]() { self->onInputAvailable(codec, index); });
+}
+
+void OnOutputAvailableCB(AMediaCodec *codec, void *userdata, int32_t index,
+ AMediaCodecBufferInfo *bufferInfo) {
+ ALOGV("OnOutputAvailableCB: index(%d), (%d, %d, %lld, 0x%x)", index, bufferInfo->offset,
+ bufferInfo->size, (long long)bufferInfo->presentationTimeUs, bufferInfo->flags);
+ CallBackHandle *self = (CallBackHandle *)userdata;
+ self->getTimer()->addOutputTime();
+ AMediaCodecBufferInfo bufferInfoCopy = *bufferInfo;
+ self->mIOQueue.push([self, codec, index, bufferInfoCopy]() {
+ AMediaCodecBufferInfo bc = bufferInfoCopy;
+ self->onOutputAvailable(codec, index, &bc);
+ });
+}
+
+void OnFormatChangedCB(AMediaCodec *codec, void *userdata, AMediaFormat *format) {
+ ALOGV("OnFormatChangedCB: format(%s)", AMediaFormat_toString(format));
+ CallBackHandle *self = (CallBackHandle *)userdata;
+ self->mIOQueue.push([self, codec, format]() { self->onFormatChanged(codec, format); });
+}
+
+void OnErrorCB(AMediaCodec *codec, void *userdata, media_status_t err, int32_t actionCode,
+ const char *detail) {
+ (void)codec;
+ ALOGV("OnErrorCB: err(%d), actionCode(%d), detail(%s)", err, actionCode, detail);
+ CallBackHandle *self = (CallBackHandle *)userdata;
+ self->mSawError = true;
+}
+
+AMediaCodec *createMediaCodec(AMediaFormat *format, const char *mime, string codecName,
+ bool isEncoder) {
+ ALOGV("In %s", __func__);
+ if (!mime) {
+ ALOGE("Please specify a mime type to create codec");
+ return nullptr;
+ }
+
+ AMediaCodec *codec;
+ if (!codecName.empty()) {
+ codec = AMediaCodec_createCodecByName(codecName.c_str());
+ if (!codec) {
+ ALOGE("Unable to create codec by name: %s", codecName.c_str());
+ return nullptr;
+ }
+ } else {
+ if (isEncoder) {
+ codec = AMediaCodec_createEncoderByType(mime);
+ } else {
+ codec = AMediaCodec_createDecoderByType(mime);
+ }
+ if (!codec) {
+ ALOGE("Unable to create codec by mime: %s", mime);
+ return nullptr;
+ }
+ }
+
+ /* Configure codec with the given format*/
+ const char *s = AMediaFormat_toString(format);
+ ALOGV("Input format: %s\n", s);
+
+ media_status_t status = AMediaCodec_configure(codec, format, nullptr, nullptr, isEncoder);
+ if (status != AMEDIA_OK) {
+ ALOGE("AMediaCodec_configure failed %d", status);
+ return nullptr;
+ }
+ return codec;
+}
\ No newline at end of file
diff --git a/media/tests/benchmark/src/native/common/BenchmarkCommon.h b/media/tests/benchmark/src/native/common/BenchmarkCommon.h
index 3ef47c2..7b87c04 100644
--- a/media/tests/benchmark/src/native/common/BenchmarkCommon.h
+++ b/media/tests/benchmark/src/native/common/BenchmarkCommon.h
@@ -19,10 +19,107 @@
#include <utils/Log.h>
+#include <inttypes.h>
+#include <mutex>
+#include <queue>
+#include <thread>
+
#include <media/NdkMediaCodec.h>
+#include <media/NdkMediaError.h>
+
+#include "Timer.h"
+
+using namespace std;
constexpr uint32_t kQueueDequeueTimeoutUs = 1000;
constexpr uint32_t kMaxCSDStrlen = 16;
constexpr uint32_t kMaxBufferSize = 1024 * 1024 * 16;
+template <typename T>
+class CallBackQueue {
+ public:
+ CallBackQueue() {}
+ ~CallBackQueue() {}
+
+ void push(T elem) {
+ bool needsNotify = false;
+ {
+ lock_guard<mutex> lock(mMutex);
+ needsNotify = mQueue.empty();
+ mQueue.push(move(elem));
+ }
+ if (needsNotify) mQueueNotEmptyCondition.notify_one();
+ }
+
+ T pop() {
+ unique_lock<mutex> lock(mMutex);
+ if (mQueue.empty()) {
+ mQueueNotEmptyCondition.wait(lock, [this]() { return !mQueue.empty(); });
+ }
+ auto result = mQueue.front();
+ mQueue.pop();
+ return result;
+ }
+
+ private:
+ mutex mMutex;
+ queue<T> mQueue;
+ condition_variable mQueueNotEmptyCondition;
+};
+
+class CallBackHandle {
+ public:
+ CallBackHandle() : mSawError(false), mIsDone(false), mTimer(nullptr) {}
+
+ virtual ~CallBackHandle() {
+ if (mIOThread.joinable()) mIOThread.join();
+ if (mTimer) delete mTimer;
+ }
+
+ void ioThread();
+
+ // Implementation in child class (Decoder/Encoder)
+ virtual void onInputAvailable(AMediaCodec *codec, int32_t index) {
+ (void)codec;
+ (void)index;
+ }
+ virtual void onFormatChanged(AMediaCodec *codec, AMediaFormat *format) {
+ (void)codec;
+ (void)format;
+ }
+ virtual void onOutputAvailable(AMediaCodec *codec, int32_t index,
+ AMediaCodecBufferInfo *bufferInfo) {
+ (void)codec;
+ (void)index;
+ (void)bufferInfo;
+ }
+
+ virtual Timer *getTimer() { return mTimer; }
+
+ // Keep a queue of all function callbacks.
+ typedef function<void()> IOTask;
+ CallBackQueue<IOTask> mIOQueue;
+ thread mIOThread;
+ bool mSawError;
+ bool mIsDone;
+
+ private:
+ Timer *mTimer;
+};
+
+// Async API's callback
+void OnInputAvailableCB(AMediaCodec *codec, void *userdata, int32_t index);
+
+void OnOutputAvailableCB(AMediaCodec *codec, void *userdata, int32_t index,
+ AMediaCodecBufferInfo *bufferInfo);
+
+void OnFormatChangedCB(AMediaCodec *codec, void *userdata, AMediaFormat *format);
+
+void OnErrorCB(AMediaCodec *codec, void * /* userdata */, media_status_t err, int32_t actionCode,
+ const char *detail);
+
+// Utility to create and configure AMediaCodec
+AMediaCodec *createMediaCodec(AMediaFormat *format, const char *mime, string codecName,
+ bool isEncoder);
+
#endif // __BENCHMARK_COMMON_H__
diff --git a/media/tests/benchmark/src/native/decoder/Android.bp b/media/tests/benchmark/src/native/decoder/Android.bp
new file mode 100644
index 0000000..f2d3db5
--- /dev/null
+++ b/media/tests/benchmark/src/native/decoder/Android.bp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+cc_library_static {
+ name: "libbenchmark_decoder",
+ defaults: [
+ "libbenchmark_common-defaults",
+ "libbenchmark_soft_sanitize_all-defaults",
+ ],
+
+ srcs: ["Decoder.cpp"],
+
+ static_libs: ["libbenchmark_extractor"],
+
+ export_include_dirs: ["."],
+
+ ldflags: ["-Wl,-Bsymbolic"]
+}
diff --git a/media/tests/benchmark/src/native/decoder/Decoder.cpp b/media/tests/benchmark/src/native/decoder/Decoder.cpp
new file mode 100644
index 0000000..21fc420
--- /dev/null
+++ b/media/tests/benchmark/src/native/decoder/Decoder.cpp
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2019 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 "decoder"
+
+#include <iostream>
+
+#include "Decoder.h"
+
+tuple<ssize_t, uint32_t, int64_t> readSampleData(uint8_t *inputBuffer, int32_t &offset,
+ vector<AMediaCodecBufferInfo> &frameInfo,
+ uint8_t *buf, int32_t frameID, size_t bufSize) {
+ ALOGV("In %s", __func__);
+ if (frameID == (int32_t)frameInfo.size()) {
+ return make_tuple(0, AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM, 0);
+ }
+ uint32_t flags = frameInfo[frameID].flags;
+ int64_t timestamp = frameInfo[frameID].presentationTimeUs;
+ ssize_t bytesCount = frameInfo[frameID].size;
+ if (bufSize < bytesCount) {
+ ALOGE("Error : insufficient resource");
+ return make_tuple(0, AMEDIACODEC_ERROR_INSUFFICIENT_RESOURCE, 0);
+ }
+
+ memcpy(buf, inputBuffer + offset, bytesCount);
+ offset += bytesCount;
+ return make_tuple(bytesCount, flags, timestamp);
+}
+
+void Decoder::onInputAvailable(AMediaCodec *mediaCodec, int32_t bufIdx) {
+ ALOGV("In %s", __func__);
+ if (mediaCodec == mCodec && mediaCodec) {
+ if (mSawInputEOS || bufIdx < 0) return;
+ if (mSignalledError) {
+ CallBackHandle::mSawError = true;
+ mDecoderDoneCondition.notify_one();
+ return;
+ }
+
+ size_t bufSize;
+ uint8_t *buf = AMediaCodec_getInputBuffer(mCodec, bufIdx, &bufSize);
+ if (!buf) {
+ mSignalledError = true;
+ mDecoderDoneCondition.notify_one();
+ return;
+ }
+
+ ssize_t bytesRead = 0;
+ uint32_t flag = 0;
+ int64_t presentationTimeUs = 0;
+ tie(bytesRead, flag, presentationTimeUs) = readSampleData(
+ mInputBuffer, mOffset, mFrameMetaData, buf, mNumInputFrame, bufSize);
+ if (flag == AMEDIACODEC_ERROR_INSUFFICIENT_RESOURCE) {
+ mSignalledError = true;
+ mDecoderDoneCondition.notify_one();
+ return;
+ }
+
+ if (flag == AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) mSawInputEOS = true;
+ ALOGV("%s bytesRead : %zd presentationTimeUs : %" PRId64 " mSawInputEOS : %s", __FUNCTION__,
+ bytesRead, presentationTimeUs, mSawInputEOS ? "TRUE" : "FALSE");
+
+ int status = AMediaCodec_queueInputBuffer(mCodec, bufIdx, 0 /* offset */, bytesRead,
+ presentationTimeUs, flag);
+ if (AMEDIA_OK != status) {
+ mSignalledError = true;
+ mDecoderDoneCondition.notify_one();
+ return;
+ }
+ mNumInputFrame++;
+ }
+}
+
+void Decoder::onOutputAvailable(AMediaCodec *mediaCodec, int32_t bufIdx,
+ AMediaCodecBufferInfo *bufferInfo) {
+ ALOGV("In %s", __func__);
+ if (mediaCodec == mCodec && mediaCodec) {
+ if (mSawOutputEOS || bufIdx < 0) return;
+ if (mSignalledError) {
+ CallBackHandle::mSawError = true;
+ mDecoderDoneCondition.notify_one();
+ return;
+ }
+
+ AMediaCodec_releaseOutputBuffer(mCodec, bufIdx, false);
+ mSawOutputEOS = (0 != (bufferInfo->flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM));
+ mNumOutputFrame++;
+ ALOGV("%s index : %d mSawOutputEOS : %s count : %u", __FUNCTION__, bufIdx,
+ mSawOutputEOS ? "TRUE" : "FALSE", mNumOutputFrame);
+
+ if (mSawOutputEOS) {
+ CallBackHandle::mIsDone = true;
+ mDecoderDoneCondition.notify_one();
+ }
+ }
+}
+
+void Decoder::onFormatChanged(AMediaCodec *mediaCodec, AMediaFormat *format) {
+ ALOGV("In %s", __func__);
+ if (mediaCodec == mCodec && mediaCodec) {
+ ALOGV("%s { %s }", __FUNCTION__, AMediaFormat_toString(format));
+ mFormat = format;
+ }
+}
+
+void Decoder::setupDecoder() {
+ if (!mFormat) mFormat = mExtractor->getFormat();
+ if (!mTimer) mTimer = new Timer();
+}
+
+int32_t Decoder::decode(uint8_t *inputBuffer, vector<AMediaCodecBufferInfo> &frameInfo,
+ string &codecName, bool asyncMode) {
+ ALOGV("In %s", __func__);
+ mInputBuffer = inputBuffer;
+ mFrameMetaData = frameInfo;
+ mOffset = 0;
+
+ const char *mime = nullptr;
+ AMediaFormat_getString(mFormat, AMEDIAFORMAT_KEY_MIME, &mime);
+ if (!mime) return AMEDIA_ERROR_INVALID_OBJECT;
+
+ int64_t sTime = mTimer->getCurTime();
+ mCodec = createMediaCodec(mFormat, mime, codecName, false /*isEncoder*/);
+ if (!mCodec) return AMEDIA_ERROR_INVALID_OBJECT;
+
+ if (asyncMode) {
+ AMediaCodecOnAsyncNotifyCallback aCB = {OnInputAvailableCB, OnOutputAvailableCB,
+ OnFormatChangedCB, OnErrorCB};
+ AMediaCodec_setAsyncNotifyCallback(mCodec, aCB, this);
+
+ CallBackHandle *callbackHandle = new CallBackHandle();
+ callbackHandle->mIOThread = thread(&CallBackHandle::ioThread, this);
+ }
+
+ AMediaCodec_start(mCodec);
+ int64_t eTime = mTimer->getCurTime();
+ int64_t timeTaken = mTimer->getTimeDiff(sTime, eTime);
+ mTimer->setInitTime(timeTaken);
+
+ mTimer->setStartTime();
+ if (!asyncMode) {
+ while (!mSawOutputEOS && !mSignalledError) {
+ /* Queue input data */
+ if (!mSawInputEOS) {
+ ssize_t inIdx = AMediaCodec_dequeueInputBuffer(mCodec, kQueueDequeueTimeoutUs);
+ if (inIdx < 0 && inIdx != AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
+ ALOGE("AMediaCodec_dequeueInputBuffer returned invalid index %zd\n", inIdx);
+ return AMEDIA_ERROR_IO;
+ } else if (inIdx >= 0) {
+ mTimer->addInputTime();
+ onInputAvailable(mCodec, inIdx);
+ }
+ }
+
+ /* Dequeue output data */
+ AMediaCodecBufferInfo info;
+ ssize_t outIdx = AMediaCodec_dequeueOutputBuffer(mCodec, &info, kQueueDequeueTimeoutUs);
+ if (outIdx == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
+ mFormat = AMediaCodec_getOutputFormat(mCodec);
+ const char *s = AMediaFormat_toString(mFormat);
+ ALOGI("Output format: %s\n", s);
+ } else if (outIdx >= 0) {
+ mTimer->addOutputTime();
+ onOutputAvailable(mCodec, outIdx, &info);
+ } else if (!(outIdx == AMEDIACODEC_INFO_TRY_AGAIN_LATER ||
+ outIdx == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED)) {
+ ALOGE("AMediaCodec_dequeueOutputBuffer returned invalid index %zd\n", outIdx);
+ return AMEDIA_ERROR_IO;
+ }
+ }
+ } else {
+ unique_lock<mutex> lock(mMutex);
+ mDecoderDoneCondition.wait(lock, [this]() { return (mSawOutputEOS || mSignalledError); });
+ }
+
+ if (codecName.empty()) {
+ char *decName;
+ AMediaCodec_getName(mCodec, &decName);
+ codecName.assign(decName);
+ AMediaCodec_releaseName(mCodec, decName);
+ }
+ return AMEDIA_OK;
+}
+
+void Decoder::deInitCodec() {
+ int64_t sTime = mTimer->getCurTime();
+ if (mFormat) {
+ AMediaFormat_delete(mFormat);
+ mFormat = nullptr;
+ }
+ if (!mCodec) return;
+ AMediaCodec_stop(mCodec);
+ AMediaCodec_delete(mCodec);
+ int64_t eTime = mTimer->getCurTime();
+ int64_t timeTaken = mTimer->getTimeDiff(sTime, eTime);
+ mTimer->setDeInitTime(timeTaken);
+}
+
+void Decoder::dumpStatistics(string inputReference) {
+ int64_t durationUs = mExtractor->getClipDuration();
+ string operation = "decode";
+ mTimer->dumpStatistics(operation, inputReference, durationUs);
+}
+
+void Decoder::resetDecoder() {
+ if (mTimer) mTimer->resetTimers();
+ if (mInputBuffer) mInputBuffer = nullptr;
+ if (!mFrameMetaData.empty()) mFrameMetaData.clear();
+}
diff --git a/media/tests/benchmark/src/native/decoder/Decoder.h b/media/tests/benchmark/src/native/decoder/Decoder.h
new file mode 100644
index 0000000..0a64128
--- /dev/null
+++ b/media/tests/benchmark/src/native/decoder/Decoder.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2019 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 __DECODER_H__
+#define __DECODER_H__
+
+#include <chrono>
+#include <condition_variable>
+#include <mutex>
+#include <queue>
+#include <thread>
+
+#include "BenchmarkCommon.h"
+#include "Extractor.h"
+#include "Timer.h"
+
+class Decoder : public CallBackHandle {
+ public:
+ Decoder()
+ : mCodec(nullptr),
+ mFormat(nullptr),
+ mExtractor(nullptr),
+ mTimer(nullptr),
+ mNumInputFrame(0),
+ mNumOutputFrame(0),
+ mSawInputEOS(false),
+ mSawOutputEOS(false),
+ mSignalledError(false),
+ mInputBuffer(nullptr) {
+ mExtractor = new Extractor();
+ }
+
+ virtual ~Decoder() {
+ if (mTimer) delete mTimer;
+ if (mExtractor) delete mExtractor;
+ }
+
+ Timer *getTimer() override { return mTimer; }
+
+ Extractor *getExtractor() { return mExtractor; }
+
+ // Decoder related utilities
+ void setupDecoder();
+
+ void deInitCodec();
+
+ void resetDecoder();
+
+ // Async callback APIs
+ void onInputAvailable(AMediaCodec *codec, int32_t index) override;
+
+ void onFormatChanged(AMediaCodec *codec, AMediaFormat *format) override;
+
+ void onOutputAvailable(AMediaCodec *codec, int32_t index,
+ AMediaCodecBufferInfo *bufferInfo) override;
+
+ // Process the frames and give decoded output
+ int32_t decode(uint8_t *inputBuffer, vector<AMediaCodecBufferInfo> &frameInfo,
+ string &codecName, bool asyncMode);
+
+ void dumpStatistics(string inputReference);
+
+ private:
+ AMediaCodec *mCodec;
+ AMediaFormat *mFormat;
+
+ Extractor *mExtractor;
+
+ Timer *mTimer;
+
+ int32_t mNumInputFrame;
+ int32_t mNumOutputFrame;
+
+ bool mSawInputEOS;
+ bool mSawOutputEOS;
+ bool mSignalledError;
+
+ int32_t mOffset;
+ uint8_t *mInputBuffer;
+ vector<AMediaCodecBufferInfo> mFrameMetaData;
+
+ /* Asynchronous locks */
+ mutex mMutex;
+ condition_variable mDecoderDoneCondition;
+};
+
+// Read input samples
+tuple<ssize_t, uint32_t, int64_t> readSampleData(uint8_t *inputBuffer, int32_t &offset,
+ vector<AMediaCodecBufferInfo> &frameSizes,
+ uint8_t *buf, int32_t frameID, size_t bufSize);
+
+#endif // __DECODER_H__
diff --git a/media/tests/benchmark/src/native/extractor/Android.bp b/media/tests/benchmark/src/native/extractor/Android.bp
new file mode 100644
index 0000000..2fbe4e8
--- /dev/null
+++ b/media/tests/benchmark/src/native/extractor/Android.bp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+cc_library_static {
+ name: "libbenchmark_extractor",
+ defaults: [
+ "libbenchmark_common-defaults",
+ "libbenchmark_soft_sanitize_all-defaults",
+ ],
+
+ srcs: ["Extractor.cpp"],
+
+ export_include_dirs: ["."],
+
+ ldflags: ["-Wl,-Bsymbolic"]
+}
diff --git a/media/tests/benchmark/src/native/extractor/Extractor.cpp b/media/tests/benchmark/src/native/extractor/Extractor.cpp
new file mode 100644
index 0000000..0726ae3
--- /dev/null
+++ b/media/tests/benchmark/src/native/extractor/Extractor.cpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2019 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 "extractor"
+
+#include <iostream>
+
+#include "Extractor.h"
+
+int32_t Extractor::initExtractor(int32_t fd, size_t fileSize) {
+ mTimer = new Timer();
+
+ mFrameBuf = (uint8_t *)calloc(kMaxBufferSize, sizeof(uint8_t));
+ if (!mFrameBuf) return -1;
+
+ int64_t sTime = mTimer->getCurTime();
+
+ mExtractor = AMediaExtractor_new();
+ if (!mExtractor) return AMEDIACODEC_ERROR_INSUFFICIENT_RESOURCE;
+ media_status_t status = AMediaExtractor_setDataSourceFd(mExtractor, fd, 0, fileSize);
+ if (status != AMEDIA_OK) return status;
+
+ int64_t eTime = mTimer->getCurTime();
+ int64_t timeTaken = mTimer->getTimeDiff(sTime, eTime);
+ mTimer->setInitTime(timeTaken);
+
+ return AMediaExtractor_getTrackCount(mExtractor);
+}
+
+void *Extractor::getCSDSample(AMediaCodecBufferInfo &frameInfo, int32_t csdIndex) {
+ char csdName[kMaxCSDStrlen];
+ void *csdBuffer = nullptr;
+ frameInfo.presentationTimeUs = 0;
+ frameInfo.flags = AMEDIACODEC_BUFFER_FLAG_CODEC_CONFIG;
+ snprintf(csdName, sizeof(csdName), "csd-%d", csdIndex);
+
+ size_t size;
+ bool csdFound = AMediaFormat_getBuffer(mFormat, csdName, &csdBuffer, &size);
+ if (!csdFound) return nullptr;
+ frameInfo.size = (int32_t)size;
+
+ return csdBuffer;
+}
+
+int32_t Extractor::getFrameSample(AMediaCodecBufferInfo &frameInfo) {
+ int32_t size = AMediaExtractor_readSampleData(mExtractor, mFrameBuf, kMaxBufferSize);
+ if (size < 0) return -1;
+
+ frameInfo.flags = AMediaExtractor_getSampleFlags(mExtractor);
+ frameInfo.size = size;
+ frameInfo.presentationTimeUs = AMediaExtractor_getSampleTime(mExtractor);
+ AMediaExtractor_advance(mExtractor);
+
+ return 0;
+}
+
+int32_t Extractor::setupTrackFormat(int32_t trackId) {
+ AMediaExtractor_selectTrack(mExtractor, trackId);
+ mFormat = AMediaExtractor_getTrackFormat(mExtractor, trackId);
+ if (!mFormat) return AMEDIA_ERROR_INVALID_OBJECT;
+
+ bool durationFound = AMediaFormat_getInt64(mFormat, AMEDIAFORMAT_KEY_DURATION, &mDurationUs);
+ if (!durationFound) return AMEDIA_ERROR_INVALID_OBJECT;
+
+ return AMEDIA_OK;
+}
+
+int32_t Extractor::extract(int32_t trackId) {
+ int32_t status = setupTrackFormat(trackId);
+ if (status != AMEDIA_OK) return status;
+
+ int32_t idx = 0;
+ AMediaCodecBufferInfo frameInfo;
+ while (1) {
+ memset(&frameInfo, 0, sizeof(AMediaCodecBufferInfo));
+ void *csdBuffer = getCSDSample(frameInfo, idx);
+ if (!csdBuffer || !frameInfo.size) break;
+ idx++;
+ }
+
+ mTimer->setStartTime();
+ while (1) {
+ int32_t status = getFrameSample(frameInfo);
+ if (status || !frameInfo.size) break;
+ mTimer->addOutputTime();
+ }
+
+ if (mFormat) {
+ AMediaFormat_delete(mFormat);
+ mFormat = nullptr;
+ }
+
+ AMediaExtractor_unselectTrack(mExtractor, trackId);
+
+ return AMEDIA_OK;
+}
+
+void Extractor::dumpStatistics(string inputReference) {
+ string operation = "extract";
+ mTimer->dumpStatistics(operation, inputReference, mDurationUs);
+}
+
+void Extractor::deInitExtractor() {
+ if (mFrameBuf) {
+ free(mFrameBuf);
+ mFrameBuf = nullptr;
+ }
+
+ int64_t sTime = mTimer->getCurTime();
+ if (mExtractor) {
+ // TODO: (b/140128505) Multiple calls result in DoS.
+ // Uncomment call to AMediaExtractor_delete() once this is resolved
+ // AMediaExtractor_delete(mExtractor);
+ mExtractor = nullptr;
+ }
+ int64_t eTime = mTimer->getCurTime();
+ int64_t deInitTime = mTimer->getTimeDiff(sTime, eTime);
+ mTimer->setDeInitTime(deInitTime);
+}
diff --git a/media/tests/benchmark/src/native/extractor/Extractor.h b/media/tests/benchmark/src/native/extractor/Extractor.h
new file mode 100644
index 0000000..361bcd7
--- /dev/null
+++ b/media/tests/benchmark/src/native/extractor/Extractor.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __EXTRACTOR_H__
+#define __EXTRACTOR_H__
+
+#include <media/NdkMediaExtractor.h>
+
+#include "BenchmarkCommon.h"
+#include "Timer.h"
+
+class Extractor {
+ public:
+ Extractor()
+ : mFormat(nullptr),
+ mExtractor(nullptr),
+ mTimer(nullptr),
+ mFrameBuf{nullptr},
+ mDurationUs{0} {}
+
+ ~Extractor() {
+ if (mTimer) delete mTimer;
+ }
+
+ int32_t initExtractor(int32_t fd, size_t fileSize);
+
+ int32_t setupTrackFormat(int32_t trackId);
+
+ void *getCSDSample(AMediaCodecBufferInfo &frameInfo, int32_t csdIndex);
+
+ int32_t getFrameSample(AMediaCodecBufferInfo &frameInfo);
+
+ int32_t extract(int32_t trackId);
+
+ void dumpStatistics(std::string inputReference);
+
+ void deInitExtractor();
+
+ AMediaFormat *getFormat() { return mFormat; }
+
+ uint8_t *getFrameBuf() { return mFrameBuf; }
+
+ int64_t getClipDuration() { return mDurationUs; }
+
+ private:
+ AMediaFormat *mFormat;
+ AMediaExtractor *mExtractor;
+ Timer *mTimer;
+ uint8_t *mFrameBuf;
+ int64_t mDurationUs;
+};
+
+#endif // __EXTRACTOR_H__
\ No newline at end of file
diff --git a/media/tests/benchmark/src/native/muxer/Android.bp b/media/tests/benchmark/src/native/muxer/Android.bp
new file mode 100644
index 0000000..6ef2a2e
--- /dev/null
+++ b/media/tests/benchmark/src/native/muxer/Android.bp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+cc_library_static {
+ name: "libbenchmark_muxer",
+ defaults: [
+ "libbenchmark_common-defaults",
+ "libbenchmark_soft_sanitize_all-defaults",
+ ],
+
+ srcs: ["Muxer.cpp"],
+
+ static_libs: ["libbenchmark_extractor"],
+
+ export_include_dirs: ["."],
+
+ ldflags: ["-Wl,-Bsymbolic"]
+}
diff --git a/media/tests/benchmark/src/native/muxer/Muxer.cpp b/media/tests/benchmark/src/native/muxer/Muxer.cpp
new file mode 100644
index 0000000..877f7ad
--- /dev/null
+++ b/media/tests/benchmark/src/native/muxer/Muxer.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2019 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 "muxer"
+
+#include <fstream>
+#include <iostream>
+
+#include "Muxer.h"
+
+int32_t Muxer::initMuxer(int32_t fd, MUXER_OUTPUT_T outputFormat) {
+ if (!mFormat) mFormat = mExtractor->getFormat();
+ if (!mTimer) mTimer = new Timer();
+
+ int64_t sTime = mTimer->getCurTime();
+ mMuxer = AMediaMuxer_new(fd, (OutputFormat)outputFormat);
+ if (!mMuxer) {
+ cout << "[ WARN ] Test Skipped. Unable to create muxer \n";
+ return AMEDIA_ERROR_INVALID_OBJECT;
+ }
+ /*
+ * AMediaMuxer_addTrack returns the index of the new track or a negative value
+ * in case of failure, which can be interpreted as a media_status_t.
+ */
+ ssize_t index = AMediaMuxer_addTrack(mMuxer, mFormat);
+ if (index < 0) {
+ cout << "[ WARN ] Test Skipped. Format not supported \n";
+ return index;
+ }
+ AMediaMuxer_start(mMuxer);
+ int64_t eTime = mTimer->getCurTime();
+ int64_t timeTaken = mTimer->getTimeDiff(sTime, eTime);
+ mTimer->setInitTime(timeTaken);
+ return AMEDIA_OK;
+}
+
+void Muxer::deInitMuxer() {
+ int64_t sTime = mTimer->getCurTime();
+ if (mFormat) {
+ AMediaFormat_delete(mFormat);
+ mFormat = nullptr;
+ }
+ if (!mMuxer) return;
+ AMediaMuxer_stop(mMuxer);
+ AMediaMuxer_delete(mMuxer);
+ int64_t eTime = mTimer->getCurTime();
+ int64_t timeTaken = mTimer->getTimeDiff(sTime, eTime);
+ mTimer->setDeInitTime(timeTaken);
+}
+
+void Muxer::resetMuxer() {
+ if (mTimer) mTimer->resetTimers();
+}
+
+void Muxer::dumpStatistics(string inputReference) {
+ string operation = "mux";
+ mTimer->dumpStatistics(operation, inputReference, mExtractor->getClipDuration());
+}
+
+int32_t Muxer::mux(uint8_t *inputBuffer, vector<AMediaCodecBufferInfo> &frameInfos) {
+ // Mux frame data
+ size_t frameIdx = 0;
+ mTimer->setStartTime();
+ while (frameIdx < frameInfos.size()) {
+ AMediaCodecBufferInfo info = frameInfos.at(frameIdx);
+ media_status_t status = AMediaMuxer_writeSampleData(mMuxer, 0, inputBuffer, &info);
+ if (status != 0) {
+ ALOGE("Error in AMediaMuxer_writeSampleData");
+ return status;
+ }
+ mTimer->addOutputTime();
+ frameIdx++;
+ }
+ return AMEDIA_OK;
+}
diff --git a/media/tests/benchmark/src/native/muxer/Muxer.h b/media/tests/benchmark/src/native/muxer/Muxer.h
new file mode 100644
index 0000000..154fd20
--- /dev/null
+++ b/media/tests/benchmark/src/native/muxer/Muxer.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2019 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 __MUXER_H__
+#define __MUXER_H__
+
+#include <media/NdkMediaMuxer.h>
+
+#include "BenchmarkCommon.h"
+#include "Timer.h"
+#include "Extractor.h"
+
+typedef enum {
+ MUXER_OUTPUT_FORMAT_MPEG_4 = 0,
+ MUXER_OUTPUT_FORMAT_WEBM = 1,
+ MUXER_OUTPUT_FORMAT_3GPP = 2,
+ MUXER_OUTPUT_FORMAT_OGG = 4,
+ MUXER_OUTPUT_FORMAT_INVALID = 5,
+} MUXER_OUTPUT_T;
+
+class Muxer {
+ public:
+ Muxer() : mFormat(nullptr), mMuxer(nullptr), mTimer(nullptr) { mExtractor = new Extractor(); }
+
+ virtual ~Muxer() {
+ if (mTimer) delete mTimer;
+ if (mExtractor) delete mExtractor;
+ }
+
+ Timer *getTimer() { return mTimer; }
+ Extractor *getExtractor() { return mExtractor; }
+
+ /* Muxer related utilities */
+ int32_t initMuxer(int32_t fd, MUXER_OUTPUT_T outputFormat);
+ void deInitMuxer();
+ void resetMuxer();
+
+ /* Process the frames and give Muxed output */
+ int32_t mux(uint8_t *inputBuffer, vector<AMediaCodecBufferInfo> &frameSizes);
+
+ void dumpStatistics(string inputReference);
+
+ private:
+ AMediaFormat *mFormat;
+ AMediaMuxer *mMuxer;
+ Extractor *mExtractor;
+ Timer *mTimer;
+};
+
+#endif // __MUXER_H__
diff --git a/media/tests/benchmark/tests/Android.bp b/media/tests/benchmark/tests/Android.bp
new file mode 100644
index 0000000..353d60e
--- /dev/null
+++ b/media/tests/benchmark/tests/Android.bp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+cc_test {
+ name: "extractorTest",
+ gtest: true,
+ defaults: [
+ "libbenchmark_common-defaults",
+ "libbenchmark_soft_sanitize_all-defaults",
+ ],
+
+ srcs: ["ExtractorTest.cpp"],
+
+ static_libs: ["libbenchmark_extractor"]
+}
+
+cc_test {
+ name: "decoderTest",
+ gtest: true,
+ defaults: [
+ "libbenchmark_common-defaults",
+ "libbenchmark_soft_sanitize_all-defaults",
+ ],
+
+ srcs: ["DecoderTest.cpp"],
+
+ static_libs: [
+ "libbenchmark_extractor",
+ "libbenchmark_decoder",
+ ],
+}
+
+cc_test {
+ name: "muxerTest",
+ gtest: true,
+ defaults: [
+ "libbenchmark_common-defaults",
+ "libbenchmark_soft_sanitize_all-defaults",
+ ],
+
+ srcs: ["MuxerTest.cpp"],
+
+ static_libs: [
+ "libbenchmark_extractor",
+ "libbenchmark_muxer",
+ ],
+}
diff --git a/media/tests/benchmark/tests/BenchmarkTestEnvironment.h b/media/tests/benchmark/tests/BenchmarkTestEnvironment.h
new file mode 100644
index 0000000..ae2eee1
--- /dev/null
+++ b/media/tests/benchmark/tests/BenchmarkTestEnvironment.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 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 __BENCHMARK_TEST_ENVIRONMENT_H__
+#define __BENCHMARK_TEST_ENVIRONMENT_H__
+
+#include <gtest/gtest.h>
+
+#include <getopt.h>
+
+using namespace std;
+
+class BenchmarkTestEnvironment : public ::testing::Environment {
+ public:
+ BenchmarkTestEnvironment() : res("/sdcard/media/") {}
+
+ // Parses the command line argument
+ int initFromOptions(int argc, char **argv);
+
+ void setRes(const char *_res) { res = _res; }
+
+ const string getRes() const { return res; }
+
+ private:
+ string res;
+};
+
+int BenchmarkTestEnvironment::initFromOptions(int argc, char **argv) {
+ static struct option options[] = {{"path", required_argument, 0, 'P'}, {0, 0, 0, 0}};
+
+ while (true) {
+ int index = 0;
+ int c = getopt_long(argc, argv, "P:", options, &index);
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 'P': {
+ setRes(optarg);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ if (optind < argc) {
+ fprintf(stderr,
+ "unrecognized option: %s\n\n"
+ "usage: %s <gtest options> <test options>\n\n"
+ "test options are:\n\n"
+ "-P, --path: Resource files directory location\n",
+ argv[optind ?: 1], argv[0]);
+ return 2;
+ }
+ return 0;
+}
+
+#endif // __BENCHMARK_TEST_ENVIRONMENT_H__
diff --git a/media/tests/benchmark/tests/DecoderTest.cpp b/media/tests/benchmark/tests/DecoderTest.cpp
new file mode 100644
index 0000000..6cb42d6
--- /dev/null
+++ b/media/tests/benchmark/tests/DecoderTest.cpp
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2019 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 "decoderTest"
+
+#include <fstream>
+#include <iostream>
+#include <limits>
+
+#include "Decoder.h"
+#include "BenchmarkTestEnvironment.h"
+
+static BenchmarkTestEnvironment *gEnv = nullptr;
+
+class DecoderTest : public ::testing::TestWithParam<tuple<string, string, bool>> {};
+
+TEST_P(DecoderTest, Decode) {
+ ALOGV("Decode the samples given by extractor");
+ tuple<string /* InputFile */, string /* CodecName */, bool /* asyncMode */> params = GetParam();
+
+ string inputFile = gEnv->getRes() + get<0>(params);
+ FILE *inputFp = fopen(inputFile.c_str(), "rb");
+ if (!inputFp) {
+ cout << "[ WARN ] Test Skipped. Unable to open input file for reading \n";
+ return;
+ }
+
+ Decoder *decoder = new Decoder();
+ Extractor *extractor = decoder->getExtractor();
+ if (!extractor) {
+ cout << "[ WARN ] Test Skipped. Extractor creation failed \n";
+ return;
+ }
+
+ // Read file properties
+ fseek(inputFp, 0, SEEK_END);
+ size_t fileSize = ftell(inputFp);
+ fseek(inputFp, 0, SEEK_SET);
+ int32_t fd = fileno(inputFp);
+
+ int32_t trackCount = extractor->initExtractor(fd, fileSize);
+ if (trackCount <= 0) {
+ cout << "[ WARN ] Test Skipped. initExtractor failed\n";
+ return;
+ }
+ for (int curTrack = 0; curTrack < trackCount; curTrack++) {
+ int32_t status = extractor->setupTrackFormat(curTrack);
+ if (status != 0) {
+ cout << "[ WARN ] Test Skipped. Track Format invalid \n";
+ return;
+ }
+
+ uint8_t *inputBuffer = (uint8_t *)malloc(kMaxBufferSize);
+ if (!inputBuffer) {
+ cout << "[ WARN ] Test Skipped. Insufficient memory \n";
+ return;
+ }
+ vector<AMediaCodecBufferInfo> frameInfo;
+ AMediaCodecBufferInfo info;
+ uint32_t inputBufferOffset = 0;
+ int32_t idx = 0;
+
+ // Get CSD data
+ while (1) {
+ void *csdBuffer = extractor->getCSDSample(info, idx);
+ if (!csdBuffer || !info.size) break;
+
+ // copy the meta data and buffer to be passed to decoder
+ if (inputBufferOffset + info.size > kMaxBufferSize) {
+ cout << "[ WARN ] Test Skipped. Memory allocated not sufficient\n";
+ free(inputBuffer);
+ return;
+ }
+ memcpy(inputBuffer + inputBufferOffset, csdBuffer, info.size);
+ frameInfo.push_back(info);
+ inputBufferOffset += info.size;
+ idx++;
+ }
+ // Get frame data
+ while (1) {
+ status = extractor->getFrameSample(info);
+ if (status || !info.size) break;
+ // copy the meta data and buffer to be passed to decoder
+ if (inputBufferOffset + info.size > kMaxBufferSize) {
+ cout << "[ WARN ] Test Skipped. Memory allocated not sufficient\n";
+ free(inputBuffer);
+ return;
+ }
+ memcpy(inputBuffer + inputBufferOffset, extractor->getFrameBuf(), info.size);
+ frameInfo.push_back(info);
+ inputBufferOffset += info.size;
+ }
+
+ string codecName = get<1>(params);
+ bool asyncMode = get<2>(params);
+ decoder->setupDecoder();
+ status = decoder->decode(inputBuffer, frameInfo, codecName, asyncMode);
+ if (status != AMEDIA_OK) {
+ cout << "[ WARN ] Test Skipped. Decode returned error \n";
+ free(inputBuffer);
+ return;
+ }
+ decoder->deInitCodec();
+ cout << "codec : " << codecName << endl;
+ string inputReference = get<0>(params);
+ decoder->dumpStatistics(inputReference);
+ free(inputBuffer);
+ decoder->resetDecoder();
+ }
+ fclose(inputFp);
+ extractor->deInitExtractor();
+ delete decoder;
+}
+
+// TODO: (b/140549596)
+// Add wav files
+INSTANTIATE_TEST_SUITE_P(
+ AudioDecoderSyncTest, DecoderTest,
+ ::testing::Values(make_tuple("bbb_44100hz_2ch_128kbps_aac_30sec.mp4", "", false),
+ make_tuple("bbb_44100hz_2ch_128kbps_mp3_30sec.mp3", "", false),
+ make_tuple("bbb_8000hz_1ch_8kbps_amrnb_30sec.3gp", "", false),
+ make_tuple("bbb_16000hz_1ch_9kbps_amrwb_30sec.3gp", "", false),
+ make_tuple("bbb_44100hz_2ch_80kbps_vorbis_30sec.mp4", "", false),
+ make_tuple("bbb_48000hz_2ch_100kbps_opus_30sec.webm", "", false)));
+
+INSTANTIATE_TEST_SUITE_P(
+ AudioDecoderAsyncTest, DecoderTest,
+ ::testing::Values(make_tuple("bbb_44100hz_2ch_128kbps_aac_30sec.mp4", "", true),
+ make_tuple("bbb_44100hz_2ch_128kbps_mp3_30sec.mp3", "", true),
+ make_tuple("bbb_8000hz_1ch_8kbps_amrnb_30sec.3gp", "", true),
+ make_tuple("bbb_16000hz_1ch_9kbps_amrwb_30sec.3gp", "", true),
+ make_tuple("bbb_44100hz_2ch_80kbps_vorbis_30sec.mp4", "", true),
+ make_tuple("bbb_48000hz_2ch_100kbps_opus_30sec.webm", "", true)));
+
+INSTANTIATE_TEST_SUITE_P(VideDecoderSyncTest, DecoderTest,
+ ::testing::Values(
+ // Hardware codecs
+ make_tuple("crowd_1920x1080_25fps_4000kbps_vp9.webm", "", false),
+ make_tuple("crowd_1920x1080_25fps_4000kbps_vp8.webm", "", false),
+ make_tuple("crowd_1920x1080_25fps_4000kbps_av1.webm", "", false),
+ make_tuple("crowd_1920x1080_25fps_7300kbps_mpeg2.mp4", "", false),
+ make_tuple("crowd_1920x1080_25fps_6000kbps_mpeg4.mp4", "", false),
+ make_tuple("crowd_352x288_25fps_6000kbps_h263.3gp", "", false),
+ make_tuple("crowd_1920x1080_25fps_6700kbps_h264.ts", "", false),
+ make_tuple("crowd_1920x1080_25fps_4000kbps_h265.mkv", "", false),
+ // Software codecs
+ make_tuple("crowd_1920x1080_25fps_4000kbps_vp9.webm",
+ "c2.android.vp9.decoder", false),
+ make_tuple("crowd_1920x1080_25fps_4000kbps_vp8.webm",
+ "c2.android.vp8.decoder", false),
+ make_tuple("crowd_1920x1080_25fps_4000kbps_av1.webm",
+ "c2.android.av1.decoder", false),
+ make_tuple("crowd_1920x1080_25fps_7300kbps_mpeg2.mp4",
+ "c2.android.mpeg2.decoder", false),
+ make_tuple("crowd_1920x1080_25fps_6000kbps_mpeg4.mp4",
+ "c2.android.mpeg4.decoder", false),
+ make_tuple("crowd_352x288_25fps_6000kbps_h263.3gp",
+ "c2.android.h263.decoder", false),
+ make_tuple("crowd_1920x1080_25fps_6700kbps_h264.ts",
+ "c2.android.avc.decoder", false),
+ make_tuple("crowd_1920x1080_25fps_4000kbps_h265.mkv",
+ "c2.android.hevc.decoder", false)));
+
+INSTANTIATE_TEST_SUITE_P(VideoDecoderAsyncTest, DecoderTest,
+ ::testing::Values(
+ // Hardware codecs
+ make_tuple("crowd_1920x1080_25fps_4000kbps_vp9.webm", "", true),
+ make_tuple("crowd_1920x1080_25fps_4000kbps_vp8.webm", "", true),
+ make_tuple("crowd_1920x1080_25fps_4000kbps_av1.webm", "", true),
+ make_tuple("crowd_1920x1080_25fps_7300kbps_mpeg2.mp4", "", true),
+ make_tuple("crowd_1920x1080_25fps_6000kbps_mpeg4.mp4", "", true),
+ make_tuple("crowd_352x288_25fps_6000kbps_h263.3gp", "", true),
+ make_tuple("crowd_1920x1080_25fps_6700kbps_h264.ts", "", true),
+ make_tuple("crowd_1920x1080_25fps_4000kbps_h265.mkv", "", true),
+ // Software codecs
+ make_tuple("crowd_1920x1080_25fps_4000kbps_vp9.webm",
+ "c2.android.vp9.decoder", true),
+ make_tuple("crowd_1920x1080_25fps_4000kbps_vp8.webm",
+ "c2.android.vp8.decoder", true),
+ make_tuple("crowd_1920x1080_25fps_4000kbps_av1.webm",
+ "c2.android.av1.decoder", true),
+ make_tuple("crowd_1920x1080_25fps_7300kbps_mpeg2.mp4",
+ "c2.android.mpeg2.decoder", true),
+ make_tuple("crowd_1920x1080_25fps_6000kbps_mpeg4.mp4",
+ "c2.android.mpeg4.decoder", true),
+ make_tuple("crowd_352x288_25fps_6000kbps_h263.3gp",
+ "c2.android.h263.decoder", true),
+ make_tuple("crowd_1920x1080_25fps_6700kbps_h264.ts",
+ "c2.android.avc.decoder", true),
+ make_tuple("crowd_1920x1080_25fps_4000kbps_h265.mkv",
+ "c2.android.hevc.decoder", true)));
+
+int main(int argc, char **argv) {
+ gEnv = new BenchmarkTestEnvironment();
+ ::testing::AddGlobalTestEnvironment(gEnv);
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = gEnv->initFromOptions(argc, argv);
+ if (status == 0) {
+ status = RUN_ALL_TESTS();
+ ALOGD("Decoder Test result = %d\n", status);
+ }
+ return status;
+}
\ No newline at end of file
diff --git a/media/tests/benchmark/tests/ExtractorTest.cpp b/media/tests/benchmark/tests/ExtractorTest.cpp
new file mode 100644
index 0000000..dd0d711
--- /dev/null
+++ b/media/tests/benchmark/tests/ExtractorTest.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2019 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 "extractorTest"
+
+#include <gtest/gtest.h>
+
+#include "Extractor.h"
+#include "BenchmarkTestEnvironment.h"
+
+static BenchmarkTestEnvironment *gEnv = nullptr;
+
+class ExtractorTest : public ::testing::TestWithParam<pair<string, int32_t>> {};
+
+TEST_P(ExtractorTest, Extract) {
+ Extractor *extractObj = new Extractor();
+
+ string inputFile = gEnv->getRes() + GetParam().first;
+ FILE *inputFp = fopen(inputFile.c_str(), "rb");
+ if (!inputFp) {
+ cout << "[ WARN ] Test Skipped. Unable to open input file for reading \n";
+ return;
+ }
+
+ // Read file properties
+ size_t fileSize = 0;
+ fseek(inputFp, 0, SEEK_END);
+ fileSize = ftell(inputFp);
+ fseek(inputFp, 0, SEEK_SET);
+ int32_t fd = fileno(inputFp);
+
+ int32_t trackCount = extractObj->initExtractor(fd, fileSize);
+ if (trackCount <= 0) {
+ cout << "[ WARN ] Test Skipped. initExtractor failed\n";
+ return;
+ }
+
+ int32_t trackID = GetParam().second;
+ int32_t status = extractObj->extract(trackID);
+ if (status != AMEDIA_OK) {
+ cout << "[ WARN ] Test Skipped. Extraction failed \n";
+ return;
+ }
+
+ extractObj->deInitExtractor();
+
+ extractObj->dumpStatistics(GetParam().first);
+
+ fclose(inputFp);
+ delete extractObj;
+}
+
+INSTANTIATE_TEST_SUITE_P(ExtractorTestAll, ExtractorTest,
+ ::testing::Values(make_pair("crowd_1920x1080_25fps_4000kbps_vp9.webm", 0),
+ make_pair("crowd_1920x1080_25fps_6000kbps_h263.3gp", 0),
+ make_pair("crowd_1920x1080_25fps_6000kbps_mpeg4.mp4", 0),
+ make_pair("crowd_1920x1080_25fps_6700kbps_h264.ts", 0),
+ make_pair("crowd_1920x1080_25fps_7300kbps_mpeg2.mp4", 0),
+ make_pair("crowd_1920x1080_25fps_4000kbps_av1.webm", 0),
+ make_pair("crowd_1920x1080_25fps_4000kbps_h265.mkv", 0),
+ make_pair("crowd_1920x1080_25fps_4000kbps_vp8.webm", 0),
+ make_pair("bbb_44100hz_2ch_128kbps_aac_5mins.mp4", 0),
+ make_pair("bbb_44100hz_2ch_128kbps_mp3_5mins.mp3", 0),
+ make_pair("bbb_44100hz_2ch_600kbps_flac_5mins.flac", 0),
+ make_pair("bbb_8000hz_1ch_8kbps_amrnb_5mins.3gp", 0),
+ make_pair("bbb_16000hz_1ch_9kbps_amrwb_5mins.3gp", 0),
+ make_pair("bbb_44100hz_2ch_80kbps_vorbis_5mins.mp4", 0),
+ make_pair("bbb_48000hz_2ch_100kbps_opus_5mins.webm", 0)));
+
+int main(int argc, char **argv) {
+ gEnv = new BenchmarkTestEnvironment();
+ ::testing::AddGlobalTestEnvironment(gEnv);
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = gEnv->initFromOptions(argc, argv);
+ if (status == 0) {
+ status = RUN_ALL_TESTS();
+ ALOGD(" Extractor Test result = %d\n", status);
+ }
+ return status;
+}
diff --git a/media/tests/benchmark/tests/MuxerTest.cpp b/media/tests/benchmark/tests/MuxerTest.cpp
new file mode 100644
index 0000000..e814f90
--- /dev/null
+++ b/media/tests/benchmark/tests/MuxerTest.cpp
@@ -0,0 +1,181 @@
+
+/*
+ * Copyright (C) 2019 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 "muxerTest"
+
+#include <fstream>
+#include <iostream>
+
+#include "Muxer.h"
+#include "BenchmarkTestEnvironment.h"
+
+#define OUTPUT_FILE_NAME "/data/local/tmp/mux.out"
+
+static BenchmarkTestEnvironment *gEnv = nullptr;
+
+class MuxerTest : public ::testing::TestWithParam<pair<string, string>> {};
+
+static MUXER_OUTPUT_T getMuxerOutFormat(string fmt) {
+ static const struct {
+ string name;
+ MUXER_OUTPUT_T value;
+ } kFormatMaps[] = {{"mp4", MUXER_OUTPUT_FORMAT_MPEG_4},
+ {"webm", MUXER_OUTPUT_FORMAT_WEBM},
+ {"3gpp", MUXER_OUTPUT_FORMAT_3GPP},
+ {"ogg", MUXER_OUTPUT_FORMAT_OGG}};
+
+ MUXER_OUTPUT_T format = MUXER_OUTPUT_FORMAT_INVALID;
+ for (size_t i = 0; i < sizeof(kFormatMaps) / sizeof(kFormatMaps[0]); ++i) {
+ if (!fmt.compare(kFormatMaps[i].name)) {
+ format = kFormatMaps[i].value;
+ break;
+ }
+ }
+ return format;
+}
+
+TEST_P(MuxerTest, Mux) {
+ ALOGV("Mux the samples given by extractor");
+ string inputFile = gEnv->getRes() + GetParam().first;
+ FILE *inputFp = fopen(inputFile.c_str(), "rb");
+ if (!inputFp) {
+ cout << "[ WARN ] Test Skipped. Unable to open input file for reading \n";
+ return;
+ }
+ string fmt = GetParam().second;
+ MUXER_OUTPUT_T outputFormat = getMuxerOutFormat(fmt);
+ if (outputFormat == MUXER_OUTPUT_FORMAT_INVALID) {
+ ALOGE("output format is MUXER_OUTPUT_FORMAT_INVALID");
+ return;
+ }
+
+ Muxer *muxerObj = new Muxer();
+ Extractor *extractor = muxerObj->getExtractor();
+ if (!extractor) {
+ cout << "[ WARN ] Test Skipped. Extractor creation failed \n";
+ return;
+ }
+
+ // Read file properties
+ size_t fileSize = 0;
+ fseek(inputFp, 0, SEEK_END);
+ fileSize = ftell(inputFp);
+ fseek(inputFp, 0, SEEK_SET);
+ int32_t fd = fileno(inputFp);
+
+ int32_t trackCount = extractor->initExtractor(fd, fileSize);
+ if (trackCount <= 0) {
+ cout << "[ WARN ] Test Skipped. initExtractor failed\n";
+ return;
+ }
+
+ for (int curTrack = 0; curTrack < trackCount; curTrack++) {
+ int32_t status = extractor->setupTrackFormat(curTrack);
+ if (status != 0) {
+ cout << "[ WARN ] Test Skipped. Track Format invalid \n";
+ return;
+ }
+
+ uint8_t *inputBuffer = (uint8_t *)malloc(kMaxBufferSize);
+ if (!inputBuffer) {
+ std::cout << "[ WARN ] Test Skipped. Insufficient memory \n";
+ return;
+ }
+ // AMediaCodecBufferInfo : <size of frame> <flags> <presentationTimeUs> <offset>
+ vector<AMediaCodecBufferInfo> frameInfos;
+ AMediaCodecBufferInfo info;
+ uint32_t inputBufferOffset = 0;
+
+ // Get Frame Data
+ while (1) {
+ status = extractor->getFrameSample(info);
+ if (status || !info.size) break;
+ // copy the meta data and buffer to be passed to muxer
+ if (inputBufferOffset + info.size > kMaxBufferSize) {
+ cout << "[ WARN ] Test Skipped. Memory allocated not sufficient\n";
+ free(inputBuffer);
+ return;
+ }
+ memcpy(inputBuffer + inputBufferOffset, extractor->getFrameBuf(), info.size);
+ info.offset = inputBufferOffset;
+ frameInfos.push_back(info);
+ inputBufferOffset += info.size;
+ }
+
+ string outputFileName = OUTPUT_FILE_NAME;
+ FILE *outputFp = fopen(outputFileName.c_str(), "w+b");
+ if (!outputFp) {
+ cout << "[ WARN ] Test Skipped. Unable to open output file for writing \n";
+ return;
+ }
+ int32_t fd = fileno(outputFp);
+ status = muxerObj->initMuxer(fd, outputFormat);
+ if (status != 0) {
+ cout << "[ WARN ] Test Skipped. initMuxer failed\n";
+ return;
+ }
+
+ status = muxerObj->mux(inputBuffer, frameInfos);
+ if (status != 0) {
+ cout << "[ WARN ] Test Skipped. Mux failed \n";
+ return;
+ }
+ muxerObj->deInitMuxer();
+ muxerObj->dumpStatistics(GetParam().first + "." + fmt.c_str());
+ free(inputBuffer);
+ fclose(outputFp);
+ muxerObj->resetMuxer();
+ }
+ fclose(inputFp);
+ extractor->deInitExtractor();
+ delete muxerObj;
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ MuxerTestAll, MuxerTest,
+ ::testing::Values(make_pair("crowd_1920x1080_25fps_4000kbps_vp8.webm", "webm"),
+ make_pair("crowd_1920x1080_25fps_4000kbps_vp9.webm", "webm"),
+ make_pair("crowd_1920x1080_25fps_6000kbps_mpeg4.mp4", "mp4"),
+ make_pair("crowd_352x288_25fps_6000kbps_h263.3gp", "mp4"),
+ make_pair("crowd_1920x1080_25fps_6700kbps_h264.ts", "mp4"),
+ make_pair("crowd_1920x1080_25fps_4000kbps_h265.mkv", "mp4"),
+ make_pair("crowd_1920x1080_25fps_6000kbps_mpeg4.mp4", "3gpp"),
+ make_pair("crowd_352x288_25fps_6000kbps_h263.3gp", "3gpp"),
+ make_pair("crowd_1920x1080_25fps_6700kbps_h264.ts", "3gpp"),
+ make_pair("crowd_1920x1080_25fps_4000kbps_h265.mkv", "3gpp"),
+ make_pair("bbb_48000hz_2ch_100kbps_opus_5mins.webm", "ogg"),
+ make_pair("bbb_44100hz_2ch_80kbps_vorbis_5mins.mp4", "webm"),
+ make_pair("bbb_48000hz_2ch_100kbps_opus_5mins.webm", "webm"),
+ make_pair("bbb_44100hz_2ch_128kbps_aac_5mins.mp4", "mp4"),
+ make_pair("bbb_8000hz_1ch_8kbps_amrnb_5mins.3gp", "mp4"),
+ make_pair("bbb_16000hz_1ch_9kbps_amrwb_5mins.3gp", "mp4"),
+ make_pair("bbb_44100hz_2ch_128kbps_aac_5mins.mp4", "3gpp"),
+ make_pair("bbb_8000hz_1ch_8kbps_amrnb_5mins.3gp", "3gpp"),
+ make_pair("bbb_16000hz_1ch_9kbps_amrwb_5mins.3gp", "3gpp")));
+
+int main(int argc, char **argv) {
+ gEnv = new BenchmarkTestEnvironment();
+ ::testing::AddGlobalTestEnvironment(gEnv);
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = gEnv->initFromOptions(argc, argv);
+ if (status == 0) {
+ status = RUN_ALL_TESTS();
+ ALOGV("Test result = %d\n", status);
+ }
+ return status;
+}
diff --git a/media/utils/Android.bp b/media/utils/Android.bp
index 0ed92f7..2dbc476 100644
--- a/media/utils/Android.bp
+++ b/media/utils/Android.bp
@@ -45,10 +45,10 @@
"-Werror",
],
- include_dirs: [
- // For android_mallopt definitions.
- "bionic/libc/private"
+ header_libs: [
+ "bionic_libc_platform_headers",
],
+
local_include_dirs: ["include"],
export_include_dirs: ["include"],
}
diff --git a/media/utils/MemoryLeakTrackUtil.cpp b/media/utils/MemoryLeakTrackUtil.cpp
index 2988b52..6166859 100644
--- a/media/utils/MemoryLeakTrackUtil.cpp
+++ b/media/utils/MemoryLeakTrackUtil.cpp
@@ -22,7 +22,7 @@
#include "media/MemoryLeakTrackUtil.h"
#include <sstream>
-#include <bionic_malloc.h>
+#include <bionic/malloc.h>
/*
* The code here originally resided in MediaPlayerService.cpp
diff --git a/media/utils/ServiceUtilities.cpp b/media/utils/ServiceUtilities.cpp
index db13903..7fe871c 100644
--- a/media/utils/ServiceUtilities.cpp
+++ b/media/utils/ServiceUtilities.cpp
@@ -222,9 +222,9 @@
off_t size = lseek(heap->getHeapID(), 0, SEEK_END);
lseek(heap->getHeapID(), 0, SEEK_SET);
- if (iMemory->pointer() == NULL || size < (off_t)iMemory->size()) {
+ if (iMemory->unsecurePointer() == NULL || size < (off_t)iMemory->size()) {
ALOGE("%s check failed: pointer %p size %zu fd size %u",
- __FUNCTION__, iMemory->pointer(), iMemory->size(), (uint32_t)size);
+ __FUNCTION__, iMemory->unsecurePointer(), iMemory->size(), (uint32_t)size);
return BAD_VALUE;
}
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index aef0ade..27d7856 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -652,7 +652,7 @@
return new NBLog::Writer();
}
success:
- NBLog::Shared *sharedRawPtr = (NBLog::Shared *) shared->pointer();
+ NBLog::Shared *sharedRawPtr = (NBLog::Shared *) shared->unsecurePointer();
new((void *) sharedRawPtr) NBLog::Shared(); // placement new here, but the corresponding
// explicit destructor not needed since it is POD
sMediaLogService->registerWriter(shared, size, name);
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index 13152d0..d54ab42 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -1588,7 +1588,7 @@
int bufOffset = ((sizeof(effect_param_cblk_t) - 1) / sizeof(int) + 1) * sizeof(int);
mCblkMemory = client->heap()->allocate(EFFECT_PARAM_BUFFER_SIZE + bufOffset);
if (mCblkMemory == 0 ||
- (mCblk = static_cast<effect_param_cblk_t *>(mCblkMemory->pointer())) == NULL) {
+ (mCblk = static_cast<effect_param_cblk_t *>(mCblkMemory->unsecurePointer())) == NULL) {
ALOGE("not enough memory for Effect size=%zu", EFFECT_PARAM_BUFFER_SIZE +
sizeof(effect_param_cblk_t));
mCblkMemory.clear();
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 6ca50a7..8fddc13 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -2079,9 +2079,9 @@
// More than 2 channels does not require stronger alignment than stereo
alignment <<= 1;
}
- if (((uintptr_t)sharedBuffer->pointer() & (alignment - 1)) != 0) {
+ if (((uintptr_t)sharedBuffer->unsecurePointer() & (alignment - 1)) != 0) {
ALOGE("Invalid buffer alignment: address %p, channel count %u",
- sharedBuffer->pointer(), channelCount);
+ sharedBuffer->unsecurePointer(), channelCount);
lStatus = BAD_VALUE;
goto Exit;
}
@@ -6756,7 +6756,7 @@
sp<IMemory> pipeMemory;
if ((roHeap == 0) ||
(pipeMemory = roHeap->allocate(pipeSize)) == 0 ||
- (pipeBuffer = pipeMemory->pointer()) == nullptr) {
+ (pipeBuffer = pipeMemory->unsecurePointer()) == nullptr) {
ALOGE("not enough memory for pipe buffer size=%zu; "
"roHeap=%p, pipeMemory=%p, pipeBuffer=%p; roHeapSize: %lld",
pipeSize, roHeap.get(), pipeMemory.get(), pipeBuffer,
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index fa35e5d..6a999fc 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -150,7 +150,7 @@
if (client != 0) {
mCblkMemory = client->heap()->allocate(size);
if (mCblkMemory == 0 ||
- (mCblk = static_cast<audio_track_cblk_t *>(mCblkMemory->pointer())) == NULL) {
+ (mCblk = static_cast<audio_track_cblk_t *>(mCblkMemory->unsecurePointer())) == NULL) {
ALOGE("%s(%d): not enough memory for AudioTrack size=%zu", __func__, mId, size);
client->heap()->dump("AudioTrack");
mCblkMemory.clear();
@@ -172,7 +172,7 @@
const sp<MemoryDealer> roHeap(thread->readOnlyHeap());
if (roHeap == 0 ||
(mBufferMemory = roHeap->allocate(bufferSize)) == 0 ||
- (mBuffer = mBufferMemory->pointer()) == NULL) {
+ (mBuffer = mBufferMemory->unsecurePointer()) == NULL) {
ALOGE("%s(%d): not enough memory for read-only buffer size=%zu",
__func__, mId, bufferSize);
if (roHeap != 0) {
@@ -187,7 +187,7 @@
case ALLOC_PIPE:
mBufferMemory = thread->pipeMemory();
// mBuffer is the virtual address as seen from current process (mediaserver),
- // and should normally be coming from mBufferMemory->pointer().
+ // and should normally be coming from mBufferMemory->unsecurePointer().
// However in this case the TrackBase does not reference the buffer directly.
// It should references the buffer via the pipe.
// Therefore, to detect incorrect usage of the buffer, we set mBuffer to NULL.
@@ -510,7 +510,11 @@
track_type type,
audio_port_handle_t portId)
: TrackBase(thread, client, attr, sampleRate, format, channelMask, frameCount,
- (sharedBuffer != 0) ? sharedBuffer->pointer() : buffer,
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ (sharedBuffer != 0) ? sharedBuffer->unsecurePointer() : buffer,
(sharedBuffer != 0) ? sharedBuffer->size() : bufferSize,
sessionId, creatorPid, uid, true /*isOut*/,
(type == TYPE_PATCH) ? ( buffer == NULL ? ALLOC_LOCAL : ALLOC_NONE) : ALLOC_CBLK,
@@ -540,7 +544,7 @@
ALOG_ASSERT(!(client == 0 && sharedBuffer != 0));
ALOGV_IF(sharedBuffer != 0, "%s(%d): sharedBuffer: %p, size: %zu",
- __func__, mId, sharedBuffer->pointer(), sharedBuffer->size());
+ __func__, mId, sharedBuffer->unsecurePointer(), sharedBuffer->size());
if (mCblk == NULL) {
return;
diff --git a/services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp b/services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp
index d4a7f31..bf1a0f7 100644
--- a/services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp
@@ -78,7 +78,10 @@
}
}
- if (isPlaybackThread && (getFlags() & flags) != flags) {
+ const uint32_t mustMatchOutputFlags =
+ AUDIO_OUTPUT_FLAG_DIRECT|AUDIO_OUTPUT_FLAG_HW_AV_SYNC|AUDIO_OUTPUT_FLAG_MMAP_NOIRQ;
+ if (isPlaybackThread && (((getFlags() ^ flags) & mustMatchOutputFlags)
+ || (getFlags() & flags) != flags)) {
return false;
}
// The only input flag that is allowed to be different is the fast flag.
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 75ce9f2..aec9cee 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -5099,7 +5099,7 @@
if (invalidate) continue;
for (auto client : desc->clientsList(false /*activeOnly*/)) {
- if (!desc->mProfile->isDirectOutput()) {
+ if (desc->isDuplicated() || !desc->mProfile->isDirectOutput()) {
// a client on a non direct outputs has necessarily a linear PCM format
// so we can call selectOutput() safely
const audio_io_handle_t newOutput = selectOutput(dstOutputs,
diff --git a/services/audiopolicy/tests/AudioPolicyTestManager.h b/services/audiopolicy/tests/AudioPolicyTestManager.h
index fe543a6..ba1412b 100644
--- a/services/audiopolicy/tests/AudioPolicyTestManager.h
+++ b/services/audiopolicy/tests/AudioPolicyTestManager.h
@@ -25,6 +25,7 @@
: AudioPolicyManager(clientInterface, true /*forTesting*/) { }
using AudioPolicyManager::getConfig;
using AudioPolicyManager::initialize;
+ using AudioPolicyManager::getOutputs;
};
} // namespace android
diff --git a/services/audiopolicy/tests/audiopolicymanager_tests.cpp b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
index 60b5009..d3d839e 100644
--- a/services/audiopolicy/tests/audiopolicymanager_tests.cpp
+++ b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
@@ -167,14 +167,15 @@
virtual void SetUpManagerConfig();
void dumpToLog();
- // When explicitly routing is needed, selectedDeviceId need to be set as the wanted port
- // id. Otherwise, selectedDeviceId need to be initialized as AUDIO_PORT_HANDLE_NONE.
+ // When explicit routing is needed, selectedDeviceId needs to be set as the wanted port
+ // id. Otherwise, selectedDeviceId needs to be initialized as AUDIO_PORT_HANDLE_NONE.
void getOutputForAttr(
audio_port_handle_t *selectedDeviceId,
audio_format_t format,
int channelMask,
int sampleRate,
audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
+ audio_io_handle_t *output = nullptr,
audio_port_handle_t *portId = nullptr,
audio_attributes_t attr = {});
void getInputForAttr(
@@ -249,9 +250,12 @@
int channelMask,
int sampleRate,
audio_output_flags_t flags,
+ audio_io_handle_t *output,
audio_port_handle_t *portId,
audio_attributes_t attr) {
- audio_io_handle_t output = AUDIO_PORT_HANDLE_NONE;
+ audio_io_handle_t localOutput;
+ if (!output) output = &localOutput;
+ *output = AUDIO_IO_HANDLE_NONE;
audio_stream_type_t stream = AUDIO_STREAM_DEFAULT;
audio_config_t config = AUDIO_CONFIG_INITIALIZER;
config.sample_rate = sampleRate;
@@ -261,9 +265,10 @@
if (!portId) portId = &localPortId;
*portId = AUDIO_PORT_HANDLE_NONE;
ASSERT_EQ(OK, mManager->getOutputForAttr(
- &attr, &output, AUDIO_SESSION_NONE, &stream, 0 /*uid*/, &config, &flags,
+ &attr, output, AUDIO_SESSION_NONE, &stream, 0 /*uid*/, &config, &flags,
selectedDeviceId, portId, {}));
ASSERT_NE(AUDIO_PORT_HANDLE_NONE, *portId);
+ ASSERT_NE(AUDIO_IO_HANDLE_NONE, *output);
}
void AudioPolicyManagerTest::getInputForAttr(
@@ -313,7 +318,7 @@
return;
}
}
- GTEST_FAIL();
+ GTEST_FAIL() << "Device port with role " << role << " and address " << address << "not found";
}
audio_port_handle_t AudioPolicyManagerTest::getDeviceIdFromPatch(
@@ -523,7 +528,7 @@
audio_port_handle_t portId;
getOutputForAttr(&selectedDeviceId,
AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT,
- &portId);
+ nullptr /*output*/, &portId);
ASSERT_EQ(selectedDeviceId, mMsdOutputDevice->getId());
ASSERT_EQ(1, patchCount.deltaFromSnapshot());
mManager->releaseOutput(portId);
@@ -535,7 +540,7 @@
audio_port_handle_t portId;
getOutputForAttr(&selectedDeviceId,
AUDIO_FORMAT_DTS, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT,
- &portId);
+ nullptr /*output*/, &portId);
ASSERT_NE(selectedDeviceId, mMsdOutputDevice->getId());
ASSERT_EQ(-1, patchCount.deltaFromSnapshot());
mManager->releaseOutput(portId);
@@ -810,9 +815,9 @@
const audio_usage_t usage = attr.usage;
audio_port_handle_t playbackRoutedPortId = AUDIO_PORT_HANDLE_NONE;
- audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
getOutputForAttr(&playbackRoutedPortId, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
- 48000 /*sampleRate*/, AUDIO_OUTPUT_FLAG_NONE, &portId, attr);
+ 48000 /*sampleRate*/, AUDIO_OUTPUT_FLAG_NONE,
+ nullptr /*output*/, nullptr /*portId*/, attr);
if (std::find_if(begin(mUsageRules), end(mUsageRules), [&usage](const auto &usageRule) {
return (std::get<0>(usageRule) == usage) &&
(std::get<2>(usageRule) == RULE_MATCH_ATTRIBUTE_USAGE);}) != end(mUsageRules) ||
@@ -965,7 +970,7 @@
std::string tags = std::string("addr=") + mMixAddress;
strncpy(attr.tags, tags.c_str(), AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
getOutputForAttr(&selectedDeviceId, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
- 48000 /*sampleRate*/, AUDIO_OUTPUT_FLAG_NONE, &mPortId, attr);
+ 48000 /*sampleRate*/, AUDIO_OUTPUT_FLAG_NONE, nullptr /*output*/, &mPortId, attr);
ASSERT_EQ(NO_ERROR, mManager->startOutput(mPortId));
ASSERT_EQ(injectionPort.id, getDeviceIdFromPatch(mClient->getLastAddedPatch()));
@@ -1108,15 +1113,14 @@
findDevicePort(role, type, address, devicePort);
audio_port_handle_t routedPortId = devicePort.id;
- audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
// Try start input or output according to the device type
if (audio_is_output_devices(type)) {
getOutputForAttr(&routedPortId, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
- 48000 /*sampleRate*/, AUDIO_OUTPUT_FLAG_NONE, &portId);
+ 48000 /*sampleRate*/, AUDIO_OUTPUT_FLAG_NONE);
} else if (audio_is_input_device(type)) {
RecordingActivityTracker tracker;
getInputForAttr({}, tracker.getRiid(), &routedPortId, AUDIO_FORMAT_PCM_16_BIT,
- AUDIO_CHANNEL_IN_STEREO, 48000 /*sampleRate*/, AUDIO_INPUT_FLAG_NONE, &portId);
+ AUDIO_CHANNEL_IN_STEREO, 48000 /*sampleRate*/, AUDIO_INPUT_FLAG_NONE);
}
ASSERT_EQ(devicePort.id, routedPortId);
@@ -1139,3 +1143,67 @@
"hfp_client_out"})
)
);
+
+class AudioPolicyManagerTVTest : public AudioPolicyManagerTestWithConfigurationFile {
+protected:
+ std::string getConfigFile() override { return sTvConfig; }
+ void testHDMIPortSelection(audio_output_flags_t flags, const char* expectedMixPortName);
+
+ static const std::string sTvConfig;
+};
+
+const std::string AudioPolicyManagerTVTest::sTvConfig =
+ AudioPolicyManagerTVTest::sExecutableDir + "test_tv_apm_configuration.xml";
+
+// SwAudioOutputDescriptor doesn't populate flags so check against the port name.
+void AudioPolicyManagerTVTest::testHDMIPortSelection(
+ audio_output_flags_t flags, const char* expectedMixPortName) {
+ ASSERT_EQ(NO_ERROR, mManager->setDeviceConnectionState(
+ AUDIO_DEVICE_OUT_AUX_DIGITAL, AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
+ "" /*address*/, "" /*name*/, AUDIO_FORMAT_DEFAULT));
+ audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
+ audio_io_handle_t output;
+ audio_port_handle_t portId;
+ getOutputForAttr(&selectedDeviceId, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO, 48000,
+ flags, &output, &portId);
+ sp<SwAudioOutputDescriptor> outDesc = mManager->getOutputs().valueFor(output);
+ ASSERT_NE(nullptr, outDesc.get());
+ audio_port port = {};
+ outDesc->toAudioPort(&port);
+ mManager->releaseOutput(portId);
+ ASSERT_EQ(NO_ERROR, mManager->setDeviceConnectionState(
+ AUDIO_DEVICE_OUT_AUX_DIGITAL, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
+ "" /*address*/, "" /*name*/, AUDIO_FORMAT_DEFAULT));
+ ASSERT_EQ(AUDIO_PORT_TYPE_MIX, port.type);
+ ASSERT_EQ(AUDIO_PORT_ROLE_SOURCE, port.role);
+ ASSERT_STREQ(expectedMixPortName, port.name);
+}
+
+TEST_F(AudioPolicyManagerTVTest, InitSuccess) {
+ // SetUp must finish with no assertions.
+}
+
+TEST_F(AudioPolicyManagerTVTest, Dump) {
+ dumpToLog();
+}
+
+TEST_F(AudioPolicyManagerTVTest, MatchNoFlags) {
+ testHDMIPortSelection(AUDIO_OUTPUT_FLAG_NONE, "primary output");
+}
+
+TEST_F(AudioPolicyManagerTVTest, MatchOutputDirectNoHwAvSync) {
+ // b/140447125: The selected port must not have HW AV Sync flag (see the config file).
+ testHDMIPortSelection(AUDIO_OUTPUT_FLAG_DIRECT, "direct");
+}
+
+TEST_F(AudioPolicyManagerTVTest, MatchOutputDirectHwAvSync) {
+ testHDMIPortSelection(static_cast<audio_output_flags_t>(
+ AUDIO_OUTPUT_FLAG_DIRECT|AUDIO_OUTPUT_FLAG_HW_AV_SYNC),
+ "tunnel");
+}
+
+TEST_F(AudioPolicyManagerTVTest, MatchOutputDirectMMapNoIrq) {
+ testHDMIPortSelection(static_cast<audio_output_flags_t>(
+ AUDIO_OUTPUT_FLAG_DIRECT|AUDIO_OUTPUT_FLAG_MMAP_NOIRQ),
+ "low latency");
+}
diff --git a/services/audiopolicy/tests/resources/Android.bp b/services/audiopolicy/tests/resources/Android.bp
index 41f5ee1..d9476d9 100644
--- a/services/audiopolicy/tests/resources/Android.bp
+++ b/services/audiopolicy/tests/resources/Android.bp
@@ -3,5 +3,6 @@
srcs: [
"test_audio_policy_configuration.xml",
"test_audio_policy_primary_only_configuration.xml",
+ "test_tv_apm_configuration.xml",
],
}
diff --git a/services/audiopolicy/tests/resources/test_tv_apm_configuration.xml b/services/audiopolicy/tests/resources/test_tv_apm_configuration.xml
new file mode 100644
index 0000000..f1638f3
--- /dev/null
+++ b/services/audiopolicy/tests/resources/test_tv_apm_configuration.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!-- Copyright (C) 2019 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.
+-->
+
+<audioPolicyConfiguration version="1.0" xmlns:xi="http://www.w3.org/2001/XInclude">
+ <globalConfiguration speaker_drc_enabled="false"/>
+ <modules>
+ <module name="primary" halVersion="2.0">
+ <attachedDevices>
+ <item>Speaker</item>
+ </attachedDevices>
+ <defaultOutputDevice>Speaker</defaultOutputDevice>
+ <mixPorts>
+ <!-- Profiles on the HDMI port are explicit for simplicity. In reality they are dynamic -->
+ <!-- Note: ports are intentionally arranged from more specific to less
+ specific in order to test b/140447125 for HW AV Sync, and similar "explicit matches" -->
+ <mixPort name="tunnel" role="source"
+ flags="AUDIO_OUTPUT_FLAG_DIRECT|AUDIO_OUTPUT_FLAG_HW_AV_SYNC">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ <mixPort name="low latency" role="source"
+ flags="AUDIO_OUTPUT_FLAG_DIRECT|AUDIO_OUTPUT_FLAG_MMAP_NOIRQ">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ <mixPort name="direct" role="source" flags="AUDIO_OUTPUT_FLAG_DIRECT">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ <mixPort name="primary output" role="source" flags="AUDIO_OUTPUT_FLAG_PRIMARY">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ </mixPorts>
+ <devicePorts>
+ <devicePort tagName="Speaker" type="AUDIO_DEVICE_OUT_SPEAKER" role="sink" />
+ <devicePort tagName="Out Aux Digital" type="AUDIO_DEVICE_OUT_AUX_DIGITAL" role="sink" />
+ </devicePorts>
+ <routes>
+ <route type="mix" sink="Speaker" sources="primary output"/>
+ <route type="mix" sink="Out Aux Digital" sources="primary output,tunnel,direct,low latency"/>
+ </routes>
+ </module>
+ </modules>
+</audioPolicyConfiguration>
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 9ba6553..a503838 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -2507,11 +2507,16 @@
return level == API_2;
}
-int32_t CameraService::BasicClient::setAudioRestriction(int32_t mode) {
+status_t CameraService::BasicClient::setAudioRestriction(int32_t mode) {
{
Mutex::Autolock l(mAudioRestrictionLock);
mAudioRestriction = mode;
}
+ sCameraService->updateAudioRestriction();
+ return OK;
+}
+
+int32_t CameraService::BasicClient::getServiceAudioRestriction() const {
return sCameraService->updateAudioRestriction();
}
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index df8c17c..7c77e16 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -261,8 +261,13 @@
// set audio restriction from client
// Will call into camera service and hold mServiceLock
- virtual int32_t setAudioRestriction(int32_t mode);
+ virtual status_t setAudioRestriction(int32_t mode);
+ // Get current global audio restriction setting
+ // Will call into camera service and hold mServiceLock
+ virtual int32_t getServiceAudioRestriction() const;
+
+ // Get current audio restriction setting for this client
virtual int32_t getAudioRestriction() const;
static bool isValidAudioRestriction(int32_t mode);
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index e996e83..c273881 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -2258,12 +2258,18 @@
return OK;
}
-int32_t Camera2Client::setAudioRestriction(int /*mode*/) {
+status_t Camera2Client::setAudioRestriction(int /*mode*/) {
// Empty implementation. setAudioRestriction is hidden interface and not
// supported by android.hardware.Camera API
return INVALID_OPERATION;
}
+int32_t Camera2Client::getGlobalAudioRestriction() {
+ // Empty implementation. getAudioRestriction is hidden interface and not
+ // supported by android.hardware.Camera API
+ return INVALID_OPERATION;
+}
+
status_t Camera2Client::waitUntilCurrentRequestIdLocked() {
int32_t activeRequestId = mStreamingProcessor->getActiveRequestId();
if (activeRequestId != 0) {
diff --git a/services/camera/libcameraservice/api1/Camera2Client.h b/services/camera/libcameraservice/api1/Camera2Client.h
index e79a442..8a17b17 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.h
+++ b/services/camera/libcameraservice/api1/Camera2Client.h
@@ -83,7 +83,8 @@
virtual void notifyError(int32_t errorCode,
const CaptureResultExtras& resultExtras);
virtual status_t setVideoTarget(const sp<IGraphicBufferProducer>& bufferProducer);
- virtual int32_t setAudioRestriction(int mode);
+ virtual status_t setAudioRestriction(int mode);
+ virtual int32_t getGlobalAudioRestriction();
/**
* Interface used by CameraService
diff --git a/services/camera/libcameraservice/api1/CameraClient.cpp b/services/camera/libcameraservice/api1/CameraClient.cpp
index 089f6cf..388a5dc 100644
--- a/services/camera/libcameraservice/api1/CameraClient.cpp
+++ b/services/camera/libcameraservice/api1/CameraClient.cpp
@@ -534,7 +534,7 @@
}
if (mHardware != nullptr) {
- VideoNativeHandleMetadata *metadata = (VideoNativeHandleMetadata*)(dataPtr->pointer());
+ VideoNativeHandleMetadata *metadata = (VideoNativeHandleMetadata*)(dataPtr->unsecurePointer());
metadata->eType = kMetadataBufferTypeNativeHandleSource;
metadata->pHandle = handle;
mHardware->releaseRecordingFrame(dataPtr);
@@ -573,7 +573,7 @@
}
if (!disconnected) {
- VideoNativeHandleMetadata *metadata = (VideoNativeHandleMetadata*)(dataPtr->pointer());
+ VideoNativeHandleMetadata *metadata = (VideoNativeHandleMetadata*)(dataPtr->unsecurePointer());
metadata->eType = kMetadataBufferTypeNativeHandleSource;
metadata->pHandle = handle;
frames.push_back(dataPtr);
@@ -916,8 +916,12 @@
ALOGE("%s: dataPtr does not contain VideoNativeHandleMetadata!", __FUNCTION__);
return;
}
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
VideoNativeHandleMetadata *metadata =
- (VideoNativeHandleMetadata*)(msg.dataPtr->pointer());
+ (VideoNativeHandleMetadata*)(msg.dataPtr->unsecurePointer());
if (metadata->eType == kMetadataBufferTypeNativeHandleSource) {
handle = metadata->pHandle;
}
@@ -1073,8 +1077,12 @@
// Check if dataPtr contains a VideoNativeHandleMetadata.
if (dataPtr->size() == sizeof(VideoNativeHandleMetadata)) {
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
VideoNativeHandleMetadata *metadata =
- (VideoNativeHandleMetadata*)(dataPtr->pointer());
+ (VideoNativeHandleMetadata*)(dataPtr->unsecurePointer());
if (metadata->eType == kMetadataBufferTypeNativeHandleSource) {
handle = metadata->pHandle;
}
@@ -1171,7 +1179,7 @@
return INVALID_OPERATION;
}
-int32_t CameraClient::setAudioRestriction(int mode) {
+status_t CameraClient::setAudioRestriction(int mode) {
if (!isValidAudioRestriction(mode)) {
ALOGE("%s: invalid audio restriction mode %d", __FUNCTION__, mode);
return BAD_VALUE;
@@ -1184,5 +1192,12 @@
return BasicClient::setAudioRestriction(mode);
}
+int32_t CameraClient::getGlobalAudioRestriction() {
+ Mutex::Autolock lock(mLock);
+ if (checkPidAndHardware() != NO_ERROR) {
+ return INVALID_OPERATION;
+ }
+ return BasicClient::getServiceAudioRestriction();
+}
}; // namespace android
diff --git a/services/camera/libcameraservice/api1/CameraClient.h b/services/camera/libcameraservice/api1/CameraClient.h
index fefa8c9..b26b612 100644
--- a/services/camera/libcameraservice/api1/CameraClient.h
+++ b/services/camera/libcameraservice/api1/CameraClient.h
@@ -59,7 +59,8 @@
virtual String8 getParameters() const;
virtual status_t sendCommand(int32_t cmd, int32_t arg1, int32_t arg2);
virtual status_t setVideoTarget(const sp<IGraphicBufferProducer>& bufferProducer);
- virtual int32_t setAudioRestriction(int mode);
+ virtual status_t setAudioRestriction(int mode);
+ virtual int32_t getGlobalAudioRestriction();
// Interface used by CameraService
CameraClient(const sp<CameraService>& cameraService,
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index be188bc..d93d26f 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -1870,8 +1870,7 @@
return res;
}
-binder::Status CameraDeviceClient::setCameraAudioRestriction(int32_t mode,
- /*out*/ int32_t* outMode) {
+binder::Status CameraDeviceClient::setCameraAudioRestriction(int32_t mode) {
ATRACE_CALL();
binder::Status res;
if (!(res = checkPidStatus(__FUNCTION__)).isOk()) return res;
@@ -1884,8 +1883,17 @@
}
Mutex::Autolock icl(mBinderSerializationLock);
+ BasicClient::setAudioRestriction(mode);
+ return binder::Status::ok();
+}
+
+binder::Status CameraDeviceClient::getGlobalAudioRestriction(/*out*/ int32_t* outMode) {
+ ATRACE_CALL();
+ binder::Status res;
+ if (!(res = checkPidStatus(__FUNCTION__)).isOk()) return res;
+ Mutex::Autolock icl(mBinderSerializationLock);
if (outMode != nullptr) {
- *outMode = BasicClient::setAudioRestriction(mode);
+ *outMode = BasicClient::getServiceAudioRestriction();
}
return binder::Status::ok();
}
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index a9aa190..fe25010 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -152,9 +152,9 @@
virtual binder::Status finalizeOutputConfigurations(int32_t streamId,
const hardware::camera2::params::OutputConfiguration &outputConfiguration) override;
- virtual binder::Status setCameraAudioRestriction(int32_t mode,
- /*out*/
- int32_t* outMode = NULL) override;
+ virtual binder::Status setCameraAudioRestriction(int32_t mode) override;
+
+ virtual binder::Status getGlobalAudioRestriction(/*out*/int32_t* outMode) override;
/**
* Interface used by CameraService
diff --git a/services/camera/libcameraservice/api2/HeicCompositeStream.cpp b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
index 5a87134..410fd2c 100644
--- a/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
+++ b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
@@ -1210,7 +1210,7 @@
outputFormat->setInt32(KEY_I_FRAME_INTERVAL, 0);
outputFormat->setInt32(KEY_COLOR_FORMAT,
useGrid ? COLOR_FormatYUV420Flexible : COLOR_FormatSurface);
- outputFormat->setInt32(KEY_FRAME_RATE, gridRows * gridCols);
+ outputFormat->setInt32(KEY_FRAME_RATE, useGrid ? gridRows * gridCols : kNoGridOpRate);
// This only serves as a hint to encoder when encoding is not real-time.
outputFormat->setInt32(KEY_OPERATING_RATE, useGrid ? kGridOpRate : kNoGridOpRate);
@@ -1671,8 +1671,13 @@
ALOGE("CB_OUTPUT_FORMAT_CHANGED: format is expected.");
break;
}
-
- parent->onHeicFormatChanged(format);
+ // Here format is MediaCodec's internal copy of output format.
+ // Make a copy since onHeicFormatChanged() might modify it.
+ sp<AMessage> formatCopy;
+ if (format != nullptr) {
+ formatCopy = format->dup();
+ }
+ parent->onHeicFormatChanged(formatCopy);
break;
}
diff --git a/services/camera/libcameraservice/device1/CameraHardwareInterface.cpp b/services/camera/libcameraservice/device1/CameraHardwareInterface.cpp
index 522d521..62ef681 100644
--- a/services/camera/libcameraservice/device1/CameraHardwareInterface.cpp
+++ b/services/camera/libcameraservice/device1/CameraHardwareInterface.cpp
@@ -165,8 +165,12 @@
mem = mHidlMemPoolMap.at(data);
}
sp<CameraHeapMemory> heapMem(static_cast<CameraHeapMemory *>(mem->handle));
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
VideoNativeHandleMetadata* md = (VideoNativeHandleMetadata*)
- heapMem->mBuffers[bufferIndex]->pointer();
+ heapMem->mBuffers[bufferIndex]->unsecurePointer();
md->pHandle = const_cast<native_handle_t*>(frameData.getNativeHandle());
sDataCbTimestamp(timestamp, (int32_t) msgType, mem, bufferIndex, this);
return hardware::Void();
@@ -192,8 +196,12 @@
hidl_msg.bufferIndex, mem->mNumBufs);
return hardware::Void();
}
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
VideoNativeHandleMetadata* md = (VideoNativeHandleMetadata*)
- mem->mBuffers[hidl_msg.bufferIndex]->pointer();
+ mem->mBuffers[hidl_msg.bufferIndex]->unsecurePointer();
md->pHandle = const_cast<native_handle_t*>(hidl_msg.frameData.getNativeHandle());
msgs.push_back({hidl_msg.timestamp, mem->mBuffers[hidl_msg.bufferIndex]});
@@ -578,7 +586,11 @@
int bufferIndex = offset / size;
if (CC_LIKELY(mHidlDevice != nullptr)) {
if (size == sizeof(VideoNativeHandleMetadata)) {
- VideoNativeHandleMetadata* md = (VideoNativeHandleMetadata*) mem->pointer();
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ VideoNativeHandleMetadata* md = (VideoNativeHandleMetadata*) mem->unsecurePointer();
// Caching the handle here because md->pHandle will be subject to HAL's edit
native_handle_t* nh = md->pHandle;
hidl_handle frame = nh;
@@ -605,7 +617,11 @@
if (size == sizeof(VideoNativeHandleMetadata)) {
uint32_t heapId = heap->getHeapID();
uint32_t bufferIndex = offset / size;
- VideoNativeHandleMetadata* md = (VideoNativeHandleMetadata*) mem->pointer();
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
+ VideoNativeHandleMetadata* md = (VideoNativeHandleMetadata*) mem->unsecurePointer();
// Caching the handle here because md->pHandle will be subject to HAL's edit
native_handle_t* nh = md->pHandle;
VideoFrameMessage msg;
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index dd5a62b..3188892 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -2802,6 +2802,27 @@
mOperatingMode = operatingMode;
}
+ // In case called from configureStreams, abort queued input buffers not belonging to
+ // any pending requests.
+ if (mInputStream != NULL && notifyRequestThread) {
+ while (true) {
+ camera3_stream_buffer_t inputBuffer;
+ status_t res = mInputStream->getInputBuffer(&inputBuffer,
+ /*respectHalLimit*/ false);
+ if (res != OK) {
+ // Exhausted acquiring all input buffers.
+ break;
+ }
+
+ inputBuffer.status = CAMERA3_BUFFER_STATUS_ERROR;
+ res = mInputStream->returnInputBuffer(inputBuffer);
+ if (res != OK) {
+ ALOGE("%s: %d: couldn't return input buffer while clearing input queue: "
+ "%s (%d)", __FUNCTION__, __LINE__, strerror(-res), res);
+ }
+ }
+ }
+
if (!mNeedConfig) {
ALOGV("%s: Skipping config, no stream changes", __FUNCTION__);
return OK;
@@ -5084,6 +5105,7 @@
ALOGW("%s: %d: couldn't get input buffer while clearing the request "
"list: %s (%d)", __FUNCTION__, __LINE__, strerror(-res), res);
} else {
+ inputBuffer.status = CAMERA3_BUFFER_STATUS_ERROR;
res = (*it)->mInputStream->returnInputBuffer(inputBuffer);
if (res != OK) {
ALOGE("%s: %d: couldn't return input buffer while clearing the request "
diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.cpp b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
index fc83684..cb59a76 100644
--- a/services/camera/libcameraservice/device3/Camera3InputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
@@ -71,7 +71,8 @@
res = mConsumer->acquireBuffer(&bufferItem, /*waitForFence*/false);
if (res != OK) {
- ALOGE("%s: Stream %d: Can't acquire next output buffer: %s (%d)",
+ // This may or may not be an error condition depending on caller.
+ ALOGV("%s: Stream %d: Can't acquire next output buffer: %s (%d)",
__FUNCTION__, mId, strerror(-res), res);
return res;
}
diff --git a/services/soundtrigger/SoundTriggerHwService.cpp b/services/soundtrigger/SoundTriggerHwService.cpp
index 377d30b..69e5f50 100644
--- a/services/soundtrigger/SoundTriggerHwService.cpp
+++ b/services/soundtrigger/SoundTriggerHwService.cpp
@@ -234,11 +234,11 @@
size_t size = event->data_offset + event->data_size;
eventMemory = mMemoryDealer->allocate(size);
- if (eventMemory == 0 || eventMemory->pointer() == NULL) {
+ if (eventMemory == 0 || eventMemory->unsecurePointer() == NULL) {
eventMemory.clear();
return eventMemory;
}
- memcpy(eventMemory->pointer(), event, size);
+ memcpy(eventMemory->unsecurePointer(), event, size);
return eventMemory;
}
@@ -283,11 +283,11 @@
size_t size = event->data_offset + event->data_size;
eventMemory = mMemoryDealer->allocate(size);
- if (eventMemory == 0 || eventMemory->pointer() == NULL) {
+ if (eventMemory == 0 || eventMemory->unsecurePointer() == NULL) {
eventMemory.clear();
return eventMemory;
}
- memcpy(eventMemory->pointer(), event, size);
+ memcpy(eventMemory->unsecurePointer(), event, size);
return eventMemory;
}
@@ -313,11 +313,11 @@
size_t size = sizeof(sound_trigger_service_state_t);
eventMemory = mMemoryDealer->allocate(size);
- if (eventMemory == 0 || eventMemory->pointer() == NULL) {
+ if (eventMemory == 0 || eventMemory->unsecurePointer() == NULL) {
eventMemory.clear();
return eventMemory;
}
- *((sound_trigger_service_state_t *)eventMemory->pointer()) = state;
+ *((sound_trigger_service_state_t *)eventMemory->unsecurePointer()) = state;
return eventMemory;
}
@@ -557,8 +557,12 @@
return NO_INIT;
}
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
struct sound_trigger_sound_model *sound_model =
- (struct sound_trigger_sound_model *)modelMemory->pointer();
+ (struct sound_trigger_sound_model *)modelMemory->unsecurePointer();
size_t structSize;
if (sound_model->type == SOUND_MODEL_TYPE_KEYPHRASE) {
@@ -651,8 +655,12 @@
return NO_INIT;
}
+ // TODO: Using unsecurePointer() has some associated security pitfalls
+ // (see declaration for details).
+ // Either document why it is safe in this case or address the
+ // issue (e.g. by copying).
struct sound_trigger_recognition_config *config =
- (struct sound_trigger_recognition_config *)dataMemory->pointer();
+ (struct sound_trigger_recognition_config *)dataMemory->unsecurePointer();
if (config->data_offset < sizeof(struct sound_trigger_recognition_config) ||
config->data_size > (UINT_MAX - config->data_offset) ||
@@ -734,9 +742,10 @@
{
ALOGV("onCallbackEvent type %d", event->mType);
+ // Memory is coming from a trusted process.
sp<IMemory> eventMemory = event->mMemory;
- if (eventMemory == 0 || eventMemory->pointer() == NULL) {
+ if (eventMemory == 0 || eventMemory->unsecurePointer() == NULL) {
return;
}
if (mModuleClients.isEmpty()) {
@@ -749,7 +758,7 @@
switch (event->mType) {
case CallbackEvent::TYPE_RECOGNITION: {
struct sound_trigger_recognition_event *recognitionEvent =
- (struct sound_trigger_recognition_event *)eventMemory->pointer();
+ (struct sound_trigger_recognition_event *)eventMemory->unsecurePointer();
{
AutoMutex lock(mLock);
sp<Model> model = getModel(recognitionEvent->model);
@@ -769,7 +778,7 @@
} break;
case CallbackEvent::TYPE_SOUNDMODEL: {
struct sound_trigger_model_event *soundmodelEvent =
- (struct sound_trigger_model_event *)eventMemory->pointer();
+ (struct sound_trigger_model_event *)eventMemory->unsecurePointer();
{
AutoMutex lock(mLock);
sp<Model> model = getModel(soundmodelEvent->model);
@@ -1082,7 +1091,8 @@
sp<IMemory> eventMemory = event->mMemory;
- if (eventMemory == 0 || eventMemory->pointer() == NULL) {
+ // Memory is coming from a trusted process.
+ if (eventMemory == 0 || eventMemory->unsecurePointer() == NULL) {
return;
}
diff --git a/soundtrigger/SoundTrigger.cpp b/soundtrigger/SoundTrigger.cpp
index 9708ea7..e297ee7 100644
--- a/soundtrigger/SoundTrigger.cpp
+++ b/soundtrigger/SoundTrigger.cpp
@@ -204,39 +204,42 @@
void SoundTrigger::onRecognitionEvent(const sp<IMemory>& eventMemory)
{
Mutex::Autolock _l(mLock);
- if (eventMemory == 0 || eventMemory->pointer() == NULL) {
+ if (eventMemory == 0 || eventMemory->unsecurePointer() == NULL) {
return;
}
if (mCallback != 0) {
+ // Memory is coming from a trusted process.
mCallback->onRecognitionEvent(
- (struct sound_trigger_recognition_event *)eventMemory->pointer());
+ (struct sound_trigger_recognition_event *)eventMemory->unsecurePointer());
}
}
void SoundTrigger::onSoundModelEvent(const sp<IMemory>& eventMemory)
{
Mutex::Autolock _l(mLock);
- if (eventMemory == 0 || eventMemory->pointer() == NULL) {
+ if (eventMemory == 0 || eventMemory->unsecurePointer() == NULL) {
return;
}
if (mCallback != 0) {
+ // Memory is coming from a trusted process.
mCallback->onSoundModelEvent(
- (struct sound_trigger_model_event *)eventMemory->pointer());
+ (struct sound_trigger_model_event *)eventMemory->unsecurePointer());
}
}
void SoundTrigger::onServiceStateChange(const sp<IMemory>& eventMemory)
{
Mutex::Autolock _l(mLock);
- if (eventMemory == 0 || eventMemory->pointer() == NULL) {
+ if (eventMemory == 0 || eventMemory->unsecurePointer() == NULL) {
return;
}
if (mCallback != 0) {
+ // Memory is coming from a trusted process.
mCallback->onServiceStateChange(
- *((sound_trigger_service_state_t *)eventMemory->pointer()));
+ *((sound_trigger_service_state_t *)eventMemory->unsecurePointer()));
}
}