Merge Android R (rvc-dev-plus-aosp-without-vendor@6692709)
Bug: 166295507
Merged-In: Ie94127c1e981cb18671d5137ba0d39355d1353da
Change-Id: Ib22ad33bda8d561af537499b6d31ab6134eeae02
diff --git a/apex/manifest_codec.json b/apex/manifest_codec.json
index 2320fd7..1f05d2e 100644
--- a/apex/manifest_codec.json
+++ b/apex/manifest_codec.json
@@ -1,4 +1,7 @@
{
"name": "com.android.media.swcodec",
- "version": 300000000
+ "version": 300000000,
+ "requireNativeLibs": [
+ ":sphal"
+ ]
}
diff --git a/drm/common/include/DrmEngineBase.h b/drm/common/include/DrmEngineBase.h
index 73f11a4..c0a5e3b 100644
--- a/drm/common/include/DrmEngineBase.h
+++ b/drm/common/include/DrmEngineBase.h
@@ -309,7 +309,7 @@
/**
* Removes all the rights information of each plug-in associated with
- * DRM framework. Will be used in master reset
+ * DRM framework.
*
* @param[in] uniqueId Unique identifier for a session
* @return status_t
diff --git a/drm/common/include/IDrmEngine.h b/drm/common/include/IDrmEngine.h
index 1837a11..a545941 100644
--- a/drm/common/include/IDrmEngine.h
+++ b/drm/common/include/IDrmEngine.h
@@ -250,7 +250,7 @@
/**
* Removes all the rights information of each plug-in associated with
- * DRM framework. Will be used in master reset
+ * DRM framework.
*
* @param[in] uniqueId Unique identifier for a session
* @return status_t
diff --git a/drm/libdrmframework/include/DrmManagerClientImpl.h b/drm/libdrmframework/include/DrmManagerClientImpl.h
index 3858675..8c8783b 100644
--- a/drm/libdrmframework/include/DrmManagerClientImpl.h
+++ b/drm/libdrmframework/include/DrmManagerClientImpl.h
@@ -230,7 +230,7 @@
/**
* Removes all the rights information of each plug-in associated with
- * DRM framework. Will be used in master reset
+ * DRM framework.
*
* @param[in] uniqueId Unique identifier for a session
* @return status_t
diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/include/FwdLockEngine.h b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/include/FwdLockEngine.h
index b62ddb9..eb5b0f6 100644
--- a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/include/FwdLockEngine.h
+++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/include/FwdLockEngine.h
@@ -252,8 +252,7 @@
/**
* Removes all the rights information of each plug-in associated with
- * DRM framework. Will be used in master reset but does nothing for
- * Forward Lock Engine.
+ * DRM framework. Does nothing for Forward Lock Engine.
*
* @param uniqueId Unique identifier for a session
* @return status_t
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/FwdLock.html b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/FwdLock.html
index 8f95cd2..c1d5b3d 100644
--- a/drm/libdrmframework/plugins/forward-lock/internal-format/doc/FwdLock.html
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/doc/FwdLock.html
@@ -488,7 +488,7 @@
<p class=MsoBodyText><b>Note:</b> The key-encryption key must be unique to each
device; this is what makes the files forward lockÂprotected. Ideally, it should
be derived from secret hardware parameters, but at the very least it should be
-persistent from one master reset to the next.</p>
+persistent from one factory reset to the next.</p>
<div style='margin-bottom:24.0pt;border:solid windowtext 1.0pt;padding:1.0pt 4.0pt 1.0pt 4.0pt;
background:#F2F2F2'>
diff --git a/drm/mediacas/plugins/clearkey/ClearKeyFetcher.cpp b/drm/mediacas/plugins/clearkey/ClearKeyFetcher.cpp
index cb69f91..466e571 100644
--- a/drm/mediacas/plugins/clearkey/ClearKeyFetcher.cpp
+++ b/drm/mediacas/plugins/clearkey/ClearKeyFetcher.cpp
@@ -62,8 +62,8 @@
}
ALOGV("descriptor_size=%zu", container.descriptor_size());
- // Sanity check to verify that the BroadcastEncryptor is sending a properly
- // formed EcmContainer. If it contains two Ecms, the ids should have different
+ // Validate that the BroadcastEncryptor is sending a properly formed
+ // EcmContainer. If it contains two Ecms, the ids should have different
// parity (one odd, one even). This does not necessarily affect decryption
// but indicates a problem with Ecm generation.
if (container.descriptor_size() == 2) {
diff --git a/include/drm/DrmManagerClient.h b/include/drm/DrmManagerClient.h
index 866edac..a38aa9b 100644
--- a/include/drm/DrmManagerClient.h
+++ b/include/drm/DrmManagerClient.h
@@ -318,7 +318,7 @@
/**
* Removes all the rights information of each plug-in associated with
- * DRM framework. Will be used in master reset
+ * DRM framework.
*
* @return status_t
* Returns DRM_NO_ERROR for success, DRM_ERROR_UNKNOWN for failure
diff --git a/media/OWNERS b/media/OWNERS
index 1afc253..bd83ad9 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -8,7 +8,6 @@
hunga@google.com
jiabin@google.com
jmtrivi@google.com
-krocard@google.com
lajos@google.com
marcone@google.com
mnaganov@google.com
diff --git a/media/codec2/components/aom/C2SoftAomDec.cpp b/media/codec2/components/aom/C2SoftAomDec.cpp
index c7046cb..9ba3b697 100644
--- a/media/codec2/components/aom/C2SoftAomDec.cpp
+++ b/media/codec2/components/aom/C2SoftAomDec.cpp
@@ -506,30 +506,28 @@
}
static void copyOutputBufferToYuvPlanarFrame(
- uint8_t *dst, const uint8_t *srcY, const uint8_t *srcU, const uint8_t *srcV,
+ uint8_t *dstY, uint8_t *dstU, uint8_t *dstV,
+ const uint8_t *srcY, const uint8_t *srcU, const uint8_t *srcV,
size_t srcYStride, size_t srcUStride, size_t srcVStride,
size_t dstYStride, size_t dstUVStride,
uint32_t width, uint32_t height) {
- uint8_t* dstStart = dst;
for (size_t i = 0; i < height; ++i) {
- memcpy(dst, srcY, width);
+ memcpy(dstY, srcY, width);
srcY += srcYStride;
- dst += dstYStride;
+ dstY += dstYStride;
}
- dst = dstStart + dstYStride * height;
for (size_t i = 0; i < height / 2; ++i) {
- memcpy(dst, srcV, width / 2);
+ memcpy(dstV, srcV, width / 2);
srcV += srcVStride;
- dst += dstUVStride;
+ dstV += dstUVStride;
}
- dst = dstStart + (dstYStride * height) + (dstUVStride * height / 2);
for (size_t i = 0; i < height / 2; ++i) {
- memcpy(dst, srcU, width / 2);
+ memcpy(dstU, srcU, width / 2);
srcU += srcUStride;
- dst += dstUVStride;
+ dstU += dstUVStride;
}
}
@@ -596,16 +594,12 @@
return;
}
-static void convertYUV420Planar16ToYUV420Planar(uint8_t *dst,
+static void convertYUV420Planar16ToYUV420Planar(
+ uint8_t *dstY, uint8_t *dstU, uint8_t *dstV,
const uint16_t *srcY, const uint16_t *srcU, const uint16_t *srcV,
size_t srcYStride, size_t srcUStride, size_t srcVStride,
- size_t dstYStride, size_t dstUVStride, size_t width, size_t height) {
-
- uint8_t *dstY = (uint8_t *)dst;
- size_t dstYSize = dstYStride * height;
- size_t dstUVSize = dstUVStride * height / 2;
- uint8_t *dstV = dstY + dstYSize;
- uint8_t *dstU = dstV + dstUVSize;
+ size_t dstYStride, size_t dstUVStride,
+ size_t width, size_t height) {
for (size_t y = 0; y < height; ++y) {
for (size_t x = 0; x < width; ++x) {
@@ -696,7 +690,9 @@
block->width(), block->height(), mWidth, mHeight,
(int)*(int64_t*)img->user_priv);
- uint8_t* dst = const_cast<uint8_t*>(wView.data()[C2PlanarLayout::PLANE_Y]);
+ uint8_t* dstY = const_cast<uint8_t*>(wView.data()[C2PlanarLayout::PLANE_Y]);
+ uint8_t* dstU = const_cast<uint8_t*>(wView.data()[C2PlanarLayout::PLANE_U]);
+ uint8_t* dstV = const_cast<uint8_t*>(wView.data()[C2PlanarLayout::PLANE_V]);
size_t srcYStride = img->stride[AOM_PLANE_Y];
size_t srcUStride = img->stride[AOM_PLANE_U];
size_t srcVStride = img->stride[AOM_PLANE_V];
@@ -710,13 +706,14 @@
const uint16_t *srcV = (const uint16_t *)img->planes[AOM_PLANE_V];
if (format == HAL_PIXEL_FORMAT_RGBA_1010102) {
- convertYUV420Planar16ToY410((uint32_t *)dst, srcY, srcU, srcV, srcYStride / 2,
+ convertYUV420Planar16ToY410((uint32_t *)dstY, srcY, srcU, srcV, srcYStride / 2,
srcUStride / 2, srcVStride / 2,
dstYStride / sizeof(uint32_t),
mWidth, mHeight);
} else {
- convertYUV420Planar16ToYUV420Planar(dst, srcY, srcU, srcV, srcYStride / 2,
- srcUStride / 2, srcVStride / 2,
+ convertYUV420Planar16ToYUV420Planar(dstY, dstU, dstV,
+ srcY, srcU, srcV,
+ srcYStride / 2, srcUStride / 2, srcVStride / 2,
dstYStride, dstUVStride,
mWidth, mHeight);
}
@@ -725,7 +722,7 @@
const uint8_t *srcU = (const uint8_t *)img->planes[AOM_PLANE_U];
const uint8_t *srcV = (const uint8_t *)img->planes[AOM_PLANE_V];
copyOutputBufferToYuvPlanarFrame(
- dst, srcY, srcU, srcV,
+ dstY, dstU, dstV, srcY, srcU, srcV,
srcYStride, srcUStride, srcVStride,
dstYStride, dstUVStride,
mWidth, mHeight);
diff --git a/media/codec2/components/avc/C2SoftAvcDec.cpp b/media/codec2/components/avc/C2SoftAvcDec.cpp
index d7b9e12..3afd670 100644
--- a/media/codec2/components/avc/C2SoftAvcDec.cpp
+++ b/media/codec2/components/avc/C2SoftAvcDec.cpp
@@ -34,7 +34,11 @@
constexpr size_t kMinInputBufferSize = 2 * 1024 * 1024;
constexpr char COMPONENT_NAME[] = "c2.android.avc.decoder";
constexpr uint32_t kDefaultOutputDelay = 8;
-constexpr uint32_t kMaxOutputDelay = 16;
+/* avc specification allows for a maximum delay of 16 frames.
+ As soft avc decoder supports interlaced, this delay would be 32 fields.
+ And avc decoder implementation has an additional delay of 2 decode calls.
+ So total maximum output delay is 34 */
+constexpr uint32_t kMaxOutputDelay = 34;
constexpr uint32_t kMinInputBytes = 4;
} // namespace
diff --git a/media/codec2/components/gav1/C2SoftGav1Dec.cpp b/media/codec2/components/gav1/C2SoftGav1Dec.cpp
index ec5f549..5dffa50 100644
--- a/media/codec2/components/gav1/C2SoftGav1Dec.cpp
+++ b/media/codec2/components/gav1/C2SoftGav1Dec.cpp
@@ -470,33 +470,28 @@
}
}
-static void copyOutputBufferToYV12Frame(uint8_t *dst, const uint8_t *srcY,
- const uint8_t *srcU,
- const uint8_t *srcV, size_t srcYStride,
- size_t srcUStride, size_t srcVStride,
+static void copyOutputBufferToYV12Frame(uint8_t *dstY, uint8_t *dstU, uint8_t *dstV,
+ const uint8_t *srcY, const uint8_t *srcU, const uint8_t *srcV,
+ size_t srcYStride, size_t srcUStride, size_t srcVStride,
+ size_t dstYStride, size_t dstUVStride,
uint32_t width, uint32_t height) {
- const size_t dstYStride = align(width, 16);
- const size_t dstUVStride = align(dstYStride / 2, 16);
- uint8_t *const dstStart = dst;
for (size_t i = 0; i < height; ++i) {
- memcpy(dst, srcY, width);
+ memcpy(dstY, srcY, width);
srcY += srcYStride;
- dst += dstYStride;
+ dstY += dstYStride;
}
- dst = dstStart + dstYStride * height;
for (size_t i = 0; i < height / 2; ++i) {
- memcpy(dst, srcV, width / 2);
+ memcpy(dstV, srcV, width / 2);
srcV += srcVStride;
- dst += dstUVStride;
+ dstV += dstUVStride;
}
- dst = dstStart + (dstYStride * height) + (dstUVStride * height / 2);
for (size_t i = 0; i < height / 2; ++i) {
- memcpy(dst, srcU, width / 2);
+ memcpy(dstU, srcU, width / 2);
srcU += srcUStride;
- dst += dstUVStride;
+ dstU += dstUVStride;
}
}
@@ -568,15 +563,11 @@
}
static void convertYUV420Planar16ToYUV420Planar(
- uint8_t *dst, const uint16_t *srcY, const uint16_t *srcU,
- const uint16_t *srcV, size_t srcYStride, size_t srcUStride,
- size_t srcVStride, size_t dstStride, size_t width, size_t height) {
- uint8_t *dstY = (uint8_t *)dst;
- size_t dstYSize = dstStride * height;
- size_t dstUVStride = align(dstStride / 2, 16);
- size_t dstUVSize = dstUVStride * height / 2;
- uint8_t *dstV = dstY + dstYSize;
- uint8_t *dstU = dstV + dstUVSize;
+ uint8_t *dstY, uint8_t *dstU, uint8_t *dstV,
+ const uint16_t *srcY, const uint16_t *srcU, const uint16_t *srcV,
+ size_t srcYStride, size_t srcUStride, size_t srcVStride,
+ size_t dstYStride, size_t dstUVStride,
+ size_t width, size_t height) {
for (size_t y = 0; y < height; ++y) {
for (size_t x = 0; x < width; ++x) {
@@ -584,7 +575,7 @@
}
srcY += srcYStride;
- dstY += dstStride;
+ dstY += dstYStride;
}
for (size_t y = 0; y < (height + 1) / 2; ++y) {
@@ -679,11 +670,17 @@
ALOGV("provided (%dx%d) required (%dx%d), out frameindex %d", block->width(),
block->height(), mWidth, mHeight, (int)buffer->user_private_data);
- uint8_t *dst = const_cast<uint8_t *>(wView.data()[C2PlanarLayout::PLANE_Y]);
+ uint8_t *dstY = const_cast<uint8_t *>(wView.data()[C2PlanarLayout::PLANE_Y]);
+ uint8_t *dstU = const_cast<uint8_t *>(wView.data()[C2PlanarLayout::PLANE_U]);
+ uint8_t *dstV = const_cast<uint8_t *>(wView.data()[C2PlanarLayout::PLANE_V]);
size_t srcYStride = buffer->stride[0];
size_t srcUStride = buffer->stride[1];
size_t srcVStride = buffer->stride[2];
+ C2PlanarLayout layout = wView.layout();
+ size_t dstYStride = layout.planes[C2PlanarLayout::PLANE_Y].rowInc;
+ size_t dstUVStride = layout.planes[C2PlanarLayout::PLANE_U].rowInc;
+
if (buffer->bitdepth == 10) {
const uint16_t *srcY = (const uint16_t *)buffer->plane[0];
const uint16_t *srcU = (const uint16_t *)buffer->plane[1];
@@ -691,19 +688,24 @@
if (format == HAL_PIXEL_FORMAT_RGBA_1010102) {
convertYUV420Planar16ToY410(
- (uint32_t *)dst, srcY, srcU, srcV, srcYStride / 2, srcUStride / 2,
- srcVStride / 2, align(mWidth, 16), mWidth, mHeight);
+ (uint32_t *)dstY, srcY, srcU, srcV, srcYStride / 2, srcUStride / 2,
+ srcVStride / 2, dstYStride / sizeof(uint32_t), mWidth, mHeight);
} else {
- convertYUV420Planar16ToYUV420Planar(dst, srcY, srcU, srcV, srcYStride / 2,
- srcUStride / 2, srcVStride / 2,
- align(mWidth, 16), mWidth, mHeight);
+ convertYUV420Planar16ToYUV420Planar(dstY, dstU, dstV,
+ srcY, srcU, srcV,
+ srcYStride / 2, srcUStride / 2, srcVStride / 2,
+ dstYStride, dstUVStride,
+ mWidth, mHeight);
}
} else {
const uint8_t *srcY = (const uint8_t *)buffer->plane[0];
const uint8_t *srcU = (const uint8_t *)buffer->plane[1];
const uint8_t *srcV = (const uint8_t *)buffer->plane[2];
- copyOutputBufferToYV12Frame(dst, srcY, srcU, srcV, srcYStride, srcUStride,
- srcVStride, mWidth, mHeight);
+ copyOutputBufferToYV12Frame(dstY, dstU, dstV,
+ srcY, srcU, srcV,
+ srcYStride, srcUStride, srcVStride,
+ dstYStride, dstUVStride,
+ mWidth, mHeight);
}
finishWork(buffer->user_private_data, work, std::move(block));
block = nullptr;
diff --git a/media/codec2/components/mpeg4_h263/C2SoftMpeg4Dec.cpp b/media/codec2/components/mpeg4_h263/C2SoftMpeg4Dec.cpp
index 61b286c..13cc0ec 100644
--- a/media/codec2/components/mpeg4_h263/C2SoftMpeg4Dec.cpp
+++ b/media/codec2/components/mpeg4_h263/C2SoftMpeg4Dec.cpp
@@ -464,34 +464,34 @@
/* TODO: can remove temporary copy after library supports writing to display
* buffer Y, U and V plane pointers using stride info. */
static void copyOutputBufferToYuvPlanarFrame(
- uint8_t *dst, uint8_t *src,
+ uint8_t *dstY, uint8_t *dstU, uint8_t *dstV, uint8_t *src,
size_t dstYStride, size_t dstUVStride,
size_t srcYStride, uint32_t width,
uint32_t height) {
size_t srcUVStride = srcYStride / 2;
uint8_t *srcStart = src;
- uint8_t *dstStart = dst;
+
size_t vStride = align(height, 16);
for (size_t i = 0; i < height; ++i) {
- memcpy(dst, src, width);
+ memcpy(dstY, src, width);
src += srcYStride;
- dst += dstYStride;
+ dstY += dstYStride;
}
+
/* U buffer */
src = srcStart + vStride * srcYStride;
- dst = dstStart + (dstYStride * height) + (dstUVStride * height / 2);
for (size_t i = 0; i < height / 2; ++i) {
- memcpy(dst, src, width / 2);
+ memcpy(dstU, src, width / 2);
src += srcUVStride;
- dst += dstUVStride;
+ dstU += dstUVStride;
}
+
/* V buffer */
src = srcStart + vStride * srcYStride * 5 / 4;
- dst = dstStart + (dstYStride * height);
for (size_t i = 0; i < height / 2; ++i) {
- memcpy(dst, src, width / 2);
+ memcpy(dstV, src, width / 2);
src += srcUVStride;
- dst += dstUVStride;
+ dstV += dstUVStride;
}
}
@@ -672,11 +672,14 @@
}
uint8_t *outputBufferY = wView.data()[C2PlanarLayout::PLANE_Y];
+ uint8_t *outputBufferU = wView.data()[C2PlanarLayout::PLANE_U];
+ uint8_t *outputBufferV = wView.data()[C2PlanarLayout::PLANE_V];
+
C2PlanarLayout layout = wView.layout();
size_t dstYStride = layout.planes[C2PlanarLayout::PLANE_Y].rowInc;
size_t dstUVStride = layout.planes[C2PlanarLayout::PLANE_U].rowInc;
(void)copyOutputBufferToYuvPlanarFrame(
- outputBufferY,
+ outputBufferY, outputBufferU, outputBufferV,
mOutputBuffer[mNumSamplesOutput & 1],
dstYStride, dstUVStride,
align(mWidth, 16), mWidth, mHeight);
diff --git a/media/codec2/components/vpx/C2SoftVpxDec.cpp b/media/codec2/components/vpx/C2SoftVpxDec.cpp
index 3eef1e3..91238e8 100644
--- a/media/codec2/components/vpx/C2SoftVpxDec.cpp
+++ b/media/codec2/components/vpx/C2SoftVpxDec.cpp
@@ -631,31 +631,30 @@
}
static void copyOutputBufferToYuvPlanarFrame(
- uint8_t *dst, const uint8_t *srcY, const uint8_t *srcU, const uint8_t *srcV,
+ uint8_t *dstY, uint8_t *dstU, uint8_t *dstV,
+ const uint8_t *srcY, const uint8_t *srcU, const uint8_t *srcV,
size_t srcYStride, size_t srcUStride, size_t srcVStride,
size_t dstYStride, size_t dstUVStride,
uint32_t width, uint32_t height) {
- uint8_t *dstStart = dst;
for (size_t i = 0; i < height; ++i) {
- memcpy(dst, srcY, width);
+ memcpy(dstY, srcY, width);
srcY += srcYStride;
- dst += dstYStride;
+ dstY += dstYStride;
}
- dst = dstStart + dstYStride * height;
for (size_t i = 0; i < height / 2; ++i) {
- memcpy(dst, srcV, width / 2);
+ memcpy(dstV, srcV, width / 2);
srcV += srcVStride;
- dst += dstUVStride;
+ dstV += dstUVStride;
}
- dst = dstStart + (dstYStride * height) + (dstUVStride * height / 2);
for (size_t i = 0; i < height / 2; ++i) {
- memcpy(dst, srcU, width / 2);
+ memcpy(dstU, srcU, width / 2);
srcU += srcUStride;
- dst += dstUVStride;
+ dstU += dstUVStride;
}
+
}
static void convertYUV420Planar16ToY410(uint32_t *dst,
@@ -721,16 +720,12 @@
return;
}
-static void convertYUV420Planar16ToYUV420Planar(uint8_t *dst,
+static void convertYUV420Planar16ToYUV420Planar(
+ uint8_t *dstY, uint8_t *dstU, uint8_t *dstV,
const uint16_t *srcY, const uint16_t *srcU, const uint16_t *srcV,
size_t srcYStride, size_t srcUStride, size_t srcVStride,
- size_t dstYStride, size_t dstUVStride, size_t width, size_t height) {
-
- uint8_t *dstY = (uint8_t *)dst;
- size_t dstYSize = dstYStride * height;
- size_t dstUVSize = dstUVStride * height / 2;
- uint8_t *dstV = dstY + dstYSize;
- uint8_t *dstU = dstV + dstUVSize;
+ size_t dstYStride, size_t dstUVStride,
+ size_t width, size_t height) {
for (size_t y = 0; y < height; ++y) {
for (size_t x = 0; x < width; ++x) {
@@ -823,7 +818,10 @@
block->width(), block->height(), mWidth, mHeight,
((c2_cntr64_t *)img->user_priv)->peekll());
- uint8_t *dst = const_cast<uint8_t *>(wView.data()[C2PlanarLayout::PLANE_Y]);
+ uint8_t *dstY = const_cast<uint8_t *>(wView.data()[C2PlanarLayout::PLANE_Y]);
+ uint8_t *dstU = const_cast<uint8_t *>(wView.data()[C2PlanarLayout::PLANE_U]);
+ uint8_t *dstV = const_cast<uint8_t *>(wView.data()[C2PlanarLayout::PLANE_V]);
+
size_t srcYStride = img->stride[VPX_PLANE_Y];
size_t srcUStride = img->stride[VPX_PLANE_U];
size_t srcVStride = img->stride[VPX_PLANE_V];
@@ -842,18 +840,18 @@
constexpr size_t kHeight = 64;
for (; i < mHeight; i += kHeight) {
queue->entries.push_back(
- [dst, srcY, srcU, srcV,
+ [dstY, srcY, srcU, srcV,
srcYStride, srcUStride, srcVStride, dstYStride,
width = mWidth, height = std::min(mHeight - i, kHeight)] {
convertYUV420Planar16ToY410(
- (uint32_t *)dst, srcY, srcU, srcV, srcYStride / 2,
+ (uint32_t *)dstY, srcY, srcU, srcV, srcYStride / 2,
srcUStride / 2, srcVStride / 2, dstYStride / sizeof(uint32_t),
width, height);
});
srcY += srcYStride / 2 * kHeight;
srcU += srcUStride / 2 * (kHeight / 2);
srcV += srcVStride / 2 * (kHeight / 2);
- dst += dstYStride * kHeight;
+ dstY += dstYStride * kHeight;
}
CHECK_EQ(0u, queue->numPending);
queue->numPending = queue->entries.size();
@@ -862,8 +860,9 @@
queue.waitForCondition(queue->cond);
}
} else {
- convertYUV420Planar16ToYUV420Planar(dst, srcY, srcU, srcV, srcYStride / 2,
- srcUStride / 2, srcVStride / 2,
+ convertYUV420Planar16ToYUV420Planar(dstY, dstU, dstV,
+ srcY, srcU, srcV,
+ srcYStride / 2, srcUStride / 2, srcVStride / 2,
dstYStride, dstUVStride,
mWidth, mHeight);
}
@@ -871,8 +870,10 @@
const uint8_t *srcY = (const uint8_t *)img->planes[VPX_PLANE_Y];
const uint8_t *srcU = (const uint8_t *)img->planes[VPX_PLANE_U];
const uint8_t *srcV = (const uint8_t *)img->planes[VPX_PLANE_V];
+
copyOutputBufferToYuvPlanarFrame(
- dst, srcY, srcU, srcV,
+ dstY, dstU, dstV,
+ srcY, srcU, srcV,
srcYStride, srcUStride, srcVStride,
dstYStride, dstUVStride,
mWidth, mHeight);
diff --git a/media/codec2/tests/C2UtilTest.cpp b/media/codec2/tests/C2UtilTest.cpp
index 59cd313..2d66df1 100644
--- a/media/codec2/tests/C2UtilTest.cpp
+++ b/media/codec2/tests/C2UtilTest.cpp
@@ -78,7 +78,7 @@
{ "value2", Enum3Value2 },
{ "value4", Enum3Value4 },
{ "invalid", Invalid } });
- Enum3 e3;
+ Enum3 e3(Invalid);
C2FieldDescriptor::namedValuesFor(e3);
// upper case
diff --git a/media/extractors/fuzzers/Android.bp b/media/extractors/fuzzers/Android.bp
index a9fc7e4..31d6f83 100644
--- a/media/extractors/fuzzers/Android.bp
+++ b/media/extractors/fuzzers/Android.bp
@@ -68,6 +68,7 @@
cc_defaults {
name: "mpeg2-extractor-fuzzer-defaults",
defaults: ["extractor-fuzzer-defaults"],
+ host_supported: true,
include_dirs: [
"frameworks/av/media/extractors/mpeg2",
@@ -80,6 +81,7 @@
"libstagefright_mpeg2extractor",
"libstagefright_esds",
"libmpeg2extractor",
+ "libmedia_helper",
],
shared_libs: [
diff --git a/media/extractors/mp4/ItemTable.cpp b/media/extractors/mp4/ItemTable.cpp
index 0773387..2599c2c 100644
--- a/media/extractors/mp4/ItemTable.cpp
+++ b/media/extractors/mp4/ItemTable.cpp
@@ -546,11 +546,11 @@
continue;
}
ALOGV("Image item id %d uses thumbnail item id %d", mRefs[i], mItemId);
- ImageItem &masterImage = itemIdToItemMap.editValueAt(itemIndex);
- if (!masterImage.thumbnails.empty()) {
+ ImageItem &imageItem = itemIdToItemMap.editValueAt(itemIndex);
+ if (!imageItem.thumbnails.empty()) {
ALOGW("already has thumbnails!");
}
- masterImage.thumbnails.push_back(mItemId);
+ imageItem.thumbnails.push_back(mItemId);
}
break;
}
@@ -929,7 +929,7 @@
status_t IpcoBox::parse(off64_t offset, size_t size) {
ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
- // push dummy as the index is 1-based
+ // push a placeholder as the index is 1-based
mItemProperties->push_back(new ItemProperty());
return parseChunks(offset, size);
}
@@ -1614,17 +1614,17 @@
return BAD_VALUE;
}
- uint32_t masterItemIndex = mDisplayables[imageIndex];
+ uint32_t imageItemIndex = mDisplayables[imageIndex];
- const ImageItem &masterImage = mItemIdToItemMap[masterItemIndex];
- if (masterImage.thumbnails.empty()) {
- *itemIndex = masterItemIndex;
+ const ImageItem &imageItem = mItemIdToItemMap[imageItemIndex];
+ if (imageItem.thumbnails.empty()) {
+ *itemIndex = imageItemIndex;
return OK;
}
- ssize_t thumbItemIndex = mItemIdToItemMap.indexOfKey(masterImage.thumbnails[0]);
+ ssize_t thumbItemIndex = mItemIdToItemMap.indexOfKey(imageItem.thumbnails[0]);
if (thumbItemIndex < 0) {
- // Do not return the master image in this case, fail it so that the
+ // Do not return the image item in this case, fail it so that the
// thumbnail extraction code knows we really don't have it.
return INVALID_OPERATION;
}
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index fd8f44e..65ba382 100644
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -1151,7 +1151,7 @@
} else if (chunk_type == FOURCC("moov")) {
mInitCheck = OK;
- return UNKNOWN_ERROR; // Return a dummy error.
+ return UNKNOWN_ERROR; // Return a generic error.
}
break;
}
@@ -5785,7 +5785,7 @@
return -EINVAL;
}
- // apply some sanity (vs strict legality) checks
+ // apply some quick (vs strict legality) checks
//
static constexpr uint32_t kMaxTrunSampleCount = 10000;
if (sampleCount > kMaxTrunSampleCount) {
diff --git a/media/extractors/tests/AndroidTest.xml b/media/extractors/tests/AndroidTest.xml
index 1f17d42..fc8152c 100644
--- a/media/extractors/tests/AndroidTest.xml
+++ b/media/extractors/tests/AndroidTest.xml
@@ -19,7 +19,7 @@
<option name="cleanup" value="true" />
<option name="push" value="ExtractorUnitTest->/data/local/tmp/ExtractorUnitTest" />
<option name="push-file"
- key="https://storage.googleapis.com/android_media/frameworks/av/media/extractors/tests/extractor-1.3.zip?unzip=true"
+ key="https://storage.googleapis.com/android_media/frameworks/av/media/extractors/tests/extractor-1.4.zip?unzip=true"
value="/data/local/tmp/ExtractorUnitTestRes/" />
</target_preparer>
diff --git a/media/extractors/tests/ExtractorUnitTest.cpp b/media/extractors/tests/ExtractorUnitTest.cpp
index b7c6c59..d91fffa 100644
--- a/media/extractors/tests/ExtractorUnitTest.cpp
+++ b/media/extractors/tests/ExtractorUnitTest.cpp
@@ -138,10 +138,23 @@
mDisableTest = false;
static const std::map<std::string, standardExtractors> mapExtractor = {
- {"aac", AAC}, {"amr", AMR}, {"mp3", MP3}, {"ogg", OGG},
- {"wav", WAV}, {"mkv", MKV}, {"flac", FLAC}, {"midi", MIDI},
- {"mpeg4", MPEG4}, {"mpeg2ts", MPEG2TS}, {"mpeg2ps", MPEG2PS}, {"mp4", MPEG4},
- {"webm", MKV}, {"ts", MPEG2TS}, {"mpeg", MPEG2PS}};
+ {"aac", AAC},
+ {"amr", AMR},
+ {"flac", FLAC},
+ {"mid", MIDI},
+ {"midi", MIDI},
+ {"mkv", MKV},
+ {"mp3", MP3},
+ {"mp4", MPEG4},
+ {"mpeg2ps", MPEG2PS},
+ {"mpeg2ts", MPEG2TS},
+ {"mpeg4", MPEG4},
+ {"mpg", MPEG2PS},
+ {"ogg", OGG},
+ {"opus", OGG},
+ {"ts", MPEG2TS},
+ {"wav", WAV},
+ {"webm", MKV}};
// Find the component type
if (mapExtractor.find(writerFormat) != mapExtractor.end()) {
mExtractorName = mapExtractor.at(writerFormat);
@@ -940,36 +953,55 @@
}
}
- virtual void SetUp() override {
- string input0 = GetParam().first;
- string input1 = GetParam().second;
-
- // Allocate memory to hold extracted data for both extractors
- struct stat buf;
- int32_t status = stat((gEnv->getRes() + input0).c_str(), &buf);
- ASSERT_EQ(status, 0) << "Unable to get file properties";
-
- // allocating the buffer size as 2x since some
- // extractors like flac, midi and wav decodes the file.
- mExtractorOutput[0] = (int8_t *)calloc(1, buf.st_size * 2);
- ASSERT_NE(mExtractorOutput[0], nullptr)
- << "Unable to allocate memory for writing extractor's output";
- mExtractorOuputSize[0] = buf.st_size * 2;
-
- status = stat((gEnv->getRes() + input1).c_str(), &buf);
- ASSERT_EQ(status, 0) << "Unable to get file properties";
-
- // allocate buffer for extractor output, 2x input file size.
- mExtractorOutput[1] = (int8_t *)calloc(1, buf.st_size * 2);
- ASSERT_NE(mExtractorOutput[1], nullptr)
- << "Unable to allocate memory for writing extractor's output";
- mExtractorOuputSize[1] = buf.st_size * 2;
- }
-
int8_t *mExtractorOutput[2]{};
size_t mExtractorOuputSize[2]{};
};
+size_t allocateOutputBuffers(string inputFileName, AMediaFormat *extractorFormat) {
+ size_t bufferSize = 0u;
+ // allocating the buffer size as sampleRate * channelCount * clipDuration since
+ // some extractors like flac, midi and wav decodes the file. These extractors
+ // advertise the mime type as raw.
+ const char *mime;
+ AMediaFormat_getString(extractorFormat, AMEDIAFORMAT_KEY_MIME, &mime);
+ if (!strcmp(mime, MEDIA_MIMETYPE_AUDIO_RAW)) {
+ int64_t clipDurationUs = -1;
+ int32_t channelCount = -1;
+ int32_t sampleRate = -1;
+ int32_t bitsPerSampple = -1;
+ if (!AMediaFormat_getInt32(extractorFormat, AMEDIAFORMAT_KEY_CHANNEL_COUNT,
+ &channelCount) || channelCount <= 0) {
+ ALOGE("Invalid channelCount for input file : %s", inputFileName.c_str());
+ return 0;
+ }
+ if (!AMediaFormat_getInt32(extractorFormat, AMEDIAFORMAT_KEY_SAMPLE_RATE, &sampleRate) ||
+ sampleRate <= 0) {
+ ALOGE("Invalid sampleRate for input file : %s", inputFileName.c_str());
+ return 0;
+ }
+ if (!AMediaFormat_getInt64(extractorFormat, AMEDIAFORMAT_KEY_DURATION, &clipDurationUs) ||
+ clipDurationUs <= 0) {
+ ALOGE("Invalid clip duration for input file : %s", inputFileName.c_str());
+ return 0;
+ }
+ if (!AMediaFormat_getInt32(extractorFormat, AMEDIAFORMAT_KEY_PCM_ENCODING,
+ &bitsPerSampple) || bitsPerSampple <= 0) {
+ ALOGE("Invalid bits per sample for input file : %s", inputFileName.c_str());
+ return 0;
+ }
+ bufferSize = bitsPerSampple * channelCount * sampleRate * (clipDurationUs / 1000000 + 1);
+ } else {
+ struct stat buf;
+ int32_t status = stat(inputFileName.c_str(), &buf);
+ if (status != 0) {
+ ALOGE("Unable to get file properties for: %s", inputFileName.c_str());
+ return 0;
+ }
+ bufferSize = buf.st_size;
+ }
+ return bufferSize;
+}
+
// Compare output of two extractors for identical content
TEST_P(ExtractorComparison, ExtractorComparisonTest) {
vector<string> inputFileNames = {GetParam().first, GetParam().second};
@@ -1011,6 +1043,13 @@
CMediaTrack *cTrack = wrap(track);
ASSERT_NE(cTrack, nullptr) << "Failed to get track wrapper for index " << trackIdx;
+ mExtractorOuputSize[idx] = allocateOutputBuffers(inputFileName, extractorFormat[idx]);
+ ASSERT_GT(mExtractorOuputSize[idx], 0u) << " Invalid size for output buffers";
+
+ mExtractorOutput[idx] = (int8_t *)calloc(1, mExtractorOuputSize[idx]);
+ ASSERT_NE(mExtractorOutput[idx], nullptr)
+ << "Unable to allocate memory for writing extractor's output";
+
MediaBufferGroup *bufferGroup = new MediaBufferGroup();
status = cTrack->start(track, bufferGroup->wrap());
ASSERT_EQ(OK, (media_status_t)status) << "Failed to start the track";
@@ -1087,14 +1126,44 @@
<< inputFileNames[1] << " extractors";
}
-INSTANTIATE_TEST_SUITE_P(ExtractorComparisonAll, ExtractorComparison,
- ::testing::Values(make_pair("swirl_144x136_vp9.mp4",
- "swirl_144x136_vp9.webm"),
- make_pair("video_480x360_mp4_vp9_333kbps_25fps.mp4",
- "video_480x360_webm_vp9_333kbps_25fps.webm"),
- make_pair("video_1280x720_av1_hdr_static_3mbps.mp4",
- "video_1280x720_av1_hdr_static_3mbps.webm"),
- make_pair("loudsoftaac.aac", "loudsoftaac.mkv")));
+INSTANTIATE_TEST_SUITE_P(
+ ExtractorComparisonAll, ExtractorComparison,
+ ::testing::Values(make_pair("swirl_144x136_vp9.mp4", "swirl_144x136_vp9.webm"),
+ make_pair("video_480x360_mp4_vp9_333kbps_25fps.mp4",
+ "video_480x360_webm_vp9_333kbps_25fps.webm"),
+ make_pair("video_1280x720_av1_hdr_static_3mbps.mp4",
+ "video_1280x720_av1_hdr_static_3mbps.webm"),
+ make_pair("swirl_132x130_mpeg4.3gp", "swirl_132x130_mpeg4.mkv"),
+ make_pair("swirl_144x136_avc.mkv", "swirl_144x136_avc.mp4"),
+ make_pair("swirl_132x130_mpeg4.mp4", "swirl_132x130_mpeg4.mkv"),
+ make_pair("crowd_508x240_25fps_hevc.mp4","crowd_508x240_25fps_hevc.mkv"),
+ make_pair("bbb_cif_768kbps_30fps_mpeg2.mp4",
+ "bbb_cif_768kbps_30fps_mpeg2.ts"),
+
+ make_pair("loudsoftaac.aac", "loudsoftaac.mkv"),
+ make_pair("sinesweepflacmkv.mkv", "sinesweepflacmp4.mp4"),
+ make_pair("sinesweepmp3lame.mp3", "sinesweepmp3lame.mkv"),
+ make_pair("sinesweepoggmp4.mp4", "sinesweepogg.ogg"),
+ make_pair("sinesweepvorbis.mp4", "sinesweepvorbis.ogg"),
+ make_pair("sinesweepvorbis.mkv", "sinesweepvorbis.ogg"),
+ make_pair("testopus.mkv", "testopus.mp4"),
+ make_pair("testopus.mp4", "testopus.opus"),
+
+ make_pair("loudsoftaac.aac", "loudsoftaac.aac"),
+ make_pair("testamr.amr", "testamr.amr"),
+ make_pair("sinesweepflac.flac", "sinesweepflac.flac"),
+ make_pair("midi_a.mid", "midi_a.mid"),
+ make_pair("sinesweepvorbis.mkv", "sinesweepvorbis.mkv"),
+ make_pair("sinesweepmp3lame.mp3", "sinesweepmp3lame.mp3"),
+ make_pair("sinesweepoggmp4.mp4", "sinesweepoggmp4.mp4"),
+ make_pair("testopus.opus", "testopus.opus"),
+ make_pair("john_cage.ogg", "john_cage.ogg"),
+ make_pair("monotestgsm.wav", "monotestgsm.wav"),
+
+ make_pair("swirl_144x136_mpeg2.mpg", "swirl_144x136_mpeg2.mpg"),
+ make_pair("swirl_132x130_mpeg4.mp4", "swirl_132x130_mpeg4.mp4"),
+ make_pair("swirl_144x136_vp9.webm", "swirl_144x136_vp9.webm"),
+ make_pair("swirl_144x136_vp8.webm", "swirl_144x136_vp8.webm")));
INSTANTIATE_TEST_SUITE_P(ConfigParamTestAll, ConfigParamTest,
::testing::Values(make_pair("aac", AAC_1),
diff --git a/media/extractors/tests/README.md b/media/extractors/tests/README.md
index 69538b6..cff09ca 100644
--- a/media/extractors/tests/README.md
+++ b/media/extractors/tests/README.md
@@ -22,7 +22,7 @@
adb push ${OUT}/data/nativetest/ExtractorUnitTest/ExtractorUnitTest /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.
+The resource file for the tests is taken from [here](https://storage.googleapis.com/android_media/frameworks/av/media/extractors/tests/extractor-1.4.zip). Download, unzip and push these files into device for testing.
```
adb push extractor /data/local/tmp/
diff --git a/media/janitors/OWNERS-codecs b/media/janitors/OWNERS-codecs
new file mode 100644
index 0000000..e201399
--- /dev/null
+++ b/media/janitors/OWNERS-codecs
@@ -0,0 +1,5 @@
+# gerrit owner/approvers for the actual software codec libraries
+# differentiated from plugins connecting those codecs to either omx or codec2 infrastructure
+essick@google.com
+lajos@google.com
+marcone@google.com
diff --git a/media/janitors/README b/media/janitors/README
new file mode 100644
index 0000000..9db8e0e
--- /dev/null
+++ b/media/janitors/README
@@ -0,0 +1,4 @@
+A collection of OWNERS files that we reference from other projects,
+such as the software codecs in directories like external/libavc.
+This is to simplify our owner/approver management across the multiple
+projects related to media.
diff --git a/media/libaaudio/examples/utils/dummy.cpp b/media/libaaudio/examples/utils/dummy.cpp
deleted file mode 100644
index 8ef7e36..0000000
--- a/media/libaaudio/examples/utils/dummy.cpp
+++ /dev/null
@@ -1,5 +0,0 @@
-/**
- * Dummy file needed to get Android Studio to scan this folder.
- */
-
-int g_DoNotUseThisVariable = 0;
diff --git a/media/libaaudio/examples/utils/unused.cpp b/media/libaaudio/examples/utils/unused.cpp
new file mode 100644
index 0000000..9a5205e
--- /dev/null
+++ b/media/libaaudio/examples/utils/unused.cpp
@@ -0,0 +1,5 @@
+/**
+ * Unused file required to get Android Studio to scan this folder.
+ */
+
+int g_DoNotUseThisVariable = 0;
diff --git a/media/libaaudio/src/binding/SharedMemoryParcelable.h b/media/libaaudio/src/binding/SharedMemoryParcelable.h
index 4ec38c5..3927f58 100644
--- a/media/libaaudio/src/binding/SharedMemoryParcelable.h
+++ b/media/libaaudio/src/binding/SharedMemoryParcelable.h
@@ -26,7 +26,7 @@
namespace aaudio {
-// Arbitrary limits for sanity checks. TODO remove after debugging.
+// Arbitrary limits for range checks.
#define MAX_SHARED_MEMORIES (32)
#define MAX_MMAP_OFFSET_BYTES (32 * 1024 * 8)
#define MAX_MMAP_SIZE_BYTES (32 * 1024 * 8)
diff --git a/media/libaaudio/src/flowgraph/AudioProcessorBase.h b/media/libaaudio/src/flowgraph/AudioProcessorBase.h
index eda46ae..972932f 100644
--- a/media/libaaudio/src/flowgraph/AudioProcessorBase.h
+++ b/media/libaaudio/src/flowgraph/AudioProcessorBase.h
@@ -267,7 +267,7 @@
AudioFloatInputPort input;
/**
- * Dummy processor. The work happens in the read() method.
+ * Do nothing. The work happens in the read() method.
*
* @param framePosition index of first frame to be processed
* @param numFrames
diff --git a/media/libaudioclient/AudioEffect.cpp b/media/libaudioclient/AudioEffect.cpp
index 3ead6cb..73b96ab 100644
--- a/media/libaudioclient/AudioEffect.cpp
+++ b/media/libaudioclient/AudioEffect.cpp
@@ -36,64 +36,10 @@
// ---------------------------------------------------------------------------
AudioEffect::AudioEffect(const String16& opPackageName)
- : mStatus(NO_INIT), mProbe(false), mOpPackageName(opPackageName)
+ : mOpPackageName(opPackageName)
{
}
-
-AudioEffect::AudioEffect(const effect_uuid_t *type,
- const String16& opPackageName,
- const effect_uuid_t *uuid,
- int32_t priority,
- effect_callback_t cbf,
- void* user,
- audio_session_t sessionId,
- audio_io_handle_t io,
- const AudioDeviceTypeAddr& device,
- bool probe
- )
- : mStatus(NO_INIT), mProbe(false), mOpPackageName(opPackageName)
-{
- AutoMutex lock(mConstructLock);
- mStatus = set(type, uuid, priority, cbf, user, sessionId, io, device, probe);
-}
-
-AudioEffect::AudioEffect(const char *typeStr,
- const String16& opPackageName,
- const char *uuidStr,
- int32_t priority,
- effect_callback_t cbf,
- void* user,
- audio_session_t sessionId,
- audio_io_handle_t io,
- const AudioDeviceTypeAddr& device,
- bool probe
- )
- : mStatus(NO_INIT), mProbe(false), mOpPackageName(opPackageName)
-{
- effect_uuid_t type;
- effect_uuid_t *pType = NULL;
- effect_uuid_t uuid;
- effect_uuid_t *pUuid = NULL;
-
- ALOGV("Constructor string\n - type: %s\n - uuid: %s", typeStr, uuidStr);
-
- if (typeStr != NULL) {
- if (stringToGuid(typeStr, &type) == NO_ERROR) {
- pType = &type;
- }
- }
-
- if (uuidStr != NULL) {
- if (stringToGuid(uuidStr, &uuid) == NO_ERROR) {
- pUuid = &uuid;
- }
- }
-
- AutoMutex lock(mConstructLock);
- mStatus = set(pType, pUuid, priority, cbf, user, sessionId, io, device, probe);
-}
-
status_t AudioEffect::set(const effect_uuid_t *type,
const effect_uuid_t *uuid,
int32_t priority,
@@ -194,6 +140,34 @@
return mStatus;
}
+status_t AudioEffect::set(const char *typeStr,
+ const char *uuidStr,
+ int32_t priority,
+ effect_callback_t cbf,
+ void* user,
+ audio_session_t sessionId,
+ audio_io_handle_t io,
+ const AudioDeviceTypeAddr& device,
+ bool probe)
+{
+ effect_uuid_t type;
+ effect_uuid_t *pType = nullptr;
+ effect_uuid_t uuid;
+ effect_uuid_t *pUuid = nullptr;
+
+ ALOGV("AudioEffect::set string\n - type: %s\n - uuid: %s",
+ typeStr ? typeStr : "nullptr", uuidStr ? uuidStr : "nullptr");
+
+ if (stringToGuid(typeStr, &type) == NO_ERROR) {
+ pType = &type;
+ }
+ if (stringToGuid(uuidStr, &uuid) == NO_ERROR) {
+ pUuid = &uuid;
+ }
+
+ return set(pType, pUuid, priority, cbf, user, sessionId, io, device, probe);
+}
+
AudioEffect::~AudioEffect()
{
diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp
index df47def..509e063 100644
--- a/media/libaudioclient/AudioRecord.cpp
+++ b/media/libaudioclient/AudioRecord.cpp
@@ -1092,7 +1092,7 @@
}
if (ssize_t(userSize) < 0 || (buffer == NULL && userSize != 0)) {
- // sanity-check. user is most-likely passing an error code, and it would
+ // Validation. user is most-likely passing an error code, and it would
// make the return value ambiguous (actualSize vs error).
ALOGE("%s(%d) (buffer=%p, size=%zu (%zu)",
__func__, mPortId, buffer, userSize, userSize);
@@ -1319,7 +1319,7 @@
mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer);
size_t readSize = audioBuffer.size;
- // Sanity check on returned size
+ // Validate on returned size
if (ssize_t(readSize) < 0 || readSize > reqSize) {
ALOGE("%s(%d): EVENT_MORE_DATA requested %zu bytes but callback returned %zd bytes",
__func__, mPortId, reqSize, ssize_t(readSize));
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index 32129f0..807aa13 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -1936,7 +1936,7 @@
}
if (ssize_t(userSize) < 0 || (buffer == NULL && userSize != 0)) {
- // Sanity-check: user is most-likely passing an error code, and it would
+ // Validation: user is most-likely passing an error code, and it would
// make the return value ambiguous (actualSize vs error).
ALOGE("%s(%d): AudioTrack::write(buffer=%p, size=%zu (%zd)",
__func__, mPortId, buffer, userSize, userSize);
@@ -2326,7 +2326,7 @@
mUserData, &audioBuffer);
size_t writtenSize = audioBuffer.size;
- // Sanity check on returned size
+ // Validate on returned size
if (ssize_t(writtenSize) < 0 || writtenSize > reqSize) {
ALOGE("%s(%d): EVENT_MORE_DATA requested %zu bytes but callback returned %zd bytes",
__func__, mPortId, reqSize, ssize_t(writtenSize));
diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp
index 16d2232..6d79aba 100644
--- a/media/libaudioclient/IAudioFlinger.cpp
+++ b/media/libaudioclient/IAudioFlinger.cpp
@@ -1001,7 +1001,7 @@
break;
}
- // Whitelist of relevant events to trigger log merging.
+ // List of relevant events that trigger log merging.
// Log merging should activate during audio activity of any kind. This are considered the
// most relevant events.
// TODO should select more wisely the items from the list
diff --git a/media/libaudioclient/OWNERS b/media/libaudioclient/OWNERS
index 482b9fb..034d161 100644
--- a/media/libaudioclient/OWNERS
+++ b/media/libaudioclient/OWNERS
@@ -1,3 +1,4 @@
gkasten@google.com
+hunga@google.com
jmtrivi@google.com
mnaganov@google.com
diff --git a/media/libaudioclient/include/media/AudioEffect.h b/media/libaudioclient/include/media/AudioEffect.h
index cb76252..3d4bb4e 100644
--- a/media/libaudioclient/include/media/AudioEffect.h
+++ b/media/libaudioclient/include/media/AudioEffect.h
@@ -339,16 +339,21 @@
*
* opPackageName: The package name used for app op checks.
*/
- AudioEffect(const String16& opPackageName);
+ explicit AudioEffect(const String16& opPackageName);
+ /* Terminates the AudioEffect and unregisters it from AudioFlinger.
+ * The effect engine is also destroyed if this AudioEffect was the last controlling
+ * the engine.
+ */
+ ~AudioEffect();
- /* Constructor.
+ /**
+ * Initialize an uninitialized AudioEffect.
*
* Parameters:
*
* type: type of effect created: can be null if uuid is specified. This corresponds to
* the OpenSL ES interface implemented by this effect.
- * opPackageName: The package name used for app op checks.
* uuid: Uuid of effect created: can be null if type is specified. This uuid corresponds to
* a particular implementation of an effect type.
* priority: requested priority for effect control: the priority level corresponds to the
@@ -356,7 +361,7 @@
* higher priorities, 0 being the normal priority.
* cbf: optional callback function (see effect_callback_t)
* user: pointer to context for use by the callback receiver.
- * sessionID: audio session this effect is associated to.
+ * sessionId: audio session this effect is associated to.
* If equal to AUDIO_SESSION_OUTPUT_MIX, the effect will be global to
* the output mix. Otherwise, the effect will be applied to all players
* (AudioTrack or MediaPLayer) within the same audio session.
@@ -369,46 +374,13 @@
* In this mode, no IEffect interface to AudioFlinger is created and all actions
* besides getters implemented in client AudioEffect object are no ops
* after effect creation.
+ *
+ * Returned status (from utils/Errors.h) can be:
+ * - NO_ERROR or ALREADY_EXISTS: successful initialization
+ * - INVALID_OPERATION: AudioEffect is already initialized
+ * - BAD_VALUE: invalid parameter
+ * - NO_INIT: audio flinger or audio hardware not initialized
*/
-
- AudioEffect(const effect_uuid_t *type,
- const String16& opPackageName,
- const effect_uuid_t *uuid = NULL,
- int32_t priority = 0,
- effect_callback_t cbf = NULL,
- void* user = NULL,
- audio_session_t sessionId = AUDIO_SESSION_OUTPUT_MIX,
- audio_io_handle_t io = AUDIO_IO_HANDLE_NONE,
- const AudioDeviceTypeAddr& device = {},
- bool probe = false);
-
- /* Constructor.
- * Same as above but with type and uuid specified by character strings
- */
- AudioEffect(const char *typeStr,
- const String16& opPackageName,
- const char *uuidStr = NULL,
- int32_t priority = 0,
- effect_callback_t cbf = NULL,
- void* user = NULL,
- audio_session_t sessionId = AUDIO_SESSION_OUTPUT_MIX,
- audio_io_handle_t io = AUDIO_IO_HANDLE_NONE,
- const AudioDeviceTypeAddr& device = {},
- bool probe = false);
-
- /* Terminates the AudioEffect and unregisters it from AudioFlinger.
- * The effect engine is also destroyed if this AudioEffect was the last controlling
- * the engine.
- */
- ~AudioEffect();
-
- /* Initialize an uninitialized AudioEffect.
- * Returned status (from utils/Errors.h) can be:
- * - NO_ERROR or ALREADY_EXISTS: successful initialization
- * - INVALID_OPERATION: AudioEffect is already initialized
- * - BAD_VALUE: invalid parameter
- * - NO_INIT: audio flinger or audio hardware not initialized
- * */
status_t set(const effect_uuid_t *type,
const effect_uuid_t *uuid = NULL,
int32_t priority = 0,
@@ -418,6 +390,18 @@
audio_io_handle_t io = AUDIO_IO_HANDLE_NONE,
const AudioDeviceTypeAddr& device = {},
bool probe = false);
+ /*
+ * Same as above but with type and uuid specified by character strings.
+ */
+ status_t set(const char *typeStr,
+ const char *uuidStr = NULL,
+ int32_t priority = 0,
+ effect_callback_t cbf = NULL,
+ void* user = NULL,
+ audio_session_t sessionId = AUDIO_SESSION_OUTPUT_MIX,
+ audio_io_handle_t io = AUDIO_IO_HANDLE_NONE,
+ const AudioDeviceTypeAddr& device = {},
+ bool probe = false);
/* Result of constructing the AudioEffect. This must be checked
* before using any AudioEffect API.
@@ -547,21 +531,20 @@
static const uint32_t kMaxPreProcessing = 10;
protected:
- bool mEnabled; // enable state
- audio_session_t mSessionId; // audio session ID
- int32_t mPriority; // priority for effect control
- status_t mStatus; // effect status
- bool mProbe; // effect created in probe mode: all commands
+ const String16 mOpPackageName; // The package name used for app op checks.
+ bool mEnabled = false; // enable state
+ audio_session_t mSessionId = AUDIO_SESSION_OUTPUT_MIX; // audio session ID
+ int32_t mPriority = 0; // priority for effect control
+ status_t mStatus = NO_INIT; // effect status
+ bool mProbe = false; // effect created in probe mode: all commands
// are no ops because mIEffect is NULL
- effect_callback_t mCbf; // callback function for status, control and
+ effect_callback_t mCbf = nullptr; // callback function for status, control and
// parameter changes notifications
- void* mUserData; // client context for callback function
- effect_descriptor_t mDescriptor; // effect descriptor
- int32_t mId; // system wide unique effect engine instance ID
+ void* mUserData = nullptr;// client context for callback function
+ effect_descriptor_t mDescriptor = {}; // effect descriptor
+ int32_t mId = -1; // system wide unique effect engine instance ID
Mutex mLock; // Mutex for mEnabled access
- Mutex mConstructLock; // Mutex for integrality construction
- String16 mOpPackageName; // The package name used for app op checks.
// IEffectClient
virtual void controlStatusChanged(bool controlGranted);
@@ -586,22 +569,12 @@
virtual void controlStatusChanged(bool controlGranted) {
sp<AudioEffect> effect = mEffect.promote();
if (effect != 0) {
- {
- // Got the mConstructLock means the construction of AudioEffect
- // has finished, we should release the mConstructLock immediately.
- AutoMutex lock(effect->mConstructLock);
- }
effect->controlStatusChanged(controlGranted);
}
}
virtual void enableStatusChanged(bool enabled) {
sp<AudioEffect> effect = mEffect.promote();
if (effect != 0) {
- {
- // Got the mConstructLock means the construction of AudioEffect
- // has finished, we should release the mConstructLock immediately.
- AutoMutex lock(effect->mConstructLock);
- }
effect->enableStatusChanged(enabled);
}
}
@@ -612,11 +585,6 @@
void *pReplyData) {
sp<AudioEffect> effect = mEffect.promote();
if (effect != 0) {
- {
- // Got the mConstructLock means the construction of AudioEffect
- // has finished, we should release the mConstructLock immediately.
- AutoMutex lock(effect->mConstructLock);
- }
effect->commandExecuted(
cmdCode, cmdSize, pCmdData, replySize, pReplyData);
}
@@ -626,11 +594,6 @@
virtual void binderDied(const wp<IBinder>& /*who*/) {
sp<AudioEffect> effect = mEffect.promote();
if (effect != 0) {
- {
- // Got the mConstructLock means the construction of AudioEffect
- // has finished, we should release the mConstructLock immediately.
- AutoMutex lock(effect->mConstructLock);
- }
effect->binderDied();
}
}
@@ -644,7 +607,7 @@
sp<IEffect> mIEffect; // IEffect binder interface
sp<EffectClient> mIEffectClient; // IEffectClient implementation
sp<IMemory> mCblkMemory; // shared memory for deferred parameter setting
- effect_param_cblk_t* mCblk; // control block for deferred parameter setting
+ effect_param_cblk_t* mCblk = nullptr; // control block for deferred parameter setting
pid_t mClientPid = (pid_t)-1;
uid_t mClientUid = (uid_t)-1;
};
diff --git a/media/libaudiohal/OWNERS b/media/libaudiohal/OWNERS
index 1456ab6..71b17e6 100644
--- a/media/libaudiohal/OWNERS
+++ b/media/libaudiohal/OWNERS
@@ -1,2 +1 @@
-krocard@google.com
mnaganov@google.com
diff --git a/media/libaudioprocessing/AudioResamplerDyn.cpp b/media/libaudioprocessing/AudioResamplerDyn.cpp
index ec56b00..1aacfd1 100644
--- a/media/libaudioprocessing/AudioResamplerDyn.cpp
+++ b/media/libaudioprocessing/AudioResamplerDyn.cpp
@@ -25,7 +25,6 @@
#include <cutils/compiler.h>
#include <cutils/properties.h>
-#include <utils/Debug.h>
#include <utils/Log.h>
#include <audio_utils/primitives.h>
@@ -636,7 +635,7 @@
const uint32_t phaseWrapLimit = c.mL << c.mShift;
size_t inFrameCount = (phaseIncrement * (uint64_t)outFrameCount + phaseFraction)
/ phaseWrapLimit;
- // sanity check that inFrameCount is in signed 32 bit integer range.
+ // validate that inFrameCount is in signed 32 bit integer range.
ALOG_ASSERT(0 <= inFrameCount && inFrameCount < (1U << 31));
//ALOGV("inFrameCount:%d outFrameCount:%d"
@@ -646,7 +645,7 @@
// NOTE: be very careful when modifying the code here. register
// pressure is very high and a small change might cause the compiler
// to generate far less efficient code.
- // Always sanity check the result with objdump or test-resample.
+ // Always validate the result with objdump or test-resample.
// the following logic is a bit convoluted to keep the main processing loop
// as tight as possible with register allocation.
diff --git a/media/libaudioprocessing/AudioResamplerFirProcess.h b/media/libaudioprocessing/AudioResamplerFirProcess.h
index 9b70a1c..1fcffcc 100644
--- a/media/libaudioprocessing/AudioResamplerFirProcess.h
+++ b/media/libaudioprocessing/AudioResamplerFirProcess.h
@@ -381,7 +381,7 @@
// NOTE: be very careful when modifying the code here. register
// pressure is very high and a small change might cause the compiler
// to generate far less efficient code.
- // Always sanity check the result with objdump or test-resample.
+ // Always validate the result with objdump or test-resample.
if (LOCKED) {
// locked polyphase (no interpolation)
diff --git a/media/libaudioprocessing/AudioResamplerSinc.cpp b/media/libaudioprocessing/AudioResamplerSinc.cpp
index 5a03a0d..f2c386d 100644
--- a/media/libaudioprocessing/AudioResamplerSinc.cpp
+++ b/media/libaudioprocessing/AudioResamplerSinc.cpp
@@ -404,7 +404,7 @@
// NOTE: be very careful when modifying the code here. register
// pressure is very high and a small change might cause the compiler
// to generate far less efficient code.
- // Always sanity check the result with objdump or test-resample.
+ // Always validate the result with objdump or test-resample.
// compute the index of the coefficient on the positive side and
// negative side
diff --git a/media/libeffects/OWNERS b/media/libeffects/OWNERS
index 7f9ae81..b7832ea 100644
--- a/media/libeffects/OWNERS
+++ b/media/libeffects/OWNERS
@@ -1,4 +1,3 @@
hunga@google.com
-krocard@google.com
mnaganov@google.com
rago@google.com
diff --git a/media/libeffects/factory/EffectsConfigLoader.c b/media/libeffects/factory/EffectsConfigLoader.c
index fcef36f..e23530e 100644
--- a/media/libeffects/factory/EffectsConfigLoader.c
+++ b/media/libeffects/factory/EffectsConfigLoader.c
@@ -394,7 +394,7 @@
}
sub_effect_entry_t *subEntry = (sub_effect_entry_t*)gSubEffectList->sub_elem->object;
effect_descriptor_t *subEffectDesc = (effect_descriptor_t*)(subEntry->object);
- // Since we return a dummy descriptor for the proxy during
+ // Since we return a stub descriptor for the proxy during
// get_descriptor call,we replace it with the correspoding
// sw effect descriptor, but with Proxy UUID
// check for Sw desc
diff --git a/media/libeffects/factory/EffectsXmlConfigLoader.cpp b/media/libeffects/factory/EffectsXmlConfigLoader.cpp
index 505be7c..30a9007 100644
--- a/media/libeffects/factory/EffectsXmlConfigLoader.cpp
+++ b/media/libeffects/factory/EffectsXmlConfigLoader.cpp
@@ -283,7 +283,7 @@
}
listPush(effectLoadResult.effectDesc.get(), subEffectList);
- // Since we return a dummy descriptor for the proxy during
+ // Since we return a stub descriptor for the proxy during
// get_descriptor call, we replace it with the corresponding
// sw effect descriptor, but keep the Proxy UUID
*effectLoadResult.effectDesc = *swEffectLoadResult.effectDesc;
diff --git a/media/libeffects/lvm/tests/Android.bp b/media/libeffects/lvm/tests/Android.bp
index 674c246..aea7703 100644
--- a/media/libeffects/lvm/tests/Android.bp
+++ b/media/libeffects/lvm/tests/Android.bp
@@ -44,6 +44,36 @@
}
cc_test {
+ name: "reverb_test",
+ host_supported: false,
+ proprietary: true,
+
+ include_dirs: [
+ "frameworks/av/media/libeffects/lvm/wrapper/Reverb"
+ ],
+
+ header_libs: [
+ "libaudioeffects",
+ ],
+
+ shared_libs: [
+ "libaudioutils",
+ "liblog",
+ "libreverbwrapper",
+ ],
+
+ srcs: [
+ "reverb_test.cpp",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+}
+
+cc_test {
name: "snr",
host_supported: false,
diff --git a/media/libeffects/lvm/tests/build_and_run_all_unit_tests_reverb.sh b/media/libeffects/lvm/tests/build_and_run_all_unit_tests_reverb.sh
new file mode 100755
index 0000000..5a972db
--- /dev/null
+++ b/media/libeffects/lvm/tests/build_and_run_all_unit_tests_reverb.sh
@@ -0,0 +1,87 @@
+#!/bin/bash
+#
+# reverb test
+#
+
+if [ -z "$ANDROID_BUILD_TOP" ]; then
+ echo "Android build environment not set"
+ exit -1
+fi
+
+# ensure we have mm
+. $ANDROID_BUILD_TOP/build/envsetup.sh
+
+mm -j
+
+echo "waiting for device"
+
+adb root && adb wait-for-device remount
+
+# location of test files
+testdir="/data/local/tmp/revTest"
+
+echo "========================================"
+echo "testing reverb"
+adb shell mkdir -p $testdir
+adb push $ANDROID_BUILD_TOP/cts/tests/tests/media/res/raw/sinesweepraw.raw $testdir
+
+E_VAL=1
+cmds="adb push $OUT/testcases/reverb_test/arm/reverb_test $testdir"
+
+fs_arr=(
+ 8000
+ 16000
+ 22050
+ 32000
+ 44100
+ 48000
+ 88200
+ 96000
+ 176400
+ 192000
+)
+
+# run reverb at different configs, saving only the stereo channel
+# pair.
+error_count=0
+for cmd in "${cmds[@]}"
+do
+ $cmd
+ for preset_val in {0..6}
+ do
+ for fs in ${fs_arr[*]}
+ do
+ for chMask in {1..22}
+ do
+ adb shell LD_LIBRARY_PATH=/system/vendor/lib/soundfx $testdir/reverb_test \
+ --input $testdir/sinesweepraw.raw \
+ --output $testdir/sinesweep_$((chMask))_$((fs)).raw \
+ --chMask $chMask --fs $fs --preset $preset_val
+
+ shell_ret=$?
+ if [ $shell_ret -ne 0 ]; then
+ echo "error: $shell_ret"
+ ((++error_count))
+ fi
+
+ # two channel files should be identical to higher channel
+ # computation (first 2 channels).
+ if [[ "$chMask" -gt 1 ]]
+ then
+ adb shell cmp $testdir/sinesweep_1_$((fs)).raw \
+ $testdir/sinesweep_$((chMask))_$((fs)).raw
+ fi
+ # cmp returns EXIT_FAILURE on mismatch.
+ shell_ret=$?
+ if [ $shell_ret -ne 0 ]; then
+ echo "error: $shell_ret"
+ ((++error_count))
+ fi
+ done
+ done
+ done
+done
+
+adb shell rm -r $testdir
+echo "$error_count errors"
+exit $error_count
diff --git a/media/libeffects/lvm/tests/reverb_test.cpp b/media/libeffects/lvm/tests/reverb_test.cpp
new file mode 100644
index 0000000..a9cf348
--- /dev/null
+++ b/media/libeffects/lvm/tests/reverb_test.cpp
@@ -0,0 +1,395 @@
+/*
+ * 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.
+ */
+#include <assert.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <iterator>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <vector>
+
+#include <audio_utils/channels.h>
+#include <audio_utils/primitives.h>
+#include <log/log.h>
+#include <system/audio.h>
+
+#include "EffectReverb.h"
+
+// This is the only symbol that needs to be exported
+extern audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM;
+
+// Global Variables
+enum ReverbParams {
+ ARG_HELP = 1,
+ ARG_INPUT,
+ ARG_OUTPUT,
+ ARG_FS,
+ ARG_CH_MASK,
+ ARG_PRESET,
+ ARG_AUX,
+ ARG_MONO_MODE,
+ ARG_FILE_CH,
+};
+
+const effect_uuid_t kReverbUuids[] = {
+ {0x172cdf00,
+ 0xa3bc,
+ 0x11df,
+ 0xa72f,
+ {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // preset-insert mode
+ {0xf29a1400,
+ 0xa3bb,
+ 0x11df,
+ 0x8ddc,
+ {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // preset-aux mode
+};
+
+// structures
+struct reverbConfigParams_t {
+ int fChannels = 2;
+ int monoMode = false;
+ int frameLength = 256;
+ int preset = 0;
+ int nrChannels = 2;
+ int sampleRate = 48000;
+ int auxiliary = 0;
+ audio_channel_mask_t chMask = AUDIO_CHANNEL_OUT_STEREO;
+};
+
+constexpr audio_channel_mask_t kReverbConfigChMask[] = {
+ AUDIO_CHANNEL_OUT_MONO,
+ AUDIO_CHANNEL_OUT_STEREO,
+ AUDIO_CHANNEL_OUT_2POINT1,
+ AUDIO_CHANNEL_OUT_2POINT0POINT2,
+ AUDIO_CHANNEL_OUT_QUAD,
+ AUDIO_CHANNEL_OUT_QUAD_BACK,
+ AUDIO_CHANNEL_OUT_QUAD_SIDE,
+ AUDIO_CHANNEL_OUT_SURROUND,
+ (1 << 4) - 1,
+ AUDIO_CHANNEL_OUT_2POINT1POINT2,
+ AUDIO_CHANNEL_OUT_3POINT0POINT2,
+ AUDIO_CHANNEL_OUT_PENTA,
+ (1 << 5) - 1,
+ AUDIO_CHANNEL_OUT_3POINT1POINT2,
+ AUDIO_CHANNEL_OUT_5POINT1,
+ AUDIO_CHANNEL_OUT_5POINT1_BACK,
+ AUDIO_CHANNEL_OUT_5POINT1_SIDE,
+ (1 << 6) - 1,
+ AUDIO_CHANNEL_OUT_6POINT1,
+ (1 << 7) - 1,
+ AUDIO_CHANNEL_OUT_5POINT1POINT2,
+ AUDIO_CHANNEL_OUT_7POINT1,
+ (1 << 8) - 1,
+};
+
+constexpr int kReverbConfigChMaskCount = std::size(kReverbConfigChMask);
+
+int reverbCreateEffect(effect_handle_t *pEffectHandle, effect_config_t *pConfig, int sessionId,
+ int ioId, int auxFlag) {
+ if (int status = AUDIO_EFFECT_LIBRARY_INFO_SYM.create_effect(&kReverbUuids[auxFlag], sessionId,
+ ioId, pEffectHandle);
+ status != 0) {
+ ALOGE("Reverb create returned an error = %d\n", status);
+ return EXIT_FAILURE;
+ }
+ int reply = 0;
+ uint32_t replySize = sizeof(reply);
+ (**pEffectHandle)
+ ->command(*pEffectHandle, EFFECT_CMD_SET_CONFIG, sizeof(effect_config_t), pConfig,
+ &replySize, &reply);
+ return reply;
+}
+
+int reverbSetConfigParam(uint32_t paramType, uint32_t paramValue, effect_handle_t effectHandle) {
+ int reply = 0;
+ uint32_t replySize = sizeof(reply);
+ uint32_t paramData[2] = {paramType, paramValue};
+ effect_param_t *effectParam =
+ (effect_param_t *)malloc(sizeof(*effectParam) + sizeof(paramData));
+ memcpy(&effectParam->data[0], ¶mData[0], sizeof(paramData));
+ effectParam->psize = sizeof(paramData[0]);
+ effectParam->vsize = sizeof(paramData[1]);
+ int status =
+ (*effectHandle)
+ ->command(effectHandle, EFFECT_CMD_SET_PARAM,
+ sizeof(effect_param_t) + sizeof(paramData), effectParam, &replySize, &reply);
+ free(effectParam);
+ if (status != 0) {
+ ALOGE("Reverb set config returned an error = %d\n", status);
+ return status;
+ }
+ return reply;
+}
+
+void printUsage() {
+ printf("\nUsage: ");
+ printf("\n <executable> [options]\n");
+ printf("\nwhere options are, ");
+ printf("\n --input <inputfile>");
+ printf("\n path to the input file");
+ printf("\n --output <outputfile>");
+ printf("\n path to the output file");
+ printf("\n --help");
+ printf("\n prints this usage information");
+ printf("\n --chMask <channel_mask>\n");
+ printf("\n 0 - AUDIO_CHANNEL_OUT_MONO");
+ printf("\n 1 - AUDIO_CHANNEL_OUT_STEREO");
+ printf("\n 2 - AUDIO_CHANNEL_OUT_2POINT1");
+ printf("\n 3 - AUDIO_CHANNEL_OUT_2POINT0POINT2");
+ printf("\n 4 - AUDIO_CHANNEL_OUT_QUAD");
+ printf("\n 5 - AUDIO_CHANNEL_OUT_QUAD_BACK");
+ printf("\n 6 - AUDIO_CHANNEL_OUT_QUAD_SIDE");
+ printf("\n 7 - AUDIO_CHANNEL_OUT_SURROUND");
+ printf("\n 8 - canonical channel index mask for 4 ch: (1 << 4) - 1");
+ printf("\n 9 - AUDIO_CHANNEL_OUT_2POINT1POINT2");
+ printf("\n 10 - AUDIO_CHANNEL_OUT_3POINT0POINT2");
+ printf("\n 11 - AUDIO_CHANNEL_OUT_PENTA");
+ printf("\n 12 - canonical channel index mask for 5 ch: (1 << 5) - 1");
+ printf("\n 13 - AUDIO_CHANNEL_OUT_3POINT1POINT2");
+ printf("\n 14 - AUDIO_CHANNEL_OUT_5POINT1");
+ printf("\n 15 - AUDIO_CHANNEL_OUT_5POINT1_BACK");
+ printf("\n 16 - AUDIO_CHANNEL_OUT_5POINT1_SIDE");
+ printf("\n 17 - canonical channel index mask for 6 ch: (1 << 6) - 1");
+ printf("\n 18 - AUDIO_CHANNEL_OUT_6POINT1");
+ printf("\n 19 - canonical channel index mask for 7 ch: (1 << 7) - 1");
+ printf("\n 20 - AUDIO_CHANNEL_OUT_5POINT1POINT2");
+ printf("\n 21 - AUDIO_CHANNEL_OUT_7POINT1");
+ printf("\n 22 - canonical channel index mask for 8 ch: (1 << 8) - 1");
+ printf("\n default 0");
+ printf("\n --fs <sampling_freq>");
+ printf("\n Sampling frequency in Hz, default 48000.");
+ printf("\n --preset <preset_value>");
+ printf("\n 0 - None");
+ printf("\n 1 - Small Room");
+ printf("\n 2 - Medium Room");
+ printf("\n 3 - Large Room");
+ printf("\n 4 - Medium Hall");
+ printf("\n 5 - Large Hall");
+ printf("\n 6 - Plate");
+ printf("\n default 0");
+ printf("\n --fch <file_channels>");
+ printf("\n number of channels in input file (1 through 8), default 1");
+ printf("\n --M");
+ printf("\n Mono mode (force all input audio channels to be identical)");
+ printf("\n --aux <auxiliary_flag> ");
+ printf("\n 0 - Insert Mode on");
+ printf("\n 1 - auxiliary Mode on");
+ printf("\n default 0");
+ printf("\n");
+}
+
+int main(int argc, const char *argv[]) {
+ if (argc == 1) {
+ printUsage();
+ return EXIT_FAILURE;
+ }
+
+ reverbConfigParams_t revConfigParams{}; // default initialize
+ const char *inputFile = nullptr;
+ const char *outputFile = nullptr;
+
+ const option long_opts[] = {
+ {"help", no_argument, nullptr, ARG_HELP},
+ {"input", required_argument, nullptr, ARG_INPUT},
+ {"output", required_argument, nullptr, ARG_OUTPUT},
+ {"fs", required_argument, nullptr, ARG_FS},
+ {"chMask", required_argument, nullptr, ARG_CH_MASK},
+ {"preset", required_argument, nullptr, ARG_PRESET},
+ {"aux", required_argument, nullptr, ARG_AUX},
+ {"M", no_argument, &revConfigParams.monoMode, true},
+ {"fch", required_argument, nullptr, ARG_FILE_CH},
+ {nullptr, 0, nullptr, 0},
+ };
+
+ while (true) {
+ const int opt = getopt_long(argc, (char *const *)argv, "i:o:", long_opts, nullptr);
+ if (opt == -1) {
+ break;
+ }
+ switch (opt) {
+ case ARG_HELP:
+ printUsage();
+ return EXIT_SUCCESS;
+ case ARG_INPUT: {
+ inputFile = (char *)optarg;
+ break;
+ }
+ case ARG_OUTPUT: {
+ outputFile = (char *)optarg;
+ break;
+ }
+ case ARG_FS: {
+ revConfigParams.sampleRate = atoi(optarg);
+ break;
+ }
+ case ARG_CH_MASK: {
+ int chMaskIdx = atoi(optarg);
+ if (chMaskIdx < 0 or chMaskIdx > kReverbConfigChMaskCount) {
+ ALOGE("Channel Mask index not in correct range\n");
+ printUsage();
+ return EXIT_FAILURE;
+ }
+ revConfigParams.chMask = kReverbConfigChMask[chMaskIdx];
+ break;
+ }
+ case ARG_PRESET: {
+ revConfigParams.preset = atoi(optarg);
+ break;
+ }
+ case ARG_AUX: {
+ revConfigParams.auxiliary = atoi(optarg);
+ break;
+ }
+ case ARG_MONO_MODE: {
+ break;
+ }
+ case ARG_FILE_CH: {
+ revConfigParams.fChannels = atoi(optarg);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ if (inputFile == nullptr) {
+ ALOGE("Error: missing input files\n");
+ printUsage();
+ return EXIT_FAILURE;
+ }
+ std::unique_ptr<FILE, decltype(&fclose)> inputFp(fopen(inputFile, "rb"), &fclose);
+
+ if (inputFp == nullptr) {
+ ALOGE("Cannot open input file %s\n", inputFile);
+ return EXIT_FAILURE;
+ }
+
+ if (outputFile == nullptr) {
+ ALOGE("Error: missing output files\n");
+ printUsage();
+ return EXIT_FAILURE;
+ }
+ std::unique_ptr<FILE, decltype(&fclose)> outputFp(fopen(outputFile, "wb"), &fclose);
+
+ if (outputFp == nullptr) {
+ ALOGE("Cannot open output file %s\n", outputFile);
+ return EXIT_FAILURE;
+ }
+
+ int32_t sessionId = 1;
+ int32_t ioId = 1;
+ effect_handle_t effectHandle = nullptr;
+ effect_config_t config;
+ config.inputCfg.samplingRate = config.outputCfg.samplingRate = revConfigParams.sampleRate;
+ config.inputCfg.channels = config.outputCfg.channels = revConfigParams.chMask;
+ config.inputCfg.format = config.outputCfg.format = AUDIO_FORMAT_PCM_FLOAT;
+ if (int status =
+ reverbCreateEffect(&effectHandle, &config, sessionId, ioId, revConfigParams.auxiliary);
+ status != 0) {
+ ALOGE("Create effect call returned error %i", status);
+ return EXIT_FAILURE;
+ }
+
+ int reply = 0;
+ uint32_t replySize = sizeof(reply);
+ (*effectHandle)->command(effectHandle, EFFECT_CMD_ENABLE, 0, nullptr, &replySize, &reply);
+ if (reply != 0) {
+ ALOGE("Command enable call returned error %d\n", reply);
+ return EXIT_FAILURE;
+ }
+
+ if (int status = reverbSetConfigParam(REVERB_PARAM_PRESET, (uint32_t)revConfigParams.preset,
+ effectHandle);
+ status != 0) {
+ ALOGE("Invalid reverb preset. Error %d\n", status);
+ return EXIT_FAILURE;
+ }
+
+ revConfigParams.nrChannels = audio_channel_count_from_out_mask(revConfigParams.chMask);
+ const int channelCount = revConfigParams.nrChannels;
+ const int frameLength = revConfigParams.frameLength;
+#ifdef BYPASS_EXEC
+ const int frameSize = (int)channelCount * sizeof(float);
+#endif
+ const int ioChannelCount = revConfigParams.fChannels;
+ const int ioFrameSize = ioChannelCount * sizeof(short);
+ const int maxChannelCount = std::max(channelCount, ioChannelCount);
+ /*
+ * Mono input will be converted to 2 channels internally in the process call
+ * by copying the same data into the second channel.
+ * Hence when channelCount is 1, output buffer should be allocated for
+ * 2 channels. The memAllocChCount takes care of allocation of sufficient
+ * memory for the output buffer.
+ */
+ const int memAllocChCount = (channelCount == 1 ? 2 : channelCount);
+
+ std::vector<short> in(frameLength * maxChannelCount);
+ std::vector<short> out(frameLength * maxChannelCount);
+ std::vector<float> floatIn(frameLength * channelCount);
+ std::vector<float> floatOut(frameLength * memAllocChCount);
+
+ int frameCounter = 0;
+
+ while (fread(in.data(), ioFrameSize, frameLength, inputFp.get()) == (size_t)frameLength) {
+ if (ioChannelCount != channelCount) {
+ adjust_channels(in.data(), ioChannelCount, in.data(), channelCount, sizeof(short),
+ frameLength * ioFrameSize);
+ }
+ memcpy_to_float_from_i16(floatIn.data(), in.data(), frameLength * channelCount);
+
+ // Mono mode will replicate the first channel to all other channels.
+ // This ensures all audio channels are identical. This is useful for testing
+ // Bass Boost, which extracts a mono signal for processing.
+ if (revConfigParams.monoMode && channelCount > 1) {
+ for (int i = 0; i < frameLength; ++i) {
+ auto *fp = &floatIn[i * channelCount];
+ std::fill(fp + 1, fp + channelCount, *fp); // replicate ch 0
+ }
+ }
+
+ audio_buffer_t inputBuffer, outputBuffer;
+ inputBuffer.frameCount = outputBuffer.frameCount = frameLength;
+ inputBuffer.f32 = floatIn.data();
+ outputBuffer.f32 = floatOut.data();
+#ifndef BYPASS_EXEC
+ if (int status = (*effectHandle)->process(effectHandle, &inputBuffer, &outputBuffer);
+ status != 0) {
+ ALOGE("\nError: Process returned with error %d\n", status);
+ return EXIT_FAILURE;
+ }
+#else
+ memcpy(floatOut.data(), floatIn.data(), frameLength * frameSize);
+#endif
+ memcpy_to_i16_from_float(out.data(), floatOut.data(), frameLength * channelCount);
+
+ if (ioChannelCount != channelCount) {
+ adjust_channels(out.data(), channelCount, out.data(), ioChannelCount, sizeof(short),
+ frameLength * channelCount * sizeof(short));
+ }
+ (void)fwrite(out.data(), ioFrameSize, frameLength, outputFp.get());
+ frameCounter += frameLength;
+ }
+
+ if (int status = AUDIO_EFFECT_LIBRARY_INFO_SYM.release_effect(effectHandle);
+ status != 0) {
+ ALOGE("Audio Preprocessing release returned an error = %d\n", status);
+ return EXIT_FAILURE;
+ }
+ printf("frameCounter: [%d]\n", frameCounter);
+
+ return EXIT_SUCCESS;
+}
diff --git a/media/libeffects/preprocessing/tests/Android.bp b/media/libeffects/preprocessing/tests/Android.bp
new file mode 100644
index 0000000..71f6e8f
--- /dev/null
+++ b/media/libeffects/preprocessing/tests/Android.bp
@@ -0,0 +1,30 @@
+// audio preprocessing unit test
+cc_test {
+ name: "AudioPreProcessingTest",
+
+ vendor: true,
+
+ relative_install_path: "soundfx",
+
+ srcs: ["PreProcessingTest.cpp"],
+
+ shared_libs: [
+ "libaudiopreprocessing",
+ "libaudioutils",
+ "liblog",
+ "libutils",
+ "libwebrtc_audio_preprocessing",
+ ],
+
+ cflags: [
+ "-DWEBRTC_POSIX",
+ "-fvisibility=default",
+ "-Wall",
+ "-Werror",
+ ],
+
+ header_libs: [
+ "libaudioeffects",
+ "libhardware_headers",
+ ],
+}
diff --git a/media/libeffects/preprocessing/tests/PreProcessingTest.cpp b/media/libeffects/preprocessing/tests/PreProcessingTest.cpp
new file mode 100644
index 0000000..5c81d78
--- /dev/null
+++ b/media/libeffects/preprocessing/tests/PreProcessingTest.cpp
@@ -0,0 +1,429 @@
+/*
+ * 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.
+ */
+
+#include <audio_effects/effect_aec.h>
+#include <audio_effects/effect_agc.h>
+#include <audio_effects/effect_ns.h>
+#include <audio_processing.h>
+#include <getopt.h>
+#include <hardware/audio_effect.h>
+#include <module_common_types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <utils/Log.h>
+#include <utils/Timers.h>
+
+#include <audio_utils/channels.h>
+#include <audio_utils/primitives.h>
+#include <log/log.h>
+#include <system/audio.h>
+
+// This is the only symbol that needs to be imported
+extern audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM;
+
+//------------------------------------------------------------------------------
+// local definitions
+//------------------------------------------------------------------------------
+
+// types of pre processing modules
+enum PreProcId {
+ PREPROC_AGC, // Automatic Gain Control
+ PREPROC_AEC, // Acoustic Echo Canceler
+ PREPROC_NS, // Noise Suppressor
+ PREPROC_NUM_EFFECTS
+};
+
+enum PreProcParams {
+ ARG_HELP = 1,
+ ARG_INPUT,
+ ARG_OUTPUT,
+ ARG_FAR,
+ ARG_FS,
+ ARG_CH_MASK,
+ ARG_AGC_TGT_LVL,
+ ARG_AGC_COMP_LVL,
+ ARG_AEC_DELAY,
+ ARG_NS_LVL,
+};
+
+struct preProcConfigParams_t {
+ int samplingFreq = 16000;
+ audio_channel_mask_t chMask = AUDIO_CHANNEL_IN_MONO;
+ int nsLevel = 0; // a value between 0-3
+ int agcTargetLevel = 3; // in dB
+ int agcCompLevel = 9; // in dB
+ int aecDelay = 0; // in ms
+};
+
+const effect_uuid_t kPreProcUuids[PREPROC_NUM_EFFECTS] = {
+ {0xaa8130e0, 0x66fc, 0x11e0, 0xbad0, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // agc uuid
+ {0xbb392ec0, 0x8d4d, 0x11e0, 0xa896, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // aec uuid
+ {0xc06c8400, 0x8e06, 0x11e0, 0x9cb6, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // ns uuid
+};
+
+constexpr audio_channel_mask_t kPreProcConfigChMask[] = {
+ AUDIO_CHANNEL_IN_MONO,
+ AUDIO_CHANNEL_IN_STEREO,
+ AUDIO_CHANNEL_IN_FRONT_BACK,
+ AUDIO_CHANNEL_IN_6,
+ AUDIO_CHANNEL_IN_2POINT0POINT2,
+ AUDIO_CHANNEL_IN_2POINT1POINT2,
+ AUDIO_CHANNEL_IN_3POINT0POINT2,
+ AUDIO_CHANNEL_IN_3POINT1POINT2,
+ AUDIO_CHANNEL_IN_5POINT1,
+ AUDIO_CHANNEL_IN_VOICE_UPLINK_MONO,
+ AUDIO_CHANNEL_IN_VOICE_DNLINK_MONO,
+ AUDIO_CHANNEL_IN_VOICE_CALL_MONO,
+};
+
+constexpr int kPreProcConfigChMaskCount = std::size(kPreProcConfigChMask);
+
+void printUsage() {
+ printf("\nUsage: ");
+ printf("\n <executable> [options]\n");
+ printf("\nwhere options are, ");
+ printf("\n --input <inputfile>");
+ printf("\n path to the input file");
+ printf("\n --output <outputfile>");
+ printf("\n path to the output file");
+ printf("\n --help");
+ printf("\n Prints this usage information");
+ printf("\n --fs <sampling_freq>");
+ printf("\n Sampling frequency in Hz, default 16000.");
+ printf("\n -ch_mask <channel_mask>\n");
+ printf("\n 0 - AUDIO_CHANNEL_IN_MONO");
+ printf("\n 1 - AUDIO_CHANNEL_IN_STEREO");
+ printf("\n 2 - AUDIO_CHANNEL_IN_FRONT_BACK");
+ printf("\n 3 - AUDIO_CHANNEL_IN_6");
+ printf("\n 4 - AUDIO_CHANNEL_IN_2POINT0POINT2");
+ printf("\n 5 - AUDIO_CHANNEL_IN_2POINT1POINT2");
+ printf("\n 6 - AUDIO_CHANNEL_IN_3POINT0POINT2");
+ printf("\n 7 - AUDIO_CHANNEL_IN_3POINT1POINT2");
+ printf("\n 8 - AUDIO_CHANNEL_IN_5POINT1");
+ printf("\n 9 - AUDIO_CHANNEL_IN_VOICE_UPLINK_MONO");
+ printf("\n 10 - AUDIO_CHANNEL_IN_VOICE_DNLINK_MONO ");
+ printf("\n 11 - AUDIO_CHANNEL_IN_VOICE_CALL_MONO ");
+ printf("\n default 0");
+ printf("\n --far <farend_file>");
+ printf("\n Path to far-end file needed for echo cancellation");
+ printf("\n --aec");
+ printf("\n Enable Echo Cancellation, default disabled");
+ printf("\n --ns");
+ printf("\n Enable Noise Suppression, default disabled");
+ printf("\n --agc");
+ printf("\n Enable Gain Control, default disabled");
+ printf("\n --ns_lvl <ns_level>");
+ printf("\n Noise Suppression level in dB, default value 0dB");
+ printf("\n --agc_tgt_lvl <target_level>");
+ printf("\n AGC Target Level in dB, default value 3dB");
+ printf("\n --agc_comp_lvl <comp_level>");
+ printf("\n AGC Comp Level in dB, default value 9dB");
+ printf("\n --aec_delay <delay>");
+ printf("\n AEC delay value in ms, default value 0ms");
+ printf("\n");
+}
+
+constexpr float kTenMilliSecVal = 0.01;
+
+int preProcCreateEffect(effect_handle_t *pEffectHandle, uint32_t effectType,
+ effect_config_t *pConfig, int sessionId, int ioId) {
+ if (int status = AUDIO_EFFECT_LIBRARY_INFO_SYM.create_effect(&kPreProcUuids[effectType],
+ sessionId, ioId, pEffectHandle);
+ status != 0) {
+ ALOGE("Audio Preprocessing create returned an error = %d\n", status);
+ return EXIT_FAILURE;
+ }
+ int reply = 0;
+ uint32_t replySize = sizeof(reply);
+ if (effectType == PREPROC_AEC) {
+ (**pEffectHandle)
+ ->command(*pEffectHandle, EFFECT_CMD_SET_CONFIG_REVERSE, sizeof(effect_config_t), pConfig,
+ &replySize, &reply);
+ }
+ (**pEffectHandle)
+ ->command(*pEffectHandle, EFFECT_CMD_SET_CONFIG, sizeof(effect_config_t), pConfig,
+ &replySize, &reply);
+ return reply;
+}
+
+int preProcSetConfigParam(uint32_t paramType, uint32_t paramValue, effect_handle_t effectHandle) {
+ int reply = 0;
+ uint32_t replySize = sizeof(reply);
+ uint32_t paramData[2] = {paramType, paramValue};
+ effect_param_t *effectParam =
+ (effect_param_t *)malloc(sizeof(*effectParam) + sizeof(paramData));
+ memcpy(&effectParam->data[0], ¶mData[0], sizeof(paramData));
+ effectParam->psize = sizeof(paramData[0]);
+ (*effectHandle)
+ ->command(effectHandle, EFFECT_CMD_SET_PARAM, sizeof(effect_param_t), effectParam,
+ &replySize, &reply);
+ free(effectParam);
+ return reply;
+}
+
+int main(int argc, const char *argv[]) {
+ if (argc == 1) {
+ printUsage();
+ return EXIT_FAILURE;
+ }
+ const char *inputFile = nullptr;
+ const char *outputFile = nullptr;
+ const char *farFile = nullptr;
+ int effectEn[PREPROC_NUM_EFFECTS] = {0};
+
+ const option long_opts[] = {
+ {"help", no_argument, nullptr, ARG_HELP},
+ {"input", required_argument, nullptr, ARG_INPUT},
+ {"output", required_argument, nullptr, ARG_OUTPUT},
+ {"far", required_argument, nullptr, ARG_FAR},
+ {"fs", required_argument, nullptr, ARG_FS},
+ {"ch_mask", required_argument, nullptr, ARG_CH_MASK},
+ {"agc_tgt_lvl", required_argument, nullptr, ARG_AGC_TGT_LVL},
+ {"agc_comp_lvl", required_argument, nullptr, ARG_AGC_COMP_LVL},
+ {"aec_delay", required_argument, nullptr, ARG_AEC_DELAY},
+ {"ns_lvl", required_argument, nullptr, ARG_NS_LVL},
+ {"aec", no_argument, &effectEn[PREPROC_AEC], 1},
+ {"agc", no_argument, &effectEn[PREPROC_AGC], 1},
+ {"ns", no_argument, &effectEn[PREPROC_NS], 1},
+ {nullptr, 0, nullptr, 0},
+ };
+ struct preProcConfigParams_t preProcCfgParams {};
+
+ while (true) {
+ const int opt = getopt_long(argc, (char *const *)argv, "i:o:", long_opts, nullptr);
+ if (opt == -1) {
+ break;
+ }
+ switch (opt) {
+ case ARG_HELP:
+ printUsage();
+ return 0;
+ case ARG_INPUT: {
+ inputFile = (char *)optarg;
+ break;
+ }
+ case ARG_OUTPUT: {
+ outputFile = (char *)optarg;
+ break;
+ }
+ case ARG_FAR: {
+ farFile = (char *)optarg;
+ break;
+ }
+ case ARG_FS: {
+ preProcCfgParams.samplingFreq = atoi(optarg);
+ break;
+ }
+ case ARG_CH_MASK: {
+ int chMaskIdx = atoi(optarg);
+ if (chMaskIdx < 0 or chMaskIdx > kPreProcConfigChMaskCount) {
+ ALOGE("Channel Mask index not in correct range\n");
+ printUsage();
+ return EXIT_FAILURE;
+ }
+ preProcCfgParams.chMask = kPreProcConfigChMask[chMaskIdx];
+ break;
+ }
+ case ARG_AGC_TGT_LVL: {
+ preProcCfgParams.agcTargetLevel = atoi(optarg);
+ break;
+ }
+ case ARG_AGC_COMP_LVL: {
+ preProcCfgParams.agcCompLevel = atoi(optarg);
+ break;
+ }
+ case ARG_AEC_DELAY: {
+ preProcCfgParams.aecDelay = atoi(optarg);
+ break;
+ }
+ case ARG_NS_LVL: {
+ preProcCfgParams.nsLevel = atoi(optarg);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ if (inputFile == nullptr) {
+ ALOGE("Error: missing input file\n");
+ printUsage();
+ return EXIT_FAILURE;
+ }
+
+ std::unique_ptr<FILE, decltype(&fclose)> inputFp(fopen(inputFile, "rb"), &fclose);
+ if (inputFp == nullptr) {
+ ALOGE("Cannot open input file %s\n", inputFile);
+ return EXIT_FAILURE;
+ }
+
+ std::unique_ptr<FILE, decltype(&fclose)> farFp(fopen(farFile, "rb"), &fclose);
+ std::unique_ptr<FILE, decltype(&fclose)> outputFp(fopen(outputFile, "wb"), &fclose);
+ if (effectEn[PREPROC_AEC]) {
+ if (farFile == nullptr) {
+ ALOGE("Far end signal file required for echo cancellation \n");
+ return EXIT_FAILURE;
+ }
+ if (farFp == nullptr) {
+ ALOGE("Cannot open far end stream file %s\n", farFile);
+ return EXIT_FAILURE;
+ }
+ struct stat statInput, statFar;
+ (void)fstat(fileno(inputFp.get()), &statInput);
+ (void)fstat(fileno(farFp.get()), &statFar);
+ if (statInput.st_size != statFar.st_size) {
+ ALOGE("Near and far end signals are of different sizes");
+ return EXIT_FAILURE;
+ }
+ }
+ if (outputFile != nullptr && outputFp == nullptr) {
+ ALOGE("Cannot open output file %s\n", outputFile);
+ return EXIT_FAILURE;
+ }
+
+ int32_t sessionId = 1;
+ int32_t ioId = 1;
+ effect_handle_t effectHandle[PREPROC_NUM_EFFECTS] = {nullptr};
+ effect_config_t config;
+ config.inputCfg.samplingRate = config.outputCfg.samplingRate = preProcCfgParams.samplingFreq;
+ config.inputCfg.channels = config.outputCfg.channels = preProcCfgParams.chMask;
+ config.inputCfg.format = config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
+
+ // Create all the effect handles
+ for (int i = 0; i < PREPROC_NUM_EFFECTS; i++) {
+ if (int status = preProcCreateEffect(&effectHandle[i], i, &config, sessionId, ioId);
+ status != 0) {
+ ALOGE("Create effect call returned error %i", status);
+ return EXIT_FAILURE;
+ }
+ }
+
+ for (int i = 0; i < PREPROC_NUM_EFFECTS; i++) {
+ if (effectEn[i] == 1) {
+ int reply = 0;
+ uint32_t replySize = sizeof(reply);
+ (*effectHandle[i])
+ ->command(effectHandle[i], EFFECT_CMD_ENABLE, 0, nullptr, &replySize, &reply);
+ if (reply != 0) {
+ ALOGE("Command enable call returned error %d\n", reply);
+ return EXIT_FAILURE;
+ }
+ }
+ }
+
+ // Set Config Params of the effects
+ if (effectEn[PREPROC_AGC]) {
+ if (int status = preProcSetConfigParam(AGC_PARAM_TARGET_LEVEL,
+ (uint32_t)preProcCfgParams.agcTargetLevel,
+ effectHandle[PREPROC_AGC]);
+ status != 0) {
+ ALOGE("Invalid AGC Target Level. Error %d\n", status);
+ return EXIT_FAILURE;
+ }
+ if (int status =
+ preProcSetConfigParam(AGC_PARAM_COMP_GAIN, (uint32_t)preProcCfgParams.agcCompLevel,
+ effectHandle[PREPROC_AGC]);
+ status != 0) {
+ ALOGE("Invalid AGC Comp Gain. Error %d\n", status);
+ return EXIT_FAILURE;
+ }
+ }
+ if (effectEn[PREPROC_NS]) {
+ if (int status = preProcSetConfigParam(NS_PARAM_LEVEL, (uint32_t)preProcCfgParams.nsLevel,
+ effectHandle[PREPROC_NS]);
+ status != 0) {
+ ALOGE("Invalid Noise Suppression level Error %d\n", status);
+ return EXIT_FAILURE;
+ }
+ }
+
+ // Process Call
+ const int frameLength = (int)(preProcCfgParams.samplingFreq * kTenMilliSecVal);
+ const int ioChannelCount = audio_channel_count_from_in_mask(preProcCfgParams.chMask);
+ const int ioFrameSize = ioChannelCount * sizeof(short);
+ int frameCounter = 0;
+ while (true) {
+ std::vector<short> in(frameLength * ioChannelCount);
+ std::vector<short> out(frameLength * ioChannelCount);
+ std::vector<short> farIn(frameLength * ioChannelCount);
+ size_t samplesRead = fread(in.data(), ioFrameSize, frameLength, inputFp.get());
+ if (samplesRead == 0) {
+ break;
+ }
+ audio_buffer_t inputBuffer, outputBuffer;
+ audio_buffer_t farInBuffer{};
+ inputBuffer.frameCount = samplesRead;
+ outputBuffer.frameCount = samplesRead;
+ inputBuffer.s16 = in.data();
+ outputBuffer.s16 = out.data();
+
+ if (farFp != nullptr) {
+ samplesRead = fread(farIn.data(), ioFrameSize, frameLength, farFp.get());
+ if (samplesRead == 0) {
+ break;
+ }
+ farInBuffer.frameCount = samplesRead;
+ farInBuffer.s16 = farIn.data();
+ }
+
+ for (int i = 0; i < PREPROC_NUM_EFFECTS; i++) {
+ if (effectEn[i] == 1) {
+ if (i == PREPROC_AEC) {
+ if (int status =
+ preProcSetConfigParam(AEC_PARAM_ECHO_DELAY, (uint32_t)preProcCfgParams.aecDelay,
+ effectHandle[PREPROC_AEC]);
+ status != 0) {
+ ALOGE("preProcSetConfigParam returned Error %d\n", status);
+ return EXIT_FAILURE;
+ }
+ }
+ if (int status =
+ (*effectHandle[i])->process(effectHandle[i], &inputBuffer, &outputBuffer);
+ status != 0) {
+ ALOGE("\nError: Process i = %d returned with error %d\n", i, status);
+ return EXIT_FAILURE;
+ }
+ if (i == PREPROC_AEC) {
+ if (int status = (*effectHandle[i])
+ ->process_reverse(effectHandle[i], &farInBuffer, &outputBuffer);
+ status != 0) {
+ ALOGE("\nError: Process reverse i = %d returned with error %d\n", i, status);
+ return EXIT_FAILURE;
+ }
+ }
+ }
+ }
+ if (outputFp != nullptr) {
+ size_t samplesWritten =
+ fwrite(out.data(), ioFrameSize, outputBuffer.frameCount, outputFp.get());
+ if (samplesWritten != outputBuffer.frameCount) {
+ ALOGE("\nError: Output file writing failed");
+ break;
+ }
+ }
+ frameCounter += frameLength;
+ }
+ // Release all the effect handles created
+ for (int i = 0; i < PREPROC_NUM_EFFECTS; i++) {
+ if (int status = AUDIO_EFFECT_LIBRARY_INFO_SYM.release_effect(effectHandle[i]);
+ status != 0) {
+ ALOGE("Audio Preprocessing release returned an error = %d\n", status);
+ return EXIT_FAILURE;
+ }
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/media/libeffects/proxy/EffectProxy.cpp b/media/libeffects/proxy/EffectProxy.cpp
index 42e44f0..c010d68 100644
--- a/media/libeffects/proxy/EffectProxy.cpp
+++ b/media/libeffects/proxy/EffectProxy.cpp
@@ -30,7 +30,7 @@
#include <media/EffectsFactoryApi.h>
namespace android {
-// This is a dummy proxy descriptor just to return to Factory during the initial
+// This is a stub proxy descriptor just to return to Factory during the initial
// GetDescriptor call. Later in the factory, it is replaced with the
// SW sub effect descriptor
// proxy UUID af8da7e0-2ca1-11e3-b71d-0002a5d5c51b
diff --git a/media/libmediahelper/TypeConverter.cpp b/media/libmediahelper/TypeConverter.cpp
index 6382ce4..705959a 100644
--- a/media/libmediahelper/TypeConverter.cpp
+++ b/media/libmediahelper/TypeConverter.cpp
@@ -57,6 +57,8 @@
MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_USB_HEADSET),
MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_HEARING_AID),
MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_ECHO_CANCELLER),
+ MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_BLE_HEADSET),
+ MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_BLE_SPEAKER),
MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_DEFAULT),
// STUB must be after DEFAULT, so the latter is picked up by toString first.
MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_OUT_STUB),
@@ -96,6 +98,7 @@
MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_USB_HEADSET),
MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_BLUETOOTH_BLE),
MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_ECHO_REFERENCE),
+ MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_BLE_HEADSET),
MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_DEFAULT),
// STUB must be after DEFAULT, so the latter is picked up by toString first.
MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_STUB),
diff --git a/media/libmediaplayerservice/tests/stagefrightRecorder/Android.bp b/media/libmediaplayerservice/tests/stagefrightRecorder/Android.bp
new file mode 100644
index 0000000..5a52ea5
--- /dev/null
+++ b/media/libmediaplayerservice/tests/stagefrightRecorder/Android.bp
@@ -0,0 +1,58 @@
+/*
+ * 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: "StagefrightRecorderTest",
+ gtest: true,
+
+ srcs: [
+ "StagefrightRecorderTest.cpp",
+ ],
+
+ include_dirs: [
+ "system/media/audio/include",
+ "frameworks/av/include",
+ "frameworks/av/camera/include",
+ "frameworks/av/media/libmediaplayerservice",
+ "frameworks/av/media/libmediametrics/include",
+ "frameworks/av/media/ndk/include",
+ ],
+
+ shared_libs: [
+ "liblog",
+ "libmedia",
+ "libbinder",
+ "libutils",
+ "libmediaplayerservice",
+ "libstagefright",
+ "libmediandk",
+ ],
+
+ compile_multilib: "32",
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+ sanitize: {
+ cfi: true,
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ },
+}
diff --git a/media/libmediaplayerservice/tests/stagefrightRecorder/StagefrightRecorderTest.cpp b/media/libmediaplayerservice/tests/stagefrightRecorder/StagefrightRecorderTest.cpp
new file mode 100644
index 0000000..ac17ef3
--- /dev/null
+++ b/media/libmediaplayerservice/tests/stagefrightRecorder/StagefrightRecorderTest.cpp
@@ -0,0 +1,318 @@
+/*
+ * 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 "StagefrightRecorderTest"
+#include <utils/Log.h>
+
+#include <gtest/gtest.h>
+
+#include <chrono>
+#include <ctime>
+#include <iostream>
+#include <string>
+#include <thread>
+
+#include <MediaPlayerService.h>
+#include <media/NdkMediaExtractor.h>
+#include <media/stagefright/MediaCodec.h>
+#include <system/audio-base.h>
+
+#include "StagefrightRecorder.h"
+
+#define OUTPUT_INFO_FILE_NAME "/data/local/tmp/stfrecorder_audio.info"
+#define OUTPUT_FILE_NAME_AUDIO "/data/local/tmp/stfrecorder_audio.raw"
+
+const bool kDebug = false;
+constexpr int32_t kMaxLoopCount = 10;
+constexpr int32_t kClipDurationInSec = 4;
+constexpr int32_t kPauseTimeInSec = 2;
+// Tolerance value for extracted clipduration is maximum 10% of total clipduration
+constexpr int32_t kToleranceValueInUs = kClipDurationInSec * 100000;
+
+using namespace android;
+
+class StagefrightRecorderTest
+ : public ::testing::TestWithParam<std::pair<output_format, audio_encoder>> {
+ public:
+ StagefrightRecorderTest() : mStfRecorder(nullptr), mOutputAudioFp(nullptr) {
+ mExpectedDurationInMs = 0;
+ mExpectedPauseInMs = 0;
+ }
+
+ ~StagefrightRecorderTest() {
+ if (mStfRecorder) free(mStfRecorder);
+ if (mOutputAudioFp) fclose(mOutputAudioFp);
+ }
+
+ void SetUp() override {
+ mStfRecorder = new StagefrightRecorder(String16(LOG_TAG));
+ ASSERT_NE(mStfRecorder, nullptr) << "Failed to create the instance of recorder";
+
+ mOutputAudioFp = fopen(OUTPUT_FILE_NAME_AUDIO, "wb");
+ ASSERT_NE(mOutputAudioFp, nullptr) << "Failed to open output file "
+ << OUTPUT_FILE_NAME_AUDIO << " for stagefright recorder";
+
+ int32_t fd = fileno(mOutputAudioFp);
+ ASSERT_GE(fd, 0) << "Failed to get the file descriptor of the output file for "
+ << OUTPUT_FILE_NAME_AUDIO;
+
+ status_t status = mStfRecorder->setOutputFile(fd);
+ ASSERT_EQ(status, OK) << "Failed to set the output file " << OUTPUT_FILE_NAME_AUDIO
+ << " for stagefright recorder";
+ }
+
+ void TearDown() override {
+ if (mOutputAudioFp) {
+ fclose(mOutputAudioFp);
+ mOutputAudioFp = nullptr;
+ }
+ if (!kDebug) {
+ int32_t status = remove(OUTPUT_FILE_NAME_AUDIO);
+ ASSERT_EQ(status, 0) << "Unable to delete the output file " << OUTPUT_FILE_NAME_AUDIO;
+ }
+ }
+
+ void setAudioRecorderFormat(output_format outputFormat, audio_encoder encoder,
+ audio_source_t audioSource = AUDIO_SOURCE_DEFAULT);
+ void recordMedia(bool isPaused = false, int32_t numStart = 0, int32_t numPause = 0);
+ void dumpInfo();
+ void setupExtractor(AMediaExtractor *extractor, int32_t &trackCount);
+ void validateOutput();
+
+ MediaRecorderBase *mStfRecorder;
+ FILE *mOutputAudioFp;
+ double mExpectedDurationInMs;
+ double mExpectedPauseInMs;
+};
+
+void StagefrightRecorderTest::setAudioRecorderFormat(output_format outputFormat,
+ audio_encoder encoder,
+ audio_source_t audioSource) {
+ status_t status = mStfRecorder->setAudioSource(audioSource);
+ ASSERT_EQ(status, OK) << "Failed to set the audio source: " << audioSource;
+
+ status = mStfRecorder->setOutputFormat(outputFormat);
+ ASSERT_EQ(status, OK) << "Failed to set the output format: " << outputFormat;
+
+ status = mStfRecorder->setAudioEncoder(encoder);
+ ASSERT_EQ(status, OK) << "Failed to set the audio encoder: " << encoder;
+}
+
+void StagefrightRecorderTest::recordMedia(bool isPause, int32_t numStart, int32_t numPause) {
+ status_t status = mStfRecorder->init();
+ ASSERT_EQ(status, OK) << "Failed to initialize stagefright recorder";
+
+ status = mStfRecorder->prepare();
+ ASSERT_EQ(status, OK) << "Failed to preapre the reorder";
+
+ // first start should succeed.
+ status = mStfRecorder->start();
+ ASSERT_EQ(status, OK) << "Failed to start the recorder";
+
+ for (int32_t count = 0; count < numStart; count++) {
+ status = mStfRecorder->start();
+ }
+
+ auto tStart = std::chrono::high_resolution_clock::now();
+ // Recording media for 4 secs
+ std::this_thread::sleep_for(std::chrono::seconds(kClipDurationInSec));
+ auto tEnd = std::chrono::high_resolution_clock::now();
+ mExpectedDurationInMs = std::chrono::duration<double, std::milli>(tEnd - tStart).count();
+
+ if (isPause) {
+ // first pause should succeed.
+ status = mStfRecorder->pause();
+ ASSERT_EQ(status, OK) << "Failed to pause the recorder";
+
+ tStart = std::chrono::high_resolution_clock::now();
+ // Paused recorder for 2 secs
+ std::this_thread::sleep_for(std::chrono::seconds(kPauseTimeInSec));
+
+ for (int32_t count = 0; count < numPause; count++) {
+ status = mStfRecorder->pause();
+ }
+
+ tEnd = std::chrono::high_resolution_clock::now();
+ mExpectedPauseInMs = std::chrono::duration<double, std::milli>(tEnd - tStart).count();
+
+ status = mStfRecorder->resume();
+ ASSERT_EQ(status, OK) << "Failed to resume the recorder";
+
+ auto tStart = std::chrono::high_resolution_clock::now();
+ // Recording media for 4 secs
+ std::this_thread::sleep_for(std::chrono::seconds(kClipDurationInSec));
+ auto tEnd = std::chrono::high_resolution_clock::now();
+ mExpectedDurationInMs += std::chrono::duration<double, std::milli>(tEnd - tStart).count();
+ }
+ status = mStfRecorder->stop();
+ ASSERT_EQ(status, OK) << "Failed to stop the recorder";
+}
+
+void StagefrightRecorderTest::dumpInfo() {
+ FILE *dumpOutput = fopen(OUTPUT_INFO_FILE_NAME, "wb");
+ int32_t dumpFd = fileno(dumpOutput);
+ Vector<String16> args;
+ status_t status = mStfRecorder->dump(dumpFd, args);
+ ASSERT_EQ(status, OK) << "Failed to dump the info for the recorder";
+ fclose(dumpOutput);
+}
+
+void StagefrightRecorderTest::setupExtractor(AMediaExtractor *extractor, int32_t &trackCount) {
+ int32_t fd = open(OUTPUT_FILE_NAME_AUDIO, O_RDONLY);
+ ASSERT_GE(fd, 0) << "Failed to open recorder's output file " << OUTPUT_FILE_NAME_AUDIO
+ << " to validate";
+
+ struct stat buf;
+ int32_t status = fstat(fd, &buf);
+ ASSERT_EQ(status, 0) << "Failed to get properties of input file " << OUTPUT_FILE_NAME_AUDIO
+ << " for extractor";
+
+ size_t fileSize = buf.st_size;
+ ASSERT_GT(fileSize, 0) << "Size of input file " << OUTPUT_FILE_NAME_AUDIO
+ << " to extractor cannot be zero";
+ ALOGV("Size of input file to extractor: %zu", fileSize);
+
+ status = AMediaExtractor_setDataSourceFd(extractor, fd, 0, fileSize);
+ ASSERT_EQ(status, AMEDIA_OK) << "Failed to set data source for extractor";
+
+ trackCount = AMediaExtractor_getTrackCount(extractor);
+ ALOGV("Number of tracks reported by extractor : %d", trackCount);
+}
+
+// Validate recoder's output using extractor
+void StagefrightRecorderTest::validateOutput() {
+ int32_t trackCount = -1;
+ AMediaExtractor *extractor = AMediaExtractor_new();
+ ASSERT_NE(extractor, nullptr) << "Failed to create extractor";
+ ASSERT_NO_FATAL_FAILURE(setupExtractor(extractor, trackCount));
+ ASSERT_EQ(trackCount, 1) << "Expected 1 track, saw " << trackCount;
+
+ for (int32_t idx = 0; idx < trackCount; idx++) {
+ AMediaExtractor_selectTrack(extractor, idx);
+ AMediaFormat *format = AMediaExtractor_getTrackFormat(extractor, idx);
+ ASSERT_NE(format, nullptr) << "Track format is NULL";
+ ALOGI("Track format = %s", AMediaFormat_toString(format));
+
+ int64_t clipDurationUs;
+ AMediaFormat_getInt64(format, AMEDIAFORMAT_KEY_DURATION, &clipDurationUs);
+ int32_t diff = abs((mExpectedDurationInMs * 1000) - clipDurationUs);
+ ASSERT_LE(diff, kToleranceValueInUs)
+ << "Expected duration: " << (mExpectedDurationInMs * 1000)
+ << " Actual duration: " << clipDurationUs << " Difference: " << diff
+ << " Difference is expected to be less than tolerance value: " << kToleranceValueInUs;
+
+ const char *mime = nullptr;
+ AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime);
+ ASSERT_NE(mime, nullptr) << "Track mime is NULL";
+ ALOGI("Track mime = %s", mime);
+
+ int32_t sampleRate, channelCount, bitRate;
+ AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &channelCount);
+ ALOGI("Channel count reported by extractor: %d", channelCount);
+ AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, &sampleRate);
+ ALOGI("Sample Rate reported by extractor: %d", sampleRate);
+ AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, &bitRate);
+ ALOGI("Bit Rate reported by extractor: %d", bitRate);
+ }
+}
+
+TEST_F(StagefrightRecorderTest, RecordingAudioSanityTest) {
+ ASSERT_NO_FATAL_FAILURE(setAudioRecorderFormat(OUTPUT_FORMAT_DEFAULT, AUDIO_ENCODER_DEFAULT));
+
+ int32_t maxAmplitude = -1;
+ status_t status = mStfRecorder->getMaxAmplitude(&maxAmplitude);
+ ASSERT_EQ(maxAmplitude, 0) << "Invalid value of max amplitude";
+
+ ASSERT_NO_FATAL_FAILURE(recordMedia());
+
+ // Verify getMetrics() behavior
+ Parcel parcel;
+ status = mStfRecorder->getMetrics(&parcel);
+ ASSERT_EQ(status, OK) << "Failed to get the parcel from getMetrics";
+ ALOGV("Size of the Parcel returned by getMetrics: %zu", parcel.dataSize());
+ ASSERT_GT(parcel.dataSize(), 0) << "Parcel size reports empty record";
+ ASSERT_NO_FATAL_FAILURE(validateOutput());
+ if (kDebug) {
+ ASSERT_NO_FATAL_FAILURE(dumpInfo());
+ }
+}
+
+TEST_P(StagefrightRecorderTest, MultiFormatAudioRecordTest) {
+ output_format outputFormat = GetParam().first;
+ audio_encoder audioEncoder = GetParam().second;
+ ASSERT_NO_FATAL_FAILURE(setAudioRecorderFormat(outputFormat, audioEncoder));
+ ASSERT_NO_FATAL_FAILURE(recordMedia());
+ // TODO(b/161687761)
+ // Skip for AMR-NB/WB output format
+ if (!(outputFormat == OUTPUT_FORMAT_AMR_NB || outputFormat == OUTPUT_FORMAT_AMR_WB)) {
+ ASSERT_NO_FATAL_FAILURE(validateOutput());
+ }
+ if (kDebug) {
+ ASSERT_NO_FATAL_FAILURE(dumpInfo());
+ }
+}
+
+TEST_F(StagefrightRecorderTest, GetActiveMicrophonesTest) {
+ ASSERT_NO_FATAL_FAILURE(
+ setAudioRecorderFormat(OUTPUT_FORMAT_DEFAULT, AUDIO_ENCODER_DEFAULT, AUDIO_SOURCE_MIC));
+
+ status_t status = mStfRecorder->init();
+ ASSERT_EQ(status, OK) << "Init failed for stagefright recorder";
+
+ status = mStfRecorder->prepare();
+ ASSERT_EQ(status, OK) << "Failed to preapre the reorder";
+
+ status = mStfRecorder->start();
+ ASSERT_EQ(status, OK) << "Failed to start the recorder";
+
+ // Record media for 4 secs
+ std::this_thread::sleep_for(std::chrono::seconds(kClipDurationInSec));
+
+ std::vector<media::MicrophoneInfo> activeMicrophones{};
+ status = mStfRecorder->getActiveMicrophones(&activeMicrophones);
+ ASSERT_EQ(status, OK) << "Failed to get Active Microphones";
+ ASSERT_GT(activeMicrophones.size(), 0) << "No active microphones are found";
+
+ status = mStfRecorder->stop();
+ ASSERT_EQ(status, OK) << "Failed to stop the recorder";
+ if (kDebug) {
+ ASSERT_NO_FATAL_FAILURE(dumpInfo());
+ }
+}
+
+TEST_F(StagefrightRecorderTest, MultiStartPauseTest) {
+ ASSERT_NO_FATAL_FAILURE(setAudioRecorderFormat(OUTPUT_FORMAT_DEFAULT, AUDIO_ENCODER_DEFAULT));
+ ASSERT_NO_FATAL_FAILURE(recordMedia(true, kMaxLoopCount, kMaxLoopCount));
+ ASSERT_NO_FATAL_FAILURE(validateOutput());
+ if (kDebug) {
+ ASSERT_NO_FATAL_FAILURE(dumpInfo());
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ StagefrightRecorderTestAll, StagefrightRecorderTest,
+ ::testing::Values(std::make_pair(OUTPUT_FORMAT_AMR_NB, AUDIO_ENCODER_AMR_NB),
+ std::make_pair(OUTPUT_FORMAT_AMR_WB, AUDIO_ENCODER_AMR_WB),
+ std::make_pair(OUTPUT_FORMAT_AAC_ADTS, AUDIO_ENCODER_AAC),
+ std::make_pair(OUTPUT_FORMAT_OGG, AUDIO_ENCODER_OPUS)));
+
+int main(int argc, char **argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = RUN_ALL_TESTS();
+ ALOGV("Test result = %d\n", status);
+ return status;
+}
diff --git a/media/libnbaio/include/media/nbaio/Pipe.h b/media/libnbaio/include/media/nbaio/Pipe.h
index 0431976..54dc08f 100644
--- a/media/libnbaio/include/media/nbaio/Pipe.h
+++ b/media/libnbaio/include/media/nbaio/Pipe.h
@@ -23,7 +23,7 @@
namespace android {
// Pipe is multi-thread safe for readers (see PipeReader), but safe for only a single writer thread.
-// It cannot UNDERRUN on write, unless we allow designation of a master reader that provides the
+// It cannot UNDERRUN on write, unless we allow designation of a primary reader that provides the
// time-base. Readers can be added and removed dynamically, and it's OK to have no readers.
class Pipe : public NBAIO_Sink {
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 63ab654..5a8d2f9 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -887,7 +887,7 @@
sp<DataConverter> converter = mConverter[portIndex];
if (converter != NULL) {
- // here we assume sane conversions of max 4:1, so result fits in int32
+ // here we assume conversions of max 4:1, so result fits in int32
if (portIndex == kPortIndexInput) {
conversionBufferSize = converter->sourceSize(bufSize);
} else {
@@ -2602,15 +2602,15 @@
unsigned int numLayers = 0;
unsigned int numBLayers = 0;
int tags;
- char dummy;
+ char tmp;
OMX_VIDEO_ANDROID_TEMPORALLAYERINGPATTERNTYPE pattern =
OMX_VIDEO_AndroidTemporalLayeringPatternNone;
- if (sscanf(tsSchema.c_str(), "webrtc.vp8.%u-layer%c", &numLayers, &dummy) == 1
+ if (sscanf(tsSchema.c_str(), "webrtc.vp8.%u-layer%c", &numLayers, &tmp) == 1
&& numLayers > 0) {
pattern = OMX_VIDEO_AndroidTemporalLayeringPatternWebRTC;
} else if ((tags = sscanf(tsSchema.c_str(), "android.generic.%u%c%u%c",
- &numLayers, &dummy, &numBLayers, &dummy))
- && (tags == 1 || (tags == 3 && dummy == '+'))
+ &numLayers, &tmp, &numBLayers, &tmp))
+ && (tags == 1 || (tags == 3 && tmp == '+'))
&& numLayers > 0 && numLayers < UINT32_MAX - numBLayers) {
numLayers += numBLayers;
pattern = OMX_VIDEO_AndroidTemporalLayeringPatternAndroid;
@@ -4757,15 +4757,15 @@
unsigned int numLayers = 0;
unsigned int numBLayers = 0;
int tags;
- char dummy;
- if (sscanf(tsSchema.c_str(), "webrtc.vp8.%u-layer%c", &numLayers, &dummy) == 1
+ char tmp;
+ if (sscanf(tsSchema.c_str(), "webrtc.vp8.%u-layer%c", &numLayers, &tmp) == 1
&& numLayers > 0) {
pattern = OMX_VIDEO_VPXTemporalLayerPatternWebRTC;
tsType = OMX_VIDEO_AndroidTemporalLayeringPatternWebRTC;
tsLayers = numLayers;
} else if ((tags = sscanf(tsSchema.c_str(), "android.generic.%u%c%u%c",
- &numLayers, &dummy, &numBLayers, &dummy))
- && (tags == 1 || (tags == 3 && dummy == '+'))
+ &numLayers, &tmp, &numBLayers, &tmp))
+ && (tags == 1 || (tags == 3 && tmp == '+'))
&& numLayers > 0 && numLayers < UINT32_MAX - numBLayers) {
pattern = OMX_VIDEO_VPXTemporalLayerPatternWebRTC;
// VPX does not have a concept of B-frames, so just count all layers
@@ -7631,8 +7631,8 @@
mInputFormat->setInt64("android._stop-time-offset-us", stopTimeOffsetUs);
}
- int32_t dummy;
- if (params->findInt32("request-sync", &dummy)) {
+ int32_t tmp;
+ if (params->findInt32("request-sync", &tmp)) {
status_t err = requestIDRFrame();
if (err != OK) {
diff --git a/media/libstagefright/OWNERS b/media/libstagefright/OWNERS
new file mode 100644
index 0000000..819389d
--- /dev/null
+++ b/media/libstagefright/OWNERS
@@ -0,0 +1,7 @@
+set noparent
+chz@google.com
+essick@google.com
+lajos@google.com
+marcone@google.com
+taklee@google.com
+wonsik@google.com
\ No newline at end of file
diff --git a/media/libstagefright/codecs/aacdec/DrcPresModeWrap.cpp b/media/libstagefright/codecs/aacdec/DrcPresModeWrap.cpp
index 168d140..157cab6 100644
--- a/media/libstagefright/codecs/aacdec/DrcPresModeWrap.cpp
+++ b/media/libstagefright/codecs/aacdec/DrcPresModeWrap.cpp
@@ -217,7 +217,7 @@
}
else { // handle other used encoder target levels
- // Sanity check: DRC presentation mode is only specified for max. 5.1 channels
+ // Validation check: DRC presentation mode is only specified for max. 5.1 channels
if (mStreamNrAACChan > 6) {
drcPresMode = 0;
}
@@ -308,7 +308,7 @@
} // switch()
} // if (mEncoderTarget == GPM_ENCODER_TARGET_LEVEL)
- // sanity again
+ // validation check again
if (newHeavy == 1) {
newBoostFactor=127; // not really needed as the same would be done by the decoder anyway
newAttFactor = 127;
diff --git a/media/libstagefright/codecs/amrnb/enc/src/qgain475.cpp b/media/libstagefright/codecs/amrnb/enc/src/qgain475.cpp
index f8da589..08a5c15 100644
--- a/media/libstagefright/codecs/amrnb/enc/src/qgain475.cpp
+++ b/media/libstagefright/codecs/amrnb/enc/src/qgain475.cpp
@@ -1106,7 +1106,7 @@
// the real, quantized gains)
gc_pred(pred_st, MR475, sf1_code_nosharp,
&sf1_exp_gcode0, &sf1_frac_gcode0,
- &sf0_exp_gcode0, &sf0_gcode0); // last two args are dummy
+ &sf0_exp_gcode0, &sf0_gcode0); // last two args are unused
sf1_gcode0 = extract_l(Pow2(14, sf1_frac_gcode0));
tmp = add (tmp, 2);
@@ -1426,7 +1426,7 @@
the real, quantized gains) */
gc_pred(pred_st, MR475, sf1_code_nosharp,
&sf1_exp_gcode0, &sf1_frac_gcode0,
- &sf0_exp_gcode0, &sf0_gcode0, /* dummy args */
+ &sf0_exp_gcode0, &sf0_gcode0, /* unused args */
pOverflow);
sf1_gcode0 = (Word16)(Pow2(14, sf1_frac_gcode0, pOverflow));
diff --git a/media/libstagefright/codecs/m4v_h263/dec/src/block_idct.cpp b/media/libstagefright/codecs/m4v_h263/dec/src/block_idct.cpp
index 3d10086..bc708e2 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/src/block_idct.cpp
+++ b/media/libstagefright/codecs/m4v_h263/dec/src/block_idct.cpp
@@ -506,6 +506,7 @@
/*----------------------------------------------------------------------------
; Function Code FOR idctrow
----------------------------------------------------------------------------*/
+__attribute__((no_sanitize("signed-integer-overflow")))
void idctrow(
int16 *blk, uint8 *pred, uint8 *dst, int width
)
@@ -828,6 +829,7 @@
/*----------------------------------------------------------------------------
; Function Code FOR idctcol
----------------------------------------------------------------------------*/
+__attribute__((no_sanitize("signed-integer-overflow")))
void idctcol(
int16 *blk
)
diff --git a/media/libstagefright/codecs/m4v_h263/dec/src/idct_vca.cpp b/media/libstagefright/codecs/m4v_h263/dec/src/idct_vca.cpp
index f35ce4f..0ba4944 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/src/idct_vca.cpp
+++ b/media/libstagefright/codecs/m4v_h263/dec/src/idct_vca.cpp
@@ -94,6 +94,7 @@
return;
}
+__attribute__((no_sanitize("signed-integer-overflow")))
void idctrow2(int16 *blk, uint8 *pred, uint8 *dst, int width)
{
int32 x0, x1, x2, x4, x5;
@@ -182,6 +183,7 @@
return ;
}
+__attribute__((no_sanitize("signed-integer-overflow")))
void idctrow3(int16 *blk, uint8 *pred, uint8 *dst, int width)
{
int32 x0, x1, x2, x3, x4, x5, x6, x7, x8;
@@ -291,6 +293,7 @@
}
+__attribute__((no_sanitize("signed-integer-overflow")))
void idctrow4(int16 *blk, uint8 *pred, uint8 *dst, int width)
{
int32 x0, x1, x2, x3, x4, x5, x6, x7, x8;
@@ -368,6 +371,7 @@
return ;
}
+__attribute__((no_sanitize("signed-integer-overflow")))
void idctcol4(int16 *blk)
{
int32 x0, x1, x2, x3, x4, x5, x6, x7, x8;
@@ -445,6 +449,7 @@
return;
}
+__attribute__((no_sanitize("signed-integer-overflow")))
void idctrow2_intra(int16 *blk, PIXEL *comp, int width)
{
int32 x0, x1, x2, x4, x5, temp;
@@ -502,6 +507,7 @@
return ;
}
+__attribute__((no_sanitize("signed-integer-overflow")))
void idctrow3_intra(int16 *blk, PIXEL *comp, int width)
{
int32 x0, x1, x2, x3, x4, x5, x6, x7, x8, temp;
@@ -575,6 +581,7 @@
return ;
}
+__attribute__((no_sanitize("signed-integer-overflow")))
void idctrow4_intra(int16 *blk, PIXEL *comp, int width)
{
int32 x0, x1, x2, x3, x4, x5, x6, x7, x8, temp;
diff --git a/media/libstagefright/foundation/tests/TypeTraits_test.cpp b/media/libstagefright/foundation/tests/TypeTraits_test.cpp
index 1e2049d..d5383d1 100644
--- a/media/libstagefright/foundation/tests/TypeTraits_test.cpp
+++ b/media/libstagefright/foundation/tests/TypeTraits_test.cpp
@@ -30,7 +30,7 @@
enum IA : int32_t { };
};
-// =========== basic sanity tests for type-support templates
+// =========== basic tests for type-support templates
TEST_F(TypeTraitsTest, StaticTests) {
// ============ is_integral_or_enum
diff --git a/media/libstagefright/omx/1.0/Omx.cpp b/media/libstagefright/omx/1.0/Omx.cpp
index eef9ce3..eb15039 100644
--- a/media/libstagefright/omx/1.0/Omx.cpp
+++ b/media/libstagefright/omx/1.0/Omx.cpp
@@ -22,7 +22,7 @@
#include <media/openmax/OMX_AsString.h>
#include <media/stagefright/omx/OMXUtils.h>
-#include <media/stagefright/omx/OMXMaster.h>
+#include <media/stagefright/omx/OMXStore.h>
#include <media/stagefright/omx/OmxGraphicBufferSource.h>
#include <media/stagefright/omx/1.0/WOmxNode.h>
@@ -41,21 +41,21 @@
constexpr size_t kMaxNodeInstances = (1 << 16);
Omx::Omx() :
- mMaster(new OMXMaster()),
+ mStore(new OMXStore()),
mParser() {
(void)mParser.parseXmlFilesInSearchDirs();
(void)mParser.parseXmlPath(mParser.defaultProfilingResultsXmlPath);
}
Omx::~Omx() {
- delete mMaster;
+ delete mStore;
}
Return<void> Omx::listNodes(listNodes_cb _hidl_cb) {
std::list<::android::IOMX::ComponentInfo> list;
char componentName[256];
for (OMX_U32 index = 0;
- mMaster->enumerateComponents(
+ mStore->enumerateComponents(
componentName, sizeof(componentName), index) == OMX_ErrorNone;
++index) {
list.push_back(::android::IOMX::ComponentInfo());
@@ -63,7 +63,7 @@
info.mName = componentName;
::android::Vector<::android::String8> roles;
OMX_ERRORTYPE err =
- mMaster->getRolesOfComponent(componentName, &roles);
+ mStore->getRolesOfComponent(componentName, &roles);
if (err == OMX_ErrorNone) {
for (OMX_U32 i = 0; i < roles.size(); ++i) {
info.mRoles.push_back(roles[i]);
@@ -101,7 +101,7 @@
this, new LWOmxObserver(observer), name.c_str());
OMX_COMPONENTTYPE *handle;
- OMX_ERRORTYPE err = mMaster->makeComponentInstance(
+ OMX_ERRORTYPE err = mStore->makeComponentInstance(
name.c_str(), &OMXNodeInstance::kCallbacks,
instance.get(), &handle);
@@ -208,7 +208,7 @@
OMX_ERRORTYPE err = OMX_ErrorNone;
if (instance->handle() != NULL) {
- err = mMaster->destroyComponentInstance(
+ err = mStore->destroyComponentInstance(
static_cast<OMX_COMPONENTTYPE*>(instance->handle()));
}
return StatusFromOMXError(err);
diff --git a/media/libstagefright/omx/1.0/OmxStore.cpp b/media/libstagefright/omx/1.0/OmxStore.cpp
index 67f478e..b5c1166 100644
--- a/media/libstagefright/omx/1.0/OmxStore.cpp
+++ b/media/libstagefright/omx/1.0/OmxStore.cpp
@@ -54,6 +54,24 @@
});
}
+ if (!nodes.empty()) {
+ auto anyNode = nodes.cbegin();
+ std::string::const_iterator first = anyNode->cbegin();
+ std::string::const_iterator last = anyNode->cend();
+ for (const std::string &name : nodes) {
+ std::string::const_iterator it1 = first;
+ for (std::string::const_iterator it2 = name.cbegin();
+ it1 != last && it2 != name.cend() && tolower(*it1) == tolower(*it2);
+ ++it1, ++it2) {
+ }
+ last = it1;
+ }
+ mPrefix = std::string(first, last);
+ LOG(INFO) << "omx common prefix: '" << mPrefix.c_str() << "'";
+ } else {
+ LOG(INFO) << "omx common prefix: no nodes";
+ }
+
MediaCodecsXmlParser parser;
parser.parseXmlFilesInSearchDirs(xmlNames, searchDirs);
if (profilingResultsXmlPath != nullptr) {
@@ -112,8 +130,6 @@
mRoleList[i] = std::move(role);
++i;
}
-
- mPrefix = parser.getCommonPrefix();
}
OmxStore::~OmxStore() {
diff --git a/media/libstagefright/omx/Android.bp b/media/libstagefright/omx/Android.bp
index 78b4f19..7c372cd 100644
--- a/media/libstagefright/omx/Android.bp
+++ b/media/libstagefright/omx/Android.bp
@@ -7,7 +7,7 @@
double_loadable: true,
srcs: [
- "OMXMaster.cpp",
+ "OMXStore.cpp",
"OMXNodeInstance.cpp",
"OMXUtils.cpp",
"OmxGraphicBufferSource.cpp",
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index ac42373..bebd516 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -22,7 +22,7 @@
#include <inttypes.h>
#include <media/stagefright/omx/OMXNodeInstance.h>
-#include <media/stagefright/omx/OMXMaster.h>
+#include <media/stagefright/omx/OMXStore.h>
#include <media/stagefright/omx/OMXUtils.h>
#include <android/IOMXBufferSource.h>
diff --git a/media/libstagefright/omx/OMXMaster.cpp b/media/libstagefright/omx/OMXStore.cpp
similarity index 91%
rename from media/libstagefright/omx/OMXMaster.cpp
rename to media/libstagefright/omx/OMXStore.cpp
index 094b1f5..e8fee42 100644
--- a/media/libstagefright/omx/OMXMaster.cpp
+++ b/media/libstagefright/omx/OMXStore.cpp
@@ -15,11 +15,11 @@
*/
//#define LOG_NDEBUG 0
-#define LOG_TAG "OMXMaster"
+#define LOG_TAG "OMXStore"
#include <android-base/properties.h>
#include <utils/Log.h>
-#include <media/stagefright/omx/OMXMaster.h>
+#include <media/stagefright/omx/OMXStore.h>
#include <media/stagefright/omx/SoftOMXPlugin.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -30,7 +30,7 @@
namespace android {
-OMXMaster::OMXMaster() {
+OMXStore::OMXStore() {
pid_t pid = getpid();
char filename[20];
@@ -55,19 +55,19 @@
addPlatformPlugin();
}
-OMXMaster::~OMXMaster() {
+OMXStore::~OMXStore() {
clearPlugins();
}
-void OMXMaster::addVendorPlugin() {
+void OMXStore::addVendorPlugin() {
addPlugin("libstagefrighthw.so");
}
-void OMXMaster::addPlatformPlugin() {
+void OMXStore::addPlatformPlugin() {
addPlugin("libstagefright_softomx_plugin.so");
}
-void OMXMaster::addPlugin(const char *libname) {
+void OMXStore::addPlugin(const char *libname) {
if (::android::base::GetIntProperty("vendor.media.omx", int64_t(1)) == 0) {
return;
}
@@ -99,7 +99,7 @@
}
}
-void OMXMaster::addPlugin(OMXPluginBase *plugin) {
+void OMXStore::addPlugin(OMXPluginBase *plugin) {
Mutex::Autolock autoLock(mLock);
OMX_U32 index = 0;
@@ -126,7 +126,7 @@
}
}
-void OMXMaster::clearPlugins() {
+void OMXStore::clearPlugins() {
Mutex::Autolock autoLock(mLock);
mPluginByComponentName.clear();
@@ -148,7 +148,7 @@
mPlugins.clear();
}
-OMX_ERRORTYPE OMXMaster::makeComponentInstance(
+OMX_ERRORTYPE OMXStore::makeComponentInstance(
const char *name,
const OMX_CALLBACKTYPE *callbacks,
OMX_PTR appData,
@@ -177,7 +177,7 @@
return err;
}
-OMX_ERRORTYPE OMXMaster::destroyComponentInstance(
+OMX_ERRORTYPE OMXStore::destroyComponentInstance(
OMX_COMPONENTTYPE *component) {
Mutex::Autolock autoLock(mLock);
@@ -193,7 +193,7 @@
return plugin->destroyComponentInstance(component);
}
-OMX_ERRORTYPE OMXMaster::enumerateComponents(
+OMX_ERRORTYPE OMXStore::enumerateComponents(
OMX_STRING name,
size_t size,
OMX_U32 index) {
@@ -213,7 +213,7 @@
return OMX_ErrorNone;
}
-OMX_ERRORTYPE OMXMaster::getRolesOfComponent(
+OMX_ERRORTYPE OMXStore::getRolesOfComponent(
const char *name,
Vector<String8> *roles) {
Mutex::Autolock autoLock(mLock);
diff --git a/media/libstagefright/omx/OMXUtils.cpp b/media/libstagefright/omx/OMXUtils.cpp
index 1b8493a..d6d280f 100644
--- a/media/libstagefright/omx/OMXUtils.cpp
+++ b/media/libstagefright/omx/OMXUtils.cpp
@@ -354,7 +354,7 @@
DescribeColorFormat2Params describeParams;
InitOMXParams(&describeParams);
describeParams.eColorFormat = (OMX_COLOR_FORMATTYPE)colorFormat;
- // reasonable dummy values
+ // reasonable initial values (that will be overwritten)
describeParams.nFrameWidth = 128;
describeParams.nFrameHeight = 128;
describeParams.nStride = 128;
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/1.0/Omx.h b/media/libstagefright/omx/include/media/stagefright/omx/1.0/Omx.h
index 5a46b26..84ae511 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/1.0/Omx.h
+++ b/media/libstagefright/omx/include/media/stagefright/omx/1.0/Omx.h
@@ -27,7 +27,7 @@
namespace android {
-struct OMXMaster;
+struct OMXStore;
struct OMXNodeInstance;
namespace hardware {
@@ -51,7 +51,7 @@
using ::android::sp;
using ::android::wp;
-using ::android::OMXMaster;
+using ::android::OMXStore;
using ::android::OMXNodeInstance;
struct Omx : public IOmx, public hidl_death_recipient {
@@ -73,7 +73,7 @@
status_t freeNode(sp<OMXNodeInstance> const& instance);
protected:
- OMXMaster* mMaster;
+ OMXStore* mStore;
Mutex mLock;
KeyedVector<wp<IBase>, sp<OMXNodeInstance> > mLiveNodes;
KeyedVector<OMXNodeInstance*, wp<IBase> > mNode2Observer;
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/OMXNodeInstance.h b/media/libstagefright/omx/include/media/stagefright/omx/OMXNodeInstance.h
index a761ef6..5f32c9e 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/OMXNodeInstance.h
+++ b/media/libstagefright/omx/include/media/stagefright/omx/OMXNodeInstance.h
@@ -33,7 +33,7 @@
class GraphicBuffer;
class IOMXBufferSource;
class IOMXObserver;
-struct OMXMaster;
+struct OMXStore;
class OMXBuffer;
using IHidlMemory = hidl::memory::V1_0::IMemory;
using hardware::media::omx::V1_0::implementation::Omx;
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/OMXMaster.h b/media/libstagefright/omx/include/media/stagefright/omx/OMXStore.h
similarity index 88%
rename from media/libstagefright/omx/include/media/stagefright/omx/OMXMaster.h
rename to media/libstagefright/omx/include/media/stagefright/omx/OMXStore.h
index 93eaef1..5d6c3ed 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/OMXMaster.h
+++ b/media/libstagefright/omx/include/media/stagefright/omx/OMXStore.h
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-#ifndef OMX_MASTER_H_
+#ifndef OMX_STORE_H_
-#define OMX_MASTER_H_
+#define OMX_STORE_H_
#include <media/hardware/OMXPluginBase.h>
@@ -27,9 +27,9 @@
namespace android {
-struct OMXMaster : public OMXPluginBase {
- OMXMaster();
- virtual ~OMXMaster();
+struct OMXStore : public OMXPluginBase {
+ OMXStore();
+ virtual ~OMXStore();
virtual OMX_ERRORTYPE makeComponentInstance(
const char *name,
@@ -66,10 +66,10 @@
void addPlugin(OMXPluginBase *plugin);
void clearPlugins();
- OMXMaster(const OMXMaster &);
- OMXMaster &operator=(const OMXMaster &);
+ OMXStore(const OMXStore &);
+ OMXStore &operator=(const OMXStore &);
};
} // namespace android
-#endif // OMX_MASTER_H_
+#endif // OMX_STORE_H_
diff --git a/media/libstagefright/tests/metadatautils/Android.bp b/media/libstagefright/tests/metadatautils/Android.bp
new file mode 100644
index 0000000..69830fc
--- /dev/null
+++ b/media/libstagefright/tests/metadatautils/Android.bp
@@ -0,0 +1,50 @@
+/*
+ * 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: "MetaDataUtilsTest",
+ gtest: true,
+
+ srcs: [
+ "MetaDataUtilsTest.cpp",
+ ],
+
+ static_libs: [
+ "libstagefright_metadatautils",
+ "libstagefright_esds",
+ ],
+
+ shared_libs: [
+ "liblog",
+ "libutils",
+ "libmediandk",
+ "libstagefright",
+ "libstagefright_foundation",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+ sanitize: {
+ cfi: true,
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ },
+}
diff --git a/media/libstagefright/tests/metadatautils/AndroidTest.xml b/media/libstagefright/tests/metadatautils/AndroidTest.xml
new file mode 100644
index 0000000..d6497f3
--- /dev/null
+++ b/media/libstagefright/tests/metadatautils/AndroidTest.xml
@@ -0,0 +1,30 @@
+<?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 MetaDataUtils unit test">
+ <option name="test-suite-tag" value="MetaDataUtilsTest" />
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="false" />
+ <option name="push" value="MetaDataUtilsTest->/data/local/tmp/MetaDataUtilsTest" />
+ <option name="push-file"
+ key="https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/tests/metadatautils/MetaDataUtilsTestRes-1.0.zip?unzip=true"
+ value="/data/local/tmp/MetaDataUtilsTestRes/" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="MetaDataUtilsTest" />
+ <option name="native-test-flag" value="-P /data/local/tmp/MetaDataUtilsTestRes/" />
+ </test>
+</configuration>
diff --git a/media/libstagefright/tests/metadatautils/MetaDataUtilsTest.cpp b/media/libstagefright/tests/metadatautils/MetaDataUtilsTest.cpp
new file mode 100644
index 0000000..9fd5fdb
--- /dev/null
+++ b/media/libstagefright/tests/metadatautils/MetaDataUtilsTest.cpp
@@ -0,0 +1,490 @@
+/*
+ * 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 "MetaDataUtilsTest"
+#include <utils/Log.h>
+
+#include <fstream>
+#include <string>
+
+#include <ESDS.h>
+#include <media/NdkMediaFormat.h>
+#include <media/stagefright/MediaCodecConstants.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaDataBase.h>
+#include <media/stagefright/MetaDataUtils.h>
+#include <media/stagefright/foundation/ABitReader.h>
+
+#include "MetaDataUtilsTestEnvironment.h"
+
+constexpr uint8_t kAdtsCsdSize = 7;
+// from AAC specs: https://www.iso.org/standard/43345.html
+constexpr int32_t kSamplingFreq[] = {96000, 88200, 64000, 48000, 44100, 32000,
+ 24000, 22050, 16000, 12000, 11025, 8000};
+constexpr uint8_t kMaxSamplingFreqIndex = sizeof(kSamplingFreq) / sizeof(kSamplingFreq[0]);
+
+static MetaDataUtilsTestEnvironment *gEnv = nullptr;
+
+using namespace android;
+
+class MetaDataValidate {
+ public:
+ MetaDataValidate() : mInputBuffer(nullptr) {}
+
+ ~MetaDataValidate() {
+ if (mInputBuffer) {
+ delete[] mInputBuffer;
+ mInputBuffer = nullptr;
+ }
+ }
+
+ void SetUpMetaDataValidate(string fileName) {
+ struct stat buf;
+ int8_t err = stat(fileName.c_str(), &buf);
+ ASSERT_EQ(err, 0) << "Failed to get file information for file: " << fileName;
+
+ mInputBufferSize = buf.st_size;
+ FILE *inputFilePtr = fopen(fileName.c_str(), "rb+");
+ ASSERT_NE(inputFilePtr, nullptr) << "Failed to open file: " << fileName;
+
+ mInputBuffer = new uint8_t[mInputBufferSize];
+ ASSERT_NE(mInputBuffer, nullptr)
+ << "Failed to allocate memory of size: " << mInputBufferSize;
+
+ int32_t numBytes =
+ fread((char *)mInputBuffer, sizeof(uint8_t), mInputBufferSize, inputFilePtr);
+ ASSERT_EQ(numBytes, mInputBufferSize) << numBytes << " of " << mInputBufferSize << " read";
+
+ fclose(inputFilePtr);
+ }
+
+ size_t mInputBufferSize;
+ const uint8_t *mInputBuffer;
+};
+
+class AvcCSDTest : public ::testing::TestWithParam<
+ tuple<string /*inputFile*/, size_t /*avcWidth*/, size_t /*avcHeight*/>> {
+ public:
+ AvcCSDTest() : mInputBuffer(nullptr) {}
+
+ ~AvcCSDTest() {
+ if (mInputBuffer) {
+ delete[] mInputBuffer;
+ mInputBuffer = nullptr;
+ }
+ }
+ virtual void SetUp() override {
+ tuple<string, size_t, size_t> params = GetParam();
+ string inputFile = gEnv->getRes() + get<0>(params);
+ mFrameWidth = get<1>(params);
+ mFrameHeight = get<2>(params);
+
+ struct stat buf;
+ int8_t err = stat(inputFile.c_str(), &buf);
+ ASSERT_EQ(err, 0) << "Failed to get information for file: " << inputFile;
+
+ mInputBufferSize = buf.st_size;
+ FILE *inputFilePtr = fopen(inputFile.c_str(), "rb+");
+ ASSERT_NE(inputFilePtr, nullptr) << "Failed to open file: " << inputFile;
+
+ mInputBuffer = new uint8_t[mInputBufferSize];
+ ASSERT_NE(mInputBuffer, nullptr)
+ << "Failed to create a buffer of size: " << mInputBufferSize;
+
+ int32_t numBytes =
+ fread((char *)mInputBuffer, sizeof(uint8_t), mInputBufferSize, inputFilePtr);
+ ASSERT_EQ(numBytes, mInputBufferSize) << numBytes << " of " << mInputBufferSize << " read";
+
+ fclose(inputFilePtr);
+ }
+
+ size_t mFrameWidth;
+ size_t mFrameHeight;
+ size_t mInputBufferSize;
+ const uint8_t *mInputBuffer;
+};
+
+class AvcCSDValidateTest : public MetaDataValidate,
+ public ::testing::TestWithParam<string /*inputFile*/> {
+ public:
+ virtual void SetUp() override {
+ string inputFile = gEnv->getRes() + GetParam();
+
+ ASSERT_NO_FATAL_FAILURE(SetUpMetaDataValidate(inputFile));
+ }
+};
+
+class AacCSDTest
+ : public ::testing::TestWithParam<tuple<uint32_t /*profile*/, uint32_t /*samplingFreqIndex*/,
+ uint32_t /*channelConfig*/>> {
+ public:
+ virtual void SetUp() override {
+ tuple<uint32_t, uint32_t, uint32_t> params = GetParam();
+ mAacProfile = get<0>(params);
+ mAacSamplingFreqIndex = get<1>(params);
+ mAacChannelConfig = get<2>(params);
+ }
+
+ uint32_t mAacProfile;
+ uint32_t mAacSamplingFreqIndex;
+ uint32_t mAacChannelConfig;
+};
+
+class AacADTSTest
+ : public ::testing::TestWithParam<
+ tuple<string /*adtsFile*/, uint32_t /*channelCount*/, uint32_t /*sampleRate*/>> {
+ public:
+ AacADTSTest() : mInputBuffer(nullptr) {}
+
+ virtual void SetUp() override {
+ tuple<string, uint32_t, uint32_t> params = GetParam();
+ string fileName = gEnv->getRes() + get<0>(params);
+ mAacChannelCount = get<1>(params);
+ mAacSampleRate = get<2>(params);
+
+ FILE *filePtr = fopen(fileName.c_str(), "r");
+ ASSERT_NE(filePtr, nullptr) << "Failed to open file: " << fileName;
+
+ mInputBuffer = new uint8_t[kAdtsCsdSize];
+ ASSERT_NE(mInputBuffer, nullptr) << "Failed to allocate a memory of size: " << kAdtsCsdSize;
+
+ int32_t numBytes = fread((void *)mInputBuffer, sizeof(uint8_t), kAdtsCsdSize, filePtr);
+ ASSERT_EQ(numBytes, kAdtsCsdSize)
+ << "Failed to read complete file, bytes read: " << numBytes;
+
+ fclose(filePtr);
+ }
+ int32_t mAacChannelCount;
+ int32_t mAacSampleRate;
+ const uint8_t *mInputBuffer;
+};
+
+class AacCSDValidateTest : public MetaDataValidate,
+ public ::testing::TestWithParam<string /*inputFile*/> {
+ public:
+ virtual void SetUp() override {
+ string inputFile = gEnv->getRes() + GetParam();
+
+ ASSERT_NO_FATAL_FAILURE(SetUpMetaDataValidate(inputFile));
+ }
+};
+
+class VorbisTest : public ::testing::TestWithParam<pair<string /*fileName*/, string /*infoFile*/>> {
+ public:
+ virtual void SetUp() override {
+ pair<string, string> params = GetParam();
+ string inputMediaFile = gEnv->getRes() + params.first;
+ mInputFileStream.open(inputMediaFile, ifstream::in);
+ ASSERT_TRUE(mInputFileStream.is_open()) << "Failed to open data file: " << inputMediaFile;
+
+ string inputInfoFile = gEnv->getRes() + params.second;
+ mInfoFileStream.open(inputInfoFile, ifstream::in);
+ ASSERT_TRUE(mInputFileStream.is_open()) << "Failed to open data file: " << inputInfoFile;
+ ASSERT_FALSE(inputInfoFile.empty()) << "Empty info file: " << inputInfoFile;
+ }
+
+ ~VorbisTest() {
+ if (mInputFileStream.is_open()) mInputFileStream.close();
+ if (mInfoFileStream.is_open()) mInfoFileStream.close();
+ }
+
+ ifstream mInputFileStream;
+ ifstream mInfoFileStream;
+};
+
+TEST_P(AvcCSDTest, AvcCSDValidationTest) {
+ AMediaFormat *csdData = AMediaFormat_new();
+ ASSERT_NE(csdData, nullptr) << "Failed to create AMedia format";
+
+ bool status = MakeAVCCodecSpecificData(csdData, mInputBuffer, mInputBufferSize);
+ ASSERT_TRUE(status) << "Failed to make AVC CSD from AMediaFormat";
+
+ int32_t avcWidth = -1;
+ status = AMediaFormat_getInt32(csdData, AMEDIAFORMAT_KEY_WIDTH, &avcWidth);
+ ASSERT_TRUE(status) << "Failed to get avc width";
+ ASSERT_EQ(avcWidth, mFrameWidth);
+
+ int32_t avcHeight = -1;
+ status = AMediaFormat_getInt32(csdData, AMEDIAFORMAT_KEY_HEIGHT, &avcHeight);
+ ASSERT_TRUE(status) << "Failed to get avc height";
+ ASSERT_EQ(avcHeight, mFrameHeight);
+
+ const char *mimeType = "";
+ status = AMediaFormat_getString(csdData, AMEDIAFORMAT_KEY_MIME, &mimeType);
+ ASSERT_TRUE(status) << "Failed to get the mime type";
+ ASSERT_STREQ(mimeType, MEDIA_MIMETYPE_VIDEO_AVC);
+
+ MetaDataBase *metaData = new MetaDataBase();
+ ASSERT_NE(metaData, nullptr) << "Failed to create MetaData Base";
+
+ status = MakeAVCCodecSpecificData(*metaData, mInputBuffer, mInputBufferSize);
+ ASSERT_TRUE(status) << "Failed to make AVC CSD from MetaDataBase";
+
+ avcWidth = -1;
+ status = metaData->findInt32(kKeyWidth, &avcWidth);
+ ASSERT_TRUE(status) << "Failed to find the width";
+ ASSERT_EQ(avcWidth, mFrameWidth);
+
+ avcHeight = -1;
+ status = metaData->findInt32(kKeyHeight, &avcHeight);
+ ASSERT_TRUE(status) << "Failed to find the height";
+ ASSERT_EQ(avcHeight, mFrameHeight);
+
+ void *csdAMediaFormatBuffer = nullptr;
+ size_t csdAMediaFormatSize;
+ status = AMediaFormat_getBuffer(csdData, AMEDIAFORMAT_KEY_CSD_AVC, &csdAMediaFormatBuffer,
+ &csdAMediaFormatSize);
+ ASSERT_TRUE(status) << "Failed to get the CSD from AMediaFormat";
+ ASSERT_NE(csdAMediaFormatBuffer, nullptr) << "Invalid CSD from AMediaFormat";
+
+ const void *csdMetaDataBaseBuffer = nullptr;
+ size_t csdMetaDataBaseSize = 0;
+ uint32_t mediaType;
+ status = metaData->findData(kKeyAVCC, &mediaType, &csdMetaDataBaseBuffer, &csdMetaDataBaseSize);
+ ASSERT_TRUE(status) << "Failed to get the CSD from MetaDataBase";
+ ASSERT_NE(csdMetaDataBaseBuffer, nullptr) << "Invalid CSD from MetaDataBase";
+ ASSERT_GT(csdMetaDataBaseSize, 0) << "CSD size must be greater than 0";
+ ASSERT_EQ(csdMetaDataBaseSize, csdAMediaFormatSize)
+ << "CSD size of MetaData type and AMediaFormat type must be same";
+
+ int32_t result = memcmp(csdAMediaFormatBuffer, csdMetaDataBaseBuffer, csdAMediaFormatSize);
+ ASSERT_EQ(result, 0) << "CSD from AMediaFormat and MetaDataBase do not match";
+
+ delete metaData;
+ AMediaFormat_delete(csdData);
+}
+
+TEST_P(AvcCSDValidateTest, AvcValidateTest) {
+ AMediaFormat *csdData = AMediaFormat_new();
+ ASSERT_NE(csdData, nullptr) << "Failed to create AMedia format";
+
+ bool status = MakeAVCCodecSpecificData(csdData, mInputBuffer, mInputBufferSize);
+ ASSERT_FALSE(status) << "MakeAVCCodecSpecificData with AMediaFormat succeeds with invalid data";
+
+ MetaDataBase *metaData = new MetaDataBase();
+ ASSERT_NE(metaData, nullptr) << "Failed to create MetaData Base";
+
+ status = MakeAVCCodecSpecificData(*metaData, mInputBuffer, mInputBufferSize);
+ ASSERT_FALSE(status) << "MakeAVCCodecSpecificData with MetaDataBase succeeds with invalid data";
+}
+
+TEST_P(AacCSDTest, AacCSDValidationTest) {
+ AMediaFormat *csdData = AMediaFormat_new();
+ ASSERT_NE(csdData, nullptr) << "Failed to create AMedia format";
+
+ ASSERT_GE(mAacSamplingFreqIndex, 0);
+ ASSERT_LT(mAacSamplingFreqIndex, kMaxSamplingFreqIndex);
+ bool status = MakeAACCodecSpecificData(csdData, mAacProfile, mAacSamplingFreqIndex,
+ mAacChannelConfig);
+ ASSERT_TRUE(status) << "Failed to make AAC CSD from AMediaFormat";
+
+ int32_t sampleRate = -1;
+ status = AMediaFormat_getInt32(csdData, AMEDIAFORMAT_KEY_SAMPLE_RATE, &sampleRate);
+ ASSERT_TRUE(status) << "Failed to get sample rate";
+ ASSERT_EQ(kSamplingFreq[mAacSamplingFreqIndex], sampleRate);
+
+ int32_t channelCount = -1;
+ status = AMediaFormat_getInt32(csdData, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &channelCount);
+ ASSERT_TRUE(status) << "Failed to get channel count";
+ ASSERT_EQ(channelCount, mAacChannelConfig);
+
+ const char *mimeType = "";
+ status = AMediaFormat_getString(csdData, AMEDIAFORMAT_KEY_MIME, &mimeType);
+ ASSERT_TRUE(status) << "Failed to get the mime type";
+ ASSERT_STREQ(mimeType, MEDIA_MIMETYPE_AUDIO_AAC);
+
+ MetaDataBase *metaData = new MetaDataBase();
+ ASSERT_NE(metaData, nullptr) << "Failed to create MetaData Base";
+
+ status = MakeAACCodecSpecificData(*metaData, mAacProfile, mAacSamplingFreqIndex,
+ mAacChannelConfig);
+ ASSERT_TRUE(status) << "Failed to make AAC CSD from MetaDataBase";
+
+ sampleRate = -1;
+ status = metaData->findInt32(kKeySampleRate, &sampleRate);
+ ASSERT_TRUE(status) << "Failed to get sampling rate";
+ ASSERT_EQ(kSamplingFreq[mAacSamplingFreqIndex], sampleRate);
+
+ channelCount = -1;
+ status = metaData->findInt32(kKeyChannelCount, &channelCount);
+ ASSERT_TRUE(status) << "Failed to get channel count";
+ ASSERT_EQ(channelCount, mAacChannelConfig);
+
+ mimeType = "";
+ status = metaData->findCString(kKeyMIMEType, &mimeType);
+ ASSERT_TRUE(status) << "Failed to get mime type";
+ ASSERT_STREQ(mimeType, MEDIA_MIMETYPE_AUDIO_AAC);
+
+ void *csdAMediaFormatBuffer = nullptr;
+ size_t csdAMediaFormatSize = 0;
+ status = AMediaFormat_getBuffer(csdData, AMEDIAFORMAT_KEY_CSD_0, &csdAMediaFormatBuffer,
+ &csdAMediaFormatSize);
+ ASSERT_TRUE(status) << "Failed to get the AMediaFormat CSD";
+ ASSERT_GT(csdAMediaFormatSize, 0) << "CSD size must be greater than 0";
+ ASSERT_NE(csdAMediaFormatBuffer, nullptr) << "Invalid CSD found";
+
+ const void *csdMetaDataBaseBuffer;
+ size_t csdMetaDataBaseSize = 0;
+ uint32_t mediaType;
+ status = metaData->findData(kKeyESDS, &mediaType, &csdMetaDataBaseBuffer, &csdMetaDataBaseSize);
+ ASSERT_TRUE(status) << "Failed to get the ESDS data from MetaDataBase";
+ ASSERT_GT(csdMetaDataBaseSize, 0) << "CSD size must be greater than 0";
+
+ ESDS esds(csdMetaDataBaseBuffer, csdMetaDataBaseSize);
+ status_t result = esds.getCodecSpecificInfo(&csdMetaDataBaseBuffer, &csdMetaDataBaseSize);
+ ASSERT_EQ(result, (status_t)OK) << "Failed to get CSD from ESDS data";
+ ASSERT_NE(csdMetaDataBaseBuffer, nullptr) << "Invalid CSD found";
+ ASSERT_EQ(csdAMediaFormatSize, csdMetaDataBaseSize)
+ << "CSD size do not match between AMediaFormat type and MetaDataBase type";
+
+ int32_t memcmpResult =
+ memcmp(csdAMediaFormatBuffer, csdMetaDataBaseBuffer, csdAMediaFormatSize);
+ ASSERT_EQ(memcmpResult, 0) << "AMediaFormat and MetaDataBase CSDs do not match";
+
+ AMediaFormat_delete(csdData);
+ delete metaData;
+}
+
+TEST_P(AacADTSTest, AacADTSValidationTest) {
+ MetaDataBase *metaData = new MetaDataBase();
+ ASSERT_NE(metaData, nullptr) << "Failed to create meta data";
+
+ bool status = MakeAACCodecSpecificData(*metaData, mInputBuffer, kAdtsCsdSize);
+ ASSERT_TRUE(status) << "Failed to make AAC CSD from MetaDataBase";
+
+ int32_t sampleRate = -1;
+ status = metaData->findInt32(kKeySampleRate, &sampleRate);
+ ASSERT_TRUE(status) << "Failed to get sampling rate";
+ ASSERT_EQ(sampleRate, mAacSampleRate);
+
+ int32_t channelCount = -1;
+ status = metaData->findInt32(kKeyChannelCount, &channelCount);
+ ASSERT_TRUE(status) << "Failed to get channel count";
+ ASSERT_EQ(channelCount, mAacChannelCount);
+
+ const char *mimeType = "";
+ status = metaData->findCString(kKeyMIMEType, &mimeType);
+ ASSERT_TRUE(status) << "Failed to get mime type";
+ ASSERT_STREQ(mimeType, MEDIA_MIMETYPE_AUDIO_AAC);
+
+ delete metaData;
+}
+
+TEST_P(AacCSDValidateTest, AacInvalidInputTest) {
+ MetaDataBase *metaData = new MetaDataBase();
+ ASSERT_NE(metaData, nullptr) << "Failed to create meta data";
+
+ bool status = MakeAACCodecSpecificData(*metaData, mInputBuffer, kAdtsCsdSize);
+ ASSERT_FALSE(status) << "MakeAACCodecSpecificData succeeds with invalid data";
+}
+
+TEST_P(VorbisTest, VorbisCommentTest) {
+ string line;
+ string tag;
+ string key;
+ string value;
+ size_t commentLength;
+ bool status;
+
+ while (getline(mInfoFileStream, line)) {
+ istringstream stringLine(line);
+ stringLine >> tag >> key >> value >> commentLength;
+ ASSERT_GT(commentLength, 0) << "Vorbis comment size must be greater than 0";
+
+ string comment;
+ string dataLine;
+
+ getline(mInputFileStream, dataLine);
+ istringstream dataStringLine(dataLine);
+ dataStringLine >> comment;
+
+ char *buffer = strndup(comment.c_str(), commentLength);
+ ASSERT_NE(buffer, nullptr) << "Failed to allocate buffer of size: " << commentLength;
+
+ AMediaFormat *fileMeta = AMediaFormat_new();
+ ASSERT_NE(fileMeta, nullptr) << "Failed to create AMedia format";
+
+ parseVorbisComment(fileMeta, buffer, commentLength);
+ free(buffer);
+
+ if (!strncasecmp(tag.c_str(), "ANDROID_HAPTIC", sizeof(tag))) {
+ int32_t numChannelExpected = stoi(value);
+ int32_t numChannelFound = -1;
+ status = AMediaFormat_getInt32(fileMeta, key.c_str(), &numChannelFound);
+ ASSERT_TRUE(status) << "Failed to get the channel count";
+ ASSERT_EQ(numChannelExpected, numChannelFound);
+ } else if (!strncasecmp(tag.c_str(), "ANDROID_LOOP", sizeof(tag))) {
+ int32_t loopExpected = !value.compare("true");
+ int32_t loopFound = -1;
+
+ status = AMediaFormat_getInt32(fileMeta, "loop", &loopFound);
+ ASSERT_TRUE(status) << "Failed to get the loop count";
+ ASSERT_EQ(loopExpected, loopFound);
+ } else {
+ const char *tagValue = "";
+ status = AMediaFormat_getString(fileMeta, key.c_str(), &tagValue);
+ ASSERT_TRUE(status) << "Failed to get the tag value";
+ ASSERT_STREQ(value.c_str(), tagValue);
+ }
+ AMediaFormat_delete(fileMeta);
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(MetaDataUtilsTestAll, AvcCSDTest,
+ ::testing::Values(make_tuple("sps_pps_userdata.h264", 8, 8),
+ make_tuple("sps_userdata_pps.h264", 8, 8),
+ make_tuple("sps_pps_sps_pps.h264", 8, 8)));
+
+// TODO(b/158067691): Add invalid test vectors with incomplete PPS or no PPS
+INSTANTIATE_TEST_SUITE_P(MetaDataUtilsTestAll, AvcCSDValidateTest,
+ ::testing::Values("sps_pps_only_startcode.h264",
+ "sps_incomplete_pps.h264",
+ // TODO(b/158067691) "sps_pps_incomplete.h264",
+ "randomdata.h264",
+ // TODO(b/158067691) "sps.h264",
+ "pps.h264"));
+
+INSTANTIATE_TEST_SUITE_P(MetaDataUtilsTestAll, AacCSDTest,
+ ::testing::Values(make_tuple(AACObjectMain, 1, 1)));
+
+INSTANTIATE_TEST_SUITE_P(MetaDataUtilsTestAll, AacADTSTest,
+ ::testing::Values(make_tuple("loudsoftaacadts", 1, 44100)));
+
+INSTANTIATE_TEST_SUITE_P(MetaDataUtilsTestAll, AacCSDValidateTest,
+ ::testing::Values("loudsoftaacadts_invalidheader",
+ "loudsoftaacadts_invalidprofile",
+ "loudsoftaacadts_invalidchannelconfig"));
+
+// TODO(b/157974508) Add test vector for vorbis thumbnail tag
+// Info file contains TAG, Key, Value and size of the vorbis comment
+INSTANTIATE_TEST_SUITE_P(
+ MetaDataUtilsTestAll, VorbisTest,
+ ::testing::Values(make_pair("vorbiscomment_sintel.dat", "vorbiscomment_sintel.info"),
+ make_pair("vorbiscomment_album.dat", "vorbiscomment_album.info"),
+ make_pair("vorbiscomment_loop.dat", "vorbiscomment_loop.info")));
+
+int main(int argc, char **argv) {
+ gEnv = new MetaDataUtilsTestEnvironment();
+ ::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/metadatautils/MetaDataUtilsTestEnvironment.h b/media/libstagefright/tests/metadatautils/MetaDataUtilsTestEnvironment.h
new file mode 100644
index 0000000..4d642bc
--- /dev/null
+++ b/media/libstagefright/tests/metadatautils/MetaDataUtilsTestEnvironment.h
@@ -0,0 +1,73 @@
+/*
+ * 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 __METADATA_UTILS_TEST_ENVIRONMENT_H__
+#define __METADATA_UTILS_TEST_ENVIRONMENT_H__
+
+#include <gtest/gtest.h>
+
+#include <getopt.h>
+
+using namespace std;
+
+class MetaDataUtilsTestEnvironment : public::testing::Environment {
+ public:
+ MetaDataUtilsTestEnvironment() : 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 MetaDataUtilsTestEnvironment::initFromOptions(int argc, char **argv) {
+ static struct option options[] = {{"path", required_argument, 0, 'P'}, {0, 0, 0, 0}};
+
+ while (true) {
+ int index = 0;
+ int c = getopt_long(argc, argv, "P:", options, &index);
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 'P': {
+ setRes(optarg);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ if (optind < argc) {
+ fprintf(stderr,
+ "unrecognized option: %s\n\n"
+ "usage: %s <gtest options> <test options>\n\n"
+ "test options are:\n\n"
+ "-P, --path: Resource files directory location\n",
+ argv[optind ?: 1], argv[0]);
+ return 2;
+ }
+ return 0;
+}
+
+#endif // __METADATA_UTILS_TEST_ENVIRONMENT_H__
diff --git a/media/libstagefright/tests/metadatautils/README.md b/media/libstagefright/tests/metadatautils/README.md
new file mode 100644
index 0000000..0862a07
--- /dev/null
+++ b/media/libstagefright/tests/metadatautils/README.md
@@ -0,0 +1,39 @@
+## Media Testing ##
+---
+#### MetaDataUtils Test
+The MetaDataUtils Unit Test Suite validates the libstagefright_metadatautils library available in libstagefright.
+
+Run the following steps to build the test suite:
+```
+m MetaDataUtilsTest
+```
+
+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/MetaDataUtilsTest/MetaDataUtilsTest /data/local/tmp/
+```
+
+To test 32-bit binary push binaries from nativetest.
+```
+adb push ${OUT}/data/nativetest/MetaDataUtilsTest/MetaDataUtilsTest /data/local/tmp/
+```
+
+The resource file for the tests is taken from [here](https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/tests/metadatautils/MetaDataUtilsTestRes-1.0.zip). Download, unzip and push these files into device for testing.
+
+```
+adb push MetaDataUtilsTestRes-1.0 /data/local/tmp/
+```
+
+usage: MetaDataUtilsTest -P \<path_to_folder\>
+```
+adb shell /data/local/tmp/MetaDataUtilsTest -P /data/local/tmp/MetaDataUtilsTestRes-1.0/
+```
+Alternatively, the test can also be run using atest command.
+
+```
+atest MetaDataUtilsTest -- --enable-module-dynamic-download=true
+```
diff --git a/media/libstagefright/tests/writer/Android.bp b/media/libstagefright/tests/writer/Android.bp
index d058ed3..b5d453e 100644
--- a/media/libstagefright/tests/writer/Android.bp
+++ b/media/libstagefright/tests/writer/Android.bp
@@ -29,13 +29,14 @@
"liblog",
"libutils",
"libmedia",
+ "libmediandk",
+ "libstagefright",
],
static_libs: [
"libstagefright_webm",
- "libdatasource",
- "libstagefright",
"libstagefright_foundation",
+ "libdatasource",
"libstagefright_esds",
"libogg",
],
diff --git a/media/libstagefright/tests/writer/AndroidTest.xml b/media/libstagefright/tests/writer/AndroidTest.xml
index a21be8a..cc890fe 100644
--- a/media/libstagefright/tests/writer/AndroidTest.xml
+++ b/media/libstagefright/tests/writer/AndroidTest.xml
@@ -19,7 +19,7 @@
<option name="cleanup" value="true" />
<option name="push" value="writerTest->/data/local/tmp/writerTest" />
<option name="push-file"
- key="https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/tests/writer/WriterTestRes.zip?unzip=true"
+ key="https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/tests/writer/WriterTestRes-1.1.zip?unzip=true"
value="/data/local/tmp/WriterTestRes/" />
</target_preparer>
<test class="com.android.tradefed.testtype.GTest" >
diff --git a/media/libstagefright/tests/writer/README.md b/media/libstagefright/tests/writer/README.md
index e103613..0e54ca7 100644
--- a/media/libstagefright/tests/writer/README.md
+++ b/media/libstagefright/tests/writer/README.md
@@ -19,10 +19,10 @@
adb push ${OUT}/data/nativetest/writerTest/writerTest /data/local/tmp/
-The resource file for the tests is taken from [here](https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/tests/writer/WriterTestRes.zip).
+The resource file for the tests is taken from [here](https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/tests/writer/WriterTestRes-1.1.zip).
Download and extract the folder. Push all the files in this folder to /data/local/tmp/ on the device.
```
-adb push WriterTestRes /data/local/tmp/
+adb push WriterTestRes-1.1/. /data/local/tmp/WriterTestRes/
```
usage: writerTest -P \<path_to_res_folder\> -C <remove_output_file>
diff --git a/media/libstagefright/tests/writer/WriterTest.cpp b/media/libstagefright/tests/writer/WriterTest.cpp
index 4c0add4..d170e7c 100644
--- a/media/libstagefright/tests/writer/WriterTest.cpp
+++ b/media/libstagefright/tests/writer/WriterTest.cpp
@@ -18,9 +18,13 @@
#define LOG_TAG "WriterTest"
#include <utils/Log.h>
+#include <binder/ProcessState.h>
+
+#include <inttypes.h>
#include <fstream>
#include <iostream>
+#include <media/NdkMediaExtractor.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/Utils.h>
@@ -39,15 +43,13 @@
#define OUTPUT_FILE_NAME "/data/local/tmp/writer.out"
-static WriterTestEnvironment *gEnv = nullptr;
+// Stts values within 0.1ms(100us) difference are fudged to save too
+// many stts entries in MPEG4Writer.
+constexpr int32_t kMpeg4MuxToleranceTimeUs = 100;
+// Tolerance value for other writers
+constexpr int32_t kMuxToleranceTimeUs = 1;
-struct configFormat {
- char mime[128];
- int32_t width;
- int32_t height;
- int32_t sampleRate;
- int32_t channelCount;
-};
+static WriterTestEnvironment *gEnv = nullptr;
enum inputId {
// audio streams
@@ -82,8 +84,8 @@
int32_t secondParam;
bool isAudio;
} kInputData[] = {
- {AAC_1, MEDIA_MIMETYPE_AUDIO_AAC, "bbb_aac_stereo_128kbps_48000hz.aac",
- "bbb_aac_stereo_128kbps_48000hz.info", 48000, 2, true},
+ {AAC_1, MEDIA_MIMETYPE_AUDIO_AAC, "audio_aac_stereo_8kbps_11025hz.aac",
+ "audio_aac_stereo_8kbps_11025hz.info", 11025, 2, true},
{AAC_ADTS_1, MEDIA_MIMETYPE_AUDIO_AAC_ADTS, "Mps_2_c2_fr1_Sc1_Dc2_0x03_raw.adts",
"Mps_2_c2_fr1_Sc1_Dc2_0x03_raw.info", 48000, 2, true},
{AMR_NB_1, MEDIA_MIMETYPE_AUDIO_AMR_NB, "sine_amrnb_1ch_12kbps_8000hz.amrnb",
@@ -94,17 +96,17 @@
"bbb_flac_stereo_680kbps_48000hz.info", 48000, 2, true},
{OPUS_1, MEDIA_MIMETYPE_AUDIO_OPUS, "bbb_opus_stereo_128kbps_48000hz.opus",
"bbb_opus_stereo_128kbps_48000hz.info", 48000, 2, true},
- {VORBIS_1, MEDIA_MIMETYPE_AUDIO_VORBIS, "bbb_vorbis_stereo_128kbps_48000hz.vorbis",
- "bbb_vorbis_stereo_128kbps_48000hz.info", 48000, 2, true},
+ {VORBIS_1, MEDIA_MIMETYPE_AUDIO_VORBIS, "bbb_vorbis_1ch_64kbps_16kHz.vorbis",
+ "bbb_vorbis_1ch_64kbps_16kHz.info", 16000, 1, true},
{AV1_1, MEDIA_MIMETYPE_VIDEO_AV1, "bbb_av1_176_144.av1", "bbb_av1_176_144.info", 176, 144,
false},
- {AVC_1, MEDIA_MIMETYPE_VIDEO_AVC, "bbb_avc_176x144_300kbps_60fps.h264",
- "bbb_avc_176x144_300kbps_60fps.info", 176, 144, false},
+ {AVC_1, MEDIA_MIMETYPE_VIDEO_AVC, "bbb_avc_352x288_768kbps_30fps.avc",
+ "bbb_avc_352x288_768kbps_30fps.info", 352, 288, false},
{H263_1, MEDIA_MIMETYPE_VIDEO_H263, "bbb_h263_352x288_300kbps_12fps.h263",
"bbb_h263_352x288_300kbps_12fps.info", 352, 288, false},
- {HEVC_1, MEDIA_MIMETYPE_VIDEO_HEVC, "bbb_hevc_176x144_176kbps_60fps.hevc",
- "bbb_hevc_176x144_176kbps_60fps.info", 176, 144, false},
+ {HEVC_1, MEDIA_MIMETYPE_VIDEO_HEVC, "bbb_hevc_340x280_768kbps_30fps.hevc",
+ "bbb_hevc_340x280_768kbps_30fps.info", 340, 280, false},
{MPEG4_1, MEDIA_MIMETYPE_VIDEO_MPEG4, "bbb_mpeg4_352x288_512kbps_30fps.m4v",
"bbb_mpeg4_352x288_512kbps_30fps.info", 352, 288, false},
{VP8_1, MEDIA_MIMETYPE_VIDEO_VP8, "bbb_vp8_176x144_240kbps_60fps.vp8",
@@ -164,6 +166,14 @@
int32_t addWriterSource(bool isAudio, configFormat params, int32_t idx = 0);
+ void setupExtractor(AMediaExtractor *extractor, string inputFileName, int32_t &trackCount);
+
+ void extract(AMediaExtractor *extractor, configFormat ¶ms, vector<BufferInfo> &bufferInfo,
+ uint8_t *buffer, size_t bufSize, size_t *bytesExtracted, int32_t idx);
+
+ void compareParams(configFormat srcParam, configFormat dstParam, vector<BufferInfo> dstBufInfo,
+ int32_t index);
+
enum standardWriters {
OGG,
AAC,
@@ -316,6 +326,146 @@
return;
}
+void WriterTest::setupExtractor(AMediaExtractor *extractor, string inputFileName,
+ int32_t &trackCount) {
+ ALOGV("Input file for extractor: %s", inputFileName.c_str());
+
+ int32_t fd = open(inputFileName.c_str(), O_RDONLY);
+ ASSERT_GE(fd, 0) << "Failed to open writer's output file to validate";
+
+ struct stat buf;
+ int32_t status = fstat(fd, &buf);
+ ASSERT_EQ(status, 0) << "Failed to get properties of input file for extractor";
+
+ size_t fileSize = buf.st_size;
+ ALOGV("Size of input file to extractor: %zu", fileSize);
+
+ status = AMediaExtractor_setDataSourceFd(extractor, fd, 0, fileSize);
+ ASSERT_EQ(status, AMEDIA_OK) << "Failed to set data source for extractor";
+
+ trackCount = AMediaExtractor_getTrackCount(extractor);
+ ASSERT_GT(trackCount, 0) << "No tracks reported by extractor";
+ ALOGV("Number of tracks reported by extractor : %d", trackCount);
+ return;
+}
+
+void WriterTest::extract(AMediaExtractor *extractor, configFormat ¶ms,
+ vector<BufferInfo> &bufferInfo, uint8_t *buffer, size_t bufSize,
+ size_t *bytesExtracted, int32_t idx) {
+ AMediaExtractor_selectTrack(extractor, idx);
+ AMediaFormat *format = AMediaExtractor_getTrackFormat(extractor, idx);
+ ASSERT_NE(format, nullptr) << "Track format is NULL";
+ ALOGI("Track format = %s", AMediaFormat_toString(format));
+
+ const char *mime = nullptr;
+ AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime);
+ ASSERT_NE(mime, nullptr) << "Track mime is NULL";
+ ALOGI("Track mime = %s", mime);
+ strlcpy(params.mime, mime, kMimeSize);
+
+ if (!strncmp(mime, "audio/", 6)) {
+ ASSERT_TRUE(
+ AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, ¶ms.channelCount))
+ << "Extractor did not report channel count";
+ ASSERT_TRUE(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, ¶ms.sampleRate))
+ << "Extractor did not report sample rate";
+ } else if (!strncmp(mime, "video/", 6) || !strncmp(mime, "image/", 6)) {
+ ASSERT_TRUE(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, ¶ms.width))
+ << "Extractor did not report width";
+ ASSERT_TRUE(AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, ¶ms.height))
+ << "Extractor did not report height";
+ } else {
+ ASSERT_TRUE(false) << "Invalid mime " << mime;
+ }
+
+ int32_t bufferOffset = 0;
+ // Get CSD data
+ int index = 0;
+ void *csdBuf;
+ while (1) {
+ csdBuf = nullptr;
+ char csdName[16];
+ snprintf(csdName, 16, "csd-%d", index);
+ size_t csdSize = 0;
+ bool csdFound = AMediaFormat_getBuffer(format, csdName, &csdBuf, &csdSize);
+ if (!csdFound || !csdBuf || !csdSize) break;
+
+ bufferInfo.push_back({static_cast<int32_t>(csdSize), CODEC_CONFIG_FLAG, 0});
+ memcpy(buffer + bufferOffset, csdBuf, csdSize);
+ bufferOffset += csdSize;
+ index++;
+ }
+
+ // Get frame data
+ while (1) {
+ ssize_t sampleSize = AMediaExtractor_getSampleSize(extractor);
+ if (sampleSize < 0) break;
+
+ uint8_t *sampleBuffer = (uint8_t *)malloc(sampleSize);
+ ASSERT_NE(sampleBuffer, nullptr) << "Failed to allocate the buffer of size " << sampleSize;
+
+ int bytesRead = AMediaExtractor_readSampleData(extractor, sampleBuffer, sampleSize);
+ ASSERT_EQ(bytesRead, sampleSize)
+ << "Number of bytes extracted does not match with sample size";
+ int64_t pts = AMediaExtractor_getSampleTime(extractor);
+ uint32_t flag = AMediaExtractor_getSampleFlags(extractor);
+
+ if (mime == MEDIA_MIMETYPE_AUDIO_VORBIS) {
+ // Removing 4 bytes of AMEDIAFORMAT_KEY_VALID_SAMPLES from sample size
+ bytesRead = bytesRead - 4;
+ }
+
+ ASSERT_LE(bufferOffset + bytesRead, bufSize)
+ << "Size of the buffer is insufficient to store the extracted data";
+ bufferInfo.push_back({bytesRead, flag, pts});
+ memcpy(buffer + bufferOffset, sampleBuffer, bytesRead);
+ bufferOffset += bytesRead;
+
+ AMediaExtractor_advance(extractor);
+ free(sampleBuffer);
+ }
+ *bytesExtracted = bufferOffset;
+ return;
+}
+
+void WriterTest::compareParams(configFormat srcParam, configFormat dstParam,
+ vector<BufferInfo> dstBufInfo, int32_t index) {
+ ASSERT_STREQ(srcParam.mime, dstParam.mime)
+ << "Extracted mime type does not match with input mime type";
+
+ if (!strncmp(srcParam.mime, "audio/", 6)) {
+ ASSERT_EQ(srcParam.channelCount, dstParam.channelCount)
+ << "Extracted channel count does not match with input channel count";
+ ASSERT_EQ(srcParam.sampleRate, dstParam.sampleRate)
+ << "Extracted sample rate does not match with input sample rate";
+ } else if (!strncmp(srcParam.mime, "video/", 6) || !strncmp(srcParam.mime, "image/", 6)) {
+ ASSERT_EQ(srcParam.width, dstParam.width)
+ << "Extracted width does not match with input width";
+ ASSERT_EQ(srcParam.height, dstParam.height)
+ << "Extracted height does not match with input height";
+ } else {
+ ASSERT_TRUE(false) << "Invalid mime type" << srcParam.mime;
+ }
+
+ int32_t toleranceValueUs = kMuxToleranceTimeUs;
+ if (mWriterName == MPEG4) {
+ toleranceValueUs = kMpeg4MuxToleranceTimeUs;
+ }
+ for (int32_t i = 0; i < dstBufInfo.size(); i++) {
+ ASSERT_EQ(mBufferInfo[index][i].size, dstBufInfo[i].size)
+ << "Input size " << mBufferInfo[index][i].size << " mismatched with extracted size "
+ << dstBufInfo[i].size;
+ ASSERT_EQ(mBufferInfo[index][i].flags, dstBufInfo[i].flags)
+ << "Input flag " << mBufferInfo[index][i].flags
+ << " mismatched with extracted size " << dstBufInfo[i].flags;
+ ASSERT_LE(abs(mBufferInfo[index][i].timeUs - dstBufInfo[i].timeUs), toleranceValueUs)
+ << "Difference between original timestamp " << mBufferInfo[index][i].timeUs
+ << " and extracted timestamp " << dstBufInfo[i].timeUs
+ << "is greater than tolerance value = " << toleranceValueUs << " micro seconds";
+ }
+ return;
+}
+
TEST_P(WriteFunctionalityTest, CreateWriterTest) {
if (mDisableTest) return;
ALOGV("Tests the creation of writers");
@@ -350,16 +500,23 @@
if (inpId[1] != UNUSED_ID) {
numTracks++;
}
+
+ size_t fileSize[numTracks];
+ configFormat param[numTracks];
for (int32_t idx = 0; idx < numTracks; idx++) {
string inputFile = gEnv->getRes();
string inputInfo = gEnv->getRes();
- configFormat param;
bool isAudio;
- getFileDetails(inputFile, inputInfo, param, isAudio, inpId[idx]);
+ getFileDetails(inputFile, inputInfo, param[idx], isAudio, inpId[idx]);
ASSERT_NE(inputFile.compare(gEnv->getRes()), 0) << "No input file specified";
+ struct stat buf;
+ status = stat(inputFile.c_str(), &buf);
+ ASSERT_EQ(status, 0) << "Failed to get properties of input file:" << inputFile;
+ fileSize[idx] = buf.st_size;
+
ASSERT_NO_FATAL_FAILURE(getInputBufferInfo(inputFile, inputInfo, idx));
- status = addWriterSource(isAudio, param, idx);
+ status = addWriterSource(isAudio, param[idx], idx);
ASSERT_EQ((status_t)OK, status) << "Failed to add source for " << writerFormat << "Writer";
}
@@ -389,6 +546,50 @@
status = mWriter->stop();
ASSERT_EQ((status_t)OK, status) << "Failed to stop the writer";
close(fd);
+
+ // Validate the output muxed file created by writer
+ // TODO(b/146423022): Skip validating output for webm writer
+ // TODO(b/146421018): Skip validating output for ogg writer
+ if (mWriterName != OGG && mWriterName != WEBM) {
+ configFormat extractorParams[numTracks];
+ vector<BufferInfo> extractorBufferInfo[numTracks];
+ int32_t trackCount = -1;
+
+ AMediaExtractor *extractor = AMediaExtractor_new();
+ ASSERT_NE(extractor, nullptr) << "Failed to create extractor";
+ ASSERT_NO_FATAL_FAILURE(setupExtractor(extractor, outputFile, trackCount));
+ ASSERT_EQ(trackCount, numTracks)
+ << "Tracks reported by extractor does not match with input number of tracks";
+
+ for (int32_t idx = 0; idx < numTracks; idx++) {
+ char *inputBuffer = (char *)malloc(fileSize[idx]);
+ ASSERT_NE(inputBuffer, nullptr)
+ << "Failed to allocate the buffer of size " << fileSize[idx];
+ mInputStream[idx].seekg(0, mInputStream[idx].beg);
+ mInputStream[idx].read(inputBuffer, fileSize[idx]);
+ ASSERT_EQ(mInputStream[idx].gcount(), fileSize[idx]);
+
+ uint8_t *extractedBuffer = (uint8_t *)malloc(fileSize[idx]);
+ ASSERT_NE(extractedBuffer, nullptr)
+ << "Failed to allocate the buffer of size " << fileSize[idx];
+ size_t bytesExtracted = 0;
+
+ ASSERT_NO_FATAL_FAILURE(extract(extractor, extractorParams[idx],
+ extractorBufferInfo[idx], extractedBuffer,
+ fileSize[idx], &bytesExtracted, idx));
+ ASSERT_GT(bytesExtracted, 0) << "Total bytes extracted by extractor cannot be zero";
+
+ ASSERT_NO_FATAL_FAILURE(
+ compareParams(param[idx], extractorParams[idx], extractorBufferInfo[idx], idx));
+
+ ASSERT_EQ(memcmp(extractedBuffer, (uint8_t *)inputBuffer, bytesExtracted), 0)
+ << "Extracted bit stream does not match with input bit stream";
+
+ free(inputBuffer);
+ free(extractedBuffer);
+ }
+ AMediaExtractor_delete(extractor);
+ }
}
TEST_P(WriteFunctionalityTest, PauseWriterTest) {
@@ -525,6 +726,176 @@
close(fd);
}
+class WriterValidityTest
+ : public WriterTest,
+ public ::testing::TestWithParam<
+ tuple<string /* writerFormat*/, inputId /* inputId0*/, bool /* addSourceFail*/>> {
+ public:
+ virtual void SetUp() override { setupWriterType(get<0>(GetParam())); }
+};
+
+TEST_P(WriterValidityTest, InvalidInputTest) {
+ if (mDisableTest) return;
+ ALOGV("Validates writer's behavior for invalid inputs");
+
+ string writerFormat = get<0>(GetParam());
+ inputId inpId = get<1>(GetParam());
+ bool addSourceFailExpected = get<2>(GetParam());
+
+ // Test writers for invalid FD value
+ int32_t fd = -1;
+ int32_t status = createWriter(fd);
+ if (status != OK) {
+ ALOGV("createWriter failed for invalid FD, this is expected behavior");
+ return;
+ }
+
+ // If writer was created for invalid fd, test it further.
+ string inputFile = gEnv->getRes();
+ string inputInfo = gEnv->getRes();
+ configFormat param;
+ bool isAudio;
+ ASSERT_NE(inpId, UNUSED_ID) << "Test expects first inputId to be a valid id";
+
+ getFileDetails(inputFile, inputInfo, param, isAudio, inpId);
+ ASSERT_NE(inputFile.compare(gEnv->getRes()), 0) << "No input file specified";
+
+ ASSERT_NO_FATAL_FAILURE(getInputBufferInfo(inputFile, inputInfo));
+ status = addWriterSource(isAudio, param);
+ if (status != OK) {
+ ASSERT_TRUE(addSourceFailExpected)
+ << "Failed to add source for " << writerFormat << " writer";
+ ALOGV("addWriterSource failed for invalid FD, this is expected behavior");
+ return;
+ }
+
+ // start the writer with valid argument but invalid FD
+ status = mWriter->start(mFileMeta.get());
+ ASSERT_NE((status_t)OK, status) << "Writer did not fail for invalid FD";
+
+ status = sendBuffersToWriter(mInputStream[0], mBufferInfo[0], mInputFrameId[0],
+ mCurrentTrack[0], 0, mBufferInfo[0].size());
+ ASSERT_NE((status_t)OK, status) << "Writer did not report error for invalid FD";
+
+ status = mCurrentTrack[0]->stop();
+ ASSERT_EQ((status_t)OK, status) << "Failed to stop the track";
+
+ status = mWriter->stop();
+ ASSERT_EQ((status_t)OK, status) << "Failed to stop " << writerFormat << " writer";
+}
+
+TEST_P(WriterValidityTest, MalFormedDataTest) {
+ if (mDisableTest) return;
+ // Enable test for Ogg writer
+ ASSERT_NE(mWriterName, OGG) << "TODO(b/160105646)";
+ ALOGV("Test writer for malformed inputs");
+
+ string writerFormat = get<0>(GetParam());
+ inputId inpId = get<1>(GetParam());
+ bool addSourceFailExpected = get<2>(GetParam());
+ int32_t fd =
+ open(OUTPUT_FILE_NAME, O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
+ ASSERT_GE(fd, 0) << "Failed to open output file to dump writer's data";
+
+ int32_t status = createWriter(fd);
+ ASSERT_EQ(status, (status_t)OK)
+ << "Failed to create writer for " << writerFormat << " output format";
+
+ string inputFile = gEnv->getRes();
+ string inputInfo = gEnv->getRes();
+ configFormat param;
+ bool isAudio;
+ ASSERT_NE(inpId, UNUSED_ID) << "Test expects first inputId to be a valid id";
+
+ getFileDetails(inputFile, inputInfo, param, isAudio, inpId);
+ ASSERT_NE(inputFile.compare(gEnv->getRes()), 0) << "No input file specified";
+
+ ASSERT_NO_FATAL_FAILURE(getInputBufferInfo(inputFile, inputInfo));
+ // Remove CSD data from input
+ mNumCsds[0] = 0;
+ status = addWriterSource(isAudio, param);
+ if (status != OK) {
+ ASSERT_TRUE(addSourceFailExpected)
+ << "Failed to add source for " << writerFormat << " writer";
+ ALOGV("%s writer failed to addSource after removing CSD from input", writerFormat.c_str());
+ return;
+ }
+
+ status = mWriter->start(mFileMeta.get());
+ ASSERT_EQ((status_t)OK, status) << "Could not start " << writerFormat << "writer";
+
+ // Skip first few frames. These may contain sync frames also.
+ int32_t frameID = mInputFrameId[0] + mBufferInfo[0].size() / 4;
+ status = sendBuffersToWriter(mInputStream[0], mBufferInfo[0], frameID, mCurrentTrack[0], 0,
+ mBufferInfo[0].size());
+ ASSERT_EQ((status_t)OK, status) << writerFormat << " writer failed";
+
+ status = mCurrentTrack[0]->stop();
+ ASSERT_EQ((status_t)OK, status) << "Failed to stop the track";
+
+ Vector<String16> args;
+ status = mWriter->dump(fd, args);
+ ASSERT_EQ((status_t)OK, status) << "Failed to dump statistics from writer";
+
+ status = mWriter->stop();
+ ASSERT_EQ((status_t)OK, status) << "Failed to stop " << writerFormat << " writer";
+ close(fd);
+}
+
+// This test is specific to MPEG4Writer to test more APIs
+TEST_P(WriteFunctionalityTest, Mpeg4WriterTest) {
+ if (mDisableTest) return;
+ if (mWriterName != standardWriters::MPEG4) return;
+ ALOGV("Test MPEG4 writer specific APIs");
+
+ inputId inpId = get<1>(GetParam());
+ int32_t fd =
+ open(OUTPUT_FILE_NAME, O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
+ ASSERT_GE(fd, 0) << "Failed to open output file to dump writer's data";
+
+ int32_t status = createWriter(fd);
+ ASSERT_EQ(status, (status_t)OK) << "Failed to create writer for mpeg4 output format";
+
+ string inputFile = gEnv->getRes();
+ string inputInfo = gEnv->getRes();
+ configFormat param;
+ bool isAudio;
+ ASSERT_NE(inpId, UNUSED_ID) << "Test expects first inputId to be a valid id";
+
+ getFileDetails(inputFile, inputInfo, param, isAudio, inpId);
+ ASSERT_NE(inputFile.compare(gEnv->getRes()), 0) << "No input file specified";
+
+ ASSERT_NO_FATAL_FAILURE(getInputBufferInfo(inputFile, inputInfo));
+ status = addWriterSource(isAudio, param);
+ ASSERT_EQ((status_t)OK, status) << "Failed to add source for mpeg4 Writer";
+
+ // signal meta data for the writer
+ sp<MPEG4Writer> mp4writer = static_cast<MPEG4Writer *>(mWriter.get());
+ status = mp4writer->setInterleaveDuration(kDefaultInterleaveDuration);
+ ASSERT_EQ((status_t)OK, status) << "setInterleaveDuration failed";
+
+ status = mp4writer->setGeoData(kDefaultLatitudex10000, kDefaultLongitudex10000);
+ ASSERT_EQ((status_t)OK, status) << "setGeoData failed";
+
+ status = mp4writer->setCaptureRate(kDefaultFPS);
+ ASSERT_EQ((status_t)OK, status) << "setCaptureRate failed";
+
+ status = mWriter->start(mFileMeta.get());
+ ASSERT_EQ((status_t)OK, status) << "Could not start the writer";
+
+ status = sendBuffersToWriter(mInputStream[0], mBufferInfo[0], mInputFrameId[0],
+ mCurrentTrack[0], 0, mBufferInfo[0].size());
+ ASSERT_EQ((status_t)OK, status) << "mpeg4 writer failed";
+
+ status = mCurrentTrack[0]->stop();
+ ASSERT_EQ((status_t)OK, status) << "Failed to stop the track";
+
+ status = mWriter->stop();
+ ASSERT_EQ((status_t)OK, status) << "Failed to stop the writer";
+ mp4writer.clear();
+ close(fd);
+}
+
class ListenerTest
: public WriterTest,
public ::testing::TestWithParam<tuple<
@@ -657,6 +1028,7 @@
make_tuple("amrwb", AMR_WB_1, UNUSED_ID, 0.5, 0.5, 1),
make_tuple("mpeg2Ts", AAC_1, UNUSED_ID, 0.2, 1, 1),
make_tuple("mpeg4", AAC_1, UNUSED_ID, 0.4, 0.3, 0.25),
+ make_tuple("mpeg4", AAC_1, UNUSED_ID, 0.3, 1, 0.5),
make_tuple("ogg", OPUS_1, UNUSED_ID, 0.7, 0.3, 1)));
// TODO: (b/144476164)
@@ -703,7 +1075,31 @@
make_tuple("webm", VP8_1, OPUS_1, 0.50),
make_tuple("webm", VORBIS_1, VP8_1, 0.25)));
+INSTANTIATE_TEST_SUITE_P(
+ WriterValidityTest, WriterValidityTest,
+ ::testing::Values(
+ make_tuple("aac", AAC_1, true),
+
+ make_tuple("amrnb", AMR_NB_1, true),
+ make_tuple("amrwb", AMR_WB_1, true),
+
+ make_tuple("mpeg4", AAC_1, false),
+ make_tuple("mpeg4", AMR_NB_1, false),
+ make_tuple("mpeg4", AVC_1, false),
+ make_tuple("mpeg4", H263_1, false),
+ make_tuple("mpeg4", HEIC_1, false),
+ make_tuple("mpeg4", HEVC_1, false),
+ make_tuple("mpeg4", MPEG4_1, false),
+
+ make_tuple("ogg", OPUS_1, true),
+
+ make_tuple("webm", OPUS_1, false),
+ make_tuple("webm", VORBIS_1, true),
+ make_tuple("webm", VP8_1, false),
+ make_tuple("webm", VP9_1, false)));
+
int main(int argc, char **argv) {
+ ProcessState::self()->startThreadPool();
gEnv = new WriterTestEnvironment();
::testing::AddGlobalTestEnvironment(gEnv);
::testing::InitGoogleTest(&argc, argv);
diff --git a/media/libstagefright/tests/writer/WriterUtility.h b/media/libstagefright/tests/writer/WriterUtility.h
index 5e79298..6b456fb 100644
--- a/media/libstagefright/tests/writer/WriterUtility.h
+++ b/media/libstagefright/tests/writer/WriterUtility.h
@@ -34,6 +34,12 @@
constexpr uint32_t kMaxTrackCount = 2;
constexpr uint32_t kMaxCSDStrlen = 16;
constexpr uint32_t kMaxCount = 20;
+constexpr int32_t kMimeSize = 128;
+constexpr int32_t kDefaultInterleaveDuration = 0;
+// Geodata is set according to ISO-6709 standard.
+constexpr int32_t kDefaultLatitudex10000 = 500000;
+constexpr int32_t kDefaultLongitudex10000 = 1000000;
+constexpr float kDefaultFPS = 30.0f;
struct BufferInfo {
int32_t size;
@@ -41,6 +47,14 @@
int64_t timeUs;
};
+struct configFormat {
+ char mime[kMimeSize];
+ int32_t width;
+ int32_t height;
+ int32_t sampleRate;
+ int32_t channelCount;
+};
+
int32_t sendBuffersToWriter(ifstream &inputStream, vector<BufferInfo> &bufferInfo,
int32_t &inputFrameId, sp<MediaAdapter> ¤tTrack, int32_t offset,
int32_t range, bool isPaused = false,
diff --git a/media/utils/EventLogTags.logtags b/media/utils/EventLogTags.logtags
index 67f0ea8..c397f34 100644
--- a/media/utils/EventLogTags.logtags
+++ b/media/utils/EventLogTags.logtags
@@ -31,7 +31,7 @@
# 6: Percent
# Default value for data of type int/long is 2 (bytes).
#
-# See system/core/logcat/event.logtags for the master copy of the tags.
+# See system/core/logcat/event.logtags for the original definition of the tags.
# 61000 - 61199 reserved for audioserver
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index f014209..1935cde 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -684,8 +684,8 @@
sp<NBLog::Writer> AudioFlinger::newWriter_l(size_t size, const char *name)
{
- // If there is no memory allocated for logs, return a dummy writer that does nothing.
- // Similarly if we can't contact the media.log service, also return a dummy writer.
+ // If there is no memory allocated for logs, return a no-op writer that does nothing.
+ // Similarly if we can't contact the media.log service, also return a no-op writer.
if (mLogMemoryDealer == 0 || sMediaLogService == 0) {
return new NBLog::Writer();
}
@@ -711,7 +711,7 @@
}
}
// Even after garbage-collecting all old writers, there is still not enough memory,
- // so return a dummy writer
+ // so return a no-op writer
return new NBLog::Writer();
}
success:
@@ -1283,9 +1283,9 @@
}
// Now set the master mute in each playback thread. Playback threads
- // assigned to HALs which do not have master mute support will apply master
- // mute during the mix operation. Threads with HALs which do support master
- // mute will simply ignore the setting.
+ // assigned to HALs which do not have master mute support will apply master mute
+ // during the mix operation. Threads with HALs which do support master mute
+ // will simply ignore the setting.
Vector<VolumeInterface *> volumeInterfaces = getAllVolumeInterfaces_l();
for (size_t i = 0; i < volumeInterfaces.size(); i++) {
volumeInterfaces[i]->setMasterMute(muted);
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index 3dfeb83..529b87c 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -292,6 +292,9 @@
}
}
+ // Prevent calls to process() and other functions on effect interface from now on.
+ // The effect engine will be released by the destructor when the last strong reference on
+ // this object is released which can happen after next process is called.
if (mHandles.size() == 0 && !mPinned) {
mState = DESTROYED;
}
@@ -565,20 +568,6 @@
}
-ssize_t AudioFlinger::EffectModule::removeHandle_l(EffectHandle *handle)
-{
- ssize_t status = EffectBase::removeHandle_l(handle);
-
- // Prevent calls to process() and other functions on effect interface from now on.
- // The effect engine will be released by the destructor when the last strong reference on
- // this object is released which can happen after next process is called.
- if (status == 0 && !mPinned) {
- mEffectInterface->close();
- }
-
- return status;
-}
-
bool AudioFlinger::EffectModule::updateState() {
Mutex::Autolock _l(mLock);
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index 2826297..2c79ac5 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -144,7 +144,7 @@
status_t addHandle(EffectHandle *handle);
ssize_t disconnectHandle(EffectHandle *handle, bool unpinIfLast);
ssize_t removeHandle(EffectHandle *handle);
- virtual ssize_t removeHandle_l(EffectHandle *handle);
+ ssize_t removeHandle_l(EffectHandle *handle);
EffectHandle* controlHandle_l();
bool purgeHandles();
@@ -240,8 +240,6 @@
return mOutBuffer != 0 ? reinterpret_cast<int16_t*>(mOutBuffer->ptr()) : NULL;
}
- ssize_t removeHandle_l(EffectHandle *handle) override;
-
status_t setDevices(const AudioDeviceTypeAddrVector &devices);
status_t setInputDevice(const AudioDeviceTypeAddr &device);
status_t setVolume(uint32_t *left, uint32_t *right, bool controller);
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index bf2e953..cd3c743 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -27,7 +27,6 @@
#include "Configuration.h"
#include <time.h>
-#include <utils/Debug.h>
#include <utils/Log.h>
#include <utils/Trace.h>
#include <system/audio.h>
diff --git a/services/audioflinger/FastMixerDumpState.cpp b/services/audioflinger/FastMixerDumpState.cpp
index a42e09c..3f20282 100644
--- a/services/audioflinger/FastMixerDumpState.cpp
+++ b/services/audioflinger/FastMixerDumpState.cpp
@@ -24,7 +24,6 @@
#include <cpustats/ThreadCpuUsage.h>
#endif
#endif
-#include <utils/Debug.h>
#include <utils/Log.h>
#include "FastMixerDumpState.h"
diff --git a/services/audioflinger/OWNERS b/services/audioflinger/OWNERS
index d02d9e0..034d161 100644
--- a/services/audioflinger/OWNERS
+++ b/services/audioflinger/OWNERS
@@ -1,4 +1,4 @@
+gkasten@google.com
hunga@google.com
jmtrivi@google.com
mnaganov@google.com
-gkasten@google.com
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index c4ef0fa..384ddb5 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -7313,7 +7313,7 @@
const ssize_t availableToRead = mPipeSource->availableToRead();
if (availableToRead >= 0) {
- // PipeSource is the master clock. It is up to the AudioRecord client to keep up.
+ // PipeSource is the primary clock. It is up to the AudioRecord client to keep up.
LOG_ALWAYS_FATAL_IF((size_t)availableToRead > mPipeFramesP2,
"more frames to read than fifo size, %zd > %zu",
availableToRead, mPipeFramesP2);
diff --git a/services/audioflinger/TypedLogger.h b/services/audioflinger/TypedLogger.h
index 6ef19bf..feb71e3 100644
--- a/services/audioflinger/TypedLogger.h
+++ b/services/audioflinger/TypedLogger.h
@@ -80,7 +80,7 @@
// TODO Permit disabling of logging at compile-time.
-// TODO A non-nullptr dummy implementation that is a nop would be faster than checking for nullptr
+// TODO A non-nullptr stub implementation that is a nop would be faster than checking for nullptr
// in the case when logging is enabled at compile-time and enabled at runtime, but it might be
// slower than nullptr check when logging is enabled at compile-time and disabled at runtime.
@@ -129,8 +129,8 @@
namespace android {
extern "C" {
-// TODO consider adding a thread_local NBLog::Writer tlDummyNBLogWriter and then
-// initialize below tlNBLogWriter to &tlDummyNBLogWriter to remove the need to
+// TODO consider adding a thread_local NBLog::Writer tlStubNBLogWriter and then
+// initialize below tlNBLogWriter to &tlStubNBLogWriter to remove the need to
// check for nullptr every time. Also reduces the need to add a new logging macro above
// each time we want to log a new type.
extern thread_local NBLog::Writer *tlNBLogWriter;
diff --git a/services/audiopolicy/OWNERS b/services/audiopolicy/OWNERS
index a8483fa..da9d32f 100644
--- a/services/audiopolicy/OWNERS
+++ b/services/audiopolicy/OWNERS
@@ -1,3 +1,2 @@
jmtrivi@google.com
-krocard@google.com
mnaganov@google.com
diff --git a/services/audiopolicy/common/include/Volume.h b/services/audiopolicy/common/include/Volume.h
index 7c8ce83..736f8b2 100644
--- a/services/audiopolicy/common/include/Volume.h
+++ b/services/audiopolicy/common/include/Volume.h
@@ -126,6 +126,7 @@
case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP:
case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES:
case AUDIO_DEVICE_OUT_USB_HEADSET:
+ case AUDIO_DEVICE_OUT_BLE_HEADSET:
return DEVICE_CATEGORY_HEADSET;
case AUDIO_DEVICE_OUT_HEARING_AID:
return DEVICE_CATEGORY_HEARING_AID;
@@ -139,6 +140,7 @@
case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER:
case AUDIO_DEVICE_OUT_USB_ACCESSORY:
case AUDIO_DEVICE_OUT_REMOTE_SUBMIX:
+ case AUDIO_DEVICE_OUT_BLE_SPEAKER:
default:
return DEVICE_CATEGORY_SPEAKER;
}
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 1fe340a..ae71959 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -462,7 +462,16 @@
}
}
}
-
+ auto musicStrategy = streamToStrategy(AUDIO_STREAM_MUSIC);
+ for (size_t i = 0; i < mOutputs.size(); i++) {
+ sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
+ // mute media strategies and delay device switch by the largest
+ // This avoid sending the music tail into the earpiece or headset.
+ setStrategyMute(musicStrategy, true, desc);
+ setStrategyMute(musicStrategy, false, desc, MUTE_TIME_MS,
+ mEngine->getOutputDevicesForAttributes(attributes_initializer(AUDIO_USAGE_MEDIA),
+ nullptr, true /*fromCache*/).types());
+ }
// Toggle the device state: UNAVAILABLE -> AVAILABLE
// This will force reading again the device configuration
status = setDeviceConnectionState(device,
@@ -1766,7 +1775,8 @@
checkAndSetVolume(curves, client->volumeSource(),
curves.getVolumeIndex(outputDesc->devices().types()),
outputDesc,
- outputDesc->devices().types());
+ outputDesc->devices().types(), 0 /*delay*/,
+ outputDesc->useHwGain() /*force*/);
// update the outputs if starting an output with a stream that can affect notification
// routing
@@ -6038,7 +6048,8 @@
if (!Intersection(deviceTypes,
{AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES,
AUDIO_DEVICE_OUT_WIRED_HEADSET, AUDIO_DEVICE_OUT_WIRED_HEADPHONE,
- AUDIO_DEVICE_OUT_USB_HEADSET, AUDIO_DEVICE_OUT_HEARING_AID}).empty() &&
+ AUDIO_DEVICE_OUT_USB_HEADSET, AUDIO_DEVICE_OUT_HEARING_AID,
+ AUDIO_DEVICE_OUT_BLE_HEADSET}).empty() &&
((volumeSource == alarmVolumeSrc ||
volumeSource == ringVolumeSrc) ||
(volumeSource == toVolumeSource(AUDIO_STREAM_NOTIFICATION)) ||
diff --git a/services/audiopolicy/service/AudioPolicyEffects.cpp b/services/audiopolicy/service/AudioPolicyEffects.cpp
index 1ec0c5e..b738633 100644
--- a/services/audiopolicy/service/AudioPolicyEffects.cpp
+++ b/services/audiopolicy/service/AudioPolicyEffects.cpp
@@ -121,8 +121,8 @@
Vector <EffectDesc *> effects = mInputSources.valueAt(index)->mEffects;
for (size_t i = 0; i < effects.size(); i++) {
EffectDesc *effect = effects[i];
- sp<AudioEffect> fx = new AudioEffect(NULL, String16("android"), &effect->mUuid, -1, 0,
- 0, audioSession, input);
+ sp<AudioEffect> fx = new AudioEffect(String16("android"));
+ fx->set(NULL, &effect->mUuid, -1, 0, 0, audioSession, input);
status_t status = fx->initCheck();
if (status != NO_ERROR && status != ALREADY_EXISTS) {
ALOGW("addInputEffects(): failed to create Fx %s on source %d",
@@ -270,8 +270,8 @@
Vector <EffectDesc *> effects = mOutputStreams.valueAt(index)->mEffects;
for (size_t i = 0; i < effects.size(); i++) {
EffectDesc *effect = effects[i];
- sp<AudioEffect> fx = new AudioEffect(NULL, String16("android"), &effect->mUuid, 0, 0, 0,
- audioSession, output);
+ sp<AudioEffect> fx = new AudioEffect(String16("android"));
+ fx->set(NULL, &effect->mUuid, 0, 0, 0, audioSession, output);
status_t status = fx->initCheck();
if (status != NO_ERROR && status != ALREADY_EXISTS) {
ALOGE("addOutputSessionEffects(): failed to create Fx %s on session %d",
@@ -970,11 +970,11 @@
for (const auto& deviceEffectsIter : mDeviceEffects) {
const auto& deviceEffects = deviceEffectsIter.second;
for (const auto& effectDesc : deviceEffects->mEffectDescriptors->mEffects) {
- auto fx = std::make_unique<AudioEffect>(
- EFFECT_UUID_NULL, String16("android"), &effectDesc->mUuid, 0, nullptr,
- nullptr, AUDIO_SESSION_DEVICE, AUDIO_IO_HANDLE_NONE,
- AudioDeviceTypeAddr{deviceEffects->getDeviceType(),
- deviceEffects->getDeviceAddress()});
+ auto fx = std::make_unique<AudioEffect>(String16("android"));
+ fx->set(EFFECT_UUID_NULL, &effectDesc->mUuid, 0, nullptr,
+ nullptr, AUDIO_SESSION_DEVICE, AUDIO_IO_HANDLE_NONE,
+ AudioDeviceTypeAddr{deviceEffects->getDeviceType(),
+ deviceEffects->getDeviceAddress()});
status_t status = fx->initCheck();
if (status != NO_ERROR && status != ALREADY_EXISTS) {
ALOGE("%s(): failed to create Fx %s on port type=%d address=%s", __func__,
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 4a509aa..7b3dfb4 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -1833,10 +1833,12 @@
mStatusWaiters++;
+ bool signalPipelineDrain = false;
if (!active && mUseHalBufManager) {
auto streamIds = mOutputStreams.getStreamIds();
if (mStatus == STATUS_ACTIVE) {
mRequestThread->signalPipelineDrain(streamIds);
+ signalPipelineDrain = true;
}
mRequestBufferSM.onWaitUntilIdle();
}
@@ -1866,6 +1868,10 @@
}
} while (!stateSeen);
+ if (signalPipelineDrain) {
+ mRequestThread->resetPipelineDrain();
+ }
+
mStatusWaiters--;
return res;
@@ -4785,6 +4791,12 @@
mStreamIdsToBeDrained = streamIds;
}
+void Camera3Device::RequestThread::resetPipelineDrain() {
+ Mutex::Autolock pl(mPauseLock);
+ mNotifyPipelineDrain = false;
+ mStreamIdsToBeDrained.clear();
+}
+
void Camera3Device::RequestThread::clearPreviousRequest() {
Mutex::Autolock l(mRequestLock);
mPrevRequest.clear();
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 408f1f9..4c5f484 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -832,6 +832,7 @@
}
void signalPipelineDrain(const std::vector<int>& streamIds);
+ void resetPipelineDrain();
status_t switchToOffline(
const std::vector<int32_t>& streamsToKeep,
diff --git a/services/oboeservice/AAudioServiceStreamShared.cpp b/services/oboeservice/AAudioServiceStreamShared.cpp
index 01b1c2e..f2cf016 100644
--- a/services/oboeservice/AAudioServiceStreamShared.cpp
+++ b/services/oboeservice/AAudioServiceStreamShared.cpp
@@ -105,7 +105,7 @@
}
int32_t capacityInFrames = numBursts * framesPerBurst;
- // Final sanity check.
+ // Final range check.
if (capacityInFrames > MAX_FRAMES_PER_BUFFER) {
ALOGE("calculateBufferCapacity() calc capacity %d > max %d",
capacityInFrames, MAX_FRAMES_PER_BUFFER);