Merge "codec2: Fix linear gralloc blobs" into rvc-dev
diff --git a/camera/ICameraClient.cpp b/camera/ICameraClient.cpp
index 8620f36..487b8b0 100644
--- a/camera/ICameraClient.cpp
+++ b/camera/ICameraClient.cpp
@@ -143,6 +143,11 @@
if (data.dataAvail() > 0) {
metadata = new camera_frame_metadata_t;
metadata->number_of_faces = data.readInt32();
+ if (metadata->number_of_faces <= 0 ||
+ metadata->number_of_faces > (int32_t)(INT32_MAX / sizeof(camera_face_t))) {
+ ALOGE("%s: Too large face count: %d", __FUNCTION__, metadata->number_of_faces);
+ return BAD_VALUE;
+ }
metadata->faces = (camera_face_t *) data.readInplace(
sizeof(camera_face_t) * metadata->number_of_faces);
}
diff --git a/camera/ndk/impl/ACameraManager.cpp b/camera/ndk/impl/ACameraManager.cpp
index f408b6a..b58ebe2 100644
--- a/camera/ndk/impl/ACameraManager.cpp
+++ b/camera/ndk/impl/ACameraManager.cpp
@@ -35,6 +35,7 @@
const char* CameraManagerGlobal::kPhysicalCameraIdKey = "PhysicalCameraId";
const char* CameraManagerGlobal::kCallbackFpKey = "CallbackFp";
const char* CameraManagerGlobal::kContextKey = "CallbackContext";
+const nsecs_t CameraManagerGlobal::kCallbackDrainTimeout = 5000000; // 5 ms
Mutex CameraManagerGlobal::sLock;
CameraManagerGlobal* CameraManagerGlobal::sInstance = nullptr;
@@ -117,7 +118,7 @@
return nullptr;
}
if (mHandler == nullptr) {
- mHandler = new CallbackHandler();
+ mHandler = new CallbackHandler(this);
}
mCbLooper->registerHandler(mHandler);
}
@@ -211,6 +212,9 @@
void CameraManagerGlobal::unregisterExtendedAvailabilityCallback(
const ACameraManager_ExtendedAvailabilityCallbacks *callback) {
Mutex::Autolock _l(mLock);
+
+ drainPendingCallbacksLocked();
+
Callback cb(callback);
mCallbacks.erase(cb);
}
@@ -223,10 +227,32 @@
void CameraManagerGlobal::unregisterAvailabilityCallback(
const ACameraManager_AvailabilityCallbacks *callback) {
Mutex::Autolock _l(mLock);
+
+ drainPendingCallbacksLocked();
+
Callback cb(callback);
mCallbacks.erase(cb);
}
+void CameraManagerGlobal::onCallbackCalled() {
+ Mutex::Autolock _l(mLock);
+ if (mPendingCallbackCnt > 0) {
+ mPendingCallbackCnt--;
+ }
+ mCallbacksCond.signal();
+}
+
+void CameraManagerGlobal::drainPendingCallbacksLocked() {
+ while (mPendingCallbackCnt > 0) {
+ auto res = mCallbacksCond.waitRelative(mLock, kCallbackDrainTimeout);
+ if (res != NO_ERROR) {
+ ALOGE("%s: Error waiting to drain callbacks: %s(%d)",
+ __FUNCTION__, strerror(-res), res);
+ break;
+ }
+ }
+}
+
template<class T>
void CameraManagerGlobal::registerAvailCallback(const T *callback) {
Mutex::Autolock _l(mLock);
@@ -250,6 +276,7 @@
msg->setPointer(kCallbackFpKey, (void *) cbFunc);
msg->setPointer(kContextKey, cb.mContext);
msg->setString(kCameraIdKey, AString(cameraId));
+ mPendingCallbackCnt++;
msg->post();
// Physical camera unavailable callback
@@ -263,6 +290,7 @@
msg->setPointer(kContextKey, cb.mContext);
msg->setString(kCameraIdKey, AString(cameraId));
msg->setString(kPhysicalCameraIdKey, AString(physicalCameraId));
+ mPendingCallbackCnt++;
msg->post();
}
}
@@ -323,6 +351,16 @@
}
void CameraManagerGlobal::CallbackHandler::onMessageReceived(
+ const sp<AMessage> &msg) {
+ onMessageReceivedInternal(msg);
+ if (msg->what() == kWhatSendSingleCallback ||
+ msg->what() == kWhatSendSingleAccessCallback ||
+ msg->what() == kWhatSendSinglePhysicalCameraCallback) {
+ notifyParent();
+ }
+}
+
+void CameraManagerGlobal::CallbackHandler::onMessageReceivedInternal(
const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatSendSingleCallback:
@@ -405,6 +443,13 @@
}
}
+void CameraManagerGlobal::CallbackHandler::notifyParent() {
+ sp<CameraManagerGlobal> parent = mParent.promote();
+ if (parent != nullptr) {
+ parent->onCallbackCalled();
+ }
+}
+
binder::Status CameraManagerGlobal::CameraServiceListener::onCameraAccessPrioritiesChanged() {
sp<CameraManagerGlobal> cm = mCameraManager.promote();
if (cm != nullptr) {
@@ -445,6 +490,7 @@
if (cbFp != nullptr) {
msg->setPointer(kCallbackFpKey, (void *) cbFp);
msg->setPointer(kContextKey, cb.mContext);
+ mPendingCallbackCnt++;
msg->post();
}
}
@@ -491,6 +537,7 @@
msg->setPointer(kCallbackFpKey, (void *) cbFp);
msg->setPointer(kContextKey, cb.mContext);
msg->setString(kCameraIdKey, AString(cameraId));
+ mPendingCallbackCnt++;
msg->post();
}
}
@@ -545,6 +592,7 @@
msg->setPointer(kContextKey, cb.mContext);
msg->setString(kCameraIdKey, AString(cameraId));
msg->setString(kPhysicalCameraIdKey, AString(physicalCameraId));
+ mPendingCallbackCnt++;
msg->post();
}
}
diff --git a/camera/ndk/impl/ACameraManager.h b/camera/ndk/impl/ACameraManager.h
index 7fba188..ccbfaa9 100644
--- a/camera/ndk/impl/ACameraManager.h
+++ b/camera/ndk/impl/ACameraManager.h
@@ -163,6 +163,12 @@
ACameraManager_PhysicalCameraAvailabilityCallback mPhysicalCamUnavailable;
void* mContext;
};
+
+ android::Condition mCallbacksCond;
+ size_t mPendingCallbackCnt = 0;
+ void onCallbackCalled();
+ void drainPendingCallbacksLocked();
+
std::set<Callback> mCallbacks;
// definition of handler and message
@@ -175,10 +181,16 @@
static const char* kPhysicalCameraIdKey;
static const char* kCallbackFpKey;
static const char* kContextKey;
+ static const nsecs_t kCallbackDrainTimeout;
class CallbackHandler : public AHandler {
public:
- CallbackHandler() {}
+ CallbackHandler(wp<CameraManagerGlobal> parent) : mParent(parent) {}
void onMessageReceived(const sp<AMessage> &msg) override;
+
+ private:
+ wp<CameraManagerGlobal> mParent;
+ void notifyParent();
+ void onMessageReceivedInternal(const sp<AMessage> &msg);
};
sp<CallbackHandler> mHandler;
sp<ALooper> mCbLooper; // Looper thread where callbacks actually happen on
diff --git a/camera/ndk/include/camera/NdkCameraManager.h b/camera/ndk/include/camera/NdkCameraManager.h
index e2b71bf..0a2ee57 100644
--- a/camera/ndk/include/camera/NdkCameraManager.h
+++ b/camera/ndk/include/camera/NdkCameraManager.h
@@ -191,6 +191,9 @@
*
* <p>Removing a callback that isn't registered has no effect.</p>
*
+ * <p>This function must not be called with a mutex lock also held by
+ * the availability callbacks.</p>
+ *
* @param manager the {@link ACameraManager} of interest.
* @param callback the {@link ACameraManager_AvailabilityCallbacks} to be unregistered.
*
@@ -382,6 +385,9 @@
*
* <p>Removing a callback that isn't registered has no effect.</p>
*
+ * <p>This function must not be called with a mutex lock also held by
+ * the extended availability callbacks.</p>
+ *
* @param manager the {@link ACameraManager} of interest.
* @param callback the {@link ACameraManager_ExtendedAvailabilityCallbacks} to be unregistered.
*
diff --git a/camera/ndk/ndk_vendor/impl/ACameraManager.cpp b/camera/ndk/ndk_vendor/impl/ACameraManager.cpp
index e2097b5..5aa9c46 100644
--- a/camera/ndk/ndk_vendor/impl/ACameraManager.cpp
+++ b/camera/ndk/ndk_vendor/impl/ACameraManager.cpp
@@ -45,6 +45,7 @@
const char* CameraManagerGlobal::kPhysicalCameraIdKey = "PhysicalCameraId";
const char* CameraManagerGlobal::kCallbackFpKey = "CallbackFp";
const char* CameraManagerGlobal::kContextKey = "CallbackContext";
+const nsecs_t CameraManagerGlobal::kCallbackDrainTimeout = 5000000; // 5 ms
Mutex CameraManagerGlobal::sLock;
CameraManagerGlobal* CameraManagerGlobal::sInstance = nullptr;
@@ -249,7 +250,7 @@
return nullptr;
}
if (mHandler == nullptr) {
- mHandler = new CallbackHandler();
+ mHandler = new CallbackHandler(this);
}
mCbLooper->registerHandler(mHandler);
}
@@ -317,6 +318,7 @@
void CameraManagerGlobal::unregisterAvailabilityCallback(
const ACameraManager_AvailabilityCallbacks *callback) {
Mutex::Autolock _l(mLock);
+ drainPendingCallbacksLocked();
Callback cb(callback);
mCallbacks.erase(cb);
}
@@ -329,10 +331,30 @@
void CameraManagerGlobal::unregisterExtendedAvailabilityCallback(
const ACameraManager_ExtendedAvailabilityCallbacks *callback) {
Mutex::Autolock _l(mLock);
+ drainPendingCallbacksLocked();
Callback cb(callback);
mCallbacks.erase(cb);
}
+void CameraManagerGlobal::onCallbackCalled() {
+ Mutex::Autolock _l(mLock);
+ if (mPendingCallbackCnt > 0) {
+ mPendingCallbackCnt--;
+ }
+ mCallbacksCond.signal();
+}
+
+void CameraManagerGlobal::drainPendingCallbacksLocked() {
+ while (mPendingCallbackCnt > 0) {
+ auto res = mCallbacksCond.waitRelative(mLock, kCallbackDrainTimeout);
+ if (res != NO_ERROR) {
+ ALOGE("%s: Error waiting to drain callbacks: %s(%d)",
+ __FUNCTION__, strerror(-res), res);
+ break;
+ }
+ }
+}
+
template <class T>
void CameraManagerGlobal::registerAvailCallback(const T *callback) {
Mutex::Autolock _l(mLock);
@@ -351,6 +373,7 @@
msg->setPointer(kCallbackFpKey, (void *) cbFunc);
msg->setPointer(kContextKey, cb.mContext);
msg->setString(kCameraIdKey, AString(cameraId.c_str()));
+ mPendingCallbackCnt++;
msg->post();
// Physical camera unavailable callback
@@ -363,6 +386,7 @@
msg->setPointer(kContextKey, cb.mContext);
msg->setString(kCameraIdKey, AString(cameraId.c_str()));
msg->setString(kPhysicalCameraIdKey, AString(physicalCameraId.c_str()));
+ mPendingCallbackCnt++;
msg->post();
}
}
@@ -406,6 +430,15 @@
}
void CameraManagerGlobal::CallbackHandler::onMessageReceived(
+ const sp<AMessage> &msg) {
+ onMessageReceivedInternal(msg);
+ if (msg->what() == kWhatSendSingleCallback ||
+ msg->what() == kWhatSendSinglePhysicalCameraCallback) {
+ notifyParent();
+ }
+}
+
+void CameraManagerGlobal::CallbackHandler::onMessageReceivedInternal(
const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatSendSingleCallback:
@@ -466,6 +499,13 @@
}
}
+void CameraManagerGlobal::CallbackHandler::notifyParent() {
+ sp<CameraManagerGlobal> parent = mParent.promote();
+ if (parent != nullptr) {
+ parent->onCallbackCalled();
+ }
+}
+
hardware::Return<void> CameraManagerGlobal::CameraServiceListener::onStatusChanged(
const CameraStatusAndId &statusAndId) {
sp<CameraManagerGlobal> cm = mCameraManager.promote();
@@ -512,6 +552,7 @@
msg->setPointer(kCallbackFpKey, (void *) cbFp);
msg->setPointer(kContextKey, cb.mContext);
msg->setString(kCameraIdKey, AString(cameraId.c_str()));
+ mPendingCallbackCnt++;
msg->post();
}
if (status == CameraDeviceStatus::STATUS_NOT_PRESENT) {
@@ -577,6 +618,7 @@
msg->setPointer(kContextKey, cb.mContext);
msg->setString(kCameraIdKey, AString(cameraId.c_str()));
msg->setString(kPhysicalCameraIdKey, AString(physicalCameraId.c_str()));
+ mPendingCallbackCnt++;
msg->post();
}
}
diff --git a/camera/ndk/ndk_vendor/impl/ACameraManager.h b/camera/ndk/ndk_vendor/impl/ACameraManager.h
index 36c8e2b..85da3e9 100644
--- a/camera/ndk/ndk_vendor/impl/ACameraManager.h
+++ b/camera/ndk/ndk_vendor/impl/ACameraManager.h
@@ -156,6 +156,12 @@
ACameraManager_PhysicalCameraAvailabilityCallback mPhysicalCamUnavailable;
void* mContext;
};
+
+ android::Condition mCallbacksCond;
+ size_t mPendingCallbackCnt = 0;
+ void onCallbackCalled();
+ void drainPendingCallbacksLocked();
+
std::set<Callback> mCallbacks;
// definition of handler and message
@@ -167,10 +173,15 @@
static const char* kPhysicalCameraIdKey;
static const char* kCallbackFpKey;
static const char* kContextKey;
+ static const nsecs_t kCallbackDrainTimeout;
class CallbackHandler : public AHandler {
public:
- CallbackHandler() {}
+ CallbackHandler(wp<CameraManagerGlobal> parent) : mParent(parent) {}
void onMessageReceived(const sp<AMessage> &msg) override;
+ private:
+ wp<CameraManagerGlobal> mParent;
+ void notifyParent();
+ void onMessageReceivedInternal(const sp<AMessage> &msg);
};
sp<CallbackHandler> mHandler;
sp<ALooper> mCbLooper; // Looper thread where callbacks actually happen on
diff --git a/drm/libmediadrm/DrmUtils.cpp b/drm/libmediadrm/DrmUtils.cpp
index 51c2e24..d85fa61 100644
--- a/drm/libmediadrm/DrmUtils.cpp
+++ b/drm/libmediadrm/DrmUtils.cpp
@@ -27,7 +27,7 @@
#include <android/hardware/drm/1.2/IDrmFactory.h>
#include <android/hardware/drm/1.3/ICryptoFactory.h>
#include <android/hardware/drm/1.3/IDrmFactory.h>
-#include <android/hidl/manager/1.0/IServiceManager.h>
+#include <android/hidl/manager/1.2/IServiceManager.h>
#include <hidl/HidlSupport.h>
#include <utils/Errors.h>
@@ -41,7 +41,7 @@
#include <mediadrm/ICrypto.h>
#include <mediadrm/IDrm.h>
-using HServiceManager = ::android::hidl::manager::V1_0::IServiceManager;
+using HServiceManager = ::android::hidl::manager::V1_2::IServiceManager;
using ::android::hardware::hidl_array;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
@@ -72,7 +72,7 @@
exit(-1);
}
- serviceManager->listByInterface(Hal::descriptor, [&](const hidl_vec<hidl_string> ®istered) {
+ serviceManager->listManifestByInterface(Hal::descriptor, [&](const hidl_vec<hidl_string> ®istered) {
for (const auto &instance : registered) {
auto factory = Hal::getService(instance);
if (factory != nullptr) {
diff --git a/drm/mediadrm/plugins/clearkey/hidl/DrmFactory.cpp b/drm/mediadrm/plugins/clearkey/hidl/DrmFactory.cpp
index ccc73b6..1ce8269 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/DrmFactory.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/DrmFactory.cpp
@@ -91,6 +91,19 @@
return Void();
}
+Return<void> DrmFactory::debug(const hidl_handle& fd, const hidl_vec<hidl_string>& /*args*/) {
+ if (fd.getNativeHandle() == nullptr || fd->numFds < 1) {
+ ALOGE("%s: missing fd for writing", __FUNCTION__);
+ return Void();
+ }
+
+ FILE* out = fdopen(dup(fd->data[0]), "w");
+ uint32_t currentSessions = SessionLibrary::get()->numOpenSessions();
+ fprintf(out, "current open sessions: %u\n", currentSessions);
+ fclose(out);
+ return Void();
+}
+
} // namespace clearkey
} // namespace V1_3
} // namespace drm
diff --git a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
index 8b7f57d..f87f830 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
@@ -139,8 +139,13 @@
std::vector<uint8_t> sessionId = session->sessionId();
Status status = setSecurityLevel(sessionId, securityLevel);
+ if (status == Status::OK) {
+ mOpenSessionOkCount++;
+ } else {
+ mSessionLibrary->destroySession(session);
+ sessionId.clear();
+ }
_hidl_cb(status, toHidlVec(sessionId));
- mOpenSessionOkCount++;
return Void();
}
@@ -151,12 +156,12 @@
sp<Session> session = mSessionLibrary->findSession(toVector(sessionId));
if (session.get()) {
+ mSessionLibrary->destroySession(session);
if (session->getMockError() != Status_V1_2::OK) {
sendSessionLostState(sessionId);
return Status::ERROR_DRM_INVALID_STATE;
}
mCloseSessionOkCount++;
- mSessionLibrary->destroySession(session);
return Status::OK;
}
mCloseSessionNotOpenedCount++;
@@ -387,6 +392,7 @@
if (isOfflineLicense) {
if (isRelease) {
mFileHandle.DeleteLicense(keySetId);
+ mSessionLibrary->destroySession(session);
} else {
if (!makeKeySetId(&keySetId)) {
_hidl_cb(Status::ERROR_DRM_UNKNOWN, hidl_vec<uint8_t>());
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/DrmFactory.h b/drm/mediadrm/plugins/clearkey/hidl/include/DrmFactory.h
index 403a8ec..63234cf 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/DrmFactory.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/DrmFactory.h
@@ -30,6 +30,7 @@
using ::android::hardware::drm::V1_1::SecurityLevel;
using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_handle;
using ::android::hardware::hidl_string;
using ::android::hardware::Return;
@@ -55,6 +56,8 @@
Return<void> getSupportedCryptoSchemes(
getSupportedCryptoSchemes_cb _hidl_cb) override;
+ Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& args);
+
private:
CLEARKEY_DISALLOW_COPY_AND_ASSIGN(DrmFactory);
};
diff --git a/media/TEST_MAPPING b/media/TEST_MAPPING
new file mode 100644
index 0000000..a6dfb21
--- /dev/null
+++ b/media/TEST_MAPPING
@@ -0,0 +1,32 @@
+{
+ "presubmit": [
+ {
+ "name": "GtsMediaTestCases",
+ "options" : [
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "include-filter": "com.google.android.media.gts.WidevineGenericOpsTests"
+ }
+ ]
+ },
+ {
+ "name": "GtsExoPlayerTestCases",
+ "options" : [
+ {
+ "include-annotation": "android.platform.test.annotations.SocPresubmit"
+ },
+ {
+ "include-filter": "com.google.android.exoplayer.gts.DashTest#testWidevine23FpsH264Fixed"
+ }
+ ]
+ }
+ ],
+ "imports": [
+ {
+ "path": "frameworks/av/drm/mediadrm/plugins"
+ }
+ ]
+}
+
diff --git a/media/audioserver/audioserver.rc b/media/audioserver/audioserver.rc
index 5484613..38c2750 100644
--- a/media/audioserver/audioserver.rc
+++ b/media/audioserver/audioserver.rc
@@ -6,16 +6,36 @@
capabilities BLOCK_SUSPEND
ioprio rt 4
writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
- onrestart restart vendor.audio-hal
- onrestart restart vendor.audio-hal-4-0-msd
- # Keep the original service names for backward compatibility
- onrestart restart vendor.audio-hal-2-0
- onrestart restart audio-hal-2-0
+
+ onrestart setprop sys.audio.restart.hal 1
on property:vts.native_server.on=1
stop audioserver
on property:vts.native_server.on=0
start audioserver
+on property:init.svc.audioserver=stopped
+ stop vendor.audio-hal
+ stop vendor.audio-hal-4-0-msd
+ # Keep the original service names for backward compatibility
+ stop vendor.audio-hal-2-0
+ stop audio-hal-2-0
+
+on property:init.svc.audioserver=running
+ start vendor.audio-hal
+ start vendor.audio-hal-4-0-msd
+ # Keep the original service names for backward compatibility
+ start vendor.audio-hal-2-0
+ start audio-hal-2-0
+
+on property:sys.audio.restart.hal=1
+ restart vendor.audio-hal
+ restart vendor.audio-hal-4-0-msd
+ # Keep the original service names for backward compatibility
+ restart vendor.audio-hal-2-0
+ restart audio-hal-2-0
+ # reset the property
+ setprop sys.audio.restart.hal 0
+
on init
mkdir /dev/socket/audioserver 0775 audioserver audioserver
diff --git a/media/codec2/components/avc/C2SoftAvcDec.cpp b/media/codec2/components/avc/C2SoftAvcDec.cpp
index d686eb1..c1edb98 100644
--- a/media/codec2/components/avc/C2SoftAvcDec.cpp
+++ b/media/codec2/components/avc/C2SoftAvcDec.cpp
@@ -502,7 +502,7 @@
status_t C2SoftAvcDec::initDecoder() {
if (OK != createDecoder()) return UNKNOWN_ERROR;
mNumCores = MIN(getCpuCoreCount(), MAX_NUM_CORES);
- mStride = ALIGN128(mWidth);
+ mStride = ALIGN32(mWidth);
mSignalledError = false;
resetPlugin();
(void) setNumCores();
@@ -520,10 +520,20 @@
size_t inSize,
uint32_t tsMarker) {
uint32_t displayStride = mStride;
+ if (outBuffer) {
+ C2PlanarLayout layout;
+ layout = outBuffer->layout();
+ displayStride = layout.planes[C2PlanarLayout::PLANE_Y].rowInc;
+ }
uint32_t displayHeight = mHeight;
size_t lumaSize = displayStride * displayHeight;
size_t chromaSize = lumaSize >> 2;
+ if (mStride != displayStride) {
+ mStride = displayStride;
+ if (OK != setParams(mStride, IVD_DECODE_FRAME)) return false;
+ }
+
ps_decode_ip->u4_size = sizeof(ivd_video_decode_ip_t);
ps_decode_ip->e_cmd = IVD_CMD_VIDEO_DECODE;
if (inBuffer) {
@@ -757,24 +767,21 @@
ALOGE("not supposed to be here, invalid decoder context");
return C2_CORRUPTED;
}
- if (mStride != ALIGN128(mWidth)) {
- mStride = ALIGN128(mWidth);
- if (OK != setParams(mStride, IVD_DECODE_FRAME)) return C2_CORRUPTED;
- }
if (mOutBlock &&
- (mOutBlock->width() != mStride || mOutBlock->height() != mHeight)) {
+ (mOutBlock->width() != ALIGN32(mWidth) || mOutBlock->height() != mHeight)) {
mOutBlock.reset();
}
if (!mOutBlock) {
uint32_t format = HAL_PIXEL_FORMAT_YV12;
C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
- c2_status_t err = pool->fetchGraphicBlock(mStride, mHeight, format, usage, &mOutBlock);
+ c2_status_t err =
+ pool->fetchGraphicBlock(ALIGN32(mWidth), mHeight, format, usage, &mOutBlock);
if (err != C2_OK) {
ALOGE("fetchGraphicBlock for Output failed with status %d", err);
return err;
}
ALOGV("provided (%dx%d) required (%dx%d)",
- mOutBlock->width(), mOutBlock->height(), mStride, mHeight);
+ mOutBlock->width(), mOutBlock->height(), ALIGN32(mWidth), mHeight);
}
return C2_OK;
@@ -909,7 +916,8 @@
if (0 < s_decode_op.u4_pic_wd && 0 < s_decode_op.u4_pic_ht) {
if (mHeaderDecoded == false) {
mHeaderDecoded = true;
- setParams(ALIGN128(s_decode_op.u4_pic_wd), IVD_DECODE_FRAME);
+ mStride = ALIGN32(s_decode_op.u4_pic_wd);
+ setParams(mStride, IVD_DECODE_FRAME);
}
if (s_decode_op.u4_pic_wd != mWidth || s_decode_op.u4_pic_ht != mHeight) {
mWidth = s_decode_op.u4_pic_wd;
diff --git a/media/codec2/components/avc/C2SoftAvcDec.h b/media/codec2/components/avc/C2SoftAvcDec.h
index ed27493..bd84de0 100644
--- a/media/codec2/components/avc/C2SoftAvcDec.h
+++ b/media/codec2/components/avc/C2SoftAvcDec.h
@@ -39,8 +39,7 @@
#define ivdext_ctl_set_num_cores_op_t ih264d_ctl_set_num_cores_op_t
#define ivdext_ctl_get_vui_params_ip_t ih264d_ctl_get_vui_params_ip_t
#define ivdext_ctl_get_vui_params_op_t ih264d_ctl_get_vui_params_op_t
-#define ALIGN64(x) ((((x) + 63) >> 6) << 6)
-#define ALIGN128(x) ((((x) + 127) >> 7) << 7)
+#define ALIGN32(x) ((((x) + 31) >> 5) << 5)
#define MAX_NUM_CORES 4
#define IVDEXT_CMD_CTL_SET_NUM_CORES \
(IVD_CONTROL_API_COMMAND_TYPE_T)IH264D_CMD_CTL_SET_NUM_CORES
diff --git a/media/codec2/components/avc/C2SoftAvcEnc.cpp b/media/codec2/components/avc/C2SoftAvcEnc.cpp
index e3d419c..ab93ce3 100644
--- a/media/codec2/components/avc/C2SoftAvcEnc.cpp
+++ b/media/codec2/components/avc/C2SoftAvcEnc.cpp
@@ -103,7 +103,7 @@
addParameter(
DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE)
- .withDefault(new C2StreamPictureSizeInfo::input(0u, 320, 240))
+ .withDefault(new C2StreamPictureSizeInfo::input(0u, 16, 16))
.withFields({
C2F(mSize, width).inRange(2, 2560, 2),
C2F(mSize, height).inRange(2, 2560, 2),
@@ -129,7 +129,7 @@
addParameter(
DefineParam(mFrameRate, C2_PARAMKEY_FRAME_RATE)
- .withDefault(new C2StreamFrameRateInfo::output(0u, 30.))
+ .withDefault(new C2StreamFrameRateInfo::output(0u, 1.))
// TODO: More restriction?
.withFields({C2F(mFrameRate, value).greaterThan(0.)})
.withSetter(Setter<decltype(*mFrameRate)>::StrictValueWithNoDeps)
diff --git a/media/codec2/components/hevc/C2SoftHevcDec.cpp b/media/codec2/components/hevc/C2SoftHevcDec.cpp
index 6db4387..e4b911d 100644
--- a/media/codec2/components/hevc/C2SoftHevcDec.cpp
+++ b/media/codec2/components/hevc/C2SoftHevcDec.cpp
@@ -497,7 +497,7 @@
status_t C2SoftHevcDec::initDecoder() {
if (OK != createDecoder()) return UNKNOWN_ERROR;
mNumCores = MIN(getCpuCoreCount(), MAX_NUM_CORES);
- mStride = ALIGN128(mWidth);
+ mStride = ALIGN32(mWidth);
mSignalledError = false;
resetPlugin();
(void) setNumCores();
@@ -515,10 +515,20 @@
size_t inSize,
uint32_t tsMarker) {
uint32_t displayStride = mStride;
+ if (outBuffer) {
+ C2PlanarLayout layout;
+ layout = outBuffer->layout();
+ displayStride = layout.planes[C2PlanarLayout::PLANE_Y].rowInc;
+ }
uint32_t displayHeight = mHeight;
size_t lumaSize = displayStride * displayHeight;
size_t chromaSize = lumaSize >> 2;
+ if (mStride != displayStride) {
+ mStride = displayStride;
+ if (OK != setParams(mStride, IVD_DECODE_FRAME)) return false;
+ }
+
ps_decode_ip->u4_size = sizeof(ivd_video_decode_ip_t);
ps_decode_ip->e_cmd = IVD_CMD_VIDEO_DECODE;
if (inBuffer) {
@@ -752,24 +762,21 @@
ALOGE("not supposed to be here, invalid decoder context");
return C2_CORRUPTED;
}
- if (mStride != ALIGN128(mWidth)) {
- mStride = ALIGN128(mWidth);
- if (OK != setParams(mStride, IVD_DECODE_FRAME)) return C2_CORRUPTED;
- }
if (mOutBlock &&
- (mOutBlock->width() != mStride || mOutBlock->height() != mHeight)) {
+ (mOutBlock->width() != ALIGN32(mWidth) || mOutBlock->height() != mHeight)) {
mOutBlock.reset();
}
if (!mOutBlock) {
uint32_t format = HAL_PIXEL_FORMAT_YV12;
C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
- c2_status_t err = pool->fetchGraphicBlock(mStride, mHeight, format, usage, &mOutBlock);
+ c2_status_t err =
+ pool->fetchGraphicBlock(ALIGN32(mWidth), mHeight, format, usage, &mOutBlock);
if (err != C2_OK) {
ALOGE("fetchGraphicBlock for Output failed with status %d", err);
return err;
}
ALOGV("provided (%dx%d) required (%dx%d)",
- mOutBlock->width(), mOutBlock->height(), mStride, mHeight);
+ mOutBlock->width(), mOutBlock->height(), ALIGN32(mWidth), mHeight);
}
return C2_OK;
@@ -904,7 +911,7 @@
if (0 < s_decode_op.u4_pic_wd && 0 < s_decode_op.u4_pic_ht) {
if (mHeaderDecoded == false) {
mHeaderDecoded = true;
- setParams(ALIGN128(s_decode_op.u4_pic_wd), IVD_DECODE_FRAME);
+ setParams(ALIGN32(s_decode_op.u4_pic_wd), IVD_DECODE_FRAME);
}
if (s_decode_op.u4_pic_wd != mWidth || s_decode_op.u4_pic_ht != mHeight) {
mWidth = s_decode_op.u4_pic_wd;
diff --git a/media/codec2/components/hevc/C2SoftHevcDec.h b/media/codec2/components/hevc/C2SoftHevcDec.h
index aecd101..600d7c1 100644
--- a/media/codec2/components/hevc/C2SoftHevcDec.h
+++ b/media/codec2/components/hevc/C2SoftHevcDec.h
@@ -37,8 +37,7 @@
#define ivdext_ctl_set_num_cores_op_t ihevcd_cxa_ctl_set_num_cores_op_t
#define ivdext_ctl_get_vui_params_ip_t ihevcd_cxa_ctl_get_vui_params_ip_t
#define ivdext_ctl_get_vui_params_op_t ihevcd_cxa_ctl_get_vui_params_op_t
-#define ALIGN64(x) ((((x) + 63) >> 6) << 6)
-#define ALIGN128(x) ((((x) + 127) >> 7) << 7)
+#define ALIGN32(x) ((((x) + 31) >> 5) << 5)
#define MAX_NUM_CORES 4
#define IVDEXT_CMD_CTL_SET_NUM_CORES \
(IVD_CONTROL_API_COMMAND_TYPE_T)IHEVCD_CXA_CMD_CTL_SET_NUM_CORES
diff --git a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp
index e0365fc..c7ca18c 100644
--- a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp
+++ b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp
@@ -571,7 +571,7 @@
if (OK != createDecoder()) return UNKNOWN_ERROR;
mNumCores = MIN(getCpuCoreCount(), MAX_NUM_CORES);
- mStride = ALIGN64(mWidth);
+ mStride = ALIGN32(mWidth);
mSignalledError = false;
resetPlugin();
(void) setNumCores();
@@ -589,10 +589,20 @@
size_t inSize,
uint32_t tsMarker) {
uint32_t displayStride = mStride;
+ if (outBuffer) {
+ C2PlanarLayout layout;
+ layout = outBuffer->layout();
+ displayStride = layout.planes[C2PlanarLayout::PLANE_Y].rowInc;
+ }
uint32_t displayHeight = mHeight;
size_t lumaSize = displayStride * displayHeight;
size_t chromaSize = lumaSize >> 2;
+ if (mStride != displayStride) {
+ mStride = displayStride;
+ if (OK != setParams(mStride)) return false;
+ }
+
ps_decode_ip->u4_size = sizeof(ivd_video_decode_ip_t);
ps_decode_ip->e_cmd = IVD_CMD_VIDEO_DECODE;
if (inBuffer) {
@@ -833,24 +843,21 @@
ALOGE("not supposed to be here, invalid decoder context");
return C2_CORRUPTED;
}
- if (mStride != ALIGN64(mWidth)) {
- mStride = ALIGN64(mWidth);
- if (OK != setParams(mStride)) return C2_CORRUPTED;
- }
if (mOutBlock &&
- (mOutBlock->width() != mStride || mOutBlock->height() != mHeight)) {
+ (mOutBlock->width() != ALIGN32(mWidth) || mOutBlock->height() != mHeight)) {
mOutBlock.reset();
}
if (!mOutBlock) {
uint32_t format = HAL_PIXEL_FORMAT_YV12;
C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
- c2_status_t err = pool->fetchGraphicBlock(mStride, mHeight, format, usage, &mOutBlock);
+ c2_status_t err =
+ pool->fetchGraphicBlock(ALIGN32(mWidth), mHeight, format, usage, &mOutBlock);
if (err != C2_OK) {
ALOGE("fetchGraphicBlock for Output failed with status %d", err);
return err;
}
ALOGV("provided (%dx%d) required (%dx%d)",
- mOutBlock->width(), mOutBlock->height(), mStride, mHeight);
+ mOutBlock->width(), mOutBlock->height(), ALIGN32(mWidth), mHeight);
}
return C2_OK;
diff --git a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.h b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.h
index 65d3b87..fd66304a 100644
--- a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.h
+++ b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.h
@@ -37,7 +37,7 @@
#define ivdext_ctl_set_num_cores_op_t impeg2d_ctl_set_num_cores_op_t
#define ivdext_ctl_get_seq_info_ip_t impeg2d_ctl_get_seq_info_ip_t
#define ivdext_ctl_get_seq_info_op_t impeg2d_ctl_get_seq_info_op_t
-#define ALIGN64(x) ((((x) + 63) >> 6) << 6)
+#define ALIGN32(x) ((((x) + 31) >> 5) << 5)
#define MAX_NUM_CORES 4
#define IVDEXT_CMD_CTL_SET_NUM_CORES \
(IVD_CONTROL_API_COMMAND_TYPE_T)IMPEG2D_CMD_CTL_SET_NUM_CORES
diff --git a/media/codec2/components/vpx/C2SoftVpxEnc.cpp b/media/codec2/components/vpx/C2SoftVpxEnc.cpp
index ebc7a8f..74e105e 100644
--- a/media/codec2/components/vpx/C2SoftVpxEnc.cpp
+++ b/media/codec2/components/vpx/C2SoftVpxEnc.cpp
@@ -67,8 +67,9 @@
mLastTimestamp(0x7FFFFFFFFFFFFFFFull),
mSignalledOutputEos(false),
mSignalledError(false) {
- memset(mTemporalLayerBitrateRatio, 0, sizeof(mTemporalLayerBitrateRatio));
- mTemporalLayerBitrateRatio[0] = 100;
+ for (int i = 0; i < MAXTEMPORALLAYERS; i++) {
+ mTemporalLayerBitrateRatio[i] = 1.0f;
+ }
}
C2SoftVpxEnc::~C2SoftVpxEnc() {
@@ -123,7 +124,8 @@
mFrameRate = mIntf->getFrameRate_l();
mIntraRefresh = mIntf->getIntraRefresh_l();
mRequestSync = mIntf->getRequestSync_l();
- mTemporalLayers = mIntf->getTemporalLayers_l()->m.layerCount;
+ mLayering = mIntf->getTemporalLayers_l();
+ mTemporalLayers = mLayering->m.layerCount;
}
switch (mBitrateMode->value) {
@@ -225,6 +227,7 @@
mTemporalPattern[5] = kTemporalUpdateGoldenRefAltRef;
mTemporalPattern[6] = kTemporalUpdateLastRefAltRef;
mTemporalPattern[7] = kTemporalUpdateNone;
+ mTemporalLayerBitrateRatio[0] = mLayering->m.bitrateRatios[0];
mTemporalPatternLength = 8;
break;
case 3:
@@ -245,6 +248,8 @@
mTemporalPattern[5] = kTemporalUpdateNone;
mTemporalPattern[6] = kTemporalUpdateGoldenRefAltRef;
mTemporalPattern[7] = kTemporalUpdateNone;
+ mTemporalLayerBitrateRatio[0] = mLayering->m.bitrateRatios[0];
+ mTemporalLayerBitrateRatio[1] = mLayering->m.bitrateRatios[1];
mTemporalPatternLength = 8;
break;
default:
@@ -255,7 +260,7 @@
for (size_t i = 0; i < mCodecConfiguration->ts_number_layers; i++) {
mCodecConfiguration->ts_target_bitrate[i] =
mCodecConfiguration->rc_target_bitrate *
- mTemporalLayerBitrateRatio[i] / 100;
+ mTemporalLayerBitrateRatio[i];
}
if (mIntf->getSyncFramePeriod() >= 0) {
mCodecConfiguration->kf_max_dist = mIntf->getSyncFramePeriod();
diff --git a/media/codec2/components/vpx/C2SoftVpxEnc.h b/media/codec2/components/vpx/C2SoftVpxEnc.h
index 62ccd1b..5e34b8a 100644
--- a/media/codec2/components/vpx/C2SoftVpxEnc.h
+++ b/media/codec2/components/vpx/C2SoftVpxEnc.h
@@ -180,7 +180,7 @@
size_t mTemporalLayers;
// Temporal layer bitrare ratio in percentage
- uint32_t mTemporalLayerBitrateRatio[MAXTEMPORALLAYERS];
+ float_t mTemporalLayerBitrateRatio[MAXTEMPORALLAYERS];
// Temporal pattern type
TemporalPatternType mTemporalPatternType;
@@ -218,6 +218,7 @@
std::shared_ptr<C2StreamBitrateInfo::output> mBitrate;
std::shared_ptr<C2StreamBitrateModeTuning::output> mBitrateMode;
std::shared_ptr<C2StreamRequestSyncFrameTuning::output> mRequestSync;
+ std::shared_ptr<C2StreamTemporalLayeringTuning::output> mLayering;
C2_DO_NOT_COPY(C2SoftVpxEnc);
};
diff --git a/media/codec2/hidl/1.0/vts/OWNERS b/media/codec2/hidl/1.0/vts/OWNERS
index 6733e0c..dbe89cf 100644
--- a/media/codec2/hidl/1.0/vts/OWNERS
+++ b/media/codec2/hidl/1.0/vts/OWNERS
@@ -5,5 +5,4 @@
wonsik@google.com
# VTS team
-yim@google.com
-zhuoyao@google.com
+dshi@google.com
diff --git a/media/codec2/hidl/1.0/vts/functional/Android.bp b/media/codec2/hidl/1.0/vts/functional/Android.bp
new file mode 100644
index 0000000..5f0ba7d
--- /dev/null
+++ b/media/codec2/hidl/1.0/vts/functional/Android.bp
@@ -0,0 +1,101 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+filegroup {
+ name: "media_c2_v1_audio_decode_res",
+ path: "res",
+ srcs: [
+ "res/bbb_aac_stereo_128kbps_48000hz.aac",
+ "res/bbb_aac_stereo_128kbps_48000hz.info",
+ "res/bbb_aac_stereo_128kbps_48000hz_multi_frame.info",
+ "res/bbb_amrwb_1ch_14kbps_16000hz.amrwb",
+ "res/bbb_amrwb_1ch_14kbps_16000hz.info",
+ "res/bbb_amrwb_1ch_14kbps_16000hz_multi_frame.info",
+ "res/bbb_flac_stereo_680kbps_48000hz.flac",
+ "res/bbb_flac_stereo_680kbps_48000hz.info",
+ "res/bbb_g711alaw_1ch_8khz.info",
+ "res/bbb_g711alaw_1ch_8khz.raw",
+ "res/bbb_g711mulaw_1ch_8khz.info",
+ "res/bbb_g711mulaw_1ch_8khz.raw",
+ "res/bbb_gsm_1ch_8khz_13kbps.info",
+ "res/bbb_gsm_1ch_8khz_13kbps.raw",
+ "res/bbb_mp3_stereo_192kbps_48000hz.info",
+ "res/bbb_mp3_stereo_192kbps_48000hz.mp3",
+ "res/bbb_mp3_stereo_192kbps_48000hz_multi_frame.info",
+ "res/bbb_opus_stereo_128kbps_48000hz.info",
+ "res/bbb_opus_stereo_128kbps_48000hz.opus",
+ "res/bbb_raw_1ch_8khz_s32le.info",
+ "res/bbb_raw_1ch_8khz_s32le.raw",
+ "res/bbb_vorbis_stereo_128kbps_48000hz.info",
+ "res/bbb_vorbis_stereo_128kbps_48000hz.vorbis",
+ "res/sine_amrnb_1ch_12kbps_8000hz.amrnb",
+ "res/sine_amrnb_1ch_12kbps_8000hz.info",
+ "res/sine_amrnb_1ch_12kbps_8000hz_multi_frame.info",
+ ],
+}
+
+filegroup {
+ name: "media_c2_v1_audio_encode_res",
+ path: "res",
+ srcs: [
+ "res/bbb_raw_2ch_48khz_s16le.raw",
+ "res/bbb_raw_1ch_8khz_s16le.raw",
+ "res/bbb_raw_1ch_16khz_s16le.raw",
+ ],
+}
+
+filegroup {
+ name: "media_c2_v1_video_decode_res",
+ path: "res",
+ srcs: [
+ "res/bbb_avc_176x144_300kbps_60fps.h264",
+ "res/bbb_avc_640x360_768kbps_30fps.h264",
+ "res/bbb_avc_176x144_300kbps_60fps.info",
+ "res/bbb_avc_640x360_768kbps_30fps.info",
+ "res/bbb_hevc_176x144_176kbps_60fps.hevc",
+ "res/bbb_hevc_640x360_1600kbps_30fps.hevc",
+ "res/bbb_hevc_176x144_176kbps_60fps.info",
+ "res/bbb_hevc_640x360_1600kbps_30fps.info",
+ "res/bbb_mpeg2_176x144_105kbps_25fps.m2v",
+ "res/bbb_mpeg2_352x288_1mbps_60fps.m2v",
+ "res/bbb_mpeg2_176x144_105kbps_25fps.info",
+ "res/bbb_mpeg2_352x288_1mbps_60fps.info",
+ "res/bbb_h263_352x288_300kbps_12fps.h263",
+ "res/bbb_h263_352x288_300kbps_12fps.info",
+ "res/bbb_mpeg4_352x288_512kbps_30fps.m4v",
+ "res/bbb_mpeg4_352x288_512kbps_30fps.info",
+ "res/bbb_vp8_176x144_240kbps_60fps.vp8",
+ "res/bbb_vp8_640x360_2mbps_30fps.vp8",
+ "res/bbb_vp8_176x144_240kbps_60fps.info",
+ "res/bbb_vp8_640x360_2mbps_30fps.info",
+ "res/bbb_vp9_176x144_285kbps_60fps.vp9",
+ "res/bbb_vp9_640x360_1600kbps_30fps.vp9",
+ "res/bbb_vp9_176x144_285kbps_60fps.info",
+ "res/bbb_vp9_640x360_1600kbps_30fps.info",
+ "res/bbb_av1_640_360.av1",
+ "res/bbb_av1_176_144.av1",
+ "res/bbb_av1_640_360.info",
+ "res/bbb_av1_176_144.info",
+ ],
+}
+
+filegroup {
+ name: "media_c2_v1_video_encode_res",
+ path: "res",
+ srcs: [
+ "res/bbb_352x288_420p_30fps_32frames.yuv",
+ ],
+}
\ No newline at end of file
diff --git a/media/codec2/hidl/1.0/vts/functional/audio/Android.bp b/media/codec2/hidl/1.0/vts/functional/audio/Android.bp
index 65f0d09..014cbe9 100644
--- a/media/codec2/hidl/1.0/vts/functional/audio/Android.bp
+++ b/media/codec2/hidl/1.0/vts/functional/audio/Android.bp
@@ -16,18 +16,22 @@
cc_test {
name: "VtsHalMediaC2V1_0TargetAudioDecTest",
+ stem: "vts_media_c2_v1_0_audio_dec_test",
defaults: ["VtsHalMediaC2V1_0Defaults"],
srcs: [
"VtsHalMediaC2V1_0TargetAudioDecTest.cpp",
- //"media_audio_hidl_test_common.cpp"
],
+ data: [":media_c2_v1_audio_decode_res"],
+ test_config: "VtsHalMediaC2V1_0TargetAudioDecTest.xml",
}
cc_test {
name: "VtsHalMediaC2V1_0TargetAudioEncTest",
+ stem: "vts_media_c2_v1_0_audio_enc_test",
defaults: ["VtsHalMediaC2V1_0Defaults"],
srcs: [
"VtsHalMediaC2V1_0TargetAudioEncTest.cpp",
- //"media_audio_hidl_test_common.cpp"
],
+ data: [":media_c2_v1_audio_encode_res"],
+ test_config: "VtsHalMediaC2V1_0TargetAudioEncTest.xml",
}
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 20f4665..264abba 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
@@ -19,21 +19,20 @@
#include <android-base/logging.h>
#include <gtest/gtest.h>
-#include <algorithm>
+#include <hidl/GtestPrinter.h>
#include <stdio.h>
+#include <algorithm>
#include <fstream>
-#include <codec2/hidl/client.h>
#include <C2AllocatorIon.h>
-#include <C2Config.h>
-#include <C2Debug.h>
#include <C2Buffer.h>
#include <C2BufferPriv.h>
+#include <C2Config.h>
+#include <C2Debug.h>
+#include <codec2/hidl/client.h>
using android::C2AllocatorIon;
-#include <VtsHalHidlTargetTestBase.h>
-#include "media_c2_audio_hidl_test_common.h"
#include "media_c2_hidl_test_common.h"
struct FrameInfo {
@@ -42,57 +41,44 @@
int64_t timestamp;
};
-class LinearBuffer : public C2Buffer {
- public:
- explicit LinearBuffer(const std::shared_ptr<C2LinearBlock>& block)
- : C2Buffer(
- {block->share(block->offset(), block->size(), ::C2Fence())}) {}
-};
+static std::vector<std::tuple<std::string, std::string, std::string, std::string>>
+ kDecodeTestParameters;
-static ComponentTestEnvironment* gEnv = nullptr;
+// Resource directory
+static std::string sResourceDir = "";
+
+class LinearBuffer : public C2Buffer {
+ public:
+ explicit LinearBuffer(const std::shared_ptr<C2LinearBlock>& block)
+ : C2Buffer({block->share(block->offset(), block->size(), ::C2Fence())}) {}
+};
namespace {
-class Codec2AudioDecHidlTest : public ::testing::VtsHalHidlTargetTestBase {
- private:
- typedef ::testing::VtsHalHidlTargetTestBase Super;
-
- public:
- ::std::string getTestCaseInfo() const override {
- return ::std::string() +
- "Component: " + gEnv->getComponent().c_str() + " | " +
- "Instance: " + gEnv->getInstance().c_str() + " | " +
- "Res: " + gEnv->getRes().c_str();
- }
-
+class Codec2AudioDecHidlTestBase : public ::testing::Test {
+ public:
// google.codec2 Audio test setup
virtual void SetUp() override {
- Super::SetUp();
+ getParams();
mDisableTest = false;
ALOGV("Codec2AudioDecHidlTest SetUp");
mClient = android::Codec2Client::CreateFromService(
- gEnv->getInstance().c_str(),
- !bool(android::Codec2Client::CreateFromService("default", true)));
+ mInstanceName.c_str(),
+ !bool(android::Codec2Client::CreateFromService("default", true)));
ASSERT_NE(mClient, nullptr);
- mListener.reset(new CodecListener(
- [this](std::list<std::unique_ptr<C2Work>>& workItems) {
- handleWorkDone(workItems);
- }));
+ mListener.reset(new CodecListener([this](std::list<std::unique_ptr<C2Work>>& workItems) {
+ handleWorkDone(workItems);
+ }));
ASSERT_NE(mListener, nullptr);
for (int i = 0; i < MAX_INPUT_BUFFERS; ++i) {
mWorkQueue.emplace_back(new C2Work);
}
- mClient->createComponent(gEnv->getComponent().c_str(), mListener,
- &mComponent);
+ mClient->createComponent(mComponentName, mListener, &mComponent);
ASSERT_NE(mComponent, nullptr);
- std::shared_ptr<C2AllocatorStore> store =
- android::GetCodec2PlatformAllocatorStore();
- CHECK_EQ(store->fetchAllocator(C2AllocatorStore::DEFAULT_LINEAR,
- &mLinearAllocator),
- C2_OK);
- mLinearPool = std::make_shared<C2PooledBlockPool>(mLinearAllocator,
- mBlockPoolId++);
+ std::shared_ptr<C2AllocatorStore> store = android::GetCodec2PlatformAllocatorStore();
+ CHECK_EQ(store->fetchAllocator(C2AllocatorStore::DEFAULT_LINEAR, &mLinearAllocator), C2_OK);
+ mLinearPool = std::make_shared<C2PooledBlockPool>(mLinearAllocator, mBlockPoolId++);
ASSERT_NE(mLinearPool, nullptr);
mCompName = unknown_comp;
@@ -101,27 +87,17 @@
standardComp CompName;
};
const StringToName kStringToName[] = {
- {"xaac", xaac},
- {"mp3", mp3},
- {"amrnb", amrnb},
- {"amrwb", amrwb},
- {"aac", aac},
- {"vorbis", vorbis},
- {"opus", opus},
- {"pcm", pcm},
- {"g711.alaw", g711alaw},
- {"g711.mlaw", g711mlaw},
- {"gsm", gsm},
- {"raw", raw},
- {"flac", flac},
+ {"xaac", xaac}, {"mp3", mp3}, {"amrnb", amrnb},
+ {"amrwb", amrwb}, {"aac", aac}, {"vorbis", vorbis},
+ {"opus", opus}, {"pcm", pcm}, {"g711.alaw", g711alaw},
+ {"g711.mlaw", g711mlaw}, {"gsm", gsm}, {"raw", raw},
+ {"flac", flac},
};
- const size_t kNumStringToName =
- sizeof(kStringToName) / sizeof(kStringToName[0]);
+ const size_t kNumStringToName = sizeof(kStringToName) / sizeof(kStringToName[0]);
// Find the component type
- std::string comp = std::string(gEnv->getComponent());
for (size_t i = 0; i < kNumStringToName; ++i) {
- if (strcasestr(comp.c_str(), kStringToName[i].Name)) {
+ if (strcasestr(mComponentName.c_str(), kStringToName[i].Name)) {
mCompName = kStringToName[i].CompName;
break;
}
@@ -140,9 +116,11 @@
mComponent->release();
mComponent = nullptr;
}
- Super::TearDown();
}
+ // Get the test parameters from GetParam call.
+ virtual void getParams() {}
+
struct outputMetaData {
uint64_t timestampUs;
uint32_t rangeLength;
@@ -155,25 +133,27 @@
// previous timestamp
bool codecConfig = ((work->worklets.front()->output.flags &
C2FrameData::FLAG_CODEC_CONFIG) != 0);
- if (!codecConfig &&
- !work->worklets.front()->output.buffers.empty()) {
+ if (!codecConfig && !work->worklets.front()->output.buffers.empty()) {
EXPECT_GE(work->worklets.front()->output.ordinal.timestamp.peeku(),
- mTimestampUs);
- mTimestampUs =
- work->worklets.front()->output.ordinal.timestamp.peeku();
- uint32_t rangeLength =
- work->worklets.front()->output.buffers[0]->data()
- .linearBlocks().front().map().get().capacity();
- //List of timestamp values and output size to calculate timestamp
+ mTimestampUs);
+ mTimestampUs = work->worklets.front()->output.ordinal.timestamp.peeku();
+ uint32_t rangeLength = work->worklets.front()
+ ->output.buffers[0]
+ ->data()
+ .linearBlocks()
+ .front()
+ .map()
+ .get()
+ .capacity();
+ // List of timestamp values and output size to calculate timestamp
if (mTimestampDevTest) {
outputMetaData meta = {mTimestampUs, rangeLength};
oBufferMetaData.push_back(meta);
}
}
bool mCsd = false;
- workDone(mComponent, work, mFlushedIndices, mQueueLock,
- mQueueCondition, mWorkQueue, mEos, mCsd,
- mFramesReceived);
+ workDone(mComponent, work, mFlushedIndices, mQueueLock, mQueueCondition, mWorkQueue,
+ mEos, mCsd, mFramesReceived);
(void)mCsd;
}
}
@@ -196,6 +176,8 @@
unknown_comp,
};
+ std::string mInstanceName;
+ std::string mComponentName;
bool mEos;
bool mDisableTest;
bool mTimestampDevTest;
@@ -218,15 +200,23 @@
std::shared_ptr<android::Codec2Client::Listener> mListener;
std::shared_ptr<android::Codec2Client::Component> mComponent;
- protected:
+ protected:
static void description(const std::string& description) {
RecordProperty("description", description);
}
};
-void validateComponent(
- const std::shared_ptr<android::Codec2Client::Component>& component,
- Codec2AudioDecHidlTest::standardComp compName, bool& disableTest) {
+class Codec2AudioDecHidlTest
+ : public Codec2AudioDecHidlTestBase,
+ public ::testing::WithParamInterface<std::tuple<std::string, std::string>> {
+ void getParams() {
+ mInstanceName = std::get<0>(GetParam());
+ mComponentName = std::get<1>(GetParam());
+ }
+};
+
+void validateComponent(const std::shared_ptr<android::Codec2Client::Component>& component,
+ Codec2AudioDecHidlTest::standardComp compName, bool& disableTest) {
// Validate its a C2 Component
if (component->getName().find("c2") == std::string::npos) {
ALOGE("Not a c2 component");
@@ -241,14 +231,12 @@
return;
}
std::vector<std::unique_ptr<C2Param>> queried;
- c2_status_t c2err =
- component->query({}, {C2PortMediaTypeSetting::input::PARAM_TYPE},
- C2_DONT_BLOCK, &queried);
+ c2_status_t c2err = component->query({}, {C2PortMediaTypeSetting::input::PARAM_TYPE},
+ C2_DONT_BLOCK, &queried);
if (c2err != C2_OK && queried.size() == 0) {
ALOGE("Query media type failed => %d", c2err);
} else {
- std::string inputDomain =
- ((C2StreamMediaTypeSetting::input*)queried[0].get())->m.value;
+ std::string inputDomain = ((C2StreamMediaTypeSetting::input*)queried[0].get())->m.value;
if (inputDomain.find("audio/") == std::string::npos) {
ALOGE("Expected Audio Component");
disableTest = true;
@@ -266,16 +254,14 @@
}
// Set Default config param.
-bool setupConfigParam(
- const std::shared_ptr<android::Codec2Client::Component>& component,
- int32_t* bitStreamInfo) {
+bool setupConfigParam(const std::shared_ptr<android::Codec2Client::Component>& component,
+ int32_t* bitStreamInfo) {
std::vector<std::unique_ptr<C2SettingResult>> failures;
C2StreamSampleRateInfo::output sampleRateInfo(0u, bitStreamInfo[0]);
C2StreamChannelCountInfo::output channelCountInfo(0u, bitStreamInfo[1]);
std::vector<C2Param*> configParam{&sampleRateInfo, &channelCountInfo};
- c2_status_t status =
- component->config(configParam, C2_DONT_BLOCK, &failures);
+ c2_status_t status = component->config(configParam, C2_DONT_BLOCK, &failures);
if (status == C2_OK && failures.size() == 0u) return true;
return false;
}
@@ -283,17 +269,15 @@
// In decoder components, often the input parameters get updated upon
// parsing the header of elementary stream. Client needs to collect this
// information and reconfigure
-void getInputChannelInfo(
- const std::shared_ptr<android::Codec2Client::Component>& component,
- Codec2AudioDecHidlTest::standardComp compName, int32_t* bitStreamInfo) {
+void getInputChannelInfo(const std::shared_ptr<android::Codec2Client::Component>& component,
+ Codec2AudioDecHidlTest::standardComp compName, int32_t* bitStreamInfo) {
// query nSampleRate and nChannels
std::initializer_list<C2Param::Index> indices{
- C2StreamSampleRateInfo::output::PARAM_TYPE,
- C2StreamChannelCountInfo::output::PARAM_TYPE,
+ C2StreamSampleRateInfo::output::PARAM_TYPE,
+ C2StreamChannelCountInfo::output::PARAM_TYPE,
};
std::vector<std::unique_ptr<C2Param>> inParams;
- c2_status_t status =
- component->query({}, indices, C2_DONT_BLOCK, &inParams);
+ c2_status_t status = component->query({}, indices, C2_DONT_BLOCK, &inParams);
if (status != C2_OK && inParams.size() == 0) {
ALOGE("Query media type failed => %d", status);
ASSERT_TRUE(false);
@@ -328,8 +312,8 @@
#define STREAM_COUNT 2
// LookUpTable of clips and metadata for component testing
-void GetURLForComponent(Codec2AudioDecHidlTest::standardComp comp, char* mURL,
- char* info, size_t streamIndex = 0) {
+void GetURLForComponent(Codec2AudioDecHidlTest::standardComp comp, char* mURL, char* info,
+ size_t streamIndex = 0) {
struct CompToURL {
Codec2AudioDecHidlTest::standardComp comp;
const char mURL[STREAM_COUNT][512];
@@ -338,52 +322,47 @@
ASSERT_TRUE(streamIndex < STREAM_COUNT);
static const CompToURL kCompToURL[] = {
- {Codec2AudioDecHidlTest::standardComp::xaac,
- {"bbb_aac_stereo_128kbps_48000hz.aac",
- "bbb_aac_stereo_128kbps_48000hz.aac"},
- {"bbb_aac_stereo_128kbps_48000hz.info",
- "bbb_aac_stereo_128kbps_48000hz_multi_frame.info"}},
- {Codec2AudioDecHidlTest::standardComp::mp3,
- {"bbb_mp3_stereo_192kbps_48000hz.mp3",
- "bbb_mp3_stereo_192kbps_48000hz.mp3"},
- {"bbb_mp3_stereo_192kbps_48000hz.info",
- "bbb_mp3_stereo_192kbps_48000hz_multi_frame.info"}},
- {Codec2AudioDecHidlTest::standardComp::aac,
- {"bbb_aac_stereo_128kbps_48000hz.aac",
- "bbb_aac_stereo_128kbps_48000hz.aac"},
- {"bbb_aac_stereo_128kbps_48000hz.info",
- "bbb_aac_stereo_128kbps_48000hz_multi_frame.info"}},
- {Codec2AudioDecHidlTest::standardComp::amrnb,
- {"sine_amrnb_1ch_12kbps_8000hz.amrnb",
- "sine_amrnb_1ch_12kbps_8000hz.amrnb"},
- {"sine_amrnb_1ch_12kbps_8000hz.info",
- "sine_amrnb_1ch_12kbps_8000hz_multi_frame.info"}},
- {Codec2AudioDecHidlTest::standardComp::amrwb,
- {"bbb_amrwb_1ch_14kbps_16000hz.amrwb",
- "bbb_amrwb_1ch_14kbps_16000hz.amrwb"},
- {"bbb_amrwb_1ch_14kbps_16000hz.info",
- "bbb_amrwb_1ch_14kbps_16000hz_multi_frame.info"}},
- {Codec2AudioDecHidlTest::standardComp::vorbis,
- {"bbb_vorbis_stereo_128kbps_48000hz.vorbis", ""},
- {"bbb_vorbis_stereo_128kbps_48000hz.info", ""}},
- {Codec2AudioDecHidlTest::standardComp::opus,
- {"bbb_opus_stereo_128kbps_48000hz.opus", ""},
- {"bbb_opus_stereo_128kbps_48000hz.info", ""}},
- {Codec2AudioDecHidlTest::standardComp::g711alaw,
- {"bbb_g711alaw_1ch_8khz.raw", ""},
- {"bbb_g711alaw_1ch_8khz.info", ""}},
- {Codec2AudioDecHidlTest::standardComp::g711mlaw,
- {"bbb_g711mulaw_1ch_8khz.raw", ""},
- {"bbb_g711mulaw_1ch_8khz.info", ""}},
- {Codec2AudioDecHidlTest::standardComp::gsm,
- {"bbb_gsm_1ch_8khz_13kbps.raw", ""},
- {"bbb_gsm_1ch_8khz_13kbps.info", ""}},
- {Codec2AudioDecHidlTest::standardComp::raw,
- {"bbb_raw_1ch_8khz_s32le.raw", ""},
- {"bbb_raw_1ch_8khz_s32le.info", ""}},
- {Codec2AudioDecHidlTest::standardComp::flac,
- {"bbb_flac_stereo_680kbps_48000hz.flac", ""},
- {"bbb_flac_stereo_680kbps_48000hz.info", ""}},
+ {Codec2AudioDecHidlTest::standardComp::xaac,
+ {"bbb_aac_stereo_128kbps_48000hz.aac", "bbb_aac_stereo_128kbps_48000hz.aac"},
+ {"bbb_aac_stereo_128kbps_48000hz.info",
+ "bbb_aac_stereo_128kbps_48000hz_multi_frame.info"}},
+ {Codec2AudioDecHidlTest::standardComp::mp3,
+ {"bbb_mp3_stereo_192kbps_48000hz.mp3", "bbb_mp3_stereo_192kbps_48000hz.mp3"},
+ {"bbb_mp3_stereo_192kbps_48000hz.info",
+ "bbb_mp3_stereo_192kbps_48000hz_multi_frame.info"}},
+ {Codec2AudioDecHidlTest::standardComp::aac,
+ {"bbb_aac_stereo_128kbps_48000hz.aac", "bbb_aac_stereo_128kbps_48000hz.aac"},
+ {"bbb_aac_stereo_128kbps_48000hz.info",
+ "bbb_aac_stereo_128kbps_48000hz_multi_frame.info"}},
+ {Codec2AudioDecHidlTest::standardComp::amrnb,
+ {"sine_amrnb_1ch_12kbps_8000hz.amrnb", "sine_amrnb_1ch_12kbps_8000hz.amrnb"},
+ {"sine_amrnb_1ch_12kbps_8000hz.info",
+ "sine_amrnb_1ch_12kbps_8000hz_multi_frame.info"}},
+ {Codec2AudioDecHidlTest::standardComp::amrwb,
+ {"bbb_amrwb_1ch_14kbps_16000hz.amrwb", "bbb_amrwb_1ch_14kbps_16000hz.amrwb"},
+ {"bbb_amrwb_1ch_14kbps_16000hz.info",
+ "bbb_amrwb_1ch_14kbps_16000hz_multi_frame.info"}},
+ {Codec2AudioDecHidlTest::standardComp::vorbis,
+ {"bbb_vorbis_stereo_128kbps_48000hz.vorbis", ""},
+ {"bbb_vorbis_stereo_128kbps_48000hz.info", ""}},
+ {Codec2AudioDecHidlTest::standardComp::opus,
+ {"bbb_opus_stereo_128kbps_48000hz.opus", ""},
+ {"bbb_opus_stereo_128kbps_48000hz.info", ""}},
+ {Codec2AudioDecHidlTest::standardComp::g711alaw,
+ {"bbb_g711alaw_1ch_8khz.raw", ""},
+ {"bbb_g711alaw_1ch_8khz.info", ""}},
+ {Codec2AudioDecHidlTest::standardComp::g711mlaw,
+ {"bbb_g711mulaw_1ch_8khz.raw", ""},
+ {"bbb_g711mulaw_1ch_8khz.info", ""}},
+ {Codec2AudioDecHidlTest::standardComp::gsm,
+ {"bbb_gsm_1ch_8khz_13kbps.raw", ""},
+ {"bbb_gsm_1ch_8khz_13kbps.info", ""}},
+ {Codec2AudioDecHidlTest::standardComp::raw,
+ {"bbb_raw_1ch_8khz_s32le.raw", ""},
+ {"bbb_raw_1ch_8khz_s32le.info", ""}},
+ {Codec2AudioDecHidlTest::standardComp::flac,
+ {"bbb_flac_stereo_680kbps_48000hz.flac", ""},
+ {"bbb_flac_stereo_680kbps_48000hz.info", ""}},
};
for (size_t i = 0; i < sizeof(kCompToURL) / sizeof(kCompToURL[0]); ++i) {
@@ -396,13 +375,11 @@
}
void decodeNFrames(const std::shared_ptr<android::Codec2Client::Component>& component,
- std::mutex &queueLock, std::condition_variable& queueCondition,
+ std::mutex& queueLock, std::condition_variable& queueCondition,
std::list<std::unique_ptr<C2Work>>& workQueue,
- std::list<uint64_t>& flushedIndices,
- std::shared_ptr<C2BlockPool>& linearPool,
- std::ifstream& eleStream,
- android::Vector<FrameInfo>* Info,
- int offset, int range, bool signalEOS = true) {
+ std::list<uint64_t>& flushedIndices, std::shared_ptr<C2BlockPool>& linearPool,
+ std::ifstream& eleStream, android::Vector<FrameInfo>* Info, int offset,
+ int range, bool signalEOS = true) {
typedef std::unique_lock<std::mutex> ULock;
int frameID = offset;
int maxRetry = 0;
@@ -426,8 +403,7 @@
}
int64_t timestamp = (*Info)[frameID].timestamp;
if ((*Info)[frameID].flags) flags = 1u << ((*Info)[frameID].flags - 1);
- if (signalEOS && ((frameID == (int)Info->size() - 1) ||
- (frameID == (offset + range - 1))))
+ if (signalEOS && ((frameID == (int)Info->size() - 1) || (frameID == (offset + range - 1))))
flags |= C2FrameData::FLAG_END_OF_STREAM;
work->input.flags = (C2FrameData::flags_t)flags;
@@ -448,9 +424,8 @@
if (size) {
std::shared_ptr<C2LinearBlock> block;
ASSERT_EQ(C2_OK,
- linearPool->fetchLinearBlock(
- size, {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE},
- &block));
+ linearPool->fetchLinearBlock(
+ size, {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}, &block));
ASSERT_TRUE(block);
// Write View
@@ -482,45 +457,49 @@
}
}
-TEST_F(Codec2AudioDecHidlTest, validateCompName) {
- if (mDisableTest) return;
+TEST_P(Codec2AudioDecHidlTest, validateCompName) {
+ if (mDisableTest) GTEST_SKIP() << "Test is disabled";
ALOGV("Checks if the given component is a valid audio component");
validateComponent(mComponent, mCompName, mDisableTest);
ASSERT_EQ(mDisableTest, false);
}
-TEST_F(Codec2AudioDecHidlTest, configComp) {
+TEST_P(Codec2AudioDecHidlTest, configComp) {
description("Tests component specific configuration");
- if (mDisableTest) return;
+ if (mDisableTest) GTEST_SKIP() << "Test is disabled";
ASSERT_EQ(mComponent->start(), C2_OK);
int32_t bitStreamInfo[2] = {0};
- ASSERT_NO_FATAL_FAILURE(
- getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
+ ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
setupConfigParam(mComponent, bitStreamInfo);
ASSERT_EQ(mComponent->stop(), C2_OK);
}
class Codec2AudioDecDecodeTest
- : public Codec2AudioDecHidlTest,
- public ::testing::WithParamInterface<std::pair<int32_t, bool>> {
+ : public Codec2AudioDecHidlTestBase,
+ public ::testing::WithParamInterface<
+ std::tuple<std::string, std::string, std::string, std::string>> {
+ void getParams() {
+ mInstanceName = std::get<0>(GetParam());
+ mComponentName = std::get<1>(GetParam());
+ }
};
TEST_P(Codec2AudioDecDecodeTest, DecodeTest) {
description("Decodes input file");
- if (mDisableTest) return;
+ if (mDisableTest) GTEST_SKIP() << "Test is disabled";
- uint32_t streamIndex = GetParam().first;
- bool signalEOS = GetParam().second;
+ uint32_t streamIndex = std::stoi(std::get<2>(GetParam()));
+ ;
+ bool signalEOS = !std::get<3>(GetParam()).compare("true");
mTimestampDevTest = true;
char mURL[512], info[512];
std::ifstream eleStream, eleInfo;
- strcpy(mURL, gEnv->getRes().c_str());
- strcpy(info, gEnv->getRes().c_str());
+ strcpy(mURL, sResourceDir.c_str());
+ strcpy(info, sResourceDir.c_str());
GetURLForComponent(mCompName, mURL, info, streamIndex);
- if (!strcmp(mURL, gEnv->getRes().c_str())) {
- ALOGV("EMPTY INPUT gEnv->getRes().c_str() %s mURL %s ",
- gEnv->getRes().c_str(), mURL);
+ if (!strcmp(mURL, sResourceDir.c_str())) {
+ ALOGV("EMPTY INPUT sResourceDir.c_str() %s mURL %s ", sResourceDir.c_str(), mURL);
return;
}
@@ -534,10 +513,8 @@
if (!(eleInfo >> bytesCount)) break;
eleInfo >> flags;
eleInfo >> timestamp;
- bool codecConfig =
- ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0;
- if (mTimestampDevTest && !codecConfig)
- mTimestampUslist.push_back(timestamp);
+ bool codecConfig = ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0;
+ if (mTimestampDevTest && !codecConfig) mTimestampUslist.push_back(timestamp);
Info.push_back({bytesCount, flags, timestamp});
}
eleInfo.close();
@@ -549,8 +526,7 @@
bitStreamInfo[0] = 8000;
bitStreamInfo[1] = 1;
} else {
- ASSERT_NO_FATAL_FAILURE(
- getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
+ ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
}
if (!setupConfigParam(mComponent, bitStreamInfo)) {
std::cout << "[ WARN ] Test Skipped \n";
@@ -560,29 +536,25 @@
ALOGV("mURL : %s", mURL);
eleStream.open(mURL, std::ifstream::binary);
ASSERT_EQ(eleStream.is_open(), true);
- ASSERT_NO_FATAL_FAILURE(decodeNFrames(
- mComponent, mQueueLock, mQueueCondition, mWorkQueue, mFlushedIndices,
- mLinearPool, eleStream, &Info, 0, (int)Info.size(), signalEOS));
+ ASSERT_NO_FATAL_FAILURE(decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
+ mFlushedIndices, mLinearPool, eleStream, &Info, 0,
+ (int)Info.size(), signalEOS));
// If EOS is not sent, sending empty input with EOS flag
size_t infoSize = Info.size();
if (!signalEOS) {
- ASSERT_NO_FATAL_FAILURE(
- waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue, 1));
- ASSERT_NO_FATAL_FAILURE(
- testInputBuffer(mComponent, mQueueLock, mWorkQueue,
- C2FrameData::FLAG_END_OF_STREAM, false));
+ ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue, 1));
+ ASSERT_NO_FATAL_FAILURE(testInputBuffer(mComponent, mQueueLock, mWorkQueue,
+ C2FrameData::FLAG_END_OF_STREAM, false));
infoSize += 1;
}
// blocking call to ensures application to Wait till all the inputs are
// consumed
- ASSERT_NO_FATAL_FAILURE(
- waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
+ ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
eleStream.close();
if (mFramesReceived != infoSize) {
ALOGE("Input buffer count and Output buffer count mismatch");
- ALOGE("framesReceived : %d inputFrames : %zu", mFramesReceived,
- infoSize);
+ ALOGE("framesReceived : %d inputFrames : %zu", mFramesReceived, infoSize);
ASSERT_TRUE(false);
}
ASSERT_EQ(mEos, true);
@@ -590,8 +562,7 @@
uint64_t expTs;
uint32_t samplesReceived = 0;
// Update SampleRate and ChannelCount
- ASSERT_NO_FATAL_FAILURE(
- getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
+ ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
int nSampleRate = bitStreamInfo[0];
int nChannels = bitStreamInfo[1];
std::list<uint64_t>::iterator itIn = mTimestampUslist.begin();
@@ -614,23 +585,17 @@
}
ASSERT_EQ(mComponent->stop(), C2_OK);
}
-// DecodeTest with StreamIndex and EOS / No EOS
-INSTANTIATE_TEST_CASE_P(StreamIndexAndEOS, Codec2AudioDecDecodeTest,
- ::testing::Values(std::make_pair(0, false),
- std::make_pair(0, true),
- std::make_pair(1, false),
- std::make_pair(1, true)));
// thumbnail test
-TEST_F(Codec2AudioDecHidlTest, ThumbnailTest) {
+TEST_P(Codec2AudioDecHidlTest, ThumbnailTest) {
description("Test Request for thumbnail");
- if (mDisableTest) return;
+ if (mDisableTest) GTEST_SKIP() << "Test is disabled";
char mURL[512], info[512];
std::ifstream eleStream, eleInfo;
- strcpy(mURL, gEnv->getRes().c_str());
- strcpy(info, gEnv->getRes().c_str());
+ strcpy(mURL, sResourceDir.c_str());
+ strcpy(info, sResourceDir.c_str());
GetURLForComponent(mCompName, mURL, info);
eleInfo.open(info);
@@ -651,8 +616,7 @@
bitStreamInfo[0] = 8000;
bitStreamInfo[1] = 1;
} else {
- ASSERT_NO_FATAL_FAILURE(
- getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
+ ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
}
if (!setupConfigParam(mComponent, bitStreamInfo)) {
std::cout << "[ WARN ] Test Skipped \n";
@@ -672,20 +636,19 @@
} while (!(flags & SYNC_FRAME));
eleStream.open(mURL, std::ifstream::binary);
ASSERT_EQ(eleStream.is_open(), true);
- ASSERT_NO_FATAL_FAILURE(decodeNFrames(
- mComponent, mQueueLock, mQueueCondition, mWorkQueue, mFlushedIndices,
- mLinearPool, eleStream, &Info, 0, i + 1));
- ASSERT_NO_FATAL_FAILURE(
- waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
+ ASSERT_NO_FATAL_FAILURE(decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
+ mFlushedIndices, mLinearPool, eleStream, &Info, 0,
+ i + 1));
+ ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
eleStream.close();
EXPECT_GE(mFramesReceived, 1U);
ASSERT_EQ(mEos, true);
ASSERT_EQ(mComponent->stop(), C2_OK);
}
-TEST_F(Codec2AudioDecHidlTest, EOSTest) {
+TEST_P(Codec2AudioDecHidlTest, EOSTest) {
description("Test empty input buffer with EOS flag");
- if (mDisableTest) return;
+ if (mDisableTest) GTEST_SKIP() << "Test is disabled";
typedef std::unique_lock<std::mutex> ULock;
ASSERT_EQ(mComponent->start(), C2_OK);
std::unique_ptr<C2Work> work;
@@ -723,15 +686,15 @@
ASSERT_EQ(mComponent->stop(), C2_OK);
}
-TEST_F(Codec2AudioDecHidlTest, FlushTest) {
+TEST_P(Codec2AudioDecHidlTest, FlushTest) {
description("Tests Flush calls");
- if (mDisableTest) return;
+ if (mDisableTest) GTEST_SKIP() << "Test is disabled";
typedef std::unique_lock<std::mutex> ULock;
char mURL[512], info[512];
std::ifstream eleStream, eleInfo;
- strcpy(mURL, gEnv->getRes().c_str());
- strcpy(info, gEnv->getRes().c_str());
+ strcpy(mURL, sResourceDir.c_str());
+ strcpy(info, sResourceDir.c_str());
GetURLForComponent(mCompName, mURL, info);
eleInfo.open(info);
@@ -753,8 +716,7 @@
bitStreamInfo[0] = 8000;
bitStreamInfo[1] = 1;
} else {
- ASSERT_NO_FATAL_FAILURE(
- getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
+ ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
}
if (!setupConfigParam(mComponent, bitStreamInfo)) {
std::cout << "[ WARN ] Test Skipped \n";
@@ -768,29 +730,25 @@
// frame after this so that the below section can be covered for all
// components
uint32_t numFramesFlushed = 128;
- ASSERT_NO_FATAL_FAILURE(decodeNFrames(
- mComponent, mQueueLock, mQueueCondition, mWorkQueue, mFlushedIndices,
- mLinearPool, eleStream, &Info, 0, numFramesFlushed, false));
+ ASSERT_NO_FATAL_FAILURE(decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
+ mFlushedIndices, mLinearPool, eleStream, &Info, 0,
+ numFramesFlushed, false));
// flush
std::list<std::unique_ptr<C2Work>> flushedWork;
- c2_status_t err =
- mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
+ c2_status_t err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
ASSERT_EQ(err, C2_OK);
- ASSERT_NO_FATAL_FAILURE(
- waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
- (size_t)MAX_INPUT_BUFFERS - flushedWork.size()));
+ ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
+ (size_t)MAX_INPUT_BUFFERS - flushedWork.size()));
uint64_t frameIndex;
{
- //Update mFlushedIndices based on the index received from flush()
+ // Update mFlushedIndices based on the index received from flush()
ULock l(mQueueLock);
for (std::unique_ptr<C2Work>& work : flushedWork) {
ASSERT_NE(work, nullptr);
frameIndex = work->input.ordinal.frameIndex.peeku();
std::list<uint64_t>::iterator frameIndexIt =
- std::find(mFlushedIndices.begin(), mFlushedIndices.end(),
- frameIndex);
- if (!mFlushedIndices.empty() &&
- (frameIndexIt != mFlushedIndices.end())) {
+ std::find(mFlushedIndices.begin(), mFlushedIndices.end(), frameIndex);
+ if (!mFlushedIndices.empty() && (frameIndexIt != mFlushedIndices.end())) {
mFlushedIndices.erase(frameIndexIt);
work->input.buffers.clear();
work->worklets.clear();
@@ -814,29 +772,24 @@
index++;
}
if (keyFrame) {
- ASSERT_NO_FATAL_FAILURE(
- decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
- mFlushedIndices, mLinearPool, eleStream, &Info, index,
- (int)Info.size() - index));
+ ASSERT_NO_FATAL_FAILURE(decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
+ mFlushedIndices, mLinearPool, eleStream, &Info, index,
+ (int)Info.size() - index));
}
eleStream.close();
- err =
- mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
+ err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
ASSERT_EQ(err, C2_OK);
- ASSERT_NO_FATAL_FAILURE(
- waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
- (size_t)MAX_INPUT_BUFFERS - flushedWork.size()));
+ ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
+ (size_t)MAX_INPUT_BUFFERS - flushedWork.size()));
{
- //Update mFlushedIndices based on the index received from flush()
+ // Update mFlushedIndices based on the index received from flush()
ULock l(mQueueLock);
for (std::unique_ptr<C2Work>& work : flushedWork) {
ASSERT_NE(work, nullptr);
frameIndex = work->input.ordinal.frameIndex.peeku();
std::list<uint64_t>::iterator frameIndexIt =
- std::find(mFlushedIndices.begin(), mFlushedIndices.end(),
- frameIndex);
- if (!mFlushedIndices.empty() &&
- (frameIndexIt != mFlushedIndices.end())) {
+ std::find(mFlushedIndices.begin(), mFlushedIndices.end(), frameIndex);
+ if (!mFlushedIndices.empty() && (frameIndexIt != mFlushedIndices.end())) {
mFlushedIndices.erase(frameIndexIt);
work->input.buffers.clear();
work->worklets.clear();
@@ -848,15 +801,15 @@
ASSERT_EQ(mComponent->stop(), C2_OK);
}
-TEST_F(Codec2AudioDecHidlTest, DecodeTestEmptyBuffersInserted) {
+TEST_P(Codec2AudioDecHidlTest, DecodeTestEmptyBuffersInserted) {
description("Decode with multiple empty input frames");
- if (mDisableTest) return;
+ if (mDisableTest) GTEST_SKIP() << "Test is disabled";
char mURL[512], info[512];
std::ifstream eleStream, eleInfo;
- strcpy(mURL, gEnv->getRes().c_str());
- strcpy(info, gEnv->getRes().c_str());
+ strcpy(mURL, sResourceDir.c_str());
+ strcpy(info, sResourceDir.c_str());
GetURLForComponent(mCompName, mURL, info);
eleInfo.open(info);
@@ -871,15 +824,16 @@
// and empty input frames at an interval of 5 frames.
while (1) {
if (!(frameId % 5)) {
- if (!(frameId % 20)) flags = 32;
- else flags = 0;
+ if (!(frameId % 20))
+ flags = 32;
+ else
+ flags = 0;
bytesCount = 0;
} else {
if (!(eleInfo >> bytesCount)) break;
eleInfo >> flags;
eleInfo >> timestamp;
- codecConfig = flags ?
- ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0 : 0;
+ codecConfig = flags ? ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0 : 0;
}
Info.push_back({bytesCount, flags, timestamp});
frameId++;
@@ -890,8 +844,7 @@
bitStreamInfo[0] = 8000;
bitStreamInfo[1] = 1;
} else {
- ASSERT_NO_FATAL_FAILURE(
- getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
+ ASSERT_NO_FATAL_FAILURE(getInputChannelInfo(mComponent, mCompName, bitStreamInfo));
}
if (!setupConfigParam(mComponent, bitStreamInfo)) {
std::cout << "[ WARN ] Test Skipped \n";
@@ -901,40 +854,59 @@
ALOGV("mURL : %s", mURL);
eleStream.open(mURL, std::ifstream::binary);
ASSERT_EQ(eleStream.is_open(), true);
- ASSERT_NO_FATAL_FAILURE(decodeNFrames(
- mComponent, mQueueLock, mQueueCondition, mWorkQueue, mFlushedIndices,
- mLinearPool, eleStream, &Info, 0, (int)Info.size()));
+ ASSERT_NO_FATAL_FAILURE(decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
+ mFlushedIndices, mLinearPool, eleStream, &Info, 0,
+ (int)Info.size()));
// blocking call to ensures application to Wait till all the inputs are
// consumed
if (!mEos) {
ALOGV("Waiting for input consumption");
- ASSERT_NO_FATAL_FAILURE(
- waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
+ ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
}
eleStream.close();
if (mFramesReceived != Info.size()) {
ALOGE("Input buffer count and Output buffer count mismatch");
- ALOGV("framesReceived : %d inputFrames : %zu", mFramesReceived,
- Info.size());
+ ALOGV("framesReceived : %d inputFrames : %zu", mFramesReceived, Info.size());
ASSERT_TRUE(false);
}
ASSERT_EQ(mComponent->stop(), C2_OK);
}
+INSTANTIATE_TEST_SUITE_P(PerInstance, Codec2AudioDecHidlTest, testing::ValuesIn(kTestParameters),
+ android::hardware::PrintInstanceTupleNameToString<>);
+
+// DecodeTest with StreamIndex and EOS / No EOS
+INSTANTIATE_TEST_SUITE_P(StreamIndexAndEOS, Codec2AudioDecDecodeTest,
+ testing::ValuesIn(kDecodeTestParameters),
+ android::hardware::PrintInstanceTupleNameToString<>);
+
} // anonymous namespace
int main(int argc, char** argv) {
- gEnv = new ComponentTestEnvironment();
- ::testing::AddGlobalTestEnvironment(gEnv);
- ::testing::InitGoogleTest(&argc, argv);
- gEnv->init(&argc, argv);
- int status = gEnv->initFromOptions(argc, argv);
- if (status == 0) {
- int status = RUN_ALL_TESTS();
- LOG(INFO) << "C2 Test result = " << status;
+ kTestParameters = getTestParameters(C2Component::DOMAIN_AUDIO, C2Component::KIND_DECODER);
+ for (auto params : kTestParameters) {
+ kDecodeTestParameters.push_back(
+ std::make_tuple(std::get<0>(params), std::get<1>(params), "0", "false"));
+ kDecodeTestParameters.push_back(
+ std::make_tuple(std::get<0>(params), std::get<1>(params), "0", "true"));
+ kDecodeTestParameters.push_back(
+ std::make_tuple(std::get<0>(params), std::get<1>(params), "1", "false"));
+ kDecodeTestParameters.push_back(
+ std::make_tuple(std::get<0>(params), std::get<1>(params), "1", "true"));
}
- return status;
-}
+
+ // Set the resource directory based on command line args.
+ // Test will fail to set up if the argument is not set.
+ for (int i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-P") == 0 && i < argc - 1) {
+ sResourceDir = argv[i + 1];
+ break;
+ }
+ }
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
\ No newline at end of file
diff --git a/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.xml b/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.xml
new file mode 100644
index 0000000..a22f8cf
--- /dev/null
+++ b/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the"License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an"AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs VtsHalMediaC2V1_0TargetAudioDecTest.">
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push-file" key="vts_media_c2_v1_0_audio_dec_test" value="/data/local/tmp/vts_media_c2_v1_0_audio_dec_test" />
+
+ <!-- Files used for audio testing -->
+ <option name="push-file" key="bbb_aac_stereo_128kbps_48000hz.aac" value="/data/local/tmp/media/bbb_aac_stereo_128kbps_48000hz.aac" />
+ <option name="push-file" key="bbb_aac_stereo_128kbps_48000hz.info" value="/data/local/tmp/media/bbb_aac_stereo_128kbps_48000hz.info" />
+ <option name="push-file" key="bbb_aac_stereo_128kbps_48000hz_multi_frame.info" value="/data/local/tmp/media/bbb_aac_stereo_128kbps_48000hz_multi_frame.info" />
+ <option name="push-file" key="bbb_amrwb_1ch_14kbps_16000hz.amrwb" value="/data/local/tmp/media/bbb_amrwb_1ch_14kbps_16000hz.amrwb" />
+ <option name="push-file" key="bbb_amrwb_1ch_14kbps_16000hz.info" value="/data/local/tmp/media/bbb_amrwb_1ch_14kbps_16000hz.info" />
+ <option name="push-file" key="bbb_amrwb_1ch_14kbps_16000hz_multi_frame.info" value="/data/local/tmp/media/bbb_amrwb_1ch_14kbps_16000hz_multi_frame.info" />
+ <option name="push-file" key="bbb_flac_stereo_680kbps_48000hz.flac" value="/data/local/tmp/media/bbb_flac_stereo_680kbps_48000hz.flac" />
+ <option name="push-file" key="bbb_flac_stereo_680kbps_48000hz.info" value="/data/local/tmp/media/bbb_flac_stereo_680kbps_48000hz.info" />
+ <option name="push-file" key="bbb_g711alaw_1ch_8khz.info" value="/data/local/tmp/media/bbb_g711alaw_1ch_8khz.info" />
+ <option name="push-file" key="bbb_g711alaw_1ch_8khz.raw" value="/data/local/tmp/media/bbb_g711alaw_1ch_8khz.raw" />
+ <option name="push-file" key="bbb_g711mulaw_1ch_8khz.info" value="/data/local/tmp/media/bbb_g711mulaw_1ch_8khz.info" />
+ <option name="push-file" key="bbb_g711mulaw_1ch_8khz.raw" value="/data/local/tmp/media/bbb_g711mulaw_1ch_8khz.raw" />
+ <option name="push-file" key="bbb_gsm_1ch_8khz_13kbps.info" value="/data/local/tmp/media/bbb_gsm_1ch_8khz_13kbps.info" />
+ <option name="push-file" key="bbb_gsm_1ch_8khz_13kbps.raw" value="/data/local/tmp/media/bbb_gsm_1ch_8khz_13kbps.raw" />
+ <option name="push-file" key="bbb_mp3_stereo_192kbps_48000hz.info" value="/data/local/tmp/media/bbb_mp3_stereo_192kbps_48000hz.info" />
+ <option name="push-file" key="bbb_mp3_stereo_192kbps_48000hz.mp3" value="/data/local/tmp/media/bbb_mp3_stereo_192kbps_48000hz.mp3" />
+ <option name="push-file" key="bbb_mp3_stereo_192kbps_48000hz_multi_frame.info" value="/data/local/tmp/media/bbb_mp3_stereo_192kbps_48000hz_multi_frame.info" />
+ <option name="push-file" key="bbb_opus_stereo_128kbps_48000hz.info" value="/data/local/tmp/media/bbb_opus_stereo_128kbps_48000hz.info" />
+ <option name="push-file" key="bbb_opus_stereo_128kbps_48000hz.opus" value="/data/local/tmp/media/bbb_opus_stereo_128kbps_48000hz.opus" />
+ <option name="push-file" key="bbb_raw_1ch_8khz_s32le.info" value="/data/local/tmp/media/bbb_raw_1ch_8khz_s32le.info" />
+ <option name="push-file" key="bbb_raw_1ch_8khz_s32le.raw" value="/data/local/tmp/media/bbb_raw_1ch_8khz_s32le.raw" />
+ <option name="push-file" key="bbb_vorbis_stereo_128kbps_48000hz.info" value="/data/local/tmp/media/bbb_vorbis_stereo_128kbps_48000hz.info" />
+ <option name="push-file" key="bbb_vorbis_stereo_128kbps_48000hz.vorbis" value="/data/local/tmp/media/bbb_vorbis_stereo_128kbps_48000hz.vorbis" />
+ <option name="push-file" key="sine_amrnb_1ch_12kbps_8000hz.amrnb" value="/data/local/tmp/media/sine_amrnb_1ch_12kbps_8000hz.amrnb" />
+ <option name="push-file" key="sine_amrnb_1ch_12kbps_8000hz.info" value="/data/local/tmp/media/sine_amrnb_1ch_12kbps_8000hz.info" />
+ <option name="push-file" key="sine_amrnb_1ch_12kbps_8000hz_multi_frame.info" value="/data/local/tmp/media/sine_amrnb_1ch_12kbps_8000hz_multi_frame.info" />
+
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="vts_media_c2_v1_0_audio_dec_test" />
+ <option name="native-test-flag" value="-P /data/local/tmp/media/" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioEncTest.cpp b/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioEncTest.cpp
index ab6bfb2..5f3ae41 100644
--- a/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioEncTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioEncTest.cpp
@@ -19,74 +19,60 @@
#include <android-base/logging.h>
#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
#include <stdio.h>
-#include <fstream>
#include <algorithm>
+#include <fstream>
-#include <codec2/hidl/client.h>
#include <C2AllocatorIon.h>
-#include <C2Config.h>
-#include <C2Debug.h>
#include <C2Buffer.h>
#include <C2BufferPriv.h>
+#include <C2Config.h>
+#include <C2Debug.h>
+#include <codec2/hidl/client.h>
using android::C2AllocatorIon;
-#include <VtsHalHidlTargetTestBase.h>
-#include "media_c2_audio_hidl_test_common.h"
#include "media_c2_hidl_test_common.h"
-class LinearBuffer : public C2Buffer {
- public:
- explicit LinearBuffer(const std::shared_ptr<C2LinearBlock>& block)
- : C2Buffer(
- {block->share(block->offset(), block->size(), ::C2Fence())}) {}
-};
+static std::vector<std::tuple<std::string, std::string, std::string, std::string>>
+ kEncodeTestParameters;
-static ComponentTestEnvironment* gEnv = nullptr;
+// Resource directory
+static std::string sResourceDir = "";
+
+class LinearBuffer : public C2Buffer {
+ public:
+ explicit LinearBuffer(const std::shared_ptr<C2LinearBlock>& block)
+ : C2Buffer({block->share(block->offset(), block->size(), ::C2Fence())}) {}
+};
namespace {
-class Codec2AudioEncHidlTest : public ::testing::VtsHalHidlTargetTestBase {
- private:
- typedef ::testing::VtsHalHidlTargetTestBase Super;
-
- public:
- ::std::string getTestCaseInfo() const override {
- return ::std::string() +
- "Component: " + gEnv->getComponent().c_str() + " | " +
- "Instance: " + gEnv->getInstance().c_str() + " | " +
- "Res: " + gEnv->getRes().c_str();
- }
-
+class Codec2AudioEncHidlTestBase : public ::testing::Test {
+ public:
// google.codec2 Audio test setup
virtual void SetUp() override {
- Super::SetUp();
+ getParams();
mDisableTest = false;
ALOGV("Codec2AudioEncHidlTest SetUp");
mClient = android::Codec2Client::CreateFromService(
- gEnv->getInstance().c_str(),
- !bool(android::Codec2Client::CreateFromService("default", true)));
+ mInstanceName.c_str(),
+ !bool(android::Codec2Client::CreateFromService("default", true)));
ASSERT_NE(mClient, nullptr);
- mListener.reset(new CodecListener(
- [this](std::list<std::unique_ptr<C2Work>>& workItems) {
- handleWorkDone(workItems);
- }));
+ mListener.reset(new CodecListener([this](std::list<std::unique_ptr<C2Work>>& workItems) {
+ handleWorkDone(workItems);
+ }));
ASSERT_NE(mListener, nullptr);
for (int i = 0; i < MAX_INPUT_BUFFERS; ++i) {
mWorkQueue.emplace_back(new C2Work);
}
- mClient->createComponent(gEnv->getComponent().c_str(), mListener,
- &mComponent);
+ mClient->createComponent(mComponentName, mListener, &mComponent);
ASSERT_NE(mComponent, nullptr);
- std::shared_ptr<C2AllocatorStore> store =
- android::GetCodec2PlatformAllocatorStore();
- CHECK_EQ(store->fetchAllocator(C2AllocatorStore::DEFAULT_LINEAR,
- &mLinearAllocator),
- C2_OK);
- mLinearPool = std::make_shared<C2PooledBlockPool>(mLinearAllocator,
- mBlockPoolId++);
+ std::shared_ptr<C2AllocatorStore> store = android::GetCodec2PlatformAllocatorStore();
+ CHECK_EQ(store->fetchAllocator(C2AllocatorStore::DEFAULT_LINEAR, &mLinearAllocator), C2_OK);
+ mLinearPool = std::make_shared<C2PooledBlockPool>(mLinearAllocator, mBlockPoolId++);
ASSERT_NE(mLinearPool, nullptr);
mCompName = unknown_comp;
@@ -95,19 +81,13 @@
standardComp CompName;
};
const StringToName kStringToName[] = {
- {"aac", aac},
- {"flac", flac},
- {"opus", opus},
- {"amrnb", amrnb},
- {"amrwb", amrwb},
+ {"aac", aac}, {"flac", flac}, {"opus", opus}, {"amrnb", amrnb}, {"amrwb", amrwb},
};
- const size_t kNumStringToName =
- sizeof(kStringToName) / sizeof(kStringToName[0]);
+ const size_t kNumStringToName = sizeof(kStringToName) / sizeof(kStringToName[0]);
// Find the component type
- std::string comp = std::string(gEnv->getComponent());
for (size_t i = 0; i < kNumStringToName; ++i) {
- if (strcasestr(comp.c_str(), kStringToName[i].Name)) {
+ if (strcasestr(mComponentName.c_str(), kStringToName[i].Name)) {
mCompName = kStringToName[i].CompName;
break;
}
@@ -126,15 +106,17 @@
mComponent->release();
mComponent = nullptr;
}
- Super::TearDown();
}
+
+ // Get the test parameters from GetParam call.
+ virtual void getParams() {}
+
// callback function to process onWorkDone received by Listener
void handleWorkDone(std::list<std::unique_ptr<C2Work>>& workItems) {
for (std::unique_ptr<C2Work>& work : workItems) {
if (!work->worklets.empty()) {
- workDone(mComponent, work, mFlushedIndices, mQueueLock,
- mQueueCondition, mWorkQueue, mEos, mCsd,
- mFramesReceived);
+ workDone(mComponent, work, mFlushedIndices, mQueueLock, mQueueCondition, mWorkQueue,
+ mEos, mCsd, mFramesReceived);
}
}
}
@@ -147,6 +129,8 @@
unknown_comp,
};
+ std::string mInstanceName;
+ std::string mComponentName;
bool mEos;
bool mCsd;
bool mDisableTest;
@@ -167,7 +151,7 @@
std::shared_ptr<android::Codec2Client::Listener> mListener;
std::shared_ptr<android::Codec2Client::Component> mComponent;
- protected:
+ protected:
static void description(const std::string& description) {
RecordProperty("description", description);
}
@@ -176,9 +160,8 @@
void getInputMaxBufSize() {
int32_t bitStreamInfo[1] = {0};
std::vector<std::unique_ptr<C2Param>> inParams;
- c2_status_t status = mComponent->query(
- {}, {C2StreamMaxBufferSizeInfo::input::PARAM_TYPE}, C2_DONT_BLOCK,
- &inParams);
+ c2_status_t status = mComponent->query({}, {C2StreamMaxBufferSizeInfo::input::PARAM_TYPE},
+ C2_DONT_BLOCK, &inParams);
if (status != C2_OK && inParams.size() == 0) {
ALOGE("Query MaxBufferSizeInfo failed => %d", status);
ASSERT_TRUE(false);
@@ -191,12 +174,19 @@
}
mInputMaxBufSize = bitStreamInfo[0];
}
-
};
-void validateComponent(
- const std::shared_ptr<android::Codec2Client::Component>& component,
- Codec2AudioEncHidlTest::standardComp compName, bool& disableTest) {
+class Codec2AudioEncHidlTest
+ : public Codec2AudioEncHidlTestBase,
+ public ::testing::WithParamInterface<std::tuple<std::string, std::string>> {
+ void getParams() {
+ mInstanceName = std::get<0>(GetParam());
+ mComponentName = std::get<1>(GetParam());
+ }
+};
+
+void validateComponent(const std::shared_ptr<android::Codec2Client::Component>& component,
+ Codec2AudioEncHidlTest::standardComp compName, bool& disableTest) {
// Validate its a C2 Component
if (component->getName().find("c2") == std::string::npos) {
ALOGE("Not a c2 component");
@@ -211,14 +201,12 @@
return;
}
std::vector<std::unique_ptr<C2Param>> queried;
- c2_status_t c2err =
- component->query({}, {C2PortMediaTypeSetting::input::PARAM_TYPE},
- C2_DONT_BLOCK, &queried);
+ c2_status_t c2err = component->query({}, {C2PortMediaTypeSetting::input::PARAM_TYPE},
+ C2_DONT_BLOCK, &queried);
if (c2err != C2_OK && queried.size() == 0) {
ALOGE("Query media type failed => %d", c2err);
} else {
- std::string inputDomain =
- ((C2StreamMediaTypeSetting::input*)queried[0].get())->m.value;
+ std::string inputDomain = ((C2StreamMediaTypeSetting::input*)queried[0].get())->m.value;
if (inputDomain.find("audio/") == std::string::npos) {
ALOGE("Expected Audio Component");
disableTest = true;
@@ -236,16 +224,14 @@
}
// Set Default config param.
-bool setupConfigParam(
- const std::shared_ptr<android::Codec2Client::Component>& component,
- int32_t nChannels, int32_t nSampleRate) {
+bool setupConfigParam(const std::shared_ptr<android::Codec2Client::Component>& component,
+ int32_t nChannels, int32_t nSampleRate) {
std::vector<std::unique_ptr<C2SettingResult>> failures;
C2StreamSampleRateInfo::input sampleRateInfo(0u, nSampleRate);
C2StreamChannelCountInfo::input channelCountInfo(0u, nChannels);
std::vector<C2Param*> configParam{&sampleRateInfo, &channelCountInfo};
- c2_status_t status =
- component->config(configParam, C2_DONT_BLOCK, &failures);
+ c2_status_t status = component->config(configParam, C2_DONT_BLOCK, &failures);
if (status == C2_OK && failures.size() == 0u) return true;
return false;
}
@@ -257,16 +243,11 @@
const char* mURL;
};
static const CompToURL kCompToURL[] = {
- {Codec2AudioEncHidlTest::standardComp::aac,
- "bbb_raw_2ch_48khz_s16le.raw"},
- {Codec2AudioEncHidlTest::standardComp::amrnb,
- "bbb_raw_1ch_8khz_s16le.raw"},
- {Codec2AudioEncHidlTest::standardComp::amrwb,
- "bbb_raw_1ch_16khz_s16le.raw"},
- {Codec2AudioEncHidlTest::standardComp::flac,
- "bbb_raw_2ch_48khz_s16le.raw"},
- {Codec2AudioEncHidlTest::standardComp::opus,
- "bbb_raw_2ch_48khz_s16le.raw"},
+ {Codec2AudioEncHidlTest::standardComp::aac, "bbb_raw_2ch_48khz_s16le.raw"},
+ {Codec2AudioEncHidlTest::standardComp::amrnb, "bbb_raw_1ch_8khz_s16le.raw"},
+ {Codec2AudioEncHidlTest::standardComp::amrwb, "bbb_raw_1ch_16khz_s16le.raw"},
+ {Codec2AudioEncHidlTest::standardComp::flac, "bbb_raw_2ch_48khz_s16le.raw"},
+ {Codec2AudioEncHidlTest::standardComp::opus, "bbb_raw_2ch_48khz_s16le.raw"},
};
for (size_t i = 0; i < sizeof(kCompToURL) / sizeof(kCompToURL[0]); ++i) {
@@ -278,21 +259,18 @@
}
void encodeNFrames(const std::shared_ptr<android::Codec2Client::Component>& component,
- std::mutex &queueLock, std::condition_variable& queueCondition,
+ std::mutex& queueLock, std::condition_variable& queueCondition,
std::list<std::unique_ptr<C2Work>>& workQueue,
- std::list<uint64_t>& flushedIndices,
- std::shared_ptr<C2BlockPool>& linearPool,
- std::ifstream& eleStream, uint32_t nFrames,
- int32_t samplesPerFrame, int32_t nChannels,
- int32_t nSampleRate, bool flushed = false,
+ std::list<uint64_t>& flushedIndices, std::shared_ptr<C2BlockPool>& linearPool,
+ std::ifstream& eleStream, uint32_t nFrames, int32_t samplesPerFrame,
+ int32_t nChannels, int32_t nSampleRate, bool flushed = false,
bool signalEOS = true) {
typedef std::unique_lock<std::mutex> ULock;
uint32_t frameID = 0;
uint32_t maxRetry = 0;
int bytesCount = samplesPerFrame * nChannels * 2;
- int32_t timestampIncr =
- (int)(((float)samplesPerFrame / nSampleRate) * 1000000);
+ int32_t timestampIncr = (int)(((float)samplesPerFrame / nSampleRate) * 1000000);
uint64_t timestamp = 0;
while (1) {
if (nFrames == 0) break;
@@ -312,8 +290,7 @@
if (!work && (maxRetry >= MAX_RETRY)) {
ASSERT_TRUE(false) << "Wait for generating C2Work exceeded timeout";
}
- if (signalEOS && (nFrames == 1))
- flags |= C2FrameData::FLAG_END_OF_STREAM;
+ if (signalEOS && (nFrames == 1)) flags |= C2FrameData::FLAG_END_OF_STREAM;
if (flushed) {
flags |= SYNC_FRAME;
flushed = false;
@@ -330,10 +307,9 @@
eleStream.read(data, bytesCount);
ASSERT_EQ(eleStream.gcount(), bytesCount);
std::shared_ptr<C2LinearBlock> block;
- ASSERT_EQ(C2_OK, linearPool->fetchLinearBlock(
- bytesCount, {C2MemoryUsage::CPU_READ,
- C2MemoryUsage::CPU_WRITE},
- &block));
+ ASSERT_EQ(C2_OK,
+ linearPool->fetchLinearBlock(
+ bytesCount, {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}, &block));
ASSERT_TRUE(block);
// Write View
C2WriteView view = block->map().get();
@@ -365,27 +341,33 @@
}
}
-TEST_F(Codec2AudioEncHidlTest, validateCompName) {
- if (mDisableTest) return;
+TEST_P(Codec2AudioEncHidlTest, validateCompName) {
+ if (mDisableTest) GTEST_SKIP() << "Test is disabled";
ALOGV("Checks if the given component is a valid audio component");
validateComponent(mComponent, mCompName, mDisableTest);
ASSERT_EQ(mDisableTest, false);
}
class Codec2AudioEncEncodeTest
- : public Codec2AudioEncHidlTest,
- public ::testing::WithParamInterface<std::pair<bool, int32_t>> {
+ : public Codec2AudioEncHidlTestBase,
+ public ::testing::WithParamInterface<
+ std::tuple<std::string, std::string, std::string, std::string>> {
+ void getParams() {
+ mInstanceName = std::get<0>(GetParam());
+ mComponentName = std::get<1>(GetParam());
+ }
};
TEST_P(Codec2AudioEncEncodeTest, EncodeTest) {
ALOGV("EncodeTest");
- if (mDisableTest) return;
+ if (mDisableTest) GTEST_SKIP() << "Test is disabled";
char mURL[512];
- strcpy(mURL, gEnv->getRes().c_str());
+ strcpy(mURL, sResourceDir.c_str());
GetURLForComponent(mCompName, mURL);
- bool signalEOS = GetParam().first;
+ bool signalEOS = !std::get<2>(GetParam()).compare("true");
// Ratio w.r.t to mInputMaxBufSize
- int32_t inputMaxBufRatio = GetParam().second;
+ int32_t inputMaxBufRatio = std::stoi(std::get<3>(GetParam()));
+ ;
// Setting default sampleRate
int32_t nChannels = 2;
@@ -414,10 +396,9 @@
default:
ASSERT_TRUE(false);
}
- int32_t samplesPerFrame =
- ((mInputMaxBufSize / inputMaxBufRatio) / (nChannels * 2));
- ALOGV("signalEOS %d mInputMaxBufSize %d samplesPerFrame %d", signalEOS,
- mInputMaxBufSize, samplesPerFrame);
+ int32_t samplesPerFrame = ((mInputMaxBufSize / inputMaxBufRatio) / (nChannels * 2));
+ ALOGV("signalEOS %d mInputMaxBufSize %d samplesPerFrame %d", signalEOS, mInputMaxBufSize,
+ samplesPerFrame);
if (!setupConfigParam(mComponent, nChannels, nSampleRate)) {
std::cout << "[ WARN ] Test Skipped \n";
@@ -429,26 +410,21 @@
eleStream.open(mURL, std::ifstream::binary);
ASSERT_EQ(eleStream.is_open(), true);
ALOGV("mURL : %s", mURL);
- ASSERT_NO_FATAL_FAILURE(
- encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
- mFlushedIndices, mLinearPool, eleStream, numFrames,
- samplesPerFrame, nChannels, nSampleRate, false,
- signalEOS));
+ ASSERT_NO_FATAL_FAILURE(encodeNFrames(
+ mComponent, mQueueLock, mQueueCondition, mWorkQueue, mFlushedIndices, mLinearPool,
+ eleStream, numFrames, samplesPerFrame, nChannels, nSampleRate, false, signalEOS));
// If EOS is not sent, sending empty input with EOS flag
if (!signalEOS) {
- ASSERT_NO_FATAL_FAILURE(
- waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue, 1));
- ASSERT_NO_FATAL_FAILURE(
- testInputBuffer(mComponent, mQueueLock, mWorkQueue,
- C2FrameData::FLAG_END_OF_STREAM, false));
+ ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue, 1));
+ ASSERT_NO_FATAL_FAILURE(testInputBuffer(mComponent, mQueueLock, mWorkQueue,
+ C2FrameData::FLAG_END_OF_STREAM, false));
numFrames += 1;
}
// blocking call to ensures application to Wait till all the inputs are
// consumed
- ASSERT_NO_FATAL_FAILURE(
- waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
+ ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
eleStream.close();
if (mFramesReceived != numFrames) {
ALOGE("Input buffer count and Output buffer count mismatch");
@@ -465,18 +441,9 @@
ASSERT_EQ(mComponent->stop(), C2_OK);
}
-// EncodeTest with EOS / No EOS and inputMaxBufRatio
-// inputMaxBufRatio is ratio w.r.t. to mInputMaxBufSize
-INSTANTIATE_TEST_CASE_P(EncodeTest, Codec2AudioEncEncodeTest,
- ::testing::Values(std::make_pair(false, 1),
- std::make_pair(false, 2),
- std::make_pair(true, 1),
- std::make_pair(true, 2)));
-
-
-TEST_F(Codec2AudioEncHidlTest, EOSTest) {
+TEST_P(Codec2AudioEncHidlTest, EOSTest) {
description("Test empty input buffer with EOS flag");
- if (mDisableTest) return;
+ if (mDisableTest) GTEST_SKIP() << "Test is disabled";
ASSERT_EQ(mComponent->start(), C2_OK);
typedef std::unique_lock<std::mutex> ULock;
@@ -514,13 +481,13 @@
ASSERT_EQ(mComponent->stop(), C2_OK);
}
-TEST_F(Codec2AudioEncHidlTest, FlushTest) {
+TEST_P(Codec2AudioEncHidlTest, FlushTest) {
description("Test Request for flush");
- if (mDisableTest) return;
+ if (mDisableTest) GTEST_SKIP() << "Test is disabled";
typedef std::unique_lock<std::mutex> ULock;
char mURL[512];
- strcpy(mURL, gEnv->getRes().c_str());
+ strcpy(mURL, sResourceDir.c_str());
GetURLForComponent(mCompName, mURL);
// Setting default configuration
@@ -570,29 +537,24 @@
eleStream.open(mURL, std::ifstream::binary);
ASSERT_EQ(eleStream.is_open(), true);
ALOGV("mURL : %s", mURL);
- ASSERT_NO_FATAL_FAILURE(
- encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
- mFlushedIndices, mLinearPool, eleStream, numFramesFlushed,
- samplesPerFrame, nChannels, nSampleRate));
+ ASSERT_NO_FATAL_FAILURE(encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
+ mFlushedIndices, mLinearPool, eleStream, numFramesFlushed,
+ samplesPerFrame, nChannels, nSampleRate));
std::list<std::unique_ptr<C2Work>> flushedWork;
- c2_status_t err =
- mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
+ c2_status_t err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
ASSERT_EQ(err, C2_OK);
- ASSERT_NO_FATAL_FAILURE(
- waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
- (size_t)MAX_INPUT_BUFFERS - flushedWork.size()));
+ ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
+ (size_t)MAX_INPUT_BUFFERS - flushedWork.size()));
uint64_t frameIndex;
{
- //Update mFlushedIndices based on the index received from flush()
+ // Update mFlushedIndices based on the index received from flush()
ULock l(mQueueLock);
for (std::unique_ptr<C2Work>& work : flushedWork) {
ASSERT_NE(work, nullptr);
frameIndex = work->input.ordinal.frameIndex.peeku();
std::list<uint64_t>::iterator frameIndexIt =
- std::find(mFlushedIndices.begin(), mFlushedIndices.end(),
- frameIndex);
- if (!mFlushedIndices.empty() &&
- (frameIndexIt != mFlushedIndices.end())) {
+ std::find(mFlushedIndices.begin(), mFlushedIndices.end(), frameIndex);
+ if (!mFlushedIndices.empty() && (frameIndexIt != mFlushedIndices.end())) {
mFlushedIndices.erase(frameIndexIt);
work->input.buffers.clear();
work->worklets.clear();
@@ -601,29 +563,24 @@
}
}
mFlushedIndices.clear();
- ASSERT_NO_FATAL_FAILURE(
- encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
- mFlushedIndices, mLinearPool, eleStream,
- numFrames - numFramesFlushed, samplesPerFrame,
- nChannels, nSampleRate, true));
+ ASSERT_NO_FATAL_FAILURE(encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
+ mFlushedIndices, mLinearPool, eleStream,
+ numFrames - numFramesFlushed, samplesPerFrame, nChannels,
+ nSampleRate, true));
eleStream.close();
- err =
- mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
+ err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
ASSERT_EQ(err, C2_OK);
- ASSERT_NO_FATAL_FAILURE(
- waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
- (size_t)MAX_INPUT_BUFFERS - flushedWork.size()));
+ ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
+ (size_t)MAX_INPUT_BUFFERS - flushedWork.size()));
{
- //Update mFlushedIndices based on the index received from flush()
+ // Update mFlushedIndices based on the index received from flush()
ULock l(mQueueLock);
for (std::unique_ptr<C2Work>& work : flushedWork) {
ASSERT_NE(work, nullptr);
frameIndex = work->input.ordinal.frameIndex.peeku();
std::list<uint64_t>::iterator frameIndexIt =
- std::find(mFlushedIndices.begin(), mFlushedIndices.end(),
- frameIndex);
- if (!mFlushedIndices.empty() &&
- (frameIndexIt != mFlushedIndices.end())) {
+ std::find(mFlushedIndices.begin(), mFlushedIndices.end(), frameIndex);
+ if (!mFlushedIndices.empty() && (frameIndexIt != mFlushedIndices.end())) {
mFlushedIndices.erase(frameIndexIt);
work->input.buffers.clear();
work->worklets.clear();
@@ -635,17 +592,39 @@
ASSERT_EQ(mComponent->stop(), C2_OK);
}
+INSTANTIATE_TEST_SUITE_P(PerInstance, Codec2AudioEncHidlTest, testing::ValuesIn(kTestParameters),
+ android::hardware::PrintInstanceTupleNameToString<>);
+
+// EncodeTest with EOS / No EOS and inputMaxBufRatio
+// inputMaxBufRatio is ratio w.r.t. to mInputMaxBufSize
+INSTANTIATE_TEST_SUITE_P(EncodeTest, Codec2AudioEncEncodeTest,
+ testing::ValuesIn(kEncodeTestParameters),
+ android::hardware::PrintInstanceTupleNameToString<>);
+
} // anonymous namespace
int main(int argc, char** argv) {
- gEnv = new ComponentTestEnvironment();
- ::testing::AddGlobalTestEnvironment(gEnv);
- ::testing::InitGoogleTest(&argc, argv);
- gEnv->init(&argc, argv);
- int status = gEnv->initFromOptions(argc, argv);
- if (status == 0) {
- int status = RUN_ALL_TESTS();
- LOG(INFO) << "C2 Test result = " << status;
+ kTestParameters = getTestParameters(C2Component::DOMAIN_AUDIO, C2Component::KIND_ENCODER);
+ for (auto params : kTestParameters) {
+ kEncodeTestParameters.push_back(
+ std::make_tuple(std::get<0>(params), std::get<1>(params), "false", "1"));
+ kEncodeTestParameters.push_back(
+ std::make_tuple(std::get<0>(params), std::get<1>(params), "false", "2"));
+ kEncodeTestParameters.push_back(
+ std::make_tuple(std::get<0>(params), std::get<1>(params), "true", "1"));
+ kEncodeTestParameters.push_back(
+ std::make_tuple(std::get<0>(params), std::get<1>(params), "true", "2"));
}
- return status;
+
+ // Set the resource directory based on command line args.
+ // Test will fail to set up if the argument is not set.
+ for (int i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-P") == 0 && i < argc - 1) {
+ sResourceDir = argv[i + 1];
+ break;
+ }
+ }
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
}
diff --git a/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioEncTest.xml b/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioEncTest.xml
new file mode 100644
index 0000000..2e37111
--- /dev/null
+++ b/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioEncTest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the"License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an"AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs VtsHalMediaC2V1_0TargetAudioEncTest.">
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push-file" key="vts_media_c2_v1_0_audio_enc_test" value="/data/local/tmp/vts_media_c2_v1_0_audio_enc_test" />
+
+ <!-- Files used for audio testing -->
+ <option name="push-file" key="bbb_raw_2ch_48khz_s16le.raw" value="/data/local/tmp/media/bbb_raw_2ch_48khz_s16le.raw" />
+ <option name="push-file" key="bbb_raw_1ch_8khz_s16le.raw" value="/data/local/tmp/media/bbb_raw_1ch_8khz_s16le.raw" />
+ <option name="push-file" key="bbb_raw_1ch_16khz_s16le.raw" value="/data/local/tmp/media/bbb_raw_1ch_16khz_s16le.raw" />
+ <option name="push-file" key="bbb_raw_2ch_48khz_s16le.raw" value="/data/local/tmp/media/bbb_raw_2ch_48khz_s16le.raw" />
+ <option name="push-file" key="bbb_raw_2ch_48khz_s16le.raw" value="/data/local/tmp/media/bbb_raw_2ch_48khz_s16le.raw" />
+
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="vts_media_c2_v1_0_audio_enc_test" />
+ <option name="native-test-flag" value="-P /data/local/tmp/media/" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/media/codec2/hidl/1.0/vts/functional/audio/media_c2_audio_hidl_test_common.h b/media/codec2/hidl/1.0/vts/functional/audio/media_c2_audio_hidl_test_common.h
deleted file mode 100644
index 4d773ce..0000000
--- a/media/codec2/hidl/1.0/vts/functional/audio/media_c2_audio_hidl_test_common.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MEDIA_C2_AUDIO_HIDL_TEST_COMMON_H
-#define MEDIA_C2_AUDIO_HIDL_TEST_COMMON_H
-
-
-#endif // MEDIA_C2_AUDIO_HIDL_TEST_COMMON_H
diff --git a/media/codec2/hidl/1.0/vts/functional/common/Android.bp b/media/codec2/hidl/1.0/vts/functional/common/Android.bp
index a011ba3..f9ec5ae 100644
--- a/media/codec2/hidl/1.0/vts/functional/common/Android.bp
+++ b/media/codec2/hidl/1.0/vts/functional/common/Android.bp
@@ -24,10 +24,14 @@
],
static_libs: [
+ "libgtest",
"VtsHalMediaC2V1_0CommonUtil",
],
shared_libs: [
"libcodec2_client",
],
+ test_suites: [
+ "vts",
+ ],
}
diff --git a/media/codec2/hidl/1.0/vts/functional/common/README.md b/media/codec2/hidl/1.0/vts/functional/common/README.md
index f2f579c..ac510c5 100644
--- a/media/codec2/hidl/1.0/vts/functional/common/README.md
+++ b/media/codec2/hidl/1.0/vts/functional/common/README.md
@@ -3,34 +3,22 @@
## master :
Functionality of master is to enumerate all the Codec2 components available in C2 media service.
-usage: `VtsHalMediaC2V1_0TargetMasterTest -I default`
+usage: `atest VtsHalMediaC2V1_0TargetMasterTest`
## component :
Functionality of component test is to validate common functionality across all the Codec2 components available in C2 media service. For a standard C2 component, these tests are expected to pass.
-usage: `VtsHalMediaC2V1_0TargetComponentTest -I software -C <comp name>`
-
-example: `VtsHalMediaC2V1_0TargetComponentTest -I software -C c2.android.vorbis.decoder`
+usage: `atest VtsHalMediaC2V1_0TargetComponentTest`
## audio :
Functionality of audio test is to validate audio specific functionality of Codec2 components. The resource files for this test are taken from `frameworks/av/media/codec2/hidl/1.0/vts/functional/res`. The path to these files on the device can be specified with `-P`. (If the device path is omitted, `/data/local/tmp/media/` is the default value.)
-usage: `VtsHalMediaC2V1_0TargetAudioDecTest -I default -C <comp name> -P <path to resource files>`
+usage: `atest VtsHalMediaC2V1_0TargetAudioDecTest`
-usage: `VtsHalMediaC2V1_0TargetAudioEncTest -I software -C <comp name> -P <path to resource files>`
-
-example: `VtsHalMediaC2V1_0TargetAudioDecTest -I software -C c2.android.flac.decoder -P /data/local/tmp/media/`
-
-example: `VtsHalMediaC2V1_0TargetAudioEncTest -I software -C c2.android.opus.encoder -P /data/local/tmp/media/`
+usage: `atest VtsHalMediaC2V1_0TargetAudioEncTest`
## video :
Functionality of video test is to validate video specific functionality of Codec2 components. The resource files for this test are taken from `frameworks/av/media/codec2/hidl/1.0/vts/functional/res`. The path to these files on the device can be specified with `-P`. (If the device path is omitted, `/data/local/tmp/media/` is the default value.)
-usage: `VtsHalMediaC2V1_0TargetVideoDecTest -I default -C <comp name> -P <path to resource files>`
-
-usage: `VtsHalMediaC2V1_0TargetVideoEncTest -I software -C <comp name> -P <path to resource files>`
-
-example: `VtsHalMediaC2V1_0TargetVideoDecTest -I software -C c2.android.avc.decoder -P /data/local/tmp/media/`
-
-example: `VtsHalMediaC2V1_0TargetVideoEncTest -I software -C c2.android.vp9.encoder -P /data/local/tmp/media/`
-
+usage: `atest VtsHalMediaC2V1_0TargetVideoDecTest`
+usage: `atest VtsHalMediaC2V1_0TargetVideoEncTest`
diff --git a/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp
index 2f02913..5d4b001 100644
--- a/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp
@@ -22,16 +22,10 @@
#include <android/hardware/media/c2/1.0/IComponentStore.h>
-void ComponentTestEnvironment::registerTestServices() {
- registerTestService<::android::hardware::media::c2::V1_0::
- IComponentStore>();
-}
-
// Test the codecs for NullBuffer, Empty Input Buffer with(out) flags set
-void testInputBuffer(
- const std::shared_ptr<android::Codec2Client::Component>& component,
- std::mutex& queueLock, std::list<std::unique_ptr<C2Work>>& workQueue,
- uint32_t flags, bool isNullBuffer) {
+void testInputBuffer(const std::shared_ptr<android::Codec2Client::Component>& component,
+ std::mutex& queueLock, std::list<std::unique_ptr<C2Work>>& workQueue,
+ uint32_t flags, bool isNullBuffer) {
std::unique_ptr<C2Work> work;
{
typedef std::unique_lock<std::mutex> ULock;
@@ -61,10 +55,8 @@
}
// Wait for all the inputs to be consumed by the plugin.
-void waitOnInputConsumption(std::mutex& queueLock,
- std::condition_variable& queueCondition,
- std::list<std::unique_ptr<C2Work>>& workQueue,
- size_t bufferCount) {
+void waitOnInputConsumption(std::mutex& queueLock, std::condition_variable& queueCondition,
+ std::list<std::unique_ptr<C2Work>>& workQueue, size_t bufferCount) {
typedef std::unique_lock<std::mutex> ULock;
uint32_t queueSize;
uint32_t maxRetry = 0;
@@ -85,29 +77,25 @@
}
// process onWorkDone received by Listener
-void workDone(
- const std::shared_ptr<android::Codec2Client::Component>& component,
- std::unique_ptr<C2Work>& work, std::list<uint64_t>& flushedIndices,
- std::mutex& queueLock, std::condition_variable& queueCondition,
- std::list<std::unique_ptr<C2Work>>& workQueue, bool& eos, bool& csd,
- uint32_t& framesReceived) {
+void workDone(const std::shared_ptr<android::Codec2Client::Component>& component,
+ std::unique_ptr<C2Work>& work, std::list<uint64_t>& flushedIndices,
+ std::mutex& queueLock, std::condition_variable& queueCondition,
+ std::list<std::unique_ptr<C2Work>>& workQueue, bool& eos, bool& csd,
+ uint32_t& framesReceived) {
// handle configuration changes in work done
if (work->worklets.front()->output.configUpdate.size() != 0) {
ALOGV("Config Update");
std::vector<std::unique_ptr<C2Param>> updates =
- std::move(work->worklets.front()->output.configUpdate);
+ std::move(work->worklets.front()->output.configUpdate);
std::vector<C2Param*> configParam;
std::vector<std::unique_ptr<C2SettingResult>> failures;
for (size_t i = 0; i < updates.size(); ++i) {
C2Param* param = updates[i].get();
if (param->index() == C2StreamInitDataInfo::output::PARAM_TYPE) {
csd = true;
- } else if ((param->index() ==
- C2StreamSampleRateInfo::output::PARAM_TYPE) ||
- (param->index() ==
- C2StreamChannelCountInfo::output::PARAM_TYPE) ||
- (param->index() ==
- C2StreamPictureSizeInfo::output::PARAM_TYPE)) {
+ } else if ((param->index() == C2StreamSampleRateInfo::output::PARAM_TYPE) ||
+ (param->index() == C2StreamChannelCountInfo::output::PARAM_TYPE) ||
+ (param->index() == C2StreamPictureSizeInfo::output::PARAM_TYPE)) {
configParam.push_back(param);
}
}
@@ -116,8 +104,7 @@
}
if (work->worklets.front()->output.flags != C2FrameData::FLAG_INCOMPLETE) {
framesReceived++;
- eos = (work->worklets.front()->output.flags &
- C2FrameData::FLAG_END_OF_STREAM) != 0;
+ eos = (work->worklets.front()->output.flags & C2FrameData::FLAG_END_OF_STREAM) != 0;
auto frameIndexIt = std::find(flushedIndices.begin(), flushedIndices.end(),
work->input.ordinal.frameIndex.peeku());
ALOGV("WorkDone: frameID received %d",
@@ -143,3 +130,33 @@
return (int64_t)tv.tv_usec + tv.tv_sec * 1000000ll;
}
+
+// Return all test parameters, a list of tuple of <instance, component>
+const std::vector<std::tuple<std::string, std::string>>& getTestParameters() {
+ return getTestParameters(C2Component::DOMAIN_OTHER, C2Component::KIND_OTHER);
+}
+
+// Return all test parameters, a list of tuple of <instance, component> with matching domain and
+// kind.
+const std::vector<std::tuple<std::string, std::string>>& getTestParameters(
+ C2Component::domain_t domain, C2Component::kind_t kind) {
+ static std::vector<std::tuple<std::string, std::string>> parameters;
+
+ auto instances = android::Codec2Client::GetServiceNames();
+ for (std::string instance : instances) {
+ std::shared_ptr<android::Codec2Client> client =
+ android::Codec2Client::CreateFromService(instance.c_str());
+ std::vector<C2Component::Traits> components = client->listComponents();
+ for (C2Component::Traits traits : components) {
+ if (instance.compare(traits.owner)) continue;
+ if (domain != C2Component::DOMAIN_OTHER &&
+ (traits.domain != domain || traits.kind != kind)) {
+ continue;
+ }
+
+ parameters.push_back(std::make_tuple(instance, traits.name));
+ }
+ }
+
+ return parameters;
+}
\ No newline at end of file
diff --git a/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h
index 23e332a..4b5e0a6 100644
--- a/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h
+++ b/media/codec2/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h
@@ -22,31 +22,34 @@
#include <codec2/hidl/client.h>
#include <getopt.h>
+#include <gtest/gtest.h>
#include <hidl/HidlSupport.h>
-#include <VtsHalHidlTargetTestEnvBase.h>
+#include <chrono>
#define MAX_RETRY 20
#define TIME_OUT 400ms
#define MAX_INPUT_BUFFERS 8
-using ::android::hardware::Void;
-using ::android::hardware::Return;
-using ::android::hardware::hidl_vec;
using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+
+using namespace ::std::chrono;
+
+static std::vector<std::tuple<std::string, std::string>> kTestParameters;
/*
* Handle Callback functions onWorkDone(), onTripped(),
* onError(), onDeath(), onFramesRendered()
*/
struct CodecListener : public android::Codec2Client::Listener {
- public:
+ public:
CodecListener(
- const std::function<void(std::list<std::unique_ptr<C2Work>>& workItems)> fn =
- nullptr)
+ const std::function<void(std::list<std::unique_ptr<C2Work>>& workItems)> fn = nullptr)
: callBack(fn) {}
- virtual void onWorkDone(
- const std::weak_ptr<android::Codec2Client::Component>& comp,
- std::list<std::unique_ptr<C2Work>>& workItems) override {
+ virtual void onWorkDone(const std::weak_ptr<android::Codec2Client::Component>& comp,
+ std::list<std::unique_ptr<C2Work>>& workItems) override {
/* TODO */
ALOGD("onWorkDone called");
(void)comp;
@@ -54,40 +57,34 @@
}
virtual void onTripped(
- const std::weak_ptr<android::Codec2Client::Component>& comp,
- const std::vector<std::shared_ptr<C2SettingResult>>& settingResults)
- override {
+ const std::weak_ptr<android::Codec2Client::Component>& comp,
+ const std::vector<std::shared_ptr<C2SettingResult>>& settingResults) override {
/* TODO */
(void)comp;
(void)settingResults;
}
- virtual void onError(
- const std::weak_ptr<android::Codec2Client::Component>& comp,
- uint32_t errorCode) override {
+ virtual void onError(const std::weak_ptr<android::Codec2Client::Component>& comp,
+ uint32_t errorCode) override {
/* TODO */
(void)comp;
ALOGD("onError called");
if (errorCode != 0) ALOGE("Error : %u", errorCode);
}
- virtual void onDeath(
- const std::weak_ptr<android::Codec2Client::Component>& comp) override {
+ virtual void onDeath(const std::weak_ptr<android::Codec2Client::Component>& comp) override {
/* TODO */
(void)comp;
}
- virtual void onInputBufferDone(
- uint64_t frameIndex, size_t arrayIndex) override {
+ virtual void onInputBufferDone(uint64_t frameIndex, size_t arrayIndex) override {
/* TODO */
(void)frameIndex;
(void)arrayIndex;
}
- virtual void onFrameRendered(
- uint64_t bufferQueueId,
- int32_t slotId,
- int64_t timestampNs) override {
+ virtual void onFrameRendered(uint64_t bufferQueueId, int32_t slotId,
+ int64_t timestampNs) override {
/* TODO */
(void)bufferQueueId;
(void)slotId;
@@ -99,96 +96,30 @@
std::function<void(std::list<std::unique_ptr<C2Work>>& workItems)> callBack;
};
-// A class for test environment setup
-class ComponentTestEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
- private:
- typedef ::testing::VtsHalHidlTargetTestEnvBase Super;
+// Return all test parameters, a list of tuple of <instance, component>.
+const std::vector<std::tuple<std::string, std::string>>& getTestParameters();
- public:
- virtual void registerTestServices() override;
-
- ComponentTestEnvironment() : res("/data/local/tmp/media/") {}
-
- void setComponent(const char* _component) { component = _component; }
-
- void setInstance(const char* _instance) { instance = _instance; }
-
- void setRes(const char* _res) { res = _res; }
-
- const hidl_string getInstance() const { return instance; }
-
- const hidl_string getComponent() const { return component; }
-
- const hidl_string getRes() const { return res; }
-
- int initFromOptions(int argc, char** argv) {
- static struct option options[] = {
- {"instance", required_argument, 0, 'I'},
- {"component", required_argument, 0, 'C'},
- {"res", required_argument, 0, 'P'},
- {0, 0, 0, 0}};
-
- while (true) {
- int index = 0;
- int c = getopt_long(argc, argv, "I:C:P:", options, &index);
- if (c == -1) {
- break;
- }
-
- switch (c) {
- case 'I':
- setInstance(optarg);
- break;
- case 'C':
- setComponent(optarg);
- break;
- case 'P':
- setRes(optarg);
- break;
- case '?':
- break;
- }
- }
-
- if (optind < argc) {
- fprintf(stderr,
- "unrecognized option: %s\n\n"
- "usage: %s <gtest options> <test options>\n\n"
- "test options are:\n\n"
- "-I, --instance: software for C2 components, else default\n"
- "-C, --component: C2 component to test\n"
- "-P, --res: Resource files directory location\n",
- argv[optind ?: 1], argv[0]);
- return 2;
- }
- return 0;
- }
-
- private:
- hidl_string instance;
- hidl_string component;
- hidl_string res;
-};
+// Return all test parameters, a list of tuple of <instance, component> with matching domain and
+// kind.
+const std::vector<std::tuple<std::string, std::string>>& getTestParameters(
+ C2Component::domain_t domain, C2Component::kind_t kind);
/*
* common functions declarations
*/
-void testInputBuffer(
- const std::shared_ptr<android::Codec2Client::Component>& component,
- std::mutex& queueLock, std::list<std::unique_ptr<C2Work>>& workQueue,
- uint32_t flags, bool isNullBuffer);
+void testInputBuffer(const std::shared_ptr<android::Codec2Client::Component>& component,
+ std::mutex& queueLock, std::list<std::unique_ptr<C2Work>>& workQueue,
+ uint32_t flags, bool isNullBuffer);
-void waitOnInputConsumption(std::mutex& queueLock,
- std::condition_variable& queueCondition,
+void waitOnInputConsumption(std::mutex& queueLock, std::condition_variable& queueCondition,
std::list<std::unique_ptr<C2Work>>& workQueue,
size_t bufferCount = MAX_INPUT_BUFFERS);
-void workDone(
- const std::shared_ptr<android::Codec2Client::Component>& component,
- std::unique_ptr<C2Work>& work, std::list<uint64_t>& flushedIndices,
- std::mutex& queueLock, std::condition_variable& queueCondition,
- std::list<std::unique_ptr<C2Work>>& workQueue, bool& eos, bool& csd,
- uint32_t& framesReceived);
+void workDone(const std::shared_ptr<android::Codec2Client::Component>& component,
+ std::unique_ptr<C2Work>& work, std::list<uint64_t>& flushedIndices,
+ std::mutex& queueLock, std::condition_variable& queueCondition,
+ std::list<std::unique_ptr<C2Work>>& workQueue, bool& eos, bool& csd,
+ uint32_t& framesReceived);
int64_t getNowUs();
diff --git a/media/codec2/hidl/1.0/vts/functional/component/VtsHalMediaC2V1_0TargetComponentTest.cpp b/media/codec2/hidl/1.0/vts/functional/component/VtsHalMediaC2V1_0TargetComponentTest.cpp
index 9dc541c..6122225 100644
--- a/media/codec2/hidl/1.0/vts/functional/component/VtsHalMediaC2V1_0TargetComponentTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/component/VtsHalMediaC2V1_0TargetComponentTest.cpp
@@ -19,11 +19,11 @@
#include <android-base/logging.h>
#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
#include <C2Config.h>
#include <codec2/hidl/client.h>
-#include <VtsHalHidlTargetTestBase.h>
#include "media_c2_hidl_test_common.h"
/* Time_Out for start(), stop(), reset(), release(), flush(), queue() are
@@ -31,50 +31,45 @@
* extra in case of timeout is 500ms, 1ms extra in case timeout is 1ms/5ms. All
* timeout is calculated in us.
*/
-#define START_TIME_OUT 550000
-#define STOP_TIME_OUT 550000
-#define RESET_TIME_OUT 550000
-#define RELEASE_TIME_OUT 550000
-#define FLUSH_TIME_OUT 6000
-#define QUEUE_TIME_OUT 2000
+#define START_TIME_OUT 550000
+#define STOP_TIME_OUT 550000
+#define RESET_TIME_OUT 550000
+#define RELEASE_TIME_OUT 550000
+#define FLUSH_TIME_OUT 6000
+#define QUEUE_TIME_OUT 2000
// Time_Out for config(), query(), querySupportedParams() are defined in
// hardware/interfaces/media/c2/1.0/IConfigurable.hal.
-#define CONFIG_TIME_OUT 6000
-#define QUERY_TIME_OUT 6000
-#define QUERYSUPPORTEDPARAMS_TIME_OUT 2000
+#define CONFIG_TIME_OUT 6000
+#define QUERY_TIME_OUT 6000
+#define QUERYSUPPORTEDPARAMS_TIME_OUT 2000
-#define CHECK_TIMEOUT(timeConsumed, TIME_OUT, FuncName) \
- if (timeConsumed > TIME_OUT) { \
- ALOGW( \
- "TIMED_OUT %s timeConsumed=%" PRId64 " us is " \
- "greater than threshold %d us", \
- FuncName, timeConsumed, TIME_OUT); \
+#define CHECK_TIMEOUT(timeConsumed, TIME_OUT, FuncName) \
+ if (timeConsumed > TIME_OUT) { \
+ ALOGW("TIMED_OUT %s timeConsumed=%" PRId64 \
+ " us is " \
+ "greater than threshold %d us", \
+ FuncName, timeConsumed, TIME_OUT); \
}
-static ComponentTestEnvironment* gEnv = nullptr;
-
namespace {
-// google.codec2 Component test setup
-class Codec2ComponentHidlTest : public ::testing::VtsHalHidlTargetTestBase {
- private:
- typedef ::testing::VtsHalHidlTargetTestBase Super;
+static std::vector<std::tuple<std::string, std::string, std::string, std::string>>
+ kInputTestParameters;
- public:
+// google.codec2 Component test setup
+class Codec2ComponentHidlTestBase : public ::testing::Test {
+ public:
virtual void SetUp() override {
- Super::SetUp();
+ getParams();
mEos = false;
- mClient = android::Codec2Client::CreateFromService(
- gEnv->getInstance().c_str());
+ mClient = android::Codec2Client::CreateFromService(mInstanceName.c_str());
ASSERT_NE(mClient, nullptr);
- mListener.reset(new CodecListener(
- [this](std::list<std::unique_ptr<C2Work>>& workItems) {
- handleWorkDone(workItems);
- }));
+ mListener.reset(new CodecListener([this](std::list<std::unique_ptr<C2Work>>& workItems) {
+ handleWorkDone(workItems);
+ }));
ASSERT_NE(mListener, nullptr);
- mClient->createComponent(gEnv->getComponent().c_str(), mListener,
- &mComponent);
+ mClient->createComponent(mComponentName.c_str(), mListener, &mComponent);
ASSERT_NE(mComponent, nullptr);
for (int i = 0; i < MAX_INPUT_BUFFERS; ++i) {
mWorkQueue.emplace_back(new C2Work);
@@ -90,8 +85,11 @@
mComponent->release();
mComponent = nullptr;
}
- Super::TearDown();
}
+
+ // Get the test parameters from GetParam call.
+ virtual void getParams() {}
+
// callback function to process onWorkDone received by Listener
void handleWorkDone(std::list<std::unique_ptr<C2Work>>& workItems) {
for (std::unique_ptr<C2Work>& work : workItems) {
@@ -99,12 +97,14 @@
bool mCsd = false;
uint32_t mFramesReceived = 0;
std::list<uint64_t> mFlushedIndices;
- workDone(mComponent, work, mFlushedIndices, mQueueLock, mQueueCondition,
- mWorkQueue, mEos, mCsd, mFramesReceived);
+ workDone(mComponent, work, mFlushedIndices, mQueueLock, mQueueCondition, mWorkQueue,
+ mEos, mCsd, mFramesReceived);
}
}
}
+ std::string mInstanceName;
+ std::string mComponentName;
bool mEos;
std::mutex mQueueLock;
std::condition_variable mQueueCondition;
@@ -114,14 +114,23 @@
std::shared_ptr<android::Codec2Client::Listener> mListener;
std::shared_ptr<android::Codec2Client::Component> mComponent;
- protected:
+ protected:
static void description(const std::string& description) {
RecordProperty("description", description);
}
};
+class Codec2ComponentHidlTest
+ : public Codec2ComponentHidlTestBase,
+ public ::testing::WithParamInterface<std::tuple<std::string, std::string>> {
+ void getParams() {
+ mInstanceName = std::get<0>(GetParam());
+ mComponentName = std::get<1>(GetParam());
+ }
+};
+
// Test Empty Flush
-TEST_F(Codec2ComponentHidlTest, EmptyFlush) {
+TEST_P(Codec2ComponentHidlTest, EmptyFlush) {
ALOGV("Empty Flush Test");
c2_status_t err = mComponent->start();
ASSERT_EQ(err, C2_OK);
@@ -137,7 +146,7 @@
}
// Test Queue Empty Work
-TEST_F(Codec2ComponentHidlTest, QueueEmptyWork) {
+TEST_P(Codec2ComponentHidlTest, QueueEmptyWork) {
ALOGV("Queue Empty Work Test");
c2_status_t err = mComponent->start();
ASSERT_EQ(err, C2_OK);
@@ -151,7 +160,7 @@
}
// Test Component Configuration
-TEST_F(Codec2ComponentHidlTest, Config) {
+TEST_P(Codec2ComponentHidlTest, Config) {
ALOGV("Configuration Test");
C2String name = mComponent->getName();
@@ -180,7 +189,7 @@
}
// Test Multiple Start Stop Reset Test
-TEST_F(Codec2ComponentHidlTest, MultipleStartStopReset) {
+TEST_P(Codec2ComponentHidlTest, MultipleStartStopReset) {
ALOGV("Multiple Start Stop and Reset Test");
for (size_t i = 0; i < MAX_RETRY; i++) {
@@ -202,21 +211,21 @@
}
// Test Component Release API
-TEST_F(Codec2ComponentHidlTest, MultipleRelease) {
+TEST_P(Codec2ComponentHidlTest, MultipleRelease) {
ALOGV("Multiple Release Test");
c2_status_t err = mComponent->start();
ASSERT_EQ(err, C2_OK);
// Query Component Domain Type
std::vector<std::unique_ptr<C2Param>> queried;
- err = mComponent->query({}, {C2PortMediaTypeSetting::input::PARAM_TYPE},
- C2_DONT_BLOCK, &queried);
+ err = mComponent->query({}, {C2PortMediaTypeSetting::input::PARAM_TYPE}, C2_DONT_BLOCK,
+ &queried);
EXPECT_NE(queried.size(), 0u);
// Configure Component Domain
std::vector<std::unique_ptr<C2SettingResult>> failures;
C2PortMediaTypeSetting::input* portMediaType =
- C2PortMediaTypeSetting::input::From(queried[0].get());
+ C2PortMediaTypeSetting::input::From(queried[0].get());
err = mComponent->config({portMediaType}, C2_DONT_BLOCK, &failures);
ASSERT_EQ(err, C2_OK);
ASSERT_EQ(failures.size(), 0u);
@@ -226,40 +235,8 @@
}
}
-class Codec2ComponentInputTests : public Codec2ComponentHidlTest,
- public ::testing::WithParamInterface<std::pair<uint32_t, bool> > {
-};
-
-TEST_P(Codec2ComponentInputTests, InputBufferTest) {
- description("Tests for different inputs");
-
- uint32_t flags = GetParam().first;
- bool isNullBuffer = GetParam().second;
- if (isNullBuffer) ALOGD("Testing for null input buffer with flag : %u", flags);
- else ALOGD("Testing for empty input buffer with flag : %u", flags);
- mEos = false;
- ASSERT_EQ(mComponent->start(), C2_OK);
- ASSERT_NO_FATAL_FAILURE(testInputBuffer(
- mComponent, mQueueLock, mWorkQueue, flags, isNullBuffer));
-
- ALOGD("Waiting for input consumption");
- ASSERT_NO_FATAL_FAILURE(
- waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
-
- if (flags == C2FrameData::FLAG_END_OF_STREAM) ASSERT_EQ(mEos, true);
- ASSERT_EQ(mComponent->stop(), C2_OK);
- ASSERT_EQ(mComponent->reset(), C2_OK);
-}
-
-INSTANTIATE_TEST_CASE_P(NonStdInputs, Codec2ComponentInputTests, ::testing::Values(
- std::make_pair(0, true),
- std::make_pair(C2FrameData::FLAG_END_OF_STREAM, true),
- std::make_pair(0, false),
- std::make_pair(C2FrameData::FLAG_CODEC_CONFIG, false),
- std::make_pair(C2FrameData::FLAG_END_OF_STREAM, false)));
-
// Test API's Timeout
-TEST_F(Codec2ComponentHidlTest, Timeout) {
+TEST_P(Codec2ComponentHidlTest, Timeout) {
ALOGV("Timeout Test");
c2_status_t err = C2_OK;
@@ -285,10 +262,8 @@
startTime = getNowUs();
err = mComponent->querySupportedParams(¶ms);
timeConsumed = getNowUs() - startTime;
- CHECK_TIMEOUT(timeConsumed, QUERYSUPPORTEDPARAMS_TIME_OUT,
- "querySupportedParams()");
- ALOGV("mComponent->querySupportedParams() timeConsumed=%" PRId64 " us",
- timeConsumed);
+ CHECK_TIMEOUT(timeConsumed, QUERYSUPPORTEDPARAMS_TIME_OUT, "querySupportedParams()");
+ ALOGV("mComponent->querySupportedParams() timeConsumed=%" PRId64 " us", timeConsumed);
ASSERT_EQ(err, C2_OK);
std::vector<std::unique_ptr<C2Param>> queried;
@@ -301,8 +276,8 @@
CHECK_TIMEOUT(timeConsumed, QUERY_TIME_OUT, "query()");
EXPECT_NE(queried.size(), 0u);
EXPECT_EQ(err, C2_OK);
- ALOGV("mComponent->query() for %s timeConsumed=%" PRId64 " us",
- p->name().c_str(), timeConsumed);
+ ALOGV("mComponent->query() for %s timeConsumed=%" PRId64 " us", p->name().c_str(),
+ timeConsumed);
startTime = getNowUs();
err = mComponent->config({queried[0].get()}, C2_DONT_BLOCK, &failures);
@@ -310,8 +285,8 @@
CHECK_TIMEOUT(timeConsumed, CONFIG_TIME_OUT, "config()");
ASSERT_EQ(err, C2_OK);
ASSERT_EQ(failures.size(), 0u);
- ALOGV("mComponent->config() for %s timeConsumed=%" PRId64 " us",
- p->name().c_str(), timeConsumed);
+ ALOGV("mComponent->config() for %s timeConsumed=%" PRId64 " us", p->name().c_str(),
+ timeConsumed);
}
std::list<std::unique_ptr<C2Work>> workList;
@@ -340,22 +315,68 @@
ALOGV("mComponent->release() timeConsumed=%" PRId64 " us", timeConsumed);
CHECK_TIMEOUT(timeConsumed, RELEASE_TIME_OUT, "release()");
ASSERT_EQ(err, C2_OK);
-
}
+class Codec2ComponentInputTests
+ : public Codec2ComponentHidlTestBase,
+ public ::testing::WithParamInterface<
+ std::tuple<std::string, std::string, std::string, std::string>> {
+ void getParams() {
+ mInstanceName = std::get<0>(GetParam());
+ mComponentName = std::get<1>(GetParam());
+ }
+};
+
+TEST_P(Codec2ComponentInputTests, InputBufferTest) {
+ description("Tests for different inputs");
+
+ uint32_t flags = std::stoul(std::get<2>(GetParam()));
+ bool isNullBuffer = !std::get<3>(GetParam()).compare("true");
+ if (isNullBuffer)
+ ALOGD("Testing for null input buffer with flag : %u", flags);
+ else
+ ALOGD("Testing for empty input buffer with flag : %u", flags);
+ mEos = false;
+ ASSERT_EQ(mComponent->start(), C2_OK);
+ ASSERT_NO_FATAL_FAILURE(
+ testInputBuffer(mComponent, mQueueLock, mWorkQueue, flags, isNullBuffer));
+
+ ALOGD("Waiting for input consumption");
+ ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
+
+ if (flags == C2FrameData::FLAG_END_OF_STREAM) ASSERT_EQ(mEos, true);
+ ASSERT_EQ(mComponent->stop(), C2_OK);
+ ASSERT_EQ(mComponent->reset(), C2_OK);
+}
+
+INSTANTIATE_TEST_SUITE_P(PerInstance, Codec2ComponentHidlTest, testing::ValuesIn(kTestParameters),
+ android::hardware::PrintInstanceTupleNameToString<>);
+
+INSTANTIATE_TEST_CASE_P(NonStdInputs, Codec2ComponentInputTests,
+ testing::ValuesIn(kInputTestParameters),
+ android::hardware::PrintInstanceTupleNameToString<>);
} // anonymous namespace
// TODO: Add test for Invalid work,
// TODO: Add test for Invalid states
int main(int argc, char** argv) {
- gEnv = new ComponentTestEnvironment();
- ::testing::AddGlobalTestEnvironment(gEnv);
- ::testing::InitGoogleTest(&argc, argv);
- gEnv->init(&argc, argv);
- int status = gEnv->initFromOptions(argc, argv);
- if (status == 0) {
- status = RUN_ALL_TESTS();
- LOG(INFO) << "C2 Test result = " << status;
+ kTestParameters = getTestParameters();
+ for (auto params : kTestParameters) {
+ kInputTestParameters.push_back(
+ std::make_tuple(std::get<0>(params), std::get<1>(params), "0", "true"));
+ kInputTestParameters.push_back(
+ std::make_tuple(std::get<0>(params), std::get<1>(params),
+ std::to_string(C2FrameData::FLAG_END_OF_STREAM), "true"));
+ kInputTestParameters.push_back(
+ std::make_tuple(std::get<0>(params), std::get<1>(params), "0", "false"));
+ kInputTestParameters.push_back(
+ std::make_tuple(std::get<0>(params), std::get<1>(params),
+ std::to_string(C2FrameData::FLAG_CODEC_CONFIG), "false"));
+ kInputTestParameters.push_back(
+ std::make_tuple(std::get<0>(params), std::get<1>(params),
+ std::to_string(C2FrameData::FLAG_END_OF_STREAM), "false"));
}
- return status;
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
}
diff --git a/media/codec2/hidl/1.0/vts/functional/master/VtsHalMediaC2V1_0TargetMasterTest.cpp b/media/codec2/hidl/1.0/vts/functional/master/VtsHalMediaC2V1_0TargetMasterTest.cpp
index e88fbc7..fb1c291 100644
--- a/media/codec2/hidl/1.0/vts/functional/master/VtsHalMediaC2V1_0TargetMasterTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/master/VtsHalMediaC2V1_0TargetMasterTest.cpp
@@ -19,30 +19,25 @@
#include <android-base/logging.h>
#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
#include <codec2/hidl/client.h>
#include <VtsHalHidlTargetTestBase.h>
#include "media_c2_hidl_test_common.h"
-static ComponentTestEnvironment* gEnv = nullptr;
-
namespace {
// google.codec2 Master test setup
-class Codec2MasterHalTest : public ::testing::VtsHalHidlTargetTestBase {
- private:
- typedef ::testing::VtsHalHidlTargetTestBase Super;
-
- public:
+class Codec2MasterHalTest : public ::testing::TestWithParam<std::string> {
+ public:
virtual void SetUp() override {
- Super::SetUp();
- mClient = android::Codec2Client::CreateFromService(
- gEnv->getInstance().c_str());
+ mClient = android::Codec2Client::CreateFromService(GetParam().c_str());
ASSERT_NE(mClient, nullptr);
}
- protected:
+ protected:
static void description(const std::string& description) {
RecordProperty("description", description);
}
@@ -58,15 +53,14 @@
}
// List Components
-TEST_F(Codec2MasterHalTest, ListComponents) {
+TEST_P(Codec2MasterHalTest, ListComponents) {
ALOGV("ListComponents Test");
C2String name = mClient->getName();
EXPECT_NE(name.empty(), true) << "Invalid Codec2Client Name";
// Get List of components from all known services
- const std::vector<C2Component::Traits> listTraits =
- mClient->ListComponents();
+ const std::vector<C2Component::Traits> listTraits = mClient->ListComponents();
if (listTraits.size() == 0)
ALOGE("Warning, ComponentInfo list empty");
@@ -79,24 +73,16 @@
ASSERT_NE(listener, nullptr);
// Create component from all known services
- component = mClient->CreateComponentByName(
- listTraits[i].name.c_str(), listener, &mClient);
- ASSERT_NE(component, nullptr) << "Create component failed for "
- << listTraits[i].name.c_str();
+ component =
+ mClient->CreateComponentByName(listTraits[i].name.c_str(), listener, &mClient);
+ ASSERT_NE(component, nullptr)
+ << "Create component failed for " << listTraits[i].name.c_str();
}
}
}
} // anonymous namespace
-int main(int argc, char** argv) {
- gEnv = new ComponentTestEnvironment();
- ::testing::InitGoogleTest(&argc, argv);
- gEnv->init(&argc, argv);
- int status = gEnv->initFromOptions(argc, argv);
- if (status == 0) {
- status = RUN_ALL_TESTS();
- LOG(INFO) << "C2 Test result = " << status;
- }
- return status;
-}
+INSTANTIATE_TEST_SUITE_P(PerInstance, Codec2MasterHalTest,
+ testing::ValuesIn(android::Codec2Client::GetServiceNames()),
+ android::hardware::PrintInstanceNameToString);
diff --git a/media/codec2/hidl/1.0/vts/functional/video/Android.bp b/media/codec2/hidl/1.0/vts/functional/video/Android.bp
index b737323..760f4da 100644
--- a/media/codec2/hidl/1.0/vts/functional/video/Android.bp
+++ b/media/codec2/hidl/1.0/vts/functional/video/Android.bp
@@ -16,6 +16,7 @@
cc_test {
name: "VtsHalMediaC2V1_0TargetVideoDecTest",
+ stem: "vts_media_c2_v1_0_video_dec_test",
defaults: ["VtsHalMediaC2V1_0Defaults"],
srcs: ["VtsHalMediaC2V1_0TargetVideoDecTest.cpp"],
header_libs: [
@@ -26,11 +27,16 @@
"libgui",
"libutils",
],
+ data: [":media_c2_v1_video_decode_res"],
+ test_config: "VtsHalMediaC2V1_0TargetVideoDecTest.xml",
}
cc_test {
name: "VtsHalMediaC2V1_0TargetVideoEncTest",
+ stem: "vts_media_c2_v1_0_video_enc_test",
defaults: ["VtsHalMediaC2V1_0Defaults"],
srcs: ["VtsHalMediaC2V1_0TargetVideoEncTest.cpp"],
+ data: [":media_c2_v1_video_encode_res"],
+ test_config: "VtsHalMediaC2V1_0TargetVideoEncTest.xml",
}
diff --git a/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
index 256603c..ec3fd7b 100644
--- a/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
@@ -19,15 +19,16 @@
#include <android-base/logging.h>
#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
#include <stdio.h>
#include <fstream>
-#include <codec2/hidl/client.h>
#include <C2AllocatorIon.h>
-#include <C2Config.h>
-#include <C2Debug.h>
#include <C2Buffer.h>
#include <C2BufferPriv.h>
+#include <C2Config.h>
+#include <C2Debug.h>
+#include <codec2/hidl/client.h>
#include <gui/BufferQueue.h>
#include <gui/IConsumerListener.h>
#include <gui/IProducerListener.h>
@@ -35,9 +36,8 @@
using android::C2AllocatorIon;
-#include <VtsHalHidlTargetTestBase.h>
-#include "media_c2_video_hidl_test_common.h"
#include "media_c2_hidl_test_common.h"
+#include "media_c2_video_hidl_test_common.h"
struct FrameInfo {
int bytesCount;
@@ -45,61 +45,47 @@
int64_t timestamp;
};
+static std::vector<std::tuple<std::string, std::string, std::string, std::string>>
+ kDecodeTestParameters;
+
+// Resource directory
+static std::string sResourceDir = "";
+
class LinearBuffer : public C2Buffer {
- public:
+ public:
explicit LinearBuffer(const std::shared_ptr<C2LinearBlock>& block)
- : C2Buffer(
- {block->share(block->offset(), block->size(), ::C2Fence())}) {}
+ : C2Buffer({block->share(block->offset(), block->size(), ::C2Fence())}) {}
explicit LinearBuffer(const std::shared_ptr<C2LinearBlock>& block, size_t size)
- : C2Buffer(
- {block->share(block->offset(), size, ::C2Fence())}) {}
+ : C2Buffer({block->share(block->offset(), size, ::C2Fence())}) {}
};
-static ComponentTestEnvironment* gEnv = nullptr;
-
namespace {
-class Codec2VideoDecHidlTest : public ::testing::VtsHalHidlTargetTestBase {
- private:
- typedef ::testing::VtsHalHidlTargetTestBase Super;
-
- public:
- ::std::string getTestCaseInfo() const override {
- return ::std::string() +
- "Component: " + gEnv->getComponent().c_str() + " | " +
- "Instance: " + gEnv->getInstance().c_str() + " | " +
- "Res: " + gEnv->getRes().c_str();
- }
-
+class Codec2VideoDecHidlTestBase : public ::testing::Test {
+ public:
// google.codec2 Video test setup
virtual void SetUp() override {
- Super::SetUp();
+ getParams();
mDisableTest = false;
ALOGV("Codec2VideoDecHidlTest SetUp");
mClient = android::Codec2Client::CreateFromService(
- gEnv->getInstance().c_str(),
- !bool(android::Codec2Client::CreateFromService("default", true)));
+ mInstanceName.c_str(),
+ !bool(android::Codec2Client::CreateFromService("default", true)));
ASSERT_NE(mClient, nullptr);
- mListener.reset(new CodecListener(
- [this](std::list<std::unique_ptr<C2Work>>& workItems) {
- handleWorkDone(workItems);
- }));
+ mListener.reset(new CodecListener([this](std::list<std::unique_ptr<C2Work>>& workItems) {
+ handleWorkDone(workItems);
+ }));
ASSERT_NE(mListener, nullptr);
for (int i = 0; i < MAX_INPUT_BUFFERS; ++i) {
mWorkQueue.emplace_back(new C2Work);
}
- mClient->createComponent(gEnv->getComponent().c_str(), mListener,
- &mComponent);
+ mClient->createComponent(mComponentName, mListener, &mComponent);
ASSERT_NE(mComponent, nullptr);
- std::shared_ptr<C2AllocatorStore> store =
- android::GetCodec2PlatformAllocatorStore();
- CHECK_EQ(store->fetchAllocator(C2AllocatorStore::DEFAULT_LINEAR,
- &mLinearAllocator),
- C2_OK);
- mLinearPool = std::make_shared<C2PooledBlockPool>(mLinearAllocator,
- mBlockPoolId++);
+ std::shared_ptr<C2AllocatorStore> store = android::GetCodec2PlatformAllocatorStore();
+ CHECK_EQ(store->fetchAllocator(C2AllocatorStore::DEFAULT_LINEAR, &mLinearAllocator), C2_OK);
+ mLinearPool = std::make_shared<C2PooledBlockPool>(mLinearAllocator, mBlockPoolId++);
ASSERT_NE(mLinearPool, nullptr);
mCompName = unknown_comp;
@@ -109,17 +95,15 @@
};
const StringToName kStringToName[] = {
- {"h263", h263}, {"avc", avc}, {"mpeg2", mpeg2}, {"mpeg4", mpeg4},
- {"hevc", hevc}, {"vp8", vp8}, {"vp9", vp9}, {"av1", av1},
+ {"h263", h263}, {"avc", avc}, {"mpeg2", mpeg2}, {"mpeg4", mpeg4},
+ {"hevc", hevc}, {"vp8", vp8}, {"vp9", vp9}, {"av1", av1},
};
- const size_t kNumStringToName =
- sizeof(kStringToName) / sizeof(kStringToName[0]);
+ const size_t kNumStringToName = sizeof(kStringToName) / sizeof(kStringToName[0]);
// Find the component type
- std::string comp = std::string(gEnv->getComponent());
for (size_t i = 0; i < kNumStringToName; ++i) {
- if (strcasestr(comp.c_str(), kStringToName[i].Name)) {
+ if (strcasestr(mComponentName.c_str(), kStringToName[i].Name)) {
mCompName = kStringToName[i].CompName;
break;
}
@@ -131,7 +115,7 @@
if (mCompName == unknown_comp) mDisableTest = true;
C2SecureModeTuning secureModeTuning{};
- mComponent->query({ &secureModeTuning }, {}, C2_MAY_BLOCK, nullptr);
+ mComponent->query({&secureModeTuning}, {}, C2_MAY_BLOCK, nullptr);
if (secureModeTuning.value == C2Config::SM_READ_PROTECTED) {
mDisableTest = true;
}
@@ -145,9 +129,11 @@
mComponent->release();
mComponent = nullptr;
}
- Super::TearDown();
}
+ // Get the test parameters from GetParam call.
+ virtual void getParams() {}
+
// callback function to process onWorkDone received by Listener
void handleWorkDone(std::list<std::unique_ptr<C2Work>>& workItems) {
for (std::unique_ptr<C2Work>& work : workItems) {
@@ -157,13 +143,10 @@
typedef std::unique_lock<std::mutex> ULock;
bool codecConfig = ((work->worklets.front()->output.flags &
C2FrameData::FLAG_CODEC_CONFIG) != 0);
- if (!codecConfig &&
- !work->worklets.front()->output.buffers.empty()) {
- EXPECT_GE(
- (work->worklets.front()->output.ordinal.timestamp.peeku()),
- mTimestampUs);
- mTimestampUs =
- work->worklets.front()->output.ordinal.timestamp.peeku();
+ if (!codecConfig && !work->worklets.front()->output.buffers.empty()) {
+ EXPECT_GE((work->worklets.front()->output.ordinal.timestamp.peeku()),
+ mTimestampUs);
+ mTimestampUs = work->worklets.front()->output.ordinal.timestamp.peeku();
ULock l(mQueueLock);
if (mTimestampDevTest) {
@@ -179,8 +162,7 @@
}
if (tsHit == false) {
if (mTimestampUslist.empty() == false) {
- EXPECT_EQ(tsHit, true)
- << "TimeStamp not recognized";
+ EXPECT_EQ(tsHit, true) << "TimeStamp not recognized";
} else {
std::cout << "[ INFO ] Received non-zero "
"output / TimeStamp not recognized \n";
@@ -189,9 +171,8 @@
}
}
bool mCsd;
- workDone(mComponent, work, mFlushedIndices, mQueueLock,
- mQueueCondition, mWorkQueue, mEos, mCsd,
- mFramesReceived);
+ workDone(mComponent, work, mFlushedIndices, mQueueLock, mQueueCondition, mWorkQueue,
+ mEos, mCsd, mFramesReceived);
(void)mCsd;
}
}
@@ -209,6 +190,9 @@
unknown_comp,
};
+ std::string mInstanceName;
+ std::string mComponentName;
+
bool mEos;
bool mDisableTest;
bool mTimestampDevTest;
@@ -229,15 +213,23 @@
std::shared_ptr<android::Codec2Client::Listener> mListener;
std::shared_ptr<android::Codec2Client::Component> mComponent;
- protected:
+ protected:
static void description(const std::string& description) {
RecordProperty("description", description);
}
};
-void validateComponent(
- const std::shared_ptr<android::Codec2Client::Component>& component,
- Codec2VideoDecHidlTest::standardComp compName, bool& disableTest) {
+class Codec2VideoDecHidlTest
+ : public Codec2VideoDecHidlTestBase,
+ public ::testing::WithParamInterface<std::tuple<std::string, std::string>> {
+ void getParams() {
+ mInstanceName = std::get<0>(GetParam());
+ mComponentName = std::get<1>(GetParam());
+ }
+};
+
+void validateComponent(const std::shared_ptr<android::Codec2Client::Component>& component,
+ Codec2VideoDecHidlTest::standardComp compName, bool& disableTest) {
// Validate its a C2 Component
if (component->getName().find("c2") == std::string::npos) {
ALOGE("Not a c2 component");
@@ -252,14 +244,12 @@
return;
}
std::vector<std::unique_ptr<C2Param>> queried;
- c2_status_t c2err =
- component->query({}, {C2PortMediaTypeSetting::input::PARAM_TYPE},
- C2_DONT_BLOCK, &queried);
+ c2_status_t c2err = component->query({}, {C2PortMediaTypeSetting::input::PARAM_TYPE},
+ C2_DONT_BLOCK, &queried);
if (c2err != C2_OK && queried.size() == 0) {
ALOGE("Query media type failed => %d", c2err);
} else {
- std::string inputDomain =
- ((C2StreamMediaTypeSetting::input*)queried[0].get())->m.value;
+ std::string inputDomain = ((C2StreamMediaTypeSetting::input*)queried[0].get())->m.value;
if (inputDomain.find("video/") == std::string::npos) {
ALOGE("Expected Video Component");
disableTest = true;
@@ -279,8 +269,8 @@
// number of elementary streams per component
#define STREAM_COUNT 2
// LookUpTable of clips and metadata for component testing
-void GetURLForComponent(Codec2VideoDecHidlTest::standardComp comp, char* mURL,
- char* info, size_t streamIndex = 1) {
+void GetURLForComponent(Codec2VideoDecHidlTest::standardComp comp, char* mURL, char* info,
+ size_t streamIndex = 1) {
struct CompToURL {
Codec2VideoDecHidlTest::standardComp comp;
const char mURL[STREAM_COUNT][512];
@@ -289,42 +279,30 @@
ASSERT_TRUE(streamIndex < STREAM_COUNT);
static const CompToURL kCompToURL[] = {
- {Codec2VideoDecHidlTest::standardComp::avc,
- {"bbb_avc_176x144_300kbps_60fps.h264",
- "bbb_avc_640x360_768kbps_30fps.h264"},
- {"bbb_avc_176x144_300kbps_60fps.info",
- "bbb_avc_640x360_768kbps_30fps.info"}},
- {Codec2VideoDecHidlTest::standardComp::hevc,
- {"bbb_hevc_176x144_176kbps_60fps.hevc",
- "bbb_hevc_640x360_1600kbps_30fps.hevc"},
- {"bbb_hevc_176x144_176kbps_60fps.info",
- "bbb_hevc_640x360_1600kbps_30fps.info"}},
- {Codec2VideoDecHidlTest::standardComp::mpeg2,
- {"bbb_mpeg2_176x144_105kbps_25fps.m2v",
- "bbb_mpeg2_352x288_1mbps_60fps.m2v"},
- {"bbb_mpeg2_176x144_105kbps_25fps.info",
- "bbb_mpeg2_352x288_1mbps_60fps.info"}},
- {Codec2VideoDecHidlTest::standardComp::h263,
- {"", "bbb_h263_352x288_300kbps_12fps.h263"},
- {"", "bbb_h263_352x288_300kbps_12fps.info"}},
- {Codec2VideoDecHidlTest::standardComp::mpeg4,
- {"", "bbb_mpeg4_352x288_512kbps_30fps.m4v"},
- {"", "bbb_mpeg4_352x288_512kbps_30fps.info"}},
- {Codec2VideoDecHidlTest::standardComp::vp8,
- {"bbb_vp8_176x144_240kbps_60fps.vp8",
- "bbb_vp8_640x360_2mbps_30fps.vp8"},
- {"bbb_vp8_176x144_240kbps_60fps.info",
- "bbb_vp8_640x360_2mbps_30fps.info"}},
- {Codec2VideoDecHidlTest::standardComp::vp9,
- {"bbb_vp9_176x144_285kbps_60fps.vp9",
- "bbb_vp9_640x360_1600kbps_30fps.vp9"},
- {"bbb_vp9_176x144_285kbps_60fps.info",
- "bbb_vp9_640x360_1600kbps_30fps.info"}},
- {Codec2VideoDecHidlTest::standardComp::av1,
- {"bbb_av1_640_360.av1",
- "bbb_av1_176_144.av1"},
- {"bbb_av1_640_360.info",
- "bbb_av1_176_144.info"}},
+ {Codec2VideoDecHidlTest::standardComp::avc,
+ {"bbb_avc_176x144_300kbps_60fps.h264", "bbb_avc_640x360_768kbps_30fps.h264"},
+ {"bbb_avc_176x144_300kbps_60fps.info", "bbb_avc_640x360_768kbps_30fps.info"}},
+ {Codec2VideoDecHidlTest::standardComp::hevc,
+ {"bbb_hevc_176x144_176kbps_60fps.hevc", "bbb_hevc_640x360_1600kbps_30fps.hevc"},
+ {"bbb_hevc_176x144_176kbps_60fps.info", "bbb_hevc_640x360_1600kbps_30fps.info"}},
+ {Codec2VideoDecHidlTest::standardComp::mpeg2,
+ {"bbb_mpeg2_176x144_105kbps_25fps.m2v", "bbb_mpeg2_352x288_1mbps_60fps.m2v"},
+ {"bbb_mpeg2_176x144_105kbps_25fps.info", "bbb_mpeg2_352x288_1mbps_60fps.info"}},
+ {Codec2VideoDecHidlTest::standardComp::h263,
+ {"", "bbb_h263_352x288_300kbps_12fps.h263"},
+ {"", "bbb_h263_352x288_300kbps_12fps.info"}},
+ {Codec2VideoDecHidlTest::standardComp::mpeg4,
+ {"", "bbb_mpeg4_352x288_512kbps_30fps.m4v"},
+ {"", "bbb_mpeg4_352x288_512kbps_30fps.info"}},
+ {Codec2VideoDecHidlTest::standardComp::vp8,
+ {"bbb_vp8_176x144_240kbps_60fps.vp8", "bbb_vp8_640x360_2mbps_30fps.vp8"},
+ {"bbb_vp8_176x144_240kbps_60fps.info", "bbb_vp8_640x360_2mbps_30fps.info"}},
+ {Codec2VideoDecHidlTest::standardComp::vp9,
+ {"bbb_vp9_176x144_285kbps_60fps.vp9", "bbb_vp9_640x360_1600kbps_30fps.vp9"},
+ {"bbb_vp9_176x144_285kbps_60fps.info", "bbb_vp9_640x360_1600kbps_30fps.info"}},
+ {Codec2VideoDecHidlTest::standardComp::av1,
+ {"bbb_av1_640_360.av1", "bbb_av1_176_144.av1"},
+ {"bbb_av1_640_360.info", "bbb_av1_176_144.info"}},
};
for (size_t i = 0; i < sizeof(kCompToURL) / sizeof(kCompToURL[0]); ++i) {
@@ -337,13 +315,11 @@
}
void decodeNFrames(const std::shared_ptr<android::Codec2Client::Component>& component,
- std::mutex &queueLock, std::condition_variable& queueCondition,
+ std::mutex& queueLock, std::condition_variable& queueCondition,
std::list<std::unique_ptr<C2Work>>& workQueue,
- std::list<uint64_t>& flushedIndices,
- std::shared_ptr<C2BlockPool>& linearPool,
- std::ifstream& eleStream,
- android::Vector<FrameInfo>* Info,
- int offset, int range, bool signalEOS = true) {
+ std::list<uint64_t>& flushedIndices, std::shared_ptr<C2BlockPool>& linearPool,
+ std::ifstream& eleStream, android::Vector<FrameInfo>* Info, int offset,
+ int range, bool signalEOS = true) {
typedef std::unique_lock<std::mutex> ULock;
int frameID = offset;
int maxRetry = 0;
@@ -367,8 +343,7 @@
}
int64_t timestamp = (*Info)[frameID].timestamp;
if ((*Info)[frameID].flags) flags = (1 << ((*Info)[frameID].flags - 1));
- if (signalEOS && ((frameID == (int)Info->size() - 1) ||
- (frameID == (offset + range - 1))))
+ if (signalEOS && ((frameID == (int)Info->size() - 1) || (frameID == (offset + range - 1))))
flags |= C2FrameData::FLAG_END_OF_STREAM;
work->input.flags = (C2FrameData::flags_t)flags;
@@ -390,10 +365,9 @@
auto alignedSize = ALIGN(size, PAGE_SIZE);
if (size) {
std::shared_ptr<C2LinearBlock> block;
- ASSERT_EQ(C2_OK,
- linearPool->fetchLinearBlock(
- alignedSize, {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE},
- &block));
+ ASSERT_EQ(C2_OK, linearPool->fetchLinearBlock(
+ alignedSize,
+ {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}, &block));
ASSERT_TRUE(block);
// Write View
@@ -424,16 +398,16 @@
}
}
-TEST_F(Codec2VideoDecHidlTest, validateCompName) {
- if (mDisableTest) return;
+TEST_P(Codec2VideoDecHidlTest, validateCompName) {
+ if (mDisableTest) GTEST_SKIP() << "Test is disabled";
ALOGV("Checks if the given component is a valid video component");
validateComponent(mComponent, mCompName, mDisableTest);
ASSERT_EQ(mDisableTest, false);
}
-TEST_F(Codec2VideoDecHidlTest, configureTunnel) {
+TEST_P(Codec2VideoDecHidlTest, configureTunnel) {
description("Attempts to configure tunneling");
- if (mDisableTest) return;
+ if (mDisableTest) GTEST_SKIP() << "Test is disabled";
ALOGV("Checks if the component can be configured for tunneling");
native_handle_t* sidebandStream{};
c2_status_t err = mComponent->configureVideoTunnel(0, &sidebandStream);
@@ -449,7 +423,7 @@
BufferQueue::createBufferQueue(&producer, &consumer);
class DummyConsumerListener : public BnConsumerListener {
- public:
+ public:
DummyConsumerListener() : BnConsumerListener() {}
void onFrameAvailable(const BufferItem&) override {}
void onBuffersReleased() override {}
@@ -458,37 +432,39 @@
consumer->consumerConnect(new DummyConsumerListener(), false);
class DummyProducerListener : public BnProducerListener {
- public:
+ public:
DummyProducerListener() : BnProducerListener() {}
virtual void onBufferReleased() override {}
virtual bool needsReleaseNotify() override { return false; }
virtual void onBuffersDiscarded(const std::vector<int32_t>&) override {}
};
IGraphicBufferProducer::QueueBufferOutput qbo{};
- producer->connect(new DummyProducerListener(),
- NATIVE_WINDOW_API_MEDIA,
- false,
- &qbo);
+ producer->connect(new DummyProducerListener(), NATIVE_WINDOW_API_MEDIA, false, &qbo);
ASSERT_EQ(producer->setSidebandStream(nativeHandle), NO_ERROR);
}
class Codec2VideoDecDecodeTest
- : public Codec2VideoDecHidlTest,
- public ::testing::WithParamInterface<std::pair<int32_t, bool>> {
+ : public Codec2VideoDecHidlTestBase,
+ public ::testing::WithParamInterface<
+ std::tuple<std::string, std::string, std::string, std::string>> {
+ void getParams() {
+ mInstanceName = std::get<0>(GetParam());
+ mComponentName = std::get<1>(GetParam());
+ }
};
// Bitstream Test
TEST_P(Codec2VideoDecDecodeTest, DecodeTest) {
description("Decodes input file");
- if (mDisableTest) return;
+ if (mDisableTest) GTEST_SKIP() << "Test is disabled";
- uint32_t streamIndex = GetParam().first;
- bool signalEOS = GetParam().second;
+ uint32_t streamIndex = std::stoi(std::get<2>(GetParam()));
+ bool signalEOS = !std::get<2>(GetParam()).compare("true");
char mURL[512], info[512];
std::ifstream eleStream, eleInfo;
- strcpy(mURL, gEnv->getRes().c_str());
- strcpy(info, gEnv->getRes().c_str());
+ strcpy(mURL, sResourceDir.c_str());
+ strcpy(info, sResourceDir.c_str());
GetURLForComponent(mCompName, mURL, info, streamIndex);
eleInfo.open(info);
@@ -504,10 +480,8 @@
if (!(eleInfo >> bytesCount)) break;
eleInfo >> flags;
eleInfo >> timestamp;
- bool codecConfig = flags ?
- ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0 : 0;
- if (mTimestampDevTest && !codecConfig)
- mTimestampUslist.push_back(timestamp);
+ bool codecConfig = flags ? ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0 : 0;
+ if (mTimestampDevTest && !codecConfig) mTimestampUslist.push_back(timestamp);
Info.push_back({bytesCount, flags, timestamp});
}
eleInfo.close();
@@ -519,52 +493,42 @@
ALOGV("mURL : %s", mURL);
eleStream.open(mURL, std::ifstream::binary);
ASSERT_EQ(eleStream.is_open(), true);
- ASSERT_NO_FATAL_FAILURE(decodeNFrames(
- mComponent, mQueueLock, mQueueCondition, mWorkQueue, mFlushedIndices,
- mLinearPool, eleStream, &Info, 0, (int)Info.size(), signalEOS));
+ ASSERT_NO_FATAL_FAILURE(decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
+ mFlushedIndices, mLinearPool, eleStream, &Info, 0,
+ (int)Info.size(), signalEOS));
// If EOS is not sent, sending empty input with EOS flag
size_t infoSize = Info.size();
if (!signalEOS) {
- ASSERT_NO_FATAL_FAILURE(
- waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue, 1));
- ASSERT_NO_FATAL_FAILURE(
- testInputBuffer(mComponent, mQueueLock, mWorkQueue,
- C2FrameData::FLAG_END_OF_STREAM, false));
+ ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue, 1));
+ ASSERT_NO_FATAL_FAILURE(testInputBuffer(mComponent, mQueueLock, mWorkQueue,
+ C2FrameData::FLAG_END_OF_STREAM, false));
infoSize += 1;
}
// blocking call to ensures application to Wait till all the inputs are
// consumed
if (!mEos) {
ALOGV("Waiting for input consumption");
- ASSERT_NO_FATAL_FAILURE(
- waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
+ ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
}
eleStream.close();
if (mFramesReceived != infoSize) {
ALOGE("Input buffer count and Output buffer count mismatch");
- ALOGV("framesReceived : %d inputFrames : %zu", mFramesReceived,
- infoSize);
+ ALOGV("framesReceived : %d inputFrames : %zu", mFramesReceived, infoSize);
ASSERT_TRUE(false);
}
if (mTimestampDevTest) EXPECT_EQ(mTimestampUslist.empty(), true);
ASSERT_EQ(mComponent->stop(), C2_OK);
}
-// DecodeTest with StreamIndex and EOS / No EOS
-INSTANTIATE_TEST_CASE_P(StreamIndexAndEOS, Codec2VideoDecDecodeTest,
- ::testing::Values(std::make_pair(0, false),
- std::make_pair(0, true),
- std::make_pair(1, false),
- std::make_pair(1, true)));
// Adaptive Test
-TEST_F(Codec2VideoDecHidlTest, AdaptiveDecodeTest) {
+TEST_P(Codec2VideoDecHidlTest, AdaptiveDecodeTest) {
description("Adaptive Decode Test");
- if (mDisableTest) return;
- if (!(mCompName == avc || mCompName == hevc || mCompName == vp8 ||
- mCompName == vp9 || mCompName == mpeg2))
+ if (mDisableTest) GTEST_SKIP() << "Test is disabled";
+ if (!(mCompName == avc || mCompName == hevc || mCompName == vp8 || mCompName == vp9 ||
+ mCompName == mpeg2))
return;
typedef std::unique_lock<std::mutex> ULock;
@@ -578,8 +542,8 @@
char mURL[512], info[512];
std::ifstream eleStream, eleInfo;
- strcpy(mURL, gEnv->getRes().c_str());
- strcpy(info, gEnv->getRes().c_str());
+ strcpy(mURL, sResourceDir.c_str());
+ strcpy(info, sResourceDir.c_str());
GetURLForComponent(mCompName, mURL, info, i % STREAM_COUNT);
eleInfo.open(info);
@@ -594,13 +558,12 @@
eleInfo >> timestamp;
timestamp += timestampOffset;
Info.push_back({bytesCount, flags, timestamp});
- bool codecConfig = flags ?
- ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0 : 0;
+ bool codecConfig =
+ flags ? ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0 : 0;
{
ULock l(mQueueLock);
- if (mTimestampDevTest && !codecConfig)
- mTimestampUslist.push_back(timestamp);
+ if (mTimestampDevTest && !codecConfig) mTimestampUslist.push_back(timestamp);
}
if (timestampMax < timestamp) timestampMax = timestamp;
}
@@ -612,10 +575,9 @@
ALOGV("mURL : %s", mURL);
eleStream.open(mURL, std::ifstream::binary);
ASSERT_EQ(eleStream.is_open(), true);
- ASSERT_NO_FATAL_FAILURE(
- decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
- mFlushedIndices, mLinearPool, eleStream, &Info,
- offset, (int)(Info.size() - offset), false));
+ ASSERT_NO_FATAL_FAILURE(decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
+ mFlushedIndices, mLinearPool, eleStream, &Info,
+ offset, (int)(Info.size() - offset), false));
eleStream.close();
offset = (int)Info.size();
@@ -650,13 +612,11 @@
// blocking call to ensures application to Wait till all the inputs are
// consumed
ALOGV("Waiting for input consumption");
- ASSERT_NO_FATAL_FAILURE(
- waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
+ ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
if (mFramesReceived != ((Info.size()) + 1)) {
ALOGE("Input buffer count and Output buffer count mismatch");
- ALOGV("framesReceived : %d inputFrames : %zu", mFramesReceived,
- Info.size() + 1);
+ ALOGV("framesReceived : %d inputFrames : %zu", mFramesReceived, Info.size() + 1);
ASSERT_TRUE(false);
}
@@ -664,15 +624,15 @@
}
// thumbnail test
-TEST_F(Codec2VideoDecHidlTest, ThumbnailTest) {
+TEST_P(Codec2VideoDecHidlTest, ThumbnailTest) {
description("Test Request for thumbnail");
- if (mDisableTest) return;
+ if (mDisableTest) GTEST_SKIP() << "Test is disabled";
char mURL[512], info[512];
std::ifstream eleStream, eleInfo;
- strcpy(mURL, gEnv->getRes().c_str());
- strcpy(info, gEnv->getRes().c_str());
+ strcpy(mURL, sResourceDir.c_str());
+ strcpy(info, sResourceDir.c_str());
GetURLForComponent(mCompName, mURL, info);
eleInfo.open(info);
@@ -703,11 +663,10 @@
} while (!(flags & SYNC_FRAME));
eleStream.open(mURL, std::ifstream::binary);
ASSERT_EQ(eleStream.is_open(), true);
- ASSERT_NO_FATAL_FAILURE(decodeNFrames(
- mComponent, mQueueLock, mQueueCondition, mWorkQueue,
- mFlushedIndices, mLinearPool, eleStream, &Info, 0, j + 1));
- ASSERT_NO_FATAL_FAILURE(
- waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
+ ASSERT_NO_FATAL_FAILURE(decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
+ mFlushedIndices, mLinearPool, eleStream, &Info, 0,
+ j + 1));
+ ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
eleStream.close();
EXPECT_GE(mFramesReceived, 1U);
ASSERT_EQ(mEos, true);
@@ -716,9 +675,9 @@
ASSERT_EQ(mComponent->release(), C2_OK);
}
-TEST_F(Codec2VideoDecHidlTest, EOSTest) {
+TEST_P(Codec2VideoDecHidlTest, EOSTest) {
description("Test empty input buffer with EOS flag");
- if (mDisableTest) return;
+ if (mDisableTest) GTEST_SKIP() << "Test is disabled";
typedef std::unique_lock<std::mutex> ULock;
ASSERT_EQ(mComponent->start(), C2_OK);
std::unique_ptr<C2Work> work;
@@ -756,16 +715,16 @@
ASSERT_EQ(mComponent->stop(), C2_OK);
}
-TEST_F(Codec2VideoDecHidlTest, FlushTest) {
+TEST_P(Codec2VideoDecHidlTest, FlushTest) {
description("Tests Flush calls");
- if (mDisableTest) return;
+ if (mDisableTest) GTEST_SKIP() << "Test is disabled";
typedef std::unique_lock<std::mutex> ULock;
ASSERT_EQ(mComponent->start(), C2_OK);
char mURL[512], info[512];
std::ifstream eleStream, eleInfo;
- strcpy(mURL, gEnv->getRes().c_str());
- strcpy(info, gEnv->getRes().c_str());
+ strcpy(mURL, sResourceDir.c_str());
+ strcpy(info, sResourceDir.c_str());
GetURLForComponent(mCompName, mURL, info);
eleInfo.open(info);
@@ -790,28 +749,24 @@
// frame after this so that the below section can be covered for all
// components
uint32_t numFramesFlushed = 128;
- ASSERT_NO_FATAL_FAILURE(decodeNFrames(
- mComponent, mQueueLock, mQueueCondition, mWorkQueue, mFlushedIndices,
- mLinearPool, eleStream, &Info, 0, numFramesFlushed, false));
+ ASSERT_NO_FATAL_FAILURE(decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
+ mFlushedIndices, mLinearPool, eleStream, &Info, 0,
+ numFramesFlushed, false));
// flush
std::list<std::unique_ptr<C2Work>> flushedWork;
- c2_status_t err =
- mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
+ c2_status_t err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
ASSERT_EQ(err, C2_OK);
- ASSERT_NO_FATAL_FAILURE(
- waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
- (size_t)MAX_INPUT_BUFFERS - flushedWork.size()));
+ ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
+ (size_t)MAX_INPUT_BUFFERS - flushedWork.size()));
{
// Update mFlushedIndices based on the index received from flush()
ULock l(mQueueLock);
for (std::unique_ptr<C2Work>& work : flushedWork) {
ASSERT_NE(work, nullptr);
- auto frameIndexIt =
- std::find(mFlushedIndices.begin(), mFlushedIndices.end(),
- work->input.ordinal.frameIndex.peeku());
- if (!mFlushedIndices.empty() &&
- (frameIndexIt != mFlushedIndices.end())) {
+ auto frameIndexIt = std::find(mFlushedIndices.begin(), mFlushedIndices.end(),
+ work->input.ordinal.frameIndex.peeku());
+ if (!mFlushedIndices.empty() && (frameIndexIt != mFlushedIndices.end())) {
mFlushedIndices.erase(frameIndexIt);
work->input.buffers.clear();
work->worklets.clear();
@@ -835,27 +790,24 @@
index++;
}
if (keyFrame) {
- ASSERT_NO_FATAL_FAILURE(
- decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
- mFlushedIndices, mLinearPool, eleStream, &Info, index,
- (int)Info.size() - index));
+ ASSERT_NO_FATAL_FAILURE(decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
+ mFlushedIndices, mLinearPool, eleStream, &Info, index,
+ (int)Info.size() - index));
}
eleStream.close();
err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
ASSERT_EQ(err, C2_OK);
- ASSERT_NO_FATAL_FAILURE(
- waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
- (size_t)MAX_INPUT_BUFFERS - flushedWork.size()));
+ ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
+ (size_t)MAX_INPUT_BUFFERS - flushedWork.size()));
{
// Update mFlushedIndices based on the index received from flush()
ULock l(mQueueLock);
for (std::unique_ptr<C2Work>& work : flushedWork) {
ASSERT_NE(work, nullptr);
uint64_t frameIndex = work->input.ordinal.frameIndex.peeku();
- std::list<uint64_t>::iterator frameIndexIt = std::find(
- mFlushedIndices.begin(), mFlushedIndices.end(), frameIndex);
- if (!mFlushedIndices.empty() &&
- (frameIndexIt != mFlushedIndices.end())) {
+ std::list<uint64_t>::iterator frameIndexIt =
+ std::find(mFlushedIndices.begin(), mFlushedIndices.end(), frameIndex);
+ if (!mFlushedIndices.empty() && (frameIndexIt != mFlushedIndices.end())) {
mFlushedIndices.erase(frameIndexIt);
work->input.buffers.clear();
work->worklets.clear();
@@ -867,15 +819,15 @@
ASSERT_EQ(mComponent->stop(), C2_OK);
}
-TEST_F(Codec2VideoDecHidlTest, DecodeTestEmptyBuffersInserted) {
+TEST_P(Codec2VideoDecHidlTest, DecodeTestEmptyBuffersInserted) {
description("Decode with multiple empty input frames");
- if (mDisableTest) return;
+ if (mDisableTest) GTEST_SKIP() << "Test is disabled";
char mURL[512], info[512];
std::ifstream eleStream, eleInfo;
- strcpy(mURL, gEnv->getRes().c_str());
- strcpy(info, gEnv->getRes().c_str());
+ strcpy(mURL, sResourceDir.c_str());
+ strcpy(info, sResourceDir.c_str());
GetURLForComponent(mCompName, mURL, info);
eleInfo.open(info);
@@ -890,15 +842,16 @@
// and empty input frames at an interval of 5 frames.
while (1) {
if (!(frameId % 5)) {
- if (!(frameId % 20)) flags = 32;
- else flags = 0;
+ if (!(frameId % 20))
+ flags = 32;
+ else
+ flags = 0;
bytesCount = 0;
} else {
if (!(eleInfo >> bytesCount)) break;
eleInfo >> flags;
eleInfo >> timestamp;
- codecConfig = flags ?
- ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0 : 0;
+ codecConfig = flags ? ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0 : 0;
}
Info.push_back({bytesCount, flags, timestamp});
frameId++;
@@ -909,39 +862,58 @@
ALOGV("mURL : %s", mURL);
eleStream.open(mURL, std::ifstream::binary);
ASSERT_EQ(eleStream.is_open(), true);
- ASSERT_NO_FATAL_FAILURE(decodeNFrames(
- mComponent, mQueueLock, mQueueCondition, mWorkQueue, mFlushedIndices,
- mLinearPool, eleStream, &Info, 0, (int)Info.size()));
+ ASSERT_NO_FATAL_FAILURE(decodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
+ mFlushedIndices, mLinearPool, eleStream, &Info, 0,
+ (int)Info.size()));
// blocking call to ensures application to Wait till all the inputs are
// consumed
if (!mEos) {
ALOGV("Waiting for input consumption");
- ASSERT_NO_FATAL_FAILURE(
- waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
+ ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
}
eleStream.close();
if (mFramesReceived != Info.size()) {
ALOGE("Input buffer count and Output buffer count mismatch");
- ALOGV("framesReceived : %d inputFrames : %zu", mFramesReceived,
- Info.size());
+ ALOGV("framesReceived : %d inputFrames : %zu", mFramesReceived, Info.size());
ASSERT_TRUE(false);
}
}
+INSTANTIATE_TEST_SUITE_P(PerInstance, Codec2VideoDecHidlTest, testing::ValuesIn(kTestParameters),
+ android::hardware::PrintInstanceTupleNameToString<>);
+
+// DecodeTest with StreamIndex and EOS / No EOS
+INSTANTIATE_TEST_SUITE_P(StreamIndexAndEOS, Codec2VideoDecDecodeTest,
+ testing::ValuesIn(kDecodeTestParameters),
+ android::hardware::PrintInstanceTupleNameToString<>);
+
} // anonymous namespace
// TODO : Video specific configuration Test
int main(int argc, char** argv) {
- gEnv = new ComponentTestEnvironment();
- ::testing::AddGlobalTestEnvironment(gEnv);
- ::testing::InitGoogleTest(&argc, argv);
- gEnv->init(&argc, argv);
- int status = gEnv->initFromOptions(argc, argv);
- if (status == 0) {
- int status = RUN_ALL_TESTS();
- LOG(INFO) << "C2 Test result = " << status;
+ kTestParameters = getTestParameters(C2Component::DOMAIN_VIDEO, C2Component::KIND_DECODER);
+ for (auto params : kTestParameters) {
+ kDecodeTestParameters.push_back(
+ std::make_tuple(std::get<0>(params), std::get<1>(params), "0", "false"));
+ kDecodeTestParameters.push_back(
+ std::make_tuple(std::get<0>(params), std::get<1>(params), "0", "true"));
+ kDecodeTestParameters.push_back(
+ std::make_tuple(std::get<0>(params), std::get<1>(params), "1", "false"));
+ kDecodeTestParameters.push_back(
+ std::make_tuple(std::get<0>(params), std::get<1>(params), "1", "true"));
}
- return status;
-}
+
+ // Set the resource directory based on command line args.
+ // Test will fail to set up if the argument is not set.
+ for (int i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-P") == 0 && i < argc - 1) {
+ sResourceDir = argv[i + 1];
+ break;
+ }
+ }
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
\ No newline at end of file
diff --git a/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.xml b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.xml
new file mode 100644
index 0000000..8761797
--- /dev/null
+++ b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the"License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an"AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs VtsHalMediaC2V1_0TargetVideoDecTest.">
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push-file" key="vts_media_c2_v1_0_video_dec_test" value="/data/local/tmp/vts_media_c2_v1_0_video_dec_test" />
+
+ <!-- Files used for video testing -->
+ <option name="push-file" key="bbb_avc_176x144_300kbps_60fps.h264" value="/data/local/tmp/media/bbb_avc_176x144_300kbps_60fps.h264" />
+ <option name="push-file" key="bbb_avc_640x360_768kbps_30fps.h264" value="/data/local/tmp/media/bbb_avc_640x360_768kbps_30fps.h264" />
+ <option name="push-file" key="bbb_avc_176x144_300kbps_60fps.info" value="/data/local/tmp/media/bbb_avc_176x144_300kbps_60fps.info" />
+ <option name="push-file" key="bbb_avc_640x360_768kbps_30fps.info" value="/data/local/tmp/media/bbb_avc_640x360_768kbps_30fps.info" />
+ <option name="push-file" key="bbb_hevc_176x144_176kbps_60fps.hevc" value="/data/local/tmp/media/bbb_hevc_176x144_176kbps_60fps.hevc" />
+ <option name="push-file" key="bbb_hevc_640x360_1600kbps_30fps.hevc" value="/data/local/tmp/media/bbb_hevc_640x360_1600kbps_30fps.hevc" />
+ <option name="push-file" key="bbb_hevc_176x144_176kbps_60fps.info" value="/data/local/tmp/media/bbb_hevc_176x144_176kbps_60fps.info" />
+ <option name="push-file" key="bbb_hevc_640x360_1600kbps_30fps.info" value="/data/local/tmp/media/bbb_hevc_640x360_1600kbps_30fps.info" />
+ <option name="push-file" key="bbb_mpeg2_176x144_105kbps_25fps.m2v" value="/data/local/tmp/media/bbb_mpeg2_176x144_105kbps_25fps.m2v" />
+ <option name="push-file" key="bbb_mpeg2_352x288_1mbps_60fps.m2v" value="/data/local/tmp/media/bbb_mpeg2_352x288_1mbps_60fps.m2v" />
+ <option name="push-file" key="bbb_mpeg2_176x144_105kbps_25fps.info" value="/data/local/tmp/media/bbb_mpeg2_176x144_105kbps_25fps.info" />
+ <option name="push-file" key="bbb_mpeg2_352x288_1mbps_60fps.info" value="/data/local/tmp/media/bbb_mpeg2_352x288_1mbps_60fps.info" />
+ <option name="push-file" key="bbb_h263_352x288_300kbps_12fps.h263" value="/data/local/tmp/media/bbb_h263_352x288_300kbps_12fps.h263" />
+ <option name="push-file" key="bbb_h263_352x288_300kbps_12fps.info" value="/data/local/tmp/media/bbb_h263_352x288_300kbps_12fps.info" />
+ <option name="push-file" key="bbb_mpeg4_352x288_512kbps_30fps.m4v" value="/data/local/tmp/media/bbb_mpeg4_352x288_512kbps_30fps.m4v" />
+ <option name="push-file" key="bbb_mpeg4_352x288_512kbps_30fps.info" value="/data/local/tmp/media/bbb_mpeg4_352x288_512kbps_30fps.info" />
+ <option name="push-file" key="bbb_vp8_176x144_240kbps_60fps.vp8" value="/data/local/tmp/media/bbb_vp8_176x144_240kbps_60fps.vp8" />
+ <option name="push-file" key="bbb_vp8_640x360_2mbps_30fps.vp8" value="/data/local/tmp/media/bbb_vp8_640x360_2mbps_30fps.vp8" />
+ <option name="push-file" key="bbb_vp8_176x144_240kbps_60fps.info" value="/data/local/tmp/media/bbb_vp8_176x144_240kbps_60fps.info" />
+ <option name="push-file" key="bbb_vp8_640x360_2mbps_30fps.info" value="/data/local/tmp/media/bbb_vp8_640x360_2mbps_30fps.info" />
+ <option name="push-file" key="bbb_vp9_176x144_285kbps_60fps.vp9" value="/data/local/tmp/media/bbb_vp9_176x144_285kbps_60fps.vp9" />
+ <option name="push-file" key="bbb_vp9_640x360_1600kbps_30fps.vp9" value="/data/local/tmp/media/bbb_vp9_640x360_1600kbps_30fps.vp9" />
+ <option name="push-file" key="bbb_vp9_176x144_285kbps_60fps.info" value="/data/local/tmp/media/bbb_vp9_176x144_285kbps_60fps.info" />
+ <option name="push-file" key="bbb_vp9_640x360_1600kbps_30fps.info" value="/data/local/tmp/media/bbb_vp9_640x360_1600kbps_30fps.info" />
+ <option name="push-file" key="bbb_av1_640_360.av1" value="/data/local/tmp/media/bbb_av1_640_360.av1" />
+ <option name="push-file" key="bbb_av1_176_144.av1" value="/data/local/tmp/media/bbb_av1_176_144.av1" />
+ <option name="push-file" key="bbb_av1_640_360.info" value="/data/local/tmp/media/bbb_av1_640_360.info" />
+ <option name="push-file" key="bbb_av1_176_144.info" value="/data/local/tmp/media/bbb_av1_176_144.info" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="vts_media_c2_v1_0_video_dec_test" />
+ <option name="native-test-flag" value="-P /data/local/tmp/media/" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoEncTest.cpp b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoEncTest.cpp
index 15f6acd..be97383 100644
--- a/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoEncTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoEncTest.cpp
@@ -19,73 +19,62 @@
#include <android-base/logging.h>
#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
#include <stdio.h>
#include <fstream>
-#include <codec2/hidl/client.h>
#include <C2AllocatorIon.h>
-#include <C2Config.h>
-#include <C2Debug.h>
#include <C2Buffer.h>
#include <C2BufferPriv.h>
+#include <C2Config.h>
+#include <C2Debug.h>
+#include <codec2/hidl/client.h>
using android::C2AllocatorIon;
-#include <VtsHalHidlTargetTestBase.h>
-#include "media_c2_video_hidl_test_common.h"
#include "media_c2_hidl_test_common.h"
+#include "media_c2_video_hidl_test_common.h"
class GraphicBuffer : public C2Buffer {
-public:
- explicit GraphicBuffer(const std::shared_ptr<C2GraphicBlock> &block)
- : C2Buffer({block->share(C2Rect(block->width(), block->height()),
- ::C2Fence())}) {}
+ public:
+ explicit GraphicBuffer(const std::shared_ptr<C2GraphicBlock>& block)
+ : C2Buffer({block->share(C2Rect(block->width(), block->height()), ::C2Fence())}) {}
};
-static ComponentTestEnvironment* gEnv = nullptr;
+static std::vector<std::tuple<std::string, std::string, std::string>> kEncodeTestParameters;
+static std::vector<std::tuple<std::string, std::string, std::string, std::string>>
+ kEncodeResolutionTestParameters;
+
+// Resource directory
+static std::string sResourceDir = "";
namespace {
-class Codec2VideoEncHidlTest : public ::testing::VtsHalHidlTargetTestBase {
- private:
- typedef ::testing::VtsHalHidlTargetTestBase Super;
-
- public:
- ::std::string getTestCaseInfo() const override {
- return ::std::string() +
- "Component: " + gEnv->getComponent().c_str() + " | " +
- "Instance: " + gEnv->getInstance().c_str() + " | " +
- "Res: " + gEnv->getRes().c_str();
- }
-
+class Codec2VideoEncHidlTestBase : public ::testing::Test {
+ public:
// google.codec2 Video test setup
virtual void SetUp() override {
- Super::SetUp();
+ getParams();
mDisableTest = false;
ALOGV("Codec2VideoEncHidlTest SetUp");
mClient = android::Codec2Client::CreateFromService(
- gEnv->getInstance().c_str(),
- !bool(android::Codec2Client::CreateFromService("default", true)));
+ mInstanceName.c_str(),
+ !bool(android::Codec2Client::CreateFromService("default", true)));
ASSERT_NE(mClient, nullptr);
- mListener.reset(new CodecListener(
- [this](std::list<std::unique_ptr<C2Work>>& workItems) {
- handleWorkDone(workItems);
- }));
+ mListener.reset(new CodecListener([this](std::list<std::unique_ptr<C2Work>>& workItems) {
+ handleWorkDone(workItems);
+ }));
ASSERT_NE(mListener, nullptr);
for (int i = 0; i < MAX_INPUT_BUFFERS; ++i) {
mWorkQueue.emplace_back(new C2Work);
}
- mClient->createComponent(gEnv->getComponent().c_str(), mListener,
- &mComponent);
+ mClient->createComponent(mComponentName, mListener, &mComponent);
ASSERT_NE(mComponent, nullptr);
- std::shared_ptr<C2AllocatorStore> store =
- android::GetCodec2PlatformAllocatorStore();
- CHECK_EQ(store->fetchAllocator(C2AllocatorStore::DEFAULT_GRAPHIC,
- &mGraphicAllocator),
+ std::shared_ptr<C2AllocatorStore> store = android::GetCodec2PlatformAllocatorStore();
+ CHECK_EQ(store->fetchAllocator(C2AllocatorStore::DEFAULT_GRAPHIC, &mGraphicAllocator),
C2_OK);
- mGraphicPool = std::make_shared<C2PooledBlockPool>(mGraphicAllocator,
- mBlockPoolId++);
+ mGraphicPool = std::make_shared<C2PooledBlockPool>(mGraphicAllocator, mBlockPoolId++);
ASSERT_NE(mGraphicPool, nullptr);
mCompName = unknown_comp;
@@ -95,17 +84,15 @@
};
const StringToName kStringToName[] = {
- {"h263", h263}, {"avc", avc}, {"mpeg4", mpeg4},
- {"hevc", hevc}, {"vp8", vp8}, {"vp9", vp9},
+ {"h263", h263}, {"avc", avc}, {"mpeg4", mpeg4},
+ {"hevc", hevc}, {"vp8", vp8}, {"vp9", vp9},
};
- const size_t kNumStringToName =
- sizeof(kStringToName) / sizeof(kStringToName[0]);
+ const size_t kNumStringToName = sizeof(kStringToName) / sizeof(kStringToName[0]);
// Find the component type
- std::string comp = std::string(gEnv->getComponent());
for (size_t i = 0; i < kNumStringToName; ++i) {
- if (strcasestr(comp.c_str(), kStringToName[i].Name)) {
+ if (strcasestr(mComponentName.c_str(), kStringToName[i].Name)) {
mCompName = kStringToName[i].CompName;
break;
}
@@ -126,9 +113,11 @@
mComponent->release();
mComponent = nullptr;
}
- Super::TearDown();
}
+ // Get the test parameters from GetParam call.
+ virtual void getParams() {}
+
bool setupConfigParam(int32_t nWidth, int32_t nHeight);
// callback function to process onWorkDone received by Listener
@@ -139,11 +128,9 @@
// previous timestamp
typedef std::unique_lock<std::mutex> ULock;
if (!mTimestampUslist.empty()) {
- EXPECT_GE((work->worklets.front()
- ->output.ordinal.timestamp.peeku()),
+ EXPECT_GE((work->worklets.front()->output.ordinal.timestamp.peeku()),
mTimestampUs);
- mTimestampUs = work->worklets.front()
- ->output.ordinal.timestamp.peeku();
+ mTimestampUs = work->worklets.front()->output.ordinal.timestamp.peeku();
// Currently this lock is redundant as no mTimestampUslist is only initialized
// before queuing any work to component. Once AdaptiveTest is added similar to
// the one in video decoders, this is needed.
@@ -151,8 +138,7 @@
if (mTimestampDevTest) {
bool tsHit = false;
- std::list<uint64_t>::iterator it =
- mTimestampUslist.begin();
+ std::list<uint64_t>::iterator it = mTimestampUslist.begin();
while (it != mTimestampUslist.end()) {
if (*it == mTimestampUs) {
mTimestampUslist.erase(it);
@@ -163,21 +149,18 @@
}
if (tsHit == false) {
if (mTimestampUslist.empty() == false) {
- EXPECT_EQ(tsHit, true)
- << "TimeStamp not recognized";
+ EXPECT_EQ(tsHit, true) << "TimeStamp not recognized";
} else {
- std::cout
- << "[ INFO ] Received non-zero "
- "output / TimeStamp not recognized \n";
+ std::cout << "[ INFO ] Received non-zero "
+ "output / TimeStamp not recognized \n";
}
}
}
}
if (work->result != C2_OK) mFailedWorkReceived++;
- workDone(mComponent, work, mFlushedIndices, mQueueLock,
- mQueueCondition, mWorkQueue, mEos, mCsd,
- mFramesReceived);
+ workDone(mComponent, work, mFlushedIndices, mQueueLock, mQueueCondition, mWorkQueue,
+ mEos, mCsd, mFramesReceived);
}
}
}
@@ -192,6 +175,8 @@
unknown_comp,
};
+ std::string mInstanceName;
+ std::string mComponentName;
bool mEos;
bool mCsd;
bool mDisableTest;
@@ -217,15 +202,23 @@
std::shared_ptr<android::Codec2Client::Listener> mListener;
std::shared_ptr<android::Codec2Client::Component> mComponent;
- protected:
+ protected:
static void description(const std::string& description) {
RecordProperty("description", description);
}
};
-void validateComponent(
- const std::shared_ptr<android::Codec2Client::Component>& component,
- Codec2VideoEncHidlTest::standardComp compName, bool& disableTest) {
+class Codec2VideoEncHidlTest
+ : public Codec2VideoEncHidlTestBase,
+ public ::testing::WithParamInterface<std::tuple<std::string, std::string>> {
+ void getParams() {
+ mInstanceName = std::get<0>(GetParam());
+ mComponentName = std::get<1>(GetParam());
+ }
+};
+
+void validateComponent(const std::shared_ptr<android::Codec2Client::Component>& component,
+ Codec2VideoEncHidlTest::standardComp compName, bool& disableTest) {
// Validate its a C2 Component
if (component->getName().find("c2") == std::string::npos) {
ALOGE("Not a c2 component");
@@ -240,14 +233,12 @@
return;
}
std::vector<std::unique_ptr<C2Param>> queried;
- c2_status_t c2err =
- component->query({}, {C2PortMediaTypeSetting::input::PARAM_TYPE},
- C2_DONT_BLOCK, &queried);
+ c2_status_t c2err = component->query({}, {C2PortMediaTypeSetting::input::PARAM_TYPE},
+ C2_DONT_BLOCK, &queried);
if (c2err != C2_OK && queried.size() == 0) {
ALOGE("Query media type failed => %d", c2err);
} else {
- std::string inputDomain =
- ((C2StreamMediaTypeSetting::input*)queried[0].get())->m.value;
+ std::string inputDomain = ((C2StreamMediaTypeSetting::input*)queried[0].get())->m.value;
if (inputDomain.find("video/") == std::string::npos) {
ALOGE("Expected Video Component");
disableTest = true;
@@ -265,12 +256,11 @@
}
// Set Default config param.
-bool Codec2VideoEncHidlTest::setupConfigParam(int32_t nWidth, int32_t nHeight) {
+bool Codec2VideoEncHidlTestBase::setupConfigParam(int32_t nWidth, int32_t nHeight) {
std::vector<std::unique_ptr<C2SettingResult>> failures;
C2StreamPictureSizeInfo::input inputSize(0u, nWidth, nHeight);
std::vector<C2Param*> configParam{&inputSize};
- c2_status_t status =
- mComponent->config(configParam, C2_DONT_BLOCK, &failures);
+ c2_status_t status = mComponent->config(configParam, C2_DONT_BLOCK, &failures);
if (status == C2_OK && failures.size() == 0u) return true;
return false;
}
@@ -281,13 +271,11 @@
}
void encodeNFrames(const std::shared_ptr<android::Codec2Client::Component>& component,
- std::mutex &queueLock, std::condition_variable& queueCondition,
+ std::mutex& queueLock, std::condition_variable& queueCondition,
std::list<std::unique_ptr<C2Work>>& workQueue,
- std::list<uint64_t>& flushedIndices,
- std::shared_ptr<C2BlockPool>& graphicPool,
- std::ifstream& eleStream, bool& disableTest,
- uint32_t frameID, uint32_t nFrames, uint32_t nWidth,
- int32_t nHeight, bool flushed = false, bool signalEOS = true) {
+ std::list<uint64_t>& flushedIndices, std::shared_ptr<C2BlockPool>& graphicPool,
+ std::ifstream& eleStream, bool& disableTest, uint32_t frameID, uint32_t nFrames,
+ uint32_t nWidth, int32_t nHeight, bool flushed = false, bool signalEOS = true) {
typedef std::unique_lock<std::mutex> ULock;
uint32_t maxRetry = 0;
@@ -313,8 +301,7 @@
if (!work && (maxRetry >= MAX_RETRY)) {
ASSERT_TRUE(false) << "Wait for generating C2Work exceeded timeout";
}
- if (signalEOS && (nFrames == 1))
- flags |= C2FrameData::FLAG_END_OF_STREAM;
+ if (signalEOS && (nFrames == 1)) flags |= C2FrameData::FLAG_END_OF_STREAM;
if (flushed) {
flags |= SYNC_FRAME;
flushed = false;
@@ -335,9 +322,9 @@
ASSERT_EQ(eleStream.gcount(), bytesCount);
}
std::shared_ptr<C2GraphicBlock> block;
- err = graphicPool->fetchGraphicBlock(
- nWidth, nHeight, HAL_PIXEL_FORMAT_YV12,
- {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}, &block);
+ err = graphicPool->fetchGraphicBlock(nWidth, nHeight, HAL_PIXEL_FORMAT_YV12,
+ {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE},
+ &block);
if (err != C2_OK) {
fprintf(stderr, "fetchGraphicBlock failed : %d\n", err);
disableTest = true;
@@ -380,27 +367,32 @@
}
}
-TEST_F(Codec2VideoEncHidlTest, validateCompName) {
- if (mDisableTest) return;
+TEST_P(Codec2VideoEncHidlTest, validateCompName) {
+ if (mDisableTest) GTEST_SKIP() << "Test is disabled";
ALOGV("Checks if the given component is a valid video component");
validateComponent(mComponent, mCompName, mDisableTest);
ASSERT_EQ(mDisableTest, false);
}
-class Codec2VideoEncEncodeTest : public Codec2VideoEncHidlTest,
- public ::testing::WithParamInterface<bool> {
+class Codec2VideoEncEncodeTest
+ : public Codec2VideoEncHidlTestBase,
+ public ::testing::WithParamInterface<std::tuple<std::string, std::string, std::string>> {
+ void getParams() {
+ mInstanceName = std::get<0>(GetParam());
+ mComponentName = std::get<1>(GetParam());
+ }
};
TEST_P(Codec2VideoEncEncodeTest, EncodeTest) {
description("Encodes input file");
- if (mDisableTest) return;
+ if (mDisableTest) GTEST_SKIP() << "Test is disabled";
char mURL[512];
int32_t nWidth = ENC_DEFAULT_FRAME_WIDTH;
int32_t nHeight = ENC_DEFAULT_FRAME_HEIGHT;
- bool signalEOS = GetParam();
+ bool signalEOS = !std::get<2>(GetParam()).compare("true");
- strcpy(mURL, gEnv->getRes().c_str());
+ strcpy(mURL, sResourceDir.c_str());
GetURLForComponent(mURL);
std::ifstream eleStream;
@@ -425,10 +417,9 @@
return;
}
ASSERT_EQ(mComponent->start(), C2_OK);
- ASSERT_NO_FATAL_FAILURE(
- encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
- mFlushedIndices, mGraphicPool, eleStream, mDisableTest,
- 0, ENC_NUM_FRAMES, nWidth, nHeight, false, signalEOS));
+ ASSERT_NO_FATAL_FAILURE(encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
+ mFlushedIndices, mGraphicPool, eleStream, mDisableTest, 0,
+ ENC_NUM_FRAMES, nWidth, nHeight, false, signalEOS));
// mDisableTest will be set if buffer was not fetched properly.
// This may happen when resolution is not proper but config suceeded
// In this cases, we skip encoding the input stream
@@ -441,25 +432,21 @@
// If EOS is not sent, sending empty input with EOS flag
inputFrames = ENC_NUM_FRAMES;
if (!signalEOS) {
- ASSERT_NO_FATAL_FAILURE(
- waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue, 1));
- ASSERT_NO_FATAL_FAILURE(
- testInputBuffer(mComponent, mQueueLock, mWorkQueue,
- C2FrameData::FLAG_END_OF_STREAM, false));
+ ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue, 1));
+ ASSERT_NO_FATAL_FAILURE(testInputBuffer(mComponent, mQueueLock, mWorkQueue,
+ C2FrameData::FLAG_END_OF_STREAM, false));
inputFrames += 1;
}
// blocking call to ensures application to Wait till all the inputs are
// consumed
ALOGD("Waiting for input consumption");
- ASSERT_NO_FATAL_FAILURE(
- waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
+ ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
eleStream.close();
if (mFramesReceived != inputFrames) {
ALOGE("Input buffer count and Output buffer count mismatch");
- ALOGE("framesReceived : %d inputFrames : %d", mFramesReceived,
- inputFrames);
+ ALOGE("framesReceived : %d inputFrames : %d", mFramesReceived, inputFrames);
ASSERT_TRUE(false);
}
@@ -475,13 +462,9 @@
ASSERT_EQ(mComponent->stop(), C2_OK);
}
-// EncodeTest with EOS / No EOS
-INSTANTIATE_TEST_CASE_P(EncodeTestwithEOS, Codec2VideoEncEncodeTest,
- ::testing::Values(true, false));
-
-TEST_F(Codec2VideoEncHidlTest, EOSTest) {
+TEST_P(Codec2VideoEncHidlTest, EOSTest) {
description("Test empty input buffer with EOS flag");
- if (mDisableTest) return;
+ if (mDisableTest) GTEST_SKIP() << "Test is disabled";
ASSERT_EQ(mComponent->start(), C2_OK);
typedef std::unique_lock<std::mutex> ULock;
@@ -519,15 +502,15 @@
ASSERT_EQ(mComponent->stop(), C2_OK);
}
-TEST_F(Codec2VideoEncHidlTest, FlushTest) {
+TEST_P(Codec2VideoEncHidlTest, FlushTest) {
description("Test Request for flush");
- if (mDisableTest) return;
+ if (mDisableTest) GTEST_SKIP() << "Test is disabled";
typedef std::unique_lock<std::mutex> ULock;
char mURL[512];
int32_t nWidth = ENC_DEFAULT_FRAME_WIDTH;
int32_t nHeight = ENC_DEFAULT_FRAME_HEIGHT;
- strcpy(mURL, gEnv->getRes().c_str());
+ strcpy(mURL, sResourceDir.c_str());
GetURLForComponent(mURL);
if (!setupConfigParam(nWidth, nHeight)) {
@@ -544,10 +527,9 @@
eleStream.open(mURL, std::ifstream::binary);
ASSERT_EQ(eleStream.is_open(), true);
ALOGV("mURL : %s", mURL);
- ASSERT_NO_FATAL_FAILURE(
- encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
- mFlushedIndices, mGraphicPool, eleStream, mDisableTest,
- 0, numFramesFlushed, nWidth, nHeight));
+ ASSERT_NO_FATAL_FAILURE(encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
+ mFlushedIndices, mGraphicPool, eleStream, mDisableTest, 0,
+ numFramesFlushed, nWidth, nHeight));
// mDisableTest will be set if buffer was not fetched properly.
// This may happen when resolution is not proper but config suceeded
// In this cases, we skip encoding the input stream
@@ -558,23 +540,20 @@
}
std::list<std::unique_ptr<C2Work>> flushedWork;
- c2_status_t err =
- mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
+ c2_status_t err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
ASSERT_EQ(err, C2_OK);
- ASSERT_NO_FATAL_FAILURE(
- waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
- (size_t)MAX_INPUT_BUFFERS - flushedWork.size()));
+ ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
+ (size_t)MAX_INPUT_BUFFERS - flushedWork.size()));
uint64_t frameIndex;
{
- //Update mFlushedIndices based on the index received from flush()
+ // Update mFlushedIndices based on the index received from flush()
ULock l(mQueueLock);
for (std::unique_ptr<C2Work>& work : flushedWork) {
ASSERT_NE(work, nullptr);
frameIndex = work->input.ordinal.frameIndex.peeku();
- std::list<uint64_t>::iterator frameIndexIt = std::find(
- mFlushedIndices.begin(), mFlushedIndices.end(), frameIndex);
- if (!mFlushedIndices.empty() &&
- (frameIndexIt != mFlushedIndices.end())) {
+ std::list<uint64_t>::iterator frameIndexIt =
+ std::find(mFlushedIndices.begin(), mFlushedIndices.end(), frameIndex);
+ if (!mFlushedIndices.empty() && (frameIndexIt != mFlushedIndices.end())) {
mFlushedIndices.erase(frameIndexIt);
work->input.buffers.clear();
work->worklets.clear();
@@ -583,11 +562,10 @@
}
}
mFlushedIndices.clear();
- ASSERT_NO_FATAL_FAILURE(
- encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
- mFlushedIndices, mGraphicPool, eleStream, mDisableTest,
- numFramesFlushed, numFrames - numFramesFlushed,
- nWidth, nHeight, true));
+ ASSERT_NO_FATAL_FAILURE(encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
+ mFlushedIndices, mGraphicPool, eleStream, mDisableTest,
+ numFramesFlushed, numFrames - numFramesFlushed, nWidth,
+ nHeight, true));
eleStream.close();
// mDisableTest will be set if buffer was not fetched properly.
// This may happen when resolution is not proper but config suceeded
@@ -600,19 +578,17 @@
err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
ASSERT_EQ(err, C2_OK);
- ASSERT_NO_FATAL_FAILURE(
- waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
- (size_t)MAX_INPUT_BUFFERS - flushedWork.size()));
+ ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue,
+ (size_t)MAX_INPUT_BUFFERS - flushedWork.size()));
{
- //Update mFlushedIndices based on the index received from flush()
+ // Update mFlushedIndices based on the index received from flush()
ULock l(mQueueLock);
for (std::unique_ptr<C2Work>& work : flushedWork) {
ASSERT_NE(work, nullptr);
frameIndex = work->input.ordinal.frameIndex.peeku();
- std::list<uint64_t>::iterator frameIndexIt = std::find(
- mFlushedIndices.begin(), mFlushedIndices.end(), frameIndex);
- if (!mFlushedIndices.empty() &&
- (frameIndexIt != mFlushedIndices.end())) {
+ std::list<uint64_t>::iterator frameIndexIt =
+ std::find(mFlushedIndices.begin(), mFlushedIndices.end(), frameIndex);
+ if (!mFlushedIndices.empty() && (frameIndexIt != mFlushedIndices.end())) {
mFlushedIndices.erase(frameIndexIt);
work->input.buffers.clear();
work->worklets.clear();
@@ -624,9 +600,9 @@
ASSERT_EQ(mComponent->stop(), C2_OK);
}
-TEST_F(Codec2VideoEncHidlTest, InvalidBufferTest) {
+TEST_P(Codec2VideoEncHidlTest, InvalidBufferTest) {
description("Tests feeding larger/smaller input buffer");
- if (mDisableTest) return;
+ if (mDisableTest) GTEST_SKIP() << "Test is disabled";
std::ifstream eleStream;
int32_t nWidth = ENC_DEFAULT_FRAME_WIDTH / 2;
@@ -638,28 +614,24 @@
}
ASSERT_EQ(mComponent->start(), C2_OK);
- ASSERT_NO_FATAL_FAILURE(
- encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
- mFlushedIndices, mGraphicPool, eleStream, mDisableTest,
- 0, 1, nWidth, nHeight, false, false));
+ ASSERT_NO_FATAL_FAILURE(encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
+ mFlushedIndices, mGraphicPool, eleStream, mDisableTest, 0,
+ 1, nWidth, nHeight, false, false));
// Feed larger input buffer.
- ASSERT_NO_FATAL_FAILURE(
- encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
- mFlushedIndices, mGraphicPool, eleStream, mDisableTest,
- 1, 1, nWidth*2, nHeight*2, false, false));
+ ASSERT_NO_FATAL_FAILURE(encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
+ mFlushedIndices, mGraphicPool, eleStream, mDisableTest, 1,
+ 1, nWidth * 2, nHeight * 2, false, false));
// Feed smaller input buffer.
- ASSERT_NO_FATAL_FAILURE(
- encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
- mFlushedIndices, mGraphicPool, eleStream, mDisableTest,
- 2, 1, nWidth/2, nHeight/2, false, true));
+ ASSERT_NO_FATAL_FAILURE(encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
+ mFlushedIndices, mGraphicPool, eleStream, mDisableTest, 2,
+ 1, nWidth / 2, nHeight / 2, false, true));
// blocking call to ensures application to Wait till all the inputs are
// consumed
ALOGD("Waiting for input consumption");
- ASSERT_NO_FATAL_FAILURE(
- waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
+ ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
if (mFramesReceived != 3) {
std::cout << "[ WARN ] Component didn't receive all buffers back \n";
@@ -674,17 +646,23 @@
ASSERT_EQ(mComponent->stop(), C2_OK);
}
-class Codec2VideoEncResolutionTest : public Codec2VideoEncHidlTest,
- public ::testing::WithParamInterface<std::pair<int32_t, int32_t> > {
+class Codec2VideoEncResolutionTest
+ : public Codec2VideoEncHidlTestBase,
+ public ::testing::WithParamInterface<
+ std::tuple<std::string, std::string, std::string, std::string>> {
+ void getParams() {
+ mInstanceName = std::get<0>(GetParam());
+ mComponentName = std::get<1>(GetParam());
+ }
};
TEST_P(Codec2VideoEncResolutionTest, ResolutionTest) {
description("Tests encoding at different resolutions");
- if (mDisableTest) return;
+ if (mDisableTest) GTEST_SKIP() << "Test is disabled";
std::ifstream eleStream;
- int32_t nWidth = GetParam().first;
- int32_t nHeight = GetParam().second;
+ int32_t nWidth = std::stoi(std::get<2>(GetParam()));
+ int32_t nHeight = std::stoi(std::get<3>(GetParam()));
ALOGD("Trying encode for width %d height %d", nWidth, nHeight);
mEos = false;
@@ -694,10 +672,9 @@
}
ASSERT_EQ(mComponent->start(), C2_OK);
- ASSERT_NO_FATAL_FAILURE(
- encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
- mFlushedIndices, mGraphicPool, eleStream, mDisableTest,
- 0, MAX_INPUT_BUFFERS, nWidth, nHeight, false, true));
+ ASSERT_NO_FATAL_FAILURE(encodeNFrames(mComponent, mQueueLock, mQueueCondition, mWorkQueue,
+ mFlushedIndices, mGraphicPool, eleStream, mDisableTest, 0,
+ MAX_INPUT_BUFFERS, nWidth, nHeight, false, true));
// mDisableTest will be set if buffer was not fetched properly.
// This may happen when resolution is not proper but config suceeded
@@ -709,31 +686,52 @@
}
ALOGD("Waiting for input consumption");
- ASSERT_NO_FATAL_FAILURE(
- waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
+ ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
ASSERT_EQ(mEos, true);
ASSERT_EQ(mComponent->stop(), C2_OK);
ASSERT_EQ(mComponent->reset(), C2_OK);
}
-INSTANTIATE_TEST_CASE_P(NonStdSizes, Codec2VideoEncResolutionTest, ::testing::Values(
- std::make_pair(52, 18),
- std::make_pair(365, 365),
- std::make_pair(484, 362),
- std::make_pair(244, 488)));
+INSTANTIATE_TEST_SUITE_P(PerInstance, Codec2VideoEncHidlTest, testing::ValuesIn(kTestParameters),
+ android::hardware::PrintInstanceTupleNameToString<>);
+
+INSTANTIATE_TEST_SUITE_P(NonStdSizes, Codec2VideoEncResolutionTest,
+ ::testing::ValuesIn(kEncodeResolutionTestParameters));
+
+// EncodeTest with EOS / No EOS
+INSTANTIATE_TEST_SUITE_P(EncodeTestwithEOS, Codec2VideoEncEncodeTest,
+ ::testing::ValuesIn(kEncodeTestParameters));
} // anonymous namespace
int main(int argc, char** argv) {
- gEnv = new ComponentTestEnvironment();
- ::testing::AddGlobalTestEnvironment(gEnv);
- ::testing::InitGoogleTest(&argc, argv);
- gEnv->init(&argc, argv);
- int status = gEnv->initFromOptions(argc, argv);
- if (status == 0) {
- int status = RUN_ALL_TESTS();
- LOG(INFO) << "C2 Test result = " << status;
+ kTestParameters = getTestParameters(C2Component::DOMAIN_VIDEO, C2Component::KIND_ENCODER);
+ for (auto params : kTestParameters) {
+ kEncodeTestParameters.push_back(
+ std::make_tuple(std::get<0>(params), std::get<1>(params), "true"));
+ kEncodeTestParameters.push_back(
+ std::make_tuple(std::get<0>(params), std::get<1>(params), "false"));
+
+ kEncodeResolutionTestParameters.push_back(
+ std::make_tuple(std::get<0>(params), std::get<1>(params), "52", "18"));
+ kEncodeResolutionTestParameters.push_back(
+ std::make_tuple(std::get<0>(params), std::get<1>(params), "365", "365"));
+ kEncodeResolutionTestParameters.push_back(
+ std::make_tuple(std::get<0>(params), std::get<1>(params), "484", "362"));
+ kEncodeResolutionTestParameters.push_back(
+ std::make_tuple(std::get<0>(params), std::get<1>(params), "244", "488"));
}
- return status;
-}
+
+ // Set the resource directory based on command line args.
+ // Test will fail to set up if the argument is not set.
+ for (int i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-P") == 0 && i < argc - 1) {
+ sResourceDir = argv[i + 1];
+ break;
+ }
+ }
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
\ No newline at end of file
diff --git a/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoEncTest.xml b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoEncTest.xml
new file mode 100644
index 0000000..260a616
--- /dev/null
+++ b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoEncTest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the"License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an"AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs VtsHalMediaC2V1_0TargetVideoEncTest.">
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push-file" key="vts_media_c2_v1_0_video_enc_test" value="/data/local/tmp/vts_media_c2_v1_0_video_enc_test" />
+
+ <!-- Files used for video testing -->
+ <option name="push-file" key="bbb_352x288_420p_30fps_32frames.yuv" value="/data/local/tmp/media/bbb_352x288_420p_30fps_32frames.yuv" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="vts_media_c2_v1_0_video_enc_test" />
+ <option name="native-test-flag" value="-P /data/local/tmp/media/" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/media/codec2/hidl/1.0/vts/functional/video/media_c2_video_hidl_test_common.h b/media/codec2/hidl/1.0/vts/functional/video/media_c2_video_hidl_test_common.h
index e37ca38..d3a693b 100644
--- a/media/codec2/hidl/1.0/vts/functional/video/media_c2_video_hidl_test_common.h
+++ b/media/codec2/hidl/1.0/vts/functional/video/media_c2_video_hidl_test_common.h
@@ -29,5 +29,4 @@
* Common video utils
*/
-
#endif // MEDIA_C2_VIDEO_HIDL_TEST_COMMON_H
diff --git a/media/codec2/hidl/client/client.cpp b/media/codec2/hidl/client/client.cpp
index 0acab49..7e4352d 100644
--- a/media/codec2/hidl/client/client.cpp
+++ b/media/codec2/hidl/client/client.cpp
@@ -219,6 +219,7 @@
if (success) {
break;
}
+ invalidate();
using namespace std::chrono_literals;
static constexpr auto kServiceRetryPeriod = 5s;
LOG(INFO) << "Failed to retrieve component traits from service "
diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp
index cc132de..01d106f 100644
--- a/media/codec2/sfplugin/CCodec.cpp
+++ b/media/codec2/sfplugin/CCodec.cpp
@@ -1948,6 +1948,13 @@
inputSurface->getHalInterface()));
}
+static void MaybeLogUnrecognizedName(const char *func, const std::string &name) {
+ thread_local std::set<std::string> sLogged{};
+ if (sLogged.insert(name).second) {
+ ALOGW("%s: Unrecognized interface name: %s", func, name.c_str());
+ }
+}
+
static status_t GetCommonAllocatorIds(
const std::vector<std::string> &names,
C2Allocator::type_t type,
@@ -1961,26 +1968,33 @@
if (names.empty()) {
return OK;
}
- std::shared_ptr<Codec2Client::Interface> intf{
- Codec2Client::CreateInterfaceByName(names[0].c_str())};
- std::vector<std::unique_ptr<C2Param>> params;
- c2_status_t err = intf->query(
- {}, {C2PortAllocatorsTuning::input::PARAM_TYPE}, C2_MAY_BLOCK, ¶ms);
- if (err == C2_OK && params.size() == 1u) {
- C2PortAllocatorsTuning::input *allocators =
- C2PortAllocatorsTuning::input::From(params[0].get());
- if (allocators && allocators->flexCount() > 0) {
- ids->insert(allocators->m.values, allocators->m.values + allocators->flexCount());
+ bool firstIteration = true;
+ for (const std::string &name : names) {
+ std::shared_ptr<Codec2Client::Interface> intf{
+ Codec2Client::CreateInterfaceByName(name.c_str())};
+ if (!intf) {
+ MaybeLogUnrecognizedName(__FUNCTION__, name);
+ continue;
}
- }
- if (ids->empty()) {
- // The component does not advertise allocators. Use default.
- ids->insert(defaultAllocatorId);
- }
- for (size_t i = 1; i < names.size(); ++i) {
- intf = Codec2Client::CreateInterfaceByName(names[i].c_str());
- err = intf->query(
+ std::vector<std::unique_ptr<C2Param>> params;
+ c2_status_t err = intf->query(
{}, {C2PortAllocatorsTuning::input::PARAM_TYPE}, C2_MAY_BLOCK, ¶ms);
+ if (firstIteration) {
+ firstIteration = false;
+ if (err == C2_OK && params.size() == 1u) {
+ C2PortAllocatorsTuning::input *allocators =
+ C2PortAllocatorsTuning::input::From(params[0].get());
+ if (allocators && allocators->flexCount() > 0) {
+ ids->insert(allocators->m.values,
+ allocators->m.values + allocators->flexCount());
+ }
+ }
+ if (ids->empty()) {
+ // The component does not advertise allocators. Use default.
+ ids->insert(defaultAllocatorId);
+ }
+ continue;
+ }
bool filtered = false;
if (err == C2_OK && params.size() == 1u) {
C2PortAllocatorsTuning::input *allocators =
@@ -2033,6 +2047,10 @@
for (const std::string &name : names) {
std::shared_ptr<Codec2Client::Interface> intf{
Codec2Client::CreateInterfaceByName(name.c_str())};
+ if (!intf) {
+ MaybeLogUnrecognizedName(__FUNCTION__, name);
+ continue;
+ }
std::vector<C2FieldSupportedValuesQuery> fields;
fields.push_back(C2FieldSupportedValuesQuery::Possible(
C2ParamField{&sUsage, &sUsage.value}));
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 6b389d5..e902b5d 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -44,6 +44,7 @@
#include <media/stagefright/MediaCodecConstants.h>
#include <media/stagefright/SkipCutBuffer.h>
#include <media/MediaCodecBuffer.h>
+#include <mediadrm/ICrypto.h>
#include <system/window.h>
#include "CCodecBufferChannel.h"
@@ -1084,7 +1085,7 @@
// TODO: handle this without going into array mode
forceArrayMode = true;
} else {
- input->buffers.reset(new GraphicInputBuffers(numInputSlots, mName));
+ input->buffers.reset(new GraphicInputBuffers(mName));
}
} else {
if (hasCryptoOrDescrambler()) {
@@ -1252,7 +1253,7 @@
if (outputSurface || !buffersBoundToCodec) {
output->buffers.reset(new GraphicOutputBuffers(mName));
} else {
- output->buffers.reset(new RawGraphicOutputBuffers(numOutputSlots, mName));
+ output->buffers.reset(new RawGraphicOutputBuffers(mName));
}
} else {
output->buffers.reset(new LinearOutputBuffers(mName));
diff --git a/media/codec2/sfplugin/CCodecBuffers.cpp b/media/codec2/sfplugin/CCodecBuffers.cpp
index d7cc175..a9120c4 100644
--- a/media/codec2/sfplugin/CCodecBuffers.cpp
+++ b/media/codec2/sfplugin/CCodecBuffers.cpp
@@ -23,6 +23,7 @@
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/MediaCodecConstants.h>
#include <media/stagefright/SkipCutBuffer.h>
+#include <mediadrm/ICrypto.h>
#include "CCodecBuffers.h"
@@ -121,6 +122,11 @@
// OutputBuffers
+OutputBuffers::OutputBuffers(const char *componentName, const char *name)
+ : CCodecBuffers(componentName, name) { }
+
+OutputBuffers::~OutputBuffers() = default;
+
void OutputBuffers::initSkipCutBuffer(
int32_t delay, int32_t padding, int32_t sampleRate, int32_t channelCount) {
CHECK(mSkipCutBuffer == nullptr);
@@ -171,8 +177,11 @@
// LocalBufferPool
-std::shared_ptr<LocalBufferPool> LocalBufferPool::Create(size_t poolCapacity) {
- return std::shared_ptr<LocalBufferPool>(new LocalBufferPool(poolCapacity));
+constexpr size_t kInitialPoolCapacity = kMaxLinearBufferSize;
+constexpr size_t kMaxPoolCapacity = kMaxLinearBufferSize * 32;
+
+std::shared_ptr<LocalBufferPool> LocalBufferPool::Create() {
+ return std::shared_ptr<LocalBufferPool>(new LocalBufferPool(kInitialPoolCapacity));
}
sp<ABuffer> LocalBufferPool::newBuffer(size_t capacity) {
@@ -192,6 +201,11 @@
mUsedSize -= mPool.back().capacity();
mPool.pop_back();
}
+ while (mUsedSize + capacity > mPoolCapacity && mPoolCapacity * 2 <= kMaxPoolCapacity) {
+ ALOGD("Increasing local buffer pool capacity from %zu to %zu",
+ mPoolCapacity, mPoolCapacity * 2);
+ mPoolCapacity *= 2;
+ }
if (mUsedSize + capacity > mPoolCapacity) {
ALOGD("mUsedSize = %zu, capacity = %zu, mPoolCapacity = %zu",
mUsedSize, capacity, mPoolCapacity);
@@ -777,11 +791,10 @@
// GraphicInputBuffers
GraphicInputBuffers::GraphicInputBuffers(
- size_t numInputSlots, const char *componentName, const char *name)
+ const char *componentName, const char *name)
: InputBuffers(componentName, name),
mImpl(mName),
- mLocalBufferPool(LocalBufferPool::Create(
- kMaxLinearBufferSize * numInputSlots)) { }
+ mLocalBufferPool(LocalBufferPool::Create()) { }
bool GraphicInputBuffers::requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) {
sp<Codec2Buffer> newBuffer = createNewBuffer();
@@ -942,7 +955,7 @@
case C2BufferData::GRAPHIC: {
// This is only called for RawGraphicOutputBuffers.
mAlloc = [format = mFormat,
- lbp = LocalBufferPool::Create(kMaxLinearBufferSize * mImpl.arraySize())] {
+ lbp = LocalBufferPool::Create()] {
return ConstGraphicBlockBuffer::AllocateEmpty(
format,
[lbp](size_t capacity) {
@@ -1079,10 +1092,9 @@
// RawGraphicOutputBuffers
RawGraphicOutputBuffers::RawGraphicOutputBuffers(
- size_t numOutputSlots, const char *componentName, const char *name)
+ const char *componentName, const char *name)
: FlexOutputBuffers(componentName, name),
- mLocalBufferPool(LocalBufferPool::Create(
- kMaxLinearBufferSize * numOutputSlots)) { }
+ mLocalBufferPool(LocalBufferPool::Create()) { }
sp<Codec2Buffer> RawGraphicOutputBuffers::wrap(const std::shared_ptr<C2Buffer> &buffer) {
if (buffer == nullptr) {
diff --git a/media/codec2/sfplugin/CCodecBuffers.h b/media/codec2/sfplugin/CCodecBuffers.h
index 85ca5d5..6244acd 100644
--- a/media/codec2/sfplugin/CCodecBuffers.h
+++ b/media/codec2/sfplugin/CCodecBuffers.h
@@ -28,6 +28,8 @@
namespace android {
+struct ICrypto;
+class MemoryDealer;
class SkipCutBuffer;
constexpr size_t kLinearBufferSize = 1048576;
@@ -156,9 +158,8 @@
class OutputBuffers : public CCodecBuffers {
public:
- OutputBuffers(const char *componentName, const char *name = "Output")
- : CCodecBuffers(componentName, name) { }
- virtual ~OutputBuffers() = default;
+ OutputBuffers(const char *componentName, const char *name = "Output");
+ virtual ~OutputBuffers();
/**
* Register output C2Buffer from the component and obtain corresponding
@@ -243,11 +244,9 @@
/**
* Create a new LocalBufferPool object.
*
- * \param poolCapacity max total size of buffers managed by this pool.
- *
* \return a newly created pool object.
*/
- static std::shared_ptr<LocalBufferPool> Create(size_t poolCapacity);
+ static std::shared_ptr<LocalBufferPool> Create();
/**
* Return an ABuffer object whose size is at least |capacity|.
@@ -679,8 +678,7 @@
class GraphicInputBuffers : public InputBuffers {
public:
- GraphicInputBuffers(
- size_t numInputSlots, const char *componentName, const char *name = "2D-BB-Input");
+ GraphicInputBuffers(const char *componentName, const char *name = "2D-BB-Input");
~GraphicInputBuffers() override = default;
bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) override;
@@ -892,8 +890,7 @@
class RawGraphicOutputBuffers : public FlexOutputBuffers {
public:
- RawGraphicOutputBuffers(
- size_t numOutputSlots, const char *componentName, const char *name = "2D-BB-Output");
+ RawGraphicOutputBuffers(const char *componentName, const char *name = "2D-BB-Output");
~RawGraphicOutputBuffers() override = default;
sp<Codec2Buffer> wrap(const std::shared_ptr<C2Buffer> &buffer) override;
diff --git a/media/codec2/sfplugin/CCodecConfig.cpp b/media/codec2/sfplugin/CCodecConfig.cpp
index 051f88a..96f86e8 100644
--- a/media/codec2/sfplugin/CCodecConfig.cpp
+++ b/media/codec2/sfplugin/CCodecConfig.cpp
@@ -617,6 +617,11 @@
return C2Value();
}));
+ add(ConfigMapper(KEY_PIXEL_ASPECT_RATIO_WIDTH, C2_PARAMKEY_PIXEL_ASPECT_RATIO, "width")
+ .limitTo((D::VIDEO | D::IMAGE) & D::RAW));
+ add(ConfigMapper(KEY_PIXEL_ASPECT_RATIO_HEIGHT, C2_PARAMKEY_PIXEL_ASPECT_RATIO, "height")
+ .limitTo((D::VIDEO | D::IMAGE) & D::RAW));
+
add(ConfigMapper(KEY_CHANNEL_COUNT, C2_PARAMKEY_CHANNEL_COUNT, "value")
.limitTo(D::AUDIO)); // read back to both formats
add(ConfigMapper(KEY_CHANNEL_COUNT, C2_PARAMKEY_CODED_CHANNEL_COUNT, "value")
diff --git a/media/codec2/sfplugin/Codec2Buffer.cpp b/media/codec2/sfplugin/Codec2Buffer.cpp
index 5b3a62f..25e7da9 100644
--- a/media/codec2/sfplugin/Codec2Buffer.cpp
+++ b/media/codec2/sfplugin/Codec2Buffer.cpp
@@ -18,6 +18,8 @@
#define LOG_TAG "Codec2Buffer"
#include <utils/Log.h>
+#include <android/hardware/cas/native/1.0/types.h>
+#include <android/hardware/drm/1.0/types.h>
#include <hidlmemory/FrameworkUtils.h>
#include <media/hardware/HardwareAPI.h>
#include <media/stagefright/CodecBase.h>
@@ -25,6 +27,7 @@
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/foundation/AUtils.h>
+#include <mediadrm/ICrypto.h>
#include <nativebase/nativebase.h>
#include <ui/Fence.h>
diff --git a/media/codec2/sfplugin/Codec2Buffer.h b/media/codec2/sfplugin/Codec2Buffer.h
index ff79946..dc788cd 100644
--- a/media/codec2/sfplugin/Codec2Buffer.h
+++ b/media/codec2/sfplugin/Codec2Buffer.h
@@ -20,16 +20,29 @@
#include <C2Buffer.h>
-#include <android/hardware/cas/native/1.0/types.h>
-#include <android/hardware/drm/1.0/types.h>
#include <binder/IMemory.h>
#include <media/hardware/VideoAPI.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/MediaCodecBuffer.h>
-#include <mediadrm/ICrypto.h>
namespace android {
+namespace hardware {
+class HidlMemory;
+namespace cas {
+namespace native {
+namespace V1_0 {
+struct SharedBuffer;
+} // namespace V1_0
+} // namespace native
+} // namespace cas
+namespace drm {
+namespace V1_0 {
+struct SharedBuffer;
+} // namespace V1_0
+} // namespace drm
+} // namespace hardware
+
/**
* Copies a graphic view into a media image.
*
diff --git a/media/codec2/sfplugin/tests/Android.bp b/media/codec2/sfplugin/tests/Android.bp
index fe5fa68..8d1a9c3 100644
--- a/media/codec2/sfplugin/tests/Android.bp
+++ b/media/codec2/sfplugin/tests/Android.bp
@@ -2,12 +2,13 @@
name: "ccodec_unit_test",
srcs: [
+ "CCodecBuffers_test.cpp",
"CCodecConfig_test.cpp",
"ReflectedParamUpdater_test.cpp",
],
defaults: [
- "libcodec2-hidl-defaults@1.0",
+ "libcodec2-impl-defaults",
"libcodec2-internal-defaults",
],
@@ -16,14 +17,24 @@
],
shared_libs: [
+ "android.hardware.media.bufferpool@2.0",
+ "android.hardware.media.c2@1.0",
"libcodec2",
"libcodec2_client",
+ "libhidlbase",
+ "libfmq",
+ "libmedia_omx",
"libsfplugin_ccodec",
"libsfplugin_ccodec_utils",
"libstagefright_foundation",
"libutils",
],
+ static_libs: [
+ "libcodec2_hidl@1.0",
+ "libstagefright_bufferpool@2.0",
+ ],
+
cflags: [
"-Werror",
"-Wall",
diff --git a/media/codec2/sfplugin/tests/CCodecBuffers_test.cpp b/media/codec2/sfplugin/tests/CCodecBuffers_test.cpp
new file mode 100644
index 0000000..5bee605
--- /dev/null
+++ b/media/codec2/sfplugin/tests/CCodecBuffers_test.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CCodecBuffers.h"
+
+#include <gtest/gtest.h>
+
+#include <media/stagefright/MediaCodecConstants.h>
+
+#include <C2PlatformSupport.h>
+
+namespace android {
+
+TEST(RawGraphicOutputBuffersTest, ChangeNumSlots) {
+ constexpr int32_t kWidth = 3840;
+ constexpr int32_t kHeight = 2160;
+
+ std::shared_ptr<RawGraphicOutputBuffers> buffers =
+ std::make_shared<RawGraphicOutputBuffers>("test");
+ sp<AMessage> format{new AMessage};
+ format->setInt32("width", kWidth);
+ format->setInt32("height", kHeight);
+ buffers->setFormat(format);
+
+ std::shared_ptr<C2BlockPool> pool;
+ ASSERT_EQ(OK, GetCodec2BlockPool(C2BlockPool::BASIC_GRAPHIC, nullptr, &pool));
+
+ // Register 4 buffers
+ std::vector<sp<MediaCodecBuffer>> clientBuffers;
+ auto registerBuffer = [&buffers, &clientBuffers, &pool] {
+ std::shared_ptr<C2GraphicBlock> block;
+ ASSERT_EQ(OK, pool->fetchGraphicBlock(
+ kWidth, kHeight, HAL_PIXEL_FORMAT_YCbCr_420_888,
+ C2MemoryUsage{C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}, &block));
+ std::shared_ptr<C2Buffer> c2Buffer = C2Buffer::CreateGraphicBuffer(block->share(
+ block->crop(), C2Fence{}));
+ size_t index;
+ sp<MediaCodecBuffer> clientBuffer;
+ ASSERT_EQ(OK, buffers->registerBuffer(c2Buffer, &index, &clientBuffer));
+ ASSERT_NE(nullptr, clientBuffer);
+ while (clientBuffers.size() <= index) {
+ clientBuffers.emplace_back();
+ }
+ ASSERT_EQ(nullptr, clientBuffers[index]) << "index = " << index;
+ clientBuffers[index] = clientBuffer;
+ };
+ for (int i = 0; i < 4; ++i) {
+ registerBuffer();
+ }
+
+ // Release 2 buffers
+ auto releaseBuffer = [&buffers, &clientBuffers, kWidth, kHeight](int index) {
+ std::shared_ptr<C2Buffer> c2Buffer;
+ ASSERT_TRUE(buffers->releaseBuffer(clientBuffers[index], &c2Buffer))
+ << "index = " << index;
+ clientBuffers[index] = nullptr;
+ // Sanity checks
+ ASSERT_TRUE(c2Buffer->data().linearBlocks().empty());
+ ASSERT_EQ(1u, c2Buffer->data().graphicBlocks().size());
+ C2ConstGraphicBlock block = c2Buffer->data().graphicBlocks().front();
+ ASSERT_EQ(kWidth, block.width());
+ ASSERT_EQ(kHeight, block.height());
+ };
+ for (int i = 0, index = 0; i < 2 && index < clientBuffers.size(); ++index) {
+ if (clientBuffers[index] == nullptr) {
+ continue;
+ }
+ releaseBuffer(index);
+ ++i;
+ }
+
+ // Simulate # of slots 4->16
+ for (int i = 2; i < 16; ++i) {
+ registerBuffer();
+ }
+
+ // Release everything
+ for (int index = 0; index < clientBuffers.size(); ++index) {
+ if (clientBuffers[index] == nullptr) {
+ continue;
+ }
+ releaseBuffer(index);
+ }
+}
+
+} // namespace android
diff --git a/media/codec2/sfplugin/tests/CCodecConfig_test.cpp b/media/codec2/sfplugin/tests/CCodecConfig_test.cpp
index 7b445a0..c9caa01 100644
--- a/media/codec2/sfplugin/tests/CCodecConfig_test.cpp
+++ b/media/codec2/sfplugin/tests/CCodecConfig_test.cpp
@@ -24,6 +24,8 @@
#include <codec2/hidl/client.h>
#include <util/C2InterfaceHelper.h>
+#include <media/stagefright/MediaCodecConstants.h>
+
namespace {
enum ExtendedC2ParamIndexKind : C2Param::type_index_t {
@@ -56,9 +58,15 @@
CCodecConfigTest()
: mReflector{std::make_shared<C2ReflectorHelper>()} {
+ }
+
+ void init(
+ C2Component::domain_t domain,
+ C2Component::kind_t kind,
+ const char *mediaType) {
sp<hardware::media::c2::V1_0::utils::CachedConfigurable> cachedConfigurable =
new hardware::media::c2::V1_0::utils::CachedConfigurable(
- std::make_unique<Configurable>(mReflector));
+ std::make_unique<Configurable>(mReflector, domain, kind, mediaType));
cachedConfigurable->init(std::make_shared<Cache>());
mConfigurable = std::make_shared<Codec2Client::Configurable>(cachedConfigurable);
}
@@ -71,9 +79,13 @@
class Configurable : public hardware::media::c2::V1_0::utils::ConfigurableC2Intf {
public:
- explicit Configurable(const std::shared_ptr<C2ReflectorHelper> &reflector)
+ Configurable(
+ const std::shared_ptr<C2ReflectorHelper> &reflector,
+ C2Component::domain_t domain,
+ C2Component::kind_t kind,
+ const char *mediaType)
: ConfigurableC2Intf("name", 0u),
- mImpl(reflector) {
+ mImpl(reflector, domain, kind, mediaType) {
}
c2_status_t query(
@@ -104,11 +116,68 @@
private:
class Impl : public C2InterfaceHelper {
public:
- explicit Impl(const std::shared_ptr<C2ReflectorHelper> &reflector)
+ Impl(const std::shared_ptr<C2ReflectorHelper> &reflector,
+ C2Component::domain_t domain,
+ C2Component::kind_t kind,
+ const char *mediaType)
: C2InterfaceHelper{reflector} {
+
setDerivedInstance(this);
addParameter(
+ DefineParam(mDomain, C2_PARAMKEY_COMPONENT_DOMAIN)
+ .withConstValue(new C2ComponentDomainSetting(domain))
+ .build());
+
+ addParameter(
+ DefineParam(mKind, C2_PARAMKEY_COMPONENT_KIND)
+ .withConstValue(new C2ComponentKindSetting(kind))
+ .build());
+
+ addParameter(
+ DefineParam(mInputStreamCount, C2_PARAMKEY_INPUT_STREAM_COUNT)
+ .withConstValue(new C2PortStreamCountTuning::input(1))
+ .build());
+
+ addParameter(
+ DefineParam(mOutputStreamCount, C2_PARAMKEY_OUTPUT_STREAM_COUNT)
+ .withConstValue(new C2PortStreamCountTuning::output(1))
+ .build());
+
+ const char *rawMediaType = "";
+ switch (domain) {
+ case C2Component::DOMAIN_IMAGE: [[fallthrough]];
+ case C2Component::DOMAIN_VIDEO:
+ rawMediaType = MIMETYPE_VIDEO_RAW;
+ break;
+ case C2Component::DOMAIN_AUDIO:
+ rawMediaType = MIMETYPE_AUDIO_RAW;
+ break;
+ default:
+ break;
+ }
+ bool isEncoder = kind == C2Component::KIND_ENCODER;
+ std::string inputMediaType{isEncoder ? rawMediaType : mediaType};
+ std::string outputMediaType{isEncoder ? mediaType : rawMediaType};
+
+ auto allocSharedString = [](const auto ¶m, const std::string &str) {
+ typedef typename std::remove_reference<decltype(param)>::type::element_type T;
+ std::shared_ptr<T> ret = T::AllocShared(str.length() + 1);
+ strcpy(ret->m.value, str.c_str());
+ return ret;
+ };
+
+ addParameter(
+ DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE)
+ .withConstValue(allocSharedString(mInputMediaType, inputMediaType))
+ .build());
+
+ addParameter(
+ DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE)
+ .withConstValue(allocSharedString(mOutputMediaType, outputMediaType))
+ .build());
+
+ addParameter(
DefineParam(mInt32Input, C2_PARAMKEY_VENDOR_INT32)
.withDefault(new C2PortVendorInt32Info::input(0))
.withFields({C2F(mInt32Input, value).any()})
@@ -129,12 +198,29 @@
.withSetter(Setter<decltype(mStringInput)::element_type>)
.build());
- // TODO: SDK params
+ addParameter(
+ DefineParam(mPixelAspectRatio, C2_PARAMKEY_PIXEL_ASPECT_RATIO)
+ .withDefault(new C2StreamPixelAspectRatioInfo::output(0u, 1, 1))
+ .withFields({
+ C2F(mPixelAspectRatio, width).any(),
+ C2F(mPixelAspectRatio, height).any(),
+ })
+ .withSetter(Setter<C2StreamPixelAspectRatioInfo::output>)
+ .build());
+
+ // TODO: more SDK params
}
private:
+ std::shared_ptr<C2ComponentDomainSetting> mDomain;
+ std::shared_ptr<C2ComponentKindSetting> mKind;
+ std::shared_ptr<C2PortStreamCountTuning::input> mInputStreamCount;
+ std::shared_ptr<C2PortStreamCountTuning::output> mOutputStreamCount;
+ std::shared_ptr<C2PortMediaTypeSetting::input> mInputMediaType;
+ std::shared_ptr<C2PortMediaTypeSetting::output> mOutputMediaType;
std::shared_ptr<C2PortVendorInt32Info::input> mInt32Input;
std::shared_ptr<C2StreamVendorInt64Info::output> mInt64Output;
std::shared_ptr<C2PortVendorStringInfo::input> mStringInput;
+ std::shared_ptr<C2StreamPixelAspectRatioInfo::output> mPixelAspectRatio;
template<typename T>
static C2R Setter(bool, C2P<T> &) {
@@ -163,6 +249,10 @@
}
TEST_F(CCodecConfigTest, SetVendorParam) {
+ // Test at audio domain, as video domain has a few local parameters that
+ // interfere with the testing.
+ init(C2Component::DOMAIN_AUDIO, C2Component::KIND_DECODER, MIMETYPE_AUDIO_AAC);
+
ASSERT_EQ(OK, mConfig.initialize(mReflector, mConfigurable));
sp<AMessage> format{new AMessage};
@@ -172,7 +262,7 @@
std::vector<std::unique_ptr<C2Param>> configUpdate;
ASSERT_EQ(OK, mConfig.getConfigUpdateFromSdkParams(
- mConfigurable, format, D::IS_INPUT | D::IS_OUTPUT, C2_MAY_BLOCK, &configUpdate));
+ mConfigurable, format, D::ALL, C2_MAY_BLOCK, &configUpdate));
ASSERT_EQ(3u, configUpdate.size());
C2PortVendorInt32Info::input *i32 =
@@ -192,6 +282,10 @@
}
TEST_F(CCodecConfigTest, VendorParamUpdate_Unsubscribed) {
+ // Test at audio domain, as video domain has a few local parameters that
+ // interfere with the testing.
+ init(C2Component::DOMAIN_AUDIO, C2Component::KIND_DECODER, MIMETYPE_AUDIO_AAC);
+
ASSERT_EQ(OK, mConfig.initialize(mReflector, mConfigurable));
std::vector<std::unique_ptr<C2Param>> configUpdate;
@@ -204,7 +298,7 @@
configUpdate.push_back(std::move(str));
// The vendor parameters are not yet subscribed
- ASSERT_FALSE(mConfig.updateConfiguration(configUpdate, D::IS_INPUT | D::IS_OUTPUT));
+ ASSERT_FALSE(mConfig.updateConfiguration(configUpdate, D::ALL));
int32_t vendorInt32{0};
ASSERT_FALSE(mConfig.mInputFormat->findInt32(KEY_VENDOR_INT32, &vendorInt32))
@@ -226,6 +320,10 @@
}
TEST_F(CCodecConfigTest, VendorParamUpdate_AllSubscribed) {
+ // Test at audio domain, as video domain has a few local parameters that
+ // interfere with the testing.
+ init(C2Component::DOMAIN_AUDIO, C2Component::KIND_DECODER, MIMETYPE_AUDIO_AAC);
+
ASSERT_EQ(OK, mConfig.initialize(mReflector, mConfigurable));
// Force subscribe to all vendor params
@@ -240,7 +338,7 @@
configUpdate.push_back(C2Param::Copy(i64));
configUpdate.push_back(std::move(str));
- ASSERT_TRUE(mConfig.updateConfiguration(configUpdate, D::IS_INPUT | D::IS_OUTPUT));
+ ASSERT_TRUE(mConfig.updateConfiguration(configUpdate, D::ALL));
int32_t vendorInt32{0};
ASSERT_TRUE(mConfig.mInputFormat->findInt32(KEY_VENDOR_INT32, &vendorInt32))
@@ -265,6 +363,10 @@
}
TEST_F(CCodecConfigTest, VendorParamUpdate_PartiallySubscribed) {
+ // Test at audio domain, as video domain has a few local parameters that
+ // interfere with the testing.
+ init(C2Component::DOMAIN_AUDIO, C2Component::KIND_DECODER, MIMETYPE_AUDIO_AAC);
+
ASSERT_EQ(OK, mConfig.initialize(mReflector, mConfigurable));
// Subscribe to example.int32 only
@@ -273,7 +375,7 @@
format->setInt32(KEY_VENDOR_INT32, 0);
configUpdate.clear();
ASSERT_EQ(OK, mConfig.getConfigUpdateFromSdkParams(
- mConfigurable, format, D::IS_INPUT | D::IS_OUTPUT, C2_MAY_BLOCK, &configUpdate));
+ mConfigurable, format, D::ALL, C2_MAY_BLOCK, &configUpdate));
ASSERT_EQ(OK, mConfig.setParameters(mConfigurable, configUpdate, C2_MAY_BLOCK));
C2PortVendorInt32Info::input i32(kCodec2Int32);
@@ -286,7 +388,7 @@
configUpdate.push_back(std::move(str));
// Only example.i32 should be updated
- ASSERT_TRUE(mConfig.updateConfiguration(configUpdate, D::IS_INPUT | D::IS_OUTPUT));
+ ASSERT_TRUE(mConfig.updateConfiguration(configUpdate, D::ALL));
int32_t vendorInt32{0};
ASSERT_TRUE(mConfig.mInputFormat->findInt32(KEY_VENDOR_INT32, &vendorInt32))
@@ -308,4 +410,51 @@
<< "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
}
+TEST_F(CCodecConfigTest, SetPixelAspectRatio) {
+ init(C2Component::DOMAIN_VIDEO, C2Component::KIND_DECODER, MIMETYPE_VIDEO_AVC);
+
+ ASSERT_EQ(OK, mConfig.initialize(mReflector, mConfigurable));
+
+ sp<AMessage> format{new AMessage};
+ format->setInt32(KEY_PIXEL_ASPECT_RATIO_WIDTH, 12);
+ format->setInt32(KEY_PIXEL_ASPECT_RATIO_HEIGHT, 11);
+
+ std::vector<std::unique_ptr<C2Param>> configUpdate;
+ ASSERT_EQ(OK, mConfig.getConfigUpdateFromSdkParams(
+ mConfigurable, format, D::ALL, C2_MAY_BLOCK, &configUpdate));
+
+ ASSERT_EQ(1u, configUpdate.size());
+ C2StreamPixelAspectRatioInfo::output *par =
+ FindParam<std::remove_pointer<decltype(par)>::type>(configUpdate);
+ ASSERT_NE(nullptr, par);
+ ASSERT_EQ(12, par->width);
+ ASSERT_EQ(11, par->height);
+}
+
+TEST_F(CCodecConfigTest, PixelAspectRatioUpdate) {
+ init(C2Component::DOMAIN_VIDEO, C2Component::KIND_DECODER, MIMETYPE_VIDEO_AVC);
+
+ ASSERT_EQ(OK, mConfig.initialize(mReflector, mConfigurable));
+
+ std::vector<std::unique_ptr<C2Param>> configUpdate;
+ C2StreamPixelAspectRatioInfo::output par(0u, 12, 11);
+ configUpdate.push_back(C2Param::Copy(par));
+
+ ASSERT_TRUE(mConfig.updateConfiguration(configUpdate, D::ALL));
+
+ int32_t parWidth{0};
+ ASSERT_TRUE(mConfig.mOutputFormat->findInt32(KEY_PIXEL_ASPECT_RATIO_WIDTH, &parWidth))
+ << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
+ ASSERT_EQ(12, parWidth);
+ ASSERT_FALSE(mConfig.mInputFormat->findInt32(KEY_PIXEL_ASPECT_RATIO_WIDTH, &parWidth))
+ << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str();
+
+ int32_t parHeight{0};
+ ASSERT_TRUE(mConfig.mOutputFormat->findInt32(KEY_PIXEL_ASPECT_RATIO_HEIGHT, &parHeight))
+ << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
+ ASSERT_EQ(11, parHeight);
+ ASSERT_FALSE(mConfig.mInputFormat->findInt32(KEY_PIXEL_ASPECT_RATIO_HEIGHT, &parHeight))
+ << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str();
+}
+
} // namespace android
diff --git a/media/codec2/vndk/util/C2InterfaceUtils.cpp b/media/codec2/vndk/util/C2InterfaceUtils.cpp
index 61ec911..0c1729b 100644
--- a/media/codec2/vndk/util/C2InterfaceUtils.cpp
+++ b/media/codec2/vndk/util/C2InterfaceUtils.cpp
@@ -216,9 +216,14 @@
if (limit.contains(minMask) && contains(minMask)) {
values[0] = minMask;
// keep only flags that are covered by limit
- std::remove_if(values.begin(), values.end(), [&limit, minMask](const C2Value::Primitive &v) -> bool {
- T value = v.ref<ValueType>() | minMask;
- return value == minMask || !limit.contains(value); });
+ values.erase(std::remove_if(values.begin(), values.end(),
+ [&limit, minMask](
+ const C2Value::Primitive &v) -> bool {
+ T value = v.ref<ValueType>() | minMask;
+ return value == minMask ||
+ !limit.contains(value);
+ }),
+ values.end());
// we also need to do it vice versa
for (const C2Value::Primitive &v : _mValues) {
T value = v.ref<ValueType>() | minMask;
@@ -264,24 +269,33 @@
template<typename T>
C2SupportedValueSet<T> C2SupportedValueSet<T>::limitedTo(const C2SupportedValueSet<T> &limit) const {
std::vector<C2Value::Primitive> values = _mValues; // make a copy
- std::remove_if(values.begin(), values.end(), [&limit](const C2Value::Primitive &v) -> bool {
- return !limit.contains(v.ref<ValueType>()); });
+ values.erase(std::remove_if(values.begin(), values.end(),
+ [&limit](const C2Value::Primitive &v) -> bool {
+ return !limit.contains(v.ref<ValueType>());
+ }),
+ values.end());
return C2SupportedValueSet(std::move(values));
}
template<typename T>
C2SupportedValueSet<T> C2SupportedValueSet<T>::limitedTo(const C2SupportedRange<T> &limit) const {
std::vector<C2Value::Primitive> values = _mValues; // make a copy
- std::remove_if(values.begin(), values.end(), [&limit](const C2Value::Primitive &v) -> bool {
- return !limit.contains(v.ref<ValueType>()); });
+ values.erase(std::remove_if(values.begin(), values.end(),
+ [&limit](const C2Value::Primitive &v) -> bool {
+ return !limit.contains(v.ref<ValueType>());
+ }),
+ values.end());
return C2SupportedValueSet(std::move(values));
}
template<typename T>
C2SupportedValueSet<T> C2SupportedValueSet<T>::limitedTo(const C2SupportedFlags<T> &limit) const {
std::vector<C2Value::Primitive> values = _mValues; // make a copy
- std::remove_if(values.begin(), values.end(), [&limit](const C2Value::Primitive &v) -> bool {
- return !limit.contains(v.ref<ValueType>()); });
+ values.erase(std::remove_if(values.begin(), values.end(),
+ [&limit](const C2Value::Primitive &v) -> bool {
+ return !limit.contains(v.ref<ValueType>());
+ }),
+ values.end());
return C2SupportedValueSet(std::move(values));
}
diff --git a/media/extractors/flac/Android.bp b/media/extractors/flac/Android.bp
index 3675611..826c1a0 100644
--- a/media/extractors/flac/Android.bp
+++ b/media/extractors/flac/Android.bp
@@ -9,6 +9,7 @@
],
shared_libs: [
+ "libbase",
"libbinder_ndk",
],
diff --git a/media/extractors/flac/FLACExtractor.cpp b/media/extractors/flac/FLACExtractor.cpp
index 5329bd1..0617e88 100644
--- a/media/extractors/flac/FLACExtractor.cpp
+++ b/media/extractors/flac/FLACExtractor.cpp
@@ -24,6 +24,7 @@
// libFLAC parser
#include "FLAC/stream_decoder.h"
+#include <android-base/properties.h>
#include <android/binder_ibinder.h> // for AIBinder_getCallingUid
#include <audio_utils/primitives.h>
#include <media/MediaExtractorPluginApi.h>
@@ -47,7 +48,8 @@
// (Note: duplicated with WAVExtractor.cpp)
static inline bool shouldExtractorOutputFloat(int bitsPerSample)
{
- return bitsPerSample > 16 && AIBinder_getCallingUid() == AID_MEDIA;
+ return bitsPerSample > 16 && AIBinder_getCallingUid() == AID_MEDIA
+ && android::base::GetBoolProperty("media.extractor.float", true);
}
class FLACParser;
diff --git a/media/extractors/midi/Android.bp b/media/extractors/midi/Android.bp
index bdb724b..b8255fc 100644
--- a/media/extractors/midi/Android.bp
+++ b/media/extractors/midi/Android.bp
@@ -10,7 +10,7 @@
static_libs: [
"libmedia_midiiowrapper",
- "libsonivox",
+ "libsonivoxwithoutjet",
"libstagefright_foundation",
"libwatchdog",
],
diff --git a/media/extractors/tests/Android.bp b/media/extractors/tests/Android.bp
index fa39b64..b3afe2f 100644
--- a/media/extractors/tests/Android.bp
+++ b/media/extractors/tests/Android.bp
@@ -45,7 +45,7 @@
"libstagefright_metadatautils",
"libmedia_midiiowrapper",
- "libsonivox",
+ "libsonivoxwithoutjet",
"libvorbisidec",
"libwebm",
"libFLAC",
diff --git a/media/extractors/wav/Android.bp b/media/extractors/wav/Android.bp
index 8ce5c3f..5d38a81 100644
--- a/media/extractors/wav/Android.bp
+++ b/media/extractors/wav/Android.bp
@@ -10,6 +10,7 @@
],
shared_libs: [
+ "libbase",
"libbinder_ndk",
],
diff --git a/media/extractors/wav/WAVExtractor.cpp b/media/extractors/wav/WAVExtractor.cpp
index 4fa7f27..d19447a 100644
--- a/media/extractors/wav/WAVExtractor.cpp
+++ b/media/extractors/wav/WAVExtractor.cpp
@@ -20,6 +20,7 @@
#include "WAVExtractor.h"
+#include <android-base/properties.h>
#include <android/binder_ibinder.h> // for AIBinder_getCallingUid
#include <audio_utils/primitives.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -43,7 +44,8 @@
// (Note: duplicated with FLACExtractor.cpp)
static inline bool shouldExtractorOutputFloat(int bitsPerSample)
{
- return bitsPerSample > 16 && AIBinder_getCallingUid() == AID_MEDIA;
+ return bitsPerSample > 16 && AIBinder_getCallingUid() == AID_MEDIA
+ && android::base::GetBoolProperty("media.extractor.float", true);
}
enum {
diff --git a/media/libaaudio/include/aaudio/AAudio.h b/media/libaaudio/include/aaudio/AAudio.h
index edc09a9..a47f189 100644
--- a/media/libaaudio/include/aaudio/AAudio.h
+++ b/media/libaaudio/include/aaudio/AAudio.h
@@ -406,6 +406,7 @@
* Use this preset for capturing audio meant to be processed in real time
* and played back for live performance (e.g karaoke).
* The capture path will minimize latency and coupling with playback path.
+ * Available since API level 29.
*/
AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE = 10,
};
diff --git a/media/libaaudio/src/Android.bp b/media/libaaudio/src/Android.bp
index 901c28f..717f31a 100644
--- a/media/libaaudio/src/Android.bp
+++ b/media/libaaudio/src/Android.bp
@@ -35,6 +35,9 @@
"libaaudio_internal",
"libaudioclient",
"libaudioutils",
+ "libmedia_helper",
+ "libmediametrics",
+ "libmediautils",
"liblog",
"libcutils",
"libutils",
@@ -75,6 +78,9 @@
shared_libs: [
"libaudioclient",
"libaudioutils",
+ "libmedia_helper",
+ "libmediametrics",
+ "libmediautils",
"liblog",
"libcutils",
"libutils",
diff --git a/media/libaaudio/src/client/AudioEndpoint.cpp b/media/libaaudio/src/client/AudioEndpoint.cpp
index 214f888..06f66d3 100644
--- a/media/libaaudio/src/client/AudioEndpoint.cpp
+++ b/media/libaaudio/src/client/AudioEndpoint.cpp
@@ -32,19 +32,12 @@
#define RIDICULOUSLY_LARGE_FRAME_SIZE 4096
AudioEndpoint::AudioEndpoint()
- : mUpCommandQueue(nullptr)
- , mDataQueue(nullptr)
- , mFreeRunning(false)
+ : mFreeRunning(false)
, mDataReadCounter(0)
, mDataWriteCounter(0)
{
}
-AudioEndpoint::~AudioEndpoint() {
- delete mDataQueue;
- delete mUpCommandQueue;
-}
-
// TODO Consider moving to a method in RingBufferDescriptor
static aaudio_result_t AudioEndpoint_validateQueueDescriptor(const char *type,
const RingBufferDescriptor *descriptor) {
@@ -144,7 +137,7 @@
return AAUDIO_ERROR_INTERNAL;
}
- mUpCommandQueue = new FifoBuffer(
+ mUpCommandQueue = std::make_unique<FifoBuffer>(
descriptor->bytesPerFrame,
descriptor->capacityInFrames,
descriptor->readCounterAddress,
@@ -173,7 +166,7 @@
? &mDataWriteCounter
: descriptor->writeCounterAddress;
- mDataQueue = new FifoBuffer(
+ mDataQueue = std::make_unique<FifoBuffer>(
descriptor->bytesPerFrame,
descriptor->capacityInFrames,
readCounterAddress,
@@ -194,18 +187,15 @@
return mDataQueue->getEmptyRoomAvailable(wrappingBuffer);
}
-int32_t AudioEndpoint::getEmptyFramesAvailable()
-{
+int32_t AudioEndpoint::getEmptyFramesAvailable() {
return mDataQueue->getEmptyFramesAvailable();
}
-int32_t AudioEndpoint::getFullFramesAvailable(WrappingBuffer *wrappingBuffer)
-{
+int32_t AudioEndpoint::getFullFramesAvailable(WrappingBuffer *wrappingBuffer) {
return mDataQueue->getFullDataAvailable(wrappingBuffer);
}
-int32_t AudioEndpoint::getFullFramesAvailable()
-{
+int32_t AudioEndpoint::getFullFramesAvailable() {
return mDataQueue->getFullFramesAvailable();
}
@@ -217,29 +207,24 @@
mDataQueue->advanceReadIndex(deltaFrames);
}
-void AudioEndpoint::setDataReadCounter(fifo_counter_t framesRead)
-{
+void AudioEndpoint::setDataReadCounter(fifo_counter_t framesRead) {
mDataQueue->setReadCounter(framesRead);
}
-fifo_counter_t AudioEndpoint::getDataReadCounter()
-{
+fifo_counter_t AudioEndpoint::getDataReadCounter() const {
return mDataQueue->getReadCounter();
}
-void AudioEndpoint::setDataWriteCounter(fifo_counter_t framesRead)
-{
+void AudioEndpoint::setDataWriteCounter(fifo_counter_t framesRead) {
mDataQueue->setWriteCounter(framesRead);
}
-fifo_counter_t AudioEndpoint::getDataWriteCounter()
-{
+fifo_counter_t AudioEndpoint::getDataWriteCounter() const {
return mDataQueue->getWriteCounter();
}
int32_t AudioEndpoint::setBufferSizeInFrames(int32_t requestedFrames,
- int32_t *actualFrames)
-{
+ int32_t *actualFrames) {
if (requestedFrames < ENDPOINT_DATA_QUEUE_SIZE_MIN) {
requestedFrames = ENDPOINT_DATA_QUEUE_SIZE_MIN;
}
@@ -248,19 +233,17 @@
return AAUDIO_OK;
}
-int32_t AudioEndpoint::getBufferSizeInFrames() const
-{
+int32_t AudioEndpoint::getBufferSizeInFrames() const {
return mDataQueue->getThreshold();
}
-int32_t AudioEndpoint::getBufferCapacityInFrames() const
-{
+int32_t AudioEndpoint::getBufferCapacityInFrames() const {
return (int32_t)mDataQueue->getBufferCapacityInFrames();
}
void AudioEndpoint::dump() const {
- ALOGD("data readCounter = %lld", (long long) mDataQueue->getReadCounter());
- ALOGD("data writeCounter = %lld", (long long) mDataQueue->getWriteCounter());
+ ALOGD("data readCounter = %lld", (long long) getDataReadCounter());
+ ALOGD("data writeCounter = %lld", (long long) getDataWriteCounter());
}
void AudioEndpoint::eraseDataMemory() {
diff --git a/media/libaaudio/src/client/AudioEndpoint.h b/media/libaaudio/src/client/AudioEndpoint.h
index f5b67e8..484d917 100644
--- a/media/libaaudio/src/client/AudioEndpoint.h
+++ b/media/libaaudio/src/client/AudioEndpoint.h
@@ -35,7 +35,6 @@
public:
AudioEndpoint();
- virtual ~AudioEndpoint();
/**
* Configure based on the EndPointDescriptor_t.
@@ -67,11 +66,11 @@
*/
void setDataReadCounter(android::fifo_counter_t framesRead);
- android::fifo_counter_t getDataReadCounter();
+ android::fifo_counter_t getDataReadCounter() const;
void setDataWriteCounter(android::fifo_counter_t framesWritten);
- android::fifo_counter_t getDataWriteCounter();
+ android::fifo_counter_t getDataWriteCounter() const;
/**
* The result is not valid until after configure() is called.
@@ -94,8 +93,8 @@
void dump() const;
private:
- android::FifoBuffer *mUpCommandQueue;
- android::FifoBuffer *mDataQueue;
+ std::unique_ptr<android::FifoBuffer> mUpCommandQueue;
+ std::unique_ptr<android::FifoBuffer> mDataQueue;
bool mFreeRunning;
android::fifo_counter_t mDataReadCounter; // only used if free-running
android::fifo_counter_t mDataWriteCounter; // only used if free-running
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 6723ec9..3db0099 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -14,9 +14,7 @@
* limitations under the License.
*/
-// This file is used in both client and server processes.
-// This is needed to make sense of the logs more easily.
-#define LOG_TAG (mInService ? "AudioStreamInternal_Service" : "AudioStreamInternal_Client")
+#define LOG_TAG "AudioStreamInternal"
//#define LOG_NDEBUG 0
#include <utils/Log.h>
@@ -28,6 +26,8 @@
#include <aaudio/AAudio.h>
#include <cutils/properties.h>
+
+#include <media/MediaMetricsItem.h>
#include <utils/String16.h>
#include <utils/Trace.h>
@@ -36,12 +36,20 @@
#include "binding/AAudioStreamConfiguration.h"
#include "binding/IAAudioService.h"
#include "binding/AAudioServiceMessage.h"
+#include "core/AudioGlobal.h"
#include "core/AudioStreamBuilder.h"
#include "fifo/FifoBuffer.h"
#include "utility/AudioClock.h"
#include "AudioStreamInternal.h"
+// We do this after the #includes because if a header uses ALOG.
+// it would fail on the reference to mInService.
+#undef LOG_TAG
+// This file is used in both client and server processes.
+// This is needed to make sense of the logs more easily.
+#define LOG_TAG (mInService ? "AudioStreamInternal_Service" : "AudioStreamInternal_Client")
+
using android::String16;
using android::Mutex;
using android::WrappingBuffer;
@@ -58,7 +66,6 @@
AudioStreamInternal::AudioStreamInternal(AAudioServiceInterface &serviceInterface, bool inService)
: AudioStream()
, mClockModel()
- , mAudioEndpoint()
, mServiceStreamHandle(AAUDIO_HANDLE_INVALID)
, mInService(inService)
, mServiceInterface(serviceInterface)
@@ -74,7 +81,6 @@
aaudio_result_t AudioStreamInternal::open(const AudioStreamBuilder &builder) {
aaudio_result_t result = AAUDIO_OK;
- int32_t capacity;
int32_t framesPerBurst;
int32_t framesPerHardwareBurst;
AAudioStreamRequest request;
@@ -139,6 +145,11 @@
return mServiceStreamHandle;
}
+ // This must match the key generated in oboeservice/AAudioServiceStreamBase.cpp
+ // so the client can have permission to log.
+ mMetricsId = std::string(AMEDIAMETRICS_KEY_PREFIX_AUDIO_STREAM)
+ + std::to_string(mServiceStreamHandle);
+
result = configurationOutput.validate();
if (result != AAUDIO_OK) {
goto error;
@@ -173,7 +184,8 @@
}
// Configure endpoint based on descriptor.
- result = mAudioEndpoint.configure(&mEndpointDescriptor, getDirection());
+ mAudioEndpoint = std::make_unique<AudioEndpoint>();
+ result = mAudioEndpoint->configure(&mEndpointDescriptor, getDirection());
if (result != AAUDIO_OK) {
goto error;
}
@@ -201,9 +213,10 @@
}
mFramesPerBurst = framesPerBurst; // only save good value
- capacity = mEndpointDescriptor.dataQueueDescriptor.capacityInFrames;
- if (capacity < mFramesPerBurst || capacity > MAX_BUFFER_CAPACITY_IN_FRAMES) {
- ALOGE("%s - bufferCapacity out of range = %d", __func__, capacity);
+ mBufferCapacityInFrames = mEndpointDescriptor.dataQueueDescriptor.capacityInFrames;
+ if (mBufferCapacityInFrames < mFramesPerBurst
+ || mBufferCapacityInFrames > MAX_BUFFER_CAPACITY_IN_FRAMES) {
+ ALOGE("%s - bufferCapacity out of range = %d", __func__, mBufferCapacityInFrames);
result = AAUDIO_ERROR_OUT_OF_RANGE;
goto error;
}
@@ -230,7 +243,7 @@
}
const int32_t callbackBufferSize = mCallbackFrames * getBytesPerFrame();
- mCallbackBuffer = new uint8_t[callbackBufferSize];
+ mCallbackBuffer = std::make_unique<uint8_t[]>(callbackBufferSize);
}
// For debugging and analyzing the distribution of MMAP timestamps.
@@ -239,7 +252,7 @@
// You can use this offset to reduce glitching.
// You can also use this offset to force glitching. By iterating over multiple
// values you can reveal the distribution of the hardware timing jitter.
- if (mAudioEndpoint.isFreeRunning()) { // MMAP?
+ if (mAudioEndpoint->isFreeRunning()) { // MMAP?
int32_t offsetMicros = (getDirection() == AAUDIO_DIRECTION_OUTPUT)
? AAudioProperty_getOutputMMapOffsetMicros()
: AAudioProperty_getInputMMapOffsetMicros();
@@ -251,7 +264,7 @@
mTimeOffsetNanos = offsetMicros * AAUDIO_NANOS_PER_MICROSECOND;
}
- setBufferSize(capacity / 2); // Default buffer size to match Q
+ setBufferSize(mBufferCapacityInFrames / 2); // Default buffer size to match Q
setState(AAUDIO_STREAM_STATE_OPEN);
@@ -274,13 +287,20 @@
if (isActive() || currentState == AAUDIO_STREAM_STATE_DISCONNECTED) {
requestStop();
}
+
+ logBufferState();
+
setState(AAUDIO_STREAM_STATE_CLOSING);
aaudio_handle_t serviceStreamHandle = mServiceStreamHandle;
mServiceStreamHandle = AAUDIO_HANDLE_INVALID;
mServiceInterface.closeStream(serviceStreamHandle);
- delete[] mCallbackBuffer;
- mCallbackBuffer = nullptr;
+ mCallbackBuffer.reset();
+
+ // Update local frame counters so we can query them after releasing the endpoint.
+ getFramesRead();
+ getFramesWritten();
+ mAudioEndpoint.reset();
result = mEndPointParcelable.close();
aaudio_result_t result2 = AudioStream::release_l();
return (result != AAUDIO_OK) ? result : result2;
@@ -539,7 +559,7 @@
case AAUDIO_SERVICE_EVENT_DISCONNECTED:
// Prevent hardware from looping on old data and making buzzing sounds.
if (getDirection() == AAUDIO_DIRECTION_OUTPUT) {
- mAudioEndpoint.eraseDataMemory();
+ mAudioEndpoint->eraseDataMemory();
}
result = AAUDIO_ERROR_DISCONNECTED;
setState(AAUDIO_STREAM_STATE_DISCONNECTED);
@@ -565,7 +585,10 @@
while (result == AAUDIO_OK) {
AAudioServiceMessage message;
- if (mAudioEndpoint.readUpCommand(&message) != 1) {
+ if (!mAudioEndpoint) {
+ break;
+ }
+ if (mAudioEndpoint->readUpCommand(&message) != 1) {
break; // no command this time, no problem
}
switch (message.what) {
@@ -593,7 +616,10 @@
while (result == AAUDIO_OK) {
AAudioServiceMessage message;
- if (mAudioEndpoint.readUpCommand(&message) != 1) {
+ if (!mAudioEndpoint) {
+ break;
+ }
+ if (mAudioEndpoint->readUpCommand(&message) != 1) {
break; // no command this time, no problem
}
switch (message.what) {
@@ -626,7 +652,7 @@
const char * fifoName = "aaRdy";
ATRACE_BEGIN(traceName);
if (ATRACE_ENABLED()) {
- int32_t fullFrames = mAudioEndpoint.getFullFramesAvailable();
+ int32_t fullFrames = mAudioEndpoint->getFullFramesAvailable();
ATRACE_INT(fifoName, fullFrames);
}
@@ -655,7 +681,7 @@
if (timeoutNanoseconds == 0) {
break; // don't block
} else if (wakeTimeNanos != 0) {
- if (!mAudioEndpoint.isFreeRunning()) {
+ if (!mAudioEndpoint->isFreeRunning()) {
// If there is software on the other end of the FIFO then it may get delayed.
// So wake up just a little after we expect it to be ready.
wakeTimeNanos += mWakeupDelayNanos;
@@ -680,12 +706,12 @@
ALOGW("processData(): past deadline by %d micros",
(int)((wakeTimeNanos - deadlineNanos) / AAUDIO_NANOS_PER_MICROSECOND));
mClockModel.dump();
- mAudioEndpoint.dump();
+ mAudioEndpoint->dump();
break;
}
if (ATRACE_ENABLED()) {
- int32_t fullFrames = mAudioEndpoint.getFullFramesAvailable();
+ int32_t fullFrames = mAudioEndpoint->getFullFramesAvailable();
ATRACE_INT(fifoName, fullFrames);
int64_t sleepForNanos = wakeTimeNanos - currentTimeNanos;
ATRACE_INT("aaSlpNs", (int32_t)sleepForNanos);
@@ -697,7 +723,7 @@
}
if (ATRACE_ENABLED()) {
- int32_t fullFrames = mAudioEndpoint.getFullFramesAvailable();
+ int32_t fullFrames = mAudioEndpoint->getFullFramesAvailable();
ATRACE_INT(fifoName, fullFrames);
}
@@ -731,11 +757,15 @@
adjustedFrames = std::min(maximumSize, adjustedFrames);
}
- // Clip against the actual size from the endpoint.
- int32_t actualFrames = 0;
- mAudioEndpoint.setBufferSizeInFrames(maximumSize, &actualFrames);
- // actualFrames should be <= actual maximum size of endpoint
- adjustedFrames = std::min(actualFrames, adjustedFrames);
+ if (mAudioEndpoint) {
+ // Clip against the actual size from the endpoint.
+ int32_t actualFrames = 0;
+ // Set to maximum size so we can write extra data when ready in order to reduce glitches.
+ // The amount we keep in the buffer is controlled by mBufferSizeInFrames.
+ mAudioEndpoint->setBufferSizeInFrames(maximumSize, &actualFrames);
+ // actualFrames should be <= actual maximum size of endpoint
+ adjustedFrames = std::min(actualFrames, adjustedFrames);
+ }
mBufferSizeInFrames = adjustedFrames;
ALOGV("%s(%d) returns %d", __func__, requestedFrames, adjustedFrames);
@@ -747,7 +777,7 @@
}
int32_t AudioStreamInternal::getBufferCapacity() const {
- return mAudioEndpoint.getBufferCapacityInFrames();
+ return mBufferCapacityInFrames;
}
int32_t AudioStreamInternal::getFramesPerBurst() const {
@@ -760,5 +790,5 @@
}
bool AudioStreamInternal::isClockModelInControl() const {
- return isActive() && mAudioEndpoint.isFreeRunning() && mClockModel.isRunning();
+ return isActive() && mAudioEndpoint->isFreeRunning() && mClockModel.isRunning();
}
diff --git a/media/libaaudio/src/client/AudioStreamInternal.h b/media/libaaudio/src/client/AudioStreamInternal.h
index 095f30c..61591b3 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.h
+++ b/media/libaaudio/src/client/AudioStreamInternal.h
@@ -155,7 +155,8 @@
IsochronousClockModel mClockModel; // timing model for chasing the HAL
- AudioEndpoint mAudioEndpoint; // source for reads or sink for writes
+ std::unique_ptr<AudioEndpoint> mAudioEndpoint; // source for reads or sink for writes
+
aaudio_handle_t mServiceStreamHandle; // opaque handle returned from service
int32_t mFramesPerBurst = MIN_FRAMES_PER_BURST; // frames per HAL transfer
@@ -164,7 +165,7 @@
// Offset from underlying frame position.
int64_t mFramesOffsetFromService = 0; // offset for timestamps
- uint8_t *mCallbackBuffer = nullptr;
+ std::unique_ptr<uint8_t[]> mCallbackBuffer;
int32_t mCallbackFrames = 0;
// The service uses this for SHARED mode.
@@ -178,6 +179,9 @@
float mStreamVolume = 1.0f;
+ int64_t mLastFramesWritten = 0;
+ int64_t mLastFramesRead = 0;
+
private:
/*
* Asynchronous write with data conversion.
@@ -207,6 +211,8 @@
int32_t mDeviceChannelCount = 0;
int32_t mBufferSizeInFrames = 0; // local threshold to control latency
+ int32_t mBufferCapacityInFrames = 0;
+
};
diff --git a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
index 9684ee4..9fa2e40 100644
--- a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
@@ -42,8 +42,8 @@
AudioStreamInternalCapture::~AudioStreamInternalCapture() {}
void AudioStreamInternalCapture::advanceClientToMatchServerPosition() {
- int64_t readCounter = mAudioEndpoint.getDataReadCounter();
- int64_t writeCounter = mAudioEndpoint.getDataWriteCounter();
+ int64_t readCounter = mAudioEndpoint->getDataReadCounter();
+ int64_t writeCounter = mAudioEndpoint->getDataWriteCounter();
// Bump offset so caller does not see the retrograde motion in getFramesRead().
int64_t offset = readCounter - writeCounter;
@@ -53,7 +53,7 @@
// Force readCounter to match writeCounter.
// This is because we cannot change the write counter in the hardware.
- mAudioEndpoint.setDataReadCounter(writeCounter);
+ mAudioEndpoint->setDataReadCounter(writeCounter);
}
// Write the data, block if needed and timeoutMillis > 0
@@ -86,7 +86,7 @@
}
// If we have gotten this far then we have at least one timestamp from server.
- if (mAudioEndpoint.isFreeRunning()) {
+ if (mAudioEndpoint->isFreeRunning()) {
//ALOGD("AudioStreamInternalCapture::processDataNow() - update remote counter");
// Update data queue based on the timing model.
// Jitter in the DSP can cause late writes to the FIFO.
@@ -95,7 +95,7 @@
// that the DSP could have written the data.
int64_t estimatedRemoteCounter = mClockModel.convertLatestTimeToPosition(currentNanoTime);
// TODO refactor, maybe use setRemoteCounter()
- mAudioEndpoint.setDataWriteCounter(estimatedRemoteCounter);
+ mAudioEndpoint->setDataWriteCounter(estimatedRemoteCounter);
}
// This code assumes that we have already received valid timestamps.
@@ -108,8 +108,8 @@
// If the capture buffer is full beyond capacity then consider it an overrun.
// For shared streams, the xRunCount is passed up from the service.
- if (mAudioEndpoint.isFreeRunning()
- && mAudioEndpoint.getFullFramesAvailable() > mAudioEndpoint.getBufferCapacityInFrames()) {
+ if (mAudioEndpoint->isFreeRunning()
+ && mAudioEndpoint->getFullFramesAvailable() > mAudioEndpoint->getBufferCapacityInFrames()) {
mXRunCount++;
if (ATRACE_ENABLED()) {
ATRACE_INT("aaOverRuns", mXRunCount);
@@ -143,7 +143,7 @@
// Calculate frame position based off of the readCounter because
// the writeCounter might have just advanced in the background,
// causing us to sleep until a later burst.
- int64_t nextPosition = mAudioEndpoint.getDataReadCounter() + mFramesPerBurst;
+ int64_t nextPosition = mAudioEndpoint->getDataReadCounter() + mFramesPerBurst;
wakeTime = mClockModel.convertPositionToLatestTime(nextPosition);
}
break;
@@ -166,7 +166,7 @@
uint8_t *destination = (uint8_t *) buffer;
int32_t framesLeft = numFrames;
- mAudioEndpoint.getFullFramesAvailable(&wrappingBuffer);
+ mAudioEndpoint->getFullFramesAvailable(&wrappingBuffer);
// Read data in one or two parts.
for (int partIndex = 0; framesLeft > 0 && partIndex < WrappingBuffer::SIZE; partIndex++) {
@@ -208,26 +208,29 @@
}
int32_t framesProcessed = numFrames - framesLeft;
- mAudioEndpoint.advanceReadIndex(framesProcessed);
+ mAudioEndpoint->advanceReadIndex(framesProcessed);
//ALOGD("readNowWithConversion() returns %d", framesProcessed);
return framesProcessed;
}
int64_t AudioStreamInternalCapture::getFramesWritten() {
- const int64_t framesWrittenHardware = isClockModelInControl()
- ? mClockModel.convertTimeToPosition(AudioClock::getNanoseconds())
- : mAudioEndpoint.getDataWriteCounter();
- // Add service offset and prevent retrograde motion.
- mLastFramesWritten = std::max(mLastFramesWritten,
- framesWrittenHardware + mFramesOffsetFromService);
+ if (mAudioEndpoint) {
+ const int64_t framesWrittenHardware = isClockModelInControl()
+ ? mClockModel.convertTimeToPosition(AudioClock::getNanoseconds())
+ : mAudioEndpoint->getDataWriteCounter();
+ // Add service offset and prevent retrograde motion.
+ mLastFramesWritten = std::max(mLastFramesWritten,
+ framesWrittenHardware + mFramesOffsetFromService);
+ }
return mLastFramesWritten;
}
int64_t AudioStreamInternalCapture::getFramesRead() {
- int64_t frames = mAudioEndpoint.getDataReadCounter() + mFramesOffsetFromService;
- //ALOGD("getFramesRead() returns %lld", (long long)frames);
- return frames;
+ if (mAudioEndpoint) {
+ mLastFramesRead = mAudioEndpoint->getDataReadCounter() + mFramesOffsetFromService;
+ }
+ return mLastFramesRead;
}
// Read data from the stream and pass it to the callback for processing.
@@ -243,7 +246,7 @@
int64_t timeoutNanos = calculateReasonableTimeout(mCallbackFrames);
// This is a BLOCKING READ!
- result = read(mCallbackBuffer, mCallbackFrames, timeoutNanos);
+ result = read(mCallbackBuffer.get(), mCallbackFrames, timeoutNanos);
if ((result != mCallbackFrames)) {
ALOGE("callbackLoop: read() returned %d", result);
if (result >= 0) {
@@ -255,7 +258,7 @@
}
// Call application using the AAudio callback interface.
- callbackResult = maybeCallDataCallback(mCallbackBuffer, mCallbackFrames);
+ callbackResult = maybeCallDataCallback(mCallbackBuffer.get(), mCallbackFrames);
if (callbackResult == AAUDIO_CALLBACK_RESULT_STOP) {
ALOGD("%s(): callback returned AAUDIO_CALLBACK_RESULT_STOP", __func__);
diff --git a/media/libaaudio/src/client/AudioStreamInternalCapture.h b/media/libaaudio/src/client/AudioStreamInternalCapture.h
index 294dbaf..6436a53 100644
--- a/media/libaaudio/src/client/AudioStreamInternalCapture.h
+++ b/media/libaaudio/src/client/AudioStreamInternalCapture.h
@@ -68,8 +68,6 @@
* @return frames written or negative error
*/
aaudio_result_t readNowWithConversion(void *buffer, int32_t numFrames);
-
- int64_t mLastFramesWritten = 0; // used to prevent retrograde motion
};
} /* namespace aaudio */
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
index 536009a..1303daf 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
@@ -87,8 +87,8 @@
}
void AudioStreamInternalPlay::advanceClientToMatchServerPosition() {
- int64_t readCounter = mAudioEndpoint.getDataReadCounter();
- int64_t writeCounter = mAudioEndpoint.getDataWriteCounter();
+ int64_t readCounter = mAudioEndpoint->getDataReadCounter();
+ int64_t writeCounter = mAudioEndpoint->getDataWriteCounter();
// Bump offset so caller does not see the retrograde motion in getFramesRead().
int64_t offset = writeCounter - readCounter;
@@ -98,7 +98,7 @@
// Force writeCounter to match readCounter.
// This is because we cannot change the read counter in the hardware.
- mAudioEndpoint.setDataWriteCounter(readCounter);
+ mAudioEndpoint->setDataWriteCounter(readCounter);
}
void AudioStreamInternalPlay::onFlushFromServer() {
@@ -135,11 +135,11 @@
// If we have gotten this far then we have at least one timestamp from server.
// If a DMA channel or DSP is reading the other end then we have to update the readCounter.
- if (mAudioEndpoint.isFreeRunning()) {
+ if (mAudioEndpoint->isFreeRunning()) {
// Update data queue based on the timing model.
int64_t estimatedReadCounter = mClockModel.convertTimeToPosition(currentNanoTime);
// ALOGD("AudioStreamInternal::processDataNow() - estimatedReadCounter = %d", (int)estimatedReadCounter);
- mAudioEndpoint.setDataReadCounter(estimatedReadCounter);
+ mAudioEndpoint->setDataReadCounter(estimatedReadCounter);
}
if (mNeedCatchUp.isRequested()) {
@@ -151,7 +151,7 @@
// If the read index passed the write index then consider it an underrun.
// For shared streams, the xRunCount is passed up from the service.
- if (mAudioEndpoint.isFreeRunning() && mAudioEndpoint.getFullFramesAvailable() < 0) {
+ if (mAudioEndpoint->isFreeRunning() && mAudioEndpoint->getFullFramesAvailable() < 0) {
mXRunCount++;
if (ATRACE_ENABLED()) {
ATRACE_INT("aaUnderRuns", mXRunCount);
@@ -170,7 +170,7 @@
// Sleep if there is too much data in the buffer.
// Calculate an ideal time to wake up.
if (wakeTimePtr != nullptr
- && (mAudioEndpoint.getFullFramesAvailable() >= getBufferSize())) {
+ && (mAudioEndpoint->getFullFramesAvailable() >= getBufferSize())) {
// By default wake up a few milliseconds from now. // TODO review
int64_t wakeTime = currentNanoTime + (1 * AAUDIO_NANOS_PER_MILLISECOND);
aaudio_stream_state_t state = getState();
@@ -188,7 +188,7 @@
{
// Sleep until the readCounter catches up and we only have
// the getBufferSize() frames of data sitting in the buffer.
- int64_t nextReadPosition = mAudioEndpoint.getDataWriteCounter() - getBufferSize();
+ int64_t nextReadPosition = mAudioEndpoint->getDataWriteCounter() - getBufferSize();
wakeTime = mClockModel.convertPositionToTime(nextReadPosition);
}
break;
@@ -210,7 +210,7 @@
uint8_t *byteBuffer = (uint8_t *) buffer;
int32_t framesLeft = numFrames;
- mAudioEndpoint.getEmptyFramesAvailable(&wrappingBuffer);
+ mAudioEndpoint->getEmptyFramesAvailable(&wrappingBuffer);
// Write data in one or two parts.
int partIndex = 0;
@@ -236,24 +236,28 @@
partIndex++;
}
int32_t framesWritten = numFrames - framesLeft;
- mAudioEndpoint.advanceWriteIndex(framesWritten);
+ mAudioEndpoint->advanceWriteIndex(framesWritten);
return framesWritten;
}
int64_t AudioStreamInternalPlay::getFramesRead() {
- const int64_t framesReadHardware = isClockModelInControl()
- ? mClockModel.convertTimeToPosition(AudioClock::getNanoseconds())
- : mAudioEndpoint.getDataReadCounter();
- // Add service offset and prevent retrograde motion.
- mLastFramesRead = std::max(mLastFramesRead, framesReadHardware + mFramesOffsetFromService);
+ if (mAudioEndpoint) {
+ const int64_t framesReadHardware = isClockModelInControl()
+ ? mClockModel.convertTimeToPosition(AudioClock::getNanoseconds())
+ : mAudioEndpoint->getDataReadCounter();
+ // Add service offset and prevent retrograde motion.
+ mLastFramesRead = std::max(mLastFramesRead, framesReadHardware + mFramesOffsetFromService);
+ }
return mLastFramesRead;
}
int64_t AudioStreamInternalPlay::getFramesWritten() {
- const int64_t framesWritten = mAudioEndpoint.getDataWriteCounter()
- + mFramesOffsetFromService;
- return framesWritten;
+ if (mAudioEndpoint) {
+ mLastFramesWritten = mAudioEndpoint->getDataWriteCounter()
+ + mFramesOffsetFromService;
+ }
+ return mLastFramesWritten;
}
@@ -268,11 +272,11 @@
// result might be a frame count
while (mCallbackEnabled.load() && isActive() && (result >= 0)) {
// Call application using the AAudio callback interface.
- callbackResult = maybeCallDataCallback(mCallbackBuffer, mCallbackFrames);
+ callbackResult = maybeCallDataCallback(mCallbackBuffer.get(), mCallbackFrames);
if (callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE) {
// Write audio data to stream. This is a BLOCKING WRITE!
- result = write(mCallbackBuffer, mCallbackFrames, timeoutNanos);
+ result = write(mCallbackBuffer.get(), mCallbackFrames, timeoutNanos);
if ((result != mCallbackFrames)) {
if (result >= 0) {
// Only wrote some of the frames requested. Must have timed out.
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.h b/media/libaaudio/src/client/AudioStreamInternalPlay.h
index cab2942..2e93157 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.h
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.h
@@ -92,8 +92,6 @@
aaudio_result_t writeNowWithConversion(const void *buffer,
int32_t numFrames);
- int64_t mLastFramesRead = 0; // used to prevent retrograde motion
-
AAudioFlowGraph mFlowGraph;
};
diff --git a/media/libaaudio/src/core/AudioGlobal.cpp b/media/libaaudio/src/core/AudioGlobal.cpp
index d299761..7f5d8d5 100644
--- a/media/libaaudio/src/core/AudioGlobal.cpp
+++ b/media/libaaudio/src/core/AudioGlobal.cpp
@@ -71,7 +71,42 @@
AAUDIO_CASE_ENUM(AAUDIO_ERROR_NO_SERVICE);
AAUDIO_CASE_ENUM(AAUDIO_ERROR_INVALID_RATE);
}
- return "Unrecognized AAudio error.";
+ return "Unrecognized";
+}
+
+const char* AudioGlobal_convertFormatToText(aaudio_format_t format) {
+ switch (format) {
+ AAUDIO_CASE_ENUM(AAUDIO_FORMAT_UNSPECIFIED);
+ AAUDIO_CASE_ENUM(AAUDIO_FORMAT_INVALID);
+ AAUDIO_CASE_ENUM(AAUDIO_FORMAT_PCM_I16);
+ AAUDIO_CASE_ENUM(AAUDIO_FORMAT_PCM_FLOAT);
+ }
+ return "Unrecognized";
+}
+
+const char* AudioGlobal_convertDirectionToText(aaudio_direction_t direction) {
+ switch (direction) {
+ AAUDIO_CASE_ENUM(AAUDIO_DIRECTION_INPUT);
+ AAUDIO_CASE_ENUM(AAUDIO_DIRECTION_OUTPUT);
+ }
+ return "Unrecognized";
+}
+
+const char* AudioGlobal_convertPerformanceModeToText(aaudio_performance_mode_t mode) {
+ switch (mode) {
+ AAUDIO_CASE_ENUM(AAUDIO_PERFORMANCE_MODE_POWER_SAVING);
+ AAUDIO_CASE_ENUM(AAUDIO_PERFORMANCE_MODE_NONE);
+ AAUDIO_CASE_ENUM(AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
+ }
+ return "Unrecognized";
+}
+
+const char* AudioGlobal_convertSharingModeToText(aaudio_sharing_mode_t mode) {
+ switch (mode) {
+ AAUDIO_CASE_ENUM(AAUDIO_SHARING_MODE_SHARED);
+ AAUDIO_CASE_ENUM(AAUDIO_SHARING_MODE_EXCLUSIVE);
+ }
+ return "Unrecognized";
}
const char* AudioGlobal_convertStreamStateToText(aaudio_stream_state_t state) {
@@ -91,7 +126,7 @@
AAUDIO_CASE_ENUM(AAUDIO_STREAM_STATE_CLOSED);
AAUDIO_CASE_ENUM(AAUDIO_STREAM_STATE_DISCONNECTED);
}
- return "Unrecognized AAudio state.";
+ return "Unrecognized";
}
#undef AAUDIO_CASE_ENUM
diff --git a/media/libaaudio/src/core/AudioGlobal.h b/media/libaaudio/src/core/AudioGlobal.h
index 312cef2..1e88d15 100644
--- a/media/libaaudio/src/core/AudioGlobal.h
+++ b/media/libaaudio/src/core/AudioGlobal.h
@@ -25,9 +25,12 @@
aaudio_policy_t AudioGlobal_getMMapPolicy();
aaudio_result_t AudioGlobal_setMMapPolicy(aaudio_policy_t policy);
+const char* AudioGlobal_convertFormatToText(aaudio_format_t format);
+const char* AudioGlobal_convertDirectionToText(aaudio_direction_t direction);
+const char* AudioGlobal_convertPerformanceModeToText(aaudio_performance_mode_t mode);
const char* AudioGlobal_convertResultToText(aaudio_result_t returnCode);
+const char* AudioGlobal_convertSharingModeToText(aaudio_sharing_mode_t mode);
const char* AudioGlobal_convertStreamStateToText(aaudio_stream_state_t state);
-
}
#endif // AAUDIO_AUDIOGLOBAL_H
diff --git a/media/libaaudio/src/core/AudioStream.cpp b/media/libaaudio/src/core/AudioStream.cpp
index f51db70..0644368 100644
--- a/media/libaaudio/src/core/AudioStream.cpp
+++ b/media/libaaudio/src/core/AudioStream.cpp
@@ -20,6 +20,9 @@
#include <atomic>
#include <stdint.h>
+
+#include <media/MediaMetricsItem.h>
+
#include <aaudio/AAudio.h>
#include "AudioStreamBuilder.h"
@@ -29,7 +32,6 @@
namespace aaudio {
-
// Sequential number assigned to streams solely for debugging purposes.
static aaudio_stream_id_t AAudio_getNextStreamId() {
static std::atomic <aaudio_stream_id_t> nextStreamId{1};
@@ -103,6 +105,26 @@
return AAUDIO_OK;
}
+void AudioStream::logOpen() {
+ if (mMetricsId.size() > 0) {
+ android::mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_PERFORMANCEMODE,
+ AudioGlobal_convertPerformanceModeToText(getPerformanceMode()))
+ .set(AMEDIAMETRICS_PROP_SHARINGMODE,
+ AudioGlobal_convertSharingModeToText(getSharingMode()))
+ .record();
+ }
+}
+
+void AudioStream::logBufferState() {
+ if (mMetricsId.size() > 0) {
+ android::mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_BUFFERSIZEFRAMES, (int32_t) getBufferSize())
+ .set(AMEDIAMETRICS_PROP_UNDERRUN, (int32_t) getXRunCount())
+ .record();
+ }
+}
+
aaudio_result_t AudioStream::systemStart() {
std::lock_guard<std::mutex> lock(mStreamLock);
@@ -111,6 +133,33 @@
return AAUDIO_ERROR_INVALID_STATE;
}
+ switch (getState()) {
+ // Is this a good time to start?
+ case AAUDIO_STREAM_STATE_OPEN:
+ case AAUDIO_STREAM_STATE_PAUSING:
+ case AAUDIO_STREAM_STATE_PAUSED:
+ case AAUDIO_STREAM_STATE_STOPPING:
+ case AAUDIO_STREAM_STATE_STOPPED:
+ case AAUDIO_STREAM_STATE_FLUSHED:
+ break; // Proceed with starting.
+
+ // Already started?
+ case AAUDIO_STREAM_STATE_STARTING:
+ case AAUDIO_STREAM_STATE_STARTED:
+ ALOGW("%s() stream was already started, state = %s", __func__,
+ AudioGlobal_convertStreamStateToText(getState()));
+ return AAUDIO_ERROR_INVALID_STATE;
+
+ // Don't start when the stream is dead!
+ case AAUDIO_STREAM_STATE_DISCONNECTED:
+ case AAUDIO_STREAM_STATE_CLOSING:
+ case AAUDIO_STREAM_STATE_CLOSED:
+ default:
+ ALOGW("%s() stream is dead, state = %s", __func__,
+ AudioGlobal_convertStreamStateToText(getState()));
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
+
aaudio_result_t result = requestStart();
if (result == AAUDIO_OK) {
// We only call this for logging in "dumpsys audio". So ignore return code.
@@ -156,8 +205,8 @@
case AAUDIO_STREAM_STATE_CLOSING:
case AAUDIO_STREAM_STATE_CLOSED:
default:
- ALOGW("safePause() stream not running, state = %s",
- AudioGlobal_convertStreamStateToText(getState()));
+ ALOGW("%s() stream not running, state = %s",
+ __func__, AudioGlobal_convertStreamStateToText(getState()));
return AAUDIO_ERROR_INVALID_STATE;
}
@@ -264,10 +313,22 @@
void AudioStream::setState(aaudio_stream_state_t state) {
ALOGD("%s(s#%d) from %d to %d", __func__, getId(), mState, state);
+ // Track transition to DISCONNECTED state.
+ if (state == AAUDIO_STREAM_STATE_DISCONNECTED && mState != state) {
+ android::mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_DISCONNECT)
+ .set(AMEDIAMETRICS_PROP_STATE, AudioGlobal_convertStreamStateToText(getState()))
+ .record();
+ }
// CLOSED is a final state
if (mState == AAUDIO_STREAM_STATE_CLOSED) {
ALOGE("%s(%d) tried to set to %d but already CLOSED", __func__, getId(), state);
+ // Once CLOSING, we can only move to CLOSED state.
+ } else if (mState == AAUDIO_STREAM_STATE_CLOSING
+ && state != AAUDIO_STREAM_STATE_CLOSED) {
+ ALOGE("%s(%d) tried to set to %d but already CLOSING", __func__, getId(), state);
+
// Once DISCONNECTED, we can only move to CLOSING or CLOSED state.
} else if (mState == AAUDIO_STREAM_STATE_DISCONNECTED
&& !(state == AAUDIO_STREAM_STATE_CLOSING
diff --git a/media/libaaudio/src/core/AudioStream.h b/media/libaaudio/src/core/AudioStream.h
index 9bda41b..613a092 100644
--- a/media/libaaudio/src/core/AudioStream.h
+++ b/media/libaaudio/src/core/AudioStream.h
@@ -95,7 +95,6 @@
int64_t *framePosition,
int64_t *timeNanoseconds) = 0;
-
/**
* Update state machine.()
* @return
@@ -114,6 +113,10 @@
*/
virtual aaudio_result_t open(const AudioStreamBuilder& builder);
+ // log to MediaMetrics
+ virtual void logOpen();
+ void logBufferState();
+
/**
* Free any hardware or system resources from the open() call.
* It is safe to call release_l() multiple times.
@@ -573,6 +576,8 @@
mIsPrivacySensitive = privacySensitive;
}
+ std::string mMetricsId; // set once during open()
+
private:
aaudio_result_t safeStop();
diff --git a/media/libaaudio/src/core/AudioStreamBuilder.cpp b/media/libaaudio/src/core/AudioStreamBuilder.cpp
index 3a1e1c9..60dad84 100644
--- a/media/libaaudio/src/core/AudioStreamBuilder.cpp
+++ b/media/libaaudio/src/core/AudioStreamBuilder.cpp
@@ -193,10 +193,14 @@
*streamPtr = audioStream;
} else {
delete audioStream;
+ audioStream = nullptr;
}
}
}
}
+ if (audioStream != nullptr) {
+ audioStream->logOpen();
+ }
}
return result;
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
index abc3239..c062882 100644
--- a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
@@ -26,6 +26,7 @@
#include <media/AudioTimestamp.h>
#include <utils/String16.h>
+#include "core/AudioGlobal.h"
#include "core/AudioStream.h"
#include "legacy/AudioStreamLegacy.h"
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.h b/media/libaaudio/src/legacy/AudioStreamLegacy.h
index da9205f..9c24b2b 100644
--- a/media/libaaudio/src/legacy/AudioStreamLegacy.h
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.h
@@ -128,6 +128,10 @@
return mFramesRead.increment(frames);
}
+ // This is used for exact matching by MediaMetrics. So do not change it.
+ // MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_AAUDIO
+ static constexpr char kCallerName[] = "aaudio";
+
MonotonicCounter mFramesWritten;
MonotonicCounter mFramesRead;
MonotonicCounter mTimestampPosition;
@@ -139,6 +143,7 @@
const android::sp<StreamDeviceCallback> mDeviceCallback;
AtomicRequestor mRequestDisconnect;
+
};
} /* namespace aaudio */
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
index 904edd1..853c0db 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
@@ -179,6 +179,9 @@
selectedDeviceId
);
+ // Set it here so it can be logged by the destructor if the open failed.
+ mAudioRecord->setCallerName(kCallerName);
+
// Did we get a valid track?
status_t status = mAudioRecord->initCheck();
if (status != OK) {
@@ -202,6 +205,9 @@
}
}
+ mMetricsId = std::string(AMEDIAMETRICS_KEY_PREFIX_AUDIO_RECORD)
+ + std::to_string(mAudioRecord->getPortId());
+
// Get the actual values from the AudioRecord.
setSamplesPerFrame(mAudioRecord->channelCount());
@@ -286,6 +292,7 @@
// Then call it from here
if (getState() != AAUDIO_STREAM_STATE_CLOSING) {
mAudioRecord->removeAudioDeviceCallback(mDeviceCallback);
+ logBufferState();
mAudioRecord.clear();
mFixedBlockWriter.close();
return AudioStream::release_l();
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
index d54d043..1120f05 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
@@ -173,6 +173,9 @@
selectedDeviceId
);
+ // Set it here so it can be logged by the destructor if the open failed.
+ mAudioTrack->setCallerName(kCallerName);
+
// Did we get a valid track?
status_t status = mAudioTrack->initCheck();
if (status != NO_ERROR) {
@@ -181,6 +184,9 @@
return AAudioConvert_androidToAAudioResult(status);
}
+ mMetricsId = std::string(AMEDIAMETRICS_KEY_PREFIX_AUDIO_TRACK)
+ + std::to_string(mAudioTrack->getPortId());
+
doSetVolume();
// Get the actual values from the AudioTrack.
@@ -215,6 +221,9 @@
: (aaudio_session_id_t) mAudioTrack->getSessionId();
setSessionId(actualSessionId);
+ mInitialBufferCapacity = getBufferCapacity();
+ mInitialFramesPerBurst = getFramesPerBurst();
+
mAudioTrack->addAudioDeviceCallback(mDeviceCallback);
// Update performance mode based on the actual stream flags.
@@ -245,6 +254,7 @@
aaudio_result_t AudioStreamTrack::release_l() {
if (getState() != AAUDIO_STREAM_STATE_CLOSING) {
mAudioTrack->removeAudioDeviceCallback(mDeviceCallback);
+ logBufferState();
// TODO Investigate why clear() causes a hang in test_various.cpp
// if I call close() from a data callback.
// But the same thing in AudioRecord is OK!
@@ -265,7 +275,16 @@
// Stream got rerouted so we disconnect.
case AudioTrack::EVENT_NEW_IAUDIOTRACK:
- processCallbackCommon(AAUDIO_CALLBACK_OPERATION_DISCONNECTED, info);
+ // request stream disconnect if the restored AudioTrack has properties not matching
+ // what was requested initially
+ if (mAudioTrack->channelCount() != getSamplesPerFrame()
+ || mAudioTrack->format() != getFormat()
+ || mAudioTrack->getSampleRate() != getSampleRate()
+ || mAudioTrack->getRoutedDeviceId() != getDeviceId()
+ || getBufferCapacity() != mInitialBufferCapacity
+ || getFramesPerBurst() != mInitialFramesPerBurst) {
+ processCallbackCommon(AAUDIO_CALLBACK_OPERATION_DISCONNECTED, info);
+ }
break;
default:
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.h b/media/libaaudio/src/legacy/AudioStreamTrack.h
index 550f693..93a1ff4 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.h
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.h
@@ -104,6 +104,10 @@
// TODO add 64-bit position reporting to AudioTrack and use it.
aaudio_wrapping_frames_t mPositionWhenPausing = 0;
+
+ // initial AudioTrack frame count and notification period
+ int32_t mInitialBufferCapacity = 0;
+ int32_t mInitialFramesPerBurst = 0;
};
} /* namespace aaudio */
diff --git a/media/libaaudio/tests/test_various.cpp b/media/libaaudio/tests/test_various.cpp
index 5bb1046..1c26615 100644
--- a/media/libaaudio/tests/test_various.cpp
+++ b/media/libaaudio/tests/test_various.cpp
@@ -28,15 +28,20 @@
// Callback function that does nothing.
aaudio_data_callback_result_t NoopDataCallbackProc(
- AAudioStream *stream,
- void *userData,
+ AAudioStream * stream,
+ void * /* userData */,
void *audioData,
int32_t numFrames
) {
- (void) stream;
- (void) userData;
- (void) audioData;
- (void) numFrames;
+ int channels = AAudioStream_getChannelCount(stream);
+ int numSamples = channels * numFrames;
+ bool allZeros = true;
+ float * const floatData = reinterpret_cast<float *>(audioData);
+ for (int i = 0; i < numSamples; i++) {
+ allZeros &= (floatData[i] == 0.0f);
+ floatData[i] = 0.0f;
+ }
+ EXPECT_TRUE(allZeros);
return AAUDIO_CALLBACK_RESULT_CONTINUE;
}
@@ -56,6 +61,7 @@
nullptr);
AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, perfMode);
AAudioStreamBuilder_setSharingMode(aaudioBuilder, sharingMode);
+ AAudioStreamBuilder_setFormat(aaudioBuilder, AAUDIO_FORMAT_PCM_FLOAT);
// Create an AAudioStream using the Builder.
ASSERT_EQ(AAUDIO_OK,
@@ -69,12 +75,33 @@
EXPECT_EQ(AAUDIO_OK, AAudioStream_requestStop(aaudioStream));
EXPECT_EQ(AAUDIO_OK, AAudioStream_release(aaudioStream));
- aaudio_stream_state_t state = AAudioStream_getState(aaudioStream);
- EXPECT_EQ(AAUDIO_STREAM_STATE_CLOSING, state);
+ EXPECT_EQ(AAUDIO_STREAM_STATE_CLOSING, AAudioStream_getState(aaudioStream));
// We should be able to call this again without crashing.
EXPECT_EQ(AAUDIO_OK, AAudioStream_release(aaudioStream));
- state = AAudioStream_getState(aaudioStream);
+ EXPECT_EQ(AAUDIO_STREAM_STATE_CLOSING, AAudioStream_getState(aaudioStream));
+
+ // We expect these not to crash.
+ AAudioStream_setBufferSizeInFrames(aaudioStream, 0);
+ AAudioStream_setBufferSizeInFrames(aaudioStream, 99999999);
+
+ // We should NOT be able to start or change a stream after it has been released.
+ EXPECT_EQ(AAUDIO_ERROR_INVALID_STATE, AAudioStream_requestStart(aaudioStream));
+ EXPECT_EQ(AAUDIO_STREAM_STATE_CLOSING, AAudioStream_getState(aaudioStream));
+ EXPECT_EQ(AAUDIO_ERROR_INVALID_STATE, AAudioStream_requestPause(aaudioStream));
+ EXPECT_EQ(AAUDIO_STREAM_STATE_CLOSING, AAudioStream_getState(aaudioStream));
+ EXPECT_EQ(AAUDIO_ERROR_INVALID_STATE, AAudioStream_requestStop(aaudioStream));
+ EXPECT_EQ(AAUDIO_STREAM_STATE_CLOSING, AAudioStream_getState(aaudioStream));
+
+ // Does this crash?
+ EXPECT_LT(0, AAudioStream_getFramesRead(aaudioStream));
+ EXPECT_LT(0, AAudioStream_getFramesWritten(aaudioStream));
+
+ // Verify Closing State. Does this crash?
+ aaudio_stream_state_t state = AAUDIO_STREAM_STATE_UNKNOWN;
+ EXPECT_EQ(AAUDIO_OK, AAudioStream_waitForStateChange(aaudioStream,
+ AAUDIO_STREAM_STATE_UNKNOWN, &state,
+ 500 * NANOS_PER_MILLISECOND));
EXPECT_EQ(AAUDIO_STREAM_STATE_CLOSING, state);
EXPECT_EQ(AAUDIO_OK, AAudioStream_close(aaudioStream));
@@ -114,6 +141,7 @@
// Request stream properties.
AAudioStreamBuilder_setDataCallback(aaudioBuilder, NoopDataCallbackProc, nullptr);
AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, perfMode);
+ AAudioStreamBuilder_setFormat(aaudioBuilder, AAUDIO_FORMAT_PCM_FLOAT);
// Create an AAudioStream using the Builder.
ASSERT_EQ(AAUDIO_OK, AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream));
diff --git a/media/libaudioclient/Android.bp b/media/libaudioclient/Android.bp
index 4762b63..0d20f20 100644
--- a/media/libaudioclient/Android.bp
+++ b/media/libaudioclient/Android.bp
@@ -156,6 +156,7 @@
aidl_interface {
name: "capture_state_listener-aidl",
+ unstable: true,
local_include_dir: "aidl",
srcs: [
"aidl/android/media/ICaptureStateListener.aidl",
diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp
index 3479a29..0bbceef 100644
--- a/media/libaudioclient/AudioRecord.cpp
+++ b/media/libaudioclient/AudioRecord.cpp
@@ -170,6 +170,10 @@
mediametrics::LogItem(mMetricsId)
.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_DTOR)
+ .set(AMEDIAMETRICS_PROP_CALLERNAME,
+ mCallerName.empty()
+ ? AMEDIAMETRICS_PROP_CALLERNAME_VALUE_UNKNOWN
+ : mCallerName.c_str())
.set(AMEDIAMETRICS_PROP_STATUS, (int32_t)mStatus)
.record();
@@ -396,6 +400,10 @@
status_t status = NO_ERROR;
mediametrics::Defer defer([&] {
mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_CALLERNAME,
+ mCallerName.empty()
+ ? AMEDIAMETRICS_PROP_CALLERNAME_VALUE_UNKNOWN
+ : mCallerName.c_str())
.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_START)
.set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(systemTime() - beginNs))
.set(AMEDIAMETRICS_PROP_STATE, stateToString(mActive))
@@ -411,6 +419,9 @@
mFramesReadServerOffset -= mFramesRead + framesFlushed;
mFramesRead = 0;
mProxy->clearTimestamp(); // timestamp is invalid until next server push
+ mPreviousTimestamp.clear();
+ mTimestampRetrogradePositionReported = false;
+ mTimestampRetrogradeTimeReported = false;
// reset current position as seen by client to 0
mProxy->setEpoch(mProxy->getEpoch() - mProxy->getPosition());
@@ -600,6 +611,39 @@
timestamp->mPosition[i] += mFramesReadServerOffset;
}
}
+
+ bool timestampRetrogradeTimeReported = false;
+ bool timestampRetrogradePositionReported = false;
+ for (int i = 0; i < ExtendedTimestamp::LOCATION_MAX; ++i) {
+ if (timestamp->mTimeNs[i] >= 0 && mPreviousTimestamp.mTimeNs[i] >= 0) {
+ if (timestamp->mTimeNs[i] < mPreviousTimestamp.mTimeNs[i]) {
+ if (!mTimestampRetrogradeTimeReported) {
+ ALOGD("%s: retrograde time adjusting [%d] current:%lld to previous:%lld",
+ __func__, i, (long long)timestamp->mTimeNs[i],
+ (long long)mPreviousTimestamp.mTimeNs[i]);
+ timestampRetrogradeTimeReported = true;
+ }
+ timestamp->mTimeNs[i] = mPreviousTimestamp.mTimeNs[i];
+ }
+ if (timestamp->mPosition[i] < mPreviousTimestamp.mPosition[i]) {
+ if (!mTimestampRetrogradePositionReported) {
+ ALOGD("%s: retrograde position"
+ " adjusting [%d] current:%lld to previous:%lld",
+ __func__, i, (long long)timestamp->mPosition[i],
+ (long long)mPreviousTimestamp.mPosition[i]);
+ timestampRetrogradePositionReported = true;
+ }
+ timestamp->mPosition[i] = mPreviousTimestamp.mPosition[i];
+ }
+ }
+ }
+ mPreviousTimestamp = *timestamp;
+ if (timestampRetrogradeTimeReported) {
+ mTimestampRetrogradeTimeReported = true;
+ }
+ if (timestampRetrogradePositionReported) {
+ mTimestampRetrogradePositionReported = true;
+ }
}
return status;
}
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index 8b357a3..ca80dc4 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -301,6 +301,10 @@
mediametrics::LogItem(mMetricsId)
.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_DTOR)
+ .set(AMEDIAMETRICS_PROP_CALLERNAME,
+ mCallerName.empty()
+ ? AMEDIAMETRICS_PROP_CALLERNAME_VALUE_UNKNOWN
+ : mCallerName.c_str())
.set(AMEDIAMETRICS_PROP_STATE, stateToString(mState))
.set(AMEDIAMETRICS_PROP_STATUS, (int32_t)mStatus)
.record();
@@ -641,6 +645,10 @@
status_t status = NO_ERROR; // logged: make sure to set this before returning.
mediametrics::Defer defer([&] {
mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_CALLERNAME,
+ mCallerName.empty()
+ ? AMEDIAMETRICS_PROP_CALLERNAME_VALUE_UNKNOWN
+ : mCallerName.c_str())
.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_START)
.set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(systemTime() - beginNs))
.set(AMEDIAMETRICS_PROP_STATE, stateToString(mState))
@@ -777,7 +785,9 @@
.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_STOP)
.set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(systemTime() - beginNs))
.set(AMEDIAMETRICS_PROP_STATE, stateToString(mState))
- .record(); });
+ .record();
+ logBufferSizeUnderruns();
+ });
ALOGV("%s(%d): prior state:%s", __func__, mPortId, stateToString(mState));
@@ -873,7 +883,7 @@
{
const int64_t beginNs = systemTime();
AutoMutex lock(mLock);
- mediametrics::Defer([&]() {
+ mediametrics::Defer defer([&]() {
mediametrics::LogItem(mMetricsId)
.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_PAUSE)
.set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(systemTime() - beginNs))
@@ -1107,6 +1117,7 @@
if (mOutput == AUDIO_IO_HANDLE_NONE || mProxy.get() == 0) {
return NO_INIT;
}
+
return (ssize_t) mProxy->getBufferSizeInFrames();
}
@@ -1128,6 +1139,16 @@
return NO_ERROR;
}
+void AudioTrack::logBufferSizeUnderruns() {
+ LOG_ALWAYS_FATAL_IF(mMetricsId.size() == 0, "mMetricsId is empty!");
+ ALOGD("%s(), mMetricsId = %s", __func__, mMetricsId.c_str());
+ // FIXME THis hangs! Why?
+// android::mediametrics::LogItem(mMetricsId)
+// .set(AMEDIAMETRICS_PROP_BUFFERSIZEFRAMES, (int32_t) getBufferSizeInFrames())
+// .set(AMEDIAMETRICS_PROP_UNDERRUN, (int32_t) getUnderrunCount())
+// .record();
+}
+
ssize_t AudioTrack::setBufferSizeInFrames(size_t bufferSizeInFrames)
{
AutoMutex lock(mLock);
@@ -1138,7 +1159,13 @@
if (!audio_is_linear_pcm(mFormat)) {
return INVALID_OPERATION;
}
- return (ssize_t) mProxy->setBufferSizeInFrames((uint32_t) bufferSizeInFrames);
+
+ ssize_t originalBufferSize = mProxy->getBufferSizeInFrames();
+ ssize_t finalBufferSize = mProxy->setBufferSizeInFrames((uint32_t) bufferSizeInFrames);
+ if (originalBufferSize != finalBufferSize) {
+ logBufferSizeUnderruns();
+ }
+ return finalBufferSize;
}
status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount)
@@ -2410,7 +2437,7 @@
{
status_t result = NO_ERROR; // logged: make sure to set this before returning.
const int64_t beginNs = systemTime();
- mediametrics::Defer([&] {
+ mediametrics::Defer defer([&] {
mediametrics::LogItem(mMetricsId)
.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_RESTORE)
.set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(systemTime() - beginNs))
diff --git a/media/libaudioclient/ToneGenerator.cpp b/media/libaudioclient/ToneGenerator.cpp
index 536b00d..050ad65 100644
--- a/media/libaudioclient/ToneGenerator.cpp
+++ b/media/libaudioclient/ToneGenerator.cpp
@@ -1262,7 +1262,9 @@
AUDIO_UID_INVALID,
-1,
&attr);
-
+ // Set caller name so it can be logged in destructor.
+ // MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_TONEGENERATOR
+ mpAudioTrack->setCallerName("tonegenerator");
if (status != NO_ERROR) {
ALOGE("AudioTrack(%p) set failed with error %d", mpAudioTrack.get(), status);
mpAudioTrack.clear();
diff --git a/media/libaudioclient/include/media/AudioRecord.h b/media/libaudioclient/include/media/AudioRecord.h
index 5c300ed..2f66658 100644
--- a/media/libaudioclient/include/media/AudioRecord.h
+++ b/media/libaudioclient/include/media/AudioRecord.h
@@ -277,6 +277,19 @@
*/
status_t getMetrics(mediametrics::Item * &item);
+ /*
+ * Set name of API that is using this object.
+ * For example "aaudio" or "opensles".
+ * This may be logged or reported as part of MediaMetrics.
+ */
+ void setCallerName(const std::string &name) {
+ mCallerName = name;
+ }
+
+ std::string getCallerName() const {
+ return mCallerName;
+ };
+
/* After it's created the track is not active. Call start() to
* make it active. If set, the callback will start being called.
* If event is not AudioSystem::SYNC_EVENT_NONE, the capture start will be delayed until
@@ -711,6 +724,10 @@
bool mInOverrun; // whether recorder is currently in overrun state
+ ExtendedTimestamp mPreviousTimestamp{}; // used to detect retrograde motion
+ bool mTimestampRetrogradePositionReported = false; // reduce log spam
+ bool mTimestampRetrogradeTimeReported = false; // reduce log spam
+
private:
class DeathNotifier : public IBinder::DeathRecipient {
public:
@@ -772,6 +789,7 @@
};
MediaMetrics mMediaMetrics;
std::string mMetricsId; // GUARDED_BY(mLock), could change in createRecord_l().
+ std::string mCallerName; // for example "aaudio"
};
}; // namespace android
diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h
index afae4d8..17af7d4 100644
--- a/media/libaudioclient/include/media/AudioTrack.h
+++ b/media/libaudioclient/include/media/AudioTrack.h
@@ -410,6 +410,19 @@
*/
status_t getMetrics(mediametrics::Item * &item);
+ /*
+ * Set name of API that is using this object.
+ * For example "aaudio" or "opensles".
+ * This may be logged or reported as part of MediaMetrics.
+ */
+ void setCallerName(const std::string &name) {
+ mCallerName = name;
+ }
+
+ std::string getCallerName() const {
+ return mCallerName;
+ };
+
/* After it's created the track is not active. Call start() to
* make it active. If set, the callback will start being called.
* If the track was previously paused, volume is ramped up over the first mix buffer.
@@ -1259,6 +1272,9 @@
};
MediaMetrics mMediaMetrics;
std::string mMetricsId; // GUARDED_BY(mLock), could change in createTrack_l().
+ std::string mCallerName; // for example "aaudio"
+
+ void logBufferSizeUnderruns();
private:
class AudioTrackCallback : public media::BnAudioTrackCallback {
diff --git a/media/libeffects/config/Android.bp b/media/libeffects/config/Android.bp
index 8476f82..8493e30 100644
--- a/media/libeffects/config/Android.bp
+++ b/media/libeffects/config/Android.bp
@@ -15,6 +15,7 @@
"libtinyxml2",
"libutils",
"libmedia_helper",
+ "libcutils",
],
header_libs: ["libaudio_system_headers"],
diff --git a/media/libeffects/config/include/media/EffectsConfig.h b/media/libeffects/config/include/media/EffectsConfig.h
index ef10e0d..57d4dd7 100644
--- a/media/libeffects/config/include/media/EffectsConfig.h
+++ b/media/libeffects/config/include/media/EffectsConfig.h
@@ -35,11 +35,6 @@
/** Default path of effect configuration file. Relative to DEFAULT_LOCATIONS. */
constexpr const char* DEFAULT_NAME = "audio_effects.xml";
-/** Default path of effect configuration file.
- * The /vendor partition is the recommended one, the others are deprecated.
- */
-constexpr const char* DEFAULT_LOCATIONS[] = {"/odm/etc", "/vendor/etc", "/system/etc"};
-
/** Directories where the effect libraries will be search for. */
constexpr const char* LD_EFFECT_LIBRARY_PATH[] =
#ifdef __LP64__
diff --git a/media/libeffects/config/src/EffectsConfig.cpp b/media/libeffects/config/src/EffectsConfig.cpp
index 85fbf11..26eaaf8 100644
--- a/media/libeffects/config/src/EffectsConfig.cpp
+++ b/media/libeffects/config/src/EffectsConfig.cpp
@@ -27,6 +27,7 @@
#include <media/EffectsConfig.h>
#include <media/TypeConverter.h>
+#include <system/audio_config.h>
using namespace tinyxml2;
@@ -338,7 +339,7 @@
return parseWithPath(path);
}
- for (const std::string& location : DEFAULT_LOCATIONS) {
+ for (const std::string& location : audio_get_configuration_paths()) {
std::string defaultPath = location + '/' + DEFAULT_NAME;
if (access(defaultPath.c_str(), R_OK) != 0) {
continue;
diff --git a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
index 1cb81a6..39f5bb6 100644
--- a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
+++ b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
@@ -1906,11 +1906,15 @@
//ALOGV("\tReverb_command cmdCode Case: "
// "EFFECT_CMD_GET_PARAM start");
effect_param_t *p = (effect_param_t *)pCmdData;
+ if (pCmdData == nullptr) {
+ ALOGW("\tLVM_ERROR : pCmdData is NULL");
+ return -EINVAL;
+ }
if (SIZE_MAX - sizeof(effect_param_t) < (size_t)p->psize) {
android_errorWriteLog(0x534e4554, "26347509");
return -EINVAL;
}
- if (pCmdData == NULL || cmdSize < sizeof(effect_param_t) ||
+ if (cmdSize < sizeof(effect_param_t) ||
cmdSize < (sizeof(effect_param_t) + p->psize) ||
pReplyData == NULL || replySize == NULL ||
*replySize < (sizeof(effect_param_t) + p->psize)) {
diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp
index 1df7c88..be3f995 100644
--- a/media/libmedia/Android.bp
+++ b/media/libmedia/Android.bp
@@ -33,6 +33,7 @@
aidl_interface {
name: "resourcemanager_aidl_interface",
+ unstable: true,
local_include_dir: "aidl",
srcs: [
"aidl/android/media/IResourceManagerClient.aidl",
@@ -186,7 +187,7 @@
srcs: ["MidiIoWrapper.cpp"],
static_libs: [
- "libsonivox",
+ "libsonivoxwithoutjet",
],
header_libs: [
@@ -311,7 +312,7 @@
"libutils",
"libbinder",
"libbinder_ndk",
- "libsonivox",
+ //"libsonivox",
"libandroidicu",
"libexpat",
"libcamera_client",
@@ -327,7 +328,7 @@
"libaudioclient",
"libbinder",
"libandroidicu",
- "libsonivox",
+ //"libsonivox",
"libmedia_omx",
],
diff --git a/media/libmedia/aidl/android/media/IResourceManagerService.aidl b/media/libmedia/aidl/android/media/IResourceManagerService.aidl
index 3dd0859..1b2d522 100644
--- a/media/libmedia/aidl/android/media/IResourceManagerService.aidl
+++ b/media/libmedia/aidl/android/media/IResourceManagerService.aidl
@@ -94,4 +94,12 @@
* remove existing override on originalPid if newPid is -1.
*/
void overridePid(int originalPid, int newPid);
+
+ /**
+ * Mark a client for pending removal
+ *
+ * @param pid pid from which the client's resources will be removed.
+ * @param clientId clientId within the pid that will be removed.
+ */
+ void markClientForPendingRemoval(int pid, long clientId);
}
diff --git a/media/libmedia/xsd/vts/Android.bp b/media/libmedia/xsd/vts/Android.bp
index 4739504..598e41b 100644
--- a/media/libmedia/xsd/vts/Android.bp
+++ b/media/libmedia/xsd/vts/Android.bp
@@ -31,4 +31,12 @@
"-Wall",
"-Werror",
],
+ data: [
+ ":media_profiles",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts"
+ ],
+ test_config: "vts_mediaProfiles_validate_test.xml",
}
diff --git a/media/libmedia/xsd/vts/vts_mediaProfiles_validate_test.xml b/media/libmedia/xsd/vts/vts_mediaProfiles_validate_test.xml
new file mode 100644
index 0000000..08ab8f4
--- /dev/null
+++ b/media/libmedia/xsd/vts/vts_mediaProfiles_validate_test.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs vts_mediaProfiles_validate_test.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="media_profiles.xsd->/data/local/tmp/media_profiles.xsd" />
+ <option name="push" value="vts_mediaProfiles_validate_test->/data/local/tmp/vts_mediaProfiles_validate_test" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="vts_mediaProfiles_validate_test" />
+ </test>
+</configuration>
diff --git a/media/libmediametrics/Android.bp b/media/libmediametrics/Android.bp
index 0cd5194..03068c7 100644
--- a/media/libmediametrics/Android.bp
+++ b/media/libmediametrics/Android.bp
@@ -22,9 +22,11 @@
export_include_dirs: ["include"],
cflags: [
- "-Werror",
- "-Wno-error=deprecated-declarations",
"-Wall",
+ "-Werror",
+ "-Wextra",
+ "-Wthread-safety",
+ "-Wunreachable-code",
],
sanitize: {
diff --git a/media/libmediametrics/MediaMetricsItem.cpp b/media/libmediametrics/MediaMetricsItem.cpp
index 4371668..7cdbe5f 100644
--- a/media/libmediametrics/MediaMetricsItem.cpp
+++ b/media/libmediametrics/MediaMetricsItem.cpp
@@ -25,6 +25,7 @@
#include <set>
#include <binder/Parcel.h>
+#include <cutils/properties.h>
#include <utils/Errors.h>
#include <utils/Log.h>
#include <utils/SortedVector.h>
diff --git a/media/libmediametrics/include/MediaMetricsConstants.h b/media/libmediametrics/include/MediaMetricsConstants.h
index eb7ac7d..00da69a 100644
--- a/media/libmediametrics/include/MediaMetricsConstants.h
+++ b/media/libmediametrics/include/MediaMetricsConstants.h
@@ -40,6 +40,9 @@
// The AudioRecord key appends the "trackId" to the prefix.
#define AMEDIAMETRICS_KEY_PREFIX_AUDIO_RECORD AMEDIAMETRICS_KEY_PREFIX_AUDIO "record."
+// The AudioStream key appends the "streamId" to the prefix.
+#define AMEDIAMETRICS_KEY_PREFIX_AUDIO_STREAM AMEDIAMETRICS_KEY_PREFIX_AUDIO "stream."
+
// The AudioThread key appends the "threadId" to the prefix.
#define AMEDIAMETRICS_KEY_PREFIX_AUDIO_THREAD AMEDIAMETRICS_KEY_PREFIX_AUDIO "thread."
@@ -83,10 +86,16 @@
// of suppressed in the Time Machine.
#define AMEDIAMETRICS_PROP_SUFFIX_CHAR_DUPLICATES_ALLOWED '#'
+#define AMEDIAMETRICS_PROP_ALLOWUID "_allowUid" // int32_t, allow client uid to post
#define AMEDIAMETRICS_PROP_AUXEFFECTID "auxEffectId" // int32 (AudioTrack)
+#define AMEDIAMETRICS_PROP_BUFFERSIZEFRAMES "bufferSizeFrames" // int32
+#define AMEDIAMETRICS_PROP_BUFFERCAPACITYFRAMES "bufferCapacityFrames" // int32
+#define AMEDIAMETRICS_PROP_BURSTFRAMES "burstFrames" // int32
+#define AMEDIAMETRICS_PROP_CALLERNAME "callerName" // string, eg. "aaudio"
#define AMEDIAMETRICS_PROP_CHANNELCOUNT "channelCount" // int32
#define AMEDIAMETRICS_PROP_CHANNELMASK "channelMask" // int32
#define AMEDIAMETRICS_PROP_CONTENTTYPE "contentType" // string attributes (AudioTrack)
+#define AMEDIAMETRICS_PROP_DIRECTION "direction" // string AAudio input or output
#define AMEDIAMETRICS_PROP_DURATIONNS "durationNs" // int64 duration time span
#define AMEDIAMETRICS_PROP_ENCODING "encoding" // string value of format
#define AMEDIAMETRICS_PROP_EVENT "event#" // string value (often func name)
@@ -99,6 +108,7 @@
#define AMEDIAMETRICS_PROP_LATENCYMS "latencyMs" // double value
#define AMEDIAMETRICS_PROP_ORIGINALFLAGS "originalFlags" // int32
#define AMEDIAMETRICS_PROP_OUTPUTDEVICES "outputDevices" // string value
+#define AMEDIAMETRICS_PROP_PERFORMANCEMODE "performanceMode" // string value, "none", lowLatency"
#define AMEDIAMETRICS_PROP_PLAYBACK_PITCH "playback.pitch" // double value (AudioTrack)
#define AMEDIAMETRICS_PROP_PLAYBACK_SPEED "playback.speed" // double value (AudioTrack)
#define AMEDIAMETRICS_PROP_ROUTEDDEVICEID "routedDeviceId" // int32
@@ -107,6 +117,7 @@
#define AMEDIAMETRICS_PROP_SELECTEDMICDIRECTION "selectedMicDirection" // int32
#define AMEDIAMETRICS_PROP_SELECTEDMICFIELDDIRECTION "selectedMicFieldDimension" // double
#define AMEDIAMETRICS_PROP_SESSIONID "sessionId" // int32
+#define AMEDIAMETRICS_PROP_SHARINGMODE "sharingMode" // string value, "exclusive", shared"
#define AMEDIAMETRICS_PROP_SOURCE "source" // string (AudioAttributes)
#define AMEDIAMETRICS_PROP_STARTUPMS "startupMs" // double value
// State is "ACTIVE" or "STOPPED" for AudioRecord
@@ -129,12 +140,15 @@
// Values are strings accepted for a given property.
// An event is a general description, which often is a function name.
+#define AMEDIAMETRICS_PROP_EVENT_VALUE_CLOSE "close"
#define AMEDIAMETRICS_PROP_EVENT_VALUE_CREATE "create"
#define AMEDIAMETRICS_PROP_EVENT_VALUE_CREATEAUDIOPATCH "createAudioPatch"
#define AMEDIAMETRICS_PROP_EVENT_VALUE_CTOR "ctor"
+#define AMEDIAMETRICS_PROP_EVENT_VALUE_DISCONNECT "disconnect"
#define AMEDIAMETRICS_PROP_EVENT_VALUE_DTOR "dtor"
#define AMEDIAMETRICS_PROP_EVENT_VALUE_FLUSH "flush" // AudioTrack
#define AMEDIAMETRICS_PROP_EVENT_VALUE_INVALIDATE "invalidate" // server track, record
+#define AMEDIAMETRICS_PROP_EVENT_VALUE_OPEN "open"
#define AMEDIAMETRICS_PROP_EVENT_VALUE_PAUSE "pause" // AudioTrack
#define AMEDIAMETRICS_PROP_EVENT_VALUE_READPARAMETERS "readParameters" // Thread
#define AMEDIAMETRICS_PROP_EVENT_VALUE_RESTORE "restore"
@@ -144,4 +158,16 @@
#define AMEDIAMETRICS_PROP_EVENT_VALUE_STOP "stop" // AudioTrack, AudioRecord
#define AMEDIAMETRICS_PROP_EVENT_VALUE_UNDERRUN "underrun" // from Thread
+// Possible values for AMEDIAMETRICS_PROP_CALLERNAME
+// Check within the framework for these strings as this header file may not be explicitly
+// included to avoid unnecessary cross-project dependencies.
+#define AMEDIAMETRICS_PROP_CALLERNAME_VALUE_AAUDIO "aaudio" // Native AAudio
+#define AMEDIAMETRICS_PROP_CALLERNAME_VALUE_JAVA "java" // Java API layer
+#define AMEDIAMETRICS_PROP_CALLERNAME_VALUE_MEDIA "media" // libmedia
+#define AMEDIAMETRICS_PROP_CALLERNAME_VALUE_OPENSLES "opensles" // Open SLES
+#define AMEDIAMETRICS_PROP_CALLERNAME_VALUE_RTP "rtp" // RTP communication
+#define AMEDIAMETRICS_PROP_CALLERNAME_VALUE_SOUNDPOOL "soundpool" // SoundPool
+#define AMEDIAMETRICS_PROP_CALLERNAME_VALUE_TONEGENERATOR "tonegenerator" // dial tones
+#define AMEDIAMETRICS_PROP_CALLERNAME_VALUE_UNKNOWN "unknown" // callerName not set
+
#endif // ANDROID_MEDIA_MEDIAMETRICSCONSTANTS_H
diff --git a/media/libmediametrics/include/media/MediaMetricsItem.h b/media/libmediametrics/include/media/MediaMetricsItem.h
index 8477f8d..303343f 100644
--- a/media/libmediametrics/include/media/MediaMetricsItem.h
+++ b/media/libmediametrics/include/media/MediaMetricsItem.h
@@ -27,12 +27,8 @@
#include <variant>
#include <binder/Parcel.h>
-#include <cutils/properties.h>
#include <utils/Errors.h>
-#include <utils/KeyedVector.h>
-#include <utils/RefBase.h>
-#include <utils/StrongPointer.h>
-#include <utils/Timers.h>
+#include <utils/Timers.h> // nsecs_t
namespace android {
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 42a0622..c0da0ce 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -2206,7 +2206,9 @@
targetSpeed,
mSelectedDeviceId);
}
-
+ // Set caller name so it can be logged in destructor.
+ // MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_MEDIA
+ t->setCallerName("media");
if ((t == 0) || (t->initCheck() != NO_ERROR)) {
ALOGE("Unable to create audio track");
delete newcbd;
diff --git a/media/libmediatranscoding/Android.bp b/media/libmediatranscoding/Android.bp
index 7468426..f948bd8 100644
--- a/media/libmediatranscoding/Android.bp
+++ b/media/libmediatranscoding/Android.bp
@@ -17,6 +17,7 @@
// AIDL interfaces of MediaTranscoding.
aidl_interface {
name: "mediatranscoding_aidl_interface",
+ unstable: true,
local_include_dir: "aidl",
srcs: [
"aidl/android/media/IMediaTranscodingService.aidl",
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 73d3a0b..63ab654 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -2053,17 +2053,34 @@
if (mIsVideo || mIsImage) {
// determine need for software renderer
bool usingSwRenderer = false;
- if (haveNativeWindow && mComponentName.startsWith("OMX.google.")) {
- usingSwRenderer = true;
- haveNativeWindow = false;
- (void)setPortMode(kPortIndexOutput, IOMX::kPortModePresetByteBuffer);
- } else if (haveNativeWindow && !storingMetadataInDecodedBuffers()) {
- err = setPortMode(kPortIndexOutput, IOMX::kPortModePresetANWBuffer);
- if (err != OK) {
- return err;
+ if (haveNativeWindow) {
+ bool requiresSwRenderer = false;
+ OMX_PARAM_U32TYPE param;
+ InitOMXParams(¶m);
+ param.nPortIndex = kPortIndexOutput;
+
+ status_t err = mOMXNode->getParameter(
+ (OMX_INDEXTYPE)OMX_IndexParamVideoAndroidRequiresSwRenderer,
+ ¶m, sizeof(param));
+
+ if (err == OK && param.nU32 == 1) {
+ requiresSwRenderer = true;
}
+
+ if (mComponentName.startsWith("OMX.google.") || requiresSwRenderer) {
+ usingSwRenderer = true;
+ haveNativeWindow = false;
+ (void)setPortMode(kPortIndexOutput, IOMX::kPortModePresetByteBuffer);
+ } else if (!storingMetadataInDecodedBuffers()) {
+ err = setPortMode(kPortIndexOutput, IOMX::kPortModePresetANWBuffer);
+ if (err != OK) {
+ return err;
+ }
+ }
+
}
+
if (encoder) {
err = setupVideoEncoder(mime, msg, outputFormat, inputFormat);
} else {
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index 00c5b40..4bc861e 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -108,6 +108,9 @@
selectedDeviceId,
selectedMicDirection,
selectedMicFieldDimension);
+ // Set caller name so it can be logged in destructor.
+ // MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_MEDIA
+ mRecord->setCallerName("media");
mInitCheck = mRecord->initCheck();
if (mInitCheck != OK) {
mRecord.clear();
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index b597583..11f2f38 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -199,6 +199,7 @@
void addResource(const MediaResourceParcel &resource);
void removeResource(const MediaResourceParcel &resource);
void removeClient();
+ void markClientForPendingRemoval();
bool reclaimResource(const std::vector<MediaResourceParcel> &resources);
private:
@@ -280,6 +281,14 @@
mService->removeClient(mPid, getId(mClient));
}
+void MediaCodec::ResourceManagerServiceProxy::markClientForPendingRemoval() {
+ Mutex::Autolock _l(mLock);
+ if (mService == nullptr) {
+ return;
+ }
+ mService->markClientForPendingRemoval(mPid, getId(mClient));
+}
+
bool MediaCodec::ResourceManagerServiceProxy::reclaimResource(
const std::vector<MediaResourceParcel> &resources) {
Mutex::Autolock _l(mLock);
@@ -297,6 +306,33 @@
////////////////////////////////////////////////////////////////////////////////
+class MediaCodec::ReleaseSurface {
+public:
+ ReleaseSurface() {
+ BufferQueue::createBufferQueue(&mProducer, &mConsumer);
+ mSurface = new Surface(mProducer, false /* controlledByApp */);
+ struct ConsumerListener : public BnConsumerListener {
+ void onFrameAvailable(const BufferItem&) override {}
+ void onBuffersReleased() override {}
+ void onSidebandStreamChanged() override {}
+ };
+ sp<ConsumerListener> listener{new ConsumerListener};
+ mConsumer->consumerConnect(listener, false);
+ mConsumer->setConsumerName(String8{"MediaCodec.release"});
+ }
+
+ const sp<Surface> &getSurface() {
+ return mSurface;
+ }
+
+private:
+ sp<IGraphicBufferProducer> mProducer;
+ sp<IGraphicBufferConsumer> mConsumer;
+ sp<Surface> mSurface;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
namespace {
enum {
@@ -1432,7 +1468,13 @@
status_t MediaCodec::release() {
sp<AMessage> msg = new AMessage(kWhatRelease, this);
+ sp<AMessage> response;
+ return PostAndAwaitResponse(msg, &response);
+}
+status_t MediaCodec::releaseAsync() {
+ sp<AMessage> msg = new AMessage(kWhatRelease, this);
+ msg->setInt32("async", 1);
sp<AMessage> response;
return PostAndAwaitResponse(msg, &response);
}
@@ -2600,7 +2642,9 @@
mResourceManagerProxy->removeClient();
- (new AMessage)->postReply(mReplyID);
+ if (mReplyID != nullptr) {
+ (new AMessage)->postReply(mReplyID);
+ }
break;
}
@@ -2987,6 +3031,26 @@
break;
}
+ int32_t async = 0;
+ if (msg->findInt32("async", &async) && async) {
+ if ((mState == CONFIGURED || mState == STARTED || mState == FLUSHED)
+ && mSurface != NULL) {
+ if (!mReleaseSurface) {
+ mReleaseSurface.reset(new ReleaseSurface);
+ }
+ status_t err = connectToSurface(mReleaseSurface->getSurface());
+ ALOGW_IF(err != OK, "error connecting to release surface: err = %d", err);
+ if (err == OK && !(mFlags & kFlagUsesSoftwareRenderer)) {
+ err = mCodec->setSurface(mReleaseSurface->getSurface());
+ ALOGW_IF(err != OK, "error setting release surface: err = %d", err);
+ }
+ if (err == OK) {
+ (void)disconnectFromSurface();
+ mSurface = mReleaseSurface->getSurface();
+ }
+ }
+ }
+
mReplyID = replyID;
setState(msg->what() == kWhatStop ? STOPPING : RELEASING);
@@ -2999,6 +3063,12 @@
pushBlankBuffersToNativeWindow(mSurface.get());
}
+ if (async) {
+ mResourceManagerProxy->markClientForPendingRemoval();
+ (new AMessage)->postReply(mReplyID);
+ mReplyID = 0;
+ }
+
break;
}
diff --git a/media/libstagefright/MediaExtractorFactory.cpp b/media/libstagefright/MediaExtractorFactory.cpp
index 94267a1..c6e753d 100644
--- a/media/libstagefright/MediaExtractorFactory.cpp
+++ b/media/libstagefright/MediaExtractorFactory.cpp
@@ -302,6 +302,12 @@
#endif
"/extractors", NULL, *newList);
+ RegisterExtractors("/system_ext/lib"
+#ifdef __LP64__
+ "64"
+#endif
+ "/extractors", NULL, *newList);
+
newList->sort(compareFunc);
gPlugins = newList;
diff --git a/media/libstagefright/codecs/amrwb/src/pvamrwbdecoder.cpp b/media/libstagefright/codecs/amrwb/src/pvamrwbdecoder.cpp
index ddc818e..c9c1cd4 100644
--- a/media/libstagefright/codecs/amrwb/src/pvamrwbdecoder.cpp
+++ b/media/libstagefright/codecs/amrwb/src/pvamrwbdecoder.cpp
@@ -337,7 +337,10 @@
st = (Decoder_State *) spd_state;
/* mode verification */
-
+ if (mode < 0 || mode >= NUM_OF_MODES)
+ {
+ return (-1);
+ }
nb_bits = AMR_WB_COMPRESSED[mode];
*frame_length = AMR_WB_PCM_FRAME;
diff --git a/media/libstagefright/codecs/mp3dec/src/pvmp3_framedecoder.cpp b/media/libstagefright/codecs/mp3dec/src/pvmp3_framedecoder.cpp
index df6cd03..a5c7f5e 100644
--- a/media/libstagefright/codecs/mp3dec/src/pvmp3_framedecoder.cpp
+++ b/media/libstagefright/codecs/mp3dec/src/pvmp3_framedecoder.cpp
@@ -659,20 +659,12 @@
huffcodetab *pHuff;
pVars = (tmp3dec_file *)pMem;
-
- pVars->num_channels = 0;
+ memset(pVars, 0, sizeof(*pVars));
pExt->totalNumberOfBitsUsed = 0;
pExt->inputBufferCurrentLength = 0;
pExt->inputBufferUsedLength = 0;
- pVars->mainDataStream.offset = 0;
-
- pv_memset((void*)pVars->mainDataBuffer,
- 0,
- BUFSIZE*sizeof(*pVars->mainDataBuffer));
-
-
pVars->inputStream.pBuffer = pExt->pInputBuffer;
/*
diff --git a/media/libstagefright/foundation/OpusHeader.cpp b/media/libstagefright/foundation/OpusHeader.cpp
index f5687e0..784e802 100644
--- a/media/libstagefright/foundation/OpusHeader.cpp
+++ b/media/libstagefright/foundation/OpusHeader.cpp
@@ -126,12 +126,20 @@
}
header->num_streams = data[kOpusHeaderNumStreamsOffset];
header->num_coupled = data[kOpusHeaderNumCoupledStreamsOffset];
- if (header->num_streams + header->num_coupled != header->channels) {
- ALOGV("Inconsistent channel mapping.");
+ if (header->num_coupled > header->num_streams ||
+ header->num_streams + header->num_coupled != header->channels) {
+ ALOGV("Inconsistent channel mapping, streams: %d coupled: %d channels: %d",
+ header->num_streams, header->num_coupled, header->channels);
return false;
}
- for (int i = 0; i < header->channels; ++i)
- header->stream_map[i] = data[kOpusHeaderStreamMapOffset + i];
+ for (int i = 0; i < header->channels; ++i) {
+ uint8_t value = data[kOpusHeaderStreamMapOffset + i];
+ if (value != 255 && value >= header->channels) {
+ ALOGV("Invalid channel mapping for index %i : %d", i, value);
+ return false;
+ }
+ header->stream_map[i] = value;
+ }
return true;
}
diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/AData.h b/media/libstagefright/foundation/include/media/stagefright/foundation/AData.h
index c6c12ff..87cf58b 100644
--- a/media/libstagefright/foundation/include/media/stagefright/foundation/AData.h
+++ b/media/libstagefright/foundation/include/media/stagefright/foundation/AData.h
@@ -28,6 +28,10 @@
#undef HIDE
#define HIDE __attribute__((visibility("hidden")))
+// The internals of AUnion cause problems with CFI
+#undef NO_CFI
+#define NO_CFI __attribute__((no_sanitize("cfi")))
+
namespace android {
/**
@@ -93,7 +97,7 @@
* \param args arbitrary arguments for constructor
*/
template<typename T, typename ...Args>
- inline static void emplace(size_t totalSize, T *addr, Args&&... args) {
+ inline static void NO_CFI emplace(size_t totalSize, T *addr, Args&&... args) {
new(addr)T(std::forward<Args>(args)...);
// clear slack space - this is not technically required
constexpr size_t size = sizeof(T);
@@ -160,7 +164,7 @@
template<
typename T, typename ...Args,
typename=typename std::enable_if<is_one_of<T, void, Ts...>::value>::type>
- inline void emplace(Args&&... args) {
+ inline void NO_CFI emplace(Args&&... args) {
_AUnion_impl::emplace(
sizeof(_type), reinterpret_cast<T*>(&mValue), std::forward<Args>(args)...);
}
diff --git a/media/libstagefright/id3/ID3.cpp b/media/libstagefright/id3/ID3.cpp
index 425468f..e97f6eb 100644
--- a/media/libstagefright/id3/ID3.cpp
+++ b/media/libstagefright/id3/ID3.cpp
@@ -107,20 +107,6 @@
}
}
-ID3::ID3(DataSourceBase *source, bool ignoreV1, off64_t offset)
- : mIsValid(false),
- mData(NULL),
- mSize(0),
- mFirstFrameOffset(0),
- mVersion(ID3_UNKNOWN),
- mRawSize(0) {
- mIsValid = parseV2(source, offset);
-
- if (!mIsValid && !ignoreV1) {
- mIsValid = parseV1(source);
- }
-}
-
ID3::ID3(const uint8_t *data, size_t size, bool ignoreV1)
: mIsValid(false),
mData(NULL),
@@ -247,44 +233,14 @@
return false;
}
- if (header.version_major == 4) {
- void *copy = malloc(size);
- if (copy == NULL) {
- free(mData);
- mData = NULL;
- ALOGE("b/24623447, no more memory");
- return false;
- }
-
- memcpy(copy, mData, size);
-
- bool success = removeUnsynchronizationV2_4(false /* iTunesHack */);
- if (!success) {
- memcpy(mData, copy, size);
- mSize = size;
-
- success = removeUnsynchronizationV2_4(true /* iTunesHack */);
-
- if (success) {
- ALOGV("Had to apply the iTunes hack to parse this ID3 tag");
- }
- }
-
- free(copy);
- copy = NULL;
-
- if (!success) {
- free(mData);
- mData = NULL;
-
- return false;
- }
- } else if (header.flags & 0x80) {
+ // first handle global unsynchronization
+ if (header.flags & 0x80) {
ALOGV("removing unsynchronization");
removeUnsynchronization();
}
+ // handle extended header, if present
mFirstFrameOffset = 0;
if (header.version_major == 3 && (header.flags & 0x40)) {
// Version 2.3 has an optional extended header.
@@ -296,6 +252,7 @@
return false;
}
+ // v2.3 does not have syncsafe integers
size_t extendedHeaderSize = U32_AT(&mData[0]);
if (extendedHeaderSize > SIZE_MAX - 4) {
free(mData);
@@ -367,6 +324,48 @@
mFirstFrameOffset = ext_size;
}
+ // Handle any v2.4 per-frame unsynchronization
+ // The id3 spec isn't clear about what should happen if the global
+ // unsynchronization flag is combined with per-frame unsynchronization,
+ // or whether that's even allowed, so this code assumes id3 writing
+ // tools do the right thing and not apply double-unsynchronization,
+ // but will honor the flags if they are set.
+ if (header.version_major == 4) {
+ void *copy = malloc(size);
+ if (copy == NULL) {
+ free(mData);
+ mData = NULL;
+ ALOGE("b/24623447, no more memory");
+ return false;
+ }
+
+ memcpy(copy, mData, size);
+
+ bool success = removeUnsynchronizationV2_4(false /* iTunesHack */);
+ if (!success) {
+ memcpy(mData, copy, size);
+ mSize = size;
+
+ success = removeUnsynchronizationV2_4(true /* iTunesHack */);
+
+ if (success) {
+ ALOGV("Had to apply the iTunes hack to parse this ID3 tag");
+ }
+ }
+
+ free(copy);
+ copy = NULL;
+
+ if (!success) {
+ free(mData);
+ mData = NULL;
+
+ return false;
+ }
+ }
+
+
+
if (header.version_major == 2) {
mVersion = ID3_V2_2;
} else if (header.version_major == 3) {
@@ -411,7 +410,7 @@
bool ID3::removeUnsynchronizationV2_4(bool iTunesHack) {
size_t oldSize = mSize;
- size_t offset = 0;
+ size_t offset = mFirstFrameOffset;
while (mSize >= 10 && offset <= mSize - 10) {
if (!memcmp(&mData[offset], "\0\0\0\0", 4)) {
break;
@@ -445,7 +444,7 @@
}
if ((flags & 2) && (dataSize >= 2)) {
- // This file has "unsynchronization", so we have to replace occurrences
+ // This frame has "unsynchronization", so we have to replace occurrences
// of 0xff 0x00 with just 0xff in order to get the real data.
size_t readOffset = offset + 11;
diff --git a/media/libstagefright/id3/test/ID3Test.cpp b/media/libstagefright/id3/test/ID3Test.cpp
index a8f1470..cd5cd9e 100644
--- a/media/libstagefright/id3/test/ID3Test.cpp
+++ b/media/libstagefright/id3/test/ID3Test.cpp
@@ -24,6 +24,7 @@
#include <datasource/FileSource.h>
#include <media/stagefright/foundation/hexdump.h>
+#include <media/MediaExtractorPluginHelper.h>
#include <ID3.h>
#include "ID3TestEnvironment.h"
@@ -42,7 +43,8 @@
string path = gEnv->getRes() + GetParam();
sp<FileSource> file = new FileSource(path.c_str());
ASSERT_EQ(file->initCheck(), (status_t)OK) << "File initialization failed! \n";
- ID3 tag(file.get());
+ DataSourceHelper helper(file->wrap());
+ ID3 tag(&helper);
ASSERT_TRUE(tag.isValid()) << "No valid ID3 tag found for " << path.c_str() << "\n";
ID3::Iterator it(tag, nullptr);
@@ -61,7 +63,8 @@
sp<android::FileSource> file = new FileSource(path.c_str());
ASSERT_EQ(file->initCheck(), (status_t)OK) << "File initialization failed! \n";
- ID3 tag(file.get());
+ DataSourceHelper helper(file->wrap());
+ ID3 tag(&helper);
ASSERT_TRUE(tag.isValid()) << "No valid ID3 tag found for " << path.c_str() << "\n";
ASSERT_TRUE(tag.version() >= versionNumber)
<< "Expected version: " << tag.version() << " Found version: " << versionNumber;
@@ -73,7 +76,8 @@
sp<android::FileSource> file = new FileSource(path.c_str());
ASSERT_EQ(file->initCheck(), (status_t)OK) << "File initialization failed! \n";
- ID3 tag(file.get());
+ DataSourceHelper helper(file->wrap());
+ ID3 tag(&helper);
ASSERT_TRUE(tag.isValid()) << "No valid ID3 tag found for " << path.c_str() << "\n";
int countTextFrames = 0;
ID3::Iterator it(tag, nullptr);
@@ -99,7 +103,8 @@
sp<android::FileSource> file = new FileSource(path.c_str());
ASSERT_EQ(file->initCheck(), (status_t)OK) << "File initialization failed! \n";
- ID3 tag(file.get());
+ DataSourceHelper helper(file->wrap());
+ ID3 tag(&helper);
ASSERT_TRUE(tag.isValid()) << "No valid ID3 tag found for " << path.c_str() << "\n";
size_t dataSize;
String8 mime;
@@ -124,7 +129,8 @@
sp<android::FileSource> file = new FileSource(path.c_str());
ASSERT_EQ(file->initCheck(), (status_t)OK) << "File initialization failed! \n";
- ID3 tag(file.get());
+ DataSourceHelper helper(file->wrap());
+ ID3 tag(&helper);
ASSERT_TRUE(tag.isValid()) << "No valid ID3 tag found for " << path.c_str() << "\n";
int count = 0;
ID3::Iterator it(tag, nullptr);
diff --git a/media/libstagefright/id3/testid3.cpp b/media/libstagefright/id3/testid3.cpp
index 9984d85..5cd51cf 100644
--- a/media/libstagefright/id3/testid3.cpp
+++ b/media/libstagefright/id3/testid3.cpp
@@ -24,6 +24,7 @@
#include <binder/ProcessState.h>
#include <datasource/FileSource.h>
#include <media/stagefright/foundation/ADebug.h>
+#include <media/MediaExtractorPluginHelper.h>
#define MAXPATHLEN 256
@@ -72,7 +73,8 @@
sp<FileSource> file = new FileSource(path);
CHECK_EQ(file->initCheck(), (status_t)OK);
- ID3 tag(file.get());
+ DataSourceHelper helper(file->wrap());
+ ID3 tag(&helper);
if (!tag.isValid()) {
printf("FAIL %s\n", path);
} else {
diff --git a/media/libstagefright/include/ID3.h b/media/libstagefright/include/ID3.h
index 2843a7a..0be5896 100644
--- a/media/libstagefright/include/ID3.h
+++ b/media/libstagefright/include/ID3.h
@@ -37,7 +37,6 @@
};
explicit ID3(DataSourceHelper *source, bool ignoreV1 = false, off64_t offset = 0);
- explicit ID3(DataSourceBase *source, bool ignoreV1 = false, off64_t offset = 0);
ID3(const uint8_t *data, size_t size, bool ignoreV1 = false);
~ID3();
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index 022c48e..63a9dad 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -139,6 +139,8 @@
// object.
status_t release();
+ status_t releaseAsync();
+
status_t flush();
status_t queueInputBuffer(
@@ -509,6 +511,9 @@
// when low latency is on
int64_t mInputBufferCounter; // number of input buffers queued since last reset/flush
+ class ReleaseSurface;
+ std::unique_ptr<ReleaseSurface> mReleaseSurface;
+
sp<BatteryChecker> mBatteryChecker;
void statsBufferSent(int64_t presentationUs);
diff --git a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
index bfdc9e7..178d334 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
@@ -790,6 +790,8 @@
constexpr char KEY_OPERATING_RATE[] = "operating-rate";
constexpr char KEY_OUTPUT_REORDER_DEPTH[] = "output-reorder-depth";
constexpr char KEY_PCM_ENCODING[] = "pcm-encoding";
+constexpr char KEY_PIXEL_ASPECT_RATIO_HEIGHT[] = "sar-height";
+constexpr char KEY_PIXEL_ASPECT_RATIO_WIDTH[] = "sar-width";
constexpr char KEY_PREPEND_HEADERS_TO_SYNC_FRAMES[] = "prepend-sps-pps-to-idr-frames";
constexpr char KEY_PRIORITY[] = "priority";
constexpr char KEY_PROFILE[] = "profile";
diff --git a/media/libstagefright/omx/SoftOMXPlugin.cpp b/media/libstagefright/omx/SoftOMXPlugin.cpp
index a720bc9..8c186c9 100644
--- a/media/libstagefright/omx/SoftOMXPlugin.cpp
+++ b/media/libstagefright/omx/SoftOMXPlugin.cpp
@@ -34,12 +34,7 @@
const char *mRole;
} kComponents[] = {
- // two choices for aac decoding.
- // configurable in media/libstagefright/data/media_codecs_google_audio.xml
- // default implementation
{ "OMX.google.aac.decoder", "aacdec", "audio_decoder.aac" },
- // alternate implementation
- { "OMX.google.xaac.decoder", "xaacdec", "audio_decoder.aac" },
{ "OMX.google.aac.encoder", "aacenc", "audio_encoder.aac" },
{ "OMX.google.amrnb.decoder", "amrdec", "audio_decoder.amrnb" },
{ "OMX.google.amrnb.encoder", "amrnbenc", "audio_encoder.amrnb" },
diff --git a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
index 9df3508..7bd33c1 100644
--- a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
+++ b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
@@ -203,6 +203,14 @@
unsigned mSerial;
};
+bool AMPEG4ElementaryAssembler::initCheck() {
+ if(mSizeLength == 0 || mIndexLength == 0 || mIndexDeltaLength == 0) {
+ android_errorWriteLog(0x534e4554, "124777537");
+ return false;
+ }
+ return true;
+}
+
ARTPAssembler::AssemblyStatus AMPEG4ElementaryAssembler::addPacket(
const sp<ARTPSource> &source) {
List<sp<ABuffer> > *queue = source->queue();
@@ -250,12 +258,16 @@
} else {
// hexdump(buffer->data(), buffer->size());
if (buffer->size() < 2) {
+ android_errorWriteLog(0x534e4554, "124783982");
+ queue->erase(queue->begin());
return MALFORMED_PACKET;
}
unsigned AU_headers_length = U16_AT(buffer->data()); // in bits
if (buffer->size() < 2 + (AU_headers_length + 7) / 8) {
+ android_errorWriteLog(0x534e4554, "124783982");
+ queue->erase(queue->begin());
return MALFORMED_PACKET;
}
@@ -359,6 +371,8 @@
return MALFORMED_PACKET;
}
if (buffer->size() < offset + header.mSize) {
+ android_errorWriteLog(0x534e4554, "124783982");
+ queue->erase(queue->begin());
return MALFORMED_PACKET;
}
diff --git a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.h b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.h
index 693fca5..57b7d71 100644
--- a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.h
+++ b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.h
@@ -36,6 +36,7 @@
AMPEG4ElementaryAssembler(
const sp<AMessage> ¬ify, const AString &desc,
const AString ¶ms);
+ virtual bool initCheck();
protected:
virtual ~AMPEG4ElementaryAssembler();
diff --git a/media/libstagefright/rtsp/ARTPAssembler.h b/media/libstagefright/rtsp/ARTPAssembler.h
index 7c147be..4082d4c 100644
--- a/media/libstagefright/rtsp/ARTPAssembler.h
+++ b/media/libstagefright/rtsp/ARTPAssembler.h
@@ -39,6 +39,7 @@
void onPacketReceived(const sp<ARTPSource> &source);
virtual void onByeReceived() = 0;
+ virtual bool initCheck() { return true; }
protected:
virtual AssemblyStatus assembleMore(const sp<ARTPSource> &source) = 0;
diff --git a/media/libstagefright/rtsp/ARTPSource.cpp b/media/libstagefright/rtsp/ARTPSource.cpp
index 4afa6f4..f5f8128 100644
--- a/media/libstagefright/rtsp/ARTPSource.cpp
+++ b/media/libstagefright/rtsp/ARTPSource.cpp
@@ -82,6 +82,10 @@
} else {
TRESPASS();
}
+
+ if (mAssembler != NULL && !mAssembler->initCheck()) {
+ mAssembler.clear();
+ }
}
static uint32_t AbsDiff(uint32_t seq1, uint32_t seq2) {
@@ -89,7 +93,7 @@
}
void ARTPSource::processRTPPacket(const sp<ABuffer> &buffer) {
- if (queuePacket(buffer) && mAssembler != NULL) {
+ if (mAssembler != NULL && queuePacket(buffer)) {
mAssembler->onPacketReceived(this);
}
}
@@ -171,7 +175,9 @@
}
void ARTPSource::byeReceived() {
- mAssembler->onByeReceived();
+ if (mAssembler != NULL) {
+ mAssembler->onByeReceived();
+ }
}
void ARTPSource::addFIR(const sp<ABuffer> &buffer) {
diff --git a/media/libstagefright/tests/extractorFactory/Android.bp b/media/libstagefright/tests/extractorFactory/Android.bp
new file mode 100644
index 0000000..e3e61d7
--- /dev/null
+++ b/media/libstagefright/tests/extractorFactory/Android.bp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+cc_test {
+ name: "ExtractorFactoryTest",
+ gtest: true,
+
+ srcs: [
+ "ExtractorFactoryTest.cpp",
+ ],
+
+ shared_libs: [
+ "liblog",
+ "libbase",
+ "libutils",
+ "libmedia",
+ "libbinder",
+ "libcutils",
+ "libdl_android",
+ "libdatasource",
+ "libmediametrics",
+ ],
+
+ static_libs: [
+ "libstagefright",
+ "libstagefright_foundation",
+ ],
+
+ include_dirs: [
+ "frameworks/av/media/libstagefright",
+ ],
+
+ // TODO: (b/150181583)
+ compile_multilib: "first",
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+ sanitize: {
+ cfi: true,
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ },
+}
diff --git a/media/libstagefright/tests/extractorFactory/AndroidTest.xml b/media/libstagefright/tests/extractorFactory/AndroidTest.xml
new file mode 100644
index 0000000..3aa6392
--- /dev/null
+++ b/media/libstagefright/tests/extractorFactory/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Test module config for extractor factory unit tests">
+ <option name="test-suite-tag" value="ExtractorFactoryTest" />
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="ExtractorFactoryTest->/data/local/tmp/ExtractorFactoryTest" />
+ <option name="push-file"
+ key="https://storage.googleapis.com/android_media/frameworks/av/media/extractors/tests/extractor.zip?unzip=true"
+ value="/data/local/tmp/ExtractorFactoryTestRes/" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="ExtractorFactoryTest" />
+ <option name="native-test-flag" value="-P /data/local/tmp/ExtractorFactoryTestRes/" />
+ </test>
+</configuration>
diff --git a/media/libstagefright/tests/extractorFactory/ExtractorFactoryTest.cpp b/media/libstagefright/tests/extractorFactory/ExtractorFactoryTest.cpp
new file mode 100644
index 0000000..d155caa
--- /dev/null
+++ b/media/libstagefright/tests/extractorFactory/ExtractorFactoryTest.cpp
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ExtractorFactoryTest"
+#include <utils/Log.h>
+
+#include <binder/ProcessState.h>
+
+#include <datasource/FileSource.h>
+#include <media/stagefright/MediaExtractorFactory.h>
+#include <media/stagefright/foundation/MediaDefs.h>
+
+#include "ExtractorFactoryTestEnvironment.h"
+
+#define OUTPUT_FILE_NAME "/data/local/tmp/exFactoryLogs"
+
+using namespace android;
+
+static ExtractorFactoryTestEnvironment *gEnv = nullptr;
+
+class ExtractorFactoryTest : public ::testing::TestWithParam<pair<string, string>> {
+ public:
+ ExtractorFactoryTest() : mDataSource(nullptr), mExtractor(nullptr) {}
+
+ ~ExtractorFactoryTest() {
+ if (mDataSource) {
+ mDataSource.clear();
+ mDataSource = nullptr;
+ }
+ if (mExtractor) {
+ mExtractor.clear();
+ mExtractor = nullptr;
+ }
+ }
+
+ int32_t createDataSource(string inputFileName);
+ int32_t createExtractor(bool createFromService, string inputMime);
+
+ sp<DataSource> mDataSource;
+ sp<IMediaExtractor> mExtractor;
+};
+
+int32_t ExtractorFactoryTest::createDataSource(string inputFileName) {
+ FILE *mInputFp = fopen(inputFileName.c_str(), "rb");
+ if (!mInputFp) {
+ ALOGE("Unable to open input file : %s for reading", inputFileName.c_str());
+ return -1;
+ }
+ struct stat buf;
+ int32_t status = stat(inputFileName.c_str(), &buf);
+ if (status != 0) {
+ ALOGE("Failed to read file properties for input file : %s", inputFileName.c_str());
+ return -1;
+ }
+ int32_t fd = fileno(mInputFp);
+ if (fd < 0) {
+ ALOGE("Invalid file descriptor for input file : %s", inputFileName.c_str());
+ return -1;
+ }
+ mDataSource = new FileSource(dup(fd), 0, buf.st_size);
+ if (!mDataSource) return -1;
+ return 0;
+}
+
+int32_t ExtractorFactoryTest::createExtractor(bool createFromService, string inputMime) {
+ ALOGV("Creating extractor for mime : %s", inputMime.c_str());
+ if (createFromService) {
+ mExtractor = MediaExtractorFactory::CreateFromService(mDataSource, inputMime.c_str());
+ } else {
+ mExtractor = MediaExtractorFactory::Create(mDataSource);
+ }
+ if (mExtractor == nullptr) return -1;
+ return 0;
+}
+
+TEST_F(ExtractorFactoryTest, ListExtractorsTest) {
+ MediaExtractorFactory::LoadExtractors();
+ vector<std::string> supportedTypes = MediaExtractorFactory::getSupportedTypes();
+ ASSERT_GT(supportedTypes.size(), 0) << " MediaExtractorFactory doesn't suuport any extractor";
+
+ FILE *outputLog = fopen(OUTPUT_FILE_NAME, "wb");
+ ASSERT_NE(outputLog, nullptr) << "Unable to open output file - " << OUTPUT_FILE_NAME
+ << " for writing";
+
+ int32_t fd = fileno(outputLog);
+ ASSERT_GE(fd, 0);
+
+ Vector<String16> args;
+ int32_t status = MediaExtractorFactory::dump(fd, args);
+ ASSERT_EQ(status, OK) << "MediaExtractorFactory dump failed";
+ fclose(outputLog);
+}
+
+TEST_P(ExtractorFactoryTest, ExtractorFactoryApiTest) {
+ string inputMime = GetParam().second;
+ string inputFileName = gEnv->getRes() + GetParam().first;
+
+ MediaExtractorFactory::LoadExtractors();
+ bool createMode[] = {true, false};
+ for (bool createFromService : createMode) {
+ int32_t status = createDataSource(inputFileName);
+ ASSERT_EQ(status, 0) << "create data source failed";
+
+ status = createExtractor(createFromService, inputMime);
+ ASSERT_EQ(status, 0) << "Extractor creation failed for input: " << inputFileName;
+
+ int32_t numTracks = mExtractor->countTracks();
+ ASSERT_GT(numTracks, 0) << "Extractor didn't find any track for the given clip";
+
+ sp<MetaData> meta = mExtractor->getMetaData();
+ ASSERT_NE(meta, nullptr) << "getMetaData returned null";
+
+ const char *mime;
+ bool valueFound = meta->findCString(kKeyMIMEType, &mime);
+ ASSERT_TRUE(valueFound) << "Extractor did not provide MIME type";
+ ASSERT_EQ(mime, inputMime) << "Extractor factory returned invalid mime type";
+ mExtractor.clear();
+ mDataSource.clear();
+ }
+}
+
+// TODO: (b/150111966)
+// Replace mime strings with appropriate definitions
+INSTANTIATE_TEST_SUITE_P(
+ ExtractorFactoryTestAll, ExtractorFactoryTest,
+ ::testing::Values(make_pair("loudsoftaac.aac", MEDIA_MIMETYPE_AUDIO_AAC_ADTS),
+ make_pair("testamr.amr", "audio/amr"),
+ make_pair("amrwb.wav", MEDIA_MIMETYPE_AUDIO_AMR_WB),
+ make_pair("john_cage.ogg", MEDIA_MIMETYPE_CONTAINER_OGG),
+ make_pair("monotestgsm.wav", MEDIA_MIMETYPE_CONTAINER_WAV),
+ make_pair("segment000001.ts", MEDIA_MIMETYPE_CONTAINER_MPEG2TS),
+ make_pair("sinesweepflac.flac", MEDIA_MIMETYPE_AUDIO_FLAC),
+ make_pair("testopus.opus", MEDIA_MIMETYPE_CONTAINER_OGG),
+ make_pair("midi_a.mid", MEDIA_MIMETYPE_AUDIO_MIDI),
+ make_pair("sinesweepvorbis.mkv", MEDIA_MIMETYPE_CONTAINER_MATROSKA),
+ make_pair("sinesweepoggmp4.mp4", "audio/mp4"),
+ make_pair("sinesweepmp3lame.mp3", MEDIA_MIMETYPE_AUDIO_MPEG),
+ make_pair("swirl_144x136_vp9.webm", "video/webm"),
+ make_pair("swirl_144x136_vp8.webm", "video/webm"),
+ make_pair("swirl_132x130_mpeg4.mp4", MEDIA_MIMETYPE_CONTAINER_MPEG4)));
+
+int main(int argc, char **argv) {
+ ProcessState::self()->startThreadPool();
+ gEnv = new ExtractorFactoryTestEnvironment();
+ ::testing::AddGlobalTestEnvironment(gEnv);
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = gEnv->initFromOptions(argc, argv);
+ if (status == 0) {
+ status = RUN_ALL_TESTS();
+ ALOGV("Test result = %d\n", status);
+ }
+ return status;
+}
diff --git a/media/libstagefright/tests/extractorFactory/ExtractorFactoryTestEnvironment.h b/media/libstagefright/tests/extractorFactory/ExtractorFactoryTestEnvironment.h
new file mode 100644
index 0000000..0fad4d3
--- /dev/null
+++ b/media/libstagefright/tests/extractorFactory/ExtractorFactoryTestEnvironment.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __EXTRACTOR_FACTORY_TEST_ENVIRONMENT_H__
+#define __EXTRACTOR_FACTORY_TEST_ENVIRONMENT_H__
+
+#include <gtest/gtest.h>
+
+#include <getopt.h>
+
+using namespace std;
+
+class ExtractorFactoryTestEnvironment : public ::testing::Environment {
+ public:
+ ExtractorFactoryTestEnvironment() : res("/data/local/tmp/") {}
+
+ // Parses the command line arguments
+ int initFromOptions(int argc, char **argv);
+
+ void setRes(const char *_res) { res = _res; }
+
+ const string getRes() const { return res; }
+
+ private:
+ string res;
+};
+
+int ExtractorFactoryTestEnvironment::initFromOptions(int argc, char **argv) {
+ static struct option options[] = {{"res", required_argument, 0, 'P'}, {0, 0, 0, 0}};
+
+ while (true) {
+ int index = 0;
+ int c = getopt_long(argc, argv, "P:", options, &index);
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 'P':
+ setRes(optarg);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (optind < argc) {
+ fprintf(stderr,
+ "unrecognized option: %s\n\n"
+ "usage: %s <gtest options> <test options>\n\n"
+ "test options are:\n\n"
+ "-P, --path: Resource files directory location\n",
+ argv[optind ?: 1], argv[0]);
+ return 2;
+ }
+ return 0;
+}
+
+#endif // __EXTRACTOR_FACTORY_TEST_ENVIRONMENT_H__
diff --git a/media/libstagefright/tests/extractorFactory/README.md b/media/libstagefright/tests/extractorFactory/README.md
new file mode 100644
index 0000000..aaa71aa
--- /dev/null
+++ b/media/libstagefright/tests/extractorFactory/README.md
@@ -0,0 +1,37 @@
+## Media Testing ##
+---
+#### Writer :
+The Writer Test Suite validates the writers available in libstagefright.
+
+Run the following steps to build the test suite:
+```
+mmm frameworks/av/media/libstagefright/tests/writer/
+```
+
+The 32-bit binaries will be created in the following path : ${OUT}/data/nativetest/
+The 64-bit binaries will be created in the following path : ${OUT}/data/nativetest64/
+
+To test 64-bit binary push binaries from nativetest64.
+
+adb push ${OUT}/data/nativetest64/ExtractorFactoryTest/ExtractorFactoryTest /data/local/tmp/
+
+To test 32-bit binary push binaries from nativetest.
+
+adb push ${OUT}/data/nativetest/ExtractorFactoryTest/ExtractorFactoryTest /data/local/tmp/
+
+The resource file for the tests is taken from [here](https://storage.googleapis.com/android_media/frameworks/av/media/extractors/tests/extractor.zip).
+Download, unzip and push these files into device for testing.
+
+```
+adb push extractor /data/local/tmp/
+```
+
+usage: ExtractorFactoryTest -P \<path_to_res_folder\>
+```
+adb shell /data/local/tmp/ExtractorFactoryTest -P /data/local/tmp/extractor/
+```
+Alternatively, the test can also be run using atest command.
+
+```
+atest ExtractorFactoryTest -- --enable-module-dynamic-download=true
+```
diff --git a/media/libstagefright/timedtext/TextDescriptions.cpp b/media/libstagefright/timedtext/TextDescriptions.cpp
index 6c94754..2c2d11d 100644
--- a/media/libstagefright/timedtext/TextDescriptions.cpp
+++ b/media/libstagefright/timedtext/TextDescriptions.cpp
@@ -504,7 +504,7 @@
return OK;
}
- parcel->write(tmpData, len);
+ parcel->writeByteArray(len, tmpData);
tmpData += len;
subChunkRemaining -= len;
}
diff --git a/media/libstagefright/xmlparser/vts/Android.bp b/media/libstagefright/xmlparser/vts/Android.bp
index 3f93e9e..132ce82 100644
--- a/media/libstagefright/xmlparser/vts/Android.bp
+++ b/media/libstagefright/xmlparser/vts/Android.bp
@@ -30,4 +30,12 @@
"-Wall",
"-Werror",
],
+ data: [
+ ":media_codecs",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts"
+ ],
+ test_config: "vts_mediaCodecs_validate_test.xml",
}
diff --git a/media/libstagefright/xmlparser/vts/vts_mediaCodecs_validate_test.xml b/media/libstagefright/xmlparser/vts/vts_mediaCodecs_validate_test.xml
new file mode 100644
index 0000000..fbb7cc6
--- /dev/null
+++ b/media/libstagefright/xmlparser/vts/vts_mediaCodecs_validate_test.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs vts_mediaCodecs_validate_test.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="media_codecs.xsd->/data/local/tmp/media_codecs.xsd" />
+ <option name="push" value="vts_mediaCodecs_validate_test->/data/local/tmp/vts_mediaCodecs_validate_test" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="vts_mediaCodecs_validate_test" />
+ </test>
+</configuration>
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 03c16f3..de7ae40 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -135,7 +135,7 @@
static sp<os::IExternalVibratorService> getExternalVibratorService() {
if (sExternalVibratorService == 0) {
- sp <IBinder> binder = defaultServiceManager()->getService(
+ sp<IBinder> binder = defaultServiceManager()->getService(
String16("external_vibrator_service"));
if (binder != 0) {
sExternalVibratorService =
@@ -411,6 +411,7 @@
status_t AudioFlinger::addEffectToHal(audio_port_handle_t deviceId,
audio_module_handle_t hwModuleId, sp<EffectHalInterface> effect) {
+ AutoMutex lock(mHardwareLock);
AudioHwDevice *audioHwDevice = mAudioHwDevs.valueFor(hwModuleId);
if (audioHwDevice == nullptr) {
return NO_INIT;
@@ -420,6 +421,7 @@
status_t AudioFlinger::removeEffectFromHal(audio_port_handle_t deviceId,
audio_module_handle_t hwModuleId, sp<EffectHalInterface> effect) {
+ AutoMutex lock(mHardwareLock);
AudioHwDevice *audioHwDevice = mAudioHwDevs.valueFor(hwModuleId);
if (audioHwDevice == nullptr) {
return NO_INIT;
@@ -439,6 +441,7 @@
{
// if module is 0, the request comes from an old policy manager and we should load
// well known modules
+ AutoMutex lock(mHardwareLock);
if (module == 0) {
ALOGW("findSuitableHwDev_l() loading well know audio hw modules");
for (size_t i = 0; i < arraysize(audio_interfaces); i++) {
@@ -1079,17 +1082,18 @@
mMasterVolume = value;
// Set master volume in the HALs which support it.
- for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
+ {
AutoMutex lock(mHardwareLock);
- AudioHwDevice *dev = mAudioHwDevs.valueAt(i);
+ for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
+ AudioHwDevice *dev = mAudioHwDevs.valueAt(i);
- mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME;
- if (dev->canSetMasterVolume()) {
- dev->hwDevice()->setMasterVolume(value);
+ mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME;
+ if (dev->canSetMasterVolume()) {
+ dev->hwDevice()->setMasterVolume(value);
+ }
+ mHardwareStatus = AUDIO_HW_IDLE;
}
- mHardwareStatus = AUDIO_HW_IDLE;
}
-
// Now set the master volume in each playback thread. Playback threads
// assigned to HALs which do not have master volume support will apply
// master volume during the mix operation. Threads with HALs which do
@@ -1156,6 +1160,9 @@
{ // scope for the lock
AutoMutex lock(mHardwareLock);
+ if (mPrimaryHardwareDev == nullptr) {
+ return INVALID_OPERATION;
+ }
sp<DeviceHalInterface> dev = mPrimaryHardwareDev->hwDevice();
mHardwareStatus = AUDIO_HW_SET_MODE;
ret = dev->setMode(mode);
@@ -1185,6 +1192,9 @@
}
AutoMutex lock(mHardwareLock);
+ if (mPrimaryHardwareDev == nullptr) {
+ return INVALID_OPERATION;
+ }
sp<DeviceHalInterface> primaryDev = mPrimaryHardwareDev->hwDevice();
if (primaryDev == nullptr) {
ALOGW("%s: no primary HAL device", __func__);
@@ -1210,6 +1220,9 @@
return false;
}
AutoMutex lock(mHardwareLock);
+ if (mPrimaryHardwareDev == nullptr) {
+ return false;
+ }
sp<DeviceHalInterface> primaryDev = mPrimaryHardwareDev->hwDevice();
if (primaryDev == nullptr) {
ALOGW("%s: no primary HAL device", __func__);
@@ -1252,15 +1265,17 @@
mMasterMute = muted;
// Set master mute in the HALs which support it.
- for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
+ {
AutoMutex lock(mHardwareLock);
- AudioHwDevice *dev = mAudioHwDevs.valueAt(i);
+ for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
+ AudioHwDevice *dev = mAudioHwDevs.valueAt(i);
- mHardwareStatus = AUDIO_HW_SET_MASTER_MUTE;
- if (dev->canSetMasterMute()) {
- dev->hwDevice()->setMasterMute(muted);
+ mHardwareStatus = AUDIO_HW_SET_MASTER_MUTE;
+ if (dev->canSetMasterMute()) {
+ dev->hwDevice()->setMasterMute(muted);
+ }
+ mHardwareStatus = AUDIO_HW_IDLE;
}
- mHardwareStatus = AUDIO_HW_IDLE;
}
// Now set the master mute in each playback thread. Playback threads
@@ -1591,16 +1606,13 @@
if (ioHandle == AUDIO_IO_HANDLE_NONE) {
String8 out_s8;
+ AutoMutex lock(mHardwareLock);
for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
String8 s;
- status_t result;
- {
- AutoMutex lock(mHardwareLock);
mHardwareStatus = AUDIO_HW_GET_PARAMETER;
sp<DeviceHalInterface> dev = mAudioHwDevs.valueAt(i)->hwDevice();
- result = dev->getParameters(keys, &s);
+ status_t result = dev->getParameters(keys, &s);
mHardwareStatus = AUDIO_HW_IDLE;
- }
if (result == OK) out_s8 += s;
}
return out_s8;
@@ -1633,6 +1645,9 @@
}
AutoMutex lock(mHardwareLock);
+ if (mPrimaryHardwareDev == nullptr) {
+ return 0;
+ }
mHardwareStatus = AUDIO_HW_GET_INPUT_BUFFER_SIZE;
sp<DeviceHalInterface> dev = mPrimaryHardwareDev->hwDevice();
@@ -1718,6 +1733,9 @@
}
AutoMutex lock(mHardwareLock);
+ if (mPrimaryHardwareDev == nullptr) {
+ return INVALID_OPERATION;
+ }
sp<DeviceHalInterface> dev = mPrimaryHardwareDev->hwDevice();
mHardwareStatus = AUDIO_HW_SET_VOICE_VOLUME;
ret = dev->setVoiceVolume(value);
@@ -2126,10 +2144,11 @@
return AUDIO_MODULE_HANDLE_NONE;
}
Mutex::Autolock _l(mLock);
+ AutoMutex lock(mHardwareLock);
return loadHwModule_l(name);
}
-// loadHwModule_l() must be called with AudioFlinger::mLock held
+// loadHwModule_l() must be called with AudioFlinger::mLock and AudioFlinger::mHardwareLock held
audio_module_handle_t AudioFlinger::loadHwModule_l(const char *name)
{
for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
@@ -2161,44 +2180,49 @@
// master mute and volume settings.
AudioHwDevice::Flags flags = static_cast<AudioHwDevice::Flags>(0);
- { // scope for auto-lock pattern
- AutoMutex lock(mHardwareLock);
-
- if (0 == mAudioHwDevs.size()) {
- mHardwareStatus = AUDIO_HW_GET_MASTER_VOLUME;
- float mv;
- if (OK == dev->getMasterVolume(&mv)) {
- mMasterVolume = mv;
- }
-
- mHardwareStatus = AUDIO_HW_GET_MASTER_MUTE;
- bool mm;
- if (OK == dev->getMasterMute(&mm)) {
- mMasterMute = mm;
- }
+ if (0 == mAudioHwDevs.size()) {
+ mHardwareStatus = AUDIO_HW_GET_MASTER_VOLUME;
+ float mv;
+ if (OK == dev->getMasterVolume(&mv)) {
+ mMasterVolume = mv;
}
- mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME;
- if (OK == dev->setMasterVolume(mMasterVolume)) {
- flags = static_cast<AudioHwDevice::Flags>(flags |
- AudioHwDevice::AHWD_CAN_SET_MASTER_VOLUME);
+ mHardwareStatus = AUDIO_HW_GET_MASTER_MUTE;
+ bool mm;
+ if (OK == dev->getMasterMute(&mm)) {
+ mMasterMute = mm;
}
-
- mHardwareStatus = AUDIO_HW_SET_MASTER_MUTE;
- if (OK == dev->setMasterMute(mMasterMute)) {
- flags = static_cast<AudioHwDevice::Flags>(flags |
- AudioHwDevice::AHWD_CAN_SET_MASTER_MUTE);
- }
-
- mHardwareStatus = AUDIO_HW_IDLE;
}
+
+ mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME;
+ if (OK == dev->setMasterVolume(mMasterVolume)) {
+ flags = static_cast<AudioHwDevice::Flags>(flags |
+ AudioHwDevice::AHWD_CAN_SET_MASTER_VOLUME);
+ }
+
+ mHardwareStatus = AUDIO_HW_SET_MASTER_MUTE;
+ if (OK == dev->setMasterMute(mMasterMute)) {
+ flags = static_cast<AudioHwDevice::Flags>(flags |
+ AudioHwDevice::AHWD_CAN_SET_MASTER_MUTE);
+ }
+
+ mHardwareStatus = AUDIO_HW_IDLE;
+
if (strcmp(name, AUDIO_HARDWARE_MODULE_ID_MSD) == 0) {
// An MSD module is inserted before hardware modules in order to mix encoded streams.
flags = static_cast<AudioHwDevice::Flags>(flags | AudioHwDevice::AHWD_IS_INSERT);
}
audio_module_handle_t handle = (audio_module_handle_t) nextUniqueId(AUDIO_UNIQUE_ID_USE_MODULE);
- mAudioHwDevs.add(handle, new AudioHwDevice(handle, name, dev, flags));
+ AudioHwDevice *audioDevice = new AudioHwDevice(handle, name, dev, flags);
+ if (strcmp(name, AUDIO_HARDWARE_MODULE_ID_PRIMARY) == 0) {
+ mPrimaryHardwareDev = audioDevice;
+ mHardwareStatus = AUDIO_HW_SET_MODE;
+ mPrimaryHardwareDev->hwDevice()->setMode(mMode);
+ mHardwareStatus = AUDIO_HW_IDLE;
+ }
+
+ mAudioHwDevs.add(handle, audioDevice);
ALOGI("loadHwModule() Loaded %s audio interface, handle %d", name, handle);
@@ -2284,6 +2308,7 @@
}
Mutex::Autolock _l(mLock);
+ AutoMutex lock(mHardwareLock);
ssize_t index = mAudioHwDevs.indexOfKey(module);
if (index < 0) {
ALOGW("%s() bad hw module %d", __func__, module);
@@ -2305,8 +2330,15 @@
return mHwAvSyncIds.valueAt(index);
}
- sp<DeviceHalInterface> dev = mPrimaryHardwareDev->hwDevice();
- if (dev == NULL) {
+ sp<DeviceHalInterface> dev;
+ {
+ AutoMutex lock(mHardwareLock);
+ if (mPrimaryHardwareDev == nullptr) {
+ return AUDIO_HW_SYNC_INVALID;
+ }
+ dev = mPrimaryHardwareDev->hwDevice();
+ }
+ if (dev == nullptr) {
return AUDIO_HW_SYNC_INVALID;
}
String8 reply;
@@ -2374,8 +2406,21 @@
status_t AudioFlinger::getMicrophones(std::vector<media::MicrophoneInfo> *microphones)
{
AutoMutex lock(mHardwareLock);
- sp<DeviceHalInterface> dev = mPrimaryHardwareDev->hwDevice();
- status_t status = dev->getMicrophones(microphones);
+ status_t status = INVALID_OPERATION;
+
+ for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
+ std::vector<media::MicrophoneInfo> mics;
+ AudioHwDevice *dev = mAudioHwDevs.valueAt(i);
+ mHardwareStatus = AUDIO_HW_GET_MICROPHONES;
+ status_t devStatus = dev->hwDevice()->getMicrophones(&mics);
+ mHardwareStatus = AUDIO_HW_IDLE;
+ if (devStatus == NO_ERROR) {
+ microphones->insert(microphones->begin(), mics.begin(), mics.end());
+ // report success if at least one HW module supports the function.
+ status = NO_ERROR;
+ }
+ }
+
return status;
}
@@ -2522,12 +2567,13 @@
// notify client processes of the new output creation
playbackThread->ioConfigChanged(AUDIO_OUTPUT_OPENED);
- // the first primary output opened designates the primary hw device
- if ((mPrimaryHardwareDev == NULL) && (flags & AUDIO_OUTPUT_FLAG_PRIMARY)) {
+ // the first primary output opened designates the primary hw device if no HW module
+ // named "primary" was already loaded.
+ AutoMutex lock(mHardwareLock);
+ if ((mPrimaryHardwareDev == nullptr) && (flags & AUDIO_OUTPUT_FLAG_PRIMARY)) {
ALOGI("Using module %d as the primary audio interface", module);
mPrimaryHardwareDev = playbackThread->getOutput()->audioHwDev;
- AutoMutex lock(mHardwareLock);
mHardwareStatus = AUDIO_HW_SET_MODE;
mPrimaryHardwareDev->hwDevice()->setMode(mMode);
mHardwareStatus = AUDIO_HW_IDLE;
@@ -3196,6 +3242,10 @@
AudioFlinger::PlaybackThread *AudioFlinger::primaryPlaybackThread_l() const
{
+ AutoMutex lock(mHardwareLock);
+ if (mPrimaryHardwareDev == nullptr) {
+ return nullptr;
+ }
for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
PlaybackThread *thread = mPlaybackThreads.valueAt(i).get();
if(thread->isDuplicating()) {
@@ -3206,7 +3256,7 @@
return thread;
}
}
- return NULL;
+ return nullptr;
}
DeviceTypeSet AudioFlinger::primaryOutputDevice_l() const
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 5c20a2d..6afbd4f 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -835,10 +835,11 @@
// NOTE: If both mLock and mHardwareLock mutexes must be held,
// always take mLock before mHardwareLock
- // These two fields are immutable after onFirstRef(), so no lock needed to access
- AudioHwDevice* mPrimaryHardwareDev; // mAudioHwDevs[0] or NULL
+ // guarded by mHardwareLock
+ AudioHwDevice* mPrimaryHardwareDev;
DefaultKeyedVector<audio_module_handle_t, AudioHwDevice*> mAudioHwDevs;
+ // These two fields are immutable after onFirstRef(), so no lock needed to access
sp<DevicesFactoryHalInterface> mDevicesFactoryHal;
sp<DevicesFactoryHalCallback> mDevicesFactoryHalCallback;
@@ -865,6 +866,7 @@
AUDIO_HW_GET_PARAMETER, // get_parameters
AUDIO_HW_SET_MASTER_MUTE, // set_master_mute
AUDIO_HW_GET_MASTER_MUTE, // get_master_mute
+ AUDIO_HW_GET_MICROPHONES, // getMicrophones
};
mutable hardware_call_state mHardwareStatus; // for dump only
diff --git a/services/audioflinger/FastThread.cpp b/services/audioflinger/FastThread.cpp
index 8b7a124..47fe0b3 100644
--- a/services/audioflinger/FastThread.cpp
+++ b/services/audioflinger/FastThread.cpp
@@ -309,7 +309,7 @@
// compute the delta value of clock_gettime(CLOCK_MONOTONIC)
uint32_t monotonicNs = nsec;
if (sec > 0 && sec < 4) {
- monotonicNs += sec * 1000000000;
+ monotonicNs += sec * 1000000000U; // unsigned to prevent signed overflow.
}
// compute raw CPU load = delta value of clock_gettime(CLOCK_THREAD_CPUTIME_ID)
uint32_t loadNs = 0;
@@ -325,7 +325,7 @@
}
loadNs = nsec;
if (sec > 0 && sec < 4) {
- loadNs += sec * 1000000000;
+ loadNs += sec * 1000000000U; // unsigned to prevent signed overflow.
}
} else {
// first time through the loop
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 105fa14..5d9c35a 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -3051,6 +3051,10 @@
{
if (!mMasterMute) {
char value[PROPERTY_VALUE_MAX];
+ if (mOutDeviceTypeAddrs.empty()) {
+ ALOGD("ro.audio.silent is ignored since no output device is set");
+ return;
+ }
if (isSingleDeviceType(outDeviceTypes(), AUDIO_DEVICE_OUT_REMOTE_SUBMIX)) {
ALOGD("ro.audio.silent will be ignored for threads on AUDIO_DEVICE_OUT_REMOTE_SUBMIX");
return;
@@ -4227,6 +4231,7 @@
(mPatch.sinks[0].id != sinkPortId);
mPatch = *patch;
mOutDeviceTypeAddrs = deviceTypeAddrs;
+ checkSilentMode_l();
if (mOutput->audioHwDev->supportsAudioPatches()) {
sp<DeviceHalInterface> hwDevice = mOutput->audioHwDev->hwDevice();
@@ -9153,6 +9158,7 @@
if (isOutput()) {
sendIoConfigEvent_l(AUDIO_OUTPUT_CONFIG_CHANGED);
mOutDeviceTypeAddrs = sinkDeviceTypeAddrs;
+ checkSilentMode_l();
} else {
sendIoConfigEvent_l(AUDIO_INPUT_CONFIG_CHANGED);
mInDeviceTypeAddr = sourceDeviceTypeAddr;
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 23d8329..4898d37 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -605,6 +605,7 @@
mediametrics::LogItem(mMetricsId)
.setPid(creatorPid)
.setUid(uid)
+ .set(AMEDIAMETRICS_PROP_ALLOWUID, (int32_t)uid)
.set(AMEDIAMETRICS_PROP_EVENT,
AMEDIAMETRICS_PROP_PREFIX_SERVER AMEDIAMETRICS_PROP_EVENT_VALUE_CTOR)
.record();
@@ -2165,6 +2166,7 @@
mediametrics::LogItem(mMetricsId)
.setPid(creatorPid)
.setUid(uid)
+ .set(AMEDIAMETRICS_PROP_ALLOWUID, (int32_t)uid)
.set(AMEDIAMETRICS_PROP_EVENT, "server." AMEDIAMETRICS_PROP_EVENT_VALUE_CTOR)
.record();
}
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioIODescriptorInterface.h b/services/audiopolicy/common/managerdefinitions/include/AudioIODescriptorInterface.h
index 1596ff7..6167f95 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioIODescriptorInterface.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioIODescriptorInterface.h
@@ -34,6 +34,8 @@
virtual audio_patch_handle_t getPatchHandle() const = 0;
virtual void setPatchHandle(audio_patch_handle_t handle) = 0;
+
+ virtual bool isMmap() = 0;
};
template <class IoDescriptor, class Filter>
@@ -48,7 +50,7 @@
// created when the mmap stream is opened). This client is never active.
// On non MMAP IOs, the preferred device is honored only if all active clients have
// a preferred device in which case the first client drives the selection.
- if (desc->getPolicyAudioPort()->isMmap()) {
+ if (desc->isMmap()) {
// The client list is never empty on a MMAP IO
return devices.getDeviceFromId(
desc->clientsList(false /*activeOnly*/)[0]->preferredDeviceId());
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
index c67a006..6f47abc 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
@@ -92,6 +92,12 @@
audio_config_base_t getConfig() const override;
audio_patch_handle_t getPatchHandle() const override;
void setPatchHandle(audio_patch_handle_t handle) override;
+ bool isMmap() override {
+ if (getPolicyAudioPort() != nullptr) {
+ return getPolicyAudioPort()->isMmap();
+ }
+ return false;
+ }
status_t open(const audio_config_t *config,
const sp<DeviceDescriptor> &device,
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
index 41f7dfc..39d1140 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
@@ -268,6 +268,12 @@
audio_config_base_t getConfig() const override;
audio_patch_handle_t getPatchHandle() const override;
void setPatchHandle(audio_patch_handle_t handle) override;
+ bool isMmap() override {
+ if (getPolicyAudioPort() != nullptr) {
+ return getPolicyAudioPort()->isMmap();
+ }
+ return false;
+ }
TrackClientVector clientsList(bool activeOnly = false,
product_strategy_t strategy = PRODUCT_STRATEGY_NONE,
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
index a757551..b82305d 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
@@ -72,8 +72,8 @@
*/
status_t getOutputForAttr(const audio_attributes_t& attributes, uid_t uid,
audio_output_flags_t flags,
- sp<SwAudioOutputDescriptor> &primaryDesc,
- std::vector<sp<SwAudioOutputDescriptor>> *secondaryDescs);
+ sp<AudioPolicyMix> &primaryMix,
+ std::vector<sp<AudioPolicyMix>> *secondaryMixes);
sp<DeviceDescriptor> getDeviceAndMixForInputSource(audio_source_t inputSource,
const DeviceVector &availableDeviceTypes,
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
index aaa28bc..d5272bc 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
@@ -152,10 +152,16 @@
bool AudioOutputDescriptor::setVolume(float volumeDb,
VolumeSource volumeSource,
const StreamTypeVector &/*streams*/,
- const DeviceTypeSet& /*deviceTypes*/,
+ const DeviceTypeSet& deviceTypes,
uint32_t delayMs,
bool force)
{
+
+ if (!supportedDevices().containsDeviceAmongTypes(deviceTypes)) {
+ ALOGV("%s output ID %d unsupported device %s",
+ __func__, getId(), toString(deviceTypes).c_str());
+ return false;
+ }
// We actually change the volume if:
// - the float value returned by computeVolume() changed
// - the force flag is set
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
index ed51389..fc1a59f 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
@@ -150,11 +150,11 @@
status_t AudioPolicyMixCollection::getOutputForAttr(
const audio_attributes_t& attributes, uid_t uid,
audio_output_flags_t flags,
- sp<SwAudioOutputDescriptor> &primaryDesc,
- std::vector<sp<SwAudioOutputDescriptor>> *secondaryDescs)
+ sp<AudioPolicyMix> &primaryMix,
+ std::vector<sp<AudioPolicyMix>> *secondaryMixes)
{
ALOGV("getOutputForAttr() querying %zu mixes:", size());
- primaryDesc = 0;
+ primaryMix.clear();
for (size_t i = 0; i < size(); i++) {
sp<AudioPolicyMix> policyMix = itemAt(i);
const bool primaryOutputMix = !is_mix_loopback_render(policyMix->mRouteFlags);
@@ -169,13 +169,7 @@
return INVALID_OPERATION;
}
- sp<SwAudioOutputDescriptor> policyDesc = policyMix->getOutput();
- if (!policyDesc) {
- ALOGV("%s: Skiping %zu: Mix has no output", __func__, i);
- continue;
- }
-
- if (primaryOutputMix && primaryDesc != 0) {
+ if (primaryOutputMix && primaryMix != nullptr) {
ALOGV("%s: Skiping %zu: Primary output already found", __func__, i);
continue; // Primary output already found
}
@@ -191,18 +185,13 @@
case MixMatchStatus::MATCH:;
}
- policyDesc->mPolicyMix = policyMix;
if (primaryOutputMix) {
- primaryDesc = policyDesc;
+ primaryMix = policyMix;
ALOGV("%s: Mix %zu: set primary desc", __func__, i);
} else {
- if (policyDesc->mIoHandle == AUDIO_IO_HANDLE_NONE) {
- ALOGV("%s: Mix %zu ignored as secondaryOutput because not opened yet", __func__, i);
- } else {
- ALOGV("%s: Add a secondary desc %zu", __func__, i);
- if (secondaryDescs != nullptr) {
- secondaryDescs->push_back(policyDesc);
- }
+ ALOGV("%s: Add a secondary desc %zu", __func__, i);
+ if (secondaryMixes != nullptr) {
+ secondaryMixes->push_back(policyMix);
}
}
}
diff --git a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
index 886e4c9..d31e443 100644
--- a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
@@ -320,7 +320,7 @@
{
String8 devAddress = (address == nullptr || !matchAddress) ? String8("") : String8(address);
// handle legacy remote submix case where the address was not always specified
- if (device_distinguishes_on_address(deviceType) && (devAddress.length() == 0)) {
+ if (audio_is_remote_submix_device(deviceType) && (devAddress.length() == 0)) {
devAddress = String8("0");
}
diff --git a/services/audiopolicy/engine/config/include/EngineConfig.h b/services/audiopolicy/engine/config/include/EngineConfig.h
index 7f5ed5e..5d22c24 100644
--- a/services/audiopolicy/engine/config/include/EngineConfig.h
+++ b/services/audiopolicy/engine/config/include/EngineConfig.h
@@ -31,9 +31,6 @@
/** Default path of audio policy usages configuration file. */
constexpr char DEFAULT_PATH[] = "/vendor/etc/audio_policy_engine_configuration.xml";
-/** Directories where the effect libraries will be search for. */
-constexpr const char* POLICY_USAGE_LIBRARY_PATH[] = {"/odm/etc/", "/vendor/etc/", "/system/etc/"};
-
using AttributesVector = std::vector<audio_attributes_t>;
using StreamVector = std::vector<audio_stream_type_t>;
diff --git a/services/audiopolicy/engine/config/src/EngineConfig.cpp b/services/audiopolicy/engine/config/src/EngineConfig.cpp
index 7f8cdd9..4842cb2 100644
--- a/services/audiopolicy/engine/config/src/EngineConfig.cpp
+++ b/services/audiopolicy/engine/config/src/EngineConfig.cpp
@@ -21,6 +21,7 @@
#include <cutils/properties.h>
#include <media/TypeConverter.h>
#include <media/convert.h>
+#include <system/audio_config.h>
#include <utils/Log.h>
#include <libxml/parser.h>
#include <libxml/xinclude.h>
@@ -693,9 +694,6 @@
return deserializeLegacyVolumeCollection(doc, cur, volumeGroups, nbSkippedElements);
}
-static const char *kConfigLocationList[] = {"/odm/etc", "/vendor/etc", "/system/etc"};
-static const int kConfigLocationListSize =
- (sizeof(kConfigLocationList) / sizeof(kConfigLocationList[0]));
static const int gApmXmlConfigFilePathMaxLength = 128;
static constexpr const char *apmXmlConfigFileName = "audio_policy_configuration.xml";
@@ -715,9 +713,9 @@
fileNames.push_back(apmXmlConfigFileName);
for (const char* fileName : fileNames) {
- for (int i = 0; i < kConfigLocationListSize; i++) {
+ for (const auto& path : audio_get_configuration_paths()) {
snprintf(audioPolicyXmlConfigFile, sizeof(audioPolicyXmlConfigFile),
- "%s/%s", kConfigLocationList[i], fileName);
+ "%s/%s", path.c_str(), fileName);
ret = parseLegacyVolumeFile(audioPolicyXmlConfigFile, volumeGroups);
if (ret == NO_ERROR) {
return ret;
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 5aa6271..bcf6f38 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -47,6 +47,7 @@
#include <media/AudioParameter.h>
#include <private/android_filesystem_config.h>
#include <system/audio.h>
+#include <system/audio_config.h>
#include "AudioPolicyManager.h"
#include <Serializer.h>
#include "TypeConverter.h"
@@ -906,7 +907,7 @@
audio_output_flags_t *flags,
audio_port_handle_t *selectedDeviceId,
bool *isRequestedDeviceForExclusiveUse,
- std::vector<sp<SwAudioOutputDescriptor>> *secondaryDescs,
+ std::vector<sp<AudioPolicyMix>> *secondaryMixes,
output_type_t *outputType)
{
DeviceVector outputDevices;
@@ -931,29 +932,45 @@
// The primary output is the explicit routing (eg. setPreferredDevice) if specified,
// otherwise, fallback to the dynamic policies, if none match, query the engine.
// Secondary outputs are always found by dynamic policies as the engine do not support them
- sp<SwAudioOutputDescriptor> policyDesc;
- status = mPolicyMixes.getOutputForAttr(*resultAttr, uid, *flags, policyDesc, secondaryDescs);
+ sp<AudioPolicyMix> primaryMix;
+ status = mPolicyMixes.getOutputForAttr(*resultAttr, uid, *flags, primaryMix, secondaryMixes);
if (status != OK) {
return status;
}
// Explicit routing is higher priority then any dynamic policy primary output
- bool usePrimaryOutputFromPolicyMixes = requestedDevice == nullptr && policyDesc != nullptr;
+ bool usePrimaryOutputFromPolicyMixes = requestedDevice == nullptr && primaryMix != nullptr;
// FIXME: in case of RENDER policy, the output capabilities should be checked
- if ((usePrimaryOutputFromPolicyMixes || !secondaryDescs->empty())
+ if ((usePrimaryOutputFromPolicyMixes
+ || (secondaryMixes != nullptr && !secondaryMixes->empty()))
&& !audio_is_linear_pcm(config->format)) {
ALOGD("%s: rejecting request as dynamic audio policy only support pcm", __func__);
return BAD_VALUE;
}
if (usePrimaryOutputFromPolicyMixes) {
- *output = policyDesc->mIoHandle;
- sp<AudioPolicyMix> mix = policyDesc->mPolicyMix.promote();
- if (mix != nullptr) {
- sp<DeviceDescriptor> deviceDesc =
- mAvailableOutputDevices.getDevice(mix->mDeviceType,
- mix->mDeviceAddress,
- AUDIO_FORMAT_DEFAULT);
+ sp<DeviceDescriptor> deviceDesc =
+ mAvailableOutputDevices.getDevice(primaryMix->mDeviceType,
+ primaryMix->mDeviceAddress,
+ AUDIO_FORMAT_DEFAULT);
+ sp<SwAudioOutputDescriptor> policyDesc = primaryMix->getOutput();
+ if (deviceDesc != nullptr
+ && (policyDesc == nullptr || (policyDesc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT))) {
+ audio_io_handle_t newOutput;
+ status = openDirectOutput(
+ *stream, session, config,
+ (audio_output_flags_t)(*flags | AUDIO_OUTPUT_FLAG_DIRECT),
+ DeviceVector(deviceDesc), &newOutput);
+ if (status != NO_ERROR) {
+ policyDesc = nullptr;
+ } else {
+ policyDesc = mOutputs.valueFor(newOutput);
+ primaryMix->setOutput(policyDesc);
+ }
+ }
+ if (policyDesc != nullptr) {
+ policyDesc->mPolicyMix = primaryMix;
+ *output = policyDesc->mIoHandle;
*selectedDeviceId = deviceDesc != 0 ? deviceDesc->getId() : AUDIO_PORT_HANDLE_NONE;
ALOGV("getOutputForAttr() returns output %d", *output);
@@ -1049,7 +1066,7 @@
const audio_port_handle_t requestedPortId = *selectedDeviceId;
audio_attributes_t resultAttr;
bool isRequestedDeviceForExclusiveUse = false;
- std::vector<sp<SwAudioOutputDescriptor>> secondaryOutputDescs;
+ std::vector<sp<AudioPolicyMix>> secondaryMixes;
const sp<DeviceDescriptor> requestedDevice =
mAvailableOutputDevices.getDeviceFromId(requestedPortId);
@@ -1060,14 +1077,20 @@
status_t status = getOutputForAttrInt(&resultAttr, output, session, attr, stream, uid,
config, flags, selectedDeviceId, &isRequestedDeviceForExclusiveUse,
- &secondaryOutputDescs, outputType);
+ secondaryOutputs != nullptr ? &secondaryMixes : nullptr, outputType);
if (status != NO_ERROR) {
return status;
}
std::vector<wp<SwAudioOutputDescriptor>> weakSecondaryOutputDescs;
- for (auto& secondaryDesc : secondaryOutputDescs) {
- secondaryOutputs->push_back(secondaryDesc->mIoHandle);
- weakSecondaryOutputDescs.push_back(secondaryDesc);
+ if (secondaryOutputs != nullptr) {
+ for (auto &secondaryMix : secondaryMixes) {
+ sp<SwAudioOutputDescriptor> outputDesc = secondaryMix->getOutput();
+ if (outputDesc != nullptr &&
+ outputDesc->mIoHandle != AUDIO_IO_HANDLE_NONE) {
+ secondaryOutputs->push_back(outputDesc->mIoHandle);
+ weakSecondaryOutputDescs.push_back(outputDesc);
+ }
+ }
}
audio_config_base_t clientConfig = {.sample_rate = config->sample_rate,
@@ -1092,6 +1115,118 @@
return NO_ERROR;
}
+status_t AudioPolicyManager::openDirectOutput(audio_stream_type_t stream,
+ audio_session_t session,
+ const audio_config_t *config,
+ audio_output_flags_t flags,
+ const DeviceVector &devices,
+ audio_io_handle_t *output) {
+
+ *output = AUDIO_IO_HANDLE_NONE;
+
+ // skip direct output selection if the request can obviously be attached to a mixed output
+ // and not explicitly requested
+ if (((flags & AUDIO_OUTPUT_FLAG_DIRECT) == 0) &&
+ audio_is_linear_pcm(config->format) && config->sample_rate <= SAMPLE_RATE_HZ_MAX &&
+ audio_channel_count_from_out_mask(config->channel_mask) <= 2) {
+ return NAME_NOT_FOUND;
+ }
+
+ // Do not allow offloading if one non offloadable effect is enabled or MasterMono is enabled.
+ // This prevents creating an offloaded track and tearing it down immediately after start
+ // when audioflinger detects there is an active non offloadable effect.
+ // FIXME: We should check the audio session here but we do not have it in this context.
+ // This may prevent offloading in rare situations where effects are left active by apps
+ // in the background.
+ sp<IOProfile> profile;
+ if (((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0) ||
+ !(mEffects.isNonOffloadableEffectEnabled() || mMasterMono)) {
+ profile = getProfileForOutput(
+ devices, config->sample_rate, config->format, config->channel_mask,
+ flags, true /* directOnly */);
+ }
+
+ if (profile == nullptr) {
+ return NAME_NOT_FOUND;
+ }
+
+ // exclusive outputs for MMAP and Offload are enforced by different session ids.
+ for (size_t i = 0; i < mOutputs.size(); i++) {
+ sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
+ if (!desc->isDuplicated() && (profile == desc->mProfile)) {
+ // reuse direct output if currently open by the same client
+ // and configured with same parameters
+ if ((config->sample_rate == desc->getSamplingRate()) &&
+ (config->format == desc->getFormat()) &&
+ (config->channel_mask == desc->getChannelMask()) &&
+ (session == desc->mDirectClientSession)) {
+ desc->mDirectOpenCount++;
+ ALOGI("%s reusing direct output %d for session %d", __func__,
+ mOutputs.keyAt(i), session);
+ *output = mOutputs.keyAt(i);
+ return NO_ERROR;
+ }
+ }
+ }
+
+ if (!profile->canOpenNewIo()) {
+ return NAME_NOT_FOUND;
+ }
+
+ sp<SwAudioOutputDescriptor> outputDesc =
+ new SwAudioOutputDescriptor(profile, mpClientInterface);
+
+ String8 address = getFirstDeviceAddress(devices);
+
+ // MSD patch may be using the only output stream that can service this request. Release
+ // MSD patch to prioritize this request over any active output on MSD.
+ AudioPatchCollection msdPatches = getMsdPatches();
+ for (size_t i = 0; i < msdPatches.size(); i++) {
+ const auto& patch = msdPatches[i];
+ for (size_t j = 0; j < patch->mPatch.num_sinks; ++j) {
+ const struct audio_port_config *sink = &patch->mPatch.sinks[j];
+ if (sink->type == AUDIO_PORT_TYPE_DEVICE &&
+ devices.containsDeviceWithType(sink->ext.device.type) &&
+ (address.isEmpty() || strncmp(sink->ext.device.address, address.string(),
+ AUDIO_DEVICE_MAX_ADDRESS_LEN) == 0)) {
+ releaseAudioPatch(patch->getHandle(), mUidCached);
+ break;
+ }
+ }
+ }
+
+ status_t status = outputDesc->open(config, devices, stream, flags, output);
+
+ // only accept an output with the requested parameters
+ if (status != NO_ERROR ||
+ (config->sample_rate != 0 && config->sample_rate != outputDesc->getSamplingRate()) ||
+ (config->format != AUDIO_FORMAT_DEFAULT && config->format != outputDesc->getFormat()) ||
+ (config->channel_mask != 0 && config->channel_mask != outputDesc->getChannelMask())) {
+ ALOGV("%s failed opening direct output: output %d sample rate %d %d,"
+ "format %d %d, channel mask %04x %04x", __func__, *output, config->sample_rate,
+ outputDesc->getSamplingRate(), config->format, outputDesc->getFormat(),
+ config->channel_mask, outputDesc->getChannelMask());
+ if (*output != AUDIO_IO_HANDLE_NONE) {
+ outputDesc->close();
+ }
+ // fall back to mixer output if possible when the direct output could not be open
+ if (audio_is_linear_pcm(config->format) &&
+ config->sample_rate <= SAMPLE_RATE_HZ_MAX) {
+ return NAME_NOT_FOUND;
+ }
+ *output = AUDIO_IO_HANDLE_NONE;
+ return BAD_VALUE;
+ }
+ outputDesc->mDirectOpenCount = 1;
+ outputDesc->mDirectClientSession = session;
+
+ addOutput(*output, outputDesc);
+ mPreviousOutputs = mOutputs;
+ ALOGV("%s returns new direct output %d", __func__, *output);
+ mpClientInterface->onAudioPortListUpdate();
+ return NO_ERROR;
+}
+
audio_io_handle_t AudioPolicyManager::getOutputForDevices(
const DeviceVector &devices,
audio_session_t session,
@@ -1101,7 +1236,6 @@
bool forceMutingHaptic)
{
audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
- status_t status;
// Discard haptic channel mask when forcing muting haptic channels.
audio_channel_mask_t channelMask = forceMutingHaptic
@@ -1136,112 +1270,13 @@
ALOGV("Set VoIP and Direct output flags for PCM format");
}
-
- sp<IOProfile> profile;
-
- // skip direct output selection if the request can obviously be attached to a mixed output
- // and not explicitly requested
- if (((*flags & AUDIO_OUTPUT_FLAG_DIRECT) == 0) &&
- audio_is_linear_pcm(config->format) && config->sample_rate <= SAMPLE_RATE_HZ_MAX &&
- audio_channel_count_from_out_mask(channelMask) <= 2) {
- goto non_direct_output;
- }
-
- // Do not allow offloading if one non offloadable effect is enabled or MasterMono is enabled.
- // This prevents creating an offloaded track and tearing it down immediately after start
- // when audioflinger detects there is an active non offloadable effect.
- // FIXME: We should check the audio session here but we do not have it in this context.
- // This may prevent offloading in rare situations where effects are left active by apps
- // in the background.
-
- if (((*flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0) ||
- !(mEffects.isNonOffloadableEffectEnabled() || mMasterMono)) {
- profile = getProfileForOutput(devices,
- config->sample_rate,
- config->format,
- channelMask,
- (audio_output_flags_t)*flags,
- true /* directOnly */);
- }
-
- if (profile != 0) {
- // exclusive outputs for MMAP and Offload are enforced by different session ids.
- for (size_t i = 0; i < mOutputs.size(); i++) {
- sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
- if (!desc->isDuplicated() && (profile == desc->mProfile)) {
- // reuse direct output if currently open by the same client
- // and configured with same parameters
- if ((config->sample_rate == desc->getSamplingRate()) &&
- (config->format == desc->getFormat()) &&
- (channelMask == desc->getChannelMask()) &&
- (session == desc->mDirectClientSession)) {
- desc->mDirectOpenCount++;
- ALOGI("%s reusing direct output %d for session %d", __func__,
- mOutputs.keyAt(i), session);
- return mOutputs.keyAt(i);
- }
- }
- }
-
- if (!profile->canOpenNewIo()) {
- goto non_direct_output;
- }
-
- sp<SwAudioOutputDescriptor> outputDesc =
- new SwAudioOutputDescriptor(profile, mpClientInterface);
-
- String8 address = getFirstDeviceAddress(devices);
-
- // MSD patch may be using the only output stream that can service this request. Release
- // MSD patch to prioritize this request over any active output on MSD.
- AudioPatchCollection msdPatches = getMsdPatches();
- for (size_t i = 0; i < msdPatches.size(); i++) {
- const auto& patch = msdPatches[i];
- for (size_t j = 0; j < patch->mPatch.num_sinks; ++j) {
- const struct audio_port_config *sink = &patch->mPatch.sinks[j];
- if (sink->type == AUDIO_PORT_TYPE_DEVICE &&
- devices.containsDeviceWithType(sink->ext.device.type) &&
- (address.isEmpty() || strncmp(sink->ext.device.address, address.string(),
- AUDIO_DEVICE_MAX_ADDRESS_LEN) == 0)) {
- releaseAudioPatch(patch->getHandle(), mUidCached);
- break;
- }
- }
- }
-
- status = outputDesc->open(config, devices, stream, *flags, &output);
-
- // only accept an output with the requested parameters
- if (status != NO_ERROR ||
- (config->sample_rate != 0 && config->sample_rate != outputDesc->getSamplingRate()) ||
- (config->format != AUDIO_FORMAT_DEFAULT && config->format != outputDesc->getFormat()) ||
- (channelMask != 0 && channelMask != outputDesc->getChannelMask())) {
- ALOGV("%s failed opening direct output: output %d sample rate %d %d,"
- "format %d %d, channel mask %04x %04x", __func__, output, config->sample_rate,
- outputDesc->getSamplingRate(), config->format, outputDesc->getFormat(),
- channelMask, outputDesc->getChannelMask());
- if (output != AUDIO_IO_HANDLE_NONE) {
- outputDesc->close();
- }
- // fall back to mixer output if possible when the direct output could not be open
- if (audio_is_linear_pcm(config->format) &&
- config->sample_rate <= SAMPLE_RATE_HZ_MAX) {
- goto non_direct_output;
- }
- return AUDIO_IO_HANDLE_NONE;
- }
- outputDesc->mDirectOpenCount = 1;
- outputDesc->mDirectClientSession = session;
-
- addOutput(output, outputDesc);
- mPreviousOutputs = mOutputs;
- ALOGV("%s returns new direct output %d", __func__, output);
- mpClientInterface->onAudioPortListUpdate();
+ audio_config_t directConfig = *config;
+ directConfig.channel_mask = channelMask;
+ status_t status = openDirectOutput(stream, session, &directConfig, *flags, devices, &output);
+ if (status != NAME_NOT_FOUND) {
return output;
}
-non_direct_output:
-
// A request for HW A/V sync cannot fallback to a mixed output because time
// stamps are embedded in audio data
if ((*flags & (AUDIO_OUTPUT_FLAG_HW_AV_SYNC | AUDIO_OUTPUT_FLAG_MMAP_NOIRQ)) != 0) {
@@ -2145,7 +2180,7 @@
if (!profile->canOpenNewIo()) {
for (size_t i = 0; i < mInputs.size(); ) {
- sp <AudioInputDescriptor> desc = mInputs.valueAt(i);
+ sp<AudioInputDescriptor> desc = mInputs.valueAt(i);
if (desc->mProfile != profile) {
i++;
continue;
@@ -2885,8 +2920,8 @@
}
audio_config_t outputConfig = mix.mFormat;
audio_config_t inputConfig = mix.mFormat;
- // NOTE: audio flinger mixer does not support mono output: configure remote submix HAL in
- // stereo and let audio flinger do the channel conversion if needed.
+ // NOTE: audio flinger mixer does not support mono output: configure remote submix HAL
+ // in stereo and let audio flinger do the channel conversion if needed.
outputConfig.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
inputConfig.channel_mask = AUDIO_CHANNEL_IN_STEREO;
rSubmixModule->addOutputProfile(address.c_str(), &outputConfig,
@@ -2916,10 +2951,11 @@
}
bool foundOutput = false;
- for (size_t j = 0 ; j < mOutputs.size() ; j++) {
+ // First try to find an already opened output supporting the device
+ for (size_t j = 0 ; j < mOutputs.size() && !foundOutput && res == NO_ERROR; j++) {
sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(j);
- if (desc->supportedDevices().contains(device)) {
+ if (!desc->isDuplicated() && desc->supportedDevices().contains(device)) {
if (mPolicyMixes.registerMix(mix, desc) != NO_ERROR) {
ALOGE("Could not register mix RENDER, dev=0x%X addr=%s", type,
address.string());
@@ -2927,10 +2963,26 @@
} else {
foundOutput = true;
}
- break;
}
}
-
+ // If no output found, try to find a direct output profile supporting the device
+ for (size_t i = 0; i < mHwModules.size() && !foundOutput && res == NO_ERROR; i++) {
+ sp<HwModule> module = mHwModules[i];
+ for (size_t j = 0;
+ j < module->getOutputProfiles().size() && !foundOutput && res == NO_ERROR;
+ j++) {
+ sp<IOProfile> profile = module->getOutputProfiles()[j];
+ if (profile->isDirectOutput() && profile->supportsDevice(device)) {
+ if (mPolicyMixes.registerMix(mix, nullptr) != NO_ERROR) {
+ ALOGE("Could not register mix RENDER, dev=0x%X addr=%s", type,
+ address.string());
+ res = INVALID_OPERATION;
+ } else {
+ foundOutput = true;
+ }
+ }
+ }
+ }
if (res != NO_ERROR) {
ALOGE(" Error registering mix %zu for device 0x%X addr %s",
i, type, address.string());
@@ -3013,58 +3065,67 @@
}
}
+// Returns true if all devices types match the predicate and are supported by one HW module
+bool AudioPolicyManager::areAllDevicesSupported(
+ const Vector<AudioDeviceTypeAddr>& devices,
+ std::function<bool(audio_devices_t)> predicate,
+ const char *context) {
+ for (size_t i = 0; i < devices.size(); i++) {
+ sp<DeviceDescriptor> devDesc = mHwModules.getDeviceDescriptor(
+ devices[i].mType, devices[i].mAddress.c_str(), String8(),
+ AUDIO_FORMAT_DEFAULT, false /*allowToCreate*/, true /*matchAddress*/);
+ if (devDesc == nullptr || (predicate != nullptr && !predicate(devices[i].mType))) {
+ ALOGE("%s: device type %#x address %s not supported or not an output device",
+ context, devices[i].mType, devices[i].mAddress.c_str());
+ return false;
+ }
+ }
+ return true;
+}
+
status_t AudioPolicyManager::setUidDeviceAffinities(uid_t uid,
const Vector<AudioDeviceTypeAddr>& devices) {
ALOGV("%s() uid=%d num devices %zu", __FUNCTION__, uid, devices.size());
- // uid/device affinity is only for output devices
- for (size_t i = 0; i < devices.size(); i++) {
- if (!audio_is_output_device(devices[i].mType)) {
- ALOGE("setUidDeviceAffinities() device=%08x is NOT an output device",
- devices[i].mType);
- return BAD_VALUE;
- }
+ if (!areAllDevicesSupported(devices, audio_is_output_device, __func__)) {
+ return BAD_VALUE;
}
status_t res = mPolicyMixes.setUidDeviceAffinities(uid, devices);
- if (res == NO_ERROR) {
- // reevaluate outputs for all given devices
- for (size_t i = 0; i < devices.size(); i++) {
- sp<DeviceDescriptor> devDesc = mHwModules.getDeviceDescriptor(
- devices[i].mType, devices[i].mAddress.c_str(), String8(),
- AUDIO_FORMAT_DEFAULT);
- SortedVector<audio_io_handle_t> outputs;
- if (checkOutputsForDevice(devDesc, AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
- outputs) != NO_ERROR) {
- ALOGE("setUidDeviceAffinities() error in checkOutputsForDevice for device=%08x"
- " addr=%s", devices[i].mType, devices[i].mAddress.c_str());
- return INVALID_OPERATION;
- }
- }
+ if (res != NO_ERROR) {
+ ALOGE("%s() Could not set all device affinities for uid = %d", __FUNCTION__, uid);
+ return res;
}
- return res;
+
+ checkForDeviceAndOutputChanges();
+ updateCallAndOutputRouting();
+
+ return NO_ERROR;
}
status_t AudioPolicyManager::removeUidDeviceAffinities(uid_t uid) {
ALOGV("%s() uid=%d", __FUNCTION__, uid);
status_t res = mPolicyMixes.removeUidDeviceAffinities(uid);
if (res != NO_ERROR) {
- ALOGE("%s() Could not remove all device affinities fo uid = %d",
+ ALOGE("%s() Could not remove all device affinities for uid = %d",
__FUNCTION__, uid);
return INVALID_OPERATION;
}
+ checkForDeviceAndOutputChanges();
+ updateCallAndOutputRouting();
+
return res;
}
status_t AudioPolicyManager::setPreferredDeviceForStrategy(product_strategy_t strategy,
const AudioDeviceTypeAddr &device) {
- ALOGI("%s() strategy=%d device=%08x addr=%s", __FUNCTION__,
+ ALOGV("%s() strategy=%d device=%08x addr=%s", __FUNCTION__,
strategy, device.mType, device.mAddress.c_str());
- // strategy preferred device is only for output devices
- if (!audio_is_output_device(device.mType)) {
- ALOGE("%s() device=%08x is NOT an output device", __FUNCTION__, device.mType);
+
+ Vector<AudioDeviceTypeAddr> devices;
+ devices.add(device);
+ if (!areAllDevicesSupported(devices, audio_is_output_device, __func__)) {
return BAD_VALUE;
}
-
status_t status = mEngine->setPreferredDeviceForStrategy(strategy, device);
if (status != NO_ERROR) {
ALOGW("Engine could not set preferred device %08x %s for strategy %d",
@@ -3123,17 +3184,10 @@
status_t AudioPolicyManager::setUserIdDeviceAffinities(int userId,
const Vector<AudioDeviceTypeAddr>& devices) {
- ALOGI("%s() userId=%d num devices %zu", __FUNCTION__, userId, devices.size());
- // userId/device affinity is only for output devices
- for (size_t i = 0; i < devices.size(); i++) {
- if (!audio_is_output_device(devices[i].mType)) {
- ALOGE("%s() device=%08x is NOT an output device",
- __FUNCTION__,
- devices[i].mType);
- return BAD_VALUE;
- }
+ ALOGI("%s() userId=%d num devices %zu", __FUNCTION__, userId, devices.size());\
+ if (!areAllDevicesSupported(devices, audio_is_output_device, __func__)) {
+ return BAD_VALUE;
}
-
status_t status = mPolicyMixes.setUserIdDeviceAffinities(userId, devices);
if (status != NO_ERROR) {
ALOGE("%s() could not set device affinity for userId %d",
@@ -3640,12 +3694,11 @@
audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE;
audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
bool isRequestedDeviceForExclusiveUse = false;
- std::vector<sp<SwAudioOutputDescriptor>> secondaryOutputs;
output_type_t outputType;
getOutputForAttrInt(&resultAttr, &output, AUDIO_SESSION_NONE, &attributes,
&stream, sourceDesc->uid(), &config, &flags,
&selectedDeviceId, &isRequestedDeviceForExclusiveUse,
- &secondaryOutputs, &outputType);
+ nullptr, &outputType);
if (output == AUDIO_IO_HANDLE_NONE) {
ALOGV("%s no output for device %s",
__FUNCTION__, sinkDevice->toString().c_str());
@@ -3756,6 +3809,22 @@
removeAudioPatch(patchDesc->getHandle());
nextAudioPortGeneration();
mpClientInterface->onAudioPatchListUpdate();
+ // SW Bridge
+ if (patch->num_sources > 1 && patch->sources[1].type == AUDIO_PORT_TYPE_MIX) {
+ sp<SwAudioOutputDescriptor> outputDesc =
+ mOutputs.getOutputFromId(patch->sources[1].id);
+ if (outputDesc == NULL) {
+ ALOGE("%s output not found for id %d", __func__, patch->sources[0].id);
+ return BAD_VALUE;
+ }
+ // Reset handle so that setOutputDevice will force new AF patch to reach the sink
+ outputDesc->setPatchHandle(AUDIO_PATCH_HANDLE_NONE);
+ setOutputDevices(outputDesc,
+ getNewOutputDevices(outputDesc, true /*fromCache*/),
+ true, /*force*/
+ 0,
+ NULL);
+ }
} else {
return BAD_VALUE;
}
@@ -4351,12 +4420,6 @@
return mAudioPortGeneration++;
}
-// Treblized audio policy xml config will be located in /odm/etc or /vendor/etc.
-static const char *kConfigLocationList[] =
- {"/odm/etc", "/vendor/etc", "/system/etc"};
-static const int kConfigLocationListSize =
- (sizeof(kConfigLocationList) / sizeof(kConfigLocationList[0]));
-
static status_t deserializeAudioPolicyXmlConfig(AudioPolicyConfig &config) {
char audioPolicyXmlConfigFile[AUDIO_POLICY_XML_CONFIG_FILE_PATH_MAX_LENGTH];
std::vector<const char*> fileNames;
@@ -4378,9 +4441,9 @@
fileNames.push_back(AUDIO_POLICY_XML_CONFIG_FILE_NAME);
for (const char* fileName : fileNames) {
- for (int i = 0; i < kConfigLocationListSize; i++) {
+ for (const auto& path : audio_get_configuration_paths()) {
snprintf(audioPolicyXmlConfigFile, sizeof(audioPolicyXmlConfigFile),
- "%s/%s", kConfigLocationList[i], fileName);
+ "%s/%s", path.c_str(), fileName);
ret = deserializeAudioPolicyFile(audioPolicyXmlConfigFile, &config);
if (ret == NO_ERROR) {
config.setSource(audioPolicyXmlConfigFile);
@@ -4540,9 +4603,6 @@
mTtsOutputAvailable = true;
}
- if ((outProfile->getFlags() & AUDIO_OUTPUT_FLAG_DIRECT) != 0) {
- continue;
- }
const DeviceVector &supportedDevices = outProfile->getSupportedDevices();
DeviceVector availProfileDevices = supportedDevices.filter(mOutputDevicesAll);
sp<DeviceDescriptor> supportedDevice = 0;
@@ -4583,12 +4643,16 @@
outProfile->getFlags() & AUDIO_OUTPUT_FLAG_PRIMARY) {
mPrimaryOutput = outputDesc;
}
- addOutput(output, outputDesc);
- setOutputDevices(outputDesc,
- DeviceVector(supportedDevice),
- true,
- 0,
- NULL);
+ if ((outProfile->getFlags() & AUDIO_OUTPUT_FLAG_DIRECT) != 0) {
+ outputDesc->close();
+ } else {
+ addOutput(output, outputDesc);
+ setOutputDevices(outputDesc,
+ DeviceVector(supportedDevice),
+ true,
+ 0,
+ NULL);
+ }
}
// open input streams needed to access attached devices to validate
// mAvailableInputDevices list
@@ -4774,7 +4838,7 @@
if (output != AUDIO_IO_HANDLE_NONE) {
addOutput(output, desc);
- if (device_distinguishes_on_address(deviceType) && address != "0") {
+ if (audio_is_remote_submix_device(deviceType) && address != "0") {
sp<AudioPolicyMix> policyMix;
if (mPolicyMixes.getAudioPolicyMix(deviceType, address, policyMix)
== NO_ERROR) {
@@ -5264,10 +5328,19 @@
for (size_t i = 0; i < mOutputs.size(); i++) {
const sp<SwAudioOutputDescriptor>& outputDescriptor = mOutputs[i];
for (const sp<TrackClientDescriptor>& client : outputDescriptor->getClientIterable()) {
- sp<SwAudioOutputDescriptor> desc;
- std::vector<sp<SwAudioOutputDescriptor>> secondaryDescs;
+ sp<AudioPolicyMix> primaryMix;
+ std::vector<sp<AudioPolicyMix>> secondaryMixes;
status_t status = mPolicyMixes.getOutputForAttr(client->attributes(), client->uid(),
- client->flags(), desc, &secondaryDescs);
+ client->flags(), primaryMix, &secondaryMixes);
+ std::vector<sp<SwAudioOutputDescriptor>> secondaryDescs;
+ for (auto &secondaryMix : secondaryMixes) {
+ sp<SwAudioOutputDescriptor> outputDesc = secondaryMix->getOutput();
+ if (outputDesc != nullptr &&
+ outputDesc->mIoHandle != AUDIO_IO_HANDLE_NONE) {
+ secondaryDescs.push_back(outputDesc);
+ }
+ }
+
if (status != OK ||
!std::equal(client->getSecondaryOutputs().begin(),
client->getSecondaryOutputs().end(),
@@ -5471,21 +5544,17 @@
}
// check dynamic policies but only for primary descriptors (secondary not used for audible
// audio routing, only used for duplication for playback capture)
- sp<SwAudioOutputDescriptor> policyDesc;
+ sp<AudioPolicyMix> policyMix;
status_t status = mPolicyMixes.getOutputForAttr(attr, 0 /*uid unknown here*/,
- AUDIO_OUTPUT_FLAG_NONE, policyDesc, nullptr);
+ AUDIO_OUTPUT_FLAG_NONE, policyMix, nullptr);
if (status != OK) {
return status;
}
- if (policyDesc != nullptr) {
- sp<AudioPolicyMix> mix = policyDesc->mPolicyMix.promote();
- if (mix != nullptr) {
- AudioDeviceTypeAddr device(mix->mDeviceType, mix->mDeviceAddress.c_str());
- devices->push_back(device);
- return NO_ERROR;
- }
+ if (policyMix != nullptr && policyMix->getOutput() != nullptr) {
+ AudioDeviceTypeAddr device(policyMix->mDeviceType, policyMix->mDeviceAddress.c_str());
+ devices->push_back(device);
+ return NO_ERROR;
}
-
DeviceVector curDevices = mEngine->getOutputDevicesForAttributes(attr, nullptr, false);
for (const auto& device : curDevices) {
devices->push_back(device->getDeviceTypeAddr());
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index c142880..b588f89 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -837,7 +837,7 @@
audio_output_flags_t *flags,
audio_port_handle_t *selectedDeviceId,
bool *isRequestedDeviceForExclusiveUse,
- std::vector<sp<SwAudioOutputDescriptor>> *secondaryDescs,
+ std::vector<sp<AudioPolicyMix>> *secondaryMixes,
output_type_t *outputType);
// internal method to return the output handle for the given device and format
audio_io_handle_t getOutputForDevices(
@@ -848,6 +848,16 @@
audio_output_flags_t *flags,
bool forceMutingHaptic = false);
+ // Internal method checking if a direct output can be opened matching the requested
+ // attributes, flags, config and devices.
+ // If NAME_NOT_FOUND is returned, an attempt can be made to open a mixed output.
+ status_t openDirectOutput(
+ audio_stream_type_t stream,
+ audio_session_t session,
+ const audio_config_t *config,
+ audio_output_flags_t flags,
+ const DeviceVector &devices,
+ audio_io_handle_t *output);
/**
* @brief getInputForDevice selects an input handle for a given input device and
* requester context
@@ -926,6 +936,12 @@
int delayMs,
uid_t uid,
sp<AudioPatch> *patchDescPtr);
+
+ bool areAllDevicesSupported(
+ const Vector<AudioDeviceTypeAddr>& devices,
+ std::function<bool(audio_devices_t)> predicate,
+ const char* context);
+
};
};
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index d743be9..a15970a 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -676,6 +676,7 @@
case AUDIO_SOURCE_VOICE_CALL:
case AUDIO_SOURCE_REMOTE_SUBMIX:
case AUDIO_SOURCE_FM_TUNER:
+ case AUDIO_SOURCE_ECHO_REFERENCE:
return true;
default:
break;
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 9af1c36..f92d673 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -2742,7 +2742,7 @@
mClientPackageName(clientPackageName),
mClientPid(clientPid), mClientUid(clientUid),
mServicePid(servicePid),
- mDisconnected(false),
+ mDisconnected(false), mUidIsTrusted(false),
mAudioRestriction(hardware::camera2::ICameraDeviceUser::AUDIO_RESTRICTION_NONE),
mRemoteBinder(remoteCallback)
{
@@ -2791,6 +2791,8 @@
if (getCurrentServingCall() != BinderCallType::HWBINDER) {
mAppOpsManager = std::make_unique<AppOpsManager>();
}
+
+ mUidIsTrusted = isTrustedCallingUid(mClientUid);
}
CameraService::BasicClient::~BasicClient() {
@@ -2905,7 +2907,9 @@
return PERMISSION_DENIED;
}
- if (res == AppOpsManager::MODE_IGNORED) {
+ // If the calling Uid is trusted (a native service), the AppOpsManager could
+ // return MODE_IGNORED. Do not treat such case as error.
+ if (!mUidIsTrusted && res == AppOpsManager::MODE_IGNORED) {
ALOGI("Camera %s: Access for \"%s\" has been restricted",
mCameraIdStr.string(), String8(mClientPackageName).string());
// Return the same error as for device policy manager rejection
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 1adf15a..18cf77a 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -323,6 +323,7 @@
const uid_t mClientUid;
const pid_t mServicePid;
bool mDisconnected;
+ bool mUidIsTrusted;
mutable Mutex mAudioRestrictionLock;
int32_t mAudioRestriction;
diff --git a/services/camera/libcameraservice/api2/CameraOfflineSessionClient.cpp b/services/camera/libcameraservice/api2/CameraOfflineSessionClient.cpp
index 8f20685..237c24b 100644
--- a/services/camera/libcameraservice/api2/CameraOfflineSessionClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraOfflineSessionClient.cpp
@@ -197,7 +197,9 @@
return PERMISSION_DENIED;
}
- if (res == AppOpsManager::MODE_IGNORED) {
+ // If the calling Uid is trusted (a native service), the AppOpsManager could
+ // return MODE_IGNORED. Do not treat such case as error.
+ if (!mUidIsTrusted && res == AppOpsManager::MODE_IGNORED) {
ALOGI("Offline Camera %s: Access for \"%s\" has been restricted",
mCameraIdStr.string(), String8(mClientPackageName).string());
// Return the same error as for device policy manager rejection
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 2d46122..b00a2d9 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -2344,6 +2344,21 @@
}
}
+bool Camera3Device::checkAbandonedStreamsLocked() {
+ if ((mInputStream.get() != nullptr) && (mInputStream->isAbandoned())) {
+ return true;
+ }
+
+ for (size_t i = 0; i < mOutputStreams.size(); i++) {
+ auto stream = mOutputStreams[i];
+ if ((stream.get() != nullptr) && (stream->isAbandoned())) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
bool Camera3Device::reconfigureCamera(const CameraMetadata& sessionParams) {
ATRACE_CALL();
bool ret = false;
@@ -2352,6 +2367,12 @@
nsecs_t maxExpectedDuration = getExpectedInFlightDuration();
Mutex::Autolock l(mLock);
+ if (checkAbandonedStreamsLocked()) {
+ ALOGW("%s: Abandoned stream detected, session parameters can't be applied correctly!",
+ __FUNCTION__);
+ return true;
+ }
+
auto rc = internalPauseAndWaitLocked(maxExpectedDuration);
if (rc == NO_ERROR) {
mNeedConfig = true;
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index b373a64..19ecf4b 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -648,6 +648,12 @@
bool reconfigureCamera(const CameraMetadata& sessionParams);
/**
+ * Return true in case of any output or input abandoned streams,
+ * otherwise return false.
+ */
+ bool checkAbandonedStreamsLocked();
+
+ /**
* Filter stream session parameters and configure camera HAL.
*/
status_t filterParamsAndConfigureLocked(const CameraMetadata& sessionParams,
diff --git a/services/camera/libcameraservice/device3/ZoomRatioMapper.cpp b/services/camera/libcameraservice/device3/ZoomRatioMapper.cpp
index 84da45a..a87de77 100644
--- a/services/camera/libcameraservice/device3/ZoomRatioMapper.cpp
+++ b/services/camera/libcameraservice/device3/ZoomRatioMapper.cpp
@@ -243,10 +243,15 @@
if (weight == 0) {
continue;
}
- // Top-left is inclusively clamped
- scaleCoordinates(entry.data.i32 + j, 1, zoomRatio, ClampInclusive);
- // Bottom-right is exclusively clamped
- scaleCoordinates(entry.data.i32 + j + 2, 1, zoomRatio, ClampExclusive);
+ // Top left (inclusive)
+ scaleCoordinates(entry.data.i32 + j, 1, zoomRatio, true /*clamp*/);
+ // Bottom right (exclusive): Use adjacent inclusive pixel to
+ // calculate.
+ entry.data.i32[j+2] -= 1;
+ entry.data.i32[j+3] -= 1;
+ scaleCoordinates(entry.data.i32 + j + 2, 1, zoomRatio, true /*clamp*/);
+ entry.data.i32[j+2] += 1;
+ entry.data.i32[j+3] += 1;
}
}
@@ -258,7 +263,7 @@
if (isResult) {
for (auto pts : kResultPointsToCorrectNoClamp) {
entry = metadata->find(pts);
- scaleCoordinates(entry.data.i32, entry.count / 2, zoomRatio, ClampOff);
+ scaleCoordinates(entry.data.i32, entry.count / 2, zoomRatio, false /*clamp*/);
}
}
@@ -282,10 +287,15 @@
if (weight == 0) {
continue;
}
- // Top-left is inclusively clamped
- scaleCoordinates(entry.data.i32 + j, 1, 1.0 / zoomRatio, ClampInclusive);
- // Bottom-right is exclusively clamped
- scaleCoordinates(entry.data.i32 + j + 2, 1, 1.0 / zoomRatio, ClampExclusive);
+ // Top-left (inclusive)
+ scaleCoordinates(entry.data.i32 + j, 1, 1.0 / zoomRatio, true /*clamp*/);
+ // Bottom-right (exclusive): Use adjacent inclusive pixel to
+ // calculate.
+ entry.data.i32[j+2] -= 1;
+ entry.data.i32[j+3] -= 1;
+ scaleCoordinates(entry.data.i32 + j + 2, 1, 1.0 / zoomRatio, true /*clamp*/);
+ entry.data.i32[j+2] += 1;
+ entry.data.i32[j+3] += 1;
}
}
for (auto rect : kRectsToCorrect) {
@@ -295,7 +305,7 @@
if (isResult) {
for (auto pts : kResultPointsToCorrectNoClamp) {
entry = metadata->find(pts);
- scaleCoordinates(entry.data.i32, entry.count / 2, 1.0 / zoomRatio, ClampOff);
+ scaleCoordinates(entry.data.i32, entry.count / 2, 1.0 / zoomRatio, false /*clamp*/);
}
}
@@ -309,28 +319,31 @@
}
void ZoomRatioMapper::scaleCoordinates(int32_t* coordPairs, int coordCount,
- float scaleRatio, ClampMode clamp) {
+ float scaleRatio, bool clamp) {
+ // A pixel's coordinate is represented by the position of its top-left corner.
+ // To avoid the rounding error, we use the coordinate for the center of the
+ // pixel instead:
+ // 1. First shift the coordinate system half pixel both horizontally and
+ // vertically, so that [x, y] is the center of the pixel, not the top-left corner.
+ // 2. Do zoom operation to scale the coordinate relative to the center of
+ // the active array (shifted by 0.5 pixel as well).
+ // 3. Shift the coordinate system back by directly using the pixel center
+ // coordinate.
for (int i = 0; i < coordCount * 2; i += 2) {
float x = coordPairs[i];
float y = coordPairs[i + 1];
- float xCentered = x - mArrayWidth / 2;
- float yCentered = y - mArrayHeight / 2;
+ float xCentered = x - (mArrayWidth - 2) / 2;
+ float yCentered = y - (mArrayHeight - 2) / 2;
float scaledX = xCentered * scaleRatio;
float scaledY = yCentered * scaleRatio;
- scaledX += mArrayWidth / 2;
- scaledY += mArrayHeight / 2;
+ scaledX += (mArrayWidth - 2) / 2;
+ scaledY += (mArrayHeight - 2) / 2;
+ coordPairs[i] = static_cast<int32_t>(std::round(scaledX));
+ coordPairs[i+1] = static_cast<int32_t>(std::round(scaledY));
// Clamp to within activeArray/preCorrectionActiveArray
- coordPairs[i] = static_cast<int32_t>(scaledX);
- coordPairs[i+1] = static_cast<int32_t>(scaledY);
- if (clamp != ClampOff) {
- int32_t right, bottom;
- if (clamp == ClampInclusive) {
- right = mArrayWidth - 1;
- bottom = mArrayHeight - 1;
- } else {
- right = mArrayWidth;
- bottom = mArrayHeight;
- }
+ if (clamp) {
+ int32_t right = mArrayWidth - 1;
+ int32_t bottom = mArrayHeight - 1;
coordPairs[i] =
std::min(right, std::max(0, coordPairs[i]));
coordPairs[i+1] =
@@ -343,25 +356,25 @@
void ZoomRatioMapper::scaleRects(int32_t* rects, int rectCount,
float scaleRatio) {
for (int i = 0; i < rectCount * 4; i += 4) {
- // Map from (l, t, width, height) to (l, t, r, b).
- // [l, t] is inclusive, and [r, b] is exclusive.
+ // Map from (l, t, width, height) to (l, t, l+width-1, t+height-1),
+ // where both top-left and bottom-right are inclusive.
int32_t coords[4] = {
rects[i],
rects[i + 1],
- rects[i] + rects[i + 2],
- rects[i + 1] + rects[i + 3]
+ rects[i] + rects[i + 2] - 1,
+ rects[i + 1] + rects[i + 3] - 1
};
// top-left
- scaleCoordinates(coords, 1, scaleRatio, ClampInclusive);
+ scaleCoordinates(coords, 1, scaleRatio, true /*clamp*/);
// bottom-right
- scaleCoordinates(coords+2, 1, scaleRatio, ClampExclusive);
+ scaleCoordinates(coords+2, 1, scaleRatio, true /*clamp*/);
// Map back to (l, t, width, height)
rects[i] = coords[0];
rects[i + 1] = coords[1];
- rects[i + 2] = coords[2] - coords[0];
- rects[i + 3] = coords[3] - coords[1];
+ rects[i + 2] = coords[2] - coords[0] + 1;
+ rects[i + 3] = coords[3] - coords[1] + 1;
}
}
diff --git a/services/camera/libcameraservice/device3/ZoomRatioMapper.h b/services/camera/libcameraservice/device3/ZoomRatioMapper.h
index aa3d913..698f87f 100644
--- a/services/camera/libcameraservice/device3/ZoomRatioMapper.h
+++ b/services/camera/libcameraservice/device3/ZoomRatioMapper.h
@@ -65,14 +65,8 @@
status_t updateCaptureResult(CameraMetadata *request, bool requestedZoomRatioIs1);
public: // Visible for testing. Do not use concurently.
- enum ClampMode {
- ClampOff,
- ClampInclusive,
- ClampExclusive,
- };
-
void scaleCoordinates(int32_t* coordPairs, int coordCount,
- float scaleRatio, ClampMode clamp);
+ float scaleRatio, bool clamp);
bool isValid() { return mIsValid; }
private:
diff --git a/services/camera/libcameraservice/tests/ZoomRatioTest.cpp b/services/camera/libcameraservice/tests/ZoomRatioTest.cpp
index 300da09..4e94991 100644
--- a/services/camera/libcameraservice/tests/ZoomRatioTest.cpp
+++ b/services/camera/libcameraservice/tests/ZoomRatioTest.cpp
@@ -171,35 +171,35 @@
std::array<int32_t, 16> originalCoords = {
0, 0, // top-left
- width, 0, // top-right
- 0, height, // bottom-left
- width, height, // bottom-right
- width / 2, height / 2, // center
- width / 4, height / 4, // top-left after 2x
- width / 3, height * 2 / 3, // bottom-left after 3x zoom
- width * 7 / 8, height / 2, // middle-right after 1.33x zoom
+ width - 1, 0, // top-right
+ 0, height - 1, // bottom-left
+ width - 1, height - 1, // bottom-right
+ (width - 1) / 2, (height - 1) / 2, // center
+ (width - 1) / 4, (height - 1) / 4, // top-left after 2x
+ (width - 1) / 3, (height - 1) * 2 / 3, // bottom-left after 3x zoom
+ (width - 1) * 7 / 8, (height - 1) / 2, // middle-right after 1.33x zoom
};
// Verify 1.0x zoom doesn't change the coordinates
auto coords = originalCoords;
- mapper.scaleCoordinates(coords.data(), coords.size()/2, 1.0f, ZoomRatioMapper::ClampOff);
+ mapper.scaleCoordinates(coords.data(), coords.size()/2, 1.0f, false /*clamp*/);
for (size_t i = 0; i < coords.size(); i++) {
EXPECT_EQ(coords[i], originalCoords[i]);
}
// Verify 2.0x zoom work as expected (no clamping)
std::array<float, 16> expected2xCoords = {
- - width / 2.0f, - height / 2.0f,// top-left
- width * 3 / 2.0f, - height / 2.0f, // top-right
- - width / 2.0f, height * 3 / 2.0f, // bottom-left
- width * 3 / 2.0f, height * 3 / 2.0f, // bottom-right
- width / 2.0f, height / 2.0f, // center
+ - (width - 1) / 2.0f, - (height - 1) / 2.0f,// top-left
+ (width - 1) * 3 / 2.0f, - (height - 1) / 2.0f, // top-right
+ - (width - 1) / 2.0f, (height - 1) * 3 / 2.0f, // bottom-left
+ (width - 1) * 3 / 2.0f, (height - 1) * 3 / 2.0f, // bottom-right
+ (width - 1) / 2.0f, (height - 1) / 2.0f, // center
0, 0, // top-left after 2x
- width / 6.0f, height - height / 6.0f, // bottom-left after 3x zoom
- width + width / 4.0f, height / 2.0f, // middle-right after 1.33x zoom
+ (width - 1) / 6.0f, (height - 1) * 5.0f / 6.0f, // bottom-left after 3x zoom
+ (width - 1) * 5.0f / 4.0f, (height - 1) / 2.0f, // middle-right after 1.33x zoom
};
coords = originalCoords;
- mapper.scaleCoordinates(coords.data(), coords.size()/2, 2.0f, ZoomRatioMapper::ClampOff);
+ mapper.scaleCoordinates(coords.data(), coords.size()/2, 2.0f, false /*clamp*/);
for (size_t i = 0; i < coords.size(); i++) {
EXPECT_LE(std::abs(coords[i] - expected2xCoords[i]), kMaxAllowedPixelError);
}
@@ -207,16 +207,16 @@
// Verify 2.0x zoom work as expected (with inclusive clamping)
std::array<float, 16> expected2xCoordsClampedInc = {
0, 0, // top-left
- static_cast<float>(width) - 1, 0, // top-right
- 0, static_cast<float>(height) - 1, // bottom-left
- static_cast<float>(width) - 1, static_cast<float>(height) - 1, // bottom-right
- width / 2.0f, height / 2.0f, // center
+ width - 1.0f, 0, // top-right
+ 0, height - 1.0f, // bottom-left
+ width - 1.0f, height - 1.0f, // bottom-right
+ (width - 1) / 2.0f, (height - 1) / 2.0f, // center
0, 0, // top-left after 2x
- width / 6.0f, height - height / 6.0f , // bottom-left after 3x zoom
- static_cast<float>(width) - 1, height / 2.0f, // middle-right after 1.33x zoom
+ (width - 1) / 6.0f, (height - 1) * 5.0f / 6.0f , // bottom-left after 3x zoom
+ width - 1.0f, (height - 1) / 2.0f, // middle-right after 1.33x zoom
};
coords = originalCoords;
- mapper.scaleCoordinates(coords.data(), coords.size()/2, 2.0f, ZoomRatioMapper::ClampInclusive);
+ mapper.scaleCoordinates(coords.data(), coords.size()/2, 2.0f, true /*clamp*/);
for (size_t i = 0; i < coords.size(); i++) {
EXPECT_LE(std::abs(coords[i] - expected2xCoordsClampedInc[i]), kMaxAllowedPixelError);
}
@@ -224,33 +224,33 @@
// Verify 2.0x zoom work as expected (with exclusive clamping)
std::array<float, 16> expected2xCoordsClampedExc = {
0, 0, // top-left
- static_cast<float>(width), 0, // top-right
- 0, static_cast<float>(height), // bottom-left
- static_cast<float>(width), static_cast<float>(height), // bottom-right
+ width - 1.0f, 0, // top-right
+ 0, height - 1.0f, // bottom-left
+ width - 1.0f, height - 1.0f, // bottom-right
width / 2.0f, height / 2.0f, // center
0, 0, // top-left after 2x
- width / 6.0f, height - height / 6.0f , // bottom-left after 3x zoom
- static_cast<float>(width), height / 2.0f, // middle-right after 1.33x zoom
+ (width - 1) / 6.0f, (height - 1) * 5.0f / 6.0f , // bottom-left after 3x zoom
+ width - 1.0f, height / 2.0f, // middle-right after 1.33x zoom
};
coords = originalCoords;
- mapper.scaleCoordinates(coords.data(), coords.size()/2, 2.0f, ZoomRatioMapper::ClampExclusive);
+ mapper.scaleCoordinates(coords.data(), coords.size()/2, 2.0f, true /*clamp*/);
for (size_t i = 0; i < coords.size(); i++) {
EXPECT_LE(std::abs(coords[i] - expected2xCoordsClampedExc[i]), kMaxAllowedPixelError);
}
// Verify 0.33x zoom work as expected
std::array<float, 16> expectedZoomOutCoords = {
- width / 3.0f, height / 3.0f, // top-left
- width * 2 / 3.0f, height / 3.0f, // top-right
- width / 3.0f, height * 2 / 3.0f, // bottom-left
- width * 2 / 3.0f, height * 2 / 3.0f, // bottom-right
- width / 2.0f, height / 2.0f, // center
- width * 5 / 12.0f, height * 5 / 12.0f, // top-left after 2x
- width * 4 / 9.0f, height * 5 / 9.0f, // bottom-left after 3x zoom-in
- width * 5 / 8.0f, height / 2.0f, // middle-right after 1.33x zoom-in
+ (width - 1) / 3.0f, (height - 1) / 3.0f, // top-left
+ (width - 1) * 2 / 3.0f, (height - 1) / 3.0f, // top-right
+ (width - 1) / 3.0f, (height - 1) * 2 / 3.0f, // bottom-left
+ (width - 1) * 2 / 3.0f, (height - 1) * 2 / 3.0f, // bottom-right
+ (width - 1) / 2.0f, (height - 1) / 2.0f, // center
+ (width - 1) * 5 / 12.0f, (height - 1) * 5 / 12.0f, // top-left after 2x
+ (width - 1) * 4 / 9.0f, (height - 1) * 5 / 9.0f, // bottom-left after 3x zoom-in
+ (width - 1) * 5 / 8.0f, (height - 1) / 2.0f, // middle-right after 1.33x zoom-in
};
coords = originalCoords;
- mapper.scaleCoordinates(coords.data(), coords.size()/2, 1.0f/3, ZoomRatioMapper::ClampOff);
+ mapper.scaleCoordinates(coords.data(), coords.size()/2, 1.0f/3, false /*clamp*/);
for (size_t i = 0; i < coords.size(); i++) {
EXPECT_LE(std::abs(coords[i] - expectedZoomOutCoords[i]), kMaxAllowedPixelError);
}
@@ -323,7 +323,8 @@
entry = metadata.find(ANDROID_SCALER_CROP_REGION);
ASSERT_EQ(entry.count, 4U);
for (int i = 0; i < 4; i++) {
- EXPECT_EQ(entry.data.i32[i], testDefaultCropSize[index][i]);
+ EXPECT_LE(std::abs(entry.data.i32[i] - testDefaultCropSize[index][i]),
+ kMaxAllowedPixelError);
}
entry = metadata.find(ANDROID_CONTROL_ZOOM_RATIO);
EXPECT_NEAR(entry.data.f[0], 2.0f, kMaxAllowedRatioError);
@@ -335,7 +336,7 @@
entry = metadata.find(ANDROID_SCALER_CROP_REGION);
ASSERT_EQ(entry.count, 4U);
for (int i = 0; i < 4; i++) {
- EXPECT_EQ(entry.data.i32[i], test2xCropRegion[index][i]);
+ EXPECT_LE(std::abs(entry.data.i32[i] - test2xCropRegion[index][i]), kMaxAllowedPixelError);
}
// Letter boxing crop region, zoomRatio is 1.0
diff --git a/services/mediametrics/AnalyticsActions.h b/services/mediametrics/AnalyticsActions.h
index 5568c91..0151134 100644
--- a/services/mediametrics/AnalyticsActions.h
+++ b/services/mediametrics/AnalyticsActions.h
@@ -16,6 +16,7 @@
#pragma once
+#include <android-base/thread_annotations.h>
#include <media/MediaMetricsItem.h>
#include <mutex>
@@ -144,7 +145,7 @@
}
mutable std::mutex mLock;
- std::map<Trigger, Action> mFilters; // GUARDED_BY mLock
+ std::map<Trigger, Action> mFilters GUARDED_BY(mLock);
};
} // namespace android::mediametrics
diff --git a/services/mediametrics/Android.bp b/services/mediametrics/Android.bp
index 58f3ea8..fb4022e 100644
--- a/services/mediametrics/Android.bp
+++ b/services/mediametrics/Android.bp
@@ -27,6 +27,7 @@
"-Wall",
"-Werror",
"-Wextra",
+ "-Wthread-safety",
],
}
diff --git a/services/mediametrics/MediaMetricsService.cpp b/services/mediametrics/MediaMetricsService.cpp
index b9703ba..4b84bea 100644
--- a/services/mediametrics/MediaMetricsService.cpp
+++ b/services/mediametrics/MediaMetricsService.cpp
@@ -282,8 +282,8 @@
} else {
result.appendFormat("Dump of the %s process:\n", kServiceName);
const char *prefixptr = prefix.size() > 0 ? prefix.c_str() : nullptr;
- dumpHeaders_l(result, sinceNs, prefixptr);
- dumpQueue_l(result, sinceNs, prefixptr);
+ dumpHeaders(result, sinceNs, prefixptr);
+ dumpQueue(result, sinceNs, prefixptr);
// TODO: maybe consider a better way of dumping audio analytics info.
const int32_t linesToDump = all ? INT32_MAX : 1000;
@@ -312,7 +312,7 @@
}
// dump headers
-void MediaMetricsService::dumpHeaders_l(String8 &result, int64_t sinceNs, const char* prefix)
+void MediaMetricsService::dumpHeaders(String8 &result, int64_t sinceNs, const char* prefix)
{
if (mediametrics::Item::isEnabled()) {
result.append("Metrics gathering: enabled\n");
@@ -337,7 +337,7 @@
}
// TODO: should prefix be a set<string>?
-void MediaMetricsService::dumpQueue_l(String8 &result, int64_t sinceNs, const char* prefix)
+void MediaMetricsService::dumpQueue(String8 &result, int64_t sinceNs, const char* prefix)
{
if (mItems.empty()) {
result.append("empty\n");
@@ -364,7 +364,7 @@
// if item != NULL, it's the item we just inserted
// true == more items eligible to be recovered
-bool MediaMetricsService::expirations_l(const std::shared_ptr<const mediametrics::Item>& item)
+bool MediaMetricsService::expirations(const std::shared_ptr<const mediametrics::Item>& item)
{
bool more = false;
@@ -419,7 +419,7 @@
do {
sleep(1);
std::lock_guard _l(mLock);
- more = expirations_l(nullptr);
+ more = expirations(nullptr);
} while (more);
}
@@ -429,7 +429,7 @@
// we assume the items are roughly in time order.
mItems.emplace_back(item);
++mItemsFinalized;
- if (expirations_l(item)
+ if (expirations(item)
&& (!mExpireFuture.valid()
|| mExpireFuture.wait_for(std::chrono::seconds(0)) == std::future_status::ready)) {
mExpireFuture = std::async(std::launch::async, [this] { processExpirations(); });
diff --git a/services/mediametrics/MediaMetricsService.h b/services/mediametrics/MediaMetricsService.h
index a93f7fb..faba197 100644
--- a/services/mediametrics/MediaMetricsService.h
+++ b/services/mediametrics/MediaMetricsService.h
@@ -23,6 +23,7 @@
#include <unordered_map>
// IMediaMetricsService must include Vector, String16, Errors
+#include <android-base/thread_annotations.h>
#include <media/IMediaMetricsService.h>
#include <mediautils/ServiceUtilities.h>
#include <utils/String8.h>
@@ -81,12 +82,11 @@
bool isRateLimited(mediametrics::Item *) const;
void saveItem(const std::shared_ptr<const mediametrics::Item>& item);
- // The following methods are GUARDED_BY(mLock)
- bool expirations_l(const std::shared_ptr<const mediametrics::Item>& item);
+ bool expirations(const std::shared_ptr<const mediametrics::Item>& item) REQUIRES(mLock);
// support for generating output
- void dumpQueue_l(String8 &result, int64_t sinceNs, const char* prefix);
- void dumpHeaders_l(String8 &result, int64_t sinceNs, const char* prefix);
+ void dumpQueue(String8 &result, int64_t sinceNs, const char* prefix) REQUIRES(mLock);
+ void dumpHeaders(String8 &result, int64_t sinceNs, const char* prefix) REQUIRES(mLock);
// The following variables accessed without mLock
@@ -102,22 +102,22 @@
mediautils::UidInfo mUidInfo; // mUidInfo can be accessed without lock (locked internally)
- mediametrics::AudioAnalytics mAudioAnalytics;
+ mediametrics::AudioAnalytics mAudioAnalytics; // mAudioAnalytics is locked internally.
std::mutex mLock;
// statistics about our analytics
- int64_t mItemsFinalized = 0; // GUARDED_BY(mLock)
- int64_t mItemsDiscarded = 0; // GUARDED_BY(mLock)
- int64_t mItemsDiscardedExpire = 0; // GUARDED_BY(mLock)
- int64_t mItemsDiscardedCount = 0; // GUARDED_BY(mLock)
+ int64_t mItemsFinalized GUARDED_BY(mLock) = 0;
+ int64_t mItemsDiscarded GUARDED_BY(mLock) = 0;
+ int64_t mItemsDiscardedExpire GUARDED_BY(mLock) = 0;
+ int64_t mItemsDiscardedCount GUARDED_BY(mLock) = 0;
// If we have a worker thread to garbage collect
- std::future<void> mExpireFuture; // GUARDED_BY(mLock)
+ std::future<void> mExpireFuture GUARDED_BY(mLock);
// Our item queue, generally (oldest at front)
// TODO: Make separate class, use segmented queue, write lock only end.
// Note: Another analytics module might have ownership of an item longer than the log.
- std::deque<std::shared_ptr<const mediametrics::Item>> mItems; // GUARDED_BY(mLock)
+ std::deque<std::shared_ptr<const mediametrics::Item>> mItems GUARDED_BY(mLock);
};
} // namespace android
diff --git a/services/mediametrics/TimeMachine.h b/services/mediametrics/TimeMachine.h
index 29adeae..c82778b 100644
--- a/services/mediametrics/TimeMachine.h
+++ b/services/mediametrics/TimeMachine.h
@@ -23,6 +23,7 @@
#include <variant>
#include <vector>
+#include <android-base/thread_annotations.h>
#include <media/MediaMetricsItem.h>
#include <utils/Timers.h>
@@ -74,25 +75,35 @@
class KeyHistory {
public:
template <typename T>
- KeyHistory(T key, pid_t pid, uid_t uid, int64_t time)
+ KeyHistory(T key, uid_t allowUid, int64_t time)
: mKey(key)
- , mPid(pid)
- , mUid(uid)
+ , mAllowUid(allowUid)
, mCreationTime(time)
, mLastModificationTime(time)
{
- putValue(BUNDLE_PID, (int32_t)pid, time);
- putValue(BUNDLE_UID, (int32_t)uid, time);
+ // allowUid allows an untrusted client with a matching uid to set properties
+ // in this key.
+ // If allowUid == (uid_t)-1, no untrusted client may set properties in the key.
+ if (allowUid != (uid_t)-1) {
+ // Set ALLOWUID property here; does not change after key creation.
+ putValue(AMEDIAMETRICS_PROP_ALLOWUID, (int32_t)allowUid, time);
+ }
}
KeyHistory(const KeyHistory &other) = default;
+ // Return NO_ERROR only if the passed in uidCheck is -1 or matches
+ // the internal mAllowUid.
+ // An external submit will always have a valid uidCheck parameter.
+ // An internal get request within mediametrics will have a uidCheck == -1 which
+ // we allow to proceed.
status_t checkPermission(uid_t uidCheck) const {
- return uidCheck != (uid_t)-1 && uidCheck != mUid ? PERMISSION_DENIED : NO_ERROR;
+ return uidCheck != (uid_t)-1 && uidCheck != mAllowUid ? PERMISSION_DENIED : NO_ERROR;
}
template <typename T>
- status_t getValue(const std::string &property, T* value, int64_t time = 0) const {
+ status_t getValue(const std::string &property, T* value, int64_t time = 0) const
+ REQUIRES(mPseudoKeyHistoryLock) {
if (time == 0) time = systemTime(SYSTEM_TIME_REALTIME);
const auto tsptr = mPropertyMap.find(property);
if (tsptr == mPropertyMap.end()) return BAD_VALUE;
@@ -108,20 +119,22 @@
}
template <typename T>
- status_t getValue(const std::string &property, T defaultValue, int64_t time = 0) const {
+ status_t getValue(const std::string &property, T defaultValue, int64_t time = 0) const
+ REQUIRES(mPseudoKeyHistoryLock){
T value;
return getValue(property, &value, time) != NO_ERROR ? defaultValue : value;
}
void putProp(
- const std::string &name, const mediametrics::Item::Prop &prop, int64_t time = 0) {
+ const std::string &name, const mediametrics::Item::Prop &prop, int64_t time = 0)
+ REQUIRES(mPseudoKeyHistoryLock) {
//alternatively: prop.visit([&](auto value) { putValue(name, value, time); });
putValue(name, prop.get(), time);
}
template <typename T>
- void putValue(const std::string &property,
- T&& e, int64_t time = 0) {
+ void putValue(const std::string &property, T&& e, int64_t time = 0)
+ REQUIRES(mPseudoKeyHistoryLock) {
if (time == 0) time = systemTime(SYSTEM_TIME_REALTIME);
mLastModificationTime = time;
if (mPropertyMap.size() >= kKeyMaxProperties &&
@@ -134,7 +147,7 @@
if (timeSequence.empty() // no elements
|| property.back() == AMEDIAMETRICS_PROP_SUFFIX_CHAR_DUPLICATES_ALLOWED
|| timeSequence.rbegin()->second != el) { // value changed
- timeSequence.emplace(time, std::move(el));
+ timeSequence.emplace_hint(timeSequence.end(), time, std::move(el));
if (timeSequence.size() > kTimeSequenceMaxElements) {
ALOGV("%s: restricting maximum elements (discarding oldest) for %s",
@@ -144,7 +157,8 @@
}
}
- std::pair<std::string, int32_t> dump(int32_t lines, int64_t time) const {
+ std::pair<std::string, int32_t> dump(int32_t lines, int64_t time) const
+ REQUIRES(mPseudoKeyHistoryLock) {
std::stringstream ss;
int32_t ll = lines;
for (auto& tsPair : mPropertyMap) {
@@ -158,7 +172,9 @@
return { ss.str(), lines - ll };
}
- int64_t getLastModificationTime() const { return mLastModificationTime; }
+ int64_t getLastModificationTime() const REQUIRES(mPseudoKeyHistoryLock) {
+ return mLastModificationTime;
+ }
private:
static std::string dump(
@@ -192,8 +208,7 @@
}
const std::string mKey;
- const pid_t mPid __unused;
- const uid_t mUid;
+ const uid_t mAllowUid;
const int64_t mCreationTime __unused;
int64_t mLastModificationTime;
@@ -267,12 +282,15 @@
if (it == mHistory.end()) {
if (!isTrusted) return PERMISSION_DENIED;
- (void)gc_l(garbage);
+ (void)gc(garbage);
+ // We set the allowUid for client access on key creation.
+ int32_t allowUid = -1;
+ (void)item->get(AMEDIAMETRICS_PROP_ALLOWUID, &allowUid);
// no keylock needed here as we are sole owner
// until placed on mHistory.
keyHistory = std::make_shared<KeyHistory>(
- key, item->getPid(), item->getUid(), time);
+ key, allowUid, time);
mHistory[key] = keyHistory;
} else {
keyHistory = it->second;
@@ -435,7 +453,8 @@
private:
// Obtains the lock for a KeyHistory.
- std::mutex &getLockForKey(const std::string &key) const {
+ std::mutex &getLockForKey(const std::string &key) const
+ RETURN_CAPABILITY(mPseudoKeyHistoryLock) {
return mKeyLocks[std::hash<std::string>{}(key) % std::size(mKeyLocks)];
}
@@ -459,7 +478,6 @@
return it->second;
}
- // GUARDED_BY mLock
/**
* Garbage collects if the TimeMachine size exceeds the high water mark.
*
@@ -471,7 +489,7 @@
*
* \return true if garbage collection was done.
*/
- bool gc_l(std::vector<std::any>& garbage) {
+ bool gc(std::vector<std::any>& garbage) REQUIRES(mLock) {
// TODO: something better than this for garbage collection.
if (mHistory.size() < mKeyHighWaterMark) return false;
@@ -540,12 +558,17 @@
*/
mutable std::mutex mLock; // Lock for mHistory
- History mHistory; // GUARDED_BY mLock
+ History mHistory GUARDED_BY(mLock);
// KEY_LOCKS is the number of mutexes for keys.
// It need not be a power of 2, but faster that way.
static inline constexpr size_t KEY_LOCKS = 256;
mutable std::mutex mKeyLocks[KEY_LOCKS]; // Hash-striped lock for KeyHistory based on key.
+
+ // Used for thread-safety analysis, we create a fake mutex object to represent
+ // the hash stripe lock mechanism, which is then tracked by the compiler.
+ class CAPABILITY("mutex") PseudoLock {};
+ static inline PseudoLock mPseudoKeyHistoryLock;
};
} // namespace android::mediametrics
diff --git a/services/mediametrics/TransactionLog.h b/services/mediametrics/TransactionLog.h
index 4f09bb0..8a22826 100644
--- a/services/mediametrics/TransactionLog.h
+++ b/services/mediametrics/TransactionLog.h
@@ -21,6 +21,7 @@
#include <sstream>
#include <string>
+#include <android-base/thread_annotations.h>
#include <media/MediaMetricsItem.h>
namespace android::mediametrics {
@@ -92,9 +93,9 @@
std::vector<std::any> garbage; // objects destroyed after lock.
std::lock_guard lock(mLock);
- (void)gc_l(garbage);
- mLog.emplace(time, item);
- mItemMap[key].emplace(time, item);
+ (void)gc(garbage);
+ mLog.emplace_hint(mLog.end(), time, item);
+ mItemMap[key].emplace_hint(mItemMap[key].end(), time, item);
return NO_ERROR; // no errors for now.
}
@@ -104,7 +105,7 @@
std::vector<std::shared_ptr<const mediametrics::Item>> get(
int64_t startTime = 0, int64_t endTime = INT64_MAX) const {
std::lock_guard lock(mLock);
- return getItemsInRange_l(mLog, startTime, endTime);
+ return getItemsInRange(mLog, startTime, endTime);
}
/**
@@ -116,7 +117,7 @@
std::lock_guard lock(mLock);
auto mapIt = mItemMap.find(key);
if (mapIt == mItemMap.end()) return {};
- return getItemsInRange_l(mapIt->second, startTime, endTime);
+ return getItemsInRange(mapIt->second, startTime, endTime);
}
/**
@@ -204,7 +205,6 @@
return { ss.str(), lines - ll };
}
- // GUARDED_BY mLock
/**
* Garbage collects if the TimeMachine size exceeds the high water mark.
*
@@ -213,7 +213,7 @@
*
* \return true if garbage collection was done.
*/
- bool gc_l(std::vector<std::any>& garbage) {
+ bool gc(std::vector<std::any>& garbage) REQUIRES(mLock) {
if (mLog.size() < mHighWaterMark) return false;
ALOGD("%s: garbage collection", __func__);
@@ -268,7 +268,7 @@
return true;
}
- static std::vector<std::shared_ptr<const mediametrics::Item>> getItemsInRange_l(
+ static std::vector<std::shared_ptr<const mediametrics::Item>> getItemsInRange(
const MapTimeItem& map,
int64_t startTime = 0, int64_t endTime = INT64_MAX) {
auto it = map.lower_bound(startTime);
@@ -289,11 +289,8 @@
mutable std::mutex mLock;
- // GUARDED_BY mLock
- MapTimeItem mLog;
-
- // GUARDED_BY mLock
- std::map<std::string /* item_key */, MapTimeItem> mItemMap;
+ MapTimeItem mLog GUARDED_BY(mLock);
+ std::map<std::string /* item_key */, MapTimeItem> mItemMap GUARDED_BY(mLock);
};
} // namespace android::mediametrics
diff --git a/services/mediametrics/tests/mediametrics_tests.cpp b/services/mediametrics/tests/mediametrics_tests.cpp
index 78eb71c..cf0dceb 100644
--- a/services/mediametrics/tests/mediametrics_tests.cpp
+++ b/services/mediametrics/tests/mediametrics_tests.cpp
@@ -813,6 +813,42 @@
ASSERT_LT(9, audioAnalytics.dump(1000).second /* lines */);
}
+TEST(mediametrics_tests, audio_analytics_permission2) {
+ constexpr int32_t transactionUid = 1010; // arbitrary
+ auto item = std::make_shared<mediametrics::Item>("audio.1");
+ (*item).set("one", (int32_t)1)
+ .set("two", (int32_t)2)
+ .set(AMEDIAMETRICS_PROP_ALLOWUID, transactionUid)
+ .setTimestamp(10);
+
+ // item2 submitted untrusted
+ auto item2 = std::make_shared<mediametrics::Item>("audio.1");
+ (*item2).set("three", (int32_t)3)
+ .setUid(transactionUid)
+ .setTimestamp(11);
+
+ auto item3 = std::make_shared<mediametrics::Item>("audio.2");
+ (*item3).set("four", (int32_t)4)
+ .setTimestamp(12);
+
+ android::mediametrics::AudioAnalytics audioAnalytics;
+
+ // untrusted entities cannot create a new key.
+ ASSERT_EQ(PERMISSION_DENIED, audioAnalytics.submit(item, false /* isTrusted */));
+ ASSERT_EQ(PERMISSION_DENIED, audioAnalytics.submit(item2, false /* isTrusted */));
+
+ // TODO: Verify contents of AudioAnalytics.
+ // Currently there is no getter API in AudioAnalytics besides dump.
+ ASSERT_EQ(9, audioAnalytics.dump(1000).second /* lines */);
+
+ ASSERT_EQ(NO_ERROR, audioAnalytics.submit(item, true /* isTrusted */));
+ // untrusted entities can add to an existing key
+ ASSERT_EQ(NO_ERROR, audioAnalytics.submit(item2, false /* isTrusted */));
+
+ // Check that we have some info in the dump.
+ ASSERT_LT(9, audioAnalytics.dump(1000).second /* lines */);
+}
+
TEST(mediametrics_tests, audio_analytics_dump) {
auto item = std::make_shared<mediametrics::Item>("audio.1");
(*item).set("one", (int32_t)1)
diff --git a/services/mediaresourcemanager/ResourceManagerService.cpp b/services/mediaresourcemanager/ResourceManagerService.cpp
index be5af00..ff45c87 100644
--- a/services/mediaresourcemanager/ResourceManagerService.cpp
+++ b/services/mediaresourcemanager/ResourceManagerService.cpp
@@ -114,6 +114,7 @@
info.uid = uid;
info.clientId = clientId;
info.client = client;
+ info.pendingRemoval = false;
index = infos.add(clientId, info);
}
@@ -648,6 +649,36 @@
return Status::ok();
}
+Status ResourceManagerService::markClientForPendingRemoval(int32_t pid, int64_t clientId) {
+ String8 log = String8::format(
+ "markClientForPendingRemoval(pid %d, clientId %lld)",
+ pid, (long long) clientId);
+ mServiceLog->add(log);
+
+ Mutex::Autolock lock(mLock);
+ if (!mProcessInfo->isValidPid(pid)) {
+ ALOGE("Rejected markClientForPendingRemoval call with invalid pid.");
+ return Status::fromServiceSpecificError(BAD_VALUE);
+ }
+ ssize_t index = mMap.indexOfKey(pid);
+ if (index < 0) {
+ ALOGV("markClientForPendingRemoval: didn't find pid %d for clientId %lld",
+ pid, (long long)clientId);
+ return Status::ok();
+ }
+ ResourceInfos &infos = mMap.editValueAt(index);
+
+ index = infos.indexOfKey(clientId);
+ if (index < 0) {
+ ALOGV("markClientForPendingRemoval: didn't find clientId %lld", (long long) clientId);
+ return Status::ok();
+ }
+
+ ResourceInfo &info = infos.editValueAt(index);
+ info.pendingRemoval = true;
+ return Status::ok();
+}
+
bool ResourceManagerService::getPriority_l(int pid, int* priority) {
int newPid = pid;
@@ -693,6 +724,12 @@
int lowestPriorityPid;
int lowestPriority;
int callingPriority;
+
+ // Before looking into other processes, check if we have clients marked for
+ // pending removal in the same process.
+ if (getBiggestClient_l(callingPid, type, client, true /* pendingRemovalOnly */)) {
+ return true;
+ }
if (!getPriority_l(callingPid, &callingPriority)) {
ALOGE("getLowestPriorityBiggestClient_l: can't get process priority for pid %d",
callingPid);
@@ -761,7 +798,8 @@
}
bool ResourceManagerService::getBiggestClient_l(
- int pid, MediaResource::Type type, std::shared_ptr<IResourceManagerClient> *client) {
+ int pid, MediaResource::Type type, std::shared_ptr<IResourceManagerClient> *client,
+ bool pendingRemovalOnly) {
ssize_t index = mMap.indexOfKey(pid);
if (index < 0) {
ALOGE("getBiggestClient_l: can't find resource info for pid %d", pid);
@@ -773,6 +811,9 @@
const ResourceInfos &infos = mMap.valueAt(index);
for (size_t i = 0; i < infos.size(); ++i) {
const ResourceList &resources = infos[i].resources;
+ if (pendingRemovalOnly && !infos[i].pendingRemoval) {
+ continue;
+ }
for (auto it = resources.begin(); it != resources.end(); it++) {
const MediaResourceParcel &resource = it->second;
if (resource.type == type) {
diff --git a/services/mediaresourcemanager/ResourceManagerService.h b/services/mediaresourcemanager/ResourceManagerService.h
index f500c62..ee982b7 100644
--- a/services/mediaresourcemanager/ResourceManagerService.h
+++ b/services/mediaresourcemanager/ResourceManagerService.h
@@ -18,6 +18,8 @@
#ifndef ANDROID_MEDIA_RESOURCEMANAGERSERVICE_H
#define ANDROID_MEDIA_RESOURCEMANAGERSERVICE_H
+#include <map>
+
#include <aidl/android/media/BnResourceManagerService.h>
#include <arpa/inet.h>
#include <media/MediaResource.h>
@@ -50,6 +52,7 @@
std::shared_ptr<IResourceManagerClient> client;
sp<DeathNotifier> deathNotifier;
ResourceList resources;
+ bool pendingRemoval{false};
};
// TODO: convert these to std::map
@@ -122,6 +125,8 @@
int originalPid,
int newPid) override;
+ Status markClientForPendingRemoval(int32_t pid, int64_t clientId) override;
+
Status removeResource(int pid, int64_t clientId, bool checkValid);
private:
@@ -146,7 +151,8 @@
// Gets the client who owns biggest piece of specified resource type from pid.
// Returns false if failed. The client will remain unchanged if failed.
bool getBiggestClient_l(int pid, MediaResource::Type type,
- std::shared_ptr<IResourceManagerClient> *client);
+ std::shared_ptr<IResourceManagerClient> *client,
+ bool pendingRemovalOnly = false);
bool isCallingPriorityHigher_l(int callingPid, int pid);
diff --git a/services/mediaresourcemanager/test/ResourceManagerService_test.cpp b/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
index 5d839fa..702935d 100644
--- a/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
+++ b/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
@@ -472,6 +472,56 @@
}
}
+ void testMarkClientForPendingRemoval() {
+ bool result;
+
+ {
+ addResource();
+ mService->mSupportsSecureWithNonSecureCodec = true;
+
+ std::vector<MediaResourceParcel> resources;
+ resources.push_back(MediaResource(MediaResource::Type::kNonSecureCodec, 1));
+
+ // Remove low priority clients
+ mService->removeClient(kTestPid1, getId(mTestClient1));
+
+ // no lower priority client
+ CHECK_STATUS_FALSE(mService->reclaimResource(kTestPid2, resources, &result));
+ verifyClients(false /* c1 */, false /* c2 */, false /* c3 */);
+
+ mService->markClientForPendingRemoval(kTestPid2, getId(mTestClient2));
+
+ // client marked for pending removal from the same process got reclaimed
+ CHECK_STATUS_TRUE(mService->reclaimResource(kTestPid2, resources, &result));
+ verifyClients(false /* c1 */, true /* c2 */, false /* c3 */);
+
+ // clean up client 3 which still left
+ mService->removeClient(kTestPid2, getId(mTestClient3));
+ }
+
+ {
+ addResource();
+ mService->mSupportsSecureWithNonSecureCodec = true;
+
+ std::vector<MediaResourceParcel> resources;
+ resources.push_back(MediaResource(MediaResource::Type::kNonSecureCodec, 1));
+
+ mService->markClientForPendingRemoval(kTestPid2, getId(mTestClient2));
+
+ // client marked for pending removal from the same process got reclaimed
+ // first, even though there are lower priority process
+ CHECK_STATUS_TRUE(mService->reclaimResource(kTestPid2, resources, &result));
+ verifyClients(false /* c1 */, true /* c2 */, false /* c3 */);
+
+ // lower priority client got reclaimed
+ CHECK_STATUS_TRUE(mService->reclaimResource(kTestPid2, resources, &result));
+ verifyClients(true /* c1 */, false /* c2 */, false /* c3 */);
+
+ // clean up client 3 which still left
+ mService->removeClient(kTestPid2, getId(mTestClient3));
+ }
+ }
+
void testRemoveClient() {
addResource();
@@ -900,4 +950,8 @@
testOverridePid();
}
+TEST_F(ResourceManagerServiceTest, markClientForPendingRemoval) {
+ testMarkClientForPendingRemoval();
+}
+
} // namespace android
diff --git a/services/oboeservice/AAudioService.cpp b/services/oboeservice/AAudioService.cpp
index aba8e08..cac7453 100644
--- a/services/oboeservice/AAudioService.cpp
+++ b/services/oboeservice/AAudioService.cpp
@@ -144,6 +144,9 @@
pid_t pid = request.getProcessId();
AAudioClientTracker::getInstance().registerClientStream(pid, serviceStream);
configurationOutput.copyFrom(*serviceStream);
+ // Log open in MediaMetrics after we have the handle because we need the handle to
+ // create the metrics ID.
+ serviceStream->logOpen(handle);
return handle;
}
}
diff --git a/services/oboeservice/AAudioService.h b/services/oboeservice/AAudioService.h
index eddb5a0..7167868 100644
--- a/services/oboeservice/AAudioService.h
+++ b/services/oboeservice/AAudioService.h
@@ -115,6 +115,7 @@
// and share them with this code. Look for "kPriorityFastMixer".
static constexpr int32_t kRealTimeAudioPriorityClient = 2;
static constexpr int32_t kRealTimeAudioPriorityService = 3;
+
};
} /* namespace android */
diff --git a/services/oboeservice/AAudioServiceEndpoint.h b/services/oboeservice/AAudioServiceEndpoint.h
index a6bb019..ad30087 100644
--- a/services/oboeservice/AAudioServiceEndpoint.h
+++ b/services/oboeservice/AAudioServiceEndpoint.h
@@ -109,6 +109,8 @@
return mConnected;
}
+ static audio_attributes_t getAudioAttributesFrom(const AAudioStreamParameters *params);
+
protected:
/**
@@ -119,8 +121,6 @@
void disconnectRegisteredStreams();
- static audio_attributes_t getAudioAttributesFrom(const AAudioStreamParameters *params);
-
mutable std::mutex mLockStreams;
std::vector<android::sp<AAudioServiceStreamBase>> mRegisteredStreams;
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index 39e90b1..dba9fb9 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -22,8 +22,12 @@
#include <iostream>
#include <mutex>
+#include <media/MediaMetricsItem.h>
+#include <media/TypeConverter.h>
+
#include "binding/IAAudioService.h"
#include "binding/AAudioServiceMessage.h"
+#include "core/AudioGlobal.h"
#include "utility/AudioClock.h"
#include "AAudioEndpointManager.h"
@@ -51,6 +55,14 @@
}
AAudioServiceStreamBase::~AAudioServiceStreamBase() {
+ // May not be set if open failed.
+ if (mMetricsId.size() > 0) {
+ mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_DTOR)
+ .set(AMEDIAMETRICS_PROP_STATE, AudioGlobal_convertStreamStateToText(getState()))
+ .record();
+ }
+
// If the stream is deleted when OPEN or in use then audio resources will leak.
// This would indicate an internal error. So we want to find this ASAP.
LOG_ALWAYS_FATAL_IF(!(getState() == AAUDIO_STREAM_STATE_CLOSED
@@ -81,6 +93,36 @@
return result.str();
}
+void AAudioServiceStreamBase::logOpen(aaudio_handle_t streamHandle) {
+ // This is the first log sent from the AAudio Service for a stream.
+ mMetricsId = std::string(AMEDIAMETRICS_KEY_PREFIX_AUDIO_STREAM)
+ + std::to_string(streamHandle);
+
+ audio_attributes_t attributes = AAudioServiceEndpoint::getAudioAttributesFrom(this);
+
+ // Once this item is logged by the server, the client with the same PID, UID
+ // can also log properties.
+ mediametrics::LogItem(mMetricsId)
+ .setPid(getOwnerProcessId())
+ .setUid(getOwnerUserId())
+ .set(AMEDIAMETRICS_PROP_ALLOWUID, (int32_t)getOwnerUserId())
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_OPEN)
+ // the following are immutable
+ .set(AMEDIAMETRICS_PROP_BUFFERCAPACITYFRAMES, (int32_t)getBufferCapacity())
+ .set(AMEDIAMETRICS_PROP_BURSTFRAMES, (int32_t)getFramesPerBurst())
+ .set(AMEDIAMETRICS_PROP_CHANNELCOUNT, (int32_t)getSamplesPerFrame())
+ .set(AMEDIAMETRICS_PROP_CONTENTTYPE, toString(attributes.content_type).c_str())
+ .set(AMEDIAMETRICS_PROP_DIRECTION,
+ AudioGlobal_convertDirectionToText(getDirection()))
+ .set(AMEDIAMETRICS_PROP_ENCODING, toString(getFormat()).c_str())
+ .set(AMEDIAMETRICS_PROP_ROUTEDDEVICEID, (int32_t)getDeviceId())
+ .set(AMEDIAMETRICS_PROP_SAMPLERATE, (int32_t)getSampleRate())
+ .set(AMEDIAMETRICS_PROP_SESSIONID, (int32_t)getSessionId())
+ .set(AMEDIAMETRICS_PROP_SOURCE, toString(attributes.source).c_str())
+ .set(AMEDIAMETRICS_PROP_USAGE, toString(attributes.usage).c_str())
+ .record();
+}
+
aaudio_result_t AAudioServiceStreamBase::open(const aaudio::AAudioStreamRequest &request) {
AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance();
aaudio_result_t result = AAUDIO_OK;
@@ -154,6 +196,10 @@
}
setState(AAUDIO_STREAM_STATE_CLOSED);
+
+ mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_CLOSE)
+ .record();
return result;
}
@@ -173,8 +219,17 @@
* An AAUDIO_SERVICE_EVENT_STARTED will be sent to the client when complete.
*/
aaudio_result_t AAudioServiceStreamBase::start() {
+ const int64_t beginNs = AudioClock::getNanoseconds();
aaudio_result_t result = AAUDIO_OK;
+ mediametrics::Defer defer([&] {
+ mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_START)
+ .set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(AudioClock::getNanoseconds() - beginNs))
+ .set(AMEDIAMETRICS_PROP_STATE, AudioGlobal_convertStreamStateToText(getState()))
+ .set(AMEDIAMETRICS_PROP_STATUS, (int32_t)result)
+ .record(); });
+
if (isRunning()) {
return AAUDIO_OK;
}
@@ -204,11 +259,20 @@
}
aaudio_result_t AAudioServiceStreamBase::pause() {
+ const int64_t beginNs = AudioClock::getNanoseconds();
aaudio_result_t result = AAUDIO_OK;
if (!isRunning()) {
return result;
}
+ mediametrics::Defer defer([&] {
+ mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_PAUSE)
+ .set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(AudioClock::getNanoseconds() - beginNs))
+ .set(AMEDIAMETRICS_PROP_STATE, AudioGlobal_convertStreamStateToText(getState()))
+ .set(AMEDIAMETRICS_PROP_STATUS, (int32_t)result)
+ .record(); });
+
// Send it now because the timestamp gets rounded up when stopStream() is called below.
// Also we don't need the timestamps while we are shutting down.
sendCurrentTimestamp();
@@ -222,7 +286,8 @@
sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
if (endpoint == nullptr) {
ALOGE("%s() has no endpoint", __func__);
- return AAUDIO_ERROR_INVALID_STATE;
+ result = AAUDIO_ERROR_INVALID_STATE; // for MediaMetric tracking
+ return result;
}
result = endpoint->stopStream(this, mClientHandle);
if (result != AAUDIO_OK) {
@@ -236,11 +301,20 @@
}
aaudio_result_t AAudioServiceStreamBase::stop() {
+ const int64_t beginNs = AudioClock::getNanoseconds();
aaudio_result_t result = AAUDIO_OK;
if (!isRunning()) {
return result;
}
+ mediametrics::Defer defer([&] {
+ mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_STOP)
+ .set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(AudioClock::getNanoseconds() - beginNs))
+ .set(AMEDIAMETRICS_PROP_STATE, AudioGlobal_convertStreamStateToText(getState()))
+ .set(AMEDIAMETRICS_PROP_STATUS, (int32_t)result)
+ .record(); });
+
setState(AAUDIO_STREAM_STATE_STOPPING);
// Send it now because the timestamp gets rounded up when stopStream() is called below.
@@ -255,7 +329,8 @@
sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
if (endpoint == nullptr) {
ALOGE("%s() has no endpoint", __func__);
- return AAUDIO_ERROR_INVALID_STATE;
+ result = AAUDIO_ERROR_INVALID_STATE; // for MediaMetric tracking
+ return result;
}
// TODO wait for data to be played out
result = endpoint->stopStream(this, mClientHandle);
@@ -280,11 +355,20 @@
}
aaudio_result_t AAudioServiceStreamBase::flush() {
+ const int64_t beginNs = AudioClock::getNanoseconds();
aaudio_result_t result = AAudio_isFlushAllowed(getState());
if (result != AAUDIO_OK) {
return result;
}
+ mediametrics::Defer defer([&] {
+ mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_FLUSH)
+ .set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(AudioClock::getNanoseconds() - beginNs))
+ .set(AMEDIAMETRICS_PROP_STATE, AudioGlobal_convertStreamStateToText(getState()))
+ .set(AMEDIAMETRICS_PROP_STATUS, (int32_t)result)
+ .record(); });
+
// Data will get flushed when the client receives the FLUSHED event.
sendServiceEvent(AAUDIO_SERVICE_EVENT_FLUSHED);
setState(AAUDIO_STREAM_STATE_FLUSHED);
@@ -321,6 +405,10 @@
void AAudioServiceStreamBase::disconnect() {
if (getState() != AAUDIO_STREAM_STATE_DISCONNECTED) {
+ mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_DISCONNECT)
+ .set(AMEDIAMETRICS_PROP_STATE, AudioGlobal_convertStreamStateToText(getState()))
+ .record();
sendServiceEvent(AAUDIO_SERVICE_EVENT_DISCONNECTED);
setState(AAUDIO_STREAM_STATE_DISCONNECTED);
}
diff --git a/services/oboeservice/AAudioServiceStreamBase.h b/services/oboeservice/AAudioServiceStreamBase.h
index aaab567..79dd738 100644
--- a/services/oboeservice/AAudioServiceStreamBase.h
+++ b/services/oboeservice/AAudioServiceStreamBase.h
@@ -68,12 +68,15 @@
// does not include EOL
virtual std::string dump() const;
- // -------------------------------------------------------------------
/**
* Open the device.
*/
virtual aaudio_result_t open(const aaudio::AAudioStreamRequest &request) = 0;
+ // We log the CLOSE from the close() method. We needed this separate method to log the OPEN
+ // because we had to wait until we generated the handle.
+ void logOpen(aaudio_handle_t streamHandle);
+
virtual aaudio_result_t close();
/**
@@ -312,6 +315,8 @@
android::sp<AAudioServiceEndpoint> mServiceEndpoint;
android::wp<AAudioServiceEndpoint> mServiceEndpointWeak;
+ std::string mMetricsId; // set once during open()
+
private:
/**
diff --git a/services/oboeservice/Android.bp b/services/oboeservice/Android.bp
index 64d835b..8b1e2c0 100644
--- a/services/oboeservice/Android.bp
+++ b/services/oboeservice/Android.bp
@@ -47,11 +47,13 @@
"libaudioclient",
"libaudioflinger",
"libaudioutils",
+ "libmedia_helper",
+ "libmediametrics",
+ "libmediautils",
"libbase",
"libbinder",
"libcutils",
"liblog",
- "libmediautils",
"libutils",
],