Merge "MediaTesting: Validate bitstream for Opus WriteTest"
diff --git a/apex/Android.bp b/apex/Android.bp
index 73dc264..c5dd420 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -42,7 +42,14 @@
// Use a custom AndroidManifest.xml used for API targeting.
androidManifest: ":com.android.media-androidManifest",
- legacy_android10_support: true,
+ // IMPORTANT: For the APEX to be installed on Android 10 (API 29),
+ // min_sdk_version should be 29. This enables the build system to make
+ // sure the package compatible to Android 10 in two ways:
+ // - build the APEX package compatible to Android 10
+ // so that the package can be installed.
+ // - build artifacts (lib/javalib/bin) against Android 10 SDK
+ // so that the artifacts can run.
+ min_sdk_version: "29",
}
apex {
@@ -79,7 +86,14 @@
// Use a custom AndroidManifest.xml used for API targeting.
androidManifest: ":com.android.media.swcodec-androidManifest",
- legacy_android10_support: true,
+ // IMPORTANT: For the APEX to be installed on Android 10 (API 29),
+ // min_sdk_version should be 29. This enables the build system to make
+ // sure the package compatible to Android 10 in two ways:
+ // - build the APEX package compatible to Android 10
+ // so that the package can be installed.
+ // - build artifacts (lib/javalib/bin) against Android 10 SDK
+ // so that the artifacts can run.
+ min_sdk_version: "29",
}
prebuilt_etc {
diff --git a/camera/CameraParameters.cpp b/camera/CameraParameters.cpp
index 68969cf..e95c91c 100644
--- a/camera/CameraParameters.cpp
+++ b/camera/CameraParameters.cpp
@@ -20,6 +20,7 @@
#include <string.h>
#include <stdlib.h>
+#include <unistd.h>
#include <camera/CameraParameters.h>
#include <system/graphics.h>
diff --git a/camera/CameraParameters2.cpp b/camera/CameraParameters2.cpp
index c29233c..a1cf355 100644
--- a/camera/CameraParameters2.cpp
+++ b/camera/CameraParameters2.cpp
@@ -21,6 +21,7 @@
#include <string.h>
#include <stdlib.h>
+#include <unistd.h>
#include <camera/CameraParameters2.h>
namespace android {
diff --git a/camera/ndk/ndk_vendor/impl/ACameraManager.cpp b/camera/ndk/ndk_vendor/impl/ACameraManager.cpp
index 70c887a..9aab29a 100644
--- a/camera/ndk/ndk_vendor/impl/ACameraManager.cpp
+++ b/camera/ndk/ndk_vendor/impl/ACameraManager.cpp
@@ -574,9 +574,8 @@
if (!serviceRet.isOk() || status != Status::NO_ERROR) {
ALOGE("%s: connect camera device failed", __FUNCTION__);
- // TODO: Convert serviceRet to camera_status_t
delete device;
- return ACAMERA_ERROR_UNKNOWN;
+ return utils::convertFromHidl(status);
}
if (deviceRemote == nullptr) {
ALOGE("%s: connect camera device failed! remote device is null", __FUNCTION__);
diff --git a/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp b/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp
index f164f28..3ecf6d5 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp
@@ -136,6 +136,8 @@
return Void();
}
+ base = static_cast<uint8_t *>(static_cast<void *>(destBase->getPointer()));
+
if (destBuffer.offset + destBuffer.size > destBase->getSize()) {
_hidl_cb(Status_V1_2::ERROR_DRM_FRAME_TOO_LARGE, 0, "invalid buffer size");
return Void();
diff --git a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
index d74bc53..7cb5a38 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
@@ -809,6 +809,11 @@
// count - number of secure stops
// list of fixed length secure stops
size_t countBufferSize = sizeof(uint32_t);
+ if (input.size() < countBufferSize) {
+ // SafetyNet logging
+ android_errorWriteLog(0x534e4554, "144766455");
+ return Status::BAD_VALUE;
+ }
uint32_t count = 0;
sscanf(reinterpret_cast<char*>(input.data()), "%04" PRIu32, &count);
diff --git a/media/codec2/components/avc/C2SoftAvcDec.cpp b/media/codec2/components/avc/C2SoftAvcDec.cpp
index 2be51dd..98d2fde 100644
--- a/media/codec2/components/avc/C2SoftAvcDec.cpp
+++ b/media/codec2/components/avc/C2SoftAvcDec.cpp
@@ -35,6 +35,7 @@
constexpr char COMPONENT_NAME[] = "c2.android.avc.decoder";
constexpr uint32_t kDefaultOutputDelay = 8;
constexpr uint32_t kMaxOutputDelay = 16;
+constexpr uint32_t kMinInputBytes = 4;
} // namespace
class C2SoftAvcDec::IntfImpl : public SimpleInterface<void>::BaseParams {
@@ -500,7 +501,7 @@
status_t C2SoftAvcDec::initDecoder() {
if (OK != createDecoder()) return UNKNOWN_ERROR;
mNumCores = MIN(getCpuCoreCount(), MAX_NUM_CORES);
- mStride = ALIGN128(mWidth);
+ mStride = ALIGN32(mWidth);
mSignalledError = false;
resetPlugin();
(void) setNumCores();
@@ -518,10 +519,20 @@
size_t inSize,
uint32_t tsMarker) {
uint32_t displayStride = mStride;
+ if (outBuffer) {
+ C2PlanarLayout layout;
+ layout = outBuffer->layout();
+ displayStride = layout.planes[C2PlanarLayout::PLANE_Y].rowInc;
+ }
uint32_t displayHeight = mHeight;
size_t lumaSize = displayStride * displayHeight;
size_t chromaSize = lumaSize >> 2;
+ if (mStride != displayStride) {
+ mStride = displayStride;
+ if (OK != setParams(mStride, IVD_DECODE_FRAME)) return false;
+ }
+
ps_decode_ip->u4_size = sizeof(ivd_video_decode_ip_t);
ps_decode_ip->e_cmd = IVD_CMD_VIDEO_DECODE;
if (inBuffer) {
@@ -755,24 +766,21 @@
ALOGE("not supposed to be here, invalid decoder context");
return C2_CORRUPTED;
}
- if (mStride != ALIGN128(mWidth)) {
- mStride = ALIGN128(mWidth);
- if (OK != setParams(mStride, IVD_DECODE_FRAME)) return C2_CORRUPTED;
- }
if (mOutBlock &&
- (mOutBlock->width() != mStride || mOutBlock->height() != mHeight)) {
+ (mOutBlock->width() != ALIGN32(mWidth) || mOutBlock->height() != mHeight)) {
mOutBlock.reset();
}
if (!mOutBlock) {
uint32_t format = HAL_PIXEL_FORMAT_YV12;
C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
- c2_status_t err = pool->fetchGraphicBlock(mStride, mHeight, format, usage, &mOutBlock);
+ c2_status_t err =
+ pool->fetchGraphicBlock(ALIGN32(mWidth), mHeight, format, usage, &mOutBlock);
if (err != C2_OK) {
ALOGE("fetchGraphicBlock for Output failed with status %d", err);
return err;
}
ALOGV("provided (%dx%d) required (%dx%d)",
- mOutBlock->width(), mOutBlock->height(), mStride, mHeight);
+ mOutBlock->width(), mOutBlock->height(), ALIGN32(mWidth), mHeight);
}
return C2_OK;
@@ -816,7 +824,7 @@
inSize, (int)work->input.ordinal.timestamp.peeku(),
(int)work->input.ordinal.frameIndex.peeku(), work->input.flags);
size_t inPos = 0;
- while (inPos < inSize) {
+ while (inPos < inSize && inSize - inPos >= kMinInputBytes) {
if (C2_OK != ensureDecoderState(pool)) {
mSignalledError = true;
work->workletsProcessed = 1u;
@@ -903,12 +911,12 @@
work->result = C2_CORRUPTED;
return;
}
- continue;
}
if (0 < s_decode_op.u4_pic_wd && 0 < s_decode_op.u4_pic_ht) {
if (mHeaderDecoded == false) {
mHeaderDecoded = true;
- setParams(ALIGN128(s_decode_op.u4_pic_wd), IVD_DECODE_FRAME);
+ mStride = ALIGN32(s_decode_op.u4_pic_wd);
+ setParams(mStride, IVD_DECODE_FRAME);
}
if (s_decode_op.u4_pic_wd != mWidth || s_decode_op.u4_pic_ht != mHeight) {
mWidth = s_decode_op.u4_pic_wd;
@@ -936,16 +944,7 @@
if (s_decode_op.u4_output_present) {
finishWork(s_decode_op.u4_ts, work);
}
- if (0 == s_decode_op.u4_num_bytes_consumed) {
- ALOGD("Bytes consumed is zero. Ignoring remaining bytes");
- break;
- }
inPos += s_decode_op.u4_num_bytes_consumed;
- if (hasPicture && (inSize - inPos)) {
- ALOGD("decoded frame in current access nal, ignoring further trailing bytes %d",
- (int)inSize - (int)inPos);
- break;
- }
}
if (eos) {
drainInternal(DRAIN_COMPONENT_WITH_EOS, pool, work);
diff --git a/media/codec2/components/avc/C2SoftAvcDec.h b/media/codec2/components/avc/C2SoftAvcDec.h
index ed27493..bd84de0 100644
--- a/media/codec2/components/avc/C2SoftAvcDec.h
+++ b/media/codec2/components/avc/C2SoftAvcDec.h
@@ -39,8 +39,7 @@
#define ivdext_ctl_set_num_cores_op_t ih264d_ctl_set_num_cores_op_t
#define ivdext_ctl_get_vui_params_ip_t ih264d_ctl_get_vui_params_ip_t
#define ivdext_ctl_get_vui_params_op_t ih264d_ctl_get_vui_params_op_t
-#define ALIGN64(x) ((((x) + 63) >> 6) << 6)
-#define ALIGN128(x) ((((x) + 127) >> 7) << 7)
+#define ALIGN32(x) ((((x) + 31) >> 5) << 5)
#define MAX_NUM_CORES 4
#define IVDEXT_CMD_CTL_SET_NUM_CORES \
(IVD_CONTROL_API_COMMAND_TYPE_T)IH264D_CMD_CTL_SET_NUM_CORES
diff --git a/media/codec2/components/avc/C2SoftAvcEnc.cpp b/media/codec2/components/avc/C2SoftAvcEnc.cpp
index e3d419c..ab93ce3 100644
--- a/media/codec2/components/avc/C2SoftAvcEnc.cpp
+++ b/media/codec2/components/avc/C2SoftAvcEnc.cpp
@@ -103,7 +103,7 @@
addParameter(
DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE)
- .withDefault(new C2StreamPictureSizeInfo::input(0u, 320, 240))
+ .withDefault(new C2StreamPictureSizeInfo::input(0u, 16, 16))
.withFields({
C2F(mSize, width).inRange(2, 2560, 2),
C2F(mSize, height).inRange(2, 2560, 2),
@@ -129,7 +129,7 @@
addParameter(
DefineParam(mFrameRate, C2_PARAMKEY_FRAME_RATE)
- .withDefault(new C2StreamFrameRateInfo::output(0u, 30.))
+ .withDefault(new C2StreamFrameRateInfo::output(0u, 1.))
// TODO: More restriction?
.withFields({C2F(mFrameRate, value).greaterThan(0.)})
.withSetter(Setter<decltype(*mFrameRate)>::StrictValueWithNoDeps)
diff --git a/media/codec2/components/gsm/C2SoftGsmDec.h b/media/codec2/components/gsm/C2SoftGsmDec.h
index 2b209fe..edd273b 100644
--- a/media/codec2/components/gsm/C2SoftGsmDec.h
+++ b/media/codec2/components/gsm/C2SoftGsmDec.h
@@ -19,10 +19,7 @@
#include <SimpleC2Component.h>
-
-extern "C" {
- #include "gsm.h"
-}
+#include "gsm.h"
namespace android {
diff --git a/media/codec2/components/hevc/C2SoftHevcDec.cpp b/media/codec2/components/hevc/C2SoftHevcDec.cpp
index 6db4387..e4b911d 100644
--- a/media/codec2/components/hevc/C2SoftHevcDec.cpp
+++ b/media/codec2/components/hevc/C2SoftHevcDec.cpp
@@ -497,7 +497,7 @@
status_t C2SoftHevcDec::initDecoder() {
if (OK != createDecoder()) return UNKNOWN_ERROR;
mNumCores = MIN(getCpuCoreCount(), MAX_NUM_CORES);
- mStride = ALIGN128(mWidth);
+ mStride = ALIGN32(mWidth);
mSignalledError = false;
resetPlugin();
(void) setNumCores();
@@ -515,10 +515,20 @@
size_t inSize,
uint32_t tsMarker) {
uint32_t displayStride = mStride;
+ if (outBuffer) {
+ C2PlanarLayout layout;
+ layout = outBuffer->layout();
+ displayStride = layout.planes[C2PlanarLayout::PLANE_Y].rowInc;
+ }
uint32_t displayHeight = mHeight;
size_t lumaSize = displayStride * displayHeight;
size_t chromaSize = lumaSize >> 2;
+ if (mStride != displayStride) {
+ mStride = displayStride;
+ if (OK != setParams(mStride, IVD_DECODE_FRAME)) return false;
+ }
+
ps_decode_ip->u4_size = sizeof(ivd_video_decode_ip_t);
ps_decode_ip->e_cmd = IVD_CMD_VIDEO_DECODE;
if (inBuffer) {
@@ -752,24 +762,21 @@
ALOGE("not supposed to be here, invalid decoder context");
return C2_CORRUPTED;
}
- if (mStride != ALIGN128(mWidth)) {
- mStride = ALIGN128(mWidth);
- if (OK != setParams(mStride, IVD_DECODE_FRAME)) return C2_CORRUPTED;
- }
if (mOutBlock &&
- (mOutBlock->width() != mStride || mOutBlock->height() != mHeight)) {
+ (mOutBlock->width() != ALIGN32(mWidth) || mOutBlock->height() != mHeight)) {
mOutBlock.reset();
}
if (!mOutBlock) {
uint32_t format = HAL_PIXEL_FORMAT_YV12;
C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
- c2_status_t err = pool->fetchGraphicBlock(mStride, mHeight, format, usage, &mOutBlock);
+ c2_status_t err =
+ pool->fetchGraphicBlock(ALIGN32(mWidth), mHeight, format, usage, &mOutBlock);
if (err != C2_OK) {
ALOGE("fetchGraphicBlock for Output failed with status %d", err);
return err;
}
ALOGV("provided (%dx%d) required (%dx%d)",
- mOutBlock->width(), mOutBlock->height(), mStride, mHeight);
+ mOutBlock->width(), mOutBlock->height(), ALIGN32(mWidth), mHeight);
}
return C2_OK;
@@ -904,7 +911,7 @@
if (0 < s_decode_op.u4_pic_wd && 0 < s_decode_op.u4_pic_ht) {
if (mHeaderDecoded == false) {
mHeaderDecoded = true;
- setParams(ALIGN128(s_decode_op.u4_pic_wd), IVD_DECODE_FRAME);
+ setParams(ALIGN32(s_decode_op.u4_pic_wd), IVD_DECODE_FRAME);
}
if (s_decode_op.u4_pic_wd != mWidth || s_decode_op.u4_pic_ht != mHeight) {
mWidth = s_decode_op.u4_pic_wd;
diff --git a/media/codec2/components/hevc/C2SoftHevcDec.h b/media/codec2/components/hevc/C2SoftHevcDec.h
index aecd101..600d7c1 100644
--- a/media/codec2/components/hevc/C2SoftHevcDec.h
+++ b/media/codec2/components/hevc/C2SoftHevcDec.h
@@ -37,8 +37,7 @@
#define ivdext_ctl_set_num_cores_op_t ihevcd_cxa_ctl_set_num_cores_op_t
#define ivdext_ctl_get_vui_params_ip_t ihevcd_cxa_ctl_get_vui_params_ip_t
#define ivdext_ctl_get_vui_params_op_t ihevcd_cxa_ctl_get_vui_params_op_t
-#define ALIGN64(x) ((((x) + 63) >> 6) << 6)
-#define ALIGN128(x) ((((x) + 127) >> 7) << 7)
+#define ALIGN32(x) ((((x) + 31) >> 5) << 5)
#define MAX_NUM_CORES 4
#define IVDEXT_CMD_CTL_SET_NUM_CORES \
(IVD_CONTROL_API_COMMAND_TYPE_T)IHEVCD_CXA_CMD_CTL_SET_NUM_CORES
diff --git a/media/codec2/components/hevc/C2SoftHevcEnc.cpp b/media/codec2/components/hevc/C2SoftHevcEnc.cpp
index 19ccbf9..c2d2540 100644
--- a/media/codec2/components/hevc/C2SoftHevcEnc.cpp
+++ b/media/codec2/components/hevc/C2SoftHevcEnc.cpp
@@ -628,6 +628,7 @@
mComplexity = mIntf->getComplexity_l();
mQuality = mIntf->getQuality_l();
mGop = mIntf->getGop_l();
+ mRequestSync = mIntf->getRequestSync_l();
}
c2_status_t status = initEncParams();
@@ -956,7 +957,7 @@
}
}
- // handle dynamic config parameters
+ // handle dynamic bitrate change
{
IntfImpl::Lock lock = mIntf->lock();
std::shared_ptr<C2StreamBitrateInfo::output> bitrate = mIntf->getBitrate_l();
@@ -983,6 +984,26 @@
work->workletsProcessed = 1u;
return;
}
+ // handle request key frame
+ {
+ IntfImpl::Lock lock = mIntf->lock();
+ std::shared_ptr<C2StreamRequestSyncFrameTuning::output> requestSync;
+ requestSync = mIntf->getRequestSync_l();
+ lock.unlock();
+ if (requestSync != mRequestSync) {
+ // we can handle IDR immediately
+ if (requestSync->value) {
+ // unset request
+ C2StreamRequestSyncFrameTuning::output clearSync(0u, C2_FALSE);
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+ mIntf->config({ &clearSync }, C2_MAY_BLOCK, &failures);
+ ALOGV("Got sync request");
+ //Force this as an IDR frame
+ s_encode_ip.i4_force_idr_flag = 1;
+ }
+ mRequestSync = requestSync;
+ }
+ }
uint64_t timeDelay = 0;
uint64_t timeTaken = 0;
diff --git a/media/codec2/components/hevc/C2SoftHevcEnc.h b/media/codec2/components/hevc/C2SoftHevcEnc.h
index 140b4a9..5ea4602 100644
--- a/media/codec2/components/hevc/C2SoftHevcEnc.h
+++ b/media/codec2/components/hevc/C2SoftHevcEnc.h
@@ -88,6 +88,7 @@
std::shared_ptr<C2StreamComplexityTuning::output> mComplexity;
std::shared_ptr<C2StreamQualityTuning::output> mQuality;
std::shared_ptr<C2StreamGopTuning::output> mGop;
+ std::shared_ptr<C2StreamRequestSyncFrameTuning::output> mRequestSync;
#ifdef FILE_DUMP_ENABLE
char mInFile[200];
char mOutFile[200];
diff --git a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp
index df7b403..82cae7c 100644
--- a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp
+++ b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp
@@ -564,7 +564,7 @@
if (OK != createDecoder()) return UNKNOWN_ERROR;
mNumCores = MIN(getCpuCoreCount(), MAX_NUM_CORES);
- mStride = ALIGN64(mWidth);
+ mStride = ALIGN32(mWidth);
mSignalledError = false;
resetPlugin();
(void) setNumCores();
@@ -582,10 +582,20 @@
size_t inSize,
uint32_t tsMarker) {
uint32_t displayStride = mStride;
+ if (outBuffer) {
+ C2PlanarLayout layout;
+ layout = outBuffer->layout();
+ displayStride = layout.planes[C2PlanarLayout::PLANE_Y].rowInc;
+ }
uint32_t displayHeight = mHeight;
size_t lumaSize = displayStride * displayHeight;
size_t chromaSize = lumaSize >> 2;
+ if (mStride != displayStride) {
+ mStride = displayStride;
+ if (OK != setParams(mStride)) return false;
+ }
+
ps_decode_ip->u4_size = sizeof(ivd_video_decode_ip_t);
ps_decode_ip->e_cmd = IVD_CMD_VIDEO_DECODE;
if (inBuffer) {
@@ -826,24 +836,21 @@
ALOGE("not supposed to be here, invalid decoder context");
return C2_CORRUPTED;
}
- if (mStride != ALIGN64(mWidth)) {
- mStride = ALIGN64(mWidth);
- if (OK != setParams(mStride)) return C2_CORRUPTED;
- }
if (mOutBlock &&
- (mOutBlock->width() != mStride || mOutBlock->height() != mHeight)) {
+ (mOutBlock->width() != ALIGN32(mWidth) || mOutBlock->height() != mHeight)) {
mOutBlock.reset();
}
if (!mOutBlock) {
uint32_t format = HAL_PIXEL_FORMAT_YV12;
C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
- c2_status_t err = pool->fetchGraphicBlock(mStride, mHeight, format, usage, &mOutBlock);
+ c2_status_t err =
+ pool->fetchGraphicBlock(ALIGN32(mWidth), mHeight, format, usage, &mOutBlock);
if (err != C2_OK) {
ALOGE("fetchGraphicBlock for Output failed with status %d", err);
return err;
}
ALOGV("provided (%dx%d) required (%dx%d)",
- mOutBlock->width(), mOutBlock->height(), mStride, mHeight);
+ mOutBlock->width(), mOutBlock->height(), ALIGN32(mWidth), mHeight);
}
return C2_OK;
diff --git a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.h b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.h
index 65d3b87..fd66304a 100644
--- a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.h
+++ b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.h
@@ -37,7 +37,7 @@
#define ivdext_ctl_set_num_cores_op_t impeg2d_ctl_set_num_cores_op_t
#define ivdext_ctl_get_seq_info_ip_t impeg2d_ctl_get_seq_info_ip_t
#define ivdext_ctl_get_seq_info_op_t impeg2d_ctl_get_seq_info_op_t
-#define ALIGN64(x) ((((x) + 63) >> 6) << 6)
+#define ALIGN32(x) ((((x) + 31) >> 5) << 5)
#define MAX_NUM_CORES 4
#define IVDEXT_CMD_CTL_SET_NUM_CORES \
(IVD_CONTROL_API_COMMAND_TYPE_T)IMPEG2D_CMD_CTL_SET_NUM_CORES
diff --git a/media/codec2/components/vpx/C2SoftVpxDec.cpp b/media/codec2/components/vpx/C2SoftVpxDec.cpp
index a759e8f..fbc9c8a 100644
--- a/media/codec2/components/vpx/C2SoftVpxDec.cpp
+++ b/media/codec2/components/vpx/C2SoftVpxDec.cpp
@@ -783,7 +783,13 @@
}
}
- CHECK(img->fmt == VPX_IMG_FMT_I420 || img->fmt == VPX_IMG_FMT_I42016);
+ if(img->fmt != VPX_IMG_FMT_I420 && img->fmt != VPX_IMG_FMT_I42016) {
+ ALOGE("img->fmt %d not supported", img->fmt);
+ mSignalledError = true;
+ work->workletsProcessed = 1u;
+ work->result = C2_CORRUPTED;
+ return false;
+ }
std::shared_ptr<C2GraphicBlock> block;
uint32_t format = HAL_PIXEL_FORMAT_YV12;
diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp
index 78ddd6d..1cbcb8d 100644
--- a/media/codec2/sfplugin/CCodec.cpp
+++ b/media/codec2/sfplugin/CCodec.cpp
@@ -1760,15 +1760,18 @@
// move all info into output-stream #0 domain
updates.emplace_back(C2Param::CopyAsStream(*info, true /* output */, stream));
}
- for (const C2ConstGraphicBlock &block : buf->data().graphicBlocks()) {
+
+ const std::vector<C2ConstGraphicBlock> blocks = buf->data().graphicBlocks();
+ // for now only do the first block
+ if (!blocks.empty()) {
// ALOGV("got output buffer with crop %u,%u+%u,%u and size %u,%u",
// block.crop().left, block.crop().top,
// block.crop().width, block.crop().height,
// block.width(), block.height());
+ const C2ConstGraphicBlock &block = blocks[0];
updates.emplace_back(new C2StreamCropRectInfo::output(stream, block.crop()));
updates.emplace_back(new C2StreamPictureSizeInfo::output(
stream, block.crop().width, block.crop().height));
- break; // for now only do the first block
}
++stream;
}
@@ -1780,7 +1783,7 @@
// copy standard infos to graphic buffers if not already present (otherwise, we
// may overwrite the actual intermediate value with a final value)
stream = 0;
- const static std::vector<C2Param::Index> stdGfxInfos = {
+ const static C2Param::Index stdGfxInfos[] = {
C2StreamRotationInfo::output::PARAM_TYPE,
C2StreamColorAspectsInfo::output::PARAM_TYPE,
C2StreamDataSpaceInfo::output::PARAM_TYPE,
diff --git a/media/codec2/sfplugin/Codec2InfoBuilder.cpp b/media/codec2/sfplugin/Codec2InfoBuilder.cpp
index 6b75eba..d7f38c5 100644
--- a/media/codec2/sfplugin/Codec2InfoBuilder.cpp
+++ b/media/codec2/sfplugin/Codec2InfoBuilder.cpp
@@ -319,10 +319,11 @@
// Obtain Codec2Client
std::vector<Traits> traits = Codec2Client::ListComponents();
- // parse APEX XML first, followed by vendor XML
+ // parse APEX XML first, followed by vendor XML.
+ // Note: APEX XML names do not depend on ro.media.xml_variant.* properties.
MediaCodecsXmlParser parser;
parser.parseXmlFilesInSearchDirs(
- parser.getDefaultXmlNames(),
+ { "media_codecs.xml", "media_codecs_performance.xml" },
{ "/apex/com.android.media.swcodec/etc" });
// TODO: remove these c2-specific files once product moved to default file names
diff --git a/media/extractors/fuzzers/Android.bp b/media/extractors/fuzzers/Android.bp
new file mode 100644
index 0000000..5fb8155
--- /dev/null
+++ b/media/extractors/fuzzers/Android.bp
@@ -0,0 +1,83 @@
+/******************************************************************************
+ *
+ * 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.
+ *
+ *****************************************************************************
+ * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
+ */
+
+cc_library {
+ name: "libextractorfuzzerbase",
+
+ srcs: [
+ "ExtractorFuzzerBase.cpp",
+ ],
+
+ local_include_dirs: [
+ "include",
+ ],
+
+ export_include_dirs: [
+ "include",
+ ],
+
+ static_libs: [
+ "liblog",
+ "libstagefright_foundation",
+ "libmedia",
+ ],
+
+ shared_libs: [
+ "libutils",
+ "libbinder",
+ "libmediandk",
+ ],
+
+ /* GETEXTRACTORDEF is not defined as extractor library is not linked in the
+ * base class. It will be included when the extractor fuzzer binary is
+ * generated.
+ */
+ allow_undefined_symbols: true,
+}
+
+cc_fuzz {
+ name: "mp4_extractor_fuzzer",
+
+ srcs: [
+ "mp4_extractor_fuzzer.cpp",
+ ],
+
+ include_dirs: [
+ "frameworks/av/media/extractors/mp4",
+ ],
+
+ static_libs: [
+ "liblog",
+ "libstagefright_foundation",
+ "libmedia",
+ "libextractorfuzzerbase",
+ "libstagefright_id3",
+ "libstagefright_esds",
+ "libmp4extractor",
+ ],
+
+ shared_libs: [
+ "libutils",
+ "libmediandk",
+ "libbinder",
+ ],
+
+ dictionary: "mp4_extractor_fuzzer.dict",
+}
diff --git a/media/extractors/fuzzers/ExtractorFuzzerBase.cpp b/media/extractors/fuzzers/ExtractorFuzzerBase.cpp
new file mode 100644
index 0000000..cbd6395
--- /dev/null
+++ b/media/extractors/fuzzers/ExtractorFuzzerBase.cpp
@@ -0,0 +1,118 @@
+/*
+ * 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 "ExtractorFuzzerBase"
+#include <utils/Log.h>
+
+#include "ExtractorFuzzerBase.h"
+
+using namespace android;
+
+bool ExtractorFuzzerBase::setDataSource(const uint8_t* data, size_t size) {
+ if ((!data) || (size == 0)) {
+ return false;
+ }
+ mBufferSource = new BufferSource(data, size);
+ mDataSource = reinterpret_cast<DataSource*>(mBufferSource.get());
+ if (!mDataSource) {
+ return false;
+ }
+ return true;
+}
+
+bool ExtractorFuzzerBase::getExtractorDef() {
+ float confidence;
+ void* meta = nullptr;
+ FreeMetaFunc freeMeta = nullptr;
+
+ ExtractorDef extractorDef = GETEXTRACTORDEF();
+ if (extractorDef.def_version == EXTRACTORDEF_VERSION_NDK_V1) {
+ extractorDef.u.v2.sniff(mDataSource->wrap(), &confidence, &meta, &freeMeta);
+ } else if (extractorDef.def_version == EXTRACTORDEF_VERSION_NDK_V2) {
+ extractorDef.u.v3.sniff(mDataSource->wrap(), &confidence, &meta, &freeMeta);
+ }
+
+ if (meta != nullptr && freeMeta != nullptr) {
+ freeMeta(meta);
+ }
+
+ return true;
+}
+
+bool ExtractorFuzzerBase::extractTracks() {
+ MediaBufferGroup* bufferGroup = new MediaBufferGroup();
+ if (!bufferGroup) {
+ return false;
+ }
+ for (size_t trackIndex = 0; trackIndex < mExtractor->countTracks(); ++trackIndex) {
+ MediaTrackHelper* track = mExtractor->getTrack(trackIndex);
+ if (!track) {
+ continue;
+ }
+ extractTrack(track, bufferGroup);
+ delete track;
+ }
+ delete bufferGroup;
+ return true;
+}
+
+void ExtractorFuzzerBase::extractTrack(MediaTrackHelper* track, MediaBufferGroup* bufferGroup) {
+ CMediaTrack* cTrack = wrap(track);
+ if (!cTrack) {
+ return;
+ }
+
+ media_status_t status = cTrack->start(track, bufferGroup->wrap());
+ if (status != AMEDIA_OK) {
+ free(cTrack);
+ return;
+ }
+
+ do {
+ MediaBufferHelper* buffer = nullptr;
+ status = track->read(&buffer);
+ if (buffer) {
+ buffer->release();
+ }
+ } while (status == AMEDIA_OK);
+
+ cTrack->stop(track);
+ free(cTrack);
+}
+
+bool ExtractorFuzzerBase::getTracksMetadata() {
+ AMediaFormat* format = AMediaFormat_new();
+ uint32_t flags = MediaExtractorPluginHelper::kIncludeExtensiveMetaData;
+
+ for (size_t trackIndex = 0; trackIndex < mExtractor->countTracks(); ++trackIndex) {
+ mExtractor->getTrackMetaData(format, trackIndex, flags);
+ }
+
+ AMediaFormat_delete(format);
+ return true;
+}
+
+bool ExtractorFuzzerBase::getMetadata() {
+ AMediaFormat* format = AMediaFormat_new();
+ mExtractor->getMetaData(format);
+ AMediaFormat_delete(format);
+ return true;
+}
+
+void ExtractorFuzzerBase::setDataSourceFlags(uint32_t flags) {
+ mBufferSource->setFlags(flags);
+}
diff --git a/media/extractors/fuzzers/README.md b/media/extractors/fuzzers/README.md
new file mode 100644
index 0000000..a6c17c0
--- /dev/null
+++ b/media/extractors/fuzzers/README.md
@@ -0,0 +1,54 @@
+# Fuzzer for extractors
+
+## Table of contents
+1. [libextractorfuzzerbase](#ExtractorFuzzerBase)
+2. [libmp4extractor](#mp4ExtractorFuzzer)
+
+# <a name="ExtractorFuzzerBase"></a> Fuzzer for libextractorfuzzerbase
+All the extractors have a common API - creating a data source, extraction
+of all the tracks, etc. These common APIs have been abstracted in a base class
+called `ExtractorFuzzerBase` to ensure code is reused between fuzzer plugins.
+
+Additionally, `ExtractorFuzzerBase` also has support for memory based buffer
+`BufferSource` since the fuzzing engine feeds data using memory buffers and
+usage of standard data source objects like FileSource, HTTPSource, etc. is
+not feasible.
+
+# <a name="mp4ExtractorFuzzer"></a> Fuzzer for libmp4extractor
+
+## Plugin Design Considerations
+The fuzzer plugin for MP4 extractor uses the `ExtractorFuzzerBase` class and
+implements only the `createExtractor` to create the MP4 extractor class.
+
+##### Maximize code coverage
+Dict file (dictionary file) is created for MP4 to ensure that the required MP4
+atoms are present in every input file that goes to the fuzzer.
+This ensures that larger code gets covered as a range of MP4 atoms will be
+present in the input data.
+
+
+## Build
+
+This describes steps to build mp4_extractor_fuzzer binary.
+
+### Android
+
+#### Steps to build
+Build the fuzzer
+```
+ $ mm -j$(nproc) mp4_extractor_fuzzer
+```
+
+#### Steps to run
+Create a directory CORPUS_DIR and copy some MP4 files to that folder
+Push this directory to device.
+
+To run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/mp4_extractor_fuzzer/mp4_extractor_fuzzer CORPUS_DIR
+```
+
+## References:
+ * http://llvm.org/docs/LibFuzzer.html
+ * https://github.com/google/oss-fuzz
diff --git a/media/extractors/fuzzers/include/ExtractorFuzzerBase.h b/media/extractors/fuzzers/include/ExtractorFuzzerBase.h
new file mode 100644
index 0000000..abf362b
--- /dev/null
+++ b/media/extractors/fuzzers/include/ExtractorFuzzerBase.h
@@ -0,0 +1,130 @@
+/******************************************************************************
+ *
+ * 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.
+ *
+ *****************************************************************************
+ * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
+ */
+
+#ifndef __EXTRACTOR_FUZZER_BASE_H__
+#define __EXTRACTOR_FUZZER_BASE_H__
+
+#include <media/DataSource.h>
+#include <media/MediaExtractorPluginHelper.h>
+#include <media/stagefright/MediaBufferGroup.h>
+
+extern "C" {
+android::ExtractorDef GETEXTRACTORDEF();
+}
+
+namespace android {
+
+class ExtractorFuzzerBase {
+ public:
+ ExtractorFuzzerBase() = default;
+ virtual ~ExtractorFuzzerBase() {
+ if (mExtractor) {
+ delete mExtractor;
+ mExtractor = nullptr;
+ }
+ if (mBufferSource) {
+ mBufferSource.clear();
+ mBufferSource = nullptr;
+ }
+ }
+
+ /** Function to create the media extractor component.
+ * To be implemented by the derived class.
+ */
+ virtual bool createExtractor() = 0;
+
+ /** Parent class functions to be reused by derived class.
+ * These are common for all media extractor components.
+ */
+ bool setDataSource(const uint8_t* data, size_t size);
+
+ bool getExtractorDef();
+
+ bool extractTracks();
+
+ bool getMetadata();
+
+ bool getTracksMetadata();
+
+ void setDataSourceFlags(uint32_t flags);
+
+ protected:
+ class BufferSource : public DataSource {
+ public:
+ BufferSource(const uint8_t* data, size_t length) : mData(data), mLength(length) {}
+ virtual ~BufferSource() { mData = nullptr; }
+
+ void setFlags(uint32_t flags) { mFlags = flags; }
+
+ uint32_t flags() { return mFlags; }
+
+ status_t initCheck() const { return mData != nullptr ? OK : NO_INIT; }
+
+ ssize_t readAt(off64_t offset, void* data, size_t size) {
+ if (!mData) {
+ return NO_INIT;
+ }
+
+ Mutex::Autolock autoLock(mLock);
+ if ((offset >= static_cast<off64_t>(mLength)) || (offset < 0)) {
+ return 0; // read beyond bounds.
+ }
+ size_t numAvailable = mLength - static_cast<size_t>(offset);
+ if (size > numAvailable) {
+ size = numAvailable;
+ }
+ return readAt_l(offset, data, size);
+ }
+
+ status_t getSize(off64_t* size) {
+ if (!mData) {
+ return NO_INIT;
+ }
+
+ Mutex::Autolock autoLock(mLock);
+ *size = static_cast<off64_t>(mLength);
+ return OK;
+ }
+
+ protected:
+ ssize_t readAt_l(off64_t offset, void* data, size_t size) {
+ void* result = memcpy(data, mData + offset, size);
+ return result != nullptr ? size : 0;
+ }
+
+ const uint8_t* mData = nullptr;
+ size_t mLength = 0;
+ Mutex mLock;
+ uint32_t mFlags = 0;
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(BufferSource);
+ };
+
+ sp<BufferSource> mBufferSource;
+ DataSource* mDataSource = nullptr;
+ MediaExtractorPluginHelper* mExtractor = nullptr;
+
+ virtual void extractTrack(MediaTrackHelper* track, MediaBufferGroup* bufferGroup);
+};
+
+} // namespace android
+
+#endif // __EXTRACTOR_FUZZER_BASE_H__
diff --git a/media/extractors/fuzzers/mp4_extractor_fuzzer.cpp b/media/extractors/fuzzers/mp4_extractor_fuzzer.cpp
new file mode 100644
index 0000000..d2cc133
--- /dev/null
+++ b/media/extractors/fuzzers/mp4_extractor_fuzzer.cpp
@@ -0,0 +1,64 @@
+/******************************************************************************
+ *
+ * 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.
+ *
+ *****************************************************************************
+ * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
+ */
+
+#include "ExtractorFuzzerBase.h"
+
+#include "MPEG4Extractor.h"
+#include "SampleTable.h"
+
+using namespace android;
+
+class MP4Extractor : public ExtractorFuzzerBase {
+ public:
+ MP4Extractor() = default;
+ ~MP4Extractor() = default;
+
+ bool createExtractor();
+};
+
+bool MP4Extractor::createExtractor() {
+ mExtractor = new MPEG4Extractor(new DataSourceHelper(mDataSource->wrap()));
+ if (!mExtractor) {
+ return false;
+ }
+ mExtractor->name();
+ setDataSourceFlags(DataSourceBase::kWantsPrefetching | DataSourceBase::kIsCachingDataSource);
+ return true;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ if ((!data) || (size == 0)) {
+ return 0;
+ }
+ MP4Extractor* extractor = new MP4Extractor();
+ if (!extractor) {
+ return 0;
+ }
+ if (extractor->setDataSource(data, size)) {
+ if (extractor->createExtractor()) {
+ extractor->getExtractorDef();
+ extractor->getMetadata();
+ extractor->extractTracks();
+ extractor->getTracksMetadata();
+ }
+ }
+ delete extractor;
+ return 0;
+}
diff --git a/media/extractors/fuzzers/mp4_extractor_fuzzer.dict b/media/extractors/fuzzers/mp4_extractor_fuzzer.dict
new file mode 100644
index 0000000..42a86f3
--- /dev/null
+++ b/media/extractors/fuzzers/mp4_extractor_fuzzer.dict
@@ -0,0 +1,248 @@
+# MP4 Atoms/Boxes
+kw1="ftyp"
+kw2="free"
+kw3="mdat"
+kw4="moov"
+kw5="mvhd"
+kw6="trak"
+kw7="tkhd"
+kw8="edts"
+kw9="elst"
+kw10="mdia"
+kw11="mdhd"
+kw12="hdlr"
+kw13="minf"
+kw14="vmhd"
+kw15="dinf"
+kw16="dref"
+kw17="url "
+kw18="stbl"
+kw19="stsd"
+kw20="avc1"
+kw21="avcC"
+kw22="stts"
+kw23="stss"
+kw24="ctts"
+kw25="stsc"
+kw26="stsz"
+kw27="stco"
+kw28="mp4a"
+kw29="esds"
+kw30="udta"
+kw31="meta"
+kw32="ilst"
+kw33="samr"
+kw34="sawb"
+kw35="ec-3"
+kw36="mp4v"
+kw37="s263"
+kw38="h263"
+kw39="H263"
+kw40="avc1"
+kw41="hvc1"
+kw42="hev1"
+kw43="ac-4"
+kw44="Opus"
+kw45="twos"
+kw46="sowt"
+kw47="alac"
+kw48="fLaC"
+kw49="av01"
+kw50=".mp3"
+kw51="keys"
+kw52="cprt"
+kw53="covr"
+kw54="mvex"
+kw55="moof"
+kw56="traf"
+kw57="mfra"
+kw58="sinf"
+kw59="schi"
+kw60="wave"
+kw61="schm"
+kw62="cbc1"
+kw63="cbcs"
+kw64="cenc"
+kw65="cens"
+kw66="frma"
+kw67="tenc"
+kw68="tref"
+kw69="thmb"
+kw70="pssh"
+kw71="mett"
+kw72="enca"
+kw73="encv"
+kw74="co64"
+kw75="stz2"
+kw76="\251xyz"
+kw77="btrt"
+kw78="hvcC"
+kw79="av1C"
+kw80="d263"
+kw81="iloc"
+kw82="iinf"
+kw83="iprp"
+kw84="pitm"
+kw85="idat"
+kw86="iref"
+kw87="ipro"
+kw88="mean"
+kw89="name"
+kw90="data"
+kw91="mehd"
+kw92="text"
+kw93="sbtl"
+kw94="trex"
+kw95="tx3g"
+kw96="colr"
+kw97="titl"
+kw98="perf"
+kw99="auth"
+kw100="gnre"
+kw101="albm"
+kw102="yrrc"
+kw103="ID32"
+kw104="----"
+kw105="sidx"
+kw106="ac-3"
+kw107="qt "
+kw108="mif1"
+kw109="heic"
+kw110="dac4"
+kw111="dec3"
+kw112="dac3"
+kw113="\251alb"
+kw114="\251ART"
+kw115="aART"
+kw116="\251day"
+kw117="\251nam"
+kw118="\251wrt"
+kw119="\251gen"
+kw120="cpil"
+kw121="trkn"
+kw122="disk"
+kw123="nclx"
+kw124="nclc"
+kw125="tfhd"
+kw126="trun"
+kw127="saiz"
+kw128="saio"
+kw129="senc"
+kw130="isom"
+kw131="iso2"
+kw132="3gp4"
+kw133="mp41"
+kw134="mp42"
+kw135="dash"
+kw136="nvr1"
+kw137="MSNV"
+kw138="wmf "
+kw139="3g2a"
+kw140="3g2b"
+kw141="msf1"
+kw142="hevc"
+kw143="pdin"
+kw144="trgr"
+kw145="smhd"
+kw146="hmhd"
+kw147="nmhd"
+kw148="cslg"
+kw149="stsh"
+kw150="padb"
+kw151="stdp"
+kw152="sdtp"
+kw153="sbgp"
+kw154="sgpd"
+kw155="subs"
+kw156="leva"
+kw157="mfhd"
+kw158="tfdt"
+kw159="tfra"
+kw160="mfro"
+kw161="skip"
+kw162="tsel"
+kw163="strk"
+kw164="stri"
+kw165="strd"
+kw166="xml "
+kw167="bxml"
+kw168="fiin"
+kw169="paen"
+kw170="fire"
+kw171="fpar"
+kw172="fecr"
+kw173="segr"
+kw174="gitn"
+kw175="meco"
+kw176="mere"
+kw177="styp"
+kw178="ssix"
+kw179="prft"
+kw180="hint"
+kw181="cdsc"
+kw182="hind"
+kw183="vdep"
+kw184="vplx"
+kw185="msrc"
+kw186="urn "
+kw187="enct"
+kw188="encs"
+kw189="rinf"
+kw190="srpp"
+kw191="stsg"
+kw192="stvi"
+kw193="tims"
+kw194="tsro"
+kw195="snro"
+kw196="rtp "
+kw197="srtp"
+kw198="rtpo"
+kw199="hnti"
+kw200="sdp "
+kw201="trpy"
+kw202="nump"
+kw203="tpyl"
+kw204="totl"
+kw205="npck"
+kw206="tpay"
+kw207="maxr"
+kw208="dmed"
+kw209="dimm"
+kw210="drep"
+kw211="tmin"
+kw212="tmax"
+kw213="pmax"
+kw214="dmax"
+kw215="payt"
+kw216="fdp "
+kw217="fdsa"
+kw218="fdpa"
+kw219="extr"
+kw220="feci"
+kw221="rm2t"
+kw222="sm2t"
+kw223="tPAT"
+kw224="tPMT"
+kw225="tOD "
+kw226="tsti"
+kw227="istm"
+kw228="pm2t"
+kw229="rrtp"
+kw230="rssr"
+kw231="rscr"
+kw232="rsrp"
+kw233="rssr"
+kw234="ccid"
+kw235="sroc"
+kw236="prtp"
+kw237="roll"
+kw238="rash"
+kw239="alst"
+kw240="rap "
+kw241="tele"
+kw242="mp71"
+kw243="iso3"
+kw244="iso4"
+kw245="iso5"
+kw246="resv"
+kw247="iso6"
diff --git a/media/extractors/mkv/MatroskaExtractor.cpp b/media/extractors/mkv/MatroskaExtractor.cpp
index 23022e4..2459bf5 100644
--- a/media/extractors/mkv/MatroskaExtractor.cpp
+++ b/media/extractors/mkv/MatroskaExtractor.cpp
@@ -1879,13 +1879,12 @@
for(size_t i = 0; i < track->GetContentEncodingCount(); i++) {
const mkvparser::ContentEncoding *encoding = track->GetContentEncodingByIndex(i);
- for(size_t j = 0; j < encoding->GetEncryptionCount(); j++) {
+ if (encoding->GetEncryptionCount() > 0) {
const mkvparser::ContentEncoding::ContentEncryption *encryption;
- encryption = encoding->GetEncryptionByIndex(j);
+ encryption = encoding->GetEncryptionByIndex(0);
AMediaFormat_setBuffer(trackInfo->mMeta,
AMEDIAFORMAT_KEY_CRYPTO_KEY, encryption->key_id, encryption->key_id_len);
trackInfo->mEncrypted = true;
- break;
}
for(size_t j = 0; j < encoding->GetCompressionCount(); j++) {
diff --git a/media/extractors/tests/ExtractorUnitTest.cpp b/media/extractors/tests/ExtractorUnitTest.cpp
index fca66a4..f18a7dc 100644
--- a/media/extractors/tests/ExtractorUnitTest.cpp
+++ b/media/extractors/tests/ExtractorUnitTest.cpp
@@ -20,6 +20,7 @@
#include <datasource/FileSource.h>
#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaCodecConstants.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaDataUtils.h>
#include <media/stagefright/foundation/OpusHeader.h>
@@ -47,10 +48,42 @@
constexpr int32_t kAudioDefaultSampleDuration = 20000; // 20ms
constexpr int32_t kRandomSeekToleranceUs = 2 * kAudioDefaultSampleDuration; // 40 ms;
constexpr int32_t kRandomSeed = 700;
+constexpr int32_t kUndefined = -1;
+
+// LookUpTable of clips and metadata for component testing
+static const struct InputData {
+ string mime;
+ string inputFile;
+ int32_t firstParam;
+ int32_t secondParam;
+ int32_t profile;
+ int32_t frameRate;
+} kInputData[] = {
+ {MEDIA_MIMETYPE_AUDIO_AAC, "test_mono_44100Hz_aac.aac", 44100, 1, AACObjectLC, kUndefined},
+ {MEDIA_MIMETYPE_AUDIO_AMR_NB, "bbb_mono_8kHz_amrnb.amr", 8000, 1, kUndefined, kUndefined},
+ {MEDIA_MIMETYPE_AUDIO_AMR_WB, "bbb_mono_16kHz_amrwb.amr", 16000, 1, kUndefined, kUndefined},
+ {MEDIA_MIMETYPE_AUDIO_VORBIS, "bbb_stereo_48kHz_vorbis.ogg", 48000, 2, kUndefined,
+ kUndefined},
+ {MEDIA_MIMETYPE_AUDIO_MSGSM, "test_mono_8kHz_gsm.wav", 8000, 1, kUndefined, kUndefined},
+ {MEDIA_MIMETYPE_AUDIO_RAW, "bbb_stereo_48kHz_flac.flac", 48000, 2, kUndefined, kUndefined},
+ {MEDIA_MIMETYPE_AUDIO_OPUS, "test_stereo_48kHz_opus.opus", 48000, 2, kUndefined,
+ kUndefined},
+ {MEDIA_MIMETYPE_AUDIO_MPEG, "bbb_stereo_48kHz_mp3.mp3", 48000, 2, kUndefined, kUndefined},
+ {MEDIA_MIMETYPE_AUDIO_RAW, "midi_a.mid", 22050, 2, kUndefined, kUndefined},
+ {MEDIA_MIMETYPE_VIDEO_MPEG2, "bbb_cif_768kbps_30fps_mpeg2.ts", 352, 288, MPEG2ProfileMain,
+ 30},
+ {MEDIA_MIMETYPE_VIDEO_MPEG4, "bbb_cif_768kbps_30fps_mpeg4.mkv", 352, 288,
+ MPEG4ProfileSimple, 30},
+ // Test (b/151677264) for MP4 extractor
+ {MEDIA_MIMETYPE_VIDEO_HEVC, "crowd_508x240_25fps_hevc.mp4", 508, 240, HEVCProfileMain,
+ 25},
+ {MEDIA_MIMETYPE_VIDEO_VP9, "bbb_340x280_30fps_vp9.webm", 340, 280, VP9Profile0, 30},
+ {MEDIA_MIMETYPE_VIDEO_MPEG2, "swirl_144x136_mpeg2.mpg", 144, 136, MPEG2ProfileMain, 12},
+};
static ExtractorUnitTestEnvironment *gEnv = nullptr;
-class ExtractorUnitTest : public ::testing::TestWithParam<pair<string, string>> {
+class ExtractorUnitTest {
public:
ExtractorUnitTest() : mInputFp(nullptr), mDataSource(nullptr), mExtractor(nullptr) {}
@@ -69,7 +102,7 @@
}
}
- virtual void SetUp() override {
+ void setupExtractor(string writerFormat) {
mExtractorName = unknown_comp;
mDisableTest = false;
@@ -78,7 +111,6 @@
{"wav", WAV}, {"mkv", MKV}, {"flac", FLAC}, {"midi", MIDI},
{"mpeg4", MPEG4}, {"mpeg2ts", MPEG2TS}, {"mpeg2ps", MPEG2PS}};
// Find the component type
- string writerFormat = GetParam().first;
if (mapExtractor.find(writerFormat) != mapExtractor.end()) {
mExtractorName = mapExtractor.at(writerFormat);
}
@@ -115,6 +147,30 @@
MediaExtractorPluginHelper *mExtractor;
};
+class ExtractorFunctionalityTest : public ExtractorUnitTest,
+ public ::testing::TestWithParam<pair<string, string>> {
+ public:
+ virtual void SetUp() override { setupExtractor(GetParam().first); }
+};
+
+class ConfigParamTest : public ExtractorUnitTest,
+ public ::testing::TestWithParam<pair<string, int32_t>> {
+ public:
+ virtual void SetUp() override { setupExtractor(GetParam().first); }
+
+ struct configFormat {
+ string mime;
+ int32_t width;
+ int32_t height;
+ int32_t sampleRate;
+ int32_t channelCount;
+ int32_t profile;
+ int32_t frameRate;
+ };
+
+ void getFileProperties(int32_t inputIdx, string &inputFile, configFormat &configParam);
+};
+
int32_t ExtractorUnitTest::setDataSource(string inputFileName) {
mInputFp = fopen(inputFileName.c_str(), "rb");
if (!mInputFp) {
@@ -171,6 +227,27 @@
return 0;
}
+void ConfigParamTest::getFileProperties(int32_t inputIdx, string &inputFile,
+ configFormat &configParam) {
+ if (inputIdx >= sizeof(kInputData) / sizeof(kInputData[0])) {
+ return;
+ }
+ inputFile += kInputData[inputIdx].inputFile;
+ configParam.mime = kInputData[inputIdx].mime;
+ size_t found = configParam.mime.find("audio/");
+ // Check if 'audio/' is present in the begininig of the mime type
+ if (found == 0) {
+ configParam.sampleRate = kInputData[inputIdx].firstParam;
+ configParam.channelCount = kInputData[inputIdx].secondParam;
+ } else {
+ configParam.width = kInputData[inputIdx].firstParam;
+ configParam.height = kInputData[inputIdx].secondParam;
+ }
+ configParam.profile = kInputData[inputIdx].profile;
+ configParam.frameRate = kInputData[inputIdx].frameRate;
+ return;
+}
+
void randomSeekTest(MediaTrackHelper *track, int64_t clipDuration) {
int32_t status = 0;
int32_t seekCount = 0;
@@ -234,7 +311,7 @@
}
}
-TEST_P(ExtractorUnitTest, CreateExtractorTest) {
+TEST_P(ExtractorFunctionalityTest, CreateExtractorTest) {
if (mDisableTest) return;
ALOGV("Checks if a valid extractor is created for a given input file");
@@ -256,7 +333,7 @@
AMediaFormat_delete(format);
}
-TEST_P(ExtractorUnitTest, ExtractorTest) {
+TEST_P(ExtractorFunctionalityTest, ExtractorTest) {
if (mDisableTest) return;
ALOGV("Validates %s Extractor for a given input file", GetParam().first.c_str());
@@ -306,7 +383,7 @@
}
}
-TEST_P(ExtractorUnitTest, MetaDataComparisonTest) {
+TEST_P(ExtractorFunctionalityTest, MetaDataComparisonTest) {
if (mDisableTest) return;
ALOGV("Validates Extractor's meta data for a given input file");
@@ -381,7 +458,7 @@
AMediaFormat_delete(extractorFormat);
}
-TEST_P(ExtractorUnitTest, MultipleStartStopTest) {
+TEST_P(ExtractorFunctionalityTest, MultipleStartStopTest) {
if (mDisableTest) return;
ALOGV("Test %s extractor for multiple start and stop calls", GetParam().first.c_str());
@@ -423,7 +500,7 @@
}
}
-TEST_P(ExtractorUnitTest, SeekTest) {
+TEST_P(ExtractorFunctionalityTest, SeekTest) {
if (mDisableTest) return;
ALOGV("Validates %s Extractor behaviour for different seek modes", GetParam().first.c_str());
@@ -586,7 +663,94 @@
seekablePoints.clear();
}
-INSTANTIATE_TEST_SUITE_P(ExtractorUnitTestAll, ExtractorUnitTest,
+// This test validates config params for a given input file.
+// For this test we only take single track files since the focus of this test is
+// to validate the file properties reported by Extractor and not multi-track behavior
+TEST_P(ConfigParamTest, ConfigParamValidation) {
+ if (mDisableTest) return;
+
+ ALOGV("Validates %s Extractor for input's file properties", GetParam().first.c_str());
+ string inputFileName = gEnv->getRes();
+ int32_t inputFileIdx = GetParam().second;
+ configFormat configParam;
+ getFileProperties(inputFileIdx, inputFileName, configParam);
+
+ int32_t status = setDataSource(inputFileName);
+ ASSERT_EQ(status, 0) << "SetDataSource failed for " << GetParam().first << "extractor";
+
+ status = createExtractor();
+ ASSERT_EQ(status, 0) << "Extractor creation failed for " << GetParam().first << "extractor";
+
+ int32_t numTracks = mExtractor->countTracks();
+ ASSERT_GT(numTracks, 0) << "Extractor didn't find any track for the given clip";
+
+ MediaTrackHelper *track = mExtractor->getTrack(0);
+ ASSERT_NE(track, nullptr) << "Failed to get track for index 0";
+
+ AMediaFormat *trackFormat = AMediaFormat_new();
+ ASSERT_NE(trackFormat, nullptr) << "AMediaFormat_new returned null format";
+
+ status = track->getFormat(trackFormat);
+ ASSERT_EQ(OK, (media_status_t)status) << "Failed to get track meta data";
+
+ const char *trackMime;
+ bool valueFound = AMediaFormat_getString(trackFormat, AMEDIAFORMAT_KEY_MIME, &trackMime);
+ ASSERT_TRUE(valueFound) << "Mime type not set by extractor";
+ ASSERT_STREQ(configParam.mime.c_str(), trackMime) << "Invalid track format";
+
+ if (!strncmp(trackMime, "audio/", 6)) {
+ int32_t trackSampleRate, trackChannelCount;
+ ASSERT_TRUE(AMediaFormat_getInt32(trackFormat, AMEDIAFORMAT_KEY_CHANNEL_COUNT,
+ &trackChannelCount));
+ ASSERT_TRUE(
+ AMediaFormat_getInt32(trackFormat, AMEDIAFORMAT_KEY_SAMPLE_RATE, &trackSampleRate));
+ ASSERT_EQ(configParam.sampleRate, trackSampleRate) << "SampleRate not as expected";
+ ASSERT_EQ(configParam.channelCount, trackChannelCount) << "ChannelCount not as expected";
+ } else {
+ int32_t trackWidth, trackHeight;
+ ASSERT_TRUE(AMediaFormat_getInt32(trackFormat, AMEDIAFORMAT_KEY_WIDTH, &trackWidth));
+ ASSERT_TRUE(AMediaFormat_getInt32(trackFormat, AMEDIAFORMAT_KEY_HEIGHT, &trackHeight));
+ ASSERT_EQ(configParam.width, trackWidth) << "Width not as expected";
+ ASSERT_EQ(configParam.height, trackHeight) << "Height not as expected";
+
+ if (configParam.frameRate != kUndefined) {
+ int32_t frameRate;
+ ASSERT_TRUE(
+ AMediaFormat_getInt32(trackFormat, AMEDIAFORMAT_KEY_FRAME_RATE, &frameRate));
+ ASSERT_EQ(configParam.frameRate, frameRate) << "frameRate not as expected";
+ }
+ }
+ // validate the profile for the input clip
+ int32_t profile;
+ if (configParam.profile != kUndefined) {
+ if (AMediaFormat_getInt32(trackFormat, AMEDIAFORMAT_KEY_PROFILE, &profile)) {
+ ASSERT_EQ(configParam.profile, profile) << "profile not as expected";
+ } else {
+ ASSERT_TRUE(false) << "profile not returned in extractor";
+ }
+ }
+
+ delete track;
+ AMediaFormat_delete(trackFormat);
+}
+
+INSTANTIATE_TEST_SUITE_P(ConfigParamTestAll, ConfigParamTest,
+ ::testing::Values(make_pair("aac", 0),
+ make_pair("amr", 1),
+ make_pair("amr", 2),
+ make_pair("ogg", 3),
+ make_pair("wav", 4),
+ make_pair("flac", 5),
+ make_pair("ogg", 6),
+ make_pair("mp3", 7),
+ make_pair("midi", 8),
+ make_pair("mpeg2ts", 9),
+ make_pair("mkv", 10),
+ make_pair("mpeg4", 11),
+ make_pair("mkv", 12),
+ make_pair("mpeg2ps", 13)));
+
+INSTANTIATE_TEST_SUITE_P(ExtractorUnitTestAll, ExtractorFunctionalityTest,
::testing::Values(make_pair("aac", "loudsoftaac.aac"),
make_pair("amr", "testamr.amr"),
make_pair("amr", "amrwb.wav"),
diff --git a/media/libaaudio/src/utility/AAudioUtilities.h b/media/libaaudio/src/utility/AAudioUtilities.h
index 76d0457..0dd866d 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.h
+++ b/media/libaaudio/src/utility/AAudioUtilities.h
@@ -21,6 +21,7 @@
#include <functional>
#include <stdint.h>
#include <sys/types.h>
+#include <unistd.h>
#include <utils/Errors.h>
#include <system/audio.h>
diff --git a/media/libaudioclient/AudioEffect.cpp b/media/libaudioclient/AudioEffect.cpp
index 28190ea..2b2506c 100644
--- a/media/libaudioclient/AudioEffect.cpp
+++ b/media/libaudioclient/AudioEffect.cpp
@@ -144,11 +144,13 @@
&mStatus, &mId, &enabled);
if (iEffect == 0 || (mStatus != NO_ERROR && mStatus != ALREADY_EXISTS)) {
- char typeBuffer[64], uuidBuffer[64];
+ char typeBuffer[64] = {}, uuidBuffer[64] = {};
guidToString(type, typeBuffer, sizeof(typeBuffer));
guidToString(uuid, uuidBuffer, sizeof(uuidBuffer));
ALOGE("set(): AudioFlinger could not create effect %s / %s, status: %d",
- typeBuffer, uuidBuffer, mStatus);
+ type != nullptr ? typeBuffer : "NULL",
+ uuid != nullptr ? uuidBuffer : "NULL",
+ mStatus);
if (iEffect == 0) {
mStatus = NO_INIT;
}
diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp
index a1b04ca..271e186 100644
--- a/media/libaudioclient/AudioRecord.cpp
+++ b/media/libaudioclient/AudioRecord.cpp
@@ -884,7 +884,6 @@
{
// previous and new IAudioRecord sequence numbers are used to detect track re-creation
uint32_t oldSequence = 0;
- uint32_t newSequence;
Proxy::Buffer buffer;
status_t status = NO_ERROR;
@@ -902,7 +901,7 @@
// start of lock scope
AutoMutex lock(mLock);
- newSequence = mSequence;
+ uint32_t newSequence = mSequence;
// did previous obtainBuffer() fail due to media server death or voluntary invalidation?
if (status == DEAD_OBJECT) {
// re-create track, unless someone else has already done so
@@ -939,6 +938,7 @@
audioBuffer->frameCount = buffer.mFrameCount;
audioBuffer->size = buffer.mFrameCount * mFrameSize;
audioBuffer->raw = buffer.mRaw;
+ audioBuffer->sequence = oldSequence;
if (nonContig != NULL) {
*nonContig = buffer.mNonContig;
}
@@ -959,6 +959,12 @@
buffer.mRaw = audioBuffer->raw;
AutoMutex lock(mLock);
+ if (audioBuffer->sequence != mSequence) {
+ // This Buffer came from a different IAudioRecord instance, so ignore the releaseBuffer
+ ALOGD("%s is no-op due to IAudioRecord sequence mismatch %u != %u",
+ __func__, audioBuffer->sequence, mSequence);
+ return;
+ }
mInOverrun = false;
mProxy->releaseBuffer(&buffer);
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index 4a80cd3..9a66d48 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -1665,7 +1665,6 @@
{
// previous and new IAudioTrack sequence numbers are used to detect track re-creation
uint32_t oldSequence = 0;
- uint32_t newSequence;
Proxy::Buffer buffer;
status_t status = NO_ERROR;
@@ -1682,7 +1681,7 @@
{ // start of lock scope
AutoMutex lock(mLock);
- newSequence = mSequence;
+ uint32_t newSequence = mSequence;
// did previous obtainBuffer() fail due to media server death or voluntary invalidation?
if (status == DEAD_OBJECT) {
// re-create track, unless someone else has already done so
@@ -1729,6 +1728,7 @@
audioBuffer->frameCount = buffer.mFrameCount;
audioBuffer->size = buffer.mFrameCount * mFrameSize;
audioBuffer->raw = buffer.mRaw;
+ audioBuffer->sequence = oldSequence;
if (nonContig != NULL) {
*nonContig = buffer.mNonContig;
}
@@ -1752,6 +1752,12 @@
buffer.mRaw = audioBuffer->raw;
AutoMutex lock(mLock);
+ if (audioBuffer->sequence != mSequence) {
+ // This Buffer came from a different IAudioTrack instance, so ignore the releaseBuffer
+ ALOGD("%s is no-op due to IAudioTrack sequence mismatch %u != %u",
+ __func__, audioBuffer->sequence, mSequence);
+ return;
+ }
mReleased += stepCount;
mInUnderrun = false;
mProxy->releaseBuffer(&buffer);
diff --git a/media/libaudioclient/include/media/AudioRecord.h b/media/libaudioclient/include/media/AudioRecord.h
index a3c0fe4..574302b 100644
--- a/media/libaudioclient/include/media/AudioRecord.h
+++ b/media/libaudioclient/include/media/AudioRecord.h
@@ -92,6 +92,11 @@
int8_t* i8; // unsigned 8-bit, offset by 0x80
// input to obtainBuffer(): unused, output: pointer to buffer
};
+
+ uint32_t sequence; // IAudioRecord instance sequence number, as of obtainBuffer().
+ // It is set by obtainBuffer() and confirmed by releaseBuffer().
+ // Not "user-serviceable".
+ // TODO Consider sp<IMemory> instead, or in addition to this.
};
/* As a convenience, if a callback is supplied, a handler thread
@@ -420,14 +425,17 @@
* frameCount number of frames requested
* size ignored
* raw ignored
+ * sequence ignored
* After error return:
* frameCount 0
* size 0
* raw undefined
+ * sequence undefined
* After successful return:
* frameCount actual number of frames available, <= number requested
* size actual number of bytes available
* raw pointer to the buffer
+ * sequence IAudioRecord instance sequence number, as of obtainBuffer()
*/
status_t obtainBuffer(Buffer* audioBuffer, int32_t waitCount,
diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h
index df5eabc..c607918 100644
--- a/media/libaudioclient/include/media/AudioTrack.h
+++ b/media/libaudioclient/include/media/AudioTrack.h
@@ -107,6 +107,11 @@
int16_t* i16; // signed 16-bit
int8_t* i8; // unsigned 8-bit, offset by 0x80
}; // input to obtainBuffer(): unused, output: pointer to buffer
+
+ uint32_t sequence; // IAudioTrack instance sequence number, as of obtainBuffer().
+ // It is set by obtainBuffer() and confirmed by releaseBuffer().
+ // Not "user-serviceable".
+ // TODO Consider sp<IMemory> instead, or in addition to this.
};
/* As a convenience, if a callback is supplied, a handler thread
@@ -692,14 +697,17 @@
* frameCount number of [empty slots for] frames requested
* size ignored
* raw ignored
+ * sequence ignored
* After error return:
* frameCount 0
* size 0
* raw undefined
+ * sequence undefined
* After successful return:
* frameCount actual number of [empty slots for] frames available, <= number requested
* size actual number of bytes available
* raw pointer to the buffer
+ * sequence IAudioTrack instance sequence number, as of obtainBuffer()
*/
status_t obtainBuffer(Buffer* audioBuffer, int32_t waitCount,
size_t *nonContig = NULL);
diff --git a/media/libcpustats/ThreadCpuUsage.cpp b/media/libcpustats/ThreadCpuUsage.cpp
index 4b7549f..e71a7db 100644
--- a/media/libcpustats/ThreadCpuUsage.cpp
+++ b/media/libcpustats/ThreadCpuUsage.cpp
@@ -21,6 +21,7 @@
#include <stdlib.h>
#include <string.h>
#include <time.h>
+#include <unistd.h>
#include <utils/Log.h>
diff --git a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
index 1cb81a6..39f5bb6 100644
--- a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
+++ b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
@@ -1906,11 +1906,15 @@
//ALOGV("\tReverb_command cmdCode Case: "
// "EFFECT_CMD_GET_PARAM start");
effect_param_t *p = (effect_param_t *)pCmdData;
+ if (pCmdData == nullptr) {
+ ALOGW("\tLVM_ERROR : pCmdData is NULL");
+ return -EINVAL;
+ }
if (SIZE_MAX - sizeof(effect_param_t) < (size_t)p->psize) {
android_errorWriteLog(0x534e4554, "26347509");
return -EINVAL;
}
- if (pCmdData == NULL || cmdSize < sizeof(effect_param_t) ||
+ if (cmdSize < sizeof(effect_param_t) ||
cmdSize < (sizeof(effect_param_t) + p->psize) ||
pReplyData == NULL || replySize == NULL ||
*replySize < (sizeof(effect_param_t) + p->psize)) {
diff --git a/media/libmedia/MediaProfiles.cpp b/media/libmedia/MediaProfiles.cpp
index 98c5497..637322f 100644
--- a/media/libmedia/MediaProfiles.cpp
+++ b/media/libmedia/MediaProfiles.cpp
@@ -29,9 +29,55 @@
#include <OMX_Video.h>
#include <sys/stat.h>
+#include <array>
+#include <string>
+#include <vector>
+
namespace android {
-constexpr char const * const MediaProfiles::xmlFiles[];
+namespace /* unnamed */ {
+
+// Returns a list of possible paths for the media_profiles XML file.
+std::array<char const*, 5> const& getXmlPaths() {
+ static std::array<std::string const, 5> const paths =
+ []() -> decltype(paths) {
+ // Directories for XML file that will be searched (in this order).
+ constexpr std::array<char const*, 4> searchDirs = {
+ "product/etc/",
+ "odm/etc/",
+ "vendor/etc/",
+ "system/etc/",
+ };
+
+ // The file name may contain a variant if the vendor property
+ // ro.vendor.media_profiles_xml_variant is set.
+ char variant[PROPERTY_VALUE_MAX];
+ property_get("ro.media.xml_variant.profiles",
+ variant,
+ "_V1_0");
+
+ std::string fileName =
+ std::string("media_profiles") + variant + ".xml";
+
+ return { searchDirs[0] + fileName,
+ searchDirs[1] + fileName,
+ searchDirs[2] + fileName,
+ searchDirs[3] + fileName,
+ "system/etc/media_profiles_V1_0.xml" // System fallback
+ };
+ }();
+ static std::array<char const*, 5> const cPaths = {
+ paths[0].data(),
+ paths[1].data(),
+ paths[2].data(),
+ paths[3].data(),
+ paths[4].data()
+ };
+ return cPaths;
+}
+
+} // unnamed namespace
+
Mutex MediaProfiles::sLock;
bool MediaProfiles::sIsInitialized = false;
MediaProfiles *MediaProfiles::sInstance = NULL;
@@ -48,7 +94,7 @@
{"amrwb", AUDIO_ENCODER_AMR_WB},
{"aac", AUDIO_ENCODER_AAC},
{"heaac", AUDIO_ENCODER_HE_AAC},
- {"aaceld", AUDIO_ENCODER_AAC_ELD},
+ {"aaceld", AUDIO_ENCODER_AAC_ELD},
{"opus", AUDIO_ENCODER_OPUS}
};
@@ -610,7 +656,7 @@
char value[PROPERTY_VALUE_MAX];
if (property_get("media.settings.xml", value, NULL) <= 0) {
const char* xmlFile = nullptr;
- for (auto const& f : xmlFiles) {
+ for (auto const& f : getXmlPaths()) {
if (checkXmlFile(f)) {
xmlFile = f;
break;
diff --git a/media/libmedia/MidiIoWrapper.cpp b/media/libmedia/MidiIoWrapper.cpp
index e71ea2c..da272e3 100644
--- a/media/libmedia/MidiIoWrapper.cpp
+++ b/media/libmedia/MidiIoWrapper.cpp
@@ -18,8 +18,9 @@
#define LOG_TAG "MidiIoWrapper"
#include <utils/Log.h>
-#include <sys/stat.h>
#include <fcntl.h>
+#include <sys/stat.h>
+#include <unistd.h>
#include <media/MidiIoWrapper.h>
#include <media/MediaExtractorPluginApi.h>
diff --git a/media/libmedia/include/media/MediaProfiles.h b/media/libmedia/include/media/MediaProfiles.h
index 3e8e7c8..4cc5b95 100644
--- a/media/libmedia/include/media/MediaProfiles.h
+++ b/media/libmedia/include/media/MediaProfiles.h
@@ -1,18 +1,18 @@
/*
- **
- ** Copyright 2010, 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.
+ *
+ * Copyright 2010, The Android Open Source Project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
#ifndef ANDROID_MEDIAPROFILES_H
@@ -82,29 +82,12 @@
{
public:
- /*
- * If property media.settings.xml is not set:
- *
- * getInstance() will search through paths listed in xmlFiles.
- * The search goes through members of xmlFiles in the order that they are
- * defined, so files at lower indices have higher priority than those at
- * higher indices.
- *
- * TODO: Add runtime validation of xml files. A search should be considered
- * successful only when validation is successful.
- */
- static constexpr char const * const xmlFiles[] = {
- "odm/etc/media_profiles_V1_0.xml",
- "vendor/etc/media_profiles_V1_0.xml",
- "system/etc/media_profiles.xml"
- };
-
/**
* Returns the singleton instance for subsequence queries or NULL if error.
*
* If property media.settings.xml is set, getInstance() will attempt to read
* from file path in media.settings.xml. Otherwise, getInstance() will
- * search through the list xmlFiles as described above.
+ * search through the list of preset XML file paths.
*
* If the search is unsuccessful, the default instance will be created
* instead.
diff --git a/media/libmedia/xsd/vts/ValidateMediaProfiles.cpp b/media/libmedia/xsd/vts/ValidateMediaProfiles.cpp
index 7729d52..4f3951a 100644
--- a/media/libmedia/xsd/vts/ValidateMediaProfiles.cpp
+++ b/media/libmedia/xsd/vts/ValidateMediaProfiles.cpp
@@ -14,23 +14,68 @@
* limitations under the License.
*/
+#include <fstream>
#include <string>
#include <android-base/file.h>
#include <android-base/properties.h>
#include "utility/ValidateXml.h"
+bool isFileReadable(std::string const& path) {
+ std::ifstream f(path);
+ return f.good();
+}
+
TEST(CheckConfig, mediaProfilesValidation) {
RecordProperty("description",
"Verify that the media profiles file "
"is valid according to the schema");
+ // Schema path.
+ constexpr char const* xsdPath = "/data/local/tmp/media_profiles.xsd";
+
+ // If "media.settings.xml" is set, it will be used as an absolute path.
std::string mediaSettingsPath = android::base::GetProperty("media.settings.xml", "");
if (mediaSettingsPath.empty()) {
- mediaSettingsPath.assign("/vendor/etc/media_profiles_V1_0.xml");
- }
+ // If "media.settings.xml" is not set, we will search through a list of
+ // file paths.
- EXPECT_ONE_VALID_XML_MULTIPLE_LOCATIONS(android::base::Basename(mediaSettingsPath).c_str(),
- {android::base::Dirname(mediaSettingsPath).c_str()},
- "/data/local/tmp/media_profiles.xsd");
+ constexpr char const* xmlSearchDirs[] = {
+ "/product/etc/",
+ "/odm/etc/",
+ "/vendor/etc/",
+ };
+
+ // The vendor may provide a vendor variant for the file name.
+ std::string variant = android::base::GetProperty(
+ "ro.media.xml_variant.profiles", "_V1_0");
+ std::string fileName = "media_profiles" + variant + ".xml";
+
+ // Fallback path does not depend on the property defined from the vendor
+ // partition.
+ constexpr char const* fallbackXmlPath =
+ "/system/etc/media_profiles_V1_0.xml";
+
+ std::vector<std::string> xmlPaths = {
+ xmlSearchDirs[0] + fileName,
+ xmlSearchDirs[1] + fileName,
+ xmlSearchDirs[2] + fileName,
+ fallbackXmlPath
+ };
+
+ auto findXmlPath =
+ std::find_if(xmlPaths.begin(), xmlPaths.end(), isFileReadable);
+ ASSERT_TRUE(findXmlPath != xmlPaths.end())
+ << "Cannot read from " << fileName
+ << " in any search directories ("
+ << xmlSearchDirs[0] << ", "
+ << xmlSearchDirs[1] << ", "
+ << xmlSearchDirs[2] << ") and from "
+ << fallbackXmlPath << ".";
+
+ char const* xmlPath = findXmlPath->c_str();
+ EXPECT_VALID_XML(xmlPath, xsdPath);
+ } else {
+ EXPECT_VALID_XML(mediaSettingsPath.c_str(), xsdPath);
+ }
}
diff --git a/media/libstagefright/codecs/gsm/dec/SoftGSM.h b/media/libstagefright/codecs/gsm/dec/SoftGSM.h
index ef86915..d5885a6 100644
--- a/media/libstagefright/codecs/gsm/dec/SoftGSM.h
+++ b/media/libstagefright/codecs/gsm/dec/SoftGSM.h
@@ -20,9 +20,7 @@
#include <media/stagefright/omx/SimpleSoftOMXComponent.h>
-extern "C" {
#include "gsm.h"
-}
namespace android {
diff --git a/media/libstagefright/codecs/m4v_h263/dec/Android.bp b/media/libstagefright/codecs/m4v_h263/dec/Android.bp
index 6b45ea2..b8b83d5 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/Android.bp
+++ b/media/libstagefright/codecs/m4v_h263/dec/Android.bp
@@ -1,6 +1,7 @@
cc_library_static {
name: "libstagefright_m4vh263dec",
vendor_available: true,
+ host_supported: true,
shared_libs: ["liblog"],
srcs: [
@@ -38,11 +39,6 @@
"src/zigzag_tab.cpp",
],
- header_libs: [
- "media_plugin_headers",
- "libstagefright_headers"
- ],
-
local_include_dirs: ["src"],
export_include_dirs: ["include"],
@@ -61,6 +57,12 @@
],
cfi: true,
},
+
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
}
//###############################################################################
diff --git a/media/libstagefright/codecs/m4v_h263/fuzzer/Android.bp b/media/libstagefright/codecs/m4v_h263/fuzzer/Android.bp
new file mode 100644
index 0000000..aa79d37
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/fuzzer/Android.bp
@@ -0,0 +1,60 @@
+/******************************************************************************
+ *
+ * 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.
+ *
+ *****************************************************************************
+ * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
+ */
+
+cc_fuzz {
+ name: "mpeg4_dec_fuzzer",
+ host_supported: true,
+ srcs: [
+ "mpeg4_h263_dec_fuzzer.cpp",
+ ],
+ static_libs: [
+ "libstagefright_m4vh263dec",
+ "liblog",
+ ],
+ cflags: [
+ "-DOSCL_IMPORT_REF=",
+ "-DMPEG4",
+ ],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+}
+
+cc_fuzz {
+ name: "h263_dec_fuzzer",
+ host_supported: true,
+ srcs: [
+ "mpeg4_h263_dec_fuzzer.cpp",
+ ],
+ static_libs: [
+ "libstagefright_m4vh263dec",
+ "liblog",
+ ],
+ cflags: [
+ "-DOSCL_IMPORT_REF=",
+ ],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+}
diff --git a/media/libstagefright/codecs/m4v_h263/fuzzer/README.md b/media/libstagefright/codecs/m4v_h263/fuzzer/README.md
new file mode 100644
index 0000000..c2a4f69
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/fuzzer/README.md
@@ -0,0 +1,57 @@
+# Fuzzer for libstagefright_m4vh263dec decoder
+
+## Plugin Design Considerations
+The fuzzer plugin for MPEG4/H263 is designed based on the understanding of the
+codec and tries to achieve the following:
+
+##### Maximize code coverage
+Dict files (dictionary files) are created for MPEG4 and H263 to ensure that the required start
+bytes are present in every input file that goes to the fuzzer.
+This ensures that decoder does not reject any input file in the first check
+
+##### Maximize utilization of input data
+The plugin feeds the entire input data to the codec using a loop.
+ * If the decode operation was successful, the input is advanced by the number of bytes consumed
+ in the decode call.
+ * If the decode operation was un-successful, the input is advanced by 1 byte so that the fuzzer
+ can proceed to feed the next frame.
+
+This ensures that the plugin tolerates any kind of input (empty, huge, malformed, etc)
+and doesnt `exit()` on any input and thereby increasing the chance of identifying vulnerabilities.
+
+##### Other considerations
+ * Two fuzzer binaries - mpeg4_dec_fuzzer and h263_dec_fuzzer are generated based on the presence
+ of a flag - 'MPEG4'
+ * The number of decode calls are kept to a maximum of 100 so that the fuzzer does not timeout.
+
+## Build
+
+This describes steps to build mpeg4_dec_fuzzer and h263_dec_fuzzer binary.
+
+### Android
+#### Steps to build
+Build the fuzzer
+```
+ $ mm -j$(nproc) mpeg4_dec_fuzzer
+ $ mm -j$(nproc) h263_dec_fuzzer
+```
+
+#### Steps to run
+Create a directory CORPUS_DIR and copy some MPEG4 or H263 files to that folder
+Push this directory to device.
+
+To run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/mpeg4_dec_fuzzer/mpeg4_dec_fuzzer CORPUS_DIR
+ $ adb shell /data/fuzz/arm64/h263_dec_fuzzer/h263_dec_fuzzer CORPUS_DIR
+```
+To run on host
+```
+ $ $ANDROID_HOST_OUT/fuzz/x86_64/mpeg4_dec_fuzzer/mpeg4_dec_fuzzer CORPUS_DIR
+ $ $ANDROID_HOST_OUT/fuzz/x86_64/h263_dec_fuzzer/h263_dec_fuzzer CORPUS_DIR
+```
+
+## References:
+ * http://llvm.org/docs/LibFuzzer.html
+ * https://github.com/google/oss-fuzz
diff --git a/media/libstagefright/codecs/m4v_h263/fuzzer/h263_dec_fuzzer.dict b/media/libstagefright/codecs/m4v_h263/fuzzer/h263_dec_fuzzer.dict
new file mode 100644
index 0000000..591d37e
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/fuzzer/h263_dec_fuzzer.dict
@@ -0,0 +1,2 @@
+# Start code (bytes 0-3)
+kw1="\x00\x00\x80\x02"
diff --git a/media/libstagefright/codecs/m4v_h263/fuzzer/mpeg4_dec_fuzzer.dict b/media/libstagefright/codecs/m4v_h263/fuzzer/mpeg4_dec_fuzzer.dict
new file mode 100644
index 0000000..76241a6
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/fuzzer/mpeg4_dec_fuzzer.dict
@@ -0,0 +1,2 @@
+# Start code (bytes 0-3)
+kw1="\x00\x00\x01\xB0"
diff --git a/media/libstagefright/codecs/m4v_h263/fuzzer/mpeg4_h263_dec_fuzzer.cpp b/media/libstagefright/codecs/m4v_h263/fuzzer/mpeg4_h263_dec_fuzzer.cpp
new file mode 100644
index 0000000..912c821
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/fuzzer/mpeg4_h263_dec_fuzzer.cpp
@@ -0,0 +1,205 @@
+/******************************************************************************
+ *
+ * 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.
+ *
+ *****************************************************************************
+ * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
+ */
+#include "mp4dec_api.h"
+#define MPEG4_MAX_WIDTH 1920
+#define MPEG4_MAX_HEIGHT 1080
+#define H263_MAX_WIDTH 352
+#define H263_MAX_HEIGHT 288
+#define DEFAULT_WIDTH 352
+#define DEFAULT_HEIGHT 288
+
+constexpr size_t kMaxNumDecodeCalls = 100;
+constexpr uint8_t kNumOutputBuffers = 2;
+constexpr int kLayer = 1;
+
+struct tagvideoDecControls;
+
+/* == ceil(num / den) * den. T must be integer type, alignment must be positive power of 2 */
+template <class T, class U>
+inline static const T align(const T &num, const U &den) {
+ return (num + (T)(den - 1)) & (T) ~(den - 1);
+}
+
+class Codec {
+ public:
+ Codec() = default;
+ ~Codec() { deInitDecoder(); }
+ bool initDecoder();
+ bool allocOutputBuffer(size_t outputBufferSize);
+ void freeOutputBuffer();
+ void handleResolutionChange();
+ void decodeFrames(const uint8_t *data, size_t size);
+ void deInitDecoder();
+
+ private:
+ tagvideoDecControls *mDecHandle = nullptr;
+ uint8_t *mOutputBuffer[kNumOutputBuffers];
+ bool mInitialized = false;
+ bool mFramesConfigured = false;
+#ifdef MPEG4
+ MP4DecodingMode mInputMode = MPEG4_MODE;
+ size_t mMaxWidth = MPEG4_MAX_WIDTH;
+ size_t mMaxHeight = MPEG4_MAX_HEIGHT;
+#else
+ MP4DecodingMode mInputMode = H263_MODE;
+ size_t mMaxWidth = H263_MAX_WIDTH;
+ size_t mMaxHeight = H263_MAX_HEIGHT;
+#endif
+ uint32_t mNumSamplesOutput = 0;
+ uint32_t mWidth = DEFAULT_WIDTH;
+ uint32_t mHeight = DEFAULT_HEIGHT;
+};
+
+bool Codec::initDecoder() {
+ mDecHandle = new tagvideoDecControls;
+ if (!mDecHandle) {
+ return false;
+ }
+ memset(mDecHandle, 0, sizeof(tagvideoDecControls));
+ return true;
+}
+
+bool Codec::allocOutputBuffer(size_t outputBufferSize) {
+ for (uint8_t i = 0; i < kNumOutputBuffers; ++i) {
+ if (!mOutputBuffer[i]) {
+ mOutputBuffer[i] = static_cast<uint8_t *>(malloc(outputBufferSize));
+ if (!mOutputBuffer[i]) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+void Codec::freeOutputBuffer() {
+ for (uint8_t i = 0; i < kNumOutputBuffers; ++i) {
+ if (mOutputBuffer[i]) {
+ free(mOutputBuffer[i]);
+ mOutputBuffer[i] = nullptr;
+ }
+ }
+}
+
+void Codec::handleResolutionChange() {
+ int32_t dispWidth, dispHeight;
+ PVGetVideoDimensions(mDecHandle, &dispWidth, &dispHeight);
+
+ int32_t bufWidth, bufHeight;
+ PVGetBufferDimensions(mDecHandle, &bufWidth, &bufHeight);
+
+ if (dispWidth != mWidth || dispHeight != mHeight) {
+ mWidth = dispWidth;
+ mHeight = dispHeight;
+ }
+}
+
+void Codec::decodeFrames(const uint8_t *data, size_t size) {
+ size_t outputBufferSize = align(mMaxWidth, 16) * align(mMaxHeight, 16) * 3 / 2;
+ uint8_t *start_code = const_cast<uint8_t *>(data);
+ static const uint8_t volInfo[] = {0x00, 0x00, 0x01, 0xB0};
+ bool volHeader = memcmp(start_code, volInfo, 4) == 0;
+ if (volHeader) {
+ PVCleanUpVideoDecoder(mDecHandle);
+ mInitialized = false;
+ }
+
+ if (!mInitialized) {
+ uint8_t *volData[1]{};
+ int32_t volSize = 0;
+
+ if (volHeader) { /* removed some codec config part */
+ volData[0] = const_cast<uint8_t *>(data);
+ volSize = size;
+ }
+
+ if (!PVInitVideoDecoder(mDecHandle, volData, &volSize, kLayer, mMaxWidth, mMaxHeight,
+ mInputMode)) {
+ return;
+ }
+ mInitialized = true;
+ MP4DecodingMode actualMode = PVGetDecBitstreamMode(mDecHandle);
+ if (mInputMode != actualMode) {
+ return;
+ }
+
+ PVSetPostProcType(mDecHandle, 0);
+ }
+ size_t yFrameSize = sizeof(uint8) * mDecHandle->size;
+ if (outputBufferSize < yFrameSize * 3 / 2) {
+ return;
+ }
+ if (!allocOutputBuffer(outputBufferSize)) {
+ return;
+ }
+ size_t numDecodeCalls = 0;
+ while ((size > 0) && (numDecodeCalls < kMaxNumDecodeCalls)) {
+ if (!mFramesConfigured) {
+ PVSetReferenceYUV(mDecHandle, mOutputBuffer[1]);
+ mFramesConfigured = true;
+ }
+
+ // Need to check if header contains new info, e.g., width/height, etc.
+ VopHeaderInfo header_info;
+ uint32_t useExtTimestamp = (numDecodeCalls == 0);
+ int32_t tempSize = (int32_t)size;
+ uint8_t *bitstreamTmp = const_cast<uint8_t *>(data);
+ uint32_t timestamp = 0;
+ if (PVDecodeVopHeader(mDecHandle, &bitstreamTmp, ×tamp, &tempSize, &header_info,
+ &useExtTimestamp, mOutputBuffer[mNumSamplesOutput & 1]) != PV_TRUE) {
+ return;
+ }
+
+ handleResolutionChange();
+
+ PVDecodeVopBody(mDecHandle, &tempSize);
+ uint32_t bytesConsumed = 1;
+ if (size > tempSize) {
+ bytesConsumed = size - tempSize;
+ }
+ data += bytesConsumed;
+ size -= bytesConsumed;
+ ++mNumSamplesOutput;
+ ++numDecodeCalls;
+ }
+ freeOutputBuffer();
+}
+
+void Codec::deInitDecoder() {
+ PVCleanUpVideoDecoder(mDecHandle);
+ delete mDecHandle;
+ mDecHandle = nullptr;
+ mInitialized = false;
+ freeOutputBuffer();
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ if (size < 4) {
+ return 0;
+ }
+ Codec *codec = new Codec();
+ if (!codec) {
+ return 0;
+ }
+ if (codec->initDecoder()) {
+ codec->decodeFrames(data, size);
+ }
+ delete codec;
+ return 0;
+}
diff --git a/media/libstagefright/foundation/tests/Android.bp b/media/libstagefright/foundation/tests/Android.bp
index f2157c9..45e81e8 100644
--- a/media/libstagefright/foundation/tests/Android.bp
+++ b/media/libstagefright/foundation/tests/Android.bp
@@ -25,3 +25,31 @@
"Utils_test.cpp",
],
}
+
+cc_test {
+ name: "MetaDataBaseUnitTest",
+ gtest: true,
+
+ srcs: [
+ "MetaDataBaseUnitTest.cpp",
+ ],
+
+ shared_libs: [
+ "libutils",
+ "liblog",
+ ],
+
+ static_libs: [
+ "libstagefright",
+ "libstagefright_foundation",
+ ],
+
+ header_libs: [
+ "libmedia_headers",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+}
diff --git a/media/libstagefright/foundation/tests/MetaDataBaseUnitTest.cpp b/media/libstagefright/foundation/tests/MetaDataBaseUnitTest.cpp
new file mode 100644
index 0000000..0aed4d2
--- /dev/null
+++ b/media/libstagefright/foundation/tests/MetaDataBaseUnitTest.cpp
@@ -0,0 +1,288 @@
+/*
+ * 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 <gtest/gtest.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <fstream>
+
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaDataBase.h>
+
+constexpr int32_t kWidth1 = 1920;
+constexpr int32_t kHeight1 = 1080;
+constexpr int32_t kWidth2 = 1280;
+constexpr int32_t kHeight2 = 920;
+constexpr int32_t kWidth3 = 720;
+constexpr int32_t kHeight3 = 480;
+constexpr int32_t kProfile = 1;
+constexpr int32_t kLevel = 1;
+constexpr int32_t kPlatformValue = 1;
+
+// Rectangle margins
+constexpr int32_t kLeft = 100;
+constexpr int32_t kTop = 100;
+constexpr int32_t kRight = 100;
+constexpr int32_t kBottom = 100;
+
+constexpr int64_t kDurationUs = 60000000;
+
+constexpr float kCaptureRate = 30.0;
+
+namespace android {
+
+class MetaDataBaseUnitTest : public ::testing::Test {};
+
+TEST_F(MetaDataBaseUnitTest, CreateMetaDataBaseTest) {
+ MetaDataBase *metaData = new MetaDataBase();
+ ASSERT_NE(metaData, nullptr) << "Failed to create meta data";
+
+ // Testing copy constructor
+ MetaDataBase *metaDataCopy = metaData;
+ ASSERT_NE(metaDataCopy, nullptr) << "Failed to create meta data copy";
+
+ delete metaData;
+}
+
+TEST_F(MetaDataBaseUnitTest, SetAndFindDataTest) {
+ MetaDataBase *metaData = new MetaDataBase();
+ ASSERT_NE(metaData, nullptr) << "Failed to create meta data";
+
+ // Setting the different key-value pair type for first time, overwrite
+ // expected to be false
+ bool status = metaData->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
+ ASSERT_FALSE(status) << "Initializing kKeyMIMEType, overwrite is expected to be false";
+
+ status = metaData->setInt32(kKeyWidth, kWidth1);
+ ASSERT_FALSE(status) << "Initializing kKeyWidth, overwrite is expected to be false";
+ status = metaData->setInt32(kKeyHeight, kHeight1);
+ ASSERT_FALSE(status) << "Initializing kKeyHeight, overwrite is expected to be false";
+ status = metaData->setInt32(kKeyVideoProfile, kProfile);
+ ASSERT_FALSE(status) << "Initializing kKeyVideoProfile, overwrite is expected to be false";
+ status = metaData->setInt32(kKeyVideoLevel, kLevel);
+ ASSERT_FALSE(status) << "Initializing kKeyVideoLevel, overwrite is expected to be false";
+
+ status = metaData->setInt64(kKeyDuration, kDurationUs);
+ ASSERT_FALSE(status) << "Initializing kKeyDuration, overwrite is expected to be false";
+
+ status = metaData->setFloat(kKeyCaptureFramerate, kCaptureRate);
+ ASSERT_FALSE(status) << "Initializing kKeyCaptureFramerate, overwrite is expected to be false";
+
+ const int32_t *platform = &kPlatformValue;
+ status = metaData->setPointer(kKeyPlatformPrivate, (void *)platform);
+ ASSERT_FALSE(status) << "Initializing kKeyPlatformPrivate, overwrite is expected to be false";
+
+ status = metaData->setRect(kKeyCropRect, kLeft, kTop, kRight, kBottom);
+ ASSERT_FALSE(status) << "Initializing kKeyCropRect, overwrite is expected to be false";
+
+ // Dump to log for reference
+ metaData->dumpToLog();
+
+ // Find the data which was set
+ const char *mime;
+ status = metaData->findCString(kKeyMIMEType, &mime);
+ ASSERT_TRUE(status) << "kKeyMIMEType key does not exists in metadata";
+ ASSERT_STREQ(mime, MEDIA_MIMETYPE_VIDEO_AVC) << "Incorrect mime type returned";
+
+ int32_t width, height, profile, level;
+ status = metaData->findInt32(kKeyWidth, &width);
+ ASSERT_TRUE(status) << "kKeyWidth key does not exists in metadata";
+ ASSERT_EQ(width, kWidth1) << "Incorrect value of width returned";
+
+ status = metaData->findInt32(kKeyHeight, &height);
+ ASSERT_TRUE(status) << "kKeyHeight key does not exists in metadata";
+ ASSERT_EQ(height, kHeight1) << "Incorrect value of height returned";
+
+ status = metaData->findInt32(kKeyVideoProfile, &profile);
+ ASSERT_TRUE(status) << "kKeyVideoProfile key does not exists in metadata";
+ ASSERT_EQ(profile, kProfile) << "Incorrect value of profile returned";
+
+ status = metaData->findInt32(kKeyVideoLevel, &level);
+ ASSERT_TRUE(status) << "kKeyVideoLevel key does not exists in metadata";
+ ASSERT_EQ(level, kLevel) << "Incorrect value of level returned";
+
+ int64_t duration;
+ status = metaData->findInt64(kKeyDuration, &duration);
+ ASSERT_TRUE(status) << "kKeyDuration key does not exists in metadata";
+ ASSERT_EQ(duration, kDurationUs) << "Incorrect value of duration returned";
+
+ float frameRate;
+ status = metaData->findFloat(kKeyCaptureFramerate, &frameRate);
+ ASSERT_TRUE(status) << "kKeyCaptureFramerate key does not exists in metadata";
+ ASSERT_EQ(frameRate, kCaptureRate) << "Incorrect value of captureFrameRate returned";
+
+ int32_t top, bottom, left, right;
+ status = metaData->findRect(kKeyCropRect, &left, &top, &right, &bottom);
+ ASSERT_TRUE(status) << "kKeyCropRect key does not exists in metadata";
+ ASSERT_EQ(left, kLeft) << "Incorrect value of left margin returned";
+ ASSERT_EQ(top, kTop) << "Incorrect value of top margin returned";
+ ASSERT_EQ(right, kRight) << "Incorrect value of right margin returned";
+ ASSERT_EQ(bottom, kBottom) << "Incorrect value of bottom margin returned";
+
+ void *platformValue;
+ status = metaData->findPointer(kKeyPlatformPrivate, &platformValue);
+ ASSERT_TRUE(status) << "kKeyPlatformPrivate key does not exists in metadata";
+ ASSERT_EQ(platformValue, &kPlatformValue) << "Incorrect value of pointer returned";
+
+ // Check for the key which is not added to metadata
+ int32_t angle;
+ status = metaData->findInt32(kKeyRotation, &angle);
+ ASSERT_FALSE(status) << "Value for an invalid key is returned when the key is not set";
+
+ delete (metaData);
+}
+
+TEST_F(MetaDataBaseUnitTest, OverWriteFunctionalityTest) {
+ MetaDataBase *metaData = new MetaDataBase();
+ ASSERT_NE(metaData, nullptr) << "Failed to create meta data";
+
+ // set/set/read to check first overwrite operation
+ bool status = metaData->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
+ ASSERT_FALSE(status) << "Initializing kKeyMIMEType, overwrite is expected to be false";
+ // Overwrite the value
+ status = metaData->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_HEVC);
+ ASSERT_TRUE(status) << "Setting kKeyMIMEType again, overwrite is expected to be true";
+ // Check the value
+ const char *mime;
+ status = metaData->findCString(kKeyMIMEType, &mime);
+ ASSERT_TRUE(status) << "kKeyMIMEType key does not exists in metadata";
+ ASSERT_STREQ(mime, MEDIA_MIMETYPE_VIDEO_HEVC) << "Mime value is not overwritten";
+
+ // set/set/set/read to check second overwrite operation
+ status = metaData->setInt32(kKeyWidth, kWidth1);
+ ASSERT_FALSE(status) << "Initializing kKeyWidth, overwrite is expected to be false";
+ status = metaData->setInt32(kKeyHeight, kHeight1);
+ ASSERT_FALSE(status) << "Initializing kKeyHeight, overwrite is expected to be false";
+ // Overwrite the value
+ status = metaData->setInt32(kKeyWidth, kWidth2);
+ ASSERT_TRUE(status) << "Setting kKeyWidth again, overwrite is expected to be true";
+ status = metaData->setInt32(kKeyHeight, kHeight2);
+ ASSERT_TRUE(status) << "Setting kKeyHeight again, overwrite is expected to be true";
+ // Overwrite the value again
+ status = metaData->setInt32(kKeyWidth, kWidth3);
+ ASSERT_TRUE(status) << "Setting kKeyWidth again, overwrite is expected to be true";
+ status = metaData->setInt32(kKeyHeight, kHeight3);
+ ASSERT_TRUE(status) << "Setting kKeyHeight again, overwrite is expected to be true";
+ // Check the value
+ int32_t width, height;
+ status = metaData->findInt32(kKeyWidth, &width);
+ ASSERT_TRUE(status) << "kKeyWidth key does not exists in metadata";
+ ASSERT_EQ(width, kWidth3) << "Value of width is not overwritten";
+
+ status = metaData->findInt32(kKeyHeight, &height);
+ ASSERT_TRUE(status) << "kKeyHeight key does not exists in metadata";
+ ASSERT_EQ(height, kHeight3) << "Value of height is not overwritten";
+
+ delete (metaData);
+}
+
+TEST_F(MetaDataBaseUnitTest, RemoveKeyTest) {
+ MetaDataBase *metaData = new MetaDataBase();
+ ASSERT_NE(metaData, nullptr) << "Failed to create meta data";
+
+ bool status = metaData->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
+ ASSERT_FALSE(status) << "Initializing kKeyMIMEType, overwrite is expected to be false";
+ // Query the key
+ status = metaData->hasData(kKeyMIMEType);
+ ASSERT_TRUE(status) << "MetaData does not have the mime key";
+
+ status = metaData->remove(kKeyMIMEType);
+ ASSERT_TRUE(status) << "Failed to remove the kKeyMIMEType key";
+
+ // Query the key
+ status = metaData->hasData(kKeyMIMEType);
+ ASSERT_FALSE(status) << "MetaData has mime key after removing it, expected to be false";
+
+ // Remove the non existing key
+ status = metaData->remove(kKeyMIMEType);
+ ASSERT_FALSE(status) << "Removed the non existing key";
+
+ // Check overwriting the removed key
+ metaData->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_HEVC);
+ ASSERT_FALSE(status) << "Overwrite should be false since the key was removed";
+
+ status = metaData->setInt32(kKeyWidth, kWidth1);
+ ASSERT_FALSE(status) << "Initializing kKeyWidth, overwrite is expected to be false";
+
+ // Clear whole metadata
+ metaData->clear();
+
+ // Check finding key after clearing the metadata
+ int32_t width;
+ status = metaData->findInt32(kKeyWidth, &width);
+ ASSERT_FALSE(status) << "MetaData found kKeyWidth key after clearing all the items in it, "
+ "expected to be false";
+
+ // Query the key
+ status = metaData->hasData(kKeyWidth);
+ ASSERT_FALSE(status)
+ << "MetaData has width key after clearing all the items in it, expected to be false";
+
+ status = metaData->hasData(kKeyMIMEType);
+ ASSERT_FALSE(status)
+ << "MetaData has mime key after clearing all the items in it, expected to be false";
+
+ // Check removing key after clearing the metadata
+ status = metaData->remove(kKeyMIMEType);
+ ASSERT_FALSE(status) << "Removed the key, after clearing the metadata";
+
+ // Checking set after clearing the metadata
+ status = metaData->setInt32(kKeyWidth, kWidth1);
+ ASSERT_FALSE(status) << "Overwrite should be false since the metadata was cleared";
+
+ metaData->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_HEVC);
+ ASSERT_FALSE(status) << "Overwrite should be false since the metadata was cleared";
+
+ delete (metaData);
+}
+
+TEST_F(MetaDataBaseUnitTest, ConvertToStringTest) {
+ MetaDataBase *metaData = new MetaDataBase();
+ ASSERT_NE(metaData, nullptr) << "Failed to create meta data";
+
+ String8 info = metaData->toString();
+ ASSERT_EQ(info.length(), 0) << "Empty MetaData length is non-zero: " << info.length();
+
+ bool status = metaData->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
+ ASSERT_FALSE(status) << "Initializing kKeyMIMEType, overwrite is expected to be false";
+
+ status = metaData->setInt32(kKeyWidth, kWidth1);
+ ASSERT_FALSE(status) << "Initializing kKeyWidth, overwrite is expected to be false";
+ status = metaData->setInt32(kKeyHeight, kHeight1);
+ ASSERT_FALSE(status) << "Initializing kKeyHeight, overwrite is expected to be false";
+ status = metaData->setInt32(kKeyVideoProfile, kProfile);
+ ASSERT_FALSE(status) << "Initializing kKeyVideoProfile, overwrite is expected to be false";
+ status = metaData->setInt32(kKeyVideoLevel, kLevel);
+ ASSERT_FALSE(status) << "Initializing kKeyVideoLevel, overwrite is expected to be false";
+
+ info = metaData->toString();
+ ASSERT_GT(info.length(), 0) << "MetaData contains no information";
+
+ // Dump to log for reference
+ metaData->dumpToLog();
+
+ // Clear whole metadata
+ metaData->clear();
+
+ info = metaData->toString();
+ ASSERT_EQ(info.length(), 0) << "MetaData length is non-zero after clearing it: "
+ << info.length();
+
+ delete (metaData);
+}
+
+} // namespace android
diff --git a/media/libstagefright/mpeg2ts/test/Android.bp b/media/libstagefright/mpeg2ts/test/Android.bp
new file mode 100644
index 0000000..4e4832a
--- /dev/null
+++ b/media/libstagefright/mpeg2ts/test/Android.bp
@@ -0,0 +1,70 @@
+/*
+ * 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: "Mpeg2tsUnitTest",
+ gtest: true,
+
+ srcs: [
+ "Mpeg2tsUnitTest.cpp"
+ ],
+
+ shared_libs: [
+ "android.hardware.cas@1.0",
+ "android.hardware.cas.native@1.0",
+ "android.hidl.token@1.0-utils",
+ "android.hidl.allocator@1.0",
+ "libcrypto",
+ "libhidlbase",
+ "libhidlmemory",
+ "liblog",
+ "libmedia",
+ "libbinder",
+ "libbinder_ndk",
+ "libutils",
+ ],
+
+ static_libs: [
+ "libdatasource",
+ "libstagefright",
+ "libstagefright_foundation",
+ "libstagefright_metadatautils",
+ "libstagefright_mpeg2support",
+ ],
+
+ include_dirs: [
+ "frameworks/av/media/extractors/",
+ "frameworks/av/media/libstagefright/",
+ ],
+
+ header_libs: [
+ "libmedia_headers",
+ "libaudioclient_headers",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ sanitize: {
+ cfi: true,
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ },
+}
diff --git a/media/libstagefright/mpeg2ts/test/AndroidTest.xml b/media/libstagefright/mpeg2ts/test/AndroidTest.xml
new file mode 100644
index 0000000..ac1294d
--- /dev/null
+++ b/media/libstagefright/mpeg2ts/test/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Test module config for Mpeg2ts unit tests">
+ <option name="test-suite-tag" value="Mpeg2tsUnitTest" />
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="Mpeg2tsUnitTest->/data/local/tmp/Mpeg2tsUnitTest" />
+ <option name="push-file"
+ key="https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/mpeg2ts/test/Mpeg2tsUnitTest.zip?unzip=true"
+ value="/data/local/tmp/Mpeg2tsUnitTestRes/" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="Mpeg2tsUnitTest" />
+ <option name="native-test-flag" value="-P /data/local/tmp/Mpeg2tsUnitTestRes/" />
+ </test>
+</configuration>
diff --git a/media/libstagefright/mpeg2ts/test/Mpeg2tsUnitTest.cpp b/media/libstagefright/mpeg2ts/test/Mpeg2tsUnitTest.cpp
new file mode 100644
index 0000000..79c233b
--- /dev/null
+++ b/media/libstagefright/mpeg2ts/test/Mpeg2tsUnitTest.cpp
@@ -0,0 +1,236 @@
+/*
+ * 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 "Mpeg2tsUnitTest"
+
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <sys/stat.h>
+
+#include <datasource/FileSource.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaDataBase.h>
+#include <media/stagefright/foundation/AUtils.h>
+
+#include "mpeg2ts/ATSParser.h"
+#include "mpeg2ts/AnotherPacketSource.h"
+
+#include "Mpeg2tsUnitTestEnvironment.h"
+
+constexpr size_t kTSPacketSize = 188;
+constexpr uint16_t kPIDMask = 0x1FFF;
+// Max value of PID which is also used for Null packets
+constexpr uint16_t kPIDMaxValue = 8191;
+constexpr uint8_t kTSSyncByte = 0x47;
+constexpr uint8_t kVideoPresent = 0x01;
+constexpr uint8_t kAudioPresent = 0x02;
+constexpr uint8_t kMetaDataPresent = 0x04;
+
+static Mpeg2tsUnitTestEnvironment *gEnv = nullptr;
+
+using namespace android;
+
+class Mpeg2tsUnitTest
+ : public ::testing ::TestWithParam<
+ tuple</*fileName*/ string, /*sourceType*/ char, /*numSource*/ uint16_t>> {
+ public:
+ Mpeg2tsUnitTest()
+ : mInputBuffer(nullptr), mSource(nullptr), mFpInput(nullptr), mParser(nullptr) {}
+
+ ~Mpeg2tsUnitTest() {
+ if (mInputBuffer) free(mInputBuffer);
+ if (mFpInput) fclose(mFpInput);
+ mSource.clear();
+ }
+
+ void SetUp() override {
+ mOffset = 0;
+ mNumDataSource = 0;
+ tuple<string, char, uint16_t> params = GetParam();
+ char sourceType = get<1>(params);
+ /* mSourceType = 0b x x x x x M A V
+ / | \
+ metaData audio video */
+ mMediaType = (sourceType & 0x07);
+ mNumDataSource = get<2>(params);
+ string inputFile = gEnv->getRes() + get<0>(params);
+ mFpInput = fopen(inputFile.c_str(), "rb");
+ ASSERT_NE(mFpInput, nullptr) << "Failed to open file: " << inputFile;
+
+ struct stat buf;
+ int8_t err = stat(inputFile.c_str(), &buf);
+ ASSERT_EQ(err, 0) << "Failed to get information for file: " << inputFile;
+
+ long fileSize = buf.st_size;
+ mTotalPackets = fileSize / kTSPacketSize;
+ int32_t fd = fileno(mFpInput);
+ ASSERT_GE(fd, 0) << "Failed to get the integer file descriptor";
+
+ mSource = new FileSource(dup(fd), 0, buf.st_size);
+ ASSERT_NE(mSource, nullptr) << "Failed to get the data source!";
+
+ mParser = new ATSParser();
+ ASSERT_NE(mParser, nullptr) << "Unable to create ATS parser!";
+ mInputBuffer = (uint8_t *)malloc(kTSPacketSize);
+ ASSERT_NE(mInputBuffer, nullptr) << "Failed to allocate memory for TS packet!";
+ }
+
+ uint64_t mOffset;
+ uint64_t mTotalPackets;
+ uint16_t mNumDataSource;
+
+ int8_t mMediaType;
+
+ uint8_t *mInputBuffer;
+ string mInputFile;
+ sp<DataSource> mSource;
+ FILE *mFpInput;
+ ATSParser *mParser;
+};
+
+TEST_P(Mpeg2tsUnitTest, MediaInfoTest) {
+ bool videoFound = false;
+ bool audioFound = false;
+ bool metaDataFound = false;
+ bool syncPointPresent = false;
+
+ int16_t totalDataSource = 0;
+ int32_t val32 = 0;
+ uint8_t numDataSource = 0;
+ uint8_t packet[kTSPacketSize];
+ ssize_t numBytesRead = -1;
+
+ ATSParser::SyncEvent event(mOffset);
+ static const ATSParser::SourceType mediaType[] = {ATSParser::VIDEO, ATSParser::AUDIO,
+ ATSParser::META, ATSParser::NUM_SOURCE_TYPES};
+ const uint32_t nMediaTypes = sizeof(mediaType) / sizeof(mediaType[0]);
+
+ while ((numBytesRead = mSource->readAt(mOffset, packet, kTSPacketSize)) == kTSPacketSize) {
+ ASSERT_TRUE(packet[0] == kTSSyncByte) << "Sync byte error!";
+
+ // pid is 13 bits
+ uint16_t pid = (packet[1] + (packet[2] << 8)) & kPIDMask;
+ ASSERT_TRUE(pid <= kPIDMaxValue) << "Invalid PID: " << pid;
+
+ status_t err = mParser->feedTSPacket(packet, kTSPacketSize, &event);
+ ASSERT_EQ(err, (status_t)OK) << "Unable to feed TS packet!";
+
+ mOffset += numBytesRead;
+ for (int i = 0; i < nMediaTypes; i++) {
+ if (mParser->hasSource(mediaType[i])) {
+ switch (mediaType[i]) {
+ case ATSParser::VIDEO:
+ videoFound = true;
+ break;
+ case ATSParser::AUDIO:
+ audioFound = true;
+ break;
+ case ATSParser::META:
+ metaDataFound = true;
+ break;
+ case ATSParser::NUM_SOURCE_TYPES:
+ numDataSource = 3;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ if (videoFound && audioFound && metaDataFound && (numDataSource == 3)) break;
+ }
+
+ for (int i = 0; i < nMediaTypes; i++) {
+ ATSParser::SourceType currentMediaType = mediaType[i];
+ if (mParser->hasSource(currentMediaType)) {
+ if (event.hasReturnedData()) {
+ syncPointPresent = true;
+ sp<AnotherPacketSource> syncPacketSource = event.getMediaSource();
+ ASSERT_NE(syncPacketSource, nullptr)
+ << "Cannot get sync source for media type: " << currentMediaType;
+
+ status_t err = syncPacketSource->start();
+ ASSERT_EQ(err, (status_t)OK) << "Error returned while starting!";
+
+ sp<MetaData> format = syncPacketSource->getFormat();
+ ASSERT_NE(format, nullptr) << "Unable to get the format of the source packet!";
+
+ MediaBufferBase *buf;
+ syncPacketSource->read(&buf, nullptr);
+ ASSERT_NE(buf, nullptr) << "Failed to read sync packet source data";
+
+ MetaDataBase &inMeta = buf->meta_data();
+ bool status = inMeta.findInt32(kKeyIsSyncFrame, &val32);
+ ASSERT_EQ(status, true) << "Sync frame key is not set";
+
+ status = inMeta.findInt32(kKeyCryptoMode, &val32);
+ ASSERT_EQ(status, false) << "Invalid packet, found scrambled packets!";
+
+ err = syncPacketSource->stop();
+ ASSERT_EQ(err, (status_t)OK) << "Error returned while stopping!";
+ }
+ sp<AnotherPacketSource> packetSource = mParser->getSource(currentMediaType);
+ ASSERT_NE(packetSource, nullptr)
+ << "Cannot get source for media type: " << currentMediaType;
+
+ status_t err = packetSource->start();
+ ASSERT_EQ(err, (status_t)OK) << "Error returned while starting!";
+ sp<MetaData> format = packetSource->getFormat();
+ ASSERT_NE(format, nullptr) << "Unable to get the format of the packet!";
+
+ err = packetSource->stop();
+ ASSERT_EQ(err, (status_t)OK) << "Error returned while stopping!";
+ }
+ }
+
+ ASSERT_EQ(videoFound, bool(mMediaType & kVideoPresent)) << "No Video packets found!";
+ ASSERT_EQ(audioFound, bool(mMediaType & kAudioPresent)) << "No Audio packets found!";
+ ASSERT_EQ(metaDataFound, bool(mMediaType & kMetaDataPresent)) << "No meta data found!";
+
+ if (videoFound || audioFound) {
+ ASSERT_TRUE(syncPointPresent) << "No sync points found for audio/video";
+ }
+
+ if (videoFound) totalDataSource += 1;
+ if (audioFound) totalDataSource += 1;
+ if (metaDataFound) totalDataSource += 1;
+
+ ASSERT_TRUE(totalDataSource == mNumDataSource)
+ << "Expected " << mNumDataSource << " data sources, found " << totalDataSource;
+ if (numDataSource == 3) {
+ ASSERT_EQ(numDataSource, mNumDataSource)
+ << "Expected " << mNumDataSource << " data sources, found " << totalDataSource;
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ infoTest, Mpeg2tsUnitTest,
+ ::testing::Values(make_tuple("crowd_1920x1080_25fps_6700kbps_h264.ts", 0x01, 1),
+ make_tuple("segment000001.ts", 0x03, 2),
+ make_tuple("bbb_44100hz_2ch_128kbps_mp3_5mins.ts", 0x02, 1)));
+
+int32_t main(int argc, char **argv) {
+ gEnv = new Mpeg2tsUnitTestEnvironment();
+ ::testing::AddGlobalTestEnvironment(gEnv);
+ ::testing::InitGoogleTest(&argc, argv);
+ uint8_t status = gEnv->initFromOptions(argc, argv);
+ if (status == 0) {
+ status = RUN_ALL_TESTS();
+ ALOGV("Mpeg2tsUnit Test Result = %d\n", status);
+ }
+ return status;
+}
diff --git a/media/libstagefright/mpeg2ts/test/Mpeg2tsUnitTestEnvironment.h b/media/libstagefright/mpeg2ts/test/Mpeg2tsUnitTestEnvironment.h
new file mode 100644
index 0000000..9e41db7
--- /dev/null
+++ b/media/libstagefright/mpeg2ts/test/Mpeg2tsUnitTestEnvironment.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 __MPEG2TS_UNIT_TEST_ENVIRONMENT_H__
+#define __MPEG2TS_UNIT_TEST_ENVIRONMENT_H__
+
+#include <gtest/gtest.h>
+
+#include <getopt.h>
+
+using namespace std;
+
+class Mpeg2tsUnitTestEnvironment : public::testing::Environment {
+ public:
+ Mpeg2tsUnitTestEnvironment() : 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 Mpeg2tsUnitTestEnvironment::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 // __MPEG2TS_UNIT_TEST_ENVIRONMENT_H__
diff --git a/media/libstagefright/mpeg2ts/test/README.md b/media/libstagefright/mpeg2ts/test/README.md
new file mode 100644
index 0000000..237ce72
--- /dev/null
+++ b/media/libstagefright/mpeg2ts/test/README.md
@@ -0,0 +1,38 @@
+## Media Testing ##
+---
+#### Mpeg2TS Unit Test :
+The Mpeg2TS Unit Test Suite validates the functionality of the libraries present in Mpeg2TS.
+
+Run the following steps to build the test suite:
+```
+mmm frameworks/av/media/libstagefright/mpeg2ts/test/
+```
+
+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/Mpeg2tsUnitTest/Mpeg2tsUnitTest /data/local/tmp/
+
+To test 32-bit binary push binaries from nativetest.
+
+adb push ${OUT}/data/nativetest/Mpeg2tsUnitTest/Mpeg2tsUnitTest /data/local/tmp/
+
+The resource file for the tests is taken from [here](https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/mpeg2ts/test/Mpeg2tsUnitTest.zip ).
+Download, unzip and push these files into device for testing.
+
+```
+adb push Mpeg2tsUnitTestRes/. /data/local/tmp/
+```
+
+usage: Mpeg2tsUnitTest -P \<path_to_folder\>
+```
+adb shell /data/local/tmp/Mpeg2tsUnitTest -P /data/local/tmp/Mpeg2tsUnitTestRes/
+```
+Alternatively, the test can also be run using atest command.
+
+```
+atest Mpeg2tsUnitTest -- --enable-module-dynamic-download=true
+```
diff --git a/media/libstagefright/rtsp/NetworkUtils.cpp b/media/libstagefright/rtsp/NetworkUtils.cpp
index cc36b78..c053be8 100644
--- a/media/libstagefright/rtsp/NetworkUtils.cpp
+++ b/media/libstagefright/rtsp/NetworkUtils.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <unistd.h>
+
//#define LOG_NDEBUG 0
#define LOG_TAG "NetworkUtils"
#include <utils/Log.h>
diff --git a/media/libstagefright/tests/writer/Android.bp b/media/libstagefright/tests/writer/Android.bp
index 7e169cb..d058ed3 100644
--- a/media/libstagefright/tests/writer/Android.bp
+++ b/media/libstagefright/tests/writer/Android.bp
@@ -28,6 +28,7 @@
"libcutils",
"liblog",
"libutils",
+ "libmedia",
],
static_libs: [
diff --git a/media/libstagefright/tests/writer/WriterListener.h b/media/libstagefright/tests/writer/WriterListener.h
new file mode 100644
index 0000000..81f0a7c
--- /dev/null
+++ b/media/libstagefright/tests/writer/WriterListener.h
@@ -0,0 +1,45 @@
+/*
+ * 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 WRITER_LISTENER_H_
+#define WRITER_LISTENER_H_
+
+#include <mutex>
+
+#include <media/IMediaRecorderClient.h>
+#include <media/mediarecorder.h>
+
+using namespace android;
+using namespace std;
+
+class WriterListener : public BnMediaRecorderClient {
+ public:
+ WriterListener() : mSignaledSize(false), mSignaledDuration(false) {}
+
+ virtual void notify(int32_t msg, int32_t ext1, int32_t ext2) {
+ ALOGV("msg : %d, ext1 : %d, ext2 : %d", msg, ext1, ext2);
+ if (ext1 == MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED) {
+ mSignaledSize = true;
+ } else if (ext1 == MEDIA_RECORDER_INFO_MAX_DURATION_REACHED) {
+ mSignaledDuration = true;
+ }
+ }
+
+ volatile bool mSignaledSize;
+ volatile bool mSignaledDuration;
+};
+
+#endif // WRITER_LISTENER_H_
diff --git a/media/libstagefright/tests/writer/WriterTest.cpp b/media/libstagefright/tests/writer/WriterTest.cpp
index ff063e3..3fa2aa6 100644
--- a/media/libstagefright/tests/writer/WriterTest.cpp
+++ b/media/libstagefright/tests/writer/WriterTest.cpp
@@ -87,36 +87,37 @@
"bbb_mpeg4_352x288_512kbps_30fps.info", 352, 288, false},
};
-class WriterTest : public ::testing::TestWithParam<pair<string, int32_t>> {
+class WriterTest {
public:
WriterTest() : mWriter(nullptr), mFileMeta(nullptr), mCurrentTrack(nullptr) {}
~WriterTest() {
- if (mWriter) {
- mWriter.clear();
- mWriter = nullptr;
- }
if (mFileMeta) {
mFileMeta.clear();
mFileMeta = nullptr;
}
if (mCurrentTrack) {
+ mCurrentTrack->stop();
mCurrentTrack.clear();
mCurrentTrack = nullptr;
}
+ if (mWriter) {
+ mWriter.clear();
+ mWriter = nullptr;
+ }
+ mBufferInfo.clear();
+ if (mInputStream.is_open()) mInputStream.close();
}
- virtual void SetUp() override {
+ void setupWriterType(string writerFormat) {
mNumCsds = 0;
mInputFrameId = 0;
mWriterName = unknown_comp;
mDisableTest = false;
-
static const std::map<std::string, standardWriters> mapWriter = {
{"ogg", OGG}, {"aac", AAC}, {"aac_adts", AAC_ADTS}, {"webm", WEBM},
{"mpeg4", MPEG4}, {"amrnb", AMR_NB}, {"amrwb", AMR_WB}, {"mpeg2Ts", MPEG2TS}};
// Find the component type
- string writerFormat = GetParam().first;
if (mapWriter.find(writerFormat) != mapWriter.end()) {
mWriterName = mapWriter.at(writerFormat);
}
@@ -126,11 +127,6 @@
}
}
- virtual void TearDown() override {
- mBufferInfo.clear();
- if (mInputStream.is_open()) mInputStream.close();
- }
-
void getInputBufferInfo(string inputFileName, string inputInfo);
int32_t createWriter(int32_t fd);
@@ -161,6 +157,12 @@
vector<BufferInfo> mBufferInfo;
};
+class WriteFunctionalityTest : public WriterTest,
+ public ::testing::TestWithParam<pair<string, int32_t>> {
+ public:
+ virtual void SetUp() override { setupWriterType(GetParam().first); }
+};
+
void WriterTest::getInputBufferInfo(string inputFileName, string inputInfo) {
std::ifstream eleInfo;
eleInfo.open(inputInfo.c_str());
@@ -270,7 +272,7 @@
return;
}
-TEST_P(WriterTest, CreateWriterTest) {
+TEST_P(WriteFunctionalityTest, CreateWriterTest) {
if (mDisableTest) return;
ALOGV("Tests the creation of writers");
@@ -284,7 +286,7 @@
<< "Failed to create writer for output format:" << GetParam().first;
}
-TEST_P(WriterTest, WriterTest) {
+TEST_P(WriteFunctionalityTest, WriterTest) {
if (mDisableTest) return;
ALOGV("Checks if for a given input, a valid muxed file has been created or not");
@@ -321,7 +323,7 @@
close(fd);
}
-TEST_P(WriterTest, PauseWriterTest) {
+TEST_P(WriteFunctionalityTest, PauseWriterTest) {
if (mDisableTest) return;
ALOGV("Validates the pause() api of writers");
@@ -378,7 +380,7 @@
close(fd);
}
-TEST_P(WriterTest, MultiStartStopPauseTest) {
+TEST_P(WriteFunctionalityTest, MultiStartStopPauseTest) {
// TODO: (b/144821804)
// Enable the test for MPE2TS writer
if (mDisableTest || mWriterName == standardWriters::MPEG2TS) return;
@@ -451,9 +453,112 @@
close(fd);
}
+class ListenerTest : public WriterTest,
+ public ::testing::TestWithParam<
+ tuple<string /* writerFormat*/, int32_t /* inputFileIdx*/,
+ float /* FileSizeLimit*/, float /* FileDurationLimit*/>> {
+ public:
+ virtual void SetUp() override {
+ tuple<string, int32_t, float, float> params = GetParam();
+ setupWriterType(get<0>(params));
+ }
+};
+
+TEST_P(ListenerTest, SetMaxFileLimitsTest) {
+ if (mDisableTest) return;
+ ALOGV("Validates writer when max file limits are set");
+
+ tuple<string, int32_t, float, float> params = GetParam();
+ string writerFormat = get<0>(params);
+ string outputFile = OUTPUT_FILE_NAME;
+ int32_t fd =
+ open(outputFile.c_str(), 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_t)OK, status) << "Failed to create writer for output format:" << writerFormat;
+
+ string inputFile = gEnv->getRes();
+ string inputInfo = gEnv->getRes();
+ configFormat param;
+ bool isAudio;
+ int32_t inputFileIdx = get<1>(params);
+ getFileDetails(inputFile, inputInfo, param, isAudio, inputFileIdx);
+ 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 " << writerFormat << "Writer";
+
+ // Read file properties
+ struct stat buf;
+ status = stat(inputFile.c_str(), &buf);
+ ASSERT_EQ(0, status);
+
+ float fileSizeLimit = get<2>(params);
+ float fileDurationLimit = get<3>(params);
+ int64_t maxFileSize = 0;
+ int64_t maxFileDuration = 0;
+
+ size_t inputFileSize = buf.st_size;
+ int64_t lastFrameTimeStampUs = mBufferInfo[mBufferInfo.size() - 1].timeUs;
+ if (fileSizeLimit > 0) {
+ maxFileSize = (int64_t)(fileSizeLimit * inputFileSize);
+ mWriter->setMaxFileSize(maxFileSize);
+ }
+ if (fileDurationLimit > 0) {
+ maxFileDuration = (int64_t)(fileDurationLimit * lastFrameTimeStampUs);
+ mWriter->setMaxFileDuration(maxFileDuration);
+ }
+
+ sp<WriterListener> listener = new WriterListener();
+ ASSERT_NE(listener, nullptr) << "unable to allocate listener";
+
+ mWriter->setListener(listener);
+ status = mWriter->start(mFileMeta.get());
+
+ ASSERT_EQ((status_t)OK, status);
+ status = sendBuffersToWriter(mInputStream, mBufferInfo, mInputFrameId, mCurrentTrack, 0,
+ mBufferInfo.size(), false, listener);
+ ASSERT_EQ((status_t)OK, status) << writerFormat << " writer failed";
+ ASSERT_TRUE(mWriter->reachedEOS()) << "EOS not signalled.";
+
+ mCurrentTrack->stop();
+ status = mWriter->stop();
+ ASSERT_EQ((status_t)OK, status) << "Failed to stop the writer";
+ close(fd);
+
+ if (maxFileSize <= 0) {
+ ASSERT_FALSE(listener->mSignaledSize);
+ } else if (maxFileDuration <= 0) {
+ ASSERT_FALSE(listener->mSignaledDuration);
+ } else if (maxFileSize > 0 && maxFileDuration <= 0) {
+ ASSERT_TRUE(listener->mSignaledSize);
+ } else if (maxFileDuration > 0 && maxFileSize <= 0) {
+ ASSERT_TRUE(listener->mSignaledDuration);
+ } else {
+ ASSERT_TRUE(listener->mSignaledSize || listener->mSignaledDuration);
+ }
+
+ if (maxFileSize > 0) {
+ struct stat buf;
+ status = stat(outputFile.c_str(), &buf);
+ ASSERT_EQ(0, status);
+ ASSERT_LE(buf.st_size, maxFileSize);
+ }
+}
+
+// TODO: (b/150923387)
+// Add WEBM input
+INSTANTIATE_TEST_SUITE_P(
+ ListenerTestAll, ListenerTest,
+ ::testing::Values(make_tuple("ogg", 0, 0.7, 0.3), make_tuple("aac", 1, 0.6, 0.7),
+ make_tuple("mpeg4", 1, 0.4, 0.3), make_tuple("amrnb", 3, 0.2, 0.6),
+ make_tuple("amrwb", 4, 0.5, 0.5), make_tuple("mpeg2Ts", 1, 0.2, 1)));
+
// TODO: (b/144476164)
// Add AAC_ADTS, FLAC, AV1 input
-INSTANTIATE_TEST_SUITE_P(WriterTestAll, WriterTest,
+INSTANTIATE_TEST_SUITE_P(WriterTestAll, WriteFunctionalityTest,
::testing::Values(make_pair("ogg", 0), make_pair("webm", 0),
make_pair("aac", 1), make_pair("mpeg4", 1),
make_pair("amrnb", 3), make_pair("amrwb", 4),
diff --git a/media/libstagefright/tests/writer/WriterUtility.cpp b/media/libstagefright/tests/writer/WriterUtility.cpp
index f24ccb6..a3043fe 100644
--- a/media/libstagefright/tests/writer/WriterUtility.cpp
+++ b/media/libstagefright/tests/writer/WriterUtility.cpp
@@ -24,9 +24,16 @@
int32_t sendBuffersToWriter(ifstream &inputStream, vector<BufferInfo> &bufferInfo,
int32_t &inputFrameId, sp<MediaAdapter> ¤tTrack, int32_t offset,
- int32_t range, bool isPaused) {
+ int32_t range, bool isPaused, sp<WriterListener> listener) {
while (1) {
if (inputFrameId >= (int)bufferInfo.size() || inputFrameId >= (offset + range)) break;
+ if (listener != nullptr) {
+ if (listener->mSignaledDuration || listener->mSignaledSize) {
+ ALOGV("Max File limit reached. No more buffers will be sent to the writer");
+ break;
+ }
+ }
+
int32_t size = bufferInfo[inputFrameId].size;
char *data = (char *)malloc(size);
if (!data) {
diff --git a/media/libstagefright/tests/writer/WriterUtility.h b/media/libstagefright/tests/writer/WriterUtility.h
index cdd6246..5e19973 100644
--- a/media/libstagefright/tests/writer/WriterUtility.h
+++ b/media/libstagefright/tests/writer/WriterUtility.h
@@ -27,8 +27,7 @@
#include <media/stagefright/MediaAdapter.h>
-using namespace android;
-using namespace std;
+#include "WriterListener.h"
#define CODEC_CONFIG_FLAG 32
@@ -43,7 +42,8 @@
int32_t sendBuffersToWriter(ifstream &inputStream, vector<BufferInfo> &bufferInfo,
int32_t &inputFrameId, sp<MediaAdapter> ¤tTrack, int32_t offset,
- int32_t range, bool isPaused = false);
+ int32_t range, bool isPaused = false,
+ sp<WriterListener> listener = nullptr);
int32_t writeHeaderBuffers(ifstream &inputStream, vector<BufferInfo> &bufferInfo,
int32_t &inputFrameId, sp<AMessage> &format, int32_t numCsds);
diff --git a/media/libstagefright/timedtext/TextDescriptions.cpp b/media/libstagefright/timedtext/TextDescriptions.cpp
index 0dc7722..2c2d11d 100644
--- a/media/libstagefright/timedtext/TextDescriptions.cpp
+++ b/media/libstagefright/timedtext/TextDescriptions.cpp
@@ -445,51 +445,75 @@
| *(tmpData + 10) << 8 | *(tmpData + 11);
parcel->writeInt32(rgba);
+ // tx3g box contains class FontTableBox() which extends ftab box
+ // This information is part of the 3gpp Timed Text Format
+ // Specification#: 26.245 / Section: 5.16(Sample Description Format)
+ // https://www.3gpp.org/ftp/Specs/archive/26_series/26.245/
+
tmpData += 12;
remaining -= 12;
- if (remaining < 2) {
+ if (remaining < 8) {
return OK;
}
- size_t dataPos = parcel->dataPosition();
-
- parcel->writeInt32(KEY_STRUCT_FONT_LIST);
- uint16_t count = U16_AT(tmpData);
- parcel->writeInt32(count);
-
- tmpData += 2;
- remaining -= 2;
-
- for (int i = 0; i < count; i++) {
- if (remaining < 3) {
- // roll back
- parcel->setDataPosition(dataPos);
- return OK;
- }
- // font ID
- parcel->writeInt32(U16_AT(tmpData));
-
- // font name length
- parcel->writeInt32(*(tmpData + 2));
-
- size_t len = *(tmpData + 2);
-
- tmpData += 3;
- remaining -= 3;
-
- if (remaining < len) {
- // roll back
- parcel->setDataPosition(dataPos);
- return OK;
- }
-
- parcel->write(tmpData, len);
- tmpData += len;
- remaining -= len;
+ size_t subChunkSize = U32_AT(tmpData);
+ if(remaining < subChunkSize) {
+ return OK;
}
- // there is a "DisparityBox" after this according to the spec, but we ignore it
+ uint32_t subChunkType = U32_AT(tmpData + 4);
+
+ if (subChunkType == FOURCC('f', 't', 'a', 'b'))
+ {
+ tmpData += 8;
+ size_t subChunkRemaining = subChunkSize - 8;
+
+ if(subChunkRemaining < 2) {
+ return OK;
+ }
+ size_t dataPos = parcel->dataPosition();
+
+ parcel->writeInt32(KEY_STRUCT_FONT_LIST);
+ uint16_t count = U16_AT(tmpData);
+ parcel->writeInt32(count);
+
+ tmpData += 2;
+ subChunkRemaining -= 2;
+
+ for (int i = 0; i < count; i++) {
+ if (subChunkRemaining < 3) {
+ // roll back
+ parcel->setDataPosition(dataPos);
+ return OK;
+ }
+ // font ID
+ parcel->writeInt32(U16_AT(tmpData));
+
+ // font name length
+ size_t len = *(tmpData + 2);
+
+ parcel->writeInt32(len);
+
+ tmpData += 3;
+ subChunkRemaining -=3;
+
+ if (subChunkRemaining < len) {
+ // roll back
+ parcel->setDataPosition(dataPos);
+ return OK;
+ }
+
+ parcel->writeByteArray(len, tmpData);
+ tmpData += len;
+ subChunkRemaining -= len;
+ }
+ tmpData += subChunkRemaining;
+ remaining -= subChunkSize;
+ } else {
+ tmpData += subChunkSize;
+ remaining -= subChunkSize;
+ }
break;
}
default:
diff --git a/media/libstagefright/timedtext/test/Android.bp b/media/libstagefright/timedtext/test/Android.bp
new file mode 100644
index 0000000..36f8891
--- /dev/null
+++ b/media/libstagefright/timedtext/test/Android.bp
@@ -0,0 +1,53 @@
+/*
+ * 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: "TimedTextUnitTest",
+ gtest: true,
+
+ srcs: [
+ "TimedTextUnitTest.cpp",
+ ],
+
+ static_libs: [
+ "libstagefright_timedtext",
+ "libstagefright_foundation",
+ ],
+
+ include_dirs: [
+ "frameworks/av/media/libstagefright",
+ ],
+
+ shared_libs: [
+ "liblog",
+ "libmedia",
+ "libbinder",
+ ],
+
+ cflags: [
+ "-Wno-multichar",
+ "-Werror",
+ "-Wall",
+ ],
+
+ sanitize: {
+ cfi: true,
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ },
+}
diff --git a/media/libstagefright/timedtext/test/AndroidTest.xml b/media/libstagefright/timedtext/test/AndroidTest.xml
new file mode 100644
index 0000000..3654e23
--- /dev/null
+++ b/media/libstagefright/timedtext/test/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Test module config for TimedText unit test">
+ <option name="test-suite-tag" value="TimedTextUnitTest" />
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="TimedTextUnitTest->/data/local/tmp/TimedTextUnitTest" />
+ <option name="push-file"
+ key="https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/timedtext/test/TimedTextUnitTest.zip?unzip=true"
+ value="/data/local/tmp/TimedTextUnitTestRes/" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="TimedTextUnitTest" />
+ <option name="native-test-flag" value="-P /data/local/tmp/TimedTextUnitTestRes/" />
+ </test>
+</configuration>
diff --git a/media/libstagefright/timedtext/test/README.md b/media/libstagefright/timedtext/test/README.md
new file mode 100644
index 0000000..3a774bd
--- /dev/null
+++ b/media/libstagefright/timedtext/test/README.md
@@ -0,0 +1,40 @@
+## Media Testing ##
+---
+#### TimedText Unit Test :
+The TimedText Unit Test Suite validates the TextDescription class available in libstagefright.
+
+Run the following steps to build the test suite:
+```
+m TimedTextUnitTest
+```
+
+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/TimedTextUnitTest/TimedTextUnitTest /data/local/tmp/
+```
+
+To test 32-bit binary push binaries from nativetest.
+```
+adb push ${OUT}/data/nativetest/TimedTextUnitTest/TimedTextUnitTest /data/local/tmp/
+```
+
+The resource file for the tests is taken from [here](https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/timedtext/test/TimedTextUnitTest.zip).
+Download, unzip and push these files into device for testing.
+
+```
+adb push TimedTextUnitTestRes/. /data/local/tmp/
+```
+
+usage: TimedTextUnitTest -P \<path_to_folder\>
+```
+adb shell /data/local/tmp/TimedTextUnitTest -P /data/local/tmp/TimedTextUnitTestRes/
+```
+Alternatively, the test can also be run using atest command.
+
+```
+atest TimedTextUnitTest -- --enable-module-dynamic-download=true
+```
diff --git a/media/libstagefright/timedtext/test/TimedTextTestEnvironment.h b/media/libstagefright/timedtext/test/TimedTextTestEnvironment.h
new file mode 100644
index 0000000..52280c1
--- /dev/null
+++ b/media/libstagefright/timedtext/test/TimedTextTestEnvironment.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TIMEDTEXT_TEST_ENVIRONMENT_H__
+#define __TIMEDTEXT_TEST_ENVIRONMENT_H__
+
+#include <gtest/gtest.h>
+
+#include <getopt.h>
+
+using namespace std;
+
+class TimedTextTestEnvironment : public ::testing::Environment {
+ public:
+ TimedTextTestEnvironment() : 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 TimedTextTestEnvironment::initFromOptions(int argc, char **argv) {
+ static struct option options[] = {{"res", required_argument, 0, 'P'}, {0, 0, 0, 0}};
+
+ while (true) {
+ int index = 0;
+ int c = getopt_long(argc, argv, "P:", options, &index);
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 'P':
+ setRes(optarg);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (optind < argc) {
+ fprintf(stderr,
+ "unrecognized option: %s\n\n"
+ "usage: %s <gtest options> <test options>\n\n"
+ "test options are:\n\n"
+ "-P, --path: Resource files directory location\n",
+ argv[optind ?: 1], argv[0]);
+ return 2;
+ }
+ return 0;
+}
+
+#endif // __TIMEDTEXT_TEST_ENVIRONMENT_H__
diff --git a/media/libstagefright/timedtext/test/TimedTextUnitTest.cpp b/media/libstagefright/timedtext/test/TimedTextUnitTest.cpp
new file mode 100644
index 0000000..d85ae39
--- /dev/null
+++ b/media/libstagefright/timedtext/test/TimedTextUnitTest.cpp
@@ -0,0 +1,379 @@
+/*
+ * 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 "TimedTextUnitTest"
+#include <utils/Log.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <fstream>
+
+#include <binder/Parcel.h>
+#include <media/stagefright/foundation/AString.h>
+#include <media/stagefright/foundation/ByteUtils.h>
+
+#include "timedtext/TextDescriptions.h"
+
+#include "TimedTextTestEnvironment.h"
+
+constexpr int32_t kStartTimeMs = 10000;
+
+enum {
+ // These keys must be in sync with the keys in
+ // frameworks/av/media/libstagefright/timedtext/TextDescriptions.h
+ KEY_DISPLAY_FLAGS = 1,
+ KEY_STYLE_FLAGS = 2,
+ KEY_BACKGROUND_COLOR_RGBA = 3,
+ KEY_HIGHLIGHT_COLOR_RGBA = 4,
+ KEY_SCROLL_DELAY = 5,
+ KEY_WRAP_TEXT = 6,
+ KEY_START_TIME = 7,
+ KEY_STRUCT_BLINKING_TEXT_LIST = 8,
+ KEY_STRUCT_FONT_LIST = 9,
+ KEY_STRUCT_HIGHLIGHT_LIST = 10,
+ KEY_STRUCT_HYPER_TEXT_LIST = 11,
+ KEY_STRUCT_KARAOKE_LIST = 12,
+ KEY_STRUCT_STYLE_LIST = 13,
+ KEY_STRUCT_TEXT_POS = 14,
+ KEY_STRUCT_JUSTIFICATION = 15,
+ KEY_STRUCT_TEXT = 16,
+
+ KEY_GLOBAL_SETTING = 101,
+ KEY_LOCAL_SETTING = 102,
+ KEY_START_CHAR = 103,
+ KEY_END_CHAR = 104,
+ KEY_FONT_ID = 105,
+ KEY_FONT_SIZE = 106,
+ KEY_TEXT_COLOR_RGBA = 107,
+};
+
+struct FontInfo {
+ int32_t displayFlag = -1;
+ int32_t horizontalJustification = -1;
+ int32_t verticalJustification = -1;
+ int32_t rgbaBackground = -1;
+ int32_t leftPos = -1;
+ int32_t topPos = -1;
+ int32_t bottomPos = -1;
+ int32_t rightPos = -1;
+ int32_t startchar = -1;
+ int32_t endChar = -1;
+ int32_t fontId = -1;
+ int32_t faceStyle = -1;
+ int32_t fontSize = -1;
+ int32_t rgbaText = -1;
+ int32_t entryCount = -1;
+};
+
+struct FontRecord {
+ int32_t fontID = -1;
+ int32_t fontNameLength = -1;
+ const uint8_t *font = nullptr;
+};
+
+using namespace android;
+
+static TimedTextTestEnvironment *gEnv = nullptr;
+
+class TimedTextUnitTest : public ::testing::TestWithParam</*filename*/ string> {
+ public:
+ TimedTextUnitTest(){};
+
+ ~TimedTextUnitTest() {
+ if (mEleStream) mEleStream.close();
+ }
+
+ virtual void SetUp() override {
+ mInputFileName = gEnv->getRes() + GetParam();
+ mEleStream.open(mInputFileName, ifstream::binary);
+ ASSERT_EQ(mEleStream.is_open(), true) << "Failed to open " << GetParam();
+
+ struct stat buf;
+ status_t status = stat(mInputFileName.c_str(), &buf);
+ ASSERT_EQ(status, 0) << "Failed to get properties of input file: " << GetParam();
+ mFileSize = buf.st_size;
+ ALOGI("Size of the input file %s = %zu", GetParam().c_str(), mFileSize);
+ }
+
+ string mInputFileName;
+ size_t mFileSize;
+ ifstream mEleStream;
+};
+
+class SRTDescriptionTest : public TimedTextUnitTest {
+ public:
+ virtual void SetUp() override { TimedTextUnitTest::SetUp(); }
+};
+
+class Text3GPPDescriptionTest : public TimedTextUnitTest {
+ public:
+ virtual void SetUp() override { TimedTextUnitTest::SetUp(); }
+};
+
+TEST_P(SRTDescriptionTest, extractSRTDescriptionTest) {
+ char data[mFileSize];
+ mEleStream.read(data, sizeof(data));
+ ASSERT_EQ(mEleStream.gcount(), mFileSize);
+
+ Parcel parcel;
+ int32_t flag = TextDescriptions::OUT_OF_BAND_TEXT_SRT | TextDescriptions::LOCAL_DESCRIPTIONS;
+ status_t status = TextDescriptions::getParcelOfDescriptions((const uint8_t *)data, mFileSize,
+ flag, kStartTimeMs, &parcel);
+ ASSERT_EQ(status, 0) << "getParcelOfDescriptions returned error";
+ ALOGI("Size of the Parcel: %zu", parcel.dataSize());
+ ASSERT_GT(parcel.dataSize(), 0) << "Parcel is empty";
+
+ parcel.setDataPosition(0);
+ int32_t key = parcel.readInt32();
+ ASSERT_EQ(key, KEY_LOCAL_SETTING) << "Parcel has invalid key";
+
+ key = parcel.readInt32();
+ ASSERT_EQ(key, KEY_START_TIME) << "Parcel has invalid start time key";
+ ASSERT_EQ(parcel.readInt32(), kStartTimeMs) << "Parcel has invalid timings";
+
+ key = parcel.readInt32();
+ ASSERT_EQ(key, KEY_STRUCT_TEXT) << "Parcel has invalid struct text key";
+ ASSERT_EQ(parcel.readInt32(), mFileSize) << "Parcel has invalid text data";
+ int32_t fileSize = parcel.readInt32();
+ ASSERT_EQ(fileSize, mFileSize) << "Parcel has invalid file size value";
+ uint8_t tmpData[fileSize];
+ status = parcel.read((void *)tmpData, fileSize);
+ ASSERT_EQ(status, 0) << "Failed to read the data from parcel";
+ // To make sure end of parcel is reached
+ ASSERT_EQ(parcel.dataAvail(), 0) << "Parcel has some data left to read";
+}
+
+// This test uses the properties of tx3g box mentioned in 3GPP Timed Text Format
+// Specification#: 26.245 / Section: 5.16(Sample Description Format)
+// https://www.3gpp.org/ftp/Specs/archive/26_series/26.245/
+
+TEST_P(Text3GPPDescriptionTest, Text3GPPGlobalDescriptionTest) {
+ char data[mFileSize];
+ mEleStream.read(data, sizeof(data));
+ ASSERT_EQ(mEleStream.gcount(), mFileSize);
+
+ const uint8_t *tmpData = (const uint8_t *)data;
+ int32_t remaining = mFileSize;
+ FontInfo fontInfo;
+ vector<FontRecord> fontRecordEntries;
+
+ // Skipping the bytes containing information about the type of subbox(tx3g)
+ tmpData += 16;
+ remaining -= 16;
+
+ fontInfo.displayFlag = U32_AT(tmpData);
+ ALOGI("Display flag: %d", fontInfo.displayFlag);
+ fontInfo.horizontalJustification = tmpData[4];
+ ALOGI("Horizontal Justification: %d", fontInfo.horizontalJustification);
+ fontInfo.verticalJustification = tmpData[5];
+ ALOGI("Vertical Justification: %d", fontInfo.verticalJustification);
+ fontInfo.rgbaBackground =
+ *(tmpData + 6) << 24 | *(tmpData + 7) << 16 | *(tmpData + 8) << 8 | *(tmpData + 9);
+ ALOGI("rgba value of background: %d", fontInfo.rgbaBackground);
+
+ tmpData += 10;
+ remaining -= 10;
+
+ if (remaining >= 8) {
+ fontInfo.leftPos = U16_AT(tmpData);
+ ALOGI("Left: %d", fontInfo.leftPos);
+ fontInfo.topPos = U16_AT(tmpData + 2);
+ ALOGI("Top: %d", fontInfo.topPos);
+ fontInfo.bottomPos = U16_AT(tmpData + 4);
+ ALOGI("Bottom: %d", fontInfo.bottomPos);
+ fontInfo.rightPos = U16_AT(tmpData + 6);
+ ALOGI("Right: %d", fontInfo.rightPos);
+
+ tmpData += 8;
+ remaining -= 8;
+
+ if (remaining >= 12) {
+ fontInfo.startchar = U16_AT(tmpData);
+ ALOGI("Start character: %d", fontInfo.startchar);
+ fontInfo.endChar = U16_AT(tmpData + 2);
+ ALOGI("End character: %d", fontInfo.endChar);
+ fontInfo.fontId = U16_AT(tmpData + 4);
+ ALOGI("Value of font Identifier: %d", fontInfo.fontId);
+ fontInfo.faceStyle = *(tmpData + 6);
+ ALOGI("Face style flag : %d", fontInfo.faceStyle);
+ fontInfo.fontSize = *(tmpData + 7);
+ ALOGI("Size of the font: %d", fontInfo.fontSize);
+ fontInfo.rgbaText = *(tmpData + 8) << 24 | *(tmpData + 9) << 16 | *(tmpData + 10) << 8 |
+ *(tmpData + 11);
+ ALOGI("rgba value of the text: %d", fontInfo.rgbaText);
+
+ tmpData += 12;
+ remaining -= 12;
+
+ if (remaining >= 10) {
+ // Skipping the bytes containing information about the type of subbox(ftab)
+ fontInfo.entryCount = U16_AT(tmpData + 8);
+ ALOGI("Value of entry count: %d", fontInfo.entryCount);
+
+ tmpData += 10;
+ remaining -= 10;
+
+ for (int32_t i = 0; i < fontInfo.entryCount; i++) {
+ if (remaining < 3) break;
+ int32_t tempFontID = U16_AT(tmpData);
+ ALOGI("Font Id: %d", tempFontID);
+ int32_t tempFontNameLength = *(tmpData + 2);
+ ALOGI("Length of font name: %d", tempFontNameLength);
+
+ tmpData += 3;
+ remaining -= 3;
+
+ if (remaining < tempFontNameLength) break;
+ const uint8_t *tmpFont = tmpData;
+ char *tmpFontName = strndup((const char *)tmpFont, tempFontNameLength);
+ ASSERT_NE(tmpFontName, nullptr) << "Font Name is null";
+ ALOGI("FontName = %s", tmpFontName);
+ free(tmpFontName);
+ tmpData += tempFontNameLength;
+ remaining -= tempFontNameLength;
+ fontRecordEntries.push_back({tempFontID, tempFontNameLength, tmpFont});
+ }
+ }
+ }
+ }
+
+ Parcel parcel;
+ int32_t flag = TextDescriptions::IN_BAND_TEXT_3GPP | TextDescriptions::GLOBAL_DESCRIPTIONS;
+ status_t status = TextDescriptions::getParcelOfDescriptions((const uint8_t *)data, mFileSize,
+ flag, kStartTimeMs, &parcel);
+ ASSERT_EQ(status, 0) << "getParcelOfDescriptions returned error";
+ ALOGI("Size of the Parcel: %zu", parcel.dataSize());
+ ASSERT_GT(parcel.dataSize(), 0) << "Parcel is empty";
+
+ parcel.setDataPosition(0);
+ int32_t key = parcel.readInt32();
+ ASSERT_EQ(key, KEY_GLOBAL_SETTING) << "Parcel has invalid key";
+
+ key = parcel.readInt32();
+ ASSERT_EQ(key, KEY_DISPLAY_FLAGS) << "Parcel has invalid DISPLAY FLAGS Key";
+ ASSERT_EQ(parcel.readInt32(), fontInfo.displayFlag)
+ << "Parcel has invalid value of display flag";
+
+ key = parcel.readInt32();
+ ASSERT_EQ(key, KEY_STRUCT_JUSTIFICATION) << "Parcel has invalid STRUCT JUSTIFICATION key";
+ ASSERT_EQ(parcel.readInt32(), fontInfo.horizontalJustification)
+ << "Parcel has invalid value of Horizontal justification";
+ ASSERT_EQ(parcel.readInt32(), fontInfo.verticalJustification)
+ << "Parcel has invalid value of Vertical justification";
+
+ key = parcel.readInt32();
+ ASSERT_EQ(key, KEY_BACKGROUND_COLOR_RGBA) << "Parcel has invalid BACKGROUND COLOR key";
+ ASSERT_EQ(parcel.readInt32(), fontInfo.rgbaBackground)
+ << "Parcel has invalid rgba background color value";
+
+ if (parcel.dataAvail() == 0) {
+ ALOGV("Completed reading the parcel");
+ return;
+ }
+
+ key = parcel.readInt32();
+ ASSERT_EQ(key, KEY_STRUCT_TEXT_POS) << "Parcel has invalid STRUCT TEXT POSITION key";
+ ASSERT_EQ(parcel.readInt32(), fontInfo.leftPos)
+ << "Parcel has invalid rgba background color value";
+ ASSERT_EQ(parcel.readInt32(), fontInfo.topPos)
+ << "Parcel has invalid rgba background color value";
+ ASSERT_EQ(parcel.readInt32(), fontInfo.bottomPos)
+ << "Parcel has invalid rgba background color value";
+ ASSERT_EQ(parcel.readInt32(), fontInfo.rightPos)
+ << "Parcel has invalid rgba background color value";
+
+ if (parcel.dataAvail() == 0) {
+ ALOGV("Completed reading the parcel");
+ return;
+ }
+
+ key = parcel.readInt32();
+ ASSERT_EQ(key, KEY_STRUCT_STYLE_LIST) << "Parcel has invalid STRUCT STYLE LIST key";
+
+ key = parcel.readInt32();
+ ASSERT_EQ(key, KEY_START_CHAR) << "Parcel has invalid START CHAR key";
+ ASSERT_EQ(parcel.readInt32(), fontInfo.startchar)
+ << "Parcel has invalid value of start character";
+
+ key = parcel.readInt32();
+ ASSERT_EQ(key, KEY_END_CHAR) << "Parcel has invalid END CHAR key";
+ ASSERT_EQ(parcel.readInt32(), fontInfo.endChar) << "Parcel has invalid value of end character";
+
+ key = parcel.readInt32();
+ ASSERT_EQ(key, KEY_FONT_ID) << "Parcel has invalid FONT ID key";
+ ASSERT_EQ(parcel.readInt32(), fontInfo.fontId) << "Parcel has invalid value of font Id";
+
+ key = parcel.readInt32();
+ ASSERT_EQ(key, KEY_STYLE_FLAGS) << "Parcel has invalid STYLE FLAGS key";
+ ASSERT_EQ(parcel.readInt32(), fontInfo.faceStyle) << "Parcel has invalid value of style flags";
+
+ key = parcel.readInt32();
+ ASSERT_EQ(key, KEY_FONT_SIZE) << "Parcel has invalid FONT SIZE key";
+ ASSERT_EQ(parcel.readInt32(), fontInfo.fontSize) << "Parcel has invalid value of font size";
+
+ key = parcel.readInt32();
+ ASSERT_EQ(key, KEY_TEXT_COLOR_RGBA) << "Parcel has invalid TEXT COLOR RGBA key";
+ ASSERT_EQ(parcel.readInt32(), fontInfo.rgbaText) << "Parcel has invalid rgba text color value";
+
+ if (parcel.dataAvail() == 0) {
+ ALOGV("Completed reading the parcel");
+ return;
+ }
+
+ key = parcel.readInt32();
+ ASSERT_EQ(key, KEY_STRUCT_FONT_LIST) << "Parcel has invalid STRUCT FONT LIST key";
+ ASSERT_EQ(parcel.readInt32(), fontInfo.entryCount) << "Parcel has invalid value of entry count";
+ ASSERT_EQ(fontInfo.entryCount, fontRecordEntries.size())
+ << "Array size does not match expected number of entries";
+ for (int32_t i = 0; i < fontInfo.entryCount; i++) {
+ ASSERT_EQ(parcel.readInt32(), fontRecordEntries[i].fontID)
+ << "Parcel has invalid value of font Id";
+ ASSERT_EQ(parcel.readInt32(), fontRecordEntries[i].fontNameLength)
+ << "Parcel has invalid value of font name length";
+ uint8_t fontName[fontRecordEntries[i].fontNameLength];
+ // written with writeByteArray() writes count, then the actual data
+ ASSERT_EQ(parcel.readInt32(), fontRecordEntries[i].fontNameLength);
+ status = parcel.read((void *)fontName, fontRecordEntries[i].fontNameLength);
+ ASSERT_EQ(status, 0) << "Failed to read the font name from parcel";
+ ASSERT_EQ(memcmp(fontName, fontRecordEntries[i].font, fontRecordEntries[i].fontNameLength),
+ 0)
+ << "Parcel has invalid font";
+ }
+ // To make sure end of parcel is reached
+ ASSERT_EQ(parcel.dataAvail(), 0) << "Parcel has some data left to read";
+}
+
+INSTANTIATE_TEST_SUITE_P(TimedTextUnitTestAll, SRTDescriptionTest,
+ ::testing::Values(("sampleTest1.srt"),
+ ("sampleTest2.srt")));
+
+INSTANTIATE_TEST_SUITE_P(TimedTextUnitTestAll, Text3GPPDescriptionTest,
+ ::testing::Values(("tx3gBox1"),
+ ("tx3gBox2")));
+
+int main(int argc, char **argv) {
+ gEnv = new TimedTextTestEnvironment();
+ ::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/xmlparser/MediaCodecsXmlParser.cpp b/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
index d905b8d..3be5e74 100644
--- a/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
+++ b/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
@@ -21,6 +21,7 @@
#include <android-base/logging.h>
#include <android-base/macros.h>
+#include <android-base/properties.h>
#include <utils/Log.h>
#include <media/stagefright/MediaErrors.h>
@@ -38,8 +39,6 @@
namespace android {
-using MCXP = MediaCodecsXmlParser;
-
namespace {
bool fileExists(const std::string &path) {
@@ -118,8 +117,8 @@
}
}
-MCXP::StringSet parseCommaSeparatedStringSet(const char *s) {
- MCXP::StringSet result;
+MediaCodecsXmlParser::StringSet parseCommaSeparatedStringSet(const char *s) {
+ MediaCodecsXmlParser::StringSet result;
for (const char *ptr = s ? : ""; *ptr; ) {
const char *end = strchrnul(ptr, ',');
if (ptr != end) { // skip empty values
@@ -136,6 +135,23 @@
} // unnamed namespace
+std::vector<std::string> MediaCodecsXmlParser::getDefaultXmlNames() {
+ static constexpr char const* prefixes[] = {
+ "media_codecs",
+ "media_codecs_performance"
+ };
+ static std::vector<std::string> variants = {
+ android::base::GetProperty("ro.media.xml_variant.codecs", ""),
+ android::base::GetProperty("ro.media.xml_variant.codecs_performance", "")
+ };
+ static std::vector<std::string> names = {
+ prefixes[0] + variants[0] + ".xml",
+ prefixes[1] + variants[1] + ".xml"
+ };
+ return names;
+}
+
+
struct MediaCodecsXmlParser::Impl {
// status + error message
struct Result {
@@ -1509,7 +1525,7 @@
nodeInfo.attributeList.push_back(Attribute{"rank", rank});
}
nodeList->insert(std::make_pair(
- std::move(order), std::move(nodeInfo)));
+ order, std::move(nodeInfo)));
}
}
}
diff --git a/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h b/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h
index b666de4..e224452 100644
--- a/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h
+++ b/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h
@@ -33,13 +33,17 @@
class MediaCodecsXmlParser {
public:
- // Treblized media codec list will be located in /odm/etc or /vendor/etc.
+ // Treblized media codec list will be located in /product/etc, /odm/etc or
+ // /vendor/etc.
static std::vector<std::string> getDefaultSearchDirs() {
- return { "/odm/etc", "/vendor/etc", "/etc" };
+ return { "/product/etc",
+ "/odm/etc",
+ "/vendor/etc",
+ "/system/etc" };
}
- static std::vector<std::string> getDefaultXmlNames() {
- return { "media_codecs.xml", "media_codecs_performance.xml" };
- }
+
+ static std::vector<std::string> getDefaultXmlNames();
+
static constexpr char const* defaultProfilingResultsXmlPath =
"/data/misc/media/media_codecs_profiling_results.xml";
diff --git a/media/libstagefright/xmlparser/test/Android.bp b/media/libstagefright/xmlparser/test/Android.bp
new file mode 100644
index 0000000..6d97c96
--- /dev/null
+++ b/media/libstagefright/xmlparser/test/Android.bp
@@ -0,0 +1,52 @@
+/*
+ * 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: "XMLParserTest",
+ gtest: true,
+
+ srcs: [
+ "XMLParserTest.cpp",
+ ],
+
+ shared_libs: [
+ "liblog",
+ "libstagefright_xmlparser",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+ data: [":xmlparsertest_test_files",],
+
+ sanitize: {
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ cfi: true,
+ },
+}
+
+filegroup {
+ name: "xmlparsertest_test_files",
+ srcs: [
+ "testdata/media_codecs_unit_test.xml",
+ "testdata/media_codecs_unit_test_caller.xml",
+ ],
+}
diff --git a/media/libstagefright/xmlparser/test/AndroidTest.xml b/media/libstagefright/xmlparser/test/AndroidTest.xml
new file mode 100644
index 0000000..2e11b1b
--- /dev/null
+++ b/media/libstagefright/xmlparser/test/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?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 xml parser unit test">
+ <option name="test-suite-tag" value="XMLParserTest" />
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="media_codecs_unit_test.xml->/data/local/tmp/media_codecs_unit_test.xml" />
+ <option name="push" value="media_codecs_unit_test_caller.xml->/data/local/tmp/media_codecs_unit_test_caller.xml" />
+ <option name="push" value="XMLParserTest->/data/local/tmp/XMLParserTest" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="XMLParserTest" />
+ </test>
+</configuration>
diff --git a/media/libstagefright/xmlparser/test/README.md b/media/libstagefright/xmlparser/test/README.md
new file mode 100644
index 0000000..e9363fd
--- /dev/null
+++ b/media/libstagefright/xmlparser/test/README.md
@@ -0,0 +1,33 @@
+## Media Testing ##
+---
+#### XML Parser
+The XMLParser Test Suite validates the XMLParser available in libstagefright.
+
+Run the following steps to build the test suite:
+```
+m XMLParserTest
+```
+
+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/XMLParserTest/XMLParserTest /data/local/tmp/
+```
+
+To test 32-bit binary push binaries from nativetest.
+```
+adb push ${OUT}/data/nativetest/XMLParserTest/XMLParserTest /data/local/tmp/
+```
+
+usage: XMLParserTest
+```
+adb shell /data/local/tmp/XMLParserTest
+```
+Alternatively, the test can also be run using atest command.
+
+```
+atest XMLParserTest
+```
diff --git a/media/libstagefright/xmlparser/test/XMLParserTest.cpp b/media/libstagefright/xmlparser/test/XMLParserTest.cpp
new file mode 100644
index 0000000..9ddd374
--- /dev/null
+++ b/media/libstagefright/xmlparser/test/XMLParserTest.cpp
@@ -0,0 +1,392 @@
+/*
+ * 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 "XMLParserTest"
+
+#include <utils/Log.h>
+
+#include <fstream>
+
+#include <media/stagefright/xmlparser/MediaCodecsXmlParser.h>
+
+#include "XMLParserTestEnvironment.h"
+
+#define XML_FILE_NAME "media_codecs_unit_test_caller.xml"
+
+using namespace android;
+
+static XMLParserTestEnvironment *gEnv = nullptr;
+
+struct CodecProperties {
+ string codecName;
+ MediaCodecsXmlParser::CodecProperties codecProp;
+};
+
+struct RoleProperties {
+ string roleName;
+ string typeName;
+ string codecName;
+ bool isEncoder;
+ size_t order;
+ vector<pair<string, string>> attributeMap;
+};
+
+class XMLParseTest : public ::testing::Test {
+ public:
+ ~XMLParseTest() {
+ if (mEleStream.is_open()) mEleStream.close();
+ mInputDataVector.clear();
+ mInputRoleVector.clear();
+ }
+
+ virtual void SetUp() override { setUpDatabase(); }
+
+ void setUpDatabase();
+
+ void setCodecProperties(string codecName, bool isEncoder, int32_t order, set<string> quirkSet,
+ set<string> domainSet, set<string> variantSet, string typeName,
+ vector<pair<string, string>> domain, vector<string> aliases,
+ string rank);
+
+ void setRoleProperties(string roleName, bool isEncoder, int32_t order, string typeName,
+ string codecName, vector<pair<string, string>> domain);
+
+ void setServiceAttribute(map<string, string> serviceAttributeNameValuePair);
+
+ void printCodecMap(const MediaCodecsXmlParser::Codec mcodec);
+
+ void checkRoleMap(int32_t index, bool isEncoder, string typeName, string codecName,
+ vector<pair<string, string>> attrMap);
+
+ bool compareMap(const map<string, string> &lhs, const map<string, string> &rhs);
+
+ ifstream mEleStream;
+ MediaCodecsXmlParser mParser;
+ vector<CodecProperties> mInputDataVector;
+ vector<RoleProperties> mInputRoleVector;
+ map<string, string> mInputServiceAttributeMap;
+};
+
+void XMLParseTest::setUpDatabase() {
+ // The values set below are specific to test vector testdata/media_codecs_unit_test.xml
+ setCodecProperties("test1.decoder", false, 1, {"attribute::disabled", "quirk::quirk1"},
+ {"telephony"}, {}, "audio/mpeg", {}, {"alias1.decoder"}, "4");
+
+ setCodecProperties("test2.decoder", false, 2, {"quirk::quirk1"}, {}, {}, "audio/3gpp", {}, {},
+ "");
+
+ setCodecProperties("test3.decoder", false, 3, {}, {}, {}, "audio/amr-wb",
+ {
+ pair<string, string>("feature-feature1", "feature1Val"),
+ pair<string, string>("feature-feature2", "0"),
+ pair<string, string>("feature-feature3", "0"),
+ },
+ {}, "");
+
+ setCodecProperties("test4.decoder", false, 4, {}, {}, {}, "audio/flac",
+ {pair<string, string>("feature-feature1", "feature1Val")}, {}, "");
+
+ setCodecProperties("test5.decoder", false, 5, {"attribute::attributeQuirk1"}, {}, {},
+ "audio/g711-mlaw", {}, {}, "");
+
+ setCodecProperties("test6.decoder", false, 6, {}, {}, {"variant1", "variant2"},
+ "audio/mp4a-latm",
+ {pair<string, string>("variant1:::variant1Limit1-range",
+ "variant1Limit1Min-variant1Limit1Max"),
+ pair<string, string>("variant1:::variant1Limit2-range",
+ "variant1Limit2Low-variant1Limit2High"),
+ pair<string, string>("variant2:::variant2Limit1", "variant2Limit1Value")},
+ {}, "");
+
+ setCodecProperties(
+ "test7.decoder", false, 7, {}, {}, {}, "audio/vorbis",
+ {
+ pair<string, string>("-min-limit1", "limit1Min"),
+ /*pair<string, string>("limit1-in", "limit1In"),*/
+ pair<string, string>("limit2-range", "limit2Min-limit2Max"),
+ pair<string, string>("limit2-scale", "limit2Scale"),
+ pair<string, string>("limit3-default", "limit3Val3"),
+ pair<string, string>("limit3-ranges", "limit3Val1,limit3Val2,limit3Val3"),
+ },
+ {}, "");
+
+ setCodecProperties("test8.encoder", true, 8, {}, {}, {}, "audio/opus",
+ {pair<string, string>("max-limit1", "limit1Max")}, {}, "");
+
+ setRoleProperties("audio_decoder.mp3", false, 1, "audio/mpeg", "test1.decoder",
+ {pair<string, string>("attribute::disabled", "present"),
+ pair<string, string>("rank", "4")});
+
+ setRoleProperties("audio_decoder.amrnb", false, 2, "audio/3gpp", "test2.decoder", {});
+
+ setRoleProperties("audio_decoder.amrwb", false, 3, "audio/amr-wb", "test3.decoder",
+ {pair<string, string>("feature-feature1", "feature1Val"),
+ pair<string, string>("feature-feature2", "0"),
+ pair<string, string>("feature-feature3", "0")});
+
+ setRoleProperties("audio/flac", false, 4, "audio/flac", "test4.decoder",
+ {pair<string, string>("feature-feature1", "feature1Val")});
+
+ setRoleProperties("audio_decoder.g711mlaw", false, 5, "audio/g711-mlaw", "test5.decoder",
+ {pair<string, string>("attribute::attributeQuirk1", "present")});
+
+ setRoleProperties("audio_decoder.aac", false, 6, "audio/mp4a-latm", "test6.decoder",
+ {pair<string, string>("variant1:::variant1Limit1-range",
+ "variant1Limit1Min-variant1Limit1Max"),
+ pair<string, string>("variant1:::variant1Limit2-range",
+ "variant1Limit2Low-variant1Limit2High"),
+ pair<string, string>("variant2:::variant2Limit1", "variant2Limit1Value")});
+
+ setRoleProperties("audio_decoder.vorbis", false, 7, "audio/vorbis", "test7.decoder",
+ {pair<string, string>("-min-limit1", "limit1Min"),
+ /*pair<string, string>("limit1-in", "limit1In"),*/
+ pair<string, string>("limit2-range", "limit2Min-limit2Max"),
+ pair<string, string>("limit2-scale", "limit2Scale"),
+ pair<string, string>("limit3-default", "limit3Val3"),
+ pair<string, string>("limit3-ranges", "limit3Val1,limit3Val2,limit3Val3")});
+
+ setRoleProperties("audio_encoder.opus", true, 8, "audio/opus", "test8.encoder",
+ {pair<string, string>("max-limit1", "limit1Max")});
+
+ setServiceAttribute(
+ {pair<string, string>("domain-telephony", "0"), pair<string, string>("domain-tv", "0"),
+ pair<string, string>("setting2", "0"), pair<string, string>("variant-variant1", "0")});
+}
+
+bool XMLParseTest::compareMap(const map<string, string> &lhs, const map<string, string> &rhs) {
+ return lhs.size() == rhs.size() && equal(lhs.begin(), lhs.end(), rhs.begin());
+}
+
+void XMLParseTest::setCodecProperties(string codecName, bool isEncoder, int32_t order,
+ set<string> quirkSet, set<string> domainSet,
+ set<string> variantSet, string typeName,
+ vector<pair<string, string>> domain, vector<string> aliases,
+ string rank) {
+ map<string, string> AttributeMapDB;
+ for (const auto &AttrStr : domain) {
+ AttributeMapDB.insert(AttrStr);
+ }
+ map<string, MediaCodecsXmlParser::AttributeMap> TypeMapDataBase;
+ TypeMapDataBase.insert(
+ pair<string, MediaCodecsXmlParser::AttributeMap>(typeName, AttributeMapDB));
+ CodecProperties codecProperty;
+ codecProperty.codecName = codecName;
+ codecProperty.codecProp.isEncoder = isEncoder;
+ codecProperty.codecProp.order = order;
+ codecProperty.codecProp.quirkSet = quirkSet;
+ codecProperty.codecProp.domainSet = domainSet;
+ codecProperty.codecProp.variantSet = variantSet;
+ codecProperty.codecProp.typeMap = TypeMapDataBase;
+ codecProperty.codecProp.aliases = aliases;
+ codecProperty.codecProp.rank = rank;
+ mInputDataVector.push_back(codecProperty);
+}
+
+void XMLParseTest::setRoleProperties(string roleName, bool isEncoder, int32_t order,
+ string typeName, string codecName,
+ vector<pair<string, string>> attributeNameValuePair) {
+ struct RoleProperties roleProperty;
+ roleProperty.roleName = roleName;
+ roleProperty.typeName = typeName;
+ roleProperty.codecName = codecName;
+ roleProperty.isEncoder = isEncoder;
+ roleProperty.order = order;
+ roleProperty.attributeMap = attributeNameValuePair;
+ mInputRoleVector.push_back(roleProperty);
+}
+
+void XMLParseTest::setServiceAttribute(map<string, string> serviceAttributeNameValuePair) {
+ for (const auto &serviceAttrStr : serviceAttributeNameValuePair) {
+ mInputServiceAttributeMap.insert(serviceAttrStr);
+ }
+}
+
+void XMLParseTest::printCodecMap(const MediaCodecsXmlParser::Codec mcodec) {
+ const string &name = mcodec.first;
+ ALOGV("codec name = %s\n", name.c_str());
+ const MediaCodecsXmlParser::CodecProperties &properties = mcodec.second;
+ bool isEncoder = properties.isEncoder;
+ ALOGV("isEncoder = %d\n", isEncoder);
+ size_t order = properties.order;
+ ALOGV("order = %zu\n", order);
+ string rank = properties.rank;
+ ALOGV("rank = %s\n", rank.c_str());
+
+ for (auto &itrQuirkSet : properties.quirkSet) {
+ ALOGV("quirkSet= %s", itrQuirkSet.c_str());
+ }
+
+ for (auto &itrDomainSet : properties.domainSet) {
+ ALOGV("domainSet= %s", itrDomainSet.c_str());
+ }
+
+ for (auto &itrVariantSet : properties.variantSet) {
+ ALOGV("variantSet= %s", itrVariantSet.c_str());
+ }
+
+ map<string, MediaCodecsXmlParser::AttributeMap> TypeMap = properties.typeMap;
+ ALOGV("The TypeMap is :");
+
+ for (auto &itrTypeMap : TypeMap) {
+ ALOGV("itrTypeMap->first\t%s\t", itrTypeMap.first.c_str());
+
+ for (auto &itrAttributeMap : itrTypeMap.second) {
+ ALOGV("AttributeMap->first = %s", itrAttributeMap.first.c_str());
+ ALOGV("AttributeMap->second = %s", itrAttributeMap.second.c_str());
+ }
+ }
+}
+
+void XMLParseTest::checkRoleMap(int32_t index, bool isEncoder, string typeName, string codecName,
+ vector<pair<string, string>> AttributePairMap) {
+ ASSERT_EQ(isEncoder, mInputRoleVector.at(index).isEncoder)
+ << "Invalid RoleMap data. IsEncoder mismatch";
+ ASSERT_EQ(typeName, mInputRoleVector.at(index).typeName)
+ << "Invalid RoleMap data. typeName mismatch";
+ ASSERT_EQ(codecName, mInputRoleVector.at(index).codecName)
+ << "Invalid RoleMap data. codecName mismatch";
+
+ vector<pair<string, string>>::iterator itr_attributeMapDB =
+ (mInputRoleVector.at(index).attributeMap).begin();
+ vector<pair<string, string>>::iterator itr_attributeMap = AttributePairMap.begin();
+ for (; itr_attributeMap != AttributePairMap.end() &&
+ itr_attributeMapDB != mInputRoleVector.at(index).attributeMap.end();
+ ++itr_attributeMap, ++itr_attributeMapDB) {
+ string attributeName = itr_attributeMap->first;
+ string attributeNameDB = itr_attributeMapDB->first;
+ string attributevalue = itr_attributeMap->second;
+ string attributeValueDB = itr_attributeMapDB->second;
+ ASSERT_EQ(attributeName, attributeNameDB)
+ << "Invalid RoleMap data. Attribute name mismatch\t" << attributeName << " != "
+ << "attributeNameDB";
+ ASSERT_EQ(attributevalue, attributeValueDB)
+ << "Invalid RoleMap data. Attribute value mismatch\t" << attributevalue << " != "
+ << "attributeValueDB";
+ }
+}
+
+TEST_F(XMLParseTest, CodecMapParseTest) {
+ string inputFileName = gEnv->getRes() + XML_FILE_NAME;
+ mEleStream.open(inputFileName, ifstream::binary);
+ ASSERT_EQ(mEleStream.is_open(), true) << "Failed to open inputfile " << inputFileName;
+
+ mParser.parseXmlPath(inputFileName);
+ for (const MediaCodecsXmlParser::Codec &mcodec : mParser.getCodecMap()) {
+ printCodecMap(mcodec);
+ const MediaCodecsXmlParser::CodecProperties &properties = mcodec.second;
+ int32_t index = properties.order - 1;
+ ASSERT_GE(index, 0) << "Invalid order";
+ ASSERT_EQ(mInputDataVector.at(index).codecName, mcodec.first.c_str())
+ << "Invalid CodecMap data. codecName mismatch";
+ ASSERT_EQ(properties.isEncoder, mInputDataVector.at(index).codecProp.isEncoder)
+ << "Invalid CodecMap data. isEncoder mismatch";
+ ASSERT_EQ(properties.order, mInputDataVector.at(index).codecProp.order)
+ << "Invalid CodecMap data. order mismatch";
+
+ set<string> quirkSetDB = mInputDataVector.at(index).codecProp.quirkSet;
+ set<string> quirkSet = properties.quirkSet;
+ set<string> quirkDifference;
+ set_difference(quirkSetDB.begin(), quirkSetDB.end(), quirkSet.begin(), quirkSet.end(),
+ inserter(quirkDifference, quirkDifference.end()));
+ ASSERT_EQ(quirkDifference.size(), 0) << "CodecMap:quirk mismatch";
+
+ map<string, MediaCodecsXmlParser::AttributeMap> TypeMapDB =
+ mInputDataVector.at(index).codecProp.typeMap;
+ map<string, MediaCodecsXmlParser::AttributeMap> TypeMap = properties.typeMap;
+ map<string, MediaCodecsXmlParser::AttributeMap>::iterator itr_TypeMapDB = TypeMapDB.begin();
+ map<string, MediaCodecsXmlParser::AttributeMap>::iterator itr_TypeMap = TypeMap.begin();
+
+ ASSERT_EQ(TypeMapDB.size(), TypeMap.size())
+ << "Invalid CodecMap data. Typemap size mismatch";
+
+ for (; itr_TypeMap != TypeMap.end() && itr_TypeMapDB != TypeMapDB.end();
+ ++itr_TypeMap, ++itr_TypeMapDB) {
+ ASSERT_EQ(itr_TypeMap->first, itr_TypeMapDB->first)
+ << "Invalid CodecMap data. type mismatch";
+ bool flag = compareMap(itr_TypeMap->second, itr_TypeMapDB->second);
+ ASSERT_TRUE(flag) << "typeMap mismatch";
+ }
+ ASSERT_EQ(mInputDataVector.at(index).codecProp.rank, properties.rank)
+ << "Invalid CodecMap data. rank mismatch";
+ }
+}
+
+TEST_F(XMLParseTest, RoleMapParseTest) {
+ string inputFileName = gEnv->getRes() + XML_FILE_NAME;
+ mEleStream.open(inputFileName, ifstream::binary);
+ ASSERT_EQ(mEleStream.is_open(), true) << "Failed to open inputfile " << inputFileName;
+
+ mParser.parseXmlPath(inputFileName);
+
+ for (auto &mRole : mParser.getRoleMap()) {
+ typedef pair<string, string> Attribute;
+ const string &roleName = mRole.first;
+ ALOGV("Role map:name = %s\n", roleName.c_str());
+ const MediaCodecsXmlParser::RoleProperties &properties = mRole.second;
+ string type = properties.type;
+ ALOGV("Role map: type = %s\n", type.c_str());
+
+ bool isEncoder = properties.isEncoder;
+ ALOGV("Role map: isEncoder = %d\n", isEncoder);
+
+ multimap<size_t, MediaCodecsXmlParser::NodeInfo> nodeList = properties.nodeList;
+ multimap<size_t, MediaCodecsXmlParser::NodeInfo>::iterator itr_Node;
+ ALOGV("\nThe multimap nodeList is : \n");
+ for (itr_Node = nodeList.begin(); itr_Node != nodeList.end(); ++itr_Node) {
+ ALOGV("itr_Node->first=ORDER=\t%zu\t", itr_Node->first);
+ int32_t index = itr_Node->first - 1;
+ MediaCodecsXmlParser::NodeInfo nodePtr = itr_Node->second;
+ ALOGV("Role map:itr_Node->second.name = %s\n", nodePtr.name.c_str());
+ vector<Attribute> attrList = nodePtr.attributeList;
+ for (auto attrNameValueList = attrList.begin(); attrNameValueList != attrList.end();
+ ++attrNameValueList) {
+ ALOGV("Role map:nodePtr.attributeList->first = %s\n",
+ attrNameValueList->first.c_str());
+ ALOGV("Role map:nodePtr.attributeList->second = %s\n",
+ attrNameValueList->second.c_str());
+ }
+ checkRoleMap(index, isEncoder, properties.type, nodePtr.name.c_str(), attrList);
+ }
+ }
+}
+
+TEST_F(XMLParseTest, ServiceAttributeMapParseTest) {
+ string inputFileName = gEnv->getRes() + XML_FILE_NAME;
+ mEleStream.open(inputFileName, ifstream::binary);
+ ASSERT_EQ(mEleStream.is_open(), true) << "Failed to open inputfile " << inputFileName;
+
+ mParser.parseXmlPath(inputFileName);
+ const auto serviceAttributeMap = mParser.getServiceAttributeMap();
+ for (const auto &attributePair : serviceAttributeMap) {
+ ALOGV("serviceAttribute.key = %s \t serviceAttribute.value = %s",
+ attributePair.first.c_str(), attributePair.second.c_str());
+ }
+ bool flag = compareMap(mInputServiceAttributeMap, serviceAttributeMap);
+ ASSERT_TRUE(flag) << "ServiceMapParseTest: typeMap mismatch";
+}
+
+int main(int argc, char **argv) {
+ gEnv = new XMLParserTestEnvironment();
+ ::testing::AddGlobalTestEnvironment(gEnv);
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = gEnv->initFromOptions(argc, argv);
+ if (status == 0) {
+ status = RUN_ALL_TESTS();
+ ALOGD("XML Parser Test Result = %d\n", status);
+ }
+ return status;
+}
diff --git a/media/libstagefright/xmlparser/test/XMLParserTestEnvironment.h b/media/libstagefright/xmlparser/test/XMLParserTestEnvironment.h
new file mode 100644
index 0000000..61a09e6
--- /dev/null
+++ b/media/libstagefright/xmlparser/test/XMLParserTestEnvironment.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 __XML_PARSER_TEST_ENVIRONMENT_H__
+#define __XML_PARSER_TEST_ENVIRONMENT_H__
+
+#include <gtest/gtest.h>
+
+#include <getopt.h>
+
+using namespace std;
+
+class XMLParserTestEnvironment : public ::testing::Environment {
+ public:
+ XMLParserTestEnvironment() : 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 XMLParserTestEnvironment::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 // __XML_PARSER_TEST_ENVIRONMENT_H__
diff --git a/media/libstagefright/xmlparser/test/testdata/media_codecs_unit_test.xml b/media/libstagefright/xmlparser/test/testdata/media_codecs_unit_test.xml
new file mode 100644
index 0000000..a7299d3
--- /dev/null
+++ b/media/libstagefright/xmlparser/test/testdata/media_codecs_unit_test.xml
@@ -0,0 +1,80 @@
+<?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.
+-->
+
+<!-- REFERENCE : frameworks/av/media/libstagefright/xmlparser/media_codecs.xsd -->
+<Included>
+ <Settings>
+ <Domain name="telephony" enabled="false" />
+ <Domain name="tv" enabled="false" />
+ <Variant name="variant1" enabled="false" />
+ <Setting name="setting1" value="settingValue1" update="true" />
+ <Setting name="setting2" enabled="false" />
+ </Settings>
+ <Decoders>
+ <!-- entry for enabled, domain, rank and update properties -->
+ <MediaCodec name="test1.decoder" type="audio/mpeg" update="false" domain="telephony" enabled="false" rank="4">
+ <Alias name="alias1.decoder" />
+ <Quirk name="quirk1" value="quirk1Value"/>
+ </MediaCodec>
+ <!-- entry for testing Quirk -->
+ <MediaCodec name="test2.decoder" type="audio/3gpp" enabled="true" >
+ <Quirk name="quirk1" value="quirk1Value"/>
+ </MediaCodec>
+ <!-- entry for testing Feature -->
+ <!-- feature2 takes value 0 (feature with same name takes lower feature's value) -->
+ <!-- feature3 gives value as 0 since it's optional -->
+ <!-- optional="true" required="true" is not a valid combination. -->
+ <!-- optional="false" required="false" is not a valid combination. -->
+ <MediaCodec name="test3.decoder" type="audio/amr-wb" >
+ <Feature name="feature1" value="feature1Val" />
+ <Feature name="feature2" value="feature2Val"/>
+ <Feature name="feature2" />
+ <Feature name="feature3" optional="true" required="false" />
+ </MediaCodec>
+ <!-- entry for testing Type -->
+ <MediaCodec name="test4.decoder">
+ <Type name="audio/flac">
+ <Feature name="feature1" value="feature1Val" />
+ </Type>
+ </MediaCodec>
+ <!-- entry for testing Attribute -->
+ <MediaCodec name="test5.decoder" type="audio/g711-mlaw" >
+ <Attribute name="attributeQuirk1" />
+ </MediaCodec>
+ <!-- entry for testing Variant -->
+ <MediaCodec name="test6.decoder" type="audio/mp4a-latm" variant="variant1,variant2" >
+ <Variant name="variant1">
+ <Limit name="variant1Limit1" min="variant1Limit1Min" max="variant1Limit1Max" />
+ <Limit name="variant1Limit2" range="variant1Limit2Low-variant1Limit2High" />
+ </Variant>
+ <Variant name="variant2">
+ <Limit name="variant2Limit1" value="variant2Limit1Value" />
+ </Variant>
+ </MediaCodec>
+ <!-- entry for testing Limit -->
+ <!-- 'in' is present in xsd file but not handled in MediaCodecsXmlParser -->
+ <MediaCodec name="test7.decoder" type="audio/vorbis" >
+ <Limit name="limit1" in="limit1In" min="limit1Min"/>
+ <Limit name="limit2" min="limit2Min" max="limit2Max" scale="limit2Scale" />
+ <Limit name="limit3" ranges="limit3Val1,limit3Val2,limit3Val3" default="limit3Val3" />
+ </MediaCodec>
+ </Decoders>
+ <Encoders>
+ <MediaCodec name="test8.encoder" type="audio/opus">
+ <Limit name="limit1" max="limit1Max" />
+ </MediaCodec>
+ </Encoders>
+</Included>
diff --git a/media/libstagefright/xmlparser/test/testdata/media_codecs_unit_test_caller.xml b/media/libstagefright/xmlparser/test/testdata/media_codecs_unit_test_caller.xml
new file mode 100644
index 0000000..d864ce9
--- /dev/null
+++ b/media/libstagefright/xmlparser/test/testdata/media_codecs_unit_test_caller.xml
@@ -0,0 +1,4 @@
+<!-- entry for testing Include -->
+<MediaCodecs>
+ <Include href="media_codecs_unit_test.xml" />
+</MediaCodecs>
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Encoder.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Encoder.java
index 45e5574..754cd8e 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Encoder.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Encoder.java
@@ -175,10 +175,10 @@
@Override
public void onError(@NonNull MediaCodec mediaCodec, @NonNull CodecException e) {
- mediaCodec.stop();
- mediaCodec.release();
- Log.e(TAG, "CodecError: " + e.toString());
+ mSignalledError = true;
+ Log.e(TAG, "Codec Error: " + e.toString());
e.printStackTrace();
+ synchronized (mLock) { mLock.notify(); }
}
@Override
diff --git a/media/tests/benchmark/src/native/decoder/C2Decoder.cpp b/media/tests/benchmark/src/native/decoder/C2Decoder.cpp
index e88d011..5254f2f 100644
--- a/media/tests/benchmark/src/native/decoder/C2Decoder.cpp
+++ b/media/tests/benchmark/src/native/decoder/C2Decoder.cpp
@@ -154,9 +154,11 @@
mStats->setDeInitTime(timeTaken);
}
-void C2Decoder::dumpStatistics(string inputReference, int64_t durationUs) {
+void C2Decoder::dumpStatistics(string inputReference, int64_t durationUs, string componentName,
+ string statsFile) {
string operation = "c2decode";
- mStats->dumpStatistics(operation, inputReference, durationUs);
+ string mode = "async";
+ mStats->dumpStatistics(operation, inputReference, durationUs, componentName, mode, statsFile);
}
void C2Decoder::resetDecoder() {
diff --git a/media/tests/benchmark/src/native/decoder/C2Decoder.h b/media/tests/benchmark/src/native/decoder/C2Decoder.h
index 0e79d51..32e1f61 100644
--- a/media/tests/benchmark/src/native/decoder/C2Decoder.h
+++ b/media/tests/benchmark/src/native/decoder/C2Decoder.h
@@ -35,7 +35,8 @@
void deInitCodec();
- void dumpStatistics(string inputReference, int64_t durationUs);
+ void dumpStatistics(string inputReference, int64_t durationUs, string componentName,
+ string statsFile);
void resetDecoder();
diff --git a/media/tests/benchmark/src/native/encoder/C2Encoder.cpp b/media/tests/benchmark/src/native/encoder/C2Encoder.cpp
index 33429ef..6a50d40 100644
--- a/media/tests/benchmark/src/native/encoder/C2Encoder.cpp
+++ b/media/tests/benchmark/src/native/encoder/C2Encoder.cpp
@@ -251,9 +251,11 @@
mStats->setDeInitTime(timeTaken);
}
-void C2Encoder::dumpStatistics(string inputReference, int64_t durationUs) {
+void C2Encoder::dumpStatistics(string inputReference, int64_t durationUs, string componentName,
+ string statsFile) {
string operation = "c2encode";
- mStats->dumpStatistics(operation, inputReference, durationUs);
+ string mode = "async";
+ mStats->dumpStatistics(operation, inputReference, durationUs, componentName, mode, statsFile);
}
void C2Encoder::resetEncoder() {
diff --git a/media/tests/benchmark/src/native/encoder/C2Encoder.h b/media/tests/benchmark/src/native/encoder/C2Encoder.h
index a4ca097..7a021f4 100644
--- a/media/tests/benchmark/src/native/encoder/C2Encoder.h
+++ b/media/tests/benchmark/src/native/encoder/C2Encoder.h
@@ -44,7 +44,8 @@
void deInitCodec();
- void dumpStatistics(string inputReference, int64_t durationUs);
+ void dumpStatistics(string inputReference, int64_t durationUs, string componentName,
+ string statsFile);
void resetEncoder();
diff --git a/media/tests/benchmark/tests/BenchmarkTestEnvironment.h b/media/tests/benchmark/tests/BenchmarkTestEnvironment.h
index ae2eee1..4edb048 100644
--- a/media/tests/benchmark/tests/BenchmarkTestEnvironment.h
+++ b/media/tests/benchmark/tests/BenchmarkTestEnvironment.h
@@ -25,7 +25,9 @@
class BenchmarkTestEnvironment : public ::testing::Environment {
public:
- BenchmarkTestEnvironment() : res("/sdcard/media/") {}
+ BenchmarkTestEnvironment()
+ : res("/data/local/tmp/MediaBenchmark/res/"),
+ statsFile("/data/local/tmp/MediaBenchmark/res/stats.csv") {}
// Parses the command line argument
int initFromOptions(int argc, char **argv);
@@ -34,8 +36,15 @@
const string getRes() const { return res; }
+ void setStatsFile(const string module) { statsFile = getRes() + module; }
+
+ const string getStatsFile() const { return statsFile; }
+
+ bool writeStatsHeader();
+
private:
string res;
+ string statsFile;
};
int BenchmarkTestEnvironment::initFromOptions(int argc, char **argv) {
@@ -70,4 +79,26 @@
return 0;
}
+/**
+ * Writes the stats header to a file
+ * <p>
+ * \param statsFile file where the stats data is to be written
+ **/
+bool BenchmarkTestEnvironment::writeStatsHeader() {
+ char statsHeader[] =
+ "currentTime, fileName, operation, componentName, NDK/SDK, sync/async, setupTime, "
+ "destroyTime, minimumTime, maximumTime, averageTime, timeToProcess1SecContent, "
+ "totalBytesProcessedPerSec, timeToFirstFrame, totalSizeInBytes, totalTime\n";
+ FILE *fpStats = fopen(statsFile.c_str(), "w");
+ if(!fpStats) {
+ return false;
+ }
+ int32_t numBytes = fwrite(statsHeader, sizeof(char), sizeof(statsHeader), fpStats);
+ fclose(fpStats);
+ if(numBytes != sizeof(statsHeader)) {
+ return false;
+ }
+ return true;
+}
+
#endif // __BENCHMARK_TEST_ENVIRONMENT_H__
diff --git a/media/tests/benchmark/tests/C2DecoderTest.cpp b/media/tests/benchmark/tests/C2DecoderTest.cpp
index dedc743..85dcbc1 100644
--- a/media/tests/benchmark/tests/C2DecoderTest.cpp
+++ b/media/tests/benchmark/tests/C2DecoderTest.cpp
@@ -136,7 +136,8 @@
mDecoder->deInitCodec();
int64_t durationUs = extractor->getClipDuration();
ALOGV("codec : %s", codecName.c_str());
- mDecoder->dumpStatistics(GetParam().first, durationUs);
+ mDecoder->dumpStatistics(GetParam().first, durationUs, codecName,
+ gEnv->getStatsFile());
mDecoder->resetDecoder();
}
}
@@ -178,6 +179,9 @@
::testing::InitGoogleTest(&argc, argv);
int status = gEnv->initFromOptions(argc, argv);
if (status == 0) {
+ gEnv->setStatsFile("C2Decoder.csv");
+ status = gEnv->writeStatsHeader();
+ ALOGV("Stats file = %d\n", status);
status = RUN_ALL_TESTS();
ALOGV("C2 Decoder Test result = %d\n", status);
}
diff --git a/media/tests/benchmark/tests/C2EncoderTest.cpp b/media/tests/benchmark/tests/C2EncoderTest.cpp
index 98eb17a..b18d856 100644
--- a/media/tests/benchmark/tests/C2EncoderTest.cpp
+++ b/media/tests/benchmark/tests/C2EncoderTest.cpp
@@ -108,7 +108,7 @@
}
string decName = "";
- string outputFileName = "decode.out";
+ string outputFileName = "/data/local/tmp/decode.out";
FILE *outFp = fopen(outputFileName.c_str(), "wb");
ASSERT_NE(outFp, nullptr) << "Unable to open output file" << outputFileName
<< " for dumping decoder's output";
@@ -140,7 +140,8 @@
mEncoder->deInitCodec();
int64_t durationUs = extractor->getClipDuration();
ALOGV("codec : %s", codecName.c_str());
- mEncoder->dumpStatistics(GetParam().first, durationUs);
+ mEncoder->dumpStatistics(GetParam().first, durationUs, codecName,
+ gEnv->getStatsFile());
mEncoder->resetEncoder();
}
}
@@ -180,6 +181,9 @@
::testing::InitGoogleTest(&argc, argv);
int status = gEnv->initFromOptions(argc, argv);
if (status == 0) {
+ gEnv->setStatsFile("C2Encoder.csv");
+ status = gEnv->writeStatsHeader();
+ ALOGV("Stats file = %d\n", status);
status = RUN_ALL_TESTS();
ALOGV("C2 Encoder Test result = %d\n", status);
}
diff --git a/media/tests/benchmark/tests/DecoderTest.cpp b/media/tests/benchmark/tests/DecoderTest.cpp
index 9f96d3b..81ef02a 100644
--- a/media/tests/benchmark/tests/DecoderTest.cpp
+++ b/media/tests/benchmark/tests/DecoderTest.cpp
@@ -84,7 +84,8 @@
decoder->deInitCodec();
ALOGV("codec : %s", codecName.c_str());
string inputReference = get<0>(params);
- decoder->dumpStatistics(inputReference);
+ decoder->dumpStatistics(inputReference, codecName, (asyncMode ? "async" : "sync"),
+ gEnv->getStatsFile());
free(inputBuffer);
decoder->resetDecoder();
}
@@ -179,8 +180,11 @@
::testing::InitGoogleTest(&argc, argv);
int status = gEnv->initFromOptions(argc, argv);
if (status == 0) {
+ gEnv->setStatsFile("Decoder.csv");
+ status = gEnv->writeStatsHeader();
+ ALOGV("Stats file = %d\n", status);
status = RUN_ALL_TESTS();
- ALOGD("Decoder Test result = %d\n", status);
+ ALOGV("Decoder Test result = %d\n", status);
}
return status;
}
\ No newline at end of file
diff --git a/media/tests/benchmark/tests/EncoderTest.cpp b/media/tests/benchmark/tests/EncoderTest.cpp
index dc2a2dd..faac847 100644
--- a/media/tests/benchmark/tests/EncoderTest.cpp
+++ b/media/tests/benchmark/tests/EncoderTest.cpp
@@ -78,7 +78,7 @@
}
string decName = "";
- string outputFileName = "decode.out";
+ string outputFileName = "/data/local/tmp/decode.out";
FILE *outFp = fopen(outputFileName.c_str(), "wb");
ASSERT_NE(outFp, nullptr) << "Unable to open output file" << outputFileName
<< " for dumping decoder's output";
@@ -133,7 +133,8 @@
encoder->deInitCodec();
ALOGV("codec : %s", codecName.c_str());
string inputReference = get<0>(params);
- encoder->dumpStatistics(inputReference, extractor->getClipDuration());
+ encoder->dumpStatistics(inputReference, extractor->getClipDuration(), codecName,
+ (asyncMode ? "async" : "sync"), gEnv->getStatsFile());
eleStream.close();
if (outFp) fclose(outFp);
@@ -214,8 +215,11 @@
::testing::InitGoogleTest(&argc, argv);
int status = gEnv->initFromOptions(argc, argv);
if (status == 0) {
+ gEnv->setStatsFile("Encoder.csv");
+ status = gEnv->writeStatsHeader();
+ ALOGV("Stats file = %d\n", status);
status = RUN_ALL_TESTS();
- ALOGD("Encoder Test result = %d\n", status);
+ ALOGV("Encoder Test result = %d\n", status);
}
return status;
}
diff --git a/media/tests/benchmark/tests/ExtractorTest.cpp b/media/tests/benchmark/tests/ExtractorTest.cpp
index ad8f1e6..d14d15b 100644
--- a/media/tests/benchmark/tests/ExtractorTest.cpp
+++ b/media/tests/benchmark/tests/ExtractorTest.cpp
@@ -48,8 +48,7 @@
ASSERT_EQ(status, AMEDIA_OK) << "Extraction failed \n";
extractObj->deInitExtractor();
-
- extractObj->dumpStatistics(GetParam().first);
+ extractObj->dumpStatistics(GetParam().first, "", gEnv->getStatsFile());
fclose(inputFp);
delete extractObj;
@@ -79,8 +78,11 @@
::testing::InitGoogleTest(&argc, argv);
int status = gEnv->initFromOptions(argc, argv);
if (status == 0) {
+ gEnv->setStatsFile("Extractor.csv");
+ status = gEnv->writeStatsHeader();
+ ALOGV("Stats file = %d\n", status);
status = RUN_ALL_TESTS();
- ALOGD(" Extractor Test result = %d\n", status);
+ ALOGV("Extractor Test result = %d\n", status);
}
return status;
}
diff --git a/media/tests/benchmark/tests/MuxerTest.cpp b/media/tests/benchmark/tests/MuxerTest.cpp
index fa2635d..991644b 100644
--- a/media/tests/benchmark/tests/MuxerTest.cpp
+++ b/media/tests/benchmark/tests/MuxerTest.cpp
@@ -113,7 +113,7 @@
ASSERT_EQ(status, 0) << "Mux failed";
muxerObj->deInitMuxer();
- muxerObj->dumpStatistics(GetParam().first + "." + fmt.c_str());
+ muxerObj->dumpStatistics(GetParam().first + "." + fmt.c_str(), fmt, gEnv->getStatsFile());
free(inputBuffer);
fclose(outputFp);
muxerObj->resetMuxer();
@@ -151,8 +151,11 @@
::testing::InitGoogleTest(&argc, argv);
int status = gEnv->initFromOptions(argc, argv);
if (status == 0) {
+ gEnv->setStatsFile("Muxer.csv");
+ status = gEnv->writeStatsHeader();
+ ALOGV("Stats file = %d\n", status);
status = RUN_ALL_TESTS();
- ALOGV("Test result = %d\n", status);
+ ALOGV("Muxer Test result = %d\n", status);
}
return status;
}
diff --git a/media/utils/TimeCheck.cpp b/media/utils/TimeCheck.cpp
index 4a3e470..59d74de 100644
--- a/media/utils/TimeCheck.cpp
+++ b/media/utils/TimeCheck.cpp
@@ -100,7 +100,6 @@
bool TimeCheck::TimeCheckThread::threadLoop()
{
status_t status = TIMED_OUT;
- const char *tag;
{
AutoMutex _l(mMutex);
@@ -109,6 +108,7 @@
}
nsecs_t endTimeNs = INT64_MAX;
+ const char *tag = "<unspecified>";
// KeyedVector mMonitorRequests is ordered so take first entry as next timeout
if (mMonitorRequests.size() != 0) {
endTimeNs = mMonitorRequests.keyAt(0);
diff --git a/services/audioflinger/FastThread.cpp b/services/audioflinger/FastThread.cpp
index 8b7a124..47fe0b3 100644
--- a/services/audioflinger/FastThread.cpp
+++ b/services/audioflinger/FastThread.cpp
@@ -309,7 +309,7 @@
// compute the delta value of clock_gettime(CLOCK_MONOTONIC)
uint32_t monotonicNs = nsec;
if (sec > 0 && sec < 4) {
- monotonicNs += sec * 1000000000;
+ monotonicNs += sec * 1000000000U; // unsigned to prevent signed overflow.
}
// compute raw CPU load = delta value of clock_gettime(CLOCK_THREAD_CPUTIME_ID)
uint32_t loadNs = 0;
@@ -325,7 +325,7 @@
}
loadNs = nsec;
if (sec > 0 && sec < 4) {
- loadNs += sec * 1000000000;
+ loadNs += sec * 1000000000U; // unsigned to prevent signed overflow.
}
} else {
// first time through the loop
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index e4402bd..f3599c4 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -1872,6 +1872,25 @@
{
mProxy->releaseBuffer(buffer);
restartIfDisabled();
+
+ // Check if the PatchTrack has enough data to write once in releaseBuffer().
+ // If not, prevent an underrun from occurring by moving the track into FS_FILLING;
+ // this logic avoids glitches when suspending A2DP with AudioPlaybackCapture.
+ // TODO: perhaps underrun avoidance could be a track property checked in isReady() instead.
+ if (mFillingUpStatus == FS_ACTIVE
+ && audio_is_linear_pcm(mFormat)
+ && !isOffloadedOrDirect()) {
+ if (sp<ThreadBase> thread = mThread.promote();
+ thread != 0) {
+ PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+ const size_t frameCount = playbackThread->frameCount() * sampleRate()
+ / playbackThread->sampleRate();
+ if (framesReady() < frameCount) {
+ ALOGD("%s(%d) Not enough data, wait for buffer to fill", __func__, mId);
+ mFillingUpStatus = FS_FILLING;
+ }
+ }
+ }
}
void AudioFlinger::PlaybackThread::PatchTrack::restartIfDisabled()
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
index dd51658..aaa28bc 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
@@ -33,7 +33,10 @@
namespace android {
-DeviceTypeSet APM_AUDIO_OUT_DEVICE_REMOTE_ALL = {AUDIO_DEVICE_OUT_REMOTE_SUBMIX};
+static const DeviceTypeSet& getAllOutRemoteDevices() {
+ static const DeviceTypeSet allOutRemoteDevices = {AUDIO_DEVICE_OUT_REMOTE_SUBMIX};
+ return allOutRemoteDevices;
+}
AudioOutputDescriptor::AudioOutputDescriptor(const sp<PolicyAudioPort>& policyAudioPort,
AudioPolicyClientInterface *clientInterface)
@@ -681,7 +684,7 @@
const sp<SwAudioOutputDescriptor> outputDesc = this->valueAt(i);
if (outputDesc->isActive(volumeSource, inPastMs, sysTime)
&& (!(outputDesc->devices()
- .containsDeviceAmongTypes(APM_AUDIO_OUT_DEVICE_REMOTE_ALL)))) {
+ .containsDeviceAmongTypes(getAllOutRemoteDevices())))) {
return true;
}
}
@@ -693,7 +696,7 @@
nsecs_t sysTime = systemTime();
for (size_t i = 0; i < size(); i++) {
const sp<SwAudioOutputDescriptor> outputDesc = valueAt(i);
- if (outputDesc->devices().containsDeviceAmongTypes(APM_AUDIO_OUT_DEVICE_REMOTE_ALL) &&
+ if (outputDesc->devices().containsDeviceAmongTypes(getAllOutRemoteDevices()) &&
outputDesc->isActive(volumeSource, inPastMs, sysTime)) {
// do not consider re routing (when the output is going to a dynamic policy)
// as "remote playback"
diff --git a/services/audiopolicy/engine/common/src/ProductStrategy.cpp b/services/audiopolicy/engine/common/src/ProductStrategy.cpp
index fe15ff6..151c7bb 100644
--- a/services/audiopolicy/engine/common/src/ProductStrategy.cpp
+++ b/services/audiopolicy/engine/common/src/ProductStrategy.cpp
@@ -73,10 +73,18 @@
audio_stream_type_t ProductStrategy::getStreamTypeForAttributes(
const audio_attributes_t &attr) const
{
- const auto iter = std::find_if(begin(mAttributesVector), end(mAttributesVector),
+ const auto &iter = std::find_if(begin(mAttributesVector), end(mAttributesVector),
[&attr](const auto &supportedAttr) {
return AudioProductStrategy::attributesMatches(supportedAttr.mAttributes, attr); });
- return iter != end(mAttributesVector) ? iter->mStream : AUDIO_STREAM_DEFAULT;
+ if (iter == end(mAttributesVector)) {
+ return AUDIO_STREAM_DEFAULT;
+ }
+ audio_stream_type_t streamType = iter->mStream;
+ ALOGW_IF(streamType == AUDIO_STREAM_DEFAULT,
+ "%s: Strategy %s supporting attributes %s has not stream type associated"
+ "fallback on MUSIC. Do not use stream volume API", __func__, mName.c_str(),
+ toString(attr).c_str());
+ return streamType != AUDIO_STREAM_DEFAULT ? streamType : AUDIO_STREAM_MUSIC;
}
audio_attributes_t ProductStrategy::getAttributesForStreamType(audio_stream_type_t streamType) const
diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp
index 02b99d0..41bb4e4 100755
--- a/services/audiopolicy/enginedefault/src/Engine.cpp
+++ b/services/audiopolicy/enginedefault/src/Engine.cpp
@@ -41,18 +41,21 @@
{
struct legacy_strategy_map { const char *name; legacy_strategy id; };
-static const std::vector<legacy_strategy_map> gLegacyStrategy = {
- { "STRATEGY_NONE", STRATEGY_NONE },
- { "STRATEGY_MEDIA", STRATEGY_MEDIA },
- { "STRATEGY_PHONE", STRATEGY_PHONE },
- { "STRATEGY_SONIFICATION", STRATEGY_SONIFICATION },
- { "STRATEGY_SONIFICATION_RESPECTFUL", STRATEGY_SONIFICATION_RESPECTFUL },
- { "STRATEGY_DTMF", STRATEGY_DTMF },
- { "STRATEGY_ENFORCED_AUDIBLE", STRATEGY_ENFORCED_AUDIBLE },
- { "STRATEGY_TRANSMITTED_THROUGH_SPEAKER", STRATEGY_TRANSMITTED_THROUGH_SPEAKER },
- { "STRATEGY_ACCESSIBILITY", STRATEGY_ACCESSIBILITY },
- { "STRATEGY_REROUTING", STRATEGY_REROUTING },
- { "STRATEGY_PATCH", STRATEGY_REROUTING }, // boiler to manage stream patch volume
+static const std::vector<legacy_strategy_map>& getLegacyStrategy() {
+ static const std::vector<legacy_strategy_map> legacyStrategy = {
+ { "STRATEGY_NONE", STRATEGY_NONE },
+ { "STRATEGY_MEDIA", STRATEGY_MEDIA },
+ { "STRATEGY_PHONE", STRATEGY_PHONE },
+ { "STRATEGY_SONIFICATION", STRATEGY_SONIFICATION },
+ { "STRATEGY_SONIFICATION_RESPECTFUL", STRATEGY_SONIFICATION_RESPECTFUL },
+ { "STRATEGY_DTMF", STRATEGY_DTMF },
+ { "STRATEGY_ENFORCED_AUDIBLE", STRATEGY_ENFORCED_AUDIBLE },
+ { "STRATEGY_TRANSMITTED_THROUGH_SPEAKER", STRATEGY_TRANSMITTED_THROUGH_SPEAKER },
+ { "STRATEGY_ACCESSIBILITY", STRATEGY_ACCESSIBILITY },
+ { "STRATEGY_REROUTING", STRATEGY_REROUTING },
+ { "STRATEGY_PATCH", STRATEGY_REROUTING }, // boiler to manage stream patch volume
+ };
+ return legacyStrategy;
};
Engine::Engine()
@@ -62,7 +65,8 @@
"Policy Engine configuration is partially invalid, skipped %zu elements",
result.nbSkippedElement);
- for (const auto &strategy : gLegacyStrategy) {
+ auto legacyStrategy = getLegacyStrategy();
+ for (const auto &strategy : legacyStrategy) {
mLegacyStrategyMap[getProductStrategyByName(strategy.name)] = strategy.id;
}
}
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 8de8ecd..a72da83 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -134,12 +134,14 @@
sp<DeviceDescriptor> device =
mHwModules.getDeviceDescriptor(deviceType, device_address, device_name, encodedFormat,
state == AUDIO_POLICY_DEVICE_STATE_AVAILABLE);
- if (device == 0) {
- return INVALID_OPERATION;
- }
+ return device ? setDeviceConnectionStateInt(device, state) : INVALID_OPERATION;
+}
+status_t AudioPolicyManager::setDeviceConnectionStateInt(const sp<DeviceDescriptor> &device,
+ audio_policy_dev_state_t state)
+{
// handle output devices
- if (audio_is_output_device(deviceType)) {
+ if (audio_is_output_device(device->type())) {
SortedVector <audio_io_handle_t> outputs;
ssize_t index = mAvailableOutputDevices.indexOf(device);
@@ -156,7 +158,7 @@
return INVALID_OPERATION;
}
ALOGV("%s() connecting device %s format %x",
- __func__, device->toString().c_str(), encodedFormat);
+ __func__, device->toString().c_str(), device->getEncodedFormat());
// register new device as available
if (mAvailableOutputDevices.add(device) < 0) {
@@ -218,16 +220,13 @@
// output device used by a dynamic policy of type recorder as no
// playback use case is affected.
bool doCheckForDeviceAndOutputChanges = true;
- if (device->type() == AUDIO_DEVICE_OUT_REMOTE_SUBMIX
- && strncmp(device_address, "0", AUDIO_DEVICE_MAX_ADDRESS_LEN) != 0) {
+ if (device->type() == AUDIO_DEVICE_OUT_REMOTE_SUBMIX && device->address() != "0") {
for (audio_io_handle_t output : outputs) {
sp<SwAudioOutputDescriptor> desc = mOutputs.valueFor(output);
sp<AudioPolicyMix> policyMix = desc->mPolicyMix.promote();
if (policyMix != nullptr
&& policyMix->mMixType == MIX_TYPE_RECORDERS
- && strncmp(device_address,
- policyMix->mDeviceAddress.string(),
- AUDIO_DEVICE_MAX_ADDRESS_LEN) == 0) {
+ && device->address() == policyMix->mDeviceAddress.string()) {
doCheckForDeviceAndOutputChanges = false;
break;
}
@@ -273,7 +272,7 @@
// a valid device selection on those outputs.
bool force = (msdOutDevices.isEmpty() || msdOutDevices != desc->devices())
&& !desc->isDuplicated()
- && (!device_distinguishes_on_address(deviceType)
+ && (!device_distinguishes_on_address(device->type())
// always force when disconnecting (a non-duplicated device)
|| (state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE));
setOutputDevices(desc, newDevices, force, 0);
@@ -289,7 +288,7 @@
} // end if is output device
// handle input devices
- if (audio_is_input_device(deviceType)) {
+ if (audio_is_input_device(device->type())) {
ssize_t index = mAvailableInputDevices.indexOf(device);
switch (state)
{
@@ -2441,6 +2440,10 @@
audio_devices_t device)
{
auto attributes = mEngine->getAttributesForStreamType(stream);
+ if (attributes == AUDIO_ATTRIBUTES_INITIALIZER) {
+ ALOGW("%s: no group for stream %s, bailing out", __func__, toString(stream).c_str());
+ return NO_ERROR;
+ }
ALOGV("%s: stream %s attributes=%s", __func__,
toString(stream).c_str(), toString(attributes).c_str());
return setVolumeIndexForAttributes(attributes, index, device);
@@ -4387,7 +4390,7 @@
// after parsing the config, mOutputDevicesAll and mInputDevicesAll contain all known devices;
// open all output streams needed to access attached devices
- onNewAudioModulesAvailable();
+ onNewAudioModulesAvailableInt(nullptr /*newDevices*/);
// make sure default device is reachable
if (mDefaultOutputDevice == 0 || !mAvailableOutputDevices.contains(mDefaultOutputDevice)) {
@@ -4444,6 +4447,16 @@
void AudioPolicyManager::onNewAudioModulesAvailable()
{
+ DeviceVector newDevices;
+ onNewAudioModulesAvailableInt(&newDevices);
+ if (!newDevices.empty()) {
+ nextAudioPortGeneration();
+ mpClientInterface->onAudioPortListUpdate();
+ }
+}
+
+void AudioPolicyManager::onNewAudioModulesAvailableInt(DeviceVector *newDevices)
+{
for (const auto& hwModule : mHwModulesAll) {
if (std::find(mHwModules.begin(), mHwModules.end(), hwModule) != mHwModules.end()) {
continue;
@@ -4507,6 +4520,7 @@
if (!device->isAttached()) {
device->attach(hwModule);
mAvailableOutputDevices.add(device);
+ if (newDevices) newDevices->add(device);
setEngineDeviceConnectionState(device, AUDIO_POLICY_DEVICE_STATE_AVAILABLE);
}
}
@@ -4562,6 +4576,7 @@
device->attach(hwModule);
device->importAudioPortAndPickAudioProfile(inProfile, true);
mAvailableInputDevices.add(device);
+ if (newDevices) newDevices->add(device);
setEngineDeviceConnectionState(device, AUDIO_POLICY_DEVICE_STATE_AVAILABLE);
}
}
@@ -5937,7 +5952,7 @@
int delayMs,
bool force)
{
- ALOGVV("applyStreamVolumes() for device %08x", device);
+ ALOGVV("applyStreamVolumes() for device %s", dumpDeviceTypes(deviceTypes).c_str());
for (const auto &volumeGroup : mEngine->getVolumeGroups()) {
auto &curves = getVolumeCurves(toVolumeSource(volumeGroup));
checkAndSetVolume(curves, toVolumeSource(volumeGroup),
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 4604676..39ed8d5 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -357,7 +357,7 @@
}
virtual const DeviceVector getAvailableOutputDevices() const
{
- return mAvailableOutputDevices;
+ return mAvailableOutputDevices.filterForEngine();
}
virtual const DeviceVector getAvailableInputDevices() const
{
@@ -776,6 +776,8 @@
std::unordered_map<uid_t, audio_flags_mask_t> mAllowedCapturePolicies;
private:
+ void onNewAudioModulesAvailableInt(DeviceVector *newDevices);
+
// Add or remove AC3 DTS encodings based on user preferences.
void modifySurroundFormats(const sp<DeviceDescriptor>& devDesc, FormatVector *formatsPtr);
void modifySurroundChannelMasks(ChannelMaskSet *channelMasksPtr);
@@ -865,6 +867,8 @@
const char *device_address,
const char *device_name,
audio_format_t encodedFormat);
+ status_t setDeviceConnectionStateInt(const sp<DeviceDescriptor> &device,
+ audio_policy_dev_state_t state);
void setEngineDeviceConnectionState(const sp<DeviceDescriptor> device,
audio_policy_dev_state_t state);
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index 3d9278b..c4139d3 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -1833,7 +1833,7 @@
void AudioPolicyService::onNewAudioModulesAvailable()
{
- mAudioCommandThread->audioModulesUpdateCommand();
+ mOutputCommandThread->audioModulesUpdateCommand();
}
diff --git a/services/audiopolicy/tests/AudioPolicyManagerTestClient.h b/services/audiopolicy/tests/AudioPolicyManagerTestClient.h
index af69466..e1721ea 100644
--- a/services/audiopolicy/tests/AudioPolicyManagerTestClient.h
+++ b/services/audiopolicy/tests/AudioPolicyManagerTestClient.h
@@ -94,6 +94,10 @@
return NO_ERROR;
}
+ void onAudioPortListUpdate() override {
+ ++mAudioPortListUpdateCount;
+ }
+
// Helper methods for tests
size_t getActivePatchesCount() const { return mActivePatches.size(); }
@@ -111,12 +115,15 @@
mAllowedModuleNames.swap(names);
}
+ size_t getAudioPortListUpdateCount() const { return mAudioPortListUpdateCount; }
+
private:
audio_module_handle_t mNextModuleHandle = AUDIO_MODULE_HANDLE_NONE + 1;
audio_io_handle_t mNextIoHandle = AUDIO_IO_HANDLE_NONE + 1;
audio_patch_handle_t mNextPatchHandle = AUDIO_PATCH_HANDLE_NONE + 1;
std::map<audio_patch_handle_t, struct audio_patch> mActivePatches;
std::set<std::string> mAllowedModuleNames;
+ size_t mAudioPortListUpdateCount = 0;
};
} // namespace android
diff --git a/services/audiopolicy/tests/AudioPolicyTestManager.h b/services/audiopolicy/tests/AudioPolicyTestManager.h
index e8c2d33..3640b6d 100644
--- a/services/audiopolicy/tests/AudioPolicyTestManager.h
+++ b/services/audiopolicy/tests/AudioPolicyTestManager.h
@@ -28,6 +28,7 @@
using AudioPolicyManager::initialize;
using AudioPolicyManager::getAvailableOutputDevices;
using AudioPolicyManager::getAvailableInputDevices;
+ uint32_t getAudioPortGeneration() const { return mAudioPortGeneration; }
};
} // namespace android
diff --git a/services/audiopolicy/tests/audiopolicymanager_tests.cpp b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
index acd61ed..0bbb86b 100644
--- a/services/audiopolicy/tests/audiopolicymanager_tests.cpp
+++ b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
@@ -103,7 +103,7 @@
audio_port_handle_t *portId = nullptr);
PatchCountCheck snapshotPatchCount() { return PatchCountCheck(mClient.get()); }
- void findDevicePort(audio_port_role_t role, audio_devices_t deviceType,
+ bool findDevicePort(audio_port_role_t role, audio_devices_t deviceType,
const std::string &address, audio_port &foundPort);
static audio_port_handle_t getDeviceIdFromPatch(const struct audio_patch* patch);
@@ -205,30 +205,32 @@
ASSERT_NE(AUDIO_PORT_HANDLE_NONE, *portId);
}
-void AudioPolicyManagerTest::findDevicePort(audio_port_role_t role,
+bool AudioPolicyManagerTest::findDevicePort(audio_port_role_t role,
audio_devices_t deviceType, const std::string &address, audio_port &foundPort) {
uint32_t numPorts = 0;
uint32_t generation1;
status_t ret;
ret = mManager->listAudioPorts(role, AUDIO_PORT_TYPE_DEVICE, &numPorts, nullptr, &generation1);
- ASSERT_EQ(NO_ERROR, ret);
+ EXPECT_EQ(NO_ERROR, ret) << "mManager->listAudioPorts returned error";
+ if (HasFailure()) return false;
uint32_t generation2;
struct audio_port ports[numPorts];
ret = mManager->listAudioPorts(role, AUDIO_PORT_TYPE_DEVICE, &numPorts, ports, &generation2);
- ASSERT_EQ(NO_ERROR, ret);
- ASSERT_EQ(generation1, generation2);
+ EXPECT_EQ(NO_ERROR, ret) << "mManager->listAudioPorts returned error";
+ EXPECT_EQ(generation1, generation2) << "Generations changed during ports retrieval";
+ if (HasFailure()) return false;
for (const auto &port : ports) {
if (port.role == role && port.ext.device.type == deviceType &&
(strncmp(port.ext.device.address, address.c_str(),
AUDIO_DEVICE_MAX_ADDRESS_LEN) == 0)) {
foundPort = port;
- return;
+ return true;
}
}
- GTEST_FAIL();
+ return false;
}
audio_port_handle_t AudioPolicyManagerTest::getDeviceIdFromPatch(
@@ -688,8 +690,8 @@
ASSERT_EQ(NO_ERROR, ret);
struct audio_port extractionPort;
- findDevicePort(AUDIO_PORT_ROLE_SOURCE, AUDIO_DEVICE_IN_REMOTE_SUBMIX,
- mMixAddress, extractionPort);
+ ASSERT_TRUE(findDevicePort(AUDIO_PORT_ROLE_SOURCE, AUDIO_DEVICE_IN_REMOTE_SUBMIX,
+ mMixAddress, extractionPort));
audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
audio_source_t source = AUDIO_SOURCE_REMOTE_SUBMIX;
@@ -701,8 +703,8 @@
ASSERT_EQ(NO_ERROR, mManager->startInput(mPortId));
ASSERT_EQ(extractionPort.id, selectedDeviceId);
- findDevicePort(AUDIO_PORT_ROLE_SINK, AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
- mMixAddress, mInjectionPort);
+ ASSERT_TRUE(findDevicePort(AUDIO_PORT_ROLE_SINK, AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
+ mMixAddress, mInjectionPort));
}
void AudioPolicyManagerTestDPPlaybackReRouting::TearDown() {
@@ -873,8 +875,8 @@
ASSERT_EQ(NO_ERROR, ret);
struct audio_port injectionPort;
- findDevicePort(AUDIO_PORT_ROLE_SINK, AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
- mMixAddress, injectionPort);
+ ASSERT_TRUE(findDevicePort(AUDIO_PORT_ROLE_SINK, AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
+ mMixAddress, injectionPort));
audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
audio_usage_t usage = AUDIO_USAGE_VIRTUAL_SOURCE;
@@ -886,8 +888,8 @@
ASSERT_EQ(NO_ERROR, mManager->startOutput(mPortId));
ASSERT_EQ(injectionPort.id, getDeviceIdFromPatch(mClient->getLastAddedPatch()));
- findDevicePort(AUDIO_PORT_ROLE_SOURCE, AUDIO_DEVICE_IN_REMOTE_SUBMIX,
- mMixAddress, mExtractionPort);
+ ASSERT_TRUE(findDevicePort(AUDIO_PORT_ROLE_SOURCE, AUDIO_DEVICE_IN_REMOTE_SUBMIX,
+ mMixAddress, mExtractionPort));
}
void AudioPolicyManagerTestDPMixRecordInjection::TearDown() {
@@ -1022,7 +1024,7 @@
audio_port devicePort;
const audio_port_role_t role = audio_is_output_device(type)
? AUDIO_PORT_ROLE_SINK : AUDIO_PORT_ROLE_SOURCE;
- findDevicePort(role, type, address, devicePort);
+ ASSERT_TRUE(findDevicePort(role, type, address, devicePort));
audio_port_handle_t routedPortId = devicePort.id;
audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
@@ -1093,3 +1095,20 @@
ASSERT_EQ(AUDIO_POLICY_DEVICE_STATE_AVAILABLE, mManager->getDeviceConnectionState(
AUDIO_DEVICE_IN_REMOTE_SUBMIX, "0"));
}
+
+TEST_F(AudioPolicyManagerDynamicHwModulesTest, ListAddedAudioPorts) {
+ struct audio_port port;
+ ASSERT_FALSE(findDevicePort(AUDIO_PORT_ROLE_SOURCE, AUDIO_DEVICE_IN_REMOTE_SUBMIX, "0", port));
+ mClient->swapAllowedModuleNames({"primary", "r_submix"});
+ mManager->onNewAudioModulesAvailable();
+ ASSERT_TRUE(findDevicePort(AUDIO_PORT_ROLE_SOURCE, AUDIO_DEVICE_IN_REMOTE_SUBMIX, "0", port));
+}
+
+TEST_F(AudioPolicyManagerDynamicHwModulesTest, ClientIsUpdated) {
+ const size_t prevAudioPortListUpdateCount = mClient->getAudioPortListUpdateCount();
+ const uint32_t prevAudioPortGeneration = mManager->getAudioPortGeneration();
+ mClient->swapAllowedModuleNames({"primary", "r_submix"});
+ mManager->onNewAudioModulesAvailable();
+ EXPECT_GT(mClient->getAudioPortListUpdateCount(), prevAudioPortListUpdateCount);
+ EXPECT_GT(mManager->getAudioPortGeneration(), prevAudioPortGeneration);
+}
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index 162b50f..ac86563 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -732,6 +732,10 @@
ALOGV("%s: state == %d, restart = %d", __FUNCTION__, params.state, restart);
+ if (params.state == Parameters::DISCONNECTED) {
+ ALOGE("%s: Camera %d has been disconnected.", __FUNCTION__, mCameraId);
+ return INVALID_OPERATION;
+ }
if ( (params.state == Parameters::PREVIEW ||
params.state == Parameters::RECORD ||
params.state == Parameters::VIDEO_SNAPSHOT)
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index bda35f3..dfe5eb0 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -339,100 +339,103 @@
status_t Camera3Device::disconnectImpl() {
ATRACE_CALL();
- Mutex::Autolock il(mInterfaceLock);
-
ALOGI("%s: E", __FUNCTION__);
status_t res = OK;
std::vector<wp<Camera3StreamInterface>> streams;
- nsecs_t maxExpectedDuration = getExpectedInFlightDuration();
{
- Mutex::Autolock l(mLock);
- if (mStatus == STATUS_UNINITIALIZED) return res;
+ Mutex::Autolock il(mInterfaceLock);
+ nsecs_t maxExpectedDuration = getExpectedInFlightDuration();
+ {
+ Mutex::Autolock l(mLock);
+ if (mStatus == STATUS_UNINITIALIZED) return res;
- if (mStatus == STATUS_ACTIVE ||
- (mStatus == STATUS_ERROR && mRequestThread != NULL)) {
- res = mRequestThread->clearRepeatingRequests();
- if (res != OK) {
- SET_ERR_L("Can't stop streaming");
- // Continue to close device even in case of error
- } else {
- res = waitUntilStateThenRelock(/*active*/ false, maxExpectedDuration);
+ if (mStatus == STATUS_ACTIVE ||
+ (mStatus == STATUS_ERROR && mRequestThread != NULL)) {
+ res = mRequestThread->clearRepeatingRequests();
if (res != OK) {
- SET_ERR_L("Timeout waiting for HAL to drain (% " PRIi64 " ns)",
- maxExpectedDuration);
+ SET_ERR_L("Can't stop streaming");
// Continue to close device even in case of error
+ } else {
+ res = waitUntilStateThenRelock(/*active*/ false, maxExpectedDuration);
+ if (res != OK) {
+ SET_ERR_L("Timeout waiting for HAL to drain (% " PRIi64 " ns)",
+ maxExpectedDuration);
+ // Continue to close device even in case of error
+ }
}
}
- }
- if (mStatus == STATUS_ERROR) {
- CLOGE("Shutting down in an error state");
- }
+ if (mStatus == STATUS_ERROR) {
+ CLOGE("Shutting down in an error state");
+ }
- if (mStatusTracker != NULL) {
- mStatusTracker->requestExit();
- }
+ if (mStatusTracker != NULL) {
+ mStatusTracker->requestExit();
+ }
- if (mRequestThread != NULL) {
- mRequestThread->requestExit();
- }
+ if (mRequestThread != NULL) {
+ mRequestThread->requestExit();
+ }
- streams.reserve(mOutputStreams.size() + (mInputStream != nullptr ? 1 : 0));
- for (size_t i = 0; i < mOutputStreams.size(); i++) {
- streams.push_back(mOutputStreams[i]);
- }
- if (mInputStream != nullptr) {
- streams.push_back(mInputStream);
+ streams.reserve(mOutputStreams.size() + (mInputStream != nullptr ? 1 : 0));
+ for (size_t i = 0; i < mOutputStreams.size(); i++) {
+ streams.push_back(mOutputStreams[i]);
+ }
+ if (mInputStream != nullptr) {
+ streams.push_back(mInputStream);
+ }
}
}
-
- // Joining done without holding mLock, otherwise deadlocks may ensue
- // as the threads try to access parent state
+ // Joining done without holding mLock and mInterfaceLock, otherwise deadlocks may ensue
+ // as the threads try to access parent state (b/143513518)
if (mRequestThread != NULL && mStatus != STATUS_ERROR) {
// HAL may be in a bad state, so waiting for request thread
// (which may be stuck in the HAL processCaptureRequest call)
// could be dangerous.
+ // give up mInterfaceLock here and then lock it again. Could this lead
+ // to other deadlocks
mRequestThread->join();
}
-
- if (mStatusTracker != NULL) {
- mStatusTracker->join();
- }
-
- HalInterface* interface;
{
- Mutex::Autolock l(mLock);
- mRequestThread.clear();
- Mutex::Autolock stLock(mTrackerLock);
- mStatusTracker.clear();
- interface = mInterface.get();
- }
+ Mutex::Autolock il(mInterfaceLock);
+ if (mStatusTracker != NULL) {
+ mStatusTracker->join();
+ }
- // Call close without internal mutex held, as the HAL close may need to
- // wait on assorted callbacks,etc, to complete before it can return.
- interface->close();
+ HalInterface* interface;
+ {
+ Mutex::Autolock l(mLock);
+ mRequestThread.clear();
+ Mutex::Autolock stLock(mTrackerLock);
+ mStatusTracker.clear();
+ interface = mInterface.get();
+ }
- flushInflightRequests();
+ // Call close without internal mutex held, as the HAL close may need to
+ // wait on assorted callbacks,etc, to complete before it can return.
+ interface->close();
- {
- Mutex::Autolock l(mLock);
- mInterface->clear();
- mOutputStreams.clear();
- mInputStream.clear();
- mDeletedStreams.clear();
- mBufferManager.clear();
- internalUpdateStatusLocked(STATUS_UNINITIALIZED);
- }
+ flushInflightRequests();
- for (auto& weakStream : streams) {
- sp<Camera3StreamInterface> stream = weakStream.promote();
- if (stream != nullptr) {
- ALOGE("%s: Stream %d leaked! strong reference (%d)!",
- __FUNCTION__, stream->getId(), stream->getStrongCount() - 1);
+ {
+ Mutex::Autolock l(mLock);
+ mInterface->clear();
+ mOutputStreams.clear();
+ mInputStream.clear();
+ mDeletedStreams.clear();
+ mBufferManager.clear();
+ internalUpdateStatusLocked(STATUS_UNINITIALIZED);
+ }
+
+ for (auto& weakStream : streams) {
+ sp<Camera3StreamInterface> stream = weakStream.promote();
+ if (stream != nullptr) {
+ ALOGE("%s: Stream %d leaked! strong reference (%d)!",
+ __FUNCTION__, stream->getId(), stream->getStrongCount() - 1);
+ }
}
}
-
ALOGI("%s: X", __FUNCTION__);
return res;
}
@@ -2165,9 +2168,7 @@
}
void Camera3Device::pauseStateNotify(bool enable) {
- // We must not hold mInterfaceLock here since this function is called from
- // RequestThread::threadLoop and holding mInterfaceLock could lead to
- // deadlocks (http://b/143513518)
+ Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
mPauseStateNotify = enable;
@@ -2744,9 +2745,7 @@
ATRACE_CALL();
bool ret = false;
- // We must not hold mInterfaceLock here since this function is called from
- // RequestThread::threadLoop and holding mInterfaceLock could lead to
- // deadlocks (http://b/143513518)
+ Mutex::Autolock il(mInterfaceLock);
nsecs_t maxExpectedDuration = getExpectedInFlightDuration();
Mutex::Autolock l(mLock);
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index cae34ce..9b0648d 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -244,7 +244,6 @@
// A lock to enforce serialization on the input/configure side
// of the public interface.
- // Only locked by public methods inherited from CameraDeviceBase.
// Not locked by methods guarded by mOutputLock, since they may act
// concurrently to the input/configure side of the interface.
// Must be locked before mLock if both will be locked by a method
diff --git a/services/mediacodec/seccomp_policy/mediacodec-arm.policy b/services/mediacodec/seccomp_policy/mediacodec-arm.policy
index 3870a11..51564ca 100644
--- a/services/mediacodec/seccomp_policy/mediacodec-arm.policy
+++ b/services/mediacodec/seccomp_policy/mediacodec-arm.policy
@@ -23,7 +23,7 @@
# on ARM is statically loaded at 0xffff 0000. See
# http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0211h/Babfeega.html
# for more details.
-mremap: arg3 == 3
+mremap: arg3 == 3 || arg3 == MREMAP_MAYMOVE
munmap: 1
mprotect: 1
madvise: 1
diff --git a/services/mediacodec/seccomp_policy/mediaswcodec-arm.policy b/services/mediacodec/seccomp_policy/mediaswcodec-arm.policy
index 9042cd7..8705cf9 100644
--- a/services/mediacodec/seccomp_policy/mediaswcodec-arm.policy
+++ b/services/mediacodec/seccomp_policy/mediaswcodec-arm.policy
@@ -31,7 +31,7 @@
# on ARM is statically loaded at 0xffff 0000. See
# http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0211h/Babfeega.html
# for more details.
-mremap: arg3 == 3
+mremap: arg3 == 3 || arg3 == MREMAP_MAYMOVE
munmap: 1
prctl: 1
getuid32: 1
diff --git a/services/mediacodec/seccomp_policy/mediaswcodec-arm64.policy b/services/mediacodec/seccomp_policy/mediaswcodec-arm64.policy
index 4faf8b2..85fd28d 100644
--- a/services/mediacodec/seccomp_policy/mediaswcodec-arm64.policy
+++ b/services/mediacodec/seccomp_policy/mediaswcodec-arm64.policy
@@ -35,7 +35,7 @@
# on ARM is statically loaded at 0xffff 0000. See
# http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0211h/Babfeega.html
# for more details.
-mremap: arg3 == 3
+mremap: arg3 == 3 || arg3 == MREMAP_MAYMOVE
munmap: 1
prctl: 1
writev: 1
diff --git a/services/mediaextractor/seccomp_policy/mediaextractor-arm.policy b/services/mediaextractor/seccomp_policy/mediaextractor-arm.policy
index 964acf4..c61393d 100644
--- a/services/mediaextractor/seccomp_policy/mediaextractor-arm.policy
+++ b/services/mediaextractor/seccomp_policy/mediaextractor-arm.policy
@@ -45,6 +45,14 @@
# for dynamically loading extractors
pread64: 1
+# mremap: Ensure |flags| are (MREMAP_MAYMOVE | MREMAP_FIXED) TODO: Once minijail
+# parser support for '<' is in this needs to be modified to also prevent
+# |old_address| and |new_address| from touching the exception vector page, which
+# on ARM is statically loaded at 0xffff 0000. See
+# http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0211h/Babfeega.html
+# for more details.
+mremap: arg3 == 3 || arg3 == MREMAP_MAYMOVE
+
# for FileSource
readlinkat: 1
_llseek: 1
diff --git a/services/oboeservice/SharedMemoryProxy.cpp b/services/oboeservice/SharedMemoryProxy.cpp
index c43ed22..78d4884 100644
--- a/services/oboeservice/SharedMemoryProxy.cpp
+++ b/services/oboeservice/SharedMemoryProxy.cpp
@@ -20,6 +20,7 @@
#include <errno.h>
#include <string.h>
+#include <unistd.h>
#include <aaudio/AAudio.h>
#include "SharedMemoryProxy.h"